@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.
- package/.eslintrc.json +4 -0
- package/.github/workflows/DesignCoreQA.yml +3 -0
- package/TODO.md +49 -6
- package/core/core/core.js +0 -3
- package/core/designCore.js +1 -1
- package/core/dimensions/alignedDimension.js +12 -11
- package/core/dimensions/angularDimension.js +38 -37
- package/core/dimensions/{baseCircularDimension.js → circularDimensionBase.js} +22 -21
- package/core/dimensions/diametricDimension.js +14 -13
- package/core/dimensions/dimension.js +62 -55
- package/core/dimensions/{baseDimension.js → dimensionBase.js} +73 -107
- package/core/dimensions/{baseLinearDimension.js → linearDimensionBase.js} +15 -14
- package/core/dimensions/radialDimension.js +12 -11
- package/core/dimensions/rotatedDimension.js +20 -19
- package/core/entities/arc.js +123 -156
- package/core/entities/arctext.js +222 -184
- package/core/entities/circle.js +77 -99
- package/core/entities/entity.js +114 -93
- package/core/entities/hatch.js +346 -273
- package/core/entities/insert.js +68 -83
- package/core/entities/line.js +21 -142
- package/core/entities/lwpolyline.js +16 -11
- package/core/entities/point.js +19 -19
- package/core/entities/polyline.js +7 -7
- package/core/entities/{basePolyline.js → polylineBase.js} +47 -299
- package/core/entities/rectangle.js +3 -1
- package/core/entities/solid.js +6 -15
- package/core/entities/text.js +190 -201
- package/core/lib/auxiliary/rubberBand.js +32 -0
- package/core/lib/auxiliary/selectionWindow.js +63 -0
- package/core/lib/auxiliary/snapPoint.js +113 -0
- package/core/lib/auxiliary/trackingLine.js +76 -0
- package/core/lib/boundingBox.js +11 -11
- package/core/lib/canvas.js +232 -168
- package/core/lib/clipboard.js +9 -3
- package/core/lib/colour.js +16 -0
- package/core/lib/commandLine.js +1 -6
- package/core/lib/commandManager.js +18 -8
- package/core/lib/constants.js +28 -0
- package/core/lib/dxf/dxf.js +15 -14
- package/core/lib/dxf/dxfIterator.js +2 -2
- package/core/lib/entityManager.js +33 -26
- package/core/lib/inputManager.js +36 -12
- package/core/lib/intersect.js +25 -0
- package/core/lib/matrix.js +1 -1
- package/core/lib/patterns.js +9 -2
- package/core/lib/plotOptions.js +67 -0
- package/core/lib/polylineUtils.js +75 -0
- package/core/lib/renderers/cairoRenderer.js +371 -0
- package/core/lib/renderers/canvasRenderer.js +315 -0
- package/core/lib/renderers/pdfRenderer.js +366 -0
- package/core/lib/renderers/rendererBase.js +284 -0
- package/core/lib/renderers/svgRenderer.js +343 -0
- package/core/lib/scene.js +28 -6
- package/core/lib/selectionManager.js +20 -20
- package/core/lib/settings.js +4 -2
- package/core/lib/snapping.js +4 -191
- package/core/lib/stateManager.js +30 -1
- package/core/lib/utils.js +26 -7
- package/core/properties/entityProperties.js +104 -0
- package/core/properties/property.js +106 -2
- package/core/properties/propertyManager.js +60 -44
- package/core/tables/block.js +50 -81
- package/core/tables/dimStyle.js +5 -5
- package/core/tables/endblk.js +1 -1
- package/core/tables/layer.js +8 -0
- package/core/tables/ltypeManager.js +1 -1
- package/core/tables/styleManagerBase.js +3 -2
- package/core/tables/tableManagerBase.js +8 -3
- package/core/tables/vport.js +2 -2
- package/core/tools/chamfer.js +117 -143
- package/core/tools/chamferFilletBase.js +138 -37
- package/core/tools/copy.js +11 -11
- package/core/tools/copybase.js +3 -3
- package/core/tools/copyclip.js +3 -3
- package/core/tools/cornerEntity.js +2 -2
- package/core/tools/cutclip.js +3 -3
- package/core/tools/explode.js +7 -7
- package/core/tools/extend.js +138 -25
- package/core/tools/fillet.js +92 -189
- package/core/tools/matchProp.js +3 -12
- package/core/tools/mirror.js +15 -14
- package/core/tools/move.js +8 -8
- package/core/tools/offset.js +31 -33
- package/core/tools/pasteclip.js +1 -1
- package/core/tools/purge.js +10 -10
- package/core/tools/rotate.js +19 -16
- package/core/tools/scale.js +7 -7
- package/core/tools/trim.js +271 -29
- package/core/tools/zoom.js +1 -1
- package/cspell.json +321 -0
- package/jest.config.js +6 -0
- package/package.json +5 -3
- package/test/dimensions/alignedDimension.test.js +17 -10
- package/test/dimensions/angularDimension.test.js +13 -13
- package/test/dimensions/diametricDimension.test.js +15 -15
- package/test/dimensions/dimension.test.js +12 -12
- package/test/dimensions/{baseDimension.test.js → dimensionBase.test.js} +73 -12
- package/test/dimensions/linearDimensionBase.test.js +62 -0
- package/test/dimensions/radialDimension.test.js +15 -15
- package/test/dimensions/rotatedDimension.test.js +18 -11
- package/test/entities/arc.test.js +83 -86
- package/test/entities/arctext.test.js +184 -57
- package/test/entities/circle.test.js +68 -96
- package/test/entities/entity.test.js +42 -8
- package/test/entities/hatch.test.js +569 -96
- package/test/entities/insert.test.js +99 -4
- package/test/entities/line.test.js +15 -41
- package/test/entities/lwpolyline.test.js +104 -0
- package/test/entities/point.test.js +3 -3
- package/test/entities/polyline.test.js +11 -0
- package/test/entities/{basePolyline.test.js → polylineBase.test.js} +192 -57
- package/test/entities/rectangle.test.js +11 -0
- package/test/entities/solid.test.js +1 -1
- package/test/entities/text.test.js +302 -77
- package/test/integration/exportIntegration.reference.dxf +9 -9
- package/test/integration/exportIntegration.test.js +5 -5
- package/test/integration/intersectionIntegration.test.js +4 -5
- package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
- package/test/integration/pdfRendererIntegration.test.js +61 -0
- package/test/integration/svgRendererIntegration.reference.svg +3343 -0
- package/test/integration/svgRendererIntegration.test.js +61 -0
- package/test/integration/userInputIntegration.reference.dxf +1165 -105
- package/test/integration/userInputIntegration.test.js +239 -132
- package/test/lib/boundingBox.test.js +3 -3
- package/test/lib/canvas.test.js +744 -125
- package/test/lib/constants.test.js +27 -0
- package/test/lib/entityManager.test.js +4 -4
- package/test/lib/inputManager.test.js +81 -20
- package/test/lib/intersect.test.js +83 -0
- package/test/lib/plotOptions.test.js +179 -0
- package/test/lib/rubberBand.test.js +84 -0
- package/test/lib/scene.test.js +9 -9
- package/test/lib/selectionManager.test.js +3 -3
- package/test/lib/settings.test.js +5 -6
- package/test/lib/snapping.test.js +72 -130
- package/test/lib/stateManager.test.js +7 -7
- package/test/lib/utils.test.js +36 -2
- package/test/properties/entityProperties.test.js +132 -0
- package/test/properties/property.test.js +53 -0
- package/test/properties/propertyManager.test.js +25 -25
- package/test/renderers/canvasRenderer.test.js +317 -0
- package/test/renderers/pdfRenderer.test.js +271 -0
- package/test/renderers/rendererBase.test.js +213 -0
- package/test/renderers/svgRenderer.test.js +326 -0
- package/test/tables/block.test.js +92 -13
- package/test/tables/endblk.test.js +2 -2
- package/test/tables/layer.test.js +30 -0
- package/test/tables/layerManager.test.js +2 -2
- package/test/test-helpers/hatchGridRenderer.js +180 -0
- package/test/test-helpers/test-helpers.js +158 -6
- package/test/tools/chamfer.test.js +273 -51
- package/test/tools/chamferFilletBase.test.js +125 -36
- package/test/tools/copy.test.js +8 -8
- package/test/tools/copybase.test.js +2 -2
- package/test/tools/copyclip.test.js +2 -2
- package/test/tools/cornerEntity.test.js +8 -8
- package/test/tools/cutclip.test.js +2 -2
- package/test/tools/erase.test.js +6 -6
- package/test/tools/explode.test.js +5 -5
- package/test/tools/extend.test.js +373 -42
- package/test/tools/fillet.test.js +230 -49
- package/test/tools/matchProp.test.js +11 -11
- package/test/tools/mirror.test.js +39 -39
- package/test/tools/move.test.js +7 -7
- package/test/tools/offset.test.js +222 -55
- package/test/tools/pasteclip.test.js +5 -5
- package/test/tools/purge.test.js +4 -4
- package/test/tools/rotate.test.js +39 -9
- package/test/tools/scale.test.js +33 -33
- package/test/tools/trim.test.js +579 -53
- package/test/tools/zoom.test.js +12 -12
- package/core/lib/selectionWindow.js +0 -83
|
@@ -2,6 +2,8 @@ import { Core } from '../../core/core/core.js';
|
|
|
2
2
|
import { Point } from '../../core/entities/point.js';
|
|
3
3
|
import { Extend } from '../../core/tools/extend.js';
|
|
4
4
|
import { SingleSelection } from '../../core/lib/selectionManager.js';
|
|
5
|
+
import { Strings } from '../../core/lib/strings.js';
|
|
6
|
+
import { DesignCore } from '../../core/designCore.js';
|
|
5
7
|
import { expect, jest } from '@jest/globals';
|
|
6
8
|
import { withMockInput } from '../test-helpers/test-helpers.js';
|
|
7
9
|
|
|
@@ -22,9 +24,9 @@ test.each(inputScenarios)('Trim.execute handles $desc', async (scenario) => {
|
|
|
22
24
|
// clear all scene entities
|
|
23
25
|
core.scene.clear();
|
|
24
26
|
// create line
|
|
25
|
-
core.scene.
|
|
27
|
+
core.scene.addEntity('Line', { points: [new Point(), new Point(10, 0)] });
|
|
26
28
|
// create circle
|
|
27
|
-
core.scene.
|
|
29
|
+
core.scene.addEntity('Circle', { points: [new Point(), new Point(10, 0)] });
|
|
28
30
|
// select line
|
|
29
31
|
core.scene.selectionManager.selectionSet.selectionSet.push(0);
|
|
30
32
|
|
|
@@ -33,7 +35,7 @@ test.each(inputScenarios)('Trim.execute handles $desc', async (scenario) => {
|
|
|
33
35
|
await extend.execute();
|
|
34
36
|
|
|
35
37
|
expect(extend.selectedBoundaryItems[0]).toEqual(core.scene.entities.get(0));
|
|
36
|
-
expect(extend.
|
|
38
|
+
expect(extend.selectedEntity).toBe(core.scene.entities.get(1));
|
|
37
39
|
expect(actionSpy).toHaveBeenCalled();
|
|
38
40
|
}, { extraMethods: { actionCommand: () => actionSpy() } });
|
|
39
41
|
});
|
|
@@ -61,12 +63,12 @@ test('Test Extend.action', () => {
|
|
|
61
63
|
// clear all scene entities
|
|
62
64
|
core.scene.clear();
|
|
63
65
|
// Add items to scene
|
|
64
|
-
core.scene.
|
|
65
|
-
core.scene.
|
|
66
|
+
core.scene.addEntity('Line', { points: [lineOneStart, lineOneEnd] });
|
|
67
|
+
core.scene.addEntity('Line', { points: [lineTwoStart, lineTwoEnd] });
|
|
66
68
|
// Select boundary item
|
|
67
69
|
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
68
70
|
// select item to trim
|
|
69
|
-
extend.
|
|
71
|
+
extend.selectedEntity = core.scene.entities.get(1);
|
|
70
72
|
// set mouse location - required for Extend
|
|
71
73
|
core.mouse.setPosFromScenePoint(new Point(40, 50));
|
|
72
74
|
// Perform Extend
|
|
@@ -93,12 +95,12 @@ test('Test Extend.action', () => {
|
|
|
93
95
|
// clear scene items
|
|
94
96
|
core.scene.clear();
|
|
95
97
|
// Add items to scene
|
|
96
|
-
core.scene.
|
|
97
|
-
core.scene.
|
|
98
|
+
core.scene.addEntity('Line', { points: [lineTwoStart, lineTwoEnd] });
|
|
99
|
+
core.scene.addEntity('Line', { points: [lineThreeStart, lineThreeEnd] });
|
|
98
100
|
// Select boundary item
|
|
99
101
|
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
100
102
|
// select item to extend
|
|
101
|
-
extend.
|
|
103
|
+
extend.selectedEntity = core.scene.entities.get(1);
|
|
102
104
|
// set mouse location - required for Extend
|
|
103
105
|
core.mouse.setPosFromScenePoint(new Point(0, 20));
|
|
104
106
|
// Perform Extend
|
|
@@ -124,12 +126,12 @@ test('Test Extend.action', () => {
|
|
|
124
126
|
// clear scene items
|
|
125
127
|
core.scene.clear();
|
|
126
128
|
// Add items to scene
|
|
127
|
-
core.scene.
|
|
128
|
-
core.scene.
|
|
129
|
+
core.scene.addEntity('Line', { points: [lineOneStart, lineOneEnd] });
|
|
130
|
+
core.scene.addEntity('Line', { points: [crossingLineStart, crossingLineEnd] });
|
|
129
131
|
// Select boundary item
|
|
130
132
|
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
131
133
|
// select item to extend
|
|
132
|
-
extend.
|
|
134
|
+
extend.selectedEntity = core.scene.entities.get(1);
|
|
133
135
|
// set mouse location - required for Extend
|
|
134
136
|
core.mouse.setPosFromScenePoint(new Point(35, 35));
|
|
135
137
|
// Perform Extend
|
|
@@ -156,8 +158,8 @@ test('Test Extend.action line - endpoint coincident with intersection', () => {
|
|
|
156
158
|
const extend = new Extend();
|
|
157
159
|
core.scene.clear();
|
|
158
160
|
|
|
159
|
-
core.scene.
|
|
160
|
-
core.scene.
|
|
161
|
+
core.scene.addEntity('Line', { points: [new Point(0, 50), new Point(25, 50)] });
|
|
162
|
+
core.scene.addEntity('Lwpolyline', { points: [
|
|
161
163
|
new Point(50, 0), new Point(50, 100), new Point(100, 100),
|
|
162
164
|
new Point(100, 0), new Point(150, 0), new Point(150, 100),
|
|
163
165
|
new Point(200, 100), new Point(200, 0),
|
|
@@ -169,7 +171,7 @@ test('Test Extend.action line - endpoint coincident with intersection', () => {
|
|
|
169
171
|
const expected = [50, 100, 150, 200];
|
|
170
172
|
for (const ex of expected) {
|
|
171
173
|
extend.selectedBoundaryItems = [boundary];
|
|
172
|
-
extend.
|
|
174
|
+
extend.selectedEntity = line;
|
|
173
175
|
core.mouse.setPosFromScenePoint(new Point(line.points[1].x - 5, 50));
|
|
174
176
|
extend.action();
|
|
175
177
|
expect(line.points[1].x).toBe(ex);
|
|
@@ -186,11 +188,11 @@ test('Test Extend.action polyline - endpoint coincident with intersection', () =
|
|
|
186
188
|
const extend = new Extend();
|
|
187
189
|
core.scene.clear();
|
|
188
190
|
|
|
189
|
-
core.scene.
|
|
190
|
-
core.scene.
|
|
191
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(50, 50)] });
|
|
192
|
+
core.scene.addEntity('Line', { points: [new Point(100, 0), new Point(100, 100)] });
|
|
191
193
|
|
|
192
194
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
193
|
-
extend.
|
|
195
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
194
196
|
core.mouse.setPosFromScenePoint(new Point(45, 45));
|
|
195
197
|
extend.action();
|
|
196
198
|
|
|
@@ -208,11 +210,11 @@ test('Test Extend.action polyline - 2-point polyline extend start', () => {
|
|
|
208
210
|
const extend = new Extend();
|
|
209
211
|
core.scene.clear();
|
|
210
212
|
|
|
211
|
-
core.scene.
|
|
212
|
-
core.scene.
|
|
213
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(50, 0), new Point(100, 0)] });
|
|
214
|
+
core.scene.addEntity('Line', { points: [new Point(0, -50), new Point(0, 50)] });
|
|
213
215
|
|
|
214
216
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
215
|
-
extend.
|
|
217
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
216
218
|
core.mouse.setPosFromScenePoint(new Point(55, 0));
|
|
217
219
|
extend.action();
|
|
218
220
|
|
|
@@ -232,11 +234,11 @@ test('Test Extend.action polyline - extend end segment', () => {
|
|
|
232
234
|
const extend = new Extend();
|
|
233
235
|
core.scene.clear();
|
|
234
236
|
|
|
235
|
-
core.scene.
|
|
236
|
-
core.scene.
|
|
237
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(50, 0), new Point(75, 0)] });
|
|
238
|
+
core.scene.addEntity('Line', { points: [new Point(100, -50), new Point(100, 50)] });
|
|
237
239
|
|
|
238
240
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
239
|
-
extend.
|
|
241
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
240
242
|
core.mouse.setPosFromScenePoint(new Point(80, 0));
|
|
241
243
|
extend.action();
|
|
242
244
|
|
|
@@ -259,11 +261,11 @@ test('Test Extend.action polyline - extend start segment', () => {
|
|
|
259
261
|
const extend = new Extend();
|
|
260
262
|
core.scene.clear();
|
|
261
263
|
|
|
262
|
-
core.scene.
|
|
263
|
-
core.scene.
|
|
264
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(25, 0), new Point(50, 0), new Point(100, 0)] });
|
|
265
|
+
core.scene.addEntity('Line', { points: [new Point(0, -50), new Point(0, 50)] });
|
|
264
266
|
|
|
265
267
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
266
|
-
extend.
|
|
268
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
267
269
|
core.mouse.setPosFromScenePoint(new Point(20, 0));
|
|
268
270
|
extend.action();
|
|
269
271
|
|
|
@@ -286,11 +288,11 @@ test('Test Extend.action polyline - reject arc end segment', () => {
|
|
|
286
288
|
const extend = new Extend();
|
|
287
289
|
core.scene.clear();
|
|
288
290
|
|
|
289
|
-
core.scene.
|
|
290
|
-
core.scene.
|
|
291
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(50, 0, 1), new Point(100, 0)] });
|
|
292
|
+
core.scene.addEntity('Line', { points: [new Point(125, -50), new Point(125, 50)] });
|
|
291
293
|
|
|
292
294
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
293
|
-
extend.
|
|
295
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
294
296
|
core.mouse.setPosFromScenePoint(new Point(100, 0));
|
|
295
297
|
extend.action();
|
|
296
298
|
|
|
@@ -314,11 +316,11 @@ test('Test Extend.action polyline - reject intersection behind endpoint', () =>
|
|
|
314
316
|
const extend = new Extend();
|
|
315
317
|
core.scene.clear();
|
|
316
318
|
|
|
317
|
-
core.scene.
|
|
318
|
-
core.scene.
|
|
319
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(50, 0), new Point(75, 0), new Point(100, 0)] });
|
|
320
|
+
core.scene.addEntity('Line', { points: [new Point(25, -50), new Point(25, 50)] });
|
|
319
321
|
|
|
320
322
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
321
|
-
extend.
|
|
323
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
322
324
|
core.mouse.setPosFromScenePoint(new Point(95, 0));
|
|
323
325
|
extend.action();
|
|
324
326
|
|
|
@@ -337,11 +339,11 @@ test('Test Extend.action polyline - reject shortening intersection', () => {
|
|
|
337
339
|
const extend = new Extend();
|
|
338
340
|
core.scene.clear();
|
|
339
341
|
|
|
340
|
-
core.scene.
|
|
341
|
-
core.scene.
|
|
342
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(50, 0), new Point(100, 0)] });
|
|
343
|
+
core.scene.addEntity('Line', { points: [new Point(75, -50), new Point(75, 50)] });
|
|
342
344
|
|
|
343
345
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
344
|
-
extend.
|
|
346
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
345
347
|
core.mouse.setPosFromScenePoint(new Point(95, 0));
|
|
346
348
|
extend.action();
|
|
347
349
|
|
|
@@ -360,11 +362,11 @@ test('Test Extend.action polyline - extend with arc start segment', () => {
|
|
|
360
362
|
const extend = new Extend();
|
|
361
363
|
core.scene.clear();
|
|
362
364
|
|
|
363
|
-
core.scene.
|
|
364
|
-
core.scene.
|
|
365
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0, 1), new Point(50, 0), new Point(100, 0)] });
|
|
366
|
+
core.scene.addEntity('Line', { points: [new Point(125, -50), new Point(125, 50)] });
|
|
365
367
|
|
|
366
368
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
367
|
-
extend.
|
|
369
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
368
370
|
core.mouse.setPosFromScenePoint(new Point(110, 0));
|
|
369
371
|
extend.action();
|
|
370
372
|
|
|
@@ -381,11 +383,11 @@ test('Test Extend.action polyline - no intersections', () => {
|
|
|
381
383
|
const extend = new Extend();
|
|
382
384
|
core.scene.clear();
|
|
383
385
|
|
|
384
|
-
core.scene.
|
|
385
|
-
core.scene.
|
|
386
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(50, 0)] });
|
|
387
|
+
core.scene.addEntity('Line', { points: [new Point(0, 50), new Point(100, 50)] });
|
|
386
388
|
|
|
387
389
|
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
388
|
-
extend.
|
|
390
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
389
391
|
core.mouse.setPosFromScenePoint(new Point(45, 0));
|
|
390
392
|
extend.action();
|
|
391
393
|
|
|
@@ -393,3 +395,332 @@ test('Test Extend.action polyline - no intersections', () => {
|
|
|
393
395
|
expect(polyline.points[0].x).toBe(0);
|
|
394
396
|
expect(polyline.points[1].x).toBe(50);
|
|
395
397
|
});
|
|
398
|
+
|
|
399
|
+
test('Extend.register returns correct metadata', () => {
|
|
400
|
+
const reg = Extend.register();
|
|
401
|
+
expect(reg.command).toBe('Extend');
|
|
402
|
+
expect(reg.shortcut).toBe('EX');
|
|
403
|
+
expect(reg.type).toBe('Tool');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('Extend.preview does not throw', () => {
|
|
407
|
+
const extend = new Extend();
|
|
408
|
+
expect(() => extend.preview()).not.toThrow();
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test('Extend.preview returns early when no boundary items set', () => {
|
|
412
|
+
core.scene.clear();
|
|
413
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] });
|
|
414
|
+
DesignCore.Scene.previewEntities.clear();
|
|
415
|
+
|
|
416
|
+
const extend = new Extend();
|
|
417
|
+
// selectedBoundaryItems is empty by default
|
|
418
|
+
extend.preview();
|
|
419
|
+
|
|
420
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test('Extend.preview returns early when findClosestItem returns undefined', () => {
|
|
424
|
+
core.scene.clear();
|
|
425
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] });
|
|
426
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(50, 0)] });
|
|
427
|
+
DesignCore.Scene.previewEntities.clear();
|
|
428
|
+
|
|
429
|
+
const extend = new Extend();
|
|
430
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
431
|
+
|
|
432
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(undefined);
|
|
433
|
+
extend.preview();
|
|
434
|
+
findSpy.mockRestore();
|
|
435
|
+
|
|
436
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test('Extend.preview returns early when hovered entity does not implement extend', () => {
|
|
440
|
+
// Circle does not override Entity.extend() — preview must not emit a notification.
|
|
441
|
+
core.scene.clear();
|
|
442
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] }); // boundary
|
|
443
|
+
core.scene.addEntity('Circle', { points: [new Point(0, 0), new Point(5, 0)] }); // unsupported
|
|
444
|
+
DesignCore.Scene.previewEntities.clear();
|
|
445
|
+
|
|
446
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
447
|
+
const extend = new Extend();
|
|
448
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
449
|
+
|
|
450
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(1);
|
|
451
|
+
DesignCore.Mouse.pointOnScene = () => new Point(5, 0);
|
|
452
|
+
extend.preview();
|
|
453
|
+
findSpy.mockRestore();
|
|
454
|
+
notifySpy.mockRestore();
|
|
455
|
+
|
|
456
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
457
|
+
expect(notifySpy).not.toHaveBeenCalled();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('Extend.preview returns early when hovered entity has no valid extension', () => {
|
|
461
|
+
// Hovered line is parallel to the boundary — no intersection ahead of its endpoint.
|
|
462
|
+
core.scene.clear();
|
|
463
|
+
core.scene.addEntity('Line', { points: [new Point(0, 50), new Point(100, 50)] }); // boundary (horizontal)
|
|
464
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(50, 0)] }); // hovered (parallel, y=0)
|
|
465
|
+
DesignCore.Scene.previewEntities.clear();
|
|
466
|
+
|
|
467
|
+
const extend = new Extend();
|
|
468
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
469
|
+
|
|
470
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(1);
|
|
471
|
+
DesignCore.Mouse.pointOnScene = () => new Point(45, 0);
|
|
472
|
+
extend.preview();
|
|
473
|
+
findSpy.mockRestore();
|
|
474
|
+
|
|
475
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
test('Extend.preview populates previewEntities when entity can be extended', () => {
|
|
479
|
+
// Vertical boundary at x=100; short horizontal line from (0,0) to (50,0).
|
|
480
|
+
// Mouse near end (45,0) → end extends to (100,0).
|
|
481
|
+
core.scene.clear();
|
|
482
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] }); // boundary
|
|
483
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(50, 0)] }); // hovered
|
|
484
|
+
DesignCore.Scene.previewEntities.clear();
|
|
485
|
+
|
|
486
|
+
const extend = new Extend();
|
|
487
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
488
|
+
|
|
489
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(1);
|
|
490
|
+
DesignCore.Mouse.pointOnScene = () => new Point(45, 0);
|
|
491
|
+
extend.preview();
|
|
492
|
+
findSpy.mockRestore();
|
|
493
|
+
|
|
494
|
+
// Preview of the extended line should be added
|
|
495
|
+
expect(DesignCore.Scene.previewEntities.count()).toBeGreaterThanOrEqual(1);
|
|
496
|
+
// The previewed line should reach the boundary
|
|
497
|
+
const preview = DesignCore.Scene.previewEntities.get(DesignCore.Scene.previewEntities.count() - 1);
|
|
498
|
+
expect(preview.points[1].x).toBeCloseTo(100);
|
|
499
|
+
expect(preview.points[1].y).toBeCloseTo(0);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test('Extend.execute returns early when boundary input is undefined', async () => {
|
|
503
|
+
core.scene.clear();
|
|
504
|
+
core.scene.selectionManager.reset();
|
|
505
|
+
core.scene.addEntity('Line', { points: [new Point(), new Point(10, 0)] });
|
|
506
|
+
|
|
507
|
+
await withMockInput(core.scene, [undefined], async () => {
|
|
508
|
+
const extend = new Extend();
|
|
509
|
+
await extend.execute();
|
|
510
|
+
expect(extend.selectedBoundaryItems).toHaveLength(0);
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
test('Extend.execute exits selection loop when selection input is undefined', async () => {
|
|
515
|
+
core.scene.clear();
|
|
516
|
+
core.scene.selectionManager.reset();
|
|
517
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] });
|
|
518
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(50, 0)] });
|
|
519
|
+
core.scene.selectionManager.selectionSet.selectionSet.push(0);
|
|
520
|
+
|
|
521
|
+
const executeCommandSpy = jest.fn();
|
|
522
|
+
await withMockInput(core.scene, [undefined], async () => {
|
|
523
|
+
const extend = new Extend();
|
|
524
|
+
await extend.execute();
|
|
525
|
+
expect(extend.selectedBoundaryItems).toHaveLength(1);
|
|
526
|
+
expect(executeCommandSpy).toHaveBeenCalled();
|
|
527
|
+
}, { extraMethods: { executeCommand: () => executeCommandSpy() } });
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
test('Extend.execute calls actionCommand once per extend selection', async () => {
|
|
531
|
+
core.scene.clear();
|
|
532
|
+
core.scene.selectionManager.reset();
|
|
533
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] });
|
|
534
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(50, 0)] });
|
|
535
|
+
core.scene.addEntity('Line', { points: [new Point(0, 5), new Point(50, 5)] });
|
|
536
|
+
core.scene.selectionManager.selectionSet.selectionSet.push(0);
|
|
537
|
+
|
|
538
|
+
const actionSpy = jest.fn();
|
|
539
|
+
await withMockInput(
|
|
540
|
+
core.scene,
|
|
541
|
+
[new SingleSelection(1, new Point(45, 0)), new SingleSelection(2, new Point(45, 5)), undefined],
|
|
542
|
+
async () => {
|
|
543
|
+
const extend = new Extend();
|
|
544
|
+
await extend.execute();
|
|
545
|
+
expect(actionSpy).toHaveBeenCalledTimes(2);
|
|
546
|
+
},
|
|
547
|
+
{ extraMethods: { actionCommand: () => actionSpy() } },
|
|
548
|
+
);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
test('Extend.action does nothing when selectedEntity is null', () => {
|
|
552
|
+
core.scene.clear();
|
|
553
|
+
core.scene.addEntity('Line', { points: [new Point(), new Point(10, 0)] });
|
|
554
|
+
const extend = new Extend();
|
|
555
|
+
extend.selectedEntity = null;
|
|
556
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
557
|
+
|
|
558
|
+
expect(() => extend.action()).not.toThrow();
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
test('Extend.action does nothing when selectedBoundaryItems is empty', () => {
|
|
562
|
+
core.scene.clear();
|
|
563
|
+
core.scene.addEntity('Line', { points: [new Point(), new Point(10, 0)] });
|
|
564
|
+
const extend = new Extend();
|
|
565
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
566
|
+
extend.selectedBoundaryItems = [];
|
|
567
|
+
|
|
568
|
+
expect(() => extend.action()).not.toThrow();
|
|
569
|
+
expect(extend.selectedEntity).toBeNull();
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test('Extend.action notifies NOEXTEND when selected entity lacks fromPolylinePoints', () => {
|
|
573
|
+
// Use a stub entity that has toPolylinePoints but no fromPolylinePoints.
|
|
574
|
+
// action() must notify and not throw.
|
|
575
|
+
core.scene.clear();
|
|
576
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] }); // boundary
|
|
577
|
+
|
|
578
|
+
const extend = new Extend();
|
|
579
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
580
|
+
extend.selectedEntity = {
|
|
581
|
+
type: 'FakeEntity',
|
|
582
|
+
toPolylinePoints: () => [new Point(0, 0), new Point(50, 0)],
|
|
583
|
+
// deliberately no fromPolylinePoints
|
|
584
|
+
};
|
|
585
|
+
core.mouse.setPosFromScenePoint(new Point(45, 0));
|
|
586
|
+
|
|
587
|
+
const notifySpy = jest.spyOn(core, 'notify').mockImplementation(() => {});
|
|
588
|
+
extend.action();
|
|
589
|
+
|
|
590
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NOEXTEND));
|
|
591
|
+
expect(extend.selectedEntity).toBeNull();
|
|
592
|
+
notifySpy.mockRestore();
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
test('Extend.action notifies NOEXTEND when selected entity is a closed polyline', () => {
|
|
596
|
+
// Closed polylines have no open endpoints — extend must notify and leave the entity unchanged.
|
|
597
|
+
core.scene.clear();
|
|
598
|
+
core.scene.addEntity('Line', { points: [new Point(200, -10), new Point(200, 10)] }); // boundary
|
|
599
|
+
// Closed square: flag bit 1 set
|
|
600
|
+
core.scene.addEntity('Lwpolyline', {
|
|
601
|
+
points: [new Point(0, 0), new Point(100, 0), new Point(100, 100), new Point(0, 100)],
|
|
602
|
+
flags: 1,
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
const extend = new Extend();
|
|
606
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
607
|
+
extend.selectedEntity = core.scene.entities.get(1);
|
|
608
|
+
core.mouse.setPosFromScenePoint(new Point(95, 0));
|
|
609
|
+
|
|
610
|
+
const notifySpy = jest.spyOn(core, 'notify').mockImplementation(() => {});
|
|
611
|
+
extend.action();
|
|
612
|
+
|
|
613
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NOEXTEND));
|
|
614
|
+
expect(extend.selectedEntity).toBeNull();
|
|
615
|
+
notifySpy.mockRestore();
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
test('Extend.preview silently skips entity that lacks fromPolylinePoints', () => {
|
|
619
|
+
// Stub entity implements toPolylinePoints but not fromPolylinePoints.
|
|
620
|
+
// preview() must return early without calling notify.
|
|
621
|
+
core.scene.clear();
|
|
622
|
+
core.scene.addEntity('Line', { points: [new Point(100, -10), new Point(100, 10)] }); // boundary
|
|
623
|
+
DesignCore.Scene.previewEntities.clear();
|
|
624
|
+
|
|
625
|
+
const stubEntity = {
|
|
626
|
+
type: 'FakeEntity',
|
|
627
|
+
toPolylinePoints: () => [new Point(0, 0), new Point(50, 0)],
|
|
628
|
+
// deliberately no fromPolylinePoints
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
632
|
+
const extend = new Extend();
|
|
633
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
634
|
+
|
|
635
|
+
const savedPointOnScene = DesignCore.Mouse.pointOnScene;
|
|
636
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(0);
|
|
637
|
+
jest.spyOn(core.scene.entities, 'get').mockReturnValue(stubEntity);
|
|
638
|
+
DesignCore.Mouse.pointOnScene = () => new Point(45, 0);
|
|
639
|
+
extend.preview();
|
|
640
|
+
findSpy.mockRestore();
|
|
641
|
+
jest.restoreAllMocks();
|
|
642
|
+
DesignCore.Mouse.pointOnScene = savedPointOnScene;
|
|
643
|
+
|
|
644
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
645
|
+
expect(notifySpy).not.toHaveBeenCalled();
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
test('Extend.action notifies when no intersection found (parallel lines)', () => {
|
|
649
|
+
core.scene.clear();
|
|
650
|
+
// Two parallel horizontal lines — no intersection possible
|
|
651
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(100, 0)] });
|
|
652
|
+
core.scene.addEntity('Line', { points: [new Point(0, 50), new Point(50, 50)] });
|
|
653
|
+
|
|
654
|
+
const extend = new Extend();
|
|
655
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
656
|
+
extend.selectedEntity = core.scene.entities.get(1);
|
|
657
|
+
core.mouse.setPosFromScenePoint(new Point(40, 50));
|
|
658
|
+
|
|
659
|
+
const notifySpy = jest.spyOn(core, 'notify').mockImplementation(() => {});
|
|
660
|
+
extend.action();
|
|
661
|
+
|
|
662
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NOEXTEND));
|
|
663
|
+
expect(extend.selectedEntity).toBeNull();
|
|
664
|
+
notifySpy.mockRestore();
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
test('Extend.action notifies when boundary item equals selected item', () => {
|
|
668
|
+
core.scene.clear();
|
|
669
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(50, 0)] });
|
|
670
|
+
const entity = core.scene.entities.get(0);
|
|
671
|
+
|
|
672
|
+
const extend = new Extend();
|
|
673
|
+
extend.selectedBoundaryItems = [entity];
|
|
674
|
+
extend.selectedEntity = entity;
|
|
675
|
+
core.mouse.setPosFromScenePoint(new Point(40, 0));
|
|
676
|
+
|
|
677
|
+
const notifySpy = jest.spyOn(core, 'notify').mockImplementation(() => {});
|
|
678
|
+
extend.action();
|
|
679
|
+
|
|
680
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NOEXTEND));
|
|
681
|
+
notifySpy.mockRestore();
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
test('Extend.action extends the start of a line', () => {
|
|
685
|
+
// Line from (50,50) to (100,50) — horizontal
|
|
686
|
+
// Boundary: vertical line at x=0
|
|
687
|
+
// Mouse near the start (50,50) → extend start back to x=0
|
|
688
|
+
core.scene.clear();
|
|
689
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 100)] });
|
|
690
|
+
core.scene.addEntity('Line', { points: [new Point(50, 50), new Point(100, 50)] });
|
|
691
|
+
|
|
692
|
+
const extend = new Extend();
|
|
693
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(0)];
|
|
694
|
+
extend.selectedEntity = core.scene.entities.get(1);
|
|
695
|
+
core.mouse.setPosFromScenePoint(new Point(55, 50));
|
|
696
|
+
extend.action();
|
|
697
|
+
|
|
698
|
+
expect(core.scene.entities.get(1).points[0].x).toBeCloseTo(0);
|
|
699
|
+
expect(core.scene.entities.get(1).points[0].y).toBeCloseTo(50);
|
|
700
|
+
expect(core.scene.entities.get(1).points[1].x).toBe(100);
|
|
701
|
+
expect(core.scene.entities.get(1).points[1].y).toBe(50);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
test('Extend.action notifies NOEXTEND when end segment of an Arc is curved', () => {
|
|
705
|
+
// A standalone Arc always has a non-zero bulge on its single segment.
|
|
706
|
+
// #extendEntity must reject the extension and notify because endSegmentBulge !== 0.
|
|
707
|
+
core.scene.clear();
|
|
708
|
+
core.scene.addEntity('Arc', {
|
|
709
|
+
points: [new Point(0, 0), new Point(10, 0), new Point(0, 10)],
|
|
710
|
+
direction: 1,
|
|
711
|
+
});
|
|
712
|
+
// Vertical boundary at x=5 — intersects the CCW quarter-arc
|
|
713
|
+
core.scene.addEntity('Line', { points: [new Point(5, -20), new Point(5, 20)] });
|
|
714
|
+
|
|
715
|
+
const extend = new Extend();
|
|
716
|
+
extend.selectedBoundaryItems = [core.scene.entities.get(1)];
|
|
717
|
+
extend.selectedEntity = core.scene.entities.get(0);
|
|
718
|
+
core.mouse.setPosFromScenePoint(new Point(9, 1)); // near arc start (10,0)
|
|
719
|
+
|
|
720
|
+
const notifySpy = jest.spyOn(core, 'notify').mockImplementation(() => {});
|
|
721
|
+
extend.action();
|
|
722
|
+
|
|
723
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NOEXTEND));
|
|
724
|
+
expect(extend.selectedEntity).toBeNull();
|
|
725
|
+
notifySpy.mockRestore();
|
|
726
|
+
});
|