ranim_items/vitem/geometry/
polygon.rs

1use ranim_core::{Extract, color, glam, traits::Anchor};
2
3use color::{AlphaColor, Srgb};
4use glam::{DVec3, dvec3};
5use itertools::Itertools;
6
7use ranim_core::{
8    primitives::vitem::{DEFAULT_STROKE_WIDTH, VItemPrimitive},
9    traits::{
10        Alignable, BoundingBox, FillColor, Interpolatable, Opacity, Rotate, Scale, Shift,
11        StrokeColor, StrokeWidth, With,
12    },
13};
14
15use crate::vitem::VItem;
16
17// MARK: ### Square ###
18/// A Square
19#[derive(Clone, Debug)]
20pub struct Square {
21    /// Center
22    pub center: DVec3,
23    /// Size
24    pub size: f64,
25    /// Up vec
26    pub up: DVec3,
27    /// Normal vec
28    pub normal: DVec3,
29
30    /// Stroke rgba
31    pub stroke_rgba: AlphaColor<Srgb>,
32    /// Stroke width
33    pub stroke_width: f32,
34    /// Fill rgba
35    pub fill_rgba: AlphaColor<Srgb>,
36}
37
38impl Interpolatable for Square {
39    fn lerp(&self, target: &Self, t: f64) -> Self {
40        Self {
41            center: Interpolatable::lerp(&self.center, &target.center, t),
42            size: Interpolatable::lerp(&self.size, &target.size, t),
43            up: Interpolatable::lerp(&self.up, &target.up, t),
44            normal: Interpolatable::lerp(&self.normal, &target.normal, t),
45            stroke_rgba: Interpolatable::lerp(&self.stroke_rgba, &target.stroke_rgba, t),
46            stroke_width: Interpolatable::lerp(&self.stroke_width, &target.stroke_width, t),
47            fill_rgba: Interpolatable::lerp(&self.fill_rgba, &target.fill_rgba, t),
48        }
49    }
50}
51
52impl Square {
53    /// Constructor
54    pub fn new(size: f64) -> Self {
55        Self {
56            center: dvec3(0.0, 0.0, 0.0),
57            size,
58            up: dvec3(0.0, 1.0, 0.0),
59            normal: dvec3(0.0, 0.0, 1.0),
60
61            stroke_rgba: AlphaColor::WHITE,
62            stroke_width: DEFAULT_STROKE_WIDTH,
63            fill_rgba: AlphaColor::TRANSPARENT,
64        }
65    }
66    /// Scale the square by the given scale, with the given anchor as the center.
67    ///
68    /// Note that this accepts a `f64` scale dispite of [`Scale`]'s `DVec3`,
69    /// because this keeps the square a square.
70    pub fn scale(&mut self, scale: f64) -> &mut Self {
71        self.scale_by_anchor(scale, Anchor::CENTER)
72    }
73    /// Scale the square by the given scale, with the given anchor as the center.
74    ///
75    /// Note that this accepts a `f64` scale dispite of [`Scale`]'s `DVec3`,
76    /// because this keeps the square a square.
77    pub fn scale_by_anchor(&mut self, scale: f64, anchor: Anchor) -> &mut Self {
78        let anchor = Anchor::Point(match anchor {
79            Anchor::Point(point) => point,
80            Anchor::Edge(edge) => self.get_bounding_box_point(edge),
81        });
82        self.size *= scale;
83        self.center.scale_by_anchor(DVec3::splat(scale), anchor);
84        self
85    }
86}
87
88// MARK: Traits impl
89impl BoundingBox for Square {
90    fn get_bounding_box(&self) -> [DVec3; 3] {
91        let right = -self.normal.cross(self.up).normalize();
92        [
93            self.center - self.size / 2.0 * right + self.size / 2.0 * self.up,
94            self.center + self.size / 2.0 * right - self.size / 2.0 * self.up,
95        ]
96        .get_bounding_box()
97    }
98}
99
100impl Shift for Square {
101    fn shift(&mut self, shift: DVec3) -> &mut Self {
102        self.center.shift(shift);
103        self
104    }
105}
106
107impl Rotate for Square {
108    fn rotate_by_anchor(&mut self, angle: f64, axis: DVec3, anchor: Anchor) -> &mut Self {
109        let anchor = Anchor::Point(match anchor {
110            Anchor::Point(point) => point,
111            Anchor::Edge(edge) => self.get_bounding_box_point(edge),
112        });
113        self.center.rotate_by_anchor(angle, axis, anchor);
114        self.up.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
115        self.normal.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
116        self
117    }
118}
119
120impl Alignable for Square {
121    fn is_aligned(&self, _other: &Self) -> bool {
122        true
123    }
124    fn align_with(&mut self, _other: &mut Self) {}
125}
126
127impl Opacity for Square {
128    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
129        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
130        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
131        self
132    }
133}
134
135impl StrokeColor for Square {
136    fn stroke_color(&self) -> AlphaColor<Srgb> {
137        self.stroke_rgba
138    }
139    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
140        self.stroke_rgba = color;
141        self
142    }
143    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
144        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
145        self
146    }
147}
148
149impl FillColor for Square {
150    fn fill_color(&self) -> AlphaColor<Srgb> {
151        self.fill_rgba
152    }
153    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
154        self.fill_rgba = color;
155        self
156    }
157    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
158        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
159        self
160    }
161}
162
163impl Extract for Square {
164    type Target = VItemPrimitive;
165    fn extract(&self) -> Vec<Self::Target> {
166        VItem::from(self.clone()).extract()
167    }
168}
169
170// MARK: Conversions
171impl From<Square> for Rectangle {
172    fn from(value: Square) -> Self {
173        let Square {
174            center,
175            size: width,
176            up,
177            normal,
178            stroke_rgba,
179            stroke_width,
180            fill_rgba,
181        } = value;
182        let right = up.cross(normal).normalize();
183        let p1 = center - width / 2.0 * right + width / 2.0 * up;
184        let p2 = center + width / 2.0 * right - width / 2.0 * up;
185        Rectangle {
186            p1,
187            p2,
188            up,
189            normal,
190            stroke_rgba,
191            stroke_width,
192            fill_rgba,
193        }
194    }
195}
196
197impl From<Square> for Polygon {
198    fn from(value: Square) -> Self {
199        Rectangle::from(value).into()
200    }
201}
202
203impl From<Square> for VItem {
204    fn from(value: Square) -> Self {
205        Rectangle::from(value).into()
206    }
207}
208
209// MARK: ### Rectangle ###
210/// Rectangle
211#[derive(Clone, Debug)]
212pub struct Rectangle {
213    /// Corner 1
214    pub p1: DVec3,
215    /// Corner 2
216    pub p2: DVec3,
217    up: DVec3,
218    /// Normal vec
219    pub normal: DVec3,
220
221    /// Stroke rgba
222    pub stroke_rgba: AlphaColor<Srgb>,
223    /// Stroke width
224    pub stroke_width: f32,
225    /// Fill rgba
226    pub fill_rgba: AlphaColor<Srgb>,
227}
228
229impl Interpolatable for Rectangle {
230    fn lerp(&self, target: &Self, t: f64) -> Self {
231        Self {
232            p1: Interpolatable::lerp(&self.p1, &target.p1, t),
233            p2: Interpolatable::lerp(&self.p2, &target.p2, t),
234            up: Interpolatable::lerp(&self.up, &target.up, t),
235            normal: Interpolatable::lerp(&self.normal, &target.normal, t),
236            stroke_rgba: Interpolatable::lerp(&self.stroke_rgba, &target.stroke_rgba, t),
237            stroke_width: Interpolatable::lerp(&self.stroke_width, &target.stroke_width, t),
238            fill_rgba: Interpolatable::lerp(&self.fill_rgba, &target.fill_rgba, t),
239        }
240    }
241}
242
243impl Rectangle {
244    /// Constructor
245    pub fn new(width: f64, height: f64) -> Self {
246        let half_width = width / 2.0;
247        let half_height = height / 2.0;
248        Self {
249            p1: dvec3(-half_width, half_height, 0.0),
250            p2: dvec3(half_width, -half_height, 0.0),
251            up: DVec3::Y,
252            normal: DVec3::Z,
253            stroke_rgba: AlphaColor::WHITE,
254            stroke_width: DEFAULT_STROKE_WIDTH,
255            fill_rgba: AlphaColor::TRANSPARENT,
256        }
257    }
258    /// Width
259    pub fn width(&self) -> f64 {
260        let right = self.up.cross(self.normal).normalize();
261        (self.p2 - self.p1).dot(right).abs()
262    }
263    /// Height
264    pub fn height(&self) -> f64 {
265        (self.p2 - self.p1).dot(self.up).abs()
266    }
267}
268
269// MARK: Traits impl
270impl BoundingBox for Rectangle {
271    fn get_bounding_box(&self) -> [DVec3; 3] {
272        [self.p1, self.p2].get_bounding_box()
273    }
274}
275
276impl Shift for Rectangle {
277    fn shift(&mut self, shift: DVec3) -> &mut Self {
278        self.p1.shift(shift);
279        self.p2.shift(shift);
280        self
281    }
282}
283
284impl Rotate for Rectangle {
285    fn rotate_by_anchor(&mut self, angle: f64, axis: DVec3, anchor: Anchor) -> &mut Self {
286        let anchor = Anchor::Point(anchor.get_pos(self));
287        self.p1.rotate_by_anchor(angle, axis, anchor);
288        self.p2.rotate_by_anchor(angle, axis, anchor);
289        self.up.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
290        self.normal.rotate_by_anchor(angle, axis, Anchor::ORIGIN);
291        self
292    }
293}
294
295impl Scale for Rectangle {
296    fn scale_by_anchor(&mut self, scale: DVec3, anchor: Anchor) -> &mut Self {
297        let anchor = Anchor::Point(anchor.get_pos(self));
298        self.p1.scale_by_anchor(scale, anchor);
299        self.p2.scale_by_anchor(scale, anchor);
300        self
301    }
302}
303
304impl Opacity for Rectangle {
305    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
306        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
307        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
308        self
309    }
310}
311
312impl Alignable for Rectangle {
313    fn align_with(&mut self, _other: &mut Self) {}
314    fn is_aligned(&self, _other: &Self) -> bool {
315        true
316    }
317}
318
319impl StrokeColor for Rectangle {
320    fn stroke_color(&self) -> AlphaColor<Srgb> {
321        self.stroke_rgba
322    }
323    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
324        self.stroke_rgba = color;
325        self
326    }
327    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
328        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
329        self
330    }
331}
332
333impl FillColor for Rectangle {
334    fn fill_color(&self) -> AlphaColor<Srgb> {
335        self.fill_rgba
336    }
337    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
338        self.fill_rgba = color;
339        self
340    }
341    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
342        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
343        self
344    }
345}
346
347// MARK: Conversions
348impl From<Rectangle> for Polygon {
349    fn from(value: Rectangle) -> Self {
350        let points = vec![
351            value.p1,
352            value.p1 - value.up * value.height(),
353            value.p2,
354            value.p2 + value.up * value.height(),
355        ];
356        Polygon {
357            points,
358            stroke_rgba: value.stroke_rgba,
359            stroke_width: value.stroke_width,
360            fill_rgba: value.fill_rgba,
361        }
362    }
363}
364
365impl From<Rectangle> for VItem {
366    fn from(value: Rectangle) -> Self {
367        Polygon::from(value).into()
368    }
369}
370
371impl Extract for Rectangle {
372    type Target = VItemPrimitive;
373    fn extract(&self) -> Vec<Self::Target> {
374        VItem::from(self.clone()).extract()
375    }
376}
377
378// MARK: ### Polygon ###
379/// A Polygon with uniform stroke and fill
380#[derive(Clone, Debug)]
381pub struct Polygon {
382    /// Corner points
383    pub points: Vec<DVec3>,
384    /// Stroke rgba
385    pub stroke_rgba: AlphaColor<Srgb>,
386    /// Stroke width
387    pub stroke_width: f32,
388    /// Fill rgba
389    pub fill_rgba: AlphaColor<Srgb>,
390    // _need_update: RefCell<bool>,
391    // _extract_cache: RefCell<Option<VItem>>,
392}
393
394impl Polygon {
395    /// Constructor
396    pub fn new(points: Vec<DVec3>) -> Self {
397        Self {
398            points,
399            stroke_rgba: AlphaColor::WHITE,
400            stroke_width: DEFAULT_STROKE_WIDTH,
401            fill_rgba: AlphaColor::TRANSPARENT,
402            // _need_update: RefCell::new(true),
403            // _extract_cache: RefCell::new(None),
404        }
405    }
406}
407
408// MARK: Traits impl
409impl BoundingBox for Polygon {
410    fn get_bounding_box(&self) -> [DVec3; 3] {
411        self.points.get_bounding_box()
412    }
413}
414
415impl Shift for Polygon {
416    fn shift(&mut self, shift: DVec3) -> &mut Self {
417        self.points.shift(shift);
418        self
419    }
420}
421
422impl Rotate for Polygon {
423    fn rotate_by_anchor(&mut self, angle: f64, axis: DVec3, anchor: Anchor) -> &mut Self {
424        self.points.rotate_by_anchor(angle, axis, anchor);
425        self
426    }
427}
428
429impl Scale for Polygon {
430    fn scale_by_anchor(&mut self, scale: DVec3, anchor: Anchor) -> &mut Self {
431        self.points.scale_by_anchor(scale, anchor);
432        self
433    }
434}
435
436impl Alignable for Polygon {
437    fn is_aligned(&self, other: &Self) -> bool {
438        self.points.len() == other.points.len()
439    }
440    fn align_with(&mut self, other: &mut Self) {
441        if self.points.len() > other.points.len() {
442            return other.align_with(self);
443        }
444        // TODO: find a better algo to minimize the distance
445        self.points
446            .resize(other.points.len(), self.points.last().cloned().unwrap());
447    }
448}
449
450impl Interpolatable for Polygon {
451    fn lerp(&self, target: &Self, t: f64) -> Self {
452        Self {
453            points: self
454                .points
455                .iter()
456                .zip(target.points.iter())
457                .map(|(a, b)| a.lerp(b, t))
458                .collect(),
459            stroke_rgba: Interpolatable::lerp(&self.stroke_rgba, &target.stroke_rgba, t),
460            stroke_width: self.stroke_width.lerp(&target.stroke_width, t),
461            fill_rgba: Interpolatable::lerp(&self.fill_rgba, &target.fill_rgba, t),
462            // _need_update: RefCell::new(true),
463            // _extract_cache: RefCell::new(None),
464        }
465    }
466}
467
468impl Opacity for Polygon {
469    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
470        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
471        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
472        self
473    }
474}
475
476impl StrokeColor for Polygon {
477    fn stroke_color(&self) -> AlphaColor<Srgb> {
478        self.stroke_rgba
479    }
480    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
481        self.stroke_rgba = color;
482        self
483    }
484    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
485        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
486        self
487    }
488}
489
490impl FillColor for Polygon {
491    fn fill_color(&self) -> AlphaColor<Srgb> {
492        self.fill_rgba
493    }
494    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
495        self.fill_rgba = color;
496        self
497    }
498    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
499        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
500        self
501    }
502}
503
504// MARK: Conversions
505impl From<Polygon> for VItem {
506    fn from(value: Polygon) -> Self {
507        let Polygon {
508            mut points,
509            stroke_rgba,
510            stroke_width,
511            fill_rgba,
512            ..
513        } = value;
514        assert!(points.len() > 2);
515
516        // Close the polygon
517        points.push(points[0]);
518
519        let anchors = points;
520        let handles = anchors
521            .iter()
522            .tuple_windows()
523            .map(|(&a, &b)| 0.5 * (a + b))
524            .collect::<Vec<_>>();
525
526        // Interleave anchors and handles
527        let vpoints = anchors.into_iter().interleave(handles).collect::<Vec<_>>();
528        VItem::from_vpoints(vpoints).with(|vitem| {
529            vitem
530                .set_fill_color(fill_rgba)
531                .set_stroke_color(stroke_rgba)
532                .set_stroke_width(stroke_width);
533        })
534    }
535}
536
537impl Extract for Polygon {
538    type Target = VItemPrimitive;
539    fn extract(&self) -> Vec<Self::Target> {
540        // trace!("extract");
541        // let mut need_update = self._need_update.borrow_mut();
542        // let mut cache = self._extract_cache.borrow_mut();
543        // if *need_update || cache.is_none() {
544        //     trace!("extract: replace vitem");
545        //     cache.replace(VItem::from(self.clone()));
546        //     *need_update = false;
547        // }
548        // cache.as_ref().unwrap().extract()
549        VItem::from(self.clone()).extract()
550    }
551}
552
553#[cfg(test)]
554mod tests {
555    use assert_float_eq::assert_float_absolute_eq;
556
557    use super::*;
558    #[test]
559    fn test_square() {
560        let square = Square::new(2.0).with(|data| {
561            data.shift(DVec3::NEG_Y)
562                .rotate(std::f64::consts::PI / 2.0, DVec3::X);
563        });
564        assert_float_absolute_eq!(square.center.distance_squared(DVec3::NEG_Y), 0.0, 1e-10);
565        assert_float_absolute_eq!(square.up.distance_squared(DVec3::Z), 0.0, 1e-10);
566        assert_float_absolute_eq!(square.normal.distance_squared(DVec3::NEG_Y), 0.0, 1e-10);
567        let square = Square::new(2.0).with(|data| {
568            data.shift(DVec3::X)
569                .rotate(std::f64::consts::PI / 2.0, DVec3::Y);
570        });
571        assert_float_absolute_eq!(square.center.distance_squared(DVec3::X), 0.0, 1e-10);
572        assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
573        assert_float_absolute_eq!(square.normal.distance_squared(DVec3::X), 0.0, 1e-10);
574        let square = Square::new(2.0).with(|data| {
575            data.shift(DVec3::NEG_Z);
576        });
577        assert_float_absolute_eq!(square.center.distance_squared(DVec3::NEG_Z), 0.0, 1e-10);
578        assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
579        assert_float_absolute_eq!(square.normal.distance_squared(DVec3::Z), 0.0, 1e-10);
580        let square = Square::new(2.0).with(|data| {
581            data.shift(DVec3::Y)
582                .rotate(-std::f64::consts::PI / 2.0, DVec3::X);
583        });
584        assert_float_absolute_eq!(square.center.distance_squared(DVec3::Y), 0.0, 1e-10);
585        assert_float_absolute_eq!(square.up.distance_squared(DVec3::NEG_Z), 0.0, 1e-10);
586        assert_float_absolute_eq!(square.normal.distance_squared(DVec3::Y), 0.0, 1e-10);
587        let square = Square::new(2.0).with(|data| {
588            data.shift(DVec3::Z);
589        });
590        assert_float_absolute_eq!(square.center.distance_squared(DVec3::Z), 0.0, 1e-10);
591        assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
592        assert_float_absolute_eq!(square.normal.distance_squared(DVec3::Z), 0.0, 1e-10);
593        let square = Square::new(2.0).with(|data| {
594            data.shift(DVec3::NEG_X)
595                .rotate(-std::f64::consts::PI / 2.0, DVec3::Y);
596        });
597        assert_float_absolute_eq!(square.center.distance_squared(DVec3::NEG_X), 0.0, 1e-10);
598        assert_float_absolute_eq!(square.up.distance_squared(DVec3::Y), 0.0, 1e-10);
599        assert_float_absolute_eq!(square.normal.distance_squared(DVec3::NEG_X), 0.0, 1e-10);
600    }
601}