use alloc::collections::BTreeSet;
use deep_causality_algebra::RealField;
use deep_causality_topology::{
CutCellRegistry, CutConstraintKind, CutFaceConstraint, LatticeComplex,
};
#[derive(Debug, Clone)]
pub(in crate::solvers::dec) struct NoSlipConstraint<R: RealField> {
edges: alloc::vec::Vec<usize>,
rows: alloc::vec::Vec<CutFaceConstraint<R>>,
}
impl<R: RealField> NoSlipConstraint<R> {
pub(in crate::solvers::dec) fn new<const D: usize>(
complex: &LatticeComplex<D, R>,
cut_registry: Option<&CutCellRegistry<D, R>>,
aperture_resolved: bool,
) -> Self {
let periodic = complex.periodic();
let mut edges: alloc::vec::Vec<usize> = alloc::vec::Vec::new();
if !periodic.iter().all(|&p| p) {
let shape = complex.shape();
for (idx, cell) in complex.iter_cells(1).enumerate() {
let axis = cell.orientation().trailing_zeros() as usize;
let pos = cell.position();
let tangential = (0..D)
.any(|w| w != axis && !periodic[w] && (pos[w] == 0 || pos[w] + 1 == shape[w]));
if tangential {
edges.push(idx);
}
}
}
let mut rows: alloc::vec::Vec<CutFaceConstraint<R>> = alloc::vec::Vec::new();
if let Some(registry) = cut_registry {
let all_rows = if aperture_resolved {
registry.cut_face_constraints(complex)
} else {
alloc::vec::Vec::new()
};
if all_rows.is_empty() {
edges.extend(registry.solid_incident_edges(complex));
} else {
let row_edges: BTreeSet<usize> = all_rows
.iter()
.flat_map(|r| r.entries().iter().map(|&(e, _)| e))
.collect();
rows = all_rows
.into_iter()
.filter(|r| r.kind() == CutConstraintKind::Tangential)
.collect();
edges.extend(
registry
.solid_incident_edges(complex)
.into_iter()
.filter(|e| !row_edges.contains(e)),
);
}
}
edges.sort_unstable();
edges.dedup();
Self { edges, rows }
}
pub(in crate::solvers::dec) fn edges(&self) -> &[usize] {
&self.edges
}
pub(in crate::solvers::dec) fn rows(&self) -> &[CutFaceConstraint<R>] {
&self.rows
}
pub(in crate::solvers::dec) fn remove_edges(&mut self, slip: &[usize]) {
if slip.is_empty() {
return;
}
self.edges.retain(|e| !slip.contains(e));
}
}