TweetFollow Us on Twitter

Understanding Graf3D
Volume Number:3
Issue Number:3
Column Tag:Pascal Procedures

Understanding Graf3D

By Scott Berfield, Mindscape, Inc.

Almost everybody has heard of GRAF3D for the Mac, but aside from Boxes, Sinegrid, and BoxSphere, there is little evidence of its use. This is probably because the only documentation in general distribution is in the form of the source code for the above programs, and in the interface files for the various compilers. As it turns out, there is a pre-release tech note from Apple which does a good job of explaining a lot of how GRAF3D works, if you're a registered developer.

In this article, I will present a brief explanation of some basic concepts of 3D math, an overview of how the Mac's Graf3D routines deal with those concepts, the data types and routines that make up Graf3d, and finally, a sample program that tries to clarify the difference between two of what I consider Graf3D's more confusing concepts.

The program included with this article was developed in LightSpeed Pascal and then converted to Borland Turbo Pascal, so the article also presents a small comparison between the two language implementations.

3D Concepts

There are several basic concepts of 3D graphics with which you will need to be familiar if you are going to be able to use Graf3D to its fullest extent. Among these are the coordinate system conventions and the various transformations and their meaning.

The Coordinate System

Three dimensional graphics are generally dealt with using a right-handed cartesian coordinate system (see fig. 1)

The three axes are labeled X, Y, and Z. Thus, each point in three dimensional space can be represented by three values. When displaying such a three-dimensional space on a two-dimensional surface (a Mac's screen, for instance) some basic trigonometric calculations are used to map the points into their proper positions. The basics of this were discovered by artists during the renaissance.

Fig. 1 3-D Coordinate System

Transformations

There are three transformations we will want to use to manipulate three-dimensional images. These are rotation, scaling, and transformation.

Rotation can be about any of the three axes. Rotation about the X axis is called Pitch. Rotation about the Y axis is called Yaw, and rotation about the Z axis is called Roll. (These terms come primarily from the aviation world.) Rotations are performed relative to the coordinate system's origin. Thus, if you wish to rotate an object around its own center, you need to move the object's center to the origin (mathematically, at least), rotate it, and then return to the prior coordinates.

Scaling is a very useful transformation. It serves to move a point toward or away from the origin. This can serve to change the apparent size of the object on screen.

Translation moves a three-dimensional point some distance in any direction.

Graf3D Concepts

The coordinate space of Graf3D is a natural extension of the Quickdraw system into a third dimension. Just as Quickdraw can address a plane ranging from -32767 to +32767 in both dimensions, Graf3D addresses a cube ranging from -32767 to +32767 along all three (X,Y, and Z) axes. (See fig. 2)

Note that the Y axis increases downward as opposed to the normal Mac coordinate system.

This leads to the basic data structure of Graf3D, the Point3D. The definition of a Point3D is:

 Type Point3D  = Record
 X :  Fixed;
 Y :  Fixed;
 Z :  Fixed;
 end;

The use of fixed-point numbers may be unfamiliar to many. A fixed-point number is a special way of handling numbers from -32767 to +32767 with up to five decimals of precision. The numbers are represented using longints. Thus the massive numbers of floating point calculations needed for 3D math can be sped up tremendously by using only integers.

If you need to translate from floating point to fixed numbers, you can either multiply the value by 65536, or you can use the routine FixRatio from the fixed-point math package to dived your value by one:

 fixedvalue:=FixRatio(FPvalue,1));

Fig. 2 GrafPort Orientation

The Transformation Matrix

In 3D graphics, it is common to combine all the math involved in rotating, scaling, and translating a point into one 4x4 matrix. The mathematical reasons behind this are beyond the scope of this article, but they are well documented in the books listed at the end of the article.

Graf3D defines a data type XfMatrix to handle these manipulations. This matrix can be post-multiplied by a Point3D to yield a new point which is the product of the three transformations. The XfMatrix is defined as:

Type XfMatrix = Array[0..3,0..3] of Fixed;

The array holds the results of all operations performed with a specific Graf3D coordinate system and can be applied to any and all points in the system.

The Port3D

Just as Quickdraw defines a grafport, Graf3D defines the Port3D. A Port3D is a complete graphics environment. You can have many separate Port3D's open at one time, each with its own coordinate system, pen location, transformation matrix, and screen mapping. (See fig. 3 on the next page.)

A Port3D is defined as a dynamic data structure as follows:

 Type Port3DPtr = ^Port3D
 Port3D = Record
 GrPort:GrafPtr;
 viewrect:Rect;
 xLeft, xRight:  Fixed;
 yTop, yBottom:  Fixed;
 pen, penPrime:  Point3D;
 eye:   Point3D;
 hSize, vSize:   Fixed;
 hCenter, vCenter: Fixed;
 xCotan, yCotan: Fixed;
 ident: Boolean;
 xForm: XfMatrix;
 end;

All operations on Port3D's refer to the port through Port3DPtr's.

According to Apple, although all the fields of a Port3D can be accessed normally, you shouldn't store any new values into them directly. Graf3D has routines for altering all the fields which will produce no harmful side effects.

The fields of the Port3D are as follows:

GrPort

This is the corresponding grafport which is used for drawing when using the Port3D. The default is the current port.

Viewrect

The viewrect field defines a subset of the grafport's portRect for use by the Port3D. All drawing will happen in this rectangle. The bounds of this rectangle are initially set to GrPort^.portBits.bounds.

XLeft, XRight, YTop, YBottom

These fields define the coordinate system, in fixed-point numbers, of the current Port3D. I call the volume of space defined by these numbers (using the LookAt procedure) the “Image Space.” These numbers do not have to match the the viewport values, and in fact will be scaled to fit the viewrect.

Pen

The pen location in 3D space.

PenPrime

PenPrime is the location of the pen in 3D space after multiplying by the transformation matrix.

Eye

This is the location of the viewer's eye in threespace. It is where you would be standing if you were a part of the coordinate system.

HSize, VSize

HSize is 1/2 the width of the viewrect. VSize is -1/2 the height of the viewrect. Both values are stored as fixed-point numbers.

HCenter, VCenter

The centers, in fixed-point, of the X and Y axes of the viewrect.

XCotan, YCotan

Viewing cotangents used to transform 3D coordinates into 2D screen coordinates.

Ident

A flag which indicates whether the matrix is currently at identity (its original state).

XForm

The transformation matrix from the current Port3D.

The Pen

The pen and penPrime fields of a Port3D deal with the 3D graphics pen. Each port has only one graphics pen, which is used for calculating screen coordinates. The 3D pen has only one characteristic: location. The Port3D pen and the grafPort pen are two different items, one with a 3D location, and one with a screen location. The grafPort pen does all the drawing for the 3D pen. The grafPort pen will not be changed by any Port3D operations.

Graf3D Routines

Initialization and Control

Procedure InitGraf3D(globalPtr : Ptr);

This is Graf3D's functional equivalent to quickdraw's InitGraf. It initializes the current Port3Dptr to globalptr. In Pascal, you should always pass @thePort3D. You will normally want to call this procedure immediately upon initializing quickdraw.

initGgraf(@thePort);

initGraf3D(@thePort3D);

Procedure OpenPort3D(port:Port3DPtr);

Initializes all fields of a port3D to the defaults and sets that port as the current one.

Procedure SetPort3D(Port: Port3DPtr);

Makes port the current Port3D and calls SetPort for that Port3D's associated grafPort.

Procedure GetPort3D(Port: Port3DPtr);

Returns a pointer to the current Port3D. GetPort3D and SetPort3D can be used to change between multiple Port3D's.

Controlling the Pen

Procedure MoveTo2D(x,y:fixed);

Moves the pen to the coordinates x,y while remaining in the same z plane.

Procedure MoveTo3D(x,y,z:fixed);

Moves the pen to the coordinates x,y,z.

Procedure Move2D(dx,dy:fixed);

Moves the pen to x+dx, y+dy while remaining in the same z plane.

Procedure Move3D(dx,dy,dz:fixed);

Moves the pen to x+dx, y+dy, z+dz.

Procedure LineTo2D(x,y:fixed);

Draws a line to the coordinates x,y while remaining in the same z plane.

Procedure LineTo3D(x,y,z:fixed);

Draws a line to the coordinates x,y,z.

Procedure Line2D((dx,dy:fixed);

Draws a line to x+dx, y+dy while remaining in the same z plane.

Procedure Line3D(dx,dy,dz:fixed);

Draws a line to x+dx, y+dy, z+dz.

Points

Function Clip3D(src1,src2:Point3D; VAR dst1,dst2:Point):boolean;

Determines if a line segment is within the viewing pyramid. If no part of the line from src1 to src2 falls within the viewing pyramid, then Clip3D returns false. Upon return, dst1 and dst2 will contain src1 and src2 as screen coordinates. Note that the transformation matrix has no effect on points passed to this function. If you want to use Clip3D on transformed points, transform them prior to calling Clip3D.

Procedure SetPt2D(VAR pt2D:Point2D; x,y:Fixed);

Assigns two fixed-point numbers to a Point2D.

Procedure SetPt3D(VAR pt3D:Point3D;x,y,z:Fixed);

Assigns three fixed-point numbers to a Point3D.

Controlling the “Camera”

Procedure ViewPort(r:rect);

This routine specifies where to put the image in the grafPort. Viewport takes a quickdraw rectangle as its argument.

Procedure LookAt(left,top,right,bottom:fixed);

This routine defines the portion of Graf3D space to map into the rectangle set with ViewPort. You can call LookAt at any time, but it must always be followed by a call to ViewAngle. LookAt sets the xLeft, yTop, xRight, and Ybottom fields of the Port3D. It also sets the eye position and the hSize and vSize fields as well as hCenter and vCenter.

Procedure ViewAngle(angle:fixed);

This routine controls the amount of perspective by setting the horizontal angle subtended by the viewing pyramid. It is the same function provided by changing to a wide-angle lens on a camera. Some common settings are 0° (no perspective at all), 10° (a telephoto lens), 25° (human eye), and 80° (a wide-angle lens). This routine sets the xCotan and yCotan fields.

Fig 3.

Transformations

Procedure Identity;

Resets the transformation matrix to an identity matrix. The ident field of the Port3D is set to true.

Procedure Scale(xFactor,yFactor,zFactor:fixed);

Scale modifies the matrix to shrink or expand by xFactor, yFactor, and zFactor. For example

Scale(3*65536,3*65536,3*65536);

will make everything three times as big when you draw.

Procedure Translate(dx,dy,dz:Fixed);

Modifies the matrix to displace all points by dx,dy,dz.

Procedure Pitch(xangle:fixed);

Modifies the matrix to rotate xAngle degrees about the x axis. A positive angle rotates clockwise when looking at the origin from positive x.

Procedure Yaw(yangle:fixed);

Modifies the matrix to rotate yAngle degrees about the y axis. A positive angle rotates clockwise when looking at the origin from positive y.

Procedure Roll(zangle:fixed);

Modifies the matrix to rotate zAngle degrees about the z axis. A positive angle rotates clockwise when looking at the origin from positive z.

Procedure Skew(zangle:fixed);

Skew modifies the matrix to skew zAngle degrees about the z axis. It only changes the x coordinates. This is the same effect used by quickdraw to italicize letters. In fact, you can obtain an approximation of a quickdraw italic with a zAngle of 15°. A positive angle rotates clockwise when looking at the origin from positive z.

Procedure Transform(src:Point3D; VAR dst:Point3D);

Transform applies the transform matrix to src and puts the result into dst. If the matrix is identity then dst will be the same as src. There is a bug in early versions of Graf3D which causes problems if you call transform with the same Point3D as src and dst. If you run into trouble, simply use a second Point3D as the destination and it should work fine.

Fig 4.

The Program

The example program is intended to show the basics of working with Graf3D (see fig. 4). It sets up two windows, one of which contains a Port3D. A grid and a tetrahedron are drawn in the window. By manipulating the three scroll bars, you can rotate the images about any of the three axes. The Rotate What? menu allows you to change between rotating the tetrahedron or rotating all of the three dimensional space. When you are rotating the object, the transformation matrix is applied to each point making up the tetrahedron. The matrix is then reset to identity and the screen is redrawn. When you are rotating space, the matrix is changed (using pitch, yaw, and roll) and the screen is redrawn without resetting the matrix. In the first instance, the points making up the tetrahedron are actually changed. In the second case, no coordinates are changed. This is a subtle distinction that eludes many people at first.

Pascals

Graf3D Demo was originally written in Lightspeed Pascal, which is the version printed here. Since then, I have received a copy of Borland's Turbo Pascal and I decided to translate the prograam into it as a way of comparing the two language implementations; both are provided on the source code disk for this issue. The translation process required about ten minutes, and the level of compatibility between the two is quite good.

Turbo Pascal

To enter and compile the program under Turbo Pascal, you simply need to enter the program as listed, enter and compile the resource file (using RMaker), and compile to disk. Turbo makes it very easy to prodce an application and allows you to link the resource file, set the bundle bit, and set the type and creator of your program with compiler directives. The compilation process is amazingly fast. Running on a Mac+ with a 20 megabyte DataFrame SCSI hard disk, it takes approximately six seconds to compile and link. It takes about two seconds less to compile to memory, whihc allows you to run the program from the Turbo environment. The application size ends up at a little over 18K.

Lightspeed Pascal

To enter and run the program, you will need to use a text editor to enter the resource text, and then run it through RMaker. Enter the program in the Lightspeed editor. Only fixmath and Graf3d need be declared as all the other standard include files are provided by default. Setup the project as shown in fig. 5.

Be sure to set ThreeD.Rsrc as the resource file under the Run Options. Build and save the project as an application. One missing piece in Lightspeed is that the creator of a new application is not set correctly for you. In this case you will need to use Fedit or SetFile or some other utility to set the creator to SB3D. The compile time for Lightspeed is also quit fast. The first compile, from a compressed project (admittedly, not a standard way to work, other than the very first time you compile something), took approximately 55 seconds. The speed of Lightspeed shows up when a minor change is made. Changing one line and recompiling took only 14 seconds. The final application size was 11.5K.

Fig. 5 LSP Link List

TML Pascal

I don't have TML Pascal, so I did no comparison, but since 3DDemo does little that is non-standard, it should run as is under TML. You should place the following at the beginning of the program:

{$T APPL !@#$  } 
{$B+    }
{$L ThreeD.Rsrc  }

Uses MacIntf, FixMath, Graf3D;

Comparisons

Both Turbo Pascal and Lightspeed Pascal offer powerful integrated programming environments for Macintosh development. Both offer fast compilation (although Turbo is faster), separate compilation of units, excellent documentation and low price. I would be hard pressed to recommend one over the other. I suspect that more experienced users will be more comfortable with Turbo with its speedy editor and direct .REL file compatibility, but the interactive debugging and syntax-checking editor with automatic formatting will appeal to beginners and dabblers (like me). You would not go wrong to purchase either of these products, and at the price, it would almost be worth buying Turbo just for the manual.

{Graf3D Demo}
{}
{by Scott Berfield }
{for MacTutor magazine  }
{}

PROGRAM ThreeDDemo;

{$I-}

USES
 Graf3D, FixMath;
{MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, fixmath, graf3d}

CONST
 object = 1;{flag indicating which we are rotating}
 world = 2;
 hellfreezesover = false;{A boolean for eventloop}
 VIEWwindowID = 32000;  {ID of our drawing window}
 INPUTWINDOWID = 32001; {ID of our control window}
 APPLEM = 0;{Menu indices}
 FILEM = 1;
 EDITM = 2;
 SWITCHM = 3;
 appleID = 128;  {Menu resource IDs}
 fileID = 129;
 editID = 130;
 SWITCHID = 131;
 lastmenu = 3;   {How many menus}
 aboutID = 128;  {About alert resource ID}
 UndoItem = 1;   {Menu item codes}
 cutitem = 3;
 copyitem = 4;
 pasteitem = 5;
 clearitem = 6;
 XScrollID = 128;{Scroll bar resource IDs}
 YScrollID = 129;
 ZScrollID = 130;
 XMIN = -200; {Limits object space (set with LOOKAT)}
 YMIN = -200;
 ZMIN = -200;
 XMAX = 200;
 YMAX = 200;
 ZMAX = 200;

VAR
 fromupdate :  boolean;
 whichcontrol : controlhandle;
 xscroll, yscroll, zscroll : controlhandle;
 myMenus : ARRAY[0..lastmenu] OF menuhandle;
 INPUTWINDOW :   windowptr;{pointers to our windows}
 VIEWWindow :  windowPtr;
 Wrecord :  windowrecord;{Storage for our windows}
 Wrecord2 : windowrecord;
 gport3d :  port3d;{Our 3D grafport}
 XROT, YROT, ZROT : integer; {current scrollbar settings}
 OXROT, OYROT, OZROT : integer; {old scroll bar settings}
 which :  integer; {Object or world rotation?}
 XSpacerot, YSpaceRot, ZSpaceRot : integer; 
 XObjRot, YObjRot, ZObjRot : integer; 
 Dtetra, tetra : ARRAY[1..4] OF point3d; 
 delta :  integer; {inc or dec the scroll bars}

{------ crash --------}
PROCEDURE crash;
BEGIN
 exittoshell;
END;

{ --------- init ---------}
PROCEDURE init;  {set everything up}
BEGIN
 initgraf(@thePort);
 initgrf3d(@theport3d);{required graf3D equivalent}
 InitFonts;
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs(@crash);
 InitCursor;
 FlushEvents(everyEvent, 0);

 XROT := 0; {Set initial values}
 YROT := 0;
 ZROT := 0;
 OXROT := 1;
 OYROT := 1;
 OZROT := 1;
 XSpaceRot := 0;
 YSpaceRot := 0;
 ZSpaceRot := 0;
 XObjRot := 0;
 YObjRot := 0;
 ZObjRot := 0;
 which := object;{ default is to rotate the object}
 setpt3d(tetra[1], 0, -6553600, 0); {tetra vertices}
 setpt3d(tetra[2], -1638400, -3276800, 0);
 setpt3d(tetra[3], 1638400, -3276800, 0);
 setpt3d(tetra[4], 0, -4915200, 1638400);
 DTetra := tetra;
END;    {init}

{----------- drawvalues ----------}
PROCEDURE drawvalues;{Draw scroll bar settings as text}
VAR
 text1, text2, text3 : str255;
 trect : rect;
BEGIN
 IF (OXROT <> XROT) OR (OYROT <> YROT) OR (OZROT <> ZROT) OR (fromupdate) 
THEN
 BEGIN  {we only draw them if only if something has changed}
 setrect(trect, 0, 45, 512, 65);
 setport(inputwindow);
 eraserect(trect);
 penpat(black);
 textfont(0);
 textsize(12);
 numtostring(xrot, text1);
 numtostring(yrot, text2);
 numtostring(zrot, text3);
 moveto(10, 55);
 drawstring(text1);
 moveto(175, 55);
 drawstring(text2);
 moveto(340, 55);
 drawstring(text3);
 OXROT := XROT;
 OYROT := YROT;
 OZROT := ZROT;
 END;
END;    {drawvalues}

{---------- drawlabels -------------}
PROCEDURE drawlabels; {label the scroll bars}
VAR
 labelrect : rect;
BEGIN
 setrect(labelrect, 0, 0, 512, 24);
 setport(inputwindow);
 eraserect(labelrect);
 textfont(0);    {Chicago font}
 textsize(12);   {12 point}
 penpat(black);  {make sure we can see it}
 CASE which OF {which labels do we draw?}
 object : 
 BEGIN
 moveto(10, 19);
 drawstring('X Rotation');
 moveto(175, 19);
 drawstring('Y Rotation');
 moveto(340, 19);
 drawstring('Z Rotation');
 END;
 world : 
 BEGIN
 moveto(10, 19);
 drawstring('Pitch');
 moveto(175, 19);
 drawstring('Yaw');
 moveto(340, 19);
 drawstring('Roll');
 END;
 END;
END;    {drawlabels}

{----------- drawgrid -----------}
PROCEDURE drawgrid;{Draw the “space grid”}
VAR
 i : integer;
BEGIN   {all coord in fixed point -- X by 65536}
 pitch(XSPACEROT * 65536);{rotate space by x value...}
 YAW(YSPACEROT * 65536);{rotate space by y value...}
 ROLL(ZSPACEROT * 65536);{rotate space by z value...}
 {now draw the grid in the newly rotated space}
 moveto3d(-6553600, 0, -6553600); {-100,0,-100}
 lineto3d(-6553600, 0, 6553600);{etc...}
 lineto3d(6553600, 0, 6553600);
 lineto3d(6553600, 0, -6553600);
 lineto3d(-6553600, 0, -6553600);
 moveto3d(0, 0, -6553600);
 lineto3d(0, 0, 6553600);
 moveto3d(-6553600, 0, 0);
 lineto3d(6553600, 0, 0);
END;    {drawgrid}

{-------- drawtetra ---------}
PROCEDURE drawtetra; {draw our object}
BEGIN
 {draw using DTetra which}
 {holds transformed coordinates}
 {Note that point3D's are already in}
 {fixed - point }
 moveto3d(Dtetra[1].x, Dtetra[1].y, Dtetra[1].z);
 lineto3d(Dtetra[2].x, Dtetra[2].y, Dtetra[2].z);
 lineto3d(Dtetra[3].x, Dtetra[3].y, Dtetra[3].z);
 lineto3d(Dtetra[1].x, Dtetra[1].y, Dtetra[1].z);
 lineto3d(Dtetra[4].x, Dtetra[4].y, Dtetra[4].z);
 moveto3d(Dtetra[2].x, Dtetra[2].y, Dtetra[2].z);
 lineto3d(Dtetra[4].x, Dtetra[4].y, Dtetra[4].z);
 lineto3d(Dtetra[3].x, Dtetra[3].y, Dtetra[3].z);
END;    {drawtetra}

{----------- drawview -----------}
PROCEDURE drawview;
{draw the contents of the view window using }
{current transform matrix}
BEGIN
 setport(viewwindow);{where we need to be to draw}
 penpat(black);  {eraser color}
 paintrect(theport^.portrect); {erase the screen}
 penpat(white);  {line color}
 drawgrid;{draw the plane -- space rotated on return}
 drawtetra; {draw the object}
 identity;{reset the transform matrix }
 setport(inputwindow);  {Back to the control window!}
END;    {Drawview}

{----------- drawinput ---------}
PROCEDURE drawinput;  {Draw the control window}
BEGIN
 setport(inputwindow);
 drawvalues;
 drawlabels;
 Drawcontrols(inputwindow);
END;    {drawinput}

{----------- TRANS -----------}
PROCEDURE TRANS; {transform on current scroll bar settings}
VAR
 i : integer;
BEGIN
 PITCH(XROT * 65536);{x rotation}
 YAW(YROT * 65536);{y rotation}
 ROLL(ZROT * 65536); {z rotation}
 IF which = object THEN {if rotating the object...}
 FOR i := 1 TO 4 DO
 BEGIN
 transform(tetra[i], Dtetra[i]); 
 {apply matrix to each point in virgin tetra and}
 END; {store it in the drawing tetra}
 identity;{reset the matrix then draw window}
 drawview;{so drawview proc controls global viewpoint}
END;    {TRANS}

{------------- updateRots ------------}
PROCEDURE updateRots;{update values from scroll bars}
BEGIN
 XROT := GETCTLVALUE(XSCROLL); {get the current values}
 YROT := GETCTLVALUE(YSCROLL);
 ZROT := GETCTLVALUE(ZSCROLL);
 DrawValues;{draw them}
 CASE which OF
 object : {which values need updating?}
 BEGIN
 XObjRot := XROT;
 YOBJROT := YROT;
 ZOBJROT := ZROT;
 TRANS;
 END;
 world : 
 BEGIN
 XspaceRot := XROT;
 YspaceROT := YROT;
 ZspaceROT := ZROT;
 drawview;
 END;
 END;
END;    {updaterots}

{--------- dowindows ------------}
PROCEDURE dowindows;  {set up windows and 3D stuff}
VAR
 Vrect : rect;
BEGIN
 InputWindow := GetNewWindow(INPUTWINDOWID, @Wrecord2, POINTER(-1));
 xScroll := GetNewControl(XScrollID, InputWindow);
 yScroll := GetNewControl(YScrollID, InputWindow);
 zScroll := GetNewControl(ZScrollID, InputWindow);
 ViewWindow := GetNewWindow(VIEWWINDOWID, @Wrecord, POINTER(-1));
 {set up a 3D grafport (uses reg. grafport for drawing}
 Open3DPort(@gPort3D);
 setport3d(@gPort3d);
 viewport(viewwindow^.portbits.bounds);
 {set the drawing area to the full window}
 lookat(XMIN * 65536, YMIN * 65536, XMAX * 65536, YMAX * 65536); {set 
the image space}
 {set the angle  25° = human field of view. } 
 {0°=no persp.  80°=fish-eye lens}
 viewangle(1638400); {25° * 65536}
END;    {doWindows}

{---------- domenus ----------}
PROCEDURE domenus; {set up menus}
VAR
 i : integer;
BEGIN
 myMenus[appleM] := GetMenu(appleID);
 AddResMenu(myMenus[appleM], 'DRVR');
 myMenus[FileM] := GetMenu(fileID);
 myMenus[EditM] := GetMenu(editID);
 mymenus[SwitchM] := GetMenu(switchID);
 FOR i := appleM TO lastmenu DO
 insertMenu(myMenus[i], 0);
 SetItemIcon(myMenus[0], 1, 195);
 DrawMenuBar;
END;    {doMenus}

{---------- aboutme -----------}
PROCEDURE aboutme; {do about box}
VAR
 foo : integer;
BEGIN
 foo := alert(aboutID, NIL);
END;    {aboutme}

{------------ applfile ---------}
PROCEDURE applfile (theItem : integer); {handle file menu}
BEGIN
 exittoshell;  {they chose Quit}
END;    {applfile}

{---------- DoCommand -----------}
PROCEDURE DoCommand (theCommand : LongInt); {menu choices}
VAR
 theMenu, theItem : integer;
 name : str255;
 RefNum : integer;
 dum :  integer;
 blah : boolean;
BEGIN
 theMenu := hiWord(theCommand);
 theItem := loWord(theCommand);
 CASE theMenu OF
 AppleID : 
 BEGIN
 IF (theItem = 1) THEN
 AboutMe{about box}
 ELSE
 BEGIN
 getItem(myMenus[appleM], theItem, name); 
 dum := OpenDeskAcc(Name)
 END;   {else}
 END;   {appleM}

 FileID : 
 ApplFile(theItem);

 EditID : 
 blah := systemEdit(theItem - 1);

 SwitchID : 
 BEGIN
 CASE which OF {adjust menuand controls}
 object : {switch to rotating space}
 BEGIN
 which := world;
 setitem(mymenus[switchM], 1, 'Rotate Object');
 setport(inputwindow);
 drawlabels;
 setctlvalue(xscroll, xspacerot);
 {pick up settings from space values}
 setctlvalue(yscroll, yspacerot);
 setctlvalue(zscroll, zspacerot);
 xrot := xspacerot; {update our holders}
 yrot := yspacerot;
 zrot := zspacerot;
 drawvalues; {values for scroll bars}
 END;   {object case}
 world : 
 BEGIN  {switch from world to object}
 which := object;
 setitem(mymenus[switchM], 1, 'Rotate Space');
 setport(inputwindow);
 drawlabels;
 setctlvalue(xscroll, xobjrot);
 setctlvalue(yscroll, yobjrot);
 setctlvalue(zscroll, zobjrot);
 xrot := xobjrot;
 yrot := yobjrot;
 zrot := zobjrot;
 drawvalues;
 END;   {world case}
 OTHERWISE
 END;   {case which of}
 END; {switch menu case}
 OTHERWISE
 END;   {case theMenu}
 hiliteMenu(0);  {turn off menu hilight}
END; {doCommand}

{--------- chagearrow ----------}
PROCEDURE changearrow;
BEGIN
 setctlvalue(whichcontrol, getctlvalue(whichcontrol) + delta);
 updaterots;
END;

{--------- ApplMouseDown -----------}
PROCEDURE ApplMouseDown (theWindow : windowPtr;
 MousePoint : point); 
VAR
 partcode : integer;
 dummy, temp : integer;
BEGIN
 IF theWindow = inputwindow THEN
 BEGIN
 setport(inputwindow);
 globaltolocal(mousepoint);
 partcode := findcontrol(mousepoint, theWindow, whichcontrol);
 CASE partcode OF
 inupbutton : 
 BEGIN
 delta := -1;
 dummy := trackcontrol(whichcontrol, mousepoint, @changearrow);
 END;
 indownbutton : 
 BEGIN
 delta := 1;
 dummy := trackcontrol(whichcontrol, mousepoint, @changearrow);
 END;
 inpageup : 
 BEGIN
 delta := -10;
 dummy := trackcontrol(whichcontrol, mousepoint, @changearrow);
 END;
 inpagedown : 
 BEGIN
 delta := 10;
 dummy := trackcontrol(whichcontrol, mousepoint, @changearrow);
 END;
 inthumb : 
 BEGIN
 temp := getctlvalue(whichcontrol);
 dummy := trackcontrol(whichcontrol, mousepoint, NIL);
 IF getctlvalue(whichcontrol) <> temp THEN
 updaterots;
 END;
 OTHERWISE
 END; {case partcode of}
 END;   {if}
END;    {applMouseDown}

{---------- DoKeyDown -----------}
PROCEDURE DoKeyDown (Event : EventRecord);   {they pressed a key}
VAR
 CharCode : char;
BEGIN
 CharCode := chr(Event.message MOD 256);
 IF BitAnd(Event.modifiers, CmdKey) = CmdKey THEN 
 {must of been a command key, right?}
 DoCommand(MenuKey(CharCode)) {pass it to menu handler}
END;  { DoKeyDown }

{----------- EventLoop ----------}
PROCEDURE EventLoop; {the meat of the Mac application -- process those 
events!}
VAR
 saveport : GrafPtr;
 GotEvent : boolean;
 NewEvent : EventRecord;
 WhichWindow : WindowPtr;
 Key : char;
 KeyCommand : LongInt;
BEGIN
flushevents(everyevent, 0);
REPEAT
 GotEvent := GetNextEvent(EveryEvent, NewEvent);
 IF GotEvent THEN
 BEGIN
 CASE NewEvent.What OF
 mouseDown : 
 BEGIN
 CASE FindWindow(NewEvent.where, whichWindow) OF
 inMenuBar : 
 doCommand(menuSelect(NewEvent.where));
 inSysWindow : 
 SystemClick(newEvent, whichWindow);
 inContent : 
 applMouseDown(whichWindow, NewEvent.where); 
 inGoAway : 
 IF TrackGoAway(whichWindow, NewEvent.Where) THEN
 BEGIN
 ExitToShell;
 END;
 inDrag : 
 IF whichWindow <> FrontWindow THEN
 SelectWindow(whichWindow)
 ELSE
 applMouseDown(whichWindow, NewEvent.where);
 OTHERWISE
 END; {case FWReturnCode}
 END; {case mouseDown}

 KeyDown : 
 BEGIN
 doKeyDown(newEvent);
 END; {Case KeyDown}

 UpdateEvt : 
 BEGIN
 getport(saveport); {store current grafport}
 setport(viewwindow); {set it to viewwindow}
 beginupdate(viewwindow);
 drawview;
 endupdate(viewwindow);
 setport(inputwindow); {now do control window}
 beginupdate(inputwindow);
 fromupdate := true; {draw values if needed}
 drawinput;
 fromupdate := false; {reset the toggle}
 endupdate(inputwindow);
 setport(saveport); {restore the port}
 END;   {updateEvt}
 OTHERWISE
 END;   {NewEvent.What}
 END;   {if}
 systemTask; {handle periodic stuff}
UNTIL HellFreezesOver;  {let it run for a long time}
END; {EventLoop}

{ ---------- Main Program ----------------}
BEGIN
 init;  {Init toolbox stuff and appl variables}
 dowindows; {draw windows and setup 3D grafport}
 domenus; {do menus}
 identity;{reset transformation matrix }
 drawview;{draw contents of view window}
 drawinput; {draw contents ofcontrol window}
 eventloop; {Handle events}
END.    {That's all for now }
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Artlantis Studio 5.1.2.7 - 3D rendering...
Artlantis Studio is a unique and ideal tool for performing very high resolution rendering easily and in real time. The new FastRadiosity engine now lets you compute images in radiosity-even in... Read more
MacUpdate Desktop 6.0.5 - Search and ins...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
BitTorrent Sync 2.0.82 - Sync files secu...
BitTorrent Sync allows you to sync unlimited files between your own devices, or share a folder with friends and family to automatically sync anything. File transfers are encrypted. Your information... Read more
Google Drive 1.20 - File backup and shar...
Google Drive is a place where you can create, share, collaborate, and keep all of your stuff. Whether you're working with a friend on a joint research project, planning a wedding with your fiancé, or... Read more
Simon 4.0.3 - Monitor changes and crashe...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
Vitamin-R 2.23 - Personal productivity t...
Vitamin-R creates the optimal conditions for your brain to work at its best by structuring your work into short bursts of distraction-free, highly focused activity alternating with opportunities for... Read more
iDefrag 5.0.0 - Disk defragmentation and...
iDefrag helps defragment and optimize your disk for improved performance. Features include: Supports HFS and HFS+ (Mac OS Extended). Supports case sensitive and journaled filesystems. Supports... Read more
PCalc 4.2 - Full-featured scientific cal...
PCalc is a full-featured, scriptable scientific calculator with support for hexadecimal, octal, and binary calculations, as well as an RPN mode, programmable functions, and an extensive set of unit... Read more
FileZilla 3.10.2 - Fast and reliable FTP...
FileZilla (ported from Windows) is a fast and reliable FTP client and server with lots of useful features and an intuitive interface. Version 3.10.2: Note: Now requires a 64-bit Intel processor.... Read more
The Hit List 1.1.11 - Advanced reminder...
The Hit List manages the daily chaos of your modern life. It's easy to learn - it's as easy as making lists. And it's powerful enough to let you plan, then forget, then act when the time is right.... Read more

Warner Bros. Interactive Entertainment A...
Warner Bros. has some exciting games coming down the pipe! | Read more »
GDC 2015 – Star Trek Timelines will Prob...
GDC 2015 – Star Trek Timelines will Probably Make Your Inner Trekkie Squeal With Glee Posted by Rob Rich on March 4th, 2015 [ permalink ] Any popular fictional universe has its fair share of fan fiction – where belo | Read more »
Protect Yourself from an Onslaught of Ca...
Surprise Attack Games has announced a Cat-astrophic new physics puzzler called Fort Meow! In the game, a young girl named Nia finds her grandfather’s journal which triggers an all mighty feline attack! Why do the cats want the journal? Who knows,... | Read more »
GDC 2015 – Jelly Reef will be Game Oven’...
GDC 2015 – Jelly Reef will be Game Oven’s Last Hurrah, and it Seems like a Good Note to Go Out on Posted by Rob Rich on March 4th, 2015 [ permalink ] It’s sad knowing that Game Oven ( | Read more »
daWindci Deluxe Review
daWindci Deluxe Review By Campbell Bird on March 4th, 2015 Our Rating: :: BLUSTERY PUZZLESUniversal App - Designed for iPhone and iPad This updated puzzle game offers some creative gameplay and new mechanics, but still suffers from... | Read more »
Dungeon Hunter 5 Coming on March 12
Gameloft has excitedly announced that Dungeon Hunter 5 is on its way! Once again, you will adventure across the land of Valenthia exploring dungeons and fighting monsters. The game will have a new asynchronous multiplayer mode called Strongholds... | Read more »
GDC 2015 – The Sandbox 2 is Coming, and...
GDC 2015 – The Sandbox 2 is Coming, and Now it has Textures! | Read more »
Warner Bros. Interactive Announces Mort...
Mortal Kombat X, by Warner Bros. and NetherRealm Studios, will be a a free-to-play fighting/card-battle Mortal Kombat game. The game promises card collecting, multiplayer team combat, classic characters such as Scorpion, Sub-Zero and Raiden, and the... | Read more »
GDC 2015 – Piloteer is Whitaker Trebella...
GDC 2015 – Piloteer is Whitaker Trebella’s Latest Project, and it’s Definitely Something DIfferent Posted by Rob Rich on March 3rd, 2015 [ permalink ] You know | Read more »
PangoLand Review
PangoLand Review By Amy Solomon on March 3rd, 2015 Our Rating: :: COME VISIT PANGO AND FRIENDSUniversal App - Designed for iPhone and iPad PangoLand is an open-ended world full of familiar characters, bright colors and interactive... | Read more »

Price Scanner via MacPrices.net

iPad: A More Positive Outlook – The ‘Book Mys...
It’s good to hear someone saying positive things about the iPad. I’ve been trying to bend my mind around how Apple’s tablet could have gone from zero to bestselling personal computing device on the... Read more
Mac Pros on sale for up to $279 off MSRP
Amazon has Mac Pros in stock and on sale for up to $279 off MSRP. Shipping is free: - 4-Core Mac Pro: $2725.87, $273 off MSRP (9%) - 6-Core Mac Pro: $3719.99, $279 off MSRP (7%) Read more
Sale! 13-inch Retina MacBook Pros for up to $...
B&H Photo has 13″ Retina MacBook Pros on sale for up to $205 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.6GHz/128GB Retina MacBook Pro: $1219.99 save $80 - 13″ 2.... Read more
Another Tranche Of IBM MobileFirst For iOS Ap...
IBM has announced the next expansion phase for  its IBM MobileFirst for iOS portfolio, with a troika of new apps to address key priorities for the Banking and Financial Services, Airline and Retail... Read more
Sale! 15-inch Retina MacBook Pros for up to $...
B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for up to $250 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $... Read more
WaterField Designs Introduces the Minimalist...
With Apple Pay gaining popularity, Android Pay coming in May 2015, and loyalty cards and receipts that can be accessed from smartphones, San Francisco’s WaterField Designs observes that it may be... Read more
Sale! 15-inch 2.2GHz Retina MacBook Pro for $...
 Best Buy has the 15″ 2.2GHz Retina MacBook Pro on sale for $1774.99 $1799.99, or $225 off MSRP. Choose free home shipping or free local store pickup (if available). Price valid for online orders... Read more
13-inch 2.5GHz MacBook Pro (refurbished) avai...
The Apple Store has Apple Certified Refurbished 13″ 2.5GHz MacBook Pros available for $170 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.5GHz... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
27-inch 3.5GHz 5K iMac in stock today and on...
 B&H Photo has the 27″ 3.5GHz 5K iMac in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP, and it’s the lowest price available for... Read more

Jobs Board

*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
Position Opening at *Apple* - Apple (United...
…Summary** As a Specialist, you help create the energy and excitement around Apple products, providing the right solutions and getting products into customers' hands. You Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Pay Automation Engineer - iOS System...
**Job Summary** At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring passion and dedication to your job Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.