@design-core/core 0.0.7 → 51.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.
Files changed (117) hide show
  1. package/.github/workflows/DesignCoreQA.yml +3 -0
  2. package/core/designCore.js +1 -1
  3. package/core/dimensions/alignedDimension.js +7 -7
  4. package/core/dimensions/angularDimension.js +32 -32
  5. package/core/dimensions/{baseCircularDimension.js → circularDimensionBase.js} +19 -19
  6. package/core/dimensions/diametricDimension.js +10 -10
  7. package/core/dimensions/dimension.js +42 -42
  8. package/core/dimensions/{baseDimension.js → dimensionBase.js} +17 -17
  9. package/core/dimensions/{baseLinearDimension.js → linearDimensionBase.js} +9 -9
  10. package/core/dimensions/radialDimension.js +8 -8
  11. package/core/dimensions/rotatedDimension.js +8 -8
  12. package/core/entities/arc.js +22 -22
  13. package/core/entities/arctext.js +13 -13
  14. package/core/entities/circle.js +7 -7
  15. package/core/entities/hatch.js +210 -40
  16. package/core/entities/insert.js +11 -6
  17. package/core/entities/point.js +19 -19
  18. package/core/entities/polyline.js +3 -3
  19. package/core/entities/{basePolyline.js → polylineBase.js} +18 -18
  20. package/core/entities/text.js +4 -4
  21. package/core/lib/boundingBox.js +11 -11
  22. package/core/lib/canvas.js +13 -6
  23. package/core/lib/clipboard.js +2 -2
  24. package/core/lib/dxf/dxf.js +11 -11
  25. package/core/lib/dxf/dxfIterator.js +2 -2
  26. package/core/lib/entityManager.js +19 -19
  27. package/core/lib/inputManager.js +1 -1
  28. package/core/lib/matrix.js +1 -1
  29. package/core/lib/patterns.js +1 -1
  30. package/core/lib/polylineUtils.js +9 -9
  31. package/core/lib/renderers/cairoRenderer.js +5 -5
  32. package/core/lib/renderers/canvasRenderer.js +4 -4
  33. package/core/lib/renderers/pdfRenderer.js +7 -7
  34. package/core/lib/renderers/rendererBase.js +1 -1
  35. package/core/lib/scene.js +1 -1
  36. package/core/lib/selectionManager.js +18 -18
  37. package/core/lib/utils.js +5 -5
  38. package/core/properties/entityProperties.js +2 -1
  39. package/core/properties/flags.js +12 -0
  40. package/core/properties/property.js +8 -3
  41. package/core/properties/propertyManager.js +52 -37
  42. package/core/tables/block.js +50 -46
  43. package/core/tables/dimStyle.js +5 -5
  44. package/core/tables/ltypeManager.js +1 -1
  45. package/core/tables/vport.js +2 -2
  46. package/core/tools/chamfer.js +6 -6
  47. package/core/tools/chamferFilletBase.js +4 -4
  48. package/core/tools/copy.js +8 -8
  49. package/core/tools/copybase.js +3 -3
  50. package/core/tools/copyclip.js +3 -3
  51. package/core/tools/cornerEntity.js +2 -2
  52. package/core/tools/cutclip.js +3 -3
  53. package/core/tools/explode.js +6 -6
  54. package/core/tools/extend.js +13 -13
  55. package/core/tools/fillet.js +6 -6
  56. package/core/tools/matchProp.js +1 -1
  57. package/core/tools/mirror.js +11 -11
  58. package/core/tools/move.js +5 -5
  59. package/core/tools/offset.js +29 -29
  60. package/core/tools/purge.js +10 -10
  61. package/core/tools/rotate.js +16 -16
  62. package/core/tools/scale.js +6 -6
  63. package/core/tools/trim.js +15 -15
  64. package/cspell.json +323 -0
  65. package/package.json +1 -1
  66. package/test/dimensions/alignedDimension.test.js +3 -3
  67. package/test/dimensions/angularDimension.test.js +7 -7
  68. package/test/dimensions/diametricDimension.test.js +9 -9
  69. package/test/dimensions/dimension.test.js +12 -12
  70. package/test/dimensions/{baseDimension.test.js → dimensionBase.test.js} +12 -12
  71. package/test/dimensions/{baseLinearDimension.test.js → linearDimensionBase.test.js} +7 -7
  72. package/test/dimensions/radialDimension.test.js +9 -9
  73. package/test/dimensions/rotatedDimension.test.js +4 -4
  74. package/test/entities/arc.test.js +10 -10
  75. package/test/entities/arctext.test.js +6 -6
  76. package/test/entities/circle.test.js +18 -18
  77. package/test/entities/hatch.test.js +362 -39
  78. package/test/entities/insert.test.js +9 -9
  79. package/test/entities/point.test.js +3 -3
  80. package/test/entities/{basePolyline.test.js → polylineBase.test.js} +131 -62
  81. package/test/entities/text.test.js +19 -19
  82. package/test/integration/exportIntegration.test.js +3 -3
  83. package/test/integration/userInputIntegration.test.js +5 -5
  84. package/test/lib/boundingBox.test.js +3 -3
  85. package/test/lib/canvas.test.js +9 -9
  86. package/test/lib/clipboard.test.js +73 -0
  87. package/test/lib/inputManager.test.js +4 -4
  88. package/test/lib/scene.test.js +9 -9
  89. package/test/lib/selectionManager.test.js +3 -3
  90. package/test/lib/snapping.test.js +1 -1
  91. package/test/properties/flags.test.js +24 -0
  92. package/test/properties/propertyManager.test.js +93 -22
  93. package/test/renderers/pdfRenderer.test.js +2 -2
  94. package/test/tables/block.test.js +63 -14
  95. package/test/tables/layerManager.test.js +2 -2
  96. package/test/test-helpers/hatchGridRenderer.js +3 -3
  97. package/test/test-helpers/test-helpers.js +6 -6
  98. package/test/tools/chamfer.test.js +60 -60
  99. package/test/tools/chamferFilletBase.test.js +36 -36
  100. package/test/tools/copy.test.js +6 -6
  101. package/test/tools/copybase.test.js +2 -2
  102. package/test/tools/copyclip.test.js +2 -2
  103. package/test/tools/cornerEntity.test.js +8 -8
  104. package/test/tools/cutclip.test.js +2 -2
  105. package/test/tools/erase.test.js +6 -6
  106. package/test/tools/explode.test.js +2 -2
  107. package/test/tools/extend.test.js +84 -84
  108. package/test/tools/fillet.test.js +62 -62
  109. package/test/tools/matchProp.test.js +6 -6
  110. package/test/tools/mirror.test.js +26 -26
  111. package/test/tools/move.test.js +7 -7
  112. package/test/tools/offset.test.js +65 -65
  113. package/test/tools/purge.test.js +4 -4
  114. package/test/tools/rotate.test.js +10 -10
  115. package/test/tools/scale.test.js +28 -28
  116. package/test/tools/trim.test.js +109 -109
  117. package/test/tools/zoom.test.js +15 -17
@@ -1,4 +1,4 @@
1
- import { BasePolyline } from '../../core/entities/basePolyline.js';
1
+ import { PolylineBase } from '../../core/entities/polylineBase.js';
2
2
  import { Point } from '../../core/entities/point';
3
3
  import { Polyline } from '../../core/entities/polyline.js';
4
4
  import { Line } from '../../core/entities/line.js';
@@ -30,7 +30,7 @@ test.each(inputScenarios)('Polyline.execute handles $desc', async (scenario) =>
30
30
  const { inputs, expectedPoints } = scenario;
31
31
 
32
32
  await withMockInput(DesignCore.Scene, inputs, async () => {
33
- const polyline = new BasePolyline({});
33
+ const polyline = new PolylineBase({});
34
34
  await polyline.execute();
35
35
 
36
36
  expect(polyline.points.length).toBe(expectedPoints);
@@ -46,7 +46,7 @@ test.each(inputScenarios)('Polyline.execute handles $desc', async (scenario) =>
46
46
  }, { extraMethods: { actionCommand: () => {} } });
47
47
  });
48
48
 
49
- test('Test BasePolyline.draw calls renderer.drawShape with toPolylinePoints', () => {
49
+ test('Test PolylineBase.draw calls renderer.drawShape with toPolylinePoints', () => {
50
50
  const polyline = new Polyline({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 5)] });
51
51
  let capturedPoints;
52
52
  const mockRenderer = {
@@ -58,11 +58,11 @@ test('Test BasePolyline.draw calls renderer.drawShape with toPolylinePoints', ()
58
58
  expect(capturedPoints).toEqual(polyline.toPolylinePoints());
59
59
  });
60
60
 
61
- test('Test BasePolyline.getClosestSegment', () => {
61
+ test('Test PolylineBase.getClosestSegment', () => {
62
62
  // Polyline with a line and an arc segment
63
63
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10)];
64
64
  points[1].bulge = 1; // Arc from (10,0) to (10,10)
65
- const polyline = new BasePolyline({ points });
65
+ const polyline = new PolylineBase({ points });
66
66
 
67
67
  // Closest to the line segment
68
68
  const testPoint1 = new Point(5, 2);
@@ -77,11 +77,11 @@ test('Test BasePolyline.getClosestSegment', () => {
77
77
 
78
78
  // ─── closing segment (closed polylines) ──────────────────────────────────────
79
79
 
80
- test('BasePolyline.getClosestSegmentIndex returns closing-segment index for closed polyline', () => {
80
+ test('PolylineBase.getClosestSegmentIndex returns closing-segment index for closed polyline', () => {
81
81
  // Square: P0=(0,0) P1=(10,0) P2=(10,10) P3=(0,10), closed flag set.
82
82
  // Closing segment is P3→P0. A point near the left edge (0,5) is closest to that segment.
83
83
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
84
- const poly = new BasePolyline({ points });
84
+ const poly = new PolylineBase({ points });
85
85
  poly.flags.setFlagValue(1);
86
86
 
87
87
  const closestIdx = poly.getClosestSegmentIndex(new Point(0, 5));
@@ -89,9 +89,9 @@ test('BasePolyline.getClosestSegmentIndex returns closing-segment index for clos
89
89
  expect(closestIdx).toBe(4);
90
90
  });
91
91
 
92
- test('BasePolyline.getClosestSegment returns closing segment as a Line for closed polyline', () => {
92
+ test('PolylineBase.getClosestSegment returns closing segment as a Line for closed polyline', () => {
93
93
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
94
- const poly = new BasePolyline({ points });
94
+ const poly = new PolylineBase({ points });
95
95
  poly.flags.setFlagValue(1);
96
96
 
97
97
  const seg = poly.getClosestSegment(new Point(0, 5));
@@ -103,21 +103,21 @@ test('BasePolyline.getClosestSegment returns closing segment as a Line for close
103
103
  expect(seg.points[1].y).toBeCloseTo(0);
104
104
  });
105
105
 
106
- test('BasePolyline.areConsecutiveSegments returns false for non-adjacent segments on a closed polyline', () => {
106
+ test('PolylineBase.areConsecutiveSegments returns false for non-adjacent segments on a closed polyline', () => {
107
107
  // 4-point closed poly: segments 1 and 3 are NOT adjacent (bug: old code returned true).
108
108
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
109
- const poly = new BasePolyline({ points });
109
+ const poly = new PolylineBase({ points });
110
110
  poly.flags.setFlagValue(1);
111
111
 
112
112
  expect(poly.areConsecutiveSegments(1, 3)).toBe(false);
113
113
  });
114
114
 
115
- test('BasePolyline.areConsecutiveSegments returns true for closing segment and its neighbours', () => {
115
+ test('PolylineBase.areConsecutiveSegments returns true for closing segment and its neighbours', () => {
116
116
  // 4-point closed poly, N=4 (closing segment index).
117
117
  // Seg N-1=3 ends at P3, closing seg starts at P3 → consecutive (diff=1).
118
118
  // Closing seg ends at P0, seg 1 starts at P0 → consecutive (wrap case).
119
119
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
120
- const poly = new BasePolyline({ points });
120
+ const poly = new PolylineBase({ points });
121
121
  poly.flags.setFlagValue(1);
122
122
 
123
123
  expect(poly.areConsecutiveSegments(3, 4)).toBe(true); // diff = 1
@@ -125,11 +125,11 @@ test('BasePolyline.areConsecutiveSegments returns true for closing segment and i
125
125
  expect(poly.areConsecutiveSegments(1, 4)).toBe(true); // wrap (order reversed)
126
126
  });
127
127
 
128
- test('BasePolyline.closestPointOnSegment handles closing-segment index correctly', () => {
128
+ test('PolylineBase.closestPointOnSegment handles closing-segment index correctly', () => {
129
129
  // 4-point square, closing segment = P3=(0,10)→P0=(0,0). Index = 4.
130
130
  // Closest point on that segment to (0, 5) should be (0, 5).
131
131
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
132
- const poly = new BasePolyline({ points });
132
+ const poly = new PolylineBase({ points });
133
133
  poly.flags.setFlagValue(1);
134
134
 
135
135
  const closest = poly.closestPointOnSegment(new Point(0, 5), 4);
@@ -138,9 +138,9 @@ test('BasePolyline.closestPointOnSegment handles closing-segment index correctly
138
138
  });
139
139
 
140
140
 
141
- test('Test BasePolyline.closestPoint', () => {
141
+ test('Test PolylineBase.closestPoint', () => {
142
142
  const points = [new Point(100, 100), new Point(200, 100, -1), new Point(200, 50)];
143
- const polyline = new BasePolyline({ points: points });
143
+ const polyline = new PolylineBase({ points: points });
144
144
  // line segment
145
145
  const point1 = new Point(150, 85);
146
146
  const closest1 = polyline.closestPoint(point1);
@@ -156,14 +156,14 @@ test('Test BasePolyline.closestPoint', () => {
156
156
  expect(closest2[1]).toBeCloseTo(10.857);
157
157
  });
158
158
 
159
- test('Test BasePolyline.boundingBox', () => {
160
- let polyline = new BasePolyline({ points: [new Point(101, 102), new Point(201, 202)] });
159
+ test('Test PolylineBase.boundingBox', () => {
160
+ let polyline = new PolylineBase({ points: [new Point(101, 102), new Point(201, 202)] });
161
161
  expect(polyline.boundingBox().xMin).toBeCloseTo(101);
162
162
  expect(polyline.boundingBox().xMax).toBeCloseTo(201);
163
163
  expect(polyline.boundingBox().yMin).toBeCloseTo(102);
164
164
  expect(polyline.boundingBox().yMax).toBeCloseTo(202);
165
165
 
166
- polyline = new BasePolyline({ points: [new Point(101, 102), new Point(-201, 202)] });
166
+ polyline = new PolylineBase({ points: [new Point(101, 102), new Point(-201, 202)] });
167
167
  expect(polyline.boundingBox().xMin).toBeCloseTo(-201);
168
168
  expect(polyline.boundingBox().xMax).toBeCloseTo(101);
169
169
  expect(polyline.boundingBox().yMin).toBeCloseTo(102);
@@ -173,7 +173,7 @@ test('Test BasePolyline.boundingBox', () => {
173
173
  let points = [new Point(101, 102), new Point(200, 102), new Point(200, 0)];
174
174
  // set the bulge
175
175
  points[1].bulge = -1;
176
- polyline = new BasePolyline({ points: points });
176
+ polyline = new PolylineBase({ points: points });
177
177
  expect(polyline.boundingBox().xMin).toBeCloseTo(101);
178
178
  expect(polyline.boundingBox().xMax).toBeCloseTo(251);
179
179
  expect(polyline.boundingBox().yMin).toBeCloseTo(0);
@@ -182,19 +182,19 @@ test('Test BasePolyline.boundingBox', () => {
182
182
  points = [new Point(101, 102), new Point(200, 102), new Point(200, 0)];
183
183
  // set the bulge
184
184
  points[1].bulge = -1;
185
- polyline = new BasePolyline({ points: points });
185
+ polyline = new PolylineBase({ points: points });
186
186
  expect(polyline.boundingBox().xMin).toBeCloseTo(101);
187
187
  expect(polyline.boundingBox().xMax).toBeCloseTo(251);
188
188
  expect(polyline.boundingBox().yMin).toBeCloseTo(0);
189
189
  expect(polyline.boundingBox().yMax).toBeCloseTo(102);
190
190
  });
191
191
 
192
- test('Test BasePolyline.getBulgeFromSegment', () => {
192
+ test('Test PolylineBase.getBulgeFromSegment', () => {
193
193
  // start point: 100,0
194
- // center: 100,50
194
+ // centre: 100,50
195
195
  // radius: 50
196
196
  const points = [new Point(), new Point(100, 0)];
197
- const polyline = new BasePolyline({ points: points });
197
+ const polyline = new PolylineBase({ points: points });
198
198
 
199
199
  // zero delta angle:
200
200
  // bulge: 0
@@ -431,10 +431,10 @@ SEQEND
431
431
  expect(file.contents).toEqual(dxfString);
432
432
  });
433
433
 
434
- test('Test BasePolyline.toPolylinePoints', () => {
434
+ test('Test PolylineBase.toPolylinePoints', () => {
435
435
  const points = [new Point(100, 100), new Point(200, 100), new Point(200, 50)];
436
436
  points[1].bulge = -1;
437
- const polyline = new BasePolyline({ points: points });
437
+ const polyline = new PolylineBase({ points: points });
438
438
 
439
439
  const polylinePoints = polyline.toPolylinePoints();
440
440
  expect(polylinePoints[0].x).toBe(100);
@@ -450,10 +450,10 @@ test('Test BasePolyline.toPolylinePoints', () => {
450
450
  expect(polylinePoints[2].bulge).toBe(0);
451
451
  });
452
452
 
453
- test('Test BasePolyline.toPolylinePoints returns defensive copy for open polyline', () => {
453
+ test('Test PolylineBase.toPolylinePoints returns defensive copy for open polyline', () => {
454
454
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10)];
455
455
  points[1].bulge = -1;
456
- const polyline = new BasePolyline({ points: points });
456
+ const polyline = new PolylineBase({ points: points });
457
457
 
458
458
  const result = polyline.toPolylinePoints();
459
459
  // Mutate the returned array and one of the returned points
@@ -466,13 +466,13 @@ test('Test BasePolyline.toPolylinePoints returns defensive copy for open polylin
466
466
  expect(polyline.points[1].bulge).toBe(-1);
467
467
  });
468
468
 
469
- test('Test BasePolyline.toPolylinePoints closed polyline appends closure point', () => {
469
+ test('Test PolylineBase.toPolylinePoints closed polyline appends closure point', () => {
470
470
  // A triangle closed via the Close command (flag bit 1 set, no duplicate
471
471
  // point stored in this.points).
472
472
  const points = [new Point(0, 0), new Point(10, 0), new Point(5, 10)];
473
473
  const flags = new Flags();
474
474
  flags.addValue(1); // closed flag
475
- const polyline = new BasePolyline({ points: points, flags: flags });
475
+ const polyline = new PolylineBase({ points: points, flags: flags });
476
476
 
477
477
  const polylinePoints = polyline.toPolylinePoints();
478
478
  // Should have 4 points: the 3 vertices plus the closure point
@@ -506,7 +506,7 @@ test('Polyline.execute handles Close option', async () => {
506
506
  test('Polyline.execute supports Undo in line mode', async () => {
507
507
  const inputs = [new Point(0, 0), new Point(10, 0), new Point(20, 0), 'Undo', new Point(10, 10), 'Close'];
508
508
  await withMockInput(DesignCore.Scene, inputs, async () => {
509
- const polyline = new BasePolyline({});
509
+ const polyline = new PolylineBase({});
510
510
  await polyline.execute();
511
511
  expect(polyline.points.length).toBe(3);
512
512
  expect(polyline.points[0]).toEqual(inputs[0]);
@@ -519,7 +519,7 @@ test('Polyline.execute supports Undo in line mode', async () => {
519
519
  test('Polyline.execute supports Undo in arc mode', async () => {
520
520
  const inputs = [new Point(0, 0), new Point(10, 0), 'Arc', new Point(10, 10), new Point(20, 20), 'Undo', 'Line', new Point(10, 20), 'Close'];
521
521
  await withMockInput(DesignCore.Scene, inputs, async () => {
522
- const polyline = new BasePolyline({});
522
+ const polyline = new PolylineBase({});
523
523
  await polyline.execute();
524
524
  expect(polyline.points.length).toBe(4);
525
525
  expect(polyline.points[0]).toEqual(inputs[0]);
@@ -537,7 +537,7 @@ test('Polyline.execute supports Undo in arc mode', async () => {
537
537
  test('Polyline.execute supports Arc/Line mode switching', async () => {
538
538
  const inputs = [new Point(0, 0), new Point(10, 0), 'Arc', new Point(10, 10), 'Line', new Point(20, 10), 'Close'];
539
539
  await withMockInput(DesignCore.Scene, inputs, async () => {
540
- const polyline = new BasePolyline({});
540
+ const polyline = new PolylineBase({});
541
541
  await polyline.execute();
542
542
  expect(polyline.points.length).toBe(4);
543
543
  expect(polyline.points[0]).toEqual(inputs[0]);
@@ -553,29 +553,29 @@ test('Polyline.execute supports Arc/Line mode switching', async () => {
553
553
 
554
554
  // ─── Auto-close when first and last point are the same ───────────────────────
555
555
 
556
- test('BasePolyline constructor auto-closes when last point matches first', () => {
556
+ test('PolylineBase constructor auto-closes when last point matches first', () => {
557
557
  // Simulate a polyline loaded with a redundant closing point (e.g. from DXF data)
558
558
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 0)];
559
- const polyline = new BasePolyline({ points });
559
+ const polyline = new PolylineBase({ points });
560
560
 
561
561
  // Duplicate point should be removed and the closed flag set
562
562
  expect(polyline.points.length).toBe(3);
563
563
  expect(polyline.flags.hasFlag(1)).toBe(true);
564
564
  });
565
565
 
566
- test('BasePolyline constructor does not auto-close when fewer than 3 unique points remain after pop', () => {
566
+ test('PolylineBase constructor does not auto-close when fewer than 3 unique points remain after pop', () => {
567
567
  // [A, B, A] — only 2 unique points after removing the duplicate; must not auto-close
568
568
  const points = [new Point(0, 0), new Point(10, 0), new Point(0, 0)];
569
- const polyline = new BasePolyline({ points });
569
+ const polyline = new PolylineBase({ points });
570
570
 
571
571
  expect(polyline.points.length).toBe(3);
572
572
  expect(polyline.flags.hasFlag(1)).toBe(false);
573
573
  });
574
574
 
575
- test('BasePolyline constructor does not auto-close with only 2 identical points', () => {
575
+ test('PolylineBase constructor does not auto-close with only 2 identical points', () => {
576
576
  // Only 2 unique points — not enough to form a closed polygon
577
577
  const points = [new Point(0, 0), new Point(0, 0)];
578
- const polyline = new BasePolyline({ points });
578
+ const polyline = new PolylineBase({ points });
579
579
 
580
580
  expect(polyline.points.length).toBe(2);
581
581
  expect(polyline.flags.hasFlag(1)).toBe(false);
@@ -593,14 +593,14 @@ test('Polyline.execute auto-closes when start point is re-entered', async () =>
593
593
  // The constructor will have auto-closed the entity when pt4 == pt1
594
594
  if (entity.points.length === 4 && entity.points.at(-1).isSame(pt1)) {
595
595
  // simulate what the constructor does: a new entity created from this data would auto-close
596
- const rebuilt = new BasePolyline({ points: [...entity.points] });
596
+ const rebuilt = new PolylineBase({ points: [...entity.points] });
597
597
  if (rebuilt.flags.hasFlag(1)) autoClosedCount++;
598
598
  }
599
599
  return 0;
600
600
  };
601
601
 
602
602
  await withMockInput(DesignCore.Scene, [pt1, pt2, pt3, pt4], async () => {
603
- const polyline = new BasePolyline({});
603
+ const polyline = new PolylineBase({});
604
604
  await polyline.execute();
605
605
  // execute does not exit early — the duplicate point is on the instance
606
606
  expect(polyline.points.length).toBe(4);
@@ -609,10 +609,10 @@ test('Polyline.execute auto-closes when start point is re-entered', async () =>
609
609
  }, { extraMethods: { actionCommand: mockActionCommand } });
610
610
  });
611
611
 
612
- test('BasePolyline.fromPolylinePoints round-trip preserves open polyline', () => {
613
- const poly = new BasePolyline({ points: [new Point(0, 0, 0.5), new Point(10, 0), new Point(10, 10)] });
612
+ test('PolylineBase.fromPolylinePoints round-trip preserves open polyline', () => {
613
+ const poly = new PolylineBase({ points: [new Point(0, 0, 0.5), new Point(10, 0), new Point(10, 10)] });
614
614
  const polyPts = poly.toPolylinePoints();
615
- const rebuilt = new BasePolyline({});
615
+ const rebuilt = new PolylineBase({});
616
616
  rebuilt.fromPolylinePoints(polyPts);
617
617
  expect(rebuilt.points.length).toBe(3);
618
618
  expect(rebuilt.points[0].x).toBe(0);
@@ -622,16 +622,16 @@ test('BasePolyline.fromPolylinePoints round-trip preserves open polyline', () =>
622
622
  expect(rebuilt.points[2].y).toBe(10);
623
623
  });
624
624
 
625
- test('BasePolyline.fromPolylinePoints round-trip strips closure point for closed polyline', () => {
625
+ test('PolylineBase.fromPolylinePoints round-trip strips closure point for closed polyline', () => {
626
626
  const flags = new Flags();
627
627
  flags.addValue(1);
628
- const poly = new BasePolyline({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)], flags: flags });
628
+ const poly = new PolylineBase({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)], flags: flags });
629
629
  const polyPts = poly.toPolylinePoints();
630
630
  // toPolylinePoints appends a closure copy of the first point
631
631
  expect(polyPts.length).toBe(5);
632
632
  const flags2 = new Flags();
633
633
  flags2.addValue(1);
634
- const rebuilt = new BasePolyline({ flags: flags2 });
634
+ const rebuilt = new PolylineBase({ flags: flags2 });
635
635
  rebuilt.fromPolylinePoints(polyPts);
636
636
  expect(rebuilt.points.length).toBe(4);
637
637
  expect(rebuilt.points[0].x).toBe(0);
@@ -641,12 +641,12 @@ test('BasePolyline.fromPolylinePoints round-trip strips closure point for closed
641
641
 
642
642
  // ─── closestPoint: closed polyline closing segment ───────────────────────────
643
643
 
644
- test('BasePolyline.closestPoint finds closest point on closing segment of closed polyline', () => {
644
+ test('PolylineBase.closestPoint finds closest point on closing segment of closed polyline', () => {
645
645
  // Square: P0=(0,0), P1=(10,0), P2=(10,10), P3=(0,10), closed flag set.
646
646
  // Closing segment is P3=(0,10) → P0=(0,0).
647
647
  // A test point on the left edge (0, 5) must resolve to (0, 5) on the closing segment.
648
648
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
649
- const poly = new BasePolyline({ points });
649
+ const poly = new PolylineBase({ points });
650
650
  poly.flags.setFlagValue(1);
651
651
 
652
652
  const [closest, dist] = poly.closestPoint(new Point(0, 5));
@@ -655,11 +655,11 @@ test('BasePolyline.closestPoint finds closest point on closing segment of closed
655
655
  expect(dist).toBeCloseTo(0);
656
656
  });
657
657
 
658
- test('BasePolyline.closestPoint closing segment is preferred over other segments', () => {
658
+ test('PolylineBase.closestPoint closing segment is preferred over other segments', () => {
659
659
  // The test point (-1, 5) is clearly nearest the closing segment (left edge),
660
660
  // not the bottom, right, or top edges.
661
661
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
662
- const poly = new BasePolyline({ points });
662
+ const poly = new PolylineBase({ points });
663
663
  poly.flags.setFlagValue(1);
664
664
 
665
665
  const [closest, dist] = poly.closestPoint(new Point(-1, 5));
@@ -668,14 +668,14 @@ test('BasePolyline.closestPoint closing segment is preferred over other segments
668
668
  expect(dist).toBeCloseTo(1);
669
669
  });
670
670
 
671
- test('BasePolyline.closestPoint for open polyline does not include closing segment', () => {
671
+ test('PolylineBase.closestPoint for open polyline does not include closing segment', () => {
672
672
  // For point (-1, 1) on the open square:
673
673
  // – Nearest on seg P0→P1 (y=0): foot (-1,0) clips to (0,0), dist = √2 ≈ 1.41
674
674
  // – Nearest on seg P2→P3 (y=10): foot (-1,10) clips to (0,10), dist ≈ 9.06
675
675
  // – If the closing segment existed (x=0, y∈[0,10]): foot (0,1), dist = 1 ← closer!
676
676
  // An open polyline must NOT check the closing segment, so it returns (0,0).
677
677
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
678
- const poly = new BasePolyline({ points });
678
+ const poly = new PolylineBase({ points });
679
679
  // flag NOT set — open polyline
680
680
 
681
681
  const [closest] = poly.closestPoint(new Point(-1, 1));
@@ -683,11 +683,80 @@ test('BasePolyline.closestPoint for open polyline does not include closing segme
683
683
  expect(closest.y).toBeCloseTo(0);
684
684
  });
685
685
 
686
- test('BasePolyline.closestPoint is consistent with getClosestSegmentIndex for closed polyline', () => {
686
+ // ─── isPointOnSegment ────────────────────────────────────────────────────────
687
+
688
+ const lineSegmentCases = [
689
+ { desc: 'midpoint', point: new Point(5, 0), expected: true },
690
+ { desc: 'start endpoint', point: new Point(0, 0), expected: true },
691
+ { desc: 'end endpoint', point: new Point(10, 0), expected: true },
692
+ { desc: 'perpendicular off-line', point: new Point(5, 5), expected: false },
693
+ { desc: 'collinear beyond end', point: new Point(15, 0), expected: false },
694
+ ];
695
+
696
+ test.each(lineSegmentCases)('PolylineBase.isPointOnSegment line segment – $desc', ({ point, expected }) => {
697
+ const A = new Point(0, 0);
698
+ const B = new Point(10, 0);
699
+ const poly = new PolylineBase({ points: [A, B] });
700
+ expect(poly.isPointOnSegment(point, A, B)).toBe(expected);
701
+ });
702
+
703
+ // CCW 180° semicircle from (10,0) to (-10,0), bulge=1, centre=(0,0), radius=10.
704
+ // Arc spans angles [0°, 180°] — the upper semicircle.
705
+ const ccwArcCases = [
706
+ { desc: 'arc midpoint (0,10) at 90°', point: new Point(0, 10), expected: true },
707
+ { desc: 'start endpoint (10,0)', point: new Point(10, 0), expected: true },
708
+ { desc: 'end endpoint (-10,0)', point: new Point(-10, 0), expected: true },
709
+ { desc: 'lower semicircle (0,-10)', point: new Point(0, -10), expected: false },
710
+ ];
711
+
712
+ test.each(ccwArcCases)('PolylineBase.isPointOnSegment CCW arc (bulge=1, 180°) – $desc', ({ point, expected }) => {
713
+ const A = new Point(10, 0);
714
+ A.bulge = 1;
715
+ const B = new Point(-10, 0);
716
+ const poly = new PolylineBase({ points: [A, B] });
717
+ expect(poly.isPointOnSegment(point, A, B)).toBe(expected);
718
+ });
719
+
720
+ // CW 180° semicircle from (10,0) to (-10,0), bulge=-1, centre=(0,0), radius=10.
721
+ // Arc spans angles ≤0° or ≥180° — the lower semicircle.
722
+ const cwArcCases = [
723
+ { desc: 'arc midpoint (0,-10) at -90°', point: new Point(0, -10), expected: true },
724
+ { desc: 'start endpoint (10,0)', point: new Point(10, 0), expected: true },
725
+ { desc: 'end endpoint (-10,0)', point: new Point(-10, 0), expected: true },
726
+ { desc: 'upper semicircle (0,10)', point: new Point(0, 10), expected: false },
727
+ ];
728
+
729
+ test.each(cwArcCases)('PolylineBase.isPointOnSegment CW arc (bulge=-1, 180°) – $desc', ({ point, expected }) => {
730
+ const A = new Point(10, 0);
731
+ A.bulge = -1;
732
+ const B = new Point(-10, 0);
733
+ const poly = new PolylineBase({ points: [A, B] });
734
+ expect(poly.isPointOnSegment(point, A, B)).toBe(expected);
735
+ });
736
+
737
+ // CCW 180° arc from (0,10) to (0,-10), bulge=1, centre=(0,0), radius=10.
738
+ // startAngle=90° > endAngle=-90° so the check wraps: angles ≥90° OR ≤-90°.
739
+ // This covers the left semicircle (through (-10,0)).
740
+ const ccwArcWrapCases = [
741
+ { desc: 'left midpoint (-10,0) at 180°', point: new Point(-10, 0), expected: true },
742
+ { desc: 'start endpoint (0,10)', point: new Point(0, 10), expected: true },
743
+ { desc: 'end endpoint (0,-10)', point: new Point(0, -10), expected: true },
744
+ { desc: 'right side (10,0) at 0°', point: new Point(10, 0), expected: false },
745
+ ];
746
+
747
+ test.each(ccwArcWrapCases)('PolylineBase.isPointOnSegment CCW arc wrapping past ±180° (bulge=1, startAngle=90° > endAngle=-90°) – $desc', ({ point, expected }) => {
748
+ const A = new Point(0, 10);
749
+ A.bulge = 1;
750
+ const B = new Point(0, -10);
751
+ const poly = new PolylineBase({ points: [A, B] });
752
+ expect(poly.isPointOnSegment(point, A, B)).toBe(expected);
753
+ });
754
+
755
+ test('PolylineBase.closestPoint is consistent with getClosestSegmentIndex for closed polyline', () => {
687
756
  // For a closed square, both methods should agree that the closing segment
688
757
  // is nearest for a point on the left edge.
689
758
  const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
690
- const poly = new BasePolyline({ points });
759
+ const poly = new PolylineBase({ points });
691
760
  poly.flags.setFlagValue(1);
692
761
 
693
762
  const testPoint = new Point(-2, 5);
@@ -702,8 +771,8 @@ test('BasePolyline.closestPoint is consistent with getClosestSegmentIndex for cl
702
771
 
703
772
  // ─── preview ─────────────────────────────────────────────────────────────────
704
773
 
705
- test('BasePolyline.preview - 0 points does nothing', () => {
706
- const poly = new BasePolyline({});
774
+ test('PolylineBase.preview - 0 points does nothing', () => {
775
+ const poly = new PolylineBase({});
707
776
  DesignCore.Scene.auxiliaryEntities.clear();
708
777
  DesignCore.Scene.previewEntities.clear();
709
778
 
@@ -712,8 +781,8 @@ test('BasePolyline.preview - 0 points does nothing', () => {
712
781
  expect(DesignCore.Scene.previewEntities.count()).toBe(0);
713
782
  });
714
783
 
715
- test('BasePolyline.preview - LINE mode creates temp entity', () => {
716
- const poly = new BasePolyline({});
784
+ test('PolylineBase.preview - LINE mode creates temp entity', () => {
785
+ const poly = new PolylineBase({});
717
786
  poly.points.push(new Point(0, 0));
718
787
 
719
788
  const origCreate = DesignCore.Scene.previewEntities.create;
@@ -730,8 +799,8 @@ test('BasePolyline.preview - LINE mode creates temp entity', () => {
730
799
  DesignCore.Scene.previewEntities.create = origCreate;
731
800
  });
732
801
 
733
- test('BasePolyline.preview - ARC mode adds rubber band and creates arc temp entity', () => {
734
- const poly = new BasePolyline({});
802
+ test('PolylineBase.preview - ARC mode adds rubber band and creates arc temp entity', () => {
803
+ const poly = new PolylineBase({});
735
804
  poly.points.push(new Point(0, 0));
736
805
  poly.points.push(new Point(10, 0));
737
806
  poly.inputMode = poly.modes.ARC;
@@ -114,9 +114,9 @@ test('Test Text.getTextFrameCorners', () => {
114
114
  expect(corners[3].y).toBeCloseTo(100);
115
115
 
116
116
  // Text frame calculation with text alignment
117
- // bounding rect is 10x10, so with center/middle alignment the text frame should be offset by 5 in both x and y from the insertion point
117
+ // bounding rect is 10x10, so with centre/middle alignment the text frame should be offset by 5 in both x and y from the insertion point
118
118
  const alignedText = new Text({ points: [new Point(100, 100)] });
119
- alignedText.setProperty('horizontalAlignment', 1); // center
119
+ alignedText.setProperty('horizontalAlignment', 1); // centre
120
120
  corners = alignedText.getTextFrameCorners();
121
121
 
122
122
  expect(corners[0].x).toBeCloseTo(95);
@@ -149,7 +149,7 @@ test('Test Text.getTextFrameCorners', () => {
149
149
  expect(corners[3].y).toBeCloseTo(105);
150
150
 
151
151
 
152
- // add rotation of 45 degrees to the center/middle aligned text and ensure the text frame corners are correctly calculated with both alignment and rotation applied
152
+ // add rotation of 45 degrees to the centre/middle aligned text and ensure the text frame corners are correctly calculated with both alignment and rotation applied
153
153
  alignedText.setRotation(45);
154
154
  corners = alignedText.getTextFrameCorners();
155
155
 
@@ -408,7 +408,7 @@ AcDbText
408
408
  // and a second alignment point with groupcode 11,21,31 for the text insertion point
409
409
  // bounding rect is 10x10, so second point should be at 100-(10*0.5), 200-(10*0.5) = 95, 195
410
410
 
411
- const centeredText = new Text({
411
+ const centredText = new Text({
412
412
  handle: text.getProperty('handle'),
413
413
  points: text.points,
414
414
  string: text.getProperty('string'),
@@ -417,12 +417,12 @@ AcDbText
417
417
  horizontalAlignment: text.getProperty('horizontalAlignment'),
418
418
  verticalAlignment: text.getProperty('verticalAlignment'),
419
419
  });
420
- centeredText.setProperty('horizontalAlignment', 1); // center
421
- centeredText.setProperty('verticalAlignment', 2); // middle
420
+ centredText.setProperty('horizontalAlignment', 1); // centre
421
+ centredText.setProperty('verticalAlignment', 2); // middle
422
422
  file = new File();
423
- centeredText.dxf(file);
423
+ centredText.dxf(file);
424
424
 
425
- const dxfCenteredString = `0
425
+ const dxfCentredString = `0
426
426
  TEXT
427
427
  5
428
428
  1
@@ -459,21 +459,21 @@ AcDbText
459
459
  73
460
460
  2
461
461
  `;
462
- expect(file.contents).toEqual(dxfCenteredString);
462
+ expect(file.contents).toEqual(dxfCentredString);
463
463
 
464
464
  // create new entity from entity data to ensure all props are loaded
465
465
  const importCenteredText = new Text({
466
- handle: centeredText.getProperty('handle'),
467
- points: centeredText.points,
468
- string: centeredText.getProperty('string'),
469
- height: centeredText.getProperty('height'),
470
- styleName: centeredText.getProperty('styleName'),
471
- horizontalAlignment: centeredText.getProperty('horizontalAlignment'),
472
- verticalAlignment: centeredText.getProperty('verticalAlignment'),
466
+ handle: centredText.getProperty('handle'),
467
+ points: centredText.points,
468
+ string: centredText.getProperty('string'),
469
+ height: centredText.getProperty('height'),
470
+ styleName: centredText.getProperty('styleName'),
471
+ horizontalAlignment: centredText.getProperty('horizontalAlignment'),
472
+ verticalAlignment: centredText.getProperty('verticalAlignment'),
473
473
  });
474
474
  file = new File();
475
475
  importCenteredText.dxf(file);
476
- expect(file.contents).toEqual(dxfCenteredString);
476
+ expect(file.contents).toEqual(dxfCentredString);
477
477
  });
478
478
 
479
479
  test('Text constructor covers all property branches', () => {
@@ -522,13 +522,13 @@ test('Text getHorizontalAlignment covers all cases', () => {
522
522
  t.setProperty('horizontalAlignment', 0);
523
523
  expect(t.getHorizontalAlignment()).toBe('left');
524
524
  t.setProperty('horizontalAlignment', 1);
525
- expect(t.getHorizontalAlignment()).toBe('center');
525
+ expect(t.getHorizontalAlignment()).toBe('centre');
526
526
  t.setProperty('horizontalAlignment', 2);
527
527
  expect(t.getHorizontalAlignment()).toBe('right');
528
528
  t.setProperty('horizontalAlignment', 3); t.setProperty('verticalAlignment', 0);
529
529
  expect(t.getHorizontalAlignment()).toBe('aligned');
530
530
  t.setProperty('horizontalAlignment', 4); t.setProperty('verticalAlignment', 0);
531
- expect(t.getHorizontalAlignment()).toBe('center');
531
+ expect(t.getHorizontalAlignment()).toBe('centre');
532
532
  t.setProperty('horizontalAlignment', 5); t.setProperty('verticalAlignment', 0);
533
533
  expect(t.getHorizontalAlignment()).toBe('fit');
534
534
  t.setProperty('horizontalAlignment', 99);
@@ -11,7 +11,7 @@ import { Lwpolyline } from '../../core/entities/lwpolyline.js';
11
11
  import { Hatch } from '../../core/entities/hatch.js';
12
12
  import { Insert } from '../../core/entities/insert.js';
13
13
  // import { ArcAlignedText } from '../../core/entities/arctext.js';
14
- import { BasePolyline } from '../../core/entities/basePolyline.js';
14
+ import { PolylineBase } from '../../core/entities/polylineBase.js';
15
15
 
16
16
  import { readFileSync } from 'fs';
17
17
  import { fileURLToPath } from 'url';
@@ -106,7 +106,7 @@ beforeAll(() => {
106
106
  // core.scene.entities.add(new Text({ points: [new Point(15, 205)], string: 'Solid', height: 4 }));
107
107
 
108
108
  // Row 2, Col 0: Hatch
109
- const boundary = new BasePolyline({
109
+ const boundary = new PolylineBase({
110
110
  points: [new Point(25, 230), new Point(75, 230), new Point(75, 280), new Point(25, 280)],
111
111
  });
112
112
  const hatch = new Hatch({
@@ -122,7 +122,7 @@ beforeAll(() => {
122
122
  blockLine.setProperty('handle', DesignCore.HandleManager.next());
123
123
  const block = DesignCore.Scene.blockManager.addItem({
124
124
  name: 'TEST_BLOCK',
125
- items: [blockLine],
125
+ entities: [blockLine],
126
126
  });
127
127
  const insert = new Insert({
128
128
  points: [new Point(130, 235)],
@@ -106,13 +106,13 @@ beforeAll(async () => {
106
106
  new Point(90, 50),
107
107
  ]);
108
108
 
109
- // col 1, row 0 — Circle, center (250,50) r=30 → idx 1
109
+ // col 1, row 0 — Circle, centre (250,50) r=30 → idx 1
110
110
  await executeWithInputs(new Circle(), [
111
111
  new Point(250, 50),
112
112
  30,
113
113
  ]);
114
114
 
115
- // col 2, row 0 — Arc, center (450,50), start (480,50), 270° CCW → idx 2
115
+ // col 2, row 0 — Arc, centre (450,50), start (480,50), 270° CCW → idx 2
116
116
  await executeWithInputs(new Arc(), [
117
117
  new Point(450, 50),
118
118
  new Point(480, 50),
@@ -149,10 +149,10 @@ beforeAll(async () => {
149
149
  // col 0, row 1 — Hatch using the rectangle boundary (idx 6) → idx 7
150
150
  {
151
151
  const hatchBoundary = DesignCore.Scene.entities.get(6);
152
- const origSelectedItems = DesignCore.Scene.selectionManager.selectedItems;
153
- DesignCore.Scene.selectionManager.selectedItems = [hatchBoundary];
152
+ const origSelectedEntities = DesignCore.Scene.selectionManager.selectedEntities;
153
+ DesignCore.Scene.selectionManager.selectedEntities = [hatchBoundary];
154
154
  await executeWithInputs(new Hatch(), ['accept']);
155
- DesignCore.Scene.selectionManager.selectedItems = origSelectedItems;
155
+ DesignCore.Scene.selectionManager.selectedEntities = origSelectedEntities;
156
156
  }
157
157
 
158
158
  // col 1, row 1 — Arc for ArcAlignedText, in its own cell → idx 8
@@ -29,9 +29,9 @@ test('Test BoundingBox.yLength', () => {
29
29
  expect(bb.yLength).toBe(100);
30
30
  });
31
31
 
32
- test('Test BoundingBox.centerPoint', () => {
33
- expect(bb.centerPoint.x).toBe(151);
34
- expect(bb.centerPoint.y).toBe(152);
32
+ test('Test BoundingBox.centrePoint', () => {
33
+ expect(bb.centrePoint.x).toBe(151);
34
+ expect(bb.centrePoint.y).toBe(152);
35
35
  });
36
36
 
37
37
  test('Test BoundingBox.isInside', () => {