From eff96bac9425e46dff2d430e82e2de13e0076d58 Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Thu, 12 Mar 2020 12:40:12 +0100 Subject: [PATCH] Add 1d noise versions, Gaussian noise, noise gradients, 4D fractal functions --- orx-noise/README.md | 289 +++++++++++--------- orx-noise/src/main/kotlin/CubicNoise1D.kt | 10 + orx-noise/src/main/kotlin/Fractal.kt | 197 ++++++++++++- orx-noise/src/main/kotlin/GaussianRandom.kt | 37 +++ orx-noise/src/main/kotlin/Gradient.kt | 72 +++++ orx-noise/src/main/kotlin/PerlinNoise1D.kt | 9 + orx-noise/src/main/kotlin/SimplexNoise1D.kt | 3 + orx-noise/src/main/kotlin/SimplexNoise2D.kt | 18 +- orx-noise/src/main/kotlin/SimplexNoise3D.kt | 11 +- orx-noise/src/main/kotlin/SimplexNoise4D.kt | 13 +- orx-noise/src/main/kotlin/UniformRandom.kt | 2 + orx-noise/src/main/kotlin/ValueNoise1D.kt | 8 + orx-noise/src/test/kotlin/TestGradient.kt | 33 +++ 13 files changed, 538 insertions(+), 164 deletions(-) create mode 100644 orx-noise/src/main/kotlin/CubicNoise1D.kt create mode 100644 orx-noise/src/main/kotlin/GaussianRandom.kt create mode 100644 orx-noise/src/main/kotlin/Gradient.kt create mode 100644 orx-noise/src/main/kotlin/PerlinNoise1D.kt create mode 100644 orx-noise/src/main/kotlin/SimplexNoise1D.kt create mode 100644 orx-noise/src/main/kotlin/ValueNoise1D.kt create mode 100644 orx-noise/src/test/kotlin/TestGradient.kt diff --git a/orx-noise/README.md b/orx-noise/README.md index 4b85b89f..c0a3e4c9 100644 --- a/orx-noise/README.md +++ b/orx-noise/README.md @@ -1,128 +1,161 @@ -# orx-noise - -A collection of noisy functions - -## Uniform random numbers - -```kotlin -val sua = Double.uniform() -val sub = Double.uniform(-1.0, 1.0) - -val v2ua = Vector2.uniform() -val v2ub = Vector2.uniform(-1.0, 1.0) -val v2uc = Vector2.uniform(Vector2(0.0, 0.0), Vector2(1.0, 1.0)) -val v2ur = Vector2.uniformRing(0.5, 1.0) - -val v3ua = Vector3.uniform() -val v3ub = Vector3.uniform(-1.0, 1.0) -val v3uc = Vector3.uniform(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0)) -val v3ur = Vector3.uniformRing(0.5, 1.0) - -val v4ua = Vector4.uniform() -val v4ub = Vector4.uniform(-1.0, 1.0) -val v4uc = Vector4.uniform(Vector4(0.0, 0.0, 0.0, 0.0), Vector4(1.0, 1.0, 1.0, 1.0)) -val v4ur = Vector4.uniformRing(0.5, 1.0) - -val ringSamples = List(500) { Vector2.uniformRing() } -``` - -## Multi-dimensional noise - -These are a mostly straight port from FastNoise-Java but have a slightly different interface. - -### Perlin noise -``` -// -- 2d -val v0 = perlinLinear(seed, x, y) -val v1 = perlinQuintic(seed, x, y) -val v2 = perlinHermite(seed, x, y) - -// -- 3d -val v3 = perlinLinear(seed, x, y, z) -val v4 = perlinQuintic(seed, x, y, z) -val v5 = perlinHermite(seed, x, y, z) -``` - -### Value noise -``` -// -- 2d -val v0 = valueLinear(seed, x, y) -val v1 = valueQuintic(seed, x, y) -val v2 = valueHermite(seed, x, y) - -// -- 3d -val v3 = valueLinear(seed, x, y, z) -val v4 = valueQuintic(seed, x, y, z) -val v5 = valueHermite(seed, x, y ,z) -``` - -### Simplex noise -``` -// -- 2d -val v0 = simplexLinear(seed, x, y) -val v1 = simplexQuintic(seed, x, y) -val v2 = simplexHermite(seed, x, y) - -// -- 3d -val v3 = simplexLinear(seed, x, y, z) -val v4 = simplexQuintic(seed, x, y, z) -val v5 = simplexHermite(seed, x, y ,z) - -// -- 4d -val v6 = simplexLinear(seed, x, y, z, w) -val v7 = simplexQuintic(seed, x, y, z, w) -val v8 = simplexHermite(seed, x, y, z, w) -``` - -### Cubic noise -``` -// -- 2d -val v0 = cubicLinear(seed, x, y) -val v1 = cubicQuintic(seed, x, y) -val v2 = cubicHermite(seed, x, y) - -// -- 3d -val v3 = cubicLinear(seed, x, y, z) -val v4 = cubicQuintic(seed, x, y, z) -val v5 = cubicHermite(seed, x, y ,z) -``` - -### Fractal noise - -The library provides 3 functions with which fractal noise can be composed. - -#### Fractal brownian motion (FBM) - -``` -val v0 = fbm(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) -val v1 = fbm(seed, x, y, ::simplexLinear, octaves, lacunarity, gain) -val v2 = fbm(seed, x, y, ::valueLinear, octaves, lacunarity, gain) - -val v3 = fbm(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) -val v4 = fbm(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain) -val v5 = fbm(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain) -``` - -#### Rigid - -``` -val v0 = rigid(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) -val v1 = rigid(seed, x, y, ::simplexLinear, octaves, lacunarity, gain) -val v2 = rigid(seed, x, y, ::valueLinear, octaves, lacunarity, gain) - -val v3 = rigid(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) -val v4 = rigid(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain) -val v5 = rigid(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain) -``` - -#### Billow - -``` -val v0 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) -val v1 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) -val v2 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) - -val v3 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) -val v4 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) -val v5 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) -``` +# orx-noise + +A collection of noisy functions + +## Uniform random numbers + +```kotlin +val sua = Double.uniform() +val sub = Double.uniform(-1.0, 1.0) + +val v2ua = Vector2.uniform() +val v2ub = Vector2.uniform(-1.0, 1.0) +val v2uc = Vector2.uniform(Vector2(0.0, 0.0), Vector2(1.0, 1.0)) +val v2ur = Vector2.uniformRing(0.5, 1.0) + +val v3ua = Vector3.uniform() +val v3ub = Vector3.uniform(-1.0, 1.0) +val v3uc = Vector3.uniform(Vector3(0.0, 0.0, 0.0), Vector3(1.0, 1.0, 1.0)) +val v3ur = Vector3.uniformRing(0.5, 1.0) + +val v4ua = Vector4.uniform() +val v4ub = Vector4.uniform(-1.0, 1.0) +val v4uc = Vector4.uniform(Vector4(0.0, 0.0, 0.0, 0.0), Vector4(1.0, 1.0, 1.0, 1.0)) +val v4ur = Vector4.uniformRing(0.5, 1.0) + +val ringSamples = List(500) { Vector2.uniformRing() } +``` + +## Multi-dimensional noise + +These are a mostly straight port from FastNoise-Java but have a slightly different interface. + +### Perlin noise +``` +// -- 1d +val v0 = perlinLinear(seed, x) +val v1 = perlinQuintic(seed, x) +val v2 = perlinHermite(seed, x) + +// -- 2d +val v3 = perlinLinear(seed, x, y) +val v4 = perlinQuintic(seed, x, y) +val v5 = perlinHermite(seed, x, y) + +// -- 3d +val v6 = perlinLinear(seed, x, y, z) +val v7 = perlinQuintic(seed, x, y, z) +val v8 = perlinHermite(seed, x, y, z) +``` + +### Value noise +``` +// -- 1d +val v0 = valueLinear(seed, x) +val v1 = valueQuintic(seed, x) +val v2 = valueHermite(seed, x) + +// -- 2d +val v2 = valueLinear(seed, x, y) +val v3 = valueQuintic(seed, x, y) +val v4 = valueHermite(seed, x, y) + +// -- 3d +val v5 = valueLinear(seed, x, y, z) +val v6 = valueQuintic(seed, x, y, z) +val v7 = valueHermite(seed, x, y ,z) +``` + +### Simplex noise +``` +// -- 1d +val v0 = simplex(seed, x) + +// -- 2d +val v1 = simplex(seed, x, y) + +// -- 3d +val v2 = simplex(seed, x, y, z) + +// -- 4d +val v3 = simplex(seed, x, y, z, w) +``` + +### Cubic noise +``` +// -- 1d +val v0 = cubic(seed, x, y) +val v1 = cubicQuintic(seed, x, y) +val v2 = cubicHermite(seed, x, y) + +// -- 2d +val v0 = cubic(seed, x, y) +val v1 = cubicQuintic(seed, x, y) +val v2 = cubicHermite(seed, x, y) + +// -- 3d +val v3 = cubic(seed, x, y, z) +val v4 = cubicQuintic(seed, x, y, z) +val v5 = cubicHermite(seed, x, y ,z) +``` + +### Fractal noise + +The library provides 3 functions with which fractal noise can be composed. + +#### Fractal brownian motion (FBM) + +``` +// 1d +val v0 = fbm(seed, x, ::perlinLinear, octaves, lacunarity, gain) +val v1 = fbm(seed, x, ::simplexLinear, octaves, lacunarity, gain) +val v2 = fbm(seed, x, ::valueLinear, octaves, lacunarity, gain) + +// 2d +val v3 = fbm(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) +val v4 = fbm(seed, x, y, ::simplexLinear, octaves, lacunarity, gain) +val v5 = fbm(seed, x, y, ::valueLinear, octaves, lacunarity, gain) + +// 3d +val v6 = fbm(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) +val v7 = fbm(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain) +val v8 = fbm(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain) +``` + +#### Rigid + +``` +// 1d +val v0 = rigid(seed, x, ::perlinLinear, octaves, lacunarity, gain) +val v1 = rigid(seed, x, ::simplexLinear, octaves, lacunarity, gain) +val v2 = rigid(seed, x, ::valueLinear, octaves, lacunarity, gain) + +// 2d +val v2 = rigid(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) +val v3 = rigid(seed, x, y, ::simplexLinear, octaves, lacunarity, gain) +val v4 = rigid(seed, x, y, ::valueLinear, octaves, lacunarity, gain) + +// 3d +val v3 = rigid(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) +val v4 = rigid(seed, x, y, z, ::simplexLinear, octaves, lacunarity, gain) +val v5 = rigid(seed, x, y, z, ::valueLinear, octaves, lacunarity, gain) +``` + +#### Billow + +``` +// 1d +val v0 = billow(seed, x, ::perlinLinear, octaves, lacunarity, gain) +val v1 = billow(seed, x, ::perlinLinear, octaves, lacunarity, gain) +val v2 = billow(seed, x, ::perlinLinear, octaves, lacunarity, gain) + +// 2d +val v3 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) +val v4 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) +val v5 = billow(seed, x, y, ::perlinLinear, octaves, lacunarity, gain) + +// 3d +val v6 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) +val v7 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) +val v8 = billow(seed, x, y, z, ::perlinLinear, octaves, lacunarity, gain) +``` diff --git a/orx-noise/src/main/kotlin/CubicNoise1D.kt b/orx-noise/src/main/kotlin/CubicNoise1D.kt new file mode 100644 index 00000000..881f3ad8 --- /dev/null +++ b/orx-noise/src/main/kotlin/CubicNoise1D.kt @@ -0,0 +1,10 @@ +package org.openrndr.extra.noise + +fun cubicLinear(seed: Int, x: Double) = cubic(seed, x, ::linear) +fun cubicQuintic(seed: Int, x: Double) = cubic(seed, x, ::quintic) +fun cubicHermite(seed: Int, x: Double) = cubic(seed, x, ::hermite) + + +fun cubic(seed: Int, x: Double, interpolator: (Double) -> Double = ::linear): Double { + return cubic(seed, x, 0.0, interpolator) +} \ No newline at end of file diff --git a/orx-noise/src/main/kotlin/Fractal.kt b/orx-noise/src/main/kotlin/Fractal.kt index 789572f7..f9fe863e 100644 --- a/orx-noise/src/main/kotlin/Fractal.kt +++ b/orx-noise/src/main/kotlin/Fractal.kt @@ -1,5 +1,28 @@ package org.openrndr.extra.noise +import kotlin.math.abs + + +inline fun fbm(seed: Int, x: Double, y: Double, z: Double, w: Double, crossinline noise: (Int, Double, Double, Double, Double) -> Double, + octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { + var sum = noise(seed, x, y, z, w) + var amp = 1.0 + + var x = x + var y = y + var z = z + var w = w + for (i in 1 until octaves) { + x *= lacunarity + y *= lacunarity + z *= lacunarity + w *= lacunarity + amp *= gain + sum += noise(seed + i, x, y, z, w) * amp + } + return sum +} + inline fun fbm(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { var sum = noise(seed, x, y, z) @@ -18,7 +41,6 @@ inline fun fbm(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (I return sum } - inline fun fbm(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { var sum = noise(seed, x, y) @@ -35,6 +57,66 @@ inline fun fbm(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, return sum } +inline fun fbm(seed: Int, x: Double, crossinline noise: (Int, Double) -> Double, + octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { + var sum = noise(seed, x) + var amp = 1.0 + + var x = x + for (i in 1 until octaves) { + x *= lacunarity + amp *= gain + sum += noise(seed + i, x) * amp + } + return sum +} + +inline fun fbmFunc1D(crossinline noise: (Int, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double) -> Double { + return { seed, x -> + fbm(seed, x, noise, octaves, lacunarity, gain) + } +} + +inline fun fbmFunc2D(crossinline noise: (Int, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double) -> Double { + return { seed, x, y -> + fbm(seed, x, y, noise, octaves, lacunarity, gain) + } +} + +inline fun fbmFunc3D(crossinline noise: (Int, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double, Double) -> Double { + return { seed, x, y, z -> + fbm(seed, x, y, z, noise, octaves, lacunarity, gain) + } +} + +inline fun fbmFunc4D(crossinline noise: (Int, Double, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double, Double, Double) -> Double { + return { seed, x, y, z, w -> + fbm(seed, x, y, z, w, noise, octaves, lacunarity, gain) + } +} + + + +inline fun billow(seed: Int, x: Double, y: Double, z: Double, w: Double, crossinline noise: (Int, Double, Double, Double, Double) -> Double, + octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { + var sum = Math.abs(noise(seed, x, y, z, w) * 2.0 - 1.0) + var amp = 1.0 + + var x = x + var y = y + var z = z + var w = w + for (i in 1 until octaves) { + x *= lacunarity + y *= lacunarity + z *= lacunarity + w *= lacunarity + amp *= gain + sum += Math.abs(noise(seed + i, x, y, z, w) * 2.0 - 1.0) * amp + } + return sum +} + inline fun billow(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { var sum = Math.abs(noise(seed, x, y, z) * 2.0 - 1.0) @@ -55,7 +137,7 @@ inline fun billow(seed: Int, x: Double, y: Double, z: Double, crossinline noise: inline fun billow(seed: Int, x: Double, y: Double, crossinline noise: (Int, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { - var sum = Math.abs(noise(seed, x, y) * 2.0 - 1.0) + var sum = abs(noise(seed, x, y) * 2.0 - 1.0) var amp = 1.0 var x = x @@ -64,7 +146,83 @@ inline fun billow(seed: Int, x: Double, y: Double, crossinline noise: (Int, Doub x *= lacunarity y *= lacunarity amp *= gain - sum += Math.abs(noise(seed + i, x, y) * 2.0 - 1.0) * amp + sum += abs(noise(seed + i, x, y) * 2.0 - 1.0) * amp + } + return sum +} + +inline fun billow(seed: Int, x: Double, crossinline noise: (Int, Double) -> Double, + octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { + var sum = abs(noise(seed, x) * 2.0 - 1.0) + var amp = 1.0 + + var x = x + for (i in 1 until octaves) { + x *= lacunarity + amp *= gain + sum += abs(noise(seed + i, x) * 2.0 - 1.0) * amp + } + return sum +} + +inline fun billowFunc1D(crossinline noise: (Int, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double) -> Double { + return { seed, x -> + billow(seed, x, noise, octaves, lacunarity, gain) + } +} + +inline fun billowFunc2D(crossinline noise: (Int, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double) -> Double { + return { seed, x, y -> + billow(seed, x, y, noise, octaves, lacunarity, gain) + } +} + +inline fun billowFunc3D(crossinline noise: (Int, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double, Double) -> Double { + return { seed, x, y, z -> + billow(seed, x, y, z, noise, octaves, lacunarity, gain) + } +} + +inline fun billowFunc4D(crossinline noise: (Int, Double, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double, Double, Double) -> Double { + return { seed, x, y, z, w -> + billow(seed, x, y, z, w, noise, octaves, lacunarity, gain) + } +} + +inline fun rigid(seed: Int, x: Double, y: Double, z: Double, w: Double, crossinline noise: (Int, Double, Double, Double, Double) -> Double, + octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { + var sum = 1.0 - Math.abs(noise(seed, x, y, z, w)) + var amp = 1.0 + + var x = x + var y = y + var z = z + var w = w + for (i in 1 until octaves) { + x *= lacunarity + y *= lacunarity + z *= lacunarity + w *= lacunarity + amp *= gain + sum -= (1.0 - abs(noise(seed + i, x, y, z, w))) * amp + } + return sum +} + +inline fun rigid(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double, + octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { + var sum = 1.0 - Math.abs(noise(seed, x, y, z)) + var amp = 1.0 + + var x = x + var y = y + var z = z + for (i in 1 until octaves) { + x *= lacunarity + y *= lacunarity + z *= lacunarity + amp *= gain + sum -= (1.0 - abs(noise(seed + i, x, y, z))) * amp } return sum } @@ -85,21 +243,40 @@ inline fun rigid(seed: Int, x: Double, y: Double, crossinline noise: (Int, Doubl return sum } -inline fun rigid(seed: Int, x: Double, y: Double, z: Double, crossinline noise: (Int, Double, Double, Double) -> Double, +inline fun rigid(seed: Int, x: Double, crossinline noise: (Int, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double { - var sum = 1.0 - Math.abs(noise(seed, x, y, z)) + var sum = 1.0 - abs(noise(seed, x)) var amp = 1.0 var x = x - var y = y - var z = z for (i in 1 until octaves) { x *= lacunarity - y *= lacunarity - z *= lacunarity amp *= gain - sum -= (1.0 - Math.abs(noise(seed + i, x, y, z))) * amp + sum -= (1.0 - abs(noise(seed + i, x))) * amp } return sum } +inline fun rigidFunc1D(crossinline noise: (Int, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double) -> Double { + return { seed, x -> + rigid(seed, x, noise, octaves, lacunarity, gain) + } +} + +inline fun rigidFunc2D(crossinline noise: (Int, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double) -> Double { + return { seed, x, y -> + rigid(seed, x, y, noise, octaves, lacunarity, gain) + } +} + +inline fun rigidFunc3D(crossinline noise: (Int, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double, Double) -> Double { + return { seed, x, y, z -> + rigid(seed, x, y, z, noise, octaves, lacunarity, gain) + } +} + +inline fun rigidFunc4D(crossinline noise: (Int, Double, Double, Double, Double) -> Double, octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): (Int, Double, Double, Double, Double) -> Double { + return { seed, x, y, z, w -> + rigid(seed, x, y, z, w, noise, octaves, lacunarity, gain) + } +} diff --git a/orx-noise/src/main/kotlin/GaussianRandom.kt b/orx-noise/src/main/kotlin/GaussianRandom.kt new file mode 100644 index 00000000..6ca43ef6 --- /dev/null +++ b/orx-noise/src/main/kotlin/GaussianRandom.kt @@ -0,0 +1,37 @@ +package org.openrndr.extra.noise + +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 +import org.openrndr.math.Vector4 +import kotlin.random.Random + +fun gaussian(mean: Double = 0.0, deviation: Double = 1.0, random: Random = Random.Default): Double { + var v1: Double + var v2: Double + var s: Double + do { + v1 = 2 * random.nextDouble() - 1 + v2 = 2 * random.nextDouble() - 1 + s = v1 * v1 + v2 * v2 + } while (s >= 1 || s == 0.0) + val multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s) + + return v1 * multiplier * deviation + mean +} + +fun Double.Companion.gaussian(mean: Double = 0.0, deviation: Double = 1.0, random: Random = Random.Default): Double { + return gaussian(mean, deviation, random) +} + +fun Vector2.Companion.gaussian(mean: Vector2 = Vector2.ZERO, deviation: Vector2 = Vector2.ONE, random: Random = Random.Default): Vector2 { + return Vector2(gaussian(mean.x, deviation.x, random), gaussian(mean.y, deviation.y, random)) +} + +fun Vector3.Companion.gaussian(mean: Vector3 = Vector3.ZERO, deviation: Vector3 = Vector3.ONE, random: Random = Random.Default): Vector3 { + return Vector3(gaussian(mean.x, deviation.x, random), gaussian(mean.y, deviation.y, random), gaussian(mean.z, deviation.z, random)) +} + +fun Vector4.Companion.gaussian(mean: Vector4 = Vector4.ZERO, deviation: Vector4 = Vector4.ONE, random: Random = Random.Default): Vector4 { + return Vector4(gaussian(mean.x, deviation.x, random), gaussian(mean.y, deviation.y, random), gaussian(mean.z, deviation.z, random), gaussian(mean.w, deviation.w, random)) +} + diff --git a/orx-noise/src/main/kotlin/Gradient.kt b/orx-noise/src/main/kotlin/Gradient.kt new file mode 100644 index 00000000..b5a7c579 --- /dev/null +++ b/orx-noise/src/main/kotlin/Gradient.kt @@ -0,0 +1,72 @@ +package org.openrndr.extra.noise + +import org.openrndr.math.Vector2 +import org.openrndr.math.Vector3 +import org.openrndr.math.Vector4 + +inline fun gradient( + crossinline noise: (seed: Int, x: Double) -> Double, + seed: Int, + x: Double, + epsilon: Double = 0.01 +): Double { + val xn = noise(seed, x - epsilon) + val xp = noise(seed, x + epsilon) + return (xp - xn) / (2.0 * epsilon) +} + +inline fun gradient( + crossinline noise: (seed: Int, x: Double, y: Double) -> Double, + seed: Int, + x: Double, + y: Double, + epsilon: Double = 0.01 +): Vector2 { + val xn = noise(seed, x - epsilon, y) + val xp = noise(seed, x + epsilon, y) + val yn = noise(seed, x, y - epsilon) + val yp = noise(seed, x, y + epsilon) + return Vector2((xp - xn) / (2.0 * epsilon), (yp - yn) / (2.0 * epsilon)) +} + +inline fun gradient( + crossinline noise: (seed: Int, x: Double, y: Double, z: Double) -> Double, + seed: Int, + x: Double, + y: Double, + z: Double, + epsilon: Double = 0.01 +): Vector3 { + val xn = noise(seed, x - epsilon, y, z) + val xp = noise(seed, x + epsilon, y, z) + val yn = noise(seed, x, y - epsilon, z) + val yp = noise(seed, x, y + epsilon, z) + val zn = noise(seed, x, y, z - epsilon) + val zp = noise(seed, x, y, z + epsilon) + return Vector3((xp - xn) / (2.0 * epsilon), (yp - yn) / (2.0 * epsilon), (zp - zn) / (2.0 * epsilon)) +} + +inline fun gradient( + crossinline noise: (seed: Int, x: Double, y: Double, z: Double, w: Double) -> Double, + seed: Int, + x: Double, + y: Double, + z: Double, + w: Double, + epsilon: Double = 0.01 +): Vector4 { + val xn = noise(seed, x - epsilon, y, z, w) + val xp = noise(seed, x + epsilon, y, z, w) + val yn = noise(seed, x, y - epsilon, z, w) + val yp = noise(seed, x, y + epsilon, z, w) + val zn = noise(seed, x, y, z - epsilon, w) + val zp = noise(seed, x, y, z + epsilon, w) + val wn = noise(seed, x, y, z, w - epsilon) + val wp = noise(seed, x, y, z, w + epsilon) + return Vector4( + (xp - xn) / (2.0 * epsilon), + (yp - yn) / (2.0 * epsilon), + (zp - zn) / (2.0 * epsilon), + (wp - wn) / (2.0 * epsilon) + ) +} \ No newline at end of file diff --git a/orx-noise/src/main/kotlin/PerlinNoise1D.kt b/orx-noise/src/main/kotlin/PerlinNoise1D.kt new file mode 100644 index 00000000..95539e8d --- /dev/null +++ b/orx-noise/src/main/kotlin/PerlinNoise1D.kt @@ -0,0 +1,9 @@ +package org.openrndr.extra.noise + +fun perlin(seed: Int, x: Double) = perlin(seed, x, ::linear) +fun perlinLinear(seed: Int, x: Double) = perlin(seed, x, ::linear) +fun perlinQuintic(seed: Int, x: Double) = perlin(seed, x, ::quintic) +fun perlinHermite(seed: Int, x: Double) = perlin(seed, x, ::hermite) + +inline fun perlin(seed: Int, x: Double, crossinline interpolator: (Double) -> Double): Double = + perlin(seed, x, 0.0, interpolator) \ No newline at end of file diff --git a/orx-noise/src/main/kotlin/SimplexNoise1D.kt b/orx-noise/src/main/kotlin/SimplexNoise1D.kt new file mode 100644 index 00000000..205edde3 --- /dev/null +++ b/orx-noise/src/main/kotlin/SimplexNoise1D.kt @@ -0,0 +1,3 @@ +package org.openrndr.extra.noise + +fun simplex(seed: Int, x: Double): Double = simplex(seed, x, 0.0) \ No newline at end of file diff --git a/orx-noise/src/main/kotlin/SimplexNoise2D.kt b/orx-noise/src/main/kotlin/SimplexNoise2D.kt index 13706e29..360a20fe 100644 --- a/orx-noise/src/main/kotlin/SimplexNoise2D.kt +++ b/orx-noise/src/main/kotlin/SimplexNoise2D.kt @@ -3,11 +3,7 @@ package org.openrndr.extra.noise private const val G2 = 1.0 / 4.0 private const val F2 = 1.0 / 2.0 -fun simplexLinear(seed: Int, x: Double, y: Double) = simplex(seed, x, y, ::linear) -fun simplexQuintic(seed: Int, x: Double, y: Double) = simplex(seed, x, y, ::quintic) -fun simplexHermite(seed: Int, x: Double, y: Double) = simplex(seed, x, y, ::hermite) - -fun simplex(seed: Int, x: Double, y: Double, interpolator: (Double) -> Double = ::linear): Double { +fun simplex(seed: Int, x: Double, y: Double): Double { var t = (x + y) * F2 val i = (x + t).fastFloor() val j = (y + t).fastFloor() @@ -16,8 +12,8 @@ fun simplex(seed: Int, x: Double, y: Double, interpolator: (Double) -> Double = val X0 = i - t val Y0 = j - t - val x0 = interpolator(x - X0) - val y0 = interpolator(y - Y0) + val x0 = x - X0 + val y0 = y - Y0 val i1: Int val j1: Int @@ -29,10 +25,10 @@ fun simplex(seed: Int, x: Double, y: Double, interpolator: (Double) -> Double = j1 = 1 } - val x1 = x0 - i1 + G2 - val y1 = y0 - j1 + G2 - val x2 = x0 - 1 + F2 - val y2 = y0 - 1 + F2 + val x1 = (x0 - i1 + G2) + val y1 = (y0 - j1 + G2) + val x2 = (x0 - 1 + F2) + val y2 = (y0 - 1 + F2) val n0: Double val n1: Double diff --git a/orx-noise/src/main/kotlin/SimplexNoise3D.kt b/orx-noise/src/main/kotlin/SimplexNoise3D.kt index fc45db00..ccf36b77 100644 --- a/orx-noise/src/main/kotlin/SimplexNoise3D.kt +++ b/orx-noise/src/main/kotlin/SimplexNoise3D.kt @@ -1,10 +1,7 @@ package org.openrndr.extra.noise -fun simplexLinear(seed: Int, x: Double, y: Double, z: Double) = simplex(seed, x, y, z, ::linear) -fun simplexQuintic(seed: Int, x: Double, y: Double, z: Double) = simplex(seed, x, y, z, ::quintic) -fun simplexHermite(seed: Int, x: Double, y: Double, z: Double) = simplex(seed, x, y, z, ::hermite) -fun simplex(seed: Int, x: Double, y: Double, z: Double, interpolator: (Double) -> Double = ::linear): Double { +fun simplex(seed: Int, x: Double, y: Double, z: Double): Double { val t = (x + y + z) / 3.0 val i = (x + t).fastFloor() @@ -12,9 +9,9 @@ fun simplex(seed: Int, x: Double, y: Double, z: Double, interpolator: (Double) - val k = (z + t).fastFloor() val t2 = (i + j + k) / 6.0 - val x0 = interpolator(x - (i - t2)) - val y0 = interpolator(y - (j - t2)) - val z0 = interpolator(z - (k - t2)) + val x0 = x - (i - t2) + val y0 = y - (j - t2) + val z0 = z - (k - t2) val i1: Int val j1: Int diff --git a/orx-noise/src/main/kotlin/SimplexNoise4D.kt b/orx-noise/src/main/kotlin/SimplexNoise4D.kt index 135271f0..14e19db7 100644 --- a/orx-noise/src/main/kotlin/SimplexNoise4D.kt +++ b/orx-noise/src/main/kotlin/SimplexNoise4D.kt @@ -14,11 +14,8 @@ private val SIMPLEX_4D = byteArrayOf( private const val F4 = ((2.23606797 - 1.0) / 4.0) private const val G4 = ((5.0 - 2.23606797) / 20.0) -fun simplexLinear(seed: Int, x: Double, y: Double, z: Double, w: Double) = simplex(seed, x, y, z, w, ::linear) -fun simplexQuintic(seed: Int, x: Double, y: Double, z: Double, w: Double) = simplex(seed, x, y, z, w, ::quintic) -fun simplexHermite(seed: Int, x: Double, y: Double, z: Double, w: Double) = simplex(seed, x, y, z, w, ::hermite) -fun simplex(seed: Int, x: Double, y: Double, z: Double, w: Double, interpolator: (Double) -> Double = ::linear): Double { +fun simplex(seed: Int, x: Double, y: Double, z: Double, w: Double): Double { var t = (x + y + z + w) * F4 val i = (x + t).fastFloor() @@ -27,10 +24,10 @@ fun simplex(seed: Int, x: Double, y: Double, z: Double, w: Double, interpolator: val l = (w + t).fastFloor() val t2 = (i + j + k + l) * G4 - val x0 = interpolator(x - (i - t2)) - val y0 = interpolator(y - (j - t2)) - val z0 = interpolator(z - (k - t2)) - val w0 = interpolator(w - (l - t2)) + val x0 = x - (i - t2) + val y0 = y - (j - t2) + val z0 = z - (k - t2) + val w0 = w - (l - t2) var c = if (x0 > y0) 32 else 0 c += if (x0 > z0) 16 else 0 diff --git a/orx-noise/src/main/kotlin/UniformRandom.kt b/orx-noise/src/main/kotlin/UniformRandom.kt index 2822aa1c..d05466e1 100644 --- a/orx-noise/src/main/kotlin/UniformRandom.kt +++ b/orx-noise/src/main/kotlin/UniformRandom.kt @@ -9,6 +9,8 @@ fun random(min: Double = -1.0, max: Double = 1.0, random: Random = Random.Defaul return (random.nextDouble() * (max - min)) + min } + + fun Double.Companion.uniform(min: Double = -1.0, max: Double = 1.0, random: Random = Random.Default): Double { return (random.nextDouble() * (max - min)) + min } diff --git a/orx-noise/src/main/kotlin/ValueNoise1D.kt b/orx-noise/src/main/kotlin/ValueNoise1D.kt new file mode 100644 index 00000000..f9a8004f --- /dev/null +++ b/orx-noise/src/main/kotlin/ValueNoise1D.kt @@ -0,0 +1,8 @@ +package org.openrndr.extra.noise + +fun valueLinear(seed: Int, x: Double) = value(seed, x, ::linear) +fun valueQuintic(seed: Int, x: Double) = value(seed, x, ::quintic) +fun valueHermite(seed: Int, x: Double) = value(seed, x, ::hermite) + +inline fun value(seed: Int, x: Double, crossinline interpolation: (Double) -> Double = ::linear): Double = + value(seed, x, 0.0) \ No newline at end of file diff --git a/orx-noise/src/test/kotlin/TestGradient.kt b/orx-noise/src/test/kotlin/TestGradient.kt new file mode 100644 index 00000000..0afe7607 --- /dev/null +++ b/orx-noise/src/test/kotlin/TestGradient.kt @@ -0,0 +1,33 @@ +import org.openrndr.extra.noise.* +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.specification.describe +import kotlin.test.assertEquals + +object TestGradient : Spek({ + describe("Noise") { + it("has a gradient") { + gradient(::perlinLinear, 100, 0.1) + } + } + + describe("FBM noise func") { + it("has a gradient") { + val func = fbmFunc1D(::perlinLinear) + gradient(func, 100, 0.1) + } + } + + describe("Billow noise func") { + it("has a gradient") { + val func = billowFunc1D(::perlinLinear) + gradient(func, 100, 0.1) + } + } + + describe("Rigid noise func") { + it("has a gradient") { + val func = rigidFunc1D(::perlinLinear) + gradient(func, 100, 0.1) + } + } +}) \ No newline at end of file