_FRACTALS IN THE REAL WORLD_ by Dick Oliver [LISTING 1] /**************************************************************** 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 /* For getch() */ #include /* For cos() and sin() */ #include /* 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 #include #include #include #include #include /* #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}}