/* Ligne polygonale 3D                          */
/*                                              */
/* Auteur: Nicolas JANEY                        */
/* nicolas.janey@univ-fcomte.fr                 */
/* Mars 2020                                    */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include "Pos3D.h"
#include "LignePolygonale3D.h"
#include "MatriceDeBase.h"

/* Constructeurs                            */

LignePolygonale3D::LignePolygonale3D(void) {
  nbPoints = 0;
  tPos = NULL;
  sp = 0;
}

LignePolygonale3D::LignePolygonale3D(int nbPoints) {
  this->nbPoints = nbPoints;
  tPos =(Pos3D **) calloc(nbPoints,sizeof(Pos3D *));
  for ( int i = 0 ; i < nbPoints ; i++ )
    tPos[i] = new Pos3D();
  sp = 0;
}

LignePolygonale3D::LignePolygonale3D(int nbPoints,Pos3D **tPos) {
  this->nbPoints = nbPoints;
  this->tPos =(Pos3D **) calloc(nbPoints,sizeof(Pos3D *));
  for ( int i = 0 ; i < nbPoints ; i++ ) {
    this->tPos[i] = new Pos3D(tPos[i]); }
  sp = nbPoints;
}

LignePolygonale3D::LignePolygonale3D(LignePolygonale3D *lp) {
  nbPoints = lp->nbPoints;
  tPos =(Pos3D **) calloc(nbPoints,sizeof(Pos3D *));
  for ( int i = 0 ; i < nbPoints ; i++ ) {
    tPos[i] = new Pos3D(lp->tPos[i]); }
  sp = lp->sp;
}

/* Destructeur                              */

LignePolygonale3D::~LignePolygonale3D(void) {
  if ( nbPoints != 0 ) {
    if ( tPos ) {
      for ( int i = 0 ; i < nbPoints ; i++)
        delete(tPos[i]);
      free(tPos);
      tPos = NULL; } }
}
/* Calcul la position d'un point sur une courbe  */
/* B-Spline controlee par quatre points          */
/* tPos : le tableau des 4 points de controle    */
/* t : la valeur de calcul du point              */
/*     t a prendre dans l'intervalle [0.0,1.0]   */
/* mb : la matrice de base                       */
/* point : le point resultat                     */

static void positionSurBSpline(Pos3D **tPos,float t,const MatriceDeBase *mb,Pos3D *point) {
  float vt[4] = { t*t*t,t*t,t,1.0F };
  float vtmb[4] = { 0.0F,0.0F,0.0F,0.0F };
  for ( int j = 0 ; j < 4 ; j++ ) {
    for ( int k = 0 ; k < 4 ; k++ )
      vtmb[j] += vt[k] * mb->c[k][j] ; }
  point->x = point->y = point->z = 0.0;
  for ( int j = 0 ; j < 4 ; j++ ) {
    point->x += vtmb[j] * tPos[j]->x ;
    point->y += vtmb[j] * tPos[j]->y ;
    point->z += vtmb[j] * tPos[j]->z ; }
}

LignePolygonale3D::LignePolygonale3D(int nbPoints,LignePolygonale3D *lp,const MatriceDeBase *m) {
  this->nbPoints = nbPoints;
  tPos =(Pos3D **) calloc(nbPoints,sizeof(Pos3D *));
  for ( int i = 0 ; i < nbPoints ; i++ ) {
    tPos[i] = new Pos3D();
    float t = i/(nbPoints-1.0)*(lp->nbPoints-3);
    int nb =(int) t;
    if ( nb == lp->nbPoints-3 )
      nb = lp->nbPoints-4;
    positionSurBSpline(&(lp->tPos[nb]),t-nb,m,tPos[i]); }
}

LignePolygonale3D::LignePolygonale3D(int nbPoints,LignePolygonale3D *lp) {
  this->nbPoints = nbPoints;
  tPos =(Pos3D **) calloc(nbPoints,sizeof(Pos3D *));
  long long *cn;
  cn =(long long *) calloc(lp->nbPoints,sizeof(long long));
  cn[0] = 1;
  cn[1] = lp->nbPoints-1;
  for ( int i = 2 ; i < lp->nbPoints ; i++ )
    cn[i] = (cn[i-1]*(lp->nbPoints-i))/i;
  for ( int i = 0 ; i < nbPoints ; i++ ) {
    tPos[i] = new Pos3D();
    float t =(float) i/(nbPoints-1);
    float mt = 1.0F-t;
    for ( int j = 0 ; j < lp->nbPoints ; j++ ) {
      float fac = cn[j]*pow(t,j)*pow(mt,lp->nbPoints-1-j);
      tPos[i]->x += fac * lp->tPos[j]->x;
      tPos[i]->y += fac * lp->tPos[j]->y;
      tPos[i]->z += fac * lp->tPos[j]->z; } }
  free(cn);
}

void LignePolygonale3D::add(Pos3D *p) {
  tPos[sp]->x = p->x;
  tPos[sp]->y = p->y;
  tPos[sp]->z = p->z;
  sp++;
}

void LignePolygonale3D::setPosition(int pos,Pos3D *p) {
  tPos[pos]->x = p->x;
  tPos[pos]->y = p->y;
  tPos[pos]->z = p->z;
}

void LignePolygonale3D::print(void) {
  for ( int i = 0 ; i < nbPoints ; i++ ) {
    tPos[i]->print();
    printf("\n"); }
}

void LignePolygonale3D::drawOpenGL(GLenum typePrimitive) {
  glBegin(typePrimitive);
  for ( int i = 0 ; i < nbPoints ; i++ ) {
    glVertex3f(tPos[i]->x,tPos[i]->y,tPos[i]->z); }
  glEnd();
}
