In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

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
Get Adobe Flash player
by cjcat2266 24 Jan 2012
    Embed
/**
 * 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);
    }
}