@pooder/kit 3.0.0 → 3.1.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 +13 -0
- package/dist/index.d.mts +7 -12
- package/dist/index.d.ts +7 -12
- package/dist/index.js +266 -210
- package/dist/index.mjs +266 -210
- package/package.json +2 -2
- package/src/dieline.ts +38 -103
- package/src/geometry.ts +90 -2
- package/src/hole.ts +215 -165
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pooder/kit",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Standard plugins for Pooder editor",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"paper": "^0.12.18",
|
|
21
21
|
"fabric": "^7.0.0",
|
|
22
|
-
"@pooder/core": "1.
|
|
22
|
+
"@pooder/core": "1.1.0"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
package/src/dieline.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
generateBleedZonePath,
|
|
16
16
|
getPathBounds,
|
|
17
17
|
HoleData,
|
|
18
|
+
resolveHolePosition,
|
|
18
19
|
} from "./geometry";
|
|
19
20
|
|
|
20
21
|
export interface DielineGeometry {
|
|
@@ -226,65 +227,6 @@ export class DielineTool implements Extension {
|
|
|
226
227
|
},
|
|
227
228
|
] as ConfigurationContribution[],
|
|
228
229
|
[ContributionPointIds.COMMANDS]: [
|
|
229
|
-
{
|
|
230
|
-
command: "reset",
|
|
231
|
-
title: "Reset Dieline",
|
|
232
|
-
handler: () => {
|
|
233
|
-
this.shape = "rect";
|
|
234
|
-
this.width = 300;
|
|
235
|
-
this.height = 300;
|
|
236
|
-
this.radius = 0;
|
|
237
|
-
this.offset = 0;
|
|
238
|
-
this.style = "solid";
|
|
239
|
-
this.insideColor = "rgba(0,0,0,0)";
|
|
240
|
-
this.outsideColor = "#ffffff";
|
|
241
|
-
this.showBleedLines = true;
|
|
242
|
-
this.holes = [];
|
|
243
|
-
this.pathData = undefined;
|
|
244
|
-
this.updateDieline();
|
|
245
|
-
return true;
|
|
246
|
-
},
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
command: "setDimensions",
|
|
250
|
-
title: "Set Dimensions",
|
|
251
|
-
handler: (width: number, height: number) => {
|
|
252
|
-
if (this.width === width && this.height === height) return true;
|
|
253
|
-
this.width = width;
|
|
254
|
-
this.height = height;
|
|
255
|
-
this.updateDieline();
|
|
256
|
-
return true;
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
command: "setShape",
|
|
261
|
-
title: "Set Shape",
|
|
262
|
-
handler: (shape: "rect" | "circle" | "ellipse" | "custom") => {
|
|
263
|
-
if (this.shape === shape) return true;
|
|
264
|
-
this.shape = shape;
|
|
265
|
-
this.updateDieline();
|
|
266
|
-
return true;
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
command: "setBleed",
|
|
271
|
-
title: "Set Bleed",
|
|
272
|
-
handler: (bleed: number) => {
|
|
273
|
-
if (this.offset === bleed) return true;
|
|
274
|
-
this.offset = bleed;
|
|
275
|
-
this.updateDieline();
|
|
276
|
-
return true;
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
command: "setHoles",
|
|
281
|
-
title: "Set Holes",
|
|
282
|
-
handler: (holes: HoleData[]) => {
|
|
283
|
-
this.holes = holes;
|
|
284
|
-
this.updateDieline(false);
|
|
285
|
-
return true;
|
|
286
|
-
},
|
|
287
|
-
},
|
|
288
230
|
{
|
|
289
231
|
command: "getGeometry",
|
|
290
232
|
title: "Get Geometry",
|
|
@@ -304,39 +246,24 @@ export class DielineTool implements Extension {
|
|
|
304
246
|
title: "Detect Edge from Image",
|
|
305
247
|
handler: async (imageUrl: string, options?: any) => {
|
|
306
248
|
try {
|
|
307
|
-
// Pass current dimensions if we want to scale immediately?
|
|
308
|
-
// But wait, the user said "It should be scaled according to width and height".
|
|
309
|
-
// If the user already set width/height on the tool, we should respect it?
|
|
310
|
-
// Or should we set width/height based on the image aspect ratio?
|
|
311
|
-
// Usually for a new trace, we might want to respect the IMAGE aspect ratio but fit into current width/height?
|
|
312
|
-
// Or just replace width/height with image dimensions?
|
|
313
|
-
// Let's assume we want to keep the current "box" size but fit the shape inside?
|
|
314
|
-
// Or if options has width/height use that.
|
|
315
|
-
|
|
316
|
-
// Let's first trace to get the natural shape (and its aspect ratio)
|
|
317
|
-
// Then we can decide how to update this.width/this.height.
|
|
318
|
-
|
|
319
249
|
const pathData = await ImageTracer.trace(imageUrl, options);
|
|
320
|
-
|
|
321
|
-
// We need to set width/height from the path bounds to avoid distortion
|
|
322
250
|
const bounds = getPathBounds(pathData);
|
|
323
251
|
|
|
324
|
-
// If we want to scale the path to specific dimensions, we can do it via ImageTracer options.scaleToWidth/Height
|
|
325
|
-
// But here we got the raw path.
|
|
326
|
-
// Let's update the TOOL's dimensions to match the detected shape's aspect ratio,
|
|
327
|
-
// while keeping the size reasonable (e.g. max dimension 300 or current size).
|
|
328
|
-
|
|
329
|
-
// If current tool size is default 300x300, we might want to resize tool to match image ratio.
|
|
330
252
|
const currentMax = Math.max(this.width, this.height);
|
|
331
253
|
const scale = currentMax / Math.max(bounds.width, bounds.height);
|
|
332
254
|
|
|
333
|
-
|
|
334
|
-
|
|
255
|
+
const newWidth = bounds.width * scale;
|
|
256
|
+
const newHeight = bounds.height * scale;
|
|
335
257
|
|
|
336
|
-
|
|
337
|
-
|
|
258
|
+
const configService =
|
|
259
|
+
this.context?.services.get<any>("ConfigurationService");
|
|
260
|
+
if (configService) {
|
|
261
|
+
configService.update("dieline.width", newWidth);
|
|
262
|
+
configService.update("dieline.height", newHeight);
|
|
263
|
+
configService.update("dieline.shape", "custom");
|
|
264
|
+
configService.update("dieline.pathData", pathData);
|
|
265
|
+
}
|
|
338
266
|
|
|
339
|
-
this.updateDieline();
|
|
340
267
|
return pathData;
|
|
341
268
|
} catch (e) {
|
|
342
269
|
console.error("Edge detection failed", e);
|
|
@@ -431,31 +358,38 @@ export class DielineTool implements Extension {
|
|
|
431
358
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
432
359
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
433
360
|
|
|
434
|
-
|
|
361
|
+
let visualWidth = width;
|
|
362
|
+
let visualHeight = height;
|
|
363
|
+
|
|
435
364
|
if (borderLength && borderLength > 0) {
|
|
436
|
-
|
|
437
|
-
|
|
365
|
+
visualWidth = Math.max(0, canvasW - borderLength * 2);
|
|
366
|
+
visualHeight = Math.max(0, canvasH - borderLength * 2);
|
|
438
367
|
}
|
|
439
368
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const normalizedPos = position ?? { x: 0.5, y: 0.5 };
|
|
443
|
-
const cx = Coordinate.toAbsolute(normalizedPos.x, canvasW);
|
|
444
|
-
const cy = Coordinate.toAbsolute(normalizedPos.y, canvasH);
|
|
369
|
+
const cx = Coordinate.toAbsolute(position?.x ?? 0.5, canvasW);
|
|
370
|
+
const cy = Coordinate.toAbsolute(position?.y ?? 0.5, canvasH);
|
|
445
371
|
|
|
446
372
|
// Clear existing objects
|
|
447
373
|
layer.remove(...layer.getObjects());
|
|
448
374
|
|
|
449
|
-
//
|
|
375
|
+
// Resolve Holes for Geometry Generation
|
|
376
|
+
const geometryForHoles = {
|
|
377
|
+
x: cx,
|
|
378
|
+
y: cy,
|
|
379
|
+
width: visualWidth,
|
|
380
|
+
height: visualHeight,
|
|
381
|
+
};
|
|
382
|
+
|
|
450
383
|
const absoluteHoles = (holes || []).map((h) => {
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
384
|
+
const pos = resolveHolePosition(
|
|
385
|
+
h,
|
|
386
|
+
geometryForHoles,
|
|
387
|
+
{ width: canvasW, height: canvasH }
|
|
454
388
|
);
|
|
455
389
|
return {
|
|
456
390
|
...h,
|
|
457
|
-
x:
|
|
458
|
-
y:
|
|
391
|
+
x: pos.x,
|
|
392
|
+
y: pos.y,
|
|
459
393
|
};
|
|
460
394
|
});
|
|
461
395
|
|
|
@@ -683,14 +617,15 @@ export class DielineTool implements Extension {
|
|
|
683
617
|
|
|
684
618
|
// Denormalize Holes for Export
|
|
685
619
|
const absoluteHoles = (holes || []).map((h) => {
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
{
|
|
620
|
+
const pos = resolveHolePosition(
|
|
621
|
+
h,
|
|
622
|
+
{ x: cx, y: cy, width, height },
|
|
623
|
+
{ width: canvasW, height: canvasH }
|
|
689
624
|
);
|
|
690
625
|
return {
|
|
691
626
|
...h,
|
|
692
|
-
x:
|
|
693
|
-
y:
|
|
627
|
+
x: pos.x,
|
|
628
|
+
y: pos.y,
|
|
694
629
|
};
|
|
695
630
|
});
|
|
696
631
|
|
package/src/geometry.ts
CHANGED
|
@@ -1,12 +1,100 @@
|
|
|
1
1
|
import paper from "paper";
|
|
2
2
|
|
|
3
|
+
export type PositionAnchor =
|
|
4
|
+
| "top-left"
|
|
5
|
+
| "top-center"
|
|
6
|
+
| "top-right"
|
|
7
|
+
| "center-left"
|
|
8
|
+
| "center"
|
|
9
|
+
| "center-right"
|
|
10
|
+
| "bottom-left"
|
|
11
|
+
| "bottom-center"
|
|
12
|
+
| "bottom-right";
|
|
13
|
+
|
|
3
14
|
export interface HoleData {
|
|
4
|
-
x
|
|
5
|
-
y
|
|
15
|
+
x?: number;
|
|
16
|
+
y?: number;
|
|
17
|
+
anchor?: PositionAnchor;
|
|
18
|
+
offsetX?: number;
|
|
19
|
+
offsetY?: number;
|
|
6
20
|
innerRadius: number;
|
|
7
21
|
outerRadius: number;
|
|
8
22
|
}
|
|
9
23
|
|
|
24
|
+
export function resolveHolePosition(
|
|
25
|
+
hole: HoleData,
|
|
26
|
+
geometry: { x: number; y: number; width: number; height: number },
|
|
27
|
+
canvasSize: { width: number; height: number }
|
|
28
|
+
): { x: number; y: number } {
|
|
29
|
+
if (hole.anchor) {
|
|
30
|
+
const { x, y, width, height } = geometry;
|
|
31
|
+
let bx = x; // center x
|
|
32
|
+
let by = y; // center y
|
|
33
|
+
|
|
34
|
+
// Calculate anchor base position based on shape bounds
|
|
35
|
+
// Note: geometry.x/y is the CENTER of the shape
|
|
36
|
+
const left = x - width / 2;
|
|
37
|
+
const right = x + width / 2;
|
|
38
|
+
const top = y - height / 2;
|
|
39
|
+
const bottom = y + height / 2;
|
|
40
|
+
|
|
41
|
+
switch (hole.anchor) {
|
|
42
|
+
case "top-left":
|
|
43
|
+
bx = left;
|
|
44
|
+
by = top;
|
|
45
|
+
break;
|
|
46
|
+
case "top-center":
|
|
47
|
+
bx = x;
|
|
48
|
+
by = top;
|
|
49
|
+
break;
|
|
50
|
+
case "top-right":
|
|
51
|
+
bx = right;
|
|
52
|
+
by = top;
|
|
53
|
+
break;
|
|
54
|
+
case "center-left":
|
|
55
|
+
bx = left;
|
|
56
|
+
by = y;
|
|
57
|
+
break;
|
|
58
|
+
case "center":
|
|
59
|
+
bx = x;
|
|
60
|
+
by = y;
|
|
61
|
+
break;
|
|
62
|
+
case "center-right":
|
|
63
|
+
bx = right;
|
|
64
|
+
by = y;
|
|
65
|
+
break;
|
|
66
|
+
case "bottom-left":
|
|
67
|
+
bx = left;
|
|
68
|
+
by = bottom;
|
|
69
|
+
break;
|
|
70
|
+
case "bottom-center":
|
|
71
|
+
bx = x;
|
|
72
|
+
by = bottom;
|
|
73
|
+
break;
|
|
74
|
+
case "bottom-right":
|
|
75
|
+
bx = right;
|
|
76
|
+
by = bottom;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
x: bx + (hole.offsetX || 0),
|
|
82
|
+
y: by + (hole.offsetY || 0),
|
|
83
|
+
};
|
|
84
|
+
} else if (hole.x !== undefined && hole.y !== undefined) {
|
|
85
|
+
// Legacy / Direct coordinates (Normalized)
|
|
86
|
+
// We assume x/y are normalized to canvas size if no anchor is present
|
|
87
|
+
// Or should we support absolute?
|
|
88
|
+
// Current system uses normalized.
|
|
89
|
+
// Coordinate.denormalizePoint logic:
|
|
90
|
+
return {
|
|
91
|
+
x: hole.x * canvasSize.width,
|
|
92
|
+
y: hole.y * canvasSize.height,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return { x: 0, y: 0 };
|
|
96
|
+
}
|
|
97
|
+
|
|
10
98
|
export interface GeometryOptions {
|
|
11
99
|
shape: "rect" | "circle" | "ellipse" | "custom";
|
|
12
100
|
width: number;
|