unit Noise;
interface
uses
	Math;

const
	kNoisekPerumationMax = 256;

type
	TNoiseValue = byte;
	TNoiseSeedArray = array[0..kNoisekPerumationMax-1] of TNoiseValue;
	TNoiseFloat = double;
	
type
	TNoise = class (TObject)
		public
			constructor Create; overload;
			constructor Create (seed: TNoiseSeedArray); overload;
			
			function GetValue (x, y, z: TNoiseFloat): TNoiseFloat; overload;
			function GetValue (x, y, z: TNoiseFloat; octaves: integer; persistence: TNoiseFloat): TNoiseFloat; overload;
			function GetNoise (x, y: integer; width, height: integer; scale: TNoiseFloat; frequency: integer): TNoiseFloat; overload;

		private
			repeatValue: integer;
			p: array[0..(kNoisekPerumationMax * 2)-1] of TNoiseValue;
			
			function Inc (num: integer): integer; inline;
			function Grad (hash: integer; x, y, z: TNoiseFloat): TNoiseFloat; inline;
			function Fade (t: TNoiseFloat): TNoiseFloat; inline;
			function Lerp (a, b, x: TNoiseFloat): TNoiseFloat; inline;
	end;

function RandomNoiseSeed (seed: cardinal = 0): TNoiseSeedArray;

implementation


function RandomNoiseSeed (seed: cardinal = 0): TNoiseSeedArray; 
var
	i: integer;
begin		
	for i := 0 to kNoisekPerumationMax - 1 do
		result[i] := Random(kNoisekPerumationMax);
end;

function TNoise.GetValue (x, y, z: TNoiseFloat; octaves: integer; persistence: TNoiseFloat): TNoiseFloat;
var
	total: TNoiseFloat = 0;
	frequency: TNoiseFloat = 1;
	amplitude: TNoiseFloat = 1;
	maxValue: TNoiseFloat = 0; // Used for normalizing result to 0.0 - 1.0
	i: integer;
begin
	for i := 0 to octaves - 1 do
		begin
			total += GetValue(x * frequency, y * frequency, z * frequency) * amplitude;
			maxValue += amplitude;
			amplitude *= persistence;
      frequency *= 2;
		end;
	result := total/maxValue;
end;

function TNoise.GetNoise (x, y: integer; width, height: integer; scale: TNoiseFloat; frequency: integer): TNoiseFloat; 
var
	nx, ny: TNoiseFloat;
begin
	nx := x/width - 0.5; 
	ny := y/height - 0.5;
	result := GetValue(nx * scale, ny * scale, 0, frequency, 0.5) / 2 + 0.5;
end;

function TNoise.GetValue (x, y, z: TNoiseFloat): TNoiseFloat;
function FMod(const a, b: TNoiseFloat): TNoiseFloat;
begin
  result:= a-b * trunc(a/b);
end;
var
	xi, yi, zi: integer;
	xf, yf, zf: TNoiseFloat;
	u, v, w: TNoiseFloat;
	aaa, aba, aab, abb, baa, bba, bab, bbb: integer;
	x1, x2, y1, y2: TNoiseFloat;
begin
	// If we have any repeat on, change the coordinates to their "local" repetitions
	if (repeatValue > 0) then			
		begin
			x := FMod(x, repeatValue);
			y := FMod(y, repeatValue);
			z := FMod(z, repeatValue);
			// ??? mod overloading for singles in trunk 3.1.1
			{x := x mod repeatValue;
			y := y mod repeatValue;
			z := z mod repeatValue;}
		end;
	
	xi := Floor(x) and 255;								// Calculate the "unit cube" that the point asked will be located in
	yi := Floor(y) and 255;								// The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
	zi := Floor(z) and 255;								// plus 1.  Next we calculate the location (from 0.0 to 1.0) in that cube.
	xf := x-Floor(x);											// We also fade the location to smooth the result.
	yf := y-Floor(y);
	zf := z-Floor(z);
	u := Fade(xf);
	v := Fade(yf);
	w := Fade(zf);
	
	aaa := p[p[p[    xi ]+    yi ]+    zi ];
	aba := p[p[p[    xi ]+self.Inc(yi)]+    zi ];
	aab := p[p[p[    xi ]+    yi ]+self.Inc(zi)];
	abb := p[p[p[    xi ]+self.Inc(yi)]+self.Inc(zi)];
	baa := p[p[p[self.Inc(xi)]+    yi ]+    zi ];
	bba := p[p[p[self.Inc(xi)]+self.Inc(yi)]+    zi ];
	bab := p[p[p[self.Inc(xi)]+    yi ]+self.Inc(zi)];
	bbb := p[p[p[self.Inc(xi)]+self.Inc(yi)]+self.Inc(zi)];
	
	x1 := Lerp(	Grad(aaa, xf  , yf  , zf),				// The gradient function calculates the dot product between a pseudorandom
				Grad(baa, xf-1, yf  , zf),							// gradient vector and the vector from the input coordinate to the 8
				u);																			// surrounding points in its unit cube.
	x2 := Lerp(	Grad(aba, xf  , yf-1, zf),				// This is all then lerped together as a sort of weighted average based on the faded (u,v,w)
				Grad(bba, xf-1, yf-1, zf),							// values we made earlier.
		          u);
	y1 := Lerp(x1, x2, v);

	x1 := Lerp(	Grad(aab, xf  , yf  , zf-1),
				Grad(bab, xf-1, yf  , zf-1),
				u);
	x2 := Lerp(	Grad(abb, xf  , yf-1, zf-1),
	          	Grad(bbb, xf-1, yf-1, zf-1),
	          	u);
	y2 := Lerp(x1, x2, v);
	
	result := (Lerp(y1, y2, w)+1)/2;						// For convenience we bound it to 0 - 1 (theoretical min/max before is -1 - 1)
end;

function TNoise.Inc (num: integer): integer;
begin
	num += 1;
	if repeatValue > 0 then
		num := num mod repeatValue;
	result := num;
end;

// http://riven8192.blogspot.com/2010/08/calculate-perlinnoise-twice-as-fast.html
function TNoise.Grad (hash: integer; x, y, z: TNoiseFloat): TNoiseFloat;
begin
	case (hash and $F) of
		$0:
			result := x + y;
		$1:
			result := -x + y;
		$2:
			result := x - y;
		$3:
			result := -x - y;
		$4:
			result := x + z;
		$5:
			result := -x + z;
		$6:
			result := x - z;
		$7:
			result := -x - z;
		$8:
			result := y + z;
		$9:
			result := -y + z;
		$A:
			result := y - z;
		$B:
			result := -y - z;
		$C:
			result := y + x;
		$D:
			result := -y + z;
		$E:
			result := y - x;
		$F:
			result :=	-y - z;
		otherwise
			result := 0; // never happens
	end;
end;

{
function TNoise.Grad (hash: integer; x, y, z: TNoiseFloat): TNoiseFloat;
var
	h: integer;
	u, v: TNoiseFloat;
begin
	h := hash and 15;									// Take the hashed value and take the first 4 bits of it (15 == 0b1111)
	
	if h < 8 then
		u := x
	else
		u := y;

	if h < 4 then
		v := y
	else if (h = 12) or (h = 14) then
		v := x
	else
		v := z;
	
	if h and 1 = 0 then
		result := u
	else
		result := -u;
		
	if h and 2 = 0 then
		result := result + v
	else
		result := result - v;
end;
}

function TNoise.Fade (t: TNoiseFloat): TNoiseFloat; 
begin
	// Fade function as defined by Ken Perlin.  This eases coordinate values
	// so that they will "ease" towards integral values.  This ends up smoothing
	// the final output.
	result := t * t * t * (t * (t * 6 - 15) + 10);			// 6t^5 - 15t^4 + 10t^3
end;

function TNoise.Lerp (a, b, x: TNoiseFloat): TNoiseFloat; 
begin
	result := a + x * (b - a);
end;

constructor TNoise.Create;
begin	
	Create(RandomNoiseSeed);
end;

constructor TNoise.Create (seed: TNoiseSeedArray);
var
	i: integer;
begin	
	repeatValue := -1;
	for i := 0 to high(p) do
		p[i] := seed[i mod kNoisekPerumationMax];
end;

end.
