@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,11 +1,134 @@
|
|
|
1
|
-
import {BasePolyline} from '../../core/entities/basePolyline';
|
|
2
|
-
import {Point} from '../../core/entities/point';
|
|
1
|
+
import { BasePolyline } from '../../core/entities/basePolyline.js';
|
|
2
|
+
import { Point } from '../../core/entities/point';
|
|
3
|
+
import { Polyline } from '../../core/entities/polyline.js';
|
|
4
|
+
import { Line } from '../../core/entities/line.js';
|
|
5
|
+
import { Arc } from '../../core/entities/arc.js';
|
|
6
|
+
import { Core } from '../../core/core/core.js';
|
|
7
|
+
import { DesignCore } from '../../core/designCore.js';
|
|
8
|
+
import { DXFFile } from '../../core/lib/dxf/dxfFile.js';
|
|
9
|
+
import { Flags } from '../../core/properties/flags.js';
|
|
10
|
+
|
|
11
|
+
import { File, withMockInput } from '../test-helpers/test-helpers.js';
|
|
12
|
+
|
|
13
|
+
// initialise core
|
|
14
|
+
new Core();
|
|
15
|
+
|
|
16
|
+
const inputScenarios = [
|
|
17
|
+
{
|
|
18
|
+
desc: 'two points and an angle',
|
|
19
|
+
inputs: [new Point(0, 0), new Point(10, 0)],
|
|
20
|
+
expectedPoints: 2,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
desc: 'three points with arc segment',
|
|
24
|
+
inputs: [new Point(0, 0), new Point(10, 0), 'Arc', new Point(10, 10)],
|
|
25
|
+
expectedPoints: 3,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
test.each(inputScenarios)('Polyline.execute handles $desc', async (scenario) => {
|
|
30
|
+
const { inputs, expectedPoints } = scenario;
|
|
31
|
+
|
|
32
|
+
await withMockInput(DesignCore.Scene, inputs, async () => {
|
|
33
|
+
const polyline = new BasePolyline({});
|
|
34
|
+
await polyline.execute();
|
|
35
|
+
|
|
36
|
+
expect(polyline.points.length).toBe(expectedPoints);
|
|
37
|
+
expect(polyline.points[0]).toBe(inputs[0]);
|
|
38
|
+
expect(polyline.points[0].bulge).toBe(0);
|
|
39
|
+
expect(polyline.points[1]).toBe(inputs[1]);
|
|
40
|
+
|
|
41
|
+
// validate arc segment bulge and end point
|
|
42
|
+
if (inputs.length > 2) {
|
|
43
|
+
expect(polyline.points[1].bulge).toBeCloseTo(1);
|
|
44
|
+
expect(polyline.points[2]).toBe(inputs[3]);
|
|
45
|
+
}
|
|
46
|
+
}, { extraMethods: { actionCommand: () => {} } });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('Test BasePolyline.getClosestSegment', () => {
|
|
50
|
+
// Polyline with a line and an arc segment
|
|
51
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10)];
|
|
52
|
+
points[1].bulge = 1; // Arc from (10,0) to (10,10)
|
|
53
|
+
const polyline = new BasePolyline({ points });
|
|
54
|
+
|
|
55
|
+
// Closest to the line segment
|
|
56
|
+
const testPoint1 = new Point(5, 2);
|
|
57
|
+
const segment1 = polyline.getClosestSegment(testPoint1);
|
|
58
|
+
expect(segment1).toBeInstanceOf(Line);
|
|
59
|
+
|
|
60
|
+
// Closest to the arc segment
|
|
61
|
+
const testPoint2 = new Point(12, 5);
|
|
62
|
+
const segment2 = polyline.getClosestSegment(testPoint2);
|
|
63
|
+
expect(segment2).toBeInstanceOf(Arc);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ─── closing segment (closed polylines) ──────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
test('BasePolyline.getClosestSegmentIndex returns closing-segment index for closed polyline', () => {
|
|
69
|
+
// Square: P0=(0,0) P1=(10,0) P2=(10,10) P3=(0,10), closed flag set.
|
|
70
|
+
// Closing segment is P3→P0. A point near the left edge (0,5) is closest to that segment.
|
|
71
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
|
|
72
|
+
const poly = new BasePolyline({ points });
|
|
73
|
+
poly.flags.setFlagValue(1);
|
|
74
|
+
|
|
75
|
+
const closestIdx = poly.getClosestSegmentIndex(new Point(0, 5));
|
|
76
|
+
// Closing segment index = points.length = 4
|
|
77
|
+
expect(closestIdx).toBe(4);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('BasePolyline.getClosestSegment returns closing segment as a Line for closed polyline', () => {
|
|
81
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
|
|
82
|
+
const poly = new BasePolyline({ points });
|
|
83
|
+
poly.flags.setFlagValue(1);
|
|
84
|
+
|
|
85
|
+
const seg = poly.getClosestSegment(new Point(0, 5));
|
|
86
|
+
expect(seg).toBeInstanceOf(Line);
|
|
87
|
+
// The segment spans P3=(0,10) → P0=(0,0)
|
|
88
|
+
expect(seg.points[0].x).toBeCloseTo(0);
|
|
89
|
+
expect(seg.points[0].y).toBeCloseTo(10);
|
|
90
|
+
expect(seg.points[1].x).toBeCloseTo(0);
|
|
91
|
+
expect(seg.points[1].y).toBeCloseTo(0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('BasePolyline.areConsecutiveSegments returns false for non-adjacent segments on a closed polyline', () => {
|
|
95
|
+
// 4-point closed poly: segments 1 and 3 are NOT adjacent (bug: old code returned true).
|
|
96
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
|
|
97
|
+
const poly = new BasePolyline({ points });
|
|
98
|
+
poly.flags.setFlagValue(1);
|
|
99
|
+
|
|
100
|
+
expect(poly.areConsecutiveSegments(1, 3)).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('BasePolyline.areConsecutiveSegments returns true for closing segment and its neighbours', () => {
|
|
104
|
+
// 4-point closed poly, N=4 (closing segment index).
|
|
105
|
+
// Seg N-1=3 ends at P3, closing seg starts at P3 → consecutive (diff=1).
|
|
106
|
+
// Closing seg ends at P0, seg 1 starts at P0 → consecutive (wrap case).
|
|
107
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
|
|
108
|
+
const poly = new BasePolyline({ points });
|
|
109
|
+
poly.flags.setFlagValue(1);
|
|
110
|
+
|
|
111
|
+
expect(poly.areConsecutiveSegments(3, 4)).toBe(true); // diff = 1
|
|
112
|
+
expect(poly.areConsecutiveSegments(4, 1)).toBe(true); // wrap
|
|
113
|
+
expect(poly.areConsecutiveSegments(1, 4)).toBe(true); // wrap (order reversed)
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('BasePolyline.closestPointOnSegment handles closing-segment index correctly', () => {
|
|
117
|
+
// 4-point square, closing segment = P3=(0,10)→P0=(0,0). Index = 4.
|
|
118
|
+
// Closest point on that segment to (0, 5) should be (0, 5).
|
|
119
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)];
|
|
120
|
+
const poly = new BasePolyline({ points });
|
|
121
|
+
poly.flags.setFlagValue(1);
|
|
122
|
+
|
|
123
|
+
const closest = poly.closestPointOnSegment(new Point(0, 5), 4);
|
|
124
|
+
expect(closest.x).toBeCloseTo(0);
|
|
125
|
+
expect(closest.y).toBeCloseTo(5);
|
|
126
|
+
});
|
|
3
127
|
|
|
4
128
|
|
|
5
129
|
test('Test BasePolyline.closestPoint', () => {
|
|
6
|
-
const points = [new Point(100, 100), new Point(200, 100), new Point(200, 50)];
|
|
7
|
-
|
|
8
|
-
const polyline = new BasePolyline({points: points});
|
|
130
|
+
const points = [new Point(100, 100), new Point(200, 100, -1), new Point(200, 50)];
|
|
131
|
+
const polyline = new BasePolyline({ points: points });
|
|
9
132
|
// line segment
|
|
10
133
|
const point1 = new Point(150, 85);
|
|
11
134
|
const closest1 = polyline.closestPoint(point1);
|
|
@@ -22,13 +145,13 @@ test('Test BasePolyline.closestPoint', () => {
|
|
|
22
145
|
});
|
|
23
146
|
|
|
24
147
|
test('Test BasePolyline.boundingBox', () => {
|
|
25
|
-
let polyline = new BasePolyline({points: [new Point(101, 102), new Point(201, 202)]});
|
|
148
|
+
let polyline = new BasePolyline({ points: [new Point(101, 102), new Point(201, 202)] });
|
|
26
149
|
expect(polyline.boundingBox().xMin).toBeCloseTo(101);
|
|
27
150
|
expect(polyline.boundingBox().xMax).toBeCloseTo(201);
|
|
28
151
|
expect(polyline.boundingBox().yMin).toBeCloseTo(102);
|
|
29
152
|
expect(polyline.boundingBox().yMax).toBeCloseTo(202);
|
|
30
153
|
|
|
31
|
-
polyline = new BasePolyline({points: [new Point(101, 102), new Point(-201, 202)]});
|
|
154
|
+
polyline = new BasePolyline({ points: [new Point(101, 102), new Point(-201, 202)] });
|
|
32
155
|
expect(polyline.boundingBox().xMin).toBeCloseTo(-201);
|
|
33
156
|
expect(polyline.boundingBox().xMax).toBeCloseTo(101);
|
|
34
157
|
expect(polyline.boundingBox().yMin).toBeCloseTo(102);
|
|
@@ -38,7 +161,7 @@ test('Test BasePolyline.boundingBox', () => {
|
|
|
38
161
|
let points = [new Point(101, 102), new Point(200, 102), new Point(200, 0)];
|
|
39
162
|
// set the bulge
|
|
40
163
|
points[1].bulge = -1;
|
|
41
|
-
polyline = new BasePolyline({points: points});
|
|
164
|
+
polyline = new BasePolyline({ points: points });
|
|
42
165
|
expect(polyline.boundingBox().xMin).toBeCloseTo(101);
|
|
43
166
|
expect(polyline.boundingBox().xMax).toBeCloseTo(251);
|
|
44
167
|
expect(polyline.boundingBox().yMin).toBeCloseTo(0);
|
|
@@ -47,7 +170,7 @@ test('Test BasePolyline.boundingBox', () => {
|
|
|
47
170
|
points = [new Point(101, 102), new Point(200, 102), new Point(200, 0)];
|
|
48
171
|
// set the bulge
|
|
49
172
|
points[1].bulge = -1;
|
|
50
|
-
polyline = new BasePolyline({points: points});
|
|
173
|
+
polyline = new BasePolyline({ points: points });
|
|
51
174
|
expect(polyline.boundingBox().xMin).toBeCloseTo(101);
|
|
52
175
|
expect(polyline.boundingBox().xMax).toBeCloseTo(251);
|
|
53
176
|
expect(polyline.boundingBox().yMin).toBeCloseTo(0);
|
|
@@ -59,7 +182,7 @@ test('Test BasePolyline.getBulgeFromSegment', () => {
|
|
|
59
182
|
// center: 100,50
|
|
60
183
|
// radius: 50
|
|
61
184
|
const points = [new Point(), new Point(100, 0)];
|
|
62
|
-
const polyline = new BasePolyline({points: points});
|
|
185
|
+
const polyline = new BasePolyline({ points: points });
|
|
63
186
|
|
|
64
187
|
// zero delta angle:
|
|
65
188
|
// bulge: 0
|
|
@@ -177,3 +300,329 @@ test('Test BasePolyline.getBulgeFromSegment', () => {
|
|
|
177
300
|
bulge = Math.tan(angle / 4);
|
|
178
301
|
expect(polyline.getBulgeFromSegment(new Point(64.6447, -14.6447))).toBeCloseTo(bulge);
|
|
179
302
|
});
|
|
303
|
+
|
|
304
|
+
test('Test Polyline.dxf', () => {
|
|
305
|
+
const points = [new Point(100, 100), new Point(200, 100), new Point(200, 50)];
|
|
306
|
+
points[1].bulge = -1;
|
|
307
|
+
const polyline = new Polyline({ handle: '1', points: points });
|
|
308
|
+
let file = new File();
|
|
309
|
+
polyline.dxf(file);
|
|
310
|
+
// console.log(file.contents);
|
|
311
|
+
|
|
312
|
+
const dxfString = `0
|
|
313
|
+
LWPOLYLINE
|
|
314
|
+
5
|
|
315
|
+
1
|
|
316
|
+
100
|
|
317
|
+
AcDbEntity
|
|
318
|
+
100
|
|
319
|
+
AcDbPolyline
|
|
320
|
+
8
|
|
321
|
+
0
|
|
322
|
+
6
|
|
323
|
+
ByLayer
|
|
324
|
+
39
|
|
325
|
+
2
|
|
326
|
+
90
|
|
327
|
+
3
|
|
328
|
+
70
|
|
329
|
+
0
|
|
330
|
+
10
|
|
331
|
+
100
|
|
332
|
+
20
|
|
333
|
+
100
|
|
334
|
+
42
|
|
335
|
+
0
|
|
336
|
+
10
|
|
337
|
+
200
|
|
338
|
+
20
|
|
339
|
+
100
|
|
340
|
+
42
|
|
341
|
+
-1
|
|
342
|
+
10
|
|
343
|
+
200
|
|
344
|
+
20
|
|
345
|
+
50
|
|
346
|
+
42
|
|
347
|
+
0
|
|
348
|
+
`;
|
|
349
|
+
|
|
350
|
+
expect(file.contents).toEqual(dxfString);
|
|
351
|
+
|
|
352
|
+
// create new entity from entity data to ensure all props are loaded
|
|
353
|
+
const newPolyline = new Polyline(polyline);
|
|
354
|
+
file = new File();
|
|
355
|
+
newPolyline.dxf(file);
|
|
356
|
+
|
|
357
|
+
expect(file.contents).toEqual(dxfString);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test('Test Polyline.dxf R12', () => {
|
|
361
|
+
const points = [new Point(100, 100), new Point(200, 100), new Point(200, 50)];
|
|
362
|
+
points[1].bulge = -1;
|
|
363
|
+
const polyline = new Polyline({ handle: '1', points: points });
|
|
364
|
+
const file = new DXFFile('R12');
|
|
365
|
+
polyline.dxf(file);
|
|
366
|
+
|
|
367
|
+
const dxfString = `0
|
|
368
|
+
POLYLINE
|
|
369
|
+
8
|
|
370
|
+
0
|
|
371
|
+
6
|
|
372
|
+
ByLayer
|
|
373
|
+
66
|
|
374
|
+
1
|
|
375
|
+
70
|
|
376
|
+
0
|
|
377
|
+
0
|
|
378
|
+
VERTEX
|
|
379
|
+
8
|
|
380
|
+
0
|
|
381
|
+
10
|
|
382
|
+
100
|
|
383
|
+
20
|
|
384
|
+
100
|
|
385
|
+
30
|
|
386
|
+
0.0
|
|
387
|
+
42
|
|
388
|
+
0
|
|
389
|
+
0
|
|
390
|
+
VERTEX
|
|
391
|
+
8
|
|
392
|
+
0
|
|
393
|
+
10
|
|
394
|
+
200
|
|
395
|
+
20
|
|
396
|
+
100
|
|
397
|
+
30
|
|
398
|
+
0.0
|
|
399
|
+
42
|
|
400
|
+
-1
|
|
401
|
+
0
|
|
402
|
+
VERTEX
|
|
403
|
+
8
|
|
404
|
+
0
|
|
405
|
+
10
|
|
406
|
+
200
|
|
407
|
+
20
|
|
408
|
+
50
|
|
409
|
+
30
|
|
410
|
+
0.0
|
|
411
|
+
42
|
|
412
|
+
0
|
|
413
|
+
0
|
|
414
|
+
SEQEND
|
|
415
|
+
8
|
|
416
|
+
0
|
|
417
|
+
`;
|
|
418
|
+
|
|
419
|
+
expect(file.contents).toEqual(dxfString);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test('Test BasePolyline.toPolylinePoints', () => {
|
|
423
|
+
const points = [new Point(100, 100), new Point(200, 100), new Point(200, 50)];
|
|
424
|
+
points[1].bulge = -1;
|
|
425
|
+
const polyline = new BasePolyline({ points: points });
|
|
426
|
+
|
|
427
|
+
const polylinePoints = polyline.toPolylinePoints();
|
|
428
|
+
expect(polylinePoints[0].x).toBe(100);
|
|
429
|
+
expect(polylinePoints[0].y).toBe(100);
|
|
430
|
+
expect(polylinePoints[0].bulge).toBe(0);
|
|
431
|
+
|
|
432
|
+
expect(polylinePoints[1].x).toBe(200);
|
|
433
|
+
expect(polylinePoints[1].y).toBe(100);
|
|
434
|
+
expect(polylinePoints[1].bulge).toBe(-1);
|
|
435
|
+
|
|
436
|
+
expect(polylinePoints[2].x).toBe(200);
|
|
437
|
+
expect(polylinePoints[2].y).toBe(50);
|
|
438
|
+
expect(polylinePoints[2].bulge).toBe(0);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test('Test BasePolyline.toPolylinePoints returns defensive copy for open polyline', () => {
|
|
442
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10)];
|
|
443
|
+
points[1].bulge = -1;
|
|
444
|
+
const polyline = new BasePolyline({ points: points });
|
|
445
|
+
|
|
446
|
+
const result = polyline.toPolylinePoints();
|
|
447
|
+
// Mutate the returned array and one of the returned points
|
|
448
|
+
result.reverse();
|
|
449
|
+
result[0].bulge = 99;
|
|
450
|
+
|
|
451
|
+
// Original this.points must be unaffected
|
|
452
|
+
expect(polyline.points.length).toBe(3);
|
|
453
|
+
expect(polyline.points[0].x).toBe(0);
|
|
454
|
+
expect(polyline.points[1].bulge).toBe(-1);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
test('Test BasePolyline.toPolylinePoints closed polyline appends closure point', () => {
|
|
458
|
+
// A triangle closed via the Close command (flag bit 1 set, no duplicate
|
|
459
|
+
// point stored in this.points).
|
|
460
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(5, 10)];
|
|
461
|
+
const flags = new Flags();
|
|
462
|
+
flags.addValue(1); // closed flag
|
|
463
|
+
const polyline = new BasePolyline({ points: points, flags: flags });
|
|
464
|
+
|
|
465
|
+
const polylinePoints = polyline.toPolylinePoints();
|
|
466
|
+
// Should have 4 points: the 3 vertices plus the closure point
|
|
467
|
+
expect(polylinePoints.length).toBe(4);
|
|
468
|
+
// Closure point matches the first point
|
|
469
|
+
expect(polylinePoints[3].x).toBe(polylinePoints[0].x);
|
|
470
|
+
expect(polylinePoints[3].y).toBe(polylinePoints[0].y);
|
|
471
|
+
// The original this.points array is not mutated
|
|
472
|
+
expect(polyline.points.length).toBe(3);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test('Polyline.execute handles Close option', async () => {
|
|
476
|
+
const pt1 = new Point(0, 0);
|
|
477
|
+
const pt2 = new Point(10, 0);
|
|
478
|
+
const pt3 = new Point(10, 10);
|
|
479
|
+
|
|
480
|
+
await withMockInput(DesignCore.Scene, [pt1, pt2, pt3, 'Close'], async () => {
|
|
481
|
+
const polyline = new Polyline({});
|
|
482
|
+
await polyline.execute();
|
|
483
|
+
|
|
484
|
+
expect(polyline.points.length).toBe(3);
|
|
485
|
+
expect(polyline.points[0]).toBe(pt1);
|
|
486
|
+
expect(polyline.points[1]).toBe(pt2);
|
|
487
|
+
expect(polyline.points[2]).toBe(pt3);
|
|
488
|
+
expect(polyline.flags.hasFlag(1)).toBe(true);
|
|
489
|
+
}, { extraMethods: { actionCommand: () => 0 } });
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
// ─── Polyline interactive options: Undo, Close, Arc, Line ───────────────
|
|
493
|
+
|
|
494
|
+
test('Polyline.execute supports Undo in line mode', async () => {
|
|
495
|
+
const inputs = [new Point(0, 0), new Point(10, 0), new Point(20, 0), 'Undo', new Point(10, 10), 'Close'];
|
|
496
|
+
await withMockInput(DesignCore.Scene, inputs, async () => {
|
|
497
|
+
const polyline = new BasePolyline({});
|
|
498
|
+
await polyline.execute();
|
|
499
|
+
expect(polyline.points.length).toBe(3);
|
|
500
|
+
expect(polyline.points[0]).toEqual(inputs[0]);
|
|
501
|
+
expect(polyline.points[1]).toEqual(inputs[1]);
|
|
502
|
+
expect(polyline.points[2]).toEqual(inputs[4]);
|
|
503
|
+
expect(polyline.flags.hasFlag(1)).toBe(true); // closed
|
|
504
|
+
}, { extraMethods: { actionCommand: () => {} } });
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test('Polyline.execute supports Undo in arc mode', async () => {
|
|
508
|
+
const inputs = [new Point(0, 0), new Point(10, 0), 'Arc', new Point(10, 10), new Point(20, 20), 'Undo', 'Line', new Point(10, 20), 'Close'];
|
|
509
|
+
await withMockInput(DesignCore.Scene, inputs, async () => {
|
|
510
|
+
const polyline = new BasePolyline({});
|
|
511
|
+
await polyline.execute();
|
|
512
|
+
expect(polyline.points.length).toBe(4);
|
|
513
|
+
expect(polyline.points[0]).toEqual(inputs[0]);
|
|
514
|
+
expect(polyline.points[1]).toEqual(inputs[1]);
|
|
515
|
+
expect(polyline.points[2]).toEqual(inputs[3]);
|
|
516
|
+
expect(polyline.points[3]).toEqual(inputs[7]);
|
|
517
|
+
expect(polyline.flags.hasFlag(1)).toBe(true); // closed
|
|
518
|
+
// After undo in arc mode
|
|
519
|
+
expect(polyline.points[2].bulge).toBeCloseTo(0);
|
|
520
|
+
expect(polyline.points[3].bulge).toBeCloseTo(0);
|
|
521
|
+
// console.log(polyline.points);
|
|
522
|
+
}, { extraMethods: { actionCommand: () => {} } });
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
test('Polyline.execute supports Arc/Line mode switching', async () => {
|
|
526
|
+
const inputs = [new Point(0, 0), new Point(10, 0), 'Arc', new Point(10, 10), 'Line', new Point(20, 10), 'Close'];
|
|
527
|
+
await withMockInput(DesignCore.Scene, inputs, async () => {
|
|
528
|
+
const polyline = new BasePolyline({});
|
|
529
|
+
await polyline.execute();
|
|
530
|
+
expect(polyline.points.length).toBe(4);
|
|
531
|
+
expect(polyline.points[0]).toEqual(inputs[0]);
|
|
532
|
+
expect(polyline.points[1]).toEqual(inputs[1]);
|
|
533
|
+
expect(polyline.points[2]).toEqual(inputs[3]);
|
|
534
|
+
expect(polyline.points[3]).toEqual(inputs[5]);
|
|
535
|
+
expect(polyline.flags.hasFlag(1)).toBe(true); // closed
|
|
536
|
+
// Arc segment bulge should be set, line segment bulge should be 0
|
|
537
|
+
expect(polyline.points[1].bulge).not.toBe(0);
|
|
538
|
+
expect(polyline.points[2].bulge).toBe(0);
|
|
539
|
+
}, { extraMethods: { actionCommand: () => {} } });
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
// ─── Auto-close when first and last point are the same ───────────────────────
|
|
543
|
+
|
|
544
|
+
test('BasePolyline constructor auto-closes when last point matches first', () => {
|
|
545
|
+
// Simulate a polyline loaded with a redundant closing point (e.g. from DXF data)
|
|
546
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 0)];
|
|
547
|
+
const polyline = new BasePolyline({ points });
|
|
548
|
+
|
|
549
|
+
// Duplicate point should be removed and the closed flag set
|
|
550
|
+
expect(polyline.points.length).toBe(3);
|
|
551
|
+
expect(polyline.flags.hasFlag(1)).toBe(true);
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
test('BasePolyline constructor does not auto-close when fewer than 3 unique points remain after pop', () => {
|
|
555
|
+
// [A, B, A] — only 2 unique points after removing the duplicate; must not auto-close
|
|
556
|
+
const points = [new Point(0, 0), new Point(10, 0), new Point(0, 0)];
|
|
557
|
+
const polyline = new BasePolyline({ points });
|
|
558
|
+
|
|
559
|
+
expect(polyline.points.length).toBe(3);
|
|
560
|
+
expect(polyline.flags.hasFlag(1)).toBe(false);
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test('BasePolyline constructor does not auto-close with only 2 identical points', () => {
|
|
564
|
+
// Only 2 unique points — not enough to form a closed polygon
|
|
565
|
+
const points = [new Point(0, 0), new Point(0, 0)];
|
|
566
|
+
const polyline = new BasePolyline({ points });
|
|
567
|
+
|
|
568
|
+
expect(polyline.points.length).toBe(2);
|
|
569
|
+
expect(polyline.flags.hasFlag(1)).toBe(false);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
test('Polyline.execute auto-closes when start point is re-entered', async () => {
|
|
573
|
+
const pt1 = new Point(0, 0);
|
|
574
|
+
const pt2 = new Point(10, 0);
|
|
575
|
+
const pt3 = new Point(10, 10);
|
|
576
|
+
const pt4 = new Point(0, 0); // same as pt1
|
|
577
|
+
|
|
578
|
+
// Track how many times actionCommand is called with data that has the closed flag set
|
|
579
|
+
let autoClosedCount = 0;
|
|
580
|
+
const mockActionCommand = (entity) => {
|
|
581
|
+
// The constructor will have auto-closed the entity when pt4 == pt1
|
|
582
|
+
if (entity.points.length === 4 && entity.points.at(-1).isSame(pt1)) {
|
|
583
|
+
// simulate what the constructor does: a new entity created from this data would auto-close
|
|
584
|
+
const rebuilt = new BasePolyline({ points: [...entity.points] });
|
|
585
|
+
if (rebuilt.flags.hasFlag(1)) autoClosedCount++;
|
|
586
|
+
}
|
|
587
|
+
return 0;
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
await withMockInput(DesignCore.Scene, [pt1, pt2, pt3, pt4], async () => {
|
|
591
|
+
const polyline = new BasePolyline({});
|
|
592
|
+
await polyline.execute();
|
|
593
|
+
// execute does not exit early — the duplicate point is on the instance
|
|
594
|
+
expect(polyline.points.length).toBe(4);
|
|
595
|
+
// The scene entity (rebuilt in actionCommand) would be auto-closed
|
|
596
|
+
expect(autoClosedCount).toBe(1);
|
|
597
|
+
}, { extraMethods: { actionCommand: mockActionCommand } });
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
test('BasePolyline.fromPolylinePoints round-trip preserves open polyline', () => {
|
|
601
|
+
const poly = new BasePolyline({ points: [new Point(0, 0, 0.5), new Point(10, 0), new Point(10, 10)] });
|
|
602
|
+
const polyPts = poly.toPolylinePoints();
|
|
603
|
+
const rebuilt = new BasePolyline({});
|
|
604
|
+
rebuilt.fromPolylinePoints(polyPts);
|
|
605
|
+
expect(rebuilt.points.length).toBe(3);
|
|
606
|
+
expect(rebuilt.points[0].x).toBe(0);
|
|
607
|
+
expect(rebuilt.points[0].y).toBe(0);
|
|
608
|
+
expect(rebuilt.points[0].bulge).toBe(0.5);
|
|
609
|
+
expect(rebuilt.points[2].x).toBe(10);
|
|
610
|
+
expect(rebuilt.points[2].y).toBe(10);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
test('BasePolyline.fromPolylinePoints round-trip strips closure point for closed polyline', () => {
|
|
614
|
+
const flags = new Flags();
|
|
615
|
+
flags.addValue(1);
|
|
616
|
+
const poly = new BasePolyline({ points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)], flags: flags });
|
|
617
|
+
const polyPts = poly.toPolylinePoints();
|
|
618
|
+
// toPolylinePoints appends a closure copy of the first point
|
|
619
|
+
expect(polyPts.length).toBe(5);
|
|
620
|
+
const flags2 = new Flags();
|
|
621
|
+
flags2.addValue(1);
|
|
622
|
+
const rebuilt = new BasePolyline({ flags: flags2 });
|
|
623
|
+
rebuilt.fromPolylinePoints(polyPts);
|
|
624
|
+
expect(rebuilt.points.length).toBe(4);
|
|
625
|
+
expect(rebuilt.points[0].x).toBe(0);
|
|
626
|
+
expect(rebuilt.points[3].x).toBe(0);
|
|
627
|
+
expect(rebuilt.points[3].y).toBe(10);
|
|
628
|
+
});
|