Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_geom/src/rect.rs

13.2 KiB, 3 runs

created by r1870400018:335, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::dim::{
2 Coord,
3 Dim,
4 FlexDim,
5 Offset,
6 Span,
7};
8
9use oxedyne_fe2o3_core::prelude::*;
10
11
12#[derive(Clone, Copy, Debug)]
13pub enum RectSide {
14 Top,
15 Right,
16 Bottom,
17 Left,
18}
19
20/// Note that this iterator does not implement the `std::iter::Iterator` in order to avoid the
21/// `Option` result.
22#[derive(Clone, Debug)]
23pub struct RectSideIter {
24 state: RectSide,
25}
26
27impl RectSideIter {
28
29 pub fn new(state: RectSide) -> Self {
30 Self {
31 state,
32 }
33 }
34
35 pub fn next(&mut self) -> RectSide {
36 let current = self.state;
37 self.state = match current {
38 RectSide::Top => RectSide::Right,
39 RectSide::Right => RectSide::Bottom,
40 RectSide::Bottom => RectSide::Left,
41 RectSide::Left => RectSide::Top,
42 };
43 current
44 }
45}
46
47#[derive(Clone, Copy, Debug)]
48pub enum RelativePosition {
49 TopLeft,
50 TopMiddle,
51 TopRight,
52 RightMiddle,
53 BottomRight,
54 BottomMiddle,
55 BottomLeft,
56 LeftMiddle,
57 Centre,
58}
59
60impl RelativePosition {
61
62 /// Position the inner rectangle with respect to the outer, with a possible offset, and
63 /// clip the result.
64 pub fn relative_to(
65 &self,
66 outer: &AbsRect,
67 inner: &AbsSize,
68 delta: Option<&Offset>,
69 )
70 -> Option<AbsRect>
71 {
72 let (x1, y1, w1, h1) = outer.tup();
73 let (w2, h2) = inner.tup();
74 // Perform initial normalisation.
75 let w2 = if w2 > w1 { w1 } else { w2 };
76 let h2 = if h2 > h1 { h1 } else { h2 };
77 let (x2, y2) = match self {
78 Self::TopLeft => (x1, y1),
79 Self::TopMiddle => (x1 + w1/Dim(2) - w2/Dim(2), y1),
80 Self::TopRight => (x1 + w1 - w2, y1),
81 Self::RightMiddle => (x1 + w1 - w2, y1 + h1/Dim(2) - h2/Dim(2)),
82 Self::BottomRight => (x1 + w1 - w2, y1 + h1 - h2),
83 Self::BottomMiddle => (x1 + w1/Dim(2) - w2/Dim(2), y1 + h1 - h2),
84 Self::BottomLeft => (x1, y1 + h1 - h2),
85 Self::LeftMiddle => (x1, y1 + h1/Dim(2) - h2/Dim(2)),
86 Self::Centre => (x1 + w1/Dim(2) - w2/Dim(2), y1 + h1/Dim(2) - h2/Dim(2)),
87 };
88 let (dx, dy) = match delta {
89 Some(offset) => offset.tup(),
90 None => (0, 0),
91 };
92 let (x2, y2) = (
93 x2.saturating_add_signed(dx),
94 y2.saturating_add_signed(dy),
95 );
96 // Perform final normalisation.
97 outer.clip(AbsRect::new(Coord::from((x2, y2)), AbsSize::from((w2, h2))))
98 }
99}
100
101#[derive(Clone, Debug)]
102pub enum Position {
103 Float(Coord),
104 Relative(RelativePosition),
105 OffsetFrom(RelativePosition, Offset),
106}
107
108impl Default for Position {
109 fn default() -> Self {
110 Self::Float(Coord::default())
111 }
112}
113
114#[derive(Clone, Debug)]
115pub struct RelSize {
116 pub x: FlexDim,
117 pub y: FlexDim,
118}
119
120impl RelSize {
121 pub fn new((x, y): (FlexDim, FlexDim)) -> Self { Self { x, y } }
122 pub fn tup(&self) -> (FlexDim, FlexDim) { (self.x.clone(), self.y.clone()) }
123}
124
125#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
126pub struct AbsSize {
127 pub x: Dim,
128 pub y: Dim,
129}
130
131impl From<(Dim, Dim)> for AbsSize {
132 fn from((x, y): (Dim, Dim)) -> Self { Self { x, y } }
133}
134
135impl From<(usize, usize)> for AbsSize {
136 fn from((x, y): (usize, usize)) -> Self {
137 Self { x: Dim::new(x), y: Dim::new(y) }
138 }
139}
140
141impl From<(u8, u8)> for AbsSize {
142 fn from((x, y): (u8, u8)) -> Self {
143 Self { x: x.into(), y: y.into() }
144 }
145}
146
147impl From<(u16, u16)> for AbsSize {
148 fn from((x, y): (u16, u16)) -> Self {
149 Self { x: x.into(), y: y.into() }
150 }
151}
152
153impl std::ops::Add<AbsSize> for AbsSize {
154 type Output = AbsSize;
155
156 fn add(self, other: AbsSize) -> Self::Output {
157 Self {
158 x: self.x + other.x,
159 y: self.y + other.y,
160 }
161 }
162}
163
164impl std::ops::Add<(Dim, Dim)> for AbsSize {
165 type Output = AbsSize;
166
167 fn add(self, (dx, dy): (Dim, Dim)) -> Self::Output {
168 Self {
169 x: self.x + dx,
170 y: self.y + dy,
171 }
172 }
173}
174
175impl std::ops::Sub<AbsSize> for AbsSize {
176 type Output = AbsSize;
177
178 fn sub(self, other: AbsSize) -> Self::Output {
179 Self {
180 x: self.x - other.x,
181 y: self.y - other.y,
182 }
183 }
184}
185
186impl std::ops::Sub<(Dim, Dim)> for AbsSize {
187 type Output = AbsSize;
188
189 fn sub(self, (dx, dy): (Dim, Dim)) -> Self::Output {
190 Self {
191 x: self.x - dx,
192 y: self.y - dy,
193 }
194 }
195}
196
197impl std::ops::AddAssign for AbsSize {
198 fn add_assign(&mut self, other: Self) {
199 *self = *self + other;
200 }
201}
202
203impl std::ops::SubAssign for AbsSize {
204 fn sub_assign(&mut self, other: Self) {
205 *self = *self - other;
206 }
207}
208
209impl AbsSize {
210
211 pub fn new<
212 X: Into<Dim>,
213 Y: Into<Dim>,
214 >(
215 (x, y): (X, Y),
216 )
217 -> Self
218 {
219 Self {
220 x: x.into(),
221 y: y.into(),
222 }
223 }
224
225 pub fn zero() -> Self { Self { x: Dim(0), y: Dim(0) } }
226 pub fn tup(&self) -> (Dim, Dim) { (self.x, self.y) }
227
228 pub fn inc_x<D: Into<Dim>>(&mut self, by: D) { self.x = self.x + by.into(); }
229 pub fn dec_x<D: Into<Dim>>(&mut self, by: D) { self.x = self.x - by.into(); }
230 pub fn inc_y<D: Into<Dim>>(&mut self, by: D) { self.y = self.y + by.into(); }
231 pub fn dec_y<D: Into<Dim>>(&mut self, by: D) { self.y = self.y - by.into(); }
232
233 /// Return the bottom right coordinate using zero-based indexing.
234 pub fn bot_right(&self) -> Coord {
235 Coord::new((
236 if self.x == Dim(0) {
237 Dim(0)
238 } else {
239 self.x - 1
240 },
241 if self.y == Dim(0) {
242 Dim(0)
243 } else {
244 self.y - 1
245 },
246 ))
247 }
248}
249
250/// A rectangle that is capable of being both relatively and absolutely defined.
251#[derive(Clone, Debug)]
252pub enum RelRect {
253 FixSize {
254 top_left: Position,
255 size: AbsSize,
256 },
257 RelSize(RelSize),
258}
259
260impl Default for RelRect {
261 fn default() -> Self {
262 Self::FixSize {
263 top_left: Position::default(),
264 size: AbsSize::default(),
265 }
266 }
267}
268
269impl From<RelSize> for RelRect {
270 fn from(size: RelSize) -> Self { Self::RelSize(size) }
271}
272
273impl RelRect {
274
275 pub fn new_fixed(top_left: Position, size: AbsSize) -> Self { Self::FixSize { top_left, size } }
276 pub fn new_rel(size: RelSize) -> Self { Self::RelSize(size) }
277
278 /// Position this inner rectangle relative to the outer rectangle.
279 pub fn relative_to(
280 &self, // 2
281 outer: AbsRect, // 1
282 )
283 -> Option<AbsRect>
284 {
285 match self {
286 Self::FixSize { top_left, size } => {
287 match top_left {
288 Position::Float(coord) =>
289 outer.clip(AbsRect::new(*coord, *size)),
290 Position::Relative(rpos) =>
291 rpos.relative_to(&outer, size, None),
292 Position::OffsetFrom(rpos, offset) =>
293 rpos.relative_to(&outer, size, Some(offset)),
294 }
295 }
296 Self::RelSize(size) => {
297 let (span_x, span_y) = outer.spans();
298 let span_x = size.x.relative_to(span_x);
299 let span_y = size.y.relative_to(span_y);
300 Some(AbsRect::from((span_x, span_y)))
301 }
302 }
303 }
304}
305
306#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
307pub struct AbsRect {
308 pub top_left: Coord,
309 pub size: AbsSize,
310}
311
312impl From<(Dim, Dim, Dim, Dim)> for AbsRect {
313 fn from((x, y, w, h): (Dim, Dim, Dim, Dim)) -> Self {
314 Self {
315 top_left: Coord::new((x, y)),
316 size: AbsSize::new((w, h)),
317 }
318 }
319}
320
321impl From<(Span, Span)> for AbsRect {
322 fn from((Span((x, w)), Span((y, h))): (Span, Span)) -> Self {
323 Self {
324 top_left: Coord::new((x, y)),
325 size: AbsSize::new((w, h)),
326 }
327 }
328}
329
330impl From<AbsSize> for AbsRect {
331 fn from(size: AbsSize) -> Self {
332 Self {
333 top_left: Coord::zero(),
334 size,
335 }
336 }
337}
338
339impl AbsRect {
340
341 pub fn new(top_left: Coord, size: AbsSize) -> Self { Self { top_left, size } }
342 pub fn new_top_left(size: AbsSize) -> Self {
343 Self {
344 top_left: Coord::from((0, 0)),
345 size,
346 }
347 }
348
349 pub fn tup(&self) -> (Dim, Dim, Dim, Dim) {
350 (self.top_left.x, self.top_left.y, self.size.x, self.size.y )
351 }
352 pub fn spans(&self) -> (Span, Span) {
353 (Span((self.top_left.x, self.size.x)), Span((self.top_left.y, self.size.y)))
354 }
355
356 pub fn bottom(&self) -> Dim {
357 self.top_left.y + self.size.y
358 }
359
360 pub fn top(&self) -> Dim {
361 self.top_left.y
362 }
363
364 pub fn right(&self) -> Dim {
365 self.top_left.x + self.size.x
366 }
367
368 pub fn left(&self) -> Dim {
369 self.top_left.x
370 }
371
372 pub fn height(&self) -> Dim {
373 self.size.y
374 }
375
376 pub fn width(&self) -> Dim {
377 self.size.x
378 }
379
380 pub fn inc_x(&mut self, by: Dim) { self.top_left.inc_x(by); }
381 pub fn dec_x(&mut self, by: Dim) { self.top_left.dec_x(by); }
382 pub fn inc_y(&mut self, by: Dim) { self.top_left.inc_y(by); }
383 pub fn dec_y(&mut self, by: Dim) { self.top_left.dec_y(by); }
384
385 pub fn inc_w(&mut self, by: Dim) { self.size.inc_x(by); }
386 pub fn dec_w(&mut self, by: Dim) { self.size.dec_x(by); }
387 pub fn inc_h(&mut self, by: Dim) { self.size.inc_y(by); }
388 pub fn dec_h(&mut self, by: Dim) { self.size.dec_y(by); }
389
390 /// Clip the goven `AbsRect` with respect to this outer `AbsRect`.
391 /// ```ignore
392 ///
393 /// e.g.
394 ///
395 /// +----------------------------------------+
396 /// | |
397 /// | | outer
398 /// | |
399 /// | |
400 /// | |
401 /// | +------------------------+
402 /// | | | |
403 /// | | | |
404 /// | | | | other
405 /// | | | |
406 /// | | | |
407 /// +------------------------|---------------+ |
408 /// | |
409 /// | |
410 /// +------------------------+
411 /// Result:
412 /// +----------------------------------------+
413 /// | |
414 /// | | outer
415 /// | |
416 /// | |
417 /// | |
418 /// | +---------------+
419 /// | | |
420 /// | | |
421 /// | result | |
422 /// | | |
423 /// | | |
424 /// +------------------------+---------------+
425 /// ```
426 pub fn clip(&self, other: Self) -> Option<Self> {
427 let (other_span_x, other_span_y) = other.spans();
428 let (span_x, span_y) = self.spans();
429 let span_x_opt = span_x.clip(other_span_x);
430 let span_y_opt = span_y.clip(other_span_y);
431 if let Some(span_x) = span_x_opt {
432 if let Some(span_y) = span_y_opt {
433 return Some(Self::from((span_x, span_y)));
434 }
435 }
436 None
437 }
438}
439
440#[derive(Clone, Debug)]
441pub enum RectView {
442 Float(AbsRect),
443 InitiallyRelative(RelRect),
444 AlwaysRelative(RelRect),
445}
446
447impl Default for RectView {
448 fn default() -> Self {
449 Self::Float(AbsRect::default())
450 }
451}
452
453impl RectView {
454
455 /// Position and clip this `RectView` with respect to the given `AbsRect`.
456 pub fn relative_to(
457 &self,
458 outer: AbsRect,
459 )
460 -> Option<AbsRect>
461 {
462 match self {
463 Self::Float(abs_rect) => outer.clip(*abs_rect),
464 Self::InitiallyRelative(rel_rect) | Self::AlwaysRelative(rel_rect) => {
465 rel_rect.relative_to(outer)
466 }
467 }
468 }
469
470 /// Fix the size of the `RectView`. If it was initially relative, it will be changed to a
471 /// floating variant.
472 pub fn set_size(
473 &mut self,
474 new_size: AbsSize,
475 outer: AbsRect,
476 ) {
477 match self {
478 Self::Float(AbsRect { size, .. }) => {
479 *size = new_size;
480 }
481 Self::InitiallyRelative(rel_rect) => {
482 let abs_rect_opt = rel_rect.relative_to(outer);
483 if let Some(mut abs_rect) = abs_rect_opt {
484 abs_rect.size = new_size;
485 *self = Self::Float(abs_rect);
486 }
487 }
488 _ => {}
489 }
490 }
491}