use core::marker::PhantomData;
use crate::{Pos, Rect, int::Int};
#[allow(private_bounds)]
pub trait Layout: Sized + crate::internal::Sealed {
fn to_1d<T: Int>(pos: Pos<T>, width: usize) -> usize;
fn to_2d<T: Int>(index: usize, width: usize) -> Pos<T>;
fn iter_pos<T: Int>(bounds: Rect<T>) -> impl Iterator<Item = Pos<T>>;
fn as_any() -> AnyLayout;
}
pub struct IterPos<T: Int, L: Layout> {
bounds: Rect<T>,
current: Pos<T>,
layout: PhantomData<L>,
}
impl<T> Iterator for IterPos<T, RowMajor>
where
T: Int,
{
type Item = Pos<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.current.y >= self.bounds.bottom() {
return None;
}
let pos = self.current;
self.current.x += T::ONE;
if self.current.x >= self.bounds.right() {
self.current.x = self.bounds.left();
self.current.y += T::ONE;
}
Some(pos)
}
}
impl<T> Iterator for IterPos<T, ColMajor>
where
T: Int,
{
type Item = Pos<T>;
fn next(&mut self) -> Option<Self::Item> {
if self.current.x >= self.bounds.right() {
return None;
}
let pos = self.current;
self.current.y += T::ONE;
if self.current.y >= self.bounds.bottom() {
self.current.y = self.bounds.top();
self.current.x += T::ONE;
}
Some(pos)
}
}
pub enum RowMajor {}
impl crate::internal::Sealed for RowMajor {}
impl Layout for RowMajor {
fn to_1d<T: Int>(pos: Pos<T>, width: usize) -> usize {
pos.y.to_usize() * width + pos.x.to_usize()
}
fn to_2d<T: Int>(index: usize, width: usize) -> Pos<T> {
Pos {
x: T::from_usize(index % width),
y: T::from_usize(index / width),
}
}
fn iter_pos<T: Int>(bounds: Rect<T>) -> impl Iterator<Item = Pos<T>> {
IterPos {
bounds,
current: Pos::new(bounds.left(), bounds.top()),
layout: PhantomData::<Self>,
}
}
fn as_any() -> AnyLayout {
AnyLayout::RowMajor
}
}
pub enum ColMajor {}
impl crate::internal::Sealed for ColMajor {}
impl Layout for ColMajor {
fn to_1d<T: Int>(pos: Pos<T>, width: usize) -> usize {
pos.x.to_usize() * width + pos.y.to_usize()
}
fn to_2d<T: Int>(index: usize, width: usize) -> Pos<T> {
Pos {
x: T::from_usize(index / width),
y: T::from_usize(index % width),
}
}
fn iter_pos<T: Int>(bounds: Rect<T>) -> impl Iterator<Item = Pos<T>> {
IterPos {
bounds,
current: Pos::new(bounds.left(), bounds.top()),
layout: PhantomData::<Self>,
}
}
fn as_any() -> AnyLayout {
AnyLayout::ColMajor
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AnyLayout {
RowMajor,
ColMajor,
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use crate::pos;
use alloc::vec::Vec;
#[test]
fn to_row_major() {
let pos = Pos::new(2, 3);
let width = 5;
let index = RowMajor::to_1d(pos, width);
assert_eq!(index, 17); }
#[test]
fn to_col_major() {
let pos = Pos::new(2, 3);
let width = 5;
let index = ColMajor::to_1d(pos, width);
assert_eq!(index, 13); }
#[test]
fn from_row_major() {
let index = 17;
let width = 5;
let pos: Pos<i32> = RowMajor::to_2d(index, width);
assert_eq!(pos.x, 2); assert_eq!(pos.y, 3); }
#[test]
fn from_col_major() {
let index = 13;
let width = 5;
let pos: Pos<i32> = ColMajor::to_2d(index, width);
assert_eq!(pos.x, 2); assert_eq!(pos.y, 3); }
#[test]
fn iter_row_major() {
let bounds = Rect::from_ltrb(0, 0, 3, 2).unwrap();
let positions: Vec<_> = RowMajor::iter_pos(bounds).collect();
assert_eq!(
positions,
&[
pos!(0, 0),
pos!(1, 0),
pos!(2, 0),
pos!(0, 1),
pos!(1, 1),
pos!(2, 1)
]
);
}
#[test]
fn iter_col_major() {
let bounds = Rect::from_ltrb(0, 0, 3, 2).unwrap();
let positions: Vec<_> = ColMajor::iter_pos(bounds).collect();
assert_eq!(
positions,
&[
pos!(0, 0),
pos!(0, 1),
pos!(1, 0),
pos!(1, 1),
pos!(2, 0),
pos!(2, 1)
]
);
}
#[test]
fn any_layout() {
assert_eq!(RowMajor::as_any(), AnyLayout::RowMajor);
assert_eq!(ColMajor::as_any(), AnyLayout::ColMajor);
}
}