Property definitions

noise $ InterpolatedNoise :: defaultinit
# 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