/*
 * 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 <m_t0.hxx>

Mallado_T0::Mallado_T0 (const int& np, const int& nt, R2 *cr, int *refs, int *nu, int *refe, Metrica* metric, Scalar& lmin, Scalar& lmax)
{
  int nax, i,j, error, naf;
  int *maret, *aretr, *refa;
  Vertice_T0* ss[3], s0;
  Arista_T0* aa[3], a0;
  Triangulo_T0* tt[3],t0;

  lmin = 1e+30;
  lmax = 0.0;

  nax = np + nt - 1 + NTROUX;
  set (np, nt, nax); 
  

  orienta2D(nu, cr, nt);

  // edges determination
  maret = new int[nax*4];
  aretr = new int[3*nt];
  error = carete (nu, nt, np, maret, this->nba, naf, aretr);
  if (error != 0) 
    {
      cerr << "Error : Cannot create T0 mesh : problem in carete." << endl;
      return;
    }
  
  // edges reference generation
  refa = new int[this->nba];
  for (i = 0;i < this->nba;i++)
    {
      int r0 = refs[maret[i*4]-1], r1 = refs[maret[1+i*4]-1];
      if ((r0 != 0) && (r1 != 0) && (maret[3+i*4] == -1))
        refa[i] = (r0 == r1)?r0:indet(cr[maret[i*4]-1], r0, maret[i*4],
                                      cr[maret[1+i*4]-1], r1, maret[1+i*4]);
      else
        refa[i] = 0;
    }

  // array of triangles containing i as vertex
  int *trsom = new int[np];
  for (i = 0; i < np; i++) trsom[i]=-1;
  for (i = 0;i < nt;i++)
    for (j = 0;j < 3;j++)
      if (trsom[nu[j+3*i]-1] < 0) 
        trsom[nu[j+3*i]-1] = i;

  // metric definition
  for (i=0; i<np; i++) 
    this->add_metrica(metric[i],i);

  // vertices definition
  for (i = 0;i < np;i++)
    {
      if (trsom[i] < 0)
        cerr << "WARNING : this vertex doesn't remain in any triangle" << endl;
      s0.set (cr[i], refs[i], saca_triangle(trsom[i]), this->saca_metrica(i));
      add_sommet (s0, i);
    }

  //edges definition
  for (i = 0;i < this->nba;i++)
    {
      short ff = 0;
      Scalar dist;

      if (maret[3+i*4] == -1)
        ff = 1;
      else
        refa[i] = 0;
      
      a0.set (this->saca_sommet(maret[i*4]-1), this->saca_sommet(maret[1+i*4]-1),
              this->saca_triangle(maret[2+i*4]), refa[i], ff);

      this->add_arete (a0, i);
      dist = distance (a0.s[0]->c, a0.s[1]->c);
      if (dist < lmin) lmin = dist;
      if (dist > lmax) lmax = dist;
    }
  if (lmin == 0)
    {
      cerr << "WARNING : degenerated mesh . minimal edge length is equal to 0" << endl;
      exit (1);
    }
  else
    {
      if (lmin < 1.0)
        {
          this->factor = 1.0/lmin;
        }
      else
        this->factor = 1.0;
    }

  // triangle definition
  for (i = 0;i < nt;i++)
    {
      for (j = 0;j < 3;j++)
        {
          ss[j] = this->saca_sommet (nu[j+3*i] - 1);
          aa[j] = this->saca_arete (aretr[j+3*i]);
          if (maret[2+aretr[j+i*3]*4] != i) 
            tt[j] = this->saca_triangle(maret[2+aretr[j+i*3]*4]);
          else 
            {
              if (maret[3+aretr[j+i*3]*4] != -1) 
                tt[j] = saca_triangle(maret[3+aretr[j+i*3]*4]);
              else 
                tt[j]=NIL;
            }
        }
      t0.set(ss,tt,aa,refe[i]);
      this->add_triangle(t0,i);
    }
  /*
   * free allocated memory
   */
  delete [] maret;
  delete [] trsom;
  delete [] refa;
  delete [] aretr;
  
}
void
Mallado_T0::build(const int& np, const int& nt, R2 *cr, int *refs, int *nu, int *refe, Metrica* metric, Scalar& lmin, Scalar& lmax)
{
  int nax, i, j, error, naf;
  int *maret, *aretr, *refa;
  Vertice_T0* ss[3], s0;
  Arista_T0* aa[3], a0;
  Triangulo_T0* tt[3],t0;
  
  lmin = 1e+30;
  lmax = 0.0;
  
  nax = np + nt - 1 + NTROUX;
  set (np, nt, nax); 
  

  orienta2D(nu, cr, nt);

  // edges determination
  maret = new int[nax*4];
  aretr = new int[3*nt];
  error = carete (nu, nt, np, maret, this->nba, naf, aretr);
  if (error != 0) 
    {
      cerr << "Error : Cannot create T0 mesh : problem in carete." << endl;
      return;
    }
  
  // edges reference generation
  refa = new int[this->nba];
  for (i = 0;i < this->nba;i++)
    {
      int r0 = refs[maret[i*4]-1], r1 = refs[maret[1+i*4]-1];
      if ((r0 != 0) && (r1 != 0) && (maret[3+i*4] == -1))
        refa[i] = (r0 == r1)?r0:indet(cr[maret[i*4]-1], r0, maret[i*4],
                                      cr[maret[1+i*4]-1], r1, maret[1+i*4]);
      else
        refa[i] = 0;
    }

  // array of triangles containing i as vertex
  int *trsom = new int[np];
  for (i = 0; i < np; i++) trsom[i]=-1;
  for (i = 0;i < nt;i++)
    for (j = 0;j < 3;j++)
      if (trsom[nu[j+3*i]-1] < 0) 
        trsom[nu[j+3*i]-1] = i;

  // metric definition
  for (i=0; i<np; i++) 
    this->add_metrica(metric[i], i);

  // vertices definition
  for (i = 0;i < np;i++)
    {
      if (trsom[i] < 0)
        cerr << "WARNING : this vertex doesn't remain in any triangle" << endl;
      s0.set (cr[i], refs[i], saca_triangle(trsom[i]), this->saca_metrica(i));
      add_sommet (s0, i);
    }

  //edges definition
  for (i = 0;i < this->nba;i++)
    {
      short ff = 0;
      Scalar dist;

      if (maret[3+i*4] == -1)
        ff = 1;
      else
        refa[i] = 0;
      
      a0.set (this->saca_sommet(maret[i*4]-1), this->saca_sommet(maret[1+i*4]-1),
              this->saca_triangle(maret[2+i*4]), refa[i], ff);

      this->add_arete (a0, i);
      dist = distance (a0.s[0]->c, a0.s[1]->c);
      if (dist < lmin) lmin = dist;
      if (dist > lmax) lmax = dist;
    }
  if (lmin == 0)
    {
      cerr << "WARNING : degenerated mesh . minimal edge length is equal to 0" << endl;
      exit (1);
    }
  else
    {
      if (lmin < 1.0)
        {
          this->factor = 1.0/lmin;
        }
      else
        this->factor= 1.0;
    }
   
  // triangle definition
  for (i = 0;i < nt;i++)
    {
      for (j = 0;j < 3;j++)
        {
          ss[j] = this->saca_sommet (nu[j+3*i] - 1);
          aa[j] = this->saca_arete (aretr[j+3*i]);
          if (maret[2+aretr[j+i*3]*4] != i) 
            tt[j] = this->saca_triangle(maret[2+aretr[j+i*3]*4]);
          else 
            {
              if (maret[3+aretr[j+i*3]*4] != -1) 
                tt[j] = saca_triangle(maret[3+aretr[j+i*3]*4]);
              else 
                tt[j]=NIL;
            }
        }
      t0.set(ss,tt,aa,refe[i]);
      this->add_triangle(t0,i);
    }
  /*
   * free allocated memory
   */
  delete [] maret;
  delete [] trsom;
  delete [] refa;
  delete [] aretr;

}
void
Mallado_T0::build(const int& np, const int& nt, rpoint *rp, int *refs, triangle *tr, int *refe) {
  R2* cr;
  int nax, i, j, error, naf;
  int *nu,*maret, *aretr, *refa;
  Vertice_T0* ss[3], s0;
  Arista_T0* aa[3], a0;
  Triangulo_T0* tt[3],t0;
  Scalar lmin,lmax;
  
  lmin = 1e+30;
  lmax = 0.0;
  
  nax = np + nt - 1 + NTROUX;
  set (np, nt, nax); 
  nu=new int[nbt*3];
  cr=new R2[nbs];
  if (nu==NIL || cr==NIL) ERROR();
  for (i=0; i<np; i++) {
    cr[i].set(Scalar(rp[i].x),Scalar(rp[i].y));
  }
  for (i=0; i<nt; i++) {
    for (j=0; j<3; j++) {
      nu[i*3+j]=tr[i][j]+1;
    }
  }
  orienta2D(nu, cr, nt);

  // edges determination
  maret = new int[nax*4];
  aretr = new int[3*nt];
  if (maret==NIL || aretr==NIL) ERROR();
  error = carete (nu, nt, np, maret, this->nba, naf, aretr);
  if (error != 0) 
    {
      cerr << "Error : Cannot create T0 mesh : problem in carete." << endl;
      return;
    }
  
  // edges reference generation
  refa = new int[this->nba];
  if (refa==NIL) ERROR();
  for (i = 0;i < this->nba;i++)
    {
      int r0 = refs[maret[i*4]-1], r1 = refs[maret[1+i*4]-1];
      if ((r0 != 0) && (r1 != 0) && (maret[3+i*4] == -1))
        refa[i] = (r0 == r1)?r0:indet(cr[maret[i*4]-1], r0, maret[i*4],
                                      cr[maret[1+i*4]-1], r1, maret[1+i*4]);
      else
        refa[i] = 0;
    }

  // array of triangles containing i as vertex
  int *trsom = new int[np];
  if (trsom==NIL) ERROR();
  for (i = 0; i < np; i++) trsom[i]=-1;
  for (i = 0;i < nt;i++)
    for (j = 0;j < 3;j++)
      if (trsom[nu[j+3*i]-1] < 0) 
        trsom[nu[j+3*i]-1] = i;


  // vertices definition
  for (i = 0;i < np;i++)
    {
      if (trsom[i] < 0)
        cerr << "WARNING : this vertex doesn't remain in any triangle" << endl;
      s0.set (cr[i], refs[i], saca_triangle(trsom[i]), this->saca_metrica(i));
      add_sommet (s0, i);
    }

  //edges definition
  for (i = 0;i < this->nba;i++)
    {
      short ff = 0;
      Scalar dist;

      if (maret[3+i*4] == -1)
        ff = 1;
      else
        refa[i] = 0;
      
      a0.set (this->saca_sommet(maret[i*4]-1), this->saca_sommet(maret[1+i*4]-1),
              this->saca_triangle(maret[2+i*4]), refa[i], ff);

      this->add_arete (a0, i);
      dist = distance (a0.s[0]->c, a0.s[1]->c);
      if (dist < lmin) lmin = dist;
      if (dist > lmax) lmax = dist;
    }
  if (lmin == 0)
    {
      cerr << "WARNING : degenerated mesh . minimal edge length is equal to 0" << endl;
      exit (1);
    }
  else
    {
      if (lmin < 1.0)
        {
          this->factor = 1.0/lmin;
        }
      else
        this->factor= 1.0;
    }
  if (this->factor>5e+3) this->factor=5e+3;
  // mesh scaling;
  for (i=0; i<np; i++) {
    s[i].c=s[i].c*this->factor;
  }
  this->paso_malla=lmin;    
  // triangle definition
  for (i = 0;i < nt;i++)
    {
      for (j = 0;j < 3;j++)
        {
          ss[j] = this->saca_sommet (nu[j+3*i] - 1);
          aa[j] = this->saca_arete (aretr[j+3*i]);
          if (maret[2+aretr[j+i*3]*4] != i) 
            tt[j] = this->saca_triangle(maret[2+aretr[j+i*3]*4]);
          else 
            {
              if (maret[3+aretr[j+i*3]*4] != -1) 
                tt[j] = saca_triangle(maret[3+aretr[j+i*3]*4]);
              else 
                tt[j]=NIL;
            }
        }
      t0.set(ss,tt,aa,refe[i]);
      this->add_triangle(t0,i);
    }
  /*
   * free allocated memory
   */
  delete [] maret;
  delete [] trsom;
  delete [] refa;
  delete [] aretr;
  delete [] nu;
  delete [] cr;
  // computing boundary
  this->crea_frontera(); 
}
void
Mallado_T0::build(const int& np, const int& nt, rpoint *rp, int *refs, triangle *tr, int *refe,Scalar escale) {
  R2* cr;
  int nax, i, j, error, naf;
  int *nu,*maret, *aretr, *refa;
  Vertice_T0* ss[3], s0;
  Arista_T0* aa[3], a0;
  Triangulo_T0* tt[3],t0;
  Scalar lmin=1e+30;
  
  nax = np + nt - 1 + NTROUX;
  set (np, nt, nax); 
  nu=new int[nbt*3];
  cr=new R2[nbs];
  if (nu==NIL || cr==NIL) ERROR();
  for (i=0; i<np; i++) {
    cr[i].set(Scalar(rp[i].x),Scalar(rp[i].y));
  }
  for (i=0; i<nt; i++) {
    for (j=0; j<3; j++) {
      nu[i*3+j]=tr[i][j]+1;
    }
  }
  orienta2D(nu, cr, nt);

  // edges determination
  maret = new int[nax*4];
  aretr = new int[3*nt];
  if (maret==NIL || aretr==NIL) ERROR();
  error = carete (nu, nt, np, maret, this->nba, naf, aretr);
  if (error != 0) 
    {
      cerr << "Error : Cannot create T0 mesh : problem in carete." << endl;
      return;
    }
  
  // edges reference generation
  refa = new int[this->nba];
  if (refa==NIL) ERROR();
  for (i = 0;i < this->nba;i++)
    {
      int r0 = refs[maret[i*4]-1], r1 = refs[maret[1+i*4]-1];
      if ((r0 != 0) && (r1 != 0) && (maret[3+i*4] == -1))
        refa[i] = (r0 == r1)?r0:indet(cr[maret[i*4]-1], r0, maret[i*4],
                                      cr[maret[1+i*4]-1], r1, maret[1+i*4]);
      else
        refa[i] = 0;
    }

  // array of triangles containing i as vertex
  int *trsom = new int[np];
  if (trsom==NIL) ERROR();
  for (i = 0; i < np; i++) trsom[i]=-1;
  for (i = 0;i < nt;i++)
    for (j = 0;j < 3;j++)
      if (trsom[nu[j+3*i]-1] < 0) 
        trsom[nu[j+3*i]-1] = i;


  // vertices definition
  for (i = 0;i < np;i++)
    {
      if (trsom[i] < 0)
        cerr << "WARNING : this vertex doesn't remain in any triangle" << endl;
      s0.set (cr[i], refs[i], saca_triangle(trsom[i]), this->saca_metrica(i));
      add_sommet (s0, i);
    }

  //edges definition
  for (i = 0;i < this->nba;i++)
    {
      short ff = 0;
      Scalar dist;

      if (maret[3+i*4] == -1)
        ff = 1;
      else
        refa[i] = 0;
      
      a0.set (this->saca_sommet(maret[i*4]-1), this->saca_sommet(maret[1+i*4]-1),
              this->saca_triangle(maret[2+i*4]), refa[i], ff);

      this->add_arete (a0, i);
      dist = distance (a0.s[0]->c, a0.s[1]->c);
      if (lmin<dist) lmin=dist;
    }
  if (lmin==0) {
    cerr << "WARNING : degenerated mesh . minimal edge length is equal to 0" << endl;
    exit (1);
  }
   
  this->factor=escale;
  // mesh scaling;
  for (i=0; i<np; i++) {
    s[i].c=s[i].c*this->factor;
  }
  this->paso_malla=lmin;    
  // triangle definition
  for (i = 0;i < nt;i++)
    {
      for (j = 0;j < 3;j++)
        {
          ss[j] = this->saca_sommet (nu[j+3*i] - 1);
          aa[j] = this->saca_arete (aretr[j+3*i]);
          if (maret[2+aretr[j+i*3]*4] != i) 
            tt[j] = this->saca_triangle(maret[2+aretr[j+i*3]*4]);
          else 
            {
              if (maret[3+aretr[j+i*3]*4] != -1) 
                tt[j] = saca_triangle(maret[3+aretr[j+i*3]*4]);
              else 
                tt[j]=NIL;
            }
        }
      t0.set(ss,tt,aa,refe[i]);
      this->add_triangle(t0,i);
    }
  /*
   * free allocated memory
   */
  delete [] maret;
  delete [] trsom;
  delete [] refa;
  delete [] aretr;
  delete [] nu;
  delete [] cr;
  // computing boundary
  this->crea_frontera(); 
}

void Mallado_T0::write(ostream& os)
{ os <<"N. vertices = "<<nbs<<"; N. triangulos = "<<nbt
     <<"; N. aristas = "<<nba<<flush;
}
ostream& operator<<(ostream& os, Mallado_T0& ml_m) {ml_m.write (os); return os;}

void Mallado_T0:: write_all() {
  int i;
  cout<<endl<<"Mallado soporte de la Metrica con: ";
  cout<<(*this)<<endl<<endl;
  
  cout<<"  *******       VERTICES          *******"<<endl
      <<"                --------"<<endl<<endl;
  
  for (i=0; i<nbs; i++) cout<<s[i]<<endl;
  cout <<endl<<endl;
  cout<<"  *******       ARISTAS          *******"<<endl
      <<"                -------"<<endl<<endl;
  
  for ( i=0; i<nba; i++) cout<<a[i]<<endl;
  
  cout<<endl<<endl;
  cout<<"  *******       TRIANGULOS          *******"<<endl
      <<"                ----------"<<endl<<endl;
  
  for ( i=0; i<nbt; i++) cout<<t[i]<<endl;
  cout<<endl<<endl;
  cout<<"  *******       METRICA          *******"<<endl
      <<"                -------"<<endl<<endl;
  
  cout.setf(ios::scientific,ios::floatfield);
  
  for ( i=0; i<nbs; i++) {
    cout<<m[i]<<endl;
  }
  cout.setf(0,ios::floatfield);
  
}
/*  -------------------------------------------------------------------

                      SUBRRUTINA EPS_MALLA

         Proposito:  Calcula la distancia mas pequegna entre dos
                     nodos de la malla.
*/

void Mallado_T0::crea_eps_malla() {
  const Scalar infini=1e+30;
  if (paso_malla==0) {
    paso_malla=infini;
    for (int i=0; i<nba;i++) {
      paso_malla=MIN(paso_malla,distance((a[i].s[0])->c,(a[i].s[1])->c));
    }
  }
}

/*  ---------------------------------------------------------------------
                     SUBRRUTINA   FRONTERA 
      
        Proposito: Obtener todas las aristas de la frontera de un mallado 
        ---------  del tipo Mallado_T0 y ordenarlas para recorrerlas
                   en el sentido positivo de la frontera. La frontera
                   puede tener distintas componentes conexas.

       Entrada:    El mallado base apuntado por this.
       -------

       Salida:     Aristas ordenadas unas detras de otras segun el
       ------      sentido positivo. Cada componente conexa estara
                   formada por una lista de aristas.
*/

void  Mallado_T0::crea_frontera() {
  if (frontera==NIL) {
    
    FraristaT0_link* comp_conex;
    AristaT0_list* arete_list;
    AristaT0_link* arete;
    
    frontera=new Frontera_T0;
    if (frontera==NIL) ERROR();     
    for (int i=0; i<nba; i++) {
      if (a[i].frontera()) {    // la arista esta en la frontera
        a[i].s[0]->front=TRUE;
        a[i].s[1]->front=TRUE;
        
        arete=new AristaT0_link;
        if (arete==NIL) ERROR();
        arete->set(a[i],NIL);
        
        arete_list=new AristaT0_list;
        if (arete_list==NIL) ERROR();
        arete_list->append(arete);
        
        comp_conex=new FraristaT0_link;
        if (comp_conex==NIL) ERROR();
        comp_conex->set(arete_list,NIL,NIL);
        
        frontera->append(comp_conex);
        frontera->testeo();
      }
    }
  }
}


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

                       SUBRRUTINA METRICA

   Proposito:  Dado un punto cualquiera, busca el triangulo donde
   ---------   se encuentra el o su proyectado e interpola la metrica
               en dicho punto o su proyectado en funcion de la metrica
               dada en cada uno de los nodos del triangulo donde se
               situa.

   Entrada:    punto = Punto donde quiero calcular la metrica interpo
   -------             lada.
               t_ini = Posicion en el array el triangulo
                       inicial..

    Salida:    Metrica interpolada en el punto dado.
    ------     t_pos=Posicion en el array de triangulos
                     del tiangulo donde se encuentra el
                     proyectado.                                     

*/
Metrica  Mallado_T0::metrica(R2 punto, int p_ini, int& t_pos) {
  
  Triangulo_T0* t_fin,*t_ini;
  Metrica mt(0,0,0);
  R2 proyec;
  R2 c_bar;
  Scalar co[3];
  Scalar eps=paso_malla/3.;
  int j,err0;

  
  t_ini=this->saca_triangle(p_ini);
 
  t_fin=this->localiza(punto,proyec,t_ini,eps);
  t_pos=t_fin-t;
  c_bar=t_fin->coor_bar(proyec);
  co[0]=c_bar.x;
  co[1]=c_bar.y;
  co[2]=1-c_bar.x-c_bar.y;
  for (j=0; j<3; j++) {
    mt=mt+(t_fin->s[j]->m->inv(err0))*co[j];
  }
  mt=mt.inv(err0);
  mt.factor=1;
  
  return mt;
}
/* ------------------------------------------------------------------

                       SUBRRUTINA INTERPOLA_SOL

   Proposito:  Dado un punto cualquiera, busca el triangulo donde
   ---------   se encuentra el o su proyectado e interpola la solucion
               en dicho punto o su proyectado en funcion de la solucion
               dada en cada uno de los nodos del triangulo donde se
               situa.

   Entrada:    punto = Punto donde quiero calcular la solucion interpo
   -------             lada.
               t_ini = Triangulo inicial.
               solucion = array que contiene la solucion en cada vertice
               cont  = posicion en el array solucion_interp del punto.
               nsol  = numero de variables de mi solucion.

    Salida:    Solucion interpolada en dicho punto y que se guarda
    ------     en el array solucion_interp a partir de la posicion cont*nsol 
                 
                                                        

*/
void  Mallado_T0::interpola_sol(R2 punto, Triangulo_T0* t_ini, \
            Scalar* solucion, Scalar* sol_interp,int cont, int nsol) {

  Triangulo_T0* t_fin;
  Vertice_T0* s0_ini=this->saca_sommet(0);
  R2 proyec;
  R2 c_bar;
  Scalar co[3];
  Scalar eps=paso_malla/3.;
  int i,j,pos0;
  int pos[3];

  
  
  t_fin=this->localiza(punto,proyec,t_ini,eps);
  c_bar=t_fin->coor_bar(proyec);
  co[0]=c_bar.x;
  co[1]=c_bar.y;
  co[2]=1-c_bar.x-c_bar.y;
  for (i=0; i<3; i++) {
    pos[i]=t_fin->s[i]-s0_ini;
  }
  for (i=0; i<nsol; i++) {
    pos0=cont*nsol+i;
    sol_interp[pos0]=0;
    for (j=0; j<3; j++) {
      sol_interp[pos0]=sol_interp[pos0] +co[j]*(solucion[pos[j]*nsol+i]);
    }
  }
  
}  


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

                        SUBRRUTINA LOCALIZA

   Proposito:  Dado un punto localiza en que triangulo se encuentra
   ---------   o bien el o su proyectado.

   Entrada:    Mallado sobre el que buscar, apuntado por this.
   -------     pt= Punto a localizar.
               t_ini= Puntero sobre el triangulo inicial.
               eps = parametro de aproximacion.

   Salida:     triangle= Triangulo en el que se encuentra o bien
   ------      el punto o bien su proyectado.
               proyec=  Proyectado.


  NOTA: el parametro 'eps' juega un papel muy importante en la velocidad
        del algoritmo cuando este se utiliza para superficies  inmersas
        en el espacio tridimensional. 
        Este parametro va a indicar cuando podemos considerar que
        un punto esta "suficientemente proximo" a la superficie.
           Habra que hacer un estudio
        sobre el valor optimo de dicho parametro. Personalmente creo
        que tiene que venir dado en funcion de paso de malla. Tambien
        posiblemente, de la variacion local de los planos tangentes.

        Lo que vaya en doble barra se utiliza para el trazado. 
        (salida grafica)

*/

 Triangulo_T0* Mallado_T0::localiza(R2 pt , R2& proyec,\
                Triangulo_T0* t_ini, const Scalar& eps) {

   R2 pr[2],r_aux;
   Triangulo_T0* triangle,*t0,tr_aux, *tr[2];
   Scalar dist=0,d_front,dif;
   Scalar dista[2];
   Arista_T0 arete(NIL,NIL,NIL,0,0),arete_aux(NIL,NIL,NIL,0,0);
   Boolean encontrado=FALSE,flag_frontera=FALSE;
   Scalar eps1=MAX(eps,1e-1);
   int cont=0,c_marca=1;
   int lugar;
   
   t0=t_ini;                                
   t0->marca=clave;
   
   do {     
     triangle=triangle_sig(t0,pt,proyec,dist,arete);     
     if (triangle) {
       paso:
       if (triangle->marca==clave && c_marca<nbt)  { //por ahi he pasado antes.
         tr[0]=t0;
         tr[1]=triangle;
         dista[0]=d_triangle(*t0,pt,pr[0]);
         dista[1]=d_triangle(*triangle,pt,pr[1]);
         r_aux.set(dista[0],dista[1]);
         dist=minimo(r_aux,lugar);
         proyec=pr[lugar];
         triangle=tr[lugar];
         if (dist>eps) { //
           dista[0]=dist;
           while (cont<nbt && (t[cont].marca==clave || dista[0]>=dist)) {
             if (t[cont].marca!=clave) {
               t[cont].marca=clave;
               c_marca++;
               dista[0]=d_triangle(t[cont],pt,pr[0]);
             }
             cont++;
           }
           if (dista[0]<dist) { //sigo por ese triangulo
             dist=dista[0];
             proyec=pr[0];
             triangle=&t[cont-1];
           }
           else { // tengo la proyeccion.
             encontrado=TRUE;
           }
         }
         else { // la distancia es lo suficiente pequegna
           // para considerarlo como proyeccion.
           encontrado=TRUE;
         }        
       }
       else {
         c_marca++;
         triangle->marca=clave;
         if (c_marca>nbt) {
           dist=d_triangle(*triangle,pt,proyec);
           encontrado=TRUE;
         }                    
       }
       
       t0=triangle; 
     }
     else {
/*   El triangle es NIL. Con lo cual o tengo el proyectado o estoy
     en una arista de la frontera.
     Notemos que si hemos recorrido una vez la frontera no hay
     que volver a recorrerla (uso de flag_frontera) ya que una
     vez recorrida el triangulo resultante es el mas proximo, de
     todo los que estan en la frontera.
*/
       if (arete.s[0]!=NIL) { //arista en la frontera
         if (flag_frontera==FALSE) { // No hemos recorrido la frontera
           flag_frontera=TRUE;
           arete_aux=arete;
           d_front=frontera->d_frontera(pt,proyec,arete_aux,tr_aux,dist);
           dif=fabs(d_front-dist);
           if (arete==arete_aux || dif==0) {
             encontrado=TRUE;
             t0=arete_aux.tr;
             dist=d_front;
             triangle=t0;
           }
           else {
             triangle=arete_aux.tr; //sigo buscando por ese triangulo.
             goto paso;
           }
         }
         else { 
/* Ya hemos pasado por la frontera y hemos devuelto un triangulo, que
   es el mas proximo de todos los que se encuentran en la frontera.
   el programa ha testeado y  el triangulo encontrado
   no solo es el mas proximo de todos los de la frontera, sino de todos
   los del dominio. No hace falta volver a recorrer la frontera.
*/               
           encontrado=TRUE;
           triangle=t0;
         }   
       }
       else {
/*   Tenemos el proyectado que cae dentro del triangulo. */

         if (dist<eps1){  // distancia suf. pequegna como para
           // considerarlo proyeccion.
           encontrado=TRUE;
           triangle=t0;
         }
         else { // minimo local seguir buscando.
           dista[0]=dist;
           while (cont<nbt && (t[cont].marca==clave ||  dista[0]>=dist)) {
             if (t[cont].marca!=clave) {
               t[cont].marca=clave;
               c_marca++;
               dista[0]=d_triangle(t[cont],pt,pr[0]);
             }
             cont++;
           }
           if (dista[0]<dist) { //sigo por ese triangulo
             dist=dista[0];
             proyec=pr[0];
             t0=&t[cont-1];
             triangle=t0;
           }
           else { // tengo la proyeccion.
             encontrado=TRUE;
             triangle=t0;
           }
         }
       }
     }
   } while (encontrado==FALSE);
   clave++;
   return triangle;
 }


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

                SUBRRUTINA CALCULO FACTOR      

    
    proposito:   calcula el factor local que debe tener
                 la metrica para que el area de los
                 triangulos no se degenere demasiado.
                 calculara un factor en el caso que
                 en numero de puntos que haya que 
                 an~adir a una arista sea mayor que
                 tres.
*/



int Mallado_T0::calculo_factor() {
  Metrica* m0,*m1;
  R2 c0,c1;
  Scalar ff,esc;
  Scalar esc1=0;
  Scalar infini=1e+30,esc0=infini;
  Scalar f0=-1,f1=infini;
  Scalar temp;
  int i, num_iter=1;
  int n0,n1,n00,n11;
  int cont=0;
  if (nba>0) {
    for (i=0; i<nba; i++) {
      c0=a[i].s[0]->c;
      c1=a[i].s[1]->c;
      m0=a[i].s[0]->m;
      m1=a[i].s[1]->m;
      //m0->factor=1;
      //m1->factor=1;
      ff=distance0(c0,*m0,c1,*m1);          
      if (ff>f0) f0=ff;
      if (ff<f1) f1=ff;
      // atencion factor ok= 2.5
      if (ff>2.5) {
        cont++;
        esc=2.5/ff;
        if (esc<esc0) esc0=esc;
        if (m0->factor==1) 
          m0->factor=esc;
        else 
          if (m0->factor>esc) m0->factor=esc;
        if (m1->factor==1)
          m1->factor=esc;
        else
          if (m1->factor>esc) m1->factor=esc;
      }
      else {
        if (m0->factor==1) m0->factor=1;
        if (m1->factor==1) m1->factor=1;                     
      }
    }
  }
  
  if (cont<int(nba/3) && f0<7.0 ) {
    for (i=0; i<nbs; i++) {
      s[i].m->factor=1;
    }
    num_iter=1;
  }
  else {    
    if (esc0==infini) 
      n0=1;
    else {
      temp=-log(esc0)/log(2);
      n00=int(temp);
      if (fabs(temp-Scalar(n0))>1e-4) n00++;
      n0=n00+1;
    }
    if (esc1==0) 
      n1=1;
    else {
      temp=log(esc1)/log(1.5);
      n11=int(temp);
      if (fabs(temp-Scalar(n1))>1e-4) n11++;
      n1=n11+1;
    }
    num_iter=MAX(n0,n1);
  }
  return num_iter;
}



Boolean Mallado_T0::cambio_factor(int paso,int num_iter) {
  Scalar temp,incr;
  int i,cont;
  Boolean terminar=FALSE;
  if (num_iter>1) {
    temp=.8/Scalar(num_iter-1);
    incr=1+temp*Scalar(paso);
  }
  if (paso) {
    cont=0;
    for (i=0;i<nbs; i++) {
      if (paso==(num_iter-1)) 
        m[i].factor=1;
      else {
        if (m[i].factor==1.0) cont++;
        if (m[i].factor<=1) {
          m[i].factor=m[i].factor*2;
          if (m[i].factor>1) m[i].factor=1.0;
        }
        else {
          m[i].factor=m[i].factor/1.5; 
          if (m[i].factor<1) m[i].factor=1.0;
        }
      }
    }
    if (cont==nbs) terminar=TRUE;
  }
  return terminar;
}
/*
-----------------------------------------------------------------------------

                             SUBRRUTINA P_FIJOS

      Localiza los puntos fijos del Mallado_T0.
*/

void Mallado_T0::p_fijos (int angulo) {

  typedef Triangulo_T0* Ptriangulo;
  typedef Arista_T0* Parista;
  Triangulo_T0_dlist* t_vec;
  Triangulo_T0_dlink* t_lk;

  FraristaT0_link* comp_conex;
  AristaT0_list* arete_list;
  AristaT0_link* arete,*arete_sig,*arete_ant; 
  Triangulo_T0* t0,*t1,*tini;
  Parista* aret;
  Arista_T0* a0;
  Vertice_T0* s0,*s1;
  R2 tang0,tang1;
  R2 rc0,rc1,rc2,rc3;
  int i,nbcc,num;
  int ang0,ang1,ang;
  int referencia,ref0,ref1,refini;
  int p_inv[3];
  Boolean fin=FALSE;
  Boolean fr;
  p_inv[0]=2;p_inv[1]=0;p_inv[2]=1;
 

/*
  
  BUSCO LOS PUNTOS FIJOS DE LA FRONTERA.
  
  */
 
  if (frontera) {
    nbcc=frontera->num_elem();
    comp_conex=frontera->principio();//cojo la primera componente
                                // conexa de la frontera.
    
    for (i=0; i<nbcc; i++) {
      arete_list=comp_conex->Aretelist; //cojo la lista de aristas de la
                                // i-esima comp. conexa.
      arete=arete_list->principio();  //cojo el principio de la lista de
                                // aristas.
      arete_ant=arete_list->fin();
      ang0=int(angle(arete_ant->a,arete->a));
      referencia=arete->a->ref;
      fin=FALSE;
      while (fin==FALSE) {
        arete_sig=arete->sig();
        if (arete_sig==NIL) {
          arete_sig=arete_list->principio();
          fin=TRUE;
        }    
        rc1=arete->a->s[0]->c;
        rc2=arete->a->s[1]->c;
        rc0=arete_ant->a->s[0]->c;
        rc3=arete_sig->a->s[1]->c;
        s0=arete->a->s[0];
        s1=arete->a->s[1];
        if ((referencia != s0->ref) && (s0->ref>=0)) {
          if (s0->ref>0)
            s0->ref=-s0->ref;
          else { 
            if (s0->ref==0) s0->ref=-999;
          }
        }
        if ((referencia != s1->ref) && (s1->ref>=0)) {
          if (s1->ref>0)
            s1->ref=-s1->ref;
          else { 
            if (s1->ref==0) s1->ref=-999;
          }
        }
        ang1=int(angle(arete->a,arete_sig->a));
        if (ang0<=angulo) 
          tang0=rc2-rc0;
        else { 
          tang0=rc2-rc1;
          if (s0->ref>0)
            s0->ref=-s0->ref;
          else{
            if (s0->ref==0) s0->ref=-999;
          }
        }
        if (ang1<=angulo)
          tang1=rc3-rc1;
        else {
          tang1=rc2-rc1;
          if (s1->ref>0)
            s1->ref=-s1->ref;
          else {
            if (s1->ref==0) s1->ref=-999;               
          }
        }
        
        if (referencia!=arete_sig->a->ref) {
          if (s1->ref>0)
            s1->ref=-s1->ref;
          else { 
            if (s1->ref==0) s1->ref=-999;
          }
          referencia=arete_sig->a->ref;
        } 
        arete_ant=arete;
        arete=arete_sig;
        ang0=ang1;
      }
      comp_conex=comp_conex->sig();
    }
  }

/*

  BUSCO LOS PUNTOS FIJOS INTERNOS.
  
  */


  
  for (i=0; i<nbs; i++) {
    s0=&s[i];    
    fr=FALSE;
    t_vec=new Triangulo_T0_dlist;
    if (t_vec==NIL) ERROR();
    t_vecinos(s0,*t_vec);
    num=t_vec->num_elem();
    if (num>=30) {
      if (s0->ref>0)
        s0->ref=-s0->ref;
      else
        if (s0->ref==0) s0->ref=-999;
    }
    aret=new Parista[num];
    if (aret==NIL) ERROR();
    num=0;
    t_lk=t_vec->principio();
    tini=t_lk->t;
    a0=tini->a[(tini->sommet(s0))];
    fr=a0->front;
    refini=tini->ref;
    ref0=refini;
    t0=tini;
    while (t_lk) {
      t_lk=t_lk->sig();
      if (t_lk) {
        t1=t_lk->t;
        ref1=t1->ref;
      }
      else {
        if (fr==FALSE)
          ref0=refini;
        else
          ref0=ref1;
      }
      if (ref0!=ref1) {
        aret[num]=t0->a[p_inv[(t0->sommet(s0))]];
        num++;
      }
      t0=t1;
      ref0=ref1;  
    }
    if (fr==TRUE && num>0) {
      if (s0->ref>0) 
        s0->ref=-s0->ref;
      else { 
        if (s0->ref==0) s0->ref=-999;
      }
    }
    else {
      if (num>0 && num!=2) {
        if (s0->ref>0) 
          s0->ref=-s0->ref;
        else {
          if (s0->ref==0) s0->ref=-999; 
        }          
      }
      if (num==2) {
        ang=int(angle(aret[0],aret[1]));
        if (ang>angulo) {
          if (s0->ref>0) 
            s0->ref=-s0->ref;
          else {
            if (s0->ref==0) s0->ref=-999;
          }
        }
      }
    }
    delete[] aret;
    delete t_vec;
  }
}

int Mallado_T0::nbt_appx() {
 
  Metrica mmtr0;
  Scalar snbt=0.0,d0,rest;
  Scalar d00,dmin,dmax,snbt0,snbt1;
  int pos0,pos1,i,j,err;
  int sig[3]={1,2,0};

  snbt0=0.0;
  snbt1=0.0;
  for (i=0; i<nbt; i++) {
    mmtr0.set(0,0,0);
    d0=0.0;
    dmax=0.0;
    dmin=1e+30;
    for (j=0; j<3; j++) {
      pos0=t[i].s[j]-&s[0];
      mmtr0=mmtr0+m[pos0].inv(err)/3.0;
      pos1=t[i].s[sig[j]]-&s[0];
      d00=distance(t[i].s[j]->c,m[pos0],t[i].s[sig[j]]->c,m[pos1]);
      dmax=MAX(dmax,d00);
      dmin=MIN(dmin,d00);
      d0+=d00/3.0;
    }
    mmtr0=mmtr0.inv(err);
    rest=d0-int(d0/1.4);
    if (rest>=0.75 && rest<1.3) rest=1;
    d0=int(d0/1.4)+rest;
    snbt0+=d0*d0;
    rest=dmin-int(dmin/1.4);
    if (rest>=0.75 && rest<1.3) rest=1;
    dmin=int(dmin/1.4)+rest;
    rest=dmax-int(dmax/1.4);
    if (rest>=0.75 && rest<1.3) rest=1;
    dmax=int(dmax/1.4)+rest;
    snbt1+=dmin*dmax;
    snbt+=sqrt(mmtr0.determinante())/sqrt(t[i].metrica().determinante());
  }
  snbt=snbt+snbt/10.0;
  
  cout<<"Approximated number of triangles to be generated-1:"<<int(snbt)<<endl;
  cout<<"Approximated number of triangles to be generated-2:"<<int(snbt0)<<endl;
  cout<<"Approximated number of triangles to be generated-3:"<<int(snbt1)<<endl;
  return int(snbt);
}

void Mallado_T0::write(char* name) {

  R2 cor;
  int i,j,paso=0,reft,refs;
  
  OPEN(escritura, name);
  
  //  Escritura del N. de vertices y triangulos.
  escritura <<" "<<nbs<<" "<<nbt<<"  -- nbs,nbt"<<endl; 
  
  for (i=0; i<nbt; i++) {
    for (j=0; j<3; j++) {
      escritura<<setw(7)<<t[i].s[j]-&s[0]+1;
    }
    escritura<<"     ";
    if (paso) {
      paso=0; escritura<<endl;}
    else
      paso++;
  }
  if (paso) escritura<<endl;
  
  paso=0;
  escritura.setf(ios::scientific,ios::floatfield);
  for (i=0; i<nbs; i++) {
    cor=s[i].c/factor;
    escritura<<setw(15)<<cor.x<<setw(15)<<cor.y<<"       ";
    if (paso) {
      paso=0; escritura<<endl;}
    else
      paso++;
  }
  if (paso) escritura<<endl;
  
  paso=0;
  for (i=0; i<nbt; i++) {
    reft=t[i].ref;
    if (reft<0) reft=-reft;
    if (reft==999) reft=0;
    escritura<<setw(8)<< reft;
    if (paso==9) {
      paso=0; escritura<<endl;}
    else
      paso++;
  }
  if (paso) escritura<<endl;
  
  paso=0;
  for (i=0; i<nbs; i++) {
    refs=s[i].ref;
    if (refs<0 && refs!=-999) refs=-refs;
    if (refs==-999 || refs==999) refs=0;
    escritura<<setw(8)<< refs;
    if (paso==9) {
      paso=0; escritura<<endl;}
    else
      paso++;
  }
  if (paso) escritura<<endl;
  
  escritura.close();
}
