UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 42.3 kB
{"version":3,"file":"index.min.mjs","names":[],"sources":["../../../../src/util/path/index.ts"],"sourcesContent":["import { cache } from '../../cache';\nimport { config } from '../../config';\nimport { halfPI, PiBy180 } from '../../constants';\nimport type { TMat2D, TRadian, TRectBounds } from '../../typedefs';\nimport { cos } from '../misc/cos';\nimport { multiplyTransformMatrices, transformPoint } from '../misc/matrix';\nimport { sin } from '../misc/sin';\nimport { toFixed } from '../misc/toFixed';\nimport type {\n TCurveInfo,\n TComplexPathData,\n TParsedAbsoluteCubicCurveCommand,\n TPathSegmentInfo,\n TPointAngle,\n TSimpleParsedCommand,\n TSimplePathData,\n TPathSegmentCommandInfo,\n TComplexParsedCommand,\n TPathSegmentInfoCommon,\n TEndPathInfo,\n TParsedArcCommand,\n TComplexParsedCommandType,\n} from './typedefs';\nimport type { XY } from '../../Point';\nimport { Point } from '../../Point';\nimport { reArcCommandPoints, rePathCommand } from './regex';\nimport { reNum } from '../../parser/constants';\n\n/**\n * Commands that may be repeated\n */\nconst repeatedCommands: Record<string, 'l' | 'L'> = {\n m: 'l',\n M: 'L',\n};\n\n/**\n * Convert an arc of a rotated ellipse to a Bezier Curve\n * @param {TRadian} theta1 start of the arc\n * @param {TRadian} theta2 end of the arc\n * @param cosTh cosine of the angle of rotation\n * @param sinTh sine of the angle of rotation\n * @param rx x-axis radius (before rotation)\n * @param ry y-axis radius (before rotation)\n * @param cx1 center x of the ellipse\n * @param cy1 center y of the ellipse\n * @param mT\n * @param fromX starting point of arc x\n * @param fromY starting point of arc y\n */\nconst segmentToBezier = (\n theta1: TRadian,\n theta2: TRadian,\n cosTh: number,\n sinTh: number,\n rx: number,\n ry: number,\n cx1: number,\n cy1: number,\n mT: number,\n fromX: number,\n fromY: number,\n): TParsedAbsoluteCubicCurveCommand => {\n const costh1 = cos(theta1),\n sinth1 = sin(theta1),\n costh2 = cos(theta2),\n sinth2 = sin(theta2),\n toX = cosTh * rx * costh2 - sinTh * ry * sinth2 + cx1,\n toY = sinTh * rx * costh2 + cosTh * ry * sinth2 + cy1,\n cp1X = fromX + mT * (-cosTh * rx * sinth1 - sinTh * ry * costh1),\n cp1Y = fromY + mT * (-sinTh * rx * sinth1 + cosTh * ry * costh1),\n cp2X = toX + mT * (cosTh * rx * sinth2 + sinTh * ry * costh2),\n cp2Y = toY + mT * (sinTh * rx * sinth2 - cosTh * ry * costh2);\n\n return ['C', cp1X, cp1Y, cp2X, cp2Y, toX, toY];\n};\n\n/**\n * Adapted from {@link http://dxr.mozilla.org/mozilla-central/source/dom/svg/SVGPathDataParser.cpp}\n * by Andrea Bogazzi code is under MPL. if you don't have a copy of the license you can take it here\n * http://mozilla.org/MPL/2.0/\n * @param toX\n * @param toY\n * @param rx\n * @param ry\n * @param {number} large 0 or 1 flag\n * @param {number} sweep 0 or 1 flag\n * @param rotateX\n */\nconst arcToSegments = (\n toX: number,\n toY: number,\n rx: number,\n ry: number,\n large: number,\n sweep: number,\n rotateX: TRadian,\n): TParsedAbsoluteCubicCurveCommand[] => {\n if (rx === 0 || ry === 0) {\n return [];\n }\n let fromX = 0,\n fromY = 0,\n root = 0;\n const PI = Math.PI,\n theta = rotateX * PiBy180,\n sinTheta = sin(theta),\n cosTh = cos(theta),\n px = 0.5 * (-cosTh * toX - sinTheta * toY),\n py = 0.5 * (-cosTh * toY + sinTheta * toX),\n rx2 = rx ** 2,\n ry2 = ry ** 2,\n py2 = py ** 2,\n px2 = px ** 2,\n pl = rx2 * ry2 - rx2 * py2 - ry2 * px2;\n let _rx = Math.abs(rx);\n let _ry = Math.abs(ry);\n\n if (pl < 0) {\n const s = Math.sqrt(1 - pl / (rx2 * ry2));\n _rx *= s;\n _ry *= s;\n } else {\n root =\n (large === sweep ? -1.0 : 1.0) * Math.sqrt(pl / (rx2 * py2 + ry2 * px2));\n }\n\n const cx = (root * _rx * py) / _ry,\n cy = (-root * _ry * px) / _rx,\n cx1 = cosTh * cx - sinTheta * cy + toX * 0.5,\n cy1 = sinTheta * cx + cosTh * cy + toY * 0.5;\n let mTheta = calcVectorAngle(1, 0, (px - cx) / _rx, (py - cy) / _ry);\n let dtheta = calcVectorAngle(\n (px - cx) / _rx,\n (py - cy) / _ry,\n (-px - cx) / _rx,\n (-py - cy) / _ry,\n );\n\n if (sweep === 0 && dtheta > 0) {\n dtheta -= 2 * PI;\n } else if (sweep === 1 && dtheta < 0) {\n dtheta += 2 * PI;\n }\n\n // Convert into cubic bezier segments <= 90deg\n const segments = Math.ceil(Math.abs((dtheta / PI) * 2)),\n result = [],\n mDelta = dtheta / segments,\n mT =\n ((8 / 3) * Math.sin(mDelta / 4) * Math.sin(mDelta / 4)) /\n Math.sin(mDelta / 2);\n let th3 = mTheta + mDelta;\n\n for (let i = 0; i < segments; i++) {\n result[i] = segmentToBezier(\n mTheta,\n th3,\n cosTh,\n sinTheta,\n _rx,\n _ry,\n cx1,\n cy1,\n mT,\n fromX,\n fromY,\n );\n fromX = result[i][5];\n fromY = result[i][6];\n mTheta = th3;\n th3 += mDelta;\n }\n return result;\n};\n\n/**\n * @private\n * Calculate the angle between two vectors\n * @param ux u endpoint x\n * @param uy u endpoint y\n * @param vx v endpoint x\n * @param vy v endpoint y\n */\nconst calcVectorAngle = (\n ux: number,\n uy: number,\n vx: number,\n vy: number,\n): TRadian => {\n const ta = Math.atan2(uy, ux),\n tb = Math.atan2(vy, vx);\n if (tb >= ta) {\n return tb - ta;\n } else {\n return 2 * Math.PI - (ta - tb);\n }\n};\n\n// functions for the Cubic beizer\n// taken from: https://github.com/konvajs/konva/blob/7.0.5/src/shapes/Path.ts#L350\nconst CB1 = (t: number) => t ** 3;\nconst CB2 = (t: number) => 3 * t ** 2 * (1 - t);\nconst CB3 = (t: number) => 3 * t * (1 - t) ** 2;\nconst CB4 = (t: number) => (1 - t) ** 3;\n\n/**\n * Calculate bounding box of a cubic Bezier curve\n * Taken from http://jsbin.com/ivomiq/56/edit (no credits available)\n * TODO: can we normalize this with the starting points set at 0 and then translated the bbox?\n * @param {number} begx starting point\n * @param {number} begy\n * @param {number} cp1x first control point\n * @param {number} cp1y\n * @param {number} cp2x second control point\n * @param {number} cp2y\n * @param {number} endx end of bezier\n * @param {number} endy\n * @return {TRectBounds} the rectangular bounds\n */\nexport function getBoundsOfCurve(\n begx: number,\n begy: number,\n cp1x: number,\n cp1y: number,\n cp2x: number,\n cp2y: number,\n endx: number,\n endy: number,\n): TRectBounds {\n let argsString: string;\n if (config.cachesBoundsOfCurve) {\n // eslint-disable-next-line\n argsString = [...arguments].join();\n if (cache.boundsOfCurveCache[argsString]) {\n return cache.boundsOfCurveCache[argsString];\n }\n }\n\n const sqrt = Math.sqrt,\n abs = Math.abs,\n tvalues = [],\n bounds: [[x: number, y: number], [x: number, y: number]] = [\n [0, 0],\n [0, 0],\n ];\n\n let b = 6 * begx - 12 * cp1x + 6 * cp2x;\n let a = -3 * begx + 9 * cp1x - 9 * cp2x + 3 * endx;\n let c = 3 * cp1x - 3 * begx;\n\n for (let i = 0; i < 2; ++i) {\n if (i > 0) {\n b = 6 * begy - 12 * cp1y + 6 * cp2y;\n a = -3 * begy + 9 * cp1y - 9 * cp2y + 3 * endy;\n c = 3 * cp1y - 3 * begy;\n }\n\n if (abs(a) < 1e-12) {\n if (abs(b) < 1e-12) {\n continue;\n }\n const t = -c / b;\n if (0 < t && t < 1) {\n tvalues.push(t);\n }\n continue;\n }\n const b2ac = b * b - 4 * c * a;\n if (b2ac < 0) {\n continue;\n }\n const sqrtb2ac = sqrt(b2ac);\n const t1 = (-b + sqrtb2ac) / (2 * a);\n if (0 < t1 && t1 < 1) {\n tvalues.push(t1);\n }\n const t2 = (-b - sqrtb2ac) / (2 * a);\n if (0 < t2 && t2 < 1) {\n tvalues.push(t2);\n }\n }\n\n let j = tvalues.length;\n const jlen = j;\n const iterator = getPointOnCubicBezierIterator(\n begx,\n begy,\n cp1x,\n cp1y,\n cp2x,\n cp2y,\n endx,\n endy,\n );\n while (j--) {\n const { x, y } = iterator(tvalues[j]);\n bounds[0][j] = x;\n bounds[1][j] = y;\n }\n\n bounds[0][jlen] = begx;\n bounds[1][jlen] = begy;\n bounds[0][jlen + 1] = endx;\n bounds[1][jlen + 1] = endy;\n const result: TRectBounds = [\n new Point(Math.min(...bounds[0]), Math.min(...bounds[1])),\n new Point(Math.max(...bounds[0]), Math.max(...bounds[1])),\n ];\n if (config.cachesBoundsOfCurve) {\n cache.boundsOfCurveCache[argsString!] = result;\n }\n return result;\n}\n\n/**\n * Converts arc to a bunch of cubic Bezier curves\n * @param {number} fx starting point x\n * @param {number} fy starting point y\n * @param {TParsedArcCommand} coords Arc command\n */\nexport const fromArcToBeziers = (\n fx: number,\n fy: number,\n [_, rx, ry, rot, large, sweep, tx, ty]: TParsedArcCommand,\n): TParsedAbsoluteCubicCurveCommand[] => {\n const segsNorm = arcToSegments(tx - fx, ty - fy, rx, ry, large, sweep, rot);\n\n for (let i = 0, len = segsNorm.length; i < len; i++) {\n segsNorm[i][1] += fx;\n segsNorm[i][2] += fy;\n segsNorm[i][3] += fx;\n segsNorm[i][4] += fy;\n segsNorm[i][5] += fx;\n segsNorm[i][6] += fy;\n }\n return segsNorm;\n};\n\n/**\n * This function takes a parsed SVG path and makes it simpler for fabricJS logic.\n * Simplification consist of:\n * - All commands converted to absolute (lowercase to uppercase)\n * - S converted to C\n * - T converted to Q\n * - A converted to C\n * @param {TComplexPathData} path the array of commands of a parsed SVG path for `Path`\n * @return {TSimplePathData} the simplified array of commands of a parsed SVG path for `Path`\n * TODO: figure out how to remove the type assertions in a nice way\n */\nexport const makePathSimpler = (path: TComplexPathData): TSimplePathData => {\n // x and y represent the last point of the path, AKA the previous command point.\n // we add them to each relative command to make it an absolute comment.\n // we also swap the v V h H with L, because are easier to transform.\n let x = 0,\n y = 0;\n // x1 and y1 represent the last point of the subpath. the subpath is started with\n // m or M command. When a z or Z command is drawn, x and y need to be resetted to\n // the last x1 and y1.\n let x1 = 0,\n y1 = 0;\n // previous will host the letter of the previous command, to handle S and T.\n // controlX and controlY will host the previous reflected control point\n const destinationPath: TSimplePathData = [];\n let previous,\n // placeholders\n controlX = 0,\n controlY = 0;\n for (const parsedCommand of path) {\n const current: TComplexParsedCommand = [...parsedCommand];\n let converted: TSimpleParsedCommand | undefined;\n switch (\n current[0] // first letter\n ) {\n case 'l': // lineto, relative\n current[1] += x;\n current[2] += y;\n // falls through\n case 'L':\n x = current[1];\n y = current[2];\n converted = ['L', x, y];\n break;\n case 'h': // horizontal lineto, relative\n current[1] += x;\n // falls through\n case 'H':\n x = current[1];\n converted = ['L', x, y];\n break;\n case 'v': // vertical lineto, relative\n current[1] += y;\n // falls through\n case 'V':\n y = current[1];\n converted = ['L', x, y];\n break;\n case 'm': // moveTo, relative\n current[1] += x;\n current[2] += y;\n // falls through\n case 'M':\n x = current[1];\n y = current[2];\n x1 = current[1];\n y1 = current[2];\n converted = ['M', x, y];\n break;\n case 'c': // bezierCurveTo, relative\n current[1] += x;\n current[2] += y;\n current[3] += x;\n current[4] += y;\n current[5] += x;\n current[6] += y;\n // falls through\n case 'C':\n controlX = current[3];\n controlY = current[4];\n x = current[5];\n y = current[6];\n converted = ['C', current[1], current[2], controlX, controlY, x, y];\n break;\n case 's': // shorthand cubic bezierCurveTo, relative\n current[1] += x;\n current[2] += y;\n current[3] += x;\n current[4] += y;\n // falls through\n case 'S':\n // would be sScC but since we are swapping sSc for C, we check just that.\n if (previous === 'C') {\n // calculate reflection of previous control points\n controlX = 2 * x - controlX;\n controlY = 2 * y - controlY;\n } else {\n // If there is no previous command or if the previous command was not a C, c, S, or s,\n // the control point is coincident with the current point\n controlX = x;\n controlY = y;\n }\n x = current[3];\n y = current[4];\n converted = ['C', controlX, controlY, current[1], current[2], x, y];\n // converted[3] and converted[4] are NOW the second control point.\n // we keep it for the next reflection.\n controlX = converted[3];\n controlY = converted[4];\n break;\n case 'q': // quadraticCurveTo, relative\n current[1] += x;\n current[2] += y;\n current[3] += x;\n current[4] += y;\n // falls through\n case 'Q':\n controlX = current[1];\n controlY = current[2];\n x = current[3];\n y = current[4];\n converted = ['Q', controlX, controlY, x, y];\n break;\n case 't': // shorthand quadraticCurveTo, relative\n current[1] += x;\n current[2] += y;\n // falls through\n case 'T':\n if (previous === 'Q') {\n // calculate reflection of previous control point\n controlX = 2 * x - controlX;\n controlY = 2 * y - controlY;\n } else {\n // If there is no previous command or if the previous command was not a Q, q, T or t,\n // assume the control point is coincident with the current point\n controlX = x;\n controlY = y;\n }\n x = current[1];\n y = current[2];\n converted = ['Q', controlX, controlY, x, y];\n break;\n case 'a':\n current[6] += x;\n current[7] += y;\n // falls through\n case 'A':\n fromArcToBeziers(x, y, current).forEach((b) => destinationPath.push(b));\n x = current[6];\n y = current[7];\n break;\n case 'z':\n case 'Z':\n x = x1;\n y = y1;\n converted = ['Z'];\n break;\n default:\n }\n if (converted) {\n destinationPath.push(converted);\n previous = converted[0];\n } else {\n previous = '';\n }\n }\n return destinationPath;\n};\n\n// todo verify if we can just use the point class here\n/**\n * Calc length from point x1,y1 to x2,y2\n * @param {number} x1 starting point x\n * @param {number} y1 starting point y\n * @param {number} x2 starting point x\n * @param {number} y2 starting point y\n * @return {number} length of segment\n */\nconst calcLineLength = (\n x1: number,\n y1: number,\n x2: number,\n y2: number,\n): number => Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);\n\n/**\n * Get an iterator that takes a percentage and returns a point\n * @param {number} begx\n * @param {number} begy\n * @param {number} cp1x\n * @param {number} cp1y\n * @param {number} cp2x\n * @param {number} cp2y\n * @param {number} endx\n * @param {number} endy\n */\nconst getPointOnCubicBezierIterator =\n (\n begx: number,\n begy: number,\n cp1x: number,\n cp1y: number,\n cp2x: number,\n cp2y: number,\n endx: number,\n endy: number,\n ) =>\n (pct: number) => {\n const c1 = CB1(pct),\n c2 = CB2(pct),\n c3 = CB3(pct),\n c4 = CB4(pct);\n return new Point(\n endx * c1 + cp2x * c2 + cp1x * c3 + begx * c4,\n endy * c1 + cp2y * c2 + cp1y * c3 + begy * c4,\n );\n };\n\nconst QB1 = (t: number) => t ** 2;\nconst QB2 = (t: number) => 2 * t * (1 - t);\nconst QB3 = (t: number) => (1 - t) ** 2;\n\nconst getTangentCubicIterator =\n (\n p1x: number,\n p1y: number,\n p2x: number,\n p2y: number,\n p3x: number,\n p3y: number,\n p4x: number,\n p4y: number,\n ) =>\n (pct: number) => {\n const qb1 = QB1(pct),\n qb2 = QB2(pct),\n qb3 = QB3(pct),\n tangentX =\n 3 * (qb3 * (p2x - p1x) + qb2 * (p3x - p2x) + qb1 * (p4x - p3x)),\n tangentY =\n 3 * (qb3 * (p2y - p1y) + qb2 * (p3y - p2y) + qb1 * (p4y - p3y));\n return Math.atan2(tangentY, tangentX);\n };\n\nconst getPointOnQuadraticBezierIterator =\n (\n p1x: number,\n p1y: number,\n p2x: number,\n p2y: number,\n p3x: number,\n p3y: number,\n ) =>\n (pct: number) => {\n const c1 = QB1(pct),\n c2 = QB2(pct),\n c3 = QB3(pct);\n return new Point(\n p3x * c1 + p2x * c2 + p1x * c3,\n p3y * c1 + p2y * c2 + p1y * c3,\n );\n };\n\nconst getTangentQuadraticIterator =\n (\n p1x: number,\n p1y: number,\n p2x: number,\n p2y: number,\n p3x: number,\n p3y: number,\n ) =>\n (pct: number) => {\n const invT = 1 - pct,\n tangentX = 2 * (invT * (p2x - p1x) + pct * (p3x - p2x)),\n tangentY = 2 * (invT * (p2y - p1y) + pct * (p3y - p2y));\n return Math.atan2(tangentY, tangentX);\n };\n\n// this will run over a path segment (a cubic or quadratic segment) and approximate it\n// with 100 segments. This will good enough to calculate the length of the curve\nconst pathIterator = (\n iterator: (pct: number) => Point,\n x1: number,\n y1: number,\n) => {\n let tempP = new Point(x1, y1),\n tmpLen = 0;\n for (let perc = 1; perc <= 100; perc += 1) {\n const p = iterator(perc / 100);\n tmpLen += calcLineLength(tempP.x, tempP.y, p.x, p.y);\n tempP = p;\n }\n return tmpLen;\n};\n\n/**\n * Given a pathInfo, and a distance in pixels, find the percentage from 0 to 1\n * that correspond to that pixels run over the path.\n * The percentage will be then used to find the correct point on the canvas for the path.\n * @param {Array} segInfo fabricJS collection of information on a parsed path\n * @param {number} distance from starting point, in pixels.\n * @return {TPointAngle} info object with x and y ( the point on canvas ) and angle, the tangent on that point;\n */\nconst findPercentageForDistance = (\n segInfo: TCurveInfo<'Q' | 'C'>,\n distance: number,\n): TPointAngle => {\n let perc = 0,\n tmpLen = 0,\n tempP: XY = { x: segInfo.x, y: segInfo.y },\n p: XY = { ...tempP },\n nextLen: number,\n nextStep = 0.01,\n lastPerc = 0;\n // nextStep > 0.0001 covers 0.00015625 that 1/64th of 1/100\n // the path\n const iterator = segInfo.iterator,\n angleFinder = segInfo.angleFinder;\n while (tmpLen < distance && nextStep > 0.0001) {\n p = iterator(perc);\n lastPerc = perc;\n nextLen = calcLineLength(tempP.x, tempP.y, p.x, p.y);\n // compare tmpLen each cycle with distance, decide next perc to test.\n if (nextLen + tmpLen > distance) {\n // we discard this step and we make smaller steps.\n perc -= nextStep;\n nextStep /= 2;\n } else {\n tempP = p;\n perc += nextStep;\n tmpLen += nextLen;\n }\n }\n return { ...p, angle: angleFinder(lastPerc) };\n};\n\n/**\n * Run over a parsed and simplified path and extract some information (length of each command and starting point)\n * @param {TSimplePathData} path parsed path commands\n * @return {TPathSegmentInfo[]} path commands information\n */\nexport const getPathSegmentsInfo = (\n path: TSimplePathData,\n): TPathSegmentInfo[] => {\n let totalLength = 0,\n //x2 and y2 are the coords of segment start\n //x1 and y1 are the coords of the current point\n x1 = 0,\n y1 = 0,\n x2 = 0,\n y2 = 0,\n iterator,\n tempInfo: TPathSegmentInfo;\n const info: TPathSegmentInfo[] = [];\n for (const current of path) {\n const basicInfo: TPathSegmentInfoCommon<keyof TPathSegmentCommandInfo> = {\n x: x1,\n y: y1,\n command: current[0],\n length: 0,\n };\n switch (\n current[0] //first letter\n ) {\n case 'M':\n tempInfo = <TPathSegmentInfoCommon<'M'>>basicInfo;\n tempInfo.x = x2 = x1 = current[1];\n tempInfo.y = y2 = y1 = current[2];\n break;\n case 'L':\n tempInfo = <TPathSegmentInfoCommon<'L'>>basicInfo;\n tempInfo.length = calcLineLength(x1, y1, current[1], current[2]);\n x1 = current[1];\n y1 = current[2];\n break;\n case 'C':\n iterator = getPointOnCubicBezierIterator(\n x1,\n y1,\n current[1],\n current[2],\n current[3],\n current[4],\n current[5],\n current[6],\n );\n tempInfo = <TCurveInfo<'C'>>basicInfo;\n tempInfo.iterator = iterator;\n tempInfo.angleFinder = getTangentCubicIterator(\n x1,\n y1,\n current[1],\n current[2],\n current[3],\n current[4],\n current[5],\n current[6],\n );\n tempInfo.length = pathIterator(iterator, x1, y1);\n\n x1 = current[5];\n y1 = current[6];\n break;\n case 'Q':\n iterator = getPointOnQuadraticBezierIterator(\n x1,\n y1,\n current[1],\n current[2],\n current[3],\n current[4],\n );\n tempInfo = <TCurveInfo<'Q'>>basicInfo;\n tempInfo.iterator = iterator;\n tempInfo.angleFinder = getTangentQuadraticIterator(\n x1,\n y1,\n current[1],\n current[2],\n current[3],\n current[4],\n );\n tempInfo.length = pathIterator(iterator, x1, y1);\n x1 = current[3];\n y1 = current[4];\n break;\n case 'Z':\n // we add those in order to ease calculations later\n tempInfo = <TEndPathInfo>basicInfo;\n tempInfo.destX = x2;\n tempInfo.destY = y2;\n tempInfo.length = calcLineLength(x1, y1, x2, y2);\n x1 = x2;\n y1 = y2;\n break;\n }\n totalLength += tempInfo.length;\n info.push(tempInfo);\n }\n info.push({ length: totalLength, x: x1, y: y1 });\n return info;\n};\n\n/**\n * Get the point on the path that is distance along the path\n * @param path\n * @param distance\n * @param infos\n */\nexport const getPointOnPath = (\n path: TSimplePathData,\n distance: number,\n infos: TPathSegmentInfo[] = getPathSegmentsInfo(path),\n): TPointAngle | undefined => {\n let i = 0;\n while (distance - infos[i].length > 0 && i < infos.length - 2) {\n distance -= infos[i].length;\n i++;\n }\n const segInfo = infos[i],\n segPercent = distance / segInfo.length,\n segment = path[i];\n\n switch (segInfo.command) {\n case 'M':\n return { x: segInfo.x, y: segInfo.y, angle: 0 };\n case 'Z':\n return {\n ...new Point(segInfo.x, segInfo.y).lerp(\n new Point(segInfo.destX, segInfo.destY),\n segPercent,\n ),\n angle: Math.atan2(segInfo.destY - segInfo.y, segInfo.destX - segInfo.x),\n };\n case 'L':\n return {\n ...new Point(segInfo.x, segInfo.y).lerp(\n new Point(segment[1]!, segment[2]!),\n segPercent,\n ),\n angle: Math.atan2(segment[2]! - segInfo.y, segment[1]! - segInfo.x),\n };\n case 'C':\n return findPercentageForDistance(segInfo, distance);\n case 'Q':\n return findPercentageForDistance(segInfo, distance);\n default:\n // throw Error('Invalid command');\n }\n};\n\nconst rePathCmdAll = new RegExp(rePathCommand, 'gi');\nconst regExpArcCommandPoints = new RegExp(reArcCommandPoints, 'g');\nconst reMyNum = new RegExp(reNum, 'gi');\nconst commandLengths = {\n m: 2,\n l: 2,\n h: 1,\n v: 1,\n c: 6,\n s: 4,\n q: 4,\n t: 2,\n a: 7,\n} as const;\n/**\n *\n * @param {string} pathString\n * @return {TComplexPathData} An array of SVG path commands\n * @example <caption>Usage</caption>\n * parsePath('M 3 4 Q 3 5 2 1 4 0 Q 9 12 2 1 4 0') === [\n * ['M', 3, 4],\n * ['Q', 3, 5, 2, 1, 4, 0],\n * ['Q', 9, 12, 2, 1, 4, 0],\n * ];\n */\nexport const parsePath = (pathString: string): TComplexPathData => {\n const chain: TComplexPathData = [];\n const all = pathString.match(rePathCmdAll) ?? [];\n for (const matchStr of all) {\n // take match string and save the first letter as the command\n const commandLetter = matchStr[0] as TComplexParsedCommandType;\n // in case of Z we have very little to do\n if (commandLetter === 'z' || commandLetter === 'Z') {\n chain.push([commandLetter]);\n continue;\n }\n const commandLength =\n commandLengths[\n commandLetter.toLowerCase() as keyof typeof commandLengths\n ];\n\n let paramArr = [];\n if (commandLetter === 'a' || commandLetter === 'A') {\n // the arc command ha some peculariaties that requires a special regex other than numbers\n // it is possible to avoid using a space between the sweep and large arc flags, making them either\n // 00, 01, 10 or 11, making them identical to a plain number for the regex reMyNum\n // reset the regexp\n regExpArcCommandPoints.lastIndex = 0;\n for (let out = null; (out = regExpArcCommandPoints.exec(matchStr)); ) {\n paramArr.push(...out.slice(1));\n }\n } else {\n paramArr = matchStr.match(reMyNum) || [];\n }\n\n // inspect the length of paramArr, if is longer than commandLength\n // we are dealing with repeated commands\n for (let i = 0; i < paramArr.length; i += commandLength) {\n const newCommand = new Array(commandLength) as TComplexParsedCommand;\n const transformedCommand = repeatedCommands[commandLetter];\n newCommand[0] =\n i > 0 && transformedCommand ? transformedCommand : commandLetter;\n for (let j = 0; j < commandLength; j++) {\n newCommand[j + 1] = parseFloat(paramArr[i + j]);\n }\n chain.push(newCommand);\n }\n }\n return chain;\n};\n\n/**\n *\n * Converts points to a smooth SVG path\n * @param {XY[]} points Array of points\n * @param {number} [correction] Apply a correction to the path (usually we use `width / 1000`). If value is undefined 0 is used as the correction value.\n * @return {(string|number)[][]} An array of SVG path commands\n */\nexport const getSmoothPathFromPoints = (\n points: Point[],\n correction = 0,\n): TSimplePathData => {\n let p1 = new Point(points[0]),\n p2 = new Point(points[1]),\n multSignX = 1,\n multSignY = 0;\n const path: TSimplePathData = [],\n len = points.length,\n manyPoints = len > 2;\n\n if (manyPoints) {\n multSignX = points[2].x < p2.x ? -1 : points[2].x === p2.x ? 0 : 1;\n multSignY = points[2].y < p2.y ? -1 : points[2].y === p2.y ? 0 : 1;\n }\n path.push([\n 'M',\n p1.x - multSignX * correction,\n p1.y - multSignY * correction,\n ]);\n let i;\n for (i = 1; i < len; i++) {\n if (!p1.eq(p2)) {\n const midPoint = p1.midPointFrom(p2);\n // p1 is our bezier control point\n // midpoint is our endpoint\n // start point is p(i-1) value.\n path.push(['Q', p1.x, p1.y, midPoint.x, midPoint.y]);\n }\n p1 = points[i];\n if (i + 1 < points.length) {\n p2 = points[i + 1];\n }\n }\n if (manyPoints) {\n multSignX = p1.x > points[i - 2].x ? 1 : p1.x === points[i - 2].x ? 0 : -1;\n multSignY = p1.y > points[i - 2].y ? 1 : p1.y === points[i - 2].y ? 0 : -1;\n }\n path.push([\n 'L',\n p1.x + multSignX * correction,\n p1.y + multSignY * correction,\n ]);\n return path;\n};\n\n/**\n * Transform a path by transforming each segment.\n * it has to be a simplified path or it won't work.\n * WARNING: this depends from pathOffset for correct operation\n * @param {TSimplePathData} path fabricJS parsed and simplified path commands\n * @param {TMat2D} transform matrix that represent the transformation\n * @param {Point} [pathOffset] `Path.pathOffset`\n * @returns {TSimplePathData} the transformed path\n */\nexport const transformPath = (\n path: TSimplePathData,\n transform: TMat2D,\n pathOffset: Point,\n): TSimplePathData => {\n if (pathOffset) {\n transform = multiplyTransformMatrices(transform, [\n 1,\n 0,\n 0,\n 1,\n -pathOffset.x,\n -pathOffset.y,\n ]);\n }\n return path.map((pathSegment) => {\n const newSegment: TSimpleParsedCommand = [...pathSegment];\n for (let i = 1; i < pathSegment.length - 1; i += 2) {\n // TODO: is there a way to get around casting to any?\n const { x, y } = transformPoint(\n {\n x: pathSegment[i] as number,\n y: pathSegment[i + 1] as number,\n },\n transform,\n );\n newSegment[i] = x;\n newSegment[i + 1] = y;\n }\n return newSegment;\n });\n};\n\n/**\n * Returns an array of path commands to create a regular polygon\n * @param {number} numVertexes\n * @param {number} radius\n * @returns {TSimplePathData} An array of SVG path commands\n */\nexport const getRegularPolygonPath = (\n numVertexes: number,\n radius: number,\n): TSimplePathData => {\n const interiorAngle = (Math.PI * 2) / numVertexes;\n // rotationAdjustment rotates the path by 1/2 the interior angle so that the polygon always has a flat side on the bottom\n // This isn't strictly necessary, but it's how we tend to think of and expect polygons to be drawn\n let rotationAdjustment = -halfPI;\n if (numVertexes % 2 === 0) {\n rotationAdjustment += interiorAngle / 2;\n }\n const d = new Array(numVertexes + 1);\n for (let i = 0; i < numVertexes; i++) {\n const rad = i * interiorAngle + rotationAdjustment;\n const { x, y } = new Point(cos(rad), sin(rad)).scalarMultiply(radius);\n d[i] = [i === 0 ? 'M' : 'L', x, y];\n }\n d[numVertexes] = ['Z'];\n return d;\n};\n\n/**\n * Join path commands to go back to svg format\n * @param {TSimplePathData} pathData fabricJS parsed path commands\n * @param {number} fractionDigits number of fraction digits to \"leave\"\n * @return {String} joined path 'M 0 0 L 20 30'\n */\nexport const joinPath = (pathData: TSimplePathData, fractionDigits?: number) =>\n pathData\n .map((segment) => {\n return segment\n .map((arg, i) => {\n if (i === 0) return arg;\n return fractionDigits === undefined\n ? arg\n : toFixed(arg, fractionDigits);\n })\n .join(' ');\n })\n .join(' ');\n"],"mappings":"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"}