use mint::Vector2;
use crate::{
GenericArray2d,
boundary::{Boundary, IntoBoundary},
storage::{Array2dStorage, Array2dStorageOwned},
util::*,
};
impl<T: Array2dStorageOwned<Item: Default>> GenericArray2d<T> {
fn downsize(&mut self, boundary: Boundary) {
let mut base1 = 0;
let mut base2 = offset_of(boundary.min, self.boundary.min, self.pitch);
let slice = self.data.slice_mut();
for _ in 0..boundary.dimension.y {
move_within(slice, base2, base1, boundary.pitch());
base1 += boundary.pitch();
base2 += self.pitch;
}
let len = boundary.len();
slice[len..].fill_with(Default::default)
}
fn upsize(&mut self, from: Boundary, to: Boundary) {
let h = from.dimension.y as usize;
let mut base1 = from.pitch() * h;
let mut base2 = offset_of(from.min, to.min, to.pitch()) + to.pitch() * h;
let slice = self.data.slice_mut();
for _ in 0..h {
base1 -= from.pitch();
base2 -= to.pitch();
move_within(slice, base1, base2, from.pitch());
}
}
pub fn resize(&mut self, boundary: impl IntoBoundary) {
let boundary = boundary.into_boundary();
if self.is_empty() {
self.boundary = boundary;
self.pitch = boundary.pitch();
self.data.vec_mut().clear();
self.data
.vec_mut()
.extend((0..boundary.len()).map(|_| Default::default()));
return;
}
if self.boundary == boundary && self.pitch == boundary.pitch() {
return;
}
let size = boundary.len();
if size > self.data.slice().len() {
self.data.vec_mut().resize_with(size, Default::default);
}
let Some(intersection) = self.boundary.intersection(boundary) else {
self.data.vec_mut().fill_with(Default::default);
self.boundary = boundary;
self.pitch = boundary.pitch();
return;
};
if intersection != self.boundary || intersection.pitch() != self.pitch {
self.downsize(intersection);
}
if intersection != boundary {
self.upsize(intersection, boundary);
}
self.boundary = boundary;
self.pitch = boundary.pitch();
}
pub fn insert(&mut self, position: impl Into<Vector2<i32>>, value: T::Item) {
let position = position.into();
if self.is_empty() {
self.boundary = Boundary::from_point(position);
self.pitch = 1;
self.data = T::from_vec(vec![value]);
} else if let Some(v) = self.get_mut(position) {
*v = value;
} else {
let min = vec_min(self.boundary.min, position);
let max = vec_max(self.max_point(), position);
self.resize(Boundary::min_max(min, max));
if let Some(v) = self.get_mut(position) {
*v = value;
}
}
}
pub fn resize_containing(&mut self, boundary: Boundary) {
if self.is_empty() {
self.resize(boundary);
} else {
let boundary = boundary.into_boundary();
let min = vec_min(self.boundary.min, boundary.min);
let max = vec_max(
self.boundary.max_non_inclusive(),
boundary.max_non_inclusive(),
);
let dimension = i2u(sub(max, min));
self.resize(Boundary { min, dimension });
}
}
pub fn try_extend<U: Into<Vector2<i32>>>(
&mut self,
positions: impl IntoIterator<Item = (U, T::Item)>,
) -> usize {
let mut discards = 0;
for (position, item) in positions {
if let Some(v) = self.get_mut(position) {
*v = item;
} else {
discards += 1;
}
}
discards
}
pub fn extend<U: Into<Vector2<i32>>>(
&mut self,
positions: impl IntoIterator<Item = (U, T::Item)> + Clone,
) {
let boundary = Boundary::from_iter(positions.clone().into_iter().map(|(x, _)| x));
self.resize_containing(boundary);
self.try_extend(positions);
}
pub fn merge<U: Array2dStorage<Item = T::Item>>(&mut self, array: &GenericArray2d<U>)
where
T::Item: Clone,
{
if self.is_empty() {
self.resize(array.boundary);
} else {
let min = vec_min(self.boundary.min, array.boundary.min);
let max = vec_max(
self.boundary.max_non_inclusive(),
array.boundary.max_non_inclusive(),
);
let dimension = i2u(sub(max, min));
self.resize(Boundary { min, dimension });
}
self.paint(array, [0, 0], |source, incoming| *source = incoming.clone());
}
pub fn expand(&mut self, by: impl Into<Vector2<i32>>) {
let target = self.boundary.expand_by(by);
self.resize(target);
}
pub fn crop(&mut self)
where
T::Item: PartialEq,
{
let area = self.cropped_boundary();
self.resize(area);
}
pub fn crop_expand(&mut self, expand: impl Into<Vector2<i32>>)
where
T::Item: PartialEq,
{
let area = self.cropped_boundary();
let result_area = area.expand_by(expand);
self.resize(result_area);
}
pub fn crop_with(&mut self, is_empty: impl FnMut(&T::Item) -> bool) {
let area = self.cropped_boundary_with(is_empty);
self.resize(area);
}
pub fn crop_expand_with(
&mut self,
expand: impl Into<Vector2<i32>>,
is_empty: impl FnMut(&T::Item) -> bool,
) {
let area = self.cropped_boundary_with(is_empty);
let result_area = area.expand_by(expand);
self.resize(result_area);
}
}
impl<T: Array2dStorage<Item: Default>> GenericArray2d<T> {
pub fn cropped_boundary(&self) -> Boundary
where
T::Item: PartialEq,
{
let default = T::Item::default();
self.cropped_boundary_with(|item| item == &default)
}
pub fn cropped_boundary_with(&self, mut is_empty: impl FnMut(&T::Item) -> bool) -> Boundary {
let mut min = Vector2 {
x: i32::MAX,
y: i32::MAX,
};
let mut max = Vector2 {
x: i32::MIN,
y: i32::MIN,
};
for (idx, value) in self.iter::<Vector2<i32>>() {
if !is_empty(value) {
min = vec_min(idx, min);
max = vec_max(idx, max);
}
}
if min.x <= max.x {
Boundary {
min,
dimension: i2u(add(sub(max, min), ONE)),
}
} else {
Boundary {
min: ZERO,
dimension: i2u(ZERO),
}
}
}
}