Skip to main content

brep_render/
geometry3d.rs

1//! Shared three-dimensional vector operations.
2
3#[inline]
4pub fn norm3(v: [f64; 3]) -> [f64; 3] {
5    let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
6    if len <= 0.0 {
7        return [0.0, 0.0, 1.0];
8    }
9    [v[0] / len, v[1] / len, v[2] / len]
10}
11
12#[inline]
13pub fn cross3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
14    [
15        a[1] * b[2] - a[2] * b[1],
16        a[2] * b[0] - a[0] * b[2],
17        a[0] * b[1] - a[1] * b[0],
18    ]
19}
20
21#[inline]
22pub fn dot3(a: [f64; 3], b: [f64; 3]) -> f64 {
23    a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
24}
25
26#[inline]
27pub fn sub3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
28    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
29}
30
31#[inline]
32pub fn add3(a: [f64; 3], b: [f64; 3]) -> [f64; 3] {
33    [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
34}
35
36#[inline]
37pub fn scale3(a: [f64; 3], s: f64) -> [f64; 3] {
38    [a[0] * s, a[1] * s, a[2] * s]
39}
40
41#[inline]
42pub fn len3(a: [f64; 3]) -> f64 {
43    dot3(a, a).sqrt()
44}
45
46/// Rotate `v` around unit `axis` by `angle` (Rodrigues).
47#[inline]
48pub fn rotate3(v: [f64; 3], axis: [f64; 3], angle: f64) -> [f64; 3] {
49    let (sin, cos) = angle.sin_cos();
50    let cross = cross3(axis, v);
51    let dot = dot3(axis, v);
52    [
53        v[0] * cos + cross[0] * sin + axis[0] * dot * (1.0 - cos),
54        v[1] * cos + cross[1] * sin + axis[1] * dot * (1.0 - cos),
55        v[2] * cos + cross[2] * sin + axis[2] * dot * (1.0 - cos),
56    ]
57}
58
59/// Normalize a finite vector whose length exceeds 1e-12.
60#[inline]
61pub fn normalize3(v: [f64; 3]) -> Option<[f64; 3]> {
62    let len = len3(v);
63    (len > 1e-12 && len.is_finite()).then(|| [v[0] / len, v[1] / len, v[2] / len])
64}
65
66/// Normalize or return the caller's fallback unchanged.
67#[inline]
68pub fn normalize3_or(v: [f64; 3], fallback: [f64; 3]) -> [f64; 3] {
69    normalize3(v).unwrap_or(fallback)
70}
71
72/// Row-major matrix composition. Valid affine results use the kernel's arithmetic.
73/// Keep the raw result for singular/projective/nonfinite display matrices: these
74/// callers historically accepted them, unlike `AffineTransform::compose`.
75pub fn mat4_mul_row_major(a: &[f64; 16], b: &[f64; 16]) -> [f64; 16] {
76    use brep_kernel::AffineTransform;
77    (AffineTransform { elements: *a }).compose(&AffineTransform { elements: *b })
78        .map(|transform| transform.elements)
79        .unwrap_or_else(|_| std::array::from_fn(|k| {
80            let (row, col) = (k / 4, k % 4);
81            (0..4).map(|n| a[row * 4 + n] * b[n * 4 + col]).sum()
82        }))
83}
84
85/// Transpose the rotation and invert its translation, preserving the raw result
86/// even when the kernel's affine-result validator refuses the display matrix.
87/// As with the kernel helper, the caller is responsible for rigidity.
88pub fn mat4_rigid_inverse(m: &[f64; 16]) -> [f64; 16] {
89    (brep_kernel::AffineTransform { elements: *m }).rigid_inverse()
90        .map(|transform| transform.elements)
91        .unwrap_or_else(|_| {
92            let mut out = [0.0; 16];
93            for row in 0..3 {
94                for col in 0..3 {
95                    out[row * 4 + col] = m[col * 4 + row];
96                }
97                out[row * 4 + 3] = -(0..3).map(|k| m[k * 4 + row] * m[k * 4 + 3]).sum::<f64>();
98            }
99            out[15] = 1.0;
100            out
101        })
102}
103