use std::iter::repeat_n;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::error::FieldError;
use crate::topology::{
LeafNumber, LeafSource, Location, Part, Remap, TileNumber, TileSource, Topology,
};
use crate::tree::Coord;
pub const LEAF_VOXELS: usize = 512;
pub trait Value:
Copy + Send + Sync + 'static + FromBytes + IntoBytes + Immutable + KnownLayout
{
fn same_bits(&self, other: &Self) -> bool;
}
mod sealed {
use super::{AnyField, Field};
pub trait Sealed: Sized {
fn any(f: Field<Self>) -> AnyField;
}
impl Sealed for f32 {
fn any(f: Field<Self>) -> AnyField {
AnyField::F32(f)
}
}
impl Sealed for f64 {
fn any(f: Field<Self>) -> AnyField {
AnyField::F64(f)
}
}
impl Sealed for u8 {
fn any(f: Field<Self>) -> AnyField {
AnyField::U8(f)
}
}
impl Sealed for u32 {
fn any(f: Field<Self>) -> AnyField {
AnyField::U32(f)
}
}
impl Sealed for [f32; 3] {
fn any(f: Field<Self>) -> AnyField {
AnyField::F32x3(f)
}
}
impl Sealed for [f64; 3] {
fn any(f: Field<Self>) -> AnyField {
AnyField::F64x3(f)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ValueType {
F32,
F64,
U8,
U32,
F32x3,
F64x3,
}
impl ValueType {
pub const fn code(self) -> u32 {
match self {
ValueType::F32 => 1,
ValueType::F64 => 2,
ValueType::U8 => 3,
ValueType::U32 => 4,
ValueType::F32x3 => 5,
ValueType::F64x3 => 6,
}
}
pub const fn from_code(code: u32) -> Option<Self> {
match code {
1 => Some(ValueType::F32),
2 => Some(ValueType::F64),
3 => Some(ValueType::U8),
4 => Some(ValueType::U32),
5 => Some(ValueType::F32x3),
6 => Some(ValueType::F64x3),
_ => None,
}
}
pub const fn name(self) -> &'static str {
match self {
ValueType::F32 => "f32",
ValueType::F64 => "f64",
ValueType::U8 => "u8",
ValueType::U32 => "u32",
ValueType::F32x3 => "[f32; 3]",
ValueType::F64x3 => "[f64; 3]",
}
}
}
impl std::fmt::Display for ValueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AnyField {
F32(Field<f32>),
F64(Field<f64>),
U8(Field<u8>),
U32(Field<u32>),
F32x3(Field<[f32; 3]>),
F64x3(Field<[f64; 3]>),
}
impl AnyField {
pub fn value_type(&self) -> ValueType {
match self {
AnyField::F32(_) => ValueType::F32,
AnyField::F64(_) => ValueType::F64,
AnyField::U8(_) => ValueType::U8,
AnyField::U32(_) => ValueType::U32,
AnyField::F32x3(_) => ValueType::F32x3,
AnyField::F64x3(_) => ValueType::F64x3,
}
}
}
impl<T: Stored> From<Field<T>> for AnyField {
fn from(f: Field<T>) -> Self {
T::any(f)
}
}
pub trait Stored: Value + sealed::Sealed {
const VALUE_TYPE: ValueType;
}
impl Value for f32 {
fn same_bits(&self, other: &Self) -> bool {
self.to_bits() == other.to_bits()
}
}
impl Stored for f32 {
const VALUE_TYPE: ValueType = ValueType::F32;
}
impl Value for f64 {
fn same_bits(&self, other: &Self) -> bool {
self.to_bits() == other.to_bits()
}
}
impl Stored for f64 {
const VALUE_TYPE: ValueType = ValueType::F64;
}
impl Value for u8 {
fn same_bits(&self, other: &Self) -> bool {
self == other
}
}
impl Stored for u8 {
const VALUE_TYPE: ValueType = ValueType::U8;
}
impl Value for u32 {
fn same_bits(&self, other: &Self) -> bool {
self == other
}
}
impl Stored for u32 {
const VALUE_TYPE: ValueType = ValueType::U32;
}
impl Value for [f32; 3] {
fn same_bits(&self, other: &Self) -> bool {
self.iter().zip(other).all(|(a, b)| a.to_bits() == b.to_bits())
}
}
impl Stored for [f32; 3] {
const VALUE_TYPE: ValueType = ValueType::F32x3;
}
impl Value for [f64; 3] {
fn same_bits(&self, other: &Self) -> bool {
self.iter().zip(other).all(|(a, b)| a.to_bits() == b.to_bits())
}
}
impl Stored for [f64; 3] {
const VALUE_TYPE: ValueType = ValueType::F64x3;
}
pub(crate) trait ForType {
type Out;
fn run<T: Stored>(self) -> Self::Out;
}
pub(crate) fn with_type<W: ForType>(value_type: ValueType, work: W) -> W::Out {
match value_type {
ValueType::F32 => work.run::<f32>(),
ValueType::F64 => work.run::<f64>(),
ValueType::U8 => work.run::<u8>(),
ValueType::U32 => work.run::<u32>(),
ValueType::F32x3 => work.run::<[f32; 3]>(),
ValueType::F64x3 => work.run::<[f64; 3]>(),
}
}
pub type MergeRule<T> = dyn Fn(&[(T, u32)]) -> Option<T> + Send + Sync;
pub fn uniform<T: Value>(values: &[(T, u32)]) -> Option<T> {
let (first, _) = *values.first()?;
values.iter().all(|(v, _)| v.same_bits(&first)).then_some(first)
}
const LEAF_COUNT: u32 = 512;
#[inline]
pub(crate) fn leaf_place(v: Coord) -> u32 {
let [x, y, z] = v.map(|c| c.cast_unsigned() & 7);
x + 8 * y + 64 * z
}
#[inline]
pub(crate) fn local_index(v: Coord) -> usize {
leaf_place(v) as usize
}
#[derive(Debug, Clone)]
pub struct Field<T> {
leaf_values: Vec<T>,
tile_values: Vec<T>,
background: T,
stamp: u64,
}
impl<T: Value> Field<T> {
pub fn new(t: &Topology, background: T) -> Self {
Self {
leaf_values: vec![background; t.leaf_count() * LEAF_VOXELS],
tile_values: vec![background; t.tile_count()],
background,
stamp: t.stamp(),
}
}
pub fn background(&self) -> T {
self.background
}
pub(crate) fn check(&self, t: &Topology) -> Result<(), FieldError> {
if self.stamp == t.stamp() { Ok(()) } else { Err(FieldError::StaleField) }
}
pub(crate) fn leaves(&self, t: &Topology) -> Result<&[[T; LEAF_VOXELS]], FieldError> {
self.check(t)?;
Ok(self.leaf_values.as_chunks().0)
}
pub(crate) fn leaves_mut(
&mut self,
t: &Topology,
) -> Result<&mut [[T; LEAF_VOXELS]], FieldError> {
self.check(t)?;
Ok(self.leaf_values.as_chunks_mut().0)
}
fn leaf_slice(&self, n: LeafNumber) -> Result<&[T], FieldError> {
let s = n.get() as usize * LEAF_VOXELS;
self.leaf_values.get(s..s + LEAF_VOXELS).ok_or(FieldError::UnknownLeaf(n.get()))
}
fn tile_value(&self, n: TileNumber) -> Result<T, FieldError> {
self.tile_values.get(n.get() as usize).copied().ok_or(FieldError::UnknownTile(n.get()))
}
pub fn leaf(&self, t: &Topology, n: LeafNumber) -> Result<&[T; LEAF_VOXELS], FieldError> {
self.check(t)?;
self.leaf_slice(n)?.try_into().map_err(|_| FieldError::UnknownLeaf(n.get()))
}
pub fn leaf_mut(
&mut self,
t: &Topology,
n: LeafNumber,
) -> Result<&mut [T; LEAF_VOXELS], FieldError> {
self.check(t)?;
let s = n.get() as usize * LEAF_VOXELS;
let slice = self.leaf_values.get_mut(s..s + LEAF_VOXELS);
slice.and_then(|c| c.try_into().ok()).ok_or(FieldError::UnknownLeaf(n.get()))
}
pub fn tile(&self, t: &Topology, n: TileNumber) -> Result<T, FieldError> {
self.check(t)?;
self.tile_value(n)
}
pub fn set_tile(&mut self, t: &Topology, n: TileNumber, value: T) -> Result<(), FieldError> {
self.check(t)?;
let slot = self.tile_values.get_mut(n.get() as usize);
*slot.ok_or(FieldError::UnknownTile(n.get()))? = value;
Ok(())
}
pub fn value_at(&self, t: &Topology, v: Coord) -> Result<T, FieldError> {
self.check(t)?;
Ok(self.at(t.locate(v), v))
}
pub(crate) fn at(&self, loc: Location, v: Coord) -> T {
match loc {
Location::Leaf(n) => self.leaf_values[n.get() as usize * LEAF_VOXELS + local_index(v)],
Location::Tile(n, _) => self.tile_values[n.get() as usize],
Location::Background => self.background,
}
}
pub(crate) fn at_mut(&mut self, n: LeafNumber, v: Coord) -> &mut T {
&mut self.leaf_values[n.get() as usize * LEAF_VOXELS + local_index(v)]
}
pub(crate) fn tile_value_at(&self, n: u32) -> Result<T, FieldError> {
self.tile_values.get(n as usize).copied().ok_or(FieldError::UnknownTile(n))
}
pub(crate) fn belongs_to(&self, t: &Topology) -> Result<(), FieldError> {
self.check(t)
}
fn merged(&self, parts: &[Part]) -> Result<Vec<(T, u32)>, FieldError> {
let mut out = Vec::new();
for p in parts {
match *p {
Part::Leaf(n) => out.extend(self.leaf_slice(n)?.iter().map(|v| (*v, 1))),
Part::Tile(t) => out.push((self.tile_value(t)?, LEAF_COUNT)),
Part::Background(k) => out.push((self.background, LEAF_COUNT.saturating_mul(k))),
}
}
Ok(out)
}
pub fn remap(&self, r: &Remap, rule: &MergeRule<T>) -> Result<Field<T>, FieldError> {
if self.stamp != r.from {
return Err(FieldError::StaleField);
}
let mut leaf_values = Vec::with_capacity(r.leaf_sources().len() * LEAF_VOXELS);
for s in r.leaf_sources() {
match *s {
LeafSource::Leaf(n) => leaf_values.extend_from_slice(self.leaf_slice(n)?),
LeafSource::Split(t) => {
leaf_values.extend(repeat_n(self.tile_value(t)?, LEAF_VOXELS))
}
LeafSource::Background => {
leaf_values.extend(repeat_n(self.background, LEAF_VOXELS))
}
}
}
let tile_values = r
.tile_sources()
.iter()
.zip(0_u32..)
.map(|(s, i)| match *s {
TileSource::Tile(t) => self.tile_value(t),
TileSource::Background => Ok(self.background),
TileSource::Merged(range) => {
rule(&self.merged(r.parts(range))?).ok_or(FieldError::MergeRefused(i))
}
})
.collect::<Result<Vec<T>, FieldError>>()?;
Ok(Field { leaf_values, tile_values, background: self.background, stamp: r.to })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tree::TileLevel;
#[test]
fn each_value_type_is_its_code_and_name_and_its_field_its_variant() {
let t = Topology::new();
let fields: [(AnyField, ValueType, u32, &str); 6] = [
(Field::new(&t, 0.0_f32).into(), f32::VALUE_TYPE, 1, "f32"),
(Field::new(&t, 0.0_f64).into(), f64::VALUE_TYPE, 2, "f64"),
(Field::new(&t, 0_u8).into(), u8::VALUE_TYPE, 3, "u8"),
(Field::new(&t, 0_u32).into(), u32::VALUE_TYPE, 4, "u32"),
(Field::new(&t, [0.0_f32; 3]).into(), <[f32; 3]>::VALUE_TYPE, 5, "[f32; 3]"),
(Field::new(&t, [0.0_f64; 3]).into(), <[f64; 3]>::VALUE_TYPE, 6, "[f64; 3]"),
];
for (field, ty, code, name) in fields {
assert_eq!(field.value_type(), ty);
assert_eq!((ty.code(), ty.name(), ty.to_string()), (code, name, name.to_owned()));
assert_eq!(ValueType::from_code(code), Some(ty));
}
assert_eq!([0, 7, u32::MAX].map(ValueType::from_code), [None; 3]);
}
#[test]
fn every_value_type_compares_by_bits() {
assert!(1.5_f32.same_bits(&1.5) && !0.0_f32.same_bits(&-0.0) && !1.0_f32.same_bits(&2.0));
assert!(1.5_f64.same_bits(&1.5) && !0.0_f64.same_bits(&-0.0) && !1.0_f64.same_bits(&2.0));
assert!(f64::NAN.same_bits(&f64::NAN) && !f64::NAN.same_bits(&-f64::NAN));
assert!(7_u8.same_bits(&7) && !7_u8.same_bits(&8));
assert!(7_u32.same_bits(&7) && !7_u32.same_bits(&8));
assert!([1.0_f32, 2.0, 3.0].same_bits(&[1.0, 2.0, 3.0]));
assert!(![1.0_f32, 2.0, 3.0].same_bits(&[1.0, 2.0, -3.0]));
assert!([1.0_f64, 2.0, 3.0].same_bits(&[1.0, 2.0, 3.0]));
assert!(![1.0_f64, 2.0, 3.0].same_bits(&[-1.0, 2.0, 3.0]));
}
#[test]
fn a_root_tile_split_passes_its_value_to_every_part() {
let t0 = Topology::new();
let mut e = t0.edit();
e.add_tile(TileLevel::Root, [0, 0, 0]).unwrap();
let (t1, _) = e.commit().unwrap();
let mut f = Field::new(&t1, 0.0_f32);
f.set_tile(&t1, TileNumber(0), 5.0).unwrap();
let mut e = t1.edit();
e.split_tile([300, 4000, 17]).unwrap();
let (t2, r) = e.commit().unwrap();
let g = f.remap(&r, &uniform).unwrap();
assert_eq!(t2.tile_count(), 32767 + 4095);
for n in t2.tile_numbers() {
assert_eq!(g.tile(&t2, n).unwrap().to_bits(), 5.0_f32.to_bits());
}
let leaf = g.leaf(&t2, LeafNumber(0)).unwrap();
assert!(leaf.iter().all(|v| v.to_bits() == 5.0_f32.to_bits()));
assert_eq!(g.value_at(&t2, [-1, 0, 0]).map(f32::to_bits), Ok(0.0_f32.to_bits()));
}
fn mean(values: &[(f64, u32)]) -> Option<f64> {
let n: f64 = values.iter().map(|(_, c)| f64::from(*c)).sum();
Some(values.iter().map(|(v, c)| v * f64::from(*c)).sum::<f64>() / n)
}
#[test]
fn a_merge_weighs_each_value_by_its_voxels() {
let t0 = Topology::new();
let mut e = t0.edit();
for i in 0..4096 {
let r = [i % 16 * 8, i / 16 % 16 * 8, i / 256 * 8];
if i == 0 { e.activate(r).map(|_| ()) } else { e.add_tile(TileLevel::Lower, r) }
.unwrap();
}
let (t1, _) = e.commit().unwrap();
let mut f = Field::new(&t1, 0.0_f64);
for n in t1.tile_numbers().collect::<Vec<_>>() {
f.set_tile(&t1, n, 2.0).unwrap();
}
let leaf = t1.leaf_numbers().next().unwrap();
f.leaf_mut(&t1, leaf).unwrap().fill(4.0);
let mut e = t1.edit();
e.merge(TileLevel::Upper, [0, 0, 0]).unwrap();
let (t2, r) = e.commit().unwrap();
let g = f.remap(&r, &mean).unwrap();
let n = t2.tile_numbers().next().unwrap();
assert_eq!(g.tile(&t2, n).map(f64::to_bits), Ok((2.0 + 2.0 / 4096.0_f64).to_bits()));
let refused = f.remap(&r, &uniform);
assert_eq!(refused.map(|_| ()), Err(FieldError::MergeRefused(0)));
}
#[test]
fn regions_activated_in_the_merging_commit_count_as_the_background() {
let t0 = Topology::new();
let mut e = t0.edit();
for i in 0..2048 {
e.add_tile(TileLevel::Lower, [i % 16 * 8, i / 16 % 16 * 8, i / 256 * 8]).unwrap();
}
let (t1, _) = e.commit().unwrap();
let mut f = Field::new(&t1, 3.0_f32);
let mut e = t1.edit();
for i in 2048..4096 {
e.activate([i % 16 * 8, i / 16 % 16 * 8, i / 256 * 8]).unwrap();
}
e.merge(TileLevel::Upper, [0, 0, 0]).unwrap();
let (t2, r) = e.commit().unwrap();
let TileSource::Merged(range) = r.tile_sources()[0] else { panic!("merged") };
assert_eq!(r.parts(range).last(), Some(&Part::Background(2048)));
let g = f.remap(&r, &uniform).unwrap();
assert_eq!(g.value_at(&t2, [100, 100, 100]).map(f32::to_bits), Ok(3.0_f32.to_bits()));
let n = t1.tile_numbers().next().unwrap();
f.set_tile(&t1, n, 1.0).unwrap();
assert_eq!(f.remap(&r, &uniform).map(|_| ()), Err(FieldError::MergeRefused(0)));
}
#[test]
fn a_stale_field_is_refused_everywhere() {
let t0 = Topology::new();
let mut e = t0.edit();
e.activate([0, 0, 0]).unwrap();
e.add_tile(TileLevel::Lower, [8, 0, 0]).unwrap();
let (t1, _) = e.commit().unwrap();
let mut f = Field::new(&t1, 1_u8);
let (t2, r) = t1.edit().commit().unwrap();
let stale = Err(FieldError::StaleField);
assert_eq!(f.leaf(&t2, LeafNumber(0)).map(|_| ()), stale);
assert_eq!(f.tile(&t2, TileNumber(0)).map(|_| ()), stale);
assert_eq!(f.value_at(&t2, [0, 0, 0]).map(|_| ()), stale);
assert_eq!(f.set_tile(&t2, TileNumber(0), 2), stale);
assert_eq!(f.leaf_mut(&t2, LeafNumber(0)).map(|_| ()), stale);
let g = f.remap(&r, &uniform).unwrap();
assert_eq!(g.remap(&r, &uniform).map(|_| ()), stale, "a remap applies once");
assert_eq!(g.value_at(&t2, [0, 0, 0]), Ok(1));
assert_eq!(f.value_at(&t1, [0, 0, 0]), Ok(1), "the old pair still works");
}
#[test]
fn a_voxel_finds_its_place_in_its_leaf() {
let t0 = Topology::new();
let mut e = t0.edit();
e.activate([-1, 2, 3]).unwrap();
let (t, _) = e.commit().unwrap();
let mut f = Field::new(&t, 0_u32);
for (i, v) in f.leaf_mut(&t, LeafNumber(0)).unwrap().iter_mut().enumerate() {
*v = u32::try_from(i).unwrap();
}
assert_eq!(f.value_at(&t, [-1, 2, 3]), Ok(7 + 8 * 2 + 64 * 3));
assert_eq!(f.leaf(&t, LeafNumber(1)).map(|_| ()), Err(FieldError::UnknownLeaf(1)));
}
}