@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
package/test/lib/scene.test.js
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import {Core} from '../../core/core/core.js';
|
|
2
|
-
import {Line} from '../../core/entities/line';
|
|
3
|
-
import {Point} from '../../core/entities/point';
|
|
1
|
+
import { Core } from '../../core/core/core.js';
|
|
2
|
+
import { Line } from '../../core/entities/line';
|
|
3
|
+
import { Point } from '../../core/entities/point';
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
const core = new Core();
|
|
7
7
|
const scene = core.scene;
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
test('Test Scene.boundingBox', () => {
|
|
11
10
|
// empty scene
|
|
12
11
|
expect(scene.boundingBox()).toBeUndefined();
|
|
13
12
|
|
|
14
|
-
const line1 = new Line({points: [new Point(-102, -102), new Point(201, 202)]});
|
|
13
|
+
const line1 = new Line({ points: [new Point(-102, -102), new Point(201, 202)] });
|
|
15
14
|
// add to scene (bypass scene.addItem())
|
|
16
|
-
scene.
|
|
15
|
+
scene.addItem('Line', line1);
|
|
17
16
|
expect(scene.boundingBox().xMin).toBeCloseTo(-102);
|
|
18
17
|
expect(scene.boundingBox().xMax).toBeCloseTo(201);
|
|
19
18
|
expect(scene.boundingBox().yMin).toBeCloseTo(-102);
|
|
20
19
|
expect(scene.boundingBox().yMax).toBeCloseTo(202);
|
|
21
20
|
|
|
22
|
-
const line2 = new Line({points: [new Point(1001, -2002), new Point(-2001, 2002)]});
|
|
21
|
+
const line2 = new Line({ points: [new Point(1001, -2002), new Point(-2001, 2002)] });
|
|
23
22
|
// add to scene (bypass scene.addItem())
|
|
24
|
-
scene.
|
|
23
|
+
scene.addItem('Line', line2);
|
|
25
24
|
expect(scene.boundingBox().xMin).toBeCloseTo(-2001);
|
|
26
25
|
expect(scene.boundingBox().xMax).toBeCloseTo(1001);
|
|
27
26
|
expect(scene.boundingBox().yMin).toBeCloseTo(-2002);
|
|
@@ -36,56 +35,21 @@ test('Test Scene.addItem', () => {
|
|
|
36
35
|
}).toThrow();
|
|
37
36
|
|
|
38
37
|
// Add a invalid item type
|
|
39
|
-
const itemCnt = scene.
|
|
38
|
+
const itemCnt = scene.entities.count();
|
|
40
39
|
const returnIndex = scene.addItem('FakeItem', {});
|
|
41
|
-
expect(scene.
|
|
40
|
+
expect(scene.entities.count()).toBe(itemCnt);
|
|
42
41
|
expect(returnIndex).toBeUndefined();
|
|
43
42
|
|
|
44
43
|
// Add a valid item with data
|
|
45
|
-
const itemCount = scene.
|
|
46
|
-
const index = scene.addItem('Line', {points: [new Point(), new Point(10, 10)]});
|
|
47
|
-
expect(scene.
|
|
44
|
+
const itemCount = scene.entities.count();
|
|
45
|
+
const index = scene.addItem('Line', { points: [new Point(), new Point(10, 10)] });
|
|
46
|
+
expect(scene.entities.count()).toBe(itemCount + 1);
|
|
48
47
|
expect(index).toBe(itemCount);
|
|
49
48
|
|
|
50
49
|
// Add a valid item with data at index - item count should not increase
|
|
51
|
-
scene.addItem('Line', {points: [new Point(123, 123), new Point(321, 321)]}, index);
|
|
52
|
-
expect(scene.
|
|
50
|
+
scene.addItem('Line', { points: [new Point(123, 123), new Point(321, 321)] }, index);
|
|
51
|
+
expect(scene.entities.count()).toBe(itemCount + 1);
|
|
53
52
|
// check the items points to make sure index was replaced
|
|
54
|
-
expect(scene.
|
|
55
|
-
expect(scene.
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test('Test Scene.findItem', () => {
|
|
59
|
-
// Add a circle with a known radius
|
|
60
|
-
const index = scene.addItem('Circle', {points: [new Point(543, 543)], radius: 123});
|
|
61
|
-
// find the item
|
|
62
|
-
const foundItems = scene.findItem('Circle', 'radius', 123);
|
|
63
|
-
expect(foundItems[0]).toBe(index);
|
|
64
|
-
expect(scene.items[index].points[0].x).toBe(543);
|
|
65
|
-
expect(scene.items[index].radius).toBe(123);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('Test Scene.getItem', () => {
|
|
69
|
-
// Add a valid item with data at index - item count should not increase
|
|
70
|
-
const index = 0;
|
|
71
|
-
scene.addItem('Line', {points: [new Point(1234, 1234), new Point(4321, 4321)]}, index);
|
|
72
|
-
// check the items points to make sure index was replaced
|
|
73
|
-
expect(scene.getItem(index).points[0].x).toBe(1234);
|
|
74
|
-
expect(scene.getItem(index).points[1].x).toBe(4321);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
test('Test Scene.removeItem', () => {
|
|
79
|
-
// Remove an existing index
|
|
80
|
-
const itemCount = scene.items.length;
|
|
81
|
-
const index = 0;
|
|
82
|
-
const removed = scene.removeItem(index);
|
|
83
|
-
expect(removed).toBe(true);
|
|
84
|
-
expect(scene.items.length).toBe(itemCount - 1);
|
|
85
|
-
|
|
86
|
-
// Remove a non-existing index
|
|
87
|
-
const itemCount2 = scene.items.length;
|
|
88
|
-
const notRemoved = scene.removeItem(100);
|
|
89
|
-
expect(notRemoved).toBe(false);
|
|
90
|
-
expect(scene.items.length).toBe(itemCount2);
|
|
53
|
+
expect(scene.entities.get(index).points[0].x).toBe(123);
|
|
54
|
+
expect(scene.entities.get(index).points[1].x).toBe(321);
|
|
91
55
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {Core} from '../../core/core/core.js';
|
|
2
|
-
import {Point} from '../../core/entities/point.js';
|
|
1
|
+
import { Core } from '../../core/core/core.js';
|
|
2
|
+
import { Point } from '../../core/entities/point.js';
|
|
3
3
|
|
|
4
4
|
const core = new Core();
|
|
5
5
|
const selectionManager = core.scene.selectionManager;
|
|
6
6
|
|
|
7
7
|
// Add a valid item to scene
|
|
8
|
-
core.scene.addItem('Line', {points: [new Point(10, 10), new Point(100, 10)]});
|
|
9
|
-
core.scene.addItem('Line', {points: [new Point(10, 20), new Point(100, 20)]});
|
|
10
|
-
core.scene.addItem('Circle', {points: [new Point(), new Point(5, 0)]});
|
|
8
|
+
core.scene.addItem('Line', { points: [new Point(10, 10), new Point(100, 10)] });
|
|
9
|
+
core.scene.addItem('Line', { points: [new Point(10, 20), new Point(100, 20)] });
|
|
10
|
+
core.scene.addItem('Circle', { points: [new Point(), new Point(5, 0)] });
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
test('Test SelectionManager.findClosestItem', () => {
|
|
@@ -59,3 +59,15 @@ test('Test SelectionManager.reset()', () => {
|
|
|
59
59
|
expect(selectionManager.selectionSet.selectionSet.length).toBe(0);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
test('Test SelectionManager.selectAll()', () => {
|
|
63
|
+
// Start with some selections
|
|
64
|
+
selectionManager.reset();
|
|
65
|
+
expect(selectionManager.selectionSet.selectionSet.length).toBe(0);
|
|
66
|
+
|
|
67
|
+
// Select all
|
|
68
|
+
selectionManager.selectAll();
|
|
69
|
+
expect(selectionManager.selectionSet.selectionSet.length).toBe(core.scene.entities.count());
|
|
70
|
+
// The indices should match all scene items
|
|
71
|
+
expect(selectionManager.selectionSet.selectionSet).toEqual([0, 1, 2]);
|
|
72
|
+
});
|
|
73
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Core} from '../../core/core/core.js';
|
|
1
|
+
import { Core } from '../../core/core/core.js';
|
|
2
2
|
|
|
3
3
|
const core = new Core();
|
|
4
4
|
const settings = core.settings;
|
|
@@ -8,12 +8,118 @@ test('Test Settings.setSetting', () => {
|
|
|
8
8
|
settings.setSetting('endsnap', false);
|
|
9
9
|
expect(settings.endsnap).toBe(false);
|
|
10
10
|
|
|
11
|
-
expect(settings.canvasbackgroundcolour).
|
|
12
|
-
settings.setSetting('canvasbackgroundcolour', '
|
|
13
|
-
expect(settings.canvasbackgroundcolour).
|
|
11
|
+
expect(settings.canvasbackgroundcolour).toEqual({ 'r': 30, 'g': 30, 'b': 30 });
|
|
12
|
+
settings.setSetting('canvasbackgroundcolour', { 'r': 130, 'g': 130, 'b': 130 });
|
|
13
|
+
expect(settings.canvasbackgroundcolour).toEqual({ 'r': 130, 'g': 130, 'b': 130 });
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
test('Test Settings.getSetting', () => {
|
|
17
17
|
expect(settings.getSetting('endsnap')).toBe(false);
|
|
18
|
-
expect(settings.getSetting('canvasbackgroundcolour')).
|
|
18
|
+
expect(settings.getSetting('canvasbackgroundcolour')).toEqual({ 'r': 130, 'g': 130, 'b': 130 });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('polar and ortho are mutually exclusive', () => {
|
|
22
|
+
settings.setSetting('polar', false);
|
|
23
|
+
settings.setSetting('ortho', false);
|
|
24
|
+
|
|
25
|
+
settings.setSetting('polar', true);
|
|
26
|
+
expect(settings.polar).toBe(true);
|
|
27
|
+
expect(settings.ortho).toBe(false);
|
|
28
|
+
|
|
29
|
+
settings.setSetting('ortho', true);
|
|
30
|
+
expect(settings.ortho).toBe(true);
|
|
31
|
+
expect(settings.polar).toBe(false);
|
|
32
|
+
|
|
33
|
+
settings.setSetting('ortho', false);
|
|
34
|
+
expect(settings.ortho).toBe(false);
|
|
35
|
+
expect(settings.polar).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// ─── constructor defaults ─────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
test('Settings constructor - colour defaults', () => {
|
|
41
|
+
const fresh = new (settings.constructor)();
|
|
42
|
+
expect(fresh.canvasbackgroundcolour).toEqual({ r: 30, g: 30, b: 30 });
|
|
43
|
+
expect(fresh.selecteditemscolour).toEqual({ r: 0, g: 255, b: 0 });
|
|
44
|
+
expect(fresh.snapcolour).toEqual({ r: 255, g: 0, b: 0 });
|
|
45
|
+
expect(fresh.gridcolour).toEqual({ r: 119, g: 118, b: 123 });
|
|
46
|
+
expect(fresh.helpergeometrycolour).toEqual({ r: 0, g: 195, b: 255 });
|
|
47
|
+
expect(fresh.polarsnapcolour).toEqual({ r: 55, g: 180, b: 75 });
|
|
48
|
+
expect(fresh.selectionWindow).toEqual({ r: 0, g: 255, b: 0 });
|
|
49
|
+
expect(fresh.crossingWindow).toEqual({ r: 0, g: 0, b: 255 });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('Settings constructor - font defaults', () => {
|
|
53
|
+
expect(settings.font).toBe('Arial');
|
|
54
|
+
expect(settings.fontupsidedown).toBe(false);
|
|
55
|
+
expect(settings.fontbackwards).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('Settings constructor - snap defaults', () => {
|
|
59
|
+
expect(settings.endsnap).toBeDefined();
|
|
60
|
+
expect(settings.midsnap).toBe(true);
|
|
61
|
+
expect(settings.centresnap).toBe(true);
|
|
62
|
+
expect(settings.nearestsnap).toBe(false);
|
|
63
|
+
expect(settings.quadrantsnap).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('Settings constructor - polar/ortho/grid defaults', () => {
|
|
67
|
+
// restore to known state after previous tests may have modified them
|
|
68
|
+
const fresh = new (settings.constructor)();
|
|
69
|
+
expect(fresh.polar).toBe(true);
|
|
70
|
+
expect(fresh.ortho).toBe(false);
|
|
71
|
+
expect(fresh.polarangle).toBe(45);
|
|
72
|
+
expect(fresh.drawgrid).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ─── setSetting ───────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
test('setSetting with false value does not trigger polar/ortho exclusion', () => {
|
|
78
|
+
settings.setSetting('polar', true);
|
|
79
|
+
settings.setSetting('ortho', false);
|
|
80
|
+
// setting polar to false should not flip ortho
|
|
81
|
+
settings.setSetting('polar', false);
|
|
82
|
+
expect(settings.polar).toBe(false);
|
|
83
|
+
expect(settings.ortho).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('setSetting updates arbitrary colour settings', () => {
|
|
87
|
+
settings.setSetting('polarsnapcolour', { r: 1, g: 2, b: 3 });
|
|
88
|
+
expect(settings.polarsnapcolour).toEqual({ r: 1, g: 2, b: 3 });
|
|
89
|
+
settings.setSetting('polarsnapcolour', { r: 55, g: 180, b: 75 });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('setSetting updates polarangle', () => {
|
|
93
|
+
settings.setSetting('polarangle', 90);
|
|
94
|
+
expect(settings.polarangle).toBe(90);
|
|
95
|
+
settings.setSetting('polarangle', 45);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('setSetting updates font settings', () => {
|
|
99
|
+
settings.setSetting('font', 'Courier');
|
|
100
|
+
expect(settings.font).toBe('Courier');
|
|
101
|
+
settings.setSetting('fontupsidedown', true);
|
|
102
|
+
expect(settings.fontupsidedown).toBe(true);
|
|
103
|
+
settings.setSetting('fontbackwards', true);
|
|
104
|
+
expect(settings.fontbackwards).toBe(true);
|
|
105
|
+
// restore
|
|
106
|
+
settings.setSetting('font', 'Arial');
|
|
107
|
+
settings.setSetting('fontupsidedown', false);
|
|
108
|
+
settings.setSetting('fontbackwards', false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// ─── getSetting ───────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
test('getSetting returns current value after setSetting', () => {
|
|
114
|
+
settings.setSetting('drawgrid', false);
|
|
115
|
+
expect(settings.getSetting('drawgrid')).toBe(false);
|
|
116
|
+
settings.setSetting('drawgrid', true);
|
|
117
|
+
expect(settings.getSetting('drawgrid')).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('getSetting returns correct type for colour objects', () => {
|
|
121
|
+
const colour = settings.getSetting('snapcolour');
|
|
122
|
+
expect(colour).toHaveProperty('r');
|
|
123
|
+
expect(colour).toHaveProperty('g');
|
|
124
|
+
expect(colour).toHaveProperty('b');
|
|
19
125
|
});
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { Core } from '../../core/core/core.js';
|
|
3
|
+
import { Snapping } from '../../core/lib/snapping.js';
|
|
4
|
+
import { Point } from '../../core/entities/point.js';
|
|
5
|
+
import { DesignCore } from '../../core/designCore.js';
|
|
4
6
|
|
|
5
7
|
const core = new Core();
|
|
6
8
|
const snapping = new Snapping();
|
|
7
9
|
|
|
8
10
|
// Add a valid item to scene
|
|
9
|
-
core.scene.addItem('Line', {points: [new Point(10, 10), new Point(100, 10)]});
|
|
11
|
+
core.scene.addItem('Line', { points: [new Point(10, 10), new Point(100, 10)] });
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
// Reset all mouse button states so tests don't affect each other via
|
|
15
|
+
// Canvas.mouseMoved() / InputManager.mouseMoved() behaviour
|
|
16
|
+
core.mouse.buttonOneDown = false;
|
|
17
|
+
core.mouse.buttonTwoDown = false;
|
|
18
|
+
core.mouse.buttonThreeDown = false;
|
|
19
|
+
});
|
|
10
20
|
|
|
11
21
|
|
|
12
22
|
test('Test Snapping.getSnapPoint', () => {
|
|
@@ -31,6 +41,98 @@ test('Test Snapping.getSnapPoint', () => {
|
|
|
31
41
|
expect(snapping.getSnapPoint()).toBeUndefined();
|
|
32
42
|
});
|
|
33
43
|
|
|
44
|
+
test('Test Snapping.snap active', () => {
|
|
45
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
46
|
+
snapping.active = true;
|
|
47
|
+
// mouse near (10, 10) endpoint of the line
|
|
48
|
+
core.mouse.mouseMoved(8, 8);
|
|
49
|
+
const result = snapping.snap();
|
|
50
|
+
expect(result).toBeDefined();
|
|
51
|
+
expect(result.x).toBe(10);
|
|
52
|
+
expect(result.y).toBe(10);
|
|
53
|
+
expect(DesignCore.Scene.auxiliaryEntities.count()).toBeGreaterThan(0);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('Test Snapping.snap inactive', () => {
|
|
57
|
+
snapping.active = false;
|
|
58
|
+
core.mouse.mouseMoved(8, 8);
|
|
59
|
+
expect(snapping.snap()).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('Test Snapping.addSnapPoint', () => {
|
|
63
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
64
|
+
snapping.addSnapPoint(new Point(10, 10));
|
|
65
|
+
expect(DesignCore.Scene.auxiliaryEntities.count()).toBe(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('Test SnapPoint.draw', () => {
|
|
69
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
70
|
+
snapping.addSnapPoint(new Point(0.5, 0.5));
|
|
71
|
+
const snapPointEntity = DesignCore.Scene.auxiliaryEntities.get(0);
|
|
72
|
+
const ctx = {
|
|
73
|
+
fillStyle: '',
|
|
74
|
+
beginPath: jest.fn(),
|
|
75
|
+
arc: jest.fn(),
|
|
76
|
+
fill: jest.fn(),
|
|
77
|
+
};
|
|
78
|
+
snapPointEntity.draw(ctx, 1);
|
|
79
|
+
expect(ctx.beginPath).toHaveBeenCalled();
|
|
80
|
+
expect(ctx.arc).toHaveBeenCalledWith(0.5, 0.5, expect.any(Number), 0, 6.283);
|
|
81
|
+
expect(ctx.fill).toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('Test Snapping.addTrackingLine', () => {
|
|
85
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
86
|
+
snapping.addTrackingLine(new Point(0.2, 0.5), new Point(0.8, 0.5));
|
|
87
|
+
expect(DesignCore.Scene.auxiliaryEntities.count()).toBe(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('Test TrackingLine.draw', () => {
|
|
91
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
92
|
+
// horizontal tracking line within the default scene bounds ([0,1] x [-1,0])
|
|
93
|
+
// note: the default matrix has d=-1, so scene y is in [-1, 0] for a 1x1 canvas
|
|
94
|
+
snapping.addTrackingLine(new Point(0.2, -0.5), new Point(0.8, -0.5));
|
|
95
|
+
const trackingLine = DesignCore.Scene.auxiliaryEntities.get(0);
|
|
96
|
+
const ctx = {
|
|
97
|
+
save: jest.fn(),
|
|
98
|
+
restore: jest.fn(),
|
|
99
|
+
beginPath: jest.fn(),
|
|
100
|
+
strokeStyle: '',
|
|
101
|
+
lineWidth: 0,
|
|
102
|
+
setLineDash: jest.fn(),
|
|
103
|
+
moveTo: jest.fn(),
|
|
104
|
+
lineTo: jest.fn(),
|
|
105
|
+
stroke: jest.fn(),
|
|
106
|
+
};
|
|
107
|
+
trackingLine.draw(ctx, 1);
|
|
108
|
+
expect(ctx.save).toHaveBeenCalled();
|
|
109
|
+
expect(ctx.moveTo).toHaveBeenCalled();
|
|
110
|
+
expect(ctx.lineTo).toHaveBeenCalled();
|
|
111
|
+
expect(ctx.stroke).toHaveBeenCalled();
|
|
112
|
+
expect(ctx.restore).toHaveBeenCalled();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('Test TrackingLine.draw degenerate zero direction', () => {
|
|
116
|
+
DesignCore.Scene.auxiliaryEntities.clear();
|
|
117
|
+
// inputPoint equals snapPoint → direction is zero vector → draw returns early
|
|
118
|
+
snapping.addTrackingLine(new Point(0.5, 0.5), new Point(0.5, 0.5));
|
|
119
|
+
const trackingLine = DesignCore.Scene.auxiliaryEntities.get(0);
|
|
120
|
+
const ctx = {
|
|
121
|
+
save: jest.fn(),
|
|
122
|
+
restore: jest.fn(),
|
|
123
|
+
beginPath: jest.fn(),
|
|
124
|
+
strokeStyle: '',
|
|
125
|
+
lineWidth: 0,
|
|
126
|
+
setLineDash: jest.fn(),
|
|
127
|
+
moveTo: jest.fn(),
|
|
128
|
+
lineTo: jest.fn(),
|
|
129
|
+
stroke: jest.fn(),
|
|
130
|
+
};
|
|
131
|
+
trackingLine.draw(ctx, 1);
|
|
132
|
+
expect(ctx.moveTo).not.toHaveBeenCalled();
|
|
133
|
+
expect(ctx.stroke).not.toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
|
|
34
136
|
test('Test Snapping.polarSnap', () => {
|
|
35
137
|
// set the mouse position
|
|
36
138
|
// note mouseMoved flips the y axis
|
|
@@ -52,6 +154,42 @@ test('Test Snapping.polarSnap', () => {
|
|
|
52
154
|
// expect(polarSnapOne.y).toBeCloseTo(100);
|
|
53
155
|
});
|
|
54
156
|
|
|
157
|
+
test('Test Snapping.polarSnap within tolerance', () => {
|
|
158
|
+
snapping.active = true;
|
|
159
|
+
// Mouse at (100, 2) from previous (0, 0): angle ≈ 1.15° which is within 4° tolerance of 0°
|
|
160
|
+
core.mouse.mouseMoved(100, 2);
|
|
161
|
+
const snapPoint = snapping.polarSnap(new Point(0, 0));
|
|
162
|
+
expect(snapPoint).toBeDefined();
|
|
163
|
+
// Snapped to 0° direction → y should be approximately 0
|
|
164
|
+
expect(snapPoint.y).toBeCloseTo(0, 0);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('Test Snapping.polarSnap inactive', () => {
|
|
168
|
+
snapping.active = false;
|
|
169
|
+
core.mouse.mouseMoved(100, 2);
|
|
170
|
+
expect(snapping.polarSnap(new Point(0, 0))).toBeUndefined();
|
|
171
|
+
});
|
|
172
|
+
|
|
55
173
|
test('Test Snapping.orthoSnap', () => {
|
|
174
|
+
snapping.active = true;
|
|
175
|
+
|
|
176
|
+
// Mouse far right of previous: x delta dominates → y locked to previous y
|
|
177
|
+
// note: mouseMoved(x, -y) maps to scene (x, y) due to y-axis flip in the matrix
|
|
178
|
+
core.mouse.mouseMoved(100, -20); // scene (100, 20), previous (0, 0): |dx|=100 > |dy|=20
|
|
179
|
+
const snapPointX = snapping.orthoSnap(new Point(0, 0));
|
|
180
|
+
expect(snapPointX).toBeDefined();
|
|
181
|
+
expect(snapPointX.x).toBeCloseTo(100);
|
|
182
|
+
expect(snapPointX.y).toBeCloseTo(0);
|
|
183
|
+
|
|
184
|
+
// Mouse far above previous: y delta dominates → x locked to previous x
|
|
185
|
+
core.mouse.mouseMoved(20, -100); // scene (20, 100), previous (0, 0): |dx|=20 < |dy|=100
|
|
186
|
+
const snapPointY = snapping.orthoSnap(new Point(0, 0));
|
|
187
|
+
expect(snapPointY).toBeDefined();
|
|
188
|
+
expect(snapPointY.x).toBeCloseTo(0);
|
|
189
|
+
expect(snapPointY.y).toBeCloseTo(100);
|
|
56
190
|
|
|
191
|
+
// Inactive: returns undefined
|
|
192
|
+
snapping.active = false;
|
|
193
|
+
core.mouse.mouseMoved(100, -20);
|
|
194
|
+
expect(snapping.orthoSnap(new Point(0, 0))).toBeUndefined();
|
|
57
195
|
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Core } from '../../core/core/core.js';
|
|
2
|
+
import { StateManager } from '../../core/lib/stateManager.js';
|
|
3
|
+
import { EntityManager } from '../../core/lib/entityManager.js';
|
|
4
|
+
import { AddState, RemoveState, UpdateState } from '../../core/lib/stateManager.js';
|
|
5
|
+
import { Line } from '../../core/entities/line.js';
|
|
6
|
+
import { Point } from '../../core/entities/point.js';
|
|
7
|
+
import { jest } from '@jest/globals';
|
|
8
|
+
|
|
9
|
+
new Core();
|
|
10
|
+
|
|
11
|
+
describe('StateManager', () => {
|
|
12
|
+
let sm;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
sm = new StateManager();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('limitHistory only allows max history states', () => {
|
|
19
|
+
const em = new EntityManager();
|
|
20
|
+
for (let i = 0; i < 20; i++) {
|
|
21
|
+
sm.commit(em, [new AddState({ id: `e${i}` })]);
|
|
22
|
+
expect(sm.getHistoryLength()).toBeLessThanOrEqual(10);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('add() creates an AddState, performs do, undo and redo', () => {
|
|
27
|
+
const em = new EntityManager();
|
|
28
|
+
const entity = { id: 'e1' };
|
|
29
|
+
sm.commit(em, [new AddState(entity)]);
|
|
30
|
+
expect(em.count()).toBe(1);
|
|
31
|
+
expect(sm.getHistoryLength()).toBe(1);
|
|
32
|
+
sm.undo();
|
|
33
|
+
expect(em.count()).toBe(0);
|
|
34
|
+
expect(sm.getHistoryLength()).toBe(1);
|
|
35
|
+
sm.redo();
|
|
36
|
+
expect(em.count()).toBe(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('remove() creates a RemoveState, performs do, undo and redo', () => {
|
|
40
|
+
const em = new EntityManager();
|
|
41
|
+
const entity = { id: 'e2' };
|
|
42
|
+
sm.commit(em, [new AddState(entity)]);
|
|
43
|
+
expect(em.count()).toBe(1);
|
|
44
|
+
sm.commit(em, [new RemoveState(entity)]);
|
|
45
|
+
expect(em.count()).toBe(0);
|
|
46
|
+
sm.undo();
|
|
47
|
+
expect(em.count()).toBe(1);
|
|
48
|
+
sm.redo();
|
|
49
|
+
expect(em.count()).toBe(0);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('update() creates UpdateState and undo restores previous properties', () => {
|
|
53
|
+
const em = new EntityManager();
|
|
54
|
+
// seed entities
|
|
55
|
+
for (let i = 0; i < 5; i++) {
|
|
56
|
+
const line = new Line({ layer: 'testLayer', points: [new Point(i, i), new Point(10, 11)] });
|
|
57
|
+
sm.commit(em, [new AddState(line)]);
|
|
58
|
+
}
|
|
59
|
+
const target = em.get(2);
|
|
60
|
+
sm.commit(em, [new UpdateState(target, { layer: 'newLayer' })]);
|
|
61
|
+
expect(em.get(2).layer).toBe('newLayer');
|
|
62
|
+
sm.undo();
|
|
63
|
+
expect(em.get(2).layer).toBe('testLayer');
|
|
64
|
+
sm.redo();
|
|
65
|
+
expect(em.get(2).layer).toBe('newLayer');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('addState removed future states', () => {
|
|
69
|
+
const em = new EntityManager();
|
|
70
|
+
const line = new Line({ layer: 'testLayer', points: [new Point(), new Point(10, 11)] });
|
|
71
|
+
sm.commit(em, [new AddState(line)]);
|
|
72
|
+
expect(em.get(0).layer).toBe('testLayer');
|
|
73
|
+
sm.commit(em, [new UpdateState(line, { layer: 'differentLayer', points: [new Point(1, 2), new Point(101, 110)] })]);
|
|
74
|
+
expect(em.get(0).layer).toBe('differentLayer');
|
|
75
|
+
sm.undo();
|
|
76
|
+
expect(em.get(0).layer).toBe('testLayer');
|
|
77
|
+
sm.redo();
|
|
78
|
+
expect(em.get(0).layer).toBe('differentLayer');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('clearHistory resets history and indices', () => {
|
|
82
|
+
const em = new EntityManager();
|
|
83
|
+
sm.commit(em, [new AddState({ id: 'a' })]);
|
|
84
|
+
expect(sm.getHistoryLength()).toBe(1);
|
|
85
|
+
sm.clearHistory();
|
|
86
|
+
expect(sm.getHistoryLength()).toBe(0);
|
|
87
|
+
expect(sm.canUndo()).toBe(false);
|
|
88
|
+
expect(sm.canRedo()).toBe(false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('canUndo and canRedo basic lifecycle', () => {
|
|
92
|
+
const em = new EntityManager();
|
|
93
|
+
expect(sm.canUndo()).toBe(false);
|
|
94
|
+
expect(sm.canRedo()).toBe(false);
|
|
95
|
+
sm.commit(em, [new AddState({ id: 'first' })]);
|
|
96
|
+
expect(sm.canUndo()).toBe(true);
|
|
97
|
+
expect(sm.canRedo()).toBe(false);
|
|
98
|
+
sm.undo();
|
|
99
|
+
expect(sm.canUndo()).toBe(false);
|
|
100
|
+
expect(sm.canRedo()).toBe(true);
|
|
101
|
+
sm.redo();
|
|
102
|
+
expect(sm.canUndo()).toBe(true);
|
|
103
|
+
expect(sm.canRedo()).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('canRedo cleared after new commit following undo', () => {
|
|
107
|
+
const em = new EntityManager();
|
|
108
|
+
sm.commit(em, [new AddState({ id: 'A' })]);
|
|
109
|
+
sm.commit(em, [new AddState({ id: 'B' })]);
|
|
110
|
+
expect(sm.canUndo()).toBe(true);
|
|
111
|
+
expect(sm.canRedo()).toBe(false);
|
|
112
|
+
sm.undo();
|
|
113
|
+
expect(sm.canRedo()).toBe(true);
|
|
114
|
+
sm.commit(em, [new AddState({ id: 'C' })]);
|
|
115
|
+
expect(sm.canRedo()).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('isModified getter reflects internal flag and stateChanged invokes callback and respects parameter', () => {
|
|
119
|
+
// initial state should be not modified
|
|
120
|
+
expect(sm.isModified).toBe(false);
|
|
121
|
+
|
|
122
|
+
const cb = jest.fn();
|
|
123
|
+
sm.setStateCallbackFunction(cb);
|
|
124
|
+
|
|
125
|
+
// calling stateChanged with default should set modified to true and call callback
|
|
126
|
+
sm.stateChanged();
|
|
127
|
+
expect(sm.isModified).toBe(true);
|
|
128
|
+
expect(cb).toHaveBeenCalledTimes(1);
|
|
129
|
+
|
|
130
|
+
// calling stateChanged(false) should clear modified flag and call callback
|
|
131
|
+
sm.stateChanged(false);
|
|
132
|
+
expect(sm.isModified).toBe(false);
|
|
133
|
+
expect(cb).toHaveBeenCalledTimes(2);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('stateChanged is invoked after commit/undo/redo', () => {
|
|
137
|
+
const em = new EntityManager();
|
|
138
|
+
const cb = jest.fn();
|
|
139
|
+
sm.setStateCallbackFunction(cb);
|
|
140
|
+
|
|
141
|
+
// commit should call stateChanged once
|
|
142
|
+
sm.commit(em, [new AddState({ id: 'x' })]);
|
|
143
|
+
expect(cb).toHaveBeenCalledTimes(1);
|
|
144
|
+
expect(sm.isModified).toBe(true);
|
|
145
|
+
|
|
146
|
+
// undo should call stateChanged again
|
|
147
|
+
sm.undo();
|
|
148
|
+
expect(cb).toHaveBeenCalledTimes(2);
|
|
149
|
+
|
|
150
|
+
// redo should call stateChanged again
|
|
151
|
+
sm.redo();
|
|
152
|
+
expect(cb).toHaveBeenCalledTimes(3);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
package/test/lib/utils.test.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Arc } from '../../core/entities/arc.js';
|
|
2
|
+
import { Utils } from '../../core/lib/utils.js';
|
|
3
|
+
import { Point } from '../../core/entities/point.js';
|
|
2
4
|
|
|
3
5
|
test('Test Utils.degrees2radians', () => {
|
|
4
6
|
// Positive
|
|
@@ -6,6 +8,8 @@ test('Test Utils.degrees2radians', () => {
|
|
|
6
8
|
expect(Utils.degrees2radians(45)).toBe(Math.PI/4);
|
|
7
9
|
expect(Utils.degrees2radians(90)).toBe(Math.PI/2);
|
|
8
10
|
expect(Utils.degrees2radians(180)).toBe(Math.PI);
|
|
11
|
+
expect(Utils.degrees2radians(270)).toBe(Math.PI * 1.5);
|
|
12
|
+
expect(Utils.degrees2radians(360)).toBe(Math.PI * 2);
|
|
9
13
|
|
|
10
14
|
// Negative
|
|
11
15
|
expect(Utils.degrees2radians(-22.5)).toBe(-Math.PI/8);
|
|
@@ -34,3 +38,79 @@ test('Test Utils.getLevenshteinDistance', () => {
|
|
|
34
38
|
expect(Utils.getLevenshteinDistance('design', 'resign')).toBe(1);
|
|
35
39
|
expect(Utils.getLevenshteinDistance('design', 'sign')).toBe(2);
|
|
36
40
|
});
|
|
41
|
+
|
|
42
|
+
test('Test Utils.sortPointsByDistance mutates array ordered by distance', () => {
|
|
43
|
+
const ref = { x: 0, y: 0 };
|
|
44
|
+
const pts = [
|
|
45
|
+
{ x: 3, y: 4 }, // dist^2 = 25
|
|
46
|
+
{ x: 1, y: 1 }, // dist^2 = 2
|
|
47
|
+
{ x: -2, y: 0 }, // dist^2 = 4
|
|
48
|
+
{ x: 0, y: 5 }, // dist^2 = 25
|
|
49
|
+
];
|
|
50
|
+
Utils.sortPointsByDistance(pts, ref);
|
|
51
|
+
// Expect ascending order: (1,1) ( -2,0 ) then the two distance=25 points (order between them not critical)
|
|
52
|
+
expect(pts[0].x).toBe(1);
|
|
53
|
+
expect(pts[0].y).toBe(1);
|
|
54
|
+
expect(pts[1].x).toBe(-2);
|
|
55
|
+
expect(pts[1].y).toBe(0);
|
|
56
|
+
const lastTwo = pts.slice(2).map((p) => `${p.x},${p.y}`);
|
|
57
|
+
expect(lastTwo.sort()).toEqual(['0,5', '3,4']);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('Test Utils.sortPointsByDistance with empty array does nothing', () => {
|
|
61
|
+
const ref = { x: 5, y: 5 };
|
|
62
|
+
const pts = [];
|
|
63
|
+
Utils.sortPointsByDistance(pts, ref);
|
|
64
|
+
expect(pts).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('Test Utils.sortPointsOnArc mutates array ordered by angular position', () => {
|
|
68
|
+
const pts = [
|
|
69
|
+
new Point(7.1, 7.1), // 45 degree
|
|
70
|
+
new Point(10, 0), // end
|
|
71
|
+
new Point(-10, 0), // start
|
|
72
|
+
new Point(-7.1, 7.1), // 45 degree
|
|
73
|
+
new Point(10, 10), // mid
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(-10, 0)], direction: 1 });
|
|
77
|
+
Utils.sortPointsOnArc(pts, arc.points[1], arc.points[2], arc.points[0], arc.direction);
|
|
78
|
+
// Expect order: (10,0), (7.1, 7.1), (10,10), (-7.1, 7.1), (-10,0)
|
|
79
|
+
expect(pts[0].x).toBe(10);
|
|
80
|
+
expect(pts[0].y).toBe(0);
|
|
81
|
+
|
|
82
|
+
expect(pts[1].x).toBe(7.1);
|
|
83
|
+
expect(pts[1].y).toBe(7.1);
|
|
84
|
+
|
|
85
|
+
expect(pts[2].x).toBe(10);
|
|
86
|
+
expect(pts[2].y).toBe(10);
|
|
87
|
+
|
|
88
|
+
expect(pts[3].x).toBe(-7.1);
|
|
89
|
+
expect(pts[3].y).toBe(7.1);
|
|
90
|
+
|
|
91
|
+
expect(pts[4].x).toBe(-10);
|
|
92
|
+
expect(pts[4].y).toBe(0);
|
|
93
|
+
|
|
94
|
+
const cwArc = new Arc({ points: [new Point(0, 0), new Point(-10, 0), new Point(10, 0)], direction: -1 });
|
|
95
|
+
Utils.sortPointsOnArc(pts, cwArc.points[1], cwArc.points[2], cwArc.points[0], cwArc.direction);
|
|
96
|
+
// Expect order: (-10,0), (-7.1, 7.1), (10,10), (7.1, 7.1), (10,0)
|
|
97
|
+
expect(pts[0].x).toBe(-10);
|
|
98
|
+
expect(pts[0].y).toBe(0);
|
|
99
|
+
|
|
100
|
+
expect(pts[1].x).toBe(-7.1);
|
|
101
|
+
expect(pts[1].y).toBe(7.1);
|
|
102
|
+
|
|
103
|
+
expect(pts[2].x).toBe(10);
|
|
104
|
+
expect(pts[2].y).toBe(10);
|
|
105
|
+
|
|
106
|
+
expect(pts[3].x).toBe(7.1);
|
|
107
|
+
expect(pts[3].y).toBe(7.1);
|
|
108
|
+
|
|
109
|
+
expect(pts[4].x).toBe(10);
|
|
110
|
+
expect(pts[4].y).toBe(0);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('Test Utils.areaOfTriangle', () => {
|
|
114
|
+
expect(Utils.areaOfTriangle(new Point(0, 0), new Point(10, 0), new Point(0, 10))).toBe(50);
|
|
115
|
+
expect(Utils.areaOfTriangle(new Point(-5, -5), new Point(5, -5), new Point(0, 5))).toBe(50);
|
|
116
|
+
});
|