@pooder/kit 3.3.0 → 3.5.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 +12 -0
- package/dist/index.d.mts +53 -57
- package/dist/index.d.ts +53 -57
- package/dist/index.js +1081 -930
- package/dist/index.mjs +1080 -929
- package/package.json +1 -1
- package/src/CanvasService.ts +65 -65
- package/src/background.ts +230 -230
- package/src/coordinate.ts +106 -106
- package/src/dieline.ts +282 -218
- package/src/feature.ts +724 -0
- package/src/film.ts +194 -194
- package/src/geometry.ts +118 -370
- package/src/image.ts +471 -496
- package/src/index.ts +1 -1
- package/src/mirror.ts +128 -128
- package/src/ruler.ts +500 -500
- package/src/tracer.ts +570 -372
- 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
|
+
EdgeFeature,
|
|
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: EdgeFeature[];
|
|
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,51 +497,38 @@ 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
|
+
const originalFeatures = absoluteFeatures.filter(
|
|
516
|
+
(f) => !f.target || f.target === "original" || f.target === "both",
|
|
517
|
+
);
|
|
518
|
+
const offsetFeatures = absoluteFeatures.filter(
|
|
519
|
+
(f) => f.target === "offset" || f.target === "both",
|
|
520
|
+
);
|
|
521
|
+
|
|
448
522
|
// 1. Draw Mask (Outside)
|
|
449
523
|
const cutW = Math.max(0, visualWidth + visualOffset * 2);
|
|
450
524
|
const cutH = Math.max(0, visualHeight + visualOffset * 2);
|
|
451
525
|
const cutR =
|
|
452
526
|
visualRadius === 0 ? 0 : Math.max(0, visualRadius + visualOffset);
|
|
453
527
|
|
|
528
|
+
// If no bleed offset, mask should match the original dieline (including its features)
|
|
529
|
+
// If bleed offset exists (positive or negative), mask matches bleed line (which only includes offset features)
|
|
530
|
+
const maskFeatures = visualOffset !== 0 ? offsetFeatures : originalFeatures;
|
|
531
|
+
|
|
454
532
|
// Use Paper.js to generate the complex mask path
|
|
455
533
|
const maskPathData = generateMaskPath({
|
|
456
534
|
canvasWidth: canvasW,
|
|
@@ -461,8 +539,8 @@ export class DielineTool implements Extension {
|
|
|
461
539
|
radius: cutR,
|
|
462
540
|
x: cx,
|
|
463
541
|
y: cy,
|
|
464
|
-
|
|
465
|
-
pathData: this.pathData,
|
|
542
|
+
features: maskFeatures,
|
|
543
|
+
pathData: this.state.pathData,
|
|
466
544
|
});
|
|
467
545
|
|
|
468
546
|
const mask = new Path(maskPathData, {
|
|
@@ -477,13 +555,13 @@ export class DielineTool implements Extension {
|
|
|
477
555
|
});
|
|
478
556
|
layer.add(mask);
|
|
479
557
|
|
|
480
|
-
// 2. Draw Inside Fill (Dieline Shape itself, merged with
|
|
558
|
+
// 2. Draw Inside Fill (Dieline Shape itself, merged with features if needed)
|
|
481
559
|
if (
|
|
482
560
|
insideColor &&
|
|
483
561
|
insideColor !== "transparent" &&
|
|
484
562
|
insideColor !== "rgba(0,0,0,0)"
|
|
485
563
|
) {
|
|
486
|
-
// Generate path for the product shape (Paper) = Dieline
|
|
564
|
+
// Generate path for the product shape (Paper) = Dieline +/- Features
|
|
487
565
|
const productPathData = generateDielinePath({
|
|
488
566
|
shape,
|
|
489
567
|
width: cutW,
|
|
@@ -491,8 +569,8 @@ export class DielineTool implements Extension {
|
|
|
491
569
|
radius: cutR,
|
|
492
570
|
x: cx,
|
|
493
571
|
y: cy,
|
|
494
|
-
|
|
495
|
-
pathData: this.pathData,
|
|
572
|
+
features: maskFeatures, // Use same features as mask for consistency
|
|
573
|
+
pathData: this.state.pathData,
|
|
496
574
|
canvasWidth: canvasW,
|
|
497
575
|
canvasHeight: canvasH,
|
|
498
576
|
});
|
|
@@ -518,8 +596,20 @@ export class DielineTool implements Extension {
|
|
|
518
596
|
radius: visualRadius,
|
|
519
597
|
x: cx,
|
|
520
598
|
y: cy,
|
|
521
|
-
|
|
522
|
-
pathData: this.pathData,
|
|
599
|
+
features: originalFeatures,
|
|
600
|
+
pathData: this.state.pathData,
|
|
601
|
+
canvasWidth: canvasW,
|
|
602
|
+
canvasHeight: canvasH,
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
shape,
|
|
606
|
+
width: cutW,
|
|
607
|
+
height: cutH,
|
|
608
|
+
radius: cutR,
|
|
609
|
+
x: cx,
|
|
610
|
+
y: cy,
|
|
611
|
+
features: offsetFeatures,
|
|
612
|
+
pathData: this.state.pathData,
|
|
523
613
|
canvasWidth: canvasW,
|
|
524
614
|
canvasHeight: canvasH,
|
|
525
615
|
},
|
|
@@ -528,7 +618,7 @@ export class DielineTool implements Extension {
|
|
|
528
618
|
|
|
529
619
|
// Use solid red for hatch lines to match dieline, background is transparent
|
|
530
620
|
if (showBleedLines !== false) {
|
|
531
|
-
const pattern = this.createHatchPattern(
|
|
621
|
+
const pattern = this.createHatchPattern(mainLine.color);
|
|
532
622
|
if (pattern) {
|
|
533
623
|
const bleedObj = new Path(bleedPathData, {
|
|
534
624
|
fill: pattern,
|
|
@@ -551,17 +641,20 @@ export class DielineTool implements Extension {
|
|
|
551
641
|
radius: cutR,
|
|
552
642
|
x: cx,
|
|
553
643
|
y: cy,
|
|
554
|
-
|
|
555
|
-
pathData: this.pathData,
|
|
644
|
+
features: offsetFeatures,
|
|
645
|
+
pathData: this.state.pathData,
|
|
556
646
|
canvasWidth: canvasW,
|
|
557
647
|
canvasHeight: canvasH,
|
|
558
648
|
});
|
|
559
649
|
|
|
560
650
|
const offsetBorderObj = new Path(offsetPathData, {
|
|
561
651
|
fill: null,
|
|
562
|
-
stroke: "
|
|
563
|
-
strokeWidth:
|
|
564
|
-
strokeDashArray:
|
|
652
|
+
stroke: offsetLine.style === "hidden" ? null : offsetLine.color,
|
|
653
|
+
strokeWidth: offsetLine.width,
|
|
654
|
+
strokeDashArray:
|
|
655
|
+
offsetLine.style === "dashed"
|
|
656
|
+
? [offsetLine.dashLength, offsetLine.dashLength]
|
|
657
|
+
: undefined,
|
|
565
658
|
selectable: false,
|
|
566
659
|
evented: false,
|
|
567
660
|
originX: "left",
|
|
@@ -571,9 +664,6 @@ export class DielineTool implements Extension {
|
|
|
571
664
|
}
|
|
572
665
|
|
|
573
666
|
// 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
667
|
const borderPathData = generateDielinePath({
|
|
578
668
|
shape,
|
|
579
669
|
width: visualWidth,
|
|
@@ -581,17 +671,18 @@ export class DielineTool implements Extension {
|
|
|
581
671
|
radius: visualRadius,
|
|
582
672
|
x: cx,
|
|
583
673
|
y: cy,
|
|
584
|
-
|
|
585
|
-
pathData: this.pathData,
|
|
674
|
+
features: originalFeatures,
|
|
675
|
+
pathData: this.state.pathData,
|
|
586
676
|
canvasWidth: canvasW,
|
|
587
677
|
canvasHeight: canvasH,
|
|
588
678
|
});
|
|
589
679
|
|
|
590
680
|
const borderObj = new Path(borderPathData, {
|
|
591
681
|
fill: "transparent",
|
|
592
|
-
stroke: "
|
|
593
|
-
strokeWidth:
|
|
594
|
-
strokeDashArray:
|
|
682
|
+
stroke: mainLine.style === "hidden" ? null : mainLine.color,
|
|
683
|
+
strokeWidth: mainLine.width,
|
|
684
|
+
strokeDashArray:
|
|
685
|
+
mainLine.style === "dashed" ? [mainLine.dashLength, mainLine.dashLength] : undefined,
|
|
595
686
|
selectable: false,
|
|
596
687
|
evented: false,
|
|
597
688
|
originX: "left",
|
|
@@ -624,16 +715,8 @@ export class DielineTool implements Extension {
|
|
|
624
715
|
layer.dirty = true;
|
|
625
716
|
this.canvasService.requestRenderAll();
|
|
626
717
|
|
|
627
|
-
// Emit change event so other tools (like
|
|
628
|
-
// Only emit if requested (to avoid loops when updating non-geometry props like holes)
|
|
718
|
+
// Emit change event so other tools (like FeatureTool) can react
|
|
629
719
|
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
720
|
const geometry = this.getGeometry();
|
|
638
721
|
if (geometry) {
|
|
639
722
|
this.context.eventBus.emit("dieline:geometry:change", geometry);
|
|
@@ -643,7 +726,7 @@ export class DielineTool implements Extension {
|
|
|
643
726
|
|
|
644
727
|
public getGeometry(): DielineGeometry | null {
|
|
645
728
|
if (!this.canvasService) return null;
|
|
646
|
-
const { unit, shape, width, height, radius,
|
|
729
|
+
const { unit, shape, width, height, radius, offset, mainLine, pathData } = this.state;
|
|
647
730
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
648
731
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
649
732
|
|
|
@@ -670,9 +753,10 @@ export class DielineTool implements Extension {
|
|
|
670
753
|
height: visualHeight,
|
|
671
754
|
radius: radius * scale,
|
|
672
755
|
offset: offset * scale,
|
|
673
|
-
// Pass scale to help other tools (like
|
|
756
|
+
// Pass scale to help other tools (like FeatureTool) convert units
|
|
674
757
|
scale,
|
|
675
|
-
|
|
758
|
+
strokeWidth: mainLine.width,
|
|
759
|
+
pathData,
|
|
676
760
|
} as DielineGeometry;
|
|
677
761
|
}
|
|
678
762
|
|
|
@@ -680,13 +764,10 @@ export class DielineTool implements Extension {
|
|
|
680
764
|
if (!this.canvasService) return null;
|
|
681
765
|
const userLayer = this.canvasService.getLayer("user");
|
|
682
766
|
|
|
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
767
|
if (!userLayer) return null;
|
|
687
768
|
|
|
688
769
|
// 1. Generate Path Data
|
|
689
|
-
const { shape, width, height, radius,
|
|
770
|
+
const { shape, width, height, radius, features, unit, pathData } = this.state;
|
|
690
771
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
691
772
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
692
773
|
|
|
@@ -703,74 +784,57 @@ export class DielineTool implements Extension {
|
|
|
703
784
|
const visualHeight = layout.height;
|
|
704
785
|
const visualRadius = radius * scale;
|
|
705
786
|
|
|
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
|
-
);
|
|
787
|
+
// Scale Features
|
|
788
|
+
const absoluteFeatures = (features || []).map((f) => {
|
|
789
|
+
const featureScale = scale;
|
|
720
790
|
|
|
721
791
|
return {
|
|
722
|
-
...
|
|
723
|
-
x:
|
|
724
|
-
y:
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
offsetY: (h.offsetY || 0) * unitScale * scale,
|
|
792
|
+
...f,
|
|
793
|
+
x: f.x,
|
|
794
|
+
y: f.y,
|
|
795
|
+
width: (f.width || 0) * featureScale,
|
|
796
|
+
height: (f.height || 0) * featureScale,
|
|
797
|
+
radius: (f.radius || 0) * featureScale,
|
|
729
798
|
};
|
|
730
799
|
});
|
|
731
800
|
|
|
732
|
-
const
|
|
801
|
+
const originalFeatures = absoluteFeatures.filter(
|
|
802
|
+
(f) => !f.target || f.target === "original" || f.target === "both",
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
const generatedPathData = generateDielinePath({
|
|
733
806
|
shape,
|
|
734
807
|
width: visualWidth,
|
|
735
808
|
height: visualHeight,
|
|
736
809
|
radius: visualRadius,
|
|
737
810
|
x: cx,
|
|
738
811
|
y: cy,
|
|
739
|
-
|
|
740
|
-
pathData
|
|
812
|
+
features: originalFeatures,
|
|
813
|
+
pathData,
|
|
741
814
|
canvasWidth: canvasW,
|
|
742
815
|
canvasHeight: canvasH,
|
|
743
816
|
});
|
|
744
817
|
|
|
745
818
|
// 2. Prepare for Export
|
|
746
|
-
// Clone the layer to not affect the stage
|
|
747
819
|
const clonedLayer = await userLayer.clone();
|
|
748
820
|
|
|
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, {
|
|
821
|
+
const clipPath = new Path(generatedPathData, {
|
|
757
822
|
originX: "left",
|
|
758
823
|
originY: "top",
|
|
759
824
|
left: 0,
|
|
760
825
|
top: 0,
|
|
761
|
-
absolutePositioned: true,
|
|
826
|
+
absolutePositioned: true,
|
|
762
827
|
});
|
|
763
828
|
|
|
764
829
|
clonedLayer.clipPath = clipPath;
|
|
765
830
|
|
|
766
831
|
// 3. Calculate Crop Area (The Dieline Bounds)
|
|
767
|
-
// We want to export only the area covered by the dieline
|
|
768
832
|
const bounds = clipPath.getBoundingRect();
|
|
769
833
|
|
|
770
834
|
// 4. Export
|
|
771
835
|
const dataUrl = clonedLayer.toDataURL({
|
|
772
836
|
format: "png",
|
|
773
|
-
multiplier: 2,
|
|
837
|
+
multiplier: 2,
|
|
774
838
|
left: bounds.left,
|
|
775
839
|
top: bounds.top,
|
|
776
840
|
width: bounds.width,
|