Skip to main content

ranim_items/vitem/geometry/
parallelogram.rs

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