Márcio Martins
Everyone knows that hexagons are the bestagons. In this project, I document the process of painting a hexagon wall panel, from the planning to execution.
Paint a wall panel, where it transitions from with solid green to white, via an increasing number of white hexagons.
The wall to be painted is 223 cm high and 400 cm wide. Here are the main rules I decided on:
- I want to simulate panels based on a matrix of probabilities. Each cell will hold the probability of a hexagon being green.
- I want these hexagons to be 20 cm wide.
- However, if an hexagon is in the edge between a green and a white patch, it should have a diameter of 19 cm, to ease the transition. These edge hexagons will be defined by counting the number of white neighbours. If the number of white neighbours is greater than 3, the hexagon will be smaller.
- This code should allow to me programatically generate several panels.
I will not go too in depth into this section, but will instead refer you to the red blob games post that I used to understand the coordinate system for a hexagonal grid. It’s a great post, and I highly recommend it.
If you wish to see the code, you can find it in the generator.R
file.
To decide if a cell is 0 or 1, I will use random generations from from the binomial distribution. I need to start with a matrix that holds the probability of each cell being colored.
First, I will create a matrix to store the probability that a cell is green. So, let’s start by deciding the size our matrix:
hex_radius <- 10
hex_gap <- 1
# Width of the wall section = 400 cm
n_col <- ceiling(500 / ((3/2 * hex_radius))) - 1
# Height of the wall section = 223 cm
n_row <- floor(223 /(sqrt(3) * hex_radius)) -1
So, our wall will be a n_row
by n_col
matrix. I want the wall to
transition from green to white, as we go from left to right. To do so,
I’ll use the logistic function. The logistic function is defined as:
Where
horizontal_grad <- modified_logistic(1:n_col, n_col * 0.8, -0.2)
plot(horizontal_grad, xlab = "Column", ylab = "Green probability")
Let’s visualize what this would look like as a wall panel:
wall <- create_matrix_binom(
matrix(data = 0, nrow = n_row, ncol = n_col),
horizontal_grad,
gain = 0.2,
seed = 13 # Using a fixed seed allows generating the same panel every time
)
draw_panel(wall, 10)
It’s not bad at all! But I want to add some randomness to it. To do so, I will use the perlin noise algorithm to increase the variability of the panel. This algorithm generates a matrix of spatially correlated random numbers. This means that the probability matrix will have some terrain-like properties, and there will be “spots” where the probability of coloring is higher than others.
set.seed(13)
noise <- noise_perlin(dim = c(n_row, n_col), frequency = 0.3)
image(t(noise))
In this image you can see lighter areas, where the probability of coloring is higher, and darker areas, where the probability of coloring is lower. Let’s add this to our wall panel probabilities. We’ll also use some “gain” to increase the bias towards green. After all, this is more art than science.
Now, let’s create the final wall panel using the sum of these two distribution matrices. I will also use a gain to increase the bias towards white. As for the radius of the edge hexagons, I decided that if 3 or more of the 6 neighbors of a hexagon are missing, it will be considered an edge hexagon, with a smaller radius.
wall <- create_matrix_binom(noise, horizontal_grad, gain = 0.05, seed = 13)
draw_panel(wall, hex_radius = 10, missing_neighbors = 3)
Now we’re talking! This is a good starting point. We have some white hexagons in the mostly solid section, and the green hexagons cluster a little more towards the right side. This will be my final version.
I added an option to my function that shows the middle coordinates of each hexagon. This will help me set up the panel before painting it. Let’s print the panel again, but with coordinates and increase the size to make it easier to read:
draw_panel(wall, hex_radius = 10, coords = T, missing_neighbors = 3, cex = 0.8)
In this image, the coordinates show are as follows: x (center, in cm) y (center, in cm) col number - row number
And now it’s time to paint! I used a laser cutter to create stencils to pain, and this is the final result:
I played around with the idea of coloring the hexagons based on the number of neighbors they have. This function assigns a color to a hexagon based on the number of missing neighbors. I didn’t want too much color, so gave up on the functionality, but I’ll leave the code here for future reference.
colors <- define_colors(wall, colors = c("#60AA20", "red"))
draw_panel(wall, hex_radius = 10, missing_neighbors = 3, color = colors)
All of these panels are generated from the same distributions, but you could create your own
wall1 <- create_matrix_binom(noise, horizontal_grad, gain = 1, seed = 13)
draw_panel(wall1, hex_radius = 10, missing_neighbors = 3, color = "#60AA20")
title("Increase bias towards green")
wall2 <- create_matrix_binom(noise, horizontal_grad, gain = 0.05, seed = 20)
draw_panel(wall2, hex_radius = 9, missing_neighbors = 3, color = "#60AA20")
title("Use another seed")
wall3 <- create_matrix_binom(noise, rep(0.5,n_col), gain = 0.3, seed = 13)
draw_panel(wall3, hex_radius = 10, missing_neighbors = 3, color = "#60AA20")
title("No horizontal gradient")
- The
create_matrix_binom
function could be improved to allow for more flexibility in the distribution of the probabilities. Instead of accepting a fixed number of distributions, it should accept a variable number of matrices with the same distribution, plus a vector of weights.