Perlin Noise
Implementation of Ken Perlin's noise algorithm.
References:
http://www.noisemachine.com/talk1/index.html
http://code.google.com/p/fractalterraingeneration/wiki/Perlin_Noise
http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html
/**
* Copyright cjcat2266 ( http://wonderfl.net/user/cjcat2266 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/sPdg
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;
/**
* References:
* http://www.noisemachine.com/talk1/index.html
* http://code.google.com/p/fractalterraingeneration/wiki/Perlin_Noise
* http://webstaff.itn.liu.se/~stegu/TNM022-2005/perlinnoiselinks/perlin-noise-math-faq.html
*/
public class Main extends Sprite
{
private var bmpd:BitmapData;
private var pn:PerlinNoise;
public function Main()
{
pn = new PerlinNoise(50, 50, 3, 123, 10, 10);
bmpd = new BitmapData(117, 117, false, 0xFF000000);
var bmp:Bitmap = new Bitmap(bmpd);
bmp.scaleX = bmp.scaleY = 4;
addChild(bmp);
addEventListener(Event.ENTER_FRAME, loop);
}
private var offset:Number = 0;
private const SPEED:Number = 2.5;
private function loop(e:Event):void
{
bmpd.lock();
offset += SPEED;
//lots of magic offsets
pn.setOffset(0, -0.5 * offset, 0.5 * offset);
pn.setOffset(1, offset, 0.43 * offset);
pn.setOffset(2, 0.25 * offset, -0.25 * offset);
for (var j:uint = 0; j < 233; ++j)
{
for (var i:uint = 0; i < 233; ++i)
{
var value:Number = 0.5 * (pn.perlinNoise(i, j) + 1);
var color:uint = ((0xFF * value) << 16) | ((0xFF * value) << 8) | (0xFF * value);
bmpd.setPixel(i, j, color);
}
}
bmpd.unlock();
}
}
}
class PerlinNoise
{
private var _gridX:Vector.<Number>;
private var _gridY:Vector.<Number>;
private var _octaveWidth:Number;
private var _octaveTotalWidth:Number;
private var _octaveTotalHeight:Number;
private var _octaveHeight:Number;
private var _numLevels:uint;
private var _seed:uint;
private var _gridWidth:uint;
private var _gridHeight:uint;
private var _offsets:Vector.<Number>;
public function PerlinNoise
(
octaveWidth:Number = 50,
octaveHeight:Number = 50,
numLevels:uint = 1,
seed:uint = 0,
gridWidth:uint = 10,
gridHeight:uint = 10
)
{
regenerate
(
octaveWidth,
octaveHeight,
numLevels,
seed,
gridWidth,
gridHeight
);
}
public function regenerate
(
octaveWidth:Number = 50,
octaveHeight:Number = 50,
numLevels:uint = 1,
seed:uint = 0,
gridWidth:uint = 25,
gridHeight:uint = 25
):void
{
_octaveWidth = (octaveWidth >= 1)?(octaveWidth):(1);
_octaveHeight = (octaveHeight >= 1)?(octaveHeight):(1);
_numLevels = (numLevels >= 1)?(numLevels):(1);
_gridWidth = (gridWidth >= 2)?(gridWidth):(2);
_gridHeight = (gridHeight >= 2)?(gridHeight):(2);
_octaveTotalWidth = _gridWidth * _octaveWidth;
_octaveTotalHeight = _gridHeight * _octaveHeight;
_offsets = new Vector.<Number>(2 * _numLevels, true);
for (var i:int = 0; i < 2 * _numLevels; ++i)
{
_offsets[i] = 0;
}
//allocate grid
_gridX = new Vector.<Number>(_gridWidth * gridHeight, true);
_gridY = new Vector.<Number>(_gridWidth * gridHeight, true);
//populate noise grid
for (var y:uint = 0; y < _gridHeight; ++y)
{
for (var x:uint = 0; x < _gridWidth; ++x)
{
//random vector
var vx:Number = noise(_seed++);
var vy:Number = noise(_seed++);
var len:Number = 1 / Math.sqrt(vx * vx + vy * vy);
vx *= len;
vy *= len;
//assign random vector to grid point
_gridX[x + y * _gridWidth] = vx;
_gridY[x + y * _gridHeight] = vy;
}
}
}
public function setOffset(level:uint = 0, x:Number = 0, y:Number = 0):void
{
level = (level < _numLevels)?(level):(_numLevels - 1);
//x
_offsets[(level << 1)] = x;
//y
_offsets[(level << 1) + 1] = y;
}
/**
*
* @param x
* @param y
* @return Random value between -1 and 1.
*/
public function perlinNoise(x:Number, y:Number):Number
{
var result:Number = 0;
var power:uint = 1;
var octaveFactor:uint = 1 << (_numLevels - 1);
for (var octaveLevel:uint = 0; octaveLevel < _numLevels; ++octaveLevel)
{
//wrapping
var wx:Number = mod((x + _offsets[(octaveLevel << 1)]) * power, _octaveTotalWidth);
var wy:Number = mod((y + _offsets[(octaveLevel << 1) + 1]) * power, _octaveTotalHeight);
//interpolation parameter
var tx:Number = 1 - mod(wx, _octaveWidth) / _octaveWidth;
var ty:Number = 1 - mod(wy, _octaveHeight) / _octaveHeight;
var tx_inv:Number = 1 - tx;
var ty_inv:Number = 1 - ty;
//curves
var txFade:Number = fade(tx);
var tyFade:Number = fade(ty);
var txFade_inv:Number = fade(1 - tx);
var tyFade_inv:Number = fade(1 - ty);
//indices for top-left grid point
var tlX:uint = uint(wx / _octaveWidth);
var tlY:uint = uint(wy / _octaveHeight);
//top-right
var trX:uint = tlX + 1;
var trY:uint = tlY;
trX = (trX == _gridWidth)?(0):(trX);
//bottom-left
var blX:uint = tlX;
var blY:uint = tlY + 1;
blY = (blY == _gridHeight)?(0):(blY);
//bottom-right
var brX:uint = trX;
var brY:uint = blY;
var index:uint;
var levelResult:Number = 0;
//top-left
index = tlX + tlY * _gridWidth;
levelResult += txFade * tyFade * (_gridX[index] * tx + _gridY[index] * ty);
//top-right
index = trX + trY * _gridWidth;
levelResult += txFade_inv * tyFade * (_gridX[index] * tx_inv + _gridY[index] * ty);
//bottom-left
index = blX + blY * _gridWidth;
levelResult += txFade * tyFade_inv * (_gridX[index] * tx + _gridY[index] * ty_inv);
//bottom-right
index = brX + brY * _gridWidth;
levelResult += txFade_inv * tyFade_inv * (_gridX[index] * tx_inv + _gridY[index] * ty_inv);
//add result
result += levelResult * 0.5 * octaveFactor;
//level increments
power <<= 1;
octaveFactor >>= 1;
}
result /= (1 << _numLevels) - 1;
return result;
}
/**
* 2D S-curve interpolation.
* @param tx
* @param ty
* @return
*/
private function fade(t:Number):Number
{
return (3.0 * t * t - 2.0 * t * t * t);
//return (t * t * t * (t * (t * 6 - 15) + 10));
}
/**
*
* @param x
* @param y Positive number.
* @return
*/
private function mod(x:Number, y:Number):Number
{
x = x % y;
if (x < 0) x += y;
return x;
}
/**
* Random number between -1 and 1.
* @param seed
* @return
*/
private function noise(x:uint):Number
{
x = (x << 13) ^ x;
return (1 - ((x * (x * x * 15731 + 789221) + 1376312589) & 0x7FFFFFFF) / 1073741824.0);
}
}