Skip to main content

ranim_items/vitem/geometry/
parallelogram.rs

1#[cfg(feature = "render")]
2use ranim_render::extract::{Extract, ExtractContext};
3
4use ranim_core::{
5    anchor::Aabb,
6    color::{AlphaColor, Srgb},
7    glam::{DAffine3, DVec3},
8    traits::{ApplyTransform, Discard, FillColor, Opacity, StrokeColor, StrokeWidth, With},
9    utils::bezier::PathBuilder,
10};
11
12use crate::vitem::{
13    DEFAULT_STROKE_WIDTH, VItem,
14    geometry::{Polygon, Rectangle, Square},
15};
16
17/// A general affine parallelogram in local coordinates.
18#[derive(Debug, Clone, ranim_macros::Interpolatable)]
19#[allow(missing_docs)]
20pub struct Parallelogram {
21    /// Local origin.
22    pub origin: DVec3,
23    /// Local edge vectors.
24    pub axes: (DVec3, DVec3),
25    pub stroke_rgba: AlphaColor<Srgb>,
26    pub stroke_width: f32,
27    pub fill_rgba: AlphaColor<Srgb>,
28}
29
30impl Parallelogram {
31    /// Creates a parallelogram with a zero local origin.
32    pub fn new(axes: (DVec3, DVec3)) -> Self {
33        Self::from_origin_and_axes(DVec3::ZERO, axes)
34    }
35    /// Creates a parallelogram with an explicit local origin and edge vectors.
36    pub fn from_origin_and_axes(origin: DVec3, axes: (DVec3, DVec3)) -> Self {
37        Self {
38            origin,
39            axes,
40            stroke_rgba: AlphaColor::WHITE,
41            stroke_width: DEFAULT_STROKE_WIDTH,
42            fill_rgba: AlphaColor::TRANSPARENT,
43        }
44    }
45    /// Returns the four vertices in winding order.
46    pub fn vertices(&self) -> [DVec3; 4] {
47        let (u, v) = self.axes;
48        [
49            self.origin,
50            self.origin + u,
51            self.origin + u + v,
52            self.origin + v,
53        ]
54    }
55}
56impl Aabb for Parallelogram {
57    fn aabb(&self) -> [DVec3; 2] {
58        self.vertices().aabb()
59    }
60}
61impl<G: Into<DAffine3>> ApplyTransform<G> for Parallelogram {
62    fn apply(&mut self, transform: G) -> &mut Self {
63        let t = transform.into();
64        self.origin = t.transform_point3(self.origin);
65        self.axes.0 = t.transform_vector3(self.axes.0);
66        self.axes.1 = t.transform_vector3(self.axes.1);
67        self
68    }
69}
70impl StrokeColor for Parallelogram {
71    fn stroke_color(&self) -> AlphaColor<Srgb> {
72        self.stroke_rgba
73    }
74    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
75        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
76        self
77    }
78    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
79        self.stroke_rgba = color;
80        self
81    }
82}
83impl FillColor for Parallelogram {
84    fn fill_color(&self) -> AlphaColor<Srgb> {
85        self.fill_rgba
86    }
87    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
88        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
89        self
90    }
91    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
92        self.fill_rgba = color;
93        self
94    }
95}
96impl Opacity for Parallelogram {
97    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
98        self.set_stroke_opacity(opacity).set_fill_opacity(opacity)
99    }
100}
101
102impl From<Rectangle> for Parallelogram {
103    fn from(value: Rectangle) -> Self {
104        let (w, h) = (value.size.x, value.size.y);
105        Self {
106            origin: DVec3::new(-w / 2.0, -h / 2.0, 0.0),
107            axes: (DVec3::X * w, DVec3::Y * h),
108            stroke_rgba: value.stroke_rgba,
109            stroke_width: value.stroke_width,
110            fill_rgba: value.fill_rgba,
111        }
112    }
113}
114impl From<Square> for Parallelogram {
115    fn from(value: Square) -> Self {
116        Parallelogram::from(Rectangle::from(value))
117    }
118}
119impl From<Parallelogram> for Polygon {
120    fn from(value: Parallelogram) -> Self {
121        let p = value.vertices();
122        Polygon::new(p.to_vec()).with(|x| {
123            x.set_stroke_color(value.stroke_rgba)
124                .set_fill_color(value.fill_rgba);
125            x.stroke_width = value.stroke_width;
126        })
127    }
128}
129impl From<Parallelogram> for VItem {
130    fn from(value: Parallelogram) -> Self {
131        let (origin, (u, v), stroke_rgba, stroke_width, fill_rgba) = (
132            value.origin,
133            value.axes,
134            value.stroke_rgba,
135            value.stroke_width,
136            value.fill_rgba,
137        );
138        VItem::from_vpoints(
139            PathBuilder::new()
140                .move_to(origin)
141                .line_to(origin + u)
142                .line_to(origin + u + v)
143                .line_to(origin + v)
144                .close_path()
145                .vpoints()
146                .into(),
147        )
148        .with(|item| {
149            item.set_fill_color(fill_rgba)
150                .set_stroke_color(stroke_rgba)
151                .set_stroke_width(stroke_width)
152                .discard()
153        })
154    }
155}
156#[cfg(feature = "render")]
157impl Extract for Parallelogram {
158    fn extract(&self, ctx: &mut ExtractContext<'_>) {
159        VItem::from(self.clone()).extract(ctx);
160    }
161}