use super::Rect;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
pub enum Direction {
#[default]
North,
East,
South,
West,
}
impl FromStr for Direction {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"North" => Ok(Direction::North),
"South" => Ok(Direction::South),
"East" => Ok(Direction::East),
"West" => Ok(Direction::West),
_ => Err(()),
}
}
}
fn find_north(rects: &[Rect], current: usize) -> Option<usize> {
let current_rect = rects.get(current).or(None)?;
if current_rect.top_edge() <= 0 {
return None;
}
let mut nearest_rect: Option<usize> = None;
let mut min_x: Option<i32> = None;
let mut min_y: Option<i32> = None;
for (i, r) in rects.iter().enumerate() {
if r == current_rect || r.right_edge() - 1 < current_rect.left_edge() || r.left_edge() + 1 > current_rect.right_edge() || r.top_edge() + 1 > current_rect.bottom_edge()
{
continue;
}
let x_distance = current_rect.left_edge() - r.right_edge();
let y_distance = current_rect.top_edge() - r.bottom_edge();
find_nearest_rect(
&mut min_x,
&mut min_y,
&mut nearest_rect,
x_distance,
y_distance,
i,
true,
);
}
nearest_rect
}
fn find_east(rects: &[Rect], current: usize, display_width: u32) -> Option<usize> {
let current_rect = rects.get(current).or(None)?;
if current_rect.right_edge() >= display_width as i32 {
return None;
}
let mut nearest_rect: Option<usize> = None;
let mut min_x: Option<i32> = None;
let mut min_y: Option<i32> = None;
for (i, r) in rects.iter().enumerate() {
if r == current_rect || r.right_edge() - 1 < current_rect.right_edge() || r.bottom_edge() - 1 < current_rect.top_edge() || r.top_edge() + 1 > current_rect.bottom_edge()
{
continue;
}
let x_distance = r.left_edge() - current_rect.right_edge();
let y_distance = r.top_edge() - current_rect.bottom_edge();
find_nearest_rect(
&mut min_x,
&mut min_y,
&mut nearest_rect,
x_distance,
y_distance,
i,
false,
);
}
nearest_rect
}
fn find_south(rects: &[Rect], current: usize, display_height: u32) -> Option<usize> {
let current_rect = rects.get(current).or(None)?;
if current_rect.y + current_rect.h as i32 >= display_height as i32 {
return None;
}
let mut nearest_rect: Option<usize> = None;
let mut min_x: Option<i32> = None;
let mut min_y: Option<i32> = None;
for (i, r) in rects.iter().enumerate() {
if r == current_rect || r.right_edge() - 1 < current_rect.left_edge() || r.left_edge() + 1 > current_rect.right_edge() || r.bottom_edge() - 1 < current_rect.top_edge()
{
continue;
}
let x_distance = current_rect.left_edge() - r.right_edge();
let y_distance = r.top_edge() - current_rect.bottom_edge();
find_nearest_rect(
&mut min_x,
&mut min_y,
&mut nearest_rect,
x_distance,
y_distance,
i,
true,
);
}
nearest_rect
}
fn find_west(rects: &[Rect], current: usize) -> Option<usize> {
let current_rect = rects.get(current).or(None)?;
if current_rect.left_edge() <= 0 {
return None;
}
let mut nearest_rect: Option<usize> = None;
let mut min_x: Option<i32> = None;
let mut min_y: Option<i32> = None;
for (i, r) in rects.iter().enumerate() {
if r == current_rect || r.left_edge() + 1 > current_rect.right_edge() || r.bottom_edge() - 1 < current_rect.top_edge() || r.top_edge() + 1 > current_rect.bottom_edge()
{
continue;
}
let x_distance = current_rect.left_edge() - r.right_edge();
let y_distance = r.top_edge() - current_rect.bottom_edge();
find_nearest_rect(
&mut min_x,
&mut min_y,
&mut nearest_rect,
x_distance,
y_distance,
i,
false,
);
}
nearest_rect
}
fn find_nearest_rect(
min_x: &mut Option<i32>,
min_y: &mut Option<i32>,
nearest_rect: &mut Option<usize>,
x_distance: i32,
y_distance: i32,
index: usize,
updown: bool,
) {
if min_x.is_none() {
*min_x = Some(x_distance);
*nearest_rect = Some(index);
}
if min_y.is_none() {
*min_y = Some(y_distance);
*nearest_rect = Some(index);
}
if updown {
if y_distance < min_y.unwrap() {
*min_y = Some(y_distance);
*nearest_rect = Some(index);
} else if y_distance == min_y.unwrap() && x_distance < min_x.unwrap() {
*min_x = Some(x_distance);
*nearest_rect = Some(index);
}
} else if x_distance < min_x.unwrap() {
*min_x = Some(x_distance);
*nearest_rect = Some(index);
} else if x_distance == min_x.unwrap() && y_distance < min_y.unwrap() {
*min_y = Some(y_distance);
*nearest_rect = Some(index);
}
}
impl Direction {
pub fn find_neighbor(
rects: &[Rect],
current: usize,
direction: Direction,
container: &Rect,
) -> Option<usize> {
if current >= rects.len() {
return None;
}
match direction {
Direction::North => find_north(rects, current),
Direction::East => find_east(rects, current, container.w),
Direction::South => find_south(rects, current, container.h),
Direction::West => find_west(rects, current),
}
}
}
#[cfg(test)]
mod tests {
use crate::geometry::{Direction, Rect};
const CONTAINER: Rect = Rect {
x: 0,
y: 0,
w: 600,
h: 600,
};
const ARRAY: [Rect; 7] = [
Rect {
x: 0,
y: 0,
w: 200,
h: 200,
},
Rect {
x: 0,
y: 200,
w: 200,
h: 200,
},
Rect {
x: 0,
y: 400,
w: 200,
h: 200,
},
Rect {
x: 200,
y: 0,
w: 200,
h: 200,
},
Rect {
x: 400,
y: 0,
w: 200,
h: 200,
},
Rect {
x: 400,
y: 200,
w: 200,
h: 400,
},
Rect {
x: 200,
y: 200,
w: 200,
h: 400,
},
];
#[test]
fn north_neighbor() {
let res = Direction::find_neighbor(&ARRAY, 0, Direction::North, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 1, Direction::North, &CONTAINER);
assert_eq!(res, Some(0));
let res = Direction::find_neighbor(&ARRAY, 2, Direction::North, &CONTAINER);
assert_eq!(res, Some(1));
let res = Direction::find_neighbor(&ARRAY, 3, Direction::North, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 4, Direction::North, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 5, Direction::North, &CONTAINER);
assert_eq!(res, Some(4));
let res = Direction::find_neighbor(&ARRAY, 6, Direction::North, &CONTAINER);
assert_eq!(res, Some(3));
}
#[test]
fn east_neighbor() {
let res = Direction::find_neighbor(&ARRAY, 0, Direction::East, &CONTAINER);
assert_eq!(res, Some(3));
let res = Direction::find_neighbor(&ARRAY, 1, Direction::East, &CONTAINER);
assert_eq!(res, Some(6));
let res = Direction::find_neighbor(&ARRAY, 2, Direction::East, &CONTAINER);
assert_eq!(res, Some(6));
let res = Direction::find_neighbor(&ARRAY, 3, Direction::East, &CONTAINER);
assert_eq!(res, Some(4));
let res = Direction::find_neighbor(&ARRAY, 4, Direction::East, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 5, Direction::East, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 6, Direction::East, &CONTAINER);
assert_eq!(res, Some(5));
}
#[test]
fn south_neighbor() {
let res = Direction::find_neighbor(&ARRAY, 0, Direction::South, &CONTAINER);
assert_eq!(res, Some(1));
let res = Direction::find_neighbor(&ARRAY, 1, Direction::South, &CONTAINER);
assert_eq!(res, Some(2));
let res = Direction::find_neighbor(&ARRAY, 2, Direction::South, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 3, Direction::South, &CONTAINER);
assert_eq!(res, Some(6));
let res = Direction::find_neighbor(&ARRAY, 4, Direction::South, &CONTAINER);
assert_eq!(res, Some(5));
let res = Direction::find_neighbor(&ARRAY, 5, Direction::South, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 6, Direction::South, &CONTAINER);
assert_eq!(res, None);
}
#[test]
fn west_neighbor() {
let res = Direction::find_neighbor(&ARRAY, 0, Direction::West, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 1, Direction::West, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 2, Direction::West, &CONTAINER);
assert_eq!(res, None);
let res = Direction::find_neighbor(&ARRAY, 3, Direction::West, &CONTAINER);
assert_eq!(res, Some(0));
let res = Direction::find_neighbor(&ARRAY, 4, Direction::West, &CONTAINER);
assert_eq!(res, Some(3));
let res = Direction::find_neighbor(&ARRAY, 5, Direction::West, &CONTAINER);
assert_eq!(res, Some(6));
let res = Direction::find_neighbor(&ARRAY, 6, Direction::West, &CONTAINER);
assert_eq!(res, Some(1));
}
}