Skip to content

Commit

Permalink
feat(geom): re-add pathFromSvg()
Browse files Browse the repository at this point in the history
  • Loading branch information
postspectacular committed Jan 18, 2019
1 parent 66267e2 commit 3c9a7b0
Showing 1 changed file with 144 additions and 1 deletion.
145 changes: 144 additions & 1 deletion packages/geom3/src/ctors/path.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isNumber } from "@thi.ng/checks";
import { eqDelta } from "@thi.ng/math";
import { eqDelta, rad } from "@thi.ng/math";
import { map, mapcat, peek } from "@thi.ng/transducers";
import {
add2,
Expand Down Expand Up @@ -243,3 +243,146 @@ export class PathBuilder {

export const pathBuilder =
() => new PathBuilder();

const CMD_RE = /[achlmqstvz]/i;

export const pathFromSvg = (svg: string) => {
const b = new PathBuilder();
try {
let cmd: string;
for (let n = svg.length, i = 0; i < n;) {
i = skipWS(svg, i);
const c = svg.charAt(i);
if (CMD_RE.test(c)) {
cmd = c;
i++;
}
let p, pa, pb, t1, t2, t3;
switch (cmd.toLowerCase()) {
case "m":
[p, i] = readPoint(svg, i);
b.moveTo(p, cmd === "m");
break;
case "l":
[p, i] = readPoint(svg, i);
b.lineTo(p, cmd === "l");
break;
case "h":
[p, i] = readFloat(svg, i);
b.hlineTo(p, cmd === "h");
break;
case "v":
[p, i] = readFloat(svg, i);
b.vlineTo(p, cmd === "v");
break;
case "q":
[pa, i] = readPoint(svg, i);
[p, i] = readPoint(svg, i);
// console.log("quadratic", pa.toString(), p.toString());
b.quadraticTo(pa, p, cmd === "q");
break;
case "c":
[pa, i] = readPoint(svg, i);
[pb, i] = readPoint(svg, i);
[p, i] = readPoint(svg, i);
// console.log("cubic", pa.toString(), pb.toString(), p.toString());
b.cubicTo(pa, pb, p, cmd === "c");
break;
case "s":
[pa, i] = readPoint(svg, i);
[p, i] = readPoint(svg, i);
// console.log("cubicChain", pa.toString(), p.toString());
b.cubicChainTo(pa, p, cmd === "s");
break;
case "t":
[p, i] = readPoint(svg, i);
// console.log("quadraticChain", p.toString());
b.quadraticChainTo(p, cmd === "t");
break;
case "a": {
[pa, i] = readPoint(svg, i);
[t1, i] = readFloat(svg, i);
[t2, i] = readFloat(svg, i);
[t3, i] = readFloat(svg, i);
[pb, i] = readPoint(svg, i);
// console.log("arc", pa.toString(), rad(t1), t2, t3, pb.toString());
b.arcTo(pb, pa, rad(t1), !!t2, !!t3, cmd === "a");
break;
}
case "z":
b.closePath();
break;
default:
throw new Error(`unsupported segment type: ${c} @ pos ${i}`);
}
}
return b.paths;
} catch (e) {
throw e instanceof Error ? e : new Error(`illegal char '${svg.charAt(e)}' @ ${e}`);
}
};

const readPoint =
(src: string, index: number): [Vec, number] => {
let x, y;
[x, index] = readFloat(src, index);
index = skipWS(src, index);
[y, index] = readFloat(src, index);
return [[x, y], index];
};

const isWS =
(c: string) => c === " " || c === "\n" || c === "\r" || c === "\t";

const skipWS =
(src: string, i: number) => {
const n = src.length;
while (i < n && isWS(src.charAt(i))) i++;
return i;
};

const readFloat =
(src: string, index: number) => {
index = skipWS(src, index);
let signOk = true;
let dotOk = true;
let expOk = false;
let commaOk = false;
let i = index;
for (let n = src.length; i < n; i++) {
const c = src.charAt(i);
// console.log("float", src.substring(index, i + 1));
if ("0" <= c && c <= "9") {
expOk = true;
commaOk = true;
signOk = false;
continue;
}
if (c === "-" || c === "+") {
if (!signOk) break;
signOk = false;
continue;
}
if (c === ".") {
if (!dotOk) break;
dotOk = false;
continue;
}
if (c === "e") {
if (!expOk) throw i;
expOk = false;
dotOk = false;
signOk = true;
continue;
}
if (c === ",") {
if (!commaOk) throw i;
i++;
}
break;
}
if (i === index) {
throw new Error(`expected coordinate @ pos: ${i}`);
}
return [parseFloat(src.substring(index, i)), i];
};

0 comments on commit 3c9a7b0

Please sign in to comment.