pix_engine/shape/
sphere.rs

1//! A shape type representing spheres used for drawing.
2//!
3//! # Examples
4//!
5//! You can create a [Sphere] using [`Sphere::new`]:
6//!
7//! ```
8//! # use pix_engine::prelude::*;
9//! let s = Sphere::new(10, 20, 100, 200);
10//! ```
11//!
12//! ...or by using the [sphere!] macro:
13//!
14//! ```
15//! # use pix_engine::prelude::*;
16//! let s = sphere!(10, 20, 15, 200);
17//!
18//! // using a point
19//! let s = sphere!([10, 20, 15], 200);
20//! let s = sphere!(point![10, 20, 15], 200);
21//! ```
22
23use crate::prelude::*;
24#[cfg(feature = "serde")]
25use serde::{Deserialize, Serialize};
26
27/// A `Sphere` positioned at `(x, y, z)` with `radius`.
28///
29/// Please see the [module-level documentation] for examples.
30///
31/// [module-level documentation]: crate::shape::sphere
32#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
33#[repr(transparent)]
34#[must_use]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub struct Sphere<T = i32>(pub(crate) [T; 4]);
37
38/// Constructs a [Sphere] at position `(x, y, z)` with `radius`.
39///
40/// ```
41/// # use pix_engine::prelude::*;
42/// let p = point!(10, 20, 10);
43/// let s = sphere!(p, 100);
44/// assert_eq!(s.x(), 10);
45/// assert_eq!(s.y(), 20);
46/// assert_eq!(s.z(), 10);
47/// assert_eq!(s.radius(), 100);
48///
49/// let s = sphere!(10, 20, 10, 100);
50/// assert_eq!(s.x(), 10);
51/// assert_eq!(s.y(), 20);
52/// assert_eq!(s.z(), 10);
53/// assert_eq!(s.radius(), 100);
54/// ```
55#[macro_export]
56macro_rules! sphere {
57    ($p:expr, $r:expr$(,)?) => {
58        $crate::prelude::Sphere::with_position($p, $r)
59    };
60    ($x:expr, $y:expr, $z:expr, $r:expr$(,)?) => {
61        $crate::prelude::Sphere::new($x, $y, $z, $r)
62    };
63}
64
65impl<T> Sphere<T> {
66    /// Constructs a `Sphere` at position `(x, y, z)` with `radius`.
67    pub const fn new(x: T, y: T, z: T, radius: T) -> Self {
68        Self([x, y, z, radius])
69    }
70}
71
72impl<T: Copy> Sphere<T> {
73    /// Returns `Sphere` coordinates  as `[x, y, z, radius]`.
74    #[inline]
75    pub fn coords(&self) -> [T; 4] {
76        self.0
77    }
78
79    /// Returns `Sphere` coordinates as a mutable slice `&mut [x, y, z, radius]`.
80    #[inline]
81    pub fn coords_mut(&mut self) -> &mut [T; 4] {
82        &mut self.0
83    }
84}
85
86impl<T: Num> Sphere<T> {
87    /// Constructs a `Sphere` at position [Point] with `radius`.
88    pub fn with_position<P: Into<Point<T, 3>>>(p: P, radius: T) -> Self {
89        let p = p.into();
90        Self::new(p.x(), p.y(), p.z(), radius)
91    }
92
93    /// Returns the `x-coordinate` of the sphere.
94    #[inline]
95    pub fn x(&self) -> T {
96        self.0[0]
97    }
98
99    /// Sets the `x-coordinate` of the sphere.
100    #[inline]
101    pub fn set_x(&mut self, x: T) {
102        self.0[0] = x;
103    }
104
105    /// Returns the `y-coordinate` of the sphere.
106    #[inline]
107    pub fn y(&self) -> T {
108        self.0[1]
109    }
110
111    /// Sets the `y-coordinate` of the sphere.
112    #[inline]
113    pub fn set_y(&mut self, y: T) {
114        self.0[1] = y;
115    }
116
117    /// Returns the `z-coordinate` of the sphere.
118    #[inline]
119    pub fn z(&self) -> T {
120        self.0[2]
121    }
122
123    /// Sets the `z-coordinate` of the sphere.
124    #[inline]
125    pub fn set_z(&mut self, z: T) {
126        self.0[2] = z;
127    }
128
129    /// Returns the `radius` of the sphere.
130    #[inline]
131    pub fn radius(&self) -> T {
132        self.0[3]
133    }
134
135    /// Sets the `radius` of the sphere.
136    #[inline]
137    pub fn set_radius(&mut self, radius: T) {
138        self.0[3] = radius;
139    }
140
141    /// Returns the center [Point].
142    pub fn center(&self) -> Point<T, 3> {
143        point!(self.x(), self.y(), self.z())
144    }
145}
146
147impl<T: Num> Contains<Point<T>> for Sphere<T> {
148    /// Returns whether this sphere contains a given [Point].
149    fn contains(&self, p: Point<T>) -> bool {
150        let px = p.x() - self.x();
151        let py = p.y() - self.y();
152        let pz = p.z() - self.z();
153        let r = self.radius() * self.radius();
154        (px * px + py * py + pz * pz) < r
155    }
156}
157
158impl<T: Num> Contains<Sphere<T>> for Sphere<T> {
159    /// Returns whether this sphere completely contains another sphere.
160    fn contains(&self, sphere: Sphere<T>) -> bool {
161        let px = sphere.x() - self.x();
162        let py = sphere.y() - self.y();
163        let pz = sphere.z() - self.z();
164        let r = self.radius() * self.radius();
165        (px * px + py * py + pz * pz) < r
166    }
167}
168
169impl<T: Num> Intersects<Sphere<T>> for Sphere<T> {
170    // FIXME: Provide a better intersection result
171    type Result = ();
172
173    /// Returns whether this sphere intersects another sphere.
174    fn intersects(&self, sphere: Sphere<T>) -> Option<Self::Result> {
175        let px = sphere.x() - self.x();
176        let py = sphere.y() - self.y();
177        let pz = sphere.z() - self.z();
178        let r = sphere.radius() + self.radius();
179        if (px * px + py * py + pz * pz) < r * r {
180            Some(())
181        } else {
182            None
183        }
184    }
185}