Hello, in this article, we are going to build together a program that generates a fractal tree using the C# language and the SimpleDraw interface.
To get started, we need to know what is a fractal tree: a fractal tree is a pattern made by drawing a line (usually vertical),
and, from the end of that line, drawing two (or more) lines with some angle respective to the previous line and half the length (or another factor).
We then repeat this process a few times and the pattern we are left with looks like a tree, hence the name.
The most common way to do this is by using recursion in a function, which is when a function calls itself again, generating a loop.
This loop needs to be constrained (with a final condition) so that our code does not run forever.
To get started, let's make a Windows Forms application in Visual Studio and choose C# as our language.
Then let's add SimpleDraw into our project, we can do this in our 'solutions manager' tab in VS, by right-clicking the 'dependencies' item and selecting our SimpleDraw binary which can be found in here.
Once we have that done, we need to actually make a canvas so we can draw on it. To do that we go to our Form1.cs [design] and
place a PictureBox element from our toolbox into it, we can set it to any size we want.
Now, let's make the building blocks of our drawing: a setup and a draw function.
namespace MyNamespace
{
public partial class Form1 : Form
{
public Form1()
{
}
void setup()
{
}
void draw()
{
}
}
}
Then, we need to make an instance of the SimpleDraw class we installed, for that we can do this:
SimpleDraw s = new SimpleDraw();
It's helpful to keep the name short as we will write it every time we want to draw things.
With that over, we can actually start working with our drawing.
In our Form1 method, we need to call s.start(setup, draw, myCanvas);
to start the drawing process.
Also we can call in setup()
:
s.toggleAntiAlias();
s.frameRate(15);
Since we want to draw a fractal tree, we need a recursive function like "drawTree(parameters)" to draw it, so our draw()
method code might look something like this:
s.background(Color.White);
drawTree(parameters);
So now we have to get to the meat & gravy of our program: the recursive function.
First, we need to know what we need to pass into our function. Well, we need an starting x and y position, as well as a size and a current angle.
So our parameters might look something like this:
drawTree(double x, double y, double currentSize, double currentAngle);
The first thing that we can do in our function is a base case for stopping. Since we want the size of our lines to get smaller with each call, we can do something like:
if (size < 4)
{
return;
}
Next, we need to draw the line that we want.
To do that, we need the coordinates of two points. The first one we already know: it's the x,y values passed in as parameters.
The next one we can use a bit of trig. to figure out.
double newX = x + currentSize * Math.Cos(currentAngle);
double newY = y - currentSize * Math.Sin(currentAngle); // Notice that it's minus because positive y is actually down since we start at top-left
Finally, we can draw our line:
s.line((int)x, (int)y, (int)newX, (int)newY);
And to top off the recursion, we can call the function again two times with new parameters:
drawTree(newX, newY, currentSize / 2, currentAngle + Math.PI / 6);
drawTree(newX, newY, currentSize / 2, currentAngle - Math.PI / 6);
Then, all we need to do is call the function for the first time in our draw method. Since we want it to start at mid-bottom we can pass in these argts:
drawTree(s.width / 2, s.height, 200, Math.PI / 2, true); // Since we want to draw our first line vertically, we need the angle to be PI/2
And boom, we've got a fractal tree. You can play around with the parameters a bit to see how it changes the patterns.
Extra:
For our next step, we are going to make the patterns a bit more customizable.
The parameters that we can change are: angle, size, number of branches, minimum size and resizing factor.
So, we can make some trackbars (sliders) in our form to control these (in this image I also changed zoom and did some rough tranlating when clicking the image, but that's not necessary).
For the size, angle and resizing factor, all we need to do are some variables for them, replace them in the code and update them in draw, and the drawTree()
method will update accordingly.
However, for the number of branches, we need to be a bit more careful with how we approach it.
The way we can do it is by dividing the angle we want in equal parts depending on how many branches there are, then start adding them up from the minimal angle.
The total angle is (2 * angle) and the number of parts we need to divide it to is (numberOfBranches - 1), that's our deltaAngle.
The minimum angle is currentAngle - angle.
double deltaAngle = (2 * angle) / (numberOfBranches - 1);
double minAngle = currentAngle - angle;
Then, we can use a for loop for calling the functions:
for (int i = 0; i < numberOfBraches; i++)
{
drawTree(newX, newY, size * resizingFactor, minAngle + i * deltaAngle);
}
We can also do extra stuff for translation and zooming, so that we can navigate in our image a bit, but that's not necessary, also you can play around with the colors to make it even more stunning.
Here's the final code:
using SimpleDrawProject;
namespace FractalTree
{
public partial class Form1 : Form
{
SimpleDraw s = new SimpleDraw();
public Form1()
{
InitializeComponent();
s.start(setup, draw, myCanvas);
}
void draw()
{
s.translate(currentTranslate.X,currentTranslate.Y);
s.zoom(zoomBar.Value * 0.1);
getData();
s.background(Color.White);
drawTree(s.width / 2, s.height, size, Math.PI / 2);
s.text("angle = " + (angle*360/(Math.PI * 2)).ToString(), 0, 0);
s.text("size = " + size.ToString(), 0, 15);
s.text("n branches = " + numberOfBranches.ToString(), 0, 30);
s.text("resize factor = " + resizeFactor.ToString(), 0, 45);
}
void getData()
{
numberOfBranches = branchBar.Value;
size = (int)(sizeBar.Value * 2);
angle = (angleBar.Value * 2 * Math.PI) / 360;
minSize = minSizeBar.Value;
resizeFactor = (double)resizeFactorBar.Value * 0.1;
}
int numberOfBranches = 2;
int size = 200;
double angle = Math.PI / 2;
int minSize = 4;
double resizeFactor = 0.5;
Point currentTranslate = new Point(0,0);
void drawTree(double x, double y, double currentSize, double currentAngle)
{
if (currentSize < minSize)
{
return;
}
double newX, newY;
newX = x + currentSize * Math.Cos(currentAngle);
newY = y - currentSize * Math.Sin(currentAngle);
s.line((int)x, (int)y, (int)newX, (int)newY);
double deltaAngle = (2 * angle) / (numberOfBranches - 1);
double minAngle = currentAngle - angle;
for (int i = 0; i < numberOfBranches; i++)
{
drawTree(newX, newY, currentSize * resizeFactor, minAngle + i * deltaAngle);
}
}
void setup()
{
s.frameRate(15);
s.toggleAntiAlias();
}
private void myCanvas_Click(object sender, EventArgs e)
{
Point pos = s.mousePos(this);
currentTranslate.X += (int)((pos.X - s.width / 2) * 0.1);
currentTranslate.Y += (int)((pos.Y - s.height / 2) * 0.1);
}
}
}
Top comments (1)
This is interesting, I am going to bookmark this and give this a shot at another date. Thank you for sharing your idea!