schematic_mesher/types/
mod.rs

1//! Shared types used throughout the library.
2
3mod direction;
4mod transform;
5
6pub use direction::{Direction, Axis};
7pub use transform::{BlockTransform, ElementRotation};
8
9use std::collections::HashMap;
10
11/// A block position in 3D space.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct BlockPosition {
14    pub x: i32,
15    pub y: i32,
16    pub z: i32,
17}
18
19impl BlockPosition {
20    pub fn new(x: i32, y: i32, z: i32) -> Self {
21        Self { x, y, z }
22    }
23
24    /// Get the neighboring position in the given direction.
25    pub fn neighbor(&self, direction: Direction) -> Self {
26        let (dx, dy, dz) = direction.offset();
27        Self {
28            x: self.x + dx,
29            y: self.y + dy,
30            z: self.z + dz,
31        }
32    }
33}
34
35/// An axis-aligned bounding box.
36#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct BoundingBox {
38    pub min: [f32; 3],
39    pub max: [f32; 3],
40}
41
42impl BoundingBox {
43    pub fn new(min: [f32; 3], max: [f32; 3]) -> Self {
44        Self { min, max }
45    }
46
47    pub fn from_points(points: impl Iterator<Item = [f32; 3]>) -> Option<Self> {
48        let mut min = [f32::MAX; 3];
49        let mut max = [f32::MIN; 3];
50        let mut has_points = false;
51
52        for p in points {
53            has_points = true;
54            for i in 0..3 {
55                min[i] = min[i].min(p[i]);
56                max[i] = max[i].max(p[i]);
57            }
58        }
59
60        if has_points {
61            Some(Self { min, max })
62        } else {
63            None
64        }
65    }
66
67    pub fn dimensions(&self) -> [f32; 3] {
68        [
69            self.max[0] - self.min[0],
70            self.max[1] - self.min[1],
71            self.max[2] - self.min[2],
72        ]
73    }
74}
75
76/// Input block for meshing, compatible with Nucleation's BlockState.
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct InputBlock {
79    /// Block name, e.g., "minecraft:stone"
80    pub name: String,
81    /// Block properties, e.g., {"facing": "north"}
82    pub properties: HashMap<String, String>,
83}
84
85impl InputBlock {
86    pub fn new(name: impl Into<String>) -> Self {
87        Self {
88            name: name.into(),
89            properties: HashMap::new(),
90        }
91    }
92
93    pub fn with_property(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
94        self.properties.insert(key.into(), value.into());
95        self
96    }
97
98    /// Get the namespace (e.g., "minecraft").
99    pub fn namespace(&self) -> &str {
100        self.name.split(':').next().unwrap_or("minecraft")
101    }
102
103    /// Get the block ID without namespace (e.g., "stone").
104    pub fn block_id(&self) -> &str {
105        self.name.split(':').nth(1).unwrap_or(&self.name)
106    }
107
108    /// Check if this is an air block.
109    pub fn is_air(&self) -> bool {
110        matches!(
111            self.name.as_str(),
112            "minecraft:air" | "minecraft:cave_air" | "minecraft:void_air" | "air"
113        )
114    }
115}
116
117/// Trait for block data sources (allows integration with Nucleation).
118pub trait BlockSource {
119    /// Get the block at a position.
120    fn get_block(&self, pos: BlockPosition) -> Option<&InputBlock>;
121
122    /// Iterate over all non-air blocks.
123    fn iter_blocks(&self) -> Box<dyn Iterator<Item = (BlockPosition, &InputBlock)> + '_>;
124
125    /// Get the bounding box of all blocks.
126    fn bounds(&self) -> BoundingBox;
127}