1use std::collections::HashMap;
8
9use crate::algs::adjacency_graph::{
10 AdjacencyOrdering, CellAdjacencyEdges, CellAdjacencyOpts, VertexAdjacencyOpts,
11 build_cell_adjacency_edges, build_cell_adjacency_graph_with_cells,
12 build_vertex_adjacency_graph_with_vertices,
13};
14use crate::mesh_error::MeshSieveError;
15use crate::topology::point::PointId;
16use crate::topology::sieve::Sieve;
17use crate::topology::sieve::strata::compute_strata;
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub enum AdjacencyWeighting {
22 None,
24 SharedBoundaryCount,
26}
27
28#[derive(Debug, Clone)]
30pub struct MeshGraph {
31 pub xadj: Vec<usize>,
33 pub adjncy: Vec<usize>,
35 pub order: Vec<PointId>,
37 pub weights: Option<Vec<u32>>,
39}
40
41impl MeshGraph {
42 #[inline]
44 pub fn neighbors(&self, i: usize) -> &[usize] {
45 &self.adjncy[self.xadj[i]..self.xadj[i + 1]]
46 }
47
48 #[inline]
50 pub fn neighbor_weights(&self, i: usize) -> Option<&[u32]> {
51 self.weights
52 .as_ref()
53 .map(|w| &w[self.xadj[i]..self.xadj[i + 1]])
54 }
55}
56
57pub fn cell_adjacency_graph<S>(
59 sieve: &S,
60 opts: CellAdjacencyOpts,
61 weighting: AdjacencyWeighting,
62) -> Result<MeshGraph, MeshSieveError>
63where
64 S: Sieve<Point = PointId>,
65{
66 let strata = compute_strata(sieve)?;
67 let cells = strata.strata.first().cloned().unwrap_or_default();
68 Ok(cell_adjacency_graph_with_cells(
69 sieve, cells, opts, weighting,
70 ))
71}
72
73pub fn cell_adjacency_graph_with_cells<S>(
75 sieve: &S,
76 cells: impl IntoIterator<Item = PointId>,
77 opts: CellAdjacencyOpts,
78 weighting: AdjacencyWeighting,
79) -> MeshGraph
80where
81 S: Sieve<Point = PointId>,
82{
83 let cells: Vec<PointId> = cells.into_iter().collect();
84 if matches!(weighting, AdjacencyWeighting::None) {
85 let graph = build_cell_adjacency_graph_with_cells(sieve, cells, opts);
86 return MeshGraph {
87 xadj: graph.xadj,
88 adjncy: graph.adjncy,
89 order: graph.order,
90 weights: None,
91 };
92 }
93
94 let cells = order_points(cells, opts.ordering);
95 build_weighted_shared_boundary_graph(
96 &cells,
97 |p| downward_boundary_points(sieve, p, opts.boundary.max_down_depth),
98 opts.symmetrize,
99 )
100}
101
102pub fn cell_adjacency_edges_for_cells<S>(
104 sieve: &S,
105 cells: impl IntoIterator<Item = PointId>,
106 cell_dimension: u32,
107 by: crate::algs::adjacency_graph::CellAdjacencyBy,
108 ordering: AdjacencyOrdering,
109) -> CellAdjacencyEdges
110where
111 S: Sieve<Point = PointId>,
112{
113 build_cell_adjacency_edges(sieve, cells, cell_dimension, by, ordering)
114}
115
116pub fn vertex_adjacency_graph<S>(
118 sieve: &S,
119 opts: VertexAdjacencyOpts,
120 weighting: AdjacencyWeighting,
121) -> Result<MeshGraph, MeshSieveError>
122where
123 S: Sieve<Point = PointId>,
124{
125 let strata = compute_strata(sieve)?;
126 let vertices: Vec<PointId> = strata
127 .chart_points
128 .iter()
129 .copied()
130 .filter(|p| strata.depth.get(p).copied() == Some(0))
131 .collect();
132 Ok(vertex_adjacency_graph_with_vertices(
133 sieve, vertices, opts, weighting,
134 ))
135}
136
137pub fn vertex_adjacency_graph_with_vertices<S>(
139 sieve: &S,
140 vertices: impl IntoIterator<Item = PointId>,
141 opts: VertexAdjacencyOpts,
142 weighting: AdjacencyWeighting,
143) -> MeshGraph
144where
145 S: Sieve<Point = PointId>,
146{
147 let vertices: Vec<PointId> = vertices.into_iter().collect();
148 if matches!(weighting, AdjacencyWeighting::None) {
149 let graph = build_vertex_adjacency_graph_with_vertices(sieve, vertices, opts);
150 return MeshGraph {
151 xadj: graph.xadj,
152 adjncy: graph.adjncy,
153 order: graph.order,
154 weights: None,
155 };
156 }
157
158 let vertices = order_points(vertices, opts.ordering);
159 build_weighted_shared_boundary_graph(
160 &vertices,
161 |p| upward_boundary_points(sieve, p, opts.max_up_depth),
162 opts.symmetrize,
163 )
164}
165
166fn order_points<I>(points: I, ordering: AdjacencyOrdering) -> Vec<PointId>
167where
168 I: IntoIterator<Item = PointId>,
169{
170 let mut out: Vec<PointId> = points.into_iter().collect();
171 match ordering {
172 AdjacencyOrdering::Input => {
173 let mut seen = std::collections::HashSet::with_capacity(out.len());
174 out.retain(|p| seen.insert(*p));
175 }
176 AdjacencyOrdering::Sorted => {
177 out.sort_unstable();
178 out.dedup();
179 }
180 }
181 out
182}
183
184fn downward_boundary_points<S>(sieve: &S, p: PointId, max_down_depth: Option<u32>) -> Vec<PointId>
185where
186 S: Sieve<Point = PointId>,
187{
188 use std::collections::{HashSet, VecDeque};
189
190 match max_down_depth {
191 Some(0) => Vec::new(),
192 Some(1) => {
193 let mut out: Vec<PointId> = sieve.cone_points(p).collect();
194 out.sort_unstable();
195 out.dedup();
196 out
197 }
198 None | Some(_) => {
199 let limit = max_down_depth.unwrap_or(u32::MAX);
200 let mut out = Vec::new();
201 let mut seen: HashSet<PointId> = HashSet::new();
202 let mut q: VecDeque<(PointId, u32)> = VecDeque::new();
203 q.extend(sieve.cone_points(p).map(|x| (x, 1)));
204 while let Some((r, d)) = q.pop_front() {
205 if seen.insert(r) {
206 out.push(r);
207 if d < limit {
208 for s in sieve.cone_points(r) {
209 if !seen.contains(&s) {
210 q.push_back((s, d + 1));
211 }
212 }
213 }
214 }
215 }
216 out.sort_unstable();
217 out.dedup();
218 out
219 }
220 }
221}
222
223fn upward_boundary_points<S>(sieve: &S, p: PointId, max_up_depth: Option<u32>) -> Vec<PointId>
224where
225 S: Sieve<Point = PointId>,
226{
227 use std::collections::{HashSet, VecDeque};
228
229 match max_up_depth {
230 Some(0) => Vec::new(),
231 Some(1) => {
232 let mut out: Vec<PointId> = sieve.support_points(p).collect();
233 out.sort_unstable();
234 out.dedup();
235 out
236 }
237 None | Some(_) => {
238 let limit = max_up_depth.unwrap_or(u32::MAX);
239 let mut out = Vec::new();
240 let mut seen: HashSet<PointId> = HashSet::new();
241 let mut q: VecDeque<(PointId, u32)> = VecDeque::new();
242 q.extend(sieve.support_points(p).map(|x| (x, 1)));
243 while let Some((r, d)) = q.pop_front() {
244 if seen.insert(r) {
245 out.push(r);
246 if d < limit {
247 for s in sieve.support_points(r) {
248 if !seen.contains(&s) {
249 q.push_back((s, d + 1));
250 }
251 }
252 }
253 }
254 }
255 out.sort_unstable();
256 out.dedup();
257 out
258 }
259 }
260}
261
262fn build_weighted_shared_boundary_graph(
263 points: &[PointId],
264 boundary: impl Fn(PointId) -> Vec<PointId>,
265 symmetrize: bool,
266) -> MeshGraph {
267 let n = points.len();
268 if n == 0 {
269 return MeshGraph {
270 xadj: vec![0],
271 adjncy: Vec::new(),
272 order: Vec::new(),
273 weights: Some(Vec::new()),
274 };
275 }
276
277 let mut incident: HashMap<PointId, Vec<usize>> = HashMap::new();
278 incident.reserve(n * 4);
279 for (i, &p) in points.iter().enumerate() {
280 for b in boundary(p) {
281 incident.entry(b).or_default().push(i);
282 }
283 }
284
285 let mut neigh: Vec<HashMap<usize, u32>> = vec![HashMap::new(); n];
286 for (_b, mut verts_on_b) in incident {
287 if verts_on_b.len() < 2 {
288 continue;
289 }
290 verts_on_b.sort_unstable();
291 verts_on_b.dedup();
292 for i in 0..verts_on_b.len() {
293 let vi = verts_on_b[i];
294 for &vj in &verts_on_b[(i + 1)..] {
295 if vi == vj {
296 continue;
297 }
298 *neigh[vi].entry(vj).or_insert(0) += 1;
299 if symmetrize {
300 *neigh[vj].entry(vi).or_insert(0) += 1;
301 }
302 }
303 }
304 }
305
306 let mut xadj = Vec::with_capacity(n + 1);
307 let mut adjncy = Vec::new();
308 let mut weights = Vec::new();
309 xadj.push(0);
310 for (i, map) in neigh.into_iter().enumerate() {
311 let mut neighbors: Vec<(usize, u32)> = map.into_iter().collect();
312 neighbors.sort_by_key(|(idx, _)| *idx);
313 if let Some(pos) = neighbors.iter().position(|(idx, _)| *idx == i) {
314 neighbors.remove(pos);
315 }
316 adjncy.extend(neighbors.iter().map(|(idx, _)| *idx));
317 weights.extend(neighbors.iter().map(|(_, w)| *w));
318 xadj.push(adjncy.len());
319 }
320
321 MeshGraph {
322 xadj,
323 adjncy,
324 order: points.to_vec(),
325 weights: Some(weights),
326 }
327}