1use crate::semantic::{MarkerType, SemanticLayers};
4use crate::spatial::{shortest_path, PathfindingConstraints};
5use crate::{Grid, Rng, Tile};
6use std::collections::{HashSet, VecDeque};
7
8#[derive(Debug, Clone, Copy)]
10pub enum MarkerConnectMethod {
11 Line,
12 Path,
13}
14
15pub fn label_regions(grid: &Grid<Tile>) -> (Vec<u32>, u32) {
17 let (w, h) = (grid.width(), grid.height());
18 let mut labels = vec![0u32; w * h];
19 let mut label = 0u32;
20
21 for y in 0..h {
22 for x in 0..w {
23 if grid[(x, y)].is_floor() && labels[y * w + x] == 0 {
24 label += 1;
25 flood_label(grid, &mut labels, x, y, label, w, h);
26 }
27 }
28 }
29 (labels, label)
30}
31
32pub fn carve_path(grid: &mut Grid<Tile>, path: &[(usize, usize)], radius: usize) {
34 if path.is_empty() {
35 return;
36 }
37
38 for &(x, y) in path {
39 carve_point(grid, x as i32, y as i32, radius);
40 }
41}
42
43pub fn clear_rect(grid: &mut Grid<Tile>, center: (usize, usize), w: usize, h: usize) {
45 if w == 0 || h == 0 {
46 return;
47 }
48
49 let x = center.0 as i32 - (w as i32 / 2);
50 let y = center.1 as i32 - (h as i32 / 2);
51 grid.fill_rect(x, y, w, h, Tile::Floor);
52}
53
54pub fn connect_markers(
56 grid: &mut Grid<Tile>,
57 layers: &SemanticLayers,
58 from: &MarkerType,
59 to: &MarkerType,
60 method: MarkerConnectMethod,
61 radius: usize,
62) -> bool {
63 let from_pos = crate::semantic::marker_positions(layers, from);
64 let to_pos = crate::semantic::marker_positions(layers, to);
65
66 let start = match from_pos.first() {
67 Some(pos) => *pos,
68 None => return false,
69 };
70 let end = match to_pos.first() {
71 Some(pos) => *pos,
72 None => return false,
73 };
74
75 match method {
76 MarkerConnectMethod::Line => {
77 let path = line_points(start, end);
78 carve_path(grid, &path, radius);
79 true
80 }
81 MarkerConnectMethod::Path => {
82 let constraints = PathfindingConstraints::default();
83 if let Some(path) = shortest_path(grid, start, end, &constraints) {
84 carve_path(grid, &path, radius);
85 true
86 } else {
87 false
88 }
89 }
90 }
91}
92
93pub fn connect_regions_spanning(
95 grid: &mut Grid<Tile>,
96 extra_connection_chance: f64,
97 rng: &mut Rng,
98) -> Vec<(usize, usize)> {
99 let (w, h) = (grid.width(), grid.height());
100 let (labels, region_count) = label_regions(grid);
101
102 if region_count <= 1 {
103 return Vec::new();
104 }
105
106 let mut regions: Vec<Vec<(usize, usize)>> = vec![Vec::new(); region_count as usize + 1];
108 for y in 0..h {
109 for x in 0..w {
110 if grid[(x, y)].is_floor() {
111 regions[labels[y * w + x] as usize].push((x, y));
112 }
113 }
114 }
115
116 let mut connectors = Vec::new();
118 for y in 1..h - 1 {
119 for x in 1..w - 1 {
120 if !grid[(x, y)].is_floor() {
121 let adjacent_regions: HashSet<u32> =
122 [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]
123 .iter()
124 .filter_map(|&(nx, ny)| {
125 if grid[(nx, ny)].is_floor() {
126 Some(labels[ny * w + nx])
127 } else {
128 None
129 }
130 })
131 .collect();
132
133 if adjacent_regions.len() >= 2 {
134 connectors.push((x, y, adjacent_regions.into_iter().collect::<Vec<_>>()));
135 }
136 }
137 }
138 }
139
140 let mut connected_regions = vec![false; region_count as usize + 1];
142 let mut connections_made = Vec::new();
143 let mut edges_used = 0;
144
145 rng.shuffle(&mut connectors);
147
148 for (x, y, adjacent) in &connectors {
149 let unconnected: Vec<u32> = adjacent
151 .iter()
152 .filter(|&&r| !connected_regions[r as usize])
153 .copied()
154 .collect();
155
156 if !unconnected.is_empty() {
157 grid.set(*x as i32, *y as i32, Tile::Floor);
159 connections_made.push((*x, *y));
160
161 for ®ion in &unconnected {
162 connected_regions[region as usize] = true;
163 }
164
165 edges_used += 1;
166 if edges_used >= region_count - 1 {
167 break;
168 }
169 } else if rng.chance(extra_connection_chance) {
170 grid.set(*x as i32, *y as i32, Tile::Floor);
172 connections_made.push((*x, *y));
173 }
174 }
175
176 connections_made
177}
178
179pub fn bridge_gaps(grid: &mut Grid<Tile>, max_distance: usize) {
180 let (w, h) = (grid.width(), grid.height());
181 let mut labels = vec![0u32; w * h];
182 let mut label = 0u32;
183 let mut regions: Vec<Vec<(usize, usize)>> = vec![vec![]];
184
185 for y in 0..h {
186 for x in 0..w {
187 if grid[(x, y)].is_floor() && labels[y * w + x] == 0 {
188 label += 1;
189 let cells = flood_label(grid, &mut labels, x, y, label, w, h);
190 regions.push(cells);
191 }
192 }
193 }
194
195 if label <= 1 {
196 return;
197 }
198
199 for r1 in 1..=label as usize {
200 for r2 in (r1 + 1)..=label as usize {
201 if let Some((x1, y1, x2, y2)) = find_closest(®ions[r1], ®ions[r2], max_distance) {
202 carve_line(grid, x1, y1, x2, y2);
203 }
204 }
205 }
206}
207
208fn flood_label(
209 grid: &Grid<Tile>,
210 labels: &mut [u32],
211 sx: usize,
212 sy: usize,
213 label: u32,
214 w: usize,
215 h: usize,
216) -> Vec<(usize, usize)> {
217 let mut stack = vec![(sx, sy)];
218 let mut cells = Vec::new();
219
220 while let Some((x, y)) = stack.pop() {
221 let idx = y * w + x;
222 if labels[idx] != 0 || !grid[(x, y)].is_floor() {
223 continue;
224 }
225 labels[idx] = label;
226 cells.push((x, y));
227
228 if x > 0 {
229 stack.push((x - 1, y));
230 }
231 if x + 1 < w {
232 stack.push((x + 1, y));
233 }
234 if y > 0 {
235 stack.push((x, y - 1));
236 }
237 if y + 1 < h {
238 stack.push((x, y + 1));
239 }
240 }
241 cells
242}
243
244fn find_closest(
245 r1: &[(usize, usize)],
246 r2: &[(usize, usize)],
247 max_dist: usize,
248) -> Option<(usize, usize, usize, usize)> {
249 let mut best = None;
250 let mut best_dist = max_dist + 1;
251
252 for &(x1, y1) in r1 {
253 for &(x2, y2) in r2 {
254 let dist = ((x1 as i32 - x2 as i32).abs() + (y1 as i32 - y2 as i32).abs()) as usize;
255 if dist < best_dist {
256 best_dist = dist;
257 best = Some((x1, y1, x2, y2));
258 }
259 }
260 }
261 best
262}
263
264fn carve_line(grid: &mut Grid<Tile>, x1: usize, y1: usize, x2: usize, y2: usize) {
265 let path = line_points((x1, y1), (x2, y2));
266 carve_path(grid, &path, 0);
267}
268
269pub fn remove_dead_ends(grid: &mut Grid<Tile>, iterations: usize) {
270 let (w, h) = (grid.width(), grid.height());
271
272 for _ in 0..iterations {
273 let mut changed = false;
274 for y in 1..h - 1 {
275 for x in 1..w - 1 {
276 if !grid[(x, y)].is_floor() {
277 continue;
278 }
279 let neighbors = [
280 grid[(x - 1, y)].is_floor(),
281 grid[(x + 1, y)].is_floor(),
282 grid[(x, y - 1)].is_floor(),
283 grid[(x, y + 1)].is_floor(),
284 ];
285 if neighbors.iter().filter(|&&b| b).count() <= 1 {
286 grid.set(x as i32, y as i32, Tile::Wall);
287 changed = true;
288 }
289 }
290 }
291 if !changed {
292 break;
293 }
294 }
295}
296
297pub fn find_chokepoints(grid: &Grid<Tile>) -> Vec<(usize, usize)> {
298 let (w, h) = (grid.width(), grid.height());
299 let mut chokepoints = Vec::new();
300
301 for y in 1..h - 1 {
302 for x in 1..w - 1 {
303 if !grid[(x, y)].is_floor() {
304 continue;
305 }
306
307 let neighbors: Vec<(usize, usize)> = [
308 (x.wrapping_sub(1), y),
309 (x + 1, y),
310 (x, y.wrapping_sub(1)),
311 (x, y + 1),
312 ]
313 .into_iter()
314 .filter(|&(nx, ny)| nx < w && ny < h && grid[(nx, ny)].is_floor())
315 .collect();
316
317 if neighbors.len() >= 2 {
318 let mut visited = vec![false; w * h];
319 visited[y * w + x] = true;
320
321 let start = neighbors[0];
322 let mut queue = VecDeque::new();
323 queue.push_back(start);
324 visited[start.1 * w + start.0] = true;
325
326 while let Some((cx, cy)) = queue.pop_front() {
327 for (nx, ny) in [
328 (cx.wrapping_sub(1), cy),
329 (cx + 1, cy),
330 (cx, cy.wrapping_sub(1)),
331 (cx, cy + 1),
332 ] {
333 if nx < w && ny < h && !visited[ny * w + nx] && grid[(nx, ny)].is_floor() {
334 visited[ny * w + nx] = true;
335 queue.push_back((nx, ny));
336 }
337 }
338 }
339
340 if neighbors
341 .iter()
342 .skip(1)
343 .any(|&(nx, ny)| !visited[ny * w + nx])
344 {
345 chokepoints.push((x, y));
346 }
347 }
348 }
349 }
350 chokepoints
351}
352
353fn carve_point(grid: &mut Grid<Tile>, x: i32, y: i32, radius: usize) {
354 if radius == 0 {
355 grid.set(x, y, Tile::Floor);
356 return;
357 }
358
359 let r = radius as i32;
360 let r2 = r * r;
361 for dy in -r..=r {
362 for dx in -r..=r {
363 if dx * dx + dy * dy <= r2 {
364 grid.set(x + dx, y + dy, Tile::Floor);
365 }
366 }
367 }
368}
369
370fn line_points(start: (usize, usize), end: (usize, usize)) -> Vec<(usize, usize)> {
371 let (mut x, mut y) = (start.0 as i32, start.1 as i32);
372 let (tx, ty) = (end.0 as i32, end.1 as i32);
373 let mut points = Vec::new();
374
375 while x != tx || y != ty {
376 if x >= 0 && y >= 0 {
377 points.push((x as usize, y as usize));
378 }
379 if (x - tx).abs() > (y - ty).abs() {
380 x += if tx > x { 1 } else { -1 };
381 } else {
382 y += if ty > y { 1 } else { -1 };
383 }
384 }
385 if tx >= 0 && ty >= 0 {
386 points.push((tx as usize, ty as usize));
387 }
388
389 points
390}