1use glam::{IVec3, Vec3};
2use strum::{Display, EnumIter, EnumString, IntoEnumIterator};
3use thiserror::Error;
4
5#[derive(Debug, Clone, PartialEq, Eq, Error)]
7pub enum DirectionParseError {
8 #[error("invalid direction string: {0}")]
10 InvalidString(String),
11 #[error("invalid IVec3 for Direction: {0:?}")]
13 InvalidVec(IVec3),
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, Default, EnumIter, EnumString)]
20#[strum(
21 parse_err_ty = DirectionParseError,
22 parse_err_fn = direction_parse_error
23)]
24pub enum Direction {
25 #[default]
27 #[strum(serialize = "+X")]
28 XPLUS,
29 #[strum(serialize = "-X")]
31 XMINUS,
32 #[strum(serialize = "+Y")]
34 YPLUS,
35 #[strum(serialize = "-Y")]
37 YMINUS,
38 #[strum(serialize = "+Z")]
40 ZPLUS,
41 #[strum(serialize = "-Z")]
43 ZMINUS,
44}
45
46impl Direction {
47 pub fn iter() -> impl Iterator<Item = Self> {
49 <Self as IntoEnumIterator>::iter()
50 }
51
52 pub const fn index(self) -> usize {
54 match self {
55 Direction::XPLUS => 0,
56 Direction::XMINUS => 1,
57 Direction::YPLUS => 2,
58 Direction::YMINUS => 3,
59 Direction::ZPLUS => 4,
60 Direction::ZMINUS => 5,
61 }
62 }
63
64 pub const fn to_ivec3(self) -> IVec3 {
66 match self {
67 Direction::XPLUS => IVec3::new(1, 0, 0),
68 Direction::XMINUS => IVec3::new(-1, 0, 0),
69 Direction::YPLUS => IVec3::new(0, 1, 0),
70 Direction::YMINUS => IVec3::new(0, -1, 0),
71 Direction::ZPLUS => IVec3::new(0, 0, 1),
72 Direction::ZMINUS => IVec3::new(0, 0, -1),
73 }
74 }
75
76 pub fn to_vec3(self) -> Vec3 {
78 self.to_ivec3().as_vec3()
79 }
80
81 pub const fn is_spatial(self) -> bool {
83 matches!(
84 self,
85 Direction::XPLUS | Direction::XMINUS | Direction::YPLUS | Direction::YMINUS
86 )
87 }
88
89 pub const fn negate(self) -> Self {
91 match self {
92 Direction::XPLUS => Direction::XMINUS,
93 Direction::XMINUS => Direction::XPLUS,
94 Direction::YPLUS => Direction::YMINUS,
95 Direction::YMINUS => Direction::YPLUS,
96 Direction::ZPLUS => Direction::ZMINUS,
97 Direction::ZMINUS => Direction::ZPLUS,
98 }
99 }
100
101 pub const fn as_udirection(self) -> UDirection {
103 match self {
104 Direction::XPLUS | Direction::XMINUS => UDirection::X,
105 Direction::YPLUS | Direction::YMINUS => UDirection::Y,
106 Direction::ZPLUS | Direction::ZMINUS => UDirection::Z,
107 }
108 }
109}
110
111impl TryFrom<IVec3> for Direction {
112 type Error = DirectionParseError;
113
114 fn try_from(value: IVec3) -> Result<Self, Self::Error> {
115 for dir in Direction::iter() {
116 if value == dir.to_ivec3() {
117 return Ok(dir);
118 }
119 }
120 Err(DirectionParseError::InvalidVec(value))
121 }
122}
123
124fn direction_parse_error(value: &str) -> DirectionParseError {
125 DirectionParseError::InvalidString(value.to_string())
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, Default, EnumIter, EnumString)]
130pub enum UDirection {
131 #[default]
133 X,
134 Y,
136 Z,
138}
139
140impl UDirection {
141 pub fn iter() -> impl Iterator<Item = Self> {
143 <Self as IntoEnumIterator>::iter()
144 }
145
146 pub const fn to_ivec3(self) -> IVec3 {
148 match self {
149 UDirection::X => IVec3::new(1, 0, 0),
150 UDirection::Y => IVec3::new(0, 1, 0),
151 UDirection::Z => IVec3::new(0, 0, 1),
152 }
153 }
154
155 pub fn to_vec3(self) -> Vec3 {
157 self.to_ivec3().as_vec3()
158 }
159
160 pub const fn is_spatial(self) -> bool {
162 matches!(self, UDirection::X | UDirection::Y)
163 }
164
165 pub const fn index(self) -> usize {
167 match self {
168 UDirection::X => 0,
169 UDirection::Y => 1,
170 UDirection::Z => 2,
171 }
172 }
173}