1use crate::aes::Aesthetic;
17use crate::data::{DataFrame, Value};
18use crate::scale::ScaleSet;
19
20use super::Stat;
21
22#[derive(Clone, Copy, Debug, PartialEq)]
23pub(crate) struct Circle {
24 pub x: f64,
25 pub y: f64,
26 pub r: f64,
27}
28
29fn place(b: Circle, a: Circle, c: &mut Circle) {
30 let (dx, dy) = (b.x - a.x, b.y - a.y);
31 let d2 = dx * dx + dy * dy;
32 if d2 > 0.0 {
33 let a2 = (a.r + c.r).powi(2);
34 let b2 = (b.r + c.r).powi(2);
35 if a2 > b2 {
36 let x = (d2 + b2 - a2) / (2.0 * d2);
37 let y = (b2 / d2 - x * x).max(0.0).sqrt();
38 c.x = b.x - x * dx - y * dy;
39 c.y = b.y - x * dy + y * dx;
40 } else {
41 let x = (d2 + a2 - b2) / (2.0 * d2);
42 let y = (a2 / d2 - x * x).max(0.0).sqrt();
43 c.x = a.x + x * dx - y * dy;
44 c.y = a.y + x * dy + y * dx;
45 }
46 } else {
47 c.x = a.x + c.r;
48 c.y = a.y;
49 }
50}
51
52fn intersects(a: Circle, b: Circle) -> bool {
53 let dr = a.r + b.r - 1e-6;
54 let (dx, dy) = (b.x - a.x, b.y - a.y);
55 dr > 0.0 && dr * dr > dx * dx + dy * dy
56}
57
58pub(crate) fn pack_siblings(circles: &mut [Circle]) -> f64 {
62 let n = circles.len();
63 if n == 0 {
64 return 0.0;
65 }
66 circles[0].x = 0.0;
67 circles[0].y = 0.0;
68 if n == 1 {
69 return circles[0].r;
70 }
71 circles[0].x = -circles[1].r;
72 circles[1].x = circles[0].r;
73 circles[1].y = 0.0;
74 if n == 2 {
75 return circles[0].r + circles[1].r;
76 }
77 let (c0, c1) = (circles[0], circles[1]);
78 place(c1, c0, &mut circles[2]);
79
80 let mut next = vec![usize::MAX; n];
82 let mut prev = vec![usize::MAX; n];
83 let (mut a, mut b) = (0usize, 1usize);
84 next[0] = 1;
85 prev[1] = 0;
86 next[1] = 2;
87 prev[2] = 1;
88 next[2] = 0;
89 prev[0] = 2;
90 let score = |c: &[Circle], i: usize, next: &[usize]| {
91 let (p, q) = (c[i], c[next[i]]);
92 let ab = p.r + q.r;
93 let dx = (p.x * q.r + q.x * p.r) / ab;
94 let dy = (p.y * q.r + q.y * p.r) / ab;
95 dx * dx + dy * dy
96 };
97
98 let mut i = 3;
99 'pack: while i < n {
100 let (ca, cb) = (circles[a], circles[b]);
101 place(ca, cb, &mut circles[i]);
102 let c = i;
103 let (mut j, mut k) = (next[b], prev[a]);
104 let (mut sj, mut sk) = (circles[b].r, circles[a].r);
105 loop {
106 if sj <= sk {
107 if intersects(circles[j], circles[c]) {
108 b = j;
109 next[a] = b;
110 prev[b] = a;
111 continue 'pack;
112 }
113 sj += circles[j].r;
114 j = next[j];
115 } else {
116 if intersects(circles[k], circles[c]) {
117 a = k;
118 next[a] = b;
119 prev[b] = a;
120 continue 'pack;
121 }
122 sk += circles[k].r;
123 k = prev[k];
124 }
125 if j == next[k] {
126 break;
127 }
128 }
129 prev[c] = a;
131 next[c] = b;
132 next[a] = c;
133 prev[b] = c;
134 b = c;
135 let mut best = score(circles, a, &next);
137 let mut cur = next[c];
138 while cur != b {
139 let s = score(circles, cur, &next);
140 if s < best {
141 a = cur;
142 best = s;
143 }
144 cur = next[cur];
145 }
146 b = next[a];
147 i += 1;
148 }
149
150 let mut chain = vec![circles[b]];
151 let mut cur = next[b];
152 while cur != b {
153 chain.push(circles[cur]);
154 cur = next[cur];
155 }
156 let e = enclose(&chain);
157 for c in circles.iter_mut() {
158 c.x -= e.x;
159 c.y -= e.y;
160 }
161 e.r
162}
163
164fn encloses_not(a: Circle, b: Circle) -> bool {
165 let dr = a.r - b.r;
166 let (dx, dy) = (b.x - a.x, b.y - a.y);
167 dr < 0.0 || dr * dr < dx * dx + dy * dy
168}
169
170fn encloses_weak(a: Circle, b: Circle) -> bool {
171 let dr = a.r - b.r + a.r.max(b.r).max(1.0) * 1e-9;
172 let (dx, dy) = (b.x - a.x, b.y - a.y);
173 dr > 0.0 && dr * dr > dx * dx + dy * dy
174}
175
176fn encloses_weak_all(a: Circle, set: &[Circle]) -> bool {
177 set.iter().all(|&b| encloses_weak(a, b))
178}
179
180fn basis2(a: Circle, b: Circle) -> Circle {
181 let (x21, y21, r21) = (b.x - a.x, b.y - a.y, b.r - a.r);
182 let l = (x21 * x21 + y21 * y21).sqrt();
183 if l == 0.0 {
184 return if a.r >= b.r { a } else { b };
185 }
186 Circle {
187 x: (a.x + b.x + x21 / l * r21) / 2.0,
188 y: (a.y + b.y + y21 / l * r21) / 2.0,
189 r: (l + a.r + b.r) / 2.0,
190 }
191}
192
193fn basis3(a: Circle, b: Circle, c: Circle) -> Circle {
194 let (x1, y1, r1) = (a.x, a.y, a.r);
195 let (a2, a3, b2, b3) = (x1 - b.x, x1 - c.x, y1 - b.y, y1 - c.y);
196 let (c2, c3) = (b.r - r1, c.r - r1);
197 let d1 = x1 * x1 + y1 * y1 - r1 * r1;
198 let d2 = d1 - b.x * b.x - b.y * b.y + b.r * b.r;
199 let d3 = d1 - c.x * c.x - c.y * c.y + c.r * c.r;
200 let ab = a3 * b2 - a2 * b3;
201 let xa = (b2 * d3 - b3 * d2) / (ab * 2.0) - x1;
202 let xb = (b3 * c2 - b2 * c3) / ab;
203 let ya = (a3 * d2 - a2 * d3) / (ab * 2.0) - y1;
204 let yb = (a2 * c3 - a3 * c2) / ab;
205 let qa = xb * xb + yb * yb - 1.0;
206 let qb = 2.0 * (r1 + xa * xb + ya * yb);
207 let qc = xa * xa + ya * ya - r1 * r1;
208 let r = -(if qa.abs() > 1e-6 {
209 (qb + (qb * qb - 4.0 * qa * qc).sqrt()) / (2.0 * qa)
210 } else {
211 qc / qb
212 });
213 Circle {
214 x: x1 + xa + xb * r,
215 y: y1 + ya + yb * r,
216 r,
217 }
218}
219
220fn basis(set: &[Circle]) -> Circle {
221 match set {
222 [a] => *a,
223 [a, b] => basis2(*a, *b),
224 [a, b, c] => basis3(*a, *b, *c),
225 _ => Circle {
226 x: 0.0,
227 y: 0.0,
228 r: 0.0,
229 },
230 }
231}
232
233fn extend_basis(set: &[Circle], p: Circle) -> Vec<Circle> {
234 if encloses_weak_all(p, set) {
235 return vec![p];
236 }
237 for &b in set {
238 if encloses_not(p, b) && encloses_weak_all(basis2(b, p), set) {
239 return vec![b, p];
240 }
241 }
242 for i in 0..set.len().saturating_sub(1) {
243 for j in i + 1..set.len() {
244 let (bi, bj) = (set[i], set[j]);
245 if encloses_not(basis2(bi, bj), p)
246 && encloses_not(basis2(bi, p), bj)
247 && encloses_not(basis2(bj, p), bi)
248 && encloses_weak_all(basis3(bi, bj, p), set)
249 {
250 return vec![bi, bj, p];
251 }
252 }
253 }
254 vec![set[0], p]
256}
257
258pub(crate) fn enclose(circles: &[Circle]) -> Circle {
261 let mut c = circles.to_vec();
262 let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
264 for i in (1..c.len()).rev() {
265 state = state
266 .wrapping_mul(6364136223846793005)
267 .wrapping_add(1442695040888963407);
268 let j = (state >> 33) as usize % (i + 1);
269 c.swap(i, j);
270 }
271 let (mut i, mut set, mut e): (usize, Vec<Circle>, Option<Circle>) = (0, Vec::new(), None);
272 let mut guard = 0usize;
273 while i < c.len() {
274 let p = c[i];
275 if e.is_some_and(|e| encloses_weak(e, p)) {
276 i += 1;
277 } else {
278 set = extend_basis(&set, p);
279 e = Some(basis(&set));
280 i = 0;
281 guard += 1;
282 if guard > 10 * c.len() * c.len() + 100 {
283 break;
284 }
285 }
286 }
287 e.unwrap_or(Circle {
288 x: 0.0,
289 y: 0.0,
290 r: 0.0,
291 })
292}
293
294type PackedGroup = (Value, Vec<(usize, f64)>, Vec<Circle>, f64);
297
298#[derive(Clone, Debug)]
300pub struct StatPack {
301 pub sort: bool,
303 pub padding: f64,
305}
306
307impl Default for StatPack {
308 fn default() -> Self {
309 StatPack {
310 sort: true,
311 padding: 0.08,
312 }
313 }
314}
315
316impl Stat for StatPack {
317 fn compute_group(&self, data: &DataFrame, _scales: &ScaleSet) -> DataFrame {
318 let weight = data.column("weight");
319 let rows: Vec<(usize, f64)> = (0..data.nrows())
320 .filter_map(|i| {
321 let w = match weight {
322 Some(c) => c[i].as_f64()?,
323 None => 1.0,
324 };
325 (w.is_finite() && w > 0.0).then_some((i, w.sqrt()))
326 })
327 .collect();
328 if rows.is_empty() {
329 return DataFrame::new();
330 }
331 let mean_r = rows.iter().map(|r| r.1).sum::<f64>() / rows.len() as f64;
332 let pad = self.padding * mean_r;
333
334 let sub = data.column("subgroup");
335 let mut groups: indexmap::IndexMap<String, (Value, Vec<(usize, f64)>)> =
336 indexmap::IndexMap::new();
337 for &(i, r) in &rows {
338 let (key, val) = match sub {
339 Some(c) => (c[i].key_str().into_owned(), c[i].clone()),
340 None => (String::new(), Value::Na),
341 };
342 groups
343 .entry(key)
344 .or_insert_with(|| (val, Vec::new()))
345 .1
346 .push((i, r));
347 }
348
349 let pack = |members: &mut Vec<(usize, f64)>| -> (Vec<Circle>, f64) {
350 if self.sort {
351 members.sort_by(|a, b| b.1.total_cmp(&a.1));
352 }
353 let mut circles: Vec<Circle> = members
354 .iter()
355 .map(|&(_, r)| Circle {
356 x: 0.0,
357 y: 0.0,
358 r: r + pad / 2.0,
359 })
360 .collect();
361 let outer = pack_siblings(&mut circles);
362 for c in circles.iter_mut() {
363 c.r -= pad / 2.0;
364 }
365 (circles, outer)
366 };
367
368 let mut leaves: Vec<(usize, Circle)> = Vec::new();
369 let mut group_rows: Vec<(Value, Circle, usize)> = Vec::new();
370 let grouped = sub.is_some();
371 let mut packed: Vec<PackedGroup> = groups
372 .into_values()
373 .map(|(val, mut m)| {
374 let (c, outer) = pack(&mut m);
375 (val, m, c, outer)
376 })
377 .collect();
378 if grouped {
379 let mut outers: Vec<Circle> = packed
380 .iter()
381 .map(|g| Circle {
382 x: 0.0,
383 y: 0.0,
384 r: g.3 + pad * 1.5,
385 })
386 .collect();
387 pack_siblings(&mut outers);
388 for (g, o) in packed.iter_mut().zip(&outers) {
389 for c in g.2.iter_mut() {
390 c.x += o.x;
391 c.y += o.y;
392 }
393 group_rows.push((g.0.clone(), Circle { r: g.3, ..*o }, g.1[0].0));
394 }
395 }
396 for g in &packed {
397 for (&(i, _), &c) in g.1.iter().zip(&g.2) {
398 leaves.push((i, c));
399 }
400 }
401 let all: Vec<Circle> = leaves
402 .iter()
403 .map(|l| l.1)
404 .chain(group_rows.iter().map(|g| g.1))
405 .collect();
406 let half = all
407 .iter()
408 .map(|c| c.x.abs().max(c.y.abs()) + c.r)
409 .fold(0.0, f64::max);
410
411 let n = leaves.len() + group_rows.len();
412 let mut out = DataFrame::new();
413 let mut part = vec![Value::Str("leaf".into()); leaves.len()];
414 part.extend(std::iter::repeat_n(
415 Value::Str("group".into()),
416 group_rows.len(),
417 ));
418 out.add_column("part".into(), part);
419 out.add_column("x".into(), all.iter().map(|c| Value::Float(c.x)).collect());
420 out.add_column("y".into(), all.iter().map(|c| Value::Float(c.y)).collect());
421 out.add_column("r".into(), all.iter().map(|c| Value::Float(c.r)).collect());
422 for (c, v) in [
423 ("xmin", -half),
424 ("xmax", half),
425 ("ymin", -half),
426 ("ymax", half),
427 ] {
428 out.add_column(c.into(), vec![Value::Float(v); n]);
429 }
430 for name in data.column_names() {
431 if out.has_column(name) {
432 continue;
433 }
434 let col = data.column(name).unwrap_or_default();
435 let mut vals: Vec<Value> = leaves.iter().map(|l| col[l.0].clone()).collect();
436 if name == "subgroup" {
437 vals.extend(group_rows.iter().map(|g| g.0.clone()));
438 } else if name == "fill" {
439 vals.extend(group_rows.iter().map(|g| col[g.2].clone()));
441 } else {
442 vals.extend(std::iter::repeat_n(Value::Na, group_rows.len()));
443 }
444 out.add_column(name.to_string(), vals);
445 }
446 out
447 }
448
449 fn required_aes(&self) -> Vec<Aesthetic> {
450 Vec::new()
451 }
452
453 fn panelwise(&self) -> bool {
455 true
456 }
457
458 fn name(&self) -> &str {
459 "pack"
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 fn no_overlap(c: &[Circle]) {
468 for i in 0..c.len() {
469 for j in i + 1..c.len() {
470 let d = (c[i].x - c[j].x).hypot(c[i].y - c[j].y);
471 assert!(d >= c[i].r + c[j].r - 1e-6, "{i} and {j} overlap: {d}");
472 }
473 }
474 }
475
476 #[test]
477 fn siblings_never_overlap_and_fit_their_enclosure() {
478 let mut c: Vec<Circle> = (1..=40)
479 .map(|k| Circle {
480 x: 0.0,
481 y: 0.0,
482 r: ((k * 37 % 11) as f64 + 1.0).sqrt(),
483 })
484 .collect();
485 let outer = pack_siblings(&mut c);
486 no_overlap(&c);
487 for k in &c {
488 assert!(k.x.hypot(k.y) + k.r <= outer + 1e-6);
489 }
490 let area: f64 = c.iter().map(|k| k.r * k.r).sum();
492 assert!(outer * outer < 2.2 * area, "{outer}");
493 }
494
495 #[test]
496 fn enclose_three_known_circles() {
497 let e = enclose(&[
498 Circle {
499 x: -1.0,
500 y: 0.0,
501 r: 1.0,
502 },
503 Circle {
504 x: 1.0,
505 y: 0.0,
506 r: 1.0,
507 },
508 Circle {
509 x: 0.0,
510 y: 0.0,
511 r: 0.5,
512 },
513 ]);
514 assert!((e.r - 2.0).abs() < 1e-9 && e.x.abs() < 1e-9 && e.y.abs() < 1e-9);
515 }
516
517 #[test]
518 fn grouped_leaves_sit_inside_their_group_circle() {
519 let mut d = DataFrame::new();
520 let s = |v: &[&str]| v.iter().map(|x| Value::Str(x.to_string())).collect();
521 d.add_column("subgroup".into(), s(&["A", "A", "A", "B", "B", "C"]));
522 d.add_column(
523 "weight".into(),
524 [9.0, 4.0, 1.0, 16.0, 2.0, 5.0]
525 .iter()
526 .map(|&v| Value::Float(v))
527 .collect(),
528 );
529 let out = StatPack::default().compute_group(&d, &ScaleSet::new());
530 assert_eq!(out.nrows(), 6 + 3);
531 let f = |c: &str, i: usize| out.column(c).unwrap()[i].as_f64().unwrap();
532 let sub = |i: usize| out.column("subgroup").unwrap()[i].key_str().into_owned();
533 let leaves: Vec<Circle> = (0..6)
534 .map(|i| Circle {
535 x: f("x", i),
536 y: f("y", i),
537 r: f("r", i),
538 })
539 .collect();
540 no_overlap(&leaves);
541 for i in 0..6 {
542 let g = (6..9).find(|&g| sub(g) == sub(i)).unwrap();
543 let d = (f("x", i) - f("x", g)).hypot(f("y", i) - f("y", g));
544 assert!(
545 d + f("r", i) <= f("r", g) + 1e-6,
546 "leaf {i} outside its group"
547 );
548 }
549 let r: Vec<f64> = (0..3).map(|i| f("r", i)).collect();
551 assert!((r[0] / r[1] - 1.5).abs() < 0.05, "{r:?}");
552 }
553}