# Fractal Canopy

## Introduction

LEVEL: MASTER
Expected Time: 90 min
A fractal canopy (click on image if animation not visible)

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

Mandelbrot Set, Created by Wolfgang Beyer with the program Ultra Fractal 3. - Own work, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=321973

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

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

GhPython node

Start your script by adding a GhPython component to the canvas.

• Add a GhPython component to the canvas Maths » Script » GhPython Script

Change the input and output names

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 Zoom in on Python component » Click on input + icon;
• Rename the inputs to: "origin", "branches", "iterations", "total_angle", "length" and "length_reduction";
• Rename the a output to "fractal"

Change the input types

Now you need to set the correct Type Hint for all input.

• Change the origin input to Point3D RMB on origin input » Type hint... » Point3D
• Change the Length_Reduction to Float RMB on origin input » Type hint... » Float
• Change all other inputs to Integer RMB on origin input » Type hint... » Integer

Add the inputs and edit the Number Slider ranges

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 Params » Geometry » Point
• Add Number Sliders to all other inputs Params » Input » Number Slider
• Edit the settings of the Number Sliders as explained in the image RMB on Number Slider » Edit

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.

Change the description

Open the Grasshopper Python Script Editor. Change the description of the inputs and outputs in the multiline string.

• Open the Grasshopper Python Script Editor Double-click on component icon
• 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.
The formula we use to calculate the branch angles

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.

A 3D fractal tree

The following image displays the result of an extra random z-axis rotation: