@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
package/core/lib/intersect.js
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
|
-
import {Point} from '../entities/point.js';
|
|
1
|
+
import { Point } from '../entities/point.js';
|
|
2
|
+
import { Constants } from './constants.js';
|
|
3
|
+
import { Utils } from './utils.js';
|
|
2
4
|
|
|
5
|
+
/** Intersection Class */
|
|
3
6
|
export class Intersection {
|
|
7
|
+
/**
|
|
8
|
+
* Enumeration of all possible intersection status values.
|
|
9
|
+
* Use these constants instead of magic strings when comparing or setting
|
|
10
|
+
* `Intersection#status`.
|
|
11
|
+
*/
|
|
12
|
+
static Status = {
|
|
13
|
+
/** The geometries do not intersect. */
|
|
14
|
+
NONE: 'None',
|
|
15
|
+
/** The geometries intersect at one or more discrete points (arc/circle/polyline aggregate). */
|
|
16
|
+
INTERSECTION: 'Intersection',
|
|
17
|
+
/** Two line segments cross in their interiors (both intersection parameters strictly inside [0,1]). */
|
|
18
|
+
CROSSING: 'Crossing',
|
|
19
|
+
/** Two line segments cross in their interiors at exactly 90°. */
|
|
20
|
+
PERPENDICULAR: 'Perpendicular',
|
|
21
|
+
/** An endpoint of one segment lies in the interior of the other segment (T-intersection). */
|
|
22
|
+
TOUCHING: 'Touching',
|
|
23
|
+
/** The segments share an endpoint exactly. */
|
|
24
|
+
ENDPOINT: 'Endpoint',
|
|
25
|
+
/** The geometries are collinear and their intervals overlap (line-line). */
|
|
26
|
+
OVERLAPPING: 'Overlapping',
|
|
27
|
+
/** The geometries are collinear / coincident (used for concentric equal-radius arcs/circles). */
|
|
28
|
+
COINCIDENT: 'Coincident',
|
|
29
|
+
/** The geometries are parallel but distinct. */
|
|
30
|
+
PARALLEL: 'Parallel',
|
|
31
|
+
/** The geometries are tangent (touch at exactly one point). */
|
|
32
|
+
TANGENT: 'Tangent',
|
|
33
|
+
/** One geometry is fully inside the other with no intersection points. */
|
|
34
|
+
INSIDE: 'Inside',
|
|
35
|
+
/** The geometries are entirely separate with one outside the other. */
|
|
36
|
+
OUTSIDE: 'Outside',
|
|
37
|
+
};
|
|
38
|
+
|
|
4
39
|
/**
|
|
5
40
|
* Intersection constructor
|
|
6
41
|
* @param {string} status - defined status of the intersection i.e inside or outside of shape
|
|
@@ -12,26 +47,144 @@ export class Intersection {
|
|
|
12
47
|
|
|
13
48
|
/**
|
|
14
49
|
* Append point to suggested intersecting points
|
|
15
|
-
* @param {
|
|
50
|
+
* @param {Point} point
|
|
16
51
|
*/
|
|
17
52
|
appendPoint(point) {
|
|
53
|
+
// check if point already exists in the array
|
|
54
|
+
for (let i = 0; i < this.points.length; i++) {
|
|
55
|
+
if (this.points[i].isSame(point)) {
|
|
56
|
+
// point already exists - return
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// push point to points if it doesn't exist
|
|
18
62
|
this.points.push(point);
|
|
19
63
|
};
|
|
20
64
|
|
|
21
65
|
/**
|
|
22
66
|
* Append points to suggested intersecting points
|
|
23
|
-
* @param {
|
|
67
|
+
* @param {Array} points
|
|
24
68
|
*/
|
|
25
69
|
appendPoints(points) {
|
|
26
|
-
|
|
70
|
+
// Forward all points through appendPoint
|
|
71
|
+
points.forEach((point) => {
|
|
72
|
+
this.appendPoint(point);
|
|
73
|
+
});
|
|
27
74
|
};
|
|
28
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Find intersections between two polylines.
|
|
78
|
+
* Aggregates all segment-level results and collapses the status to a binary
|
|
79
|
+
* outcome: {@link Intersection.Status.NONE} or {@link Intersection.Status.INTERSECTION}.
|
|
80
|
+
* Use this method when you only need to know whether any intersection exists
|
|
81
|
+
* (e.g. selection / touch tests). For granular status detail (CROSSING, TOUCHING,
|
|
82
|
+
* ENDPOINT, OVERLAPPING, etc.) call {@link intersectSegmentSegment} directly.
|
|
83
|
+
* @param {Array} points1 - boundary points array
|
|
84
|
+
* @param {Array} points2 - selected points array
|
|
85
|
+
* @param {boolean} extend - extend the selected entity
|
|
86
|
+
* @return {Intersection}
|
|
87
|
+
*/
|
|
88
|
+
static intersectPolylinePolyline(points1, points2, extend) {
|
|
89
|
+
const result = new Intersection(Intersection.Status.NONE);
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < points1.length - 1; i++) {
|
|
92
|
+
for (let j = 0; j < points2.length - 1; j++) {
|
|
93
|
+
const inter = this.intersectSegmentSegment(
|
|
94
|
+
points1[i], points1[i + 1],
|
|
95
|
+
points2[j], points2[j + 1],
|
|
96
|
+
false, extend,
|
|
97
|
+
);
|
|
98
|
+
result.appendPoints(inter.points);
|
|
99
|
+
// COINCIDENT (identical concentric circles) produces no discrete points,
|
|
100
|
+
// so it must be promoted to INTERSECTION explicitly.
|
|
101
|
+
if (inter.status === Intersection.Status.COINCIDENT) {
|
|
102
|
+
result.status = Intersection.Status.INTERSECTION;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (result.points.length > 0) result.status = Intersection.Status.INTERSECTION;
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Find intersections between two polyline segments.
|
|
113
|
+
* Returns the full granular status (CROSSING, PERPENDICULAR, TOUCHING, ENDPOINT,
|
|
114
|
+
* OVERLAPPING, TANGENT, etc.) needed by geometric operations such as trim and extend.
|
|
115
|
+
* @param {Point} b1 - segment one start point
|
|
116
|
+
* @param {Point} b2 - segment one end point
|
|
117
|
+
* @param {Point} b3 - segment two start point
|
|
118
|
+
* @param {Point} b4 - segment two end point
|
|
119
|
+
* @param {boolean} extendSegOne - extend segment one to infinite form
|
|
120
|
+
* @param {boolean} extendSegTwo - extend segment two to infinite form
|
|
121
|
+
* @return {Intersection}
|
|
122
|
+
*/
|
|
123
|
+
static intersectSegmentSegment(b1, b2, b3, b4, extendSegOne, extendSegTwo) {
|
|
124
|
+
const seg1IsArc = b1.bulge !== 0 && b1.bulge !== undefined;
|
|
125
|
+
const seg2IsArc = b3.bulge !== 0 && b3.bulge !== undefined;
|
|
126
|
+
|
|
127
|
+
// line vs line
|
|
128
|
+
if (!seg1IsArc && !seg2IsArc) {
|
|
129
|
+
return this.#intersectLineLine({ start: b1, end: b2 }, { start: b3, end: b4 }, extendSegOne, extendSegTwo);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const arc1 = seg1IsArc ? this.#buildArc(b1, b2) : null;
|
|
133
|
+
const arc2 = seg2IsArc ? this.#buildArc(b3, b4) : null;
|
|
134
|
+
|
|
135
|
+
let candidatePoints;
|
|
136
|
+
let innerStatus;
|
|
137
|
+
|
|
138
|
+
if (seg1IsArc && seg2IsArc) {
|
|
139
|
+
// arc vs arc
|
|
140
|
+
const inter = this.intersectCircleCircle(arc1, arc2);
|
|
141
|
+
candidatePoints = inter.points;
|
|
142
|
+
innerStatus = inter.status;
|
|
143
|
+
} else if (seg1IsArc) {
|
|
144
|
+
// arc(seg1) vs line(seg2)
|
|
145
|
+
const inter = this.intersectCircleLine(arc1, { start: b3, end: b4 }, extendSegTwo);
|
|
146
|
+
candidatePoints = inter.points;
|
|
147
|
+
innerStatus = inter.status;
|
|
148
|
+
} else {
|
|
149
|
+
// line(seg1) vs arc(seg2)
|
|
150
|
+
const inter = this.intersectCircleLine(arc2, { start: b1, end: b2 }, extendSegOne);
|
|
151
|
+
candidatePoints = inter.points;
|
|
152
|
+
innerStatus = inter.status;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = new Intersection(innerStatus || Intersection.Status.NONE);
|
|
156
|
+
|
|
157
|
+
for (let i = 0; i < candidatePoints.length; i++) {
|
|
158
|
+
const pt = candidatePoints[i];
|
|
159
|
+
let valid = true;
|
|
160
|
+
|
|
161
|
+
// seg1 arc filter - skipped when extending seg1
|
|
162
|
+
if (seg1IsArc && !extendSegOne && !pt.isOnArc(arc1.startPoint, arc1.endPoint, arc1.centre, arc1.direction)) {
|
|
163
|
+
valid = false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// seg2 arc filter - skipped when extending seg2
|
|
167
|
+
if (valid && seg2IsArc && !extendSegTwo) {
|
|
168
|
+
if (!pt.isOnArc(arc2.startPoint, arc2.endPoint, arc2.centre, arc2.direction)) {
|
|
169
|
+
valid = false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (valid) {
|
|
174
|
+
result.appendPoint(pt);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (result.points.length > 0 && result.status !== Intersection.Status.TANGENT) result.status = Intersection.Status.INTERSECTION;
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
29
182
|
/**
|
|
30
183
|
* Find intersections between circle and line
|
|
31
|
-
* @param {
|
|
32
|
-
* @param {
|
|
184
|
+
* @param {Circle} circle
|
|
185
|
+
* @param {Line} line
|
|
33
186
|
* @param {boolean} extend - extend the line as a ray
|
|
34
|
-
* @
|
|
187
|
+
* @return {Intersect}
|
|
35
188
|
*/
|
|
36
189
|
static intersectCircleLine(circle, line, extend) {
|
|
37
190
|
const c = circle.centre;
|
|
@@ -42,47 +195,49 @@ export class Intersection {
|
|
|
42
195
|
|
|
43
196
|
|
|
44
197
|
let result;
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
198
|
+
const lineDir = a2.subtract(a1); // direction vector of the line segment
|
|
199
|
+
const centreToStart = a1.subtract(c); // vector from circle centre to line start
|
|
200
|
+
const quadA = lineDir.dot(lineDir); // squared length of the line segment
|
|
201
|
+
const quadB = 2 * lineDir.dot(centreToStart); // linear coefficient (projection term)
|
|
202
|
+
const quadC = centreToStart.dot(centreToStart) - r * r; // constant coefficient (signed distance²)
|
|
203
|
+
const discriminant = quadB * quadB - 4 * quadA * quadC;
|
|
204
|
+
|
|
205
|
+
if (discriminant < 0) {
|
|
206
|
+
result = new Intersection(Intersection.Status.OUTSIDE);
|
|
207
|
+
} else if (discriminant === 0) {
|
|
208
|
+
result = new Intersection(Intersection.Status.TANGENT);
|
|
209
|
+
const u = -quadB / (2 * quadA);
|
|
210
|
+
if (0 <= u && u <= 1 || extend) {
|
|
211
|
+
result.appendPoint(a1.lerp(a2, u));
|
|
212
|
+
}
|
|
58
213
|
} else {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const
|
|
214
|
+
const sqrtDiscriminant = Math.sqrt(discriminant); // square root of discriminant — half-spread between the two roots
|
|
215
|
+
const lineParam1 = (-quadB + sqrtDiscriminant) / (2 * quadA); // parametric t for first intersection (0 = line start, 1 = line end)
|
|
216
|
+
const lineParam2 = (-quadB - sqrtDiscriminant) / (2 * quadA); // parametric t for second intersection (0 = line start, 1 = line end)
|
|
62
217
|
|
|
63
|
-
if ((
|
|
64
|
-
if ((
|
|
65
|
-
result = new Intersection(
|
|
218
|
+
if ((lineParam1 < 0 || lineParam1 > 1) && (lineParam2 < 0 || lineParam2 > 1)) {
|
|
219
|
+
if ((lineParam1 < 0 && lineParam2 < 0) || (lineParam1 > 1 && lineParam2 > 1)) {
|
|
220
|
+
result = new Intersection(Intersection.Status.OUTSIDE);
|
|
66
221
|
if (extend) {
|
|
67
|
-
result.
|
|
68
|
-
result.
|
|
222
|
+
result.appendPoint(a1.lerp(a2, lineParam1));
|
|
223
|
+
result.appendPoint(a1.lerp(a2, lineParam2));
|
|
69
224
|
}
|
|
70
225
|
} else {
|
|
71
|
-
result = new Intersection(
|
|
226
|
+
result = new Intersection(Intersection.Status.INSIDE);
|
|
72
227
|
if (extend) {
|
|
73
|
-
result.
|
|
74
|
-
result.
|
|
228
|
+
result.appendPoint(a1.lerp(a2, lineParam1));
|
|
229
|
+
result.appendPoint(a1.lerp(a2, lineParam2));
|
|
75
230
|
}
|
|
76
231
|
}
|
|
77
232
|
} else {
|
|
78
|
-
result = new Intersection(
|
|
233
|
+
result = new Intersection(Intersection.Status.INTERSECTION);
|
|
79
234
|
|
|
80
|
-
if (0 <=
|
|
81
|
-
result.
|
|
235
|
+
if (0 <= lineParam1 && lineParam1 <= 1 || extend) {
|
|
236
|
+
result.appendPoint(a1.lerp(a2, lineParam1));
|
|
82
237
|
}
|
|
83
238
|
|
|
84
|
-
if (0 <=
|
|
85
|
-
result.
|
|
239
|
+
if (0 <= lineParam2 && lineParam2 <= 1 || extend) {
|
|
240
|
+
result.appendPoint(a1.lerp(a2, lineParam2));
|
|
86
241
|
}
|
|
87
242
|
}
|
|
88
243
|
}
|
|
@@ -90,23 +245,12 @@ export class Intersection {
|
|
|
90
245
|
return result;
|
|
91
246
|
};
|
|
92
247
|
|
|
93
|
-
/**
|
|
94
|
-
* Find intersections between circle and line
|
|
95
|
-
* @param {line} line
|
|
96
|
-
* @param {circle} circle
|
|
97
|
-
* @param {boolean} extend - extend the line as a ray
|
|
98
|
-
* @returns
|
|
99
|
-
*/
|
|
100
|
-
static intersectLineCircle(line, circle, extend) {
|
|
101
|
-
return this.intersectCircleLine(circle, line, extend);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
248
|
/**
|
|
105
249
|
* Find intersections between two circles
|
|
106
|
-
* @param {
|
|
107
|
-
* @param {
|
|
250
|
+
* @param {Circle} circle1
|
|
251
|
+
* @param {Circle} circle2
|
|
108
252
|
* @param {boolean} extend - unused
|
|
109
|
-
* @
|
|
253
|
+
* @return {Intersect}
|
|
110
254
|
*/
|
|
111
255
|
static intersectCircleCircle(circle1, circle2, extend) {
|
|
112
256
|
const c1 = circle1.centre;
|
|
@@ -122,457 +266,186 @@ export class Intersection {
|
|
|
122
266
|
// Determine actual distance between circle circles
|
|
123
267
|
const cDist = c1.distance(c2);
|
|
124
268
|
|
|
125
|
-
if (cDist
|
|
126
|
-
result = new Intersection(
|
|
269
|
+
if (cDist === 0) {
|
|
270
|
+
result = new Intersection(r1 === r2 ? Intersection.Status.COINCIDENT : Intersection.Status.INSIDE);
|
|
271
|
+
} else if (cDist > rMax) {
|
|
272
|
+
result = new Intersection(Intersection.Status.OUTSIDE);
|
|
127
273
|
} else if (cDist < rMin) {
|
|
128
|
-
result = new Intersection(
|
|
274
|
+
result = new Intersection(Intersection.Status.INSIDE);
|
|
129
275
|
} else {
|
|
130
|
-
result = new Intersection(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
result.
|
|
144
|
-
|
|
145
|
-
p.x + b * (c2.y - c1.y),
|
|
146
|
-
p.y - b * (c2.x - c1.x),
|
|
147
|
-
),
|
|
148
|
-
);
|
|
276
|
+
result = new Intersection(Intersection.Status.INTERSECTION);
|
|
277
|
+
|
|
278
|
+
// Distance from c1 to the radical axis along the line of centres
|
|
279
|
+
const axialDist = (r1 * r1 - r2 * r2 + cDist * cDist) / (2 * cDist);
|
|
280
|
+
// Half the length of the chord connecting the two intersection points
|
|
281
|
+
const halfChordLength = Math.sqrt(r1 * r1 - axialDist * axialDist);
|
|
282
|
+
// Point on the line of centres where the perpendicular bisector of the chord passes through
|
|
283
|
+
const chordMidpoint = c1.lerp(c2, axialDist / cDist);
|
|
284
|
+
// Normalised scale factor for the perpendicular offset
|
|
285
|
+
const perpScale = halfChordLength / cDist;
|
|
286
|
+
// Perpendicular offset vector from chordMidpoint to each intersection point
|
|
287
|
+
const perpOffset = new Point(-(c2.y - c1.y), c2.x - c1.x).scale(perpScale);
|
|
288
|
+
|
|
289
|
+
result.appendPoint(chordMidpoint.add(perpOffset));
|
|
290
|
+
result.appendPoint(chordMidpoint.subtract(perpOffset));
|
|
149
291
|
}
|
|
150
292
|
|
|
151
293
|
return result;
|
|
152
294
|
};
|
|
153
295
|
|
|
154
296
|
/**
|
|
155
|
-
*
|
|
156
|
-
* @param {
|
|
157
|
-
* @param {
|
|
158
|
-
* @
|
|
159
|
-
* @returns
|
|
297
|
+
* Build arc object from two polyline points
|
|
298
|
+
* @param {Point} b1 - start point with bulge
|
|
299
|
+
* @param {Point} b2 - end point
|
|
300
|
+
* @return {Object} arc with centre, startPoint, endPoint, radius, direction
|
|
160
301
|
*/
|
|
161
|
-
static
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
let rectPoints = {start: min, end: topRight};
|
|
172
|
-
const inter1 = this.intersectArcLine(arc, rectPoints, extend);
|
|
173
|
-
|
|
174
|
-
rectPoints = {start: topRight, end: max};
|
|
175
|
-
const inter2 = this.intersectArcLine(arc, rectPoints, extend);
|
|
176
|
-
|
|
177
|
-
rectPoints = {start: max, end: bottomLeft};
|
|
178
|
-
const inter3 = this.intersectArcLine(arc, rectPoints, extend);
|
|
179
|
-
|
|
180
|
-
rectPoints = {start: bottomLeft, end: min};
|
|
181
|
-
const inter4 = this.intersectArcLine(arc, rectPoints, extend);
|
|
182
|
-
|
|
183
|
-
const result = new Intersection('No Intersection');
|
|
184
|
-
|
|
185
|
-
result.appendPoints(inter1.points);
|
|
186
|
-
result.appendPoints(inter2.points);
|
|
187
|
-
result.appendPoints(inter3.points);
|
|
188
|
-
result.appendPoints(inter4.points);
|
|
189
|
-
|
|
190
|
-
if (result.points.length > 0) {
|
|
191
|
-
result.status = 'Intersection';
|
|
192
|
-
} else {
|
|
193
|
-
result.status = inter1.status;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return result;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Find intersections between arc segment and line
|
|
201
|
-
* @param {arc} arc
|
|
202
|
-
* @param {line} line
|
|
203
|
-
* @param {boolean} extend
|
|
204
|
-
* @returns
|
|
205
|
-
*/
|
|
206
|
-
static intersectArcLine(arc, line, extend) {
|
|
207
|
-
const inter1 = this.intersectCircleLine(arc, line, extend);
|
|
208
|
-
const result = new Intersection('No Intersection');
|
|
209
|
-
|
|
210
|
-
if (!extend) {
|
|
211
|
-
for (let i = 0; i < inter1.points.length; i++) {
|
|
212
|
-
if (inter1.points[i].isOnArc(arc.startPoint, arc.endPoint, arc.centre, arc.direction)) {
|
|
213
|
-
result.appendPoints(inter1.points[i]);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (result.points.length > 0) {
|
|
218
|
-
result.status = 'Intersection';
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return result;
|
|
302
|
+
static #buildArc(b1, b2) {
|
|
303
|
+
const arc = {};
|
|
304
|
+
arc.centre = b1.bulgeCentrePoint(b2);
|
|
305
|
+
arc.startPoint = b1;
|
|
306
|
+
arc.endPoint = b2;
|
|
307
|
+
arc.radius = arc.centre.distance(b1);
|
|
308
|
+
arc.direction = b1.bulge;
|
|
309
|
+
return arc;
|
|
223
310
|
}
|
|
224
311
|
|
|
225
312
|
/**
|
|
226
|
-
* Find intersections between
|
|
227
|
-
* @param {
|
|
228
|
-
* @param {
|
|
229
|
-
* @param {boolean}
|
|
230
|
-
* @
|
|
313
|
+
* Find intersections between two lines
|
|
314
|
+
* @param {Line} line1
|
|
315
|
+
* @param {Line} line2
|
|
316
|
+
* @param {boolean} extendSegOne
|
|
317
|
+
* @param {boolean} extendSegTwo
|
|
318
|
+
* @return {Intersection}
|
|
231
319
|
*/
|
|
232
|
-
static
|
|
233
|
-
|
|
234
|
-
|
|
320
|
+
static #intersectLineLine(line1, line2, extendSegOne, extendSegTwo) {
|
|
321
|
+
const aStart = line1.start;
|
|
322
|
+
const aEnd = line1.end;
|
|
323
|
+
const bStart = line2.start;
|
|
324
|
+
const bEnd = line2.end;
|
|
325
|
+
extendSegOne = extendSegOne || false;
|
|
326
|
+
extendSegTwo = extendSegTwo || false;
|
|
235
327
|
|
|
236
|
-
|
|
237
|
-
* Find intersections between circle and arc segment
|
|
238
|
-
* @param {circle} circle
|
|
239
|
-
* @param {arc} arc
|
|
240
|
-
* @param {boolean} extend
|
|
241
|
-
* @returns
|
|
242
|
-
*/
|
|
243
|
-
static intersectCircleArc(circle, arc, extend) {
|
|
244
|
-
const inter1 = this.intersectCircleCircle(circle, arc, extend);
|
|
245
|
-
const result = new Intersection('No Intersection');
|
|
246
|
-
|
|
247
|
-
if (!extend) {
|
|
248
|
-
for (let i = 0; i < inter1.points.length; i++) {
|
|
249
|
-
if (inter1.points[i].isOnArc(arc.startPoint, arc.endPoint, arc.centre, arc.direction)) {
|
|
250
|
-
result.points.push(inter1.points[i]);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
328
|
+
let result;
|
|
253
329
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
330
|
+
// Check if any endpoints are coincident
|
|
331
|
+
if (aStart.isSame(bStart)) {
|
|
332
|
+
result = new Intersection(Intersection.Status.ENDPOINT);
|
|
333
|
+
result.appendPoint(new Point(aStart.x, aStart.y));
|
|
334
|
+
return result;
|
|
257
335
|
}
|
|
258
336
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
* @param {arc} arc
|
|
265
|
-
* @param {circle} circle
|
|
266
|
-
* @param {boolean} extend
|
|
267
|
-
* @returns
|
|
268
|
-
*/
|
|
269
|
-
static intersectArcCircle(arc, circle, extend) {
|
|
270
|
-
return this.intersectCircleArc(circle, arc, extend);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Find intersections between circle and rectangle
|
|
275
|
-
* @param {circle} circle
|
|
276
|
-
* @param {rectangle} rectangle
|
|
277
|
-
* @param {boolean} extend
|
|
278
|
-
* @returns
|
|
279
|
-
*/
|
|
280
|
-
static intersectCircleRectangle(circle, rectangle, extend) {
|
|
281
|
-
const r1 = rectangle.start;
|
|
282
|
-
const r2 = rectangle.end;
|
|
283
|
-
extend = extend || false;
|
|
284
|
-
|
|
285
|
-
const min = r1.min(r2);
|
|
286
|
-
const max = r1.max(r2);
|
|
287
|
-
const topRight = new Point(max.x, min.y);
|
|
288
|
-
const bottomLeft = new Point(min.x, max.y);
|
|
289
|
-
|
|
290
|
-
let rectPoints = {start: min, end: topRight};
|
|
291
|
-
const inter1 = this.intersectCircleLine(circle, rectPoints);
|
|
292
|
-
|
|
293
|
-
rectPoints = {start: topRight, end: max};
|
|
294
|
-
const inter2 = this.intersectCircleLine(circle, rectPoints);
|
|
295
|
-
|
|
296
|
-
rectPoints = {start: max, end: bottomLeft};
|
|
297
|
-
const inter3 = this.intersectCircleLine(circle, rectPoints);
|
|
337
|
+
if (aStart.isSame(bEnd)) {
|
|
338
|
+
result = new Intersection(Intersection.Status.ENDPOINT);
|
|
339
|
+
result.appendPoint(new Point(aStart.x, aStart.y));
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
298
342
|
|
|
299
|
-
|
|
300
|
-
|
|
343
|
+
if (aEnd.isSame(bStart)) {
|
|
344
|
+
result = new Intersection(Intersection.Status.ENDPOINT);
|
|
345
|
+
result.appendPoint(new Point(aEnd.x, aEnd.y));
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
301
348
|
|
|
302
|
-
|
|
349
|
+
if (aEnd.isSame(bEnd)) {
|
|
350
|
+
result = new Intersection(Intersection.Status.ENDPOINT);
|
|
351
|
+
result.appendPoint(new Point(aEnd.x, aEnd.y));
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
303
354
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
355
|
+
// Check if any endpoints of one line are on the other line
|
|
356
|
+
if (aStart.isOnLine(bStart, bEnd)) {
|
|
357
|
+
result = new Intersection(Intersection.Status.TOUCHING);
|
|
358
|
+
result.appendPoint(new Point(aStart.x, aStart.y));
|
|
359
|
+
return result;
|
|
360
|
+
}
|
|
308
361
|
|
|
309
|
-
if (
|
|
310
|
-
result
|
|
311
|
-
|
|
312
|
-
result
|
|
362
|
+
if (aEnd.isOnLine(bStart, bEnd)) {
|
|
363
|
+
result = new Intersection(Intersection.Status.TOUCHING);
|
|
364
|
+
result.appendPoint(new Point(aEnd.x, aEnd.y));
|
|
365
|
+
return result;
|
|
313
366
|
}
|
|
314
367
|
|
|
315
|
-
|
|
316
|
-
|
|
368
|
+
if (bStart.isOnLine(aStart, aEnd)) {
|
|
369
|
+
result = new Intersection(Intersection.Status.TOUCHING);
|
|
370
|
+
result.appendPoint(new Point(bStart.x, bStart.y));
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
317
373
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
* @returns
|
|
324
|
-
*/
|
|
325
|
-
static intersectLineLine(line1, line2, extend) {
|
|
326
|
-
const a1 = line1.start;
|
|
327
|
-
const a2 = line1.end;
|
|
328
|
-
const b1 = line2.start;
|
|
329
|
-
const b2 = line2.end;
|
|
330
|
-
extend = extend || false;
|
|
374
|
+
if (bEnd.isOnLine(aStart, aEnd)) {
|
|
375
|
+
result = new Intersection(Intersection.Status.TOUCHING);
|
|
376
|
+
result.appendPoint(new Point(bEnd.x, bEnd.y));
|
|
377
|
+
return result;
|
|
378
|
+
}
|
|
331
379
|
|
|
332
|
-
|
|
380
|
+
// Guard against zero-length (degenerate) segments.
|
|
381
|
+
// Endpoint and touching cases were already handled above; anything remaining
|
|
382
|
+
// means the point simply does not lie on the other segment.
|
|
383
|
+
if (aStart.isSame(aEnd) || bStart.isSame(bEnd)) {
|
|
384
|
+
return new Intersection(Intersection.Status.NONE);
|
|
385
|
+
}
|
|
333
386
|
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
387
|
+
// Direction vectors and offset between start points
|
|
388
|
+
const line1Dir = aEnd.subtract(aStart);
|
|
389
|
+
const line2Dir = bEnd.subtract(bStart);
|
|
390
|
+
const startDiff = aStart.subtract(bStart);
|
|
391
|
+
|
|
392
|
+
// Normalise the cross product to |sin(angle)| so the tolerance is scale-independent
|
|
393
|
+
const directionCross = line1Dir.cross(line2Dir);
|
|
394
|
+
const line1Len = line1Dir.length();
|
|
395
|
+
const line2Len = line2Dir.length();
|
|
396
|
+
const isParallel = Math.abs(directionCross) / (line1Len * line2Len) < Constants.Tolerance.EPSILON;
|
|
397
|
+
|
|
398
|
+
if (!isParallel) {
|
|
399
|
+
// Lines are not parallel
|
|
400
|
+
// lerp parameters: 0 = segment start, 1 = segment end
|
|
401
|
+
const line1Lerp = line2Dir.cross(startDiff) / directionCross;
|
|
402
|
+
const line2Lerp = line1Dir.cross(startDiff) / directionCross;
|
|
403
|
+
|
|
404
|
+
// If both lerp values are between 0 and 1, the intersection is within the line segments
|
|
405
|
+
const line1InRange = 0 <= line1Lerp && line1Lerp <= 1;
|
|
406
|
+
const line2InRange = 0 <= line2Lerp && line2Lerp <= 1;
|
|
407
|
+
const isWithinSegments = line1InRange && line2InRange;
|
|
408
|
+
const isExtended = (line1InRange || extendSegOne) && (line2InRange || extendSegTwo);
|
|
409
|
+
|
|
410
|
+
if (isWithinSegments || isExtended) {
|
|
411
|
+
const isPerpendicular = Utils.round(line1Dir.dot(line2Dir)) === 0;
|
|
412
|
+
result = new Intersection(isPerpendicular ? Intersection.Status.PERPENDICULAR : Intersection.Status.CROSSING);
|
|
413
|
+
result.appendPoint(aStart.lerp(aEnd, line1Lerp));
|
|
350
414
|
} else {
|
|
351
|
-
result = new Intersection(
|
|
415
|
+
result = new Intersection(Intersection.Status.NONE);
|
|
352
416
|
}
|
|
353
417
|
} else {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
* @param {boolean} extend
|
|
380
|
-
* @returns
|
|
381
|
-
*/
|
|
382
|
-
static intersectPolylineLine(polyline, line, extend) {
|
|
383
|
-
const result = new Intersection('No Intersection');
|
|
384
|
-
const length = polyline.points.length;
|
|
385
|
-
|
|
386
|
-
for (let i = 0; i < length - 1; i++) {
|
|
387
|
-
const b1 = polyline.points[i];
|
|
388
|
-
const b2 = polyline.points[(i + 1) % length];
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (b1.bulge === 0) {
|
|
392
|
-
const line2 = {start: b1, end: b2};
|
|
393
|
-
const inter = this.intersectLineLine(line2, line, extend);
|
|
394
|
-
result.appendPoints(inter.points);
|
|
418
|
+
// Lines are parallel or coincident
|
|
419
|
+
const startDiffLen = startDiff.length();
|
|
420
|
+
const isCollinear = startDiffLen < Constants.Tolerance.EPSILON ||
|
|
421
|
+
Math.abs(line2Dir.cross(startDiff)) / (line2Len * startDiffLen) < Constants.Tolerance.EPSILON;
|
|
422
|
+
if (isCollinear) {
|
|
423
|
+
// Lines are collinear — check whether the segments actually overlap
|
|
424
|
+
// Project all four endpoints onto the shared direction axis
|
|
425
|
+
const len2 = line1Dir.x * line1Dir.x + line1Dir.y * line1Dir.y;
|
|
426
|
+
const t0 = 0; // aStart projects to 0
|
|
427
|
+
const t1 = 1; // aEnd projects to 1 (parametric)
|
|
428
|
+
const t2 = (bStart.subtract(aStart).x * line1Dir.x + bStart.subtract(aStart).y * line1Dir.y) / len2;
|
|
429
|
+
const t3 = (bEnd.subtract(aStart).x * line1Dir.x + bEnd.subtract(aStart).y * line1Dir.y) / len2;
|
|
430
|
+
|
|
431
|
+
const bMin = Math.min(t2, t3);
|
|
432
|
+
const bMax = Math.max(t2, t3);
|
|
433
|
+
|
|
434
|
+
if (bMax < t0 || bMin > t1) {
|
|
435
|
+
// Segments are collinear but disjoint
|
|
436
|
+
result = new Intersection(Intersection.Status.NONE);
|
|
437
|
+
} else {
|
|
438
|
+
// Segments overlap — return the two endpoints of the shared interval
|
|
439
|
+
result = new Intersection(Intersection.Status.OVERLAPPING);
|
|
440
|
+
result.appendPoint(aStart.lerp(aEnd, Math.max(t0, bMin)));
|
|
441
|
+
result.appendPoint(aStart.lerp(aEnd, Math.min(t1, bMax)));
|
|
442
|
+
}
|
|
395
443
|
} else {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
arc.startPoint = b1;
|
|
399
|
-
arc.endPoint = b2;
|
|
400
|
-
arc.radius = arc.centre.distance(b1);
|
|
401
|
-
arc.direction = b1.bulge;
|
|
402
|
-
|
|
403
|
-
const interArc = this.intersectArcLine(arc, line, extend);
|
|
404
|
-
result.appendPoints(interArc.points);
|
|
444
|
+
// Lines are parallel but not coincident
|
|
445
|
+
result = new Intersection(Intersection.Status.PARALLEL);
|
|
405
446
|
}
|
|
406
447
|
}
|
|
407
448
|
|
|
408
|
-
if (result.points.length > 0) result.status = 'Intersection';
|
|
409
|
-
return result;
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Find intersections between lwpolyline and rectangle
|
|
414
|
-
* @param {polyline} polyline
|
|
415
|
-
* @param {rectangle} rectangle
|
|
416
|
-
* @param {boolean} extend
|
|
417
|
-
* @returns
|
|
418
|
-
*/
|
|
419
|
-
static intersectLwpolylineRectangle(polyline, rectangle, extend) {
|
|
420
|
-
return this.intersectPolylineRectangle(polyline, rectangle, extend);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* Find intersections between polyline and rectangle
|
|
425
|
-
* @param {polyline} polyline
|
|
426
|
-
* @param {rectangle} rectangle
|
|
427
|
-
* @param {boolean} extend
|
|
428
|
-
* @returns
|
|
429
|
-
*/
|
|
430
|
-
static intersectPolylineRectangle(polyline, rectangle, extend) {
|
|
431
|
-
const r1 = rectangle.start;
|
|
432
|
-
const r2 = rectangle.end;
|
|
433
|
-
extend = extend || false;
|
|
434
|
-
|
|
435
|
-
const min = r1.min(r2);
|
|
436
|
-
const max = r1.max(r2);
|
|
437
|
-
const topRight = new Point(max.x, min.y);
|
|
438
|
-
const bottomLeft = new Point(min.x, max.y);
|
|
439
|
-
|
|
440
|
-
let rectPoints = {start: min, end: topRight};
|
|
441
|
-
const inter1 = this.intersectPolylineLine(polyline, rectPoints, extend);
|
|
442
|
-
|
|
443
|
-
rectPoints = {start: topRight, end: max};
|
|
444
|
-
const inter2 = this.intersectPolylineLine(polyline, rectPoints, extend);
|
|
445
|
-
|
|
446
|
-
rectPoints = {start: max, end: bottomLeft};
|
|
447
|
-
const inter3 = this.intersectPolylineLine(polyline, rectPoints, extend);
|
|
448
|
-
|
|
449
|
-
rectPoints = {start: bottomLeft, end: min};
|
|
450
|
-
const inter4 = this.intersectPolylineLine(polyline, rectPoints, extend);
|
|
451
|
-
|
|
452
|
-
const result = new Intersection('No Intersection');
|
|
453
|
-
|
|
454
|
-
result.appendPoints(inter1.points);
|
|
455
|
-
result.appendPoints(inter2.points);
|
|
456
|
-
result.appendPoints(inter3.points);
|
|
457
|
-
result.appendPoints(inter4.points);
|
|
458
|
-
|
|
459
|
-
if (result.points.length > 0) {
|
|
460
|
-
result.status = 'Intersection';
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
return result;
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Find intersections between line and rectangle
|
|
469
|
-
* @param {line} line
|
|
470
|
-
* @param {rectangle} rectangle
|
|
471
|
-
* @param {boolean} extend
|
|
472
|
-
* @returns
|
|
473
|
-
*/
|
|
474
|
-
static intersectLineRectangle(line, rectangle, extend) {
|
|
475
|
-
const r1 = rectangle.start;
|
|
476
|
-
const r2 = rectangle.end;
|
|
477
|
-
extend = extend || false;
|
|
478
|
-
|
|
479
|
-
const min = r1.min(r2);
|
|
480
|
-
const max = r1.max(r2);
|
|
481
|
-
const topRight = new Point(max.x, min.y);
|
|
482
|
-
const bottomLeft = new Point(min.x, max.y);
|
|
483
|
-
|
|
484
|
-
let rectPoints = {start: min, end: topRight};
|
|
485
|
-
const inter1 = this.intersectLineLine(rectPoints, line, extend);
|
|
486
|
-
|
|
487
|
-
rectPoints = {start: topRight, end: max};
|
|
488
|
-
const inter2 = this.intersectLineLine(rectPoints, line, extend);
|
|
489
|
-
|
|
490
|
-
rectPoints = {start: max, end: bottomLeft};
|
|
491
|
-
const inter3 = this.intersectLineLine(rectPoints, line, extend);
|
|
492
|
-
|
|
493
|
-
rectPoints = {start: bottomLeft, end: min};
|
|
494
|
-
const inter4 = this.intersectLineLine(rectPoints, line, extend);
|
|
495
|
-
|
|
496
|
-
const result = new Intersection('No Intersection');
|
|
497
|
-
|
|
498
|
-
result.appendPoints(inter1.points);
|
|
499
|
-
result.appendPoints(inter2.points);
|
|
500
|
-
result.appendPoints(inter3.points);
|
|
501
|
-
result.appendPoints(inter4.points);
|
|
502
|
-
|
|
503
|
-
if (result.points.length > 0) {
|
|
504
|
-
result.status = 'Intersection';
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
return result;
|
|
508
|
-
};
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Find intersections between rectangle and line
|
|
512
|
-
* @param {rectangle} rectangle
|
|
513
|
-
* @param {line} line
|
|
514
|
-
* @param {boolean} extend
|
|
515
|
-
* @returns
|
|
516
|
-
*/
|
|
517
|
-
static intersectRectangleLine(rectangle, line, extend) {
|
|
518
|
-
return this.intersectLineRectangle(line, rectangle, extend);
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Find intersections between text and rectangles
|
|
523
|
-
* Text is represented by its bounding box
|
|
524
|
-
* @param {text} text
|
|
525
|
-
* @param {rectangle} rectangle
|
|
526
|
-
* @param {boolean} extend
|
|
527
|
-
* @returns
|
|
528
|
-
*/
|
|
529
|
-
static intersectTextRectangle(text, rectangle, extend) {
|
|
530
|
-
return this.intersectRectangleRectangle(text, rectangle, extend);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* Find intersections between two rectangles
|
|
535
|
-
* @param {rectangle} rectangle1
|
|
536
|
-
* @param {rectangle} rectangle2
|
|
537
|
-
* @param {boolean} extend
|
|
538
|
-
* @returns
|
|
539
|
-
*/
|
|
540
|
-
static intersectRectangleRectangle(rectangle1, rectangle2, extend) {
|
|
541
|
-
const a1 = rectangle1.start;
|
|
542
|
-
const a2 = rectangle1.end;
|
|
543
|
-
// const b1 = rectangle2.start;
|
|
544
|
-
// const b2 = rectangle2.end;
|
|
545
|
-
extend = extend || false;
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const min = a1.min(a2);
|
|
549
|
-
const max = a1.max(a2);
|
|
550
|
-
const topRight = new Point(max.x, min.y);
|
|
551
|
-
const bottomLeft = new Point(min.x, max.y);
|
|
552
|
-
|
|
553
|
-
let rectPoints = {start: min, end: topRight};
|
|
554
|
-
const inter1 = this.intersectLineRectangle(rectPoints, rectangle2);
|
|
555
|
-
|
|
556
|
-
rectPoints = {start: topRight, end: max};
|
|
557
|
-
const inter2 = this.intersectLineRectangle(rectPoints, rectangle2);
|
|
558
|
-
|
|
559
|
-
rectPoints = {start: max, end: bottomLeft};
|
|
560
|
-
const inter3 = this.intersectLineRectangle(rectPoints, rectangle2);
|
|
561
|
-
|
|
562
|
-
rectPoints = {start: bottomLeft, end: min};
|
|
563
|
-
const inter4 = this.intersectLineRectangle(rectPoints, rectangle2);
|
|
564
|
-
|
|
565
|
-
const result = new Intersection('No Intersection');
|
|
566
|
-
|
|
567
|
-
result.appendPoints(inter1.points);
|
|
568
|
-
result.appendPoints(inter2.points);
|
|
569
|
-
result.appendPoints(inter3.points);
|
|
570
|
-
result.appendPoints(inter4.points);
|
|
571
|
-
|
|
572
|
-
if (result.points.length > 0) {
|
|
573
|
-
result.status = 'Intersection';
|
|
574
|
-
}
|
|
575
|
-
|
|
576
449
|
return result;
|
|
577
450
|
};
|
|
578
451
|
}
|