Fractal Canopy
Introduction
Expected Time: 90 min
In this tutorial we will create a fractal canopy using Python. This is an example of a script that will almost be impossible to recreate in Grasshopper only, because it involves recursion. In about 100 lines of code, we will develop several functions, to create our final fractal structure.
This tutorial is for students that already have experience in Python programming. Do not expect an extensive review on how functions and methods work in this tutorial. The final Python script is available in the final chapter of this tutorial.
Background Information
Fractals are mathematical shapes that exceed their topological dimension. [1] Simply said, in a fully calculated fractal geometry, you can zoom in infinitely and you will see repeating structures. One of the most famous fractal shapes is the Mandelbrot set. However, in this tutorial we will focus on a more simple structure: the fractal canopy.
A fractal canopy starts with one line. The end of this line will split up in two branches. This process continues until the structure reaches a certain depth. Using the algorithm of a fractal canopy, you can create tree-like structures. The amount of lines in the tree can be expressed by the following formula:
Where:
- n is the amount of iterations (amount of depth levels)
- i is the iteration (depth level)
- branches is the amount of split ups from each line
As you can see, the amount of branches increases exponentially by the amount of iterations (levels or depth of the tree). Therefore, in our script, we should make sure that we define a clear limitation to this depth. Otherwise, our Grasshopper application, will definitely crash.
The script we are about to create, will by no means be the fastest method to generate a fractal canopy. However, without using any CPython libraries or parallel computing, this is as fast as we can get it for now.
Setting up our Grasshopper script
Start your script by adding a GhPython component to the canvas.
- Add a GhPython component to the canvas
Change the names of the inputs and outputs of the Python component. Make sure you type the names exactly as in the example image. Do not add capital characters.
- Add some inputs to the Python component ;
- Rename the inputs to: "origin", "branches", "iterations", "total_angle", "length" and "length_reduction";
- Rename the a output to "fractal"
Now you need to set the correct Type Hint for all input.
- Change the origin input to Point3D
- Change the Length_Reduction to Float
- Change all other inputs to Integer
Add a Point parameter and some Number Sliders to the inputs. Make sure you edit the ranges of the Number Sliders, as explained in the illustration. If you do not follow these guidelines, you risk that your model will crash after executing the script.
- Connect a Point parameter to the origin input
- Add Number Sliders to all other inputs
- Edit the settings of the Number Sliders as explained in the image
If you do not change the lower bound of the iterations Number slider to 1, the script will crash and you may loose your work. Make sure you change it to 1!
Transformation function
In this chapter, we will create a function that is able to transform the origin point to an endpoint location. In a later stage, we will use this origin and endpoint to create the branch, visualized as a line.
Open the Grasshopper Python Script Editor. Change the description of the inputs and outputs in the multiline string.
- Open the Grasshopper Python Script Editor
- Change the description of the inputs and output in the multiline string
In this script we will use the Rhino.Geometry library, since it is more efficient than the Rhinoscriptsyntax. Furthermore, we need the Math library to convert angle degrees to radians.
- Import the Rhino.Geometry library as rg
- Import the math library
Create a transformation function. For the transformation function, we will need the inputs as shown in the following image: start (the startpoint of the branch), x (translation in the x direction), y (translation in the y direction), z (translation in the z direction), rotationAxis (the axis that is used to rotate the point around, a vector) and finally angleDeg (the amount of degrees to rotate around the rotationAxis. We close the function with a return end statement.
- Write the transformation function similar to the following image:
As you can read in the RhinoCommon API, transformations can be achieved using several methods. In our case, we will use the translation and rotation method.
Transformations of geometry in Python work quite differently than Grasshopper. In Python, you need to create a so called "transformationmatrix" first, which is applied on some geometry. Our transformation consists of a translation and rotation. Let's first review the RhinoCommon documentation about the translation function. The input of a translation function should be a Rhino.Geometry.Vector3d type. This translation results in a transformationmatrix.
First we need to convert our angle in degrees to radians. Convert the angle value to radians using the math module.
- Convert the angle degrees to radians
Now we create a translationmatrix using the method we found earlier in the RhinoCommon API.
- Create a translationmatrix using our function parameters
Similar to the translation method, we can also create a rotationmatrix. Take a look at the RhinoCommon API to find out how this function works.
- Create a rotationmatrix using our function parameters
Multiply the translationmatrix with the rotationmatrix. Do not reverse the multiplication to translation * rotation. This will lead to a different result.
- Calculate the final transformationmatrix
Copy the startpoint to an endpoint using the Point3d() method.
- Create an endpoint
Transform the endpoint using the transformationmatrix we calculated earlier.
- Transform the endpoint to the correct location
Our function should now be working. You can test it by calling the function and using some random values. For the rotationAxis, we will need to create a vector first. As you can see, printing the test result will lead to a point with the coordinates (2, 0, 0). This is correct, because we first moved the endpoint two steps in the y-directions, and then rotated it around the origin point (along the y-axis) 90 degrees clockwise.
If your script is not displaying the expected result, take a look at the error message. Apparently we made a crucial mistake in line 36, just before line 39 as stated in the error message. We forgot to add the ) bracket at the end of the Transform function.
You can now remove the test code between line 40 and 49.
- Remove the test code
BranchAngles function
In this chapter, we will develop a BranchAngles function. As you can see in the following image, the branches in our fractal tree have a certain total angle and a specific angle for each branch. According these inputs (amount of branches and total angle) we should calculate all angles in a list.
Create the branchAngles function. Add an angles list variable that will contain the final result.
- Create a branchAngles function
Using the script between line 45 and 57, we can calculate the angles.
- In line 46, we check if the tree has more than one branch;
- If that is the case, in line 48 we iterate over all the branches;
- Next, in line 51, we use a formula that calculates the angle, it is advised to find out if you understand the formula on a piece of paper, before you continue;
- In line 54, we add the angles to the final list;
- If the fractal tree does not have more than one branch (line 55), the function should return an angle of 0. The algorithm will create a fractal tree that only consists out of one line.
Using the following code, you can check if the second function works. Using different parameters, including extreme cases, you can check if the algorithm gives you the expected result.
Afterwards you can remove the testing code (line 41 - 60) and continue with the next chapter.
CreateBranch function
In this chapter we will create a recursive function that generates the branches. "Recursively" means that a function calls itself to iterate to a next step.
Create a createBranch function with the following parameters: i (iterations), start, total_angle (of each branch), lineLength (the length of a branch line), length_red (length reduction), lines (a list that contains the lines of the fractal), branches (the amount of branches) and angles (angle of each branch). The functions should return the list of lines, which can be used in the next branch iteration.
- Write a createBranch function with the following parameters:
First we reduce the iteration step with one. Since our initial value will be based on the Number Slider in our Grasshopper Script.
- Reduce the iteration
Next we create a rotationAxis, in this case it should be the y-axis. The lines of a branch should rotate around the y-axis from the origin point. Furthermore, we generate an end-point, by transforming the start point and the transformation function we created earlier.
- Create a y-rotation axis 3D vector;
- Create an end-point from the start-point using the transformation function
Between the start and end we create a line. This line is added to the list of lines that the function finally returns.
- Calculate a line between start and end;
- Append the line to the list of lines
This is a good moment to check if our function works. As you can see in the following image, first (in line 79) we calculate the angles of a branch. Next, the fractal is created with the createBranch function. For the "lines" parameter, we use an empty list. In the viewport we can see a line, which is exactly what we expect.
- Check if the function works in its current state
Finally we make the function recursive. If the script is not in its final iteration, it should create a line for each angle. There are two parameters that should be different than the original functions inputs. First, we add each angle to the total_angle. This angle changes using the for-loop. By multiplying the lineLength with the reductionfactor, a branch becomes shorter when the script gets deeper into the tree.
If we reach the final iteration, the complete lines list is returned.
- Recursively recall the createBranch() function
Main function
To complete our script, we need a main function that combines everything we coded earlier. This main function returns the final fractal. Add the following parameters: origin (point), i (amount of iterations), length (the initial length of the first branch), totalAngle (the totalAngle of all splitted branches) and length_reduction (the reduction factor of the branch length).
- Create a main() function
In the first step, we calculate the branch angles using the branchAngles function.
- Calculate the branch angles with the branchAngles() function
We set an INITIAL_ANGLE with value 0. The variable is written in capital character, to indicate that this variable should not be changed. Furthermore we create a lines variable containing an empty list.
- Set an INITIAL_ANGLE variable with value 0;
- Set a lines variable that contains an empty list
Finally, we use the createBranch() function to generate the fractal
- Generate the fractal with the createBranch() function
In line 104, outslide the main function, we use all parameters from our Grasshopper Number sliders to calculate the final fractal output.
- Retrieve the final result using the main() function
To give a better indication of the tree complexity, we print the amount of lines.
- Print the amount of lines
After clicking on OK, you can see the final Grasshopper script.
The final result:
3D Fractal Tree
If you are interested in further developing this script, you can use the transformation() function to add extra translations and rotations. In the Downloads chapter, an example is given for a 3D version of a fractal tree. By importing the "random" library at the beginning of the script, it is possible to add a random rotation around the z-axis.
The following image displays the result of an extra random z-axis rotation:
Final script
Downloads
Here you can Download the final Fractal script. Feel free to use the script in your project, but always remember to Credit the Author. The script includes a 3D version of the fractal tree.
- Download the Fractal Script
References