{
  "version": 3,
  "sources": ["../../../../../src/lib/shapes/shared/freehand/getStrokeOutlinePoints.ts"],
  "sourcesContent": ["import { Vec, VecLike } from '@tldraw/editor'\nimport {\n\tloadSrcFromStrokePoints,\n\tresolveTaper,\n\tsrcCount,\n\tsrcInputX,\n\tsrcInputY,\n\tsrcIsCap,\n\tsrcRadius,\n\tsrcRunningLength,\n\tsrcX,\n\tsrcY,\n\tsrcZ,\n} from './core'\nimport type { StrokeOptions, StrokePoint } from './types'\n\nconst { PI } = Math\n\n// Browser strokes seem to be off if PI is regular, a tiny offset seems to fix it\nconst FIXED_PI = PI + 0.0001\n\n// How far the simplified outline tracks may deviate from the raw tracks, as a fraction of the\n// stroke size. Well below visible thresholds: the parity harness shows sub-0.1px differences at\n// default stroke sizes.\nconst TRACK_TOLERANCE_RATIO = 0.05\n\n// The maximum number of intermediate points the track simplifier may drop per kept segment.\nconst SIMPLIFY_WINDOW = 8\n\n// How many steps to take when rounding a corner\nconst MIN_ROUNDED_CORNER_STEPS = 8\nconst MAX_ROUNDED_CORNER_STEPS = 13\n\n// How many steps to take when rounding a corner\nconst MIN_CAP_STEPS = 8\nconst MAX_CAP_STEPS = 29\n\n// Dot product threshold for identifying a hard corner\nconst HARD_CORNER_DPR = -0.62\n\n// ---------------------------------------------------------------------------------\n// Track buffers: the left and right outline tracks, written by `buildTracks` and read\n// either by svgInk's path writer or materialized into Vecs by the public functions.\n// Reusable and non-reentrant, like the pipeline buffers in core.ts.\n// ---------------------------------------------------------------------------------\n\nlet trackCapacity = 1024\nexport let trackLeftX = new Float64Array(trackCapacity)\nexport let trackLeftY = new Float64Array(trackCapacity)\nexport let trackRightX = new Float64Array(trackCapacity)\nexport let trackRightY = new Float64Array(trackCapacity)\nexport let trackLeftCount = 0\nexport let trackRightCount = 0\n\n// Tracks grow while being written (corners append a variable number of points), so unlike\n// the other buffers a grow here must copy the points written so far.\nfunction growTracks() {\n\ttrackCapacity *= 2\n\tconst nlx = new Float64Array(trackCapacity)\n\tnlx.set(trackLeftX)\n\ttrackLeftX = nlx\n\tconst nly = new Float64Array(trackCapacity)\n\tnly.set(trackLeftY)\n\ttrackLeftY = nly\n\tconst nrx = new Float64Array(trackCapacity)\n\tnrx.set(trackRightX)\n\ttrackRightX = nrx\n\tconst nry = new Float64Array(trackCapacity)\n\tnry.set(trackRightY)\n\ttrackRightY = nry\n}\n\n/**\n * Drop track points that lie within tolderance (`tol`) of the segment between their kept neighbors.\n * The outline tracks are dense on gentle curves and straight runs where the quadratic smoothing\n * used for rendering needs far fewer points; this keeps the simplified polyline within `tol` of\n * the original one. Works in place: kept points are compacted toward the front of the arrays.\n *\n * @param xs - The x coordinates of the track to simplify\n * @param ys - The y coordinates of the track to simplify\n * @param len - The number of points in the track\n * @param tol - The tolerance\n *\n * @returns The number of points in the simplified track.\n */\nfunction simplifyTrack(xs: Float64Array, ys: Float64Array, len: number, tol: number): number {\n\tif (len <= 2 || tol <= 0) return len\n\tconst tol2 = tol * tol\n\tlet out = 1\n\tlet anchor = 0\n\tconst lastIdx = len - 1\n\twhile (anchor < lastIdx) {\n\t\tlet best = anchor + 1\n\t\tconst maxJ = anchor + SIMPLIFY_WINDOW > lastIdx ? lastIdx : anchor + SIMPLIFY_WINDOW\n\t\tconst ax = xs[anchor]\n\t\tconst ay = ys[anchor]\n\t\touter: for (let j = anchor + 2; j <= maxJ; j++) {\n\t\t\tconst acx = xs[j] - ax\n\t\t\tconst acy = ys[j] - ay\n\t\t\tconst l2 = acx * acx + acy * acy\n\t\t\tfor (let k = anchor + 1; k < j; k++) {\n\t\t\t\tlet t = l2 === 0 ? 0 : ((xs[k] - ax) * acx + (ys[k] - ay) * acy) / l2\n\t\t\t\tt = t < 0 ? 0 : t > 1 ? 1 : t\n\t\t\t\tconst ex = xs[k] - (ax + acx * t)\n\t\t\t\tconst ey = ys[k] - (ay + acy * t)\n\t\t\t\tif (ex * ex + ey * ey > tol2) break outer\n\t\t\t}\n\t\t\tbest = j\n\t\t}\n\t\t// Compaction never overtakes the read cursor: the `out`th kept index is always >= out.\n\t\txs[out] = xs[best]\n\t\tys[out] = ys[best]\n\t\tout++\n\t\tanchor = best\n\t}\n\treturn out\n}\n\n/**\n * Build the left and right outline tracks for the stroke points currently loaded in the\n * track-source buffers, into the track buffers. This is the array core of\n * `getStrokeOutlineTracks`.\n *\n * `hasAnchor`/`anchorX`/`anchorY` carry the original predecessor of point 1 when the\n * caller has cut or altered the sequence in front of it (svgInk's elbow partitions): the\n * second point's vector is derived from the anchor rather than from point 0, preserving\n * the direction it had in the uncut stroke. It only applies when there are more than two\n * points; two-point sequences derive both vectors from each other.\n *\n * @internal\n */\nexport function buildTracks(\n\toptions: StrokeOptions,\n\thasAnchor: boolean,\n\tanchorX: number,\n\tanchorY: number\n): void {\n\tconst { size = 16, smoothing = 0.5 } = options\n\n\tlet lc = 0\n\tlet rc = 0\n\ttrackLeftCount = 0\n\ttrackRightCount = 0\n\n\tconst n = srcCount\n\n\t// We can't do anything with an empty array or a stroke with negative size.\n\tif (n === 0 || size <= 0) return\n\n\t// Local captures of the source buffers, hoisting the binding reads out of the loop.\n\t// The track buffers can grow mid-loop, so those are re-captured after growth.\n\tconst sx = srcX\n\tconst sy = srcY\n\tconst six = srcInputX\n\tconst siy = srcInputY\n\tconst sr = srcRadius\n\tconst srl = srcRunningLength\n\tconst scap = srcIsCap\n\tlet lxs = trackLeftX\n\tlet lys = trackLeftY\n\tlet rxs = trackRightX\n\tlet rys = trackRightY\n\n\t// The total length of the line\n\tconst totalLength = srl[n - 1]\n\n\t// The minimum allowed distance between points (squared)\n\tconst minDistance = Math.pow(size * smoothing, 2)\n\n\t// Stroke point vectors are derived on the fly from consecutive points: a point's vector is\n\t// the unit vector pointing back at its predecessor (matching what getStrokePoints used to\n\t// store). The first point shares the second point's vector; a lone point keeps the legacy\n\t// unnormalized (1, 1).\n\tlet curVecX = 1\n\tlet curVecY = 1\n\tif (n > 1) {\n\t\tconst dx = sx[0] - sx[1]\n\t\tconst dy = sy[0] - sy[1]\n\t\tconst l = (dx * dx + dy * dy) ** 0.5\n\t\tif (l === 0) {\n\t\t\tcurVecX = dx\n\t\t\tcurVecY = dy\n\t\t} else {\n\t\t\tcurVecX = dx / l\n\t\t\tcurVecY = dy / l\n\t\t}\n\t}\n\n\t// Previous vector\n\tlet prevVecX = curVecX\n\tlet prevVecY = curVecY\n\n\t// Previous left and right points\n\tlet plx = sx[0]\n\tlet ply = sy[0]\n\tlet prx = plx\n\tlet pry = ply\n\n\t// Temporary left and right points\n\tlet tlx = plx\n\tlet tly = ply\n\tlet trx = prx\n\tlet trY = pry\n\n\t// Keep track of whether the previous point is a sharp corner\n\t// ... so that we don't detect the same corner twice\n\tlet isPrevPointSharpCorner = false\n\n\t/*\n    Find the outline's left and right points\n\n    Iterating through the points and populate the rightPts and leftPts arrays,\n    skipping the first and last pointsm, which will get caps later on.\n  */\n\n\tfor (let i = 0; i < n; i++) {\n\t\tconst pointX = sx[i]\n\t\tconst pointY = sy[i]\n\t\tconst radius = sr[i]\n\t\tconst vecX = curVecX\n\t\tconst vecY = curVecY\n\n\t\t// Derive the next point's vector (the last point reuses its own), and advance the\n\t\t// running vector so the next iteration picks it up regardless of `continue`s below.\n\t\tlet nextVecX = vecX\n\t\tlet nextVecY = vecY\n\t\tif (i < n - 1) {\n\t\t\tconst fromX = i === 0 && n > 2 && hasAnchor ? anchorX : pointX\n\t\t\tconst fromY = i === 0 && n > 2 && hasAnchor ? anchorY : pointY\n\t\t\tconst dx = fromX - sx[i + 1]\n\t\t\tconst dy = fromY - sy[i + 1]\n\t\t\tconst l = (dx * dx + dy * dy) ** 0.5\n\t\t\tif (l === 0) {\n\t\t\t\tnextVecX = dx\n\t\t\t\tnextVecY = dy\n\t\t\t} else {\n\t\t\t\tnextVecX = dx / l\n\t\t\t\tnextVecY = dy / l\n\t\t\t}\n\t\t}\n\t\tcurVecX = nextVecX\n\t\tcurVecY = nextVecY\n\n\t\t// Make sure a corner's worth of points will fit on each side.\n\t\tif (\n\t\t\tlc + MAX_ROUNDED_CORNER_STEPS + 1 > trackCapacity ||\n\t\t\trc + MAX_ROUNDED_CORNER_STEPS + 1 > trackCapacity\n\t\t) {\n\t\t\tgrowTracks()\n\t\t\tlxs = trackLeftX\n\t\t\tlys = trackLeftY\n\t\t\trxs = trackRightX\n\t\t\trys = trackRightY\n\t\t}\n\n\t\t/*\n      Handle sharp corners\n\n      Find the difference (dot product) between the current and next vector.\n      If the next vector is at more than a right angle to the current vector,\n      draw a cap at the current point.\n    */\n\n\t\tconst prevDpr = vecX * prevVecX + vecY * prevVecY\n\t\tconst nextDpr = i < n - 1 ? nextVecX * vecX + nextVecY * vecY : 1\n\n\t\tconst isPointSharpCorner = prevDpr < 0 && !isPrevPointSharpCorner\n\t\tconst isNextPointSharpCorner = nextDpr < 0.2\n\n\t\tif (isPointSharpCorner || isNextPointSharpCorner) {\n\t\t\t// It's a sharp corner. Draw a rounded cap and move on to the next point\n\t\t\t// Considering saving these and drawing them later? So that we can avoid\n\t\t\t// crossing future points.\n\n\t\t\tif (nextDpr > HARD_CORNER_DPR && totalLength - srl[i] > radius) {\n\t\t\t\t// Draw a \"soft\" corner\n\t\t\t\tconst offsetX = prevVecX * radius\n\t\t\t\tconst offsetY = prevVecY * radius\n\t\t\t\tconst cpr = prevVecX * nextVecY - prevVecY * nextVecX\n\n\t\t\t\tif (cpr < 0) {\n\t\t\t\t\ttlx = pointX + offsetX\n\t\t\t\t\ttly = pointY + offsetY\n\t\t\t\t\ttrx = pointX - offsetX\n\t\t\t\t\ttrY = pointY - offsetY\n\t\t\t\t} else {\n\t\t\t\t\ttlx = pointX - offsetX\n\t\t\t\t\ttly = pointY - offsetY\n\t\t\t\t\ttrx = pointX + offsetX\n\t\t\t\t\ttrY = pointY + offsetY\n\t\t\t\t}\n\n\t\t\t\tlxs[lc] = tlx\n\t\t\t\tlys[lc] = tly\n\t\t\t\tlc++\n\t\t\t\trxs[rc] = trx\n\t\t\t\trys[rc] = trY\n\t\t\t\trc++\n\t\t\t} else {\n\t\t\t\t// Draw a \"sharp\" corner: rotate around the input point\n\t\t\t\tconst inX = six[i]\n\t\t\t\tconst inY = siy[i]\n\t\t\t\t// The arm swept around the point starts perpendicular to the\n\t\t\t\t// incoming direction, one radius long.\n\t\t\t\tconst dx = -prevVecY * radius\n\t\t\t\tconst dy = prevVecX * radius\n\n\t\t\t\tfor (let step = 1 / MAX_ROUNDED_CORNER_STEPS, t = 0; t < 1; t += step) {\n\t\t\t\t\tlet angle = FIXED_PI * t\n\t\t\t\t\tlet s = Math.sin(angle)\n\t\t\t\t\tlet c = Math.cos(angle)\n\t\t\t\t\ttlx = inX + (dx * c - dy * s)\n\t\t\t\t\ttly = inY + (dx * s + dy * c)\n\t\t\t\t\tlxs[lc] = tlx\n\t\t\t\t\tlys[lc] = tly\n\t\t\t\t\tlc++\n\n\t\t\t\t\tangle = FIXED_PI + FIXED_PI * -t\n\t\t\t\t\ts = Math.sin(angle)\n\t\t\t\t\tc = Math.cos(angle)\n\t\t\t\t\ttrx = inX + (dx * c - dy * s)\n\t\t\t\t\ttrY = inY + (dx * s + dy * c)\n\t\t\t\t\trxs[rc] = trx\n\t\t\t\t\trys[rc] = trY\n\t\t\t\t\trc++\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tplx = tlx\n\t\t\tply = tly\n\t\t\tprx = trx\n\t\t\tpry = trY\n\n\t\t\tif (isNextPointSharpCorner) {\n\t\t\t\tisPrevPointSharpCorner = true\n\t\t\t}\n\n\t\t\tcontinue\n\t\t}\n\n\t\tisPrevPointSharpCorner = false\n\n\t\tif (scap[i]) {\n\t\t\t// Project one radius to each side, perpendicular to the direction of travel.\n\t\t\tconst offsetX = vecY * radius\n\t\t\tconst offsetY = -vecX * radius\n\t\t\tlxs[lc] = pointX - offsetX\n\t\t\tlys[lc] = pointY - offsetY\n\t\t\tlc++\n\t\t\trxs[rc] = pointX + offsetX\n\t\t\trys[rc] = pointY + offsetY\n\t\t\trc++\n\n\t\t\tcontinue\n\t\t}\n\n\t\t/*\n      Add regular points\n\n      Project points to either side of the current point, using the\n      calculated size as a distance. If a point's distance to the\n      previous point on that side greater than the minimum distance\n      (or if the corner is kinda sharp), add the points to the side's\n      points array.\n    */\n\n\t\t// Project one radius to each side, perpendicular to the direction of\n\t\t// travel. The direction blends the current and next vectors, leaning\n\t\t// into the next vector as the upcoming turn sharpens.\n\t\tconst lerpedX = nextVecX + (vecX - nextVecX) * nextDpr\n\t\tconst lerpedY = nextVecY + (vecY - nextVecY) * nextDpr\n\t\tconst offsetX = lerpedY * radius\n\t\tconst offsetY = -lerpedX * radius\n\n\t\ttlx = pointX - offsetX\n\t\ttly = pointY - offsetY\n\n\t\tif (i <= 1 || (plx - tlx) ** 2 + (ply - tly) ** 2 > minDistance) {\n\t\t\tlxs[lc] = tlx\n\t\t\tlys[lc] = tly\n\t\t\tlc++\n\t\t\tplx = tlx\n\t\t\tply = tly\n\t\t}\n\n\t\ttrx = pointX + offsetX\n\t\ttrY = pointY + offsetY\n\n\t\tif (i <= 1 || (prx - trx) ** 2 + (pry - trY) ** 2 > minDistance) {\n\t\t\trxs[rc] = trx\n\t\t\trys[rc] = trY\n\t\t\trc++\n\t\t\tprx = trx\n\t\t\tpry = trY\n\t\t}\n\n\t\t// Set variables for next iteration\n\t\tprevVecX = vecX\n\t\tprevVecY = vecY\n\t}\n\n\tconst tolerance = size * TRACK_TOLERANCE_RATIO\n\n\ttrackLeftCount = simplifyTrack(trackLeftX, trackLeftY, lc, tolerance)\n\ttrackRightCount = simplifyTrack(trackRightX, trackRightY, rc, tolerance)\n}\n\n/**\n * @internal\n *\n * `vectorAnchor` is the original predecessor of `strokePoints[1]` when the caller has cut or\n * altered the sequence in front of it (svgInk's elbow partitions): the second point's vector is\n * derived from the anchor rather than from `strokePoints[0]`, preserving the direction it had in\n * the uncut stroke. It only applies when there are more than two points; two-point sequences\n * derive both vectors from each other.\n */\nexport function getStrokeOutlineTracks(\n\tstrokePoints: StrokePoint[],\n\toptions: StrokeOptions = {},\n\tvectorAnchor?: VecLike\n): { left: Vec[]; right: Vec[] } {\n\tloadSrcFromStrokePoints(strokePoints)\n\tbuildTracks(\n\t\toptions,\n\t\t!!vectorAnchor,\n\t\tvectorAnchor ? vectorAnchor.x : 0,\n\t\tvectorAnchor ? vectorAnchor.y : 0\n\t)\n\n\tconst lxs = trackLeftX\n\tconst lys = trackLeftY\n\tconst rxs = trackRightX\n\tconst rys = trackRightY\n\tconst left: Vec[] = new Array(trackLeftCount)\n\tfor (let i = 0; i < trackLeftCount; i++) {\n\t\tleft[i] = new Vec(lxs[i], lys[i])\n\t}\n\tconst right: Vec[] = new Array(trackRightCount)\n\tfor (let i = 0; i < trackRightCount; i++) {\n\t\tright[i] = new Vec(rxs[i], rys[i])\n\t}\n\treturn { left, right }\n}\n\n/** Pick a step count for a polygonal arc so its chord error stays within `tol`. */\nfunction arcSteps(radius: number, sweep: number, tol: number, min: number, max: number) {\n\tif (radius <= tol) return min\n\tconst maxAngle = 2 * Math.acos(1 - tol / radius)\n\tconst steps = Math.ceil(sweep / maxAngle)\n\treturn steps < min ? min : steps > max ? max : steps\n}\n\n/**\n * Build the full outline (tracks plus caps) for the stroke points currently loaded in the\n * track-source buffers. This is the shared core of `getStrokeOutlinePoints` and\n * `getStroke`.\n *\n * @internal\n */\nexport function outlineFromSrc(options: StrokeOptions = {}): Vec[] {\n\tconst { size = 16, start = {}, end = {}, last: isComplete = false } = options\n\n\tconst { cap: capStart = true } = start\n\tconst { cap: capEnd = true } = end\n\n\tconst n = srcCount\n\n\t// We can't do anything with an empty array or a stroke with negative size.\n\tif (n === 0 || size <= 0) {\n\t\treturn []\n\t}\n\n\t// The total length of the line\n\tconst totalLength = srcRunningLength[n - 1]\n\n\tconst taperStart = resolveTaper(start.taper, size, totalLength)\n\tconst taperEnd = resolveTaper(end.taper, size, totalLength)\n\n\t// Our collected left and right points\n\tbuildTracks(options, false, 0, 0)\n\n\t// Chord tolerance for the polygonal caps below: caps don't need a fixed number of segments,\n\t// they need enough segments that the polygon is indistinguishable from the arc.\n\tconst capTolerance = Math.max(0.05, size * 0.02)\n\n\tconst firstRadius = srcRadius[0]\n\tconst firstPoint = new Vec(srcX[0], srcY[0], srcZ[0])\n\n\tconst lastPoint =\n\t\tn > 1 ? new Vec(srcX[n - 1], srcY[n - 1], srcZ[n - 1]) : Vec.AddXY(firstPoint, 1, 1)\n\n\t/*\n    Draw a dot for very short or completed strokes\n\n    If the line is too short to gather left or right points and if the line is\n    not tapered on either side, draw a dot. If the line is tapered, then only\n    draw a dot if the line is both very short and complete. If we draw a dot,\n    we can just return those points.\n  */\n\n\tif (n === 1) {\n\t\tif (!(taperStart || taperEnd) || isComplete) {\n\t\t\tconst start = Vec.Add(\n\t\t\t\tfirstPoint,\n\t\t\t\tVec.Sub(firstPoint, lastPoint).uni().per().mul(-firstRadius)\n\t\t\t)\n\t\t\tconst dotPts: Vec[] = []\n\t\t\tconst steps = arcSteps(\n\t\t\t\tfirstRadius,\n\t\t\t\tFIXED_PI * 2,\n\t\t\t\tcapTolerance,\n\t\t\t\tMIN_ROUNDED_CORNER_STEPS,\n\t\t\t\tMAX_ROUNDED_CORNER_STEPS\n\t\t\t)\n\t\t\tfor (let step = 1 / steps, t = step; t <= 1; t += step) {\n\t\t\t\tdotPts.push(Vec.RotWith(start, firstPoint, FIXED_PI * 2 * t))\n\t\t\t}\n\t\t\treturn dotPts\n\t\t}\n\t}\n\n\t/*\n    Draw a start cap\n\n    Unless the line has a tapered start, or unless the line has a tapered end\n    and the line is very short, draw a start cap around the first point. Use\n    the distance between the second left and right point for the cap's radius.\n    Finally remove the first left and right points. :psyduck:\n  */\n\n\tconst startCap: Vec[] = []\n\tif (taperStart || (taperEnd && n === 1)) {\n\t\t// The start point is tapered, noop\n\t} else if (capStart) {\n\t\t// Draw the round cap - rotate the right point around the start point to the left point\n\t\tconst firstRight = new Vec(trackRightX[0], trackRightY[0])\n\t\tconst steps = arcSteps(firstRadius, FIXED_PI, capTolerance, 4, 8)\n\t\tfor (let step = 1 / steps, t = step; t <= 1; t += step) {\n\t\t\tconst pt = Vec.RotWith(firstRight, firstPoint, FIXED_PI * t)\n\t\t\tstartCap.push(pt)\n\t\t}\n\t} else {\n\t\t// Draw the flat cap - add a point to the left and right of the start point\n\t\tconst cornersVector = new Vec(trackLeftX[0] - trackRightX[0], trackLeftY[0] - trackRightY[0])\n\t\tconst offsetA = Vec.Mul(cornersVector, 0.5)\n\t\tconst offsetB = Vec.Mul(cornersVector, 0.51)\n\n\t\tstartCap.push(\n\t\t\tVec.Sub(firstPoint, offsetA),\n\t\t\tVec.Sub(firstPoint, offsetB),\n\t\t\tVec.Add(firstPoint, offsetB),\n\t\t\tVec.Add(firstPoint, offsetA)\n\t\t)\n\t}\n\n\t/*\n    Draw an end cap\n\n    If the line does not have a tapered end, and unless the line has a tapered\n    start and the line is very short, draw a cap around the last point. Finally,\n    remove the last left and right points. Otherwise, add the last point. Note\n    that This cap is a full-turn-and-a-half: this prevents incorrect caps on\n    sharp end turns.\n  */\n\n\tconst endCap: Vec[] = []\n\tconst lastRadius = srcRadius[n - 1]\n\n\t// The exit vector at the last point points back at its predecessor,\n\t// normalized; a lone point keeps the legacy (1, 1). The cap then starts\n\t// perpendicular to that vector.\n\tlet lastVecX = 1\n\tlet lastVecY = 1\n\tif (n > 1) {\n\t\tconst dx = srcX[n - 2] - srcX[n - 1]\n\t\tconst dy = srcY[n - 2] - srcY[n - 1]\n\t\tconst l = (dx * dx + dy * dy) ** 0.5\n\t\tif (l === 0) {\n\t\t\tlastVecX = dx\n\t\t\tlastVecY = dy\n\t\t} else {\n\t\t\tlastVecX = dx / l\n\t\t\tlastVecY = dy / l\n\t\t}\n\t}\n\tconst direction = new Vec(-lastVecY, lastVecX)\n\n\tif (taperEnd || (taperStart && n === 1)) {\n\t\t// Tapered end - push the last point to the line\n\t\tendCap.push(lastPoint)\n\t} else if (capEnd) {\n\t\t// Draw the round end cap\n\t\tconst start = Vec.Add(lastPoint, Vec.Mul(direction, lastRadius))\n\t\tconst steps = arcSteps(lastRadius, FIXED_PI * 3, capTolerance, MIN_CAP_STEPS, MAX_CAP_STEPS)\n\t\tfor (let step = 1 / steps, t = step; t < 1; t += step) {\n\t\t\tendCap.push(Vec.RotWith(start, lastPoint, FIXED_PI * 3 * t))\n\t\t}\n\t} else {\n\t\t// Draw the flat end cap\n\t\tendCap.push(\n\t\t\tVec.Add(lastPoint, Vec.Mul(direction, lastRadius)),\n\t\t\tVec.Add(lastPoint, Vec.Mul(direction, lastRadius * 0.99)),\n\t\t\tVec.Sub(lastPoint, Vec.Mul(direction, lastRadius * 0.99)),\n\t\t\tVec.Sub(lastPoint, Vec.Mul(direction, lastRadius))\n\t\t)\n\t}\n\n\t/*\n    Return the points in the correct winding order: begin on the left side, then\n    continue around the end cap, then come back along the right side, and finally\n    complete the start cap.\n  */\n\n\tconst lxs = trackLeftX\n\tconst lys = trackLeftY\n\tconst rxs = trackRightX\n\tconst rys = trackRightY\n\tconst leftPts: Vec[] = new Array(trackLeftCount)\n\tfor (let i = 0; i < trackLeftCount; i++) {\n\t\tleftPts[i] = new Vec(lxs[i], lys[i])\n\t}\n\tconst rightPtsReversed: Vec[] = new Array(trackRightCount)\n\tfor (let i = 0; i < trackRightCount; i++) {\n\t\trightPtsReversed[i] = new Vec(rxs[trackRightCount - 1 - i], rys[trackRightCount - 1 - i])\n\t}\n\n\treturn leftPts.concat(endCap, rightPtsReversed, startCap)\n}\n\n/**\n * ## getStrokeOutlinePoints\n *\n * Get an array of points (as `[x, y]`) representing the outline of a stroke.\n *\n * @param points - An array of StrokePoints as returned from `getStrokePoints`.\n * @param options - An object with options.\n * @public\n */\nexport function getStrokeOutlinePoints(\n\tstrokePoints: StrokePoint[],\n\toptions: StrokeOptions = {}\n): Vec[] {\n\tloadSrcFromStrokePoints(strokePoints)\n\treturn outlineFromSrc(options)\n}\n"],
  "mappings": "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",
  "names": ["offsetX", "offsetY", "start"]
}
