use bevy::reflect::Reflect;
use crate::prelude::*;
#[derive(Default, Reflect)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct IntegrationBuilder {
path: Route,
integration_fields: Vec<(SectorID, Vec<FieldCell>, IntegrationField)>,
has_expanded_portals: bool,
has_los_pass: bool,
has_cost_pass: bool,
}
impl IntegrationBuilder {
pub fn new(path: Route, cost_fields: &SectorCostFields) -> Self {
let mut int_fields = Vec::with_capacity(path.get().len());
for (sector, goal) in path.get().iter() {
let cost = cost_fields.get_scaled().get(sector).unwrap();
int_fields.push((*sector, Vec::new(), IntegrationField::new(goal, cost)));
}
IntegrationBuilder {
path,
integration_fields: int_fields,
has_expanded_portals: false,
has_los_pass: false,
has_cost_pass: false,
}
}
pub fn get_route(&self) -> &Route {
&self.path
}
pub fn get_integration_fields(&self) -> &Vec<(SectorID, Vec<FieldCell>, IntegrationField)> {
&self.integration_fields
}
pub fn get_mut_integration_fields(
&mut self,
) -> &mut Vec<(SectorID, Vec<FieldCell>, IntegrationField)> {
&mut self.integration_fields
}
pub fn has_expanded_portals(&self) -> bool {
self.has_expanded_portals
}
pub fn set_expanded_portals(&mut self) {
self.has_expanded_portals = true;
}
pub fn has_los_pass(&self) -> bool {
self.has_los_pass
}
pub fn set_los_pass(&mut self) {
self.has_los_pass = true;
}
pub fn has_cost_pass(&self) -> bool {
self.has_cost_pass
}
pub fn set_cost_pass(&mut self) {
self.has_cost_pass = true;
}
pub fn expand_field_portals(
&mut self,
sector_portals: &SectorPortals,
sector_cost_fields_scaled: &SectorCostFields,
map_dimensions: &MapDimensions,
) {
for (i, (sector_id, goals, field)) in self.integration_fields.iter_mut().enumerate() {
if i == 0 {
goals.push(self.path.get()[i].1);
field.set_field_cell_value(INT_BITS_GOAL, self.path.get()[i].1);
} else {
let neighbour_sector_id = self.path.get()[i - 1].0;
let expanded_goals = sector_portals
.get()
.get(sector_id)
.unwrap()
.expand_portal_into_goals(
sector_cost_fields_scaled,
sector_id,
&self.path.get()[i].1, &neighbour_sector_id,
map_dimensions,
);
for g in expanded_goals.iter() {
goals.push(*g);
field.set_field_cell_value(INT_BITS_PORTAL, *g)
}
}
}
}
pub fn calculate_los(&mut self) {
let fields = self.get_mut_integration_fields();
if let Some((_sector, goals, field)) = fields.first_mut() {
field.set_initial_los(goals[0]);
field.calculate_sector_goal_los(goals, &goals[0]);
}
for (_sector, goals, field) in fields.iter_mut() {
if field.los_corners.is_empty() {
for g in goals {
field.add_los_corner(*g);
}
}
}
}
pub fn build_integrated_cost(&mut self, cost_fields: &SectorCostFields) {
for (sector_id, _goals, int_field) in self.get_mut_integration_fields() {
let cost_field = cost_fields.get_scaled().get(sector_id).unwrap();
int_field.calculate_field(cost_field);
}
}
}
pub const INT_BITS_LOS: u32 = 0b0000_0000_0000_0001_0000_0000_0000_0000;
pub const INT_BITS_GOAL: u32 = 0b0000_0000_0000_0010_0000_0000_0000_0000;
pub const INT_BITS_WAVE_BLOCKED: u32 = 0b0000_0000_0000_0100_0000_0000_0000_0000;
pub const INT_BITS_PORTAL: u32 = 0b0000_0000_0000_1000_0000_0000_0000_0000;
pub const INT_BITS_IMPASSABLE: u32 = 0b0000_0010_0000_0000_0000_0000_0000_0000;
pub const INT_BITS_CORNER: u32 = 0b0000_0100_0000_0000_0000_0000_0000_0000;
pub const INT_FILTER_BITS_COST: u32 = 0b0000_0000_0000_0000_1111_1111_1111_1111;
pub const INT_FILTER_BITS_FLAGS: u32 = 0b1111_1111_1111_1111_0000_0000_0000_0000;
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Reflect)]
pub struct IntegrationField {
field: [[u32; FIELD_RESOLUTION]; FIELD_RESOLUTION],
los_corners: Vec<FieldCell>,
}
impl Default for IntegrationField {
fn default() -> Self {
IntegrationField {
field: [[u16::MAX as u32; FIELD_RESOLUTION]; FIELD_RESOLUTION],
los_corners: Vec::default(),
}
}
}
impl Field<u32> for IntegrationField {
fn get(&self) -> &[[u32; FIELD_RESOLUTION]; FIELD_RESOLUTION] {
&self.field
}
fn get_field_cell_value(&self, field_cell: FieldCell) -> u32 {
self.field[field_cell.get_column()][field_cell.get_row()]
}
fn set_field_cell_value(&mut self, value: u32, field_cell: FieldCell) {
self.field[field_cell.get_column()][field_cell.get_row()] = value;
}
}
impl IntegrationField {
pub fn new(goal: &FieldCell, cost: &CostField) -> Self {
let mut field = IntegrationField::default();
for (column, rows) in cost.get().iter().enumerate() {
for (row, value) in rows.iter().enumerate() {
if *value == u8::MAX {
field.set_field_cell_value(
65535 + INT_BITS_IMPASSABLE,
FieldCell::new(column, row),
);
}
}
}
field.set_field_cell_value(INT_BITS_GOAL, *goal);
field
}
pub fn set_initial_los(&mut self, cell_id: FieldCell) {
self.set_field_cell_value(INT_BITS_LOS, cell_id);
}
pub fn add_los_corner(&mut self, corner: FieldCell) {
self.los_corners.push(corner);
}
pub fn calculate_sector_goal_los(&mut self, active_wavefront: &[FieldCell], goal: &FieldCell) {
let wavefront_cost = 1;
propagate_los(self, active_wavefront, wavefront_cost, goal);
}
pub fn calculate_field(&mut self, cost_field: &CostField) {
let mut queue: Vec<(FieldCell, u32)> = Vec::new();
for goal in self.los_corners.iter() {
queue.push(((*goal), self.get_field_cell_value(*goal)));
}
process_neighbours(self, queue, cost_field);
}
}
fn propagate_los(
field: &mut IntegrationField,
active_wavefront: &[FieldCell],
mut wavefront_cost: u32,
goal: &FieldCell,
) {
let mut moved_wavefront: Vec<FieldCell> = Vec::new();
for wavefront in active_wavefront.iter() {
let neighbours = Ordinal::get_orthogonal_cell_neighbours(*wavefront);
for n in neighbours.iter() {
let cost = field.get_field_cell_value(*n);
if cost & INT_BITS_WAVE_BLOCKED == INT_BITS_WAVE_BLOCKED
|| cost & INT_BITS_GOAL == INT_BITS_GOAL
{
} else if cost & INT_BITS_IMPASSABLE == INT_BITS_IMPASSABLE {
let dir = Ordinal::cell_to_cell_direction(*n, *wavefront);
match dir {
Ordinal::North | Ordinal::South => {
if let Some(west) = Ordinal::get_cell_neighbour(*wavefront, Ordinal::West) {
let west_cost = field.get_field_cell_value(west);
if west_cost & INT_BITS_IMPASSABLE != INT_BITS_IMPASSABLE {
extend_los_corner(field, n, Ordinal::West, goal, wavefront_cost);
}
}
if let Some(east) = Ordinal::get_cell_neighbour(*wavefront, Ordinal::East) {
let east_cost = field.get_field_cell_value(east);
if east_cost & INT_BITS_IMPASSABLE != INT_BITS_IMPASSABLE {
extend_los_corner(field, n, Ordinal::East, goal, wavefront_cost);
}
}
}
Ordinal::East | Ordinal::West => {
if let Some(north) = Ordinal::get_cell_neighbour(*wavefront, Ordinal::North)
{
let north_cost = field.get_field_cell_value(north);
if north_cost & INT_BITS_IMPASSABLE != INT_BITS_IMPASSABLE {
extend_los_corner(field, n, Ordinal::North, goal, wavefront_cost);
}
}
if let Some(south) = Ordinal::get_cell_neighbour(*wavefront, Ordinal::South)
{
let south_cost = field.get_field_cell_value(south);
if south_cost & INT_BITS_IMPASSABLE != INT_BITS_IMPASSABLE {
extend_los_corner(field, n, Ordinal::South, goal, wavefront_cost);
}
}
}
_ => {
panic!("Dir should only be orthogonal")
}
}
} else if cost & INT_BITS_LOS != INT_BITS_LOS {
let mut value = wavefront_cost;
value |= INT_BITS_LOS;
field.set_field_cell_value(value, *n);
moved_wavefront.push(*n);
}
}
}
wavefront_cost += 1;
if !moved_wavefront.is_empty() {
propagate_los(field, &moved_wavefront, wavefront_cost, goal);
}
}
fn extend_los_corner(
field: &mut IntegrationField,
neighbour: &FieldCell,
ord: Ordinal,
goal: &FieldCell,
wavefront_cost: u32,
) {
if let Some(adj) = Ordinal::get_cell_neighbour(*neighbour, ord) {
let value = field.get_field_cell_value(adj);
if value & INT_BITS_IMPASSABLE != INT_BITS_IMPASSABLE {
let end = check_los_corner_propagation(&adj, goal);
let blocked_cells = adj.get_cells_between_points(&end);
for (i, blocked) in blocked_cells.iter().enumerate() {
let value = field.get_field_cell_value(*blocked);
if value & INT_BITS_IMPASSABLE == INT_BITS_IMPASSABLE {
break;
}
if i > 0 {
let previous = &blocked_cells[i - 1];
match Ordinal::cell_to_cell_direction(*blocked, *previous) {
Ordinal::NorthEast => {
if let Some(south) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::South)
{
if let Some(west) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::West)
{
let s_v =
field.get_field_cell_value(south) & INT_BITS_IMPASSABLE;
let w_v =
field.get_field_cell_value(west) & INT_BITS_IMPASSABLE;
if s_v == INT_BITS_IMPASSABLE && w_v == INT_BITS_IMPASSABLE {
break;
}
}
}
}
Ordinal::SouthEast => {
if let Some(north) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::North)
{
if let Some(west) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::West)
{
let n_v =
field.get_field_cell_value(north) & INT_BITS_IMPASSABLE;
let w_v =
field.get_field_cell_value(west) & INT_BITS_IMPASSABLE;
if n_v == INT_BITS_IMPASSABLE && w_v == INT_BITS_IMPASSABLE {
break;
}
}
}
}
Ordinal::SouthWest => {
if let Some(north) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::North)
{
if let Some(east) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::East)
{
let n_v =
field.get_field_cell_value(north) & INT_BITS_IMPASSABLE;
let e_v =
field.get_field_cell_value(east) & INT_BITS_IMPASSABLE;
if n_v == INT_BITS_IMPASSABLE && e_v == INT_BITS_IMPASSABLE {
break;
}
}
}
}
Ordinal::NorthWest => {
if let Some(south) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::South)
{
if let Some(east) =
Ordinal::get_cell_neighbour(*blocked, Ordinal::East)
{
let s_v =
field.get_field_cell_value(south) & INT_BITS_IMPASSABLE;
let e_v =
field.get_field_cell_value(east) & INT_BITS_IMPASSABLE;
if s_v == INT_BITS_IMPASSABLE && e_v == INT_BITS_IMPASSABLE {
break;
}
}
}
}
Ordinal::Zero => panic!("Neighbour not found"),
_ => {}
}
}
if value & INT_BITS_WAVE_BLOCKED != INT_BITS_WAVE_BLOCKED {
field.add_los_corner(*blocked);
field.set_field_cell_value(
wavefront_cost + 1 + i as u32 + INT_BITS_WAVE_BLOCKED + INT_BITS_CORNER,
*blocked,
);
}
}
}
}
}
fn check_los_corner_propagation(adj: &FieldCell, goal: &FieldCell) -> FieldCell {
if adj.get_column() == goal.get_column() {
if adj.get_row() > goal.get_row() {
FieldCell::new(adj.get_column(), FIELD_RESOLUTION - 1)
} else {
FieldCell::new(adj.get_column(), 0)
}
} else if adj.get_row() == goal.get_row() {
if adj.get_column() > goal.get_column() {
FieldCell::new(FIELD_RESOLUTION - 1, adj.get_row())
} else {
FieldCell::new(0, adj.get_row())
}
} else {
let delta_column = adj.get_column() as f32 - goal.get_column() as f32;
let delta_row = adj.get_row() as f32 - goal.get_row() as f32;
let gradient = delta_row / delta_column;
let intercept = -gradient * (adj.get_column() as f32) + adj.get_row() as f32;
let mut exists = None;
if adj.get_column() > goal.get_column() {
let d = (FIELD_RESOLUTION - 1)
.checked_sub(adj.get_column())
.unwrap();
for x in 0..=d {
let end_col = adj.get_column() + x;
let end_row = (gradient * (end_col as f32) + intercept).floor();
if end_row > FIELD_RESOLUTION as f32 - 1.0 {
if end_col < FIELD_RESOLUTION {
exists = Some(FieldCell::new(end_col, FIELD_RESOLUTION - 1));
break;
} else {
exists = Some(FieldCell::new(FIELD_RESOLUTION - 1, FIELD_RESOLUTION - 1));
break;
}
} else if end_row < 0.0 {
if end_col < FIELD_RESOLUTION {
exists = Some(FieldCell::new(end_col, 0));
break;
} else {
exists = Some(FieldCell::new(FIELD_RESOLUTION - 1, 0));
break;
}
} else if end_col == FIELD_RESOLUTION - 1 {
exists = Some(FieldCell::new(end_col, end_row as usize));
break;
}
}
if let Some(end) = exists {
end
} else {
panic!("LOS corner prop failed to find increment boundary");
}
} else {
let d = adj.get_column();
for x in 0..=d {
let end_col = adj.get_column().checked_sub(x).unwrap();
let end_row = (gradient * (end_col as f32) + intercept).floor() as usize;
if end_col == 0 {
if end_row > FIELD_RESOLUTION - 1 {
exists = Some(FieldCell::new(end_col, FIELD_RESOLUTION - 1));
break;
} else {
exists = Some(FieldCell::new(end_col, end_row));
break;
}
}
if end_row == 0 {
exists = Some(FieldCell::new(end_col, end_row));
break;
}
if end_row > FIELD_RESOLUTION - 1 {
exists = Some(FieldCell::new(end_col, FIELD_RESOLUTION - 1));
break;
}
}
if let Some(end) = exists {
end
} else {
panic!("LOS corner prop failed to find decrement boundary");
}
}
}
}
fn process_neighbours(
int_field: &mut IntegrationField,
queue: Vec<(FieldCell, u32)>,
cost_field: &CostField,
) {
let mut next_neighbours = Vec::new();
for (cell, prev_int_cost) in queue.iter() {
let neighbours = Ordinal::get_orthogonal_cell_neighbours(*cell);
for n in neighbours.iter() {
let n_int = int_field.get_field_cell_value(*n);
if n_int & INT_BITS_IMPASSABLE != INT_BITS_IMPASSABLE
&& n_int & INT_BITS_LOS != INT_BITS_LOS
{
let cell_cost = cost_field.get_field_cell_value(*n) as u32;
let int_cost = cell_cost + (prev_int_cost & INT_FILTER_BITS_COST);
if int_cost < (n_int & INT_FILTER_BITS_COST) {
int_field.set_field_cell_value(int_cost, *n);
next_neighbours.push((*n, int_cost));
}
}
}
}
if !next_neighbours.is_empty() {
process_neighbours(int_field, next_neighbours, cost_field);
}
}
#[rustfmt::skip]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hori_los_prop_max() {
let goal = FieldCell::new(4, 3);
let adjacent = FieldCell::new(5, 3);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(9, 3);
assert_eq!(actual, result)
}
#[test]
fn hori_los_prop_min() {
let goal = FieldCell::new(5, 3);
let adjacent = FieldCell::new(4, 3);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(0, 3);
assert_eq!(actual, result)
}
#[test]
fn vert_los_prop_max() {
let goal = FieldCell::new(4, 3);
let adjacent = FieldCell::new(4, 5);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(4, 9);
assert_eq!(actual, result)
}
#[test]
fn vert_los_prop_min() {
let goal = FieldCell::new(4, 5);
let adjacent = FieldCell::new(4, 3);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(4, 0);
assert_eq!(actual, result)
}
#[test]
fn los_prop_left_right_down() {
let goal = FieldCell::new(4, 3);
let adjacent = FieldCell::new(5, 7);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(6, 9);
assert_eq!(actual, result)
}
#[test]
fn los_prop_left_right_up() {
let goal = FieldCell::new(4, 3);
let adjacent = FieldCell::new(7, 2);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(9, 1);
assert_eq!(actual, result)
}
#[test]
fn los_prop_right_left_down() {
let goal = FieldCell::new(5, 1);
let adjacent = FieldCell::new(3, 3);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(0, 6);
assert_eq!(actual, result)
}
#[test]
fn los_prop_right_left_up() {
let goal = FieldCell::new(8, 7);
let adjacent = FieldCell::new(6, 3);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(4, 0);
assert_eq!(actual, result)
}
#[test]
fn los_prop_right_left_up2() {
let goal = FieldCell::new(4, 6);
let adjacent = FieldCell::new(3, 5);
let result = check_los_corner_propagation(&adjacent, &goal);
let actual = FieldCell::new(0, 2);
assert_eq!(actual, result)
}
#[test]
fn basic_field() {
let cost_field = CostField::default();
let goal = FieldCell::new(4, 4);
let mut integration_field = IntegrationField::new(&goal, &cost_field);
integration_field.add_los_corner(goal);
integration_field.calculate_field(&cost_field);
let mut result = *integration_field.get();
for col in result.iter_mut() {
for value in col.iter_mut() {
*value &= INT_FILTER_BITS_COST
}
}
let actual: [[u32; FIELD_RESOLUTION]; FIELD_RESOLUTION] = [
[8,7,6,5,4,5,6,7,8,9], [7,6,5,4,3,4,5,6,7,8], [6,5,4,3,2,3,4,5,6,7], [5,4,3,2,1,2,3,4,5,6],
[4,3,2,1,0,1,2,3,4,5],
[5,4,3,2,1,2,3,4,5,6],
[6,5,4,3,2,3,4,5,6,7],
[7,6,5,4,3,4,5,6,7,8],
[8,7,6,5,4,5,6,7,8,9],
[9,8,7,6,5,6,7,8,9,10]
];
assert_eq!(actual, result);
}
}