svg-path-commander 2.1.2 → 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 -849
  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 +947 -611
  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 -396
  20. package/src/math/bezier.ts +0 -261
  21. package/src/math/cubicTools.ts +0 -124
  22. package/src/math/distanceSquareRoot.ts +0 -15
  23. package/src/math/lineTools.ts +0 -69
  24. package/src/math/midPoint.ts +0 -18
  25. package/src/math/polygonTools.ts +0 -48
  26. package/src/math/quadTools.ts +0 -100
  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 -63
  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 -63
  55. package/src/process/projection2d.ts +0 -52
  56. package/src/process/quadToCubic.ts +0 -31
  57. package/src/process/relativizeSegment.ts +0 -59
  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 -110
  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 -98
  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 -68
  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 -504
  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 -75
  94. package/test/static.test.ts +0 -330
  95. package/vite.config.mts +0 -41
  96. package/vitest.config-ui.mts +0 -26
  97. package/vitest.config.mts +0 -26
@@ -1,396 +0,0 @@
1
- import { getPointAtLineLength } from './lineTools';
2
- import type { Point } 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 {
41
- x: cx + cosA * x - sinA * y,
42
- y: cy + sinA * x + cosA * y,
43
- };
44
- };
45
-
46
- /**
47
- * Returns the angle between two points.
48
- * @param v0 starting point
49
- * @param v1 ending point
50
- * @returns the angle in radian
51
- */
52
- const angleBetween = (v0: Point, v1: Point) => {
53
- const { x: v0x, y: v0y } = v0;
54
- const { x: v1x, y: v1y } = v1;
55
- const p = v0x * v1x + v0y * v1y;
56
- const n = Math.sqrt((v0x ** 2 + v0y ** 2) * (v1x ** 2 + v1y ** 2));
57
- const sign = v0x * v1y - v0y * v1x < 0 ? -1 : 1;
58
- return sign * Math.acos(p / n);
59
- };
60
-
61
- /**
62
- * Returns the following properties for an Arc segment: center, start angle,
63
- * end angle, and radiuses on X and Y axis.
64
- *
65
- * @param x1 the starting point X
66
- * @param y1 the starting 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
72
- * @param x2 the ending point X
73
- * @param y2 the ending point Y
74
- * @returns properties specific to Arc segments
75
- */
76
- const getArcProps = (
77
- x1: number,
78
- y1: number,
79
- RX: number,
80
- RY: number,
81
- angle: number,
82
- LAF: number,
83
- SF: number,
84
- x: number,
85
- y: number,
86
- ) => {
87
- const { abs, sin, cos, sqrt, PI } = Math;
88
- let rx = abs(RX);
89
- let ry = abs(RY);
90
- const xRot = ((angle % 360) + 360) % 360;
91
- const xRotRad = xRot * (PI / 180);
92
-
93
- // istanbul ignore next @preserve
94
- if (x1 === x && y1 === y) {
95
- return {
96
- rx,
97
- ry,
98
- startAngle: 0,
99
- endAngle: 0,
100
- center: { x, y },
101
- };
102
- }
103
-
104
- if (rx === 0 || ry === 0) {
105
- return {
106
- rx,
107
- ry,
108
- startAngle: 0,
109
- endAngle: 0,
110
- center: { x: (x + x1) / 2, y: (y + y1) / 2 },
111
- };
112
- }
113
-
114
- const dx = (x1 - x) / 2;
115
- const dy = (y1 - y) / 2;
116
-
117
- const transformedPoint = {
118
- x: cos(xRotRad) * dx + sin(xRotRad) * dy,
119
- y: -sin(xRotRad) * dx + cos(xRotRad) * dy,
120
- };
121
-
122
- const radiiCheck = transformedPoint.x ** 2 / rx ** 2 + transformedPoint.y ** 2 / ry ** 2;
123
-
124
- if (radiiCheck > 1) {
125
- rx *= sqrt(radiiCheck);
126
- ry *= sqrt(radiiCheck);
127
- }
128
-
129
- const cSquareNumerator = rx ** 2 * ry ** 2 - rx ** 2 * transformedPoint.y ** 2 - ry ** 2 * transformedPoint.x ** 2;
130
- const cSquareRootDenom = rx ** 2 * transformedPoint.y ** 2 + ry ** 2 * transformedPoint.x ** 2;
131
-
132
- let cRadicand = cSquareNumerator / cSquareRootDenom;
133
- /* istanbul ignore next @preserve */
134
- cRadicand = cRadicand < 0 ? 0 : cRadicand;
135
- const cCoef = (LAF !== SF ? 1 : -1) * sqrt(cRadicand);
136
- const transformedCenter = {
137
- x: cCoef * ((rx * transformedPoint.y) / ry),
138
- y: cCoef * (-(ry * transformedPoint.x) / rx),
139
- };
140
-
141
- const center = {
142
- x: cos(xRotRad) * transformedCenter.x - sin(xRotRad) * transformedCenter.y + (x1 + x) / 2,
143
- y: sin(xRotRad) * transformedCenter.x + cos(xRotRad) * transformedCenter.y + (y1 + y) / 2,
144
- };
145
-
146
- const startVector = {
147
- x: (transformedPoint.x - transformedCenter.x) / rx,
148
- y: (transformedPoint.y - transformedCenter.y) / ry,
149
- };
150
-
151
- const startAngle = angleBetween({ x: 1, y: 0 }, startVector);
152
-
153
- const endVector = {
154
- x: (-transformedPoint.x - transformedCenter.x) / rx,
155
- y: (-transformedPoint.y - transformedCenter.y) / ry,
156
- };
157
-
158
- let sweepAngle = angleBetween(startVector, endVector);
159
- if (!SF && sweepAngle > 0) {
160
- sweepAngle -= 2 * PI;
161
- } else if (SF && sweepAngle < 0) {
162
- sweepAngle += 2 * PI;
163
- }
164
- sweepAngle %= 2 * PI;
165
-
166
- const endAngle = startAngle + sweepAngle;
167
-
168
- // point.ellipticalArcStartAngle = startAngle;
169
- // point.ellipticalArcEndAngle = startAngle + sweepAngle;
170
- // point.ellipticalArcAngle = alpha;
171
-
172
- // point.ellipticalArcCenter = center;
173
- // point.resultantRx = rx;
174
- // point.resultantRy = ry;
175
-
176
- return {
177
- center,
178
- startAngle,
179
- endAngle,
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)),
386
- },
387
- max: {
388
- x: max(...extremes.map(n => n.x)),
389
- y: max(...extremes.map(n => n.y)),
390
- },
391
- };
392
- };
393
-
394
- export { arcPoint, angleBetween, getArcLength, arcLength, getArcBBox, getArcProps, getPointAtArcLength };
395
-
396
- export {};
@@ -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
- };