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