commonMain.ru.casperix.math.perlin.ValueNoise2D.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of math Show documentation
Show all versions of math Show documentation
Simple set of geometric and other types
package ru.casperix.math.perlin
import ru.casperix.math.vector.float64.Vector3d
import ru.casperix.misc.pow
import ru.casperix.math.function.Random2DFunction
import ru.casperix.math.function.Random3DFunction
import ru.casperix.math.function.primeNumberList
import ru.casperix.math.function.random2dFunctions
import ru.casperix.math.function.Function2D
import ru.casperix.math.interpolation.float64.InterpolateDoubleFunction
import ru.casperix.math.interpolation.float64.cosineInterpolate
import kotlin.math.pow
/**
* https://habr.com/ru/post/142592/
*/
class ValueNoise2D(val persistence: Double = 0.5, val octaveAmount: Int = 8, val scale: Vector3d = Vector3d.ONE, val interpolateFunction: InterpolateDoubleFunction = cosineInterpolate, val smoothed: Boolean = true) {
class OctaveCache(val frequencyX: Double, val frequencyY: Double, val amplitude: Double, val primeNumber: Int, val randomFunction: Random2DFunction)
val normalizer: Double
val octaves: Array
companion object {
val randomWithFactor: Random3DFunction =
{ x, y, factor ->
val it = x + y * factor
val n = (it shl 13) xor it
(1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) and 0x7FFFFFFF) / 1073741824.0)
}
}
init {
if (octaveAmount < 1) throw Error("Octave amount must be positive (now: $octaveAmount)")
this.octaves = Array(octaveAmount) { index ->
val frequency = 2.pow(index)
val amplitude = persistence.pow(index)
val primeNumber = primeNumberList[(index + 128) % primeNumberList.size]
OctaveCache(frequency.toDouble() * scale.x, frequency.toDouble() * scale.y, amplitude, primeNumber, random2dFunctions[index % random2dFunctions.size])
}
var amplitude = 0.0
octaves.forEach { amplitude += it.amplitude }
normalizer = ((if (smoothed) 2.0 else 1.0) / amplitude) * scale.z
}
val output: Function2D = { x, y ->
var total = 0.0
for (octave in octaves) {
total += interpolate2dFlat(x * octave.frequencyX, y * octave.frequencyY, octave.primeNumber) * octave.amplitude
}
total * normalizer
}
private fun interpolate2dFlat(x: Double, y: Double, randomFactor: Int): Double {
val baseX = x.toInt()
val fractionalX = x - baseX
val baseY = y.toInt()
val fractionalY = y - baseY
val v1 = randomWithFactor(baseX, baseY, randomFactor)
val v2 = randomWithFactor(baseX + 1, baseY, randomFactor)
val v3 = randomWithFactor(baseX, baseY + 1, randomFactor)
val v4 = randomWithFactor(baseX + 1, baseY + 1, randomFactor)
val i1 = interpolateFunction(v1, v2, fractionalX)
val i2 = interpolateFunction(v3, v4, fractionalX)
return interpolateFunction(i1, i2, fractionalY)
}
}