curvy 2.0.0-alpha.13 → 2.0.0-alpha.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/characteristic/index.mjs +1 -1
  2. package/dist/curve/cubic2d.d.ts +92 -3
  3. package/dist/curve/curve2d.d.ts +81 -0
  4. package/dist/curve/index.mjs +1 -1
  5. package/dist/curve/linear2d.d.ts +113 -3
  6. package/dist/curve/quadratic2d.d.ts +92 -3
  7. package/dist/curve/rationalCubic2d.d.ts +100 -38
  8. package/dist/interval/index.mjs +2 -2
  9. package/dist/matrix/index.mjs +47 -1
  10. package/dist/matrix/index.mjs.map +1 -1
  11. package/dist/matrix/matrix3x3.d.ts +87 -1
  12. package/dist/matrix/matrix4x4.d.ts +57 -6
  13. package/dist/matrix/traits.d.ts +34 -0
  14. package/dist/monotonicity/index.mjs +1 -1
  15. package/dist/monotonicity/monotonicity.d.ts +25 -0
  16. package/dist/number.d.ts +53 -0
  17. package/dist/number.mjs +56 -1
  18. package/dist/number.mjs.map +1 -1
  19. package/dist/path/cubic2d.d.ts +22 -0
  20. package/dist/path/index.mjs +25 -149
  21. package/dist/path/index.mjs.map +1 -1
  22. package/dist/path/linear2d.d.ts +24 -0
  23. package/dist/path/path2d.d.ts +54 -0
  24. package/dist/path/quadratic2d.d.ts +22 -0
  25. package/dist/path/rationalCubic2d.d.ts +21 -0
  26. package/dist/polynomial/index.mjs +2 -2
  27. package/dist/shared/affine2d-Dvna84gc.mjs +332 -0
  28. package/dist/shared/affine2d-Dvna84gc.mjs.map +1 -0
  29. package/dist/shared/{characteristic-BkEr-rr8.mjs → characteristic--7nC4Q_a.mjs} +4 -5
  30. package/dist/shared/characteristic--7nC4Q_a.mjs.map +1 -0
  31. package/dist/shared/{cubic-BL6dpuHh.mjs → cubic-BiGuAnZ0.mjs} +56 -27
  32. package/dist/shared/cubic-BiGuAnZ0.mjs.map +1 -0
  33. package/dist/shared/{interval-Dm35Xhy4.mjs → interval-Dcqpo73t.mjs} +5 -5
  34. package/dist/shared/interval-Dcqpo73t.mjs.map +1 -0
  35. package/dist/shared/{interval2d-AtneyI45.mjs → interval2d-C2Pk66-2.mjs} +3 -3
  36. package/dist/shared/{interval2d-AtneyI45.mjs.map → interval2d-C2Pk66-2.mjs.map} +1 -1
  37. package/dist/shared/matrix3x3.internal-Cod-r7H3.mjs +233 -0
  38. package/dist/shared/matrix3x3.internal-Cod-r7H3.mjs.map +1 -0
  39. package/dist/shared/{matrix4x4-BWmf4lZM.mjs → matrix4x4-BrqgYZCD.mjs} +72 -223
  40. package/dist/shared/matrix4x4-BrqgYZCD.mjs.map +1 -0
  41. package/dist/shared/monotonicity-DNxBoC7F.mjs +64 -0
  42. package/dist/shared/monotonicity-DNxBoC7F.mjs.map +1 -0
  43. package/dist/shared/{quadratic-DPrUsFCp.mjs → quadratic-BmMqkvxx.mjs} +2 -2
  44. package/dist/shared/{quadratic-DPrUsFCp.mjs.map → quadratic-BmMqkvxx.mjs.map} +1 -1
  45. package/dist/shared/{rationalCubic2d-H1J2Fjyy.mjs → rationalCubic2d-CZiP8nIq.mjs} +282 -326
  46. package/dist/shared/rationalCubic2d-CZiP8nIq.mjs.map +1 -0
  47. package/dist/shared/{rationalCubic2d-CeHgpNht.mjs → rationalCubic2d-gM7VeiiT.mjs} +585 -125
  48. package/dist/shared/rationalCubic2d-gM7VeiiT.mjs.map +1 -0
  49. package/dist/shared/{solution-B2JdOZpe.mjs → solution-BW0BBMXI.mjs} +22 -6
  50. package/dist/shared/solution-BW0BBMXI.mjs.map +1 -0
  51. package/dist/shared/{vector2-CSkaMXvp.mjs → vector2-DuA3EmfL.mjs} +5 -3
  52. package/dist/shared/vector2-DuA3EmfL.mjs.map +1 -0
  53. package/dist/shared/{vector2.internal-DP1nTBdS.mjs → vector2.internal-rPnW0RCx.mjs} +10 -5
  54. package/dist/shared/vector2.internal-rPnW0RCx.mjs.map +1 -0
  55. package/dist/shared/{vector3-CR2LPV03.mjs → vector3-oxtggkDp.mjs} +2 -2
  56. package/dist/shared/{vector3-CR2LPV03.mjs.map → vector3-oxtggkDp.mjs.map} +1 -1
  57. package/dist/shared/vector3.internal-CfWHl9SG.mjs +135 -0
  58. package/dist/shared/vector3.internal-CfWHl9SG.mjs.map +1 -0
  59. package/dist/shared/{vector4-PO_7EYd-.mjs → vector4-DGHuCvTV.mjs} +2 -2
  60. package/dist/shared/{vector4-PO_7EYd-.mjs.map → vector4-DGHuCvTV.mjs.map} +1 -1
  61. package/dist/shared/vector4.internal-DH5DzNYO.mjs +116 -0
  62. package/dist/shared/vector4.internal-DH5DzNYO.mjs.map +1 -0
  63. package/dist/solution/index.mjs +1 -1
  64. package/dist/solution/solution.d.ts +16 -0
  65. package/dist/splines/basis2d.d.ts +64 -0
  66. package/dist/splines/bezier2d.d.ts +72 -0
  67. package/dist/splines/cardinal2d.d.ts +78 -0
  68. package/dist/splines/hermite2d.d.ts +76 -0
  69. package/dist/splines/index.mjs +109 -15
  70. package/dist/splines/index.mjs.map +1 -1
  71. package/dist/splines/linear2d.d.ts +65 -0
  72. package/dist/splines/rationalBezier2d.d.ts +76 -0
  73. package/dist/transform/affine2d.d.ts +339 -0
  74. package/dist/transform/affine2d.internal.d.ts +2 -0
  75. package/dist/transform/index.d.ts +3 -0
  76. package/dist/transform/index.mjs +2 -0
  77. package/dist/transform/traits.d.ts +19 -0
  78. package/dist/vector/index.mjs +3 -3
  79. package/dist/vector/vector2.d.ts +49 -0
  80. package/package.json +5 -1
  81. package/dist/shared/characteristic-BkEr-rr8.mjs.map +0 -1
  82. package/dist/shared/cubic-BL6dpuHh.mjs.map +0 -1
  83. package/dist/shared/interval-Dm35Xhy4.mjs.map +0 -1
  84. package/dist/shared/matrix4x4-BWmf4lZM.mjs.map +0 -1
  85. package/dist/shared/monotonicity-BdebFOtv.mjs +0 -34
  86. package/dist/shared/monotonicity-BdebFOtv.mjs.map +0 -1
  87. package/dist/shared/rationalCubic2d-CeHgpNht.mjs.map +0 -1
  88. package/dist/shared/rationalCubic2d-H1J2Fjyy.mjs.map +0 -1
  89. package/dist/shared/solution-B2JdOZpe.mjs.map +0 -1
  90. package/dist/shared/vector2-CSkaMXvp.mjs.map +0 -1
  91. package/dist/shared/vector2.internal-DP1nTBdS.mjs.map +0 -1
  92. package/dist/shared/vector4.internal-BJ1QN2Ck.mjs +0 -246
  93. package/dist/shared/vector4.internal-BJ1QN2Ck.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"number.mjs","names":[],"sources":["../src/number.ts"],"sourcesContent":["import { dual } from './utils.ts'\n\nconst PRECISION = 8\n\n/**\n * Default tolerance for approximate equality between floating-point numbers.\n *\n * @since 1.1.0\n */\nexport const EPSILON = 1e-10\n\n/**\n * Approximate equality between two numbers, within an absolute tolerance.\n *\n * @param a - The first value.\n * @param b - The second value.\n * @param eps - The maximum allowed absolute difference. Defaults to {@link EPSILON}.\n * @returns `true` when `|a - b| <= eps`.\n * @since 1.1.0\n */\nexport function epsEquals(a: number, b: number, eps: number = EPSILON): boolean {\n return Math.abs(a - b) <= eps\n}\n\n/**\n * Snaps near-zero values to exactly zero for stable sign tests.\n *\n * @param value - The value to test.\n * @param eps - The threshold below which `value` is considered zero.\n * @returns `0` when `|value| < eps`, otherwise `value` unchanged.\n * @since 1.1.0\n */\nexport function clampToZero(value: number, eps: number): number {\n return Math.abs(value) < eps ? 0 : value\n}\n\n/**\n * Rounding function that rounds a number to a specified precision.\n *\n * @param value - The number to round.\n * @param precision - The number of decimal places to round to (default is 8).\n * @returns The rounded number.\n * @example\n * ```ts\n * import { round } from 'curvy/utils'\n *\n * const roundedValue = round(1.23456789, 4) // 1.2346\n * const roundedValue2 = round(1.23456789) // 1.23456789\n * ```\n * @since 1.0.0\n */\nexport function round(value: number, precision = PRECISION): number {\n const scale = 10 ** precision\n const result = Math.round(value * scale) / scale\n if (result === 0) {\n return 0\n }\n return result\n}\n\n/**\n * Rounding function that rounds a number down to a specified precision.\n *\n * @param value - The number to round down.\n * @param precision - The number of decimal places to round down to (default is 8).\n * @returns The rounded down number.\n * @example\n * ```ts\n * import { roundDown } from 'curvy/utils'\n *\n * const roundedDownValue = roundDown(1.23456789, 4) // 1.2345\n * const roundedDownValue2 = roundDown(1.23456789) // 1.23456789\n * ```\n * @since 1.0.0\n */\nexport function roundDown(value: number, precision = PRECISION): number {\n const scale = 10 ** precision\n const result = Math.floor(value * scale) / scale\n if (result === 0) {\n return 0\n }\n return result\n}\n\n/**\n * Rounding function that rounds a number up to a specified precision.\n *\n * @param value - The number to round up.\n * @param precision - The number of decimal places to round up to (default is 8).\n * @returns The rounded up number.\n * @example\n * ```ts\n * import { roundUp } from 'curvy/utils'\n *\n * const roundedUpValue = roundUp(1.23456789, 4) // 1.2346\n * const roundedUpValue2 = roundUp(1.23456789) // 1.23456789\n * ```\n * @since 1.0.0\n */\nexport function roundUp(value: number, precision = PRECISION): number {\n const scale = 10 ** precision\n const result = Math.ceil(value * scale) / scale\n if (result === 0) {\n return 0\n }\n return result\n}\n\nexport const lerp: {\n /**\n * Linearly interpolates between two values.\n *\n * @param t - The interpolation factor (between 0 and 1).\n * @param a - The start value.\n * @param b - The end value.\n * @returns The interpolated value.\n * @example\n * ```ts\n * import { lerp } from 'curvy/utils'\n *\n * lerp(0.5, 10, 20) // 15\n * ```\n * @since 1.0.0\n */\n (t: number, a: number, b: number): number\n /**\n * Linearly interpolates between two values.\n *\n * @param a - The start value.\n * @param b - The end value.\n * @returns A function that takes the interpolation factor and returns the interpolated value.\n * @example\n * ```ts\n * import { lerp } from 'curvy/utils'\n *\n * lerp(10, 20)(0.5) // 15\n * ```\n * @since 1.0.0\n */\n (a: number, b: number): (t: number) => number\n} = dual(3, (t: number, a: number, b: number) => b * t + a * (1 - t))\n\nexport const normalize: {\n /**\n * Normalizes a value between two bounds to the unit interval.\n *\n * @param x - The value to normalize.\n * @param a - The lower bound.\n * @param b - The upper bound.\n * @returns The normalized value.\n * @example\n * ```ts\n * import { normalize } from 'curvy/utils'\n *\n * normalize(15, 10, 20) // 0.5\n * ```\n * @since 1.0.0\n */\n (x: number, a: number, b: number): number\n /**\n * Normalizes a value between two bounds to the unit interval.\n *\n * @param a - The lower bound.\n * @param b - The upper bound.\n * @returns A function that takes a value and returns the normalized value.\n * @example\n * ```ts\n * import { normalize } from 'curvy/utils'\n *\n * normalize(10, 20)(15) // 0.5\n * ```\n * @since 1.0.0\n */\n (a: number, b: number): (x: number) => number\n} = dual(3, (x: number, a: number, b: number) => (x - a) / (b - a))\n\nexport const remap: {\n /**\n * Remaps a value from one range to another.\n *\n * @param x - The value to remap.\n * @param x1 - The lower bound of the original range.\n * @param x2 - The upper bound of the original range.\n * @param y1 - The lower bound of the new range.\n * @param y2 - The upper bound of the new range.\n * @returns The remapped value.\n * @example\n * ```ts\n * import { remap } from 'curvy/utils'\n *\n * remap(15, 10, 20, 0, 100) // 50\n * ```\n * @since 1.0.0\n */\n (x: number, x1: number, x2: number, y1: number, y2: number): number\n /**\n * Remaps a value from one range to another.\n *\n * @param x1 - The lower bound of the original range.\n * @param x2 - The upper bound of the original range.\n * @param y1 - The lower bound of the new range.\n * @param y2 - The upper bound of the new range.\n * @returns A function that takes a value and returns the remapped value.\n * @example\n * ```ts\n * import { remap } from 'curvy/utils'\n *\n * remap(10, 20, 0, 100)(15) // 50\n * ```\n * @since 1.0.0\n */\n (x1: number, x2: number, y1: number, y2: number): (x: number) => number\n} = dual(\n 5,\n (x: number, x1: number, x2: number, y1: number, y2: number) =>\n y1 + ((x - x1) / (x2 - x1)) * (y2 - y1),\n)\n\nexport const clamp: {\n /**\n * Clamps a value between two bounds.\n *\n * @param value - The value to clamp.\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @returns The clamped value.\n * @example\n * ```ts\n * import { clamp } from 'curvy/utils'\n *\n * clamp(25, 10, 20) // 20\n * ```\n * @since 1.0.0\n */\n (value: number, min: number, max: number): number\n /**\n * Clamps a value between two bounds.\n *\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @returns A function that takes a value and returns the clamped value.\n * @example\n * ```ts\n * import { clamp } from 'curvy/utils'\n *\n * clamp(10, 20)(25) // 20\n * ```\n * @since 1.0.0\n */\n (min: number, max: number): (value: number) => number\n} = dual(3, (value: number, min: number, max: number) =>\n value < min ? min : value > max ? max : value,\n)\n\nexport const clip: {\n /**\n * Clips a value to a specified range, returning a fallback value if the value is outside the range.\n *\n * @param value - The value to clip.\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @param onClip - The fallback value to return if the value is outside the range.\n * @returns The original value if within range, otherwise the fallback.\n * @example\n * ```ts\n * import { clip } from 'curvy/utils'\n *\n * clip(25, 10, 20, 15) // 15\n * ```\n * @since 1.0.0\n */\n <T>(value: number, min: number, max: number, onClip: T): T | number\n /**\n * Clips a value to a specified range, returning a fallback value if the value is outside the range.\n *\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @param onClip - The fallback value to return if the value is outside the range.\n * @returns A function that takes a value and returns it if within range, otherwise the fallback.\n * @example\n * ```ts\n * import { clip } from 'curvy/utils'\n *\n * clip(10, 20, 15)(25) // 15\n * ```\n * @since 1.0.0\n */\n <T>(min: number, max: number, onClip: T): (value: number) => T | number\n} = dual(4, (value: number, min: number, max: number, onClip: unknown) =>\n value < min ? onClip : value > max ? onClip : value,\n)\n\n/**\n * Returns the minimum and maximum of two numbers.\n *\n * @param a - The first number.\n * @param b - The second number.\n * @returns A tuple containing the minimum and maximum values.\n * @example\n * ```ts\n * import { minMax } from 'curvy/utils'\n *\n * const [minValue, maxValue] = minMax(10, 20) // [10, 20]\n * const [minValue2, maxValue2] = minMax(20, 10) // [10, 20]\n * ```\n * @since 1.0.0\n */\nexport function minMax(a: number, b: number): [number, number] {\n return a < b ? [a, b] : [b, a]\n}\n\n/**\n * Calculates the modulus of a number.\n *\n * @param n - The number to calculate the modulus of.\n * @param m - The modulus.\n * @returns The modulus of the number.\n * @example\n * ```ts\n * import { mod } from 'curvy/utils'\n *\n * const modulus = mod(10, 3) // 1\n * const modulus2 = mod(-10, 3) // 2\n * ```\n * @since 1.0.0\n */\nexport function mod(n: number, m: number): number {\n return ((n % m) + m) % m\n}\n"],"mappings":";;AAEA,MAAM,YAAY;;;;;;AAOlB,MAAa,UAAU;;;;;;;;;;AAWvB,SAAgB,UAAU,GAAW,GAAW,MAAc,SAAkB;CAC9E,OAAO,KAAK,IAAI,IAAI,EAAE,IAAI;;;;;;;;;;AAW5B,SAAgB,YAAY,OAAe,KAAqB;CAC9D,OAAO,KAAK,IAAI,MAAM,GAAG,MAAM,IAAI;;;;;;;;;;;;;;;;;AAkBrC,SAAgB,MAAM,OAAe,YAAY,WAAmB;CAClE,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,KAAK,MAAM,QAAQ,MAAM,GAAG;CAC3C,IAAI,WAAW,GACb,OAAO;CAET,OAAO;;;;;;;;;;;;;;;;;AAkBT,SAAgB,UAAU,OAAe,YAAY,WAAmB;CACtE,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,KAAK,MAAM,QAAQ,MAAM,GAAG;CAC3C,IAAI,WAAW,GACb,OAAO;CAET,OAAO;;;;;;;;;;;;;;;;;AAkBT,SAAgB,QAAQ,OAAe,YAAY,WAAmB;CACpE,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,KAAK,KAAK,QAAQ,MAAM,GAAG;CAC1C,IAAI,WAAW,GACb,OAAO;CAET,OAAO;;AAGT,MAAa,OAgCT,KAAK,IAAI,GAAW,GAAW,MAAc,IAAI,IAAI,KAAK,IAAI,GAAG;AAErE,MAAa,YAgCT,KAAK,IAAI,GAAW,GAAW,OAAe,IAAI,MAAM,IAAI,GAAG;AAEnE,MAAa,QAoCT,KACF,IACC,GAAW,IAAY,IAAY,IAAY,OAC9C,MAAO,IAAI,OAAO,KAAK,OAAQ,KAAK,IACvC;AAED,MAAa,QAgCT,KAAK,IAAI,OAAe,KAAa,QACvC,QAAQ,MAAM,MAAM,QAAQ,MAAM,MAAM,MACzC;AAED,MAAa,OAkCT,KAAK,IAAI,OAAe,KAAa,KAAa,WACpD,QAAQ,MAAM,SAAS,QAAQ,MAAM,SAAS,MAC/C;;;;;;;;;;;;;;;;AAiBD,SAAgB,OAAO,GAAW,GAA6B;CAC7D,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE;;;;;;;;;;;;;;;;;AAkBhC,SAAgB,IAAI,GAAW,GAAmB;CAChD,QAAS,IAAI,IAAK,KAAK"}
1
+ {"version":3,"file":"number.mjs","names":[],"sources":["../src/number.ts"],"sourcesContent":["import { dual } from './utils.ts'\n\nconst PRECISION = 8\n\n/**\n * Default tolerance for approximate equality between floating-point numbers.\n *\n * @since 1.1.0\n */\nexport const EPSILON = 1e-10\n\n/**\n * Default relative tolerance for sign and degeneracy decisions about\n * *computed* quantities — derivative values, determinants, discriminants.\n *\n * Scale-free: multiply by the natural magnitude of the quantity under test\n * (e.g. the largest derivative value over the interval) to get an absolute\n * threshold. Values inside the resulting band are indistinguishable from\n * accumulated rounding noise and must not decide a sign question.\n *\n * The value sits ~4,500× above IEEE double rounding noise (2⁻⁵² ≈ 2.2e-16\n * relative per operation) — generous headroom for noise accumulated across a\n * construction pipeline — and far below any geometrically meaningful feature.\n *\n * @since 2.0.0\n */\nexport const RELATIVE_TOLERANCE = 1e-12\n\n/**\n * Approximate equality between two numbers, within an absolute tolerance.\n *\n * This is the raw absolute comparison — the right tool when the domain is\n * normalized by construction (curve parameter space, where `[0, 1]` makes an\n * absolute band meaningful) or when a caller-chosen absolute band is the\n * actual question. For identity questions about computed values of arbitrary\n * magnitude (\"is this the same point / value?\"), use {@link coincident},\n * which adds a magnitude-relative term.\n *\n * @param a - The first value.\n * @param b - The second value.\n * @param eps - The maximum allowed absolute difference. Defaults to {@link EPSILON}.\n * @returns `true` when `|a - b| <= eps`.\n * @since 1.1.0\n */\nexport function epsEquals(a: number, b: number, eps: number = EPSILON): boolean {\n return Math.abs(a - b) <= eps\n}\n\n/**\n * Library-policy approximate equality: `true` when\n * `|a - b| <= absolute + relative * max(|a|, |b|)`.\n *\n * The relative term covers rounding noise accumulated by arithmetic, which\n * grows with the magnitude of the values; the absolute term covers the\n * neighborhood of zero, where relative comparison degenerates (no nonzero\n * value is relatively close to `0`). The band is strictly wider than the\n * plain absolute comparison, never narrower.\n *\n * Defaults: `absolute = ` {@link EPSILON} — a modeling floor that assumes\n * coordinates of order ~1; pass an explicit value when your data has a\n * different natural resolution — and `relative = ` {@link RELATIVE_TOLERANCE}.\n *\n * Not transitive: `coincident(a, b)` and `coincident(b, c)` do not imply\n * `coincident(a, c)` — chains of pairwise-coincident values can drift beyond\n * the band. Pairwise semantics are intentional (e.g. path continuity checks\n * each junction locally).\n *\n * The tolerance parameters are positional, mirroring {@link epsEquals}: the\n * common override is `absolute` alone, for a domain with a known resolution.\n *\n * @param a - The first value.\n * @param b - The second value.\n * @param absolute - Absolute floor of the band. Defaults to {@link EPSILON}.\n * @param relative - Magnitude-proportional term of the band. Defaults to {@link RELATIVE_TOLERANCE}.\n * @returns `true` when the values are equal to within the combined band.\n * @since 2.0.0\n */\nexport function coincident(\n a: number,\n b: number,\n absolute: number = EPSILON,\n relative: number = RELATIVE_TOLERANCE,\n): boolean {\n return Math.abs(a - b) <= absolute + relative * Math.max(Math.abs(a), Math.abs(b))\n}\n\n/**\n * Snaps near-zero values to exactly zero for stable sign tests.\n *\n * @param value - The value to test.\n * @param eps - The threshold below which `value` is considered zero.\n * @returns `0` when `|value| < eps`, otherwise `value` unchanged.\n * @since 1.1.0\n */\nexport function clampToZero(value: number, eps: number): number {\n return Math.abs(value) < eps ? 0 : value\n}\n\n/**\n * Rounding function that rounds a number to a specified precision.\n *\n * @param value - The number to round.\n * @param precision - The number of decimal places to round to (default is 8).\n * @returns The rounded number.\n * @example\n * ```ts\n * import { round } from 'curvy/utils'\n *\n * const roundedValue = round(1.23456789, 4) // 1.2346\n * const roundedValue2 = round(1.23456789) // 1.23456789\n * ```\n * @since 1.0.0\n */\nexport function round(value: number, precision = PRECISION): number {\n const scale = 10 ** precision\n const result = Math.round(value * scale) / scale\n if (result === 0) {\n return 0\n }\n return result\n}\n\n/**\n * Rounding function that rounds a number down to a specified precision.\n *\n * @param value - The number to round down.\n * @param precision - The number of decimal places to round down to (default is 8).\n * @returns The rounded down number.\n * @example\n * ```ts\n * import { roundDown } from 'curvy/utils'\n *\n * const roundedDownValue = roundDown(1.23456789, 4) // 1.2345\n * const roundedDownValue2 = roundDown(1.23456789) // 1.23456789\n * ```\n * @since 1.0.0\n */\nexport function roundDown(value: number, precision = PRECISION): number {\n const scale = 10 ** precision\n const result = Math.floor(value * scale) / scale\n if (result === 0) {\n return 0\n }\n return result\n}\n\n/**\n * Rounding function that rounds a number up to a specified precision.\n *\n * @param value - The number to round up.\n * @param precision - The number of decimal places to round up to (default is 8).\n * @returns The rounded up number.\n * @example\n * ```ts\n * import { roundUp } from 'curvy/utils'\n *\n * const roundedUpValue = roundUp(1.23456789, 4) // 1.2346\n * const roundedUpValue2 = roundUp(1.23456789) // 1.23456789\n * ```\n * @since 1.0.0\n */\nexport function roundUp(value: number, precision = PRECISION): number {\n const scale = 10 ** precision\n const result = Math.ceil(value * scale) / scale\n if (result === 0) {\n return 0\n }\n return result\n}\n\nexport const lerp: {\n /**\n * Linearly interpolates between two values.\n *\n * @param t - The interpolation factor (between 0 and 1).\n * @param a - The start value.\n * @param b - The end value.\n * @returns The interpolated value.\n * @example\n * ```ts\n * import { lerp } from 'curvy/utils'\n *\n * lerp(0.5, 10, 20) // 15\n * ```\n * @since 1.0.0\n */\n (t: number, a: number, b: number): number\n /**\n * Linearly interpolates between two values.\n *\n * @param a - The start value.\n * @param b - The end value.\n * @returns A function that takes the interpolation factor and returns the interpolated value.\n * @example\n * ```ts\n * import { lerp } from 'curvy/utils'\n *\n * lerp(10, 20)(0.5) // 15\n * ```\n * @since 1.0.0\n */\n (a: number, b: number): (t: number) => number\n} = dual(3, (t: number, a: number, b: number) => b * t + a * (1 - t))\n\nexport const normalize: {\n /**\n * Normalizes a value between two bounds to the unit interval.\n *\n * @param x - The value to normalize.\n * @param a - The lower bound.\n * @param b - The upper bound.\n * @returns The normalized value.\n * @example\n * ```ts\n * import { normalize } from 'curvy/utils'\n *\n * normalize(15, 10, 20) // 0.5\n * ```\n * @since 1.0.0\n */\n (x: number, a: number, b: number): number\n /**\n * Normalizes a value between two bounds to the unit interval.\n *\n * @param a - The lower bound.\n * @param b - The upper bound.\n * @returns A function that takes a value and returns the normalized value.\n * @example\n * ```ts\n * import { normalize } from 'curvy/utils'\n *\n * normalize(10, 20)(15) // 0.5\n * ```\n * @since 1.0.0\n */\n (a: number, b: number): (x: number) => number\n} = dual(3, (x: number, a: number, b: number) => (x - a) / (b - a))\n\nexport const remap: {\n /**\n * Remaps a value from one range to another.\n *\n * @param x - The value to remap.\n * @param x1 - The lower bound of the original range.\n * @param x2 - The upper bound of the original range.\n * @param y1 - The lower bound of the new range.\n * @param y2 - The upper bound of the new range.\n * @returns The remapped value.\n * @example\n * ```ts\n * import { remap } from 'curvy/utils'\n *\n * remap(15, 10, 20, 0, 100) // 50\n * ```\n * @since 1.0.0\n */\n (x: number, x1: number, x2: number, y1: number, y2: number): number\n /**\n * Remaps a value from one range to another.\n *\n * @param x1 - The lower bound of the original range.\n * @param x2 - The upper bound of the original range.\n * @param y1 - The lower bound of the new range.\n * @param y2 - The upper bound of the new range.\n * @returns A function that takes a value and returns the remapped value.\n * @example\n * ```ts\n * import { remap } from 'curvy/utils'\n *\n * remap(10, 20, 0, 100)(15) // 50\n * ```\n * @since 1.0.0\n */\n (x1: number, x2: number, y1: number, y2: number): (x: number) => number\n} = dual(\n 5,\n (x: number, x1: number, x2: number, y1: number, y2: number) =>\n y1 + ((x - x1) / (x2 - x1)) * (y2 - y1),\n)\n\nexport const clamp: {\n /**\n * Clamps a value between two bounds.\n *\n * @param value - The value to clamp.\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @returns The clamped value.\n * @example\n * ```ts\n * import { clamp } from 'curvy/utils'\n *\n * clamp(25, 10, 20) // 20\n * ```\n * @since 1.0.0\n */\n (value: number, min: number, max: number): number\n /**\n * Clamps a value between two bounds.\n *\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @returns A function that takes a value and returns the clamped value.\n * @example\n * ```ts\n * import { clamp } from 'curvy/utils'\n *\n * clamp(10, 20)(25) // 20\n * ```\n * @since 1.0.0\n */\n (min: number, max: number): (value: number) => number\n} = dual(3, (value: number, min: number, max: number) =>\n value < min ? min : value > max ? max : value,\n)\n\nexport const clip: {\n /**\n * Clips a value to a specified range, returning a fallback value if the value is outside the range.\n *\n * @param value - The value to clip.\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @param onClip - The fallback value to return if the value is outside the range.\n * @returns The original value if within range, otherwise the fallback.\n * @example\n * ```ts\n * import { clip } from 'curvy/utils'\n *\n * clip(25, 10, 20, 15) // 15\n * ```\n * @since 1.0.0\n */\n <T>(value: number, min: number, max: number, onClip: T): T | number\n /**\n * Clips a value to a specified range, returning a fallback value if the value is outside the range.\n *\n * @param min - The lower bound.\n * @param max - The upper bound.\n * @param onClip - The fallback value to return if the value is outside the range.\n * @returns A function that takes a value and returns it if within range, otherwise the fallback.\n * @example\n * ```ts\n * import { clip } from 'curvy/utils'\n *\n * clip(10, 20, 15)(25) // 15\n * ```\n * @since 1.0.0\n */\n <T>(min: number, max: number, onClip: T): (value: number) => T | number\n} = dual(4, (value: number, min: number, max: number, onClip: unknown) =>\n value < min ? onClip : value > max ? onClip : value,\n)\n\n/**\n * Returns the minimum and maximum of two numbers.\n *\n * @param a - The first number.\n * @param b - The second number.\n * @returns A tuple containing the minimum and maximum values.\n * @example\n * ```ts\n * import { minMax } from 'curvy/utils'\n *\n * const [minValue, maxValue] = minMax(10, 20) // [10, 20]\n * const [minValue2, maxValue2] = minMax(20, 10) // [10, 20]\n * ```\n * @since 1.0.0\n */\nexport function minMax(a: number, b: number): [number, number] {\n return a < b ? [a, b] : [b, a]\n}\n\n/**\n * Calculates the modulus of a number.\n *\n * @param n - The number to calculate the modulus of.\n * @param m - The modulus.\n * @returns The modulus of the number.\n * @example\n * ```ts\n * import { mod } from 'curvy/utils'\n *\n * const modulus = mod(10, 3) // 1\n * const modulus2 = mod(-10, 3) // 2\n * ```\n * @since 1.0.0\n */\nexport function mod(n: number, m: number): number {\n return ((n % m) + m) % m\n}\n"],"mappings":";;AAEA,MAAM,YAAY;;;;;;AAOlB,MAAa,UAAU;;;;;;;;;;;;;;;;AAiBvB,MAAa,qBAAqB;;;;;;;;;;;;;;;;;AAkBlC,SAAgB,UAAU,GAAW,GAAW,MAAc,SAAkB;CAC9E,OAAO,KAAK,IAAI,IAAI,EAAE,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC5B,SAAgB,WACd,GACA,GACA,WAAmB,SACnB,WAAmB,oBACV;CACT,OAAO,KAAK,IAAI,IAAI,EAAE,IAAI,WAAW,WAAW,KAAK,IAAI,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE,CAAC;;;;;;;;;;AAWpF,SAAgB,YAAY,OAAe,KAAqB;CAC9D,OAAO,KAAK,IAAI,MAAM,GAAG,MAAM,IAAI;;;;;;;;;;;;;;;;;AAkBrC,SAAgB,MAAM,OAAe,YAAY,WAAmB;CAClE,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,KAAK,MAAM,QAAQ,MAAM,GAAG;CAC3C,IAAI,WAAW,GACb,OAAO;CAET,OAAO;;;;;;;;;;;;;;;;;AAkBT,SAAgB,UAAU,OAAe,YAAY,WAAmB;CACtE,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,KAAK,MAAM,QAAQ,MAAM,GAAG;CAC3C,IAAI,WAAW,GACb,OAAO;CAET,OAAO;;;;;;;;;;;;;;;;;AAkBT,SAAgB,QAAQ,OAAe,YAAY,WAAmB;CACpE,MAAM,QAAQ,MAAM;CACpB,MAAM,SAAS,KAAK,KAAK,QAAQ,MAAM,GAAG;CAC1C,IAAI,WAAW,GACb,OAAO;CAET,OAAO;;AAGT,MAAa,OAgCT,KAAK,IAAI,GAAW,GAAW,MAAc,IAAI,IAAI,KAAK,IAAI,GAAG;AAErE,MAAa,YAgCT,KAAK,IAAI,GAAW,GAAW,OAAe,IAAI,MAAM,IAAI,GAAG;AAEnE,MAAa,QAoCT,KACF,IACC,GAAW,IAAY,IAAY,IAAY,OAC9C,MAAO,IAAI,OAAO,KAAK,OAAQ,KAAK,IACvC;AAED,MAAa,QAgCT,KAAK,IAAI,OAAe,KAAa,QACvC,QAAQ,MAAM,MAAM,QAAQ,MAAM,MAAM,MACzC;AAED,MAAa,OAkCT,KAAK,IAAI,OAAe,KAAa,KAAa,WACpD,QAAQ,MAAM,SAAS,QAAQ,MAAM,SAAS,MAC/C;;;;;;;;;;;;;;;;AAiBD,SAAgB,OAAO,GAAW,GAA6B;CAC7D,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE;;;;;;;;;;;;;;;;;AAkBhC,SAAgB,IAAI,GAAW,GAAmB;CAChD,QAAS,IAAI,IAAK,KAAK"}
@@ -2,6 +2,7 @@ import type { Interval2d } from '../interval/interval2d.ts';
2
2
  import type { CubicCurve2d } from '../curve/cubic2d.ts';
3
3
  import type { Closed } from '../interval/interval.ts';
4
4
  import type * as Solution from '../solution/solution.ts';
5
+ import type { Affine2d } from '../transform/affine2d.ts';
5
6
  import type { Pipeable } from '../utils.ts';
6
7
  import type { Vector2 } from '../vector/vector2.ts';
7
8
  import type { CubicPath2dTypeId } from './cubic2d.internal.ts';
@@ -260,3 +261,24 @@ export declare const solveByDistance: {
260
261
  * @since 2.0.0
261
262
  */
262
263
  export declare const boundingBox: (p: CubicPath2d) => Interval2d<Closed, Closed>;
264
+ export declare const transform: {
265
+ /**
266
+ * Applies an `Affine2d` transform to every curve in a `CubicPath2d`,
267
+ * returning a new path whose image is the affine image of the original.
268
+ * Trait brands are dropped.
269
+ *
270
+ * @param p - The cubic path.
271
+ * @param a - The affine transform.
272
+ * @returns A new unbranded `CubicPath2d` whose curves are the affine images of the input's.
273
+ * @since 2.0.0
274
+ */
275
+ (p: CubicPath2d, a: Affine2d): CubicPath2d;
276
+ /**
277
+ * Applies an `Affine2d` transform to every curve in a `CubicPath2d`.
278
+ *
279
+ * @param a - The affine transform.
280
+ * @returns A function that takes a `CubicPath2d` and returns the transformed path.
281
+ * @since 2.0.0
282
+ */
283
+ (a: Affine2d): (p: CubicPath2d) => CubicPath2d;
284
+ };
@@ -1,138 +1,43 @@
1
1
  import { t as __exportAll } from "../shared/chunk-25Hzd3z8.mjs";
2
- import { Pipeable, dual, invariant } from "../utils.mjs";
3
- import { epsEquals } from "../number.mjs";
4
- import { p as unit } from "../shared/interval-Dm35Xhy4.mjs";
5
- import { o as none, r as isNone, s as one } from "../shared/solution-B2JdOZpe.mjs";
6
- import { n as isDecreasing, r as isIncreasing } from "../shared/quadratic-DPrUsFCp.mjs";
7
- import { a as union } from "../shared/interval2d-AtneyI45.mjs";
8
- import { b as solveAtY$2, g as length$2, h as boundingBox$2, v as solve$2, y as solveAtX$2 } from "../shared/rationalCubic2d-CeHgpNht.mjs";
9
- import { n as rationalCubic2d_exports, o as linear2d_exports, r as cubic2d_exports } from "../shared/rationalCubic2d-H1J2Fjyy.mjs";
2
+ import { invariant } from "../utils.mjs";
3
+ import { g as Ops } from "../shared/rationalCubic2d-gM7VeiiT.mjs";
4
+ import { n as rationalCubic2d_exports, o as linear2d_exports, r as cubic2d_exports, s as makeMethods } from "../shared/rationalCubic2d-CZiP8nIq.mjs";
10
5
  //#region src/path/quadratic2d.internal.ts
11
6
  const QuadraticPath2dTypeId = Symbol("curvy/path/quadratic2d");
12
- /** @internal */
13
- var QuadraticPath2dImpl = class extends Pipeable {
14
- [QuadraticPath2dTypeId] = QuadraticPath2dTypeId;
15
- curves;
16
- constructor(curves) {
17
- super();
18
- this.curves = curves;
19
- }
20
- [Symbol.iterator]() {
21
- return this.curves[Symbol.iterator]();
22
- }
23
- };
7
+ const methods = makeMethods(QuadraticPath2dTypeId, Ops);
24
8
  /** @internal */
25
9
  const isQuadraticPath2d$1 = (p) => typeof p === "object" && p !== null && QuadraticPath2dTypeId in p;
26
10
  /** @internal */
27
- const make$1 = (...curves) => new QuadraticPath2dImpl(curves);
11
+ const make$1 = methods.make;
28
12
  /** @internal */
29
- const fromArray$1 = (curves) => new QuadraticPath2dImpl(curves);
13
+ const fromArray$1 = methods.fromArray;
30
14
  /** @internal */
31
- const append$1 = dual(2, (p, c) => fromArray$1([...p, c]));
15
+ const append$1 = methods.append;
32
16
  /** @internal */
33
- const length$1 = (p) => {
34
- let total = 0;
35
- for (const curve of p) total += length$2(curve, unit);
36
- return total;
37
- };
17
+ const length$1 = methods.length;
38
18
  /** @internal */
39
- const solve$1 = dual(2, (p, u) => {
40
- const curves = p instanceof QuadraticPath2dImpl ? p.curves : [...p];
41
- if (u === 1) return solve$2(curves.at(-1), 1);
42
- const t = u * curves.length;
43
- const i = Math.floor(t);
44
- const curve = curves[i];
45
- return solve$2(curve, t - i);
46
- });
19
+ const solve$1 = methods.solve;
47
20
  /** @internal */
48
- const toPathData$1 = (p) => {
49
- let result = "";
50
- let prevEndX = NaN;
51
- let prevEndY = NaN;
52
- for (const curve of p) {
53
- const c0x = curve.x.c0;
54
- const c0y = curve.y.c0;
55
- const c1x = curve.x.c1;
56
- const c1y = curve.y.c1;
57
- const c2x = curve.x.c2;
58
- const c2y = curve.y.c2;
59
- const startX = c0x;
60
- const startY = c0y;
61
- const ctrlX = c0x + c1x / 2;
62
- const ctrlY = c0y + c1y / 2;
63
- const endX = c0x + c1x + c2x;
64
- const endY = c0y + c1y + c2y;
65
- if (!epsEquals(startX, prevEndX) || !epsEquals(startY, prevEndY)) result += ` M ${startX},${startY}`;
66
- result += ` Q ${ctrlX},${ctrlY} ${endX},${endY}`;
67
- prevEndX = endX;
68
- prevEndY = endY;
69
- }
70
- return result.slice(1);
71
- };
21
+ const toPathData$1 = methods.toPathData;
72
22
  /** @internal */
73
- const isContinuous$1 = (p) => {
74
- let prevEndX = NaN;
75
- let prevEndY = NaN;
76
- let first = true;
77
- for (const curve of p) {
78
- const startX = curve.x.c0;
79
- const startY = curve.y.c0;
80
- if (!first && (!epsEquals(startX, prevEndX) || !epsEquals(startY, prevEndY))) return false;
81
- prevEndX = curve.x.c0 + curve.x.c1 + curve.x.c2;
82
- prevEndY = curve.y.c0 + curve.y.c1 + curve.y.c2;
83
- first = false;
84
- }
85
- return true;
86
- };
23
+ const isContinuous$1 = methods.isContinuous;
87
24
  /** @internal */
88
25
  const asContinuous$1 = (p) => {
89
26
  invariant(isContinuous$1(p), "quadratic path is not continuous");
90
27
  return p;
91
28
  };
92
29
  /** @internal */
93
- const isIncreasingX$1 = (p) => {
94
- let prevEnd = Number.NEGATIVE_INFINITY;
95
- for (const c of p) {
96
- if (!isIncreasing(c.x, unit)) return false;
97
- if (c.x.c0 < prevEnd - 1e-10) return false;
98
- prevEnd = c.x.c0 + c.x.c1 + c.x.c2;
99
- }
100
- return true;
101
- };
30
+ const isIncreasingX$1 = methods.isIncreasingX;
102
31
  /** @internal */
103
- const isDecreasingX$1 = (p) => {
104
- let prevEnd = Number.POSITIVE_INFINITY;
105
- for (const c of p) {
106
- if (!isDecreasing(c.x, unit)) return false;
107
- if (c.x.c0 > prevEnd + 1e-10) return false;
108
- prevEnd = c.x.c0 + c.x.c1 + c.x.c2;
109
- }
110
- return true;
111
- };
32
+ const isDecreasingX$1 = methods.isDecreasingX;
112
33
  /** @internal */
113
- const isMonotonicX$1 = (p) => isIncreasingX$1(p) || isDecreasingX$1(p);
34
+ const isMonotonicX$1 = methods.isMonotonicX;
114
35
  /** @internal */
115
- const isIncreasingY$1 = (p) => {
116
- let prevEnd = Number.NEGATIVE_INFINITY;
117
- for (const c of p) {
118
- if (!isIncreasing(c.y, unit)) return false;
119
- if (c.y.c0 < prevEnd - 1e-10) return false;
120
- prevEnd = c.y.c0 + c.y.c1 + c.y.c2;
121
- }
122
- return true;
123
- };
36
+ const isIncreasingY$1 = methods.isIncreasingY;
124
37
  /** @internal */
125
- const isDecreasingY$1 = (p) => {
126
- let prevEnd = Number.POSITIVE_INFINITY;
127
- for (const c of p) {
128
- if (!isDecreasing(c.y, unit)) return false;
129
- if (c.y.c0 > prevEnd + 1e-10) return false;
130
- prevEnd = c.y.c0 + c.y.c1 + c.y.c2;
131
- }
132
- return true;
133
- };
38
+ const isDecreasingY$1 = methods.isDecreasingY;
134
39
  /** @internal */
135
- const isMonotonicY$1 = (p) => isIncreasingY$1(p) || isDecreasingY$1(p);
40
+ const isMonotonicY$1 = methods.isMonotonicY;
136
41
  /** @internal */
137
42
  const asIncreasingX$1 = (p) => {
138
43
  invariant(isIncreasingX$1(p), "quadratic path is not increasing in x");
@@ -164,44 +69,13 @@ const asMonotonicY$1 = (p) => {
164
69
  return p;
165
70
  };
166
71
  /** @internal */
167
- const solveAtX$1 = dual(2, (p, x) => {
168
- for (const c of p) {
169
- const sx = c.x.c0;
170
- const ex = c.x.c0 + c.x.c1 + c.x.c2;
171
- const lo = Math.min(sx, ex);
172
- const hi = Math.max(sx, ex);
173
- if (x >= lo - 1e-10 && x <= hi + 1e-10) {
174
- if (epsEquals(x, sx)) return one(c.y.c0);
175
- if (epsEquals(x, ex)) return one(c.y.c0 + c.y.c1 + c.y.c2);
176
- const sol = solveAtX$2(c, x);
177
- if (!isNone(sol)) return sol;
178
- }
179
- }
180
- return none;
181
- });
72
+ const solveAtX$1 = methods.solveAtX;
182
73
  /** @internal */
183
- const solveAtY$1 = dual(2, (p, y) => {
184
- for (const c of p) {
185
- const sy = c.y.c0;
186
- const ey = c.y.c0 + c.y.c1 + c.y.c2;
187
- const lo = Math.min(sy, ey);
188
- const hi = Math.max(sy, ey);
189
- if (y >= lo - 1e-10 && y <= hi + 1e-10) {
190
- if (epsEquals(y, sy)) return one(c.x.c0);
191
- if (epsEquals(y, ey)) return one(c.x.c0 + c.x.c1 + c.x.c2);
192
- const sol = solveAtY$2(c, y);
193
- if (!isNone(sol)) return sol;
194
- }
195
- }
196
- return none;
197
- });
74
+ const solveAtY$1 = methods.solveAtY;
198
75
  /** @internal */
199
- const boundingBox$1 = (p) => {
200
- const iter = p[Symbol.iterator]();
201
- let acc = boundingBox$2(iter.next().value);
202
- for (let next = iter.next(); !next.done; next = iter.next()) acc = union(acc, boundingBox$2(next.value));
203
- return acc;
204
- };
76
+ const boundingBox$1 = methods.boundingBox;
77
+ /** @internal */
78
+ const transform$1 = methods.transform;
205
79
  //#endregion
206
80
  //#region src/path/quadratic2d.ts
207
81
  var quadratic2d_exports = /* @__PURE__ */ __exportAll({
@@ -228,7 +102,8 @@ var quadratic2d_exports = /* @__PURE__ */ __exportAll({
228
102
  solve: () => solve,
229
103
  solveAtX: () => solveAtX,
230
104
  solveAtY: () => solveAtY,
231
- toPathData: () => toPathData
105
+ toPathData: () => toPathData,
106
+ transform: () => transform
232
107
  });
233
108
  /**
234
109
  * Creates a new `QuadraticPath2d` instance from a sequence of curves.
@@ -377,6 +252,7 @@ const solveAtY = solveAtY$1;
377
252
  * @since 2.0.0
378
253
  */
379
254
  const boundingBox = boundingBox$1;
255
+ const transform = transform$1;
380
256
  //#endregion
381
257
  export { cubic2d_exports as CubicPath2d, linear2d_exports as LinearPath2d, quadratic2d_exports as QuadraticPath2d, rationalCubic2d_exports as RationalCubicPath2d };
382
258
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["isQuadraticPath2d","make","fromArray","append","length","QuadraticCurve2d.length","Interval.unit","solve","QuadraticCurve2d.solve","toPathData","isContinuous","asContinuous","isIncreasingX","QuadraticPolynomial.isIncreasing","isDecreasingX","QuadraticPolynomial.isDecreasing","isMonotonicX","isIncreasingY","isDecreasingY","isMonotonicY","asIncreasingX","asDecreasingX","asMonotonicX","asIncreasingY","asDecreasingY","asMonotonicY","solveAtX","Solution.one","QuadraticCurve2d.solveAtX","Solution.isNone","Solution.none","solveAtY","QuadraticCurve2d.solveAtY","boundingBox","QuadraticCurve2d.boundingBox","Interval2d.union","internal.make","internal.fromArray","internal.isQuadraticPath2d","internal.append","internal.length","internal.solve","internal.toPathData","internal.isContinuous","internal.asContinuous","internal.isIncreasingX","internal.isDecreasingX","internal.isMonotonicX","internal.isIncreasingY","internal.isDecreasingY","internal.isMonotonicY","internal.asIncreasingX","internal.asDecreasingX","internal.asMonotonicX","internal.asIncreasingY","internal.asDecreasingY","internal.asMonotonicY","internal.solveAtX","internal.solveAtY","internal.boundingBox"],"sources":["../../src/path/quadratic2d.internal.ts","../../src/path/quadratic2d.ts"],"sourcesContent":["import * as Interval2d from '../interval/interval2d.ts'\nimport * as QuadraticCurve2d from '../curve/quadratic2d.ts'\nimport * as Interval from '../interval/interval.ts'\nimport * as QuadraticPolynomial from '../polynomial/quadratic.ts'\nimport * as Solution from '../solution/solution.ts'\nimport { dual, Pipeable } from '../utils.ts'\nimport { invariant } from '../utils.ts'\nimport { EPSILON, epsEquals } from '../number.ts'\nimport type { Vector2 } from '../vector/vector2.ts'\nimport type { QuadraticPath2d } from './quadratic2d.ts'\nimport {\n PathTraits,\n type Continuous,\n type DecreasingX,\n type DecreasingY,\n type IncreasingX,\n type IncreasingY,\n type MonotonicX,\n type MonotonicY,\n} from './traits.ts'\n\nexport const QuadraticPath2dTypeId: unique symbol = Symbol('curvy/path/quadratic2d')\nexport type QuadraticPath2dTypeId = typeof QuadraticPath2dTypeId\n\n/** @internal */\nexport class QuadraticPath2dImpl extends Pipeable implements QuadraticPath2d<unknown> {\n readonly [QuadraticPath2dTypeId]: QuadraticPath2dTypeId = QuadraticPath2dTypeId\n declare readonly [PathTraits]: unknown\n\n readonly curves: ReadonlyArray<QuadraticCurve2d.QuadraticCurve2d>\n\n constructor(curves: ReadonlyArray<QuadraticCurve2d.QuadraticCurve2d>) {\n super()\n this.curves = curves\n }\n\n [Symbol.iterator](): Iterator<QuadraticCurve2d.QuadraticCurve2d> {\n return this.curves[Symbol.iterator]()\n }\n}\n\n/** @internal */\nexport const isQuadraticPath2d = (p: unknown): p is QuadraticPath2d =>\n typeof p === 'object' && p !== null && QuadraticPath2dTypeId in p\n\n/** @internal */\nexport const make = (\n ...curves: ReadonlyArray<QuadraticCurve2d.QuadraticCurve2d>\n): QuadraticPath2d => new QuadraticPath2dImpl(curves)\n\n/** @internal */\nexport const fromArray = (\n curves: ReadonlyArray<QuadraticCurve2d.QuadraticCurve2d>,\n): QuadraticPath2d => new QuadraticPath2dImpl(curves)\n\n/** @internal */\nexport const append = dual<\n (c: QuadraticCurve2d.QuadraticCurve2d) => (p: QuadraticPath2d) => QuadraticPath2d,\n (p: QuadraticPath2d, c: QuadraticCurve2d.QuadraticCurve2d) => QuadraticPath2d\n>(2, (p: QuadraticPath2d, c: QuadraticCurve2d.QuadraticCurve2d) => fromArray([...p, c]))\n\n/** @internal */\nexport const length = (p: QuadraticPath2d) => {\n let total = 0\n for (const curve of p) {\n total += QuadraticCurve2d.length(curve, Interval.unit)\n }\n\n return total\n}\n\n/** @internal */\nexport const solve = dual<\n (u: number) => (p: QuadraticPath2d) => Vector2,\n (p: QuadraticPath2d, u: number) => Vector2\n>(2, (p: QuadraticPath2d, u: number) => {\n const curves = p instanceof QuadraticPath2dImpl ? p.curves : [...p]\n\n if (u === 1) {\n const last = curves.at(-1) as QuadraticCurve2d.QuadraticCurve2d\n return QuadraticCurve2d.solve(last, 1)\n }\n\n const t = u * curves.length\n const i = Math.floor(t)\n const curve = curves[i] as QuadraticCurve2d.QuadraticCurve2d\n return QuadraticCurve2d.solve(curve, t - i)\n})\n\n// Quadratic Bernstein basis: P(t) = (1-t)²·p0 + 2(1-t)t·p1 + t²·p2\n// expanded gives c0 = p0, c1 = -2p0 + 2p1, c2 = p0 - 2p1 + p2, so:\n// p0 = c0, p1 = c0 + c1/2, p2 = c0 + c1 + c2.\n/** @internal */\nexport const toPathData = (p: QuadraticPath2d): string => {\n let result = ''\n let prevEndX = Number.NaN\n let prevEndY = Number.NaN\n\n for (const curve of p) {\n const c0x = curve.x.c0\n const c0y = curve.y.c0\n const c1x = curve.x.c1\n const c1y = curve.y.c1\n const c2x = curve.x.c2\n const c2y = curve.y.c2\n\n const startX = c0x\n const startY = c0y\n const ctrlX = c0x + c1x / 2\n const ctrlY = c0y + c1y / 2\n const endX = c0x + c1x + c2x\n const endY = c0y + c1y + c2y\n\n if (!epsEquals(startX, prevEndX) || !epsEquals(startY, prevEndY)) {\n result += ` M ${startX},${startY}`\n }\n result += ` Q ${ctrlX},${ctrlY} ${endX},${endY}`\n\n prevEndX = endX\n prevEndY = endY\n }\n\n return result.slice(1)\n}\n\n/** @internal */\nexport const isContinuous = <T>(p: QuadraticPath2d<T>): p is QuadraticPath2d<T & Continuous> => {\n let prevEndX = Number.NaN\n let prevEndY = Number.NaN\n let first = true\n\n for (const curve of p) {\n const startX = curve.x.c0\n const startY = curve.y.c0\n if (!first && (!epsEquals(startX, prevEndX) || !epsEquals(startY, prevEndY))) {\n return false\n }\n prevEndX = curve.x.c0 + curve.x.c1 + curve.x.c2\n prevEndY = curve.y.c0 + curve.y.c1 + curve.y.c2\n first = false\n }\n return true\n}\n\n/** @internal */\nexport const asContinuous = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & Continuous> => {\n invariant(isContinuous(p), 'quadratic path is not continuous')\n return p\n}\n\n// Per-axis monotonicity for the path: every segment's axis polynomial must be\n// strictly monotonic over the unit interval AND adjacent segments' axis\n// ranges must not overlap (modulo EPSILON for float drift).\n/** @internal */\nexport const isIncreasingX = <T>(p: QuadraticPath2d<T>): p is QuadraticPath2d<T & IncreasingX> => {\n let prevEnd = Number.NEGATIVE_INFINITY\n for (const c of p) {\n if (!QuadraticPolynomial.isIncreasing(c.x, Interval.unit)) {\n return false\n }\n if (c.x.c0 < prevEnd - EPSILON) {\n return false\n }\n prevEnd = c.x.c0 + c.x.c1 + c.x.c2\n }\n return true\n}\n\n/** @internal */\nexport const isDecreasingX = <T>(p: QuadraticPath2d<T>): p is QuadraticPath2d<T & DecreasingX> => {\n let prevEnd = Number.POSITIVE_INFINITY\n for (const c of p) {\n if (!QuadraticPolynomial.isDecreasing(c.x, Interval.unit)) {\n return false\n }\n if (c.x.c0 > prevEnd + EPSILON) {\n return false\n }\n prevEnd = c.x.c0 + c.x.c1 + c.x.c2\n }\n return true\n}\n\n/** @internal */\nexport const isMonotonicX = <T>(p: QuadraticPath2d<T>): p is QuadraticPath2d<T & MonotonicX> =>\n isIncreasingX(p) || isDecreasingX(p)\n\n/** @internal */\nexport const isIncreasingY = <T>(p: QuadraticPath2d<T>): p is QuadraticPath2d<T & IncreasingY> => {\n let prevEnd = Number.NEGATIVE_INFINITY\n for (const c of p) {\n if (!QuadraticPolynomial.isIncreasing(c.y, Interval.unit)) {\n return false\n }\n if (c.y.c0 < prevEnd - EPSILON) {\n return false\n }\n prevEnd = c.y.c0 + c.y.c1 + c.y.c2\n }\n return true\n}\n\n/** @internal */\nexport const isDecreasingY = <T>(p: QuadraticPath2d<T>): p is QuadraticPath2d<T & DecreasingY> => {\n let prevEnd = Number.POSITIVE_INFINITY\n for (const c of p) {\n if (!QuadraticPolynomial.isDecreasing(c.y, Interval.unit)) {\n return false\n }\n if (c.y.c0 > prevEnd + EPSILON) {\n return false\n }\n prevEnd = c.y.c0 + c.y.c1 + c.y.c2\n }\n return true\n}\n\n/** @internal */\nexport const isMonotonicY = <T>(p: QuadraticPath2d<T>): p is QuadraticPath2d<T & MonotonicY> =>\n isIncreasingY(p) || isDecreasingY(p)\n\n/** @internal */\nexport const asIncreasingX = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & IncreasingX> => {\n invariant(isIncreasingX(p), 'quadratic path is not increasing in x')\n return p\n}\n\n/** @internal */\nexport const asDecreasingX = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & DecreasingX> => {\n invariant(isDecreasingX(p), 'quadratic path is not decreasing in x')\n return p\n}\n\n/** @internal */\nexport const asMonotonicX = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & MonotonicX> => {\n invariant(isMonotonicX(p), 'quadratic path is not monotonic in x')\n return p\n}\n\n/** @internal */\nexport const asIncreasingY = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & IncreasingY> => {\n invariant(isIncreasingY(p), 'quadratic path is not increasing in y')\n return p\n}\n\n/** @internal */\nexport const asDecreasingY = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & DecreasingY> => {\n invariant(isDecreasingY(p), 'quadratic path is not decreasing in y')\n return p\n}\n\n/** @internal */\nexport const asMonotonicY = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & MonotonicY> => {\n invariant(isMonotonicY(p), 'quadratic path is not monotonic in y')\n return p\n}\n\n// solveAtX/Y on a monotonic-axis path. Two defenses against float drift at\n// interior knots, where two adjacent segments both bracket the query and the\n// polynomial-inverse solver may return `none` for both:\n// 1. Snap to t=0/t=1 endpoint y when the query is within EPSILON of a\n// segment's start or end — bypasses polynomial inversion at knots.\n// 2. Fall through to the next bracketing segment on `none`. `MonotonicX/Y`\n// admits at most one real solution across the path, so retrying is safe.\n/** @internal */\nexport const solveAtX = dual(2, (p: QuadraticPath2d, x: number): Solution.AtMostOne<number> => {\n for (const c of p) {\n const sx = c.x.c0\n const ex = c.x.c0 + c.x.c1 + c.x.c2\n const lo = Math.min(sx, ex)\n const hi = Math.max(sx, ex)\n if (x >= lo - EPSILON && x <= hi + EPSILON) {\n if (epsEquals(x, sx)) {\n return Solution.one(c.y.c0)\n }\n if (epsEquals(x, ex)) {\n return Solution.one(c.y.c0 + c.y.c1 + c.y.c2)\n }\n const sol = QuadraticCurve2d.solveAtX(c, x) as Solution.AtMostOne<number>\n if (!Solution.isNone(sol)) {\n return sol\n }\n }\n }\n return Solution.none\n})\n\n/** @internal */\nexport const solveAtY = dual(2, (p: QuadraticPath2d, y: number): Solution.AtMostOne<number> => {\n for (const c of p) {\n const sy = c.y.c0\n const ey = c.y.c0 + c.y.c1 + c.y.c2\n const lo = Math.min(sy, ey)\n const hi = Math.max(sy, ey)\n if (y >= lo - EPSILON && y <= hi + EPSILON) {\n if (epsEquals(y, sy)) {\n return Solution.one(c.x.c0)\n }\n if (epsEquals(y, ey)) {\n return Solution.one(c.x.c0 + c.x.c1 + c.x.c2)\n }\n const sol = QuadraticCurve2d.solveAtY(c, y) as Solution.AtMostOne<number>\n if (!Solution.isNone(sol)) {\n return sol\n }\n }\n }\n return Solution.none\n})\n\n/** @internal */\nexport const boundingBox = (\n p: QuadraticPath2d,\n): Interval2d.Interval2d<Interval.Closed, Interval.Closed> => {\n const iter = p[Symbol.iterator]()\n const first = iter.next()\n let acc = QuadraticCurve2d.boundingBox(first.value as QuadraticCurve2d.QuadraticCurve2d)\n for (let next = iter.next(); !next.done; next = iter.next()) {\n acc = Interval2d.union(acc, QuadraticCurve2d.boundingBox(next.value))\n }\n return acc\n}\n","import type { Interval2d } from '../interval/interval2d.ts'\nimport type { QuadraticCurve2d } from '../curve/quadratic2d.ts'\nimport type { Closed } from '../interval/interval.ts'\nimport type * as Solution from '../solution/solution.ts'\nimport type { Pipeable } from '../utils.ts'\nimport type { Vector2 } from '../vector/vector2.ts'\nimport type { QuadraticPath2dTypeId } from './quadratic2d.internal.ts'\nimport * as internal from './quadratic2d.internal.ts'\nimport type {\n Continuous,\n DecreasingX,\n DecreasingY,\n IncreasingX,\n IncreasingY,\n MonotonicX,\n MonotonicY,\n PathTraits,\n} from './traits.ts'\n\nexport type {\n Continuous,\n DecreasingX,\n DecreasingY,\n IncreasingX,\n IncreasingY,\n MonotonicX,\n MonotonicY,\n} from './traits.ts'\n\n/**\n * A quadratic path in 2D space.\n *\n * All fields are readonly and immutable, and all operations create new instances.\n *\n * The `Trait` type parameter accumulates trait brands as the path is refined\n * via `isContinuous` / `asContinuous`.\n *\n * @since 1.0.0\n */\nexport interface QuadraticPath2d<out Trait = unknown> extends Pipeable, Iterable<QuadraticCurve2d> {\n readonly [QuadraticPath2dTypeId]: QuadraticPath2dTypeId\n readonly [PathTraits]: Trait\n}\n\n/**\n * Creates a new `QuadraticPath2d` instance from a sequence of curves.\n *\n * @param curves - The curves to create the path from.\n * @returns A new `QuadraticPath2d` instance.\n * @since 2.0.0\n */\nexport const make: (...curves: ReadonlyArray<QuadraticCurve2d>) => QuadraticPath2d = internal.make\n\n/**\n * Creates a new `QuadraticPath2d` instance from an array of curves.\n *\n * @param curves - The curves to create the path from.\n * @returns A new `QuadraticPath2d` instance.\n * @since 2.0.0\n */\nexport const fromArray: (curves: ReadonlyArray<QuadraticCurve2d>) => QuadraticPath2d =\n internal.fromArray\n\n/**\n * Checks if a value is a `QuadraticPath2d`.\n *\n * @param p - The value to check.\n * @returns `true` if the value is a `QuadraticPath2d`, `false` otherwise.\n * @since 1.0.0\n */\nexport const isQuadraticPath2d: (p: unknown) => p is QuadraticPath2d = internal.isQuadraticPath2d\n\nexport const append: {\n /**\n * Appends a quadratic curve to a quadratic path.\n *\n * @param c - The quadratic curve to append.\n * @returns A function that takes a quadratic path and returns a new quadratic path.\n * @since 1.0.0\n */\n (c: QuadraticCurve2d): (p: QuadraticPath2d) => QuadraticPath2d\n /**\n * Appends a quadratic curve to a quadratic path.\n *\n * @param p - The quadratic path to append to.\n * @param c - The quadratic curve to append.\n * @returns A new `QuadraticPath2d` instance with the appended curve.\n * @since 1.0.0\n */\n (p: QuadraticPath2d, c: QuadraticCurve2d): QuadraticPath2d\n} = internal.append\n\n/**\n * Calculates the length of a quadratic path.\n *\n * @param p - The quadratic path to calculate the length of.\n * @returns The length of the path.\n */\nexport const length: (p: QuadraticPath2d) => number = internal.length\n\nexport const solve: {\n /**\n * Solves a quadratic path at a given parameter.\n *\n * @param u - The parameter to solve for.\n * @returns A function that takes a quadratic path and returns the solved point.\n * @since 1.0.0\n */\n (u: number): (p: QuadraticPath2d) => Vector2\n /**\n * Solves a quadratic path at a given parameter.\n *\n * @param p - The quadratic path to solve.\n * @param u - The parameter to solve for.\n * @returns The solved point.\n * @since 1.0.0\n */\n (p: QuadraticPath2d, u: number): Vector2\n} = internal.solve\n\n/**\n * Serializes a quadratic path as an SVG path data string (the value of a\n * `<path>` element's `d` attribute), using `M` and `Q` commands. Discontinuities\n * between curves emit a fresh `M` command.\n *\n * @param p - The quadratic path to serialize.\n * @returns The SVG path data string.\n * @since 2.0.0\n */\nexport const toPathData: (p: QuadraticPath2d) => string = internal.toPathData\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & Continuous>` when adjacent curves connect.\n *\n * @since 2.0.0\n */\nexport const isContinuous: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & Continuous> =\n internal.isContinuous\n\n/**\n * Asserts that the quadratic path is continuous, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asContinuous: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & Continuous> =\n internal.asContinuous\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & IncreasingX>` when every segment's x-polynomial is\n * strictly increasing on `[0, 1]` and adjacent segments' x-ranges don't\n * overlap.\n *\n * @since 2.0.0\n */\nexport const isIncreasingX: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & IncreasingX> =\n internal.isIncreasingX\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & DecreasingX>`.\n *\n * @since 2.0.0\n */\nexport const isDecreasingX: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & DecreasingX> =\n internal.isDecreasingX\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & MonotonicX>`.\n *\n * @since 2.0.0\n */\nexport const isMonotonicX: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & MonotonicX> =\n internal.isMonotonicX\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & IncreasingY>`.\n *\n * @since 2.0.0\n */\nexport const isIncreasingY: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & IncreasingY> =\n internal.isIncreasingY\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & DecreasingY>`.\n *\n * @since 2.0.0\n */\nexport const isDecreasingY: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & DecreasingY> =\n internal.isDecreasingY\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & MonotonicY>`.\n *\n * @since 2.0.0\n */\nexport const isMonotonicY: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & MonotonicY> =\n internal.isMonotonicY\n\n/**\n * Asserts that the path is strictly increasing in x, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asIncreasingX: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & IncreasingX> =\n internal.asIncreasingX\n\n/**\n * Asserts that the path is strictly decreasing in x, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asDecreasingX: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & DecreasingX> =\n internal.asDecreasingX\n\n/**\n * Asserts that the path is monotonic in x, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asMonotonicX: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & MonotonicX> =\n internal.asMonotonicX\n\n/**\n * Asserts that the path is strictly increasing in y, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asIncreasingY: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & IncreasingY> =\n internal.asIncreasingY\n\n/**\n * Asserts that the path is strictly decreasing in y, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asDecreasingY: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & DecreasingY> =\n internal.asDecreasingY\n\n/**\n * Asserts that the path is monotonic in y, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asMonotonicY: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & MonotonicY> =\n internal.asMonotonicY\n\nexport const solveAtX: {\n /**\n * Evaluates the path's y value at a given x. Requires the path to carry\n * the `MonotonicX` brand. Returns `Solution.none` when x is outside the\n * path's x-range.\n *\n * @param p - A path branded `MonotonicX`.\n * @param x - The x coordinate.\n * @returns The y value at x, or `none` when x is outside the path's range.\n * @since 2.0.0\n */\n <T extends MonotonicX>(p: QuadraticPath2d<T>, x: number): Solution.AtMostOne<number>\n /** @since 2.0.0 */\n (x: number): <T extends MonotonicX>(p: QuadraticPath2d<T>) => Solution.AtMostOne<number>\n} = internal.solveAtX as never\n\nexport const solveAtY: {\n /**\n * Evaluates the path's x value at a given y. Requires the path to carry\n * the `MonotonicY` brand. Returns `Solution.none` when y is outside the\n * path's y-range.\n *\n * @param p - A path branded `MonotonicY`.\n * @param y - The y coordinate.\n * @returns The x value at y, or `none` when y is outside the path's range.\n * @since 2.0.0\n */\n <T extends MonotonicY>(p: QuadraticPath2d<T>, y: number): Solution.AtMostOne<number>\n /** @since 2.0.0 */\n (y: number): <T extends MonotonicY>(p: QuadraticPath2d<T>) => Solution.AtMostOne<number>\n} = internal.solveAtY as never\n\n/**\n * Computes the axis-aligned bounding box of the path — the smallest closed\n * `Box2d` enclosing every segment.\n *\n * @param p - The quadratic path.\n * @returns A closed `Box2d` enclosing the path.\n * @since 2.0.0\n */\nexport const boundingBox: (p: QuadraticPath2d) => Interval2d<Closed, Closed> = internal.boundingBox\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["Path2d.makeMethods","quadraticCurveInternal.Ops","isQuadraticPath2d","make","fromArray","append","length","solve","toPathData","isContinuous","asContinuous","isIncreasingX","isDecreasingX","isMonotonicX","isIncreasingY","isDecreasingY","isMonotonicY","asIncreasingX","asDecreasingX","asMonotonicX","asIncreasingY","asDecreasingY","asMonotonicY","solveAtX","solveAtY","boundingBox","transform","internal.make","internal.fromArray","internal.isQuadraticPath2d","internal.append","internal.length","internal.solve","internal.toPathData","internal.isContinuous","internal.asContinuous","internal.isIncreasingX","internal.isDecreasingX","internal.isMonotonicX","internal.isIncreasingY","internal.isDecreasingY","internal.isMonotonicY","internal.asIncreasingX","internal.asDecreasingX","internal.asMonotonicX","internal.asIncreasingY","internal.asDecreasingY","internal.asMonotonicY","internal.solveAtX","internal.solveAtY","internal.boundingBox","internal.transform"],"sources":["../../src/path/quadratic2d.internal.ts","../../src/path/quadratic2d.ts"],"sourcesContent":["import type * as QuadraticCurve2d from '../curve/quadratic2d.ts'\nimport * as quadraticCurveInternal from '../curve/quadratic2d.internal.ts'\nimport type { Closed } from '../interval/interval.ts'\nimport type { Interval2d } from '../interval/interval2d.ts'\nimport { invariant } from '../utils.ts'\nimport * as Path2d from './path2d.ts'\nimport type { QuadraticPath2d } from './quadratic2d.ts'\nimport {\n type Continuous,\n type DecreasingX,\n type DecreasingY,\n type IncreasingX,\n type IncreasingY,\n type MonotonicX,\n type MonotonicY,\n} from './traits.ts'\n\nexport const QuadraticPath2dTypeId: unique symbol = Symbol('curvy/path/quadratic2d')\nexport type QuadraticPath2dTypeId = typeof QuadraticPath2dTypeId\n\n// Generic operation surface, built once for QuadraticCurve2d's Ops.\nconst methods = Path2d.makeMethods(QuadraticPath2dTypeId, quadraticCurveInternal.Ops)\n\n/** @internal */\nexport const isQuadraticPath2d = (p: unknown): p is QuadraticPath2d =>\n typeof p === 'object' && p !== null && QuadraticPath2dTypeId in p\n\n/** @internal */\nexport const make = methods.make as (\n ...curves: ReadonlyArray<QuadraticCurve2d.QuadraticCurve2d>\n) => QuadraticPath2d\n\n/** @internal */\nexport const fromArray = methods.fromArray as (\n curves: ReadonlyArray<QuadraticCurve2d.QuadraticCurve2d>,\n) => QuadraticPath2d\n\n/** @internal */\nexport const append = methods.append as never\n\n/** @internal */\nexport const length = methods.length as (p: QuadraticPath2d) => number\n\n/** @internal */\nexport const solve = methods.solve as never\n\n/** @internal */\nexport const toPathData = methods.toPathData as (p: QuadraticPath2d) => string\n\n/** @internal */\nexport const isContinuous = methods.isContinuous as unknown as <T>(\n p: QuadraticPath2d<T>,\n) => p is QuadraticPath2d<T & Continuous>\n\n/** @internal */\nexport const asContinuous = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & Continuous> => {\n invariant(isContinuous(p), 'quadratic path is not continuous')\n return p\n}\n\n/** @internal */\nexport const isIncreasingX = methods.isIncreasingX as unknown as <T>(\n p: QuadraticPath2d<T>,\n) => p is QuadraticPath2d<T & IncreasingX>\n\n/** @internal */\nexport const isDecreasingX = methods.isDecreasingX as unknown as <T>(\n p: QuadraticPath2d<T>,\n) => p is QuadraticPath2d<T & DecreasingX>\n\n/** @internal */\nexport const isMonotonicX = methods.isMonotonicX as unknown as <T>(\n p: QuadraticPath2d<T>,\n) => p is QuadraticPath2d<T & MonotonicX>\n\n/** @internal */\nexport const isIncreasingY = methods.isIncreasingY as unknown as <T>(\n p: QuadraticPath2d<T>,\n) => p is QuadraticPath2d<T & IncreasingY>\n\n/** @internal */\nexport const isDecreasingY = methods.isDecreasingY as unknown as <T>(\n p: QuadraticPath2d<T>,\n) => p is QuadraticPath2d<T & DecreasingY>\n\n/** @internal */\nexport const isMonotonicY = methods.isMonotonicY as unknown as <T>(\n p: QuadraticPath2d<T>,\n) => p is QuadraticPath2d<T & MonotonicY>\n\n/** @internal */\nexport const asIncreasingX = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & IncreasingX> => {\n invariant(isIncreasingX(p), 'quadratic path is not increasing in x')\n return p\n}\n\n/** @internal */\nexport const asDecreasingX = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & DecreasingX> => {\n invariant(isDecreasingX(p), 'quadratic path is not decreasing in x')\n return p\n}\n\n/** @internal */\nexport const asMonotonicX = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & MonotonicX> => {\n invariant(isMonotonicX(p), 'quadratic path is not monotonic in x')\n return p\n}\n\n/** @internal */\nexport const asIncreasingY = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & IncreasingY> => {\n invariant(isIncreasingY(p), 'quadratic path is not increasing in y')\n return p\n}\n\n/** @internal */\nexport const asDecreasingY = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & DecreasingY> => {\n invariant(isDecreasingY(p), 'quadratic path is not decreasing in y')\n return p\n}\n\n/** @internal */\nexport const asMonotonicY = <T>(p: QuadraticPath2d<T>): QuadraticPath2d<T & MonotonicY> => {\n invariant(isMonotonicY(p), 'quadratic path is not monotonic in y')\n return p\n}\n\n/** @internal */\nexport const solveAtX = methods.solveAtX as never\n\n/** @internal */\nexport const solveAtY = methods.solveAtY as never\n\n/** @internal */\nexport const boundingBox = methods.boundingBox as (p: QuadraticPath2d) => Interval2d<Closed, Closed>\n\n/** @internal */\nexport const transform = methods.transform as never\n","import type { Interval2d } from '../interval/interval2d.ts'\nimport type { QuadraticCurve2d } from '../curve/quadratic2d.ts'\nimport type { Closed } from '../interval/interval.ts'\nimport type * as Solution from '../solution/solution.ts'\nimport type { Affine2d } from '../transform/affine2d.ts'\nimport type { Pipeable } from '../utils.ts'\nimport type { Vector2 } from '../vector/vector2.ts'\nimport type { QuadraticPath2dTypeId } from './quadratic2d.internal.ts'\nimport * as internal from './quadratic2d.internal.ts'\nimport type {\n Continuous,\n DecreasingX,\n DecreasingY,\n IncreasingX,\n IncreasingY,\n MonotonicX,\n MonotonicY,\n PathTraits,\n} from './traits.ts'\n\nexport type {\n Continuous,\n DecreasingX,\n DecreasingY,\n IncreasingX,\n IncreasingY,\n MonotonicX,\n MonotonicY,\n} from './traits.ts'\n\n/**\n * A quadratic path in 2D space.\n *\n * All fields are readonly and immutable, and all operations create new instances.\n *\n * The `Trait` type parameter accumulates trait brands as the path is refined\n * via `isContinuous` / `asContinuous`.\n *\n * @since 1.0.0\n */\nexport interface QuadraticPath2d<out Trait = unknown> extends Pipeable, Iterable<QuadraticCurve2d> {\n readonly [QuadraticPath2dTypeId]: QuadraticPath2dTypeId\n readonly [PathTraits]: Trait\n}\n\n/**\n * Creates a new `QuadraticPath2d` instance from a sequence of curves.\n *\n * @param curves - The curves to create the path from.\n * @returns A new `QuadraticPath2d` instance.\n * @since 2.0.0\n */\nexport const make: (...curves: ReadonlyArray<QuadraticCurve2d>) => QuadraticPath2d = internal.make\n\n/**\n * Creates a new `QuadraticPath2d` instance from an array of curves.\n *\n * @param curves - The curves to create the path from.\n * @returns A new `QuadraticPath2d` instance.\n * @since 2.0.0\n */\nexport const fromArray: (curves: ReadonlyArray<QuadraticCurve2d>) => QuadraticPath2d =\n internal.fromArray\n\n/**\n * Checks if a value is a `QuadraticPath2d`.\n *\n * @param p - The value to check.\n * @returns `true` if the value is a `QuadraticPath2d`, `false` otherwise.\n * @since 1.0.0\n */\nexport const isQuadraticPath2d: (p: unknown) => p is QuadraticPath2d = internal.isQuadraticPath2d\n\nexport const append: {\n /**\n * Appends a quadratic curve to a quadratic path.\n *\n * @param c - The quadratic curve to append.\n * @returns A function that takes a quadratic path and returns a new quadratic path.\n * @since 1.0.0\n */\n (c: QuadraticCurve2d): (p: QuadraticPath2d) => QuadraticPath2d\n /**\n * Appends a quadratic curve to a quadratic path.\n *\n * @param p - The quadratic path to append to.\n * @param c - The quadratic curve to append.\n * @returns A new `QuadraticPath2d` instance with the appended curve.\n * @since 1.0.0\n */\n (p: QuadraticPath2d, c: QuadraticCurve2d): QuadraticPath2d\n} = internal.append\n\n/**\n * Calculates the length of a quadratic path.\n *\n * @param p - The quadratic path to calculate the length of.\n * @returns The length of the path.\n */\nexport const length: (p: QuadraticPath2d) => number = internal.length\n\nexport const solve: {\n /**\n * Solves a quadratic path at a given parameter.\n *\n * @param u - The parameter to solve for.\n * @returns A function that takes a quadratic path and returns the solved point.\n * @since 1.0.0\n */\n (u: number): (p: QuadraticPath2d) => Vector2\n /**\n * Solves a quadratic path at a given parameter.\n *\n * @param p - The quadratic path to solve.\n * @param u - The parameter to solve for.\n * @returns The solved point.\n * @since 1.0.0\n */\n (p: QuadraticPath2d, u: number): Vector2\n} = internal.solve\n\n/**\n * Serializes a quadratic path as an SVG path data string (the value of a\n * `<path>` element's `d` attribute), using `M` and `Q` commands. Discontinuities\n * between curves emit a fresh `M` command.\n *\n * @param p - The quadratic path to serialize.\n * @returns The SVG path data string.\n * @since 2.0.0\n */\nexport const toPathData: (p: QuadraticPath2d) => string = internal.toPathData\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & Continuous>` when adjacent curves connect.\n *\n * @since 2.0.0\n */\nexport const isContinuous: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & Continuous> =\n internal.isContinuous\n\n/**\n * Asserts that the quadratic path is continuous, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asContinuous: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & Continuous> =\n internal.asContinuous\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & IncreasingX>` when every segment's x-polynomial is\n * strictly increasing on `[0, 1]` and adjacent segments' x-ranges don't\n * overlap.\n *\n * @since 2.0.0\n */\nexport const isIncreasingX: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & IncreasingX> =\n internal.isIncreasingX\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & DecreasingX>`.\n *\n * @since 2.0.0\n */\nexport const isDecreasingX: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & DecreasingX> =\n internal.isDecreasingX\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & MonotonicX>`.\n *\n * @since 2.0.0\n */\nexport const isMonotonicX: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & MonotonicX> =\n internal.isMonotonicX\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & IncreasingY>`.\n *\n * @since 2.0.0\n */\nexport const isIncreasingY: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & IncreasingY> =\n internal.isIncreasingY\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & DecreasingY>`.\n *\n * @since 2.0.0\n */\nexport const isDecreasingY: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & DecreasingY> =\n internal.isDecreasingY\n\n/**\n * Type-narrowing predicate: refines `QuadraticPath2d<T>` to\n * `QuadraticPath2d<T & MonotonicY>`.\n *\n * @since 2.0.0\n */\nexport const isMonotonicY: <T>(p: QuadraticPath2d<T>) => p is QuadraticPath2d<T & MonotonicY> =\n internal.isMonotonicY\n\n/**\n * Asserts that the path is strictly increasing in x, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asIncreasingX: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & IncreasingX> =\n internal.asIncreasingX\n\n/**\n * Asserts that the path is strictly decreasing in x, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asDecreasingX: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & DecreasingX> =\n internal.asDecreasingX\n\n/**\n * Asserts that the path is monotonic in x, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asMonotonicX: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & MonotonicX> =\n internal.asMonotonicX\n\n/**\n * Asserts that the path is strictly increasing in y, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asIncreasingY: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & IncreasingY> =\n internal.asIncreasingY\n\n/**\n * Asserts that the path is strictly decreasing in y, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asDecreasingY: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & DecreasingY> =\n internal.asDecreasingY\n\n/**\n * Asserts that the path is monotonic in y, throwing on failure.\n *\n * @since 2.0.0\n */\nexport const asMonotonicY: <T>(p: QuadraticPath2d<T>) => QuadraticPath2d<T & MonotonicY> =\n internal.asMonotonicY\n\nexport const solveAtX: {\n /**\n * Evaluates the path's y value at a given x. Requires the path to carry\n * the `MonotonicX` brand. Returns `Solution.none` when x is outside the\n * path's x-range.\n *\n * @param p - A path branded `MonotonicX`.\n * @param x - The x coordinate.\n * @returns The y value at x, or `none` when x is outside the path's range.\n * @since 2.0.0\n */\n <T extends MonotonicX>(p: QuadraticPath2d<T>, x: number): Solution.AtMostOne<number>\n /** @since 2.0.0 */\n (x: number): <T extends MonotonicX>(p: QuadraticPath2d<T>) => Solution.AtMostOne<number>\n} = internal.solveAtX as never\n\nexport const solveAtY: {\n /**\n * Evaluates the path's x value at a given y. Requires the path to carry\n * the `MonotonicY` brand. Returns `Solution.none` when y is outside the\n * path's y-range.\n *\n * @param p - A path branded `MonotonicY`.\n * @param y - The y coordinate.\n * @returns The x value at y, or `none` when y is outside the path's range.\n * @since 2.0.0\n */\n <T extends MonotonicY>(p: QuadraticPath2d<T>, y: number): Solution.AtMostOne<number>\n /** @since 2.0.0 */\n (y: number): <T extends MonotonicY>(p: QuadraticPath2d<T>) => Solution.AtMostOne<number>\n} = internal.solveAtY as never\n\n/**\n * Computes the axis-aligned bounding box of the path — the smallest closed\n * `Box2d` enclosing every segment.\n *\n * @param p - The quadratic path.\n * @returns A closed `Box2d` enclosing the path.\n * @since 2.0.0\n */\nexport const boundingBox: (p: QuadraticPath2d) => Interval2d<Closed, Closed> = internal.boundingBox\n\nexport const transform: {\n /**\n * Applies an `Affine2d` transform to every curve in a `QuadraticPath2d`,\n * returning a new path whose image is the affine image of the original.\n * Trait brands are dropped.\n *\n * @param p - The quadratic path.\n * @param a - The affine transform.\n * @returns A new unbranded `QuadraticPath2d` whose curves are the affine images of the input's.\n * @since 2.0.0\n */\n (p: QuadraticPath2d, a: Affine2d): QuadraticPath2d\n /**\n * Applies an `Affine2d` transform to every curve in a `QuadraticPath2d`.\n *\n * @param a - The affine transform.\n * @returns A function that takes a `QuadraticPath2d` and returns the transformed path.\n * @since 2.0.0\n */\n (a: Affine2d): (p: QuadraticPath2d) => QuadraticPath2d\n} = internal.transform\n"],"mappings":";;;;;AAiBA,MAAa,wBAAuC,OAAO,yBAAyB;AAIpF,MAAM,UAAUA,YAAmB,uBAAuBC,IAA2B;;AAGrF,MAAaC,uBAAqB,MAChC,OAAO,MAAM,YAAY,MAAM,QAAQ,yBAAyB;;AAGlE,MAAaC,SAAO,QAAQ;;AAK5B,MAAaC,cAAY,QAAQ;;AAKjC,MAAaC,WAAS,QAAQ;;AAG9B,MAAaC,WAAS,QAAQ;;AAG9B,MAAaC,UAAQ,QAAQ;;AAG7B,MAAaC,eAAa,QAAQ;;AAGlC,MAAaC,iBAAe,QAAQ;;AAKpC,MAAaC,kBAAmB,MAA2D;CACzF,UAAUD,eAAa,EAAE,EAAE,mCAAmC;CAC9D,OAAO;;;AAIT,MAAaE,kBAAgB,QAAQ;;AAKrC,MAAaC,kBAAgB,QAAQ;;AAKrC,MAAaC,iBAAe,QAAQ;;AAKpC,MAAaC,kBAAgB,QAAQ;;AAKrC,MAAaC,kBAAgB,QAAQ;;AAKrC,MAAaC,iBAAe,QAAQ;;AAKpC,MAAaC,mBAAoB,MAA4D;CAC3F,UAAUN,gBAAc,EAAE,EAAE,wCAAwC;CACpE,OAAO;;;AAIT,MAAaO,mBAAoB,MAA4D;CAC3F,UAAUN,gBAAc,EAAE,EAAE,wCAAwC;CACpE,OAAO;;;AAIT,MAAaO,kBAAmB,MAA2D;CACzF,UAAUN,eAAa,EAAE,EAAE,uCAAuC;CAClE,OAAO;;;AAIT,MAAaO,mBAAoB,MAA4D;CAC3F,UAAUN,gBAAc,EAAE,EAAE,wCAAwC;CACpE,OAAO;;;AAIT,MAAaO,mBAAoB,MAA4D;CAC3F,UAAUN,gBAAc,EAAE,EAAE,wCAAwC;CACpE,OAAO;;;AAIT,MAAaO,kBAAmB,MAA2D;CACzF,UAAUN,eAAa,EAAE,EAAE,uCAAuC;CAClE,OAAO;;;AAIT,MAAaO,aAAW,QAAQ;;AAGhC,MAAaC,aAAW,QAAQ;;AAGhC,MAAaC,gBAAc,QAAQ;;AAGnC,MAAaC,cAAY,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpFjC,MAAa,OAAwEC;;;;;;;;AASrF,MAAa,YACXC;;;;;;;;AASF,MAAa,oBAA0DC;AAEvE,MAAa,SAkBTC;;;;;;;AAQJ,MAAa,SAAyCC;AAEtD,MAAa,QAkBTC;;;;;;;;;;AAWJ,MAAa,aAA6CC;;;;;;;AAQ1D,MAAa,eACXC;;;;;;AAOF,MAAa,eACXC;;;;;;;;;AAUF,MAAa,gBACXC;;;;;;;AAQF,MAAa,gBACXC;;;;;;;AAQF,MAAa,eACXC;;;;;;;AAQF,MAAa,gBACXC;;;;;;;AAQF,MAAa,gBACXC;;;;;;;AAQF,MAAa,eACXC;;;;;;AAOF,MAAa,gBACXC;;;;;;AAOF,MAAa,gBACXC;;;;;;AAOF,MAAa,eACXC;;;;;;AAOF,MAAa,gBACXC;;;;;;AAOF,MAAa,gBACXC;;;;;;AAOF,MAAa,eACXC;AAEF,MAAa,WAcTC;AAEJ,MAAa,WAcTC;;;;;;;;;AAUJ,MAAa,cAAkEC;AAE/E,MAAa,YAoBTC"}
@@ -2,6 +2,7 @@ import type { Interval2d } from '../interval/interval2d.ts';
2
2
  import type { LinearCurve2d } from '../curve/linear2d.ts';
3
3
  import type { Closed } from '../interval/interval.ts';
4
4
  import type * as Solution from '../solution/solution.ts';
5
+ import type { Affine2d } from '../transform/affine2d.ts';
5
6
  import type { Pipeable } from '../utils.ts';
6
7
  import type { Vector2 } from '../vector/vector2.ts';
7
8
  import type { LinearPath2dTypeId } from './linear2d.internal.ts';
@@ -243,3 +244,26 @@ export declare const solveAtY: {
243
244
  * @since 2.0.0
244
245
  */
245
246
  export declare const boundingBox: (p: LinearPath2d) => Interval2d<Closed, Closed>;
247
+ export declare const transform: {
248
+ /**
249
+ * Applies an `Affine2d` transform to every curve in a `LinearPath2d`,
250
+ * returning a new path whose image is the affine image of the original.
251
+ * Trait brands are dropped — affine maps can flip the sense of monotonicity
252
+ * (a reflection turns `IncreasingX` into `DecreasingX`), so any brands the
253
+ * caller still wants must be reasserted on the result.
254
+ *
255
+ * @param p - The linear path.
256
+ * @param a - The affine transform.
257
+ * @returns A new unbranded `LinearPath2d` whose curves are the affine images of the input's.
258
+ * @since 2.0.0
259
+ */
260
+ (p: LinearPath2d, a: Affine2d): LinearPath2d;
261
+ /**
262
+ * Applies an `Affine2d` transform to every curve in a `LinearPath2d`.
263
+ *
264
+ * @param a - The affine transform.
265
+ * @returns A function that takes a `LinearPath2d` and returns the transformed path.
266
+ * @since 2.0.0
267
+ */
268
+ (a: Affine2d): (p: LinearPath2d) => LinearPath2d;
269
+ };
@@ -0,0 +1,54 @@
1
+ import type { Curve2dOps } from '../curve/curve2d.ts';
2
+ import * as Interval from '../interval/interval.ts';
3
+ import * as Interval2d from '../interval/interval2d.ts';
4
+ import * as Solution from '../solution/solution.ts';
5
+ import type { Affine2d } from '../transform/affine2d.ts';
6
+ import { Pipeable } from '../utils.ts';
7
+ import type { Vector2 } from '../vector/vector2.ts';
8
+ import { PathTraits, type Continuous, type DecreasingX, type DecreasingY, type IncreasingX, type IncreasingY, type MonotonicX, type MonotonicY } from './traits.ts';
9
+ /**
10
+ * Generic 2D path — a sequence of curves of a single kind `C` plus optional
11
+ * trait brands (`Continuous`, `MonotonicX`, etc.) tracking properties of the
12
+ * path as a whole.
13
+ *
14
+ * Each concrete path kind (`LinearPath2d`, `QuadraticPath2d`, `CubicPath2d`)
15
+ * is a `Path2d<TheirCurveKind, Trait>` plus a per-kind `TypeId` slot for
16
+ * cheap runtime identification. The per-kind modules re-export an operation
17
+ * surface derived from {@link makeMethods} over their curve's
18
+ * {@link Curve2dOps}.
19
+ *
20
+ * @since 2.0.0
21
+ */
22
+ export interface Path2d<C, Trait = unknown> extends Pipeable, Iterable<C> {
23
+ readonly [PathTraits]: Trait;
24
+ }
25
+ /**
26
+ * Builds the bundle of generic path operations for a curve kind `C`, given
27
+ * that curve's {@link Curve2dOps} and the per-kind `TypeId` symbol. Each
28
+ * polynomial-degree path module calls this once and re-exports the returned
29
+ * functions with curve-narrowed signatures.
30
+ *
31
+ * `ops` and `typeId` are captured by closure, so the returned operations
32
+ * incur no per-call lookup overhead.
33
+ *
34
+ * @since 2.0.0
35
+ */
36
+ export declare const makeMethods: <C>(typeId: symbol, ops: Curve2dOps<C>) => {
37
+ fromArray: (curves: ReadonlyArray<C>) => Path2d<C>;
38
+ make: (...curves: ReadonlyArray<C>) => Path2d<C>;
39
+ append: ((c: C) => (p: Path2d<C>) => Path2d<C>) & ((p: Path2d<C>, c: C) => Path2d<C>);
40
+ length: (p: Path2d<C>) => number;
41
+ solve: ((u: number) => (p: Path2d<C>) => Vector2) & ((p: Path2d<C>, u: number) => Vector2);
42
+ toPathData: (p: Path2d<C>) => string;
43
+ isContinuous: <T>(p: Path2d<C, T>) => p is Path2d<C, T & Continuous>;
44
+ isIncreasingX: <T>(p: Path2d<C, T>) => p is Path2d<C, T & IncreasingX>;
45
+ isDecreasingX: <T>(p: Path2d<C, T>) => p is Path2d<C, T & DecreasingX>;
46
+ isMonotonicX: <T>(p: Path2d<C, T>) => p is Path2d<C, T & MonotonicX>;
47
+ isIncreasingY: <T>(p: Path2d<C, T>) => p is Path2d<C, T & IncreasingY>;
48
+ isDecreasingY: <T>(p: Path2d<C, T>) => p is Path2d<C, T & DecreasingY>;
49
+ isMonotonicY: <T>(p: Path2d<C, T>) => p is Path2d<C, T & MonotonicY>;
50
+ solveAtX: ((...args: Array<any>) => any) & ((p: Path2d<C>, x: number) => Solution.AtMostOne<number>);
51
+ solveAtY: ((...args: Array<any>) => any) & ((p: Path2d<C>, y: number) => Solution.AtMostOne<number>);
52
+ boundingBox: (p: Path2d<C>) => Interval2d.Interval2d<Interval.Closed, Interval.Closed>;
53
+ transform: ((a: Affine2d) => (p: Path2d<C>) => Path2d<C>) & ((p: Path2d<C>, a: Affine2d) => Path2d<C>);
54
+ };
@@ -2,6 +2,7 @@ import type { Interval2d } from '../interval/interval2d.ts';
2
2
  import type { QuadraticCurve2d } from '../curve/quadratic2d.ts';
3
3
  import type { Closed } from '../interval/interval.ts';
4
4
  import type * as Solution from '../solution/solution.ts';
5
+ import type { Affine2d } from '../transform/affine2d.ts';
5
6
  import type { Pipeable } from '../utils.ts';
6
7
  import type { Vector2 } from '../vector/vector2.ts';
7
8
  import type { QuadraticPath2dTypeId } from './quadratic2d.internal.ts';
@@ -232,3 +233,24 @@ export declare const solveAtY: {
232
233
  * @since 2.0.0
233
234
  */
234
235
  export declare const boundingBox: (p: QuadraticPath2d) => Interval2d<Closed, Closed>;
236
+ export declare const transform: {
237
+ /**
238
+ * Applies an `Affine2d` transform to every curve in a `QuadraticPath2d`,
239
+ * returning a new path whose image is the affine image of the original.
240
+ * Trait brands are dropped.
241
+ *
242
+ * @param p - The quadratic path.
243
+ * @param a - The affine transform.
244
+ * @returns A new unbranded `QuadraticPath2d` whose curves are the affine images of the input's.
245
+ * @since 2.0.0
246
+ */
247
+ (p: QuadraticPath2d, a: Affine2d): QuadraticPath2d;
248
+ /**
249
+ * Applies an `Affine2d` transform to every curve in a `QuadraticPath2d`.
250
+ *
251
+ * @param a - The affine transform.
252
+ * @returns A function that takes a `QuadraticPath2d` and returns the transformed path.
253
+ * @since 2.0.0
254
+ */
255
+ (a: Affine2d): (p: QuadraticPath2d) => QuadraticPath2d;
256
+ };
@@ -1,6 +1,7 @@
1
1
  import type { RationalCubicCurve2d } from '../curve/rationalCubic2d.ts';
2
2
  import type { Closed } from '../interval/interval.ts';
3
3
  import type { Interval2d } from '../interval/interval2d.ts';
4
+ import type { Affine2d } from '../transform/affine2d.ts';
4
5
  import type { Pipeable } from '../utils.ts';
5
6
  import type { Vector2 } from '../vector/vector2.ts';
6
7
  import type { CubicPath2d } from './cubic2d.ts';
@@ -141,3 +142,23 @@ export declare const approximateAsCubicPath: {
141
142
  */
142
143
  (tolerance: number): (p: RationalCubicPath2d) => CubicPath2d;
143
144
  };
145
+ export declare const transform: {
146
+ /**
147
+ * Applies an `Affine2d` transform to every curve in a `RationalCubicPath2d`,
148
+ * returning a new path whose image is the affine image of the original.
149
+ *
150
+ * @param p - The rational cubic path.
151
+ * @param a - The affine transform.
152
+ * @returns A new `RationalCubicPath2d` whose curves are the affine images of the input's.
153
+ * @since 2.0.0
154
+ */
155
+ (p: RationalCubicPath2d, a: Affine2d): RationalCubicPath2d;
156
+ /**
157
+ * Applies an `Affine2d` transform to every curve in a `RationalCubicPath2d`.
158
+ *
159
+ * @param a - The affine transform.
160
+ * @returns A function that takes a `RationalCubicPath2d` and returns the transformed path.
161
+ * @since 2.0.0
162
+ */
163
+ (a: Affine2d): (p: RationalCubicPath2d) => RationalCubicPath2d;
164
+ };
@@ -1,3 +1,3 @@
1
- import { t as cubic_exports } from "../shared/cubic-BL6dpuHh.mjs";
2
- import { o as quadratic_exports, p as linear_exports } from "../shared/quadratic-DPrUsFCp.mjs";
1
+ import { t as cubic_exports } from "../shared/cubic-BiGuAnZ0.mjs";
2
+ import { o as quadratic_exports, p as linear_exports } from "../shared/quadratic-BmMqkvxx.mjs";
3
3
  export { cubic_exports as CubicPolynomial, linear_exports as LinearPolynomial, quadratic_exports as QuadraticPolynomial };