1use std::convert::TryFrom;
4use derive_more::From;
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> {
35 fn aabb (&self) -> Aabb3 <S>;
36}
37
38pub trait Bsphere <S : Ring> : Stereometric <S> {
41 fn sphere (&self) -> Sphere3 <S>;
42}
43
44pub trait Stereometric <S : Ring> {
48 fn volume (&self) -> Positive <S>;
49}
50
51#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
58#[derive(From, Clone, Debug, PartialEq)]
59pub enum Variant <S> {
60 Bounded (Bounded <S>),
61 Unbounded (Unbounded <S>)
62}
63
64#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
66#[derive(From, Clone, Debug, PartialEq)]
67pub enum Bounded <S> {
68 Sphere (Sphere <S>),
69 Capsule (Capsule <S>),
70 Cylinder (Cylinder <S>),
71 Cone (Cone <S>),
72 Cube (Cube <S>),
73 Cuboid (Cuboid <S>)
74}
75
76#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
78#[derive(From, Clone, Debug, PartialEq)]
79pub enum Unbounded <S> {
80 Orthant (Orthant),
81 Halfspace (Halfspace <S>)
82}
83
84#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
90#[derive(Clone, Copy, Debug, PartialEq)]
91pub struct Orthant {
92 pub normal_axis : SignedAxis3
93}
94
95#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
97#[derive(Clone, Copy, Debug, PartialEq)]
98pub struct Halfspace <S> {
99 pub normal_vector : Unit3 <S>
100}
101
102#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
104#[derive(Clone, Copy, Debug, PartialEq)]
105pub struct Sphere <S> {
106 pub radius : Positive <S>
107}
108
109#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
111#[derive(Clone, Copy, Debug, PartialEq)]
112pub struct Capsule <S> {
113 pub radius : Positive <S>,
114 pub half_height : Positive <S>
115}
116
117#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
119#[derive(Clone, Copy, Debug, PartialEq)]
120pub struct Cylinder <S> {
121 pub radius : Positive <S>,
122 pub half_height : Positive <S>
123}
124
125#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
127#[derive(Clone, Copy, Debug, PartialEq)]
128pub struct Cone <S> {
129 pub radius : Positive <S>,
130 pub half_height : Positive <S>
131}
132
133#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
135#[derive(Clone, Copy, Debug, PartialEq)]
136pub struct Cube <S> {
137 pub half_extent : Positive <S>
138}
139
140#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
142#[derive(Clone, Copy, Debug, PartialEq)]
143pub struct Cuboid <S> {
144 pub half_extent_x : Positive <S>,
145 pub half_extent_y : Positive <S>,
146 pub half_extent_z : Positive <S>
147}
148
149impl <S> Halfspace <S> {
154 #[inline]
155 pub fn numcast <T> (self) -> Option <Halfspace <T>> where
156 S : num::NumCast,
157 T : num::NumCast
158 {
159 Some (Halfspace {
160 normal_vector: self.normal_vector.numcast()?
161 })
162 }
163}
164
165impl <S> Sphere <S> {
166 #[inline]
167 pub fn numcast <T> (self) -> Option <Sphere <T>> where
168 S : num::NumCast,
169 T : num::NumCast
170 {
171 Some (Sphere {
172 radius: self.radius.numcast()?
173 })
174 }
175}
176
177impl <S> Capsule <S> {
178 #[inline]
179 pub fn numcast <T> (self) -> Option <Capsule <T>> where
180 S : num::NumCast,
181 T : num::NumCast
182 {
183 Some (Capsule {
184 radius: self.radius.numcast()?,
185 half_height: self.half_height.numcast()?
186 })
187 }
188}
189
190impl <S> Cylinder <S> {
191 #[inline]
192 pub fn numcast <T> (self) -> Option <Cylinder <T>> where
193 S : num::NumCast,
194 T : num::NumCast
195 {
196 Some (Cylinder {
197 radius: self.radius.numcast()?,
198 half_height: self.half_height.numcast()?
199 })
200 }
201}
202
203impl <S> Cone <S> {
204 #[inline]
205 pub fn numcast <T> (self) -> Option <Cone <T>> where
206 S : num::NumCast,
207 T : num::NumCast
208 {
209 Some (Cone {
210 radius: self.radius.numcast()?,
211 half_height: self.half_height.numcast()?
212 })
213 }
214}
215
216impl <S> Cube <S> {
217 #[inline]
218 pub fn numcast <T> (self) -> Option <Cube <T>> where
219 S : num::NumCast,
220 T : num::NumCast
221 {
222 Some (Cube {
223 half_extent: self.half_extent.numcast()?
224 })
225 }
226}
227
228impl <S> Cuboid <S> {
229 #[inline]
230 pub fn numcast <T> (self) -> Option <Cuboid <T>> where
231 S : num::NumCast,
232 T : num::NumCast
233 {
234 Some (Cuboid {
235 half_extent_x: self.half_extent_x.numcast()?,
236 half_extent_y: self.half_extent_y.numcast()?,
237 half_extent_z: self.half_extent_z.numcast()?
238 })
239 }
240}
241
242pub fn report_sizes() {
247 use std::mem::size_of;
248 println!("shape report sizes...");
249
250 show!(size_of::<Variant <f32>>());
251 show!(size_of::<Variant <f64>>());
252
253 show!(size_of::<Bounded <f32>>());
254 show!(size_of::<Bounded <f64>>());
255
256 show!(size_of::<Unbounded <f32>>());
257 show!(size_of::<Unbounded <f64>>());
258
259 show!(size_of::<Sphere <f32>>());
260 show!(size_of::<Capsule <f32>>());
261 show!(size_of::<Cylinder <f32>>());
262 show!(size_of::<Cone <f32>>());
263 show!(size_of::<Cube <f32>>());
264 show!(size_of::<Cuboid <f32>>());
265
266 show!(size_of::<Orthant>());
267 show!(size_of::<Halfspace <f32>>());
268
269 show!(size_of::<Sphere <f64>>());
270 show!(size_of::<Capsule <f64>>());
271 show!(size_of::<Cylinder <f64>>());
272 show!(size_of::<Cone <f64>>());
273 show!(size_of::<Cube <f64>>());
274 show!(size_of::<Cuboid <f64>>());
275
276 show!(size_of::<Orthant>());
277 show!(size_of::<Halfspace <f64>>());
278
279 println!("...shape report sizes");
280}
281
282impl <S> Shape <S> for Variant <S> where
290 S : Real + num::float::FloatCore + std::fmt::Debug
291{ }
292impl <S> Aabb <S> for Variant <S> where
293 S : Real + num::float::FloatCore + std::fmt::Debug
294{
295 fn aabb (&self) -> Aabb3 <S> {
298 match self {
299 Variant::Bounded (bounded) => bounded.aabb(),
300 Variant::Unbounded (unbounded) => unbounded.aabb()
301 }
302 }
303}
304impl <S> Stereometric <S> for Variant <S> where
305 S : Real + num::float::FloatCore + std::fmt::Debug
306{
307 fn volume (&self) -> Positive <S> {
310 match self {
311 Variant::Bounded (bounded) => bounded.volume(),
312 Variant::Unbounded (unbounded) => unbounded.volume()
313 }
314 }
315}
316
317impl <S> TryFrom <Variant <S>> for Bounded <S> where S : Real + std::fmt::Debug {
321 type Error = Variant <S>;
322 fn try_from (variant : Variant <S>) -> Result <Self, Self::Error> {
323 match variant {
324 Variant::Bounded (bounded) => Ok (bounded),
325 _ => Err (variant)
326 }
327 }
328}
329impl <S> Shape <S> for Bounded <S> where S : Real + std::fmt::Debug { }
330impl <S> Aabb <S> for Bounded <S> where S : Real + std::fmt::Debug {
331 fn aabb (&self) -> Aabb3 <S> {
332 match self {
333 Bounded::Sphere (sphere) => sphere.aabb(),
334 Bounded::Capsule (capsule) => capsule.aabb(),
335 Bounded::Cylinder (cylinder) => cylinder.aabb(),
336 Bounded::Cone (cone) => cone.aabb(),
337 Bounded::Cube (cube) => cube.aabb(),
338 Bounded::Cuboid (cuboid) => cuboid.aabb()
339 }
340 }
341}
342impl <S : Real> Stereometric <S> for Bounded <S> {
343 fn volume (&self) -> Positive <S> {
345 match self {
346 Bounded::Sphere (sphere) => sphere.volume(),
347 Bounded::Capsule (capsule) => capsule.volume(),
348 Bounded::Cylinder (cylinder) => cylinder.volume(),
349 Bounded::Cone (cone) => cone.volume(),
350 Bounded::Cube (cube) => cube.volume(),
351 Bounded::Cuboid (cuboid) => cuboid.volume()
352 }
353 }
354}
355
356impl <S : Real> TryFrom <Variant <S>> for Unbounded <S> {
360 type Error = Variant <S>;
361 fn try_from (variant : Variant <S>) -> Result <Self, Self::Error> {
362 match variant {
363 Variant::Unbounded (unbounded) => Ok (unbounded),
364 _ => Err (variant)
365 }
366 }
367}
368impl <S> Shape <S> for Unbounded <S> where
369 S : Real + num::float::FloatCore + std::fmt::Debug
370{ }
371impl <S> Aabb <S> for Unbounded <S> where
372 S : Real + num::float::FloatCore + std::fmt::Debug
373{
374 fn aabb (&self) -> Aabb3 <S> {
375 match self {
376 Unbounded::Orthant (orthant) => orthant.aabb(),
377 Unbounded::Halfspace (halfspace) => halfspace.aabb()
378 }
379 }
380}
381impl <S> Stereometric <S> for Unbounded <S> where
382 S : Real + num::float::FloatCore + std::fmt::Debug
383{
384 fn volume (&self) -> Positive <S> {
386 if cfg!(debug_assertions) {
387 let volume = match self {
388 Unbounded::Orthant (orthant) => orthant.volume(),
389 Unbounded::Halfspace (halfspace) => halfspace.volume()
390 };
391 debug_assert_eq!(*volume, S::infinity());
392 }
393 Positive::infinity()
394 }
395}
396
397impl <S : Ring> Sphere <S> {
401 #[inline]
402 pub fn unit() -> Self where S : Field {
404 use num::One;
405 Sphere { radius: Positive::one() }
406 }
407 #[inline]
416 pub fn noisy (radius : S) -> Self where S : std::fmt::Debug {
417 assert_ne!(radius, S::zero());
418 Sphere { radius: Positive::unchecked (radius.abs()) }
419 }
420 #[inline]
429 pub fn unchecked (radius : S) -> Self where S : std::fmt::Debug {
430 debug_assert_ne!(radius, S::zero());
431 Sphere { radius: Positive::unchecked (radius.abs()) }
432 }
433 #[inline]
434 pub fn sphere3 (&self, center : Point3 <S>) -> Sphere3 <S> {
435 Sphere3 { center, radius: self.radius }
436 }
437}
438impl <S : Real> TryFrom <Bounded <S>> for Sphere <S> {
439 type Error = Bounded <S>;
440 fn try_from (bounded : Bounded <S>) -> Result <Self, Self::Error> {
441 match bounded {
442 Bounded::Sphere (sphere) => Ok (sphere),
443 _ => Err (bounded)
444 }
445 }
446}
447impl <S> Shape <S> for Sphere <S> where S : Real + std::fmt::Debug { }
448impl <S> Aabb <S> for Sphere <S> where S : Real + std::fmt::Debug {
449 fn aabb (&self) -> Aabb3 <S> {
450 Aabb3::with_minmax ([-*self.radius; 3].into(), [ *self.radius; 3].into())
451 }
452}
453impl <S : Real> Stereometric <S> for Sphere <S> {
454 fn volume (&self) -> Positive <S> {
455 let four = Positive::unchecked (S::four());
456 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
457 four * frac_pi_3 * self.radius * self.radius * self.radius
458 }
459}
460
461impl <S : Ring> Capsule <S> {
465 #[inline]
482 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
483 assert_ne!(radius, S::zero());
484 assert_ne!(half_height, S::zero());
485 let radius = Positive::unchecked (radius.abs());
486 let half_height = Positive::unchecked (half_height.abs());
487 Capsule { radius, half_height }
488 }
489 #[inline]
506 pub fn unchecked (radius : S, half_height : S) -> Self where
507 S : std::fmt::Debug
508 {
509 debug_assert_ne!(radius, S::zero());
510 let radius = Positive::unchecked (radius.abs());
511 let half_height = Positive::unchecked (half_height.abs());
512 Capsule { radius, half_height }
513 }
514 #[inline]
516 pub fn height (&self) -> NonNegative <S> where S : Field {
517 self.half_height * NonNegative::unchecked (S::two())
518 }
519 #[inline]
520 pub fn capsule3 (&self, center : Point3 <S>) -> Capsule3 <S> {
521 Capsule3 { center, radius: self.radius, half_height: self.half_height }
522 }
523}
524impl <S> Shape <S> for Capsule <S> where S : Real + std::fmt::Debug { }
525impl <S> Aabb <S> for Capsule <S> where S : Real + std::fmt::Debug {
526 fn aabb (&self) -> Aabb3 <S> {
527 let r = *self.radius;
528 let hh = *self.half_height;
529 Aabb3::with_minmax ([-r, -r, -r - hh].into(), [ r, r, r + hh].into())
530 }
531}
532impl <S : Real> Stereometric <S> for Capsule <S> {
533 fn volume (&self) -> Positive <S> {
534 let r = self.radius;
535 let h = self.height();
536 let r2 = r * r;
537 let r3 = r2 * r;
538 let pi = Positive::unchecked (S::pi());
539 let four = Positive::unchecked (S::four());
540 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
541 let cylinder_volume = pi * r2 * h;
542 let sphere_volume = four * frac_pi_3 * r3;
543 sphere_volume + cylinder_volume
544 }
545}
546
547impl <S : Ring> Cylinder <S> {
551 #[inline]
552 pub fn unit() -> Self where S : Field {
554 use num::One;
555 Cylinder {
556 radius: Positive::one(), half_height: Positive::one()
557 }
558 }
559 #[inline]
569 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
570 assert_ne!(radius, S::zero());
571 assert_ne!(half_height, S::zero());
572 let radius = Positive::unchecked (radius.abs());
573 let half_height = Positive::unchecked (half_height.abs());
574 Cylinder { radius, half_height }
575 }
576 #[inline]
586 pub fn unchecked (radius : S, half_height : S) -> Self where
587 S : std::fmt::Debug
588 {
589 debug_assert_ne!(radius, S::zero());
590 debug_assert_ne!(half_height, S::zero());
591 let radius = Positive::unchecked (radius.abs());
592 let half_height = Positive::unchecked (half_height.abs());
593 Cylinder { radius, half_height }
594 }
595 #[inline]
596 pub fn cylinder3 (&self, center : Point3 <S>) -> Cylinder3 <S> {
597 Cylinder3 { center, radius: self.radius, half_height: self.half_height }
598 }
599 #[inline]
600 pub fn height (&self) -> Positive <S> where S : Field {
601 self.half_height * Positive::unchecked (S::two())
602 }
603}
604impl <S> Shape <S> for Cylinder <S> where S : Real + std::fmt::Debug { }
605impl <S> Aabb <S> for Cylinder <S> where S : Real + std::fmt::Debug {
606 fn aabb (&self) -> Aabb3 <S> {
607 let r = *self.radius;
608 let hh = *self.half_height;
609 Aabb3::with_minmax ([-r, -r, -hh].into(), [ r, r, hh].into())
610 }
611}
612impl <S : Real> Stereometric <S> for Cylinder <S> {
613 fn volume (&self) -> Positive <S> {
614 let pi = Positive::unchecked (S::pi());
615 pi * self.radius * self.radius * self.height()
616 }
617}
618
619impl <S : Ring> Cone <S> {
623 #[inline]
633 pub fn noisy (radius : S, half_height : S) -> Self where S : std::fmt::Debug {
634 assert_ne!(radius, S::zero());
635 assert_ne!(half_height, S::zero());
636 let radius = Positive::unchecked (radius.abs());
637 let half_height = Positive::unchecked (half_height.abs());
638 Cone { radius, half_height }
639 }
640}
641impl <S> Shape <S> for Cone <S> where S : Real + std::fmt::Debug { }
642impl <S> Aabb <S> for Cone <S> where S : Real + std::fmt::Debug {
643 fn aabb (&self) -> Aabb3 <S> {
644 let r = *self.radius;
645 let hh = *self.half_height;
646 Aabb3::with_minmax ([-r, -r, -hh].into(), [ r, r, hh].into())
647 }
648}
649impl <S : Real> Stereometric <S> for Cone <S> {
650 fn volume (&self) -> Positive <S> {
651 let frac_pi_3 = Positive::unchecked (S::frac_pi_3());
652 let two = Positive::unchecked (S::two());
653 frac_pi_3 * self.radius * self.radius * two * self.half_height
654 }
655}
656
657impl <S : Ring> Cube <S> {
661 #[inline]
670 pub fn noisy (half_extent : S) -> Self where S : std::fmt::Debug {
671 assert_ne!(half_extent, S::zero());
672 let half_extent = Positive::unchecked (half_extent.abs());
673 Cube { half_extent }
674 }
675 #[inline]
676 pub fn extent (&self) -> Positive <S> where S : Field {
677 self.half_extent * Positive::unchecked (S::two())
678 }
679}
680impl <S> Shape <S> for Cube <S> where S : Field + std::fmt::Debug { }
681impl <S> Aabb <S> for Cube <S> where S : Field + std::fmt::Debug {
682 fn aabb (&self) -> Aabb3 <S> {
683 Aabb3::with_minmax (
684 [-*self.half_extent; 3].into(),
685 [ *self.half_extent; 3].into())
686 }
687}
688impl <S : Field> Stereometric <S> for Cube <S> {
689 fn volume (&self) -> Positive <S> {
690 let extent = self.extent();
691 extent * extent * extent
692 }
693}
694
695impl <S : Ring> Cuboid <S> {
699 #[inline]
708 pub fn noisy (half_extents : [S; 3]) -> Self where S : std::fmt::Debug {
709 assert_ne!(half_extents[0], S::zero());
710 assert_ne!(half_extents[1], S::zero());
711 assert_ne!(half_extents[2], S::zero());
712 let half_extent_x = Positive::unchecked (half_extents[0].abs());
713 let half_extent_y = Positive::unchecked (half_extents[1].abs());
714 let half_extent_z = Positive::unchecked (half_extents[2].abs());
715 Cuboid { half_extent_x, half_extent_y, half_extent_z }
716 }
717 #[inline]
718 pub fn extents (&self) -> [Positive <S>; 3] where S : Field {
719 let two = Positive::unchecked (S::two());
720 [
721 self.half_extent_x * two,
722 self.half_extent_y * two,
723 self.half_extent_z * two
724 ]
725 }
726 #[inline]
727 pub fn half_extents_vec (&self) -> Vector3 <S> {
728 [ *self.half_extent_x,
729 *self.half_extent_y,
730 *self.half_extent_z
731 ].into()
732 }
733 #[inline]
734 pub fn max (&self) -> Point3 <S> {
735 Vector3 {
736 x: *self.half_extent_x,
737 y: *self.half_extent_y,
738 z: *self.half_extent_z
739 }.into()
740 }
741 #[inline]
742 pub fn min (&self) -> Point3 <S> {
743 Vector3 {
744 x: -*self.half_extent_x,
745 y: -*self.half_extent_y,
746 z: -*self.half_extent_z
747 }.into()
748 }
749 #[inline]
750 pub fn aabb3 (&self, center : Point3 <S>) -> Aabb3 <S> where
751 S : std::fmt::Debug
752 {
753 Aabb3::with_minmax (
754 center + self.min().0,
755 center + self.max().0
756 )
757 }
758}
759impl <S> From <Aabb3 <S>> for Cuboid <S> where S : Field {
760 fn from (aabb : Aabb3 <S>) -> Self {
761 Cuboid {
763 half_extent_x: Positive::unchecked (*aabb.width() / S::two()),
764 half_extent_y: Positive::unchecked (*aabb.depth() / S::two()),
765 half_extent_z: Positive::unchecked (*aabb.height() / S::two())
766 }
767 }
768}
769impl <S> Shape <S> for Cuboid <S> where S : Field + std::fmt::Debug { }
770impl <S> Aabb <S> for Cuboid <S> where S : Field + std::fmt::Debug {
771 fn aabb (&self) -> Aabb3 <S> {
772 Aabb3::with_minmax (self.min(), self.max())
773 }
774}
775impl <S : Field> Stereometric <S> for Cuboid <S> {
776 fn volume (&self) -> Positive <S> {
777 let [x, y, z] = self.extents();
778 x * y * z
779 }
780}
781
782impl Orthant {
786 pub fn try_from <S : Real> (halfspace : &Halfspace <S>) -> Option <Self> {
787 SignedAxis3::try_from (&halfspace.normal_vector)
788 .map (|normal_axis| Orthant { normal_axis })
789 }
790}
791impl From <SignedAxis3> for Orthant {
792 fn from (normal_axis : SignedAxis3) -> Self {
793 Orthant { normal_axis }
794 }
795}
796impl <S> Shape <S> for Orthant where
797 S : Field + num::float::FloatCore + std::fmt::Debug
798{ }
799impl <S> Aabb <S> for Orthant where
800 S : Field + num::float::FloatCore + std::fmt::Debug
801{
802 fn aabb (&self) -> Aabb3 <S> {
821 use num::float::FloatCore;
822 let axis_vec = self.normal_axis.to_vec::<S>();
823 let surface = Vector3::broadcast (S::one()) - axis_vec.map (FloatCore::abs);
824
825 let do_min = |i| if surface[i] == S::one() || axis_vec[i] == S::one() {
826 S::neg_infinity()
827 } else {
828 debug_assert_eq!(axis_vec[i], -S::one());
829 S::zero()
830 };
831 let do_max = |i| if surface[i] == S::one() {
832 S::infinity()
833 } else if axis_vec[i] == S::one() {
834 S::zero()
835 } else {
836 debug_assert_eq!(axis_vec[i], -S::one());
837 S::infinity()
838 };
839 let min = [ do_min (0), do_min (1), do_min (2) ].into();
840 let max = [ do_max (0), do_max (1), do_max (2) ].into();
841
842 Aabb3::with_minmax (min, max)
843 }
844}
845impl <S> Stereometric <S> for Orthant where S : Ring + num::float::FloatCore {
846 fn volume (&self) -> Positive <S> {
848 Positive::infinity()
849 }
850}
851
852impl <S : Real> From <Unit3 <S>> for Halfspace <S> {
856 fn from (normal_vector : Unit3 <S>) -> Self {
857 Halfspace { normal_vector }
858 }
859}
860impl <S> Shape <S> for Halfspace <S> where
861 S : Real + num::float::FloatCore + std::fmt::Debug
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 Some (orthant) = Orthant::try_from (self) {
868 orthant.aabb()
869 } else {
870 Aabb3::with_minmax (
871 [S::neg_infinity(); 3].into(),
872 [S::infinity(); 3].into())
873 }
874 }
875}
876impl <S> Stereometric <S> for Halfspace <S> where
877 S : Real + num::float::FloatCore
878{
879 fn volume (&self) -> Positive <S> {
881 Positive::infinity()
882 }
883}