1use terrain_forge::{
6 algorithms::Bsp,
7 analysis::{DelaunayTriangulation, Graph, GraphAnalysis, Point},
8 Algorithm, Grid, Tile,
9};
10
11fn main() {
12 println!("=== Delaunay Triangulation Demo ===\n");
13
14 println!("1. Generating Base Dungeon:");
16 let mut grid = Grid::new(40, 30);
17 let bsp = Bsp::default();
18 bsp.generate(&mut grid, 42424);
19
20 let room_count = count_rooms(&grid);
21 println!(
22 " Generated {}x{} dungeon with ~{} rooms",
23 grid.width(),
24 grid.height(),
25 room_count
26 );
27
28 println!("\n2. Identifying Room Centers:");
30 let room_centers = find_room_centers(&grid);
31 println!(" Found {} room centers:", room_centers.len());
32 for (i, center) in room_centers.iter().enumerate() {
33 println!(" Room {}: ({:.1}, {:.1})", i + 1, center.x, center.y);
34 }
35
36 println!("\n3. Creating Delaunay Triangulation:");
38 let triangulation = DelaunayTriangulation::new(room_centers.clone());
39
40 println!(" Triangulation results:");
41 println!(" Vertices: {}", triangulation.points.len());
42 println!(" Triangles: {}", triangulation.triangles.len());
43 println!(" Edges: {}", triangulation.edges.len());
44
45 println!("\n4. Generating Minimum Spanning Tree:");
47 let mst_edges = triangulation.minimum_spanning_tree();
48
49 println!(" MST results:");
50 println!(" Edges: {}", mst_edges.len());
51 println!(
52 " Expected for {} rooms: {}",
53 room_centers.len(),
54 room_centers.len().saturating_sub(1)
55 );
56
57 let total_length: f32 = mst_edges
58 .iter()
59 .map(|edge| edge.length(&triangulation.points))
60 .sum();
61 println!(" Total length: {:.1}", total_length);
62
63 println!("\n5. Graph Analysis:");
65 let graph = Graph::new(triangulation.points.clone(), mst_edges.clone());
66 let analysis = GraphAnalysis::analyze(&graph);
67
68 println!(" Connectivity analysis:");
69 println!(" Connected: {}", analysis.is_connected);
70 println!(" Components: {}", analysis.component_count);
71 println!(" Diameter: {:.1}", analysis.diameter);
72 println!(" Avg clustering: {:.3}", analysis.average_clustering);
73
74 println!("\n6. Comparison: MST vs Full Triangulation:");
76 let full_graph = Graph::new(triangulation.points.clone(), triangulation.edges.clone());
77 let full_analysis = GraphAnalysis::analyze(&full_graph);
78
79 println!(" Full triangulation:");
80 println!(" Edges: {}", full_analysis.edge_count);
81 println!(" Diameter: {:.1}", full_analysis.diameter);
82 println!(
83 " Avg clustering: {:.3}",
84 full_analysis.average_clustering
85 );
86
87 println!(" MST (optimized):");
88 println!(" Edges: {}", analysis.edge_count);
89 println!(" Diameter: {:.1}", analysis.diameter);
90 println!(" Avg clustering: {:.3}", analysis.average_clustering);
91
92 println!("\n7. Pathfinding Example:");
94 if room_centers.len() >= 2 {
95 let start = 0;
96 let end = room_centers.len() - 1;
97
98 if let Some(path) = graph.shortest_path(start, end) {
99 println!(" Path from room {} to room {}:", start + 1, end + 1);
100 print!(" Route: ");
101 for (i, &room) in path.iter().enumerate() {
102 if i > 0 {
103 print!(" -> ");
104 }
105 print!("Room {}", room + 1);
106 }
107 println!();
108
109 let mut path_length = 0.0;
110 for i in 0..(path.len() - 1) {
111 path_length += room_centers[path[i]].distance_to(&room_centers[path[i + 1]]);
112 }
113 println!(" Total distance: {:.1}", path_length);
114 } else {
115 println!(" No path found between rooms");
116 }
117 }
118
119 println!("\n8. Performance Analysis:");
121 let start = std::time::Instant::now();
122 let _ = DelaunayTriangulation::new(room_centers.clone());
123 println!(" Triangulation time: {:?}", start.elapsed());
124
125 let start = std::time::Instant::now();
126 let _ = triangulation.minimum_spanning_tree();
127 println!(" MST generation time: {:?}", start.elapsed());
128
129 println!("\n✅ Delaunay triangulation demo complete!");
130 println!(" - Natural room connections via triangulation");
131 println!(" - Optimal corridor networks with MST");
132 println!(" - Graph analysis for connectivity insights");
133}
134
135fn count_rooms(grid: &Grid<Tile>) -> usize {
136 let mut room_count = 0;
138 let mut visited = vec![vec![false; grid.width()]; grid.height()];
139
140 for y in 0..grid.height() {
141 for x in 0..grid.width() {
142 if !visited[y][x] {
143 if let Some(tile) = grid.get(x as i32, y as i32) {
144 if tile.is_floor() {
145 flood_fill(grid, &mut visited, x, y);
146 room_count += 1;
147 }
148 }
149 }
150 }
151 }
152
153 room_count
154}
155
156fn flood_fill(grid: &Grid<Tile>, visited: &mut [Vec<bool>], start_x: usize, start_y: usize) {
157 let mut stack = vec![(start_x, start_y)];
158
159 while let Some((x, y)) = stack.pop() {
160 if visited[y][x] {
161 continue;
162 }
163 visited[y][x] = true;
164
165 for (dx, dy) in [(-1, 0), (1, 0), (0, -1), (0, 1)] {
166 let nx = x as i32 + dx;
167 let ny = y as i32 + dy;
168
169 if nx >= 0 && ny >= 0 && (nx as usize) < grid.width() && (ny as usize) < grid.height() {
170 let nx = nx as usize;
171 let ny = ny as usize;
172
173 if !visited[ny][nx] {
174 if let Some(tile) = grid.get(nx as i32, ny as i32) {
175 if tile.is_floor() {
176 stack.push((nx, ny));
177 }
178 }
179 }
180 }
181 }
182 }
183}
184
185fn find_room_centers(grid: &Grid<Tile>) -> Vec<Point> {
186 let mut centers = Vec::new();
187 let mut visited = vec![vec![false; grid.width()]; grid.height()];
188
189 for y in 0..grid.height() {
190 for x in 0..grid.width() {
191 if !visited[y][x] {
192 if let Some(tile) = grid.get(x as i32, y as i32) {
193 if tile.is_floor() {
194 let center = find_room_center(grid, &mut visited, x, y);
195 centers.push(center);
196 }
197 }
198 }
199 }
200 }
201
202 centers
203}
204
205fn find_room_center(
206 grid: &Grid<Tile>,
207 visited: &mut [Vec<bool>],
208 start_x: usize,
209 start_y: usize,
210) -> Point {
211 let mut room_cells = Vec::new();
212 let mut stack = vec![(start_x, start_y)];
213
214 while let Some((x, y)) = stack.pop() {
215 if visited[y][x] {
216 continue;
217 }
218 visited[y][x] = true;
219 room_cells.push((x, y));
220
221 for (dx, dy) in [(-1, 0), (1, 0), (0, -1), (0, 1)] {
222 let nx = x as i32 + dx;
223 let ny = y as i32 + dy;
224
225 if nx >= 0 && ny >= 0 && (nx as usize) < grid.width() && (ny as usize) < grid.height() {
226 let nx = nx as usize;
227 let ny = ny as usize;
228
229 if !visited[ny][nx] {
230 if let Some(tile) = grid.get(nx as i32, ny as i32) {
231 if tile.is_floor() {
232 stack.push((nx, ny));
233 }
234 }
235 }
236 }
237 }
238 }
239
240 let sum_x: usize = room_cells.iter().map(|(x, _)| x).sum();
242 let sum_y: usize = room_cells.iter().map(|(_, y)| y).sum();
243 let count = room_cells.len();
244
245 Point::new(sum_x as f32 / count as f32, sum_y as f32 / count as f32)
246}