use nalgebra::{Point2, Vector2};
use ndarray::{s, Array2, ArrayView2, ArrayViewMut2};
use serde::Serialize;
use crate::{
cell_map::Bounds, extensions::Point2Ext, map_metadata::CellMapMetadata, CellMap, Error, Layer,
};
pub trait Slicer<'a, L, T>
where
L: Layer,
{
type Output;
type OutputMut;
fn slice(&self, data: &'a Array2<T>) -> Option<Self::Output>;
fn slice_mut(&self, data: &'a mut Array2<T>) -> Option<Self::OutputMut>;
fn advance(&mut self);
fn index(&self) -> Option<Point2<usize>>;
fn reset(&mut self, layer: Option<L>);
}
pub(crate) type RectBounds = Vector2<(usize, usize)>;
#[derive(Debug, Clone, Copy)]
pub struct Cells {
bounds: RectBounds,
index: Point2<usize>,
}
#[derive(Debug, Clone, Copy)]
pub struct Windows {
bounds: RectBounds,
index: Point2<usize>,
semi_width: Vector2<usize>,
}
#[derive(Debug, Clone)]
#[allow(missing_copy_implementations)]
pub struct Line {
bounds: Bounds,
map_meta: CellMapMetadata,
start_parent: Point2<f64>,
end_parent: Point2<f64>,
dir: Vector2<f64>,
dir_sign: Vector2<f64>,
start_map: Point2<f64>,
end_map: Point2<f64>,
current_map: Option<Point2<f64>>,
end_index: Point2<usize>,
#[cfg(feature = "debug_iters")]
step_report_file: std::sync::Arc<std::fs::File>,
}
#[derive(Debug, Clone, Copy, Serialize)]
struct LineStepData {
start_parent: Point2<f64>,
end_parent: Point2<f64>,
dir: Vector2<f64>,
dir_sign: Vector2<f64>,
start_map: Point2<f64>,
end_map: Point2<f64>,
current_map: Option<Point2<f64>>,
end_index: Point2<usize>,
delta: Vector2<f64>,
}
impl Cells {
pub(crate) fn from_map<L: Layer, T>(map: &CellMap<L, T>) -> Self {
let cells = map.num_cells();
Self {
bounds: Vector2::new((0, cells.x), (0, cells.y)),
index: Point2::new(0, 0),
}
}
}
impl<'a, L, T> Slicer<'a, L, T> for Cells
where
L: Layer,
T: 'a,
{
type Output = &'a T;
type OutputMut = &'a mut T;
fn slice(&self, data: &'a Array2<T>) -> Option<Self::Output> {
data.get(self.index.as_array2_index())
}
fn slice_mut(&self, data: &'a mut Array2<T>) -> Option<Self::OutputMut> {
data.get_mut(self.index.as_array2_index())
}
fn advance(&mut self) {
self.index.x += 1;
if !self.index.in_bounds(&self.bounds) {
self.index.y += 1;
self.index.x = self.bounds.x.0;
}
}
fn index(&self) -> Option<Point2<usize>> {
if self.index.in_bounds(&self.bounds) {
Some(self.index)
} else {
None
}
}
fn reset(&mut self, _layer: Option<L>) {
self.index = Point2::new(self.bounds.x.0, self.bounds.y.0);
}
}
impl Windows {
pub(crate) fn from_map<L: Layer, T>(
map: &CellMap<L, T>,
semi_width: Vector2<usize>,
) -> Result<Self, Error> {
let cells = map.num_cells();
if semi_width.x * 2 + 1 > cells.x || semi_width.y * 2 + 1 > cells.y {
Err(Error::WindowLargerThanMap(
semi_width * 2 + Vector2::new(1, 1),
cells,
))
} else {
let bounds = Vector2::new(
(semi_width.x, cells.x - semi_width.x),
(semi_width.y, cells.y - semi_width.y),
);
Ok(Self {
bounds,
index: Point2::new(bounds.x.0, bounds.y.0),
semi_width,
})
}
}
}
impl<'a, L, T> Slicer<'a, L, T> for Windows
where
L: Layer,
T: 'a,
{
type Output = ArrayView2<'a, T>;
type OutputMut = ArrayViewMut2<'a, T>;
fn slice(&self, data: &'a Array2<T>) -> Option<Self::Output> {
if self.index.in_bounds(&self.bounds) {
let x0 = self.index.x - self.semi_width.x;
let x1 = self.index.x + self.semi_width.x + 1;
let y0 = self.index.y - self.semi_width.y;
let y1 = self.index.y + self.semi_width.y + 1;
Some(data.slice(s![y0..y1, x0..x1]))
} else {
None
}
}
fn slice_mut(&self, data: &'a mut Array2<T>) -> Option<Self::OutputMut> {
if self.index.in_bounds(&self.bounds) {
let x0 = self.index.x - self.semi_width.x;
let x1 = self.index.x + self.semi_width.x + 1;
let y0 = self.index.y - self.semi_width.y;
let y1 = self.index.y + self.semi_width.y + 1;
Some(data.slice_mut(s![y0..y1, x0..x1]))
} else {
None
}
}
fn advance(&mut self) {
self.index.x += 1;
if !self.index.in_bounds(&self.bounds) {
self.index.y += 1;
self.index.x = self.bounds.x.0;
}
}
fn index(&self) -> Option<Point2<usize>> {
if self.index.in_bounds(&self.bounds) {
Some(self.index)
} else {
None
}
}
fn reset(&mut self, _layer: Option<L>) {
self.index = Point2::new(self.bounds.x.0, self.bounds.y.0);
}
}
impl Line {
pub(crate) fn from_map<L: Layer, T>(
map_meta: CellMapMetadata,
start_parent: Point2<f64>,
end_parent: Point2<f64>,
) -> Result<Self, Error> {
let start_map = map_meta.to_parent.inverse_transform_point(&start_parent);
let end_map = map_meta.to_parent.inverse_transform_point(&end_parent);
let map_x_bounds = (
map_meta.cell_bounds.x.0 as f64,
map_meta.cell_bounds.x.1 as f64,
);
let map_y_bounds = (
map_meta.cell_bounds.y.0 as f64,
map_meta.cell_bounds.y.1 as f64,
);
if start_map.x < map_x_bounds.0
|| start_map.x > map_x_bounds.1
|| start_map.y < map_y_bounds.0
|| start_map.y > map_y_bounds.1
{
return Err(Error::PositionOutsideMap(
"Line::Start".into(),
start_parent,
));
}
if end_map.x < map_x_bounds.0
|| end_map.x > map_x_bounds.1
|| end_map.y < map_y_bounds.0
|| end_map.y > map_y_bounds.1
{
return Err(Error::PositionOutsideMap("Line::End".into(), start_parent));
}
let dir = end_map - start_map;
let dir_sign = dir.map(|v| if v < 0.0 { 0.0 } else { 1.0 });
let end_cell = map_meta
.index(end_parent)
.ok_or_else(|| Error::PositionOutsideMap("Line::End".into(), end_parent))?;
Ok(Self {
bounds: map_meta.cell_bounds,
map_meta,
start_parent,
end_parent,
dir,
dir_sign,
start_map,
end_map,
current_map: Some(start_map),
end_index: end_cell,
#[cfg(feature = "debug_iters")]
step_report_file: std::sync::Arc::new(
std::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open("line_step_report.json")
.unwrap(),
),
})
}
fn get_current_index(&self) -> Option<Point2<usize>> {
let current_map_isize = self.current_map?.map(|e| e as isize);
self.map_meta.cell_bounds.get_index(current_map_isize)
}
}
impl<'a, L, T> Slicer<'a, L, T> for Line
where
L: Layer,
T: 'a,
{
type Output = &'a T;
type OutputMut = &'a mut T;
fn slice(&self, data: &'a Array2<T>) -> Option<Self::Output> {
let index = self.get_current_index()?;
data.get(index.as_array2_index())
}
fn slice_mut(&self, data: &'a mut Array2<T>) -> Option<Self::OutputMut> {
let index = self.get_current_index()?;
data.get_mut(index.as_array2_index())
}
fn advance(&mut self) {
let curr_index = match self.get_current_index() {
Some(i) => i,
None => return,
};
let param = (self.current_map.unwrap() - self.start_map).norm()
/ (self.end_map - self.start_map).norm();
let delta_param: Vector2<f64> = ((curr_index.cast() + self.dir_sign)
- self.current_map.unwrap())
.component_div(&self.dir)
+ Vector2::from_element(self.map_meta.cell_boundary_precision);
let delta = delta_param.x.min(delta_param.y);
if delta + param > 1.0 {
self.current_map = None;
}
else {
self.current_map = Some(self.current_map.unwrap() + (self.dir * delta));
}
#[cfg(feature = "debug_iters")]
{
use std::io::Write;
let rpt = LineStepData {
start_parent: self.start_parent,
end_parent: self.end_parent,
dir: self.dir,
dir_sign: self.dir_sign,
start_map: self.start_map,
end_map: self.end_map,
current_map: self.current_map,
end_index: self.end_index,
delta: delta_param,
};
let val = serde_json::to_string_pretty(&rpt).unwrap();
writeln!(
std::sync::Arc::<std::fs::File>::get_mut(&mut self.step_report_file).unwrap(),
"{},",
val
)
.unwrap();
}
}
fn index(&self) -> Option<Point2<usize>> {
self.get_current_index()
}
fn reset(&mut self, _layer: Option<L>) {
self.current_map = Some(self.start_map)
}
}