#include <smoothers/CTaubinSmoother.h>

//=====================================================================================================================
CTaubinSmoother::CTaubinSmoother( double factor1, double factor2, int iterations, int layers )
{
    dFactor1 = factor1;
    dFactor2 = factor2;
    iIterations = iterations;
}

//=====================================================================================================================
CTaubinSmoother::~CTaubinSmoother()
{
}

//=====================================================================================================================
bool    CTaubinSmoother::smooth( CSmoothingMesh * mesh )
{  
  vctl::MCTriS & smoothed_mesh = *mesh;
  double smooth_factor_1  = dFactor1;
  double smooth_factor_2  = dFactor2;
  int smooth_cycle = iIterations;


  vctl::MCVertex                           * actual_vertex = (smoothed_mesh.GetVerticeS())->GetFirst();      // ukazatel na aktualni vrchol site
  std::vector<vctl::MCPoint3D>             point_vector;           // vektor bodu s novymi pozicemi vrcholu, zachovani poradi podle vrcholu
  std::vector<vctl::MCPoint3D>::iterator   point_vector_iter;      // iterator vektoru bodu
  double                                   factors[2];             // faktory vyhlazeni podle Taubina


  // inicializace vektoru bodu novych pozic vrcholu
  point_vector.resize(smoothed_mesh.GetVerticeS()->GetNumber());

  // inicializace pole faktoru vyhlazeni
  factors[0] = smooth_factor_1;
  // prepocet hodnot faktoru vyhlazeni
  factors[1] = 1 / (smooth_factor_2 - (1/smooth_factor_1));

  // cyklus opakovani vyhlazovani site
  for (int smooth_iter = 0; smooth_iter < smooth_cycle; smooth_iter++)
  {
    // cyklus pruchodu site pro dva parametry vyhlazeni
    for (int step = 0; step < 2; step++)
    {
      // inicializace iteratoru novych souradnic na zacatek
      point_vector_iter = point_vector.begin();
      // nastaveni ukazatele na prvni vrchol site a iteratoru vektoru
      actual_vertex = (smoothed_mesh.GetVerticeS())->GetFirst();

      // cyklus vrcholu site pro vypocet jejich nove pozice
      while (actual_vertex != NULL)
      {
        // vypocet nove pozice pro aktualni vrchol podle okolnich tri
        if ( !this->smoothVertex( mesh, actual_vertex, *point_vector_iter, factors[step])) return false;

        // ziskani dalsiho vrcholu site a dalsiho bodu
        actual_vertex = actual_vertex->GetNext();
        ++point_vector_iter;
      }

      // nastaveni ukazatele na prvni vrchol site a iteratoru vektoru
      actual_vertex = (smoothed_mesh.GetVerticeS())->GetFirst();
      point_vector_iter = point_vector.begin();

      // cyklus vrcholu site pro nastaveni vypoctene pozice
      while (actual_vertex != NULL)
      {
        // nastaveni vypoctene nove pozice vrcholu jako aktualni
        actual_vertex->SetPoint3D(*point_vector_iter);

        // ziskani dalsiho vrcholu site a dalsiho bodu
        actual_vertex = actual_vertex->GetNext();
        ++point_vector_iter;
      }
    }
  }

  return true;
}

//=====================================================================================================================
bool    CTaubinSmoother::smoothVertex( CSmoothingMesh * mesh,
                                       vctl::MCVertex * actual_vertex,
                                       vctl::MCPoint3D & smoothed_point,
                                       double smooth_factor)
{
  std::vector<vctl::MCVertex *>             neighbour_vertex;               // vektor sousednich vrcholu pro aktualni vrchol
  std::vector<vctl::MCVertex *>::iterator   neighbour_vertex_iter;          // iterator vektoru sousednich vrcholu
  double                                    dx = 0, dy = 0, dz = 0;         // point coordinate accumulation variables


  // inicializace vyhlazeneho vrcholu
  smoothed_point.SetXYZ(0.0, 0.0, 0.0);

  // ziskani okolnich vrcholu pro aktualni vrchol
  mesh->getNeighbourVert( actual_vertex, neighbour_vertex, 1 );

  // cyklus okolnich vrcholu
  for (neighbour_vertex_iter = neighbour_vertex.begin(); neighbour_vertex_iter != neighbour_vertex.end(); ++neighbour_vertex_iter)
  {
    // point coordinate accumulation
    dx += ((*neighbour_vertex_iter)->GetX() - actual_vertex->GetX());
    dy += ((*neighbour_vertex_iter)->GetY() - actual_vertex->GetY());
    dz += ((*neighbour_vertex_iter)->GetZ() - actual_vertex->GetZ());
  }

  // vypocet souradnic vyhlazenho vrcholu
  smoothed_point.SetX( actual_vertex->GetX() + (smooth_factor * (dx / neighbour_vertex.size())) );
  smoothed_point.SetY( actual_vertex->GetY() + (smooth_factor * (dy / neighbour_vertex.size())) );
  smoothed_point.SetZ( actual_vertex->GetZ() + (smooth_factor * (dz / neighbour_vertex.size())) );

  return true;
}
