{"version":3,"file":"index.mjs","names":[],"sources":["../../src/constants.ts","../../src/getStrokeRadius.ts","../../src/simulatePressure.ts","../../src/vec.ts","../../src/getStrokeOutlinePoints.ts","../../src/getStrokePoints.ts","../../src/getStroke.ts","../../src/index.ts"],"sourcesContent":["/**\n * Constants used throughout the stroke generation algorithm.\n * @internal\n */\n\nconst { PI } = Math\n\n/**\n * Rate of change for simulated pressure.\n * Controls how quickly pressure changes based on drawing velocity.\n * Higher values make pressure more responsive to speed changes.\n */\nexport const RATE_OF_PRESSURE_CHANGE = 0.275\n\n/**\n * PI with a tiny offset to fix browser rendering artifacts.\n * Some browsers render strokes incorrectly when using exact PI.\n */\nexport const FIXED_PI = PI + 0.0001\n\n/**\n * Number of segments for rounded start caps.\n */\nexport const START_CAP_SEGMENTS = 13\n\n/**\n * Number of segments for rounded end caps.\n * Higher than start caps for smoother appearance at stroke endings.\n */\nexport const END_CAP_SEGMENTS = 29\n\n/**\n * Number of segments for sharp corner caps.\n */\nexport const CORNER_CAP_SEGMENTS = 13\n\n/**\n * Pixels to skip at the end of a stroke to reduce noise.\n */\nexport const END_NOISE_THRESHOLD = 3\n\n/**\n * Minimum interpolation factor for streamline.\n * Used when streamline is at maximum (1.0).\n */\nexport const MIN_STREAMLINE_T = 0.15\n\n/**\n * Range for interpolation factor calculation.\n * Added to MIN_STREAMLINE_T based on (1 - streamline).\n */\nexport const STREAMLINE_T_RANGE = 0.85\n\n/**\n * Minimum stroke radius to prevent invisible strokes.\n */\nexport const MIN_RADIUS = 0.01\n\n/**\n * Default pressure for the first point of a stroke.\n * Lower than subsequent points to prevent fat starts,\n * since drawn lines almost always start slow.\n */\nexport const DEFAULT_FIRST_PRESSURE = 0.25\n\n/**\n * Default pressure for subsequent points when no pressure is provided.\n */\nexport const DEFAULT_PRESSURE = 0.5\n\n/**\n * Unit offset vector used as placeholder for initial vector\n * and for creating a second point when only one point is provided.\n */\nexport const UNIT_OFFSET: [number, number] = [1, 1]\n","/**\n * Compute a radius based on the pressure.\n * @param size\n * @param thinning\n * @param pressure\n * @param easing\n * @internal\n */\nexport function getStrokeRadius(\n  size: number,\n  thinning: number,\n  pressure: number,\n  easing: (t: number) => number = (t) => t\n) {\n  return size * easing(0.5 - thinning * (0.5 - pressure))\n}\n","import { RATE_OF_PRESSURE_CHANGE } from './constants'\n\nconst { min } = Math\n\n/**\n * Simulate pressure based on the distance between points and stroke size.\n * This creates a natural-looking pressure effect based on drawing velocity.\n *\n * @param prevPressure The previous pressure value\n * @param distance The distance from the previous point\n * @param size The base stroke size\n * @returns The simulated pressure value (0-1)\n * @internal\n */\nexport function simulatePressure(\n  prevPressure: number,\n  distance: number,\n  size: number\n): number {\n  // Speed of change - how fast should the pressure be changing?\n  const sp = min(1, distance / size)\n  // Rate of change - how much of a change is there?\n  const rp = min(1, 1 - sp)\n  // Accelerate the pressure\n  return min(\n    1,\n    prevPressure + (rp - prevPressure) * (sp * RATE_OF_PRESSURE_CHANGE)\n  )\n}\n","import type { Vec2 } from './types'\n\n/**\n * Negate a vector.\n * @param A\n * @internal\n */\nexport function neg(A: Vec2): Vec2 {\n  return [-A[0], -A[1]]\n}\n\n/**\n * Add vectors.\n * @param A\n * @param B\n * @internal\n */\nexport function add(A: Vec2, B: Vec2): Vec2 {\n  return [A[0] + B[0], A[1] + B[1]]\n}\n\n/**\n * Add vectors into an existing output vector (allocation-free).\n * @param out Output vector to mutate\n * @param A\n * @param B\n * @internal\n */\nexport function addInto(out: Vec2, A: Vec2, B: Vec2): Vec2 {\n  out[0] = A[0] + B[0]\n  out[1] = A[1] + B[1]\n  return out\n}\n\n/**\n * Subtract vectors.\n * @param A\n * @param B\n * @internal\n */\nexport function sub(A: Vec2, B: Vec2): Vec2 {\n  return [A[0] - B[0], A[1] - B[1]]\n}\n\n/**\n * Subtract vectors into an existing output vector (allocation-free).\n * @param out Output vector to mutate\n * @param A\n * @param B\n * @internal\n */\nexport function subInto(out: Vec2, A: Vec2, B: Vec2): Vec2 {\n  out[0] = A[0] - B[0]\n  out[1] = A[1] - B[1]\n  return out\n}\n\n/**\n * Vector multiplication by scalar\n * @param A\n * @param n\n * @internal\n */\nexport function mul(A: Vec2, n: number): Vec2 {\n  return [A[0] * n, A[1] * n]\n}\n\n/**\n * Vector multiplication by scalar into an existing output vector (allocation-free).\n * @param out Output vector to mutate\n * @param A\n * @param n\n * @internal\n */\nexport function mulInto(out: Vec2, A: Vec2, n: number): Vec2 {\n  out[0] = A[0] * n\n  out[1] = A[1] * n\n  return out\n}\n\n/**\n * Vector division by scalar.\n * @param A\n * @param n\n * @internal\n */\nexport function div(A: Vec2, n: number): Vec2 {\n  return [A[0] / n, A[1] / n]\n}\n\n/**\n * Perpendicular rotation of a vector A\n * @param A\n * @internal\n */\nexport function per(A: Vec2): Vec2 {\n  return [A[1], -A[0]]\n}\n\n/**\n * Perpendicular rotation into an existing output vector (allocation-free).\n * @param out Output vector to mutate\n * @param A\n * @internal\n */\nexport function perInto(out: Vec2, A: Vec2): Vec2 {\n  const temp = A[0]\n  out[0] = A[1]\n  out[1] = -temp\n  return out\n}\n\n/**\n * Dot product\n * @param A\n * @param B\n * @internal\n */\nexport function dpr(A: Vec2, B: Vec2): number {\n  return A[0] * B[0] + A[1] * B[1]\n}\n\n/**\n * Get whether two vectors are equal.\n * @param A\n * @param B\n * @internal\n */\nexport function isEqual(A: Vec2, B: Vec2): boolean {\n  return A[0] === B[0] && A[1] === B[1]\n}\n\n/**\n * Length of the vector\n * @param A\n * @internal\n */\nexport function len(A: Vec2): number {\n  return Math.hypot(A[0], A[1])\n}\n\n/**\n * Length of the vector squared\n * @param A\n * @internal\n */\nexport function len2(A: Vec2): number {\n  return A[0] * A[0] + A[1] * A[1]\n}\n\n/**\n * Dist length from A to B squared (inlined for performance).\n * @param A\n * @param B\n * @internal\n */\nexport function dist2(A: Vec2, B: Vec2): number {\n  const dx = A[0] - B[0]\n  const dy = A[1] - B[1]\n  return dx * dx + dy * dy\n}\n\n/**\n * Get normalized / unit vector.\n * @param A\n * @internal\n */\nexport function uni(A: Vec2): Vec2 {\n  return div(A, len(A))\n}\n\n/**\n * Dist length from A to B\n * @param A\n * @param B\n * @internal\n */\nexport function dist(A: Vec2, B: Vec2): number {\n  return Math.hypot(A[1] - B[1], A[0] - B[0])\n}\n\n/**\n * Mean between two vectors or mid vector between two vectors\n * @param A\n * @param B\n * @internal\n */\nexport function med(A: Vec2, B: Vec2): Vec2 {\n  return mul(add(A, B), 0.5)\n}\n\n/**\n * Rotate a vector around another vector by r (radians)\n * @param A vector\n * @param C center\n * @param r rotation in radians\n * @internal\n */\nexport function rotAround(A: Vec2, C: Vec2, r: number): Vec2 {\n  const s = Math.sin(r)\n  const c = Math.cos(r)\n\n  const px = A[0] - C[0]\n  const py = A[1] - C[1]\n\n  const nx = px * c - py * s\n  const ny = px * s + py * c\n\n  return [nx + C[0], ny + C[1]]\n}\n\n/**\n * Rotate a vector around another vector by r (radians) into an existing output vector (allocation-free).\n * @param out Output vector to mutate\n * @param A vector\n * @param C center\n * @param r rotation in radians\n * @internal\n */\nexport function rotAroundInto(out: Vec2, A: Vec2, C: Vec2, r: number): Vec2 {\n  const s = Math.sin(r)\n  const c = Math.cos(r)\n\n  const px = A[0] - C[0]\n  const py = A[1] - C[1]\n\n  const nx = px * c - py * s\n  const ny = px * s + py * c\n\n  out[0] = nx + C[0]\n  out[1] = ny + C[1]\n  return out\n}\n\n/**\n * Interpolate vector A to B with a scalar t\n * @param A\n * @param B\n * @param t scalar\n * @internal\n */\nexport function lrp(A: Vec2, B: Vec2, t: number): Vec2 {\n  return add(A, mul(sub(B, A), t))\n}\n\n/**\n * Interpolate vector A to B with a scalar t into an existing output vector (allocation-free).\n * @param out Output vector to mutate\n * @param A\n * @param B\n * @param t scalar\n * @internal\n */\nexport function lrpInto(out: Vec2, A: Vec2, B: Vec2, t: number): Vec2 {\n  const dx = B[0] - A[0]\n  const dy = B[1] - A[1]\n  out[0] = A[0] + dx * t\n  out[1] = A[1] + dy * t\n  return out\n}\n\n/**\n * Project a point A in the direction B by a scalar c\n * @param A\n * @param B\n * @param c\n * @internal\n */\nexport function prj(A: Vec2, B: Vec2, c: number): Vec2 {\n  return add(A, mul(B, c))\n}\n\n/**\n * Project a point A in the direction B by a scalar c into an existing output vector (allocation-free).\n * @param out Output vector to mutate\n * @param A\n * @param B\n * @param c\n * @internal\n */\nexport function prjInto(out: Vec2, A: Vec2, B: Vec2, c: number): Vec2 {\n  out[0] = A[0] + B[0] * c\n  out[1] = A[1] + B[1] * c\n  return out\n}\n","import {\n  CORNER_CAP_SEGMENTS,\n  END_CAP_SEGMENTS,\n  END_NOISE_THRESHOLD,\n  FIXED_PI,\n  MIN_RADIUS,\n  START_CAP_SEGMENTS,\n} from './constants'\nimport { getStrokeRadius } from './getStrokeRadius'\nimport { simulatePressure } from './simulatePressure'\nimport type { StrokeOptions, StrokePoint, Vec2 } from './types'\nimport {\n  add,\n  addInto,\n  dist2,\n  dpr,\n  lrpInto,\n  mul,\n  mulInto,\n  neg,\n  per,\n  perInto,\n  prj,\n  rotAround,\n  rotAroundInto,\n  sub,\n  subInto,\n  uni,\n} from './vec'\n\n// Scratch buffers for allocation-free hot loop calculations\nconst _offset: Vec2 = [0, 0]\nconst _tl: Vec2 = [0, 0]\nconst _tr: Vec2 = [0, 0]\n\n/**\n * Draw a dot (circle) for very short strokes.\n */\nfunction drawDot(center: Vec2, radius: number): Vec2[] {\n  const offsetPoint = add(center, [1, 1])\n  const start = prj(center, uni(per(sub(center, offsetPoint))), -radius)\n  const dotPts: Vec2[] = []\n  const step = 1 / START_CAP_SEGMENTS\n  for (let t = step; t <= 1; t += step) {\n    dotPts.push(rotAround(start, center, FIXED_PI * 2 * t))\n  }\n  return dotPts\n}\n\n/**\n * Draw a rounded start cap by rotating points from right to left around the start point.\n */\nfunction drawRoundStartCap(\n  center: Vec2,\n  rightPoint: Vec2,\n  segments: number\n): Vec2[] {\n  const cap: Vec2[] = []\n  const step = 1 / segments\n  for (let t = step; t <= 1; t += step) {\n    cap.push(rotAround(rightPoint, center, FIXED_PI * t))\n  }\n  return cap\n}\n\n/**\n * Draw a flat start cap with squared-off edges.\n */\nfunction drawFlatStartCap(\n  center: Vec2,\n  leftPoint: Vec2,\n  rightPoint: Vec2\n): Vec2[] {\n  const cornersVector = sub(leftPoint, rightPoint)\n  const offsetA = mul(cornersVector, 0.5)\n  const offsetB = mul(cornersVector, 0.51)\n  return [\n    sub(center, offsetA),\n    sub(center, offsetB),\n    add(center, offsetB),\n    add(center, offsetA),\n  ]\n}\n\n/**\n * Draw a rounded end cap (1.5 turns to handle sharp end turns correctly).\n */\nfunction drawRoundEndCap(\n  center: Vec2,\n  direction: Vec2,\n  radius: number,\n  segments: number\n): Vec2[] {\n  const cap: Vec2[] = []\n  const start = prj(center, direction, radius)\n  const step = 1 / segments\n  for (let t = step; t < 1; t += step) {\n    cap.push(rotAround(start, center, FIXED_PI * 3 * t))\n  }\n  return cap\n}\n\n/**\n * Draw a flat end cap with squared-off edges.\n */\nfunction drawFlatEndCap(center: Vec2, direction: Vec2, radius: number): Vec2[] {\n  return [\n    add(center, mul(direction, radius)),\n    add(center, mul(direction, radius * 0.99)),\n    sub(center, mul(direction, radius * 0.99)),\n    sub(center, mul(direction, radius)),\n  ]\n}\n\n/**\n * Compute the taper distance from a taper option value.\n * - false or undefined: no taper (0)\n * - true: taper the full length (max of size and totalLength)\n * - number: use that exact taper distance\n */\nfunction computeTaperDistance(\n  taper: boolean | number | undefined,\n  size: number,\n  totalLength: number\n): number {\n  if (taper === false || taper === undefined) return 0\n  if (taper === true) return Math.max(size, totalLength)\n  return taper\n}\n\n/**\n * Compute the initial pressure by averaging the first few points.\n * This prevents \"fat starts\" since drawn lines almost always start slow.\n */\nfunction computeInitialPressure(\n  points: StrokePoint[],\n  shouldSimulatePressure: boolean,\n  size: number\n): number {\n  return points.slice(0, 10).reduce((acc, curr) => {\n    let pressure = curr.pressure\n    if (shouldSimulatePressure) {\n      pressure = simulatePressure(acc, curr.distance, size)\n    }\n    return (acc + pressure) / 2\n  }, points[0].pressure)\n}\n\n/**\n * ## getStrokeOutlinePoints\n * @description Get an array of points (as `[x, y]`) representing the outline of a stroke.\n * @param points An array of StrokePoints as returned from `getStrokePoints`.\n * @param options (optional) An object with options.\n * @param options.size\tThe base size (diameter) of the stroke.\n * @param options.thinning The effect of pressure on the stroke's size.\n * @param options.smoothing\tHow much to soften the stroke's edges.\n * @param options.easing\tAn easing function to apply to each point's pressure.\n * @param options.simulatePressure Whether to simulate pressure based on velocity.\n * @param options.start Cap, taper and easing for the start of the line.\n * @param options.end Cap, taper and easing for the end of the line.\n * @param options.last Whether to handle the points as a completed stroke.\n */\nexport function getStrokeOutlinePoints(\n  points: StrokePoint[],\n  options: Partial<StrokeOptions> = {} as Partial<StrokeOptions>\n): Vec2[] {\n  const {\n    size = 16,\n    smoothing = 0.5,\n    thinning = 0.5,\n    simulatePressure: shouldSimulatePressure = true,\n    easing = (t) => t,\n    start = {},\n    end = {},\n    last: isComplete = false,\n  } = options\n\n  const { cap: capStart = true, easing: taperStartEase = (t) => t * (2 - t) } =\n    start\n\n  const { cap: capEnd = true, easing: taperEndEase = (t) => --t * t * t + 1 } =\n    end\n\n  // We can't do anything with an empty array or a stroke with negative size.\n  if (points.length === 0 || size <= 0) {\n    return []\n  }\n\n  // The total length of the line\n  const totalLength = points[points.length - 1].runningLength\n\n  const taperStart = computeTaperDistance(start.taper, size, totalLength)\n  const taperEnd = computeTaperDistance(end.taper, size, totalLength)\n\n  // The minimum allowed distance between points (squared)\n  const minDistance = Math.pow(size * smoothing, 2)\n\n  // Our collected left and right points\n  const leftPts: Vec2[] = []\n  const rightPts: Vec2[] = []\n\n  // Previous pressure (averaged from first few points to prevent fat starts)\n  let prevPressure = computeInitialPressure(\n    points,\n    shouldSimulatePressure,\n    size\n  )\n\n  // The current radius\n  let radius = getStrokeRadius(\n    size,\n    thinning,\n    points[points.length - 1].pressure,\n    easing\n  )\n\n  // The radius of the first saved point\n  let firstRadius: number | undefined = undefined\n\n  // Previous vector\n  let prevVector = points[0].vector\n\n  // Previous left and right points\n  let prevLeftPoint = points[0].point\n  let prevRightPoint = prevLeftPoint\n\n  // Temporary left and right points\n  let tempLeftPoint: Vec2 = prevLeftPoint\n  let tempRightPoint: Vec2 = prevRightPoint\n\n  // Keep track of whether the previous point is a sharp corner\n  // ... so that we don't detect the same corner twice\n  let isPrevPointSharpCorner = false\n\n  /*\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  for (let i = 0; i < points.length; i++) {\n    let { pressure } = points[i]\n    const { point, vector, distance, runningLength } = points[i]\n    const isLastPoint = i === points.length - 1\n\n    // Removes noise from the end of the line\n    if (!isLastPoint && totalLength - runningLength < END_NOISE_THRESHOLD) {\n      continue\n    }\n\n    /*\n      Calculate the radius\n\n      If not thinning, the current point's radius will be half the size; or\n      otherwise, the size will be based on the current (real or simulated)\n      pressure.\n    */\n\n    if (thinning) {\n      if (shouldSimulatePressure) {\n        // If we're simulating pressure, then do so based on the distance\n        // between the current point and the previous point, and the size\n        // of the stroke. Otherwise, use the input pressure.\n        pressure = simulatePressure(prevPressure, distance, size)\n      }\n\n      radius = getStrokeRadius(size, thinning, pressure, easing)\n    } else {\n      radius = size / 2\n    }\n\n    if (firstRadius === undefined) {\n      firstRadius = radius\n    }\n\n    /*\n      Apply tapering\n\n      If the current length is within the taper distance at either the\n      start or the end, calculate the taper strengths. Apply the smaller\n      of the two taper strengths to the radius.\n    */\n\n    const taperStartStrength =\n      runningLength < taperStart\n        ? taperStartEase(runningLength / taperStart)\n        : 1\n\n    const taperEndStrength =\n      totalLength - runningLength < taperEnd\n        ? taperEndEase((totalLength - runningLength) / taperEnd)\n        : 1\n\n    radius = Math.max(\n      MIN_RADIUS,\n      radius * Math.min(taperStartStrength, taperEndStrength)\n    )\n\n    /* Add points to left and right */\n\n    /*\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    const nextVector = (!isLastPoint ? points[i + 1] : points[i]).vector\n    const nextDpr = !isLastPoint ? dpr(vector, nextVector) : 1.0\n    const prevDpr = dpr(vector, prevVector)\n\n    const isPointSharpCorner = prevDpr < 0 && !isPrevPointSharpCorner\n    const isNextPointSharpCorner = nextDpr !== null && nextDpr < 0\n\n    if (isPointSharpCorner || isNextPointSharpCorner) {\n      // It's a sharp corner. Draw a rounded cap and move on to the next point\n      // Considering saving these and drawing them later? So that we can avoid\n      // crossing future points.\n\n      // Use mutable operations for the offset calculation\n      perInto(_offset, prevVector)\n      mulInto(_offset, _offset, radius)\n\n      const step = 1 / CORNER_CAP_SEGMENTS\n      for (let t = 0; t <= 1; t += step) {\n        // Calculate left point: rotate (point - offset) around point\n        subInto(_tl, point, _offset)\n        rotAroundInto(_tl, _tl, point, FIXED_PI * t)\n        tempLeftPoint = [_tl[0], _tl[1]]\n        leftPts.push(tempLeftPoint)\n\n        // Calculate right point: rotate (point + offset) around point\n        addInto(_tr, point, _offset)\n        rotAroundInto(_tr, _tr, point, FIXED_PI * -t)\n        tempRightPoint = [_tr[0], _tr[1]]\n        rightPts.push(tempRightPoint)\n      }\n\n      prevLeftPoint = tempLeftPoint\n      prevRightPoint = tempRightPoint\n\n      if (isNextPointSharpCorner) {\n        isPrevPointSharpCorner = true\n      }\n      continue\n    }\n\n    isPrevPointSharpCorner = false\n\n    // Handle the last point\n    if (isLastPoint) {\n      perInto(_offset, vector)\n      mulInto(_offset, _offset, radius)\n      leftPts.push(sub(point, _offset))\n      rightPts.push(add(point, _offset))\n      continue\n    }\n\n    /*\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    // Use mutable operations for offset calculation\n    lrpInto(_offset, nextVector, vector, nextDpr)\n    perInto(_offset, _offset)\n    mulInto(_offset, _offset, radius)\n\n    subInto(_tl, point, _offset)\n    tempLeftPoint = [_tl[0], _tl[1]]\n\n    if (i <= 1 || dist2(prevLeftPoint, tempLeftPoint) > minDistance) {\n      leftPts.push(tempLeftPoint)\n      prevLeftPoint = tempLeftPoint\n    }\n\n    addInto(_tr, point, _offset)\n    tempRightPoint = [_tr[0], _tr[1]]\n\n    if (i <= 1 || dist2(prevRightPoint, tempRightPoint) > minDistance) {\n      rightPts.push(tempRightPoint)\n      prevRightPoint = tempRightPoint\n    }\n\n    // Set variables for next iteration\n    prevPressure = pressure\n    prevVector = vector\n  }\n\n  /*\n    Drawing caps\n\n    Now that we have our points on either side of the line, we need to\n    draw caps at the start and end. Tapered lines don't have caps, but\n    may have dots for very short lines.\n  */\n\n  const firstPoint: Vec2 = [points[0].point[0], points[0].point[1]]\n\n  const lastPoint: Vec2 =\n    points.length > 1\n      ? [points[points.length - 1].point[0], points[points.length - 1].point[1]]\n      : add(points[0].point, [1, 1])\n\n  const startCap: Vec2[] = []\n\n  const endCap: Vec2[] = []\n\n  // Draw a dot for very short or completed strokes\n  if (points.length === 1) {\n    if (!(taperStart || taperEnd) || isComplete) {\n      return drawDot(firstPoint, firstRadius || radius)\n    }\n  } else {\n    // Draw start cap (unless tapered)\n    if (taperStart || (taperEnd && points.length === 1)) {\n      // The start point is tapered, noop\n    } else if (capStart) {\n      startCap.push(\n        ...drawRoundStartCap(firstPoint, rightPts[0], START_CAP_SEGMENTS)\n      )\n    } else {\n      startCap.push(...drawFlatStartCap(firstPoint, leftPts[0], rightPts[0]))\n    }\n\n    // Draw end cap (unless tapered)\n    const direction = per(neg(points[points.length - 1].vector))\n\n    if (taperEnd || (taperStart && points.length === 1)) {\n      // Tapered end - push the last point to the line\n      endCap.push(lastPoint)\n    } else if (capEnd) {\n      endCap.push(\n        ...drawRoundEndCap(lastPoint, direction, radius, END_CAP_SEGMENTS)\n      )\n    } else {\n      endCap.push(...drawFlatEndCap(lastPoint, direction, radius))\n    }\n  }\n\n  /*\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  return leftPts.concat(endCap, rightPts.reverse(), startCap)\n}\n","import {\n  DEFAULT_FIRST_PRESSURE,\n  DEFAULT_PRESSURE,\n  MIN_STREAMLINE_T,\n  STREAMLINE_T_RANGE,\n  UNIT_OFFSET,\n} from './constants'\nimport type { StrokeOptions, StrokePoint, Vec2 } from './types'\nimport { add, dist, isEqual, lrp, subInto, uni } from './vec'\n\n// Scratch buffer for allocation-free vector calculation in hot loop\nconst _vectorDiff: Vec2 = [0, 0]\n\n/**\n * Check if a pressure value is valid (defined and non-negative).\n * Returns false for undefined, NaN, and negative values.\n */\nfunction isValidPressure(pressure: number | undefined): pressure is number {\n  return pressure != null && pressure >= 0\n}\n\n/**\n * ## getStrokePoints\n * @description Get an array of points as objects with an adjusted point, pressure, vector, distance, and runningLength.\n * @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional in both cases.\n * @param options (optional) An object with options.\n * @param options.size\tThe base size (diameter) of the stroke.\n * @param options.thinning The effect of pressure on the stroke's size.\n * @param options.smoothing\tHow much to soften the stroke's edges.\n * @param options.easing\tAn easing function to apply to each point's pressure.\n * @param options.simulatePressure Whether to simulate pressure based on velocity.\n * @param options.start Cap, taper and easing for the start of the line.\n * @param options.end Cap, taper and easing for the end of the line.\n * @param options.last Whether to handle the points as a completed stroke.\n */\nexport function getStrokePoints<\n  T extends number[],\n  K extends { x: number; y: number; pressure?: number },\n>(points: (T | K)[], options = {} as StrokeOptions): StrokePoint[] {\n  const { streamline = 0.5, size = 16, last: isComplete = false } = options\n\n  // If we don't have any points, return an empty array.\n  if (points.length === 0) return []\n\n  // Find the interpolation level between points.\n  const t = MIN_STREAMLINE_T + (1 - streamline) * STREAMLINE_T_RANGE\n\n  // Whatever the input is, make sure that the points are in number[][].\n  let pts = Array.isArray(points[0])\n    ? (points as T[])\n    : (points as K[]).map(({ x, y, pressure = DEFAULT_PRESSURE }) => [\n        x,\n        y,\n        pressure,\n      ])\n\n  // Add extra points between the two, to help avoid \"dash\" lines\n  // for strokes with tapered start and ends. Don't mutate the\n  // input array!\n  if (pts.length === 2) {\n    const last = pts[1]\n    pts = pts.slice(0, -1)\n    for (let i = 1; i < 5; i++) {\n      pts.push(lrp(pts[0] as Vec2, last as Vec2, i / 4))\n    }\n  }\n\n  // If there's only one point, add another point at a 1pt offset.\n  // Don't mutate the input array!\n  if (pts.length === 1) {\n    pts = [...pts, [...add(pts[0] as Vec2, UNIT_OFFSET), ...pts[0].slice(2)]]\n  }\n\n  // The strokePoints array will hold the points for the stroke.\n  // Start it out with the first point, which needs no adjustment.\n  const strokePoints: StrokePoint[] = [\n    {\n      point: [pts[0][0], pts[0][1]],\n      pressure: isValidPressure(pts[0][2]) ? pts[0][2] : DEFAULT_FIRST_PRESSURE,\n      vector: [...UNIT_OFFSET],\n      distance: 0,\n      runningLength: 0,\n    },\n  ]\n\n  // A flag to see whether we've already reached out minimum length\n  let hasReachedMinimumLength = false\n\n  // We use the runningLength to keep track of the total distance\n  let runningLength = 0\n\n  // We're set this to the latest point, so we can use it to calculate\n  // the distance and vector of the next point.\n  let prev = strokePoints[0]\n\n  const max = pts.length - 1\n\n  // Iterate through all of the points, creating StrokePoints.\n  for (let i = 1; i < pts.length; i++) {\n    const point: Vec2 =\n      isComplete && i === max\n        ? // If we're at the last point, and `options.last` is true,\n          // then add the actual input point.\n          [pts[i][0], pts[i][1]]\n        : // Otherwise, using the t calculated from the streamline\n          // option, interpolate a new point between the previous\n          // point the current point.\n          lrp(prev.point, pts[i] as Vec2, t)\n\n    // If the new point is the same as the previous point, skip ahead.\n    if (isEqual(prev.point, point)) continue\n\n    // How far is the new point from the previous point?\n    const distance = dist(point, prev.point)\n\n    // Add this distance to the total \"running length\" of the line.\n    runningLength += distance\n\n    // At the start of the line, we wait until the new point is a\n    // certain distance away from the original point, to avoid noise\n    if (i < max && !hasReachedMinimumLength) {\n      if (runningLength < size) continue\n      hasReachedMinimumLength = true\n      // TODO: Backfill the missing points so that tapering works correctly.\n    }\n    // Create a new strokepoint (it will be the new \"previous\" one).\n    // Use scratch buffer for vector difference to reduce allocations\n    subInto(_vectorDiff, prev.point, point)\n    prev = {\n      // The adjusted point\n      point,\n      // The input pressure (or default if not specified)\n      pressure: isValidPressure(pts[i][2]) ? pts[i][2] : DEFAULT_PRESSURE,\n      // The vector from the current point to the previous point\n      vector: uni(_vectorDiff),\n      // The distance between the current point and the previous point\n      distance,\n      // The total distance so far\n      runningLength,\n    }\n\n    // Push it to the strokePoints array.\n    strokePoints.push(prev)\n  }\n\n  // Set the vector of the first point to be the same as the second point.\n  strokePoints[0].vector = strokePoints[1]?.vector || [0, 0]\n\n  return strokePoints\n}\n","import type { StrokeOptions, Vec2 } from './types'\nimport { getStrokeOutlinePoints } from './getStrokeOutlinePoints'\nimport { getStrokePoints } from './getStrokePoints'\n\n/**\n * ## getStroke\n * @description Get an array of points describing a polygon that surrounds the input points.\n * @param points An array of points (as `[x, y, pressure]` or `{x, y, pressure}`). Pressure is optional in both cases.\n * @param options (optional) An object with options.\n * @param options.size\tThe base size (diameter) of the stroke.\n * @param options.thinning The effect of pressure on the stroke's size.\n * @param options.smoothing\tHow much to soften the stroke's edges.\n * @param options.easing\tAn easing function to apply to each point's pressure.\n * @param options.simulatePressure Whether to simulate pressure based on velocity.\n * @param options.start Cap, taper and easing for the start of the line.\n * @param options.end Cap, taper and easing for the end of the line.\n * @param options.last Whether to handle the points as a completed stroke.\n */\n\nexport function getStroke(\n  points: (number[] | { x: number; y: number; pressure?: number })[],\n  options: StrokeOptions = {} as StrokeOptions\n): Vec2[] {\n  return getStrokeOutlinePoints(getStrokePoints(points, options), options)\n}\n","import { getStroke } from './getStroke'\n\nexport default getStroke\n\nexport * from './getStrokeOutlinePoints'\nexport * from './getStrokePoints'\nexport * from './getStroke'\nexport * from './types'\n"],"mappings":"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"}