ranim_items/vitem/geometry/
arc.rs1use color::{AlphaColor, Srgb};
2use glam::DVec3;
3use ranim_core::Extract;
4use ranim_core::anchor::{Aabb, Locate};
5use ranim_core::core_item::CoreItem;
6use ranim_core::core_item::vitem::Basis2d;
7use ranim_core::{color, glam};
8
9use ranim_core::traits::{
10 Opacity, RotateTransform, ScaleTransform, ShiftTransform, StrokeColor, With,
11};
12
13use crate::vitem::geometry::EllipticArc;
14use crate::vitem::{DEFAULT_STROKE_WIDTH, VItem};
15use ranim_core::anchor::AabbPoint;
16
17#[derive(Clone, Debug, ranim_macros::Interpolatable)]
20pub struct Arc {
21 pub basis: Basis2d,
23 pub center: DVec3,
25 pub radius: f64,
27 pub angle: f64,
29
30 pub stroke_rgba: AlphaColor<Srgb>,
32 pub stroke_width: f32,
34}
35
36impl Arc {
37 pub fn new(angle: f64, radius: f64) -> Self {
39 Self {
40 basis: Basis2d::default(),
41 center: DVec3::ZERO,
42 radius,
43 angle,
44 stroke_rgba: AlphaColor::WHITE,
45 stroke_width: DEFAULT_STROKE_WIDTH,
46 }
47 }
48 pub fn scale(&mut self, scale: f64) -> &mut Self {
53 self.scale_by_anchor(scale, AabbPoint::CENTER)
54 }
55 pub fn scale_by_anchor<T>(&mut self, scale: f64, anchor: T) -> &mut Self
60 where
61 T: Locate<Self>,
62 {
63 let anchor = anchor.locate(self);
64 self.radius *= scale;
65 self.center
66 .shift(-anchor)
67 .scale(DVec3::splat(scale))
68 .shift(anchor);
69 self
70 }
71 pub fn start(&self) -> DVec3 {
73 self.center + self.radius * self.basis.u()
74 }
75 pub fn end(&self) -> DVec3 {
77 let u = self.angle.cos() * self.basis.u();
78 let v = self.angle.sin() * self.basis.v();
79 self.center + self.radius * (u + v)
80 }
81}
82
83impl Aabb for Arc {
85 fn aabb(&self) -> [DVec3; 2] {
87 VItem::from(self.clone()).aabb()
88 }
89}
90
91impl ShiftTransform for Arc {
92 fn shift(&mut self, shift: DVec3) -> &mut Self {
93 self.center.shift(shift);
94 self
95 }
96}
97
98impl RotateTransform for Arc {
99 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
100 self.center.rotate_on_axis(axis, angle);
101 self.basis.rotate_on_axis(axis, angle);
102 self
103 }
104}
105
106impl Opacity for Arc {
107 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
108 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
109 self
110 }
111}
112
113impl StrokeColor for Arc {
114 fn stroke_color(&self) -> AlphaColor<Srgb> {
115 self.stroke_rgba
116 }
117 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
118 self.stroke_rgba = color;
119 self
120 }
121 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
122 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
123 self
124 }
125}
126
127impl From<Arc> for VItem {
129 fn from(value: Arc) -> Self {
130 EllipticArc::from(value).into()
131 }
132}
133
134impl Extract for Arc {
135 type Target = CoreItem;
136 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
137 VItem::from(self.clone()).extract_into(buf);
138 }
139}
140
141#[derive(Clone, Debug, ranim_macros::Interpolatable)]
144pub struct ArcBetweenPoints {
145 pub basis: Basis2d,
147 pub start: DVec3,
149 pub end: DVec3,
151 pub angle: f64,
153
154 pub stroke_rgba: AlphaColor<Srgb>,
156 pub stroke_width: f32,
158}
159
160impl ArcBetweenPoints {
161 pub fn new(start: DVec3, end: DVec3, angle: f64) -> Self {
163 Self {
164 basis: Basis2d::default(),
165 start,
166 end,
167 angle,
168
169 stroke_rgba: AlphaColor::WHITE,
170 stroke_width: DEFAULT_STROKE_WIDTH,
171 }
172 }
173 pub fn scale(&mut self, scale: f64) -> &mut Self {
178 self.scale_at(scale, AabbPoint::CENTER)
179 }
180 pub fn scale_at<T>(&mut self, scale: f64, anchor: T) -> &mut Self
185 where
186 T: Locate<Self>,
187 {
188 let point = anchor.locate(self);
189 self.start
190 .shift(-point)
191 .scale(DVec3::splat(scale))
192 .shift(point);
193 self.end
194 .shift(-point)
195 .scale(DVec3::splat(scale))
196 .shift(point);
197 self
198 }
199}
200
201impl Aabb for ArcBetweenPoints {
203 fn aabb(&self) -> [DVec3; 2] {
205 Arc::from(self.clone()).aabb()
207 }
208}
209
210impl ShiftTransform for ArcBetweenPoints {
211 fn shift(&mut self, shift: DVec3) -> &mut Self {
212 self.start.shift(shift);
213 self.end.shift(shift);
214 self
215 }
216}
217
218impl RotateTransform for ArcBetweenPoints {
219 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
220 self.start.rotate_on_axis(axis, angle);
221 self.end.rotate_on_axis(axis, angle);
222 self.basis.rotate_on_axis(axis, angle);
223 self
224 }
225}
226
227impl Opacity for ArcBetweenPoints {
228 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
229 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
230 self
231 }
232}
233
234impl StrokeColor for ArcBetweenPoints {
235 fn stroke_color(&self) -> AlphaColor<Srgb> {
236 self.stroke_rgba
237 }
238 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
239 self.stroke_rgba = color;
240 self
241 }
242 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
243 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
244 self
245 }
246}
247
248impl From<ArcBetweenPoints> for Arc {
250 fn from(value: ArcBetweenPoints) -> Arc {
251 let ArcBetweenPoints {
252 basis: proj,
253 start,
254 end,
255 angle,
256 stroke_rgba,
257 stroke_width,
258 } = value;
259 let radius = (start.distance(end) / 2.0) / (angle / 2.0).sin();
260
261 Arc {
262 basis: proj,
263 angle,
264 radius,
265 center: DVec3::ZERO,
266 stroke_rgba,
267 stroke_width,
268 }
269 .with(|arc| {
270 let cur_start = arc.start();
271
272 let v1 = arc.end() - arc.start();
273 let v2 = end - start;
274
275 let rot_angle = v1.angle_between(v2);
276 let mut rot_axis = v1.cross(v2);
277 if rot_axis.length_squared() <= f64::EPSILON {
278 rot_axis = DVec3::NEG_Z;
279 }
280 rot_axis = rot_axis.normalize();
281 arc.shift(start - cur_start);
282 arc.shift(-start);
283 arc.rotate_on_axis(rot_axis, rot_angle);
284 arc.shift(start);
285 })
286 }
287}
288
289impl From<ArcBetweenPoints> for VItem {
290 fn from(value: ArcBetweenPoints) -> Self {
291 Arc::from(value).into()
292 }
293}
294
295impl Extract for ArcBetweenPoints {
296 type Target = CoreItem;
297 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
298 Arc::from(self.clone()).extract_into(buf);
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use std::f64::consts::PI;
305
306 use assert_float_eq::assert_float_absolute_eq;
307 use glam::dvec3;
308 use ranim_core::traits::ShiftTransformExt;
309
310 use crate::vitem::geometry::anchor::Origin;
311
312 use super::*;
313
314 #[test]
315 fn test_arc() {
316 let arc = Arc::new(PI / 2.0, 2.0);
317 assert_float_absolute_eq!(
318 arc.start().distance_squared(dvec3(2.0, 0.0, 0.0)),
319 0.0,
320 1e-10
321 );
322 assert_float_absolute_eq!(arc.end().distance_squared(dvec3(0.0, 2.0, 0.0)), 0.0, 1e-10);
323
324 let arc_between_points =
325 ArcBetweenPoints::new(dvec3(2.0, 0.0, 0.0), dvec3(0.0, 2.0, 0.0), PI / 2.0);
326 let arc_between_points = Arc::from(arc_between_points);
327 assert_float_absolute_eq!(
328 arc.center.distance_squared(arc_between_points.center),
329 0.0,
330 1e-10
331 );
332 assert_float_absolute_eq!(arc.radius - arc_between_points.radius, 0.0, 1e-10);
333 assert_float_absolute_eq!(arc.angle - arc_between_points.angle, 0.0, 1e-10);
334
335 let arc_between_points =
336 ArcBetweenPoints::new(dvec3(0.0, 2.0, 0.0), dvec3(2.0, 0.0, 0.0), PI / 2.0);
337 let arc_between_points = Arc::from(arc_between_points);
338 let arc = Arc::new(PI / 2.0, 2.0).with(|arc| {
339 arc.with_origin(Origin, |x| {
340 x.rotate_on_axis(DVec3::NEG_Z, PI);
341 })
342 .shift(dvec3(2.0, 2.0, 0.0));
343 });
344 assert_float_absolute_eq!(
345 arc.center.distance_squared(arc_between_points.center),
346 0.0,
347 1e-10
348 );
349 assert_float_absolute_eq!(arc.radius - arc_between_points.radius, 0.0, 1e-10);
350 assert_float_absolute_eq!(arc.angle - arc_between_points.angle, 0.0, 1e-10);
351 }
352}