1#[derive(Clone, Copy, Debug, PartialEq)]
5pub struct Constraints {
6 pub min_width: f32,
7 pub max_width: f32,
8 pub min_height: f32,
9 pub max_height: f32,
10}
11
12impl Constraints {
13 pub fn tight(width: f32, height: f32) -> Self {
15 Self {
16 min_width: width,
17 max_width: width,
18 min_height: height,
19 max_height: height,
20 }
21 }
22
23 pub fn loose(max_width: f32, max_height: f32) -> Self {
25 Self {
26 min_width: 0.0,
27 max_width,
28 min_height: 0.0,
29 max_height,
30 }
31 }
32
33 pub fn is_tight(&self) -> bool {
35 self.min_width == self.max_width && self.min_height == self.max_height
36 }
37
38 pub fn is_bounded(&self) -> bool {
40 self.max_width.is_finite() && self.max_height.is_finite()
41 }
42
43 pub fn constrain(&self, width: f32, height: f32) -> (f32, f32) {
45 (
46 width.clamp(self.min_width, self.max_width),
47 height.clamp(self.min_height, self.max_height),
48 )
49 }
50
51 #[inline]
53 pub fn has_bounded_width(&self) -> bool {
54 self.max_width.is_finite()
55 }
56
57 #[inline]
59 pub fn has_bounded_height(&self) -> bool {
60 self.max_height.is_finite()
61 }
62
63 #[inline]
65 pub fn has_tight_width(&self) -> bool {
66 self.min_width == self.max_width
67 }
68
69 #[inline]
71 pub fn has_tight_height(&self) -> bool {
72 self.min_height == self.max_height
73 }
74
75 pub fn tighten_width(self, width: f32) -> Self {
77 Self {
78 min_width: width,
79 max_width: width,
80 ..self
81 }
82 }
83
84 pub fn tighten_height(self, height: f32) -> Self {
86 Self {
87 min_height: height,
88 max_height: height,
89 ..self
90 }
91 }
92
93 pub fn copy_with_width(self, min_width: f32, max_width: f32) -> Self {
95 Self {
96 min_width,
97 max_width,
98 ..self
99 }
100 }
101
102 pub fn copy_with_height(self, min_height: f32, max_height: f32) -> Self {
104 Self {
105 min_height,
106 max_height,
107 ..self
108 }
109 }
110
111 pub fn deflate(self, horizontal: f32, vertical: f32) -> Self {
114 Self {
115 min_width: (self.min_width - horizontal).max(0.0),
116 max_width: (self.max_width - horizontal).max(0.0),
117 min_height: (self.min_height - vertical).max(0.0),
118 max_height: (self.max_height - vertical).max(0.0),
119 }
120 }
121
122 pub fn loosen(self) -> Self {
124 Self {
125 min_width: 0.0,
126 min_height: 0.0,
127 ..self
128 }
129 }
130
131 pub fn enforce(self, width: f32, height: f32) -> Self {
133 Self {
134 min_width: width.clamp(self.min_width, self.max_width),
135 max_width: width.clamp(self.min_width, self.max_width),
136 min_height: height.clamp(self.min_height, self.max_height),
137 max_height: height.clamp(self.min_height, self.max_height),
138 }
139 }
140}
141
142#[cfg(test)]
143#[path = "tests/constraints_tests.rs"]
144mod tests;
145
146#[derive(Clone, Copy, Debug, PartialEq)]
149pub struct BoundRange {
150 pub low: f32,
151 pub high: f32,
152}
153
154impl BoundRange {
155 pub const ANY: Self = Self {
157 low: f32::NEG_INFINITY,
158 high: f32::INFINITY,
159 };
160
161 pub const fn exactly(value: f32) -> Self {
163 Self {
164 low: value,
165 high: value,
166 }
167 }
168
169 pub const fn up_to(value: f32) -> Self {
171 Self {
172 low: f32::NEG_INFINITY,
173 high: value,
174 }
175 }
176
177 pub const fn from(value: f32) -> Self {
179 Self {
180 low: value,
181 high: f32::INFINITY,
182 }
183 }
184
185 pub fn contains(self, value: f32) -> bool {
186 self.low <= value && value <= self.high
187 }
188
189 pub fn intersect(self, other: Self) -> Option<Self> {
191 let range = Self {
192 low: self.low.max(other.low),
193 high: self.high.min(other.high),
194 };
195 (range.low <= range.high).then_some(range)
196 }
197
198 pub fn outset(self, inset: f32) -> Self {
202 Self {
203 low: if self.low <= 0.0 {
204 f32::NEG_INFINITY
205 } else {
206 self.low + inset
207 },
208 high: self.high + inset,
209 }
210 }
211}
212
213#[derive(Clone, Copy, Debug, PartialEq)]
215pub struct AxisHold {
216 pub min: BoundRange,
217 pub max: BoundRange,
218}
219
220impl AxisHold {
221 pub const fn sized(size: f32) -> Self {
224 Self {
225 min: BoundRange::up_to(size),
226 max: BoundRange::from(size),
227 }
228 }
229
230 pub const fn at_min(size: f32) -> Self {
233 Self {
234 min: BoundRange::exactly(size),
235 max: BoundRange::from(size),
236 }
237 }
238
239 pub fn contains(self, min: f32, max: f32) -> bool {
240 self.min.contains(min) && self.max.contains(max)
241 }
242
243 pub fn intersect(self, other: Self) -> Option<Self> {
245 Some(Self {
246 min: self.min.intersect(other.min)?,
247 max: self.max.intersect(other.max)?,
248 })
249 }
250
251 pub fn outset(self, inset: f32) -> Self {
254 Self {
255 min: self.min.outset(inset),
256 max: self.max.outset(inset),
257 }
258 }
259}
260
261#[derive(Clone, Copy, Debug, PartialEq)]
267pub struct ConstraintsHold {
268 pub width: AxisHold,
269 pub height: AxisHold,
270}
271
272impl ConstraintsHold {
273 pub const fn sized(width: f32, height: f32) -> Self {
275 Self {
276 width: AxisHold::sized(width),
277 height: AxisHold::sized(height),
278 }
279 }
280
281 pub const fn at_min(width: f32, height: f32) -> Self {
284 Self {
285 width: AxisHold::at_min(width),
286 height: AxisHold::at_min(height),
287 }
288 }
289
290 pub fn contains(&self, constraints: Constraints) -> bool {
291 self.width
292 .contains(constraints.min_width, constraints.max_width)
293 && self
294 .height
295 .contains(constraints.min_height, constraints.max_height)
296 }
297
298 pub fn intersect(self, other: Self) -> Option<Self> {
300 Some(Self {
301 width: self.width.intersect(other.width)?,
302 height: self.height.intersect(other.height)?,
303 })
304 }
305}
306
307#[derive(Clone, Copy, Debug, PartialEq)]
311pub struct WrappedHold {
312 pub size: cranpose_ui_graphics::Size,
313 pub hold: Option<ConstraintsHold>,
314}