@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pooder/kit",
3
- "version": "3.0.0",
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.0.0"
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
- this.width = bounds.width * scale;
334
- this.height = bounds.height * scale;
255
+ const newWidth = bounds.width * scale;
256
+ const newHeight = bounds.height * scale;
335
257
 
336
- this.shape = "custom";
337
- this.pathData = pathData;
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
- // Handle borderLength (Margin)
361
+ let visualWidth = width;
362
+ let visualHeight = height;
363
+
435
364
  if (borderLength && borderLength > 0) {
436
- width = Math.max(0, canvasW - borderLength * 2);
437
- height = Math.max(0, canvasH - borderLength * 2);
365
+ visualWidth = Math.max(0, canvasW - borderLength * 2);
366
+ visualHeight = Math.max(0, canvasH - borderLength * 2);
438
367
  }
439
368
 
440
- // Handle Position
441
- // this.position is normalized (0-1). Default to center (0.5, 0.5).
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
- // Denormalize Holes for Geometry Generation
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 p = Coordinate.denormalizePoint(
452
- { x: h.x, y: h.y },
453
- { width: canvasW, height: canvasH },
384
+ const pos = resolveHolePosition(
385
+ h,
386
+ geometryForHoles,
387
+ { width: canvasW, height: canvasH }
454
388
  );
455
389
  return {
456
390
  ...h,
457
- x: p.x,
458
- y: p.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 p = Coordinate.denormalizePoint(
687
- { x: h.x, y: h.y },
688
- { width: canvasW, height: canvasH },
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: p.x,
693
- y: p.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: number;
5
- y: number;
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;