mirror of
https://github.com/Pomax/BezierInfo-2.git
synced 2025-08-28 10:40:52 +02:00
227 lines
5.9 KiB
JavaScript
227 lines
5.9 KiB
JavaScript
/**
|
|
* Normalise an SVG path to absolute coordinates
|
|
* and full commands, rather than relative coordinates
|
|
* and/or shortcut commands.
|
|
*/
|
|
export default function normalizePath(d) {
|
|
// preprocess "d" so that we have spaces between values
|
|
d = d
|
|
.replace(/,/g, " ") // replace commas with spaces
|
|
.replace(/-/g, " - ") // add spacing around minus signs
|
|
.replace(/-\s+/g, "-") // remove spacing to the right of minus signs.
|
|
.replace(/([a-zA-Z])/g, " $1 ");
|
|
|
|
// set up the variables used in this function
|
|
const instructions = d.replace(/([a-zA-Z])\s?/g, "|$1").split("|"),
|
|
instructionLength = instructions.length;
|
|
|
|
let i,
|
|
instruction,
|
|
op,
|
|
lop,
|
|
args = [],
|
|
alen,
|
|
a,
|
|
sx = 0,
|
|
sy = 0,
|
|
x = 0,
|
|
y = 0,
|
|
cx = 0,
|
|
cy = 0,
|
|
cx2 = 0,
|
|
cy2 = 0,
|
|
rx = 0,
|
|
ry = 0,
|
|
xrot = 0,
|
|
lflag = 0,
|
|
sweep = 0,
|
|
normalized = "";
|
|
|
|
// we run through the instruction list starting at 1, not 0,
|
|
// because we split up "|M x y ...." so the first element will
|
|
// always be an empty string. By design.
|
|
for (i = 1; i < instructionLength; i++) {
|
|
// which instruction is this?
|
|
instruction = instructions[i];
|
|
op = instruction.substring(0, 1);
|
|
lop = op.toLowerCase();
|
|
|
|
// what are the arguments? note that we need to convert
|
|
// all strings into numbers, or + will do silly things.
|
|
args = instruction.replace(op, "").trim().split(" ");
|
|
args = args
|
|
.filter(function (v) {
|
|
return v !== "";
|
|
})
|
|
.map(parseFloat);
|
|
alen = args.length;
|
|
|
|
// we could use a switch, but elaborate code in a "case" with
|
|
// fallthrough is just horrid to read. So let's use ifthen
|
|
// statements instead.
|
|
|
|
// moveto command (plus possible lineto)
|
|
if (lop === "m") {
|
|
normalized += "M ";
|
|
if (op === "m") {
|
|
x += args[0];
|
|
y += args[1];
|
|
} else {
|
|
x = args[0];
|
|
y = args[1];
|
|
}
|
|
// records start position, for dealing
|
|
// with the shape close operator ('Z')
|
|
sx = x;
|
|
sy = y;
|
|
normalized += x + " " + y + " ";
|
|
if (alen > 2) {
|
|
for (a = 0; a < alen; a += 2) {
|
|
if (op === "m") {
|
|
x += args[a];
|
|
y += args[a + 1];
|
|
} else {
|
|
x = args[a];
|
|
y = args[a + 1];
|
|
}
|
|
normalized += "L " + x + " " + y + " ";
|
|
}
|
|
}
|
|
}
|
|
|
|
// lineto commands
|
|
else if (lop === "l") {
|
|
for (a = 0; a < alen; a += 2) {
|
|
if (op === "l") {
|
|
x += args[a];
|
|
y += args[a + 1];
|
|
} else {
|
|
x = args[a];
|
|
y = args[a + 1];
|
|
}
|
|
normalized += "L " + x + " " + y + " ";
|
|
}
|
|
} else if (lop === "h") {
|
|
for (a = 0; a < alen; a++) {
|
|
if (op === "h") {
|
|
x += args[a];
|
|
} else {
|
|
x = args[a];
|
|
}
|
|
normalized += "L " + x + " " + y + " ";
|
|
}
|
|
} else if (lop === "v") {
|
|
for (a = 0; a < alen; a++) {
|
|
if (op === "v") {
|
|
y += args[a];
|
|
} else {
|
|
y = args[a];
|
|
}
|
|
normalized += "L " + x + " " + y + " ";
|
|
}
|
|
}
|
|
|
|
// quadratic curveto commands
|
|
else if (lop === "q") {
|
|
for (a = 0; a < alen; a += 4) {
|
|
if (op === "q") {
|
|
cx = x + args[a];
|
|
cy = y + args[a + 1];
|
|
x += args[a + 2];
|
|
y += args[a + 3];
|
|
} else {
|
|
cx = args[a];
|
|
cy = args[a + 1];
|
|
x = args[a + 2];
|
|
y = args[a + 3];
|
|
}
|
|
normalized += "Q " + cx + " " + cy + " " + x + " " + y + " ";
|
|
}
|
|
} else if (lop === "t") {
|
|
for (a = 0; a < alen; a += 2) {
|
|
// reflect previous cx/cy over x/y
|
|
cx = x + (x - cx);
|
|
cy = y + (y - cy);
|
|
// then get real end point
|
|
if (op === "t") {
|
|
x += args[a];
|
|
y += args[a + 1];
|
|
} else {
|
|
x = args[a];
|
|
y = args[a + 1];
|
|
}
|
|
normalized += "Q " + cx + " " + cy + " " + x + " " + y + " ";
|
|
}
|
|
}
|
|
|
|
// cubic curveto commands
|
|
else if (lop === "c") {
|
|
for (a = 0; a < alen; a += 6) {
|
|
if (op === "c") {
|
|
cx = x + args[a];
|
|
cy = y + args[a + 1];
|
|
cx2 = x + args[a + 2];
|
|
cy2 = y + args[a + 3];
|
|
x += args[a + 4];
|
|
y += args[a + 5];
|
|
} else {
|
|
cx = args[a];
|
|
cy = args[a + 1];
|
|
cx2 = args[a + 2];
|
|
cy2 = args[a + 3];
|
|
x = args[a + 4];
|
|
y = args[a + 5];
|
|
}
|
|
normalized += "C " + cx + " " + cy + " " + cx2 + " " + cy2 + " " + x + " " + y + " ";
|
|
}
|
|
} else if (lop === "s") {
|
|
for (a = 0; a < alen; a += 4) {
|
|
// reflect previous cx2/cy2 over x/y
|
|
cx = x + (x - cx2);
|
|
cy = y + (y - cy2);
|
|
// then get real control and end point
|
|
if (op === "s") {
|
|
cx2 = x + args[a];
|
|
cy2 = y + args[a + 1];
|
|
x += args[a + 2];
|
|
y += args[a + 3];
|
|
} else {
|
|
cx2 = args[a];
|
|
cy2 = args[a + 1];
|
|
x = args[a + 2];
|
|
y = args[a + 3];
|
|
}
|
|
normalized += "C " + cx + " " + cy + " " + cx2 + " " + cy2 + " " + x + " " + y + " ";
|
|
}
|
|
}
|
|
|
|
// rx ry x-axis-rotation large-arc-flag sweep-flag x y
|
|
// a 25,25 -30 0, 1 50,-25
|
|
|
|
// arc command
|
|
else if (lop === "a") {
|
|
for (a = 0; a < alen; a += 7) {
|
|
rx = args[a];
|
|
ry = args[a + 1];
|
|
xrot = args[a + 2];
|
|
lflag = args[a + 3];
|
|
sweep = args[a + 4];
|
|
if (op === "a") {
|
|
x += args[a + 5];
|
|
y += args[a + 6];
|
|
} else {
|
|
x = args[a + 5];
|
|
y = args[a + 6];
|
|
}
|
|
normalized += "A " + rx + " " + ry + " " + xrot + " " + lflag + " " + sweep + " " + x + " " + y + " ";
|
|
}
|
|
} else if (lop === "z") {
|
|
normalized += "Z ";
|
|
// not unimportant: path closing changes the current x/y coordinate
|
|
x = sx;
|
|
y = sy;
|
|
}
|
|
}
|
|
return normalized.trim();
|
|
}
|