Classic Computer Magazine Archive COMPUTE! ISSUE 87 / AUGUST 1987 / PAGE 89

Fractal Mountains For Amiga

Matthew Timmerman

With this landscape-generating program and a 512K Amiga computer, you can create fascinating graphics displays based on the principles of fractal geometry.

"Fractal Mountains for Amiga" is an intriguing graphics program that draws landscape-like scenes containing rough, crinkled-looking mountain shapes. Although you can run it and simply enjoy the pictures it creates, the program is based on highly advanced mathematical concepts which you may wish to learn more about. Type in the program and save a copy before you run it.

When you run Fractal Mountains, it asks you to enter a number for the random seed. This value determines which landscape the program generates. There are 65,536 possible landscapes, so you needn't repeat a landscape very often unless you find one you like particularly. Enter a number in the range indicated by the onscreen prompt. (The first time you run the program, try entering 70 for the random seed.)

The second prompt asks you to enter a number for the maximum variation. This value determines the cragginess of the mountains. Although the program prompts you to enter a value in the range 0-2, you aren't necessarily limited to that range. A variation of 0 gives you a perfectly flat plane (no variation), while a value of 2 gives you extremely high, rugged mountain peaks. (The first time you run the program, try entering 1.96 for the maximum variation.)

After you've entered those values, the program draws the landscape. Please be patient while the process is underway. Although Amiga BASIC is one of the fastest microcomputer BASICs, it still takes considerable time to perform the tens of thousands of calculations this program requires.

This is only 1 of 65,536 possible landscapes with "Fractal Mountains for Amiga."

Once the picture is complete, you can save the picture to disk by pressing the S key. To show you what is happening, the program draws a white line on the screen indicating which line of the picture it is saving. The picture is saved in ILBM (InterLeaved BitMap) format, which allows you to load it into Deluxe Paint, Graphicraft, and other IFF-compatible art programs. To exit the program, press the space bar.

If you find a mountain that you like, but it is too smooth, use the same random seed value with a higher maximum variation. If you get a landscape which is mostly water, try it again using a negative maximum variation. The negative value inverts the landscape: What was once land becomes water, and vice versa. Since gravity is meaningless in this universe, pictures look as good upside down as they do right side up. Don't be afraid to experiment with different values. Not all combinations give pleasing results, but exploration is one of the interesting aspects of using a program like this.

Why Fractals?

A fractal is an object with a fractional dimension—a value between 1 and 2, for instance, or between 2 and 3. In his book The Fractal Geometry of Nature, Benoit Mandelbrot tells us to imagine a piece of aluminum foil. When it's flat, it is a simple plane with two dimensions. As you begin to crinkle the foil, it can no longer be confined to two dimensions, but it is not yet three-dimensional. Therefore, it has a dimension somewhere 2 and 3. The aluminum foil becomes more crinkled until, eventually, it becomes a solid block filling three dimensions.

The same analogy can be applied to a straight line becoming bent until it becomes infinitely bent and complex, completely darkening the surface on which it is drawn. At that point, the line is no longer a line, but a two-dimensional plane.

Another aspect of fractals is self-similarity, meaning that the big parts of the object look like the little parts. To take a rough example, if you break a chunk of rock from a mountain, the rock looks like a miniature mountain. It's not the same, but it has the same general look. This phenomenon occurs throughout nature: in tree bark, snowflakes, coastlines of continents, trees, clouds, surfaces of proteins, all types of turbulence, and the positioning of stars, planets, solar systems, and galaxies—all of which are only a few of the countless possibilities.

The algorithm used to create these pictures is derived from one described in the September 1984 issue of Scientific American. However, whereas that formula was based on a triangle, this one is based on a square (for display purposes).

To understand how the program works, imagine that you begin with a square, putting a point in the center of all four sides of the shape. Next, raise or lower those points a random amount, as much as half the length of the square. At that point, you put a point in the center of the square and give it the average height of all the corners. Raise or lower this point by a random amount, using the same conditions as in the previous adjustment, and connect the points.

You now have four smaller squares. By repeating this process, you obtain smaller and smaller squares, eventually obtaining a good approximation of a natural landscape surface.

The most difficult part of making a convincing mountain is putting the picture on the screen. This program draws the mountains as if the sun were directly above them, for a couple of reasons. Since the algorithm produces no overhanging pieces, you don't have to worry about one part of the landscape shadowing another part. Secondly, the program already takes a considerable time to work, without adding the extra complication and delay of computing the effect of light falling at an angle.

The landscape is stored as a square grid of height values in an array named lv. In order to draw the landscape, each surface in the array must be given a "shade value" in relation to how bright the surface appears. The subroutine Getshade calculates the shade value as the slope of the plane in relation to the light source.

The problem is that the four points do not necessarily fall all on the same plane. For this reason, a point is placed in the center of each square and a separate shade value is calculated for all four triangles that are formed. What you get is an aerial view of mountains, islands, or whatever happens to come out. The waterline is at zero, meaning that all points in the viewing area with negative values are covered with water. In addition, snow covers all peaks more than three-quarters as high as the highest point in the picture.

Fractal Mountains For Amiga

For instructions on entering this program, please refer to "COMPUTE!'s Guide to Typing In Programs" elsewhere in this issue.

' Copyright 1987 Compute! Publications, Inc.
' All Rights Reserved.
CLEAR 32767
DEFSNG a - z
DIM Lv (64, 64)
DIM cmap$ (31)
PRINT" Copyright 1987"
PRINT"Compute! Publications, Inc."
PRINT" All Rights Reserved." : PRINT
RANDOMIZE
PRINT "Enter maximum variation (0 - 2) (1 is nice) " ; : INPUT max
PRINT "Enter a filename to save picture under."
INPUT "(Saving at end is optional.)" ; fiL$
FOR a = 1 TO 10
PRINT RND
NEXT
SCREEN 2, 320, 200, 5, 1
WINDOW 3, "Mountain", (0, 0) - (311, 186), 28, 2
FOR a = 0 TO 15
PALETTE a, a/15, a/25, a/50
PALETTE a + 16, a/15, a/15, a/15 4 a$ = CHR$ (a*17)
cmap$(a) = a$ + CHR$(a * 10.2) + CHR$(a * 5.1)
cmap$(a + 16) = a$ + a$ + a$
NEXT
PALETTE 16, 0, .25, .5
cmap$(16) = CHR$(0) + CHR$(64) + CHR$(128)
COLOR 15
maxLv = 0
MakeMount : 
FOR iter = 6 TO 1 STEP -1
sk = 2 ^ iter
hL = sk/2
PRINT "Doing Iteration" ; iter
Dotops : 
PRINT "Tops & Bottoms " ;
FOR y = 0 TO 64 STEP sk
FOR x = hL TO 64 STEP sk
ran = (RND-.5) * max * sk
oLd = (Lv(x-hL, y) + Lv(x + hL, y)) / 2
Lv(x, y) = oLd + ran
NEXT x
NEXT y
Dobottoms : 
PRINT "Sides " ;
FOR x = 0 TO 64 STEP sk
FOR y = hL TO 64 STEP sk
ran = (RND - .5) * max * sk
oLd = (Lv(x, y - hL) + Lv(x, y + hL)) / 2
Lv(x, y) = oLd + ran
NEXT y
NEXT x
Docentres : 
PRINT "Centers "
FOR x = hL TO 64 STEP sk
FOR y = hL TO 64 STEP sk
ran = (RND - .5) * max * sk
oLd1 = (Lv(x + hL, y - hL) + Lv(x-hL, y + hL)) / 2
oLd2 = (Lv(x - hL, y - hL) + Lv(x+hL, y + hL)) / 2
oLd = (oLd1 + oLd2)/2
Lv(x, y) = oLd + ran
IF Lv(x, y) > maxLv THEN maxLv = Lv(x, y)
NEXT y
NEXT x
NEXT iter
snowLine = maxLv - maxLv/4
drawmount : 
CLS
xm =
ym = 1
xshift = .5
yp = 70
FOR x = 0 TO 64
IF Lv(x, 0) < 0 THEN Lv(x, 0) = 0
NEXT x
FOR y = 0 TO 63
IF Lv(0, y) < 0 THEN Lv(0, y) = 0
FOR x = 0 TO 63
IF Lv (x + 1, y + 1) < 0 THEN Lv(x + 1, y + 1) = 0
Lv = Lv(x, y) + Lv(x + 1, y) + Lv(x, y + 1)
Lv = (Lv + Lv(x + 1, y + 1))/4
a = x : b = y
rxl = xm * a + xshift * b
ryl = ym * b + yp -Lv(a, b)
GOSUB getshade : 
shadel = shade
a = x + 1
rx2 = xm * a + xshift * b
ry2 = ym * b + yp - Lv(a, b)
GOSUB getshade : 
shade2 = shade
a = x : b = y + 1
rx3 = xm * a + xshift * b
ry3 = ym * b + yp -Lv(a, b)
GOSUB getshade : 
shade3 = shade
a = x + 1
rx4 = xm * a + xshift * b
ry4 = ym * b + yp -Lv(a, b)
GOSUB getshade : 
shade4 = shade
a = x + .5 : b = y + .5
rx = xm * a + xshift * b
ry = ym * b + yp
a = x : b = y
ry = ry - Lv
AREA (rx, ry)
AREA (rx1, ry1)
AREA (rx2, ry2)
COLOR shadel
AREAFILL
AREA (rx, ry)
AREA (rx2, ry2)
AREA (rx4, ry4)
COLOR shade2
AREAFILL
AREA (rx, ry)
AREA (rx1, ry1)
AREA (rx3, ry3)
COLOR shade3
AREAFILL
AREA (rx, ry)
AREA (rx3, ry3)
AREA (rx4, ry4)
COLOR shade4
AREAFILL
NEXT x
NEXT y
ender : 
a $ = INKEY$
IF a$ = "s" THEN GOTO savepic
IF a$ <> "" THEN GOTO ender
end 2 : 
WINDOW CLOSE 3
SCREEN CLOSE 2
WINDOW OUTPUT 1
END
getshade : 
c = x + 1 - (b - y)
d = y + (a - x)
xc = x + .5
yc = y + .5
xrun1 = xc - a
xrun2 = xc - c
yrun1 = yc - b
yrun2 = yc - d
rise1 = Lv - Lv (a, b)
rise2 = Lv - Lv (c, d)
yrise = ABS(risel *xrun2 - rise2 *xrun1)
yrunl = ABS(yrunl * xrun2 - xrunl *yrun2)
IF yrun = yrise THEN yrun = l : yrise = l
xrise = ABS (risel * yrun2 - rise2 * yrunl)
xrun = ABS (xrunl * yrun2 - yrunl * xrun2)
IF xrun = xrise THEN xrun = l : xrise = l
xrise = xrise / 2
yrise = yrise / 2
xshade = 1 -ABS (xrise / (xrun + xrise))
yshade = 1 -ABS (yrise / (yrun + yrise))
shade = 14 * xshade *yshade + 1
IF Lv > snowLine THEN shade = shade + 16
IF Lv < = 0 THEN shade = 16
RETURN
savepic : 
rastport & = WINDOW (8)
bitmap & = PEEKL (rastport & + 4)
topLine = 60 - INT (maxLv)
IF topLine < 0 THEN topLine = 0
topadd = topLine * 40
FOR a = 0 TO 4
pLane& (a) = PEEKL (bitmap & + 8 + a * 4) + topadd
NEXT
bottomLine = 144
Lines = bottomLine - topLine
OPEN fiL$ FOR OUTPUT AS 1
a$ = MKL$ (Lines * 40 * 5 + 144)
PRINT # 1, "FORM" ; a$; "ILBMBMHD" ; MKL$ (20);
PRINT # 1, MKI$ (320) ; MKI$ (Lines); MKL$ (0) ;
PRINT # 1, CHR$ (5) ; MKI$(0) ; CHR$ (0) ;
PRINT # 1, MKI$ (0) ; CHR$ (10) ; CHR$ (11) ;
PRINT # 1, MKI$ (320); MKI$ (200) ;
PRINT # 1, ‘CMAP" ; MKL$ (96) ;
FOR a = 0 TO 31
PRINT # 1, cmap$ (a) ;
NEXT
PRINT # 1, "BODY" ; MKL$ (Lines * 40 * 5) ;
FOR a = 1 TO Lines
FOR p = 0 TO 4
FOR b = 0 TO 39 STEP 4
PRINT # 1, MKL$ (PEEKL(pLane& (p) + b));
NEXT b
POKEL pLane & (p), -1
PLane & (p) = pLane & (p) + 40
NEXT p
NEXT a
CLOSE
GOTO end2