svg-path-commander 2.1.3 → 2.1.5

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