Skip to content

Instantly share code, notes, and snippets.

@airhadoken
Last active August 29, 2015 14:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save airhadoken/25440b14998d75708e66 to your computer and use it in GitHub Desktop.
Save airhadoken/25440b14998d75708e66 to your computer and use it in GitHub Desktop.

Making an Art Bot, a walkthrough

I had a great idea, to make a bot that could recreate abstract art in the style of several artists.

Of course this is too big a scope so I started with one that's comparatively easy to replicate in code: Mondrian.

Prelim 0: Drawing on a canvas:

I followed my "Steal everything that isn't nailed down" rule by appropriating a bit of boilerplate code from Paul O'Leary McCann's (@polm23) code for @dupdupdraw. I just needed to have something that would allow me to easily draw to the canvas, and I had worked on some enhancements for @dupdudpdraw previously, so it was an easy decision.

Prelim 1: Drawing random lines.

The first thing I wanted to do was put black lines in a grid at irregular intervals on a white background. The easiest way to start is to first draw a bunch of full-height vertical lines, then subdivide the space between each vertical line plus the left and right edges with horizontal lines.

First we'll define a quick array sum function using Array.prototype.reduce:

function asum(arr) {
	return arr.reduce(function(a, b) {
		return a + b;
	}, 0)
}

Then we'll use that to generate random lines until the widths between those lines exceeds 512 pixels total

var xparts = [];
while(asum(xparts) < 512) {
  xparts.push(Math.floor(Math.random() * 512));
}

Note that I'm going for conciseness here. It's a Schlemiel's algorithm (one where excess traversing happens), but it's hardly a large enough data size to justify writing less terse code.

Now we'll take our white canvas and draw black lines on it, marked by each line.

context.fillStyle = "rgb(0,0,0)";
xparts.reduce(function(offset, xp, i) {
  context.fillRect(offset + xp, 0, 7, 512);
});

and the result of running it once.

I'm calling it a "line" here, but it's really a 7x512px black rectangle. So the vertical lines are now set, but we need horizontal lines to break up the columns now. That's pretty easy using the same pattern, with the addition of mapping over the existing vertical lines (the "xparts"):

var yparts = xparts.map(function() {
  var yp = [];
  while(asum(yp) < 512) {
    yp.push(Math.floor(Math.random() * 512));
  }
  return yp;
});

Drawing between the vertical lines isn't too bad either; we just draw between zero or the sum of all previous offsets, to the current offset.

context.fillStyle = "rgb(0,0,0)";
xparts.reduce(function(offset, xp, i) {
  yparts[i].reduce(function(yoffset, yp, i) {
    context.fillRect(offset, yoffset + yp, xp, 7);
    return yoffset + yp;
  }, 0);
});

Now the script will give us a grid of black lines like this:

skeletal random neoplastic image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment