use std::fmt;
use lina::Point3;
use crate::util::{PrimitiveFloat, Pos3Like};
#[derive(Debug, Clone, Copy)]
pub struct BoundingSphere<P: Pos3Like> {
pub center: P,
pub radius: P::Scalar,
}
pub fn ritter_sphere<I, ScalarT>(positions: I) -> BoundingSphere<I::Item>
where
I: Iterator + Clone,
I::Item: Pos3Like<Scalar = ScalarT>,
ScalarT: PrimitiveFloat,
{
assert!(positions.clone().next().is_some(), "point set must not be empty");
let furthest_away_from = |origin| {
let mut max = ScalarT::zero();
let mut out = origin;
for p in positions.clone().map(|p| p.to_point3()) {
let dist = p.distance2_from(origin);
if dist > max {
max = dist;
out = p;
}
}
out
};
let x = positions.clone().next().unwrap().to_point3();
let y = furthest_away_from(x);
let z = furthest_away_from(y);
let center = Point3::centroid([y, z]).unwrap();
let mut radius = y.distance_from(center);
for p in positions {
let dist = p.to_point3().distance_from(center);
if dist > radius {
radius = dist;
}
}
BoundingSphere { center: center.convert(), radius }
}
pub fn fast_sphere<I, ScalarT>(positions: I) -> BoundingSphere<I::Item>
where
I: Iterator + Clone,
I::Item: Pos3Like<Scalar = ScalarT>,
ScalarT: PrimitiveFloat,
{
assert!(positions.clone().next().is_some(), "point set must not be empty");
let bounding_box = BoundingBox::around(positions.clone());
let center = bounding_box.center();
let radius = positions
.map(|p| p.to_point3().distance_from(center))
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap();
BoundingSphere { center: center.convert(), radius }
}
pub struct BoundingBox<F: PrimitiveFloat> {
x_range: [F; 2],
y_range: [F; 2],
z_range: [F; 2],
}
impl<F: PrimitiveFloat> BoundingBox<F> {
pub fn new() -> Self {
Self {
x_range: [F::infinity(), F::neg_infinity()],
y_range: [F::infinity(), F::neg_infinity()],
z_range: [F::infinity(), F::neg_infinity()],
}
}
pub fn around<I>(iter: I) -> Self
where
I: IntoIterator,
I::Item: Pos3Like<Scalar = F>
{
let mut out = Self::new();
for pos in iter {
out.add_point(pos);
}
out
}
pub fn x(&self) -> [F; 2] {
self.x_range
}
pub fn y(&self) -> [F; 2] {
self.y_range
}
pub fn z(&self) -> [F; 2] {
self.z_range
}
pub fn center(&self) -> Point3<F> {
Point3::new(
(self.x_range[1] + self.x_range[0]) / F::from_f32(2.0),
(self.y_range[1] + self.y_range[0]) / F::from_f32(2.0),
(self.z_range[1] + self.z_range[0]) / F::from_f32(2.0),
)
}
pub fn add_point<P: Pos3Like<Scalar = F>>(&mut self, p: P) {
fn min<F: PartialOrd>(state: &mut F, new: F) {
if new < *state {
*state = new;
}
}
fn max<F: PartialOrd>(state: &mut F, new: F) {
if new > *state {
*state = new;
}
}
min(&mut self.x_range[0], p.x());
max(&mut self.x_range[1], p.x());
min(&mut self.y_range[0], p.y());
max(&mut self.y_range[1], p.y());
min(&mut self.z_range[0], p.z());
max(&mut self.z_range[1], p.z());
}
pub fn is_valid(&self) -> bool {
self.x_range[0].is_finite()
&& self.x_range[1].is_finite()
&& self.y_range[0].is_finite()
&& self.y_range[1].is_finite()
&& self.z_range[0].is_finite()
&& self.z_range[1].is_finite()
}
}
impl<F: PrimitiveFloat> fmt::Debug for BoundingBox<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BoundingBox")
.field("x", &(self.x_range[0]..self.x_range[1]))
.field("y", &(self.y_range[0]..self.y_range[1]))
.field("z", &(self.z_range[0]..self.z_range[1]))
.finish()
}
}