use crate::index::BruteForceIndex;
use crate::model::{Point3, RigidTransform};
use crate::multiaxis::sim::tridexel::TriDexel;
use crate::stock_sim::DexelGrid;
use crate::verify::{deviation_map, excess_samples, DeviationMap, DeviationOptions, ExcessSample};
use crate::{CamError, MeshSnapshot};
pub fn axis_rotation(axis: usize) -> RigidTransform {
let rotation = match axis {
0 => [[0.0, 1.0, 0.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0]],
1 => [[0.0, 0.0, 1.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
_ => [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
};
RigidTransform::new(rotation, Point3::ZERO).expect("a cyclic permutation is a proper rotation")
}
pub fn dexel_grid_of(td: &TriDexel, axis: usize) -> DexelGrid {
let g = &td.grids[axis.min(2)];
let o = axis_rotation(g.axis).apply_point(g.origin);
DexelGrid {
origin: o,
cell_x: g.cell[0],
cell_y: g.cell[1],
nx: g.n[0],
ny: g.n[1],
columns: g.columns.clone(),
z_range: g.range,
}
}
pub fn deviation_maps(td: &TriDexel, part: &MeshSnapshot, options: &DeviationOptions) -> Result<[DeviationMap; 3], CamError> {
if part.is_empty() {
return Err(CamError::InvalidInput("deviation: the part mesh is empty".into()));
}
let mut maps = Vec::with_capacity(3);
for axis in [2usize, 0, 1] {
let grid = dexel_grid_of(td, axis);
let rotated = part.transformed(&axis_rotation(axis));
let index = BruteForceIndex::build(&rotated);
maps.push(deviation_map(&grid, &rotated, &index, options));
}
let y = maps.pop().unwrap();
let x = maps.pop().unwrap();
let z = maps.pop().unwrap();
Ok([z, x, y])
}
fn flip_rotation() -> RigidTransform {
RigidTransform::new([[-1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, -1.0]], Point3::ZERO).expect("a half turn is a proper rotation")
}
fn flipped(grid: &DexelGrid) -> DexelGrid {
let mut columns = Vec::with_capacity(grid.columns.len());
for iy in 0..grid.ny {
for ix in (0..grid.nx).rev() {
let mut c = grid.column(ix, iy).clone();
c.intervals = c.intervals.iter().rev().map(|[a, b]| [-b, -a]).collect();
columns.push(c);
}
}
DexelGrid {
origin: Point3::new(-(grid.origin.x + grid.nx as f64 * grid.cell_x), grid.origin.y, -grid.z_range[1]),
cell_x: grid.cell_x,
cell_y: grid.cell_y,
nx: grid.nx,
ny: grid.ny,
columns,
z_range: [-grid.z_range[1], -grid.z_range[0]],
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct ViewSample {
pub view: &'static str,
pub sample: ExcessSample,
}
pub fn excess_samples_all_views(td: &TriDexel, part: &MeshSnapshot, options: &DeviationOptions) -> Result<Vec<ViewSample>, CamError> {
if part.is_empty() {
return Err(CamError::InvalidInput("deviation: the part mesh is empty".into()));
}
let mut out = Vec::new();
for (axis, views) in [(2usize, ["+Z", "-Z"]), (0, ["+X", "-X"]), (1, ["+Y", "-Y"])] {
let grid = dexel_grid_of(td, axis);
for (view, flip) in views.into_iter().zip([false, true]) {
let (grid, frame) = if flip {
(flipped(&grid), axis_rotation(axis).then(&flip_rotation()))
} else {
(grid.clone(), axis_rotation(axis))
};
let rotated = part.transformed(&frame);
let index = BruteForceIndex::build(&rotated);
let map = deviation_map(&grid, &rotated, &index, options);
let back = frame.inverse();
out.extend(excess_samples(&map, &rotated, &index, options).into_iter().map(|mut sample| {
sample.point = back.apply_point(sample.point);
sample.nearest = back.apply_point(sample.nearest);
ViewSample { view, sample }
}));
}
}
Ok(out)
}