use crate::error::FieldError;
use crate::field::{Field, LEAF_VOXELS, Value, local_index};
use crate::manifest::Offset;
use crate::topology::{LeafNumber, Location, Topology};
pub const GATHER_EDGE: usize = 10;
pub const GATHER_VOXELS: usize = GATHER_EDGE * GATHER_EDGE * GATHER_EDGE;
#[derive(Clone, Copy)]
enum Source<'a, T> {
Leaf(&'a [T; LEAF_VOXELS]),
Value(T),
}
fn block(d: [i32; 3]) -> usize {
let c = (d[0] + 1) + 3 * (d[1] + 1) + 9 * (d[2] + 1);
c.cast_unsigned() as usize
}
fn sources<'a, T: Value>(
t: &Topology,
f: &'a Field<T>,
n: LeafNumber,
) -> Result<[Source<'a, T>; 27], FieldError> {
let mut out = [Source::Value(f.background()); 27];
out[block([0, 0, 0])] = Source::Leaf(f.leaf(t, n)?);
let row = t.neighbours(n).map_err(|_| FieldError::UnknownLeaf(n.get()))?;
for d in Offset::all() {
out[block(d.delta())] = match row[d.index()] {
Location::Leaf(m) => Source::Leaf(f.leaf(t, m)?),
Location::Tile(tile, _) => Source::Value(f.tile(t, tile)?),
Location::Background => Source::Value(f.background()),
};
}
Ok(out)
}
pub fn gather<T: Value>(
t: &Topology,
f: &Field<T>,
n: LeafNumber,
out: &mut [T; GATHER_VOXELS],
) -> Result<(), FieldError> {
let sources = sources(t, f, n)?;
let cells =
(-1..=8_i32).flat_map(|z| (-1..=8).flat_map(move |y| (-1..=8).map(move |x| [x, y, z])));
for (o, g) in out.iter_mut().zip(cells) {
*o = match sources[block(g.map(|c| c.div_euclid(8)))] {
Source::Leaf(values) => values[local_index(g)],
Source::Value(v) => v,
};
}
Ok(())
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GhostWidth {
One,
Two,
Three,
}
impl GhostWidth {
pub fn layers(self) -> i32 {
match self {
GhostWidth::One => 1,
GhostWidth::Two => 2,
GhostWidth::Three => 3,
}
}
pub fn edge(self) -> usize {
match self {
GhostWidth::One => 10,
GhostWidth::Two => 12,
GhostWidth::Three => 14,
}
}
pub fn voxels(self) -> usize {
self.edge() * self.edge() * self.edge()
}
}
pub fn gather_wide<T: Value>(
t: &Topology,
f: &Field<T>,
n: LeafNumber,
width: GhostWidth,
out: &mut Vec<T>,
) -> Result<(), FieldError> {
let sources = sources(t, f, n)?;
let w = width.layers();
out.clear();
out.reserve(width.voxels());
for z in -w..8 + w {
for y in -w..8 + w {
for x in -w..8 + w {
let g = [x, y, z];
out.push(match sources[block(g.map(|c| c.div_euclid(8)))] {
Source::Leaf(values) => values[local_index(g)],
Source::Value(v) => v,
});
}
}
}
Ok(())
}