svgbob/buffer/fragment_buffer/fragment/
rect.rs1use 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 pub is_broken: bool,
18}
19
20impl Rect {
21 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 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 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}