use std::collections::{BTreeMap, BTreeSet};
use crate::cache::{CacheEntry, CacheFile};
use crate::error::{FormatError, HermiteError, Rule};
use crate::field::local_index;
use crate::field::{Field, LEAF_VOXELS};
use crate::meta::GridMeta;
use crate::topology::{LeafNumber, Location, Topology};
use crate::tree::{Axis, Coord, TileLevel};
use crate::view::{GridView, ViewLocation};
pub const VERSION: &str = "3";
pub const CROSSINGS: &str = "crossings";
pub const NORMAL_TOLERANCE: f64 = 1e-6;
const MASK_BYTES: usize = 3 * 8 * 8;
const CROSSING_BYTES: usize = 20;
const KEY_VERSION: &str = "brodo.hermite.version";
const KEY_SET: &str = "brodo.hermite.set";
const KEY_FIELD: &str = "brodo.hermite.field";
const KEY_GROUP: &str = "brodo.hermite.group.";
const PREFIX: &str = "brodo.hermite.";
const OLD_SET: &str = "hermite.set";
const OLD_VERSION: &str = "hermite.version";
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Crossing {
pub fraction: f32,
pub normal: [f32; 3],
pub group: u32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Crossings {
stamp: u64,
masks: Vec<[[u64; 8]; 3]>,
starts: Vec<u32>,
items: Vec<Crossing>,
}
#[derive(Debug, Clone)]
pub struct CrossingsBuilder {
leaves: usize,
built: Crossings,
}
fn rank(masks: &[[u64; 8]; 3], axis: usize, place: usize) -> Option<(u32, bool)> {
let own = masks.get(axis)?;
let word = *own.get(place / 64)?;
let ones = |w: &[u64]| w.iter().map(|x| x.count_ones()).sum::<u32>();
let before = masks.iter().take(axis).map(|m| ones(m)).sum::<u32>()
+ ones(own.get(..place / 64)?)
+ (word & ((1_u64 << (place % 64)) - 1)).count_ones();
Some((before, word >> (place % 64) & 1 == 1))
}
impl Crossings {
pub fn builder(t: &Topology) -> CrossingsBuilder {
CrossingsBuilder {
leaves: t.leaf_count(),
built: Crossings {
stamp: t.stamp(),
masks: Vec::with_capacity(t.leaf_count()),
starts: vec![0],
items: Vec::new(),
},
}
}
pub fn get(&self, leaf: LeafNumber, axis: Axis, place: usize) -> Option<Crossing> {
let n = usize::try_from(leaf.get()).ok()?;
let (before, set) = rank(self.masks.get(n)?, axis.index(), place)?;
let at = self.starts.get(n)?.checked_add(before)?;
set.then(|| self.items.get(usize::try_from(at).ok()?).copied()).flatten()
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
fn belongs_to(&self, t: &Topology) -> bool {
self.stamp == t.stamp() && self.masks.len() == t.leaf_count()
}
fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.masks.len() * MASK_BYTES + self.items.len() * 20);
for w in self.masks.iter().flatten().flatten() {
out.extend_from_slice(&w.to_le_bytes());
}
for c in &self.items {
out.extend_from_slice(&c.fraction.to_le_bytes());
for x in c.normal {
out.extend_from_slice(&x.to_le_bytes());
}
out.extend_from_slice(&c.group.to_le_bytes());
}
out
}
}
impl CrossingsBuilder {
pub fn leaf(
&mut self,
n: LeafNumber,
axes: [&[(usize, Crossing)]; 3],
) -> Result<(), HermiteError> {
let b = &mut self.built;
if usize::try_from(n.get()).ok() != Some(b.masks.len()) || b.masks.len() >= self.leaves {
return Err(HermiteError::Build("a leaf out of number order"));
}
let mut masks = [[0_u64; 8]; 3];
for (mask, crossings) in masks.iter_mut().zip(axes) {
let mut last = None;
for (place, _) in crossings {
let word = mask.get_mut(place / 64).ok_or(HermiteError::Build("a place"))?;
if last.is_some_and(|l| l >= *place) {
return Err(HermiteError::Build("places out of order"));
}
last = Some(*place);
*word |= 1 << (place % 64);
}
}
b.items.extend(axes.iter().flat_map(|a| a.iter().map(|(_, c)| *c)));
b.masks.push(masks);
let end = u32::try_from(b.items.len()).map_err(|_| HermiteError::Build("a count"))?;
b.starts.push(end);
Ok(())
}
pub fn build(self) -> Result<Crossings, HermiteError> {
if self.built.masks.len() != self.leaves {
return Err(HermiteError::Build("a leaf left out"));
}
Ok(self.built)
}
}
#[derive(Debug, Clone)]
pub struct Hermite {
topology: Topology,
signs: Field<u8>,
crossings: Crossings,
group_names: Vec<String>,
}
#[derive(Debug, Clone)]
struct ViewCrossings<'a> {
masks: &'a [u8],
items: &'a [u8],
starts: Vec<u32>,
}
#[derive(Debug, Clone)]
pub struct HermiteView<'a> {
signs: GridView<'a, u8>,
crossings: ViewCrossings<'a>,
group_names: Vec<String>,
}
fn step(axis: usize) -> Coord {
let mut e = [0; 3];
if let Some(c) = e.get_mut(axis) {
*c = 1;
}
e
}
fn plus(v: Coord, d: Coord) -> Coord {
let ([a, b, c], [x, y, z]) = (v, d);
[a.wrapping_add(x), b.wrapping_add(y), c.wrapping_add(z)]
}
fn minus(v: Coord, d: Coord) -> Coord {
let ([a, b, c], [x, y, z]) = (v, d);
[a.wrapping_sub(x), b.wrapping_sub(y), c.wrapping_sub(z)]
}
fn neighbour(v: Coord, d: Coord, forward: bool) -> Option<Coord> {
let step = |c: i32, k: i32| if forward { c.checked_add(k) } else { c.checked_sub(k) };
Some([step(v[0], d[0])?, step(v[1], d[1])?, step(v[2], d[2])?])
}
fn violation(at: Option<Coord>, axis: Option<usize>, rule: Rule) -> HermiteError {
HermiteError::Violation { at, axis: axis.and_then(|a| Axis::ALL.get(a).copied()), rule }
}
fn check_names(names: &[String]) -> Result<(), HermiteError> {
let mut seen = BTreeSet::new();
for (id, name) in names.iter().enumerate() {
let bad = if id == 0 { !name.is_empty() } else { name.is_empty() || !seen.insert(name) };
if bad {
return Err(HermiteError::BadGroupName(u32::try_from(id).unwrap_or(u32::MAX)));
}
}
Ok(())
}
trait Source {
fn leaves(&self) -> Result<Vec<Coord>, HermiteError>;
fn tiles(&self) -> Result<Vec<(TileLevel, Coord, u32)>, HermiteError>;
fn is_leaf(&self, v: Coord) -> Result<bool, HermiteError>;
fn holds_node(&self, level: TileLevel, v: Coord) -> Result<bool, HermiteError>;
fn sign_at(&self, v: Coord) -> Result<u8, HermiteError>;
fn sign_leaf(&self, n: u32) -> Result<&[u8; LEAF_VOXELS], HermiteError>;
fn crossing(&self, axis: usize, n: u32, place: usize)
-> Result<Option<Crossing>, HermiteError>;
fn sign_tile(&self, t: u32) -> Result<u8, HermiteError>;
fn sign_background(&self) -> u8;
}
fn check_crossing(c: Crossing, axis: usize, lower_inside: bool, groups: usize) -> Result<(), Rule> {
let Crossing { fraction, normal, group } = c;
if !(0.0..=1.0).contains(&fraction) {
return Err(Rule::Fraction);
}
let length = normal.iter().map(|x| f64::from(*x) * f64::from(*x)).sum::<f64>().sqrt();
let near = (length - 1.0).abs().partial_cmp(&NORMAL_TOLERANCE);
if !matches!(near, Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)) {
return Err(Rule::NormalLength);
}
let along = normal.get(axis).copied().unwrap_or(0.0);
if (lower_inside && along < 0.0) || (!lower_inside && along > 0.0) {
return Err(Rule::NormalInward);
}
if usize::try_from(group).map_or(true, |g| g >= groups) {
return Err(Rule::UnnamedGroup);
}
Ok(())
}
fn voxel(origin: Coord, i: usize) -> Coord {
let i = i32::try_from(i).unwrap_or(0);
plus(origin, [i & 7, i >> 3 & 7, i >> 6])
}
fn check(s: &impl Source, names: &[String]) -> Result<(), HermiteError> {
check_names(names)?;
let groups = names.len().max(1);
if s.sign_background() != 0 {
return Err(violation(None, None, Rule::Sign));
}
let tiles = s.tiles()?;
for (_, origin, t) in &tiles {
if s.sign_tile(*t)? > 1 {
return Err(violation(Some(*origin), None, Rule::Sign));
}
}
let leaves = s.leaves()?;
for (n, origin) in (0_u32..).zip(&leaves) {
let bad = s.sign_leaf(n)?.iter().position(|sign| *sign > 1);
if let Some(i) = bad {
return Err(violation(Some(voxel(*origin, i)), None, Rule::Sign));
}
}
for (level, origin, t) in &tiles {
check_tile_faces(s, *level, *origin, s.sign_tile(*t)?)?;
}
for (n, origin) in (0_u32..).zip(leaves) {
check_leaf(s, n, origin, groups)?;
}
Ok(())
}
fn check_leaf(s: &impl Source, n: u32, origin: Coord, groups: usize) -> Result<(), HermiteError> {
let signs = s.sign_leaf(n)?;
let sign_of = |v: Coord| -> Result<u8, HermiteError> {
let local = minus(v, origin);
if local.iter().all(|c| (0..8).contains(c)) {
return signs.get(local_index(v)).copied().ok_or(HermiteError::Violation {
at: Some(v),
axis: None,
rule: Rule::Sign,
});
}
s.sign_at(v)
};
for axis in 0..3 {
let e = step(axis);
for (i, sign) in signs.iter().enumerate() {
let v = voxel(origin, i);
let crossing = s.crossing(axis, n, i)?;
let beyond = match neighbour(v, e, true) {
Some(w) => sign_of(w)?,
None => 0,
};
match (crossing, beyond == *sign) {
(None, true) => {}
(Some(c), false) => check_crossing(c, axis, *sign == 1, groups)
.map_err(|rule| violation(Some(v), Some(axis), rule))?,
(Some(_), true) | (None, false) => {
return Err(violation(Some(v), Some(axis), Rule::NoCrossing));
}
}
match neighbour(v, e, false) {
Some(u) => {
let in_leaf = minus(u, origin).iter().all(|c| (0..8).contains(c));
if !in_leaf && !s.is_leaf(u)? && sign_of(u)? != *sign {
return Err(violation(Some(u), Some(axis), Rule::Owner));
}
}
None if *sign != 0 => return Err(violation(Some(v), Some(axis), Rule::Owner)),
None => {}
}
}
}
Ok(())
}
fn check_tile_faces(
s: &impl Source,
level: TileLevel,
origin: Coord,
sign: u8,
) -> Result<(), HermiteError> {
let far = level.edge() - 1;
for axis in 0..3 {
let e = step(axis);
let upper = plus(origin, e.map(|c| c * far));
let face = Face { axis, sign, forward: true };
match neighbour(upper, e, true) {
Some(o) => face.check_region(s, level, o)?,
None if sign != 0 => return Err(violation(Some(upper), Some(axis), Rule::Owner)),
None => {}
}
let face = Face { forward: false, ..face };
match neighbour(origin, e, false) {
Some(o) => face.check_region(s, level, o)?,
None if sign != 0 => return Err(violation(Some(origin), Some(axis), Rule::Owner)),
None => {}
}
}
Ok(())
}
#[derive(Clone, Copy)]
struct Face {
axis: usize,
sign: u8,
forward: bool,
}
impl Face {
fn check_region(
self,
s: &impl Source,
level: TileLevel,
at: Coord,
) -> Result<(), HermiteError> {
if !s.holds_node(level, at)? {
if s.sign_at(at)? == self.sign {
return Ok(());
}
let e = step(self.axis);
let owner = if self.forward { minus(at, e) } else { at };
return Err(violation(Some(owner), Some(self.axis), Rule::Owner));
}
let finer = match level {
TileLevel::Root => TileLevel::Upper,
TileLevel::Upper => TileLevel::Lower,
TileLevel::Lower => return Ok(()),
};
let [a, b] = [step((self.axis + 1) % 3), step((self.axis + 2) % 3)];
let edge = usize::try_from(finer.edge()).unwrap_or(usize::MAX);
for i in (0..level.edge()).step_by(edge) {
for j in (0..level.edge()).step_by(edge) {
self.check_region(s, finer, plus(at, plus(a.map(|c| c * i), b.map(|c| c * j))))?;
}
}
Ok(())
}
}
struct Owned<'a>(&'a Hermite);
impl Source for Owned<'_> {
fn leaves(&self) -> Result<Vec<Coord>, HermiteError> {
let t = &self.0.topology;
Ok(t.leaf_numbers().map(|n| t.leaf_origin(n)).collect::<Result<_, _>>()?)
}
fn tiles(&self) -> Result<Vec<(TileLevel, Coord, u32)>, HermiteError> {
let t = &self.0.topology;
let mut out = Vec::with_capacity(t.tile_count());
for n in t.tile_numbers() {
let (level, origin) = t.tile(n)?;
out.push((level, origin, n.get()));
}
Ok(out)
}
fn is_leaf(&self, v: Coord) -> Result<bool, HermiteError> {
Ok(matches!(self.0.topology.locate(v), Location::Leaf(_)))
}
fn holds_node(&self, level: TileLevel, v: Coord) -> Result<bool, HermiteError> {
let t = &self.0.topology;
Ok(match level {
TileLevel::Root => t.has_upper_node(v),
TileLevel::Upper => t.has_lower_node(v),
TileLevel::Lower => matches!(t.locate(v), Location::Leaf(_)),
})
}
fn sign_at(&self, v: Coord) -> Result<u8, HermiteError> {
Ok(self.0.signs.value_at(&self.0.topology, v)?)
}
fn sign_leaf(&self, n: u32) -> Result<&[u8; LEAF_VOXELS], HermiteError> {
Ok(self.0.signs.leaf(&self.0.topology, crate::topology::LeafNumber(n))?)
}
fn crossing(
&self,
axis: usize,
n: u32,
place: usize,
) -> Result<Option<Crossing>, HermiteError> {
let axis = Axis::ALL.get(axis).copied().ok_or(HermiteError::Missing("an axis"))?;
Ok(self.0.crossings.get(LeafNumber(n), axis, place))
}
fn sign_tile(&self, t: u32) -> Result<u8, HermiteError> {
Ok(self.0.signs.tile_value_at(t)?)
}
fn sign_background(&self) -> u8 {
self.0.signs.background()
}
}
impl Hermite {
pub fn new(
topology: Topology,
signs: Field<u8>,
crossings: Crossings,
group_names: Vec<String>,
) -> Result<Hermite, HermiteError> {
if signs.belongs_to(&topology).is_err() {
return Err(HermiteError::OtherTopology("sign"));
}
if !crossings.belongs_to(&topology) {
return Err(HermiteError::OtherTopology(CROSSINGS));
}
let group_names = if group_names == [""] { Vec::new() } else { group_names };
let set = Hermite { topology, signs, crossings, group_names };
check(&Owned(&set), &set.group_names)?;
Ok(set)
}
pub fn topology(&self) -> &Topology {
&self.topology
}
pub fn signs(&self) -> &Field<u8> {
&self.signs
}
pub fn crossings(&self) -> &Crossings {
&self.crossings
}
pub fn group_names(&self) -> &[String] {
&self.group_names
}
fn keyed(meta: &GridMeta, set: &str, field: &str) -> Result<GridMeta, HermiteError> {
if meta.extra.contains_key(crate::meta::SDF_KEY) {
return Err(HermiteError::ClaimsSdf);
}
if let Some(key) = meta.extra.keys().find(|k| crate::cache::reserved(k)) {
return Err(HermiteError::ReservedKey(key.clone()));
}
let mut m = meta.clone();
m.extra.insert(KEY_VERSION.to_owned(), VERSION.to_owned());
m.extra.insert(KEY_SET.to_owned(), set.to_owned());
m.extra.insert(KEY_FIELD.to_owned(), field.to_owned());
Ok(m)
}
fn sign_entry(&self, set: &str, meta: &GridMeta) -> Result<CacheEntry, HermiteError> {
let mut sign_meta = Self::keyed(meta, set, "sign")?;
for (id, name) in self.group_names.iter().enumerate().skip(1) {
sign_meta.extra.insert(format!("{KEY_GROUP}{id}"), name.clone());
}
Ok(CacheEntry::with_reserved_keys(&sign_meta, &self.topology, &self.signs)?)
}
pub fn entries(&self, set: &str, meta: &GridMeta) -> Result<Vec<CacheEntry>, HermiteError> {
let sign = self.sign_entry(set, meta)?;
let mut extra = BTreeMap::new();
extra.insert(KEY_VERSION.to_owned(), VERSION.to_owned());
extra.insert(KEY_SET.to_owned(), set.to_owned());
extra.insert(KEY_FIELD.to_owned(), CROSSINGS.to_owned());
let bytes =
CacheEntry::bytes_with_reserved_keys(&meta.name, extra, self.crossings.to_bytes());
Ok(vec![sign, bytes])
}
}
struct Viewed<'a, 'b> {
set: &'b HermiteView<'a>,
regions: crate::view::Regions,
}
impl Source for Viewed<'_, '_> {
fn leaves(&self) -> Result<Vec<Coord>, HermiteError> {
Ok(self.regions.leaves.clone())
}
fn tiles(&self) -> Result<Vec<(TileLevel, Coord, u32)>, HermiteError> {
Ok(self.regions.tiles.clone())
}
fn is_leaf(&self, v: Coord) -> Result<bool, HermiteError> {
Ok(matches!(self.set.signs.locate(v)?, ViewLocation::Leaf(_)))
}
fn holds_node(&self, level: TileLevel, v: Coord) -> Result<bool, HermiteError> {
Ok(self.set.signs.holds_node(level, v)?)
}
fn sign_at(&self, v: Coord) -> Result<u8, HermiteError> {
Ok(self.set.signs.get(v)?)
}
fn sign_leaf(&self, n: u32) -> Result<&[u8; LEAF_VOXELS], HermiteError> {
Ok(self.set.signs.leaf(n)?)
}
fn crossing(
&self,
axis: usize,
n: u32,
place: usize,
) -> Result<Option<Crossing>, HermiteError> {
let c = &self.set.crossings;
sparse_crossing(c.masks, c.items, &c.starts, n, axis, place)
}
fn sign_tile(&self, t: u32) -> Result<u8, HermiteError> {
Ok(self.set.signs.tile_value(t)?)
}
fn sign_background(&self) -> u8 {
self.set.signs.background()
}
}
fn leaf_masks(masks: &[u8], n: u32) -> Option<[[u64; 8]; 3]> {
let at = usize::try_from(n).ok()?.checked_mul(MASK_BYTES)?;
let bytes = masks.get(at..at.checked_add(MASK_BYTES)?)?;
let mut out = [[0_u64; 8]; 3];
for (w, b) in out.iter_mut().flatten().zip(bytes.as_chunks::<8>().0) {
*w = u64::from_le_bytes(*b);
}
Some(out)
}
fn sparse_crossing(
masks: &[u8],
items: &[u8],
starts: &[u32],
n: u32,
axis: usize,
place: usize,
) -> Result<Option<Crossing>, HermiteError> {
let lost = || HermiteError::OtherTopology(CROSSINGS);
let m = leaf_masks(masks, n).ok_or_else(lost)?;
let (before, set) = rank(&m, axis, place).ok_or_else(lost)?;
if !set {
return Ok(None);
}
let k = starts.get(usize::try_from(n).map_err(|_| lost())?).ok_or_else(lost)?;
let at = usize::try_from(k.checked_add(before).ok_or_else(lost)?).map_err(|_| lost())?;
let at = at.checked_mul(CROSSING_BYTES).ok_or_else(lost)?;
let b = items.get(at..at.checked_add(CROSSING_BYTES).ok_or_else(lost)?).ok_or_else(lost)?;
let mut words = b.as_chunks::<4>().0.iter().copied();
let mut next = || words.next().ok_or_else(lost);
let fraction = f32::from_le_bytes(next()?);
let normal =
[f32::from_le_bytes(next()?), f32::from_le_bytes(next()?), f32::from_le_bytes(next()?)];
Ok(Some(Crossing { fraction, normal, group: u32::from_le_bytes(next()?) }))
}
fn sparse(bytes: &[u8], leaves: usize) -> Result<ViewCrossings<'_>, HermiteError> {
let lost = || HermiteError::OtherTopology(CROSSINGS);
let (masks, items) = bytes
.split_at_checked(leaves.checked_mul(MASK_BYTES).ok_or_else(lost)?)
.ok_or_else(lost)?;
let mut starts = Vec::with_capacity(leaves.saturating_add(1));
let mut total = 0_u32;
starts.push(total);
for leaf in masks.as_chunks::<MASK_BYTES>().0 {
let ones: u32 =
leaf.as_chunks::<8>().0.iter().map(|w| u64::from_le_bytes(*w).count_ones()).sum();
total = total.checked_add(ones).ok_or_else(lost)?;
starts.push(total);
}
let expected = usize::try_from(total).ok().and_then(|n| n.checked_mul(CROSSING_BYTES));
if expected != Some(items.len()) {
return Err(lost());
}
Ok(ViewCrossings { masks, items, starts })
}
fn group_table(meta: &GridMeta) -> Result<Vec<String>, HermiteError> {
let mut by_id = BTreeMap::new();
for (key, name) in &meta.extra {
let Some(id) = key.strip_prefix(KEY_GROUP) else { continue };
let n: u32 = id.parse().map_err(|_| HermiteError::BadGroupKey(key.clone()))?;
if n == 0 || n.to_string() != id {
return Err(HermiteError::BadGroupKey(key.clone()));
}
by_id.insert(n, name.clone());
}
let mut names = vec![String::new()];
for (expected, (id, name)) in (1_u32..).zip(by_id) {
if id != expected {
return Err(HermiteError::BadGroupName(expected));
}
names.push(name);
}
if names.len() == 1 {
names.clear();
}
Ok(names)
}
fn extras(file: &CacheFile) -> Vec<&BTreeMap<String, String>> {
let grids = file.grids().into_iter().map(|g| &g.meta().extra);
grids.chain(file.bytes_entries().into_iter().map(|b| b.extra)).collect()
}
fn check_version(file: &CacheFile, set: &str) -> Result<(), HermiteError> {
for extra in extras(file) {
if extra.get(KEY_SET).map(String::as_str) == Some(set) {
let v = extra.get(KEY_VERSION).map(String::as_str).unwrap_or_default();
if v != VERSION {
return Err(HermiteError::UnsupportedVersion(v.to_owned()));
}
} else if extra.get(OLD_SET).map(String::as_str) == Some(set) {
let v = extra.get(OLD_VERSION).cloned().unwrap_or_default();
return Err(HermiteError::UnsupportedVersion(v));
}
}
Ok(())
}
impl<'a> HermiteView<'a> {
pub fn find(file: &'a CacheFile, set: &str) -> Result<HermiteView<'a>, HermiteError> {
check_version(file, set)?;
let mut sign = None;
for g in file.grids() {
let extra = &g.meta().extra;
if extra.get(KEY_SET).map(String::as_str) != Some(set) {
continue;
}
let field = extra.get(KEY_FIELD).cloned().unwrap_or_default();
if field != "sign" {
return Err(HermiteError::UnknownField(field));
}
if sign.replace(g).is_some() {
return Err(HermiteError::Twice("sign"));
}
}
let sign = sign.ok_or(HermiteError::Missing("sign"))?;
let mut crossings = None;
for b in file.bytes_entries() {
if b.extra.get(KEY_SET).map(String::as_str) != Some(set) {
continue;
}
let field = b.extra.get(KEY_FIELD).cloned().unwrap_or_default();
if field != CROSSINGS {
return Err(HermiteError::UnknownField(field));
}
let ours = [KEY_VERSION, KEY_SET, KEY_FIELD];
if let Some(k) =
b.extra.keys().find(|k| k.starts_with(PREFIX) && !ours.contains(&k.as_str()))
{
return Err(HermiteError::ReservedKey(k.clone()));
}
if crossings.replace(b.bytes).is_some() {
return Err(HermiteError::Twice(CROSSINGS));
}
}
let crossings = crossings.ok_or(HermiteError::Missing(CROSSINGS))?;
let signs: GridView<'a, u8> = sign.view()?;
let view = HermiteView {
crossings: sparse(crossings, signs.leaf_count())?,
signs,
group_names: group_table(sign.meta())?,
};
let regions = view.signs.regions()?;
check(&Viewed { set: &view, regions }, &view.group_names)?;
Ok(view)
}
pub fn signs(&self) -> &GridView<'a, u8> {
&self.signs
}
pub fn crossing(
&self,
leaf: u32,
axis: Axis,
place: usize,
) -> Result<Option<Crossing>, HermiteError> {
let c = &self.crossings;
sparse_crossing(c.masks, c.items, &c.starts, leaf, axis.index(), place)
}
pub fn group_names(&self) -> &[String] {
&self.group_names
}
}
impl From<FormatError> for HermiteError {
fn from(e: FormatError) -> Self {
HermiteError::Format(e)
}
}