ranim_items/vitem/geometry/
anchor.rs1use ranim_core::{
2 glam::{DAffine3, DVec2, DVec3},
3 prelude::Transformed,
4 traits::Locate,
5};
6
7use crate::vitem::geometry::{Arc, ArcBetweenPoints, Circle, Ellipse, EllipticArc};
8
9#[derive(Debug, Clone, Copy)]
11pub struct Origin;
12
13#[derive(Debug, Clone, Copy)]
15pub enum Focus {
16 Pos,
18 Neg,
20}
21
22impl<T, G> Locate<Transformed<T, G>> for Origin
23where
24 Origin: Locate<T>,
25 G: Clone + Into<DAffine3>,
26{
27 fn locate(&self, target: &Transformed<T, G>) -> DVec3 {
28 target
29 .transform
30 .clone()
31 .into()
32 .transform_point3(self.locate(&target.inner))
33 }
34}
35
36impl<T, G> Locate<Transformed<T, G>> for Focus
37where
38 Focus: Locate<T>,
39 G: Clone + Into<DAffine3>,
40{
41 fn locate(&self, target: &Transformed<T, G>) -> DVec3 {
42 target
43 .transform
44 .clone()
45 .into()
46 .transform_point3(self.locate(&target.inner))
47 }
48}
49
50impl Locate<Arc> for Origin {
51 fn locate(&self, _target: &Arc) -> DVec3 {
52 DVec3::ZERO
53 }
54}
55
56impl Locate<Arc> for Focus {
57 fn locate(&self, _target: &Arc) -> DVec3 {
58 DVec3::ZERO
59 }
60}
61
62impl Locate<ArcBetweenPoints> for Origin {
63 fn locate(&self, target: &ArcBetweenPoints) -> DVec3 {
64 target.center()
65 }
66}
67
68impl Locate<ArcBetweenPoints> for Focus {
69 fn locate(&self, target: &ArcBetweenPoints) -> DVec3 {
70 target.center()
71 }
72}
73
74impl Locate<Circle> for Origin {
75 fn locate(&self, _target: &Circle) -> DVec3 {
76 DVec3::ZERO
77 }
78}
79
80impl Locate<Circle> for Focus {
81 fn locate(&self, _target: &Circle) -> DVec3 {
82 DVec3::ZERO
83 }
84}
85
86fn ellipse_focus(radius: DVec2) -> DVec3 {
87 let DVec2 { x: rx, y: ry } = radius;
88 let c = (rx * rx - ry * ry).abs().sqrt();
89 if rx > ry { DVec3::X * c } else { DVec3::Y * c }
90}
91
92impl Locate<EllipticArc> for Origin {
93 fn locate(&self, _target: &EllipticArc) -> DVec3 {
94 DVec3::ZERO
95 }
96}
97
98impl Locate<EllipticArc> for Focus {
99 fn locate(&self, target: &EllipticArc) -> DVec3 {
100 let focus = ellipse_focus(target.radius);
101 if matches!(self, Focus::Pos) {
102 focus
103 } else {
104 -focus
105 }
106 }
107}
108
109impl Locate<Ellipse> for Origin {
110 fn locate(&self, _target: &Ellipse) -> DVec3 {
111 DVec3::ZERO
112 }
113}
114
115impl Locate<Ellipse> for Focus {
116 fn locate(&self, target: &Ellipse) -> DVec3 {
117 let focus = ellipse_focus(target.radius);
118 if matches!(self, Focus::Pos) {
119 focus
120 } else {
121 -focus
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use ranim_core::{
130 glam::{DAffine3, dvec2, dvec3},
131 prelude::TransformedExt,
132 };
133
134 #[test]
135 fn transformed_focus_is_located_in_local_space_then_moved() {
136 let ellipse = Ellipse::new(dvec2(5.0, 3.0));
137 let placed = ellipse.transformed(DAffine3::from_translation(dvec3(2.0, 0.0, 0.0)));
138 assert_eq!(Focus::Pos.locate(&placed), dvec3(6.0, 0.0, 0.0));
139 }
140}