Created
April 5, 2020 02:39
-
-
Save spacejack/25ec6fe532d0868174add72d37361657 to your computer and use it in GitHub Desktop.
SVGPath parser/serializer sketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Expected number of values for each command type */ | |
const CMD_LENGTHS = { | |
a: 7, c: 6, C: 6, h: 1, l: 2, m: 2, M: 2, q: 4, s: 4, S: 4, t: 2, v: 1, z: 0 | |
} | |
type CmdType = keyof typeof CMD_LENGTHS | |
export interface PathSeg { | |
/** Command type string */ | |
cmd: CmdType | |
/** Values array */ | |
v: number[] | |
} | |
export function PathSeg (cmd: string, v: number[]): PathSeg { | |
const n = CMD_LENGTHS[cmd as CmdType] | |
if (n == null) { | |
console.warn('Unrecognized command type in path: ' + cmd) | |
} else { | |
if (n !== v.length) { | |
console.warn(`Unexpected value count ${v.length} for command ${cmd}`) | |
} | |
} | |
return {cmd: cmd as CmdType, v} | |
} | |
export namespace PathSeg { | |
export function clone(src: PathSeg) { | |
return { | |
cmd: src.cmd, | |
v: src.v.slice() | |
} | |
} | |
} | |
/** Parse the vertices & control points from a svg path d attribute */ | |
export function dParse (path: string): PathSeg[] { | |
const rx = /[a-z]/ig | |
const indices: number[] = [] | |
for (let match = rx.exec(path); !!match; match = rx.exec(path)) { | |
indices.push(match.index) | |
} | |
const lenm1 = indices.length - 1 | |
return indices.map((idx, i) => { | |
const idx2 = i < lenm1 ? indices[i + 1] : path.length | |
const cmd = path[idx] | |
const str = path.slice(idx + 1, idx2) | |
return PathSeg(cmd, parseValues(str)) | |
}) | |
} | |
/** Parse the values from a path segment string. This string should not include commands. */ | |
function parseValues (str: string) { | |
const values: number[] = [] | |
let nstr = '' | |
for (let i = 0; i < str.length; ++i) { | |
const c = str[i] | |
if ((c < '0' || c > '9') && c !== '.') { | |
if (nstr) { | |
values.push(parseReal(nstr)) | |
nstr = c === '-' ? c : '' | |
} else { | |
if (c === '-') { | |
nstr = c | |
} | |
} | |
} else { | |
nstr += c | |
} | |
} | |
if (nstr.length > 0) { | |
values.push(parseReal(nstr)) | |
} | |
return values | |
} | |
/** Parse a real number or throw an Error */ | |
function parseReal (str: string) { | |
const n = Number(str) | |
if (!Number.isFinite(n)) { | |
throw new Error('Failed to parse number from: ' + str) | |
} | |
return n | |
} | |
/** Given an array of `PathPart`s, return an SVGPath `d` attribute string */ | |
export function dSerialize (seg: PathSeg[]) { | |
return seg.reduce((d, p) => { | |
if (d.length > 0) d += ' ' | |
d = d + `${p.cmd}${p.v.join(',')}` | |
return d | |
}, '') | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment