@design-core/core 0.0.6 → 0.0.7
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/TODO.md +49 -6
- package/core/core/core.js +0 -3
- package/core/dimensions/alignedDimension.js +5 -4
- package/core/dimensions/angularDimension.js +6 -5
- package/core/dimensions/baseCircularDimension.js +3 -2
- package/core/dimensions/baseDimension.js +59 -93
- package/core/dimensions/baseLinearDimension.js +6 -5
- package/core/dimensions/diametricDimension.js +4 -3
- package/core/dimensions/dimension.js +21 -14
- package/core/dimensions/radialDimension.js +4 -3
- package/core/dimensions/rotatedDimension.js +12 -11
- package/core/entities/arc.js +112 -145
- package/core/entities/arctext.js +212 -174
- package/core/entities/basePolyline.js +30 -282
- package/core/entities/circle.js +77 -99
- package/core/entities/entity.js +114 -93
- package/core/entities/hatch.js +322 -249
- package/core/entities/insert.js +66 -81
- package/core/entities/line.js +21 -142
- package/core/entities/lwpolyline.js +16 -11
- package/core/entities/polyline.js +4 -4
- package/core/entities/rectangle.js +3 -1
- package/core/entities/solid.js +6 -15
- package/core/entities/text.js +188 -199
- 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/canvas.js +228 -166
- package/core/lib/clipboard.js +7 -1
- 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 +4 -3
- package/core/lib/entityManager.js +18 -11
- package/core/lib/inputManager.js +35 -11
- package/core/lib/intersect.js +25 -0
- package/core/lib/patterns.js +8 -1
- 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 +27 -5
- package/core/lib/selectionManager.js +2 -2
- 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 +21 -2
- package/core/properties/entityProperties.js +104 -0
- package/core/properties/property.js +106 -2
- package/core/properties/propertyManager.js +29 -13
- package/core/tables/block.js +4 -37
- package/core/tables/endblk.js +1 -1
- package/core/tables/layer.js +8 -0
- package/core/tables/styleManagerBase.js +3 -2
- package/core/tables/tableManagerBase.js +8 -3
- package/core/tools/chamfer.js +111 -137
- package/core/tools/chamferFilletBase.js +135 -34
- package/core/tools/copy.js +3 -3
- package/core/tools/explode.js +2 -2
- package/core/tools/extend.js +132 -19
- package/core/tools/fillet.js +86 -183
- package/core/tools/matchProp.js +2 -11
- package/core/tools/mirror.js +7 -6
- package/core/tools/move.js +3 -3
- package/core/tools/offset.js +3 -5
- package/core/tools/pasteclip.js +1 -1
- package/core/tools/rotate.js +6 -3
- package/core/tools/scale.js +1 -1
- package/core/tools/trim.js +265 -23
- package/core/tools/zoom.js +1 -1
- package/jest.config.js +6 -0
- package/package.json +5 -3
- package/test/dimensions/alignedDimension.test.js +14 -7
- package/test/dimensions/angularDimension.test.js +6 -6
- package/test/dimensions/baseDimension.test.js +69 -8
- package/test/dimensions/baseLinearDimension.test.js +62 -0
- package/test/dimensions/diametricDimension.test.js +6 -6
- package/test/dimensions/radialDimension.test.js +6 -6
- package/test/dimensions/rotatedDimension.test.js +14 -7
- package/test/entities/arc.test.js +80 -83
- package/test/entities/arctext.test.js +180 -53
- package/test/entities/basePolyline.test.js +68 -2
- package/test/entities/circle.test.js +57 -85
- package/test/entities/entity.test.js +42 -8
- package/test/entities/hatch.test.js +543 -70
- package/test/entities/insert.test.js +96 -1
- package/test/entities/line.test.js +15 -41
- package/test/entities/lwpolyline.test.js +104 -0
- package/test/entities/polyline.test.js +11 -0
- package/test/entities/rectangle.test.js +11 -0
- package/test/entities/solid.test.js +1 -1
- package/test/entities/text.test.js +294 -69
- package/test/integration/exportIntegration.reference.dxf +9 -9
- package/test/integration/exportIntegration.test.js +2 -2
- 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/canvas.test.js +742 -123
- package/test/lib/constants.test.js +27 -0
- package/test/lib/entityManager.test.js +4 -4
- package/test/lib/inputManager.test.js +80 -19
- 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/settings.test.js +5 -6
- package/test/lib/snapping.test.js +71 -129
- 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 +3 -3
- 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 +31 -1
- package/test/tables/endblk.test.js +2 -2
- package/test/tables/layer.test.js +30 -0
- package/test/test-helpers/hatchGridRenderer.js +180 -0
- package/test/test-helpers/test-helpers.js +152 -0
- package/test/tools/chamfer.test.js +227 -5
- package/test/tools/chamferFilletBase.test.js +98 -9
- package/test/tools/copy.test.js +2 -2
- package/test/tools/explode.test.js +3 -3
- package/test/tools/extend.test.js +331 -0
- package/test/tools/fillet.test.js +181 -0
- package/test/tools/matchProp.test.js +5 -5
- package/test/tools/mirror.test.js +13 -13
- package/test/tools/offset.test.js +170 -3
- package/test/tools/pasteclip.test.js +5 -5
- package/test/tools/rotate.test.js +32 -2
- package/test/tools/scale.test.js +5 -5
- package/test/tools/trim.test.js +526 -0
- package/test/tools/zoom.test.js +7 -7
- package/core/lib/selectionWindow.js +0 -83
|
@@ -8,6 +8,7 @@ import { Text } from '../../core/entities/text.js';
|
|
|
8
8
|
import { Polyline } from '../../core/entities/polyline.js';
|
|
9
9
|
import { Hatch } from '../../core/entities/hatch.js';
|
|
10
10
|
import { Rectangle } from '../../core/entities/rectangle.js';
|
|
11
|
+
import { ArcAlignedText } from '../../core/entities/arctext.js';
|
|
11
12
|
import { Copy } from '../../core/tools/copy.js';
|
|
12
13
|
import { Move } from '../../core/tools/move.js';
|
|
13
14
|
import { Rotate } from '../../core/tools/rotate.js';
|
|
@@ -15,9 +16,14 @@ import { Erase } from '../../core/tools/erase.js';
|
|
|
15
16
|
import { MatchProp } from '../../core/tools/matchProp.js';
|
|
16
17
|
import { Trim } from '../../core/tools/trim.js';
|
|
17
18
|
import { Extend } from '../../core/tools/extend.js';
|
|
19
|
+
import { Mirror } from '../../core/tools/mirror.js';
|
|
20
|
+
import { Scale } from '../../core/tools/scale.js';
|
|
21
|
+
import { Offset } from '../../core/tools/offset.js';
|
|
22
|
+
import { Fillet } from '../../core/tools/fillet.js';
|
|
23
|
+
import { Chamfer } from '../../core/tools/chamfer.js';
|
|
18
24
|
import { SingleSelection } from '../../core/lib/selectionManager.js';
|
|
19
25
|
|
|
20
|
-
import { readFileSync } from 'fs';
|
|
26
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
21
27
|
import { fileURLToPath } from 'url';
|
|
22
28
|
import { dirname, join } from 'path';
|
|
23
29
|
|
|
@@ -26,6 +32,16 @@ const __dirname = dirname(__filename);
|
|
|
26
32
|
|
|
27
33
|
let core;
|
|
28
34
|
|
|
35
|
+
// Width/height of each grid cell in DXF units.
|
|
36
|
+
// Entities for each command live within (col*CELL, row*CELL)→((col+1)*CELL,(row+1)*CELL).
|
|
37
|
+
//
|
|
38
|
+
// Grid layout (6 columns × 4 rows):
|
|
39
|
+
// Row 0: Line | Circle | Arc | Polyline | Rectangle | Text
|
|
40
|
+
// Row 1: Hatch | ArcText | Copy | Move | Mirror | Scale
|
|
41
|
+
// Row 2: Rotate | Erase | Offset | Trim | Extend | Fillet
|
|
42
|
+
// Row 3: Chamfer | MatchProp
|
|
43
|
+
const CELL = 200;
|
|
44
|
+
|
|
29
45
|
/**
|
|
30
46
|
* Create a mock for inputManager.requestInput that feeds
|
|
31
47
|
* sequential inputs and rejects when exhausted (simulating Escape)
|
|
@@ -43,7 +59,8 @@ function mockRequestInput(inputs) {
|
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
/**
|
|
46
|
-
* Execute an entity command with mocked user inputs
|
|
62
|
+
* Execute an entity creation command with mocked user inputs.
|
|
63
|
+
* Does NOT set activeCommand so actionCommand() routes through addItem().
|
|
47
64
|
* @param {Object} command - the command instance to execute
|
|
48
65
|
* @param {Array} inputs - array of input values to feed sequentially
|
|
49
66
|
*/
|
|
@@ -55,7 +72,8 @@ async function executeWithInputs(command, inputs) {
|
|
|
55
72
|
}
|
|
56
73
|
|
|
57
74
|
/**
|
|
58
|
-
* Execute a tool command with mocked user inputs and pre-populated selection
|
|
75
|
+
* Execute a tool command with mocked user inputs and pre-populated selection.
|
|
76
|
+
* Sets activeCommand so actionCommand() calls tool.action().
|
|
59
77
|
* @param {Object} tool - the tool instance to execute
|
|
60
78
|
* @param {Array} selectionIndices - entity indices to pre-select
|
|
61
79
|
* @param {Array} inputs - array of input values to feed sequentially
|
|
@@ -78,172 +96,257 @@ async function executeToolWithInputs(tool, selectionIndices, inputs) {
|
|
|
78
96
|
beforeAll(async () => {
|
|
79
97
|
core = new Core();
|
|
80
98
|
|
|
81
|
-
//
|
|
99
|
+
// =====================================================================
|
|
100
|
+
// Phase 1: Showcase entities (idx 0–7)
|
|
101
|
+
// =====================================================================
|
|
82
102
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
new Point(
|
|
87
|
-
new Point(100, 100),
|
|
88
|
-
new Point(200, 0),
|
|
103
|
+
// col 0, row 0 — Line → idx 0
|
|
104
|
+
await executeWithInputs(new Line(), [
|
|
105
|
+
new Point(10, 50),
|
|
106
|
+
new Point(90, 50),
|
|
89
107
|
]);
|
|
90
|
-
// idx 0: Line (0,0)→(100,100)
|
|
91
|
-
// idx 1: Line (100,100)→(200,0)
|
|
92
108
|
|
|
93
|
-
// Circle
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
50,
|
|
109
|
+
// col 1, row 0 — Circle, center (250,50) r=30 → idx 1
|
|
110
|
+
await executeWithInputs(new Circle(), [
|
|
111
|
+
new Point(250, 50),
|
|
112
|
+
30,
|
|
98
113
|
]);
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
new Point(530, 500),
|
|
106
|
-
90,
|
|
114
|
+
|
|
115
|
+
// col 2, row 0 — Arc, center (450,50), start (480,50), 270° CCW → idx 2
|
|
116
|
+
await executeWithInputs(new Arc(), [
|
|
117
|
+
new Point(450, 50),
|
|
118
|
+
new Point(480, 50),
|
|
119
|
+
270,
|
|
107
120
|
]);
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
new Point(
|
|
114
|
-
new Point(50, 200),
|
|
115
|
-
new Point(50, 250),
|
|
116
|
-
new Point(0, 250),
|
|
121
|
+
|
|
122
|
+
// col 3, row 0 — Polyline, open V-shape → idx 3
|
|
123
|
+
await executeWithInputs(new Polyline(), [
|
|
124
|
+
new Point(610, 80),
|
|
125
|
+
new Point(650, 20),
|
|
126
|
+
new Point(690, 80),
|
|
117
127
|
]);
|
|
118
|
-
// idx 4: Polyline
|
|
119
128
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
new Point(
|
|
124
|
-
|
|
129
|
+
// col 4, row 0 — Rectangle (stored as closed Polyline) → idx 4
|
|
130
|
+
await executeWithInputs(new Rectangle(), [
|
|
131
|
+
new Point(820, 20),
|
|
132
|
+
new Point(880, 80),
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
// col 5, row 0 — Text → idx 5
|
|
136
|
+
await executeWithInputs(new Text(), [
|
|
137
|
+
new Point(1010, 50),
|
|
138
|
+
10,
|
|
125
139
|
0,
|
|
126
|
-
'
|
|
140
|
+
'Text',
|
|
127
141
|
]);
|
|
128
|
-
// idx 5: Text
|
|
129
142
|
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
new Point(
|
|
134
|
-
new Point(700, 100),
|
|
143
|
+
// col 0, row 1 — Rectangle boundary for Hatch, in its own cell → idx 6
|
|
144
|
+
await executeWithInputs(new Rectangle(), [
|
|
145
|
+
new Point(20, 240),
|
|
146
|
+
new Point(180, 360),
|
|
135
147
|
]);
|
|
136
|
-
// idx 6: Polyline (rectangle)
|
|
137
148
|
|
|
138
|
-
// Hatch
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
// col 0, row 1 — Hatch using the rectangle boundary (idx 6) → idx 7
|
|
150
|
+
{
|
|
151
|
+
const hatchBoundary = DesignCore.Scene.entities.get(6);
|
|
152
|
+
const origSelectedItems = DesignCore.Scene.selectionManager.selectedItems;
|
|
153
|
+
DesignCore.Scene.selectionManager.selectedItems = [hatchBoundary];
|
|
154
|
+
await executeWithInputs(new Hatch(), ['accept']);
|
|
155
|
+
DesignCore.Scene.selectionManager.selectedItems = origSelectedItems;
|
|
156
|
+
}
|
|
142
157
|
|
|
143
|
-
|
|
144
|
-
await executeWithInputs(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// idx 7: Hatch
|
|
149
|
-
|
|
150
|
-
// Additional lines for trim and extend geometry
|
|
151
|
-
// Trim: horizontal boundary and vertical target that cross
|
|
152
|
-
const trimBoundary = new Line();
|
|
153
|
-
await executeWithInputs(trimBoundary, [
|
|
154
|
-
new Point(800, 50),
|
|
155
|
-
new Point(1000, 50),
|
|
158
|
+
// col 1, row 1 — Arc for ArcAlignedText, in its own cell → idx 8
|
|
159
|
+
await executeWithInputs(new Arc(), [
|
|
160
|
+
new Point(300, 300),
|
|
161
|
+
new Point(350, 300),
|
|
162
|
+
270,
|
|
156
163
|
]);
|
|
157
|
-
// idx 8: Line (800,50)→(1000,50) - trim boundary
|
|
158
164
|
|
|
159
|
-
|
|
160
|
-
await executeWithInputs(
|
|
161
|
-
new Point(
|
|
162
|
-
|
|
165
|
+
// col 1, row 1 — ArcAlignedText on Arc (idx 8) → idx 9
|
|
166
|
+
await executeWithInputs(new ArcAlignedText(), [
|
|
167
|
+
new SingleSelection(8, new Point(350, 300)),
|
|
168
|
+
10, // height (asked because style is STANDARD)
|
|
169
|
+
'ArcText',
|
|
163
170
|
]);
|
|
164
|
-
// idx 9: Line (900,0)→(900,100) - trim target (intersects at 900,50)
|
|
165
171
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
new Point(1100, 0),
|
|
170
|
-
new Point(1100, 200),
|
|
171
|
-
]);
|
|
172
|
-
// idx 10: Line (1100,0)→(1100,200) - extend boundary
|
|
172
|
+
// =====================================================================
|
|
173
|
+
// Phase 2: Dedicated geometry for each tool cell (idx 10–25)
|
|
174
|
+
// =====================================================================
|
|
173
175
|
|
|
174
|
-
|
|
175
|
-
await executeWithInputs(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
]);
|
|
179
|
-
|
|
176
|
+
// col 2, row 1 — Copy source
|
|
177
|
+
await executeWithInputs(new Line(), [new Point(410, 230), new Point(490, 230)]); // idx 10
|
|
178
|
+
|
|
179
|
+
// col 3, row 1 — Move source
|
|
180
|
+
await executeWithInputs(new Line(), [new Point(610, 230), new Point(690, 230)]); // idx 11
|
|
181
|
+
|
|
182
|
+
// col 4, row 1 — Mirror source
|
|
183
|
+
await executeWithInputs(new Line(), [new Point(810, 230), new Point(890, 230)]); // idx 12
|
|
184
|
+
|
|
185
|
+
// col 5, row 1 — Scale source (circle)
|
|
186
|
+
await executeWithInputs(new Circle(), [new Point(1050, 230), 20]); // idx 13
|
|
187
|
+
|
|
188
|
+
// col 0, row 2 — Rotate source
|
|
189
|
+
await executeWithInputs(new Line(), [new Point(10, 450), new Point(90, 450)]); // idx 14
|
|
190
|
+
|
|
191
|
+
// col 1, row 2 — Erase target (will be removed)
|
|
192
|
+
await executeWithInputs(new Line(), [new Point(210, 450), new Point(290, 450)]); // idx 15
|
|
193
|
+
|
|
194
|
+
// col 2, row 2 — Offset source
|
|
195
|
+
await executeWithInputs(new Line(), [new Point(410, 450), new Point(490, 450)]); // idx 16
|
|
180
196
|
|
|
181
|
-
//
|
|
197
|
+
// col 3, row 2 — Trim: horizontal boundary + vertical target that crosses it
|
|
198
|
+
await executeWithInputs(new Line(), [new Point(620, 430), new Point(720, 430)]); // idx 17 (boundary)
|
|
199
|
+
await executeWithInputs(new Line(), [new Point(670, 400), new Point(670, 470)]); // idx 18 (target; intersection at 670,430)
|
|
200
|
+
|
|
201
|
+
// col 4, row 2 — Extend: vertical boundary + short horizontal target
|
|
202
|
+
await executeWithInputs(new Line(), [new Point(820, 410), new Point(820, 490)]); // idx 19 (boundary at x=820)
|
|
203
|
+
await executeWithInputs(new Line(), [new Point(840, 450), new Point(880, 450)]); // idx 20 (target; left end faces x=820)
|
|
204
|
+
|
|
205
|
+
// col 5, row 2 — Fillet: two Lines sharing a right-angle corner at (1060,440)
|
|
206
|
+
await executeWithInputs(new Line(), [new Point(1010, 440), new Point(1060, 440)]); // idx 21 (horizontal arm)
|
|
207
|
+
await executeWithInputs(new Line(), [new Point(1060, 440), new Point(1060, 490)]); // idx 22 (vertical arm)
|
|
208
|
+
|
|
209
|
+
// col 0, row 3 — Chamfer: two Lines sharing a right-angle corner at (60,620)
|
|
210
|
+
await executeWithInputs(new Line(), [new Point(10, 620), new Point(60, 620)]); // idx 23 (horizontal arm)
|
|
211
|
+
await executeWithInputs(new Line(), [new Point(60, 620), new Point(60, 670)]); // idx 24 (vertical arm)
|
|
212
|
+
|
|
213
|
+
// col 1, row 3 — MatchProp source on a dedicated layer
|
|
182
214
|
DesignCore.LayerManager.addItem({ name: 'TOOL_LAYER' });
|
|
183
215
|
DesignCore.LayerManager.setCstyle('TOOL_LAYER');
|
|
216
|
+
await executeWithInputs(new Line(), [new Point(210, 650), new Point(290, 650)]); // idx 25
|
|
217
|
+
DesignCore.LayerManager.setCstyle('0');
|
|
184
218
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
]);
|
|
190
|
-
// idx 12: Line on TOOL_LAYER
|
|
219
|
+
// =====================================================================
|
|
220
|
+
// Phase 3: Tool operations
|
|
221
|
+
// Index-shifting removals (Trim, Erase) happen last.
|
|
222
|
+
// =====================================================================
|
|
191
223
|
|
|
192
|
-
|
|
224
|
+
// Copy: copy idx 10 downward → adds idx 26
|
|
225
|
+
await executeToolWithInputs(new Copy(), [10], [
|
|
226
|
+
new Point(410, 230), // base point
|
|
227
|
+
new Point(410, 310), // destination (delta 0,+80)
|
|
228
|
+
]);
|
|
193
229
|
|
|
194
|
-
//
|
|
195
|
-
|
|
230
|
+
// Move: shift idx 11 downward (UpdateState, no new entity)
|
|
231
|
+
await executeToolWithInputs(new Move(), [11], [
|
|
232
|
+
new Point(610, 230), // base point
|
|
233
|
+
new Point(610, 310), // destination
|
|
234
|
+
]);
|
|
196
235
|
|
|
197
|
-
//
|
|
198
|
-
await executeToolWithInputs(new
|
|
199
|
-
new Point(
|
|
200
|
-
new Point(
|
|
236
|
+
// Mirror: mirror idx 12 about a horizontal axis at y=250; keep source → adds idx 27
|
|
237
|
+
await executeToolWithInputs(new Mirror(), [12], [
|
|
238
|
+
new Point(800, 250), // first mirror-axis point
|
|
239
|
+
new Point(900, 250), // second mirror-axis point
|
|
240
|
+
'No', // do not erase source
|
|
201
241
|
]);
|
|
202
|
-
// idx 13: copy of line[0]
|
|
203
242
|
|
|
204
|
-
//
|
|
205
|
-
await executeToolWithInputs(new
|
|
206
|
-
new Point(
|
|
207
|
-
|
|
243
|
+
// Scale: scale idx 13 (circle) by ×2 from its centre (UpdateState)
|
|
244
|
+
await executeToolWithInputs(new Scale(), [13], [
|
|
245
|
+
new Point(1050, 230), // base point (= circle centre)
|
|
246
|
+
2, // scale factor
|
|
208
247
|
]);
|
|
209
248
|
|
|
210
|
-
// Rotate: rotate
|
|
211
|
-
await executeToolWithInputs(new Rotate(), [
|
|
212
|
-
new Point(
|
|
213
|
-
|
|
249
|
+
// Rotate: rotate idx 14 by 90° CCW about its left endpoint (UpdateState)
|
|
250
|
+
await executeToolWithInputs(new Rotate(), [14], [
|
|
251
|
+
new Point(10, 450), // base point
|
|
252
|
+
90, // angle in degrees
|
|
214
253
|
]);
|
|
215
254
|
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
255
|
+
// Offset: offset idx 16 upward by 20 units; pre-selecting skips SINGLESELECTION prompt → adds idx 28
|
|
256
|
+
await executeToolWithInputs(new Offset(), [16], [
|
|
257
|
+
20, // offset distance
|
|
258
|
+
new Point(450, 470), // side point (above the line at y=450)
|
|
220
259
|
]);
|
|
221
|
-
// idx 0: layer changed to TOOL_LAYER
|
|
222
260
|
|
|
223
|
-
// Extend: extend
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
261
|
+
// Extend: extend the left end of idx 20 to boundary idx 19 (at x=820)
|
|
262
|
+
{
|
|
263
|
+
const origPointOnScene = DesignCore.Mouse.pointOnScene.bind(DesignCore.Mouse);
|
|
264
|
+
DesignCore.Mouse.pointOnScene = () => new Point(840, 450);
|
|
265
|
+
await executeToolWithInputs(new Extend(), [19], [
|
|
266
|
+
new SingleSelection(20, new Point(840, 450)),
|
|
267
|
+
]);
|
|
268
|
+
DesignCore.Mouse.pointOnScene = origPointOnScene;
|
|
269
|
+
}
|
|
227
270
|
|
|
228
|
-
|
|
229
|
-
|
|
271
|
+
// MatchProp: copy properties from idx 25 (TOOL_LAYER) onto idx 0 (UpdateState)
|
|
272
|
+
await executeToolWithInputs(new MatchProp(), [25], [
|
|
273
|
+
{ selectionSet: [0] },
|
|
230
274
|
]);
|
|
231
275
|
|
|
232
|
-
//
|
|
233
|
-
|
|
234
|
-
DesignCore.
|
|
276
|
+
// Fillet: radius=10, trimMode=true; fillet the corner of idx 21 & idx 22 → adds Arc (idx 29)
|
|
277
|
+
DesignCore.Scene.headers.filletRadius = 10;
|
|
278
|
+
DesignCore.Scene.headers.trimMode = true;
|
|
279
|
+
await executeToolWithInputs(new Fillet(), [], [
|
|
280
|
+
new SingleSelection(21, new Point(1035, 440)), // horizontal arm, away from corner
|
|
281
|
+
new SingleSelection(22, new Point(1060, 465)), // vertical arm, away from corner
|
|
282
|
+
]);
|
|
235
283
|
|
|
236
|
-
|
|
237
|
-
|
|
284
|
+
// Chamfer: distA=distB=10, distance mode, trimMode=true → adds Line (idx 30)
|
|
285
|
+
DesignCore.Scene.headers.chamferDistanceA = 10;
|
|
286
|
+
DesignCore.Scene.headers.chamferDistanceB = 10;
|
|
287
|
+
DesignCore.Scene.headers.chamferMode = false;
|
|
288
|
+
DesignCore.Scene.headers.trimMode = true;
|
|
289
|
+
await executeToolWithInputs(new Chamfer(), [], [
|
|
290
|
+
new SingleSelection(23, new Point(35, 620)), // horizontal arm, away from corner
|
|
291
|
+
new SingleSelection(24, new Point(60, 645)), // vertical arm, away from corner
|
|
238
292
|
]);
|
|
239
|
-
// idx 9 removed, trimmed line added at end
|
|
240
|
-
// indices 10+ shift down by 1: matchprop source (was 12) → now 11
|
|
241
293
|
|
|
242
|
-
|
|
294
|
+
// Trim: remove the lower segment of idx 18 using boundary idx 17.
|
|
295
|
+
// Mouse at (670,450) — between the intersection (670,430) and the bottom end (670,470).
|
|
296
|
+
{
|
|
297
|
+
const origPointOnScene = DesignCore.Mouse.pointOnScene.bind(DesignCore.Mouse);
|
|
298
|
+
DesignCore.Mouse.pointOnScene = () => new Point(670, 450);
|
|
299
|
+
await executeToolWithInputs(new Trim(), [17], [
|
|
300
|
+
new SingleSelection(18, new Point(670, 450)),
|
|
301
|
+
]);
|
|
302
|
+
DesignCore.Mouse.pointOnScene = origPointOnScene;
|
|
303
|
+
}
|
|
304
|
+
// → idx 18 removed; trimmed-remainder line added at end of entity list
|
|
305
|
+
|
|
306
|
+
// Erase: remove the dedicated erase-target (idx 15).
|
|
307
|
+
// idx 15 < 18, so the Trim removal above did not affect its index.
|
|
308
|
+
await executeToolWithInputs(new Erase(), [15], []);
|
|
309
|
+
|
|
310
|
+
// =====================================================================
|
|
311
|
+
// Phase 4: Text labels — placed at the top of each grid cell
|
|
312
|
+
// =====================================================================
|
|
313
|
+
|
|
314
|
+
const addLabel = async (col, row, name) => {
|
|
315
|
+
await executeWithInputs(new Text(), [
|
|
316
|
+
new Point(col * CELL + 10, row * CELL + 15),
|
|
317
|
+
8, // height
|
|
318
|
+
0, // rotation
|
|
319
|
+
name,
|
|
320
|
+
]);
|
|
321
|
+
};
|
|
243
322
|
|
|
244
|
-
//
|
|
245
|
-
await
|
|
246
|
-
|
|
323
|
+
// Row 0
|
|
324
|
+
await addLabel(0, 0, 'Line');
|
|
325
|
+
await addLabel(1, 0, 'Circle');
|
|
326
|
+
await addLabel(2, 0, 'Arc');
|
|
327
|
+
await addLabel(3, 0, 'Polyline');
|
|
328
|
+
await addLabel(4, 0, 'Rectangle');
|
|
329
|
+
await addLabel(5, 0, 'Text');
|
|
330
|
+
|
|
331
|
+
// Row 1
|
|
332
|
+
await addLabel(0, 1, 'Hatch');
|
|
333
|
+
await addLabel(1, 1, 'ArcText');
|
|
334
|
+
await addLabel(2, 1, 'Copy');
|
|
335
|
+
await addLabel(3, 1, 'Move');
|
|
336
|
+
await addLabel(4, 1, 'Mirror');
|
|
337
|
+
await addLabel(5, 1, 'Scale');
|
|
338
|
+
|
|
339
|
+
// Row 2
|
|
340
|
+
await addLabel(0, 2, 'Rotate');
|
|
341
|
+
await addLabel(1, 2, 'Erase');
|
|
342
|
+
await addLabel(2, 2, 'Offset');
|
|
343
|
+
await addLabel(3, 2, 'Trim');
|
|
344
|
+
await addLabel(4, 2, 'Extend');
|
|
345
|
+
await addLabel(5, 2, 'Fillet');
|
|
346
|
+
|
|
347
|
+
// Row 3
|
|
348
|
+
await addLabel(0, 3, 'Chamfer');
|
|
349
|
+
await addLabel(1, 3, 'MatchProp');
|
|
247
350
|
});
|
|
248
351
|
|
|
249
352
|
let dxfOutput;
|
|
@@ -252,17 +355,21 @@ beforeAll(() => {
|
|
|
252
355
|
dxfOutput = core.saveFile('R2018');
|
|
253
356
|
});
|
|
254
357
|
|
|
255
|
-
test('
|
|
358
|
+
test('DXF output contains all entity types', () => {
|
|
256
359
|
expect(dxfOutput).toContain('LINE');
|
|
257
360
|
expect(dxfOutput).toContain('CIRCLE');
|
|
258
361
|
expect(dxfOutput).toContain('ARC');
|
|
259
|
-
expect(dxfOutput).toContain('
|
|
362
|
+
expect(dxfOutput).toContain('LWPOLYLINE');
|
|
260
363
|
expect(dxfOutput).toContain('TEXT');
|
|
261
364
|
expect(dxfOutput).toContain('HATCH');
|
|
365
|
+
expect(dxfOutput).toContain('ARCALIGNEDTEXT');
|
|
262
366
|
});
|
|
263
367
|
|
|
264
|
-
test('
|
|
368
|
+
test('DXF output matches reference file', () => {
|
|
265
369
|
const referencePath = join(__dirname, 'userInputIntegration.reference.dxf');
|
|
370
|
+
if (!existsSync(referencePath)) {
|
|
371
|
+
writeFileSync(referencePath, dxfOutput, 'utf8');
|
|
372
|
+
}
|
|
266
373
|
const reference = readFileSync(referencePath, 'utf8');
|
|
267
374
|
|
|
268
375
|
expect(dxfOutput).toBe(reference);
|