thedes_geometry/
orientation.rs

1use std::{
2    fmt,
3    ops::{Add, Index, IndexMut, Neg, Sub},
4};
5
6use num::{
7    CheckedAdd,
8    CheckedSub,
9    One,
10    traits::{SaturatingAdd, SaturatingSub},
11};
12
13use crate::CoordPair;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub enum Axis {
17    Y,
18    X,
19}
20
21impl Axis {
22    pub const ALL: [Self; 2] = [Self::Y, Self::X];
23
24    pub fn shift(self) -> Self {
25        match self {
26            Self::Y => Self::X,
27            Self::X => Self::Y,
28        }
29    }
30}
31
32impl fmt::Display for Axis {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            Self::Y => write!(f, "y"),
36            Self::X => write!(f, "x"),
37        }
38    }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
42pub enum Order {
43    Backwards,
44    Forwards,
45}
46
47impl Order {
48    pub const ALL: [Self; 2] = [Self::Backwards, Self::Forwards];
49
50    pub fn move_unit<C>(self, target: C) -> C
51    where
52        C: Add<Output = C> + Sub<Output = C> + One,
53    {
54        self.move_by(C::one(), target)
55    }
56
57    pub fn checked_move_unit<C>(self, target: &C) -> Option<C>
58    where
59        C: CheckedAdd + CheckedSub + One,
60    {
61        self.checked_move_by(&C::one(), target)
62    }
63
64    pub fn saturating_move_unit<C>(self, target: &C) -> C
65    where
66        C: SaturatingAdd + SaturatingSub + One,
67    {
68        self.saturating_move_by(&C::one(), target)
69    }
70
71    pub fn move_by<C>(self, magnitude: C, target: C) -> C
72    where
73        C: Add<Output = C> + Sub<Output = C>,
74    {
75        match self {
76            Self::Backwards => target - magnitude,
77            Self::Forwards => target + magnitude,
78        }
79    }
80
81    pub fn checked_move_by<C>(self, magnitude: &C, target: &C) -> Option<C>
82    where
83        C: CheckedAdd + CheckedSub,
84    {
85        match self {
86            Self::Backwards => target.checked_sub(magnitude),
87            Self::Forwards => target.checked_add(magnitude),
88        }
89    }
90
91    pub fn saturating_move_by<C>(self, magnitude: &C, target: &C) -> C
92    where
93        C: SaturatingAdd + SaturatingSub,
94    {
95        match self {
96            Self::Backwards => target.saturating_add(magnitude),
97            Self::Forwards => target.saturating_sub(magnitude),
98        }
99    }
100}
101
102impl fmt::Display for Order {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        match self {
105            Self::Backwards => write!(f, "-"),
106            Self::Forwards => write!(f, "+"),
107        }
108    }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
112pub enum Direction {
113    Up,
114    Left,
115    Down,
116    Right,
117}
118
119impl Direction {
120    pub const ALL: [Self; 4] = [Self::Up, Self::Left, Self::Down, Self::Right];
121
122    pub fn new(axis: Axis, order: Order) -> Self {
123        match (axis, order) {
124            (Axis::Y, Order::Backwards) => Self::Up,
125            (Axis::X, Order::Backwards) => Self::Left,
126            (Axis::Y, Order::Forwards) => Self::Down,
127            (Axis::X, Order::Forwards) => Self::Right,
128        }
129    }
130
131    pub fn axis(self) -> Axis {
132        match self {
133            Self::Up | Self::Down => Axis::Y,
134            Self::Left | Self::Right => Axis::X,
135        }
136    }
137
138    pub fn order(self) -> Order {
139        match self {
140            Self::Up | Self::Left => Order::Backwards,
141            Self::Down | Self::Right => Order::Forwards,
142        }
143    }
144
145    pub fn move_unit<C>(self, target: CoordPair<C>) -> CoordPair<C>
146    where
147        C: Add<Output = C> + Sub<Output = C> + One,
148    {
149        DirectionVec::unit(self).mov(target)
150    }
151
152    pub fn checked_move_unit<C>(
153        self,
154        target: &CoordPair<C>,
155    ) -> Option<CoordPair<C>>
156    where
157        C: CheckedAdd + CheckedSub + One + Clone,
158    {
159        DirectionVec::unit(self).as_ref().checked_move(target)
160    }
161
162    pub fn checked_move_unit_by_ref<C>(
163        self,
164        target: CoordPair<&C>,
165    ) -> Option<CoordPair<C>>
166    where
167        C: CheckedAdd + CheckedSub + One + Clone,
168    {
169        DirectionVec::unit(self).as_ref().checked_move_by_ref(target)
170    }
171
172    pub fn saturating_move_unit<C>(self, target: &CoordPair<C>) -> CoordPair<C>
173    where
174        C: SaturatingAdd + SaturatingSub + One + Clone,
175    {
176        DirectionVec::unit(self).as_ref().saturating_move(target)
177    }
178
179    pub fn saturating_move_unit_by_ref<C>(
180        self,
181        target: CoordPair<&C>,
182    ) -> CoordPair<C>
183    where
184        C: SaturatingAdd + SaturatingSub + One + Clone,
185    {
186        DirectionVec::unit(self).as_ref().saturating_move_by_ref(target)
187    }
188
189    pub fn move_by<C>(self, magnitude: C, target: CoordPair<C>) -> CoordPair<C>
190    where
191        C: Add<Output = C> + Sub<Output = C>,
192    {
193        DirectionVec { direction: self, magnitude }.mov(target)
194    }
195
196    pub fn checked_move_by<C>(
197        self,
198        magnitude: &C,
199        target: &CoordPair<C>,
200    ) -> Option<CoordPair<C>>
201    where
202        C: CheckedAdd + CheckedSub + Clone,
203    {
204        DirectionVec { direction: self, magnitude }.checked_move(target)
205    }
206
207    pub fn checked_move_by_ref_by<C>(
208        self,
209        magnitude: &C,
210        target: CoordPair<&C>,
211    ) -> Option<CoordPair<C>>
212    where
213        C: CheckedAdd + CheckedSub + Clone,
214    {
215        DirectionVec { direction: self, magnitude }.checked_move_by_ref(target)
216    }
217
218    pub fn saturating_move_by<C>(
219        self,
220        magnitude: &C,
221        target: &CoordPair<C>,
222    ) -> CoordPair<C>
223    where
224        C: SaturatingAdd + SaturatingSub + Clone,
225    {
226        self.saturating_move_by_ref_by(magnitude, target.as_ref())
227    }
228
229    pub fn saturating_move_by_ref_by<C>(
230        self,
231        magnitude: &C,
232        target: CoordPair<&C>,
233    ) -> CoordPair<C>
234    where
235        C: SaturatingAdd + SaturatingSub + Clone,
236    {
237        match self.axis() {
238            Axis::Y => CoordPair {
239                y: self.order().saturating_move_by(magnitude, target.y),
240                x: target.x.clone(),
241            },
242            Axis::X => CoordPair {
243                x: self.order().saturating_move_by(magnitude, target.x),
244                y: target.y.clone(),
245            },
246        }
247    }
248}
249
250impl fmt::Display for Direction {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        match self {
253            Self::Up => write!(f, "-y"),
254            Self::Left => write!(f, "-x"),
255            Self::Down => write!(f, "+y"),
256            Self::Right => write!(f, "+x"),
257        }
258    }
259}
260
261impl Neg for Direction {
262    type Output = Self;
263
264    fn neg(self) -> Self::Output {
265        match self {
266            Self::Up => Self::Down,
267            Self::Left => Self::Right,
268            Self::Right => Self::Left,
269            Self::Down => Self::Up,
270        }
271    }
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
275pub struct DirectionMap<T> {
276    pub up: T,
277    pub left: T,
278    pub down: T,
279    pub right: T,
280}
281
282impl<T> DirectionMap<T> {
283    pub fn from_dirs<F>(mut generator: F) -> DirectionMap<T>
284    where
285        F: FnMut(Direction) -> T,
286    {
287        Self {
288            up: generator(Direction::Up),
289            left: generator(Direction::Left),
290            down: generator(Direction::Down),
291            right: generator(Direction::Right),
292        }
293    }
294
295    pub fn map<F, U>(self, mut mapper: F) -> DirectionMap<U>
296    where
297        F: FnMut(T) -> U,
298    {
299        self.map_with_dirs(|elem, _| mapper(elem))
300    }
301
302    pub fn map_with_dirs<F, U>(self, mut mapper: F) -> DirectionMap<U>
303    where
304        F: FnMut(T, Direction) -> U,
305    {
306        DirectionMap {
307            up: mapper(self.up, Direction::Up),
308            left: mapper(self.left, Direction::Left),
309            down: mapper(self.down, Direction::Down),
310            right: mapper(self.right, Direction::Right),
311        }
312    }
313
314    pub fn as_ref(&self) -> DirectionMap<&T> {
315        DirectionMap {
316            up: &self.up,
317            left: &self.left,
318            down: &self.down,
319            right: &self.right,
320        }
321    }
322
323    pub fn as_mut(&mut self) -> DirectionMap<&mut T> {
324        DirectionMap {
325            up: &mut self.up,
326            left: &mut self.left,
327            down: &mut self.down,
328            right: &mut self.right,
329        }
330    }
331}
332
333impl<'a, T> DirectionMap<&'a T> {
334    pub fn copied(self) -> DirectionMap<T>
335    where
336        T: Copy,
337    {
338        self.map(|a| *a)
339    }
340
341    pub fn cloned(self) -> DirectionMap<T>
342    where
343        T: Clone,
344    {
345        self.map(Clone::clone)
346    }
347}
348
349impl<'a, T> DirectionMap<&'a mut T> {
350    pub fn copied(self) -> DirectionMap<T>
351    where
352        T: Copy,
353    {
354        self.map(|a| *a)
355    }
356
357    pub fn cloned(self) -> DirectionMap<T>
358    where
359        T: Clone,
360    {
361        self.map(|a| a.clone())
362    }
363
364    pub fn share(self) -> DirectionMap<&'a T> {
365        self.map(|a| &*a)
366    }
367}
368
369impl<T> DirectionMap<Option<T>> {
370    pub fn transpose(self) -> Option<DirectionMap<T>> {
371        Some(DirectionMap {
372            up: self.up?,
373            left: self.left?,
374            down: self.down?,
375            right: self.right?,
376        })
377    }
378
379    pub fn from_transposed(transposed: Option<DirectionMap<T>>) -> Self {
380        match transposed {
381            Some(table) => table.map(Some),
382            None => Self::from_dirs(|_| None),
383        }
384    }
385}
386
387impl<T, E> DirectionMap<Result<T, E>> {
388    pub fn transpose(self) -> Result<DirectionMap<T>, E> {
389        Ok(DirectionMap {
390            up: self.up?,
391            left: self.left?,
392            down: self.down?,
393            right: self.right?,
394        })
395    }
396
397    pub fn from_transposed(transposed: Result<DirectionMap<T>, E>) -> Self
398    where
399        E: Clone,
400    {
401        match transposed {
402            Ok(table) => table.map(Ok),
403            Err(error) => Self::from_dirs(|_| Err(error.clone())),
404        }
405    }
406}
407
408impl<T> Index<Direction> for DirectionMap<T> {
409    type Output = T;
410
411    fn index(&self, index: Direction) -> &Self::Output {
412        match index {
413            Direction::Up => &self.up,
414            Direction::Left => &self.left,
415            Direction::Down => &self.down,
416            Direction::Right => &self.right,
417        }
418    }
419}
420
421impl<T> Index<(Axis, Order)> for DirectionMap<T> {
422    type Output = T;
423
424    fn index(&self, (axis, order): (Axis, Order)) -> &Self::Output {
425        &self[Direction::new(axis, order)]
426    }
427}
428
429impl<T> IndexMut<Direction> for DirectionMap<T> {
430    fn index_mut(&mut self, index: Direction) -> &mut Self::Output {
431        match index {
432            Direction::Up => &mut self.up,
433            Direction::Left => &mut self.left,
434            Direction::Down => &mut self.down,
435            Direction::Right => &mut self.right,
436        }
437    }
438}
439
440impl<T> IndexMut<(Axis, Order)> for DirectionMap<T> {
441    fn index_mut(&mut self, (axis, order): (Axis, Order)) -> &mut Self::Output {
442        &mut self[Direction::new(axis, order)]
443    }
444}
445
446#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
447pub struct DirectionFlags {
448    bits: u8,
449}
450
451impl DirectionFlags {
452    pub fn new(map: DirectionMap<bool>) -> Self {
453        Self::packed([map, DirectionMap::default()])
454    }
455
456    pub fn packed(maps: [DirectionMap<bool>; 2]) -> Self {
457        let mut bits = 0_u8;
458        for map in maps.into_iter().rev() {
459            for direction in Direction::ALL.into_iter().rev() {
460                bits <<= 1;
461                bits |= u8::from(map[direction]);
462            }
463        }
464        Self { bits }
465    }
466
467    pub fn map(self) -> DirectionMap<bool> {
468        self.packed_maps()[0]
469    }
470
471    pub fn packed_maps(self) -> [DirectionMap<bool>; 2] {
472        let mut bits = self.bits;
473        let mut maps = [DirectionMap::default(); 2];
474        for map in &mut maps {
475            for direction in Direction::ALL {
476                map[direction] = (bits & 1) != 0;
477                bits >>= 1;
478            }
479        }
480        maps
481    }
482
483    pub fn with<F>(self, mapper: F) -> Self
484    where
485        F: FnOnce(DirectionMap<bool>) -> DirectionMap<bool>,
486    {
487        self.packed_with(|maps| [mapper(maps[0]), maps[1]])
488    }
489
490    pub fn packed_with<F>(self, mapper: F) -> Self
491    where
492        F: FnOnce([DirectionMap<bool>; 2]) -> [DirectionMap<bool>; 2],
493    {
494        Self::packed(mapper(self.packed_maps()))
495    }
496
497    pub fn modify<F, T>(&mut self, modifier: F) -> T
498    where
499        F: FnOnce(&mut DirectionMap<bool>) -> T,
500    {
501        self.packed_modify(|maps| modifier(&mut maps[0]))
502    }
503
504    pub fn packed_modify<F, T>(&mut self, modifier: F) -> T
505    where
506        F: FnOnce(&mut [DirectionMap<bool>; 2]) -> T,
507    {
508        let mut maps = self.packed_maps();
509        let output = modifier(&mut maps);
510        *self = Self::packed(maps);
511        output
512    }
513}
514
515impl From<DirectionMap<bool>> for DirectionFlags {
516    fn from(map: DirectionMap<bool>) -> Self {
517        Self::new(map)
518    }
519}
520
521impl From<DirectionFlags> for DirectionMap<bool> {
522    fn from(flags: DirectionFlags) -> Self {
523        flags.map()
524    }
525}
526
527impl From<[DirectionMap<bool>; 2]> for DirectionFlags {
528    fn from(packed_maps: [DirectionMap<bool>; 2]) -> Self {
529        Self::packed(packed_maps)
530    }
531}
532
533impl From<DirectionFlags> for [DirectionMap<bool>; 2] {
534    fn from(flags: DirectionFlags) -> Self {
535        flags.packed_maps()
536    }
537}
538
539#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
540pub struct DirectionVec<C> {
541    pub direction: Direction,
542    pub magnitude: C,
543}
544
545impl<C> DirectionVec<C> {
546    pub fn unit(direction: Direction) -> Self
547    where
548        C: One,
549    {
550        Self { direction, magnitude: C::one() }
551    }
552
553    pub fn as_ref(&self) -> DirectionVec<&C> {
554        DirectionVec { direction: self.direction, magnitude: &self.magnitude }
555    }
556
557    pub fn as_mut(&mut self) -> DirectionVec<&mut C> {
558        DirectionVec {
559            direction: self.direction,
560            magnitude: &mut self.magnitude,
561        }
562    }
563
564    pub fn map<F, C0>(self, mapper: F) -> DirectionVec<C0>
565    where
566        F: FnOnce(C) -> C0,
567    {
568        DirectionVec {
569            direction: self.direction,
570            magnitude: mapper(self.magnitude),
571        }
572    }
573
574    pub fn with_mangitude<C0>(&self, new_magnitude: C0) -> DirectionVec<C0> {
575        self.as_ref().map(|_| new_magnitude)
576    }
577
578    pub fn mov(self, target: CoordPair<C>) -> CoordPair<C>
579    where
580        C: Add<Output = C> + Sub<Output = C>,
581    {
582        match self.direction.axis() {
583            Axis::Y => CoordPair {
584                y: self.direction.order().move_by(self.magnitude, target.y),
585                ..target
586            },
587            Axis::X => CoordPair {
588                x: self.direction.order().move_by(self.magnitude, target.x),
589                ..target
590            },
591        }
592    }
593}
594
595impl<'a, C> DirectionVec<&'a C> {
596    pub fn copied(self) -> DirectionVec<C>
597    where
598        C: Copy,
599    {
600        self.map(|m| *m)
601    }
602
603    pub fn cloned(self) -> DirectionVec<C>
604    where
605        C: Clone,
606    {
607        self.map(Clone::clone)
608    }
609
610    pub fn checked_move(self, target: &CoordPair<C>) -> Option<CoordPair<C>>
611    where
612        C: CheckedAdd + CheckedSub + Clone,
613    {
614        self.checked_move_by_ref(target.as_ref())
615    }
616
617    pub fn checked_move_by_ref(
618        self,
619        target: CoordPair<&C>,
620    ) -> Option<CoordPair<C>>
621    where
622        C: CheckedAdd + CheckedSub + Clone,
623    {
624        match self.direction.axis() {
625            Axis::Y => Some(CoordPair {
626                y: self
627                    .direction
628                    .order()
629                    .checked_move_by(self.magnitude, target.y)?,
630                x: target.x.clone(),
631            }),
632            Axis::X => Some(CoordPair {
633                x: self
634                    .direction
635                    .order()
636                    .checked_move_by(self.magnitude, target.x)?,
637                y: target.y.clone(),
638            }),
639        }
640    }
641
642    pub fn saturating_move(self, target: &CoordPair<C>) -> CoordPair<C>
643    where
644        C: SaturatingAdd + SaturatingSub + Clone,
645    {
646        self.saturating_move_by_ref(target.as_ref())
647    }
648
649    pub fn saturating_move_by_ref(self, target: CoordPair<&C>) -> CoordPair<C>
650    where
651        C: SaturatingAdd + SaturatingSub + Clone,
652    {
653        match self.direction.axis() {
654            Axis::Y => CoordPair {
655                y: self
656                    .direction
657                    .order()
658                    .saturating_move_by(self.magnitude, target.y),
659                x: target.x.clone(),
660            },
661            Axis::X => CoordPair {
662                x: self
663                    .direction
664                    .order()
665                    .saturating_move_by(self.magnitude, target.x),
666                y: target.y.clone(),
667            },
668        }
669    }
670}
671
672impl<'a, C> DirectionVec<&'a mut C> {
673    pub fn copied(self) -> DirectionVec<C>
674    where
675        C: Copy,
676    {
677        self.map(|m| *m)
678    }
679
680    pub fn cloned(self) -> DirectionVec<C>
681    where
682        C: Clone,
683    {
684        self.map(|m| m.clone())
685    }
686
687    pub fn share(self) -> DirectionVec<&'a C> {
688        self.map(|m| &*m)
689    }
690}
691
692#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
693pub enum Diagonal {
694    TopLeft,
695    TopRight,
696    BottomLeft,
697    BottomRight,
698}
699
700impl Diagonal {
701    pub const ALL: [Self; 4] =
702        [Self::TopLeft, Self::TopRight, Self::BottomLeft, Self::BottomRight];
703
704    pub fn new(axes_orders: CoordPair<Order>) -> Self {
705        match axes_orders {
706            CoordPair { y: Order::Backwards, x: Order::Backwards } => {
707                Self::TopLeft
708            },
709            CoordPair { y: Order::Backwards, x: Order::Forwards } => {
710                Self::TopRight
711            },
712            CoordPair { y: Order::Forwards, x: Order::Backwards } => {
713                Self::BottomLeft
714            },
715            CoordPair { y: Order::Forwards, x: Order::Forwards } => {
716                Self::BottomRight
717            },
718        }
719    }
720
721    pub fn axes_orders(self) -> CoordPair<Order> {
722        match self {
723            Self::TopLeft => {
724                CoordPair { y: Order::Backwards, x: Order::Backwards }
725            },
726            Self::TopRight => {
727                CoordPair { y: Order::Backwards, x: Order::Forwards }
728            },
729            Self::BottomLeft => {
730                CoordPair { y: Order::Forwards, x: Order::Backwards }
731            },
732            Self::BottomRight => {
733                CoordPair { y: Order::Forwards, x: Order::Forwards }
734            },
735        }
736    }
737}
738
739impl fmt::Display for Diagonal {
740    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
741        match self {
742            Self::TopLeft => write!(f, "-y -x"),
743            Self::TopRight => write!(f, "-y +x"),
744            Self::BottomLeft => write!(f, "+y -x"),
745            Self::BottomRight => write!(f, "+y +x"),
746        }
747    }
748}
749
750#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
751pub struct DiagonalMap<T> {
752    pub top_left: T,
753    pub top_right: T,
754    pub bottom_left: T,
755    pub bottom_right: T,
756}
757
758impl<T> Index<Diagonal> for DiagonalMap<T> {
759    type Output = T;
760
761    fn index(&self, index: Diagonal) -> &Self::Output {
762        match index {
763            Diagonal::TopLeft => &self.top_left,
764            Diagonal::TopRight => &self.top_right,
765            Diagonal::BottomLeft => &self.bottom_left,
766            Diagonal::BottomRight => &self.bottom_right,
767        }
768    }
769}
770
771impl<T> Index<CoordPair<Order>> for DiagonalMap<T> {
772    type Output = T;
773
774    fn index(&self, index: CoordPair<Order>) -> &Self::Output {
775        &self[Diagonal::new(index)]
776    }
777}
778
779impl<T> IndexMut<Diagonal> for DiagonalMap<T> {
780    fn index_mut(&mut self, index: Diagonal) -> &mut Self::Output {
781        match index {
782            Diagonal::TopLeft => &mut self.top_left,
783            Diagonal::TopRight => &mut self.top_right,
784            Diagonal::BottomLeft => &mut self.bottom_left,
785            Diagonal::BottomRight => &mut self.bottom_right,
786        }
787    }
788}
789
790impl<T> IndexMut<CoordPair<Order>> for DiagonalMap<T> {
791    fn index_mut(&mut self, index: CoordPair<Order>) -> &mut Self::Output {
792        &mut self[Diagonal::new(index)]
793    }
794}