#![allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct PyAabb {
pub min: [f64; 3],
pub max: [f64; 3],
}
impl PyAabb {
pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
Self { min, max }
}
pub fn unit() -> Self {
Self::new([-0.5; 3], [0.5; 3])
}
pub fn from_center_half_extents(center: [f64; 3], he: [f64; 3]) -> Self {
Self {
min: [center[0] - he[0], center[1] - he[1], center[2] - he[2]],
max: [center[0] + he[0], center[1] + he[1], center[2] + he[2]],
}
}
pub fn center(&self) -> [f64; 3] {
[
(self.min[0] + self.max[0]) * 0.5,
(self.min[1] + self.max[1]) * 0.5,
(self.min[2] + self.max[2]) * 0.5,
]
}
pub fn half_extents(&self) -> [f64; 3] {
[
(self.max[0] - self.min[0]) * 0.5,
(self.max[1] - self.min[1]) * 0.5,
(self.max[2] - self.min[2]) * 0.5,
]
}
pub fn surface_area(&self) -> f64 {
let dx = self.max[0] - self.min[0];
let dy = self.max[1] - self.min[1];
let dz = self.max[2] - self.min[2];
2.0 * (dx * dy + dy * dz + dz * dx)
}
pub fn volume(&self) -> f64 {
let dx = (self.max[0] - self.min[0]).max(0.0);
let dy = (self.max[1] - self.min[1]).max(0.0);
let dz = (self.max[2] - self.min[2]).max(0.0);
dx * dy * dz
}
pub fn contains_point(&self, p: [f64; 3]) -> bool {
p[0] >= self.min[0]
&& p[0] <= self.max[0]
&& p[1] >= self.min[1]
&& p[1] <= self.max[1]
&& p[2] >= self.min[2]
&& p[2] <= self.max[2]
}
pub fn intersects(&self, other: &PyAabb) -> bool {
self.min[0] <= other.max[0]
&& self.max[0] >= other.min[0]
&& self.min[1] <= other.max[1]
&& self.max[1] >= other.min[1]
&& self.min[2] <= other.max[2]
&& self.max[2] >= other.min[2]
}
pub fn merged(&self, other: &PyAabb) -> PyAabb {
PyAabb {
min: [
self.min[0].min(other.min[0]),
self.min[1].min(other.min[1]),
self.min[2].min(other.min[2]),
],
max: [
self.max[0].max(other.max[0]),
self.max[1].max(other.max[1]),
self.max[2].max(other.max[2]),
],
}
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct PySphere {
pub center: [f64; 3],
pub radius: f64,
}
impl PySphere {
pub fn new(center: [f64; 3], radius: f64) -> Self {
Self {
center,
radius: radius.max(0.0),
}
}
pub fn unit() -> Self {
Self::new([0.0; 3], 1.0)
}
pub fn surface_area(&self) -> f64 {
4.0 * std::f64::consts::PI * self.radius * self.radius
}
pub fn volume(&self) -> f64 {
(4.0 / 3.0) * std::f64::consts::PI * self.radius * self.radius * self.radius
}
pub fn contains_point(&self, p: [f64; 3]) -> bool {
let dx = p[0] - self.center[0];
let dy = p[1] - self.center[1];
let dz = p[2] - self.center[2];
dx * dx + dy * dy + dz * dz <= self.radius * self.radius
}
pub fn signed_distance(&self, p: [f64; 3]) -> f64 {
let dx = p[0] - self.center[0];
let dy = p[1] - self.center[1];
let dz = p[2] - self.center[2];
(dx * dx + dy * dy + dz * dz).sqrt() - self.radius
}
pub fn overlaps(&self, other: &PySphere) -> bool {
let dx = self.center[0] - other.center[0];
let dy = self.center[1] - other.center[1];
let dz = self.center[2] - other.center[2];
let dist2 = dx * dx + dy * dy + dz * dz;
let sum_r = self.radius + other.radius;
dist2 <= sum_r * sum_r
}
pub fn aabb(&self) -> PyAabb {
PyAabb::from_center_half_extents(self.center, [self.radius; 3])
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct PyConvexHull {
pub vertices: Vec<[f64; 3]>,
}
impl PyConvexHull {
pub fn new(vertices: Vec<[f64; 3]>) -> Self {
Self { vertices }
}
pub fn unit_cube() -> Self {
let verts: Vec<[f64; 3]> = [
[-0.5, -0.5, -0.5],
[0.5, -0.5, -0.5],
[0.5, 0.5, -0.5],
[-0.5, 0.5, -0.5],
[-0.5, -0.5, 0.5],
[0.5, -0.5, 0.5],
[0.5, 0.5, 0.5],
[-0.5, 0.5, 0.5],
]
.to_vec();
Self::new(verts)
}
pub fn vertex_count(&self) -> usize {
self.vertices.len()
}
pub fn aabb(&self) -> Option<PyAabb> {
if self.vertices.is_empty() {
return None;
}
let mut mn = self.vertices[0];
let mut mx = self.vertices[0];
for &v in &self.vertices {
for k in 0..3 {
mn[k] = mn[k].min(v[k]);
mx[k] = mx[k].max(v[k]);
}
}
Some(PyAabb::new(mn, mx))
}
pub fn centroid(&self) -> Option<[f64; 3]> {
if self.vertices.is_empty() {
return None;
}
let n = self.vertices.len() as f64;
let mut c = [0.0f64; 3];
for v in &self.vertices {
c[0] += v[0];
c[1] += v[1];
c[2] += v[2];
}
Some([c[0] / n, c[1] / n, c[2] / n])
}
pub fn may_contain_point(&self, p: [f64; 3]) -> bool {
match self.aabb() {
Some(aabb) => aabb.contains_point(p),
None => false,
}
}
pub fn support(&self, d: [f64; 3]) -> Option<[f64; 3]> {
self.vertices
.iter()
.max_by(|a, b| {
let da = a[0] * d[0] + a[1] * d[1] + a[2] * d[2];
let db = b[0] * d[0] + b[1] * d[1] + b[2] * d[2];
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
})
.copied()
}
}