svgbob/buffer/fragment_buffer/fragment/
rect.rs

1use crate::{fragment::Bounds, util, Cell, Point};
2use parry2d::shape::{ConvexPolygon, Polyline, Segment, Shape};
3use sauron::{
4    html::attributes::{class, classes_flag},
5    svg::{attributes::*, *},
6    Node,
7};
8use std::{cmp::Ordering, fmt};
9
10#[derive(Debug, Clone)]
11pub struct Rect {
12    pub start: Point,
13    pub end: Point,
14    pub is_filled: bool,
15    pub radius: Option<f32>,
16    //TODO:Make this as enum
17    pub is_broken: bool,
18}
19
20impl Rect {
21    /// creates a new rect and reorder the points swapping the end points if necessary
22    /// such that the start is the most top-left and end point is the most bottom-right
23    pub(crate) fn new(
24        start: Point,
25        end: Point,
26        is_filled: bool,
27        is_broken: bool,
28    ) -> Self {
29        let mut rect = Rect {
30            start,
31            end,
32            is_filled,
33            radius: None,
34            is_broken,
35        };
36        rect.sort_reorder_end_points();
37        rect
38    }
39
40    pub(crate) fn rounded_new(
41        start: Point,
42        end: Point,
43        is_filled: bool,
44        radius: f32,
45        is_broken: bool,
46    ) -> Self {
47        let mut rect = Rect {
48            start,
49            end,
50            is_filled,
51            radius: Some(radius),
52            is_broken,
53        };
54        rect.sort_reorder_end_points();
55        rect
56    }
57
58    /// reorder the end points swap end points such that
59    /// start < end
60    pub(crate) fn sort_reorder_end_points(&mut self) {
61        if self.start > self.end {
62            std::mem::swap(&mut self.start, &mut self.end);
63        }
64    }
65
66    /// recompute the rect with start and end point offset by the cell
67    /// location
68    pub(crate) fn absolute_position(&self, cell: Cell) -> Self {
69        Rect {
70            start: cell.absolute_position(self.start),
71            end: cell.absolute_position(self.end),
72            ..*self
73        }
74    }
75
76    pub(crate) fn scale(&self, scale: f32) -> Self {
77        Rect {
78            start: self.start.scale(scale),
79            end: self.end.scale(scale),
80            radius: self.radius.map(|r| r * scale),
81            ..*self
82        }
83    }
84
85    pub(crate) fn width(&self) -> f32 {
86        self.end.x - self.start.x
87    }
88
89    pub(crate) fn height(&self) -> f32 {
90        self.end.y - self.start.y
91    }
92
93    pub(crate) fn is_broken(&self) -> bool {
94        self.is_broken
95    }
96
97    pub fn is_rounded(&self) -> bool {
98        if let Some(ref r) = &self.radius {
99            *r > 0.0
100        } else {
101            false
102        }
103    }
104}
105
106impl Bounds for Rect {
107    fn bounds(&self) -> (Point, Point) {
108        let aabb = Segment::new(*self.start, *self.end).local_aabb();
109        (Point::from(*aabb.mins), Point::from(*aabb.maxs))
110    }
111}
112
113impl fmt::Display for Rect {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        write!(f, "R {} {}", self.start, self.end)
116    }
117}
118
119impl From<Rect> for Polyline {
120    fn from(rect: Rect) -> Polyline {
121        Polyline::new(
122            vec![
123                *rect.start,
124                *Point::new(rect.end.x, rect.start.y),
125                *rect.end,
126                *Point::new(rect.start.x, rect.end.y),
127                *rect.start,
128            ],
129            None,
130        )
131    }
132}
133
134impl From<Rect> for ConvexPolygon {
135    fn from(rect: Rect) -> ConvexPolygon {
136        ConvexPolygon::from_convex_polyline(vec![
137            *rect.start,
138            *Point::new(rect.end.x, rect.start.y),
139            *rect.end,
140            *Point::new(rect.start.x, rect.end.y),
141        ])
142        .expect("must create a convex polygon")
143    }
144}
145
146impl<MSG> From<Rect> for Node<MSG> {
147    fn from(r: Rect) -> Node<MSG> {
148        rect(
149            [
150                x(r.start.x),
151                y(r.start.y),
152                width(r.width()),
153                height(r.height()),
154                classes_flag([
155                    ("broken", r.is_broken),
156                    ("solid", !r.is_broken),
157                    ("filled", r.is_filled),
158                    ("nofill", !r.is_filled),
159                ]),
160                if let Some(radius) = r.radius {
161                    rx(radius)
162                } else {
163                    rx(0)
164                },
165            ],
166            [],
167        )
168    }
169}
170
171impl Eq for Rect {}
172
173impl Ord for Rect {
174    fn cmp(&self, other: &Self) -> Ordering {
175        self.start
176            .cmp(&other.start)
177            .then(self.end.cmp(&other.end))
178            .then(self.is_filled.cmp(&other.is_filled))
179            .then(util::opt_ord(self.radius, other.radius))
180            .then(self.is_broken.cmp(&other.is_broken))
181    }
182}
183
184impl PartialOrd for Rect {
185    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
186        Some(self.cmp(other))
187    }
188}
189
190impl PartialEq for Rect {
191    fn eq(&self, other: &Self) -> bool {
192        self.cmp(other) == Ordering::Equal
193    }
194}