@design-core/core 51.2.0 → 52.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/core/dimensions/alignedDimension.js +3 -1
  2. package/core/dimensions/angularDimension.js +118 -36
  3. package/core/dimensions/circularDimensionBase.js +32 -12
  4. package/core/dimensions/diametricDimension.js +29 -1
  5. package/core/dimensions/dimension.js +58 -5
  6. package/core/dimensions/dimensionBase.js +107 -4
  7. package/core/dimensions/linearDimensionBase.js +75 -37
  8. package/core/dimensions/radialDimension.js +26 -1
  9. package/core/dimensions/rotatedDimension.js +6 -3
  10. package/core/entities/entity.js +9 -0
  11. package/core/lib/auxiliary/snapPoint.js +9 -0
  12. package/core/lib/canvas.js +22 -0
  13. package/core/lib/commandManager.js +4 -4
  14. package/core/lib/dxf/dxf.js +8 -2
  15. package/core/lib/inputManager.js +23 -3
  16. package/core/lib/renderers/pdfRenderer.js +17 -1
  17. package/core/lib/scene.js +2 -0
  18. package/core/lib/selectionManager.js +28 -0
  19. package/core/lib/strings.js +1 -0
  20. package/core/properties/dimType.js +14 -0
  21. package/core/properties/property.js +1 -0
  22. package/core/tables/dimStyle.js +32 -2
  23. package/core/tables/dimStyleManager.js +2 -0
  24. package/core/tables/styleManagerBase.js +17 -3
  25. package/core/tables/tableManagerBase.js +24 -2
  26. package/core/tools/gripStretch.js +97 -0
  27. package/core/tools/offset.js +7 -1
  28. package/core/tools/tool.js +10 -0
  29. package/cspell.json +3 -0
  30. package/package.json +1 -1
  31. package/test/dimensions/alignedDimension.test.js +165 -0
  32. package/test/dimensions/angularDimension.test.js +149 -0
  33. package/test/dimensions/diametricDimension.test.js +77 -4
  34. package/test/dimensions/dimension.test.js +130 -0
  35. package/test/dimensions/dimensionBase.test.js +97 -4
  36. package/test/dimensions/radialDimension.test.js +34 -0
  37. package/test/dimensions/rotatedDimension.test.js +80 -0
  38. package/test/integration/exportIntegration.reference.dxf +3 -9
  39. package/test/integration/exportIntegration.test.js +1 -0
  40. package/test/integration/userInputIntegration.reference.dxf +3 -9
  41. package/test/integration/userInputIntegration.test.js +1 -0
  42. package/test/lib/inputManager.test.js +316 -1
  43. package/test/lib/selectionManager.test.js +42 -0
  44. package/test/renderers/pdfRenderer.test.js +26 -0
  45. package/test/tables/dimStyleManager.test.js +90 -0
  46. package/test/tables/dimstyle.test.js +32 -0
  47. package/test/tables/tableManagerBase.test.js +167 -0
  48. package/test/tools/gripStretch.test.js +108 -0
  49. package/test/tools/offset.test.js +2 -0
  50. package/test/tools/purge.test.js +3 -1
  51. package/test/tools/tool.test.js +25 -0
@@ -1,5 +1,7 @@
1
1
  import { Core } from '../../core/core/core.js';
2
2
  import { TableManagerBase } from '../../core/tables/tableManagerBase.js';
3
+ import { DesignCore } from '../../core/designCore.js';
4
+ import { Point } from '../../core/entities/point.js';
3
5
 
4
6
  // initialise core
5
7
  new Core();
@@ -192,3 +194,168 @@ test('Test tableManagerBase.updateItem', () => {
192
194
  tableManagerBase.updateItem(0, 'textHeight', 20);
193
195
  expect(tableManagerBase.getItemByIndex(0).textHeight).toBe(20);
194
196
  });
197
+
198
+ describe('tableManagerBase deleteItem return contract', () => {
199
+ beforeEach(() => {
200
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'Standard' }];
201
+ tableManagerBase.indelibleItems = ['Standard'];
202
+ });
203
+
204
+ test('returns the spliced array containing the deleted item', () => {
205
+ const deleted = tableManagerBase.deleteItem(0);
206
+
207
+ expect(Array.isArray(deleted)).toBe(true);
208
+ expect(deleted).toEqual([{ name: 'itemOne' }]);
209
+ expect(tableManagerBase.itemExists('itemOne')).toBe(false);
210
+ });
211
+
212
+ test('returns undefined for an invalid index', () => {
213
+ expect(tableManagerBase.deleteItem(10)).toBeUndefined();
214
+ expect(tableManagerBase.itemCount()).toBe(2);
215
+ });
216
+
217
+ test('returns undefined and keeps indelible items, ignoring case', () => {
218
+ tableManagerBase.indelibleItems = ['STANDARD'];
219
+
220
+ expect(tableManagerBase.deleteItem(1, false)).toBeUndefined();
221
+ expect(tableManagerBase.itemExists('Standard')).toBe(true);
222
+ });
223
+
224
+ test('invalidates the name lookup cache', () => {
225
+ expect(tableManagerBase.getItemByName('itemOne')).toBeDefined();
226
+
227
+ tableManagerBase.deleteItem(0);
228
+
229
+ expect(tableManagerBase.getItemByName('itemOne')).toBeUndefined();
230
+ });
231
+ });
232
+
233
+ describe('tableManagerBase lookups', () => {
234
+ beforeEach(() => {
235
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'itemTwo' }];
236
+ });
237
+
238
+ test('getItemIndex is case-insensitive and returns -1 when missing', () => {
239
+ expect(tableManagerBase.getItemIndex('ITEMTWO')).toBe(1);
240
+ expect(tableManagerBase.getItemIndex('missing')).toBe(-1);
241
+ });
242
+
243
+ test('itemExists and getItemByName are case-insensitive', () => {
244
+ expect(tableManagerBase.itemExists('ITEMONE')).toBe(true);
245
+ expect(tableManagerBase.getItemByName('itemone').name).toBe('itemOne');
246
+ });
247
+
248
+ test('getItemByName cache is refreshed after addItem and rename', () => {
249
+ expect(tableManagerBase.getItemByName('itemOne')).toBeDefined();
250
+
251
+ tableManagerBase.addItem({ name: 'itemThree' });
252
+ expect(tableManagerBase.getItemByName('itemThree')).toBeDefined();
253
+
254
+ tableManagerBase.renameItem(0, 'itemRenamed');
255
+ expect(tableManagerBase.getItemByName('itemOne')).toBeUndefined();
256
+ expect(tableManagerBase.getItemByName('itemRenamed')).toBeDefined();
257
+ });
258
+
259
+ test('clearItems removes all items', () => {
260
+ tableManagerBase.clearItems();
261
+
262
+ expect(tableManagerBase.itemCount()).toBe(0);
263
+ expect(tableManagerBase.getItemByName('itemOne')).toBeUndefined();
264
+ });
265
+
266
+ test('addItem returns the existing item when not overwriting', () => {
267
+ const existing = tableManagerBase.getItemByName('itemOne');
268
+
269
+ expect(tableManagerBase.addItem({ name: 'ITEMONE' })).toBe(existing);
270
+ });
271
+ });
272
+
273
+ describe('tableManagerBase renameItem', () => {
274
+ beforeEach(() => {
275
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'itemTwo' }, { name: 'Standard' }];
276
+ tableManagerBase.indelibleItems = ['Standard'];
277
+ });
278
+
279
+ test('returns the new name', () => {
280
+ expect(tableManagerBase.renameItem(0, 'renamed')).toBe('renamed');
281
+ });
282
+
283
+ test('returns undefined when the name is unchanged apart from case', () => {
284
+ expect(tableManagerBase.renameItem(0, 'ITEMONE')).toBeUndefined();
285
+ expect(tableManagerBase.getItemByIndex(0).name).toBe('itemOne');
286
+ });
287
+
288
+ test('cannot rename an indelible item', () => {
289
+ expect(tableManagerBase.renameItem(2, 'other')).toBeUndefined();
290
+ expect(tableManagerBase.getItemByIndex(2).name).toBe('Standard');
291
+ });
292
+
293
+ test('makes a clashing name unique', () => {
294
+ expect(tableManagerBase.renameItem(0, 'itemTwo')).toBe('itemTwo_1');
295
+ });
296
+ });
297
+
298
+ describe('tableManagerBase scene references', () => {
299
+ const originalProperty = tableManagerBase.itemProperty;
300
+
301
+ const addLine = (layer) => {
302
+ const line = DesignCore.Scene.addEntity('Line', { points: [new Point(), new Point(10, 10)], layer: layer });
303
+ return line;
304
+ };
305
+
306
+ beforeEach(() => {
307
+ tableManagerBase.itemProperty = 'layer';
308
+ tableManagerBase.items = [{ name: 'itemOne' }, { name: 'itemTwo' }];
309
+ tableManagerBase.indelibleItems = [];
310
+ while (DesignCore.Scene.entities.count() > 0) {
311
+ DesignCore.Scene.entities.remove(0);
312
+ }
313
+ });
314
+
315
+ afterAll(() => {
316
+ tableManagerBase.itemProperty = originalProperty;
317
+ });
318
+
319
+ test('updateSceneItem renames references through the property API', () => {
320
+ addLine('itemOne');
321
+ addLine('itemTwo');
322
+
323
+ tableManagerBase.updateSceneItem('itemOne', 'renamed');
324
+
325
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('renamed');
326
+ expect(DesignCore.Scene.entities.get(1).getProperty('layer')).toBe('itemTwo');
327
+ });
328
+
329
+ test('updateSceneItem matches names case-insensitively', () => {
330
+ addLine('ITEMONE');
331
+
332
+ tableManagerBase.updateSceneItem('itemOne', 'renamed');
333
+
334
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('renamed');
335
+ });
336
+
337
+ test('updateSceneItem ignores an undefined name', () => {
338
+ addLine('itemOne');
339
+
340
+ tableManagerBase.updateSceneItem(undefined, 'renamed');
341
+
342
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('itemOne');
343
+ });
344
+
345
+ test('renameItem updates dependent entities', () => {
346
+ addLine('itemOne');
347
+
348
+ tableManagerBase.renameItem(0, 'renamed');
349
+
350
+ expect(DesignCore.Scene.entities.get(0).getProperty('layer')).toBe('renamed');
351
+ });
352
+
353
+ test('purge removes only unused items', () => {
354
+ addLine('itemOne');
355
+
356
+ tableManagerBase.purge();
357
+
358
+ expect(tableManagerBase.itemExists('itemOne')).toBe(true);
359
+ expect(tableManagerBase.itemExists('itemTwo')).toBe(false);
360
+ });
361
+ });
@@ -0,0 +1,108 @@
1
+ import { Tool } from '../../core/tools/tool.js';
2
+ import { GripStretch } from '../../core/tools/gripStretch.js';
3
+ import { Core } from '../../core/core/core.js';
4
+ import { DesignCore } from '../../core/designCore.js';
5
+ import { DimensionBase } from '../../core/dimensions/dimensionBase.js';
6
+ import { Point } from '../../core/entities/point.js';
7
+ import { jest } from '@jest/globals';
8
+
9
+ const core = new Core();
10
+
11
+ test('GripStretch is a Tool with type GripStretch', () => {
12
+ const gripStretch = new GripStretch();
13
+ expect(gripStretch).toBeInstanceOf(Tool);
14
+ expect(gripStretch.type).toBe('GripStretch');
15
+ });
16
+
17
+ test('GripStretch is not registered as a typeable command', () => {
18
+ expect(typeof GripStretch.register).toBe('undefined');
19
+ });
20
+
21
+ test('GripStretch.action commits the dropped grip position to the real entity', () => {
22
+ core.scene.clear();
23
+ const dim = new DimensionBase({});
24
+ dim.points = [
25
+ Object.assign(new Point(0, 0), { sequence: 13 }),
26
+ Object.assign(new Point(10, 10), { sequence: 11 }),
27
+ ];
28
+ DesignCore.Scene.entities.add(dim);
29
+ core.scene.selectionManager.addToSelectionSet(0);
30
+
31
+ const grip = dim.getGrips()[0];
32
+ const gripStretch = new GripStretch(0, grip);
33
+ gripStretch.points.push(new Point(50, 60));
34
+
35
+ gripStretch.action();
36
+
37
+ const entity = core.scene.entities.get(0);
38
+ const pt11 = entity.getPointBySequence(entity.points, 11);
39
+ expect(pt11.x).toBe(50);
40
+ expect(pt11.y).toBe(60);
41
+ expect(entity.dimType.hasUserPositionedText()).toBe(true);
42
+ });
43
+
44
+ test('GripStretch.preview live-updates the selected-entity preview clone, not the real entity', () => {
45
+ core.scene.clear();
46
+ const dim = new DimensionBase({});
47
+ dim.points = [
48
+ Object.assign(new Point(0, 0), { sequence: 13 }),
49
+ Object.assign(new Point(10, 10), { sequence: 11 }),
50
+ ];
51
+ DesignCore.Scene.entities.add(dim);
52
+ core.scene.selectionManager.addToSelectionSet(0);
53
+
54
+ const grip = dim.getGrips()[0];
55
+ const gripStretch = new GripStretch(0, grip);
56
+ jest.spyOn(DesignCore.Mouse, 'pointOnScene').mockReturnValue(new Point(50, 60));
57
+
58
+ gripStretch.preview();
59
+
60
+ const previewEntity = core.scene.selectionManager.selectedEntities[0];
61
+ const previewPt11 = previewEntity.getPointBySequence(previewEntity.points, 11);
62
+ expect(previewPt11.x).toBe(50);
63
+ expect(previewPt11.y).toBe(60);
64
+
65
+ const realPt11 = core.scene.entities.get(0).getPointBySequence(core.scene.entities.get(0).points, 11);
66
+ expect(realPt11.x).toBe(10);
67
+ expect(realPt11.y).toBe(10);
68
+
69
+ jest.restoreAllMocks();
70
+ });
71
+
72
+ test('GripStretch.execute requests the stretch point then commits, keeping the entity selected', async () => {
73
+ core.scene.clear();
74
+ const dim = new DimensionBase({});
75
+ dim.points = [
76
+ Object.assign(new Point(0, 0), { sequence: 13 }),
77
+ Object.assign(new Point(10, 10), { sequence: 11 }),
78
+ ];
79
+ DesignCore.Scene.entities.add(dim);
80
+ core.scene.selectionManager.addToSelectionSet(0);
81
+
82
+ const grip = dim.getGrips()[0];
83
+ const gripStretch = new GripStretch(0, grip);
84
+
85
+ const origInputManager = DesignCore.Scene.inputManager;
86
+ DesignCore.Scene.inputManager = {
87
+ requestInput: async (op) => {
88
+ expect(op.promptMessage).toBe('Specify stretch point');
89
+ return new Point(50, 60);
90
+ },
91
+ actionCommand: () => gripStretch.action(),
92
+ refreshPreview: () => {},
93
+ };
94
+
95
+ try {
96
+ await gripStretch.execute();
97
+ } finally {
98
+ DesignCore.Scene.inputManager = origInputManager;
99
+ }
100
+
101
+ const entity = core.scene.entities.get(0);
102
+ const pt11 = entity.getPointBySequence(entity.points, 11);
103
+ expect(pt11.x).toBe(50);
104
+ expect(pt11.y).toBe(60);
105
+ // grip editing must leave the entity selected afterwards, unlike most commands
106
+ expect(core.scene.selectionManager.selectionSet.selectionSet).toContain(0);
107
+ });
108
+
@@ -269,6 +269,7 @@ test('Test Offset.action - offset circle outward', () => {
269
269
  expect(newCircle.points[0].y).toBeCloseTo(0);
270
270
  // New radius should be 15
271
271
  expect(newCircle.getProperty('radius')).toBeCloseTo(15);
272
+ expect(newCircle.type).toBe('Circle'); // not an Arc
272
273
  });
273
274
 
274
275
  test('Test Offset.action - offset circle inward', () => {
@@ -288,6 +289,7 @@ test('Test Offset.action - offset circle inward', () => {
288
289
  expect(newCircle.points[0].x).toBeCloseTo(0);
289
290
  expect(newCircle.points[0].y).toBeCloseTo(0);
290
291
  expect(newCircle.getProperty('radius')).toBeCloseTo(7);
292
+ expect(newCircle.type).toBe('Circle'); // not an Arc
291
293
  });
292
294
 
293
295
  test('Test Offset.action - offset circle inward returns null when radius would be zero', () => {
@@ -46,7 +46,9 @@ test('Test purge.action', () => {
46
46
  expect(dimStyleManager.itemCount()).toBe(4);
47
47
  purge.option = 'Dimstyles';
48
48
  purge.action();
49
- expect(dimStyleManager.itemCount()).toBe(1);
49
+ // STANDARD is indelible and ISO-25 is the current style
50
+ expect(dimStyleManager.itemCount()).toBe(2);
51
+ expect(dimStyleManager.itemExists('ISO-25')).toBe(true);
50
52
 
51
53
  expect(layerManager.itemCount()).toBe(4);
52
54
  purge.option = 'Layers';
@@ -0,0 +1,25 @@
1
+ import { Core } from '../../core/core/core.js';
2
+ import { DesignCore } from '../../core/designCore.js';
3
+ import { Tool } from '../../core/tools/tool.js';
4
+ import { jest } from '@jest/globals';
5
+
6
+ new Core();
7
+
8
+ afterEach(() => {
9
+ jest.restoreAllMocks();
10
+ });
11
+
12
+ test('Tool.cancel default delegates to inputManager.reset()', () => {
13
+ const tool = new Tool();
14
+ const resetSpy = jest.spyOn(DesignCore.Scene.inputManager, 'reset').mockImplementation(() => {});
15
+
16
+ tool.cancel();
17
+
18
+ expect(resetSpy).toHaveBeenCalledTimes(1);
19
+ });
20
+
21
+ test('Tool.preview and Tool.action are no-ops by default', () => {
22
+ const tool = new Tool();
23
+ expect(() => tool.preview()).not.toThrow();
24
+ expect(() => tool.action()).not.toThrow();
25
+ });