use nalgebra::{Affine2, Isometry2, Matrix3, Point2, Vector2};
use serde::{Deserialize, Serialize};
use crate::{cell_map::Bounds, CellMapParams};
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub(crate) struct CellMapMetadata {
pub cell_size: Vector2<f64>,
pub cell_bounds: Bounds,
pub num_cells: Vector2<usize>,
pub cell_boundary_precision: f64,
pub to_parent: Affine2<f64>,
}
impl CellMapMetadata {
pub fn is_in_map(&self, index: Point2<usize>) -> bool {
index.x < self.num_cells.x && index.y < self.num_cells.y
}
pub fn position(&self, index: Point2<usize>) -> Option<Point2<f64>> {
if self.is_in_map(index) {
Some(self.position_unchecked(index))
} else {
None
}
}
pub fn position_unchecked(&self, index: Point2<usize>) -> Point2<f64> {
let index_centre = index.cast()
+ Vector2::new(
self.cell_bounds.x.0 as f64 + 0.5,
self.cell_bounds.y.0 as f64 + 0.5,
);
self.to_parent.transform_point(&index_centre)
}
pub fn index(&self, position: Point2<f64>) -> Option<Point2<usize>> {
let index = unsafe { self.index_unchecked(position) };
if index.x < 0 || index.y < 0 {
return None;
}
let index = index.map(|v| v as usize);
if self.is_in_map(index) {
Some(index)
} else {
None
}
}
pub unsafe fn index_unchecked(&self, position: Point2<f64>) -> Point2<isize> {
let cell = self.get_cell(position);
self.cell_bounds.get_index_unchecked(cell)
}
pub fn get_cell(&self, position: Point2<f64>) -> Point2<isize> {
let els: Vec<isize> = self
.to_parent
.inverse_transform_point(&position)
.iter()
.zip(self.cell_size.iter())
.map(|(&v, &s)| {
let v_floor = v as isize;
let v_next_floor = (s * self.cell_boundary_precision + v) as isize;
if v_floor != v_next_floor {
v_next_floor
} else {
v_floor
}
})
.collect();
Point2::new(els[0], els[1])
}
pub(crate) fn calc_to_parent(
position: Vector2<f64>,
rotation_rad: f64,
cell_size: Vector2<f64>,
) -> Affine2<f64> {
let isom_from_parent = Isometry2::new(position, rotation_rad);
let scale = Matrix3::new(cell_size.x, 0.0, 0.0, 0.0, cell_size.y, 0.0, 0.0, 0.0, 1.0);
Affine2::from_matrix_unchecked(isom_from_parent.to_matrix() * scale)
}
}
impl From<CellMapParams> for CellMapMetadata {
fn from(params: CellMapParams) -> Self {
let to_parent = Self::calc_to_parent(
params.position_in_parent,
params.rotation_in_parent_rad,
params.cell_size,
);
Self {
cell_size: params.cell_size,
cell_bounds: params.cell_bounds,
num_cells: params.cell_bounds.get_num_cells(),
cell_boundary_precision: params.cell_boundary_precision,
to_parent,
}
}
}