
/*

PolyCSG 009
-----------

  . Added TranslatePoly class
  . Fixed memory leak in Redraw() - was not deleting all Poly's

  > Interesting poly shapes

Motion 001
----------

  . Moved classes to Classes folder

RTRay 010
---------

  . Added more detailed frame rate indicators

Animation 001
-------------

  . Renders scrolling array of circles with pulsing wave

*/



#include <windows.h>
#include <mmsystem.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>



//--- CONSTANTS --------------------------------------------------------------//

//- Settings

#define FULL_WINDOW_DRAG
#define ROUND_IMAGE_SIZE



//---- CLASS DECLARATIONS ----------------------------------------------------//

class Image;
class Scanline;
class Poly;
class Raytrace;

#include "Classes\Image.h"
#include "Classes\Scanline.h"
#include "Classes\Poly.h"
#include "Classes\Raytrace.h"

  class XORPoly;
  class ANDPoly;
  class ORPoly;
  class SUBPoly;

  #include "Classes\Poly\XORPoly.h"
  #include "Classes\Poly\ANDPoly.h"
  #include "Classes\Poly\ORPoly.h"
  #include "Classes\Poly\SUBPoly.h"

  class TranslatePoly;
  class RotatePoly;

  class RegularPoly;
  //class StarPoly;

  #include "Classes\Poly\TranslatePoly.h"
  #include "Classes\Poly\RotatePoly.h"

  #include "Classes\Poly\RegularPoly.h"
  //#include "Classes\Poly\StarPoly.h"



//--- DATA -------------------------------------------------------------------//

//---- Windows data

HINSTANCE hMainInstance;
HWND hMainWindow = NULL;

char szAppName[40] = "RTRay 010";

int mouse_x = 0, mouse_y = 0;
int mouse_xstart = 0, mouse_ystart = 0;

bool mouse_drag = false;

float rx=0, ry=0, rrx=-1, rry=-1;



//---- Image buffer data

int width = 480;
int height = 360;

Image *im;



//---- Inputs

int key[256] = { false };



//---- Timer data

float time = 0;
float lastTime = 0;
float startTime = 0;



//---- USER CLASSES ----------------------------------------------------------//

class Speedo
{
private:

  float lastTime;
  float myTime;
  bool  first;

public:

  Speedo( )
  {
    first = true;
  }

  float getValue( float time, float speed )
  {
    if( first )
    {
      lastTime = myTime = time;
      first = false;
    }
    else
    {
      float d = time-lastTime;
      myTime += d * speed;
      lastTime = time;
    }

    return myTime;
  }
};


typedef void CIRCLES_DRAW_FN(Image*,float,float,float);


class Circles
{
private:

  Speedo * speedo1;

public:

  Circles( )
  {
    speedo1 = new Speedo();
  }

  ~Circles( )
  {
    delete speedo1;
  }

  void redraw( float time, Image *_im, CIRCLES_DRAW_FN fnDraw )
  {
    int i, j;

    //  We want the time to go up by 1 each second
    //  0.25*a + 0.75*b = 1
    //  a = ( 1 - 0.75*b )/0.25

    float speed = 0.5;

//    if( (time-(int)time) < 0.25 ) speed = 2.5;
    if( key[ VK_SPACE ] ) speed = 2.5;

    float t2 = speedo1->getValue(time, speed*0.5)-0.25;

    float offsetX = time / 11.f;
    float offsetY = time / 9.f;

    offsetX = 120.f * ( offsetX-(int)offsetX );
    offsetY = 120.f * ( offsetY-(int)offsetY );

    for(j=0; j<4; j++)
    {
      for(i=0; i<5; i++)
      {
        float x = 60+120*i - offsetX;
        float y = 60+120*j - offsetY;
        float rad = 30.0f + 20.0f*sinf( 3.14159f*t2 - x*(0.6912f/120.f) - y*(0.7997f/120.f));

        fnDraw( im, x, y, rad );
      }
    }

  }
};



//---- USER DATA -------------------------------------------------------------//

Circles *circles1;



//---- USER FUNCTIONS --------------------------------------------------------//

void drawTarget( Image* im, float x, float y, float rad )
{
  int col = 0x000000;

  for(int k=0; k<6 && rad > 0; k++)
  {
    col += 0x010203 * 0x07;
    im->fillCircleAA( x, y, rad, col );
    rad -= 10;
  }
}

void drawCircle( Image* im, float x, float y, float rad )
{
  im->fillCircleAA( x, y, rad, 0x445566 );
}



//---- Display ---------------------------------------------------------------//

void Display(HDC dc, int *buf, int w, int h)
{
  BITMAPINFO bmi;

  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bmi.bmiHeader.biWidth = w;
  bmi.bmiHeader.biHeight = -h;
  bmi.bmiHeader.biPlanes = 1;
  bmi.bmiHeader.biBitCount = 32;
  bmi.bmiHeader.biCompression = BI_RGB;
  bmi.bmiHeader.biSizeImage = 0;
  bmi.bmiHeader.biXPelsPerMeter = 1000;
  bmi.bmiHeader.biYPelsPerMeter = 1000;
  bmi.bmiHeader.biClrUsed = 0;
  bmi.bmiHeader.biClrImportant = 0;

  StretchDIBits(
    dc,
    0, 0, w, h,
    0, 0, w, h,
    (LPVOID)buf,
    (LPBITMAPINFO)&bmi,
    DIB_RGB_COLORS, SRCCOPY
  );

}



//---- Startup ---------------------------------------------------------------//

void Startup()
{
  //- Allocate image

  #ifdef ROUND_IMAGE_SIZE

  width = (width+7) & -8;
  height = (height+7) & -8;

  #endif

  im = new Image( width, height );

  startTime = timeGetTime()*0.001;

  //---- USER STARTUP CODE

  circles1 = new Circles();
}



//---- Shutdown --------------------------------------------------------------//

void Shutdown()
{
  //---- USER SHUTDOWN CODE

  delete circles1;
}



//---- Redraw ----------------------------------------------------------------//

void Redraw(HDC dc)
{
  static int frame = 0;

  time = timeGetTime() * 0.001 - startTime;
//  time += 0.05f;

  float fps = time > lastTime ? (1.0 / (time-lastTime)) : 0.0;
  lastTime = time;


  
  //---- USER REDRAW CODE

  im->clear( 0x000000 );

//  circles1->redraw( time, im, drawTarget );

  //---- end of USER REDRAW CODE


  //  used by beat matcher and frame rate indicator as a string buffer

  char s[ 1024 ];



  //---- Beat matcher based on space bar


  static bool lastSpace = false;
  static int numSpaces = 0;

  static double startTime = -1.0;

  static double bps = 0;

  double timeDiff = 0;

  if( key[ 32 ] && !lastSpace )  //  Space pressed and wasn't last time
  {
    lastSpace = true;

    if( startTime < 0 )  //  First time in
    {
      startTime = time;
    }
    else
    {
      timeDiff = time-startTime;
      numSpaces ++;

      bps = timeDiff / (0.0+numSpaces);
    }
  }
  else if( ! key[ 32 ] )
  {
    lastSpace = false;
  }

  //  Display

  if( numSpaces < 1 )
  {
    sprintf( s, "Not enough data to do beat matching" );
    im->drawText( 9, 20, s, 0x00FF00 );
  }
  else
  {
    sprintf( s, "Number of beats: %6d       Time since matching started: %0.5f", numSpaces, time-startTime );
    im->drawText( 9, 20, s, 0x99FF00 );

    sprintf( s, "Beats per second: %0.10f      Seconds per beat:%0.10f", 1.0/bps, bps );
    im->drawText( 9, 30, s, 0x99FF00 );
  }



  //---- Frame rate indicator

  frame ++;

  int f_i, f_j;

  for(f_j=0; f_j<8; f_j++) for(f_i=0; f_i<8; f_i++)
  {
    im->buf[f_j*width+f_i] = ((-(((f_i+f_j+frame)>>2)&1)) ^ 0xFFFF00 );
  }

  //  FPS based on current frame

  static float times[10] ={0,0,0,0,0,0,0,0,0,0};
  static int timeptr = 0;

  float time10 = time-times[timeptr];

  times[timeptr] = time;
  timeptr = (timeptr+1)%10;

  sprintf( s, "f:%08d ps:%020.10f ea:%012.8f ra:%020.10f", frame, fps, 0.1*time10, 10.0f / time10 );
  im->drawText( 9, 1, s, 0xFFFFFF );

  //- Display

  Display(dc, im->buf, width, height);
}



//---- WinProc ---------------------------------------------------------------//

LRESULT CALLBACK WinProc(HWND hwnd, UINT uiMsg, UINT wParam, LONG lParam)
{
  HDC dc;
  PAINTSTRUCT ps;

  switch(uiMsg) {

  case WM_CREATE:
    Startup();
    SetTimer(hwnd, 1, 10, NULL);
    break;

  case WM_TIMER:
    dc = GetDC(hwnd);
    Redraw(dc);
    ReleaseDC(hwnd, dc);
    return 0L;

  case WM_PAINT:
    dc = BeginPaint(hwnd, &ps);
    Redraw(dc);
    EndPaint(hwnd, &ps);
    return 0L;

  case WM_LBUTTONDOWN:
    mouse_xstart = HIWORD(lParam);
    mouse_ystart = LOWORD(lParam);
    mouse_drag = true;
    SetCapture(hwnd);
    return 0L;

  case WM_MOUSEMOVE:
    mouse_x = HIWORD(lParam);
    mouse_y = LOWORD(lParam);

    if(mouse_drag)
    {
      ry -= (mouse_y-mouse_ystart) * 0.004;
      rx += (mouse_x-mouse_xstart) * 0.004;
      mouse_xstart = mouse_x;
      mouse_ystart = mouse_y;
    }
    return 0L;

  case WM_LBUTTONUP:
    mouse_drag = false;
    ReleaseCapture();
    return 0L;

  #ifdef FULL_WINDOW_DRAG

  case WM_NCHITTEST:
    return HTCAPTION;

  #endif

  case WM_KEYDOWN:
    if( wParam == VK_ESCAPE ) DestroyWindow( hwnd );
    if( wParam >= 0 && wParam < 256) key[ wParam] = 1;
    return 0L;

  case WM_KEYUP:
    if( wParam >= 0 && wParam < 256) key[ wParam] = 0;
    return 0L;

  case WM_DESTROY:
	  Shutdown();
    KillTimer(hwnd, 1);
    PostQuitMessage(0);
    return 0L;

  }

  return DefWindowProc(hwnd, uiMsg, wParam, lParam);
}




//---- WinMain ---------------------------------------------------------------//

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASS wndclass;
  char cSwitch;
  MSG msg;
  RECT rc;

  hMainInstance = hInstance;

  if(hPrevInstance) return 1;

  //- Create window class

  wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  wndclass.lpfnWndProc   = WinProc;
  wndclass.cbClsExtra    = 0;
  wndclass.cbWndExtra    = 0;
  wndclass.hInstance     = hInstance;
  wndclass.hIcon         = NULL;
  wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
  wndclass.lpszMenuName  = NULL;
  wndclass.lpszClassName = szAppName;

  if(!RegisterClass(&wndclass)) return 1;

  //- Create main window

  GetWindowRect(GetDesktopWindow(), &rc);

  hMainWindow = CreateWindowEx(
    0,
    szAppName,
    szAppName,
    WS_POPUP,
    (rc.right-width)/2,
    (rc.bottom-height)/2,
    width,
    height,
    NULL,
    NULL,
    hInstance,
    NULL
  );

  if(!hMainWindow) return 1;

  ShowWindow(hMainWindow, SW_SHOW);

  //- Message loop

  while(TRUE) {
    while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
      if (msg.message == WM_QUIT) return TRUE;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    HDC dc = GetDC(hMainWindow);
    Redraw(dc);
    ReleaseDC(hMainWindow, dc);
  }

  return 0;
}



//---- END OF FILE -----------------------------------------------------------//




