use crate::Coord;
use alloc::borrow::Cow;
use core::ops::Range;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct MultiPoint<'a, T: Coord> {
coords: Cow<'a, [T]>,
}
pub type MultiPoint2<'a, C = f64> = MultiPoint<'a, [C; 2]>;
pub type MultiPoint3<'a, C = f64> = MultiPoint<'a, [C; 3]>;
impl<'a, T: Coord> MultiPoint<'a, T> {
pub fn new() -> Self {
Self {
coords: Cow::Borrowed(&[]),
}
}
pub fn from_raw(coords: Cow<'a, [T]>) -> Self {
Self { coords }
}
pub fn raw_coords(&self) -> &[T] {
self.as_ref()
}
pub fn iter(&self) -> Iter<T> {
Iter {
slice: &self.coords,
pos: 0,
end: self.coords.len(),
}
}
pub fn iter_range(&self, range: Range<usize>) -> Iter<T> {
Iter {
slice: &self.coords,
pos: range.start,
end: range.end,
}
}
pub fn get(&self, index: usize) -> Option<T> {
self.coords.get(index).cloned()
}
pub fn len(&self) -> usize {
self.coords.len()
}
pub fn is_empty(&self) -> bool {
self.coords.is_empty()
}
pub fn push(&mut self, coord: T) {
self.coords.to_mut().push(coord);
}
pub fn clear(&mut self) {
self.coords.to_mut().clear();
}
pub fn transform<T2: Coord>(&self, f: impl Fn(&T) -> T2) -> MultiPoint<T2> {
MultiPoint {
coords: self.coords.iter().map(f).collect(),
}
}
pub fn transform_inplace(&mut self, mut f: impl FnMut(&T) -> T) {
self.coords.to_mut().iter_mut().for_each(|c| {
*c = f(c);
});
}
}
impl<T: Coord> AsRef<[T]> for MultiPoint<'_, T> {
fn as_ref(&self) -> &[T] {
self.coords.as_ref()
}
}
impl<'a, T: Coord> IntoIterator for &'a MultiPoint<'_, T> {
type Item = T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct Iter<'a, T: Coord> {
slice: &'a [T],
pos: usize,
end: usize,
}
impl<'a, T: Coord> Iterator for Iter<'a, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let v = if self.pos < self.end {
Some(self.slice[self.pos].clone())
} else {
None
};
self.pos += 1;
v
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mpoints_basic() {
let mut mpoints = MultiPoint2::new();
assert!(mpoints.is_empty());
assert_eq!(mpoints.len(), 0);
mpoints.push([0.0, 0.0]);
assert!(!mpoints.is_empty());
assert_eq!(mpoints.len(), 1);
mpoints.push([1.0, 1.0]);
mpoints.push([2.0, 2.0]);
mpoints.push([3.0, 3.0]);
assert_eq!(mpoints.get(0), Some([0.0, 0.0]));
assert_eq!(mpoints.get(1), Some([1.0, 1.0]));
assert_eq!(mpoints.get(2), Some([2.0, 2.0]));
assert_eq!(mpoints.get(3), Some([3.0, 3.0]));
assert_eq!(mpoints.len(), 4);
assert_eq!(mpoints.iter().count(), 4);
assert_eq!((&mpoints).into_iter().count(), 4);
for (i, point) in mpoints.iter().enumerate() {
match i {
0 => assert_eq!(point, [0.0, 0.0]),
1 => assert_eq!(point, [1.0, 1.0]),
2 => assert_eq!(point, [2.0, 2.0]),
3 => assert_eq!(point, [3.0, 3.0]),
_ => unreachable!(),
}
}
for (i, point) in mpoints.iter_range(1..3).enumerate() {
match i {
0 => assert_eq!(point, [1.0, 1.0]),
1 => assert_eq!(point, [2.0, 2.0]),
_ => unreachable!(),
}
}
mpoints.clear();
assert!(mpoints.is_empty());
assert_eq!(mpoints.len(), 0);
assert_eq!(mpoints.iter().count(), 0);
}
#[test]
fn test_mpoints_from_raw() {
let mpoints = MultiPoint2::from_raw([[1.2, 2.1], [3.4, 4.3]][..].into());
assert_eq!(mpoints.as_ref(), [[1.2, 2.1], [3.4, 4.3]]);
assert_eq!(mpoints.raw_coords(), [[1.2, 2.1], [3.4, 4.3]]);
}
#[test]
fn test_transform() {
{
let mpoints =
MultiPoint2::from_raw([[0., 0.], [5., 0.], [5., 5.], [0., 5.]][..].into());
let new_mpoints = mpoints.transform(|[x, y]| [x + 2., y + 1.]);
assert_eq!(
new_mpoints.raw_coords(),
[[2., 1.], [7., 1.], [7., 6.], [2., 6.]]
);
}
{
let mut mpoints =
MultiPoint2::from_raw([[0., 0.], [5., 0.], [5., 5.], [0., 5.]][..].into());
mpoints.transform_inplace(|[x, y]| [x + 2., y + 1.]);
assert_eq!(
mpoints.raw_coords(),
[[2., 1.], [7., 1.], [7., 6.], [2., 6.]]
);
}
}
}