@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
|
@@ -16,28 +16,28 @@ const arcInputScenarios = [
|
|
|
16
16
|
{
|
|
17
17
|
desc: 'two points and an angle',
|
|
18
18
|
input: [new SingleSelection(0, new Point(5, 0)), 2.5, 'Test Arc'],
|
|
19
|
-
|
|
19
|
+
selectedEntities: [new Arc({ points: [new Point(), new Point(100, 0), new Point(-100, 0)] })],
|
|
20
20
|
expectedTextHeight: 2.5,
|
|
21
21
|
expectedText: 'Test Arc',
|
|
22
22
|
},
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
test.each(arcInputScenarios)('ArcText.execute handles $desc', async (scenario) => {
|
|
26
|
-
const { input,
|
|
26
|
+
const { input, selectedEntities, expectedTextHeight, expectedText } = scenario;
|
|
27
27
|
|
|
28
28
|
await withMockInput(DesignCore.Scene, input, async () => {
|
|
29
29
|
const arcText = new ArcAlignedText();
|
|
30
30
|
await arcText.execute();
|
|
31
31
|
|
|
32
|
-
expect(arcText.styleName).toBe('STANDARD');
|
|
33
|
-
expect(arcText.height).toBe(expectedTextHeight);
|
|
34
|
-
expect(arcText.string).toBe(expectedText);
|
|
32
|
+
expect(arcText.getProperty('styleName')).toBe('STANDARD');
|
|
33
|
+
expect(arcText.getProperty('height')).toBe(expectedTextHeight);
|
|
34
|
+
expect(arcText.getProperty('string')).toBe(expectedText);
|
|
35
35
|
|
|
36
36
|
// arc props
|
|
37
|
-
expect(arcText.radius).toBe(100);
|
|
37
|
+
expect(arcText.getProperty('radius')).toBe(100);
|
|
38
38
|
expect(arcText.startAngle()).toBe(0);
|
|
39
39
|
expect(arcText.endAngle()).toBeCloseTo(3.14159);
|
|
40
|
-
}, {
|
|
40
|
+
}, { selectedEntities });
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
test('ArcAlignedCharacter', () => {
|
|
@@ -52,7 +52,7 @@ test('ArcAlignedCharacter', () => {
|
|
|
52
52
|
expect(ac.position.y).toBe(point.y);
|
|
53
53
|
expect(ac.angle).toBeCloseTo(angle);
|
|
54
54
|
expect(ac.baseline.x).toBe(10.5);
|
|
55
|
-
expect(ac.baseline.y).toBe(19.
|
|
55
|
+
expect(ac.baseline.y).toBe(19.5);
|
|
56
56
|
expect(ac.boundingBox).toBeInstanceOf(BoundingBox);
|
|
57
57
|
});
|
|
58
58
|
|
|
@@ -105,7 +105,7 @@ test('ArcText getArcAlignedCharacters', () => {
|
|
|
105
105
|
// offsetFromLeft 0 - mm
|
|
106
106
|
// textReversed 0 = forward, 1 = reversed
|
|
107
107
|
// textOrientation 1 = outward, 2 = inward
|
|
108
|
-
// textAlignment 1 = fit to arc, 2 = left align, 3 = right align, 4 =
|
|
108
|
+
// textAlignment 1 = fit to arc, 2 = left align, 3 = right align, 4 = centre
|
|
109
109
|
// ArcSide convex = 1, concave = 2
|
|
110
110
|
let chars = [];
|
|
111
111
|
|
|
@@ -115,14 +115,14 @@ test('ArcText getArcAlignedCharacters', () => {
|
|
|
115
115
|
expect(chars.length).toBe(4);
|
|
116
116
|
// First character
|
|
117
117
|
expect(chars[0].character).toBe('T');
|
|
118
|
-
expect(chars[0].position.x).toBeCloseTo(-2.
|
|
119
|
-
expect(chars[0].position.y).toBeCloseTo(101.
|
|
120
|
-
expect(chars[0].angle).toBeCloseTo(0.
|
|
118
|
+
expect(chars[0].position.x).toBeCloseTo(-2.05483);
|
|
119
|
+
expect(chars[0].position.y).toBeCloseTo(101.22915);
|
|
120
|
+
expect(chars[0].angle).toBeCloseTo(0.02030);
|
|
121
121
|
// Fourth character
|
|
122
122
|
expect(chars.at(-1).character).toBe('t');
|
|
123
|
-
expect(chars.at(-1).position.x).toBeCloseTo(2.
|
|
124
|
-
expect(chars.at(-1).position.y).toBeCloseTo(101.
|
|
125
|
-
expect(chars.at(-1).angle).toBeCloseTo(-0.
|
|
123
|
+
expect(chars.at(-1).position.x).toBeCloseTo(2.05483);
|
|
124
|
+
expect(chars.at(-1).position.y).toBeCloseTo(101.22915);
|
|
125
|
+
expect(chars.at(-1).angle).toBeCloseTo(-0.02030);
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
// //////// Text Alignment Tests //////////
|
|
@@ -133,29 +133,29 @@ test('ArcText getArcAlignedCharacters', () => {
|
|
|
133
133
|
expect(chars.length).toBe(4);
|
|
134
134
|
// First character - Reversed!
|
|
135
135
|
expect(chars[0].character).toBe('t');
|
|
136
|
-
expect(chars[0].position.x).toBeCloseTo(101.
|
|
137
|
-
expect(chars[0].position.y).toBeCloseTo(
|
|
138
|
-
expect(chars[0].angle).toBeCloseTo(-1.
|
|
136
|
+
expect(chars[0].position.x).toBeCloseTo(101.23852);
|
|
137
|
+
expect(chars[0].position.y).toBeCloseTo(1.52491);
|
|
138
|
+
expect(chars[0].angle).toBeCloseTo(-1.55573);
|
|
139
139
|
// Last character - Reversed!
|
|
140
140
|
expect(chars.at(-1).character).toBe('T');
|
|
141
|
-
expect(chars.at(-1).position.x).toBeCloseTo(101.
|
|
142
|
-
expect(chars.at(-1).position.y).toBeCloseTo(5.
|
|
143
|
-
expect(chars.at(-1).angle).toBeCloseTo(-1.
|
|
141
|
+
expect(chars.at(-1).position.x).toBeCloseTo(101.09324);
|
|
142
|
+
expect(chars.at(-1).position.y).toBeCloseTo(5.63200);
|
|
143
|
+
expect(chars.at(-1).angle).toBeCloseTo(-1.51514);
|
|
144
144
|
|
|
145
|
-
// Create ArcAlignedText - left aligned - textAlignment =
|
|
145
|
+
// Create ArcAlignedText - left aligned - textAlignment = 2
|
|
146
146
|
const arcTextAlignLeft = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, textAlignment: 2 });
|
|
147
147
|
chars = arcTextAlignLeft.getArcAlignedCharacters();
|
|
148
148
|
expect(chars.length).toBe(4);
|
|
149
149
|
// First character
|
|
150
150
|
expect(chars[0].character).toBe('T');
|
|
151
|
-
expect(chars[0].position.x).toBeCloseTo(-101.
|
|
152
|
-
expect(chars[0].position.y).toBeCloseTo(0.
|
|
153
|
-
expect(chars[0].angle).toBeCloseTo(1.
|
|
151
|
+
expect(chars[0].position.x).toBeCloseTo(-101.24664);
|
|
152
|
+
expect(chars[0].position.y).toBeCloseTo(0.82499);
|
|
153
|
+
expect(chars[0].angle).toBeCloseTo(1.56265);
|
|
154
154
|
// Last character
|
|
155
155
|
expect(chars.at(-1).character).toBe('t');
|
|
156
|
-
expect(chars.at(-1).position.x).toBeCloseTo(-101.
|
|
157
|
-
expect(chars.at(-1).position.y).toBeCloseTo(
|
|
158
|
-
expect(chars.at(-1).angle).toBeCloseTo(1.
|
|
156
|
+
expect(chars.at(-1).position.x).toBeCloseTo(-101.12976);
|
|
157
|
+
expect(chars.at(-1).position.y).toBeCloseTo(4.93298);
|
|
158
|
+
expect(chars.at(-1).angle).toBeCloseTo(1.52206);
|
|
159
159
|
|
|
160
160
|
// Create ArcAlignedText - fit to arc - textAlignment = 1
|
|
161
161
|
const arcTextFitToArc = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, textAlignment: 1 });
|
|
@@ -163,14 +163,14 @@ test('ArcText getArcAlignedCharacters', () => {
|
|
|
163
163
|
expect(chars.length).toBe(4);
|
|
164
164
|
// First character
|
|
165
165
|
expect(chars[0].character).toBe('T');
|
|
166
|
-
expect(chars[0].position.x).toBeCloseTo(-101.
|
|
167
|
-
expect(chars[0].position.y).toBeCloseTo(0.
|
|
168
|
-
expect(chars[0].angle).toBeCloseTo(1.
|
|
166
|
+
expect(chars[0].position.x).toBeCloseTo(-101.24664);
|
|
167
|
+
expect(chars[0].position.y).toBeCloseTo(0.82499);
|
|
168
|
+
expect(chars[0].angle).toBeCloseTo(1.56265);
|
|
169
169
|
// Last character
|
|
170
170
|
expect(chars.at(-1).character).toBe('t');
|
|
171
|
-
expect(chars.at(-1).position.x).toBeCloseTo(101.
|
|
172
|
-
expect(chars.at(-1).position.y).toBeCloseTo(
|
|
173
|
-
expect(chars.at(-1).angle).toBeCloseTo(-1.
|
|
171
|
+
expect(chars.at(-1).position.x).toBeCloseTo(101.23852);
|
|
172
|
+
expect(chars.at(-1).position.y).toBeCloseTo(1.52491);
|
|
173
|
+
expect(chars.at(-1).angle).toBeCloseTo(-1.55573);
|
|
174
174
|
|
|
175
175
|
// //////// Arc Side Tests //////////
|
|
176
176
|
|
|
@@ -180,14 +180,14 @@ test('ArcText getArcAlignedCharacters', () => {
|
|
|
180
180
|
expect(chars.length).toBe(4);
|
|
181
181
|
// First character
|
|
182
182
|
expect(chars[0].character).toBe('T');
|
|
183
|
-
expect(chars[0].position.x).toBeCloseTo(-2.
|
|
184
|
-
expect(chars[0].position.y).toBeCloseTo(101.
|
|
185
|
-
expect(chars[0].angle).toBeCloseTo(0.
|
|
183
|
+
expect(chars[0].position.x).toBeCloseTo(-2.05483);
|
|
184
|
+
expect(chars[0].position.y).toBeCloseTo(101.22915);
|
|
185
|
+
expect(chars[0].angle).toBeCloseTo(0.02030);
|
|
186
186
|
// Fourth character
|
|
187
187
|
expect(chars.at(-1).character).toBe('t');
|
|
188
|
-
expect(chars.at(-1).position.x).toBeCloseTo(2.
|
|
189
|
-
expect(chars.at(-1).position.y).toBeCloseTo(101.
|
|
190
|
-
expect(chars.at(-1).angle).toBeCloseTo(-0.
|
|
188
|
+
expect(chars.at(-1).position.x).toBeCloseTo(2.05483);
|
|
189
|
+
expect(chars.at(-1).position.y).toBeCloseTo(101.22915);
|
|
190
|
+
expect(chars.at(-1).angle).toBeCloseTo(-0.02030);
|
|
191
191
|
|
|
192
192
|
// Create ArcAlignedText with string 'Test' and radius 100
|
|
193
193
|
const arcTextConcave = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, arcSide: 2 });
|
|
@@ -195,14 +195,14 @@ test('ArcText getArcAlignedCharacters', () => {
|
|
|
195
195
|
expect(chars.length).toBe(4);
|
|
196
196
|
// First character
|
|
197
197
|
expect(chars[0].character).toBe('T');
|
|
198
|
-
expect(chars[0].position.x).toBeCloseTo(-2.
|
|
199
|
-
expect(chars[0].position.y).toBeCloseTo(98.
|
|
200
|
-
expect(chars[0].angle).toBeCloseTo(0.
|
|
198
|
+
expect(chars[0].position.x).toBeCloseTo(-2.05482);
|
|
199
|
+
expect(chars[0].position.y).toBeCloseTo(98.72862);
|
|
200
|
+
expect(chars[0].angle).toBeCloseTo(0.02081);
|
|
201
201
|
// Fourth character
|
|
202
202
|
expect(chars.at(-1).character).toBe('t');
|
|
203
|
-
expect(chars.at(-1).position.x).toBeCloseTo(2.
|
|
204
|
-
expect(chars.at(-1).position.y).toBeCloseTo(98.
|
|
205
|
-
expect(chars.at(-1).angle).toBeCloseTo(-0.
|
|
203
|
+
expect(chars.at(-1).position.x).toBeCloseTo(2.05482);
|
|
204
|
+
expect(chars.at(-1).position.y).toBeCloseTo(98.72862);
|
|
205
|
+
expect(chars.at(-1).angle).toBeCloseTo(-0.02081);
|
|
206
206
|
});
|
|
207
207
|
|
|
208
208
|
test('ArcText.dxf', () => {
|
|
@@ -290,7 +290,21 @@ STANDARD
|
|
|
290
290
|
expect(file.contents).toEqual(dxfString);
|
|
291
291
|
|
|
292
292
|
// create new entity from entity data to ensure all props are loaded
|
|
293
|
-
const newArcAlignedText = new ArcAlignedText(
|
|
293
|
+
const newArcAlignedText = new ArcAlignedText({
|
|
294
|
+
handle: arctext.getProperty('handle'),
|
|
295
|
+
points: arctext.points,
|
|
296
|
+
string: arctext.getProperty('string'),
|
|
297
|
+
styleName: arctext.getProperty('styleName'),
|
|
298
|
+
radius: arctext.getProperty('radius'),
|
|
299
|
+
height: arctext.getProperty('height'),
|
|
300
|
+
characterSpacing: arctext.getProperty('characterSpacing'),
|
|
301
|
+
offsetFromArc: arctext.getProperty('offsetFromArc'),
|
|
302
|
+
offsetFromRight: arctext.getProperty('offsetFromRight'),
|
|
303
|
+
offsetFromLeft: arctext.getProperty('offsetFromLeft'),
|
|
304
|
+
textOrientation: arctext.getProperty('textOrientation'),
|
|
305
|
+
textAlignment: arctext.getProperty('textAlignment'),
|
|
306
|
+
arcSide: arctext.getProperty('arcSide'),
|
|
307
|
+
});
|
|
294
308
|
file = new File();
|
|
295
309
|
newArcAlignedText.dxf(file);
|
|
296
310
|
expect(file.contents).toEqual(dxfString);
|
|
@@ -301,32 +315,32 @@ test('Test ArcText.snaps', () => {
|
|
|
301
315
|
const point = new Point(100, 100);
|
|
302
316
|
const snaps = arcText.snaps(point, 1);
|
|
303
317
|
const nodeSnaps = snaps.filter((s) => s.type === 'node');
|
|
304
|
-
expect(nodeSnaps[0].snapPoint.x).toBeCloseTo(-2.
|
|
305
|
-
expect(nodeSnaps[0].snapPoint.y).toBeCloseTo(101.
|
|
318
|
+
expect(nodeSnaps[0].snapPoint.x).toBeCloseTo(-2.05483);
|
|
319
|
+
expect(nodeSnaps[0].snapPoint.y).toBeCloseTo(101.22915);
|
|
306
320
|
});
|
|
307
321
|
|
|
308
322
|
test('Test ArcText.closestPoint', () => {
|
|
309
323
|
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100 });
|
|
310
324
|
const point = new Point(100, 100);
|
|
311
325
|
const closest = arcText.closestPoint(point);
|
|
312
|
-
expect(closest[0].x).toBeCloseTo(2.
|
|
313
|
-
expect(closest[0].y).toBeCloseTo(101.
|
|
326
|
+
expect(closest[0].x).toBeCloseTo(2.05483);
|
|
327
|
+
expect(closest[0].y).toBeCloseTo(101.22915);
|
|
314
328
|
});
|
|
315
329
|
|
|
316
330
|
test('Test ArcText.boundingBox', () => {
|
|
317
331
|
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100 });
|
|
318
|
-
expect(arcText.boundingBox().xMin).toBeCloseTo(-
|
|
319
|
-
expect(arcText.boundingBox().xMax).toBeCloseTo(
|
|
320
|
-
expect(arcText.boundingBox().yMin).toBeCloseTo(99.
|
|
321
|
-
expect(arcText.boundingBox().yMax).toBeCloseTo(102.
|
|
332
|
+
expect(arcText.boundingBox().xMin).toBeCloseTo(-2.81733);
|
|
333
|
+
expect(arcText.boundingBox().xMax).toBeCloseTo(2.46733);
|
|
334
|
+
expect(arcText.boundingBox().yMin).toBeCloseTo(99.97915);
|
|
335
|
+
expect(arcText.boundingBox().yMax).toBeCloseTo(102.49878);
|
|
322
336
|
});
|
|
323
337
|
|
|
324
338
|
test('Test ArcText.toPolylinePoints - returns correct points array', () => {
|
|
325
339
|
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100 });
|
|
326
340
|
const result = arcText.toPolylinePoints();
|
|
327
341
|
expect(result.length).toBe(4);
|
|
328
|
-
expect(result[0].x).toBeCloseTo(-2.
|
|
329
|
-
expect(result[0].y).toBeCloseTo(101.
|
|
342
|
+
expect(result[0].x).toBeCloseTo(-2.05483);
|
|
343
|
+
expect(result[0].y).toBeCloseTo(101.22915);
|
|
330
344
|
});
|
|
331
345
|
|
|
332
346
|
test('ArcText.snaps returns a centre snap at points[0]', () => {
|
|
@@ -343,3 +357,116 @@ test('ArcText.snaps returns node snaps only for non-space characters', () => {
|
|
|
343
357
|
const nodeSnaps = arcText.snaps(new Point(0, 0), 100).filter((s) => s.type === 'node');
|
|
344
358
|
expect(nodeSnaps.length).toBe(2);
|
|
345
359
|
});
|
|
360
|
+
|
|
361
|
+
test('ArcAlignedCharacter respects explicit height and width', () => {
|
|
362
|
+
const ac = new ArcAlignedCharacter('A', new Point(0, 0), 0, 5, 3);
|
|
363
|
+
expect(ac.height).toBe(5);
|
|
364
|
+
expect(ac.width).toBe(3);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('ArcText getArcAlignedCharacters empty string returns empty array', () => {
|
|
368
|
+
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: '', radius: 100 });
|
|
369
|
+
expect(arcText.getArcAlignedCharacters()).toHaveLength(0);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test('ArcText boundingBox empty string returns BoundingBox instance', () => {
|
|
373
|
+
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: '', radius: 100 });
|
|
374
|
+
expect(arcText.boundingBox()).toBeInstanceOf(BoundingBox);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test('ArcText toPolylinePoints empty string returns empty array', () => {
|
|
378
|
+
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: '', radius: 100 });
|
|
379
|
+
expect(arcText.toPolylinePoints()).toHaveLength(0);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test('ArcText snaps with empty string returns only centre snap', () => {
|
|
383
|
+
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: '', radius: 100 });
|
|
384
|
+
const snaps = arcText.snaps(new Point(0, 0), 1);
|
|
385
|
+
expect(snaps).toHaveLength(1);
|
|
386
|
+
expect(snaps[0].type).toBe('centre');
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test('ArcText closestPoint returns distance 0.1 when within height * 0.35', () => {
|
|
390
|
+
// Single char 'T' centre-aligned sits at (0, 101.25); height = 2.5, threshold = 0.875
|
|
391
|
+
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'T', radius: 100 });
|
|
392
|
+
const chars = arcText.getArcAlignedCharacters();
|
|
393
|
+
const charPos = chars[0].position;
|
|
394
|
+
// 0.5 units away is within the 0.875 threshold
|
|
395
|
+
const closePoint = new Point(charPos.x + 0.5, charPos.y);
|
|
396
|
+
const [, dist] = arcText.closestPoint(closePoint);
|
|
397
|
+
expect(dist).toBe(0.1);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test('ArcText getArcAlignedCharacters fit-to-arc single character places at midpoint', () => {
|
|
401
|
+
const t = new ArcAlignedText({ points: [new Point(0, 0)], radius: 10, string: 'X',
|
|
402
|
+
startAngle: 0, endAngle: 180, textAlignment: 1 });
|
|
403
|
+
const chars = t.getArcAlignedCharacters();
|
|
404
|
+
expect(chars).toHaveLength(1);
|
|
405
|
+
// midpoint of 0°–180° arc at radius 10 is 90° → point near (0, 10)
|
|
406
|
+
expect(chars[0].position.x).toBeCloseTo(0, 0);
|
|
407
|
+
expect(chars[0].position.y).toBeGreaterThan(0);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test('ArcText getArcAlignedCharacters single character is placed at arc midpoint', () => {
|
|
411
|
+
// Single char with centre alignment: charOffsetAngles = [0], stringStartPoint = arcMidPoint
|
|
412
|
+
// arcMidAngle(0, PI) = PI/2 → project(PI/2, 101.25) = (0, 101.25), angle = PI/2 - PI/2 = 0
|
|
413
|
+
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'X', radius: 100 });
|
|
414
|
+
const chars = arcText.getArcAlignedCharacters();
|
|
415
|
+
expect(chars).toHaveLength(1);
|
|
416
|
+
expect(chars[0].character).toBe('X');
|
|
417
|
+
expect(chars[0].position.x).toBeCloseTo(0, 4);
|
|
418
|
+
expect(chars[0].position.y).toBeCloseTo(101.25, 4);
|
|
419
|
+
expect(chars[0].angle).toBeCloseTo(0, 4);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
test('ArcText getArcAlignedCharacters textOrientation inward reverses character order', () => {
|
|
423
|
+
// textOrientation 2 reverses the string before placing characters
|
|
424
|
+
const arcText = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, textOrientation: 2 });
|
|
425
|
+
const chars = arcText.getArcAlignedCharacters();
|
|
426
|
+
expect(chars).toHaveLength(4);
|
|
427
|
+
expect(chars[0].character).toBe('t'); // reversed
|
|
428
|
+
expect(chars.at(-1).character).toBe('T'); // reversed
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('ArcText getArcAlignedCharacters offsetFromLeft shifts character positions', () => {
|
|
432
|
+
// offsetFromLeft moves the string start away from the arc end for left-aligned text
|
|
433
|
+
const base = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, textAlignment: 2, offsetFromLeft: 0 });
|
|
434
|
+
const offset = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, textAlignment: 2, offsetFromLeft: 10 });
|
|
435
|
+
const baseChars = base.getArcAlignedCharacters();
|
|
436
|
+
const offsetChars = offset.getArcAlignedCharacters();
|
|
437
|
+
expect(offsetChars[0].position.x).not.toBeCloseTo(baseChars[0].position.x, 1);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
test('ArcText getArcAlignedCharacters offsetFromRight shifts character positions', () => {
|
|
441
|
+
// offsetFromRight moves the string start away from the arc start for right-aligned text
|
|
442
|
+
const base = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, textAlignment: 3, offsetFromRight: 0 });
|
|
443
|
+
const offset = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Test', radius: 100, textAlignment: 3, offsetFromRight: 10 });
|
|
444
|
+
const baseChars = base.getArcAlignedCharacters();
|
|
445
|
+
const offsetChars = offset.getArcAlignedCharacters();
|
|
446
|
+
expect(offsetChars[0].position.x).not.toBeCloseTo(baseChars[0].position.x, 1);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test('ArcAlignedText.toCharacters returns one entry per character with scene coords', () => {
|
|
450
|
+
const arc = new ArcAlignedText({ points: [new Point(0, 0)], string: 'AB', radius: 100 });
|
|
451
|
+
const chars = arc.toCharacters();
|
|
452
|
+
expect(chars).toHaveLength(2);
|
|
453
|
+
expect(chars[0].char).toBe('A');
|
|
454
|
+
expect(chars[1].char).toBe('B');
|
|
455
|
+
expect(typeof chars[0].x).toBe('number');
|
|
456
|
+
expect(typeof chars[0].y).toBe('number');
|
|
457
|
+
expect(typeof chars[0].rotation).toBe('number');
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('ArcAlignedText.draw calls renderer.drawText with all characters', () => {
|
|
461
|
+
const arc = new ArcAlignedText({ points: [new Point(0, 0)], string: 'Hi', radius: 50 });
|
|
462
|
+
let calledChars;
|
|
463
|
+
const mockRenderer = {
|
|
464
|
+
drawText(characters) {
|
|
465
|
+
calledChars = characters;
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
arc.draw(mockRenderer);
|
|
469
|
+
expect(calledChars).toHaveLength(2);
|
|
470
|
+
expect(calledChars[0].char).toBe('H');
|
|
471
|
+
expect(calledChars[1].char).toBe('i');
|
|
472
|
+
});
|
|
@@ -96,7 +96,7 @@ ByLayer
|
|
|
96
96
|
30
|
|
97
97
|
0.0
|
|
98
98
|
39
|
|
99
|
-
|
|
99
|
+
1
|
|
100
100
|
40
|
|
101
101
|
100
|
|
102
102
|
`;
|
|
@@ -105,7 +105,7 @@ ByLayer
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
// create new entity from entity data to ensure all props are loaded
|
|
108
|
-
const newCircle = new Circle(circle);
|
|
108
|
+
const newCircle = new Circle({ handle: circle.getProperty('handle'), points: circle.points });
|
|
109
109
|
file = new File();
|
|
110
110
|
newCircle.dxf(file);
|
|
111
111
|
expect(file.contents).toEqual(dxfString);
|
|
@@ -127,90 +127,23 @@ test('Test Circle.toPolylinePoints', () => {
|
|
|
127
127
|
expect(circlePoints[2].bulge).toBe(0);
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
-
test('Test Circle.trim returns empty array when provided with empty intersection list', () => {
|
|
131
|
-
const circle = new Circle({ points: [new Point(0, 0), new Point(100, 0)] });
|
|
132
|
-
|
|
133
|
-
expect(circle.trim([])).toEqual([]);
|
|
134
|
-
expect(circle.trim()).toEqual([]);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('Test Circle.trim does not modify circle', () => {
|
|
138
|
-
const circle = new Circle({ points: [new Point(0, 0), new Point(10, 0)] });
|
|
139
|
-
|
|
140
|
-
DesignCore.Mouse.pointOnScene = () => new Point(7.71, 7.71);
|
|
141
|
-
|
|
142
|
-
// test a single intersection - not valid
|
|
143
|
-
const intersections = [new Point(10, 0)];
|
|
144
|
-
expect(circle.trim(intersections)).toEqual([]);
|
|
145
|
-
|
|
146
|
-
// test two intersections - should return an arc
|
|
147
|
-
intersections.push(new Point(-10, 0));
|
|
148
|
-
let trimWithTwoPoints = circle.trim(intersections);
|
|
149
|
-
let trimResult = trimWithTwoPoints[0].entity;
|
|
150
|
-
expect(trimResult.type).toEqual('Arc');
|
|
151
|
-
// Verify trimmed arc has a unique handle (not the circle's)
|
|
152
|
-
expect(trimResult.handle).toBeUndefined();
|
|
153
|
-
expect(trimResult.points[0].x).toEqual(0);
|
|
154
|
-
expect(trimResult.points[0].y).toEqual(0);
|
|
155
|
-
|
|
156
|
-
expect(trimResult.points[1].x).toEqual(10);
|
|
157
|
-
expect(trimResult.points[1].y).toEqual(0);
|
|
158
|
-
|
|
159
|
-
expect(trimResult.points[2].x).toEqual(-10);
|
|
160
|
-
expect(trimResult.points[2].y).toEqual(0);
|
|
161
|
-
|
|
162
|
-
expect(trimWithTwoPoints[1].entity).toEqual(circle);
|
|
163
|
-
|
|
164
|
-
// test three intersections - should return an arc from first two points found
|
|
165
|
-
intersections.push(new Point(0, 10));
|
|
166
|
-
trimWithTwoPoints = circle.trim(intersections);
|
|
167
|
-
trimResult = trimWithTwoPoints[0].entity;
|
|
168
|
-
expect(trimResult.type).toEqual('Arc');
|
|
169
|
-
expect(trimResult.points[0].x).toEqual(0);
|
|
170
|
-
expect(trimResult.points[0].y).toEqual(0);
|
|
171
|
-
|
|
172
|
-
expect(trimResult.points[1].x).toEqual(10);
|
|
173
|
-
expect(trimResult.points[1].y).toEqual(0);
|
|
174
|
-
|
|
175
|
-
expect(trimResult.points[2].x).toEqual(0);
|
|
176
|
-
expect(trimResult.points[2].y).toEqual(10);
|
|
177
|
-
|
|
178
|
-
expect(trimWithTwoPoints[1].entity).toEqual(circle);
|
|
179
|
-
|
|
180
|
-
// test four intersections - should return an arc from first two points found
|
|
181
|
-
intersections.push(new Point(0, -10));
|
|
182
|
-
trimWithTwoPoints = circle.trim(intersections);
|
|
183
|
-
trimResult = trimWithTwoPoints[0].entity;
|
|
184
|
-
expect(trimResult.type).toEqual('Arc');
|
|
185
|
-
expect(trimResult.points[0].x).toEqual(0);
|
|
186
|
-
expect(trimResult.points[0].y).toEqual(0);
|
|
187
|
-
|
|
188
|
-
expect(trimResult.points[1].x).toEqual(10);
|
|
189
|
-
expect(trimResult.points[1].y).toEqual(0);
|
|
190
|
-
|
|
191
|
-
expect(trimResult.points[2].x).toEqual(0);
|
|
192
|
-
expect(trimResult.points[2].y).toEqual(10);
|
|
193
|
-
|
|
194
|
-
expect(trimWithTwoPoints[1].entity).toEqual(circle);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
130
|
test('Circle.execute creates circle from diameter input', async () => {
|
|
198
|
-
const
|
|
131
|
+
const centre = new Point(0, 0);
|
|
199
132
|
|
|
200
|
-
await withMockInput(DesignCore.Scene, [
|
|
133
|
+
await withMockInput(DesignCore.Scene, [centre, 'Diameter', 20], async () => {
|
|
201
134
|
const circle = new Circle({});
|
|
202
135
|
await circle.execute();
|
|
203
136
|
|
|
204
|
-
expect(circle.points[0]).toBe(
|
|
137
|
+
expect(circle.points[0]).toBe(centre);
|
|
205
138
|
expect(circle.getRadius()).toBeCloseTo(10);
|
|
206
139
|
});
|
|
207
140
|
});
|
|
208
141
|
|
|
209
142
|
test('Circle.execute re-prompts on zero or negative radius', async () => {
|
|
210
|
-
const
|
|
143
|
+
const centre = new Point(0, 0);
|
|
211
144
|
|
|
212
145
|
// 0 and -5 are rejected; 10 is accepted
|
|
213
|
-
await withMockInput(DesignCore.Scene, [
|
|
146
|
+
await withMockInput(DesignCore.Scene, [centre, 0, -5, 10], async () => {
|
|
214
147
|
const circle = new Circle({});
|
|
215
148
|
await circle.execute();
|
|
216
149
|
|
|
@@ -219,10 +152,10 @@ test('Circle.execute re-prompts on zero or negative radius', async () => {
|
|
|
219
152
|
});
|
|
220
153
|
|
|
221
154
|
test('Circle.execute re-prompts on zero or negative diameter', async () => {
|
|
222
|
-
const
|
|
155
|
+
const centre = new Point(0, 0);
|
|
223
156
|
|
|
224
157
|
// 'Diameter', then 0 and -20 rejected, then 20 accepted (radius = 10)
|
|
225
|
-
await withMockInput(DesignCore.Scene, [
|
|
158
|
+
await withMockInput(DesignCore.Scene, [centre, 'Diameter', 0, -20, 20], async () => {
|
|
226
159
|
const circle = new Circle({});
|
|
227
160
|
await circle.execute();
|
|
228
161
|
|
|
@@ -234,16 +167,16 @@ test('Circle.register returns command object', () => {
|
|
|
234
167
|
expect(Circle.register()).toEqual({ command: 'Circle', shortcut: 'C', type: 'Entity' });
|
|
235
168
|
});
|
|
236
169
|
|
|
237
|
-
test('Circle constructor loads radius from DXF group code 40', () => {
|
|
238
|
-
const circle = new Circle({ points: [new Point(0, 0)], 40: 50 });
|
|
170
|
+
test('Circle constructor loads radius from DXF group code 40 via fromDxf', () => {
|
|
171
|
+
const circle = new Circle(Circle.fromDxf({ points: [new Point(0, 0)], 40: 50 }));
|
|
239
172
|
expect(circle.getRadius()).toBeCloseTo(50);
|
|
240
173
|
expect(circle.points[1].x).toBeCloseTo(50);
|
|
241
174
|
expect(circle.points[1].y).toBeCloseTo(0);
|
|
242
175
|
});
|
|
243
176
|
|
|
244
177
|
test('Circle.execute creates circle from numeric radius input', async () => {
|
|
245
|
-
const
|
|
246
|
-
await withMockInput(DesignCore.Scene, [
|
|
178
|
+
const centre = new Point(0, 0);
|
|
179
|
+
await withMockInput(DesignCore.Scene, [centre, 25], async () => {
|
|
247
180
|
const circle = new Circle({});
|
|
248
181
|
await circle.execute();
|
|
249
182
|
expect(circle.getRadius()).toBeCloseTo(25);
|
|
@@ -252,8 +185,8 @@ test('Circle.execute creates circle from numeric radius input', async () => {
|
|
|
252
185
|
|
|
253
186
|
test('Circle.execute re-prompts on zero-distance point input', async () => {
|
|
254
187
|
const notifySpy = jest.spyOn(DesignCore.Core, 'notify').mockImplementation(() => {});
|
|
255
|
-
const
|
|
256
|
-
await withMockInput(DesignCore.Scene, [
|
|
188
|
+
const centre = new Point(0, 0);
|
|
189
|
+
await withMockInput(DesignCore.Scene, [centre, new Point(0, 0), new Point(10, 0)], async () => {
|
|
257
190
|
const circle = new Circle({});
|
|
258
191
|
await circle.execute();
|
|
259
192
|
expect(notifySpy).toHaveBeenCalledWith(expect.stringContaining(Strings.Error.NONZERO));
|
|
@@ -263,10 +196,10 @@ test('Circle.execute re-prompts on zero-distance point input', async () => {
|
|
|
263
196
|
});
|
|
264
197
|
|
|
265
198
|
test('Circle.preview does not throw with 0 or 1 points', () => {
|
|
266
|
-
const origCreate = DesignCore.Scene.
|
|
199
|
+
const origCreate = DesignCore.Scene.previewEntities.create;
|
|
267
200
|
const origMouse = DesignCore.Mouse.pointOnScene;
|
|
268
201
|
const calls = [];
|
|
269
|
-
DesignCore.Scene.
|
|
202
|
+
DesignCore.Scene.previewEntities.create = (type, obj) => calls.push([type, obj]);
|
|
270
203
|
DesignCore.Mouse.pointOnScene = () => new Point(5, 5);
|
|
271
204
|
|
|
272
205
|
const circle0 = new Circle({});
|
|
@@ -278,16 +211,20 @@ test('Circle.preview does not throw with 0 or 1 points', () => {
|
|
|
278
211
|
expect(() => circle1.preview()).not.toThrow();
|
|
279
212
|
expect(calls.some(([type]) => type === 'Circle')).toBe(true);
|
|
280
213
|
|
|
281
|
-
DesignCore.Scene.
|
|
214
|
+
DesignCore.Scene.previewEntities.create = origCreate;
|
|
282
215
|
DesignCore.Mouse.pointOnScene = origMouse;
|
|
283
216
|
});
|
|
284
217
|
|
|
285
|
-
test('Circle.draw
|
|
218
|
+
test('Circle.draw calls renderer.drawShape with toPolylinePoints', () => {
|
|
286
219
|
const circle = new Circle({ points: [new Point(0, 0), new Point(10, 0)] });
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
220
|
+
let capturedPoints;
|
|
221
|
+
const mockRenderer = {
|
|
222
|
+
drawShape(points) {
|
|
223
|
+
capturedPoints = points;
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
circle.draw(mockRenderer);
|
|
227
|
+
expect(capturedPoints).toEqual(circle.toPolylinePoints());
|
|
291
228
|
});
|
|
292
229
|
|
|
293
230
|
test('Circle.snaps returns centre snap', () => {
|
|
@@ -326,21 +263,21 @@ test('Circle.snaps nearest snap does not fire when too far', () => {
|
|
|
326
263
|
test('Circle.fromPolylinePoints round-trip preserves centre and radius', () => {
|
|
327
264
|
const circle = new Circle({ points: [new Point(5, 10), new Point(5, 10).project(0, 8)] });
|
|
328
265
|
const polyPts = circle.toPolylinePoints();
|
|
329
|
-
const rebuilt = new Circle({});
|
|
330
|
-
rebuilt.
|
|
266
|
+
const rebuilt = new Circle({}).fromPolylinePoints(polyPts);
|
|
267
|
+
expect(rebuilt.type).toBe('Circle');
|
|
331
268
|
expect(rebuilt.points[0].x).toBeCloseTo(5, 10);
|
|
332
269
|
expect(rebuilt.points[0].y).toBeCloseTo(10, 10);
|
|
333
|
-
expect(rebuilt.radius).toBeCloseTo(8, 10);
|
|
270
|
+
expect(rebuilt.getProperty('radius')).toBeCloseTo(8, 10);
|
|
334
271
|
});
|
|
335
272
|
|
|
336
273
|
test('Circle.fromPolylinePoints precision drift is negligible', () => {
|
|
337
274
|
const circle = new Circle({ points: [new Point(123.456, -789.012), new Point(123.456, -789.012).project(1.234, 56.789)] });
|
|
338
275
|
const polyPts = circle.toPolylinePoints();
|
|
339
|
-
const rebuilt = new Circle({});
|
|
340
|
-
rebuilt.
|
|
276
|
+
const rebuilt = new Circle({}).fromPolylinePoints(polyPts);
|
|
277
|
+
expect(rebuilt.type).toBe('Circle');
|
|
341
278
|
const drift = Math.hypot(rebuilt.points[0].x - circle.points[0].x, rebuilt.points[0].y - circle.points[0].y);
|
|
342
279
|
expect(drift).toBeLessThan(1e-10);
|
|
343
|
-
expect(Math.abs(rebuilt.radius - circle.radius)).toBeLessThan(1e-10);
|
|
280
|
+
expect(Math.abs(rebuilt.getProperty('radius') - circle.getProperty('radius'))).toBeLessThan(1e-10);
|
|
344
281
|
});
|
|
345
282
|
|
|
346
283
|
test('Circle.snaps returns two tangent snaps from outside', () => {
|
|
@@ -357,6 +294,41 @@ test('Circle.snaps returns two tangent snaps from outside', () => {
|
|
|
357
294
|
DesignCore.Scene.inputManager.inputPoint = savedInputPoint;
|
|
358
295
|
});
|
|
359
296
|
|
|
297
|
+
describe('Circle.fromDxf', () => {
|
|
298
|
+
test('projects edge point from DXF radius code', () => {
|
|
299
|
+
const centre = new Point(0, 0);
|
|
300
|
+
const result = Circle.fromDxf({ points: [centre], 40: 10 });
|
|
301
|
+
expect(result.points.length).toBe(2);
|
|
302
|
+
expect(result.points[0]).toBe(centre);
|
|
303
|
+
expect(result.points[1].x).toBeCloseTo(10);
|
|
304
|
+
expect(result.points[1].y).toBeCloseTo(0);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('preserves all other data properties', () => {
|
|
308
|
+
const result = Circle.fromDxf({ points: [new Point(0, 0)], 40: 10, 8: 'MyLayer' });
|
|
309
|
+
expect(result[8]).toBe('MyLayer');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test('returns data unchanged when no centre point', () => {
|
|
313
|
+
const data = { 40: 10 };
|
|
314
|
+
expect(Circle.fromDxf(data)).toBe(data);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('returns data unchanged when no radius code', () => {
|
|
318
|
+
const data = { points: [new Point(0, 0)] };
|
|
319
|
+
expect(Circle.fromDxf(data)).toBe(data);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('circle constructed via fromDxf matches circle constructed directly', () => {
|
|
323
|
+
const centre = new Point(100, 100);
|
|
324
|
+
const fromDxfCircle = new Circle(Circle.fromDxf({ points: [centre], 40: 50 }));
|
|
325
|
+
const directCircle = new Circle({ points: [centre, new Point(150, 100)] });
|
|
326
|
+
expect(fromDxfCircle.getRadius()).toBeCloseTo(directCircle.getRadius());
|
|
327
|
+
expect(fromDxfCircle.points[0].x).toBeCloseTo(directCircle.points[0].x);
|
|
328
|
+
expect(fromDxfCircle.points[0].y).toBeCloseTo(directCircle.points[0].y);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
360
332
|
test('Circle.snaps returns no tangent when fromPoint is inside the circle', () => {
|
|
361
333
|
const circle = new Circle({ points: [new Point(0, 0), new Point(10, 0)] });
|
|
362
334
|
const savedInputPoint = DesignCore.Scene.inputManager.inputPoint;
|