Skip to content

Commit

Permalink
Expose packCircles, packEnclosingCircle.
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock committed Mar 22, 2016
1 parent 71511e4 commit fddac40
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 109 deletions.
2 changes: 2 additions & 0 deletions index.js
@@ -1,6 +1,8 @@
export {version} from "./build/package";
export {default as hierarchy} from "./src/hierarchy";
export {default as pack} from "./src/pack/index";
export {default as packCircles} from "./src/pack/circles";
export {default as packEnclosingCircle} from "./src/pack/enclosingCircle";
export {default as partition} from "./src/partition";
export {default as treemap} from "./src/treemap/index";
export {default as treemapBinary} from "./src/treemap/binary";
Expand Down
96 changes: 96 additions & 0 deletions src/pack/circles.js
@@ -0,0 +1,96 @@
function place(A, B, C) {
var a = A._,
b = B._,
c = C._,
ax = a.x,
ay = a.y,
da = b.r + c.r,
db = a.r + c.r,
dx = b.x - ax,
dy = b.y - ay,
dc = dx * dx + dy * dy;
if (dc) {
var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc),
y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
c.x = ax + x * dx + y * dy;
c.y = ay + x * dy - y * dx;
} else {
c.x = ax + db;
c.y = ay;
}
C.score = c.x * c.x + c.y * c.y;
}

function intersects(a, b) {
var dx = b.x - a.x,
dy = b.y - a.y,
dr = a.r + b.r;
return dr * dr > dx * dx + dy * dy;
}

function newLink(circle) {
return {_: circle, next: null, previous: null, score: NaN};
}

export default function(circles) {
if (!(n = circles.length)) return;
circles = circles.map(newLink);

var a, b, c,
i, j, k,
sj, sk,
n;

a = circles[0], a.score = a._.r * a._.r, a._.x = a._.r, a._.y = 0;
if (!(n > 1)) return;

b = circles[1], b.score = b._.r * b._.r, b._.x = -b._.r, b._.y = 0;
if (!(n > 2)) return;

// Initialize the front-chain using the first three circles a, b and c.
place(b, a, c = circles[2]);
a.next = c.previous = b;
b.next = a.previous = c;
c.next = b.previous = a;

// Attempt to place each remaining circle…
pack: for (i = 3; i < n; ++i) {
place(a, b, c = circles[i]);

// If there are only three elements in the front-chain…
if ((k = a.previous) === (j = b.next)) {
// If the new circle intersects the third circle,
// rotate the front chain to try the next position.
if (intersects(j._, c._)) {
a = b, b = j, --i;
continue pack;
}
}

// Find the closest intersecting circle on the front-chain, if any.
else {
sj = j._.r, sk = k._.r;
do {
if (sj <= sk) {
if (intersects(j._, c._)) {
b = j, a.next = b, b.previous = a, --i;
continue pack;
}
j = j.next, sj += j._.r;
} else {
if (intersects(k._, c._)) {
a = k, a.next = b, b.previous = a, --i;
continue pack;
}
k = k.previous, sk += k._.r;
}
} while (j !== k.next);
}

// Success! Insert the new circle c between a and b.
c.previous = a, c.next = b, a.next = b.previous = b = c;

// Now recompute the closest circle a to the origin.
while ((c = c.next) !== b) if (c.score < a.score) a = c, b = a.next;
}
}
6 changes: 0 additions & 6 deletions src/pack/contains.js

This file was deleted.

11 changes: 8 additions & 3 deletions src/pack/enclose.js → src/pack/enclosingCircle.js
@@ -1,5 +1,3 @@
import contains from "./contains";

export default function(circles) {
return encloseN(shuffle(circles), []);
}
Expand All @@ -20,6 +18,13 @@ function shuffle(array) {
return {head: head, tail: node};
}

function encloses(a, b) {
var dx = b.x - a.x,
dy = b.y - a.y,
dr = a.r - b.r;
return dr * dr + 1e-6 > dx * dx + dy * dy;
}

// Returns the smallest circle that contains circles L and intersects circles B.
function encloseN(L, B) {
var circle,
Expand All @@ -36,7 +41,7 @@ function encloseN(L, B) {

while (l1) {
p1 = l1.circle, l2 = l1.next;
if (!circle || !contains(circle, p1)) {
if (!circle || !encloses(circle, p1)) {

// Temporarily truncate L before l1.
if (l0) L.tail = l0, l0.next = null;
Expand Down
98 changes: 4 additions & 94 deletions src/pack/index.js
@@ -1,7 +1,7 @@
import hierarchy from "../hierarchy";
import rebind from "../rebind";
import enclose from "./enclose";
import intersects from "./intersects";
import enclosingCircle from "./enclosingCircle";
import packCircles from "./circles";

export default function() {
var layout = hierarchy(),
Expand Down Expand Up @@ -43,8 +43,8 @@ export default function() {

function packChildren(node) {
if (node.children) {
packSiblings(node.children);
var e = enclose(node.children);
packCircles(node.children);
var e = enclosingCircle(node.children);
node.children.forEach(translateNode(-e.x, -e.y));
node.r = e.r;
} else {
Expand Down Expand Up @@ -86,93 +86,3 @@ function translateNode(dx, dy) {
node.x += dx, node.y += dy;
};
}

function place(A, B, C) {
var a = A._,
b = B._,
c = C._,
ax = a.x,
ay = a.y,
da = b.r + c.r,
db = a.r + c.r,
dx = b.x - ax,
dy = b.y - ay,
dc = dx * dx + dy * dy;
if (dc) {
var x = 0.5 + ((db *= db) - (da *= da)) / (2 * dc),
y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
c.x = ax + x * dx + y * dy;
c.y = ay + x * dy - y * dx;
} else {
c.x = ax + db;
c.y = ay;
}
C.score = c.x * c.x + c.y * c.y;
}

function newNode(circle) {
return {_: circle, next: null, previous: null, score: NaN};
}

function packSiblings(circles) {
if (!(n = circles.length)) return;
circles = circles.map(newNode);

var a, b, c,
i, j, k,
sj, sk,
n;

a = circles[0], a.score = a._.r * a._.r, a._.x = a._.r, a._.y = 0;
if (!(n > 1)) return;

b = circles[1], b.score = b._.r * b._.r, b._.x = -b._.r, b._.y = 0;
if (!(n > 2)) return;

// Initialize the front-chain using the first three circles a, b and c.
place(b, a, c = circles[2]);
a.next = c.previous = b;
b.next = a.previous = c;
c.next = b.previous = a;

// Attempt to place each remaining circle…
pack: for (i = 3; i < n; ++i) {
place(a, b, c = circles[i]);

// If there are only three elements in the front-chain…
if ((k = a.previous) === (j = b.next)) {
// If the new circle intersects the third circle,
// rotate the front chain to try the next position.
if (intersects(j._, c._)) {
a = b, b = j, --i;
continue pack;
}
}

// Find the closest intersecting circle on the front-chain, if any.
else {
sj = j._.r, sk = k._.r;
do {
if (sj <= sk) {
if (intersects(j._, c._)) {
b = j, a.next = b, b.previous = a, --i;
continue pack;
}
j = j.next, sj += j._.r;
} else {
if (intersects(k._, c._)) {
a = k, a.next = b, b.previous = a, --i;
continue pack;
}
k = k.previous, sk += k._.r;
}
} while (j !== k.next);
}

// Success! Insert the new circle c between a and b.
c.previous = a, c.next = b, a.next = b.previous = b = c;

// Now recompute the closest circle a to the origin.
while ((c = c.next) !== b) if (c.score < a.score) a = c, b = a.next;
}
}
6 changes: 0 additions & 6 deletions src/pack/intersects.js

This file was deleted.

0 comments on commit fddac40

Please sign in to comment.