pub mod cost_field;
pub mod flow_field;
pub mod integration_field;
use std::collections::BTreeMap;
use crate::prelude::*;
use bevy::prelude::*;
use bevy::utils::Duration;
pub trait Field<T> {
fn get(&self) -> &[[T; FIELD_RESOLUTION]; FIELD_RESOLUTION];
fn get_field_cell_value(&self, field_cell: FieldCell) -> T;
fn set_field_cell_value(&mut self, value: T, field_cell: FieldCell);
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash, Reflect)]
pub struct FieldCell((usize, usize));
impl FieldCell {
pub fn new(column: usize, row: usize) -> Self {
FieldCell((column, row))
}
pub fn get_column_row(&self) -> (usize, usize) {
self.0
}
pub fn get_column(&self) -> usize {
self.0 .0
}
pub fn get_row(&self) -> usize {
self.0 .1
}
pub fn get_boundary_ordinal_from_field_cell(&self) -> Vec<Ordinal> {
let mut boundaries = Vec::new();
if self.get_row() == 0 {
boundaries.push(Ordinal::North);
}
if self.get_column() == FIELD_RESOLUTION - 1 {
boundaries.push(Ordinal::East);
}
if self.get_row() == FIELD_RESOLUTION - 1 {
boundaries.push(Ordinal::South);
}
if self.get_column() == 0 {
boundaries.push(Ordinal::West);
}
if !boundaries.is_empty() {
boundaries
} else {
panic!("{:?} does not sit along the boundary", self);
}
}
pub fn get_cells_between_points(&self, target: &FieldCell) -> Vec<FieldCell> {
let source_col = self.get_column() as i32;
let source_row = self.get_row() as i32;
let target_col = target.get_column() as i32;
let target_row = target.get_row() as i32;
if source_col == target_col {
let mut fields = Vec::new();
if source_row < target_row {
for row in source_row..=target_row {
fields.push(FieldCell::new(source_col as usize, row as usize));
}
fields
} else {
for row in target_row..=source_row {
fields.push(FieldCell::new(source_col as usize, row as usize));
}
fields.reverse();
fields
}
} else if source_row == target_row {
let mut fields = Vec::new();
if source_col < target_col {
for col in source_col..=target_col {
fields.push(FieldCell::new(col as usize, source_row as usize));
}
fields
} else {
for col in target_col..=source_col {
fields.push(FieldCell::new(col as usize, source_row as usize));
}
fields.reverse();
fields
}
} else if (target_row - source_row).abs() < (target_col - source_col).abs() {
if source_col > target_col {
let mut fields =
walk_bresenham_shallow(target_col, target_row, source_col, source_row);
fields.reverse();
fields
} else {
walk_bresenham_shallow(source_col, source_row, target_col, target_row)
}
} else if source_row > target_row {
let mut fields = walk_bresenham_steep(target_col, target_row, source_col, source_row);
fields.reverse();
fields
} else {
walk_bresenham_steep(source_col, source_row, target_col, target_row)
}
}
}
fn walk_bresenham_shallow(col_0: i32, row_0: i32, col_1: i32, row_1: i32) -> Vec<FieldCell> {
let mut cells = Vec::new();
let delta_col = col_1 - col_0;
let mut delta_row = row_1 - row_0;
let mut row_increment = 1;
if delta_row < 0 {
row_increment = -1;
delta_row *= -1;
}
let mut difference = 2 * delta_row - delta_col;
let mut row = row_0;
for col in col_0..=col_1 {
cells.push(FieldCell::new(col as usize, row as usize));
if difference > 0 {
row += row_increment;
difference += 2 * (delta_row - delta_col);
} else {
difference += 2 * delta_row;
}
}
cells
}
fn walk_bresenham_steep(col_0: i32, row_0: i32, col_1: i32, row_1: i32) -> Vec<FieldCell> {
let mut cells = Vec::new();
let mut delta_col = col_1 - col_0;
let delta_row = row_1 - row_0;
let mut col_increment = 1;
if delta_col < 0 {
col_increment = -1;
delta_col *= -1;
}
let mut difference = 2 * delta_col - delta_row;
let mut col = col_0;
for row in row_0..=row_1 {
cells.push(FieldCell::new(col as usize, row as usize));
if difference > 0 {
col += col_increment;
difference += 2 * (delta_col - delta_row);
} else {
difference += 2 * delta_col;
}
}
cells
}
#[derive(Clone, Copy, Debug, Reflect)]
pub struct RouteMetadata {
source_sector: SectorID,
target_sector: SectorID,
target_goal: FieldCell,
time_generated: Duration,
}
impl PartialEq for RouteMetadata {
fn eq(&self, other: &Self) -> bool {
self.source_sector == other.source_sector
&& self.target_sector == other.target_sector
&& self.target_goal == other.target_goal
}
}
impl Eq for RouteMetadata {}
impl Ord for RouteMetadata {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.source_sector, self.target_sector, self.target_goal).cmp(&(
other.source_sector,
other.target_sector,
other.target_goal,
))
}
}
impl PartialOrd for RouteMetadata {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl RouteMetadata {
pub fn get_source_sector(&self) -> SectorID {
self.source_sector
}
pub fn get_target_sector(&self) -> SectorID {
self.target_sector
}
pub fn get_target_goal(&self) -> FieldCell {
self.target_goal
}
pub fn get_time_generated(&self) -> Duration {
self.time_generated
}
}
#[derive(Component, Default, Clone)]
pub struct RouteCache(BTreeMap<RouteMetadata, Vec<(SectorID, FieldCell)>>);
impl RouteCache {
pub fn get(&self) -> &BTreeMap<RouteMetadata, Vec<(SectorID, FieldCell)>> {
&self.0
}
pub fn get_mut(&mut self) -> &mut BTreeMap<RouteMetadata, Vec<(SectorID, FieldCell)>> {
&mut self.0
}
pub fn get_route(
&self,
source_sector: SectorID,
target_sector: SectorID,
goal_id: FieldCell,
) -> Option<&Vec<(SectorID, FieldCell)>> {
let route_data = RouteMetadata {
source_sector,
target_sector,
target_goal: goal_id,
time_generated: Duration::default(),
};
let route = self.0.get(&route_data);
trace!("Route: {:?}", route);
route
}
pub fn insert_route(
&mut self,
source_sector: SectorID,
target_sector: SectorID,
goal_id: FieldCell,
elapsed_duration: Duration,
route: Vec<(SectorID, FieldCell)>,
) {
let route_data = RouteMetadata {
source_sector,
target_sector,
target_goal: goal_id,
time_generated: elapsed_duration,
};
self.0.insert(route_data, route);
}
pub fn remove_route(&mut self, route_metadata: RouteMetadata) {
self.0.remove(&route_metadata);
}
}
#[derive(Clone, Copy, Reflect)]
pub struct FlowFieldMetadata {
sector_id: SectorID,
goal_id: FieldCell,
time_generated: Duration,
}
impl PartialEq for FlowFieldMetadata {
fn eq(&self, other: &Self) -> bool {
self.sector_id == other.sector_id && self.goal_id == other.goal_id
}
}
impl Eq for FlowFieldMetadata {}
impl FlowFieldMetadata {
pub fn get_sector_id(&self) -> SectorID {
self.sector_id
}
pub fn get_goal_id(&self) -> FieldCell {
self.goal_id
}
pub fn get_time_generated(&self) -> Duration {
self.time_generated
}
}
impl Ord for FlowFieldMetadata {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.sector_id, self.goal_id).cmp(&(other.sector_id, other.goal_id))
}
}
impl PartialOrd for FlowFieldMetadata {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[derive(Component, Default)]
pub struct FlowFieldCache(BTreeMap<FlowFieldMetadata, FlowField>);
impl FlowFieldCache {
pub fn get(&self) -> &BTreeMap<FlowFieldMetadata, FlowField> {
&self.0
}
pub fn get_mut(&mut self) -> &mut BTreeMap<FlowFieldMetadata, FlowField> {
&mut self.0
}
pub fn get_field(&self, sector_id: SectorID, goal_id: FieldCell) -> Option<&FlowField> {
let flow_meta = FlowFieldMetadata {
sector_id,
goal_id,
time_generated: Duration::default(),
};
self.0.get(&flow_meta)
}
pub fn insert_field(
&mut self,
sector_id: SectorID,
goal_id: FieldCell,
elapsed_duration: Duration,
field: FlowField,
) {
let flow_meta = FlowFieldMetadata {
sector_id,
goal_id,
time_generated: elapsed_duration,
};
self.0.insert(flow_meta, field);
}
pub fn remove_field(&mut self, flow_meta: FlowFieldMetadata) {
self.0.remove(&flow_meta);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn field_cell_line_horizontal() {
let source = FieldCell::new(3, 4);
let target = FieldCell::new(7, 4);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(3, 4),
FieldCell::new(4, 4),
FieldCell::new(5, 4),
FieldCell::new(6, 4),
FieldCell::new(7, 4),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_horizontal_reverse() {
let source = FieldCell::new(7, 4);
let target = FieldCell::new(3, 4);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(7, 4),
FieldCell::new(6, 4),
FieldCell::new(5, 4),
FieldCell::new(4, 4),
FieldCell::new(3, 4),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_vertical() {
let source = FieldCell::new(3, 4);
let target = FieldCell::new(3, 7);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(3, 4),
FieldCell::new(3, 5),
FieldCell::new(3, 6),
FieldCell::new(3, 7),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_vertical_reverse() {
let source = FieldCell::new(3, 7);
let target = FieldCell::new(3, 4);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(3, 7),
FieldCell::new(3, 6),
FieldCell::new(3, 5),
FieldCell::new(3, 4),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_vertical_steep() {
let source = FieldCell::new(3, 0);
let target = FieldCell::new(4, 9);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(3, 0),
FieldCell::new(3, 1),
FieldCell::new(3, 2),
FieldCell::new(3, 3),
FieldCell::new(3, 4),
FieldCell::new(4, 5),
FieldCell::new(4, 6),
FieldCell::new(4, 7),
FieldCell::new(4, 8),
FieldCell::new(4, 9),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_pos_gradient() {
let source = FieldCell::new(3, 4);
let target = FieldCell::new(7, 6);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(3, 4),
FieldCell::new(4, 4),
FieldCell::new(5, 5),
FieldCell::new(6, 5),
FieldCell::new(7, 6),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_pos_gradient_reverse() {
let source = FieldCell::new(7, 6);
let target = FieldCell::new(3, 4);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(7, 6),
FieldCell::new(6, 5),
FieldCell::new(5, 5),
FieldCell::new(4, 4),
FieldCell::new(3, 4),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_neg_gradient() {
let source = FieldCell::new(3, 4);
let target = FieldCell::new(7, 2);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(3, 4),
FieldCell::new(4, 4),
FieldCell::new(5, 3),
FieldCell::new(6, 3),
FieldCell::new(7, 2),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_neg_gradient_reverse() {
let source = FieldCell::new(7, 2);
let target = FieldCell::new(3, 4);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![
FieldCell::new(7, 2),
FieldCell::new(6, 3),
FieldCell::new(5, 3),
FieldCell::new(4, 4),
FieldCell::new(3, 4),
];
assert_eq!(actual, result);
}
#[test]
fn field_cell_line_zero() {
let source = FieldCell::new(3, 4);
let target = FieldCell::new(3, 4);
let result = source.get_cells_between_points(&target);
let actual: Vec<FieldCell> = vec![FieldCell::new(3, 4)];
assert_eq!(actual, result);
}
}