Skip to content

Commit 31c0bfe

Browse files
Make SurfaceReconstruction an inout param (#7)
Allow passing an existing `SurfaceReconstruction` to the reconstruction procedure to reuse memory allocated for the surface mesh.
1 parent 9f9f5f8 commit 31c0bfe

5 files changed

Lines changed: 95 additions & 46 deletions

File tree

splashsurf/src/reconstruction.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ pub(crate) fn entry_point_generic<I: Index, R: Real>(
5353
splashsurf_lib::reconstruct_surface::<I, R>(particle_positions.as_slice(), &params)?;
5454

5555
let grid = reconstruction.grid();
56-
let density_map = reconstruction.density_map();
56+
let density_map = reconstruction
57+
.density_map()
58+
.ok_or_else(|| anyhow::anyhow!("No density map was created during reconstruction"))?;
5759
let mesh = reconstruction.mesh();
5860

5961
{

splashsurf_lib/src/lib.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -146,20 +146,32 @@ pub struct SurfaceReconstruction<I: Index, R: Real> {
146146
/// The background grid that was used as a basis for generating the density map for marching cubes
147147
grid: UniformGrid<I, R>,
148148
/// The point-based density map generated from the particles that was used as input to marching cubes
149-
density_map: DensityMap<I, R>,
149+
density_map: Option<DensityMap<I, R>>,
150150
/// The actual mesh that is the result of the surface reconstruction
151151
mesh: TriMesh3d<R>,
152152
}
153153

154+
impl<I: Index, R: Real> Default for SurfaceReconstruction<I, R> {
155+
fn default() -> Self {
156+
let grid = UniformGrid::new_zero();
157+
let mesh = TriMesh3d::default();
158+
Self {
159+
grid,
160+
density_map: None,
161+
mesh,
162+
}
163+
}
164+
}
165+
154166
impl<I: Index, R: Real> SurfaceReconstruction<I, R> {
155167
/// Returns a reference to the actual triangulated surface mesh that is the result of the reconstruction
156168
pub fn mesh(&self) -> &TriMesh3d<R> {
157169
&self.mesh
158170
}
159171

160172
/// Returns a reference to the sparse density map (discretized on the vertices of the background grid) that is used as input for marching cubes
161-
pub fn density_map(&self) -> &DensityMap<I, R> {
162-
&self.density_map
173+
pub fn density_map(&self) -> Option<&DensityMap<I, R>> {
174+
self.density_map.as_ref()
163175
}
164176

165177
/// Returns a reference to the virtual background grid that was used as a basis for discretization of the density map for marching cubes, can be used to convert the density map to a hex mesh (using [sparse_density_map_to_hex_mesh](density_map::sparse_density_map_to_hex_mesh))
@@ -205,6 +217,17 @@ pub fn reconstruct_surface<I: Index, R: Real>(
205217
parameters: &Parameters<R>,
206218
) -> Result<SurfaceReconstruction<I, R>, ReconstructionError<I, R>> {
207219
profile!("reconstruct_surface");
220+
let mut surface = SurfaceReconstruction::default();
221+
reconstruct_surface_inplace(particle_positions, parameters, &mut surface)?;
222+
Ok(surface)
223+
}
224+
225+
pub fn reconstruct_surface_inplace<'a, I: Index, R: Real>(
226+
particle_positions: &[Vector3<R>],
227+
parameters: &Parameters<R>,
228+
surface: &'a mut SurfaceReconstruction<I, R>,
229+
) -> Result<(), ReconstructionError<I, R>> {
230+
profile!("reconstruct_surface_inplace");
208231

209232
let Parameters {
210233
particle_radius,
@@ -217,12 +240,13 @@ pub fn reconstruct_surface<I: Index, R: Real>(
217240
enable_multi_threading,
218241
} = parameters.clone();
219242

220-
let grid = grid_for_reconstruction(
243+
surface.grid = grid_for_reconstruction(
221244
particle_positions,
222245
particle_radius,
223246
cube_size,
224247
domain_aabb.as_ref(),
225248
)?;
249+
let grid = &surface.grid;
226250

227251
info!(
228252
"Using a grid with {:?}x{:?}x{:?} points and {:?}x{:?}x{:?} cells of edge length {}.",
@@ -291,14 +315,16 @@ pub fn reconstruct_surface<I: Index, R: Real>(
291315
enable_multi_threading,
292316
);
293317

294-
let mesh =
295-
marching_cubes::triangulate_density_map::<I, R>(&grid, &density_map, iso_surface_threshold);
318+
marching_cubes::triangulate_density_map::<I, R>(
319+
&grid,
320+
&density_map,
321+
iso_surface_threshold,
322+
&mut surface.mesh,
323+
);
324+
325+
surface.density_map = Some(density_map);
296326

297-
Ok(SurfaceReconstruction {
298-
grid,
299-
density_map,
300-
mesh,
301-
})
327+
Ok(())
302328
}
303329

304330
/// Constructs the background grid for marching cubes based on the parameters supplied to the surface reconstruction

splashsurf_lib/src/marching_cubes.rs

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use log::info;
2-
use nalgebra::Vector3;
32

43
use crate::marching_cubes_lut::get_marching_cubes_triangulation;
54
use crate::mesh::TriMesh3d;
@@ -10,11 +9,12 @@ pub fn triangulate_density_map<I: Index, R: Real>(
109
grid: &UniformGrid<I, R>,
1110
density_map: &DensityMap<I, R>,
1211
iso_surface_threshold: R,
13-
) -> TriMesh3d<R> {
12+
mesh: &mut TriMesh3d<R>,
13+
) {
1414
profile!("triangulate_density_map");
1515
let marching_cubes_data =
16-
interpolate_points_to_cell_data::<I, R>(&grid, &density_map, iso_surface_threshold);
17-
triangulate::<I, R>(marching_cubes_data)
16+
interpolate_points_to_cell_data::<I, R>(&grid, &density_map, iso_surface_threshold, mesh);
17+
triangulate::<I, R>(marching_cubes_data, mesh)
1818
}
1919

2020
/// Flag indicating whether a vertex is above or below the iso-surface
@@ -73,9 +73,7 @@ impl Default for CellData {
7373

7474
/// Input for the marching cubes algorithm
7575
#[derive(Clone, Debug)]
76-
pub(crate) struct MarchingCubesInput<I: Index, R: Real> {
77-
/// All interpolated vertices on the iso-surface
78-
vertices: Vec<Vector3<R>>,
76+
pub(crate) struct MarchingCubesInput<I: Index> {
7977
/// Data for all cells that marching cubes has to visit
8078
cell_data: MapType<I, CellData>,
8179
}
@@ -91,7 +89,8 @@ pub(crate) fn interpolate_points_to_cell_data<I: Index, R: Real>(
9189
grid: &UniformGrid<I, R>,
9290
density_map: &DensityMap<I, R>,
9391
iso_surface_threshold: R,
94-
) -> MarchingCubesInput<I, R> {
92+
mesh: &mut TriMesh3d<R>,
93+
) -> MarchingCubesInput<I> {
9594
profile!("interpolate_points_to_cell_data");
9695

9796
// Note: This functions assumes that the default value for missing point data is below the iso-surface threshold
@@ -100,7 +99,8 @@ pub(crate) fn interpolate_points_to_cell_data<I: Index, R: Real>(
10099
// Map from flat cell index to all data that is required per cell for the marching cubes triangulation
101100
let mut cell_data: MapType<I, CellData> = new_map();
102101
// Storage for vertices that are created on edges crossing the iso-surface
103-
let mut vertices = Vec::new();
102+
let vertices = &mut mesh.vertices;
103+
vertices.clear();
104104

105105
// Generate iso-surface vertices and identify affected cells & edges
106106
{
@@ -227,31 +227,25 @@ pub(crate) fn interpolate_points_to_cell_data<I: Index, R: Real>(
227227
);
228228
info!("Interpolation done.");
229229

230-
MarchingCubesInput {
231-
vertices,
232-
cell_data,
233-
}
230+
MarchingCubesInput { cell_data }
234231
}
235232

236233
/// Converts the marching cubes input cell data into a triangle surface mesh
237234
#[inline(never)]
238-
pub(crate) fn triangulate<I: Index, R: Real>(input: MarchingCubesInput<I, R>) -> TriMesh3d<R> {
235+
pub(crate) fn triangulate<I: Index, R: Real>(
236+
input: MarchingCubesInput<I>,
237+
mesh: &mut TriMesh3d<R>,
238+
) {
239239
profile!("triangulate");
240240

241-
let MarchingCubesInput {
242-
vertices,
243-
cell_data,
244-
} = input;
241+
let MarchingCubesInput { cell_data } = input;
245242

246243
info!(
247244
"Starting marching cubes triangulation of {} cells...",
248245
cell_data.len()
249246
);
250247

251-
let mut mesh = TriMesh3d {
252-
vertices,
253-
triangles: Vec::new(),
254-
};
248+
mesh.triangles.clear();
255249

256250
// Triangulate affected cells
257251
for (&_flat_cell_index, cell_data) in &cell_data {
@@ -273,8 +267,6 @@ pub(crate) fn triangulate<I: Index, R: Real>(input: MarchingCubesInput<I, R>) ->
273267
mesh.vertices.len()
274268
);
275269
info!("Triangulation done.");
276-
277-
mesh
278270
}
279271

280272
#[allow(unused)]
@@ -328,9 +320,11 @@ fn assert_cell_data_point_data_consistency<I: Index, R: Real>(
328320

329321
#[test]
330322
fn test_interpolate_cell_data() {
323+
use nalgebra::Vector3;
331324
let iso_surface_threshold = 0.25;
332325
//let default_value = 0.0;
333326

327+
let mut trimesh = crate::TriMesh3d::default();
334328
let origin = Vector3::new(0.0, 0.0, 0.0);
335329
let n_cubes_per_dim = [1, 1, 1];
336330
let cube_size = 1.0;
@@ -341,10 +335,14 @@ fn test_interpolate_cell_data() {
341335

342336
let mut sparse_data = new_map();
343337

344-
let marching_cubes_data =
345-
interpolate_points_to_cell_data(&grid, &sparse_data.clone().into(), iso_surface_threshold);
338+
let marching_cubes_data = interpolate_points_to_cell_data(
339+
&grid,
340+
&sparse_data.clone().into(),
341+
iso_surface_threshold,
342+
&mut trimesh,
343+
);
346344

347-
assert_eq!(marching_cubes_data.vertices.len(), 0);
345+
assert_eq!(trimesh.vertices.len(), 0);
348346
assert_eq!(marching_cubes_data.cell_data.len(), 0);
349347

350348
let points = vec![
@@ -362,12 +360,16 @@ fn test_interpolate_cell_data() {
362360
sparse_data.insert(grid.flatten_point_index_array(&ijk), val);
363361
}
364362

365-
let marching_cubes_data =
366-
interpolate_points_to_cell_data(&grid, &sparse_data.clone().into(), iso_surface_threshold);
363+
let marching_cubes_data = interpolate_points_to_cell_data(
364+
&grid,
365+
&sparse_data.clone().into(),
366+
iso_surface_threshold,
367+
&mut trimesh,
368+
);
367369

368370
assert_eq!(marching_cubes_data.cell_data.len(), 1);
369371
// Check that the correct number of vertices was created
370-
assert_eq!(marching_cubes_data.vertices.len(), 6);
372+
assert_eq!(trimesh.vertices.len(), 6);
371373

372374
let cell = &marching_cubes_data.cell_data[&0];
373375

@@ -389,6 +391,6 @@ fn test_interpolate_cell_data() {
389391
assert!(cell.iso_surface_vertices[11].is_some());
390392

391393
// TODO: Continue writing test
392-
let _mesh = triangulate(marching_cubes_data);
394+
let _mesh = triangulate(marching_cubes_data, &mut trimesh);
393395
//println!("{:?}", mesh)
394396
}

splashsurf_lib/src/mesh.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,17 @@ pub struct TriMesh3d<R: Real> {
1313
pub triangles: Vec<[usize; 3]>,
1414
}
1515

16+
impl<R: Real> Default for TriMesh3d<R> {
17+
fn default() -> Self {
18+
Self {
19+
vertices: Vec::new(),
20+
triangles: Vec::new(),
21+
}
22+
}
23+
}
24+
1625
/// A hexahedral (volumetric) mesh in 3D
17-
#[derive(Clone, Debug)]
26+
#[derive(Clone, Debug, Default)]
1827
pub struct HexMesh3d<R: Real> {
1928
/// Coordinates of all vertices of the mesh
2029
pub vertices: Vec<Vector3<R>>,
@@ -23,14 +32,14 @@ pub struct HexMesh3d<R: Real> {
2332
}
2433

2534
/// A point cloud in 3D
26-
#[derive(Clone, Debug)]
35+
#[derive(Clone, Debug, Default)]
2736
pub struct PointCloud3d<R: Real> {
2837
/// Coordinates of all points in the point cloud
2938
pub points: Vec<Vector3<R>>,
3039
}
3140

3241
/// A mesh with attached vertex or point data
33-
#[derive(Clone, Debug)]
42+
#[derive(Clone, Debug, Default)]
3443
pub struct MeshWithPointData<MeshT, DataT> {
3544
/// The mesh geometry itself
3645
pub mesh: MeshT,

splashsurf_lib/src/uniform_grid.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,16 @@ impl<I: Index, R: Real> UniformCartesianCubeGrid3d<I, R> {
200200
})
201201
}
202202

203+
/// Create a new zeroed grid
204+
pub(crate) fn new_zero() -> Self {
205+
Self {
206+
aabb: AxisAlignedBoundingBox3d::new(Vector3::zeros(), Vector3::zeros()),
207+
cell_size: R::zero(),
208+
n_points_per_dim: [I::zero(); 3],
209+
n_cells_per_dim: [I::zero(); 3],
210+
}
211+
}
212+
203213
/// Returns the bounding box of the grid
204214
#[inline(always)]
205215
pub fn aabb(&self) -> &AxisAlignedBoundingBox3d<R> {

0 commit comments

Comments
 (0)