bevy_flowfield_tiles_plugin/
bundle.rs

1//! Defines a bundle which can be spawned as/inserted into an entity which
2//! movable actors can query for pathing data
3//!
4
5use crate::prelude::*;
6use bevy::prelude::*;
7
8/// Defines all required components for generating [FlowField] Tiles
9#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
10#[derive(Bundle)]
11pub struct FlowFieldTilesBundle {
12	/// [CostField]s of all sectors
13	pub sector_cost_fields: SectorCostFields,
14	/// Portals for all sectors
15	pub sector_portals: SectorPortals,
16	/// Graph describing how to get from one sector to another
17	pub portal_graph: PortalGraph,
18	/// Size of the world
19	pub map_dimensions: MapDimensions,
20	/// Cache of overarching portal-portal routes
21	pub route_cache: RouteCache,
22	/// Cache of [FlowField]s that can be queried in a steering pipeline
23	pub flow_field_cache: FlowFieldCache,
24}
25
26impl FlowFieldTilesBundle {
27	/// Get a reference to the [SectorCostFields]
28	pub fn get_sector_cost_fields(&self) -> &SectorCostFields {
29		&self.sector_cost_fields
30	}
31	/// Get a reference to the [SectorPortals]
32	pub fn get_sector_portals(&self) -> &SectorPortals {
33		&self.sector_portals
34	}
35	/// Get a reference to the [PortalGraph]
36	pub fn get_portal_graph(&self) -> &PortalGraph {
37		&self.portal_graph
38	}
39	/// Get a reference to the [MapDimensions]
40	pub fn get_map_dimensions(&self) -> &MapDimensions {
41		&self.map_dimensions
42	}
43	/// Get a reference to the [RouteCache]
44	pub fn get_route_cache(&self) -> &RouteCache {
45		&self.route_cache
46	}
47	/// Get a mutable reference to the [RouteCache]
48	pub fn get_route_cache_mut(&mut self) -> &mut RouteCache {
49		&mut self.route_cache
50	}
51	/// Get a reference to the [FlowFieldCache]
52	pub fn get_flowfield_cache(&self) -> &FlowFieldCache {
53		&self.flow_field_cache
54	}
55	/// Get a mutable reference to the [FlowFieldCache]
56	pub fn get_flowfield_cache_mut(&mut self) -> &mut FlowFieldCache {
57		&mut self.flow_field_cache
58	}
59	/// Create a new instance of [FlowFieldTilesBundle] based on map dimensions
60	pub fn new(map_length: u32, map_depth: u32, sector_resolution: u32, actor_size: f32) -> Self {
61		let map_dimensions =
62			MapDimensions::new(map_length, map_depth, sector_resolution, actor_size);
63		let cost_fields = SectorCostFields::new(&map_dimensions);
64		let mut portals = SectorPortals::new(map_length, map_depth, sector_resolution);
65		// update default portals for cost fields
66		for sector_id in cost_fields.get_scaled().keys() {
67			portals.update_portals(*sector_id, &cost_fields, &map_dimensions);
68		}
69		let graph = PortalGraph::new(&portals, &cost_fields, &map_dimensions);
70		let route_cache = RouteCache::default();
71		let cache = FlowFieldCache::default();
72		FlowFieldTilesBundle {
73			sector_cost_fields: cost_fields,
74			sector_portals: portals,
75			portal_graph: graph,
76			map_dimensions,
77			route_cache,
78			flow_field_cache: cache,
79		}
80	}
81	/// Create a new instance of [FlowFieldTilesBundle] based on map dimensions where the [SectorCostFields] are derived from a `.ron` file
82	#[cfg(feature = "ron")]
83	pub fn from_ron(
84		map_length: u32,
85		map_depth: u32,
86		sector_resolution: u32,
87		actor_size: f32,
88		path: &str,
89	) -> Self {
90		let map_dimensions =
91			MapDimensions::new(map_length, map_depth, sector_resolution, actor_size);
92		let cost_fields = SectorCostFields::from_ron(path.to_string(), &map_dimensions);
93		if ((map_length * map_depth) / (sector_resolution * sector_resolution)) as usize
94			!= cost_fields.get_baseline().len()
95		{
96			panic!("Map size ({}, {}) with resolution {} produces ({}x{}) sectors. Ron file only produces {} sectors", map_length, map_depth, sector_resolution, map_length/sector_resolution, map_depth/sector_resolution, cost_fields.get_baseline().len());
97		}
98		let mut portals = SectorPortals::new(map_length, map_depth, sector_resolution);
99		// update default portals for cost fields
100		for sector_id in cost_fields.get_scaled().keys() {
101			portals.update_portals(*sector_id, &cost_fields, &map_dimensions);
102		}
103		let graph = PortalGraph::new(&portals, &cost_fields, &map_dimensions);
104		let route_cache = RouteCache::default();
105		let cache = FlowFieldCache::default();
106		FlowFieldTilesBundle {
107			sector_cost_fields: cost_fields,
108			sector_portals: portals,
109			portal_graph: graph,
110			map_dimensions,
111			route_cache,
112			flow_field_cache: cache,
113		}
114	}
115	/// Create a new instance of [FlowFieldTilesBundle] from a directory containing CSV [CostField] files
116	#[cfg(not(tarpaulin_include))]
117	#[cfg(feature = "csv")]
118	pub fn from_csv(
119		map_length: u32,
120		map_depth: u32,
121		sector_resolution: u32,
122		actor_size: f32,
123		directory: &str,
124	) -> Self {
125		let map_dimensions =
126			MapDimensions::new(map_length, map_depth, sector_resolution, actor_size);
127		let cost_fields = SectorCostFields::from_csv_dir(&map_dimensions, directory.to_string());
128		let mut portals = SectorPortals::new(map_length, map_depth, sector_resolution);
129		// update default portals for cost fields
130		for sector_id in cost_fields.get_scaled().keys() {
131			portals.update_portals(*sector_id, &cost_fields, &map_dimensions);
132		}
133		let graph = PortalGraph::new(&portals, &cost_fields, &map_dimensions);
134		let route_cache = RouteCache::default();
135		let cache = FlowFieldCache::default();
136		FlowFieldTilesBundle {
137			sector_cost_fields: cost_fields,
138			sector_portals: portals,
139			portal_graph: graph,
140			map_dimensions,
141			route_cache,
142			flow_field_cache: cache,
143		}
144	}
145	/// From a greyscale heightmap image initialise a bundle where the
146	/// [CostField]s are derived from the pixel values of the image
147	#[cfg(not(tarpaulin_include))]
148	#[cfg(feature = "heightmap")]
149	pub fn from_heightmap(
150		map_length: u32,
151		map_depth: u32,
152		sector_resolution: u32,
153		actor_size: f32,
154		file_path: &str,
155	) -> Self {
156		let map_dimensions =
157			MapDimensions::new(map_length, map_depth, sector_resolution, actor_size);
158		let cost_fields = SectorCostFields::from_heightmap(&map_dimensions, file_path.to_string());
159		let mut portals = SectorPortals::new(map_length, map_depth, sector_resolution);
160		// update default portals for cost fields
161		for sector_id in cost_fields.get_scaled().keys() {
162			portals.update_portals(*sector_id, &cost_fields, &map_dimensions);
163		}
164		let graph = PortalGraph::new(&portals, &cost_fields, &map_dimensions);
165		let route_cache = RouteCache::default();
166		let cache = FlowFieldCache::default();
167		FlowFieldTilesBundle {
168			sector_cost_fields: cost_fields,
169			sector_portals: portals,
170			portal_graph: graph,
171			map_dimensions,
172			route_cache,
173			flow_field_cache: cache,
174		}
175	}
176	/// From a list of 2d meshes and their translation initialise a bundle. The vertex points of the meshes must be within the `map_length` and `map_depth` of the world.
177	///
178	/// The default cell Costs can be set with `internal_cost` and
179	/// `external_cost` for cells within any meshs and for cells outside of any
180	/// meshes
181	#[cfg(not(tarpaulin_include))]
182	#[cfg(feature = "2d")]
183	pub fn from_bevy_2d_meshes(
184		meshes: Vec<(&Mesh, Vec2)>,
185		map_length: u32,
186		map_depth: u32,
187		sector_resolution: u32,
188		actor_size: f32,
189		internal_cost: u8,
190		external_cost: u8,
191	) -> Self {
192		let map_dimensions =
193			MapDimensions::new(map_length, map_depth, sector_resolution, actor_size);
194		let cost_fields = SectorCostFields::from_bevy_2d_meshes(
195			&map_dimensions,
196			&meshes,
197			internal_cost,
198			external_cost,
199		);
200		let mut portals = SectorPortals::new(
201			map_dimensions.get_length(),
202			map_dimensions.get_depth(),
203			sector_resolution,
204		);
205		// update default portals for cost fields
206		for sector_id in cost_fields.get_scaled().keys() {
207			portals.update_portals(*sector_id, &cost_fields, &map_dimensions);
208		}
209		let graph = PortalGraph::new(&portals, &cost_fields, &map_dimensions);
210		let route_cache = RouteCache::default();
211		let cache = FlowFieldCache::default();
212		FlowFieldTilesBundle {
213			sector_cost_fields: cost_fields,
214			sector_portals: portals,
215			portal_graph: graph,
216			map_dimensions,
217			route_cache,
218			flow_field_cache: cache,
219		}
220	}
221}
222
223// #[rustfmt::skip]
224#[cfg(test)]
225mod tests {
226	use super::*;
227	#[test]
228	fn valid_map_dimensions() {
229		let _map_dimsions = MapDimensions::new(10, 10, 10, 0.5);
230	}
231	#[test]
232	#[should_panic]
233	fn invalid_map_dimensions() {
234		MapDimensions::new(99, 3, 10, 1.0);
235	}
236	#[test]
237	fn new_bundle() {
238		let b = FlowFieldTilesBundle::new(30, 30, 10, 0.5);
239		let _ = b.get_sector_cost_fields();
240		let _ = b.get_sector_portals();
241		let _ = b.get_portal_graph();
242		let _ = b.get_map_dimensions();
243		let _ = b.get_route_cache();
244		let _ = b.get_flowfield_cache();
245		let mut b = FlowFieldTilesBundle::new(30, 30, 10, 0.5);
246		let _ = b.get_route_cache_mut();
247		let _ = b.get_flowfield_cache_mut();
248	}
249	#[test]
250	fn new_bundle_from_ron() {
251		let path = env!("CARGO_MANIFEST_DIR").to_string()
252			+ "/assets/sector_cost_fields_continuous_layout.ron";
253		let _ = FlowFieldTilesBundle::from_ron(30, 30, 10, 0.5, &path);
254	}
255}