declare-render 1.0.5 → 1.0.7
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/examples/output/arrow-test.png +0 -0
- package/examples/output/canvas-1-test.png +0 -0
- package/examples/output/shape-test.png +0 -0
- package/examples/test.ts +456 -0
- package/package.json +2 -5
- package/src/canvas-renderers/shape-render/index.ts +9 -7
- package/src/index.ts +2 -0
- package/src/types.ts +30 -23
- package/examples/arrow-test.ts +0 -252
- package/examples/shape-test.ts +0 -411
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/examples/test.ts
ADDED
|
@@ -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.
|
|
3
|
+
"version": "1.0.7",
|
|
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
|
|
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
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseRender } from "../base-renderer";
|
|
2
|
-
import type { ShapeRenderData, ShapeCommand } from "../../types";
|
|
2
|
+
import type { ShapeRenderData, ShapeCommand, ShapeStyle } from "../../types";
|
|
3
3
|
import { cloneDeep, isNumber } from "lodash-es";
|
|
4
4
|
import { type CanvasRenderingContext2D } from "canvas";
|
|
5
5
|
|
|
@@ -20,8 +20,8 @@ export class ShapeRender extends BaseRender<ShapeRenderData> {
|
|
|
20
20
|
return this.getContainerCoordinates();
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
/** Apply style to ctx. Used for layer style (inherit) and per-command override. */
|
|
24
|
+
private applyStyle(style: ShapeStyle | undefined) {
|
|
25
25
|
if (!style) return;
|
|
26
26
|
|
|
27
27
|
if (style.fillStyle) {
|
|
@@ -236,15 +236,17 @@ export class ShapeRender extends BaseRender<ShapeRenderData> {
|
|
|
236
236
|
const offsetY = y;
|
|
237
237
|
|
|
238
238
|
const drawImpl = () => {
|
|
239
|
-
this.ctx.save();
|
|
240
|
-
this.applyStyle();
|
|
241
239
|
this.applyShadow();
|
|
242
240
|
|
|
241
|
+
this.applyStyle(this.data.style);
|
|
242
|
+
this.ctx.save();
|
|
243
243
|
for (const cmd of this.data.shapes) {
|
|
244
|
+
if ("style" in cmd && cmd.style) {
|
|
245
|
+
this.applyStyle(cmd.style);
|
|
246
|
+
}
|
|
244
247
|
this.executeCommand(cmd, offsetX, offsetY);
|
|
248
|
+
this.ctx.restore();
|
|
245
249
|
}
|
|
246
|
-
|
|
247
|
-
this.ctx.restore();
|
|
248
250
|
};
|
|
249
251
|
|
|
250
252
|
if (!rotate) {
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -71,6 +71,19 @@ export interface ContainerRenderData {
|
|
|
71
71
|
layers: ChildRenderers;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/** Style for shape layer or per-command override. Commands inherit layer style; command.style overrides for that command only. */
|
|
75
|
+
export interface ShapeStyle {
|
|
76
|
+
fillStyle?: string;
|
|
77
|
+
strokeStyle?: string;
|
|
78
|
+
lineWidth?: number;
|
|
79
|
+
lineCap?: "butt" | "round" | "square";
|
|
80
|
+
lineJoin?: "bevel" | "round" | "miter";
|
|
81
|
+
miterLimit?: number;
|
|
82
|
+
lineDash?: number[];
|
|
83
|
+
lineDashOffset?: number;
|
|
84
|
+
globalAlpha?: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
74
87
|
export interface ShapeRenderData {
|
|
75
88
|
id: string | number;
|
|
76
89
|
type: "shape";
|
|
@@ -79,17 +92,7 @@ export interface ShapeRenderData {
|
|
|
79
92
|
width?: number;
|
|
80
93
|
height?: number;
|
|
81
94
|
rotate?: number;
|
|
82
|
-
style?:
|
|
83
|
-
fillStyle?: string;
|
|
84
|
-
strokeStyle?: string;
|
|
85
|
-
lineWidth?: number;
|
|
86
|
-
lineCap?: "butt" | "round" | "square";
|
|
87
|
-
lineJoin?: "bevel" | "round" | "miter";
|
|
88
|
-
miterLimit?: number;
|
|
89
|
-
lineDash?: number[];
|
|
90
|
-
lineDashOffset?: number;
|
|
91
|
-
globalAlpha?: number;
|
|
92
|
-
};
|
|
95
|
+
style?: ShapeStyle;
|
|
93
96
|
shadow?: {
|
|
94
97
|
color: string;
|
|
95
98
|
blur: number;
|
|
@@ -100,14 +103,14 @@ export interface ShapeRenderData {
|
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
export type ShapeCommand =
|
|
103
|
-
| { type: "rect"; x: number; y: number; width: number; height: number }
|
|
104
|
-
| { type: "fillRect"; x: number; y: number; width: number; height: number }
|
|
105
|
-
| { type: "strokeRect"; x: number; y: number; width: number; height: number }
|
|
106
|
-
| { type: "clearRect"; x: number; y: number; width: number; height: number }
|
|
107
|
-
| { type: "beginPath" }
|
|
108
|
-
| { type: "closePath" }
|
|
109
|
-
| { type: "moveTo"; x: number; y: number }
|
|
110
|
-
| { type: "lineTo"; x: number; y: number }
|
|
106
|
+
| { type: "rect"; x: number; y: number; width: number; height: number; style?: ShapeStyle }
|
|
107
|
+
| { type: "fillRect"; x: number; y: number; width: number; height: number; style?: ShapeStyle }
|
|
108
|
+
| { type: "strokeRect"; x: number; y: number; width: number; height: number; style?: ShapeStyle }
|
|
109
|
+
| { type: "clearRect"; x: number; y: number; width: number; height: number; style?: ShapeStyle }
|
|
110
|
+
| { type: "beginPath"; style?: ShapeStyle }
|
|
111
|
+
| { type: "closePath"; style?: ShapeStyle }
|
|
112
|
+
| { type: "moveTo"; x: number; y: number; style?: ShapeStyle }
|
|
113
|
+
| { type: "lineTo"; x: number; y: number; style?: ShapeStyle }
|
|
111
114
|
| {
|
|
112
115
|
type: "arc";
|
|
113
116
|
x: number;
|
|
@@ -116,6 +119,7 @@ export type ShapeCommand =
|
|
|
116
119
|
startAngle: number;
|
|
117
120
|
endAngle: number;
|
|
118
121
|
counterclockwise?: boolean;
|
|
122
|
+
style?: ShapeStyle;
|
|
119
123
|
}
|
|
120
124
|
| {
|
|
121
125
|
type: "arcTo";
|
|
@@ -124,6 +128,7 @@ export type ShapeCommand =
|
|
|
124
128
|
x2: number;
|
|
125
129
|
y2: number;
|
|
126
130
|
radius: number;
|
|
131
|
+
style?: ShapeStyle;
|
|
127
132
|
}
|
|
128
133
|
| {
|
|
129
134
|
type: "quadraticCurveTo";
|
|
@@ -131,6 +136,7 @@ export type ShapeCommand =
|
|
|
131
136
|
cp1y: number;
|
|
132
137
|
x: number;
|
|
133
138
|
y: number;
|
|
139
|
+
style?: ShapeStyle;
|
|
134
140
|
}
|
|
135
141
|
| {
|
|
136
142
|
type: "bezierCurveTo";
|
|
@@ -140,10 +146,11 @@ export type ShapeCommand =
|
|
|
140
146
|
cp2y: number;
|
|
141
147
|
x: number;
|
|
142
148
|
y: number;
|
|
149
|
+
style?: ShapeStyle;
|
|
143
150
|
}
|
|
144
|
-
| { type: "fill" }
|
|
145
|
-
| { type: "stroke" }
|
|
146
|
-
| { type: "fillAndStroke" };
|
|
151
|
+
| { type: "fill"; style?: ShapeStyle }
|
|
152
|
+
| { type: "stroke"; style?: ShapeStyle }
|
|
153
|
+
| { type: "fillAndStroke"; style?: ShapeStyle };
|
|
147
154
|
|
|
148
155
|
export type ChildRenderers = (
|
|
149
156
|
| ImgRenderData
|
|
@@ -180,7 +187,7 @@ ContainerRenderData: { "id": string|number, "type": "container", "x": number, "y
|
|
|
180
187
|
|
|
181
188
|
ShapeRenderData: { "id": string|number, "type": "shape", "x": number, "y": number, "width"?: number, "height"?: number, "rotate"?: number, "style"?: { "fillStyle"?: string, "strokeStyle"?: string, "lineWidth"?: number, "lineCap"?: "butt"|"round"|"square", "lineJoin"?: "bevel"|"round"|"miter", "miterLimit"?: number, "lineDash"?: number[], "lineDashOffset"?: number, "globalAlpha"?: number }, "shadow"?: { "color": string, "blur": number, "X": number, "Y": number }, "shapes": Array<ShapeCommand> }
|
|
182
189
|
|
|
183
|
-
ShapeCommand: { "type": "rect"|"fillRect"|"strokeRect"|"clearRect"|"beginPath"|"closePath"|"moveTo"|"lineTo"|"arc"|"arcTo"|"quadraticCurveTo"|"bezierCurveTo"|"fill"|"stroke"|"fillAndStroke", ...additional properties based on type }
|
|
190
|
+
ShapeCommand: { "type": "rect"|"fillRect"|"strokeRect"|"clearRect"|"beginPath"|"closePath"|"moveTo"|"lineTo"|"arc"|"arcTo"|"quadraticCurveTo"|"bezierCurveTo"|"fill"|"stroke"|"fillAndStroke", "style"?: ShapeStyle (optional; overrides layer style for this command), ...additional properties based on type }
|
|
184
191
|
`.trim();
|
|
185
192
|
|
|
186
193
|
// ----- Metrics (canvas-dependent) -----
|
package/examples/arrow-test.ts
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { Renderer } from "../src/index";
|
|
2
|
-
import { RenderData, RendererType, type ShapeRenderData } from "../src/types";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
|
|
10
|
-
// Helper function to create an arrow shape
|
|
11
|
-
function createArrow(
|
|
12
|
-
id: string | number,
|
|
13
|
-
x: number,
|
|
14
|
-
y: number,
|
|
15
|
-
endX: number,
|
|
16
|
-
endY: number,
|
|
17
|
-
arrowLength: number = 20,
|
|
18
|
-
arrowAngle: number = Math.PI / 6, // 30 degrees
|
|
19
|
-
): ShapeRenderData {
|
|
20
|
-
// Calculate arrow direction
|
|
21
|
-
const dx = endX - x;
|
|
22
|
-
const dy = endY - y;
|
|
23
|
-
const angle = Math.atan2(dy, dx);
|
|
24
|
-
|
|
25
|
-
// Calculate arrowhead points
|
|
26
|
-
const arrowX1 = endX - arrowLength * Math.cos(angle - arrowAngle);
|
|
27
|
-
const arrowY1 = endY - arrowLength * Math.sin(angle - arrowAngle);
|
|
28
|
-
const arrowX2 = endX - arrowLength * Math.cos(angle + arrowAngle);
|
|
29
|
-
const arrowY2 = endY - arrowLength * Math.sin(angle + arrowAngle);
|
|
30
|
-
|
|
31
|
-
// Calculate bounds
|
|
32
|
-
const minX = Math.min(x, endX, arrowX1, arrowX2);
|
|
33
|
-
const minY = Math.min(y, endY, arrowY1, arrowY2);
|
|
34
|
-
const maxX = Math.max(x, endX, arrowX1, arrowX2);
|
|
35
|
-
const maxY = Math.max(y, endY, arrowY1, arrowY2);
|
|
36
|
-
|
|
37
|
-
// Normalize coordinates relative to shape position
|
|
38
|
-
const normalizedX = x - minX;
|
|
39
|
-
const normalizedY = y - minY;
|
|
40
|
-
const normalizedEndX = endX - minX;
|
|
41
|
-
const normalizedEndY = endY - minY;
|
|
42
|
-
const normalizedArrowX1 = arrowX1 - minX;
|
|
43
|
-
const normalizedArrowY1 = arrowY1 - minY;
|
|
44
|
-
const normalizedArrowX2 = arrowX2 - minX;
|
|
45
|
-
const normalizedArrowY2 = arrowY2 - minY;
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
id,
|
|
49
|
-
type: RendererType.SHAPE,
|
|
50
|
-
x: minX,
|
|
51
|
-
y: minY,
|
|
52
|
-
width: maxX - minX,
|
|
53
|
-
height: maxY - minY,
|
|
54
|
-
style: {
|
|
55
|
-
strokeStyle: "#000000",
|
|
56
|
-
fillStyle: "#000000",
|
|
57
|
-
lineWidth: 3,
|
|
58
|
-
lineCap: "round",
|
|
59
|
-
lineJoin: "round",
|
|
60
|
-
},
|
|
61
|
-
shapes: [
|
|
62
|
-
{ type: "beginPath" },
|
|
63
|
-
{ type: "moveTo", x: normalizedX, y: normalizedY },
|
|
64
|
-
{ type: "lineTo", x: normalizedEndX, y: normalizedEndY },
|
|
65
|
-
{ type: "stroke" },
|
|
66
|
-
// Arrowhead
|
|
67
|
-
{ type: "beginPath" },
|
|
68
|
-
{ type: "moveTo", x: normalizedEndX, y: normalizedEndY },
|
|
69
|
-
{ type: "lineTo", x: normalizedArrowX1, y: normalizedArrowY1 },
|
|
70
|
-
{ type: "lineTo", x: normalizedArrowX2, y: normalizedArrowY2 },
|
|
71
|
-
{ type: "closePath" },
|
|
72
|
-
{ type: "fill" },
|
|
73
|
-
],
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Test file with black shapes including arrows
|
|
78
|
-
const arrowTestSchema: RenderData = {
|
|
79
|
-
id: "arrow-test",
|
|
80
|
-
width: 800,
|
|
81
|
-
height: 600,
|
|
82
|
-
layers: [
|
|
83
|
-
// Horizontal arrow pointing right
|
|
84
|
-
createArrow("arrow1", 50, 100, 200, 100, 25),
|
|
85
|
-
|
|
86
|
-
// Vertical arrow pointing down
|
|
87
|
-
createArrow("arrow2", 250, 50, 250, 200, 25),
|
|
88
|
-
|
|
89
|
-
// Diagonal arrow pointing up-right
|
|
90
|
-
createArrow("arrow3", 50, 300, 200, 200, 30),
|
|
91
|
-
|
|
92
|
-
// Diagonal arrow pointing down-right
|
|
93
|
-
createArrow("arrow4", 50, 400, 200, 500, 30),
|
|
94
|
-
|
|
95
|
-
// Curved arrow (using bezier curve)
|
|
96
|
-
{
|
|
97
|
-
id: "curved-arrow",
|
|
98
|
-
type: RendererType.SHAPE,
|
|
99
|
-
x: 300,
|
|
100
|
-
y: 100,
|
|
101
|
-
width: 200,
|
|
102
|
-
height: 200,
|
|
103
|
-
style: {
|
|
104
|
-
strokeStyle: "#000000",
|
|
105
|
-
fillStyle: "#000000",
|
|
106
|
-
lineWidth: 3,
|
|
107
|
-
lineCap: "round",
|
|
108
|
-
},
|
|
109
|
-
shapes: [
|
|
110
|
-
{ type: "beginPath" },
|
|
111
|
-
{ type: "moveTo", x: 0, y: 0 },
|
|
112
|
-
{
|
|
113
|
-
type: "bezierCurveTo",
|
|
114
|
-
cp1x: 50,
|
|
115
|
-
cp1y: 0,
|
|
116
|
-
cp2x: 150,
|
|
117
|
-
cp2y: 200,
|
|
118
|
-
x: 200,
|
|
119
|
-
y: 200,
|
|
120
|
-
},
|
|
121
|
-
{ type: "stroke" },
|
|
122
|
-
// Arrowhead at end
|
|
123
|
-
{ type: "beginPath" },
|
|
124
|
-
{ type: "moveTo", x: 200, y: 200 },
|
|
125
|
-
{ type: "lineTo", x: 180, y: 185 },
|
|
126
|
-
{ type: "lineTo", x: 185, y: 195 },
|
|
127
|
-
{ type: "closePath" },
|
|
128
|
-
{ type: "fill" },
|
|
129
|
-
],
|
|
130
|
-
} as ShapeRenderData,
|
|
131
|
-
|
|
132
|
-
// Simple black circle
|
|
133
|
-
{
|
|
134
|
-
id: "circle1",
|
|
135
|
-
type: RendererType.SHAPE,
|
|
136
|
-
x: 550,
|
|
137
|
-
y: 50,
|
|
138
|
-
width: 100,
|
|
139
|
-
height: 100,
|
|
140
|
-
style: {
|
|
141
|
-
fillStyle: "#000000",
|
|
142
|
-
strokeStyle: "#000000",
|
|
143
|
-
lineWidth: 2,
|
|
144
|
-
},
|
|
145
|
-
shapes: [
|
|
146
|
-
{ type: "beginPath" },
|
|
147
|
-
{
|
|
148
|
-
type: "arc",
|
|
149
|
-
x: 50,
|
|
150
|
-
y: 50,
|
|
151
|
-
radius: 45,
|
|
152
|
-
startAngle: 0,
|
|
153
|
-
endAngle: Math.PI * 2,
|
|
154
|
-
counterclockwise: false,
|
|
155
|
-
},
|
|
156
|
-
{ type: "fillAndStroke" },
|
|
157
|
-
],
|
|
158
|
-
} as ShapeRenderData,
|
|
159
|
-
|
|
160
|
-
// Black rectangle
|
|
161
|
-
{
|
|
162
|
-
id: "rect1",
|
|
163
|
-
type: RendererType.SHAPE,
|
|
164
|
-
x: 550,
|
|
165
|
-
y: 200,
|
|
166
|
-
width: 150,
|
|
167
|
-
height: 100,
|
|
168
|
-
style: {
|
|
169
|
-
fillStyle: "#000000",
|
|
170
|
-
strokeStyle: "#000000",
|
|
171
|
-
lineWidth: 2,
|
|
172
|
-
},
|
|
173
|
-
shapes: [
|
|
174
|
-
{ type: "fillRect", x: 0, y: 0, width: 150, height: 100 },
|
|
175
|
-
{ type: "strokeRect", x: 0, y: 0, width: 150, height: 100 },
|
|
176
|
-
],
|
|
177
|
-
} as ShapeRenderData,
|
|
178
|
-
|
|
179
|
-
// Black triangle
|
|
180
|
-
{
|
|
181
|
-
id: "triangle1",
|
|
182
|
-
type: RendererType.SHAPE,
|
|
183
|
-
x: 550,
|
|
184
|
-
y: 350,
|
|
185
|
-
width: 150,
|
|
186
|
-
height: 150,
|
|
187
|
-
style: {
|
|
188
|
-
fillStyle: "#000000",
|
|
189
|
-
strokeStyle: "#000000",
|
|
190
|
-
lineWidth: 2,
|
|
191
|
-
},
|
|
192
|
-
shapes: [
|
|
193
|
-
{ type: "beginPath" },
|
|
194
|
-
{ type: "moveTo", x: 75, y: 0 },
|
|
195
|
-
{ type: "lineTo", x: 150, y: 150 },
|
|
196
|
-
{ type: "lineTo", x: 0, y: 150 },
|
|
197
|
-
{ type: "closePath" },
|
|
198
|
-
{ type: "fillAndStroke" },
|
|
199
|
-
],
|
|
200
|
-
} as ShapeRenderData,
|
|
201
|
-
|
|
202
|
-
// Arrow pointing to the circle
|
|
203
|
-
createArrow("arrow-to-circle", 300, 350, 550, 100, 30),
|
|
204
|
-
|
|
205
|
-
// Arrow pointing to the rectangle
|
|
206
|
-
createArrow("arrow-to-rect", 300, 400, 550, 250, 30),
|
|
207
|
-
|
|
208
|
-
// Arrow pointing to the triangle
|
|
209
|
-
createArrow("arrow-to-triangle", 300, 450, 550, 425, 30),
|
|
210
|
-
|
|
211
|
-
// Multiple arrows forming a flow diagram
|
|
212
|
-
createArrow("flow1", 50, 550, 150, 550, 20),
|
|
213
|
-
createArrow("flow2", 150, 550, 250, 550, 20),
|
|
214
|
-
createArrow("flow3", 250, 550, 350, 550, 20),
|
|
215
|
-
createArrow("flow4", 350, 550, 450, 550, 20),
|
|
216
|
-
],
|
|
217
|
-
output: {
|
|
218
|
-
type: "png",
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
async function main() {
|
|
223
|
-
try {
|
|
224
|
-
console.log("Creating renderer...");
|
|
225
|
-
const renderer = new Renderer(arrowTestSchema);
|
|
226
|
-
console.log("Drawing shapes...");
|
|
227
|
-
const buffer = await renderer.draw();
|
|
228
|
-
const outputPath = path.join(__dirname, "output", "arrow-test.png");
|
|
229
|
-
|
|
230
|
-
// Ensure output directory exists
|
|
231
|
-
const outputDir = path.dirname(outputPath);
|
|
232
|
-
if (!fs.existsSync(outputDir)) {
|
|
233
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
console.log("Writing file...");
|
|
237
|
-
fs.writeFileSync(outputPath, buffer);
|
|
238
|
-
console.log(`Arrow test rendered successfully: ${outputPath}`);
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.error("Error rendering arrow test:");
|
|
241
|
-
console.error(error);
|
|
242
|
-
if (error instanceof Error) {
|
|
243
|
-
console.error("Stack:", error.stack);
|
|
244
|
-
}
|
|
245
|
-
process.exit(1);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
main().catch((error) => {
|
|
250
|
-
console.error("Unhandled error:", error);
|
|
251
|
-
process.exit(1);
|
|
252
|
-
});
|
package/examples/shape-test.ts
DELETED
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
import { Renderer } from "../src/index";
|
|
2
|
-
import { RenderData, RendererType, type ShapeRenderData } from "../src/types";
|
|
3
|
-
import * as fs from "fs";
|
|
4
|
-
import * as path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
-
const __dirname = path.dirname(__filename);
|
|
9
|
-
|
|
10
|
-
// Test various canvas shapes
|
|
11
|
-
const shapeTestSchema: RenderData = {
|
|
12
|
-
id: "shape-test",
|
|
13
|
-
width: 800,
|
|
14
|
-
height: 1200,
|
|
15
|
-
layers: [
|
|
16
|
-
// Rectangle shapes
|
|
17
|
-
{
|
|
18
|
-
id: "rect1",
|
|
19
|
-
type: RendererType.SHAPE,
|
|
20
|
-
x: 50,
|
|
21
|
-
y: 50,
|
|
22
|
-
width: 200,
|
|
23
|
-
height: 150,
|
|
24
|
-
style: {
|
|
25
|
-
fillStyle: "#3498db",
|
|
26
|
-
strokeStyle: "#2980b9",
|
|
27
|
-
lineWidth: 3,
|
|
28
|
-
},
|
|
29
|
-
shapes: [
|
|
30
|
-
{ type: "fillRect", x: 0, y: 0, width: 200, height: 150 },
|
|
31
|
-
{ type: "strokeRect", x: 0, y: 0, width: 200, height: 150 },
|
|
32
|
-
],
|
|
33
|
-
} as ShapeRenderData,
|
|
34
|
-
|
|
35
|
-
// Circle using arc
|
|
36
|
-
{
|
|
37
|
-
id: "circle1",
|
|
38
|
-
type: RendererType.SHAPE,
|
|
39
|
-
x: 300,
|
|
40
|
-
y: 50,
|
|
41
|
-
width: 150,
|
|
42
|
-
height: 150,
|
|
43
|
-
style: {
|
|
44
|
-
fillStyle: "#e74c3c",
|
|
45
|
-
strokeStyle: "#c0392b",
|
|
46
|
-
lineWidth: 2,
|
|
47
|
-
},
|
|
48
|
-
shapes: [
|
|
49
|
-
{ type: "beginPath" },
|
|
50
|
-
{
|
|
51
|
-
type: "arc",
|
|
52
|
-
x: 75,
|
|
53
|
-
y: 75,
|
|
54
|
-
radius: 70,
|
|
55
|
-
startAngle: 0,
|
|
56
|
-
endAngle: Math.PI * 2,
|
|
57
|
-
counterclockwise: false,
|
|
58
|
-
},
|
|
59
|
-
{ type: "fillAndStroke" },
|
|
60
|
-
],
|
|
61
|
-
} as ShapeRenderData,
|
|
62
|
-
|
|
63
|
-
// Triangle using lines
|
|
64
|
-
{
|
|
65
|
-
id: "triangle1",
|
|
66
|
-
type: RendererType.SHAPE,
|
|
67
|
-
x: 500,
|
|
68
|
-
y: 50,
|
|
69
|
-
width: 200,
|
|
70
|
-
height: 200,
|
|
71
|
-
style: {
|
|
72
|
-
fillStyle: "#2ecc71",
|
|
73
|
-
strokeStyle: "#27ae60",
|
|
74
|
-
lineWidth: 4,
|
|
75
|
-
},
|
|
76
|
-
shapes: [
|
|
77
|
-
{ type: "beginPath" },
|
|
78
|
-
{ type: "moveTo", x: 100, y: 0 },
|
|
79
|
-
{ type: "lineTo", x: 200, y: 200 },
|
|
80
|
-
{ type: "lineTo", x: 0, y: 200 },
|
|
81
|
-
{ type: "closePath" },
|
|
82
|
-
{ type: "fillAndStroke" },
|
|
83
|
-
],
|
|
84
|
-
} as ShapeRenderData,
|
|
85
|
-
|
|
86
|
-
// Star shape using lines
|
|
87
|
-
{
|
|
88
|
-
id: "star1",
|
|
89
|
-
type: RendererType.SHAPE,
|
|
90
|
-
x: 50,
|
|
91
|
-
y: 250,
|
|
92
|
-
width: 200,
|
|
93
|
-
height: 200,
|
|
94
|
-
style: {
|
|
95
|
-
fillStyle: "#f39c12",
|
|
96
|
-
strokeStyle: "#e67e22",
|
|
97
|
-
lineWidth: 2,
|
|
98
|
-
},
|
|
99
|
-
shapes: [
|
|
100
|
-
{ type: "beginPath" },
|
|
101
|
-
{ type: "moveTo", x: 100, y: 0 },
|
|
102
|
-
{ type: "lineTo", x: 120, y: 70 },
|
|
103
|
-
{ type: "lineTo", x: 200, y: 70 },
|
|
104
|
-
{ type: "lineTo", x: 135, y: 115 },
|
|
105
|
-
{ type: "lineTo", x: 160, y: 200 },
|
|
106
|
-
{ type: "lineTo", x: 100, y: 150 },
|
|
107
|
-
{ type: "lineTo", x: 40, y: 200 },
|
|
108
|
-
{ type: "lineTo", x: 65, y: 115 },
|
|
109
|
-
{ type: "lineTo", x: 0, y: 70 },
|
|
110
|
-
{ type: "lineTo", x: 80, y: 70 },
|
|
111
|
-
{ type: "closePath" },
|
|
112
|
-
{ type: "fillAndStroke" },
|
|
113
|
-
],
|
|
114
|
-
} as ShapeRenderData,
|
|
115
|
-
|
|
116
|
-
// Heart shape using bezier curves
|
|
117
|
-
{
|
|
118
|
-
id: "heart1",
|
|
119
|
-
type: RendererType.SHAPE,
|
|
120
|
-
x: 300,
|
|
121
|
-
y: 250,
|
|
122
|
-
width: 200,
|
|
123
|
-
height: 180,
|
|
124
|
-
style: {
|
|
125
|
-
fillStyle: "#e91e63",
|
|
126
|
-
strokeStyle: "#c2185b",
|
|
127
|
-
lineWidth: 2,
|
|
128
|
-
},
|
|
129
|
-
shapes: [
|
|
130
|
-
{ type: "beginPath" },
|
|
131
|
-
{ type: "moveTo", x: 100, y: 60 },
|
|
132
|
-
{
|
|
133
|
-
type: "bezierCurveTo",
|
|
134
|
-
cp1x: 100,
|
|
135
|
-
cp1y: 30,
|
|
136
|
-
cp2x: 50,
|
|
137
|
-
cp2y: 30,
|
|
138
|
-
x: 50,
|
|
139
|
-
y: 60,
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
type: "bezierCurveTo",
|
|
143
|
-
cp1x: 50,
|
|
144
|
-
cp1y: 100,
|
|
145
|
-
cp2x: 100,
|
|
146
|
-
cp2y: 140,
|
|
147
|
-
x: 100,
|
|
148
|
-
y: 180,
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
type: "bezierCurveTo",
|
|
152
|
-
cp1x: 100,
|
|
153
|
-
cp1y: 140,
|
|
154
|
-
cp2x: 150,
|
|
155
|
-
cp2y: 100,
|
|
156
|
-
x: 150,
|
|
157
|
-
y: 60,
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
type: "bezierCurveTo",
|
|
161
|
-
cp1x: 150,
|
|
162
|
-
cp1y: 30,
|
|
163
|
-
cp2x: 100,
|
|
164
|
-
cp2y: 30,
|
|
165
|
-
x: 100,
|
|
166
|
-
y: 60,
|
|
167
|
-
},
|
|
168
|
-
{ type: "fillAndStroke" },
|
|
169
|
-
],
|
|
170
|
-
} as ShapeRenderData,
|
|
171
|
-
|
|
172
|
-
// Smiley face using arcs
|
|
173
|
-
{
|
|
174
|
-
id: "smiley1",
|
|
175
|
-
type: RendererType.SHAPE,
|
|
176
|
-
x: 550,
|
|
177
|
-
y: 250,
|
|
178
|
-
width: 200,
|
|
179
|
-
height: 200,
|
|
180
|
-
style: {
|
|
181
|
-
fillStyle: "#ffeb3b",
|
|
182
|
-
strokeStyle: "#fbc02d",
|
|
183
|
-
lineWidth: 3,
|
|
184
|
-
},
|
|
185
|
-
shapes: [
|
|
186
|
-
{ type: "beginPath" },
|
|
187
|
-
{
|
|
188
|
-
type: "arc",
|
|
189
|
-
x: 100,
|
|
190
|
-
y: 100,
|
|
191
|
-
radius: 80,
|
|
192
|
-
startAngle: 0,
|
|
193
|
-
endAngle: Math.PI * 2,
|
|
194
|
-
counterclockwise: false,
|
|
195
|
-
},
|
|
196
|
-
{ type: "fill" },
|
|
197
|
-
{ type: "stroke" },
|
|
198
|
-
// Left eye
|
|
199
|
-
{ type: "beginPath" },
|
|
200
|
-
{
|
|
201
|
-
type: "arc",
|
|
202
|
-
x: 70,
|
|
203
|
-
y: 80,
|
|
204
|
-
radius: 10,
|
|
205
|
-
startAngle: 0,
|
|
206
|
-
endAngle: Math.PI * 2,
|
|
207
|
-
counterclockwise: false,
|
|
208
|
-
},
|
|
209
|
-
{ type: "fill" },
|
|
210
|
-
// Right eye
|
|
211
|
-
{ type: "beginPath" },
|
|
212
|
-
{
|
|
213
|
-
type: "arc",
|
|
214
|
-
x: 130,
|
|
215
|
-
y: 80,
|
|
216
|
-
radius: 10,
|
|
217
|
-
startAngle: 0,
|
|
218
|
-
endAngle: Math.PI * 2,
|
|
219
|
-
counterclockwise: false,
|
|
220
|
-
},
|
|
221
|
-
{ type: "fill" },
|
|
222
|
-
// Mouth
|
|
223
|
-
{ type: "beginPath" },
|
|
224
|
-
{
|
|
225
|
-
type: "arc",
|
|
226
|
-
x: 100,
|
|
227
|
-
y: 100,
|
|
228
|
-
radius: 50,
|
|
229
|
-
startAngle: 0,
|
|
230
|
-
endAngle: Math.PI,
|
|
231
|
-
counterclockwise: false,
|
|
232
|
-
},
|
|
233
|
-
{ type: "stroke" },
|
|
234
|
-
],
|
|
235
|
-
} as ShapeRenderData,
|
|
236
|
-
|
|
237
|
-
// Rotated rectangle
|
|
238
|
-
{
|
|
239
|
-
id: "rotated-rect",
|
|
240
|
-
type: RendererType.SHAPE,
|
|
241
|
-
x: 50,
|
|
242
|
-
y: 500,
|
|
243
|
-
width: 150,
|
|
244
|
-
height: 100,
|
|
245
|
-
rotate: 45,
|
|
246
|
-
style: {
|
|
247
|
-
fillStyle: "#9b59b6",
|
|
248
|
-
strokeStyle: "#8e44ad",
|
|
249
|
-
lineWidth: 2,
|
|
250
|
-
},
|
|
251
|
-
shapes: [
|
|
252
|
-
{ type: "fillRect", x: 0, y: 0, width: 150, height: 100 },
|
|
253
|
-
{ type: "strokeRect", x: 0, y: 0, width: 150, height: 100 },
|
|
254
|
-
],
|
|
255
|
-
} as ShapeRenderData,
|
|
256
|
-
|
|
257
|
-
// Shape with shadow
|
|
258
|
-
{
|
|
259
|
-
id: "shadow-shape",
|
|
260
|
-
type: RendererType.SHAPE,
|
|
261
|
-
x: 250,
|
|
262
|
-
y: 500,
|
|
263
|
-
width: 150,
|
|
264
|
-
height: 150,
|
|
265
|
-
style: {
|
|
266
|
-
fillStyle: "#16a085",
|
|
267
|
-
strokeStyle: "#138d75",
|
|
268
|
-
lineWidth: 2,
|
|
269
|
-
},
|
|
270
|
-
shadow: {
|
|
271
|
-
color: "rgba(0, 0, 0, 0.5)",
|
|
272
|
-
blur: 10,
|
|
273
|
-
X: 5,
|
|
274
|
-
Y: 5,
|
|
275
|
-
},
|
|
276
|
-
shapes: [
|
|
277
|
-
{ type: "beginPath" },
|
|
278
|
-
{
|
|
279
|
-
type: "arc",
|
|
280
|
-
x: 75,
|
|
281
|
-
y: 75,
|
|
282
|
-
radius: 60,
|
|
283
|
-
startAngle: 0,
|
|
284
|
-
endAngle: Math.PI * 2,
|
|
285
|
-
counterclockwise: false,
|
|
286
|
-
},
|
|
287
|
-
{ type: "fillAndStroke" },
|
|
288
|
-
],
|
|
289
|
-
} as ShapeRenderData,
|
|
290
|
-
|
|
291
|
-
// Complex path with quadratic curves
|
|
292
|
-
{
|
|
293
|
-
id: "curve-shape",
|
|
294
|
-
type: RendererType.SHAPE,
|
|
295
|
-
x: 450,
|
|
296
|
-
y: 500,
|
|
297
|
-
width: 200,
|
|
298
|
-
height: 150,
|
|
299
|
-
style: {
|
|
300
|
-
fillStyle: "#34495e",
|
|
301
|
-
strokeStyle: "#2c3e50",
|
|
302
|
-
lineWidth: 3,
|
|
303
|
-
lineCap: "round",
|
|
304
|
-
lineJoin: "round",
|
|
305
|
-
},
|
|
306
|
-
shapes: [
|
|
307
|
-
{ type: "beginPath" },
|
|
308
|
-
{ type: "moveTo", x: 0, y: 75 },
|
|
309
|
-
{
|
|
310
|
-
type: "quadraticCurveTo",
|
|
311
|
-
cp1x: 50,
|
|
312
|
-
cp1y: 0,
|
|
313
|
-
x: 100,
|
|
314
|
-
y: 75,
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
type: "quadraticCurveTo",
|
|
318
|
-
cp1x: 150,
|
|
319
|
-
cp1y: 150,
|
|
320
|
-
x: 200,
|
|
321
|
-
y: 75,
|
|
322
|
-
},
|
|
323
|
-
{ type: "stroke" },
|
|
324
|
-
],
|
|
325
|
-
} as ShapeRenderData,
|
|
326
|
-
|
|
327
|
-
// Dashed line rectangle
|
|
328
|
-
{
|
|
329
|
-
id: "dashed-rect",
|
|
330
|
-
type: RendererType.SHAPE,
|
|
331
|
-
x: 50,
|
|
332
|
-
y: 700,
|
|
333
|
-
width: 200,
|
|
334
|
-
height: 150,
|
|
335
|
-
style: {
|
|
336
|
-
strokeStyle: "#e74c3c",
|
|
337
|
-
lineWidth: 4,
|
|
338
|
-
lineDash: [10, 5],
|
|
339
|
-
lineDashOffset: 0,
|
|
340
|
-
},
|
|
341
|
-
shapes: [
|
|
342
|
-
{ type: "strokeRect", x: 0, y: 0, width: 200, height: 150 },
|
|
343
|
-
],
|
|
344
|
-
} as ShapeRenderData,
|
|
345
|
-
|
|
346
|
-
// Multiple shapes in one renderer (same style)
|
|
347
|
-
{
|
|
348
|
-
id: "multi-shape",
|
|
349
|
-
type: RendererType.SHAPE,
|
|
350
|
-
x: 300,
|
|
351
|
-
y: 700,
|
|
352
|
-
width: 300,
|
|
353
|
-
height: 200,
|
|
354
|
-
style: {
|
|
355
|
-
fillStyle: "#95a5a6",
|
|
356
|
-
strokeStyle: "#7f8c8d",
|
|
357
|
-
lineWidth: 2,
|
|
358
|
-
},
|
|
359
|
-
shapes: [
|
|
360
|
-
// Background rectangle
|
|
361
|
-
{ type: "fillRect", x: 0, y: 0, width: 300, height: 200 },
|
|
362
|
-
// Circle
|
|
363
|
-
{ type: "beginPath" },
|
|
364
|
-
{
|
|
365
|
-
type: "arc",
|
|
366
|
-
x: 75,
|
|
367
|
-
y: 75,
|
|
368
|
-
radius: 50,
|
|
369
|
-
startAngle: 0,
|
|
370
|
-
endAngle: Math.PI * 2,
|
|
371
|
-
counterclockwise: false,
|
|
372
|
-
},
|
|
373
|
-
{ type: "fill" },
|
|
374
|
-
// Square
|
|
375
|
-
{ type: "fillRect", x: 150, y: 25, width: 100, height: 100 },
|
|
376
|
-
// Triangle
|
|
377
|
-
{ type: "beginPath" },
|
|
378
|
-
{ type: "moveTo", x: 275, y: 25 },
|
|
379
|
-
{ type: "lineTo", x: 300, y: 125 },
|
|
380
|
-
{ type: "lineTo", x: 250, y: 125 },
|
|
381
|
-
{ type: "closePath" },
|
|
382
|
-
{ type: "fill" },
|
|
383
|
-
],
|
|
384
|
-
} as ShapeRenderData,
|
|
385
|
-
],
|
|
386
|
-
output: {
|
|
387
|
-
type: "png",
|
|
388
|
-
},
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
async function main() {
|
|
392
|
-
try {
|
|
393
|
-
const renderer = new Renderer(shapeTestSchema);
|
|
394
|
-
const buffer = await renderer.draw();
|
|
395
|
-
const outputPath = path.join(__dirname, "output", "shape-test.png");
|
|
396
|
-
|
|
397
|
-
// Ensure output directory exists
|
|
398
|
-
const outputDir = path.dirname(outputPath);
|
|
399
|
-
if (!fs.existsSync(outputDir)) {
|
|
400
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
fs.writeFileSync(outputPath, buffer);
|
|
404
|
-
console.log(`Shape test rendered successfully: ${outputPath}`);
|
|
405
|
-
} catch (error) {
|
|
406
|
-
console.error("Error rendering shape test:", error);
|
|
407
|
-
process.exit(1);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
main();
|