use super::sieve_trait::Sieve;
use crate::mesh_error::MeshSieveError;
pub trait Orientation: Copy + Default + std::fmt::Debug + 'static {
fn compose(a: Self, b: Self) -> Self;
fn inverse(a: Self) -> Self;
}
impl Orientation for () {
#[inline]
fn compose(_: (), _: ()) {}
#[inline]
fn inverse(_: ()) {}
}
impl Orientation for i32 {
#[inline]
fn compose(a: i32, b: i32) -> i32 {
a + b
}
#[inline]
fn inverse(a: i32) -> i32 {
-a
}
}
pub trait OrientedSieve: Sieve {
type Orient: Orientation;
type ConeOIter<'a>: Iterator<Item = (Self::Point, Self::Orient)>
where
Self: 'a;
type SupportOIter<'a>: Iterator<Item = (Self::Point, Self::Orient)>
where
Self: 'a;
fn cone_o<'a>(&'a self, p: Self::Point) -> Self::ConeOIter<'a>;
fn support_o<'a>(&'a self, p: Self::Point) -> Self::SupportOIter<'a>;
fn add_arrow_o(
&mut self,
src: Self::Point,
dst: Self::Point,
payload: Self::Payload,
orient: Self::Orient,
);
fn closure_o<I>(&self, seeds: I) -> Vec<(Self::Point, Self::Orient)>
where
I: IntoIterator<Item = Self::Point>,
{
use std::collections::HashMap;
let mut stack: Vec<(Self::Point, Self::Orient)> = seeds
.into_iter()
.map(|p| (p, Self::Orient::default()))
.collect();
let mut best: HashMap<Self::Point, Self::Orient> = HashMap::new();
while let Some((p, acc)) = stack.pop() {
if best.contains_key(&p) {
continue;
}
best.insert(p, acc);
for (q, o) in self.cone_o(p) {
let nxt = <Self::Orient as Orientation>::compose(acc, o);
if !best.contains_key(&q) {
stack.push((q, nxt));
}
}
}
let mut out: Vec<_> = best.into_iter().collect();
out.sort_unstable_by_key(|(pt, _)| *pt);
out
}
fn star_o<I>(&self, seeds: I) -> Vec<(Self::Point, Self::Orient)>
where
I: IntoIterator<Item = Self::Point>,
{
use std::collections::HashMap;
let mut stack: Vec<(Self::Point, Self::Orient)> = seeds
.into_iter()
.map(|p| (p, Self::Orient::default()))
.collect();
let mut best: HashMap<Self::Point, Self::Orient> = HashMap::new();
while let Some((p, acc)) = stack.pop() {
if best.contains_key(&p) {
continue;
}
best.insert(p, acc);
for (q, o_src_p) in self.support_o(p) {
let step = <Self::Orient as Orientation>::inverse(o_src_p);
let nxt = <Self::Orient as Orientation>::compose(acc, step);
if !best.contains_key(&q) {
stack.push((q, nxt));
}
}
}
let mut out: Vec<_> = best.into_iter().collect();
out.sort_unstable_by_key(|(pt, _)| *pt);
out
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FaceOrientationMismatch<P, O> {
pub face: P,
pub cell_a: P,
pub cell_b: P,
pub orient_a: O,
pub orient_b: O,
}
pub fn validate_adjacent_face_orientations<S>(
sieve: &mut S,
) -> Result<Vec<FaceOrientationMismatch<S::Point, S::Orient>>, MeshSieveError>
where
S: OrientedSieve,
S::Point: Ord,
S::Orient: Orientation + PartialEq,
{
let faces: Vec<_> = sieve.height_stratum(1)?.collect();
let mut mismatches = Vec::new();
for face in faces {
let supports: Vec<_> = sieve.support_o(face).collect();
if supports.len() < 2 {
continue;
}
for i in 0..supports.len() {
for j in (i + 1)..supports.len() {
let (cell_a, orient_a) = supports[i];
let (cell_b, orient_b) = supports[j];
if orient_b != S::Orient::inverse(orient_a) {
mismatches.push(FaceOrientationMismatch {
face,
cell_a,
cell_b,
orient_a,
orient_b,
});
}
}
}
}
Ok(mismatches)
}
pub fn repair_adjacent_face_orientations<S>(sieve: &mut S) -> Result<usize, MeshSieveError>
where
S: OrientedSieve,
S::Point: Ord,
S::Orient: Orientation + PartialEq,
{
let faces: Vec<_> = sieve.height_stratum(1)?.collect();
let mut flips = 0;
for face in faces {
let supports: Vec<_> = sieve.support_o(face).collect();
if supports.len() < 2 {
continue;
}
let (_, ref_orient) = supports[0];
let expected = S::Orient::inverse(ref_orient);
for (cell, orient) in supports.into_iter().skip(1) {
if orient == expected {
continue;
}
let payload = {
let mut found = None;
for (dst, pay) in sieve.cone(cell) {
if dst == face {
found = Some(pay);
break;
}
}
found.ok_or_else(|| {
MeshSieveError::MissingPointInCone(format!(
"missing arrow ({cell:?} -> {face:?}) while repairing"
))
})?
};
sieve.add_arrow_o(cell, face, payload, expected);
flips += 1;
}
}
Ok(flips)
}