toosoon-utils 4.0.6 → 4.1.1

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 (45) hide show
  1. package/README.md +716 -88
  2. package/lib/constants.d.ts +2 -0
  3. package/lib/constants.js +5 -3
  4. package/lib/extras/_wip/pool.d.ts +56 -0
  5. package/lib/extras/_wip/pool.js +67 -0
  6. package/lib/{classes/color-scale.d.ts → extras/color-scale/ColorScale.d.ts} +1 -1
  7. package/lib/{classes/color-scale.js → extras/color-scale/ColorScale.js} +2 -2
  8. package/lib/extras/curves/ArcCurve.d.ts +19 -0
  9. package/lib/extras/curves/ArcCurve.js +21 -0
  10. package/lib/extras/curves/CatmulRomCurve.d.ts +62 -0
  11. package/lib/extras/curves/CatmulRomCurve.js +75 -0
  12. package/lib/extras/curves/CubicBezierCurve.d.ts +62 -0
  13. package/lib/extras/curves/CubicBezierCurve.js +75 -0
  14. package/lib/extras/curves/Curve.d.ts +95 -0
  15. package/lib/extras/curves/Curve.js +174 -0
  16. package/lib/extras/curves/EllipseCurve.d.ts +63 -0
  17. package/lib/extras/curves/EllipseCurve.js +76 -0
  18. package/lib/extras/curves/LineCurve.d.ts +51 -0
  19. package/lib/extras/curves/LineCurve.js +71 -0
  20. package/lib/extras/curves/Path.d.ts +164 -0
  21. package/lib/extras/curves/Path.js +367 -0
  22. package/lib/extras/curves/PathCurve.d.ts +69 -0
  23. package/lib/extras/curves/PathCurve.js +148 -0
  24. package/lib/extras/curves/PathSVG.d.ts +41 -0
  25. package/lib/extras/curves/PathSVG.js +135 -0
  26. package/lib/extras/curves/PolylineCurve.d.ts +27 -0
  27. package/lib/extras/curves/PolylineCurve.js +39 -0
  28. package/lib/extras/curves/QuadraticBezierCurve.d.ts +52 -0
  29. package/lib/extras/curves/QuadraticBezierCurve.js +63 -0
  30. package/lib/extras/curves/SplineCurve.d.ts +24 -0
  31. package/lib/extras/curves/SplineCurve.js +38 -0
  32. package/lib/extras/curves/index.d.ts +12 -0
  33. package/lib/extras/curves/index.js +12 -0
  34. package/lib/{classes/frame-rate.js → extras/frame-rate/FrameRate.js} +1 -1
  35. package/lib/geometry.d.ts +87 -68
  36. package/lib/geometry.js +144 -140
  37. package/lib/prng.js +2 -1
  38. package/lib/random.d.ts +13 -13
  39. package/lib/random.js +14 -14
  40. package/lib/tsconfig.tsbuildinfo +1 -1
  41. package/lib/types.d.ts +3 -10
  42. package/package.json +4 -3
  43. /package/lib/{classes/_pool.d.ts → extras/_/pool.d.ts} +0 -0
  44. /package/lib/{classes/_pool.js → extras/_/pool.js} +0 -0
  45. /package/lib/{classes/frame-rate.d.ts → extras/frame-rate/FrameRate.d.ts} +0 -0
@@ -0,0 +1,367 @@
1
+ import { EPSILON, PI } from '../../../src/constants';
2
+ import { isCoincident, isCollinear } from '../../../src/geometry';
3
+ import LineCurve from './LineCurve';
4
+ import PolylineCurve from './PolylineCurve';
5
+ import QuadraticBezierCurve from './QuadraticBezierCurve';
6
+ import CubicBezierCurve from './CubicBezierCurve';
7
+ import CatmullRomCurve from './CatmulRomCurve';
8
+ import SplineCurve from './SplineCurve';
9
+ import EllipseCurve from './EllipseCurve';
10
+ import ArcCurve from './ArcCurve';
11
+ import PathCurve from './PathCurve';
12
+ /**
13
+ * Utility class for manipulating connected curves
14
+ *
15
+ * It works by providing methods for creating curves similar to the 2D Canvas API
16
+ *
17
+ *
18
+ * @exports
19
+ * @class CurvePath
20
+ * @extends PathCurve
21
+ */
22
+ export default class Path extends PathCurve {
23
+ /**
24
+ * Path current offset
25
+ */
26
+ currentPosition = [NaN, NaN];
27
+ /**
28
+ * Create a path from the given list of points
29
+ *
30
+ * @param {Point[]} points Array of points defining the path
31
+ * @param {Point[]} type Type of curve used for creating the path
32
+ * @return {this}
33
+ */
34
+ setFromPoints(points, type) {
35
+ this.moveTo(...points[0]);
36
+ if (type === 'polyline')
37
+ return this.polylineTo(points);
38
+ if (type === 'spline')
39
+ return this.splineTo(points);
40
+ for (let i = 1, l = points.length; i < l; i++) {
41
+ this.lineTo(...points[i]);
42
+ }
43
+ return this;
44
+ }
45
+ /**
46
+ * Move {@link Path#currentPosition} to the coordinates specified by `x` and `y`
47
+ *
48
+ * @param {number} x X-axis coordinate of the point
49
+ * @param {number} y Y-axis coordinate of the point
50
+ * @return {this}
51
+ */
52
+ moveTo(x, y) {
53
+ this._setCurrentPosition(x, y);
54
+ return this;
55
+ }
56
+ /**
57
+ * Draw a line from the current position to the position specified by `x` and `y`
58
+ * Add an instance of {@link LineCurve} to the path
59
+ *
60
+ * @param {number} x X-axis coordinate of the point
61
+ * @param {number} y Y-axis coordinate of the point
62
+ * @return {this}
63
+ */
64
+ lineTo(x, y) {
65
+ if (this.currentPosition.every(isNaN))
66
+ return this.moveTo(x, y);
67
+ const curve = new LineCurve(...this.currentPosition, x, y);
68
+ this.add(curve);
69
+ this._setCurrentPosition(x, y);
70
+ return this;
71
+ }
72
+ /**
73
+ * Draw a Polyline curve from the current position through the given points
74
+ * Add an instance of {@link PolylineCurve} to the path
75
+ *
76
+ * @param {Point[]} points Array of points defining the curve
77
+ * @returns {this}
78
+ */
79
+ polylineTo(points) {
80
+ if (this.currentPosition.every(isNaN))
81
+ this.moveTo(...points[0]);
82
+ const curve = new PolylineCurve([this.currentPosition].concat(points));
83
+ this.add(curve);
84
+ this._setCurrentPosition(...points[points.length - 1]);
85
+ return this;
86
+ }
87
+ /**
88
+ * Draw an Arc curve from the current position, tangential to the 2 segments created by both control points
89
+ * Add an instance of {@link ArcCurve} to the path
90
+ *
91
+ * @param {number} x1 X-axis coordinate of the first control point
92
+ * @param {number} y1 Y-axis coordinate of the first control point
93
+ * @param {number} x2 X-axis coordinate of the second control point
94
+ * @param {number} y2 Y-axis coordinate of the second control point
95
+ * @param {number} radius Arc radius (Must be non-negative)
96
+ * @returns {this}
97
+ */
98
+ arcTo(x1, y1, x2, y2, radius) {
99
+ if (this.currentPosition.every(isNaN))
100
+ this.moveTo(x1, y1);
101
+ if (radius < 0) {
102
+ console.warn(`Path.arcTo()`, `Arc radius must be non-negative.`, radius);
103
+ return this.lineTo(x2, y2);
104
+ }
105
+ const x0 = this.currentPosition[0];
106
+ const y0 = this.currentPosition[1];
107
+ // (x1, y1) is coincident with (x0, y0)
108
+ if (isCoincident(x0, y0, x1, y1)) {
109
+ return this;
110
+ }
111
+ // (x0, y0), (x1 ,y1) and (x2, y2) are collinear
112
+ if (isCollinear(x0, y0, x1, y1, x2, y2) || radius <= EPSILON) {
113
+ return this.lineTo(x2, y2);
114
+ }
115
+ const dx01 = x0 - x1;
116
+ const dy01 = y2 - y1;
117
+ const dx20 = x2 - x0;
118
+ const dy20 = y2 - y0;
119
+ const dx21 = x2 - x1;
120
+ const dy21 = y2 - y1;
121
+ const l20 = Math.hypot(dx20, dy20);
122
+ const l21 = Math.hypot(dx21, dy21);
123
+ const l01 = Math.hypot(dx01, dy01);
124
+ const l = radius * Math.tan((PI - Math.acos((l21 + l01 - l20) / (2 * l21 * l01))) / 2);
125
+ const t01 = l / l01;
126
+ const t21 = l / l21;
127
+ if (Math.abs(t01 - 1) > EPSILON) {
128
+ return this.lineTo(x1 + t01 * dx01, y1 + t01 * dy01);
129
+ }
130
+ const startX = x1 + t01 * dx01;
131
+ const startY = y1 + t01 * dy01;
132
+ const endX = x1 + t21 * dx21;
133
+ const endY = y1 + t21 * dy21;
134
+ const normalX = dy01 * -1;
135
+ const normalY = dx01;
136
+ const centerX = (startX + endX) / 2;
137
+ const centerY = (startY + endY) / 2;
138
+ const d = Math.sqrt(radius ** 2 - l ** 2);
139
+ const cx = centerX + (normalX / Math.hypot(normalX, normalY)) * d;
140
+ const cy = centerY + (normalY / Math.hypot(normalX, normalY)) * d;
141
+ const startAngle = Math.atan2(startY - cy, startX - cx);
142
+ const endAngle = Math.atan2(endY - cy, endX - cx);
143
+ const counterclockwise = dy01 * dx21 < dx01 * dy21;
144
+ this.arc(x1 + t21 * dx21, y1 + t21 * dy21, radius, startAngle, endAngle, counterclockwise);
145
+ return this;
146
+ }
147
+ /**
148
+ * Draw a Quadratic Bézier curve from the current position to the end point specified by `x` and `y`, using the control point specified by `cpx` and `cpy`
149
+ * Add an instance of {@link QuadraticBezierCurve} to the path
150
+ *
151
+ * @param {number} cpx X-axis coordinate of the control point
152
+ * @param {number} cpy Y-axis coordinate of the control point
153
+ * @param {number} x2 X-axis coordinate of the end point
154
+ * @param {number} y2 Y-axis coordinate of the end point
155
+ * @return {this}
156
+ */
157
+ quadraticCurveTo(cpx, cpy, x2, y2) {
158
+ if (this.currentPosition.every(isNaN))
159
+ this.moveTo(cpx, cpy);
160
+ const curve = new QuadraticBezierCurve(...this.currentPosition, cpx, cpy, x2, y2);
161
+ this.add(curve);
162
+ this._setCurrentPosition(x2, y2);
163
+ return this;
164
+ }
165
+ /**
166
+ * Draw a Cubic Bézier curve from the current position to the end point specified by `x` and `y`, using the control point specified by (`cp1x`, `cp1y`) and (`cp2x`, `cp2y`)
167
+ * Add an instance of {@link CubicBezierCurve} to the path
168
+ *
169
+ * @param {number} cp1x X-axis coordinate of the first control point
170
+ * @param {number} cp1y Y-axis coordinate of the first control point
171
+ * @param {number} cp2x X-axis coordinate of the second control point
172
+ * @param {number} cp2y Y-axis coordinate of the second control point
173
+ * @param {number} x2 X-axis coordinate of the end point
174
+ * @param {number} y2 Y-axis coordinate of the end point
175
+ * @return {this}
176
+ */
177
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) {
178
+ if (this.currentPosition.every(isNaN))
179
+ this.moveTo(cp1x, cp1y);
180
+ const curve = new CubicBezierCurve(...this.currentPosition, cp1x, cp1y, cp2x, cp2y, x2, y2);
181
+ this.add(curve);
182
+ this._setCurrentPosition(x2, y2);
183
+ return this;
184
+ }
185
+ /**
186
+ * Draw a Catmul-Rom curve from the current position to the end point specified by `x` and `y`, using the control points specified by (`cp1x`, `cp1y`) and (`cp2x`, `cp2y`)
187
+ * Add an instance of {@link CatmullRomCurve} to the path
188
+ *
189
+ * @param {number} cp1x X-axis coordinate of the first control point
190
+ * @param {number} cp1y Y-axis coordinate of the first control point
191
+ * @param {number} cp2x X-axis coordinate of the second control point
192
+ * @param {number} cp2y Y-axis coordinate of the second control point
193
+ * @param {number} x2 X-axis coordinate of the end point
194
+ * @param {number} y2 Y-axis coordinate of the end point
195
+ * @return {this}
196
+ */
197
+ catmulRomCurveTo(cp1x, cp1y, cp2x, cp2y, x2, y2) {
198
+ if (this.currentPosition.every(isNaN))
199
+ this.moveTo(cp1x, cp1y);
200
+ const curve = new CatmullRomCurve(...this.currentPosition, cp1x, cp1y, cp2x, cp2y, x2, y2);
201
+ this.add(curve);
202
+ this._setCurrentPosition(x2, y2);
203
+ return this;
204
+ }
205
+ /**
206
+ * Draw a Spline curve from the current position through the given points
207
+ * Add an instance of {@link SplineCurve} to the path
208
+ *
209
+ * @param {Point[]} points Array of points defining the curve
210
+ * @return {this}
211
+ */
212
+ splineTo(points) {
213
+ if (this.currentPosition.every(isNaN))
214
+ this.moveTo(...points[0]);
215
+ const curve = new SplineCurve([this.currentPosition].concat(points));
216
+ this.add(curve);
217
+ this._setCurrentPosition(...points[points.length - 1]);
218
+ return this;
219
+ }
220
+ /**
221
+ * Draw an Ellispe curve which is centered at (`cx`, `cy`) position
222
+ * Add an instance of {@link EllipseCurve} to the path
223
+ *
224
+ * @param {number} cx X-axis coordinate of the center of the circle
225
+ * @param {number} cy Y-axis coordinate of the center of the circle
226
+ * @param {number} rx X-radius of the ellipse
227
+ * @param {number} ry Y-radius of the ellipse
228
+ * @param {number} [rotation] Rotation angle of the ellipse (in radians), counterclockwise from the positive X-axis
229
+ * @param {number} [startAngle] Start angle of the arc (in radians)
230
+ * @param {number} [endAngle] End angle of the arc (in radians)
231
+ * @param {boolean} [counterclockwise] Flag indicating the direction of the arc
232
+ * @return {this}
233
+ */
234
+ ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise) {
235
+ if (this.currentPosition.every(isNaN))
236
+ this.moveTo(cx, cy);
237
+ else if (!isCoincident(...this.currentPosition, cx, cy))
238
+ this.lineTo(cx, cy);
239
+ const curve = new EllipseCurve(cx, cy, rx, ry, rotation, startAngle, endAngle, counterclockwise);
240
+ this.add(curve);
241
+ this._setCurrentPosition(...curve.getPoint(1));
242
+ return this;
243
+ }
244
+ /**
245
+ * Draw an Arc curve which is centered at (`cx`, `cy`) position
246
+ * Add an instance of {@link ArcCurve} to the path
247
+ *
248
+ * @param {number} cx X-axis coordinate of the center of the circle
249
+ * @param {number} cy Y-axis coordinate of the center of the circle
250
+ * @param {number} radius Radius of the circle
251
+ * @param {number} [startAngle] Start angle of the arc (in radians)
252
+ * @param {number} [endAngle] End angle of the arc (in radians)
253
+ * @param {boolean} [counterclockwise] Flag indicating the direction of the arc
254
+ * @return {this}
255
+ */
256
+ arc(cx, cy, radius, startAngle, endAngle, counterclockwise) {
257
+ if (this.currentPosition.every(isNaN))
258
+ this.moveTo(cx, cy);
259
+ else if (!isCoincident(...this.currentPosition, cx, cy))
260
+ this.lineTo(cx, cy);
261
+ const curve = new ArcCurve(cx, cy, radius, startAngle, endAngle, counterclockwise);
262
+ this.add(curve);
263
+ this._setCurrentPosition(...curve.getPoint(1));
264
+ return this;
265
+ }
266
+ /**
267
+ * Draw a rectangular path from the start position specified by `x` and `y` to the end position using `width` and `height`
268
+ * Add an instance of {@link PolylineCurve} to the path
269
+ *
270
+ * @param {number} x X-axis coordinate of the rectangle starting point
271
+ * @param {number} y Y-axis coordinate of the rectangle starting point
272
+ * @param {number} width Rectangle width (Positive values are to the right and negative to the left)
273
+ * @param {number} height Rectangle height (Positive values are down, and negative are up)
274
+ * @return {this}
275
+ */
276
+ rect(x, y, width, height) {
277
+ if (this.currentPosition.every(isNaN))
278
+ this.moveTo(x, y);
279
+ else if (!isCoincident(...this.currentPosition, x, y))
280
+ this.lineTo(x, y);
281
+ const curve = new PolylineCurve([
282
+ [x + width, y],
283
+ [x + width, y + height],
284
+ [x, y + height],
285
+ [x, y]
286
+ ]);
287
+ this.add(curve);
288
+ this._setCurrentPosition(x, y);
289
+ return this;
290
+ }
291
+ /**
292
+ * Draw a rounded rectangular path from the start position specified by `x` and `y` to the end position using `width` and `height`
293
+ *
294
+ * @param {number} x X-axis coordinate of the rectangle starting point
295
+ * @param {number} y Y-axis coordinate of the rectangle starting point
296
+ * @param {number} width Rectangle width (Positive values are to the right and negative to the left)
297
+ * @param {number} height Rectangle height (Positive values are down, and negative are up)
298
+ * @param {number|number[]} radius Radius of the circular arc to be used for the corners of the rectangle
299
+ * @return {this}
300
+ */
301
+ roundRect(x, y, width, height, radius) {
302
+ let topLeftRadius = typeof radius === 'number' ? radius : radius[0];
303
+ let topRightRadius = typeof radius === 'number' ? radius : radius[1] ?? radius[0];
304
+ let bottomRightRadius = typeof radius === 'number' ? radius : radius[2] ?? topLeftRadius;
305
+ let bottomLeftRadius = typeof radius === 'number' ? radius : radius[3] ?? topRightRadius;
306
+ const maxRadius = Math.min(width / 2, height / 2);
307
+ topLeftRadius = Math.min(topLeftRadius, maxRadius);
308
+ topRightRadius = Math.min(topRightRadius, maxRadius);
309
+ bottomRightRadius = Math.min(bottomRightRadius, maxRadius);
310
+ bottomLeftRadius = Math.min(bottomLeftRadius, maxRadius);
311
+ if (this.currentPosition.every(isNaN))
312
+ this.moveTo(x + topLeftRadius, y);
313
+ else if (!isCoincident(...this.currentPosition, x, y))
314
+ this.lineTo(x + topLeftRadius, y);
315
+ // Top-Right corner
316
+ if (topRightRadius > 0) {
317
+ this.lineTo(x + width - topRightRadius, y);
318
+ this.arcTo(x + width, y, x + width, y + topRightRadius, topRightRadius);
319
+ }
320
+ else {
321
+ this.lineTo(x + width, y);
322
+ }
323
+ // Bottom-Right corner
324
+ if (bottomRightRadius > 0) {
325
+ this.lineTo(x + width, y + height - bottomRightRadius);
326
+ this.arcTo(x + width, y + height, x + width - bottomRightRadius, y + height, bottomRightRadius);
327
+ }
328
+ else {
329
+ this.lineTo(x + width, y + height);
330
+ }
331
+ // Bottom-Left corner
332
+ if (bottomLeftRadius > 0) {
333
+ this.lineTo(x + bottomLeftRadius, y + height);
334
+ this.arcTo(x, y + height, x, y + height - bottomLeftRadius, bottomLeftRadius);
335
+ }
336
+ else {
337
+ this.lineTo(x, y + height);
338
+ }
339
+ // Top-Left corner
340
+ if (topLeftRadius > 0) {
341
+ this.lineTo(x, y + topLeftRadius);
342
+ this.arcTo(x, y, x + topLeftRadius, y, topLeftRadius);
343
+ }
344
+ else {
345
+ this.lineTo(x, y);
346
+ }
347
+ return this;
348
+ }
349
+ /**
350
+ * Add a line curve to close the curve path
351
+ * Add an instance of {@link LineCurve} to the path
352
+ */
353
+ closePath() {
354
+ super.closePath();
355
+ const endPoint = this.curves[this.curves.length - 1]?.getPoint(1);
356
+ this._setCurrentPosition(...endPoint);
357
+ return this;
358
+ }
359
+ // public transform() {}
360
+ // public translate(x: number, y: number) {}
361
+ // public rotate(angle: number) {}
362
+ // public restore() {}
363
+ _setCurrentPosition(x, y) {
364
+ this.currentPosition[0] = x;
365
+ this.currentPosition[1] = y;
366
+ }
367
+ }
@@ -0,0 +1,69 @@
1
+ import type { Point } from '../../../src/types';
2
+ import Curve from './Curve';
3
+ /**
4
+ * Utility class for manipulating connected curves
5
+ *
6
+ * @exports
7
+ * @class PathCurve
8
+ * @extends Curve
9
+ */
10
+ export default class PathCurve extends Curve {
11
+ readonly type: string;
12
+ /**
13
+ * Array of curves composing the path
14
+ */
15
+ curves: Curve[];
16
+ /**
17
+ * Array of points composing the path
18
+ */
19
+ points: Point[];
20
+ autoClose: boolean;
21
+ /**
22
+ * Add a curve to this curve path
23
+ *
24
+ * @param {Curve} curve Curve to add
25
+ */
26
+ add(curve: Curve): void;
27
+ /**
28
+ * Interpolate a point on the curve path
29
+ *
30
+ * @param {number} t Normalized time value to interpolate
31
+ * @returns {Point|null} Interpolated coordinates on the curve
32
+ */
33
+ getPoint(t: number): Point;
34
+ /**
35
+ * Compute the curve shape into an array of points
36
+ *
37
+ * @param {number} [divisions=40] Number of divisions
38
+ * @returns {Point[]}
39
+ */
40
+ getPoints(divisions?: number): Point[];
41
+ /**
42
+ * Compute the curve shape into an array of equi-spaced points across the entire curve path
43
+ *
44
+ * @param {number} [divisions=40] Number of divisions
45
+ * @returns {Point[]}
46
+ */
47
+ getSpacedPoints(divisions?: number): Point[];
48
+ /**
49
+ * Compute the total arc length of the curve path
50
+ *
51
+ * @returns {number}
52
+ */
53
+ getLength(): number;
54
+ /**
55
+ * Compute the cumulative curve lengths of the curve path
56
+ *
57
+ * @returns {number[]}
58
+ */
59
+ getCurveLengths(): number[];
60
+ /**
61
+ * Update the cached cumulative segment lengths
62
+ */
63
+ updateArcLengths(): void;
64
+ /**
65
+ * Add a line curve to close the curve path
66
+ * Add an instance of {@link LineCurve} to the path
67
+ */
68
+ closePath(): void;
69
+ }
@@ -0,0 +1,148 @@
1
+ import { isCoincident } from '../../../src/geometry';
2
+ import Curve from './Curve';
3
+ import LineCurve from './LineCurve';
4
+ /**
5
+ * Utility class for manipulating connected curves
6
+ *
7
+ * @exports
8
+ * @class PathCurve
9
+ * @extends Curve
10
+ */
11
+ export default class PathCurve extends Curve {
12
+ type = 'PathCurve';
13
+ /**
14
+ * Array of curves composing the path
15
+ */
16
+ curves = [];
17
+ /**
18
+ * Array of points composing the path
19
+ */
20
+ points = [];
21
+ autoClose = false;
22
+ /**
23
+ * Add a curve to this curve path
24
+ *
25
+ * @param {Curve} curve Curve to add
26
+ */
27
+ add(curve) {
28
+ this.curves.push(curve);
29
+ }
30
+ /**
31
+ * Interpolate a point on the curve path
32
+ *
33
+ * @param {number} t Normalized time value to interpolate
34
+ * @returns {Point|null} Interpolated coordinates on the curve
35
+ */
36
+ getPoint(t) {
37
+ const d = t * this.getLength();
38
+ const curveLengths = this.getCurveLengths();
39
+ let i = 0;
40
+ while (i < curveLengths.length) {
41
+ if (curveLengths[i] >= d) {
42
+ const delta = curveLengths[i] - d;
43
+ const curve = this.curves[i];
44
+ const segmentLength = curve.getLength();
45
+ const u = segmentLength === 0 ? 0 : 1 - delta / segmentLength;
46
+ return curve.getPointAt(u);
47
+ }
48
+ i++;
49
+ }
50
+ console.warn(`PathCurve.getPoint()`, `No point found in curve.`, this);
51
+ return [0, 0];
52
+ }
53
+ /**
54
+ * Compute the curve shape into an array of points
55
+ *
56
+ * @param {number} [divisions=40] Number of divisions
57
+ * @returns {Point[]}
58
+ */
59
+ getPoints(divisions = 40) {
60
+ const curves = this.curves;
61
+ const points = [];
62
+ let lastPoint = null;
63
+ for (let i = 0; i < curves.length; i++) {
64
+ const curve = curves[i];
65
+ const resolution = curve.type === 'EllipseCurve'
66
+ ? divisions * 2
67
+ : curve.type === 'LineCurve'
68
+ ? 1
69
+ : curve.type === 'SplineCurve'
70
+ ? divisions * curve.points.length
71
+ : divisions;
72
+ const points = curve.getPoints(resolution);
73
+ for (let j = 0; j < points.length; j++) {
74
+ const point = points[j];
75
+ if (lastPoint && isCoincident(...lastPoint, ...point))
76
+ continue;
77
+ points.push(point);
78
+ lastPoint = point;
79
+ }
80
+ }
81
+ if (this.autoClose && !Curve.isClosed(points)) {
82
+ points.push(points[0]);
83
+ }
84
+ return points;
85
+ }
86
+ /**
87
+ * Compute the curve shape into an array of equi-spaced points across the entire curve path
88
+ *
89
+ * @param {number} [divisions=40] Number of divisions
90
+ * @returns {Point[]}
91
+ */
92
+ getSpacedPoints(divisions = 40) {
93
+ const points = [];
94
+ for (let i = 0; i <= divisions; i++) {
95
+ points.push(this.getPoint(i / divisions));
96
+ }
97
+ if (this.autoClose && !Curve.isClosed(points)) {
98
+ points.push(points[0]);
99
+ }
100
+ return points;
101
+ }
102
+ /**
103
+ * Compute the total arc length of the curve path
104
+ *
105
+ * @returns {number}
106
+ */
107
+ getLength() {
108
+ const lengths = this.getCurveLengths();
109
+ return lengths[lengths.length - 1];
110
+ }
111
+ /**
112
+ * Compute the cumulative curve lengths of the curve path
113
+ *
114
+ * @returns {number[]}
115
+ */
116
+ getCurveLengths() {
117
+ if (this._cacheArcLengths.length === this.curves.length) {
118
+ return this._cacheArcLengths;
119
+ }
120
+ const lengths = [];
121
+ let sums = 0;
122
+ for (let i = 0, l = this.curves.length; i < l; i++) {
123
+ sums += this.curves[i].getLength();
124
+ lengths.push(sums);
125
+ }
126
+ this._cacheArcLengths = lengths;
127
+ return lengths;
128
+ }
129
+ /**
130
+ * Update the cached cumulative segment lengths
131
+ */
132
+ updateArcLengths() {
133
+ this.needsUpdate = true;
134
+ this._cacheArcLengths = [];
135
+ this.getCurveLengths();
136
+ }
137
+ /**
138
+ * Add a line curve to close the curve path
139
+ * Add an instance of {@link LineCurve} to the path
140
+ */
141
+ closePath() {
142
+ const startPoint = this.curves[0]?.getPoint(0);
143
+ const endPoint = this.curves[this.curves.length - 1]?.getPoint(1);
144
+ if (!isCoincident(...startPoint, ...endPoint)) {
145
+ this.add(new LineCurve(...endPoint, ...startPoint));
146
+ }
147
+ }
148
+ }
@@ -0,0 +1,41 @@
1
+ import { Point } from '../../../src/types';
2
+ import Path from './Path';
3
+ /**
4
+ * Utility class for manipulating connected curves and generating SVG path
5
+ *
6
+ * It works by serializing 2D Canvas API to SVG path data
7
+ *
8
+ * @exports
9
+ * @class PathSVG
10
+ * @extends Path
11
+ */
12
+ export default class PathSVG extends Path {
13
+ /**
14
+ * Resolution used for Catmul-Rom curve and Spline curve approximations
15
+ */
16
+ readonly curveResolution: number;
17
+ constructor({ curveResolution }: {
18
+ curveResolution?: number;
19
+ });
20
+ private _commands;
21
+ moveTo(x: number, y: number): this;
22
+ lineTo(x: number, y: number): this;
23
+ polylineTo(points: Point[]): this;
24
+ quadraticCurveTo(cpx: number, cpy: number, x2: number, y2: number): this;
25
+ bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x2: number, y2: number): this;
26
+ catmulRomCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x2: number, y2: number): this;
27
+ splineTo(points: Point[]): this;
28
+ ellipse(cx: number, cy: number, rx: number, ry: number, rotation?: number, startAngle?: number, endAngle?: number, counterclockwise?: boolean): this;
29
+ arc(cx: number, cy: number, radius: number, startAngle?: number, endAngle?: number, counterclockwise?: boolean): this;
30
+ rect(x: number, y: number, width: number, height: number): this;
31
+ closePath(): this;
32
+ private _writeLineTo;
33
+ private _writeArc;
34
+ private _write;
35
+ /**
36
+ * Return SVG path data string
37
+ *
38
+ * @returns {string}
39
+ */
40
+ toString(): string;
41
+ }