@design-core/core 0.0.6 → 50.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +4 -0
- package/.github/workflows/DesignCoreQA.yml +3 -0
- package/TODO.md +49 -6
- package/core/core/core.js +0 -3
- package/core/designCore.js +1 -1
- package/core/dimensions/alignedDimension.js +12 -11
- package/core/dimensions/angularDimension.js +38 -37
- package/core/dimensions/{baseCircularDimension.js → circularDimensionBase.js} +22 -21
- package/core/dimensions/diametricDimension.js +14 -13
- package/core/dimensions/dimension.js +62 -55
- package/core/dimensions/{baseDimension.js → dimensionBase.js} +73 -107
- package/core/dimensions/{baseLinearDimension.js → linearDimensionBase.js} +15 -14
- package/core/dimensions/radialDimension.js +12 -11
- package/core/dimensions/rotatedDimension.js +20 -19
- package/core/entities/arc.js +123 -156
- package/core/entities/arctext.js +222 -184
- package/core/entities/circle.js +77 -99
- package/core/entities/entity.js +114 -93
- package/core/entities/hatch.js +346 -273
- package/core/entities/insert.js +68 -83
- package/core/entities/line.js +21 -142
- package/core/entities/lwpolyline.js +16 -11
- package/core/entities/point.js +19 -19
- package/core/entities/polyline.js +7 -7
- package/core/entities/{basePolyline.js → polylineBase.js} +47 -299
- package/core/entities/rectangle.js +3 -1
- package/core/entities/solid.js +6 -15
- package/core/entities/text.js +190 -201
- package/core/lib/auxiliary/rubberBand.js +32 -0
- package/core/lib/auxiliary/selectionWindow.js +63 -0
- package/core/lib/auxiliary/snapPoint.js +113 -0
- package/core/lib/auxiliary/trackingLine.js +76 -0
- package/core/lib/boundingBox.js +11 -11
- package/core/lib/canvas.js +232 -168
- package/core/lib/clipboard.js +9 -3
- package/core/lib/colour.js +16 -0
- package/core/lib/commandLine.js +1 -6
- package/core/lib/commandManager.js +18 -8
- package/core/lib/constants.js +28 -0
- package/core/lib/dxf/dxf.js +15 -14
- package/core/lib/dxf/dxfIterator.js +2 -2
- package/core/lib/entityManager.js +33 -26
- package/core/lib/inputManager.js +36 -12
- package/core/lib/intersect.js +25 -0
- package/core/lib/matrix.js +1 -1
- package/core/lib/patterns.js +9 -2
- package/core/lib/plotOptions.js +67 -0
- package/core/lib/polylineUtils.js +75 -0
- package/core/lib/renderers/cairoRenderer.js +371 -0
- package/core/lib/renderers/canvasRenderer.js +315 -0
- package/core/lib/renderers/pdfRenderer.js +366 -0
- package/core/lib/renderers/rendererBase.js +284 -0
- package/core/lib/renderers/svgRenderer.js +343 -0
- package/core/lib/scene.js +28 -6
- package/core/lib/selectionManager.js +20 -20
- package/core/lib/settings.js +4 -2
- package/core/lib/snapping.js +4 -191
- package/core/lib/stateManager.js +30 -1
- package/core/lib/utils.js +26 -7
- package/core/properties/entityProperties.js +104 -0
- package/core/properties/property.js +106 -2
- package/core/properties/propertyManager.js +60 -44
- package/core/tables/block.js +50 -81
- package/core/tables/dimStyle.js +5 -5
- package/core/tables/endblk.js +1 -1
- package/core/tables/layer.js +8 -0
- package/core/tables/ltypeManager.js +1 -1
- package/core/tables/styleManagerBase.js +3 -2
- package/core/tables/tableManagerBase.js +8 -3
- package/core/tables/vport.js +2 -2
- package/core/tools/chamfer.js +117 -143
- package/core/tools/chamferFilletBase.js +138 -37
- package/core/tools/copy.js +11 -11
- package/core/tools/copybase.js +3 -3
- package/core/tools/copyclip.js +3 -3
- package/core/tools/cornerEntity.js +2 -2
- package/core/tools/cutclip.js +3 -3
- package/core/tools/explode.js +7 -7
- package/core/tools/extend.js +138 -25
- package/core/tools/fillet.js +92 -189
- package/core/tools/matchProp.js +3 -12
- package/core/tools/mirror.js +15 -14
- package/core/tools/move.js +8 -8
- package/core/tools/offset.js +31 -33
- package/core/tools/pasteclip.js +1 -1
- package/core/tools/purge.js +10 -10
- package/core/tools/rotate.js +19 -16
- package/core/tools/scale.js +7 -7
- package/core/tools/trim.js +271 -29
- package/core/tools/zoom.js +1 -1
- package/cspell.json +321 -0
- package/jest.config.js +6 -0
- package/package.json +5 -3
- package/test/dimensions/alignedDimension.test.js +17 -10
- package/test/dimensions/angularDimension.test.js +13 -13
- package/test/dimensions/diametricDimension.test.js +15 -15
- package/test/dimensions/dimension.test.js +12 -12
- package/test/dimensions/{baseDimension.test.js → dimensionBase.test.js} +73 -12
- package/test/dimensions/linearDimensionBase.test.js +62 -0
- package/test/dimensions/radialDimension.test.js +15 -15
- package/test/dimensions/rotatedDimension.test.js +18 -11
- package/test/entities/arc.test.js +83 -86
- package/test/entities/arctext.test.js +184 -57
- package/test/entities/circle.test.js +68 -96
- package/test/entities/entity.test.js +42 -8
- package/test/entities/hatch.test.js +569 -96
- package/test/entities/insert.test.js +99 -4
- package/test/entities/line.test.js +15 -41
- package/test/entities/lwpolyline.test.js +104 -0
- package/test/entities/point.test.js +3 -3
- package/test/entities/polyline.test.js +11 -0
- package/test/entities/{basePolyline.test.js → polylineBase.test.js} +192 -57
- package/test/entities/rectangle.test.js +11 -0
- package/test/entities/solid.test.js +1 -1
- package/test/entities/text.test.js +302 -77
- package/test/integration/exportIntegration.reference.dxf +9 -9
- package/test/integration/exportIntegration.test.js +5 -5
- package/test/integration/intersectionIntegration.test.js +4 -5
- package/test/integration/pdfRendererIntegration.reference.pdf +17709 -0
- package/test/integration/pdfRendererIntegration.test.js +61 -0
- package/test/integration/svgRendererIntegration.reference.svg +3343 -0
- package/test/integration/svgRendererIntegration.test.js +61 -0
- package/test/integration/userInputIntegration.reference.dxf +1165 -105
- package/test/integration/userInputIntegration.test.js +239 -132
- package/test/lib/boundingBox.test.js +3 -3
- package/test/lib/canvas.test.js +744 -125
- package/test/lib/constants.test.js +27 -0
- package/test/lib/entityManager.test.js +4 -4
- package/test/lib/inputManager.test.js +81 -20
- package/test/lib/intersect.test.js +83 -0
- package/test/lib/plotOptions.test.js +179 -0
- package/test/lib/rubberBand.test.js +84 -0
- package/test/lib/scene.test.js +9 -9
- package/test/lib/selectionManager.test.js +3 -3
- package/test/lib/settings.test.js +5 -6
- package/test/lib/snapping.test.js +72 -130
- package/test/lib/stateManager.test.js +7 -7
- package/test/lib/utils.test.js +36 -2
- package/test/properties/entityProperties.test.js +132 -0
- package/test/properties/property.test.js +53 -0
- package/test/properties/propertyManager.test.js +25 -25
- package/test/renderers/canvasRenderer.test.js +317 -0
- package/test/renderers/pdfRenderer.test.js +271 -0
- package/test/renderers/rendererBase.test.js +213 -0
- package/test/renderers/svgRenderer.test.js +326 -0
- package/test/tables/block.test.js +92 -13
- package/test/tables/endblk.test.js +2 -2
- package/test/tables/layer.test.js +30 -0
- package/test/tables/layerManager.test.js +2 -2
- package/test/test-helpers/hatchGridRenderer.js +180 -0
- package/test/test-helpers/test-helpers.js +158 -6
- package/test/tools/chamfer.test.js +273 -51
- package/test/tools/chamferFilletBase.test.js +125 -36
- package/test/tools/copy.test.js +8 -8
- package/test/tools/copybase.test.js +2 -2
- package/test/tools/copyclip.test.js +2 -2
- package/test/tools/cornerEntity.test.js +8 -8
- package/test/tools/cutclip.test.js +2 -2
- package/test/tools/erase.test.js +6 -6
- package/test/tools/explode.test.js +5 -5
- package/test/tools/extend.test.js +373 -42
- package/test/tools/fillet.test.js +230 -49
- package/test/tools/matchProp.test.js +11 -11
- package/test/tools/mirror.test.js +39 -39
- package/test/tools/move.test.js +7 -7
- package/test/tools/offset.test.js +222 -55
- package/test/tools/pasteclip.test.js +5 -5
- package/test/tools/purge.test.js +4 -4
- package/test/tools/rotate.test.js +39 -9
- package/test/tools/scale.test.js +33 -33
- package/test/tools/trim.test.js +579 -53
- package/test/tools/zoom.test.js +12 -12
- package/core/lib/selectionWindow.js +0 -83
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Core } from '../../core/core/core.js';
|
|
2
2
|
import { Point } from '../../core/entities/point.js';
|
|
3
3
|
import { Chamfer } from '../../core/tools/chamfer.js';
|
|
4
|
+
import { DesignCore } from '../../core/designCore.js';
|
|
4
5
|
import { Strings } from '../../core/lib/strings.js';
|
|
5
6
|
import { SingleSelection } from '../../core/lib/selectionManager.js';
|
|
6
7
|
import { expect, jest } from '@jest/globals';
|
|
@@ -20,7 +21,7 @@ test('Chamfer.register returns correct command object', () => {
|
|
|
20
21
|
|
|
21
22
|
test('Chamfer.action does nothing when entities are not set', () => {
|
|
22
23
|
core.scene.clear();
|
|
23
|
-
core.scene.
|
|
24
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(10, 0)] });
|
|
24
25
|
const countBefore = core.scene.entities.count();
|
|
25
26
|
|
|
26
27
|
const chamfer = new Chamfer();
|
|
@@ -31,8 +32,8 @@ test('Chamfer.action does nothing when entities are not set', () => {
|
|
|
31
32
|
|
|
32
33
|
test('Chamfer.action notifies when first entity type is not supported', () => {
|
|
33
34
|
core.scene.clear();
|
|
34
|
-
core.scene.
|
|
35
|
-
core.scene.
|
|
35
|
+
core.scene.addEntity('Circle', { points: [new Point(0, 0), new Point(5, 0)] });
|
|
36
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(10, 0)] });
|
|
36
37
|
|
|
37
38
|
const notifySpy = jest.spyOn(core, 'notify');
|
|
38
39
|
|
|
@@ -49,8 +50,8 @@ test('Chamfer.action notifies when first entity type is not supported', () => {
|
|
|
49
50
|
|
|
50
51
|
test('Chamfer.action notifies when second entity type is not supported', () => {
|
|
51
52
|
core.scene.clear();
|
|
52
|
-
core.scene.
|
|
53
|
-
core.scene.
|
|
53
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(10, 0)] });
|
|
54
|
+
core.scene.addEntity('Circle', { points: [new Point(0, 0), new Point(5, 0)] });
|
|
54
55
|
|
|
55
56
|
const notifySpy = jest.spyOn(core, 'notify');
|
|
56
57
|
|
|
@@ -67,8 +68,8 @@ test('Chamfer.action notifies when second entity type is not supported', () => {
|
|
|
67
68
|
|
|
68
69
|
test('Chamfer.action notifies for parallel lines', () => {
|
|
69
70
|
core.scene.clear();
|
|
70
|
-
core.scene.
|
|
71
|
-
core.scene.
|
|
71
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(10, 0)] });
|
|
72
|
+
core.scene.addEntity('Line', { points: [new Point(0, 5), new Point(10, 5)] });
|
|
72
73
|
|
|
73
74
|
const notifySpy = jest.spyOn(core, 'notify');
|
|
74
75
|
|
|
@@ -89,8 +90,8 @@ test('Chamfer.action dist=0 trimMode=true trims both lines to intersection', ()
|
|
|
89
90
|
// Lines only touching when extended: horizontal (0,0)→(8,0), vertical (10,-10)→(10,-2)
|
|
90
91
|
// Infinite extensions meet at (10, 0).
|
|
91
92
|
core.scene.clear();
|
|
92
|
-
core.scene.
|
|
93
|
-
core.scene.
|
|
93
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(8, 0)] });
|
|
94
|
+
core.scene.addEntity('Line', { points: [new Point(10, -10), new Point(10, -2)] });
|
|
94
95
|
|
|
95
96
|
core.scene.headers.chamferDistanceA = 0;
|
|
96
97
|
core.scene.headers.chamferDistanceB = 0;
|
|
@@ -121,8 +122,8 @@ test('Chamfer.action dist=0 trimMode=true trims both lines to intersection', ()
|
|
|
121
122
|
|
|
122
123
|
test('Chamfer.action dist=0 trimMode=false makes no changes', () => {
|
|
123
124
|
core.scene.clear();
|
|
124
|
-
core.scene.
|
|
125
|
-
core.scene.
|
|
125
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(8, 0)] });
|
|
126
|
+
core.scene.addEntity('Line', { points: [new Point(10, -10), new Point(10, -2)] });
|
|
126
127
|
|
|
127
128
|
core.scene.headers.chamferDistanceA = 0;
|
|
128
129
|
core.scene.headers.chamferDistanceB = 0;
|
|
@@ -145,8 +146,8 @@ test('Chamfer.action distance method trimMode=true adds chamfer line and trims',
|
|
|
145
146
|
// Perpendicular L-corner at origin: horizontal (-10,0)→(0,0), vertical (0,0)→(0,10)
|
|
146
147
|
// Equal distances of 2 → chamfer endpoints at (-2,0) and (0,2)
|
|
147
148
|
core.scene.clear();
|
|
148
|
-
core.scene.
|
|
149
|
-
core.scene.
|
|
149
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
150
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
150
151
|
|
|
151
152
|
core.scene.headers.chamferDistanceA = 2;
|
|
152
153
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -190,8 +191,8 @@ test('Chamfer.action distance method trimMode=true adds chamfer line and trims',
|
|
|
190
191
|
test('Chamfer.action distance method asymmetric distances', () => {
|
|
191
192
|
// L-corner at origin, distA=3 along horizontal, distB=1 along vertical
|
|
192
193
|
core.scene.clear();
|
|
193
|
-
core.scene.
|
|
194
|
-
core.scene.
|
|
194
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
195
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
195
196
|
|
|
196
197
|
core.scene.headers.chamferDistanceA = 3;
|
|
197
198
|
core.scene.headers.chamferDistanceB = 1;
|
|
@@ -215,8 +216,8 @@ test('Chamfer.action distance method asymmetric distances', () => {
|
|
|
215
216
|
|
|
216
217
|
test('Chamfer.action distance method trimMode=false adds chamfer line only', () => {
|
|
217
218
|
core.scene.clear();
|
|
218
|
-
core.scene.
|
|
219
|
-
core.scene.
|
|
219
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
220
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
220
221
|
|
|
221
222
|
core.scene.headers.chamferDistanceA = 2;
|
|
222
223
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -244,8 +245,8 @@ test('Chamfer.action distance method trimMode=false adds chamfer line only', ()
|
|
|
244
245
|
test('Chamfer.action notifies when distance is too large for the segments', () => {
|
|
245
246
|
// Lines only 5 units long from intersection — distance 10 won't fit
|
|
246
247
|
core.scene.clear();
|
|
247
|
-
core.scene.
|
|
248
|
-
core.scene.
|
|
248
|
+
core.scene.addEntity('Line', { points: [new Point(-5, 0), new Point(0, 0)] });
|
|
249
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 5)] });
|
|
249
250
|
|
|
250
251
|
core.scene.headers.chamferDistanceA = 10;
|
|
251
252
|
core.scene.headers.chamferDistanceB = 10;
|
|
@@ -272,8 +273,8 @@ test('Chamfer.action angle method 45° on perpendicular lines matches distance m
|
|
|
272
273
|
// With perpendicular lines and 45°, the chamfer endpoint on line2 should also be 2 units
|
|
273
274
|
// from the intersection — matching a symmetric distance chamfer.
|
|
274
275
|
core.scene.clear();
|
|
275
|
-
core.scene.
|
|
276
|
-
core.scene.
|
|
276
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
277
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
277
278
|
|
|
278
279
|
core.scene.headers.chamferLength = 2;
|
|
279
280
|
core.scene.headers.chamferAngle = 45; // degrees
|
|
@@ -306,8 +307,8 @@ test('Chamfer.action notifies when click coincides with intersection', () => {
|
|
|
306
307
|
// L-corner at origin. Clicking firstClickPoint exactly at (0,0) makes
|
|
307
308
|
// firstClickDistance = 0, triggering the "Selected corner cannot be chamfered" guard.
|
|
308
309
|
core.scene.clear();
|
|
309
|
-
core.scene.
|
|
310
|
-
core.scene.
|
|
310
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
311
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
311
312
|
|
|
312
313
|
core.scene.headers.chamferDistanceA = 2;
|
|
313
314
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -332,8 +333,8 @@ test('Chamfer.action notifies when click coincides with intersection', () => {
|
|
|
332
333
|
|
|
333
334
|
test('Chamfer.action angle method notifies INVALIDNUMBER when angle is zero', () => {
|
|
334
335
|
core.scene.clear();
|
|
335
|
-
core.scene.
|
|
336
|
-
core.scene.
|
|
336
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
337
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
337
338
|
|
|
338
339
|
core.scene.headers.chamferLength = 2;
|
|
339
340
|
core.scene.headers.chamferAngle = 0; // alpha = 0 * π/180 = 0, fails alpha <= 0 guard
|
|
@@ -361,8 +362,8 @@ test('Chamfer.action angle method notifies PARALLELLINES when chamfer direction
|
|
|
361
362
|
// candidate1 = rotate(-1,0) by +π/2 = (0,-1); dot with secondClickUnit (0,1) = -1 < 0.
|
|
362
363
|
// candidate2 = rotate(-1,0) by -π/2 = (0, 1) → parallel to secondLineDirection (0,1) → cross = 0.
|
|
363
364
|
core.scene.clear();
|
|
364
|
-
core.scene.
|
|
365
|
-
core.scene.
|
|
365
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
366
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
366
367
|
|
|
367
368
|
core.scene.headers.chamferLength = 2;
|
|
368
369
|
core.scene.headers.chamferAngle = 90; // degrees
|
|
@@ -396,8 +397,8 @@ test('Chamfer.execute returns early when first input is undefined', async () =>
|
|
|
396
397
|
|
|
397
398
|
test('Chamfer.execute selects two lines and calls executeCommand', async () => {
|
|
398
399
|
core.scene.clear();
|
|
399
|
-
core.scene.
|
|
400
|
-
core.scene.
|
|
400
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
401
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
401
402
|
|
|
402
403
|
const executeCommandSpy = jest.fn();
|
|
403
404
|
|
|
@@ -543,7 +544,7 @@ test('Chamfer.execute sets chamferMode to true via Method=Angle option', async (
|
|
|
543
544
|
|
|
544
545
|
test('Chamfer.execute re-prompts and notifies when first selected entity is not a Line', async () => {
|
|
545
546
|
core.scene.clear();
|
|
546
|
-
core.scene.
|
|
547
|
+
core.scene.addEntity('Circle', { points: [new Point(0, 0), new Point(5, 0)] });
|
|
547
548
|
|
|
548
549
|
const notifySpy = jest.spyOn(core, 'notify');
|
|
549
550
|
|
|
@@ -562,8 +563,8 @@ test('Chamfer.execute re-prompts and notifies when first selected entity is not
|
|
|
562
563
|
|
|
563
564
|
test('Chamfer.execute re-prompts and notifies when second selected entity is not a Line', async () => {
|
|
564
565
|
core.scene.clear();
|
|
565
|
-
core.scene.
|
|
566
|
-
core.scene.
|
|
566
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(10, 0)] });
|
|
567
|
+
core.scene.addEntity('Circle', { points: [new Point(0, 0), new Point(5, 0)] });
|
|
567
568
|
|
|
568
569
|
const notifySpy = jest.spyOn(core, 'notify');
|
|
569
570
|
|
|
@@ -593,8 +594,8 @@ test('Chamfer.execute re-prompts and notifies when second selected entity is not
|
|
|
593
594
|
test('Chamfer.action dist>0 trimMode=true Line first + Lwpolyline second keepStart: chamfer embedded in polyline', () => {
|
|
594
595
|
// Click polyline near (-15,0) → segment 1. keepStart keeps the start portion.
|
|
595
596
|
core.scene.clear();
|
|
596
|
-
core.scene.
|
|
597
|
-
core.scene.
|
|
597
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
598
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-20, 0), new Point(-10, 0), new Point(0, 0), new Point(10, 0)] });
|
|
598
599
|
|
|
599
600
|
core.scene.headers.chamferDistanceA = 2;
|
|
600
601
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -634,8 +635,8 @@ test('Chamfer.action dist>0 trimMode=true Line first + Lwpolyline second keepSta
|
|
|
634
635
|
|
|
635
636
|
test('Chamfer.action dist>0 trimMode=true Lwpolyline first + Line second keepStart: same result with entities reversed', () => {
|
|
636
637
|
core.scene.clear();
|
|
637
|
-
core.scene.
|
|
638
|
-
core.scene.
|
|
638
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-20, 0), new Point(-10, 0), new Point(0, 0), new Point(10, 0)] });
|
|
639
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
639
640
|
|
|
640
641
|
core.scene.headers.chamferDistanceA = 2;
|
|
641
642
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -675,8 +676,8 @@ test('Chamfer.action dist>0 trimMode=true Line + Lwpolyline keepEnd: keeps end p
|
|
|
675
676
|
// Click polyline near (8,0) → segment 3 (right of intersection). keepEnd.
|
|
676
677
|
// polyChamfer = (2,0), lineChamfer = (0,2).
|
|
677
678
|
core.scene.clear();
|
|
678
|
-
core.scene.
|
|
679
|
-
core.scene.
|
|
679
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
680
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-20, 0), new Point(-10, 0), new Point(0, 0), new Point(10, 0)] });
|
|
680
681
|
|
|
681
682
|
core.scene.headers.chamferDistanceA = 2;
|
|
682
683
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -719,7 +720,7 @@ test('Chamfer.action dist>0 trimMode=true same Lwpolyline consecutive segments:
|
|
|
719
720
|
// Segment 1: (-10,0)→(0,0), Segment 2: (0,0)→(0,10). Corner at (0,0).
|
|
720
721
|
// dist=2 → chamfer at (-2,0) and (0,2).
|
|
721
722
|
core.scene.clear();
|
|
722
|
-
core.scene.
|
|
723
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-10, 0), new Point(0, 0), new Point(0, 10)] });
|
|
723
724
|
|
|
724
725
|
core.scene.headers.chamferDistanceA = 2;
|
|
725
726
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -762,7 +763,7 @@ test('Chamfer.action dist>0 trimMode=true same Lwpolyline open ends: separate ch
|
|
|
762
763
|
// dist=2, clicks at (-3,0) and (1,3):
|
|
763
764
|
// firstChamfer = (-1,0), secondChamfer = (1,2).
|
|
764
765
|
core.scene.clear();
|
|
765
|
-
core.scene.
|
|
766
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-5, 0), new Point(0, 0), new Point(1, 0), new Point(1, 5)] });
|
|
766
767
|
|
|
767
768
|
core.scene.headers.chamferDistanceA = 2;
|
|
768
769
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -808,7 +809,7 @@ test('Chamfer.action dist>0 trimMode=true closed Lwpolyline seg3+closing-seg: co
|
|
|
808
809
|
// Closing segment (idx=4) goes (0,10)→(0,0); seg 3 goes (10,10)→(0,10).
|
|
809
810
|
// Corner at (0,10) — chamfer dist=2 trims to (2,10) on seg3 and (0,8) on closing seg.
|
|
810
811
|
core.scene.clear();
|
|
811
|
-
core.scene.
|
|
812
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] });
|
|
812
813
|
const polyEntity = core.scene.entities.get(0);
|
|
813
814
|
polyEntity.flags.setFlagValue(1);
|
|
814
815
|
|
|
@@ -846,7 +847,7 @@ test('Chamfer.action dist>0 trimMode=true closed Lwpolyline closing-wrap (seg4+s
|
|
|
846
847
|
// Bug: old code had no closed-poly guard, so closing-wrap was treated as open-ends.
|
|
847
848
|
// Fix: closed poly must NOT enter the open-ends path → corner-splice at index 0.
|
|
848
849
|
core.scene.clear();
|
|
849
|
-
core.scene.
|
|
850
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] });
|
|
850
851
|
const polyEntity = core.scene.entities.get(0);
|
|
851
852
|
polyEntity.flags.setFlagValue(1);
|
|
852
853
|
|
|
@@ -872,17 +873,26 @@ test('Chamfer.action dist>0 trimMode=true closed Lwpolyline closing-wrap (seg4+s
|
|
|
872
873
|
const poly = core.scene.entities.get(0);
|
|
873
874
|
// Shared vertex (0,0) at index 0 replaced by two chamfer points → total 5 points
|
|
874
875
|
expect(poly.points.length).toBe(5);
|
|
875
|
-
//
|
|
876
|
-
// The
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
expect(
|
|
876
|
+
// Verify the actual chamfer point coordinates and traversal order.
|
|
877
|
+
// The closing segment (0,10)→(0,0) is chamfered at (0,2),
|
|
878
|
+
// seg 1 (0,0)→(10,0) is chamfered at (2,0).
|
|
879
|
+
// After splice: [(0,2), (2,0), (10,0), (10,10), (0,10)]
|
|
880
|
+
expect(poly.points[0].x).toBeCloseTo(0);
|
|
881
|
+
expect(poly.points[0].y).toBeCloseTo(2);
|
|
882
|
+
expect(poly.points[1].x).toBeCloseTo(2);
|
|
883
|
+
expect(poly.points[1].y).toBeCloseTo(0);
|
|
884
|
+
expect(poly.points[2].x).toBeCloseTo(10);
|
|
885
|
+
expect(poly.points[2].y).toBeCloseTo(0);
|
|
886
|
+
expect(poly.points[3].x).toBeCloseTo(10);
|
|
887
|
+
expect(poly.points[3].y).toBeCloseTo(10);
|
|
888
|
+
expect(poly.points[4].x).toBeCloseTo(0);
|
|
889
|
+
expect(poly.points[4].y).toBeCloseTo(10);
|
|
880
890
|
});
|
|
881
891
|
|
|
882
892
|
test('Chamfer.action dist>0 trimMode=false closed Lwpolyline closing-wrap: standalone chamfer Line only', () => {
|
|
883
893
|
// Same square, closed, trimMode=false. No polyline mutation; only chamfer Line added.
|
|
884
894
|
core.scene.clear();
|
|
885
|
-
core.scene.
|
|
895
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10)] });
|
|
886
896
|
const polyEntity = core.scene.entities.get(0);
|
|
887
897
|
polyEntity.flags.setFlagValue(1);
|
|
888
898
|
|
|
@@ -912,7 +922,7 @@ test('Chamfer.action dist>0 trimMode=true open Lwpolyline seg1+lastIdx still tak
|
|
|
912
922
|
// Open 4-point polyline (no flags). Seg 1 and seg 3 (lastIdx=3) → open-ends path still fires.
|
|
913
923
|
// Regression guard: the closed-poly fix must not break the open-poly open-ends case.
|
|
914
924
|
core.scene.clear();
|
|
915
|
-
core.scene.
|
|
925
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-5, 0), new Point(0, 0), new Point(1, 0), new Point(1, 5)] });
|
|
916
926
|
const polyEntity = core.scene.entities.get(0);
|
|
917
927
|
// No flags set — open polyline
|
|
918
928
|
|
|
@@ -939,8 +949,8 @@ test('Chamfer.action dist>0 trimMode=true open Lwpolyline seg1+lastIdx still tak
|
|
|
939
949
|
|
|
940
950
|
test('Chamfer.action dist>0 trimMode=false Line + Lwpolyline: standalone chamfer Line added, entities unchanged', () => {
|
|
941
951
|
core.scene.clear();
|
|
942
|
-
core.scene.
|
|
943
|
-
core.scene.
|
|
952
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
953
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-20, 0), new Point(-10, 0), new Point(0, 0), new Point(10, 0)] });
|
|
944
954
|
|
|
945
955
|
core.scene.headers.chamferDistanceA = 2;
|
|
946
956
|
core.scene.headers.chamferDistanceB = 2;
|
|
@@ -968,3 +978,215 @@ test('Chamfer.action dist>0 trimMode=false Line + Lwpolyline: standalone chamfer
|
|
|
968
978
|
expect(core.scene.entities.get(0).points.length).toBe(2);
|
|
969
979
|
expect(core.scene.entities.get(1).points.length).toBe(4);
|
|
970
980
|
});
|
|
981
|
+
|
|
982
|
+
// ─── action: entity property inheritance ─────────────────────────────────────
|
|
983
|
+
|
|
984
|
+
test('Chamfer.action chamfer line inherits layer, lineWidth, lineType from first entity', () => {
|
|
985
|
+
// Perpendicular L-corner at origin; first entity on a non-default layer.
|
|
986
|
+
core.scene.clear();
|
|
987
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)], layer: 'walls', lineWidth: 5, lineType: 'DASHED' });
|
|
988
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
989
|
+
|
|
990
|
+
core.scene.headers.chamferDistanceA = 2;
|
|
991
|
+
core.scene.headers.chamferDistanceB = 2;
|
|
992
|
+
core.scene.headers.chamferMode = false;
|
|
993
|
+
core.scene.headers.trimMode = false;
|
|
994
|
+
|
|
995
|
+
const chamfer = new Chamfer();
|
|
996
|
+
chamfer.firstPick.entity = core.scene.entities.get(0);
|
|
997
|
+
chamfer.secondPick.entity = core.scene.entities.get(1);
|
|
998
|
+
chamfer.firstPick.clickPoint = new Point(-5, 0);
|
|
999
|
+
chamfer.secondPick.clickPoint = new Point(0, 5);
|
|
1000
|
+
chamfer.action();
|
|
1001
|
+
|
|
1002
|
+
expect(core.scene.entities.count()).toBe(3);
|
|
1003
|
+
const chamferLine = core.scene.entities.get(2);
|
|
1004
|
+
expect(chamferLine.type).toBe('Line');
|
|
1005
|
+
expect(chamferLine.getProperty('layer')).toBe('walls');
|
|
1006
|
+
expect(chamferLine.getProperty('lineWidth')).toBe(5);
|
|
1007
|
+
expect(chamferLine.getProperty('lineType')).toBe('DASHED');
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
test('Chamfer.preview does not throw', () => {
|
|
1011
|
+
const chamfer = new Chamfer();
|
|
1012
|
+
expect(() => chamfer.preview()).not.toThrow();
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
test('Chamfer.preview returns early when findClosestItem returns undefined', () => {
|
|
1016
|
+
core.scene.clear();
|
|
1017
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
1018
|
+
DesignCore.Scene.previewEntities.clear();
|
|
1019
|
+
|
|
1020
|
+
const chamfer = new Chamfer();
|
|
1021
|
+
chamfer.firstPick.entity = core.scene.entities.get(0);
|
|
1022
|
+
chamfer.firstPick.clickPoint = new Point(-5, 0);
|
|
1023
|
+
|
|
1024
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(undefined);
|
|
1025
|
+
DesignCore.Mouse.pointOnScene = () => new Point(0, 5);
|
|
1026
|
+
chamfer.preview();
|
|
1027
|
+
findSpy.mockRestore();
|
|
1028
|
+
|
|
1029
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
test('Chamfer.preview adds chamfer line to previewEntities in no-trim mode', () => {
|
|
1033
|
+
core.scene.clear();
|
|
1034
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
1035
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
1036
|
+
DesignCore.Scene.previewEntities.clear();
|
|
1037
|
+
|
|
1038
|
+
core.scene.headers.chamferDistanceA = 2;
|
|
1039
|
+
core.scene.headers.chamferDistanceB = 2;
|
|
1040
|
+
core.scene.headers.chamferMode = false;
|
|
1041
|
+
core.scene.headers.trimMode = false;
|
|
1042
|
+
|
|
1043
|
+
const chamfer = new Chamfer();
|
|
1044
|
+
chamfer.firstPick.entity = core.scene.entities.get(0);
|
|
1045
|
+
chamfer.firstPick.clickPoint = new Point(-5, 0);
|
|
1046
|
+
|
|
1047
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(1);
|
|
1048
|
+
DesignCore.Mouse.pointOnScene = () => new Point(0, 5);
|
|
1049
|
+
chamfer.preview();
|
|
1050
|
+
findSpy.mockRestore();
|
|
1051
|
+
|
|
1052
|
+
expect(DesignCore.Scene.previewEntities.count()).toBeGreaterThanOrEqual(1);
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
test('Chamfer.preview skips chamfer line in no-trim mode when distances are both 0 (sharp corner)', () => {
|
|
1056
|
+
core.scene.clear();
|
|
1057
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
1058
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
1059
|
+
DesignCore.Scene.previewEntities.clear();
|
|
1060
|
+
|
|
1061
|
+
core.scene.headers.chamferDistanceA = 0;
|
|
1062
|
+
core.scene.headers.chamferDistanceB = 0;
|
|
1063
|
+
core.scene.headers.chamferMode = false;
|
|
1064
|
+
core.scene.headers.trimMode = false;
|
|
1065
|
+
|
|
1066
|
+
const chamfer = new Chamfer();
|
|
1067
|
+
chamfer.firstPick.entity = core.scene.entities.get(0);
|
|
1068
|
+
chamfer.firstPick.clickPoint = new Point(-5, 0);
|
|
1069
|
+
|
|
1070
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(1);
|
|
1071
|
+
DesignCore.Mouse.pointOnScene = () => new Point(0, 5);
|
|
1072
|
+
chamfer.preview();
|
|
1073
|
+
findSpy.mockRestore();
|
|
1074
|
+
|
|
1075
|
+
expect(DesignCore.Scene.previewEntities.count()).toBe(0);
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
test('Chamfer.preview adds 5 entities to previewEntities in trim mode', () => {
|
|
1079
|
+
core.scene.clear();
|
|
1080
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
1081
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
1082
|
+
DesignCore.Scene.previewEntities.clear();
|
|
1083
|
+
|
|
1084
|
+
core.scene.headers.chamferDistanceA = 2;
|
|
1085
|
+
core.scene.headers.chamferDistanceB = 2;
|
|
1086
|
+
core.scene.headers.chamferMode = false;
|
|
1087
|
+
core.scene.headers.trimMode = true;
|
|
1088
|
+
|
|
1089
|
+
const chamfer = new Chamfer();
|
|
1090
|
+
chamfer.firstPick.entity = core.scene.entities.get(0);
|
|
1091
|
+
chamfer.firstPick.clickPoint = new Point(-5, 0);
|
|
1092
|
+
|
|
1093
|
+
const findSpy = jest.spyOn(core.scene.selectionManager, 'findClosestItem').mockReturnValue(1);
|
|
1094
|
+
DesignCore.Mouse.pointOnScene = () => new Point(0, 5);
|
|
1095
|
+
chamfer.preview();
|
|
1096
|
+
findSpy.mockRestore();
|
|
1097
|
+
|
|
1098
|
+
// dulled firstSeg + dulled secondSeg + firstTrimLine + secondTrimLine + chamferLine = 5
|
|
1099
|
+
expect(DesignCore.Scene.previewEntities.count()).toBeGreaterThanOrEqual(5);
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
test('Chamfer.execute returns early when second-entity input is undefined (inner loop)', async () => {
|
|
1103
|
+
core.scene.clear();
|
|
1104
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
1105
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
1106
|
+
|
|
1107
|
+
const executeCommandSpy = jest.fn();
|
|
1108
|
+
await withMockInput(
|
|
1109
|
+
core.scene,
|
|
1110
|
+
[new SingleSelection(0, new Point(-5, 0))],
|
|
1111
|
+
async () => {
|
|
1112
|
+
const chamfer = new Chamfer();
|
|
1113
|
+
await chamfer.execute();
|
|
1114
|
+
expect(executeCommandSpy).not.toHaveBeenCalled();
|
|
1115
|
+
},
|
|
1116
|
+
{ extraMethods: { executeCommand: executeCommandSpy } },
|
|
1117
|
+
);
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
test('Chamfer.execute notifies NONCONSECUTIVESEGMENTS for non-adjacent polyline segments', async () => {
|
|
1121
|
+
// 5-point polyline → 4 segments. Pick segment 1 (near -15,0) then segment 3 (near 5,0).
|
|
1122
|
+
// Neither consecutive nor open-ends → NONCONSECUTIVESEGMENTS, then undefined to exit.
|
|
1123
|
+
core.scene.clear();
|
|
1124
|
+
core.scene.addEntity('Lwpolyline', { points: [new Point(-20, 0), new Point(-10, 0), new Point(0, 0), new Point(10, 0), new Point(20, 0)] });
|
|
1125
|
+
|
|
1126
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
1127
|
+
await withMockInput(
|
|
1128
|
+
core.scene,
|
|
1129
|
+
[new SingleSelection(0, new Point(-15, 0)), new SingleSelection(0, new Point(5, 0)), undefined],
|
|
1130
|
+
async () => {
|
|
1131
|
+
const chamfer = new Chamfer();
|
|
1132
|
+
await chamfer.execute();
|
|
1133
|
+
},
|
|
1134
|
+
);
|
|
1135
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Message.NONCONSECUTIVESEGMENTS));
|
|
1136
|
+
notifySpy.mockRestore();
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
test('Chamfer.execute Method→Distance sets chamferMode to false', async () => {
|
|
1140
|
+
core.scene.headers.chamferMode = true;
|
|
1141
|
+
|
|
1142
|
+
await withMockInput(
|
|
1143
|
+
core.scene,
|
|
1144
|
+
['Method', 'Distance', undefined],
|
|
1145
|
+
async () => {
|
|
1146
|
+
const chamfer = new Chamfer();
|
|
1147
|
+
await chamfer.execute();
|
|
1148
|
+
},
|
|
1149
|
+
);
|
|
1150
|
+
|
|
1151
|
+
expect(core.scene.headers.chamferMode).toBe(false);
|
|
1152
|
+
});
|
|
1153
|
+
|
|
1154
|
+
test('Chamfer.execute Trim→Trim keeps trimMode=true', async () => {
|
|
1155
|
+
core.scene.headers.trimMode = true;
|
|
1156
|
+
|
|
1157
|
+
await withMockInput(
|
|
1158
|
+
core.scene,
|
|
1159
|
+
['Trim', 'Trim', undefined],
|
|
1160
|
+
async () => {
|
|
1161
|
+
const chamfer = new Chamfer();
|
|
1162
|
+
await chamfer.execute();
|
|
1163
|
+
},
|
|
1164
|
+
);
|
|
1165
|
+
|
|
1166
|
+
expect(core.scene.headers.trimMode).toBe(true);
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
test('Chamfer.action angle method angle=180 notifies INVALIDNUMBER', () => {
|
|
1170
|
+
// alpha = 180 * π/180 = π; checks alpha >= Math.PI → INVALIDNUMBER
|
|
1171
|
+
core.scene.clear();
|
|
1172
|
+
core.scene.addEntity('Line', { points: [new Point(-10, 0), new Point(0, 0)] });
|
|
1173
|
+
core.scene.addEntity('Line', { points: [new Point(0, 0), new Point(0, 10)] });
|
|
1174
|
+
|
|
1175
|
+
core.scene.headers.chamferLength = 2;
|
|
1176
|
+
core.scene.headers.chamferAngle = 180;
|
|
1177
|
+
core.scene.headers.chamferMode = true;
|
|
1178
|
+
core.scene.headers.trimMode = true;
|
|
1179
|
+
|
|
1180
|
+
const notifySpy = jest.spyOn(core, 'notify');
|
|
1181
|
+
|
|
1182
|
+
const chamfer = new Chamfer();
|
|
1183
|
+
chamfer.firstPick.entity = core.scene.entities.get(0);
|
|
1184
|
+
chamfer.secondPick.entity = core.scene.entities.get(1);
|
|
1185
|
+
chamfer.firstPick.clickPoint = new Point(-5, 0);
|
|
1186
|
+
chamfer.secondPick.clickPoint = new Point(0, 5);
|
|
1187
|
+
chamfer.action();
|
|
1188
|
+
|
|
1189
|
+
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.INVALIDNUMBER));
|
|
1190
|
+
expect(core.scene.entities.count()).toBe(2);
|
|
1191
|
+
notifySpy.mockRestore();
|
|
1192
|
+
});
|