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 basis: (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 basis vectors.
35    pub fn new(origin: DVec3, basis: (DVec3, DVec3)) -> Self {
36        Self {
37            origin,
38            basis,
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            basis: (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.basis.0.rotate_on_axis(axis, angle);
73        self.basis.1.rotate_on_axis(axis, angle);
74        self
75    }
76}
77
78impl ScaleTransform for Parallelogram {
79    fn scale(&mut self, scale: DVec3) -> &mut Self {
80        self.origin.scale(scale).discard();
81        self.basis.0 *= scale;
82        self.basis.1 *= scale;
83        self
84    }
85}
86
87impl StrokeColor for Parallelogram {
88    fn stroke_color(&self) -> AlphaColor<Srgb> {
89        self.stroke_rgba
90    }
91
92    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
93        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
94        self
95    }
96
97    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
98        self.stroke_rgba = color;
99        self
100    }
101}
102
103impl FillColor for Parallelogram {
104    fn fill_color(&self) -> AlphaColor<Srgb> {
105        self.fill_rgba
106    }
107
108    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
109        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
110        self
111    }
112
113    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
114        self.fill_rgba = color;
115        self
116    }
117}
118
119impl Opacity for Parallelogram {
120    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
121        self.set_stroke_opacity(opacity).set_fill_opacity(opacity);
122        self
123    }
124}
125
126impl From<Rectangle> for Parallelogram {
127    fn from(value: Rectangle) -> Self {
128        let Rectangle {
129            basis,
130            p0,
131            size,
132            stroke_rgba,
133            stroke_width,
134            fill_rgba,
135        } = value;
136        let (u, v) = basis.uv();
137        let basis = (u * size.x, v * size.y);
138        Self {
139            origin: p0,
140            basis,
141            stroke_rgba,
142            stroke_width,
143            fill_rgba,
144        }
145    }
146}
147
148impl From<Square> for Parallelogram {
149    fn from(value: Square) -> Self {
150        let Square {
151            basis,
152            center,
153            size,
154            stroke_rgba,
155            stroke_width,
156            fill_rgba,
157        } = value;
158        let (u, v) = basis.uv();
159        let basis = (u * size, v * size);
160        let origin = center - (u + v) * size / 2.;
161        Self {
162            origin,
163            basis,
164            stroke_rgba,
165            stroke_width,
166            fill_rgba,
167        }
168    }
169}
170
171impl From<Parallelogram> for Polygon {
172    fn from(value: Parallelogram) -> Self {
173        let Parallelogram {
174            stroke_rgba,
175            stroke_width,
176            fill_rgba,
177            ..
178        } = value;
179        Polygon::new(value.vertices().to_vec()).with(|item| {
180            item.set_stroke_color(stroke_rgba).set_fill_color(fill_rgba);
181            item.stroke_width = stroke_width;
182        })
183    }
184}
185
186impl From<Parallelogram> for VItem {
187    fn from(value: Parallelogram) -> Self {
188        let Parallelogram {
189            origin,
190            basis: (u, v),
191            stroke_rgba,
192            stroke_width,
193            fill_rgba,
194        } = value;
195        VItem::from_vpoints(
196            PathBuilder::new()
197                .move_to(origin)
198                .line_to(origin + u)
199                .line_to(origin + u + v)
200                .line_to(origin + v)
201                .close_path()
202                .vpoints()
203                .into(),
204        )
205        .with(|item| {
206            item.set_fill_color(fill_rgba)
207                .set_stroke_color(stroke_rgba)
208                .set_stroke_width(stroke_width)
209                .discard()
210        })
211    }
212}
213
214impl Extract for Parallelogram {
215    type Target = CoreItem;
216
217    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
218        VItem::from(self.clone()).extract_into(buf)
219    }
220}