from random import random
from time import time
from math import floor

# Ken Perlin's "Improved Noise"
# http://mrl.nyu.edu/~perlin/noise/
# http://research.thenerdtank.com/therandomuniverse/classes/Perlin.class.txt

class Perlin:
	p = [0 for p in range(512)]
	seed = None
	permutation = [151,160,137,91,90,15,
	131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
	190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
	88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
	77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
	102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
	135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
	5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
	223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
	129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
	251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
	49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
	138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]
	default_size = 64;
	
	def __init__(self, seed = None):
		# Populate the permutation array
		for i in range(256):
			self.p[256 + i] = self.p[i] = self.permutation[i]
		
		# set the seed
		if not seed:
			self.seed = time()
		else:
			self.seed = seed
	
	def GetSeed(self):
		return self.seed
	
	def SetSeed(self, seed):
		self.seed = seed
	
	def Noise(self, x, y, z, size = None):
		if not size:
			size = self.default_size
		
		# set the initial value and initial size
		value = 0.0
		initialSize = size
		
		# Add finer and finer hues of smoothed noise together
		while(size >= 1):
			value += self.SmoothNoise(float(x) / size, float(y) / size, float(z) / size) * size;
			size /= 2.0;
		
		# Return the result over the initial size
		return value / initialSize;
	
	# This function determines what cube the point passed resides in
	# and determines its value.
	def SmoothNoise(self, x, y, z):
		# Offset each coordinate by the seed value
		x += self.seed
		y += self.seed
		z += self.seed
		
		orig_x = x
		orig_y = y
		orig_z = z
		
		X1 = int(floor(x)) & 255                  # FIND UNIT CUBE THAT
		Y1 = int(floor(y)) & 255                  # CONTAINS POINT.
		Z1 = int(floor(z)) & 255
		x -= floor(x)                             # FIND RELATIVE X,Y,Z
		y -= floor(y)                             # OF POINT IN CUBE.
		z -= floor(z)
		u = self.Fade(x)                        # COMPUTE FADE CURVES
		v = self.Fade(y)                        # FOR EACH OF X,Y,Z.
		w = self.Fade(z)
		
		A  = self.p[X1] + Y1
		AA = self.p[A] + Z1
		AB = self.p[A + 1] + Z1      # HASH COORDINATES OF
		B  = self.p[X1 + 1] + Y1
		BA = self.p[B] + Z1
		BB = self.p[B+1] + Z1      # THE 8 CUBE CORNERS,
		
		# Interpolate between the 8 points determined
		result = self.Lerp(w, self.Lerp(v, self.Lerp(u, self.Grad(self.p[AA], x, y, z), # AND ADD
		self.Grad(self.p[BA], x - 1, y, z)), # BLENDED
		self.Lerp(u, self.Grad(self.p[AB], x, y - 1, z),  # RESULTS
		self.Grad(self.p[BB], x - 1, y - 1, z))), # FROM  8
		self.Lerp(v, self.Lerp(u, self.Grad(self.p[AA + 1], x, y, z - 1),  # CORNERS
		self.Grad(self.p[BA + 1], x - 1, y, z - 1)), # OF CUBE
		self.Lerp(u, self.Grad(self.p[AB + 1], x, y - 1, z - 1),
		self.Grad(self.p[BB + 1], x - 1, y - 1, z - 1))))
		
		return result
	
	def Fade(self, t):
		return t * t * t * ((t * ((t * 6) - 15)) + 10)
	
	def Lerp(self, t, a, b):
		# Make a weighted interpolaton between points
		return a + t * (b - a)
	
	def Grad(self, hash, x, y, z):
		h = hash & 15                     # CONVERT LO 4 BITS OF HASH CODE
		u = h < 8 and x or y                 # INTO 12 GRADIENT DIRECTIONS.
		v = h < 4 and y or (h == 12 or (h == 14 and x or z))
		
		return ((h & 1) == 0 and u or -u) + ((h & 2) == 0 and v or -v)
	
	# One dimension of noise
	def Noise1D(self, x):
		x += self.seed
		
		value = 0.0
		initialSize = size = 3
		
		while(size >= 1):
			value += self.SmoothNoise(x * 3 / size, 100 / size, 100 / size)
			size -= 1
		
		if (value < -1):
			value = -1
		if (value > 1):
			value = 1
		
		return value
   
   	# Two dimensions of noise
	def Noise2D(self, x, y):
		x += self.seed
		y += self.seed
		
		value = 0.0
		initialSize = size = 3
		
		while(size >= 1):
			value += self.SmoothNoise(x * 3 / size, y * 3 / size, 100 / size)
			size -= 1
		
		if (value < -1):
			value = -1
		if (value > 1):
			value = 1
		
		return value

if __name__ == '__main__':
	from GfxPygame2d import gfx
	from time import sleep
	import psyco
	psyco.full()
	perl = Perlin()
	for x in range(100):
		for y in range(100):
			v = perl.Noise2D(x / 50.0, y / 50.0)
			gfx.screen.set_at((x, y), [(v + 1.0) * 127 for i in range(3)])
			gfx.Flip()
	
	while 1:
		sleep(0.1)
		gfx.Flip()


