1use std::collections::{BTreeMap, HashSet};
4
5use crate::data::storage::Storage;
6use crate::mesh_error::MeshSieveError;
7use crate::topology::cell_type::CellType;
8use crate::topology::labels::LabelSet;
9use crate::topology::point::PointId;
10use crate::topology::sieve::Sieve;
11
12use super::MeshData;
13
14#[derive(Debug)]
16pub struct MeshBundle<S, V, St, CtSt>
17where
18 St: Storage<V>,
19 CtSt: Storage<CellType>,
20{
21 pub meshes: Vec<MeshData<S, V, St, CtSt>>,
23}
24
25impl<S, V, St, CtSt> MeshBundle<S, V, St, CtSt>
26where
27 St: Storage<V>,
28 CtSt: Storage<CellType>,
29{
30 pub fn new(meshes: Vec<MeshData<S, V, St, CtSt>>) -> Self {
32 Self { meshes }
33 }
34
35 pub fn push(&mut self, mesh: MeshData<S, V, St, CtSt>) {
37 self.meshes.push(mesh);
38 }
39
40 pub fn meshes(&self) -> &[MeshData<S, V, St, CtSt>] {
42 &self.meshes
43 }
44
45 pub fn meshes_mut(&mut self) -> &mut [MeshData<S, V, St, CtSt>] {
47 &mut self.meshes
48 }
49}
50
51impl<S, V, St, CtSt> MeshBundle<S, V, St, CtSt>
52where
53 S: Sieve<Point = PointId>,
54 St: Storage<V>,
55 CtSt: Storage<CellType>,
56{
57 pub fn sync_labels(&mut self) -> usize {
64 let mut combined: Vec<(String, PointId, i32)> = Vec::new();
65 for mesh in &self.meshes {
66 if let Some(labels) = &mesh.labels {
67 for (name, point, value) in labels.iter() {
68 combined.push((name.to_string(), point, value));
69 }
70 }
71 }
72
73 combined.sort_by(|a, b| {
74 a.0.cmp(&b.0)
75 .then_with(|| a.1.cmp(&b.1))
76 .then_with(|| a.2.cmp(&b.2))
77 });
78
79 let mut applied = 0usize;
80 for mesh in &mut self.meshes {
81 if combined.is_empty() {
82 break;
83 }
84 let points: HashSet<PointId> = mesh.sieve.points().collect();
85 if points.is_empty() {
86 continue;
87 }
88 let labels = mesh.labels.get_or_insert_with(LabelSet::new);
89 for (name, point, value) in &combined {
90 if points.contains(point) {
91 labels.set_label(*point, name, *value);
92 applied += 1;
93 }
94 }
95 }
96
97 applied
98 }
99}
100
101impl<S, V, St, CtSt> MeshBundle<S, V, St, CtSt>
102where
103 S: Sieve<Point = PointId>,
104 V: Clone + Default + PartialEq,
105 St: Storage<V> + Clone,
106 CtSt: Storage<CellType>,
107{
108 pub fn sync_coordinates(&mut self) -> Result<(), MeshSieveError> {
113 let mut dimension: Option<usize> = None;
114 let mut values: BTreeMap<PointId, Vec<V>> = BTreeMap::new();
115
116 for mesh in &self.meshes {
117 let Some(coords) = &mesh.coordinates else {
118 continue;
119 };
120 if let Some(existing_dim) = dimension {
121 if coords.dimension() != existing_dim {
122 return Err(MeshSieveError::InvalidGeometry(format!(
123 "coordinate dimension mismatch: expected {existing_dim}, found {}",
124 coords.dimension()
125 )));
126 }
127 } else {
128 dimension = Some(coords.dimension());
129 }
130
131 for point in coords.section().atlas().points() {
132 let slice = coords.try_restrict(point)?;
133 if let Some(existing) = values.get(&point) {
134 if existing.as_slice() != slice {
135 return Err(MeshSieveError::InvalidGeometry(format!(
136 "conflicting coordinate values for point {point:?}"
137 )));
138 }
139 } else {
140 values.insert(point, slice.to_vec());
141 }
142 }
143 }
144
145 if values.is_empty() {
146 return Ok(());
147 }
148
149 for mesh in &mut self.meshes {
150 let Some(coords) = &mut mesh.coordinates else {
151 continue;
152 };
153 let points: Vec<_> = coords.section().atlas().points().collect();
154 for point in points {
155 if let Some(val) = values.get(&point) {
156 coords.section_mut().try_set(point, val)?;
157 }
158 }
159 }
160
161 Ok(())
162 }
163}