Skip to content

Commit d582e94

Browse files
committed
Fix postprocessing combined with particle AABB filtering
1 parent 78000e3 commit d582e94

File tree

2 files changed

+50
-21
lines changed

2 files changed

+50
-21
lines changed

splashsurf/src/reconstruct.rs

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,25 @@ pub fn reconstruction_pipeline<I: Index, R: Real>(
10161016
// Perform the surface reconstruction
10171017
let reconstruction = splashsurf_lib::reconstruct_surface::<I, R>(particle_positions, params)?;
10181018

1019+
// Filters a particle quantity based on an optional mask of particles inside the reconstruction domain
1020+
fn filtered_quantity<'a, T: Clone>(
1021+
values: &'a [T],
1022+
particles_inside: Option<&[bool]>,
1023+
) -> Cow<'a, [T]> {
1024+
if let Some(particles_inside) = particles_inside {
1025+
assert_eq!(values.len(), particles_inside.len());
1026+
let filtered_values = values
1027+
.iter()
1028+
.zip(particles_inside.iter().copied())
1029+
.filter(|(_, is_inside)| *is_inside)
1030+
.map(|(v, _)| v.clone())
1031+
.collect::<Vec<_>>();
1032+
Cow::Owned(filtered_values)
1033+
} else {
1034+
Cow::Borrowed(values)
1035+
}
1036+
}
1037+
10191038
let reconstruction_output = if postprocessing.output_raw_mesh {
10201039
Some(reconstruction.clone())
10211040
} else {
@@ -1070,6 +1089,17 @@ pub fn reconstruction_pipeline<I: Index, R: Real>(
10701089
.as_ref()
10711090
.map(Vec::is_empty)
10721091
.unwrap_or(true));
1092+
let filtered_particle_positions_cow = if interpolator_required {
1093+
// TODO: Re-use filtered particles from reconstruction?
1094+
filtered_quantity(
1095+
particle_positions,
1096+
reconstruction.particle_inside_aabb().map(Vec::as_slice),
1097+
)
1098+
} else {
1099+
Cow::Borrowed(particle_positions)
1100+
};
1101+
let filtered_particle_positions = filtered_particle_positions_cow.as_ref();
1102+
10731103
let interpolator = if interpolator_required {
10741104
profile!("initialize interpolator");
10751105
info!("Post-processing: Initializing interpolator...");
@@ -1089,13 +1119,13 @@ pub fn reconstruction_pipeline<I: Index, R: Real>(
10891119
.ok_or_else(|| anyhow::anyhow!("Particle densities were not returned by surface reconstruction but are required for SPH normal computation"))?
10901120
.as_slice();
10911121
assert_eq!(
1092-
particle_positions.len(),
1122+
filtered_particle_positions.len(),
10931123
particle_densities.len(),
10941124
"There has to be one density value per particle"
10951125
);
10961126

10971127
Some(SphInterpolator::new(
1098-
&particle_positions,
1128+
&filtered_particle_positions,
10991129
particle_densities,
11001130
particle_rest_mass,
11011131
params.compact_support_radius,
@@ -1127,17 +1157,17 @@ pub fn reconstruction_pipeline<I: Index, R: Real>(
11271157
{
11281158
let search_radius = params.compact_support_radius;
11291159

1130-
let mut domain = Aabb3d::from_points(particle_positions);
1160+
let mut domain = Aabb3d::from_points(filtered_particle_positions);
11311161
domain.grow_uniformly(search_radius);
11321162

11331163
let mut nl = Vec::new();
11341164
splashsurf_lib::neighborhood_search::neighborhood_search_spatial_hashing_parallel::<I, R>(
11351165
&domain,
1136-
particle_positions,
1166+
filtered_particle_positions,
11371167
search_radius,
11381168
&mut nl,
11391169
);
1140-
assert_eq!(nl.len(), particle_positions.len());
1170+
assert_eq!(nl.len(), filtered_particle_positions.len());
11411171
Cow::Owned(nl)
11421172
}
11431173
);
@@ -1151,8 +1181,9 @@ pub fn reconstruction_pipeline<I: Index, R: Real>(
11511181
nl.iter()
11521182
.copied()
11531183
.map(|j| {
1154-
let dist =
1155-
(particle_positions[i] - particle_positions[j]).norm_squared();
1184+
let dist = (filtered_particle_positions[i]
1185+
- filtered_particle_positions[j])
1186+
.norm_squared();
11561187

11571188
R::one() - (dist / squared_r).clamp(R::zero(), R::one())
11581189
})
@@ -1299,10 +1330,12 @@ pub fn reconstruction_pipeline<I: Index, R: Real>(
12991330
{
13001331
info!("Interpolating attribute \"{}\"...", attribute.name);
13011332

1333+
let particles_inside = reconstruction.particle_inside_aabb().map(Vec::as_slice);
13021334
match &attribute.data {
13031335
AttributeData::ScalarReal(values) => {
1336+
let filtered_values = filtered_quantity(values, particles_inside);
13041337
let interpolated_values = interpolator.interpolate_scalar_quantity(
1305-
values.as_slice(),
1338+
&filtered_values,
13061339
mesh_with_data.vertices(),
13071340
true,
13081341
);
@@ -1312,8 +1345,9 @@ pub fn reconstruction_pipeline<I: Index, R: Real>(
13121345
));
13131346
}
13141347
AttributeData::Vector3Real(values) => {
1348+
let filtered_values = filtered_quantity(values, particles_inside);
13151349
let interpolated_values = interpolator.interpolate_vector_quantity(
1316-
values.as_slice(),
1350+
&filtered_values,
13171351
mesh_with_data.vertices(),
13181352
true,
13191353
);

splashsurf_lib/src/lib.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -219,18 +219,6 @@ impl<R: Real> Parameters<R> {
219219
)
220220
}
221221

222-
/// Returns the given reconstruction parameters with the rest density set to the given value
223-
pub fn with_rest_density(mut self, rest_density: R) -> Self {
224-
self.rest_density = rest_density;
225-
self
226-
}
227-
228-
/// Returns the given reconstruction parameters with the iso surface threshold set to the given value
229-
pub fn with_iso_surface_threshold(mut self, iso_surface_threshold: R) -> Self {
230-
self.iso_surface_threshold = iso_surface_threshold;
231-
self
232-
}
233-
234222
/// Tries to convert the parameters from one [Real] type to another [Real] type, returns `None` if conversion fails
235223
pub fn try_convert<T: Real>(&self) -> Option<Parameters<T>> {
236224
Some(Parameters {
@@ -289,6 +277,11 @@ impl<I: Index, R: Real> SurfaceReconstruction<I, R> {
289277
self.particle_densities.as_ref()
290278
}
291279

280+
/// Returns a reference to the per input particle boolean vector indicating whether the particle was inside the specified AABB (if any)
281+
pub fn particle_inside_aabb(&self) -> Option<&Vec<bool>> {
282+
self.particle_inside_aabb.as_ref()
283+
}
284+
292285
/// Returns a reference to the global list of per-particle neighborhood lists if computed during the reconstruction (`None` if not specified in the parameters)
293286
pub fn particle_neighbors(&self) -> Option<&Vec<Vec<usize>>> {
294287
self.particle_neighbors.as_ref()
@@ -378,6 +371,7 @@ pub fn reconstruct_surface_inplace<I: Index, R: Real>(
378371
.particle_inside_aabb
379372
.take()
380373
.unwrap_or_default();
374+
particle_inside.clear();
381375
utils::reserve_total(&mut particle_inside, particle_positions.len());
382376
particle_positions
383377
.par_iter()
@@ -404,6 +398,7 @@ pub fn reconstruct_surface_inplace<I: Index, R: Real>(
404398
output_surface.particle_inside_aabb = Some(particle_inside);
405399
Cow::Owned(filtered_particles)
406400
} else {
401+
output_surface.particle_inside_aabb = None;
407402
Cow::Borrowed(particle_positions)
408403
};
409404
let particle_positions = filtered_particle_positions.as_ref();

0 commit comments

Comments
 (0)