@remotion/paths 4.0.162 → 4.0.164

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.
@@ -0,0 +1 @@
1
+ export declare function arrayOfLength<T>(length: number, value: T): T[];
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.arrayOfLength = void 0;
4
+ function arrayOfLength(length, value) {
5
+ const array = Array(length);
6
+ for (let i = 0; i < length; i++) {
7
+ array[i] = value;
8
+ }
9
+ return array;
10
+ }
11
+ exports.arrayOfLength = arrayOfLength;
@@ -0,0 +1,7 @@
1
+ import type { Command } from './command';
2
+ /**
3
+ * Converts a command object to a string to be used in a `d` attribute
4
+ * @param {Object} command A command object
5
+ * @return {String} The string for the `d` attribute
6
+ */
7
+ export declare function commandToString(command: Command): string;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.commandToString = void 0;
4
+ const command_1 = require("./command");
5
+ /**
6
+ * Converts a command object to a string to be used in a `d` attribute
7
+ * @param {Object} command A command object
8
+ * @return {String} The string for the `d` attribute
9
+ */
10
+ function commandToString(command) {
11
+ return `${command.type} ${command_1.typeMap[command.type]
12
+ .map((p) => command[p])
13
+ .join(' ')}`;
14
+ }
15
+ exports.commandToString = commandToString;
@@ -0,0 +1,37 @@
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
+ };
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.typeMap = void 0;
4
+ /**
5
+ * List of params for each command type in a path `d` attribute
6
+ */
7
+ exports.typeMap = {
8
+ M: ['x', 'y'],
9
+ L: ['x', 'y'],
10
+ H: ['x'],
11
+ V: ['y'],
12
+ C: ['x1', 'y1', 'x2', 'y2', 'x', 'y'],
13
+ S: ['x2', 'y2', 'x', 'y'],
14
+ Q: ['x1', 'y1', 'x', 'y'],
15
+ T: ['x', 'y'],
16
+ A: ['rx', 'ry', 'xAxisRotation', 'largeArcFlag', 'sweepFlag', 'x', 'y'],
17
+ Z: [],
18
+ m: ['x', 'y'],
19
+ l: ['x', 'y'],
20
+ h: ['x'],
21
+ v: ['y'],
22
+ c: ['x1', 'y1', 'x2', 'y2', 'x', 'y'],
23
+ s: ['x2', 'y2', 'x', 'y'],
24
+ q: ['x1', 'y1', 'x', 'y'],
25
+ t: ['x', 'y'],
26
+ a: ['rx', 'ry', 'xAxisRotation', 'largeArcFlag', 'sweepFlag', 'x', 'y'],
27
+ z: [],
28
+ };
@@ -0,0 +1,22 @@
1
+ import type { Command } from './command';
2
+ /**
3
+ * Converts command A to have the same type as command B.
4
+ *
5
+ * e.g., L0,5 -> C0,5,0,5,0,5
6
+ *
7
+ * Uses these rules:
8
+ * x1 <- x
9
+ * x2 <- x
10
+ * y1 <- y
11
+ * y2 <- y
12
+ * rx <- 0
13
+ * ry <- 0
14
+ * xAxisRotation <- read from B
15
+ * largeArcFlag <- read from B
16
+ * sweepflag <- read from B
17
+ *
18
+ * @param {Object} aCommand Command object from path `d` attribute
19
+ * @param {Object} bCommand Command object from path `d` attribute to match against
20
+ * @return {Object} aCommand converted to type of bCommand
21
+ */
22
+ export declare function convertToSameType(aCommand: Command, bCommand: Command): Command;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertToSameType = void 0;
4
+ /**
5
+ * Converts command A to have the same type as command B.
6
+ *
7
+ * e.g., L0,5 -> C0,5,0,5,0,5
8
+ *
9
+ * Uses these rules:
10
+ * x1 <- x
11
+ * x2 <- x
12
+ * y1 <- y
13
+ * y2 <- y
14
+ * rx <- 0
15
+ * ry <- 0
16
+ * xAxisRotation <- read from B
17
+ * largeArcFlag <- read from B
18
+ * sweepflag <- read from B
19
+ *
20
+ * @param {Object} aCommand Command object from path `d` attribute
21
+ * @param {Object} bCommand Command object from path `d` attribute to match against
22
+ * @return {Object} aCommand converted to type of bCommand
23
+ */
24
+ function convertToSameType(aCommand, bCommand) {
25
+ const conversionMap = {
26
+ x1: 'x',
27
+ y1: 'y',
28
+ x2: 'x',
29
+ y2: 'y',
30
+ };
31
+ const readFromBKeys = ['xAxisRotation', 'largeArcFlag', 'sweepFlag'];
32
+ // convert (but ignore M types)
33
+ if (aCommand.type !== bCommand.type && bCommand.type.toUpperCase() !== 'M') {
34
+ const aConverted = {
35
+ type: bCommand.type,
36
+ };
37
+ Object.keys(bCommand).forEach((bKey) => {
38
+ const bValue = bCommand[bKey];
39
+ // first read from the A command
40
+ let aValue = aCommand[bKey];
41
+ // if it is one of these values, read from B no matter what
42
+ if (aValue === undefined) {
43
+ if (readFromBKeys.includes(bKey)) {
44
+ aValue = bValue;
45
+ }
46
+ else {
47
+ // if it wasn't in the A command, see if an equivalent was
48
+ if (aValue === undefined &&
49
+ conversionMap[bKey]) {
50
+ aValue =
51
+ aCommand[conversionMap[bKey]];
52
+ }
53
+ // if it doesn't have a converted value, use 0
54
+ if (aValue === undefined) {
55
+ aValue = 0;
56
+ }
57
+ }
58
+ }
59
+ // @ts-expect-error
60
+ aConverted[bKey] = aValue;
61
+ });
62
+ // update the type to match B
63
+ aConverted.type = bCommand.type;
64
+ aCommand = aConverted;
65
+ }
66
+ return aCommand;
67
+ }
68
+ exports.convertToSameType = convertToSameType;
@@ -0,0 +1,11 @@
1
+ import type { Command } from './command';
2
+ /**
3
+ * Extends an array of commandsToExtend to the length of the referenceCommands by
4
+ * splitting segments until the number of commands match. Ensures all the actual
5
+ * points of commandsToExtend are in the extended array.
6
+ *
7
+ * @param {Object[]} commandsToExtend The command object array to extend
8
+ * @param {Object[]} referenceCommands The command object array to match in length
9
+ * @return {Object[]} The extended commandsToExtend array
10
+ */
11
+ export declare function extend(commandsToExtend: Command[], referenceCommands: Command[]): Command[];
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extend = void 0;
4
+ const array_of_length_1 = require("./array-of-length");
5
+ const split_segment_1 = require("./split-segment");
6
+ /**
7
+ * Extends an array of commandsToExtend to the length of the referenceCommands by
8
+ * splitting segments until the number of commands match. Ensures all the actual
9
+ * points of commandsToExtend are in the extended array.
10
+ *
11
+ * @param {Object[]} commandsToExtend The command object array to extend
12
+ * @param {Object[]} referenceCommands The command object array to match in length
13
+ * @return {Object[]} The extended commandsToExtend array
14
+ */
15
+ function extend(commandsToExtend, referenceCommands) {
16
+ // compute insertion points:
17
+ // number of segments in the path to extend
18
+ const numSegmentsToExtend = commandsToExtend.length - 1;
19
+ // number of segments in the reference path.
20
+ const numReferenceSegments = referenceCommands.length - 1;
21
+ // this value is always between [0, 1].
22
+ const segmentRatio = numSegmentsToExtend / numReferenceSegments;
23
+ // create a map, mapping segments in referenceCommands to how many points
24
+ // should be added in that segment (should always be >= 1 since we need each
25
+ // point itself).
26
+ // 0 = segment 0-1, 1 = segment 1-2, n-1 = last vertex
27
+ const countPointsPerSegment = (0, array_of_length_1.arrayOfLength)(numReferenceSegments, undefined).reduce((accum, _d, i) => {
28
+ const insertIndex = Math.floor(segmentRatio * i);
29
+ accum[insertIndex] = (accum[insertIndex] || 0) + 1;
30
+ return accum;
31
+ }, []);
32
+ // extend each segment to have the correct number of points for a smooth interpolation
33
+ const extended = countPointsPerSegment.reduce((_extended, segmentCount, i) => {
34
+ // if last command, just add `segmentCount` number of times
35
+ if (i === commandsToExtend.length - 1) {
36
+ const lastCommandCopies = (0, array_of_length_1.arrayOfLength)(segmentCount, {
37
+ ...commandsToExtend[commandsToExtend.length - 1],
38
+ });
39
+ // convert M to L
40
+ if (lastCommandCopies[0].type === 'M') {
41
+ lastCommandCopies.forEach((d) => {
42
+ d.type = 'L';
43
+ });
44
+ }
45
+ return _extended.concat(lastCommandCopies);
46
+ }
47
+ // otherwise, split the segment segmentCount times.
48
+ return _extended.concat((0, split_segment_1.splitSegment)(commandsToExtend[i], commandsToExtend[i + 1], segmentCount));
49
+ }, []);
50
+ // add in the very first point since splitSegment only adds in the ones after it
51
+ extended.unshift(commandsToExtend[0]);
52
+ return extended;
53
+ }
54
+ exports.extend = extend;
@@ -0,0 +1,8 @@
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;
@@ -0,0 +1,174 @@
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 command_1 = require("./command");
37
+ const command_to_string_1 = require("./command-to-string");
38
+ const convert_to_same_type_1 = require("./convert-to-same-type");
39
+ const extend_command_1 = require("./extend-command");
40
+ const path_commands_from_string_1 = require("./path-commands-from-string");
41
+ /**
42
+ * Interpolate from A to B by extending A and B during interpolation to have
43
+ * the same number of points. This allows for a smooth transition when they
44
+ * have a different number of points.
45
+ *
46
+ * Ignores the `Z` command in paths unless both A and B end with it.
47
+ *
48
+ * This function works directly with arrays of command objects instead of with
49
+ * path `d` strings (see interpolatePath for working with `d` strings).
50
+ *
51
+ * @param {Object[]} aCommandsInput Array of path commands
52
+ * @param {Object[]} bCommandsInput Array of path commands
53
+ * @returns {Function} Interpolation function that maps t ([0, 1]) to an array of path commands.
54
+ */
55
+ function interpolatePathCommands(aCommandsInput, bCommandsInput) {
56
+ // make a copy so we don't mess with the input arrays
57
+ let aCommands = aCommandsInput === null || aCommandsInput === undefined
58
+ ? []
59
+ : aCommandsInput.slice();
60
+ let bCommands = bCommandsInput === null || bCommandsInput === undefined
61
+ ? []
62
+ : bCommandsInput.slice();
63
+ // both input sets are empty, so we don't interpolate
64
+ if (!aCommands.length && !bCommands.length) {
65
+ return function () {
66
+ return [];
67
+ };
68
+ }
69
+ // do we add Z during interpolation? yes if both have it. (we'd expect both to have it or not)
70
+ const addZ = (aCommands.length === 0 || aCommands[aCommands.length - 1].type === 'Z') &&
71
+ (bCommands.length === 0 || bCommands[bCommands.length - 1].type === 'Z');
72
+ // we temporarily remove Z
73
+ if (aCommands.length > 0 && aCommands[aCommands.length - 1].type === 'Z') {
74
+ aCommands.pop();
75
+ }
76
+ if (bCommands.length > 0 && bCommands[bCommands.length - 1].type === 'Z') {
77
+ bCommands.pop();
78
+ }
79
+ // if A is empty, treat it as if it used to contain just the first point
80
+ // of B. This makes it so the line extends out of from that first point.
81
+ if (!aCommands.length) {
82
+ aCommands.push(bCommands[0]);
83
+ // otherwise if B is empty, treat it as if it contains the first point
84
+ // of A. This makes it so the line retracts into the first point.
85
+ }
86
+ else if (!bCommands.length) {
87
+ bCommands.push(aCommands[0]);
88
+ }
89
+ // extend to match equal size
90
+ const numPointsToExtend = Math.abs(bCommands.length - aCommands.length);
91
+ if (numPointsToExtend !== 0) {
92
+ // B has more points than A, so add points to A before interpolating
93
+ if (bCommands.length > aCommands.length) {
94
+ aCommands = (0, extend_command_1.extend)(aCommands, bCommands);
95
+ // else if A has more points than B, add more points to B
96
+ }
97
+ else if (bCommands.length < aCommands.length) {
98
+ bCommands = (0, extend_command_1.extend)(bCommands, aCommands);
99
+ }
100
+ }
101
+ // commands have same length now.
102
+ // convert commands in A to the same type as those in B
103
+ aCommands = aCommands.map((aCommand, i) => (0, convert_to_same_type_1.convertToSameType)(aCommand, bCommands[i]));
104
+ // create mutable interpolated command objects
105
+ const interpolatedCommands = aCommands.map((aCommand) => ({ ...aCommand }));
106
+ if (addZ) {
107
+ interpolatedCommands.push({ type: 'Z' });
108
+ aCommands.push({ type: 'Z' }); // required for when returning at t == 0
109
+ }
110
+ return function (t) {
111
+ // at 1 return the final value without the extensions used during interpolation
112
+ if (t === 1) {
113
+ return bCommandsInput === null || bCommandsInput === undefined
114
+ ? []
115
+ : bCommandsInput;
116
+ }
117
+ // work with aCommands directly since interpolatedCommands are mutated
118
+ if (t === 0) {
119
+ return aCommands;
120
+ }
121
+ // interpolate the commands using the mutable interpolated command objs
122
+ for (let i = 0; i < interpolatedCommands.length; ++i) {
123
+ // if (interpolatedCommands[i].type === 'Z') continue;
124
+ const aCommand = aCommands[i];
125
+ const bCommand = bCommands[i];
126
+ const interpolatedCommand = interpolatedCommands[i];
127
+ for (const arg of command_1.typeMap[interpolatedCommand.type]) {
128
+ // @ts-expect-error
129
+ interpolatedCommand[arg] =
130
+ (1 - t) * aCommand[arg] +
131
+ t * bCommand[arg];
132
+ // do not use floats for flags (#27), round to integer
133
+ if (arg === 'largeArcFlag' || arg === 'sweepFlag') {
134
+ // @ts-expect-error
135
+ interpolatedCommand[arg] = Math.round(interpolatedCommand[arg]);
136
+ }
137
+ }
138
+ }
139
+ return interpolatedCommands;
140
+ };
141
+ }
142
+ /**
143
+ * @description Interpolates between two SVG paths.
144
+ * @param {number} value A number - 0 means first path, 1 means second path, any other values will be interpolated
145
+ * @param {string} firstPath The first valid SVG path
146
+ * @param {string} secondPath The second valid SVG path
147
+ * @see [Documentation](https://remotion.dev/docs/paths/interpolate-path)
148
+ */
149
+ const interpolatePath = (value, firstPath, secondPath) => {
150
+ // at 1 return the final value without the extensions used during interpolation
151
+ if (value === 1) {
152
+ return secondPath;
153
+ }
154
+ if (value === 0) {
155
+ return firstPath;
156
+ }
157
+ const aCommands = (0, path_commands_from_string_1.pathCommandsFromString)(firstPath);
158
+ if (aCommands.length === 0) {
159
+ throw new TypeError(`SVG Path "${firstPath}" is not valid`);
160
+ }
161
+ const bCommands = (0, path_commands_from_string_1.pathCommandsFromString)(secondPath);
162
+ if (bCommands.length === 0) {
163
+ throw new TypeError(`SVG Path "${secondPath}" is not valid`);
164
+ }
165
+ const commandInterpolator = interpolatePathCommands(aCommands, bCommands);
166
+ const interpolatedCommands = commandInterpolator(value);
167
+ // convert to a string (fastest concat: https://jsperf.com/join-concat/150)
168
+ return interpolatedCommands
169
+ .map((c) => {
170
+ return (0, command_to_string_1.commandToString)(c);
171
+ })
172
+ .join(' ');
173
+ };
174
+ exports.interpolatePath = interpolatePath;
@@ -0,0 +1,8 @@
1
+ import { type Command } from './command';
2
+ /**
3
+ * Takes a path `d` string and converts it into an array of command
4
+ * objects. Drops the `Z` character.
5
+ *
6
+ * @param {String|null} d A path `d` string
7
+ */
8
+ export declare function pathCommandsFromString(d: string | null): Command[];
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pathCommandsFromString = void 0;
4
+ const command_1 = require("./command");
5
+ const commandTokenRegex = /[MLCSTQAHVZmlcstqahv]|-?[\d.e+-]+/g;
6
+ /**
7
+ * Takes a path `d` string and converts it into an array of command
8
+ * objects. Drops the `Z` character.
9
+ *
10
+ * @param {String|null} d A path `d` string
11
+ */
12
+ function pathCommandsFromString(d) {
13
+ // split into valid tokens
14
+ const tokens = (d || '').match(commandTokenRegex) || [];
15
+ const commands = [];
16
+ let commandArgs;
17
+ let command;
18
+ // iterate over each token, checking if we are at a new command
19
+ // by presence in the typeMap
20
+ for (let i = 0; i < tokens.length; ++i) {
21
+ commandArgs = command_1.typeMap[tokens[i]];
22
+ // new command found:
23
+ if (commandArgs) {
24
+ command = {
25
+ type: tokens[i],
26
+ };
27
+ // add each of the expected args for this command:
28
+ for (let a = 0; a < commandArgs.length; ++a) {
29
+ // @ts-expect-error
30
+ command[commandArgs[a]] = Number(tokens[i + a + 1]);
31
+ }
32
+ // need to increment our token index appropriately since
33
+ // we consumed token args
34
+ i += commandArgs.length;
35
+ commands.push(command);
36
+ }
37
+ }
38
+ return commands;
39
+ }
40
+ exports.pathCommandsFromString = pathCommandsFromString;
@@ -0,0 +1,11 @@
1
+ import type { Command } from './command';
2
+ /**
3
+ * Convert command objects to arrays of points, run de Casteljau's algorithm on it
4
+ * to split into to the desired number of segments.
5
+ *
6
+ * @param {Object} commandStart The start command object
7
+ * @param {Object} commandEnd The end command object
8
+ * @param {Number} segmentCount The number of segments to create
9
+ * @return {Object[]} An array of commands representing the segments in sequence
10
+ */
11
+ export declare const splitCurve: (commandStartX: number, commandStartY: number, commandEnd: Command, segmentCount: number) => Command[];
@@ -0,0 +1,165 @@
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 = 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
+ * Convert segments represented as points back into a command object
78
+ *
79
+ * @param {Number[][]} points Array of [x,y] points: [start, control1, control2, ..., end]
80
+ * Represents a segment
81
+ * @return {Object} A command object representing the segment.
82
+ */
83
+ function pointsToCommand(points) {
84
+ let x2;
85
+ let y2;
86
+ let x1;
87
+ let y1;
88
+ if (points.length === 4) {
89
+ x2 = points[2][0];
90
+ y2 = points[2][1];
91
+ }
92
+ if (points.length >= 3) {
93
+ x1 = points[1][0];
94
+ y1 = points[1][1];
95
+ }
96
+ const x = points[points.length - 1][0];
97
+ const y = points[points.length - 1][1];
98
+ let type = 'L';
99
+ if (points.length === 4) {
100
+ // start, control1, control2, end
101
+ type = 'C';
102
+ }
103
+ else if (points.length === 3) {
104
+ // start, control, end
105
+ type = 'Q';
106
+ }
107
+ return { x2, y2, x1, y1, x, y, type };
108
+ }
109
+ /**
110
+ * Runs de Casteljau's algorithm enough times to produce the desired number of segments.
111
+ *
112
+ * @param {Number[][]} points Array of [x,y] points for de Casteljau (the initial segment to split)
113
+ * @param {Number} segmentCount Number of segments to split the original into
114
+ * @return {Number[][][]} Array of segments
115
+ */
116
+ function splitCurveAsPoints(points, segmentCount) {
117
+ segmentCount = segmentCount || 2;
118
+ const segments = [];
119
+ let remainingCurve = points;
120
+ const tIncrement = 1 / segmentCount;
121
+ // x-----x-----x-----x
122
+ // t= 0.33 0.66 1
123
+ // x-----o-----------x
124
+ // r= 0.33
125
+ // x-----o-----x
126
+ // r= 0.5 (0.33 / (1 - 0.33)) === tIncrement / (1 - (tIncrement * (i - 1))
127
+ // x-----x-----x-----x----x
128
+ // t= 0.25 0.5 0.75 1
129
+ // x-----o----------------x
130
+ // r= 0.25
131
+ // x-----o----------x
132
+ // r= 0.33 (0.25 / (1 - 0.25))
133
+ // x-----o----x
134
+ // r= 0.5 (0.25 / (1 - 0.5))
135
+ for (let i = 0; i < segmentCount - 1; i++) {
136
+ const tRelative = tIncrement / (1 - tIncrement * i);
137
+ const split = decasteljau(remainingCurve, tRelative);
138
+ segments.push(split.left);
139
+ remainingCurve = split.right;
140
+ }
141
+ // last segment is just to the end from the last point
142
+ segments.push(remainingCurve);
143
+ return segments;
144
+ }
145
+ /**
146
+ * Convert command objects to arrays of points, run de Casteljau's algorithm on it
147
+ * to split into to the desired number of segments.
148
+ *
149
+ * @param {Object} commandStart The start command object
150
+ * @param {Object} commandEnd The end command object
151
+ * @param {Number} segmentCount The number of segments to create
152
+ * @return {Object[]} An array of commands representing the segments in sequence
153
+ */
154
+ const splitCurve = (commandStartX, commandStartY, commandEnd, segmentCount) => {
155
+ const points = [[commandStartX, commandStartY]];
156
+ if (commandEnd.x1 !== null && commandEnd.x1 !== undefined) {
157
+ points.push([commandEnd.x1, commandEnd.y1]);
158
+ }
159
+ if (commandEnd.x2 !== null && commandEnd.x2 !== undefined) {
160
+ points.push([commandEnd.x2, commandEnd.y2]);
161
+ }
162
+ points.push([commandEnd.x, commandEnd.y]);
163
+ return splitCurveAsPoints(points, segmentCount).map(pointsToCommand);
164
+ };
165
+ exports.splitCurve = splitCurve;
@@ -0,0 +1,14 @@
1
+ import type { Command } from './command';
2
+ /**
3
+ * Interpolate between command objects commandStart and commandEnd segmentCount times.
4
+ * If the types are L, Q, or C then the curves are split as per de Casteljau's algorithm.
5
+ * Otherwise we just copy commandStart segmentCount - 1 times, finally ending with commandEnd.
6
+ *
7
+ * @param {Object} commandStart Command object at the beginning of the segment
8
+ * @param {Object} commandEnd Command object at the end of the segment
9
+ * @param {Number} segmentCount The number of segments to split this into. If only 1
10
+ * Then [commandEnd] is returned.
11
+ * @return {Object[]} Array of ~segmentCount command objects between commandStart and
12
+ * commandEnd. (Can be segmentCount+1 objects if commandStart is type M).
13
+ */
14
+ export declare function splitSegment(commandStart: Command, commandEnd: Command, segmentCount: number): Command[];
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.splitSegment = void 0;
4
+ const array_of_length_1 = require("./array-of-length");
5
+ const split_curve_1 = require("./split-curve");
6
+ /**
7
+ * Interpolate between command objects commandStart and commandEnd segmentCount times.
8
+ * If the types are L, Q, or C then the curves are split as per de Casteljau's algorithm.
9
+ * Otherwise we just copy commandStart segmentCount - 1 times, finally ending with commandEnd.
10
+ *
11
+ * @param {Object} commandStart Command object at the beginning of the segment
12
+ * @param {Object} commandEnd Command object at the end of the segment
13
+ * @param {Number} segmentCount The number of segments to split this into. If only 1
14
+ * Then [commandEnd] is returned.
15
+ * @return {Object[]} Array of ~segmentCount command objects between commandStart and
16
+ * commandEnd. (Can be segmentCount+1 objects if commandStart is type M).
17
+ */
18
+ // TODO: Delete and replace
19
+ function splitSegment(commandStart, commandEnd, segmentCount) {
20
+ let segments = [];
21
+ // line, quadratic bezier, or cubic bezier
22
+ if (commandEnd.type === 'L' ||
23
+ commandEnd.type === 'Q' ||
24
+ commandEnd.type === 'C') {
25
+ if (commandStart.type !== 'Z') {
26
+ segments = segments.concat((0, split_curve_1.splitCurve)(commandStart.x, commandStart.y, commandEnd, segmentCount));
27
+ }
28
+ // general case - just copy the same point
29
+ }
30
+ else {
31
+ const copyCommand = { ...commandStart };
32
+ // convert M to L
33
+ if (copyCommand.type === 'M') {
34
+ copyCommand.type = 'L';
35
+ }
36
+ segments = segments.concat((0, array_of_length_1.arrayOfLength)(segmentCount - 1, undefined).map(() => copyCommand));
37
+ segments.push(commandEnd);
38
+ }
39
+ return segments;
40
+ }
41
+ exports.splitSegment = splitSegment;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/paths",
3
- "version": "4.0.162",
3
+ "version": "4.0.164",
4
4
  "description": "Utility functions for SVG paths",
5
5
  "main": "dist/index.js",
6
6
  "sideEffects": false,