Classic Computer Magazine Archive ANTIC VOL. 4, NO. 12 / APRIL 1986


Three-dimensional ST landscapes

by PATRICK BASS, Antic ST Program Editor

Don'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...

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.

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.

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).

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.

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.

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.

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.

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.

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.

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.

Leaving is even easier. terminate() automatically closes the virtual workstation, restores the original color palette and exits this application.

Listing 1 3-D FRACTALS

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