// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkPerlinNoise.h"
#include "vtkObjectFactory.h"
#include <cassert>
#include <cmath>

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkPerlinNoise);

// These functions are from Greg Ward's recursive implementation in
// Graphics Gems II.  I've kept the names the same for instructional
// purposes, and only changed things where optimizations could be made
// or where correctness fixes were necessary.

static double hermite(double p0, double p1, double r0, double r1, double t)
{
  double tt = t * t;

  return (p0 * ((2.0 * t - 3.0) * tt + 1.0) + p1 * (-2.0 * t + 3.0) * tt +
    r0 * ((t - 2.0) * t + 1.0) * t + r1 * (t - 1.0) * tt);
}

// assumes 32 bit ints, but so it seems does VTK
static double frand(unsigned int s)
{
  s = (s << 13) ^ s;
  s = (s * (s * s * 15731 + 789221) + 1376312589) & VTK_INT_MAX;

  return 1.0 - double(s) / (VTK_INT_MAX / 2 + 1);
}

static void rand3abcd(int x, int y, int z, double outv[4])
{
  outv[0] = frand(67 * x + 59 * y + 71 * z);
  outv[1] = frand(73 * x + 79 * y + 83 * z);
  outv[2] = frand(89 * x + 97 * y + 101 * z);
  outv[3] = frand(103 * x + 107 * y + 109 * z);
}

static void interpolate(double f[4], int i, int n, int xlim[3][2], const double xarg[3])
{
  double f0[4], f1[4];

  if (n == 0)
  {
    rand3abcd(xlim[0][i & 1], xlim[1][(i >> 1) & 1], xlim[2][i >> 2], f);
    return;
  }
  n--;
  assert((n >= 0) && (n <= 2));
  interpolate(f0, i, n, xlim, xarg);
  interpolate(f1, i | (1 << n), n, xlim, xarg);

  f[0] = (1.0 - xarg[n]) * f0[0] + xarg[n] * f1[0];
  f[1] = (1.0 - xarg[n]) * f0[1] + xarg[n] * f1[1];
  f[2] = (1.0 - xarg[n]) * f0[2] + xarg[n] * f1[2];
  f[3] = hermite(f0[3], f1[3], f0[n], f1[n], xarg[n]);
}

static void perlinNoise(double x[3], double noise[4])
{
  double xarg[3];
  int xlim[3][2];

  xlim[0][0] = int(floor(x[0]));
  xlim[1][0] = int(floor(x[1]));
  xlim[2][0] = int(floor(x[2]));

  xlim[0][1] = xlim[0][0] + 1;
  xlim[1][1] = xlim[1][0] + 1;
  xlim[2][1] = xlim[2][0] + 1;

  xarg[0] = x[0] - xlim[0][0];
  xarg[1] = x[1] - xlim[1][0];
  xarg[2] = x[2] - xlim[2][0];

  interpolate(noise, 0, 3, xlim, xarg);
}

vtkPerlinNoise::vtkPerlinNoise()
{
  this->Frequency[0] = 1.0;
  this->Frequency[1] = 1.0;
  this->Frequency[2] = 1.0;

  this->Phase[0] = 0.0;
  this->Phase[1] = 0.0;
  this->Phase[2] = 0.0;

  this->Amplitude = 1.0;
}

double vtkPerlinNoise::EvaluateFunction(double x[3])
{
  double xd[3];
  double noise[4];

  xd[0] = x[0] * this->Frequency[0] - this->Phase[0] * 2.0;
  xd[1] = x[1] * this->Frequency[1] - this->Phase[1] * 2.0;
  xd[2] = x[2] * this->Frequency[2] - this->Phase[2] * 2.0;
  perlinNoise(xd, noise);
  return noise[3] * this->Amplitude;
}

// Evaluate PerlinNoise gradient.
void vtkPerlinNoise::EvaluateGradient(double* vtkNotUsed(x), // Was x[3]
  double n[3])
{
  // contrary to the paper, the vector computed as a byproduct of
  // the Perlin Noise computation isn't a gradient;  it's a tangent.
  // Doing this right will take some work.
  n[0] = 0.0;
  n[1] = 0.0;
  n[2] = 0.0;
}

void vtkPerlinNoise::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "Amplitude: " << this->Amplitude << "\n";
  os << indent << "Frequency: (" << this->Frequency[0] << ", " << this->Frequency[1] << ", "
     << this->Frequency[2] << ")\n";

  os << indent << "Phase: (" << this->Phase[0] << ", " << this->Phase[1] << ", " << this->Phase[2]
     << ")\n";
}
VTK_ABI_NAMESPACE_END
