@design-core/core 0.0.7 → 50.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 (113) 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 +28 -28
  16. package/core/entities/insert.js +5 -5
  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 +6 -4
  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/property.js +1 -1
  39. package/core/properties/propertyManager.js +37 -37
  40. package/core/tables/block.js +48 -46
  41. package/core/tables/dimStyle.js +5 -5
  42. package/core/tables/ltypeManager.js +1 -1
  43. package/core/tables/vport.js +2 -2
  44. package/core/tools/chamfer.js +6 -6
  45. package/core/tools/chamferFilletBase.js +4 -4
  46. package/core/tools/copy.js +8 -8
  47. package/core/tools/copybase.js +3 -3
  48. package/core/tools/copyclip.js +3 -3
  49. package/core/tools/cornerEntity.js +2 -2
  50. package/core/tools/cutclip.js +3 -3
  51. package/core/tools/explode.js +6 -6
  52. package/core/tools/extend.js +13 -13
  53. package/core/tools/fillet.js +6 -6
  54. package/core/tools/matchProp.js +1 -1
  55. package/core/tools/mirror.js +11 -11
  56. package/core/tools/move.js +5 -5
  57. package/core/tools/offset.js +29 -29
  58. package/core/tools/purge.js +10 -10
  59. package/core/tools/rotate.js +16 -16
  60. package/core/tools/scale.js +6 -6
  61. package/core/tools/trim.js +15 -15
  62. package/cspell.json +321 -0
  63. package/package.json +1 -1
  64. package/test/dimensions/alignedDimension.test.js +3 -3
  65. package/test/dimensions/angularDimension.test.js +7 -7
  66. package/test/dimensions/diametricDimension.test.js +9 -9
  67. package/test/dimensions/dimension.test.js +12 -12
  68. package/test/dimensions/{baseDimension.test.js → dimensionBase.test.js} +12 -12
  69. package/test/dimensions/{baseLinearDimension.test.js → linearDimensionBase.test.js} +7 -7
  70. package/test/dimensions/radialDimension.test.js +9 -9
  71. package/test/dimensions/rotatedDimension.test.js +4 -4
  72. package/test/entities/arc.test.js +10 -10
  73. package/test/entities/arctext.test.js +6 -6
  74. package/test/entities/circle.test.js +18 -18
  75. package/test/entities/hatch.test.js +39 -39
  76. package/test/entities/insert.test.js +9 -9
  77. package/test/entities/point.test.js +3 -3
  78. package/test/entities/{basePolyline.test.js → polylineBase.test.js} +131 -62
  79. package/test/entities/text.test.js +19 -19
  80. package/test/integration/exportIntegration.test.js +3 -3
  81. package/test/integration/userInputIntegration.test.js +5 -5
  82. package/test/lib/boundingBox.test.js +3 -3
  83. package/test/lib/canvas.test.js +9 -9
  84. package/test/lib/inputManager.test.js +4 -4
  85. package/test/lib/scene.test.js +9 -9
  86. package/test/lib/selectionManager.test.js +3 -3
  87. package/test/lib/snapping.test.js +1 -1
  88. package/test/properties/propertyManager.test.js +22 -22
  89. package/test/renderers/pdfRenderer.test.js +2 -2
  90. package/test/tables/block.test.js +63 -14
  91. package/test/tables/layerManager.test.js +2 -2
  92. package/test/test-helpers/hatchGridRenderer.js +3 -3
  93. package/test/test-helpers/test-helpers.js +6 -6
  94. package/test/tools/chamfer.test.js +60 -60
  95. package/test/tools/chamferFilletBase.test.js +36 -36
  96. package/test/tools/copy.test.js +6 -6
  97. package/test/tools/copybase.test.js +2 -2
  98. package/test/tools/copyclip.test.js +2 -2
  99. package/test/tools/cornerEntity.test.js +8 -8
  100. package/test/tools/cutclip.test.js +2 -2
  101. package/test/tools/erase.test.js +6 -6
  102. package/test/tools/explode.test.js +2 -2
  103. package/test/tools/extend.test.js +84 -84
  104. package/test/tools/fillet.test.js +62 -62
  105. package/test/tools/matchProp.test.js +6 -6
  106. package/test/tools/mirror.test.js +26 -26
  107. package/test/tools/move.test.js +7 -7
  108. package/test/tools/offset.test.js +65 -65
  109. package/test/tools/purge.test.js +4 -4
  110. package/test/tools/rotate.test.js +10 -10
  111. package/test/tools/scale.test.js +28 -28
  112. package/test/tools/trim.test.js +109 -109
  113. package/test/tools/zoom.test.js +5 -5
@@ -1,4 +1,4 @@
1
- import { BaseDimension } from '../../core/dimensions/baseDimension.js';
1
+ import { DimensionBase } from '../../core/dimensions/dimensionBase.js';
2
2
  import { Point } from '../../core/entities/point.js';
3
3
  import { Core } from '../../core/core/core.js';
4
4
  import { Text } from '../../core/entities/text.js';
@@ -9,11 +9,11 @@ import { jest } from '@jest/globals';
9
9
  // initialise core
10
10
  new Core();
11
11
 
12
- describe('BaseDimension', () => {
12
+ describe('DimensionBase', () => {
13
13
  let baseDim;
14
14
 
15
15
  beforeEach(() => {
16
- baseDim = new BaseDimension({});
16
+ baseDim = new DimensionBase({});
17
17
  baseDim.points = [new Point(1, 2, 0), new Point(3, 4, 0)];
18
18
  });
19
19
 
@@ -164,7 +164,7 @@ describe('BaseDimension', () => {
164
164
  });
165
165
 
166
166
  test('constructor throws on invalid dimType', () => {
167
- expect(() => new BaseDimension({ 70: 999 })).toThrow(/Invalid Dimension Type/);
167
+ expect(() => new DimensionBase({ 70: 999 })).toThrow(/Invalid Dimension Type/);
168
168
  });
169
169
 
170
170
  test('getDimensionValue uses textOverride', () => {
@@ -241,11 +241,11 @@ describe('BaseDimension', () => {
241
241
 
242
242
  describe('draw()', () => {
243
243
  /**
244
- * Return a BaseDimension with a stubbed buildDimension() that produces one Line.
245
- * @return {BaseDimension}
244
+ * Return a DimensionBase with a stubbed buildDimension() that produces one Line.
245
+ * @return {DimensionBase}
246
246
  */
247
247
  function makeDim() {
248
- const dim = new BaseDimension({});
248
+ const dim = new DimensionBase({});
249
249
  dim.points = [new Point(0, 0), new Point(10, 0)];
250
250
  dim.buildDimension = jest.fn(() => {
251
251
  return [new Line({ points: [new Point(0, 0), new Point(10, 0)] })];
@@ -256,21 +256,21 @@ describe('BaseDimension', () => {
256
256
  test('draw() returns the block items array', () => {
257
257
  const dim = makeDim();
258
258
  const result = dim.draw({});
259
- expect(result).toBe(dim.block.items);
259
+ expect(result).toBe(dim.block.entities);
260
260
  });
261
261
 
262
262
  test('draw() calls refresh() (via buildDimension) when block is empty', () => {
263
263
  const dim = makeDim();
264
- expect(dim.block.items).toHaveLength(0);
264
+ expect(dim.block.entities).toHaveLength(0);
265
265
  dim.draw({});
266
266
  expect(dim.buildDimension).toHaveBeenCalledTimes(1);
267
- expect(dim.block.items.length).toBeGreaterThan(0);
267
+ expect(dim.block.entities.length).toBeGreaterThan(0);
268
268
  });
269
269
 
270
270
  test('draw() does not call refresh() when block already has items', () => {
271
271
  const dim = makeDim();
272
272
  // pre-populate the block
273
- dim.block.addItem(new Line({ points: [new Point(0, 0), new Point(1, 1)] }));
273
+ dim.block.addEntity(new Line({ points: [new Point(0, 0), new Point(1, 1)] }));
274
274
  dim.draw({});
275
275
  expect(dim.buildDimension).not.toHaveBeenCalled();
276
276
  });
@@ -292,7 +292,7 @@ describe('BaseDimension', () => {
292
292
  test('draw() block items have BYBLOCK colour after refresh', () => {
293
293
  const dim = makeDim();
294
294
  dim.draw({});
295
- for (const item of dim.block.items) {
295
+ for (const item of dim.block.entities) {
296
296
  expect(item.entityColour.aci).toBe(0); // BYBLOCK = ACI 0
297
297
  }
298
298
  });
@@ -1,5 +1,5 @@
1
1
  import { AlignedDimension } from '../../core/dimensions/alignedDimension.js';
2
- import { BaseLinearDimension } from '../../core/dimensions/baseLinearDimension.js';
2
+ import { LinearDimensionBase } from '../../core/dimensions/linearDimensionBase.js';
3
3
  import { Point } from '../../core/entities/point.js';
4
4
  import { DesignCore } from '../../core/designCore.js';
5
5
  import { Core } from '../../core/core/core.js';
@@ -9,16 +9,16 @@ new Core();
9
9
 
10
10
  // ─── constructor ─────────────────────────────────────────────────────────────
11
11
 
12
- test('BaseLinearDimension can be instantiated', () => {
13
- const dim = new BaseLinearDimension();
14
- expect(dim).toBeInstanceOf(BaseLinearDimension);
12
+ test('LinearDimensionBase can be instantiated', () => {
13
+ const dim = new LinearDimensionBase();
14
+ expect(dim).toBeInstanceOf(LinearDimensionBase);
15
15
  expect(dim.points).toBeDefined();
16
16
  expect(Array.isArray(dim.points)).toBe(true);
17
17
  });
18
18
 
19
19
  // ─── preview ─────────────────────────────────────────────────────────────────
20
20
 
21
- test('BaseLinearDimension.preview - 0 points does nothing', () => {
21
+ test('LinearDimensionBase.preview - 0 points does nothing', () => {
22
22
  const dim = new AlignedDimension();
23
23
  DesignCore.Scene.auxiliaryEntities.clear();
24
24
  DesignCore.Scene.previewEntities.clear();
@@ -28,7 +28,7 @@ test('BaseLinearDimension.preview - 0 points does nothing', () => {
28
28
  expect(DesignCore.Scene.previewEntities.count()).toBe(0);
29
29
  });
30
30
 
31
- test('BaseLinearDimension.preview - 1 point adds RubberBand to auxiliaryEntities', () => {
31
+ test('LinearDimensionBase.preview - 1 point adds RubberBand to auxiliaryEntities', () => {
32
32
  const dim = new AlignedDimension();
33
33
  dim.points = [new Point(0, 0)];
34
34
 
@@ -44,7 +44,7 @@ test('BaseLinearDimension.preview - 1 point adds RubberBand to auxiliaryEntities
44
44
  DesignCore.Scene.auxiliaryEntities.add = origAdd;
45
45
  });
46
46
 
47
- test('BaseLinearDimension.preview - >1 points creates temp entity', () => {
47
+ test('LinearDimensionBase.preview - >1 points creates temp entity', () => {
48
48
  const dim = new AlignedDimension();
49
49
  dim.points = [new Point(0, 0), new Point(10, 0)];
50
50
 
@@ -17,28 +17,28 @@ const scenarios = [
17
17
 
18
18
  { desc: 'Radial dimension from circle selection - Text outside circle',
19
19
  input: [new SingleSelection(0, new Point()), new Point(20, 0)],
20
- selectedItems: [new Circle({ points: [new Point(0, 0), new Point(10, 0)] })],
20
+ selectedEntities: [new Circle({ points: [new Point(0, 0), new Point(10, 0)] })],
21
21
  expectedDimType: 4,
22
22
  dimensionValue: 10,
23
23
  dimensionEntities: 6,
24
24
  },
25
25
  { desc: 'Radial dimension from circle selection - Text inside circle',
26
26
  input: [new SingleSelection(0, new Point()), new Point(0, 0)],
27
- selectedItems: [new Circle({ points: [new Point(0, 0), new Point(10, 0)] })],
27
+ selectedEntities: [new Circle({ points: [new Point(0, 0), new Point(10, 0)] })],
28
28
  expectedDimType: 4,
29
29
  dimensionValue: 10,
30
30
  dimensionEntities: 5,
31
31
  },
32
32
  { desc: 'Radial dimension from arc selection',
33
33
  input: [new SingleSelection(0, new Point()), new Point(20, 10)],
34
- selectedItems: [new Arc({ points: [new Point(0, 0), new Point(20, 0), new Point(20, 20)] })],
34
+ selectedEntities: [new Arc({ points: [new Point(0, 0), new Point(20, 0), new Point(20, 20)] })],
35
35
  expectedDimType: 4,
36
36
  dimensionValue: 20,
37
37
  dimensionEntities: 6,
38
38
  },
39
39
  { desc: 'Radial dimension from polyline selection',
40
40
  input: [new SingleSelection(0, new Point(30, 10)), new Point(30, 10)],
41
- selectedItems: [new Polyline({ points: [new Point(0, 0), new Point(20, 0, 1), new Point(20, 20)] })],
41
+ selectedEntities: [new Polyline({ points: [new Point(0, 0), new Point(20, 0, 1), new Point(20, 20)] })],
42
42
  expectedDimType: 4,
43
43
  dimensionValue: 10,
44
44
  dimensionEntities: 6,
@@ -46,7 +46,7 @@ const scenarios = [
46
46
  ];
47
47
 
48
48
  test.each(scenarios)('RadialDimension.execute handles $desc', async (scenario) => {
49
- const { input, selectedItems, expectedDimType, dimensionValue, dimensionEntities } = scenario;
49
+ const { input, selectedEntities, expectedDimType, dimensionValue, dimensionEntities } = scenario;
50
50
 
51
51
  await withMockInput(DesignCore.Scene, input, async () => {
52
52
  const dim = new RadialDimension();
@@ -66,7 +66,7 @@ test.each(scenarios)('RadialDimension.execute handles $desc', async (scenario) =
66
66
  expect(entity.getProperty('string')).toContain('R');
67
67
  }
68
68
  }
69
- }, { selectedItems });
69
+ }, { selectedEntities });
70
70
  });
71
71
 
72
72
  test('constructor sets default properties', () => {
@@ -77,14 +77,14 @@ test('constructor sets default properties', () => {
77
77
 
78
78
 
79
79
  test('getPointsFromSelection returns correct sequenced points', () => {
80
- const center = new Point(0, 0);
80
+ const centre = new Point(0, 0);
81
81
  const textPos = new Point(10, 0);
82
82
  // Mock item with getRadius
83
- const item = { points: [center], getRadius: () => 10 };
83
+ const item = { points: [centre], getRadius: () => 10 };
84
84
  const result = RadialDimension.getPointsFromSelection([item], textPos);
85
85
  expect(Array.isArray(result)).toBe(true);
86
86
  expect(result.length).toBe(3);
87
- expect(result[0].sequence).toBe(10); // center
87
+ expect(result[0].sequence).toBe(10); // centre
88
88
  expect(result[1].sequence).toBe(15); // radius point
89
89
  expect(result[2].sequence).toBe(11); // text position
90
90
  });
@@ -13,20 +13,20 @@ const scenarios = [
13
13
 
14
14
  { desc: 'Rotated dimension from point selection (angle 0)',
15
15
  input: [0, new Point(), new Point(10, 0), new Point(5, 5)],
16
- selectedItems: [],
16
+ selectedEntities: [],
17
17
  expectedDimType: 0,
18
18
  dimensionValue: 10,
19
19
  },
20
20
  { desc: 'Rotated dimension from point selection (angle 90)',
21
21
  input: [90, new Point(), new Point(0, 10), new Point(5, 5)],
22
- selectedItems: [],
22
+ selectedEntities: [],
23
23
  expectedDimType: 0,
24
24
  dimensionValue: 10,
25
25
  },
26
26
  ];
27
27
 
28
28
  test.each(scenarios)('Dimension.execute handles $desc', async (scenario) => {
29
- const { input, selectedItems, expectedDimType, dimensionValue } = scenario;
29
+ const { input, selectedEntities, expectedDimType, dimensionValue } = scenario;
30
30
 
31
31
  await withMockInput(DesignCore.Scene, input, async () => {
32
32
  const dim = new RotatedDimension();
@@ -43,7 +43,7 @@ test.each(scenarios)('Dimension.execute handles $desc', async (scenario) => {
43
43
  expect(Number(entity.getProperty('string'))).toBeCloseTo(dimensionValue);
44
44
  }
45
45
  }
46
- }, { selectedItems });
46
+ }, { selectedEntities });
47
47
  });
48
48
 
49
49
  test('constructor sets default properties', () => {
@@ -58,7 +58,7 @@ describe('Arc.execute input validation and notifications', () => {
58
58
  test('Notifies on invalid point input (duplicate)', async () => {
59
59
  const input1 = new Point(0, 0);
60
60
  const input2 = new Point(10, 0);
61
- const input3 = new Point(0, 0); // Same as center
61
+ const input3 = new Point(0, 0); // Same as centre
62
62
  await withMockInput(DesignCore.Scene, [input1, input2, input3, new Point(10, 10)], async () => {
63
63
  const arc = new Arc({});
64
64
  await arc.execute();
@@ -450,8 +450,8 @@ test('Arc.fromPolylinePoints precision drift is negligible', () => {
450
450
  const polyPts = arc.toPolylinePoints();
451
451
  const rebuilt = new Arc({});
452
452
  rebuilt.fromPolylinePoints(polyPts);
453
- const centerDrift = Math.hypot(rebuilt.points[0].x - arc.points[0].x, rebuilt.points[0].y - arc.points[0].y);
454
- expect(centerDrift).toBeLessThan(1e-10);
453
+ const centreDrift = Math.hypot(rebuilt.points[0].x - arc.points[0].x, rebuilt.points[0].y - arc.points[0].y);
454
+ expect(centreDrift).toBeLessThan(1e-10);
455
455
  expect(Math.abs(rebuilt.getProperty('radius') - arc.getProperty('radius'))).toBeLessThan(1e-10);
456
456
  });
457
457
 
@@ -489,10 +489,10 @@ test('Arc.snaps returns no tangent when fromPoint is inside the arc radius', ()
489
489
 
490
490
  describe('Arc.fromDxf', () => {
491
491
  test('projects start and end points from DXF angle codes', () => {
492
- const center = new Point(0, 0);
493
- const result = Arc.fromDxf({ points: [center], 40: 10, 50: 0, 51: 90 });
492
+ const centre = new Point(0, 0);
493
+ const result = Arc.fromDxf({ points: [centre], 40: 10, 50: 0, 51: 90 });
494
494
  expect(result.points.length).toBe(3);
495
- expect(result.points[0]).toBe(center);
495
+ expect(result.points[0]).toBe(centre);
496
496
  expect(result.points[1].x).toBeCloseTo(10); // project(0°, 10)
497
497
  expect(result.points[1].y).toBeCloseTo(0);
498
498
  expect(result.points[2].x).toBeCloseTo(0); // project(90°, 10)
@@ -513,15 +513,15 @@ describe('Arc.fromDxf', () => {
513
513
  expect(result[6]).toBe('ByLayer');
514
514
  });
515
515
 
516
- test('returns data unchanged when no center point', () => {
516
+ test('returns data unchanged when no centre point', () => {
517
517
  const data = { 40: 10, 50: 0, 51: 90 };
518
518
  expect(Arc.fromDxf(data)).toBe(data);
519
519
  });
520
520
 
521
521
  test('arc constructed via fromDxf matches arc constructed directly', () => {
522
- const center = new Point(100, 100);
523
- const fromDxfArc = new Arc(Arc.fromDxf({ points: [center], 40: 50, 50: 0, 51: 90 }));
524
- const directArc = new Arc({ points: [center, new Point(150, 100), new Point(100, 150)] });
522
+ const centre = new Point(100, 100);
523
+ const fromDxfArc = new Arc(Arc.fromDxf({ points: [centre], 40: 50, 50: 0, 51: 90 }));
524
+ const directArc = new Arc({ points: [centre, new Point(150, 100), new Point(100, 150)] });
525
525
  expect(fromDxfArc.getProperty('radius')).toBeCloseTo(directArc.getProperty('radius'));
526
526
  expect(fromDxfArc.startAngle()).toBeCloseTo(directArc.startAngle());
527
527
  expect(fromDxfArc.endAngle()).toBeCloseTo(directArc.endAngle());
@@ -16,14 +16,14 @@ const arcInputScenarios = [
16
16
  {
17
17
  desc: 'two points and an angle',
18
18
  input: [new SingleSelection(0, new Point(5, 0)), 2.5, 'Test Arc'],
19
- selectedItems: [new Arc({ points: [new Point(), new Point(100, 0), new Point(-100, 0)] })],
19
+ selectedEntities: [new Arc({ points: [new Point(), new Point(100, 0), new Point(-100, 0)] })],
20
20
  expectedTextHeight: 2.5,
21
21
  expectedText: 'Test Arc',
22
22
  },
23
23
  ];
24
24
 
25
25
  test.each(arcInputScenarios)('ArcText.execute handles $desc', async (scenario) => {
26
- const { input, selectedItems, expectedTextHeight, expectedText } = scenario;
26
+ const { input, selectedEntities, expectedTextHeight, expectedText } = scenario;
27
27
 
28
28
  await withMockInput(DesignCore.Scene, input, async () => {
29
29
  const arcText = new ArcAlignedText();
@@ -37,7 +37,7 @@ test.each(arcInputScenarios)('ArcText.execute handles $desc', async (scenario) =
37
37
  expect(arcText.getProperty('radius')).toBe(100);
38
38
  expect(arcText.startAngle()).toBe(0);
39
39
  expect(arcText.endAngle()).toBeCloseTo(3.14159);
40
- }, { selectedItems });
40
+ }, { selectedEntities });
41
41
  });
42
42
 
43
43
  test('ArcAlignedCharacter', () => {
@@ -105,7 +105,7 @@ test('ArcText getArcAlignedCharacters', () => {
105
105
  // offsetFromLeft 0 - mm
106
106
  // textReversed 0 = forward, 1 = reversed
107
107
  // textOrientation 1 = outward, 2 = inward
108
- // textAlignment 1 = fit to arc, 2 = left align, 3 = right align, 4 = center
108
+ // textAlignment 1 = fit to arc, 2 = left align, 3 = right align, 4 = centre
109
109
  // ArcSide convex = 1, concave = 2
110
110
  let chars = [];
111
111
 
@@ -387,7 +387,7 @@ test('ArcText snaps with empty string returns only centre snap', () => {
387
387
  });
388
388
 
389
389
  test('ArcText closestPoint returns distance 0.1 when within height * 0.35', () => {
390
- // Single char 'T' center-aligned sits at (0, 101.25); height = 2.5, threshold = 0.875
390
+ // Single char 'T' centre-aligned sits at (0, 101.25); height = 2.5, threshold = 0.875
391
391
  const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'T', radius: 100 });
392
392
  const chars = arcText.getArcAlignedCharacters();
393
393
  const charPos = chars[0].position;
@@ -408,7 +408,7 @@ test('ArcText getArcAlignedCharacters fit-to-arc single character places at midp
408
408
  });
409
409
 
410
410
  test('ArcText getArcAlignedCharacters single character is placed at arc midpoint', () => {
411
- // Single char with center alignment: charOffsetAngles = [0], stringStartPoint = arcMidPoint
411
+ // Single char with centre alignment: charOffsetAngles = [0], stringStartPoint = arcMidPoint
412
412
  // arcMidAngle(0, PI) = PI/2 → project(PI/2, 101.25) = (0, 101.25), angle = PI/2 - PI/2 = 0
413
413
  const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'X', radius: 100 });
414
414
  const chars = arcText.getArcAlignedCharacters();
@@ -128,22 +128,22 @@ test('Test Circle.toPolylinePoints', () => {
128
128
  });
129
129
 
130
130
  test('Circle.execute creates circle from diameter input', async () => {
131
- const center = new Point(0, 0);
131
+ const centre = new Point(0, 0);
132
132
 
133
- await withMockInput(DesignCore.Scene, [center, 'Diameter', 20], async () => {
133
+ await withMockInput(DesignCore.Scene, [centre, 'Diameter', 20], async () => {
134
134
  const circle = new Circle({});
135
135
  await circle.execute();
136
136
 
137
- expect(circle.points[0]).toBe(center);
137
+ expect(circle.points[0]).toBe(centre);
138
138
  expect(circle.getRadius()).toBeCloseTo(10);
139
139
  });
140
140
  });
141
141
 
142
142
  test('Circle.execute re-prompts on zero or negative radius', async () => {
143
- const center = new Point(0, 0);
143
+ const centre = new Point(0, 0);
144
144
 
145
145
  // 0 and -5 are rejected; 10 is accepted
146
- await withMockInput(DesignCore.Scene, [center, 0, -5, 10], async () => {
146
+ await withMockInput(DesignCore.Scene, [centre, 0, -5, 10], async () => {
147
147
  const circle = new Circle({});
148
148
  await circle.execute();
149
149
 
@@ -152,10 +152,10 @@ test('Circle.execute re-prompts on zero or negative radius', async () => {
152
152
  });
153
153
 
154
154
  test('Circle.execute re-prompts on zero or negative diameter', async () => {
155
- const center = new Point(0, 0);
155
+ const centre = new Point(0, 0);
156
156
 
157
157
  // 'Diameter', then 0 and -20 rejected, then 20 accepted (radius = 10)
158
- await withMockInput(DesignCore.Scene, [center, 'Diameter', 0, -20, 20], async () => {
158
+ await withMockInput(DesignCore.Scene, [centre, 'Diameter', 0, -20, 20], async () => {
159
159
  const circle = new Circle({});
160
160
  await circle.execute();
161
161
 
@@ -175,8 +175,8 @@ test('Circle constructor loads radius from DXF group code 40 via fromDxf', () =>
175
175
  });
176
176
 
177
177
  test('Circle.execute creates circle from numeric radius input', async () => {
178
- const center = new Point(0, 0);
179
- await withMockInput(DesignCore.Scene, [center, 25], async () => {
178
+ const centre = new Point(0, 0);
179
+ await withMockInput(DesignCore.Scene, [centre, 25], async () => {
180
180
  const circle = new Circle({});
181
181
  await circle.execute();
182
182
  expect(circle.getRadius()).toBeCloseTo(25);
@@ -185,8 +185,8 @@ test('Circle.execute creates circle from numeric radius input', async () => {
185
185
 
186
186
  test('Circle.execute re-prompts on zero-distance point input', async () => {
187
187
  const notifySpy = jest.spyOn(DesignCore.Core, 'notify').mockImplementation(() => {});
188
- const center = new Point(0, 0);
189
- await withMockInput(DesignCore.Scene, [center, new Point(0, 0), new Point(10, 0)], async () => {
188
+ const centre = new Point(0, 0);
189
+ await withMockInput(DesignCore.Scene, [centre, new Point(0, 0), new Point(10, 0)], async () => {
190
190
  const circle = new Circle({});
191
191
  await circle.execute();
192
192
  expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.NONZERO));
@@ -296,10 +296,10 @@ test('Circle.snaps returns two tangent snaps from outside', () => {
296
296
 
297
297
  describe('Circle.fromDxf', () => {
298
298
  test('projects edge point from DXF radius code', () => {
299
- const center = new Point(0, 0);
300
- const result = Circle.fromDxf({ points: [center], 40: 10 });
299
+ const centre = new Point(0, 0);
300
+ const result = Circle.fromDxf({ points: [centre], 40: 10 });
301
301
  expect(result.points.length).toBe(2);
302
- expect(result.points[0]).toBe(center);
302
+ expect(result.points[0]).toBe(centre);
303
303
  expect(result.points[1].x).toBeCloseTo(10);
304
304
  expect(result.points[1].y).toBeCloseTo(0);
305
305
  });
@@ -309,7 +309,7 @@ describe('Circle.fromDxf', () => {
309
309
  expect(result[8]).toBe('MyLayer');
310
310
  });
311
311
 
312
- test('returns data unchanged when no center point', () => {
312
+ test('returns data unchanged when no centre point', () => {
313
313
  const data = { 40: 10 };
314
314
  expect(Circle.fromDxf(data)).toBe(data);
315
315
  });
@@ -320,9 +320,9 @@ describe('Circle.fromDxf', () => {
320
320
  });
321
321
 
322
322
  test('circle constructed via fromDxf matches circle constructed directly', () => {
323
- const center = new Point(100, 100);
324
- const fromDxfCircle = new Circle(Circle.fromDxf({ points: [center], 40: 50 }));
325
- const directCircle = new Circle({ points: [center, new Point(150, 100)] });
323
+ const centre = new Point(100, 100);
324
+ const fromDxfCircle = new Circle(Circle.fromDxf({ points: [centre], 40: 50 }));
325
+ const directCircle = new Circle({ points: [centre, new Point(150, 100)] });
326
326
  expect(fromDxfCircle.getRadius()).toBeCloseTo(directCircle.getRadius());
327
327
  expect(fromDxfCircle.points[0].x).toBeCloseTo(directCircle.points[0].x);
328
328
  expect(fromDxfCircle.points[0].y).toBeCloseTo(directCircle.points[0].y);
@@ -2,7 +2,7 @@ import { Hatch } from '../../core/entities/hatch.js';
2
2
  import { Point } from '../../core/entities/point.js';
3
3
  import { DesignCore } from '../../core/designCore.js';
4
4
 
5
- import { BasePolyline } from '../../core/entities/basePolyline.js';
5
+ import { PolylineBase } from '../../core/entities/polylineBase.js';
6
6
  import { Circle } from '../../core/entities/circle.js';
7
7
  import { Line } from '../../core/entities/line.js';
8
8
  import { Polyline } from '../../core/entities/polyline.js';
@@ -17,7 +17,7 @@ import { Core } from '../../core/core/core.js';
17
17
  new Core();
18
18
 
19
19
  const points = [new Point(100, 100, 1), new Point(200, 100, 1)];
20
- const boundaryShape = new BasePolyline({ points: points });
20
+ const boundaryShape = new PolylineBase({ points: points });
21
21
 
22
22
  const hatch = new Hatch({ handle: '1' });
23
23
  hatch.setProperty('childEntities', [boundaryShape]);
@@ -56,10 +56,10 @@ test.each(hatchInputScenarios)('Hatch.execute handles $desc', async (scenario) =
56
56
 
57
57
  await withMockInput(DesignCore.Scene, [true], async () => {
58
58
  // Manual mock for selected items
59
- DesignCore.Scene.selectionManager.selectedItems = [];
59
+ DesignCore.Scene.selectionManager.selectedEntities = [];
60
60
 
61
61
  for (let i = 0; i < boundaryItems.length; i++) {
62
- DesignCore.Scene.selectionManager.selectedItems.push(boundaryItems[i]);
62
+ DesignCore.Scene.selectionManager.selectedEntities.push(boundaryItems[i]);
63
63
  }
64
64
 
65
65
  const hatch = new Hatch({ patternName: pattern, scale: scale, angle: angle });
@@ -448,60 +448,60 @@ test('Test Hatch.processBoundaryData', () => {
448
448
 
449
449
  test('Test Hatch.processSelection', () => {
450
450
  // create selected items defining a square
451
- let selectedItems = [];
452
- selectedItems.push(new Line({ points: [new Point(100, 100), new Point(200, 100)] }));
453
- selectedItems.push(new Line({ points: [new Point(200, 100), new Point(200, 200)] }));
454
- selectedItems.push(new Line({ points: [new Point(200, 200), new Point(100, 200)] }));
455
- selectedItems.push(new Line({ points: [new Point(100, 200), new Point(100, 100)] }));
451
+ let selectedEntities = [];
452
+ selectedEntities.push(new Line({ points: [new Point(100, 100), new Point(200, 100)] }));
453
+ selectedEntities.push(new Line({ points: [new Point(200, 100), new Point(200, 200)] }));
454
+ selectedEntities.push(new Line({ points: [new Point(200, 200), new Point(100, 200)] }));
455
+ selectedEntities.push(new Line({ points: [new Point(100, 200), new Point(100, 100)] }));
456
456
 
457
457
  let hatch = new Hatch();
458
- let boundaryData = hatch.processSelection(selectedItems);
458
+ let boundaryData = hatch.processSelection(selectedEntities);
459
459
  expect(boundaryData.length).toEqual(1);
460
460
  expect(boundaryData[0].points.length).toEqual(4);
461
461
 
462
462
 
463
463
  // create selected items defining a circle
464
- selectedItems = [];
465
- selectedItems.push(new Circle({ points: [new Point(100, 100), new Point(200, 100)] }));
464
+ selectedEntities = [];
465
+ selectedEntities.push(new Circle({ points: [new Point(100, 100), new Point(200, 100)] }));
466
466
 
467
467
  hatch = new Hatch();
468
- boundaryData = hatch.processSelection(selectedItems);
468
+ boundaryData = hatch.processSelection(selectedEntities);
469
469
  expect(boundaryData.length).toEqual(1);
470
470
  expect(boundaryData[0].points.length).toEqual(2);
471
471
 
472
472
  // create selected items defining a square (polyline)
473
- selectedItems = [];
474
- selectedItems.push(new Polyline({
473
+ selectedEntities = [];
474
+ selectedEntities.push(new Polyline({
475
475
  points: [new Point(100, 100), new Point(200, 100), new Point(200, 200),
476
476
  new Point(100, 200), new Point(100, 100)],
477
477
  }));
478
478
 
479
479
  hatch = new Hatch();
480
- boundaryData = hatch.processSelection(selectedItems);
480
+ boundaryData = hatch.processSelection(selectedEntities);
481
481
  expect(boundaryData.length).toEqual(1);
482
482
  expect(boundaryData[0].points.length).toEqual(4);
483
483
 
484
484
  // create selected items defining a pill shape
485
- selectedItems = [];
486
- selectedItems.push(new Line({ points: [new Point(100, 100), new Point(200, 100)] }));
487
- selectedItems.push(new Arc({ points: [new Point(200, 150), new Point(200, 100), new Point(200, 200)] }));
488
- selectedItems.push(new Line({ points: [new Point(200, 200), new Point(100, 200)] }));
489
- selectedItems.push(new Arc({ points: [new Point(100, 150), new Point(100, 200), new Point(100, 100)] }));
485
+ selectedEntities = [];
486
+ selectedEntities.push(new Line({ points: [new Point(100, 100), new Point(200, 100)] }));
487
+ selectedEntities.push(new Arc({ points: [new Point(200, 150), new Point(200, 100), new Point(200, 200)] }));
488
+ selectedEntities.push(new Line({ points: [new Point(200, 200), new Point(100, 200)] }));
489
+ selectedEntities.push(new Arc({ points: [new Point(100, 150), new Point(100, 200), new Point(100, 100)] }));
490
490
 
491
491
  hatch = new Hatch();
492
- boundaryData = hatch.processSelection(selectedItems);
492
+ boundaryData = hatch.processSelection(selectedEntities);
493
493
  expect(boundaryData.length).toEqual(1);
494
494
  // 2 lines + 2 arcs: each shared connection point is merged (not duplicated),
495
495
  // so the result is 4 unique vertices, not 5
496
496
  expect(boundaryData[0].points.length).toEqual(4);
497
497
 
498
498
  // Test a selection with invalid items
499
- selectedItems = [];
500
- selectedItems.push(new Text({ points: [new Point(100, 100), new Point(200, 100)] }));
501
- selectedItems.push(new Circle({ points: [new Point(100, 100), new Point(200, 100)] }));
499
+ selectedEntities = [];
500
+ selectedEntities.push(new Text({ points: [new Point(100, 100), new Point(200, 100)] }));
501
+ selectedEntities.push(new Circle({ points: [new Point(100, 100), new Point(200, 100)] }));
502
502
 
503
503
  hatch = new Hatch();
504
- boundaryData = hatch.processSelection(selectedItems);
504
+ boundaryData = hatch.processSelection(selectedEntities);
505
505
  expect(boundaryData.length).toEqual(1);
506
506
  expect(boundaryData[0].points.length).toEqual(2);
507
507
  });
@@ -539,7 +539,7 @@ function makeMockCtx() {
539
539
 
540
540
  test('Test Hatch.draw solid fill renders filled boundary', () => {
541
541
  const solidHatch = new Hatch({ patternName: 'SOLID' });
542
- solidHatch.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] })]);
542
+ solidHatch.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] })]);
543
543
 
544
544
  const ctx = makeMockCtx();
545
545
  solidHatch.draw(ctx);
@@ -549,7 +549,7 @@ test('Test Hatch.draw solid fill renders filled boundary', () => {
549
549
 
550
550
  test('Test Hatch.draw unknown pattern does not stroke pattern lines', () => {
551
551
  const unknownHatch = new Hatch({ patternName: 'ANSI31' });
552
- unknownHatch.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] })]);
552
+ unknownHatch.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] })]);
553
553
  // Override pattern to a nonexistent name
554
554
  unknownHatch.setProperty('patternName', 'NOTAPATTERN');
555
555
 
@@ -567,7 +567,7 @@ test('Test Hatch.draw tight bound line count - square boundary', () => {
567
567
  // yIncrement = ceil(70.71 / 3.175) = 23 → loop i=-23..22 = 46 raw lines
568
568
  // i=-23 (|ly|=73.025 > halfY=70.71) misses the square → 45 clipped segments
569
569
  const squareHatch = new Hatch({ patternName: 'ANSI31' });
570
- squareHatch.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
570
+ squareHatch.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
571
571
  const ctx = makeMockCtx();
572
572
  squareHatch.draw(ctx);
573
573
  expect(ctx.getMoveCalls()).toBe(45);
@@ -580,7 +580,7 @@ test('Test Hatch.draw tight bound line count - flat boundary', () => {
580
580
  // yIncrement = ceil(38.89 / 3.175) = 13 → loop i=-13..12 = 26 raw lines
581
581
  // i=-13 (|ly|=41.275 > halfY=38.89) misses the flat box → 25 clipped segments
582
582
  const flatHatch = new Hatch({ patternName: 'ANSI31' });
583
- flatHatch.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 10), new Point(0, 10)] })]);
583
+ flatHatch.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 10), new Point(0, 10)] })]);
584
584
  const ctx = makeMockCtx();
585
585
  flatHatch.draw(ctx);
586
586
  expect(ctx.getMoveCalls()).toBe(25);
@@ -589,10 +589,10 @@ test('Test Hatch.draw tight bound line count - flat boundary', () => {
589
589
  test('Test Hatch.draw tight bound produces fewer lines for flat boundary', () => {
590
590
  // A flat boundary (100x10) should produce fewer lines than a square (100x100).
591
591
  const squareHatch = new Hatch({ patternName: 'ANSI31' });
592
- squareHatch.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
592
+ squareHatch.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
593
593
 
594
594
  const flatHatch = new Hatch({ patternName: 'ANSI31' });
595
- flatHatch.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 10), new Point(0, 10)] })]);
595
+ flatHatch.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 10), new Point(0, 10)] })]);
596
596
 
597
597
  const squareCtx = makeMockCtx();
598
598
  squareHatch.draw(squareCtx);
@@ -614,14 +614,14 @@ test('Hatch.buildPatternCache no childEntities sets cachedPattern to empty array
614
614
 
615
615
  test('Hatch.buildPatternCache solid fill sets cachedPattern to empty array', () => {
616
616
  const h = new Hatch({ patternName: 'SOLID' });
617
- h.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
617
+ h.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
618
618
  h.buildPatternCache();
619
619
  expect(h.cachedPattern).toEqual([]);
620
620
  });
621
621
 
622
622
  test('Hatch.buildPatternCache unknown pattern sets cachedPattern to empty array', () => {
623
623
  const h = new Hatch({ patternName: 'HONEY' });
624
- h.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
624
+ h.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })]);
625
625
  h.setProperty('patternName', 'NOTAPATTERN');
626
626
  h.buildPatternCache();
627
627
  expect(h.cachedPattern).toEqual([]);
@@ -631,9 +631,9 @@ test('Hatch.buildPatternCache unknown pattern sets cachedPattern to empty array'
631
631
 
632
632
  /**
633
633
  * Build a 100×100 square boundary
634
- * @return {Array} array containing one BasePolyline boundary
634
+ * @return {Array} array containing one PolylineBase boundary
635
635
  */
636
- const makeSquare = () => [new BasePolyline({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })];
636
+ const makeSquare = () => [new PolylineBase({ points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)] })];
637
637
 
638
638
  /**
639
639
  * Sum all segment counts across all families in a cached hatch
@@ -731,9 +731,9 @@ test('Hatch.buildPatternCache scale change invalidates and rebuilds cache with u
731
731
 
732
732
  /**
733
733
  * A 20×20 inner square hole centred in the 100×100 outer square
734
- * @return {BasePolyline} inner boundary polyline
734
+ * @return {PolylineBase} inner boundary polyline
735
735
  */
736
- const makeInnerSquare = () => new BasePolyline({ points: [new Point(40, 40), new Point(60, 40), new Point(60, 60), new Point(40, 60)] });
736
+ const makeInnerSquare = () => new PolylineBase({ points: [new Point(40, 40), new Point(60, 40), new Point(60, 60), new Point(40, 60)] });
737
737
 
738
738
  test('Hatch.buildPatternCache ANSI31 nested boundary: no segment midpoint inside inner hole', () => {
739
739
  // Even-odd rule: a ray from inside the hole crosses 2 boundaries → even → not hatched
@@ -951,7 +951,7 @@ test('Hatch.setProperty patternName updates pattern and solid flag via setter',
951
951
  test('Hatch.setProperty points translation shifts child entity positions', () => {
952
952
  const h = new Hatch({ patternName: 'ANSI31' });
953
953
  // h.points = [Point(0,0), Point(1,1)] — 45° angle set by constructor
954
- h.setProperty('childEntities', [new BasePolyline({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] })]);
954
+ h.setProperty('childEntities', [new PolylineBase({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] })]);
955
955
 
956
956
  // New points: same 45° angle (delta angle = 0 → no rotation), origin shifted by (5, 0)
957
957
  h.setProperty('points', [new Point(5, 0), new Point(6, 1)]);