UNPKG

rabbit-ear

Version:
86 lines (83 loc) 2.7 kB
/* Rabbit Ear 0.9.4 alpha 2024-04-20 (c) Kraft, GNU GPLv3 License */ const markerRegEx = /[MmLlSsQqLlHhVvCcSsQqTtAaZz]/g; const digitRegEx = /-?[0-9]*\.?\d+/g; const pathCommandNames = { m: "move", l: "line", v: "vertical", h: "horizontal", a: "ellipse", c: "curve", s: "smoothCurve", q: "quadCurve", t: "smoothQuadCurve", z: "close", }; Object.keys(pathCommandNames).forEach((key) => { const s = pathCommandNames[key]; pathCommandNames[key.toUpperCase()] = s.charAt(0).toUpperCase() + s.slice(1); }); const add2path = (a, b) => [a[0] + (b[0] || 0), a[1] + (b[1] || 0)]; const getEndpoint = (command, values, offset = [0, 0]) => { const upper = command.toUpperCase(); let origin = command === upper ? [0, 0] : offset; if (command === "V") { origin = [offset[0], 0]; } if (command === "H") { origin = [0, offset[1]]; } switch (upper) { case "V": return add2path(origin, [0, values[0]]); case "H": return add2path(origin, [values[0], 0]); case "M": case "L": case "T": return add2path(origin, values); case "A": return add2path(origin, [values[5], values[6]]); case "C": return add2path(origin, [values[4], values[5]]); case "S": case "Q": return add2path(origin, [values[2], values[3]]); case "Z": return undefined; default: return origin; } }; const parsePathCommands = (d) => { const results = []; let match = markerRegEx.exec(d); while (match !== null) { results.push(match); match = markerRegEx.exec(d); } return results .map((result, i, arr) => ({ command: result[0], start: result.index, end: i === arr.length - 1 ? d.length - 1 : arr[(i + 1) % arr.length].index - 1, })) .map(({ command, start, end }) => { const valueString = d.substring(start + 1, end + 1); const strings = valueString.match(digitRegEx); const values = strings ? strings.map(parseFloat) : []; return { command, values }; }); }; const parsePathCommandsWithEndpoints = (d) => { let pen = [0, 0]; const parsedCommands = parsePathCommands(d); const commands = parsedCommands .map(obj => ({ ...obj, end: undefined, start: undefined })); if (!commands.length) { return commands; } commands.forEach((command, i) => { commands[i].end = getEndpoint(command.command, command.values, pen); commands[i].start = i === 0 ? pen : commands[i - 1].end; pen = commands[i].end; }); const last = commands[commands.length - 1]; const firstDrawCommand = commands .filter(el => el.command.toUpperCase() !== "M" && el.command.toUpperCase() !== "Z") .shift(); if (last.command.toUpperCase() === "Z") { last.end = [...firstDrawCommand.start]; } return commands; }; export { parsePathCommands, parsePathCommandsWithEndpoints, pathCommandNames };