/******************************************************************************

   Module Name: crane
   Description: 3D Origami Crane Animator
   Version:     v1.00
   Authors:     Mike Robert
   Date:        3/30/90

   For Jennifer Lee.

   Revision History:

   Bugs:

   Enhancements:

******************************************************************************/

/* INCLUDE FILES
*/

#define  NOCOMM

#include <Windows.h>
#include <math.h>
#include <process.h>
#include "WinUnDoc.h"
#include "crane.h"
#include "dialogs.h"

/* SYMBOLIC CONSTANTS
*/

/*  Miscellaneous.  */

#define  PI                   3.14159265     /* The value of ã */

#define  CNT_ANIMATE          4              /* Animate count */

#define  MAX_MOVE             100.           /* Maximum movement */

#define  MAX_POINTS            56            /* Maximum points */

#define  MAX_SHAPES            11            /* Maximum shapes */

/*  Crane Point Flags.  */

#define  CPF_NORMAL           0x0000         /* Normal point */
#define  CPF_START            0x0001         /* New line sequence start */
#define  CPF_MOVE             0x0002         /* Movement point */
#define  CPF_EOL              0x8000         /* End of point list */

/*  ROTation.  */

#define  ROT_INC              ( PI / 18. )   /* Rotation degree increment */
#define  ROT_MIN              ( 0. )         /* Rotation minimum */
#define  ROT_MAX              ( PI * 2. )    /* Rotation maximum */

/*  Z-SiZing.  */

#define  ZSZ_ATMINSIZ         -5000.
#define  ZSZ_ATMAXSIZ         5000.

#define  ZSZ_WINORG           -1500
#define  ZSZ_WINEXT           3000

/* MACROS
*/

#define  WITHIN(a,b,c)        ( (a) >= (b) && (a) <= (c) )

/* TYPEDEFS
*/

typedef struct _POINT3D
{
   double         x, y, z;                   /* XYZ coordinates (floating) */
}
   POINT3D;                                  /* 3-dimensional point */

typedef POINT3D FAR *LPPOINT3D;

typedef struct _ANIMATEPT
{
   WORD           unPtIndex;                 /* Point index */
   double         dfMinRot,                  /* Minimum rotation */
                  dfMaxRot,                  /* Maximum rotation */
                  dfCurRot,                  /* Current rotation */
                  dfIncRot,                  /* Rotation increment */
                  dfDst;                     /* Distance */
}
   ANIMATEPT;                                /* Animate point structure */

typedef ANIMATEPT FAR *LPANIMATEPT;

typedef struct _SHAPE
{
   WORD           unIndex,                   /* Index */
                  unCnt;                     /* Point count */
}
   SHAPE;                                    /* Shape structure */

typedef SHAPE FAR *LPSHAPE;

typedef struct _CRANEPT
{
   WORD           unFlags;                   /* Flags */
   POINT3D        pt3dOrg,                   /* Original point */
                  pt3dNew;                   /* New point */
}
   CRANEPT;                                  /* Crane point structure */

typedef CRANEPT FAR *LPCRANEPT;

/* GLOBAL VARIABLES
*/

static   HANDLE         hModule;             /* Module instance handle */

static   HWND           hClientWnd;          /* Client window handle */
static   HDC            hClientDC;           /* Client display context handle */

static   FARPROC        lpfnXYZDataDlgProc;  /* XYZ data dialog function pointer */
static   HWND           hXYZDataDlg = NULL;  /* Data dialog window handle */

static   BOOL           bAnimate = FALSE;    /* Animation flag */

static   BOOL           bRecording = FALSE;  /* Recording flag */
static   WORD           unRecordCnt = 0;     /* Record count */
static   int            fhRecordFile;        /* Record file handle */

static   RECT           rErase,              /* Erase rectangle */
                        rOldErase,           /* Old erase rectangle */
                        rUpdate;             /* Update rectangle */

static   POINT          aptList[ MAX_POINTS ];
                                             /* Point list */

static   POINT3D        pt3dRot;             /* Rotation amounts */

static   POINT3D        pt3dLoc;             /* Current location */

static   CRANEPT        cptMove =            /* Movement crane point */
                        {
                           CPF_MOVE,
                           { 0., 0., MAX_MOVE },
                           { 0., 0., 0. }
                        };

static   ANIMATEPT      aaptList[ CNT_ANIMATE ] =
                                             /* Animate point list */
                        {
                           {
                              33,
                              PI * 3. / 4. + PI / 40.,
                              PI * 5. / 4. - PI / 40.,
                              PI,
                              -PI / 20.,
                              1050.
                           },
                           {
                              37,
                              PI * 3. / 4. + PI / 40.,
                              PI * 5. / 4. - PI / 40.,
                              PI,
                              -PI / 20.,
                              1050.
                           },
                           {
                              41,
                              -PI / 4. + PI / 40.,
                              PI / 4. - PI / 40.,
                              0.,
                              PI / 20.,
                              1050.
                           },
                           {
                              45,
                              -PI / 4. + PI / 40.,
                              PI / 4. - PI / 40.,
                              0.,
                              PI / 20.,
                              1050.
                           }
                        };

static   POINT3D        pt3dMin =            /* Coordinate minimums */
                        { -1050., -405., -810. },
                        pt3dMax =            /* Coordinate maximums */
                        { 1050., 405., 810. };

static   SHAPE          ashpList[ ] =        /* Shape list */
                        {
                           { 0, 4 },
                           { 4, 7 },
                           { 11, 5 },
                           { 16, 5 },
                           { 21, 5 },
                           { 26, 5 },
                           { 31, 4 },
                           { 35, 4 },
                           { 39, 4 },
                           { 43, 4 },
                           { 47, 9 }
                        };

static   CRANEPT        acptList[ ] =        /* Crane points */
                        {
                           {                 /* 0 */
                              CPF_START,
                              { 0., -405., -810. },
                              { 0., 0., 0. }
                           },
                           {                 /* 1 */
                              CPF_NORMAL,
                              { 0., 405., -180. },
                              { 0., 0., 0. }
                           },
                           {                 /* 2 */
                              CPF_NORMAL,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 3 */
                              CPF_NORMAL,
                              { 0., -405., -810. },
                              { 0., 0., 0. }
                           },
                           {                 /* 4 */
                              CPF_START,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 5 */
                              CPF_NORMAL,
                              { 0., 405., 300. },
                              { 0., 0., 0. }
                           },
                           {                 /* 6 */
                              CPF_NORMAL,
                              { 0., -135., 720. },
                              { 0., 0., 0. }
                           },
                           {                 /* 7 */
                              CPF_NORMAL,
                              { 0., 135., 810. },
                              { 0., 0., 0. }
                           },
                           {                 /* 8 */
                              CPF_NORMAL,
                              { 0., -195., 780. },
                              { 0., 0., 0. }
                           },
                           {                 /* 9 */
                              CPF_NORMAL,
                              { 0., -225., 690. },
                              { 0., 0., 0. }
                           },
                           {                 /* 10 */
                              CPF_NORMAL,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 11 */
                              CPF_START,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 12 */
                              CPF_NORMAL,
                              { 0., 405., -180. },
                              { 0., 0., 0. }
                           },
                           {                 /* 13 */
                              CPF_NORMAL,
                              { -450., 75., -270. },
                              { 0., 0., 0. }
                           },
                           {                 /* 14 */
                              CPF_NORMAL,
                              { -90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 15 */
                              CPF_NORMAL,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 16 */
                              CPF_START,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 17 */
                              CPF_NORMAL,
                              { 0., 405., 300. },
                              { 0., 0., 0. }
                           },
                           {                 /* 18 */
                              CPF_NORMAL,
                              { -450., 75., 390. },
                              { 0., 0., 0. }
                           },
                           {                 /* 19 */
                              CPF_NORMAL,
                              { -90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 20 */
                              CPF_NORMAL,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 21 */
                              CPF_START,
                              { 450., 75., -270. },
                              { 0., 0., 0. }
                           },
                           {                 /* 22 */
                              CPF_NORMAL,
                              { 90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 23 */
                              CPF_NORMAL,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 24 */
                              CPF_NORMAL,
                              { 0., 405., -180. },
                              { 0., 0., 0. }
                           },
                           {                 /* 25 */
                              CPF_NORMAL,
                              { 450., 75., -270. },
                              { 0., 0., 0. }
                           },
                           {                 /* 26 */
                              CPF_START,
                              { 90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 27 */
                              CPF_NORMAL,
                              { 450., 75., 390. },
                              { 0., 0., 0. }
                           },
                           {                 /* 28 */
                              CPF_NORMAL,
                              { 0., 405., 300. },
                              { 0., 0., 0. }
                           },
                           {                 /* 29 */
                              CPF_NORMAL,
                              { 0., 255., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 30 */
                              CPF_NORMAL,
                              { 90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 31 */
                              CPF_START,
                              { -90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 32 */
                              CPF_NORMAL,
                              { -450., 75., -270. },
                              { 0., 0., 0. }
                           },
                           {                 /* 33 */
                              CPF_NORMAL,
                              { -1050., 0., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 34 */
                              CPF_NORMAL,
                              { -90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 35 */
                              CPF_START,
                              { -90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 36 */
                              CPF_NORMAL,
                              { -450., 75., 390. },
                              { 0., 0., 0. }
                           },
                           {                 /* 37 */
                              CPF_NORMAL,
                              { -1050., 0., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 38 */
                              CPF_NORMAL,
                              { -90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 39 */
                              CPF_START,
                              { 90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 40 */
                              CPF_NORMAL,
                              { 450., 75., -270. },
                              { 0., 0., 0. }
                           },
                           {                 /* 41 */
                              CPF_NORMAL,
                              { 1050., 0., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 42 */
                              CPF_NORMAL,
                              { 90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 43 */
                              CPF_START,
                              { 90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 44 */
                              CPF_NORMAL,
                              { 450., 75., 390. },
                              { 0., 0., 0. }
                           },
                           {                 /* 45 */
                              CPF_NORMAL,
                              { 1050., 0., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 46 */
                              CPF_NORMAL,
                              { 90., 195., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 47 */
                              CPF_START,
                              { -270., 135., 390. },
                              { 0., 0., 0. }
                           },
                           {                 /* 48 */
                              CPF_NORMAL,
                              { 0., -15., 330. },
                              { 0., 0., 0. }
                           },
                           {                 /* 49 */
                              CPF_NORMAL,
                              { 270., 135., 390. },
                              { 0., 0., 0. }
                           },
                           {                 /* 50 */
                              CPF_NORMAL,
                              { 270., 45., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 51 */
                              CPF_NORMAL,
                              { 270., 135., -270. },
                              { 0., 0., 0. }
                           },
                           {                 /* 52 */
                              CPF_NORMAL,
                              { 0., -45., -210. },
                              { 0., 0., 0. }
                           },
                           {                 /* 53 */
                              CPF_NORMAL,
                              { -270., 135., -270. },
                              { 0., 0., 0. }
                           },
                           {                 /* 54 */
                              CPF_NORMAL,
                              { -270., 45., 60. },
                              { 0., 0., 0. }
                           },
                           {                 /* 55 */
                              CPF_NORMAL,
                              { -270., 135., 390. },
                              { 0., 0., 0. }
                           },
                           {                 /* 56 */
                              CPF_EOL,
                              { 0., 0., 0. },
                              { 0., 0., 0. }
                           }
                        };

#define  MAX_POINTS            56            /* Maximum points */

#define  MAX_SHAPES            11            /* Maximum shapes */

/* FUNCTION PROTOYPES
*/

void PASCAL WinMain(

   HANDLE hInstance,    /* Instance handle.
                        */
      
   HANDLE hPrevInstance,
                        /* Previous instance handle.
                        */
      
   LPSTR lpszCmdLine,   /* Command line string pointer.
                        */
      
   int nCmdShow         /* Show command parameter.
                        */
      
   );

long FAR PASCAL WindowProc( 

   HWND hWnd,           /* Window handle.
                        */

   unsigned nMsg,       /* Message type.
                        */

   WORD wParam,         /* 16-bit parameter.
                        */

   LONG lParam          /* 32-bit parameter.
                        */

   );

BOOL NEAR PASCAL Init( 

   HANDLE hInstance,    /* Instance handle.
                        */
      
   HANDLE hPrevInstance,
                        /* Previous instance handle.
                        */
      
   LPSTR lpszCmdLine,   /* Command line string pointer.
                        */
      
   int nCmdShow         /* Show command parameter.
                        */
      
   );

void NEAR PASCAL Menu( 

   HWND hWnd,           /* Window handle.
                        */

   WORD wID             /* Command ID.
                        */

   );

void NEAR PASCAL InitMenu(

   HWND hWnd,           /* Window handle.
                        */

   HMENU hMenu          /* Menu handle.
                        */

   );

BOOL FAR PASCAL XYZDataDlgProc( 

   HWND hDlg,           /* Dialog window handle.
                        */

   unsigned nMsg,       /* Message to dialog.
                        */

   WORD wParam,         /* Word parameter.
                        */

   LONG lParam          /* Long parameter.
                        */

   );

void NEAR PASCAL UpdateXYZDataDlg( void );

BOOL FAR PASCAL AboutDlgProc( 

   HWND hDlg,           /* Dialog window handle.
                        */

   unsigned nMsg,       /* Message type.
                        */

   WORD wParam,         /* 16-bit parameter.
                        */

   LONG lParam          /* 32-bit parameter.
                        */

   );

void NEAR PASCAL AnimateCranePts(

   HWND hWnd            /* Window handle.
                        */

   );

void NEAR PASCAL RecordPlayback( 

   HWND hWnd            /* Window handle.
                        */

   );
      
void NEAR PASCAL RecordRead( void );

void NEAR PASCAL RecordWrite( void );

void NEAR PASCAL Key( 

   HWND hWnd,           /* Window handle.
                        */

   WORD wId             /* Key ID.
                        */

   );
      
void NEAR PASCAL CalcNewCranePts( void );

void NEAR PASCAL CalcNewPt(

   LPCRANEPT lpcpt      /* Crane point pointer.
                        */

   );

void NEAR PASCAL Size(

   HWND hWnd,           /* Window handle.
                        */

   LONG lParam          /* 32-bit parameter.
                        */

   );

void NEAR PASCAL Paint( 

   HWND hWnd            /* Window handle.
                        */

   );

void NEAR PASCAL ResetCranePts( void );

void NEAR PASCAL myYield( void );

/******************************************************************************

   Function Name: WinMain

   Description:

      Top level procedure called by windows on application startup.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void PASCAL WinMain(

   HANDLE hInstance,    /* Instance handle.
                        */
      
   HANDLE hPrevInstance,
                        /* Previous instance handle.
                        */
      
   LPSTR lpszCmdLine,   /* Command line string pointer.
                        */
      
   int nCmdShow         /* Show command parameter.
                        */
      
   )

{
   /*  Variable definition.  */

   MSG            stMsg;                     /* Message structure */

   /*  Initialize ourselves.  */

   if ( !Init( hInstance, hPrevInstance, lpszCmdLine, nCmdShow ) )
      exit( 1 );

   /*  Process messages until exit.  */

   while ( GetMessage( &stMsg, NULL, 0, 0 ) )
   {
      /*  Translate and dispatch the message.  */

      TranslateMessage( &stMsg );
      DispatchMessage( &stMsg );

   }

   exit( stMsg.wParam );

}

/******************************************************************************

   Function Name: Init

   Description:

      Provides initialization for the first instance and every following
      instance.

   Control Flow:

   Return Values:

      Failure: FALSE
      Success: TRUE

   Notes:

******************************************************************************/

BOOL NEAR PASCAL Init( 

   HANDLE hInstance,    /* Instance handle.
                        */
      
   HANDLE hPrevInstance,
                        /* Previous instance handle.
                        */
      
   LPSTR lpszCmdLine,   /* Command line string pointer.
                        */
      
   int nCmdShow         /* Show command parameter.
                        */
      
   )
      
{
   /*  Variable definition.  */

   WNDCLASS       wcClass;                   /* Class information storage */

   OFSTRUCT       ofRecord;                  /* Record open file structure */

   /*  Perform the appropiate initialization.  */

   if ( !hPrevInstance )
   {
      wcClass.lpszClassName    = "Crane";
      wcClass.hInstance        = hInstance;
      wcClass.lpfnWndProc      = WindowProc;
      wcClass.hCursor          = LoadCursor( NULL, IDC_ARROW );
      wcClass.hIcon            = LoadIcon( hInstance, "CraneIcon" );
      wcClass.lpszMenuName     = "CraneMenu";
      wcClass.hbrBackground    = GetStockObject( WHITE_BRUSH );
      wcClass.style            = CS_OWNDC;
      wcClass.cbClsExtra       = 0;
      wcClass.cbWndExtra       = 0;

      RegisterClass( &wcClass );

   }

   /*  Store this instance so window procedures can use it.  */

   hModule = hInstance;

   /*  Create the window.  */

   hClientWnd = CreateWindow( "Crane",
                              " Crane ",
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT,
                              SW_SHOWNORMAL,
                              CW_USEDEFAULT,
                              0,
                              NULL,
                              NULL,
                              hInstance,
                              NULL
                            );

   /*  Get our display context and initialize it.  */

   hClientDC = GetDC( hClientWnd );

   SetMapMode( hClientDC, MM_ANISOTROPIC );
   SetWindowOrg( hClientDC, ZSZ_WINORG, ZSZ_WINORG );
   SetWindowExt( hClientDC, ZSZ_WINEXT, ZSZ_WINEXT );

   SelectObject( hClientDC, GetStockObject( BLACK_PEN ) );

   /*  Initialize the crane data.  */

   ResetCranePts( );

   /*  Create the dialog procedure pointer.  */

   lpfnXYZDataDlgProc = MakeProcInstance( XYZDataDlgProc, hModule );

   /*  Open the record file.  */

   fhRecordFile = OpenFile( "RECORD.DAT", &ofRecord, OF_CREATE | OF_READWRITE );
   if ( !fhRecordFile )
      MessageBox( hClientWnd,
                  "Unable to open record file",
                  "Error",
                  MB_ICONEXCLAMATION | MB_OK
                );

   unRecordCnt = 0;

   /*  Display this window on the screen.  */

   ShowWindow( hClientWnd, nCmdShow );

   /*  Force immediate paint of the client area.  */

   UpdateWindow( hClientWnd );

   return ( TRUE );

}

/******************************************************************************

   Function Name: WindowProc

   Description:

      Processes the messages for the Crane window.

   Control Flow:

   Return Values:

      DefWindowProc specific return values if message not processed
      else 0L signalling message was processed.

   Notes:

******************************************************************************/

long FAR PASCAL WindowProc( 

   HWND hWnd,           /* Window handle.
                        */

   unsigned nMsg,       /* Message type.
                        */

   WORD wParam,         /* 16-bit parameter.
                        */

   LONG lParam          /* 32-bit parameter.
                        */

   )

{
   /*  Process the message.  */

   switch ( nMsg )
   {
      case WM_INITMENU:
         InitMenu( hWnd, wParam );
         break;

      case WM_COMMAND:
         Menu( hWnd, wParam );
         break;

      case WM_KEYUP:
         Key( hWnd, wParam );
         break;

      case WM_SIZE:
         Size( hWnd, lParam );
         break;

      case WM_PAINT:
         Paint( hWnd );
         break;

      case WM_CLOSE:

         if ( bAnimate )
         {
            bAnimate = FALSE;
            PostMessage( hWnd, nMsg, wParam, lParam );
            break;
         }

         DestroyWindow( hWnd );
         break;

      case WM_DESTROY:

         _lclose( fhRecordFile );

         FreeProcInstance( lpfnXYZDataDlgProc );

         ReleaseDC( hWnd, hClientDC );

         PostQuitMessage( 0 );
         break;

      default:
         return ( DefWindowProc( hWnd, nMsg, wParam, lParam ) );
         break;

   }

   return ( 0L );

}

/******************************************************************************

   Function Name: Menu

   Description:

      Provides processing for menu commands.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL Menu( 

   HWND hWnd,           /* Window handle.
                        */

   WORD wId             /* Command ID.
                        */

   )
      
{
   /*  Variable definition.  */

   FARPROC        lpfnProc;                  /* Procedure pointer */

   /*  Check for a Control command.  */

   if ( WITHIN( wId, MI_CONTROL_MIN, MI_CONTROL_MAX ) )
   {
      switch ( wId )
      {
         /*  Check for the Reset command.  */

         case MI_CONTROL_RESET:

            ResetCranePts( );

            InvalidateRect( hWnd, NULL, TRUE );

            break;

         /*  Check for the About... command.  */

         case MI_CONTROL_DATA:

            hXYZDataDlg = CreateDialog( hModule,
                                        "DATABOX",
                                        hWnd,
                                        lpfnXYZDataDlgProc
                                      );

            break;

         /*  Check for the About... command.  */

         case MI_CONTROL_ABOUT:

            lpfnProc = MakeProcInstance( AboutDlgProc, hModule );
            DialogBox( hModule, "ABOUTBOX", hWnd, lpfnProc );
            FreeProcInstance( lpfnProc );

            break;

      }

   }

   /*  Check for an Animate command.  */

   else if ( WITHIN( wId, MI_ANIMATE_MIN, MI_ANIMATE_MAX ) )
   {
      switch ( wId )
      {
         /*  Check for the Start command.  */

         case MI_ANIMATE_START:

            bAnimate = TRUE;

            rOldErase.left   = ZSZ_WINORG;
            rOldErase.top    = ZSZ_WINORG;
            rOldErase.right  = ZSZ_WINORG + ZSZ_WINEXT + 1;
            rOldErase.bottom = ZSZ_WINORG + ZSZ_WINEXT + 1;

            AnimateCranePts( hWnd );

            break;

         /*  Check for the Stop command.  */

         case MI_ANIMATE_STOP:

            bAnimate = FALSE;

            break;

      }

   }

   /*  Check for a Record command.  */

   else if ( WITHIN( wId, MI_RECORD_MIN, MI_RECORD_MAX ) )
   {
      switch ( wId )
      {
         /*  Check for the Start command.  */

         case MI_RECORD_START:

            bRecording = TRUE;

            break;

         /*  Check for the Stop command.  */

         case MI_RECORD_STOP:

            bRecording = FALSE;

            break;

         /*  Check for the Playback command.  */

         case MI_RECORD_PLAYBACK:

            RecordPlayback( hWnd );

            break;

         /*  Check for the Reset command.  */

         case MI_RECORD_RESET:

            unRecordCnt = 0;

            _llseek( fhRecordFile, 0L, 0 );

            break;

      }

   }

   return;

}

/******************************************************************************

   Function Name: InitMenu

   Description:

      Changes the status of any of the menuitems that require moment to
      moment changes.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL InitMenu(

   HWND hWnd,           /* Window handle.
                        */

   HMENU hMenu          /* Menu handle.
                        */

   )

{
   /*  Update the apropiate menu items.  */

   if ( !hXYZDataDlg )
      EnableMenuItem( hMenu, MI_CONTROL_DATA, MF_ENABLED );
   else
      EnableMenuItem( hMenu, MI_CONTROL_DATA, MF_DISABLED | MF_GRAYED );

   if ( !bAnimate )
   {
      EnableMenuItem( hMenu, MI_ANIMATE_START, MF_ENABLED );
      EnableMenuItem( hMenu, MI_ANIMATE_STOP, MF_DISABLED | MF_GRAYED );
   }
   else
   {
      EnableMenuItem( hMenu, MI_ANIMATE_START, MF_DISABLED | MF_GRAYED );
      EnableMenuItem( hMenu, MI_ANIMATE_STOP, MF_ENABLED );
   }

   if ( !bRecording )
      EnableMenuItem( hMenu, MI_RECORD_START, MF_ENABLED );
   else
      EnableMenuItem( hMenu, MI_RECORD_START, MF_DISABLED | MF_GRAYED );

   if ( bRecording )
      EnableMenuItem( hMenu, MI_RECORD_STOP, MF_ENABLED );
   else
      EnableMenuItem( hMenu, MI_RECORD_STOP, MF_DISABLED | MF_GRAYED );

   if ( !bRecording && !bAnimate && unRecordCnt != 0 )
      EnableMenuItem( hMenu, MI_RECORD_PLAYBACK, MF_ENABLED );
   else
      EnableMenuItem( hMenu, MI_RECORD_PLAYBACK, MF_DISABLED | MF_GRAYED );

   return;

}

/******************************************************************************

   Function Name: XYZDataDlgProc

   Description:

      This routine will process the XYZ data dialog box.

   Control Flow:

   Return Values:

      Success: TRUE
      Failure: FALSE

   Notes:

******************************************************************************/

BOOL FAR PASCAL XYZDataDlgProc( 

   HWND hDlg,           /* Dialog window handle.
                        */

   unsigned nMsg,       /* Message to dialog.
                        */

   WORD wParam,         /* Word parameter.
                        */

   LONG lParam          /* Long parameter.
                        */

   )

{
   /*  Process the message.  */

   switch ( nMsg )
   {
      /*  Process the init dialog message.  */

      case WM_INITDIALOG:

         /*  Update the data fields.  */

         UpdateXYZDataDlg( );

         SetFocus( GetDlgItem( hDlg, IDOK ) );

         return ( FALSE );

         break;

      /*  Process the command message.  */

      case WM_COMMAND:

         /*  Parse the control ID.  */

         switch ( wParam )
         {
            /*  Process the Ok button. */

            case IDOK:

               DestroyWindow( hDlg );
               break;

            /*  Default processing.  */

            default:
               return ( FALSE );
               break;

         }

         break;

      /*  Process the destroy message.  */

      case WM_DESTROY:

         hXYZDataDlg = NULL;

         break;

      /*  Default processing.  */

      default:

         return ( FALSE );
         break;

   }

   return ( TRUE );

}

/******************************************************************************

   Function Name: UpdateXYZDataDlg

   Description:

      Updates the controls in the XYZ data dialog.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL UpdateXYZDataDlg( void )

{
   /*  Update the controls.  */

   SetDlgItemInt( hXYZDataDlg, ID_XROT, (int) ( pt3dRot.x / PI * 180. ), TRUE );
   SetDlgItemInt( hXYZDataDlg, ID_YROT, (int) ( pt3dRot.y / PI * 180. ), TRUE );
   SetDlgItemInt( hXYZDataDlg, ID_ZROT, (int) ( pt3dRot.z / PI * 180. ), TRUE );

   SetDlgItemInt( hXYZDataDlg, ID_XLOC, (int) ( pt3dLoc.x ), TRUE );
   SetDlgItemInt( hXYZDataDlg, ID_YLOC, (int) ( pt3dLoc.y ), TRUE );
   SetDlgItemInt( hXYZDataDlg, ID_ZLOC, (int) ( pt3dLoc.z ), TRUE );

   SetDlgItemInt( hXYZDataDlg, ID_XMOV, (int) ( cptMove.pt3dNew.x ), TRUE );
   SetDlgItemInt( hXYZDataDlg, ID_YMOV, (int) ( cptMove.pt3dNew.y ), TRUE );
   SetDlgItemInt( hXYZDataDlg, ID_ZMOV, (int) ( cptMove.pt3dNew.z ), TRUE );

   return;

}

/******************************************************************************

   Function Name: AboutDlgProc

   Description:

      Processes the messages for the About dialog window.

   Control Flow:

   Return Values:

      Dialog function dependent.

   Notes:

******************************************************************************/

BOOL FAR PASCAL AboutDlgProc( 

   HWND hDlg,           /* Dialog window handle.
                        */

   unsigned nMsg,       /* Message type.
                        */

   WORD wParam,         /* 16-bit parameter.
                        */

   LONG lParam          /* 32-bit parameter.
                        */

   )

{
   /*  Process the message.  */

   switch ( nMsg )
   {
      /*  Process the command message.  */

      case WM_COMMAND:

         /*  Parse the control ID.  */

         switch ( wParam )
         {
            /*  Process the OK button.  */

            case IDOK:
               EndDialog( hDlg, TRUE );
               break;

            /*  Default processing.  */

            default:
               return ( FALSE );
               break;

         }

         break;

      /*  Default processing.  */

      default:
         return ( FALSE );
         break;

   }

   return ( TRUE );

}

/******************************************************************************

   Function Name: AnimateCranePts

   Description:

      Performs animation on the specific crane points.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL AnimateCranePts(

   HWND hWnd            /* Window handle.
                        */

   )

{
   /*  Variable definition.  */

   WORD           unI,                       /* Indexing variable */
                  unIndex;                   /* Index */

   /*  Animate the points while animation is requested.  */

   while ( bAnimate )
   {
      /*  Initialize the erase rectangle.  */

      rErase.left   = 32767;
      rErase.top    = 32767;
      rErase.right  = -32767;
      rErase.bottom = -32767;

      /*  Loop and animate the points.  */

      for ( unI = 0; unI < CNT_ANIMATE; ++unI )
      {
         aaptList[ unI ].dfCurRot += aaptList[ unI ].dfIncRot;

         if ( aaptList[ unI ].dfCurRot <= aaptList[ unI ].dfMinRot ||
              aaptList[ unI ].dfCurRot >= aaptList[ unI ].dfMaxRot
            )
            aaptList[ unI ].dfIncRot = -aaptList[ unI ].dfIncRot;

         unIndex = aaptList[ unI ].unPtIndex;

         acptList[ unIndex ].pt3dOrg.x = cos( aaptList[ unI ].dfCurRot ) *
                                          aaptList[ unI ].dfDst;
         acptList[ unIndex ].pt3dOrg.y = sin( aaptList[ unI ].dfCurRot ) *
                                          aaptList[ unI ].dfDst;

      }

      /*  Compute the location offset.  */

      CalcNewPt( &cptMove );

      pt3dLoc.x += cptMove.pt3dNew.x;
      pt3dLoc.y += cptMove.pt3dNew.y;
      pt3dLoc.z += cptMove.pt3dNew.z;

      /*  Calculate the new points.  */

      CalcNewCranePts( );

      /*  Compute and erase the update rectangle.  */

      UnionRect( &rUpdate, &rErase, &rOldErase );

      rOldErase = rErase;

      /*  Convert the update rectangle to device units.  */

      LPtoDP( hClientDC, (LPPOINT) &rUpdate, 2 );

      rUpdate.left   -= 2;
      rUpdate.top    -= 2;
      rUpdate.right  += 2;
      rUpdate.bottom += 2;

      InvalidateRect( hWnd, &rUpdate, TRUE );

      /*  Convert back to logical units.  */

      DPtoLP( hClientDC, (LPPOINT) &rUpdate, 2 );

      /*  If recording then write the data.  */

      if ( bRecording )
         RecordWrite( );

      /*  Update the dialog.  */

      if ( hXYZDataDlg )
         UpdateXYZDataDlg( );

      /*  Yield.  */

      myYield( );

   }

   return;

}

/******************************************************************************

   Function Name: CalcNewCranePts

   Description:

      Calculate the new crane points.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL CalcNewCranePts( void )

{
   /*  Variable definition.  */

   WORD           unI;                       /* Indexing variable */

   /*  Recalculate the points.  */

   for ( unI = 0; acptList[ unI ].unFlags != CPF_EOL; ++unI )
   {
      CalcNewPt( &acptList[ unI ] );

      aptList[ unI ].x = (int) acptList[ unI ].pt3dNew.x;
      aptList[ unI ].y = (int) acptList[ unI ].pt3dNew.y;

      if ( aptList[ unI ].x < rErase.left )
         rErase.left = aptList[ unI ].x;

      if ( aptList[ unI ].y < rErase.top )
         rErase.top = aptList[ unI ].y;

      if ( aptList[ unI ].x > rErase.right )
         rErase.right = aptList[ unI ].x;

      if ( aptList[ unI ].y > rErase.bottom )
         rErase.bottom = aptList[ unI ].y;

   }

   return;

}

/******************************************************************************

   Function Name: CalcNewPt

   Description:

      Calculates the new point.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL CalcNewPt(

   LPCRANEPT lpcpt      /* Crane point pointer.
                        */

   )

{
   /*  Variable definition.  */

   double         dfRot,                     /* Rotation */
                  dfDst;                     /* Distance */

   /*  Reset the point.  */

   lpcpt->pt3dNew = lpcpt->pt3dOrg;

   /*  Perform Z rotation.  */

   if ( lpcpt->pt3dNew.x != 0. || lpcpt->pt3dNew.y != 0. )
   {
      dfRot = atan2( lpcpt->pt3dNew.y, lpcpt->pt3dNew.x ) + pt3dRot.z;

      dfDst = sqrt( lpcpt->pt3dNew.x * lpcpt->pt3dNew.x +
                    lpcpt->pt3dNew.y * lpcpt->pt3dNew.y
                  );

      lpcpt->pt3dNew.x = ( cos( dfRot ) * dfDst );
      lpcpt->pt3dNew.y = ( sin( dfRot ) * dfDst );

   }

   /*  Perform X rotation.  */

   if ( lpcpt->pt3dNew.z != 0. || lpcpt->pt3dNew.y != 0. )
   {
      dfRot = atan2( lpcpt->pt3dNew.y, lpcpt->pt3dNew.z ) + pt3dRot.x;

      dfDst = sqrt( lpcpt->pt3dNew.z * lpcpt->pt3dNew.z +
                    lpcpt->pt3dNew.y * lpcpt->pt3dNew.y
                  );

      lpcpt->pt3dNew.z = ( cos( dfRot ) * dfDst );
      lpcpt->pt3dNew.y = ( sin( dfRot ) * dfDst );

   }

   /*  Perform Y rotation.  */

   if ( lpcpt->pt3dNew.x != 0. || lpcpt->pt3dNew.z != 0. )
   {
      dfRot = atan2( lpcpt->pt3dNew.z, lpcpt->pt3dNew.x ) + pt3dRot.y;

      dfDst = sqrt( lpcpt->pt3dNew.x * lpcpt->pt3dNew.x +
                    lpcpt->pt3dNew.z * lpcpt->pt3dNew.z
                  );

      lpcpt->pt3dNew.x = ( cos( dfRot ) * dfDst );
      lpcpt->pt3dNew.z = ( sin( dfRot ) * dfDst );

   }

   /*  Exit if move point.  */

   if ( lpcpt->unFlags == CPF_MOVE )
      return;

   /*  Adjust relative location and size in space.  */

   lpcpt->pt3dNew.x += pt3dLoc.x;
   lpcpt->pt3dNew.y += pt3dLoc.y;
   lpcpt->pt3dNew.z += pt3dLoc.z;

   if ( lpcpt->pt3dNew.x != 0. || lpcpt->pt3dNew.y != 0. )
   {
      dfRot = atan2( lpcpt->pt3dNew.y, lpcpt->pt3dNew.x );

      dfDst = sqrt( lpcpt->pt3dNew.x * lpcpt->pt3dNew.x +
                    lpcpt->pt3dNew.y * lpcpt->pt3dNew.y
                  );

      dfDst *= ( lpcpt->pt3dNew.z - ZSZ_ATMINSIZ ) / ZSZ_ATMAXSIZ;

      if ( dfDst < 0. )
         dfDst = 0.;

      lpcpt->pt3dNew.x = ( cos( dfRot ) * dfDst );
      lpcpt->pt3dNew.y = ( sin( dfRot ) * dfDst );

   }

   return;

}

/******************************************************************************

   Function Name: RecordPlayback

   Description:

      Plays back the recording.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL RecordPlayback( 

   HWND hWnd            /* Window handle.
                        */

   )
      
{
   /*  Variable definition.  */

   RECT           rClient;                   /* Client rectangle */

   HBRUSH         hEraseBrush;               /* Brush handle */

   WORD           unC,                       /* Counting variable */
                  unI;                       /* Indexing variable */

   /*  Erase the background.  */

   hEraseBrush = GetStockObject( WHITE_BRUSH );

   GetClientRect( hWnd, &rClient );

   DPtoLP( hClientDC, (LPPOINT) &rClient, 2 );

   FillRect( hClientDC, &rClient, hEraseBrush );

   /*  Playback the records.  */

   _llseek( fhRecordFile, 0L, 0 );

   for ( unC = 0; unC < unRecordCnt; ++unC )
   {
      /*  Read the next set of points.  */

      RecordRead( );

      /*  Perform our painting.  */

      FillRect( hClientDC, &rUpdate, hEraseBrush );

      for ( unI = 0; unI < MAX_SHAPES; ++unI )
         Polyline( hClientDC,
                   &aptList[ ashpList[ unI ].unIndex ],
                   ashpList[ unI ].unCnt
                 );

   }

   return;

}

/******************************************************************************

   Function Name: RecordRead

   Description:

      Reads one set of points from the file.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL RecordRead( void )

{
   /*  Read the points in.  */

   _lread( fhRecordFile, (LPSTR) &rUpdate, sizeof ( RECT ) );
   _lread( fhRecordFile, (LPSTR) aptList, sizeof ( POINT ) * MAX_POINTS );

   return;

}

/******************************************************************************

   Function Name: RecordWrite

   Description:

      Writes one set of points to the file.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL RecordWrite( void )

{
   /*  Read the points in.  */

   _lwrite( fhRecordFile, (LPSTR) &rUpdate, sizeof ( RECT ) );
   _lwrite( fhRecordFile, (LPSTR) aptList, sizeof ( POINT ) * MAX_POINTS );

   ++unRecordCnt;

   return;

}

/******************************************************************************

   Function Name: Key

   Description:

      Provides processing for key events.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL Key( 

   HWND hWnd,           /* Window handle.
                        */

   WORD wId             /* Key ID.
                        */

   )
      
{
   /*  Parse the key pressed and adjust the appropiate rotation amount.  */

   switch ( wId )
   {
      case VK_NUMPAD2:

         pt3dRot.x -= ROT_INC;

         if ( pt3dRot.x <= ROT_MIN )
            pt3dRot.x = ROT_MAX;

         break;

      case VK_NUMPAD8:

         pt3dRot.x += ROT_INC;

         if ( pt3dRot.x >= ROT_MAX )
            pt3dRot.x = ROT_MIN;

         break;

      case VK_NUMPAD4:

         pt3dRot.y -= ROT_INC;

         if ( pt3dRot.y <= ROT_MIN )
            pt3dRot.y = ROT_MAX;

         break;

      case VK_NUMPAD6:

         pt3dRot.y += ROT_INC;

         if ( pt3dRot.y >= ROT_MAX )
            pt3dRot.y = ROT_MIN;

         break;

      case VK_NUMPAD1:

         pt3dRot.z -= ROT_INC;

         if ( pt3dRot.z <= ROT_MIN )
            pt3dRot.z = ROT_MAX;

         break;

      case VK_NUMPAD9:

         pt3dRot.z += ROT_INC;

         if ( pt3dRot.z >= ROT_MAX )
            pt3dRot.z = ROT_MIN;

         break;

      default:

         return;
         break;

   }

   /*  If not animating then calculate the new points and paint them.  */

   if ( !bAnimate )
   {
      CalcNewCranePts( );
      InvalidateRect( hWnd, NULL, TRUE );
   }

   return;

}

/******************************************************************************

   Function Name: Size

   Description:

      Performs processing for re-sizing of the client window.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL Size(

   HWND hWnd,           /* Window handle.
                        */

   LONG lParam          /* 32-bit parameter.
                        */

   )

{
   /*  Variable definition.  */

   POINT          ptExt,                     /* Extents */
                  ptOrg;                     /* Origin */

   /*  Get the new size and update the mapping.  */

   ptExt = MAKEPOINT( lParam );

   if ( ptExt.x < ptExt.y )
   {
      ptOrg.x = 0;
      ptOrg.y = ( ptExt.y - ptExt.x ) / 2;
      ptExt.y = ptExt.x;
   }
   else
   {
      ptOrg.x = ( ptExt.x - ptExt.y ) / 2;
      ptOrg.y = 0;
      ptExt.x = ptExt.y;
   }

   SetViewportOrg( hClientDC, ptOrg.x, ptOrg.y );
   SetViewportExt( hClientDC, ptExt.x, ptExt.y );

   InvalidateRect( hWnd, NULL, TRUE );

   return;

}

/******************************************************************************

   Function Name: Paint

   Description:

      Performs updates on the client area as requested by window messages.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL Paint( 

   HWND hWnd            /* Window handle.
                        */

   )

{
   /*  Variable definition.  */

   PAINTSTRUCT    stPS;                      /* Painting structure */

   WORD           unI;                       /* Indexing variable */

   /*  Prepare the client area for painting.  */

   BeginPaint( hWnd, &stPS );

   /*  Perform our painting.  */

   for ( unI = 0; unI < MAX_SHAPES; ++unI )
      Polyline( hClientDC,
                &aptList[ ashpList[ unI ].unIndex ],
                ashpList[ unI ].unCnt
              );

   /*  We're done so exit.  */

   EndPaint( hWnd, &stPS );

   return;

}

/******************************************************************************

   Function Name: ResetCranePts

   Description:

      Resets the crane points back to the origin.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL ResetCranePts( void )
      
{
   /*  Reset the points.  */

   pt3dRot.x = 0.;
   pt3dRot.y = 0.;
   pt3dRot.z = 0.;

   pt3dLoc.x = 0.;
   pt3dLoc.y = 0.;
   pt3dLoc.z = 0.;

   CalcNewCranePts( );

   return;

}

/******************************************************************************

   Function Name: myYield

   Description:

      Uses PeekMessage to yield control.

   Control Flow:

   Return Values:

      None.

   Notes:

******************************************************************************/

void NEAR PASCAL myYield( void )

{
   /*  Variable definition.  */

   MSG            stMsg;                     /* Buffer to hold message */

   /*  Process messages until exit.  */

   while ( PeekMessage( &stMsg, NULL, 0, 0, PM_REMOVE ) )
   {
      TranslateMessage( &stMsg );
      DispatchMessage( &stMsg );
   }

   return;

}
