@design-core/core 0.0.6 → 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 (173) hide show
  1. package/.eslintrc.json +4 -0
  2. package/.github/workflows/DesignCoreQA.yml +3 -0
  3. package/TODO.md +49 -6
  4. package/core/core/core.js +0 -3
  5. package/core/designCore.js +1 -1
  6. package/core/dimensions/alignedDimension.js +12 -11
  7. package/core/dimensions/angularDimension.js +38 -37
  8. package/core/dimensions/{baseCircularDimension.js → circularDimensionBase.js} +22 -21
  9. package/core/dimensions/diametricDimension.js +14 -13
  10. package/core/dimensions/dimension.js +62 -55
  11. package/core/dimensions/{baseDimension.js → dimensionBase.js} +73 -107
  12. package/core/dimensions/{baseLinearDimension.js → linearDimensionBase.js} +15 -14
  13. package/core/dimensions/radialDimension.js +12 -11
  14. package/core/dimensions/rotatedDimension.js +20 -19
  15. package/core/entities/arc.js +123 -156
  16. package/core/entities/arctext.js +222 -184
  17. package/core/entities/circle.js +77 -99
  18. package/core/entities/entity.js +114 -93
  19. package/core/entities/hatch.js +346 -273
  20. package/core/entities/insert.js +68 -83
  21. package/core/entities/line.js +21 -142
  22. package/core/entities/lwpolyline.js +16 -11
  23. package/core/entities/point.js +19 -19
  24. package/core/entities/polyline.js +7 -7
  25. package/core/entities/{basePolyline.js → polylineBase.js} +47 -299
  26. package/core/entities/rectangle.js +3 -1
  27. package/core/entities/solid.js +6 -15
  28. package/core/entities/text.js +190 -201
  29. package/core/lib/auxiliary/rubberBand.js +32 -0
  30. package/core/lib/auxiliary/selectionWindow.js +63 -0
  31. package/core/lib/auxiliary/snapPoint.js +113 -0
  32. package/core/lib/auxiliary/trackingLine.js +76 -0
  33. package/core/lib/boundingBox.js +11 -11
  34. package/core/lib/canvas.js +232 -168
  35. package/core/lib/clipboard.js +9 -3
  36. package/core/lib/colour.js +16 -0
  37. package/core/lib/commandLine.js +1 -6
  38. package/core/lib/commandManager.js +18 -8
  39. package/core/lib/constants.js +28 -0
  40. package/core/lib/dxf/dxf.js +15 -14
  41. package/core/lib/dxf/dxfIterator.js +2 -2
  42. package/core/lib/entityManager.js +33 -26
  43. package/core/lib/inputManager.js +36 -12
  44. package/core/lib/intersect.js +25 -0
  45. package/core/lib/matrix.js +1 -1
  46. package/core/lib/patterns.js +9 -2
  47. package/core/lib/plotOptions.js +67 -0
  48. package/core/lib/polylineUtils.js +75 -0
  49. package/core/lib/renderers/cairoRenderer.js +371 -0
  50. package/core/lib/renderers/canvasRenderer.js +315 -0
  51. package/core/lib/renderers/pdfRenderer.js +366 -0
  52. package/core/lib/renderers/rendererBase.js +284 -0
  53. package/core/lib/renderers/svgRenderer.js +343 -0
  54. package/core/lib/scene.js +28 -6
  55. package/core/lib/selectionManager.js +20 -20
  56. package/core/lib/settings.js +4 -2
  57. package/core/lib/snapping.js +4 -191
  58. package/core/lib/stateManager.js +30 -1
  59. package/core/lib/utils.js +26 -7
  60. package/core/properties/entityProperties.js +104 -0
  61. package/core/properties/property.js +106 -2
  62. package/core/properties/propertyManager.js +60 -44
  63. package/core/tables/block.js +50 -81
  64. package/core/tables/dimStyle.js +5 -5
  65. package/core/tables/endblk.js +1 -1
  66. package/core/tables/layer.js +8 -0
  67. package/core/tables/ltypeManager.js +1 -1
  68. package/core/tables/styleManagerBase.js +3 -2
  69. package/core/tables/tableManagerBase.js +8 -3
  70. package/core/tables/vport.js +2 -2
  71. package/core/tools/chamfer.js +117 -143
  72. package/core/tools/chamferFilletBase.js +138 -37
  73. package/core/tools/copy.js +11 -11
  74. package/core/tools/copybase.js +3 -3
  75. package/core/tools/copyclip.js +3 -3
  76. package/core/tools/cornerEntity.js +2 -2
  77. package/core/tools/cutclip.js +3 -3
  78. package/core/tools/explode.js +7 -7
  79. package/core/tools/extend.js +138 -25
  80. package/core/tools/fillet.js +92 -189
  81. package/core/tools/matchProp.js +3 -12
  82. package/core/tools/mirror.js +15 -14
  83. package/core/tools/move.js +8 -8
  84. package/core/tools/offset.js +31 -33
  85. package/core/tools/pasteclip.js +1 -1
  86. package/core/tools/purge.js +10 -10
  87. package/core/tools/rotate.js +19 -16
  88. package/core/tools/scale.js +7 -7
  89. package/core/tools/trim.js +271 -29
  90. package/core/tools/zoom.js +1 -1
  91. package/cspell.json +321 -0
  92. package/jest.config.js +6 -0
  93. package/package.json +5 -3
  94. package/test/dimensions/alignedDimension.test.js +17 -10
  95. package/test/dimensions/angularDimension.test.js +13 -13
  96. package/test/dimensions/diametricDimension.test.js +15 -15
  97. package/test/dimensions/dimension.test.js +12 -12
  98. package/test/dimensions/{baseDimension.test.js → dimensionBase.test.js} +73 -12
  99. package/test/dimensions/linearDimensionBase.test.js +62 -0
  100. package/test/dimensions/radialDimension.test.js +15 -15
  101. package/test/dimensions/rotatedDimension.test.js +18 -11
  102. package/test/entities/arc.test.js +83 -86
  103. package/test/entities/arctext.test.js +184 -57
  104. package/test/entities/circle.test.js +68 -96
  105. package/test/entities/entity.test.js +42 -8
  106. package/test/entities/hatch.test.js +569 -96
  107. package/test/entities/insert.test.js +99 -4
  108. package/test/entities/line.test.js +15 -41
  109. package/test/entities/lwpolyline.test.js +104 -0
  110. package/test/entities/point.test.js +3 -3
  111. package/test/entities/polyline.test.js +11 -0
  112. package/test/entities/{basePolyline.test.js → polylineBase.test.js} +192 -57
  113. package/test/entities/rectangle.test.js +11 -0
  114. package/test/entities/solid.test.js +1 -1
  115. package/test/entities/text.test.js +302 -77
  116. package/test/integration/exportIntegration.reference.dxf +9 -9
  117. package/test/integration/exportIntegration.test.js +5 -5
  118. package/test/integration/intersectionIntegration.test.js +4 -5
  119. package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
  120. package/test/integration/pdfRendererIntegration.test.js +61 -0
  121. package/test/integration/svgRendererIntegration.reference.svg +3343 -0
  122. package/test/integration/svgRendererIntegration.test.js +61 -0
  123. package/test/integration/userInputIntegration.reference.dxf +1165 -105
  124. package/test/integration/userInputIntegration.test.js +239 -132
  125. package/test/lib/boundingBox.test.js +3 -3
  126. package/test/lib/canvas.test.js +744 -125
  127. package/test/lib/constants.test.js +27 -0
  128. package/test/lib/entityManager.test.js +4 -4
  129. package/test/lib/inputManager.test.js +81 -20
  130. package/test/lib/intersect.test.js +83 -0
  131. package/test/lib/plotOptions.test.js +179 -0
  132. package/test/lib/rubberBand.test.js +84 -0
  133. package/test/lib/scene.test.js +9 -9
  134. package/test/lib/selectionManager.test.js +3 -3
  135. package/test/lib/settings.test.js +5 -6
  136. package/test/lib/snapping.test.js +72 -130
  137. package/test/lib/stateManager.test.js +7 -7
  138. package/test/lib/utils.test.js +36 -2
  139. package/test/properties/entityProperties.test.js +132 -0
  140. package/test/properties/property.test.js +53 -0
  141. package/test/properties/propertyManager.test.js +25 -25
  142. package/test/renderers/canvasRenderer.test.js +317 -0
  143. package/test/renderers/pdfRenderer.test.js +271 -0
  144. package/test/renderers/rendererBase.test.js +213 -0
  145. package/test/renderers/svgRenderer.test.js +326 -0
  146. package/test/tables/block.test.js +92 -13
  147. package/test/tables/endblk.test.js +2 -2
  148. package/test/tables/layer.test.js +30 -0
  149. package/test/tables/layerManager.test.js +2 -2
  150. package/test/test-helpers/hatchGridRenderer.js +180 -0
  151. package/test/test-helpers/test-helpers.js +158 -6
  152. package/test/tools/chamfer.test.js +273 -51
  153. package/test/tools/chamferFilletBase.test.js +125 -36
  154. package/test/tools/copy.test.js +8 -8
  155. package/test/tools/copybase.test.js +2 -2
  156. package/test/tools/copyclip.test.js +2 -2
  157. package/test/tools/cornerEntity.test.js +8 -8
  158. package/test/tools/cutclip.test.js +2 -2
  159. package/test/tools/erase.test.js +6 -6
  160. package/test/tools/explode.test.js +5 -5
  161. package/test/tools/extend.test.js +373 -42
  162. package/test/tools/fillet.test.js +230 -49
  163. package/test/tools/matchProp.test.js +11 -11
  164. package/test/tools/mirror.test.js +39 -39
  165. package/test/tools/move.test.js +7 -7
  166. package/test/tools/offset.test.js +222 -55
  167. package/test/tools/pasteclip.test.js +5 -5
  168. package/test/tools/purge.test.js +4 -4
  169. package/test/tools/rotate.test.js +39 -9
  170. package/test/tools/scale.test.js +33 -33
  171. package/test/tools/trim.test.js +579 -53
  172. package/test/tools/zoom.test.js +12 -12
  173. package/core/lib/selectionWindow.js +0 -83
@@ -1,6 +1,7 @@
1
1
  import { Text } from '../../core/entities/text.js';
2
2
  import { Point } from '../../core/entities/point.js';
3
3
  import { DesignCore } from '../../core/designCore.js';
4
+ import { Input } from '../../core/lib/input.js';
4
5
 
5
6
  import { File, withMockInput } from '../test-helpers/test-helpers.js';
6
7
  import { Core } from '../../core/core/core.js';
@@ -45,10 +46,10 @@ test.each(textInputScenarios)('Text.execute handles $desc', async (scenario) =>
45
46
  expect(text.points.length).toBeGreaterThanOrEqual(1);
46
47
  expect(text.points[0].x).toBe(pt0.x);
47
48
  expect(text.points[0].y).toBe(pt0.y);
48
- expect(text.height).toBe(expectedHeight);
49
- expect(text.rotation).toBe(expectedRotation);
50
- expect(text.string).toBe(expectedString);
51
- expect(text.styleName).toBe(styleName);
49
+ expect(text.getProperty('height')).toBe(expectedHeight);
50
+ expect(text.getProperty('rotation')).toBe(expectedRotation);
51
+ expect(text.getProperty('string')).toBe(expectedString);
52
+ expect(text.getProperty('styleName')).toBe(styleName);
52
53
  });
53
54
  });
54
55
 
@@ -96,8 +97,8 @@ test('Test Text.getTextFrameCorners', () => {
96
97
 
97
98
  // backwards and upsideDown
98
99
  text.setRotation(0);
99
- text.backwards = true;
100
- text.upsideDown = true;
100
+ text.setProperty('backwards', true);
101
+ text.setProperty('upsideDown', true);
101
102
  corners = text.getTextFrameCorners();
102
103
 
103
104
  expect(corners[0].x).toBeCloseTo(90);
@@ -113,9 +114,9 @@ test('Test Text.getTextFrameCorners', () => {
113
114
  expect(corners[3].y).toBeCloseTo(100);
114
115
 
115
116
  // Text frame calculation with text alignment
116
- // 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
117
118
  const alignedText = new Text({ points: [new Point(100, 100)] });
118
- alignedText.horizontalAlignment = 1; // center
119
+ alignedText.setProperty('horizontalAlignment', 1); // centre
119
120
  corners = alignedText.getTextFrameCorners();
120
121
 
121
122
  expect(corners[0].x).toBeCloseTo(95);
@@ -132,7 +133,7 @@ test('Test Text.getTextFrameCorners', () => {
132
133
 
133
134
  // set vertical alignment to middle as well
134
135
  // offset the text frame up by another 5 in y so the corners should be at 95,95 - 105,105
135
- alignedText.verticalAlignment = 2; // middle
136
+ alignedText.setProperty('verticalAlignment', 2); // middle
136
137
  corners = alignedText.getTextFrameCorners();
137
138
 
138
139
  expect(corners[0].x).toBeCloseTo(95);
@@ -148,7 +149,7 @@ test('Test Text.getTextFrameCorners', () => {
148
149
  expect(corners[3].y).toBeCloseTo(105);
149
150
 
150
151
 
151
- // 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
152
153
  alignedText.setRotation(45);
153
154
  corners = alignedText.getTextFrameCorners();
154
155
 
@@ -165,7 +166,7 @@ test('Test Text.getTextFrameCorners', () => {
165
166
  expect(corners[3].y).toBeCloseTo(100);
166
167
 
167
168
  // flip the text backwards and ensure the text frame corners are still correctly calculated with the flipped text
168
- alignedText.backwards = true;
169
+ alignedText.setProperty('backwards', true);
169
170
  corners = alignedText.getTextFrameCorners();
170
171
 
171
172
  expect(corners[0].x).toBeCloseTo(92.92893);
@@ -182,7 +183,7 @@ test('Test Text.getTextFrameCorners', () => {
182
183
 
183
184
  // test right and top alignment
184
185
  const rightAlignedtext = new Text({ points: [new Point(100, 100)] });
185
- rightAlignedtext.horizontalAlignment = 2; // right
186
+ rightAlignedtext.setProperty('horizontalAlignment', 2); // right
186
187
  corners = rightAlignedtext.getTextFrameCorners();
187
188
 
188
189
  expect(corners[0].x).toBeCloseTo(90);
@@ -198,7 +199,7 @@ test('Test Text.getTextFrameCorners', () => {
198
199
  expect(corners[3].y).toBeCloseTo(110);
199
200
 
200
201
  // add vertical alignment top and ensure the text frame is still correctly calculated with the right and top alignment applied
201
- rightAlignedtext.verticalAlignment = 3; // top
202
+ rightAlignedtext.setProperty('verticalAlignment', 3); // top
202
203
  corners = rightAlignedtext.getTextFrameCorners();
203
204
 
204
205
  expect(corners[0].x).toBeCloseTo(90);
@@ -219,47 +220,47 @@ test('Test Text.setRotation', () => {
219
220
 
220
221
  // Zero
221
222
  setRotText.setRotation(0);
222
- expect(setRotText.rotation).toBeCloseTo(0);
223
+ expect(setRotText.getProperty('rotation')).toBeCloseTo(0);
223
224
 
224
225
  // Positive
225
226
  setRotText.setRotation(22.5);
226
- expect(setRotText.rotation).toBe(22.5);
227
+ expect(setRotText.getProperty('rotation')).toBe(22.5);
227
228
 
228
229
  setRotText.setRotation(23);
229
- expect(setRotText.rotation).toBe(23);
230
+ expect(setRotText.getProperty('rotation')).toBe(23);
230
231
 
231
232
  setRotText.setRotation(45);
232
- expect(setRotText.rotation).toBe(45);
233
+ expect(setRotText.getProperty('rotation')).toBe(45);
233
234
 
234
235
  setRotText.setRotation(90);
235
- expect(setRotText.rotation).toBe(90);
236
+ expect(setRotText.getProperty('rotation')).toBe(90);
236
237
 
237
238
  setRotText.setRotation(135);
238
- expect(setRotText.rotation).toBe(135);
239
+ expect(setRotText.getProperty('rotation')).toBe(135);
239
240
 
240
241
  setRotText.setRotation(180);
241
- expect(setRotText.rotation).toBe(180);
242
+ expect(setRotText.getProperty('rotation')).toBe(180);
242
243
 
243
244
  setRotText.setRotation(225);
244
- expect(setRotText.rotation).toBe(225);
245
+ expect(setRotText.getProperty('rotation')).toBe(225);
245
246
 
246
247
  setRotText.setRotation(270);
247
- expect(setRotText.rotation).toBe(270);
248
+ expect(setRotText.getProperty('rotation')).toBe(270);
248
249
 
249
250
  // Greater than 360
250
251
  setRotText.setRotation((360 + 90));
251
- expect(setRotText.rotation).toBe(90);
252
+ expect(setRotText.getProperty('rotation')).toBe(90);
252
253
 
253
254
  // Negative
254
255
  setRotText.setRotation(-22.5);
255
- expect(setRotText.rotation).toBe(337.5);
256
+ expect(setRotText.getProperty('rotation')).toBe(337.5);
256
257
 
257
258
  setRotText.setRotation(-90);
258
- expect(setRotText.rotation).toBe(270);
259
+ expect(setRotText.getProperty('rotation')).toBe(270);
259
260
 
260
261
  // precision - rounds to closest 5 dp
261
262
  setRotText.setRotation(10.123456789);
262
- expect(setRotText.rotation).toBe(10.12346);
263
+ expect(setRotText.getProperty('rotation')).toBe(10.12346);
263
264
  });
264
265
 
265
266
 
@@ -307,13 +308,13 @@ test('Test Text.flags', () => {
307
308
  const text = new Text();
308
309
  expect(text.flags.getFlagValue()).toBe(0);
309
310
  // set backwards - set 2 on flags
310
- text.backwards = true;
311
+ text.setProperty('backwards', true);
311
312
  expect(text.flags.getFlagValue()).toBe(2);
312
313
  // set upsideDown - set 4 on flags
313
- text.upsideDown = true;
314
+ text.setProperty('upsideDown', true);
314
315
  expect(text.flags.getFlagValue()).toBe(6);
315
316
  // unset backwards - remove 2 on flags
316
- text.backwards = false;
317
+ text.setProperty('backwards', false);
317
318
  expect(text.flags.getFlagValue()).toBe(4);
318
319
  });
319
320
 
@@ -322,10 +323,10 @@ test('Test Text.backwards', () => {
322
323
  // 2 = Text is backward (mirrored in X).
323
324
  // 4 = Text is upside down (mirrored in Y).
324
325
  const text = new Text();
325
- expect(text.backwards).toBe(false);
326
+ expect(text.getProperty('backwards')).toBe(false);
326
327
  expect(text.flags.getFlagValue()).toBe(0);
327
- text.backwards = true;
328
- expect(text.backwards).toBe(true);
328
+ text.setProperty('backwards', true);
329
+ expect(text.getProperty('backwards')).toBe(true);
329
330
  expect(text.flags.getFlagValue()).toBe(2);
330
331
  });
331
332
 
@@ -334,10 +335,10 @@ test('Test Text.upsideDown', () => {
334
335
  // 2 = Text is backward (mirrored in X).
335
336
  // 4 = Text is upside down (mirrored in Y).
336
337
  const text = new Text();
337
- expect(text.upsideDown).toBe(false);
338
+ expect(text.getProperty('upsideDown')).toBe(false);
338
339
  expect(text.flags.getFlagValue()).toBe(0);
339
- text.upsideDown = true;
340
- expect(text.upsideDown).toBe(true);
340
+ text.setProperty('upsideDown', true);
341
+ expect(text.getProperty('upsideDown')).toBe(true);
341
342
  expect(text.flags.getFlagValue()).toBe(4);
342
343
  });
343
344
 
@@ -388,7 +389,15 @@ AcDbText
388
389
  expect(file.contents).toEqual(dxfString);
389
390
 
390
391
  // create new entity from entity data to ensure all props are loaded
391
- const newText = new Text(text);
392
+ const newText = new Text({
393
+ handle: text.getProperty('handle'),
394
+ points: text.points,
395
+ string: text.getProperty('string'),
396
+ height: text.getProperty('height'),
397
+ styleName: text.getProperty('styleName'),
398
+ horizontalAlignment: text.getProperty('horizontalAlignment'),
399
+ verticalAlignment: text.getProperty('verticalAlignment'),
400
+ });
392
401
  file = new File();
393
402
  newText.dxf(file);
394
403
  expect(file.contents).toEqual(dxfString);
@@ -399,13 +408,21 @@ AcDbText
399
408
  // and a second alignment point with groupcode 11,21,31 for the text insertion point
400
409
  // bounding rect is 10x10, so second point should be at 100-(10*0.5), 200-(10*0.5) = 95, 195
401
410
 
402
- const centeredText = new Text(text);
403
- centeredText.horizontalAlignment = 1; // center
404
- centeredText.verticalAlignment = 2; // middle
411
+ const centredText = new Text({
412
+ handle: text.getProperty('handle'),
413
+ points: text.points,
414
+ string: text.getProperty('string'),
415
+ height: text.getProperty('height'),
416
+ styleName: text.getProperty('styleName'),
417
+ horizontalAlignment: text.getProperty('horizontalAlignment'),
418
+ verticalAlignment: text.getProperty('verticalAlignment'),
419
+ });
420
+ centredText.setProperty('horizontalAlignment', 1); // centre
421
+ centredText.setProperty('verticalAlignment', 2); // middle
405
422
  file = new File();
406
- centeredText.dxf(file);
423
+ centredText.dxf(file);
407
424
 
408
- const dxfCenteredString = `0
425
+ const dxfCentredString = `0
409
426
  TEXT
410
427
  5
411
428
  1
@@ -442,23 +459,31 @@ AcDbText
442
459
  73
443
460
  2
444
461
  `;
445
- expect(file.contents).toEqual(dxfCenteredString);
462
+ expect(file.contents).toEqual(dxfCentredString);
446
463
 
447
464
  // create new entity from entity data to ensure all props are loaded
448
- const importCenteredText = new Text(centeredText);
465
+ const importCenteredText = new Text({
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
+ });
449
474
  file = new File();
450
475
  importCenteredText.dxf(file);
451
- expect(file.contents).toEqual(dxfCenteredString);
476
+ expect(file.contents).toEqual(dxfCentredString);
452
477
  });
453
478
 
454
479
  test('Text constructor covers all property branches', () => {
455
480
  // Minimal data
456
481
  let t = new Text({ points: [new Point(1, 2)] });
457
- expect(t.string).toBe('');
458
- expect(t.height).toBe(2.5);
459
- expect(t.horizontalAlignment).toBe(0);
460
- expect(t.verticalAlignment).toBe(0);
461
- expect(t.styleName).toBe('STANDARD');
482
+ expect(t.getProperty('string')).toBe('');
483
+ expect(t.getProperty('height')).toBe(2.5);
484
+ expect(t.getProperty('horizontalAlignment')).toBe(0);
485
+ expect(t.getProperty('verticalAlignment')).toBe(0);
486
+ expect(t.getProperty('styleName')).toBe('STANDARD');
462
487
 
463
488
  // All DXF groupcodes
464
489
  t = new Text({
@@ -478,49 +503,49 @@ test('Text constructor covers all property branches', () => {
478
503
  flags: 2,
479
504
  71: 4,
480
505
  });
481
- expect(['abc', 'def']).toContain(t.string);
482
- expect(['FOO', 'BAR']).toContain(t.styleName);
483
- expect([5, 6]).toContain(t.height);
484
- expect([45, 90]).toContain(t.rotation);
485
- expect([2, 1]).toContain(t.horizontalAlignment);
486
- expect([3, 2]).toContain(t.verticalAlignment);
506
+ expect(['abc', 'def']).toContain(t.getProperty('string'));
507
+ expect(['FOO', 'BAR']).toContain(t.getProperty('styleName'));
508
+ expect([5, 6]).toContain(t.getProperty('height'));
509
+ expect([45, 90]).toContain(t.getProperty('rotation'));
510
+ expect([2, 1]).toContain(t.getProperty('horizontalAlignment'));
511
+ expect([3, 2]).toContain(t.getProperty('verticalAlignment'));
487
512
  expect([2, 4, 6, 0]).toContain(t.flags.getFlagValue());
488
513
  });
489
514
 
490
515
  test('Text static register and getApproximateWidth', () => {
491
516
  expect(Text.register()).toEqual({ command: 'Text', shortcut: 'DT', type: 'Entity' });
492
- expect(Text.getApproximateWidth('abc', 10)).toBeCloseTo(18);
517
+ expect(Text.getApproximateWidth('abc', 10)).toBeCloseTo(16.2);
493
518
  });
494
519
 
495
520
  test('Text getHorizontalAlignment covers all cases', () => {
496
521
  const t = new Text({ points: [new Point()] });
497
- t.horizontalAlignment = 0;
522
+ t.setProperty('horizontalAlignment', 0);
498
523
  expect(t.getHorizontalAlignment()).toBe('left');
499
- t.horizontalAlignment = 1;
500
- expect(t.getHorizontalAlignment()).toBe('center');
501
- t.horizontalAlignment = 2;
524
+ t.setProperty('horizontalAlignment', 1);
525
+ expect(t.getHorizontalAlignment()).toBe('centre');
526
+ t.setProperty('horizontalAlignment', 2);
502
527
  expect(t.getHorizontalAlignment()).toBe('right');
503
- t.horizontalAlignment = 3; t.verticalAlignment = 0;
528
+ t.setProperty('horizontalAlignment', 3); t.setProperty('verticalAlignment', 0);
504
529
  expect(t.getHorizontalAlignment()).toBe('aligned');
505
- t.horizontalAlignment = 4; t.verticalAlignment = 0;
506
- expect(t.getHorizontalAlignment()).toBe('center');
507
- t.horizontalAlignment = 5; t.verticalAlignment = 0;
530
+ t.setProperty('horizontalAlignment', 4); t.setProperty('verticalAlignment', 0);
531
+ expect(t.getHorizontalAlignment()).toBe('centre');
532
+ t.setProperty('horizontalAlignment', 5); t.setProperty('verticalAlignment', 0);
508
533
  expect(t.getHorizontalAlignment()).toBe('fit');
509
- t.horizontalAlignment = 99;
534
+ t.setProperty('horizontalAlignment', 99);
510
535
  expect(t.getHorizontalAlignment()).toBe('left');
511
536
  });
512
537
 
513
538
  test('Text getVerticalAlignment covers all cases', () => {
514
539
  const t = new Text({ points: [new Point()] });
515
- t.verticalAlignment = 0;
540
+ t.setProperty('verticalAlignment', 0);
516
541
  expect(t.getVerticalAlignment()).toBe('alphabetic');
517
- t.verticalAlignment = 1;
542
+ t.setProperty('verticalAlignment', 1);
518
543
  expect(t.getVerticalAlignment()).toBe('bottom');
519
- t.verticalAlignment = 2;
544
+ t.setProperty('verticalAlignment', 2);
520
545
  expect(t.getVerticalAlignment()).toBe('middle');
521
- t.verticalAlignment = 3;
546
+ t.setProperty('verticalAlignment', 3);
522
547
  expect(t.getVerticalAlignment()).toBe('top');
523
- t.verticalAlignment = 99;
548
+ t.setProperty('verticalAlignment', 99);
524
549
  expect(t.getVerticalAlignment()).toBe('alphabetic');
525
550
  });
526
551
 
@@ -578,13 +603,13 @@ test('Text toPolylinePoints returns correct array', () => {
578
603
  test('Text setBackwards and setUpsideDown edge cases', () => {
579
604
  const t = new Text({ points: [new Point()] });
580
605
  t.setBackwards(true);
581
- expect(t.backwards).toBe(true);
606
+ expect(t.getProperty('backwards')).toBe(true);
582
607
  t.setBackwards(false);
583
- expect(t.backwards).toBe(false);
608
+ expect(t.getProperty('backwards')).toBe(false);
584
609
  t.setUpsideDown(true);
585
- expect(t.upsideDown).toBe(true);
610
+ expect(t.getProperty('upsideDown')).toBe(true);
586
611
  t.setUpsideDown(false);
587
- expect(t.upsideDown).toBe(false);
612
+ expect(t.getProperty('upsideDown')).toBe(false);
588
613
  });
589
614
 
590
615
  test('Text setRotation handles undefined and height 0', () => {
@@ -611,8 +636,8 @@ test('Text.execute re-prompts on zero or negative height', async () => {
611
636
  const text = new Text({});
612
637
  await text.execute();
613
638
 
614
- expect(text.height).toBe(5);
615
- expect(text.string).toBe('Hello');
639
+ expect(text.getProperty('height')).toBe(5);
640
+ expect(text.getProperty('string')).toBe('Hello');
616
641
  });
617
642
  });
618
643
 
@@ -624,7 +649,7 @@ test('Text.execute does not create entity for empty string', async () => {
624
649
  const text = new Text({});
625
650
  await text.execute();
626
651
 
627
- expect(text.string).toBe('');
652
+ expect(text.getProperty('string')).toBe('');
628
653
  }, { extraMethods: { executeCommand: () => {
629
654
  executeCommandCalled = true;
630
655
  } } });
@@ -639,3 +664,203 @@ test('Text.snaps node snap position matches points[0]', () => {
639
664
  expect(nodeSnaps[0].snapPoint.x).toBe(5);
640
665
  expect(nodeSnaps[0].snapPoint.y).toBe(10);
641
666
  });
667
+
668
+ test('Text.preview - uses rubber band when not waiting for string input', () => {
669
+ const text = new Text({ points: [new Point(5, 5)] });
670
+ DesignCore.Scene.auxiliaryEntities.clear();
671
+ DesignCore.Scene.previewEntities.clear();
672
+ // Simulate a pending POINT prompt (not STRING)
673
+ DesignCore.Scene.inputManager.promptOption = { types: [Input.Type.POINT] };
674
+
675
+ text.preview();
676
+
677
+ expect(DesignCore.Scene.auxiliaryEntities.count()).toBeGreaterThanOrEqual(1);
678
+ expect(DesignCore.Scene.previewEntities.count()).toBe(0);
679
+
680
+ DesignCore.Scene.inputManager.promptOption = undefined;
681
+ });
682
+
683
+ test('Text.preview - shows text entity when waiting for string input', () => {
684
+ const text = new Text({ points: [new Point(5, 5)], height: 5, rotation: 0, string: '' });
685
+ DesignCore.Scene.auxiliaryEntities.clear();
686
+ DesignCore.Scene.previewEntities.clear();
687
+ // Simulate a pending STRING prompt
688
+ DesignCore.Scene.inputManager.promptOption = { types: [Input.Type.STRING] };
689
+
690
+ text.preview();
691
+
692
+ expect(DesignCore.Scene.previewEntities.count()).toBeGreaterThanOrEqual(1);
693
+ expect(DesignCore.Scene.auxiliaryEntities.count()).toBe(0);
694
+
695
+ DesignCore.Scene.inputManager.promptOption = undefined;
696
+ });
697
+
698
+ test('Text.getApproximateWidth unknown character uses fallback width', () => {
699
+ // Unknown characters fall back to 0.56 em per character
700
+ const unknown = Text.getApproximateWidth('€', 10);
701
+ expect(unknown).toBeCloseTo(5.6);
702
+ });
703
+
704
+ test('Text.getApproximateWidth empty string returns 0', () => {
705
+ expect(Text.getApproximateWidth('', 10)).toBe(0);
706
+ });
707
+
708
+ test('Text setRotation with height 0 does not move points[1]', () => {
709
+ const t = new Text({ points: [new Point(0, 0)] });
710
+ t.setProperty('height', 0);
711
+ const originalPoint1 = t.points[1];
712
+ t.setRotation(45);
713
+ expect(t.points[1]).toBe(originalPoint1);
714
+ });
715
+
716
+ test('Text.fromDxf remaps insertion point from sequence-11 point', () => {
717
+ // When DXF points[1] has sequence == 11, fromDxf uses it as the insertion point (points[0])
718
+ const data = Text.fromDxf({
719
+ points: [
720
+ { x: 0, y: 0 },
721
+ { x: 50, y: 75, sequence: 11 },
722
+ ],
723
+ });
724
+ const t = new Text(data);
725
+ expect(t.points[0].x).toBe(50);
726
+ expect(t.points[0].y).toBe(75);
727
+ });
728
+
729
+ test('Text getHorizontalAlignment unsupported cases return left when verticalAlignment !== 0', () => {
730
+ const t = new Text({ points: [new Point()] });
731
+ t.setProperty('verticalAlignment', 1); // non-zero
732
+
733
+ t.setProperty('horizontalAlignment', 3);
734
+ expect(t.getHorizontalAlignment()).toBe('left');
735
+
736
+ t.setProperty('horizontalAlignment', 4);
737
+ expect(t.getHorizontalAlignment()).toBe('left');
738
+
739
+ t.setProperty('horizontalAlignment', 5);
740
+ expect(t.getHorizontalAlignment()).toBe('left');
741
+ });
742
+
743
+ test('Text.dxf includes non-zero flags and rotation', () => {
744
+ const text = new Text({ handle: '1', points: [new Point(0, 0)] });
745
+ text.setProperty('backwards', true); // flag value 2
746
+ text.setProperty('upsideDown', true); // flag value 4 → combined = 6
747
+ text.setRotation(90);
748
+ text.setProperty('string', 'R');
749
+
750
+ const file = new File();
751
+ text.dxf(file);
752
+
753
+ expect(file.contents).toContain('71\n6\n');
754
+ expect(file.contents).toContain('50\n90\n');
755
+ });
756
+
757
+ test('Text.toCharacters returns one entry with scene coords, rotation and string', () => {
758
+ const text = new Text({ points: [new Point(5, 10), new Point(15, 10)], string: 'Hello', height: 2.5, rotation: 0 });
759
+ const chars = text.toCharacters();
760
+ expect(chars).toHaveLength(1);
761
+ expect(chars[0].char).toBe('Hello');
762
+ expect(typeof chars[0].x).toBe('number');
763
+ expect(typeof chars[0].y).toBe('number');
764
+ expect(chars[0].rotation).toBeCloseTo(0);
765
+ expect(chars[0].upsideDownOffset).toBe(0);
766
+ expect(chars[0].backwardsOffset).toBe(0);
767
+ });
768
+
769
+ test('Text.toCharacters returns empty array for empty string', () => {
770
+ const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: '', height: 2.5 });
771
+ expect(text.toCharacters()).toEqual([]);
772
+ });
773
+
774
+ test('Text.draw calls renderer.drawText with correct font and string', () => {
775
+ const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: 'Hello', height: 2.5 });
776
+ let calledChars; let calledHeight;
777
+ const mockRenderer = {
778
+ drawText(characters, fontName, height) {
779
+ calledChars = characters;
780
+ calledHeight = height;
781
+ },
782
+ measureText: () => ({ width: 20 }),
783
+ };
784
+ text.draw(mockRenderer);
785
+ expect(calledChars).toHaveLength(1);
786
+ expect(calledChars[0].char).toBe('Hello');
787
+ expect(calledHeight).toBe(2.5);
788
+ });
789
+
790
+ test('Text.draw skips call for empty string', () => {
791
+ const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: '', height: 2.5 });
792
+ let drawTextCalled = false;
793
+ const mockRenderer = { drawText() {
794
+ drawTextCalled = true;
795
+ }, measureText() {} };
796
+ text.draw(mockRenderer);
797
+ expect(drawTextCalled).toBe(false);
798
+ });
799
+
800
+ test('Text.draw updates boundingRect from measureText result', () => {
801
+ const text = new Text({ points: [new Point(0, 0), new Point(10, 0)], string: 'Hi', height: 3 });
802
+ const mockRenderer = {
803
+ drawText() {},
804
+ measureText: () => ({ width: 42 }),
805
+ };
806
+ text.draw(mockRenderer);
807
+ expect(text.boundingRect.width).toBe(42);
808
+ expect(text.boundingRect.height).toBe(3);
809
+ });
810
+
811
+ describe('Text.fromDxf', () => {
812
+ test('projects points[1] from rotation angle (group code 50)', () => {
813
+ const data = Text.fromDxf({
814
+ points: [new Point(10, 20)],
815
+ 50: 90,
816
+ 40: 2.5,
817
+ });
818
+ const t = new Text(data);
819
+ expect(t.getProperty('rotation')).toBeCloseTo(90);
820
+ expect(t.points[1].x).toBeCloseTo(10);
821
+ expect(t.points[1].y).toBeCloseTo(22.5);
822
+ });
823
+
824
+ test('remaps sequence-11 alignment point to points[0]', () => {
825
+ const data = Text.fromDxf({
826
+ points: [
827
+ new Point(0, 0),
828
+ { x: 30, y: 40, sequence: 11 },
829
+ ],
830
+ });
831
+ const t = new Text(data);
832
+ expect(t.points[0].x).toBe(30);
833
+ expect(t.points[0].y).toBe(40);
834
+ });
835
+
836
+ test('preserves other properties unchanged', () => {
837
+ const data = Text.fromDxf({
838
+ points: [new Point(0, 0)],
839
+ 1: 'hello',
840
+ 40: 5,
841
+ layer: 'TEST',
842
+ });
843
+ expect(data[1]).toBe('hello');
844
+ expect(data[40]).toBe(5);
845
+ expect(data.layer).toBe('TEST');
846
+ });
847
+
848
+ test('returns data unchanged when no points present', () => {
849
+ const data = Text.fromDxf({ 1: 'abc' });
850
+ expect(data[1]).toBe('abc');
851
+ expect(data.points).toEqual([]);
852
+ });
853
+
854
+ test('result equivalent to direct construction with setRotation', () => {
855
+ const pt = new Point(5, 10);
856
+ const fromDxfData = Text.fromDxf({ points: [pt], 50: 45, 40: 2.5 });
857
+ const t1 = new Text(fromDxfData);
858
+
859
+ const t2 = new Text({ points: [pt] });
860
+ t2.setRotation(45);
861
+
862
+ expect(t1.points[0].x).toBeCloseTo(t2.points[0].x);
863
+ expect(t1.points[0].y).toBeCloseTo(t2.points[0].y);
864
+ expect(t1.getProperty('rotation')).toBeCloseTo(t2.getProperty('rotation'));
865
+ });
866
+ });
@@ -861,7 +861,7 @@ AcDbViewportTableRecord
861
861
  21
862
862
  1.0
863
863
  12
864
- 142.5
864
+ 145
865
865
  22
866
866
  142.5
867
867
  13
@@ -891,7 +891,7 @@ AcDbViewportTableRecord
891
891
  40
892
892
  275
893
893
  41
894
- 0.9272727272727272
894
+ 0.9454545454545454
895
895
  42
896
896
  50.0
897
897
  43
@@ -1133,7 +1133,7 @@ ByLayer
1133
1133
  31
1134
1134
  0.0
1135
1135
  39
1136
- 2
1136
+ 1
1137
1137
  0
1138
1138
  ENDBLK
1139
1139
  5
@@ -1173,7 +1173,7 @@ ByLayer
1173
1173
  31
1174
1174
  0.0
1175
1175
  39
1176
- 2
1176
+ 1
1177
1177
  0
1178
1178
  TEXT
1179
1179
  5
@@ -1221,7 +1221,7 @@ ByLayer
1221
1221
  30
1222
1222
  0.0
1223
1223
  39
1224
- 2
1224
+ 1
1225
1225
  40
1226
1226
  25
1227
1227
  0
@@ -1271,9 +1271,9 @@ ByLayer
1271
1271
  30
1272
1272
  0.0
1273
1273
  39
1274
- 2
1275
- 40
1276
1274
  1
1275
+ 40
1276
+ 25
1277
1277
  100
1278
1278
  AcDbArc
1279
1279
  50
@@ -1377,7 +1377,7 @@ POLYLINE_LAYER
1377
1377
  6
1378
1378
  ByLayer
1379
1379
  39
1380
- 2
1380
+ 1
1381
1381
  90
1382
1382
  4
1383
1383
  70
@@ -1447,7 +1447,7 @@ LWPOLYLINE_LAYER
1447
1447
  6
1448
1448
  ByLayer
1449
1449
  39
1450
- 2
1450
+ 1
1451
1451
  90
1452
1452
  4
1453
1453
  70