@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.
Files changed (139) hide show
  1. package/LICENSE +376 -0
  2. package/README.md +49 -0
  3. package/dist/geometry.js +6486 -0
  4. package/dist/geometry.min.js +8 -0
  5. package/dist/joint.d.ts +5536 -0
  6. package/dist/joint.js +39629 -0
  7. package/dist/joint.min.js +8 -0
  8. package/dist/joint.nowrap.js +39626 -0
  9. package/dist/joint.nowrap.min.js +8 -0
  10. package/dist/vectorizer.js +9135 -0
  11. package/dist/vectorizer.min.js +8 -0
  12. package/dist/version.mjs +3 -0
  13. package/index.js +3 -0
  14. package/joint.mjs +27 -0
  15. package/package.json +192 -0
  16. package/src/V/annotation.mjs +0 -0
  17. package/src/V/index.mjs +2642 -0
  18. package/src/anchors/index.mjs +123 -0
  19. package/src/config/index.mjs +12 -0
  20. package/src/connectionPoints/index.mjs +202 -0
  21. package/src/connectionStrategies/index.mjs +73 -0
  22. package/src/connectors/curve.mjs +553 -0
  23. package/src/connectors/index.mjs +6 -0
  24. package/src/connectors/jumpover.mjs +452 -0
  25. package/src/connectors/normal.mjs +12 -0
  26. package/src/connectors/rounded.mjs +17 -0
  27. package/src/connectors/smooth.mjs +44 -0
  28. package/src/connectors/straight.mjs +110 -0
  29. package/src/dia/Cell.mjs +945 -0
  30. package/src/dia/CellView.mjs +1316 -0
  31. package/src/dia/Element.mjs +519 -0
  32. package/src/dia/ElementView.mjs +859 -0
  33. package/src/dia/Graph.mjs +1112 -0
  34. package/src/dia/HighlighterView.mjs +319 -0
  35. package/src/dia/Link.mjs +565 -0
  36. package/src/dia/LinkView.mjs +2207 -0
  37. package/src/dia/Paper.mjs +3171 -0
  38. package/src/dia/PaperLayer.mjs +75 -0
  39. package/src/dia/ToolView.mjs +69 -0
  40. package/src/dia/ToolsView.mjs +128 -0
  41. package/src/dia/attributes/calc.mjs +128 -0
  42. package/src/dia/attributes/connection.mjs +75 -0
  43. package/src/dia/attributes/defs.mjs +76 -0
  44. package/src/dia/attributes/eval.mjs +64 -0
  45. package/src/dia/attributes/index.mjs +69 -0
  46. package/src/dia/attributes/legacy.mjs +148 -0
  47. package/src/dia/attributes/offset.mjs +53 -0
  48. package/src/dia/attributes/props.mjs +30 -0
  49. package/src/dia/attributes/shape.mjs +92 -0
  50. package/src/dia/attributes/text.mjs +180 -0
  51. package/src/dia/index.mjs +13 -0
  52. package/src/dia/layers/GridLayer.mjs +176 -0
  53. package/src/dia/ports.mjs +874 -0
  54. package/src/elementTools/Control.mjs +153 -0
  55. package/src/elementTools/HoverConnect.mjs +37 -0
  56. package/src/elementTools/index.mjs +5 -0
  57. package/src/env/index.mjs +43 -0
  58. package/src/g/bezier.mjs +175 -0
  59. package/src/g/curve.mjs +956 -0
  60. package/src/g/ellipse.mjs +245 -0
  61. package/src/g/extend.mjs +64 -0
  62. package/src/g/geometry.helpers.mjs +58 -0
  63. package/src/g/index.mjs +17 -0
  64. package/src/g/intersection.mjs +511 -0
  65. package/src/g/line.bearing.mjs +30 -0
  66. package/src/g/line.length.mjs +5 -0
  67. package/src/g/line.mjs +356 -0
  68. package/src/g/line.squaredLength.mjs +10 -0
  69. package/src/g/path.mjs +2260 -0
  70. package/src/g/point.mjs +375 -0
  71. package/src/g/points.mjs +247 -0
  72. package/src/g/polygon.mjs +51 -0
  73. package/src/g/polyline.mjs +523 -0
  74. package/src/g/rect.mjs +556 -0
  75. package/src/g/types.mjs +10 -0
  76. package/src/highlighters/addClass.mjs +27 -0
  77. package/src/highlighters/index.mjs +5 -0
  78. package/src/highlighters/list.mjs +111 -0
  79. package/src/highlighters/mask.mjs +220 -0
  80. package/src/highlighters/opacity.mjs +17 -0
  81. package/src/highlighters/stroke.mjs +100 -0
  82. package/src/layout/index.mjs +4 -0
  83. package/src/layout/ports/port.mjs +188 -0
  84. package/src/layout/ports/portLabel.mjs +224 -0
  85. package/src/linkAnchors/index.mjs +76 -0
  86. package/src/linkTools/Anchor.mjs +235 -0
  87. package/src/linkTools/Arrowhead.mjs +103 -0
  88. package/src/linkTools/Boundary.mjs +48 -0
  89. package/src/linkTools/Button.mjs +121 -0
  90. package/src/linkTools/Connect.mjs +85 -0
  91. package/src/linkTools/HoverConnect.mjs +161 -0
  92. package/src/linkTools/Segments.mjs +393 -0
  93. package/src/linkTools/Vertices.mjs +253 -0
  94. package/src/linkTools/helpers.mjs +33 -0
  95. package/src/linkTools/index.mjs +8 -0
  96. package/src/mvc/Collection.mjs +560 -0
  97. package/src/mvc/Data.mjs +46 -0
  98. package/src/mvc/Dom/Dom.mjs +587 -0
  99. package/src/mvc/Dom/Event.mjs +130 -0
  100. package/src/mvc/Dom/animations.mjs +122 -0
  101. package/src/mvc/Dom/events.mjs +69 -0
  102. package/src/mvc/Dom/index.mjs +13 -0
  103. package/src/mvc/Dom/methods.mjs +392 -0
  104. package/src/mvc/Dom/props.mjs +77 -0
  105. package/src/mvc/Dom/vars.mjs +5 -0
  106. package/src/mvc/Events.mjs +337 -0
  107. package/src/mvc/Listener.mjs +33 -0
  108. package/src/mvc/Model.mjs +239 -0
  109. package/src/mvc/View.mjs +323 -0
  110. package/src/mvc/ViewBase.mjs +182 -0
  111. package/src/mvc/index.mjs +9 -0
  112. package/src/mvc/mvcUtils.mjs +90 -0
  113. package/src/polyfills/array.js +4 -0
  114. package/src/polyfills/base64.js +68 -0
  115. package/src/polyfills/index.mjs +5 -0
  116. package/src/polyfills/number.js +3 -0
  117. package/src/polyfills/string.js +3 -0
  118. package/src/polyfills/typedArray.js +47 -0
  119. package/src/routers/index.mjs +6 -0
  120. package/src/routers/manhattan.mjs +856 -0
  121. package/src/routers/metro.mjs +91 -0
  122. package/src/routers/normal.mjs +6 -0
  123. package/src/routers/oneSide.mjs +60 -0
  124. package/src/routers/orthogonal.mjs +323 -0
  125. package/src/routers/rightAngle.mjs +1056 -0
  126. package/src/shapes/index.mjs +3 -0
  127. package/src/shapes/standard.mjs +755 -0
  128. package/src/util/cloneCells.mjs +67 -0
  129. package/src/util/getRectPoint.mjs +65 -0
  130. package/src/util/index.mjs +5 -0
  131. package/src/util/svgTagTemplate.mjs +110 -0
  132. package/src/util/util.mjs +1754 -0
  133. package/src/util/utilHelpers.mjs +2402 -0
  134. package/src/util/wrappers.mjs +56 -0
  135. package/types/geometry.d.ts +815 -0
  136. package/types/index.d.ts +53 -0
  137. package/types/joint.d.ts +4391 -0
  138. package/types/joint.head.d.ts +12 -0
  139. 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
+ });