| [e4c61dd] | 1 | import array from "../array.js";
|
|---|
| 2 | import lcg from "../lcg.js";
|
|---|
| 3 | import {packEncloseRandom} from "./enclose.js";
|
|---|
| 4 |
|
|---|
| 5 | function place(b, a, c) {
|
|---|
| 6 | var dx = b.x - a.x, x, a2,
|
|---|
| 7 | dy = b.y - a.y, y, b2,
|
|---|
| 8 | d2 = dx * dx + dy * dy;
|
|---|
| 9 | if (d2) {
|
|---|
| 10 | a2 = a.r + c.r, a2 *= a2;
|
|---|
| 11 | b2 = b.r + c.r, b2 *= b2;
|
|---|
| 12 | if (a2 > b2) {
|
|---|
| 13 | x = (d2 + b2 - a2) / (2 * d2);
|
|---|
| 14 | y = Math.sqrt(Math.max(0, b2 / d2 - x * x));
|
|---|
| 15 | c.x = b.x - x * dx - y * dy;
|
|---|
| 16 | c.y = b.y - x * dy + y * dx;
|
|---|
| 17 | } else {
|
|---|
| 18 | x = (d2 + a2 - b2) / (2 * d2);
|
|---|
| 19 | y = Math.sqrt(Math.max(0, a2 / d2 - x * x));
|
|---|
| 20 | c.x = a.x + x * dx - y * dy;
|
|---|
| 21 | c.y = a.y + x * dy + y * dx;
|
|---|
| 22 | }
|
|---|
| 23 | } else {
|
|---|
| 24 | c.x = a.x + c.r;
|
|---|
| 25 | c.y = a.y;
|
|---|
| 26 | }
|
|---|
| 27 | }
|
|---|
| 28 |
|
|---|
| 29 | function intersects(a, b) {
|
|---|
| 30 | var dr = a.r + b.r - 1e-6, dx = b.x - a.x, dy = b.y - a.y;
|
|---|
| 31 | return dr > 0 && dr * dr > dx * dx + dy * dy;
|
|---|
| 32 | }
|
|---|
| 33 |
|
|---|
| 34 | function score(node) {
|
|---|
| 35 | var a = node._,
|
|---|
| 36 | b = node.next._,
|
|---|
| 37 | ab = a.r + b.r,
|
|---|
| 38 | dx = (a.x * b.r + b.x * a.r) / ab,
|
|---|
| 39 | dy = (a.y * b.r + b.y * a.r) / ab;
|
|---|
| 40 | return dx * dx + dy * dy;
|
|---|
| 41 | }
|
|---|
| 42 |
|
|---|
| 43 | function Node(circle) {
|
|---|
| 44 | this._ = circle;
|
|---|
| 45 | this.next = null;
|
|---|
| 46 | this.previous = null;
|
|---|
| 47 | }
|
|---|
| 48 |
|
|---|
| 49 | export function packSiblingsRandom(circles, random) {
|
|---|
| 50 | if (!(n = (circles = array(circles)).length)) return 0;
|
|---|
| 51 |
|
|---|
| 52 | var a, b, c, n, aa, ca, i, j, k, sj, sk;
|
|---|
| 53 |
|
|---|
| 54 | // Place the first circle.
|
|---|
| 55 | a = circles[0], a.x = 0, a.y = 0;
|
|---|
| 56 | if (!(n > 1)) return a.r;
|
|---|
| 57 |
|
|---|
| 58 | // Place the second circle.
|
|---|
| 59 | b = circles[1], a.x = -b.r, b.x = a.r, b.y = 0;
|
|---|
| 60 | if (!(n > 2)) return a.r + b.r;
|
|---|
| 61 |
|
|---|
| 62 | // Place the third circle.
|
|---|
| 63 | place(b, a, c = circles[2]);
|
|---|
| 64 |
|
|---|
| 65 | // Initialize the front-chain using the first three circles a, b and c.
|
|---|
| 66 | a = new Node(a), b = new Node(b), c = new Node(c);
|
|---|
| 67 | a.next = c.previous = b;
|
|---|
| 68 | b.next = a.previous = c;
|
|---|
| 69 | c.next = b.previous = a;
|
|---|
| 70 |
|
|---|
| 71 | // Attempt to place each remaining circle…
|
|---|
| 72 | pack: for (i = 3; i < n; ++i) {
|
|---|
| 73 | place(a._, b._, c = circles[i]), c = new Node(c);
|
|---|
| 74 |
|
|---|
| 75 | // Find the closest intersecting circle on the front-chain, if any.
|
|---|
| 76 | // “Closeness” is determined by linear distance along the front-chain.
|
|---|
| 77 | // “Ahead” or “behind” is likewise determined by linear distance.
|
|---|
| 78 | j = b.next, k = a.previous, sj = b._.r, sk = a._.r;
|
|---|
| 79 | do {
|
|---|
| 80 | if (sj <= sk) {
|
|---|
| 81 | if (intersects(j._, c._)) {
|
|---|
| 82 | b = j, a.next = b, b.previous = a, --i;
|
|---|
| 83 | continue pack;
|
|---|
| 84 | }
|
|---|
| 85 | sj += j._.r, j = j.next;
|
|---|
| 86 | } else {
|
|---|
| 87 | if (intersects(k._, c._)) {
|
|---|
| 88 | a = k, a.next = b, b.previous = a, --i;
|
|---|
| 89 | continue pack;
|
|---|
| 90 | }
|
|---|
| 91 | sk += k._.r, k = k.previous;
|
|---|
| 92 | }
|
|---|
| 93 | } while (j !== k.next);
|
|---|
| 94 |
|
|---|
| 95 | // Success! Insert the new circle c between a and b.
|
|---|
| 96 | c.previous = a, c.next = b, a.next = b.previous = b = c;
|
|---|
| 97 |
|
|---|
| 98 | // Compute the new closest circle pair to the centroid.
|
|---|
| 99 | aa = score(a);
|
|---|
| 100 | while ((c = c.next) !== b) {
|
|---|
| 101 | if ((ca = score(c)) < aa) {
|
|---|
| 102 | a = c, aa = ca;
|
|---|
| 103 | }
|
|---|
| 104 | }
|
|---|
| 105 | b = a.next;
|
|---|
| 106 | }
|
|---|
| 107 |
|
|---|
| 108 | // Compute the enclosing circle of the front chain.
|
|---|
| 109 | a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = packEncloseRandom(a, random);
|
|---|
| 110 |
|
|---|
| 111 | // Translate the circles to put the enclosing circle around the origin.
|
|---|
| 112 | for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;
|
|---|
| 113 |
|
|---|
| 114 | return c.r;
|
|---|
| 115 | }
|
|---|
| 116 |
|
|---|
| 117 | export default function(circles) {
|
|---|
| 118 | packSiblingsRandom(circles, lcg());
|
|---|
| 119 | return circles;
|
|---|
| 120 | }
|
|---|