1#[derive(Clone, Copy, Debug, PartialEq)]
5pub struct Constraints {
6 pub min_width: f32,
8 pub max_width: f32,
10 pub min_height: f32,
12 pub max_height: f32,
14}
15
16impl Constraints {
17 pub fn tight(width: f32, height: f32) -> Self {
19 Self {
20 min_width: width,
21 max_width: width,
22 min_height: height,
23 max_height: height,
24 }
25 }
26
27 pub fn loose(max_width: f32, max_height: f32) -> Self {
29 Self {
30 min_width: 0.0,
31 max_width,
32 min_height: 0.0,
33 max_height,
34 }
35 }
36
37 pub fn is_tight(&self) -> bool {
39 self.min_width == self.max_width && self.min_height == self.max_height
40 }
41
42 pub fn is_bounded(&self) -> bool {
44 self.max_width.is_finite() && self.max_height.is_finite()
45 }
46
47 pub fn constrain(&self, width: f32, height: f32) -> (f32, f32) {
49 (
50 width.clamp(self.min_width, self.max_width),
51 height.clamp(self.min_height, self.max_height),
52 )
53 }
54
55 #[inline]
57 pub fn has_bounded_width(&self) -> bool {
58 self.max_width.is_finite()
59 }
60
61 #[inline]
63 pub fn has_bounded_height(&self) -> bool {
64 self.max_height.is_finite()
65 }
66
67 #[inline]
69 pub fn has_tight_width(&self) -> bool {
70 self.min_width == self.max_width
71 }
72
73 #[inline]
75 pub fn has_tight_height(&self) -> bool {
76 self.min_height == self.max_height
77 }
78
79 pub fn tighten_width(self, width: f32) -> Self {
81 Self {
82 min_width: width,
83 max_width: width,
84 ..self
85 }
86 }
87
88 pub fn tighten_height(self, height: f32) -> Self {
90 Self {
91 min_height: height,
92 max_height: height,
93 ..self
94 }
95 }
96
97 pub fn copy_with_width(self, min_width: f32, max_width: f32) -> Self {
99 Self {
100 min_width,
101 max_width,
102 ..self
103 }
104 }
105
106 pub fn copy_with_height(self, min_height: f32, max_height: f32) -> Self {
108 Self {
109 min_height,
110 max_height,
111 ..self
112 }
113 }
114
115 pub fn deflate(self, horizontal: f32, vertical: f32) -> Self {
118 Self {
119 min_width: (self.min_width - horizontal).max(0.0),
120 max_width: (self.max_width - horizontal).max(0.0),
121 min_height: (self.min_height - vertical).max(0.0),
122 max_height: (self.max_height - vertical).max(0.0),
123 }
124 }
125
126 pub fn loosen(self) -> Self {
128 Self {
129 min_width: 0.0,
130 min_height: 0.0,
131 ..self
132 }
133 }
134
135 pub fn enforce(self, width: f32, height: f32) -> Self {
137 Self {
138 min_width: width.clamp(self.min_width, self.max_width),
139 max_width: width.clamp(self.min_width, self.max_width),
140 min_height: height.clamp(self.min_height, self.max_height),
141 max_height: height.clamp(self.min_height, self.max_height),
142 }
143 }
144}
145
146#[cfg(test)]
147#[path = "tests/constraints_tests.rs"]
148mod tests;
149
150#[derive(Clone, Copy, Debug, PartialEq)]
153pub struct BoundRange {
154 pub low: f32,
156 pub high: f32,
158}
159
160impl BoundRange {
161 pub const ANY: Self = Self {
163 low: f32::NEG_INFINITY,
164 high: f32::INFINITY,
165 };
166
167 pub const fn exactly(value: f32) -> Self {
169 Self {
170 low: value,
171 high: value,
172 }
173 }
174
175 pub const fn up_to(value: f32) -> Self {
177 Self {
178 low: f32::NEG_INFINITY,
179 high: value,
180 }
181 }
182
183 pub const fn from(value: f32) -> Self {
185 Self {
186 low: value,
187 high: f32::INFINITY,
188 }
189 }
190
191 pub fn contains(self, value: f32) -> bool {
193 self.low <= value && value <= self.high
194 }
195
196 pub fn intersect(self, other: Self) -> Option<Self> {
198 let range = Self {
199 low: self.low.max(other.low),
200 high: self.high.min(other.high),
201 };
202 (range.low <= range.high).then_some(range)
203 }
204
205 pub fn outset(self, inset: f32) -> Self {
209 Self {
210 low: if self.low <= 0.0 {
211 f32::NEG_INFINITY
212 } else {
213 self.low + inset
214 },
215 high: self.high + inset,
216 }
217 }
218}
219
220#[derive(Clone, Copy, Debug, PartialEq)]
222pub struct AxisHold {
223 pub min: BoundRange,
225 pub max: BoundRange,
227}
228
229impl AxisHold {
230 pub const fn sized(size: f32) -> Self {
233 Self {
234 min: BoundRange::up_to(size),
235 max: BoundRange::from(size),
236 }
237 }
238
239 pub const fn at_min(size: f32) -> Self {
242 Self {
243 min: BoundRange::exactly(size),
244 max: BoundRange::from(size),
245 }
246 }
247
248 pub fn contains(self, min: f32, max: f32) -> bool {
250 self.min.contains(min) && self.max.contains(max)
251 }
252
253 pub fn intersect(self, other: Self) -> Option<Self> {
255 Some(Self {
256 min: self.min.intersect(other.min)?,
257 max: self.max.intersect(other.max)?,
258 })
259 }
260
261 pub fn outset(self, inset: f32) -> Self {
264 Self {
265 min: self.min.outset(inset),
266 max: self.max.outset(inset),
267 }
268 }
269}
270
271#[derive(Clone, Copy, Debug, PartialEq)]
277pub struct ConstraintsHold {
278 pub width: AxisHold,
280 pub height: AxisHold,
282}
283
284impl ConstraintsHold {
285 pub const fn sized(width: f32, height: f32) -> Self {
287 Self {
288 width: AxisHold::sized(width),
289 height: AxisHold::sized(height),
290 }
291 }
292
293 pub const fn at_min(width: f32, height: f32) -> Self {
296 Self {
297 width: AxisHold::at_min(width),
298 height: AxisHold::at_min(height),
299 }
300 }
301
302 pub fn contains(&self, constraints: Constraints) -> bool {
304 self.width
305 .contains(constraints.min_width, constraints.max_width)
306 && self
307 .height
308 .contains(constraints.min_height, constraints.max_height)
309 }
310
311 pub fn intersect(self, other: Self) -> Option<Self> {
313 Some(Self {
314 width: self.width.intersect(other.width)?,
315 height: self.height.intersect(other.height)?,
316 })
317 }
318}
319
320#[derive(Clone, Copy, Debug, PartialEq)]
324pub struct WrappedHold {
325 pub size: cranpose_ui_graphics::Size,
327 pub hold: Option<ConstraintsHold>,
329}