use crate::coordinates::{ Distance, Neighbors };
use crate::collection::Grid2D;
#[ derive( Debug, Clone, Copy, PartialEq, Eq, Hash ) ]
pub enum FlowDirection
{
None,
Move( i32, i32 ),
}
#[ derive( Debug, Clone ) ]
pub struct IntegrationField< System, Orientation >
{
pub max_cost : u32,
_phantom_system : std::marker::PhantomData< System >,
_phantom_orientation : std::marker::PhantomData< Orientation >,
}
#[ derive( Debug, Clone ) ]
pub struct FlowField< System, Orientation >
{
#[ allow( dead_code ) ]
integration : IntegrationField< System, Orientation >,
#[ allow( dead_code ) ]
width : i32,
#[ allow( dead_code ) ]
height : i32,
_phantom_system : std::marker::PhantomData< System >,
_phantom_orientation : std::marker::PhantomData< Orientation >,
}
impl< System, Orientation > IntegrationField< System, Orientation >
{
pub fn new( _width : i32, _height : i32 ) -> Self
{
Self
{
max_cost : u32::MAX,
_phantom_system : std::marker::PhantomData,
_phantom_orientation : std::marker::PhantomData,
}
}
pub fn get_cost< C >( &self, _coord : &C ) -> u32
where
C : Clone,
Grid2D< System, Orientation, u32 > : std::ops::Index< C, Output = u32 >,
{
0
}
pub fn set_cost< C >( &mut self, _coord : &C, _cost : u32 )
where
C : Clone,
Grid2D< System, Orientation, u32 > : std::ops::IndexMut< C, Output = u32 >,
{
}
pub fn in_bounds< C >( &self, _coord : &C ) -> bool
where
C : Clone,
Grid2D< System, Orientation, u32 > : std::ops::Index< C, Output = u32 >,
{
true
}
}
impl< System, Orientation > FlowField< System, Orientation >
{
pub fn new( width : i32, height : i32 ) -> Self
{
let integration = IntegrationField::new( width, height );
Self
{
integration,
width,
height,
_phantom_system : std::marker::PhantomData,
_phantom_orientation : std::marker::PhantomData,
}
}
pub fn calculate_flow< C, Fa, Fc >( &mut self, goal : &C, is_passable : Fa, get_cost : Fc )
where
C : Distance + Neighbors + Clone + PartialEq + std::hash::Hash + Ord,
Fa : Fn( &C ) -> bool,
Fc : Fn( &C ) -> u32,
Grid2D< System, Orientation, u32 > : std::ops::Index< C, Output = u32 > + std::ops::IndexMut< C, Output = u32 >,
Grid2D< System, Orientation, FlowDirection > : std::ops::IndexMut< C, Output = FlowDirection >,
{
self.calculate_integration_field( goal, &is_passable, &get_cost );
self.generate_flow_directions( &is_passable );
}
pub fn get_flow_direction< C >( &self, _coord : &C ) -> Option< FlowDirection >
where
C : Clone,
Grid2D< System, Orientation, FlowDirection > : std::ops::Index< C, Output = FlowDirection >,
{
None
}
pub fn get_flow_directions_batch< C >( &self, coords : &[ C ] ) -> Vec< Option< FlowDirection > >
where
C : Clone,
Grid2D< System, Orientation, FlowDirection > : std::ops::Index< C, Output = FlowDirection >,
{
coords.iter()
.map( | coord | self.get_flow_direction( coord ) )
.collect()
}
fn calculate_integration_field< C, Fa, Fc >( &mut self, _goal : &C, _is_passable : &Fa, _get_cost : &Fc )
where
C : Distance + Neighbors + Clone + PartialEq + std::hash::Hash + Ord,
Fa : Fn( &C ) -> bool,
Fc : Fn( &C ) -> u32,
Grid2D< System, Orientation, u32 > : std::ops::Index< C, Output = u32 > + std::ops::IndexMut< C, Output = u32 >,
{
}
fn generate_flow_directions< C, Fa >( &mut self, _is_passable : &Fa )
where
C : Neighbors + Clone,
Fa : Fn( &C ) -> bool,
Grid2D< System, Orientation, u32 > : std::ops::Index< C, Output = u32 >,
Grid2D< System, Orientation, FlowDirection > : std::ops::IndexMut< C, Output = FlowDirection >,
{
}
pub fn apply_flow< C >( &self, current_pos : &C ) -> Option< C >
where
C : Neighbors + Clone,
Grid2D< System, Orientation, FlowDirection > : std::ops::Index< C, Output = FlowDirection >,
{
match self.get_flow_direction( current_pos )?
{
FlowDirection::None => None,
FlowDirection::Move( _dx, _dy ) =>
{
let neighbors = current_pos.neighbors();
neighbors.into_iter().next() }
}
}
pub fn calculate_group_flow< C >( &self, unit_positions : &[ C ] ) -> Vec< Option< C > >
where
C : Distance + Neighbors + Clone,
Grid2D< System, Orientation, FlowDirection > : std::ops::Index< C, Output = FlowDirection >,
{
unit_positions.iter()
.map( | pos |
{
self.apply_flow( pos )
})
.collect()
}
}
pub struct FlowFieldAnalyzer;
impl FlowFieldAnalyzer
{
pub fn analyze_flow< System, Orientation >
(
_field : &FlowField< System, Orientation >
) -> FlowFieldAnalysis
{
FlowFieldAnalysis
{
unreachable_positions : 0, convergence_points : 0, average_path_length : 0.0, bottleneck_positions : Vec::new(), }
}
pub fn optimize_flow< System, Orientation >
(
_field : &mut FlowField< System, Orientation >
)
{
}
}
#[ derive( Debug, Clone ) ]
pub struct FlowFieldAnalysis
{
pub unreachable_positions : u32,
pub convergence_points : u32,
pub average_path_length : f32,
pub bottleneck_positions : Vec< ( i32, i32 ) >,
}
#[ derive( Debug, Clone ) ]
pub struct MultiGoalFlowField< System, Orientation >
{
pub goal_fields : Vec< FlowField< System, Orientation > >,
#[ allow( dead_code ) ]
width : i32,
#[ allow( dead_code ) ]
height : i32,
_phantom_system : std::marker::PhantomData< System >,
_phantom_orientation : std::marker::PhantomData< Orientation >,
}
impl< System, Orientation > MultiGoalFlowField< System, Orientation >
{
pub fn new( width : i32, height : i32 ) -> Self
{
Self
{
goal_fields : Vec::new(),
width,
height,
_phantom_system : std::marker::PhantomData,
_phantom_orientation : std::marker::PhantomData,
}
}
pub fn add_goal< C, Fa, Fc >( &mut self, goal : &C, is_passable : Fa, get_cost : Fc )
where
C : Distance + Neighbors + Clone + PartialEq + std::hash::Hash + Ord,
Fa : Fn( &C ) -> bool + Clone,
Fc : Fn( &C ) -> u32 + Clone,
Grid2D< System, Orientation, u32 > : std::ops::Index< C, Output = u32 > + std::ops::IndexMut< C, Output = u32 >,
Grid2D< System, Orientation, FlowDirection > : std::ops::IndexMut< C, Output = FlowDirection >,
{
let mut goal_field = FlowField::new
(
self.width,
self.height
);
goal_field.calculate_flow( goal, is_passable, get_cost );
self.goal_fields.push( goal_field );
self.recalculate_combined_field();
}
fn recalculate_combined_field( &mut self )
{
}
pub fn get_optimal_direction< C >( &self, _pos : &C ) -> Option< FlowDirection >
where
C : Clone,
Grid2D< System, Orientation, FlowDirection > : std::ops::Index< C, Output = FlowDirection >,
{
None
}
}
#[ derive( Debug, Clone ) ]
pub struct DynamicFlowField< System, Orientation >
{
dirty_positions : std::collections::HashSet< ( i32, i32 ) >,
pub width : i32,
pub height : i32,
_phantom_system : std::marker::PhantomData< System >,
_phantom_orientation : std::marker::PhantomData< Orientation >,
}
impl< System, Orientation > DynamicFlowField< System, Orientation >
{
pub fn new( width : i32, height : i32 ) -> Self
{
Self
{
dirty_positions : std::collections::HashSet::new(),
width,
height,
_phantom_system : std::marker::PhantomData,
_phantom_orientation : std::marker::PhantomData,
}
}
pub fn mark_dirty( &mut self, pos : ( i32, i32 ) )
{
self.dirty_positions.insert( pos );
}
pub fn incremental_update< C, Fa, Fc >( &mut self, _is_passable : Fa, _get_cost : Fc )
where
C : Distance + Neighbors + Clone + PartialEq + std::hash::Hash,
Fa : Fn( &C ) -> bool,
Fc : Fn( &C ) -> u32,
Grid2D< System, Orientation, u32 > : std::ops::Index< C, Output = u32 > + std::ops::IndexMut< C, Output = u32 >,
Grid2D< System, Orientation, FlowDirection > : std::ops::IndexMut< C, Output = FlowDirection >,
{
self.dirty_positions.clear();
}
}
#[ cfg( test ) ]
mod tests
{
use super::*;
#[ test ]
fn test_flow_field_creation()
{
let flow_field = FlowField::< (), () >::new( 10, 10 );
assert_eq!( flow_field.width, 10 );
assert_eq!( flow_field.height, 10 );
}
#[ test ]
fn test_integration_field_creation()
{
let integration = IntegrationField::< (), () >::new( 5, 5 );
assert_eq!( integration.max_cost, u32::MAX );
}
#[ test ]
fn test_flow_direction_enum()
{
let dir = FlowDirection::Move( 1, 0 );
match dir
{
FlowDirection::Move( dx, dy ) =>
{
assert_eq!( dx, 1 );
assert_eq!( dy, 0 );
}
FlowDirection::None => panic!( "Expected Move direction" ),
}
}
#[ test ]
fn test_multi_goal_flow_field_creation()
{
let multi_field = MultiGoalFlowField::< (), () >::new( 8, 8 );
assert_eq!( multi_field.goal_fields.len(), 0 );
}
#[ test ]
fn test_dynamic_flow_field_dirty_marking()
{
let mut dynamic_field = DynamicFlowField::< (), () >::new( 6, 6 );
dynamic_field.mark_dirty( ( 3, 3 ) );
assert!( dynamic_field.dirty_positions.contains( &( 3, 3 ) ) );
}
}