ranim_items/vitem/geometry/
polygon.rs

1use std::f64::consts::{PI, TAU};
2
3use ranim_core::{
4    Extract,
5    anchor::{Aabb, AabbPoint, Locate},
6    color,
7    core_item::CoreItem,
8    glam::{DVec2, DVec3, dvec2, dvec3},
9    traits::{Discard, RotateTransform, ScaleTransform, ShiftTransform, ShiftTransformExt},
10};
11
12use color::{AlphaColor, Srgb};
13use itertools::Itertools;
14
15use crate::vitem::{DEFAULT_STROKE_WIDTH, VItem, geometry::Circle};
16use ranim_core::traits::{Alignable, FillColor, Opacity, StrokeColor, StrokeWidth, With};
17
18// MARK: ### Square ###
19/// A Square
20#[derive(Clone, Debug, ranim_macros::Interpolatable)]
21pub struct Square {
22    /// Axes
23    pub axes: (DVec3, DVec3),
24    /// Center
25    pub center: DVec3,
26    /// Size
27    pub size: f64,
28
29    /// Stroke rgba
30    pub stroke_rgba: AlphaColor<Srgb>,
31    /// Stroke width
32    pub stroke_width: f32,
33    /// Fill rgba
34    pub fill_rgba: AlphaColor<Srgb>,
35}
36
37impl Square {
38    /// Constructor
39    pub fn new(size: f64) -> Self {
40        Self {
41            axes: (DVec3::X, DVec3::Y),
42            center: dvec3(0.0, 0.0, 0.0),
43            size,
44
45            stroke_rgba: AlphaColor::WHITE,
46            stroke_width: DEFAULT_STROKE_WIDTH,
47            fill_rgba: AlphaColor::TRANSPARENT,
48        }
49    }
50    /// Scale the square by the given scale, with the given anchor as the center.
51    ///
52    /// Note that this accepts a `f64` scale dispite of [`ScaleTransform`]'s `DVec3`,
53    /// because this keeps the square a square.
54    pub fn scale(&mut self, scale: f64) -> &mut Self {
55        self.scale_at(scale, AabbPoint::CENTER)
56    }
57    /// Scale the square by the given scale, with the given anchor as the center.
58    ///
59    /// Note that this accepts a `f64` scale dispite of [`ScaleTransform`]'s `DVec3`,
60    /// because this keeps the square a square.
61    pub fn scale_at<T>(&mut self, scale: f64, anchor: T) -> &mut Self
62    where
63        T: Locate<Self>,
64    {
65        let anchor = anchor.locate(self);
66        self.size *= scale;
67        self.center
68            .shift(-anchor)
69            .scale(DVec3::splat(scale))
70            .shift(anchor);
71        self
72    }
73}
74
75// MARK: Traits impl
76impl Aabb for Square {
77    fn aabb(&self) -> [DVec3; 2] {
78        let (u, v) = (self.axes.0.normalize(), self.axes.1.normalize());
79        [
80            self.center + self.size / 2.0 * (u + v),
81            self.center - self.size / 2.0 * (u + v),
82        ]
83        .aabb()
84    }
85}
86
87impl ShiftTransform for Square {
88    fn shift(&mut self, shift: DVec3) -> &mut Self {
89        self.center.shift(shift);
90        self
91    }
92}
93
94impl RotateTransform for Square {
95    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
96        self.center.rotate_on_axis(axis, angle);
97        self.axes.0.rotate_on_axis(axis, angle);
98        self.axes.0 = self.axes.0.normalize();
99        self.axes.1.rotate_on_axis(axis, angle);
100        self.axes.1 = self.axes.1.normalize();
101        self
102    }
103}
104
105impl Alignable for Square {
106    fn is_aligned(&self, _other: &Self) -> bool {
107        true
108    }
109    fn align_with(&mut self, _other: &mut Self) {}
110}
111
112impl Opacity for Square {
113    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
114        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
115        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
116        self
117    }
118}
119
120impl StrokeColor for Square {
121    fn stroke_color(&self) -> AlphaColor<Srgb> {
122        self.stroke_rgba
123    }
124    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
125        self.stroke_rgba = color;
126        self
127    }
128    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
129        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
130        self
131    }
132}
133
134impl FillColor for Square {
135    fn fill_color(&self) -> AlphaColor<Srgb> {
136        self.fill_rgba
137    }
138    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
139        self.fill_rgba = color;
140        self
141    }
142    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
143        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
144        self
145    }
146}
147
148impl Extract for Square {
149    type Target = CoreItem;
150    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
151        VItem::from(self.clone()).extract_into(buf);
152    }
153}
154
155// MARK: Conversions
156impl From<Square> for Rectangle {
157    fn from(value: Square) -> Self {
158        let Square {
159            axes,
160            center,
161            size: width,
162            stroke_rgba,
163            stroke_width,
164            fill_rgba,
165        } = value;
166        let (u, v) = (axes.0.normalize(), axes.1.normalize());
167        let p0 = center - width / 2.0 * u - width / 2.0 * v;
168        Rectangle {
169            axes,
170            p0,
171            size: dvec2(width, width),
172            stroke_rgba,
173            stroke_width,
174            fill_rgba,
175        }
176    }
177}
178
179impl From<Square> for RegularPolygon {
180    fn from(value: Square) -> Self {
181        RegularPolygon::new(4, value.size / 2.0 * 2.0f64.sqrt()).with(|x| {
182            x.axes = value.axes;
183            x.stroke_rgba = value.stroke_rgba;
184            x.stroke_width = value.stroke_width;
185            x.fill_rgba = value.fill_rgba;
186        })
187    }
188}
189
190impl From<Square> for Polygon {
191    fn from(value: Square) -> Self {
192        Rectangle::from(value).into()
193    }
194}
195
196impl From<Square> for VItem {
197    fn from(value: Square) -> Self {
198        Rectangle::from(value).into()
199    }
200}
201
202// MARK: ### Rectangle ###
203/// Rectangle
204#[derive(Clone, Debug, ranim_macros::Interpolatable)]
205pub struct Rectangle {
206    /// Axes info
207    pub axes: (DVec3, DVec3),
208    /// Bottom left corner (minimum)
209    pub p0: DVec3,
210    /// Width and height
211    pub size: DVec2,
212
213    /// Stroke rgba
214    pub stroke_rgba: AlphaColor<Srgb>,
215    /// Stroke width
216    pub stroke_width: f32,
217    /// Fill rgba
218    pub fill_rgba: AlphaColor<Srgb>,
219}
220
221impl Rectangle {
222    /// Constructor
223    pub fn new(width: f64, height: f64) -> Self {
224        let half_width = width / 2.0;
225        let half_height = height / 2.0;
226        let p0 = dvec3(-half_width, -half_height, 0.0);
227        let size = dvec2(width, height);
228        Self::from_min_size(p0, size)
229    }
230    /// Construct a rectangle from the bottom-left point (minimum) and size.
231    pub fn from_min_size(p0: DVec3, size: DVec2) -> Self {
232        Self {
233            axes: (DVec3::X, DVec3::Y),
234            p0,
235            size,
236            stroke_rgba: AlphaColor::WHITE,
237            stroke_width: DEFAULT_STROKE_WIDTH,
238            fill_rgba: AlphaColor::TRANSPARENT,
239        }
240    }
241    /// Width
242    pub fn width(&self) -> f64 {
243        self.size.x.abs()
244    }
245    /// Height
246    pub fn height(&self) -> f64 {
247        self.size.y.abs()
248    }
249}
250
251// MARK: Traits impl
252impl Aabb for Rectangle {
253    fn aabb(&self) -> [DVec3; 2] {
254        let (u, v) = (self.axes.0.normalize(), self.axes.1.normalize());
255        let p1 = self.p0;
256        let p2 = self.p0 + self.size.x * u + self.size.y * v;
257        [p1, p2].aabb()
258    }
259}
260
261impl ShiftTransform for Rectangle {
262    fn shift(&mut self, shift: DVec3) -> &mut Self {
263        self.p0.shift(shift);
264        self
265    }
266}
267
268impl RotateTransform for Rectangle {
269    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
270        self.p0.rotate_on_axis(axis, angle);
271        self.axes.0.rotate_on_axis(axis, angle);
272        self.axes.0 = self.axes.0.normalize();
273        self.axes.1.rotate_on_axis(axis, angle);
274        self.axes.1 = self.axes.1.normalize();
275        self
276    }
277}
278
279impl ScaleTransform for Rectangle {
280    fn scale(&mut self, scale: DVec3) -> &mut Self {
281        self.p0.scale(scale);
282        let (u, v) = (self.axes.0.normalize(), self.axes.1.normalize());
283        let scale_u = scale.dot(u);
284        let scale_v = scale.dot(v);
285        self.size *= dvec2(scale_u, scale_v);
286        self
287    }
288}
289
290impl Opacity for Rectangle {
291    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
292        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
293        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
294        self
295    }
296}
297
298impl Alignable for Rectangle {
299    fn align_with(&mut self, _other: &mut Self) {}
300    fn is_aligned(&self, _other: &Self) -> bool {
301        true
302    }
303}
304
305impl StrokeColor for Rectangle {
306    fn stroke_color(&self) -> AlphaColor<Srgb> {
307        self.stroke_rgba
308    }
309    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
310        self.stroke_rgba = color;
311        self
312    }
313    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
314        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
315        self
316    }
317}
318
319impl FillColor for Rectangle {
320    fn fill_color(&self) -> AlphaColor<Srgb> {
321        self.fill_rgba
322    }
323    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
324        self.fill_rgba = color;
325        self
326    }
327    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
328        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
329        self
330    }
331}
332
333// MARK: Conversions
334impl From<Rectangle> for Polygon {
335    fn from(value: Rectangle) -> Self {
336        let p0 = value.p0;
337        let (u, v) = (value.axes.0.normalize(), value.axes.1.normalize());
338        let DVec2 { x: w, y: h } = value.size;
339        let points = vec![p0, p0 + u * w, p0 + u * w + v * h, p0 + v * h];
340        Polygon {
341            axes: value.axes,
342            points,
343            stroke_rgba: value.stroke_rgba,
344            stroke_width: value.stroke_width,
345            fill_rgba: value.fill_rgba,
346        }
347    }
348}
349
350impl From<Rectangle> for VItem {
351    fn from(value: Rectangle) -> Self {
352        Polygon::from(value).into()
353    }
354}
355
356impl Extract for Rectangle {
357    type Target = CoreItem;
358    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
359        VItem::from(self.clone()).extract_into(buf);
360    }
361}
362
363// MARK: ### Polygon ###
364/// A Polygon with uniform stroke and fill
365#[derive(Clone, Debug, ranim_macros::Interpolatable)]
366pub struct Polygon {
367    /// Axes info
368    pub axes: (DVec3, DVec3),
369    /// Corner points
370    pub points: Vec<DVec3>,
371    /// Stroke rgba
372    pub stroke_rgba: AlphaColor<Srgb>,
373    /// Stroke width
374    pub stroke_width: f32,
375    /// Fill rgba
376    pub fill_rgba: AlphaColor<Srgb>,
377}
378
379impl Polygon {
380    /// Constructor
381    pub fn new(points: Vec<DVec3>) -> Self {
382        Self {
383            axes: (DVec3::X, DVec3::Y),
384            points,
385            stroke_rgba: AlphaColor::WHITE,
386            stroke_width: DEFAULT_STROKE_WIDTH,
387            fill_rgba: AlphaColor::TRANSPARENT,
388        }
389    }
390}
391
392// MARK: Traits impl
393impl Aabb for Polygon {
394    fn aabb(&self) -> [DVec3; 2] {
395        self.points.aabb()
396    }
397}
398
399impl ShiftTransform for Polygon {
400    fn shift(&mut self, shift: DVec3) -> &mut Self {
401        self.points.shift(shift);
402        self
403    }
404}
405
406impl RotateTransform for Polygon {
407    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
408        self.points.rotate_on_axis(axis, angle);
409        self.axes.0.rotate_on_axis(axis, angle);
410        self.axes.0 = self.axes.0.normalize();
411        self.axes.1.rotate_on_axis(axis, angle);
412        self.axes.1 = self.axes.1.normalize();
413        self
414    }
415}
416
417impl ScaleTransform for Polygon {
418    fn scale(&mut self, scale: DVec3) -> &mut Self {
419        self.points.scale(scale);
420        self
421    }
422}
423
424// impl AffineTransform for Polygon {
425//     fn affine_transform_at_point(&mut self, mat: DAffine3, origin: DVec3) -> &mut Self {
426//         self.points.affine_transform_at_point(mat, origin);
427//         // TODO: how to transform axes?
428//         self
429//     }
430// }
431
432impl Alignable for Polygon {
433    fn is_aligned(&self, other: &Self) -> bool {
434        self.points.len() == other.points.len()
435    }
436    fn align_with(&mut self, other: &mut Self) {
437        if self.points.len() > other.points.len() {
438            return other.align_with(self);
439        }
440        // TODO: find a better algo to minimize the distance
441        self.points
442            .resize(other.points.len(), self.points.last().cloned().unwrap());
443    }
444}
445
446impl Opacity for Polygon {
447    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
448        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
449        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
450        self
451    }
452}
453
454impl StrokeColor for Polygon {
455    fn stroke_color(&self) -> AlphaColor<Srgb> {
456        self.stroke_rgba
457    }
458    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
459        self.stroke_rgba = color;
460        self
461    }
462    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
463        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
464        self
465    }
466}
467
468impl FillColor for Polygon {
469    fn fill_color(&self) -> AlphaColor<Srgb> {
470        self.fill_rgba
471    }
472    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
473        self.fill_rgba = color;
474        self
475    }
476    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
477        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
478        self
479    }
480}
481
482// MARK: Conversions
483impl From<Polygon> for VItem {
484    fn from(value: Polygon) -> Self {
485        let Polygon {
486            mut points,
487            stroke_rgba,
488            stroke_width,
489            fill_rgba,
490            axes,
491            ..
492        } = value;
493        assert!(points.len() > 2);
494
495        // Close the polygon
496        points.push(points[0]);
497
498        let anchors = points;
499        let handles = anchors
500            .iter()
501            .tuple_windows()
502            .map(|(&a, &b)| 0.5 * (a + b))
503            .collect::<Vec<_>>();
504
505        // Interleave anchors and handles
506        let vpoints = anchors.into_iter().interleave(handles).collect::<Vec<_>>();
507        VItem::from_vpoints(vpoints)
508            .with_normal(axes.0.cross(axes.1).normalize())
509            .with(|vitem| {
510                vitem
511                    .set_fill_color(fill_rgba)
512                    .set_stroke_color(stroke_rgba)
513                    .set_stroke_width(stroke_width);
514            })
515    }
516}
517
518impl Extract for Polygon {
519    type Target = CoreItem;
520    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
521        VItem::from(self.clone()).extract_into(buf);
522    }
523}
524
525#[derive(Debug, Clone, ranim_macros::Interpolatable)]
526/// A regular polygon.
527pub struct RegularPolygon {
528    /// Local coordinate system
529    pub axes: (DVec3, DVec3),
530    /// Center of the polygon
531    pub center: DVec3,
532    /// Number of sides
533    pub sides: usize,
534    /// Radius of the polygon (i.e. distance from center to a vertex)
535    pub radius: f64,
536    /// Stroke rgba
537    pub stroke_rgba: AlphaColor<Srgb>,
538    /// Stroke width
539    pub stroke_width: f32,
540    /// Fill rgba
541    pub fill_rgba: AlphaColor<Srgb>,
542}
543
544impl Alignable for RegularPolygon {
545    fn is_aligned(&self, _other: &Self) -> bool {
546        true
547    }
548    fn align_with(&mut self, _other: &mut Self) {}
549}
550
551impl RegularPolygon {
552    /// Creates a new regular polygon.
553    pub fn new(sides: usize, radius: f64) -> Self {
554        assert!(sides >= 3);
555        Self {
556            axes: (DVec3::X, DVec3::Y),
557            center: DVec3::ZERO,
558            sides,
559            radius,
560            stroke_rgba: AlphaColor::WHITE,
561            stroke_width: DEFAULT_STROKE_WIDTH,
562            fill_rgba: AlphaColor::TRANSPARENT,
563        }
564    }
565    /// Returns the vertices of the polygon.
566    pub fn points(&self) -> Vec<DVec3> {
567        let &Self {
568            sides,
569            radius,
570            center,
571            ..
572        } = self;
573        let u = self.axes.0.normalize();
574        let normal = self.axes.0.cross(self.axes.1).normalize();
575        (0..sides)
576            .map(|i| TAU * (i as f64 / sides as f64))
577            .map(|angle| u.rotate_axis(normal, angle) * radius + center)
578            .collect()
579    }
580    /// Returns the outer circle of the polygon.
581    pub fn outer_circle(&self) -> Circle {
582        Circle::new(self.radius).with(|x| x.move_to(self.center).discard())
583    }
584    /// Returns the inner circle of the polygon.
585    pub fn inner_circle(&self) -> Circle {
586        Circle::new(self.radius * (PI / self.sides as f64).cos())
587            .with(|x| x.move_to(self.center).discard())
588    }
589}
590
591impl Aabb for RegularPolygon {
592    fn aabb(&self) -> [DVec3; 2] {
593        self.points().aabb()
594    }
595}
596
597impl ShiftTransform for RegularPolygon {
598    fn shift(&mut self, offset: DVec3) -> &mut Self {
599        self.center.shift(offset);
600        self
601    }
602}
603
604impl RotateTransform for RegularPolygon {
605    fn rotate_on_axis(&mut self, axis: DVec3, angle: f64) -> &mut Self {
606        self.axes.0.rotate_on_axis(axis, angle);
607        self.axes.0 = self.axes.0.normalize();
608        self.axes.1.rotate_on_axis(axis, angle);
609        self.axes.1 = self.axes.1.normalize();
610        self.center.rotate_on_axis(axis, angle);
611        self
612    }
613}
614
615impl Opacity for RegularPolygon {
616    fn set_opacity(&mut self, opacity: f32) -> &mut Self {
617        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
618        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
619        self
620    }
621}
622
623impl FillColor for RegularPolygon {
624    fn fill_color(&self) -> AlphaColor<Srgb> {
625        self.fill_rgba
626    }
627
628    fn set_fill_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
629        self.fill_rgba = color;
630        self
631    }
632
633    fn set_fill_opacity(&mut self, opacity: f32) -> &mut Self {
634        self.fill_rgba = self.fill_rgba.with_alpha(opacity);
635        self
636    }
637}
638
639impl StrokeColor for RegularPolygon {
640    fn stroke_color(&self) -> AlphaColor<Srgb> {
641        self.stroke_rgba
642    }
643
644    fn set_stroke_opacity(&mut self, opacity: f32) -> &mut Self {
645        self.stroke_rgba = self.stroke_rgba.with_alpha(opacity);
646        self
647    }
648
649    fn set_stroke_color(&mut self, color: AlphaColor<Srgb>) -> &mut Self {
650        self.stroke_rgba = color;
651        self
652    }
653}
654
655impl From<RegularPolygon> for Polygon {
656    fn from(value: RegularPolygon) -> Self {
657        Polygon::new(value.points()).with(|x| {
658            x.axes = value.axes;
659            x.fill_rgba = value.fill_rgba;
660            x.stroke_rgba = value.stroke_rgba;
661            x.stroke_width = value.stroke_width;
662        })
663    }
664}
665
666impl Extract for RegularPolygon {
667    type Target = CoreItem;
668
669    fn extract_into(&self, buf: &mut Vec<Self::Target>) {
670        Polygon::from(self.clone()).extract_into(buf);
671    }
672}