use crate::{Aabb, Vec3};
const LANES: usize = 8;
const EXPONENT_MASK: u32 = 0x7f80_0000;
#[must_use]
pub fn all_finite(values: &[f32]) -> bool {
let mut lanes = [true; LANES];
let (chunks, remainder) = values.as_chunks::<LANES>();
for chunk in chunks {
for (lane, value) in lanes.iter_mut().zip(chunk) {
*lane &= value.to_bits() & EXPONENT_MASK != EXPONENT_MASK;
}
}
let mut finite = lanes.iter().all(|lane| *lane);
for value in remainder {
finite &= value.to_bits() & EXPONENT_MASK != EXPONENT_MASK;
}
finite
}
#[must_use]
pub fn all_finite_non_negative(values: &[f32]) -> bool {
let mut lanes = [true; LANES];
let (chunks, remainder) = values.as_chunks::<LANES>();
for chunk in chunks {
for (lane, value) in lanes.iter_mut().zip(chunk) {
*lane &= value.to_bits() & EXPONENT_MASK != EXPONENT_MASK && *value >= 0.0;
}
}
let mut valid = lanes.iter().all(|lane| *lane);
for value in remainder {
valid &= value.to_bits() & EXPONENT_MASK != EXPONENT_MASK && *value >= 0.0;
}
valid
}
#[must_use]
pub fn all_within(values: &[f32], low: f32, high: f32) -> bool {
let mut lanes = [true; LANES];
let (chunks, remainder) = values.as_chunks::<LANES>();
for chunk in chunks {
for (lane, value) in lanes.iter_mut().zip(chunk) {
*lane &= *value >= low && *value <= high;
}
}
let mut inside = lanes.iter().all(|lane| *lane);
for value in remainder {
inside &= *value >= low && *value <= high;
}
inside
}
#[must_use]
pub fn min_max(values: &[f32]) -> (f32, f32) {
let mut low = [f32::INFINITY; LANES];
let mut high = [f32::NEG_INFINITY; LANES];
let (chunks, remainder) = values.as_chunks::<LANES>();
for chunk in chunks {
for ((low, high), value) in low.iter_mut().zip(high.iter_mut()).zip(chunk) {
*low = low.min(*value);
*high = high.max(*value);
}
}
let mut minimum = f32::INFINITY;
let mut maximum = f32::NEG_INFINITY;
for (low, high) in low.iter().zip(&high) {
minimum = minimum.min(*low);
maximum = maximum.max(*high);
}
for value in remainder {
minimum = minimum.min(*value);
maximum = maximum.max(*value);
}
if minimum.is_infinite() && maximum.is_infinite() {
return (f32::INFINITY, f32::NEG_INFINITY);
}
(minimum, maximum)
}
#[must_use]
pub fn points_aabb(points: &[[f32; 3]]) -> Aabb {
let mut low = [[f32::INFINITY; 3]; LANES];
let mut high = [[f32::NEG_INFINITY; 3]; LANES];
let (chunks, remainder) = points.as_chunks::<LANES>();
for chunk in chunks {
for ((low, high), point) in low.iter_mut().zip(high.iter_mut()).zip(chunk) {
for ((low, high), value) in low.iter_mut().zip(high.iter_mut()).zip(point) {
*low = low.min(*value);
*high = high.max(*value);
}
}
}
let mut bounds = Aabb::EMPTY;
for (low, high) in low.iter().zip(&high) {
bounds.min = bounds.min.min(Vec3::from_array(*low));
bounds.max = bounds.max.max(Vec3::from_array(*high));
}
for point in remainder {
bounds.extend(Vec3::from_array(*point));
}
if bounds.is_empty() {
Aabb::EMPTY
} else {
bounds
}
}
#[must_use]
pub fn spheres_aabb(centers: &[[f32; 3]], radii: &[f32]) -> Aabb {
let len = centers.len().min(radii.len());
let (centers, radii) = (¢ers[..len], &radii[..len]);
let mut low = [[f32::INFINITY; 3]; LANES];
let mut high = [[f32::NEG_INFINITY; 3]; LANES];
let (center_chunks, center_remainder) = centers.as_chunks::<LANES>();
let (radius_chunks, radius_remainder) = radii.as_chunks::<LANES>();
for (centers, radii) in center_chunks.iter().zip(radius_chunks) {
for (((low, high), center), radius) in
low.iter_mut().zip(high.iter_mut()).zip(centers).zip(radii)
{
let extent = radius.abs();
for ((low, high), value) in low.iter_mut().zip(high.iter_mut()).zip(center) {
*low = low.min(*value - extent);
*high = high.max(*value + extent);
}
}
}
let mut bounds = Aabb::EMPTY;
for (low, high) in low.iter().zip(&high) {
bounds.min = bounds.min.min(Vec3::from_array(*low));
bounds.max = bounds.max.max(Vec3::from_array(*high));
}
for (center, radius) in center_remainder.iter().zip(radius_remainder) {
bounds.extend_sphere(Vec3::from_array(*center), *radius);
}
if bounds.is_empty() {
Aabb::EMPTY
} else {
bounds
}
}
#[cfg(test)]
#[path = "simd_tests.rs"]
mod tests;