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