3-D Math
Volume Number: 3
Issue Number: 3
Column Tag: Basic School
The 3-D Math Package
By Dave Kelly, MacTutor Editorial Board
Benchmark Wars
Benchmarks are not always what they seem to be. If you recall, in February,
MacTutor printed results from benchmark tests which Microsoft provided to compare
the MS Compiler 1.0 with ZBasic. To make a short story longer some of the
benchmarks do not provide a true realistic view of the capabilities of each product. As
a matter of fact, Zedcor says that "several of the benchmark times Microsoft claims
they are grossly misleading and, in some cases, intentionally deceptive". Well, since
you are such an intelligent audience, you may decide for yourself.
The Microsoft benchmark for math operations basically is as follows:
FOR i% = 1 TO 10000
FOR j% = 1 TO 10
c = 1.2345 + 3.1415
NEXT j%
NEXT i%
It is interesting to note that if the values used in the math are declared
beforehand that the MS times are much slower.
a!=1.2345: b!=3.1415
FOR i% = 1 TO 10000
FOR j% = 1 TO 10
c! = a! (math) b!
NEXT j%
NEXT i%
It appears that in the first benchmark the MS Compiler computes the variable to
be assigned to c! at compile time, thus making this benchmark become a test to see how
long it takes to assign a variable. When run, this explains why all the Microsoft
results for math were 1 second. Since these benchmarks are supposed to check math
operations the results are now invalid. It might be interesting for those of you that
want to use the MS Compiler that your programs will run faster when you use actual
numbers instead of variables in calculations. The math operations in this case were
not really done at runtime. The actual results (for the second benchmark) are given in
the next figure.
MS (b) MS (d) ZBasic
Addition 14 sec 40 sec 52 sec
Subtraction 13 sec 48 sec 64 sec
Multiply 16 sec 249 sec 211 sec
Division 39 sec 457 sec 446 sec
Now that we are comparing like operations, it may be concluded then that ZBasic
(BCD math) math is about the same speed as MS Basic (d) (SANE math) with more
accuracy. MS Basic (b) is fastest but gets wrong answers. Also not previously
mentioned before, if you use ZBasic's LONG INTEGER mode the times are much faster,
but keep in mind that you have to keep track of all decimal points yourself. Since the
MS Compiler doesn't have the LONG INTEGER mode it is impossible to compare it.
Some notes on the integer FOR-NEXT loop benchmark (30,000 iterations). If
the loop is modified to do 900,000 iterations (impossible without LONG INTEGERS)
then ZBasic comes out faster (10 sec). To simulate this we have the loop:
FOR j = 1 TO 30
FOR i = 1 TO 30000
NEXT i
NEXT j
the times are : MS Basic (b) or (d) = 52 sec
ZBasic 3.04 = 7 sec.
Please realize that now the rules have changed. These are times for a loop inside
another loop. But the time difference is at least 5X.
In the Random Disk I/O benchmark for ZBasic there is an extra RECORD
statement in the PUT benchmark. With this line deleted the result was 16 sec. for PUT
and 7 sec GET. That's a 2 to 3 times advantage over the MS Compiler.
In the Sequential PRINT# benchmark a comma delimiter is printed to the file in a
corrected benchmark (with PRINT #1,x$;","). When corrected the Sequential
INPUT# routine does not crash. In fact the crash could have been avoided by setting the
string length error checking flag on in the ZBasic configuration. The new times are
PRINT# =27 sec and INPUT# = 14 sec. Also if the string is 200 characters long
instead of one character, the ZBasic times are 72 sec for PRINT# and 232 sec for
INPUT#; MS Compiler times are 193 sec for print and 921 sec for INPUT#.
In summary, it appears after all this that ZBasic is better than the original
benchmark had indicated. Because of this and the support and determination that
Zedcor has to make ZBasic successful, I have to say that the two products have an equal
rating (MS Compiler had a slight advantage before). But the war is not over. Zedcor is
working on more bug repairs and improvements. I hope that Microsoft will be doing
the same. The competition is good, but I hope that we haven't gone too far off the deep
end with faulty benchmark tests.
Let's step around the controversy surrounding the continuing saga of Basic Wars
to get into some programming. I'm still wondering when the dust will settle. Of
course, the implementation of our programming efforts here will be different
depending on the version of Basic that we use. I'm extremely grateful for the library
routines that have been provided by CLR thus far. There are two new library packages
from CLR: Graph3DLib and VWLib. CLR Graph3DLib allows you to access Apple's 3D
graphics package from within your MS Basic program. CLR VWLib allows you to play
VideoWorks movies from your MS Basic program.
Graph3DLib
When will I ever want to use 3D graphics anyway? Much of the time two
dimensional graphics will be sufficient for what you would like to display (examples: a
map, a x-y graph), however, there are some types of data or some objects that are
difficult to express in 2D. With other types of graphics where you want to show depth
or perspective just as your eye would truly see it. (examples: a orthographic picture,
a painting or illustration, x-y-z graph, animation). As they say: "A picture is worth a
thousand words". Sometimes, the visualization of something can mean the difference
between understanding and confusion.
Fig. 1 Output of our 2-d function demo
Intro to 3D
First we should think about what is going on in the 3D world vs. the 2D world.
When you look at your computer screen (or when graphics are output to your screen
or printer) you see a plane with points or lines on the plane which are arranged so as
to display what we call 2 dimensional graphics. Your Macintosh screen is only capable
of displaying two dimensional objects. When objects are seen in three dimensions, an
object which is close to us seems larger than the object appears when it is far away.
The trick is converting the 3 dimensional display into 2 dimensions so we can display
it on the Macintosh screen. The new CLR library Graph3DLib will assist us in this
conversion.
Way back in algebra I was taught that a straight line is represented
mathematically by the equation y = mx + b. Many of you remember this. The m and b
are constants where m is the slope of the line and b is the value of y when x = 0.
Plotting this equation (where only x and y are involved) requires only 2 dimensions,
one for x and one for y.
In three dimensions, the equation of a line (y = mx+b) would appear only in the
x-y plane. There would be no displacement in the z direction as z is a constant (z=0)
in the equation. It should be noted that the axes shown are pointing in a positive
direction. There are various mathematical equations involving x, y and z as variables
in the same equation. This type of equation can best be visualized by building a 3D
model of the equation.
We have all seen 3D models of one sort or another. Remember the 3D models of
molecules used in physics and chemistry to help the student visualize the subject being
studied. If you take a 3D model in your hand and look at it you will see that the various
coordinates of the model will look larger or small to the human eye depending on the
angle which you are viewing the model (i.e. parts in the back are further away and
therefore appear to be smaller to the eye). Now for just a moment, pretend you are
looking at one of these models through a glass window. If the model is smaller than the
window, you can see the entire object. If the model is bigger than the window, you can
only see the part of the object that is in line with your eye and the window.
Note that it also depends on where you are standing (at what angle to the right or
left of the window) and where the model is located. The size of the object makes a
difference too. If the model is close to the window and very large you may not be able to
see the entire model (kind of like looking straight into the side of a wall in Maze
Wars).
I found that using CLR Graph3DLib was both easy and hard. The concepts are easy
to execute but the hard part is finding just the right angle to view the object. Of course
that won't be a problem is you are going to rotate the object anyway. First it is
important to understand how the coordinate axes are organized. The coordinate axes
are not explained in the CLR Graph3DLib manual. In order to be consistent with the
Macintosh screen coordinates the axes are arranged differently. The x axis points to
the right, y axis points down (toward higher screen coordinate numbers) and because
the y axis is pointing down the z axis must point into the Macintosh screen (z is
perpendicular to x and y axes and must conform to the "right hand rule").
Making your own 3D!
The graph3D statments use a variable type called fixed point (a static variable).
Like single precision numbers, fixed point numbers are four bytes long. But, Basic
does not have the fixed variable type. Therefore, Graph3DLib uses single precision
numbers to store the fixed point numbers. Graph3DLib provides conversion
statements to convert to and from single precision and fixed point. The manual
explains the conversion statements sufficiently. The conversion of numbers is the
most cumbersome of the entire 3D process. Beware! If you forget to convert a
number, the routines won't work properly and you probably think that it is the
routines fault. Beware of pilot error! You may want to initialize all the variables you
will use at the beginning of the program and use them as needed in the 3D statements.
For example you may want to set the variable zero! to '0' using Fs2Fix 0!,zero! at the
beginning of your program so that the variable zero! will already be converted when
needed. All 3D statements use the fixed point variables so remember to convert to
fixed point first.
To use Graph3DLib you first open up a 3-dimensional drawing port with the
OpenPort3D statement (note there is a typo in the manual on page 7 Open3DPort
should be OpenPort3D). You may specify more than one port if you want to use
multiple windows for your output.
The next step is to specify a viewport. A viewport is like the viewing window a
mentioned above; a viewport identifies what coordinates will be able to be seen in the
3D drawing port. A viewport is identified in two-dimensional screen coordinates. You
indicate the screen rectangle in which the drawing will be made.
Next our 3 dimensional coordinate system will be mapped to the viewport
rectangle by using the LookAt statment. LookAt tells the viewport what coordinates we
will 'look at' through the viewport. This can be a bit confusing unless you think of the
viewport as an x-y plane and lookat is defining the 3D coordinates of the plane. The
following statements from my function demo program will set the viewport coordinates
from -10 to 10 in the x direction (x1=-10, x2=10) and -10 to 10 in the y direction
(y1=-10 and y2=10).
Fs2Fix -10!,x1!
Fs2Fix -10!,y1!
Fs2Fix 10!,x2!
Fs2Fix 10!,y2!
LookAt x1!,y1!,x2!,y2!
It is recommended that the center of the viewport be the origin of the coordinate
system although it is not a requirement. If you try to rotate an object when the origin
is on the edge of the viewport you won't be able to see the object when it rotates off the
edge of the viewport. If the viewport is not a perfect square then the object will be
drawn somewhat distorted. If you want it that way then there is nothing wrong with
doing it that way. Notice that there is no mention made of the z axis when defining the
what we will LookAt when we see the viewport. Since we are looking (physically) at a
two dimensional screen, Graph3DLib references the x and y coordinates (3D) to the x
and y on the Macintosh screen (2D). When the z axis is shown it will have the proper
dimensions in relation to the x and y coordinates. In other words, if the z axis is
rotated so that it points in the vertical (2D y direction) then it will have dimensions of
the vertical direction. You don't need to worry about it because the 3D package takes
care of that for you.
The only thing left to define before you can draw is the viewangle. The viewangle
is defined as the horizontal angle (in degrees) subtended by the viewing pyramid. The
viewing pyramid is defined by the pyramid formed by drawing lines from the corners
of the viewport to the eye. To visualize the viewangle, image you are standing very
close to your window. If you are standing very close to the edge of your window you
must turn your head a wide angle to see the edges of the window. If your eye was right
on the surface of the window then you would have to turn by 90 degrees to see the edge.
This also gives you a very wide viewport. If you are standing much further away from
the window, the angle which you must turn your head in order to see the edges is much
smaller. According to the Graph3dLib manual, 25 degrees is the normal perspective of
the human eye. 10 degrees gives the effect of a telephoto lens whereas a large
viewangle of 80 degrees give the effect of a wide angle lens.
You may specify the Pitch(x Angle), Yaw (y Angle) or Roll (z Angle) using the
Pitch, Yaw and Roll statements respectively. By changing this angle before drawing
the object will make the object appear to rotate in space. The Scale statement lets you
shrink or expand the drawing on each axis in the proportion factor specified. You may
move the pen to any 2D or 3D coordinate (using MoveTo2D or MoveTo3D) and then
draw lines from the pen location to another point (LineTo2D or LineTo3D). You may
also draw from the current pen location to a point dx, dy or dz units away. (dx is a
displacement in the x direction). There are a few math operation statements provided
for doing math operations on fixed point variables, but they won't be faster than the
operations in Basic because of the time Basic takes to call the library routines.
The 3D function demo program sets up two viewports in the same window and
calculates and displays the following 3D plots:
y = sin(x)