NoC W10: Infinite Pattern
Trees
Link: p5.js Web Editor | Trees (p5js.org)
Idea: Project Extension
In this project, I mainly want to create some objects that can elaborate my midterm ink-wash project. I decided to draw an ink-wash-style tree using recursion. In order to find a proper visual reference, I searched on the internet to find what a tree in an ink-wash painting should look like.
Coding 01: Observation Always the First
From this painting, I can see that the tree is actually very different from the recursion tree that we have drawn from the class. The branches are not equally the same weight and not straight at all.
In this case, I think I need to make some modifications to the codes. I first make the first branch and the second branch slightly different by assigning one of them a random value from 0 – 1 and assigning the other a 1 – value. With this code, the branch will mostly be different from the other.
let branch1 = random(1); let branch2 = 1 - branch1;
After this, the problem becomes the direction of the branches. In the reference painting, the direction is not straightforward and has no limitation of left or right. In this case, I divide the process of drawing a branch into several lines and add the random() when drawing the single branch. This will make it curve and smooth.
for (var i = 0; i < random(2, 5); i++) { var end = new p5.Vector( start.x + sin(deg) * len * 2, start.y - cos(deg) * len * 2 ); line(start.x, start.y, end.x, end.y); start = end; deg += random((10 - len) / ui.branchCurve, (len - 10) / ui.branchCurve); }
In addition, the direction of the recursion branch is also a problem. I add a random number to determine its left or right direction by assigning them positive or negative values. Then I make the direction determined by 1-branch value which means that the more strong the branch is the straight it will be. This is also following the real physic rules 🙂
let branch1 = random(1); let branch2 = 1 - branch1; let angle = random(-0.2, 0.2); drawTree(len * sqrt(branch1), start, deg + angle * (1-branch1)); drawTree(len * sqrt(branch2), start, deg + angle * (1-branch2));
One last modification is the recursion length. I make it determined by sqrt(branchValue) since in this way we can limit it multiplied by a number always minus 1. Also, we can make sure that the stronger branch are able to decrease slower which will make the main branch more straightforward and the slender branches more curve. (code is above)
This is what the final tree looks like:
Coding 02: Decorations!
I plan to have 2 versions of decorations. The first one is the pine trees.
As for the pine trees, the most essential element is the pine leaves or the pine needles. I directly use line() to draw the single needle and rotate it several times to simulate the pine. The length is random which will make it more realistic.
function drawFlowers() { push(); for (let i = 0; i < 8; i++) { push(); rotate((i * PI) / 10 + PI); stroke(33, 40, 33); strokeWeight(0.5); let length = random(5, 8); line(0, 0, length, length); pop(); } pop(); }
Besides the pine needles, also I found that in ink-wash paintings, the leaves are always blurred color blocks(sorry for not using professional jargon). So I use circle() to add another color block on the needles to simulate that effect:
function drawLeaves() { push(); for (let i = 0; i < 5; i++) { rotate((1 * PI) / 5); translate(5, 0); fill(59, 102, 57, 8); noStroke(); circle(0, 0, 20); } pop(); }
And this is the first decoration. The second one is the flowers! I use Vertex to draw the shape of a single flower petal and rotate them to make it a flower.
function drawPink() { push(); scale(0.5); for (let i = 0; i < 5; i++) { push(); rotate((i * PI) / 5 + PI); fill(249, 60, 88, 100); noStroke(); push(); rotate(PI / 2); translate(0, -24); beginShape(); curveVertex(0, 0); curveVertex(0, 0); curveVertex(0 - 5, 0 + 10); curveVertex(0 - 6, 0 + 20); curveVertex(0, 0 + 24); curveVertex(0 + 6, 0 + 20); curveVertex(0 + 5, 0 + 10); curveVertex(0, 0); curveVertex(0, 0); endShape(); pop(); pop(); } pop(); }
Coding 03: GUI Callback, Call Who Back?
This project also has dat.gui embedded. But I use onChange() this time. According to my search result, onChange is a callback function that can be used in dat.gui. With this function, we can execute another function once we make changes on the gui panel:
gui .add(ui, "flower") .listen() .onFinishChange(function reDraw() { let root = new p5.Vector(width * 0.5, height * 0.98); background(255); drawTree(ui.length, root, 0); });
Although I drew a static image and have no draw() function, with onChange(), I can update my canvas immediately after I adjust the gui index. That’s super cool!
Reflection: Recursion is Elegant But My Tree is not That Much T^T
I’ve learned recursion when in the CS course. But I have never thought I can also use it in creative coding. After learning the recursion to draw these patterns, I found that recursion is really elegant and simple in code. I can’t control myself falling into its charm. After all, I feel kind of satisfied with my tree but not that much. It also needs several attempts to generate a satisfying and good-looking tree. I think there are still many parameters that need further adjustment.