curvy 2.0.0-alpha.13 → 2.0.0-alpha.14

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 +102 -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 +122 -3
  6. package/dist/curve/quadratic2d.d.ts +103 -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-CeHgpNht.mjs → rationalCubic2d-CZSwwKtS.mjs} +591 -125
  46. package/dist/shared/rationalCubic2d-CZSwwKtS.mjs.map +1 -0
  47. package/dist/shared/{rationalCubic2d-H1J2Fjyy.mjs → rationalCubic2d-bveuGqBZ.mjs} +282 -326
  48. package/dist/shared/rationalCubic2d-bveuGqBZ.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
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Trait brands for matrices.
3
+ *
4
+ * Brands are phantom types — they exist only at the type level and carry no
5
+ * runtime data. A matrix value at runtime is identical regardless of the
6
+ * traits attached to its type. Refiners (`isInvertible`) and assertions
7
+ * (`asInvertible`) on each matrix module attach these brands to the matrix's
8
+ * `Traits` parameter at the type level.
9
+ *
10
+ * @since 2.0.0
11
+ */
12
+ declare const InvertibleId: unique symbol;
13
+ /**
14
+ * Brand for invertible matrices — matrices whose determinant is non-zero
15
+ * (within the default absolute tolerance). An `Invertible`-branded matrix
16
+ * narrows {@link inverse} from `Solution.AtMostOne` to `Solution.One`.
17
+ *
18
+ * @since 2.0.0
19
+ */
20
+ export type Invertible = {
21
+ readonly [InvertibleId]: 'invertible';
22
+ };
23
+ /**
24
+ * Phantom property key used by matrix interfaces to hold their `Traits` type
25
+ * parameter. The runtime object never has a value at this key — the `declare`
26
+ * modifier on the impl class makes the property type-visible only. The symbol
27
+ * itself is exported as a real value so that bundlers can resolve the
28
+ * computed key reference even though the property is never assigned.
29
+ *
30
+ * @since 2.0.0
31
+ */
32
+ export declare const MatrixTraits: unique symbol;
33
+ export type MatrixTraits = typeof MatrixTraits;
34
+ export {};
@@ -1,2 +1,2 @@
1
- import { r as monotonicity_exports } from "../shared/monotonicity-BdebFOtv.mjs";
1
+ import { r as monotonicity_exports } from "../shared/monotonicity-DNxBoC7F.mjs";
2
2
  export { monotonicity_exports as Monotonicity };
@@ -78,3 +78,28 @@ export declare const isStrict: (m: Monotonicity) => m is 1 | 2;
78
78
  * @since 2.0.0
79
79
  */
80
80
  export declare const fromComparison: (s0: number, s1: number) => GuaranteedMonotonicity;
81
+ /**
82
+ * Classifies monotonicity from the extreme values of a function's derivative
83
+ * over an interval, tolerating rounding noise in the derivative.
84
+ *
85
+ * `min` and `max` must be the true range extremes of the derivative on the
86
+ * interval (for a polynomial derivative: its values at the interval endpoints
87
+ * and at any interior stationary point). Each bound sets its sign-coverage
88
+ * bit only when it exceeds the tolerance, so a derivative that merely grazes
89
+ * zero — or dips past it by an ulp-scale amount that construction arithmetic
90
+ * cannot distinguish from zero — does not register as a crossing.
91
+ *
92
+ * The default tolerance is {@link RELATIVE_TOLERANCE} scaled by the larger
93
+ * bound magnitude, making the classification invariant under scaling of the
94
+ * input data. Derivative values are used rather than derivative *root
95
+ * locations* because values are well-conditioned where roots are not: a root
96
+ * computed from rounded coefficients can land on either side of an interval
97
+ * boundary, while the value it corresponds to stays pinned near zero.
98
+ *
99
+ * @param min - The smallest derivative value attained on the interval.
100
+ * @param max - The largest derivative value attained on the interval.
101
+ * @param tolerance - Absolute threshold below which a derivative value is treated as zero. Defaults to `RELATIVE_TOLERANCE * max(|min|, |max|)`.
102
+ * @returns The sign-coverage classification.
103
+ * @since 2.0.0
104
+ */
105
+ export declare const fromDerivativeRange: (min: number, max: number, tolerance?: number) => Monotonicity;
package/dist/number.d.ts CHANGED
@@ -4,9 +4,32 @@
4
4
  * @since 1.1.0
5
5
  */
6
6
  export declare const EPSILON = 1e-10;
7
+ /**
8
+ * Default relative tolerance for sign and degeneracy decisions about
9
+ * *computed* quantities — derivative values, determinants, discriminants.
10
+ *
11
+ * Scale-free: multiply by the natural magnitude of the quantity under test
12
+ * (e.g. the largest derivative value over the interval) to get an absolute
13
+ * threshold. Values inside the resulting band are indistinguishable from
14
+ * accumulated rounding noise and must not decide a sign question.
15
+ *
16
+ * The value sits ~4,500× above IEEE double rounding noise (2⁻⁵² ≈ 2.2e-16
17
+ * relative per operation) — generous headroom for noise accumulated across a
18
+ * construction pipeline — and far below any geometrically meaningful feature.
19
+ *
20
+ * @since 2.0.0
21
+ */
22
+ export declare const RELATIVE_TOLERANCE = 1e-12;
7
23
  /**
8
24
  * Approximate equality between two numbers, within an absolute tolerance.
9
25
  *
26
+ * This is the raw absolute comparison — the right tool when the domain is
27
+ * normalized by construction (curve parameter space, where `[0, 1]` makes an
28
+ * absolute band meaningful) or when a caller-chosen absolute band is the
29
+ * actual question. For identity questions about computed values of arbitrary
30
+ * magnitude ("is this the same point / value?"), use {@link coincident},
31
+ * which adds a magnitude-relative term.
32
+ *
10
33
  * @param a - The first value.
11
34
  * @param b - The second value.
12
35
  * @param eps - The maximum allowed absolute difference. Defaults to {@link EPSILON}.
@@ -14,6 +37,36 @@ export declare const EPSILON = 1e-10;
14
37
  * @since 1.1.0
15
38
  */
16
39
  export declare function epsEquals(a: number, b: number, eps?: number): boolean;
40
+ /**
41
+ * Library-policy approximate equality: `true` when
42
+ * `|a - b| <= absolute + relative * max(|a|, |b|)`.
43
+ *
44
+ * The relative term covers rounding noise accumulated by arithmetic, which
45
+ * grows with the magnitude of the values; the absolute term covers the
46
+ * neighborhood of zero, where relative comparison degenerates (no nonzero
47
+ * value is relatively close to `0`). The band is strictly wider than the
48
+ * plain absolute comparison, never narrower.
49
+ *
50
+ * Defaults: `absolute = ` {@link EPSILON} — a modeling floor that assumes
51
+ * coordinates of order ~1; pass an explicit value when your data has a
52
+ * different natural resolution — and `relative = ` {@link RELATIVE_TOLERANCE}.
53
+ *
54
+ * Not transitive: `coincident(a, b)` and `coincident(b, c)` do not imply
55
+ * `coincident(a, c)` — chains of pairwise-coincident values can drift beyond
56
+ * the band. Pairwise semantics are intentional (e.g. path continuity checks
57
+ * each junction locally).
58
+ *
59
+ * The tolerance parameters are positional, mirroring {@link epsEquals}: the
60
+ * common override is `absolute` alone, for a domain with a known resolution.
61
+ *
62
+ * @param a - The first value.
63
+ * @param b - The second value.
64
+ * @param absolute - Absolute floor of the band. Defaults to {@link EPSILON}.
65
+ * @param relative - Magnitude-proportional term of the band. Defaults to {@link RELATIVE_TOLERANCE}.
66
+ * @returns `true` when the values are equal to within the combined band.
67
+ * @since 2.0.0
68
+ */
69
+ export declare function coincident(a: number, b: number, absolute?: number, relative?: number): boolean;
17
70
  /**
18
71
  * Snaps near-zero values to exactly zero for stable sign tests.
19
72
  *
package/dist/number.mjs CHANGED
@@ -8,8 +8,31 @@ const PRECISION = 8;
8
8
  */
9
9
  const EPSILON = 1e-10;
10
10
  /**
11
+ * Default relative tolerance for sign and degeneracy decisions about
12
+ * *computed* quantities — derivative values, determinants, discriminants.
13
+ *
14
+ * Scale-free: multiply by the natural magnitude of the quantity under test
15
+ * (e.g. the largest derivative value over the interval) to get an absolute
16
+ * threshold. Values inside the resulting band are indistinguishable from
17
+ * accumulated rounding noise and must not decide a sign question.
18
+ *
19
+ * The value sits ~4,500× above IEEE double rounding noise (2⁻⁵² ≈ 2.2e-16
20
+ * relative per operation) — generous headroom for noise accumulated across a
21
+ * construction pipeline — and far below any geometrically meaningful feature.
22
+ *
23
+ * @since 2.0.0
24
+ */
25
+ const RELATIVE_TOLERANCE = 1e-12;
26
+ /**
11
27
  * Approximate equality between two numbers, within an absolute tolerance.
12
28
  *
29
+ * This is the raw absolute comparison — the right tool when the domain is
30
+ * normalized by construction (curve parameter space, where `[0, 1]` makes an
31
+ * absolute band meaningful) or when a caller-chosen absolute band is the
32
+ * actual question. For identity questions about computed values of arbitrary
33
+ * magnitude ("is this the same point / value?"), use {@link coincident},
34
+ * which adds a magnitude-relative term.
35
+ *
13
36
  * @param a - The first value.
14
37
  * @param b - The second value.
15
38
  * @param eps - The maximum allowed absolute difference. Defaults to {@link EPSILON}.
@@ -20,6 +43,38 @@ function epsEquals(a, b, eps = EPSILON) {
20
43
  return Math.abs(a - b) <= eps;
21
44
  }
22
45
  /**
46
+ * Library-policy approximate equality: `true` when
47
+ * `|a - b| <= absolute + relative * max(|a|, |b|)`.
48
+ *
49
+ * The relative term covers rounding noise accumulated by arithmetic, which
50
+ * grows with the magnitude of the values; the absolute term covers the
51
+ * neighborhood of zero, where relative comparison degenerates (no nonzero
52
+ * value is relatively close to `0`). The band is strictly wider than the
53
+ * plain absolute comparison, never narrower.
54
+ *
55
+ * Defaults: `absolute = ` {@link EPSILON} — a modeling floor that assumes
56
+ * coordinates of order ~1; pass an explicit value when your data has a
57
+ * different natural resolution — and `relative = ` {@link RELATIVE_TOLERANCE}.
58
+ *
59
+ * Not transitive: `coincident(a, b)` and `coincident(b, c)` do not imply
60
+ * `coincident(a, c)` — chains of pairwise-coincident values can drift beyond
61
+ * the band. Pairwise semantics are intentional (e.g. path continuity checks
62
+ * each junction locally).
63
+ *
64
+ * The tolerance parameters are positional, mirroring {@link epsEquals}: the
65
+ * common override is `absolute` alone, for a domain with a known resolution.
66
+ *
67
+ * @param a - The first value.
68
+ * @param b - The second value.
69
+ * @param absolute - Absolute floor of the band. Defaults to {@link EPSILON}.
70
+ * @param relative - Magnitude-proportional term of the band. Defaults to {@link RELATIVE_TOLERANCE}.
71
+ * @returns `true` when the values are equal to within the combined band.
72
+ * @since 2.0.0
73
+ */
74
+ function coincident(a, b, absolute = EPSILON, relative = RELATIVE_TOLERANCE) {
75
+ return Math.abs(a - b) <= absolute + relative * Math.max(Math.abs(a), Math.abs(b));
76
+ }
77
+ /**
23
78
  * Snaps near-zero values to exactly zero for stable sign tests.
24
79
  *
25
80
  * @param value - The value to test.
@@ -135,6 +190,6 @@ function mod(n, m) {
135
190
  return (n % m + m) % m;
136
191
  }
137
192
  //#endregion
138
- export { EPSILON, clamp, clampToZero, clip, epsEquals, lerp, minMax, mod, normalize, remap, round, roundDown, roundUp };
193
+ export { EPSILON, RELATIVE_TOLERANCE, clamp, clampToZero, clip, coincident, epsEquals, lerp, minMax, mod, normalize, remap, round, roundDown, roundUp };
139
194
 
140
195
  //# sourceMappingURL=number.mjs.map
@@ -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 { m as Ops } from "../shared/rationalCubic2d-CZSwwKtS.mjs";
4
+ import { n as rationalCubic2d_exports, o as linear2d_exports, r as cubic2d_exports, s as makeMethods } from "../shared/rationalCubic2d-bveuGqBZ.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","QuadraticCurve2d.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 * as QuadraticCurve2d from '../curve/quadratic2d.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, QuadraticCurve2d.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":";;;;;AAgBA,MAAa,wBAAuC,OAAO,yBAAyB;AAIpF,MAAM,UAAUA,YAAmB,uBAAuBC,IAAqB;;AAG/E,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnFjC,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"}