ranim_items/vitem/geometry/
elliptic_arc.rs1use std::f64::consts::TAU;
2
3use ranim_core::{
4 Extract,
5 color::{AlphaColor, Srgb},
6 components::vpoint::VPointVec,
7 core_item::{
8 CoreItem,
9 vitem::{Basis2d, DEFAULT_STROKE_WIDTH},
10 },
11 glam::{DVec2, DVec3},
12 traits::{
13 Aabb, Discard, Opacity, RotateTransform, ShiftTransform, StrokeColor, StrokeWidth, With,
14 },
15};
16
17use crate::vitem::{
18 VItem,
19 geometry::{Arc, Circle, Ellipse},
20};
21
22#[derive(Debug, Clone, ranim_macros::Interpolatable)]
24pub struct EllipticArc {
25 pub basis: Basis2d,
27 pub center: DVec3,
29 pub radius: DVec2,
31 pub start_angle: f64,
33 pub angle: f64,
35 pub stroke_rgba: AlphaColor<Srgb>,
37 pub stroke_width: f32,
39}
40
41impl EllipticArc {
42 pub fn new(start_angle: f64, angle: f64, radius: DVec2) -> Self {
44 EllipticArc {
45 basis: Basis2d::default(),
46 center: DVec3::ZERO,
47 radius,
48 start_angle,
49 angle,
50 stroke_rgba: AlphaColor::WHITE,
51 stroke_width: DEFAULT_STROKE_WIDTH,
52 }
53 }
54
55 fn generate_vpoints(&self) -> Vec<DVec3> {
56 const NUM_SEGMENTS: usize = 8;
57 let len = 2 * NUM_SEGMENTS + 1;
58
59 let &EllipticArc {
60 basis,
61 center,
62 radius,
63 start_angle,
64 angle,
65 ..
66 } = self;
67
68 let (u, v) = basis.uv();
69 let DVec2 { x: rx, y: ry } = radius;
70 let mut vpoints = (0..len)
71 .map(|i| i as f64 / NUM_SEGMENTS as f64 / 2. * angle + start_angle)
72 .map(|theta| {
73 let (mut x, mut y) = (theta.cos(), theta.sin());
74 if x.abs() < 1.8e-7 {
75 x = 0.;
76 }
77 if y.abs() < 1.8e-7 {
78 y = 0.;
79 }
80 x * rx * u + y * ry * v
81 })
82 .collect::<Vec<_>>();
83
84 let k = (angle / NUM_SEGMENTS as f64 / 2.).cos();
85 vpoints.iter_mut().skip(1).step_by(2).for_each(|p| *p /= k);
86 vpoints.shift(center);
87 vpoints
88 }
89}
90
91impl From<Arc> for EllipticArc {
92 fn from(value: Arc) -> Self {
93 let Arc {
94 basis,
95 center,
96 radius,
97 angle,
98 stroke_rgba,
99 stroke_width,
100 } = value;
101 EllipticArc {
102 basis,
103 center,
104 radius: DVec2::splat(radius),
105 start_angle: 0.,
106 angle,
107 stroke_rgba,
108 stroke_width,
109 }
110 }
111}
112
113impl From<Circle> for EllipticArc {
114 fn from(value: Circle) -> Self {
115 let Circle {
116 basis,
117 center,
118 radius,
119 stroke_rgba,
120 stroke_width,
121 ..
122 } = value;
123 EllipticArc {
124 basis,
125 center,
126 radius: DVec2::splat(radius),
127 start_angle: 0.,
128 angle: TAU,
129 stroke_rgba,
130 stroke_width,
131 }
132 }
133}
134
135impl From<EllipticArc> for VItem {
136 fn from(value: EllipticArc) -> Self {
137 let EllipticArc {
138 stroke_rgba,
139 stroke_width,
140 ..
141 } = value;
142 VItem::from_vpoints(value.generate_vpoints()).with(|vitem| {
143 vitem
144 .set_stroke_color(stroke_rgba)
145 .set_stroke_width(stroke_width)
146 .discard()
147 })
148 }
149}
150
151impl From<Ellipse> for EllipticArc {
152 fn from(value: Ellipse) -> Self {
153 let Ellipse {
154 basis,
155 center,
156 radius,
157 stroke_rgba,
158 stroke_width,
159 ..
160 } = value;
161 EllipticArc {
162 basis,
163 center,
164 radius,
165 start_angle: 0.,
166 angle: TAU,
167 stroke_rgba,
168 stroke_width,
169 }
170 }
171}
172
173impl Aabb for EllipticArc {
174 fn aabb(&self) -> [DVec3; 2] {
175 VPointVec(self.generate_vpoints()).aabb()
178 }
179}
180
181impl Extract for EllipticArc {
182 type Target = CoreItem;
183 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
184 VItem::from(self.clone()).extract_into(buf);
185 }
186}
187
188impl StrokeColor for EllipticArc {
189 fn stroke_color(&self) -> AlphaColor<Srgb> {
190 self.stroke_rgba
191 }
192 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
193 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
194 self
195 }
196 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
197 self.stroke_rgba = color;
198 self
199 }
200}
201
202impl Opacity for EllipticArc {
203 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
204 self.set_stroke_opacity(opacity);
205 self
206 }
207}
208
209impl ShiftTransform for EllipticArc {
210 fn shift(&mut self, shift: DVec3) -> &mut Self {
211 self.center += shift;
212 self
213 }
214}
215
216impl RotateTransform for EllipticArc {
217 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
218 self.basis.rotate_on_axis(axis, angle);
219 self.center.rotate_on_axis(axis, angle);
220 self
221 }
222}