2020-05-08 15:23:30 +00:00
package noise
import (
"math"
"math/rand"
)
const perlinB = 0x100
const perlinBM = 0xff
const perlinN = 0x1000
// Perlin contains a perlin noise function (both 1D and 2D).
type Perlin struct {
Seed int64
p [ perlinB + perlinB + 2 ] int
g1 [ perlinB + perlinB + 2 ] float64
g2 [ perlinB + perlinB + 2 ] [ 2 ] float64
}
// NewPerlin creates a new perlin noise function with seed seed.
func NewPerlin ( seed int64 ) * Perlin {
p := & Perlin { Seed : seed }
p . Init ( )
return p
}
func perlinSmoothCurve ( t float64 ) float64 { return t * t * ( 3. - 2. * t ) }
func perlinLerp ( t , a , b float64 ) float64 { return a + t * ( b - a ) }
func perlinAt2D ( q [ 2 ] float64 , rx , ry float64 ) float64 { return rx * q [ 0 ] + ry * q [ 1 ] }
2020-05-15 08:20:54 +00:00
// noise2d generates 2-dimensional noise. The result is in range [-sqrt(1/4),sqrt(1/4)].
2020-05-08 15:23:30 +00:00
func ( p * Perlin ) noise1d ( x float64 ) float64 {
tx := x + perlinN
bx0 := int ( tx ) & perlinBM
bx1 := ( bx0 + 1 ) & perlinBM
rx0 := tx - math . Floor ( tx )
rx1 := rx0 - 1.
sx := perlinSmoothCurve ( rx0 )
u := rx0 * p . g1 [ p . p [ bx0 ] ]
v := rx1 * p . g1 [ p . p [ bx1 ] ]
return perlinLerp ( sx , u , v )
}
2020-05-15 08:20:54 +00:00
// noise2d generates 2-dimensional noise. The result is in range [-sqrt(1/2),sqrt(1/2)].
2020-05-08 15:23:30 +00:00
func ( p * Perlin ) noise2d ( x , y float64 ) float64 {
tx := x + perlinN
bx0 := int ( tx ) & perlinBM
bx1 := ( bx0 + 1 ) & perlinBM
rx0 := tx - math . Floor ( tx )
rx1 := rx0 - 1.
ty := y + perlinN
by0 := int ( ty ) & perlinBM
by1 := ( by0 + 1 ) & perlinBM
ry0 := ty - math . Floor ( ty )
ry1 := ry0 - 1.
i := p . p [ bx0 ]
j := p . p [ bx1 ]
b00 := p . p [ i + by0 ]
b10 := p . p [ j + by0 ]
b01 := p . p [ i + by1 ]
b11 := p . p [ j + by1 ]
sx := perlinSmoothCurve ( rx0 )
sy := perlinSmoothCurve ( ry0 )
u := perlinAt2D ( p . g2 [ b00 ] , rx0 , ry0 )
v := perlinAt2D ( p . g2 [ b10 ] , rx1 , ry0 )
a := perlinLerp ( sx , u , v )
u = perlinAt2D ( p . g2 [ b01 ] , rx0 , ry1 )
v = perlinAt2D ( p . g2 [ b11 ] , rx1 , ry1 )
b := perlinLerp ( sx , u , v )
return perlinLerp ( sy , a , b )
}
func normalize2D ( v [ 2 ] float64 ) [ 2 ] float64 {
var s float64
s = math . Sqrt ( v [ 0 ] * v [ 0 ] + v [ 1 ] * v [ 1 ] )
v [ 0 ] /= s
v [ 1 ] /= s
return v
}
// Init initialised the Perlin noise source. Must be called unless struct was created with NewPerlin(...).
func ( p * Perlin ) Init ( ) {
r := rand . New ( rand . NewSource ( p . Seed ) )
for i := 0 ; i < perlinB ; i ++ {
p . p [ i ] = i
p . g1 [ i ] = float64 ( ( r . Int ( ) % ( perlinB + perlinB ) ) - perlinB ) / perlinB
for j := 0 ; j < 2 ; j ++ {
p . g2 [ i ] [ j ] = float64 ( ( r . Int ( ) % ( perlinB + perlinB ) ) - perlinB ) / perlinB
}
p . g2 [ i ] = normalize2D ( p . g2 [ i ] )
for j := 0 ; j < 3 ; j ++ {
r . Int ( )
}
}
for i := perlinB ; i > 0 ; i -- {
j := r . Int ( ) % perlinB
p . p [ i ] , p . p [ j ] = p . p [ j ] , p . p [ i ]
}
for i := 0 ; i < perlinB + 2 ; i ++ {
p . p [ perlinB + i ] = p . p [ i ]
p . g1 [ perlinB + i ] = p . g1 [ i ]
for j := 0 ; j < 2 ; j ++ {
p . g2 [ perlinB + i ] [ j ] = p . g2 [ i ] [ j ]
}
}
}
// Noise1D generates the noise value for x. alpha defines the weight when the sum is formed. Typically it is 2 (function is noisier when alpha approaches 1). beta is the harmonic scaling/spacing (typically 2). n defines the number of harmonics.
func ( p * Perlin ) Noise1D ( x , alpha , beta float64 , n int ) float64 {
var val , sum float64
var scale float64 = 1
var coord float64 = x
for i := 0 ; i < n ; i ++ {
val = p . noise1d ( coord )
sum += val / scale
scale *= alpha
coord *= beta
}
return sum
}
// Noise2D generates the noise value for coordinate (x, y). alpha defines the weight when the sum is formed. Typically it is 2 (function is noisier when alpha approaches 1). beta is the harmonic scaling/spacing (typically 2). n defines the number of harmonics.
func ( p * Perlin ) Noise2D ( x , y , alpha , beta float64 , n int ) float64 {
var val , sum float64
var scale float64 = 1
var coord = [ 2 ] float64 { x , y }
for i := 0 ; i < n ; i ++ {
val = p . noise2d ( coord [ 0 ] , coord [ 1 ] )
sum += val / scale
scale *= alpha
coord [ 0 ] *= beta
coord [ 1 ] *= beta
}
return sum
}