1use std::convert::TryFrom;
4use derive_more::{From, TryFrom};
5use num_traits as num;
6#[cfg(feature = "derive_serdes")]
7use serde::{Deserialize, Serialize};
8
9use crate::*;
10
11use super::{Aabb3, Capsule3, Cylinder3, Sphere3};
12
13pub trait Shape <S : Ring> : Aabb <S> { } pub trait Aabb <S : Ring> : Stereometric <S> {
33 fn aabb (&self) -> Aabb3 <S>;
34}
35
36pub trait Bsphere <S : Ring> : Stereometric <S> {
39 fn sphere (&self) -> Sphere3 <S>;
40}
41
42pub trait Stereometric <S : Ring> {
46 fn volume (&self) -> Positive <S>;
47}
48
49#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
56#[derive(From, Clone, Debug, Eq, PartialEq, TryFrom)]
57pub enum Variant <S> {
58 Bounded (Bounded <S>),
59 Unbounded (Unbounded <S>)
60}
61
62#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
64#[derive(From, Clone, Debug, Eq, PartialEq)]
65pub enum Bounded <S> {
66 Sphere (Sphere <S>),
67 Capsule (Capsule <S>),
68 Cylinder (Cylinder <S>),
69 Cone (Cone <S>),
70 Cube (Cube <S>),
71 Cuboid (Cuboid <S>)
72}
73
74#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
76#[derive(From, Clone, Debug, Eq, PartialEq)]
77pub enum Unbounded <S> {
78 Orthant (Orthant),
79 Halfspace (Halfspace <S>)
80}
81
82#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
88#[derive(Clone, Copy, Debug, Eq, PartialEq)]
89pub struct Orthant {
90 pub normal_axis : SignedAxis3
91}
92
93#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
95#[derive(Clone, Copy, Debug, Eq, PartialEq)]
96pub struct Halfspace <S> {
97 pub normal_vector : Unit3 <S>
98}
99
100#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
102#[derive(Clone, Copy, Debug, Eq, PartialEq)]
103pub struct Sphere <S> {
104 pub radius : Positive <S>
105}
106
107#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
110pub struct Capsule <S> {
111 pub radius : Positive <S>,
112 pub half_height : Positive <S>
113}
114
115#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
118pub struct Cylinder <S> {
119 pub radius : Positive <S>,
120 pub half_height : Positive <S>
121}
122
123#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
125#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126pub struct Cone <S> {
127 pub radius : Positive <S>,
128 pub half_height : Positive <S>
129}
130
131#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
133#[derive(Clone, Copy, Debug, Eq, PartialEq)]
134pub struct Cube <S> {
135 pub half_extent : Positive <S>
136}
137
138#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
140#[derive(Clone, Copy, Debug, Eq, PartialEq)]
141pub struct Cuboid <S> {
142 pub half_extent_x : Positive <S>,
143 pub half_extent_y : Positive <S>,
144 pub half_extent_z : Positive <S>
145}
146
147pub fn report_sizes() {
152 use std::mem::size_of;
153 println!("shape report sizes...");
154
155 show!(size_of::<Variant <f32>>());
156 show!(size_of::<Variant <f64>>());
157
158 show!(size_of::<Bounded <f32>>());
159 show!(size_of::<Bounded <f64>>());
160
161 show!(size_of::<Unbounded <f32>>());
162 show!(size_of::<Unbounded <f64>>());
163
164 show!(size_of::<Sphere <f32>>());
165 show!(size_of::<Capsule <f32>>());
166 show!(size_of::<Cylinder <f32>>());
167 show!(size_of::<Cone <f32>>());
168 show!(size_of::<Cube <f32>>());
169 show!(size_of::<Cuboid <f32>>());
170
171 show!(size_of::<Orthant>());
172 show!(size_of::<Halfspace <f32>>());
173
174 show!(size_of::<Sphere <f64>>());
175 show!(size_of::<Capsule <f64>>());
176 show!(size_of::<Cylinder <f64>>());
177 show!(size_of::<Cone <f64>>());
178 show!(size_of::<Cube <f64>>());
179 show!(size_of::<Cuboid <f64>>());
180
181 show!(size_of::<Orthant>());
182 show!(size_of::<Halfspace <f64>>());
183
184 println!("...shape report sizes");
185}
186
187impl <S> Shape <S> for Variant <S> where
195 S : Real + num::float::FloatCore + std::fmt::Debug
196{ }
197impl <S> Aabb <S> for Variant <S> where
198 S : Real + num::float::FloatCore + std::fmt::Debug
199{
200 fn aabb (&self) -> Aabb3 <S> {
202 match self {
203 Variant::Bounded (bounded) => bounded.aabb(),
204 Variant::Unbounded (unbounded) => unbounded.aabb()
205 }
206 }
207}
208impl <S> Stereometric <S> for Variant <S> where
209 S : Real + num::float::FloatCore + std::fmt::Debug
210{
211 fn volume (&self) -> Positive <S> {
213 match self {
214 Variant::Bounded (bounded) => bounded.volume(),
215 Variant::Unbounded (unbounded) => unbounded.volume()
216 }
217 }
218}
219
220impl <S> Shape <S> for Bounded <S> where S : Real + std::fmt::Debug { }
224impl <S> Aabb <S> for Bounded <S> where S : Real + std::fmt::Debug {
225 fn aabb (&self) -> Aabb3 <S> {
226 match self {
227 Bounded::Sphere (sphere) => sphere.aabb(),
228 Bounded::Capsule (capsule) => capsule.aabb(),
229 Bounded::Cylinder (cylinder) => cylinder.aabb(),
230 Bounded::Cone (cone) => cone.aabb(),
231 Bounded::Cube (cube) => cube.aabb(),
232 Bounded::Cuboid (cuboid) => cuboid.aabb()
233 }
234 }
235}
236impl <S : Real> Stereometric <S> for Bounded <S> {
237 fn volume (&self) -> Positive <S> {
239 match self {
240 Bounded::Sphere (sphere) => sphere.volume(),
241 Bounded::Capsule (capsule) => capsule.volume(),
242 Bounded::Cylinder (cylinder) => cylinder.volume(),
243 Bounded::Cone (cone) => cone.volume(),
244 Bounded::Cube (cube) => cube.volume(),
245 Bounded::Cuboid (cuboid) => cuboid.volume()
246 }
247 }
248}
249
250impl <S> Shape <S> for Unbounded <S> where
254 S : Real + num::float::FloatCore + std::fmt::Debug
255{ }
256impl <S> Aabb <S> for Unbounded <S> where
257 S : Real + num::float::FloatCore + std::fmt::Debug
258{
259 fn aabb (&self) -> Aabb3 <S> {
260 match self {
261 Unbounded::Orthant (orthant) => orthant.aabb(),
262 Unbounded::Halfspace (halfspace) => halfspace.aabb()
263 }
264 }
265}
266impl <S> Stereometric <S> for Unbounded <S> where
267 S : Real + num::float::FloatCore + std::fmt::Debug
268{
269 fn volume (&self) -> Positive <S> {
271 if cfg!(debug_assertions) {
272 let volume = match self {
273 Unbounded::Orthant (orthant) => orthant.volume(),
274 Unbounded::Halfspace (halfspace) => halfspace.volume()
275 };
276 debug_assert_eq!(*volume, S::infinity());
277 }
278 Positive::infinity()
279 }
280}
281
282impl <S : Ring> Sphere <S> {
286 #[inline]
287 pub fn unit() -> Self where S : Field {
289 use num::One;
290 Sphere { radius: Positive::one() }
291 }
292 #[inline]
301 pub fn noisy (radius : S) -> Self where S : std::fmt::Debug {
302 assert_ne!(radius, S::zero());
303 Sphere { radius: Positive::unchecked (radius.abs()) }
304 }
305 #[inline]
314 pub fn unchecked (radius : S) -> Self where S : std::fmt::Debug {
315 debug_assert_ne!(radius, S::zero());
316 Sphere { radius: Positive::unchecked (radius.abs()) }
317 }
318 #[inline]
319 pub const fn sphere3 (&self, center : Point3 <S>) -> Sphere3 <S> {
320 Sphere3 { center, radius: self.radius }
321 }
322}
323impl <S : Real> TryFrom <Bounded <S>> for Sphere <S> {
324 type Error = Bounded <S>;
325 fn try_from (bounded : Bounded <S>) -> Result <Self, Self::Error> {
326 match bounded {
327 Bounded::Sphere (sphere) => Ok (sphere),
328 _ => Err (bounded)
329 }
330 }
331}
332impl <S> Shape <S> for Sphere <S> where S : Real + std::fmt::Debug { }
333impl <S> Aabb <S> for Sphere <S> where S : Real + std::fmt::Debug {
334 fn aabb (&self) -> Aabb3 <S> {
335 Aabb3::with_minmax ([-*self.radius; 3].into(), [ *self.radius; 3].into())
336 }
337}
338impl <S : Real> Stereometric <S> for Sphere <S> {
339 fn volume (&self) -> Positive <S> {
340 let four = Positive::unchecked (S::four());
341 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
342 four * frac_pi_3 * self.radius * self.radius * self.radius
343 }
344}
345
346impl <S : Ring> Capsule <S> {
350 #[inline]
366 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
367 assert_ne!(radius, S::zero());
368 assert_ne!(half_height, S::zero());
369 let radius = Positive::unchecked (radius.abs());
370 let half_height = Positive::unchecked (half_height.abs());
371 Capsule { radius, half_height }
372 }
373 #[inline]
389 pub fn unchecked (radius : S, half_height : S) -> Self where
390 S : std::fmt::Debug
391 {
392 debug_assert_ne!(radius, S::zero());
393 let radius = Positive::unchecked (radius.abs());
394 let half_height = Positive::unchecked (half_height.abs());
395 Capsule { radius, half_height }
396 }
397 #[inline]
399 pub fn height (&self) -> NonNegative <S> where S : Field {
400 self.half_height * NonNegative::unchecked (S::two())
401 }
402 #[inline]
403 pub const fn capsule3 (&self, center : Point3 <S>) -> Capsule3 <S> {
404 Capsule3 { center, radius: self.radius, half_height: self.half_height }
405 }
406}
407impl <S> Shape <S> for Capsule <S> where S : Real + std::fmt::Debug { }
408impl <S> Aabb <S> for Capsule <S> where S : Real + std::fmt::Debug {
409 fn aabb (&self) -> Aabb3 <S> {
410 let r = *self.radius;
411 let hh = *self.half_height;
412 Aabb3::with_minmax ([-r, -r, -r - hh].into(), [ r, r, r + hh].into())
413 }
414}
415impl <S : Real> Stereometric <S> for Capsule <S> {
416 fn volume (&self) -> Positive <S> {
417 let r = self.radius;
418 let h = self.height();
419 let r2 = r * r;
420 let r3 = r2 * r;
421 let pi = Positive::unchecked (S::pi());
422 let four = Positive::unchecked (S::four());
423 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
424 let cylinder_volume = pi * r2 * h;
425 let sphere_volume = four * frac_pi_3 * r3;
426 sphere_volume + cylinder_volume
427 }
428}
429
430impl <S : Ring> Cylinder <S> {
434 #[inline]
435 pub fn unit() -> Self where S : Field {
437 use num::One;
438 Cylinder {
439 radius: Positive::one(), half_height: Positive::one()
440 }
441 }
442 #[inline]
452 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
453 assert_ne!(radius, S::zero());
454 assert_ne!(half_height, S::zero());
455 let radius = Positive::unchecked (radius.abs());
456 let half_height = Positive::unchecked (half_height.abs());
457 Cylinder { radius, half_height }
458 }
459 #[inline]
469 pub fn unchecked (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
470 debug_assert_ne!(radius, S::zero());
471 debug_assert_ne!(half_height, S::zero());
472 let radius = Positive::unchecked (radius.abs());
473 let half_height = Positive::unchecked (half_height.abs());
474 Cylinder { radius, half_height }
475 }
476 #[inline]
477 pub const fn cylinder3 (&self, center : Point3 <S>) -> Cylinder3 <S> {
478 Cylinder3 { center, radius: self.radius, half_height: self.half_height }
479 }
480 #[inline]
481 pub fn height (&self) -> Positive <S> where S : Field {
482 self.half_height * Positive::unchecked (S::two())
483 }
484}
485impl <S> Shape <S> for Cylinder <S> where S : Real + std::fmt::Debug { }
486impl <S> Aabb <S> for Cylinder <S> where S : Real + std::fmt::Debug {
487 fn aabb (&self) -> Aabb3 <S> {
488 let r = *self.radius;
489 let hh = *self.half_height;
490 Aabb3::with_minmax ([-r, -r, -hh].into(), [ r, r, hh].into())
491 }
492}
493impl <S : Real> Stereometric <S> for Cylinder <S> {
494 fn volume (&self) -> Positive <S> {
495 let pi = Positive::unchecked (S::pi());
496 pi * self.radius * self.radius * self.height()
497 }
498}
499
500impl <S : Ring> Cone <S> {
504 #[inline]
513 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
514 assert_ne!(radius, S::zero());
515 assert_ne!(half_height, S::zero());
516 let radius = Positive::unchecked (radius.abs());
517 let half_height = Positive::unchecked (half_height.abs());
518 Cone { radius, half_height }
519 }
520}
521impl <S> Shape <S> for Cone <S> where S : Real + std::fmt::Debug { }
522impl <S> Aabb <S> for Cone <S> where S : Real + std::fmt::Debug {
523 fn aabb (&self) -> Aabb3 <S> {
524 let r = *self.radius;
525 let hh = *self.half_height;
526 Aabb3::with_minmax ([-r, -r, -hh].into(), [ r, r, hh].into())
527 }
528}
529impl <S : Real> Stereometric <S> for Cone <S> {
530 fn volume (&self) -> Positive <S> {
531 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
532 let two = Positive::unchecked (S::two());
533 frac_pi_3 * self.radius * self.radius * two * self.half_height
534 }
535}
536
537impl <S : Ring> Cube <S> {
541 #[inline]
550 pub fn noisy (half_extent : S) -> Self where S : std::fmt::Debug {
551 assert_ne!(half_extent, S::zero());
552 let half_extent = Positive::unchecked (half_extent.abs());
553 Cube { half_extent }
554 }
555 #[inline]
556 pub fn extent (&self) -> Positive <S> where S : Field {
557 self.half_extent * Positive::unchecked (S::two())
558 }
559}
560impl <S> Shape <S> for Cube <S> where S : Field + std::fmt::Debug { }
561impl <S> Aabb <S> for Cube <S> where S : Field + std::fmt::Debug {
562 fn aabb (&self) -> Aabb3 <S> {
563 Aabb3::with_minmax (
564 [-*self.half_extent; 3].into(),
565 [ *self.half_extent; 3].into())
566 }
567}
568impl <S : Field> Stereometric <S> for Cube <S> {
569 fn volume (&self) -> Positive <S> {
570 let extent = self.extent();
571 extent * extent * extent
572 }
573}
574
575impl <S : Ring> Cuboid <S> {
579 #[inline]
588 pub fn noisy (half_extents : [S; 3]) -> Self where S : std::fmt::Debug {
589 assert_ne!(half_extents[0], S::zero());
590 assert_ne!(half_extents[1], S::zero());
591 assert_ne!(half_extents[2], S::zero());
592 let half_extent_x = Positive::unchecked (half_extents[0].abs());
593 let half_extent_y = Positive::unchecked (half_extents[1].abs());
594 let half_extent_z = Positive::unchecked (half_extents[2].abs());
595 Cuboid { half_extent_x, half_extent_y, half_extent_z }
596 }
597 #[inline]
598 pub fn extents (&self) -> [Positive <S>; 3] where S : Field {
599 let two = Positive::unchecked (S::two());
600 [ self.half_extent_x * two,
601 self.half_extent_y * two,
602 self.half_extent_z * two
603 ]
604 }
605 #[inline]
606 pub fn half_extents_vec (&self) -> Vector3 <S> {
607 [ *self.half_extent_x,
608 *self.half_extent_y,
609 *self.half_extent_z
610 ].into()
611 }
612 #[inline]
613 pub fn max (&self) -> Point3 <S> {
614 Vector3 {
615 x: *self.half_extent_x,
616 y: *self.half_extent_y,
617 z: *self.half_extent_z
618 }.into()
619 }
620 #[inline]
621 pub fn min (&self) -> Point3 <S> {
622 Vector3 {
623 x: -*self.half_extent_x,
624 y: -*self.half_extent_y,
625 z: -*self.half_extent_z
626 }.into()
627 }
628 #[inline]
629 pub fn aabb3 (&self, center : Point3 <S>) -> Aabb3 <S> where S : std::fmt::Debug {
630 Aabb3::with_minmax (
631 center + self.min().0,
632 center + self.max().0
633 )
634 }
635}
636impl <S> From <Aabb3 <S>> for Cuboid <S> where S : Field {
637 fn from (aabb : Aabb3 <S>) -> Self {
638 Cuboid {
640 half_extent_x: Positive::unchecked (*aabb.width() / S::two()),
641 half_extent_y: Positive::unchecked (*aabb.depth() / S::two()),
642 half_extent_z: Positive::unchecked (*aabb.height() / S::two())
643 }
644 }
645}
646impl <S> Shape <S> for Cuboid <S> where S : Field + std::fmt::Debug { }
647impl <S> Aabb <S> for Cuboid <S> where S : Field + std::fmt::Debug {
648 fn aabb (&self) -> Aabb3 <S> {
649 Aabb3::with_minmax (self.min(), self.max())
650 }
651}
652impl <S : Field> Stereometric <S> for Cuboid <S> {
653 fn volume (&self) -> Positive <S> {
654 let [x, y, z] = self.extents();
655 x * y * z
656 }
657}
658
659impl Orthant {
663 pub fn try_from <S : Real> (halfspace : &Halfspace <S>) -> Option <Self> {
664 SignedAxis3::try_from (&halfspace.normal_vector)
665 .map (|normal_axis| Orthant { normal_axis })
666 }
667}
668impl From <SignedAxis3> for Orthant {
669 fn from (normal_axis : SignedAxis3) -> Self {
670 Orthant { normal_axis }
671 }
672}
673impl <S> Shape <S> for Orthant where
674 S : Field + num::float::FloatCore + std::fmt::Debug
675{ }
676impl <S> Aabb <S> for Orthant where
677 S : Field + num::float::FloatCore + std::fmt::Debug
678{
679 fn aabb (&self) -> Aabb3 <S> {
698 use num::float::FloatCore;
699 let axis_vec = self.normal_axis.to_vec::<S>();
700 let surface = Vector3::broadcast (S::one()) - axis_vec.map (FloatCore::abs);
701
702 let do_min = |i| if surface[i] == S::one() || axis_vec[i] == S::one() {
703 S::neg_infinity()
704 } else {
705 debug_assert_eq!(axis_vec[i], -S::one());
706 S::zero()
707 };
708 let do_max = |i| if surface[i] == S::one() {
709 S::infinity()
710 } else if axis_vec[i] == S::one() {
711 S::zero()
712 } else {
713 debug_assert_eq!(axis_vec[i], -S::one());
714 S::infinity()
715 };
716 let min = [ do_min (0), do_min (1), do_min (2) ].into();
717 let max = [ do_max (0), do_max (1), do_max (2) ].into();
718
719 Aabb3::with_minmax (min, max)
720 }
721}
722impl <S> Stereometric <S> for Orthant where S : Ring + num::float::FloatCore {
723 fn volume (&self) -> Positive <S> {
725 Positive::infinity()
726 }
727}
728
729impl <S : Real> From <Unit3 <S>> for Halfspace <S> {
733 fn from (normal_vector : Unit3 <S>) -> Self {
734 Halfspace { normal_vector }
735 }
736}
737impl <S> Shape <S> for Halfspace <S> where
738 S : Real + num::float::FloatCore + std::fmt::Debug
739{ }
740impl <S> Aabb <S> for Halfspace <S> where
741 S : Real + num::float::FloatCore + std::fmt::Debug
742{
743 fn aabb (&self) -> Aabb3 <S> {
744 if let Some (orthant) = Orthant::try_from (self) {
745 orthant.aabb()
746 } else {
747 Aabb3::with_minmax ([S::neg_infinity(); 3].into(), [S::infinity(); 3].into())
748 }
749 }
750}
751impl <S> Stereometric <S> for Halfspace <S> where S : Real + num::float::FloatCore {
752 fn volume (&self) -> Positive <S> {
754 Positive::infinity()
755 }
756}
757
758impl <S> Halfspace <S> {
763 #[inline]
764 pub fn numcast <T> (self) -> Option <Halfspace <T>> where
765 S : num::NumCast,
766 T : num::NumCast
767 {
768 Some (Halfspace {
769 normal_vector: self.normal_vector.numcast()?
770 })
771 }
772}
773
774impl <S> Sphere <S> {
775 #[inline]
776 pub fn numcast <T> (self) -> Option <Sphere <T>> where
777 S : num::NumCast,
778 T : num::NumCast
779 {
780 Some (Sphere {
781 radius: self.radius.numcast()?
782 })
783 }
784}
785
786impl <S> Capsule <S> {
787 #[inline]
788 pub fn numcast <T> (self) -> Option <Capsule <T>> where
789 S : num::NumCast,
790 T : num::NumCast
791 {
792 Some (Capsule {
793 radius: self.radius.numcast()?,
794 half_height: self.half_height.numcast()?
795 })
796 }
797}
798
799impl <S> Cylinder <S> {
800 #[inline]
801 pub fn numcast <T> (self) -> Option <Cylinder <T>> where
802 S : num::NumCast,
803 T : num::NumCast
804 {
805 Some (Cylinder {
806 radius: self.radius.numcast()?,
807 half_height: self.half_height.numcast()?
808 })
809 }
810}
811
812impl <S> Cone <S> {
813 #[inline]
814 pub fn numcast <T> (self) -> Option <Cone <T>> where
815 S : num::NumCast,
816 T : num::NumCast
817 {
818 Some (Cone {
819 radius: self.radius.numcast()?,
820 half_height: self.half_height.numcast()?
821 })
822 }
823}
824
825impl <S> Cube <S> {
826 #[inline]
827 pub fn numcast <T> (self) -> Option <Cube <T>> where
828 S : num::NumCast,
829 T : num::NumCast
830 {
831 Some (Cube {
832 half_extent: self.half_extent.numcast()?
833 })
834 }
835}
836
837impl <S> Cuboid <S> {
838 #[inline]
839 pub fn numcast <T> (self) -> Option <Cuboid <T>> where
840 S : num::NumCast,
841 T : num::NumCast
842 {
843 Some (Cuboid {
844 half_extent_x: self.half_extent_x.numcast()?,
845 half_extent_y: self.half_extent_y.numcast()?,
846 half_extent_z: self.half_extent_z.numcast()?
847 })
848 }
849}