1#[cfg(not(feature = "serde"))]
6#[derive(Debug, Clone, PartialEq, Default)]
7pub struct Circle {
8 pub x: f32,
9 pub y: f32,
10 pub radius: f32,
11}
12
13#[cfg(feature = "serde")]
14#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
15pub struct Circle {
16 pub x: f32,
17 pub y: f32,
18 pub radius: f32,
19}
20
21impl Circle {
22 pub fn distance_to_origin(&self) -> f32 {
23 (self.x * self.x + self.y * self.y).sqrt()
24 }
25 fn distance(&self, other: &Circle) -> f32 {
26 let x = self.x - other.x;
27 let y = self.y - other.y;
28 (x * x + y * y).sqrt()
29 }
30 fn intersect(&self, other: &Circle) -> bool {
31 self.distance(other) + 1e-4 < self.radius + other.radius
33 }
34 fn new_node(radius: f32, c_m: &Circle, c_n: &Circle) -> (Circle, Circle) {
35 let dist = c_n.distance(c_m);
36 let phi1 = (c_n.y - c_m.y).atan2(c_n.x - c_m.x);
38 let phi2 = ((dist.powi(2) + (c_m.radius + radius).powi(2) - (c_n.radius + radius).powi(2))
39 / (2.0 * (c_m.radius + radius) * dist))
40 .acos();
41 let solution_1 = Circle {
43 x: c_m.x + (c_m.radius + radius) * (phi1 + phi2).cos(),
44 y: c_m.y + (c_m.radius + radius) * (phi1 + phi2).sin(),
45 radius,
46 };
47 let solution_2 = Circle {
49 x: c_m.x + (c_m.radius + radius) * (phi1 - phi2).cos(),
50 y: c_m.y + (c_m.radius + radius) * (phi1 - phi2).sin(),
51 radius,
52 };
53 (solution_1, solution_2)
54 }
55
56 fn initialise(radii: &[f32]) -> Vec<Circle> {
57 match radii.len() {
58 0 => Vec::new(),
59 1 => vec![Circle {
60 x: 0.0,
61 y: 0.0,
62 radius: radii[0],
63 }],
64 2 => {
66 let r0 = radii[0];
67 let r1 = radii[1];
68 let ray = r0 + r1;
69 vec![
70 Circle {
71 x: ray / 2.0,
72 y: 0.0,
73 radius: r0,
74 },
75 Circle {
76 x: -ray / 2.0,
77 y: 0.0,
78 radius: r1,
79 },
80 ]
81 }
82 _ => {
84 let r0 = radii[0];
85 let r1 = radii[1];
86 let r2 = radii[2];
87 let mut front = vec![
88 Circle {
89 x: 0.0,
90 y: 0.0,
91 radius: r0,
92 },
93 Circle {
94 x: r0 + r1,
95 y: 0.0,
96 radius: r1,
97 },
98 ];
99 {
100 let (sol_1, sol_2) = Self::new_node(r2, &front[0], &front[1]);
101 if sol_1.y < 0.0 {
102 front.push(sol_1);
103 } else {
104 front.push(sol_2)
105 }
106 }
107 let (a, b, c) = (r0 + r1, r0 + r2, r1 + r2);
109 let s = (a + b + c) / 2.0;
110 let delta = (s * (s - a) * (s - b) * (s - c)).sqrt();
111 let (l1, l2, l3) = (
112 a + delta / (s - a),
113 b + delta / (s - b),
114 c + delta / (s - c),
115 );
116 let l_sum = l1 + l2 + l3;
117 let (l1, l2, l3) = (l1 / l_sum, l2 / l_sum, l3 / l_sum);
118 let x_correction = l1 * front[2].x + l2 * front[1].x + l3 * front[0].x;
119 let y_correction = l1 * front[2].y + l2 * front[1].y + l3 * front[0].y;
120 front
121 .drain(..)
122 .map(|mut c| {
123 c.x = c.x - x_correction;
124 c.y = c.y - y_correction;
125 c
126 })
127 .collect()
128 }
129 }
130 }
131}
132
133
134#[derive(Debug, Clone, Default)]
135pub struct CirclePacker {
136 radii: Vec<f32>,
137 circles: Vec<Circle>,
138 front: Vec<usize>,
139}
140
141impl CirclePacker {
142 pub fn push(&mut self, radius: f32) {
144 self.radii.push(radius);
145
146 if self.radii.len() <= 3 {
147 self.circles = Circle::initialise(&self.radii);
148 self.front = (0..self.circles.len()).collect();
149 } else {
150 let mut index = self.front_closest();
151 let mut next_index = self.front_get_next_index(index);
152 let mut candidate = self.candidate(radius, index, next_index);
153 while let Some(i) = self.front_get_intersection(index, &candidate) {
154 let (front_dist, back_dist) = self.front_distance_to_node(index, i);
155
156 if front_dist < back_dist {
157 let (break_start, break_end) = self.front_remove(index, i);
158 candidate = self.candidate(
159 radius,
160 break_start,
161 break_end,
162 );
163 index = break_start;
164 next_index = break_end;
165 } else {
166 let (break_start, break_end) = self.front_remove(i, next_index);
167 candidate = self.candidate(
168 radius,
169 break_start,
170 break_end,
171 );
172 index = break_start;
173 next_index = break_end;
174 }
175 }
176 self.insert(candidate, next_index);
177 }
178 }
179
180 pub fn circles(&self) -> Vec<Circle> {
182 let (center_x, center_y) = self.center_of_mass();
183 self.circles.iter().map(|c| {
184 Circle {
185 x: c.x - center_x,
186 y: c.y - center_y,
187 radius: c.radius,
188 }
189 }).collect()
190 }
191
192 pub fn circles_in(&self, embedding_circle: &Circle) -> Vec<Circle> {
194 let mut center_circles = self.circles();
195 if let Some(radius) = center_circles.iter().map(|c| c.distance_to_origin() + c.radius).max_by(|a,b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)) {
196 let radius_ratio = embedding_circle.radius / radius;
197 center_circles.iter_mut().map(|c| {
198 c.x = c.x*radius_ratio + embedding_circle.x;
199 c.y = c.y*radius_ratio + embedding_circle.y;
200 c.radius = c.radius * radius_ratio;
201 }).collect()
202 }
203 center_circles
204 }
205
206 fn candidate(&self, radius: f32, index: usize, next_index: usize) -> Circle {
207 let (solution_1, solution_2) = Circle::new_node(radius, &self.circles[self.front[index]], &self.circles[self.front[next_index]]);
208 let c1 = &self.circles[self.front[index]];
209 let c2 = &self.circles[self.front[next_index]];
210 let determinate = (solution_1.x - c1.x)*(c2.y - c1.y) - (solution_1.y - c1.y)*(c2.x - c1.x);
214 if determinate > 0.0 {
215 solution_2
216 } else {
217 solution_1
218 }
219 }
220
221 fn insert(&mut self, node: Circle, front_break_end: usize) {
223 let front_index = self.circles.len();
224 self.circles.push(node);
225 if 0 != front_break_end {
226 self.front.insert(front_break_end, front_index);
227 } else {
228 self.front.push(front_index);
229 }
230 }
231
232 fn front_closest(&self) -> usize {
234 self.front.iter()
235 .enumerate()
236 .min_by(|(_, m), (_, n)| {
237 self.circles[**m].distance_to_origin()
238 .partial_cmp(&self.circles[**n].distance_to_origin())
239 .unwrap_or(std::cmp::Ordering::Equal)
240 })
241 .unwrap()
242 .0
243 }
244
245 fn front_get_next_index(&self, start_index: usize) -> usize {
247 (start_index + 1) % self.front.len()
248 }
249 fn front_get_intersection(&self, start_index: usize, candidate: &Circle) -> Option<usize> {
251 let mut intersecting_nodes = self.front
252 .iter()
253 .enumerate()
254 .filter_map(|(i, n)| {
255 if self.circles[*n].intersect(candidate) {
256 Some(i)
257 } else {
258 None
259 }
260 })
261 .collect::<Vec<usize>>();
262 intersecting_nodes.sort_by(|a, b| {
263 let (a_b, a_f) = self.front_distance_to_node(start_index, *a);
264 let a = a_b.min(a_f);
265 let (b_b, b_f) = self.front_distance_to_node(start_index, *b);
266 let b = b_b.min(b_f);
267 a.partial_cmp(&b).unwrap_or(std::cmp::Ordering::Equal)
268 });
269 intersecting_nodes.first().map(|f| *f)
270 }
271 fn front_distance_to_node(&self, start_index: usize, end_index: usize) -> (f32, f32) {
273 let total_dist: f32 = self.front.iter().map(|n| self.circles[*n].radius).sum();
274 match start_index.cmp(&end_index) {
275 std::cmp::Ordering::Less => {
276 let front_dist: f32 = self.front[start_index..end_index].iter().map(|i| self.circles[*i].radius).sum();
277 (
278 2.0 * front_dist,
279 2.0 * total_dist - 2.0 * front_dist - 2.0 * self.circles[self.front[end_index]].radius,
280 )
281 }
282 std::cmp::Ordering::Equal => (0.0, total_dist),
283 std::cmp::Ordering::Greater => {
284 let back_dist: f32 = self.front[end_index..start_index].iter().map(|i| self.circles[*i].radius).sum();
285 (
286 2.0 * total_dist - 2.0 * back_dist - 2.0 * self.circles[self.front[start_index]].radius,
287 2.0 * back_dist - 2.0 * self.circles[self.front[end_index]].radius,
288 )
289 }
290 }
291 }
292 fn front_remove(&mut self, start_index: usize, end_index: usize) -> (usize, usize) {
294 if start_index < end_index {
295 self.front.drain((start_index + 1)..end_index);
296 (start_index, self.front_get_next_index(start_index))
297 } else {
298 self.front.drain((start_index + 1)..);
299 self.front.drain(..end_index);
300 (
301 self.front.len() - 1,
302 0,
303 )
304 }
305 }
306
307 fn center_of_mass(&self) -> (f32, f32) {
308 let x: f32 = self.circles.iter().map(|c| c.x * (std::f32::consts::PI * c.radius.powi(2))).sum();
309 let y: f32 = self.circles.iter().map(|c| c.y * (std::f32::consts::PI * c.radius.powi(2))).sum();
310 let radius: f32 = self.circles.iter().map(|c| (std::f32::consts::PI * c.radius.powi(2))).sum();
311 (x/radius, y/radius)
312 }
313}
314
315#[derive(Debug, Clone, Default)]
319pub struct CircleSortedPacker {
320 radii: Vec<(usize,f32)>,
321}
322
323impl CircleSortedPacker {
324 pub fn push(&mut self, radius: f32) {
326 self.radii.push((self.radii.len(),radius));
327 self.radii.sort_by(|a,b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
328 }
329
330 pub fn circles(&self) -> Vec<Circle> {
332 let mut circles = vec![Circle::default(); self.radii.len()];
333 let mut packer = CirclePacker::default();
334 for (_,f) in &self.radii {
335 packer.push(*f);
336 }
337 packer.circles().drain(..).zip(&self.radii).for_each(|(circle, (i,_))| {
338 circles[*i] = circle;
339 });
340 circles
341 }
342
343 pub fn circles_in(&self, embedding_circle: &Circle) -> Vec<Circle> {
345 let mut circles = vec![Circle::default(); self.radii.len()];
346 let mut packer = CirclePacker::default();
347 for (_,f) in &self.radii {
348 packer.push(*f);
349 }
350 packer.circles_in(embedding_circle).drain(..).zip(&self.radii).for_each(|(circle, (i,_))| {
351 circles[*i] = circle;
352 });
353 circles
354 }
355}
356
357
358#[cfg(test)]
359mod tests {
360 use crate::{Circle, CirclePacker};
361 use rand::{prelude::*, rngs::SmallRng};
362 use assert_approx_eq::assert_approx_eq;
363 fn has_nan(circle: &Circle) -> bool {
364 circle.x.is_nan() || circle.y.is_nan() || circle.radius.is_nan()
365 }
366
367 fn front_touches(packer: &CirclePacker) {
368 tracing::info!("Packer: {:?}", packer);
369 for i in 1..packer.front.len() {
370 assert_eq!(i,packer.front_get_next_index(i-1));
371 let cm = &packer.circles[packer.front[i-1]];
372 let cn = &packer.circles[packer.front[i]];
373 assert_approx_eq!(cm.distance(cn), cm.radius + cn.radius, 1e-4f32);
374 }
375 if 1 < packer.front.len() {
376 assert_eq!(0,packer.front_get_next_index(packer.front.len() - 1));
377 let cm = &packer.circles[packer.front[packer.front.len() - 1]];
378 let cn = &packer.circles[packer.front[0]];
379 assert_approx_eq!(cm.distance(cn), cm.radius + cn.radius, 1e-4f32);
380 }
381 }
382 #[tracing_test::traced_test]
383 #[test]
384 pub fn new_node_does_not_intersect_touching() {
385 let mut rng = SmallRng::from_seed([0; 32]);
386 for _ in 0..20 {
387 let mut circle_1 = Circle {
388 x: rng.gen(),
389 y: rng.gen(),
390 radius: rng.gen(),
391 };
392
393 let mut circle_2 = Circle {
394 x: rng.gen(),
395 y: rng.gen(),
396 radius: 0.0,
397 };
398 while circle_1.distance(&circle_2) < circle_1.radius {
399 circle_1.x = rng.gen();
400 circle_1.y = rng.gen();
401 circle_1.radius = rng.gen();
402 circle_2.x = rng.gen();
403 circle_2.y = rng.gen();
404 }
405 let dist = circle_1.distance(&circle_2);
406 circle_2.radius = dist - circle_1.radius;
407
408 let radius: f32 = rng.gen();
409
410 let (circle_3, circle_4) = Circle::new_node(radius, &circle_1, &circle_2);
411 assert!(!circle_3.x.is_nan(), "Candidate Circle has a NAN");
412 assert!(!circle_3.y.is_nan(), "Candidate Circle has a NAN");
413 assert!(!circle_4.x.is_nan(), "Candidate Circle has a NAN");
414 assert!(!circle_4.y.is_nan(), "Candidate Circle has a NAN");
415 assert_approx_eq!(circle_1.distance(&circle_2), circle_1.radius + circle_2.radius);
416 assert_approx_eq!(circle_1.distance(&circle_3), circle_1.radius + circle_3.radius);
417 assert_approx_eq!(circle_1.distance(&circle_4), circle_1.radius + circle_4.radius);
418 assert_approx_eq!(circle_2.distance(&circle_3), circle_2.radius + circle_3.radius);
419 assert_approx_eq!(circle_2.distance(&circle_4), circle_2.radius + circle_4.radius);
420 assert!(!circle_1.intersect(&circle_2));
421 assert!(!circle_1.intersect(&circle_3));
422 assert!(!circle_1.intersect(&circle_4));
423 assert!(!circle_2.intersect(&circle_3));
424 assert!(!circle_2.intersect(&circle_4));
425 }
426 }
427
428 fn radii_work(radii: &[f32]) {
429 let mut packer = CirclePacker::default();
430 for radius in radii {
431 let span = tracing::info_span!("Radius: {}", radius);
432 let _entered = span.enter();
433 packer.push(*radius);
434 front_touches(&packer);
435 let circles = packer.circles();
436 for i in 0..circles.len() {
437 assert!(!has_nan(&circles[i]), "Circle {} has a nan on radius {}", i, radius);
438 for j in 0..i {
439 assert!(!circles[i].intersect(&circles[j]), "{} {:?} intersects with {} {:?} on radius {}", i, &circles[i], j, &circles[j], radius);
440 }
441 for j in (i+1)..circles.len() {
442 assert!(!circles[i].intersect(&circles[j]), "{} {:?} intersects with {} {:?} on radius {}", i, &circles[i], j, &circles[j], radius);
443 }
444 }
445 }
446 }
447
448 #[tracing_test::traced_test]
449 #[test]
450 pub fn it_works() {
451 radii_work(&[50.0, 100.1, 40.2, 30.3, 60.4, 40.5, 80.6, 100.7, 50.8])
452 }
453
454 #[tracing_test::traced_test]
455 #[test]
456 pub fn it_works_2() {
457 radii_work(&[32.0, 38.1, 35.2, 1.3, 49.4, 2.5, 85.6])
458 }
459
460 #[tracing_test::traced_test]
461 #[test]
462 pub fn it_works_3() {
463 radii_work(&[89.0, 96.1, 8.2, 71.3])
464 }
465
466 #[tracing_test::traced_test]
467 #[test]
468 pub fn it_works_4() {
469 radii_work(&[27.0, 56.1, 15.2, 89.3, 91.4, 13.5, 27.6, 86.7])
470 }
471
472 #[tracing_test::traced_test]
473 #[test]
474 pub fn it_works_fuzz() {
475 let mut rng = SmallRng::from_seed([0; 32]);
476 for _ in 0..20 {
477 let mut packer = CirclePacker::default();
478 for _ in 0..50 {
479 let radius: f32 = rng.gen();
480 packer.push(100.0f32 * radius);
481 front_touches(&packer);
482 let circles = packer.circles();
483 for i in 0..circles.len() {
484 assert!(!has_nan(&circles[i]), "Circle {} has a nan on radius {}", i, radius);
485 for j in 0..i {
486 assert!(!circles[i].intersect(&circles[j]), "{} {:?} intersects with {} {:?} on radius {}", i, &circles[i], j, &circles[j], radius);
487 }
488 for j in (i+1)..circles.len() {
489 assert!(!circles[i].intersect(&circles[j]), "{} {:?} intersects with {} {:?} on radius {}", i, &circles[i], j, &circles[j], radius);
490 }
491 }
492 }
493 }
494 }
495
496 #[tracing_test::traced_test]
497 #[test]
498 pub fn contained() {
499 let mut packer = CirclePacker::default();
500 for radius in [32.0, 38.1, 35.2, 1.3, 49.4, 2.5, 85.6, 84.7, 29.8, 7.9, 31.10, 6.11, 11.12] {
501 packer.push(radius);
502 }
503 let embedding_circle = Circle {x: 10.0, y: 100.0, radius: 350.0};
504 let circles = packer.circles_in(&embedding_circle);
505 for circle in circles {
506 assert!(circle.distance(&embedding_circle) + circle.radius <= 350.0 + 1e-4);
507 }
508 }
509}