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