@design-core/core 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +14 -7
- package/.github/workflows/DesignCoreQA.yml +3 -1
- package/README.md +11 -4
- package/TODO.md +19 -0
- package/core/core/core.js +49 -42
- package/core/designCore.js +61 -2
- package/core/dimensions/alignedDimension.js +142 -0
- package/core/dimensions/angularDimension.js +550 -0
- package/core/dimensions/baseCircularDimension.js +244 -0
- package/core/dimensions/baseDimension.js +558 -0
- package/core/dimensions/baseLinearDimension.js +371 -0
- package/core/dimensions/diametricDimension.js +82 -0
- package/core/dimensions/dimension.js +298 -0
- package/core/dimensions/linearDimension.js +29 -0
- package/core/dimensions/radialDimension.js +78 -0
- package/core/dimensions/rotatedDimension.js +167 -0
- package/core/entities/arc.js +314 -60
- package/core/entities/arctext.js +642 -0
- package/core/entities/basePolyline.js +553 -94
- package/core/entities/circle.js +181 -54
- package/core/entities/entity.js +154 -74
- package/core/entities/hatch.js +807 -0
- package/core/entities/insert.js +200 -39
- package/core/entities/line.js +196 -134
- package/core/entities/lwpolyline.js +21 -44
- package/core/entities/point.js +194 -29
- package/core/entities/polyline.js +54 -7
- package/core/entities/rectangle.js +43 -13
- package/core/entities/solid.js +43 -6
- package/core/entities/text.js +324 -181
- package/core/lib/boundingBox.js +73 -13
- package/core/lib/canvas.js +256 -65
- package/core/lib/clipboard.js +198 -0
- package/core/lib/colour.js +144 -0
- package/core/lib/colours.js +367 -301
- package/core/lib/commandLine.js +16 -28
- package/core/lib/commandManager.js +115 -58
- package/core/lib/constants.js +23 -0
- package/core/lib/dxf/dxf.js +249 -34
- package/core/lib/dxf/dxfFile.js +23 -38
- package/core/lib/dxf/dxfIterator.js +17 -5
- package/core/lib/dxf/dxfRead.js +23 -7
- package/core/lib/dxf/dxfWrite.js +39 -129
- package/core/lib/dxf/handleManager.js +97 -0
- package/core/lib/dxf/sections/blocks.js +16 -2
- package/core/lib/dxf/sections/entities.js +17 -3
- package/core/lib/dxf/sections/header.js +10 -1
- package/core/lib/dxf/sections/objects.js +36 -0
- package/core/lib/dxf/sections/section.js +43 -5
- package/core/lib/dxf/sections/tables.js +17 -3
- package/core/lib/entityManager.js +178 -0
- package/core/lib/fileio.js +14 -4
- package/core/lib/headers.js +229 -0
- package/core/lib/input.js +46 -0
- package/core/lib/inputManager.js +199 -194
- package/core/lib/intersect.js +341 -468
- package/core/lib/logging.js +25 -1
- package/core/lib/matrix.js +6 -8
- package/core/lib/mouse.js +24 -79
- package/core/lib/patterns.js +690 -0
- package/core/lib/promptOptions.js +133 -0
- package/core/lib/scene.js +71 -174
- package/core/lib/selectionManager.js +74 -48
- package/core/lib/selectionWindow.js +24 -10
- package/core/lib/settings.js +18 -11
- package/core/lib/snapping.js +129 -21
- package/core/lib/stateManager.js +288 -0
- package/core/lib/strings.js +57 -5
- package/core/lib/utils.js +95 -11
- package/core/objects/dictionary.js +43 -0
- package/core/objects/dictionaryManager.js +80 -0
- package/core/properties/dimType.js +117 -0
- package/core/properties/flags.js +66 -0
- package/core/properties/property.js +26 -0
- package/core/properties/propertyManager.js +65 -19
- package/core/tables/appId.js +31 -0
- package/core/tables/appIdManager.js +44 -0
- package/core/tables/block.js +323 -0
- package/core/tables/blockManager.js +48 -0
- package/core/tables/blockRecord.js +29 -0
- package/core/tables/blockRecordManager.js +37 -0
- package/core/tables/dimStyle.js +627 -0
- package/core/tables/dimStyleManager.js +187 -0
- package/core/tables/endblk.js +28 -0
- package/core/tables/layer.js +219 -0
- package/core/tables/layerManager.js +47 -0
- package/core/tables/ltype.js +85 -0
- package/core/tables/ltypeManager.js +65 -0
- package/core/tables/style.js +145 -0
- package/core/tables/styleManager.js +55 -0
- package/core/tables/styleManagerBase.js +110 -0
- package/core/tables/tableManagerBase.js +354 -0
- package/core/tables/ucs.js +28 -0
- package/core/tables/ucsManager.js +44 -0
- package/core/tables/view.js +28 -0
- package/core/tables/viewManager.js +44 -0
- package/core/tables/vport.js +70 -0
- package/core/tables/vportManager.js +65 -0
- package/core/tools/chamfer.js +343 -0
- package/core/tools/chamferFilletBase.js +162 -0
- package/core/tools/copy.js +68 -40
- package/core/tools/copybase.js +65 -0
- package/core/tools/copyclip.js +59 -0
- package/core/tools/cornerEntity.js +138 -0
- package/core/tools/cutclip.js +72 -0
- package/core/tools/distance.js +37 -9
- package/core/tools/erase.js +43 -13
- package/core/tools/explode.js +117 -0
- package/core/tools/extend.js +71 -32
- package/core/tools/fillet.js +336 -0
- package/core/tools/identify.js +33 -6
- package/core/tools/matchProp.js +106 -0
- package/core/tools/mirror.js +141 -0
- package/core/tools/move.js +65 -32
- package/core/tools/offset.js +321 -0
- package/core/tools/pan.js +54 -0
- package/core/tools/pasteclip.js +93 -0
- package/core/tools/purge.js +90 -0
- package/core/tools/rotate.js +105 -70
- package/core/tools/scale.js +166 -0
- package/core/tools/tool.js +14 -4
- package/core/tools/trim.js +71 -32
- package/core/tools/zoom.js +152 -0
- package/package.json +3 -305
- package/test/dimensions/alignedDimension.test.js +211 -0
- package/test/dimensions/angularDimension.test.js +243 -0
- package/test/dimensions/baseDimension.test.js +239 -0
- package/test/dimensions/diametricDimension.test.js +203 -0
- package/test/dimensions/dimension.test.js +102 -0
- package/test/dimensions/radialDimension.test.js +203 -0
- package/test/dimensions/rotatedDimension.test.js +224 -0
- package/test/entities/arc.test.js +463 -41
- package/test/entities/arctext.test.js +329 -0
- package/test/entities/basePolyline.test.js +459 -10
- package/test/entities/circle.test.js +312 -7
- package/test/entities/entity.test.js +28 -9
- package/test/entities/hatch.test.js +606 -0
- package/test/entities/insert.test.js +90 -0
- package/test/entities/line.test.js +182 -6
- package/test/entities/point.test.js +375 -37
- package/test/entities/solid.test.js +156 -0
- package/test/entities/text.test.js +533 -7
- package/test/integration/exportIntegration.reference.dxf +1706 -0
- package/test/integration/exportIntegration.test.js +186 -0
- package/test/integration/importExportIntegration.test.js +126 -0
- package/test/integration/intersectionIntegration.test.js +880 -0
- package/test/integration/userInputIntegration.reference.dxf +1410 -0
- package/test/integration/userInputIntegration.test.js +269 -0
- package/test/lib/boundingBox.test.js +26 -2
- package/test/lib/canvas.test.js +613 -5
- package/test/lib/clipboard.test.js +70 -0
- package/test/lib/colour.test.js +114 -0
- package/test/lib/colours.test.js +63 -18
- package/test/lib/commandLine.test.js +159 -22
- package/test/lib/commandManager.test.js +4 -4
- package/test/lib/constants.test.js +9 -0
- package/test/lib/dxf/dxfFile.test.js +6 -2
- package/test/lib/dxf/dxfIterator.test.js +1 -1
- package/test/lib/dxf/dxfWrite.test.js +173 -0
- package/test/lib/dxf/handleManager.test.js +137 -0
- package/test/lib/dxf/sections/blocks.test.js +5 -5
- package/test/lib/dxf/sections/entities.test.js +4 -4
- package/test/lib/dxf/sections/header.test.js +18 -4
- package/test/lib/dxf/sections/objects.test.js +132 -0
- package/test/lib/dxf/sections/section.test.js +29 -29
- package/test/lib/dxf/sections/tables.test.js +3 -3
- package/test/lib/entityManager.test.js +163 -0
- package/test/lib/headers.test.js +233 -0
- package/test/lib/inputManager.test.js +1144 -5
- package/test/lib/intersect.test.js +454 -331
- package/test/lib/logging.test.js +1 -1
- package/test/lib/matrix.test.js +1 -1
- package/test/lib/mouse.test.js +327 -4
- package/test/lib/patterns.test.js +105 -0
- package/test/lib/promptOptions.test.js +179 -0
- package/test/lib/scene.test.js +16 -52
- package/test/lib/selectionManager.test.js +17 -5
- package/test/lib/settings.test.js +111 -5
- package/test/lib/snapping.test.js +142 -4
- package/test/lib/stateManager.test.js +155 -0
- package/test/lib/utils.test.js +81 -1
- package/test/objects/dictionary.test.js +81 -0
- package/test/objects/dictionaryManager.test.js +34 -0
- package/test/properties/dimType.test.js +62 -0
- package/test/properties/flags.test.js +164 -0
- package/test/properties/property.test.js +13 -0
- package/test/properties/propertyManager.test.js +10 -11
- package/test/tables/appId.test.js +46 -0
- package/test/tables/appIdManager.test.js +72 -0
- package/test/tables/block.test.js +157 -0
- package/test/tables/blockManager.test.js +52 -0
- package/test/tables/blockRecord.test.js +39 -0
- package/test/tables/blockRecordManager.test.js +60 -0
- package/test/tables/dimstyle.test.js +72 -0
- package/test/tables/endblk.test.js +30 -0
- package/test/tables/layer.test.js +95 -0
- package/test/tables/layerManager.test.js +172 -0
- package/test/{styles → tables}/ltype.test.js +7 -7
- package/test/{styles → tables}/ltypeManager.test.js +2 -2
- package/test/tables/style.test.js +44 -0
- package/test/tables/stylemanagerbase.test.js +220 -0
- package/test/tables/tableManagerBase.test.js +194 -0
- package/test/tables/ucs.test.js +37 -0
- package/test/tables/ucsManager.test.js +48 -0
- package/test/tables/view.test.js +37 -0
- package/test/tables/viewManager.test.js +48 -0
- package/test/tables/vport.test.js +117 -0
- package/test/tables/vportManager.test.js +54 -0
- package/test/test-helpers/test-helpers.js +66 -0
- package/test/tools/chamfer.test.js +970 -0
- package/test/tools/chamferFilletBase.test.js +446 -0
- package/test/tools/copy.test.js +22 -14
- package/test/tools/copybase.test.js +93 -0
- package/test/tools/copyclip.test.js +92 -0
- package/test/tools/cornerEntity.test.js +201 -0
- package/test/tools/cutclip.test.js +94 -0
- package/test/tools/distance.test.js +140 -18
- package/test/tools/erase.test.js +12 -12
- package/test/tools/explode.test.js +45 -0
- package/test/tools/extend.test.js +326 -44
- package/test/tools/fillet.test.js +831 -0
- package/test/tools/identify.test.js +115 -16
- package/test/tools/matchProp.test.js +84 -0
- package/test/tools/mirror.test.js +475 -0
- package/test/tools/move.test.js +75 -19
- package/test/tools/offset.test.js +748 -0
- package/test/tools/pan.test.js +72 -0
- package/test/tools/pasteclip.test.js +100 -0
- package/test/tools/purge.test.js +89 -0
- package/test/tools/rotate.test.js +89 -23
- package/test/tools/scale.test.js +529 -0
- package/test/tools/trim.test.js +419 -84
- package/test/tools/zoom.test.js +504 -0
- package/core/entities/alignedDimension.js +0 -279
- package/core/entities/angularDimension.js +0 -194
- package/core/entities/baseDimension.js +0 -227
- package/core/entities/block.js +0 -247
- package/core/entities/diametricDimension.js +0 -133
- package/core/entities/dimension.js +0 -202
- package/core/entities/radialDimension.js +0 -126
- package/core/styles/dimStyle.js +0 -719
- package/core/styles/dimStyleManager.js +0 -47
- package/core/styles/layer.js +0 -133
- package/core/styles/layerManager.js +0 -40
- package/core/styles/ltype.js +0 -86
- package/core/styles/ltypeManager.js +0 -50
- package/core/styles/style.js +0 -158
- package/core/styles/styleManager.js +0 -43
- package/core/styles/styleManagerBase.js +0 -299
- package/test/entities/block.test.js +0 -22
- package/test/styles/dimstyle.test.js +0 -19
- package/test/styles/layerManager.test.js +0 -150
- package/test/styles/style.test.js +0 -33
- package/test/styles/stylemanagerbase.test.js +0 -219
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
import {Strings} from '../lib/strings.js';
|
|
2
|
-
import {Entity} from './entity.js';
|
|
3
|
-
import {Input, PromptOptions} from '../lib/inputManager.js';
|
|
4
|
-
import {Logging} from '../lib/logging.js';
|
|
5
|
-
import {Utils} from '../lib/utils.js';
|
|
6
|
-
import {DXFFile} from '../lib/dxf/dxfFile.js';
|
|
7
|
-
import {BoundingBox} from '../lib/boundingBox.js';
|
|
8
|
-
import {Point} from './point.js';
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
1
|
+
import { Strings } from '../lib/strings.js';
|
|
2
|
+
import { Entity } from './entity.js';
|
|
3
|
+
import { Input, PromptOptions } from '../lib/inputManager.js';
|
|
4
|
+
import { Logging } from '../lib/logging.js';
|
|
5
|
+
import { Utils } from '../lib/utils.js';
|
|
6
|
+
import { DXFFile } from '../lib/dxf/dxfFile.js';
|
|
7
|
+
import { BoundingBox } from '../lib/boundingBox.js';
|
|
8
|
+
import { Point } from './point.js';
|
|
9
|
+
import { Line } from './line.js';
|
|
10
|
+
import { Arc } from './arc.js';
|
|
11
|
+
import { Flags } from '../properties/flags.js';
|
|
12
|
+
import { Property } from '../properties/property.js';
|
|
13
|
+
import { AddState, RemoveState, UpdateState } from '../lib/stateManager.js';
|
|
14
|
+
|
|
15
|
+
import { DesignCore } from '../designCore.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Base Polyline Entity Class
|
|
19
|
+
* @extends Entity
|
|
20
|
+
*/
|
|
12
21
|
export class BasePolyline extends Entity {
|
|
22
|
+
/**
|
|
23
|
+
* Create a Base Polyline
|
|
24
|
+
* @param {Array} data
|
|
25
|
+
*/
|
|
13
26
|
constructor(data) {
|
|
14
27
|
super(data);
|
|
15
28
|
|
|
@@ -29,58 +42,71 @@ export class BasePolyline extends Entity {
|
|
|
29
42
|
});
|
|
30
43
|
|
|
31
44
|
Object.defineProperty(this, 'flags', {
|
|
32
|
-
value:
|
|
45
|
+
value: data?.flags instanceof Flags ? data.flags : new Flags(),
|
|
33
46
|
writable: true,
|
|
34
47
|
});
|
|
35
48
|
|
|
36
49
|
if (data) {
|
|
37
|
-
if (data
|
|
50
|
+
if (data.hasOwnProperty('40')) {
|
|
38
51
|
// DXF Groupcode 40 - Start Width
|
|
39
52
|
}
|
|
40
53
|
|
|
41
|
-
if (data
|
|
54
|
+
if (data.hasOwnProperty('41')) {
|
|
42
55
|
// DXF Groupcode 41 - End Width
|
|
43
56
|
}
|
|
44
57
|
|
|
45
|
-
if (data
|
|
58
|
+
if (data.hasOwnProperty('43')) {
|
|
46
59
|
// DXF Groupcode 43 - Constant Width
|
|
47
60
|
}
|
|
61
|
+
}
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
// DXF Groupcode 70 - Polyline flag (bit-coded; default = 0):
|
|
64
|
+
// 1 = This is a closed polyline (or a polygon mesh closed in the M direction)
|
|
65
|
+
// 2 = Curve-fit vertices have been added
|
|
66
|
+
// 4 = Spline-fit vertices have been added
|
|
67
|
+
// 8 = This is a 3D polyline
|
|
68
|
+
// 16 = This is a 3D polygon mesh
|
|
69
|
+
// 32 = The polygon mesh is closed in the N direction
|
|
70
|
+
// 64 = The polyline is a polyface mesh
|
|
71
|
+
// 128 = The linetype pattern is generated continuously around the vertices of this polyline
|
|
72
|
+
|
|
73
|
+
this.flags.setFlagValue(Property.loadValue([data?.flags, data?.[70]], 0));
|
|
74
|
+
|
|
75
|
+
// If the first and last points are the same the polyline should be considered closed.
|
|
76
|
+
// Require at least 4 points so that after removing the duplicate endpoint at least 3 remain.
|
|
77
|
+
if (this.points.length >= 4 && this.points[0].isSame(this.points.at(-1))) {
|
|
78
|
+
this.points.pop();
|
|
79
|
+
this.flags.addValue(1);
|
|
62
80
|
}
|
|
63
81
|
}
|
|
64
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Execute method
|
|
85
|
+
* executes the workflow, requesting input required to create an entity
|
|
86
|
+
*/
|
|
65
87
|
async execute() {
|
|
66
88
|
try {
|
|
67
89
|
const op = new PromptOptions(Strings.Input.START, [Input.Type.POINT]);
|
|
68
90
|
const pt1 = await DesignCore.Scene.inputManager.requestInput(op);
|
|
91
|
+
if (pt1 === undefined) return;
|
|
69
92
|
this.points.push(pt1);
|
|
70
93
|
|
|
71
|
-
let pt2;
|
|
72
|
-
let op2;
|
|
73
94
|
let index;
|
|
74
95
|
while (true) {
|
|
75
|
-
|
|
76
|
-
let prompt = Strings.Input.NEXTPOINT;
|
|
96
|
+
const options = [];
|
|
77
97
|
if (this.points.length >= 2) {
|
|
78
|
-
options
|
|
79
|
-
|
|
98
|
+
options.push(this.inputMode === this.modes.LINE ? this.modes.ARC : this.modes.LINE);
|
|
99
|
+
}
|
|
100
|
+
if (this.points.length >= 3) {
|
|
101
|
+
options.push('Close');
|
|
102
|
+
}
|
|
103
|
+
if (this.points.length > 1) {
|
|
104
|
+
options.push('Undo');
|
|
80
105
|
}
|
|
81
106
|
|
|
82
|
-
op2 = new PromptOptions(
|
|
83
|
-
pt2 = await DesignCore.Scene.inputManager.requestInput(op2);
|
|
107
|
+
const op2 = new PromptOptions(Strings.Input.NEXTPOINT, [Input.Type.POINT, Input.Type.DYNAMIC], options);
|
|
108
|
+
const pt2 = await DesignCore.Scene.inputManager.requestInput(op2);
|
|
109
|
+
if (pt2 === undefined) break;
|
|
84
110
|
|
|
85
111
|
if (Input.getType(pt2) === Input.Type.POINT) {
|
|
86
112
|
if (this.inputMode === this.modes.ARC) {
|
|
@@ -92,10 +118,29 @@ export class BasePolyline extends Entity {
|
|
|
92
118
|
index = DesignCore.Scene.inputManager.actionCommand(this, index);
|
|
93
119
|
} else if (Input.getType(pt2) === Input.Type.STRING) {
|
|
94
120
|
// options are converted to input in the prompt options class
|
|
95
|
-
if (pt2 ===
|
|
121
|
+
if (pt2 === 'Undo') {
|
|
122
|
+
this.points.pop();
|
|
123
|
+
// Always reset bulge to 0 after undo
|
|
124
|
+
if (this.points.length > 0) {
|
|
125
|
+
this.points.at(-1).bulge = 0;
|
|
126
|
+
}
|
|
127
|
+
// Keep the scene entity in sync with the updated points
|
|
128
|
+
if (index !== undefined) {
|
|
129
|
+
index = DesignCore.Scene.inputManager.actionCommand(this, index);
|
|
130
|
+
}
|
|
131
|
+
continue;
|
|
132
|
+
} else if (pt2 === 'Close') {
|
|
133
|
+
// Only set bulge if in arc mode and at least two points
|
|
134
|
+
if (this.inputMode === this.modes.ARC && this.points.length > 1) {
|
|
135
|
+
this.points.at(-1).bulge = this.getBulgeFromSegment(pt1);
|
|
136
|
+
}
|
|
137
|
+
// Set flags to indicate closed shape
|
|
138
|
+
this.flags.addValue(1);
|
|
139
|
+
DesignCore.Scene.inputManager.executeCommand(this, index);
|
|
140
|
+
return;
|
|
141
|
+
} else if (pt2 === this.modes.ARC) {
|
|
96
142
|
this.inputMode = this.modes.ARC;
|
|
97
|
-
}
|
|
98
|
-
if (pt2 === this.modes.LINE) {
|
|
143
|
+
} else if (pt2 === this.modes.LINE) {
|
|
99
144
|
this.inputMode = this.modes.LINE;
|
|
100
145
|
}
|
|
101
146
|
}
|
|
@@ -105,26 +150,33 @@ export class BasePolyline extends Entity {
|
|
|
105
150
|
}
|
|
106
151
|
}
|
|
107
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Preview the entity during creation
|
|
155
|
+
*/
|
|
108
156
|
preview() {
|
|
109
157
|
const mousePoint = DesignCore.Mouse.pointOnScene();
|
|
110
158
|
|
|
111
159
|
if (this.points.length >= 1) {
|
|
112
160
|
const points = [...this.points, mousePoint];
|
|
113
|
-
DesignCore.Scene.
|
|
161
|
+
DesignCore.Scene.tempEntities.create(this.type, { points: points });
|
|
114
162
|
}
|
|
115
163
|
|
|
116
164
|
if (this.inputMode === this.modes.ARC) {
|
|
117
|
-
const arcpoints = Utils.cloneObject(
|
|
165
|
+
const arcpoints = Utils.cloneObject(this.points);
|
|
118
166
|
arcpoints.at(-1).bulge = this.getBulgeFromSegment(mousePoint);
|
|
119
167
|
const points = [...arcpoints, mousePoint];
|
|
120
|
-
DesignCore.Scene.
|
|
168
|
+
DesignCore.Scene.tempEntities.create(this.type, { points: points });
|
|
121
169
|
}
|
|
122
170
|
}
|
|
123
171
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Draw the polyline
|
|
174
|
+
* @param {Object} ctx
|
|
175
|
+
* @param {number} scale
|
|
176
|
+
* @param {boolean} stroke - don't stroke hatch boundary shapes
|
|
177
|
+
*/
|
|
178
|
+
draw(ctx, scale, stroke = true) {
|
|
179
|
+
for (let i = 0; i < this.points.length; i++) {
|
|
128
180
|
if (this.points[i].bulge === 0) {
|
|
129
181
|
ctx.lineTo(this.points[i].x, this.points[i].y);
|
|
130
182
|
} else {
|
|
@@ -143,60 +195,76 @@ export class BasePolyline extends Entity {
|
|
|
143
195
|
ctx.arcNegative(centerPoint.x, centerPoint.y, radius, centerPoint.angle(this.points[i]), centerPoint.angle(nextPoint));
|
|
144
196
|
}
|
|
145
197
|
}
|
|
146
|
-
|
|
147
|
-
// debug centerpoint
|
|
148
|
-
// ctx.arc(centerPoint.x, centerPoint.y, 3, 0, 2 * Math.PI);
|
|
149
198
|
}
|
|
150
199
|
}
|
|
151
200
|
|
|
152
201
|
// handle a closed shape
|
|
153
|
-
if (this.flags
|
|
202
|
+
if (this.flags.hasFlag(1)) {
|
|
154
203
|
ctx.lineTo(this.points[0].x, this.points[0].y);
|
|
155
204
|
}
|
|
156
205
|
|
|
157
|
-
|
|
206
|
+
if (stroke) {
|
|
207
|
+
ctx.stroke();
|
|
208
|
+
}
|
|
158
209
|
}
|
|
159
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Write the entity to file in the dxf format
|
|
213
|
+
* @param {DXFFile} file
|
|
214
|
+
*/
|
|
160
215
|
dxf(file) {
|
|
161
|
-
file.writeGroupCode('0', '
|
|
162
|
-
file.writeGroupCode('5',
|
|
216
|
+
file.writeGroupCode('0', 'LWPOLYLINE');
|
|
217
|
+
file.writeGroupCode('5', this.handle, DXFFile.Version.R2000); // Handle
|
|
163
218
|
file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
|
|
164
|
-
file.writeGroupCode('100', '
|
|
219
|
+
file.writeGroupCode('100', 'AcDbPolyline', DXFFile.Version.R2000);
|
|
165
220
|
file.writeGroupCode('8', this.layer); // LAYERNAME
|
|
166
|
-
file.writeGroupCode('
|
|
167
|
-
file.writeGroupCode('20', '0');
|
|
168
|
-
file.writeGroupCode('30', '0');
|
|
221
|
+
file.writeGroupCode('6', this.lineType);
|
|
169
222
|
file.writeGroupCode('39', this.lineWidth);
|
|
170
|
-
file.writeGroupCode('
|
|
171
|
-
file.writeGroupCode('
|
|
172
|
-
this.vertices(file);
|
|
173
|
-
file.writeGroupCode('0', 'SEQEND');
|
|
174
|
-
file.writeGroupCode('5', file.nextHandle(), DXFFile.Version.R2000); // Handle
|
|
175
|
-
file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
|
|
176
|
-
file.writeGroupCode('8', this.layer);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
vertices(file) {
|
|
223
|
+
file.writeGroupCode('90', this.points.length);
|
|
224
|
+
file.writeGroupCode('70', this.flags.getFlagValue());
|
|
180
225
|
for (let i = 0; i < this.points.length; i++) {
|
|
181
|
-
file.writeGroupCode('
|
|
182
|
-
file.writeGroupCode('
|
|
183
|
-
file.writeGroupCode('100', 'AcDbEntity', DXFFile.Version.R2000);
|
|
184
|
-
file.writeGroupCode('100', 'AcDbVertex', DXFFile.Version.R2000);
|
|
185
|
-
file.writeGroupCode('100', 'AcDb2dVertex', DXFFile.Version.R2000);
|
|
186
|
-
file.writeGroupCode('8', this.layer);
|
|
187
|
-
file.writeGroupCode('10', this.points[i].x); // X
|
|
188
|
-
file.writeGroupCode('20', this.points[i].y); // Y
|
|
189
|
-
file.writeGroupCode('30', '0.0');
|
|
226
|
+
file.writeGroupCode('10', this.points[i].x);
|
|
227
|
+
file.writeGroupCode('20', this.points[i].y);
|
|
190
228
|
file.writeGroupCode('42', this.points[i].bulge);
|
|
191
229
|
}
|
|
192
230
|
}
|
|
193
231
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
/**
|
|
233
|
+
* Return a list of points representing a polyline version of this entity.
|
|
234
|
+
* For closed polylines (flag bit 1 set), appends a copy of the first point
|
|
235
|
+
* at the end so that callers (e.g. hatch boundary detection, intersection
|
|
236
|
+
* tests) can treat the sequence as a simple open chain where the first and
|
|
237
|
+
* last point are equal — the same convention used by Circle.toPolylinePoints().
|
|
238
|
+
* The closure point carries bulge = 0 because it is just the destination;
|
|
239
|
+
* the actual closing-segment bulge lives on the last stored point.
|
|
240
|
+
* @return {Array}
|
|
241
|
+
*/
|
|
242
|
+
toPolylinePoints() {
|
|
243
|
+
const points = this.points.map((p) => new Point(p.x, p.y, p.bulge, p.sequence));
|
|
244
|
+
if (this.flags.hasFlag(1)) {
|
|
245
|
+
points.push(new Point(this.points[0].x, this.points[0].y));
|
|
246
|
+
}
|
|
247
|
+
return points;
|
|
198
248
|
}
|
|
199
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Set entity points from a polyline point representation
|
|
252
|
+
* @param {Array} points
|
|
253
|
+
*/
|
|
254
|
+
fromPolylinePoints(points) {
|
|
255
|
+
const pts = points.map((p) => new Point(p.x, p.y, p.bulge));
|
|
256
|
+
if (this.flags.hasFlag(1) && pts.length >= 2 && pts[0].isSame(pts.at(-1))) {
|
|
257
|
+
pts.pop();
|
|
258
|
+
}
|
|
259
|
+
this.points = pts;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get snap points
|
|
264
|
+
* @param {Point} mousePoint
|
|
265
|
+
* @param {number} delta
|
|
266
|
+
* @return {Array} - array of snap points
|
|
267
|
+
*/
|
|
200
268
|
snaps(mousePoint, delta) {
|
|
201
269
|
const snaps = [];
|
|
202
270
|
|
|
@@ -209,16 +277,16 @@ export class BasePolyline extends Entity {
|
|
|
209
277
|
|
|
210
278
|
if (DesignCore.Settings.midsnap) {
|
|
211
279
|
for (let i = 1; i < this.points.length; i++) {
|
|
212
|
-
if (this.points[i-1].bulge === 0) {
|
|
213
|
-
snaps.push(
|
|
280
|
+
if (this.points[i - 1].bulge === 0) {
|
|
281
|
+
snaps.push(this.points[i - 1].midPoint(this.points[i]));
|
|
214
282
|
}
|
|
215
283
|
}
|
|
216
284
|
}
|
|
217
285
|
|
|
218
286
|
if (DesignCore.Settings.centresnap) {
|
|
219
287
|
for (let i = 1; i < this.points.length; i++) {
|
|
220
|
-
if (this.points[i-1].bulge !== 0) {
|
|
221
|
-
snaps.push(
|
|
288
|
+
if (this.points[i - 1].bulge !== 0) {
|
|
289
|
+
snaps.push(this.points[i - 1].bulgeCentrePoint(this.points[i]));
|
|
222
290
|
}
|
|
223
291
|
}
|
|
224
292
|
}
|
|
@@ -235,6 +303,89 @@ export class BasePolyline extends Entity {
|
|
|
235
303
|
return snaps;
|
|
236
304
|
}
|
|
237
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Get the index and segment object for the segment closest to point P
|
|
308
|
+
* @param {Point} P
|
|
309
|
+
* @return {Object}
|
|
310
|
+
*/
|
|
311
|
+
#getClosestSegmentData(P) {
|
|
312
|
+
let closestIndex = -1;
|
|
313
|
+
let closestSegment = null;
|
|
314
|
+
let minDistance = Infinity;
|
|
315
|
+
|
|
316
|
+
const segmentCount = this.flags.hasFlag(1) ? this.points.length : this.points.length - 1;
|
|
317
|
+
for (let i = 1; i <= segmentCount; i++) {
|
|
318
|
+
const A = this.points[i - 1];
|
|
319
|
+
const B = this.points[i % this.points.length];
|
|
320
|
+
|
|
321
|
+
let closestPoint;
|
|
322
|
+
let candidateSegment;
|
|
323
|
+
if (A.bulge !== 0) {
|
|
324
|
+
const center = A.bulgeCentrePoint(B);
|
|
325
|
+
const direction = A.bulge > 0 ? 1 : -1;
|
|
326
|
+
closestPoint = P.closestPointOnArc(A, B, center, direction);
|
|
327
|
+
candidateSegment = new Arc({ points: [center, A, B], direction });
|
|
328
|
+
} else {
|
|
329
|
+
closestPoint = P.closestPointOnLine(A, B);
|
|
330
|
+
candidateSegment = new Line({ points: [A, B] });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (closestPoint) {
|
|
334
|
+
const dist = P.distance(closestPoint);
|
|
335
|
+
if (dist < minDistance) {
|
|
336
|
+
minDistance = dist;
|
|
337
|
+
closestIndex = i;
|
|
338
|
+
closestSegment = candidateSegment;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return { index: closestIndex, segment: closestSegment };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get the 1-based index of the segment closest to point P
|
|
348
|
+
* @param {Point} P
|
|
349
|
+
* @return {number} - 1-based segment index, or -1 if no segments
|
|
350
|
+
*/
|
|
351
|
+
getClosestSegmentIndex(P) {
|
|
352
|
+
return this.#getClosestSegmentData(P).index;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Get the segment closest to point P
|
|
357
|
+
* @param {Point} P
|
|
358
|
+
* @return {Line|Arc|null} - closest segment
|
|
359
|
+
*/
|
|
360
|
+
getClosestSegment(P) {
|
|
361
|
+
return this.#getClosestSegmentData(P).segment;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Determine if two segments (by 1-based index) are consecutive in this polyline
|
|
366
|
+
* @param {number} segIndex1
|
|
367
|
+
* @param {number} segIndex2
|
|
368
|
+
* @return {boolean}
|
|
369
|
+
*/
|
|
370
|
+
areConsecutiveSegments(segIndex1, segIndex2) {
|
|
371
|
+
const diff = Math.abs(segIndex1 - segIndex2);
|
|
372
|
+
if (diff === 1) return true;
|
|
373
|
+
// Closed polyline: closing segment (index = points.length) and segment 1 are consecutive
|
|
374
|
+
if (this.flags.hasFlag(1)) {
|
|
375
|
+
const closeIdx = this.points.length;
|
|
376
|
+
if ((segIndex1 === closeIdx && segIndex2 === 1) ||
|
|
377
|
+
(segIndex2 === closeIdx && segIndex1 === 1)) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Get closest point on entity
|
|
386
|
+
* @param {Point} P
|
|
387
|
+
* @return {Array} - [Point, distance]
|
|
388
|
+
*/
|
|
238
389
|
closestPoint(P) {
|
|
239
390
|
let distance = Infinity;
|
|
240
391
|
let minPnt = P;
|
|
@@ -246,7 +397,11 @@ export class BasePolyline extends Entity {
|
|
|
246
397
|
let pnt;
|
|
247
398
|
if (A.bulge !== 0) {
|
|
248
399
|
const C = A.bulgeCentrePoint(B);
|
|
249
|
-
|
|
400
|
+
// -ve bulge is clockwise
|
|
401
|
+
// +ve bulge is counter clockwise
|
|
402
|
+
// -ve arc is clockwise
|
|
403
|
+
// +ve arc is counter clockwise
|
|
404
|
+
const direction = A.bulge > 0 ? 1 : -1;
|
|
250
405
|
pnt = P.closestPointOnArc(A, B, C, direction);
|
|
251
406
|
} else {
|
|
252
407
|
pnt = P.closestPointOnLine(A, B);
|
|
@@ -265,8 +420,312 @@ export class BasePolyline extends Entity {
|
|
|
265
420
|
return [minPnt, distance];
|
|
266
421
|
}
|
|
267
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Check if a point falls on a specific segment of the polyline
|
|
425
|
+
* @param {Point} point - the point to test
|
|
426
|
+
* @param {Point} A - segment start point
|
|
427
|
+
* @param {Point} B - segment end point
|
|
428
|
+
* @return {boolean}
|
|
429
|
+
*/
|
|
430
|
+
isPointOnSegment(point, A, B) {
|
|
431
|
+
if (A.bulge !== 0) {
|
|
432
|
+
const center = A.bulgeCentrePoint(B);
|
|
433
|
+
const direction = A.bulge > 0 ? 1 : -1;
|
|
434
|
+
return point.isOnArc(A, B, center, direction);
|
|
435
|
+
}
|
|
436
|
+
return point.isOnLine(A, B);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Get the closest point on a segment to a given point
|
|
441
|
+
* @param {Point} point - the reference point
|
|
442
|
+
* @param {number} segmentIndex - 1-based segment index
|
|
443
|
+
* @return {Point|null}
|
|
444
|
+
*/
|
|
445
|
+
closestPointOnSegment(point, segmentIndex) {
|
|
446
|
+
const A = this.points[segmentIndex - 1];
|
|
447
|
+
const B = this.points[segmentIndex % this.points.length];
|
|
448
|
+
|
|
449
|
+
if (A.bulge !== 0) {
|
|
450
|
+
const center = A.bulgeCentrePoint(B);
|
|
451
|
+
const direction = A.bulge > 0 ? 1 : -1;
|
|
452
|
+
const closest = point.closestPointOnArc(A, B, center, direction);
|
|
453
|
+
|
|
454
|
+
if (closest) {
|
|
455
|
+
return closest;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Point not on arc - return the nearer endpoint
|
|
459
|
+
return point.distance(A) < point.distance(B) ? A : B;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return point.closestPointOnLine(A, B);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Get the normalised position of a point along a segment for ordering
|
|
467
|
+
* @param {Point} point - a point on the segment
|
|
468
|
+
* @param {number} segmentIndex - 1-based segment index
|
|
469
|
+
* @return {number}
|
|
470
|
+
*/
|
|
471
|
+
positionOnSegment(point, segmentIndex) {
|
|
472
|
+
const A = this.points[segmentIndex - 1];
|
|
473
|
+
const B = this.points[segmentIndex % this.points.length];
|
|
474
|
+
|
|
475
|
+
if (A.bulge !== 0) {
|
|
476
|
+
const center = A.bulgeCentrePoint(B);
|
|
477
|
+
const direction = A.bulge > 0 ? 1 : -1;
|
|
478
|
+
const startAngle = center.angle(A);
|
|
479
|
+
const pointAngle = center.angle(point);
|
|
480
|
+
return ((pointAngle - startAngle) * direction + 4 * Math.PI) % (2 * Math.PI);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return A.distance(point);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Extend the entity
|
|
488
|
+
* Only extends when the end segment closest to the mouse is a line segment (bulge === 0)
|
|
489
|
+
* @param {Array} intersections - array of intersection points
|
|
490
|
+
* @return {Array} - array of state changes
|
|
491
|
+
*/
|
|
492
|
+
extend(intersections) {
|
|
493
|
+
const stateChanges = [];
|
|
494
|
+
|
|
495
|
+
if (!intersections?.length) {
|
|
496
|
+
return stateChanges;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const mousePosition = DesignCore.Mouse.pointOnScene();
|
|
500
|
+
const lastIndex = this.points.length - 1;
|
|
501
|
+
|
|
502
|
+
// Determine which end segment is closer to the mouse
|
|
503
|
+
const closestOnFirst = this.closestPointOnSegment(mousePosition, 1);
|
|
504
|
+
const closestOnLast = this.closestPointOnSegment(mousePosition, lastIndex);
|
|
505
|
+
const distToFirst = mousePosition.distance(closestOnFirst);
|
|
506
|
+
const distToLast = mousePosition.distance(closestOnLast);
|
|
507
|
+
|
|
508
|
+
// Identify the selected end: the endpoint index and the bulge of its segment
|
|
509
|
+
let endPointIndex;
|
|
510
|
+
let endSegmentBulge;
|
|
511
|
+
|
|
512
|
+
if (distToFirst < distToLast) {
|
|
513
|
+
endPointIndex = 0;
|
|
514
|
+
endSegmentBulge = this.points[0].bulge;
|
|
515
|
+
} else if (distToLast < distToFirst) {
|
|
516
|
+
endPointIndex = lastIndex;
|
|
517
|
+
endSegmentBulge = this.points[lastIndex - 1].bulge;
|
|
518
|
+
} else {
|
|
519
|
+
// Tie (includes 2-point polyline where both segments are the same)
|
|
520
|
+
// Break tie by distance to the actual endpoints
|
|
521
|
+
if (mousePosition.distance(this.points[0]) <= mousePosition.distance(this.points[lastIndex])) {
|
|
522
|
+
endPointIndex = 0;
|
|
523
|
+
endSegmentBulge = this.points[0].bulge;
|
|
524
|
+
} else {
|
|
525
|
+
endPointIndex = lastIndex;
|
|
526
|
+
endSegmentBulge = this.points[lastIndex - 1].bulge;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Only allow extending line segments (not arcs)
|
|
531
|
+
if (endSegmentBulge !== 0) {
|
|
532
|
+
DesignCore.Core.notify(`${this.type} - ${Strings.Message.NOEXTEND}`);
|
|
533
|
+
return stateChanges;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const endPoint = this.points[endPointIndex];
|
|
537
|
+
const adjacentPoint = endPointIndex === 0 ? this.points[1] : this.points[lastIndex - 1];
|
|
538
|
+
|
|
539
|
+
// Sort intersections by distance from the end point
|
|
540
|
+
Utils.sortPointsByDistance(intersections, endPoint);
|
|
541
|
+
|
|
542
|
+
// Find the closest intersection that lies beyond the endpoint
|
|
543
|
+
const direction = endPoint.subtract(adjacentPoint);
|
|
544
|
+
const newEndPoint = intersections.find((p) => {
|
|
545
|
+
if (p.isSame(endPoint)) return false;
|
|
546
|
+
if (p.subtract(endPoint).dot(direction) <= 0) return false;
|
|
547
|
+
if (adjacentPoint.distance(p) <= adjacentPoint.distance(endPoint)) return false;
|
|
548
|
+
return true;
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
if (!newEndPoint) {
|
|
552
|
+
return stateChanges;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Build the new points array
|
|
556
|
+
const newPoints = this.points.map((p) => p.clone());
|
|
557
|
+
newPoints[endPointIndex] = newEndPoint;
|
|
558
|
+
|
|
559
|
+
if (newPoints[endPointIndex].isSame(this.points[endPointIndex])) {
|
|
560
|
+
return stateChanges;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
stateChanges.push(new UpdateState(this, { points: newPoints }));
|
|
564
|
+
return stateChanges;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Trim the entity
|
|
569
|
+
* @param {Array} intersections - array of intersection points
|
|
570
|
+
* @return {Array} - array of state changes
|
|
571
|
+
*/
|
|
572
|
+
trim(intersections) {
|
|
573
|
+
const stateChanges = [];
|
|
574
|
+
|
|
575
|
+
if (!intersections?.length) {
|
|
576
|
+
return stateChanges;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const mousePosition = DesignCore.Mouse.pointOnScene();
|
|
580
|
+
|
|
581
|
+
// Find the segment index closest to the mouse
|
|
582
|
+
let mouseSegmentIndex = 0;
|
|
583
|
+
let minDistance = Infinity;
|
|
584
|
+
|
|
585
|
+
for (let i = 1; i < this.points.length; i++) {
|
|
586
|
+
const closestPoint = this.closestPointOnSegment(mousePosition, i);
|
|
587
|
+
if (closestPoint) {
|
|
588
|
+
const dist = mousePosition.distance(closestPoint);
|
|
589
|
+
if (dist < minDistance) {
|
|
590
|
+
minDistance = dist;
|
|
591
|
+
mouseSegmentIndex = i;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Filter out intersections that coincide with existing polyline vertices
|
|
597
|
+
const filteredIntersections = intersections.filter((point) => !this.points.some((vertex) => vertex.isSame(point)));
|
|
598
|
+
|
|
599
|
+
if (!filteredIntersections.length) {
|
|
600
|
+
return stateChanges;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Locate each intersection on its segment with an ordering value
|
|
604
|
+
const locatedIntersections = [];
|
|
605
|
+
|
|
606
|
+
for (const point of filteredIntersections) {
|
|
607
|
+
for (let i = 1; i < this.points.length; i++) {
|
|
608
|
+
if (this.isPointOnSegment(point, this.points[i - 1], this.points[i])) {
|
|
609
|
+
locatedIntersections.push({
|
|
610
|
+
segmentIndex: i,
|
|
611
|
+
point: point,
|
|
612
|
+
positionAlongSegment: this.positionOnSegment(point, i),
|
|
613
|
+
});
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (!locatedIntersections.length) {
|
|
620
|
+
return stateChanges;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Sort by segment index, then by position along segment
|
|
624
|
+
locatedIntersections.sort((a, b) => {
|
|
625
|
+
if (a.segmentIndex !== b.segmentIndex) return a.segmentIndex - b.segmentIndex;
|
|
626
|
+
return a.positionAlongSegment - b.positionAlongSegment;
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
// Precompute the mouse position along its segment
|
|
630
|
+
const mouseClosest = this.closestPointOnSegment(mousePosition, mouseSegmentIndex);
|
|
631
|
+
const mousePos = mouseClosest ? this.positionOnSegment(mouseClosest, mouseSegmentIndex) : 0;
|
|
632
|
+
|
|
633
|
+
// Find the nearest intersections that bracket the mouse segment
|
|
634
|
+
// - trimBefore: the last intersection at or before the mouse
|
|
635
|
+
// - trimAfter: the first intersection at or after the mouse
|
|
636
|
+
let trimBefore = null;
|
|
637
|
+
let trimAfter = null;
|
|
638
|
+
|
|
639
|
+
for (const loc of locatedIntersections) {
|
|
640
|
+
if (loc.segmentIndex < mouseSegmentIndex) {
|
|
641
|
+
trimBefore = loc;
|
|
642
|
+
} else if (loc.segmentIndex === mouseSegmentIndex) {
|
|
643
|
+
if (loc.positionAlongSegment <= mousePos) {
|
|
644
|
+
trimBefore = loc;
|
|
645
|
+
} else if (!trimAfter) {
|
|
646
|
+
trimAfter = loc;
|
|
647
|
+
}
|
|
648
|
+
} else if (!trimAfter) {
|
|
649
|
+
trimAfter = loc;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Build new polyline(s) from the remaining portions
|
|
654
|
+
// Portion 1: start of polyline to trimBefore point
|
|
655
|
+
if (trimBefore) {
|
|
656
|
+
const points = [];
|
|
657
|
+
|
|
658
|
+
for (let i = 0; i < trimBefore.segmentIndex - 1; i++) {
|
|
659
|
+
points.push(this.points[i].clone());
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Add the segment start point (with adjusted bulge for arcs)
|
|
663
|
+
const segStart = this.points[trimBefore.segmentIndex - 1];
|
|
664
|
+
const segStartClone = segStart.clone();
|
|
665
|
+
if (segStart.bulge !== 0) {
|
|
666
|
+
segStartClone.bulge = segStart.partialBulge(this.points[trimBefore.segmentIndex], trimBefore.point);
|
|
667
|
+
}
|
|
668
|
+
points.push(segStartClone);
|
|
669
|
+
|
|
670
|
+
// Add the trim point (skip if it coincides with the last added point)
|
|
671
|
+
const trimPoint = trimBefore.point.clone();
|
|
672
|
+
if (!points.at(-1).isSame(trimPoint)) {
|
|
673
|
+
points.push(trimPoint);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (points.length >= 2) {
|
|
677
|
+
const polyline = Utils.cloneObject(this);
|
|
678
|
+
polyline.points = points;
|
|
679
|
+
polyline.flags.removeValue(1); // clear closed bit
|
|
680
|
+
stateChanges.push(new AddState(polyline));
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Portion 2: trimAfter point to end of polyline
|
|
685
|
+
if (trimAfter) {
|
|
686
|
+
const points = [];
|
|
687
|
+
const trimPoint = trimAfter.point.clone();
|
|
688
|
+
|
|
689
|
+
// If the segment is an arc, set remaining arc bulge
|
|
690
|
+
const segStart = this.points[trimAfter.segmentIndex - 1];
|
|
691
|
+
if (segStart.bulge !== 0) {
|
|
692
|
+
trimPoint.bulge = segStart.partialBulge(this.points[trimAfter.segmentIndex], trimPoint, true);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
points.push(trimPoint);
|
|
696
|
+
|
|
697
|
+
// Add remaining points (skip if coincides with the trim point)
|
|
698
|
+
for (let i = trimAfter.segmentIndex; i < this.points.length; i++) {
|
|
699
|
+
const nextPoint = this.points[i].clone();
|
|
700
|
+
if (!points.at(-1).isSame(nextPoint)) {
|
|
701
|
+
points.push(nextPoint);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (points.length >= 2) {
|
|
706
|
+
const polyline = Utils.cloneObject(this);
|
|
707
|
+
polyline.points = points;
|
|
708
|
+
polyline.flags.removeValue(1); // clear closed bit
|
|
709
|
+
stateChanges.push(new AddState(polyline));
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (trimBefore || trimAfter) {
|
|
714
|
+
stateChanges.push(new RemoveState(this));
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return stateChanges;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Return boundingbox for entity
|
|
722
|
+
* @return {BoundingBox}
|
|
723
|
+
*/
|
|
268
724
|
boundingBox() {
|
|
269
|
-
let xmin
|
|
725
|
+
let xmin = Infinity;
|
|
726
|
+
let xmax = -Infinity;
|
|
727
|
+
let ymin = Infinity;
|
|
728
|
+
let ymax = -Infinity;
|
|
270
729
|
|
|
271
730
|
for (let i = 0; i < this.points.length; i++) {
|
|
272
731
|
const nextPoint = this.points[i + 1] || this.points[0];
|
|
@@ -278,10 +737,10 @@ export class BasePolyline extends Entity {
|
|
|
278
737
|
boundingBox = BoundingBox.arcBoundingBox(centerPoint, this.points[i], nextPoint, this.points[i].bulge);
|
|
279
738
|
}
|
|
280
739
|
|
|
281
|
-
xmin = Math.min(xmin
|
|
282
|
-
xmax = Math.max(xmax
|
|
283
|
-
ymin = Math.min(ymin
|
|
284
|
-
ymax = Math.max(ymax
|
|
740
|
+
xmin = Math.min(xmin, boundingBox.xMin);
|
|
741
|
+
xmax = Math.max(xmax, boundingBox.xMax);
|
|
742
|
+
ymin = Math.min(ymin, boundingBox.yMin);
|
|
743
|
+
ymax = Math.max(ymax, boundingBox.yMax);
|
|
285
744
|
}
|
|
286
745
|
|
|
287
746
|
|
|
@@ -293,8 +752,8 @@ export class BasePolyline extends Entity {
|
|
|
293
752
|
|
|
294
753
|
/**
|
|
295
754
|
* Get the bulge value from the previous segment and the selected point
|
|
296
|
-
* @param {
|
|
297
|
-
* @
|
|
755
|
+
* @param {Point} point
|
|
756
|
+
* @return {number} polyline bulge value
|
|
298
757
|
*/
|
|
299
758
|
getBulgeFromSegment(point) {
|
|
300
759
|
const lastSegBulge = this.points.at(-2).bulge;
|
|
@@ -307,14 +766,14 @@ export class BasePolyline extends Entity {
|
|
|
307
766
|
// angle is perpendicular to the center-to-end point ray, in the direction of the bulge
|
|
308
767
|
lastSegAngle = centerPoint.angle(this.points.at(-1)) + (Math.PI / 2) * Math.sign(lastSegBulge);
|
|
309
768
|
} else {
|
|
310
|
-
// line segment angle is the angle from point 0 to
|
|
769
|
+
// line segment angle is the angle from point 0 to point 1
|
|
311
770
|
lastSegAngle = this.points.at(-2).angle(this.points.at(-1));
|
|
312
771
|
}
|
|
313
772
|
|
|
314
773
|
const mouseAngle = this.points.at(-1).angle(point) % (2 * Math.PI);
|
|
315
774
|
// get the angle delta between point and the previous segment
|
|
316
775
|
// ensure that the angle is always less than 2 * Math.PI
|
|
317
|
-
const angleDelta = ((mouseAngle - lastSegAngle) + 3 * Math.PI) % (2*Math.PI) - Math.PI;
|
|
776
|
+
const angleDelta = ((mouseAngle - lastSegAngle) + 3 * Math.PI) % (2 * Math.PI) - Math.PI;
|
|
318
777
|
// angleDelta is 1/2 the included angle
|
|
319
778
|
// bulge is tan of (included angle * 0.25)
|
|
320
779
|
const bulge = Math.tan((angleDelta * 2) / 4);
|