grid symmetry helpers
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
use crate::config::SymmetryType;
|
||||
|
||||
pub fn mirror_coords(
|
||||
row: usize,
|
||||
col: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
symmetry: SymmetryType,
|
||||
) -> Vec<(usize, usize)> {
|
||||
let mut coords = vec![(row, col)];
|
||||
|
||||
let mirror_row = |i: usize| height.checked_sub(1).and_then(|h| h.checked_sub(i));
|
||||
let mirror_col = |i: usize| width.checked_sub(1).and_then(|w| w.checked_sub(i));
|
||||
|
||||
match symmetry {
|
||||
SymmetryType::Rotational180 => {
|
||||
if let (Some(mr), Some(mc)) = (mirror_row(row), mirror_col(col)) {
|
||||
coords.push((mr, mc));
|
||||
}
|
||||
}
|
||||
SymmetryType::Rotational90 => {
|
||||
// each 90-degree image is kept only when it lands inside the grid,
|
||||
// so non-square grids no longer underflow or go out of bounds
|
||||
if let Some(mc) = mirror_col(row) {
|
||||
if col < height {
|
||||
coords.push((col, mc));
|
||||
}
|
||||
}
|
||||
if let (Some(mr), Some(mc)) = (mirror_row(row), mirror_col(col)) {
|
||||
coords.push((mr, mc));
|
||||
}
|
||||
if let Some(mr) = mirror_row(col) {
|
||||
if row < width {
|
||||
coords.push((mr, row));
|
||||
}
|
||||
}
|
||||
}
|
||||
SymmetryType::Diagonal => {
|
||||
if width == height {
|
||||
coords.push((col, row));
|
||||
}
|
||||
}
|
||||
SymmetryType::Mirror => {
|
||||
if let Some(mc) = mirror_col(col) {
|
||||
coords.push((row, mc));
|
||||
}
|
||||
}
|
||||
SymmetryType::None => {}
|
||||
}
|
||||
coords.sort();
|
||||
coords.dedup();
|
||||
coords
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn rotational_180() {
|
||||
let coords = mirror_coords(0, 0, 5, 5, SymmetryType::Rotational180);
|
||||
assert_eq!(coords, vec![(0, 0), (4, 4)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotational_180_center() {
|
||||
let coords = mirror_coords(2, 2, 5, 5, SymmetryType::Rotational180);
|
||||
assert_eq!(coords, vec![(2, 2)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotational_90() {
|
||||
let coords = mirror_coords(0, 1, 5, 5, SymmetryType::Rotational90);
|
||||
assert!(coords.contains(&(0, 1)));
|
||||
assert!(coords.contains(&(1, 4)));
|
||||
assert!(coords.contains(&(4, 3)));
|
||||
assert!(coords.contains(&(3, 0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mirror_lr() {
|
||||
let coords = mirror_coords(1, 0, 5, 5, SymmetryType::Mirror);
|
||||
assert_eq!(coords, vec![(1, 0), (1, 4)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagonal() {
|
||||
let coords = mirror_coords(1, 3, 5, 5, SymmetryType::Diagonal);
|
||||
assert_eq!(coords, vec![(1, 3), (3, 1)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rotational_90_non_square_stays_in_bounds() {
|
||||
let coords = mirror_coords(0, 3, 5, 7, SymmetryType::Rotational90);
|
||||
for (r, c) in &coords {
|
||||
assert!(*r < 7 && *c < 5, "point ({}, {}) out of bounds", r, c);
|
||||
}
|
||||
assert!(coords.contains(&(0, 3)));
|
||||
let coords2 = mirror_coords(6, 4, 5, 7, SymmetryType::Rotational90);
|
||||
for (r, c) in &coords2 {
|
||||
assert!(*r < 7 && *c < 5, "point ({}, {}) out of bounds", r, c);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_symmetry() {
|
||||
let coords = mirror_coords(1, 2, 5, 5, SymmetryType::None);
|
||||
assert_eq!(coords, vec![(1, 2)]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user