home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Applications / Teapot / Teapot.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-18  |  12.5 KB  |  529 lines  |  [TEXT/KAHL]

  1. /* Title:    Teapot.c
  2.  * Author:    David Phillip Oster
  3.  * Synopsis:    The Macintosh interface for teapot.
  4.  *    Teapot is an example of 3-D graphics on the Mac. Useless
  5.  *    in itself, it is meant to inspire others.
  6.  * History:
  7.  *        February 19, 1987 - dpo - added this note
  8.  * Note: this program was designed to compile using a C compiler that
  9.  * supports ANSI prototypes, such as LightSpeed C Version 2, by Think
  10.  * Technologies, Inc. (Lexington, Ma.) To compile this program with some 
  11.  * other C compiler, extern declarations of the form:
  12.     pascal void MoveTo2D(Fixed x, Fixed y);
  13.  * need to be changed to:
  14.     pascal void MoveTo2D();
  15.  * i.e.: the argument list needs to be removed.
  16.  *         November 17, 1995 - dpo converted to THINK C 7
  17.  */
  18. #include <setjmp.h>
  19. #include "Graf3D.h"
  20. #include "teapot.h"
  21. #include "TeapotRes.h"
  22.  
  23. #define NIL 0L
  24. #define SKIPGRAYLINE 2
  25.  
  26. enum{ ABOUTI = 1 };    /* Apple menu items */
  27.  
  28. enum{  DRAWI = 1,
  29.     QUITI = DRAWI + SKIPGRAYLINE };    /* File Menu items */
  30.  
  31. #define LASTMENU kScaleMenu    /* change this if add a new menu */
  32.  
  33. typedef EventRecord *EventPtr;
  34. typedef long int LongInt;
  35. typedef short int Integer;
  36.  
  37. /* ********* the forward declarations ********* */
  38.  
  39. static void GoMouseDown(EventPtr);    /* handle mouse down */
  40. static void GoKey(EventPtr);        /* handle key down */
  41. static void GoMenuBar(EventPtr);    /* handle click in menu bar */
  42. static void GoCommand(LongInt);    /* handle menu command, click or key */
  43. static void DoAppleMenu(Integer);
  44. static void DoDeskAcc(Integer);
  45. static void DoDrawing(void);        /* redo a new drawing */
  46. static void DoFileMenu(Integer);
  47. static void DoEditMenu(Integer);
  48. static void GoActiveEvent(EventPtr);
  49. static void GoUpdateEvent(EventPtr);
  50. static PicHandle PicContents(WindowPtr);    /* turn window contents to picture */
  51. static void GoGoAway(EventPtr, WindowPtr);
  52. static void DoQuit(void);        /* procedure so can pass pointer to InitDialog */
  53. static void DoParamMenu(Integer, Integer, int *);    /* parameter menus */
  54. static Boolean UserFrontWindow(void);
  55. static void DoDecompile(void);
  56.  
  57. /* order of these is important. pass address in DoParamMenu
  58.  */
  59. typedef struct{
  60.     int    pitch;
  61.     int    yaw;
  62.     int    roll;
  63.     int    view;
  64.     int    smooth;
  65.     int    scale;
  66. } UserParams;
  67.  
  68. UserParams val = {
  69. /* pitch(x)    yaw (y)    roll(z)    view    smooth    scale*/
  70.    70,        0,        20,      10,        6,        6
  71. };
  72.  
  73.     /* synonym for above structure as an integer array */
  74. int *valInt = (int *) &val;
  75.  
  76. jmp_buf drawBuf; /* hold continuation for setjmp/longjmp */
  77.  
  78. Integer ourResFile;     /* initialize this early (DAs may play with */
  79.          /* our resFile) */
  80.  
  81. /* OneEvent - dispatch on events, do idle processing
  82.  */
  83.  
  84. void OneEvent(void){
  85.     EventRecord myEvent;
  86.  
  87.      do{
  88.         if(GetNextEvent(everyEvent, &myEvent)){
  89.             switch(myEvent.what){
  90.                 case updateEvt:    GoUpdateEvent( &myEvent);
  91.                     break;
  92.                 case activateEvt : GoActiveEvent( &myEvent);
  93.                     break;
  94.                 case mouseDown:    GoMouseDown( &myEvent);
  95.                     break;
  96.                 case keyDown:    GoKey( &myEvent);
  97.                     break;
  98.                 default : ;
  99.             }
  100.         }
  101.         SystemTask();
  102.     }while( ! UserFrontWindow());
  103. }
  104.  
  105.  
  106. /* GoMouseDown - handle mouse down from event manager
  107.  *
  108.  * Note: we forbid the user to drag the window (we don't implement dragging)
  109.  * because we aren't prepared to handle the update event that would occur when
  110.  * the user dragged back onto the screen a partially offscreen window. We
  111.  * do handle redrawing after a desk accessory is closed though.
  112.  * 
  113.  * Note: we forbid the user to resize the window (we don't implement growing)
  114.  * just out of laziness.
  115.  */
  116. static void GoMouseDown(EventPtr theEvent){
  117.     WindowPtr whichWindow;
  118.  
  119.     switch(FindWindow(theEvent->where, &whichWindow)){
  120.         case inMenuBar : GoMenuBar(theEvent);                break;
  121.         case inGoAway  : GoGoAway(theEvent, whichWindow);    break;
  122.         case inSysWindow :
  123.              SystemClick(theEvent, whichWindow);
  124.             SetPort(FrontWindow());
  125.             break;
  126.     }
  127. }
  128.  
  129. /* GoKey - handle key down from event manager
  130.  * <command>- . is a special case. 
  131.  */
  132. static void GoKey(EventPtr theEvent){
  133.     if((theEvent->modifiers & cmdKey) && ((theEvent->message & charCodeMask) != '.')){
  134.         GoCommand(MenuKey( (Integer) (theEvent->message & charCodeMask) ));
  135.     }
  136. }
  137.  
  138.  
  139. /* GoMenuBar - handle mousedown in menu bar
  140.  */
  141. static void GoMenuBar(EventPtr theEvent){
  142.     GoCommand(MenuSelect(theEvent->where));
  143. }
  144.  
  145. /* GoCommand - dispatches on the menu.
  146.  * It guarantees that the item in the menubar is
  147.  * hilighted long enough to be able to see it.
  148.  */
  149. static void GoCommand(LongInt theCom){
  150.     LongInt endTime, newTime;
  151.     Integer theLow;
  152.     
  153.     theLow = LoWord(theCom);
  154.     endTime = 5 + TickCount();
  155.     switch(HiWord(theCom)){
  156.         case kAppleMenu : DoAppleMenu(theLow);
  157.             break;
  158.         case kFileMenu :    DoFileMenu(theLow);
  159.             break;
  160.         case kEditMenu :    DoEditMenu(theLow);
  161.             break;
  162.         case kPitchMenu:
  163.         case kYawMenu:
  164.         case kRollMenu:
  165.         case kViewMenu:
  166.         case kSmoothMenu:
  167.         case kScaleMenu:
  168.             DoParamMenu(HiWord(theCom),theLow, &valInt[HiWord(theCom)-kPitchMenu]);
  169.             break;
  170.     }
  171.     newTime = TickCount();
  172.     if(newTime < endTime){            /* let's see that command */
  173.         Delay(endTime - newTime, &newTime);
  174.     }
  175.     HiliteMenu(0);
  176. }
  177.  
  178. /* PreserveContents - save our drawing window if something will cover
  179.  * it.
  180.  */
  181. void PreserveContents(void){
  182.     if(qd.thePort != NIL &&
  183.         ((WindowPeek) qd.thePort)->windowKind == userKind &&
  184.         NIL ==  GetWindowPic(qd.thePort)){
  185.  
  186.         SetWindowPic(qd.thePort, PicContents(qd.thePort));
  187.     }
  188. }
  189.  
  190. /* DoAppleMenu - handle mouse down in apple menu
  191.  */
  192.  
  193. void DoAppleMenu(Integer theItem){
  194.     GrafPtr savePort;
  195.  
  196.     switch(theItem){
  197.         case ABOUTI: 
  198.             GetPort(&savePort);
  199.             PreserveContents();
  200.             Alert(rAbout, NIL);
  201.             SetPort(savePort);
  202.             break;
  203.         default :    DoDeskAcc(theItem);    break;
  204.     }
  205. }
  206.  
  207. /* DoDeskAcc - run a desk accessory
  208.  */
  209. static void DoDeskAcc(Integer theItem){
  210.     Str255 accessory;
  211.     
  212.     PreserveContents();
  213.     GetItem(GetMHandle(kAppleMenu), theItem, accessory);
  214.     theItem = OpenDeskAcc(accessory);    /* don't care about return value */
  215. }
  216.  
  217. /* DoFileMenu -
  218.  */
  219. static void DoFileMenu(Integer theItem){
  220.     switch(theItem){
  221.         case DRAWI :    DoDrawing();
  222.             break;
  223.         case QUITI :    DoQuit();
  224.             break;
  225.     }
  226. }
  227.  
  228. /* DoEditMenu - _should_ put the teapot on the clipboard if cut or copy
  229.  * actually just passes event to desk accessory.
  230.  */
  231. static void DoEditMenu(Integer theItem){
  232.     if( ! SystemEdit(theItem -1)){
  233.         switch(theItem){
  234.             case cutCmd:
  235.             case copyCmd:
  236.             break;
  237.         }
  238.     }
  239. }
  240.  
  241. /* DoQuit - a proc so we can pass it in InitDialog
  242.  */
  243. static void DoQuit(){
  244.     ExitToShell();
  245. }
  246.  
  247. /* TrimToDigits - truncate the string to remove non-digit chars.
  248.  * except: null characters get turned into okay spaces.
  249.  */
  250. static void TrimToDigits(Str255 s){
  251.     int i;
  252.     for(i=1;i<=s[0];i++){
  253.         if( s[i] == '\0'){
  254.             s[i] = ' ';
  255.         } else if( s[i] < '-' || s[i] > '9'){
  256.             break;
  257.         }
  258.     }
  259.     s[0] = i-1;
  260. }
  261.  
  262. /* DoParamMenu - do a click on menu m, item i.
  263.  * put resultant value in v.
  264.  */
  265. static void DoParamMenu(Integer m, Integer i, int *v){
  266.     Str255 s;
  267.     int j, jMax;
  268.     LongInt n;
  269.     MenuHandle theMenu;
  270.  
  271.     theMenu = GetMHandle(m);
  272.     GetItem(theMenu, i, s);
  273.     jMax = CountMItems(theMenu);
  274.     for(j=1 ; j<= jMax ; j++){
  275.         CheckItem(theMenu, j, j==i);
  276.     }
  277.     TrimToDigits(s);
  278.     StringToNum(s, &n);
  279.     *v = n;
  280. }
  281.  
  282. /* IsNumSame - return true if sPre is a numeric prefix of s. ignore nulls in s
  283.  */
  284. static Boolean IsNumSame(unsigned char *sPre, unsigned char *s){
  285.     int pLen, sLen;
  286.     
  287.     pLen = *sPre++;
  288.     sLen = *s++;
  289.     if(*s == '\0'){    /* skip leading null char, if any */
  290.         s++;
  291.         sLen--;
  292.     }
  293.     if(pLen > sLen){    /* prefix too big to match */
  294.         return FALSE;
  295.     }
  296.     while(pLen > 0){
  297.         if(*sPre++ != *s++)
  298.             return FALSE;
  299.         sLen--;
  300.         pLen--;
  301.     }
  302.     return sLen == 0 || ! ('0' <= *s && *s <= '9'); /* true if s out of digits */
  303. }
  304.  
  305. /* CheckInitial - if i is in the menu, checkmark it.
  306.  */
  307. static void CheckInitial(MenuHandle m, int i){
  308.     Str255 sVal, sItem;
  309.  
  310.     NumToString( (LongInt) i, sVal);
  311.  
  312.     for(i=1;i<= CountMItems(m);i++){
  313.         GetItem(m, i, sItem);
  314.  
  315.         if(IsNumSame(sVal, sItem)){
  316.             CheckItem(m, i, TRUE);
  317.             return;
  318.         }
  319.     }
  320. }
  321.  
  322. /* FindInitials - draw the initial checkmarks.
  323.  */
  324. static void FindInitials(void){
  325.     Integer i;
  326.  
  327.     for(i = kPitchMenu ; i <= LASTMENU ; i++){
  328.         CheckInitial(GetMHandle(i), valInt[i - kPitchMenu]);
  329.     }
  330. }
  331.  
  332. /* PicContents - make pic of window's contents
  333.  */
  334. static PicHandle PicContents(WindowPtr w){
  335.     PicHandle p;
  336.     GrafPtr savePort;
  337.  
  338.     GetPort(&savePort);
  339.     SetPort(w);
  340.     p = OpenPicture(&qd.thePort->portRect);
  341.     CopyBits(&qd.thePort->portBits,&qd.thePort->portBits,
  342.         &qd.thePort->portRect,&qd.thePort->portRect,srcCopy, NIL);
  343.     ClosePicture();
  344.     SetPort(savePort);
  345.     if(MemError() != noErr){
  346.         KillPicture(p);
  347.         return NIL;
  348.     }
  349.     return p;
  350. }
  351.  
  352. /* GoActiveEvent - handle activate/deactivate events.
  353.  */
  354. static void GoActiveEvent(EventPtr theEvent){
  355.     WindowPtr w, savePort;
  356.     PicHandle p;
  357.  
  358.     w = (WindowPtr) theEvent->message;
  359.     if( ((WindowPeek) w)->windowKind == userKind){
  360.         if(theEvent->modifiers & activeFlag){
  361.             SetPort(w);
  362.             if(NIL != (p = GetWindowPic(w))){
  363.                 DrawPicture(p, &qd.thePort->portRect);
  364.                 KillPicture(p);
  365.                 SetWindowPic(w, NIL);
  366.             }
  367.         }else{
  368.             GetPort(&savePort);
  369.             SetPort(w);
  370.             PreserveContents();
  371.             SetPort(savePort);
  372.         }
  373.     }
  374. }
  375.  
  376. /* GoUpdateEvent - clean these out of the event queue.
  377.  */
  378. static void GoUpdateEvent(EventPtr theEvent){
  379.     BeginUpdate((WindowPtr) theEvent->message);
  380.     EndUpdate((WindowPtr) theEvent->message);
  381. }
  382.  
  383. /* GoGoAway - handles mouse in GoAway
  384.  */
  385. static void GoGoAway(EventPtr theEvent, WindowPtr w){
  386.     if(TrackGoAway(w, theEvent->where) && 
  387.         ((WindowPeek) w)->windowKind == userKind){
  388.  
  389.         DoQuit();
  390.     }
  391. }
  392.  
  393. /* DoDrawing - start drawing again
  394.  */
  395. static void DoDrawing(void){
  396.     if(UserFrontWindow()){
  397.         HiliteMenu(0);    /* since we never return, do it heer */
  398.         longjmp(drawBuf, 0);
  399.     }
  400. }
  401.  
  402. /* UserFrontWindow - true if drawing window on top
  403.  */
  404. static Boolean UserFrontWindow(void){
  405.     if(FrontWindow() == NIL){
  406.         return FALSE;
  407.     }
  408.     return ((WindowPeek) FrontWindow())->windowKind == userKind;
  409. }
  410.  
  411. /* Int2Fix - do this faster than FixRatio(i,1)
  412.  */
  413. static Fixed Int2Fix(Integer i){
  414.     union {
  415.         Fixed f;
  416.         struct { Integer whole, frac; } i;
  417.     } iAndF;
  418.     
  419.     iAndF.i.whole = i;
  420.     iAndF.i.frac = 0;
  421.     return iAndF.f;
  422. }
  423.  
  424. /* SetDrawTransform - set up the viewing transform:
  425.  * world coordinates are roughly from 4 to -0.5 vertically,
  426.  * and the appropriate horizontally to correct for the aspect
  427.  * ratio of the picture. mutiply by user's scale factor
  428.  *
  429.  * 1.) re-initialize the matrix with Identity()
  430.  * 2.) set scale factor for world coordinates, preserving x, y ratio of window
  431.  * 3.) set wide-angleness of lens (24° is human eye standard)
  432.  * 4.) tip about the x, y, and z axes. these values are in degrees, expressed
  433.  *    as fixed point numbers.
  434.  */
  435. static void SetDrawTransform(void){
  436.     int width,height;    /* set in main */
  437.  
  438.     width = qd.thePort->portRect.right - qd.thePort->portRect.left;
  439.     height = qd.thePort->portRect.bottom - qd.thePort->portRect.top;
  440.     Identity();    /* order of next few lines is important */
  441.     if(val.scale)LookAt(FixRatio(-50,2*val.scale),     /* left */
  442.         FixRatio(40*height,width*val.scale),         /* top */
  443.         FixRatio(50,2*val.scale),                     /* right */
  444.         FixRatio(-10*height,width*val.scale));        /* bottom */
  445.  
  446.     ViewAngle(Int2Fix(val.view));
  447.  
  448.     if(val.roll)Roll(     Int2Fix(val.roll));
  449.     if(val.yaw)Yaw(       Int2Fix(val.yaw));
  450.     if(val.pitch)Pitch(   Int2Fix(val.pitch));
  451. }
  452.  
  453. Port3DPtr thePort3D;
  454.  
  455. StringPtr binName = "\pTeapot.binData";
  456. main(){
  457.     Port3D my3Port;
  458.     GrafPtr myPort;
  459.     LongInt len;
  460.     Integer f;
  461.     OSErr eCode;
  462.  
  463.     FlushEvents(everyEvent, 0);
  464.     InitGraf(&qd.thePort);
  465.     InitGrf3d(&thePort3D);
  466.     InitFonts();
  467.     InitWindows();
  468.     InitCursor();
  469.     InitDialogs( 0 );
  470.     InitMenus();
  471.     TEInit();
  472.     SetMenuBar(GetNewMBar(kMBAR));
  473.     AddResMenu(GetMHandle(kAppleMenu), 'DRVR');
  474.     DrawMenuBar();
  475.     ourResFile = CurResFile();    /* get it before DAs change it */
  476.  
  477.     myPort = GetNewWindow(rWin, NIL, (WindowPtr) -1L);
  478.     SetPort(myPort);
  479.     Open3DPort(&my3Port);
  480.  
  481.     SetCursor( *GetCursor(watchCursor));
  482.     DisableItem(GetMHandle(kFileMenu), DRAWI);
  483.  
  484.     len = sizeof(Point3D)*(1+DUCKCOUNT);
  485.     f = 0;
  486.     /* use the binary data file if we can, else try to make it */
  487.     if( ! (noErr == FSOpen(binName, 0, &f) && noErr == FSRead(f, &len, &Pt3ds))){
  488.  
  489.         FloatsToFixeds( (float *) &ducks, (Fixed *) &Pt3ds, (DUCKCOUNT+1)*3);
  490.         FSClose(f);
  491.         if(Create(binName, 0, '????', '????') == noErr){
  492.             if(FSOpen(binName, 0, &f) == noErr){
  493.                 len = sizeof(Point3D)*(1+DUCKCOUNT);
  494.                 eCode = FSWrite(f, &len, &Pt3ds);
  495.                 FSClose(f);
  496.                 if(eCode != noErr)
  497.                     FSDelete(binName, 0);
  498.                 FlushVol(NIL, 0);
  499.                 f = 0;
  500.             }
  501.         }
  502.     }
  503.     if(0 != f){
  504.         FSClose(f);
  505.     }
  506.     InitCursor();
  507.     EnableItem(GetMHandle(kFileMenu), DRAWI);
  508.     FindInitials();
  509.     ShowWindow(qd.thePort);
  510.  
  511.     if(setjmp(drawBuf)){    /* come here when user selects "draw"  */
  512.     }
  513.     SetPort(myPort);
  514.     BackPat(&qd.black);
  515.     EraseRect(&myPort->portRect);
  516.  
  517.     SetDrawTransform();
  518.  
  519.     PenPat(&qd.white);
  520.  
  521.     BaseGrid();
  522.     DisplayPatches(val.smooth);
  523.     SysBeep(1);
  524.     while(TRUE){
  525.         OneEvent();
  526.     }
  527. }
  528.  
  529.