@design-core/core 0.0.1 → 0.0.3
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 +14 -7
- package/.github/workflows/DesignCoreQA.yml +3 -1
- package/README.md +11 -4
- package/TODO.md +19 -0
- package/core/core/core.js +49 -42
- package/core/designCore.js +61 -2
- package/core/dimensions/alignedDimension.js +142 -0
- package/core/dimensions/angularDimension.js +550 -0
- package/core/dimensions/baseCircularDimension.js +244 -0
- package/core/dimensions/baseDimension.js +558 -0
- package/core/dimensions/baseLinearDimension.js +371 -0
- package/core/dimensions/diametricDimension.js +82 -0
- package/core/dimensions/dimension.js +298 -0
- package/core/dimensions/linearDimension.js +29 -0
- package/core/dimensions/radialDimension.js +78 -0
- package/core/dimensions/rotatedDimension.js +167 -0
- package/core/entities/arc.js +314 -60
- package/core/entities/arctext.js +642 -0
- package/core/entities/basePolyline.js +553 -94
- package/core/entities/circle.js +181 -54
- package/core/entities/entity.js +154 -74
- package/core/entities/hatch.js +807 -0
- package/core/entities/insert.js +200 -39
- package/core/entities/line.js +196 -134
- package/core/entities/lwpolyline.js +21 -44
- package/core/entities/point.js +194 -29
- package/core/entities/polyline.js +54 -7
- package/core/entities/rectangle.js +43 -13
- package/core/entities/solid.js +43 -6
- package/core/entities/text.js +324 -181
- package/core/lib/boundingBox.js +73 -13
- package/core/lib/canvas.js +256 -65
- package/core/lib/clipboard.js +198 -0
- package/core/lib/colour.js +144 -0
- package/core/lib/colours.js +367 -301
- package/core/lib/commandLine.js +16 -28
- package/core/lib/commandManager.js +115 -58
- package/core/lib/constants.js +23 -0
- package/core/lib/dxf/dxf.js +249 -34
- package/core/lib/dxf/dxfFile.js +23 -38
- package/core/lib/dxf/dxfIterator.js +17 -5
- package/core/lib/dxf/dxfRead.js +23 -7
- package/core/lib/dxf/dxfWrite.js +39 -129
- package/core/lib/dxf/handleManager.js +97 -0
- package/core/lib/dxf/sections/blocks.js +16 -2
- package/core/lib/dxf/sections/entities.js +17 -3
- package/core/lib/dxf/sections/header.js +10 -1
- package/core/lib/dxf/sections/objects.js +36 -0
- package/core/lib/dxf/sections/section.js +43 -5
- package/core/lib/dxf/sections/tables.js +17 -3
- package/core/lib/entityManager.js +178 -0
- package/core/lib/fileio.js +14 -4
- package/core/lib/headers.js +229 -0
- package/core/lib/input.js +46 -0
- package/core/lib/inputManager.js +199 -194
- package/core/lib/intersect.js +341 -468
- package/core/lib/logging.js +25 -1
- package/core/lib/matrix.js +6 -8
- package/core/lib/mouse.js +24 -79
- package/core/lib/patterns.js +690 -0
- package/core/lib/promptOptions.js +133 -0
- package/core/lib/scene.js +71 -174
- package/core/lib/selectionManager.js +74 -48
- package/core/lib/selectionWindow.js +24 -10
- package/core/lib/settings.js +18 -11
- package/core/lib/snapping.js +129 -21
- package/core/lib/stateManager.js +288 -0
- package/core/lib/strings.js +57 -5
- package/core/lib/utils.js +95 -11
- package/core/objects/dictionary.js +43 -0
- package/core/objects/dictionaryManager.js +80 -0
- package/core/properties/dimType.js +117 -0
- package/core/properties/flags.js +66 -0
- package/core/properties/property.js +26 -0
- package/core/properties/propertyManager.js +65 -19
- package/core/tables/appId.js +31 -0
- package/core/tables/appIdManager.js +44 -0
- package/core/tables/block.js +323 -0
- package/core/tables/blockManager.js +48 -0
- package/core/tables/blockRecord.js +29 -0
- package/core/tables/blockRecordManager.js +37 -0
- package/core/tables/dimStyle.js +627 -0
- package/core/tables/dimStyleManager.js +187 -0
- package/core/tables/endblk.js +28 -0
- package/core/tables/layer.js +219 -0
- package/core/tables/layerManager.js +47 -0
- package/core/tables/ltype.js +85 -0
- package/core/tables/ltypeManager.js +65 -0
- package/core/tables/style.js +145 -0
- package/core/tables/styleManager.js +55 -0
- package/core/tables/styleManagerBase.js +110 -0
- package/core/tables/tableManagerBase.js +354 -0
- package/core/tables/ucs.js +28 -0
- package/core/tables/ucsManager.js +44 -0
- package/core/tables/view.js +28 -0
- package/core/tables/viewManager.js +44 -0
- package/core/tables/vport.js +70 -0
- package/core/tables/vportManager.js +65 -0
- package/core/tools/chamfer.js +343 -0
- package/core/tools/chamferFilletBase.js +162 -0
- package/core/tools/copy.js +68 -40
- package/core/tools/copybase.js +65 -0
- package/core/tools/copyclip.js +59 -0
- package/core/tools/cornerEntity.js +138 -0
- package/core/tools/cutclip.js +72 -0
- package/core/tools/distance.js +37 -9
- package/core/tools/erase.js +43 -13
- package/core/tools/explode.js +117 -0
- package/core/tools/extend.js +71 -32
- package/core/tools/fillet.js +336 -0
- package/core/tools/identify.js +33 -6
- package/core/tools/matchProp.js +106 -0
- package/core/tools/mirror.js +141 -0
- package/core/tools/move.js +65 -32
- package/core/tools/offset.js +321 -0
- package/core/tools/pan.js +54 -0
- package/core/tools/pasteclip.js +93 -0
- package/core/tools/purge.js +90 -0
- package/core/tools/rotate.js +105 -70
- package/core/tools/scale.js +166 -0
- package/core/tools/tool.js +14 -4
- package/core/tools/trim.js +71 -32
- package/core/tools/zoom.js +152 -0
- package/package.json +3 -305
- package/test/dimensions/alignedDimension.test.js +211 -0
- package/test/dimensions/angularDimension.test.js +243 -0
- package/test/dimensions/baseDimension.test.js +239 -0
- package/test/dimensions/diametricDimension.test.js +203 -0
- package/test/dimensions/dimension.test.js +102 -0
- package/test/dimensions/radialDimension.test.js +203 -0
- package/test/dimensions/rotatedDimension.test.js +224 -0
- package/test/entities/arc.test.js +463 -41
- package/test/entities/arctext.test.js +329 -0
- package/test/entities/basePolyline.test.js +459 -10
- package/test/entities/circle.test.js +312 -7
- package/test/entities/entity.test.js +28 -9
- package/test/entities/hatch.test.js +606 -0
- package/test/entities/insert.test.js +90 -0
- package/test/entities/line.test.js +182 -6
- package/test/entities/point.test.js +375 -37
- package/test/entities/solid.test.js +156 -0
- package/test/entities/text.test.js +533 -7
- package/test/integration/exportIntegration.reference.dxf +1706 -0
- package/test/integration/exportIntegration.test.js +186 -0
- package/test/integration/importExportIntegration.test.js +126 -0
- package/test/integration/intersectionIntegration.test.js +880 -0
- package/test/integration/userInputIntegration.reference.dxf +1410 -0
- package/test/integration/userInputIntegration.test.js +269 -0
- package/test/lib/boundingBox.test.js +26 -2
- package/test/lib/canvas.test.js +613 -5
- package/test/lib/clipboard.test.js +70 -0
- package/test/lib/colour.test.js +114 -0
- package/test/lib/colours.test.js +63 -18
- package/test/lib/commandLine.test.js +159 -22
- package/test/lib/commandManager.test.js +4 -4
- package/test/lib/constants.test.js +9 -0
- package/test/lib/dxf/dxfFile.test.js +6 -2
- package/test/lib/dxf/dxfIterator.test.js +1 -1
- package/test/lib/dxf/dxfWrite.test.js +173 -0
- package/test/lib/dxf/handleManager.test.js +137 -0
- package/test/lib/dxf/sections/blocks.test.js +5 -5
- package/test/lib/dxf/sections/entities.test.js +4 -4
- package/test/lib/dxf/sections/header.test.js +18 -4
- package/test/lib/dxf/sections/objects.test.js +132 -0
- package/test/lib/dxf/sections/section.test.js +29 -29
- package/test/lib/dxf/sections/tables.test.js +3 -3
- package/test/lib/entityManager.test.js +163 -0
- package/test/lib/headers.test.js +233 -0
- package/test/lib/inputManager.test.js +1144 -5
- package/test/lib/intersect.test.js +454 -331
- package/test/lib/logging.test.js +1 -1
- package/test/lib/matrix.test.js +1 -1
- package/test/lib/mouse.test.js +327 -4
- package/test/lib/patterns.test.js +105 -0
- package/test/lib/promptOptions.test.js +179 -0
- package/test/lib/scene.test.js +16 -52
- package/test/lib/selectionManager.test.js +17 -5
- package/test/lib/settings.test.js +111 -5
- package/test/lib/snapping.test.js +142 -4
- package/test/lib/stateManager.test.js +155 -0
- package/test/lib/utils.test.js +81 -1
- package/test/objects/dictionary.test.js +81 -0
- package/test/objects/dictionaryManager.test.js +34 -0
- package/test/properties/dimType.test.js +62 -0
- package/test/properties/flags.test.js +164 -0
- package/test/properties/property.test.js +13 -0
- package/test/properties/propertyManager.test.js +10 -11
- package/test/tables/appId.test.js +46 -0
- package/test/tables/appIdManager.test.js +72 -0
- package/test/tables/block.test.js +157 -0
- package/test/tables/blockManager.test.js +52 -0
- package/test/tables/blockRecord.test.js +39 -0
- package/test/tables/blockRecordManager.test.js +60 -0
- package/test/tables/dimstyle.test.js +72 -0
- package/test/tables/endblk.test.js +30 -0
- package/test/tables/layer.test.js +95 -0
- package/test/tables/layerManager.test.js +172 -0
- package/test/{styles → tables}/ltype.test.js +7 -7
- package/test/{styles → tables}/ltypeManager.test.js +2 -2
- package/test/tables/style.test.js +44 -0
- package/test/tables/stylemanagerbase.test.js +220 -0
- package/test/tables/tableManagerBase.test.js +194 -0
- package/test/tables/ucs.test.js +37 -0
- package/test/tables/ucsManager.test.js +48 -0
- package/test/tables/view.test.js +37 -0
- package/test/tables/viewManager.test.js +48 -0
- package/test/tables/vport.test.js +117 -0
- package/test/tables/vportManager.test.js +54 -0
- package/test/test-helpers/test-helpers.js +66 -0
- package/test/tools/chamfer.test.js +970 -0
- package/test/tools/chamferFilletBase.test.js +446 -0
- package/test/tools/copy.test.js +22 -14
- package/test/tools/copybase.test.js +93 -0
- package/test/tools/copyclip.test.js +92 -0
- package/test/tools/cornerEntity.test.js +201 -0
- package/test/tools/cutclip.test.js +94 -0
- package/test/tools/distance.test.js +140 -18
- package/test/tools/erase.test.js +12 -12
- package/test/tools/explode.test.js +45 -0
- package/test/tools/extend.test.js +326 -44
- package/test/tools/fillet.test.js +831 -0
- package/test/tools/identify.test.js +115 -16
- package/test/tools/matchProp.test.js +84 -0
- package/test/tools/mirror.test.js +475 -0
- package/test/tools/move.test.js +75 -19
- package/test/tools/offset.test.js +748 -0
- package/test/tools/pan.test.js +72 -0
- package/test/tools/pasteclip.test.js +100 -0
- package/test/tools/purge.test.js +89 -0
- package/test/tools/rotate.test.js +89 -23
- package/test/tools/scale.test.js +529 -0
- package/test/tools/trim.test.js +419 -84
- package/test/tools/zoom.test.js +504 -0
- package/core/entities/alignedDimension.js +0 -279
- package/core/entities/angularDimension.js +0 -194
- package/core/entities/baseDimension.js +0 -227
- package/core/entities/block.js +0 -247
- package/core/entities/diametricDimension.js +0 -133
- package/core/entities/dimension.js +0 -202
- package/core/entities/radialDimension.js +0 -126
- package/core/styles/dimStyle.js +0 -719
- package/core/styles/dimStyleManager.js +0 -47
- package/core/styles/layer.js +0 -133
- package/core/styles/layerManager.js +0 -40
- package/core/styles/ltype.js +0 -86
- package/core/styles/ltypeManager.js +0 -50
- package/core/styles/style.js +0 -158
- package/core/styles/styleManager.js +0 -43
- package/core/styles/styleManagerBase.js +0 -299
- package/test/entities/block.test.js +0 -22
- package/test/styles/dimstyle.test.js +0 -19
- package/test/styles/layerManager.test.js +0 -150
- package/test/styles/style.test.js +0 -33
- package/test/styles/stylemanagerbase.test.js +0 -219
|
@@ -1,27 +1,126 @@
|
|
|
1
|
-
import {Core} from '../../core/core/core.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { Core } from '../../core/core/core.js';
|
|
2
|
+
import { Point } from '../../core/entities/point.js';
|
|
3
|
+
import { Identify } from '../../core/tools/identify.js';
|
|
4
|
+
import { expect, jest } from '@jest/globals';
|
|
5
|
+
import { withMockInput } from '../test-helpers/test-helpers.js';
|
|
5
6
|
|
|
6
7
|
const core = new Core();
|
|
7
|
-
let output;
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
output = notification;
|
|
11
|
-
};
|
|
9
|
+
// ─── register ────────────────────────────────────────────────────────────────
|
|
12
10
|
|
|
13
|
-
test('
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
test('Identify.register returns correct command object', () => {
|
|
12
|
+
const reg = Identify.register();
|
|
13
|
+
expect(reg.command).toBe('Identify');
|
|
14
|
+
expect(reg.shortcut).toBe('ID');
|
|
15
|
+
});
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// ─── action ───────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
test('Identify.action notifies formatted X and Y coordinates', () => {
|
|
20
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
const identify = new Identify();
|
|
20
23
|
identify.points.push(new Point(0, 10));
|
|
24
|
+
identify.action();
|
|
25
|
+
|
|
26
|
+
expect(notifySpy).toHaveBeenCalledWith('X:0.0 Y:10.0');
|
|
27
|
+
notifySpy.mockRestore();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('Identify.action at origin formats both coordinates as zero', () => {
|
|
31
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
32
|
+
|
|
33
|
+
const identify = new Identify();
|
|
34
|
+
identify.points.push(new Point(0, 0));
|
|
35
|
+
identify.action();
|
|
36
|
+
|
|
37
|
+
expect(notifySpy).toHaveBeenCalledWith('X:0.0 Y:0.0');
|
|
38
|
+
notifySpy.mockRestore();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('Identify.action with negative coordinates', () => {
|
|
42
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
43
|
+
|
|
44
|
+
const identify = new Identify();
|
|
45
|
+
identify.points.push(new Point(-5, -3));
|
|
46
|
+
identify.action();
|
|
47
|
+
|
|
48
|
+
expect(notifySpy).toHaveBeenCalledWith('X:-5.0 Y:-3.0');
|
|
49
|
+
notifySpy.mockRestore();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('Identify.action with fractional coordinates rounded to 1 decimal place', () => {
|
|
53
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
21
54
|
|
|
22
|
-
|
|
55
|
+
const identify = new Identify();
|
|
56
|
+
identify.points.push(new Point(1.5, 2.5));
|
|
57
|
+
identify.action();
|
|
58
|
+
|
|
59
|
+
expect(notifySpy).toHaveBeenCalledWith('X:1.5 Y:2.5');
|
|
60
|
+
notifySpy.mockRestore();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('Identify.action uses the last point when multiple points are present', () => {
|
|
64
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
65
|
+
|
|
66
|
+
const identify = new Identify();
|
|
67
|
+
identify.points.push(new Point(1, 2));
|
|
68
|
+
identify.points.push(new Point(7, 8));
|
|
23
69
|
identify.action();
|
|
24
70
|
|
|
25
|
-
expect(
|
|
26
|
-
|
|
71
|
+
expect(notifySpy).toHaveBeenCalledWith('X:7.0 Y:8.0');
|
|
72
|
+
notifySpy.mockRestore();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ─── execute ──────────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
test('Identify.execute collects one point and calls executeCommand', async () => {
|
|
78
|
+
const executeCommandSpy = jest.fn();
|
|
79
|
+
|
|
80
|
+
await withMockInput(
|
|
81
|
+
core.scene,
|
|
82
|
+
[new Point(3, 4)],
|
|
83
|
+
async () => {
|
|
84
|
+
const identify = new Identify();
|
|
85
|
+
await identify.execute();
|
|
86
|
+
},
|
|
87
|
+
{ extraMethods: { executeCommand: executeCommandSpy } },
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(executeCommandSpy).toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('Identify.execute returns early when input is undefined', async () => {
|
|
94
|
+
const executeCommandSpy = jest.fn();
|
|
95
|
+
|
|
96
|
+
await withMockInput(
|
|
97
|
+
core.scene,
|
|
98
|
+
[],
|
|
99
|
+
async () => {
|
|
100
|
+
const identify = new Identify();
|
|
101
|
+
await identify.execute();
|
|
102
|
+
},
|
|
103
|
+
{ extraMethods: { executeCommand: executeCommandSpy } },
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(executeCommandSpy).not.toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('Identify.execute stores the collected point for action', async () => {
|
|
110
|
+
let capturedIdentify;
|
|
111
|
+
const executeCommandSpy = jest.fn();
|
|
112
|
+
|
|
113
|
+
await withMockInput(
|
|
114
|
+
core.scene,
|
|
115
|
+
[new Point(9, 11)],
|
|
116
|
+
async () => {
|
|
117
|
+
capturedIdentify = new Identify();
|
|
118
|
+
await capturedIdentify.execute();
|
|
119
|
+
},
|
|
120
|
+
{ extraMethods: { executeCommand: executeCommandSpy } },
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(capturedIdentify.points).toHaveLength(1);
|
|
124
|
+
expect(capturedIdentify.points[0].x).toBe(9);
|
|
125
|
+
expect(capturedIdentify.points[0].y).toBe(11);
|
|
27
126
|
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Core } from '../../core/core/core.js';
|
|
2
|
+
import { DesignCore } from '../../core/designCore.js';
|
|
3
|
+
import { Point } from '../../core/entities/point.js';
|
|
4
|
+
import { MatchProp } from '../../core/tools/matchProp.js';
|
|
5
|
+
import { Input } from '../../core/lib/inputManager.js';
|
|
6
|
+
import { jest } from '@jest/globals';
|
|
7
|
+
|
|
8
|
+
const core = new Core();
|
|
9
|
+
|
|
10
|
+
// Add items to scene
|
|
11
|
+
DesignCore.Scene.addItem('Line', { points: [new Point(10, 0), new Point(20, 0)], layer: 'test' });
|
|
12
|
+
DesignCore.Scene.addItem('Circle', { points: [new Point(10, 0), new Point(20, 0)] });
|
|
13
|
+
DesignCore.Scene.addItem('Polyline', { points: [new Point(10, 0), new Point(20, 0)] });
|
|
14
|
+
DesignCore.Scene.addItem('Arc', { points: [new Point(10, 0), new Point(10, 10), new Point(0, 10)] });
|
|
15
|
+
DesignCore.Scene.addItem('Rectangle', { points: [new Point(10, 0), new Point(0, 10)] });
|
|
16
|
+
DesignCore.Scene.addItem('Text', { points: [new Point(10, 0), new Point(0, 10)], height: 10, rotation: 0, string: 'text test' });
|
|
17
|
+
|
|
18
|
+
test('Test MatchProp.execute requests source and targets and completes', async () => {
|
|
19
|
+
const match = new MatchProp();
|
|
20
|
+
|
|
21
|
+
// Use existing items added at top of file
|
|
22
|
+
const src = core.scene.entities.get(0);
|
|
23
|
+
const tgt1 = core.scene.entities.get(1);
|
|
24
|
+
const tgt2 = core.scene.entities.get(2);
|
|
25
|
+
|
|
26
|
+
const im = core.scene.inputManager;
|
|
27
|
+
|
|
28
|
+
const promptCalls = [];
|
|
29
|
+
const reqSpy = jest
|
|
30
|
+
.spyOn(im, 'requestInput')
|
|
31
|
+
// First prompt: pick source entity (single selection)
|
|
32
|
+
.mockImplementationOnce((po) => {
|
|
33
|
+
promptCalls.push(po);
|
|
34
|
+
return Promise.resolve(src);
|
|
35
|
+
})
|
|
36
|
+
// Second prompt: pick target entities (selection set)
|
|
37
|
+
.mockImplementationOnce((po) => {
|
|
38
|
+
promptCalls.push(po);
|
|
39
|
+
return Promise.resolve([tgt1, tgt2]);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await expect(match.execute()).resolves.toBeUndefined();
|
|
43
|
+
|
|
44
|
+
// Validate input sequence
|
|
45
|
+
expect(reqSpy).toHaveBeenCalledTimes(2);
|
|
46
|
+
expect(promptCalls[0].types).toContain(Input.Type.SINGLESELECTION);
|
|
47
|
+
expect(promptCalls[1].types).toContain(Input.Type.SELECTIONSET);
|
|
48
|
+
|
|
49
|
+
reqSpy.mockRestore();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
test('Test MatchProp.register', () => {
|
|
54
|
+
const command = MatchProp.register();
|
|
55
|
+
expect(command.command).toBe('MatchProp');
|
|
56
|
+
expect(command.shortcut).toBe('MA');
|
|
57
|
+
expect(command.type).toBeUndefined();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('Test MatchProp.action matches properties from source to targets', () => {
|
|
61
|
+
const match = new MatchProp();
|
|
62
|
+
|
|
63
|
+
// Use existing items added at top of file
|
|
64
|
+
match.sourceIndex = 0; // Line with layer 'test'
|
|
65
|
+
match.destinationSetIndices = [1, 2, 3, 4, 5]; // Circle, Polyline, Arc, Rectangle, Text
|
|
66
|
+
|
|
67
|
+
match.action();
|
|
68
|
+
|
|
69
|
+
// Validate that layer property was matched to targets
|
|
70
|
+
for (let i = 0; i < match.destinationSetIndices.length; i++) {
|
|
71
|
+
const tgt = core.scene.entities.get(match.destinationSetIndices[i]);
|
|
72
|
+
expect(tgt.layer).toBe('test');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const circle = core.scene.entities.get(1);
|
|
76
|
+
expect(circle.radius).toBe(10); // original radius
|
|
77
|
+
|
|
78
|
+
const arc = core.scene.entities.get(3);
|
|
79
|
+
expect(arc.radius).toBe(10); // original radius
|
|
80
|
+
|
|
81
|
+
const text = core.scene.entities.get(5);
|
|
82
|
+
expect(text.height).toBe(10); // original height
|
|
83
|
+
expect(text.string).toBe('text test'); // original string
|
|
84
|
+
});
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import { Core } from '../../core/core/core.js';
|
|
2
|
+
import { Point } from '../../core/entities/point.js';
|
|
3
|
+
import { Mirror } from '../../core/tools/mirror.js';
|
|
4
|
+
|
|
5
|
+
const core = new Core();
|
|
6
|
+
|
|
7
|
+
const inputScenarios = [
|
|
8
|
+
{
|
|
9
|
+
desc: 'mirror across X axis keeping source',
|
|
10
|
+
points: [new Point(0, 5), new Point(10, 5)],
|
|
11
|
+
inputs: [new Point(0, 0), new Point(1, 0), 'No'],
|
|
12
|
+
expectedCount: 2,
|
|
13
|
+
checkIndex: 1,
|
|
14
|
+
expectedX: 0,
|
|
15
|
+
expectedY: -5,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
desc: 'mirror across X axis erasing source',
|
|
19
|
+
points: [new Point(0, 5), new Point(10, 5)],
|
|
20
|
+
inputs: [new Point(0, 0), new Point(1, 0), 'Yes'],
|
|
21
|
+
expectedCount: 1,
|
|
22
|
+
checkIndex: 0,
|
|
23
|
+
expectedX: 0,
|
|
24
|
+
expectedY: -5,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
desc: 'mirror across Y axis keeping source',
|
|
28
|
+
points: [new Point(5, 0), new Point(5, 10)],
|
|
29
|
+
inputs: [new Point(0, 0), new Point(0, 1), 'No'],
|
|
30
|
+
expectedCount: 2,
|
|
31
|
+
checkIndex: 1,
|
|
32
|
+
expectedX: -5,
|
|
33
|
+
expectedY: 0,
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
test.each(inputScenarios)('Mirror.execute handles $desc', async (scenario) => {
|
|
38
|
+
const { points, inputs, expectedCount, checkIndex, expectedX, expectedY } = scenario;
|
|
39
|
+
|
|
40
|
+
core.scene.clear();
|
|
41
|
+
core.scene.selectionManager.reset();
|
|
42
|
+
|
|
43
|
+
core.scene.addItem('Line', { points });
|
|
44
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
45
|
+
|
|
46
|
+
const origInputManager = core.scene.inputManager;
|
|
47
|
+
let callCount = 0;
|
|
48
|
+
core.scene.inputManager = {
|
|
49
|
+
requestInput: async () => {
|
|
50
|
+
if (callCount < inputs.length) {
|
|
51
|
+
return inputs[callCount++];
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
executeCommand: () => {},
|
|
55
|
+
reset: () => {},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const mirror = new Mirror();
|
|
59
|
+
await mirror.execute();
|
|
60
|
+
mirror.action();
|
|
61
|
+
|
|
62
|
+
expect(core.scene.entities.count()).toBe(expectedCount);
|
|
63
|
+
|
|
64
|
+
const entity = core.scene.entities.get(checkIndex);
|
|
65
|
+
expect(entity.points[0].x).toBeCloseTo(expectedX);
|
|
66
|
+
expect(entity.points[0].y).toBeCloseTo(expectedY);
|
|
67
|
+
|
|
68
|
+
core.scene.inputManager = origInputManager;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('Mirror.execute - cancel at first point does not mirror', async () => {
|
|
72
|
+
core.scene.clear();
|
|
73
|
+
core.scene.selectionManager.reset();
|
|
74
|
+
|
|
75
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
76
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
77
|
+
|
|
78
|
+
const origInputManager = core.scene.inputManager;
|
|
79
|
+
core.scene.inputManager = {
|
|
80
|
+
requestInput: async () => undefined,
|
|
81
|
+
executeCommand: () => {},
|
|
82
|
+
reset: () => {},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const mirror = new Mirror();
|
|
86
|
+
await mirror.execute();
|
|
87
|
+
|
|
88
|
+
expect(core.scene.entities.count()).toBe(1);
|
|
89
|
+
expect(core.scene.entities.get(0).points[0].y).toBeCloseTo(5);
|
|
90
|
+
|
|
91
|
+
core.scene.inputManager = origInputManager;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('Mirror.execute - cancel at second point does not mirror', async () => {
|
|
95
|
+
core.scene.clear();
|
|
96
|
+
core.scene.selectionManager.reset();
|
|
97
|
+
|
|
98
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
99
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
100
|
+
|
|
101
|
+
const origInputManager = core.scene.inputManager;
|
|
102
|
+
let callCount = 0;
|
|
103
|
+
const inputs = [new Point(0, 0)]; // provides pt1, then undefined on pt2
|
|
104
|
+
core.scene.inputManager = {
|
|
105
|
+
requestInput: async () => {
|
|
106
|
+
if (callCount < inputs.length) {
|
|
107
|
+
return inputs[callCount++];
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
executeCommand: () => {},
|
|
111
|
+
reset: () => {},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const mirror = new Mirror();
|
|
115
|
+
await mirror.execute();
|
|
116
|
+
|
|
117
|
+
expect(core.scene.entities.count()).toBe(1);
|
|
118
|
+
expect(core.scene.entities.get(0).points[0].y).toBeCloseTo(5);
|
|
119
|
+
|
|
120
|
+
core.scene.inputManager = origInputManager;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('Mirror.execute - undefined erase prompt defaults to keeping source', async () => {
|
|
124
|
+
core.scene.clear();
|
|
125
|
+
core.scene.selectionManager.reset();
|
|
126
|
+
|
|
127
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
128
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
129
|
+
|
|
130
|
+
const origInputManager = core.scene.inputManager;
|
|
131
|
+
let callCount = 0;
|
|
132
|
+
const inputs = [new Point(0, 0), new Point(1, 0)]; // pt1 + pt2, then undefined on erase
|
|
133
|
+
core.scene.inputManager = {
|
|
134
|
+
requestInput: async () => {
|
|
135
|
+
if (callCount < inputs.length) {
|
|
136
|
+
return inputs[callCount++];
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
executeCommand: () => {},
|
|
140
|
+
reset: () => {},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const mirror = new Mirror();
|
|
144
|
+
await mirror.execute();
|
|
145
|
+
mirror.action();
|
|
146
|
+
|
|
147
|
+
// eraseSource defaults to false — original kept and mirrored copy added
|
|
148
|
+
expect(mirror.eraseSource).toBe(false);
|
|
149
|
+
expect(core.scene.entities.count()).toBe(2);
|
|
150
|
+
expect(core.scene.entities.get(1).points[0].y).toBeCloseTo(-5);
|
|
151
|
+
|
|
152
|
+
core.scene.inputManager = origInputManager;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('Mirror.execute - entity count unchanged when keeping source', async () => {
|
|
156
|
+
core.scene.clear();
|
|
157
|
+
core.scene.selectionManager.reset();
|
|
158
|
+
|
|
159
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
160
|
+
core.scene.addItem('Circle', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
161
|
+
|
|
162
|
+
const entityCount = core.scene.entities.count();
|
|
163
|
+
|
|
164
|
+
for (let i = 0; i < entityCount; i++) {
|
|
165
|
+
core.scene.selectionManager.addToSelectionSet(i);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const origInputManager = core.scene.inputManager;
|
|
169
|
+
let callCount = 0;
|
|
170
|
+
const inputs = [new Point(0, 0), new Point(1, 0), 'No'];
|
|
171
|
+
core.scene.inputManager = {
|
|
172
|
+
requestInput: async () => {
|
|
173
|
+
if (callCount < inputs.length) {
|
|
174
|
+
return inputs[callCount++];
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
executeCommand: () => {},
|
|
178
|
+
reset: () => {},
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const mirror = new Mirror();
|
|
182
|
+
await mirror.execute();
|
|
183
|
+
mirror.action();
|
|
184
|
+
|
|
185
|
+
expect(core.scene.entities.count()).toBe(entityCount * 2);
|
|
186
|
+
|
|
187
|
+
core.scene.inputManager = origInputManager;
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('Test Mirror.action - keep source (default)', () => {
|
|
191
|
+
core.scene.clear();
|
|
192
|
+
core.scene.selectionManager.reset();
|
|
193
|
+
|
|
194
|
+
// Add entities
|
|
195
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
196
|
+
core.scene.addItem('Circle', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
197
|
+
core.scene.addItem('Polyline', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
198
|
+
core.scene.addItem('Arc', { points: [new Point(0, 5), new Point(10, 5), new Point(5, 10)] });
|
|
199
|
+
core.scene.addItem('Rectangle', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
200
|
+
|
|
201
|
+
const entityCount = core.scene.entities.count();
|
|
202
|
+
|
|
203
|
+
// Select all
|
|
204
|
+
for (let i = 0; i < entityCount; i++) {
|
|
205
|
+
core.scene.selectionManager.addToSelectionSet(i);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const mirror = new Mirror();
|
|
209
|
+
|
|
210
|
+
// Mirror line along the X axis (y=0)
|
|
211
|
+
mirror.points.push(new Point(0, 0));
|
|
212
|
+
mirror.points.push(new Point(1, 0));
|
|
213
|
+
mirror.eraseSource = false;
|
|
214
|
+
|
|
215
|
+
mirror.action();
|
|
216
|
+
|
|
217
|
+
// Originals kept + mirrored copies added
|
|
218
|
+
expect(core.scene.entities.count()).toBe(entityCount * 2);
|
|
219
|
+
|
|
220
|
+
// Originals unchanged
|
|
221
|
+
for (let i = 0; i < entityCount; i++) {
|
|
222
|
+
expect(core.scene.entities.get(i).points[0].y).toBeCloseTo(5);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Mirrored copies have y negated
|
|
226
|
+
for (let i = entityCount; i < core.scene.entities.count(); i++) {
|
|
227
|
+
expect(core.scene.entities.get(i).points[0].y).toBeCloseTo(-5);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Mirrored copies have unique handles
|
|
231
|
+
for (let i = 0; i < entityCount; i++) {
|
|
232
|
+
const original = core.scene.entities.get(i);
|
|
233
|
+
const copy = core.scene.entities.get(i + entityCount);
|
|
234
|
+
expect(copy.handle).toBeDefined();
|
|
235
|
+
expect(copy.handle).not.toBe(original.handle);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('Test Mirror.action - erase source', () => {
|
|
240
|
+
core.scene.clear();
|
|
241
|
+
core.scene.selectionManager.reset();
|
|
242
|
+
|
|
243
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
244
|
+
core.scene.addItem('Circle', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
245
|
+
|
|
246
|
+
const entityCount = core.scene.entities.count();
|
|
247
|
+
|
|
248
|
+
for (let i = 0; i < entityCount; i++) {
|
|
249
|
+
core.scene.selectionManager.addToSelectionSet(i);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const mirror = new Mirror();
|
|
253
|
+
|
|
254
|
+
// Mirror line along the X axis (y=0)
|
|
255
|
+
mirror.points.push(new Point(0, 0));
|
|
256
|
+
mirror.points.push(new Point(1, 0));
|
|
257
|
+
mirror.eraseSource = true;
|
|
258
|
+
|
|
259
|
+
mirror.action();
|
|
260
|
+
|
|
261
|
+
// No new entities — originals replaced in place
|
|
262
|
+
expect(core.scene.entities.count()).toBe(entityCount);
|
|
263
|
+
|
|
264
|
+
// All entities now have y negated
|
|
265
|
+
for (let i = 0; i < entityCount; i++) {
|
|
266
|
+
expect(core.scene.entities.get(i).points[0].y).toBeCloseTo(-5);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test('Test Mirror.action - mirror across Y axis', () => {
|
|
271
|
+
core.scene.clear();
|
|
272
|
+
core.scene.selectionManager.reset();
|
|
273
|
+
|
|
274
|
+
core.scene.addItem('Line', { points: [new Point(5, 0), new Point(5, 10)] });
|
|
275
|
+
|
|
276
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
277
|
+
|
|
278
|
+
const mirror = new Mirror();
|
|
279
|
+
|
|
280
|
+
// Mirror line along the Y axis (x=0)
|
|
281
|
+
mirror.points.push(new Point(0, 0));
|
|
282
|
+
mirror.points.push(new Point(0, 1));
|
|
283
|
+
mirror.eraseSource = false;
|
|
284
|
+
|
|
285
|
+
mirror.action();
|
|
286
|
+
|
|
287
|
+
const mirrored = core.scene.entities.get(1);
|
|
288
|
+
expect(mirrored.points[0].x).toBeCloseTo(-5);
|
|
289
|
+
expect(mirrored.points[0].y).toBeCloseTo(0);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test('Test Mirror.action - arc direction is reversed', () => {
|
|
293
|
+
core.scene.clear();
|
|
294
|
+
core.scene.selectionManager.reset();
|
|
295
|
+
|
|
296
|
+
// CCW arc (direction = 1)
|
|
297
|
+
core.scene.addItem('Arc', { points: [new Point(0, 5), new Point(10, 5), new Point(5, 10)], direction: 1 });
|
|
298
|
+
|
|
299
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
300
|
+
|
|
301
|
+
const mirror = new Mirror();
|
|
302
|
+
|
|
303
|
+
// Mirror across X axis
|
|
304
|
+
mirror.points.push(new Point(0, 0));
|
|
305
|
+
mirror.points.push(new Point(1, 0));
|
|
306
|
+
mirror.eraseSource = false;
|
|
307
|
+
|
|
308
|
+
mirror.action();
|
|
309
|
+
|
|
310
|
+
const original = core.scene.entities.get(0);
|
|
311
|
+
const mirrored = core.scene.entities.get(1);
|
|
312
|
+
|
|
313
|
+
expect(original.direction).toBe(1);
|
|
314
|
+
expect(mirrored.direction).toBe(-1);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('Test Mirror.getMirroredPoints', () => {
|
|
318
|
+
const mirror = new Mirror();
|
|
319
|
+
const pt1 = new Point(0, 0);
|
|
320
|
+
const pt2 = new Point(1, 0);
|
|
321
|
+
|
|
322
|
+
// Mirror two points across X axis
|
|
323
|
+
const points = [new Point(3, 4), new Point(-2, 7)];
|
|
324
|
+
const result = mirror.getMirroredPoints(points, pt1, pt2);
|
|
325
|
+
|
|
326
|
+
expect(result[0].x).toBeCloseTo(3);
|
|
327
|
+
expect(result[0].y).toBeCloseTo(-4);
|
|
328
|
+
expect(result[1].x).toBeCloseTo(-2);
|
|
329
|
+
expect(result[1].y).toBeCloseTo(-7);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('Mirror.action - zero-length mirror line does not modify entities', () => {
|
|
333
|
+
core.scene.clear();
|
|
334
|
+
core.scene.selectionManager.reset();
|
|
335
|
+
|
|
336
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
337
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
338
|
+
|
|
339
|
+
const mirror = new Mirror();
|
|
340
|
+
mirror.points.push(new Point(3, 3)); // pt1 === pt2 — degenerate mirror line
|
|
341
|
+
mirror.points.push(new Point(3, 3));
|
|
342
|
+
mirror.eraseSource = false;
|
|
343
|
+
|
|
344
|
+
mirror.action();
|
|
345
|
+
|
|
346
|
+
// No new entity should have been added
|
|
347
|
+
expect(core.scene.entities.count()).toBe(1);
|
|
348
|
+
// Original entity is unchanged
|
|
349
|
+
expect(core.scene.entities.get(0).points[0].y).toBeCloseTo(5);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
test('Mirror.execute - requests selection set when none pre-selected', async () => {
|
|
353
|
+
core.scene.clear();
|
|
354
|
+
core.scene.selectionManager.reset();
|
|
355
|
+
|
|
356
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
357
|
+
// Intentionally do NOT add to selection set — triggers the SELECTIONSET prompt
|
|
358
|
+
|
|
359
|
+
const origInputManager = core.scene.inputManager;
|
|
360
|
+
let callCount = 0;
|
|
361
|
+
core.scene.inputManager = {
|
|
362
|
+
requestInput: async () => {
|
|
363
|
+
callCount++;
|
|
364
|
+
if (callCount === 1) {
|
|
365
|
+
// SELECTIONSET prompt: populate the selection manually and return
|
|
366
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (callCount === 2) return new Point(0, 0); // pt1
|
|
370
|
+
if (callCount === 3) return new Point(1, 0); // pt2
|
|
371
|
+
if (callCount === 4) return 'No'; // keep source
|
|
372
|
+
},
|
|
373
|
+
executeCommand: () => {},
|
|
374
|
+
reset: () => {},
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const mirror = new Mirror();
|
|
378
|
+
await mirror.execute();
|
|
379
|
+
mirror.action();
|
|
380
|
+
|
|
381
|
+
expect(core.scene.entities.count()).toBe(2);
|
|
382
|
+
expect(core.scene.entities.get(1).points[0].y).toBeCloseTo(-5);
|
|
383
|
+
|
|
384
|
+
core.scene.inputManager = origInputManager;
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test('Mirror.preview - no points set, adds no temp entities', () => {
|
|
388
|
+
core.scene.clear();
|
|
389
|
+
core.scene.selectionManager.reset();
|
|
390
|
+
|
|
391
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
392
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
393
|
+
|
|
394
|
+
const mirror = new Mirror();
|
|
395
|
+
// No points pushed — guard condition should prevent any work
|
|
396
|
+
mirror.preview();
|
|
397
|
+
|
|
398
|
+
expect(core.scene.tempEntities.count()).toBe(0);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test('Mirror.preview - one point set, draws temp mirror line using mouse position', () => {
|
|
402
|
+
core.scene.clear();
|
|
403
|
+
core.scene.selectionManager.reset();
|
|
404
|
+
|
|
405
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
406
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
407
|
+
|
|
408
|
+
const mirror = new Mirror();
|
|
409
|
+
mirror.points.push(new Point(3, 3)); // pt1 distinct from default mouse (0,0) scene position
|
|
410
|
+
mirror.preview();
|
|
411
|
+
|
|
412
|
+
// Temp mirror line should have been created
|
|
413
|
+
expect(core.scene.tempEntities.count()).toBeGreaterThanOrEqual(1);
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test('Mirror.preview - two distinct points, draws mirror line and mirrors selected items', () => {
|
|
417
|
+
core.scene.clear();
|
|
418
|
+
core.scene.selectionManager.reset();
|
|
419
|
+
|
|
420
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
421
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
422
|
+
|
|
423
|
+
const mirror = new Mirror();
|
|
424
|
+
mirror.points.push(new Point(0, 0)); // pt1
|
|
425
|
+
mirror.points.push(new Point(1, 0)); // pt2 — mirror across X axis
|
|
426
|
+
mirror.preview();
|
|
427
|
+
|
|
428
|
+
// Temp mirror line should exist
|
|
429
|
+
expect(core.scene.tempEntities.count()).toBeGreaterThanOrEqual(1);
|
|
430
|
+
|
|
431
|
+
// The selectedItems copy should have its points mirrored (y negated)
|
|
432
|
+
const previewItem = core.scene.selectionManager.selectedItems[0];
|
|
433
|
+
expect(previewItem.points[0].y).toBeCloseTo(-5);
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test('Mirror.preview - identical points, draws line but skips mirroring selected items', () => {
|
|
437
|
+
core.scene.clear();
|
|
438
|
+
core.scene.selectionManager.reset();
|
|
439
|
+
|
|
440
|
+
core.scene.addItem('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
441
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
442
|
+
|
|
443
|
+
const mirror = new Mirror();
|
|
444
|
+
mirror.points.push(new Point(5, 5)); // pt1
|
|
445
|
+
mirror.points.push(new Point(5, 5)); // pt2 — same as pt1, zero-length mirror line
|
|
446
|
+
mirror.preview();
|
|
447
|
+
|
|
448
|
+
// Temp line is still drawn
|
|
449
|
+
expect(core.scene.tempEntities.count()).toBeGreaterThanOrEqual(1);
|
|
450
|
+
|
|
451
|
+
// Selected item points should be unchanged (mirroring skipped)
|
|
452
|
+
const previewItem = core.scene.selectionManager.selectedItems[0];
|
|
453
|
+
expect(previewItem.points[0].y).toBeCloseTo(5);
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test('Mirror.preview - reverses direction for entities with a direction property', () => {
|
|
457
|
+
core.scene.clear();
|
|
458
|
+
core.scene.selectionManager.reset();
|
|
459
|
+
|
|
460
|
+
// Arc with CCW direction
|
|
461
|
+
core.scene.addItem('Arc', { points: [new Point(0, 5), new Point(10, 5), new Point(5, 10)], direction: 1 });
|
|
462
|
+
core.scene.selectionManager.addToSelectionSet(0);
|
|
463
|
+
|
|
464
|
+
const mirror = new Mirror();
|
|
465
|
+
mirror.points.push(new Point(0, 0)); // pt1
|
|
466
|
+
mirror.points.push(new Point(1, 0)); // pt2 — mirror across X axis
|
|
467
|
+
mirror.preview();
|
|
468
|
+
|
|
469
|
+
// Original entity direction is unchanged
|
|
470
|
+
expect(core.scene.entities.get(0).direction).toBe(1);
|
|
471
|
+
|
|
472
|
+
// Preview copy has direction reversed
|
|
473
|
+
const previewItem = core.scene.selectionManager.selectedItems[0];
|
|
474
|
+
expect(previewItem.direction).toBe(-1);
|
|
475
|
+
});
|