Title: Draw a curly tree fractal using less memory in C#
To draw trees, the example Draw a curly tree fractal in C# first builds lists holding the end points of the tree's branches. It then finds the bounds of those points, makes a transformation to center the tree nicely, and then draws the branches.
Unfortunately the tree can contain a lot of branches, so the lists of end points can be big. For example, to draw a level 20 tree, the start and end point lists could contain more than 4 million points and they take up a lot of memory.
This example doesn't build lists of branch end points. Instead it traverses the tree once to find its bounds and then again to draw it.
The following FindBranchBounds method finds the tree's bounds.
// Find the bounds for this branch and its descendants.
// The direction parameter is in radians.
private void FindBranchBounds(
float x, float y, float direction, int level,
float length, float angleA, float angleB,
float length_scaleA, float length_scaleB,
ref float xmin, ref float xmax, ref float ymin, ref float ymax)
{
// Find the new segment.
if (length < 0.1) return;
x += (float)(length * Math.Cos(direction));
if (xmin > x) xmin = x;
if (xmax < x) xmax = x;
y += (float)(length * Math.Sin(direction));
if (ymin > y) ymin = y;
if (ymax < y) ymax = y;
if (level > 0)
{
FindBranchBounds(
x, y, direction + angleA, level - 1,
length * length_scaleA, angleA, angleB,
length_scaleA, length_scaleB,
ref xmin,ref xmax, ref ymin, ref ymax);
FindBranchBounds(
x, y, direction + angleB, level - 1,
length * length_scaleB, angleA, angleB,
length_scaleA, length_scaleB,
ref xmin, ref xmax, ref ymin, ref ymax);
}
}
This method finds the end point for the current branch and if necessary updates the bounds variables xmin, xmax, ymin, and ymax.
Note that the method doesn't update the bounds for the branch's starting point because that point was already considered as the end point of the current branch's parent. The only exception is at the tree's root. The code that makes the first call to this method initializes the bounds variables to 0 and the root is at (0, 0) so the bounds do take that point into account.
After it updates the bounds variables, the FindBranchBounds method recursively calls itself to expand the bounds to include this branch's children.
The following code shows the DrawBranch method.
// Draw this branch and its descendants.
// The direction parameter is in radians.
private void DrawBranch(Graphics gr, Pen pen,
float x, float y, float direction, int level,
float length, float angleA, float angleB,
float length_scaleA, float length_scaleB)
{
if (length < 0.1) return;
// Draw the new segment.
PointF point1 = new PointF(x, y);
x += (float)(length * Math.Cos(direction));
y += (float)(length * Math.Sin(direction));
PointF point2 = new PointF(x, y);
pen.Width = Distance(point1, point2) / 10f;
gr.DrawLine(pen, point1, point2);
if (level > 0)
{
DrawBranch(gr, pen,
x, y, direction + angleA, level - 1,
length * length_scaleA, angleA, angleB,
length_scaleA, length_scaleB);
DrawBranch(gr, pen,
x, y, direction + angleB, level - 1,
length * length_scaleB, angleA, angleB,
length_scaleA, length_scaleB);
}
}
This method has the same structure as the FindBranchBounds method except it draws the branches instead of updating the tree's bounds.
You could use a single method to update the tree's bounds and draw the tree, passing it a parameter to tell it which task to perform.
The rest of the program is similar to the previous example. See the earlier post for more information.
Download the example to experiment with it and to see additional details.
|