ranim_items/vitem/geometry/
circle.rs

1use std::f64::consts::PI;
2
3use color::{AlphaColor, Srgb};
4use glam::DVec3;
5use ranim_core::{
6    Extract,
7    anchor::{Aabb, Locate},
8    color,
9    core_item::CoreItem,
10    glam,
11    traits::{RotateTransform, ScaleTransform, ShiftTransform},
12};
13
14use crate::vitem::DEFAULT_STROKE_WIDTH;
15use ranim_core::anchor::AabbPoint;
16use ranim_core::traits::{FillColor, Opacity, StrokeColor, With};
17
18use crate::vitem::VItem;
19
20use super::Arc;
21
22// MARK: ### Circle ###
23/// An circle
24#[derive(Clone, Debug, ranim_macros::Interpolatable)]
25pub struct Circle {
26    /// Axes
27    pub axes: (DVec3, DVec3),
28    /// Center
29    pub center: DVec3,
30    /// Radius
31    pub radius: f64,
32
33    /// Stroke rgba
34    pub stroke_rgba: AlphaColor<Srgb>,
35    /// Stroke width
36    pub stroke_width: f32,
37    /// Fill rgba
38    pub fill_rgba: AlphaColor<Srgb>,
39}
40
41impl Circle {
42    /// Constructor
43    pub fn new(radius: f64) -> Self {
44        Self {
45            axes: (DVec3::X, DVec3::Y),
46            center: DVec3::ZERO,
47            radius,
48
49            stroke_rgba: AlphaColor::WHITE,
50            stroke_width: DEFAULT_STROKE_WIDTH,
51            fill_rgba: AlphaColor::TRANSPARENT,
52        }
53    }
54    /// Scale the circle by the given scale, with the given anchor as the center.
55    ///
56    /// Note that this accepts a `f64` scale dispite of [`ranim_core::traits::ScaleTransform`]'s `DVec3`,
57    /// because this keeps the circle a circle.
58    pub fn scale(&mut self, scale: f64) -> &mut Self {
59        self.scale_by_anchor(scale, AabbPoint::CENTER)
60    }
61    /// Scale the circle by the given scale, with the given anchor as the center.
62    ///
63    /// Note that this accepts a `f64` scale dispite of [`ranim_core::traits::ScaleTransform`]'s `DVec3`,
64    /// because this keeps the circle a circle.
65    pub fn scale_by_anchor<T>(&mut self, scale: f64, anchor: T) -> &mut Self
66    where
67        T: Locate<Self>,
68    {
69        let point = anchor.locate(self);
70        self.radius *= scale;
71        self.center
72            .shift(-point)
73            .scale(DVec3::splat(scale))
74            .shift(point);
75        self
76    }
77}
78
79// MARK: Traits impl
80impl Aabb for Circle {
81    fn aabb(&self) -> [DVec3; 2] {
82        let (u, v) = (self.axes.0.normalize(), self.axes.1.normalize());
83        let r = self.radius * (u + v);
84        [self.center + r, self.center - r].aabb()
85    }
86}
87
88impl ShiftTransform for Circle {
89    fn shift(&mut self, shift: DVec3) -> &mut Self {
90        self.center.shift(shift);
91        self
92    }
93}
94
95impl RotateTransform for Circle {
96    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
97        self.center.rotate_on_axis(axis, angle);
98        self.axes.0.rotate_on_axis(axis, angle);
99        self.axes.0 = self.axes.0.normalize();
100        self.axes.1.rotate_on_axis(axis, angle);
101        self.axes.1 = self.axes.1.normalize();
102        self
103    }
104}
105
106impl Opacity for Circle {
107    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
108        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
109        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
110        self
111    }
112}
113
114impl StrokeColor for Circle {
115    fn stroke_color(&self) -> AlphaColor<Srgb> {
116        self.stroke_rgba
117    }
118    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
119        self.stroke_rgba = color;
120        self
121    }
122    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
123        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
124        self
125    }
126}
127
128impl FillColor for Circle {
129    fn fill_color(&self) -> AlphaColor<Srgb> {
130        self.fill_rgba
131    }
132    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
133        self.fill_rgba = color;
134        self
135    }
136    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
137        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
138        self
139    }
140}
141
142// MARK: Conversions
143impl From<Circle> for Arc {
144    fn from(value: Circle) -> Self {
145        let Circle {
146            axes,
147            center,
148            radius,
149            stroke_rgba,
150            stroke_width,
151            ..
152        } = value;
153        Self {
154            axes,
155            center,
156            radius,
157            angle: 2.0 * PI,
158            stroke_rgba,
159            stroke_width,
160        }
161    }
162}
163
164impl From<Circle> for VItem {
165    fn from(value: Circle) -> Self {
166        let fill_rgba = value.fill_rgba;
167        VItem::from(Arc::from(value)).with(|item| {
168            item.set_fill_color(fill_rgba);
169        })
170    }
171}
172
173impl Extract for Circle {
174    type Target = CoreItem;
175    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
176        VItem::from(self.clone()).extract_into(buf);
177    }
178}