use axiolid_core::{Point3, Vec3};
#[derive(Debug, Clone, PartialEq)]
pub struct ConvexShape {
pub(crate) points: Vec<Point3>,
pub(crate) normals: Vec<Vec3>,
pub(crate) edges: Vec<Vec3>,
}
impl ConvexShape {
#[must_use]
pub fn from_points(points: &[Point3]) -> Self {
let edges = derive_edges(points);
Self {
points: points.to_vec(),
normals: Vec::new(),
edges,
}
}
#[must_use]
pub fn with_faces(points: &[Point3], normals: &[Vec3]) -> Self {
let edges = derive_edges(points);
Self {
points: points.to_vec(),
normals: normals.to_vec(),
edges,
}
}
#[must_use]
pub fn from_aabb(min: Point3, max: Point3) -> Self {
let corners = [
Point3::new(min.x, min.y, min.z),
Point3::new(max.x, min.y, min.z),
Point3::new(max.x, max.y, min.z),
Point3::new(min.x, max.y, min.z),
Point3::new(min.x, min.y, max.z),
Point3::new(max.x, min.y, max.z),
Point3::new(max.x, max.y, max.z),
Point3::new(min.x, max.y, max.z),
];
Self::with_faces(
&corners,
&[
Vec3::new(1.0, 0.0, 0.0),
Vec3::new(0.0, 1.0, 0.0),
Vec3::new(0.0, 0.0, 1.0),
],
)
}
#[must_use]
pub fn points(&self) -> &[Point3] {
&self.points
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.points.is_empty()
}
}
fn derive_edges(points: &[Point3]) -> Vec<Vec3> {
const MAX_POINTS_FOR_FULL_EDGES: usize = 16;
let mut edges = Vec::new();
if points.len() <= MAX_POINTS_FOR_FULL_EDGES {
for i in 0..points.len() {
for j in (i + 1)..points.len() {
let direction = points[j] - points[i];
if direction.length_squared() > 0.0 {
edges.push(direction);
}
}
}
} else {
for window in points.windows(2) {
let direction = window[1] - window[0];
if direction.length_squared() > 0.0 {
edges.push(direction);
}
}
}
edges
}