/*
 * ADAPT2D : a software for automatic mesh adaptation in 2D
 *
 * AUTHOR : Manuel J. Castro Diaz(e-mail:castro@gamba.cie.uma.es)
 * ADAPTED FOR FREEFEM : Prud'homme Christophe (e-mail:prudhomm@ann.jussieu.fr) 
 *
 * this code is public domain
 * 
 * You may copy freely these files and use it for    
 * teaching or research. These or part of these may   
 * not be sold or used for a commercial purpose without
 * our consent
 * 
 * Any problems should be reported to the AUTHOR
 * at the following address : castro@gamba.cie.uma.es
 */


#include <t_t0.hxx>

Triangulo_T0::Triangulo_T0() {
  ref=0;
  marca=-1;
  for (int i=0; i<3; i++) {
    s[i]=NIL;
    t[i]=NIL;
    a[i]=NIL;
  }
}
Triangulo_T0::Triangulo_T0(Vertice_T0* s0, Vertice_T0* s1, Vertice_T0* s2,Triangulo_T0* t0, Triangulo_T0* t1, Triangulo_T0* t2,Arista_T0* a0, Arista_T0* a1, Arista_T0* a2, int reff=0) { 
  ref=reff;
  marca=-1;
  s[0]=s0; s[1]=s1; s[2]=s2;
  t[0]=t0; t[1]=t1; t[2]=t2;
  a[0]=a0; a[1]=a1; a[2]=a2;
}
Triangulo_T0::Triangulo_T0(Vertice_T0& s0, Vertice_T0& s1, Vertice_T0& s2,Triangulo_T0& t0, Triangulo_T0& t1, Triangulo_T0& t2,Arista_T0& a0, Arista_T0& a1, Arista_T0& a2, int reff=0) { 
  ref=reff;
  marca=-1;
  s[0]=&s0; s[1]=&s1; s[2]=&s2;
  t[0]=&t0; t[1]=&t1; t[2]=&t2;
  a[0]=&a0; a[1]=&a1; a[2]=&a2;
}
Triangulo_T0::Triangulo_T0(Vertice_T0* s0[], Triangulo_T0* t0[], Arista_T0* a0[],int reff=0) {
  ref=reff;
  marca=-1;
  for (int i=0; i<3; i++) { 
    s[i]=s0[i];
    t[i]=t0[i];
    a[i]=a0[i];
  }
}
Triangulo_T0::Triangulo_T0(const Triangulo_T0& tt) {
  ref=tt.ref;
  marca=tt.marca;
  for (int i=0; i<3; i++) { 
    s[i]=tt.s[i];
    t[i]=tt.t[i];
    a[i]=tt.a[i];
  }
}
void Triangulo_T0::set(Vertice_T0* s0, Vertice_T0* s1, Vertice_T0* s2,Triangulo_T0* t0, Triangulo_T0* t1, Triangulo_T0* t2,Arista_T0* a0, Arista_T0* a1, Arista_T0* a2, int reff=0) { 
  ref=reff;
  s[0]=s0; s[1]=s1; s[2]=s2;
  t[0]=t0; t[1]=t1; t[2]=t2;
  a[0]=a0; a[1]=a1; a[2]=a2;
}                                                          
void Triangulo_T0::set(Vertice_T0& s0, Vertice_T0& s1, Vertice_T0& s2, Triangulo_T0& t0, Triangulo_T0& t1, Triangulo_T0& t2,Arista_T0& a0, Arista_T0& a1, Arista_T0& a2, int reff=0) { 
  ref=reff;
  s[0]=&s0; s[1]=&s1; s[2]=&s2;
  t[0]=&t0; t[1]=&t1; t[2]=&t2;
  a[0]=&a0; a[1]=&a1; a[2]=&a2;
}
void Triangulo_T0::set(Vertice_T0* s0[], Triangulo_T0* t0[], Arista_T0* a0[],int reff=0) {
  ref=reff;
  for (int i=0; i<3; i++) { 
    s[i]=s0[i];
    t[i]=t0[i];
    a[i]=a0[i];
  }
}
void Triangulo_T0::set(const Triangulo_T0& tt) {
  ref=tt.ref;
  marca=tt.marca;
  for (int i=0; i<3; i++) { 
    s[i]=tt.s[i];
    t[i]=tt.t[i];
    a[i]=tt.a[i];
  }
}

Triangulo_T0& Triangulo_T0::operator=(const Triangulo_T0& tt)  {
  ref=tt.ref;
  marca=tt.marca;
  for (int i=0; i<3; i++) { 
    s[i]=tt.s[i];
    t[i]=tt.t[i];
    a[i]=tt.a[i];
  }
  return *this;
}
void Triangulo_T0::write(ostream& os)
{
  os <<"Triangulo_T0:"<<this<<"; Reference:"<< ref
     <<",   Task:"<<marca<< endl
     <<"   Vertices       :"<<s[0]<<", "<<s[1]<<", "<<s[2]<<endl
     <<"   Edges          :"<<a[0]<<", "<<a[1]<<", "<<a[2]<<endl
     <<"   Neig. Triangles:"<<t[0]<<", "<<t[1]<<", "<<t[2]<<endl;
}
ostream& operator<<(ostream& os, Triangulo_T0& tt) {tt.write(os); return os;}


/*     *********   AREA subroutine  ************.
      
       output: Positive area.
*/
Scalar Triangulo_T0::area() {
  R2 v1,v2;
  Scalar det;
  v1=(s[1]->c)-(s[0]->c);
  v2=(s[2]->c)-(s[0]->c);    
  det=fabs(v1.x*v2.y-v1.y*v2.x)/2.0;
  return det;
}
/*     
       ***********  AREA2D  Subroutine  *****************
         
         salida: Signed area. (Negative = not positive oriented)
  
*/
Scalar Triangulo_T0::area2D() {
  R2 v1,v2;
  Scalar det;
  v1=(s[1]->c)-(s[0]->c);
  v2=(s[2]->c)-(s[0]->c);
  det=(v1.x*v2.y-v1.y*v2.x)/2.0;
  return det;
}


/*
   ****************  SOMMET SUBROUTINE  *****************
      It gives the locar position (0-1-2) of the vertex s0 in the triangle
          pointed by this.
      Input: s0: Vertex (pointer).
      Output:  Integer. 0-1-2. Local positon in the triangle.
               -1 if the vertex is not a vertex of the triangle.
*/
int Triangulo_T0::sommet(Vertice_T0* s0) {
  int pos=-1;
  for (int i=0; i<3; i++) {
    if (s[i]==s0) {
      pos=i;
      break;
    }
  }
  return pos;
}
/*
    ***************  ARISTA SUBROUTINE  ******************
    It gives the local position (0-1-2) of the edge with vertices s0-s1 in the
    triangle pointed by this.
      Input: Vertices s0 and s1 (pointers).
    
      Output: Local position in the triangle: 0-1-2.
              -1 if the edge is not in the triangle.
*/
int Triangulo_T0::arista(Vertice_T0* s0, Vertice_T0* s1) {
  int p_pos[3];
  int p_neg[3];
  int i,pos=-1;
  int aa=-1;
  p_pos[0]=1;p_pos[1]=2;p_pos[2]=0;
  p_neg[0]=2;p_neg[1]=0;p_neg[2]=1;
  
  for (i=0; i<3; i++) {
    if (s[i]==s0) {
      pos=i;
      break;
                }
  }
  if (pos>=0) {
    if (s1==s[p_pos[pos]])
      aa=pos;
    else {
      if (s1==s[p_neg[pos]])
        aa=p_neg[pos];
    }
  }
  return aa;
}

/*    
     *********      ARISTA SUBROUTINE    **********
        Similar than the previous one, but the input data is
        is now the pointer aa to the edge and not the vertices.
*/
int Triangulo_T0::arista(Arista_T0* aa) {
  int pos=-1;
  for (int i=0; i<3; i++) {
    if (a[i]==aa) {
      pos=i;
      break;
    }
  }
  return pos;
}


/*   
     *********** SUBRRUTINA  VECINO  *****************
     Determina el triangulo vecino a uno dado por la
     arista que empieza o termina por vertice so segun
     un sentido.
     Entrada: Triangulo apuntado por this.
     Puntero a un vertice. (so)
     sentido + o -.(sent=1 o -1).
     (Si sent=1 -> trian. vecino por la arista que
     tiene como primer vertice so.
          Si sent=-1 ->trian. vecino por la arista que
          tiene por vertice final so. )
          Salida: Puntero al triangulo vecino.
          (NIL si no hay).
          */
Triangulo_T0*  Triangulo_T0::vecino(Vertice_T0* so, int sent) {
  Triangulo_T0* tr=NIL;
  int inv[3];
  int pos=-1;
  inv[0]=2;inv[1]=0;inv[2]=1;
  for (int i=0; i<3; i++) {
        if (s[i]==so) {
          pos=i;
          break;
        }
  }
  if (pos>=0) {
    if (sent==1) 
      tr=t[pos];
    else
      tr=t[inv[pos]];
  }
  return tr;
}

/*    
***********   SUBRRUTINA SOMMET_SIG  *************
Determina la posicion del vertice siguiente a uno 
dado por un puntero al mismo.
Entrada:  Triangulo apuntado por this.
Puntero al vertice. (so).
Salida:   La posicion local del siguiente vertice
al dado en dicho triangulo. (0-1-2).
    (-1 si el vertice dado no pertenece al
    triangulo apuntado por this.)
    */
int Triangulo_T0::sommet_sig(Vertice_T0* so) {
  int som=-1,i;
  int p[3];
  p[0]=1;p[1]=2;p[2]=0;
  for (i=0; i<3; i++) {
    if (s[i]==so) {
      som=p[i];
      break;
    }
  }
  return som;
}




/*                Coor_bar Subroutine
                  
      Compute the barycentric coordinates of point pt.
      
      Input:   pt= Point.
      -----
      
      Output:  coor= The baricentric coordinates of point pt are:
      ------                (coor.x,coor.y,1-coor.x-coor.y)
      
      
      ------------------------------------------------------------------------
      */

R2 Triangulo_T0::coor_bar(const R2& pt) {
  R2 p1,p2,p3;
  R2 coor;
  p1=s[0]->c; 
  p2=s[1]->c; 
  p3=s[2]->c; 
  coor=coor_b(p1,p2,p3,pt);
  return coor;
}


/* ------------------------------------------------------------------   
   
                 D_TRIANGLE Subroutine.
                 
     compute the distance d(pt,tt)
     pt given point.
     tt given triangle.
     
     Input: pt= Point.
     -----  tt= Triangle.
     
     Output:  dist= distance d(pt,tt). 0 if pt \in tt.
     ------   pr_pt= coordinates of the projection of pt onto tt..
     */              

Scalar d_triangle(Triangulo_T0 tt, const R2& pt,R2& pr_pt) {
  
  R2 proyec_pt,pt1,pt2,pt3;
  R2 c_bar;
  Scalar nn,dist;
  Scalar da0,da1,da2;
  
  proyec_pt=pt;
  nn=tt.area();
  if (nn<EPS) {
    cerr<<"Error. Degenerated triangle. Area:"<<nn<<endl;
    cerr<<"Error in subroutine Triangulo_T0::d_triangle."<<endl;
    exit(1);
  }
  c_bar=tt.coor_bar(proyec_pt);
  if (c_bar.x>=0.0 && c_bar.x<=1.0 && c_bar.y>=0.0 && c_bar.y<=1.0 \
      && (c_bar.x+c_bar.y)<=1 ) {
    // pt \in tt.
    dist=0;
    pr_pt=proyec_pt;
  }
  else {
    da0=d_arete(*(tt.a[0]),proyec_pt,pt1);
    da1=d_arete(*(tt.a[1]),proyec_pt,pt2);
    da2=d_arete(*(tt.a[2]),proyec_pt,pt3);
    dist=MIN(da0,MIN(da1,da2));
    if (dist==da0) 
      pr_pt=pt1;
    else {
      if (dist==da1) 
        pr_pt=pt2;
      else 
        pr_pt=pt3;
    }
  }
  return dist;
}    

           
/* --------------------------------------------------------------------

                         SUBRRUTINA TRIANGLE_SIG

    Proposito:  Dado un triangulo y un punto da el triangulo siguiente
    ---------   que debemos tomar para que la sucesion de triangulos
                que formamos sea tal que la distancia de estos al
                punto dado sea DECRECIENTE.


    Entrada:   trian= puntero a un triangulo dado.
    -------    pt = Punto dado.


    Salida:    O bien el triangulo siguiente a tomar (trian_sig) o
    ------     bien, si trian_sig=NIL:
                 - la distancia (dist) del punto al triangulo dado (trian),
                 - El punto proyectado (proyec) sobre el triangulo dado,
                 - Si el punto proyectado cae sobre una arista que
                   esta en la frontera, dicha arista (arete).
*/

Triangulo_T0* triangle_sig( Triangulo_T0* trian, const R2& pt, R2& proyec, Scalar& dist, Arista_T0& arete) {

  Triangulo_T0* trian_sig=NIL;
  R2 so[3];
  R2 vec1,vec2;
  Scalar area[3];
  int pos1[3],a1=-1,pos=0;
  int ka[2][3];
  
  ka[0][0]=0;ka[0][1]=1;ka[0][2]=2;
  ka[1][0]=1;ka[1][1]=2;ka[1][2]=0;
  
  so[2]=pt;
  arete.set(NIL,NIL,NIL,0,0);
  dist=0;
  proyec.set(0,0);
  
  for (int i=0; i<3; i++) {  
    so[0]=(trian->s[ka[0][i]])->c; 
    so[1]=(trian->s[ka[1][i]])->c; 
    vec1=(so[1]-so[0]);
    vec2=(so[2]-so[0]);
    area[i]=vec1.x*vec2.y-vec1.y*vec2.x; // oriented area
    if (area[i]<0) {  // Negative oriented triangles.
      a1++;
      pos1[a1]=i;
    }
  }
  if (a1>=0) {
    switch (a1) {
    case 0:
      pos=0;
      trian_sig=trian->t[pos1[0]]; //Neighbor triangle by the edge i.
      break;
    case 1:
      if (area[pos1[0]]<area[pos1[1]]){ 
        trian_sig=trian->t[pos1[0]];
        pos=0;
      }
      else{
        pos=1;
        trian_sig=trian->t[pos1[1]];
      }
    }
    if (trian_sig==NIL) { //boundary edge.
      dist=d_arete(*(trian->a[pos1[pos]]),pt,proyec);
      arete=*(trian->a[pos1[pos]]);
    }
  }
  else {
    dist=d_triangle(*trian,pt,proyec);
  }
  return trian_sig;
  
} 
Metrica Triangulo_T0::metrica() {
  Metrica mt;
  R2 ar0,ar1,ar2;
  Scalar a,b,c;
  Scalar det;

  ar0=s[1]->c-s[0]->c;
  ar1=s[2]->c-s[1]->c;
  ar2=s[0]->c-s[2]->c;
  if (ar0.norme() <1e-20 || ar1.norme() <1e-20 || ar2.norme() <1e-20) {
    cerr<<"Error. Degenerated triangle."<<endl;
    cerr<<"Error in subroutine Triangulo_T0::metrica."<<endl;
    exit(-1);
  }
  det=2*(ar0.x*ar0.x*(ar1.x*ar1.y*ar2.y*ar2.y-ar2.x*ar2.y*ar1.y*ar1.y) 
        -ar1.x*ar1.x*(ar0.x*ar0.y*ar2.y*ar2.y-ar2.x*ar2.y*ar0.y*ar0.y)
        +ar2.x*ar2.x*(ar0.x*ar0.y*ar1.y*ar1.y-ar1.x*ar1.y*ar0.y*ar0.y));
  a=2*((ar1.x*ar1.y*ar2.y*ar2.y-ar2.x*ar2.y*ar1.y*ar1.y) 
       -(ar0.x*ar0.y*ar2.y*ar2.y-ar2.x*ar2.y*ar0.y*ar0.y)
       +(ar0.x*ar0.y*ar1.y*ar1.y-ar1.x*ar1.y*ar0.y*ar0.y));
  b=ar0.x*ar0.x*(ar2.y*ar2.y-ar1.y*ar1.y) 
   -ar1.x*ar1.x*(ar2.y*ar2.y-ar0.y*ar0.y)
   +ar2.x*ar2.x*(ar1.y*ar1.y-ar0.y*ar0.y);
  c=2*(ar0.x*ar0.x*(ar1.x*ar1.y-ar2.x*ar2.y) 
      -ar1.x*ar1.x*(ar0.x*ar0.y-ar2.x*ar2.y)
      +ar2.x*ar2.x*(ar0.x*ar0.y-ar1.x*ar1.y));
  mt.set(a/det,b/det,c/det);

  return mt;
}
