#![allow(clippy::needless_range_loop)]
use crate::{
geometry::{Region, Side, Split, regions},
prelude::{Face, HyperBox, IndexSpace},
};
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec};
use datasize::DataSize;
use std::{array, ops::Range, slice};
mod blocks;
mod interfaces;
mod neighbors;
pub use blocks::{BlockId, TreeBlocks};
pub use interfaces::{TreeInterface, TreeInterfaces};
pub use neighbors::{NeighborId, TreeBlockNeighbor, TreeCellNeighbor, TreeNeighbors};
const NULL: usize = usize::MAX;
#[derive(
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Debug,
serde::Serialize,
serde::Deserialize,
DataSize,
)]
pub struct ActiveCellId(pub usize);
#[derive(
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Debug,
serde::Serialize,
serde::Deserialize,
DataSize,
)]
pub struct CellId(pub usize);
impl CellId {
pub const ROOT: CellId = CellId(0);
pub fn child<const N: usize>(offset: Self, split: Split<N>) -> Self {
Self(offset.0 + split.to_linear())
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
struct Cell<const N: usize> {
bounds: HyperBox<N>,
parent: usize,
children: usize,
active_offset: usize,
active_count: usize,
level: usize,
}
impl<const N: usize> DataSize for Cell<N> {
const IS_DYNAMIC: bool = false;
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
0
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(from = "TreeSer<N>", into = "TreeSer<N>")]
pub struct Tree<const N: usize> {
domain: HyperBox<N>,
periodic: [bool; N],
active_values: BitVec<usize, Lsb0>,
active_offsets: Vec<usize>,
active_to_cell: Vec<usize>,
level_offsets: Vec<usize>,
cells: Vec<Cell<N>>,
}
impl<const N: usize> Tree<N> {
pub fn new(domain: HyperBox<N>) -> Self {
let mut result = Self {
domain,
periodic: [false; N],
active_values: BitVec::new(),
active_offsets: vec![0, 0],
active_to_cell: Vec::new(),
level_offsets: Vec::new(),
cells: Vec::new(),
};
result.build();
result
}
pub fn set_periodic(&mut self, axis: usize, periodic: bool) {
self.periodic[axis] = periodic;
}
pub fn domain(&self) -> HyperBox<N> {
self.domain
}
pub fn num_active_cells(&self) -> usize {
self.active_offsets.len() - 1
}
pub fn num_cells(&self) -> usize {
self.cells.len()
}
pub fn num_levels(&self) -> usize {
self.level_offsets.len() - 1
}
pub fn level_cells(&self, level: usize) -> impl Iterator<Item = CellId> + ExactSizeIterator {
(self.level_offsets[level]..self.level_offsets[level + 1]).map(CellId)
}
pub fn cell_indices(&self) -> impl Iterator<Item = CellId> {
(0..self.num_cells()).map(CellId)
}
pub fn active_cell_indices(&self) -> impl Iterator<Item = ActiveCellId> {
(0..self.num_active_cells()).map(ActiveCellId)
}
pub fn bounds(&self, cell: CellId) -> HyperBox<N> {
self.cells[cell.0].bounds
}
pub fn active_bounds(&self, active: ActiveCellId) -> HyperBox<N> {
self.bounds(self.cell_from_active_index(active))
}
pub fn level(&self, cell: CellId) -> usize {
self.cells[cell.0].level
}
pub fn active_level(&self, cell: ActiveCellId) -> usize {
self.active_offsets[cell.0 + 1] - self.active_offsets[cell.0]
}
pub fn children(&self, cell: CellId) -> Option<CellId> {
if self.cells[cell.0].children == NULL {
return None;
}
Some(CellId(self.cells[cell.0].children))
}
pub fn child(&self, cell: CellId, child: Split<N>) -> Option<CellId> {
if self.cells[cell.0].children == NULL {
return None;
}
Some(CellId(self.cells[cell.0].children + child.to_linear()))
}
pub fn parent(&self, cell: CellId) -> Option<CellId> {
if self.cells[cell.0].parent == NULL {
return None;
}
Some(CellId(self.cells[cell.0].parent))
}
pub fn active_zvalue(&self, active: ActiveCellId) -> &BitSlice<usize, Lsb0> {
&self.active_values
[N * self.active_offsets[active.0]..N * self.active_offsets[active.0 + 1]]
}
pub fn active_split(&self, active: ActiveCellId, level: usize) -> Split<N> {
Split::pack(array::from_fn(|axis| {
self.active_zvalue(active)[N * level + axis]
}))
}
pub fn most_recent_active_split(&self, active: ActiveCellId) -> Option<Split<N>> {
if self.num_cells() == 1 {
return None;
}
Some(self.active_split(active, self.active_level(active) - 1))
}
pub fn check_refine_flags(&self, flags: &[bool]) -> bool {
assert!(flags.len() == self.num_active_cells());
for cell in self.active_cell_indices() {
if !flags[cell.0] {
continue;
}
for coarse in self.active_coarse_neighborhood(cell) {
if !flags[coarse.0] {
return false;
}
}
}
true
}
pub fn balance_refine_flags(&self, flags: &mut [bool]) {
assert!(flags.len() == self.num_active_cells());
loop {
let mut is_balanced = true;
for cell in self.active_cell_indices() {
if !flags[cell.0] {
continue;
}
for coarse in self.active_coarse_neighborhood(cell) {
if !flags[coarse.0] {
is_balanced = false;
flags[coarse.0] = true;
}
}
}
if is_balanced {
break;
}
}
}
pub fn refine_active_index_map(&self, flags: &[bool], map: &mut [ActiveCellId]) {
assert!(flags.len() == self.num_active_cells());
assert!(map.len() == self.num_active_cells());
let mut cursor = 0;
for cell in 0..self.num_active_cells() {
map[cell] = ActiveCellId(cursor);
if flags[cell] {
cursor += Split::<N>::COUNT;
} else {
cursor += 1;
}
}
}
pub fn refine(&mut self, flags: &[bool]) {
assert!(self.num_active_cells() == flags.len());
let num_flags = flags.iter().copied().filter(|&p| p).count();
let total_active_cells = self.num_active_cells() + (Split::<N>::COUNT - 1) * num_flags;
let mut active_values = BitVec::with_capacity(total_active_cells * N);
let mut active_offsets = Vec::with_capacity(total_active_cells);
active_offsets.push(0);
for active in 0..self.num_active_cells() {
if flags[active] {
for split in Split::<N>::enumerate() {
active_values.extend_from_bitslice(self.active_zvalue(ActiveCellId(active)));
for axis in 0..N {
active_values.push(split.is_set(axis));
}
active_offsets.push(active_values.len() / N);
}
} else {
active_values.extend_from_bitslice(self.active_zvalue(ActiveCellId(active)));
active_offsets.push(active_values.len() / N);
}
}
self.active_values.clone_from(&active_values);
self.active_offsets.clone_from(&active_offsets);
self.build();
}
pub fn check_coarsen_flags(&self, flags: &[bool]) -> bool {
assert!(flags.len() == self.num_active_cells());
if flags.len() == 1 {
return true;
}
if flags.len() == Split::<N>::COUNT {
return flags.iter().all(|&b| !b);
}
for cell in self.active_cell_indices() {
if !flags[cell.0] {
for neighbor in self.active_coarse_neighborhood(cell) {
if flags[neighbor.0] {
return false;
}
}
}
}
let mut cell = 0;
while cell < self.num_active_cells() {
if !flags[cell] {
cell += 1;
continue;
}
let level = self.active_level(ActiveCellId(cell));
let split = self.most_recent_active_split(ActiveCellId(cell)).unwrap();
if split != Split::<N>::empty() {
return false;
}
for offset in 0..Split::<N>::COUNT {
if self.active_level(ActiveCellId(cell + offset)) != level {
return false;
}
}
if !flags[cell..cell + Split::<N>::COUNT].iter().all(|&b| b) {
return false;
}
cell += Split::<N>::COUNT;
}
true
}
pub fn balance_coarsen_flags(&self, flags: &mut [bool]) {
assert!(flags.len() == self.num_active_cells());
if flags.len() == 1 {
return;
}
if flags.len() == Split::<N>::COUNT {
flags.fill(false);
}
loop {
let mut is_balanced = true;
for cell in self.active_cell_indices() {
if !flags[cell.0] {
for neighbor in self.active_coarse_neighborhood(cell) {
if flags[neighbor.0] {
is_balanced = false;
}
flags[neighbor.0] = false;
}
}
}
let mut cell = 0;
while cell < self.num_active_cells() {
if !flags[cell] {
cell += 1;
continue;
}
let level = self.active_level(ActiveCellId(cell));
let split = self.most_recent_active_split(ActiveCellId(cell)).unwrap();
if split != Split::<N>::empty() {
flags[cell] = false;
is_balanced = false;
cell += 1;
continue;
}
for offset in 0..Split::<N>::COUNT {
if self.active_level(ActiveCellId(cell + offset)) != level {
flags[cell] = false;
is_balanced = false;
cell += 1;
continue;
}
}
if !flags[cell..cell + Split::<N>::COUNT].iter().all(|&b| b) {
flags[cell..cell + Split::<N>::COUNT].fill(false);
is_balanced = false;
}
cell += Split::<N>::COUNT;
}
if is_balanced {
break;
}
}
}
pub fn coarsen_active_index_map(&self, flags: &[bool], map: &mut [ActiveCellId]) {
assert!(flags.len() == self.num_active_cells());
assert!(map.len() == self.num_active_cells());
let mut cursor = 0;
let mut cell = 0;
while cell < self.num_active_cells() {
if flags[cell] {
map[cell..cell + Split::<N>::COUNT].fill(ActiveCellId(cursor));
cell += Split::<N>::COUNT;
} else {
map[cell] = ActiveCellId(cursor);
cell += 1;
}
cursor += 1;
}
}
pub fn coarsen(&mut self, flags: &[bool]) {
assert!(flags.len() == self.num_active_cells());
let num_flags = flags.iter().copied().filter(|&p| p).count();
debug_assert!(num_flags % Split::<N>::COUNT == 0);
let total_active = self.num_active_cells() - num_flags / Split::<N>::COUNT;
let mut active_values = BitVec::with_capacity(total_active * N);
let mut active_offsets = Vec::new();
active_offsets.push(0);
let mut cursor = 0;
while cursor < self.num_active_cells() {
let zvalue = self.active_zvalue(ActiveCellId(cursor));
if flags[cursor] {
#[cfg(debug_assertions)]
for split in Split::<N>::enumerate() {
assert!(flags[cursor + split.to_linear()])
}
active_values.extend_from_bitslice(&zvalue[0..zvalue.len().saturating_sub(N)]);
cursor += Split::<N>::COUNT;
} else {
active_values.extend_from_bitslice(zvalue);
cursor += 1;
}
active_offsets.push(active_values.len() / N);
}
self.active_values.clone_from(&active_values);
self.active_offsets.clone_from(&active_offsets);
self.build();
}
pub fn build(&mut self) {
self.active_to_cell.resize(self.num_active_cells(), 0);
self.level_offsets.clear();
self.cells.clear();
self.cells.push(Cell {
bounds: self.domain,
parent: NULL,
children: NULL,
active_offset: 0,
active_count: self.num_active_cells(),
level: 0,
});
self.level_offsets.push(0);
self.level_offsets.push(1);
loop {
let level = self.level_offsets.len() - 2;
let level_cells = self.level_offsets[level]..self.level_offsets[level + 1];
let next_level_start = self.cells.len();
for parent in level_cells {
if self.cells[parent].active_count == 1 {
debug_assert!(
self.active_level(ActiveCellId(self.cells[parent].active_offset)) == level
);
self.active_to_cell[self.cells[parent].active_offset] = parent;
continue;
}
self.cells[parent].children = self.cells.len();
let active_start = self.cells[parent].active_offset;
let active_end = active_start + self.cells[parent].active_count;
let mut cursor = active_start;
debug_assert!(self.active_level(ActiveCellId(cursor)) > level);
let bounds = self.cells[parent].bounds;
for mask in Split::<N>::enumerate() {
let child_cell_start = cursor;
while cursor < active_end
&& mask == self.active_split(ActiveCellId(cursor), level)
{
cursor += 1;
}
let child_cell_end = cursor;
self.cells.push(Cell {
bounds: bounds.subdivide(mask),
parent,
children: NULL,
active_offset: child_cell_start,
active_count: child_cell_end - child_cell_start,
level: level + 1,
});
}
}
let next_level_end = self.cells.len();
if next_level_start >= next_level_end {
break;
}
self.level_offsets.push(next_level_end);
}
#[cfg(debug_assertions)]
for cell in self.cell_indices() {
let active = ActiveCellId(self.cells[cell.0].active_offset);
assert!(self.active_level(active) >= self.level(cell));
}
}
pub fn cell_from_active_index(&self, active: ActiveCellId) -> CellId {
debug_assert!(
active.0 < self.num_active_cells(),
"Active cell index is expected to be less that the number of active cells."
);
CellId(self.active_to_cell[active.0])
}
pub fn active_index_from_cell(&self, cell: CellId) -> Option<ActiveCellId> {
debug_assert!(
cell.0 < self.num_cells(),
"Cell index is expected to be less that the number of cells."
);
if self.cells[cell.0].active_count != 1 {
return None;
}
Some(ActiveCellId(self.cells[cell.0].active_offset))
}
pub fn active_children(
&self,
cell: CellId,
) -> impl Iterator<Item = ActiveCellId> + ExactSizeIterator {
let (offset, count) = (
self.cells[cell.0].active_offset,
self.cells[cell.0].active_count,
);
(offset..offset + count).map(ActiveCellId)
}
pub fn is_active(&self, node: CellId) -> bool {
let result = self.cells[node.0].children == NULL;
debug_assert!(!result || self.cells[node.0].active_count == 1);
result
}
pub fn cell_from_point(&self, point: [f64; N]) -> CellId {
debug_assert!(self.domain.contains(point));
let mut node = CellId(0);
while let Some(children) = self.children(node) {
let bounds = self.bounds(node);
let center = bounds.center();
node = CellId::child(
children,
Split::<N>::pack(array::from_fn(|axis| point[axis] > center[axis])),
);
}
node
}
pub fn cell_from_point_cached(&self, point: [f64; N], mut cache: CellId) -> CellId {
debug_assert!(self.domain.contains(point));
while !self.bounds(cache).contains(point) {
cache = self.parent(cache).unwrap();
}
let mut node = cache;
while let Some(children) = self.children(node) {
let bounds = self.bounds(node);
let center = bounds.center();
node = CellId::child(
children,
Split::<N>::pack(array::from_fn(|axis| point[axis] > center[axis])),
)
}
node
}
pub fn neighbor(&self, cell: CellId, face: Face<N>) -> Option<CellId> {
let mut region = Region::CENTRAL;
region.set_side(face.axis, if face.side { Side::Right } else { Side::Left });
self.neighbor_region(cell, region)
}
pub fn neighbor_region(&self, cell: CellId, region: Region<N>) -> Option<CellId> {
let active_indices = ActiveCellId(self.cells[cell.0].active_offset);
debug_assert!(self.active_level(active_indices) >= self.level(cell));
let is_periodic = (0..N)
.map(|axis| region.side(axis) == Side::Middle || self.periodic[axis])
.all(|b| b);
if cell == CellId::ROOT && is_periodic {
return Some(CellId::ROOT);
}
let parent = self.parent(cell)?;
debug_assert!(self.level(cell) > 0 && self.level(cell) == self.level(parent) + 1);
let split = self.active_split(active_indices, self.level(parent));
if split.is_inner_region(region) {
let children = self.children(parent).unwrap();
return Some(CellId::child(children, split.as_outer_region(region)));
}
let mut parent_region = region;
for axis in 0..N {
match (region.side(axis), split.is_set(axis)) {
(Side::Left, true) | (Side::Right, false) => {
parent_region.set_side(axis, Side::Middle);
}
_ => {}
}
}
let parent_neighbor = self.neighbor_region(parent, parent_region)?;
let Some(parent_neighbor_children) = self.children(parent_neighbor) else {
return Some(parent_neighbor);
};
let mut neighbor_split = split;
for axis in 0..N {
match (region.side(axis), split.is_set(axis)) {
(Side::Left, false) | (Side::Right, true) => {
neighbor_split = neighbor_split.toggled(axis);
}
(Side::Left, true) | (Side::Right, false) => {
neighbor_split = neighbor_split.toggled(axis);
}
_ => {}
}
}
Some(CellId::child(parent_neighbor_children, neighbor_split))
}
pub fn _neighbor_region2(&self, cell: CellId, region: Region<N>) -> Option<CellId> {
let is_periodic = (0..N)
.map(|axis| region.side(axis) == Side::Middle || self.periodic[axis])
.all(|b| b);
if cell == CellId::ROOT && is_periodic {
return Some(CellId::ROOT);
}
let active_index = ActiveCellId(self.cells[cell.0].active_offset);
let mut cursor = cell;
while let Some(parent) = self.parent(cursor) {
cursor = parent;
if self
.active_split(active_index, self.level(cursor))
.is_inner_region(region)
{
break;
}
}
if self.children(cursor).is_some() {
let split = self.active_split(active_index, self.level(cursor));
if split.is_inner_region(region) {
cursor = CellId::child(
self.children(cursor).unwrap(),
split.as_outer_region(region),
)
}
}
if cursor == CellId::ROOT {
if !is_periodic {
return None;
}
debug_assert!(self.level(cell) > 0);
let split = self.active_split(active_index, self.level(cursor));
cursor = CellId::child(
self.children(cursor).unwrap(),
split.as_inner_region(region),
);
}
while self.level(cursor) < self.level(cell) {
let Some(children) = self.children(cursor) else {
break;
};
let split = self
.active_split(active_index, self.level(cursor))
.as_inner_region(region);
cursor = CellId::child(children, split);
}
Some(cursor)
}
pub fn active_neighbors_in_region(
&self,
cell: CellId,
region: Region<N>,
) -> impl Iterator<Item = ActiveCellId> + '_ {
let level = self.level(cell);
self.neighbor_region(cell, region)
.into_iter()
.flat_map(move |neighbor| {
self.active_children(neighbor).filter(move |&active| {
for l in level..self.active_level(active) {
if !region
.reverse()
.is_split_adjacent(self.active_split(active, l))
{
return false;
}
}
true
})
})
}
pub fn active_neighborhood(
&self,
cell: ActiveCellId,
) -> impl Iterator<Item = ActiveCellId> + '_ {
regions().flat_map(move |region| {
self.active_neighbors_in_region(self.cell_from_active_index(cell), region)
})
}
pub fn active_coarse_neighborhood(
&self,
cell: ActiveCellId,
) -> impl Iterator<Item = ActiveCellId> + '_ {
regions().flat_map(move |region| {
let neighbor = self.neighbor_region(self.cell_from_active_index(cell), region)?;
if self.level(neighbor) < self.active_level(cell) {
return self.active_index_from_cell(neighbor);
}
None
})
}
pub fn is_boundary_face(&self, cell: CellId, face: Face<N>) -> bool {
let mut region = Region::CENTRAL;
region.set_side(face.axis, if face.side { Side::Right } else { Side::Left });
self.boundary_region(cell, region) != Region::CENTRAL
}
pub fn boundary_region(&self, cell: CellId, region: Region<N>) -> Region<N> {
let Some(active) = self.active_index_from_cell(cell) else {
return region;
};
let mut result = region;
let mut level = self.level(cell);
while level > 0 && result != Region::CENTRAL {
let split = self.active_split(active, level - 1);
for axis in 0..N {
match (result.side(axis), split.is_set(axis)) {
(Side::Left, true) => result.set_side(axis, Side::Middle),
(Side::Right, false) => result.set_side(axis, Side::Middle),
_ => {}
}
}
level -= 1;
}
result
}
}
impl<const N: usize> DataSize for Tree<N> {
const IS_DYNAMIC: bool = true;
const STATIC_HEAP_SIZE: usize = 0;
fn estimate_heap_size(&self) -> usize {
self.active_offsets.estimate_heap_size()
+ self.active_values.capacity() / size_of::<usize>()
+ self.active_to_cell.estimate_heap_size()
+ self.level_offsets.estimate_heap_size()
+ self.cells.estimate_heap_size()
}
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct TreeSer<const N: usize> {
domain: HyperBox<N>,
#[serde(with = "crate::array")]
periodic: [bool; N],
active_values: BitVec<usize, Lsb0>,
active_offsets: Vec<usize>,
}
impl<const N: usize> From<TreeSer<N>> for Tree<N> {
fn from(value: TreeSer<N>) -> Self {
let mut result = Tree {
domain: value.domain,
periodic: value.periodic,
active_values: value.active_values,
active_offsets: value.active_offsets,
active_to_cell: Vec::default(),
level_offsets: Vec::default(),
cells: Vec::default(),
};
result.build();
result
}
}
impl<const N: usize> From<Tree<N>> for TreeSer<N> {
fn from(value: Tree<N>) -> Self {
Self {
domain: value.domain,
periodic: value.periodic,
active_values: value.active_values,
active_offsets: value.active_offsets,
}
}
}
impl<const N: usize> Default for TreeSer<N> {
fn default() -> Self {
Self {
domain: HyperBox::UNIT,
periodic: [false; N],
active_values: BitVec::default(),
active_offsets: Vec::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn neighbors() {
let mut tree = Tree::<2>::new(HyperBox::UNIT);
assert_eq!(tree.bounds(CellId::ROOT), HyperBox::UNIT);
assert_eq!(tree.num_cells(), 1);
assert_eq!(tree.num_active_cells(), 1);
assert_eq!(tree.num_levels(), 1);
assert_eq!(tree.neighbor(CellId::ROOT, Face::negative(0)), None);
tree.refine(&[true]);
tree.build();
assert_eq!(tree.num_cells(), 5);
assert_eq!(tree.num_active_cells(), 4);
assert_eq!(tree.num_levels(), 2);
for split in Split::enumerate() {
assert_eq!(tree.active_split(ActiveCellId(split.to_linear()), 0), split);
}
for i in 0..4 {
assert_eq!(tree.cell_from_active_index(ActiveCellId(i)), CellId(i + 1));
}
tree.refine(&[true, false, false, false]);
tree.build();
assert_eq!(tree.cell_from_active_index(ActiveCellId(0)), CellId(5));
assert!(tree.is_boundary_face(CellId(5), Face::negative(0)));
assert!(tree.is_boundary_face(CellId(5), Face::negative(1)));
assert_eq!(
tree.boundary_region(CellId(5), Region::new([Side::Left, Side::Right])),
Region::new([Side::Left, Side::Middle])
);
assert_eq!(
tree.neighbor_region(CellId(5), Region::new([Side::Right, Side::Right])),
Some(CellId(8))
);
assert_eq!(
tree.neighbor_region(CellId(4), Region::new([Side::Left, Side::Left])),
Some(CellId(1))
);
}
#[test]
fn periodic_neighbors() {
let mut tree = Tree::<2>::new(HyperBox::UNIT);
tree.set_periodic(0, true);
tree.set_periodic(1, true);
assert_eq!(
tree.neighbor(CellId::ROOT, Face::negative(0)),
Some(CellId::ROOT)
);
tree.refine(&[true]);
tree.refine(&[true, false, false, false]);
tree.build();
assert_eq!(tree.neighbor(CellId(5), Face::negative(0)), Some(CellId(2)));
assert_eq!(
tree.neighbor_region(CellId(5), Region::new([Side::Left, Side::Left])),
Some(CellId(4))
);
}
#[test]
fn active_neighbors_in_region() {
let mut tree = Tree::<2>::new(HyperBox::UNIT);
tree.refine(&[true]);
tree.refine(&[true, false, false, false]);
tree.build();
assert!(
tree.active_neighbors_in_region(CellId(2), Region::new([Side::Left, Side::Middle]))
.eq([ActiveCellId(1), ActiveCellId(3)].into_iter())
);
assert!(
tree.active_neighbors_in_region(CellId(3), Region::new([Side::Middle, Side::Left]))
.eq([ActiveCellId(2), ActiveCellId(3)].into_iter())
);
assert!(
tree.active_neighbors_in_region(CellId(4), Region::new([Side::Left, Side::Left]))
.eq([ActiveCellId(3)].into_iter())
);
assert!(
tree.active_neighbors_in_region(CellId(6), Region::new([Side::Right, Side::Right]))
.eq([ActiveCellId(4)].into_iter())
);
}
#[test]
fn refinement_and_coarsening() {
let mut tree = Tree::<2>::new(HyperBox::UNIT);
tree.refine(&[true]);
tree.refine(&[true, false, false, false]);
for _ in 0..1 {
let mut flags: Vec<bool> = vec![true; tree.num_active_cells()];
tree.balance_refine_flags(&mut flags);
tree.refine(&flags);
}
for _ in 0..2 {
let mut flags = vec![true; tree.num_active_cells()];
tree.balance_coarsen_flags(&mut flags);
let mut coarsen_map = vec![ActiveCellId(0); tree.num_active_cells()];
tree.coarsen_active_index_map(&flags, &mut coarsen_map);
tree.coarsen(&flags);
}
let mut other_tree = Tree::<2>::new(HyperBox::UNIT);
other_tree.refine(&[true]);
assert_eq!(tree, other_tree);
}
use rand::Rng;
#[test]
fn fuzz_serialize() -> eyre::Result<()> {
let mut tree = Tree::<2>::new(HyperBox::UNIT);
let mut rng = rand::rng();
for _ in 0..4 {
let mut flags = vec![false; tree.num_active_cells()];
rng.fill(flags.as_mut_slice());
tree.balance_coarsen_flags(&mut flags);
tree.refine(&mut flags);
}
let data = ron::to_string(&tree)?;
let tree2: Tree<2> = ron::from_str(data.as_str())?;
assert_eq!(tree, tree2);
Ok(())
}
#[test]
fn cell_from_point() -> eyre::Result<()> {
let mut tree = Tree::<2>::new(HyperBox::UNIT);
tree.refine(&[true]);
tree.refine(&[true, false, false, false]);
assert_eq!(tree.cell_from_point([0.0, 0.0]), CellId(5));
assert_eq!(
tree.active_index_from_cell(CellId(5)),
Some(ActiveCellId(0))
);
assert_eq!(tree.cell_from_point([0.51, 0.67]), CellId(4));
assert_eq!(
tree.active_index_from_cell(CellId(4)),
Some(ActiveCellId(6))
);
let mut rng = rand::rng();
for _ in 0..50 {
let x: f64 = rng.random_range(0.0..1.0);
let y: f64 = rng.random_range(0.0..1.0);
let cache: usize = rng.random_range(..tree.num_cells());
assert_eq!(
tree.cell_from_point_cached([x, y], CellId(cache)),
tree.cell_from_point([x, y])
);
}
Ok(())
}
}