1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
 package {
public class PerlinNoise {
//initialize random seed
public var seed:uint = prng(Math.random()*0x7fffffff);
public function PerlinNoise() {
}
public function getFractalNoise(size:int,octaves:int,decay:Number):Array {
var field:Array;
var totalAmplitude:Number = 1;
var totalField:Array = getPerlinNoise_2D(size, 1, 1);
var freq:int;
var amp:Number;
for(var o:int = 1; o <= octaves; o++){
freq = Math.pow(2,o);
amp = Math.pow(decay,o);
if(freq < size) {//cannot have frequency > len .. no subpixel rendering
field = getPerlinNoise_2D(size, amp, freq);
totalAmplitude+=amp;
for(var g:int = 0; g < size; g++){
for(var h:int = 0; h < size; h++){
totalField[g][h] += field[g][h];
}
}
}
}
//normalize values to between 1 * 1
for(var i:int = 0; i < size; i++){
for(var j:int = 0; j < size; j++){
totalField[i][j] = totalField[i][j]/totalAmplitude;
}
}
return totalField;
}
public function getPerlinNoise_1D(len:int=200, octaves:int=8, decay:Number=1):Array {
var graph:Array;
//totalGraph holds accumulated values
var totalGraph:Array = getNoiseFunctionResults(len, 1, 1);
var totalAmplitude:Number= 1;//used to normalize amplitude
for(var o:int = 1; o <= octaves; o++){
if(Math.pow(2,o) < len) {//cannot have frequency > len
graph = getNoiseFunctionResults(len, Math.pow(decay,o)/*amplitude*/, Math.pow(2,o)/*frequency*/);
totalAmplitude+=Math.pow(decay,o);
for(var g:int = 0; g < graph.length; g++){
//add the graphs together by shifting them so they are centred on y=0 (subtract their amplitude/2)
totalGraph[g] += graph[g];
}
}
}
//normalize values in totalGraph to be between 0.5 & 0.5 by dividing by accumulated amplitude
for(var i:int = 0; i < totalGraph.length; i++){
totalGraph[i] = totalGraph[i]/totalAmplitude;
}
return totalGraph;
}
public function getPerlinNoise_2D(size:int=200, amplitude:Number=1, frequency:int=5):Array {
var spacing:Number = size/frequency;
var gradient:Array = [];
var gp:Vector.<Number>;//grid point
var seed:uint;
var nextseed:uint = prng(Math.random()*0x7fffffff);
for(var g:int = 0; g <= frequency; g++){
gradient[g] = [];
for(var h:int = 0; h <= frequency; h++){
seed = prng(nextseed); //using 'old' nextseed ;)
nextseed = prng(seed);
//each point in the gradient is a 2point vector
gp = gradient[g][h] = new Vector.<Number>(2,true);//fixed length vector  maybe help memory use?
gp[0] = (seed/0x7fffffff)*2  1;
gp[1] = (nextseed/0x7fffffff)*2  1;
}
}
//calculate the resulting value for each x,y
var results:Array = []
var gx0:int;
var gx1:int;
var gy0:int;
var gy1:int;
var s:Number;
var t:Number;
var u:Number;
var v:Number;
for(var x:int= 0; x < size; x++){
results[x] = [];
for(var y:int = 0; y < size; y++){
//determine the surrounding gridpoints as multiples of spacing
gx0 = Math.floor(x/spacing);
gx1 = gx0+1;
gy0 = Math.floor(y/spacing);
gy1 = gy0 +1;
var xf:Number = (x  gx0)/spacing;//x as a fractional value of spacing
var yf:Number = (y  gy0)/spacing;//y as a fractional value of spacing
//calculate the dotproducts of the 2 vectors
/*s = g(x0, y0) · ((x, y)  (x0, y0)) ,
t = g(x1, y0) · ((x, y)  (x1, y0)) ,
u = g(x0, y1) · ((x, y)  (x0, y1)) ,
v = g(x1, y1) · ((x, y)  (x1, y1)) . */
/*s = gradient[x0][y0] · [xf  gx0, yf  gy0];
t = gradient[x1][y0] · [xf  gx1, yf = gy0];
u = gradient[x0][y1] · [xf  gx0, yf  gy1];
v = gradient[x1][y1] · [xf  gx1, yf  gy1];*/
s = gradient[gx0][gy0][0]*(x/spacing  gx0)+ gradient[gx0][gy0][1]*(y/spacing  gy0);
t = gradient[gx1][gy0][0]*(x/spacing  gx1) + gradient[gx1][gy0][1]*(y/spacing  gy0);
u = gradient[gx0][gy1][0]*(x/spacing  gx0) + gradient[gx0][gy1][1]*(y/spacing  gy1);
v = gradient[gx1][gy1][0]*(x/spacing  gx1) + gradient[gx1][gy1][1]*(y/spacing gy1);
//calcuate the weighted averages
var Sx:Number = S_curve(x/spacinggx0);
var a:Number = s + Sx*(t  s);
var b:Number = u + Sx*(v  u);
var Sy:Number = S_curve(y/spacing  gy0);
var z:Number = a + Sy*(b  a);
results[x][y] = z;
}
}
return results;
}
// Scurve takes number between 0 & 1 and shifts its closer to 0 or 1
private function S_curve(p:Number):Number {
return 3*p*p  2*p*p*p;
}
private function getNoiseFunctionResults(len:int=200, amplitude:Number=1, freq:int=1):Array {
//trace("getNoise(amplitude="+amp+",frequency"+freq+")");
var result:Number = 0;//between 0.5 & 0.5
var wavelength:Number= len/freq;//divide imgWidth by frequency to get wavelength
var results:Array = [];
//start with random seed
var seed:uint = Math.round(Math.random()*0x7FFFFFFF); //seed for prng... can be any uint (except 0)
//get next seed  used for interpolation
var nextseed:uint = prng(seed);
//for each x value
for(var x:int = 0; x < len; x++){
//if we are on a factor of wavelength ... get psuedorandom y value
if(x % wavelength == 0){
//store nextseed as seed
seed = nextseed;
//get next nextseed
nextseed = prng(seed);
//get y value from pseudorandom number
result = (seed/0x7FFFFFFF)*amplitude  amplitude/2;
} else {
//interpolate value between seed & nextseed
result = (interpolate(seed, nextseed, (x % wavelength)/wavelength)/0x7FFFFFFF)*amplitude  amplitude/2;
}
results.push(result);
}
return results;
}
private function prng(seed:uint):uint {
//to get a full period sequence you should feed back the seed
return seed * 16807 % 0x7FFFFFFF;
//return 0xCCCCCCCC;
//to get a value between 0 & 1, divide result / 0x7FFFFFFF
}
private function interpolate(a:int,b:int,i:Number):Number {
//cosine interpolation
var ft:Number = i*Math.PI;
var f:Number = (1  Math.cos(ft)) * .5;
return a*(1f) + b*f;
}
}
}
// Copyright (c) 2008 David Wilhelm
// MIT license: http://www.opensource.org/licenses/mitlicense.php
