use crate::marching_cubes::{CellData, MarchingCubesInput, RelativeToThreshold};
use crate::uniform_grid::CellIndex;
use crate::{DensityMap, Index, MapType, Real, UniformGrid, profile};
use log::trace;
use nalgebra::Vector3;
pub(crate) fn construct_mc_input<I: Index, R: Real>(
grid: &UniformGrid<I, R>,
density_map: &DensityMap<I, R>,
iso_surface_threshold: R,
vertices: &mut Vec<Vector3<R>>,
) -> MarchingCubesInput<I> {
let mut marching_cubes_data = MarchingCubesInput::default();
interpolate_points_to_cell_data_generic::<I, R>(
grid,
density_map,
iso_surface_threshold,
vertices,
&mut marching_cubes_data,
);
update_cell_data_threshold_flags(
grid,
density_map,
iso_surface_threshold,
true,
&mut marching_cubes_data,
);
marching_cubes_data
}
#[inline(never)]
fn interpolate_points_to_cell_data_generic<I: Index, R: Real>(
grid: &UniformGrid<I, R>,
density_map: &DensityMap<I, R>,
iso_surface_threshold: R,
vertices: &mut Vec<Vector3<R>>,
marching_cubes_data: &mut MarchingCubesInput<I>,
) {
profile!("interpolate_points_to_cell_data_generic");
trace!(
"Starting interpolation of cell data for marching cubes (excluding boundary layer)... (Input: {} existing vertices)",
vertices.len()
);
let cell_data: &mut MapType<I, CellData> = &mut marching_cubes_data.cell_data;
{
profile!("generate_iso_surface_vertices");
density_map.for_each(|flat_point_index, point_value| {
let point = grid.try_unflatten_point_index(flat_point_index).unwrap();
if point_value < iso_surface_threshold {
return;
}
let neighborhood = grid.get_point_neighborhood(&point);
for neighbor_edge in neighborhood.neighbor_edge_iter() {
let neighbor = neighbor_edge.neighbor_index();
let flat_neighbor_index = grid.flatten_point_index(neighbor);
let neighbor_value = if let Some(v) = density_map.get(flat_neighbor_index) {
v
} else {
R::zero()
};
if !(neighbor_value < iso_surface_threshold) {
continue;
}
let alpha = (iso_surface_threshold - point_value) / (neighbor_value - point_value);
let point_coords = grid.point_coordinates(&point);
let neighbor_coords = grid.point_coordinates(neighbor);
let interpolated_coords =
(point_coords) * (R::one() - alpha) + neighbor_coords * alpha;
let vertex_index = vertices.len();
vertices.push(interpolated_coords);
for cell in grid.cells_adjacent_to_edge(&neighbor_edge).iter().flatten() {
let flat_cell_index = grid.flatten_cell_index(cell);
let cell_data_entry = cell_data.entry(flat_cell_index).or_default();
let local_edge_index = cell.local_edge_index_of(&neighbor_edge).unwrap();
assert!(
cell_data_entry.iso_surface_vertices[local_edge_index].is_none(),
"Overwriting already existing vertex. This is a bug."
);
cell_data_entry.iso_surface_vertices[local_edge_index] = Some(vertex_index);
let local_vertex_index = cell.local_point_index_of(point.index()).unwrap();
cell_data_entry.corner_above_threshold[local_vertex_index] =
RelativeToThreshold::Above;
}
}
});
}
trace!(
"Cell data interpolation done. (Output: cell data for marching cubes with {} cells and {} vertices)",
cell_data.len(),
vertices.len()
);
}
fn update_cell_data_threshold_flags<I: Index, R: Real>(
grid: &UniformGrid<I, R>,
density_map: &DensityMap<I, R>,
iso_surface_threshold: R,
skip_points_above_threshold: bool,
marching_cubes_input: &mut MarchingCubesInput<I>,
) {
profile!("relative_to_threshold_postprocessing");
let update_corner_flags =
|cell: &CellIndex<I>, local_point_index: usize, flag: &mut RelativeToThreshold| {
let point = cell.global_point_index_of(local_point_index).unwrap();
let flat_point_index = grid.flatten_point_index(&point);
*flag = {
if let Some(point_value) = density_map.get(flat_point_index) {
if point_value > iso_surface_threshold {
RelativeToThreshold::Above
} else {
RelativeToThreshold::Below
}
} else {
RelativeToThreshold::Below
}
}
};
if skip_points_above_threshold {
for (&flat_cell_index, cell_data) in marching_cubes_input.cell_data.iter_mut() {
let cell = grid.try_unflatten_cell_index(flat_cell_index).unwrap();
cell_data
.corner_above_threshold
.iter_mut()
.enumerate()
.filter(|(_, flag)| **flag != RelativeToThreshold::Above)
.for_each(|(local_point_index, flag)| {
update_corner_flags(&cell, local_point_index, flag)
});
}
} else {
for (&flat_cell_index, cell_data) in marching_cubes_input.cell_data.iter_mut() {
let cell = grid.try_unflatten_cell_index(flat_cell_index).unwrap();
cell_data
.corner_above_threshold
.iter_mut()
.enumerate()
.for_each(|(local_point_index, flag)| {
update_corner_flags(&cell, local_point_index, flag)
});
}
}
}