Click here to Skip to main content
15,886,554 members
Articles / Programming Languages / XML

Create a Fractal Christmas Tree

Rate me:
Please Sign up or sign in to vote.
4.94/5 (33 votes)
22 Dec 20023 min read 245.2K   4.2K   60   32
This program explores how trees are made using a simple fractal algorithm.

Sample Image - fractalTree.jpg

What is a Fractal?

A fractal is a shape that is symmetric in scale, meaning that it looks the same, or similar, magnified as it does unmagnified. There are many naturally occurring objects that exhibit this self-similarity in scale. Trees, coastlines, mountain ranges, and clouds are some examples.

How Do You Make a Fractal?

A fractal is typically made using recursion. Given a line segment, and operation is performed on that segment which generally yields one or more new segments. The same operation is then performed again, on the new segments. In order to prevent the program from going into an infinite loop, the recursion algorithm usually includes a test that prevents it from operating on a segment smaller than a single pixel.

How Does this Program Create a Tree?

Creating a tree is very simple. Given the "stem" as a seed "branch":

  1. Draw the branch.
  2. Select a point on the branch where two new branches will be placed, one on each side of the current branch.
  3. From the user inputs, determine the angle and length of these two new branches.
  4. Recurse using the left branch.
  5. Recurse using the right branch.

This is accomplished in very few lines of code:

C#
public void Start(Line seed)
{
    h15=Math.Abs(seed.Size.Height)/5;
    Fractal(seed, depth);
}

private void Fractal(Line l, int depth)
{
    // draw the branch in a color based on its length
    int len=l.Length;
    if (len < 3)
    {
        gr.DrawLine(pbr3, l.StartPoint, l.EndPoint);
    }
    else if (len < h15)
    {
        gr.DrawLine(pbr2, l.StartPoint, l.EndPoint);
    }
    else
    {
        gr.DrawLine(pbr1, l.StartPoint, l.EndPoint);
    }

    while ( (depth != 0) && (l.Length > 1) )
    {
        // get the second segment when we split the line
        Line l2=l.SplitAt(bp)[1];

        // if no change, then exit.
        if (l2.Length==l.Length)
        {
            return;
        }

        // get the left and right branch starting points
        Line bp1=l.SplitAt(bp+MathEx.Dev(bpDev))[1];
        Line bp2=l.SplitAt(bp+MathEx.Dev(bpDev))[1];

        // the new "master" branch starting point has no randomness
        l=l2;

        // get the left and right branches
        Line lineLeft=bp1.Branch(360-ba+MathEx.Dev(baDev), 
            (int)(bp1.Length*bl/100+MathEx.Dev(blDev)));
        Line lineRight=bp2.Branch(ba+MathEx.Dev(baDev), 
            (int)(bp2.Length*bl/100+MathEx.Dev(blDev)));

        // recurse both branches
        Fractal(lineLeft, depth-1);
        Fractal(lineRight, depth-1);
    }
}

Adding Randomness

You will notice that the above code looks a bit more complicated than it needs to be, what with all those MathEx.Dev function calls. These take the user's specified +/- deviation and adjust the branch angle, length, and position randomly. This makes our trees much more natural looking!

A Note About the User Interface

The program is set up to autogenerate the tree when the main parameters (branch point, angle, and length) are changed. Changing the +/- deviation and color does not automatically regenerate the tree. After changing these parameters, click on the "Generate" button. Once you add randomness, click on the "Generate" button several times to see different trees.

On slow computers, you may want to turn off auto-generation.

Play with the "depth" spin control. This controls the level of recursion. Going higher than 4 can take a long time to generate the tree. You can get a better understanding of the algorithm by setting the depth to 1, then incrementing it.

A Note about the Code

This program includes a rough prototype of what I call my "Application Automation Layer". Have fun exploring the concepts behind it. For example, the GUI is generated from a text file, and the color picker controls are "replicated":

The color channel (red, green, or blue):

GUI:ColorChannelPicker
STATIC s1 at (0, 3) caption "#:"
SPIN spChannel at (15, 0) size (40, 20) storage value 
    options (min:0 max:255 step:1) \
    onchange "ColorChannelChangeEvent(@, #)"
COLORBOX clrbox at (60, 0) size (20, 20) storage channelColor
GUIEND

The color picker (one of three), which has three color channels and the final color:

GUI:ColorPicker
INHERIT inhRed at (0, 0) postfix R gui ColorChannelPicker
INHERIT inhGreen at (90, 0) postfix G gui ColorChannelPicker
INHERIT inhBlue at (180, 0) postfix B gui ColorChannelPicker
COLORBOX clrbox at (270, 0) size (60, 20) storage compositeColor
GUIEND

And the main GUI itself:

GUI:MainForm
STATIC s1 at (10, 13) caption "Branch Point:"
STATIC s2 at (10, 33) caption "Branch Angle:"
STATIC s3 at (10, 53) caption "Branch Size:"
STATIC s4 at (150, 13) caption "%"
STATIC s5 at (150, 33) caption "degrees"
STATIC s6 at (150, 53) caption "%"

STATIC s7 at (200, 13) size (100, 15) caption 
    "Deviation +/- :" options (rightjustify)
STATIC s8 at (200, 33) size (100, 15) caption 
    "Deviation +/- :" options (rightjustify)
STATIC s9 at (200, 53) size (100, 15) caption 
    "Deviation +/- :" options (rightjustify)

SPIN spBP at (100, 10) size (50, 20) storage branchPoint 
    options (min:20 max:80 step:1) \
    onchange "AutoUpdateCheck"
SPIN spBPDev at (300, 10) size (50, 20) storage 
    bpDev options (min:0 max:9 step:1)

SPIN spBA at (100, 30) size (50, 20) storage 
    branchAngle options (min:20 max:160 step:1) \
    onchange "AutoUpdateCheck"
SPIN spBADev at (300, 30) size (50, 20) storage 
    baDev options (min:0 max:9 step:1)

SPIN spBS at (100, 50) size (50, 20) storage 
    branchSize options (min:20 max:80 step:1) \
    onchange "AutoUpdateCheck"
SPIN spBSDev at (300, 50) size (50, 20) storage 
    bsDev options (min:0 max:9 step:1)

STATIC s10 at (350, 13) caption "%"
STATIC s11 at (350, 33) caption "degrees"
STATIC s12 at (350, 53) caption "%"

STATIC s13 at (10, 83) caption "Large Branches:"
INHERIT inhColor1 at (100, 80) prefix clr1 gui ColorPicker

STATIC s14 at (10, 108) caption "Small Branches:"
INHERIT inhColor2 at (100, 105) prefix clr2 gui ColorPicker

STATIC s15 at (10, 133) caption "Leaves:"
INHERIT inhColor3 at (100, 130) prefix clr3 gui ColorPicker

BUTTON btnGenerate at (10, 170) size (80, 25) 
    caption "Generate!" \
    onselect "EventUpdateTree"
	
CHECKBOX ckAutoGen at (100, 175) size (100, 20) caption 
    "Auto Generate" storage autoUpdate

STATIC s16 at (200, 178) size (70, 15) options 
    (rightjustify) caption "Depth:"
SPIN spDepth at (275, 175) size (50, 20) storage 
    depth options (min:1 max:10 step:1) \
    onchange "AutoUpdateCheck"

OWNERDRAW fractalTree at (10, 200) options 
    (autosizeW:10 autosizeH:10) \
    onpaint "EventPaintTree()"  \
    onsize "EventUpdateTree()"

GUIEND

Prior Art

Many years ago, I wrote a program for the Reuben H. Fleet Space Theatre in San Diego, California (http://www.rhfleet.org/ [^]) as part of their symmetry exhibit. I believe the exhibit is now permanently on display. This program is based on one of the two exhibits that I made for the Space Theatre. The other exhibit demonstrated the self-similiarity of coastlines.

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
GeneralMy vote of 5 Pin
JamesHollowell18-Dec-12 14:49
JamesHollowell18-Dec-12 14:49 
GeneralMy vote of 5 Pin
Mohsen Kazemi1-Jul-10 10:13
Mohsen Kazemi1-Jul-10 10:13 
Generaldatabase Pin
Member 384134123-Feb-07 19:41
Member 384134123-Feb-07 19:41 
GeneralPrinting Pin
Heath Stewart2-Jan-03 6:52
protectorHeath Stewart2-Jan-03 6:52 
GeneralRe: Printing Pin
Marc Clifton2-Jan-03 7:04
mvaMarc Clifton2-Jan-03 7:04 
GeneralVery interesting Pin
Sameh Ahmed27-Dec-02 22:41
Sameh Ahmed27-Dec-02 22:41 
GeneraldataMatrix Pin
David Stone23-Dec-02 13:34
sitebuilderDavid Stone23-Dec-02 13:34 
GeneralRe: dataMatrix Pin
Marc Clifton23-Dec-02 14:00
mvaMarc Clifton23-Dec-02 14:00 
GeneralRe: dataMatrix Pin
freshthinking14-Jun-03 0:26
freshthinking14-Jun-03 0:26 
GeneralPretty :-) Pin
Taka Muraoka23-Dec-02 11:51
Taka Muraoka23-Dec-02 11:51 
GeneralRe: Pretty :-) Pin
Marc Clifton23-Dec-02 15:29
mvaMarc Clifton23-Dec-02 15:29 
I read the white paper on your web site but without examples, it's all a bit abstract.

Yes, I agree. In fact, those articles are so old it's laughable.

Let me first give a really brief description of what it DOES:

1. Provides a common data exchange mechanism between technologies that have different data representation schemes. A typical example is the data representation differences between 1) the application, 2) the database, 3) the visualization program, such as Visio, 4) the GUI.

2. Decouples unrelated objects by using a meta-interface (for example, a bridge design pattern). This reduces build time dependencies and problems related to changes in object designs.

3. Promotes component-based development. The application itself is considered another technology which is merely another plug-in to the framework. Each component of the AAL is itself a DLL that registers itself into the AAL framework. Components communicate via an instrumented messaging system (see #4 below) and exchange data via an instrumented data exchange mechanism.

4. A fallout of this scheme is that an application is automatically instrumented—data exchanges, event invocation, object to object messaging all include instrumentation, so that it is very easy to trace an application.

I'll put together a pretty picture for you soon. Uwe Keim also expressed an interest in this!

For example, the GUI description file seems to be simply a resource file that is read at runtime.

Yes. The advantages are not so much in the "resource file" but in the underlying implementation of the GUI objects (which are derived from MFC). One thing that is lacking in a typical Windows resource file is the association between the resource and the data container for that resource. For example, a list will have an index and the list itself (we'll ignore multiple selection lists for right now). Instead of making this association in the resource file, the programmer has to create a derived class and declare variables in C++, then do the data exchange.

Instead, the AAL uses a generic DataContainer mechanism. A typical scenario is this--the database module reads in a list and stores it in the DataContainer using the generic DataMatrix object. The GUI list reads it out of the DataContainer, transferring the information in the DataMatrix to the list control. In that process, I can do all sorts of nifty things--hide columns in the DataMatrix that are useful but we don't want to display in the list, for example.

Now, the above describes what I would call common sense programming and doesn't need scripts, etc., but the problem I encountered, and the reason for the AAL, is described next.

Are you selling this as a development tool or is it something you just use for youself?

No, I'm not selling it, and I'm more than happy to give it away for free (remember what you said: every line of code is a liability). There are really two camps--those that don't have a problem with non-standard approaches, and those people that do. I'm not going to argue until I'm blue in the face with those that do have a problem. I just won't work with them. Arrogant? No. I just know my limits.

The concept of the AAL was originally created to support a multi-developer effort for Space Systems/Loral. We were writing a program to automate the design of satellites. It had a large Oracle database, about 8 different unrelated but interconnected modules, interfaced with Visio, etc.

I needed a framework that allowed me to debug things that junior programmers were doing. I needed a framework that enforced a particular coding style and promoted some sort of consistency. I needed to break the concept that the database group did database stuff and the C++ group did C++ stuff.

And I needed a system that could be modified literally overnight to accomodate changing requirements. Before Agile Programming, before Extreme Programming, there was little ol' me with my Application Coordinator (what it was called then).

After I left Integration Partners (who had the contract with Space Systems/Loral) I rewrote the whole thing an called it the Application Automation Layer. It includes a script engine to provide the meta-glue for all the technology components. I've used it successfully for the last 4 years now on numerous customer contracts and applications. I think it saves me tons of time because I can do rapid prototyping AND the system, being component based and script driven, is highly flexible to changing customer requirements. I seem to get a lot of those customers--people don't really know what they want until they see something and go, hmmm...that's not really what I had in mind. I love it!

Marc

Help! I'm an AI running around in someone's f*cked up universe simulator.
Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus
Every line of code is a liability - Taka Muraoka

GeneralRe: Pretty :-) Pin
Taka Muraoka23-Dec-02 15:50
Taka Muraoka23-Dec-02 15:50 
GeneralRe: Pretty :-) Pin
Marc Clifton24-Dec-02 1:16
mvaMarc Clifton24-Dec-02 1:16 
GeneralRe: Pretty :-) Pin
Taka Muraoka24-Dec-02 12:12
Taka Muraoka24-Dec-02 12:12 
GeneralWOW! Now lets add some christmas decoration.... Pin
Anonymous23-Dec-02 9:58
Anonymous23-Dec-02 9:58 
GeneralRe: WOW! Now lets add some christmas decoration.... Pin
funkycoder30-Dec-02 21:50
funkycoder30-Dec-02 21:50 
GeneralGetting a System.NullReferenceException... Pin
Uwe Keim23-Dec-02 5:53
sitebuilderUwe Keim23-Dec-02 5:53 
GeneralRe: Getting a System.NullReferenceException... Pin
Marc Clifton23-Dec-02 8:05
mvaMarc Clifton23-Dec-02 8:05 
GeneralVery Cool Pin
Paul M Watt23-Dec-02 5:43
mentorPaul M Watt23-Dec-02 5:43 
GeneralCool! Pin
Shog923-Dec-02 5:34
sitebuilderShog923-Dec-02 5:34 
GeneralThanks, Marc! Pin
Roger Wright23-Dec-02 5:22
professionalRoger Wright23-Dec-02 5:22 
GeneralRe: Thanks, Marc! Pin
Marc Clifton23-Dec-02 5:28
mvaMarc Clifton23-Dec-02 5:28 
GeneralShweeeeeet tree Marc Pin
Jason Henderson23-Dec-02 5:22
Jason Henderson23-Dec-02 5:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.