// voronoi.cpp
//
// Copyright (C) 2003, 2004 Jason Bevins
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or (at
// your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
// License (COPYING.txt) for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// The developer's email is jlbezigvins@gmzigail.com (for great email, take
// off every 'zig'.)
//

#include "mathconsts.h"
#include "module/voronoi.h"

using namespace noise::module;

Voronoi::Voronoi ():
  Module (GetSourceModuleCount ()),
  m_displacement   (DEFAULT_VORONOI_DISPLACEMENT),
  m_enableDistance (false                       ),
  m_frequency      (DEFAULT_VORONOI_FREQUENCY   ),
  m_seed           (DEFAULT_VORONOI_SEED        )
{
}

double Voronoi::GetValue (double x, double y, double z) const
{
  // This method could be more efficient by caching the seed values.  Fix
  // later.

  x *= m_frequency;
  y *= m_frequency;
  z *= m_frequency;

  int xInt = (x > 0.0? (int)x: (int)x - 1);
  int yInt = (y > 0.0? (int)y: (int)y - 1);
  int zInt = (z > 0.0? (int)z: (int)z - 1);

  double minDist = 2147483647.0;
  double xCandidate = 0;
  double yCandidate = 0;
  double zCandidate = 0;

  // Inside each unit cube, there is a seed point at a random position.  Go
  // through each of the nearby cubes until we find a cube with a seed point
  // that is closest to the specified position.
  for (int zCur = zInt - 2; zCur <= zInt + 2; zCur++) {
    for (int yCur = yInt - 2; yCur <= yInt + 2; yCur++) {
      for (int xCur = xInt - 2; xCur <= xInt + 2; xCur++) {

        // Calculate the position and distance to the seed point inside of
        // this unit cube.
        double xPos = xCur + ValueNoise3D (xCur, yCur, zCur, m_seed    );
        double yPos = yCur + ValueNoise3D (xCur, yCur, zCur, m_seed + 1);
        double zPos = zCur + ValueNoise3D (xCur, yCur, zCur, m_seed + 2);
        double xDist = xPos - x;
        double yDist = yPos - y;
        double zDist = zPos - z;
        double dist = xDist * xDist + yDist * yDist + zDist * zDist;

        if (dist < minDist) {
          // This seed point is closer to any others found so far, so record
          // this seed point.
          minDist = dist;
          xCandidate = xPos;
          yCandidate = yPos;
          zCandidate = zPos;
        }
      }
    }
  }

  double value;
  if (m_enableDistance) {
    // Determine the distance to the nearest seed point.
    double xDist = xCandidate - x;
    double yDist = yCandidate - y;
    double zDist = zCandidate - z;
    value = (sqrt (xDist * xDist + yDist * yDist + zDist * zDist)
      ) * SQRT_3 - 1.0;
  } else {
    value = 0.0;
  }

  // Return the calculated distance with the displacement value applied.
  return value + (m_displacement * (double)ValueNoise3D (
    (int)(floor (xCandidate)),
    (int)(floor (yCandidate)),
    (int)(floor (zCandidate))));
}
