Skip to content

Commit

Permalink
reuse lcg during pack (#193)
Browse files Browse the repository at this point in the history
* reuse lcg during pack

* remove unused default
  • Loading branch information
mbostock authored Apr 1, 2022
1 parent 9e0ac43 commit c3833aa
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 23 deletions.
5 changes: 1 addition & 4 deletions src/array.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import lcg from "./lcg.js";

export default function(x) {
return typeof x === "object" && "length" in x
? x // Array, TypedArray, NodeList, array-like
: Array.from(x); // Map, Set, iterable, string, or anything else
}

export function shuffle(array) {
const random = lcg();
export function shuffle(array, random) {
let m = array.length,
t,
i;
Expand Down
7 changes: 6 additions & 1 deletion src/pack/enclose.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import {shuffle} from "../array.js";
import lcg from "../lcg.js";

export default function(circles) {
var i = 0, n = (circles = shuffle(Array.from(circles))).length, B = [], p, e;
return packEncloseRandom(circles, lcg());
}

export function packEncloseRandom(circles, random) {
var i = 0, n = (circles = shuffle(Array.from(circles), random)).length, B = [], p, e;

while (i < n) {
p = circles[i];
Expand Down
14 changes: 8 additions & 6 deletions src/pack/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {packEnclose} from "./siblings.js";
import {optional} from "../accessors.js";
import constant, {constantZero} from "../constant.js";
import lcg from "../lcg.js";
import {packSiblingsRandom} from "./siblings.js";

function defaultRadius(d) {
return Math.sqrt(d.value);
Expand All @@ -13,15 +14,16 @@ export default function() {
padding = constantZero;

function pack(root) {
const random = lcg();
root.x = dx / 2, root.y = dy / 2;
if (radius) {
root.eachBefore(radiusLeaf(radius))
.eachAfter(packChildren(padding, 0.5))
.eachAfter(packChildrenRandom(padding, 0.5, random))
.eachBefore(translateChild(1));
} else {
root.eachBefore(radiusLeaf(defaultRadius))
.eachAfter(packChildren(constantZero, 1))
.eachAfter(packChildren(padding, root.r / Math.min(dx, dy)))
.eachAfter(packChildrenRandom(constantZero, 1, random))
.eachAfter(packChildrenRandom(padding, root.r / Math.min(dx, dy), random))
.eachBefore(translateChild(Math.min(dx, dy) / (2 * root.r)));
}
return root;
Expand Down Expand Up @@ -50,7 +52,7 @@ function radiusLeaf(radius) {
};
}

function packChildren(padding, k) {
function packChildrenRandom(padding, k, random) {
return function(node) {
if (children = node.children) {
var children,
Expand All @@ -60,7 +62,7 @@ function packChildren(padding, k) {
e;

if (r) for (i = 0; i < n; ++i) children[i].r += r;
e = packEnclose(children);
e = packSiblingsRandom(children, random);
if (r) for (i = 0; i < n; ++i) children[i].r -= r;
node.r = e + r;
}
Expand Down
9 changes: 5 additions & 4 deletions src/pack/siblings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import array from "../array.js";
import enclose from "./enclose.js";
import lcg from "../lcg.js";
import {packEncloseRandom} from "./enclose.js";

function place(b, a, c) {
var dx = b.x - a.x, x, a2,
Expand Down Expand Up @@ -45,7 +46,7 @@ function Node(circle) {
this.previous = null;
}

export function packEnclose(circles) {
export function packSiblingsRandom(circles, random) {
if (!(n = (circles = array(circles)).length)) return 0;

var a, b, c, n, aa, ca, i, j, k, sj, sk;
Expand Down Expand Up @@ -105,7 +106,7 @@ export function packEnclose(circles) {
}

// Compute the enclosing circle of the front chain.
a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = enclose(a);
a = [b._], c = b; while ((c = c.next) !== b) a.push(c._); c = packEncloseRandom(a, random);

// Translate the circles to put the enclosing circle around the origin.
for (i = 0; i < n; ++i) a = circles[i], a.x -= c.x, a.y -= c.y;
Expand All @@ -114,6 +115,6 @@ export function packEnclose(circles) {
}

export default function(circles) {
packEnclose(circles);
packSiblingsRandom(circles, lcg());
return circles;
}
18 changes: 10 additions & 8 deletions test/pack/deterministic-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import assert from "assert";
import {hierarchy, stratify, pack} from "../../src/index.js";

it("pack is deterministic", () => {
const data = stratify().path((d) => d)([41, 41, 11, 11, 4, 4]
.flatMap((n, i) => Array.from({ length: n }, (_, j) => ({ i, j })))
.map(({ i, j }) => `/${i}/${i}-${j}`));
const root = hierarchy(data);
root.sum(() => 1);
const packed = Array.from(pack().size([100, 100]).padding(0)(root),
({ x, y }) => [+x.toPrecision(5), +y.toPrecision(5)]);
assert.deepStrictEqual(packed, [[50,50],[25,50],[75,50],[50,80.443],[50,19.557],[27.269,16.539],[72.731,83.461],[21.875,50],[28.125,50],[25,55.413],[25,44.587],[18.75,44.587],[31.25,44.587],[31.25,55.413],[18.75,55.413],[15.625,50],[34.375,50],[12.5,55.413],[37.5,44.587],[28.125,60.825],[21.875,39.175],[28.125,39.175],[21.875,60.825],[25,66.238],[12.5,44.587],[25,33.762],[37.5,55.413],[34.375,60.825],[15.625,60.825],[15.625,39.175],[34.375,39.175],[40.625,60.825],[9.375,39.175],[40.625,39.175],[9.375,60.825],[9.375,50],[6.25,44.587],[40.625,50],[43.75,55.413],[31.25,66.238],[18.75,33.762],[31.25,33.762],[18.75,66.238],[21.875,28.349],[28.125,28.349],[28.125,71.651],[21.875,71.651],[6.25,55.413],[71.875,50],[78.125,50],[75,55.413],[75,44.587],[68.75,44.587],[81.25,44.587],[81.25,55.413],[68.75,55.413],[65.625,50],[84.375,50],[62.5,55.413],[87.5,44.587],[78.125,60.825],[71.875,39.175],[78.125,39.175],[71.875,60.825],[75,66.238],[62.5,44.587],[75,33.762],[87.5,55.413],[84.375,60.825],[65.625,60.825],[65.625,39.175],[84.375,39.175],[90.625,60.825],[59.375,39.175],[90.625,39.175],[59.375,60.825],[59.375,50],[56.25,44.587],[90.625,50],[93.75,55.413],[81.25,66.238],[68.75,33.762],[81.25,33.762],[68.75,66.238],[71.875,28.349],[78.125,28.349],[78.125,71.651],[71.875,71.651],[56.25,55.413],[48.438,77.736],[54.688,77.736],[51.563,83.149],[51.563,72.324],[45.313,72.324],[57.813,72.324],[57.813,83.149],[45.313,83.149],[42.188,77.736],[60.938,77.736],[39.063,83.149],[48.438,16.851],[54.688,16.851],[51.563,22.264],[51.563,11.438],[45.313,11.438],[57.813,11.438],[57.813,22.264],[45.313,22.264],[42.188,16.851],[60.938,16.851],[39.063,22.264],[24.144,16.539],[30.394,16.539],[27.269,21.952],[27.269,11.127],[69.606,83.461],[75.856,83.461],[72.731,88.873],[72.731,78.048]]);
const data = stratify().path((d) => d)(
[41, 41, 11, 11, 4, 4]
.flatMap((n, i) => Array.from({length: n}, (_, j) => ({i, j})))
.map(({i, j}) => `/${i}/${i}-${j}`)
);
const packer = pack().size([100, 100]).padding(0);
const pack1 = packer(hierarchy(data).count());
for (let i = 0; i < 40; ++i) {
assert.deepStrictEqual(packer(hierarchy(data).count()), pack1);
}
});

0 comments on commit c3833aa

Please sign in to comment.