| 1 | import {shuffle} from "../array.js";
|
|---|
| 2 | import lcg from "../lcg.js";
|
|---|
| 3 |
|
|---|
| 4 | export default function(circles) {
|
|---|
| 5 | return packEncloseRandom(circles, lcg());
|
|---|
| 6 | }
|
|---|
| 7 |
|
|---|
| 8 | export function packEncloseRandom(circles, random) {
|
|---|
| 9 | var i = 0, n = (circles = shuffle(Array.from(circles), random)).length, B = [], p, e;
|
|---|
| 10 |
|
|---|
| 11 | while (i < n) {
|
|---|
| 12 | p = circles[i];
|
|---|
| 13 | if (e && enclosesWeak(e, p)) ++i;
|
|---|
| 14 | else e = encloseBasis(B = extendBasis(B, p)), i = 0;
|
|---|
| 15 | }
|
|---|
| 16 |
|
|---|
| 17 | return e;
|
|---|
| 18 | }
|
|---|
| 19 |
|
|---|
| 20 | function extendBasis(B, p) {
|
|---|
| 21 | var i, j;
|
|---|
| 22 |
|
|---|
| 23 | if (enclosesWeakAll(p, B)) return [p];
|
|---|
| 24 |
|
|---|
| 25 | // If we get here then B must have at least one element.
|
|---|
| 26 | for (i = 0; i < B.length; ++i) {
|
|---|
| 27 | if (enclosesNot(p, B[i])
|
|---|
| 28 | && enclosesWeakAll(encloseBasis2(B[i], p), B)) {
|
|---|
| 29 | return [B[i], p];
|
|---|
| 30 | }
|
|---|
| 31 | }
|
|---|
| 32 |
|
|---|
| 33 | // If we get here then B must have at least two elements.
|
|---|
| 34 | for (i = 0; i < B.length - 1; ++i) {
|
|---|
| 35 | for (j = i + 1; j < B.length; ++j) {
|
|---|
| 36 | if (enclosesNot(encloseBasis2(B[i], B[j]), p)
|
|---|
| 37 | && enclosesNot(encloseBasis2(B[i], p), B[j])
|
|---|
| 38 | && enclosesNot(encloseBasis2(B[j], p), B[i])
|
|---|
| 39 | && enclosesWeakAll(encloseBasis3(B[i], B[j], p), B)) {
|
|---|
| 40 | return [B[i], B[j], p];
|
|---|
| 41 | }
|
|---|
| 42 | }
|
|---|
| 43 | }
|
|---|
| 44 |
|
|---|
| 45 | // If we get here then something is very wrong.
|
|---|
| 46 | throw new Error;
|
|---|
| 47 | }
|
|---|
| 48 |
|
|---|
| 49 | function enclosesNot(a, b) {
|
|---|
| 50 | var dr = a.r - b.r, dx = b.x - a.x, dy = b.y - a.y;
|
|---|
| 51 | return dr < 0 || dr * dr < dx * dx + dy * dy;
|
|---|
| 52 | }
|
|---|
| 53 |
|
|---|
| 54 | function enclosesWeak(a, b) {
|
|---|
| 55 | var dr = a.r - b.r + Math.max(a.r, b.r, 1) * 1e-9, dx = b.x - a.x, dy = b.y - a.y;
|
|---|
| 56 | return dr > 0 && dr * dr > dx * dx + dy * dy;
|
|---|
| 57 | }
|
|---|
| 58 |
|
|---|
| 59 | function enclosesWeakAll(a, B) {
|
|---|
| 60 | for (var i = 0; i < B.length; ++i) {
|
|---|
| 61 | if (!enclosesWeak(a, B[i])) {
|
|---|
| 62 | return false;
|
|---|
| 63 | }
|
|---|
| 64 | }
|
|---|
| 65 | return true;
|
|---|
| 66 | }
|
|---|
| 67 |
|
|---|
| 68 | function encloseBasis(B) {
|
|---|
| 69 | switch (B.length) {
|
|---|
| 70 | case 1: return encloseBasis1(B[0]);
|
|---|
| 71 | case 2: return encloseBasis2(B[0], B[1]);
|
|---|
| 72 | case 3: return encloseBasis3(B[0], B[1], B[2]);
|
|---|
| 73 | }
|
|---|
| 74 | }
|
|---|
| 75 |
|
|---|
| 76 | function encloseBasis1(a) {
|
|---|
| 77 | return {
|
|---|
| 78 | x: a.x,
|
|---|
| 79 | y: a.y,
|
|---|
| 80 | r: a.r
|
|---|
| 81 | };
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | function encloseBasis2(a, b) {
|
|---|
| 85 | var x1 = a.x, y1 = a.y, r1 = a.r,
|
|---|
| 86 | x2 = b.x, y2 = b.y, r2 = b.r,
|
|---|
| 87 | x21 = x2 - x1, y21 = y2 - y1, r21 = r2 - r1,
|
|---|
| 88 | l = Math.sqrt(x21 * x21 + y21 * y21);
|
|---|
| 89 | return {
|
|---|
| 90 | x: (x1 + x2 + x21 / l * r21) / 2,
|
|---|
| 91 | y: (y1 + y2 + y21 / l * r21) / 2,
|
|---|
| 92 | r: (l + r1 + r2) / 2
|
|---|
| 93 | };
|
|---|
| 94 | }
|
|---|
| 95 |
|
|---|
| 96 | function encloseBasis3(a, b, c) {
|
|---|
| 97 | var x1 = a.x, y1 = a.y, r1 = a.r,
|
|---|
| 98 | x2 = b.x, y2 = b.y, r2 = b.r,
|
|---|
| 99 | x3 = c.x, y3 = c.y, r3 = c.r,
|
|---|
| 100 | a2 = x1 - x2,
|
|---|
| 101 | a3 = x1 - x3,
|
|---|
| 102 | b2 = y1 - y2,
|
|---|
| 103 | b3 = y1 - y3,
|
|---|
| 104 | c2 = r2 - r1,
|
|---|
| 105 | c3 = r3 - r1,
|
|---|
| 106 | d1 = x1 * x1 + y1 * y1 - r1 * r1,
|
|---|
| 107 | d2 = d1 - x2 * x2 - y2 * y2 + r2 * r2,
|
|---|
| 108 | d3 = d1 - x3 * x3 - y3 * y3 + r3 * r3,
|
|---|
| 109 | ab = a3 * b2 - a2 * b3,
|
|---|
| 110 | xa = (b2 * d3 - b3 * d2) / (ab * 2) - x1,
|
|---|
| 111 | xb = (b3 * c2 - b2 * c3) / ab,
|
|---|
| 112 | ya = (a3 * d2 - a2 * d3) / (ab * 2) - y1,
|
|---|
| 113 | yb = (a2 * c3 - a3 * c2) / ab,
|
|---|
| 114 | A = xb * xb + yb * yb - 1,
|
|---|
| 115 | B = 2 * (r1 + xa * xb + ya * yb),
|
|---|
| 116 | C = xa * xa + ya * ya - r1 * r1,
|
|---|
| 117 | r = -(Math.abs(A) > 1e-6 ? (B + Math.sqrt(B * B - 4 * A * C)) / (2 * A) : C / B);
|
|---|
| 118 | return {
|
|---|
| 119 | x: x1 + xa + xb * r,
|
|---|
| 120 | y: y1 + ya + yb * r,
|
|---|
| 121 | r: r
|
|---|
| 122 | };
|
|---|
| 123 | }
|
|---|