svg-path-commander 2.1.0 → 2.1.2

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 (63) hide show
  1. package/README.md +63 -7
  2. package/dist/svg-path-commander.cjs +1 -1
  3. package/dist/svg-path-commander.cjs.map +1 -1
  4. package/dist/svg-path-commander.d.ts +314 -40
  5. package/dist/svg-path-commander.js +1 -1
  6. package/dist/svg-path-commander.js.map +1 -1
  7. package/dist/svg-path-commander.mjs +1085 -1032
  8. package/dist/svg-path-commander.mjs.map +1 -1
  9. package/package.json +8 -8
  10. package/src/convert/pathToAbsolute.ts +5 -88
  11. package/src/convert/pathToCurve.ts +22 -28
  12. package/src/convert/pathToRelative.ts +4 -78
  13. package/src/convert/pathToString.ts +40 -7
  14. package/src/index.ts +145 -58
  15. package/src/interface.ts +3 -2
  16. package/src/math/arcTools.ts +259 -80
  17. package/src/math/bezier.ts +58 -58
  18. package/src/math/cubicTools.ts +68 -25
  19. package/src/math/distanceSquareRoot.ts +3 -1
  20. package/src/math/lineTools.ts +42 -25
  21. package/src/math/midPoint.ts +3 -1
  22. package/src/math/polygonTools.ts +48 -0
  23. package/src/math/quadTools.ts +46 -25
  24. package/src/math/rotateVector.ts +3 -2
  25. package/src/math/roundTo.ts +7 -0
  26. package/src/parser/finalizeSegment.ts +11 -7
  27. package/src/parser/parsePathString.ts +4 -5
  28. package/src/parser/pathParser.ts +1 -1
  29. package/src/process/absolutizeSegment.ts +63 -0
  30. package/src/process/arcToCubic.ts +2 -2
  31. package/src/process/iterate.ts +58 -0
  32. package/src/process/lineToCubic.ts +1 -1
  33. package/src/process/normalizePath.ts +17 -28
  34. package/src/process/normalizeSegment.ts +55 -18
  35. package/src/process/optimizePath.ts +40 -60
  36. package/src/process/projection2d.ts +4 -3
  37. package/src/process/relativizeSegment.ts +59 -0
  38. package/src/process/reverseCurve.ts +8 -5
  39. package/src/process/reversePath.ts +88 -75
  40. package/src/process/roundPath.ts +18 -14
  41. package/src/process/roundSegment.ts +9 -0
  42. package/src/process/segmentToCubic.ts +10 -8
  43. package/src/process/shortenSegment.ts +26 -34
  44. package/src/process/splitCubic.ts +10 -9
  45. package/src/process/splitPath.ts +38 -4
  46. package/src/process/transformPath.ts +75 -73
  47. package/src/types.ts +30 -0
  48. package/src/util/getPathArea.ts +3 -3
  49. package/src/util/getPathBBox.ts +69 -19
  50. package/src/util/getPointAtLength.ts +100 -4
  51. package/src/util/getPropertiesAtLength.ts +3 -4
  52. package/src/util/getPropertiesAtPoint.ts +5 -5
  53. package/src/util/getTotalLength.ts +56 -4
  54. package/test/class.test.ts +17 -14
  55. package/test/fixtures/shapes.js +26 -26
  56. package/test/fixtures/simpleShapes.js +18 -18
  57. package/test/static.test.ts +54 -28
  58. package/cypress.config.ts +0 -29
  59. package/src/math/polygonArea.ts +0 -27
  60. package/src/math/polygonLength.ts +0 -21
  61. package/src/process/fixArc.ts +0 -23
  62. package/src/process/replaceArc.ts +0 -52
  63. package/src/util/pathFactory.ts +0 -130
package/src/interface.ts CHANGED
@@ -5,7 +5,8 @@ export type SegmentProperties = {
5
5
  index: number;
6
6
  length: number;
7
7
  lengthAtSegment: number;
8
- [key: string]: any;
8
+ // point: Point;
9
+ // [key: string]: any;
9
10
  };
10
11
 
11
12
  export type PointProperties = {
@@ -106,7 +107,7 @@ export type LengthFactory = {
106
107
  };
107
108
 
108
109
  export type Options = {
109
- round: 'auto' | 'off' | number;
110
+ round: 'off' | number;
110
111
  origin: number[];
111
112
  };
112
113
 
@@ -1,4 +1,4 @@
1
- import { default as getLineSegmentProperties } from './lineTools';
1
+ import { getPointAtLineLength } from './lineTools';
2
2
  import type { Point } from '../types';
3
3
 
4
4
  /**
@@ -8,59 +8,38 @@ import type { Point } from '../types';
8
8
  * @param theta the angle in radians
9
9
  * @returns the arc length
10
10
  */
11
- const ellipticalArcLength = (rx: number, ry: number, theta: number) => {
11
+ const arcLength = (rx: number, ry: number, theta: number) => {
12
12
  const halfTheta = theta / 2;
13
13
  const sinHalfTheta = Math.sin(halfTheta);
14
14
  const cosHalfTheta = Math.cos(halfTheta);
15
15
  const term1 = rx ** 2 * sinHalfTheta ** 2;
16
16
  const term2 = ry ** 2 * cosHalfTheta ** 2;
17
- const arcLength = Math.sqrt(term1 + term2) * theta;
18
- return Math.abs(arcLength);
17
+ const length = Math.sqrt(term1 + term2) * theta;
18
+ return Math.abs(length);
19
19
  };
20
20
 
21
21
  /**
22
- * Returns the most extreme points in an Arc segment.
23
- * @param x Center X coordinate of the ellipse arc
24
- * @param y Center Y coordinate of the ellipse arc
25
- * @param rx Radius on the X axis of the ellipse
26
- * @param ry Radius on the Y axis of the ellipse
27
- * @param rotation The ellipse rotation angle in radians
28
- * @param startAngle The ellipse start angle in radians
29
- * @param endAngle The ellipse end angle in radians
30
- * @see https://stackoverflow.com/questions/87734/how-do-you-calculate-the-axis-aligned-bounding-box-of-an-ellipse
22
+ * Find point on ellipse at given angle around ellipse (theta);
23
+ * @param cx the center X
24
+ * @param cy the center Y
25
+ * @param rx the radius X
26
+ * @param ry the radius Y
27
+ * @param alpha the arc rotation angle in radians
28
+ * @param theta the arc sweep angle in radians
29
+ * @returns a point around ellipse at given angle
31
30
  */
32
- const minmax = (
33
- x: number,
34
- y: number,
35
- rx: number,
36
- ry: number,
37
- rotation: number,
38
- startAngle: number,
39
- endAngle: number,
40
- ) => {
41
- const { cos, sin, min, max } = Math;
42
- const cosRotation = cos(rotation);
43
- const sinRotation = sin(rotation);
44
-
45
- // Rotate parametric equations
46
- const xRotated = (t: number) => {
47
- return x + rx * cos(t) * cosRotation - ry * sin(t) * sinRotation;
48
- };
49
- const yRotated = (t: number) => {
50
- return y + ry * sin(t) * cosRotation + rx * cos(t) * sinRotation;
51
- };
52
-
53
- // Evaluate at start and end angles
54
- const startX = xRotated(startAngle);
55
- const startY = yRotated(startAngle);
56
- const endX = xRotated(endAngle);
57
- const endY = yRotated(endAngle);
31
+ const arcPoint = (cx: number, cy: number, rx: number, ry: number, alpha: number, theta: number) => {
32
+ const { sin, cos } = Math;
33
+ // theta is angle in radians around arc
34
+ // alpha is angle of rotation of ellipse in radians
35
+ const cosA = cos(alpha);
36
+ const sinA = sin(alpha);
37
+ const x = rx * cos(theta);
38
+ const y = ry * sin(theta);
58
39
 
59
- // Find minimum and maximum x and y values
60
- // Return AABB
61
40
  return {
62
- min: { x: min(startX, endX), y: min(startY, endY) },
63
- max: { x: max(startX, endX), y: max(startY, endY) },
41
+ x: cx + cosA * x - sinA * y,
42
+ y: cy + sinA * x + cosA * y,
64
43
  };
65
44
  };
66
45
 
@@ -68,7 +47,7 @@ const minmax = (
68
47
  * Returns the angle between two points.
69
48
  * @param v0 starting point
70
49
  * @param v1 ending point
71
- * @returns the angle
50
+ * @returns the angle in radian
72
51
  */
73
52
  const angleBetween = (v0: Point, v1: Point) => {
74
53
  const { x: v0x, y: v0y } = v0;
@@ -76,26 +55,25 @@ const angleBetween = (v0: Point, v1: Point) => {
76
55
  const p = v0x * v1x + v0y * v1y;
77
56
  const n = Math.sqrt((v0x ** 2 + v0y ** 2) * (v1x ** 2 + v1y ** 2));
78
57
  const sign = v0x * v1y - v0y * v1x < 0 ? -1 : 1;
79
- const angle = sign * Math.acos(p / n);
80
-
81
- return angle;
58
+ return sign * Math.acos(p / n);
82
59
  };
83
60
 
84
61
  /**
85
- * Returns properties for an Arc segment.
62
+ * Returns the following properties for an Arc segment: center, start angle,
63
+ * end angle, and radiuses on X and Y axis.
86
64
  *
87
65
  * @param x1 the starting point X
88
66
  * @param y1 the starting point Y
89
- * @param c1x the first control point X
90
- * @param c1y the first control point Y
91
- * @param c2x the second control point X
92
- * @param c2y the second control point Y
67
+ * @param RX the radius on X axis
68
+ * @param RY the radius on Y axis
69
+ * @param angle the ellipse rotation in degrees
70
+ * @param LAF the large arc flag
71
+ * @param SF the sweep flag
93
72
  * @param x2 the ending point X
94
73
  * @param y2 the ending point Y
95
- * @param distance a [0-1] ratio
96
- * @returns properties specific to Arc segmentas well as the segment length, point at length and the bounding box
74
+ * @returns properties specific to Arc segments
97
75
  */
98
- const getSegmentProperties = (
76
+ const getArcProps = (
99
77
  x1: number,
100
78
  y1: number,
101
79
  RX: number,
@@ -105,7 +83,6 @@ const getSegmentProperties = (
105
83
  SF: number,
106
84
  x: number,
107
85
  y: number,
108
- distance?: number,
109
86
  ) => {
110
87
  const { abs, sin, cos, sqrt, PI } = Math;
111
88
  let rx = abs(RX);
@@ -113,16 +90,25 @@ const getSegmentProperties = (
113
90
  const xRot = ((angle % 360) + 360) % 360;
114
91
  const xRotRad = xRot * (PI / 180);
115
92
 
93
+ // istanbul ignore next @preserve
116
94
  if (x1 === x && y1 === y) {
117
95
  return {
118
- point: { x, y },
119
- length: 0,
120
- bbox: { min: { x, y }, max: { x, y } },
96
+ rx,
97
+ ry,
98
+ startAngle: 0,
99
+ endAngle: 0,
100
+ center: { x, y },
121
101
  };
122
102
  }
123
103
 
124
104
  if (rx === 0 || ry === 0) {
125
- return getLineSegmentProperties(x1, y1, x, y, distance);
105
+ return {
106
+ rx,
107
+ ry,
108
+ startAngle: 0,
109
+ endAngle: 0,
110
+ center: { x: (x + x1) / 2, y: (y + y1) / 2 },
111
+ };
126
112
  }
127
113
 
128
114
  const dx = (x1 - x) / 2;
@@ -144,6 +130,7 @@ const getSegmentProperties = (
144
130
  const cSquareRootDenom = rx ** 2 * transformedPoint.y ** 2 + ry ** 2 * transformedPoint.x ** 2;
145
131
 
146
132
  let cRadicand = cSquareNumerator / cSquareRootDenom;
133
+ /* istanbul ignore next @preserve */
147
134
  cRadicand = cRadicand < 0 ? 0 : cRadicand;
148
135
  const cCoef = (LAF !== SF ? 1 : -1) * sqrt(cRadicand);
149
136
  const transformedCenter = {
@@ -176,17 +163,8 @@ const getSegmentProperties = (
176
163
  }
177
164
  sweepAngle %= 2 * PI;
178
165
 
179
- const alpha = startAngle + sweepAngle * (distance || 0);
180
166
  const endAngle = startAngle + sweepAngle;
181
- const ellipseComponentX = rx * cos(alpha);
182
- const ellipseComponentY = ry * sin(alpha);
183
-
184
- const point = {
185
- x: cos(xRotRad) * ellipseComponentX - sin(xRotRad) * ellipseComponentY + center.x,
186
- y: sin(xRotRad) * ellipseComponentX + cos(xRotRad) * ellipseComponentY + center.y,
187
- };
188
167
 
189
- // to be used later
190
168
  // point.ellipticalArcStartAngle = startAngle;
191
169
  // point.ellipticalArcEndAngle = startAngle + sweepAngle;
192
170
  // point.ellipticalArcAngle = alpha;
@@ -194,24 +172,225 @@ const getSegmentProperties = (
194
172
  // point.ellipticalArcCenter = center;
195
173
  // point.resultantRx = rx;
196
174
  // point.resultantRy = ry;
197
- // point.length = ellipticalArcLength(rx, ry, sweepAngle);
198
- // point.box = minmax(center.x, center.y, rx, ry, xRotRad, startAngle, startAngle + sweepAngle);
199
175
 
200
176
  return {
201
- point,
202
177
  center,
203
- angle: alpha,
204
178
  startAngle,
205
179
  endAngle,
206
- radiusX: rx,
207
- radiusY: ry,
208
- get length() {
209
- return ellipticalArcLength(rx, ry, sweepAngle);
180
+ rx,
181
+ ry,
182
+ };
183
+ };
184
+
185
+ /**
186
+ * Returns the length of an Arc segment.
187
+ *
188
+ * @param x1 the starting point X
189
+ * @param y1 the starting point Y
190
+ * @param c1x the first control point X
191
+ * @param c1y the first control point Y
192
+ * @param c2x the second control point X
193
+ * @param c2y the second control point Y
194
+ * @param x2 the ending point X
195
+ * @param y2 the ending point Y
196
+ * @returns the length of the Arc segment
197
+ */
198
+ const getArcLength = (
199
+ x1: number,
200
+ y1: number,
201
+ RX: number,
202
+ RY: number,
203
+ angle: number,
204
+ LAF: number,
205
+ SF: number,
206
+ x: number,
207
+ y: number,
208
+ ) => {
209
+ const { rx, ry, startAngle, endAngle } = getArcProps(x1, y1, RX, RY, angle, LAF, SF, x, y);
210
+ return arcLength(rx, ry, endAngle - startAngle);
211
+ };
212
+
213
+ /**
214
+ * Returns a point along an Arc segment at a given distance.
215
+ *
216
+ * @param x1 the starting point X
217
+ * @param y1 the starting point Y
218
+ * @param RX the radius on X axis
219
+ * @param RY the radius on Y axis
220
+ * @param angle the ellipse rotation in degrees
221
+ * @param LAF the large arc flag
222
+ * @param SF the sweep flag
223
+ * @param x2 the ending point X
224
+ * @param y2 the ending point Y
225
+ * @param distance a [0-1] ratio
226
+ * @returns a point along the Arc segment
227
+ */
228
+ const getPointAtArcLength = (
229
+ x1: number,
230
+ y1: number,
231
+ RX: number,
232
+ RY: number,
233
+ angle: number,
234
+ LAF: number,
235
+ SF: number,
236
+ x: number,
237
+ y: number,
238
+ distance?: number,
239
+ ) => {
240
+ let point = { x: x1, y: y1 };
241
+ const { center, rx, ry, startAngle, endAngle } = getArcProps(x1, y1, RX, RY, angle, LAF, SF, x, y);
242
+
243
+ /* istanbul ignore else @preserve */
244
+ if (typeof distance === 'number') {
245
+ const length = arcLength(rx, ry, endAngle - startAngle);
246
+ if (distance <= 0) {
247
+ point = { x: x1, y: y1 };
248
+ } else if (distance >= length) {
249
+ point = { x, y };
250
+ } else {
251
+ /* istanbul ignore next @preserve */
252
+ if (x1 === x && y1 === y) {
253
+ return { x, y };
254
+ }
255
+ /* istanbul ignore next @preserve */
256
+ if (rx === 0 || ry === 0) {
257
+ return getPointAtLineLength(x1, y1, x, y, distance);
258
+ }
259
+ const { PI, cos, sin } = Math;
260
+ const sweepAngle = endAngle - startAngle;
261
+ const xRot = ((angle % 360) + 360) % 360;
262
+ const xRotRad = xRot * (PI / 180);
263
+ const alpha = startAngle + sweepAngle * (distance / length);
264
+ const ellipseComponentX = rx * cos(alpha);
265
+ const ellipseComponentY = ry * sin(alpha);
266
+
267
+ point = {
268
+ x: cos(xRotRad) * ellipseComponentX - sin(xRotRad) * ellipseComponentY + center.x,
269
+ y: sin(xRotRad) * ellipseComponentX + cos(xRotRad) * ellipseComponentY + center.y,
270
+ };
271
+ }
272
+ }
273
+
274
+ return point;
275
+ };
276
+
277
+ /**
278
+ * Returns the extrema for an Arc segment.
279
+ * @see https://github.com/herrstrietzel/svg-pathdata-getbbox
280
+ *
281
+ * @param x1 the starting point X
282
+ * @param y1 the starting point Y
283
+ * @param RX the radius on X axis
284
+ * @param RY the radius on Y axis
285
+ * @param angle the ellipse rotation in degrees
286
+ * @param LAF the large arc flag
287
+ * @param SF the sweep flag
288
+ * @param x2 the ending point X
289
+ * @param y2 the ending point Y
290
+ * @returns the extrema of the Arc segment
291
+ *
292
+ */
293
+ const getArcBBox = (
294
+ x1: number,
295
+ y1: number,
296
+ RX: number,
297
+ RY: number,
298
+ angle: number,
299
+ LAF: number,
300
+ SF: number,
301
+ x: number,
302
+ y: number,
303
+ ) => {
304
+ const { center, rx, ry, startAngle, endAngle } = getArcProps(x1, y1, RX, RY, angle, LAF, SF, x, y);
305
+ const deltaAngle = endAngle - startAngle;
306
+ const { min, max, tan, atan2, PI } = Math;
307
+
308
+ // final on path point
309
+ const p = { x, y };
310
+
311
+ // circle/elipse center coordinates
312
+ const { x: cx, y: cy } = center;
313
+
314
+ // collect extreme points – add end point
315
+ const extremes = [p];
316
+
317
+ // rotation to radians
318
+ const alpha = (angle * PI) / 180;
319
+ const tangent = tan(alpha);
320
+
321
+ /**
322
+ * find min/max from zeroes of directional derivative along x and y
323
+ * along x axis
324
+ */
325
+ const theta = atan2(-ry * tangent, rx);
326
+ const angle1 = theta;
327
+ const angle2 = theta + PI;
328
+ const angle3 = atan2(ry, rx * tangent);
329
+ const angle4 = angle3 + PI;
330
+
331
+ // inner bounding box
332
+ const xArr = [x1, x];
333
+ const yArr = [y1, y];
334
+ const xMin = min(...xArr);
335
+ const xMax = max(...xArr);
336
+ const yMin = min(...yArr);
337
+ const yMax = max(...yArr);
338
+
339
+ // on path point close after start
340
+ const angleAfterStart = endAngle - deltaAngle * 0.001;
341
+ const pP2 = arcPoint(cx, cy, rx, ry, alpha, angleAfterStart);
342
+
343
+ // on path point close before end
344
+ const angleBeforeEnd = endAngle - deltaAngle * 0.999;
345
+ const pP3 = arcPoint(cx, cy, rx, ry, alpha, angleBeforeEnd);
346
+
347
+ /**
348
+ * expected extremes
349
+ * if leaving inner bounding box
350
+ * (between segment start and end point)
351
+ * otherwise exclude elliptic extreme points
352
+ */
353
+
354
+ // right
355
+ // istanbul ignore if @preserve
356
+ if (pP2.x > xMax || pP3.x > xMax) {
357
+ // get point for this theta
358
+ extremes.push(arcPoint(cx, cy, rx, ry, alpha, angle1));
359
+ }
360
+
361
+ // left
362
+ // istanbul ignore if @preserve
363
+ if (pP2.x < xMin || pP3.x < xMin) {
364
+ // get anti-symmetric point
365
+ extremes.push(arcPoint(cx, cy, rx, ry, alpha, angle2));
366
+ }
367
+
368
+ // top
369
+ // istanbul ignore if @preserve
370
+ if (pP2.y < yMin || pP3.y < yMin) {
371
+ // get anti-symmetric point
372
+ extremes.push(arcPoint(cx, cy, rx, ry, alpha, angle4));
373
+ }
374
+
375
+ // bottom
376
+ // istanbul ignore if @preserve
377
+ if (pP2.y > yMax || pP3.y > yMax) {
378
+ // get point for this theta
379
+ extremes.push(arcPoint(cx, cy, rx, ry, alpha, angle3));
380
+ }
381
+
382
+ return {
383
+ min: {
384
+ x: min(...extremes.map(n => n.x)),
385
+ y: min(...extremes.map(n => n.y)),
210
386
  },
211
- get bbox() {
212
- return minmax(center.x, center.y, rx, ry, xRotRad, startAngle, startAngle + sweepAngle);
387
+ max: {
388
+ x: max(...extremes.map(n => n.x)),
389
+ y: max(...extremes.map(n => n.y)),
213
390
  },
214
391
  };
215
392
  };
216
393
 
217
- export default getSegmentProperties;
394
+ export { arcPoint, angleBetween, getArcLength, arcLength, getArcBBox, getArcProps, getPointAtArcLength };
395
+
396
+ export {};
@@ -1,12 +1,20 @@
1
- import { type Point } from '../types';
1
+ import type {
2
+ PointTuple,
3
+ DerivedPoint,
4
+ QuadPoints,
5
+ CubicPoints,
6
+ DerivedQuadPoints,
7
+ DerivedCubicPoints,
8
+ QuadCoordinates,
9
+ CubicCoordinates,
10
+ DeriveCallback,
11
+ } from '../types';
2
12
 
3
13
  /**
4
14
  * Tools from bezier.js by Mike 'Pomax' Kamermans
5
15
  * @see https://github.com/Pomax/bezierjs
6
16
  */
7
17
 
8
- const ZERO = { x: 0, y: 0 };
9
-
10
18
  const Tvalues = [
11
19
  -0.0640568928626056260850430826247450385909, 0.0640568928626056260850430826247450385909,
12
20
  -0.1911188674736163091586398207570696318404, 0.1911188674736163091586398207570696318404,
@@ -37,31 +45,12 @@ const Cvalues = [
37
45
  0.0123412297999871995468056670700372915759, 0.0123412297999871995468056670700372915759,
38
46
  ];
39
47
 
40
- type DerivedPoint = Point & { t: number };
41
- type QuadPoints = [Point, Point, Point, Point, Point, Point];
42
- type CubicPoints = [Point, Point, Point, Point, Point, Point, Point, Point];
43
- type DerivedQuadPoints = [DerivedPoint, DerivedPoint, DerivedPoint, DerivedPoint, DerivedPoint, DerivedPoint];
44
- type DerivedCubicPoints = [
45
- DerivedPoint,
46
- DerivedPoint,
47
- DerivedPoint,
48
- DerivedPoint,
49
- DerivedPoint,
50
- DerivedPoint,
51
- DerivedPoint,
52
- DerivedPoint,
53
- ];
54
- export type QuadCoordinates = [number, number, number, number, number, number];
55
- export type CubicCoordinates = [number, number, number, number, number, number, number, number];
56
-
57
- type DeriveCallback = (t: number) => Point;
58
-
59
48
  /**
60
49
  *
61
50
  * @param points
62
51
  * @returns
63
52
  */
64
- const derive = (points: QuadPoints | CubicPoints) => {
53
+ const deriveBezier = (points: QuadPoints | CubicPoints) => {
65
54
  const dpoints = [] as (DerivedCubicPoints | DerivedQuadPoints)[];
66
55
  for (let p = points, d = p.length, c = d - 1; d > 1; d -= 1, c -= 1) {
67
56
  const list = [] as unknown as DerivedCubicPoints | DerivedQuadPoints;
@@ -83,7 +72,7 @@ const derive = (points: QuadPoints | CubicPoints) => {
83
72
  * @param points
84
73
  * @param t
85
74
  */
86
- const compute = (points: DerivedQuadPoints | DerivedCubicPoints, t: number) => {
75
+ const computeBezier = (points: DerivedQuadPoints | DerivedCubicPoints, t: number) => {
87
76
  // shortcuts
88
77
  /* istanbul ignore next @preserve */
89
78
  if (t === 0) {
@@ -128,7 +117,7 @@ const compute = (points: DerivedQuadPoints | DerivedCubicPoints, t: number) => {
128
117
  let d = 0;
129
118
  /* istanbul ignore else @preserve */
130
119
  if (order === 2) {
131
- p = [p[0], p[1], p[2], ZERO as DerivedPoint];
120
+ p = [p[0], p[1], p[2], { x: 0, y: 0 } as DerivedPoint];
132
121
  a = mt2;
133
122
  b = mt * t * 2;
134
123
  c = t2;
@@ -145,14 +134,14 @@ const compute = (points: DerivedQuadPoints | DerivedCubicPoints, t: number) => {
145
134
  };
146
135
  };
147
136
 
148
- const arcfn = (derivativeFn: DeriveCallback, t: number) => {
137
+ const calculateBezier = (derivativeFn: DeriveCallback, t: number) => {
149
138
  const d = derivativeFn(t);
150
139
  const l = d.x * d.x + d.y * d.y;
151
140
 
152
141
  return Math.sqrt(l);
153
142
  };
154
143
 
155
- const lengthFn = (derivativeFn: DeriveCallback) => {
144
+ const bezierLength = (derivativeFn: DeriveCallback) => {
156
145
  const z = 0.5;
157
146
  const len = Tvalues.length;
158
147
 
@@ -160,7 +149,7 @@ const lengthFn = (derivativeFn: DeriveCallback) => {
160
149
 
161
150
  for (let i = 0, t; i < len; i++) {
162
151
  t = z * Tvalues[i] + z;
163
- sum += Cvalues[i] * arcfn(derivativeFn, t);
152
+ sum += Cvalues[i] * calculateBezier(derivativeFn, t);
164
153
  }
165
154
  return z * sum;
166
155
  };
@@ -169,7 +158,7 @@ const lengthFn = (derivativeFn: DeriveCallback) => {
169
158
  * Returns the length of CubicBezier / Quad segment.
170
159
  * @param curve cubic / quad bezier segment
171
160
  */
172
- export const length = (curve: CubicCoordinates | QuadCoordinates) => {
161
+ const getBezierLength = (curve: CubicCoordinates | QuadCoordinates) => {
173
162
  const points = [] as unknown as CubicPoints | QuadPoints;
174
163
  for (let idx = 0, len = curve.length, step = 2; idx < len; idx += step) {
175
164
  points.push({
@@ -177,9 +166,9 @@ export const length = (curve: CubicCoordinates | QuadCoordinates) => {
177
166
  y: curve[idx + 1],
178
167
  });
179
168
  }
180
- const dpoints = derive(points);
181
- return lengthFn((t: number) => {
182
- return compute(dpoints[0], t);
169
+ const dpoints = deriveBezier(points);
170
+ return bezierLength((t: number) => {
171
+ return computeBezier(dpoints[0], t);
183
172
  });
184
173
  };
185
174
 
@@ -188,66 +177,64 @@ const CBEZIER_MINMAX_EPSILON = 0.00000001;
188
177
 
189
178
  /**
190
179
  * Returns the most extreme points in a Quad Bezier segment.
191
- * @param A
180
+ * @param A an array which consist of X/Y values
192
181
  */
193
182
  // https://github.com/kpym/SVGPathy/blob/acd1a50c626b36d81969f6e98e8602e128ba4302/lib/box.js#L89
194
- export const minmaxQ = (A: [number, number, number]) => {
195
- const min = Math.min(A[0], A[2]);
196
- const max = Math.max(A[0], A[2]);
183
+ const minmaxQ = ([v1, cp, v2]: [number, number, number]) => {
184
+ const min = Math.min(v1, v2);
185
+ const max = Math.max(v1, v2);
197
186
 
198
- /* istanbul ignore else @preserve */
199
- if (A[1] >= A[0] ? A[2] >= A[1] : A[2] <= A[1]) {
187
+ /* istanbul ignore next @preserve */
188
+ if (cp >= v1 ? v2 >= cp : v2 <= cp) {
200
189
  // if no extremum in ]0,1[
201
- return [min, max] as [number, number];
190
+ return [min, max] as PointTuple;
202
191
  }
203
192
 
204
193
  // check if the extremum E is min or max
205
- const E = (A[0] * A[2] - A[1] * A[1]) / (A[0] - 2 * A[1] + A[2]);
206
- return (E < min ? [E, max] : [min, E]) as [number, number];
194
+ const E = (v1 * v2 - cp * cp) / (v1 - 2 * cp + v2);
195
+ return (E < min ? [E, max] : [min, E]) as PointTuple;
207
196
  };
208
197
 
209
198
  /**
210
199
  * Returns the most extreme points in a Cubic Bezier segment.
211
- * @param A
200
+ * @param A an array which consist of X/Y values
212
201
  * @see https://github.com/kpym/SVGPathy/blob/acd1a50c626b36d81969f6e98e8602e128ba4302/lib/box.js#L127
213
202
  */
214
- export const minmaxC = (A: [number, number, number, number]) => {
215
- const K = A[0] - 3 * A[1] + 3 * A[2] - A[3];
203
+ const minmaxC = ([v1, cp1, cp2, v2]: [number, number, number, number]) => {
204
+ const K = v1 - 3 * cp1 + 3 * cp2 - v2;
216
205
 
217
206
  // if the polynomial is (almost) quadratic and not cubic
218
- /* istanbul ignore else @preserve */
207
+ /* istanbul ignore next @preserve */
219
208
  if (Math.abs(K) < CBEZIER_MINMAX_EPSILON) {
220
- if (A[0] === A[3] && A[0] === A[1]) {
209
+ if (v1 === v2 && v1 === cp1) {
221
210
  // no curve, point targeting same location
222
- return [A[0], A[3]] as [number, number];
211
+ return [v1, v2] as PointTuple;
223
212
  }
224
213
 
225
- return minmaxQ([A[0], -0.5 * A[0] + 1.5 * A[1], A[0] - 3 * A[1] + 3 * A[2]]);
214
+ return minmaxQ([v1, -0.5 * v1 + 1.5 * cp1, v1 - 3 * cp1 + 3 * cp2]);
226
215
  }
227
216
 
228
217
  // the reduced discriminant of the derivative
229
- const T = -A[0] * A[2] + A[0] * A[3] - A[1] * A[2] - A[1] * A[3] + A[1] * A[1] + A[2] * A[2];
218
+ const T = -v1 * cp2 + v1 * v2 - cp1 * cp2 - cp1 * v2 + cp1 * cp1 + cp2 * cp2;
230
219
 
231
220
  // if the polynomial is monotone in [0,1]
232
221
  if (T <= 0) {
233
- return [Math.min(A[0], A[3]), Math.max(A[0], A[3])] as [number, number];
222
+ return [Math.min(v1, v2), Math.max(v1, v2)] as PointTuple;
234
223
  }
235
224
  const S = Math.sqrt(T);
236
225
 
237
226
  // potential extrema
238
- let min = Math.min(A[0], A[3]);
239
- let max = Math.max(A[0], A[3]);
227
+ let min = Math.min(v1, v2);
228
+ let max = Math.max(v1, v2);
240
229
 
241
- const L = A[0] - 2 * A[1] + A[2];
230
+ const L = v1 - 2 * cp1 + cp2;
242
231
  // check local extrema
243
232
  for (let R = (L + S) / K, i = 1; i <= 2; R = (L - S) / K, i++) {
233
+ // istanbul ignore next @preserve
244
234
  if (R > 0 && R < 1) {
245
235
  // if the extrema is for R in [0,1]
246
236
  const Q =
247
- A[0] * (1 - R) * (1 - R) * (1 - R) +
248
- A[1] * 3 * (1 - R) * (1 - R) * R +
249
- A[2] * 3 * (1 - R) * R * R +
250
- A[3] * R * R * R;
237
+ v1 * (1 - R) * (1 - R) * (1 - R) + cp1 * 3 * (1 - R) * (1 - R) * R + cp2 * 3 * (1 - R) * R * R + v2 * R * R * R;
251
238
  if (Q < min) {
252
239
  min = Q;
253
240
  }
@@ -257,5 +244,18 @@ export const minmaxC = (A: [number, number, number, number]) => {
257
244
  }
258
245
  }
259
246
 
260
- return [min, max] as [number, number];
247
+ return [min, max] as PointTuple;
248
+ };
249
+
250
+ export {
251
+ Cvalues,
252
+ Tvalues,
253
+ minmaxC,
254
+ minmaxQ,
255
+ getBezierLength,
256
+ bezierLength,
257
+ calculateBezier,
258
+ computeBezier,
259
+ deriveBezier,
260
+ CBEZIER_MINMAX_EPSILON,
261
261
  };