use super::{PREVIEW_DISTANCE, PREVIEW_YAW, PreviewView, encode_png};
use glam::Vec3;
use indicatrix::{
geometry::plane::GpuFacetPlane,
optics::raytracer::{Camera, DEFAULT_FOV_DEG},
};
use indicatrix_solid::{
mesh_cache::MeshCache,
raster::{SolidRasterizer, SolidStyle},
};
#[must_use]
pub fn render_view_solid(
planes: &[GpuFacetPlane],
size: u32,
view: PreviewView,
) -> Option<Vec<u8>> {
if size == 0 {
return None;
}
let half_spaces: Vec<(Vec3, f32)> = planes
.iter()
.map(|plane| (Vec3::from(plane.normal), -plane.d))
.collect();
let mut cache = MeshCache::default();
let mesh = cache.get_or_build(&half_spaces)?;
let camera = Camera::new(PREVIEW_YAW, view.pitch(), PREVIEW_DISTANCE, DEFAULT_FOV_DEG);
let style = SolidStyle {
background: [0, 0, 0, 255],
..SolidStyle::default()
};
let mut raster = SolidRasterizer::new(size, size);
raster.render_prepared(mesh, &camera, &style);
encode_png(size, size, &raster.color)
}
#[cfg(test)]
mod tests {
use super::*;
fn cube() -> Vec<GpuFacetPlane> {
[
Vec3::X,
Vec3::NEG_X,
Vec3::Y,
Vec3::NEG_Y,
Vec3::Z,
Vec3::NEG_Z,
]
.into_iter()
.map(|n| GpuFacetPlane::new(n, -0.5))
.collect()
}
#[test]
fn a_closed_stone_draws_a_png_of_the_requested_size_with_painted_pixels() {
let png = render_view_solid(&cube(), 48, PreviewView::Top).expect("a cube closes");
let decoded = image::load_from_memory(&png).unwrap().to_rgba8();
assert_eq!(decoded.dimensions(), (48, 48));
assert!(
decoded.pixels().any(|p| p.0[..3] != [0, 0, 0]),
"something other than the background was painted"
);
}
#[test]
fn an_open_arrangement_and_a_zero_size_draw_nothing() {
assert!(render_view_solid(&cube()[..3], 32, PreviewView::Front).is_none());
assert!(render_view_solid(&cube(), 0, PreviewView::Front).is_none());
}
}