@joint/core 4.0.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/LICENSE +376 -0
- package/README.md +49 -0
- package/dist/geometry.js +6486 -0
- package/dist/geometry.min.js +8 -0
- package/dist/joint.d.ts +5536 -0
- package/dist/joint.js +39629 -0
- package/dist/joint.min.js +8 -0
- package/dist/joint.nowrap.js +39626 -0
- package/dist/joint.nowrap.min.js +8 -0
- package/dist/vectorizer.js +9135 -0
- package/dist/vectorizer.min.js +8 -0
- package/dist/version.mjs +3 -0
- package/index.js +3 -0
- package/joint.mjs +27 -0
- package/package.json +192 -0
- package/src/V/annotation.mjs +0 -0
- package/src/V/index.mjs +2642 -0
- package/src/anchors/index.mjs +123 -0
- package/src/config/index.mjs +12 -0
- package/src/connectionPoints/index.mjs +202 -0
- package/src/connectionStrategies/index.mjs +73 -0
- package/src/connectors/curve.mjs +553 -0
- package/src/connectors/index.mjs +6 -0
- package/src/connectors/jumpover.mjs +452 -0
- package/src/connectors/normal.mjs +12 -0
- package/src/connectors/rounded.mjs +17 -0
- package/src/connectors/smooth.mjs +44 -0
- package/src/connectors/straight.mjs +110 -0
- package/src/dia/Cell.mjs +945 -0
- package/src/dia/CellView.mjs +1316 -0
- package/src/dia/Element.mjs +519 -0
- package/src/dia/ElementView.mjs +859 -0
- package/src/dia/Graph.mjs +1112 -0
- package/src/dia/HighlighterView.mjs +319 -0
- package/src/dia/Link.mjs +565 -0
- package/src/dia/LinkView.mjs +2207 -0
- package/src/dia/Paper.mjs +3171 -0
- package/src/dia/PaperLayer.mjs +75 -0
- package/src/dia/ToolView.mjs +69 -0
- package/src/dia/ToolsView.mjs +128 -0
- package/src/dia/attributes/calc.mjs +128 -0
- package/src/dia/attributes/connection.mjs +75 -0
- package/src/dia/attributes/defs.mjs +76 -0
- package/src/dia/attributes/eval.mjs +64 -0
- package/src/dia/attributes/index.mjs +69 -0
- package/src/dia/attributes/legacy.mjs +148 -0
- package/src/dia/attributes/offset.mjs +53 -0
- package/src/dia/attributes/props.mjs +30 -0
- package/src/dia/attributes/shape.mjs +92 -0
- package/src/dia/attributes/text.mjs +180 -0
- package/src/dia/index.mjs +13 -0
- package/src/dia/layers/GridLayer.mjs +176 -0
- package/src/dia/ports.mjs +874 -0
- package/src/elementTools/Control.mjs +153 -0
- package/src/elementTools/HoverConnect.mjs +37 -0
- package/src/elementTools/index.mjs +5 -0
- package/src/env/index.mjs +43 -0
- package/src/g/bezier.mjs +175 -0
- package/src/g/curve.mjs +956 -0
- package/src/g/ellipse.mjs +245 -0
- package/src/g/extend.mjs +64 -0
- package/src/g/geometry.helpers.mjs +58 -0
- package/src/g/index.mjs +17 -0
- package/src/g/intersection.mjs +511 -0
- package/src/g/line.bearing.mjs +30 -0
- package/src/g/line.length.mjs +5 -0
- package/src/g/line.mjs +356 -0
- package/src/g/line.squaredLength.mjs +10 -0
- package/src/g/path.mjs +2260 -0
- package/src/g/point.mjs +375 -0
- package/src/g/points.mjs +247 -0
- package/src/g/polygon.mjs +51 -0
- package/src/g/polyline.mjs +523 -0
- package/src/g/rect.mjs +556 -0
- package/src/g/types.mjs +10 -0
- package/src/highlighters/addClass.mjs +27 -0
- package/src/highlighters/index.mjs +5 -0
- package/src/highlighters/list.mjs +111 -0
- package/src/highlighters/mask.mjs +220 -0
- package/src/highlighters/opacity.mjs +17 -0
- package/src/highlighters/stroke.mjs +100 -0
- package/src/layout/index.mjs +4 -0
- package/src/layout/ports/port.mjs +188 -0
- package/src/layout/ports/portLabel.mjs +224 -0
- package/src/linkAnchors/index.mjs +76 -0
- package/src/linkTools/Anchor.mjs +235 -0
- package/src/linkTools/Arrowhead.mjs +103 -0
- package/src/linkTools/Boundary.mjs +48 -0
- package/src/linkTools/Button.mjs +121 -0
- package/src/linkTools/Connect.mjs +85 -0
- package/src/linkTools/HoverConnect.mjs +161 -0
- package/src/linkTools/Segments.mjs +393 -0
- package/src/linkTools/Vertices.mjs +253 -0
- package/src/linkTools/helpers.mjs +33 -0
- package/src/linkTools/index.mjs +8 -0
- package/src/mvc/Collection.mjs +560 -0
- package/src/mvc/Data.mjs +46 -0
- package/src/mvc/Dom/Dom.mjs +587 -0
- package/src/mvc/Dom/Event.mjs +130 -0
- package/src/mvc/Dom/animations.mjs +122 -0
- package/src/mvc/Dom/events.mjs +69 -0
- package/src/mvc/Dom/index.mjs +13 -0
- package/src/mvc/Dom/methods.mjs +392 -0
- package/src/mvc/Dom/props.mjs +77 -0
- package/src/mvc/Dom/vars.mjs +5 -0
- package/src/mvc/Events.mjs +337 -0
- package/src/mvc/Listener.mjs +33 -0
- package/src/mvc/Model.mjs +239 -0
- package/src/mvc/View.mjs +323 -0
- package/src/mvc/ViewBase.mjs +182 -0
- package/src/mvc/index.mjs +9 -0
- package/src/mvc/mvcUtils.mjs +90 -0
- package/src/polyfills/array.js +4 -0
- package/src/polyfills/base64.js +68 -0
- package/src/polyfills/index.mjs +5 -0
- package/src/polyfills/number.js +3 -0
- package/src/polyfills/string.js +3 -0
- package/src/polyfills/typedArray.js +47 -0
- package/src/routers/index.mjs +6 -0
- package/src/routers/manhattan.mjs +856 -0
- package/src/routers/metro.mjs +91 -0
- package/src/routers/normal.mjs +6 -0
- package/src/routers/oneSide.mjs +60 -0
- package/src/routers/orthogonal.mjs +323 -0
- package/src/routers/rightAngle.mjs +1056 -0
- package/src/shapes/index.mjs +3 -0
- package/src/shapes/standard.mjs +755 -0
- package/src/util/cloneCells.mjs +67 -0
- package/src/util/getRectPoint.mjs +65 -0
- package/src/util/index.mjs +5 -0
- package/src/util/svgTagTemplate.mjs +110 -0
- package/src/util/util.mjs +1754 -0
- package/src/util/utilHelpers.mjs +2402 -0
- package/src/util/wrappers.mjs +56 -0
- package/types/geometry.d.ts +815 -0
- package/types/index.d.ts +53 -0
- package/types/joint.d.ts +4391 -0
- package/types/joint.head.d.ts +12 -0
- package/types/vectorizer.d.ts +327 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import { Rect } from './rect.mjs';
|
|
2
|
+
import { Point } from './point.mjs';
|
|
3
|
+
import { Line } from './line.mjs';
|
|
4
|
+
import { types } from './types.mjs';
|
|
5
|
+
import { clonePoints, parsePoints, convexHull } from './points.mjs';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
export const Polyline = function(points) {
|
|
9
|
+
|
|
10
|
+
if (!(this instanceof Polyline)) {
|
|
11
|
+
return new Polyline(points);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof points === 'string') {
|
|
15
|
+
return new Polyline.parse(points);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
this.points = (Array.isArray(points) ? points.map(Point) : []);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
Polyline.parse = function(svgString) {
|
|
22
|
+
return new Polyline(parsePoints(svgString));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
Polyline.fromRect = function(rect) {
|
|
26
|
+
return new Polyline([
|
|
27
|
+
rect.topLeft(),
|
|
28
|
+
rect.topRight(),
|
|
29
|
+
rect.bottomRight(),
|
|
30
|
+
rect.bottomLeft(),
|
|
31
|
+
rect.topLeft(),
|
|
32
|
+
]);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
Polyline.prototype = {
|
|
36
|
+
|
|
37
|
+
type: types.Polyline,
|
|
38
|
+
|
|
39
|
+
bbox: function() {
|
|
40
|
+
|
|
41
|
+
var x1 = Infinity;
|
|
42
|
+
var x2 = -Infinity;
|
|
43
|
+
var y1 = Infinity;
|
|
44
|
+
var y2 = -Infinity;
|
|
45
|
+
|
|
46
|
+
var points = this.points;
|
|
47
|
+
var numPoints = points.length;
|
|
48
|
+
if (numPoints === 0) return null; // if points array is empty
|
|
49
|
+
|
|
50
|
+
for (var i = 0; i < numPoints; i++) {
|
|
51
|
+
|
|
52
|
+
var point = points[i];
|
|
53
|
+
var x = point.x;
|
|
54
|
+
var y = point.y;
|
|
55
|
+
|
|
56
|
+
if (x < x1) x1 = x;
|
|
57
|
+
if (x > x2) x2 = x;
|
|
58
|
+
if (y < y1) y1 = y;
|
|
59
|
+
if (y > y2) y2 = y;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return new Rect(x1, y1, x2 - x1, y2 - y1);
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
clone: function() {
|
|
66
|
+
return new Polyline(clonePoints(this.points));
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
closestPoint: function(p) {
|
|
70
|
+
|
|
71
|
+
var cpLength = this.closestPointLength(p);
|
|
72
|
+
|
|
73
|
+
return this.pointAtLength(cpLength);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
closestPointLength: function(p) {
|
|
77
|
+
|
|
78
|
+
var points = this.lengthPoints();
|
|
79
|
+
var numPoints = points.length;
|
|
80
|
+
if (numPoints === 0) return 0; // if points array is empty
|
|
81
|
+
if (numPoints === 1) return 0; // if there is only one point
|
|
82
|
+
|
|
83
|
+
var cpLength;
|
|
84
|
+
var minSqrDistance = Infinity;
|
|
85
|
+
var length = 0;
|
|
86
|
+
var n = numPoints - 1;
|
|
87
|
+
for (var i = 0; i < n; i++) {
|
|
88
|
+
|
|
89
|
+
var line = new Line(points[i], points[i + 1]);
|
|
90
|
+
var lineLength = line.length();
|
|
91
|
+
|
|
92
|
+
var cpNormalizedLength = line.closestPointNormalizedLength(p);
|
|
93
|
+
var cp = line.pointAt(cpNormalizedLength);
|
|
94
|
+
|
|
95
|
+
var sqrDistance = cp.squaredDistance(p);
|
|
96
|
+
if (sqrDistance < minSqrDistance) {
|
|
97
|
+
minSqrDistance = sqrDistance;
|
|
98
|
+
cpLength = length + (cpNormalizedLength * lineLength);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
length += lineLength;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return cpLength;
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
closestPointNormalizedLength: function(p) {
|
|
108
|
+
|
|
109
|
+
var cpLength = this.closestPointLength(p);
|
|
110
|
+
if (cpLength === 0) return 0; // shortcut
|
|
111
|
+
|
|
112
|
+
var length = this.length();
|
|
113
|
+
if (length === 0) return 0; // prevents division by zero
|
|
114
|
+
|
|
115
|
+
return cpLength / length;
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
closestPointTangent: function(p) {
|
|
119
|
+
|
|
120
|
+
var cpLength = this.closestPointLength(p);
|
|
121
|
+
|
|
122
|
+
return this.tangentAtLength(cpLength);
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
// Returns `true` if the area surrounded by the polyline contains the point `p`.
|
|
126
|
+
// Implements the even-odd SVG algorithm (self-intersections are "outside").
|
|
127
|
+
// (Uses horizontal rays to the right of `p` to look for intersections.)
|
|
128
|
+
// Closes open polylines (always imagines a final closing segment).
|
|
129
|
+
containsPoint: function(p) {
|
|
130
|
+
|
|
131
|
+
var points = this.points;
|
|
132
|
+
var numPoints = points.length;
|
|
133
|
+
if (numPoints === 0) return false; // shortcut (this polyline has no points)
|
|
134
|
+
|
|
135
|
+
var x = p.x;
|
|
136
|
+
var y = p.y;
|
|
137
|
+
|
|
138
|
+
// initialize a final closing segment by creating one from last-first points on polyline
|
|
139
|
+
var startIndex = numPoints - 1; // start of current polyline segment
|
|
140
|
+
var endIndex = 0; // end of current polyline segment
|
|
141
|
+
var numIntersections = 0;
|
|
142
|
+
var segment = new Line();
|
|
143
|
+
var ray = new Line();
|
|
144
|
+
var rayEnd = new Point();
|
|
145
|
+
for (; endIndex < numPoints; endIndex++) {
|
|
146
|
+
var start = points[startIndex];
|
|
147
|
+
var end = points[endIndex];
|
|
148
|
+
if (p.equals(start)) return true; // shortcut (`p` is a point on polyline)
|
|
149
|
+
// current polyline segment
|
|
150
|
+
segment.start = start;
|
|
151
|
+
segment.end = end;
|
|
152
|
+
if (segment.containsPoint(p)) return true; // shortcut (`p` lies on a polyline segment)
|
|
153
|
+
|
|
154
|
+
// do we have an intersection?
|
|
155
|
+
if (((y <= start.y) && (y > end.y)) || ((y > start.y) && (y <= end.y))) {
|
|
156
|
+
// this conditional branch IS NOT entered when `segment` is collinear/coincident with `ray`
|
|
157
|
+
// (when `y === start.y === end.y`)
|
|
158
|
+
// this conditional branch IS entered when `segment` touches `ray` at only one point
|
|
159
|
+
// (e.g. when `y === start.y !== end.y`)
|
|
160
|
+
// since this branch is entered again for the following segment, the two touches cancel out
|
|
161
|
+
|
|
162
|
+
var xDifference = (((start.x - x) > (end.x - x)) ? (start.x - x) : (end.x - x));
|
|
163
|
+
if (xDifference >= 0) {
|
|
164
|
+
// segment lies at least partially to the right of `p`
|
|
165
|
+
rayEnd.x = x + xDifference;
|
|
166
|
+
rayEnd.y = y; // right
|
|
167
|
+
ray.start = p;
|
|
168
|
+
ray.end = rayEnd;
|
|
169
|
+
if (segment.intersect(ray)) {
|
|
170
|
+
// an intersection was detected to the right of `p`
|
|
171
|
+
numIntersections++;
|
|
172
|
+
}
|
|
173
|
+
} // else: `segment` lies completely to the left of `p` (i.e. no intersection to the right)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// move to check the next polyline segment
|
|
177
|
+
startIndex = endIndex;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// returns `true` for odd numbers of intersections (even-odd algorithm)
|
|
181
|
+
return ((numIntersections % 2) === 1);
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
close: function() {
|
|
185
|
+
const { start, end, points } = this;
|
|
186
|
+
if (start && end && !start.equals(end)) {
|
|
187
|
+
points.push(start.clone());
|
|
188
|
+
}
|
|
189
|
+
return this;
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
lengthPoints: function() {
|
|
193
|
+
return this.points;
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
convexHull: function() {
|
|
197
|
+
return new Polyline(convexHull(this.points));
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
// Checks whether two polylines are exactly the same.
|
|
201
|
+
// If `p` is undefined or null, returns false.
|
|
202
|
+
equals: function(p) {
|
|
203
|
+
|
|
204
|
+
if (!p) return false;
|
|
205
|
+
|
|
206
|
+
var points = this.points;
|
|
207
|
+
var otherPoints = p.points;
|
|
208
|
+
|
|
209
|
+
var numPoints = points.length;
|
|
210
|
+
if (otherPoints.length !== numPoints) return false; // if the two polylines have different number of points, they cannot be equal
|
|
211
|
+
|
|
212
|
+
for (var i = 0; i < numPoints; i++) {
|
|
213
|
+
|
|
214
|
+
var point = points[i];
|
|
215
|
+
var otherPoint = p.points[i];
|
|
216
|
+
|
|
217
|
+
// as soon as an inequality is found in points, return false
|
|
218
|
+
if (!point.equals(otherPoint)) return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// if no inequality found in points, return true
|
|
222
|
+
return true;
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
intersectionWithLine: function(l) {
|
|
226
|
+
var line = new Line(l);
|
|
227
|
+
var intersections = [];
|
|
228
|
+
var points = this.lengthPoints();
|
|
229
|
+
var l2 = new Line();
|
|
230
|
+
for (var i = 0, n = points.length - 1; i < n; i++) {
|
|
231
|
+
l2.start = points[i];
|
|
232
|
+
l2.end = points[i + 1];
|
|
233
|
+
var int = line.intersectionWithLine(l2);
|
|
234
|
+
if (int) intersections.push(int[0]);
|
|
235
|
+
}
|
|
236
|
+
return (intersections.length > 0) ? intersections : null;
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
isDifferentiable: function() {
|
|
240
|
+
|
|
241
|
+
var points = this.points;
|
|
242
|
+
var numPoints = points.length;
|
|
243
|
+
if (numPoints === 0) return false;
|
|
244
|
+
|
|
245
|
+
var line = new Line();
|
|
246
|
+
var n = numPoints - 1;
|
|
247
|
+
for (var i = 0; i < n; i++) {
|
|
248
|
+
line.start = points[i];
|
|
249
|
+
line.end = points[i + 1];
|
|
250
|
+
// as soon as a differentiable line is found between two points, return true
|
|
251
|
+
if (line.isDifferentiable()) return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// if no differentiable line is found between pairs of points, return false
|
|
255
|
+
return false;
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
length: function() {
|
|
259
|
+
|
|
260
|
+
var points = this.lengthPoints();
|
|
261
|
+
var numPoints = points.length;
|
|
262
|
+
if (numPoints === 0) return 0; // if points array is empty
|
|
263
|
+
|
|
264
|
+
var length = 0;
|
|
265
|
+
var n = numPoints - 1;
|
|
266
|
+
for (var i = 0; i < n; i++) {
|
|
267
|
+
length += points[i].distance(points[i + 1]);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return length;
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
pointAt: function(ratio) {
|
|
274
|
+
|
|
275
|
+
var points = this.lengthPoints();
|
|
276
|
+
var numPoints = points.length;
|
|
277
|
+
if (numPoints === 0) return null; // if points array is empty
|
|
278
|
+
if (numPoints === 1) return points[0].clone(); // if there is only one point
|
|
279
|
+
|
|
280
|
+
if (ratio <= 0) return points[0].clone();
|
|
281
|
+
if (ratio >= 1) return points[numPoints - 1].clone();
|
|
282
|
+
|
|
283
|
+
var polylineLength = this.length();
|
|
284
|
+
var length = polylineLength * ratio;
|
|
285
|
+
|
|
286
|
+
return this.pointAtLength(length);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
pointAtLength: function(length) {
|
|
290
|
+
|
|
291
|
+
var points = this.lengthPoints();
|
|
292
|
+
var numPoints = points.length;
|
|
293
|
+
if (numPoints === 0) return null; // if points array is empty
|
|
294
|
+
if (numPoints === 1) return points[0].clone(); // if there is only one point
|
|
295
|
+
|
|
296
|
+
var fromStart = true;
|
|
297
|
+
if (length < 0) {
|
|
298
|
+
fromStart = false; // negative lengths mean start calculation from end point
|
|
299
|
+
length = -length; // absolute value
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
var l = 0;
|
|
303
|
+
var n = numPoints - 1;
|
|
304
|
+
for (var i = 0; i < n; i++) {
|
|
305
|
+
var index = (fromStart ? i : (n - 1 - i));
|
|
306
|
+
|
|
307
|
+
var a = points[index];
|
|
308
|
+
var b = points[index + 1];
|
|
309
|
+
var line = new Line(a, b);
|
|
310
|
+
var d = a.distance(b);
|
|
311
|
+
|
|
312
|
+
if (length <= (l + d)) {
|
|
313
|
+
return line.pointAtLength((fromStart ? 1 : -1) * (length - l));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
l += d;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// if length requested is higher than the length of the polyline, return last endpoint
|
|
320
|
+
var lastPoint = (fromStart ? points[numPoints - 1] : points[0]);
|
|
321
|
+
return lastPoint.clone();
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
round: function(precision) {
|
|
325
|
+
|
|
326
|
+
var points = this.points;
|
|
327
|
+
var numPoints = points.length;
|
|
328
|
+
|
|
329
|
+
for (var i = 0; i < numPoints; i++) {
|
|
330
|
+
points[i].round(precision);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return this;
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
scale: function(sx, sy, origin) {
|
|
337
|
+
|
|
338
|
+
var points = this.points;
|
|
339
|
+
var numPoints = points.length;
|
|
340
|
+
|
|
341
|
+
for (var i = 0; i < numPoints; i++) {
|
|
342
|
+
points[i].scale(sx, sy, origin);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return this;
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
simplify: function(opt = {}) {
|
|
349
|
+
|
|
350
|
+
const points = this.points;
|
|
351
|
+
if (points.length < 3) return this; // we need at least 3 points
|
|
352
|
+
|
|
353
|
+
// TODO: we may also accept startIndex and endIndex to specify where to start and end simplification
|
|
354
|
+
|
|
355
|
+
// Due to the nature of the algorithm, we do not use 0 as the default value for `threshold`
|
|
356
|
+
// because of the rounding errors that can occur when comparing distances.
|
|
357
|
+
const threshold = opt.threshold || 1e-10; // = max distance of middle point from chord to be simplified
|
|
358
|
+
|
|
359
|
+
// start at the beginning of the polyline and go forward
|
|
360
|
+
let currentIndex = 0;
|
|
361
|
+
// we need at least one intermediate point (3 points) in every iteration
|
|
362
|
+
// as soon as that stops being true, we know we reached the end of the polyline
|
|
363
|
+
while (points[currentIndex + 2]) {
|
|
364
|
+
const firstIndex = currentIndex;
|
|
365
|
+
const middleIndex = (currentIndex + 1);
|
|
366
|
+
const lastIndex = (currentIndex + 2);
|
|
367
|
+
|
|
368
|
+
const firstPoint = points[firstIndex];
|
|
369
|
+
const middlePoint = points[middleIndex];
|
|
370
|
+
const lastPoint = points[lastIndex];
|
|
371
|
+
|
|
372
|
+
const chord = new Line(firstPoint, lastPoint); // = connection between first and last point
|
|
373
|
+
const closestPoint = chord.closestPoint(middlePoint); // = closest point on chord from middle point
|
|
374
|
+
const closestPointDistance = closestPoint.distance(middlePoint);
|
|
375
|
+
if (closestPointDistance <= threshold) {
|
|
376
|
+
// middle point is close enough to the chord = simplify
|
|
377
|
+
// 1) remove middle point:
|
|
378
|
+
points.splice(middleIndex, 1);
|
|
379
|
+
// 2) in next iteration, investigate the newly-created triplet of points
|
|
380
|
+
// - do not change `currentIndex`
|
|
381
|
+
// = (first point stays, point after removed point becomes middle point)
|
|
382
|
+
} else {
|
|
383
|
+
// middle point is far from the chord
|
|
384
|
+
// 1) preserve middle point
|
|
385
|
+
// 2) in next iteration, move `currentIndex` by one step:
|
|
386
|
+
currentIndex += 1;
|
|
387
|
+
// = (point after first point becomes first point)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// `points` array was modified in-place
|
|
392
|
+
return this;
|
|
393
|
+
},
|
|
394
|
+
|
|
395
|
+
tangentAt: function(ratio) {
|
|
396
|
+
|
|
397
|
+
var points = this.lengthPoints();
|
|
398
|
+
var numPoints = points.length;
|
|
399
|
+
if (numPoints === 0) return null; // if points array is empty
|
|
400
|
+
if (numPoints === 1) return null; // if there is only one point
|
|
401
|
+
|
|
402
|
+
if (ratio < 0) ratio = 0;
|
|
403
|
+
if (ratio > 1) ratio = 1;
|
|
404
|
+
|
|
405
|
+
var polylineLength = this.length();
|
|
406
|
+
var length = polylineLength * ratio;
|
|
407
|
+
|
|
408
|
+
return this.tangentAtLength(length);
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
tangentAtLength: function(length) {
|
|
412
|
+
|
|
413
|
+
var points = this.lengthPoints();
|
|
414
|
+
var numPoints = points.length;
|
|
415
|
+
if (numPoints === 0) return null; // if points array is empty
|
|
416
|
+
if (numPoints === 1) return null; // if there is only one point
|
|
417
|
+
|
|
418
|
+
var fromStart = true;
|
|
419
|
+
if (length < 0) {
|
|
420
|
+
fromStart = false; // negative lengths mean start calculation from end point
|
|
421
|
+
length = -length; // absolute value
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
var lastValidLine; // differentiable (with a tangent)
|
|
425
|
+
var l = 0; // length so far
|
|
426
|
+
var n = numPoints - 1;
|
|
427
|
+
for (var i = 0; i < n; i++) {
|
|
428
|
+
var index = (fromStart ? i : (n - 1 - i));
|
|
429
|
+
|
|
430
|
+
var a = points[index];
|
|
431
|
+
var b = points[index + 1];
|
|
432
|
+
var line = new Line(a, b);
|
|
433
|
+
var d = a.distance(b);
|
|
434
|
+
|
|
435
|
+
if (line.isDifferentiable()) { // has a tangent line (line length is not 0)
|
|
436
|
+
if (length <= (l + d)) {
|
|
437
|
+
return line.tangentAtLength((fromStart ? 1 : -1) * (length - l));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
lastValidLine = line;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
l += d;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// if length requested is higher than the length of the polyline, return last valid endpoint
|
|
447
|
+
if (lastValidLine) {
|
|
448
|
+
var ratio = (fromStart ? 1 : 0);
|
|
449
|
+
return lastValidLine.tangentAt(ratio);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// if no valid line, return null
|
|
453
|
+
return null;
|
|
454
|
+
},
|
|
455
|
+
|
|
456
|
+
toString: function() {
|
|
457
|
+
|
|
458
|
+
return this.points + '';
|
|
459
|
+
},
|
|
460
|
+
|
|
461
|
+
translate: function(tx, ty) {
|
|
462
|
+
|
|
463
|
+
var points = this.points;
|
|
464
|
+
var numPoints = points.length;
|
|
465
|
+
|
|
466
|
+
for (var i = 0; i < numPoints; i++) {
|
|
467
|
+
points[i].translate(tx, ty);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return this;
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
// Return svgString that can be used to recreate this line.
|
|
474
|
+
serialize: function() {
|
|
475
|
+
|
|
476
|
+
var points = this.points;
|
|
477
|
+
var numPoints = points.length;
|
|
478
|
+
if (numPoints === 0) return ''; // if points array is empty
|
|
479
|
+
|
|
480
|
+
var output = '';
|
|
481
|
+
for (var i = 0; i < numPoints; i++) {
|
|
482
|
+
|
|
483
|
+
var point = points[i];
|
|
484
|
+
output += point.x + ',' + point.y + ' ';
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return output.trim();
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
Object.defineProperty(Polyline.prototype, 'start', {
|
|
492
|
+
// Getter for the first point of the polyline.
|
|
493
|
+
|
|
494
|
+
configurable: true,
|
|
495
|
+
|
|
496
|
+
enumerable: true,
|
|
497
|
+
|
|
498
|
+
get: function() {
|
|
499
|
+
|
|
500
|
+
var points = this.points;
|
|
501
|
+
var numPoints = points.length;
|
|
502
|
+
if (numPoints === 0) return null; // if points array is empty
|
|
503
|
+
|
|
504
|
+
return this.points[0];
|
|
505
|
+
},
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
Object.defineProperty(Polyline.prototype, 'end', {
|
|
509
|
+
// Getter for the last point of the polyline.
|
|
510
|
+
|
|
511
|
+
configurable: true,
|
|
512
|
+
|
|
513
|
+
enumerable: true,
|
|
514
|
+
|
|
515
|
+
get: function() {
|
|
516
|
+
|
|
517
|
+
var points = this.points;
|
|
518
|
+
var numPoints = points.length;
|
|
519
|
+
if (numPoints === 0) return null; // if points array is empty
|
|
520
|
+
|
|
521
|
+
return this.points[numPoints - 1];
|
|
522
|
+
},
|
|
523
|
+
});
|