@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,85 +1,507 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {Point} from '../../core/entities/point.js';
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { Strings } from '../../core/lib/strings.js';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
describe('Arc.execute input validation and notifications', () => {
|
|
5
|
+
let notifySpy;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
notifySpy = jest.spyOn(DesignCore.Core, 'notify').mockImplementation(() => {});
|
|
8
|
+
});
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
notifySpy.mockRestore();
|
|
11
|
+
});
|
|
7
12
|
|
|
8
|
-
test('
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
test('Notifies when arc angle is too small (point input)', async () => {
|
|
14
|
+
// Three points nearly colinear (angle ~0)
|
|
15
|
+
const input1 = new Point(0, 0);
|
|
16
|
+
const input2 = new Point(10, 0);
|
|
17
|
+
const input3 = new Point(20, 0.0001); // Very small angle
|
|
18
|
+
await withMockInput(DesignCore.Scene, [input1, input2, input3, new Point(10, 10)], async () => {
|
|
19
|
+
const arc = new Arc({});
|
|
20
|
+
await arc.execute();
|
|
21
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.INVALIDNUMBER));
|
|
22
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.MINVALUE));
|
|
23
|
+
});
|
|
24
|
+
});
|
|
12
25
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
26
|
+
test('Notifies when arc angle is too small (angle input)', async () => {
|
|
27
|
+
const input1 = new Point(0, 0);
|
|
28
|
+
const input2 = new Point(10, 0);
|
|
29
|
+
const input3 = 0.5; // degrees, too small
|
|
30
|
+
await withMockInput(DesignCore.Scene, [input1, input2, input3, 90], async () => {
|
|
31
|
+
const arc = new Arc({});
|
|
32
|
+
await arc.execute();
|
|
33
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.INVALIDNUMBER));
|
|
34
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.MINVALUE));
|
|
35
|
+
});
|
|
36
|
+
});
|
|
16
37
|
|
|
38
|
+
test('Notifies on invalid number input', async () => {
|
|
39
|
+
const input1 = new Point(0, 0);
|
|
40
|
+
const input2 = new Point(10, 0);
|
|
41
|
+
const input3 = 'not-a-number';
|
|
42
|
+
await withMockInput(DesignCore.Scene, [input1, input2, input3, 90], async () => {
|
|
43
|
+
const arc = new Arc({});
|
|
44
|
+
await arc.execute();
|
|
45
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.INPUT));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
17
48
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
49
|
+
test('Notifies on invalid point input (duplicate)', async () => {
|
|
50
|
+
const input1 = new Point(0, 0);
|
|
51
|
+
const input2 = new Point(10, 0);
|
|
52
|
+
const input3 = new Point(0, 0); // Same as center
|
|
53
|
+
await withMockInput(DesignCore.Scene, [input1, input2, input3, new Point(10, 10)], async () => {
|
|
54
|
+
const arc = new Arc({});
|
|
55
|
+
await arc.execute();
|
|
56
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.INVALIDPOINT));
|
|
57
|
+
});
|
|
58
|
+
});
|
|
21
59
|
|
|
60
|
+
test('Accepts valid input and does not notify errors', async () => {
|
|
61
|
+
const input1 = new Point(0, 0);
|
|
62
|
+
const input2 = new Point(10, 0);
|
|
63
|
+
const input3 = new Point(10, 10);
|
|
64
|
+
await withMockInput(DesignCore.Scene, [input1, input2, input3], async () => {
|
|
65
|
+
const arc = new Arc({});
|
|
66
|
+
await arc.execute();
|
|
67
|
+
// Should not notify any errors
|
|
68
|
+
expect(notifySpy).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
import { Core } from '../../core/core/core.js';
|
|
73
|
+
import { Arc } from '../../core/entities/arc.js';
|
|
74
|
+
import { Point } from '../../core/entities/point.js';
|
|
75
|
+
import { DesignCore } from '../../core/designCore.js';
|
|
76
|
+
import { AddState, RemoveState } from '../../core/lib/stateManager.js';
|
|
77
|
+
|
|
78
|
+
import { File, withMockInput } from '../test-helpers/test-helpers.js';
|
|
79
|
+
|
|
80
|
+
// initialise core
|
|
81
|
+
new Core();
|
|
82
|
+
|
|
83
|
+
const arcInputScenarios = [
|
|
84
|
+
{
|
|
85
|
+
desc: 'two points and an angle',
|
|
86
|
+
input1: new Point(0, 0),
|
|
87
|
+
input2: new Point(10, 0),
|
|
88
|
+
input3: 90, // degrees
|
|
89
|
+
expectedPt2: (input1, input2) => input2.rotate(input1, Math.PI / 2),
|
|
90
|
+
expectedStart: 0,
|
|
91
|
+
expectedEnd: Math.PI / 2,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
desc: 'three points',
|
|
95
|
+
input1: new Point(0, 0),
|
|
96
|
+
input2: new Point(10, 0),
|
|
97
|
+
input3: new Point(10, 10),
|
|
98
|
+
expectedPt2: (input1, input2, input3) => input3,
|
|
99
|
+
expectedStart: 0,
|
|
100
|
+
expectedEnd: Math.PI / 4,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
test.each(arcInputScenarios)('Arc.execute handles $desc', async (scenario) => {
|
|
105
|
+
const { input1, input2, input3, expectedPt2, expectedStart, expectedEnd } = scenario;
|
|
106
|
+
|
|
107
|
+
await withMockInput(DesignCore.Scene, [input1, input2, input3, input3], async () => {
|
|
108
|
+
const arc = new Arc({});
|
|
109
|
+
await arc.execute();
|
|
22
110
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
111
|
+
expect(arc.points.length).toBe(3);
|
|
112
|
+
expect(arc.points[0]).toBe(input1);
|
|
113
|
+
expect(arc.points[1]).toBe(input2);
|
|
114
|
+
const expPt2 = expectedPt2(input1, input2, input3);
|
|
115
|
+
expect(arc.points[2].x).toBeCloseTo(expPt2.x);
|
|
116
|
+
expect(arc.points[2].y).toBeCloseTo(expPt2.y);
|
|
26
117
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// expect(DesignCore.Scene.items.length).toBe(1),
|
|
118
|
+
expect(arc.startAngle()).toBe(expectedStart);
|
|
119
|
+
expect(arc.endAngle()).toBeCloseTo(expectedEnd);
|
|
120
|
+
});
|
|
31
121
|
});
|
|
32
122
|
|
|
33
123
|
test('Test Arc.startAngle', () => {
|
|
34
124
|
// clockwise 45 degrees 0 - 45
|
|
35
|
-
let arc = new Arc({points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)]});
|
|
125
|
+
let arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)] });
|
|
36
126
|
expect(arc.startAngle()).toBe(0);
|
|
37
127
|
|
|
38
128
|
// clockwise 45 degrees 45 - 90
|
|
39
|
-
arc = new Arc({points: [new Point(100, 100), new Point(170.71, 170.71), new Point(100, 200)]});
|
|
129
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(170.71, 170.71), new Point(100, 200)] });
|
|
40
130
|
expect(arc.startAngle()).toBeCloseTo(Math.PI / 4);
|
|
41
131
|
});
|
|
42
132
|
|
|
43
133
|
test('Test Arc.endAngle', () => {
|
|
44
134
|
// clockwise 45 degrees 0 - 45
|
|
45
|
-
let arc = new Arc({points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)]});
|
|
135
|
+
let arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)] });
|
|
46
136
|
expect(arc.endAngle()).toBe(Math.PI / 4);
|
|
47
137
|
|
|
48
138
|
// clockwise 45 degrees 45 - 90
|
|
49
|
-
arc = new Arc({points: [new Point(100, 100), new Point(170.71, 170.71), new Point(100, 200)]});
|
|
139
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(170.71, 170.71), new Point(100, 200)] });
|
|
50
140
|
expect(arc.endAngle()).toBeCloseTo(Math.PI / 2);
|
|
51
141
|
});
|
|
52
142
|
|
|
53
|
-
test('Test Arc.
|
|
143
|
+
test('Test Arc.totalAngle', () => {
|
|
54
144
|
// clockwise 45 degrees 0 - 45
|
|
55
|
-
|
|
145
|
+
let arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: 0 });
|
|
146
|
+
expect(arc.totalAngle).toBe(315);
|
|
147
|
+
|
|
148
|
+
// clockwise 45 degrees 45 - 90
|
|
149
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(170.71, 170.71), new Point(100, 200)], direction: 0 });
|
|
150
|
+
expect(arc.totalAngle).toBeCloseTo(315);
|
|
151
|
+
|
|
152
|
+
// clockwise 90 degrees 0 - 90
|
|
153
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(100, 200)], direction: 0 });
|
|
154
|
+
expect(arc.totalAngle).toBeCloseTo(270);
|
|
155
|
+
|
|
156
|
+
// clockwise 90 degrees 45 - 315
|
|
157
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(170.71, 170.71), new Point(170.71, 29.29)], direction: 0 });
|
|
158
|
+
expect(arc.totalAngle).toBeCloseTo(90);
|
|
159
|
+
|
|
160
|
+
// clockwise 180 degrees 0 - 180
|
|
161
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(0, 100)], direction: 0 });
|
|
162
|
+
expect(arc.totalAngle).toBeCloseTo(180);
|
|
163
|
+
|
|
164
|
+
// clockwise 180 degrees 90 - 270
|
|
165
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(100, 200), new Point(100, 0)], direction: 0 });
|
|
166
|
+
expect(arc.totalAngle).toBeCloseTo(180);
|
|
167
|
+
|
|
168
|
+
// clockwise 360 degrees 0 - 360
|
|
169
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(100, 200), new Point(100, 200)], direction: 0 });
|
|
170
|
+
expect(arc.totalAngle).toBeCloseTo(360);
|
|
171
|
+
|
|
172
|
+
/* counter clockwise */
|
|
173
|
+
|
|
174
|
+
// counter clockwise 45 degrees 0 - 45
|
|
175
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: 1 });
|
|
176
|
+
expect(arc.totalAngle).toBeCloseTo(-45);
|
|
177
|
+
|
|
178
|
+
// counter clockwise 315 degrees 45 - 0
|
|
179
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(170.71, 170.71), new Point(200, 100)], direction: 1 });
|
|
180
|
+
expect(arc.totalAngle).toBeCloseTo(-315);
|
|
181
|
+
|
|
182
|
+
// counter clockwise 180 degrees 270 - 90
|
|
183
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(100, 0), new Point(100, 200)], direction: 1 });
|
|
184
|
+
expect(arc.totalAngle).toBeCloseTo(-180);
|
|
185
|
+
|
|
186
|
+
// counter clockwise 135 degrees 270 - 45
|
|
187
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(100, 0), new Point(170.71, 170.71)], direction: 1 });
|
|
188
|
+
expect(arc.totalAngle).toBeCloseTo(-135);
|
|
189
|
+
|
|
190
|
+
// counter clockwise 270 degrees 45 - 315
|
|
191
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(170.71, 170.71), new Point(170.71, 29.29)], direction: 1 });
|
|
192
|
+
expect(arc.totalAngle).toBeCloseTo(-270);
|
|
193
|
+
|
|
194
|
+
// clockwise 0 degrees 0 - 360
|
|
195
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(100, 200), new Point(100, 200)], direction: 1 });
|
|
196
|
+
expect(arc.totalAngle).toBeCloseTo(-360);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
test('Test Arc.closestPoint', () => {
|
|
201
|
+
// clockwise 315 degrees 0 - 45
|
|
202
|
+
// direction: ccw > 0, cw <= 0
|
|
203
|
+
let arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: 0 });
|
|
204
|
+
// inside
|
|
205
|
+
let point = new Point(128.3525, 169.4344);
|
|
206
|
+
let closest = arc.closestPoint(point);
|
|
207
|
+
expect(closest[0].x).toBeCloseTo(137.8);
|
|
208
|
+
expect(closest[0].y).toBeCloseTo(192.578);
|
|
209
|
+
expect(closest[1]).toBeCloseTo(25);
|
|
210
|
+
|
|
211
|
+
// outside
|
|
212
|
+
point = new Point(147.2541, 215.7240);
|
|
213
|
+
closest = arc.closestPoint(point);
|
|
214
|
+
expect(closest[0].x).toBeCloseTo(137.8);
|
|
215
|
+
expect(closest[0].y).toBeCloseTo(192.578);
|
|
216
|
+
expect(closest[1]).toBeCloseTo(25);
|
|
217
|
+
|
|
218
|
+
// counter clockwise 45 degrees 0 - 45
|
|
219
|
+
// direction: ccw > 0, cw <= 0
|
|
220
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: 1 });
|
|
56
221
|
// inside
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
61
|
-
expect(
|
|
222
|
+
point = new Point(128.3525, 169.4344);
|
|
223
|
+
closest = arc.closestPoint(point);
|
|
224
|
+
expect(closest[0].x).toBeCloseTo(point.x);
|
|
225
|
+
expect(closest[0].y).toBeCloseTo(point.y);
|
|
226
|
+
expect(closest[1]).toBeCloseTo(Infinity);
|
|
62
227
|
|
|
63
228
|
// outside
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
expect(
|
|
67
|
-
expect(
|
|
68
|
-
expect(
|
|
229
|
+
point = new Point(147.2541, 215.7240);
|
|
230
|
+
closest = arc.closestPoint(point);
|
|
231
|
+
expect(closest[0].x).toBeCloseTo(point.x);
|
|
232
|
+
expect(closest[0].y).toBeCloseTo(point.y);
|
|
233
|
+
expect(closest[1]).toBeCloseTo(Infinity);
|
|
69
234
|
});
|
|
70
235
|
|
|
71
236
|
test('Test Arc.boundingBox', () => {
|
|
72
|
-
// clockwise
|
|
73
|
-
let arc = new Arc({points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)]});
|
|
237
|
+
// clockwise 315 degrees 0 - 45
|
|
238
|
+
let arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)] });
|
|
74
239
|
expect(arc.boundingBox().xMin).toBeCloseTo(170.71);
|
|
75
240
|
expect(arc.boundingBox().xMax).toBeCloseTo(200);
|
|
76
241
|
expect(arc.boundingBox().yMin).toBeCloseTo(100);
|
|
77
242
|
expect(arc.boundingBox().yMax).toBeCloseTo(170.71);
|
|
78
243
|
|
|
79
244
|
// anticlockwise 45 degrees: 0 - 45
|
|
80
|
-
arc = new Arc({points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: -1});
|
|
245
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: -1 });
|
|
81
246
|
expect(arc.boundingBox().xMin).toBeCloseTo(0);
|
|
82
247
|
expect(arc.boundingBox().xMax).toBeCloseTo(200);
|
|
83
248
|
expect(arc.boundingBox().yMin).toBeCloseTo(0);
|
|
84
249
|
expect(arc.boundingBox().yMax).toBeCloseTo(200);
|
|
85
250
|
});
|
|
251
|
+
|
|
252
|
+
test('Test Arc.dxf', () => {
|
|
253
|
+
const arc = new Arc({ handle: '1', points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)] });
|
|
254
|
+
let file = new File();
|
|
255
|
+
arc.dxf(file);
|
|
256
|
+
// console.log(file.contents);
|
|
257
|
+
|
|
258
|
+
const dxfString = `0
|
|
259
|
+
ARC
|
|
260
|
+
5
|
|
261
|
+
1
|
|
262
|
+
100
|
|
263
|
+
AcDbEntity
|
|
264
|
+
100
|
|
265
|
+
AcDbCircle
|
|
266
|
+
8
|
|
267
|
+
0
|
|
268
|
+
6
|
|
269
|
+
ByLayer
|
|
270
|
+
10
|
|
271
|
+
100
|
|
272
|
+
20
|
|
273
|
+
100
|
|
274
|
+
30
|
|
275
|
+
0.0
|
|
276
|
+
39
|
|
277
|
+
2
|
|
278
|
+
40
|
|
279
|
+
100
|
|
280
|
+
100
|
|
281
|
+
AcDbArc
|
|
282
|
+
50
|
|
283
|
+
0
|
|
284
|
+
51
|
|
285
|
+
45
|
|
286
|
+
`;
|
|
287
|
+
|
|
288
|
+
expect(file.contents).toEqual(dxfString);
|
|
289
|
+
|
|
290
|
+
// create new entity from entity data to ensure all props are loaded
|
|
291
|
+
const newArc = new Arc(arc);
|
|
292
|
+
file = new File();
|
|
293
|
+
newArc.dxf(file);
|
|
294
|
+
expect(file.contents).toEqual(dxfString);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
test('Test Arc.toPolylinePoints', () => {
|
|
299
|
+
// clockwise 45 degrees 0 - 45
|
|
300
|
+
let arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: 0 });
|
|
301
|
+
let arcPoints = arc.toPolylinePoints();
|
|
302
|
+
expect(arcPoints[0].x).toBe(200);
|
|
303
|
+
expect(arcPoints[0].y).toBe(100);
|
|
304
|
+
expect(arcPoints[0].bulge).toBeCloseTo(-5.02734);
|
|
305
|
+
|
|
306
|
+
expect(arcPoints[1].x).toBeCloseTo(170.71);
|
|
307
|
+
expect(arcPoints[1].y).toBeCloseTo(170.71);
|
|
308
|
+
expect(arcPoints[1].bulge).toBeCloseTo(0);
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
// counter clockwise 45 degrees 0 - 45
|
|
312
|
+
arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)], direction: 1 });
|
|
313
|
+
arcPoints = arc.toPolylinePoints();
|
|
314
|
+
expect(arcPoints[0].x).toBe(200);
|
|
315
|
+
expect(arcPoints[0].y).toBe(100);
|
|
316
|
+
expect(arcPoints[0].bulge).toBeCloseTo(0.1989);
|
|
317
|
+
|
|
318
|
+
expect(arcPoints[1].x).toBeCloseTo(170.71);
|
|
319
|
+
expect(arcPoints[1].y).toBeCloseTo(170.71);
|
|
320
|
+
expect(arcPoints[1].bulge).toBeCloseTo(0);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test('Arc constructor handles missing/invalid data', () => {
|
|
324
|
+
// No points
|
|
325
|
+
expect(() => new Arc({})).not.toThrow();
|
|
326
|
+
// Invalid points
|
|
327
|
+
expect(() => new Arc({ points: [null, undefined, {}] })).not.toThrow();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
test('Arc.trim returns empty for no intersections', () => {
|
|
331
|
+
const arc = new Arc({ points: [new Point(100, 100), new Point(200, 100), new Point(170.71, 170.71)] });
|
|
332
|
+
expect(arc.trim([])).toEqual([]);
|
|
333
|
+
expect(arc.trim()).toEqual([]);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
test('Arc.trim returns remove + add states when trimming between two intersections', () => {
|
|
338
|
+
const arc = new Arc({ points: [new Point(), new Point(10, 0), new Point(-10, 0)], handle: 'B2' });
|
|
339
|
+
// Intersection points on the arc.
|
|
340
|
+
const i1 = new Point(7.71, 7.71);
|
|
341
|
+
const i2 = new Point(-7.71, 7.71);
|
|
342
|
+
const intersections = [i1, i2];
|
|
343
|
+
|
|
344
|
+
// set the mouse position to mid point of the arc
|
|
345
|
+
DesignCore.Mouse.pointOnScene = () => new Point(0, 10);
|
|
346
|
+
|
|
347
|
+
const changes = arc.trim(intersections);
|
|
348
|
+
|
|
349
|
+
expect(Array.isArray(changes)).toBe(true);
|
|
350
|
+
expect(changes.length).toBeGreaterThanOrEqual(2);
|
|
351
|
+
|
|
352
|
+
expect(changes[0]).toBeInstanceOf(AddState);
|
|
353
|
+
expect(changes[1]).toBeInstanceOf(AddState);
|
|
354
|
+
expect(changes[2]).toBeInstanceOf(RemoveState);
|
|
355
|
+
|
|
356
|
+
// Verify trimmed arcs have unique handles (not the original)
|
|
357
|
+
expect(changes[0].entity.handle).toBeUndefined();
|
|
358
|
+
expect(changes[1].entity.handle).toBeUndefined();
|
|
359
|
+
|
|
360
|
+
expect(changes[0].entity.points[0].x).toBe(0);
|
|
361
|
+
expect(changes[0].entity.points[0].y).toBe(0);
|
|
362
|
+
|
|
363
|
+
expect(changes[0].entity.points[1].x).toBeCloseTo(10);
|
|
364
|
+
expect(changes[0].entity.points[1].y).toBeCloseTo(0);
|
|
365
|
+
expect(changes[0].entity.points[2].x).toBeCloseTo(7.71);
|
|
366
|
+
expect(changes[0].entity.points[2].y).toBeCloseTo(7.71);
|
|
367
|
+
|
|
368
|
+
expect(changes[1].entity.points[1].x).toBeCloseTo(-7.71);
|
|
369
|
+
expect(changes[1].entity.points[1].y).toBeCloseTo(7.71);
|
|
370
|
+
expect(changes[1].entity.points[2].x).toBe(-10);
|
|
371
|
+
expect(changes[1].entity.points[2].y).toBe(0);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('Arc.getRadius returns the arc radius', () => {
|
|
375
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(0, 10)] });
|
|
376
|
+
expect(arc.getRadius()).toBe(10);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test('Arc constructor loads from DXF group codes', () => {
|
|
380
|
+
const arc = new Arc({ points: [new Point(100, 100)], 40: 50, 50: 0, 51: 90 });
|
|
381
|
+
expect(arc.radius).toBe(50);
|
|
382
|
+
expect(arc.points[1].x).toBeCloseTo(150);
|
|
383
|
+
expect(arc.points[1].y).toBeCloseTo(100);
|
|
384
|
+
expect(arc.points[2].x).toBeCloseTo(100);
|
|
385
|
+
expect(arc.points[2].y).toBeCloseTo(150);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
test('Arc constructor loads direction from data', () => {
|
|
389
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(0, 10)], direction: -1 });
|
|
390
|
+
expect(arc.direction).toBe(-1);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
test('Arc.toPolylinePoints handles a full circle (360 degrees)', () => {
|
|
394
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 0)], direction: 1 });
|
|
395
|
+
const pts = arc.toPolylinePoints();
|
|
396
|
+
expect(pts).toHaveLength(3);
|
|
397
|
+
expect(pts[0].bulge).toBe(1);
|
|
398
|
+
expect(pts[1].bulge).toBe(1);
|
|
399
|
+
expect(pts[2].x).toBeCloseTo(pts[0].x);
|
|
400
|
+
expect(pts[2].y).toBeCloseTo(pts[0].y);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test('Arc.preview does not throw with 1, 2 or 3 points', () => {
|
|
404
|
+
const origCreate = DesignCore.Scene.tempEntities.create;
|
|
405
|
+
const origMouse = DesignCore.Mouse.pointOnScene;
|
|
406
|
+
DesignCore.Scene.tempEntities.create = () => {};
|
|
407
|
+
DesignCore.Mouse.pointOnScene = () => new Point(5, 5);
|
|
408
|
+
|
|
409
|
+
const arc1 = new Arc({});
|
|
410
|
+
arc1.points = [new Point(0, 0)];
|
|
411
|
+
expect(() => arc1.preview()).not.toThrow();
|
|
412
|
+
|
|
413
|
+
const arc2 = new Arc({});
|
|
414
|
+
arc2.points = [new Point(0, 0), new Point(10, 0)];
|
|
415
|
+
expect(() => arc2.preview()).not.toThrow();
|
|
416
|
+
|
|
417
|
+
const arc3 = new Arc({});
|
|
418
|
+
arc3.points = [new Point(0, 0), new Point(10, 0), new Point(0, 10)];
|
|
419
|
+
expect(() => arc3.preview()).not.toThrow();
|
|
420
|
+
|
|
421
|
+
DesignCore.Scene.tempEntities.create = origCreate;
|
|
422
|
+
DesignCore.Mouse.pointOnScene = origMouse;
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test('Arc.draw does not throw (ccw and cw)', () => {
|
|
426
|
+
const ctx = { arc: jest.fn(), stroke: jest.fn() };
|
|
427
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(0, 10)], direction: 1 });
|
|
428
|
+
expect(() => arc.draw(ctx, 1)).not.toThrow();
|
|
429
|
+
expect(ctx.arc).toHaveBeenCalledTimes(1);
|
|
430
|
+
expect(ctx.stroke).toHaveBeenCalledTimes(1);
|
|
431
|
+
|
|
432
|
+
const ctx2 = { arc: jest.fn(), stroke: jest.fn() };
|
|
433
|
+
const arcCw = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(0, 10)], direction: -1 });
|
|
434
|
+
expect(() => arcCw.draw(ctx2, 1)).not.toThrow();
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test('Arc.snaps returns end, centre and nearest snap points', () => {
|
|
438
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(0, 10)], direction: 1 });
|
|
439
|
+
|
|
440
|
+
DesignCore.Settings.endsnap = true;
|
|
441
|
+
DesignCore.Settings.centresnap = true;
|
|
442
|
+
DesignCore.Settings.nearestsnap = false;
|
|
443
|
+
const snaps = arc.snaps(new Point(5, 5), 10);
|
|
444
|
+
expect(snaps.length).toBe(3); // start, end, centre
|
|
445
|
+
|
|
446
|
+
// nearestsnap: closestPoint returns [pnt, distance] (no third element),
|
|
447
|
+
// so closest[2] === true is never satisfied and no snap is pushed
|
|
448
|
+
DesignCore.Settings.endsnap = false;
|
|
449
|
+
DesignCore.Settings.centresnap = false;
|
|
450
|
+
DesignCore.Settings.nearestsnap = true;
|
|
451
|
+
const nearSnaps = arc.snaps(new Point(10, 0), 100);
|
|
452
|
+
expect(nearSnaps.length).toBe(0);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
test('Arc.execute handles negative angle (clockwise)', async () => {
|
|
456
|
+
const input1 = new Point(0, 0);
|
|
457
|
+
const input2 = new Point(10, 0);
|
|
458
|
+
const input3 = -90; // degrees — clockwise
|
|
459
|
+
await withMockInput(DesignCore.Scene, [input1, input2, input3], async () => {
|
|
460
|
+
const arc = new Arc({});
|
|
461
|
+
await arc.execute();
|
|
462
|
+
expect(arc.points.length).toBe(3);
|
|
463
|
+
expect(arc.direction).toBe(-1);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('Arc.execute notifies on angle > 360', async () => {
|
|
468
|
+
const notifySpy = jest.spyOn(DesignCore.Core, 'notify').mockImplementation(() => {});
|
|
469
|
+
await withMockInput(DesignCore.Scene, [new Point(0, 0), new Point(10, 0), 361, 90], async () => {
|
|
470
|
+
const arc = new Arc({});
|
|
471
|
+
await arc.execute();
|
|
472
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.INVALIDNUMBER));
|
|
473
|
+
});
|
|
474
|
+
notifySpy.mockRestore();
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test('Arc.trim returns empty when only intersection is at an endpoint', () => {
|
|
478
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(-10, 0)] });
|
|
479
|
+
// intersection exactly at the start point — filtered out, nothing to trim
|
|
480
|
+
const changes = arc.trim([new Point(10, 0)]);
|
|
481
|
+
expect(changes).toEqual([]);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
test('Arc.fromPolylinePoints round-trip preserves centre, radius and angles', () => {
|
|
485
|
+
const arc = new Arc({ points: [new Point(0, 0), new Point(10, 0), new Point(0, 10)] });
|
|
486
|
+
arc.radius = 10;
|
|
487
|
+
const polyPts = arc.toPolylinePoints();
|
|
488
|
+
const rebuilt = new Arc({});
|
|
489
|
+
rebuilt.fromPolylinePoints(polyPts);
|
|
490
|
+
expect(rebuilt.points[0].x).toBeCloseTo(0, 10);
|
|
491
|
+
expect(rebuilt.points[0].y).toBeCloseTo(0, 10);
|
|
492
|
+
expect(rebuilt.radius).toBeCloseTo(10, 10);
|
|
493
|
+
expect(rebuilt.startAngle()).toBeCloseTo(arc.startAngle(), 10);
|
|
494
|
+
expect(rebuilt.endAngle()).toBeCloseTo(arc.endAngle(), 10);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
test('Arc.fromPolylinePoints precision drift is negligible', () => {
|
|
498
|
+
const arc = new Arc({ points: [new Point(50, 50), new Point(50, 50).project(0.7, 30), new Point(50, 50).project(2.1, 30)] });
|
|
499
|
+
arc.radius = 30;
|
|
500
|
+
const polyPts = arc.toPolylinePoints();
|
|
501
|
+
const rebuilt = new Arc({});
|
|
502
|
+
rebuilt.fromPolylinePoints(polyPts);
|
|
503
|
+
const centerDrift = Math.hypot(rebuilt.points[0].x - arc.points[0].x, rebuilt.points[0].y - arc.points[0].y);
|
|
504
|
+
expect(centerDrift).toBeLessThan(1e-10);
|
|
505
|
+
expect(Math.abs(rebuilt.radius - arc.radius)).toBeLessThan(1e-10);
|
|
506
|
+
});
|
|
507
|
+
|