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

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

Introduced properties

protected fun gradient_vector(x: Int, y: Int, w: Int): Float

noise :: InterpolatedNoise :: gradient_vector

Get the component w of the gradient unit vector at x, y

Redefined properties

redef type SELF: InterpolatedNoise

noise $ InterpolatedNoise :: SELF

Type of this instance, automatically specialized in every class
redef fun [](x: Float, y: Float): Float

noise $ InterpolatedNoise :: []

Get the noise value at x, y
redef fun core_serialize_to(v: Serializer)

noise $ InterpolatedNoise :: core_serialize_to

Actual serialization of self to serializer
redef init from_deserializer(v: Deserializer)

noise $ InterpolatedNoise :: from_deserializer

Create an instance of this class from the deserializer

All properties

fun !=(other: nullable Object): Bool

core :: Object :: !=

Have self and other different values?
fun ==(other: nullable Object): Bool

core :: Object :: ==

Have self and other the same value?
type CLASS: Class[SELF]

core :: Object :: CLASS

The type of the class of self.
type SELF: Object

core :: Object :: SELF

Type of this instance, automatically specialized in every class
abstract fun [](x: Float, y: Float): Float

noise :: Noise :: []

Get the noise value at x, y
protected fun accept_json_serializer(v: JsonSerializer)

serialization :: Serializable :: accept_json_serializer

Refinable service to customize the serialization of this class to JSON
protected fun accept_msgpack_attribute_counter(v: AttributeCounter)

serialization :: Serializable :: accept_msgpack_attribute_counter

Hook to customize the behavior of the AttributeCounter
protected fun accept_msgpack_serializer(v: MsgPackSerializer)

serialization :: Serializable :: accept_msgpack_serializer

Hook to customize the serialization of this class to MessagePack
protected fun add_to_bundle(bundle: NativeBundle, key: JavaString)

serialization :: Serializable :: add_to_bundle

Called by []= to dynamically choose the appropriate method according
fun amplitude: Float

noise :: Noise :: amplitude

Amplitude of the values returned by []
fun amplitude=(value: Float)

noise :: Noise :: amplitude=

Set the desired amplitude of the values returned by []
protected fun class_factory(name: String): CLASS

core :: Object :: class_factory

Implementation used by get_class to create the specific class.
fun class_name: String

core :: Object :: class_name

The class name of the object.
fun core_serialize_to(serializer: Serializer)

serialization :: Serializable :: core_serialize_to

Actual serialization of self to serializer
fun frequency: Float

noise :: Noise :: frequency

Frequency of this noise
fun frequency=(value: Float)

noise :: Noise :: frequency=

Set the frequency if this noise
init from_deserializer(deserializer: Deserializer)

serialization :: Serializable :: from_deserializer

Create an instance of this class from the deserializer
fun get_class: CLASS

core :: Object :: get_class

The meta-object representing the dynamic type of self.
protected fun gradient_vector(x: Int, y: Int, w: Int): Float

noise :: InterpolatedNoise :: gradient_vector

Get the component w of the gradient unit vector at x, y
fun hash: Int

core :: Object :: hash

The hash code of the object.
init init

core :: Object :: init

fun inspect: String

core :: Object :: inspect

Developer readable representation of self.
protected fun inspect_head: String

core :: Object :: inspect_head

Return "CLASSNAME:#OBJECTID".
intern fun is_same_instance(other: nullable Object): Bool

core :: Object :: is_same_instance

Return true if self and other are the same instance (i.e. same identity).
fun is_same_serialized(other: nullable Object): Bool

core :: Object :: is_same_serialized

Is self the same as other in a serialization context?
intern fun is_same_type(other: Object): Bool

core :: Object :: is_same_type

Return true if self and other have the same dynamic type.
fun max: Float

noise :: Noise :: max

Highest possible value returned by []
fun max=(max: Float)

noise :: Noise :: max=

Highest possible value returned by []
fun min: Float

noise :: Noise :: min

Lowest possible value returned by []
fun min=(min: Float)

noise :: Noise :: min=

Lowest possible value returned by []
protected fun msgpack_extra_array_items: Int

serialization :: Serializable :: msgpack_extra_array_items

Hook to request a larger than usual metadata array
intern fun object_id: Int

core :: Object :: object_id

An internal hash code for the object based on its identity.
fun output

core :: Object :: output

Display self on stdout (debug only).
intern fun output_class_name

core :: Object :: output_class_name

Display class name on stdout (debug only).
fun period: Float

noise :: Noise :: period

Distance between reference points of the noise
fun period=(period: Float)

noise :: Noise :: period=

Distance between reference points of the noise
fun seed: Int

noise :: Noise :: seed

Seed to the random number generator gradient_vector
fun seed=(seed: Int)

noise :: Noise :: seed=

Seed to the random number generator gradient_vector
fun serialization_hash: Int

core :: Object :: serialization_hash

Hash value use for serialization
fun serialize_msgpack(plain: nullable Bool): Bytes

serialization :: Serializable :: serialize_msgpack

Serialize self to MessagePack bytes
fun serialize_to(serializer: Serializer)

serialization :: Serializable :: serialize_to

Serialize self to serializer
fun serialize_to_json(plain: nullable Bool, pretty: nullable Bool): String

serialization :: Serializable :: serialize_to_json

Serialize self to JSON
intern fun sys: Sys

core :: Object :: sys

Return the global sys object, the only instance of the Sys class.
fun to_json: String

serialization :: Serializable :: to_json

Serialize self to plain JSON
abstract fun to_jvalue(env: JniEnv): JValue

core :: Object :: to_jvalue

fun to_pretty_json: String

serialization :: Serializable :: to_pretty_json

Serialize self to plain pretty JSON
fun to_s: String

core :: Object :: to_s

User readable representation of self.
package_diagram noise::InterpolatedNoise InterpolatedNoise noise::Noise Noise noise::InterpolatedNoise->noise::Noise serialization::Serializable Serializable noise::Noise->serialization::Serializable ...serialization::Serializable ... ...serialization::Serializable->serialization::Serializable

Ancestors

interface Object

core :: Object

The root of the class hierarchy.
interface Serializable

serialization :: Serializable

Instances of this class can be passed to Serializer::serialize

Parents

abstract class Noise

noise :: Noise

2D noise generator

Class definitions

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