# Simple interpolated noise
#
# Generates smoother noise than `PerlinNoise`.
#
# Each coordinates at a multiple of `period` defines a random vector and
# values in between are interpolated from these vectors.
#
# This implementation uses a custom deterministic pseudo random number
# generator seeded with `seed`.
# It can be further customized by redefining `gradient_vector`.
# This process do not require any state, so this class only holds the
# attributes of the generator and does not keep any generated data.
#
# ## Usage example
#
# ~~~
# var map = new InterpolatedNoise
# map.min = 0.0
# map.max = 16.0
# map.period = 20.0
# map.seed = 0
#
# var max = 0.0
# var min = 100.0
# for y in 30.times do
# for x in 70.times do
# # Get a value at x, y
# var val = map[x.to_f, y.to_f]
# printn val.to_i.to_hex
#
# max = max.max(val)
# min = min.min(val)
# end
# print ""
# end
# assert max <= map.max
# assert min >= map.min
# ~~~
#
# ## Result at seed == 0
#
# ~~~raw
# 89abcddeeeeeeeddcba9877666555555555666778766555544444555566789abcddeee
# 789abcddeeeeeeddccba887766655555555566677766555544444555566779abcddeee
# 689abcddeeeeeeeddcba988776655555555555667666555554455555566778abccdeee
# 678abccdeeeeeeeedccba988766655555555555666655555555555556666789abcddee
# 5789abcddeeeeeeeddcba998776655544444555666655555555555556666789abcddee
# 5689abcddeeeeeeeedccba98776655544444455566555555555555566666789abccdde
# 4679abccdeeeffeeeddcba98776655444444445565555555555555666666789abbcddd
# 4678abccdeeeffeeeedcba98876555444444444555555555566666666666689aabccdd
# 46789abcdeeeeffeeedccb988765544443344445555566666666666666666789abccdd
# 45789abcddeeeffeeeddcb987765544433334445555666666666666666666789abbccd
# 45789abcddeeeeeeeeddcb987665444333333445556666666777777777766789aabccc
# 45789abcddeeeeeeeeddca987655443333333445566666777777777777776789aabbcc
# 45789abcddeeeeeeeedcca9876544333333333455666777777788877777767899aabbc
# 46789abcddeeeeeeeddcba9876544333222333455667777888888888877767899aabbb
# 46789abcdddeeeeedddcba87655433222223334566777888889998888877778899aabb
# 5678aabcdddeeeedddccb987654332222222334566778889999999998887778899aaab
# 5689abbcddddeedddccba9865443222222223345677889999aaaa99998877788999aaa
# 6789abbcddddddddccbba8765432221111223345678899aaaaaaaaaa9988778889999a
# 6789abccdddddddccbba9865433221111122344577899aabbbbbbbaaa9987788889999
# 789abbccddddddccbba9876543211111111234567899aabbbccccbbbaa987788888899
# 889abbccdddddccbba9886543211000001123456889abbcccccccccbba988888888888
# 899abbcccddddcccbaa9875432211000011223457899abbcccccccccbba98888888888
# 899abbccccddccccbba9876533211000001123456789aabccccddcccbbaa9998888888
# 899abbccccccccccbbaa9765432111000011223456899abbcccdddcccbba9999988888
# 899abbbcccccccccbbaa9865432211000011123456789abbccdddddcccbba999988888
# 899aabbcccccccccbbaa9875433211100001122346789abbccddddddcccbaa99988888
# 899aabbbcccccccbbbbaa876543211100001122345689aabccdddddddccbaaa9988887
# 899aabbbbbbccbbbbbbaa876543221110001112335679aabccddddddddcbbaa9988877
# 899aaabbbbbbbbbbbbbaa9765433211111111123356789abccddddddddccbaa9988777
# 8999aaaabbbbbbbbbbaaa9765433221111111122356789abccdddeedddccbaa9988777
# ~~~
class InterpolatedNoise
super Noise
redef fun [](x, y)
do
x = x/period
y = y/period
# Get grid coordinates
var x0 = if x > 0.0 then x.to_i else x.to_i - 1
var x1 = x0 + 1
var y0 = if y > 0.0 then y.to_i else y.to_i - 1
var y1 = y0 + 1
# Position in grid
var sx = x - x0.to_f
var sy = y - y0.to_f
# Interpolate
var n0 = gradient_dot_product(x0, y0, x, y)
var n1 = gradient_dot_product(x1, y0, x, y)
var ix0 = sx.lerp(n0, n1)
n0 = gradient_dot_product(x0, y1, x, y)
n1 = gradient_dot_product(x1, y1, x, y)
var ix1 = sx.lerp(n0, n1)
var val = sy.lerp(ix0, ix1)
# Return value in [min...max] from val in [-1.0...1.0]
val /= 2.0
val += 0.5
return val.lerp(min, max)
end
# Get the component `w` of the gradient unit vector at `x`, `y`
#
# `w` at 0 targets the X axis, at 1 the Y axis.
#
# Returns a value between -1.0 and 1.0.
#
# Require: `w == 0 or w == 1`
protected fun gradient_vector(x, y, w: Int): Float
do
assert w == 0 or w == 1
# Use our own deterministic pseudo random number generator
#
# These magic prime numbers were determined good enough by
# non-emperical experimentation. They may need to be changed/improved.
var seed = 817721 + self.seed
var i = seed * (x+seed) * 25111217 * (y+seed) * 72233613
var mod = 137121
var angle = (i.mask.abs%mod).to_f*2.0*pi/mod.to_f
# Debug code to evaluate the efficiency of the random angle generator
# The average of the produced angles should be at pi
#
#var sum = once new Container[Float](0.0)
#var count = once new Container[Float](0.0)
#sum.item += angle
#count.item += 1.0
#if count.item.to_i % 1000 == 0 then print "avg:{sum.item/count.item}/{count.item} i:{i} a:{angle} ({x}, {y}: {seed})"
if w == 0 then return angle.cos
return angle.sin
end
private fun gradient_dot_product(ix, iy: Int, x, y: Float): Float
do
var dx = x - ix.to_f
var dy = y - iy.to_f
return dx*gradient_vector(ix, iy, 0) + dy*gradient_vector(ix, iy, 1)
end
end
lib/noise/noise.nit:204,1--350,3