All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.ru.casperix.math.perlin.ValueNoise2D.kt Maven / Gradle / Ivy

There is a newer version: 1.9.0
Show newest version
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)
	}

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy