
#include "Models.h"
#include <GL/glut.h>
#include <fstream>
using namespace std;

typedef Transform *TransformPtr;
typedef Object *ObjectPtr;
static bool lightIsUsed[8] = { 0,0,0,0,0,0,0,0 };
static const int lightName[8] = { GL_LIGHT0, GL_LIGHT1, GL_LIGHT2,
         GL_LIGHT3, GL_LIGHT4, GL_LIGHT5, GL_LIGHT6, GL_LIGHT7 };
inline static float clamp(float x) {
   if (x > 1)
      return 1;
   else if (x < 0)
      return 0;
   else
      return x;
}



View::View(Object *world, Projection projectionType) {
   object = world;
   projType = projectionType;
   keepAspect = true;
   standardDefaults = true;
   background = 0;
   xmin = -5;
   xmax = 5;
   ymin = -5;
   ymax = 5;
   zmin = -10;
   zmax = 10;
   viewXmin = 0;
   viewYmin = 0;
   viewXmax = 1;
   viewYmax = 1;
   eyex = eyey = 0;
   eyez = 20;
   upx = upz = 0;
   upy = 1;
   refx = refy = refz = 0;
}

View::~View() {
   if (background)
      delete[] background;
}

void View::setWorld(Object *world) {
   object = world;
}

void View::setProjectionType(Projection proj) {
   projType = proj;
}

Projection View::getProjectionType() {
   return projType;
}

void View::setViewVolume(float left, float right, float bottom,
                         float top, float far, float near) {
   xmin = left;
   xmax = right;
   ymin = bottom;
   ymax = top;
   zmin = far;
   zmax = near;
}

void View::setNDCViewport(float xstart, float xend, float ystart, float yend) {
   viewXmin = xstart;
   viewXmax = xend;
   viewYmin = ystart;
   viewYmax = yend;
}

void View::setBackground(float red, float green, float blue) {
   if (!background)
      background = new float[3];
   background[0] = clamp(red);
   background[1] = clamp(green);
   background[2] = clamp(blue);
}

void View::setKeepAspectRatio(bool keep) {
  keepAspect = keep;
}

bool View::getKeepAspectRatio() {
   return keepAspect;
}

void View::setUseStandardDefaults(bool useStandardDefaults) {
   standardDefaults = useStandardDefaults;
}

bool View::getUseStandardDefaults() {
   return standardDefaults;
}

void View::setViewEye(float x, float y, float z) {
   eyex = x;
   eyey = y;
   eyez = z;
}


void View::setViewCenter(float x, float y, float z) {
   refx = x;
   refy = y;
   refz = z;
}


void View::setViewUp(float dx, float dy, float dz) {
   upx = dx;
   upy = dy;
   upz = dz;
}


void View::render(){
   if (!object)
      return;
   glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT);
   if (standardDefaults) {
      float color[4] = { 0.6, 0.6, 0.6, 1 };
      float emissive[4] = { 0, 0, 0, 1 };
      float specular[4] = { 0.4, 0.4, 0.4, 1 };
      glColor4fv(color);     // Default drawing color.
      glEnable(GL_DEPTH_TEST);
      glEnable(GL_NORMALIZE);
      glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,color);
      glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,emissive);
      glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular);
      glMateriali(GL_FRONT_AND_BACK,GL_SHININESS,10);
      glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,1);
      glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,1);
   }
   for (int i = 0; i < 8; i++)
      glDisable(lightName[i]);
   glDisable(GL_LIGHTING);
   int viewport[4];
   glGetIntegerv(GL_VIEWPORT, viewport);
   int x, y, w, h;
   x = (int)(viewport[0] + (viewport[2]*viewXmin));
   y = (int)(viewport[1] + (viewport[3]*viewYmin));
   w = (int)(viewport[0] + (viewport[2]*viewXmax)) - x;
   h = (int)(viewport[1] + (viewport[3]*viewYmax)) - y;
   glViewport(x,y,w,h);
   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   glLoadIdentity();
   float x1 = xmin, x2 = xmax, y1 = ymin, y2 = ymax;
   if (keepAspect) {
      float viewAspect = float(h)/float(w);
      float worldAspect = (y1-y2)/(x1-x2);
      float ratio = worldAspect / viewAspect;
      if (fabs(ratio) > 1) {
          float excess = (x2-x1)*ratio - (x2-x1); 
          x1 -= excess/2;
          x2 += excess/2;
      }
      else if (fabs(ratio)<1) {
          float excess = (y2-y1)/ratio - (y2-y1); 
          y1 -= excess/2;
          y2 += excess/2;
      }
   }
   float d =sqrt( (eyex-refx)*(eyex-refx) + 
                 (eyey-refy)*(eyey-refy) + (eyez-refz)*(eyez-refz) );
   float near = d - zmax;
   float far = d - zmin;
   if (projType == PERSPECTIVE) {
      x1 = x1 * (near/d);
      x2 = x2 * (near/d);
      y1 = y1 * (near/d);
      y2 = y2 * (near/d);
      glFrustum(x1,x2,y1,y2,near,far);
   }
   else
      glOrtho(x1,x2,y1,y2,near,far);
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
   glLoadIdentity();
   gluLookAt(eyex,eyey,eyez,refx,refy,refz,upx,upy,upz);
   if (background) {   
      glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT);
      glDisable(GL_DEPTH_TEST);
      glColor3fv(background);
      glBegin(GL_POLYGON);
      glVertex3f(x1,y1,near);
      glVertex3f(x1,y2,near);
      glVertex3f(x2,y2,near);
      glVertex3f(x2,y1,near);
      glEnd();
      glPopAttrib();
   }
   object->turnOnLights();
   object->render();
   glMatrixMode(GL_MODELVIEW);
   glPopMatrix();
   glMatrixMode(GL_PROJECTION);
   glPopMatrix();
   glPopAttrib();
   glFlush();
}



Object::Object() {
   color = specular = emissive = 0;
   transforms = 0;
   autodel = 0;
   lit = true;
   visible = true;
   transformCount = 0;
   transformArraySize = 0;
}


Object::~Object() {
   if (color) 
      delete[] color;
   if (specular) 
      delete[] specular;
   if (emissive)
      delete[] emissive;
   if (transforms) {
      for (int i = 0; i < transformCount; i++)
         if (autodel[i])
            delete transforms[i];
      delete[] transforms;
      delete[] autodel;
   }
}


void Object::renderLights() {
}


bool Object::containsLights() {
   return false;
}


void Object::render() {   
   if (!visible)
      return;
   bool needsPop = 0;
   if (glIsEnabled(GL_LIGHTING) != lit || color || emissive || specular) {
      glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT);
      if (lit)
         glEnable(GL_LIGHTING);
      else
         glDisable(GL_LIGHTING);
      if (color) {
         glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE,color);
         glColor4fv(color); 
      }
      if (emissive)
         glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,emissive);
      if (specular)
         glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular);
      needsPop = 1;
   }
   if (transforms) {
      glPushMatrix();
      for (int i = transformCount-1; i >= 0; i--)
         transforms[i]->apply();
   }
   renderObject();
   if (transforms) {
      glPopMatrix();
   }
   if (needsPop) {
      glPopAttrib();
   }
}


void Object::turnOnLights() {
   if (!visible)
      return;
   if (containsLights()) {
      if (transforms) {
         glPushMatrix();
         for (int i = transformCount-1; i >= 0; i--)
            transforms[i]->apply();
      }
      renderLights();
      if (transforms) {
         glPopMatrix();
      }
   }
}


Object *Object::addTransform(Transform *transform, bool autodelete) {
   if (transformCount == 0) {
      transforms = new TransformPtr[3];
      autodel = new bool[3];
      transformArraySize = 3;
   }
   if (transformCount == transformArraySize) {  
      Transform **newTr = new TransformPtr[2*transformArraySize];
      bool *newAd = new bool[2*transformArraySize];
      for (int i = 0; i < transformCount; i++) {
         newTr[i] = transforms[i];
         transforms[i] = 0;
         newAd[i] = autodel[i];
      }
      delete[] transforms;
      transforms = newTr;
      delete[] autodel;
      autodel = newAd;
      transformArraySize *= 2;
   }
   transforms[transformCount] = transform;
   autodel[transformCount] = autodelete;
   transformCount++;
   return this;
}

Object *Object::setColor(float red, float green, float blue) {
   if (!color)
      color = new float[4];
   color[0] = clamp(red);
   color[1] = clamp(green);
   color[2] = clamp(blue);
   color[3] = 1;
   return this;
}


Object *Object::setSpecularColor(float red, float green, float blue) {
   if (!specular)
      specular = new float[4];
   specular[0] = clamp(red);
   specular[1] = clamp(green);
   specular[2] = clamp(blue);
   specular[3] = 1;
   return this;
}


Object *Object::setEmissiveColor(float red, float green, float blue) {
   if (!emissive)
      emissive = new float[4];
   emissive[0] = clamp(red);
   emissive[1] = clamp(green);
   emissive[2] = clamp(blue);
   emissive[3] = 1;
   return this;
}


Object *Object::setIsLit(bool isLit) {
   lit = isLit;
   return this;
}


Object *Object::setVisible(bool isVisible) {
   visible = isVisible;
   return this;
}


void Object::getColor(float &red, float &green, float &blue) {
   if (!color)
      red = green = blue = -1;
   else {
      red = color[0];
      green = color[1];
      blue = color[2];
   }
}


void Object::getSpecularColor(float &red, float &green, float &blue) {
   if (!specular)
      red = green = blue = -1;
   else {
      red = specular[0];
      green = specular[1];
      blue = specular[2];
   }
}


void Object::getEmissiveColor(float &red, float &green, float &blue) {
   if (!emissive)
      red = green = blue = -1;
   else {
      red = emissive[0];
      green = emissive[1];
      blue = emissive[2];
   }
}


bool Object::isLit() {
   return lit;
}


bool Object::getIsVisible() {
   return visible;
}


int Object::getTransformCount() {
   return transformCount;
}


Transform *Object::getTransform(int index) {
   if (index < 0 || index >= transformCount)
      return 0;
   else
      return transforms[index];
}


void Object::deleteTransform(int index) {
   if (index < 0 || index >= transformCount)
      return;
   if (autodel[index])
      delete transforms[index];
   for (int i = index+1; i < transformCount; i++) {
      transforms[i-1] = transforms[i];
      autodel[i-1] = autodel[i];
   }
   transforms[index] = 0;
   if (transformCount == 0) {
      delete[] transforms;
      delete[] autodel;
      transformArraySize = 0;
   }
}



Model::Model() {
   objects = 0;
   autodel = 0;
   objectCount = objectArraySize = 0;
}


Model::Model(Object *object, Transform *transform) {
   objects = 0;
   autodel = 0;
   objectCount = objectArraySize = 0;
   if (object != 0)
      addObject(object);
   if (transform != 0)
      addTransform(transform);
}

Model::~Model() {
   if (objectCount > 0) {
      for (int i = 0; i < objectCount; i++) {
         if (autodel[i])
            delete objects[i];
         objects[i] = 0;
      }
      delete[] objects;
      delete[] autodel;
   }
}


void Model::renderObject() {
   for (int i = 0; i < objectCount; i++)
      objects[i]->render();
}


bool Model::containsLights() {
   for (int i = 0; i < objectCount; i++)
      if (objects[i]->containsLights())
         return true;
   return false;
}


void Model::renderLights() {
   for (int i = 0; i < objectCount; i++)
      objects[i]->turnOnLights();
}


Model *Model::addObject(Object *object, bool autodelete) {
   if (objectCount == 0) {
      objects = new ObjectPtr[3];
      autodel = new bool[3];
      objectArraySize = 3;
   }
   if (objectCount == objectArraySize) {
      Object **newObj = new ObjectPtr[2*objectArraySize];
      bool *newAd = new bool[2*objectArraySize];
      for (int i = 0; i < objectCount; i++) {
         newObj[i] = objects[i];
         objects[i] = 0;
         newAd[i] = autodel[i];
      }
      delete[] objects;
      objects = newObj;
      delete[] autodel;
      autodel = newAd;
      objectArraySize *= 2;
   }
   objects[objectCount] = object;
   autodel[objectCount] = autodelete;
   objectCount++;
   return this;
}


int Model::getObjectCount() {
   return objectCount;
}


Object *Model::getObject(int index) {
   if (index < 0 || index > objectCount)
      return 0;
   else
      return objects[index];
}


void Model::deleteObject(int index) {
   if (index < 0 || index >= objectCount)
      return;
   if (autodel[index])
      delete objects[index];
   for (int i = index+1; i < objectCount;i++) {
      objects[i-1] = objects[i];
      autodel[i-1] = autodel[i];
   }
   objects[index] = 0;
   if (objectCount == 0) {
      delete[] objects;
      delete[] autodel;
      objectArraySize = 0;
   }
}



BasicObject::BasicObject(BasicObjectType type, bool isWireframe) {
   this->type = type;
   wireframe = isWireframe;
}


BasicObjectType BasicObject::getType() {
   return type;
}


void BasicObject::setType(BasicObjectType type) {
   this->type = type;
}


static int getWireObjectID(BasicObjectType type);
static int getSolidObjectID(BasicObjectType type);

void BasicObject::renderObject() {
   if (wireframe)
      glCallList(getWireObjectID(type));
   else
      glCallList(getSolidObjectID(type));
}


BasicObject *BasicObject::setIsWireframe(bool isWireframe) {
   wireframe = isWireframe;
   return this;
}


bool BasicObject::isWireframe() {
   return wireframe;
}



DisplayListObject::DisplayListObject(int id) {
   this->id = id;
}


void DisplayListObject::renderObject() {
   if (id)
      glCallList(id);
}


int DisplayListObject::getID() {
   return id;
}


IndexedFaceSet::IndexedFaceSet(const string fileName) : DisplayListObject(0) {
   setData(fileName);
}


IndexedFaceSet::IndexedFaceSet(float vertices[][3], int vertexCount,
                   int faceData[], int faceDataCount,
                   IFSNormalFlag normalsBy, float normals[][3],
                   IFSColorFlag colorsBy, float colors[][3]) 
                                                     : DisplayListObject(0) {
   setData(vertices,vertexCount,faceData,faceDataCount,
           normalsBy,normals,colorsBy,colors);
}


bool IndexedFaceSet::setData(const string fileName) {
   ifstream f(fileName.c_str());
   if (f.bad()) {
      if (id != 0)
        glDeleteLists(id,1);
      id = 0;
      return false;
   }
   typedef float Vertex[3];
   Vertex *vertex = 0;
   int *faceData = 0;
   Vertex *normal = 0;
   Vertex *color = 0;
   int vertexCt, faceDataCt;
   IFSNormalFlag normalFlag = NO_NORMALS;
   IFSColorFlag colorFlag = NO_COLORS;
   f >> vertexCt >> faceDataCt;
   int flag;
   f >> flag;
   if (flag == 1)
      normalFlag = NORMAL_BY_FACE;
   else if (flag == 2)
      normalFlag = NORMAL_BY_VERTEX;
   f >> flag;
   if (flag == 1)
      colorFlag = COLOR_BY_FACE;
   else if (flag == 2)
      colorFlag = COLOR_BY_VERTEX;
   if (f.bad() || vertexCt <= 0 || faceDataCt <= 0) {
      if (id != 0)
        glDeleteLists(id,1);
      id = 0;
      return false;
   }
   vertex = new Vertex[vertexCt];
   faceData = new int[faceDataCt];
   int faceCt = 0;
   for (int i = 0; i < vertexCt; i++) {
      f >> vertex[i][0] >> vertex[i][1] >> vertex[i][2];
   }
   for (int i = 0; i < faceDataCt; i++) {
      f >> faceData[i];
      if (faceData[i] == -1)
         faceCt++;
   }
   if (faceData[faceDataCt-1] != -1)
      faceCt++;  // To make the last -1 optional
   if (normalFlag != NO_NORMALS) {
      if (normalFlag == NORMAL_BY_FACE) {
         normal = new Vertex[faceCt];
         for (int i = 0; i < faceCt; i++)
            f >> normal[i][0] >> normal[i][1] >> normal[i][2];
      }
      else {
         normal = new Vertex[vertexCt];
         for (int i = 0; i < vertexCt; i++)
            f >> normal[i][0] >> normal[i][1] >> normal[i][2];
      }
   }
   if (colorFlag != NO_COLORS) {
      if (colorFlag == COLOR_BY_FACE) {
         color = new Vertex[faceCt];
         for (int i = 0; i < faceCt; i++)
            f >> color[i][0] >> color[i][1] >> color[i][2];
      }
      else {
         color = new Vertex[vertexCt];
         for (int i = 0; i < vertexCt; i++)
            f >> color[i][0] >> color[i][1] >> color[i][2];
      }
   }
   bool valid;
   if (f.bad()) {
      if (id != 0)
        glDeleteLists(id,1);
      id = 0;
      valid = false;
   }
   else {
      valid = setData(vertex,vertexCt,faceData,faceDataCt,
                     normalFlag,normal,colorFlag,color);
   }
   if (faceData)
      delete[] faceData;
   if (color)
      delete[] color;
   if (normal)
      delete[] normal;
   if (vertex)
      delete[] vertex;
   return valid;
}


bool IndexedFaceSet::setData(float vertices[][3], int vertexCount,
                   int faceData[], int faceDataCount,
                   IFSNormalFlag normalsBy, float normals[][3],
                   IFSColorFlag colorsBy, float colors[][3]) {
   bool valid = true;
   int newid;
   if (vertices == 0 || vertexCount <= 0 || faceData == 0 || faceDataCount <= 0)
      valid = false;
   else {  
      if (normals == 0)
         normalsBy = NO_NORMALS;
      if (colors == 0)
         colorsBy = NO_COLORS;
      newid = glGenLists(1); 
      int pos = 0;
      int face = 0;
      glNewList(newid,GL_COMPILE);
      glPushAttrib(GL_CURRENT_BIT | GL_LIGHTING_BIT);
      glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
      glEnable(GL_COLOR_MATERIAL);
      while (pos < faceDataCount) {
         glBegin(GL_POLYGON);
         if (normalsBy == NORMAL_BY_FACE)
            glNormal3fv(normals[face]);
         if (colorsBy == COLOR_BY_FACE)
            glColor3fv(colors[face]);
         while (pos < faceDataCount && faceData[pos] != -1) {
            if (normalsBy == NORMAL_BY_VERTEX)
                glNormal3fv(normals[faceData[pos]]);
            if (colorsBy == COLOR_BY_VERTEX)
                glColor3fv(colors[faceData[pos]]);
            glVertex3fv(vertices[faceData[pos]]);
            pos++;
         }
         glEnd();
         pos++;
         face++;
      }
      glPopAttrib();
      glEndList();
   }
   if (id != 0)
      glDeleteLists(id,1);
   if (valid)
      id = newid;
   else
      id = 0;
   return valid;
}


IndexedFaceSet::~IndexedFaceSet() {
   if (id != 0)
      glDeleteLists(id,1);
}


Light::Light(float x, float y, float z, bool isPositional) {
   id = -1;
   for (int i = 0; i < 8; i++) {
      if (!lightIsUsed[i]) {
         lightIsUsed[i] = true;
         id = i;
         break;
      }
   }
   if (id == -1)
      throw "Too many lights.  There is a limit of eight lights.";
   position[0] = x;
   position[1] = y;
   position[2] = z;
   position[3] = isPositional? 1 : 0;
   attenuation =0.05;
   isSpot = 0;
   spotCutoff = 20;
   spotDirection[0] = spotDirection[1] = 0;
   spotDirection[2] = -1;
   spotExponent = 4;
}


Light::~Light() {
   lightIsUsed[id] = false;
}


void Light::renderObject() {
}


void Light::renderLights() {
   if (!visible)
      return;
   glEnable(lightName[id]);
   glLightfv(lightName[id],GL_POSITION,position);
   glLightf(lightName[id],GL_CONSTANT_ATTENUATION,1);
   glLightf(lightName[id],GL_LINEAR_ATTENUATION,attenuation);
   glLightf(lightName[id],GL_QUADRATIC_ATTENUATION,0);
   if (isSpot && (position[3] == 1)) {
      glLightf(lightName[id],GL_SPOT_CUTOFF,spotCutoff);
      glLightfv(lightName[id],GL_SPOT_DIRECTION,spotDirection);
      glLightf(lightName[id],GL_SPOT_EXPONENT,spotExponent);
   }
   else
      glLightf(lightName[id],GL_SPOT_CUTOFF,180);
   if (color) {
      colorvec[0] = color[0];
      colorvec[1] = color[1];
      colorvec[2] = color[2];
      colorvec[3] = 1;
   }
   else {
      colorvec[0] = colorvec[1] = colorvec[2] = colorvec[3] = 1;
   }
   glLightfv(lightName[id],GL_DIFFUSE,colorvec);
   glLightfv(lightName[id],GL_SPECULAR,colorvec);
   for (int i = 0; i < 4; i++) {
      colorvec[i] /= 20;
      if (colorvec[i] > 0.2)
         colorvec[1] = 0.2;
   }
   glLightfv(lightName[id],GL_AMBIENT,colorvec);
}


bool Light::containsLights() {
   return visible;
}

Object *Light::setColor(float red, float green, float blue) {
   if (!color)
      color = new float[4];
   color[0] = red;
   color[1] = green;
   color[2] = blue;
   color[3] = 1;
   return this;
}

Light *Light::setAttenuation(float attenuation) {
   this->attenuation = fabs(attenuation);
   return this;
}


float Light::getAttenuation() {
  return attenuation;
}


Light *Light::setIsSpot(bool isSpot) {
   this->isSpot = isSpot;
   return this;
}


Light *Light::setSpotDirection(float dx, float dy, float dz) {
   spotDirection[0] = dx;
   spotDirection[1] = dy;
   spotDirection[2] = dz;
   return this;
}


Light *Light::setSpotCutoff(float spotCutoff) {
   this->spotCutoff = spotCutoff;
   return this;
}


Light *Light::setSpotExponent(float spotExponent) {
   this->spotExponent = spotExponent;
   return this;
}




Light *Light::setPosition(float x, float y, float z, bool isPositional) {
   position[0] = x;
   position[1] = y;
   position[2] = z;
   position[3] = isPositional? 1 : 0;
   return this;
}


Rotate::Rotate(float angle, float axis_x, float axis_y, float axis_z) {
   theta = angle;
   dx = axis_x;
   dy = axis_y;
   dz = axis_z;
}


Rotate::Rotate(float angle) {
   theta = angle;
   dx = 0;
   dy = 0;
   dz = 1;
}


void Rotate::setAngle(float angle) {
   theta = angle;
}


void Rotate::setAxis(float axis_x, float axis_y, float axis_z) {
   dx = axis_x;
   dy = axis_y;
   dz = axis_z;
}


float Rotate::getAngle() {
   return theta;
}


float Rotate::getAxisX() {
   return dx;
}


float Rotate::getAxisY() {
   return dy;
}


float Rotate::getAxisZ() {
   return dz;
}


void Rotate::apply() {
   glRotatef(theta,dx,dy,dz);
}



Translate::Translate(float dx, float dy, float dz) {
   tx = dx;
   ty = dy;
   tz = dz;
}


void Translate::set(float dx, float dy, float dz) {
   tx = dx;
   ty = dy;
   tz = dz;
}


float Translate::getDx() {
   return tx;
}


float Translate::getDy() {
   return ty;
}


float Translate::getDz() {
   return tz;
}


void Translate::apply() {
   glTranslatef(tx,ty,tz);
}



Scale::Scale(float scale_x, float scale_y, float scale_z) {
   sx = scale_x;
   sy = scale_y;
   sz = scale_z;
}


Scale::Scale(float scale) {
   sx = scale;
   sy = scale;
   sz = scale;
}


void Scale::set(float scale_x, float scale_y, float scale_z) {
   sx = scale_x;
   sy = scale_y;
   sz = scale_z;
}


void Scale::set(float scale) {
   sx = scale;
   sy = scale;
   sz = scale;
}


float Scale::getScaleX() {
   return sx;
}


float Scale::getScaleY() {
   return sy;
}


float Scale::getScaleZ() {
   return sz;
}


void Scale::apply() {
   glScalef(sx,sy,sz);
}


static int makeBasicObject(BasicObjectType type, bool wireframe) {
   int id = glGenLists(1);
   glNewList(id,GL_COMPILE);
   switch (type) {
      case LINE:
         glBegin(GL_LINES);
         glVertex2f(-0.5,0);
         glVertex2f(0.5,0);
         glEnd();
         break;
      case SQUARE:
         if (wireframe)
            glBegin(GL_LINE_LOOP);
         else {
            glNormal3f(0,0,1);
            glBegin(GL_POLYGON);
         }
         glVertex2f(-0.5,-0.5);
         glVertex2f(0.5,-0.5);
         glVertex2f(0.5,0.5);
         glVertex2f(-0.5,0.5);
         glEnd();
         break;
      case CIRCLE:
         if (wireframe) {
            glBegin(GL_LINE_LOOP);
            float theta = 2*3.141592654 / 32;
            for (int i = 0; i < 32; i++) {
               glVertex2f(0.5*cos(i*theta),0.5*sin(i*theta));
            }
            glEnd();
         }
         else {
            GLUquadric *circ = gluNewQuadric();
            gluQuadricDrawStyle(circ,GLU_FILL);
            gluDisk(circ,0,0.5,32,4);
            gluDeleteQuadric(circ);
         }
         break;
      case CUBE:
         if (wireframe)
            glutWireCube(1);
         else
            glutSolidCube(1);
         break;
      case SPHERE:
         if (wireframe)
            glutWireSphere(0.5,32,16);
         else
            glutSolidSphere(0.5,32,16);
         break;
      case CONE:
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glRotatef(-90,1,0,0);
         glTranslatef(0,0,-0.5);
         if (wireframe) {
            glutWireCone(0.5,1,16,6);
         }
         else {
            glutSolidCone(0.5,1,32,8);
            GLUquadric *circ = gluNewQuadric();
            gluQuadricDrawStyle(circ,GLU_FILL);
            gluDisk(circ,0,0.5,32,4);
            gluDeleteQuadric(circ);
         }
         glPopMatrix();
         break;
      case CYLINDER: {
         GLUquadric *cyl = gluNewQuadric();
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glRotatef(90,1,0,0);
         glTranslatef(0,0,-0.5);
         if (wireframe)
            gluQuadricDrawStyle(cyl,GLU_LINE);
         else
            gluQuadricDrawStyle(cyl,GLU_FILL);
         gluCylinder(cyl,0.5,0.5,1,32,8);
         gluQuadricOrientation(cyl,GLU_INSIDE);
         gluDisk(cyl,0,0.5,32,4);
         glTranslatef(0,0,1);
         gluQuadricOrientation(cyl,GLU_OUTSIDE);
         gluDisk(cyl,0,0.5,32,4);
         glPopMatrix();
         gluDeleteQuadric(cyl);
         } break;
      case ICOSAHEDRON:
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glScalef(0.5, 0.5, 0.5);
         if (wireframe)
            glutWireIcosahedron();
         else
            glutSolidIcosahedron();
         glPopMatrix();
         break;
      case TETRAHEDRON:
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glScalef(0.57735, 0.57735, 0.57735);
         if (wireframe)
            glutWireTetrahedron();
         else
            glutSolidTetrahedron();
         glPopMatrix();
         break;
      case OCTAHEDRON:
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glScalef(0.5, 0.5, 0.5);
         if (wireframe)
            glutWireOctahedron();
         else
            glutSolidOctahedron();
         glPopMatrix();
         break;
      case TORUS:
         if (wireframe)
            glutWireTorus(0.18,0.32,16,32);
         else
            glutSolidTorus(0.18,0.32,16,32);
         break;
      case TEAPOT:
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glScalef(-0.45, 0.45, 0.45);
         if (wireframe)
            glutWireTeapot(1);
         else
            glutSolidTeapot(1);
         glPopMatrix();
         break;
      case DODECAHEDRON:
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
         glScalef(0.28, 0.28, 0.28);
         if (wireframe)
            glutWireDodecahedron();
         else
            glutSolidDodecahedron();
         glPopMatrix();
         break;
   }
   glEndList();
   return id;
}

static int wireObjectID[TEAPOT+1] = { 0 };
static int solidObjectID[TEAPOT+1] = { 0 };

static int getWireObjectID(BasicObjectType type) {
   if (wireObjectID[type] == 0)
      wireObjectID[type] = makeBasicObject(type,true);
   return wireObjectID[type];
}

static int getSolidObjectID(BasicObjectType type) {
   if (solidObjectID[type]== 0)
      solidObjectID[type] = makeBasicObject(type,false);
   return solidObjectID[type];
}


