use nalgebra::{Matrix4, Point3, Vector3};
use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Neg, Sub};
pub(crate) trait GeomScalar:
Copy
+ Clone
+ PartialEq
+ Debug
+ 'static
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ Neg<Output = Self>
{
fn from_f64(v: f64) -> Self;
fn value(self) -> f64;
fn sqrt(self) -> Self;
fn abs(self) -> Self;
fn min(self, other: Self) -> Self;
fn max(self, other: Self) -> Self;
#[inline]
fn is_finite(self) -> bool {
self.value().is_finite()
}
}
impl GeomScalar for f64 {
#[inline]
fn from_f64(v: f64) -> Self {
v
}
#[inline]
fn value(self) -> f64 {
self
}
#[inline]
fn sqrt(self) -> Self {
f64::sqrt(self)
}
#[inline]
fn abs(self) -> Self {
f64::abs(self)
}
#[inline]
fn min(self, other: Self) -> Self {
f64::min(self, other)
}
#[inline]
fn max(self, other: Self) -> Self {
f64::max(self, other)
}
}
#[inline]
pub(crate) fn magnitude_squared3<S: GeomScalar>(v: &Vector3<S>) -> S {
v.x * v.x + v.y * v.y + v.z * v.z
}
#[inline]
pub(crate) fn try_normalize3<S: GeomScalar>(v: &Vector3<S>, min_norm: f64) -> Option<Vector3<S>> {
let n = magnitude_squared3(v).sqrt();
if n.value() <= min_norm {
None
} else {
Some(Vector3::new(v.x / n, v.y / n, v.z / n))
}
}
#[inline]
pub(crate) fn transform_point4<S: GeomScalar>(m: &Matrix4<S>, p: &Point3<S>) -> Point3<S> {
let (x, y, z) = (p.x, p.y, p.z);
let n = m[(3, 0)] * x + m[(3, 1)] * y + m[(3, 2)] * z + m[(3, 3)];
let tx = m[(0, 0)] * x + m[(0, 1)] * y + m[(0, 2)] * z + m[(0, 3)];
let ty = m[(1, 0)] * x + m[(1, 1)] * y + m[(1, 2)] * z + m[(1, 3)];
let tz = m[(2, 0)] * x + m[(2, 1)] * y + m[(2, 2)] * z + m[(2, 3)];
if n.value() != 0.0 {
Point3::new(tx / n, ty / n, tz / n)
} else {
Point3::new(tx, ty, tz)
}
}
pub(crate) trait MeshSink<S: GeomScalar> {
fn vertex_count(&self) -> usize;
fn reserve(&mut self, vertices: usize, indices: usize);
fn add_vertex(&mut self, position: Point3<S>, normal: Vector3<S>);
fn add_triangle(&mut self, i0: u32, i1: u32, i2: u32);
fn position(&self, index: usize) -> Point3<S>;
fn set_position(&mut self, index: usize, position: Point3<S>);
fn transform_normals(&mut self, transform: &Matrix4<S>);
}
impl MeshSink<f64> for crate::mesh::Mesh {
#[inline]
fn vertex_count(&self) -> usize {
crate::mesh::Mesh::vertex_count(self)
}
#[inline]
fn reserve(&mut self, vertices: usize, indices: usize) {
self.positions.reserve(vertices * 3);
self.normals.reserve(vertices * 3);
self.indices.reserve(indices);
}
#[inline]
fn add_vertex(&mut self, position: Point3<f64>, normal: Vector3<f64>) {
crate::mesh::Mesh::add_vertex(self, position, normal)
}
#[inline]
fn add_triangle(&mut self, i0: u32, i1: u32, i2: u32) {
crate::mesh::Mesh::add_triangle(self, i0, i1, i2)
}
#[inline]
fn position(&self, index: usize) -> Point3<f64> {
Point3::new(
self.positions[index * 3] as f64,
self.positions[index * 3 + 1] as f64,
self.positions[index * 3 + 2] as f64,
)
}
#[inline]
fn set_position(&mut self, index: usize, position: Point3<f64>) {
self.positions[index * 3] = position.x as f32;
self.positions[index * 3 + 1] = position.y as f32;
self.positions[index * 3 + 2] = position.z as f32;
}
#[inline]
fn transform_normals(&mut self, transform: &Matrix4<f64>) {
let normal_matrix = transform.try_inverse().unwrap_or(*transform).transpose();
self.normals.chunks_exact_mut(3).for_each(|chunk| {
let normal = Vector3::new(chunk[0] as f64, chunk[1] as f64, chunk[2] as f64);
let transformed = (normal_matrix * normal.to_homogeneous()).xyz().normalize();
chunk[0] = transformed.x as f32;
chunk[1] = transformed.y as f32;
chunk[2] = transformed.z as f32;
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn transform_point4_matches_nalgebra_bitwise() {
let mut state = 0x9E3779B97F4A7C15u64;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
(state >> 11) as f64 / (1u64 << 53) as f64 * 20.0 - 10.0
};
for _ in 0..2000 {
let mut m = Matrix4::<f64>::zeros();
for i in 0..4 {
for j in 0..4 {
m[(i, j)] = next();
}
}
let p = Point3::new(next(), next(), next());
let a = m.transform_point(&p);
let b = transform_point4(&m, &p);
assert_eq!(a.x.to_bits(), b.x.to_bits(), "x mismatch");
assert_eq!(a.y.to_bits(), b.y.to_bits(), "y mismatch");
assert_eq!(a.z.to_bits(), b.z.to_bits(), "z mismatch");
}
for _ in 0..2000 {
let mut m = Matrix4::<f64>::identity();
for i in 0..3 {
for j in 0..4 {
m[(i, j)] = next();
}
}
let p = Point3::new(next(), next(), next());
let a = m.transform_point(&p);
let b = transform_point4(&m, &p);
assert_eq!(a.x.to_bits(), b.x.to_bits());
assert_eq!(a.y.to_bits(), b.y.to_bits());
assert_eq!(a.z.to_bits(), b.z.to_bits());
}
}
#[test]
fn try_normalize3_matches_nalgebra_bitwise() {
let mut state = 0xD1B54A32D192ED03u64;
let mut next = || {
state ^= state << 13;
state ^= state >> 7;
state ^= state << 17;
(state >> 11) as f64 / (1u64 << 53) as f64 * 4.0 - 2.0
};
for _ in 0..4000 {
let v = Vector3::new(next(), next(), next());
let a = v.try_normalize(1e-10);
let b = try_normalize3(&v, 1e-10);
match (a, b) {
(None, None) => {}
(Some(a), Some(b)) => {
assert_eq!(a.x.to_bits(), b.x.to_bits());
assert_eq!(a.y.to_bits(), b.y.to_bits());
assert_eq!(a.z.to_bits(), b.z.to_bits());
}
_ => panic!("try_normalize disagreement on {v:?}"),
}
assert_eq!(
v.magnitude_squared().to_bits(),
magnitude_squared3(&v).to_bits()
);
}
}
}