//! Generate a time-parameterized animation of a CSG object.
//!
//! > cargo run --example animation
//!
//! The animation will be saved as a series of VTK files in the `target/test_renderings/animation` directory.
//! Applications like ParaView can natively render groups of VTK files as an animation.

use crater::{
    bounding::BoundingBox,
    csg::{
        algebra::CSGAlgebra,
        marching_cubes::{marching_cubes, MarchingCubesParams},
        regions::{Region, Side},
        surfaces::Surface,
        transformations::Translate,
    },
    mesh::MeshCollection,
    serde::vtk::evaluations::GeometryEvaluator,
    utils::Parallel,
};

/// Emit a [`Region`] that is parameterized by time.
fn region_at_time(t: f64) -> Region<3> {
    let omega = 100.0 / (2.0 * std::f64::consts::PI);
    let a = 1.0;
    let (kx, ky) = (1.0, 1.0);

    // Make a wavy surface
    let top = -Surface::parse_str(
        format!("z - {a} * (math::sin(math::sqrt({kx} * x^2 + {ky} * y^2) - {omega} * {t}))",)
            .as_str(),
    )
    .unwrap();

    // Make another wavy surface
    let bottom = Region::HalfSpace(
        Surface::parse_str(
            format!("z - {a} * (math::cos(math::sqrt({kx} * x^2 + {ky} * y^2) - {omega} * {t}))",)
                .as_str(),
        )
        .unwrap()
        .transform(Translate([0.0, 0.0, -3.0])),
        Side::Positive,
    );

    // Consider the region between the two wavy surfaces
    top & bottom
}

fn main() {
    let mut path_buf = std::path::PathBuf::from("./target/test_renderings/");
    path_buf.push("animation");

    std::fs::create_dir_all(&path_buf).expect("Failed to create directory");
    let resolution = 50;
    let t_max = 10;
    let bound = 10.0;
    let bounds = BoundingBox {
        min: [-bound, -bound, -bound],
        max: [bound, bound, bound],
    };

    for t in 0..t_max {
        let region = region_at_time(t as f64 / 100.0);
        // TODO: Params should not need to take ownership of the region/algebra
        let mesh: MeshCollection = marching_cubes::<Parallel>(&MarchingCubesParams {
            region,
            resolution: (resolution, resolution, resolution),
            bounds,
            algebra: CSGAlgebra::default(),
        })
        .into();

        let mut frame_path = path_buf.clone();
        frame_path.push(format!("frame_{:04}", t));
        frame_path.set_extension("vtk");

        // Write the mesh to VTK:
        let algebra = CSGAlgebra::default();
        let regions = [region_at_time(t as f64 / 100.0)];
        mesh.to_vtk(Some(&[
            Box::new(GeometryEvaluator::Gradients(&regions, &algebra)),
            // Box::new(GeometryEvaluator::Normals(&regions, &algebra)),
            // Box::new(GeometryEvaluator::Curvature(&regions, &algebra)),
            // Box::new(MeshColoring),
        ]))
        .export_be(frame_path)
        .expect("Failed to write frame");
    }
}