math 0.0.3 → 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 (203) 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 -16
  199. package/skills/math/SKILL.md +140 -0
  200. package/AUTHORS +0 -1
  201. package/Cakefile +0 -28
  202. package/math.coffee +0 -112
  203. package/math.js +0 -141
@@ -0,0 +1,912 @@
1
+ import { distance, subtract, copy, squaredDistance, transformMat3, normalize, rotateTowards, squaredLength, scaleAndAdd, dot, perpendicular, signedAngle } from '../core/vec3.js';
2
+ import { rotationTo } from '../core/quat.js';
3
+ import { create } from '../core/mat3.js';
4
+
5
+ // FABRIK (Forward And Backward Reaching Inverse Kinematics) for 3D chains.
6
+ //
7
+ // Two passes, each preserving every bone length exactly: `forward` drags the chain onto the target
8
+ // from the effector back, `backward` pushes it out from a pinned base. Both are exported, because
9
+ // `forward` alone with no base pin is a follower - a rope or tentacle whose head leads and whose
10
+ // body trails - and that wants one pass per frame rather than a converged solve.
11
+ //
12
+ // The constraint model follows Caliko, the reference implementation accompanying Aristidou &
13
+ // Lasenby (2011), "FABRIK: A fast, iterative solver for the Inverse Kinematics problem".
14
+ /**
15
+ * How a joint may rotate relative to the bone before it.
16
+ *
17
+ * A {@link JointType.BALL} joint with a `rotor` of PI is unconstrained, which is the default, so
18
+ * there is no separate "no constraint" type.
19
+ */
20
+ var JointType;
21
+ (function (JointType) {
22
+ /** Rotates freely within a cone of `rotor` radians about the previous bone's direction. */
23
+ JointType[JointType["BALL"] = 0] = "BALL";
24
+ /** Rotates only in the plane perpendicular to `rotationAxis`, which is in world space. */
25
+ JointType[JointType["GLOBAL_HINGE"] = 1] = "GLOBAL_HINGE";
26
+ /**
27
+ * Rotates only in the plane perpendicular to `rotationAxis`, which is fixed in the previous
28
+ * bone's frame.
29
+ *
30
+ * That frame comes from {@link mat3.fromDirection}, so local **+Z** is the previous bone's own
31
+ * direction and local +X and +Y are perpendicular to it. (`fabrik2` uses +Y for the same idea,
32
+ * so a rig ported from 2D needs its axes rotated.) The frame's roll about the bone is arbitrary
33
+ * and flips near world -Z, so a hinge whose parent swings through there will pop - hinge about a
34
+ * `GLOBAL_HINGE` axis you rebuild from your own frame if the parent turns freely.
35
+ */
36
+ JointType[JointType["LOCAL_HINGE"] = 2] = "LOCAL_HINGE";
37
+ })(JointType || (JointType = {}));
38
+ /**
39
+ * How the first bone in a chain is constrained.
40
+ *
41
+ * The first bone has no bone before it to be constrained against, so it is constrained against a
42
+ * direction held on the chain instead. `GLOBAL_` types read that direction as world space, while
43
+ * `LOCAL_` types read it relative to the bone this chain is connected to, and so only mean
44
+ * anything for a chain in a {@link Structure3}, where {@link solveStructure} resolves them.
45
+ *
46
+ * A `LOCAL_` direction is read in the host bone's frame from {@link mat3.fromDirection}, where
47
+ * **+Z** is the host bone's own direction. (`fabrik2`'s equivalent uses +Y.) That frame's roll is
48
+ * arbitrary and flips near world -Z, so a host bone that swings through there makes the constraint
49
+ * pop.
50
+ */
51
+ var BaseboneConstraintType;
52
+ (function (BaseboneConstraintType) {
53
+ /** The first bone may point anywhere. */
54
+ BaseboneConstraintType[BaseboneConstraintType["NONE"] = 0] = "NONE";
55
+ /** Confined to a cone of `baseboneRotor` radians about a world-space direction. */
56
+ BaseboneConstraintType[BaseboneConstraintType["GLOBAL_ROTOR"] = 1] = "GLOBAL_ROTOR";
57
+ /** Confined to a cone of `baseboneRotor` radians about a direction relative to the host bone. */
58
+ BaseboneConstraintType[BaseboneConstraintType["LOCAL_ROTOR"] = 2] = "LOCAL_ROTOR";
59
+ /** Confined to the plane perpendicular to a world-space hinge axis. */
60
+ BaseboneConstraintType[BaseboneConstraintType["GLOBAL_HINGE"] = 3] = "GLOBAL_HINGE";
61
+ /** Confined to the plane perpendicular to a hinge axis relative to the host bone. */
62
+ BaseboneConstraintType[BaseboneConstraintType["LOCAL_HINGE"] = 4] = "LOCAL_HINGE";
63
+ })(BaseboneConstraintType || (BaseboneConstraintType = {}));
64
+ /** Which end of a host bone a connected chain hangs off. */
65
+ var BoneConnectionPoint;
66
+ (function (BoneConnectionPoint) {
67
+ /** The host bone's start - the joint it shares with the bone before it. */
68
+ BoneConnectionPoint[BoneConnectionPoint["START"] = 0] = "START";
69
+ /** The host bone's end. */
70
+ BoneConnectionPoint[BoneConnectionPoint["END"] = 1] = "END";
71
+ })(BoneConnectionPoint || (BoneConnectionPoint = {}));
72
+ // the most iterations a solve runs before giving up on reaching the target
73
+ const DEFAULT_MAX_ITERATIONS = 20;
74
+ // how close the effector must get before a solve stops early. in world units, so scale it with the
75
+ // scene - the default suits a chain a few units long
76
+ const DEFAULT_SOLVE_DISTANCE_THRESHOLD = 0.01;
77
+ // how little an iteration may improve the solve distance before it counts as stalled. a constrained
78
+ // chain can reach a pose no further iteration improves on while still short of the threshold, and
79
+ // without this it would burn every remaining iteration going nowhere. two orders of magnitude below
80
+ // the distance threshold, so a solve still closing in on the target is never mistaken for a stalled
81
+ // one
82
+ const DEFAULT_MIN_ITERATION_CHANGE = 1e-4;
83
+ // below this squared length a vector carries no usable direction - 1e-12 in length terms, well above
84
+ // a normalize's noise floor and well below anything meaningful. every test against it also checks
85
+ // for NaN, so a value poisoned upstream takes the same fallback instead of spreading down the chain
86
+ const DEGENERATE_SQUARED_LENGTH = 1e-24;
87
+ /**
88
+ * Normalizes `axis` into `out`, leaving `out` untouched if `axis` has no direction to give.
89
+ *
90
+ * A zero axis cannot be normalized, and storing one turns every constraint that reads it into NaN.
91
+ * `out` always starts as a valid unit vector, so keeping it is the safe fallback.
92
+ */
93
+ function setUnitAxis(out, axis) {
94
+ if (!hasDirection(squaredLength(axis)))
95
+ return out;
96
+ return normalize(out, axis);
97
+ }
98
+ /**
99
+ * Whether a squared length is a usable magnitude: a real number, and not effectively zero.
100
+ *
101
+ * NaN counts as unusable. A value poisoned upstream then takes the same fallback as a zero-length
102
+ * one, instead of spreading down the chain and leaving it stuck.
103
+ */
104
+ function hasDirection(squaredLength) {
105
+ if (Number.isNaN(squaredLength))
106
+ return false;
107
+ return squaredLength >= DEGENERATE_SQUARED_LENGTH;
108
+ }
109
+ /** Creates an unconstrained joint. */
110
+ function createJoint3() {
111
+ return {
112
+ type: JointType.BALL,
113
+ rotor: Math.PI,
114
+ clockwise: Math.PI,
115
+ anticlockwise: Math.PI,
116
+ rotationAxis: [0, 0, 1],
117
+ referenceAxis: [1, 0, 0],
118
+ };
119
+ }
120
+ /** Creates an empty chain with a fixed base at the origin and no basebone constraint. */
121
+ function createChain3() {
122
+ return {
123
+ bones: [],
124
+ length: 0,
125
+ base: [0, 0, 0],
126
+ fixedBase: true,
127
+ baseboneConstraintType: BaseboneConstraintType.NONE,
128
+ baseboneAxis: [0, 1, 0],
129
+ baseboneReferenceAxis: [1, 0, 0],
130
+ baseboneWorldAxis: [0, 1, 0],
131
+ baseboneWorldReferenceAxis: [1, 0, 0],
132
+ baseboneRotor: Math.PI,
133
+ baseboneClockwise: Math.PI,
134
+ baseboneAnticlockwise: Math.PI,
135
+ embeddedTarget: [0, 0, 0],
136
+ useEmbeddedTarget: false,
137
+ maxIterations: DEFAULT_MAX_ITERATIONS,
138
+ solveDistanceThreshold: DEFAULT_SOLVE_DISTANCE_THRESHOLD,
139
+ minIterationChange: DEFAULT_MIN_ITERATION_CHANGE,
140
+ solveDistance: Number.POSITIVE_INFINITY,
141
+ bestSolution: [],
142
+ };
143
+ }
144
+ /**
145
+ * Appends a bone spanning `start` to `end`, copying both.
146
+ *
147
+ * The bone's length is taken from the distance between them. When this is the first bone the
148
+ * chain's base is moved to `start`.
149
+ *
150
+ * @param chain the chain to append to
151
+ * @param start the bone's start point
152
+ * @param end the bone's end point
153
+ * @param joint the bone's joint, or a fresh unconstrained one if omitted
154
+ * @returns the appended bone
155
+ */
156
+ function addBone(chain, start, end, joint = createJoint3()) {
157
+ const bone = {
158
+ start: [start[0], start[1], start[2]],
159
+ end: [end[0], end[1], end[2]],
160
+ length: distance(start, end),
161
+ joint,
162
+ };
163
+ if (chain.bones.length === 0) {
164
+ chain.base[0] = start[0];
165
+ chain.base[1] = start[1];
166
+ chain.base[2] = start[2];
167
+ }
168
+ chain.bones.push(bone);
169
+ chain.length += bone.length;
170
+ // six numbers of best-pose scratch per bone, so `solve` never allocates
171
+ chain.bestSolution.push(0, 0, 0, 0, 0, 0);
172
+ return bone;
173
+ }
174
+ /**
175
+ * Appends a bone starting where the chain currently ends, running `length` along `direction`.
176
+ *
177
+ * @param chain the chain to append to, which must already have at least one bone
178
+ * @param direction the direction to extend in, assumed to be unit length
179
+ * @param length the length of the new bone
180
+ * @param joint the bone's joint, or a fresh unconstrained one if omitted
181
+ * @returns the appended bone
182
+ */
183
+ function addConsecutiveBone(chain, direction, length, joint = createJoint3()) {
184
+ const previous = chain.bones[chain.bones.length - 1];
185
+ _addConsecutive_end[0] = previous.end[0] + direction[0] * length;
186
+ _addConsecutive_end[1] = previous.end[1] + direction[1] * length;
187
+ _addConsecutive_end[2] = previous.end[2] + direction[2] * length;
188
+ return addBone(chain, previous.end, _addConsecutive_end, joint);
189
+ }
190
+ const _addConsecutive_end = [0, 0, 0];
191
+ /**
192
+ * Prepends a bone at the base end, extending the chain backward.
193
+ *
194
+ * This is what a follower grows with: driven by {@link forward} alone the base end is the tail.
195
+ *
196
+ * `joint` becomes the joint of the bone that was previously first, because that is the junction the
197
+ * new bone creates. The new first bone's own joint is unused, as always.
198
+ *
199
+ * @param chain the chain to prepend to, which must already have at least one bone
200
+ * @param direction the direction the new bone points, from its own start toward the existing chain. Assumed to be unit length
201
+ * @param length the length of the new bone
202
+ * @param joint the joint for the junction this creates, or a fresh unconstrained one if omitted
203
+ * @returns the prepended bone
204
+ */
205
+ function addBoneAtBase(chain, direction, length, joint = createJoint3()) {
206
+ const first = chain.bones[0];
207
+ // the bone that was first now sits at index 1, so it is the one whose joint governs the new
208
+ // junction. its old joint was the unused basebone slot
209
+ first.joint = joint;
210
+ const bone = {
211
+ start: [
212
+ first.start[0] - direction[0] * length,
213
+ first.start[1] - direction[1] * length,
214
+ first.start[2] - direction[2] * length,
215
+ ],
216
+ end: [first.start[0], first.start[1], first.start[2]],
217
+ length,
218
+ joint: createJoint3(),
219
+ };
220
+ chain.bones.unshift(bone);
221
+ chain.length += length;
222
+ chain.base[0] = bone.start[0];
223
+ chain.base[1] = bone.start[1];
224
+ chain.base[2] = bone.start[2];
225
+ chain.bestSolution.push(0, 0, 0, 0, 0, 0);
226
+ return bone;
227
+ }
228
+ /**
229
+ * Sets a ball-joint constraint: the bone may rotate within a cone of `rotor` radians about the
230
+ * direction of the bone before it.
231
+ *
232
+ * @param joint the joint to configure
233
+ * @param rotor the half-angle of the cone in radians, clamped to [0, PI]
234
+ * @returns the joint
235
+ */
236
+ function setBallJoint(joint, rotor) {
237
+ joint.type = JointType.BALL;
238
+ joint.rotor = clampAngle(rotor);
239
+ return joint;
240
+ }
241
+ /**
242
+ * Sets a hinge constraint: the bone may only rotate in the plane perpendicular to `rotationAxis`,
243
+ * and within `clockwise` / `anticlockwise` radians of `referenceAxis` in that plane.
244
+ *
245
+ * `referenceAxis` is projected onto the hinge plane and normalized, so it need only be roughly
246
+ * perpendicular. Pass PI for both limits for a hinge that spins freely.
247
+ *
248
+ * @param joint the joint to configure
249
+ * @param type either {@link JointType.GLOBAL_HINGE} (axes in world space) or {@link JointType.LOCAL_HINGE} (axes relative to the previous bone)
250
+ * @param rotationAxis the hinge axis, assumed to be unit length
251
+ * @param clockwise the clockwise limit in radians, clamped to [0, PI]
252
+ * @param anticlockwise the anticlockwise limit in radians, clamped to [0, PI]
253
+ * @param referenceAxis the zero direction the limits are measured from
254
+ * @returns the joint
255
+ */
256
+ function setHingeJoint(joint, type, rotationAxis, clockwise, anticlockwise, referenceAxis) {
257
+ joint.type = type;
258
+ joint.clockwise = clampAngle(clockwise);
259
+ joint.anticlockwise = clampAngle(anticlockwise);
260
+ setUnitAxis(joint.rotationAxis, rotationAxis);
261
+ orthonormalize(joint.referenceAxis, referenceAxis, joint.rotationAxis);
262
+ return joint;
263
+ }
264
+ /**
265
+ * Confines the first bone to a cone of `rotor` radians about `axis`.
266
+ *
267
+ * @param chain the chain to configure
268
+ * @param type either {@link BaseboneConstraintType.GLOBAL_ROTOR} or {@link BaseboneConstraintType.LOCAL_ROTOR}
269
+ * @param axis the cone axis, assumed to be unit length
270
+ * @param rotor the half-angle of the cone in radians, clamped to [0, PI]
271
+ * @returns the chain
272
+ */
273
+ function setBaseboneRotorConstraint(chain, type, axis, rotor) {
274
+ chain.baseboneConstraintType = type;
275
+ chain.baseboneRotor = clampAngle(rotor);
276
+ setUnitAxis(chain.baseboneAxis, axis);
277
+ copy(chain.baseboneWorldAxis, chain.baseboneAxis);
278
+ return chain;
279
+ }
280
+ /**
281
+ * Confines the first bone to the plane perpendicular to `rotationAxis`, within `clockwise` /
282
+ * `anticlockwise` radians of `referenceAxis`.
283
+ *
284
+ * @param chain the chain to configure
285
+ * @param type either {@link BaseboneConstraintType.GLOBAL_HINGE} or {@link BaseboneConstraintType.LOCAL_HINGE}
286
+ * @param rotationAxis the hinge axis, assumed to be unit length
287
+ * @param clockwise the clockwise limit in radians, clamped to [0, PI]
288
+ * @param anticlockwise the anticlockwise limit in radians, clamped to [0, PI]
289
+ * @param referenceAxis the zero direction the limits are measured from
290
+ * @returns the chain
291
+ */
292
+ function setBaseboneHingeConstraint(chain, type, rotationAxis, clockwise, anticlockwise, referenceAxis) {
293
+ chain.baseboneConstraintType = type;
294
+ chain.baseboneClockwise = clampAngle(clockwise);
295
+ chain.baseboneAnticlockwise = clampAngle(anticlockwise);
296
+ setUnitAxis(chain.baseboneAxis, rotationAxis);
297
+ orthonormalize(chain.baseboneReferenceAxis, referenceAxis, chain.baseboneAxis);
298
+ copy(chain.baseboneWorldAxis, chain.baseboneAxis);
299
+ copy(chain.baseboneWorldReferenceAxis, chain.baseboneReferenceAxis);
300
+ return chain;
301
+ }
302
+ /**
303
+ * Moves the chain's pinned base, without moving the bones.
304
+ *
305
+ * The next {@link backward} or {@link solve} pulls the chain to it.
306
+ */
307
+ function setBaseLocation(chain, base) {
308
+ chain.base[0] = base[0];
309
+ chain.base[1] = base[1];
310
+ chain.base[2] = base[2];
311
+ return chain;
312
+ }
313
+ /**
314
+ * Lays the chain out straight from its base along `direction`, discarding the current pose.
315
+ *
316
+ * A dead-straight chain is the worst starting pose for {@link solve} - see the note there. Bend
317
+ * `direction` slightly between bones instead if the chain will be solved cold.
318
+ */
319
+ function straighten(chain, direction) {
320
+ const bones = chain.bones;
321
+ let x = chain.base[0];
322
+ let y = chain.base[1];
323
+ let z = chain.base[2];
324
+ for (let i = 0; i < bones.length; i++) {
325
+ const bone = bones[i];
326
+ bone.start[0] = x;
327
+ bone.start[1] = y;
328
+ bone.start[2] = z;
329
+ x += direction[0] * bone.length;
330
+ y += direction[1] * bone.length;
331
+ z += direction[2] * bone.length;
332
+ bone.end[0] = x;
333
+ bone.end[1] = y;
334
+ bone.end[2] = z;
335
+ }
336
+ return chain;
337
+ }
338
+ /**
339
+ * Writes the end effector's position - the last bone's end - into `out`.
340
+ *
341
+ * A chain with no bones has its base as its effector, matching {@link solve}, which treats an empty
342
+ * chain as a supported state rather than an error.
343
+ */
344
+ function getEffector(out, chain) {
345
+ const count = chain.bones.length;
346
+ return copy(out, count === 0 ? chain.base : chain.bones[count - 1].end);
347
+ }
348
+ /** Writes the unit direction of bone `index`, from its start toward its end, into `out`. */
349
+ function getBoneDirection(out, chain, index) {
350
+ const bone = chain.bones[index];
351
+ subtract(out, bone.end, bone.start);
352
+ return normalizeOr(out, UP);
353
+ }
354
+ /**
355
+ * Writes the rotation taking `up` onto the direction of bone `index` into `out`.
356
+ *
357
+ * Use it to orient a mesh along a bone, passing whichever axis the mesh is modelled along - `up`
358
+ * is `[0, 1, 0]` for a cylinder or capsule built along Y. The roll about the bone is arbitrary.
359
+ */
360
+ function getBoneRotation(out, chain, index, up) {
361
+ getBoneDirection(_boneRotation_direction, chain, index);
362
+ return rotationTo(out, up, _boneRotation_direction);
363
+ }
364
+ const _boneRotation_direction = [0, 0, 0];
365
+ /** Whether `target` is within reach of the chain's base, so a solve can place the effector exactly on it. */
366
+ function isReachable(chain, target) {
367
+ return squaredDistance(chain.base, target) <= chain.length * chain.length;
368
+ }
369
+ /**
370
+ * The forward pass: snaps the end effector onto `target` and drags the rest of the chain after it.
371
+ *
372
+ * A non-finite target leaves the chain untouched rather than poisoning it.
373
+ *
374
+ * The base is not pinned, so the whole chain moves. Run alone once per frame this is a follower
375
+ * rather than a solver - a rope or tentacle whose head leads. Pair it with {@link backward}, or use
376
+ * {@link solve}, to keep the base put.
377
+ *
378
+ * @param chain the chain to move, mutated in place
379
+ * @param target where the end effector should go
380
+ * @returns the chain
381
+ */
382
+ function forward(chain, target) {
383
+ const bones = chain.bones;
384
+ const count = bones.length;
385
+ if (count === 0)
386
+ return chain;
387
+ // a non-finite target would be written straight into the effector and spread down the chain
388
+ if (!Number.isFinite(target[0]) || !Number.isFinite(target[1]) || !Number.isFinite(target[2]))
389
+ return chain;
390
+ // snap the effector onto the target. the rest of the pass follows from it
391
+ const effector = bones[count - 1];
392
+ effector.end[0] = target[0];
393
+ effector.end[1] = target[1];
394
+ effector.end[2] = target[2];
395
+ // the outer-to-inner direction of the bone one step further out, which is what a joint on this
396
+ // bone is constrained against on this pass. undefined for the effector itself.
397
+ let hasReference = false;
398
+ for (let i = count - 1; i >= 0; i--) {
399
+ const bone = bones[i];
400
+ const start = bone.start;
401
+ const end = bone.end;
402
+ // this bone's outer-to-inner direction
403
+ let dx = start[0] - end[0];
404
+ let dy = start[1] - end[1];
405
+ let dz = start[2] - end[2];
406
+ const squaredLength = dx * dx + dy * dy + dz * dz;
407
+ if (!hasDirection(squaredLength)) {
408
+ // the two ends coincide, so there is no direction to preserve. carry on along the bone
409
+ // further out, or straight up if this is the effector
410
+ dx = hasReference ? _pass_reference[0] : UP[0];
411
+ dy = hasReference ? _pass_reference[1] : UP[1];
412
+ dz = hasReference ? _pass_reference[2] : UP[2];
413
+ }
414
+ else {
415
+ const inverseLength = 1 / Math.sqrt(squaredLength);
416
+ dx *= inverseLength;
417
+ dy *= inverseLength;
418
+ dz *= inverseLength;
419
+ }
420
+ constrainForward(chain, i, dx, dy, dz, hasReference);
421
+ dx = _pass_direction[0];
422
+ dy = _pass_direction[1];
423
+ dz = _pass_direction[2];
424
+ const x = end[0] + dx * bone.length;
425
+ const y = end[1] + dy * bone.length;
426
+ const z = end[2] + dz * bone.length;
427
+ start[0] = x;
428
+ start[1] = y;
429
+ start[2] = z;
430
+ // the previous bone shares this point
431
+ if (i > 0) {
432
+ const previousEnd = bones[i - 1].end;
433
+ previousEnd[0] = x;
434
+ previousEnd[1] = y;
435
+ previousEnd[2] = z;
436
+ }
437
+ _pass_reference[0] = dx;
438
+ _pass_reference[1] = dy;
439
+ _pass_reference[2] = dz;
440
+ hasReference = true;
441
+ }
442
+ return chain;
443
+ }
444
+ /**
445
+ * The backward pass: pins the base and pushes each bone outward from it.
446
+ *
447
+ * Basebone constraints and hinge reference-axis limits are applied here only - the forward pass
448
+ * skips the latter, because clamping on both degrades the solution.
449
+ *
450
+ * @param chain the chain to move, mutated in place
451
+ * @param base where the base should go, used only when `chain.fixedBase` is set
452
+ * @returns the chain
453
+ */
454
+ function backward(chain, base) {
455
+ const bones = chain.bones;
456
+ const count = bones.length;
457
+ if (count === 0)
458
+ return chain;
459
+ if (!Number.isFinite(base[0]) || !Number.isFinite(base[1]) || !Number.isFinite(base[2]))
460
+ return chain;
461
+ if (chain.fixedBase) {
462
+ const start = bones[0].start;
463
+ start[0] = base[0];
464
+ start[1] = base[1];
465
+ start[2] = base[2];
466
+ }
467
+ let hasReference = false;
468
+ for (let i = 0; i < count; i++) {
469
+ const bone = bones[i];
470
+ const start = bone.start;
471
+ const end = bone.end;
472
+ // this bone's inner-to-outer direction
473
+ let dx = end[0] - start[0];
474
+ let dy = end[1] - start[1];
475
+ let dz = end[2] - start[2];
476
+ const squaredLength = dx * dx + dy * dy + dz * dz;
477
+ if (!hasDirection(squaredLength)) {
478
+ dx = hasReference ? _pass_reference[0] : UP[0];
479
+ dy = hasReference ? _pass_reference[1] : UP[1];
480
+ dz = hasReference ? _pass_reference[2] : UP[2];
481
+ }
482
+ else {
483
+ const inverseLength = 1 / Math.sqrt(squaredLength);
484
+ dx *= inverseLength;
485
+ dy *= inverseLength;
486
+ dz *= inverseLength;
487
+ }
488
+ constrainBackward(chain, i, dx, dy, dz, hasReference);
489
+ dx = _pass_direction[0];
490
+ dy = _pass_direction[1];
491
+ dz = _pass_direction[2];
492
+ // when the base is free, the first bone swings about its end rather than its start
493
+ if (i === 0 && !chain.fixedBase) {
494
+ start[0] = end[0] - dx * bone.length;
495
+ start[1] = end[1] - dy * bone.length;
496
+ start[2] = end[2] - dz * bone.length;
497
+ }
498
+ const x = start[0] + dx * bone.length;
499
+ const y = start[1] + dy * bone.length;
500
+ const z = start[2] + dz * bone.length;
501
+ end[0] = x;
502
+ end[1] = y;
503
+ end[2] = z;
504
+ if (i < count - 1) {
505
+ const nextStart = bones[i + 1].start;
506
+ nextStart[0] = x;
507
+ nextStart[1] = y;
508
+ nextStart[2] = z;
509
+ }
510
+ _pass_reference[0] = dx;
511
+ _pass_reference[1] = dy;
512
+ _pass_reference[2] = dz;
513
+ hasReference = true;
514
+ }
515
+ return chain;
516
+ }
517
+ /**
518
+ * One full FABRIK iteration - {@link forward} then {@link backward}. Often enough on its own for an
519
+ * unconstrained chain reaching a nearby target. use {@link solve} to iterate to a tolerance.
520
+ *
521
+ * @param chain the chain to move, mutated in place
522
+ * @param target where the end effector should go
523
+ * @returns the distance from the effector to `target` afterwards
524
+ */
525
+ function iterate(chain, target) {
526
+ if (chain.bones.length === 0)
527
+ return Number.POSITIVE_INFINITY;
528
+ forward(chain, target);
529
+ backward(chain, chain.base);
530
+ return distance(chain.bones[chain.bones.length - 1].end, target);
531
+ }
532
+ /**
533
+ * Solves the chain for `target`, iterating until it is close enough, stops improving, or runs out
534
+ * of attempts.
535
+ *
536
+ * A constrained chain does not converge monotonically, so the best pose seen is kept and restored.
537
+ *
538
+ * A dead-straight chain is the worst case to solve cold: every direction lies on one line, leaving
539
+ * nothing sideways to bend it with. Give it a slight bend if the first solve matters.
540
+ *
541
+ * @param chain the chain to solve, mutated in place
542
+ * @param target where the end effector should go
543
+ * @returns the distance from the effector to `target`, also stored as `chain.solveDistance`
544
+ */
545
+ function solve(chain, target) {
546
+ const count = chain.bones.length;
547
+ if (count === 0) {
548
+ chain.solveDistance = Number.POSITIVE_INFINITY;
549
+ return chain.solveDistance;
550
+ }
551
+ // in case bones were pushed onto the chain directly rather than through addBone
552
+ if (chain.bestSolution.length < count * 6) {
553
+ chain.bestSolution.length = count * 6;
554
+ chain.bestSolution.fill(0);
555
+ }
556
+ let best = Number.POSITIVE_INFINITY;
557
+ let previous = Number.POSITIVE_INFINITY;
558
+ for (let i = 0; i < chain.maxIterations; i++) {
559
+ const distance = iterate(chain, target);
560
+ if (distance < best) {
561
+ best = distance;
562
+ saveSolution(chain);
563
+ if (distance <= chain.solveDistanceThreshold)
564
+ break;
565
+ }
566
+ else if (Math.abs(distance - previous) < chain.minIterationChange) {
567
+ // ground to a halt - more iterations will not improve on what we already have
568
+ break;
569
+ }
570
+ previous = distance;
571
+ }
572
+ if (best !== Number.POSITIVE_INFINITY) {
573
+ restoreSolution(chain);
574
+ }
575
+ chain.solveDistance = best;
576
+ return best;
577
+ }
578
+ /** Creates an empty structure. */
579
+ function createStructure3() {
580
+ return { chains: [], connections: [] };
581
+ }
582
+ /**
583
+ * Adds a chain that hangs off nothing, solving directly for the structure's target.
584
+ *
585
+ * @returns the index of the added chain
586
+ */
587
+ function addChain(structure, chain) {
588
+ structure.chains.push(chain);
589
+ structure.connections.push({ hostChain: -1, hostBone: 0, point: BoneConnectionPoint.END });
590
+ return structure.chains.length - 1;
591
+ }
592
+ /**
593
+ * Adds a chain whose base is pinned to one end of a bone in a chain already in the structure.
594
+ *
595
+ * Add the host first: {@link solveStructure} walks chains in insertion order, so a chain always
596
+ * sees its host already posed.
597
+ *
598
+ * @param structure the structure to add to
599
+ * @param chain the chain to add
600
+ * @param hostChain the index of the chain to hang off
601
+ * @param hostBone the index of the bone within that chain
602
+ * @param point which end of the host bone to attach to
603
+ * @returns the index of the added chain
604
+ */
605
+ function connectChain(structure, chain, hostChain, hostBone, point) {
606
+ structure.chains.push(chain);
607
+ structure.connections.push({ hostChain, hostBone, point });
608
+ return structure.chains.length - 1;
609
+ }
610
+ /**
611
+ * Solves every chain in the structure.
612
+ *
613
+ * A connected chain has its base moved onto its host bone's connection point and any `LOCAL_`
614
+ * basebone constraint resolved through the host bone's frame first. A chain with
615
+ * `useEmbeddedTarget` set solves for its own `embeddedTarget` instead - how a walker's legs each
616
+ * reach their own foothold.
617
+ *
618
+ * @param structure the structure to solve, mutated in place
619
+ * @param target the target for every chain that does not use an embedded target
620
+ */
621
+ function solveStructure(structure, target) {
622
+ const chains = structure.chains;
623
+ for (let i = 0; i < chains.length; i++) {
624
+ const chain = chains[i];
625
+ const connection = structure.connections[i];
626
+ if (connection.hostChain >= 0) {
627
+ const host = chains[connection.hostChain];
628
+ const hostBone = host.bones[connection.hostBone];
629
+ setBaseLocation(chain, connection.point === BoneConnectionPoint.START ? hostBone.start : hostBone.end);
630
+ // being connected IS what fixes this chain's base - a connected chain with a free base
631
+ // would drift away from the host it is supposed to hang off, so the flag is not the
632
+ // caller's to clear here
633
+ chain.fixedBase = true;
634
+ const type = chain.baseboneConstraintType;
635
+ if (type === BaseboneConstraintType.LOCAL_ROTOR || type === BaseboneConstraintType.LOCAL_HINGE) {
636
+ // the constraint is expressed in the host bone's frame, so rotate it into the world
637
+ getBoneDirection(_structure_direction, host, connection.hostBone);
638
+ basisFromDirection(_structure_basis, _structure_direction);
639
+ transformMat3(chain.baseboneWorldAxis, chain.baseboneAxis, _structure_basis);
640
+ normalize(chain.baseboneWorldAxis, chain.baseboneWorldAxis);
641
+ if (type === BaseboneConstraintType.LOCAL_HINGE) {
642
+ transformMat3(chain.baseboneWorldReferenceAxis, chain.baseboneReferenceAxis, _structure_basis);
643
+ orthonormalize(chain.baseboneWorldReferenceAxis, chain.baseboneWorldReferenceAxis, chain.baseboneWorldAxis);
644
+ }
645
+ }
646
+ }
647
+ solve(chain, chain.useEmbeddedTarget ? chain.embeddedTarget : target);
648
+ }
649
+ }
650
+ const _structure_direction = [0, 0, 0];
651
+ const _structure_basis = /* @__PURE__ */ create();
652
+ /* internals */
653
+ const UP = [0, 1, 0];
654
+ // the direction the current pass is constraining against - the previous bone's, whichever way the
655
+ // pass is walking - and the constrained result handed back from `constrain*`
656
+ const _pass_reference = [0, 0, 0];
657
+ const _pass_direction = [0, 0, 0];
658
+ const _constrain_axis = [0, 0, 0];
659
+ const _constrain_reference = [0, 0, 0];
660
+ const _constrain_basis = /* @__PURE__ */ create();
661
+ function clampAngle(radians) {
662
+ return radians < 0 ? 0 : radians > Math.PI ? Math.PI : radians;
663
+ }
664
+ /** Writes the component of `a` perpendicular to the unit vector `axis`, normalized. */
665
+ function orthonormalize(out, a, axis) {
666
+ // the component of `a` perpendicular to `axis`
667
+ scaleAndAdd(out, a, axis, -dot(a, axis));
668
+ if (!hasDirection(squaredLength(out))) {
669
+ // `a` is parallel to `axis`, so it names no direction in the plane - any will do
670
+ return perpendicular(out, axis);
671
+ }
672
+ return normalize(out, out);
673
+ }
674
+ /** Normalizes `out` in place, falling back to `fallback` when it has no length. */
675
+ function normalizeOr(out, fallback) {
676
+ if (!hasDirection(squaredLength(out))) {
677
+ return copy(out, fallback);
678
+ }
679
+ return normalize(out, out);
680
+ }
681
+ /**
682
+ * Flattens a direction into a hinge's plane.
683
+ *
684
+ * A direction parallel to the hinge axis projects to nothing, leaving no in-plane direction to
685
+ * normalize. Fall back to the hinge's reference axis, which lies in the plane by construction.
686
+ */
687
+ function projectOntoHinge(out, x, y, z, axis, referenceAxis) {
688
+ const d = x * axis[0] + y * axis[1] + z * axis[2];
689
+ out[0] = x - axis[0] * d;
690
+ out[1] = y - axis[1] * d;
691
+ out[2] = z - axis[2] * d;
692
+ if (!hasDirection(squaredLength(out))) {
693
+ return copy(out, referenceAxis);
694
+ }
695
+ return normalize(out, out);
696
+ }
697
+ /** Rotates the unit vector `a` about the unit vector `axis` by `radians` (Rodrigues). */
698
+ function rotateAboutAxis(out, a, axis, radians) {
699
+ const ax = a[0];
700
+ const ay = a[1];
701
+ const az = a[2];
702
+ const kx = axis[0];
703
+ const ky = axis[1];
704
+ const kz = axis[2];
705
+ const c = Math.cos(radians);
706
+ const s = Math.sin(radians);
707
+ const d = (kx * ax + ky * ay + kz * az) * (1 - c);
708
+ out[0] = ax * c + (ky * az - kz * ay) * s + kx * d;
709
+ out[1] = ay * c + (kz * ax - kx * az) * s + ky * d;
710
+ out[2] = az * c + (kx * ay - ky * ax) * s + kz * d;
711
+ return out;
712
+ }
713
+ /** Flattens into the hinge plane, then clamps the swing about the reference axis. Writes `_pass_direction`. */
714
+ function constrainHinge(x, y, z, axis, referenceAxis, clockwise, anticlockwise) {
715
+ projectOntoHinge(_pass_direction, x, y, z, axis, referenceAxis);
716
+ if (clockwise >= Math.PI && anticlockwise >= Math.PI)
717
+ return;
718
+ // anticlockwise about the hinge axis is positive
719
+ const signed = signedAngle(referenceAxis, _pass_direction, axis);
720
+ if (signed > anticlockwise) {
721
+ rotateAboutAxis(_pass_direction, referenceAxis, axis, anticlockwise);
722
+ }
723
+ else if (signed < -clockwise) {
724
+ rotateAboutAxis(_pass_direction, referenceAxis, axis, -clockwise);
725
+ }
726
+ }
727
+ /**
728
+ * Builds an orthonormal basis with `direction` as its Z axis, into `out`.
729
+ *
730
+ * Used to express a joint axis relative to the bone it hangs off. The X and Y axes are picked
731
+ * arbitrarily but deterministically (the Frisvad method), because a bone frame has no meaningful
732
+ * "up" to anchor them to - unlike a look-at matrix, which takes one and fails when the direction is
733
+ * parallel to it.
734
+ *
735
+ * No choice of X and Y varies continuously over the whole sphere. This one is continuous everywhere
736
+ * except at `direction` = (0, 0, -1), where the basis flips, so a local hinge whose parent swings
737
+ * through there will pop.
738
+ */
739
+ function basisFromDirection(out, direction) {
740
+ const x = direction[0];
741
+ const y = direction[1];
742
+ const z = direction[2];
743
+ if (z < -0.9999999) {
744
+ // the antipode, where the construction below divides by zero
745
+ out[0] = 0;
746
+ out[1] = -1;
747
+ out[2] = 0;
748
+ out[3] = -1;
749
+ out[4] = 0;
750
+ out[5] = 0;
751
+ out[6] = x;
752
+ out[7] = y;
753
+ out[8] = z;
754
+ return out;
755
+ }
756
+ const a = 1 / (1 + z);
757
+ const b = -x * y * a;
758
+ // column 0 - the X axis
759
+ out[0] = 1 - x * x * a;
760
+ out[1] = b;
761
+ out[2] = -x;
762
+ // column 1 - the Y axis
763
+ out[3] = b;
764
+ out[4] = 1 - y * y * a;
765
+ out[5] = -y;
766
+ // column 2 - the Z axis, which is `direction` itself
767
+ out[6] = x;
768
+ out[7] = y;
769
+ out[8] = z;
770
+ return out;
771
+ }
772
+ /** Resolves a local hinge's axes into world space through the frame of bone `index - 1`. */
773
+ function resolveLocalHinge(chain, index, joint) {
774
+ getBoneDirection(_constrain_axis, chain, index - 1);
775
+ basisFromDirection(_constrain_basis, _constrain_axis);
776
+ transformMat3(_constrain_axis, joint.rotationAxis, _constrain_basis);
777
+ normalize(_constrain_axis, _constrain_axis);
778
+ transformMat3(_constrain_reference, joint.referenceAxis, _constrain_basis);
779
+ orthonormalize(_constrain_reference, _constrain_reference, _constrain_axis);
780
+ }
781
+ /**
782
+ * Constrains a bone's outer-to-inner direction during the forward pass.
783
+ *
784
+ * Two joints limit it. This bone's own hinge confines it to a plane. The rotor bounding its bend
785
+ * away from the bone further out belongs to the joint between the two, which is the next bone's, so
786
+ * a rotor means the same angle on both passes.
787
+ *
788
+ * Hinge reference-axis limits are left to the backward pass, because clamping on both lands on
789
+ * worse poses.
790
+ *
791
+ * Writes the result into `_pass_direction`, which starts out holding the unconstrained direction,
792
+ * so a branch that does not apply can simply return.
793
+ */
794
+ function constrainForward(chain, index, x, y, z, hasReference) {
795
+ // start from the unconstrained direction. each branch below narrows it if it applies
796
+ _pass_direction[0] = x;
797
+ _pass_direction[1] = y;
798
+ _pass_direction[2] = z;
799
+ // first: whatever confines this bone's own orientation, neighbours aside
800
+ if (index === 0) {
801
+ const type = chain.baseboneConstraintType;
802
+ if (type === BaseboneConstraintType.GLOBAL_HINGE || type === BaseboneConstraintType.LOCAL_HINGE) {
803
+ projectOntoHinge(_pass_direction, x, y, z, chain.baseboneWorldAxis, chain.baseboneWorldReferenceAxis);
804
+ return;
805
+ }
806
+ // a basebone rotor is measured against the base's own axis, which this pass knows nothing
807
+ // about - the backward pass applies it
808
+ }
809
+ else {
810
+ const joint = chain.bones[index].joint;
811
+ if (joint.type === JointType.GLOBAL_HINGE) {
812
+ projectOntoHinge(_pass_direction, x, y, z, joint.rotationAxis, joint.referenceAxis);
813
+ return;
814
+ }
815
+ if (joint.type === JointType.LOCAL_HINGE) {
816
+ resolveLocalHinge(chain, index, joint);
817
+ projectOntoHinge(_pass_direction, x, y, z, _constrain_axis, _constrain_reference);
818
+ return;
819
+ }
820
+ }
821
+ // the rotor shared with the bone one step further out. the end effector has no such bone
822
+ if (!hasReference)
823
+ return;
824
+ const outer = chain.bones[index + 1].joint;
825
+ if (outer.type !== JointType.BALL || outer.rotor >= Math.PI)
826
+ return;
827
+ _pass_direction[0] = x;
828
+ _pass_direction[1] = y;
829
+ _pass_direction[2] = z;
830
+ rotateTowards(_pass_direction, _pass_reference, _pass_direction, outer.rotor);
831
+ }
832
+ /**
833
+ * Constrains a bone's inner-to-outer direction during the backward pass.
834
+ *
835
+ * Writes the result into `_pass_direction`, which starts out holding the unconstrained direction,
836
+ * so a branch that does not apply can simply return.
837
+ */
838
+ function constrainBackward(chain, index, x, y, z, hasReference) {
839
+ // start from the unconstrained direction. each branch below narrows it if it applies
840
+ _pass_direction[0] = x;
841
+ _pass_direction[1] = y;
842
+ _pass_direction[2] = z;
843
+ if (index === 0) {
844
+ switch (chain.baseboneConstraintType) {
845
+ case BaseboneConstraintType.GLOBAL_ROTOR:
846
+ case BaseboneConstraintType.LOCAL_ROTOR: {
847
+ if (chain.baseboneRotor >= Math.PI)
848
+ return;
849
+ rotateTowards(_pass_direction, chain.baseboneWorldAxis, _pass_direction, chain.baseboneRotor);
850
+ return;
851
+ }
852
+ case BaseboneConstraintType.GLOBAL_HINGE:
853
+ case BaseboneConstraintType.LOCAL_HINGE: {
854
+ constrainHinge(x, y, z, chain.baseboneWorldAxis, chain.baseboneWorldReferenceAxis, chain.baseboneClockwise, chain.baseboneAnticlockwise);
855
+ return;
856
+ }
857
+ default:
858
+ return;
859
+ }
860
+ }
861
+ const joint = chain.bones[index].joint;
862
+ switch (joint.type) {
863
+ case JointType.BALL: {
864
+ if (!hasReference || joint.rotor >= Math.PI)
865
+ return;
866
+ rotateTowards(_pass_direction, _pass_reference, _pass_direction, joint.rotor);
867
+ return;
868
+ }
869
+ case JointType.GLOBAL_HINGE: {
870
+ constrainHinge(x, y, z, joint.rotationAxis, joint.referenceAxis, joint.clockwise, joint.anticlockwise);
871
+ return;
872
+ }
873
+ case JointType.LOCAL_HINGE: {
874
+ resolveLocalHinge(chain, index, joint);
875
+ constrainHinge(x, y, z, _constrain_axis, _constrain_reference, joint.clockwise, joint.anticlockwise);
876
+ return;
877
+ }
878
+ default:
879
+ return;
880
+ }
881
+ }
882
+ function saveSolution(chain) {
883
+ const bones = chain.bones;
884
+ const solution = chain.bestSolution;
885
+ for (let i = 0; i < bones.length; i++) {
886
+ const bone = bones[i];
887
+ const offset = i * 6;
888
+ solution[offset] = bone.start[0];
889
+ solution[offset + 1] = bone.start[1];
890
+ solution[offset + 2] = bone.start[2];
891
+ solution[offset + 3] = bone.end[0];
892
+ solution[offset + 4] = bone.end[1];
893
+ solution[offset + 5] = bone.end[2];
894
+ }
895
+ }
896
+ function restoreSolution(chain) {
897
+ const bones = chain.bones;
898
+ const solution = chain.bestSolution;
899
+ for (let i = 0; i < bones.length; i++) {
900
+ const bone = bones[i];
901
+ const offset = i * 6;
902
+ bone.start[0] = solution[offset];
903
+ bone.start[1] = solution[offset + 1];
904
+ bone.start[2] = solution[offset + 2];
905
+ bone.end[0] = solution[offset + 3];
906
+ bone.end[1] = solution[offset + 4];
907
+ bone.end[2] = solution[offset + 5];
908
+ }
909
+ }
910
+
911
+ export { BaseboneConstraintType, BoneConnectionPoint, JointType, addBone, addBoneAtBase, addChain, addConsecutiveBone, backward, connectChain, createChain3, createJoint3, createStructure3, forward, getBoneDirection, getBoneRotation, getEffector, isReachable, iterate, setBallJoint, setBaseLocation, setBaseboneHingeConstraint, setBaseboneRotorConstraint, setHingeJoint, solve, solveStructure, straighten };
912
+ //# sourceMappingURL=fabrik3.js.map