@remotion/paths 4.0.167 → 4.0.168
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.
- package/dist/cut-instruction.d.ts +11 -0
- package/dist/cut-instruction.js +64 -0
- package/dist/cut-path.d.ts +1 -0
- package/dist/cut-path.js +38 -0
- package/dist/evolve-path.js +11 -0
- package/dist/get-bounding-box.js +1 -20
- package/dist/helpers/convert-q-to-c-instruction.d.ts +2 -0
- package/dist/helpers/convert-q-to-c-instruction.js +19 -0
- package/dist/helpers/iterate.d.ts +5 -5
- package/dist/helpers/reduced-analysis.d.ts +13 -0
- package/dist/helpers/reduced-analysis.js +89 -0
- package/dist/helpers/remove-a-s-t-curves.d.ts +1 -1
- package/dist/helpers/remove-a-s-t-curves.js +9 -5
- package/dist/helpers/types.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/interpolate-path/convert-to-same-instruction-type.js +0 -38
- package/dist/interpolate-path/interpolate-instruction-of-same-kind.d.ts +2 -2
- package/dist/interpolate-path/interpolate-instruction-of-same-kind.js +0 -15
- package/dist/interpolate-path/points-to-command.js +6 -2
- package/dist/interpolate-path/split-segment.js +1 -3
- package/dist/reduce-instructions.js +1 -1
- package/dist/warp-path/warp-helpers.js +7 -25
- package/package.json +1 -1
- package/dist/helpers/split-curve.d.ts +0 -47
- package/dist/helpers/split-curve.js +0 -190
- package/dist/interpolate-path.d.ts +0 -8
- package/dist/interpolate-path.js +0 -366
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CInstruction, Point, ReducedInstruction } from './helpers/types';
|
|
2
|
+
export declare function cutCInstruction({ progress, lastPoint, instruction, }: {
|
|
3
|
+
progress: number;
|
|
4
|
+
lastPoint: Point;
|
|
5
|
+
instruction: CInstruction;
|
|
6
|
+
}): CInstruction;
|
|
7
|
+
export declare const cutInstruction: ({ instruction, lastPoint, progress, }: {
|
|
8
|
+
instruction: ReducedInstruction;
|
|
9
|
+
lastPoint: Point;
|
|
10
|
+
progress: number;
|
|
11
|
+
}) => ReducedInstruction;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cutInstruction = exports.cutCInstruction = void 0;
|
|
4
|
+
const cutLInstruction = ({ instruction, lastPoint, progress, }) => {
|
|
5
|
+
const x = lastPoint.x + (instruction.x - lastPoint.x) * progress;
|
|
6
|
+
const y = lastPoint.y + (instruction.y - lastPoint.y) * progress;
|
|
7
|
+
return {
|
|
8
|
+
type: 'L',
|
|
9
|
+
x,
|
|
10
|
+
y,
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
function interpolatePoint(pA, pB, factor) {
|
|
14
|
+
return {
|
|
15
|
+
x: pA.x + (pB.x - pA.x) * factor,
|
|
16
|
+
y: pA.y + (pB.y - pA.y) * factor,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function cutCInstruction({ progress, lastPoint, instruction, }) {
|
|
20
|
+
// De Casteljau's algorithm for Bezier splitting
|
|
21
|
+
const u = progress;
|
|
22
|
+
// Points of original curve
|
|
23
|
+
const p0 = { x: lastPoint.x, y: lastPoint.y };
|
|
24
|
+
const p1 = { x: instruction.cp1x, y: instruction.cp1y };
|
|
25
|
+
const p2 = { x: instruction.cp2x, y: instruction.cp2y };
|
|
26
|
+
const p3 = { x: instruction.x, y: instruction.y };
|
|
27
|
+
// First level of interpolation
|
|
28
|
+
const p01 = interpolatePoint(p0, p1, u);
|
|
29
|
+
const p12 = interpolatePoint(p1, p2, u);
|
|
30
|
+
const p23 = interpolatePoint(p2, p3, u);
|
|
31
|
+
// Second level of interpolation
|
|
32
|
+
const p012 = interpolatePoint(p01, p12, u);
|
|
33
|
+
const p123 = interpolatePoint(p12, p23, u);
|
|
34
|
+
// Third level of interpolation (this is the mid-point of the curve at u)
|
|
35
|
+
const p0123 = interpolatePoint(p012, p123, u);
|
|
36
|
+
return {
|
|
37
|
+
type: 'C',
|
|
38
|
+
cp1x: p01.x,
|
|
39
|
+
cp1y: p01.y,
|
|
40
|
+
cp2x: p012.x,
|
|
41
|
+
cp2y: p012.y,
|
|
42
|
+
x: p0123.x,
|
|
43
|
+
y: p0123.y,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
exports.cutCInstruction = cutCInstruction;
|
|
47
|
+
const cutInstruction = ({ instruction, lastPoint, progress, }) => {
|
|
48
|
+
if (instruction.type === 'M') {
|
|
49
|
+
return instruction;
|
|
50
|
+
}
|
|
51
|
+
if (instruction.type === 'L') {
|
|
52
|
+
return cutLInstruction({ instruction, lastPoint, progress });
|
|
53
|
+
}
|
|
54
|
+
if (instruction.type === 'C') {
|
|
55
|
+
return cutCInstruction({ instruction, lastPoint, progress });
|
|
56
|
+
}
|
|
57
|
+
// TODO: Could we cut it as well?
|
|
58
|
+
if (instruction.type === 'Z') {
|
|
59
|
+
return instruction;
|
|
60
|
+
}
|
|
61
|
+
// @ts-expect-error
|
|
62
|
+
throw new TypeError(`${instruction.type} is not supported.`);
|
|
63
|
+
};
|
|
64
|
+
exports.cutInstruction = cutInstruction;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const cutPath: (d: string, length: number) => string;
|
package/dist/cut-path.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cutPath = void 0;
|
|
4
|
+
const cut_instruction_1 = require("./cut-instruction");
|
|
5
|
+
const reduced_analysis_1 = require("./helpers/reduced-analysis");
|
|
6
|
+
const parse_path_1 = require("./parse-path");
|
|
7
|
+
const reduce_instructions_1 = require("./reduce-instructions");
|
|
8
|
+
const serialize_instructions_1 = require("./serialize-instructions");
|
|
9
|
+
const cutPath = (d, length) => {
|
|
10
|
+
const parsed = (0, parse_path_1.parsePath)(d);
|
|
11
|
+
const reduced = (0, reduce_instructions_1.reduceInstructions)(parsed);
|
|
12
|
+
const constructed = (0, reduced_analysis_1.conductAnalysis)(reduced);
|
|
13
|
+
const newInstructions = [];
|
|
14
|
+
let summedUpLength = 0;
|
|
15
|
+
for (const segment of constructed) {
|
|
16
|
+
for (const instructionAndInfo of segment.instructionsAndInfo) {
|
|
17
|
+
if (summedUpLength + instructionAndInfo.length > length) {
|
|
18
|
+
const remainingLength = length - summedUpLength;
|
|
19
|
+
const progress = remainingLength / instructionAndInfo.length;
|
|
20
|
+
// cut
|
|
21
|
+
const cut = (0, cut_instruction_1.cutInstruction)({
|
|
22
|
+
instruction: instructionAndInfo.instruction,
|
|
23
|
+
lastPoint: instructionAndInfo.startPoint,
|
|
24
|
+
progress,
|
|
25
|
+
});
|
|
26
|
+
newInstructions.push(cut);
|
|
27
|
+
return (0, serialize_instructions_1.serializeInstructions)(newInstructions);
|
|
28
|
+
}
|
|
29
|
+
summedUpLength += instructionAndInfo.length;
|
|
30
|
+
newInstructions.push(instructionAndInfo.instruction);
|
|
31
|
+
if (summedUpLength === length) {
|
|
32
|
+
return (0, serialize_instructions_1.serializeInstructions)(newInstructions);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return (0, serialize_instructions_1.serializeInstructions)(newInstructions);
|
|
37
|
+
};
|
|
38
|
+
exports.cutPath = cutPath;
|
package/dist/evolve-path.js
CHANGED
|
@@ -10,6 +10,17 @@ const get_length_1 = require("./get-length");
|
|
|
10
10
|
*/
|
|
11
11
|
const evolvePath = (progress, path) => {
|
|
12
12
|
const length = (0, get_length_1.getLength)(path);
|
|
13
|
+
if (progress === 0) {
|
|
14
|
+
// Because Remotion has not the same rounding as the browser, the length may be a bit too short.
|
|
15
|
+
// This causes a browser artifact https://github.com/remotion-dev/remotion/issues/3960
|
|
16
|
+
// But any line inbetween length and length * 2 will be invisible.
|
|
17
|
+
// So we just select the middle I guess!
|
|
18
|
+
const extendedLength = length * 1.5;
|
|
19
|
+
return {
|
|
20
|
+
strokeDasharray: `${extendedLength} ${extendedLength}`,
|
|
21
|
+
strokeDashoffset: extendedLength,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
13
24
|
const strokeDasharray = `${length} ${length}`;
|
|
14
25
|
const strokeDashoffset = length - progress * length;
|
|
15
26
|
return { strokeDasharray, strokeDashoffset };
|
package/dist/get-bounding-box.js
CHANGED
|
@@ -133,25 +133,6 @@ const getBoundingBoxFromInstructions = (instructions) => {
|
|
|
133
133
|
y = seg.y;
|
|
134
134
|
break;
|
|
135
135
|
}
|
|
136
|
-
case 'Q': {
|
|
137
|
-
const qxMinMax = minmaxQ([x, seg.cpx, seg.x]);
|
|
138
|
-
if (minX > qxMinMax[0]) {
|
|
139
|
-
minX = qxMinMax[0];
|
|
140
|
-
}
|
|
141
|
-
if (maxX < qxMinMax[1]) {
|
|
142
|
-
maxX = qxMinMax[1];
|
|
143
|
-
}
|
|
144
|
-
const qyMinMax = minmaxQ([y, seg.cpy, seg.y]);
|
|
145
|
-
if (minY > qyMinMax[0]) {
|
|
146
|
-
minY = qyMinMax[0];
|
|
147
|
-
}
|
|
148
|
-
if (maxY < qyMinMax[1]) {
|
|
149
|
-
maxY = qyMinMax[1];
|
|
150
|
-
}
|
|
151
|
-
x = seg.x;
|
|
152
|
-
y = seg.y;
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
136
|
case 'Z':
|
|
156
137
|
x = lastMoveX;
|
|
157
138
|
y = lastMoveY;
|
|
@@ -179,7 +160,7 @@ exports.getBoundingBoxFromInstructions = getBoundingBoxFromInstructions;
|
|
|
179
160
|
*/
|
|
180
161
|
const getBoundingBox = (d) => {
|
|
181
162
|
const parsed = (0, parse_path_1.parsePath)(d);
|
|
182
|
-
const unarced = (0, remove_a_s_t_curves_1.
|
|
163
|
+
const unarced = (0, remove_a_s_t_curves_1.removeATSHVQInstructions)((0, normalize_path_1.normalizeInstructions)(parsed));
|
|
183
164
|
return (0, exports.getBoundingBoxFromInstructions)(unarced);
|
|
184
165
|
};
|
|
185
166
|
exports.getBoundingBox = getBoundingBox;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertQToCInstruction = void 0;
|
|
4
|
+
const convertQToCInstruction = (instruction, startPoint) => {
|
|
5
|
+
const cp1x = startPoint.x + (2 / 3) * (instruction.cpx - startPoint.x);
|
|
6
|
+
const cp1y = startPoint.y + (2 / 3) * (instruction.cpy - startPoint.y);
|
|
7
|
+
const cp2x = instruction.x + (2 / 3) * (instruction.cpx - instruction.x);
|
|
8
|
+
const cp2y = instruction.y + (2 / 3) * (instruction.cpy - instruction.y);
|
|
9
|
+
return {
|
|
10
|
+
type: 'C',
|
|
11
|
+
cp1x,
|
|
12
|
+
cp1y,
|
|
13
|
+
cp2x,
|
|
14
|
+
cp2y,
|
|
15
|
+
x: instruction.x,
|
|
16
|
+
y: instruction.y,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
exports.convertQToCInstruction = convertQToCInstruction;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { AbsoluteInstruction, ReducedInstruction } from './types';
|
|
2
|
-
export declare const iterateOverSegments: <T extends ReducedInstruction>({ segments, iterate, }: {
|
|
3
|
-
segments: AbsoluteInstruction[];
|
|
1
|
+
import type { AbsoluteInstruction, QInstruction, ReducedInstruction } from './types';
|
|
2
|
+
export declare const iterateOverSegments: <T extends QInstruction | ReducedInstruction>({ segments, iterate, }: {
|
|
3
|
+
segments: (AbsoluteInstruction | QInstruction)[];
|
|
4
4
|
iterate: (options: {
|
|
5
|
-
segment: AbsoluteInstruction;
|
|
6
|
-
prevSegment: AbsoluteInstruction | null;
|
|
5
|
+
segment: AbsoluteInstruction | QInstruction;
|
|
6
|
+
prevSegment: (AbsoluteInstruction | QInstruction) | null;
|
|
7
7
|
x: number;
|
|
8
8
|
y: number;
|
|
9
9
|
initialX: number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Point, Properties, ReducedInstruction } from './types';
|
|
2
|
+
type SegmentInstruction = {
|
|
3
|
+
function: Properties | null;
|
|
4
|
+
length: number;
|
|
5
|
+
instruction: ReducedInstruction;
|
|
6
|
+
startPoint: Point;
|
|
7
|
+
};
|
|
8
|
+
type Segment = {
|
|
9
|
+
startPoint: Point;
|
|
10
|
+
instructionsAndInfo: SegmentInstruction[];
|
|
11
|
+
};
|
|
12
|
+
export declare const conductAnalysis: (instructions: ReducedInstruction[]) => Segment[];
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.conductAnalysis = void 0;
|
|
4
|
+
const bezier_1 = require("./bezier");
|
|
5
|
+
const linear_1 = require("./linear");
|
|
6
|
+
const conductAnalysis = (instructions) => {
|
|
7
|
+
let currentPoint = { x: 0, y: 0 };
|
|
8
|
+
let moveStart = { x: 0, y: 0 };
|
|
9
|
+
const segments = [];
|
|
10
|
+
for (let i = 0; i < instructions.length; i++) {
|
|
11
|
+
const instruction = instructions[i];
|
|
12
|
+
// moveTo
|
|
13
|
+
if (instruction.type === 'M') {
|
|
14
|
+
currentPoint = { x: instruction.x, y: instruction.y };
|
|
15
|
+
moveStart = { x: currentPoint.x, y: currentPoint.y };
|
|
16
|
+
segments.push({
|
|
17
|
+
startPoint: { x: instruction.x, y: instruction.y },
|
|
18
|
+
instructionsAndInfo: [
|
|
19
|
+
{
|
|
20
|
+
instruction,
|
|
21
|
+
function: null,
|
|
22
|
+
length: 0,
|
|
23
|
+
startPoint: currentPoint,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (instruction.type === 'L') {
|
|
29
|
+
if (segments.length > 0) {
|
|
30
|
+
const length = Math.sqrt((currentPoint.x - instruction.x) ** 2 +
|
|
31
|
+
(currentPoint.y - instruction.y) ** 2);
|
|
32
|
+
segments[segments.length - 1].instructionsAndInfo.push({
|
|
33
|
+
instruction,
|
|
34
|
+
length,
|
|
35
|
+
function: (0, linear_1.makeLinearPosition)({
|
|
36
|
+
x0: currentPoint.x,
|
|
37
|
+
x1: instruction.x,
|
|
38
|
+
y0: currentPoint.y,
|
|
39
|
+
y1: instruction.y,
|
|
40
|
+
}),
|
|
41
|
+
startPoint: currentPoint,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
currentPoint = { x: instruction.x, y: instruction.y };
|
|
45
|
+
}
|
|
46
|
+
if (instruction.type === 'Z') {
|
|
47
|
+
if (segments.length > 0) {
|
|
48
|
+
const length = Math.sqrt((segments[segments.length - 1].startPoint.x - currentPoint.x) ** 2 +
|
|
49
|
+
(segments[segments.length - 1].startPoint.y - currentPoint.y) ** 2);
|
|
50
|
+
segments[segments.length - 1].instructionsAndInfo.push({
|
|
51
|
+
instruction,
|
|
52
|
+
function: (0, linear_1.makeLinearPosition)({
|
|
53
|
+
x0: currentPoint.x,
|
|
54
|
+
x1: moveStart.x,
|
|
55
|
+
y0: currentPoint.y,
|
|
56
|
+
y1: moveStart.y,
|
|
57
|
+
}),
|
|
58
|
+
length,
|
|
59
|
+
startPoint: { ...currentPoint },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
currentPoint = { x: moveStart.x, y: moveStart.y };
|
|
63
|
+
}
|
|
64
|
+
if (instruction.type === 'C') {
|
|
65
|
+
const curve = (0, bezier_1.makeCubic)({
|
|
66
|
+
startX: currentPoint.x,
|
|
67
|
+
startY: currentPoint.y,
|
|
68
|
+
cp1x: instruction.cp1x,
|
|
69
|
+
cp1y: instruction.cp1y,
|
|
70
|
+
cp2x: instruction.cp2x,
|
|
71
|
+
cp2y: instruction.cp2y,
|
|
72
|
+
x: instruction.x,
|
|
73
|
+
y: instruction.y,
|
|
74
|
+
});
|
|
75
|
+
const length = curve.getTotalLength();
|
|
76
|
+
if (segments.length > 0) {
|
|
77
|
+
segments[segments.length - 1].instructionsAndInfo.push({
|
|
78
|
+
instruction,
|
|
79
|
+
length,
|
|
80
|
+
function: curve,
|
|
81
|
+
startPoint: { ...currentPoint },
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
currentPoint = { x: instruction.x, y: instruction.y };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return segments;
|
|
88
|
+
};
|
|
89
|
+
exports.conductAnalysis = conductAnalysis;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { AbsoluteInstruction, ReducedInstruction } from './types';
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const removeATSHVQInstructions: (segments: AbsoluteInstruction[]) => ReducedInstruction[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.removeATSHVQInstructions = void 0;
|
|
4
|
+
const convert_q_to_c_instruction_1 = require("./convert-q-to-c-instruction");
|
|
4
5
|
const iterate_1 = require("./iterate");
|
|
5
6
|
const TAU = Math.PI * 2;
|
|
6
7
|
function approximate_unit_arc(theta1, delta_theta) {
|
|
@@ -157,7 +158,7 @@ function arcToCircle({ x1, y1, x2, y2, largeArcFlag, sweepFlag, rx, ry, phi, })
|
|
|
157
158
|
});
|
|
158
159
|
}
|
|
159
160
|
// Requires path to be normalized
|
|
160
|
-
const
|
|
161
|
+
const removeATSHVQInstructions = (segments) => {
|
|
161
162
|
return (0, iterate_1.iterateOverSegments)({
|
|
162
163
|
segments,
|
|
163
164
|
iterate: ({ segment, prevSegment, x, y, cpX, cpY }) => {
|
|
@@ -223,13 +224,13 @@ const removeATSHVInstructions = (segments) => {
|
|
|
223
224
|
const newControlX = x - vectorX;
|
|
224
225
|
const newControlY = y - vectorY;
|
|
225
226
|
return [
|
|
226
|
-
{
|
|
227
|
+
(0, convert_q_to_c_instruction_1.convertQToCInstruction)({
|
|
227
228
|
type: 'Q',
|
|
228
229
|
cpx: newControlX,
|
|
229
230
|
cpy: newControlY,
|
|
230
231
|
x: segment.x,
|
|
231
232
|
y: segment.y,
|
|
232
|
-
},
|
|
233
|
+
}, { x, y }),
|
|
233
234
|
];
|
|
234
235
|
}
|
|
235
236
|
if (segment.type === 'S') {
|
|
@@ -264,8 +265,11 @@ const removeATSHVInstructions = (segments) => {
|
|
|
264
265
|
},
|
|
265
266
|
];
|
|
266
267
|
}
|
|
268
|
+
if (segment.type === 'Q') {
|
|
269
|
+
return [(0, convert_q_to_c_instruction_1.convertQToCInstruction)(segment, { x, y })];
|
|
270
|
+
}
|
|
267
271
|
return [segment];
|
|
268
272
|
},
|
|
269
273
|
});
|
|
270
274
|
};
|
|
271
|
-
exports.
|
|
275
|
+
exports.removeATSHVQInstructions = removeATSHVQInstructions;
|
package/dist/helpers/types.d.ts
CHANGED
|
@@ -54,8 +54,8 @@ export type QInstruction = {
|
|
|
54
54
|
export type ZInstruction = {
|
|
55
55
|
type: 'Z';
|
|
56
56
|
};
|
|
57
|
-
export type ReducedInstruction = MInstruction | LInstruction | CInstruction |
|
|
58
|
-
export type AbsoluteInstruction = ReducedInstruction | {
|
|
57
|
+
export type ReducedInstruction = MInstruction | LInstruction | CInstruction | ZInstruction;
|
|
58
|
+
export type AbsoluteInstruction = ReducedInstruction | QInstruction | {
|
|
59
59
|
type: 'A';
|
|
60
60
|
rx: number;
|
|
61
61
|
ry: number;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PathInternals = exports.warpPath = exports.translatePath = exports.serializeInstructions = exports.scalePath = exports.reversePath = exports.resetPath = exports.reduceInstructions = exports.parsePath = exports.normalizePath = exports.interpolatePath = exports.getTangentAtLength = exports.getSubpaths = exports.getPointAtLength = exports.getLength = exports.getInstructionIndexAtLength = exports.getBoundingBox = exports.extendViewBox = exports.evolvePath = void 0;
|
|
4
|
-
const
|
|
4
|
+
const cut_path_1 = require("./cut-path");
|
|
5
5
|
const debug_path_1 = require("./debug-path");
|
|
6
|
+
const get_bounding_box_1 = require("./get-bounding-box");
|
|
6
7
|
var evolve_path_1 = require("./evolve-path");
|
|
7
8
|
Object.defineProperty(exports, "evolvePath", { enumerable: true, get: function () { return evolve_path_1.evolvePath; } });
|
|
8
9
|
var extend_viewbox_1 = require("./extend-viewbox");
|
|
@@ -42,4 +43,5 @@ Object.defineProperty(exports, "warpPath", { enumerable: true, get: function ()
|
|
|
42
43
|
exports.PathInternals = {
|
|
43
44
|
getBoundingBoxFromInstructions: get_bounding_box_1.getBoundingBoxFromInstructions,
|
|
44
45
|
debugPath: debug_path_1.debugPath,
|
|
46
|
+
cutPath: cut_path_1.cutPath,
|
|
45
47
|
};
|
|
@@ -26,43 +26,8 @@ const convertToCCommand = (command, currentPoint) => {
|
|
|
26
26
|
y: command.y,
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
if (command.type === 'Q') {
|
|
30
|
-
return {
|
|
31
|
-
type: 'C',
|
|
32
|
-
cp1x: command.cpx,
|
|
33
|
-
cp1y: command.cpy,
|
|
34
|
-
cp2x: command.cpx,
|
|
35
|
-
cp2y: command.cpy,
|
|
36
|
-
x: command.x,
|
|
37
|
-
y: command.y,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
29
|
throw new Error('all types should be handled');
|
|
41
30
|
};
|
|
42
|
-
const convertToQCommand = (command) => {
|
|
43
|
-
if (command.type === 'M' || command.type === 'Q' || command.type === 'Z') {
|
|
44
|
-
throw new Error('unexpected');
|
|
45
|
-
}
|
|
46
|
-
if (command.type === 'C') {
|
|
47
|
-
return {
|
|
48
|
-
type: 'Q',
|
|
49
|
-
cpx: command.cp1x,
|
|
50
|
-
cpy: command.cp1y,
|
|
51
|
-
x: command.x,
|
|
52
|
-
y: command.y,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
if (command.type === 'L') {
|
|
56
|
-
return {
|
|
57
|
-
type: 'Q',
|
|
58
|
-
cpx: command.x,
|
|
59
|
-
cpy: command.y,
|
|
60
|
-
x: command.x,
|
|
61
|
-
y: command.y,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
throw new Error('unhandled');
|
|
65
|
-
};
|
|
66
31
|
/**
|
|
67
32
|
* Converts command A to have the same type as command B.
|
|
68
33
|
*
|
|
@@ -96,9 +61,6 @@ function convertToSameInstructionType(aCommand, bCommand, currentPoint) {
|
|
|
96
61
|
if (bCommand.type === 'L') {
|
|
97
62
|
return convertToLCommand(aCommand);
|
|
98
63
|
}
|
|
99
|
-
if (bCommand.type === 'Q') {
|
|
100
|
-
return convertToQCommand(aCommand);
|
|
101
|
-
}
|
|
102
64
|
if (bCommand.type === 'Z') {
|
|
103
65
|
return {
|
|
104
66
|
type: 'Z',
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { CInstruction, LInstruction, MInstruction,
|
|
2
|
-
export declare const interpolateInstructionOfSameKind: (t: number, first: ReducedInstruction, second: ReducedInstruction) => LInstruction | CInstruction | MInstruction |
|
|
1
|
+
import type { CInstruction, LInstruction, MInstruction, ReducedInstruction, ZInstruction } from '../helpers/types';
|
|
2
|
+
export declare const interpolateInstructionOfSameKind: (t: number, first: ReducedInstruction, second: ReducedInstruction) => LInstruction | CInstruction | MInstruction | ZInstruction;
|
|
@@ -19,15 +19,6 @@ const interpolateCInstructions = (t, first, second) => {
|
|
|
19
19
|
y: (1 - t) * first.y + t * second.y,
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
|
-
const interpolateQInstructions = (t, first, second) => {
|
|
23
|
-
return {
|
|
24
|
-
type: 'Q',
|
|
25
|
-
cpx: (1 - t) * first.cpx + t * second.cpx,
|
|
26
|
-
cpy: (1 - t) * first.cpy + t * second.cpy,
|
|
27
|
-
x: (1 - t) * first.x + t * second.x,
|
|
28
|
-
y: (1 - t) * first.y + t * second.y,
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
22
|
const interpolateMInstructions = (t, first, second) => {
|
|
32
23
|
return {
|
|
33
24
|
type: 'M',
|
|
@@ -48,12 +39,6 @@ const interpolateInstructionOfSameKind = (t, first, second) => {
|
|
|
48
39
|
}
|
|
49
40
|
return interpolateCInstructions(t, first, second);
|
|
50
41
|
}
|
|
51
|
-
if (first.type === 'Q') {
|
|
52
|
-
if (second.type !== 'Q') {
|
|
53
|
-
throw new Error('mismatch');
|
|
54
|
-
}
|
|
55
|
-
return interpolateQInstructions(t, first, second);
|
|
56
|
-
}
|
|
57
42
|
if (first.type === 'M') {
|
|
58
43
|
if (second.type !== 'M') {
|
|
59
44
|
throw new Error('mismatch');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.pointsToInstruction = void 0;
|
|
4
|
+
const convert_q_to_c_instruction_1 = require("../helpers/convert-q-to-c-instruction");
|
|
4
5
|
/**
|
|
5
6
|
* Convert segments represented as points back into a command object
|
|
6
7
|
*
|
|
@@ -29,13 +30,16 @@ function pointsToInstruction(points) {
|
|
|
29
30
|
if (points.length === 3) {
|
|
30
31
|
const x1 = points[1][0];
|
|
31
32
|
const y1 = points[1][1];
|
|
32
|
-
return {
|
|
33
|
+
return (0, convert_q_to_c_instruction_1.convertQToCInstruction)({
|
|
33
34
|
type: 'Q',
|
|
34
35
|
cpx: x1,
|
|
35
36
|
cpy: y1,
|
|
36
37
|
x,
|
|
37
38
|
y,
|
|
38
|
-
}
|
|
39
|
+
}, {
|
|
40
|
+
x: points[0][0],
|
|
41
|
+
y: points[0][1],
|
|
42
|
+
});
|
|
39
43
|
}
|
|
40
44
|
return {
|
|
41
45
|
type: 'L',
|
|
@@ -17,9 +17,7 @@ const split_curve_1 = require("./split-curve");
|
|
|
17
17
|
function splitSegmentInstructions(commandStart, commandEnd, segmentCount) {
|
|
18
18
|
let segments = [];
|
|
19
19
|
// line, quadratic bezier, or cubic bezier
|
|
20
|
-
if (commandEnd.type === 'L' ||
|
|
21
|
-
commandEnd.type === 'Q' ||
|
|
22
|
-
commandEnd.type === 'C') {
|
|
20
|
+
if (commandEnd.type === 'L' || commandEnd.type === 'C') {
|
|
23
21
|
if (commandStart.type !== 'Z') {
|
|
24
22
|
segments = segments.concat((0, split_curve_1.splitCurveInstructions)(commandStart.x, commandStart.y, commandEnd, segmentCount));
|
|
25
23
|
}
|
|
@@ -10,6 +10,6 @@ const normalize_path_1 = require("./normalize-path");
|
|
|
10
10
|
*/
|
|
11
11
|
const reduceInstructions = (instruction) => {
|
|
12
12
|
const simplified = (0, normalize_path_1.normalizeInstructions)(instruction);
|
|
13
|
-
return (0, remove_a_s_t_curves_1.
|
|
13
|
+
return (0, remove_a_s_t_curves_1.removeATSHVQInstructions)(simplified);
|
|
14
14
|
};
|
|
15
15
|
exports.reduceInstructions = reduceInstructions;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fixZInstruction = exports.warpTransform = exports.svgPathInterpolate = void 0;
|
|
4
|
+
const convert_q_to_c_instruction_1 = require("../helpers/convert-q-to-c-instruction");
|
|
4
5
|
const euclideanDistance = (points) => {
|
|
5
6
|
const startPoint = points[0];
|
|
6
7
|
const endPoint = points[points.length - 1];
|
|
@@ -65,13 +66,16 @@ function createLineSegment(points) {
|
|
|
65
66
|
y: points[1][1],
|
|
66
67
|
};
|
|
67
68
|
case 3:
|
|
68
|
-
return {
|
|
69
|
+
return (0, convert_q_to_c_instruction_1.convertQToCInstruction)({
|
|
69
70
|
type: 'Q',
|
|
70
71
|
cpx: points[1][0],
|
|
71
72
|
cpy: points[1][1],
|
|
72
73
|
x: points[2][0],
|
|
73
74
|
y: points[2][1],
|
|
74
|
-
}
|
|
75
|
+
}, {
|
|
76
|
+
x: points[0][0],
|
|
77
|
+
y: points[0][1],
|
|
78
|
+
});
|
|
75
79
|
case 4:
|
|
76
80
|
return {
|
|
77
81
|
type: 'C',
|
|
@@ -104,13 +108,7 @@ function warpInterpolate(path, threshold, deltaFunction) {
|
|
|
104
108
|
if (segment.type === 'L') {
|
|
105
109
|
points.push([segment.x, segment.y]);
|
|
106
110
|
}
|
|
107
|
-
if (segment.type === '
|
|
108
|
-
points.push([segment.cpx, segment.cpy]);
|
|
109
|
-
points.push([segment.x, segment.y]);
|
|
110
|
-
}
|
|
111
|
-
if (segment.type === 'C' ||
|
|
112
|
-
segment.type === 'Q' ||
|
|
113
|
-
segment.type === 'L') {
|
|
111
|
+
if (segment.type === 'C' || segment.type === 'L') {
|
|
114
112
|
return interpolateUntil(points, threshold, deltaFunction).map((rawSegment) => createLineSegment(rawSegment));
|
|
115
113
|
}
|
|
116
114
|
return [segment];
|
|
@@ -144,22 +142,6 @@ const warpTransform = (path, transformer) => {
|
|
|
144
142
|
},
|
|
145
143
|
];
|
|
146
144
|
}
|
|
147
|
-
if (segment.type === 'Q') {
|
|
148
|
-
const { x, y } = transformer({ x: segment.x, y: segment.y });
|
|
149
|
-
const { x: cpx, y: cpy } = transformer({
|
|
150
|
-
x: segment.cpx,
|
|
151
|
-
y: segment.cpy,
|
|
152
|
-
});
|
|
153
|
-
return [
|
|
154
|
-
{
|
|
155
|
-
type: 'Q',
|
|
156
|
-
x,
|
|
157
|
-
y,
|
|
158
|
-
cpx,
|
|
159
|
-
cpy,
|
|
160
|
-
},
|
|
161
|
-
];
|
|
162
|
-
}
|
|
163
145
|
if (segment.type === 'C') {
|
|
164
146
|
const { x, y } = transformer({ x: segment.x, y: segment.y });
|
|
165
147
|
const { x: cp1x, y: cp1y } = transformer({
|
package/package.json
CHANGED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* List of params for each command type in a path `d` attribute
|
|
3
|
-
*/
|
|
4
|
-
export declare const typeMap: {
|
|
5
|
-
M: string[];
|
|
6
|
-
L: string[];
|
|
7
|
-
H: string[];
|
|
8
|
-
V: string[];
|
|
9
|
-
C: string[];
|
|
10
|
-
S: string[];
|
|
11
|
-
Q: string[];
|
|
12
|
-
T: string[];
|
|
13
|
-
A: string[];
|
|
14
|
-
Z: never[];
|
|
15
|
-
m: string[];
|
|
16
|
-
l: string[];
|
|
17
|
-
h: string[];
|
|
18
|
-
v: string[];
|
|
19
|
-
c: string[];
|
|
20
|
-
s: string[];
|
|
21
|
-
q: string[];
|
|
22
|
-
t: string[];
|
|
23
|
-
a: string[];
|
|
24
|
-
z: never[];
|
|
25
|
-
};
|
|
26
|
-
export type Command = {
|
|
27
|
-
x2?: number | undefined;
|
|
28
|
-
y2?: number | undefined;
|
|
29
|
-
x1?: number | undefined;
|
|
30
|
-
y1?: number | undefined;
|
|
31
|
-
x?: number;
|
|
32
|
-
y?: number;
|
|
33
|
-
xAxisRotate?: number;
|
|
34
|
-
largeArcFlag?: boolean;
|
|
35
|
-
sweepFlag?: boolean;
|
|
36
|
-
type: keyof typeof typeMap;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Convert command objects to arrays of points, run de Casteljau's algorithm on it
|
|
40
|
-
* to split into to the desired number of segments.
|
|
41
|
-
*
|
|
42
|
-
* @param {Object} commandStart The start command object
|
|
43
|
-
* @param {Object} commandEnd The end command object
|
|
44
|
-
* @param {Number} segmentCount The number of segments to create
|
|
45
|
-
* @return {Object[]} An array of commands representing the segments in sequence
|
|
46
|
-
*/
|
|
47
|
-
export declare const splitCurve: (commandStart: Command, commandEnd: Command, segmentCount: number) => Command[];
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
|
|
4
|
-
Copied and adapted from https://github.com/pbeshai/d3-interpolate-path:
|
|
5
|
-
Copyright 2016, Peter Beshai
|
|
6
|
-
All rights reserved.
|
|
7
|
-
|
|
8
|
-
Redistribution and use in source and binary forms, with or without modification,
|
|
9
|
-
are permitted provided that the following conditions are met:
|
|
10
|
-
|
|
11
|
-
* Redistributions of source code must retain the above copyright notice, this
|
|
12
|
-
list of conditions and the following disclaimer.
|
|
13
|
-
|
|
14
|
-
* Redistributions in binary form must reproduce the above copyright notice,
|
|
15
|
-
this list of conditions and the following disclaimer in the documentation
|
|
16
|
-
and/or other materials provided with the distribution.
|
|
17
|
-
|
|
18
|
-
* Neither the name of the author nor the names of contributors may be used to
|
|
19
|
-
endorse or promote products derived from this software without specific prior
|
|
20
|
-
written permission.
|
|
21
|
-
|
|
22
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
23
|
-
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
24
|
-
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
25
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
26
|
-
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
27
|
-
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
28
|
-
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
29
|
-
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
30
|
-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
31
|
-
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
-
|
|
33
|
-
*/
|
|
34
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.splitCurve = exports.typeMap = void 0;
|
|
36
|
-
/**
|
|
37
|
-
* de Casteljau's algorithm for drawing and splitting bezier curves.
|
|
38
|
-
* Inspired by https://pomax.github.io/bezierinfo/
|
|
39
|
-
*
|
|
40
|
-
* @param {Number[][]} points Array of [x,y] points: [start, control1, control2, ..., end]
|
|
41
|
-
* The original segment to split.
|
|
42
|
-
* @param {Number} t Where to split the curve (value between [0, 1])
|
|
43
|
-
* @return {Object} An object { left, right } where left is the segment from 0..t and
|
|
44
|
-
* right is the segment from t..1.
|
|
45
|
-
*/
|
|
46
|
-
function decasteljau(points, t) {
|
|
47
|
-
const left = [];
|
|
48
|
-
const right = [];
|
|
49
|
-
function decasteljauRecurse(_points, _t) {
|
|
50
|
-
if (_points.length === 1) {
|
|
51
|
-
left.push(_points[0]);
|
|
52
|
-
right.push(_points[0]);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
const newPoints = Array(_points.length - 1);
|
|
56
|
-
for (let i = 0; i < newPoints.length; i++) {
|
|
57
|
-
if (i === 0) {
|
|
58
|
-
left.push(_points[0]);
|
|
59
|
-
}
|
|
60
|
-
if (i === newPoints.length - 1) {
|
|
61
|
-
right.push(_points[i + 1]);
|
|
62
|
-
}
|
|
63
|
-
newPoints[i] = [
|
|
64
|
-
(1 - _t) * _points[i][0] + _t * _points[i + 1][0],
|
|
65
|
-
(1 - _t) * _points[i][1] + _t * _points[i + 1][1],
|
|
66
|
-
];
|
|
67
|
-
}
|
|
68
|
-
decasteljauRecurse(newPoints, _t);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (points.length) {
|
|
72
|
-
decasteljauRecurse(points, t);
|
|
73
|
-
}
|
|
74
|
-
return { left, right: right.reverse() };
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* List of params for each command type in a path `d` attribute
|
|
78
|
-
*/
|
|
79
|
-
exports.typeMap = {
|
|
80
|
-
M: ['x', 'y'],
|
|
81
|
-
L: ['x', 'y'],
|
|
82
|
-
H: ['x'],
|
|
83
|
-
V: ['y'],
|
|
84
|
-
C: ['x1', 'y1', 'x2', 'y2', 'x', 'y'],
|
|
85
|
-
S: ['x2', 'y2', 'x', 'y'],
|
|
86
|
-
Q: ['x1', 'y1', 'x', 'y'],
|
|
87
|
-
T: ['x', 'y'],
|
|
88
|
-
A: ['rx', 'ry', 'xAxisRotation', 'largeArcFlag', 'sweepFlag', 'x', 'y'],
|
|
89
|
-
Z: [],
|
|
90
|
-
m: ['x', 'y'],
|
|
91
|
-
l: ['x', 'y'],
|
|
92
|
-
h: ['x'],
|
|
93
|
-
v: ['y'],
|
|
94
|
-
c: ['x1', 'y1', 'x2', 'y2', 'x', 'y'],
|
|
95
|
-
s: ['x2', 'y2', 'x', 'y'],
|
|
96
|
-
q: ['x1', 'y1', 'x', 'y'],
|
|
97
|
-
t: ['x', 'y'],
|
|
98
|
-
a: ['rx', 'ry', 'xAxisRotation', 'largeArcFlag', 'sweepFlag', 'x', 'y'],
|
|
99
|
-
z: [],
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* Convert segments represented as points back into a command object
|
|
103
|
-
*
|
|
104
|
-
* @param {Number[][]} points Array of [x,y] points: [start, control1, control2, ..., end]
|
|
105
|
-
* Represents a segment
|
|
106
|
-
* @return {Object} A command object representing the segment.
|
|
107
|
-
*/
|
|
108
|
-
function pointsToCommand(points) {
|
|
109
|
-
let x2;
|
|
110
|
-
let y2;
|
|
111
|
-
let x1;
|
|
112
|
-
let y1;
|
|
113
|
-
if (points.length === 4) {
|
|
114
|
-
x2 = points[2][0];
|
|
115
|
-
y2 = points[2][1];
|
|
116
|
-
}
|
|
117
|
-
if (points.length >= 3) {
|
|
118
|
-
x1 = points[1][0];
|
|
119
|
-
y1 = points[1][1];
|
|
120
|
-
}
|
|
121
|
-
const x = points[points.length - 1][0];
|
|
122
|
-
const y = points[points.length - 1][1];
|
|
123
|
-
let type = 'L';
|
|
124
|
-
if (points.length === 4) {
|
|
125
|
-
// start, control1, control2, end
|
|
126
|
-
type = 'C';
|
|
127
|
-
}
|
|
128
|
-
else if (points.length === 3) {
|
|
129
|
-
// start, control, end
|
|
130
|
-
type = 'Q';
|
|
131
|
-
}
|
|
132
|
-
return { x2, y2, x1, y1, x, y, type };
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Runs de Casteljau's algorithm enough times to produce the desired number of segments.
|
|
136
|
-
*
|
|
137
|
-
* @param {Number[][]} points Array of [x,y] points for de Casteljau (the initial segment to split)
|
|
138
|
-
* @param {Number} segmentCount Number of segments to split the original into
|
|
139
|
-
* @return {Number[][][]} Array of segments
|
|
140
|
-
*/
|
|
141
|
-
function splitCurveAsPoints(points, segmentCount) {
|
|
142
|
-
segmentCount = segmentCount || 2;
|
|
143
|
-
const segments = [];
|
|
144
|
-
let remainingCurve = points;
|
|
145
|
-
const tIncrement = 1 / segmentCount;
|
|
146
|
-
// x-----x-----x-----x
|
|
147
|
-
// t= 0.33 0.66 1
|
|
148
|
-
// x-----o-----------x
|
|
149
|
-
// r= 0.33
|
|
150
|
-
// x-----o-----x
|
|
151
|
-
// r= 0.5 (0.33 / (1 - 0.33)) === tIncrement / (1 - (tIncrement * (i - 1))
|
|
152
|
-
// x-----x-----x-----x----x
|
|
153
|
-
// t= 0.25 0.5 0.75 1
|
|
154
|
-
// x-----o----------------x
|
|
155
|
-
// r= 0.25
|
|
156
|
-
// x-----o----------x
|
|
157
|
-
// r= 0.33 (0.25 / (1 - 0.25))
|
|
158
|
-
// x-----o----x
|
|
159
|
-
// r= 0.5 (0.25 / (1 - 0.5))
|
|
160
|
-
for (let i = 0; i < segmentCount - 1; i++) {
|
|
161
|
-
const tRelative = tIncrement / (1 - tIncrement * i);
|
|
162
|
-
const split = decasteljau(remainingCurve, tRelative);
|
|
163
|
-
segments.push(split.left);
|
|
164
|
-
remainingCurve = split.right;
|
|
165
|
-
}
|
|
166
|
-
// last segment is just to the end from the last point
|
|
167
|
-
segments.push(remainingCurve);
|
|
168
|
-
return segments;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Convert command objects to arrays of points, run de Casteljau's algorithm on it
|
|
172
|
-
* to split into to the desired number of segments.
|
|
173
|
-
*
|
|
174
|
-
* @param {Object} commandStart The start command object
|
|
175
|
-
* @param {Object} commandEnd The end command object
|
|
176
|
-
* @param {Number} segmentCount The number of segments to create
|
|
177
|
-
* @return {Object[]} An array of commands representing the segments in sequence
|
|
178
|
-
*/
|
|
179
|
-
const splitCurve = (commandStart, commandEnd, segmentCount) => {
|
|
180
|
-
const points = [[commandStart.x, commandStart.y]];
|
|
181
|
-
if (commandEnd.x1 !== null && commandEnd.x1 !== undefined) {
|
|
182
|
-
points.push([commandEnd.x1, commandEnd.y1]);
|
|
183
|
-
}
|
|
184
|
-
if (commandEnd.x2 !== null && commandEnd.x2 !== undefined) {
|
|
185
|
-
points.push([commandEnd.x2, commandEnd.y2]);
|
|
186
|
-
}
|
|
187
|
-
points.push([commandEnd.x, commandEnd.y]);
|
|
188
|
-
return splitCurveAsPoints(points, segmentCount).map(pointsToCommand);
|
|
189
|
-
};
|
|
190
|
-
exports.splitCurve = splitCurve;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Interpolates between two SVG paths.
|
|
3
|
-
* @param {number} value A number - 0 means first path, 1 means second path, any other values will be interpolated
|
|
4
|
-
* @param {string} firstPath The first valid SVG path
|
|
5
|
-
* @param {string} secondPath The second valid SVG path
|
|
6
|
-
* @see [Documentation](https://remotion.dev/docs/paths/interpolate-path)
|
|
7
|
-
*/
|
|
8
|
-
export declare const interpolatePath: (value: number, firstPath: string, secondPath: string) => string;
|
package/dist/interpolate-path.js
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
|
|
4
|
-
Copied and adapted from https://github.com/pbeshai/d3-interpolate-path:
|
|
5
|
-
Copyright 2016, Peter Beshai
|
|
6
|
-
All rights reserved.
|
|
7
|
-
|
|
8
|
-
Redistribution and use in source and binary forms, with or without modification,
|
|
9
|
-
are permitted provided that the following conditions are met:
|
|
10
|
-
|
|
11
|
-
* Redistributions of source code must retain the above copyright notice, this
|
|
12
|
-
list of conditions and the following disclaimer.
|
|
13
|
-
|
|
14
|
-
* Redistributions in binary form must reproduce the above copyright notice,
|
|
15
|
-
this list of conditions and the following disclaimer in the documentation
|
|
16
|
-
and/or other materials provided with the distribution.
|
|
17
|
-
|
|
18
|
-
* Neither the name of the author nor the names of contributors may be used to
|
|
19
|
-
endorse or promote products derived from this software without specific prior
|
|
20
|
-
written permission.
|
|
21
|
-
|
|
22
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
23
|
-
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
24
|
-
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
25
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
26
|
-
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
27
|
-
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
28
|
-
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
29
|
-
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
30
|
-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
31
|
-
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
32
|
-
|
|
33
|
-
*/
|
|
34
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.interpolatePath = void 0;
|
|
36
|
-
const split_curve_1 = require("./helpers/split-curve");
|
|
37
|
-
const commandTokenRegex = /[MLCSTQAHVZmlcstqahv]|-?[\d.e+-]+/g;
|
|
38
|
-
function arrayOfLength(length, value) {
|
|
39
|
-
const array = Array(length);
|
|
40
|
-
for (let i = 0; i < length; i++) {
|
|
41
|
-
array[i] = value;
|
|
42
|
-
}
|
|
43
|
-
return array;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Converts a command object to a string to be used in a `d` attribute
|
|
47
|
-
* @param {Object} command A command object
|
|
48
|
-
* @return {String} The string for the `d` attribute
|
|
49
|
-
*/
|
|
50
|
-
function commandToString(command) {
|
|
51
|
-
return `${command.type} ${split_curve_1.typeMap[command.type]
|
|
52
|
-
.map((p) => command[p])
|
|
53
|
-
.join(' ')}`;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Converts command A to have the same type as command B.
|
|
57
|
-
*
|
|
58
|
-
* e.g., L0,5 -> C0,5,0,5,0,5
|
|
59
|
-
*
|
|
60
|
-
* Uses these rules:
|
|
61
|
-
* x1 <- x
|
|
62
|
-
* x2 <- x
|
|
63
|
-
* y1 <- y
|
|
64
|
-
* y2 <- y
|
|
65
|
-
* rx <- 0
|
|
66
|
-
* ry <- 0
|
|
67
|
-
* xAxisRotation <- read from B
|
|
68
|
-
* largeArcFlag <- read from B
|
|
69
|
-
* sweepflag <- read from B
|
|
70
|
-
*
|
|
71
|
-
* @param {Object} aCommand Command object from path `d` attribute
|
|
72
|
-
* @param {Object} bCommand Command object from path `d` attribute to match against
|
|
73
|
-
* @return {Object} aCommand converted to type of bCommand
|
|
74
|
-
*/
|
|
75
|
-
function convertToSameType(aCommand, bCommand) {
|
|
76
|
-
const conversionMap = {
|
|
77
|
-
x1: 'x',
|
|
78
|
-
y1: 'y',
|
|
79
|
-
x2: 'x',
|
|
80
|
-
y2: 'y',
|
|
81
|
-
};
|
|
82
|
-
const readFromBKeys = ['xAxisRotation', 'largeArcFlag', 'sweepFlag'];
|
|
83
|
-
// convert (but ignore M types)
|
|
84
|
-
if (aCommand.type !== bCommand.type && bCommand.type.toUpperCase() !== 'M') {
|
|
85
|
-
const aConverted = {
|
|
86
|
-
type: bCommand.type,
|
|
87
|
-
};
|
|
88
|
-
Object.keys(bCommand).forEach((bKey) => {
|
|
89
|
-
const bValue = bCommand[bKey];
|
|
90
|
-
// first read from the A command
|
|
91
|
-
let aValue = aCommand[bKey];
|
|
92
|
-
// if it is one of these values, read from B no matter what
|
|
93
|
-
if (aValue === undefined) {
|
|
94
|
-
if (readFromBKeys.includes(bKey)) {
|
|
95
|
-
aValue = bValue;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
// if it wasn't in the A command, see if an equivalent was
|
|
99
|
-
if (aValue === undefined &&
|
|
100
|
-
conversionMap[bKey]) {
|
|
101
|
-
aValue =
|
|
102
|
-
aCommand[conversionMap[bKey]];
|
|
103
|
-
}
|
|
104
|
-
// if it doesn't have a converted value, use 0
|
|
105
|
-
if (aValue === undefined) {
|
|
106
|
-
aValue = 0;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
// @ts-expect-error
|
|
111
|
-
aConverted[bKey] = aValue;
|
|
112
|
-
});
|
|
113
|
-
// update the type to match B
|
|
114
|
-
aConverted.type = bCommand.type;
|
|
115
|
-
aCommand = aConverted;
|
|
116
|
-
}
|
|
117
|
-
return aCommand;
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Interpolate between command objects commandStart and commandEnd segmentCount times.
|
|
121
|
-
* If the types are L, Q, or C then the curves are split as per de Casteljau's algorithm.
|
|
122
|
-
* Otherwise we just copy commandStart segmentCount - 1 times, finally ending with commandEnd.
|
|
123
|
-
*
|
|
124
|
-
* @param {Object} commandStart Command object at the beginning of the segment
|
|
125
|
-
* @param {Object} commandEnd Command object at the end of the segment
|
|
126
|
-
* @param {Number} segmentCount The number of segments to split this into. If only 1
|
|
127
|
-
* Then [commandEnd] is returned.
|
|
128
|
-
* @return {Object[]} Array of ~segmentCount command objects between commandStart and
|
|
129
|
-
* commandEnd. (Can be segmentCount+1 objects if commandStart is type M).
|
|
130
|
-
*/
|
|
131
|
-
function splitSegment(commandStart, commandEnd, segmentCount) {
|
|
132
|
-
let segments = [];
|
|
133
|
-
// line, quadratic bezier, or cubic bezier
|
|
134
|
-
if (commandEnd.type === 'L' ||
|
|
135
|
-
commandEnd.type === 'Q' ||
|
|
136
|
-
commandEnd.type === 'C') {
|
|
137
|
-
segments = segments.concat((0, split_curve_1.splitCurve)(commandStart, commandEnd, segmentCount));
|
|
138
|
-
// general case - just copy the same point
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
const copyCommand = { ...commandStart };
|
|
142
|
-
// convert M to L
|
|
143
|
-
if (copyCommand.type === 'M') {
|
|
144
|
-
copyCommand.type = 'L';
|
|
145
|
-
}
|
|
146
|
-
segments = segments.concat(arrayOfLength(segmentCount - 1, undefined).map(() => copyCommand));
|
|
147
|
-
segments.push(commandEnd);
|
|
148
|
-
}
|
|
149
|
-
return segments;
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Extends an array of commandsToExtend to the length of the referenceCommands by
|
|
153
|
-
* splitting segments until the number of commands match. Ensures all the actual
|
|
154
|
-
* points of commandsToExtend are in the extended array.
|
|
155
|
-
*
|
|
156
|
-
* @param {Object[]} commandsToExtend The command object array to extend
|
|
157
|
-
* @param {Object[]} referenceCommands The command object array to match in length
|
|
158
|
-
* @return {Object[]} The extended commandsToExtend array
|
|
159
|
-
*/
|
|
160
|
-
function extend(commandsToExtend, referenceCommands) {
|
|
161
|
-
// compute insertion points:
|
|
162
|
-
// number of segments in the path to extend
|
|
163
|
-
const numSegmentsToExtend = commandsToExtend.length - 1;
|
|
164
|
-
// number of segments in the reference path.
|
|
165
|
-
const numReferenceSegments = referenceCommands.length - 1;
|
|
166
|
-
// this value is always between [0, 1].
|
|
167
|
-
const segmentRatio = numSegmentsToExtend / numReferenceSegments;
|
|
168
|
-
// create a map, mapping segments in referenceCommands to how many points
|
|
169
|
-
// should be added in that segment (should always be >= 1 since we need each
|
|
170
|
-
// point itself).
|
|
171
|
-
// 0 = segment 0-1, 1 = segment 1-2, n-1 = last vertex
|
|
172
|
-
const countPointsPerSegment = arrayOfLength(numReferenceSegments, undefined).reduce((accum, _d, i) => {
|
|
173
|
-
const insertIndex = Math.floor(segmentRatio * i);
|
|
174
|
-
accum[insertIndex] = (accum[insertIndex] || 0) + 1;
|
|
175
|
-
return accum;
|
|
176
|
-
}, []);
|
|
177
|
-
// extend each segment to have the correct number of points for a smooth interpolation
|
|
178
|
-
const extended = countPointsPerSegment.reduce((_extended, segmentCount, i) => {
|
|
179
|
-
// if last command, just add `segmentCount` number of times
|
|
180
|
-
if (i === commandsToExtend.length - 1) {
|
|
181
|
-
const lastCommandCopies = arrayOfLength(segmentCount, {
|
|
182
|
-
...commandsToExtend[commandsToExtend.length - 1],
|
|
183
|
-
});
|
|
184
|
-
// convert M to L
|
|
185
|
-
if (lastCommandCopies[0].type === 'M') {
|
|
186
|
-
lastCommandCopies.forEach((d) => {
|
|
187
|
-
d.type = 'L';
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
return _extended.concat(lastCommandCopies);
|
|
191
|
-
}
|
|
192
|
-
// otherwise, split the segment segmentCount times.
|
|
193
|
-
return _extended.concat(splitSegment(commandsToExtend[i], commandsToExtend[i + 1], segmentCount));
|
|
194
|
-
}, []);
|
|
195
|
-
// add in the very first point since splitSegment only adds in the ones after it
|
|
196
|
-
extended.unshift(commandsToExtend[0]);
|
|
197
|
-
return extended;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Takes a path `d` string and converts it into an array of command
|
|
201
|
-
* objects. Drops the `Z` character.
|
|
202
|
-
*
|
|
203
|
-
* @param {String|null} d A path `d` string
|
|
204
|
-
*/
|
|
205
|
-
function pathCommandsFromString(d) {
|
|
206
|
-
// split into valid tokens
|
|
207
|
-
const tokens = (d || '').match(commandTokenRegex) || [];
|
|
208
|
-
const commands = [];
|
|
209
|
-
let commandArgs;
|
|
210
|
-
let command;
|
|
211
|
-
// iterate over each token, checking if we are at a new command
|
|
212
|
-
// by presence in the typeMap
|
|
213
|
-
for (let i = 0; i < tokens.length; ++i) {
|
|
214
|
-
commandArgs = split_curve_1.typeMap[tokens[i]];
|
|
215
|
-
// new command found:
|
|
216
|
-
if (commandArgs) {
|
|
217
|
-
command = {
|
|
218
|
-
type: tokens[i],
|
|
219
|
-
};
|
|
220
|
-
// add each of the expected args for this command:
|
|
221
|
-
for (let a = 0; a < commandArgs.length; ++a) {
|
|
222
|
-
// @ts-expect-error
|
|
223
|
-
command[commandArgs[a]] = Number(tokens[i + a + 1]);
|
|
224
|
-
}
|
|
225
|
-
// need to increment our token index appropriately since
|
|
226
|
-
// we consumed token args
|
|
227
|
-
i += commandArgs.length;
|
|
228
|
-
commands.push(command);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
return commands;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Interpolate from A to B by extending A and B during interpolation to have
|
|
235
|
-
* the same number of points. This allows for a smooth transition when they
|
|
236
|
-
* have a different number of points.
|
|
237
|
-
*
|
|
238
|
-
* Ignores the `Z` command in paths unless both A and B end with it.
|
|
239
|
-
*
|
|
240
|
-
* This function works directly with arrays of command objects instead of with
|
|
241
|
-
* path `d` strings (see interpolatePath for working with `d` strings).
|
|
242
|
-
*
|
|
243
|
-
* @param {Object[]} aCommandsInput Array of path commands
|
|
244
|
-
* @param {Object[]} bCommandsInput Array of path commands
|
|
245
|
-
* @returns {Function} Interpolation function that maps t ([0, 1]) to an array of path commands.
|
|
246
|
-
*/
|
|
247
|
-
function interpolatePathCommands(aCommandsInput, bCommandsInput) {
|
|
248
|
-
// make a copy so we don't mess with the input arrays
|
|
249
|
-
let aCommands = aCommandsInput === null || aCommandsInput === undefined
|
|
250
|
-
? []
|
|
251
|
-
: aCommandsInput.slice();
|
|
252
|
-
let bCommands = bCommandsInput === null || bCommandsInput === undefined
|
|
253
|
-
? []
|
|
254
|
-
: bCommandsInput.slice();
|
|
255
|
-
// both input sets are empty, so we don't interpolate
|
|
256
|
-
if (!aCommands.length && !bCommands.length) {
|
|
257
|
-
return function () {
|
|
258
|
-
return [];
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
// do we add Z during interpolation? yes if both have it. (we'd expect both to have it or not)
|
|
262
|
-
const addZ = (aCommands.length === 0 || aCommands[aCommands.length - 1].type === 'Z') &&
|
|
263
|
-
(bCommands.length === 0 || bCommands[bCommands.length - 1].type === 'Z');
|
|
264
|
-
// we temporarily remove Z
|
|
265
|
-
if (aCommands.length > 0 && aCommands[aCommands.length - 1].type === 'Z') {
|
|
266
|
-
aCommands.pop();
|
|
267
|
-
}
|
|
268
|
-
if (bCommands.length > 0 && bCommands[bCommands.length - 1].type === 'Z') {
|
|
269
|
-
bCommands.pop();
|
|
270
|
-
}
|
|
271
|
-
// if A is empty, treat it as if it used to contain just the first point
|
|
272
|
-
// of B. This makes it so the line extends out of from that first point.
|
|
273
|
-
if (!aCommands.length) {
|
|
274
|
-
aCommands.push(bCommands[0]);
|
|
275
|
-
// otherwise if B is empty, treat it as if it contains the first point
|
|
276
|
-
// of A. This makes it so the line retracts into the first point.
|
|
277
|
-
}
|
|
278
|
-
else if (!bCommands.length) {
|
|
279
|
-
bCommands.push(aCommands[0]);
|
|
280
|
-
}
|
|
281
|
-
// extend to match equal size
|
|
282
|
-
const numPointsToExtend = Math.abs(bCommands.length - aCommands.length);
|
|
283
|
-
if (numPointsToExtend !== 0) {
|
|
284
|
-
// B has more points than A, so add points to A before interpolating
|
|
285
|
-
if (bCommands.length > aCommands.length) {
|
|
286
|
-
aCommands = extend(aCommands, bCommands);
|
|
287
|
-
// else if A has more points than B, add more points to B
|
|
288
|
-
}
|
|
289
|
-
else if (bCommands.length < aCommands.length) {
|
|
290
|
-
bCommands = extend(bCommands, aCommands);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
// commands have same length now.
|
|
294
|
-
// convert commands in A to the same type as those in B
|
|
295
|
-
aCommands = aCommands.map((aCommand, i) => convertToSameType(aCommand, bCommands[i]));
|
|
296
|
-
// create mutable interpolated command objects
|
|
297
|
-
const interpolatedCommands = aCommands.map((aCommand) => ({ ...aCommand }));
|
|
298
|
-
if (addZ) {
|
|
299
|
-
interpolatedCommands.push({ type: 'Z' });
|
|
300
|
-
aCommands.push({ type: 'Z' }); // required for when returning at t == 0
|
|
301
|
-
}
|
|
302
|
-
return function (t) {
|
|
303
|
-
// at 1 return the final value without the extensions used during interpolation
|
|
304
|
-
if (t === 1) {
|
|
305
|
-
return bCommandsInput === null || bCommandsInput === undefined
|
|
306
|
-
? []
|
|
307
|
-
: bCommandsInput;
|
|
308
|
-
}
|
|
309
|
-
// work with aCommands directly since interpolatedCommands are mutated
|
|
310
|
-
if (t === 0) {
|
|
311
|
-
return aCommands;
|
|
312
|
-
}
|
|
313
|
-
// interpolate the commands using the mutable interpolated command objs
|
|
314
|
-
for (let i = 0; i < interpolatedCommands.length; ++i) {
|
|
315
|
-
// if (interpolatedCommands[i].type === 'Z') continue;
|
|
316
|
-
const aCommand = aCommands[i];
|
|
317
|
-
const bCommand = bCommands[i];
|
|
318
|
-
const interpolatedCommand = interpolatedCommands[i];
|
|
319
|
-
for (const arg of split_curve_1.typeMap[interpolatedCommand.type]) {
|
|
320
|
-
// @ts-expect-error
|
|
321
|
-
interpolatedCommand[arg] =
|
|
322
|
-
(1 - t) * aCommand[arg] +
|
|
323
|
-
t * bCommand[arg];
|
|
324
|
-
// do not use floats for flags (#27), round to integer
|
|
325
|
-
if (arg === 'largeArcFlag' || arg === 'sweepFlag') {
|
|
326
|
-
// @ts-expect-error
|
|
327
|
-
interpolatedCommand[arg] = Math.round(interpolatedCommand[arg]);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
return interpolatedCommands;
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* @description Interpolates between two SVG paths.
|
|
336
|
-
* @param {number} value A number - 0 means first path, 1 means second path, any other values will be interpolated
|
|
337
|
-
* @param {string} firstPath The first valid SVG path
|
|
338
|
-
* @param {string} secondPath The second valid SVG path
|
|
339
|
-
* @see [Documentation](https://remotion.dev/docs/paths/interpolate-path)
|
|
340
|
-
*/
|
|
341
|
-
const interpolatePath = (value, firstPath, secondPath) => {
|
|
342
|
-
// at 1 return the final value without the extensions used during interpolation
|
|
343
|
-
if (value === 1) {
|
|
344
|
-
return secondPath;
|
|
345
|
-
}
|
|
346
|
-
if (value === 0) {
|
|
347
|
-
return firstPath;
|
|
348
|
-
}
|
|
349
|
-
const aCommands = pathCommandsFromString(firstPath);
|
|
350
|
-
if (aCommands.length === 0) {
|
|
351
|
-
throw new TypeError(`SVG Path "${firstPath}" is not valid`);
|
|
352
|
-
}
|
|
353
|
-
const bCommands = pathCommandsFromString(secondPath);
|
|
354
|
-
if (bCommands.length === 0) {
|
|
355
|
-
throw new TypeError(`SVG Path "${secondPath}" is not valid`);
|
|
356
|
-
}
|
|
357
|
-
const commandInterpolator = interpolatePathCommands(aCommands, bCommands);
|
|
358
|
-
const interpolatedCommands = commandInterpolator(value);
|
|
359
|
-
// convert to a string (fastest concat: https://jsperf.com/join-concat/150)
|
|
360
|
-
return interpolatedCommands
|
|
361
|
-
.map((c) => {
|
|
362
|
-
return commandToString(c);
|
|
363
|
-
})
|
|
364
|
-
.join(' ');
|
|
365
|
-
};
|
|
366
|
-
exports.interpolatePath = interpolatePath;
|