schematic_mesher/types/
direction.rs

1//! Direction and axis types for face and rotation handling.
2
3use serde::{Deserialize, Serialize};
4
5/// The six cardinal directions / face directions.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[serde(rename_all = "lowercase")]
8pub enum Direction {
9    Down,
10    Up,
11    North,
12    South,
13    West,
14    East,
15}
16
17impl Direction {
18    /// All six directions in order.
19    pub const ALL: [Direction; 6] = [
20        Direction::Down,
21        Direction::Up,
22        Direction::North,
23        Direction::South,
24        Direction::West,
25        Direction::East,
26    ];
27
28    /// Get the offset for this direction.
29    pub fn offset(&self) -> (i32, i32, i32) {
30        match self {
31            Direction::Down => (0, -1, 0),
32            Direction::Up => (0, 1, 0),
33            Direction::North => (0, 0, -1),
34            Direction::South => (0, 0, 1),
35            Direction::West => (-1, 0, 0),
36            Direction::East => (1, 0, 0),
37        }
38    }
39
40    /// Get the normal vector for this direction.
41    pub fn normal(&self) -> [f32; 3] {
42        match self {
43            Direction::Down => [0.0, -1.0, 0.0],
44            Direction::Up => [0.0, 1.0, 0.0],
45            Direction::North => [0.0, 0.0, -1.0],
46            Direction::South => [0.0, 0.0, 1.0],
47            Direction::West => [-1.0, 0.0, 0.0],
48            Direction::East => [1.0, 0.0, 0.0],
49        }
50    }
51
52    /// Get the opposite direction.
53    pub fn opposite(&self) -> Direction {
54        match self {
55            Direction::Down => Direction::Up,
56            Direction::Up => Direction::Down,
57            Direction::North => Direction::South,
58            Direction::South => Direction::North,
59            Direction::West => Direction::East,
60            Direction::East => Direction::West,
61        }
62    }
63
64    /// Get the axis this direction is on.
65    pub fn axis(&self) -> Axis {
66        match self {
67            Direction::Down | Direction::Up => Axis::Y,
68            Direction::North | Direction::South => Axis::Z,
69            Direction::West | Direction::East => Axis::X,
70        }
71    }
72
73    /// Parse from string (case-insensitive).
74    pub fn from_str(s: &str) -> Option<Self> {
75        match s.to_lowercase().as_str() {
76            "down" => Some(Direction::Down),
77            "up" => Some(Direction::Up),
78            "north" => Some(Direction::North),
79            "south" => Some(Direction::South),
80            "west" => Some(Direction::West),
81            "east" => Some(Direction::East),
82            _ => None,
83        }
84    }
85
86    /// Rotate this direction by X rotation (around X axis, in 90-degree increments).
87    /// Looking from +X towards origin, positive rotation goes Up -> North -> Down -> South.
88    pub fn rotate_x(self, degrees: i32) -> Direction {
89        let steps = ((degrees / 90) % 4 + 4) % 4;
90        let mut dir = self;
91        for _ in 0..steps {
92            dir = match dir {
93                Direction::Up => Direction::North,
94                Direction::North => Direction::Down,
95                Direction::Down => Direction::South,
96                Direction::South => Direction::Up,
97                // X rotation doesn't affect East/West
98                Direction::East => Direction::East,
99                Direction::West => Direction::West,
100            };
101        }
102        dir
103    }
104
105    /// Rotate this direction by Y rotation (around Y axis, in 90-degree increments).
106    /// Looking from +Y (above), positive rotation goes North -> East -> South -> West.
107    pub fn rotate_y(self, degrees: i32) -> Direction {
108        let steps = ((degrees / 90) % 4 + 4) % 4;
109        let mut dir = self;
110        for _ in 0..steps {
111            dir = match dir {
112                Direction::North => Direction::East,
113                Direction::East => Direction::South,
114                Direction::South => Direction::West,
115                Direction::West => Direction::North,
116                // Y rotation doesn't affect Up/Down
117                Direction::Up => Direction::Up,
118                Direction::Down => Direction::Down,
119            };
120        }
121        dir
122    }
123
124    /// Rotate this direction by a block transform (X then Y rotation).
125    pub fn rotate_by_transform(self, x_rot: i32, y_rot: i32) -> Direction {
126        // Apply X rotation first, then Y rotation (same order as geometry transform)
127        self.rotate_x(x_rot).rotate_y(y_rot)
128    }
129}
130
131impl std::fmt::Display for Direction {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        match self {
134            Direction::Down => write!(f, "down"),
135            Direction::Up => write!(f, "up"),
136            Direction::North => write!(f, "north"),
137            Direction::South => write!(f, "south"),
138            Direction::West => write!(f, "west"),
139            Direction::East => write!(f, "east"),
140        }
141    }
142}
143
144/// The three axes.
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
146#[serde(rename_all = "lowercase")]
147pub enum Axis {
148    X,
149    Y,
150    Z,
151}
152
153impl Axis {
154    /// Get the unit vector for this axis.
155    pub fn unit_vector(&self) -> [f32; 3] {
156        match self {
157            Axis::X => [1.0, 0.0, 0.0],
158            Axis::Y => [0.0, 1.0, 0.0],
159            Axis::Z => [0.0, 0.0, 1.0],
160        }
161    }
162
163    /// Parse from string (case-insensitive).
164    pub fn from_str(s: &str) -> Option<Self> {
165        match s.to_lowercase().as_str() {
166            "x" => Some(Axis::X),
167            "y" => Some(Axis::Y),
168            "z" => Some(Axis::Z),
169            _ => None,
170        }
171    }
172}
173
174impl std::fmt::Display for Axis {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        match self {
177            Axis::X => write!(f, "x"),
178            Axis::Y => write!(f, "y"),
179            Axis::Z => write!(f, "z"),
180        }
181    }
182}