use std::collections::{BTreeMap, HashSet};
use crate::data::storage::Storage;
use crate::mesh_error::MeshSieveError;
use crate::topology::cell_type::CellType;
use crate::topology::labels::LabelSet;
use crate::topology::point::PointId;
use crate::topology::sieve::Sieve;
use super::MeshData;
#[derive(Debug)]
pub struct MeshBundle<S, V, St, CtSt>
where
St: Storage<V>,
CtSt: Storage<CellType>,
{
pub meshes: Vec<MeshData<S, V, St, CtSt>>,
}
impl<S, V, St, CtSt> MeshBundle<S, V, St, CtSt>
where
St: Storage<V>,
CtSt: Storage<CellType>,
{
pub fn new(meshes: Vec<MeshData<S, V, St, CtSt>>) -> Self {
Self { meshes }
}
pub fn push(&mut self, mesh: MeshData<S, V, St, CtSt>) {
self.meshes.push(mesh);
}
pub fn meshes(&self) -> &[MeshData<S, V, St, CtSt>] {
&self.meshes
}
pub fn meshes_mut(&mut self) -> &mut [MeshData<S, V, St, CtSt>] {
&mut self.meshes
}
}
impl<S, V, St, CtSt> MeshBundle<S, V, St, CtSt>
where
S: Sieve<Point = PointId>,
St: Storage<V>,
CtSt: Storage<CellType>,
{
pub fn sync_labels(&mut self) -> usize {
let mut combined: Vec<(String, PointId, i32)> = Vec::new();
for mesh in &self.meshes {
if let Some(labels) = &mesh.labels {
for (name, point, value) in labels.iter() {
combined.push((name.to_string(), point, value));
}
}
}
combined.sort_by(|a, b| {
a.0.cmp(&b.0)
.then_with(|| a.1.cmp(&b.1))
.then_with(|| a.2.cmp(&b.2))
});
let mut applied = 0usize;
for mesh in &mut self.meshes {
if combined.is_empty() {
break;
}
let points: HashSet<PointId> = mesh.sieve.points().collect();
if points.is_empty() {
continue;
}
let labels = mesh.labels.get_or_insert_with(LabelSet::new);
for (name, point, value) in &combined {
if points.contains(point) {
labels.set_label(*point, name, *value);
applied += 1;
}
}
}
applied
}
}
impl<S, V, St, CtSt> MeshBundle<S, V, St, CtSt>
where
S: Sieve<Point = PointId>,
V: Clone + Default + PartialEq,
St: Storage<V> + Clone,
CtSt: Storage<CellType>,
{
pub fn sync_coordinates(&mut self) -> Result<(), MeshSieveError> {
let mut dimension: Option<usize> = None;
let mut values: BTreeMap<PointId, Vec<V>> = BTreeMap::new();
for mesh in &self.meshes {
let Some(coords) = &mesh.coordinates else {
continue;
};
if let Some(existing_dim) = dimension {
if coords.dimension() != existing_dim {
return Err(MeshSieveError::InvalidGeometry(format!(
"coordinate dimension mismatch: expected {existing_dim}, found {}",
coords.dimension()
)));
}
} else {
dimension = Some(coords.dimension());
}
for point in coords.section().atlas().points() {
let slice = coords.try_restrict(point)?;
if let Some(existing) = values.get(&point) {
if existing.as_slice() != slice {
return Err(MeshSieveError::InvalidGeometry(format!(
"conflicting coordinate values for point {point:?}"
)));
}
} else {
values.insert(point, slice.to_vec());
}
}
}
if values.is_empty() {
return Ok(());
}
for mesh in &mut self.meshes {
let Some(coords) = &mut mesh.coordinates else {
continue;
};
let points: Vec<_> = coords.section().atlas().points().collect();
for point in points {
if let Some(val) = values.get(&point) {
coords.section_mut().try_set(point, val)?;
}
}
}
Ok(())
}
}