@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/geometry.ts
CHANGED
|
@@ -1,98 +1,22 @@
|
|
|
1
1
|
import paper from "paper";
|
|
2
2
|
|
|
3
|
-
export type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
offsetY?: number;
|
|
21
|
-
innerRadius: number;
|
|
22
|
-
outerRadius: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function resolveHolePosition(
|
|
26
|
-
hole: HoleData,
|
|
27
|
-
geometry: { x: number; y: number; width: number; height: number },
|
|
28
|
-
canvasSize: { width: number; height: number },
|
|
29
|
-
): { x: number; y: number } {
|
|
30
|
-
if (hole.anchor) {
|
|
31
|
-
const { x, y, width, height } = geometry;
|
|
32
|
-
let bx = x; // center x
|
|
33
|
-
let by = y; // center y
|
|
34
|
-
|
|
35
|
-
// Calculate anchor base position based on shape bounds
|
|
36
|
-
// Note: geometry.x/y is the CENTER of the shape
|
|
37
|
-
const left = x - width / 2;
|
|
38
|
-
const right = x + width / 2;
|
|
39
|
-
const top = y - height / 2;
|
|
40
|
-
const bottom = y + height / 2;
|
|
41
|
-
|
|
42
|
-
switch (hole.anchor) {
|
|
43
|
-
case "top-left":
|
|
44
|
-
bx = left;
|
|
45
|
-
by = top;
|
|
46
|
-
break;
|
|
47
|
-
case "top-center":
|
|
48
|
-
bx = x;
|
|
49
|
-
by = top;
|
|
50
|
-
break;
|
|
51
|
-
case "top-right":
|
|
52
|
-
bx = right;
|
|
53
|
-
by = top;
|
|
54
|
-
break;
|
|
55
|
-
case "center-left":
|
|
56
|
-
bx = left;
|
|
57
|
-
by = y;
|
|
58
|
-
break;
|
|
59
|
-
case "center":
|
|
60
|
-
bx = x;
|
|
61
|
-
by = y;
|
|
62
|
-
break;
|
|
63
|
-
case "center-right":
|
|
64
|
-
bx = right;
|
|
65
|
-
by = y;
|
|
66
|
-
break;
|
|
67
|
-
case "bottom-left":
|
|
68
|
-
bx = left;
|
|
69
|
-
by = bottom;
|
|
70
|
-
break;
|
|
71
|
-
case "bottom-center":
|
|
72
|
-
bx = x;
|
|
73
|
-
by = bottom;
|
|
74
|
-
break;
|
|
75
|
-
case "bottom-right":
|
|
76
|
-
bx = right;
|
|
77
|
-
by = bottom;
|
|
78
|
-
break;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
x: bx + (hole.offsetX || 0),
|
|
83
|
-
y: by + (hole.offsetY || 0),
|
|
84
|
-
};
|
|
85
|
-
} else if (hole.x !== undefined && hole.y !== undefined) {
|
|
86
|
-
// Legacy / Direct coordinates (Normalized relative to Dieline Geometry)
|
|
87
|
-
// Formula: absolute = normalized * width + (center - width/2)
|
|
88
|
-
// This handles padding correctly.
|
|
89
|
-
const { x, width, y, height } = geometry;
|
|
90
|
-
return {
|
|
91
|
-
x: hole.x * width + (x - width / 2) + (hole.offsetX || 0),
|
|
92
|
-
y: hole.y * height + (y - height / 2) + (hole.offsetY || 0),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
return { x: 0, y: 0 };
|
|
3
|
+
export type FeatureOperation = "add" | "subtract";
|
|
4
|
+
export type FeatureShape = "rect" | "circle";
|
|
5
|
+
|
|
6
|
+
export interface EdgeFeature {
|
|
7
|
+
id: string;
|
|
8
|
+
groupId?: string; // For grouping features together (e.g. double-layer hole)
|
|
9
|
+
operation: FeatureOperation;
|
|
10
|
+
shape: FeatureShape;
|
|
11
|
+
x: number; // Normalized 0-1 relative to geometry bounds
|
|
12
|
+
y: number; // Normalized 0-1 relative to geometry bounds
|
|
13
|
+
width?: number; // For rect (Physical units)
|
|
14
|
+
height?: number; // For rect (Physical units)
|
|
15
|
+
radius?: number; // For circle or rect corners (Physical units)
|
|
16
|
+
rotation?: number; // Degrees
|
|
17
|
+
target?: "original" | "offset" | "both";
|
|
18
|
+
color?: string; // Hex color for the marker
|
|
19
|
+
strokeDash?: number[]; // Stroke dash array for the marker
|
|
96
20
|
}
|
|
97
21
|
|
|
98
22
|
export interface GeometryOptions {
|
|
@@ -102,7 +26,7 @@ export interface GeometryOptions {
|
|
|
102
26
|
radius: number;
|
|
103
27
|
x: number;
|
|
104
28
|
y: number;
|
|
105
|
-
|
|
29
|
+
features: Array<EdgeFeature>;
|
|
106
30
|
pathData?: string;
|
|
107
31
|
canvasWidth?: number;
|
|
108
32
|
canvasHeight?: number;
|
|
@@ -113,6 +37,24 @@ export interface MaskGeometryOptions extends GeometryOptions {
|
|
|
113
37
|
canvasHeight: number;
|
|
114
38
|
}
|
|
115
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Resolves the absolute position of a feature based on normalized coordinates.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveFeaturePosition(
|
|
44
|
+
feature: EdgeFeature,
|
|
45
|
+
geometry: { x: number; y: number; width: number; height: number },
|
|
46
|
+
): { x: number; y: number } {
|
|
47
|
+
const { x, y, width, height } = geometry;
|
|
48
|
+
// geometry.x/y is the Center.
|
|
49
|
+
const left = x - width / 2;
|
|
50
|
+
const top = y - height / 2;
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
x: left + feature.x * width,
|
|
54
|
+
y: top + feature.y * height,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
116
58
|
/**
|
|
117
59
|
* Initializes paper.js project if not already initialized.
|
|
118
60
|
*/
|
|
@@ -153,9 +95,6 @@ function createBaseShape(options: GeometryOptions): paper.PathItem {
|
|
|
153
95
|
path.pathData = pathData;
|
|
154
96
|
// Align center
|
|
155
97
|
path.position = center;
|
|
156
|
-
// Scale to match width/height if needed?
|
|
157
|
-
// For now, assume pathData is correct size, but we might want to support resizing.
|
|
158
|
-
// If width/height are provided and different from bounds, we could scale.
|
|
159
98
|
if (
|
|
160
99
|
width > 0 &&
|
|
161
100
|
height > 0 &&
|
|
@@ -166,7 +105,6 @@ function createBaseShape(options: GeometryOptions): paper.PathItem {
|
|
|
166
105
|
}
|
|
167
106
|
return path;
|
|
168
107
|
} else {
|
|
169
|
-
// Fallback
|
|
170
108
|
return new paper.Path.Rectangle({
|
|
171
109
|
point: [x - width / 2, y - height / 2],
|
|
172
110
|
size: [Math.max(0, width), Math.max(0, height)],
|
|
@@ -175,215 +113,95 @@ function createBaseShape(options: GeometryOptions): paper.PathItem {
|
|
|
175
113
|
}
|
|
176
114
|
|
|
177
115
|
/**
|
|
178
|
-
* Creates
|
|
179
|
-
* For Rect/Circle, we can just adjust params.
|
|
180
|
-
* For Custom shapes, we need a true offset algorithm (Paper.js doesn't have a robust one built-in for all cases,
|
|
181
|
-
* but we can simulate it or use a simple scaling if offset is small, OR rely on a library like Clipper.js.
|
|
182
|
-
* However, since we want to avoid heavy deps, let's try a simple approach:
|
|
183
|
-
* If it's a simple shape, we re-create it.
|
|
184
|
-
* If it's custom, we unfortunately have to scale it for now as a poor-man's offset,
|
|
185
|
-
* UNLESS we implement a stroke expansion.
|
|
186
|
-
*
|
|
187
|
-
* Stroke Expansion Trick:
|
|
188
|
-
* 1. Create path
|
|
189
|
-
* 2. Set strokeWidth = offset * 2
|
|
190
|
-
* 3. Convert stroke to path (paper.js has path.expand())
|
|
191
|
-
* 4. Union original + expanded (for positive offset) or Subtract (for negative).
|
|
116
|
+
* Creates a Paper.js Item for a single feature.
|
|
192
117
|
*/
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
|
|
118
|
+
function createFeatureItem(
|
|
119
|
+
feature: EdgeFeature,
|
|
120
|
+
center: paper.Point,
|
|
196
121
|
): paper.PathItem {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
radius:
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
stroker.strokeColor = new paper.Color("black");
|
|
217
|
-
stroker.strokeWidth = Math.abs(offset) * 2;
|
|
218
|
-
// Round join usually looks better for offsets
|
|
219
|
-
stroker.strokeJoin = "round";
|
|
220
|
-
stroker.strokeCap = "round";
|
|
221
|
-
|
|
222
|
-
// Expand stroke to path
|
|
223
|
-
// @ts-ignore - paper.js types might be missing expand depending on version, but it exists in recent versions
|
|
224
|
-
// If expand is not available, we might fallback to scaling.
|
|
225
|
-
// Assuming modern paper.js
|
|
226
|
-
let expanded: paper.Item;
|
|
227
|
-
try {
|
|
228
|
-
// @ts-ignore
|
|
229
|
-
expanded = stroker.expand({ stroke: true, fill: false, insert: false });
|
|
230
|
-
} catch (e) {
|
|
231
|
-
// Fallback if expand fails or not present
|
|
232
|
-
stroker.remove();
|
|
233
|
-
// Fallback to scaling (imperfect)
|
|
234
|
-
const scaleX =
|
|
235
|
-
(original.bounds.width + offset * 2) / original.bounds.width;
|
|
236
|
-
const scaleY =
|
|
237
|
-
(original.bounds.height + offset * 2) / original.bounds.height;
|
|
238
|
-
original.scale(scaleX, scaleY);
|
|
239
|
-
return original;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
stroker.remove();
|
|
243
|
-
|
|
244
|
-
// The expanded stroke is a "ring".
|
|
245
|
-
// For positive offset: Union(Original, Ring)
|
|
246
|
-
// For negative offset: Subtract(Original, Ring) ? No, that makes a hole.
|
|
247
|
-
// For negative offset: We want the "inner" boundary of the ring.
|
|
248
|
-
|
|
249
|
-
// Actually, expand() returns a Group or Path.
|
|
250
|
-
// If it's a closed path, the ring has an outer and inner boundary.
|
|
251
|
-
|
|
252
|
-
let result: paper.PathItem;
|
|
253
|
-
|
|
254
|
-
if (offset > 0) {
|
|
255
|
-
// @ts-ignore
|
|
256
|
-
result = original.unite(expanded);
|
|
257
|
-
} else {
|
|
258
|
-
// For negative offset (shrink), we want the original MINUS the stroke?
|
|
259
|
-
// No, the stroke is centered on the line.
|
|
260
|
-
// So the inner edge of the stroke is at -offset.
|
|
261
|
-
// We want the area INSIDE the inner edge.
|
|
262
|
-
// That is Original SUBTRACT the Ring?
|
|
263
|
-
// Yes, if we subtract the ring, we lose the border area.
|
|
264
|
-
// @ts-ignore
|
|
265
|
-
result = original.subtract(expanded);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Cleanup
|
|
269
|
-
original.remove();
|
|
270
|
-
expanded.remove();
|
|
122
|
+
let item: paper.PathItem;
|
|
123
|
+
|
|
124
|
+
if (feature.shape === "rect") {
|
|
125
|
+
const w = feature.width || 10;
|
|
126
|
+
const h = feature.height || 10;
|
|
127
|
+
const r = feature.radius || 0;
|
|
128
|
+
item = new paper.Path.Rectangle({
|
|
129
|
+
point: [center.x - w / 2, center.y - h / 2],
|
|
130
|
+
size: [w, h],
|
|
131
|
+
radius: r,
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
// Circle
|
|
135
|
+
const r = feature.radius || 5;
|
|
136
|
+
item = new paper.Path.Circle({
|
|
137
|
+
center: center,
|
|
138
|
+
radius: r,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
271
141
|
|
|
272
|
-
|
|
142
|
+
if (feature.rotation) {
|
|
143
|
+
item.rotate(feature.rotation, center);
|
|
273
144
|
}
|
|
274
145
|
|
|
275
|
-
return
|
|
146
|
+
return item;
|
|
276
147
|
}
|
|
277
148
|
|
|
278
149
|
/**
|
|
279
150
|
* Internal helper to generate the Dieline Shape (Paper Item).
|
|
280
|
-
*
|
|
151
|
+
* Logic: (Base U Adds) - Subtracts
|
|
281
152
|
*/
|
|
282
153
|
function getDielineShape(options: GeometryOptions): paper.PathItem {
|
|
283
154
|
// 1. Create Base Shape
|
|
284
155
|
let mainShape = createBaseShape(options);
|
|
285
156
|
|
|
286
|
-
const {
|
|
287
|
-
|
|
288
|
-
if (
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
point: [
|
|
300
|
-
center.x - hole.outerRadius,
|
|
301
|
-
center.y - hole.outerRadius,
|
|
302
|
-
],
|
|
303
|
-
size: [hole.outerRadius * 2, hole.outerRadius * 2],
|
|
304
|
-
})
|
|
305
|
-
: new paper.Path.Circle({
|
|
306
|
-
center: center,
|
|
307
|
-
radius: hole.outerRadius,
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// REMOVED: Intersects check. We want to process all holes defined in config.
|
|
311
|
-
// If a hole is completely outside, it might form an island, but that's better than missing it.
|
|
312
|
-
// Users can remove the hole if they don't want it.
|
|
313
|
-
|
|
314
|
-
// Create Cut (Inner Radius)
|
|
315
|
-
const cut =
|
|
316
|
-
hole.shape === "square"
|
|
317
|
-
? new paper.Path.Rectangle({
|
|
318
|
-
point: [
|
|
319
|
-
center.x - hole.innerRadius,
|
|
320
|
-
center.y - hole.innerRadius,
|
|
321
|
-
],
|
|
322
|
-
size: [hole.innerRadius * 2, hole.innerRadius * 2],
|
|
323
|
-
})
|
|
324
|
-
: new paper.Path.Circle({
|
|
325
|
-
center: center,
|
|
326
|
-
radius: hole.innerRadius,
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// Union Lugs
|
|
330
|
-
if (!lugsPath) {
|
|
331
|
-
lugsPath = lug;
|
|
157
|
+
const { features } = options;
|
|
158
|
+
|
|
159
|
+
if (features && features.length > 0) {
|
|
160
|
+
const adds: paper.PathItem[] = [];
|
|
161
|
+
const subtracts: paper.PathItem[] = [];
|
|
162
|
+
|
|
163
|
+
features.forEach((f) => {
|
|
164
|
+
const pos = resolveFeaturePosition(f, options);
|
|
165
|
+
const center = new paper.Point(pos.x, pos.y);
|
|
166
|
+
const item = createFeatureItem(f, center);
|
|
167
|
+
|
|
168
|
+
if (f.operation === "add") {
|
|
169
|
+
adds.push(item);
|
|
332
170
|
} else {
|
|
333
|
-
|
|
334
|
-
const temp = lugsPath.unite(lug);
|
|
335
|
-
lugsPath.remove();
|
|
336
|
-
lug.remove();
|
|
337
|
-
lugsPath = temp;
|
|
338
|
-
} catch (e) {
|
|
339
|
-
console.error("Geometry: Failed to unite lug", e);
|
|
340
|
-
// Keep previous lugsPath, ignore this one to prevent crash
|
|
341
|
-
lug.remove();
|
|
342
|
-
}
|
|
171
|
+
subtracts.push(item);
|
|
343
172
|
}
|
|
173
|
+
});
|
|
344
174
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
175
|
+
// 2. Process Additions (Union)
|
|
176
|
+
if (adds.length > 0) {
|
|
177
|
+
// Unite all additions first to avoid artifacts?
|
|
178
|
+
// Or unite one by one to mainShape?
|
|
179
|
+
// Unite one by one is safer for simple logic.
|
|
180
|
+
for (const item of adds) {
|
|
349
181
|
try {
|
|
350
|
-
const temp =
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
182
|
+
const temp = mainShape.unite(item);
|
|
183
|
+
mainShape.remove();
|
|
184
|
+
item.remove();
|
|
185
|
+
mainShape = temp;
|
|
354
186
|
} catch (e) {
|
|
355
|
-
console.error("Geometry: Failed to unite
|
|
356
|
-
|
|
187
|
+
console.error("Geometry: Failed to unite feature", e);
|
|
188
|
+
item.remove();
|
|
357
189
|
}
|
|
358
190
|
}
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// 2. Add Lugs to Main Shape (Union) - Additive Fusion
|
|
362
|
-
if (lugsPath) {
|
|
363
|
-
try {
|
|
364
|
-
const temp = mainShape.unite(lugsPath);
|
|
365
|
-
mainShape.remove();
|
|
366
|
-
// @ts-ignore
|
|
367
|
-
lugsPath.remove();
|
|
368
|
-
mainShape = temp;
|
|
369
|
-
} catch (e) {
|
|
370
|
-
console.error("Geometry: Failed to unite lugsPath to mainShape", e);
|
|
371
|
-
}
|
|
372
191
|
}
|
|
373
192
|
|
|
374
|
-
// 3.
|
|
375
|
-
if (
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
);
|
|
193
|
+
// 3. Process Subtractions (Difference)
|
|
194
|
+
if (subtracts.length > 0) {
|
|
195
|
+
for (const item of subtracts) {
|
|
196
|
+
try {
|
|
197
|
+
const temp = mainShape.subtract(item);
|
|
198
|
+
mainShape.remove();
|
|
199
|
+
item.remove();
|
|
200
|
+
mainShape = temp;
|
|
201
|
+
} catch (e) {
|
|
202
|
+
console.error("Geometry: Failed to subtract feature", e);
|
|
203
|
+
item.remove();
|
|
204
|
+
}
|
|
387
205
|
}
|
|
388
206
|
}
|
|
389
207
|
}
|
|
@@ -393,7 +211,6 @@ function getDielineShape(options: GeometryOptions): paper.PathItem {
|
|
|
393
211
|
|
|
394
212
|
/**
|
|
395
213
|
* Generates the path data for the Dieline (Product Shape).
|
|
396
|
-
* Logic: (BaseShape UNION IntersectingLugs) SUBTRACT Cuts
|
|
397
214
|
*/
|
|
398
215
|
export function generateDielinePath(options: GeometryOptions): string {
|
|
399
216
|
const paperWidth = options.canvasWidth || options.width * 2 || 2000;
|
|
@@ -419,16 +236,13 @@ export function generateMaskPath(options: MaskGeometryOptions): string {
|
|
|
419
236
|
|
|
420
237
|
const { canvasWidth, canvasHeight } = options;
|
|
421
238
|
|
|
422
|
-
// 1. Canvas Background
|
|
423
239
|
const maskRect = new paper.Path.Rectangle({
|
|
424
240
|
point: [0, 0],
|
|
425
241
|
size: [canvasWidth, canvasHeight],
|
|
426
242
|
});
|
|
427
243
|
|
|
428
|
-
// 2. Re-create Product Shape
|
|
429
244
|
const mainShape = getDielineShape(options);
|
|
430
245
|
|
|
431
|
-
// 3. Subtract Product from Mask
|
|
432
246
|
const finalMask = maskRect.subtract(mainShape);
|
|
433
247
|
|
|
434
248
|
maskRect.remove();
|
|
@@ -441,92 +255,25 @@ export function generateMaskPath(options: MaskGeometryOptions): string {
|
|
|
441
255
|
}
|
|
442
256
|
|
|
443
257
|
/**
|
|
444
|
-
* Generates the path data for the Bleed Zone
|
|
258
|
+
* Generates the path data for the Bleed Zone.
|
|
445
259
|
*/
|
|
446
260
|
export function generateBleedZonePath(
|
|
447
|
-
|
|
261
|
+
originalOptions: GeometryOptions,
|
|
262
|
+
offsetOptions: GeometryOptions,
|
|
448
263
|
offset: number,
|
|
449
264
|
): string {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
const paperHeight =
|
|
265
|
+
const paperWidth =
|
|
266
|
+
originalOptions.canvasWidth || originalOptions.width * 2 || 2000;
|
|
267
|
+
const paperHeight =
|
|
268
|
+
originalOptions.canvasHeight || originalOptions.height * 2 || 2000;
|
|
453
269
|
ensurePaper(paperWidth, paperHeight);
|
|
454
270
|
paper.project.activeLayer.removeChildren();
|
|
455
271
|
|
|
456
|
-
// 1. Original Shape
|
|
457
|
-
const shapeOriginal = getDielineShape(
|
|
458
|
-
|
|
459
|
-
// 2. Offset Shape
|
|
460
|
-
|
|
461
|
-
// But we still need to respect holes if they exist.
|
|
462
|
-
// getDielineShape handles holes.
|
|
463
|
-
// The issue is: do holes shrink/expand with bleed?
|
|
464
|
-
// Usually, bleed is only for the outer cut. Holes are internal cuts.
|
|
465
|
-
// Internal cuts usually also have bleed if they are die-cut, but maybe different direction?
|
|
466
|
-
// For simplicity, let's assume we offset the FINAL shape (including holes).
|
|
467
|
-
|
|
468
|
-
// Actually, getDielineShape calls createBaseShape.
|
|
469
|
-
// Let's modify generateBleedZonePath to use createOffsetShape logic if possible,
|
|
470
|
-
// OR just perform offset on the final shape result.
|
|
471
|
-
|
|
472
|
-
// The previous logic was: create base shape with adjusted width/height/radius.
|
|
473
|
-
// This works for Rect/Circle.
|
|
474
|
-
// For Custom, we need createOffsetShape.
|
|
475
|
-
|
|
476
|
-
let shapeOffset: paper.PathItem;
|
|
477
|
-
|
|
478
|
-
if (options.shape === "custom") {
|
|
479
|
-
// For custom shape, we offset the base shape first, then apply holes?
|
|
480
|
-
// Or offset the final result?
|
|
481
|
-
// Bleed is usually "outside" the cut line.
|
|
482
|
-
// If we have a donut, bleed is outside the outer circle AND inside the inner circle?
|
|
483
|
-
// Or just outside the outer?
|
|
484
|
-
// Let's assume bleed expands the solid area.
|
|
485
|
-
|
|
486
|
-
// So we take the final shape (Original) and expand it.
|
|
487
|
-
// We can use the same Stroke Expansion trick on the final shape.
|
|
488
|
-
|
|
489
|
-
// Since shapeOriginal is already the final shape (Base - Holes),
|
|
490
|
-
// we can try to offset it directly.
|
|
491
|
-
|
|
492
|
-
const stroker = shapeOriginal.clone() as paper.Path;
|
|
493
|
-
stroker.strokeColor = new paper.Color("black");
|
|
494
|
-
stroker.strokeWidth = Math.abs(offset) * 2;
|
|
495
|
-
stroker.strokeJoin = "round";
|
|
496
|
-
stroker.strokeCap = "round";
|
|
497
|
-
|
|
498
|
-
let expanded: paper.Item;
|
|
499
|
-
try {
|
|
500
|
-
// @ts-ignore
|
|
501
|
-
expanded = stroker.expand({ stroke: true, fill: false, insert: false });
|
|
502
|
-
} catch (e) {
|
|
503
|
-
// Fallback
|
|
504
|
-
stroker.remove();
|
|
505
|
-
shapeOffset = shapeOriginal.clone();
|
|
506
|
-
// scaling fallback...
|
|
507
|
-
return shapeOffset.pathData; // Fail gracefully
|
|
508
|
-
}
|
|
509
|
-
stroker.remove();
|
|
510
|
-
|
|
511
|
-
if (offset > 0) {
|
|
512
|
-
// @ts-ignore
|
|
513
|
-
shapeOffset = shapeOriginal.unite(expanded);
|
|
514
|
-
} else {
|
|
515
|
-
// @ts-ignore
|
|
516
|
-
shapeOffset = shapeOriginal.subtract(expanded);
|
|
517
|
-
}
|
|
518
|
-
expanded.remove();
|
|
519
|
-
} else {
|
|
520
|
-
// Legacy logic for standard shapes (still valid and fast)
|
|
521
|
-
// Adjust dimensions for offset
|
|
522
|
-
const offsetOptions: GeometryOptions = {
|
|
523
|
-
...options,
|
|
524
|
-
width: Math.max(0, options.width + offset * 2),
|
|
525
|
-
height: Math.max(0, options.height + offset * 2),
|
|
526
|
-
radius: options.radius === 0 ? 0 : Math.max(0, options.radius + offset),
|
|
527
|
-
};
|
|
528
|
-
shapeOffset = getDielineShape(offsetOptions);
|
|
529
|
-
}
|
|
272
|
+
// 1. Generate Original Shape
|
|
273
|
+
const shapeOriginal = getDielineShape(originalOptions);
|
|
274
|
+
|
|
275
|
+
// 2. Generate Offset Shape
|
|
276
|
+
const shapeOffset = getDielineShape(offsetOptions);
|
|
530
277
|
|
|
531
278
|
// 3. Calculate Difference
|
|
532
279
|
let bleedZone: paper.PathItem;
|
|
@@ -538,7 +285,6 @@ export function generateBleedZonePath(
|
|
|
538
285
|
|
|
539
286
|
const pathData = bleedZone.pathData;
|
|
540
287
|
|
|
541
|
-
// Cleanup
|
|
542
288
|
shapeOriginal.remove();
|
|
543
289
|
shapeOffset.remove();
|
|
544
290
|
bleedZone.remove();
|
|
@@ -547,8 +293,8 @@ export function generateBleedZonePath(
|
|
|
547
293
|
}
|
|
548
294
|
|
|
549
295
|
/**
|
|
550
|
-
* Finds the nearest point on the Dieline geometry for a given target point.
|
|
551
|
-
* Used for constraining
|
|
296
|
+
* Finds the nearest point on the Dieline geometry (Base Shape ONLY) for a given target point.
|
|
297
|
+
* Used for constraining feature movement.
|
|
552
298
|
*/
|
|
553
299
|
export function getNearestPointOnDieline(
|
|
554
300
|
point: { x: number; y: number },
|
|
@@ -557,6 +303,8 @@ export function getNearestPointOnDieline(
|
|
|
557
303
|
ensurePaper(options.width * 2, options.height * 2);
|
|
558
304
|
paper.project.activeLayer.removeChildren();
|
|
559
305
|
|
|
306
|
+
// We constrain to the BASE shape, not including other features,
|
|
307
|
+
// because usually you want to snap to the main edge.
|
|
560
308
|
const shape = createBaseShape(options);
|
|
561
309
|
|
|
562
310
|
const p = new paper.Point(point.x, point.y);
|