@pooder/kit 3.4.0 → 4.0.0
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/CHANGELOG.md +17 -0
- package/dist/index.d.mts +62 -56
- package/dist/index.d.ts +62 -56
- package/dist/index.js +978 -862
- package/dist/index.mjs +977 -861
- package/package.json +2 -2
- package/src/CanvasService.ts +25 -1
- package/src/background.ts +230 -230
- package/src/coordinate.ts +106 -106
- package/src/dieline.ts +272 -218
- package/src/feature.ts +767 -0
- package/src/film.ts +194 -194
- package/src/geometry.ts +172 -375
- package/src/image.ts +512 -471
- package/src/index.ts +1 -1
- package/src/mirror.ts +128 -128
- package/src/ruler.ts +500 -500
- package/src/tracer.ts +570 -486
- package/src/white-ink.ts +373 -373
- package/src/hole.ts +0 -786
package/src/dieline.ts
CHANGED
|
@@ -14,8 +14,7 @@ import {
|
|
|
14
14
|
generateMaskPath,
|
|
15
15
|
generateBleedZonePath,
|
|
16
16
|
getPathBounds,
|
|
17
|
-
|
|
18
|
-
resolveHolePosition,
|
|
17
|
+
DielineFeature,
|
|
19
18
|
} from "./geometry";
|
|
20
19
|
|
|
21
20
|
export interface DielineGeometry {
|
|
@@ -29,6 +28,31 @@ export interface DielineGeometry {
|
|
|
29
28
|
offset: number;
|
|
30
29
|
borderLength?: number;
|
|
31
30
|
scale?: number;
|
|
31
|
+
strokeWidth?: number;
|
|
32
|
+
pathData?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface LineStyle {
|
|
36
|
+
width: number;
|
|
37
|
+
color: string;
|
|
38
|
+
dashLength: number;
|
|
39
|
+
style: "solid" | "dashed" | "hidden";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface DielineState {
|
|
43
|
+
unit: Unit;
|
|
44
|
+
shape: "rect" | "circle" | "ellipse" | "custom";
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
radius: number;
|
|
48
|
+
offset: number;
|
|
49
|
+
padding: number | string;
|
|
50
|
+
mainLine: LineStyle;
|
|
51
|
+
offsetLine: LineStyle;
|
|
52
|
+
insideColor: string;
|
|
53
|
+
outsideColor: string;
|
|
54
|
+
showBleedLines: boolean;
|
|
55
|
+
features: DielineFeature[];
|
|
32
56
|
pathData?: string;
|
|
33
57
|
}
|
|
34
58
|
|
|
@@ -38,46 +62,47 @@ export class DielineTool implements Extension {
|
|
|
38
62
|
name: "DielineTool",
|
|
39
63
|
};
|
|
40
64
|
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
private state: DielineState = {
|
|
66
|
+
unit: "mm",
|
|
67
|
+
shape: "rect",
|
|
68
|
+
width: 500,
|
|
69
|
+
height: 500,
|
|
70
|
+
radius: 0,
|
|
71
|
+
offset: 0,
|
|
72
|
+
padding: 140,
|
|
73
|
+
mainLine: {
|
|
74
|
+
width: 2.7,
|
|
75
|
+
color: "#FF0000",
|
|
76
|
+
dashLength: 5,
|
|
77
|
+
style: "solid",
|
|
78
|
+
},
|
|
79
|
+
offsetLine: {
|
|
80
|
+
width: 2.7,
|
|
81
|
+
color: "#FF0000",
|
|
82
|
+
dashLength: 5,
|
|
83
|
+
style: "solid",
|
|
84
|
+
},
|
|
85
|
+
insideColor: "rgba(0,0,0,0)",
|
|
86
|
+
outsideColor: "#ffffff",
|
|
87
|
+
showBleedLines: true,
|
|
88
|
+
features: [],
|
|
89
|
+
};
|
|
56
90
|
|
|
57
91
|
private canvasService?: CanvasService;
|
|
58
92
|
private context?: ExtensionContext;
|
|
59
93
|
|
|
60
|
-
constructor(
|
|
61
|
-
options?: Partial<{
|
|
62
|
-
unit: Unit;
|
|
63
|
-
shape: "rect" | "circle" | "ellipse" | "custom";
|
|
64
|
-
width: number;
|
|
65
|
-
height: number;
|
|
66
|
-
radius: number;
|
|
67
|
-
// Position is normalized (0-1)
|
|
68
|
-
position: { x: number; y: number };
|
|
69
|
-
padding: number | string;
|
|
70
|
-
offset: number;
|
|
71
|
-
style: "solid" | "dashed";
|
|
72
|
-
insideColor: string;
|
|
73
|
-
outsideColor: string;
|
|
74
|
-
showBleedLines: boolean;
|
|
75
|
-
holes: HoleData[];
|
|
76
|
-
pathData: string;
|
|
77
|
-
}>,
|
|
78
|
-
) {
|
|
94
|
+
constructor(options?: Partial<DielineState>) {
|
|
79
95
|
if (options) {
|
|
80
|
-
|
|
96
|
+
// Deep merge for styles to avoid overwriting defaults with partial objects
|
|
97
|
+
if (options.mainLine) {
|
|
98
|
+
Object.assign(this.state.mainLine, options.mainLine);
|
|
99
|
+
delete options.mainLine;
|
|
100
|
+
}
|
|
101
|
+
if (options.offsetLine) {
|
|
102
|
+
Object.assign(this.state.offsetLine, options.offsetLine);
|
|
103
|
+
delete options.offsetLine;
|
|
104
|
+
}
|
|
105
|
+
Object.assign(this.state, options);
|
|
81
106
|
}
|
|
82
107
|
}
|
|
83
108
|
|
|
@@ -92,40 +117,64 @@ export class DielineTool implements Extension {
|
|
|
92
117
|
const configService = context.services.get<any>("ConfigurationService");
|
|
93
118
|
if (configService) {
|
|
94
119
|
// Load initial config
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
const s = this.state;
|
|
121
|
+
s.unit = configService.get("dieline.unit", s.unit);
|
|
122
|
+
s.shape = configService.get("dieline.shape", s.shape);
|
|
123
|
+
s.width = configService.get("dieline.width", s.width);
|
|
124
|
+
s.height = configService.get("dieline.height", s.height);
|
|
125
|
+
s.radius = configService.get("dieline.radius", s.radius);
|
|
126
|
+
s.padding = configService.get("dieline.padding", s.padding);
|
|
127
|
+
s.offset = configService.get("dieline.offset", s.offset);
|
|
128
|
+
|
|
129
|
+
// Main Line
|
|
130
|
+
s.mainLine.width = configService.get("dieline.strokeWidth", s.mainLine.width);
|
|
131
|
+
s.mainLine.color = configService.get("dieline.strokeColor", s.mainLine.color);
|
|
132
|
+
s.mainLine.dashLength = configService.get("dieline.dashLength", s.mainLine.dashLength);
|
|
133
|
+
s.mainLine.style = configService.get("dieline.style", s.mainLine.style);
|
|
134
|
+
|
|
135
|
+
// Offset Line
|
|
136
|
+
s.offsetLine.width = configService.get("dieline.offsetStrokeWidth", s.offsetLine.width);
|
|
137
|
+
s.offsetLine.color = configService.get("dieline.offsetStrokeColor", s.offsetLine.color);
|
|
138
|
+
s.offsetLine.dashLength = configService.get("dieline.offsetDashLength", s.offsetLine.dashLength);
|
|
139
|
+
s.offsetLine.style = configService.get("dieline.offsetStyle", s.offsetLine.style);
|
|
140
|
+
|
|
141
|
+
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
142
|
+
s.outsideColor = configService.get("dieline.outsideColor", s.outsideColor);
|
|
143
|
+
s.showBleedLines = configService.get("dieline.showBleedLines", s.showBleedLines);
|
|
144
|
+
s.features = configService.get("dieline.features", s.features);
|
|
145
|
+
s.pathData = configService.get("dieline.pathData", s.pathData);
|
|
117
146
|
|
|
118
147
|
// Listen for changes
|
|
119
148
|
configService.onAnyChange((e: { key: string; value: any }) => {
|
|
120
149
|
if (e.key.startsWith("dieline.")) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
150
|
+
console.log(`[DielineTool] Config change detected: ${e.key} -> ${e.value}`);
|
|
151
|
+
|
|
152
|
+
switch (e.key) {
|
|
153
|
+
case "dieline.unit": s.unit = e.value; break;
|
|
154
|
+
case "dieline.shape": s.shape = e.value; break;
|
|
155
|
+
case "dieline.width": s.width = e.value; break;
|
|
156
|
+
case "dieline.height": s.height = e.value; break;
|
|
157
|
+
case "dieline.radius": s.radius = e.value; break;
|
|
158
|
+
case "dieline.padding": s.padding = e.value; break;
|
|
159
|
+
case "dieline.offset": s.offset = e.value; break;
|
|
160
|
+
|
|
161
|
+
case "dieline.strokeWidth": s.mainLine.width = e.value; break;
|
|
162
|
+
case "dieline.strokeColor": s.mainLine.color = e.value; break;
|
|
163
|
+
case "dieline.dashLength": s.mainLine.dashLength = e.value; break;
|
|
164
|
+
case "dieline.style": s.mainLine.style = e.value; break;
|
|
165
|
+
|
|
166
|
+
case "dieline.offsetStrokeWidth": s.offsetLine.width = e.value; break;
|
|
167
|
+
case "dieline.offsetStrokeColor": s.offsetLine.color = e.value; break;
|
|
168
|
+
case "dieline.offsetDashLength": s.offsetLine.dashLength = e.value; break;
|
|
169
|
+
case "dieline.offsetStyle": s.offsetLine.style = e.value; break;
|
|
170
|
+
|
|
171
|
+
case "dieline.insideColor": s.insideColor = e.value; break;
|
|
172
|
+
case "dieline.outsideColor": s.outsideColor = e.value; break;
|
|
173
|
+
case "dieline.showBleedLines": s.showBleedLines = e.value; break;
|
|
174
|
+
case "dieline.features": s.features = e.value; break;
|
|
175
|
+
case "dieline.pathData": s.pathData = e.value; break;
|
|
128
176
|
}
|
|
177
|
+
this.updateDieline();
|
|
129
178
|
}
|
|
130
179
|
});
|
|
131
180
|
}
|
|
@@ -141,6 +190,7 @@ export class DielineTool implements Extension {
|
|
|
141
190
|
}
|
|
142
191
|
|
|
143
192
|
contribute() {
|
|
193
|
+
const s = this.state;
|
|
144
194
|
return {
|
|
145
195
|
[ContributionPointIds.CONFIGURATIONS]: [
|
|
146
196
|
{
|
|
@@ -148,14 +198,14 @@ export class DielineTool implements Extension {
|
|
|
148
198
|
type: "select",
|
|
149
199
|
label: "Unit",
|
|
150
200
|
options: ["px", "mm", "cm", "in"],
|
|
151
|
-
default:
|
|
201
|
+
default: s.unit,
|
|
152
202
|
},
|
|
153
203
|
{
|
|
154
204
|
id: "dieline.shape",
|
|
155
205
|
type: "select",
|
|
156
206
|
label: "Shape",
|
|
157
207
|
options: ["rect", "circle", "ellipse", "custom"],
|
|
158
|
-
default:
|
|
208
|
+
default: s.shape,
|
|
159
209
|
},
|
|
160
210
|
{
|
|
161
211
|
id: "dieline.width",
|
|
@@ -163,7 +213,7 @@ export class DielineTool implements Extension {
|
|
|
163
213
|
label: "Width",
|
|
164
214
|
min: 10,
|
|
165
215
|
max: 2000,
|
|
166
|
-
default:
|
|
216
|
+
default: s.width,
|
|
167
217
|
},
|
|
168
218
|
{
|
|
169
219
|
id: "dieline.height",
|
|
@@ -171,7 +221,7 @@ export class DielineTool implements Extension {
|
|
|
171
221
|
label: "Height",
|
|
172
222
|
min: 10,
|
|
173
223
|
max: 2000,
|
|
174
|
-
default:
|
|
224
|
+
default: s.height,
|
|
175
225
|
},
|
|
176
226
|
{
|
|
177
227
|
id: "dieline.radius",
|
|
@@ -179,20 +229,14 @@ export class DielineTool implements Extension {
|
|
|
179
229
|
label: "Corner Radius",
|
|
180
230
|
min: 0,
|
|
181
231
|
max: 500,
|
|
182
|
-
default:
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
id: "dieline.position",
|
|
186
|
-
type: "json",
|
|
187
|
-
label: "Position (Normalized)",
|
|
188
|
-
default: this.radius,
|
|
232
|
+
default: s.radius,
|
|
189
233
|
},
|
|
190
234
|
{
|
|
191
235
|
id: "dieline.padding",
|
|
192
236
|
type: "select",
|
|
193
237
|
label: "View Padding",
|
|
194
238
|
options: [0, 10, 20, 40, 60, 100, "2%", "5%", "10%", "15%", "20%"],
|
|
195
|
-
default:
|
|
239
|
+
default: s.padding,
|
|
196
240
|
},
|
|
197
241
|
{
|
|
198
242
|
id: "dieline.offset",
|
|
@@ -200,38 +244,91 @@ export class DielineTool implements Extension {
|
|
|
200
244
|
label: "Bleed Offset",
|
|
201
245
|
min: -100,
|
|
202
246
|
max: 100,
|
|
203
|
-
default:
|
|
247
|
+
default: s.offset,
|
|
204
248
|
},
|
|
205
249
|
{
|
|
206
250
|
id: "dieline.showBleedLines",
|
|
207
251
|
type: "boolean",
|
|
208
252
|
label: "Show Bleed Lines",
|
|
209
|
-
default:
|
|
253
|
+
default: s.showBleedLines,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
id: "dieline.strokeWidth",
|
|
257
|
+
type: "number",
|
|
258
|
+
label: "Line Width",
|
|
259
|
+
min: 0.1,
|
|
260
|
+
max: 10,
|
|
261
|
+
step: 0.1,
|
|
262
|
+
default: s.mainLine.width,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: "dieline.strokeColor",
|
|
266
|
+
type: "color",
|
|
267
|
+
label: "Line Color",
|
|
268
|
+
default: s.mainLine.color,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: "dieline.dashLength",
|
|
272
|
+
type: "number",
|
|
273
|
+
label: "Dash Length",
|
|
274
|
+
min: 1,
|
|
275
|
+
max: 50,
|
|
276
|
+
default: s.mainLine.dashLength,
|
|
210
277
|
},
|
|
211
278
|
{
|
|
212
279
|
id: "dieline.style",
|
|
213
280
|
type: "select",
|
|
214
281
|
label: "Line Style",
|
|
215
|
-
options: ["solid", "dashed"],
|
|
216
|
-
default:
|
|
282
|
+
options: ["solid", "dashed", "hidden"],
|
|
283
|
+
default: s.mainLine.style,
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: "dieline.offsetStrokeWidth",
|
|
287
|
+
type: "number",
|
|
288
|
+
label: "Offset Line Width",
|
|
289
|
+
min: 0.1,
|
|
290
|
+
max: 10,
|
|
291
|
+
step: 0.1,
|
|
292
|
+
default: s.offsetLine.width,
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: "dieline.offsetStrokeColor",
|
|
296
|
+
type: "color",
|
|
297
|
+
label: "Offset Line Color",
|
|
298
|
+
default: s.offsetLine.color,
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
id: "dieline.offsetDashLength",
|
|
302
|
+
type: "number",
|
|
303
|
+
label: "Offset Dash Length",
|
|
304
|
+
min: 1,
|
|
305
|
+
max: 50,
|
|
306
|
+
default: s.offsetLine.dashLength,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
id: "dieline.offsetStyle",
|
|
310
|
+
type: "select",
|
|
311
|
+
label: "Offset Line Style",
|
|
312
|
+
options: ["solid", "dashed", "hidden"],
|
|
313
|
+
default: s.offsetLine.style,
|
|
217
314
|
},
|
|
218
315
|
{
|
|
219
316
|
id: "dieline.insideColor",
|
|
220
317
|
type: "color",
|
|
221
318
|
label: "Inside Color",
|
|
222
|
-
default:
|
|
319
|
+
default: s.insideColor,
|
|
223
320
|
},
|
|
224
321
|
{
|
|
225
322
|
id: "dieline.outsideColor",
|
|
226
323
|
type: "color",
|
|
227
324
|
label: "Outside Color",
|
|
228
|
-
default:
|
|
325
|
+
default: s.outsideColor,
|
|
229
326
|
},
|
|
230
327
|
{
|
|
231
|
-
id: "dieline.
|
|
328
|
+
id: "dieline.features",
|
|
232
329
|
type: "json",
|
|
233
|
-
label: "
|
|
234
|
-
default:
|
|
330
|
+
label: "Edge Features",
|
|
331
|
+
default: s.features,
|
|
235
332
|
},
|
|
236
333
|
] as ConfigurationContribution[],
|
|
237
334
|
[ContributionPointIds.COMMANDS]: [
|
|
@@ -257,23 +354,17 @@ export class DielineTool implements Extension {
|
|
|
257
354
|
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
258
355
|
const bounds = getPathBounds(pathData);
|
|
259
356
|
|
|
260
|
-
const currentMax = Math.max(
|
|
357
|
+
const currentMax = Math.max(s.width, s.height);
|
|
261
358
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
262
359
|
|
|
263
360
|
const newWidth = bounds.width * scale;
|
|
264
361
|
const newHeight = bounds.height * scale;
|
|
265
362
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
configService.update("dieline.height", newHeight);
|
|
272
|
-
configService.update("dieline.shape", "custom");
|
|
273
|
-
configService.update("dieline.pathData", pathData);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return pathData;
|
|
363
|
+
return {
|
|
364
|
+
pathData,
|
|
365
|
+
width: newWidth,
|
|
366
|
+
height: newHeight,
|
|
367
|
+
};
|
|
277
368
|
} catch (e) {
|
|
278
369
|
console.error("Edge detection failed", e);
|
|
279
370
|
throw e;
|
|
@@ -349,15 +440,15 @@ export class DielineTool implements Extension {
|
|
|
349
440
|
containerWidth: number,
|
|
350
441
|
containerHeight: number,
|
|
351
442
|
): number {
|
|
352
|
-
if (typeof this.padding === "number") {
|
|
353
|
-
return this.padding;
|
|
443
|
+
if (typeof this.state.padding === "number") {
|
|
444
|
+
return this.state.padding;
|
|
354
445
|
}
|
|
355
|
-
if (typeof this.padding === "string") {
|
|
356
|
-
if (this.padding.endsWith("%")) {
|
|
357
|
-
const percent = parseFloat(this.padding) / 100;
|
|
446
|
+
if (typeof this.state.padding === "string") {
|
|
447
|
+
if (this.state.padding.endsWith("%")) {
|
|
448
|
+
const percent = parseFloat(this.state.padding) / 100;
|
|
358
449
|
return Math.min(containerWidth, containerHeight) * percent;
|
|
359
450
|
}
|
|
360
|
-
return parseFloat(this.padding) || 0;
|
|
451
|
+
return parseFloat(this.state.padding) || 0;
|
|
361
452
|
}
|
|
362
453
|
return 0;
|
|
363
454
|
}
|
|
@@ -372,14 +463,14 @@ export class DielineTool implements Extension {
|
|
|
372
463
|
shape,
|
|
373
464
|
radius,
|
|
374
465
|
offset,
|
|
375
|
-
|
|
466
|
+
mainLine,
|
|
467
|
+
offsetLine,
|
|
376
468
|
insideColor,
|
|
377
469
|
outsideColor,
|
|
378
|
-
position,
|
|
379
470
|
showBleedLines,
|
|
380
|
-
|
|
381
|
-
} = this;
|
|
382
|
-
let { width, height } = this;
|
|
471
|
+
features,
|
|
472
|
+
} = this.state;
|
|
473
|
+
let { width, height } = this.state;
|
|
383
474
|
|
|
384
475
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
385
476
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
@@ -406,45 +497,24 @@ export class DielineTool implements Extension {
|
|
|
406
497
|
// Clear existing objects
|
|
407
498
|
layer.remove(...layer.getObjects());
|
|
408
499
|
|
|
409
|
-
//
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
width: visualWidth,
|
|
414
|
-
height: visualHeight,
|
|
415
|
-
// Pass scale/unit context if needed by resolveHolePosition (though currently unused there)
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
const absoluteHoles = (holes || []).map((h) => {
|
|
419
|
-
// Scale hole radii and offsets: mm -> current unit -> pixels
|
|
420
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
421
|
-
const offsetScale = unitScale * scale;
|
|
422
|
-
|
|
423
|
-
// Apply scaling to offsets BEFORE resolving position
|
|
424
|
-
const hWithPixelOffsets = {
|
|
425
|
-
...h,
|
|
426
|
-
offsetX: (h.offsetX || 0) * offsetScale,
|
|
427
|
-
offsetY: (h.offsetY || 0) * offsetScale,
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
const pos = resolveHolePosition(hWithPixelOffsets, geometryForHoles, {
|
|
431
|
-
width: canvasW,
|
|
432
|
-
height: canvasH,
|
|
433
|
-
});
|
|
500
|
+
// Scale Features for Geometry Generation
|
|
501
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
502
|
+
// Scale current unit -> pixels (features share the same unit as the dieline)
|
|
503
|
+
const featureScale = scale;
|
|
434
504
|
|
|
435
505
|
return {
|
|
436
|
-
...
|
|
437
|
-
x:
|
|
438
|
-
y:
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
// Store scaled offsets in the result for consistency, though pos is already resolved
|
|
443
|
-
offsetX: hWithPixelOffsets.offsetX,
|
|
444
|
-
offsetY: hWithPixelOffsets.offsetY,
|
|
506
|
+
...f,
|
|
507
|
+
x: f.x,
|
|
508
|
+
y: f.y,
|
|
509
|
+
width: (f.width || 0) * featureScale,
|
|
510
|
+
height: (f.height || 0) * featureScale,
|
|
511
|
+
radius: (f.radius || 0) * featureScale,
|
|
445
512
|
};
|
|
446
513
|
});
|
|
447
514
|
|
|
515
|
+
// Split features into Cut (Physical) and Visual (All)
|
|
516
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
517
|
+
|
|
448
518
|
// 1. Draw Mask (Outside)
|
|
449
519
|
const cutW = Math.max(0, visualWidth + visualOffset * 2);
|
|
450
520
|
const cutH = Math.max(0, visualHeight + visualOffset * 2);
|
|
@@ -461,8 +531,8 @@ export class DielineTool implements Extension {
|
|
|
461
531
|
radius: cutR,
|
|
462
532
|
x: cx,
|
|
463
533
|
y: cy,
|
|
464
|
-
|
|
465
|
-
pathData: this.pathData,
|
|
534
|
+
features: cutFeatures,
|
|
535
|
+
pathData: this.state.pathData,
|
|
466
536
|
});
|
|
467
537
|
|
|
468
538
|
const mask = new Path(maskPathData, {
|
|
@@ -477,13 +547,13 @@ export class DielineTool implements Extension {
|
|
|
477
547
|
});
|
|
478
548
|
layer.add(mask);
|
|
479
549
|
|
|
480
|
-
// 2. Draw Inside Fill (Dieline Shape itself, merged with
|
|
550
|
+
// 2. Draw Inside Fill (Dieline Shape itself, merged with features if needed)
|
|
481
551
|
if (
|
|
482
552
|
insideColor &&
|
|
483
553
|
insideColor !== "transparent" &&
|
|
484
554
|
insideColor !== "rgba(0,0,0,0)"
|
|
485
555
|
) {
|
|
486
|
-
// Generate path for the product shape (Paper) = Dieline
|
|
556
|
+
// Generate path for the product shape (Paper) = Dieline +/- Features
|
|
487
557
|
const productPathData = generateDielinePath({
|
|
488
558
|
shape,
|
|
489
559
|
width: cutW,
|
|
@@ -491,8 +561,8 @@ export class DielineTool implements Extension {
|
|
|
491
561
|
radius: cutR,
|
|
492
562
|
x: cx,
|
|
493
563
|
y: cy,
|
|
494
|
-
|
|
495
|
-
pathData: this.pathData,
|
|
564
|
+
features: cutFeatures, // Use same features as mask for consistency
|
|
565
|
+
pathData: this.state.pathData,
|
|
496
566
|
canvasWidth: canvasW,
|
|
497
567
|
canvasHeight: canvasH,
|
|
498
568
|
});
|
|
@@ -518,8 +588,20 @@ export class DielineTool implements Extension {
|
|
|
518
588
|
radius: visualRadius,
|
|
519
589
|
x: cx,
|
|
520
590
|
y: cy,
|
|
521
|
-
|
|
522
|
-
pathData: this.pathData,
|
|
591
|
+
features: cutFeatures,
|
|
592
|
+
pathData: this.state.pathData,
|
|
593
|
+
canvasWidth: canvasW,
|
|
594
|
+
canvasHeight: canvasH,
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
shape,
|
|
598
|
+
width: cutW,
|
|
599
|
+
height: cutH,
|
|
600
|
+
radius: cutR,
|
|
601
|
+
x: cx,
|
|
602
|
+
y: cy,
|
|
603
|
+
features: cutFeatures,
|
|
604
|
+
pathData: this.state.pathData,
|
|
523
605
|
canvasWidth: canvasW,
|
|
524
606
|
canvasHeight: canvasH,
|
|
525
607
|
},
|
|
@@ -528,7 +610,7 @@ export class DielineTool implements Extension {
|
|
|
528
610
|
|
|
529
611
|
// Use solid red for hatch lines to match dieline, background is transparent
|
|
530
612
|
if (showBleedLines !== false) {
|
|
531
|
-
const pattern = this.createHatchPattern(
|
|
613
|
+
const pattern = this.createHatchPattern(mainLine.color);
|
|
532
614
|
if (pattern) {
|
|
533
615
|
const bleedObj = new Path(bleedPathData, {
|
|
534
616
|
fill: pattern,
|
|
@@ -551,17 +633,20 @@ export class DielineTool implements Extension {
|
|
|
551
633
|
radius: cutR,
|
|
552
634
|
x: cx,
|
|
553
635
|
y: cy,
|
|
554
|
-
|
|
555
|
-
pathData: this.pathData,
|
|
636
|
+
features: cutFeatures,
|
|
637
|
+
pathData: this.state.pathData,
|
|
556
638
|
canvasWidth: canvasW,
|
|
557
639
|
canvasHeight: canvasH,
|
|
558
640
|
});
|
|
559
641
|
|
|
560
642
|
const offsetBorderObj = new Path(offsetPathData, {
|
|
561
643
|
fill: null,
|
|
562
|
-
stroke: "
|
|
563
|
-
strokeWidth:
|
|
564
|
-
strokeDashArray:
|
|
644
|
+
stroke: offsetLine.style === "hidden" ? null : offsetLine.color,
|
|
645
|
+
strokeWidth: offsetLine.width,
|
|
646
|
+
strokeDashArray:
|
|
647
|
+
offsetLine.style === "dashed"
|
|
648
|
+
? [offsetLine.dashLength, offsetLine.dashLength]
|
|
649
|
+
: undefined,
|
|
565
650
|
selectable: false,
|
|
566
651
|
evented: false,
|
|
567
652
|
originX: "left",
|
|
@@ -571,9 +656,6 @@ export class DielineTool implements Extension {
|
|
|
571
656
|
}
|
|
572
657
|
|
|
573
658
|
// 4. Draw Dieline (Visual Border)
|
|
574
|
-
// This should outline the product shape AND the holes.
|
|
575
|
-
// NOTE: We need to use absoluteHoles (denormalized) here, NOT holes (normalized 0-1)
|
|
576
|
-
// generateDielinePath expects holes to be in absolute coordinates (matching width/height scale)
|
|
577
659
|
const borderPathData = generateDielinePath({
|
|
578
660
|
shape,
|
|
579
661
|
width: visualWidth,
|
|
@@ -581,17 +663,18 @@ export class DielineTool implements Extension {
|
|
|
581
663
|
radius: visualRadius,
|
|
582
664
|
x: cx,
|
|
583
665
|
y: cy,
|
|
584
|
-
|
|
585
|
-
pathData: this.pathData,
|
|
666
|
+
features: absoluteFeatures,
|
|
667
|
+
pathData: this.state.pathData,
|
|
586
668
|
canvasWidth: canvasW,
|
|
587
669
|
canvasHeight: canvasH,
|
|
588
670
|
});
|
|
589
671
|
|
|
590
672
|
const borderObj = new Path(borderPathData, {
|
|
591
673
|
fill: "transparent",
|
|
592
|
-
stroke: "
|
|
593
|
-
strokeWidth:
|
|
594
|
-
strokeDashArray:
|
|
674
|
+
stroke: mainLine.style === "hidden" ? null : mainLine.color,
|
|
675
|
+
strokeWidth: mainLine.width,
|
|
676
|
+
strokeDashArray:
|
|
677
|
+
mainLine.style === "dashed" ? [mainLine.dashLength, mainLine.dashLength] : undefined,
|
|
595
678
|
selectable: false,
|
|
596
679
|
evented: false,
|
|
597
680
|
originX: "left",
|
|
@@ -624,16 +707,8 @@ export class DielineTool implements Extension {
|
|
|
624
707
|
layer.dirty = true;
|
|
625
708
|
this.canvasService.requestRenderAll();
|
|
626
709
|
|
|
627
|
-
// Emit change event so other tools (like
|
|
628
|
-
// Only emit if requested (to avoid loops when updating non-geometry props like holes)
|
|
710
|
+
// Emit change event so other tools (like FeatureTool) can react
|
|
629
711
|
if (emitEvent && this.context) {
|
|
630
|
-
// FIX: Ensure we use the exact same geometry values as used in rendering above.
|
|
631
|
-
// Although getGeometry() recalculates layout, it should be identical if props haven't changed.
|
|
632
|
-
// But to be absolutely safe and avoid micro-differences or race conditions, we can reuse calculated values.
|
|
633
|
-
// However, getGeometry is public API, so it's better if it's correct.
|
|
634
|
-
// Let's verify getGeometry logic matches updateDieline logic perfectly.
|
|
635
|
-
// Yes, both use Coordinate.calculateLayout with the same inputs.
|
|
636
|
-
|
|
637
712
|
const geometry = this.getGeometry();
|
|
638
713
|
if (geometry) {
|
|
639
714
|
this.context.eventBus.emit("dieline:geometry:change", geometry);
|
|
@@ -643,7 +718,7 @@ export class DielineTool implements Extension {
|
|
|
643
718
|
|
|
644
719
|
public getGeometry(): DielineGeometry | null {
|
|
645
720
|
if (!this.canvasService) return null;
|
|
646
|
-
const { unit, shape, width, height, radius,
|
|
721
|
+
const { unit, shape, width, height, radius, offset, mainLine, pathData } = this.state;
|
|
647
722
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
648
723
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
649
724
|
|
|
@@ -670,9 +745,10 @@ export class DielineTool implements Extension {
|
|
|
670
745
|
height: visualHeight,
|
|
671
746
|
radius: radius * scale,
|
|
672
747
|
offset: offset * scale,
|
|
673
|
-
// Pass scale to help other tools (like
|
|
748
|
+
// Pass scale to help other tools (like FeatureTool) convert units
|
|
674
749
|
scale,
|
|
675
|
-
|
|
750
|
+
strokeWidth: mainLine.width,
|
|
751
|
+
pathData,
|
|
676
752
|
} as DielineGeometry;
|
|
677
753
|
}
|
|
678
754
|
|
|
@@ -680,13 +756,10 @@ export class DielineTool implements Extension {
|
|
|
680
756
|
if (!this.canvasService) return null;
|
|
681
757
|
const userLayer = this.canvasService.getLayer("user");
|
|
682
758
|
|
|
683
|
-
// Even if no user images, we might want to export the shape?
|
|
684
|
-
// But usually "Cut Image" implies the printed content.
|
|
685
|
-
// If empty, maybe just return null or empty string.
|
|
686
759
|
if (!userLayer) return null;
|
|
687
760
|
|
|
688
761
|
// 1. Generate Path Data
|
|
689
|
-
const { shape, width, height, radius,
|
|
762
|
+
const { shape, width, height, radius, features, unit, pathData } = this.state;
|
|
690
763
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
691
764
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
692
765
|
|
|
@@ -703,74 +776,55 @@ export class DielineTool implements Extension {
|
|
|
703
776
|
const visualHeight = layout.height;
|
|
704
777
|
const visualRadius = radius * scale;
|
|
705
778
|
|
|
706
|
-
//
|
|
707
|
-
const
|
|
708
|
-
const
|
|
709
|
-
const unitScale = Coordinate.convertUnit(1, "mm", unit);
|
|
710
|
-
|
|
711
|
-
const pos = resolveHolePosition(
|
|
712
|
-
{
|
|
713
|
-
...h,
|
|
714
|
-
offsetX: (h.offsetX || 0) * unitScale * scale,
|
|
715
|
-
offsetY: (h.offsetY || 0) * unitScale * scale,
|
|
716
|
-
},
|
|
717
|
-
{ x: cx, y: cy, width: visualWidth, height: visualHeight },
|
|
718
|
-
{ width: canvasW, height: canvasH },
|
|
719
|
-
);
|
|
779
|
+
// Scale Features
|
|
780
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
781
|
+
const featureScale = scale;
|
|
720
782
|
|
|
721
783
|
return {
|
|
722
|
-
...
|
|
723
|
-
x:
|
|
724
|
-
y:
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
offsetY: (h.offsetY || 0) * unitScale * scale,
|
|
784
|
+
...f,
|
|
785
|
+
x: f.x,
|
|
786
|
+
y: f.y,
|
|
787
|
+
width: (f.width || 0) * featureScale,
|
|
788
|
+
height: (f.height || 0) * featureScale,
|
|
789
|
+
radius: (f.radius || 0) * featureScale,
|
|
729
790
|
};
|
|
730
791
|
});
|
|
731
792
|
|
|
732
|
-
const
|
|
793
|
+
const cutFeatures = absoluteFeatures.filter((f) => !f.skipCut);
|
|
794
|
+
|
|
795
|
+
const generatedPathData = generateDielinePath({
|
|
733
796
|
shape,
|
|
734
797
|
width: visualWidth,
|
|
735
798
|
height: visualHeight,
|
|
736
799
|
radius: visualRadius,
|
|
737
800
|
x: cx,
|
|
738
801
|
y: cy,
|
|
739
|
-
|
|
740
|
-
pathData
|
|
802
|
+
features: cutFeatures,
|
|
803
|
+
pathData,
|
|
741
804
|
canvasWidth: canvasW,
|
|
742
805
|
canvasHeight: canvasH,
|
|
743
806
|
});
|
|
744
807
|
|
|
745
808
|
// 2. Prepare for Export
|
|
746
|
-
// Clone the layer to not affect the stage
|
|
747
809
|
const clonedLayer = await userLayer.clone();
|
|
748
810
|
|
|
749
|
-
|
|
750
|
-
// Note: In Fabric, clipPath is relative to the object center usually,
|
|
751
|
-
// but for a Group that is full-canvas (left=0, top=0), absolute coordinates should work
|
|
752
|
-
// if we configure it correctly.
|
|
753
|
-
// However, Fabric's clipPath handling can be tricky with Groups.
|
|
754
|
-
// Safest bet: Position the clipPath absolutely and ensuring group is absolute.
|
|
755
|
-
|
|
756
|
-
const clipPath = new Path(pathData, {
|
|
811
|
+
const clipPath = new Path(generatedPathData, {
|
|
757
812
|
originX: "left",
|
|
758
813
|
originY: "top",
|
|
759
814
|
left: 0,
|
|
760
815
|
top: 0,
|
|
761
|
-
absolutePositioned: true,
|
|
816
|
+
absolutePositioned: true,
|
|
762
817
|
});
|
|
763
818
|
|
|
764
819
|
clonedLayer.clipPath = clipPath;
|
|
765
820
|
|
|
766
821
|
// 3. Calculate Crop Area (The Dieline Bounds)
|
|
767
|
-
// We want to export only the area covered by the dieline
|
|
768
822
|
const bounds = clipPath.getBoundingRect();
|
|
769
823
|
|
|
770
824
|
// 4. Export
|
|
771
825
|
const dataUrl = clonedLayer.toDataURL({
|
|
772
826
|
format: "png",
|
|
773
|
-
multiplier: 2,
|
|
827
|
+
multiplier: 2,
|
|
774
828
|
left: bounds.left,
|
|
775
829
|
top: bounds.top,
|
|
776
830
|
width: bounds.width,
|