@excalidraw/excalidraw 0.17.1-1d71f84 → 0.17.1-4689a6b

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 (111) hide show
  1. package/CHANGELOG.md +1 -0
  2. package/dist/browser/dev/excalidraw-assets-dev/{chunk-AK7SWNLN.js → chunk-23CKV3WP.js} +4 -2
  3. package/dist/browser/dev/excalidraw-assets-dev/chunk-23CKV3WP.js.map +7 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/{chunk-RWZVJAQU.js → chunk-7D5BMEAB.js} +2227 -1976
  5. package/dist/browser/dev/excalidraw-assets-dev/chunk-7D5BMEAB.js.map +7 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js → en-W7TECCRB.js} +2 -2
  7. package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js → image-JKT6GXZD.js} +2 -2
  8. package/dist/browser/dev/index.css +20 -0
  9. package/dist/browser/dev/index.css.map +2 -2
  10. package/dist/browser/dev/index.js +770 -585
  11. package/dist/browser/dev/index.js.map +4 -4
  12. package/dist/browser/prod/excalidraw-assets/chunk-DWOM5R6H.js +55 -0
  13. package/dist/browser/prod/excalidraw-assets/{chunk-CTYINSWT.js → chunk-SK23VHAR.js} +2 -2
  14. package/dist/browser/prod/excalidraw-assets/{en-LROPV2RN.js → en-SMMH575S.js} +1 -1
  15. package/dist/browser/prod/excalidraw-assets/image-WDEQS5RL.js +1 -0
  16. package/dist/browser/prod/index.css +1 -1
  17. package/dist/browser/prod/index.js +22 -22
  18. package/dist/{prod/en-II4GK66F.json → dev/en-CVBEBUBY.json} +3 -1
  19. package/dist/dev/index.css +20 -0
  20. package/dist/dev/index.css.map +2 -2
  21. package/dist/dev/index.js +2383 -2074
  22. package/dist/dev/index.js.map +4 -4
  23. package/dist/excalidraw/actions/actionBoundText.js +4 -1
  24. package/dist/excalidraw/actions/actionCanvas.js +3 -1
  25. package/dist/excalidraw/actions/actionDuplicateSelection.js +4 -0
  26. package/dist/excalidraw/actions/actionExport.d.ts +1 -1
  27. package/dist/excalidraw/actions/actionFinalize.d.ts +1 -1
  28. package/dist/excalidraw/actions/actionFinalize.js +3 -3
  29. package/dist/excalidraw/actions/actionFlip.d.ts +3 -3
  30. package/dist/excalidraw/actions/actionFlip.js +6 -6
  31. package/dist/excalidraw/actions/actionGroup.js +4 -2
  32. package/dist/excalidraw/actions/actionHistory.js +3 -0
  33. package/dist/excalidraw/actions/actionZindex.d.ts +11 -11
  34. package/dist/excalidraw/actions/shortcuts.js +1 -1
  35. package/dist/excalidraw/analytics.js +1 -1
  36. package/dist/excalidraw/components/App.d.ts +13 -3
  37. package/dist/excalidraw/components/App.js +212 -83
  38. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +24 -10
  39. package/dist/excalidraw/components/DarkModeToggle.js +3 -1
  40. package/dist/excalidraw/components/HelpDialog.js +8 -6
  41. package/dist/excalidraw/components/RadioGroup.d.ts +2 -1
  42. package/dist/excalidraw/components/RadioGroup.js +1 -1
  43. package/dist/excalidraw/components/TTDDialog/MermaidToExcalidraw.js +6 -2
  44. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.d.ts +18 -0
  45. package/dist/excalidraw/components/dropdownMenu/DropdownMenuItemContentRadio.js +9 -0
  46. package/dist/excalidraw/components/hyperlink/Hyperlink.js +3 -3
  47. package/dist/excalidraw/components/hyperlink/helpers.js +2 -3
  48. package/dist/excalidraw/components/icons.d.ts +3 -0
  49. package/dist/excalidraw/components/icons.js +5 -1
  50. package/dist/excalidraw/components/main-menu/DefaultItems.d.ts +12 -2
  51. package/dist/excalidraw/components/main-menu/DefaultItems.js +38 -7
  52. package/dist/excalidraw/constants.d.ts +0 -3
  53. package/dist/excalidraw/constants.js +0 -3
  54. package/dist/excalidraw/data/magic.js +2 -1
  55. package/dist/excalidraw/data/reconcile.d.ts +6 -0
  56. package/dist/excalidraw/data/reconcile.js +49 -0
  57. package/dist/excalidraw/data/restore.d.ts +3 -3
  58. package/dist/excalidraw/data/restore.js +5 -6
  59. package/dist/excalidraw/data/transform.d.ts +1 -1
  60. package/dist/excalidraw/data/transform.js +12 -3
  61. package/dist/excalidraw/element/binding.d.ts +22 -9
  62. package/dist/excalidraw/element/binding.js +403 -26
  63. package/dist/excalidraw/element/bounds.d.ts +0 -1
  64. package/dist/excalidraw/element/bounds.js +0 -3
  65. package/dist/excalidraw/element/collision.d.ts +14 -19
  66. package/dist/excalidraw/element/collision.js +36 -713
  67. package/dist/excalidraw/element/embeddable.js +18 -43
  68. package/dist/excalidraw/element/index.d.ts +0 -1
  69. package/dist/excalidraw/element/index.js +0 -1
  70. package/dist/excalidraw/element/linearElementEditor.d.ts +10 -10
  71. package/dist/excalidraw/element/linearElementEditor.js +6 -4
  72. package/dist/excalidraw/element/newElement.d.ts +1 -1
  73. package/dist/excalidraw/element/newElement.js +2 -1
  74. package/dist/excalidraw/element/textElement.d.ts +0 -1
  75. package/dist/excalidraw/element/textElement.js +0 -30
  76. package/dist/excalidraw/element/types.d.ts +17 -2
  77. package/dist/excalidraw/errors.d.ts +3 -0
  78. package/dist/excalidraw/errors.js +3 -0
  79. package/dist/excalidraw/fractionalIndex.d.ts +40 -0
  80. package/dist/excalidraw/fractionalIndex.js +241 -0
  81. package/dist/excalidraw/frame.d.ts +1 -1
  82. package/dist/excalidraw/hooks/useCreatePortalContainer.js +2 -1
  83. package/dist/excalidraw/locales/en.json +3 -1
  84. package/dist/excalidraw/renderer/helpers.js +2 -2
  85. package/dist/excalidraw/renderer/interactiveScene.js +1 -1
  86. package/dist/excalidraw/renderer/renderElement.js +3 -3
  87. package/dist/excalidraw/renderer/renderSnaps.js +2 -1
  88. package/dist/excalidraw/scene/Scene.d.ts +7 -6
  89. package/dist/excalidraw/scene/Scene.js +28 -13
  90. package/dist/excalidraw/scene/export.js +4 -3
  91. package/dist/excalidraw/types.d.ts +4 -3
  92. package/dist/excalidraw/utils.d.ts +1 -0
  93. package/dist/excalidraw/utils.js +1 -0
  94. package/dist/excalidraw/zindex.d.ts +2 -2
  95. package/dist/excalidraw/zindex.js +9 -13
  96. package/dist/{dev/en-II4GK66F.json → prod/en-CVBEBUBY.json} +3 -1
  97. package/dist/prod/index.css +1 -1
  98. package/dist/prod/index.js +36 -36
  99. package/dist/utils/collision.d.ts +4 -0
  100. package/dist/utils/collision.js +48 -0
  101. package/dist/utils/geometry/geometry.d.ts +71 -0
  102. package/dist/utils/geometry/geometry.js +674 -0
  103. package/dist/utils/geometry/shape.d.ts +55 -0
  104. package/dist/utils/geometry/shape.js +149 -0
  105. package/package.json +2 -1
  106. package/dist/browser/dev/excalidraw-assets-dev/chunk-AK7SWNLN.js.map +0 -7
  107. package/dist/browser/dev/excalidraw-assets-dev/chunk-RWZVJAQU.js.map +0 -7
  108. package/dist/browser/prod/excalidraw-assets/chunk-LL4GORAM.js +0 -55
  109. package/dist/browser/prod/excalidraw-assets/image-EFCJDJH3.js +0 -1
  110. /package/dist/browser/dev/excalidraw-assets-dev/{en-5TCZHGGJ.js.map → en-W7TECCRB.js.map} +0 -0
  111. /package/dist/browser/dev/excalidraw-assets-dev/{image-EDKQZH7Z.js.map → image-JKT6GXZD.js.map} +0 -0
@@ -0,0 +1,674 @@
1
+ import { distance2d } from "../../excalidraw/math";
2
+ const DEFAULT_THRESHOLD = 10e-5;
3
+ /**
4
+ * utils
5
+ */
6
+ // the two vectors are ao and bo
7
+ export const cross = (a, b, o) => {
8
+ return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
9
+ };
10
+ export const isClosed = (polygon) => {
11
+ const first = polygon[0];
12
+ const last = polygon[polygon.length - 1];
13
+ return first[0] === last[0] && first[1] === last[1];
14
+ };
15
+ export const close = (polygon) => {
16
+ return isClosed(polygon) ? polygon : [...polygon, polygon[0]];
17
+ };
18
+ /**
19
+ * angles
20
+ */
21
+ // convert radians to degress
22
+ export const angleToDegrees = (angle) => {
23
+ return (angle * 180) / Math.PI;
24
+ };
25
+ // convert degrees to radians
26
+ export const angleToRadians = (angle) => {
27
+ return (angle / 180) * Math.PI;
28
+ };
29
+ // return the angle of reflection given an angle of incidence and a surface angle in degrees
30
+ export const angleReflect = (incidenceAngle, surfaceAngle) => {
31
+ const a = surfaceAngle * 2 - incidenceAngle;
32
+ return a >= 360 ? a - 360 : a < 0 ? a + 360 : a;
33
+ };
34
+ /**
35
+ * points
36
+ */
37
+ const rotate = (point, angle) => {
38
+ return [
39
+ point[0] * Math.cos(angle) - point[1] * Math.sin(angle),
40
+ point[0] * Math.sin(angle) + point[1] * Math.cos(angle),
41
+ ];
42
+ };
43
+ const isOrigin = (point) => {
44
+ return point[0] === 0 && point[1] === 0;
45
+ };
46
+ // rotate a given point about a given origin at the given angle
47
+ export const pointRotate = (point, angle, origin) => {
48
+ const r = angleToRadians(angle);
49
+ if (!origin || isOrigin(origin)) {
50
+ return rotate(point, r);
51
+ }
52
+ return rotate(point.map((c, i) => c - origin[i]), r).map((c, i) => c + origin[i]);
53
+ };
54
+ // translate a point by an angle (in degrees) and distance
55
+ export const pointTranslate = (point, angle = 0, distance = 0) => {
56
+ const r = angleToRadians(angle);
57
+ return [
58
+ point[0] + distance * Math.cos(r),
59
+ point[1] + distance * Math.sin(r),
60
+ ];
61
+ };
62
+ export const pointInverse = (point) => {
63
+ return [-point[0], -point[1]];
64
+ };
65
+ export const pointAdd = (pointA, pointB) => {
66
+ return [pointA[0] + pointB[0], pointA[1] + pointB[1]];
67
+ };
68
+ export const distanceToPoint = (p1, p2) => {
69
+ return distance2d(...p1, ...p2);
70
+ };
71
+ /**
72
+ * lines
73
+ */
74
+ // return the angle of a line, in degrees
75
+ export const lineAngle = (line) => {
76
+ return angleToDegrees(Math.atan2(line[1][1] - line[0][1], line[1][0] - line[0][0]));
77
+ };
78
+ // get the distance between the endpoints of a line segment
79
+ export const lineLength = (line) => {
80
+ return Math.sqrt(Math.pow(line[1][0] - line[0][0], 2) + Math.pow(line[1][1] - line[0][1], 2));
81
+ };
82
+ // get the midpoint of a line segment
83
+ export const lineMidpoint = (line) => {
84
+ return [
85
+ (line[0][0] + line[1][0]) / 2,
86
+ (line[0][1] + line[1][1]) / 2,
87
+ ];
88
+ };
89
+ // return the coordinates resulting from rotating the given line about an origin by an angle in degrees
90
+ // note that when the origin is not given, the midpoint of the given line is used as the origin
91
+ export const lineRotate = (line, angle, origin) => {
92
+ return line.map((point) => pointRotate(point, angle, origin || lineMidpoint(line)));
93
+ };
94
+ // returns the coordinates resulting from translating a line by an angle in degrees and a distance.
95
+ export const lineTranslate = (line, angle, distance) => {
96
+ return line.map((point) => pointTranslate(point, angle, distance));
97
+ };
98
+ export const lineInterpolate = (line, clamp = false) => {
99
+ const [[x1, y1], [x2, y2]] = line;
100
+ return (t) => {
101
+ const t0 = clamp ? (t < 0 ? 0 : t > 1 ? 1 : t) : t;
102
+ return [(x2 - x1) * t0 + x1, (y2 - y1) * t0 + y1];
103
+ };
104
+ };
105
+ /**
106
+ * curves
107
+ */
108
+ function clone(p) {
109
+ return [...p];
110
+ }
111
+ export const curveToBezier = (pointsIn, curveTightness = 0) => {
112
+ const len = pointsIn.length;
113
+ if (len < 3) {
114
+ throw new Error("A curve must have at least three points.");
115
+ }
116
+ const out = [];
117
+ if (len === 3) {
118
+ out.push(clone(pointsIn[0]), clone(pointsIn[1]), clone(pointsIn[2]), clone(pointsIn[2]));
119
+ }
120
+ else {
121
+ const points = [];
122
+ points.push(pointsIn[0], pointsIn[0]);
123
+ for (let i = 1; i < pointsIn.length; i++) {
124
+ points.push(pointsIn[i]);
125
+ if (i === pointsIn.length - 1) {
126
+ points.push(pointsIn[i]);
127
+ }
128
+ }
129
+ const b = [];
130
+ const s = 1 - curveTightness;
131
+ out.push(clone(points[0]));
132
+ for (let i = 1; i + 2 < points.length; i++) {
133
+ const cachedVertArray = points[i];
134
+ b[0] = [cachedVertArray[0], cachedVertArray[1]];
135
+ b[1] = [
136
+ cachedVertArray[0] + (s * points[i + 1][0] - s * points[i - 1][0]) / 6,
137
+ cachedVertArray[1] + (s * points[i + 1][1] - s * points[i - 1][1]) / 6,
138
+ ];
139
+ b[2] = [
140
+ points[i + 1][0] + (s * points[i][0] - s * points[i + 2][0]) / 6,
141
+ points[i + 1][1] + (s * points[i][1] - s * points[i + 2][1]) / 6,
142
+ ];
143
+ b[3] = [points[i + 1][0], points[i + 1][1]];
144
+ out.push(b[1], b[2], b[3]);
145
+ }
146
+ }
147
+ return out;
148
+ };
149
+ export const curveRotate = (curve, angle, origin) => {
150
+ return curve.map((p) => pointRotate(p, angle, origin));
151
+ };
152
+ export const cubicBezierPoint = (t, controlPoints) => {
153
+ const [p0, p1, p2, p3] = controlPoints;
154
+ const x = Math.pow(1 - t, 3) * p0[0] +
155
+ 3 * Math.pow(1 - t, 2) * t * p1[0] +
156
+ 3 * (1 - t) * Math.pow(t, 2) * p2[0] +
157
+ Math.pow(t, 3) * p3[0];
158
+ const y = Math.pow(1 - t, 3) * p0[1] +
159
+ 3 * Math.pow(1 - t, 2) * t * p1[1] +
160
+ 3 * (1 - t) * Math.pow(t, 2) * p2[1] +
161
+ Math.pow(t, 3) * p3[1];
162
+ return [x, y];
163
+ };
164
+ const solveCubicEquation = (a, b, c, d) => {
165
+ // This function solves the cubic equation ax^3 + bx^2 + cx + d = 0
166
+ const roots = [];
167
+ const discriminant = 18 * a * b * c * d -
168
+ 4 * Math.pow(b, 3) * d +
169
+ Math.pow(b, 2) * Math.pow(c, 2) -
170
+ 4 * a * Math.pow(c, 3) -
171
+ 27 * Math.pow(a, 2) * Math.pow(d, 2);
172
+ if (discriminant >= 0) {
173
+ const C = Math.cbrt((discriminant + Math.sqrt(discriminant)) / 2);
174
+ const D = Math.cbrt((discriminant - Math.sqrt(discriminant)) / 2);
175
+ const root1 = (-b - C - D) / (3 * a);
176
+ const root2 = (-b + (C + D) / 2) / (3 * a);
177
+ const root3 = (-b + (C + D) / 2) / (3 * a);
178
+ roots.push(root1, root2, root3);
179
+ }
180
+ else {
181
+ const realPart = -b / (3 * a);
182
+ const root1 = 2 * Math.sqrt(-b / (3 * a)) * Math.cos(Math.acos(realPart) / 3);
183
+ const root2 = 2 *
184
+ Math.sqrt(-b / (3 * a)) *
185
+ Math.cos((Math.acos(realPart) + 2 * Math.PI) / 3);
186
+ const root3 = 2 *
187
+ Math.sqrt(-b / (3 * a)) *
188
+ Math.cos((Math.acos(realPart) + 4 * Math.PI) / 3);
189
+ roots.push(root1, root2, root3);
190
+ }
191
+ return roots;
192
+ };
193
+ const findClosestParameter = (point, controlPoints) => {
194
+ // This function finds the parameter t that minimizes the distance between the point
195
+ // and any point on the cubic Bezier curve.
196
+ const [p0, p1, p2, p3] = controlPoints;
197
+ // Use the direct formula to find the parameter t
198
+ const a = p3[0] - 3 * p2[0] + 3 * p1[0] - p0[0];
199
+ const b = 3 * p2[0] - 6 * p1[0] + 3 * p0[0];
200
+ const c = 3 * p1[0] - 3 * p0[0];
201
+ const d = p0[0] - point[0];
202
+ const rootsX = solveCubicEquation(a, b, c, d);
203
+ // Do the same for the y-coordinate
204
+ const e = p3[1] - 3 * p2[1] + 3 * p1[1] - p0[1];
205
+ const f = 3 * p2[1] - 6 * p1[1] + 3 * p0[1];
206
+ const g = 3 * p1[1] - 3 * p0[1];
207
+ const h = p0[1] - point[1];
208
+ const rootsY = solveCubicEquation(e, f, g, h);
209
+ // Select the real root that is between 0 and 1 (inclusive)
210
+ const validRootsX = rootsX.filter((root) => root >= 0 && root <= 1);
211
+ const validRootsY = rootsY.filter((root) => root >= 0 && root <= 1);
212
+ if (validRootsX.length === 0 || validRootsY.length === 0) {
213
+ // No valid roots found, use the midpoint as a fallback
214
+ return 0.5;
215
+ }
216
+ // Choose the parameter t that minimizes the distance
217
+ let minDistance = Infinity;
218
+ let closestT = 0;
219
+ for (const rootX of validRootsX) {
220
+ for (const rootY of validRootsY) {
221
+ const distance = Math.sqrt((rootX - point[0]) ** 2 + (rootY - point[1]) ** 2);
222
+ if (distance < minDistance) {
223
+ minDistance = distance;
224
+ closestT = (rootX + rootY) / 2; // Use the average for a smoother result
225
+ }
226
+ }
227
+ }
228
+ return closestT;
229
+ };
230
+ export const cubicBezierDistance = (point, controlPoints) => {
231
+ // Calculate the closest point on the Bezier curve to the given point
232
+ const t = findClosestParameter(point, controlPoints);
233
+ // Calculate the coordinates of the closest point on the curve
234
+ const [closestX, closestY] = cubicBezierPoint(t, controlPoints);
235
+ // Calculate the distance between the given point and the closest point on the curve
236
+ const distance = Math.sqrt((point[0] - closestX) ** 2 + (point[1] - closestY) ** 2);
237
+ return distance;
238
+ };
239
+ /**
240
+ * polygons
241
+ */
242
+ export const polygonRotate = (polygon, angle, origin) => {
243
+ return polygon.map((p) => pointRotate(p, angle, origin));
244
+ };
245
+ export const polygonBounds = (polygon) => {
246
+ let xMin = Infinity;
247
+ let xMax = -Infinity;
248
+ let yMin = Infinity;
249
+ let yMax = -Infinity;
250
+ for (let i = 0, l = polygon.length; i < l; i++) {
251
+ const p = polygon[i];
252
+ const x = p[0];
253
+ const y = p[1];
254
+ if (x != null && isFinite(x) && y != null && isFinite(y)) {
255
+ if (x < xMin) {
256
+ xMin = x;
257
+ }
258
+ if (x > xMax) {
259
+ xMax = x;
260
+ }
261
+ if (y < yMin) {
262
+ yMin = y;
263
+ }
264
+ if (y > yMax) {
265
+ yMax = y;
266
+ }
267
+ }
268
+ }
269
+ return [
270
+ [xMin, yMin],
271
+ [xMax, yMax],
272
+ ];
273
+ };
274
+ export const polygonCentroid = (vertices) => {
275
+ let a = 0;
276
+ let x = 0;
277
+ let y = 0;
278
+ const l = vertices.length;
279
+ for (let i = 0; i < l; i++) {
280
+ const s = i === l - 1 ? 0 : i + 1;
281
+ const v0 = vertices[i];
282
+ const v1 = vertices[s];
283
+ const f = v0[0] * v1[1] - v1[0] * v0[1];
284
+ a += f;
285
+ x += (v0[0] + v1[0]) * f;
286
+ y += (v0[1] + v1[1]) * f;
287
+ }
288
+ const d = a * 3;
289
+ return [x / d, y / d];
290
+ };
291
+ export const polygonScale = (polygon, scale, origin) => {
292
+ if (!origin) {
293
+ origin = polygonCentroid(polygon);
294
+ }
295
+ const p = [];
296
+ for (let i = 0, l = polygon.length; i < l; i++) {
297
+ const v = polygon[i];
298
+ const d = lineLength([origin, v]);
299
+ const a = lineAngle([origin, v]);
300
+ p[i] = pointTranslate(origin, a, d * scale);
301
+ }
302
+ return p;
303
+ };
304
+ export const polygonScaleX = (polygon, scale, origin) => {
305
+ if (!origin) {
306
+ origin = polygonCentroid(polygon);
307
+ }
308
+ const p = [];
309
+ for (let i = 0, l = polygon.length; i < l; i++) {
310
+ const v = polygon[i];
311
+ const d = lineLength([origin, v]);
312
+ const a = lineAngle([origin, v]);
313
+ const t = pointTranslate(origin, a, d * scale);
314
+ p[i] = [t[0], v[1]];
315
+ }
316
+ return p;
317
+ };
318
+ export const polygonScaleY = (polygon, scale, origin) => {
319
+ if (!origin) {
320
+ origin = polygonCentroid(polygon);
321
+ }
322
+ const p = [];
323
+ for (let i = 0, l = polygon.length; i < l; i++) {
324
+ const v = polygon[i];
325
+ const d = lineLength([origin, v]);
326
+ const a = lineAngle([origin, v]);
327
+ const t = pointTranslate(origin, a, d * scale);
328
+ p[i] = [v[0], t[1]];
329
+ }
330
+ return p;
331
+ };
332
+ export const polygonReflectX = (polygon, reflectFactor = 1) => {
333
+ const [[min], [max]] = polygonBounds(polygon);
334
+ const p = [];
335
+ for (let i = 0, l = polygon.length; i < l; i++) {
336
+ const [x, y] = polygon[i];
337
+ const r = [min + max - x, y];
338
+ if (reflectFactor === 0) {
339
+ p[i] = [x, y];
340
+ }
341
+ else if (reflectFactor === 1) {
342
+ p[i] = r;
343
+ }
344
+ else {
345
+ const t = lineInterpolate([[x, y], r]);
346
+ p[i] = t(Math.max(Math.min(reflectFactor, 1), 0));
347
+ }
348
+ }
349
+ return p;
350
+ };
351
+ export const polygonReflectY = (polygon, reflectFactor = 1) => {
352
+ const [[, min], [, max]] = polygonBounds(polygon);
353
+ const p = [];
354
+ for (let i = 0, l = polygon.length; i < l; i++) {
355
+ const [x, y] = polygon[i];
356
+ const r = [x, min + max - y];
357
+ if (reflectFactor === 0) {
358
+ p[i] = [x, y];
359
+ }
360
+ else if (reflectFactor === 1) {
361
+ p[i] = r;
362
+ }
363
+ else {
364
+ const t = lineInterpolate([[x, y], r]);
365
+ p[i] = t(Math.max(Math.min(reflectFactor, 1), 0));
366
+ }
367
+ }
368
+ return p;
369
+ };
370
+ export const polygonTranslate = (polygon, angle, distance) => {
371
+ return polygon.map((p) => pointTranslate(p, angle, distance));
372
+ };
373
+ /**
374
+ * ellipses
375
+ */
376
+ export const ellipseAxes = (ellipse) => {
377
+ const widthGreaterThanHeight = ellipse.halfWidth > ellipse.halfHeight;
378
+ const majorAxis = widthGreaterThanHeight
379
+ ? ellipse.halfWidth * 2
380
+ : ellipse.halfHeight * 2;
381
+ const minorAxis = widthGreaterThanHeight
382
+ ? ellipse.halfHeight * 2
383
+ : ellipse.halfWidth * 2;
384
+ return {
385
+ majorAxis,
386
+ minorAxis,
387
+ };
388
+ };
389
+ export const ellipseFocusToCenter = (ellipse) => {
390
+ const { majorAxis, minorAxis } = ellipseAxes(ellipse);
391
+ return Math.sqrt(majorAxis ** 2 - minorAxis ** 2);
392
+ };
393
+ export const ellipseExtremes = (ellipse) => {
394
+ const { center, angle } = ellipse;
395
+ const { majorAxis, minorAxis } = ellipseAxes(ellipse);
396
+ const cos = Math.cos(angle);
397
+ const sin = Math.sin(angle);
398
+ const sqSum = majorAxis ** 2 + minorAxis ** 2;
399
+ const sqDiff = (majorAxis ** 2 - minorAxis ** 2) * Math.cos(2 * angle);
400
+ const yMax = Math.sqrt((sqSum - sqDiff) / 2);
401
+ const xAtYMax = (yMax * sqSum * sin * cos) /
402
+ (majorAxis ** 2 * sin ** 2 + minorAxis ** 2 * cos ** 2);
403
+ const xMax = Math.sqrt((sqSum + sqDiff) / 2);
404
+ const yAtXMax = (xMax * sqSum * sin * cos) /
405
+ (majorAxis ** 2 * cos ** 2 + minorAxis ** 2 * sin ** 2);
406
+ return [
407
+ pointAdd([xAtYMax, yMax], center),
408
+ pointAdd(pointInverse([xAtYMax, yMax]), center),
409
+ pointAdd([xMax, yAtXMax], center),
410
+ pointAdd([xMax, yAtXMax], center),
411
+ ];
412
+ };
413
+ export const pointRelativeToCenter = (point, center, angle) => {
414
+ const translated = pointAdd(point, pointInverse(center));
415
+ const rotated = pointRotate(translated, -angleToDegrees(angle));
416
+ return rotated;
417
+ };
418
+ /**
419
+ * relationships
420
+ */
421
+ const topPointFirst = (line) => {
422
+ return line[1][1] > line[0][1] ? line : [line[1], line[0]];
423
+ };
424
+ export const pointLeftofLine = (point, line) => {
425
+ const t = topPointFirst(line);
426
+ return cross(point, t[1], t[0]) < 0;
427
+ };
428
+ export const pointRightofLine = (point, line) => {
429
+ const t = topPointFirst(line);
430
+ return cross(point, t[1], t[0]) > 0;
431
+ };
432
+ export const distanceToSegment = (point, line) => {
433
+ const [x, y] = point;
434
+ const [[x1, y1], [x2, y2]] = line;
435
+ const A = x - x1;
436
+ const B = y - y1;
437
+ const C = x2 - x1;
438
+ const D = y2 - y1;
439
+ const dot = A * C + B * D;
440
+ const len_sq = C * C + D * D;
441
+ let param = -1;
442
+ if (len_sq !== 0) {
443
+ param = dot / len_sq;
444
+ }
445
+ let xx;
446
+ let yy;
447
+ if (param < 0) {
448
+ xx = x1;
449
+ yy = y1;
450
+ }
451
+ else if (param > 1) {
452
+ xx = x2;
453
+ yy = y2;
454
+ }
455
+ else {
456
+ xx = x1 + param * C;
457
+ yy = y1 + param * D;
458
+ }
459
+ const dx = x - xx;
460
+ const dy = y - yy;
461
+ return Math.sqrt(dx * dx + dy * dy);
462
+ };
463
+ export const pointOnLine = (point, line, threshold = DEFAULT_THRESHOLD) => {
464
+ const distance = distanceToSegment(point, line);
465
+ if (distance === 0) {
466
+ return true;
467
+ }
468
+ return distance < threshold;
469
+ };
470
+ export const pointOnPolyline = (point, polyline, threshold = DEFAULT_THRESHOLD) => {
471
+ return polyline.some((line) => pointOnLine(point, line, threshold));
472
+ };
473
+ export const lineIntersectsLine = (lineA, lineB) => {
474
+ const [[a0x, a0y], [a1x, a1y]] = lineA;
475
+ const [[b0x, b0y], [b1x, b1y]] = lineB;
476
+ // shared points
477
+ if (a0x === b0x && a0y === b0y) {
478
+ return true;
479
+ }
480
+ if (a1x === b1x && a1y === b1y) {
481
+ return true;
482
+ }
483
+ // point on line
484
+ if (pointOnLine(lineA[0], lineB) || pointOnLine(lineA[1], lineB)) {
485
+ return true;
486
+ }
487
+ if (pointOnLine(lineB[0], lineA) || pointOnLine(lineB[1], lineA)) {
488
+ return true;
489
+ }
490
+ const denom = (b1y - b0y) * (a1x - a0x) - (b1x - b0x) * (a1y - a0y);
491
+ if (denom === 0) {
492
+ return false;
493
+ }
494
+ const deltaY = a0y - b0y;
495
+ const deltaX = a0x - b0x;
496
+ const numer0 = (b1x - b0x) * deltaY - (b1y - b0y) * deltaX;
497
+ const numer1 = (a1x - a0x) * deltaY - (a1y - a0y) * deltaX;
498
+ const quotA = numer0 / denom;
499
+ const quotB = numer1 / denom;
500
+ return quotA > 0 && quotA < 1 && quotB > 0 && quotB < 1;
501
+ };
502
+ export const lineIntersectsPolygon = (line, polygon) => {
503
+ let intersects = false;
504
+ const closed = close(polygon);
505
+ for (let i = 0, l = closed.length - 1; i < l; i++) {
506
+ const v0 = closed[i];
507
+ const v1 = closed[i + 1];
508
+ if (lineIntersectsLine(line, [v0, v1]) ||
509
+ (pointOnLine(v0, line) && pointOnLine(v1, line))) {
510
+ intersects = true;
511
+ break;
512
+ }
513
+ }
514
+ return intersects;
515
+ };
516
+ export const pointInBezierEquation = (p0, p1, p2, p3, [mx, my], lineThreshold) => {
517
+ // B(t) = p0 * (1-t)^3 + 3p1 * t * (1-t)^2 + 3p2 * t^2 * (1-t) + p3 * t^3
518
+ const equation = (t, idx) => Math.pow(1 - t, 3) * p3[idx] +
519
+ 3 * t * Math.pow(1 - t, 2) * p2[idx] +
520
+ 3 * Math.pow(t, 2) * (1 - t) * p1[idx] +
521
+ p0[idx] * Math.pow(t, 3);
522
+ const lineSegmentPoints = [];
523
+ let t = 0;
524
+ while (t <= 1.0) {
525
+ const tx = equation(t, 0);
526
+ const ty = equation(t, 1);
527
+ const diff = Math.sqrt(Math.pow(tx - mx, 2) + Math.pow(ty - my, 2));
528
+ if (diff < lineThreshold) {
529
+ return true;
530
+ }
531
+ lineSegmentPoints.push([tx, ty]);
532
+ t += 0.1;
533
+ }
534
+ // check the distance from line segments to the given point
535
+ return false;
536
+ };
537
+ export const cubicBezierEquation = (curve) => {
538
+ const [p0, p1, p2, p3] = curve;
539
+ // B(t) = p0 * (1-t)^3 + 3p1 * t * (1-t)^2 + 3p2 * t^2 * (1-t) + p3 * t^3
540
+ return (t, idx) => Math.pow(1 - t, 3) * p3[idx] +
541
+ 3 * t * Math.pow(1 - t, 2) * p2[idx] +
542
+ 3 * Math.pow(t, 2) * (1 - t) * p1[idx] +
543
+ p0[idx] * Math.pow(t, 3);
544
+ };
545
+ export const polyLineFromCurve = (curve, segments = 10) => {
546
+ const equation = cubicBezierEquation(curve);
547
+ let startingPoint = [equation(0, 0), equation(0, 1)];
548
+ const lineSegments = [];
549
+ let t = 0;
550
+ const increment = 1 / segments;
551
+ for (let i = 0; i < segments; i++) {
552
+ t += increment;
553
+ if (t <= 1) {
554
+ const nextPoint = [equation(t, 0), equation(t, 1)];
555
+ lineSegments.push([startingPoint, nextPoint]);
556
+ startingPoint = nextPoint;
557
+ }
558
+ }
559
+ return lineSegments;
560
+ };
561
+ export const pointOnCurve = (point, curve, threshold = DEFAULT_THRESHOLD) => {
562
+ return pointOnPolyline(point, polyLineFromCurve(curve), threshold);
563
+ };
564
+ export const pointOnPolycurve = (point, polycurve, threshold = DEFAULT_THRESHOLD) => {
565
+ return polycurve.some((curve) => pointOnCurve(point, curve, threshold));
566
+ };
567
+ export const pointInPolygon = (point, polygon) => {
568
+ const x = point[0];
569
+ const y = point[1];
570
+ let inside = false;
571
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
572
+ const xi = polygon[i][0];
573
+ const yi = polygon[i][1];
574
+ const xj = polygon[j][0];
575
+ const yj = polygon[j][1];
576
+ if (((yi > y && yj <= y) || (yi <= y && yj > y)) &&
577
+ x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) {
578
+ inside = !inside;
579
+ }
580
+ }
581
+ return inside;
582
+ };
583
+ export const pointOnPolygon = (point, polygon, threshold = DEFAULT_THRESHOLD) => {
584
+ let on = false;
585
+ const closed = close(polygon);
586
+ for (let i = 0, l = closed.length - 1; i < l; i++) {
587
+ if (pointOnLine(point, [closed[i], closed[i + 1]], threshold)) {
588
+ on = true;
589
+ break;
590
+ }
591
+ }
592
+ return on;
593
+ };
594
+ export const polygonInPolygon = (polygonA, polygonB) => {
595
+ let inside = true;
596
+ const closed = close(polygonA);
597
+ for (let i = 0, l = closed.length - 1; i < l; i++) {
598
+ const v0 = closed[i];
599
+ // Points test
600
+ if (!pointInPolygon(v0, polygonB)) {
601
+ inside = false;
602
+ break;
603
+ }
604
+ // Lines test
605
+ if (lineIntersectsPolygon([v0, closed[i + 1]], polygonB)) {
606
+ inside = false;
607
+ break;
608
+ }
609
+ }
610
+ return inside;
611
+ };
612
+ export const polygonIntersectPolygon = (polygonA, polygonB) => {
613
+ let intersects = false;
614
+ let onCount = 0;
615
+ const closed = close(polygonA);
616
+ for (let i = 0, l = closed.length - 1; i < l; i++) {
617
+ const v0 = closed[i];
618
+ const v1 = closed[i + 1];
619
+ if (lineIntersectsPolygon([v0, v1], polygonB)) {
620
+ intersects = true;
621
+ break;
622
+ }
623
+ if (pointOnPolygon(v0, polygonB)) {
624
+ ++onCount;
625
+ }
626
+ if (onCount === 2) {
627
+ intersects = true;
628
+ break;
629
+ }
630
+ }
631
+ return intersects;
632
+ };
633
+ const distanceToEllipse = (point, ellipse) => {
634
+ const { angle, halfWidth, halfHeight, center } = ellipse;
635
+ const a = halfWidth;
636
+ const b = halfHeight;
637
+ const [rotatedPointX, rotatedPointY] = pointRelativeToCenter(point, center, angle);
638
+ const px = Math.abs(rotatedPointX);
639
+ const py = Math.abs(rotatedPointY);
640
+ let tx = 0.707;
641
+ let ty = 0.707;
642
+ for (let i = 0; i < 3; i++) {
643
+ const x = a * tx;
644
+ const y = b * ty;
645
+ const ex = ((a * a - b * b) * tx ** 3) / a;
646
+ const ey = ((b * b - a * a) * ty ** 3) / b;
647
+ const rx = x - ex;
648
+ const ry = y - ey;
649
+ const qx = px - ex;
650
+ const qy = py - ey;
651
+ const r = Math.hypot(ry, rx);
652
+ const q = Math.hypot(qy, qx);
653
+ tx = Math.min(1, Math.max(0, ((qx * r) / q + ex) / a));
654
+ ty = Math.min(1, Math.max(0, ((qy * r) / q + ey) / b));
655
+ const t = Math.hypot(ty, tx);
656
+ tx /= t;
657
+ ty /= t;
658
+ }
659
+ const [minX, minY] = [
660
+ a * tx * Math.sign(rotatedPointX),
661
+ b * ty * Math.sign(rotatedPointY),
662
+ ];
663
+ return distanceToPoint([rotatedPointX, rotatedPointY], [minX, minY]);
664
+ };
665
+ export const pointOnEllipse = (point, ellipse, threshold = DEFAULT_THRESHOLD) => {
666
+ return distanceToEllipse(point, ellipse) <= threshold;
667
+ };
668
+ export const pointInEllipse = (point, ellipse) => {
669
+ const { center, angle, halfWidth, halfHeight } = ellipse;
670
+ const [rotatedPointX, rotatedPointY] = pointRelativeToCenter(point, center, angle);
671
+ return ((rotatedPointX / halfWidth) * (rotatedPointX / halfWidth) +
672
+ (rotatedPointY / halfHeight) * (rotatedPointY / halfHeight) <=
673
+ 1);
674
+ };