Skip to main content

mesh_sieve/algs/
transform.rs

1//! Mesh transformation helpers for coordinate updates.
2
3use crate::data::section::Section;
4use crate::data::storage::Storage;
5use crate::io::MeshData;
6use crate::mesh_error::MeshSieveError;
7use crate::topology::cell_type::CellType;
8use crate::topology::point::PointId;
9use crate::topology::sieve::Sieve;
10
11/// Coordinate update strategies for mesh transforms.
12pub enum CoordinateTransform<'a, St>
13where
14    St: Storage<f64>,
15{
16    /// Update coordinates using a user-supplied function.
17    ///
18    /// The function receives the point ID and a mutable slice of the
19    /// coordinate tuple for that point.
20    Function(&'a mut dyn FnMut(PointId, &mut [f64]) -> Result<(), MeshSieveError>),
21    /// Update coordinates by adding a displacement section.
22    ///
23    /// Displacement slices must match the coordinate dimension for each point.
24    Displacement(&'a Section<f64, St>),
25}
26
27/// Hook set for updating derived data after coordinate transforms.
28pub struct TransformHooks<'a, M, St, CtSt>
29where
30    St: Storage<f64>,
31    CtSt: Storage<CellType>,
32{
33    /// Invoked after coordinates are updated.
34    pub after_update:
35        Option<&'a mut dyn FnMut(&MeshData<M, f64, St, CtSt>) -> Result<(), MeshSieveError>>,
36}
37
38/// Apply a coordinate transformation to a mesh, leaving topology unchanged.
39pub fn transform_mesh<M, St, CtSt>(
40    mesh: &mut MeshData<M, f64, St, CtSt>,
41    transform: CoordinateTransform<'_, St>,
42    mut hooks: TransformHooks<'_, M, St, CtSt>,
43) -> Result<(), MeshSieveError>
44where
45    M: Sieve<Point = PointId>,
46    St: Storage<f64>,
47    CtSt: Storage<CellType>,
48{
49    let coords = mesh
50        .coordinates
51        .as_mut()
52        .ok_or_else(|| MeshSieveError::InvalidGeometry("mesh is missing coordinates".into()))?;
53    let dim = coords.dimension();
54    let points: Vec<PointId> = coords.section().atlas().points().collect();
55
56    match transform {
57        CoordinateTransform::Function(update) => {
58            for point in points {
59                let slice = coords.try_restrict_mut(point)?;
60                if slice.len() != dim {
61                    return Err(MeshSieveError::SliceLengthMismatch {
62                        point,
63                        expected: dim,
64                        found: slice.len(),
65                    });
66                }
67                update(point, slice)?;
68            }
69        }
70        CoordinateTransform::Displacement(displacement) => {
71            for point in points {
72                let disp = displacement.try_restrict(point)?;
73                if disp.len() != dim {
74                    return Err(MeshSieveError::SliceLengthMismatch {
75                        point,
76                        expected: dim,
77                        found: disp.len(),
78                    });
79                }
80                let slice = coords.try_restrict_mut(point)?;
81                if slice.len() != dim {
82                    return Err(MeshSieveError::SliceLengthMismatch {
83                        point,
84                        expected: dim,
85                        found: slice.len(),
86                    });
87                }
88                for (coord, delta) in slice.iter_mut().zip(disp.iter()) {
89                    *coord += *delta;
90                }
91            }
92        }
93    }
94
95    if let Some(after_update) = hooks.after_update.as_mut() {
96        after_update(mesh)?;
97    }
98
99    Ok(())
100}