Flow fields
--
Drawing fluid lines
The year 2022 started as an inspiration for me. I participated in the early days of Genuary, which is a movement on Twitter (mostly) where artists around the world follow a list of daily themes for creating generative art.
If you’ve never heard of these terms, generative art is an artistic expression in which the artist creates a set of rules to be followed. By executing these rules we can generate a creative output. Note that the more variable parameters we have in these rules, the more different the results will be.
Here are some of my results in the first few days:
If you want to see more of my work with generative art, follow me on Twitter, as I always post my results there: https://twitter.com/phcacique
By the way, the topics are always a little vague, which allows for the artist’s greater creativity, including his own interpretation of the theme.
One of the themes I enjoyed working on was the fourth day: The next next Fidenza. I didn’t know the topic and decided to research a little to find out what it is.
Turns out it’s a very interesting piece of art created by Tyler Hobbs (https://tylerxhobbs.com/fidenza) and it’s based on flow diagrams for building curved lines, as in this next piece.
The most interesting thing is that this is one of the best selling works as NFT (Non Fungible Token) and the artist explains the basic steps of the algorithm on his website.
Once I had learned a bit about the concept, I started thinking about the Genuary topic. Remember that the interpretation of the theme is part of the creation of the work. I was torn between trying to reproduce the algorithm by adding something of my own or trying to create something different, but so amazing that it could be as famous as the original, being the next Fidenza.
As I had never used flow fields to draw, I decided to go the first way, so I would learn one more technique for my arsenal.
In this article, I want to show you the main idea of the algorithm and show you some interesting results.
Flow Fields
For this context, a flow field diagram is nothing more than a table containing values that represent the angle that a line must have when passing through a certain location.
You see, this concept can be used in many other ways. As for example in a temperature map, we have a table that represents not the angle, but the temperature in a geographic position.
See, in the figure below, how the weather widget uses this concept on the iPhone.
In this case, for each Cartesian point (x,y) on the map we have an equivalent (i,j) in a table, with the temperature value at that point. By plotting this value on the graph, a mapping is made that tells us which color corresponds to that value within a certain range. In this case, the redder, the warmer, and the greener, the colder.
Thumbs up to the summer heat in São Paulo!
In the same way, imagine that we are going to make a square table in the range from 0 to 360º and that instead of a point of view with a color, we are going to draw an arrow in the direction of that angle.
We would have something like this:
But what determines the angle for each cell? Well, it depends on the context to be presented. For example, we could use that weather forecast API and show the wind direction on a map.
For this specific case, I made a simple function that relates the horizontal position of the cell to the total width of the table, returning the sine value of this number. That is:
α = sin(i / w)
In which:
i = horizontal position in the table
w = number of columns
A tip for having interesting results here is to always vary the angulation of the arrows according to the position in the table. See that if we have more varied functions, we have different results for the same algorithm. So it all depends on the function that generates the angle value.
If I vary the value according to the Y coordinate as well, we can have something like this:
In this case, our function is:
α = sin(i / w) + sin(j / h)
In which:
i = horizontal position in the table
j = vertical position in the table
w = number of columns
h = number of lines
Another possible variation in creating the table is to determine the number of rows and columns for the space of our canvas, which will cause us to have greater or lesser variation of angles in a small space on the screen.
See the result with 3x more rows and columns:
Drawing the lines
Now that we know how to create a table with angles that fill the space, we can think about how to draw the lines.
The concept is quite simple and widely used in various design algorithms.
To draw a line, we need to know the coordinates of two points on the graph. With these values, we can calculate the angle between this line and the X axis.
Another option is to have the desired size of the line and the angle it should form with the X axis (which is closer to what we have, right?)
See the graphic below for a representation of this concept. We want to find the values of point P based on the angle α (which will be the one in our table) and the size of the line segment: R.
What we are doing here is a transformation from the polar coordinate system to the Cartesian system. For this, we will use the trigonometric relations that we know.
We can observe a triangle here, right? In it we have to:
dy = opposite side = y coordinate of P
dx = adjacent side = x coordinate of P
R = hypotenuse = distance from point P to origin
Take the value of cosine, for example. If you remember your college entrance exams well, you know that:
cos = op / hip
That is, the cosine of angle α is equal to the opposite side divided by the hypotenuse of the triangle formed, that is:
cos(α) = dy / R
or:
dy = cos(a) * R
Here we are considering the distance from P to the origin of the Cartesian system, but if we want to have the distance from point P to another point in space, we can just add to the value found the coordinate of that point. So:
dy = y’ + cos(a) * R
In which:
y’ = y-coordinate of the point of origin of the segment R
Analogously, we can compare the sine of the angle, being:
sin = adj / hip
sin(α) = dx / R
dx = sin(α) * R
dx = x’ + sin(α) * R
So we have two equations that give us the coordinates of point P based on the size of the desired line segment and the given angle:
dx = x’ + sin(α) * R
dy = y’ + cos(a) * R
Now, all we need is to choose a point on the screen, check in the table what the drawing angle should be and then establish what the next point would be with the equations we found above.
Now we have a line segment, to have a more complete line, we repeat the procedure several times, now taking the new point as a starting point.
If we use small R values, we will have smoother lines, but we will need to repeat this procedure more times for a longer line.
See a line that was drawn based on our algorithm:
Here, a random point was chosen at one of the edges of the canvas and a line with several segments was drawn. In this case, there were 100 segments of 40px size.
Here are some variations of this method, drawing more or less lines, with different angle determination functions:
Fidenza
What makes the Fidenza algorithm so interesting is the variation in the thickness of the lines, the number of segments that make up each one of them, the size of the table with flow diagram and an extra component: collision detection.
You can add an algorithm that detects if a line collides with another to determine whether or not it can exist, which will give the work a more orderly look. Take a look:
In this case, I varied the sizes of the lines (the number of segments), their thickness, their colors and checked that one did not collide with the other, getting as close as possible to the original Fidenza algorithm.
See how interesting it was? Note that we can still explore this algorithm quite a bit. Can you imagine what it would be like if we used the line area to write words in a poem? We would have a directed flow of reading. Or even if we left long, thin lines and drew flowers at the ends?
What would you do to improve this algorithm and give it your personal touch? If you decide to implement the algorithm in your favorite language, don’t forget to send me the result, ok?