import { isPathLetter } from '../modules/core/regex.js' import Point from '../types/Point.js' const segmentParameters = { M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 7, Z: 0 } const pathHandlers = { M: function (c, p, p0) { p.x = p0.x = c[0] p.y = p0.y = c[1] return [ 'M', p.x, p.y ] }, L: function (c, p) { p.x = c[0] p.y = c[1] return [ 'L', c[0], c[1] ] }, H: function (c, p) { p.x = c[0] return [ 'H', c[0] ] }, V: function (c, p) { p.y = c[0] return [ 'V', c[0] ] }, C: function (c, p) { p.x = c[4] p.y = c[5] return [ 'C', c[0], c[1], c[2], c[3], c[4], c[5] ] }, S: function (c, p) { p.x = c[2] p.y = c[3] return [ 'S', c[0], c[1], c[2], c[3] ] }, Q: function (c, p) { p.x = c[2] p.y = c[3] return [ 'Q', c[0], c[1], c[2], c[3] ] }, T: function (c, p) { p.x = c[0] p.y = c[1] return [ 'T', c[0], c[1] ] }, Z: function (c, p, p0) { p.x = p0.x p.y = p0.y return [ 'Z' ] }, A: function (c, p) { p.x = c[5] p.y = c[6] return [ 'A', c[0], c[1], c[2], c[3], c[4], c[5], c[6] ] } } const mlhvqtcsaz = 'mlhvqtcsaz'.split('') for (let i = 0, il = mlhvqtcsaz.length; i < il; ++i) { pathHandlers[mlhvqtcsaz[i]] = (function (i) { return function (c, p, p0) { if (i === 'H') c[0] = c[0] + p.x else if (i === 'V') c[0] = c[0] + p.y else if (i === 'A') { c[5] = c[5] + p.x c[6] = c[6] + p.y } else { for (let j = 0, jl = c.length; j < jl; ++j) { c[j] = c[j] + (j % 2 ? p.y : p.x) } } return pathHandlers[i](c, p, p0) } })(mlhvqtcsaz[i].toUpperCase()) } function makeAbsolut (parser) { const command = parser.segment[0] return pathHandlers[command](parser.segment.slice(1), parser.p, parser.p0) } function segmentComplete (parser) { return parser.segment.length && parser.segment.length - 1 === segmentParameters[parser.segment[0].toUpperCase()] } function startNewSegment (parser, token) { parser.inNumber && finalizeNumber(parser, false) const pathLetter = isPathLetter.test(token) if (pathLetter) { parser.segment = [ token ] } else { const lastCommand = parser.lastCommand const small = lastCommand.toLowerCase() const isSmall = lastCommand === small parser.segment = [ small === 'm' ? (isSmall ? 'l' : 'L') : lastCommand ] } parser.inSegment = true parser.lastCommand = parser.segment[0] return pathLetter } function finalizeNumber (parser, inNumber) { if (!parser.inNumber) throw new Error('Parser Error') parser.number && parser.segment.push(parseFloat(parser.number)) parser.inNumber = inNumber parser.number = '' parser.pointSeen = false parser.hasExponent = false if (segmentComplete(parser)) { finalizeSegment(parser) } } function finalizeSegment (parser) { parser.inSegment = false if (parser.absolute) { parser.segment = makeAbsolut(parser) } parser.segments.push(parser.segment) } function isArcFlag (parser) { if (!parser.segment.length) return false const isArc = parser.segment[0].toUpperCase() === 'A' const length = parser.segment.length return isArc && (length === 4 || length === 5) } function isExponential (parser) { return parser.lastToken.toUpperCase() === 'E' } export function pathParser (d, toAbsolute = true) { let index = 0 let token = '' const parser = { segment: [], inNumber: false, number: '', lastToken: '', inSegment: false, segments: [], pointSeen: false, hasExponent: false, absolute: toAbsolute, p0: new Point(), p: new Point() } while ((parser.lastToken = token, token = d.charAt(index++))) { if (!parser.inSegment) { if (startNewSegment(parser, token)) { continue } } if (token === '.') { if (parser.pointSeen || parser.hasExponent) { finalizeNumber(parser, false) --index continue } parser.inNumber = true parser.pointSeen = true parser.number += token continue } if (!isNaN(parseInt(token))) { if (parser.number === '0' || isArcFlag(parser)) { parser.inNumber = true parser.number = token finalizeNumber(parser, true) continue } parser.inNumber = true parser.number += token continue } if (token === ' ' || token === ',') { if (parser.inNumber) { finalizeNumber(parser, false) } continue } if (token === '-') { if (parser.inNumber && !isExponential(parser)) { finalizeNumber(parser, false) --index continue } parser.number += token parser.inNumber = true continue } if (token.toUpperCase() === 'E') { parser.number += token parser.hasExponent = true continue } if (isPathLetter.test(token)) { if (parser.inNumber) { finalizeNumber(parser, false) } else if (!segmentComplete(parser)) { throw new Error('parser Error') } else { finalizeSegment(parser) } --index } } if (parser.inNumber) { finalizeNumber(parser, false) } if (parser.inSegment && segmentComplete(parser)) { finalizeSegment(parser) } return parser.segments }