spatial_workflow/
spatial_workflow.rs1use terrain_forge::{
6 algorithms,
7 spatial::{
8 dijkstra_map, distance_field, flow_field_from_dijkstra, morphological_transform,
9 DistanceMetric, MorphologyOp, PathfindingConstraints, StructuringElement,
10 },
11 Grid, Tile,
12};
13
14fn main() {
15 println!("=== Complete Spatial Analysis Workflow ===\n");
16
17 println!("1. Generating Base Dungeon:");
19 let mut grid = Grid::new(40, 30);
20 let algo = algorithms::get("bsp").unwrap();
21 algo.generate(&mut grid, 42424);
22
23 let original_floors = grid.count(|t| t.is_floor());
24 println!(
25 " Generated {}x{} dungeon with {} floor tiles",
26 grid.width(),
27 grid.height(),
28 original_floors
29 );
30
31 println!("\n2. Cleaning Up Small Features:");
33 let cleanup_element = StructuringElement::rectangle(3, 3);
34 let cleaned = morphological_transform(&grid, MorphologyOp::Opening, &cleanup_element);
35
36 let cleaned_floors = cleaned.count(|t| t.is_floor());
37 println!(
38 " Removed {} small features ({} floors remaining)",
39 original_floors - cleaned_floors,
40 cleaned_floors
41 );
42
43 println!("\n3. Analyzing Distance from Walls:");
45 let distance_map = distance_field(&cleaned, DistanceMetric::Euclidean);
46
47 let mut max_distance = 0.0;
48 let mut center_points = Vec::new();
49
50 for y in 0..distance_map.height() {
51 for x in 0..distance_map.width() {
52 let dist = distance_map.get(x, y);
53 if dist != f32::INFINITY {
54 if dist > max_distance {
55 max_distance = dist;
56 center_points.clear();
57 center_points.push((x, y));
58 } else if (dist - max_distance).abs() < 0.1 {
59 center_points.push((x, y));
60 }
61 }
62 }
63 }
64
65 println!(" Maximum distance from walls: {:.1}", max_distance);
66 println!(" Found {} center points", center_points.len());
67
68 println!("\n4. Creating Strategic Pathfinding Network:");
70
71 let strategic_points: Vec<_> = center_points.into_iter().take(3).collect();
73 println!(" Strategic points: {:?}", strategic_points);
74
75 let constraints = PathfindingConstraints::default();
76 let strategic_dijkstra = dijkstra_map(&cleaned, &strategic_points, &constraints);
77
78 println!("\n5. Generating AI Movement Flow Field:");
80 let flow_field = flow_field_from_dijkstra(&strategic_dijkstra);
81
82 let mut flow_coverage = 0;
84 for y in 0..flow_field.height() {
85 for x in 0..flow_field.width() {
86 let (dx, dy) = flow_field.get_direction(x, y);
87 if dx != 0 || dy != 0 {
88 flow_coverage += 1;
89 }
90 }
91 }
92
93 println!(" Flow field covers {} cells", flow_coverage);
94
95 println!("\n6. Identifying Chokepoints:");
97 let thin_element = StructuringElement::rectangle(2, 2);
98 let thinned = morphological_transform(&cleaned, MorphologyOp::Erosion, &thin_element);
99
100 let mut chokepoints = Vec::new();
101 for y in 1..cleaned.height() - 1 {
102 for x in 1..cleaned.width() - 1 {
103 if let (Some(original), Some(thinned_cell)) = (
104 cleaned.get(x as i32, y as i32),
105 thinned.get(x as i32, y as i32),
106 ) {
107 if original.is_floor() && !thinned_cell.is_floor() {
108 let neighbors = count_floor_neighbors(&cleaned, x, y);
110 if (2..=4).contains(&neighbors) {
111 chokepoints.push((x, y));
112 }
113 }
114 }
115 }
116 }
117
118 println!(" Found {} potential chokepoints", chokepoints.len());
119
120 println!("\n7. Performance Summary:");
122
123 let start = std::time::Instant::now();
124 let _ = morphological_transform(&grid, MorphologyOp::Opening, &cleanup_element);
125 println!(" Morphological cleanup: {:?}", start.elapsed());
126
127 let start = std::time::Instant::now();
128 let _ = distance_field(&cleaned, DistanceMetric::Euclidean);
129 println!(" Distance field calculation: {:?}", start.elapsed());
130
131 let start = std::time::Instant::now();
132 let _ = dijkstra_map(&cleaned, &strategic_points, &constraints);
133 println!(" Dijkstra map generation: {:?}", start.elapsed());
134
135 let start = std::time::Instant::now();
136 let _ = flow_field_from_dijkstra(&strategic_dijkstra);
137 println!(" Flow field generation: {:?}", start.elapsed());
138
139 println!("\n8. Spatial Analysis Results:");
141 println!(" Original dungeon: {} floors", original_floors);
142 println!(" After cleanup: {} floors", cleaned_floors);
143 println!(" Strategic locations: {}", strategic_points.len());
144 println!(" Chokepoints identified: {}", chokepoints.len());
145 println!(
146 " Flow field coverage: {:.1}%",
147 100.0 * flow_coverage as f32 / cleaned_floors as f32
148 );
149
150 println!("\n✅ Spatial analysis workflow complete!");
151 println!(" The dungeon is now ready for:");
152 println!(" - AI pathfinding using flow fields");
153 println!(" - Strategic placement at center points");
154 println!(" - Tactical analysis of chokepoints");
155 println!(" - Distance-based gameplay mechanics");
156}
157
158fn count_floor_neighbors(grid: &Grid<Tile>, x: usize, y: usize) -> usize {
159 let mut count = 0;
160 for dy in -1..=1 {
161 for dx in -1..=1 {
162 if dx == 0 && dy == 0 {
163 continue;
164 }
165 if let Some(tile) = grid.get(x as i32 + dx, y as i32 + dy) {
166 if tile.is_floor() {
167 count += 1;
168 }
169 }
170 }
171 }
172 count
173}