1use std::f64::consts::{PI, TAU};
4
5use ranim_core::{
6 Extract,
7 anchor::Aabb,
8 color::{self, AlphaColor, Srgb},
9 core_item::CoreItem,
10 glam::{DMat4, DVec3},
11 traits::{FillColor, Interpolatable, Opacity, ShiftTransform, With},
12};
13
14use crate::mesh::MeshItem;
15
16use super::Surface;
17
18#[derive(Debug, Clone, PartialEq)]
26pub struct Sphere {
27 pub center: DVec3,
29 pub radius: f64,
31 pub resolution: (u32, u32),
33 pub fill_rgba: AlphaColor<Srgb>,
35}
36
37impl Sphere {
38 pub fn new(radius: f64) -> Self {
40 Self {
41 center: DVec3::ZERO,
42 radius,
43 resolution: (101, 51),
44 fill_rgba: color::palette::css::BLUE.with_alpha(1.0),
45 }
46 }
47
48 pub fn unit() -> Self {
50 Self::new(1.0)
51 }
52
53 pub fn with_center(mut self, center: DVec3) -> Self {
55 self.center = center;
56 self
57 }
58
59 pub fn with_resolution(mut self, resolution: (u32, u32)) -> Self {
61 self.resolution = resolution;
62 self
63 }
64
65 pub fn with_fill_color(mut self, color: AlphaColor<Srgb>) -> Self {
67 self.fill_rgba = color;
68 self
69 }
70
71 pub fn points_uv_func(u: f64, v: f64, r: f64) -> DVec3 {
73 Self::normals_uv_func(u, v) * r
74 }
75
76 pub fn normals_uv_func(u: f64, v: f64) -> DVec3 {
78 let x = u.cos() * v.sin();
79 let y = u.sin() * v.sin();
80 let z = -v.cos();
81 DVec3::new(x, y, z)
82 }
83}
84
85impl From<Sphere> for MeshItem {
86 fn from(value: Sphere) -> Self {
87 Surface::from(value).into()
88 }
89}
90
91impl From<Sphere> for Surface {
92 fn from(value: Sphere) -> Self {
93 Surface::from_uv_func(
94 |u, v| Sphere::points_uv_func(u, v, value.radius),
95 (0.0, TAU),
96 (0.0, PI),
97 value.resolution,
98 )
99 .with_transform(DMat4::from_translation(value.center))
100 .with(|x| {
101 x.set_fill_color(value.fill_rgba);
102 })
103 }
104}
105
106impl Interpolatable for Sphere {
107 fn lerp(&self, target: &Self, t: f64) -> Self {
108 Self {
109 center: Interpolatable::lerp(&self.center, &target.center, t),
110 radius: Interpolatable::lerp(&self.radius, &target.radius, t),
111 resolution: if t < 0.5 {
112 self.resolution
113 } else {
114 target.resolution
115 },
116 fill_rgba: Interpolatable::lerp(&self.fill_rgba, &target.fill_rgba, t),
117 }
118 }
119}
120
121impl FillColor for Sphere {
122 fn fill_color(&self) -> AlphaColor<Srgb> {
123 self.fill_rgba
124 }
125 fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
126 self.fill_rgba = color;
127 self
128 }
129 fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
130 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
131 self
132 }
133}
134
135impl Opacity for Sphere {
136 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
137 self.fill_rgba = self.fill_rgba.with_alpha(opacity);
138 self
139 }
140}
141
142impl ShiftTransform for Sphere {
143 fn shift(&mut self, offset: DVec3) -> &mut Self {
144 self.center += offset;
145 self
146 }
147}
148
149impl Aabb for Sphere {
150 fn aabb(&self) -> [DVec3; 2] {
151 let r = DVec3::splat(self.radius);
152 [self.center - r, self.center + r]
153 }
154}
155
156impl Extract for Sphere {
157 type Target = CoreItem;
158 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
159 Surface::from(self.clone()).extract_into(buf);
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use ranim_core::glam::dvec3;
167
168 #[test]
169 fn test_sphere_center_to_transform() {
170 let sphere = Sphere::new(1.0).with_center(dvec3(1.0, 2.0, 3.0));
171 let surface = Surface::from(sphere);
172 assert_eq!(
173 surface.transform,
174 DMat4::from_translation(dvec3(1.0, 2.0, 3.0))
175 );
176 }
177
178 #[test]
179 fn test_sphere_aabb() {
180 let sphere = Sphere::new(1.0).with_center(dvec3(1.0, 2.0, 3.0));
181 let [min, max] = sphere.aabb();
182 assert_eq!(min, dvec3(0.0, 1.0, 2.0));
183 assert_eq!(max, dvec3(2.0, 3.0, 4.0));
184 }
185
186 #[test]
187 fn test_sphere_shift() {
188 let mut sphere = Sphere::new(1.0);
189 sphere.shift(dvec3(1.0, 0.0, 0.0));
190 assert_eq!(sphere.center, dvec3(1.0, 0.0, 0.0));
191 }
192
193 #[test]
194 fn test_sphere_interpolation() {
195 let a = Sphere::new(1.0).with_center(dvec3(0.0, 0.0, 0.0));
196 let b = Sphere::new(3.0).with_center(dvec3(2.0, 0.0, 0.0));
197 let mid = a.lerp(&b, 0.5);
198 assert!((mid.radius - 2.0).abs() < 1e-10);
199 assert!((mid.center.x - 1.0).abs() < 1e-10);
200 }
201
202 #[test]
203 fn test_sphere_to_surface() {
204 let sphere = Sphere::new(1.0)
205 .with_center(dvec3(1.0, 0.0, 0.0))
206 .with_resolution((5, 5));
207 let surface = Surface::from(sphere);
208 assert_eq!(surface.vertices.len(), 25);
209 assert_eq!(surface.resolution, (5, 5));
210 assert_eq!(
211 surface.transform,
212 DMat4::from_translation(dvec3(1.0, 0.0, 0.0))
213 );
214 }
215}