microcad_core/geo2d/
bounds.rs1use derive_more::Deref;
7use geo::coord;
8
9use crate::*;
10
11pub type Bounds2D = Bounds<Vec2>;
13
14impl Bounds2D {
15 pub fn is_valid(&self) -> bool {
17 self.min.x <= self.max.x && self.min.y <= self.max.y
18 }
19
20 pub fn width(&self) -> Scalar {
22 (self.max.x - self.min.x).max(0.0)
23 }
24
25 pub fn height(&self) -> Scalar {
27 (self.max.y - self.min.y).max(0.0)
28 }
29
30 pub fn max_extent(&self) -> Scalar {
32 self.width().max(self.height())
33 }
34
35 pub fn rect(&self) -> Option<Rect> {
37 if self.is_valid() {
38 Some(Rect::new(
39 coord! {x: self.min.x, y: self.min.y },
40 coord! {x: self.max.x, y: self.max.y },
41 ))
42 } else {
43 None
44 }
45 }
46
47 pub fn enlarge(&self, factor: Scalar) -> Self {
49 match self.rect() {
50 Some(rect) => {
51 let c = rect.center();
52 let s: geo::Coord = (rect.width(), rect.height()).into();
53 let s = s * 0.5 * (1.0 + factor);
54 Rect::new(c - s, c + s).into()
55 }
56 None => Bounds2D::default(),
57 }
58 }
59
60 pub fn extend(mut self, other: Bounds2D) -> Self {
62 self.extend_by_point(other.min);
63 self.extend_by_point(other.max);
64 self
65 }
66
67 pub fn extend_by_point(&mut self, p: Vec2) {
69 self.min.x = p.x.min(self.min.x);
70 self.min.y = p.y.min(self.min.y);
71 self.max.x = p.x.max(self.max.x);
72 self.max.y = p.y.max(self.max.y);
73 }
74}
75
76impl Default for Bounds2D {
77 fn default() -> Self {
78 let min = Scalar::MAX;
80 let max = Scalar::MIN;
81 Self::new((min, min).into(), (max, max).into())
82 }
83}
84
85impl From<Rect> for Bounds2D {
86 fn from(rect: Rect) -> Self {
87 Self::new(rect.min().x_y().into(), rect.max().x_y().into())
88 }
89}
90
91impl From<Option<Rect>> for Bounds2D {
92 fn from(rect: Option<Rect>) -> Self {
93 match rect {
94 Some(rect) => rect.into(),
95 None => Bounds2D::default(),
96 }
97 }
98}
99
100impl From<Size2> for Bounds2D {
101 fn from(value: Size2) -> Self {
102 Self::new(Vec2::new(0.0, 0.0), Vec2::new(value.width, value.height))
103 }
104}
105
106impl std::fmt::Display for Bounds2D {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 match self.rect() {
109 Some(rect) => write!(
110 f,
111 "[{min:?}, {max:?}]",
112 min = rect.min().x_y(),
113 max = rect.max().x_y()
114 ),
115 None => write!(f, "[no bounds]"),
116 }
117 }
118}
119
120pub trait CalcBounds2D {
122 fn calc_bounds_2d(&self) -> Bounds2D;
124}
125
126#[derive(Clone, Default, Debug, Deref)]
128pub struct WithBounds2D<T: CalcBounds2D + Transformed2D> {
129 pub bounds: Bounds2D,
131 #[deref]
133 pub inner: T,
134}
135
136impl<T: CalcBounds2D + Transformed2D> WithBounds2D<T> {
137 pub fn new(inner: T) -> Self {
139 Self {
140 bounds: inner.calc_bounds_2d(),
141 inner,
142 }
143 }
144
145 pub fn update_bounds(&mut self) {
147 self.bounds = self.inner.calc_bounds_2d()
148 }
149}
150
151impl<T: CalcBounds2D + Transformed2D> Transformed2D for WithBounds2D<T> {
152 fn transformed_2d(&self, mat: &Mat3) -> Self {
153 let inner = self.inner.transformed_2d(mat);
154 let bounds = inner.calc_bounds_2d();
155 Self { inner, bounds }
156 }
157}
158
159impl From<Geometry2D> for WithBounds2D<Geometry2D> {
160 fn from(geo: Geometry2D) -> Self {
161 Self::new(geo)
162 }
163}
164
165#[test]
166fn bounds_2d_test() {
167 let bounds1 = Bounds2D::new(Vec2::new(0.0, 1.0), Vec2::new(2.0, 3.0));
168 let bounds2 = Bounds2D::new(Vec2::new(4.0, 5.0), Vec2::new(6.0, 7.0));
169
170 let bounds1 = bounds1.extend(bounds2);
171
172 assert_eq!(bounds1.min, Vec2::new(0.0, 1.0));
173 assert_eq!(bounds1.max, Vec2::new(6.0, 7.0));
174}