@pooder/kit 3.2.0 → 3.4.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.2.0",
3
+ "version": "3.4.0",
4
4
  "description": "Standard plugins for Pooder editor",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
package/src/dieline.ts CHANGED
@@ -493,6 +493,8 @@ export class DielineTool implements Extension {
493
493
  y: cy,
494
494
  holes: absoluteHoles,
495
495
  pathData: this.pathData,
496
+ canvasWidth: canvasW,
497
+ canvasHeight: canvasH,
496
498
  });
497
499
 
498
500
  const insideObj = new Path(productPathData, {
@@ -518,6 +520,8 @@ export class DielineTool implements Extension {
518
520
  y: cy,
519
521
  holes: absoluteHoles,
520
522
  pathData: this.pathData,
523
+ canvasWidth: canvasW,
524
+ canvasHeight: canvasH,
521
525
  },
522
526
  visualOffset,
523
527
  );
@@ -549,6 +553,8 @@ export class DielineTool implements Extension {
549
553
  y: cy,
550
554
  holes: absoluteHoles,
551
555
  pathData: this.pathData,
556
+ canvasWidth: canvasW,
557
+ canvasHeight: canvasH,
552
558
  });
553
559
 
554
560
  const offsetBorderObj = new Path(offsetPathData, {
@@ -577,6 +583,8 @@ export class DielineTool implements Extension {
577
583
  y: cy,
578
584
  holes: absoluteHoles,
579
585
  pathData: this.pathData,
586
+ canvasWidth: canvasW,
587
+ canvasHeight: canvasH,
580
588
  });
581
589
 
582
590
  const borderObj = new Path(borderPathData, {
@@ -730,6 +738,8 @@ export class DielineTool implements Extension {
730
738
  y: cy,
731
739
  holes: absoluteHoles,
732
740
  pathData: this.pathData,
741
+ canvasWidth: canvasW,
742
+ canvasHeight: canvasH,
733
743
  });
734
744
 
735
745
  // 2. Prepare for Export
package/src/geometry.ts CHANGED
@@ -14,6 +14,7 @@ export type PositionAnchor =
14
14
  export interface HoleData {
15
15
  x?: number;
16
16
  y?: number;
17
+ shape?: "circle" | "square";
17
18
  anchor?: PositionAnchor;
18
19
  offsetX?: number;
19
20
  offsetY?: number;
@@ -103,6 +104,8 @@ export interface GeometryOptions {
103
104
  y: number;
104
105
  holes: Array<HoleData>;
105
106
  pathData?: string;
107
+ canvasWidth?: number;
108
+ canvasHeight?: number;
106
109
  }
107
110
 
108
111
  export interface MaskGeometryOptions extends GeometryOptions {
@@ -287,21 +290,41 @@ function getDielineShape(options: GeometryOptions): paper.PathItem {
287
290
  let cutsPath: paper.PathItem | null = null;
288
291
 
289
292
  holes.forEach((hole) => {
293
+ const center = new paper.Point(hole.x!, hole.y!);
294
+
290
295
  // Create Lug (Outer Radius)
291
- const lug = new paper.Path.Circle({
292
- center: [hole.x, hole.y],
293
- radius: hole.outerRadius,
294
- });
296
+ const lug =
297
+ hole.shape === "square"
298
+ ? new paper.Path.Rectangle({
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
+ });
295
309
 
296
310
  // REMOVED: Intersects check. We want to process all holes defined in config.
297
311
  // If a hole is completely outside, it might form an island, but that's better than missing it.
298
312
  // Users can remove the hole if they don't want it.
299
313
 
300
314
  // Create Cut (Inner Radius)
301
- const cut = new paper.Path.Circle({
302
- center: [hole.x, hole.y],
303
- radius: hole.innerRadius,
304
- });
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
+ });
305
328
 
306
329
  // Union Lugs
307
330
  if (!lugsPath) {
@@ -373,7 +396,9 @@ function getDielineShape(options: GeometryOptions): paper.PathItem {
373
396
  * Logic: (BaseShape UNION IntersectingLugs) SUBTRACT Cuts
374
397
  */
375
398
  export function generateDielinePath(options: GeometryOptions): string {
376
- ensurePaper(options.width * 2, options.height * 2);
399
+ const paperWidth = options.canvasWidth || options.width * 2 || 2000;
400
+ const paperHeight = options.canvasHeight || options.height * 2 || 2000;
401
+ ensurePaper(paperWidth, paperHeight);
377
402
  paper.project.activeLayer.removeChildren();
378
403
 
379
404
  const mainShape = getDielineShape(options);
@@ -423,8 +448,9 @@ export function generateBleedZonePath(
423
448
  offset: number,
424
449
  ): string {
425
450
  // Ensure canvas is large enough
426
- const maxDim = Math.max(options.width, options.height) + Math.abs(offset) * 4;
427
- ensurePaper(maxDim, maxDim);
451
+ const paperWidth = options.canvasWidth || options.width * 2 || 2000;
452
+ const paperHeight = options.canvasHeight || options.height * 2 || 2000;
453
+ ensurePaper(paperWidth, paperHeight);
428
454
  paper.project.activeLayer.removeChildren();
429
455
 
430
456
  // 1. Original Shape
package/src/hole.ts CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  ConfigurationContribution,
7
7
  ConfigurationService,
8
8
  } from "@pooder/core";
9
- import { Circle, Group, Point } from "fabric";
9
+ import { Circle, Group, Point, Rect } from "fabric";
10
10
  import CanvasService from "./CanvasService";
11
11
  import { DielineGeometry } from "./dieline";
12
12
  import {
@@ -179,10 +179,12 @@ export class HoleTool implements Extension {
179
179
  const lastHole = currentHoles[currentHoles.length - 1];
180
180
  const innerRadius = lastHole?.innerRadius ?? 15;
181
181
  const outerRadius = lastHole?.outerRadius ?? 25;
182
+ const shape = lastHole?.shape ?? "circle";
182
183
 
183
184
  const newHole = {
184
185
  x: normalizedX,
185
186
  y: normalizedY,
187
+ shape,
186
188
  innerRadius,
187
189
  outerRadius,
188
190
  };
@@ -216,6 +218,7 @@ export class HoleTool implements Extension {
216
218
  if (!this.handleDielineChange) {
217
219
  this.handleDielineChange = (geometry: DielineGeometry) => {
218
220
  this.currentGeometry = geometry;
221
+ this.redraw();
219
222
  const changed = this.enforceConstraints();
220
223
  // Only sync if constraints actually moved something
221
224
  if (changed) {
@@ -367,21 +370,38 @@ export class HoleTool implements Extension {
367
370
  if (!this.canvasService) return;
368
371
  const objects = this.canvasService.canvas
369
372
  .getObjects()
370
- .filter((obj: any) => obj.data?.type === "hole-marker");
373
+ .filter(
374
+ (obj: any) =>
375
+ obj.data?.type === "hole-marker" || obj.name === "hole-marker",
376
+ );
377
+
378
+ // If we have markers but no state, or mismatch, we should be careful.
379
+ // However, if we just dragged one, we expect them to match.
380
+ if (objects.length === 0 && this.holes.length > 0) {
381
+ console.warn("HoleTool: No markers found on canvas to sync from");
382
+ return;
383
+ }
371
384
 
372
- // Sort objects by index
385
+ // Sort objects by index to match this.holes order
373
386
  objects.sort(
374
- (a: any, b: any) => (a.data?.index ?? 0) - (b.data?.index ?? 0)
387
+ (a: any, b: any) => (a.data?.index ?? 0) - (b.data?.index ?? 0),
375
388
  );
376
389
 
377
390
  // Update holes based on canvas positions
378
- // We need to preserve original hole properties (radii, anchor)
379
- // If a hole has an anchor, we update offsetX/Y instead of x/y
380
391
  const newHoles = objects.map((obj, i) => {
381
392
  const original = this.holes[i];
382
393
  const newAbsX = obj.left!;
383
394
  const newAbsY = obj.top!;
384
395
 
396
+ // Validate coordinates to prevent NaN issues
397
+ if (isNaN(newAbsX) || isNaN(newAbsY)) {
398
+ console.error("HoleTool: Invalid marker coordinates", {
399
+ newAbsX,
400
+ newAbsY,
401
+ });
402
+ return original;
403
+ }
404
+
385
405
  // Get current scale to denormalize offsets
386
406
  const scale = this.currentGeometry?.scale || 1;
387
407
  const unit = this.currentGeometry?.unit || "mm";
@@ -435,7 +455,7 @@ export class HoleTool implements Extension {
435
455
  by = bottom;
436
456
  break;
437
457
  }
438
-
458
+
439
459
  return {
440
460
  ...original,
441
461
  // Denormalize offset back to physical units (mm)
@@ -444,11 +464,14 @@ export class HoleTool implements Extension {
444
464
  // Clear direct coordinates if we use anchor
445
465
  x: undefined,
446
466
  y: undefined,
467
+ // Ensure other properties are preserved
468
+ innerRadius: original.innerRadius,
469
+ outerRadius: original.outerRadius,
470
+ shape: original.shape || "circle",
447
471
  };
448
472
  }
449
473
 
450
474
  // If no anchor, use normalized coordinates relative to Dieline Geometry
451
- // normalized = (absolute - (center - width/2)) / width
452
475
  let normalizedX = 0.5;
453
476
  let normalizedY = 0.5;
454
477
 
@@ -459,19 +482,23 @@ export class HoleTool implements Extension {
459
482
  normalizedX = width > 0 ? (newAbsX - left) / width : 0.5;
460
483
  normalizedY = height > 0 ? (newAbsY - top) / height : 0.5;
461
484
  } else {
462
- // Fallback to Canvas normalization if no geometry (should rare)
485
+ // Fallback to Canvas normalization
463
486
  const { width, height } = this.canvasService!.canvas;
464
487
  normalizedX = Coordinate.toNormalized(newAbsX, width || 800);
465
488
  normalizedY = Coordinate.toNormalized(newAbsY, height || 600);
466
489
  }
467
-
490
+
468
491
  return {
469
492
  ...original,
470
493
  x: normalizedX,
471
494
  y: normalizedY,
472
- // Ensure radii are preserved
495
+ // Clear offsets if we are using direct normalized coordinates
496
+ offsetX: undefined,
497
+ offsetY: undefined,
498
+ // Ensure other properties are preserved
473
499
  innerRadius: original?.innerRadius ?? 15,
474
500
  outerRadius: original?.outerRadius ?? 25,
501
+ shape: original?.shape || "circle",
475
502
  };
476
503
  });
477
504
 
@@ -544,26 +571,49 @@ export class HoleTool implements Extension {
544
571
  { width: geometry.width, height: geometry.height } // Use geometry dims instead of canvas
545
572
  );
546
573
 
547
- const innerCircle = new Circle({
548
- radius: visualInnerRadius,
549
- fill: "transparent",
550
- stroke: "red",
551
- strokeWidth: 2,
552
- originX: "center",
553
- originY: "center",
554
- });
574
+ const isSquare = hole.shape === "square";
575
+
576
+ const innerMarker = isSquare
577
+ ? new Rect({
578
+ width: visualInnerRadius * 2,
579
+ height: visualInnerRadius * 2,
580
+ fill: "transparent",
581
+ stroke: "red",
582
+ strokeWidth: 2,
583
+ originX: "center",
584
+ originY: "center",
585
+ })
586
+ : new Circle({
587
+ radius: visualInnerRadius,
588
+ fill: "transparent",
589
+ stroke: "red",
590
+ strokeWidth: 2,
591
+ originX: "center",
592
+ originY: "center",
593
+ });
555
594
 
556
- const outerCircle = new Circle({
557
- radius: visualOuterRadius,
558
- fill: "transparent",
559
- stroke: "#666",
560
- strokeWidth: 1,
561
- strokeDashArray: [5, 5],
562
- originX: "center",
563
- originY: "center",
564
- });
595
+ const outerMarker = isSquare
596
+ ? new Rect({
597
+ width: visualOuterRadius * 2,
598
+ height: visualOuterRadius * 2,
599
+ fill: "transparent",
600
+ stroke: "#666",
601
+ strokeWidth: 1,
602
+ strokeDashArray: [5, 5],
603
+ originX: "center",
604
+ originY: "center",
605
+ })
606
+ : new Circle({
607
+ radius: visualOuterRadius,
608
+ fill: "transparent",
609
+ stroke: "#666",
610
+ strokeWidth: 1,
611
+ strokeDashArray: [5, 5],
612
+ originX: "center",
613
+ originY: "center",
614
+ });
565
615
 
566
- const holeGroup = new Group([outerCircle, innerCircle], {
616
+ const holeGroup = new Group([outerMarker, innerMarker], {
567
617
  left: pos.x,
568
618
  top: pos.y,
569
619
  originX: "center",