//------------------------------------------------------------------------------
//--  Plot3d.cpp - Plotting module for MegaTune.                              --
//--                                                                          --
//--  Copyright (c) 2003 by Eric Fahlgren, All Rights Reserved.               --
//--                                                                          --
//--  This program is free software; you can redistribute it and/or           --
//--  modify it under the terms of the GNU General Public License             --
//--  as published by the Free Software Foundation; either version            --
//--  2 of the License, or (at your option) any later version.                --
//--  See http://www.gnu.org/licenses/gpl.txt                                 --
//------------------------------------------------------------------------------

#include "stdafx.h"
#include <math.h>
#include "plot3d.h"

//------------------------------------------------------------------------------

void Point3::xform(M33 &m)
{
   // Destroys the original point.
   double x0 = x, y0 = y, z0 = z;

   x = x0 * m[0][0] + y0 * m[1][0] + z0 * m[2][0];
   y = x0 * m[0][1] + y0 * m[1][1] + z0 * m[2][1];
   z = x0 * m[0][2] + y0 * m[1][2] + z0 * m[2][2];
}

double Point3::mag()
{
   return sqrt(x*x + y*y + z*z);
}

//------------------------------------------------------------------------------

void M33::rot(double a1, double a2, double a3)
{
   // Spacecraft Dynamics by Thomas R. Kane, ASIN 0070378436
   // McGraw Hill College Division, Jan, 1983
   double s1 = sin(a1), c1 = cos(a1);
   double s2 = sin(a2), c2 = cos(a2);
   double s3 = sin(a3), c3 = cos(a3);

   // Space 123, easy on the brain for this app.
   m[0][0] =  c2 * c3;
   m[0][1] =  s1 * s2 * c3 - s3 * c1;
   m[0][2] =  c1 * s2 * c3 + s3 * s1 ;
   m[1][0] =  c2 * s3;
   m[1][1] =  s1 * s2 * s3 + c3 * c1;
   m[1][2] =  c1 * s2 * s3 - c3 * s1;
   m[2][0] = -s2;
   m[2][1] =  s1 * c2;
   m[2][2] =  c1 * c2;

   /* Body 313, standard Euler angles...
   m[0][0] =  c3 * c1      - s3 * s1 * c2;
   m[0][1] = -s3 * c1      - c3 * s1 * c2;
   m[0][2] =  s2 * s1;
   m[1][0] =  c3 * s1      + s3 * c1 * c2;
   m[1][1] =  c3 * c1 * c2 - s3 * s1;
   m[1][2] = -s2 * c1;
   m[2][0] =  s2 * s3;
   m[2][1] =  s2 * c3;
   m[2][2] =  c2;
   */
}

//------------------------------------------------------------------------------

void plot3d::rotate(Point3 &p3)
{
   p3 = p3 - center;
   p3.x /= scale.x;
   p3.y /= scale.y;
   p3.z /= scale.z;
   p3.xform(X);
   printf("%f %f %f\n", p3.x, p3.y, p3.z);
   //p3 = p3 + center;
}

POINT plot3d::world2screen(Point3 p3)
{
   rotate(p3);
   POINT p = {
      long(slopeX * (p3.x - minWorld) + double(minScreenX)),
      long(slopeY * (p3.y - minWorld) + double(minScreenY))
   };
   p.x = min(max(p.x, minScreenX), maxScreenX);
   p.y = min(max(p.y, minScreenY), maxScreenY);
   return p;
}

//------------------------------------------------------------------------------

void plot3d::recalcSettings()
{
   center = (corner1+corner2) / 2.0;
   scale  = (corner2-corner1) / 2.0;

//   Point3 c1 = corner1; rotate(c1);
//   Point3 c2 = corner2; rotate(c2);
   minWorld = -1.4; // sqrt(2), really. //min(min(c1.x, c2.x), min(c1.y, c2.y));
   maxWorld =  1.4; //max(max(c1.x, c2.x), max(c1.y, c2.y));

   slopeX = double(maxScreenX-minScreenX) / (maxWorld-minWorld);
   slopeY = double(maxScreenY-minScreenY) / (maxWorld-minWorld);
}

void plot3d::setWorldCoordinates(Point3 c1, Point3 c2)
{
   corner1 = c1;
   corner2 = c2;
   recalcSettings();
}

void plot3d::setDeviceCoordinates(int x1, int y1, int x2, int y2)
{
   minScreenX = x1; maxScreenX = x2;
   minScreenY = y1; maxScreenY = y2;
   recalcSettings();
}

void plot3d::setRotation(double psi, double theta, double phi)
{
   X.rot(psi, theta, phi);
   recalcSettings();
}

//------------------------------------------------------------------------------

static CBrush brushblk(RGB(0, 0, 0));

void plot3d::clear()
{
   CRect plot(minScreenX, minScreenY, maxScreenX, maxScreenY);
   plot.DeflateRect(4, 4);
   CDC *dc = hWnd->GetDC();
      oldSpot.x = -1;
      oldCursor.x = -1;

      dc->FillRect(plot, &brushblk);
   hWnd->ReleaseDC(dc);

}

//------------------------------------------------------------------------------

static CPen pline(PS_SOLID | PS_ENDCAP_ROUND, 1, RGB(150, 150, 150));

void plot3d::drawLineSegment(Point3 p1, Point3 p2)
{
   POINT pp1 = world2screen(p1);
   POINT pp2 = world2screen(p2);
   CDC *dc = hWnd->GetDC();
      dc->SelectObject(&pline);
      dc->MoveTo(pp1);
      dc->LineTo(pp2);
   hWnd->ReleaseDC(dc);
}

void plot3d::drawLineSegment8(Point3 p3[8])
{
   POINT p2[8];
   for (int i = 0; i < 8; i++) {
      p2[i] = world2screen(p3[i]);
   }
   CDC *dc = hWnd->GetDC();
      dc->SelectObject(&pline);
      dc->Polyline(p2, 8);
   hWnd->ReleaseDC(dc);
}

//------------------------------------------------------------------------------

static CPen pspot(PS_SOLID, 2, RGB(0, 255, 0));

void plot3d::spot(CDC *dc, POINT &p)
{
   enum { size = 5 };
   dc->Ellipse(p.x - size, p.y - size, p.x + size, p.y + size);
}

void plot3d::drawSpot(Point3 p)
{
   POINT Spot = world2screen(p);

   if (Spot.x != oldSpot.x || Spot.y != oldSpot.y) {
      CDC *dc = hWnd->GetDC();
         int nOldMode = dc->GetROP2();
         dc->SetROP2(R2_XORPEN);
      
         dc->SelectObject(&pspot);
         if (oldSpot.x != -1) {
            spot(dc, oldSpot);
         }
         spot(dc, Spot);
         dc->SetROP2(nOldMode);
      hWnd->ReleaseDC(dc);
      oldSpot = Spot;
   }
}

//------------------------------------------------------------------------------

static CPen pcursor(PS_SOLID, 3, RGB(255, 0, 0));

void plot3d::cursor(CDC *dc, POINT &p)
{
   enum { sizei = 2, sizeo = 6 };
   dc->MoveTo(p.x-sizei, p.y-sizei); dc->LineTo(p.x-sizeo, p.y-sizeo);
   dc->MoveTo(p.x-sizei, p.y+sizei); dc->LineTo(p.x-sizeo, p.y+sizeo);
   dc->MoveTo(p.x+sizei, p.y-sizei); dc->LineTo(p.x+sizeo, p.y-sizeo);
   dc->MoveTo(p.x+sizei, p.y+sizei); dc->LineTo(p.x+sizeo, p.y+sizeo);
}

void plot3d::drawCursor(Point3 p)
{
   POINT Cursor = world2screen(p);

   if (Cursor.x != oldCursor.x || Cursor.y != oldCursor.y) {
      CDC *dc = hWnd->GetDC();
         int nOldMode = dc->GetROP2();
         dc->SetROP2(R2_XORPEN);

         dc->SelectObject(&pcursor);

         if (oldCursor.x != -1) { // Erase the old one.
			cursor(dc, oldCursor);
         }

         cursor(dc, Cursor);

         dc->SetROP2(nOldMode);
      hWnd->ReleaseDC(dc);
      oldCursor = Cursor;
   }
}

//------------------------------------------------------------------------------

