Skip to content

Commit 10830fa

Browse files
committed
Code deduplication
1 parent 613f3dd commit 10830fa

File tree

4 files changed

+109
-108
lines changed

4 files changed

+109
-108
lines changed

splashsurf_lib/src/io/bgeo_format.rs

Lines changed: 14 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
//! Helper functions for the BGEO file format
22
3+
use crate::mesh::{AttributeData, MeshAttribute};
34
use crate::utils::IteratorExt;
4-
use crate::Real;
5+
use crate::{utils, Real};
56
use anyhow::{anyhow, Context};
67
use flate2::read::GzDecoder;
78
use flate2::write::GzEncoder;
89
use flate2::Compression;
910
use nalgebra::Vector3;
1011
use nom::{Finish, Parser};
12+
use num_traits::FromPrimitive;
1113
use num_traits::ToPrimitive;
14+
use parser::bgeo_parser;
1215
use std::fs::{File, OpenOptions};
1316
use std::io;
1417
use std::io::{BufWriter, Read};
1518
use std::path::Path;
1619

17-
use crate::mesh::{AttributeData, MeshAttribute};
18-
use parser::bgeo_parser;
1920
// TODO: Find out why there is a 1.0 float value between position vector and id int in splishsplash output
2021
// TODO: Better error messages, skip nom errors
2122

@@ -332,57 +333,20 @@ impl AttributeStorage {
332333

333334
/// Tries to convert this BGEO attribute storage into a mesh [`AttributeData`] storage
334335
fn try_into_attribute_data<R: Real>(&self) -> Result<AttributeData<R>, anyhow::Error> {
335-
// TODO: Simplify error handling and de-duplicate with e.g. VTK code
336336
match self {
337-
AttributeStorage::Int(data) => {
338-
let data = data
339-
.iter()
340-
.map(|v| u64::try_from(*v))
341-
.collect::<Result<Vec<_>, _>>()
342-
.context(anyhow!(
343-
"Failed to convert an attribute value from i32 to u64 type"
344-
))?;
345-
Ok(AttributeData::ScalarU64(data))
346-
}
347-
AttributeStorage::Float(data) => {
348-
let data = data
349-
.iter()
350-
.map(|v| {
351-
R::from_f32(*v).ok_or_else(|| {
352-
anyhow!("Cannot convert an attribute value from f32 to Real type")
353-
})
354-
})
355-
.collect::<Result<Vec<_>, _>>()
356-
.context(anyhow!(
357-
"Failed to convert an attribute value from f32 to Real type"
358-
))?;
359-
Ok(AttributeData::ScalarReal(data))
360-
}
337+
AttributeStorage::Int(data) => utils::try_convert_scalar_slice(data, u64::from_i32)
338+
.map(|v| AttributeData::ScalarU64(v))
339+
.context(anyhow!("failed to convert integer attribute")),
340+
AttributeStorage::Float(data) => utils::try_convert_scalar_slice(data, R::from_f32)
341+
.map(|v| AttributeData::ScalarReal(v))
342+
.context(anyhow!("failed to convert float attribute")),
361343
AttributeStorage::Vector(n, data) => {
362344
if *n == 3 {
363-
let data = data
364-
.chunks_exact(3)
365-
.map(|v| {
366-
Some(Vector3::new(
367-
R::from_f32(v[0])?,
368-
R::from_f32(v[1])?,
369-
R::from_f32(v[2])?,
370-
))
371-
})
372-
.map(|vec| {
373-
vec.ok_or_else(|| {
374-
anyhow!(
375-
"Failed to convert an attribute vector from f32 to Real type"
376-
)
377-
})
378-
})
379-
.collect::<Result<Vec<_>, _>>()
380-
.context(anyhow!(
381-
"Failed to convert an attribute vector from f32 to Real type"
382-
))?;
383-
Ok(AttributeData::Vector3Real(data))
345+
utils::try_convert_scalar_slice_to_vectors(data, R::from_f32)
346+
.map(|v| AttributeData::Vector3Real(v))
347+
.context(anyhow!("failed to convert vector attribute"))
384348
} else {
385-
Err(anyhow!("Unsupported vector attribute dimension: {}", n))
349+
Err(anyhow!("unsupported vector attribute size: {}", n))
386350
}
387351
}
388352
}

splashsurf_lib/src/io/vtk_format.rs

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::mesh::{AttributeData, IntoVtkDataSet, MeshAttribute, MeshWithData, TriMesh3d};
44
use crate::utils::IteratorExt;
5-
use crate::{Real, RealConvert};
5+
use crate::{utils, Real, RealConvert};
66
use anyhow::{anyhow, Context};
77
use nalgebra::Vector3;
88
use std::borrow::Cow;
@@ -312,24 +312,12 @@ fn try_convert_io_buffer_to_attribute<R: Real>(
312312
) -> Result<AttributeData<R>, anyhow::Error> {
313313
match num_comp {
314314
1 => match &io_buffer {
315-
IOBuffer::U32(vec) => try_map_scalars_to_real(vec, |val| {
316-
R::from_u32(val).ok_or_else(|| {
317-
anyhow!("Cannot convert an attribute value from u32 to Real type")
318-
})
319-
})
320-
.map(|v| AttributeData::ScalarReal(v)),
321-
IOBuffer::F32(vec) => try_map_scalars_to_real(vec, |val| {
322-
R::from_f32(val).ok_or_else(|| {
323-
anyhow!("Cannot convert an attribute value from f32 to Real type")
324-
})
325-
})
326-
.map(|v| AttributeData::ScalarReal(v)),
327-
IOBuffer::F64(vec) => try_map_scalars_to_real(vec, |val| {
328-
R::from_f64(val).ok_or_else(|| {
329-
anyhow!("Cannot convert an attribute value from f64 to Real type")
330-
})
331-
})
332-
.map(|v| AttributeData::ScalarReal(v)),
315+
IOBuffer::U32(vec) => utils::try_convert_scalar_slice(vec, R::from_u32)
316+
.map(|v| AttributeData::ScalarReal(v)),
317+
IOBuffer::F32(vec) => utils::try_convert_scalar_slice(vec, R::from_f32)
318+
.map(|v| AttributeData::ScalarReal(v)),
319+
IOBuffer::F64(vec) => utils::try_convert_scalar_slice(vec, R::from_f64)
320+
.map(|v| AttributeData::ScalarReal(v)),
333321
_ => Err(anyhow!("Unsupported IOBuffer scalar data type")),
334322
},
335323
3 => match &io_buffer {
@@ -348,45 +336,12 @@ fn try_convert_io_buffer_to_attribute<R: Real>(
348336
}
349337
}
350338

351-
fn try_map_scalars_to_real<R: Real, T: Copy, F: Fn(T) -> Result<R, anyhow::Error>>(
352-
io_buffer: &[T],
353-
f: F,
354-
) -> Result<Vec<R>, anyhow::Error> {
355-
io_buffer
356-
.iter()
357-
.copied()
358-
.map(f)
359-
.try_collect_with_capacity(io_buffer.len())
360-
}
361-
362339
/// Tries to convert a vector of consecutive coordinate triplets into a vector of `Vector3`, also converts between floating point types
363340
fn particles_from_coords<RealOut: Real, RealIn: Real>(
364341
coords: &[RealIn],
365342
) -> Result<Vec<Vector3<RealOut>>, anyhow::Error> {
366-
if coords.len() % 3 != 0 {
367-
return Err(anyhow!(
368-
"Particle point buffer length is not divisible by 3"
369-
));
370-
}
371-
372-
let num_points = coords.len() / 3;
373-
let positions = coords
374-
.chunks_exact(3)
375-
.map(|triplet| {
376-
Some(Vector3::new(
377-
triplet[0].try_convert()?,
378-
triplet[1].try_convert()?,
379-
triplet[2].try_convert()?,
380-
))
381-
})
382-
.map(|vec| {
383-
vec.ok_or_else(|| {
384-
anyhow!("Failed to convert coordinate from input to output float type, value out of range?")
385-
})
386-
})
387-
.try_collect_with_capacity(num_points)?;
388-
389-
Ok(positions)
343+
utils::try_convert_scalar_slice_to_vectors(coords, |v| v.try_convert())
344+
.context(anyhow!("failed to convert particle coordinates"))
390345
}
391346

392347
/// Wrapper for a slice of particle positions for converting it into a VTK `UnstructuredGridPiece`

splashsurf_lib/src/traits.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
use std::fmt::{Debug, Display};
2-
use std::hash::Hash;
3-
use std::ops::{AddAssign, MulAssign, SubAssign};
4-
51
use bytemuck::Pod;
62
use nalgebra::{RealField, SMatrix};
73
use num_integer::Integer;
84
use num_traits::{
95
Bounded, CheckedAdd, CheckedMul, CheckedSub, FromPrimitive, NumCast, SaturatingSub, ToPrimitive,
106
};
7+
use std::fmt::{Debug, Display};
8+
use std::hash::Hash;
9+
use std::ops::{AddAssign, MulAssign, SubAssign};
1110

1211
/// Convenience trait that combines `Send` and `Sync`
1312
pub trait ThreadSafe: Sync + Send {}

splashsurf_lib/src/utils.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,91 @@
11
//! Internal helper functions and types
22
3+
use anyhow::{anyhow, Context};
34
use log::info;
5+
use nalgebra::{SVector, Scalar};
6+
use num_traits::Zero;
47
use rayon::prelude::*;
58
use std::cell::UnsafeCell;
9+
use std::fmt::Debug;
10+
11+
/// Converts a slice of scalar values to a vector of the same length, returns an error if conversion fails
12+
pub fn try_convert_scalar_slice<
13+
ScalarFrom: Copy + Debug,
14+
ScalarTo,
15+
F: Fn(ScalarFrom) -> Option<ScalarTo>,
16+
>(
17+
values: &[ScalarFrom],
18+
f: F,
19+
) -> Result<Vec<ScalarTo>, anyhow::Error> {
20+
values
21+
.iter()
22+
.copied()
23+
.map(|v| {
24+
f(v).ok_or_else(|| {
25+
anyhow!(
26+
"failed to convert value {:?} from type {} to {}",
27+
v,
28+
std::any::type_name::<ScalarFrom>(),
29+
std::any::type_name::<ScalarTo>()
30+
)
31+
})
32+
})
33+
.try_collect_with_capacity(values.len())
34+
}
35+
36+
/// Converts a slice of scalar values to a vector of [`nalgebra::SVector`], returns an error if conversion fails or the input slice's length is not a multiple of the vector length.
37+
pub fn try_convert_scalar_slice_to_vectors<
38+
const N: usize,
39+
ScalarFrom: Copy + Debug,
40+
ScalarTo: Scalar + Zero,
41+
F: Fn(ScalarFrom) -> Option<ScalarTo>,
42+
>(
43+
values: &[ScalarFrom],
44+
f: F,
45+
) -> Result<Vec<SVector<ScalarTo, N>>, anyhow::Error> {
46+
{
47+
if values.len() % N != 0 {
48+
Err(anyhow!("input slice length is not a multiple of {}", N))
49+
} else {
50+
values
51+
.chunks_exact(N)
52+
.map(|v| {
53+
let mut v_out = SVector::zeros();
54+
for i in 0..N {
55+
v_out[i] = f(v[i]).ok_or_else(|| {
56+
anyhow!(
57+
"failed to convert value {:?} from type {} to {}",
58+
v,
59+
std::any::type_name::<ScalarFrom>(),
60+
std::any::type_name::<ScalarTo>()
61+
)
62+
})?;
63+
}
64+
Ok(v_out)
65+
})
66+
.try_collect_with_capacity(values.len() / N)
67+
}
68+
}
69+
.context(anyhow!(
70+
"failed to convert scalar slice to vectors of length {}",
71+
N
72+
))
73+
}
74+
75+
#[cfg(test)]
76+
mod tests {
77+
use num_traits::FromPrimitive;
78+
#[test]
79+
fn test_try_convert_scalar_slice() {
80+
let values = vec![1, -1];
81+
assert!(super::try_convert_scalar_slice(&values, u64::from_i32).is_err());
82+
let values = vec![1, -1];
83+
assert_eq!(
84+
super::try_convert_scalar_slice(&values, f32::from_i64).unwrap(),
85+
vec![1.0, -1.0]
86+
);
87+
}
88+
}
689

790
/// "Convert" an empty vector to preserve allocated memory if size and alignment matches
891
/// See https://users.rust-lang.org/t/pattern-how-to-reuse-a-vec-str-across-loop-iterations/61657/5

0 commit comments

Comments
 (0)