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;
6
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 axes: (DVec3, DVec3),
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 axes: (DVec3::X, DVec3::Y),
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.axes.0.normalize()
74 }
75 pub fn end(&self) -> DVec3 {
77 let u = self.angle.cos() * self.axes.0.normalize();
78 let v = self.angle.sin() * self.axes.1.normalize();
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.axes.0.rotate_on_axis(axis, angle);
102 self.axes.0 = self.axes.0.normalize();
103 self.axes.1.rotate_on_axis(axis, angle);
104 self.axes.1 = self.axes.1.normalize();
105 self
106 }
107}
108
109impl Opacity for Arc {
110 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
111 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
112 self
113 }
114}
115
116impl StrokeColor for Arc {
117 fn stroke_color(&self) -> AlphaColor<Srgb> {
118 self.stroke_rgba
119 }
120 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
121 self.stroke_rgba = color;
122 self
123 }
124 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
125 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
126 self
127 }
128}
129
130impl From<Arc> for VItem {
132 fn from(value: Arc) -> Self {
133 EllipticArc::from(value).into()
134 }
135}
136
137impl Extract for Arc {
138 type Target = CoreItem;
139 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
140 VItem::from(self.clone()).extract_into(buf);
141 }
142}
143
144#[derive(Clone, Debug, ranim_macros::Interpolatable)]
147pub struct ArcBetweenPoints {
148 pub axes: (DVec3, DVec3),
150 pub start: DVec3,
152 pub end: DVec3,
154 pub angle: f64,
156
157 pub stroke_rgba: AlphaColor<Srgb>,
159 pub stroke_width: f32,
161}
162
163impl ArcBetweenPoints {
164 pub fn new(start: DVec3, end: DVec3, angle: f64) -> Self {
166 Self {
167 axes: (DVec3::X, DVec3::Y),
168 start,
169 end,
170 angle,
171
172 stroke_rgba: AlphaColor::WHITE,
173 stroke_width: DEFAULT_STROKE_WIDTH,
174 }
175 }
176 pub fn scale(&mut self, scale: f64) -> &mut Self {
181 self.scale_at(scale, AabbPoint::CENTER)
182 }
183 pub fn scale_at<T>(&mut self, scale: f64, anchor: T) -> &mut Self
188 where
189 T: Locate<Self>,
190 {
191 let point = anchor.locate(self);
192 self.start
193 .shift(-point)
194 .scale(DVec3::splat(scale))
195 .shift(point);
196 self.end
197 .shift(-point)
198 .scale(DVec3::splat(scale))
199 .shift(point);
200 self
201 }
202}
203
204impl Aabb for ArcBetweenPoints {
206 fn aabb(&self) -> [DVec3; 2] {
208 Arc::from(self.clone()).aabb()
210 }
211}
212
213impl ShiftTransform for ArcBetweenPoints {
214 fn shift(&mut self, shift: DVec3) -> &mut Self {
215 self.start.shift(shift);
216 self.end.shift(shift);
217 self
218 }
219}
220
221impl RotateTransform for ArcBetweenPoints {
222 fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
223 self.start.rotate_on_axis(axis, angle);
224 self.end.rotate_on_axis(axis, angle);
225 self.axes.0.rotate_on_axis(axis, angle);
226 self.axes.0 = self.axes.0.normalize();
227 self.axes.1.rotate_on_axis(axis, angle);
228 self.axes.1 = self.axes.1.normalize();
229 self
230 }
231}
232
233impl Opacity for ArcBetweenPoints {
234 fn set_opacity(&mut self, opacity: f32) -> &mut Self {
235 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
236 self
237 }
238}
239
240impl StrokeColor for ArcBetweenPoints {
241 fn stroke_color(&self) -> AlphaColor<Srgb> {
242 self.stroke_rgba
243 }
244 fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
245 self.stroke_rgba = color;
246 self
247 }
248 fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
249 self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
250 self
251 }
252}
253
254impl From<ArcBetweenPoints> for Arc {
256 fn from(value: ArcBetweenPoints) -> Arc {
257 let ArcBetweenPoints {
258 axes: proj,
259 start,
260 end,
261 angle,
262 stroke_rgba,
263 stroke_width,
264 } = value;
265 let radius = (start.distance(end) / 2.0) / (angle / 2.0).sin();
266
267 Arc {
268 axes: proj,
269 angle,
270 radius,
271 center: DVec3::ZERO,
272 stroke_rgba,
273 stroke_width,
274 }
275 .with(|arc| {
276 let cur_start = arc.start();
277
278 let v1 = arc.end() - arc.start();
279 let v2 = end - start;
280
281 let rot_angle = v1.angle_between(v2);
282 let mut rot_axis = v1.cross(v2);
283 if rot_axis.length_squared() <= f64::EPSILON {
284 rot_axis = DVec3::NEG_Z;
285 }
286 rot_axis = rot_axis.normalize();
287 arc.shift(start - cur_start);
288 arc.shift(-start);
289 arc.rotate_on_axis(rot_axis, rot_angle);
290 arc.shift(start);
291 })
292 }
293}
294
295impl From<ArcBetweenPoints> for VItem {
296 fn from(value: ArcBetweenPoints) -> Self {
297 Arc::from(value).into()
298 }
299}
300
301impl Extract for ArcBetweenPoints {
302 type Target = CoreItem;
303 fn extract_into(&self, buf: &mut Vec<Self::Target>) {
304 Arc::from(self.clone()).extract_into(buf);
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use std::f64::consts::PI;
311
312 use assert_float_eq::assert_float_absolute_eq;
313 use glam::dvec3;
314 use ranim_core::traits::ShiftTransformExt;
315
316 use crate::vitem::geometry::anchor::Origin;
317
318 use super::*;
319
320 #[test]
321 fn test_arc() {
322 let arc = Arc::new(PI / 2.0, 2.0);
323 assert_float_absolute_eq!(
324 arc.start().distance_squared(dvec3(2.0, 0.0, 0.0)),
325 0.0,
326 1e-10
327 );
328 assert_float_absolute_eq!(arc.end().distance_squared(dvec3(0.0, 2.0, 0.0)), 0.0, 1e-10);
329
330 let arc_between_points =
331 ArcBetweenPoints::new(dvec3(2.0, 0.0, 0.0), dvec3(0.0, 2.0, 0.0), PI / 2.0);
332 let arc_between_points = Arc::from(arc_between_points);
333 assert_float_absolute_eq!(
334 arc.center.distance_squared(arc_between_points.center),
335 0.0,
336 1e-10
337 );
338 assert_float_absolute_eq!(arc.radius - arc_between_points.radius, 0.0, 1e-10);
339 assert_float_absolute_eq!(arc.angle - arc_between_points.angle, 0.0, 1e-10);
340
341 let arc_between_points =
342 ArcBetweenPoints::new(dvec3(0.0, 2.0, 0.0), dvec3(2.0, 0.0, 0.0), PI / 2.0);
343 let arc_between_points = Arc::from(arc_between_points);
344 let arc = Arc::new(PI / 2.0, 2.0).with(|arc| {
345 arc.with_origin(Origin, |x| {
346 x.rotate_on_axis(DVec3::NEG_Z, PI);
347 })
348 .shift(dvec3(2.0, 2.0, 0.0));
349 });
350 assert_float_absolute_eq!(
351 arc.center.distance_squared(arc_between_points.center),
352 0.0,
353 1e-10
354 );
355 assert_float_absolute_eq!(arc.radius - arc_between_points.radius, 0.0, 1e-10);
356 assert_float_absolute_eq!(arc.angle - arc_between_points.angle, 0.0, 1e-10);
357 }
358}