math 0.0.0 → 0.1.0

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 (201) hide show
  1. package/API.md +1605 -0
  2. package/LICENSE +21 -0
  3. package/README.md +197 -0
  4. package/dist/color/color.js +136 -0
  5. package/dist/color/color.js.map +1 -0
  6. package/dist/color/colorspace.js +43 -0
  7. package/dist/color/colorspace.js.map +1 -0
  8. package/dist/color/hsl.js +123 -0
  9. package/dist/color/hsl.js.map +1 -0
  10. package/dist/color/index.js +7 -0
  11. package/dist/color/index.js.map +1 -0
  12. package/dist/color/parse.js +276 -0
  13. package/dist/color/parse.js.map +1 -0
  14. package/dist/core/angle.js +43 -0
  15. package/dist/core/angle.js.map +1 -0
  16. package/dist/core/euler.js +212 -0
  17. package/dist/core/euler.js.map +1 -0
  18. package/dist/core/mat2.js +385 -0
  19. package/dist/core/mat2.js.map +1 -0
  20. package/dist/core/mat2d.js +422 -0
  21. package/dist/core/mat2d.js.map +1 -0
  22. package/dist/core/mat3.js +749 -0
  23. package/dist/core/mat3.js.map +1 -0
  24. package/dist/core/mat4.js +2197 -0
  25. package/dist/core/mat4.js.map +1 -0
  26. package/dist/core/polar.js +200 -0
  27. package/dist/core/polar.js.map +1 -0
  28. package/dist/core/quat.js +730 -0
  29. package/dist/core/quat.js.map +1 -0
  30. package/dist/core/quat2.js +883 -0
  31. package/dist/core/quat2.js.map +1 -0
  32. package/dist/core/scalar.js +112 -0
  33. package/dist/core/scalar.js.map +1 -0
  34. package/dist/core/spherical.js +248 -0
  35. package/dist/core/spherical.js.map +1 -0
  36. package/dist/core/vec2.js +597 -0
  37. package/dist/core/vec2.js.map +1 -0
  38. package/dist/core/vec3.js +916 -0
  39. package/dist/core/vec3.js.map +1 -0
  40. package/dist/core/vec4.js +573 -0
  41. package/dist/core/vec4.js.map +1 -0
  42. package/dist/geometry/circumcircle.js +39 -0
  43. package/dist/geometry/circumcircle.js.map +1 -0
  44. package/dist/geometry/index.js +6 -0
  45. package/dist/geometry/index.js.map +1 -0
  46. package/dist/geometry/polygon2-decompose.js +411 -0
  47. package/dist/geometry/polygon2-decompose.js.map +1 -0
  48. package/dist/geometry/polygon2-triangulate.js +222 -0
  49. package/dist/geometry/polygon2-triangulate.js.map +1 -0
  50. package/dist/geometry/quickhull2.js +123 -0
  51. package/dist/geometry/quickhull2.js.map +1 -0
  52. package/dist/geometry/quickhull3.js +605 -0
  53. package/dist/geometry/quickhull3.js.map +1 -0
  54. package/dist/ik/fabrik2.js +675 -0
  55. package/dist/ik/fabrik2.js.map +1 -0
  56. package/dist/ik/fabrik3.js +912 -0
  57. package/dist/ik/fabrik3.js.map +1 -0
  58. package/dist/ik/index.js +5 -0
  59. package/dist/ik/index.js.map +1 -0
  60. package/dist/index.js +27 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/noise/fractal.js +185 -0
  63. package/dist/noise/fractal.js.map +1 -0
  64. package/dist/noise/index.js +16 -0
  65. package/dist/noise/index.js.map +1 -0
  66. package/dist/noise/perlin2d.js +50 -0
  67. package/dist/noise/perlin2d.js.map +1 -0
  68. package/dist/noise/perlin3d.js +72 -0
  69. package/dist/noise/perlin3d.js.map +1 -0
  70. package/dist/noise/permutation.js +120 -0
  71. package/dist/noise/permutation.js.map +1 -0
  72. package/dist/noise/simplex2d.js +84 -0
  73. package/dist/noise/simplex2d.js.map +1 -0
  74. package/dist/noise/simplex3d.js +147 -0
  75. package/dist/noise/simplex3d.js.map +1 -0
  76. package/dist/noise/simplex4d.js +149 -0
  77. package/dist/noise/simplex4d.js.map +1 -0
  78. package/dist/noise/worley2d.js +50 -0
  79. package/dist/noise/worley2d.js.map +1 -0
  80. package/dist/noise/worley3d.js +61 -0
  81. package/dist/noise/worley3d.js.map +1 -0
  82. package/dist/random/index.js +9 -0
  83. package/dist/random/index.js.map +1 -0
  84. package/dist/random/isaac32.js +164 -0
  85. package/dist/random/isaac32.js.map +1 -0
  86. package/dist/random/isaac64.js +215 -0
  87. package/dist/random/isaac64.js.map +1 -0
  88. package/dist/random/mulberry32.js +48 -0
  89. package/dist/random/mulberry32.js.map +1 -0
  90. package/dist/random/random.js +120 -0
  91. package/dist/random/random.js.map +1 -0
  92. package/dist/shapes/box2.js +300 -0
  93. package/dist/shapes/box2.js.map +1 -0
  94. package/dist/shapes/box3.js +550 -0
  95. package/dist/shapes/box3.js.map +1 -0
  96. package/dist/shapes/circle.js +6 -0
  97. package/dist/shapes/circle.js.map +1 -0
  98. package/dist/shapes/frustum.js +662 -0
  99. package/dist/shapes/frustum.js.map +1 -0
  100. package/dist/shapes/index.js +25 -0
  101. package/dist/shapes/index.js.map +1 -0
  102. package/dist/shapes/obb3.js +557 -0
  103. package/dist/shapes/obb3.js.map +1 -0
  104. package/dist/shapes/plane3.js +267 -0
  105. package/dist/shapes/plane3.js.map +1 -0
  106. package/dist/shapes/polygon2.js +451 -0
  107. package/dist/shapes/polygon2.js.map +1 -0
  108. package/dist/shapes/raycast3.js +164 -0
  109. package/dist/shapes/raycast3.js.map +1 -0
  110. package/dist/shapes/segment2.js +82 -0
  111. package/dist/shapes/segment2.js.map +1 -0
  112. package/dist/shapes/sphere.js +23 -0
  113. package/dist/shapes/sphere.js.map +1 -0
  114. package/dist/shapes/triangle2.js +79 -0
  115. package/dist/shapes/triangle2.js.map +1 -0
  116. package/dist/shapes/triangle3.js +60 -0
  117. package/dist/shapes/triangle3.js.map +1 -0
  118. package/dist/src/color/color.d.ts +50 -0
  119. package/dist/src/color/colorspace.d.ts +15 -0
  120. package/dist/src/color/hsl.d.ts +28 -0
  121. package/dist/src/color/index.d.ts +5 -0
  122. package/dist/src/color/parse.d.ts +15 -0
  123. package/dist/src/core/angle.d.ts +29 -0
  124. package/dist/src/core/arrays.d.ts +4 -0
  125. package/dist/src/core/euler.d.ts +78 -0
  126. package/dist/src/core/index.d.ts +27 -0
  127. package/dist/src/core/mat2.d.ts +217 -0
  128. package/dist/src/core/mat2d.d.ts +218 -0
  129. package/dist/src/core/mat3.d.ts +295 -0
  130. package/dist/src/core/mat4.d.ts +667 -0
  131. package/dist/src/core/polar.d.ts +151 -0
  132. package/dist/src/core/quat.d.ts +370 -0
  133. package/dist/src/core/quat2.d.ts +344 -0
  134. package/dist/src/core/scalar.d.ts +75 -0
  135. package/dist/src/core/spherical.d.ts +165 -0
  136. package/dist/src/core/vec2.d.ts +403 -0
  137. package/dist/src/core/vec3.d.ts +493 -0
  138. package/dist/src/core/vec4.d.ts +336 -0
  139. package/dist/src/geometry/circumcircle.d.ts +9 -0
  140. package/dist/src/geometry/index.d.ts +5 -0
  141. package/dist/src/geometry/polygon2-decompose.d.ts +25 -0
  142. package/dist/src/geometry/polygon2-triangulate.d.ts +15 -0
  143. package/dist/src/geometry/quickhull2.d.ts +10 -0
  144. package/dist/src/geometry/quickhull3.d.ts +27 -0
  145. package/dist/src/ik/fabrik2.d.ts +314 -0
  146. package/dist/src/ik/fabrik3.d.ts +371 -0
  147. package/dist/src/ik/index.d.ts +2 -0
  148. package/dist/src/index.d.ts +1 -0
  149. package/dist/src/noise/fractal.d.ts +95 -0
  150. package/dist/src/noise/index.d.ts +9 -0
  151. package/dist/src/noise/perlin2d.d.ts +19 -0
  152. package/dist/src/noise/perlin3d.d.ts +20 -0
  153. package/dist/src/noise/permutation.d.ts +22 -0
  154. package/dist/src/noise/simplex2d.d.ts +19 -0
  155. package/dist/src/noise/simplex3d.d.ts +20 -0
  156. package/dist/src/noise/simplex4d.d.ts +24 -0
  157. package/dist/src/noise/worley2d.d.ts +22 -0
  158. package/dist/src/noise/worley3d.d.ts +23 -0
  159. package/dist/src/random/index.d.ts +8 -0
  160. package/dist/src/random/isaac32.d.ts +62 -0
  161. package/dist/src/random/isaac64.d.ts +75 -0
  162. package/dist/src/random/mulberry32.d.ts +43 -0
  163. package/dist/src/random/random.d.ts +67 -0
  164. package/dist/src/shapes/box2.d.ts +174 -0
  165. package/dist/src/shapes/box3.d.ts +213 -0
  166. package/dist/src/shapes/circle.d.ts +7 -0
  167. package/dist/src/shapes/frustum.d.ts +147 -0
  168. package/dist/src/shapes/index.d.ts +20 -0
  169. package/dist/src/shapes/obb3.d.ts +105 -0
  170. package/dist/src/shapes/plane3.d.ts +130 -0
  171. package/dist/src/shapes/polygon2.d.ts +155 -0
  172. package/dist/src/shapes/raycast3.d.ts +42 -0
  173. package/dist/src/shapes/segment2.d.ts +34 -0
  174. package/dist/src/shapes/sphere.d.ts +19 -0
  175. package/dist/src/shapes/triangle2.d.ts +53 -0
  176. package/dist/src/shapes/triangle3.d.ts +29 -0
  177. package/dist/src/time/easing.d.ts +21 -0
  178. package/dist/src/time/index.d.ts +6 -0
  179. package/dist/src/time/spring-core.d.ts +15 -0
  180. package/dist/src/time/spring.d.ts +36 -0
  181. package/dist/src/time/spring2.d.ts +11 -0
  182. package/dist/src/time/spring3.d.ts +11 -0
  183. package/dist/src/time/spring4.d.ts +11 -0
  184. package/dist/time/easing.js +24 -0
  185. package/dist/time/easing.js.map +1 -0
  186. package/dist/time/index.js +11 -0
  187. package/dist/time/index.js.map +1 -0
  188. package/dist/time/spring-core.js +48 -0
  189. package/dist/time/spring-core.js.map +1 -0
  190. package/dist/time/spring.js +62 -0
  191. package/dist/time/spring.js.map +1 -0
  192. package/dist/time/spring2.js +31 -0
  193. package/dist/time/spring2.js.map +1 -0
  194. package/dist/time/spring3.js +32 -0
  195. package/dist/time/spring3.js.map +1 -0
  196. package/dist/time/spring4.js +32 -0
  197. package/dist/time/spring4.js.map +1 -0
  198. package/package.json +90 -12
  199. package/skills/math/SKILL.md +140 -0
  200. package/math.coffee +0 -36
  201. package/math.js +0 -67
@@ -0,0 +1,314 @@
1
+ import { type Vec2 } from '../core/index.js';
2
+ /** What a joint's clockwise and anticlockwise limits are measured from. */
3
+ export declare enum ConstraintCoordinateSystem {
4
+ /** The direction of the previous bone, so the limits bound how far this bone may bend. */
5
+ LOCAL = 0,
6
+ /** A fixed world direction, so the limits bound this bone's absolute heading. */
7
+ GLOBAL = 1
8
+ }
9
+ /**
10
+ * How the first bone in a chain is constrained.
11
+ *
12
+ * The first bone has no bone before it, so it is constrained against a direction held on the chain.
13
+ * The `LOCAL_` types only mean anything for a chain in a {@link Structure2}, where
14
+ * {@link solveStructure} resolves them against the bone the chain hangs off.
15
+ */
16
+ export declare enum BaseboneConstraintType {
17
+ /** The first bone may point anywhere. */
18
+ NONE = 0,
19
+ /** Constrained against a fixed world direction. */
20
+ GLOBAL_ABSOLUTE = 1,
21
+ /** Constrained against the host bone's direction, so the chain bends relative to what it hangs off. */
22
+ LOCAL_RELATIVE = 2,
23
+ /**
24
+ * Constrained against a direction expressed in the host bone's frame, where **+Y** is the host
25
+ * bone's own direction. (`fabrik3`'s equivalent uses +Z, so a rig ported between the two needs
26
+ * its axes rotated.)
27
+ */
28
+ LOCAL_ABSOLUTE = 3
29
+ }
30
+ /** Which end of a host bone a connected chain hangs off. */
31
+ export declare enum BoneConnectionPoint {
32
+ /** The host bone's start - the joint it shares with the bone before it. */
33
+ START = 0,
34
+ /** The host bone's end. */
35
+ END = 1
36
+ }
37
+ /**
38
+ * A joint's rotational limits: a wedge about a baseline.
39
+ *
40
+ * Angles are in radians, and a limit of PI means unconstrained in that direction. Anticlockwise is
41
+ * positive, matching {@link vec2.signedAngle}.
42
+ */
43
+ export type Joint2 = {
44
+ /** How far the bone may swing clockwise of the baseline, in [0, PI]. */
45
+ clockwise: number;
46
+ /** How far the bone may swing anticlockwise of the baseline, in [0, PI]. */
47
+ anticlockwise: number;
48
+ /** What `clockwise` and `anticlockwise` are measured from. */
49
+ coordinateSystem: ConstraintCoordinateSystem;
50
+ /** The baseline for a `GLOBAL` joint - a world direction. Unit length. Unused when `LOCAL`. */
51
+ globalAxis: Vec2;
52
+ };
53
+ /** A single bone: two points, the fixed distance between them, and how it may rotate. */
54
+ export type Bone2 = {
55
+ /** The joint shared with the previous bone; on the first bone, the base. */
56
+ start: Vec2;
57
+ /** The joint shared with the next bone; on the last bone, the end effector. */
58
+ end: Vec2;
59
+ /** The rest distance between `start` and `end`, preserved by every solve. */
60
+ length: number;
61
+ /** How far this bone may rotate away from the bone before it. */
62
+ joint: Joint2;
63
+ };
64
+ /**
65
+ * A chain of bones, from the base (index 0) to the end effector (the last bone's `end`).
66
+ *
67
+ * Allocate one with {@link createChain2}, fill it with {@link addBone} or
68
+ * {@link addConsecutiveBone}, then solve it every frame.
69
+ *
70
+ * `bones[0].joint` is unused - the first bone is governed by the chain's basebone fields instead,
71
+ * so a joint's fields never change meaning with its index.
72
+ */
73
+ export type Chain2 = {
74
+ /**
75
+ * The bones, base first and end effector last.
76
+ *
77
+ * Neighbours share a point - bone `i`'s end is bone `i + 1`'s start - and the solver keeps it
78
+ * that way, so moving one moves the other.
79
+ */
80
+ bones: Bone2[];
81
+ /** The sum of every bone length - the chain's reach. Maintained by {@link addBone}. */
82
+ length: number;
83
+ /** Where the base of the chain is pinned when `fixedBase` is set. */
84
+ base: Vec2;
85
+ /** Whether `backward` pins the base to `base`. Clear it to let the whole chain drift. Forced on for a chain connected in a structure. */
86
+ fixedBase: boolean;
87
+ /** How the first bone is held; see {@link BaseboneConstraintType}. */
88
+ baseboneConstraintType: BaseboneConstraintType;
89
+ /** The baseline direction, in the space named by `baseboneConstraintType`. Unit length. */
90
+ baseboneAxis: Vec2;
91
+ /** `baseboneAxis` in world space. Equal to it for `GLOBAL_ABSOLUTE`; resolved by {@link solveStructure} otherwise. */
92
+ baseboneWorldAxis: Vec2;
93
+ /** How far the first bone may swing clockwise of the baseline, in [0, PI]. */
94
+ baseboneClockwise: number;
95
+ /** How far the first bone may swing anticlockwise of the baseline, in [0, PI]. */
96
+ baseboneAnticlockwise: number;
97
+ /** The target this chain solves for inside a {@link Structure2} when `useEmbeddedTarget` is set. */
98
+ embeddedTarget: Vec2;
99
+ /**
100
+ * Whether this chain solves for its own `embeddedTarget` instead of the target passed to
101
+ * {@link solveStructure}. Ignored outside a {@link Structure2}.
102
+ */
103
+ useEmbeddedTarget: boolean;
104
+ /** The most iterations {@link solve} will run. */
105
+ maxIterations: number;
106
+ /** {@link solve} stops as soon as the effector is this close to the target. */
107
+ solveDistanceThreshold: number;
108
+ /**
109
+ * How still an iteration must be before {@link solve} treats the chain as stuck.
110
+ *
111
+ * Only consulted on an iteration that fails to beat the best distance so far, and compared
112
+ * against the previous iteration rather than the best - so a solve that is slowly getting worse
113
+ * runs its full budget. That is deliberate: FABRIK often worsens before it improves, and every
114
+ * stricter rule tried here bought fewer iterations at a disproportionate cost in final pose.
115
+ */
116
+ minIterationChange: number;
117
+ /** The distance from the effector to the target after the last {@link solve}. */
118
+ solveDistance: number;
119
+ /** Scratch holding the best pose {@link solve} has seen, four numbers per bone. Grown by {@link addBone}. */
120
+ bestSolution: number[];
121
+ };
122
+ /** A chain's attachment to a bone in another chain of the same structure. */
123
+ export type Connection = {
124
+ /** The index of the host chain in `structure.chains`, or -1 for a chain that hangs off nothing. */
125
+ hostChain: number;
126
+ /** The index of the host bone within that chain. */
127
+ hostBone: number;
128
+ /** Which end of the host bone this chain's base is pinned to. */
129
+ point: BoneConnectionPoint;
130
+ };
131
+ /** A set of chains, each optionally hanging off a bone of another. */
132
+ export type Structure2 = {
133
+ /** Solved in insertion order, so a connected chain always sees its host already posed. */
134
+ chains: Chain2[];
135
+ /** Parallel to `chains`. */
136
+ connections: Connection[];
137
+ };
138
+ /** Creates an unconstrained joint. */
139
+ export declare function createJoint2(): Joint2;
140
+ /** Creates an empty chain with a fixed base at the origin and no basebone constraint. */
141
+ export declare function createChain2(): Chain2;
142
+ /**
143
+ * Appends a bone spanning `start` to `end`, copying both.
144
+ *
145
+ * The bone's length is taken from the distance between them. When this is the first bone the
146
+ * chain's base is moved to `start`.
147
+ *
148
+ * @param chain the chain to append to
149
+ * @param start the bone's start point
150
+ * @param end the bone's end point
151
+ * @param joint the bone's joint, or a fresh unconstrained one if omitted
152
+ * @returns the appended bone
153
+ */
154
+ export declare function addBone(chain: Chain2, start: Vec2, end: Vec2, joint?: Joint2): Bone2;
155
+ /**
156
+ * Appends a bone starting where the chain currently ends, running `length` along `direction`.
157
+ *
158
+ * @param chain the chain to append to, which must already have at least one bone
159
+ * @param direction the direction to extend in, assumed to be unit length
160
+ * @param length the length of the new bone
161
+ * @param joint the bone's joint, or a fresh unconstrained one if omitted
162
+ * @returns the appended bone
163
+ */
164
+ export declare function addConsecutiveBone(chain: Chain2, direction: Vec2, length: number, joint?: Joint2): Bone2;
165
+ /**
166
+ * Prepends a bone at the base end, extending the chain backward.
167
+ *
168
+ * This is what a follower grows with: driven by {@link forward} alone the base end is the tail.
169
+ *
170
+ * `joint` becomes the joint of the bone that was previously first, because that is the junction the
171
+ * new bone creates. The new first bone's own joint is unused, as always.
172
+ *
173
+ * @param chain the chain to prepend to, which must already have at least one bone
174
+ * @param direction the direction the new bone points, from its own start toward the existing chain. Assumed to be unit length
175
+ * @param length the length of the new bone
176
+ * @param joint the joint for the junction this creates, or a fresh unconstrained one if omitted
177
+ * @returns the prepended bone
178
+ */
179
+ export declare function addBoneAtBase(chain: Chain2, direction: Vec2, length: number, joint?: Joint2): Bone2;
180
+ /**
181
+ * Sets a joint's limits relative to the previous bone's direction, which is the usual case.
182
+ *
183
+ * @param joint the joint to configure
184
+ * @param clockwise how far the bone may swing clockwise of the previous bone, in radians, clamped to [0, PI]
185
+ * @param anticlockwise how far it may swing anticlockwise, in radians, clamped to [0, PI]
186
+ * @returns the joint
187
+ */
188
+ export declare function setLocalJoint(joint: Joint2, clockwise: number, anticlockwise: number): Joint2;
189
+ /**
190
+ * Sets a joint's limits relative to a fixed world direction, pinning the bone's absolute heading
191
+ * however the bones before it move.
192
+ *
193
+ * @param joint the joint to configure
194
+ * @param axis the world direction the limits are measured from, assumed to be unit length
195
+ * @param clockwise how far the bone may swing clockwise of `axis`, in radians, clamped to [0, PI]
196
+ * @param anticlockwise how far it may swing anticlockwise, in radians, clamped to [0, PI]
197
+ * @returns the joint
198
+ */
199
+ export declare function setGlobalJoint(joint: Joint2, axis: Vec2, clockwise: number, anticlockwise: number): Joint2;
200
+ /**
201
+ * Constrains the first bone to a wedge about `axis`.
202
+ *
203
+ * @param chain the chain to configure
204
+ * @param type which space `axis` is expressed in
205
+ * @param axis the baseline direction, assumed to be unit length. Ignored for {@link BaseboneConstraintType.LOCAL_RELATIVE}, which uses the host bone's direction
206
+ * @param clockwise how far the first bone may swing clockwise of the baseline, in radians, clamped to [0, PI]
207
+ * @param anticlockwise how far it may swing anticlockwise, in radians, clamped to [0, PI]
208
+ * @returns the chain
209
+ */
210
+ export declare function setBaseboneConstraint(chain: Chain2, type: BaseboneConstraintType, axis: Vec2, clockwise: number, anticlockwise: number): Chain2;
211
+ /**
212
+ * Moves the chain's pinned base, without moving the bones.
213
+ *
214
+ * The next {@link backward} or {@link solve} pulls the chain to it.
215
+ */
216
+ export declare function setBaseLocation(chain: Chain2, base: Vec2): Chain2;
217
+ /**
218
+ * Lays the chain out straight from its base along `direction`, discarding the current pose.
219
+ *
220
+ * A dead-straight chain is the worst starting pose for {@link solve} - see the note there. Bend
221
+ * `direction` slightly between bones instead if the chain will be solved cold.
222
+ */
223
+ export declare function straighten(chain: Chain2, direction: Vec2): Chain2;
224
+ /**
225
+ * Writes the end effector's position - the last bone's end - into `out`.
226
+ *
227
+ * A chain with no bones has its base as its effector, matching {@link solve}, which treats an empty
228
+ * chain as a supported state rather than an error.
229
+ */
230
+ export declare function getEffector(out: Vec2, chain: Chain2): Vec2;
231
+ /** Writes the unit direction of bone `index`, from its start toward its end, into `out`. */
232
+ export declare function getBoneDirection(out: Vec2, chain: Chain2, index: number): Vec2;
233
+ /** The angle of bone `index`, in radians, measured counter-clockwise from the +X axis. */
234
+ export declare function getBoneAngle(chain: Chain2, index: number): number;
235
+ /** Whether `target` is within reach of the chain's base, so a solve can place the effector exactly on it. */
236
+ export declare function isReachable(chain: Chain2, target: Vec2): boolean;
237
+ /**
238
+ * The forward pass: snaps the end effector onto `target` and drags the rest of the chain after it.
239
+ *
240
+ * A non-finite target leaves the chain untouched rather than poisoning it.
241
+ *
242
+ * The base is not pinned, so the whole chain moves. Run alone once per frame this is a follower
243
+ * rather than a solver - a rope or tentacle whose head leads. Pair it with {@link backward}, or use
244
+ * {@link solve}, to keep the base put.
245
+ *
246
+ * @param chain the chain to move, mutated in place
247
+ * @param target where the end effector should go
248
+ * @returns the chain
249
+ */
250
+ export declare function forward(chain: Chain2, target: Vec2): Chain2;
251
+ /**
252
+ * The backward pass: pins the base and pushes each bone outward from it. The basebone constraint
253
+ * is applied here.
254
+ *
255
+ * @param chain the chain to move, mutated in place
256
+ * @param base where the base should go, used only when `chain.fixedBase` is set
257
+ * @returns the chain
258
+ */
259
+ export declare function backward(chain: Chain2, base: Vec2): Chain2;
260
+ /**
261
+ * One full FABRIK iteration - {@link forward} then {@link backward}.
262
+ *
263
+ * @param chain the chain to move, mutated in place
264
+ * @param target where the end effector should go
265
+ * @returns the distance from the effector to `target` afterwards
266
+ */
267
+ export declare function iterate(chain: Chain2, target: Vec2): number;
268
+ /**
269
+ * Solves the chain for `target`, iterating until it is close enough, stops improving, or runs out
270
+ * of attempts.
271
+ *
272
+ * A constrained chain does not converge monotonically, so the best pose seen is kept and restored.
273
+ *
274
+ * A dead-straight chain is the worst case to solve cold: every direction lies on one line, leaving
275
+ * nothing sideways to bend it with. Give it a slight bend if the first solve matters.
276
+ *
277
+ * @param chain the chain to solve, mutated in place
278
+ * @param target where the end effector should go
279
+ * @returns the distance from the effector to `target`, also stored as `chain.solveDistance`
280
+ */
281
+ export declare function solve(chain: Chain2, target: Vec2): number;
282
+ /** Creates an empty structure. */
283
+ export declare function createStructure2(): Structure2;
284
+ /**
285
+ * Adds a chain that hangs off nothing, solving directly for the structure's target.
286
+ *
287
+ * @returns the index of the added chain
288
+ */
289
+ export declare function addChain(structure: Structure2, chain: Chain2): number;
290
+ /**
291
+ * Adds a chain whose base is pinned to one end of a bone in a chain already in the structure.
292
+ *
293
+ * Add the host first: {@link solveStructure} walks chains in insertion order, so a chain always
294
+ * sees its host already posed.
295
+ *
296
+ * @param structure the structure to add to
297
+ * @param chain the chain to add
298
+ * @param hostChain the index of the chain to hang off
299
+ * @param hostBone the index of the bone within that chain
300
+ * @param point which end of the host bone to attach to
301
+ * @returns the index of the added chain
302
+ */
303
+ export declare function connectChain(structure: Structure2, chain: Chain2, hostChain: number, hostBone: number, point: BoneConnectionPoint): number;
304
+ /**
305
+ * Solves every chain in the structure.
306
+ *
307
+ * A connected chain has its base moved onto its host bone's connection point and any `LOCAL_`
308
+ * basebone constraint resolved against the host bone's direction first. A chain with
309
+ * `useEmbeddedTarget` set solves for its own `embeddedTarget` instead.
310
+ *
311
+ * @param structure the structure to solve, mutated in place
312
+ * @param target the target for every chain that does not use an embedded target
313
+ */
314
+ export declare function solveStructure(structure: Structure2, target: Vec2): void;
@@ -0,0 +1,371 @@
1
+ import { type Quat, type Vec3 } from '../core/index.js';
2
+ /**
3
+ * How a joint may rotate relative to the bone before it.
4
+ *
5
+ * A {@link JointType.BALL} joint with a `rotor` of PI is unconstrained, which is the default, so
6
+ * there is no separate "no constraint" type.
7
+ */
8
+ export declare enum JointType {
9
+ /** Rotates freely within a cone of `rotor` radians about the previous bone's direction. */
10
+ BALL = 0,
11
+ /** Rotates only in the plane perpendicular to `rotationAxis`, which is in world space. */
12
+ GLOBAL_HINGE = 1,
13
+ /**
14
+ * Rotates only in the plane perpendicular to `rotationAxis`, which is fixed in the previous
15
+ * bone's frame.
16
+ *
17
+ * That frame comes from {@link mat3.fromDirection}, so local **+Z** is the previous bone's own
18
+ * direction and local +X and +Y are perpendicular to it. (`fabrik2` uses +Y for the same idea,
19
+ * so a rig ported from 2D needs its axes rotated.) The frame's roll about the bone is arbitrary
20
+ * and flips near world -Z, so a hinge whose parent swings through there will pop - hinge about a
21
+ * `GLOBAL_HINGE` axis you rebuild from your own frame if the parent turns freely.
22
+ */
23
+ LOCAL_HINGE = 2
24
+ }
25
+ /**
26
+ * How the first bone in a chain is constrained.
27
+ *
28
+ * The first bone has no bone before it to be constrained against, so it is constrained against a
29
+ * direction held on the chain instead. `GLOBAL_` types read that direction as world space, while
30
+ * `LOCAL_` types read it relative to the bone this chain is connected to, and so only mean
31
+ * anything for a chain in a {@link Structure3}, where {@link solveStructure} resolves them.
32
+ *
33
+ * A `LOCAL_` direction is read in the host bone's frame from {@link mat3.fromDirection}, where
34
+ * **+Z** is the host bone's own direction. (`fabrik2`'s equivalent uses +Y.) That frame's roll is
35
+ * arbitrary and flips near world -Z, so a host bone that swings through there makes the constraint
36
+ * pop.
37
+ */
38
+ export declare enum BaseboneConstraintType {
39
+ /** The first bone may point anywhere. */
40
+ NONE = 0,
41
+ /** Confined to a cone of `baseboneRotor` radians about a world-space direction. */
42
+ GLOBAL_ROTOR = 1,
43
+ /** Confined to a cone of `baseboneRotor` radians about a direction relative to the host bone. */
44
+ LOCAL_ROTOR = 2,
45
+ /** Confined to the plane perpendicular to a world-space hinge axis. */
46
+ GLOBAL_HINGE = 3,
47
+ /** Confined to the plane perpendicular to a hinge axis relative to the host bone. */
48
+ LOCAL_HINGE = 4
49
+ }
50
+ /** Which end of a host bone a connected chain hangs off. */
51
+ export declare enum BoneConnectionPoint {
52
+ /** The host bone's start - the joint it shares with the bone before it. */
53
+ START = 0,
54
+ /** The host bone's end. */
55
+ END = 1
56
+ }
57
+ /**
58
+ * A joint's rotational limits.
59
+ *
60
+ * Which fields apply depends on `type`: `rotor` for {@link JointType.BALL}, and `rotationAxis`,
61
+ * `referenceAxis`, `clockwise` and `anticlockwise` for the two hinge types. All angles are in
62
+ * radians, and a limit of PI means unconstrained.
63
+ */
64
+ export type Joint3 = {
65
+ /** Which of the fields below apply. */
66
+ type: JointType;
67
+ /** Ball: the half-angle of the cone about the previous bone's direction, in [0, PI]. */
68
+ rotor: number;
69
+ /** Hinge: how far the bone may swing clockwise of `referenceAxis`, in [0, PI]. */
70
+ clockwise: number;
71
+ /** Hinge: how far the bone may swing anticlockwise of `referenceAxis`, in [0, PI]. */
72
+ anticlockwise: number;
73
+ /** Hinge: the axis the bone rotates about. Unit length. */
74
+ rotationAxis: Vec3;
75
+ /** Hinge: the zero direction the clockwise and anticlockwise limits are measured from. Unit length, perpendicular to `rotationAxis`. */
76
+ referenceAxis: Vec3;
77
+ };
78
+ /** A single bone: two points, the fixed distance between them, and how it may rotate. */
79
+ export type Bone3 = {
80
+ /** The joint shared with the previous bone; on the first bone, the base. */
81
+ start: Vec3;
82
+ /** The joint shared with the next bone; on the last bone, the end effector. */
83
+ end: Vec3;
84
+ /** The rest distance between `start` and `end`, preserved by every solve. */
85
+ length: number;
86
+ /** How far this bone may rotate away from the bone before it. */
87
+ joint: Joint3;
88
+ };
89
+ /**
90
+ * A chain of bones, from the base (index 0) to the end effector (the last bone's `end`).
91
+ *
92
+ * Allocate one with {@link createChain3}, fill it with {@link addBone} or
93
+ * {@link addConsecutiveBone}, then solve it every frame.
94
+ *
95
+ * `bones[0].joint` is unused - the first bone is governed by the chain's basebone fields instead,
96
+ * so a joint's fields never change meaning with its index.
97
+ */
98
+ export type Chain3 = {
99
+ /**
100
+ * The bones, base first and end effector last.
101
+ *
102
+ * Neighbours share a point - bone `i`'s end is bone `i + 1`'s start - and the solver keeps it
103
+ * that way, so moving one moves the other.
104
+ */
105
+ bones: Bone3[];
106
+ /** The sum of every bone length - the chain's reach. Maintained by {@link addBone}. */
107
+ length: number;
108
+ /** Where the base of the chain is pinned when `fixedBase` is set. */
109
+ base: Vec3;
110
+ /** Whether `backward` pins the base to `base`. Clear it to let the whole chain drift. Forced on for a chain connected in a structure. */
111
+ fixedBase: boolean;
112
+ /** How the first bone is held; see {@link BaseboneConstraintType}. */
113
+ baseboneConstraintType: BaseboneConstraintType;
114
+ /** The cone axis or hinge axis, in the space named by `baseboneConstraintType`. Unit length. */
115
+ baseboneAxis: Vec3;
116
+ /** The hinge reference axis, in the space named by `baseboneConstraintType`. Unit length. */
117
+ baseboneReferenceAxis: Vec3;
118
+ /** `baseboneAxis` in world space. Equal to it for `GLOBAL_` types; resolved by {@link solveStructure} for `LOCAL_` ones. */
119
+ baseboneWorldAxis: Vec3;
120
+ /** `baseboneReferenceAxis` in world space. */
121
+ baseboneWorldReferenceAxis: Vec3;
122
+ /** The half-angle of the basebone cone, in [0, PI]. */
123
+ baseboneRotor: number;
124
+ /** The basebone hinge's clockwise limit, in [0, PI]. */
125
+ baseboneClockwise: number;
126
+ /** The basebone hinge's anticlockwise limit, in [0, PI]. */
127
+ baseboneAnticlockwise: number;
128
+ /** The target this chain solves for inside a {@link Structure3} when `useEmbeddedTarget` is set. */
129
+ embeddedTarget: Vec3;
130
+ /**
131
+ * Whether this chain solves for its own `embeddedTarget` instead of the target passed to
132
+ * {@link solveStructure}. Ignored outside a {@link Structure3}.
133
+ */
134
+ useEmbeddedTarget: boolean;
135
+ /** The most iterations {@link solve} will run. */
136
+ maxIterations: number;
137
+ /** {@link solve} stops as soon as the effector is this close to the target. */
138
+ solveDistanceThreshold: number;
139
+ /**
140
+ * How still an iteration must be before {@link solve} treats the chain as stuck.
141
+ *
142
+ * Only consulted on an iteration that fails to beat the best distance so far, and compared
143
+ * against the previous iteration rather than the best - so a solve that is slowly getting worse
144
+ * runs its full budget. That is deliberate: FABRIK often worsens before it improves, and every
145
+ * stricter rule tried here bought fewer iterations at a disproportionate cost in final pose.
146
+ */
147
+ minIterationChange: number;
148
+ /** The distance from the effector to the target after the last {@link solve}. */
149
+ solveDistance: number;
150
+ /** Scratch holding the best pose {@link solve} has seen, six numbers per bone. Grown by {@link addBone}. */
151
+ bestSolution: number[];
152
+ };
153
+ /** A chain's attachment to a bone in another chain of the same structure. */
154
+ export type Connection = {
155
+ /** The index of the host chain in `structure.chains`, or -1 for a chain that hangs off nothing. */
156
+ hostChain: number;
157
+ /** The index of the host bone within that chain. */
158
+ hostBone: number;
159
+ /** Which end of the host bone this chain's base is pinned to. */
160
+ point: BoneConnectionPoint;
161
+ };
162
+ /** A set of chains, each optionally hanging off a bone of another. */
163
+ export type Structure3 = {
164
+ /** Solved in insertion order, so a connected chain always sees its host already posed. */
165
+ chains: Chain3[];
166
+ /** Parallel to `chains`. */
167
+ connections: Connection[];
168
+ };
169
+ /** Creates an unconstrained joint. */
170
+ export declare function createJoint3(): Joint3;
171
+ /** Creates an empty chain with a fixed base at the origin and no basebone constraint. */
172
+ export declare function createChain3(): Chain3;
173
+ /**
174
+ * Appends a bone spanning `start` to `end`, copying both.
175
+ *
176
+ * The bone's length is taken from the distance between them. When this is the first bone the
177
+ * chain's base is moved to `start`.
178
+ *
179
+ * @param chain the chain to append to
180
+ * @param start the bone's start point
181
+ * @param end the bone's end point
182
+ * @param joint the bone's joint, or a fresh unconstrained one if omitted
183
+ * @returns the appended bone
184
+ */
185
+ export declare function addBone(chain: Chain3, start: Vec3, end: Vec3, joint?: Joint3): Bone3;
186
+ /**
187
+ * Appends a bone starting where the chain currently ends, running `length` along `direction`.
188
+ *
189
+ * @param chain the chain to append to, which must already have at least one bone
190
+ * @param direction the direction to extend in, assumed to be unit length
191
+ * @param length the length of the new bone
192
+ * @param joint the bone's joint, or a fresh unconstrained one if omitted
193
+ * @returns the appended bone
194
+ */
195
+ export declare function addConsecutiveBone(chain: Chain3, direction: Vec3, length: number, joint?: Joint3): Bone3;
196
+ /**
197
+ * Prepends a bone at the base end, extending the chain backward.
198
+ *
199
+ * This is what a follower grows with: driven by {@link forward} alone the base end is the tail.
200
+ *
201
+ * `joint` becomes the joint of the bone that was previously first, because that is the junction the
202
+ * new bone creates. The new first bone's own joint is unused, as always.
203
+ *
204
+ * @param chain the chain to prepend to, which must already have at least one bone
205
+ * @param direction the direction the new bone points, from its own start toward the existing chain. Assumed to be unit length
206
+ * @param length the length of the new bone
207
+ * @param joint the joint for the junction this creates, or a fresh unconstrained one if omitted
208
+ * @returns the prepended bone
209
+ */
210
+ export declare function addBoneAtBase(chain: Chain3, direction: Vec3, length: number, joint?: Joint3): Bone3;
211
+ /**
212
+ * Sets a ball-joint constraint: the bone may rotate within a cone of `rotor` radians about the
213
+ * direction of the bone before it.
214
+ *
215
+ * @param joint the joint to configure
216
+ * @param rotor the half-angle of the cone in radians, clamped to [0, PI]
217
+ * @returns the joint
218
+ */
219
+ export declare function setBallJoint(joint: Joint3, rotor: number): Joint3;
220
+ /**
221
+ * Sets a hinge constraint: the bone may only rotate in the plane perpendicular to `rotationAxis`,
222
+ * and within `clockwise` / `anticlockwise` radians of `referenceAxis` in that plane.
223
+ *
224
+ * `referenceAxis` is projected onto the hinge plane and normalized, so it need only be roughly
225
+ * perpendicular. Pass PI for both limits for a hinge that spins freely.
226
+ *
227
+ * @param joint the joint to configure
228
+ * @param type either {@link JointType.GLOBAL_HINGE} (axes in world space) or {@link JointType.LOCAL_HINGE} (axes relative to the previous bone)
229
+ * @param rotationAxis the hinge axis, assumed to be unit length
230
+ * @param clockwise the clockwise limit in radians, clamped to [0, PI]
231
+ * @param anticlockwise the anticlockwise limit in radians, clamped to [0, PI]
232
+ * @param referenceAxis the zero direction the limits are measured from
233
+ * @returns the joint
234
+ */
235
+ export declare function setHingeJoint(joint: Joint3, type: JointType.GLOBAL_HINGE | JointType.LOCAL_HINGE, rotationAxis: Vec3, clockwise: number, anticlockwise: number, referenceAxis: Vec3): Joint3;
236
+ /**
237
+ * Confines the first bone to a cone of `rotor` radians about `axis`.
238
+ *
239
+ * @param chain the chain to configure
240
+ * @param type either {@link BaseboneConstraintType.GLOBAL_ROTOR} or {@link BaseboneConstraintType.LOCAL_ROTOR}
241
+ * @param axis the cone axis, assumed to be unit length
242
+ * @param rotor the half-angle of the cone in radians, clamped to [0, PI]
243
+ * @returns the chain
244
+ */
245
+ export declare function setBaseboneRotorConstraint(chain: Chain3, type: BaseboneConstraintType.GLOBAL_ROTOR | BaseboneConstraintType.LOCAL_ROTOR, axis: Vec3, rotor: number): Chain3;
246
+ /**
247
+ * Confines the first bone to the plane perpendicular to `rotationAxis`, within `clockwise` /
248
+ * `anticlockwise` radians of `referenceAxis`.
249
+ *
250
+ * @param chain the chain to configure
251
+ * @param type either {@link BaseboneConstraintType.GLOBAL_HINGE} or {@link BaseboneConstraintType.LOCAL_HINGE}
252
+ * @param rotationAxis the hinge axis, assumed to be unit length
253
+ * @param clockwise the clockwise limit in radians, clamped to [0, PI]
254
+ * @param anticlockwise the anticlockwise limit in radians, clamped to [0, PI]
255
+ * @param referenceAxis the zero direction the limits are measured from
256
+ * @returns the chain
257
+ */
258
+ export declare function setBaseboneHingeConstraint(chain: Chain3, type: BaseboneConstraintType.GLOBAL_HINGE | BaseboneConstraintType.LOCAL_HINGE, rotationAxis: Vec3, clockwise: number, anticlockwise: number, referenceAxis: Vec3): Chain3;
259
+ /**
260
+ * Moves the chain's pinned base, without moving the bones.
261
+ *
262
+ * The next {@link backward} or {@link solve} pulls the chain to it.
263
+ */
264
+ export declare function setBaseLocation(chain: Chain3, base: Vec3): Chain3;
265
+ /**
266
+ * Lays the chain out straight from its base along `direction`, discarding the current pose.
267
+ *
268
+ * A dead-straight chain is the worst starting pose for {@link solve} - see the note there. Bend
269
+ * `direction` slightly between bones instead if the chain will be solved cold.
270
+ */
271
+ export declare function straighten(chain: Chain3, direction: Vec3): Chain3;
272
+ /**
273
+ * Writes the end effector's position - the last bone's end - into `out`.
274
+ *
275
+ * A chain with no bones has its base as its effector, matching {@link solve}, which treats an empty
276
+ * chain as a supported state rather than an error.
277
+ */
278
+ export declare function getEffector(out: Vec3, chain: Chain3): Vec3;
279
+ /** Writes the unit direction of bone `index`, from its start toward its end, into `out`. */
280
+ export declare function getBoneDirection(out: Vec3, chain: Chain3, index: number): Vec3;
281
+ /**
282
+ * Writes the rotation taking `up` onto the direction of bone `index` into `out`.
283
+ *
284
+ * Use it to orient a mesh along a bone, passing whichever axis the mesh is modelled along - `up`
285
+ * is `[0, 1, 0]` for a cylinder or capsule built along Y. The roll about the bone is arbitrary.
286
+ */
287
+ export declare function getBoneRotation(out: Quat, chain: Chain3, index: number, up: Vec3): Quat;
288
+ /** Whether `target` is within reach of the chain's base, so a solve can place the effector exactly on it. */
289
+ export declare function isReachable(chain: Chain3, target: Vec3): boolean;
290
+ /**
291
+ * The forward pass: snaps the end effector onto `target` and drags the rest of the chain after it.
292
+ *
293
+ * A non-finite target leaves the chain untouched rather than poisoning it.
294
+ *
295
+ * The base is not pinned, so the whole chain moves. Run alone once per frame this is a follower
296
+ * rather than a solver - a rope or tentacle whose head leads. Pair it with {@link backward}, or use
297
+ * {@link solve}, to keep the base put.
298
+ *
299
+ * @param chain the chain to move, mutated in place
300
+ * @param target where the end effector should go
301
+ * @returns the chain
302
+ */
303
+ export declare function forward(chain: Chain3, target: Vec3): Chain3;
304
+ /**
305
+ * The backward pass: pins the base and pushes each bone outward from it.
306
+ *
307
+ * Basebone constraints and hinge reference-axis limits are applied here only - the forward pass
308
+ * skips the latter, because clamping on both degrades the solution.
309
+ *
310
+ * @param chain the chain to move, mutated in place
311
+ * @param base where the base should go, used only when `chain.fixedBase` is set
312
+ * @returns the chain
313
+ */
314
+ export declare function backward(chain: Chain3, base: Vec3): Chain3;
315
+ /**
316
+ * One full FABRIK iteration - {@link forward} then {@link backward}. Often enough on its own for an
317
+ * unconstrained chain reaching a nearby target. use {@link solve} to iterate to a tolerance.
318
+ *
319
+ * @param chain the chain to move, mutated in place
320
+ * @param target where the end effector should go
321
+ * @returns the distance from the effector to `target` afterwards
322
+ */
323
+ export declare function iterate(chain: Chain3, target: Vec3): number;
324
+ /**
325
+ * Solves the chain for `target`, iterating until it is close enough, stops improving, or runs out
326
+ * of attempts.
327
+ *
328
+ * A constrained chain does not converge monotonically, so the best pose seen is kept and restored.
329
+ *
330
+ * A dead-straight chain is the worst case to solve cold: every direction lies on one line, leaving
331
+ * nothing sideways to bend it with. Give it a slight bend if the first solve matters.
332
+ *
333
+ * @param chain the chain to solve, mutated in place
334
+ * @param target where the end effector should go
335
+ * @returns the distance from the effector to `target`, also stored as `chain.solveDistance`
336
+ */
337
+ export declare function solve(chain: Chain3, target: Vec3): number;
338
+ /** Creates an empty structure. */
339
+ export declare function createStructure3(): Structure3;
340
+ /**
341
+ * Adds a chain that hangs off nothing, solving directly for the structure's target.
342
+ *
343
+ * @returns the index of the added chain
344
+ */
345
+ export declare function addChain(structure: Structure3, chain: Chain3): number;
346
+ /**
347
+ * Adds a chain whose base is pinned to one end of a bone in a chain already in the structure.
348
+ *
349
+ * Add the host first: {@link solveStructure} walks chains in insertion order, so a chain always
350
+ * sees its host already posed.
351
+ *
352
+ * @param structure the structure to add to
353
+ * @param chain the chain to add
354
+ * @param hostChain the index of the chain to hang off
355
+ * @param hostBone the index of the bone within that chain
356
+ * @param point which end of the host bone to attach to
357
+ * @returns the index of the added chain
358
+ */
359
+ export declare function connectChain(structure: Structure3, chain: Chain3, hostChain: number, hostBone: number, point: BoneConnectionPoint): number;
360
+ /**
361
+ * Solves every chain in the structure.
362
+ *
363
+ * A connected chain has its base moved onto its host bone's connection point and any `LOCAL_`
364
+ * basebone constraint resolved through the host bone's frame first. A chain with
365
+ * `useEmbeddedTarget` set solves for its own `embeddedTarget` instead - how a walker's legs each
366
+ * reach their own foothold.
367
+ *
368
+ * @param structure the structure to solve, mutated in place
369
+ * @param target the target for every chain that does not use an embedded target
370
+ */
371
+ export declare function solveStructure(structure: Structure3, target: Vec3): void;