Sol LeWitt's Wall Drawing 614 with D3.js Treemap and Randomization

November 4, 2016
Category: Sol-LeWitt
Tags: D3.js

Aren’t familiar with Sol LeWitt or his art? Read this.

Wall Drawing 614: Rectangles formed by 3-inch (8 cm) wide India ink bands, meeting at right angles.

Here is a preview:

Open my D3 implementation of Wall Drawing 614 in a new window →

It is also available as a block.

Design Details

  • LeWitt’s instructions are pretty open. The drafter can choose the number of rectangles. I chose 40.
  • Since screens work in pixels instead of inches, I chose to make the bands 15px wide. I tried various configurations and I think 15px
  • I chose to conform this to the size of the viewer’s window and have each display randomly generated, so each viewer will have a unique experience.
  • Just as the size of the wall this piece was implemented on determined the density of the lines, the size of the window will determine the density. You can try out different densities by resizing the screen. The illustration will rebuild after you resize the window.
  • If you like this, I also made a version that you can customize.

Technical Details

  • The placement and size of the rectangles are handled by d3.treemap().
  • I tried a few different tiling methods and decided to use d3.treemapBinary. I liked the look of it the best.
  • Each viewer gets a unique experience because the size of each rectangle is set at random on each page load.

The underlying data set

So much of implementing these drawings with D3.js rely on making a solid, usable data set to join elements with. (After all, D3 stands for data-driven-documents.) Creating these data sets is where I spend most of my time. Once I’ve created them, everything else follows pretty quickly. Here is the function that creates the underlying data set on this piece. I owe major props to Eric Davis on helping me figure this out. I was getting close, but nothing I made was accepted by treemap(). He helped me identify my issues, which included making this data set and then passing it to d3.hierarchy before passing it to treemap.

function getRandomArbitrary(min, max) {
	  return Math.random() * (max - min) + min;
}

function treeData() {
  var obj = {name: "rect"};
  var children = [];
  for (var row = 0; row < 40; row++) {
    children.push({id: row, value: getRandomArbitrary(60, 1000)})
  }
  obj['children'] = children;
  return obj;
}

Working with treemap

One of the difficulties of D3.js transitioning from v3 to v4 recently is that most of the tutorials you can find on the web no longer work. Thankfully Mike Bostock created a great example of a treemap with v4 that I referenced quite a bit.

After a few passes through the documentation and a call on my friend Eric Davis, we figured out how to pass data to d3.treemap():

  1. Create a data set that has a clear parent-child structure with unique parents
  2. Pass this to d3.hierarchy
  3. Call .sum() on d3.hierarchy and sum the values before passing it to treemap
  4. Pass it to treemap

See below:

var width = window.innerWidth + 30,
	height = window.innerHeight + 30;
var format = d3.format(",d");

var treemap = d3.treemap()
    .size([width, height])
    .padding(15)
    .round(true)
    .tile(d3.treemapBinary);

var tree = d3.hierarchy(tree);

tree.sum(function(d) { return d.value; });
// always log for debugging!
console.log(tree);

treemap(tree);

d3.select("body")
	.selectAll(".node")
	.data(tree.leaves())
	.enter().append("div")
	  .attr("class", "node")
	  .attr("title", function(d) { return d.data.id; })
	  .style("left", function(d) { return d.x0 + "px"; })
	  .style("top", function(d) { return d.y0 + "px"; })
	  .style("width", function(d) { return d.x1 - d.x0 + "px"; })
	  .style("height", function(d) { return d.y1 - d.y0 + "px"; })
	  .style("background", "#fff");

Rebuilding on screen resize

When you resize the screen, the divs gets destroyed and then rebuilt based on the new screen size. Here is the function I use to handle that. It is the same thing I used on 86 and on my Jekyll posts heatmap calendar. I wrapped the D3 instructions in its own function, called that function on first load, then wrote a function to destroy the node divs when the screen is resized and reexecute the D3 instructions after the screen resize ends. I’m sure it can be done with regular javascript, but jQuery makes this kind of thing fast and easy:

// run on first load
sol614();

$(window).resize(function() {
    if(this.resizeTO) clearTimeout(this.resizeTO);
    this.resizeTO = setTimeout(function() {
        $(this).trigger('resizeEnd');
    }, 500);
});

//resize on resizeEnd function
$(window).bind('resizeEnd', function() {
	 d3.selectAll("div.node").remove();
	 sol614();
});

Want to dig in a little further? Check out my implementation and view source. All of the specs are there.

Tools Used

  • D3.js - The D3.js library is well-suited to Sol LeWitt’s works. D3’s foundational principle of joining data to elements is easy to apply to LeWitt’s drawings.
  • jQuery - I’m using jQuery to detect changes in the window size and trigger removing & rebuilding the visualization.

Inspiration and References

Detail shot of the version on display at MASSMoCA:

Detail shot of the version on display at MASSMoCA:

Another shot of the version on display at MASSMoCA:

Detail shot of the version on display at MASSMoCA:

If you like this, I also made a version that you can customize. Check it out.

Find this post useful?

Buy me a coffeeBuy me a coffee