use axiolid_core::Tolerance;
use crate::{FieldConfig, LayeredCell, LayeredFieldError};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct FieldEvidence {
pub cells_sampled: usize,
pub surface_hits: usize,
pub occupancy_spans: usize,
pub empty_cells: usize,
pub multi_layer_cells: usize,
pub parallel_triangles_skipped: usize,
pub out_of_bounds_hits: usize,
pub boundary_contacts: usize,
pub coincident_hits_merged: usize,
pub degenerate_triangles: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LayeredField {
width: usize,
height: usize,
cells: Vec<LayeredCell>,
evidence: FieldEvidence,
}
impl LayeredField {
pub fn with_config(config: &FieldConfig) -> Result<Self, LayeredFieldError> {
let (width, height) = config.dimensions();
Self::empty(width, height)
}
pub fn empty(width: usize, height: usize) -> Result<Self, LayeredFieldError> {
let cell_count = width
.checked_mul(height)
.ok_or(LayeredFieldError::InvalidDimensions)?;
if cell_count == 0 {
return Err(LayeredFieldError::InvalidDimensions);
}
Ok(Self {
width,
height,
cells: vec![LayeredCell::empty(); cell_count],
evidence: FieldEvidence::default(),
})
}
pub const fn dimensions(&self) -> (usize, usize) {
(self.width, self.height)
}
pub fn cell_count(&self) -> usize {
self.cells.len()
}
pub fn linear_index(&self, x: usize, y: usize) -> Option<usize> {
(x < self.width && y < self.height).then_some(y * self.width + x)
}
pub fn cell(&self, x: usize, y: usize) -> Option<&LayeredCell> {
self.linear_index(x, y).map(|index| &self.cells[index])
}
pub fn cells(&self) -> &[LayeredCell] {
&self.cells
}
pub const fn evidence(&self) -> FieldEvidence {
self.evidence
}
pub fn from_cells(
width: usize,
height: usize,
cells: Vec<LayeredCell>,
evidence: FieldEvidence,
) -> Result<Self, LayeredFieldError> {
let expected = width
.checked_mul(height)
.ok_or(LayeredFieldError::InvalidDimensions)?;
if expected == 0 || cells.len() != expected {
return Err(LayeredFieldError::DimensionMismatch);
}
Ok(Self {
width,
height,
cells,
evidence,
})
}
pub fn derive_occupancy(&self, tolerance: Tolerance) -> Result<Self, LayeredFieldError> {
let mut derived = self.clone();
let mut evidence = self.evidence;
evidence.occupancy_spans = 0;
for (index, cell) in self.cells.iter().enumerate() {
let updated = cell.derive_occupancy(tolerance)?;
evidence.occupancy_spans += updated.occupancy().len();
derived.cells[index] = updated;
}
derived.evidence = evidence;
Ok(derived)
}
}