use std::collections::BTreeSet;
use std::cmp::Ordering;
use crate::analysis::Connectivity;
use crate::error::{FieldError, NodeKind, TreeError};
use crate::field::{Field, Value};
use crate::gather::{GATHER_EDGE, GATHER_VOXELS, gather};
use crate::parallel::par_update;
use crate::topology::{Location, Remap, Topology};
use crate::tree::Coord;
const CELL: i64 = 8;
fn steps(d: [i64; 3], conn: Connectivity) -> i64 {
let along = d.map(|c| if c == 0 { 0 } else { (c.abs() - 1) * CELL + 1 });
match conn {
Connectivity::Six => along.iter().sum(),
Connectivity::TwentySix => along.into_iter().max().unwrap_or(0),
}
}
fn reach(k: u32) -> i64 {
if k == 0 { 0 } else { (i64::from(k) - 1) / CELL + 1 }
}
fn any_within(k: u32, conn: Connectivity, mut visit: impl FnMut([i64; 3]) -> bool) -> bool {
for rho in 1..=reach(k) {
for z in -rho..=rho {
for y in -rho..=rho {
let on_shell = z.abs() == rho || y.abs() == rho;
let xs: Vec<i64> = if on_shell { (-rho..=rho).collect() } else { vec![-rho, rho] };
for x in xs {
if steps([x, y, z], conn) <= i64::from(k) && visit([x, y, z]) {
return true;
}
}
}
}
}
false
}
fn origin(cell: [i64; 3]) -> Option<Coord> {
let o = |c: i64| i32::try_from(c.checked_mul(CELL)?).ok();
Some([o(cell[0])?, o(cell[1])?, o(cell[2])?])
}
fn index(v: Coord) -> [i64; 3] {
v.map(|c| i64::from(c).div_euclid(CELL))
}
fn add(a: [i64; 3], b: [i64; 3]) -> [i64; 3] {
[a[0] + b[0], a[1] + b[1], a[2] + b[2]]
}
fn regions(t: &Topology) -> Result<Vec<([i64; 3], i64)>, TreeError> {
let mut out: Vec<([i64; 3], i64)> = t
.leaf_numbers()
.map(|n| t.leaf_origin(n).map(|o| (index(o), 1)))
.collect::<Result<_, _>>()?;
for n in t.tile_numbers() {
let (level, o) = t.tile(n)?;
out.push((index(o), i64::from(level.edge()) / CELL));
}
Ok(out)
}
fn around(lo: [i64; 3], side: i64, k: u32, conn: Connectivity, mut visit: impl FnMut([i64; 3])) {
let r = reach(k);
let hi = lo.map(|c| c + side);
let gap = |c: i64, axis: usize| {
if c < lo[axis] {
c - lo[axis]
} else if c >= hi[axis] {
c - hi[axis] + 1
} else {
0
}
};
for z in lo[2] - r..hi[2] + r {
for y in lo[1] - r..hi[1] + r {
let inside = gap(y, 1) == 0 && gap(z, 2) == 0;
let xs: Vec<i64> = if inside {
(lo[0] - r..lo[0]).chain(hi[0]..hi[0] + r).collect()
} else {
(lo[0] - r..hi[0] + r).collect()
};
for x in xs {
if steps([gap(x, 0), gap(y, 1), gap(z, 2)], conn) <= i64::from(k) {
visit([x, y, z]);
}
}
}
}
}
pub fn dilate(t: &Topology, conn: Connectivity, k: u32) -> Result<(Topology, Remap), TreeError> {
within_count(®ions(t)?, k)?;
let mut e = t.edit();
for o in grown(t, conn, k)? {
e.activate(o)?;
}
e.commit()
}
fn bounds(regions: &[([i64; 3], i64)]) -> Option<([i64; 3], [i64; 3])> {
let mut it = regions.iter();
let (lo, side) = it.next()?;
let mut b = (*lo, lo.map(|c| c + side));
for (lo, side) in it {
b.0 = [0, 1, 2].map(|k| b.0[k].min(lo[k]));
b.1 = [0, 1, 2].map(|k| b.1[k].max(lo[k] + side));
}
Some(b)
}
fn within_count(regions: &[([i64; 3], i64)], k: u32) -> Result<(), TreeError> {
let r = reach(k);
let wide = |n: i64| u128::try_from(n).unwrap_or(0);
let cube = |n: i64| wide(n).saturating_mul(wide(n)).saturating_mul(wide(n));
let shells = regions.iter().fold(0_u128, |sum, (_, side)| {
sum.saturating_add(cube(side + 2 * r).saturating_sub(cube(*side)))
});
let (first, last) = (i64::from(i32::MIN) / CELL, i64::from(i32::MAX) / CELL + 1);
let grown = bounds(regions).map_or(0, |(lo, hi)| {
(0..3).fold(1_u128, |v, k| {
let side = (hi[k] + r).min(last) - (lo[k] - r).max(first);
v.saturating_mul(wide(side))
})
});
if shells.min(grown) > u128::from(u32::MAX) {
return Err(TreeError::ArenaFull(NodeKind::Leaf));
}
Ok(())
}
fn grown(t: &Topology, conn: Connectivity, k: u32) -> Result<BTreeSet<Coord>, TreeError> {
let mut cells = BTreeSet::new();
for (lo, side) in regions(t)? {
around(lo, side, k, conn, |c| {
if let Some(o) = origin(c).filter(|o| t.locate(*o) == Location::Background) {
cells.insert(o);
}
});
}
Ok(cells)
}
fn near_background(t: &Topology, cell: [i64; 3], k: u32, conn: Connectivity) -> bool {
any_within(k, conn, |d| {
origin(add(cell, d)).is_none_or(|o| t.locate(o) == Location::Background)
})
}
pub fn erode(t: &Topology, conn: Connectivity, k: u32) -> Result<(Topology, Remap), TreeError> {
let widest =
bounds(®ions(t)?).map_or(0, |(lo, hi)| (0..3).map(|a| hi[a] - lo[a]).max().unwrap_or(0));
let k = u32::try_from(widest.saturating_mul(CELL)).map_or(k, |w| k.min(w));
let (leaves, split) = removed(t, conn, k)?;
let mut e = t.edit();
for o in &split {
e.split_tile(*o)?;
}
for o in leaves.iter().chain(&split) {
e.deactivate(*o)?;
}
e.commit()
}
fn removed(
t: &Topology,
conn: Connectivity,
k: u32,
) -> Result<(Vec<Coord>, BTreeSet<Coord>), TreeError> {
let mut leaves = Vec::new();
for n in t.leaf_numbers() {
let o = t.leaf_origin(n)?;
if near_background(t, index(o), k, conn) {
leaves.push(o);
}
}
let r = reach(k);
let mut split = BTreeSet::new();
for n in t.tile_numbers() {
let (level, o) = t.tile(n)?;
let (lo, side) = (index(o), i64::from(level.edge()) / CELL);
let edge = |c: i64| c < r || c >= side - r;
for z in 0..side {
for y in 0..side {
let xs: Vec<i64> = if edge(y) || edge(z) {
(0..side).collect()
} else {
(0..r).chain(side - r..side).collect()
};
for x in xs {
let cell = add(lo, [x, y, z]);
if near_background(t, cell, k, conn) {
split.extend(origin(cell));
}
}
}
}
}
Ok((leaves, split))
}
pub trait Ordered: Value + PartialOrd + sealed::Sealed {}
mod sealed {
pub trait Sealed {}
impl Sealed for f32 {}
impl Sealed for f64 {}
impl Sealed for u8 {}
impl Sealed for u32 {}
}
impl Ordered for f32 {}
impl Ordered for f64 {}
impl Ordered for u8 {}
impl Ordered for u32 {}
fn pick<T: Ordered>(a: T, b: T, keep: Ordering) -> T {
match a.partial_cmp(&b) {
None if a.partial_cmp(&a).is_none() => a,
None => b,
Some(o) if o == keep.reverse() => b,
Some(_) => a,
}
}
fn neighbourhood(conn: Connectivity) -> Vec<isize> {
let edge = isize::try_from(GATHER_EDGE).unwrap_or(10);
let mut out = Vec::new();
for z in -1..=1_isize {
for y in -1..=1_isize {
for x in -1..=1_isize {
let n = x.abs() + y.abs() + z.abs();
if n > 0 && (conn == Connectivity::TwentySix || n == 1) {
out.push(x + edge * (y + edge * z));
}
}
}
}
out
}
fn filter<T: Ordered>(
t: &Topology,
f: &Field<T>,
conn: Connectivity,
k: u32,
keep: Ordering,
) -> Result<Field<T>, FieldError> {
f.check(t)?;
let near = neighbourhood(conn);
let mut current = f.clone();
for _ in 0..k {
let mut next = current.clone();
par_update(t, &mut next, |n, out| {
let mut g = [current.background(); GATHER_VOXELS];
gather(t, ¤t, n, &mut g)?;
for (i, o) in out.iter_mut().enumerate() {
let c = (i % 8 + 1)
+ GATHER_EDGE * (i / 8 % 8 + 1)
+ GATHER_EDGE * GATHER_EDGE * (i / 64 + 1);
*o = near.iter().fold(g[c], |v, d| {
g.get(c.wrapping_add_signed(*d)).map_or(v, |w| pick(v, *w, keep))
});
}
Ok::<(), FieldError>(())
})?;
let settled = same_leaves(t, ¤t, &next)?;
current = next;
if settled {
break;
}
}
Ok(current)
}
fn same_leaves<T: Value>(t: &Topology, a: &Field<T>, b: &Field<T>) -> Result<bool, FieldError> {
for n in t.leaf_numbers() {
if !a.leaf(t, n)?.iter().zip(b.leaf(t, n)?).all(|(x, y)| x.same_bits(y)) {
return Ok(false);
}
}
Ok(true)
}
pub fn dilate_values<T: Ordered>(
t: &Topology,
f: &Field<T>,
conn: Connectivity,
k: u32,
) -> Result<Field<T>, FieldError> {
filter(t, f, conn, k, Ordering::Greater)
}
pub fn erode_values<T: Ordered>(
t: &Topology,
f: &Field<T>,
conn: Connectivity,
k: u32,
) -> Result<Field<T>, FieldError> {
filter(t, f, conn, k, Ordering::Less)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_root_tile_grows_and_shrinks_by_its_shell() {
let empty = Topology::new();
let mut e = empty.edit();
e.add_tile(crate::tree::TileLevel::Root, [0, 0, 0]).unwrap();
let (t, _) = e.commit().unwrap();
assert_eq!(grown(&t, Connectivity::Six, 1).unwrap().len(), 6 * 512 * 512);
let (leaves, split) = removed(&t, Connectivity::Six, 1).unwrap();
assert!(leaves.is_empty());
assert_eq!(split.len(), 512 * 512 * 512 - 510 * 510 * 510);
}
#[test]
fn the_box_holds_every_region() {
assert_eq!(bounds(&[]), None);
assert_eq!(bounds(&[([1, 2, 3], 2), ([-4, 5, 0], 1)]), Some(([-4, 2, 0], [3, 6, 5])));
}
#[test]
fn a_dilation_is_refused_just_beyond_a_u32s_count() {
let refused = |regions: &[([i64; 3], i64)], k| within_count(regions, k).is_err();
assert!(!refused(&[([0; 3], 3)], 6488));
assert!(refused(&[([0; 3], 3)], 6489));
let (first, last) = (i64::from(i32::MIN) / CELL, i64::from(i32::MAX) / CELL + 1);
for lo in [first, last - 1] {
assert!(!refused(&[([lo; 3], 1)], 8 * 1624), "{lo}");
assert!(refused(&[([lo; 3], 1)], 8 * 1625), "{lo}");
}
let mut many = vec![([0, 0, 0], 1); 528];
many.push(([54, 56, 65336], 1));
assert!(!refused(&many, 800));
many.push(([54, 56, 65337], 1));
assert!(refused(&many, 800));
}
}