#include<iostream>
#include<cstdlib>
#include<fstream>
#include<cmath>

using namespace std;

class PostScript :  ofstream {
     private:
       double Zp, Zo;
       double R[4][4];
     public:
       PostScript(const char nombre[]);
      ~PostScript(void);
       void moveto(double x, double y);
       void lineto(double x, double y);
       void perspectiva(double zo, double zp);
       void moveto(double x, double y, double z);
       void lineto(double x, double y, double z);
       void rotacion(char eje, double ang);
       void transf(double x, double y, double z,
                   double& xp, double& yp, double& zp);
};
  
PostScript::PostScript(const char nombre[]) : ofstream(nombre)
{
    *this << "%!PS\n"
             "72 8.5 2 div mul\n" 
             "72 11  2 div mul\n"
             "translate\n"
             "72 2.54 div dup scale\n"
             "0.01 setlinewidth\n";
    for (int i=0; i<4; i++)
      for (int j=0; j<4; j++)
        R[i][j]= i==j ? 1 : 0;
    
}

void PostScript::rotacion(char eje, double ang)
{
    double m[4][4];
    double t[4][4];

    ang*= M_PI/180.0;

    switch (eje)
      {
       case 'x': case 'X':
          m[0][0]=   1;  m[0][1]= 0;         m[0][2]= 0; 
          m[1][0]=   0;  m[1][1]= cos(ang);  m[1][2]= -sin(ang);
          m[2][0]=   0;  m[2][1]= sin(ang);  m[2][2]=  cos(ang);
          break;

       case 'y': case 'Y':
          m[0][0]= cos(ang);  m[0][1]= 0;  m[0][2]= -sin(ang); 
          m[1][0]=        0;  m[1][1]= 1;  m[1][2]=         0;
          m[2][0]= sin(ang);  m[2][1]= 0;  m[2][2]=  cos(ang);
          break;

       case 'z': case 'Z':
          m[0][0]= cos(ang);  m[0][1]=-sin(ang);  m[0][2]=  0; 
          m[1][0]= sin(ang);  m[1][1]= cos(ang);  m[1][2]=  0;
          m[2][0]=        0;  m[2][1]= 0;         m[2][2]=  1;
          break;
      }

    for (int i=0; i<3; i++)
       for (int j=0; j<3; j++)
         {
          double s=0;
          for (int k=0; k<3; k++)
             s+= R[i][k] * m[k][j];
          t[i][j]= s;
         }

    for (int i=0; i<3; i++)
       for (int j=0; j<3; j++)
        R[i][j]= t[i][j]; 
}



void PostScript::transf(double x, double y, double z,
                double& xp, double& yp, double& zp)
{
    xp= R[0][0] * x + R[0][1] * y + R[0][2] * z; 
    yp= R[1][0] * x + R[1][1] * y + R[1][2] * z; 
    zp= R[2][0] * x + R[2][1] * y + R[2][2] * z; 
}



PostScript::~PostScript(void)
{
    *this << "stroke\n"
             "showpage\n";
    close();
}

void PostScript::moveto(double x, double y)
{
    *this << x << " " << y << " moveto\n";
}

void PostScript::lineto(double x, double y)
{
    *this << x << " " << y << " lineto\n";
}

void PostScript::perspectiva(double zo, double zp)
{
    Zo= zo;
    Zp= zp;
}

void PostScript::moveto(double x, double y, double z)
{
   transf(x,y,z, x,y,z);
   double xp= (Zo-Zp) / (Zo-z)  * x;
   double yp= (Zo-Zp) / (Zo-z)  * y;
   moveto(xp, yp);
}

void PostScript::lineto(double x, double y, double z)
{
   transf(x,y,z, x,y,z);
   double xp= (Zo-Zp) / (Zo-z)  * x;
   double yp= (Zo-Zp) / (Zo-z)  * y;
   lineto(xp, yp);
}

int main(int nargs, char* args[])
{
    PostScript p("cubo.ps");
    p.perspectiva( 60, 40 );   // observador a 60 cm y plano a 40
    p.rotacion('y', 30);
    p.rotacion('x', 30);

    double L=5;
    p.moveto(-L, L,-L);
    p.lineto(-L, L, L);
    p.lineto( L, L, L);
    p.lineto( L, L,-L);
    p.lineto(-L, L,-L);

    p.moveto(-L,-L,-L);
    p.lineto(-L,-L, L);
    p.lineto( L,-L, L);
    p.lineto( L,-L,-L);
    p.lineto(-L,-L,-L);

    p.moveto(-L, L,-L);
    p.lineto(-L,-L,-L);

    p.moveto(-L, L, L);
    p.lineto(-L,-L, L);

    p.moveto( L, L,-L);
    p.lineto( L,-L,-L);

    p.moveto( L, L, L);
    p.lineto( L,-L, L);

    return EXIT_SUCCESS;


}
