use core::{marker::PhantomData, ops::Range};
use crate::{
Pos, Rect, Size,
int::Int,
layout::{Linear, RowMajor, Traversal},
};
pub struct Block<const W: usize, const H: usize, G = RowMajor, C = G> {
grid: PhantomData<G>,
cell: PhantomData<C>,
}
impl<const W: usize, const H: usize, G: Traversal, C: Traversal> Traversal for Block<W, H, G, C> {
fn iter_pos<T: Int>(rect: Rect<T>) -> impl Iterator<Item = Pos<T>> {
G::iter_rect(
rect,
Size {
width: W,
height: H,
},
)
.flat_map(move |block_rect| C::iter_pos(block_rect))
}
fn iter_rect<T: Int>(rect: Rect<T>, size: Size) -> impl Iterator<Item = Rect<T>> {
G::iter_rect(
rect,
Size {
width: W,
height: H,
},
)
.flat_map(move |block_rect| C::iter_rect(block_rect, size))
}
}
impl<const W: usize, const H: usize, G: Linear, C: Linear> Linear for Block<W, H, G, C> {
fn pos_to_index(pos: Pos<usize>, width: usize) -> usize {
let block_x = pos.x / W;
let block_y = pos.y / H;
let cell_x = pos.x % W;
let cell_y = pos.y % H;
let block_pos = Pos::new(block_x, block_y);
let cell_pos = Pos::new(cell_x, cell_y);
let blocks_per_row = width / W;
let block_offset = G::pos_to_index(block_pos, blocks_per_row);
let cell_offset = C::pos_to_index(cell_pos, W);
block_offset * (W * H) + cell_offset
}
fn index_to_pos(index: usize, width: usize) -> Pos<usize> {
let cells_per_block = W * H;
let block_index = index / cells_per_block;
let cell_index = index % cells_per_block;
let block_grid_width = width / W;
let block_pos = G::index_to_pos(block_index, block_grid_width);
let cell_pos = C::index_to_pos(cell_index, W);
block_pos * Pos::new(W, H) + cell_pos
}
fn len_aligned(size: Size) -> usize {
G::len_aligned(size)
}
fn rect_to_range(grid_size: Size, rect: Rect<usize>) -> Option<Range<usize>> {
if !rect.width().is_multiple_of(W) || !rect.height().is_multiple_of(H) {
return None;
}
let start = Self::pos_to_index(rect.top_left(), grid_size.width);
let end = Self::pos_to_index(rect.bottom_right() - Pos::new(1, 1), grid_size.width) + 1;
if end > grid_size.width * grid_size.height {
return None;
}
Some(start..end)
}
fn slice_rect_aligned<E>(slice: &[E], size: Size, rect: Rect<usize>) -> Option<&[E]> {
let range = Self::rect_to_range(size, rect)?;
if range.end > slice.len() {
return None;
}
Some(&slice[range])
}
fn slice_rect_aligned_mut<E>(
slice: &mut [E],
size: Size,
rect: Rect<usize>,
) -> Option<&mut [E]> {
let range = Self::rect_to_range(size, rect)?;
if range.end > slice.len() {
return None;
}
Some(&mut slice[range])
}
fn slice_aligned<E>(slice: &[E], size: Size, axis: usize) -> &[E] {
G::slice_aligned(slice, size, axis)
}
fn slice_aligned_mut<E>(slice: &mut [E], size: Size, axis: usize) -> &mut [E] {
G::slice_aligned_mut(slice, size, axis)
}
}
#[cfg(test)]
mod tests {
extern crate alloc;
use crate::layout::ColumnMajor;
use super::*;
use alloc::vec::Vec;
#[test]
fn test_block_row_major_blocks_row_major_cells_positions() {
let rect = Rect::from_ltwh(0, 0, 4, 4);
let positions: Vec<_> = Block::<2, 2>::iter_pos(rect).collect();
assert_eq!(
positions,
&[
Pos::new(0, 0),
Pos::new(1, 0),
Pos::new(0, 1),
Pos::new(1, 1),
Pos::new(2, 0),
Pos::new(3, 0),
Pos::new(2, 1),
Pos::new(3, 1),
Pos::new(0, 2),
Pos::new(1, 2),
Pos::new(0, 3),
Pos::new(1, 3),
Pos::new(2, 2),
Pos::new(3, 2),
Pos::new(2, 3),
Pos::new(3, 3),
]
);
}
#[test]
fn test_block_row_major_big_blocks_row_major_small_blocks() {
let rect = Rect::from_ltwh(0, 0, 16, 16);
let size = Size::new(2, 2);
let blocks: Vec<_> = Block::<4, 4>::iter_rect(rect, size).collect();
assert_eq!(
blocks,
&[
Rect::from_ltwh(0, 0, 2, 2),
Rect::from_ltwh(2, 0, 2, 2),
Rect::from_ltwh(0, 2, 2, 2),
Rect::from_ltwh(2, 2, 2, 2),
Rect::from_ltwh(4, 0, 2, 2),
Rect::from_ltwh(6, 0, 2, 2),
Rect::from_ltwh(4, 2, 2, 2),
Rect::from_ltwh(6, 2, 2, 2),
Rect::from_ltwh(8, 0, 2, 2),
Rect::from_ltwh(10, 0, 2, 2),
Rect::from_ltwh(8, 2, 2, 2),
Rect::from_ltwh(10, 2, 2, 2),
Rect::from_ltwh(12, 0, 2, 2),
Rect::from_ltwh(14, 0, 2, 2),
Rect::from_ltwh(12, 2, 2, 2),
Rect::from_ltwh(14, 2, 2, 2),
Rect::from_ltwh(0, 4, 2, 2),
Rect::from_ltwh(2, 4, 2, 2),
Rect::from_ltwh(0, 6, 2, 2),
Rect::from_ltwh(2, 6, 2, 2),
Rect::from_ltwh(4, 4, 2, 2),
Rect::from_ltwh(6, 4, 2, 2),
Rect::from_ltwh(4, 6, 2, 2),
Rect::from_ltwh(6, 6, 2, 2),
Rect::from_ltwh(8, 4, 2, 2),
Rect::from_ltwh(10, 4, 2, 2),
Rect::from_ltwh(8, 6, 2, 2),
Rect::from_ltwh(10, 6, 2, 2),
Rect::from_ltwh(12, 4, 2, 2),
Rect::from_ltwh(14, 4, 2, 2),
Rect::from_ltwh(12, 6, 2, 2),
Rect::from_ltwh(14, 6, 2, 2),
Rect::from_ltwh(0, 8, 2, 2),
Rect::from_ltwh(2, 8, 2, 2),
Rect::from_ltwh(0, 10, 2, 2),
Rect::from_ltwh(2, 10, 2, 2),
Rect::from_ltwh(4, 8, 2, 2),
Rect::from_ltwh(6, 8, 2, 2),
Rect::from_ltwh(4, 10, 2, 2),
Rect::from_ltwh(6, 10, 2, 2),
Rect::from_ltwh(8, 8, 2, 2),
Rect::from_ltwh(10, 8, 2, 2),
Rect::from_ltwh(8, 10, 2, 2),
Rect::from_ltwh(10, 10, 2, 2),
Rect::from_ltwh(12, 8, 2, 2),
Rect::from_ltwh(14, 8, 2, 2),
Rect::from_ltwh(12, 10, 2, 2),
Rect::from_ltwh(14, 10, 2, 2),
Rect::from_ltwh(0, 12, 2, 2),
Rect::from_ltwh(2, 12, 2, 2),
Rect::from_ltwh(0, 14, 2, 2),
Rect::from_ltwh(2, 14, 2, 2),
Rect::from_ltwh(4, 12, 2, 2),
Rect::from_ltwh(6, 12, 2, 2),
Rect::from_ltwh(4, 14, 2, 2),
Rect::from_ltwh(6, 14, 2, 2),
Rect::from_ltwh(8, 12, 2, 2),
Rect::from_ltwh(10, 12, 2, 2),
Rect::from_ltwh(8, 14, 2, 2),
Rect::from_ltwh(10, 14, 2, 2),
Rect::from_ltwh(12, 12, 2, 2),
Rect::from_ltwh(14, 12, 2, 2),
Rect::from_ltwh(12, 14, 2, 2),
Rect::from_ltwh(14, 14, 2, 2),
]
);
}
#[test]
fn test_block_col_major_blocks_col_major_cells_positions() {
let rect = Rect::from_ltwh(0, 0, 4, 4);
let positions: Vec<_> = Block::<2, 2, ColumnMajor>::iter_pos(rect).collect();
assert_eq!(
positions,
&[
Pos::new(0, 0),
Pos::new(0, 1),
Pos::new(1, 0),
Pos::new(1, 1),
Pos::new(0, 2),
Pos::new(0, 3),
Pos::new(1, 2),
Pos::new(1, 3),
Pos::new(2, 0),
Pos::new(2, 1),
Pos::new(3, 0),
Pos::new(3, 1),
Pos::new(2, 2),
Pos::new(2, 3),
Pos::new(3, 2),
Pos::new(3, 3),
]
);
}
#[test]
fn test_block_row_major_blocks_col_major_cells_positions() {
let rect = Rect::from_ltwh(0, 0, 4, 4);
let positions: Vec<_> = Block::<2, 2, RowMajor, ColumnMajor>::iter_pos(rect).collect();
assert_eq!(
positions,
&[
Pos::new(0, 0),
Pos::new(0, 1),
Pos::new(1, 0),
Pos::new(1, 1),
Pos::new(2, 0),
Pos::new(2, 1),
Pos::new(3, 0),
Pos::new(3, 1),
Pos::new(0, 2),
Pos::new(0, 3),
Pos::new(1, 2),
Pos::new(1, 3),
Pos::new(2, 2),
Pos::new(2, 3),
Pos::new(3, 2),
Pos::new(3, 3),
]
);
}
#[test]
fn test_block_col_major_blocks_row_major_cells_positions() {
let rect = Rect::from_ltwh(0, 0, 4, 4);
let positions: Vec<_> = Block::<2, 2, ColumnMajor, RowMajor>::iter_pos(rect).collect();
assert_eq!(
positions,
&[
Pos::new(0, 0),
Pos::new(1, 0),
Pos::new(0, 1),
Pos::new(1, 1),
Pos::new(0, 2),
Pos::new(1, 2),
Pos::new(0, 3),
Pos::new(1, 3),
Pos::new(2, 0),
Pos::new(3, 0),
Pos::new(2, 1),
Pos::new(3, 1),
Pos::new(2, 2),
Pos::new(3, 2),
Pos::new(2, 3),
Pos::new(3, 3),
]
);
}
#[test]
fn test_block_row_major_to_1d() {
let expected: Vec<_> = (0..16).collect();
let actual: Vec<_> = (0..4)
.flat_map(|y| (0..4).map(move |x| Block::<4, 4>::pos_to_index(Pos::new(x, y), 4)))
.collect();
assert_eq!(actual, expected);
}
#[test]
fn test_block_col_major_to_1d() {
let expected: Vec<_> = (0..16).collect();
let actual: Vec<_> = (0..4)
.flat_map(|x| {
(0..4).map(move |y| Block::<4, 4, ColumnMajor>::pos_to_index(Pos::new(x, y), 4))
})
.collect();
assert_eq!(actual, expected);
}
#[test]
fn test_pos_to_index() {
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(0, 0), 4), 0);
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(1, 0), 4), 1);
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(0, 1), 4), 2);
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(1, 1), 4), 3);
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(2, 0), 4), 4);
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(3, 0), 4), 5);
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(2, 1), 4), 6);
assert_eq!(Block::<2, 2>::pos_to_index(Pos::new(3, 1), 4), 7);
}
#[test]
fn test_block_row_major_to_2d() {
let expected: Vec<_> = (0..4)
.flat_map(|y| (0..4).map(move |x| Pos::new(x, y)))
.collect();
let actual: Vec<_> = (0..16).map(|i| Block::<4, 4>::index_to_pos(i, 4)).collect();
assert_eq!(actual, expected);
}
#[test]
fn len_aligned() {
let size = Size::new(4, 2);
assert_eq!(Block::<2, 2>::len_aligned(size), 2);
}
#[test]
fn slice_aligned_mut_in_bounds() {
#[rustfmt::skip]
let slice = &mut [
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
assert_eq!(
Block::<2, 2>::slice_aligned_mut(slice, size, 0),
&mut [0, 1, 2, 3]
);
}
#[test]
fn slice_aligned_in_bounds() {
#[rustfmt::skip]
let slice = &[
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
assert_eq!(Block::<2, 2>::slice_aligned(slice, size, 0), &[0, 1, 2, 3]);
}
#[test]
fn slice_aligned_out_of_bounds() {
#[rustfmt::skip]
let slice = &[
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
assert_eq!(Block::<2, 2>::slice_aligned(slice, size, 2), &[]);
}
#[test]
fn slice_rect_aligned_full() {
#[rustfmt::skip]
let slice = &[
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
assert_eq!(
Block::<2, 2>::slice_rect_aligned(slice, size, Rect::from_ltwh(0, 0, 4, 2)),
Some(&[0, 1, 2, 3, 4, 5, 6, 7][..])
);
}
#[test]
fn slice_rect_aligned_partial() {
#[rustfmt::skip]
let slice = &[
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
assert_eq!(
Block::<2, 2>::slice_rect_aligned(slice, size, Rect::from_ltwh(0, 0, 2, 2)),
Some(&[0, 1, 2, 3][..])
);
assert_eq!(
Block::<2, 2>::slice_rect_aligned(slice, size, Rect::from_ltwh(2, 0, 2, 2)),
Some(&[4, 5, 6, 7][..])
);
}
#[test]
fn slice_rect_aligned_out_of_bounds() {
#[rustfmt::skip]
let slice = &[
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
assert_eq!(
Block::<2, 2>::slice_rect_aligned(slice, size, Rect::from_ltwh(0, 0, 5, 2)),
None
);
}
#[test]
fn slice_rect_unaligned_partial() {
#[rustfmt::skip]
let slice = &mut [
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
let rect = Rect::from_ltwh(0, 1, 1, 2);
assert_eq!(
Block::<2, 2>::slice_rect_aligned_mut(slice, size, rect),
None
);
}
#[test]
fn slice_rect_aligned_mut_full() {
#[rustfmt::skip]
let slice = &mut [
0, 1, 2, 3,
4, 5, 6, 7,
];
let size = Size::new(4, 2);
assert_eq!(
Block::<2, 2>::slice_rect_aligned_mut(slice, size, Rect::from_ltwh(0, 0, 4, 2)),
Some(&mut [0, 1, 2, 3, 4, 5, 6, 7][..])
);
}
}