diff options
Diffstat (limited to 'src/utils/pathParser.js')
-rw-r--r-- | src/utils/pathParser.js | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/src/utils/pathParser.js b/src/utils/pathParser.js new file mode 100644 index 0000000..1ab90a3 --- /dev/null +++ b/src/utils/pathParser.js @@ -0,0 +1,234 @@ +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 (var 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 (var 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' || (parser.inNumber && isArcFlag(parser))) { + finalizeNumber(parser, true) + } + + 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 + +} |