@pooder/kit 3.1.0 → 3.2.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/src/hole.ts CHANGED
@@ -152,12 +152,22 @@ export class HoleTool implements Extension {
152
152
  title: "Add Hole",
153
153
  handler: (x: number, y: number) => {
154
154
  if (!this.canvasService) return false;
155
- const { width, height } = this.canvasService.canvas;
156
155
 
157
- const normalizedHole = Coordinate.normalizePoint(
158
- { x, y },
159
- { width: width || 800, height: height || 600 }
160
- );
156
+ // Normalize relative to Dieline Geometry if available
157
+ let normalizedX = 0.5;
158
+ let normalizedY = 0.5;
159
+
160
+ if (this.currentGeometry) {
161
+ const { x: gx, y: gy, width: gw, height: gh } = this.currentGeometry;
162
+ const left = gx - gw / 2;
163
+ const top = gy - gh / 2;
164
+ normalizedX = gw > 0 ? (x - left) / gw : 0.5;
165
+ normalizedY = gh > 0 ? (y - top) / gh : 0.5;
166
+ } else {
167
+ const { width, height } = this.canvasService.canvas;
168
+ normalizedX = Coordinate.toNormalized(x, width || 800);
169
+ normalizedY = Coordinate.toNormalized(y, height || 600);
170
+ }
161
171
 
162
172
  const configService = this.context?.services.get<ConfigurationService>(
163
173
  "ConfigurationService"
@@ -171,8 +181,8 @@ export class HoleTool implements Extension {
171
181
  const outerRadius = lastHole?.outerRadius ?? 25;
172
182
 
173
183
  const newHole = {
174
- x: normalizedHole.x,
175
- y: normalizedHole.y,
184
+ x: normalizedX,
185
+ y: normalizedY,
176
186
  innerRadius,
177
187
  outerRadius,
178
188
  };
@@ -294,7 +304,14 @@ export class HoleTool implements Extension {
294
304
  if (!target || target.data?.type !== "hole-marker") return;
295
305
 
296
306
  // Update state when hole is moved
297
- this.syncHolesFromCanvas();
307
+ // Ensure final position is constrained (handles case where 'modified' reports unconstrained coords)
308
+ const changed = this.enforceConstraints();
309
+
310
+ // If enforceConstraints changed something, it already synced.
311
+ // If not, we sync manually to save the move (which was valid).
312
+ if (!changed) {
313
+ this.syncHolesFromCanvas();
314
+ }
298
315
  };
299
316
  canvas.on("object:modified", this.handleModified);
300
317
  }
@@ -365,6 +382,11 @@ export class HoleTool implements Extension {
365
382
  const newAbsX = obj.left!;
366
383
  const newAbsY = obj.top!;
367
384
 
385
+ // Get current scale to denormalize offsets
386
+ const scale = this.currentGeometry?.scale || 1;
387
+ const unit = this.currentGeometry?.unit || "mm";
388
+ const unitScale = Coordinate.convertUnit(1, "mm", unit);
389
+
368
390
  if (original && original.anchor && this.currentGeometry) {
369
391
  // Reverse calculate offset from anchor
370
392
  const { x, y, width, height } = this.currentGeometry;
@@ -416,25 +438,37 @@ export class HoleTool implements Extension {
416
438
 
417
439
  return {
418
440
  ...original,
419
- offsetX: newAbsX - bx,
420
- offsetY: newAbsY - by,
441
+ // Denormalize offset back to physical units (mm)
442
+ offsetX: (newAbsX - bx) / scale / unitScale,
443
+ offsetY: (newAbsY - by) / scale / unitScale,
421
444
  // Clear direct coordinates if we use anchor
422
445
  x: undefined,
423
446
  y: undefined,
424
447
  };
425
448
  }
426
449
 
427
- // If no anchor, use normalized coordinates
428
- const { width, height } = this.canvasService!.canvas;
429
- const p = Coordinate.normalizePoint(
430
- { x: newAbsX, y: newAbsY },
431
- { width: width || 800, height: height || 600 }
432
- );
450
+ // If no anchor, use normalized coordinates relative to Dieline Geometry
451
+ // normalized = (absolute - (center - width/2)) / width
452
+ let normalizedX = 0.5;
453
+ let normalizedY = 0.5;
454
+
455
+ if (this.currentGeometry) {
456
+ const { x, y, width, height } = this.currentGeometry;
457
+ const left = x - width / 2;
458
+ const top = y - height / 2;
459
+ normalizedX = width > 0 ? (newAbsX - left) / width : 0.5;
460
+ normalizedY = height > 0 ? (newAbsY - top) / height : 0.5;
461
+ } else {
462
+ // Fallback to Canvas normalization if no geometry (should rare)
463
+ const { width, height } = this.canvasService!.canvas;
464
+ normalizedX = Coordinate.toNormalized(newAbsX, width || 800);
465
+ normalizedY = Coordinate.toNormalized(newAbsY, height || 600);
466
+ }
433
467
 
434
468
  return {
435
469
  ...original,
436
- x: p.x,
437
- y: p.y,
470
+ x: normalizedX,
471
+ y: normalizedY,
438
472
  // Ensure radii are preserved
439
473
  innerRadius: original?.innerRadius ?? 15,
440
474
  outerRadius: original?.outerRadius ?? 25,
@@ -486,18 +520,32 @@ export class HoleTool implements Extension {
486
520
  y: (height || 600) / 2,
487
521
  width: width || 800,
488
522
  height: height || 600,
489
- };
523
+ scale: 1, // Default scale if no geometry loaded
524
+ } as any;
490
525
 
491
526
  holes.forEach((hole, index) => {
527
+ // Geometry scale is needed.
528
+ const scale = geometry.scale || 1;
529
+ const unit = geometry.unit || "mm";
530
+ const unitScale = Coordinate.convertUnit(1, 'mm', unit);
531
+
532
+ const visualInnerRadius = hole.innerRadius * unitScale * scale;
533
+ const visualOuterRadius = hole.outerRadius * unitScale * scale;
534
+
492
535
  // Resolve position
536
+ // Apply unit conversion and scale to offsets before resolving (mm -> px)
493
537
  const pos = resolveHolePosition(
494
- hole,
538
+ {
539
+ ...hole,
540
+ offsetX: (hole.offsetX || 0) * unitScale * scale,
541
+ offsetY: (hole.offsetY || 0) * unitScale * scale,
542
+ },
495
543
  geometry,
496
- { width: width || 800, height: height || 600 }
544
+ { width: geometry.width, height: geometry.height } // Use geometry dims instead of canvas
497
545
  );
498
546
 
499
547
  const innerCircle = new Circle({
500
- radius: hole.innerRadius,
548
+ radius: visualInnerRadius,
501
549
  fill: "transparent",
502
550
  stroke: "red",
503
551
  strokeWidth: 2,
@@ -506,7 +554,7 @@ export class HoleTool implements Extension {
506
554
  });
507
555
 
508
556
  const outerCircle = new Circle({
509
- radius: hole.outerRadius,
557
+ radius: visualOuterRadius,
510
558
  fill: "transparent",
511
559
  stroke: "#666",
512
560
  strokeWidth: 1,
@@ -592,14 +640,23 @@ export class HoleTool implements Extension {
592
640
  objects.forEach((obj: any, i: number) => {
593
641
  const currentPos = new Point(obj.left, obj.top);
594
642
  // We need to pass the hole's radii to calculateConstrainedPosition
595
- const holeData = this.holes[i];
596
-
597
- const newPos = this.calculateConstrainedPosition(
598
- currentPos,
599
- constraintGeometry,
600
- holeData?.innerRadius ?? 15,
601
- holeData?.outerRadius ?? 25
602
- );
643
+ const holeData = this.holes[i];
644
+
645
+ // Scale radii for constraint calculation (since geometry is in pixels)
646
+ // Geometry scale is needed.
647
+ const scale = geometry.scale || 1;
648
+ const unit = geometry.unit || "mm";
649
+ const unitScale = Coordinate.convertUnit(1, 'mm', unit);
650
+
651
+ const innerR = (holeData?.innerRadius ?? 15) * unitScale * scale;
652
+ const outerR = (holeData?.outerRadius ?? 25) * unitScale * scale;
653
+
654
+ const newPos = this.calculateConstrainedPosition(
655
+ currentPos,
656
+ constraintGeometry,
657
+ innerR,
658
+ outerR
659
+ );
603
660
 
604
661
  if (currentPos.distanceFrom(newPos) > 0.1) {
605
662
  obj.set({