Skip to main content

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