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
@@ -1,4 +1,5 @@
1
1
  import type { LinearPath2d } from '../path/linear2d.ts';
2
+ import type { Affine2d } from '../transform/affine2d.ts';
2
3
  import type { Pipeable } from '../utils.ts';
3
4
  import type { Vector2 } from '../vector/vector2.ts';
4
5
  import type { Linear2dTypeId } from './linear2d.internal.ts';
@@ -89,6 +90,70 @@ export declare const prepend: {
89
90
  */
90
91
  (p: Linear2d, ...points: ReadonlyArray<Vector2>): Linear2d;
91
92
  };
93
+ export declare const mapPoints: {
94
+ /**
95
+ * Applies a per-point transform to every point in a `Linear2d`, preserving
96
+ * point count and spline identity.
97
+ *
98
+ * @param f - The point transform.
99
+ * @returns A function that takes a `Linear2d` and returns the mapped polyline.
100
+ * @since 2.0.0
101
+ */
102
+ (f: (p: Vector2) => Vector2): (s: Linear2d) => Linear2d;
103
+ /**
104
+ * Applies a per-point transform to every point in a `Linear2d`.
105
+ *
106
+ * @param s - The polyline.
107
+ * @param f - The point transform.
108
+ * @returns A new `Linear2d` with the transform applied to each point.
109
+ * @since 2.0.0
110
+ */
111
+ (s: Linear2d, f: (p: Vector2) => Vector2): Linear2d;
112
+ };
113
+ export declare const flatMap: {
114
+ /**
115
+ * Hands the polyline's full point array to `f` and uses its returned
116
+ * `Linear2d` as the result. Useful for transforms that need to see all
117
+ * points at once — resampling, smoothing, decimation — while still going
118
+ * through this module's builders to maintain the 2-or-more-points invariant.
119
+ *
120
+ * @param f - The bulk transform.
121
+ * @returns A function that takes a `Linear2d` and returns the rebuilt polyline.
122
+ * @since 2.0.0
123
+ */
124
+ (f: (points: ReadonlyArray<Vector2>) => Linear2d): (s: Linear2d) => Linear2d;
125
+ /**
126
+ * Hands the polyline's full point array to `f` and uses its returned
127
+ * `Linear2d` as the result.
128
+ *
129
+ * @param s - The polyline.
130
+ * @param f - The bulk transform.
131
+ * @returns The `Linear2d` returned by `f`.
132
+ * @since 2.0.0
133
+ */
134
+ (s: Linear2d, f: (points: ReadonlyArray<Vector2>) => Linear2d): Linear2d;
135
+ };
136
+ export declare const transform: {
137
+ /**
138
+ * Applies an `Affine2d` transform to every point of a `Linear2d`. Polylines
139
+ * are degree-1 in every basis, so the transformed polyline is exactly the
140
+ * affine image of the original.
141
+ *
142
+ * @param s - The polyline.
143
+ * @param a - The affine transform.
144
+ * @returns A new `Linear2d` whose points are the affine images of the input's.
145
+ * @since 2.0.0
146
+ */
147
+ (s: Linear2d, a: Affine2d): Linear2d;
148
+ /**
149
+ * Applies an `Affine2d` transform to every point of a `Linear2d`.
150
+ *
151
+ * @param a - The affine transform.
152
+ * @returns A function that takes a `Linear2d` and returns the transformed polyline.
153
+ * @since 2.0.0
154
+ */
155
+ (a: Affine2d): (s: Linear2d) => Linear2d;
156
+ };
92
157
  /**
93
158
  * Converts a `Linear2d` to a `LinearPath2d` by joining consecutive points
94
159
  * with straight segments. The result is unbranded; assert
@@ -1,4 +1,5 @@
1
1
  import type { RationalCubicPath2d } from '../path/rationalCubic2d.ts';
2
+ import type { Affine2d } from '../transform/affine2d.ts';
2
3
  import type { Pipeable } from '../utils.ts';
3
4
  import type { Vector2, Weighted } from '../vector/vector2.ts';
4
5
  import type { Bezier2d } from './bezier2d.ts';
@@ -116,6 +117,81 @@ export declare const fromBezier: (b: Bezier2d) => RationalBezier2d;
116
117
  * @since 2.0.0
117
118
  */
118
119
  export declare const toPath: (r: RationalBezier2d) => RationalCubicPath2d;
120
+ export declare const mapPoints: {
121
+ /**
122
+ * Applies a per-point transform to every weighted control point in a
123
+ * `RationalBezier2d`, preserving point count.
124
+ *
125
+ * The transform sees each control point as a `Vector2.Weighted` —
126
+ * matching the type's storage form. To lift a position-only function into
127
+ * a weighted mapper while preserving weights, use
128
+ * {@link Vector2.liftWithWeight}.
129
+ *
130
+ * @param f - The weighted-point transform.
131
+ * @returns A function that takes a `RationalBezier2d` and returns the mapped spline.
132
+ * @since 2.0.0
133
+ */
134
+ (f: (p: Weighted) => Weighted): (s: RationalBezier2d) => RationalBezier2d;
135
+ /**
136
+ * Applies a per-point transform to every weighted control point in a
137
+ * `RationalBezier2d`.
138
+ *
139
+ * @param s - The rational bezier.
140
+ * @param f - The weighted-point transform.
141
+ * @returns A new `RationalBezier2d` with the transform applied to each point.
142
+ * @since 2.0.0
143
+ */
144
+ (s: RationalBezier2d, f: (p: Weighted) => Weighted): RationalBezier2d;
145
+ };
146
+ export declare const flatMap: {
147
+ /**
148
+ * Hands the bezier's full weighted control-point array to `f` and uses its
149
+ * returned `RationalBezier2d` as the result. The returned bezier goes
150
+ * through this module's builders, so the `3n + 1` point-count invariant is
151
+ * enforced at construction.
152
+ *
153
+ * @param f - The bulk transform.
154
+ * @returns A function that takes a `RationalBezier2d` and returns the rebuilt spline.
155
+ * @since 2.0.0
156
+ */
157
+ (f: (points: ReadonlyArray<Weighted>) => RationalBezier2d): (s: RationalBezier2d) => RationalBezier2d;
158
+ /**
159
+ * Hands the bezier's full weighted control-point array to `f` and uses its
160
+ * returned `RationalBezier2d` as the result.
161
+ *
162
+ * @param s - The rational bezier.
163
+ * @param f - The bulk transform.
164
+ * @returns The `RationalBezier2d` returned by `f`.
165
+ * @since 2.0.0
166
+ */
167
+ (s: RationalBezier2d, f: (points: ReadonlyArray<Weighted>) => RationalBezier2d): RationalBezier2d;
168
+ };
169
+ export declare const transform: {
170
+ /**
171
+ * Applies an `Affine2d` transform to every control-point position of a
172
+ * `RationalBezier2d`, preserving each point's weight. Rational Bézier
173
+ * evaluation is `Σ w_i N_i(t) P_i / Σ w_i N_i(t)`; affine maps commute with
174
+ * the numerator's barycentric combination, so transforming positions while
175
+ * holding weights fixed exactly parameterizes the affine image of the
176
+ * original curve.
177
+ *
178
+ * @param s - The rational bezier.
179
+ * @param a - The affine transform.
180
+ * @returns A new `RationalBezier2d` whose positions are the affine images
181
+ * of the input's, with weights unchanged.
182
+ * @since 2.0.0
183
+ */
184
+ (s: RationalBezier2d, a: Affine2d): RationalBezier2d;
185
+ /**
186
+ * Applies an `Affine2d` transform to every control-point position of a
187
+ * `RationalBezier2d`.
188
+ *
189
+ * @param a - The affine transform.
190
+ * @returns A function that takes a `RationalBezier2d` and returns the transformed bezier.
191
+ * @since 2.0.0
192
+ */
193
+ (a: Affine2d): (s: RationalBezier2d) => RationalBezier2d;
194
+ };
119
195
  export declare const subdivide: {
120
196
  /**
121
197
  * Splits a `RationalBezier2d` at the given global parameter `u ∈ (0, 1)`
@@ -0,0 +1,339 @@
1
+ import type { Matrix2x2 } from '../matrix/matrix2x2.ts';
2
+ import type { Matrix3x3 } from '../matrix/matrix3x3.ts';
3
+ import type * as Solution from '../solution/solution.ts';
4
+ import type { Pipeable } from '../utils.ts';
5
+ import type { Vector2 } from '../vector/vector2.ts';
6
+ import type { Affine2dTypeId } from './affine2d.internal.ts';
7
+ import type { AffineTraits, Invertible } from './traits.ts';
8
+ export type { Invertible } from './traits.ts';
9
+ /**
10
+ * A 2D affine transformation — the most general map of the form
11
+ *
12
+ * ```
13
+ * x' = a · x + b · y + tx
14
+ * y' = c · x + d · y + ty
15
+ * ```
16
+ *
17
+ * Represented internally as a 3×3 matrix in homogeneous form (the third row is
18
+ * always `[0, 0, 1]`), so that composition is matrix multiplication and a
19
+ * single instance can describe any chain of translates, scales, rotations,
20
+ * reflections, and shears.
21
+ *
22
+ * Affine maps commute with polynomial evaluation: transforming a spline's
23
+ * control points and then sampling produces the same point as sampling first
24
+ * and then transforming. Concrete spline modules expose `transform(s, a)`
25
+ * helpers that take advantage of this.
26
+ *
27
+ * The `Traits` type parameter is a phantom marker that accumulates trait
28
+ * brands as the transform is refined via `isInvertible` / `asInvertible`. The
29
+ * runtime value carries no trait data — `Traits` only flows through the type
30
+ * system to enable tighter return types on operations like {@link inverse}.
31
+ *
32
+ * @since 2.0.0
33
+ */
34
+ export interface Affine2d<out Traits = unknown> extends Pipeable {
35
+ readonly [Affine2dTypeId]: Affine2dTypeId;
36
+ readonly [AffineTraits]: Traits;
37
+ /**
38
+ * The underlying 3×3 homogeneous matrix. Last row is always `[0, 0, 1]`.
39
+ */
40
+ readonly matrix: Matrix3x3;
41
+ }
42
+ /**
43
+ * Checks if a value is an `Affine2d`.
44
+ *
45
+ * @param a - The value to check.
46
+ * @returns `true` if the value is an `Affine2d`, `false` otherwise.
47
+ * @since 2.0.0
48
+ */
49
+ export declare const isAffine2d: (a: unknown) => a is Affine2d;
50
+ /**
51
+ * Lifts a `Matrix3x3` into an `Affine2d` after asserting its last row is
52
+ * `[0, 0, 1]`. Use this when constructing affine transforms from an already-
53
+ * built matrix; prefer the named constructors below for ordinary usage.
54
+ *
55
+ * @param matrix - The matrix to lift. Must have a last row of `[0, 0, 1]`.
56
+ * @returns A new `Affine2d` wrapping the matrix.
57
+ * @throws If the matrix's last row is not `[0, 0, 1]` within tolerance.
58
+ * @since 2.0.0
59
+ */
60
+ export declare const fromMatrix: (matrix: Matrix3x3) => Affine2d;
61
+ /**
62
+ * The identity transform — leaves every point fixed.
63
+ *
64
+ * @since 2.0.0
65
+ */
66
+ export declare const identity: Affine2d;
67
+ /**
68
+ * Creates an `Affine2d` that translates by `(tx, ty)`.
69
+ *
70
+ * @param tx - The x translation.
71
+ * @param ty - The y translation.
72
+ * @returns A new translating `Affine2d`.
73
+ * @since 2.0.0
74
+ */
75
+ export declare const translate: (tx: number, ty: number) => Affine2d;
76
+ /**
77
+ * Creates an `Affine2d` that translates by the components of a `Vector2`.
78
+ *
79
+ * @param v - The vector whose components define the translation.
80
+ * @returns A new translating `Affine2d`.
81
+ * @since 2.0.0
82
+ */
83
+ export declare const translateBy: (v: Vector2) => Affine2d;
84
+ export declare const scale: {
85
+ /**
86
+ * Creates an `Affine2d` that scales uniformly by `xy` on both axes,
87
+ * centered on the origin.
88
+ *
89
+ * @param xy - The uniform scale factor.
90
+ * @returns A new scaling `Affine2d`.
91
+ * @since 2.0.0
92
+ */
93
+ (xy: number): Affine2d;
94
+ /**
95
+ * Creates an `Affine2d` that scales by `sx` on the x axis and `sy` on the
96
+ * y axis, centered on the origin.
97
+ *
98
+ * @param sx - The x scale factor.
99
+ * @param sy - The y scale factor.
100
+ * @returns A new scaling `Affine2d`.
101
+ * @since 2.0.0
102
+ */
103
+ (sx: number, sy: number): Affine2d;
104
+ };
105
+ /**
106
+ * Creates an `Affine2d` that scales by `(sx, sy)` about `origin`. Points at
107
+ * `origin` are left fixed; every other point moves toward or away from
108
+ * `origin` along the corresponding axis.
109
+ *
110
+ * @param sx - The x scale factor.
111
+ * @param sy - The y scale factor.
112
+ * @param origin - The fixed point of the scaling.
113
+ * @returns A new scaling `Affine2d`.
114
+ * @since 2.0.0
115
+ */
116
+ export declare const scaleAround: (sx: number, sy: number, origin: Vector2) => Affine2d;
117
+ /**
118
+ * Creates an `Affine2d` that rotates by `theta` radians around the origin.
119
+ * Counter-clockwise in the standard mathematical orientation (with y up); if
120
+ * your coordinate system has y down, rotation is clockwise.
121
+ *
122
+ * @param theta - The rotation angle in radians.
123
+ * @returns A new rotating `Affine2d`.
124
+ * @since 2.0.0
125
+ */
126
+ export declare const rotate: (theta: number) => Affine2d;
127
+ /**
128
+ * Creates an `Affine2d` that rotates by `theta` radians around `origin`.
129
+ *
130
+ * @param theta - The rotation angle in radians.
131
+ * @param origin - The fixed point of the rotation.
132
+ * @returns A new rotating `Affine2d`.
133
+ * @since 2.0.0
134
+ */
135
+ export declare const rotateAround: (theta: number, origin: Vector2) => Affine2d;
136
+ /**
137
+ * Reflects points across the x axis: `(x, y) → (x, -y)`.
138
+ *
139
+ * @since 2.0.0
140
+ */
141
+ export declare const reflectX: Affine2d;
142
+ /**
143
+ * Reflects points across the y axis: `(x, y) → (-x, y)`.
144
+ *
145
+ * @since 2.0.0
146
+ */
147
+ export declare const reflectY: Affine2d;
148
+ /**
149
+ * Creates an `Affine2d` that reflects across the line passing through
150
+ * `origin` in the direction `direction`. `direction` need not be unit length;
151
+ * it is normalized internally.
152
+ *
153
+ * @param origin - A point on the mirror line.
154
+ * @param direction - A non-zero vector parallel to the mirror line.
155
+ * @returns A new reflecting `Affine2d`.
156
+ * @throws If `direction` is the zero vector.
157
+ * @since 2.0.0
158
+ */
159
+ export declare const reflectAcrossLine: (origin: Vector2, direction: Vector2) => Affine2d;
160
+ /**
161
+ * Creates an `Affine2d` shear with `kx` horizontal shear (x gains `kx · y`)
162
+ * and `ky` vertical shear (y gains `ky · x`).
163
+ *
164
+ * @param kx - The horizontal shear factor.
165
+ * @param ky - The vertical shear factor.
166
+ * @returns A new shearing `Affine2d`.
167
+ * @since 2.0.0
168
+ */
169
+ export declare const shear: (kx: number, ky: number) => Affine2d;
170
+ export declare const andThen: {
171
+ /**
172
+ * Sequences two `Affine2d` transforms, applying `a` first and then `b`.
173
+ * Designed for pipe chains:
174
+ *
175
+ * ```ts
176
+ * import * as Affine2d from 'curvy/transform'
177
+ *
178
+ * const t = Affine2d.identity.pipe(
179
+ * Affine2d.andThen(Affine2d.translate(1, 0)),
180
+ * Affine2d.andThen(Affine2d.rotate(Math.PI / 2)),
181
+ * )
182
+ * // applying t = translate first, then rotate
183
+ * ```
184
+ *
185
+ * In matrix terms this returns `b · a`, the matrix that yields the same
186
+ * result as applying `a` then `b` to a column-vector point.
187
+ *
188
+ * @param a - The transform applied first.
189
+ * @param b - The transform applied second.
190
+ * @returns A new combined `Affine2d`.
191
+ * @since 2.0.0
192
+ */
193
+ (a: Affine2d, b: Affine2d): Affine2d;
194
+ /**
195
+ * Pipe-friendly form of {@link andThen}. `andThen(b)(a)` applies `a` then
196
+ * `b`.
197
+ *
198
+ * @param b - The transform applied after the piped-in transform.
199
+ * @returns A function that takes the transform to apply first and returns the sequenced transform.
200
+ * @since 2.0.0
201
+ */
202
+ (b: Affine2d): (a: Affine2d) => Affine2d;
203
+ };
204
+ export declare const inverse: {
205
+ /**
206
+ * Inverts an `Invertible`-branded `Affine2d`. Because the brand guarantees
207
+ * non-singularity, the result is always a single transform — wrapped in
208
+ * `Solution.One`.
209
+ *
210
+ * @param a - The transform to invert.
211
+ * @since 2.0.0
212
+ */
213
+ <T extends Invertible>(a: Affine2d<T>): Solution.One<Affine2d<T>>;
214
+ /**
215
+ * Inverts an `Affine2d`. Returns `Solution.none` if the transform is
216
+ * singular (e.g. a scale with a zero factor). Use {@link asInvertible} or
217
+ * {@link isInvertible} to refine the type and tighten the return to
218
+ * `Solution.One`, or use {@link inverseUnsafe} if you want the throwing form.
219
+ *
220
+ * @param a - The transform to invert.
221
+ * @since 2.0.0
222
+ */
223
+ <T>(a: Affine2d<T>): Solution.AtMostOne<Affine2d<T>>;
224
+ };
225
+ /**
226
+ * Inverts an `Affine2d`. Throws when the transform is singular (e.g. a scale
227
+ * with a zero factor). Useful when invertibility has already been validated
228
+ * out-of-band; otherwise prefer {@link inverse}.
229
+ *
230
+ * @param a - The transform to invert.
231
+ * @returns The inverse transform.
232
+ * @throws If `a` is singular.
233
+ * @since 2.0.0
234
+ */
235
+ export declare const inverseUnsafe: (a: Affine2d) => Affine2d;
236
+ /**
237
+ * Type-narrowing predicate: refines an `Affine2d<T>` to
238
+ * `Affine2d<T & Invertible>` when the underlying matrix is invertible
239
+ * (determinant non-zero within the default absolute tolerance).
240
+ *
241
+ * @param a - The transform to check.
242
+ * @returns `true` when `a` is invertible.
243
+ * @since 2.0.0
244
+ */
245
+ export declare const isInvertible: <T>(a: Affine2d<T>) => a is Affine2d<T & Invertible>;
246
+ /**
247
+ * Asserts that the transform is invertible, throwing on a singular transform.
248
+ *
249
+ * @param a - The transform to assert against.
250
+ * @returns The same transform, typed with the `Invertible` brand.
251
+ * @throws When `a` is singular.
252
+ * @since 2.0.0
253
+ */
254
+ export declare const asInvertible: <T>(a: Affine2d<T>) => Affine2d<T & Invertible>;
255
+ export declare const apply: {
256
+ /**
257
+ * Applies an `Affine2d` to a `Vector2`, returning the transformed point.
258
+ *
259
+ * @param a - The transform to apply.
260
+ * @param p - The point to transform.
261
+ * @returns The transformed point.
262
+ * @since 2.0.0
263
+ */
264
+ (a: Affine2d, p: Vector2): Vector2;
265
+ /**
266
+ * Applies an `Affine2d` to a `Vector2`.
267
+ *
268
+ * @param p - The point to transform.
269
+ * @returns A function that takes the transform and returns the transformed point.
270
+ * @since 2.0.0
271
+ */
272
+ (p: Vector2): (a: Affine2d) => Vector2;
273
+ };
274
+ /**
275
+ * Returns a `Vector2 → Vector2` function that applies `a` to its argument.
276
+ * Designed as the bridge to per-point mappers like `Bezier2d.mapPoints`:
277
+ *
278
+ * ```ts
279
+ * Bezier2d.mapPoints(spline, Affine2d.applyTo(transform))
280
+ * ```
281
+ *
282
+ * @param a - The transform to lift.
283
+ * @returns A function that applies `a` to a point.
284
+ * @since 2.0.0
285
+ */
286
+ export declare const applyTo: (a: Affine2d) => (p: Vector2) => Vector2;
287
+ /**
288
+ * Extracts the 2×2 linear part of an `Affine2d` — the upper-left block of
289
+ * its homogeneous matrix, ignoring translation. Maps direction vectors
290
+ * (tangents, differences of points) under the transform.
291
+ *
292
+ * @param a - The transform.
293
+ * @returns The 2×2 linear part.
294
+ * @since 2.0.0
295
+ */
296
+ export declare const linear: (a: Affine2d) => Matrix2x2;
297
+ /**
298
+ * Extracts the translation vector of an `Affine2d` — the third column of its
299
+ * homogeneous matrix, ignoring the linear part.
300
+ *
301
+ * @param a - The transform.
302
+ * @returns The translation vector.
303
+ * @since 2.0.0
304
+ */
305
+ export declare const translation: (a: Affine2d) => Vector2;
306
+ /**
307
+ * Returns the `Affine2d` corresponding to `a`'s linear part with translation
308
+ * zeroed. Equivalent to building an affine from {@link linear} but stays in
309
+ * the `Affine2d` type so it can be applied via {@link apply} or {@link applyTo}.
310
+ *
311
+ * Use this to transform anything that is *not* a position — tangent vectors
312
+ * in Hermite control data, or the higher-degree coefficients of a polynomial
313
+ * curve, both of which see only the linear part of an affine map.
314
+ *
315
+ * @param a - The transform.
316
+ * @returns The translation-free `Affine2d`.
317
+ * @since 2.0.0
318
+ */
319
+ export declare const linearPart: (a: Affine2d) => Affine2d;
320
+ export declare const equals: {
321
+ /**
322
+ * Checks if two `Affine2d` instances are approximately equal within the
323
+ * default absolute tolerance.
324
+ *
325
+ * @param a - The first transform.
326
+ * @param b - The second transform.
327
+ * @returns `true` when each matrix entry is within tolerance.
328
+ * @since 2.0.0
329
+ */
330
+ (a: Affine2d, b: Affine2d): boolean;
331
+ /**
332
+ * Checks if two `Affine2d` instances are approximately equal.
333
+ *
334
+ * @param b - The second transform.
335
+ * @returns A function that takes the first transform and returns the comparison.
336
+ * @since 2.0.0
337
+ */
338
+ (b: Affine2d): (a: Affine2d) => boolean;
339
+ };
@@ -0,0 +1,2 @@
1
+ export declare const Affine2dTypeId: unique symbol;
2
+ export type Affine2dTypeId = typeof Affine2dTypeId;
@@ -0,0 +1,3 @@
1
+ import * as Affine2dNs from './affine2d.ts';
2
+ export type Affine2d = Affine2dNs.Affine2d;
3
+ export { Affine2dNs as Affine2d };
@@ -0,0 +1,2 @@
1
+ import { t as affine2d_exports } from "../shared/affine2d-Dvna84gc.mjs";
2
+ export { affine2d_exports as Affine2d };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Trait brands for affine transforms.
3
+ *
4
+ * The `Invertible` brand is shared with matrices — an invertible `Affine2d`
5
+ * is one whose underlying 3x3 matrix is invertible — so it is re-exported
6
+ * from {@link ../matrix/traits.ts} rather than redeclared here.
7
+ *
8
+ * @since 2.0.0
9
+ */
10
+ export type { Invertible } from '../matrix/traits.ts';
11
+ /**
12
+ * Phantom property key used by affine interfaces to hold their `Traits` type
13
+ * parameter. The runtime object never has a value at this key — the `declare`
14
+ * modifier on the impl class makes the property type-visible only.
15
+ *
16
+ * @since 2.0.0
17
+ */
18
+ export declare const AffineTraits: unique symbol;
19
+ export type AffineTraits = typeof AffineTraits;
@@ -1,4 +1,4 @@
1
- import { l as vector2_exports } from "../shared/vector2-CSkaMXvp.mjs";
2
- import { n as vector3_exports } from "../shared/vector3-CR2LPV03.mjs";
3
- import { r as vector4_exports } from "../shared/vector4-PO_7EYd-.mjs";
1
+ import { u as vector2_exports } from "../shared/vector2-DuA3EmfL.mjs";
2
+ import { n as vector3_exports } from "../shared/vector3-oxtggkDp.mjs";
3
+ import { r as vector4_exports } from "../shared/vector4-DGHuCvTV.mjs";
4
4
  export { vector2_exports as Vector2, vector3_exports as Vector3, vector4_exports as Vector4 };
@@ -87,6 +87,55 @@ export declare const withWeight: {
87
87
  * @since 2.0.0
88
88
  */
89
89
  export declare const unweighted: (w: Weighted) => Vector2;
90
+ export declare const liftWithWeight: {
91
+ /**
92
+ * Lifts a `Vector2 → Vector2` function to operate on `Vector2.Weighted` by
93
+ * applying the function to the position and preserving the input's weight.
94
+ *
95
+ * Designed as the bridge between point-only utilities (e.g. {@link setR},
96
+ * `Vector2.add(offset)`) and weighted-spline mappers like
97
+ * `RationalBezier2d.mapPoints`. The default of "preserve weight" is the safe
98
+ * choice for the common case — translating, rotating, or scaling positions
99
+ * on a curve like a conic arc shouldn't silently collapse the curve's
100
+ * geometry-defining weights to a uniform value.
101
+ *
102
+ * @param fn - The position-only function to lift.
103
+ * @returns A function from `Weighted` to `Weighted` that preserves weight.
104
+ * @since 2.0.0
105
+ */
106
+ (fn: (v: Vector2) => Vector2): (w: Weighted) => Weighted;
107
+ /**
108
+ * Lifts a `Vector2 → Vector2` function to operate on `Vector2.Weighted`,
109
+ * replacing the input's weight with the given value.
110
+ *
111
+ * @param fn - The position-only function to lift.
112
+ * @param weight - The positive scalar weight to attach to each output.
113
+ * @returns A function from `Weighted` to `Weighted` that overrides weight.
114
+ * @since 2.0.0
115
+ */
116
+ (fn: (v: Vector2) => Vector2, weight: number): (w: Weighted) => Weighted;
117
+ /**
118
+ * Applies a `Vector2 → Vector2` function to the position of a
119
+ * `Vector2.Weighted`, preserving the input's weight.
120
+ *
121
+ * @param w - The weighted point to transform.
122
+ * @param fn - The position-only function to apply.
123
+ * @returns A new `Vector2.Weighted` with the function applied to position and the original weight.
124
+ * @since 2.0.0
125
+ */
126
+ (w: Weighted, fn: (v: Vector2) => Vector2): Weighted;
127
+ /**
128
+ * Applies a `Vector2 → Vector2` function to the position of a
129
+ * `Vector2.Weighted`, replacing the weight with the given value.
130
+ *
131
+ * @param w - The weighted point to transform.
132
+ * @param fn - The position-only function to apply.
133
+ * @param weight - The positive scalar weight to attach to the output.
134
+ * @returns A new `Vector2.Weighted` with the function applied to position and the overridden weight.
135
+ * @since 2.0.0
136
+ */
137
+ (w: Weighted, fn: (v: Vector2) => Vector2, weight: number): Weighted;
138
+ };
90
139
  export declare const weightedEquals: {
91
140
  /**
92
141
  * Checks if two `Vector2.Weighted` instances are approximately equal —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "curvy",
3
- "version": "2.0.0-alpha.13",
3
+ "version": "2.0.0-alpha.14",
4
4
  "description": "Spline and Curve utilities",
5
5
  "homepage": "https://github.com/tkofh/curvy#readme",
6
6
  "bugs": {
@@ -58,6 +58,10 @@
58
58
  "types": "./dist/characteristic/index.d.ts",
59
59
  "import": "./dist/characteristic/index.mjs"
60
60
  },
61
+ "./transform": {
62
+ "types": "./dist/transform/index.d.ts",
63
+ "import": "./dist/transform/index.mjs"
64
+ },
61
65
  "./number": {
62
66
  "types": "./dist/number.d.ts",
63
67
  "import": "./dist/number.mjs"
@@ -1 +0,0 @@
1
- {"version":3,"file":"characteristic-BkEr-rr8.mjs","names":["Matrix4x4.make","CubicPolynomial.fromVector","Matrix4x4.vectorProductLeft"],"sources":["../../src/characteristic/characteristic.ts"],"sourcesContent":["import * as Matrix4x4 from '../matrix/matrix4x4.ts'\nimport * as CubicPolynomial from '../polynomial/cubic.ts'\nimport { roundDown } from '../number.ts'\nimport type * as Vector4 from '../vector/vector4.ts'\n\n/**\n * The cubic Bézier characteristic matrix.\n *\n * Maps Bernstein-basis control points `(P₀, P₁, P₂, P₃)` to monomial cubic\n * coefficients `(c₀, c₁, c₂, c₃)` for `c₀ + c₁·t + c₂·t² + c₃·t³`. The matrix\n * IS the Bézier spline family — combined with control points (via {@link apply}),\n * it produces the cubic polynomial that parameterizes the curve.\n *\n * @since 2.0.0\n */\nexport const cubicBezier: Matrix4x4.Matrix4x4 = Matrix4x4.make(\n 1,\n 0,\n 0,\n 0,\n -3,\n 3,\n 0,\n 0,\n 3,\n -6,\n 3,\n 0,\n -1,\n 3,\n -3,\n 1,\n)\n\n/**\n * The cubic Hermite characteristic matrix.\n *\n * Maps Hermite control data `(P₀, V₀, P₁, V₁)` — two endpoints and two\n * tangent vectors — to monomial cubic coefficients.\n *\n * @since 2.0.0\n */\nexport const cubicHermite: Matrix4x4.Matrix4x4 = Matrix4x4.make(\n 1,\n 0,\n 0,\n 0,\n 0,\n 1,\n 0,\n 0,\n -3,\n -2,\n 3,\n -1,\n 2,\n 1,\n -2,\n 1,\n)\n\n/**\n * The uniform cubic B-spline (basis spline) characteristic matrix.\n *\n * Maps a window of four control points to the monomial cubic coefficients\n * of one segment of the corresponding uniform cubic B-spline. Stride-1\n * application produces a C² piecewise spline; the curve does not generally\n * pass through the control points.\n *\n * @since 2.0.0\n */\nexport const cubicBasisSpline: Matrix4x4.Matrix4x4 = Matrix4x4.make(\n 1 / 6,\n 4 / 6,\n roundDown(1 / 6),\n 0,\n -0.5,\n 0,\n 0.5,\n 0,\n 0.5,\n -1,\n 0.5,\n 0,\n -1 / 6,\n 0.5,\n -0.5,\n 1 / 6,\n)\n\nconst cardinalCache = new Map<number, Matrix4x4.Matrix4x4>()\n\n/**\n * The cubic Cardinal spline characteristic matrix, parameterized by tension.\n *\n * Maps four control points to the monomial cubic coefficients of one\n * Cardinal segment. The tension `s` controls how tightly the curve hugs\n * the control points: `s = 0` produces straight-line segments between\n * adjacent points, `s = 0.5` is the Catmull-Rom variant (see\n * {@link cubicCatmullRom}), and higher values produce looser, more curved\n * shapes.\n *\n * Cached per tension value — repeated calls with the same tension return\n * the same matrix instance.\n *\n * @param tension - The tension parameter.\n * @returns The characteristic matrix for that tension.\n * @since 2.0.0\n */\nexport const cubicCardinal = (tension: number): Matrix4x4.Matrix4x4 => {\n let m = cardinalCache.get(tension)\n if (m === undefined) {\n m = Matrix4x4.make(\n 0,\n 1,\n 0,\n 0,\n -tension,\n 0,\n tension,\n 0,\n 2 * tension,\n tension - 3,\n 3 - 2 * tension,\n -tension,\n -tension,\n 2 - tension,\n tension - 2,\n tension,\n )\n cardinalCache.set(tension, m)\n }\n return m\n}\n\n/**\n * The cubic Catmull-Rom characteristic matrix — Cardinal with tension `0.5`.\n * The most common Cardinal-family variant; reused widely enough to deserve\n * a constant.\n *\n * @since 2.0.0\n */\nexport const cubicCatmullRom: Matrix4x4.Matrix4x4 = cubicCardinal(0.5)\n\n/**\n * Applies a characteristic matrix to N channels of control values, producing\n * N cubic polynomials in monomial form.\n *\n * Each channel is a `Vector4` packing one axis's values across the four\n * control points: `(v₀, v₁, v₂, v₃)`. The result tuple has the same arity\n * as the input.\n *\n * The polynomial 2D spline pipeline applies this with two channels (x, y);\n * the rational 2D pipeline uses three (x, y, w) where `w` is the projective\n * denominator. Higher channel counts generalize to 3D, rational 3D, and\n * beyond — the characteristic matrix doesn't care about the geometric\n * domain, only about how to combine four samples per channel.\n *\n * @param matrix - The characteristic matrix of the spline family.\n * @param channels - One `Vector4` per output polynomial, packing the four control values for that channel.\n * @returns A tuple of cubic polynomials, one per input channel.\n * @since 2.0.0\n */\nexport const apply = <const Channels extends ReadonlyArray<Vector4.Vector4>>(\n matrix: Matrix4x4.Matrix4x4,\n ...channels: Channels\n): { readonly [K in keyof Channels]: CubicPolynomial.CubicPolynomial } => {\n const result = channels.map((c) =>\n CubicPolynomial.fromVector(Matrix4x4.vectorProductLeft(matrix, c)),\n )\n return result as { readonly [K in keyof Channels]: CubicPolynomial.CubicPolynomial }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAeA,MAAa,cAAmCA,KAC9C,GACA,GACA,GACA,GACA,IACA,GACA,GACA,GACA,GACA,IACA,GACA,GACA,IACA,GACA,IACA,EACD;;;;;;;;;AAUD,MAAa,eAAoCA,KAC/C,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,IACA,IACA,GACA,IACA,GACA,GACA,IACA,EACD;;;;;;;;;;;AAYD,MAAa,mBAAwCA,KACnD,IAAI,GACJ,IAAI,GACJ,UAAU,IAAI,EAAE,EAChB,GACA,KACA,GACA,IACA,GACA,IACA,IACA,IACA,GACA,KAAK,GACL,IACA,KACA,IAAI,EACL;AAED,MAAM,gCAAgB,IAAI,KAAkC;;;;;;;;;;;;;;;;;;AAmB5D,MAAa,iBAAiB,YAAyC;CACrE,IAAI,IAAI,cAAc,IAAI,QAAQ;CAClC,IAAI,MAAM,KAAA,GAAW;EACnB,IAAIA,KACF,GACA,GACA,GACA,GACA,CAAC,SACD,GACA,SACA,GACA,IAAI,SACJ,UAAU,GACV,IAAI,IAAI,SACR,CAAC,SACD,CAAC,SACD,IAAI,SACJ,UAAU,GACV,QACD;EACD,cAAc,IAAI,SAAS,EAAE;;CAE/B,OAAO;;;;;;;;;AAUT,MAAa,kBAAuC,cAAc,GAAI;;;;;;;;;;;;;;;;;;;;AAqBtE,MAAa,SACX,QACA,GAAG,aACqE;CAIxE,OAHe,SAAS,KAAK,MAC3BC,WAA2BC,kBAA4B,QAAQ,EAAE,CAAC,CAEvD"}