use crate::topology::point::PointId;
use crate::topology::sieve::{InMemorySieve, Sieve};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TreeCell<const D: usize> {
pub level: u8,
pub coords: [u32; D],
}
impl<const D: usize> TreeCell<D> {
pub fn parent(&self) -> Option<Self> {
if self.level == 0 {
None
} else {
let mut coords = self.coords;
for coord in &mut coords {
*coord /= 2;
}
Some(Self {
level: self.level - 1,
coords,
})
}
}
pub fn children(&self) -> Vec<Self> {
let count = 1usize << D;
let mut children = Vec::with_capacity(count);
for idx in 0..count {
let mut coords = [0u32; D];
for axis in 0..D {
let bit = (idx >> axis) & 1;
coords[axis] = self.coords[axis] * 2 + bit as u32;
}
children.push(Self {
level: self.level + 1,
coords,
});
}
children
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ForestVertex<const D: usize> {
pub coords: [u32; D],
}
#[derive(Debug, Clone)]
pub struct ForestMeshView<const D: usize> {
pub sieve: InMemorySieve<PointId, ()>,
pub cell_points: HashMap<TreeCell<D>, PointId>,
pub vertex_points: HashMap<ForestVertex<D>, PointId>,
pub max_level: u8,
}
#[derive(Debug, Clone)]
pub struct Forest<const D: usize> {
leaves: HashSet<TreeCell<D>>,
}
pub type QuadForest = Forest<2>;
pub type OctForest = Forest<3>;
impl<const D: usize> Default for Forest<D> {
fn default() -> Self {
Self::new()
}
}
impl<const D: usize> Forest<D> {
pub fn new() -> Self {
let mut leaves = HashSet::new();
leaves.insert(TreeCell {
level: 0,
coords: [0; D],
});
Self { leaves }
}
pub fn leaves(&self) -> impl Iterator<Item = &TreeCell<D>> {
self.leaves.iter()
}
pub fn leaf_count(&self) -> usize {
self.leaves.len()
}
pub fn refine_by_indicator<F>(&mut self, indicator: F, threshold: f64) -> usize
where
F: Fn(&TreeCell<D>) -> f64,
{
let to_refine: Vec<_> = self
.leaves
.iter()
.copied()
.filter(|cell| indicator(cell) > threshold)
.collect();
self.refine_cells(&to_refine)
}
pub fn coarsen_by_indicator<F>(&mut self, indicator: F, threshold: f64) -> usize
where
F: Fn(&TreeCell<D>) -> f64,
{
let mut parent_to_children: HashMap<TreeCell<D>, Vec<TreeCell<D>>> = HashMap::new();
for leaf in &self.leaves {
if let Some(parent) = leaf.parent() {
parent_to_children.entry(parent).or_default().push(*leaf);
}
}
let mut to_coarsen = Vec::new();
let sibling_count = 1 << D;
for (parent, children) in parent_to_children {
if children.len() == sibling_count
&& children.iter().all(|child| indicator(child) < threshold)
{
to_coarsen.push((parent, children));
}
}
let mut coarsened = 0;
for (parent, children) in to_coarsen {
let mut removed = 0;
for child in children {
if self.leaves.remove(&child) {
removed += 1;
}
}
if removed == sibling_count {
self.leaves.insert(parent);
coarsened += 1;
}
}
coarsened
}
pub fn conforming_view(&self) -> ForestMeshView<D> {
let mut balanced = self.clone();
balanced.balance();
balanced.build_view()
}
fn refine_cells(&mut self, cells: &[TreeCell<D>]) -> usize {
let mut refined = 0;
for cell in cells {
if self.leaves.remove(cell) {
for child in cell.children() {
self.leaves.insert(child);
}
refined += 1;
}
}
refined
}
fn max_level(&self) -> u8 {
self.leaves.iter().map(|cell| cell.level).max().unwrap_or(0)
}
fn balance(&mut self) {
loop {
let leaves: Vec<_> = self.leaves.iter().copied().collect();
let max_level = leaves.iter().map(|cell| cell.level).max().unwrap_or(0);
let mut to_refine = HashSet::new();
for (i, cell) in leaves.iter().enumerate() {
for other in leaves.iter().skip(i + 1) {
if are_face_neighbors(cell, other, max_level) {
if cell.level < other.level {
to_refine.insert(*cell);
} else if other.level < cell.level {
to_refine.insert(*other);
}
}
}
}
if to_refine.is_empty() {
break;
}
let cells: Vec<_> = to_refine.into_iter().collect();
self.refine_cells(&cells);
}
}
fn build_view(&self) -> ForestMeshView<D> {
let max_level = self.max_level();
let leaves: Vec<_> = self.leaves.iter().copied().collect();
let mut sieve = InMemorySieve::<PointId, ()>::default();
let mut cell_points = HashMap::new();
let mut vertex_points = HashMap::new();
let mut next_id = 1u64;
for cell in &leaves {
let point = PointId::new(next_id).expect("cell point id");
next_id += 1;
cell_points.insert(*cell, point);
}
for cell in &leaves {
let cell_point = cell_points[cell];
for vertex in cell_vertices(cell, max_level) {
let vertex_point = vertex_points.entry(vertex).or_insert_with(|| {
let point = PointId::new(next_id).expect("vertex point id");
next_id += 1;
point
});
sieve.add_arrow(cell_point, *vertex_point, ());
}
}
ForestMeshView {
sieve,
cell_points,
vertex_points,
max_level,
}
}
}
fn cell_bounds<const D: usize>(cell: &TreeCell<D>, max_level: u8) -> [(u32, u32); D] {
let scale = 1u32 << (max_level - cell.level);
let mut bounds = [(0u32, 0u32); D];
for axis in 0..D {
let start = cell.coords[axis] * scale;
bounds[axis] = (start, start + scale);
}
bounds
}
fn are_face_neighbors<const D: usize>(a: &TreeCell<D>, b: &TreeCell<D>, max_level: u8) -> bool {
let a_bounds = cell_bounds(a, max_level);
let b_bounds = cell_bounds(b, max_level);
let mut touching_axis = None;
for axis in 0..D {
let (a0, a1) = a_bounds[axis];
let (b0, b1) = b_bounds[axis];
if a1 == b0 || b1 == a0 {
if touching_axis.is_some() {
return false;
}
touching_axis = Some(axis);
} else if a0 >= b1 || b0 >= a1 {
return false;
}
}
if let Some(axis) = touching_axis {
for other_axis in 0..D {
if other_axis == axis {
continue;
}
let (a0, a1) = a_bounds[other_axis];
let (b0, b1) = b_bounds[other_axis];
if a0 >= b1 || b0 >= a1 {
return false;
}
}
true
} else {
false
}
}
fn cell_vertices<const D: usize>(cell: &TreeCell<D>, max_level: u8) -> Vec<ForestVertex<D>> {
let bounds = cell_bounds(cell, max_level);
let mut vertices = Vec::with_capacity(1 << D);
for idx in 0..(1 << D) {
let mut coords = [0u32; D];
for axis in 0..D {
let bit = (idx >> axis) & 1;
coords[axis] = if bit == 0 {
bounds[axis].0
} else {
bounds[axis].1
};
}
vertices.push(ForestVertex { coords });
}
vertices
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn forest_refine_and_coarsen_by_indicator() {
let mut forest = QuadForest::new();
assert_eq!(forest.leaf_count(), 1);
let refined = forest.refine_by_indicator(
|cell| {
if cell.level == 0 { 1.0 } else { 0.0 }
},
0.5,
);
assert_eq!(refined, 1);
assert_eq!(forest.leaf_count(), 4);
forest.refine_by_indicator(
|cell| {
if cell.level == 1 && cell.coords == [0, 0] {
1.0
} else {
0.0
}
},
0.5,
);
assert_eq!(forest.leaf_count(), 7);
let coarsened = forest.coarsen_by_indicator(|_| 0.0, 0.1);
assert!(coarsened > 0);
assert_eq!(forest.leaf_count(), 4);
let coarsened_again = forest.coarsen_by_indicator(|_| 0.0, 0.1);
assert!(coarsened_again > 0);
assert_eq!(forest.leaf_count(), 1);
}
#[test]
fn forest_conforming_view_has_consistent_topology() {
let mut forest = QuadForest::new();
forest.refine_by_indicator(|cell| if cell.level == 0 { 1.0 } else { 0.0 }, 0.0);
forest.refine_by_indicator(
|cell| {
if cell.level == 1 && cell.coords == [0, 0] {
1.0
} else {
0.0
}
},
0.5,
);
let view = forest.conforming_view();
assert_eq!(view.max_level, 2);
assert_eq!(view.cell_points.len(), 16);
for cell_point in view.cell_points.values() {
let cone: Vec<_> = view.sieve.cone_points(*cell_point).collect();
assert_eq!(cone.len(), 4);
}
}
}