svg-path-commander 2.0.10 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/README.md +64 -8
  3. package/dist/svg-path-commander.cjs +1 -1
  4. package/dist/svg-path-commander.cjs.map +1 -1
  5. package/dist/svg-path-commander.d.ts +236 -43
  6. package/dist/svg-path-commander.js +1 -1
  7. package/dist/svg-path-commander.js.map +1 -1
  8. package/dist/svg-path-commander.mjs +790 -650
  9. package/dist/svg-path-commander.mjs.map +1 -1
  10. package/package.json +20 -22
  11. package/src/convert/pathToAbsolute.ts +16 -70
  12. package/src/convert/pathToCurve.ts +36 -28
  13. package/src/convert/pathToRelative.ts +33 -62
  14. package/src/index.ts +37 -39
  15. package/src/interface.ts +33 -33
  16. package/src/math/arcTools.ts +394 -0
  17. package/src/math/bezier.ts +253 -0
  18. package/src/math/cubicTools.ts +122 -0
  19. package/src/math/distanceSquareRoot.ts +3 -1
  20. package/src/math/lineTools.ts +67 -0
  21. package/src/math/midPoint.ts +3 -1
  22. package/src/math/polygonArea.ts +3 -1
  23. package/src/math/polygonLength.ts +2 -1
  24. package/src/math/quadTools.ts +98 -0
  25. package/src/parser/isMoveCommand.ts +17 -0
  26. package/src/parser/parsePathString.ts +5 -5
  27. package/src/parser/scanSegment.ts +12 -3
  28. package/src/process/absolutizeSegment.ts +58 -0
  29. package/src/process/iterate.ts +33 -0
  30. package/src/process/normalizePath.ts +34 -28
  31. package/src/process/normalizeSegment.ts +8 -9
  32. package/src/process/projection2d.ts +2 -1
  33. package/src/process/relativizeSegment.ts +61 -0
  34. package/src/process/reversePath.ts +1 -1
  35. package/src/process/roundPath.ts +8 -10
  36. package/src/process/segmentToCubic.ts +1 -1
  37. package/src/process/shortenSegment.ts +3 -3
  38. package/src/process/splitCubic.ts +8 -7
  39. package/src/process/splitPath.ts +39 -5
  40. package/src/process/transformPath.ts +81 -94
  41. package/src/types.ts +40 -1
  42. package/src/util/distanceEpsilon.ts +3 -0
  43. package/src/util/getClosestPoint.ts +1 -1
  44. package/src/util/getPathArea.ts +3 -3
  45. package/src/util/getPathBBox.ts +86 -18
  46. package/src/util/getPointAtLength.ts +98 -4
  47. package/src/util/getPropertiesAtLength.ts +4 -3
  48. package/src/util/getPropertiesAtPoint.ts +4 -1
  49. package/src/util/getTotalLength.ts +71 -4
  50. package/src/util/isPointInStroke.ts +2 -1
  51. package/src/util/shapeToPathArray.ts +8 -4
  52. package/test/class.test.ts +502 -0
  53. package/test/fixtures/getMarkup.ts +17 -0
  54. package/{cypress → test}/fixtures/shapes.js +39 -39
  55. package/test/fixtures/simpleShapes.js +75 -0
  56. package/test/static.test.ts +324 -0
  57. package/tsconfig.json +9 -4
  58. package/{vite.config.ts → vite.config.mts} +10 -1
  59. package/vitest.config-ui.mts +26 -0
  60. package/vitest.config.mts +26 -0
  61. package/cypress/e2e/svg-path-commander.spec.ts +0 -868
  62. package/cypress/fixtures/simpleShapes.js +0 -75
  63. package/cypress/plugins/esbuild-istanbul.ts +0 -50
  64. package/cypress/plugins/tsCompile.ts +0 -34
  65. package/cypress/support/commands.ts +0 -37
  66. package/cypress/support/e2e.ts +0 -21
  67. package/cypress/test.html +0 -36
  68. package/cypress.config.ts +0 -29
  69. package/src/process/fixArc.ts +0 -23
  70. package/src/util/pathLengthFactory.ts +0 -114
  71. package/src/util/segmentArcFactory.ts +0 -219
  72. package/src/util/segmentCubicFactory.ts +0 -114
  73. package/src/util/segmentLineFactory.ts +0 -45
  74. package/src/util/segmentQuadFactory.ts +0 -109
  75. /package/{cypress/fixtures/shapeObjects.js → test/fixtures/shapeObjects.ts} +0 -0
@@ -1,219 +0,0 @@
1
- import type { LengthFactory } from '../interface';
2
- import segmentLineFactory from './segmentLineFactory';
3
- import distanceSquareRoot from '../math/distanceSquareRoot';
4
-
5
- /**
6
- * Returns an angle value between two points.
7
- *
8
- * @param v0
9
- * @param v1
10
- * @returns a number representing an angle
11
- */
12
- const angleBetween = (v0: { x: number; y: number }, v1: { x: number; y: number }): number => {
13
- const { x: v0x, y: v0y } = v0;
14
- const { x: v1x, y: v1y } = v1;
15
- const p = v0x * v1x + v0y * v1y;
16
- const n = Math.sqrt((v0x ** 2 + v0y ** 2) * (v1x ** 2 + v1y ** 2));
17
- const sign = v0x * v1y - v0y * v1x < 0 ? -1 : 1;
18
- const angle = sign * Math.acos(p / n);
19
-
20
- return angle;
21
- };
22
-
23
- /**
24
- * Returns a {x,y} point at a given length, the total length and
25
- * the minimum and maximum {x,y} coordinates of a C (cubic-bezier) segment.
26
- *
27
- * @see https://github.com/MadLittleMods/svg-curve-lib/blob/master/src/js/svg-curve-lib.js
28
- *
29
- * @param x1 the starting x position
30
- * @param y1 the starting y position
31
- * @param RX x-radius of the arc
32
- * @param RY y-radius of the arc
33
- * @param angle x-axis-rotation of the arc
34
- * @param LAF large-arc-flag of the arc
35
- * @param SF sweep-flag of the arc
36
- * @param x the ending x position
37
- * @param y the ending y position
38
- * @param t the point distance
39
- * @returns the requested point
40
- */
41
- const getPointAtArcSegmentLength = (
42
- x1: number,
43
- y1: number,
44
- RX: number,
45
- RY: number,
46
- angle: number,
47
- LAF: number,
48
- SF: number,
49
- x: number,
50
- y: number,
51
- t: number,
52
- ): { x: number; y: number } => {
53
- const { abs, sin, cos, sqrt, PI } = Math;
54
- let rx = abs(RX);
55
- let ry = abs(RY);
56
- const xRot = ((angle % 360) + 360) % 360;
57
- const xRotRad = xRot * (PI / 180);
58
-
59
- if (x1 === x && y1 === y) {
60
- return { x: x1, y: y1 };
61
- }
62
-
63
- if (rx === 0 || ry === 0) {
64
- return segmentLineFactory(x1, y1, x, y, t).point;
65
- }
66
-
67
- const dx = (x1 - x) / 2;
68
- const dy = (y1 - y) / 2;
69
-
70
- const transformedPoint = {
71
- x: cos(xRotRad) * dx + sin(xRotRad) * dy,
72
- y: -sin(xRotRad) * dx + cos(xRotRad) * dy,
73
- };
74
-
75
- const radiiCheck = transformedPoint.x ** 2 / rx ** 2 + transformedPoint.y ** 2 / ry ** 2;
76
-
77
- if (radiiCheck > 1) {
78
- rx *= sqrt(radiiCheck);
79
- ry *= sqrt(radiiCheck);
80
- }
81
-
82
- const cSquareNumerator = rx ** 2 * ry ** 2 - rx ** 2 * transformedPoint.y ** 2 - ry ** 2 * transformedPoint.x ** 2;
83
-
84
- const cSquareRootDenom = rx ** 2 * transformedPoint.y ** 2 + ry ** 2 * transformedPoint.x ** 2;
85
-
86
- let cRadicand = cSquareNumerator / cSquareRootDenom;
87
- cRadicand = cRadicand < 0 ? 0 : cRadicand;
88
- const cCoef = (LAF !== SF ? 1 : -1) * sqrt(cRadicand);
89
- const transformedCenter = {
90
- x: cCoef * ((rx * transformedPoint.y) / ry),
91
- y: cCoef * (-(ry * transformedPoint.x) / rx),
92
- };
93
-
94
- const center = {
95
- x: cos(xRotRad) * transformedCenter.x - sin(xRotRad) * transformedCenter.y + (x1 + x) / 2,
96
- y: sin(xRotRad) * transformedCenter.x + cos(xRotRad) * transformedCenter.y + (y1 + y) / 2,
97
- };
98
-
99
- const startVector = {
100
- x: (transformedPoint.x - transformedCenter.x) / rx,
101
- y: (transformedPoint.y - transformedCenter.y) / ry,
102
- };
103
-
104
- const startAngle = angleBetween({ x: 1, y: 0 }, startVector);
105
-
106
- const endVector = {
107
- x: (-transformedPoint.x - transformedCenter.x) / rx,
108
- y: (-transformedPoint.y - transformedCenter.y) / ry,
109
- };
110
-
111
- let sweepAngle = angleBetween(startVector, endVector);
112
- if (!SF && sweepAngle > 0) {
113
- sweepAngle -= 2 * PI;
114
- } else if (SF && sweepAngle < 0) {
115
- sweepAngle += 2 * PI;
116
- }
117
- sweepAngle %= 2 * PI;
118
-
119
- const alpha = startAngle + sweepAngle * t;
120
- const ellipseComponentX = rx * cos(alpha);
121
- const ellipseComponentY = ry * sin(alpha);
122
-
123
- const point = {
124
- x: cos(xRotRad) * ellipseComponentX - sin(xRotRad) * ellipseComponentY + center.x,
125
- y: sin(xRotRad) * ellipseComponentX + cos(xRotRad) * ellipseComponentY + center.y,
126
- };
127
-
128
- // to be used later
129
- // point.ellipticalArcStartAngle = startAngle;
130
- // point.ellipticalArcEndAngle = startAngle + sweepAngle;
131
- // point.ellipticalArcAngle = alpha;
132
-
133
- // point.ellipticalArcCenter = center;
134
- // point.resultantRx = rx;
135
- // point.resultantRy = ry;
136
-
137
- return point;
138
- };
139
-
140
- /**
141
- * Returns a {x,y} point at a given length, the total length and
142
- * the shape minimum and maximum {x,y} coordinates of an A (arc-to) segment.
143
- *
144
- * @param X1 the starting x position
145
- * @param Y1 the starting y position
146
- * @param RX x-radius of the arc
147
- * @param RY y-radius of the arc
148
- * @param angle x-axis-rotation of the arc
149
- * @param LAF large-arc-flag of the arc
150
- * @param SF sweep-flag of the arc
151
- * @param X2 the ending x position
152
- * @param Y2 the ending y position
153
- * @param distance the point distance
154
- * @returns the segment length, point, min & max
155
- */
156
- const segmentArcFactory = (
157
- X1: number,
158
- Y1: number,
159
- RX: number,
160
- RY: number,
161
- angle: number,
162
- LAF: number,
163
- SF: number,
164
- X2: number,
165
- Y2: number,
166
- distance: number,
167
- ): LengthFactory => {
168
- const distanceIsNumber = typeof distance === 'number';
169
- let x = X1;
170
- let y = Y1;
171
- let LENGTH = 0;
172
- let prev = [x, y, LENGTH];
173
- let cur = [x, y] as [number, number];
174
- let t = 0;
175
- let POINT = { x: 0, y: 0 };
176
- let POINTS = [{ x, y }];
177
-
178
- if (distanceIsNumber && distance <= 0) {
179
- POINT = { x, y };
180
- }
181
-
182
- const sampleSize = 300;
183
- for (let j = 0; j <= sampleSize; j += 1) {
184
- t = j / sampleSize;
185
-
186
- ({ x, y } = getPointAtArcSegmentLength(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, t));
187
- POINTS = [...POINTS, { x, y }];
188
- LENGTH += distanceSquareRoot(cur, [x, y]);
189
- cur = [x, y];
190
-
191
- if (distanceIsNumber && LENGTH > distance && distance > prev[2]) {
192
- const dv = (LENGTH - distance) / (LENGTH - prev[2]);
193
-
194
- POINT = {
195
- x: cur[0] * (1 - dv) + prev[0] * dv,
196
- y: cur[1] * (1 - dv) + prev[1] * dv,
197
- };
198
- }
199
- prev = [x, y, LENGTH];
200
- }
201
-
202
- if (distanceIsNumber && distance >= LENGTH) {
203
- POINT = { x: X2, y: Y2 };
204
- }
205
-
206
- return {
207
- length: LENGTH,
208
- point: POINT,
209
- min: {
210
- x: Math.min(...POINTS.map(n => n.x)),
211
- y: Math.min(...POINTS.map(n => n.y)),
212
- },
213
- max: {
214
- x: Math.max(...POINTS.map(n => n.x)),
215
- y: Math.max(...POINTS.map(n => n.y)),
216
- },
217
- };
218
- };
219
- export default segmentArcFactory;
@@ -1,114 +0,0 @@
1
- import { LengthFactory } from 'src/interface';
2
- import distanceSquareRoot from '../math/distanceSquareRoot';
3
-
4
- /**
5
- * Returns a {x,y} point at a given length, the total length and
6
- * the minimum and maximum {x,y} coordinates of a C (cubic-bezier) segment.
7
- *
8
- * @param x1 the starting point X
9
- * @param y1 the starting point Y
10
- * @param c1x the first control point X
11
- * @param c1y the first control point Y
12
- * @param c2x the second control point X
13
- * @param c2y the second control point Y
14
- * @param x2 the ending point X
15
- * @param y2 the ending point Y
16
- * @param t a [0-1] ratio
17
- * @returns the point at cubic-bezier segment length
18
- */
19
- const getPointAtCubicSegmentLength = (
20
- x1: number,
21
- y1: number,
22
- c1x: number,
23
- c1y: number,
24
- c2x: number,
25
- c2y: number,
26
- x2: number,
27
- y2: number,
28
- t: number,
29
- ): { x: number; y: number } => {
30
- const t1 = 1 - t;
31
- return {
32
- x: t1 ** 3 * x1 + 3 * t1 ** 2 * t * c1x + 3 * t1 * t ** 2 * c2x + t ** 3 * x2,
33
- y: t1 ** 3 * y1 + 3 * t1 ** 2 * t * c1y + 3 * t1 * t ** 2 * c2y + t ** 3 * y2,
34
- };
35
- };
36
-
37
- /**
38
- * Returns the length of a C (cubic-bezier) segment
39
- * or an {x,y} point at a given length.
40
- *
41
- * @param x1 the starting point X
42
- * @param y1 the starting point Y
43
- * @param c1x the first control point X
44
- * @param c1y the first control point Y
45
- * @param c2x the second control point X
46
- * @param c2y the second control point Y
47
- * @param x2 the ending point X
48
- * @param y2 the ending point Y
49
- * @param distance the point distance
50
- * @returns the segment length, point, min & max
51
- */
52
- const segmentCubicFactory = (
53
- x1: number,
54
- y1: number,
55
- c1x: number,
56
- c1y: number,
57
- c2x: number,
58
- c2y: number,
59
- x2: number,
60
- y2: number,
61
- distance?: number,
62
- ): LengthFactory => {
63
- const distanceIsNumber = typeof distance === 'number';
64
- let x = x1;
65
- let y = y1;
66
- let LENGTH = 0;
67
- let prev = [x, y, LENGTH];
68
- let cur = [x, y] as [number, number];
69
- let t = 0;
70
- let POINT = { x: 0, y: 0 };
71
- let POINTS = [{ x, y }];
72
-
73
- if (distanceIsNumber && distance <= 0) {
74
- POINT = { x, y };
75
- }
76
-
77
- const sampleSize = 300;
78
- for (let j = 0; j <= sampleSize; j += 1) {
79
- t = j / sampleSize;
80
-
81
- ({ x, y } = getPointAtCubicSegmentLength(x1, y1, c1x, c1y, c2x, c2y, x2, y2, t));
82
- POINTS = [...POINTS, { x, y }];
83
- LENGTH += distanceSquareRoot(cur, [x, y]);
84
- cur = [x, y];
85
-
86
- if (distanceIsNumber && LENGTH > distance && distance > prev[2]) {
87
- const dv = (LENGTH - distance) / (LENGTH - prev[2]);
88
-
89
- POINT = {
90
- x: cur[0] * (1 - dv) + prev[0] * dv,
91
- y: cur[1] * (1 - dv) + prev[1] * dv,
92
- };
93
- }
94
- prev = [x, y, LENGTH];
95
- }
96
-
97
- if (distanceIsNumber && distance >= LENGTH) {
98
- POINT = { x: x2, y: y2 };
99
- }
100
-
101
- return {
102
- length: LENGTH,
103
- point: POINT,
104
- min: {
105
- x: Math.min(...POINTS.map(n => n.x)),
106
- y: Math.min(...POINTS.map(n => n.y)),
107
- },
108
- max: {
109
- x: Math.max(...POINTS.map(n => n.x)),
110
- y: Math.max(...POINTS.map(n => n.y)),
111
- },
112
- };
113
- };
114
- export default segmentCubicFactory;
@@ -1,45 +0,0 @@
1
- import midPoint from '../math/midPoint';
2
- import distanceSquareRoot from '../math/distanceSquareRoot';
3
- import type { LengthFactory } from '../interface';
4
-
5
- /**
6
- * Returns a {x,y} point at a given length, the total length and
7
- * the minimum and maximum {x,y} coordinates of a line (L,V,H,Z) segment.
8
- *
9
- * @param x1 the starting point X
10
- * @param y1 the starting point Y
11
- * @param x2 the ending point X
12
- * @param y2 the ending point Y
13
- * @param distance the distance to point
14
- * @returns the segment length, point, min & max
15
- */
16
- const segmentLineFactory = (x1: number, y1: number, x2: number, y2: number, distance?: number): LengthFactory => {
17
- const length = distanceSquareRoot([x1, y1], [x2, y2]);
18
- let point = { x: 0, y: 0 };
19
-
20
- /* istanbul ignore else */
21
- if (typeof distance === 'number') {
22
- if (distance <= 0) {
23
- point = { x: x1, y: y1 };
24
- } else if (distance >= length) {
25
- point = { x: x2, y: y2 };
26
- } else {
27
- const [x, y] = midPoint([x1, y1], [x2, y2], distance / length);
28
- point = { x, y };
29
- }
30
- }
31
-
32
- return {
33
- length,
34
- point,
35
- min: {
36
- x: Math.min(x1, x2),
37
- y: Math.min(y1, y2),
38
- },
39
- max: {
40
- x: Math.max(x1, x2),
41
- y: Math.max(y1, y2),
42
- },
43
- };
44
- };
45
- export default segmentLineFactory;
@@ -1,109 +0,0 @@
1
- import { LengthFactory } from 'src/interface';
2
- import distanceSquareRoot from '../math/distanceSquareRoot';
3
-
4
- /**
5
- * Returns the {x,y} coordinates of a point at a
6
- * given length of a quadratic-bezier segment.
7
- *
8
- * @see https://github.com/substack/point-at-length
9
- *
10
- * @param x1 the starting point X
11
- * @param y1 the starting point Y
12
- * @param cx the control point X
13
- * @param cy the control point Y
14
- * @param x2 the ending point X
15
- * @param y2 the ending point Y
16
- * @param t a [0-1] ratio
17
- * @returns the requested {x,y} coordinates
18
- */
19
- const getPointAtQuadSegmentLength = (
20
- x1: number,
21
- y1: number,
22
- cx: number,
23
- cy: number,
24
- x2: number,
25
- y2: number,
26
- t: number,
27
- ): { x: number; y: number } => {
28
- const t1 = 1 - t;
29
- return {
30
- x: t1 ** 2 * x1 + 2 * t1 * t * cx + t ** 2 * x2,
31
- y: t1 ** 2 * y1 + 2 * t1 * t * cy + t ** 2 * y2,
32
- };
33
- };
34
-
35
- /**
36
- * Returns a {x,y} point at a given length, the total length and
37
- * the minimum and maximum {x,y} coordinates of a Q (quadratic-bezier) segment.
38
- *
39
- * @param x1 the starting point X
40
- * @param y1 the starting point Y
41
- * @param qx the control point X
42
- * @param qy the control point Y
43
- * @param x2 the ending point X
44
- * @param y2 the ending point Y
45
- * @param distance the distance to point
46
- * @returns the segment length, point, min & max
47
- */
48
- const segmentQuadFactory = (
49
- x1: number,
50
- y1: number,
51
- qx: number,
52
- qy: number,
53
- x2: number,
54
- y2: number,
55
- distance?: number,
56
- ): LengthFactory => {
57
- const distanceIsNumber = typeof distance === 'number';
58
- let x = x1;
59
- let y = y1;
60
- let LENGTH = 0;
61
- let prev = [x, y, LENGTH];
62
- let cur = [x, y] as [number, number];
63
- let t = 0;
64
- let POINT = { x: 0, y: 0 };
65
- let POINTS = [{ x, y }];
66
-
67
- if (distanceIsNumber && distance <= 0) {
68
- POINT = { x, y };
69
- }
70
-
71
- const sampleSize = 300;
72
- for (let j = 0; j <= sampleSize; j += 1) {
73
- t = j / sampleSize;
74
-
75
- ({ x, y } = getPointAtQuadSegmentLength(x1, y1, qx, qy, x2, y2, t));
76
- POINTS = [...POINTS, { x, y }];
77
- LENGTH += distanceSquareRoot(cur, [x, y]);
78
- cur = [x, y];
79
-
80
- if (distanceIsNumber && LENGTH > distance && distance > prev[2]) {
81
- const dv = (LENGTH - distance) / (LENGTH - prev[2]);
82
-
83
- POINT = {
84
- x: cur[0] * (1 - dv) + prev[0] * dv,
85
- y: cur[1] * (1 - dv) + prev[1] * dv,
86
- };
87
- }
88
- prev = [x, y, LENGTH];
89
- }
90
-
91
- /* istanbul ignore else */
92
- if (distanceIsNumber && distance >= LENGTH) {
93
- POINT = { x: x2, y: y2 };
94
- }
95
-
96
- return {
97
- length: LENGTH,
98
- point: POINT,
99
- min: {
100
- x: Math.min(...POINTS.map(n => n.x)),
101
- y: Math.min(...POINTS.map(n => n.y)),
102
- },
103
- max: {
104
- x: Math.max(...POINTS.map(n => n.x)),
105
- y: Math.max(...POINTS.map(n => n.y)),
106
- },
107
- };
108
- };
109
- export default segmentQuadFactory;