3-D FRACTALS
Three-dimensional ST landscapes
by PATRICK BASS, Antic ST Program EditorDon't ask me to explain 'em.
Elsewhere in this issue, Charlie Jackson does a good job
of introducing the concept of fractals. But I can move colorful,
graphic images around a computer screen. So when Antic decided to
cover fractals, I opened my mouth and said, "How about three-dimensional
fractals?" All eyes swiveled expectantly in my direction, and I realized
I had just volunteered.
I went off, sat down, scratched my head and began figuring
how to use fractal information to create a 3-D effect.
And that, folks, is what I'm a-gonna pass on to you now...
JUST LIKE MAGIC
There is no magic in twisting an object on the video screen. Most of
you have plotted dots onscreen in a top-down, left-right pattern, right?
Using an XY plotting system to get a horizontal line,
you keep Y constant, and sweep X from the left to the right edge. If you
add or subtract a third value, Z, to the Y component just before you plot
the dot along the line- and consider the Z component the altitude-the
resulting line will rise and fall as the Z component rises and falls. We
may liken this Z line to the ridge-line of a mountain chain.
To make a diagonal line, each time you step up X, add
or subtract a constant amount to Y The result is a line that descends irregularly
from upper left to lower right while carrying the Z component along with
it.
HIT THE SLOPES
To simulate the slope of the view, when it comes time to start plotting
the next line down, either subtract (to slope left), or add (to slope right)
a small constant value to the left edge and the right edge of the display
rectangle.
When we've plotted each dot, drop one dot down and draw
a line from there to the bottom of the display, which is a value
we have previously selected to cover the deepest possible valley. By drawing
from back to front, we don't need to solve the "hidden-line" removal problem
encountered when drawing from front to back.
That's all there is to it. No division or multiplication,
just add or subtract every value except where we get the value for the
Z (height) component.
HIGH/LOW COLORS
Most published fractal images have had color added to them to make
the different regions stand out. On the 520ST, we have at least 16 available
color values ranging from 0 to 15. If we pass the resulting color number
of each XY point to the plotting routine as the Z component, the resulting
different "altitudes" in the final image will each be a different color.
In each of our images the sea-level, or infinity region, is colored jet
black.
Now let's put all of this together into a program. We
will use the Developers (Alcyon) C package, because Hippo-C does not support
floating-point math as of this writing. If you have the disk version of
Antic, you can port the program (FRACTL3D.PRG) over into your 520ST
and run it. (You'll find an ST porting HELP file on side 2 of the monthly
disk.) Otherwise type in the program from the listings section.
Save the program on disk, then compile and assemble it
down into a ".o" file. Link and Relmod this ".o" file together with apstart,
aesbind, vdibind, osbind, and libf into an
executable program, (.pgm).
RUNNING IN 3-D
FRACTL3D is completely mouse-driven and pretty much self-explanatory.
It will work in any resolution. You have a choice of creating two-dimensional
or three-dimensional fractals in three color palettes and then saving the
images to disk as DEGAS picture files.
After you get through the introductory Alert boxes, you
choose your magnification. We're going to work on the familiar Mandlebrot
equation for Julia curves.
Click on the > or < to increase or decrease magnification.
You can choose any value. But if you use all the default values the first
time through, you are guaranteed to get a good image.
The next two boxes select the X and Y coordinates of our
magnification window. Following that we must choose the vertical offset
scale for the Z coordinate. This will be ignored if we later choose a two-dimensional
fractal.
Now we choose between a two or three-dimensional fractal.
It may be a good idea to first choose the two-dimensional image, so you
can better see the differences in the three-dimensional fractal.
If you choose a three-dimensional representation, you
will next be asked if you want hills or valleys. Finally, choose one of
three color palettes and off you go!
The full fractal will take about 20 minutes to an hour
to draw, depending on how much black space (infinity) is in the image.
The more black, the longer it will take. You can abort any image in process
by pressing and holding either mouse button.
When the image is complete, a box will appear permitting
you to save the picture. Whether or not you choose to save your picture,
you are given the choice to start all over again with the original default
values.
PROGRAM TAKE-APART
The only #include file we need here is osbind.h. Below
that we have a block of #define statements, which simply cause the
compiler to replace the first constant (wherever it sees one) with the
second constant. This means, for example, each time the pre-processor finds
the string TRUE in the source code it will replace it with the string 1.
It is a convention of C to make defines all upper-case.
The first two declaration lines save space for the in
and out arrays. (See sidebar on BASIC VDI calls). Then the rest of the
integers are declared-including three color palettes, Earth, Wind and Fire.
The char declarations include all the alert box strings, and the
path and filenames.
Note: Although our published listing of alert()
breaks at the word "written," you should type the entire alert string on
a single line without a carriage return.
Next is the required C function, main(). This is
a very short one, unlike our previous programs, and merely does exactly
what it says. First, initialize(), then, do (draw fractal) while
(not finished). And when finished, terminate() the program.
INITIALIZATION
The starting sequence of instructions is found here. We open our virtual
workstation and determine the width and height of the screen. We save the
color palette currently in use, get the output resolution (0, 1, 2), present
a Hello box, save space for a second screen and then leave.
DRAWING THE FRACTAL
Our biggest routine here is draw_fractal(). First it will save
space for a button and make sure finished = FALSE, then it
will erase the video screen, ask for the ranges of the picture and determine
how the picture is displayed.
graf_mouse( 256, 0x0L); will hide the mouse from
view. Next we enter a double-nested loop, yp and xp, in which
we figure and plot each fractal point in turn, from left to right and from
top to bottom.
Again, the logarithm for figuring each point can be found
Charles Jackson's introductory fractal article in this issue. Evnt_mouse()
tests the mouse buttons for an early exit if either button is pressed,
graf_mouse( 257, 0x0L ); will cause the mouse to reappear. We inquire
whether the user wishes to save the resulting image, and then test if the
user wishes to draw another fractal. If not, we are finished, and fall
out of this section back to main(). If the user wants to save the
picture, the next routine, save_it(), does the job in DEGAS format
and the routine comes to an end.
PLOT RIGHT ALONG
Inside plot_point() is where we figure the Z component offset
and either add or subtract it. The first switch( terrain) statement
sorts that out. Terrain is where I execute the choice of whether
HILLS (1) or VALLEYS (2) are desired.
If HILLS are desired, the scaled color number (0-15) is
subtracted from YP in the XP,YP pair. This causes the resulting
point to be proportionally higher on the screen. If VALLEYS are
wanted, the color number is added, thus moving the point lower.
We keep track of the last point known in old_xp and old_yp.
We v_pline() from the old point to the newly computed point and
then go down to the computed bottom. And right before we leave,
we set the old_xp, old_yp point to the new pair now that
we're finished with them.
ALERT BOXES
Inside get_ranges() there are four sections of code that each
do more or less the same thing. We'll go over the first in detail. First
we set the value we are interested in to a default value, here side=.11;.
Next we set button to FALSE, making sure it's turned off.
The following statement opens a loop by doing exactly
what it says: while (button does not_equal SELECT) perform the block
of code between the braces.
And since the button is FALSE the first time through we
drop to the next statement. This statement says: Take the floating-point
number side, convert it to an ASCII string with five numbers to
the right of the decimal point and place down the string starting at memory
location numbuff. The next line transfers the first five characters
of that number inside the matching alert string, starting with the
alert string's twentieth character.
The line below puts the Alert box on the screen and waits
for a user response. The button number (1,2,3) is returned to the user
inside button. The call takes the form: x = form alert( icon,
string );, where icon is the number of the icon desired (1-3),
or 0 if no icon is wanted. string is the location of the alert string.
After the user clicks an Alert button, we test for two
of the values in the two if statements below the form_ alert()
line. Again, they do exactly what they say they do.
QUESTIONABLE PRACTICES
The next block of code is called ask_questions(), where we first
determine if the final picture will be displayed in TWO_DEE or THREE_DEE.
The code then determines the slope_rate and slope_amount, and figures the
image left and right side.
Next, if the user wants a TWO_DEE picture, certain variables
are reset. The default type of terrain is determined, and changed if desired.
The switch( resolution); block of code will determine the proper
color_step value and correct the filename extender to conform to DEGAS
standard. Finally, we decide which palette to use and implement it in the
switch( palette); block.
The last procedure in the listing is the clear_screen()
code which does exactly that. Actually, it fills a rectangle with a solid
background pattern.
TERMINATION
Leaving is even easier. terminate() automatically closes the
virtual workstation, restores the original color palette and exits this
application.
Listing 1 3-D FRACTALS
C ROUTINE IDENTIFICATIONS
In published C listings for the ST, it is often difficult to distinguish between subroutines supplied by the GEM libraries and subroutines written by the user. We therefore offer the following guide:
Generally, any function call in C that starts with the small letter v will be a call to the VDI Library. Such calls would include v_pline(), vst_effects() and so forth. Even though there are a lot of them, only a few are used frequently and they can be easily spotted.
The AES Library is broken into 11 different sub-libraries, each of which are identified by their own five-character prefix on the call name. The 11 different Libraries are:
1. APPL_ Applications Library
2. EVNT_ Event Library
3. MENU_ Menu Library
4. OBJC_ Object Library
5. FORM_ Forms Library
6. GRAF_ Graphics Library
7. SCRP_ Scrap Library
8. FSEL_ File Selector Library
9. WIND_ Window Library
10. RSRC_ Resource Library
11. SHEL_ Shell Library
Using examples from the table above, those of you familiar
with the C listings previously published in Antic will recognize
appl_init() as belonging to the applications library, and
form_alert() as being supplied by the forms library inside
GEM.
In most cases, a routine that begins with a capital letter
followed by lower-case letters will be either a BIOS, BDOS, or XBIOS routine.
But this is not always the case and depends on whether OSBIND.H has been
included in the program.
C conventions require that #defines are written
entirely in capital letters. So when you see an all-caps word, it has probably
been defined elsewhere-either at the top of the code or in an included
file.
Keep in mind that much of this is based on standards set
by Alcyon C in the developer's toolkit. Other language developers may choose
to alter these standards. But we hope not.-Patrick Bass