Plot #1: Learning Hexagons

Plot #1: Learning Hexagons

As a software engineer that builds web applications, it took me some time to wrap my head around how generative art is rendered. Web browsers use a box model for positioning, which means everything is encapsulated in a box and you manipulate the contents within it.

This is very different from the standard x, y-coordinate system.

Since I'm familiar with Python, I decided to use vsketch as my primary tool. This is a really incredible piece of open source software developed specifically for creating pen plotter art. The framework is built to mimic Processing, a popular open source software tool for creating generative art that has a wider set of capabilities.

The real benefit to this approach is that I can learn how to create generative art with Processing and apply those principals to vsketch. I'm currently reading Generative Art: A Practical Guide Using Processing (free PDF).

Naïvety can be useful

When you have an infinite set of possibilities, where do you even start? Drawing a line, sure, easy enough. But where's the sweet spot between bewildering complexity and an accomplishable challenge?

I decided to start with a "simple" hexagon. I figured there must be some hexagon or polygon method that could create this basic shape. And there is, in fact, a polygon method, but I wanted to know how to make one from "scratch," er, math.

What I didn't realize was that a hexagon, or any regular polygon for that matter, is calculated using a circle. After doing some googling, I came across this excellent guide that details exactly what a hexagon is and how to create a grid of hexagons.

After dabbling for hours, things started to take shape. There are some serious hacks in this code snippet, but it does the job:

spacing = vsketch.Param(0.0, step=0.01, decimals=2)
nested_count = vsketch.Param(11, step=1)
rows = vsketch.Param(11, step=1)
cols = vsketch.Param(11, step=1)

def draw(self, vsk: vsketch.Vsketch) -> None:
    vsk.size("a4", landscape=False)
    vsk.scale("cm")

    radius = 1
    points = 6

    class RegularPolygon:
        def __init__(self, points=6, radius=1):
            self.points = points
            self.radius = radius
            self.coordinates = []

        def generate_coordinates(self, vsk):
            self.coordinates = []
            for i in range(self.points):
                with vsk.pushMatrix():
                    angle = 2 * math.pi * i / self.points
                    x = self.radius * math.cos(angle)
                    y = self.radius * math.sin(angle)
                    self.coordinates.append((x, y))
            return self.coordinates

    mid_column = self.cols // 2

    for row in range(self.rows):
        with vsk.pushMatrix():
            offset = vsk.random(.25, .5)
            is_odd_row = row % 2 == 1
            absolute_offset = abs(offset)
            offset = absolute_offset if is_odd_row else -absolute_offset
            for col in range(self.cols):
                with vsk.pushMatrix():

                    rows_remaining = self.rows - row
                    begin_trimming = (mid_column + 1) - rows_remaining
                    if begin_trimming > col:
                        continue
                    for i in range(self.nested_count-1, -1, -1):
                        size = (i + 1) / self.nested_count
                        polygon = RegularPolygon(points, size)
                        coordinates = polygon.generate_coordinates(vsk)

                        if mid_column - row <= col:
                            vsk.polygon(
                                coordinates,
                                close=True,
                            )

                        vsk.translate((size / self.nested_count) * offset,
                                      (size / self.nested_count) * offset)

                vsk.translate((3 / 2 * radius) + self.spacing,
                              (math.sqrt(3) / 2 * radius) + self.spacing)
        vsk.translate(0, (math.sqrt(3) * radius) + self.spacing)

I'm not going to break this code down, but, in essence, it creates nested hexagons in a "diagonal" grid and trims the edges to create a unified shape. It needs more work to make it truly dynamic, but it's a good start.

Next Steps

Is this generative art? Sort of. Purists would contend that there needs to be more randomness involved. And while that's true, I don't think you should inject too much randomness while you're learning the basics. Randomness is easy to add once you understand the basic concepts and mental models.

Nested hexagons, with a twist
Topographic hexagons
Vertices from hexagons

These pieces turned out much better than I expected for my first plots. The next step will be experimenting with some different mediums: black cardstock and various pens.

Of course, I'll also be dabbling with new algorithms, increased randomness and reporting on everything I learn.

You can follow me on Instagram for snapshots of the work. (And, yes, the photography needs some improvement 🙃)