Fractals in the Real World

Generate fractal images using this "fractal template" drawing tool.


April 01, 1991
URL:http://www.drdobbs.com/tools/fractals-in-the-real-world/184408534

Figure 1


Copyright © 1991, Dr. Dobb's Journal

Figure 2


Copyright © 1991, Dr. Dobb's Journal

Figure 3


Copyright © 1991, Dr. Dobb's Journal

Figure 4


Copyright © 1991, Dr. Dobb's Journal

APR91: FRACTALS IN THE REAL WORLD

FRACTALS IN THE REAL WORLD

A general-purpose interactive drawing and modeling tool

This article contains the following executables: OLIVER.ARC

Dick Oliver

Dick has taught computer science to all ages since 1979. He is now president of Cedar Software and author of the "Fractal Grafics" Guidebook and software. He can be reached at Cedar Software, RI, Box 5140, Morrisville, VT 05661; 802-888-5275.


Some of the hardest things to model with traditional geometry -- plants, splashing water, and other complex natural forms--are the easiest to draw with fractals. Fractal drawing doesn't require new technology--though computationally intensive, it's well within the capabilities of modern PCs. Ironically, the popularity of "Mandelbrot Set" fractal programs, which are enchanting but useless for interactive drawing and real-world modeling, has helped keep the potential of fractals from being widely understood.

This article describes how to generate fractal images using a general drawing tool I call a "fractal template." This tool is a subset of a mouse-oriented fractal drawing system, "Fractal Grafics," which my company provides. The template is simple enough to be implemented interactively on your PC, intuitive enough for creative design work, and capable of producing any image displayable by your hardware (though some images are a lot easier to draw than others!). I'll use Microsoft C 5.1/6.0, but porting to other compilers should be straightforward.

What's a Fractal?

Fractals are infinitely detailed shapes: You can magnify them as much as you want, and you'll still see complex detail. Conventional, "linear" shapes such as Bezier curves and rectangles, on the other hand, always look like straight lines when greatly magnified (see Figure 1).

How do you describe an infinitely detailed shape? The simplest way is through "successive approximation." Imagine an example: Start with a triangle, and lay three smaller triangles over it so that a hole is left uncovered in the middle. Then cover each of those three small triangles with three more, even smaller ones. Continue that process forever, and you have the well-known fractal called "Sierpinski's Triangle" (see Figure 2).

Growing From a Seed

You can draw an approximation of Sierpinski's Triangle using a fractal template. The original triangle can be defined as an array of x, y points, shown here as Example 1(a). Call it _seed_, because the rest grows from it. (To improve performance, the triangle is declared as two arrays of ints, _seedx_ and _seedy_, rather than a single struct.)

Example 1: Triangle transformations

  (a)   #define NPOINTS 3
        int seedx [NPOINTS] = {-200, 200, 0},
            seedy [NPOINTS] = {-200, -200, 200};

  (b)   #define NTRANS 3

        float movex [NTRANS] = {-100.0, 100.0, 0.0},
              movey [NTRANS] = {-100.0, -100.0, 100.0},
              sizex [NTRANS] = {.05, 0.5, 0.5},
              sizey [NTRANS] = {0.5, 0.5, 0.5};

  (c)   float spinx [NTRANS] = {0.0, 0.0, 0.0},
              spiny [NTRANS] = {0.0, 0.0, 0.0};

  (d)   a =   sizex * cos (spinx)
        b = - sizey * sin (spiny)
        c =   sizex * sin (spinx)
        d =   sizey * cos (spiny)

  (e)  x2 = a * x1 + b * y1 + movex
        y2 = c * x1 + d * y1 + movey

  (f)   a3 = a2 * a1 + b2 * c1
        b3 = a2 * b1 + b2 * d1
        c3 = c2 * a1 + d2 * c1
        d3 = c2 * b1 + d2 * d1

Now you need to describe the placement of the three smaller triangles as a transformation of the seed. Together, the "seed" and "transformations" define the fractal by pointing toward an infinite progression of smaller triangles being transformed into still smaller ones.

In our example, each triangle is half the size of the original, and offset half way to one of the corners. So we define a _size_ variable, and a _move_ in x,y, for each of the three transformations; see Example 1(b).

Eventually, we want to both describe a wide variety of shapes besides Sierpinski's Triangle, and to interactively place, resize, and maybe even spin and stretch the seed shape to choose transformations. Because we have defined separate _size_ variables for the x and y axes, we can describe a stretching transformation by increasing the size on one axis only. (For simple shrinking, the x and y sizes will have the same value.)

Similarly, separate _spinx_ and _spiny_ variables allow rotation and skewing transformations, as shown in Example 1(c). Because the three transformations that define a Sierpinski's Triangle don't involve any rotation, for now the spin variables are zero.

By using matrix algebra, there is a simple way to do all this at once: with "affine transformations." An affine transformation T can be expressed as four variables (a, b, c, and d) plus the x,y displacement (movex, movey). To derive these variables from size and rotation, use the formulas in Example 1(d). Example 1(e) shows how to go from a point (x1,y1) on the big seed triangle to the corresponding point (x2,y2) on one of the smaller, "transformed" triangles.

Finally, given two transformations, T1 and T2, there is a third transformation, T3, that represents the combination of the two. The formula in Example 1(f) shows how to produce T3.

With this, you're ready to start drawing. Listing One (sierp.c, page 101) and Listing Two (sierp.h, page 101) contain the C code to draw Sierpinski's Triangle on a VGA screen. After initializing the screen, the equations in Example 1(d) are used to compute the transformations defined by the size and spin variables. In the _draw_ function, the seed shape is transformed using the approach in Example 1(e), so that its size and rotation are unchanged, but its position is closer to the center of the screen. _draw_ draws the transformed shape on the screen using the approach in example 1(f); then it calls itself recursively to draw smaller and smaller triangles. The recursive calls continue until _iter_, the iteration counter originally set to NLEVELS, reaches zero.

The Chaos Game

Technically, a shape isn't really "fractal" until you draw the "infinite" level of detail. Of course, the smallest level distinguishable at the resolution of your screen will do. With Sierpinski's Triangle, that level is reached when each tiny triangle is one pixel wide--about the sixth level of detail on a VGA screen.

Michael Barnsley and his colleagues at Georgia Tech have discovered a mathematical magic trick which allows you to skip immediately to that finest level of detail and "paint" the whole shape at once on your screen. They call it the "Chaos Game," and it is played as follows:

Start at any point on the screen, and randomly select one of the transformations which define the fractal. Apply the transformation to the point, as in Example 1(e), and make a dot at the resulting new point. Again choose one of the transformations at random, apply it to the new point, and make another dot wherever it ends up. Believe it or not, if you continue this seemingly random journey around the screen, a fractal will quickly appear!

If you're choosing between the same three transformations defined above, you'll get the same Sierpinski's Triangle as drawn by the "successive approximation" method. The _paint_ function in Listing One demonstrates the Chaos Game, or "random iteration" algorithm.

How does this work? One way to understand it is to notice that the resulting shape is made up of three miniature copies of itself. If you took every dot in the whole big shape and applied one of the transformations to it, you'd get one of those three miniatures. So each "random pick" goes from a dot on the whole to the corresponding dot on one of the parts. Leap around this way on the shape for a while, and you end up landing on almost all the dots in all three copies.

Drawing a Fractal Tree

You've seen two ways to draw a fractal, given a seed shape and some transformations. To see how this sort of approach can model real-world phenomena, imagine the growth of a fractal tree. If our seed shape looked like the trunk of a tree, and the transformed trunks were placed like the first level of branches, the _draw_ function described above would grow a realistic tree on the screen. Change the #include "sierp.h" in Listing One to #include "maple.h" (Listing Three, page 101), and you can watch it happen. The _paint_ function shows only the finest level of foliage, skipping the trunk and branches altogether.

Figure 3 and Listing Three show how you can model the genetics of different species of plants by defining templates. Note that the template data for the maple tree and maple leaf are very similar, while the template for a pine tree is quite different.

Interactive Design

Movement, size, and rotation are easy to represent visually. By displaying the seed shape and the first level of transformed copies, you can show the entire definition of a complex fractal on the screen. The template can be interactively manipulated in real time with simple menu selections like "spin," "shrink," "grow," and "stretch." Display the user's changes in real time, and you have the essentials of an interactive fractal drawing program! See Listing Four (page 102) for an example called the FACDRAW program. As with Listing One, you can change the #include statement to change the starting template.

When FRACDRAW starts with the "maple" template, you'll see several polygons on the screen. The largest is always the seed shape, and the others are transformed copies of it. By pressing one of the keys listed on the menu, you can change a transformation. A T-shaped "handle" is always displayed in the center of one of the polygons to let you know which transformation you are currently editing. Picking "Next Part" moves the handle to another polygon, allowing you to edit the corresponding transformation.

If you want to swivel one of the branches of the tree, you can pick Next Part (press the Tab key) until the handle is in the center of that branch. Then, picking spin (the + or - key) will spin that branch. The arrow keys will move that particular branch relative to the rest. When you "draw," your changes will be carried throughout all levels of the tree.

When the draw and paint functions place the fractal on the screen, they apply a final transformation to the whole thing, independent of the shape itself. By placing the handle on the seed shape, you can edit that final transformation, which effectively spins, resizes, and moves the whole fractal at once.

You can also edit the seed polygon itself. Picking "NextPoint" moves the handle to a corner point on the seed, and the arrow keys will then move that point. All the menu choices will then modify the seed shape instead of the transformation data. Picking Next Part puts the handle back in the center of a polygon, letting you edit the transformations again.

Fractal Drawing Techniques

To get creative with fractal templates, you'll need to learn two basic drawing techniques. You've already seen the first in action. To draw the maple tree, I modeled the natural growth of a tree geometrically. This works great when you want to draw something that grows level-by-level. FLAKE.H in Listing Five (page 105) gives you the data for a snowflake drawn the same way.

If you want to draw things like clouds and mountains, which don't obviously grow out of a simple "seed," you'll need a more powerful technique called "tiling." To draw each of the fractals in Figure 4 and Listing Five, I sketched a rough outline of the whole shape, and then "tiled" it with copies of itself. Listing Six (page 106) contains templates for cumulus, cirrus, and stratus clouds, as well as for other images (pine tree, leaf, and so on).

How does tiling work? A surprising theorem by John Elton has proven that any shape can be tiled with smaller copies of itself, and that applying the random iteration algorithm with the transformations used in the tiling will recreate the original shape. Because each transformation can be expressed as only six numbers, complex images can be described in very few bytes. Michael Barnsley and Alan Sloan have achieved image compression ratios of more than 10,000:1 in this fashion (see References).

Coloring

You can add color to your fractal images with a simple two-dimensional array. Each transformation, or "part" of the template, can have a separate color at each level of detail. The _draw_ routine in FRACDRAW.C (Listing Four) uses just such an array to assign colors.

Usually, you'll want to use one of two coloring schemes: coloring by level, or coloring by transformation. The maple template defined in Listing Three uses the former, assigning a separate color to each level of detail, from a brown trunk to green leaves. The _sierp_ template uses the latter, giving each "part" its own color throughout all levels.

Coloring with the _paint_ function can actually use the same color array, even though the fractal skips immediately to the infinite level of detail. You can #include the "color.c" template definition to see how this works. The comments in Listing Four explain how it's done. You can experiment with color assignments for both the _draw_ and _paint_ functions by playing around with the COLOR definitions in the header files.

A New Way of Seeing

Drawing with traditional tools involves breaking an image into many separate shapes and rendering each individually with a curve, polygon, line, or dot. Drawing with fractals, on the other hand, demands that you find reflections of the whole in each part and work with entire sections of the image as indivisible, infinitely detailed forms. The symmetries and structures revealed by fractal drawing are thus quite different than those revealed by more atomistic approaches. At first, you may find it difficult to associate the form of the template with your resulting fractal artwork. With practice, though, you'll start seeing the global changes and intricate symmetries quite easily.

With the right mathematical tools, the complex, irregular beauty that surrounds us in nature is as easy to model and describe as the smooth linearity of man-made artifacts.

References

For access to a wide variety of books and software on fractals and computer graphics, contact Media Magic, P.O. Box 507, Nicasio, CA 94946.

Barnsley, Michael J. "A Better Way to Compress Images." BYTE (January, 1988).

Barnsley, Michael J. Fractals Everywhere. San Diego, Calif.: Academic Press, 1988.

Mandelbrot, Benoit B. The Fractal Geometry of Nature. San Francisco, Calif.: W.H. Freeman, 1982.

Oliver, Dick T. The Fractal Grafics Guidebook. Morrisville, Vt.: Cedar Software, 1990.


_FRACTALS IN THE REAL WORLD_
by Dick Oliver


[LISTING ONE]


/****************************************************************
  SIERP.C -- (C) 1990 by Dick Oliver, R1 Box 5140, Morrisville, VT  05661
   A program to "draw" and "paint" Sierpinksi's Triangle, defined by a
   "seed" (or "parent") shape and three transformations of that shape
   ("children"). The author makes no claims as to readability or
   suitability for a particular task, but I'll be happy to give advice
   and assistance.
*****************************************************************/

#include <stdio.h>   /* For getch() */
#include <math.h>    /* For cos() and sin() */
#include <graph.h>   /* For graphics calls */

#include "sierp.h"  /* You can change this for other template definitions */

int seedx[NPOINTS] = {SEEDX},   /* The "parent" polygon */
    seedy[NPOINTS] = {SEEDY};

      /* The tranformations which define the "children" */
float movex[NTRANS] = {MOVEX},  /* Displacement */
      movey[NTRANS] = {MOVEY},
      sizex[NTRANS] = {SIZEX},  /* Size change */
      sizey[NTRANS] = {SIZEY},
      spinx[NTRANS] = {SPINX},  /* Rotation */
      spiny[NTRANS] = {SPINY},

      /* The transformation matrix T, computed from the above variables */
      Ta[NTRANS], Tb[NTRANS], Tc[NTRANS], Td[NTRANS];

/* Function prototypes */
void draw(float a, float b, float c, float d, float mx, float my, int iter);
void paint(int mx, int my);

void main(void)
{   int t;
    _setvideomode(_VRES16COLOR);    /* Initialize the screen */
    _clearscreen(_GCLEARSCREEN);

    /* Compute a,b,c,d from the move, size, and spin variables */
    for (t = 0; t < NTRANS; t++)
    {   Ta[t] =   sizex[t] * cos(spinx[t]);
       Tb[t] = - sizey[t] * sin(spiny[t]);
       Tc[t] =   sizex[t] * sin(spinx[t]);
       Td[t] =   sizey[t] * cos(spiny[t]);
    }
    /* Invoke draw with an initial transformation to move the triangle
       to the center of the screen, unchanged in size or rotation */
    draw(1.0, 0.0, 0.0, 1.0, (float) CENTERX, (float) CENTERY, NLEVELS);
    _settextposition(30,0);
    _outtext("Press any key to paint.");
    getch();
    _clearscreen(_GCLEARSCREEN);

    /* Invoke paint, specifying the center of the screen */
    paint(CENTERX, CENTERY);
    _settextposition(30,0);
    _outtext("Press any key to exit.");
    getch();

    _setvideomode(_DEFAULTMODE); /* Go back to text mode and exit */
}


/* This recursive routine draws one "parent" polygon, then calls itself
   to draw the "children" using the transformations defined above */
void draw(float a, float b, float c, float d, float mx, float my, int iter)
{   int t;
    iter--;   /* Count one more level of drawing depth */
    {    /* Use a,b,c,d,mx,my to transform the polygon */
       float x1, y1;  /* Point on the parent */
       int p, x2[NTRANS], y2[NTRANS]; /* Points on the child */
       for (p = 0; p < NPOINTS; p++)
       {   x1 = seedx[p];
       y1 = seedy[p];
       x2[p] = a * x1 + b * y1 + mx;
       y2[p] = c * x1 + d * y1 + my;
       }
       /* Now draw the new polygon on the screen */
       _moveto(x2[NPOINTS - 1], y2[NPOINTS - 1]);
       for (p = 0; p < NPOINTS; p++) _lineto(x2[p], y2[p]);
    }
    if (iter < 0) return;  /* If we're at the deepest level, back out */

    /* Do a recursive call for each "child" of the polygon we just drew */
    for (t = 0; t < NTRANS; t++)
    {   draw(Ta[t] * a + Tc[t] * b,
        Tb[t] * a + Td[t] * b,
        Ta[t] * c + Tc[t] * d,
        Tb[t] * c + Td[t] * d,
        movex[t] * a + movey[t] * b + mx,
        movex[t] * c + movey[t] * d + my,
        iter);
    }
}


/* This routine uses "random iteration" to paint the fractal dot-by-dot.
   The resulting shape will be the same as if we called draw with a
   huge value for the number of levels */
void paint(int mx, int my)
{   int t;
    unsigned long ct = 0;    /* Counter for number of dots painted so far */
    float x1 = 0.0, y1 = 0.0, x2, y2; /* Current and next dot */

    /* Keep going until a key is pressed or we reach the COUNT limit */
    while(!kbhit() && (++ct < COUNT))
    {   t = rand() % NTRANS;  /* Pick one of the transformations at random */

       /* Then move from a dot on the "whole" to the corresponding dot
          on some transformed "part" */
       x2 = x1 * Ta[t] + y1 * Tb[t] + movex[t];
       y2 = x1 * Tc[t] + y1 * Td[t] + movey[t];
       x1 = x2, y1 = y2;

       /* Skip the first few dots--it takes a while to "find" the fractal */
       if (ct > 8) _setpixel((int) x2 + mx, (int) y2 + my);
    }
}






[LISTING TWO]


/****************************************************************
   SIERP.H --    Header file for Sierpinski's Triangle template
     This (and the other header files like it) can be used to define
     the initial fractal template for the SIERP.C and FRACDRAW.C programs
*****************************************************************/

#define NPOINTS 3    /* Number of points on the "parent" polygon */
#define NTRANS  3    /* Number of transformed "children" */
#define NLEVELS 6    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen x, y*/
#define CENTERY 240
#define SEEDX -200,  200,  0  /* The "parent" polygon */
#define SEEDY -200, -200, 200

        /* The tranformations which define the "children" */
#define MOVEX -100.0,  100.0,   0.0  /* Displacement */
#define MOVEY -100.0, -100.0, 100.0
#define SIZEX   0.5,   0.5,   0.5    /* Size change */
#define SIZEY   0.5,   0.5,   0.5
#define SPINX   0.0,   0.0,   0.0    /* Rotation */
#define SPINY   0.0,   0.0,   0.0

/* The following color definitions are ignored by the SIERP program
   and used only by FRACDRAW.
   PALETTE defines the 16-color VGA palette
   COLOR intializes a two-dimensional array with color values:
   each column in the array definition below corresponds to one level
   of detail, and each row corresponds to a "part", or transformation.
   Note that the array only needs to be 6 by 3 for the template defined
   above, but more rows are included in case the user inserts additional
   "parts".
*/

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
       _BLUE, _MAGENTA, _BROWN, _WHITE, \
       _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
       _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR   {{2, 2, 2, 2, 2, 2},\
            {1, 1, 1, 1, 1, 1},\
       {5, 5, 5, 5, 5, 5},\
       {4, 4, 4, 4, 4, 4},\
       {2, 2, 2, 2, 2, 2},\
       {3, 3, 3, 3, 3, 3},\
       {7, 7, 7, 7, 7, 7},\
       {8, 8, 8, 8, 8, 8},\
       {1, 1, 1, 1, 1, 1}}






[LISTING THREE]


/*****************************************************************
   MAPLE.H --- Header file for maple tree template
     This (and the other header files like it) can be used to define
     the initial fractal template for the SIERP.C and FRACDRAW.C programs
*****************************************************************/
#define NPOINTS 4    /* Number of points on the "parent" polygon */
#define NTRANS  3    /* Number of transformed "children" */
#define NLEVELS 6    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 350
        /* The "parent" polygon */
#define SEEDX 6,20,-6,-12
#define SEEDY -120,120,120,-120
         /* The tranformations which define the "children" */
#define MOVEX -6.1,-46,48  /* Displacement */
#define MOVEY -156,-40,-38
#define SIZEX  .65,.57,.58    /* Size change */
#define SIZEY  .56,.77,.82
#define SPINX   6.28,5.52,.44    /* Rotation */
#define SPINY   6.28,5.52,.44

/* The following color definitions are ignored by the SIERP program
   and used only by FRACDRAW.  See similar #defines in SIERP.H (Listing 2)
*/
#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
       _BLUE, _MAGENTA, _BROWN, _WHITE, \
       _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
       _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2}}





[LISTING FOUR]


/*****************************************************************
   FRACDRAW.C -- Drawing with fractals
   Copyright 1990 by Dick Oliver, R1 Box 5140, Morrisville, VT  05661
   A program for interactive fractal drawing.
   The author makes no claims as to readability or suitability for a
   particular task, but I'll be happy to give advice and assistance.
*****************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <graph.h>
#include <math.h>
#include <ctype.h>
#include <bios.h>

        /* #include file for initial template definition, can be changed */
#include "maple.h"
        /* Numerical constants */
#define PI 3.141592
#define TWOPI 6.283853
#define HALFPI 1.570796
#define ALMOSTZERO 0.00002
#define MAXSIZE 0.998
#define MAXINT 32767
        /* Keyboard constants */
#define ENTER 13
#define BACKSPACE 8
#define ESC 27
#define END -'O'
#define HOME -'G'
#define INSERT -'R'
#define DELETE -'S'
#define TAB 9
#define UNTAB -15
#define UP -'H'
#define DN -'P'
#define LT -'K'
#define RT -'M'
#define NULLKEY '^'

        /* Generic getch() replacement */
#define geta if ((a = getch()) == 0) a = -getch();\
         else if (a > 0) a = toupper(a)

        /* Main menu */
#define MENUMSG "ACTION    KEY\n"\
       "  Draw     D\n"\
       "  Paint    P\n"\
       "  Both     B\n"\
       " Next Part Tab\n"\
       " NextPoint ~\n"\
       " Insert    Ins\n"\
       " Delete    Del\n"\
       "  Grow     *\n"\
       "  Shrink   /\n"\
       "  Spin     + -\n"\
       "  Skew     ; \'\n"\
       "  Squish   [\n"\
       "  Stretch  ]\n"\
       " Quit      ESC\n\n\n"\
       "     DRAWING\n        WITH\n    FRACTALS\n\n"\
       " (C) 1990 by\n Dick Oliver"

#define MENUKEYS {'D', 'P', 'B', TAB, '`', INSERT, DELETE, \
         '*', '/', '+', ';', '[', ']', ESC}
#define MENUWD 15 /* width of menu in characters */
#define NMENUITEMS 14 /* number of menu items */
#define HAND 64 /* size of handle in pixels */
#define MAXPTS 19 /* max. no. of points on seed */
#define MAXTRANS 19 /* max. no. of parts of template */

/* template variables:
   spininc is the amount to rotate (in radians) each time spin is picked
   sizeinc is the amount to grow or shrink
   ra, rb, rc, rd, rmx, and rmy are the reverse of the initial tranformation
   fa, fb, fc, and fd are the tranformations computed from
   sizex, sizey, spinx, and spiny
   movex and movey are the translation part of the transformations
   asprat is the aspect ratio (always 1 for VGA)
   fx and fy are used for various temporary storage purposes
   x and y are the points on the seed polygon */

float sizeinc = 0.16, spininc = PI / 16,
     ra, rb, rc, rd, rmx, rmy,
     fa[MAXTRANS + 1], fb[MAXTRANS + 1], fc[MAXTRANS + 1], fd[MAXTRANS + 1],
     sizex[MAXTRANS + 1] = {1, SIZEX}, sizey[MAXTRANS + 1] = {1, SIZEY},
     spinx[MAXTRANS + 1] = {0, SPINX}, spiny[MAXTRANS + 1] = {0, SPINY},
     movex[MAXTRANS + 1] = {CENTERX, MOVEX},
     movey[MAXTRANS + 1] = {CENTERY, MOVEY},
     asprat, fx, fy, x[MAXPTS] = {SEEDX}, y[MAXPTS] = {SEEDY};

     /* menu vars */
char a, menukeys[] = MENUKEYS;

/* xtop, etc. are the points on the handle
   drawclr is the text and handle color
   xx, yy, midx, and midy are used for various pixel-shuffling operations
   menuitem is the current menu choice
   hand is the size of the handle in pixels
   sk is used to keep track of when to re-sketch the template
   xo and yo are the current template corners for quick erasing
   thispt & thistran are the current point/part
   npts is the number of point, ntrans is the number of parts,
   level is the number of levels of detail to draw or paint
   color determines the color to make each part at each level
*/
int xtop, ytop, xctr, yctr, xlft, ylft, xrgt, yrgt,
    drawclr, i, j, xx, yy, midx, midy, menuitem = 0, hand = HAND, sk,
    xo[MAXTRANS + 1][MAXPTS], yo[MAXTRANS + 1][MAXPTS], moveinc = 16,
    thispt = 0, thistran = 0, npts = NPOINTS, ntrans = NTRANS,
    level = NLEVELS - 1, color[MAXTRANS][NLEVELS] = COLOR;

    /* ptmode means we're in "point" mode rather than "part" mode*/
enum {OFF, ON} ptmode = OFF;

     /* standard Microsoft video variables */
struct videoconfig vc;
long palette[16] = PALETTE;

    /* these function prototypes are needed to avoid confusion about parameter
     * types (most of the functions aren't prototyped) */
void draw(float a, float b, float c, float d, float mx, float my, int iter);
void warp(float spinxinc, float spinyinc, float sizexinc, float sizeyinc);

main()
{   hello();        /* initialize everything */
    while(1)        /* the main event-processing loop */
    {     geta;       /* geta is a #define */
       switch(a)   /* what shall we do now? */
        {   case BACKSPACE:
       case ' ':
                    /* move ">" to new menu item */
                _settextposition(menuitem + 2, 1);
                _outtext(" ");
               if (a == ' ')
               {     if (++menuitem == NMENUITEMS) menuitem = 0;
               }
               else  if (--menuitem < 0) menuitem = NMENUITEMS - 1;
                _settextposition(menuitem + 2, 1);
                _outtext(">");
                break;
            case ENTER:     /* pick a menu item */
               ungetch(menukeys[menuitem]);
               break;
            default:
      sk = 0;
               switch(a)
                {   case LT: case DN:
                    case RT: case UP:
                        /* move a point or part of the template */
                       xx = 0, yy = 0;
                       switch (a)
                        {   case LT:      xx = -moveinc;   break;
                            case RT:      xx = moveinc;    break;
                            case UP:      yy = -moveinc;   break;
                            case DN:      yy = moveinc;    break;
                        }
                        if (!ptmode && (thistran == 0))
                            *movex += xx, *movey += yy;
                        else
                        {   if (ptmode)
                            {   x[thispt] += xx * ra + yy * rb;
                                y[thispt] += xx * rc + yy * rd;
                            }
                            else movex[thistran] += xx * ra + yy * rb,
                                 movey[thistran] += xx * rc + yy * rd;
                        }
                        break;
                    case '/':          /* Shrink */
                        if (ptmode)
                        {    fx = 1 / (sizeinc + 1);
                            warp(0.0, 0.0, fx, fx);
                        }
                        else
                        {   if ((sizex[thistran] /= sizeinc + 1) == 0)
                                sizex[thistran] = ALMOSTZERO;
                            if ((sizey[thistran] /= sizeinc + 1) == 0)
                                sizey[thistran] = ALMOSTZERO;
                            computef(thistran);
                        }
                        break;
                    case '*':            /* Grow */
                         if (ptmode) warp(0.0, 0.0,sizeinc+1, sizeinc+1);
                        else
                        {   if (((sizex[thistran] *= sizeinc + 1)
                                 > MAXSIZE)
                                && (thistran > 0))
                                    sizex[thistran] = MAXSIZE;
                            if (((sizey[thistran] *= sizeinc + 1)
                                 > MAXSIZE)
                                && (thistran > 0))
                                    sizey[thistran] = MAXSIZE;
                            computef(thistran);
                        }
                        break;
                    case '[':         /* Squish x-axis */
                        if (ptmode) warp(0.0, 0.0, 1/(sizeinc + 1), 1.0);
                        else
                        {    if ((sizex[thistran] /= (sizeinc + 1)) == 0)
                                sizex[thistran] = ALMOSTZERO;
                            computef(thistran);
                        }
                        break;
                    case ']':    /* Stretch x-axis */
                        if (ptmode) warp(0.0, 0.0, sizeinc + 1, 1.0);
                        else
                        {    if (((sizex[thistran] *= sizeinc + 1)
                                 > MAXSIZE)
                                && (thistran > 0))
                                    sizex[thistran] = MAXSIZE;
                            computef(thistran);
                        }
                        break;
                    case '-':      /* Spin counter-clockwise */
                        if (ptmode) warp(-spininc, -spininc, 1.0, 1.0);
                        else
                        {   if ((spinx[thistran] -= spininc) < 0)
                                spinx[thistran] += TWOPI;
                            if ((spiny[thistran] -= spininc) < 0)
                                spiny[thistran] += TWOPI;
                            computef(thistran);
                        }
                        break;
                    case '+':     /* Spin clockwise */
                        if (ptmode) warp(spininc, spininc, 1.0, 1.0);
                        else
                        {   if ((spinx[thistran] += spininc) >= TWOPI)
                                spinx[thistran] -= TWOPI;
                            if ((spiny[thistran] += spininc) >= TWOPI)
                                spiny[thistran] -= TWOPI;
                            computef(thistran);
                        }
                        break;
                    case ';':     /* Skew x-axis counter-clockwise */
                        if (ptmode) warp(spininc, 0.0, 1.0, 1.0);
                        else
                        {   if ((spinx[thistran] += spininc) >= TWOPI)
                                spinx[thistran] -= TWOPI;
                            computef(thistran);
                        }
                        break;
                    case '\'':   /* Skew x-axis clockwise */
                        if (ptmode) warp(-spininc, 0.0, 1.0, 1.0);
                        else
                        {   if ((spinx[thistran] -= spininc) < 0)
                                spinx[thistran] += TWOPI;
                            computef(thistran);
                        }
                        break;
                    case '`':    /* NextPoint */
                        if (ptmode) ++thispt;
                        else ptmode = ON, thistran = 0;
                        if (thispt >= npts) thispt = 0;
                        break;
                    default:
                        switch(a)
                        {   case TAB: /* Next part */
                                if (ptmode)
                                {    ptmode = OFF;
                                     midpoint();
                                }
                                else
                                {    if (++thistran > ntrans) thistran = 0;
                                }
                                break;
                            case 'D':
                            case 'P':
                            case 'B':   /* Draw and/or Paint */
                                _clearscreen(_GCLEARSCREEN);
                                _setcliprgn(0, 0,
                                           vc.numxpixels - 1,
                                           vc.numypixels - 1);
                                _setcolor(**color);
                                if ((a == 'D') || (a == 'B'))
                                    draw(*fa, *fb, *fc, *fd,
                     *movex, *movey, level);
                                if ((a == 'P') || (a == 'B')) paint();
                                printf("\7");
                                getch();
                                _clearscreen(_GCLEARSCREEN);
                                printmenu();
                                break;
                            case ESC:   /* Quit */
                                _setvideomode(_DEFAULTMODE);
                                printf("Seeyalater!");
                                exit(0);
                            case INSERT:  /* Insert a point or part */
                                if (ptmode)
                                {   if (npts < MAXPTS)
                                    {   erase();
                                        ++npts;
                                        for (i = npts - 1; i > thispt; i--)
                                            x[i] = x[i - 1],
                                            y[i] = y[i - 1];
                                        if (thispt > 0)
                                            xx = x[thispt - 1],
                                            yy = y[thispt - 1];
                                        else xx = x[npts - 1],
                                            yy = y[npts - 1];
                                        if ((xx == x[thispt]) &&
                                            (yy == y[thispt]))
                                            x[thispt] += moveinc,
                                            y[thispt] += moveinc;
                                        else x[thispt] =
                                                (xx + x[thispt]) / 2,
                                            y[thispt] =
                                                (yy + y[thispt]) / 2;
                                    }
                                    else printf("\7");
                                }
                                else
                                {   if ((ntrans < MAXTRANS) && (ntrans > 0))
                                    {   ++ntrans;
                                        for (i = ntrans; i > thistran; i--)
                                        {   if (i > 1)
                                            {   movex[i] = movex[i - 1];
                                                movey[i] = movey[i - 1];
                                                spinx[i] = spinx[i - 1];
                                                spiny[i] = spiny[i - 1];
                                                sizex[i] = sizex[i - 1];
                                                sizey[i] = sizey[i - 1];
                                                for (j = 0; j < NLEVELS;
                                                    j++)
                                                    color[i - 1][j] =
                                                        color[i - 2][j];
                                                fa[i] = fa[i - 1];
                                                fb[i] = fb[i - 1];
                                                fc[i] = fc[i - 1];
                                                fd[i] = fd[i - 1];
                                            }
                                            else
                                            {   spinx[1] = 0;
                                                spiny[1] = 0;
                                                sizex[1] = sizey[1];
                                                computef(1);
                                            }
                                        }
                                        if (thistran == 0) thistran = 1,i = 1;
                                        if (thistran > 1) j = thistran - 1;
                                        else j = ntrans;
                                        if ((movex[i] == movex[j]) &&
                                            (movey[i] == movey[j]))
                                            movex[i] += moveinc,
                                            movey[i] += moveinc;
                                        else movex[i] =
                                                (movex[i] + movex[j]) / 2,
                                            movey[i] =
                                                (movey[i] + movey[j]) / 2;
                                    }
                                    else
                                    {   if (ntrans == 0) thistran = ++ntrans;
                                        else printf("\7");
                                    }
                                }
                                break;
                            case DELETE:   /* Delete a point or part */
                                erase();
                                if (ptmode)
                                {   if (npts > 1)
                                    {   if (thispt == --npts) --thispt;
                                        else for (i = thispt; i < npts; i++)
                                                x[i] = x[i + 1],
                                                y[i] = y[i + 1];
                                    }
                                    else printf("\7");
                                }
                                else
                                {   if (ntrans > 0)
                                    {   --ntrans;
                                    }
                                    else printf("\7");
                                    if (ntrans > 0)
                                    {   if (thistran == 0) thistran = 1;
                                        else
                                            for (i = thistran;
                                                i <= ntrans; i++)
                                            {   movex[i] = movex[i + 1];
                                                movey[i] = movey[i + 1];
                                                spinx[i] = spinx[i + 1];
                                                spiny[i] = spiny[i + 1];
                                                sizex[i] = sizex[i + 1];
                                                sizey[i] = sizey[i + 1];
                                                for (j = 0; j < NLEVELS;
                                                    j++)
                                                    color[i - 1][j] =
                                                        color[i][j];
                                                fa[i] = fa[i + 1];
                                                fb[i] = fb[i + 1];
                                                fc[i] = fc[i + 1];
                                                fd[i] = fd[i + 1];
                                            }
                                    }
                                    if (thistran > ntrans) --thistran;
                                }
                        }
                        sk = 1;
                }
                erase();
                sketch(sk);
        }
    }
}
    /* midpoint() -- find the center of the seed */
midpoint()
{   int xx, yy;
    midx = 0, midy = 0;
    for (i = 0; i < npts; i++) midx += x[i], midy += y[i];
    midx /= npts, midy /= npts;
    for (i = 0; i < npts; i++) x[i] -= midx, y[i] -= midy;
    for (i = 1; i <= ntrans; i++)
    {   xx = midx * fa[i] + midy * fb[i];
        yy = midx * fc[i] + midy * fd[i];
        movex[i] -= midx - xx;
        movey[i] -= midy - yy;
    }
    xx = midx * *fa + midy * *fb,
    yy = midx * *fc + midy * *fd;
    *movex += xx,
    *movey += yy;
}

    /* compute the affine transformations expressed by the template */
computef(int i)
{   fa[i] =  sizex[i] * cos(spinx[i]);
    fb[i] = -sizey[i] * sin(spiny[i]);
    fc[i] =  sizex[i] * sin(spinx[i]);
    fd[i] =  sizey[i] * cos(spiny[i]);
    if (i == 0)
    {   if ((fx = *fa * *fd - *fb * *fc) == 0) fx = 0.001;
        ra = *fd / fx;
        rb = - *fb / fx;
        rc = - *fc / fx;
        rd = *fa / fx;
    }
}
     /* warp the seed shape  (used to skew, squish, and stretch) */
void warp(float spinxinc, float spinyinc, float sizexinc, float sizeyinc)
{   float a, b, c, d, dsizex, dsizey, dspinx, dspiny;
    dspinx = spinxinc + *spinx;
    dspiny = spinyinc + *spiny;
    dsizex = sizexinc * *sizex;
    dsizey = sizeyinc * *sizey;
    a =  cos(dspinx) * dsizex;
    b = -sin(dspiny) * dsizey;
    c =  sin(dspinx) * dsizex;
    d =  cos(dspiny) * dsizey;
    for (i = 0; i < MAXPTS; i++)
    {   fx = x[i] * a + y[i] * b;
        fy = x[i] * c + y[i] * d;
        x[i] = fx * ra + fy * rb;
        y[i] = fx * rc + fy * rd;
    }
}

    /*  sketch() -- sketch the template and the handle.
     *  Note that the handle shows not only which part you are on,
     *  but also the relative size and orientation of both axes. */
sketch(int all)
{   int i, j, x1, y1, inc, tran0 = 0;
    float x2, y2, a, b, c, d, mx, my;
    inc = hand;
    if (ptmode)
    {   tran0 = 1;
        inc *= *sizey / 2;
        fx = x[thispt],  fy = y[thispt];
        x1 = fx * *fa + fy * *fb + *movex;
        y1 = fx * *fc + fy * *fd + *movey;
        xctr = x1, yctr = (y1 + inc) * asprat;
        xtop = x1, ytop = (y1 - inc) * asprat;
        y1 *= asprat;
        xlft = x1 - inc, ylft = y1;
        xrgt = x1 + inc, yrgt = y1;
    }
    else
    {   if (thistran == 0) x1 = 0, y1 = 0, tran0 = 1;
        else x1 = movex[thistran], y1 = movey[thistran];
        if (tran0) fx = x1, fy = y1 - inc;
        else fx = x1 - inc * fb[thistran],
            fy = y1 - inc * fd[thistran];
        xtop = fx * *fa + fy * *fb + *movex;
        ytop = (fx * *fc + fy * *fd + *movey) * asprat;
        xctr = x1 * *fa + y1 * *fb + *movex;
        yctr = (x1 * *fc + y1 * *fd + *movey) * asprat;
        inc /= 2;
        if (tran0) fx = x1 - inc, fy = y1;
        else fx = x1 - inc * fa[thistran],
            fy = y1 - inc * fc[thistran];
        xlft = fx * *fa + fy * *fb + *movex;
        ylft = (fx * *fc + fy * *fd + *movey) * asprat;
        if (tran0) fx = x1 + inc, fy = y1;
        else fx = x1 + inc * fa[thistran],
            fy = y1 + inc * fc[thistran];
        xrgt = fx * *fa + fy * *fb + *movex;
        yrgt = (fx * *fc + fy * *fd + *movey) * asprat;
    }
    _setcolor(**color);
    for (j = 0; j < npts; j++)
    {   x1 = x[j] * *fa + y[j] * *fb + *movex;
        y1 = (x[j] * *fc + y[j] * *fd + *movey) * asprat;
        (*xo)[j] = x1, (*yo)[j] = y1;
        if (j == 0) _moveto(x1, y1);
        else _lineto(x1, y1);
    }
    _lineto(**xo, **yo);
    for (i = 1; i <= ntrans; i++)
    {   if ((thistran == 0) || (i == thistran) || (all))
        {   _setcolor(color[i - 1][level]);
            a = fa[i] * *fa + fc[i] * *fb;
            b = fb[i] * *fa + fd[i] * *fb;
            c = fa[i] * *fc + fc[i] * *fd;
            d = fb[i] * *fc + fd[i] * *fd;
            mx = movex[i] * *fa + movey[i] * *fb + *movex;
            my = movex[i] * *fc + movey[i] * *fd + *movey;
            for (j = 0; j < npts; j++)
            {   x1 = a * x[j] + b * y[j] + mx;
                y1 = (c * x[j] + d * y[j] + my) * asprat;
                if (j == 0) _moveto(x1, y1);
                else _lineto(x1, y1);
                xo[i][j] = x1, yo[i][j] = y1;
            }
            _lineto(*(xo[i]), *(yo[i]));
        }
    }
    _setcolor(drawclr);
    _moveto(xtop, ytop);
    _lineto(xctr, yctr);
    _moveto(xlft, ylft);
    _lineto(xrgt, yrgt);
}
    /* erase the template */
erase()
{   _setcolor(0);
    _moveto(**xo, **yo);
    for (i = 1; i < npts; i++) _lineto((*xo)[i], (*yo)[i]);
    _lineto(**xo, **yo);
    for (i = 1; i <= ntrans; i++)
    {   if ((thistran == 0) || (i == thistran))
        {   _moveto(*(xo[i]), *(yo[i]));
            for (j = 0; j < npts; j++) _lineto(xo[i][j], yo[i][j]);
            _lineto(*(xo[i]), *(yo[i]));
        }
    }
    _moveto(xtop, ytop);
    _lineto(xctr, yctr);
    _moveto(xlft, ylft);
    _lineto(xrgt, yrgt);
}

   /* paint() -- uses the "Chaos Game", or "random iteration" algorithm
    *            to paint the "infinite-level" fractal on the screen. */
paint()
{   int i, j, p[MAXTRANS], tc, tp, ci[NLEVELS], cc = 0, mx, my;
    unsigned long ct = COUNT;
    float x1 = 0.0, y1 = 0.0, x2, y2 = 0, sx[MAXTRANS], sy[MAXTRANS];
    mx = *movex, my = *movey;

    /* First, we need to compute the relative area of each part of the
       template.  This is done by comparing the size of the determinants
       of the matrix (a,b,c,d).  These weights are then used to decide
       how often to visit each part--big parts get more visits than small
       ones, giving the overall fractal an even density. */
    for (i = 1; i <= ntrans; i++)
        y2 += (sx[i - 1] =
                fabs(fa[i] * fd[i] - fb[i] * fc[i]));
    if (y2 == 0) y2 = 0.01;
    x2 = MAXINT / y2;
    j = 0;
    for (i = 0; i < ntrans; i++)
    {   if ((xx = sx[i] * x2) == 0) xx = 1;
        p[i] = (j += xx);
    }

    /* We skip the first eight points on our journey, because it may take
       that long to settle down from wherever we started onto the fractal.*/
    for (j = 0; j < 8; j++)
    {   i = rand() % ntrans + 1;
        x2 = x1 * fa[i] + y1 * fb[i] + movex[i];
        y2 = x1 * fc[i] + y1 * fd[i] + movey[i];
        x1 = x2, y1 = y2;
        ci[cc] = i;
        if (++cc == level) cc = 0;
    }

    /* Now we put it on the screen.  The cc, tc, and ci variables are used
       to determine coloring.  At each iteration, we choose a level at
       random out of all the levels we will be coloring.  We then find the
       color for that level based on which part was "visited" that many
       iterations ago.  How does this work?  Each iteration of the orbit
       goes from a point on the "whole" to a point on a "part".  Therefore,
       if we were in part #3 one iteration ago, we are now in a level 1
       reflection of part #3 within the current part.  If we were at part #5
       two cycles ago, we are now painting a point within a level 2
       reflection of part #5, and so on.  */
    while(!kbhit() && (--ct != 0))
    {   j = rand();
        for (i = 0; i < ntrans; i++) if (j < p[i]) break;
        i++;
        x2 = x1 * fa[i] + y1 * fb[i] + movex[i];
        y2 = x1 * fc[i] + y1 * fd[i] + movey[i];
        x1 = x2, y1 = y2;
        ci[cc] = i - 1;
        j = rand() % level;
        if ((i = cc - j) < 0) i += level;
        if ((tc = color[ci[i]][j + 1]) > 0)
        {   _setcolor(tc);
            _setpixel((int) (x2 * *fa + y2 * *fb + mx),
                    (int) ((x2 * *fc + y2 * *fd + my) * asprat));
        }
        if (++cc == level) cc = 0;
    }
}

/* draw() -- uses the "successive approximation" algorithm to draw
   the fractal.*/
void draw(float a, float b, float c, float d, float mx, float my, int iter)
{   int i;

    /* When we start drawing, iter is the number of levels to draw.
       Each time we go down one level, we decrement iter. */
    iter--;

    /* If user hits ESC, pass that keypress up through the recursive
       calls until we get back to the main procedure.*/
    if (kbhit() && (getch() == ESC))
    {   ungetch(ESC);
        return;
    }

    /*  Draw a reflection of the seed polygon using the current
        transformation */
    for (i = 0; i < npts; i++)
    {    fx = x[i] * a + y[i] * b + mx;
         fy = x[i] * c + y[i] * d + my;
         if (i == 0)
         {    xx = fx, yy = fy;
              _moveto((int) fx, (int) (fy * asprat));
         }
         else _lineto((int) fx, (int) (fy * asprat));
    }
    _lineto((int) xx, (int) (yy * asprat));

    /* If iter has counted all the way down to zero, don't draw the next
       deepest level, but back out one level instead */

    if (iter < 0) return;
    else
    {   /* Call draw recursively for each transformation, drawing the
           next deepest level of each part */
        for (i = 1; i <= ntrans; i++)
        {   _setcolor(color[i - 1][level - iter]);
            draw(fa[i] * a + fc[i] * b,
                 fb[i] * a + fd[i] * b,
                 fa[i] * c + fc[i] * d,
                 fb[i] * c + fd[i] * d,
                 a * movex[i] + b * movey[i] + mx,
                 c * movex[i] + d * movey[i] + my,
                 iter);
        }
    }
}

    /* display the menu */
printmenu()
{   _settextwindow(1, vc.numtextcols - MENUWD,
            vc.numtextrows, vc.numtextcols);
    _clearscreen(_GWINDOW);
    _outtext(MENUMSG);
    _settextposition(menuitem + 2, 1);
    _outtext(">");
    _setcliprgn(0, 0, vc.numxpixels - (MENUWD + 1) *
                (vc.numxpixels / vc.numtextcols) - 1,
                vc.numypixels - 1);
}

    /* hello() -- initialize everything */
hello()
{   if (!_setvideomode(_VRES16COLOR))
    {   printf("Can't set video mode.  VGA required.");
    }
    _getvideoconfig(&vc);
    _remapallpalette(palette);
    _wrapon(_GWRAPOFF);
    _clearscreen(_GCLEARSCREEN);
    printmenu();
    asprat = (float) (4 * vc.numypixels) / (float) (3 * vc.numxpixels);
    drawclr = vc.numcolors - 1;
    for (i = 0; i <= ntrans; i++) computef(i);
    sketch(0);
}





[LISTING FIVE]


/*****************************************************************
     FLAKE.H --- Header file for snowflake template
     This (and the other header files like it) can be used to define
     the initial fractal template for the SIERP.C and FRACDRAW.C programs
     This template models the crystalization process to draw a realistic
     snowflake shape.
     See additional comments in SIERP.H
*****************************************************************/
#define NPOINTS 6    /* Number of points on the "parent" polygon */
#define NTRANS  6    /* Number of transformed "children" */
#define NLEVELS 5    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 240
#define SEEDX 1,21,21,1,-21,-21  /* The "parent" polygon */
#define SEEDY -27,-15,9,40,9,-15

/* The tranformations which define the "children" */
#define MOVEX -1,55,55,1,-55,-55  /* Displacement */
#define MOVEY -65,-35,35,65,35,-35
#define SIZEX   .18,.18,.18,.18,.18,.18    /* Size change */
#define SIZEY   .91,.91,.91,.91,.91,.91
#define SPINX   0,1.05,2.09,3.14,4.19,5.24    /* Rotation */
#define SPINY   0,1.05,2.09,3.14,4.19,5.24

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
            _BLUE, _MAGENTA, _BROWN, _WHITE, \
       _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
       _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15}}





[LISTING SIX]


/****************************************************************
   CUMULUS.H--- Header file for cumulus cloud template
   This (and the other header files like it) can be used to define
   the initial fractal template for the SIERP.C and FRACDRAW.C programs
   See additional comments in SIERP.H
****************************************************************/

#define NPOINTS 7    /* Number of points on the "parent" polygon */
#define NTRANS  6    /* Number of transformed "children" */
#define NLEVELS 5    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 240
#define SEEDX 23,17,-4,-10,-27,7,44  /* The "parent" polygon */
#define SEEDY 0,55,55,0,9,-66,10

/* The tranformations which define the "children" */
#define MOVEX   85,-94,-3,51.5,-49,0  /* Displacement */
#define MOVEY   15,13,3.5,-35,-40,40
#define SIZEX   .36,.4,.53,.48,.4,.87    /* Size change */
#define SIZEY   .36,.47,.53,.53,.4,.33
#define SPINX   .25,6,6.2,0.15,6.2,0    /* Rotation */
#define SPINY   .25,6,6.2,0.15,6.2,6.3

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
      _BLUE, _MAGENTA, _BROWN, _WHITE, \
      _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
      _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{15,15,15, 7, 8},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15}}

/*****************************************************************
   CIRRUS.H--    Header file for cirrus cloud template
     In this and the other cloud models (STRATUS and CUMULUS), the air
     currents which control cloud formation are modelled as arrows.
     When those forces are reflected throughout all levels of scale,
     a realistic image of the cloud type that results from those air
     currents appears.  Cirrus clouds are characterized by high-altitude
     rising cross winds, stratus clouds by slow horizontal air flow,
     and cumulus clouds by warm air rising from the ground.
*****************************************************************/

#define NPOINTS 7    /* Number of points on the "parent" polygon */
#define NTRANS  6    /* Number of transformed "children" */
#define NLEVELS 5    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 180  /* Center of the screen */
#define CENTERY 240
#define SEEDX 16,-27,-42,-7,-27,54,23  /* The "parent" polygon */
#define SEEDY  33,52,36,-7,-13,-43,36

/* The tranformations which define the "children" */
#define MOVEX 143.4,-90,5,-4  /* Displacement */
#define MOVEY 11.08,13.6,-15.5,45
#define SIZEX   .75,.43,.38,.75    /* Size change */
#define SIZEY   .45,.47,.44,.21
#define SPINX   6.07,0.05,0.02,0.0    /* Rotation */
#define SPINY   6.07,0.05,0.02,6.28

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
       _BLUE, _MAGENTA, _BROWN, _WHITE, \
       _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
       _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{15,15,15, 7, 8},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15}}


/*****************************************************************
   LEAF.H ---     Header file for maple leaf template
    This is the "geometric genetic code" for a maple leaf.  Note the close
    similarity to the MAPLE tree template.
*****************************************************************/
#define NPOINTS 4    /* Number of points on the "parent" polygon */
#define NTRANS  4    /* Number of transformed "children" */
#define NLEVELS 6    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 350
#define SEEDX 6,20,-6,-12  /* The "parent" polygon */
#define SEEDY -120,120,120,-120

/* The tranformations which define the "children" */
#define MOVEX -1.15,-53,51,-6  /* Displacement */
#define MOVEY 35,7,6,-111
#define SIZEX  0.14,0.62,0.65,0.49 /* Size change */
#define SIZEY  0.51,0.72,0.68,0.51
#define SPINX  6.26,5.47,0.81,6.28 /* Rotation */
#define SPINY  6.26,5.47,0.81,6.28

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
       _BLUE, _MAGENTA, _BROWN, _WHITE, \
       _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
       _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2},\
               {6, 6,14,14,10, 2}}

/*****************************************************************
   MOUSE.H  --     Header file for cartoon mouse template
     This template was made by tracing the rough outline of an image and
     tiling with copies of itself.
*****************************************************************/
#define NPOINTS 14    /* Number of points on the "parent" polygon */
#define NTRANS  18    /* Number of transformed "children" */
#define NLEVELS 2    /* Number of levels to draw */
#define COUNT 50000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 240
#define SEEDX 131,140,97,6,-56,-97,-146,-148,-121,-101,-47,-3,32,29
#define SEEDY 5,55,99,133,121,70,21,-17,-17,-31,-20,-78,-93,-71

/* The tranformations which define the "children" */
#define MOVEX -89,36,-60,-4,4,61,-71,1,81,-49,-133,-130,-8,-3,-36,-24,13,15
#define MOVEY -3,13,25,-35,-63,-43,101,116,56,87,-50,-24,104,-1,-20,-27,-16,76
#define SIZEX .31,.4,.62,.07,.19,.32,.19,.4,.55,.31,.12,.17,.21,.06,.06,\
         .08,.11,.42
#define SIZEY .18,.35,.44,.27,.27,.48,.06,.13,.33,.20,.04,.23,.12,.16,.14,\
         .2,.23,.2
#define SPINX 3.23,6.4,.32,2.72,5.84,4.61,.75,6.25,5.34,.29,3.16,5.9,3.04,\
              4.15,4.32,.91,1.1,2.55
#define SPINY 3.6,6.5,.77,.37,3.49,4.28,.75,6.25,5.27,.1,2.85,2.65,2.53,\
              3.56,3.73,3.73,3.53,5.89

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
       _BLUE, _MAGENTA, _BROWN, _WHITE, \
       _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
       _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{6,6},\
               {6,6},\
               {6,6},\
               {6,6},\
               {6,6},\
               {6,6},\
               {1,1},\
               {6,6}, {6,6}, {1,1}, {7,7}, {8,8}, {12,12},\
               {9,9}, {9,9}, {15,15}, {15,15}, {1,1}}

/*****************************************************************
   PINE.H --- Header file for pine tree template
     This (and the other header files like it) can be used to define
     the initial fractal template for the SIERP.C and FRACDRAW.C programs
*****************************************************************/

#define NPOINTS 4    /* Number of points on the "parent" polygon */
#define NTRANS  6    /* Number of transformed "children" */
#define NLEVELS 5    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 350
#define SEEDX 6,20,-6,-12  /* The "parent" polygon */
#define SEEDY -120,120,120,-120

/* The tranformations which define the "children" */

#define MOVEX -41.2,36.9,5.13,-14.64,2.2,40.07 /* Displacement */
#define MOVEY 14.987,-61.31,7.10,-32.33,-50.46
#define SIZEX  0.39,0.41,0.52,0.35,0.86,0.37 /* Size change */
#define SIZEY  0.39,0.31,0.17,0.24,0.79,0.42
#define SPINX  5.62,0.61,6.15,5.43,3.27,0.54 /* Rotation */
#define SPINY  4.91,1.27,0.13,4.71,6.28,1.4

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
      _BLUE, _MAGENTA, _BROWN, _WHITE, \
      _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
      _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{6, 6,14,10, 2},\
               {6, 6,14,10, 2},\
               {6, 6,14,10, 2},\
               {6, 6,14,10, 2},\
               {6, 6,14,10, 2},\
               {6, 6,14,10, 2},\
               {6, 6,14,10, 2},\
               {6, 6,14,10, 2},\
               {6, 6,14,10, 2}}
/*****************************************************************
  STRATUS.H --     Header file for stratus cloud template
****************************************************************/

#define NPOINTS 6    /* Number of points on the "parent" polygon */
#define NTRANS  5    /* Number of transformed "children" */
#define NLEVELS 5    /* Number of levels to draw */
#define COUNT 10000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 240
#define SEEDX 40,80,40,-40,-80,-40  /* The "parent" polygon */
#define SEEDY 22,-2,-22,-22,2,22

/* The tranformations which define the "children" */
#define MOVEX -70,-44,45,60,-3.3  /* Displacement */
#define MOVEY 11,-34,-31,1.6,42
#define SIZEX   .75,.43,.38,.75,.8    /* Size change */
#define SIZEY   .45,.47,.44,.61,.2
#define SPINX   6.3,6.3,6.3,6.3,0    /* Rotation */
#define SPINY   6.3,6.3,6.3,6.3,6.3

#define PALETTE {_BLACK, _RED, _GREEN, _CYAN, \
      _BLUE, _MAGENTA, _BROWN, _WHITE, \
      _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
      _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{15,15,15, 7, 8},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15},\
               {15,15,15,15,15}}
/*****************************************************************
   ZOOM.H --   Header file for abstract zooming artwork template
     Note that this very simple template creates a visually complex image,
     while seemingly simple linear forms like the cartoon mouse require
     much more work and information to express as fractals.  When drawing
     with fractals, richly detailed trees are simpler and easier to create
     than smooth objects like tables and chairs!
*****************************************************************/

#define NPOINTS 5    /* Number of points on the "parent" polygon */
#define NTRANS  5    /* Number of transformed "children" */
#define NLEVELS 4    /* Number of levels to draw */
#define COUNT 20000  /* Number of dots to paint */
#define CENTERX 320  /* Center of the screen */
#define CENTERY 340
#define SEEDX -66,-334,60,272,66
#define SEEDY -7,100,-120,-27,55

/* The tranformations which define the "children" */

#define MOVEX 55,104,185,30,-45
#define MOVEY -309,-1,-50,28,-25
#define SIZEX .27,.36,.5,.28,.98
#define SIZEY .27,.27,.18,.21,.5
#define SPINX 4.71,3.88,3.34,4.3,6
#define SPINY 2.48,.93,.39,1.16,6

#define PALETTE {_BLUE, _RED, _GREEN, _CYAN, \
                _BLACK, _MAGENTA, _BROWN, _WHITE, \
      _GRAY, _LIGHTBLUE, _LIGHTGREEN, _LIGHTCYAN, \
      _LIGHTRED, _LIGHTMAGENTA, _LIGHTYELLOW, _BRIGHTWHITE}

#define COLOR {{15,15,15,15},\
               {14,14,14,14},\
               {14,14,14,14},\
               {14,14,14,14},\
               {14,14,14,14},\
               {15,15,15,15},\
               {15,15,15,15}}


Copyright © 1991, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.