Skip to main content

box3d_rust/math_functions/
internal.rs

1// Internal helpers from math_internal.h needed by tests (and later by collision).
2// Part of the math_functions module.
3
4use super::*;
5
6/// Assume v is a unit vector. (math_internal.h: b3ArbitraryPerp)
7pub fn arbitrary_perp(v: Vec3) -> Vec3 {
8    // Suppose vector a has all equal components and is a unit vector: a = (s, s, s)
9    // Then 3*s*s = 1, s = sqrt(1/3) = 0.57735. This means that at least one component
10    // of a unit vector must be greater or equal to 0.57735.
11    let p = if v.x < -0.5 || 0.5 < v.x {
12        // x is non-zero and it should not go into the x component
13        // dot([ay + bz, cx, dx], [x, y, z]) = ayx + bzx + cxy + dzx
14        // for the dot product to be zero need: c = -a, d = -b
15        let a = 0.67;
16        let b = -0.42;
17        Vec3 {
18            x: a * v.y + b * v.z,
19            y: -a * v.x,
20            z: -b * v.x,
21        }
22    } else if v.y < -0.5 || 0.5 < v.y {
23        // y is non-zero and it should not go into the y component
24        // p = [ay, bx + cz, dy]
25        // axy + bxy + cyz + dyz = 0
26        // b = -a, d = -c
27        let a = 0.67;
28        let c = -0.42;
29        Vec3 {
30            x: a * v.y,
31            y: -a * v.x + c * v.z,
32            z: -c * v.y,
33        }
34    } else {
35        // This would trip if the input is not a unit vector
36        debug_assert!(v.z < -0.5 || 0.5 < v.z);
37
38        // z is non-zero and it should not go into the z component
39        // p = [az, bz, cx + dy]
40        // axz + byz + cxz + dyz = 0
41        // c = -a, d = -b
42        let a = 0.67;
43        let b = -0.42;
44        Vec3 {
45            x: a * v.z,
46            y: b * v.z,
47            z: -a * v.x - b * v.y,
48        }
49    };
50
51    debug_assert!(length_squared(p) > 0.1);
52    debug_assert!(abs_float(dot(p, v)) < 100.0 * f32::EPSILON);
53
54    normalize(p)
55}
56
57/// Scalar triple product a · (b × c). (math_internal.h: b3ScalarTripleProduct)
58pub fn scalar_triple_product(a: Vec3, b: Vec3, c: Vec3) -> f32 {
59    let d = Vec3 {
60        x: b.y * c.z - b.z * c.y,
61        y: b.z * c.x - b.x * c.z,
62        z: b.x * c.y - b.y * c.x,
63    };
64    a.x * d.x + a.y * d.y + a.z * d.z
65}
66
67/// √3. (math_internal.h: B3_SQRT3)
68pub const SQRT3: f32 = 1.732050808;
69
70/// Empty AABB (inverted bounds). (math_internal.h: B3_BOUNDS3_EMPTY)
71pub const BOUNDS3_EMPTY: Aabb = Aabb {
72    lower_bound: Vec3 {
73        x: f32::MAX,
74        y: f32::MAX,
75        z: f32::MAX,
76    },
77    upper_bound: Vec3 {
78        x: -f32::MAX,
79        y: -f32::MAX,
80        z: -f32::MAX,
81    },
82};
83
84/// Align `x` up to a multiple of 8. (math_internal.h: b3AlignUp8)
85pub fn align_up8(x: usize) -> usize {
86    (x + 7) & !7
87}
88
89/// Component-wise product sum used for AABB extent under rotation.
90/// (math_internal.h: b3ModifiedCross)
91pub fn modified_cross(a: Vec3, b: Vec3) -> Vec3 {
92    Vec3 {
93        x: a.y * b.z + a.z * b.y,
94        y: a.z * b.x + a.x * b.z,
95        z: a.x * b.y + a.y * b.x,
96    }
97}
98
99/// 2D dot product. (math_internal.h: b3Dot2)
100pub fn dot2(v1: Vec2, v2: Vec2) -> f32 {
101    v1.x * v2.x + v1.y * v2.y
102}
103
104/// 2D length. (math_internal.h: b3Length2)
105pub fn length2(v: Vec2) -> f32 {
106    dot2(v, v).sqrt()
107}
108
109/// 2D length squared. (math_internal.h: b3LengthSquared2)
110pub fn length_squared2(v: Vec2) -> f32 {
111    dot2(v, v)
112}
113
114/// 2D vector addition. (math_internal.h: b3Add2)
115pub fn add2(a: Vec2, b: Vec2) -> Vec2 {
116    Vec2 {
117        x: a.x + b.x,
118        y: a.y + b.y,
119    }
120}
121
122/// 2D vector subtraction. (math_internal.h: b3Sub2)
123pub fn sub2(a: Vec2, b: Vec2) -> Vec2 {
124    Vec2 {
125        x: a.x - b.x,
126        y: a.y - b.y,
127    }
128}
129
130/// 2D scalar multiply. (math_internal.h: b3MulSV2)
131pub fn mul_sv2(s: f32, v: Vec2) -> Vec2 {
132    Vec2 {
133        x: s * v.x,
134        y: s * v.y,
135    }
136}
137
138/// 2D cross product (scalar). (math_internal.h: b3Cross2)
139pub fn cross2(a: Vec2, b: Vec2) -> f32 {
140    a.x * b.y - a.y * b.x
141}
142
143/// 2D distance squared. (math_internal.h: b3DistanceSquared2)
144pub fn distance_squared2(a: Vec2, b: Vec2) -> f32 {
145    let dx = b.x - a.x;
146    let dy = b.y - a.y;
147    dx * dx + dy * dy
148}
149
150/// Unit normal from three points. (math_internal.h: b3MakeNormalFromPoints)
151pub fn make_normal_from_points(point1: Vec3, point2: Vec3, point3: Vec3) -> Vec3 {
152    normalize(cross(sub(point2, point1), sub(point3, point1)))
153}
154
155/// Index of the largest component. (math_internal.h: b3MaxElementIndex)
156pub fn max_element_index(v: Vec3) -> i32 {
157    if v.x < v.y {
158        if v.y < v.z {
159            2
160        } else {
161            1
162        }
163    } else if v.x < v.z {
164        2
165    } else {
166        0
167    }
168}
169
170/// Major axis of a vector (same as [`max_element_index`]). (math_internal.h: b3MajorAxis)
171pub fn major_axis(v: Vec3) -> i32 {
172    max_element_index(v)
173}
174
175/// Get a Vec3 component by index. (math_internal.h: b3GetByIndex)
176pub fn get_by_index(v: Vec3, index: i32) -> f32 {
177    debug_assert!((0..3).contains(&index));
178    match index {
179        0 => v.x,
180        1 => v.y,
181        2 => v.z,
182        _ => unreachable!(),
183    }
184}
185
186/// Negative if `p` is below the triangle v1-v2-v3. (math_internal.h: b3SignedVolume)
187pub fn signed_volume(v1: Vec3, v2: Vec3, v3: Vec3, p: Vec3) -> f32 {
188    let e1 = sub(v2, v1);
189    let e2 = sub(v3, v1);
190    let n = cross(e1, e2);
191    dot(n, sub(p, v1))
192}
193
194/// 2π. (math_internal.h: B3_TWO_PI)
195pub const TWO_PI: f32 = 6.283185307;
196
197/// Diagonal matrix. (math_internal.h: b3MakeDiagonalMatrix)
198pub fn make_diagonal_matrix(a: f32, b: f32, c: f32) -> Matrix3 {
199    Matrix3 {
200        cx: Vec3 {
201            x: a,
202            y: 0.0,
203            z: 0.0,
204        },
205        cy: Vec3 {
206            x: 0.0,
207            y: b,
208            z: 0.0,
209        },
210        cz: Vec3 {
211            x: 0.0,
212            y: 0.0,
213            z: c,
214        },
215    }
216}
217
218/// True if both closest-point fractions lie on their segments. (math_internal.h: b3IsWithinSegments)
219pub fn is_within_segments(result: &SegmentDistanceResult) -> bool {
220    (0.0 <= result.fraction1 && result.fraction1 <= 1.0)
221        && (0.0 <= result.fraction2 && result.fraction2 <= 1.0)
222}
223
224/// Plane through `point` with given `normal`. (math_internal.h: b3MakePlaneFromNormalAndPoint)
225pub fn make_plane_from_normal_and_point(normal: Vec3, point: Vec3) -> Plane {
226    Plane {
227        normal,
228        offset: dot(normal, point),
229    }
230}
231
232/// Plane through three points. (math_internal.h: b3MakePlaneFromPoints)
233pub fn make_plane_from_points(point1: Vec3, point2: Vec3, point3: Vec3) -> Plane {
234    let mut plane = Plane {
235        normal: cross(sub(point2, point1), sub(point3, point1)),
236        offset: 0.0,
237    };
238    plane.normal = normalize(plane.normal);
239    plane.offset = dot(plane.normal, point1);
240    plane
241}
242
243/// Transform a plane by a rigid transform. (math_internal.h: b3TransformPlane)
244pub fn transform_plane(transform: Transform, plane: Plane) -> Plane {
245    let normal = rotate_vector(transform.q, plane.normal);
246    Plane {
247        normal,
248        offset: plane.offset + dot(normal, transform.p),
249    }
250}
251
252/// Signed separation of a point from a plane. (math_internal.h: b3PlaneSeparation)
253pub fn plane_separation(plane: Plane, point: Vec3) -> f32 {
254    dot(plane.normal, point) - plane.offset
255}
256
257/// Rotate a central inertia tensor by a quaternion. (math_internal.h: b3RotateInertia)
258pub fn rotate_inertia(q: Quat, central_inertia: Matrix3) -> Matrix3 {
259    let rotation_matrix = make_matrix_from_quat(q);
260    mul_mm(
261        rotation_matrix,
262        mul_mm(central_inertia, transpose(rotation_matrix)),
263    )
264}
265
266/// Box inertia about the center for an AABB from `min` to `max`.
267/// (math_functions.c: b3BoxInertia)
268pub fn box_inertia(mass: f32, min: Vec3, max: Vec3) -> Matrix3 {
269    let delta = sub(max, min);
270    let ixx = mass * (delta.y * delta.y + delta.z * delta.z) / 12.0;
271    let iyy = mass * (delta.x * delta.x + delta.z * delta.z) / 12.0;
272    let izz = mass * (delta.x * delta.x + delta.y * delta.y) / 12.0;
273    make_diagonal_matrix(ixx, iyy, izz)
274}
275
276/// Solid sphere inertia about its center. (math_functions.c: b3SphereInertia)
277pub fn sphere_inertia(mass: f32, radius: f32) -> Matrix3 {
278    let i = 0.4 * mass * radius * radius;
279    make_diagonal_matrix(i, i, i)
280}
281
282/// Solid cylinder inertia about its center, axis along Y.
283/// (math_functions.c: b3CylinderInertia)
284pub fn cylinder_inertia(mass: f32, radius: f32, height: f32) -> Matrix3 {
285    let ixx = mass * (3.0 * radius * radius + height * height) / 12.0;
286    let iyy = 0.5 * mass * radius * radius;
287    make_diagonal_matrix(ixx, iyy, ixx)
288}
289
290/// A triangle with vertex indices and edge flags.
291/// (math_internal.h: b3Triangle)
292#[derive(Debug, Clone, Copy, PartialEq, Default)]
293pub struct Triangle {
294    pub vertices: [Vec3; 3],
295    pub i1: i32,
296    pub i2: i32,
297    pub i3: i32,
298    pub flags: i32,
299}
300
301/// Closest point on a triangle and the feature that owns it.
302/// (math_internal.h: b3TrianglePoint)
303#[derive(Debug, Clone, Copy, PartialEq)]
304pub struct TrianglePoint {
305    pub point: Vec3,
306    pub feature: crate::manifold::TriangleFeature,
307}
308
309/// Closest point on triangle ABC to query point Q (Ericson §5.1.5).
310/// (math_functions.c: b3ClosestPointOnTriangle)
311pub fn closest_point_on_triangle(a: Vec3, b: Vec3, c: Vec3, q: Vec3) -> TrianglePoint {
312    use crate::manifold::TriangleFeature;
313
314    // Check if P lies in vertex region of A
315    let ab = sub(b, a);
316    let ac = sub(c, a);
317    let aq = sub(q, a);
318
319    let d1 = dot(ab, aq);
320    let d2 = dot(ac, aq);
321    if d1 <= 0.0 && d2 <= 0.0 {
322        return TrianglePoint {
323            point: a,
324            feature: TriangleFeature::Vertex1,
325        };
326    }
327
328    // Check if P lies in vertex region of B
329    let bq = sub(q, b);
330
331    let d3 = dot(ab, bq);
332    let d4 = dot(ac, bq);
333    if d3 > 0.0 && d4 <= d3 {
334        return TrianglePoint {
335            point: b,
336            feature: TriangleFeature::Vertex2,
337        };
338    }
339
340    // Check if P lies in edge region AB
341    let vc = d1 * d4 - d3 * d2;
342    if vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0 {
343        let t = d1 / (d1 - d3);
344        return TrianglePoint {
345            point: mul_add(a, t, ab),
346            feature: TriangleFeature::Edge1,
347        };
348    }
349
350    // Check if P lies in vertex region of C
351    let cq = sub(q, c);
352
353    let d5 = dot(ab, cq);
354    let d6 = dot(ac, cq);
355    if d6 >= 0.0 && d5 <= d6 {
356        return TrianglePoint {
357            point: c,
358            feature: TriangleFeature::Vertex3,
359        };
360    }
361
362    // Check if P lies in edge region AC
363    let vb = d5 * d2 - d1 * d6;
364    if vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0 {
365        let t = d2 / (d2 - d6);
366        return TrianglePoint {
367            point: mul_add(a, t, ac),
368            feature: TriangleFeature::Edge3,
369        };
370    }
371
372    // Check if P lies in edge region of BC
373    let va = d3 * d6 - d5 * d4;
374    if va <= 0.0 && d4 >= d3 && d5 >= d6 {
375        let bc = sub(c, b);
376        let t = (d4 - d3) / ((d4 - d3) + (d5 - d6));
377        return TrianglePoint {
378            point: mul_add(b, t, bc),
379            feature: TriangleFeature::Edge2,
380        };
381    }
382
383    // P inside face region ABC
384    let t1 = vb / (va + vb + vc);
385    let t2 = vc / (va + vb + vc);
386
387    let mut p = mul_add(a, t1, ab);
388    p = mul_add(p, t2, ac);
389    TrianglePoint {
390        point: p,
391        feature: TriangleFeature::TriangleFace,
392    }
393}