declare-render 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,7 @@ const schema: RenderData = {
20
20
  id: "my-canvas",
21
21
  width: 800,
22
22
  height: 600,
23
- renderers: [
23
+ layers: [
24
24
  {
25
25
  id: "text-1",
26
26
  type: "text",
@@ -45,7 +45,8 @@ const buffer = await renderer.draw();
45
45
 
46
46
  ## Features
47
47
 
48
- - **Text Renderers**: Render text with various styling options
49
- - **Image Renderers**: Render images or solid color rectangles
50
- - **Container Renderers**: Organize renderers with flexbox-like layouts
51
- - **Multiple Output Formats**: PNG and JPG support
48
+ - **Text Layers**: Render text with various styling options
49
+ - **Image Layers**: Render images or solid color rectangles
50
+ - **Container Layers**: Organize layers with flexbox-like layouts
51
+ - **Multiple Output Formats**: PNG and JPG support
52
+
Binary file
Binary file
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Declare-render examples: all test schemas in one file.
3
+ * Each schema is commented with what it demonstrates and which output file it produces.
4
+ */
5
+ import { Renderer } from "../src/index";
6
+ import { RenderData, RendererType, type ShapeRenderData } from "../src/types";
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import { fileURLToPath } from "url";
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Helper: create an arrow shape (used by arrow-test schema)
16
+ // ---------------------------------------------------------------------------
17
+ function createArrow(
18
+ id: string | number,
19
+ x: number,
20
+ y: number,
21
+ endX: number,
22
+ endY: number,
23
+ arrowLength: number = 20,
24
+ arrowAngle: number = Math.PI / 6,
25
+ ): ShapeRenderData {
26
+ const dx = endX - x;
27
+ const dy = endY - y;
28
+ const angle = Math.atan2(dy, dx);
29
+ const arrowX1 = endX - arrowLength * Math.cos(angle - arrowAngle);
30
+ const arrowY1 = endY - arrowLength * Math.sin(angle - arrowAngle);
31
+ const arrowX2 = endX - arrowLength * Math.cos(angle + arrowAngle);
32
+ const arrowY2 = endY - arrowLength * Math.sin(angle + arrowAngle);
33
+ const minX = Math.min(x, endX, arrowX1, arrowX2);
34
+ const minY = Math.min(y, endY, arrowY1, arrowY2);
35
+ const maxX = Math.max(x, endX, arrowX1, arrowX2);
36
+ const maxY = Math.max(y, endY, arrowY1, arrowY2);
37
+ const normalizedX = x - minX;
38
+ const normalizedY = y - minY;
39
+ const normalizedEndX = endX - minX;
40
+ const normalizedEndY = endY - minY;
41
+ const normalizedArrowX1 = arrowX1 - minX;
42
+ const normalizedArrowY1 = arrowY1 - minY;
43
+ const normalizedArrowX2 = arrowX2 - minX;
44
+ const normalizedArrowY2 = arrowY2 - minY;
45
+ return {
46
+ id,
47
+ type: RendererType.SHAPE,
48
+ x: minX,
49
+ y: minY,
50
+ width: maxX - minX,
51
+ height: maxY - minY,
52
+ style: {
53
+ strokeStyle: "#000000",
54
+ fillStyle: "#000000",
55
+ lineWidth: 3,
56
+ lineCap: "round",
57
+ lineJoin: "round",
58
+ },
59
+ shapes: [
60
+ { type: "beginPath" },
61
+ { type: "moveTo", x: normalizedX, y: normalizedY },
62
+ { type: "lineTo", x: normalizedEndX, y: normalizedEndY },
63
+ { type: "stroke" },
64
+ { type: "beginPath" },
65
+ { type: "moveTo", x: normalizedEndX, y: normalizedEndY },
66
+ { type: "lineTo", x: normalizedArrowX1, y: normalizedArrowY1 },
67
+ { type: "lineTo", x: normalizedArrowX2, y: normalizedArrowY2 },
68
+ { type: "closePath" },
69
+ { type: "fill" },
70
+ ],
71
+ };
72
+ }
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // Schema 1: Shape test
76
+ // Purpose: Rectangles, circles (arc), triangles (lines), star, heart (bezier),
77
+ // smiley (multiple arcs), rotated rect, shadow, quadratic curves, dashed line,
78
+ // multiple shapes in one layer. Output: shape-test.png
79
+ // ---------------------------------------------------------------------------
80
+ const shapeTestSchema: RenderData = {
81
+ id: "shape-test",
82
+ width: 800,
83
+ height: 1200,
84
+ layers: [
85
+ {
86
+ id: "rect1",
87
+ type: RendererType.SHAPE,
88
+ x: 50,
89
+ y: 50,
90
+ width: 200,
91
+ height: 150,
92
+ style: { fillStyle: "#3498db", strokeStyle: "#2980b9", lineWidth: 3 },
93
+ shapes: [
94
+ { type: "fillRect", x: 0, y: 0, width: 200, height: 150 },
95
+ { type: "strokeRect", x: 0, y: 0, width: 200, height: 150 },
96
+ ],
97
+ } as ShapeRenderData,
98
+ {
99
+ id: "circle1",
100
+ type: RendererType.SHAPE,
101
+ x: 300,
102
+ y: 50,
103
+ width: 150,
104
+ height: 150,
105
+ style: { fillStyle: "#e74c3c", strokeStyle: "#c0392b", lineWidth: 2 },
106
+ shapes: [
107
+ { type: "beginPath" },
108
+ { type: "arc", x: 75, y: 75, radius: 70, startAngle: 0, endAngle: Math.PI * 2, counterclockwise: false },
109
+ { type: "fillAndStroke" },
110
+ ],
111
+ } as ShapeRenderData,
112
+ {
113
+ id: "triangle1",
114
+ type: RendererType.SHAPE,
115
+ x: 500,
116
+ y: 50,
117
+ width: 200,
118
+ height: 200,
119
+ style: { fillStyle: "#2ecc71", strokeStyle: "#27ae60", lineWidth: 4 },
120
+ shapes: [
121
+ { type: "beginPath" },
122
+ { type: "moveTo", x: 100, y: 0 },
123
+ { type: "lineTo", x: 200, y: 200 },
124
+ { type: "lineTo", x: 0, y: 200 },
125
+ { type: "closePath" },
126
+ { type: "fillAndStroke" },
127
+ ],
128
+ } as ShapeRenderData,
129
+ {
130
+ id: "star1",
131
+ type: RendererType.SHAPE,
132
+ x: 50,
133
+ y: 250,
134
+ width: 200,
135
+ height: 200,
136
+ style: { fillStyle: "#f39c12", strokeStyle: "#e67e22", lineWidth: 2 },
137
+ shapes: [
138
+ { type: "beginPath" },
139
+ { type: "moveTo", x: 100, y: 0 },
140
+ { type: "lineTo", x: 120, y: 70 },
141
+ { type: "lineTo", x: 200, y: 70 },
142
+ { type: "lineTo", x: 135, y: 115 },
143
+ { type: "lineTo", x: 160, y: 200 },
144
+ { type: "lineTo", x: 100, y: 150 },
145
+ { type: "lineTo", x: 40, y: 200 },
146
+ { type: "lineTo", x: 65, y: 115 },
147
+ { type: "lineTo", x: 0, y: 70 },
148
+ { type: "lineTo", x: 80, y: 70 },
149
+ { type: "closePath" },
150
+ { type: "fillAndStroke" },
151
+ ],
152
+ } as ShapeRenderData,
153
+ {
154
+ id: "heart1",
155
+ type: RendererType.SHAPE,
156
+ x: 300,
157
+ y: 250,
158
+ width: 200,
159
+ height: 180,
160
+ style: { fillStyle: "#e91e63", strokeStyle: "#c2185b", lineWidth: 2 },
161
+ shapes: [
162
+ { type: "beginPath" },
163
+ { type: "moveTo", x: 100, y: 60 },
164
+ { type: "bezierCurveTo", cp1x: 100, cp1y: 30, cp2x: 50, cp2y: 30, x: 50, y: 60 },
165
+ { type: "bezierCurveTo", cp1x: 50, cp1y: 100, cp2x: 100, cp2y: 140, x: 100, y: 180 },
166
+ { type: "bezierCurveTo", cp1x: 100, cp1y: 140, cp2x: 150, cp2y: 100, x: 150, y: 60 },
167
+ { type: "bezierCurveTo", cp1x: 150, cp1y: 30, cp2x: 100, cp2y: 30, x: 100, y: 60 },
168
+ { type: "fillAndStroke" },
169
+ ],
170
+ } as ShapeRenderData,
171
+ {
172
+ id: "smiley1",
173
+ type: RendererType.SHAPE,
174
+ x: 550,
175
+ y: 250,
176
+ width: 200,
177
+ height: 200,
178
+ style: { fillStyle: "#ffeb3b", strokeStyle: "#fbc02d", lineWidth: 3 },
179
+ shapes: [
180
+ { type: "beginPath" },
181
+ { type: "arc", x: 100, y: 100, radius: 80, startAngle: 0, endAngle: Math.PI * 2, counterclockwise: false },
182
+ { type: "fill" },
183
+ { type: "stroke" },
184
+ { type: "beginPath" },
185
+ { type: "arc", x: 70, y: 80, radius: 10, startAngle: 0, endAngle: Math.PI * 2, counterclockwise: false },
186
+ { type: "fill" },
187
+ { type: "beginPath" },
188
+ { type: "arc", x: 130, y: 80, radius: 10, startAngle: 0, endAngle: Math.PI * 2, counterclockwise: false },
189
+ { type: "fill" },
190
+ { type: "beginPath" },
191
+ { type: "arc", x: 100, y: 100, radius: 50, startAngle: 0, endAngle: Math.PI, counterclockwise: false },
192
+ { type: "stroke" },
193
+ ],
194
+ } as ShapeRenderData,
195
+ {
196
+ id: "rotated-rect",
197
+ type: RendererType.SHAPE,
198
+ x: 50,
199
+ y: 500,
200
+ width: 150,
201
+ height: 100,
202
+ rotate: 45,
203
+ style: { fillStyle: "#9b59b6", strokeStyle: "#8e44ad", lineWidth: 2 },
204
+ shapes: [
205
+ { type: "fillRect", x: 0, y: 0, width: 150, height: 100 },
206
+ { type: "strokeRect", x: 0, y: 0, width: 150, height: 100 },
207
+ ],
208
+ } as ShapeRenderData,
209
+ {
210
+ id: "shadow-shape",
211
+ type: RendererType.SHAPE,
212
+ x: 250,
213
+ y: 500,
214
+ width: 150,
215
+ height: 150,
216
+ style: { fillStyle: "#16a085", strokeStyle: "#138d75", lineWidth: 2 },
217
+ shadow: { color: "rgba(0, 0, 0, 0.5)", blur: 10, X: 5, Y: 5 },
218
+ shapes: [
219
+ { type: "beginPath" },
220
+ { type: "arc", x: 75, y: 75, radius: 60, startAngle: 0, endAngle: Math.PI * 2, counterclockwise: false },
221
+ { type: "fillAndStroke" },
222
+ ],
223
+ } as ShapeRenderData,
224
+ {
225
+ id: "curve-shape",
226
+ type: RendererType.SHAPE,
227
+ x: 450,
228
+ y: 500,
229
+ width: 200,
230
+ height: 150,
231
+ style: { fillStyle: "#34495e", strokeStyle: "#2c3e50", lineWidth: 3, lineCap: "round", lineJoin: "round" },
232
+ shapes: [
233
+ { type: "beginPath" },
234
+ { type: "moveTo", x: 0, y: 75 },
235
+ { type: "quadraticCurveTo", cp1x: 50, cp1y: 0, x: 100, y: 75 },
236
+ { type: "quadraticCurveTo", cp1x: 150, cp1y: 150, x: 200, y: 75 },
237
+ { type: "stroke" },
238
+ ],
239
+ } as ShapeRenderData,
240
+ {
241
+ id: "dashed-rect",
242
+ type: RendererType.SHAPE,
243
+ x: 50,
244
+ y: 700,
245
+ width: 200,
246
+ height: 150,
247
+ style: { strokeStyle: "#e74c3c", lineWidth: 4, lineDash: [10, 5], lineDashOffset: 0 },
248
+ shapes: [{ type: "strokeRect", x: 0, y: 0, width: 200, height: 150 }],
249
+ } as ShapeRenderData,
250
+ {
251
+ id: "multi-shape",
252
+ type: RendererType.SHAPE,
253
+ x: 300,
254
+ y: 700,
255
+ width: 300,
256
+ height: 200,
257
+ style: { fillStyle: "#95a5a6", strokeStyle: "#7f8c8d", lineWidth: 2 },
258
+ shapes: [
259
+ { type: "fillRect", x: 0, y: 0, width: 300, height: 200 },
260
+ { type: "beginPath" },
261
+ { type: "arc", x: 75, y: 75, radius: 50, startAngle: 0, endAngle: Math.PI * 2, counterclockwise: false },
262
+ { type: "fill" },
263
+ { type: "fillRect", x: 150, y: 25, width: 100, height: 100 },
264
+ { type: "beginPath" },
265
+ { type: "moveTo", x: 275, y: 25 },
266
+ { type: "lineTo", x: 300, y: 125 },
267
+ { type: "lineTo", x: 250, y: 125 },
268
+ { type: "closePath" },
269
+ { type: "fill" },
270
+ ],
271
+ } as ShapeRenderData,
272
+ ],
273
+ output: { type: "png" },
274
+ };
275
+
276
+ // ---------------------------------------------------------------------------
277
+ // Schema 2: Arrow test
278
+ // Purpose: Directional arrows (createArrow), curved arrow, circles/rects/triangles,
279
+ // arrows pointing to shapes, flow diagram. Output: arrow-test.png
280
+ // ---------------------------------------------------------------------------
281
+ const arrowTestSchema: RenderData = {
282
+ id: "arrow-test",
283
+ width: 800,
284
+ height: 600,
285
+ layers: [
286
+ createArrow("arrow1", 50, 100, 200, 100, 25),
287
+ createArrow("arrow2", 250, 50, 250, 200, 25),
288
+ createArrow("arrow3", 50, 300, 200, 200, 30),
289
+ createArrow("arrow4", 50, 400, 200, 500, 30),
290
+ {
291
+ id: "curved-arrow",
292
+ type: RendererType.SHAPE,
293
+ x: 300,
294
+ y: 100,
295
+ width: 200,
296
+ height: 200,
297
+ style: { strokeStyle: "#000000", fillStyle: "#000000", lineWidth: 3, lineCap: "round" },
298
+ shapes: [
299
+ { type: "beginPath" },
300
+ { type: "moveTo", x: 0, y: 0 },
301
+ { type: "bezierCurveTo", cp1x: 50, cp1y: 0, cp2x: 150, cp2y: 200, x: 200, y: 200 },
302
+ { type: "stroke" },
303
+ { type: "beginPath" },
304
+ { type: "moveTo", x: 200, y: 200 },
305
+ { type: "lineTo", x: 180, y: 185 },
306
+ { type: "lineTo", x: 185, y: 195 },
307
+ { type: "closePath" },
308
+ { type: "fill" },
309
+ ],
310
+ } as ShapeRenderData,
311
+ {
312
+ id: "circle1",
313
+ type: RendererType.SHAPE,
314
+ x: 550,
315
+ y: 50,
316
+ width: 100,
317
+ height: 100,
318
+ style: { fillStyle: "#000000", strokeStyle: "#000000", lineWidth: 2 },
319
+ shapes: [
320
+ { type: "beginPath" },
321
+ { type: "arc", x: 50, y: 50, radius: 45, startAngle: 0, endAngle: Math.PI * 2, counterclockwise: false },
322
+ { type: "fillAndStroke" },
323
+ ],
324
+ } as ShapeRenderData,
325
+ {
326
+ id: "rect1",
327
+ type: RendererType.SHAPE,
328
+ x: 550,
329
+ y: 200,
330
+ width: 150,
331
+ height: 100,
332
+ style: { fillStyle: "#000000", strokeStyle: "#000000", lineWidth: 2 },
333
+ shapes: [
334
+ { type: "fillRect", x: 0, y: 0, width: 150, height: 100 },
335
+ { type: "strokeRect", x: 0, y: 0, width: 150, height: 100 },
336
+ ],
337
+ } as ShapeRenderData,
338
+ {
339
+ id: "triangle1",
340
+ type: RendererType.SHAPE,
341
+ x: 550,
342
+ y: 350,
343
+ width: 150,
344
+ height: 150,
345
+ style: { fillStyle: "#000000", strokeStyle: "#000000", lineWidth: 2 },
346
+ shapes: [
347
+ { type: "beginPath" },
348
+ { type: "moveTo", x: 75, y: 0 },
349
+ { type: "lineTo", x: 150, y: 150 },
350
+ { type: "lineTo", x: 0, y: 150 },
351
+ { type: "closePath" },
352
+ { type: "fillAndStroke" },
353
+ ],
354
+ } as ShapeRenderData,
355
+ createArrow("arrow-to-circle", 300, 350, 550, 100, 30),
356
+ createArrow("arrow-to-rect", 300, 400, 550, 250, 30),
357
+ createArrow("arrow-to-triangle", 300, 450, 550, 425, 30),
358
+ createArrow("flow1", 50, 550, 150, 550, 20),
359
+ createArrow("flow2", 150, 550, 250, 550, 20),
360
+ createArrow("flow3", 250, 550, 350, 550, 20),
361
+ createArrow("flow4", 350, 550, 450, 550, 20),
362
+ ],
363
+ output: { type: "png" },
364
+ };
365
+
366
+ // ---------------------------------------------------------------------------
367
+ // Schema 3: Canvas-1 test
368
+ // Purpose: Minimal agent-style layout: circle + square + arrow line, with
369
+ // per-command style (fillStyle/strokeStyle on fill/stroke commands). Output: canvas-1-test.png
370
+ // ---------------------------------------------------------------------------
371
+ const canvas1Schema: RenderData = {
372
+ id: "canvas-1",
373
+ width: 400,
374
+ height: 300,
375
+ layers: [
376
+ {
377
+ id: "circle",
378
+ type: "shape",
379
+ x: 120,
380
+ y: 150,
381
+ shapes: [
382
+ { type: "beginPath" },
383
+ { type: "arc", x: 0, y: 0, radius: 40, startAngle: 0, endAngle: 6.283185307179586, counterclockwise: false },
384
+ { type: "closePath" },
385
+ { type: "fill", style: { fillStyle: "#4A90D9" } },
386
+ ],
387
+ },
388
+ {
389
+ id: "square",
390
+ type: "shape",
391
+ x: 280,
392
+ y: 150,
393
+ shapes: [
394
+ { type: "beginPath" },
395
+ { type: "rect", x: -40, y: -40, width: 80, height: 80 },
396
+ { type: "closePath" },
397
+ { type: "fill", style: { fillStyle: "#4A90D9" } },
398
+ ],
399
+ },
400
+ {
401
+ id: "arrow-line",
402
+ type: "shape",
403
+ x: 0,
404
+ y: 0,
405
+ shapes: [
406
+ { type: "beginPath" },
407
+ { type: "moveTo", x: 160, y: 150 },
408
+ { type: "lineTo", x: 240, y: 150 },
409
+ { type: "stroke", style: { strokeStyle: "#333333", lineWidth: 3 } },
410
+ { type: "beginPath" },
411
+ { type: "moveTo", x: 240, y: 150 },
412
+ { type: "lineTo", x: 230, y: 140 },
413
+ { type: "lineTo", x: 230, y: 160 },
414
+ { type: "closePath" },
415
+ { type: "fill", style: { strokeStyle: "#333333", lineWidth: 3 } },
416
+ ],
417
+ },
418
+ ],
419
+ };
420
+
421
+ // ---------------------------------------------------------------------------
422
+ // Run all tests: render each schema and write to output/<name>.png
423
+ // ---------------------------------------------------------------------------
424
+ const TESTS: { name: string; schema: RenderData }[] = [
425
+ { name: "shape-test", schema: shapeTestSchema },
426
+ { name: "arrow-test", schema: arrowTestSchema },
427
+ { name: "canvas-1-test", schema: canvas1Schema },
428
+ ];
429
+
430
+ async function main() {
431
+ const outputDir = path.join(__dirname, "output");
432
+ if (!fs.existsSync(outputDir)) {
433
+ fs.mkdirSync(outputDir, { recursive: true });
434
+ }
435
+
436
+ for (const { name, schema } of TESTS) {
437
+ try {
438
+ const renderer = new Renderer(schema);
439
+ const buffer = await renderer.draw();
440
+ const outputPath = path.join(outputDir, `${name}.png`);
441
+ fs.writeFileSync(outputPath, buffer);
442
+ console.log(`Rendered: ${outputPath}`);
443
+ } catch (error) {
444
+ console.error(`Error rendering ${name}:`, error);
445
+ if (error instanceof Error) console.error(error.stack);
446
+ process.exit(1);
447
+ }
448
+ }
449
+
450
+ console.log("All tests passed.");
451
+ }
452
+
453
+ main().catch((error) => {
454
+ console.error("Unhandled error:", error);
455
+ process.exit(1);
456
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "declare-render",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "You can declare canvas shapes by JSON format.",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -24,9 +24,6 @@
24
24
  "type": "module",
25
25
  "scripts": {
26
26
  "rebuild:canvas": "pnpm rebuild canvas",
27
- "test:shape": "tsx examples/shape-test.ts",
28
- "test:shape:ts-node": "NODE_OPTIONS='--loader ts-node/esm' node examples/shape-test.ts",
29
- "test:arrow": "tsx examples/arrow-test.ts",
30
- "test:arrow:ts-node": "NODE_OPTIONS='--loader ts-node/esm' node examples/arrow-test.ts"
27
+ "test": "tsx examples/test.ts"
31
28
  }
32
29
  }