//! Draw a 3 dimensional shape using solid
//! This draws the classic csg example in the wiki on
//! [Constructive Solid Geometry](https://en.wikipedia.org/wiki/Constructive_solid_geometry)
//!
//! >>> cargo run --example csg
//!
//! Writes an .stl file to target/stl/csg.stl
use crater::{
    bounding::BoundingBox,
    csg::{
        algebra::CSGAlgebra,
        marching_cubes::{marching_cubes, MarchingCubesParams},
        regions::Region,
        surfaces::{IntoSurface, Surface3D},
        transformations::{RotateAbout, Translate},
    },
    mesh::MeshCollection,
    serde::vtk::evaluations::{GeometryEvaluator, MeshColoring},
    utils::Parallel,
};

fn make_csg() -> Region<3> {
    let c_r = 1.0;
    let s_r = 2.0 * c_r;
    let b_r = 0.7 * s_r;
    let c1 = -Surface3D::Cylinder { r: c_r }.into_surface();
    let c2 = -Surface3D::Cylinder { r: c_r }
        .into_surface()
        .transform(RotateAbout(2, 0, std::f64::consts::FRAC_PI_2));
    let c3 = -Surface3D::Cylinder { r: c_r }
        .into_surface()
        .transform(RotateAbout(2, 1, std::f64::consts::FRAC_PI_2));

    let sphere = -Surface3D::Sphere { r: s_r }.into_surface();

    let box_sides = (
        Surface3D::Plane {
            normal: [1.0, 0.0, 0.0],
        }
        .into_surface()
        .transform(Translate([b_r, 0.0, 0.0])),
        Surface3D::Plane {
            normal: [1.0, 0.0, 0.0],
        }
        .into_surface()
        .transform(Translate([-b_r, 0.0, 0.0])),
        Surface3D::Plane {
            normal: [0.0, 1.0, 0.0],
        }
        .into_surface()
        .transform(Translate([0.0, b_r, 0.0])),
        Surface3D::Plane {
            normal: [0.0, 1.0, 0.0],
        }
        .into_surface()
        .transform(Translate([0.0, -b_r, 0.0])),
        Surface3D::Plane {
            normal: [0.0, 0.0, 1.0],
        }
        .into_surface()
        .transform(Translate([0.0, 0.0, b_r])),
        Surface3D::Plane {
            normal: [0.0, 0.0, 1.0],
        }
        .into_surface()
        .transform(Translate([0.0, 0.0, -b_r])),
    );

    let box_region = -box_sides.0
        & -(-box_sides.1)
        & -box_sides.2
        & -(-box_sides.3)
        & -box_sides.4
        & -(-box_sides.5);

    (sphere & box_region) & -(c1 | c2 | c3)
}

fn main() {
    // Run marching cubes to render this shape
    let resolution = 30;
    let bound = 3.0;
    let algebra = CSGAlgebra::default();
    let params = MarchingCubesParams {
        region: make_csg(),
        bounds: BoundingBox {
            min: [-bound, -bound, -bound],
            max: [bound, bound, bound],
        },
        resolution: (resolution, resolution, resolution),
        algebra,
    };
    let mesh: MeshCollection = marching_cubes::<Parallel>(&params).into();

    let mut path_buf = std::path::PathBuf::from("./target/test_renderings/");
    path_buf.push("csg");
    path_buf.set_extension("stl");
    std::fs::create_dir_all(path_buf.parent().expect("Failed to get parent directory"))
        .expect("Failed to create directory");
    mesh.write_stl(path_buf.to_str().unwrap())
        .expect("Failed to write csg.stl");

    // VTK
    let csg = [make_csg()];
    let algebra = CSGAlgebra::default();
    mesh.to_vtk(Some(&[
        Box::new(GeometryEvaluator::Gradients(&csg, &algebra)),
        Box::new(GeometryEvaluator::Normals(&csg, &algebra)),
        Box::new(MeshColoring),
    ]))
    .export_ascii(path_buf.with_extension("vtk").to_str().unwrap())
    .unwrap();
}