This is the third part of a series of texts on various methods for creating generative art with random numbers. The first part is Shepherding Random Numbers, and the second part is Shepherding Random Grids. This text does not rely too heavily on the previous ones, but I recommend looking at them even so.
In this part I will describe a simple form of differential growth. I have written about this technique previously in my essay On Generative Algorithms. Specifically in the following sections: Differential Line, Differential Mesh, Differential Mesh 3d. Moreover, several other people have done similar work. My favorites are Floraform by Nervous System, and Cellular Forms by Andy Lomas.
You can click or tap all animations to restart them.
Differential Growth is a way in which biological systems develop shape. By controlling the rate at which different parts of a surface grows, you can control the shape it grows into.
In this case we will consider lines, not surfaces. However the principle is similar. Some good examples of shapes that inspired this particular system are walnuts, flower petals and the layers of cabbage.
While differential growth in nature is a multi-layered and complicated process, it can be simulated with only a few factors:
- Nodes and Edges: nodes are connected to a certain number of neighboring nodes through edges.
- Attraction: connected nodes will try to maintain a reasonably close proximity to each other. In the figure below attraction happens between connected nodes in the loop.
- Rejection: nodes will try to avoid being too close to surrounding nodes (within a certain distance). Rejection fores are indicated by cyan lines in the figure below.
- Splits: If an edges gets too long, a new node will be injected at the middle of the edge.
- Growth: in addition to the splits, new nodes are injected according to some growth scheme. I will describe this further soon.
The simplest kind of growth scheme is to randomly select an edge, and insert a new node at the middle of it. To reduce clutter, you should avoid inserting nodes if the edge is too short. This will tend to lead to collisions.
The intensity at which new nodes are inserted will greatly impact the rate of growth as well as the shape, so there are a number of things to experiment with.
This section is a little more technical.
So far the growth has been uniform. One way of introducing some variation in growth is to prioritize parts of the structure where the curvature of the surface (line) is greater. Below we indicate the curvature with cyan circles. Larger circles indicate greater (estimated) curvature.
One way to estimate how much the line bends at a given point is to compare an edge to one of it's neighbouring edges. The greater the angle between them, the greater the curvature. The value I display in the figure below is the following: for normalized vectors (edges) a and b, the curvature is 1-abs(dot(a, b)). I should point out that this is not how you calculate the actual curvature.
The next challenge is to sample edges to split such that we tend to split edges that have greater curvature. If we use the formula listed above, we will get a number for the curvature between which ranges between 0 and 1. We then generate a random number (from a uniform distribution) between 0 and 1. We only split the edge if the edge is sufficiently long, and if this random number is less than the value of the curvature. That way, greater curvatures will tend to be selected more frequently. We can repeat this process for every edge, in every iteration.
This process is quite computationally intensive. The most costly operation is to find nods that are in the vicinity of a given node, so that you can calculate the rejection forces. A useful tip for implementing this in an efficient manner is to use k-d trees for looking up nearby nodes. Most k-d tree implementation will let you look up all entries within a certain radius of a given point, which happens to be exactly what you want in this case.
Below is a structure that will grow a little more intricate than the previous samples.
By drawing strokes along the outline of the structure as it grows, we get a radically different result. You should also note that we are not restricted to closed loops, you can easily extend this logic to work with lines, or perhaps even grids?
Hopefully this was an understandable intro to the idea of differential growth.
You can also extend this to surfaces. The major difference is that you will have to handle a triangular mesh structure of some kind, which is a little more complicated.
Finally, if you would like to read up on the actual biological processes, I'd suggest starting with Morphogenesis.