use derive_more::{From, TryInto};
#[cfg(feature = "derive_serdes")]
use serde::{Deserialize, Serialize};
use crate::*;
use super::{Aabb3, Capsule3, Cone3, Cylinder3, Hull3, Plane3, Primitive, Sphere3};
pub trait Shape <S> : Aabb <S> { fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>>;
}
pub trait Aabb <S> {
fn aabb (&self) -> Aabb3 <S>;
}
pub trait Bsphere <S> {
fn sphere (&self) -> Sphere3 <S>;
}
pub trait Stereometric <S> {
fn volume (&self) -> Positive <S>;
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq, From, TryInto)]
pub enum Variant <S> {
Bounded (Bounded <S>),
Unbounded (Unbounded <S>)
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq, From, TryInto)]
pub enum Bounded <S> {
Sphere (Sphere <S>),
Capsule (Capsule <S>),
Cylinder (Cylinder <S>),
Cone (Cone <S>),
Cube (Cube <S>),
Cuboid (Cuboid <S>),
Hull (Hull <S>)
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Eq, PartialEq, From, TryInto)]
pub enum Unbounded <S> {
Orthant (Orthant),
Halfspace (Halfspace <S>)
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Orthant {
pub normal_axis : SignedAxis3
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Halfspace <S> {
pub normal : Unit3 <S>
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Sphere <S> {
pub radius : Positive <S>
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Capsule <S> {
pub radius : Positive <S>,
pub half_height : Positive <S>
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Cylinder <S> {
pub radius : Positive <S>,
pub half_height : Positive <S>
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Cone <S> {
pub radius : Positive <S>,
pub half_height : Positive <S>
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Cube <S> {
pub extent : Positive <S>
}
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Cuboid <S> {
pub extents : Vector3 <Positive <S>>
}
pub type Hull <S> = Hull3 <S>;
pub fn report_sizes() {
use std::mem::size_of;
println!("shape report sizes...");
show!(size_of::<Variant <f32>>());
show!(size_of::<Variant <f64>>());
show!(size_of::<Bounded <f32>>());
show!(size_of::<Bounded <f64>>());
show!(size_of::<Unbounded <f32>>());
show!(size_of::<Unbounded <f64>>());
show!(size_of::<Sphere <f32>>());
show!(size_of::<Capsule <f32>>());
show!(size_of::<Cylinder <f32>>());
show!(size_of::<Cone <f32>>());
show!(size_of::<Cube <f32>>());
show!(size_of::<Cuboid <f32>>());
show!(size_of::<Orthant>());
show!(size_of::<Halfspace <f32>>());
show!(size_of::<Sphere <f64>>());
show!(size_of::<Capsule <f64>>());
show!(size_of::<Cylinder <f64>>());
show!(size_of::<Cone <f64>>());
show!(size_of::<Cube <f64>>());
show!(size_of::<Cuboid <f64>>());
show!(size_of::<Orthant>());
show!(size_of::<Halfspace <f64>>());
println!("...shape report sizes");
}
impl <S> Shape <S> for Variant <S> where
S : Real + num::real::Real + num::float::FloatCore + approx::RelativeEq
+ std::fmt::Debug + MaybeSerDes + 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
match self {
Variant::Bounded (bounded) => bounded.to_primitive (position),
Variant::Unbounded (unbounded) => unbounded.to_primitive (position)
}
}
}
impl <S> Aabb <S> for Variant <S> where
S : Real + num::float::FloatCore + std::fmt::Debug
{
fn aabb (&self) -> Aabb3 <S> {
match self {
Variant::Bounded (bounded) => bounded.aabb(),
Variant::Unbounded (unbounded) => unbounded.aabb()
}
}
}
impl <S> Stereometric <S> for Variant <S> where
S : Real + num::float::FloatCore + std::fmt::Debug
{
fn volume (&self) -> Positive <S> {
match self {
Variant::Bounded (bounded) => bounded.volume(),
Variant::Unbounded (unbounded) => unbounded.volume()
}
}
}
impl <S> Shape <S> for Bounded <S> where
S : Real + num::real::Real + approx::RelativeEq + std::fmt::Debug + MaybeSerDes
+ 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
match self {
Bounded::Sphere (sphere) => sphere.to_primitive (position),
Bounded::Capsule (capsule) => capsule.to_primitive (position),
Bounded::Cylinder (cylinder) => cylinder.to_primitive (position),
Bounded::Cuboid (cuboid) => cuboid.to_primitive (position),
Bounded::Hull (hull) => hull.to_primitive (position),
Bounded::Cone (cone) => cone.to_primitive (position),
Bounded::Cube (cube) => cube.to_primitive (position)
}
}
}
impl <S> Aabb <S> for Bounded <S> where S : Real + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
match self {
Bounded::Sphere (sphere) => sphere.aabb(),
Bounded::Capsule (capsule) => capsule.aabb(),
Bounded::Cylinder (cylinder) => cylinder.aabb(),
Bounded::Cone (cone) => cone.aabb(),
Bounded::Cube (cube) => cube.aabb(),
Bounded::Cuboid (cuboid) => cuboid.aabb(),
Bounded::Hull (hull) => hull.aabb()
}
}
}
impl <S : Real> Stereometric <S> for Bounded <S> {
fn volume (&self) -> Positive <S> {
match self {
Bounded::Sphere (sphere) => sphere.volume(),
Bounded::Capsule (capsule) => capsule.volume(),
Bounded::Cylinder (cylinder) => cylinder.volume(),
Bounded::Cone (cone) => cone.volume(),
Bounded::Cube (cube) => cube.volume(),
Bounded::Cuboid (cuboid) => cuboid.volume(),
Bounded::Hull (hull) => hull.volume()
}
}
}
impl <S> Shape <S> for Unbounded <S> where
S : Real + num::float::FloatCore + approx::RelativeEq + std::fmt::Debug + 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
match self {
Unbounded::Orthant (orthant) => orthant.to_primitive (position),
Unbounded::Halfspace (halfspace) => halfspace.to_primitive (position)
}
}
}
impl <S> Aabb <S> for Unbounded <S> where
S : Real + num::float::FloatCore + std::fmt::Debug
{
fn aabb (&self) -> Aabb3 <S> {
match self {
Unbounded::Orthant (orthant) => orthant.aabb(),
Unbounded::Halfspace (halfspace) => halfspace.aabb()
}
}
}
impl <S> Stereometric <S> for Unbounded <S> where
S : Real + num::float::FloatCore + std::fmt::Debug
{
fn volume (&self) -> Positive <S> {
if cfg!(debug_assertions) {
let volume = match self {
Unbounded::Orthant (orthant) => orthant.volume(),
Unbounded::Halfspace (halfspace) => halfspace.volume()
};
debug_assert_eq!(*volume, S::infinity());
}
Positive::infinity()
}
}
impl <S : OrderedRing> Sphere <S> {
#[inline]
pub fn unit() -> Self where S : Field {
use num::One;
Sphere { radius: Positive::one() }
}
#[inline]
pub fn noisy (radius : S) -> Self where S : std::fmt::Debug {
assert_ne!(radius, S::zero());
Sphere { radius: Positive::unchecked (radius.abs()) }
}
#[inline]
pub fn unchecked (radius : S) -> Self where S : std::fmt::Debug {
debug_assert_ne!(radius, S::zero());
Sphere { radius: Positive::unchecked (radius.abs()) }
}
#[inline]
pub const fn sphere3 (self, center : Point3 <S>) -> Sphere3 <S> {
Sphere3 { center, radius: self.radius }
}
}
impl <S> Shape <S> for Sphere <S> where
S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
Box::new (self.sphere3 (position))
}
}
impl <S> Aabb <S> for Sphere <S> where S : Real + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
Aabb3::with_minmax_unchecked ([-*self.radius; 3].into(), [ *self.radius; 3].into())
}
}
impl <S : Real> Stereometric <S> for Sphere <S> {
fn volume (&self) -> Positive <S> {
let four = Positive::unchecked (S::four());
let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
four * frac_pi_3 * self.radius * self.radius * self.radius
}
}
impl_numcast_fields!(Sphere, radius);
impl <S : OrderedRing> Capsule <S> {
#[inline]
pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
assert_ne!(radius, S::zero());
assert_ne!(half_height, S::zero());
let radius = Positive::unchecked (radius.abs());
let half_height = Positive::unchecked (half_height.abs());
Capsule { radius, half_height }
}
#[inline]
pub fn unchecked (radius : S, half_height : S) -> Self where
S : std::fmt::Debug
{
debug_assert_ne!(radius, S::zero());
let radius = Positive::unchecked (radius.abs());
let half_height = Positive::unchecked (half_height.abs());
Capsule { radius, half_height }
}
#[inline]
pub fn height (self) -> NonNegative <S> {
self.half_height * NonNegative::unchecked (S::two())
}
#[inline]
pub const fn capsule3 (self, center : Point3 <S>) -> Capsule3 <S> {
Capsule3 { center, radius: self.radius, half_height: self.half_height }
}
}
impl <S> Shape <S> for Capsule <S> where
S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
Box::new (self.capsule3 (position))
}
}
impl <S> Aabb <S> for Capsule <S> where S : Real + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
let r = *self.radius;
let hh = *self.half_height;
Aabb3::with_minmax_unchecked ([-r, -r, -r - hh].into(), [ r, r, r + hh].into())
}
}
impl <S : Real> Stereometric <S> for Capsule <S> {
fn volume (&self) -> Positive <S> {
let r = self.radius;
let h = self.height();
let r2 = r * r;
let r3 = r2 * r;
let pi = Positive::unchecked (S::pi());
let four = Positive::unchecked (S::four());
let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
let cylinder_volume = pi * r2 * h;
let sphere_volume = four * frac_pi_3 * r3;
sphere_volume + cylinder_volume
}
}
impl_numcast_fields!(Capsule, radius, half_height);
impl <S : OrderedRing> Cylinder <S> {
#[inline]
pub fn unit() -> Self where S : Field {
use num::One;
Cylinder {
radius: Positive::one(), half_height: Positive::one()
}
}
#[inline]
pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
assert_ne!(radius, S::zero());
assert_ne!(half_height, S::zero());
let radius = Positive::unchecked (radius.abs());
let half_height = Positive::unchecked (half_height.abs());
Cylinder { radius, half_height }
}
#[inline]
pub fn unchecked (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
debug_assert_ne!(radius, S::zero());
debug_assert_ne!(half_height, S::zero());
let radius = Positive::unchecked (radius.abs());
let half_height = Positive::unchecked (half_height.abs());
Cylinder { radius, half_height }
}
#[inline]
pub const fn cylinder3 (self, center : Point3 <S>) -> Cylinder3 <S> {
Cylinder3 { center, radius: self.radius, half_height: self.half_height }
}
#[inline]
pub fn height (self) -> Positive <S> {
self.half_height * Positive::unchecked (S::two())
}
}
impl <S> Shape <S> for Cylinder <S> where
S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
Box::new (self.cylinder3 (position))
}
}
impl <S> Aabb <S> for Cylinder <S> where S : Real + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
let r = *self.radius;
let hh = *self.half_height;
Aabb3::with_minmax_unchecked ([-r, -r, -hh].into(), [ r, r, hh].into())
}
}
impl <S : Real> Stereometric <S> for Cylinder <S> {
fn volume (&self) -> Positive <S> {
let pi = Positive::unchecked (S::pi());
pi * self.radius * self.radius * self.height()
}
}
impl_numcast_fields!(Cylinder, radius, half_height);
impl <S : OrderedRing> Cone <S> {
#[inline]
pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
assert_ne!(radius, S::zero());
assert_ne!(half_height, S::zero());
let radius = Positive::unchecked (radius.abs());
let half_height = Positive::unchecked (half_height.abs());
Cone { radius, half_height }
}
}
impl <S> Shape <S> for Cone <S> where
S : Real + num::real::Real + approx::RelativeEq + std::fmt::Debug + MaybeSerDes
+ 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
Box::new (Cone3 {
center: position,
half_height: self.half_height,
radius: self.radius
})
}
}
impl <S> Aabb <S> for Cone <S> where S : Real + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
let r = *self.radius;
let hh = *self.half_height;
Aabb3::with_minmax_unchecked ([-r, -r, -hh].into(), [ r, r, hh].into())
}
}
impl <S : Real> Stereometric <S> for Cone <S> {
fn volume (&self) -> Positive <S> {
let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
let two = Positive::unchecked (S::two());
frac_pi_3 * self.radius * self.radius * two * self.half_height
}
}
impl_numcast_fields!(Cone, radius, half_height);
impl <S : OrderedRing> Cube <S> {
#[inline]
pub fn noisy (extent : S) -> Self where S : std::fmt::Debug {
assert_ne!(extent, S::zero());
Cube { extent: Positive::unchecked (extent.abs()) }
}
#[inline]
pub fn dimensions (self) -> Vector3 <Positive <S>> {
Vector3::broadcast (self.extent * Positive::unchecked (S::two()))
}
#[inline]
pub fn width (self) -> Positive <S> {
self.extent * Positive::unchecked (S::two())
}
#[inline]
pub fn height (self) -> Positive <S> {
self.extent * Positive::unchecked (S::two())
}
#[inline]
pub fn depth (self) -> Positive <S> {
self.extent * Positive::unchecked (S::two())
}
}
impl <S> Shape <S> for Cube <S> where S : OrderedField + std::fmt::Debug + 'static {
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
let mut aabb = self.aabb();
aabb.translate (position.0);
Box::new (aabb)
}
}
impl <S> Aabb <S> for Cube <S> where S : OrderedField + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
Aabb3::with_minmax_unchecked (
[-*self.extent; 3].into(),
[ *self.extent; 3].into())
}
}
impl <S : OrderedField> Stereometric <S> for Cube <S> {
fn volume (&self) -> Positive <S> {
self.extent * self.extent * self.extent
}
}
impl_numcast_fields!(Cube, extent);
impl <S : OrderedRing> Cuboid <S> {
#[inline]
pub fn noisy (extents : [S; 3]) -> Self where S : std::fmt::Debug {
assert_ne!(extents[0], S::zero());
assert_ne!(extents[1], S::zero());
assert_ne!(extents[2], S::zero());
let extents = vector3 (
Positive::unchecked (extents[0].abs()),
Positive::unchecked (extents[1].abs()),
Positive::unchecked (extents[2].abs()));
Cuboid { extents }
}
#[inline]
pub fn dimensions (self) -> Vector3 <Positive <S>> {
self.extents * Positive::unchecked (S::two())
}
#[inline]
pub fn width (self) -> Positive <S> {
self.extents.x * Positive::unchecked (S::two())
}
#[inline]
pub fn depth (self) -> Positive <S> {
self.extents.y * Positive::unchecked (S::two())
}
#[inline]
pub fn height (self) -> Positive <S> {
self.extents.z * Positive::unchecked (S::two())
}
#[inline]
pub fn max (self) -> Point3 <S> {
self.extents.map (|e| *e).into()
}
#[inline]
pub fn min (self) -> Point3 <S> {
self.extents.map (|e| -*e).into()
}
#[inline]
pub fn aabb3 (self, center : Point3 <S>) -> Aabb3 <S> where S : std::fmt::Debug {
Aabb3::with_minmax_unchecked (
center + self.min().0,
center + self.max().0)
}
}
impl <S> TryFrom <Aabb3 <S>> for Cuboid <S> where S : OrderedField {
type Error = Aabb3 <S>;
fn try_from (aabb : Aabb3 <S>) -> Result <Self, Self::Error> {
let extents = {
let [x, y, z] = aabb.extents().into_array();
vector3 (
Positive::new (*x).ok_or (aabb)?,
Positive::new (*y).ok_or (aabb)?,
Positive::new (*z).ok_or (aabb)?)
};
Ok (Cuboid { extents })
}
}
impl <S> Shape <S> for Cuboid <S> where S : OrderedField + std::fmt::Debug + 'static {
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
let mut aabb = self.aabb();
aabb.translate (position.0);
Box::new (aabb)
}
}
impl <S> Aabb <S> for Cuboid <S> where S : OrderedField + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
Aabb3::with_minmax_unchecked (self.min(), self.max())
}
}
impl <S : OrderedField> Stereometric <S> for Cuboid <S> {
fn volume (&self) -> Positive <S> {
self.extents.x * self.extents.y * self.extents.z
}
}
impl_numcast_fields!(Cuboid, extents);
impl <S> Shape <S> for Hull <S> where
S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
let mut aabb = self.clone();
aabb.translate (position.0);
Box::new (aabb)
}
}
impl <S> Aabb <S> for Hull <S> where S : Real + std::fmt::Debug {
fn aabb (&self) -> Aabb3 <S> {
Aabb3::containing (self.points()).unwrap()
}
}
impl <S : Real> Stereometric <S> for Hull <S> {
fn volume (&self) -> Positive <S> {
unimplemented!("TODO: hull volume")
}
}
impl <S : Field> TryFrom <Halfspace <S>> for Orthant {
type Error = Halfspace <S>;
fn try_from (halfspace : Halfspace <S>) -> Result <Self, Self::Error> {
SignedAxis3::try_from (*halfspace.normal)
.map (|normal_axis| Orthant { normal_axis })
.map_err (|_| halfspace)
}
}
impl From <SignedAxis3> for Orthant {
fn from (normal_axis : SignedAxis3) -> Self {
Orthant { normal_axis }
}
}
impl <S> Shape <S> for Orthant where
S : Real + num::float::FloatCore + approx::RelativeEq + std::fmt::Debug + 'static
{
#[expect(clippy::renamed_function_params)]
fn to_primitive (&self, base : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
Box::new (Plane3 { base, normal: self.normal_axis.to_unit() })
}
}
impl <S> Aabb <S> for Orthant where
S : OrderedField + num::float::FloatCore + std::fmt::Debug
{
fn aabb (&self) -> Aabb3 <S> {
use num::float::FloatCore;
let axis_vec = self.normal_axis.to_vec::<S>();
let surface = Vector3::broadcast (S::one()) - axis_vec.map (FloatCore::abs);
let do_min = |i| if surface[i] == S::one() || axis_vec[i] == S::one() {
S::neg_infinity()
} else {
debug_assert_eq!(axis_vec[i], -S::one());
S::zero()
};
let do_max = |i| if surface[i] == S::one() {
S::infinity()
} else if axis_vec[i] == S::one() {
S::zero()
} else {
debug_assert_eq!(axis_vec[i], -S::one());
S::infinity()
};
let min = [ do_min (0), do_min (1), do_min (2) ].into();
let max = [ do_max (0), do_max (1), do_max (2) ].into();
Aabb3::with_minmax_unchecked (min, max)
}
}
impl <S> Stereometric <S> for Orthant where S : OrderedRing + num::float::FloatCore {
fn volume (&self) -> Positive <S> {
Positive::infinity()
}
}
impl <S : Copy> Halfspace <S> {
#[inline]
pub const fn plane3 (self, base : Point3 <S>) -> Plane3 <S> {
Plane3 { base, normal: self.normal }
}
}
impl <S : Real> From <Unit3 <S>> for Halfspace <S> {
fn from (normal_vector : Unit3 <S>) -> Self {
Halfspace { normal: normal_vector }
}
}
impl <S> Shape <S> for Halfspace <S> where
S : Real + num::float::FloatCore + approx::RelativeEq + std::fmt::Debug + 'static
{
fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
Box::new (self.plane3 (position))
}
}
impl <S> Aabb <S> for Halfspace <S> where
S : Real + num::float::FloatCore + std::fmt::Debug
{
fn aabb (&self) -> Aabb3 <S> {
if let Ok (orthant) = Orthant::try_from (*self) {
orthant.aabb()
} else {
Aabb3::with_minmax_unchecked (
[S::neg_infinity(); 3].into(),
[S::infinity(); 3].into())
}
}
}
impl <S> Stereometric <S> for Halfspace <S> where S : Real + num::float::FloatCore {
fn volume (&self) -> Positive <S> {
Positive::infinity()
}
}
impl_numcast_fields!(Halfspace, normal);