Skip to content

Commit

Permalink
Fix the Squarification algorithm. The largest areas must be placed fi…
Browse files Browse the repository at this point in the history
…rst.
  • Loading branch information
rmmh committed Dec 12, 2015
1 parent a5c3115 commit 318c345
Showing 1 changed file with 19 additions and 10 deletions.
29 changes: 19 additions & 10 deletions webtreemap.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ var kBorderWidth = 1;
// TODO: do this with a nested div to allow it to be CSS-styleable.
var kPadding = 4;

// x/y ratio to aim for -- wider rectangles are better for text display
var kAspectRatio = 1.2;

var focused = null;

function focus(tree) {
Expand Down Expand Up @@ -108,7 +111,7 @@ function position(dom, x, y, width, height) {
function selectSpan(nodes, space, start) {
// Add rectangle one by one, stopping when aspect ratios begin to go
// bad. Result is [start,end) covering the best run for this span.
// http://scholar.google.com/scholar?cluster=5972512107845615474
// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.36.6685
var node = nodes[start];
var rmin = node.data['$area']; // Smallest seen child so far.
var rmax = rmin; // Largest child.
Expand All @@ -124,10 +127,10 @@ function selectSpan(nodes, space, start) {

// This formula is from the paper, but you can easily prove to
// yourself it's taking the larger of the x/y aspect ratio or the
// y/x aspect ratio. The additional magic fudge constant of 5
// makes us prefer wider rectangles to taller ones.
var score = Math.max(5*space*space*rmax / (rsum*rsum),
1*rsum*rsum / (space*space*rmin));
// y/x aspect ratio. The additional magic fudge constant of kAspectRatio
// lets us prefer wider rectangles to taller ones.
var score = Math.max(space*space*rmax / (rsum*rsum),
kAspectRatio*rsum*rsum / (space*space*rmin));
if (last_score && score > last_score) {
rsum -= size; // Undo size addition from just above.
break;
Expand All @@ -151,6 +154,15 @@ function layout(tree, level, width, height) {

var pixels_to_units = Math.sqrt(total / ((x2 - x1) * (y2 - y1)));

// The algorithm does best at laying out items from largest to smallest.
// Sort them to ensure this.
if (!tree.children.sorted) {
tree.children.sort(function (a, b) {
return b.data['$area'] - a.data['$area'];
});
tree.children.sorted = true;
}

for (var start = 0, child; child = tree.children[start]; ++start) {
if (x2 - x1 < 60 || y2 - y1 < 40) {
if (child.dom) {
Expand All @@ -160,11 +172,8 @@ function layout(tree, level, width, height) {
continue;
}

// In theory we can dynamically decide whether to split in x or y based
// on aspect ratio. In practice, changing split direction with this
// layout doesn't look very good.
// var ysplit = (y2 - y1) > (x2 - x1);
var ysplit = true;
// Dynamically decide whether to split in x or y based on aspect ratio.
var ysplit = ((y2 - y1) / (x2 - x1)) > kAspectRatio;

var space; // Space available along layout axis.
if (ysplit)
Expand Down

0 comments on commit 318c345

Please sign in to comment.