[orx-noise] Add demos and extensions for uniform simplex sampling
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package org.openrndr.extra.noise.simplexrange
|
||||
|
||||
import org.openrndr.extra.math.simplexrange.simplexUpscale
|
||||
import org.openrndr.math.LinearType
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* Generates a random value within the convex hull of the elements in the list using a uniform
|
||||
* distribution over the simplex formed by the elements, optionally applying bias to the distribution.
|
||||
*
|
||||
* @param random The random number generator used to produce random values. Defaults to `Random.Default`.
|
||||
* @param biasOrder The number of iterations to apply bias adjustments to the weights. Defaults to 0.
|
||||
* @param biasAmount The magnitude of the bias adjustment applied during each iteration. Defaults to 0.0.
|
||||
* @return A value of type `T` representing the weighted interpolation of the list elements,
|
||||
* with weights sampled uniformly or with bias adjustments if specified.
|
||||
*/
|
||||
fun <T : LinearType<T>> List<T>.uniformSimplex(random: Random = Random.Default,
|
||||
biasOrder: Int = 0,
|
||||
biasAmount: Double = 0.0): T {
|
||||
return when (size) {
|
||||
0 -> error("")
|
||||
1 -> this[0]
|
||||
2 -> {
|
||||
val x = random.nextDouble()
|
||||
this[0] * (1.0 - x) + this[1] * x
|
||||
}
|
||||
|
||||
else -> {
|
||||
val r = DoubleArray(size - 1) { random.nextDouble() }
|
||||
val b = simplexUpscale(r)
|
||||
|
||||
if (biasOrder > 0) {
|
||||
for (i in 0 until biasOrder) {
|
||||
b[random.nextInt(b.size)] += random.nextDouble(biasAmount)
|
||||
}
|
||||
val sum = b.sum()
|
||||
for (i in 0 until b.size) {
|
||||
b[i] = b[i] / sum
|
||||
}
|
||||
}
|
||||
var result = this[0] * b[0]
|
||||
for (i in 1 until size) {
|
||||
result += this[i] * b[i]
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import org.openrndr.math.LinearType
|
||||
import org.openrndr.extra.math.simplexrange.SimplexRange4D
|
||||
import org.openrndr.extra.math.simplexrange.SimplexRange3D
|
||||
import org.openrndr.extra.math.simplexrange.SimplexRange2D
|
||||
import kotlin.math.pow
|
||||
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
@@ -16,6 +18,41 @@ fun <T:LinearType<T>> SimplexRange2D<T>.uniform(random: Random): T {
|
||||
return value(random.nextDouble(), random.nextDouble())
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a uniformly distributed value within the SimplexRange2D.
|
||||
*
|
||||
* @param random the random number generator used to produce random values.
|
||||
* @return a value of type T sampled uniformly within the 2D simplex range.
|
||||
*/
|
||||
fun <T:LinearType<T>> SimplexRange2D<T>.uniformPower(exp: Double, random: Random = Random.Default): T {
|
||||
val b = upscale(random.nextDouble(), random.nextDouble())
|
||||
for (i in 0 until b.size) {
|
||||
b[i] = b[i].pow(exp)
|
||||
}
|
||||
val sum = b.sum()
|
||||
return x0 * b[0] / sum + x1 * b[1] / sum + x2 * b[2] / sum
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random point within the simplex represented by the `SimplexRange2D`,
|
||||
* forming an interpolation of the three control points `x0`, `x1`, `x2` using a weighted
|
||||
* random combination normalized to sum to 1.
|
||||
*
|
||||
* @param random The random number generator used to produce the random weights.
|
||||
* @return A randomly interpolated point of type `T` within the simplex.
|
||||
*/
|
||||
fun <T:LinearType<T>> SimplexRange2D<T>.uniformCube(random: Random): T {
|
||||
val r = DoubleArray(3) { random.nextDouble() }
|
||||
val sum = r.sum()
|
||||
if (sum > 0.0) {
|
||||
for (i in 0 until r.size) {
|
||||
r[i] /= sum
|
||||
}
|
||||
}
|
||||
return this.x0 * r[0] + x1 * r[1] + x2 * r[2]
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a uniformly distributed value within the 3D simplex range.
|
||||
*
|
||||
@@ -26,6 +63,24 @@ fun <T:LinearType<T>> SimplexRange3D<T>.uniform(random: Random): T {
|
||||
return value(random.nextDouble(), random.nextDouble(), random.nextDouble())
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random point within the simplex represented by the `SimplexRange3D`,
|
||||
* forming an interpolation of the four control points using a weighted random combination normalized to sum to 1.
|
||||
*
|
||||
* @param random The random number generator used to produce the random weights.
|
||||
* @return A randomly interpolated point of type `T` within the simplex.
|
||||
*/
|
||||
fun <T:LinearType<T>> SimplexRange3D<T>.uniformCube(random: Random): T {
|
||||
val r = DoubleArray(4) { random.nextDouble() }
|
||||
val sum = r.sum()
|
||||
if (sum > 0.0) {
|
||||
for (i in 0 until r.size) {
|
||||
r[i] /= sum
|
||||
}
|
||||
}
|
||||
return this.x0 * r[0] + x1 * r[1] + x2 * r[2] + x3 * r[3]
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a uniformly distributed value within the 4D simplex range using a random generator.
|
||||
*
|
||||
@@ -35,3 +90,21 @@ fun <T:LinearType<T>> SimplexRange3D<T>.uniform(random: Random): T {
|
||||
fun <T:LinearType<T>> SimplexRange4D<T>.uniform(random: Random): T {
|
||||
return value(random.nextDouble(), random.nextDouble(), random.nextDouble(), random.nextDouble())
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random point within the simplex represented by the `SimplexRange4D`,
|
||||
* forming an interpolation of the five control points using a weighted random combination normalized to sum to 1.
|
||||
*
|
||||
* @param random The random number generator used to produce the random weights.
|
||||
* @return A randomly interpolated point of type `T` within the simplex.
|
||||
*/
|
||||
fun <T:LinearType<T>> SimplexRange4D<T>.uniformCube(random: Random): T {
|
||||
val r = DoubleArray(5) { random.nextDouble() }
|
||||
val sum = r.sum()
|
||||
if (sum > 0.0) {
|
||||
for (i in 0 until r.size) {
|
||||
r[i] /= sum
|
||||
}
|
||||
}
|
||||
return this.x0 * r[0] + x1 * r[1] + x2 * r[2] + x3 * r[3] + x4 * r[4]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package simplexrange
|
||||
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.extra.math.simplexrange.SimplexRange3D
|
||||
import kotlin.random.Random
|
||||
import org.openrndr.extra.noise.simplexrange.uniform
|
||||
import org.openrndr.extra.noise.simplexrange.uniformCube
|
||||
|
||||
/**
|
||||
* This demo creates a dynamic graphical output utilizing simplex and
|
||||
* linear interpolation-based color ranges.
|
||||
*
|
||||
* Functionalities:
|
||||
* - Defines a list of base colors converted to LAB color space for smooth interpolation.
|
||||
* - Constructs a 3D simplex range and a 2D linear range for color sampling.
|
||||
* - Randomly populates two sections of the screen with rectangles filled with colors
|
||||
* sampled from simplex and linear ranges respectively.
|
||||
* - Draws a vertical divider line in the middle of the application window.
|
||||
*/
|
||||
fun main() {
|
||||
application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
}
|
||||
program {
|
||||
extend {
|
||||
val colors = listOf(ColorRGBa.BLACK, ColorRGBa.RED, ColorRGBa.GREEN, ColorRGBa.BLUE).map { it.toLABa() }
|
||||
drawer.stroke = null
|
||||
val sr = SimplexRange3D(colors[0], colors[1], colors[2], colors[3])
|
||||
|
||||
val r = Random((seconds * 2).toInt())
|
||||
|
||||
// Draw the simplex sampling on the left
|
||||
drawer.rectangles {
|
||||
for (y in 0 until 40) {
|
||||
for (x in 0 until 20) {
|
||||
fill = sr.uniform(r).toRGBa()
|
||||
rectangle(x * width / 40.0, y * height / 40.0, width / 40.0, height / 40.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the bilinear sampling on the right
|
||||
drawer.rectangles {
|
||||
for (y in 0 until 40) {
|
||||
for (x in 20 until 40) {
|
||||
fill = sr.uniformCube(r).toRGBa()
|
||||
rectangle(x * width / 40.0, y * height / 40.0, width / 40.0, height / 40.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
drawer.stroke = ColorRGBa.BLACK
|
||||
drawer.lineSegment(drawer.bounds.vertical(0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package simplexrange
|
||||
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.isolated
|
||||
import kotlin.random.Random
|
||||
import org.openrndr.extra.noise.simplexrange.uniformSimplex
|
||||
import org.openrndr.math.Polar
|
||||
import org.openrndr.math.Vector2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
* This demo creates a dynamic graphical output utilizing simplex and
|
||||
* linear interpolation-based color ranges.
|
||||
*
|
||||
* Functionalities:
|
||||
* - Defines a list of base colors converted to LAB color space for smooth interpolation.
|
||||
* - Constructs a 3D simplex range and a 2D linear range for color sampling.
|
||||
* - Randomly populates two sections of the screen with rectangles filled with colors
|
||||
* sampled from simplex and linear ranges respectively.
|
||||
* - Draws a vertical divider line in the middle of the application window.
|
||||
*/
|
||||
fun main() {
|
||||
application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
}
|
||||
program {
|
||||
extend {
|
||||
val positions = (0 until 3).map { Polar((30.0 + it * 120.0), 180.0).cartesian }
|
||||
val colors = listOf(ColorRGBa.PINK, ColorRGBa.RED, ColorRGBa.BLUE).map { it.toLABa() }
|
||||
val freq = 1.0
|
||||
|
||||
drawer.stroke = null
|
||||
|
||||
val power = cos(floor(seconds*freq)/freq) *16.0 + 16.0
|
||||
drawer.isolated {
|
||||
drawer.translate(drawer.bounds.position(0.25,0.25) + Vector2(0.0, 40.0))
|
||||
val rp = Random((seconds * freq).toInt())
|
||||
val rc = Random((seconds * freq).toInt())
|
||||
for (i in 0 until 32 * 32) {
|
||||
drawer.fill = colors.uniformSimplex(rc, 1, power).toRGBa()
|
||||
drawer.circle(positions.uniformSimplex(rp, 1, power), 2.0)
|
||||
}
|
||||
}
|
||||
drawer.isolated {
|
||||
drawer.translate(drawer.bounds.position(0.75,0.75) + Vector2(0.0, 40.0))
|
||||
val rp = Random((seconds * freq).toInt())
|
||||
val rc = Random((seconds * freq).toInt())
|
||||
for (i in 0 until 32 * 32) {
|
||||
drawer.fill = colors.uniformSimplex(rc, 2, power).toRGBa()
|
||||
drawer.circle(positions.uniformSimplex(rp, 2, power), 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
drawer.isolated {
|
||||
drawer.stroke = null
|
||||
val rc = Random((seconds * freq).toInt())
|
||||
drawer.translate(drawer.bounds.position(0.5,0.0) + Vector2(20.0, 20.0))
|
||||
|
||||
for (i in 0 until 32 * 32) {
|
||||
val x = i.mod(32)
|
||||
val y = i / 32
|
||||
drawer.fill = colors.uniformSimplex(rc, 1, power).toRGBa()
|
||||
drawer.rectangle(x * 10.0, y * 10.0, 10.0, 10.0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
drawer.isolated {
|
||||
drawer.stroke = null
|
||||
val rc = Random((seconds * freq).toInt())
|
||||
|
||||
drawer.translate(drawer.bounds.position(0.0,0.5) + Vector2(20.0, 20.0))
|
||||
|
||||
for (i in 0 until 32 * 32) {
|
||||
val x = i.mod(32)
|
||||
val y = i / 32
|
||||
drawer.fill = colors.uniformSimplex(rc, 2, power).toRGBa()
|
||||
drawer.rectangle(x * 10.0, y * 10.0, 10.0, 10.0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package simplexrange
|
||||
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.isolated
|
||||
import org.openrndr.extra.math.simplexrange.SimplexRange2D
|
||||
import org.openrndr.extra.math.simplexrange.SimplexRange3D
|
||||
import org.openrndr.extra.noise.scatter
|
||||
import kotlin.random.Random
|
||||
import org.openrndr.extra.noise.simplexrange.uniformSimplex
|
||||
import org.openrndr.math.Polar
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.shape.Rectangle
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
* This demo creates a dynamic graphical output utilizing simplex and
|
||||
* linear interpolation-based color ranges.
|
||||
*
|
||||
* Functionalities:
|
||||
* - Defines a list of base colors converted to LAB color space for smooth interpolation.
|
||||
* - Constructs a 3D simplex range and a 2D linear range for color sampling.
|
||||
* - Randomly populates two sections of the screen with rectangles filled with colors
|
||||
* sampled from simplex and linear ranges respectively.
|
||||
* - Draws a vertical divider line in the middle of the application window.
|
||||
*/
|
||||
fun main() {
|
||||
application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
}
|
||||
program {
|
||||
extend {
|
||||
val positions = (0 until 3).map { Polar((30.0 + it * 120.0), 180.0).cartesian }
|
||||
val colors = listOf(ColorRGBa.PINK, ColorRGBa.RED, ColorRGBa.BLUE).map { it.toLABa() }
|
||||
|
||||
val positionsr = SimplexRange2D(positions[0], positions[1], positions[2])
|
||||
val colorsr = SimplexRange2D(colors[0], colors[1], colors[2])
|
||||
val freq = 1.0
|
||||
|
||||
drawer.stroke = null
|
||||
|
||||
|
||||
val r = Random((seconds * freq).toInt())
|
||||
val points = Rectangle(0.0, 0.0, 360.0, 360.0).scatter(10.0, random = r)
|
||||
|
||||
drawer.circles(points, 2.0)
|
||||
|
||||
val power = cos(floor(seconds*freq)/freq) *16.0 + 16.0
|
||||
drawer.isolated {
|
||||
drawer.translate(drawer.bounds.position(0.75,0.25) + Vector2(0.0, 40.0))
|
||||
|
||||
for (point in points) {
|
||||
drawer.fill = colorsr.value(point.x/360.0, point.y/360.0).toRGBa()
|
||||
drawer.circle(positionsr.value(point.x/360.0, point.y/360.0), 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user