declare-render 1.0.2 → 1.0.4
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/arrow-test.ts +252 -0
- package/examples/output/arrow-test.png +0 -0
- package/examples/output/shape-test.png +0 -0
- package/examples/shape-test.ts +411 -0
- package/package.json +6 -2
- package/src/canvas-renderers/base-renderer.ts +11 -4
- package/src/canvas-renderers/container-renderer/index.ts +20 -23
- package/src/canvas-renderers/img-renderer/index.ts +2 -26
- package/src/canvas-renderers/shape-render/index.ts +279 -0
- package/src/canvas-renderers/text-renderer/types.ts +2 -44
- package/src/index.ts +11 -2
- package/src/types.ts +174 -12
- package/tsconfig.json +5 -3
- package/examples/sunscreen-guide.ts +0 -206
|
@@ -0,0 +1,252 @@
|
|
|
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
|
+
});
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,411 @@
|
|
|
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();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "declare-render",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "You can declare canvas shapes by JSON format.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
},
|
|
24
24
|
"type": "module",
|
|
25
25
|
"scripts": {
|
|
26
|
-
"rebuild:canvas": "pnpm rebuild canvas"
|
|
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
31
|
}
|
|
28
32
|
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type {
|
|
2
|
+
ContainerRenderData,
|
|
3
|
+
ImgRenderData,
|
|
4
|
+
ShapeRenderData,
|
|
5
|
+
TextRenderData,
|
|
6
|
+
} from "../types";
|
|
4
7
|
|
|
5
8
|
export abstract class BaseRender<
|
|
6
|
-
T extends
|
|
9
|
+
T extends
|
|
10
|
+
| TextRenderData
|
|
11
|
+
| ImgRenderData
|
|
12
|
+
| ContainerRenderData
|
|
13
|
+
| ShapeRenderData,
|
|
7
14
|
> {
|
|
8
15
|
protected data!: T;
|
|
9
16
|
abstract get container(): { x1: number; x2: number; y1: number; y2: number };
|