feat(exporters): implement image_dimensions with TDD
This commit is contained in:
@@ -1,5 +1,55 @@
|
||||
use lib::{BlockPalette, StructureExporter, VoxelGrid, VoxelType};
|
||||
|
||||
const GAP: u32 = 1;
|
||||
|
||||
// Returns VoxelTypes that (1) are configured in the palette AND (2) appear in the grid.
|
||||
fn active_voxel_types(palette: &BlockPalette, grid: &VoxelGrid) -> Vec<VoxelType> {
|
||||
let mut candidates = vec![VoxelType::Body];
|
||||
if palette.blocks.outline.is_some() {
|
||||
candidates.push(VoxelType::Outline);
|
||||
}
|
||||
if palette.blocks.shadow.is_some() {
|
||||
candidates.push(VoxelType::Shadow);
|
||||
}
|
||||
candidates.retain(|&vt| {
|
||||
(0..grid.depth).any(|z|
|
||||
(0..grid.height).any(|y|
|
||||
(0..grid.width).any(|x| grid.get(x, y, z) == Some(vt))
|
||||
)
|
||||
)
|
||||
});
|
||||
candidates
|
||||
}
|
||||
|
||||
pub fn image_dimensions(grid: &VoxelGrid, palette: &BlockPalette, cell_size: u32) -> (u32, u32) {
|
||||
let label_height = cell_size + 4;
|
||||
let per_layer_height = if grid.height == 0 {
|
||||
0
|
||||
} else {
|
||||
grid.height * (cell_size + GAP) - GAP
|
||||
};
|
||||
let layer_block_height = label_height + GAP + per_layer_height;
|
||||
let total_grid_height = grid.depth * layer_block_height
|
||||
+ grid.depth.saturating_sub(1) * GAP;
|
||||
|
||||
let active = active_voxel_types(palette, grid);
|
||||
let legend_height = if active.is_empty() {
|
||||
0
|
||||
} else {
|
||||
active.len() as u32 * (cell_size + GAP) + GAP
|
||||
};
|
||||
|
||||
let img_width = if grid.width == 0 {
|
||||
1
|
||||
} else {
|
||||
GAP + grid.width * (cell_size + GAP)
|
||||
};
|
||||
let img_height = total_grid_height
|
||||
+ if legend_height > 0 { GAP + legend_height } else { 0 };
|
||||
|
||||
(img_width, img_height)
|
||||
}
|
||||
|
||||
pub struct PngExporter {
|
||||
palette: BlockPalette,
|
||||
cell_size: u32,
|
||||
@@ -20,3 +70,50 @@ impl StructureExporter for PngExporter {
|
||||
"png"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn palette_full() -> BlockPalette {
|
||||
serde_json::from_str(r#"{
|
||||
"name": "test",
|
||||
"blocks": {
|
||||
"body": "minecraft:stone",
|
||||
"outline": "minecraft:cobblestone",
|
||||
"shadow": "minecraft:gravel"
|
||||
}
|
||||
}"#).unwrap()
|
||||
}
|
||||
|
||||
fn palette_body_only() -> BlockPalette {
|
||||
serde_json::from_str(r#"{
|
||||
"name": "test",
|
||||
"blocks": { "body": "minecraft:stone" }
|
||||
}"#).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimensions_single_layer() {
|
||||
let grid = VoxelGrid::new(4, 3, 1);
|
||||
let palette = palette_full();
|
||||
let (w, h) = image_dimensions(&grid, &palette, 16);
|
||||
// width: 1 + 4*(16+1) = 69
|
||||
assert_eq!(w, 69);
|
||||
// label_height = 20, per_layer = 3*17-1 = 50, layer_block = 71
|
||||
// total_grid = 71
|
||||
// legend = 3*17 + 1 = 52 (BUT: all-None grid → 0 active types → legend_height=0)
|
||||
// total_h = 71 + 0 = 71
|
||||
assert_eq!(h, 71);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dimensions_multi_layer() {
|
||||
let g1 = VoxelGrid::new(4, 3, 1);
|
||||
let g3 = VoxelGrid::new(4, 3, 3);
|
||||
let palette = palette_full();
|
||||
let (_, h1) = image_dimensions(&g1, &palette, 16);
|
||||
let (_, h3) = image_dimensions(&g3, &palette, 16);
|
||||
assert!(h3 > h1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user