@remotion/paths 4.0.164 → 4.0.166
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/debug-path.d.ts +6 -0
- package/dist/debug-path.js +57 -0
- package/dist/get-end-position.d.ts +2 -0
- package/dist/get-end-position.js +70 -0
- package/dist/helpers/types.d.ts +13 -12
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -1
- package/dist/interpolate-path/convert-to-same-instruction-type.d.ts +22 -0
- package/dist/interpolate-path/convert-to-same-instruction-type.js +109 -0
- package/dist/interpolate-path/de-casteljau.d.ts +14 -0
- package/dist/interpolate-path/de-casteljau.js +44 -0
- package/dist/interpolate-path/extend-command.d.ts +2 -2
- package/dist/interpolate-path/extend-command.js +8 -7
- package/dist/interpolate-path/interpolate-commands.d.ts +16 -0
- package/dist/interpolate-path/interpolate-commands.js +108 -0
- package/dist/interpolate-path/interpolate-instruction-of-same-kind.d.ts +2 -0
- package/dist/interpolate-path/interpolate-instruction-of-same-kind.js +73 -0
- package/dist/interpolate-path/interpolate-instructions.d.ts +16 -0
- package/dist/interpolate-path/interpolate-instructions.js +92 -0
- package/dist/interpolate-path/interpolate-path.js +8 -116
- package/dist/interpolate-path/points-to-command.d.ts +9 -0
- package/dist/interpolate-path/points-to-command.js +46 -0
- package/dist/interpolate-path/split-curve-as-points.d.ts +8 -0
- package/dist/interpolate-path/split-curve-as-points.js +40 -0
- package/dist/interpolate-path/split-curve.d.ts +3 -3
- package/dist/interpolate-path/split-curve.js +18 -122
- package/dist/interpolate-path/split-segment.d.ts +2 -2
- package/dist/interpolate-path/split-segment.js +12 -12
- package/dist/warp-path/warp-helpers.d.ts +0 -3
- package/dist/warp-path/warp-helpers.js +1 -4
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.interpolateInstructionOfSameKind = void 0;
|
|
4
|
+
const interpolateLInstruction = (t, first, second) => {
|
|
5
|
+
return {
|
|
6
|
+
type: 'L',
|
|
7
|
+
x: (1 - t) * first.x + t * second.x,
|
|
8
|
+
y: (1 - t) * first.y + t * second.y,
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
const interpolateCInstructions = (t, first, second) => {
|
|
12
|
+
return {
|
|
13
|
+
type: 'C',
|
|
14
|
+
cp1x: (1 - t) * first.cp1x + t * second.cp1x,
|
|
15
|
+
cp2x: (1 - t) * first.cp2x + t * second.cp2x,
|
|
16
|
+
cp1y: (1 - t) * first.cp1y + t * second.cp1y,
|
|
17
|
+
cp2y: (1 - t) * first.cp2y + t * second.cp2y,
|
|
18
|
+
x: (1 - t) * first.x + t * second.x,
|
|
19
|
+
y: (1 - t) * first.y + t * second.y,
|
|
20
|
+
};
|
|
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
|
+
const interpolateMInstructions = (t, first, second) => {
|
|
32
|
+
return {
|
|
33
|
+
type: 'M',
|
|
34
|
+
x: (1 - t) * first.x + t * second.x,
|
|
35
|
+
y: (1 - t) * first.y + t * second.y,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
const interpolateInstructionOfSameKind = (t, first, second) => {
|
|
39
|
+
if (first.type === 'L') {
|
|
40
|
+
if (second.type !== 'L') {
|
|
41
|
+
throw new Error('mismatch');
|
|
42
|
+
}
|
|
43
|
+
return interpolateLInstruction(t, first, second);
|
|
44
|
+
}
|
|
45
|
+
if (first.type === 'C') {
|
|
46
|
+
if (second.type !== 'C') {
|
|
47
|
+
throw new Error('mismatch');
|
|
48
|
+
}
|
|
49
|
+
return interpolateCInstructions(t, first, second);
|
|
50
|
+
}
|
|
51
|
+
if (first.type === 'Q') {
|
|
52
|
+
if (second.type !== 'Q') {
|
|
53
|
+
throw new Error('mismatch');
|
|
54
|
+
}
|
|
55
|
+
return interpolateQInstructions(t, first, second);
|
|
56
|
+
}
|
|
57
|
+
if (first.type === 'M') {
|
|
58
|
+
if (second.type !== 'M') {
|
|
59
|
+
throw new Error('mismatch');
|
|
60
|
+
}
|
|
61
|
+
return interpolateMInstructions(t, first, second);
|
|
62
|
+
}
|
|
63
|
+
if (first.type === 'Z') {
|
|
64
|
+
if (second.type !== 'Z') {
|
|
65
|
+
throw new Error('mismatch');
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
type: 'Z',
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
throw new Error('mismatch');
|
|
72
|
+
};
|
|
73
|
+
exports.interpolateInstructionOfSameKind = interpolateInstructionOfSameKind;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReducedInstruction } from '../helpers/types';
|
|
2
|
+
/**
|
|
3
|
+
* Interpolate from A to B by extending A and B during interpolation to have
|
|
4
|
+
* the same number of points. This allows for a smooth transition when they
|
|
5
|
+
* have a different number of points.
|
|
6
|
+
*
|
|
7
|
+
* Ignores the `Z` command in paths unless both A and B end with it.
|
|
8
|
+
*
|
|
9
|
+
* This function works directly with arrays of command objects instead of with
|
|
10
|
+
* path `d` strings (see interpolatePath for working with `d` strings).
|
|
11
|
+
*
|
|
12
|
+
* @param {Object[]} aCommandsInput Array of path commands
|
|
13
|
+
* @param {Object[]} bCommandsInput Array of path commands
|
|
14
|
+
* @returns {Function} Interpolation function that maps t ([0, 1]) to an array of path commands.
|
|
15
|
+
*/
|
|
16
|
+
export declare function interpolateInstructions(aCommandsInput: ReducedInstruction[], bCommandsInput: ReducedInstruction[]): (t: number) => ReducedInstruction[];
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.interpolateInstructions = void 0;
|
|
4
|
+
const get_end_position_1 = require("../get-end-position");
|
|
5
|
+
const convert_to_same_instruction_type_1 = require("./convert-to-same-instruction-type");
|
|
6
|
+
const extend_command_1 = require("./extend-command");
|
|
7
|
+
const interpolate_instruction_of_same_kind_1 = require("./interpolate-instruction-of-same-kind");
|
|
8
|
+
/**
|
|
9
|
+
* Interpolate from A to B by extending A and B during interpolation to have
|
|
10
|
+
* the same number of points. This allows for a smooth transition when they
|
|
11
|
+
* have a different number of points.
|
|
12
|
+
*
|
|
13
|
+
* Ignores the `Z` command in paths unless both A and B end with it.
|
|
14
|
+
*
|
|
15
|
+
* This function works directly with arrays of command objects instead of with
|
|
16
|
+
* path `d` strings (see interpolatePath for working with `d` strings).
|
|
17
|
+
*
|
|
18
|
+
* @param {Object[]} aCommandsInput Array of path commands
|
|
19
|
+
* @param {Object[]} bCommandsInput Array of path commands
|
|
20
|
+
* @returns {Function} Interpolation function that maps t ([0, 1]) to an array of path commands.
|
|
21
|
+
*/
|
|
22
|
+
function interpolateInstructions(aCommandsInput, bCommandsInput) {
|
|
23
|
+
// make a copy so we don't mess with the input arrays
|
|
24
|
+
let aCommands = aCommandsInput.slice();
|
|
25
|
+
let bCommands = bCommandsInput.slice();
|
|
26
|
+
// both input sets are empty, so we don't interpolate
|
|
27
|
+
if (!aCommands.length && !bCommands.length) {
|
|
28
|
+
return function () {
|
|
29
|
+
return [];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// do we add Z during interpolation? yes if both have it. (we'd expect both to have it or not)
|
|
33
|
+
const addZ = (aCommands.length === 0 || aCommands[aCommands.length - 1].type === 'Z') &&
|
|
34
|
+
(bCommands.length === 0 || bCommands[bCommands.length - 1].type === 'Z');
|
|
35
|
+
// we temporarily remove Z
|
|
36
|
+
if (aCommands.length > 0 && aCommands[aCommands.length - 1].type === 'Z') {
|
|
37
|
+
aCommands.pop();
|
|
38
|
+
}
|
|
39
|
+
if (bCommands.length > 0 && bCommands[bCommands.length - 1].type === 'Z') {
|
|
40
|
+
bCommands.pop();
|
|
41
|
+
}
|
|
42
|
+
// if A is empty, treat it as if it used to contain just the first point
|
|
43
|
+
// of B. This makes it so the line extends out of from that first point.
|
|
44
|
+
if (!aCommands.length) {
|
|
45
|
+
aCommands.push(bCommands[0]);
|
|
46
|
+
// otherwise if B is empty, treat it as if it contains the first point
|
|
47
|
+
// of A. This makes it so the line retracts into the first point.
|
|
48
|
+
}
|
|
49
|
+
else if (!bCommands.length) {
|
|
50
|
+
bCommands.push(aCommands[0]);
|
|
51
|
+
}
|
|
52
|
+
// extend to match equal size
|
|
53
|
+
const numPointsToExtend = Math.abs(bCommands.length - aCommands.length);
|
|
54
|
+
if (numPointsToExtend !== 0) {
|
|
55
|
+
// B has more points than A, so add points to A before interpolating
|
|
56
|
+
if (bCommands.length > aCommands.length) {
|
|
57
|
+
aCommands = (0, extend_command_1.extendInstruction)(aCommands, bCommands);
|
|
58
|
+
// else if A has more points than B, add more points to B
|
|
59
|
+
}
|
|
60
|
+
else if (bCommands.length < aCommands.length) {
|
|
61
|
+
bCommands = (0, extend_command_1.extendInstruction)(bCommands, aCommands);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// commands have same length now.
|
|
65
|
+
// convert commands in A to the same type as those in B
|
|
66
|
+
const aSameType = aCommands.map((aCommand, i) => {
|
|
67
|
+
const commandsUntilNow = aCommands.slice(0, i);
|
|
68
|
+
const point = (0, get_end_position_1.getEndPosition)(commandsUntilNow);
|
|
69
|
+
return (0, convert_to_same_instruction_type_1.convertToSameInstructionType)(aCommand, bCommands[i], point);
|
|
70
|
+
});
|
|
71
|
+
const interpolatedCommands = [];
|
|
72
|
+
if (addZ) {
|
|
73
|
+
aSameType.push({ type: 'Z' }); // required for when returning at t == 0
|
|
74
|
+
bCommands.push({ type: 'Z' });
|
|
75
|
+
}
|
|
76
|
+
return function (t) {
|
|
77
|
+
// at 1 return the final value without the extensions used during interpolation
|
|
78
|
+
if (t === 1) {
|
|
79
|
+
return bCommandsInput;
|
|
80
|
+
}
|
|
81
|
+
// work with aCommands directly since interpolatedCommands are mutated
|
|
82
|
+
if (t === 0) {
|
|
83
|
+
return aSameType;
|
|
84
|
+
}
|
|
85
|
+
// interpolate the commands using the mutable interpolated command objs
|
|
86
|
+
for (let i = 0; i < aSameType.length; ++i) {
|
|
87
|
+
interpolatedCommands.push((0, interpolate_instruction_of_same_kind_1.interpolateInstructionOfSameKind)(t, aSameType[i], bCommands[i]));
|
|
88
|
+
}
|
|
89
|
+
return interpolatedCommands;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
exports.interpolateInstructions = interpolateInstructions;
|
|
@@ -33,112 +33,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
33
33
|
*/
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
35
|
exports.interpolatePath = void 0;
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
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
|
-
}
|
|
36
|
+
const parse_path_1 = require("../parse-path");
|
|
37
|
+
const reduce_instructions_1 = require("../reduce-instructions");
|
|
38
|
+
const serialize_instructions_1 = require("../serialize-instructions");
|
|
39
|
+
const interpolate_instructions_1 = require("./interpolate-instructions");
|
|
142
40
|
/**
|
|
143
41
|
* @description Interpolates between two SVG paths.
|
|
144
42
|
* @param {number} value A number - 0 means first path, 1 means second path, any other values will be interpolated
|
|
@@ -154,21 +52,15 @@ const interpolatePath = (value, firstPath, secondPath) => {
|
|
|
154
52
|
if (value === 0) {
|
|
155
53
|
return firstPath;
|
|
156
54
|
}
|
|
157
|
-
const aCommands = (0,
|
|
55
|
+
const aCommands = (0, reduce_instructions_1.reduceInstructions)((0, parse_path_1.parsePath)(firstPath));
|
|
158
56
|
if (aCommands.length === 0) {
|
|
159
57
|
throw new TypeError(`SVG Path "${firstPath}" is not valid`);
|
|
160
58
|
}
|
|
161
|
-
const bCommands = (0,
|
|
59
|
+
const bCommands = (0, reduce_instructions_1.reduceInstructions)((0, parse_path_1.parsePath)(secondPath));
|
|
162
60
|
if (bCommands.length === 0) {
|
|
163
61
|
throw new TypeError(`SVG Path "${secondPath}" is not valid`);
|
|
164
62
|
}
|
|
165
|
-
const commandInterpolator =
|
|
166
|
-
|
|
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(' ');
|
|
63
|
+
const commandInterpolator = (0, interpolate_instructions_1.interpolateInstructions)(aCommands, bCommands);
|
|
64
|
+
return (0, serialize_instructions_1.serializeInstructions)(commandInterpolator(value));
|
|
173
65
|
};
|
|
174
66
|
exports.interpolatePath = interpolatePath;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReducedInstruction } from '../helpers/types';
|
|
2
|
+
/**
|
|
3
|
+
* Convert segments represented as points back into a command object
|
|
4
|
+
*
|
|
5
|
+
* @param {Number[][]} points Array of [x,y] points: [start, control1, control2, ..., end]
|
|
6
|
+
* Represents a segment
|
|
7
|
+
* @return {Object} A command object representing the segment.
|
|
8
|
+
*/
|
|
9
|
+
export declare function pointsToInstruction(points: number[][]): ReducedInstruction;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pointsToInstruction = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Convert segments represented as points back into a command object
|
|
6
|
+
*
|
|
7
|
+
* @param {Number[][]} points Array of [x,y] points: [start, control1, control2, ..., end]
|
|
8
|
+
* Represents a segment
|
|
9
|
+
* @return {Object} A command object representing the segment.
|
|
10
|
+
*/
|
|
11
|
+
function pointsToInstruction(points) {
|
|
12
|
+
const x = points[points.length - 1][0];
|
|
13
|
+
const y = points[points.length - 1][1];
|
|
14
|
+
if (points.length === 4) {
|
|
15
|
+
const x1 = points[1][0];
|
|
16
|
+
const y1 = points[1][1];
|
|
17
|
+
const x2 = points[2][0];
|
|
18
|
+
const y2 = points[2][1];
|
|
19
|
+
return {
|
|
20
|
+
type: 'C',
|
|
21
|
+
cp1x: x1,
|
|
22
|
+
cp1y: y1,
|
|
23
|
+
cp2x: x2,
|
|
24
|
+
cp2y: y2,
|
|
25
|
+
x,
|
|
26
|
+
y,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
if (points.length === 3) {
|
|
30
|
+
const x1 = points[1][0];
|
|
31
|
+
const y1 = points[1][1];
|
|
32
|
+
return {
|
|
33
|
+
type: 'Q',
|
|
34
|
+
cpx: x1,
|
|
35
|
+
cpy: y1,
|
|
36
|
+
x,
|
|
37
|
+
y,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
type: 'L',
|
|
42
|
+
x,
|
|
43
|
+
y,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
exports.pointsToInstruction = pointsToInstruction;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs de Casteljau's algorithm enough times to produce the desired number of segments.
|
|
3
|
+
*
|
|
4
|
+
* @param {Number[][]} points Array of [x,y] points for de Casteljau (the initial segment to split)
|
|
5
|
+
* @param {Number} segmentCount Number of segments to split the original into
|
|
6
|
+
* @return {Number[][][]} Array of segments
|
|
7
|
+
*/
|
|
8
|
+
export declare function splitCurveAsPoints(points: number[][], segmentCount?: number): number[][][];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.splitCurveAsPoints = void 0;
|
|
4
|
+
const de_casteljau_1 = require("./de-casteljau");
|
|
5
|
+
/**
|
|
6
|
+
* Runs de Casteljau's algorithm enough times to produce the desired number of segments.
|
|
7
|
+
*
|
|
8
|
+
* @param {Number[][]} points Array of [x,y] points for de Casteljau (the initial segment to split)
|
|
9
|
+
* @param {Number} segmentCount Number of segments to split the original into
|
|
10
|
+
* @return {Number[][][]} Array of segments
|
|
11
|
+
*/
|
|
12
|
+
function splitCurveAsPoints(points, segmentCount = 2) {
|
|
13
|
+
const segments = [];
|
|
14
|
+
let remainingCurve = points;
|
|
15
|
+
const tIncrement = 1 / segmentCount;
|
|
16
|
+
// x-----x-----x-----x
|
|
17
|
+
// t= 0.33 0.66 1
|
|
18
|
+
// x-----o-----------x
|
|
19
|
+
// r= 0.33
|
|
20
|
+
// x-----o-----x
|
|
21
|
+
// r= 0.5 (0.33 / (1 - 0.33)) === tIncrement / (1 - (tIncrement * (i - 1))
|
|
22
|
+
// x-----x-----x-----x----x
|
|
23
|
+
// t= 0.25 0.5 0.75 1
|
|
24
|
+
// x-----o----------------x
|
|
25
|
+
// r= 0.25
|
|
26
|
+
// x-----o----------x
|
|
27
|
+
// r= 0.33 (0.25 / (1 - 0.25))
|
|
28
|
+
// x-----o----x
|
|
29
|
+
// r= 0.5 (0.25 / (1 - 0.5))
|
|
30
|
+
for (let i = 0; i < segmentCount - 1; i++) {
|
|
31
|
+
const tRelative = tIncrement / (1 - tIncrement * i);
|
|
32
|
+
const split = (0, de_casteljau_1.decasteljau)(remainingCurve, tRelative);
|
|
33
|
+
segments.push(split.left);
|
|
34
|
+
remainingCurve = split.right;
|
|
35
|
+
}
|
|
36
|
+
// last segment is just to the end from the last point
|
|
37
|
+
segments.push(remainingCurve);
|
|
38
|
+
return segments;
|
|
39
|
+
}
|
|
40
|
+
exports.splitCurveAsPoints = splitCurveAsPoints;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CInstruction, LInstruction, QInstruction, ReducedInstruction } from '../helpers/types';
|
|
2
2
|
/**
|
|
3
3
|
* Convert command objects to arrays of points, run de Casteljau's algorithm on it
|
|
4
4
|
* to split into to the desired number of segments.
|
|
5
5
|
*
|
|
6
6
|
* @param {Object} commandStart The start command object
|
|
7
|
-
* @param {Object}
|
|
7
|
+
* @param {Object} instructionEnd The end command object
|
|
8
8
|
* @param {Number} segmentCount The number of segments to create
|
|
9
9
|
* @return {Object[]} An array of commands representing the segments in sequence
|
|
10
10
|
*/
|
|
11
|
-
export declare const
|
|
11
|
+
export declare const splitCurveInstructions: (instructionStartX: number, instructionStartY: number, instructionEnd: LInstruction | QInstruction | CInstruction, segmentCount: number) => ReducedInstruction[];
|
|
@@ -32,134 +32,30 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
32
32
|
|
|
33
33
|
*/
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
}
|
|
35
|
+
exports.splitCurveInstructions = void 0;
|
|
36
|
+
const points_to_command_1 = require("./points-to-command");
|
|
37
|
+
const split_curve_as_points_1 = require("./split-curve-as-points");
|
|
145
38
|
/**
|
|
146
39
|
* Convert command objects to arrays of points, run de Casteljau's algorithm on it
|
|
147
40
|
* to split into to the desired number of segments.
|
|
148
41
|
*
|
|
149
42
|
* @param {Object} commandStart The start command object
|
|
150
|
-
* @param {Object}
|
|
43
|
+
* @param {Object} instructionEnd The end command object
|
|
151
44
|
* @param {Number} segmentCount The number of segments to create
|
|
152
45
|
* @return {Object[]} An array of commands representing the segments in sequence
|
|
153
46
|
*/
|
|
154
|
-
const
|
|
155
|
-
const points = [[
|
|
156
|
-
if (
|
|
157
|
-
points.push([
|
|
158
|
-
}
|
|
159
|
-
if (
|
|
160
|
-
points.push([
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
47
|
+
const splitCurveInstructions = (instructionStartX, instructionStartY, instructionEnd, segmentCount) => {
|
|
48
|
+
const points = [[instructionStartX, instructionStartY]];
|
|
49
|
+
if (instructionEnd.type === 'Q') {
|
|
50
|
+
points.push([instructionEnd.cpx, instructionEnd.cpy]);
|
|
51
|
+
}
|
|
52
|
+
if (instructionEnd.type === 'C') {
|
|
53
|
+
points.push([instructionEnd.cp1x, instructionEnd.cp1y]);
|
|
54
|
+
points.push([instructionEnd.cp2x, instructionEnd.cp2y]);
|
|
55
|
+
}
|
|
56
|
+
points.push([instructionEnd.x, instructionEnd.y]);
|
|
57
|
+
return (0, split_curve_as_points_1.splitCurveAsPoints)(points, segmentCount).map((p) => {
|
|
58
|
+
return (0, points_to_command_1.pointsToInstruction)(p);
|
|
59
|
+
});
|
|
164
60
|
};
|
|
165
|
-
exports.
|
|
61
|
+
exports.splitCurveInstructions = splitCurveInstructions;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReducedInstruction } from '../helpers/types';
|
|
2
2
|
/**
|
|
3
3
|
* Interpolate between command objects commandStart and commandEnd segmentCount times.
|
|
4
4
|
* If the types are L, Q, or C then the curves are split as per de Casteljau's algorithm.
|
|
@@ -11,4 +11,4 @@ import type { Command } from './command';
|
|
|
11
11
|
* @return {Object[]} Array of ~segmentCount command objects between commandStart and
|
|
12
12
|
* commandEnd. (Can be segmentCount+1 objects if commandStart is type M).
|
|
13
13
|
*/
|
|
14
|
-
export declare function
|
|
14
|
+
export declare function splitSegmentInstructions(commandStart: ReducedInstruction, commandEnd: ReducedInstruction, segmentCount: number): ReducedInstruction[];
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const array_of_length_1 = require("./array-of-length");
|
|
3
|
+
exports.splitSegmentInstructions = void 0;
|
|
5
4
|
const split_curve_1 = require("./split-curve");
|
|
6
5
|
/**
|
|
7
6
|
* Interpolate between command objects commandStart and commandEnd segmentCount times.
|
|
@@ -15,27 +14,28 @@ const split_curve_1 = require("./split-curve");
|
|
|
15
14
|
* @return {Object[]} Array of ~segmentCount command objects between commandStart and
|
|
16
15
|
* commandEnd. (Can be segmentCount+1 objects if commandStart is type M).
|
|
17
16
|
*/
|
|
18
|
-
|
|
19
|
-
function splitSegment(commandStart, commandEnd, segmentCount) {
|
|
17
|
+
function splitSegmentInstructions(commandStart, commandEnd, segmentCount) {
|
|
20
18
|
let segments = [];
|
|
21
19
|
// line, quadratic bezier, or cubic bezier
|
|
22
20
|
if (commandEnd.type === 'L' ||
|
|
23
21
|
commandEnd.type === 'Q' ||
|
|
24
22
|
commandEnd.type === 'C') {
|
|
25
23
|
if (commandStart.type !== 'Z') {
|
|
26
|
-
segments = segments.concat((0, split_curve_1.
|
|
24
|
+
segments = segments.concat((0, split_curve_1.splitCurveInstructions)(commandStart.x, commandStart.y, commandEnd, segmentCount));
|
|
27
25
|
}
|
|
28
26
|
// general case - just copy the same point
|
|
29
27
|
}
|
|
30
28
|
else {
|
|
31
|
-
const copyCommand =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
const copyCommand = commandStart.type === 'M'
|
|
30
|
+
? {
|
|
31
|
+
type: 'L',
|
|
32
|
+
x: commandStart.x,
|
|
33
|
+
y: commandStart.y,
|
|
34
|
+
}
|
|
35
|
+
: commandStart;
|
|
36
|
+
segments = segments.concat(new Array(segmentCount - 1).fill(true).map(() => copyCommand));
|
|
37
37
|
segments.push(commandEnd);
|
|
38
38
|
}
|
|
39
39
|
return segments;
|
|
40
40
|
}
|
|
41
|
-
exports.
|
|
41
|
+
exports.splitSegmentInstructions = splitSegmentInstructions;
|