use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use crate::dual_contouring::DualContouring;
use crate::fields::ReferenceField;
use crate::greedy_quads::GreedyQuads;
use crate::manifold_dual_contouring::ManifoldDualContouring;
use crate::marching_cubes::{FaceAmbiguity, MarchingCubes};
use crate::marching_tetrahedra::MarchingTetrahedra;
use crate::surface_nets::SurfaceNets;
use crate::{MeshBuffer, RuntimeShape3, Sdf};
const RESOLUTIONS: [u32; 3] = [17, 25, 33];
use crate::validate::mesh_hash;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Algorithm {
GreedyQuads,
MarchingCubes,
MarchingCubes33,
MarchingCubes33Trilinear,
MarchingTetrahedra,
SurfaceNets,
DualContouring,
ManifoldDualContouring,
SubgridMarchingTetrahedra,
}
const SUBGRID_SAMPLES: u32 = 16;
impl Algorithm {
const ALL: [Self; 9] = [
Self::GreedyQuads,
Self::MarchingCubes,
Self::MarchingCubes33,
Self::MarchingCubes33Trilinear,
Self::MarchingTetrahedra,
Self::SurfaceNets,
Self::DualContouring,
Self::ManifoldDualContouring,
Self::SubgridMarchingTetrahedra,
];
fn name(self) -> &'static str {
match self {
Self::GreedyQuads => "greedy_quads",
Self::MarchingCubes => "marching_cubes",
Self::MarchingCubes33 => "marching_cubes+decider",
Self::MarchingCubes33Trilinear => "marching_cubes+trilinear",
Self::MarchingTetrahedra => "marching_tetrahedra",
Self::SurfaceNets => "surface_nets",
Self::DualContouring => "dual_contouring",
Self::ManifoldDualContouring => "manifold_dual_contouring",
Self::SubgridMarchingTetrahedra => "subgrid_marching_tetrahedra",
}
}
}
struct Entry {
algorithm: &'static str,
field: &'static str,
samples: u32,
vertices: usize,
triangles: usize,
hash: u64,
}
fn extract<F>(algorithm: Algorithm, field: &F, samples: u32) -> MeshBuffer<f64>
where
F: Sdf<Scalar = f64> + ReferenceField,
{
let (lo, hi) = field.domain();
let cell_size = (hi[0] - lo[0]) / f64::from(samples - 1);
let shape = RuntimeShape3::new([samples; 3]).expect("valid shape");
let mut out = MeshBuffer::<f64>::new();
match algorithm {
Algorithm::GreedyQuads => GreedyQuads::<f64>::new()
.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction"),
Algorithm::MarchingCubes => MarchingCubes::<f64>::new()
.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction"),
Algorithm::MarchingCubes33 => {
let mut mc = MarchingCubes::<f64>::new();
mc.set_face_ambiguity(FaceAmbiguity::AsymptoticDecider);
mc.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction");
}
Algorithm::MarchingCubes33Trilinear => {
let mut mc = MarchingCubes::<f64>::new();
mc.set_face_ambiguity(FaceAmbiguity::AsymptoticDecider);
mc.set_interior_ambiguity(crate::marching_cubes::InteriorAmbiguity::Trilinear);
mc.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction");
}
Algorithm::MarchingTetrahedra => MarchingTetrahedra::<f64>::new()
.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction"),
Algorithm::SurfaceNets => SurfaceNets::<f64>::new()
.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction"),
Algorithm::DualContouring => DualContouring::<f64>::new()
.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction"),
Algorithm::ManifoldDualContouring => ManifoldDualContouring::<f64>::new()
.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction"),
Algorithm::SubgridMarchingTetrahedra => {
crate::subgrid::extract::SubgridMarchingTetrahedra::<f64>::new(SUBGRID_SAMPLES)
.expect("a positive sampling resolution")
.extract(field, &shape, lo, cell_size, &mut out)
.expect("extraction");
}
}
out
}
fn compute_all() -> Vec<Entry> {
let mut entries = Vec::new();
crate::for_each_reference_field!(f64, |name, field| {
for algorithm in Algorithm::ALL {
for samples in RESOLUTIONS {
let mesh = extract(algorithm, &field, samples);
entries.push(Entry {
algorithm: algorithm.name(),
field: name,
samples,
vertices: mesh.vertex_count(),
triangles: mesh.triangle_count(),
hash: mesh_hash(&mesh),
});
}
}
});
entries
}
fn fixture_path() -> std::path::PathBuf {
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("golden_hashes.json")
}
fn render(entries: &[Entry]) -> String {
let mut out = String::from("[\n");
for (i, e) in entries.iter().enumerate() {
let comma = if i + 1 == entries.len() { "" } else { "," };
out.push_str(&format!(
" {{\"algorithm\":\"{}\",\"field\":\"{}\",\"samples\":{},\"vertices\":{},\"triangles\":{},\"hash\":\"{:016x}\"}}{comma}\n",
e.algorithm, e.field, e.samples, e.vertices, e.triangles, e.hash
));
}
out.push_str("]\n");
out
}
fn field_of<'a>(line: &'a str, key: &str) -> Option<&'a str> {
let needle = format!("\"{key}\":");
let start = line.find(&needle)? + needle.len();
let rest = &line[start..];
let rest = rest.strip_prefix('"').unwrap_or(rest);
let end = rest.find(['"', ',', '}'])?;
Some(&rest[..end])
}
#[cfg(test)]
mod tests;