Convert orx-shader-phrases and orx-noise to MPP
This commit is contained in:
387
orx-noise/src/commonMain/kotlin/Random.kt
Normal file
387
orx-noise/src/commonMain/kotlin/Random.kt
Normal file
@@ -0,0 +1,387 @@
|
||||
package org.openrndr.extra.noise
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.Vector3
|
||||
import org.openrndr.math.Vector4
|
||||
import org.openrndr.shape.Rectangle
|
||||
import kotlin.math.ln
|
||||
import kotlin.math.max
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
import org.openrndr.extra.noise.fbm as orxFbm
|
||||
import kotlin.random.Random as DefaultRandom
|
||||
|
||||
private data class RandomState(var seed: String, var rng: DefaultRandom)
|
||||
/**
|
||||
* Deterministic Random using a seed to guarantee the same random values between iterations
|
||||
*/
|
||||
object Random {
|
||||
var rnd: DefaultRandom
|
||||
|
||||
private var seedTracking: Int = 0
|
||||
private var nextGaussian: Double = 0.0
|
||||
private var hasNextGaussian = false
|
||||
|
||||
private var state: RandomState
|
||||
|
||||
enum class Fractal {
|
||||
FBM, BILLOW, RIGID
|
||||
}
|
||||
|
||||
enum class Noise {
|
||||
LINEAR, QUINTIC, HERMIT
|
||||
}
|
||||
|
||||
var seed: String = "OPENRNDR"
|
||||
set(value) {
|
||||
field = value
|
||||
rnd = newRandomGenerator(value)
|
||||
}
|
||||
|
||||
init {
|
||||
rnd = newRandomGenerator(seed)
|
||||
|
||||
state = RandomState(seed, rnd)
|
||||
}
|
||||
|
||||
private fun newRandomGenerator(newSeed: String): DefaultRandom {
|
||||
return DefaultRandom(stringToInt(newSeed))
|
||||
}
|
||||
|
||||
private fun stringToInt(str: String): Int = str.toCharArray().fold(0) { i: Int, c: Char ->
|
||||
i + c.toInt()
|
||||
}
|
||||
|
||||
fun resetState() {
|
||||
rnd = newRandomGenerator(seed)
|
||||
}
|
||||
|
||||
fun randomizeSeed() {
|
||||
val seedBase = seed.replace(Regex("""-\d+"""), "")
|
||||
seedTracking = int0(999999)
|
||||
seed = "${seedBase}-${seedTracking}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when you want to get non-deterministic values aka random every call
|
||||
*
|
||||
* @param fn
|
||||
*/
|
||||
fun unseeded(fn: () -> Unit) {
|
||||
push()
|
||||
|
||||
fn()
|
||||
|
||||
pop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this when you want to get non-deterministic values aka random every call
|
||||
* Follow it by calling .pop
|
||||
*/
|
||||
fun push() {
|
||||
state = RandomState(seed, rnd)
|
||||
|
||||
rnd = DefaultRandom
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to go back to seeded state
|
||||
*/
|
||||
fun pop() {
|
||||
resetState()
|
||||
|
||||
seed = state.seed
|
||||
rnd = state.rng
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this inside `extend` methods to get the same result between iterations
|
||||
*
|
||||
* @param fn
|
||||
*/
|
||||
fun isolated(fn: Random.() -> Unit) {
|
||||
val state = rnd
|
||||
val currentSeed = seed
|
||||
|
||||
resetState()
|
||||
|
||||
this.fn()
|
||||
|
||||
seed = currentSeed
|
||||
rnd = state
|
||||
}
|
||||
|
||||
fun double(min: Double = -1.0, max: Double = 1.0): Double {
|
||||
return Double.uniform(min, max, rnd)
|
||||
}
|
||||
|
||||
fun double0(max: Double = 1.0): Double {
|
||||
return rnd.nextDouble(max)
|
||||
}
|
||||
|
||||
fun int0(max: Int = Int.MAX_VALUE): Int {
|
||||
return rnd.nextInt(max)
|
||||
}
|
||||
|
||||
fun int(min: Int = 0, max: Int = Int.MAX_VALUE): Int {
|
||||
return rnd.nextInt(min, max)
|
||||
}
|
||||
|
||||
fun bool(probability: Double = 0.5): Boolean {
|
||||
return rnd.nextDouble(1.0) < probability
|
||||
}
|
||||
|
||||
fun <T> pick(coll: Collection<T>): T {
|
||||
return pick(coll, count = 1).first()
|
||||
}
|
||||
|
||||
fun <T> pick(coll: Collection<T>, compareAgainst: Collection<T>): T {
|
||||
return pick(coll, compareAgainst, 1).first()
|
||||
}
|
||||
|
||||
fun <T> pick(coll: Collection<T>, compareAgainst: Collection<T> = listOf(), count: Int): MutableList<T> {
|
||||
var list = coll.toMutableList()
|
||||
val picked = mutableListOf<T>()
|
||||
|
||||
if (coll.isEmpty()) return list
|
||||
|
||||
while (picked.size < count) {
|
||||
if (list.isEmpty()) {
|
||||
list = coll.toMutableList()
|
||||
}
|
||||
|
||||
var index = int0(list.size)
|
||||
var newElem = list.elementAt(index)
|
||||
|
||||
while (compareAgainst.contains(newElem)) {
|
||||
index = int0(list.size)
|
||||
newElem = list.elementAt(index)
|
||||
}
|
||||
|
||||
picked.add(list[index])
|
||||
list.removeAt(index)
|
||||
}
|
||||
|
||||
return picked
|
||||
}
|
||||
|
||||
fun gaussian(mean: Double = 0.0, standardDeviation: Double = 1.0): Double {
|
||||
return gaussian(mean, standardDeviation, rnd)
|
||||
}
|
||||
|
||||
/**
|
||||
* https://en.wikipedia.org/wiki/Pareto_distribution
|
||||
*/
|
||||
fun pareto(alpha: Double = 1.0): () -> Double {
|
||||
val invAlpha = 1.0 / max(alpha, 0.0)
|
||||
|
||||
return {
|
||||
1.0 / (1.0 - double0()).pow(invAlpha)
|
||||
}
|
||||
}
|
||||
|
||||
fun vector2(min: Double = -1.0, max: Double = 1.0): Vector2 {
|
||||
return Vector2.uniform(min, max, rnd)
|
||||
}
|
||||
|
||||
fun vector3(min: Double = -1.0, max: Double = 1.0): Vector3 {
|
||||
return Vector3.uniform(min, max, rnd)
|
||||
}
|
||||
|
||||
fun vector4(min: Double = -1.0, max: Double = 1.0): Vector4 {
|
||||
return Vector4.uniform(min, max, rnd)
|
||||
}
|
||||
|
||||
fun perlin(x: Double, y: Double, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> perlinLinear(sd, x, y)
|
||||
Noise.QUINTIC -> perlinQuintic(sd, x, y)
|
||||
Noise.HERMIT -> perlinHermite(sd, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
fun perlin(position: Vector2, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> perlinLinear(sd, position)
|
||||
Noise.QUINTIC -> perlinQuintic(sd, position)
|
||||
Noise.HERMIT -> perlinHermite(sd, position)
|
||||
}
|
||||
}
|
||||
|
||||
fun perlin(x: Double, y: Double, z: Double, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> perlinLinear(sd, x, y, z)
|
||||
Noise.QUINTIC -> perlinQuintic(sd, x, y, z)
|
||||
Noise.HERMIT -> perlinHermite(sd, x, y, z)
|
||||
}
|
||||
}
|
||||
|
||||
fun perlin(position: Vector3, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> perlinLinear(sd, position)
|
||||
Noise.QUINTIC -> perlinQuintic(sd, position)
|
||||
Noise.HERMIT -> perlinHermite(sd, position)
|
||||
}
|
||||
}
|
||||
|
||||
fun value(x: Double, y: Double, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> valueLinear(sd, x, y)
|
||||
Noise.QUINTIC -> valueQuintic(sd, x, y)
|
||||
Noise.HERMIT -> valueHermite(sd, x, y)
|
||||
}
|
||||
}
|
||||
|
||||
fun value(position: Vector2, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> valueLinear(sd, position)
|
||||
Noise.QUINTIC -> valueQuintic(sd, position)
|
||||
Noise.HERMIT -> valueHermite(sd, position)
|
||||
}
|
||||
}
|
||||
|
||||
fun value(x: Double, y: Double, z: Double, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> valueLinear(sd, x, y, z)
|
||||
Noise.QUINTIC -> valueQuintic(sd, x, y, z)
|
||||
Noise.HERMIT -> valueHermite(sd, x, y, z)
|
||||
}
|
||||
}
|
||||
|
||||
fun value(position: Vector3, type: Noise = Noise.LINEAR): Double {
|
||||
val sd = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Noise.LINEAR -> valueLinear(sd, position)
|
||||
Noise.QUINTIC -> valueQuintic(sd, position)
|
||||
Noise.HERMIT -> valueHermite(sd, position)
|
||||
}
|
||||
}
|
||||
|
||||
fun simplex(x: Double, y: Double): Double {
|
||||
return simplex(stringToInt(seed), x, y)
|
||||
}
|
||||
|
||||
fun simplex(position: Vector2): Double {
|
||||
return simplex(stringToInt(seed), position)
|
||||
}
|
||||
|
||||
fun simplex(x: Double, y: Double, z: Double): Double {
|
||||
return simplex(stringToInt(seed), x, y, z)
|
||||
}
|
||||
|
||||
fun simplex(position: Vector3): Double {
|
||||
return simplex(stringToInt(seed), position)
|
||||
}
|
||||
|
||||
fun simplex(x: Double, y: Double, z: Double, w: Double): Double {
|
||||
return simplex(stringToInt(seed), x, y, z, w)
|
||||
}
|
||||
|
||||
fun simplex(position: Vector4): Double {
|
||||
return simplex(stringToInt(seed), position)
|
||||
}
|
||||
|
||||
fun fbm(x: Double, y: Double, noiseFun: (Int, Double, Double) -> Double, type: Fractal = Fractal.FBM,
|
||||
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
|
||||
val s = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Fractal.FBM -> orxFbm(s, x, y, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.RIGID -> rigid(s, x, y, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.BILLOW -> billow(s, x, y, noiseFun, octaves, lacunarity, gain)
|
||||
}
|
||||
}
|
||||
|
||||
fun fbm(position: Vector2, noiseFun: (Int, Double, Double) -> Double,
|
||||
type: Fractal = Fractal.FBM,
|
||||
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
|
||||
val s = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Fractal.FBM -> orxFbm(s, position, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.RIGID -> rigid(s, position, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.BILLOW -> billow(s, position, noiseFun, octaves, lacunarity, gain)
|
||||
}
|
||||
}
|
||||
|
||||
fun fbm(x: Double, y: Double, z: Double, noiseFun: (Int, Double, Double, Double) -> Double, type: Fractal = Fractal.FBM,
|
||||
octaves: Int = 8, lacunarity: Double = 0.5, gain: Double = 0.5): Double {
|
||||
val s = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Fractal.FBM -> orxFbm(s, x, y, z, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.RIGID -> rigid(s, x, y, z, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.BILLOW -> billow(s, x, y, z, noiseFun, octaves, lacunarity, gain)
|
||||
}
|
||||
}
|
||||
|
||||
fun fbm(position: Vector3, noiseFun: (Int, Double, Double, Double) -> Double,
|
||||
type: Fractal = Fractal.FBM, octaves: Int = 8,
|
||||
lacunarity: Double = 0.5, gain: Double = 0.5): Double {
|
||||
val s = stringToInt(seed)
|
||||
|
||||
return when (type) {
|
||||
Fractal.FBM -> orxFbm(s, position, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.RIGID -> rigid(s, position, noiseFun, octaves, lacunarity, gain)
|
||||
Fractal.BILLOW -> billow(s, position, noiseFun, octaves, lacunarity, gain)
|
||||
}
|
||||
}
|
||||
|
||||
fun cubic(x: Double, y: Double): Double {
|
||||
return cubic(stringToInt(seed), x, y)
|
||||
}
|
||||
|
||||
fun cubic(position: Vector2): Double {
|
||||
return cubic(stringToInt(seed), position)
|
||||
}
|
||||
|
||||
fun cubic(x: Double, y: Double, z: Double): Double {
|
||||
return cubic(stringToInt(seed), x, y, z)
|
||||
}
|
||||
|
||||
fun cubic(position: Vector3):Double {
|
||||
return cubic(stringToInt(seed), position)
|
||||
}
|
||||
|
||||
fun ring2d(innerRadius: Double = 0.0, outerRadius: Double = 1.0, count: Int = 1): Any {
|
||||
return when (count) {
|
||||
1 -> Vector2.uniformRing(innerRadius, outerRadius, rnd)
|
||||
else -> Vector2.uniformsRing(count, innerRadius, outerRadius, rnd)
|
||||
}
|
||||
}
|
||||
|
||||
fun ring3d(innerRadius: Double = 0.0, outerRadius: Double = 1.0, count: Int = 1): Any {
|
||||
return when (count) {
|
||||
1 -> Vector3.uniformRing(innerRadius, outerRadius, rnd)
|
||||
else -> Vector3.uniformsRing(count, innerRadius, outerRadius, rnd)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> roll(elements: Collection<T>, distribution: (Int) -> List<Double>): T {
|
||||
val result = double0()
|
||||
val probabilities = distribution(elements.size)
|
||||
val index = probabilities.indexOfFirst { result <= it }
|
||||
|
||||
return elements.elementAtOrNull(index) ?: elements.last()
|
||||
}
|
||||
|
||||
fun point(rect: Rectangle): Vector2 {
|
||||
return rect.position(vector2(0.0, 1.0))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user