Skip to content

Instantly share code, notes, and snippets.

@EduardoLopes
Created November 8, 2019 22:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EduardoLopes/ec6cc0b1f99d907363d4a1d7ce9ee40a to your computer and use it in GitHub Desktop.
Save EduardoLopes/ec6cc0b1f99d907363d4a1d7ce9ee40a to your computer and use it in GitHub Desktop.
import "random" for Random
class Perlin{
construct new(seed, dimension, octaves, tile, unbias){
_random = Random.new(seed)
_dimension = dimension
_octaves = octaves
_tile = tile
_unbias = unbias
_scale_factor = 2 * _dimension.pow(-0.5)
_gradient = {}
}
smoothstep(t){
return t * t * (3.0 - 2.0 * t)
}
lerp(t, a, b){
return a + t * (b - a)
}
generate_gradient(){
if(_dimension == 1){
return [_random.float(-1, 1), 0]
}
var random_point = []
for(d in 0..._dimension){
random_point.add(randn_bm(0, 1, 1))
}
var scale = 0
for(p in random_point){
scale = scale + (p * p)
}
scale.pow(-0.5)
System.print(random_point)
for(i in 0...random_point.count){
random_point[i] = random_point[i] * scale
}
return random_point
}
//https://stackoverflow.com/questions/25582882/javascript-math-random-normal-distribution-gaussian-bell-curve/39187274#39187274
randn_bm(min, max, skew){
var u = 0
var v = 0
while(u == 0) u = _random.float() //Converting [0,1) to (0,1)
while(v == 0) v = _random.float()
var num = ( -2.0 * u.log ).sqrt * ( 2.0 * Num.pi * v ).cos
num = num / 10.0 + 0.5 // Translate to 0 -> 1
if (num > 1 || num < 0) num = randn_bm(min, max, skew) // resample between 0 and 1 if out of range
num = num.pow(skew) // Skew
num = num * (max - min) // Stretch to fill range
num = num + min // offset to min
return num
}
get_plain_noise(point){
if(point.count != _dimension){
System.print("expected %(_dimension) values, got %(point.count)")
}
var grid_coords = []
for (coord in point){
var min_coord = coord.floor
var max_coord = min_coord + 1
grid_coords.add([min_coord, max_coord])
}
var dots = []
for(grid_point in cartesian_product(grid_coords)){
var grid_point_hash = get_grid_point_hash(grid_point)
if(!_gradient[grid_point_hash]){
_gradient[grid_point_hash] = generate_gradient()
}
var gradient = _gradient[grid_point_hash]
var dot = 0
for(i in 0..._dimension){
dot = dot + gradient[i] * (point[i] - grid_point[i])
}
dots.add(dot)
}
var dim = _dimension
while(dots.count > 1){
dim = dim - 1
var s = smoothstep(point[dim] - grid_coords[dim][0])
var next_dots = []
while(dots.count > 0){
next_dots.add(lerp(s, dots.removeAt(0), dots.removeAt(0)))
}
dots = next_dots
}
return dots[0] * _scale_factor
}
get_grid_point_hash(grid_point){
var hash = ""
grid_point.each {|n|
hash = hash + "%(n)"
}
return hash
}
cartesian_product(list){
var results = [[]]
for (i in 0...list.count) {
var currentSubArray = list[i]
var temp = []
for ( j in 0...results.count) {
for (k in 0...currentSubArray.count) {
temp.add(results[j] + [currentSubArray[k]])
}
}
results = temp
}
return results
}
noise(point){
//return
var ret = 0
for(oct in 0..._octaves){
var oct_2 = 1 << oct
var new_point = []
for(i in 0...point.count){
var coord = point[i]
coord = coord * oct_2
if(_tile[i]){
coord = coord % _tile[i] * oct_2
}
new_point.add(coord)
}
ret = ret + get_plain_noise(new_point) / oct_2
}
ret = ret / (2 - 2.pow(1 - _octaves))
if(_unbias){
var r = (ret + 1) / 2
for(i in 0...(_octaves / 2 + 0.5).floor){
r = smoothstep(r)
}
ret = r * 2 - 1
}
return ret
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment