@design-core/core 0.0.6 → 0.0.7

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 (145) hide show
  1. package/.eslintrc.json +4 -0
  2. package/TODO.md +49 -6
  3. package/core/core/core.js +0 -3
  4. package/core/dimensions/alignedDimension.js +5 -4
  5. package/core/dimensions/angularDimension.js +6 -5
  6. package/core/dimensions/baseCircularDimension.js +3 -2
  7. package/core/dimensions/baseDimension.js +59 -93
  8. package/core/dimensions/baseLinearDimension.js +6 -5
  9. package/core/dimensions/diametricDimension.js +4 -3
  10. package/core/dimensions/dimension.js +21 -14
  11. package/core/dimensions/radialDimension.js +4 -3
  12. package/core/dimensions/rotatedDimension.js +12 -11
  13. package/core/entities/arc.js +112 -145
  14. package/core/entities/arctext.js +212 -174
  15. package/core/entities/basePolyline.js +30 -282
  16. package/core/entities/circle.js +77 -99
  17. package/core/entities/entity.js +114 -93
  18. package/core/entities/hatch.js +322 -249
  19. package/core/entities/insert.js +66 -81
  20. package/core/entities/line.js +21 -142
  21. package/core/entities/lwpolyline.js +16 -11
  22. package/core/entities/polyline.js +4 -4
  23. package/core/entities/rectangle.js +3 -1
  24. package/core/entities/solid.js +6 -15
  25. package/core/entities/text.js +188 -199
  26. package/core/lib/auxiliary/rubberBand.js +32 -0
  27. package/core/lib/auxiliary/selectionWindow.js +63 -0
  28. package/core/lib/auxiliary/snapPoint.js +113 -0
  29. package/core/lib/auxiliary/trackingLine.js +76 -0
  30. package/core/lib/canvas.js +228 -166
  31. package/core/lib/clipboard.js +7 -1
  32. package/core/lib/colour.js +16 -0
  33. package/core/lib/commandLine.js +1 -6
  34. package/core/lib/commandManager.js +18 -8
  35. package/core/lib/constants.js +28 -0
  36. package/core/lib/dxf/dxf.js +4 -3
  37. package/core/lib/entityManager.js +18 -11
  38. package/core/lib/inputManager.js +35 -11
  39. package/core/lib/intersect.js +25 -0
  40. package/core/lib/patterns.js +8 -1
  41. package/core/lib/plotOptions.js +67 -0
  42. package/core/lib/polylineUtils.js +75 -0
  43. package/core/lib/renderers/cairoRenderer.js +371 -0
  44. package/core/lib/renderers/canvasRenderer.js +315 -0
  45. package/core/lib/renderers/pdfRenderer.js +366 -0
  46. package/core/lib/renderers/rendererBase.js +284 -0
  47. package/core/lib/renderers/svgRenderer.js +343 -0
  48. package/core/lib/scene.js +27 -5
  49. package/core/lib/selectionManager.js +2 -2
  50. package/core/lib/settings.js +4 -2
  51. package/core/lib/snapping.js +4 -191
  52. package/core/lib/stateManager.js +30 -1
  53. package/core/lib/utils.js +21 -2
  54. package/core/properties/entityProperties.js +104 -0
  55. package/core/properties/property.js +106 -2
  56. package/core/properties/propertyManager.js +29 -13
  57. package/core/tables/block.js +4 -37
  58. package/core/tables/endblk.js +1 -1
  59. package/core/tables/layer.js +8 -0
  60. package/core/tables/styleManagerBase.js +3 -2
  61. package/core/tables/tableManagerBase.js +8 -3
  62. package/core/tools/chamfer.js +111 -137
  63. package/core/tools/chamferFilletBase.js +135 -34
  64. package/core/tools/copy.js +3 -3
  65. package/core/tools/explode.js +2 -2
  66. package/core/tools/extend.js +132 -19
  67. package/core/tools/fillet.js +86 -183
  68. package/core/tools/matchProp.js +2 -11
  69. package/core/tools/mirror.js +7 -6
  70. package/core/tools/move.js +3 -3
  71. package/core/tools/offset.js +3 -5
  72. package/core/tools/pasteclip.js +1 -1
  73. package/core/tools/rotate.js +6 -3
  74. package/core/tools/scale.js +1 -1
  75. package/core/tools/trim.js +265 -23
  76. package/core/tools/zoom.js +1 -1
  77. package/jest.config.js +6 -0
  78. package/package.json +5 -3
  79. package/test/dimensions/alignedDimension.test.js +14 -7
  80. package/test/dimensions/angularDimension.test.js +6 -6
  81. package/test/dimensions/baseDimension.test.js +69 -8
  82. package/test/dimensions/baseLinearDimension.test.js +62 -0
  83. package/test/dimensions/diametricDimension.test.js +6 -6
  84. package/test/dimensions/radialDimension.test.js +6 -6
  85. package/test/dimensions/rotatedDimension.test.js +14 -7
  86. package/test/entities/arc.test.js +80 -83
  87. package/test/entities/arctext.test.js +180 -53
  88. package/test/entities/basePolyline.test.js +68 -2
  89. package/test/entities/circle.test.js +57 -85
  90. package/test/entities/entity.test.js +42 -8
  91. package/test/entities/hatch.test.js +543 -70
  92. package/test/entities/insert.test.js +96 -1
  93. package/test/entities/line.test.js +15 -41
  94. package/test/entities/lwpolyline.test.js +104 -0
  95. package/test/entities/polyline.test.js +11 -0
  96. package/test/entities/rectangle.test.js +11 -0
  97. package/test/entities/solid.test.js +1 -1
  98. package/test/entities/text.test.js +294 -69
  99. package/test/integration/exportIntegration.reference.dxf +9 -9
  100. package/test/integration/exportIntegration.test.js +2 -2
  101. package/test/integration/intersectionIntegration.test.js +4 -5
  102. package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
  103. package/test/integration/pdfRendererIntegration.test.js +61 -0
  104. package/test/integration/svgRendererIntegration.reference.svg +3343 -0
  105. package/test/integration/svgRendererIntegration.test.js +61 -0
  106. package/test/integration/userInputIntegration.reference.dxf +1165 -105
  107. package/test/integration/userInputIntegration.test.js +239 -132
  108. package/test/lib/canvas.test.js +742 -123
  109. package/test/lib/constants.test.js +27 -0
  110. package/test/lib/entityManager.test.js +4 -4
  111. package/test/lib/inputManager.test.js +80 -19
  112. package/test/lib/intersect.test.js +83 -0
  113. package/test/lib/plotOptions.test.js +179 -0
  114. package/test/lib/rubberBand.test.js +84 -0
  115. package/test/lib/settings.test.js +5 -6
  116. package/test/lib/snapping.test.js +71 -129
  117. package/test/lib/stateManager.test.js +7 -7
  118. package/test/lib/utils.test.js +36 -2
  119. package/test/properties/entityProperties.test.js +132 -0
  120. package/test/properties/property.test.js +53 -0
  121. package/test/properties/propertyManager.test.js +3 -3
  122. package/test/renderers/canvasRenderer.test.js +317 -0
  123. package/test/renderers/pdfRenderer.test.js +271 -0
  124. package/test/renderers/rendererBase.test.js +213 -0
  125. package/test/renderers/svgRenderer.test.js +326 -0
  126. package/test/tables/block.test.js +31 -1
  127. package/test/tables/endblk.test.js +2 -2
  128. package/test/tables/layer.test.js +30 -0
  129. package/test/test-helpers/hatchGridRenderer.js +180 -0
  130. package/test/test-helpers/test-helpers.js +152 -0
  131. package/test/tools/chamfer.test.js +227 -5
  132. package/test/tools/chamferFilletBase.test.js +98 -9
  133. package/test/tools/copy.test.js +2 -2
  134. package/test/tools/explode.test.js +3 -3
  135. package/test/tools/extend.test.js +331 -0
  136. package/test/tools/fillet.test.js +181 -0
  137. package/test/tools/matchProp.test.js +5 -5
  138. package/test/tools/mirror.test.js +13 -13
  139. package/test/tools/offset.test.js +170 -3
  140. package/test/tools/pasteclip.test.js +5 -5
  141. package/test/tools/rotate.test.js +32 -2
  142. package/test/tools/scale.test.js +5 -5
  143. package/test/tools/trim.test.js +526 -0
  144. package/test/tools/zoom.test.js +7 -7
  145. 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);
@@ -115,7 +116,7 @@ test('Test Text.getTextFrameCorners', () => {
115
116
  // Text frame calculation with text alignment
116
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
118
  const alignedText = new Text({ points: [new Point(100, 100)] });
118
- alignedText.horizontalAlignment = 1; // center
119
+ alignedText.setProperty('horizontalAlignment', 1); // center
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);
@@ -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,9 +408,17 @@ 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 centeredText = 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
+ centeredText.setProperty('horizontalAlignment', 1); // center
421
+ centeredText.setProperty('verticalAlignment', 2); // middle
405
422
  file = new File();
406
423
  centeredText.dxf(file);
407
424
 
@@ -445,7 +462,15 @@ AcDbText
445
462
  expect(file.contents).toEqual(dxfCenteredString);
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: 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'),
473
+ });
449
474
  file = new File();
450
475
  importCenteredText.dxf(file);
451
476
  expect(file.contents).toEqual(dxfCenteredString);
@@ -454,11 +479,11 @@ AcDbText
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;
524
+ t.setProperty('horizontalAlignment', 1);
500
525
  expect(t.getHorizontalAlignment()).toBe('center');
501
- t.horizontalAlignment = 2;
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;
530
+ t.setProperty('horizontalAlignment', 4); t.setProperty('verticalAlignment', 0);
506
531
  expect(t.getHorizontalAlignment()).toBe('center');
507
- t.horizontalAlignment = 5; t.verticalAlignment = 0;
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
@@ -113,13 +113,13 @@ beforeAll(() => {
113
113
  patternName: 'ANSI31',
114
114
  layer: 'HATCH_LAYER',
115
115
  });
116
- hatch.childEntities = [boundary];
116
+ hatch.setProperty('childEntities', [boundary]);
117
117
  core.scene.entities.add(hatch);
118
118
  core.scene.entities.add(new Text({ points: [new Point(15, 205)], string: 'Hatch', height: 4 }));
119
119
 
120
120
  // Row 2, Col 1: Insert
121
121
  const blockLine = new Line({ points: [new Point(0, 0), new Point(40, 40)] });
122
- blockLine.handle = DesignCore.HandleManager.next();
122
+ blockLine.setProperty('handle', DesignCore.HandleManager.next());
123
123
  const block = DesignCore.Scene.blockManager.addItem({
124
124
  name: 'TEST_BLOCK',
125
125
  items: [blockLine],
@@ -285,7 +285,7 @@ describe('Entity.touched - selection rectangle intersection', () => {
285
285
  const charPos = pts[0];
286
286
  const sE = selectionExtremes(
287
287
  new Point(charPos.x - 5, charPos.y - 5),
288
- new Point(charPos.x + 5, charPos.y + 5),
288
+ new Point(charPos.x + 1, charPos.y + 5),
289
289
  );
290
290
  expect(arcText.touched(sE)).toBe(true);
291
291
  }
@@ -705,7 +705,7 @@ describe('Hatch touched tests', () => {
705
705
  const boundary = new Polyline({
706
706
  points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100), new Point(0, 0)],
707
707
  });
708
- hatch.childEntities.push(boundary);
708
+ hatch.setProperty('childEntities', [boundary]);
709
709
 
710
710
  // Selection rectangle crossing the boundary
711
711
  const sE = selectionExtremes(new Point(-5, 50), new Point(5, 60));
@@ -717,7 +717,7 @@ describe('Hatch touched tests', () => {
717
717
  const boundary = new Polyline({
718
718
  points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10), new Point(0, 0)],
719
719
  });
720
- hatch.childEntities.push(boundary);
720
+ hatch.setProperty('childEntities', [boundary]);
721
721
 
722
722
  // Selection rectangle far from hatch
723
723
  const sE = selectionExtremes(new Point(500, 500), new Point(600, 600));
@@ -732,8 +732,7 @@ describe('Hatch touched tests', () => {
732
732
  const boundary2 = new Polyline({
733
733
  points: [new Point(50, 50), new Point(60, 50), new Point(60, 60), new Point(50, 60), new Point(50, 50)],
734
734
  });
735
- hatch.childEntities.push(boundary1);
736
- hatch.childEntities.push(boundary2);
735
+ hatch.setProperty('childEntities', [boundary1, boundary2]);
737
736
 
738
737
  // Touch only the second boundary
739
738
  const sE = selectionExtremes(new Point(48, 55), new Point(52, 58));