Files
orx/orx-math/src/jvmDemo/kotlin/rbf/RbfInterpolation02.kt

113 lines
4.5 KiB
Kotlin

package rbf
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.isolated
import org.openrndr.draw.shadeStyle
import org.openrndr.extra.color.spaces.OKHSV
import org.openrndr.extra.color.spaces.OKLab
import org.openrndr.extra.color.tools.shadeLuminosity
import org.openrndr.extra.color.tools.shiftHue
import org.openrndr.extra.math.rbf.Rbf2DInterpolator
import org.openrndr.extra.math.rbf.rbfGaussian
import org.openrndr.extra.math.rbf.rbfInverseMultiQuadratic
import org.openrndr.extra.math.rbf.rbfInverseQuadratic
import org.openrndr.extra.noise.uniform
import org.openrndr.extra.shaderphrases.noise.fhash12Phrase
import org.openrndr.extra.shaderphrases.rbf.rbfGaussianPhrase
import org.openrndr.extra.shaderphrases.rbf.rbfInverseMultiQuadraticPhrase
import org.openrndr.extra.shaderphrases.rbf.rbfInverseQuadraticPhrase
import org.openrndr.math.Vector3
import kotlin.collections.indices
import kotlin.collections.map
import kotlin.collections.toTypedArray
import kotlin.random.Random
import kotlin.ranges.until
import kotlin.text.trimIndent
import kotlin.text.trimMargin
fun main() {
application {
configure {
width = 720
height = 720
}
program {
val r = Random(0)
val points = drawer.bounds.offsetEdges(-100.0).uniform(20, r)
val colors = (0 until points.size).map {
ColorRGBa.PINK
.shiftHue<OKHSV>(Double.uniform(-180.0, 180.0, r))
.shadeLuminosity<OKLab>(Double.uniform(0.4, 1.0, r))
.toLinear()
}
// Here the `scale` and `smoothing` values are hand-tuned
val scale = 0.04 / 5.0
val interpolator = Rbf2DInterpolator(
points,
colors.map { doubleArrayOf(it.r, it.g, it.b) }.toTypedArray<DoubleArray>(),
smoothing = 0.09,
rbf = rbfInverseMultiQuadratic(scale)
)
/**
* Shader style that implements RBF interpolation in the fragment shader.
* Uses Gaussian RBF function to interpolate colors between given points.
* Includes custom distance calculation and color interpolation functions.
*/
val ss = shadeStyle {
fragmentPreamble = """${fhash12Phrase}
|${rbfInverseMultiQuadraticPhrase}
|float squaredDistance(vec2 p, vec2 q) {
| vec2 d = p - q;
| return dot(d, d);
|}
|vec3 rbfInterpolate(vec2 p) {
| vec3 c = p_mean;
| for (int i = 0; i < p_weights_SIZE; ++i) {
| float r = rbfInverseMultiQuadratic(squaredDistance(p_points[i], p), $scale);
| c.r += p_weights[i].r * r;
| c.g += p_weights[i].g * r;
| c.b += p_weights[i].b * r;
| }
| return c;
|}
""".trimMargin()
fragmentTransform = """
x_fill.rgb = rbfInterpolate(c_boundsPosition.xy * vec2(720.0, 720.0));
""".trimIndent()
val weights = (0 until points.size).map {
Vector3(interpolator.weights[it][0], interpolator.weights[it][1], interpolator.weights[it][2])
}.toTypedArray()
parameter("weights", weights)
parameter("points", points.toTypedArray())
parameter("mean", Vector3(interpolator.mean[0], interpolator.mean[1], interpolator.mean[2]))
}
extend {
// draw the interpolated colors
drawer.isolated {
drawer.shadeStyle = ss
drawer.rectangle(drawer.bounds)
}
// draw the original points and colors for reference
drawer.circles {
for (i in points.indices) {
fill = colors[i]
circle(points[i], 10.0)
}
}
// compute color on CPU for comparison
drawer.fill = interpolator.interpolate(mouse.position).let {
ColorRGBa(it[0], it[1], it[2], 1.0)
}
drawer.circle(mouse.position, 30.0)
}
}
}
}