@pooder/kit 3.0.1 → 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
@@ -9,7 +9,11 @@ import {
9
9
  import { Circle, Group, Point } from "fabric";
10
10
  import CanvasService from "./CanvasService";
11
11
  import { DielineGeometry } from "./dieline";
12
- import { getNearestPointOnDieline, HoleData } from "./geometry";
12
+ import {
13
+ getNearestPointOnDieline,
14
+ HoleData,
15
+ resolveHolePosition,
16
+ } from "./geometry";
13
17
  import { Coordinate } from "./coordinate";
14
18
 
15
19
  export class HoleTool implements Extension {
@@ -19,10 +23,7 @@ export class HoleTool implements Extension {
19
23
  name: "HoleTool",
20
24
  };
21
25
 
22
- private innerRadius: number = 15;
23
- private outerRadius: number = 25;
24
- private style: "solid" | "dashed" = "solid";
25
- private holes: Array<{ x: number; y: number }> = [];
26
+ private holes: HoleData[] = [];
26
27
  private constraintTarget: "original" | "bleed" = "bleed";
27
28
 
28
29
  private canvasService?: CanvasService;
@@ -39,12 +40,9 @@ export class HoleTool implements Extension {
39
40
 
40
41
  constructor(
41
42
  options?: Partial<{
42
- innerRadius: number;
43
- outerRadius: number;
44
- style: "solid" | "dashed";
45
- holes: Array<{ x: number; y: number }>;
43
+ holes: HoleData[];
46
44
  constraintTarget: "original" | "bleed";
47
- }>,
45
+ }>
48
46
  ) {
49
47
  if (options) {
50
48
  Object.assign(this, options);
@@ -61,64 +59,31 @@ export class HoleTool implements Extension {
61
59
  }
62
60
 
63
61
  const configService = context.services.get<ConfigurationService>(
64
- "ConfigurationService",
62
+ "ConfigurationService"
65
63
  );
66
64
  if (configService) {
67
65
  // Load initial config
68
- this.innerRadius = configService.get(
69
- "hole.innerRadius",
70
- this.innerRadius,
71
- );
72
- this.outerRadius = configService.get(
73
- "hole.outerRadius",
74
- this.outerRadius,
75
- );
76
- this.style = configService.get("hole.style", this.style);
77
66
  this.constraintTarget = configService.get(
78
67
  "hole.constraintTarget",
79
- this.constraintTarget,
68
+ this.constraintTarget
80
69
  );
81
70
 
82
71
  // Load holes from dieline.holes (SSOT)
83
- const dielineHoles = configService.get("dieline.holes", []);
84
- if (this.canvasService) {
85
- const { width, height } = this.canvasService.canvas;
86
- this.holes = dielineHoles.map((h: any) => {
87
- const p = Coordinate.denormalizePoint(h, {
88
- width: width || 800,
89
- height: height || 600,
90
- });
91
- return { x: p.x, y: p.y };
92
- });
93
- }
94
-
72
+ this.holes = configService.get("dieline.holes", []);
73
+
95
74
  // Listen for changes
96
75
  configService.onAnyChange((e: { key: string; value: any }) => {
97
76
  if (this.isUpdatingConfig) return;
98
77
 
99
- if (e.key.startsWith("hole.")) {
100
- const prop = e.key.split(".")[1];
101
- if (prop && prop in this) {
102
- (this as any)[prop] = e.value;
103
- this.redraw();
104
- // Allow syncHolesToDieline to run to update dieline.holes
105
- this.syncHolesToDieline();
106
- }
78
+ if (e.key === "hole.constraintTarget") {
79
+ this.constraintTarget = e.value;
80
+ this.enforceConstraints();
107
81
  }
82
+
108
83
  // Listen for dieline.holes changes (e.g. from undo/redo or other sources)
109
84
  if (e.key === "dieline.holes") {
110
- const holes = e.value || [];
111
- if (this.canvasService) {
112
- const { width, height } = this.canvasService.canvas;
113
- this.holes = holes.map((h: any) => {
114
- const p = Coordinate.denormalizePoint(h, {
115
- width: width || 800,
116
- height: height || 600,
117
- });
118
- return { x: p.x, y: p.y };
119
- });
120
- this.redraw();
121
- }
85
+ this.holes = e.value || [];
86
+ this.redraw();
122
87
  }
123
88
  });
124
89
  }
@@ -135,29 +100,6 @@ export class HoleTool implements Extension {
135
100
  contribute() {
136
101
  return {
137
102
  [ContributionPointIds.CONFIGURATIONS]: [
138
- {
139
- id: "hole.innerRadius",
140
- type: "number",
141
- label: "Inner Radius",
142
- min: 1,
143
- max: 100,
144
- default: 15,
145
- },
146
- {
147
- id: "hole.outerRadius",
148
- type: "number",
149
- label: "Outer Radius",
150
- min: 1,
151
- max: 100,
152
- default: 25,
153
- },
154
- {
155
- id: "hole.style",
156
- type: "select",
157
- label: "Line Style",
158
- options: ["solid", "dashed"],
159
- default: "solid",
160
- },
161
103
  {
162
104
  id: "hole.constraintTarget",
163
105
  type: "select",
@@ -183,13 +125,25 @@ export class HoleTool implements Extension {
183
125
  } as any);
184
126
  }
185
127
 
186
- this.innerRadius = 15;
187
- this.outerRadius = 25;
188
- this.style = "solid";
189
- this.holes = [defaultPos];
128
+ const { width, height } = this.canvasService.canvas;
129
+ const normalizedHole = Coordinate.normalizePoint(defaultPos, {
130
+ width: width || 800,
131
+ height: height || 600,
132
+ });
190
133
 
191
- this.redraw();
192
- this.syncHolesToDieline();
134
+ const configService = this.context?.services.get<ConfigurationService>(
135
+ "ConfigurationService"
136
+ );
137
+ if (configService) {
138
+ configService.update("dieline.holes", [
139
+ {
140
+ x: normalizedHole.x,
141
+ y: normalizedHole.y,
142
+ innerRadius: 15,
143
+ outerRadius: 25,
144
+ },
145
+ ]);
146
+ }
193
147
  return true;
194
148
  },
195
149
  },
@@ -197,10 +151,43 @@ export class HoleTool implements Extension {
197
151
  command: "addHole",
198
152
  title: "Add Hole",
199
153
  handler: (x: number, y: number) => {
200
- if (!this.holes) this.holes = [];
201
- this.holes.push({ x, y });
202
- this.redraw();
203
- this.syncHolesToDieline();
154
+ if (!this.canvasService) return false;
155
+
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
+ }
171
+
172
+ const configService = this.context?.services.get<ConfigurationService>(
173
+ "ConfigurationService"
174
+ );
175
+
176
+ if (configService) {
177
+ const currentHoles = configService.get("dieline.holes", []) as HoleData[];
178
+ // Use last hole's radii or default
179
+ const lastHole = currentHoles[currentHoles.length - 1];
180
+ const innerRadius = lastHole?.innerRadius ?? 15;
181
+ const outerRadius = lastHole?.outerRadius ?? 25;
182
+
183
+ const newHole = {
184
+ x: normalizedX,
185
+ y: normalizedY,
186
+ innerRadius,
187
+ outerRadius,
188
+ };
189
+ configService.update("dieline.holes", [...currentHoles, newHole]);
190
+ }
204
191
  return true;
205
192
  },
206
193
  },
@@ -208,9 +195,12 @@ export class HoleTool implements Extension {
208
195
  command: "clearHoles",
209
196
  title: "Clear Holes",
210
197
  handler: () => {
211
- this.holes = [];
212
- this.redraw();
213
- this.syncHolesToDieline();
198
+ const configService = this.context?.services.get<ConfigurationService>(
199
+ "ConfigurationService"
200
+ );
201
+ if (configService) {
202
+ configService.update("dieline.holes", []);
203
+ }
214
204
  return true;
215
205
  },
216
206
  },
@@ -274,6 +264,9 @@ export class HoleTool implements Extension {
274
264
 
275
265
  if (!this.currentGeometry) return;
276
266
 
267
+ const index = target.data?.index ?? -1;
268
+ const holeData = this.holes[index];
269
+
277
270
  // Calculate effective geometry based on constraint target
278
271
  const effectiveOffset =
279
272
  this.constraintTarget === "original"
@@ -284,13 +277,18 @@ export class HoleTool implements Extension {
284
277
  width: Math.max(0, this.currentGeometry.width + effectiveOffset * 2),
285
278
  height: Math.max(
286
279
  0,
287
- this.currentGeometry.height + effectiveOffset * 2,
280
+ this.currentGeometry.height + effectiveOffset * 2
288
281
  ),
289
282
  radius: Math.max(0, this.currentGeometry.radius + effectiveOffset),
290
283
  };
291
284
 
292
285
  const p = new Point(target.left, target.top);
293
- const newPos = this.calculateConstrainedPosition(p, constraintGeometry);
286
+ const newPos = this.calculateConstrainedPosition(
287
+ p,
288
+ constraintGeometry,
289
+ holeData?.innerRadius ?? 15,
290
+ holeData?.outerRadius ?? 25
291
+ );
294
292
 
295
293
  target.set({
296
294
  left: newPos.x,
@@ -306,7 +304,14 @@ export class HoleTool implements Extension {
306
304
  if (!target || target.data?.type !== "hole-marker") return;
307
305
 
308
306
  // Update state when hole is moved
309
- 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
+ }
310
315
  };
311
316
  canvas.on("object:modified", this.handleModified);
312
317
  }
@@ -316,25 +321,6 @@ export class HoleTool implements Extension {
316
321
 
317
322
  private initializeHoles() {
318
323
  if (!this.canvasService) return;
319
- // Default hole if none exist
320
- if (!this.holes || this.holes.length === 0) {
321
- let defaultPos = { x: this.canvasService.canvas.width! / 2, y: 50 };
322
-
323
- if (this.currentGeometry) {
324
- const g = this.currentGeometry;
325
- // Default to Top-Center of Dieline shape
326
- const topCenter = { x: g.x, y: g.y - g.height / 2 };
327
- // Snap to exact shape edge
328
- const snapped = getNearestPointOnDieline(topCenter, {
329
- ...g,
330
- holes: [],
331
- } as any);
332
- defaultPos = snapped;
333
- }
334
-
335
- this.holes = [defaultPos];
336
- }
337
-
338
324
  this.redraw();
339
325
  this.syncHolesToDieline();
340
326
  }
@@ -383,38 +369,127 @@ export class HoleTool implements Extension {
383
369
  .getObjects()
384
370
  .filter((obj: any) => obj.data?.type === "hole-marker");
385
371
 
386
- const holes = objects.map((obj) => ({ x: obj.left!, y: obj.top! }));
387
- this.holes = holes;
372
+ // Sort objects by index
373
+ objects.sort(
374
+ (a: any, b: any) => (a.data?.index ?? 0) - (b.data?.index ?? 0)
375
+ );
376
+
377
+ // 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
+ const newHoles = objects.map((obj, i) => {
381
+ const original = this.holes[i];
382
+ const newAbsX = obj.left!;
383
+ const newAbsY = obj.top!;
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
+
390
+ if (original && original.anchor && this.currentGeometry) {
391
+ // Reverse calculate offset from anchor
392
+ const { x, y, width, height } = this.currentGeometry;
393
+ let bx = x;
394
+ let by = y;
395
+ const left = x - width / 2;
396
+ const right = x + width / 2;
397
+ const top = y - height / 2;
398
+ const bottom = y + height / 2;
399
+
400
+ switch (original.anchor) {
401
+ case "top-left":
402
+ bx = left;
403
+ by = top;
404
+ break;
405
+ case "top-center":
406
+ bx = x;
407
+ by = top;
408
+ break;
409
+ case "top-right":
410
+ bx = right;
411
+ by = top;
412
+ break;
413
+ case "center-left":
414
+ bx = left;
415
+ by = y;
416
+ break;
417
+ case "center":
418
+ bx = x;
419
+ by = y;
420
+ break;
421
+ case "center-right":
422
+ bx = right;
423
+ by = y;
424
+ break;
425
+ case "bottom-left":
426
+ bx = left;
427
+ by = bottom;
428
+ break;
429
+ case "bottom-center":
430
+ bx = x;
431
+ by = bottom;
432
+ break;
433
+ case "bottom-right":
434
+ bx = right;
435
+ by = bottom;
436
+ break;
437
+ }
438
+
439
+ return {
440
+ ...original,
441
+ // Denormalize offset back to physical units (mm)
442
+ offsetX: (newAbsX - bx) / scale / unitScale,
443
+ offsetY: (newAbsY - by) / scale / unitScale,
444
+ // Clear direct coordinates if we use anchor
445
+ x: undefined,
446
+ y: undefined,
447
+ };
448
+ }
449
+
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;
388
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
+ }
467
+
468
+ return {
469
+ ...original,
470
+ x: normalizedX,
471
+ y: normalizedY,
472
+ // Ensure radii are preserved
473
+ innerRadius: original?.innerRadius ?? 15,
474
+ outerRadius: original?.outerRadius ?? 25,
475
+ };
476
+ });
477
+
478
+ this.holes = newHoles;
389
479
  this.syncHolesToDieline();
390
480
  }
391
481
 
392
482
  private syncHolesToDieline() {
393
483
  if (!this.context || !this.canvasService) return;
394
484
 
395
- const { holes, innerRadius, outerRadius } = this;
396
- const currentHoles = holes || [];
397
- const width = this.canvasService.canvas.width || 800;
398
- const height = this.canvasService.canvas.height || 600;
399
-
400
485
  const configService = this.context.services.get<ConfigurationService>(
401
- "ConfigurationService",
486
+ "ConfigurationService"
402
487
  );
403
488
 
404
489
  if (configService) {
405
490
  this.isUpdatingConfig = true;
406
491
  try {
407
- // Update dieline.holes (Normalized coordinates)
408
- const normalizedHoles = currentHoles.map((h) => {
409
- const p = Coordinate.normalizePoint(h, { width, height });
410
- return {
411
- x: p.x,
412
- y: p.y,
413
- innerRadius,
414
- outerRadius,
415
- };
416
- });
417
- configService.update("dieline.holes", normalizedHoles);
492
+ configService.update("dieline.holes", this.holes);
418
493
  } finally {
419
494
  this.isUpdatingConfig = false;
420
495
  }
@@ -424,6 +499,7 @@ export class HoleTool implements Extension {
424
499
  private redraw() {
425
500
  if (!this.canvasService) return;
426
501
  const canvas = this.canvasService.canvas;
502
+ const { width, height } = canvas;
427
503
 
428
504
  // Remove existing holes
429
505
  const existing = canvas
@@ -431,16 +507,45 @@ export class HoleTool implements Extension {
431
507
  .filter((obj: any) => obj.data?.type === "hole-marker");
432
508
  existing.forEach((obj) => canvas.remove(obj));
433
509
 
434
- const { innerRadius, outerRadius, style, holes } = this;
510
+ const holes = this.holes;
435
511
 
436
512
  if (!holes || holes.length === 0) {
437
513
  this.canvasService.requestRenderAll();
438
514
  return;
439
515
  }
440
516
 
517
+ // Resolve geometry if needed for anchors
518
+ const geometry = this.currentGeometry || {
519
+ x: (width || 800) / 2,
520
+ y: (height || 600) / 2,
521
+ width: width || 800,
522
+ height: height || 600,
523
+ scale: 1, // Default scale if no geometry loaded
524
+ } as any;
525
+
441
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
+
535
+ // Resolve position
536
+ // Apply unit conversion and scale to offsets before resolving (mm -> px)
537
+ const pos = resolveHolePosition(
538
+ {
539
+ ...hole,
540
+ offsetX: (hole.offsetX || 0) * unitScale * scale,
541
+ offsetY: (hole.offsetY || 0) * unitScale * scale,
542
+ },
543
+ geometry,
544
+ { width: geometry.width, height: geometry.height } // Use geometry dims instead of canvas
545
+ );
546
+
442
547
  const innerCircle = new Circle({
443
- radius: innerRadius,
548
+ radius: visualInnerRadius,
444
549
  fill: "transparent",
445
550
  stroke: "red",
446
551
  strokeWidth: 2,
@@ -449,18 +554,18 @@ export class HoleTool implements Extension {
449
554
  });
450
555
 
451
556
  const outerCircle = new Circle({
452
- radius: outerRadius,
557
+ radius: visualOuterRadius,
453
558
  fill: "transparent",
454
559
  stroke: "#666",
455
560
  strokeWidth: 1,
456
- strokeDashArray: style === "dashed" ? [5, 5] : undefined,
561
+ strokeDashArray: [5, 5],
457
562
  originX: "center",
458
563
  originY: "center",
459
564
  });
460
565
 
461
566
  const holeGroup = new Group([outerCircle, innerCircle], {
462
- left: hole.x,
463
- top: hole.y,
567
+ left: pos.x,
568
+ top: pos.y,
464
569
  originX: "center",
465
570
  originY: "center",
466
571
  selectable: true,
@@ -494,12 +599,6 @@ export class HoleTool implements Extension {
494
599
  });
495
600
 
496
601
  canvas.add(holeGroup);
497
-
498
- // Ensure hole markers are always on top of Dieline layer
499
- // Dieline layer uses bringObjectToFront, so we must be aggressive
500
- // But we can't control when Dieline updates.
501
- // Ideally, HoleTool should use a dedicated overlay layer above Dieline.
502
- // For now, let's just bring to front.
503
602
  canvas.bringObjectToFront(holeGroup);
504
603
  });
505
604
 
@@ -513,9 +612,6 @@ export class HoleTool implements Extension {
513
612
  public enforceConstraints(): boolean {
514
613
  const geometry = this.currentGeometry;
515
614
  if (!geometry || !this.canvasService) {
516
- console.log(
517
- "[HoleTool] Skipping enforceConstraints: No geometry or canvas service",
518
- );
519
615
  return false;
520
616
  }
521
617
 
@@ -533,29 +629,36 @@ export class HoleTool implements Extension {
533
629
  .getObjects()
534
630
  .filter((obj: any) => obj.data?.type === "hole-marker");
535
631
 
536
- console.log(
537
- `[HoleTool] Enforcing constraints on ${objects.length} markers`,
538
- );
539
-
540
632
  let changed = false;
541
633
  // Sort objects by index to maintain order in options.holes
542
634
  objects.sort(
543
- (a: any, b: any) => (a.data?.index ?? 0) - (b.data?.index ?? 0),
635
+ (a: any, b: any) => (a.data?.index ?? 0) - (b.data?.index ?? 0)
544
636
  );
545
637
 
546
- const newHoles: { x: number; y: number }[] = [];
638
+ const newHoles: HoleData[] = [];
547
639
 
548
- objects.forEach((obj: any) => {
640
+ objects.forEach((obj: any, i: number) => {
549
641
  const currentPos = new Point(obj.left, obj.top);
550
- const newPos = this.calculateConstrainedPosition(
551
- currentPos,
552
- constraintGeometry,
553
- );
642
+ // We need to pass the hole's radii to calculateConstrainedPosition
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
+ );
554
660
 
555
661
  if (currentPos.distanceFrom(newPos) > 0.1) {
556
- console.log(
557
- `[HoleTool] Moving hole from (${currentPos.x}, ${currentPos.y}) to (${newPos.x}, ${newPos.y})`,
558
- );
559
662
  obj.set({
560
663
  left: newPos.x,
561
664
  top: newPos.y,
@@ -563,23 +666,28 @@ export class HoleTool implements Extension {
563
666
  obj.setCoords();
564
667
  changed = true;
565
668
  }
566
- newHoles.push({ x: obj.left, y: obj.top });
669
+
670
+ // Update data logic is handled in syncHolesFromCanvas which is called on modified
671
+ // But here we are modifying programmatically.
672
+ // We should probably just let the visual update happen, and then sync?
673
+ // Or just push to newHoles list to verify change?
567
674
  });
568
675
 
569
676
  if (changed) {
570
- this.holes = newHoles;
571
- this.canvasService.requestRenderAll();
572
- // We return true instead of syncing directly to avoid recursion
677
+ // If we moved things programmatically, we should update the state
678
+ this.syncHolesFromCanvas();
573
679
  return true;
574
680
  }
575
681
  return false;
576
682
  }
577
683
 
578
- private calculateConstrainedPosition(p: Point, g: DielineGeometry): Point {
684
+ private calculateConstrainedPosition(
685
+ p: Point,
686
+ g: DielineGeometry,
687
+ innerRadius: number,
688
+ outerRadius: number
689
+ ): Point {
579
690
  // Use Paper.js to get accurate nearest point
580
- // This handles ellipses, rects, and rounded rects correctly
581
-
582
- // Convert to holes format for geometry options
583
691
  const options = {
584
692
  ...g,
585
693
  holes: [], // We don't need holes for boundary calculation
@@ -599,7 +707,6 @@ export class HoleTool implements Extension {
599
707
 
600
708
  // Vector from center to nearest point (approximate normal for convex shapes)
601
709
  const center = new Point(g.x, g.y);
602
- const centerToNearest = nearestP.subtract(center);
603
710
 
604
711
  const distToCenter = p.distanceFrom(center);
605
712
  const nearestDistToCenter = nearestP.distanceFrom(center);
@@ -612,9 +719,9 @@ export class HoleTool implements Extension {
612
719
  // Clamp distance
613
720
  let clampedDist = signedDist;
614
721
  if (signedDist > 0) {
615
- clampedDist = Math.min(signedDist, this.innerRadius);
722
+ clampedDist = Math.min(signedDist, innerRadius);
616
723
  } else {
617
- clampedDist = Math.max(signedDist, -this.outerRadius);
724
+ clampedDist = Math.max(signedDist, -outerRadius);
618
725
  }
619
726
 
620
727
  // Reconstruct point