1use crate::core::IgraphResult;
10
11pub fn layout_merge_dla(layouts: &[&[[f64; 2]]]) -> IgraphResult<Vec<[f64; 2]>> {
37 let n_components = layouts.len();
38
39 if n_components == 0 {
40 return Ok(Vec::new());
41 }
42 if n_components == 1 {
43 return Ok(layouts[0].to_vec());
44 }
45
46 let mut centers_x = vec![0.0_f64; n_components];
48 let mut centers_y = vec![0.0_f64; n_components];
49 let mut radii = vec![0.0_f64; n_components];
50 let mut placement_radii = vec![0.0_f64; n_components];
51 let mut total_nodes = 0usize;
52
53 let mut area = 0.0_f64;
54 for (i, layout) in layouts.iter().enumerate() {
55 let size = layout.len();
56 total_nodes += size;
57
58 if size == 0 {
59 continue;
60 }
61
62 let (cx, cy, r) = bounding_circle(layout);
64 centers_x[i] = cx;
65 centers_y[i] = cy;
66 radii[i] = r;
67
68 placement_radii[i] = (size as f64).powf(0.75);
70 area += placement_radii[i] * placement_radii[i];
71 }
72
73 let mut order: Vec<usize> = (0..n_components).collect();
75 order.sort_unstable_by(|&a, &b| layouts[b].len().cmp(&layouts[a].len()));
76
77 let grid_extent = (5.0 * area).sqrt();
79 let mut grid = MergeGrid::new(
80 -grid_extent,
81 grid_extent,
82 200,
83 -grid_extent,
84 grid_extent,
85 200,
86 );
87
88 let mut placed_x = vec![0.0_f64; n_components];
90 let mut placed_y = vec![0.0_f64; n_components];
91
92 let first = order[0];
94 placed_x[first] = 0.0;
95 placed_y[first] = 0.0;
96 grid.place_sphere(0.0, 0.0, placement_radii[first], first);
97
98 let mut rng = SplitMix64::new(42);
99 let startr = grid_extent;
100 let killr = grid_extent + 5.0;
101
102 for &comp_idx in order.iter().skip(1) {
104 let r = placement_radii[comp_idx];
105 if r <= 0.0 {
106 placed_x[comp_idx] = 0.0;
107 placed_y[comp_idx] = 0.0;
108 continue;
109 }
110
111 let (px, py) = dla_walk(&grid, r, 0.0, 0.0, startr, killr, &mut rng);
112 placed_x[comp_idx] = px;
113 placed_y[comp_idx] = py;
114 grid.place_sphere(px, py, r, comp_idx);
115 }
116
117 let mut result = Vec::with_capacity(total_nodes);
119 for (i, layout) in layouts.iter().enumerate() {
120 let ox = placed_x[i];
121 let oy = placed_y[i];
122 let nr = radii[i];
123 let scale = if nr > 0.0 {
124 placement_radii[i] / nr
125 } else {
126 1.0
127 };
128 let cx = centers_x[i];
129 let cy = centers_y[i];
130
131 for p in *layout {
132 result.push([scale * (p[0] - cx) + ox, scale * (p[1] - cy) + oy]);
133 }
134 }
135
136 Ok(result)
137}
138
139fn bounding_circle(points: &[[f64; 2]]) -> (f64, f64, f64) {
140 if points.is_empty() {
141 return (0.0, 0.0, 0.0);
142 }
143 if points.len() == 1 {
144 return (points[0][0], points[0][1], 0.0);
145 }
146
147 let mut xmin = points[0][0];
148 let mut xmax = points[0][0];
149 let mut ymin = points[0][1];
150 let mut ymax = points[0][1];
151
152 for p in points.iter().skip(1) {
153 if p[0] < xmin {
154 xmin = p[0];
155 }
156 if p[0] > xmax {
157 xmax = p[0];
158 }
159 if p[1] < ymin {
160 ymin = p[1];
161 }
162 if p[1] > ymax {
163 ymax = p[1];
164 }
165 }
166
167 let cx = (xmin + xmax) / 2.0;
168 let cy = (ymin + ymax) / 2.0;
169 let dx = xmax - xmin;
170 let dy = ymax - ymin;
171 let r = (dx * dx + dy * dy).sqrt() / 2.0;
172
173 (cx, cy, r)
174}
175
176fn dla_walk(
177 grid: &MergeGrid,
178 r: f64,
179 cx: f64,
180 cy: f64,
181 startr: f64,
182 killr: f64,
183 rng: &mut SplitMix64,
184) -> (f64, f64) {
185 let max_attempts = 100_000;
186
187 for _ in 0..max_attempts {
188 let angle = rng.next_uniform() * std::f64::consts::TAU;
190 let len = rng.next_uniform() * 0.5 * startr + 0.5 * startr;
191 let mut x = cx + len * angle.cos();
192 let mut y = cy + len * angle.sin();
193
194 if grid.get_sphere(x, y, r) >= 0 {
196 continue;
197 }
198
199 let step_size = startr / 100.0;
201 loop {
202 let dist = ((x - cx) * (x - cx) + (y - cy) * (y - cy)).sqrt();
203 if dist >= killr {
204 break;
205 }
206
207 let a = rng.next_uniform() * std::f64::consts::TAU;
208 let l = rng.next_uniform() * step_size;
209 let nx = x + l * a.cos();
210 let ny = y + l * a.sin();
211
212 if grid.get_sphere(nx, ny, r) >= 0 {
213 return (x, y);
214 }
215 x = nx;
216 y = ny;
217 }
218 }
219
220 let angle = rng.next_uniform() * std::f64::consts::TAU;
222 (cx + startr * angle.cos(), cy + startr * angle.sin())
223}
224
225struct MergeGrid {
230 minx: f64,
231 maxx: f64,
232 miny: f64,
233 maxy: f64,
234 stepsx: usize,
235 stepsy: usize,
236 deltax: f64,
237 deltay: f64,
238 data: Vec<i32>, }
240
241impl MergeGrid {
242 fn new(minx: f64, maxx: f64, stepsx: usize, miny: f64, maxy: f64, stepsy: usize) -> Self {
243 Self {
244 minx,
245 maxx,
246 miny,
247 maxy,
248 stepsx,
249 stepsy,
250 deltax: (maxx - minx) / stepsx as f64,
251 deltay: (maxy - miny) / stepsy as f64,
252 data: vec![0; stepsx * stepsy],
253 }
254 }
255
256 fn which(&self, xc: f64, yc: f64) -> (usize, usize) {
257 let cx = if xc <= self.minx {
258 0
259 } else if xc >= self.maxx {
260 self.stepsx - 1
261 } else {
262 ((xc - self.minx) / self.deltax) as usize
263 };
264
265 let cy = if yc <= self.miny {
266 0
267 } else if yc >= self.maxy {
268 self.stepsy - 1
269 } else {
270 ((yc - self.miny) / self.deltay) as usize
271 };
272
273 (cx.min(self.stepsx - 1), cy.min(self.stepsy - 1))
274 }
275
276 fn place_sphere(&mut self, x: f64, y: f64, r: f64, id: usize) {
277 let (cx, cy) = self.which(x, y);
278 let val = (id as i32) + 1;
279
280 self.data[cy * self.stepsx + cx] = val;
281
282 let mut i = 0i32;
285 while (cx as i32 + i) < self.stepsx as i32 {
286 let gx = self.minx + (cx as f64 + i as f64) * self.deltax;
287 if Self::grid_dist(x, y, gx, y) >= r {
288 break;
289 }
290 let mut j = 0i32;
291 while (cy as i32 + j) < self.stepsy as i32 {
292 let gy = self.miny + (cy as f64 + j as f64) * self.deltay;
293 if Self::grid_dist(x, y, gx, gy) >= r {
294 break;
295 }
296 let idx = (cy as i32 + j) as usize * self.stepsx + (cx as i32 + i) as usize;
297 self.data[idx] = val;
298 j += 1;
299 }
300 i += 1;
301 }
302
303 i = 0;
305 while (cx as i32 + i) < self.stepsx as i32 {
306 let gx = self.minx + (cx as f64 + i as f64) * self.deltax;
307 if Self::grid_dist(x, y, gx, y) >= r {
308 break;
309 }
310 let mut j = 1i32;
311 while (cy as i32 - j) > 0 {
312 let gy = self.miny + (cy as f64 - j as f64 + 1.0) * self.deltay;
313 if Self::grid_dist(x, y, gx, gy) >= r {
314 break;
315 }
316 let idx = (cy as i32 - j) as usize * self.stepsx + (cx as i32 + i) as usize;
317 self.data[idx] = val;
318 j += 1;
319 }
320 i += 1;
321 }
322
323 i = 1;
325 while (cx as i32 - i) > 0 {
326 let gx = self.minx + (cx as f64 - i as f64 + 1.0) * self.deltax;
327 if Self::grid_dist(x, y, gx, y) >= r {
328 break;
329 }
330 let mut j = 0i32;
331 while (cy as i32 + j) < self.stepsy as i32 {
332 let gy = self.miny + (cy as f64 + j as f64) * self.deltay;
333 if Self::grid_dist(x, y, gx, gy) >= r {
334 break;
335 }
336 let idx = (cy as i32 + j) as usize * self.stepsx + (cx as i32 - i) as usize;
337 self.data[idx] = val;
338 j += 1;
339 }
340 i += 1;
341 }
342
343 i = 1;
345 while (cx as i32 - i) > 0 {
346 let gx = self.minx + (cx as f64 - i as f64 + 1.0) * self.deltax;
347 if Self::grid_dist(x, y, gx, y) >= r {
348 break;
349 }
350 let mut j = 1i32;
351 while (cy as i32 - j) > 0 {
352 let gy = self.miny + (cy as f64 - j as f64 + 1.0) * self.deltay;
353 if Self::grid_dist(x, y, gx, gy) >= r {
354 break;
355 }
356 let idx = (cy as i32 - j) as usize * self.stepsx + (cx as i32 - i) as usize;
357 self.data[idx] = val;
358 j += 1;
359 }
360 i += 1;
361 }
362 }
363
364 fn get_sphere(&self, x: f64, y: f64, r: f64) -> i32 {
365 if x - r <= self.minx || x + r >= self.maxx || y - r <= self.miny || y + r >= self.maxy {
366 return -1;
367 }
368
369 let (cx, cy) = self.which(x, y);
370 let ret = self.data[cy * self.stepsx + cx] - 1;
371 if ret >= 0 {
372 return ret;
373 }
374
375 if let Some(id) = self.check_quadrant(x, y, r, cx, cy, 1, 1) {
377 return id;
378 }
379 if let Some(id) = self.check_quadrant(x, y, r, cx, cy, 1, -1) {
380 return id;
381 }
382 if let Some(id) = self.check_quadrant(x, y, r, cx, cy, -1, 1) {
383 return id;
384 }
385 if let Some(id) = self.check_quadrant(x, y, r, cx, cy, -1, -1) {
386 return id;
387 }
388
389 -1
390 }
391
392 fn check_quadrant(
393 &self,
394 x: f64,
395 y: f64,
396 r: f64,
397 cx: usize,
398 cy: usize,
399 dx: i32,
400 dy: i32,
401 ) -> Option<i32> {
402 let i_start: i32 = i32::from(dx <= 0);
403 let j_start: i32 = i32::from(dy <= 0);
404
405 let mut i = i_start;
406 loop {
407 let gxi = cx as i32 + i * dx;
408 if gxi < 0 || gxi >= self.stepsx as i32 {
409 break;
410 }
411 let gx = self.minx + gxi as f64 * self.deltax;
412 if Self::grid_dist(x, y, gx, y) >= r {
413 break;
414 }
415
416 let mut j = j_start;
417 loop {
418 let gyj = cy as i32 + j * dy;
419 if gyj < 0 || gyj >= self.stepsy as i32 {
420 break;
421 }
422 let gy = self.miny + gyj as f64 * self.deltay;
423 if Self::grid_dist(x, y, gx, gy) >= r {
424 break;
425 }
426
427 let idx = gyj as usize * self.stepsx + gxi as usize;
428 let val = self.data[idx] - 1;
429 if val >= 0 {
430 return Some(val);
431 }
432 j += 1;
433 }
434 i += 1;
435 }
436 None
437 }
438
439 fn grid_dist(x1: f64, y1: f64, x2: f64, y2: f64) -> f64 {
440 let dx = x1 - x2;
441 let dy = y1 - y2;
442 (dx * dx + dy * dy).sqrt()
443 }
444}
445
446struct SplitMix64 {
451 state: u64,
452}
453
454impl SplitMix64 {
455 fn new(seed: u64) -> Self {
456 Self { state: seed }
457 }
458
459 fn next_u64(&mut self) -> u64 {
460 self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
461 let mut z = self.state;
462 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
463 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
464 z ^ (z >> 31)
465 }
466
467 fn next_uniform(&mut self) -> f64 {
468 (self.next_u64() >> 11) as f64 / ((1u64 << 53) as f64)
469 }
470}
471
472#[cfg(test)]
477mod tests {
478 use super::*;
479
480 #[test]
481 fn test_merge_empty() {
482 let result = layout_merge_dla(&[]).unwrap();
483 assert!(result.is_empty());
484 }
485
486 #[test]
487 fn test_merge_single() {
488 let layout = vec![[0.0, 0.0], [1.0, 0.0], [0.5, 1.0]];
489 let result = layout_merge_dla(&[&layout]).unwrap();
490 assert_eq!(result.len(), 3);
491 }
492
493 #[test]
494 fn test_merge_two_components() {
495 let l1 = vec![[0.0, 0.0], [1.0, 0.0], [0.5, 1.0]];
496 let l2 = vec![[0.0, 0.0], [1.0, 1.0]];
497 let result = layout_merge_dla(&[&l1, &l2]).unwrap();
498 assert_eq!(result.len(), 5);
499 for p in &result {
500 assert!(p[0].is_finite() && p[1].is_finite());
501 }
502 }
503
504 #[test]
505 fn test_merge_three_components() {
506 let l1 = vec![[0.0, 0.0], [1.0, 0.0], [2.0, 0.0], [3.0, 0.0]];
507 let l2 = vec![[0.0, 0.0], [1.0, 1.0], [2.0, 2.0]];
508 let l3 = vec![[0.0, 0.0], [0.5, 0.5]];
509 let result = layout_merge_dla(&[&l1, &l2, &l3]).unwrap();
510 assert_eq!(result.len(), 9);
511 for p in &result {
512 assert!(p[0].is_finite() && p[1].is_finite());
513 }
514 }
515
516 #[test]
517 fn test_merge_single_vertex_components() {
518 let l1 = vec![[0.0, 0.0]];
519 let l2 = vec![[1.0, 1.0]];
520 let l3 = vec![[2.0, 2.0]];
521 let result = layout_merge_dla(&[&l1, &l2, &l3]).unwrap();
522 assert_eq!(result.len(), 3);
523 }
524
525 #[test]
526 fn test_merge_empty_component() {
527 let l1: Vec<[f64; 2]> = vec![];
528 let l2 = vec![[0.0, 0.0], [1.0, 0.0]];
529 let result = layout_merge_dla(&[&l1[..], &l2]).unwrap();
530 assert_eq!(result.len(), 2);
531 }
532
533 #[test]
534 fn test_merge_deterministic() {
535 let l1 = vec![[0.0, 0.0], [1.0, 0.0], [0.5, 1.0]];
536 let l2 = vec![[0.0, 0.0], [1.0, 1.0]];
537 let r1 = layout_merge_dla(&[&l1, &l2]).unwrap();
538 let r2 = layout_merge_dla(&[&l1, &l2]).unwrap();
539 for i in 0..5 {
540 assert!((r1[i][0] - r2[i][0]).abs() < 1e-10);
541 assert!((r1[i][1] - r2[i][1]).abs() < 1e-10);
542 }
543 }
544
545 #[test]
546 fn test_merge_no_overlap() {
547 let l1 = vec![[0.0, 0.0], [1.0, 0.0], [0.5, 1.0], [0.5, -1.0]];
548 let l2 = vec![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]];
549 let result = layout_merge_dla(&[&l1, &l2]).unwrap();
550 let cx1 = (result[0][0] + result[1][0] + result[2][0] + result[3][0]) / 4.0;
552 let cy1 = (result[0][1] + result[1][1] + result[2][1] + result[3][1]) / 4.0;
553 let cx2 = (result[4][0] + result[5][0] + result[6][0]) / 3.0;
554 let cy2 = (result[4][1] + result[5][1] + result[6][1]) / 3.0;
555 let dist = ((cx1 - cx2) * (cx1 - cx2) + (cy1 - cy2) * (cy1 - cy2)).sqrt();
556 assert!(dist > 0.1);
557 }
558}