Perlinized Hexagons

In my last post, Hexagons, I talked a bit about how cool hexagons are, and gave one method of creating a hex grid. I also used this image as a header:

This was a bit of a tease, as I didn’t show how to create such an image. But I promised I’d come back around and give some code for it, so here we go.

First, some refactoring

As I’m sure most of you guessed, this image uses Perlin noise, but you could use any other function to get different textures. Check out my articles on flow fields (parts one and two) for more ideas along these lines. I’ll also be using the same Perlin noise library mentioned in the second of those articles.

I’m going to take the basic code from the tiled hexagons in the last article and change it up a bit. Mainly all I’m doing here is removing the translation code and calculating each x, y drawing point directly. Here’s what that looks like:

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.lineWidth = 0.5;

function hexagon(x, y, r) {
  let angle = 0;
  for(let i = 0; i < 6; i ++) {
    context.lineTo(x + Math.cos(angle) * r, y + Math.sin(angle) * r);
    angle += Math.PI / 3;
  }
  context.closePath();
}

const radius = 10;
const ydelta = Math.sin(Math.PI / 3) * radius;
let even = true;

for(let y = 0; y < 900; y += ydelta) {
  let offset = 0;
  let yy = y;
  if(even) {
    offset = radius * 1.5;
  }
  for(let x = 0; x < 900; x += radius * 3) {
    context.beginPath();
    hexagon(x + offset, y, radius);
    context.stroke();
  }
  even = !even;
}

Here’s what that gives us. Notice that I made the radius much smaller and stroked it instead of filling it.

Again, this all builds off of the last article. I had to remove the translation code because I need the exact x, y coordinates of every point of every hexagon, so I can feed that into a Perlin noise function to know how much to shift it.

Adding some noise

The Perlin function is going to make use of this library: https://github.com/josephg/noisejs. Feel free to add that to your project however you’d like. Or add a different noise function. It doesn’t really matter which one you use.

I need a function that’s going to take an x, y coordinate, calculate an offset based on Perlin noise, and return a new, shifted x, y coordinate. Since JavaScript doesn’t let us return multiple values, I’ll return and object with x, y properties. Here’s the function:

function perlinize(x, y) {
  const scale = 0.01;
  const strength = 20;
  const angle = noise.perlin2(x * scale, y * scale) * Math.PI;
  return {
    x: x + Math.cos(angle) * strength,
    y: y + Math.sin(angle) * strength,
  }
}

To revisit how a Perlin flow field works, we’re going to use 2-dimensional Perlin noise, feeding it an x, y value. Since the numbers we’re using will be in the hundreds or even thousands for pixel values, we’ll scale that down a bit with a scale value. This function returns a value from -1 to +1. Other Perlin noise functions sometimes return values from 0 to 1. Either will work just fine, but might need some tweaking. I take that value and multiply it by PI, which will give me a number from -PI to +PI. We’ll call that an angle in radians. In degrees, it would be -180 to +180. And we’ll use the sine and cosine of that angle to create an offset based with a customizable strength. Then, we return the original x, y coordinate + that x, y offset.

Now we just need to alter our code to use the perlinize function. I’ll just show the relevant function:

function hexagon(x, y, r) {
  let angle = 0;
  for(let i = 0; i < 6; i ++) {
    const p = perlinize(x + Math.cos(angle) * r, y + Math.sin(angle) * r);
    context.lineTo(p.x, p.y);
    angle += Math.PI / 3;
  }
  context.closePath();
}

And this is where this gets us.

Each hexagon still perfectly tiles because the Perlinized offset for each shared point of each hexagon should be exactly the same, at least down to an adequate degree of precision.

Shading

Now let’s add some shading. Basically we just want a grayscale value for each hexagon. To enhance the effect, the shading should be coordinated with the offset flow field values, so we’ll use the same Perlin noise settings. Here’s a function that will accomplish that:

function getShading(x, y) {
  const scale = 0.01;
  const value = (noise.perlin2(x * scale, y * scale) + 1) / 2;
  const shade = 255 - value * 255;
  return "rgb(" + shade + "," + shade + "," + shade + ")";
}

This takes an x, y coordinate and returns and rgb color string. Remember that this particular noise function returns -1 to +1. I’ll add 1 to that to have a range of 0 to 2, then divide by 2 to get 0 to 1. Then I’ll calculate a shade value by subtracting that normalized value by 255 and subtracting from 255. A bit convoluted, I know. This is all made a lot easier if you have a map range function. It would look something like this:

const n = noise.perlin2(x * scale, y * scale);
const shade = map(n, -1, 1, 255, 0);

To see how a map function would work, check out this video.

Finally, we just need to alter our drawing code to set the fill color based on the x, y of each hexagon, and do a fill before the stroke:

  for(let x = 0; x < 900; x += radius * 3) {
    context.beginPath();
    hexagon(x + offset, y, radius);
    context.fillStyle = getShading(x + offset, y);
    context.fill();
    context.stroke();
  }

And here is the result:

Summary

Don’t stop here. There are all kinds of variables and different rendering techniques to experiment with here. This is all just about taking two different concepts – hex grids and Perlin noise – and combining them in a creative way. This worked out pretty well, but there are an infinite other combinations waiting for you to discover.

Late breaking comment…

One thing I had in writing this but totally spaced out on while writing the article is that this code is extremely unoptimized. Every single x, y point here is calculated and then “perlinized” three times. OK, some of the ones on the edges only get calculated only once or twice, but you get the point.

Strategies for optimizing would probably involve creating a single point grid one time and then using a loop to draw using the pre-calculated points in that grid.

This is left as an exercise for the reader.™

Hexagons

I know what you’re thinking. I’m thinking it too. So let’s stop tiptoeing around the subject and just say it out loud:

Hexagons are freaking cool.

Oh… you weren’t thinking that? Oops. My bad. I just assumed. Must be just me. So let me state my case on why hexagons are so damn neat.

First, let’s get some definitions out of the way. A hexagon is a six-sided polygon. And just to be clear, the hexagon I’m talking about is the regular, convex polygon type of hexagon. Which is to say that all of its sides are the same length and all of its angles are the same (120 degrees).

Cool things about hexagons

  1. A hexagon is made up of six equilateral triangles. Each triangle is composed of three 60-degree angles, and three sides that are the same length, which is the radius of the hexagon.
  2. This means that each side of the hexagon is the same length as its radius.
  3. Hexagons are one of the three regular tessellations. That means you can take a bunch of hexagons and pack them together perfectly with no space left over. The only other two regular polygons you can do that with are squares and triangles. And in a sense a hexagon tiling and triangle tiling wind up being exactly the same thing. See #1 above.
  4. Hexagons appear pretty regularly in nature. The bounds of any snowflake, the cells of a beehive, basalt columns, the compound eyes of insects, the packing of bubbles, the cloud formations on the north pole of Saturn.

    By NASA/JPL-Caltech/Space Science Institute – http://www.nasa.gov/sites/default/files/pia18274_full.jpg, Public Domain, https://commons.wikimedia.org/w/index.php?curid=33952464
  5. When you tile hexagons, exactly six hexagons will surround every other hexagon. If you connect the center points of the six surrounding hexagons, you get another, larger hexagon.
  6. If you connect every other point of a hexagon to its center, you have a perfect isometric cube.

All of these points, and more, make hexagons a great shape to play with if you’re doing any kind of creative coding.

So, you want to draw a hexagon…

Now you’re saying, “Gosh, Keith. These hexagons sound wicked pissah! How can I draw one???” Well, let me tell you.

You need three parameters, the x, y position and a radius. You can also throw in a rotation parameter, but I’ll start without that. I’m going to do this in JavaScript, and assume there’s an 800×800 canvas element on the page with an id of “canvas”. You should be able to easily convert this code to whatever language you are working with.

const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");

function hexagon(x, y, r) {
  context.save();
  context.translate(x, y);
  let angle = 0;
  for(let i = 0; i < 6; i ++) {
    context.lineTo(Math.cos(angle) * r, Math.sin(angle) * r);
    angle += Math.PI / 3; // 60 degrees
  }
  context.closePath();
  context.restore();
}

context.beginPath();
hexagon(400, 400, 100);
context.stroke();


Run that, and we get a very nice hexagon. You can fill it instead of stroke it, make a bunch at different positions and of different sizes and colored differently. I’m not going to dive into the code there much, as it’s pretty simple.

Note, that I’m keeping it super simple here. You can optimize this, you can rework this, make it more reusable, make it draw regular polygons of any number of sides, pass in a rotation value and set angle to that instead of zero, and on and on. Maybe you want to pass in the context, or make the function a method of the context object. And you should do all of that. Or some of that. Or however much of that you want to do. But I’m in charge today, and we’re doing hexagons. And we’re doing them simple.

What about that tessellation stuff?

Yeah, now let’s make a bunch of hexagons and stick them together perfectly to see them tile. With squares, this is really easy. Loop through on the y and x axes and draw a square at each point. With hexagons, it’s a bit trickier. There are certainly multiple ways to go about this. I’ll give you one method, but if you come up with something that fits your needs better, go for it.

First of all, note that with the hexagon we drew above, it’s a bit wider than it is tall. Here, I’ve draw lines from the center to each point of the hexagon.

We can see that the width is two times the radius. Simple. But the height is a bit less. Knowing a bit of trigonometry, we can figure out that the distance from the center to the top of the hexagon is the sine of 60 degrees times the radius. So the height of the hexagon is twice that.

Here’s a hex grid.

The main thing to note is that unlike a grid of squares or rectangles, each alternate row or column overlaps with the previous and next row or column. If you consider we have even rows and odd rows, the even rows are horizontally offset from the odd rows by 1.5 times the radius. To see that, consider the horizontal distance from the center of one hexagon to the center of one of the hexagons immediately to its left or right, but in an alternate row.

Within each row, the center-to-center horizontal distance between each hexagon is 3 times the radius. To see that, consider the distance from the center of one hexagon to the center of the hexagon directly to its left or right in the same row.

And vertically, each row is offset from the previous row by one half a hexagon height, or sin(60) * radius.

Knowing all of that, we can put together a double for loop that draws a grid.

const radius = 50;
const ydelta = Math.sin(Math.PI / 3) * radius;
let even = true;

for(let y = 0; y < 900; y += ydelta) {
  context.save();
  if(even) {
    context.translate(radius * 1.5, 0);
  }
  for(let x = 0; x < 900; x += radius * 3) {
    context.beginPath();
    hexagon(x, y, radius);
    context.stroke();
  }
  context.restore();
  even = !even;
}

I’ll walk through this code quickly. First we set the radius to whatever. 50 in this case. Then we calculate how far apart to place each row. We said that was sin(60) * radius. 60 degrees is PI / 3 radians. And I’ll set an even variable to true to know whether we are on an even or odd row.

Then we create a double for loop on y and x. I have an 800 pixel canvas, but I looped through to 900 on each axis so I didn’t wind up with any empty space on the right or bottom edges. The y loop increments by ydelta and the x loop increments by radius * 3, all as discussed above.

If we’re on an even row, I’ll translate the canvas by radius * 1.5. Note that I did a context.save() before that, and follow up with a context.restore() at the end of the loop.

Inside the inner x loop, I just draw the hexagon. And at the very end of the loop I set even to !even to make the next row be odd, meaning it will not get that horizontal translation applied.

And now you can make a hex grid. Of course, you can fill each hexagon instead of stroking it. If you used different colors, that works out well, but if you are using all the same color, they’re all going to blend in together. A trick to handle that is to reduce the radius you pass into the hexagon function like so:

context.beginPath();
hexagon(x, y, radius - 5);
context.fill();

This gives you a 10 pixel border between each hexagon (5 pixels on each side).

Another strategy for filling a space with hexagons is to start with a single hexagon at the center and draw six new hexagons around that one and continue out in rings from there.

Hexagons are also a common tiling pattern in various strategy games. In that case, you need a bit more logic to figure out which six tiles are adjacent to any given tile and how to move from one to the next. Beyond the scope of this tutorial.

But what about that cool image at the top with the warped, 3d-looking hexagons?

Yeah, that one is pretty neat, isn’t it? Try it for yourself. I’ll write up another article on how I did that soon.

2018 in Review

Here were are again… At the rate I’m going here, this blog will just be a collection of annual years in review.

Anyway, an interesting year. For most of my programming career, I’ve had my job and then my personal projects. This past year drew to a close, the day job took a lot more of my focus. In fact, for the last 2-3 months at least, I haven’t really had any big personal projects. Work has been taking most of my attention. That’s not a bad thing. I’ve taken on a new role, really a whole new career path at work and I’ve grown in so many ways. More on that later.

Personal Projects

As far as personal projects, I started the year being into the Rust programming language, but before too long I got way more into Go. As far as programming languages, Go has excited me more than anything else has in many years. It takes a lot of the strengths of many different languages. It’s a compiled, strongly-typed systems programming language, but at the same time it can be expressive enough to make it really fun to do creative coding with. As with Rust, I have been generating graphics with bindings for Cairo. Initially I was using go-cairo. But as that has seen very little actual work over the last couple of years, I wound up forking it and doing a lot of work on it myself, resulting in my own go-cairo. I also ported the bitlib library that I’ve used in other languages over to bitlib for Go or blgo.

GIFs
I created a lot of animated GIFs with the above setup. And a couple months of the year I led a gif-a-day kind of thing on twitter. #MayContainGifs and #GulyIsForJifs. These were great fun and it was really nice to have other people playing along.

Wine

I also did two side projects for other people in 2018. I was approached by the designer, Jenny Doll. She was working with the Anarchist Wine Company and wanted to create a series of generative, data-driven labels for a line of wines. Yes, please. The wines lines are called Anarchist, Philosopher, Rosé Against the Machine, and Conspiracy Theory. For each group, we took data relating to historical anarchists, philosophers, disruptors and conspiracy theories and worked them into a generative design. For example, a list of philosophers, their birth dates, date of death and some other key date in their life. These were interpolated into various “hot spots” on a square canvas. Then I used my Random Lissajous Web algorithm (one of the few techniques I can say with a good amount of confidence that I invented) and gave each data set some different parameters. The web also reacted in different ways to the aforementioned hot spots, and each set had a different color palette. In the end, the result is very abstracted from the original data. But damn, they looked cool once they wound up on the bottles.

Elixir

Another project came up at just about the same time and wound up being very similar. I was approached by Design Science UK to create some graphics for Elixir Europe, a community for European life science organizations. They needed images for their 2017 annual report. A full back/front cover spread and full page images for each section. They also wanted something like the Random Lissajous Webs, but in a way that represented the idea of each section of the report: platforms, communities, activities, governance. These again got pretty abstracted from the concepts. Essentially what I wound up doing was generating lots of different images with random parameters, picking ones that I liked and kind of matching them up with the section concepts. You can browse the actual publication here:

Both this and the wine project gave me some practice in creating high resolution images. This would have been a problem in my previous platform of choice, HTML/JS/Canvas. But generating these in Go worked out really great. The final challenge came a few months later. Elixir wanted a large banner for a conference they were presenting at. The banner would look something like this:

But the required dimensions were roughly 11 feet by 7 feet, at a resolution of 150 dpi! This wound up being so large that even Go/Cairo choked on it. I wound up having to render it in four different overlapping tiles and then stitch it back into one giant image.

Work

For the last almost two years, I’ve been working at Notarize. We do on-line notarizations. Not the sexiest thing in the world, but I think it’s a great example of taking some important function that has not really changed in centuries and bringing it into the modern world. In addition, we’ve extended the service so that we are able to do full mortgage closings completely on line. You can buy a house, without ever leaving your house. You can buy a house on your phone. I mean fully buy a house. Sign all the documents, get them notarized. Done deal. You own the house. The company is changing laws and revolutionizing the industry. When I started, I was the 7th engineer. At this point, there are nearly 40 of us in the engineering department alone.

That’s me, front and center with the blue shirt.

In November of 2017, I made the first big career jump in ages, becoming an engineering manager, and wound up managing something like 10-11 other developers. At the end of 2018, the person in charge of engineering gave notice. There was nobody in the wings to take over his duties and it’s not the kind of hire you get overnight. So, to make a long story short, I’m now the Director of Engineering of 38 engineers. Factually, I’m leaning heavily on one of the other managers, who is taking on about as much extra stuff as I am, but he didn’t want the title. I didn’t particularly want it, but I didn’t really want to see the position go unfilled. In short, I think (hope) I am better than nothing.

This is a huge life change for me. It means I’m writing virtually no code at work anymore. And that’s OK. The me of a few years ago can’t believe that I’m saying it’s OK, but it is. We have a really high quality team of amazing developers here who are more than capable of cranking out code day and night. I’m focusing on process and code quality and keeping this crew of 40 evil geniuses happy and productive and not killing each other.

2019

Like last year, I’m not really making any specific resolutions, but I have some guidelines I’d like to focus on a bit better this year. I published these last year.

To summarize,

  1. Creation is greater than Consumption.
  2. Enlightenment is greater than Entertainment.
  3. Advice is greater than Authority.
  4. Commendation is greater than Condemnation.

I think I did pretty well on 3 and 4 last year. I don’t feel like I created enough in 2018 though. And what I created was more for entertainment than enlightenment.

So for 2019 I’m going to work on 1 and 2. I have two ideas for books that have been floating around in my head. One for several years. I’ve started these both a number of times. I plan to actually finish both of them this year. Watch this space for more details. I’d also like to publish more tutorials and articles. Maybe even some more videos. But let’s start with some more tutorial-based blog posts here.

I’m also doing less and less on “social media” these days. It’s become less and less social and more and more media. I left Facebook for good this year and it was one of the best things I did for myself. I’m about -this- close to closing my Instagram account. But I do follow some family members there, which is the only reason I’m keeping it at this point. I played with Mastodon a bit earlier in 2018, but despite all its promises of being a social media utopia, it’s not for me. So I’m down to Twitter as the only thing I really check on a regular basis. And I’m trying to do less and less there as well. Oh, no, wait. I’m also on Reddit regularly, though I don’t post a lot. Just the odd reply here and there, and my interests are constantly changing. To be honest, I miss the old days of forums. I’ve considered setting up some kind of creative coding forum or discourse. Something I will look more into this year, unless someone knows of a good one out there already. But for now, the guideline is going to be “tweet less, blog more”.

Speaking of guidelines, a few others I want to note for myself to try to follow in 2019 are John Maeda’s Four Rules from 1999:

  1. Don’t speak ill of others.
  2. Avoid passive aggressive behavior.
  3. Listen broadly, but don’t waffle on decisions.
  4. When in error — admit, apologize, move forward.

And while we’re at it, here’s a gem from Woody Guthrie – his resolutions from 1943. Holds up pretty well, IMO. I love the conditional on number 3. 🙂

Happy 2019 all!