all_is_cubes_base/math/
axis.rs1use core::fmt;
2
3use crate::math::{Face6, Rgb};
4
5#[expect(clippy::exhaustive_enums)]
13#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, exhaust::Exhaust)]
14#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
15#[repr(u8)]
17#[allow(missing_docs)]
18pub enum Axis {
19 X = 0,
20 Y = 1,
21 Z = 2,
22}
23
24impl Axis {
25 pub const ALL: [Self; 3] = [Self::X, Self::Y, Self::Z];
27
28 #[mutants::skip]
35 #[inline]
36 pub fn color(&self) -> Rgb {
37 match self {
38 Axis::X => Rgb::UNIFORM_LUMINANCE_RED,
39 Axis::Y => Rgb::UNIFORM_LUMINANCE_GREEN,
40 Axis::Z => Rgb::UNIFORM_LUMINANCE_BLUE,
41 }
42 }
43
44 #[inline]
46 pub fn positive_face(&self) -> Face6 {
47 match self {
48 Axis::X => Face6::PX,
49 Axis::Y => Face6::PY,
50 Axis::Z => Face6::PZ,
51 }
52 }
53
54 #[inline]
56 pub fn negative_face(&self) -> Face6 {
57 match self {
58 Axis::X => Face6::NX,
59 Axis::Y => Face6::NY,
60 Axis::Z => Face6::NZ,
61 }
62 }
63
64 #[inline]
66 pub const fn index(self) -> usize {
67 self as usize
68 }
69
70 #[inline]
72 #[must_use]
73 pub const fn increment(self) -> Self {
74 match self {
75 Axis::X => Axis::Y,
76 Axis::Y => Axis::Z,
77 Axis::Z => Axis::X,
78 }
79 }
80
81 #[inline]
83 #[must_use]
84 pub const fn decrement(self) -> Self {
85 match self {
86 Axis::X => Axis::Z,
87 Axis::Y => Axis::X,
88 Axis::Z => Axis::Y,
89 }
90 }
91}
92
93impl fmt::LowerHex for Axis {
95 #[allow(clippy::missing_inline_in_public_items)]
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 f.write_str(match self {
98 Axis::X => "x",
99 Axis::Y => "y",
100 Axis::Z => "z",
101 })
102 }
103}
104impl fmt::UpperHex for Axis {
106 #[allow(clippy::missing_inline_in_public_items)]
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.write_str(match self {
109 Axis::X => "X",
110 Axis::Y => "Y",
111 Axis::Z => "Z",
112 })
113 }
114}
115
116impl From<Axis> for u8 {
117 #[inline]
118 fn from(value: Axis) -> Self {
119 value as u8
120 }
121}
122impl From<Axis> for usize {
123 #[inline]
124 fn from(value: Axis) -> Self {
125 value as usize
126 }
127}
128
129mod impl_index_axis {
130 use super::Axis;
131 use core::ops;
132
133 impl<T> ops::Index<Axis> for [T; 3] {
134 type Output = T;
135
136 #[inline]
137 fn index(&self, index: Axis) -> &Self::Output {
138 &self[index as usize]
139 }
140 }
141 impl<T> ops::IndexMut<Axis> for [T; 3] {
142 #[inline]
143 fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
144 &mut self[index as usize]
145 }
146 }
147
148 macro_rules! impl_xyz_e {
149 ($x:ident $y:ident $z:ident, $($type:tt)*) => {
150 impl<T, U> ops::Index<Axis> for $($type)*<T, U> {
151 type Output = T;
152
153 #[inline]
154 fn index(&self, index: Axis) -> &Self::Output {
155 match index {
156 Axis::X => &self.$x,
157 Axis::Y => &self.$y,
158 Axis::Z => &self.$z,
159 }
160 }
161 }
162 impl<T, U> ops::IndexMut<Axis> for $($type)*<T, U> {
163 #[inline]
164 fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
165 match index {
166 Axis::X => &mut self.$x,
167 Axis::Y => &mut self.$y,
168 Axis::Z => &mut self.$z,
169 }
170 }
171 }
172 };
173 }
174 impl_xyz_e!(x y z, euclid::Vector3D);
175 impl_xyz_e!(x y z, euclid::Point3D);
176 impl_xyz_e!(width height depth, euclid::Size3D);
177
178 }
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn axis_conversion() {
187 assert_eq!(u8::from(Axis::X), 0);
188 assert_eq!(u8::from(Axis::Y), 1);
189 assert_eq!(u8::from(Axis::Z), 2);
190
191 for axis in Axis::ALL {
192 assert_eq!(usize::from(axis), usize::from(u8::from(axis)));
193 assert_eq!(usize::from(axis), axis.index());
194 }
195 }
196
197 #[test]
198 fn axis_fmt() {
199 use Axis::*;
200 assert_eq!(
201 format!("{X:x} {Y:x} {Z:x} {X:X} {Y:X} {Z:X}"),
202 "x y z X Y Z"
203 );
204 }
205
206 #[test]
207 fn inc_dec_properties() {
208 for axis in Axis::ALL {
209 assert_ne!(axis, axis.increment());
210 assert_ne!(axis, axis.decrement());
211 assert_eq!(axis, axis.increment().decrement());
212 assert_eq!(axis, axis.decrement().increment());
213 }
214 }
215}