@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
package/core/entities/insert.js
CHANGED
|
@@ -5,11 +5,11 @@ import { Block } from '../tables/block.js';
|
|
|
5
5
|
import { BoundingBox } from '../lib/boundingBox.js';
|
|
6
6
|
import { Point } from './point.js';
|
|
7
7
|
import { Utils } from '../lib/utils.js';
|
|
8
|
-
import { Property } from '../properties/property.js';
|
|
9
8
|
import { Logging } from '../lib/logging.js';
|
|
9
|
+
import { Property } from '../properties/property.js';
|
|
10
10
|
|
|
11
11
|
import { DesignCore } from '../designCore.js';
|
|
12
|
-
import { SnapPoint } from '../lib/
|
|
12
|
+
import { SnapPoint } from '../lib/auxiliary/snapPoint.js';
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -34,59 +34,29 @@ export class Insert extends Entity {
|
|
|
34
34
|
writable: true,
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
get:
|
|
41
|
-
set:
|
|
42
|
-
enumerable: true,
|
|
37
|
+
this.properties.add(Property.Names.ROTATION, {
|
|
38
|
+
type: Property.Type.NUMBER,
|
|
39
|
+
dxfCode: 50,
|
|
40
|
+
get: (entity) => entity.getRotation(),
|
|
41
|
+
set: (entity, value) => entity.setRotation(value),
|
|
43
42
|
});
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
this.properties.add(Property.Names.BLOCKNAME, {
|
|
45
|
+
type: Property.Type.LABEL,
|
|
46
|
+
readOnly: true,
|
|
47
|
+
get: (entity) => entity.block?.name ?? '',
|
|
48
|
+
});
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
if (data) {
|
|
51
|
+
// DXF Groupcode 2 - Block name
|
|
52
|
+
if (data.blockName !== undefined) {
|
|
53
|
+
const block = DesignCore.Scene.blockManager.getItemByName(data.blockName);
|
|
51
54
|
this.block = block;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Logging.instance.warn(`${this.type} - ${err}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (data.hasOwnProperty('42')) {
|
|
61
|
-
// DXF Groupcode 42 - Y Scale Factor (optional, default = 1)
|
|
62
|
-
const err = 'Groupcode 42 not implemented';
|
|
63
|
-
Logging.instance.warn(`${this.type} - ${err}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (data.hasOwnProperty('43')) {
|
|
67
|
-
// DXF Groupcode 43 - Z Scale Factor (optional, default = 1)
|
|
68
|
-
const err = 'Groupcode 43 not implemented';
|
|
69
|
-
Logging.instance.warn(`${this.type} - ${err}`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (data.hasOwnProperty('44')) {
|
|
73
|
-
// DXF Groupcode 44 - Column Spacing (optional, default = 0)
|
|
74
|
-
const err = 'Groupcode 44 not implemented';
|
|
75
|
-
Logging.instance.warn(`${this.type} - ${err}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (data.hasOwnProperty('45')) {
|
|
79
|
-
// DXF Groupcode 45 - Row Spacing (optional, default = 0)
|
|
80
|
-
const err = 'Groupcode 45 not implemented';
|
|
81
|
-
Logging.instance.warn(`${this.type} - ${err}`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (data.hasOwnProperty('rotation') || data.hasOwnProperty('50')) {
|
|
85
|
-
// DXF Groupcode 50 - Text Rotation, angle in degrees
|
|
86
|
-
// if we get rotation data store this as a point[1] at an angle from point[0]
|
|
87
|
-
// this allows all the entities to be rotated by rotating the points i.e. not all entities have a rotation property
|
|
88
|
-
|
|
89
|
-
this.setRotation(Property.loadValue([data.rotation, data[50]], 0));
|
|
57
|
+
// Named scalar rotation (internal API); DXF code 50 is handled by fromDxf
|
|
58
|
+
if (data.rotation !== undefined) {
|
|
59
|
+
this.setRotation(data.rotation);
|
|
90
60
|
} else {
|
|
91
61
|
// create points[1] used to determine the rotation
|
|
92
62
|
if (this.points[0] === undefined) {
|
|
@@ -95,18 +65,6 @@ export class Insert extends Entity {
|
|
|
95
65
|
|
|
96
66
|
this.points[1] = this.points[0].add(new Point(10, 0));
|
|
97
67
|
}
|
|
98
|
-
|
|
99
|
-
if (data.hasOwnProperty('70')) {
|
|
100
|
-
// DXF Groupcode 70 - Column Count (optional, default = 1)
|
|
101
|
-
const err = 'Groupcode 70 not implemented';
|
|
102
|
-
Logging.instance.warn(`${this.type} - ${err}`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (data.hasOwnProperty('71')) {
|
|
106
|
-
// DXF Groupcode 71 - Row Count(optional, default = 1)
|
|
107
|
-
const err = 'Groupcode 71 not implemented';
|
|
108
|
-
Logging.instance.warn(`${this.type} - ${err}`);
|
|
109
|
-
}
|
|
110
68
|
}
|
|
111
69
|
}
|
|
112
70
|
|
|
@@ -122,6 +80,40 @@ export class Insert extends Entity {
|
|
|
122
80
|
return command;
|
|
123
81
|
}
|
|
124
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Normalise raw DXF data before construction
|
|
85
|
+
* @param {Object} data - raw DXF group code object
|
|
86
|
+
* @return {Object} normalised data with blockName and rotation encoded as points[1]
|
|
87
|
+
*
|
|
88
|
+
* Handles:
|
|
89
|
+
* - Group code 2: block name
|
|
90
|
+
* - Group code 50: rotation angle (degrees) encoded into points[1]
|
|
91
|
+
* - Group codes 41–45, 70, 71: unimplemented — logged as warnings
|
|
92
|
+
*/
|
|
93
|
+
static fromDxf(data) {
|
|
94
|
+
const normalised = { ...data };
|
|
95
|
+
|
|
96
|
+
// DXF group code 2 - Block name
|
|
97
|
+
if (data[2] !== undefined) {
|
|
98
|
+
normalised.blockName = data[2];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// DXF group code 50 - rotation angle in degrees; encode into points
|
|
102
|
+
if (data[50] !== undefined) {
|
|
103
|
+
normalised.rotation = data[50];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Unimplemented group codes
|
|
107
|
+
const unimplemented = { 41: 'X Scale Factor', 42: 'Y Scale Factor', 43: 'Z Scale Factor', 44: 'Column Spacing', 45: 'Row Spacing', 70: 'Column Count', 71: 'Row Count' };
|
|
108
|
+
for (const [code, label] of Object.entries(unimplemented)) {
|
|
109
|
+
if (data[code] !== undefined) {
|
|
110
|
+
Logging.instance.warn(`Insert - Groupcode ${code} (${label}) not implemented`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return normalised;
|
|
115
|
+
}
|
|
116
|
+
|
|
125
117
|
/**
|
|
126
118
|
* Execute method
|
|
127
119
|
* executes the workflow, requesting input required to create an entity
|
|
@@ -144,37 +136,30 @@ export class Insert extends Entity {
|
|
|
144
136
|
*/
|
|
145
137
|
dxf(file) {
|
|
146
138
|
file.writeGroupCode('0', 'INSERT');
|
|
147
|
-
file.writeGroupCode('5', this.
|
|
139
|
+
file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
|
|
148
140
|
file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
|
|
149
|
-
file.writeGroupCode('8', this.
|
|
141
|
+
file.writeGroupCode('8', this.getProperty(Property.Names.LAYER));
|
|
150
142
|
this.writeDxfColour(file);
|
|
151
143
|
file.writeGroupCode('100', 'AcDbBlockReference', DXFFile.Version.R2000);
|
|
152
144
|
file.writeGroupCode('2', this.block.name);
|
|
153
145
|
file.writeGroupCode('10', this.points[0].x);
|
|
154
146
|
file.writeGroupCode('20', this.points[0].y);
|
|
155
147
|
file.writeGroupCode('30', '0.0');
|
|
156
|
-
if (this.
|
|
157
|
-
file.writeGroupCode('50', this.
|
|
148
|
+
if (this.getProperty(Property.Names.ROTATION)) {
|
|
149
|
+
file.writeGroupCode('50', this.getProperty(Property.Names.ROTATION));
|
|
158
150
|
}
|
|
159
151
|
}
|
|
160
152
|
|
|
161
153
|
/**
|
|
162
154
|
* Draw the entity
|
|
163
|
-
* @param {Object}
|
|
164
|
-
* @
|
|
155
|
+
* @param {Object} renderer
|
|
156
|
+
* @return {Array} block entities for the canvas to render recursively
|
|
165
157
|
*/
|
|
166
|
-
draw(
|
|
158
|
+
draw(renderer) {
|
|
167
159
|
// blocks are associated with an insert point.
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
ctx.save();
|
|
172
|
-
ctx.translate(this.points[0].x, this.points[0].y);
|
|
173
|
-
const rotation = Utils.degrees2radians(this.rotation);
|
|
174
|
-
ctx.rotate(rotation);
|
|
175
|
-
// pass *this* to the block to allow colour ByBlock
|
|
176
|
-
this.block.draw(ctx, scale, this);
|
|
177
|
-
ctx.restore();
|
|
160
|
+
// Apply the insert location and rotation so block entities draw correctly.
|
|
161
|
+
renderer.applyTransform({ x: this.points[0].x, y: this.points[0].y, rotation: Utils.degrees2radians(this.getRotation()) });
|
|
162
|
+
return this.block.entities;
|
|
178
163
|
}
|
|
179
164
|
|
|
180
165
|
/**
|
|
@@ -219,10 +204,10 @@ export class Insert extends Entity {
|
|
|
219
204
|
|
|
220
205
|
for (let snap = 0; snap < blockSnaps.length; snap++) {
|
|
221
206
|
const sp = blockSnaps[snap];
|
|
222
|
-
// offset the
|
|
207
|
+
// offset the entity snap point by the block insert location
|
|
223
208
|
const rotatedPoint = sp.snapPoint.add(this.points[0]);
|
|
224
|
-
// rotate the snap point to match the
|
|
225
|
-
const adjustedPoint = rotatedPoint.rotate(this.points[0], Utils.degrees2radians(this.
|
|
209
|
+
// rotate the snap point to match the entity positions
|
|
210
|
+
const adjustedPoint = rotatedPoint.rotate(this.points[0], Utils.degrees2radians(this.getRotation()));
|
|
226
211
|
snaps.push(new SnapPoint(adjustedPoint, sp.type));
|
|
227
212
|
}
|
|
228
213
|
|
|
@@ -250,7 +235,7 @@ export class Insert extends Entity {
|
|
|
250
235
|
closestPoint(P) {
|
|
251
236
|
// get the closest point from the blocks entities
|
|
252
237
|
// rotate P to match the block rotation
|
|
253
|
-
const rotatedPoint = P.rotate(this.points[0], Utils.degrees2radians(-this.
|
|
238
|
+
const rotatedPoint = P.rotate(this.points[0], Utils.degrees2radians(-this.getRotation()));
|
|
254
239
|
// adjust P by the insert position
|
|
255
240
|
const adjustedPoint = rotatedPoint.subtract(this.points[0]);
|
|
256
241
|
return this.block.closestPoint(adjustedPoint);
|
|
@@ -273,7 +258,7 @@ export class Insert extends Entity {
|
|
|
273
258
|
* @return {boolean} true if touched
|
|
274
259
|
*/
|
|
275
260
|
touched(selection) {
|
|
276
|
-
const layer = DesignCore.LayerManager.getItemByName(this.
|
|
261
|
+
const layer = DesignCore.LayerManager.getItemByName(this.getProperty(Property.Names.LAYER));
|
|
277
262
|
|
|
278
263
|
if (!layer?.isSelectable) {
|
|
279
264
|
return;
|
package/core/entities/line.js
CHANGED
|
@@ -5,11 +5,11 @@ import { Input, PromptOptions } from '../lib/inputManager.js';
|
|
|
5
5
|
import { Logging } from '../lib/logging.js';
|
|
6
6
|
import { DXFFile } from '../lib/dxf/dxfFile.js';
|
|
7
7
|
import { BoundingBox } from '../lib/boundingBox.js';
|
|
8
|
-
import {
|
|
8
|
+
import { Property } from '../properties/property.js';
|
|
9
9
|
|
|
10
10
|
import { DesignCore } from '../designCore.js';
|
|
11
|
-
|
|
12
|
-
import { SnapPoint } from '../lib/
|
|
11
|
+
|
|
12
|
+
import { SnapPoint } from '../lib/auxiliary/snapPoint.js';
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -70,13 +70,18 @@ export class Line extends Entity {
|
|
|
70
70
|
if (pt2 === undefined) break;
|
|
71
71
|
|
|
72
72
|
if (Input.getType(pt2) === Input.Type.STRING && pt2 === 'Close') {
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
// Commit a 2-point segment from the last accumulated point back to pt1
|
|
74
|
+
const segment = new Line({ points: [this.points.at(-1), pt1] });
|
|
75
|
+
DesignCore.Scene.inputManager.executeCommand(segment);
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
// Commit a fresh 2-point segment; keep this.points as the accumulator
|
|
80
|
+
// so preview() always draws from the last confirmed point to the mouse.
|
|
81
|
+
const segStart = this.points.at(-1);
|
|
78
82
|
this.points.push(pt2);
|
|
79
|
-
|
|
83
|
+
const segment = new Line({ points: [segStart, pt2] });
|
|
84
|
+
DesignCore.Scene.inputManager.actionCommand(segment);
|
|
80
85
|
}
|
|
81
86
|
} catch (err) {
|
|
82
87
|
Logging.instance.error(`${this.type} - ${err}`);
|
|
@@ -90,19 +95,16 @@ export class Line extends Entity {
|
|
|
90
95
|
if (this.points.length >= 1) {
|
|
91
96
|
const mousePoint = DesignCore.Mouse.pointOnScene();
|
|
92
97
|
const points = [this.points.at(-1), mousePoint];
|
|
93
|
-
DesignCore.Scene.
|
|
98
|
+
DesignCore.Scene.previewEntities.create(this.type, { points: points });
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
/**
|
|
98
103
|
* Draw the entity
|
|
99
|
-
* @param {Object}
|
|
100
|
-
* @param {number} scale
|
|
104
|
+
* @param {Object} renderer
|
|
101
105
|
*/
|
|
102
|
-
draw(
|
|
103
|
-
|
|
104
|
-
ctx.lineTo(this.points[1].x, this.points[1].y);
|
|
105
|
-
ctx.stroke();
|
|
106
|
+
draw(renderer) {
|
|
107
|
+
renderer.drawShape(this.toPolylinePoints());
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
/**
|
|
@@ -111,18 +113,18 @@ export class Line extends Entity {
|
|
|
111
113
|
*/
|
|
112
114
|
dxf(file) {
|
|
113
115
|
file.writeGroupCode('0', 'LINE');
|
|
114
|
-
file.writeGroupCode('5', this.
|
|
116
|
+
file.writeGroupCode('5', this.getProperty(Property.Names.HANDLE), DXFFile.Version.R2000); // Handle
|
|
115
117
|
file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
|
|
116
118
|
file.writeGroupCode('100', 'AcDbLine', DXFFile.Version.R2000);
|
|
117
|
-
file.writeGroupCode('8', this.
|
|
118
|
-
file.writeGroupCode('6', this.
|
|
119
|
+
file.writeGroupCode('8', this.getProperty(Property.Names.LAYER));
|
|
120
|
+
file.writeGroupCode('6', this.getProperty(Property.Names.LINETYPE));
|
|
119
121
|
file.writeGroupCode('10', this.points[0].x);
|
|
120
122
|
file.writeGroupCode('20', this.points[0].y);
|
|
121
123
|
file.writeGroupCode('30', '0.0');
|
|
122
124
|
file.writeGroupCode('11', this.points[1].x);
|
|
123
125
|
file.writeGroupCode('21', this.points[1].y);
|
|
124
126
|
file.writeGroupCode('31', '0.0');
|
|
125
|
-
file.writeGroupCode('39', this.
|
|
127
|
+
file.writeGroupCode('39', this.getProperty(Property.Names.LINEWIDTH));
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
/**
|
|
@@ -136,134 +138,11 @@ export class Line extends Entity {
|
|
|
136
138
|
/**
|
|
137
139
|
* Set entity points from a polyline point representation
|
|
138
140
|
* @param {Array} points
|
|
141
|
+
* @return {Line}
|
|
139
142
|
*/
|
|
140
143
|
fromPolylinePoints(points) {
|
|
141
144
|
this.points = [new Point(points[0].x, points[0].y), new Point(points[1].x, points[1].y)];
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Trim the entity
|
|
146
|
-
* @param {Array} intersections
|
|
147
|
-
* @return {Array} - array of state changes
|
|
148
|
-
*/
|
|
149
|
-
trim(intersections) {
|
|
150
|
-
// array to hold state changes
|
|
151
|
-
const stateChanges = [];
|
|
152
|
-
|
|
153
|
-
if (intersections?.length === 0 || !intersections) {
|
|
154
|
-
return stateChanges;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// get the mouse position
|
|
158
|
-
const mousePosition = DesignCore.Mouse.pointOnScene();
|
|
159
|
-
// get the point on the line closest to the mouse
|
|
160
|
-
const pointOnLine = mousePosition.closestPointOnLine(this.points[0], this.points[1]);
|
|
161
|
-
// remove any intersections that are at the end points of the line
|
|
162
|
-
intersections = intersections.filter((p) => !p.isSame(this.points[0]) && !p.isSame(this.points[1]));
|
|
163
|
-
|
|
164
|
-
Utils.sortPointsByDistance(intersections, this.points[0]);
|
|
165
|
-
|
|
166
|
-
// Add line points to the start and the end of the point array
|
|
167
|
-
const testPoints = [this.points[0], ...intersections, this.points[1]];
|
|
168
|
-
|
|
169
|
-
// Test if mouse position is between two intersection points
|
|
170
|
-
if (testPoints.length > 1) {
|
|
171
|
-
for (let i = 0; i < testPoints.length - 1; i++) {
|
|
172
|
-
const startPoint = testPoints[i];
|
|
173
|
-
const endPoint = testPoints[i + 1];
|
|
174
|
-
|
|
175
|
-
// check if the mouse is between startPoint and endPoint
|
|
176
|
-
if (pointOnLine.isOnLine(startPoint, endPoint)) {
|
|
177
|
-
const newPoints = [];
|
|
178
|
-
|
|
179
|
-
for (const p of testPoints) {
|
|
180
|
-
const inOriginalLine = this.points.indexOf(p) !== -1;
|
|
181
|
-
const inIntersections = intersections.indexOf(p) !== -1;
|
|
182
|
-
|
|
183
|
-
// Keep points which:
|
|
184
|
-
// - The mouse is between are intersections
|
|
185
|
-
// - The mouse is not between and in this line
|
|
186
|
-
const isBetween = p.isSame(startPoint) || p.isSame(endPoint);
|
|
187
|
-
if ((isBetween && inIntersections) || (!isBetween && inOriginalLine)) {
|
|
188
|
-
newPoints.push(p);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (newPoints.length % 2 === 0) {
|
|
193
|
-
// Add lines for each point pair
|
|
194
|
-
for (let j = 0; j < newPoints.length; j += 2) {
|
|
195
|
-
const line = Utils.cloneObject(this);
|
|
196
|
-
line.points = [newPoints[j], newPoints[j + 1]];
|
|
197
|
-
const addState = new AddState(line);
|
|
198
|
-
stateChanges.push(addState);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
// Remove the existing line
|
|
202
|
-
const removeState = new RemoveState(this);
|
|
203
|
-
stateChanges.push(removeState);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return stateChanges;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Extend the entity
|
|
213
|
-
* @param {Array} intersections
|
|
214
|
-
* @return {Array} - array of state changes
|
|
215
|
-
*/
|
|
216
|
-
extend(intersections) {
|
|
217
|
-
// array to hold state changes
|
|
218
|
-
const stateChanges = [];
|
|
219
|
-
|
|
220
|
-
if (intersections?.length === 0 || !intersections) {
|
|
221
|
-
return stateChanges;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
let originPoint;
|
|
225
|
-
// Find which end is closer to the mouse
|
|
226
|
-
if (this.points[0].distance(DesignCore.Mouse.pointOnScene()) < this.points[1].distance(DesignCore.Mouse.pointOnScene())) {
|
|
227
|
-
originPoint = 0;
|
|
228
|
-
} else {
|
|
229
|
-
originPoint = 1;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
Utils.sortPointsByDistance(intersections, this.points[originPoint]);
|
|
233
|
-
|
|
234
|
-
const endPoint = this.points[originPoint];
|
|
235
|
-
const adjacentPoint = this.points[1 - originPoint];
|
|
236
|
-
|
|
237
|
-
// Find the closest intersection that lies beyond the endpoint
|
|
238
|
-
const direction = endPoint.subtract(adjacentPoint);
|
|
239
|
-
const newEndPoint = intersections.find((p) => {
|
|
240
|
-
if (p.isSame(endPoint)) return false;
|
|
241
|
-
if (p.subtract(endPoint).dot(direction) <= 0) return false;
|
|
242
|
-
if (adjacentPoint.distance(p) <= adjacentPoint.distance(endPoint)) return false;
|
|
243
|
-
return true;
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
if (!newEndPoint) {
|
|
247
|
-
return stateChanges;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const newPoints = [];
|
|
251
|
-
if (originPoint === 0) {
|
|
252
|
-
newPoints.push(newEndPoint, this.points[1]);
|
|
253
|
-
} else {
|
|
254
|
-
newPoints.push(this.points[0], newEndPoint);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (newPoints[0].isSame(this.points[0]) && newPoints[1].isSame(this.points[1])) {
|
|
258
|
-
// No change
|
|
259
|
-
return stateChanges;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// update the line
|
|
263
|
-
const addState = new UpdateState(this, { points: newPoints });
|
|
264
|
-
stateChanges.push(addState);
|
|
265
|
-
|
|
266
|
-
return stateChanges;
|
|
145
|
+
return this;
|
|
267
146
|
}
|
|
268
147
|
|
|
269
148
|
/**
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Logging } from '../lib/logging.js';
|
|
2
|
-
import { BasePolyline } from './basePolyline.js';
|
|
3
2
|
import { Polyline } from './polyline.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* LWPolyline Entity Class
|
|
7
|
-
*
|
|
6
|
+
* LWPolyline is a DXF import-only entity. The constructor pre-processes the
|
|
7
|
+
* raw DXF data and returns a {@link Polyline} instance. It is never
|
|
8
|
+
* instantiated as a standalone entity within the application.
|
|
8
9
|
*/
|
|
9
|
-
export class Lwpolyline
|
|
10
|
+
export class Lwpolyline {
|
|
10
11
|
static type = 'Lwpolyline';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -14,16 +15,20 @@ export class Lwpolyline extends BasePolyline {
|
|
|
14
15
|
* @param {Array} data
|
|
15
16
|
*/
|
|
16
17
|
constructor(data) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Logging.instance.error(`LWPOLYLINE Vertices Count`);
|
|
24
|
-
}
|
|
18
|
+
if (data?.hasOwnProperty('90')) {
|
|
19
|
+
// DXF Groupcode 90 - Number of vertices
|
|
20
|
+
if (!Array.isArray(data.points)) {
|
|
21
|
+
Logging.instance.error(`LWPOLYLINE: group code 90 specifies ${data[90]} vertices but points array is missing or invalid`);
|
|
22
|
+
} else if (data.points.length !== data[90]) {
|
|
23
|
+
Logging.instance.error(`LWPOLYLINE: group code 90 specifies ${data[90]} vertices but ${data.points.length} were parsed`);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
26
|
+
|
|
27
|
+
if (data?.points?.length >= 4 && data.points[0].isSame(data.points.at(-1))) {
|
|
28
|
+
data.points.pop();
|
|
29
|
+
data[70] = (data[70] ?? 0) | 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
return new Polyline(data);
|
|
28
33
|
}
|
|
29
34
|
|
package/core/entities/point.js
CHANGED
|
@@ -298,16 +298,16 @@ export class Point {
|
|
|
298
298
|
* Find the closest point on a arc between start and end points
|
|
299
299
|
* @param {Point} startPoint
|
|
300
300
|
* @param {Point} endPoint
|
|
301
|
-
* @param {Point}
|
|
301
|
+
* @param {Point} centrePoint
|
|
302
302
|
* @param {number} direction - CCW if > 0
|
|
303
303
|
* @return {Point} the closest point on the arc or null
|
|
304
304
|
*/
|
|
305
|
-
closestPointOnArc(startPoint, endPoint,
|
|
306
|
-
const length = this.distance(
|
|
307
|
-
const radius =
|
|
305
|
+
closestPointOnArc(startPoint, endPoint, centrePoint, direction = 0) {
|
|
306
|
+
const length = this.distance(centrePoint);
|
|
307
|
+
const radius = centrePoint.distance(startPoint);
|
|
308
308
|
|
|
309
|
-
const Cx =
|
|
310
|
-
const Cy =
|
|
309
|
+
const Cx = centrePoint.x + radius * (this.x - centrePoint.x) / length;
|
|
310
|
+
const Cy = centrePoint.y + radius * (this.y - centrePoint.y) / length;
|
|
311
311
|
const closest = new Point(Cx, Cy);
|
|
312
312
|
|
|
313
313
|
// circle
|
|
@@ -315,7 +315,7 @@ export class Point {
|
|
|
315
315
|
return closest;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
if (closest.isOnArc(startPoint, endPoint,
|
|
318
|
+
if (closest.isOnArc(startPoint, endPoint, centrePoint, direction)) {
|
|
319
319
|
return closest;
|
|
320
320
|
}
|
|
321
321
|
|
|
@@ -327,15 +327,15 @@ export class Point {
|
|
|
327
327
|
* Determine if point is on arc segment
|
|
328
328
|
* @param {Point} startPoint
|
|
329
329
|
* @param {Point} endPoint
|
|
330
|
-
* @param {Point}
|
|
330
|
+
* @param {Point} centrePoint
|
|
331
331
|
* @param {number} direction - CCW if > 0
|
|
332
332
|
* @return {Point} true or false
|
|
333
333
|
*/
|
|
334
|
-
isOnArc(startPoint, endPoint,
|
|
334
|
+
isOnArc(startPoint, endPoint, centrePoint, direction = 0) {
|
|
335
335
|
// direction: ccw arc > 0, clockwise arc <= 0
|
|
336
|
-
const snapAngle =
|
|
337
|
-
const startAngle =
|
|
338
|
-
const endAngle =
|
|
336
|
+
const snapAngle = centrePoint.angle(this);
|
|
337
|
+
const startAngle = centrePoint.angle(startPoint);
|
|
338
|
+
const endAngle = centrePoint.angle(endPoint);
|
|
339
339
|
|
|
340
340
|
if (direction > 0) {
|
|
341
341
|
// Counter Clockwise Arc
|
|
@@ -438,7 +438,7 @@ export class Point {
|
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
/**
|
|
441
|
-
* Returns apothem; the distance from arc
|
|
441
|
+
* Returns apothem; the distance from arc centre to cord midpoint
|
|
442
442
|
* @param {Point} nextPoint
|
|
443
443
|
* @return {number} apothem
|
|
444
444
|
*/
|
|
@@ -465,12 +465,12 @@ export class Point {
|
|
|
465
465
|
|
|
466
466
|
let a = this.apothem(nextPoint);
|
|
467
467
|
|
|
468
|
-
// check if the
|
|
468
|
+
// check if the centre point is inverted. i.e. at 180 it goes inside the arc
|
|
469
469
|
if (Math.abs(this.bulgeAngle()) > Math.PI) {
|
|
470
470
|
a = -a;
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
-
// get the direction from the midpoint to the
|
|
473
|
+
// get the direction from the midpoint to the centre point
|
|
474
474
|
const direction = this.angle(nextPoint) + (Math.PI / 2) * Math.sign(this.bulge);
|
|
475
475
|
// centre point is the apothem distance from the mid point in the given direction
|
|
476
476
|
const centre = midp.project(direction, a);
|
|
@@ -486,12 +486,12 @@ export class Point {
|
|
|
486
486
|
* @return {number} - the partial bulge value
|
|
487
487
|
*/
|
|
488
488
|
partialBulge(nextPoint, splitPoint, afterSplit = false) {
|
|
489
|
-
const
|
|
489
|
+
const centre = this.bulgeCentrePoint(nextPoint);
|
|
490
490
|
const direction = this.bulge > 0 ? 1 : -1;
|
|
491
491
|
|
|
492
|
-
const startAngle =
|
|
493
|
-
const endAngle =
|
|
494
|
-
const splitAngle =
|
|
492
|
+
const startAngle = centre.angle(this);
|
|
493
|
+
const endAngle = centre.angle(nextPoint);
|
|
494
|
+
const splitAngle = centre.angle(splitPoint);
|
|
495
495
|
|
|
496
496
|
let includedAngle;
|
|
497
497
|
if (afterSplit) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { DXFFile } from '../lib/dxf/dxfFile.js';
|
|
2
|
-
import {
|
|
2
|
+
import { PolylineBase } from './polylineBase.js';
|
|
3
3
|
import { Point } from './point.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Polyline Entity Class
|
|
7
|
-
* @extends
|
|
7
|
+
* @extends PolylineBase
|
|
8
8
|
*/
|
|
9
|
-
export class Polyline extends
|
|
9
|
+
export class Polyline extends PolylineBase {
|
|
10
10
|
static type = 'Polyline';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -47,14 +47,14 @@ export class Polyline extends BasePolyline {
|
|
|
47
47
|
if (file.version < DXFFile.Version.R2000) {
|
|
48
48
|
// POLYLINE/VERTEX/SEQEND format for R12
|
|
49
49
|
file.writeGroupCode('0', 'POLYLINE');
|
|
50
|
-
file.writeGroupCode('8', this.layer);
|
|
51
|
-
file.writeGroupCode('6', this.lineType);
|
|
50
|
+
file.writeGroupCode('8', this.getProperty('layer'));
|
|
51
|
+
file.writeGroupCode('6', this.getProperty('lineType'));
|
|
52
52
|
file.writeGroupCode('66', 1); // vertices-follow flag
|
|
53
53
|
file.writeGroupCode('70', this.flags.getFlagValue());
|
|
54
54
|
|
|
55
55
|
for (let i = 0; i < this.points.length; i++) {
|
|
56
56
|
file.writeGroupCode('0', 'VERTEX');
|
|
57
|
-
file.writeGroupCode('8', this.layer);
|
|
57
|
+
file.writeGroupCode('8', this.getProperty('layer'));
|
|
58
58
|
file.writeGroupCode('10', this.points[i].x);
|
|
59
59
|
file.writeGroupCode('20', this.points[i].y);
|
|
60
60
|
file.writeGroupCode('30', '0.0');
|
|
@@ -62,7 +62,7 @@ export class Polyline extends BasePolyline {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
file.writeGroupCode('0', 'SEQEND');
|
|
65
|
-
file.writeGroupCode('8', this.layer);
|
|
65
|
+
file.writeGroupCode('8', this.getProperty('layer'));
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
68
|
|