1use derive_more::{From, TryInto};
4#[cfg(feature = "derive_serdes")]
5use serde::{Deserialize, Serialize};
6
7use crate::*;
8
9use super::{Aabb3, Capsule3, Cone3, Cylinder3, Hull3, Plane3, Primitive, Sphere3};
10
11pub trait Shape <S> : Aabb <S> { fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>>;
26}
27
28pub trait Aabb <S> {
33 fn aabb (&self) -> Aabb3 <S>;
34}
35
36pub trait Bsphere <S> {
39 fn sphere (&self) -> Sphere3 <S>;
40}
41
42pub trait Stereometric <S> {
46 fn volume (&self) -> Positive <S>;
47}
48
49#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
56#[derive(Clone, Debug, Eq, PartialEq, From, TryInto)]
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(Clone, Debug, Eq, PartialEq, From, TryInto)]
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 Hull (Hull <S>)
73}
74
75#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
77#[derive(Clone, Debug, Eq, PartialEq, From, TryInto)]
78pub enum Unbounded <S> {
79 Orthant (Orthant),
80 Halfspace (Halfspace <S>)
81}
82
83#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
89#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90pub struct Orthant {
91 pub normal_axis : SignedAxis3
92}
93
94#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
97pub struct Halfspace <S> {
98 pub normal : Unit3 <S>
99}
100
101#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
103#[derive(Clone, Copy, Debug, Eq, PartialEq)]
104pub struct Sphere <S> {
105 pub radius : Positive <S>
106}
107
108#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub struct Capsule <S> {
112 pub radius : Positive <S>,
113 pub half_height : Positive <S>
114}
115
116#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
118#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119pub struct Cylinder <S> {
120 pub radius : Positive <S>,
121 pub half_height : Positive <S>
122}
123
124#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
126#[derive(Clone, Copy, Debug, Eq, PartialEq)]
127pub struct Cone <S> {
128 pub radius : Positive <S>,
129 pub half_height : Positive <S>
130}
131
132#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
135#[derive(Clone, Copy, Debug, Eq, PartialEq)]
136pub struct Cube <S> {
137 pub extent : Positive <S>
138}
139
140#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
142#[derive(Clone, Copy, Debug, Eq, PartialEq)]
143pub struct Cuboid <S> {
144 pub extents : Vector3 <Positive <S>>
145}
146
147pub type Hull <S> = Hull3 <S>;
149
150pub fn report_sizes() {
155 use std::mem::size_of;
156 println!("shape report sizes...");
157
158 show!(size_of::<Variant <f32>>());
159 show!(size_of::<Variant <f64>>());
160
161 show!(size_of::<Bounded <f32>>());
162 show!(size_of::<Bounded <f64>>());
163
164 show!(size_of::<Unbounded <f32>>());
165 show!(size_of::<Unbounded <f64>>());
166
167 show!(size_of::<Sphere <f32>>());
168 show!(size_of::<Capsule <f32>>());
169 show!(size_of::<Cylinder <f32>>());
170 show!(size_of::<Cone <f32>>());
171 show!(size_of::<Cube <f32>>());
172 show!(size_of::<Cuboid <f32>>());
173
174 show!(size_of::<Orthant>());
175 show!(size_of::<Halfspace <f32>>());
176
177 show!(size_of::<Sphere <f64>>());
178 show!(size_of::<Capsule <f64>>());
179 show!(size_of::<Cylinder <f64>>());
180 show!(size_of::<Cone <f64>>());
181 show!(size_of::<Cube <f64>>());
182 show!(size_of::<Cuboid <f64>>());
183
184 show!(size_of::<Orthant>());
185 show!(size_of::<Halfspace <f64>>());
186
187 println!("...shape report sizes");
188}
189
190impl <S> Shape <S> for Variant <S> where
198 S : Real + num::real::Real + num::float::FloatCore + approx::RelativeEq
199 + std::fmt::Debug + MaybeSerDes + 'static
200{
201 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
202 match self {
203 Variant::Bounded (bounded) => bounded.to_primitive (position),
204 Variant::Unbounded (unbounded) => unbounded.to_primitive (position)
205 }
206 }
207}
208impl <S> Aabb <S> for Variant <S> where
209 S : Real + num::float::FloatCore + std::fmt::Debug
210{
211 fn aabb (&self) -> Aabb3 <S> {
213 match self {
214 Variant::Bounded (bounded) => bounded.aabb(),
215 Variant::Unbounded (unbounded) => unbounded.aabb()
216 }
217 }
218}
219impl <S> Stereometric <S> for Variant <S> where
220 S : Real + num::float::FloatCore + std::fmt::Debug
221{
222 fn volume (&self) -> Positive <S> {
224 match self {
225 Variant::Bounded (bounded) => bounded.volume(),
226 Variant::Unbounded (unbounded) => unbounded.volume()
227 }
228 }
229}
230
231impl <S> Shape <S> for Bounded <S> where
235 S : Real + num::real::Real + approx::RelativeEq + std::fmt::Debug + MaybeSerDes
236 + 'static
237{
238 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
239 match self {
240 Bounded::Sphere (sphere) => sphere.to_primitive (position),
241 Bounded::Capsule (capsule) => capsule.to_primitive (position),
242 Bounded::Cylinder (cylinder) => cylinder.to_primitive (position),
243 Bounded::Cuboid (cuboid) => cuboid.to_primitive (position),
244 Bounded::Hull (hull) => hull.to_primitive (position),
245 Bounded::Cone (cone) => cone.to_primitive (position),
246 Bounded::Cube (cube) => cube.to_primitive (position)
247 }
248 }
249}
250impl <S> Aabb <S> for Bounded <S> where S : Real + std::fmt::Debug {
251 fn aabb (&self) -> Aabb3 <S> {
252 match self {
253 Bounded::Sphere (sphere) => sphere.aabb(),
254 Bounded::Capsule (capsule) => capsule.aabb(),
255 Bounded::Cylinder (cylinder) => cylinder.aabb(),
256 Bounded::Cone (cone) => cone.aabb(),
257 Bounded::Cube (cube) => cube.aabb(),
258 Bounded::Cuboid (cuboid) => cuboid.aabb(),
259 Bounded::Hull (hull) => hull.aabb()
260 }
261 }
262}
263impl <S : Real> Stereometric <S> for Bounded <S> {
264 fn volume (&self) -> Positive <S> {
266 match self {
267 Bounded::Sphere (sphere) => sphere.volume(),
268 Bounded::Capsule (capsule) => capsule.volume(),
269 Bounded::Cylinder (cylinder) => cylinder.volume(),
270 Bounded::Cone (cone) => cone.volume(),
271 Bounded::Cube (cube) => cube.volume(),
272 Bounded::Cuboid (cuboid) => cuboid.volume(),
273 Bounded::Hull (hull) => hull.volume()
274 }
275 }
276}
277
278impl <S> Shape <S> for Unbounded <S> where
282 S : Real + num::float::FloatCore + approx::RelativeEq + std::fmt::Debug + 'static
283{
284 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
285 match self {
286 Unbounded::Orthant (orthant) => orthant.to_primitive (position),
287 Unbounded::Halfspace (halfspace) => halfspace.to_primitive (position)
288 }
289 }
290}
291impl <S> Aabb <S> for Unbounded <S> where
292 S : Real + num::float::FloatCore + std::fmt::Debug
293{
294 fn aabb (&self) -> Aabb3 <S> {
295 match self {
296 Unbounded::Orthant (orthant) => orthant.aabb(),
297 Unbounded::Halfspace (halfspace) => halfspace.aabb()
298 }
299 }
300}
301impl <S> Stereometric <S> for Unbounded <S> where
302 S : Real + num::float::FloatCore + std::fmt::Debug
303{
304 fn volume (&self) -> Positive <S> {
306 if cfg!(debug_assertions) {
307 let volume = match self {
308 Unbounded::Orthant (orthant) => orthant.volume(),
309 Unbounded::Halfspace (halfspace) => halfspace.volume()
310 };
311 debug_assert_eq!(*volume, S::infinity());
312 }
313 Positive::infinity()
314 }
315}
316
317impl <S : OrderedRing> Sphere <S> {
321 #[inline]
322 pub fn unit() -> Self where S : Field {
324 use num::One;
325 Sphere { radius: Positive::one() }
326 }
327 #[inline]
336 pub fn noisy (radius : S) -> Self where S : std::fmt::Debug {
337 assert_ne!(radius, S::zero());
338 Sphere { radius: Positive::unchecked (radius.abs()) }
339 }
340 #[inline]
349 pub fn unchecked (radius : S) -> Self where S : std::fmt::Debug {
350 debug_assert_ne!(radius, S::zero());
351 Sphere { radius: Positive::unchecked (radius.abs()) }
352 }
353 #[inline]
354 pub const fn sphere3 (self, center : Point3 <S>) -> Sphere3 <S> {
355 Sphere3 { center, radius: self.radius }
356 }
357}
358impl <S> Shape <S> for Sphere <S> where
359 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
360{
361 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
362 Box::new (self.sphere3 (position))
363 }
364}
365impl <S> Aabb <S> for Sphere <S> where S : Real + std::fmt::Debug {
366 fn aabb (&self) -> Aabb3 <S> {
367 Aabb3::with_minmax_unchecked ([-*self.radius; 3].into(), [ *self.radius; 3].into())
368 }
369}
370impl <S : Real> Stereometric <S> for Sphere <S> {
371 fn volume (&self) -> Positive <S> {
372 let four = Positive::unchecked (S::four());
373 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
374 four * frac_pi_3 * self.radius * self.radius * self.radius
375 }
376}
377impl_numcast_fields!(Sphere, radius);
378
379impl <S : OrderedRing> Capsule <S> {
383 #[inline]
399 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
400 assert_ne!(radius, S::zero());
401 assert_ne!(half_height, S::zero());
402 let radius = Positive::unchecked (radius.abs());
403 let half_height = Positive::unchecked (half_height.abs());
404 Capsule { radius, half_height }
405 }
406 #[inline]
422 pub fn unchecked (radius : S, half_height : S) -> Self where
423 S : std::fmt::Debug
424 {
425 debug_assert_ne!(radius, S::zero());
426 let radius = Positive::unchecked (radius.abs());
427 let half_height = Positive::unchecked (half_height.abs());
428 Capsule { radius, half_height }
429 }
430 #[inline]
432 pub fn height (self) -> NonNegative <S> {
433 self.half_height * NonNegative::unchecked (S::two())
434 }
435 #[inline]
436 pub const fn capsule3 (self, center : Point3 <S>) -> Capsule3 <S> {
437 Capsule3 { center, radius: self.radius, half_height: self.half_height }
438 }
439}
440impl <S> Shape <S> for Capsule <S> where
441 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
442{
443 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
444 Box::new (self.capsule3 (position))
445 }
446}
447impl <S> Aabb <S> for Capsule <S> where S : Real + std::fmt::Debug {
448 fn aabb (&self) -> Aabb3 <S> {
449 let r = *self.radius;
450 let hh = *self.half_height;
451 Aabb3::with_minmax_unchecked ([-r, -r, -r - hh].into(), [ r, r, r + hh].into())
452 }
453}
454impl <S : Real> Stereometric <S> for Capsule <S> {
455 fn volume (&self) -> Positive <S> {
456 let r = self.radius;
457 let h = self.height();
458 let r2 = r * r;
459 let r3 = r2 * r;
460 let pi = Positive::unchecked (S::pi());
461 let four = Positive::unchecked (S::four());
462 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
463 let cylinder_volume = pi * r2 * h;
464 let sphere_volume = four * frac_pi_3 * r3;
465 sphere_volume + cylinder_volume
466 }
467}
468impl_numcast_fields!(Capsule, radius, half_height);
469
470impl <S : OrderedRing> Cylinder <S> {
474 #[inline]
475 pub fn unit() -> Self where S : Field {
477 use num::One;
478 Cylinder {
479 radius: Positive::one(), half_height: Positive::one()
480 }
481 }
482 #[inline]
492 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
493 assert_ne!(radius, S::zero());
494 assert_ne!(half_height, S::zero());
495 let radius = Positive::unchecked (radius.abs());
496 let half_height = Positive::unchecked (half_height.abs());
497 Cylinder { radius, half_height }
498 }
499 #[inline]
509 pub fn unchecked (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
510 debug_assert_ne!(radius, S::zero());
511 debug_assert_ne!(half_height, S::zero());
512 let radius = Positive::unchecked (radius.abs());
513 let half_height = Positive::unchecked (half_height.abs());
514 Cylinder { radius, half_height }
515 }
516 #[inline]
517 pub const fn cylinder3 (self, center : Point3 <S>) -> Cylinder3 <S> {
518 Cylinder3 { center, radius: self.radius, half_height: self.half_height }
519 }
520 #[inline]
521 pub fn height (self) -> Positive <S> {
522 self.half_height * Positive::unchecked (S::two())
523 }
524}
525impl <S> Shape <S> for Cylinder <S> where
526 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
527{
528 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
529 Box::new (self.cylinder3 (position))
530 }
531}
532impl <S> Aabb <S> for Cylinder <S> where S : Real + std::fmt::Debug {
533 fn aabb (&self) -> Aabb3 <S> {
534 let r = *self.radius;
535 let hh = *self.half_height;
536 Aabb3::with_minmax_unchecked ([-r, -r, -hh].into(), [ r, r, hh].into())
537 }
538}
539impl <S : Real> Stereometric <S> for Cylinder <S> {
540 fn volume (&self) -> Positive <S> {
541 let pi = Positive::unchecked (S::pi());
542 pi * self.radius * self.radius * self.height()
543 }
544}
545impl_numcast_fields!(Cylinder, radius, half_height);
546
547impl <S : OrderedRing> Cone <S> {
551 #[inline]
560 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
561 assert_ne!(radius, S::zero());
562 assert_ne!(half_height, S::zero());
563 let radius = Positive::unchecked (radius.abs());
564 let half_height = Positive::unchecked (half_height.abs());
565 Cone { radius, half_height }
566 }
567}
568impl <S> Shape <S> for Cone <S> where
569 S : Real + num::real::Real + approx::RelativeEq + std::fmt::Debug + MaybeSerDes
570 + 'static
571{
572 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
573 Box::new (Cone3 {
574 center: position,
575 half_height: self.half_height,
576 radius: self.radius
577 })
578 }
579}
580impl <S> Aabb <S> for Cone <S> where S : Real + std::fmt::Debug {
581 fn aabb (&self) -> Aabb3 <S> {
582 let r = *self.radius;
583 let hh = *self.half_height;
584 Aabb3::with_minmax_unchecked ([-r, -r, -hh].into(), [ r, r, hh].into())
585 }
586}
587impl <S : Real> Stereometric <S> for Cone <S> {
588 fn volume (&self) -> Positive <S> {
589 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
590 let two = Positive::unchecked (S::two());
591 frac_pi_3 * self.radius * self.radius * two * self.half_height
592 }
593}
594impl_numcast_fields!(Cone, radius, half_height);
595
596impl <S : OrderedRing> Cube <S> {
600 #[inline]
609 pub fn noisy (extent : S) -> Self where S : std::fmt::Debug {
610 assert_ne!(extent, S::zero());
611 Cube { extent: Positive::unchecked (extent.abs()) }
612 }
613 #[inline]
614 pub fn dimensions (self) -> Vector3 <Positive <S>> {
615 Vector3::broadcast (self.extent * Positive::unchecked (S::two()))
616 }
617 #[inline]
618 pub fn width (self) -> Positive <S> {
619 self.extent * Positive::unchecked (S::two())
620 }
621 #[inline]
622 pub fn height (self) -> Positive <S> {
623 self.extent * Positive::unchecked (S::two())
624 }
625 #[inline]
626 pub fn depth (self) -> Positive <S> {
627 self.extent * Positive::unchecked (S::two())
628 }
629}
630impl <S> Shape <S> for Cube <S> where S : OrderedField + std::fmt::Debug + 'static {
631 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
632 let mut aabb = self.aabb();
633 aabb.translate (position.0);
634 Box::new (aabb)
635 }
636}
637impl <S> Aabb <S> for Cube <S> where S : OrderedField + std::fmt::Debug {
638 fn aabb (&self) -> Aabb3 <S> {
639 Aabb3::with_minmax_unchecked (
640 [-*self.extent; 3].into(),
641 [ *self.extent; 3].into())
642 }
643}
644impl <S : OrderedField> Stereometric <S> for Cube <S> {
645 fn volume (&self) -> Positive <S> {
646 self.extent * self.extent * self.extent
647 }
648}
649impl_numcast_fields!(Cube, extent);
650
651impl <S : OrderedRing> Cuboid <S> {
655 #[inline]
664 pub fn noisy (extents : [S; 3]) -> Self where S : std::fmt::Debug {
665 assert_ne!(extents[0], S::zero());
666 assert_ne!(extents[1], S::zero());
667 assert_ne!(extents[2], S::zero());
668 let extents = vector3 (
669 Positive::unchecked (extents[0].abs()),
670 Positive::unchecked (extents[1].abs()),
671 Positive::unchecked (extents[2].abs()));
672 Cuboid { extents }
673 }
674 #[inline]
675 pub fn dimensions (self) -> Vector3 <Positive <S>> {
676 self.extents * Positive::unchecked (S::two())
677 }
678 #[inline]
680 pub fn width (self) -> Positive <S> {
681 self.extents.x * Positive::unchecked (S::two())
682 }
683 #[inline]
685 pub fn depth (self) -> Positive <S> {
686 self.extents.y * Positive::unchecked (S::two())
687 }
688 #[inline]
690 pub fn height (self) -> Positive <S> {
691 self.extents.z * Positive::unchecked (S::two())
692 }
693 #[inline]
694 pub fn max (self) -> Point3 <S> {
695 self.extents.map (|e| *e).into()
696 }
697 #[inline]
698 pub fn min (self) -> Point3 <S> {
699 self.extents.map (|e| -*e).into()
700 }
701 #[inline]
702 pub fn aabb3 (self, center : Point3 <S>) -> Aabb3 <S> where S : std::fmt::Debug {
703 Aabb3::with_minmax_unchecked (
704 center + self.min().0,
705 center + self.max().0)
706 }
707}
708impl <S> TryFrom <Aabb3 <S>> for Cuboid <S> where S : OrderedField {
709 type Error = Aabb3 <S>;
710 fn try_from (aabb : Aabb3 <S>) -> Result <Self, Self::Error> {
711 let extents = {
712 let [x, y, z] = aabb.extents().into_array();
713 vector3 (
714 Positive::new (*x).ok_or (aabb)?,
715 Positive::new (*y).ok_or (aabb)?,
716 Positive::new (*z).ok_or (aabb)?)
717 };
718 Ok (Cuboid { extents })
719 }
720}
721impl <S> Shape <S> for Cuboid <S> where S : OrderedField + std::fmt::Debug + 'static {
722 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
723 let mut aabb = self.aabb();
724 aabb.translate (position.0);
725 Box::new (aabb)
726 }
727}
728impl <S> Aabb <S> for Cuboid <S> where S : OrderedField + std::fmt::Debug {
729 fn aabb (&self) -> Aabb3 <S> {
730 Aabb3::with_minmax_unchecked (self.min(), self.max())
731 }
732}
733impl <S : OrderedField> Stereometric <S> for Cuboid <S> {
734 fn volume (&self) -> Positive <S> {
735 self.extents.x * self.extents.y * self.extents.z
736 }
737}
738impl_numcast_fields!(Cuboid, extents);
739
740impl <S> Shape <S> for Hull <S> where
744 S : Real + num::real::Real + std::fmt::Debug + MaybeSerDes + 'static
745{
746 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
747 let mut aabb = self.clone();
748 aabb.translate (position.0);
749 Box::new (aabb)
750 }
751}
752impl <S> Aabb <S> for Hull <S> where S : Real + std::fmt::Debug {
753 fn aabb (&self) -> Aabb3 <S> {
754 Aabb3::containing (self.points()).unwrap()
757 }
758}
759impl <S : Real> Stereometric <S> for Hull <S> {
760 fn volume (&self) -> Positive <S> {
761 unimplemented!("TODO: hull volume")
762 }
763}
764
765impl <S : Field> TryFrom <Halfspace <S>> for Orthant {
769 type Error = Halfspace <S>;
770 fn try_from (halfspace : Halfspace <S>) -> Result <Self, Self::Error> {
771 SignedAxis3::try_from (*halfspace.normal)
772 .map (|normal_axis| Orthant { normal_axis })
773 .map_err (|_| halfspace)
774 }
775}
776impl From <SignedAxis3> for Orthant {
777 fn from (normal_axis : SignedAxis3) -> Self {
778 Orthant { normal_axis }
779 }
780}
781impl <S> Shape <S> for Orthant where
782 S : Real + num::float::FloatCore + approx::RelativeEq + std::fmt::Debug + 'static
783{
784 #[expect(clippy::renamed_function_params)]
785 fn to_primitive (&self, base : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
786 Box::new (Plane3 { base, normal: self.normal_axis.to_unit() })
787 }
788}
789impl <S> Aabb <S> for Orthant where
790 S : OrderedField + num::float::FloatCore + std::fmt::Debug
791{
792 fn aabb (&self) -> Aabb3 <S> {
811 use num::float::FloatCore;
812 let axis_vec = self.normal_axis.to_vec::<S>();
813 let surface = Vector3::broadcast (S::one()) - axis_vec.map (FloatCore::abs);
814
815 let do_min = |i| if surface[i] == S::one() || axis_vec[i] == S::one() {
816 S::neg_infinity()
817 } else {
818 debug_assert_eq!(axis_vec[i], -S::one());
819 S::zero()
820 };
821 let do_max = |i| if surface[i] == S::one() {
822 S::infinity()
823 } else if axis_vec[i] == S::one() {
824 S::zero()
825 } else {
826 debug_assert_eq!(axis_vec[i], -S::one());
827 S::infinity()
828 };
829 let min = [ do_min (0), do_min (1), do_min (2) ].into();
830 let max = [ do_max (0), do_max (1), do_max (2) ].into();
831
832 Aabb3::with_minmax_unchecked (min, max)
833 }
834}
835impl <S> Stereometric <S> for Orthant where S : OrderedRing + num::float::FloatCore {
836 fn volume (&self) -> Positive <S> {
838 Positive::infinity()
839 }
840}
841
842impl <S : Copy> Halfspace <S> {
846 #[inline]
847 pub const fn plane3 (self, base : Point3 <S>) -> Plane3 <S> {
848 Plane3 { base, normal: self.normal }
849 }
850}
851impl <S : Real> From <Unit3 <S>> for Halfspace <S> {
852 fn from (normal_vector : Unit3 <S>) -> Self {
853 Halfspace { normal: normal_vector }
854 }
855}
856impl <S> Shape <S> for Halfspace <S> where
857 S : Real + num::float::FloatCore + approx::RelativeEq + std::fmt::Debug + 'static
858{
859 fn to_primitive (&self, position : Point3 <S>) -> Box <dyn Primitive <S, Point3 <S>>> {
860 Box::new (self.plane3 (position))
861 }
862}
863impl <S> Aabb <S> for Halfspace <S> where
864 S : Real + num::float::FloatCore + std::fmt::Debug
865{
866 fn aabb (&self) -> Aabb3 <S> {
867 if let Ok (orthant) = Orthant::try_from (*self) {
868 orthant.aabb()
869 } else {
870 Aabb3::with_minmax_unchecked (
871 [S::neg_infinity(); 3].into(),
872 [S::infinity(); 3].into())
873 }
874 }
875}
876impl <S> Stereometric <S> for Halfspace <S> where S : Real + num::float::FloatCore {
877 fn volume (&self) -> Positive <S> {
879 Positive::infinity()
880 }
881}
882impl_numcast_fields!(Halfspace, normal);