Newer
Older
perlin_noise / js / jsPerlinNoise.js
@clewis clewis on 28 Feb 2024 5 KB initial commit
/*
* 
* A Perlin Noise generator to dynamically generate a terrian-like effect.
*
* References:
* 	 http://freespace.virgin.net/hugo.elias/models/m_perlin.htm
*
* Start date:  28 April 2015
*
*/
var perlin = (function() {
	'use strict';

	// number of points to generate
	var _NUMPTS = 101;
	
	// number of prime numbers in each array
	var _NUMPRIMES = 5;
	
	// holds the 5, 6 and 10 digit prime numbers
	let _p5d;
	let _p6d;
	let _p10d;
	
	// 5 digit prime numbers
	var _prime5 =  [     15731,      16069,      17579,      31963,      22453];
	var _prime6 =  [    789221,     790289,     811081,     211777,     566173];
	var _prime10 = [1376312589, 1480028201, 1500450271, 1000075057, 1023465798];
	
	// holds the 100 points
	var _perlin = [];
	
	// initialize the array
	for (var i = 0; i < _NUMPTS; i++) {
		_perlin[i] = 0.0;
	}
	
			// get a random number from each of the prime number arrays
		_p5d = _prime5[Math.floor(Math.random() * 5)];
		_p6d = _prime6[Math.floor(Math.random() * 5)];
		_p10d = _prime10[Math.floor(Math.random() * 5)];

	
	
	var _perlin1D = function(persist, octave, x) {
		
		var total = 0;
		var freq = 0;
		var amp = 0;
		
		for (var i = 0; i < octave; i++) {
			freq = Math.pow(2, i);
			amp = Math.pow(persist, i);
			total += _interpolateNoise(x * freq) * amp;
		}
		
		return total;
	};
	
	/*
	 * interpolatedNoise - performs a successive smoothing functions on the
	 * 		input value.
	 * 
	 * ARGUMENT:
	 * x:  float value to be smoothed.
	 */	
	var _interpolateNoise = function(x) {
		
		var intX = Math.floor(x);
		var fractionX = x - intX;
		
		var v1 = _smoothedNoise(intX);
		var v2 = _smoothedNoise(intX + 1);
		
		return _cosineInterpolation(v1, v2, fractionX);
	};

	/*
	 * cosineInterpolation - 
	 * 
	 * Interpolates between two numbers using a cosine function	
	 */	
	var _cosineInterpolation = function (a, b, x) {
	
		var ft = (x * Math.PI);
		var f = (1 - Math.cos(ft)) * 0.5;
		
		return a * (1 - f) + (b * f);

	};
	
	/*
	 * findNoise - Random number generator.  Unlike other RNG this method
	 * 		will return the same number for the same input.
	 * 
	 * ARGUMENTS:
	 * x: an integer value
	 * 
	 * RETURNS:
	 * returns a floating point number between -1.0 and 1.0.  
	 */
	var _findNoise = function(x) {
		x = (x << 13) ^ x;
		
		return (1.0 - ( ( x * (x * x * _p5d + _p6d) + _p10d)
																						& 0x7fffffff ) / 1073741824.0);
		
		// original return value as published on 
		// http://freespace.virgin.net/hugo.elias/models/m_perlin.htm

	};
	
	/*
	 * smoothedNoise - smooths the output to make it look less random
	 * 
	 * ARGUMENTS:
	 * x:  integer value to be smoothed.  It is done by averaging between
	 * 		 the points before and after the given point.
	 * 
	 * RETURNS:
	 * a float value that has been "smoothed".
	 */
	var _smoothedNoise = function(x) {
		return (_findNoise(x) /2) + (_findNoise(x - 1) / 4) + _findNoise(x+1)/4;
	};
	
	var perlinNoise1D = function(persist, octave) {
	
		for (var i = 0; i < _NUMPTS; i++) {
			_perlin[i] = _perlin1D(persist, octave, 0.1 * i);
		}
		
		return _perlin;
	
	};
	
	/*
	* draw the "terrain" on the given context
	*/
	var draw = function(context, width, height, color, fill) {
	
		if (context === undefined) 
			return null;
			
		if (color === undefined) 
			color = '#000000';			// black
			
		if (width === undefined)
			width = 400;
			
		if (height === undefined)
			height = 400;
			
		if (fill === undefined)
			fill = true;
			
		var step = width / _NUMPTS;
			
		context.lineWidth = 1;
		context.strokeStyle = color;
		
		// start drawing at the bottom left corner
		context.beginPath();
		context.moveTo(0, height);
		
		for (var i = 0; i < _NUMPTS; i++) {
			context.lineTo(step * i, height / 2 + _perlin[i] * height / 2);
		}
		
		context.lineTo(width, height / 2 + _perlin[_NUMPTS - 1] * height / 2);
		context.lineTo(width, height);
		context.closePath();
		context.stroke();	

		if (fill) {
			context.fillStyle = color;
			context.fill();
		}
	
	};
	
	// public functions
	return {
	
		perlinNoise1D: perlinNoise1D,
		draw: draw
	
	};
	
})();

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
canvas.width = 800;
canvas.height = 400;

function drawTerrain() {

	var octave = document.getElementById('octave');
	octave.addEventListener("change", generate, false);

	var persist = document.getElementById('persist');
	persist.addEventListener("change", generate, false);

	function generate() {
		context.clearRect(0, 0, canvas.width, canvas.height);
		perlin.perlinNoise1D(persist.value, octave.value);
		perlin.draw(context, canvas.width, canvas.height);
	}

}

function eventWindowLoaded() {
	perlin.perlinNoise1D(0.3, 4);
	perlin.draw(context, canvas.width, canvas.height);
	drawTerrain();

	let perlin1 = new (perlin.perlinNoise1D(0.3, 4))();

	console.log("perlin1:");
	console.log(perlin1);

	let perlin2 = new (perlin.perlinNoise1D(0.3, 4))();
	console.log("perlin2");
	console.log(perlin2);

}

window.addEventListener("load", eventWindowLoaded, false);


//var pts = perlin.perlinNoise1D(0.3, 1);
//perlin.draw(context, canvas.width, canvas.height);