ranim_items/vitem/geometry/
parallelogram.rs

1use ranim_core::{
2    Extract,
3    color::{AlphaColor, Srgb},
4    core_item::{CoreItem, vitem::DEFAULT_STROKE_WIDTH},
5    glam::DVec3,
6    traits::{
7        Aabb, Discard, FillColor, Opacity, RotateTransform, ScaleTransform, ShiftTransform,
8        StrokeColor, StrokeWidth, With,
9    },
10    utils::bezier::PathBuilder,
11};
12
13use crate::vitem::{
14    VItem,
15    geometry::{Polygon, Rectangle, Square},
16};
17
18/// A parallelogram.
19#[derive(Debug, Clone, ranim_macros::Interpolatable)]
20pub struct Parallelogram {
21    /// Origin of the paralleogram
22    pub origin: DVec3,
23    /// vectors representing two edges of the paralleogram
24    pub axes: (DVec3, DVec3),
25    /// Stroke rgba
26    pub stroke_rgba: AlphaColor<Srgb>,
27    /// Stroke width
28    pub stroke_width: f32,
29    /// Fill rgba
30    pub fill_rgba: AlphaColor<Srgb>,
31}
32
33impl Parallelogram {
34    /// Create a new parallelogram with the given origin and axes vectors.
35    pub fn new(origin: DVec3, axes: (DVec3, DVec3)) -> Self {
36        Self {
37            origin,
38            axes,
39            stroke_rgba: AlphaColor::WHITE,
40            stroke_width: DEFAULT_STROKE_WIDTH,
41            fill_rgba: AlphaColor::TRANSPARENT,
42        }
43    }
44
45    /// Get the vertices of the parallelogram.
46    pub fn vertices(&self) -> [DVec3; 4] {
47        let &Parallelogram {
48            origin,
49            axes: (u, v),
50            ..
51        } = self;
52        [origin, origin + u, origin + u + v, origin + v]
53    }
54}
55
56impl Aabb for Parallelogram {
57    fn aabb(&self) -> [DVec3; 2] {
58        self.vertices().aabb()
59    }
60}
61
62impl ShiftTransform for Parallelogram {
63    fn shift(&mut self, offset: DVec3) -> &mut Self {
64        self.origin += offset;
65        self
66    }
67}
68
69impl RotateTransform for Parallelogram {
70    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
71        self.origin.rotate_on_axis(axis, angle);
72        self.axes.0.rotate_on_axis(axis, angle);
73        self.axes.0 = self.axes.0.normalize();
74        self.axes.1.rotate_on_axis(axis, angle);
75        self.axes.1 = self.axes.1.normalize();
76        self
77    }
78}
79
80impl ScaleTransform for Parallelogram {
81    fn scale(&mut self, scale: DVec3) -> &mut Self {
82        self.origin.scale(scale).discard();
83        self.axes.0 *= scale;
84        self.axes.1 *= scale;
85        self
86    }
87}
88
89impl StrokeColor for Parallelogram {
90    fn stroke_color(&self) -> AlphaColor<Srgb> {
91        self.stroke_rgba
92    }
93
94    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
95        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
96        self
97    }
98
99    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
100        self.stroke_rgba = color;
101        self
102    }
103}
104
105impl FillColor for Parallelogram {
106    fn fill_color(&self) -> AlphaColor<Srgb> {
107        self.fill_rgba
108    }
109
110    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
111        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
112        self
113    }
114
115    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
116        self.fill_rgba = color;
117        self
118    }
119}
120
121impl Opacity for Parallelogram {
122    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
123        self.set_stroke_opacity(opacity).set_fill_opacity(opacity);
124        self
125    }
126}
127
128impl From<Rectangle> for Parallelogram {
129    fn from(value: Rectangle) -> Self {
130        let Rectangle {
131            axes,
132            p0,
133            size,
134            stroke_rgba,
135            stroke_width,
136            fill_rgba,
137        } = value;
138        let (u, v) = (axes.0.normalize(), axes.1.normalize());
139        let axes = (u * size.x, v * size.y);
140        Self {
141            origin: p0,
142            axes,
143            stroke_rgba,
144            stroke_width,
145            fill_rgba,
146        }
147    }
148}
149
150impl From<Square> for Parallelogram {
151    fn from(value: Square) -> Self {
152        let Square {
153            axes,
154            center,
155            size,
156            stroke_rgba,
157            stroke_width,
158            fill_rgba,
159        } = value;
160        let (u, v) = (axes.0.normalize(), axes.1.normalize());
161        let axes = (u * size, v * size);
162        let origin = center - (u + v) * size / 2.;
163        Self {
164            origin,
165            axes,
166            stroke_rgba,
167            stroke_width,
168            fill_rgba,
169        }
170    }
171}
172
173impl From<Parallelogram> for Polygon {
174    fn from(value: Parallelogram) -> Self {
175        let Parallelogram {
176            stroke_rgba,
177            stroke_width,
178            fill_rgba,
179            ..
180        } = value;
181        Polygon::new(value.vertices().to_vec()).with(|item| {
182            item.set_stroke_color(stroke_rgba).set_fill_color(fill_rgba);
183            item.stroke_width = stroke_width;
184        })
185    }
186}
187
188impl From<Parallelogram> for VItem {
189    fn from(value: Parallelogram) -> Self {
190        let Parallelogram {
191            origin,
192            axes: (u, v),
193            stroke_rgba,
194            stroke_width,
195            fill_rgba,
196        } = value;
197        VItem::from_vpoints(
198            PathBuilder::new()
199                .move_to(origin)
200                .line_to(origin + u)
201                .line_to(origin + u + v)
202                .line_to(origin + v)
203                .close_path()
204                .vpoints()
205                .into(),
206        )
207        .with(|item| {
208            item.set_fill_color(fill_rgba)
209                .set_stroke_color(stroke_rgba)
210                .set_stroke_width(stroke_width)
211                .discard()
212        })
213    }
214}
215
216impl Extract for Parallelogram {
217    type Target = CoreItem;
218
219    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
220        VItem::from(self.clone()).extract_into(buf)
221    }
222}