[orx-noise] Add ShapeProvider.poissonDiskSampling
This commit is contained in:
@@ -6,6 +6,7 @@ import org.openrndr.math.clamp
|
|||||||
import org.openrndr.shape.Rectangle
|
import org.openrndr.shape.Rectangle
|
||||||
import kotlin.math.ceil
|
import kotlin.math.ceil
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO v2
|
* TODO v2
|
||||||
@@ -24,8 +25,10 @@ internal const val epsilon = 0.0000001
|
|||||||
* @param height the height of the area
|
* @param height the height of the area
|
||||||
* @param r the minimum distance between each point
|
* @param r the minimum distance between each point
|
||||||
* @param tries number of candidates per point
|
* @param tries number of candidates per point
|
||||||
* @param boundsMapper a custom function to check if a point is within bounds
|
|
||||||
* @param randomOnRing generate random points on a ring with an annulus from r to 2r
|
* @param randomOnRing generate random points on a ring with an annulus from r to 2r
|
||||||
|
* @param random a random number generator, default value is [Random.Default]
|
||||||
|
* @param boundsMapper a custom function to check if a point is within bounds
|
||||||
|
|
||||||
* @return a list of points
|
* @return a list of points
|
||||||
*/
|
*/
|
||||||
fun poissonDiskSampling(
|
fun poissonDiskSampling(
|
||||||
@@ -34,7 +37,9 @@ fun poissonDiskSampling(
|
|||||||
r: Double,
|
r: Double,
|
||||||
tries: Int = 30,
|
tries: Int = 30,
|
||||||
randomOnRing: Boolean = false,
|
randomOnRing: Boolean = false,
|
||||||
boundsMapper: ((w: Double, h: Double, v: Vector2) -> Boolean)? = null
|
random: Random = Random.Default,
|
||||||
|
initialPoint: Vector2 = Vector2(width/2.0, height/2.0),
|
||||||
|
boundsMapper: ((w: Double, h: Double, v: Vector2) -> Boolean)? = null,
|
||||||
): List<Vector2> {
|
): List<Vector2> {
|
||||||
val disk = mutableListOf<Vector2>()
|
val disk = mutableListOf<Vector2>()
|
||||||
val queue = mutableListOf<Int>()
|
val queue = mutableListOf<Int>()
|
||||||
@@ -46,7 +51,7 @@ fun poissonDiskSampling(
|
|||||||
val rows = ceil(height / cellSize).toInt()
|
val rows = ceil(height / cellSize).toInt()
|
||||||
val cols = ceil(width / cellSize).toInt()
|
val cols = ceil(width / cellSize).toInt()
|
||||||
|
|
||||||
val grid = List(rows * cols) { -1 }.toMutableList()
|
val grid = MutableList(rows * cols) { -1 }
|
||||||
|
|
||||||
fun addPoint(v: Vector2) {
|
fun addPoint(v: Vector2) {
|
||||||
val x = (v.x / cellSize).fastFloor()
|
val x = (v.x / cellSize).fastFloor()
|
||||||
@@ -60,21 +65,21 @@ fun poissonDiskSampling(
|
|||||||
queue.add(disk.lastIndex)
|
queue.add(disk.lastIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
addPoint(Vector2(width / 2.0, height / 2.0))
|
addPoint(initialPoint)
|
||||||
|
|
||||||
val boundsRect = Rectangle(0.0, 0.0, width, height)
|
val boundsRect = Rectangle(0.0, 0.0, width, height)
|
||||||
|
|
||||||
while (queue.isNotEmpty()) {
|
while (queue.isNotEmpty()) {
|
||||||
val activeIndex = Random.pick(queue)
|
val activeIndex = queue.random(random)
|
||||||
val active = disk[activeIndex]
|
val active = disk[activeIndex]
|
||||||
|
|
||||||
var candidateAccepted = false
|
var candidateAccepted = false
|
||||||
|
|
||||||
candidateSearch@ for (l in 0 until tries) {
|
candidateSearch@ for (l in 0 until tries) {
|
||||||
val c = if (randomOnRing) {
|
val c = if (randomOnRing) {
|
||||||
active + Random.ring2d(r, 2 * r) as Vector2
|
active + Vector2.uniformRing(r, 2 * r, random)
|
||||||
} else {
|
} else {
|
||||||
active + Polar(Random.double0(360.0), radius).cartesian
|
active + Polar(random.nextDouble(0.0, 360.0), radius).cartesian
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!boundsRect.contains(c)) continue@candidateSearch
|
if (!boundsRect.contains(c)) continue@candidateSearch
|
||||||
|
|||||||
31
orx-noise/src/commonMain/kotlin/ShapeNoise.kt
Normal file
31
orx-noise/src/commonMain/kotlin/ShapeNoise.kt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package org.openrndr.extra.noise
|
||||||
|
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.shape.*
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
fun ShapeProvider.uniform(random: Random = Random.Default): Vector2 {
|
||||||
|
val shape = shape
|
||||||
|
return Vector2.uniformSequence(shape.bounds, random).first {
|
||||||
|
shape.contains(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ShapeProvider.poissonDiskSampling(
|
||||||
|
r: Double,
|
||||||
|
tries: Int = 30,
|
||||||
|
random: Random = Random.Default
|
||||||
|
): List<Vector2> {
|
||||||
|
val shape = shape
|
||||||
|
val bounds = shape.bounds
|
||||||
|
val poissonBounds = Rectangle(0.0, 0.0, bounds.width, bounds.height)
|
||||||
|
|
||||||
|
val initialPoint = this.uniform(random).map(bounds, poissonBounds)
|
||||||
|
|
||||||
|
return poissonDiskSampling(bounds.width, bounds.height, r, tries, false, random, initialPoint) { _, _, point ->
|
||||||
|
val contourPoint = point.map(poissonBounds, bounds)
|
||||||
|
shape.contains(contourPoint)
|
||||||
|
}.map {
|
||||||
|
it.map(poissonBounds, bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -70,6 +70,14 @@ fun Vector2.Companion.uniforms(count: Int,
|
|||||||
random: Random = Random.Default): List<Vector2> =
|
random: Random = Random.Default): List<Vector2> =
|
||||||
List(count) { Vector2.uniform(rect, random) }
|
List(count) { Vector2.uniform(rect, random) }
|
||||||
|
|
||||||
|
fun Vector2.Companion.uniformSequence(rect: Rectangle,
|
||||||
|
random: Random = Random.Default): Sequence<Vector2> =
|
||||||
|
sequence {
|
||||||
|
while(true) {
|
||||||
|
yield(uniform(rect, random))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Vector2.Companion.uniformsRing(count: Int,
|
fun Vector2.Companion.uniformsRing(count: Int,
|
||||||
innerRadius: Double = 0.0, outerRadius: Double = 1.0,
|
innerRadius: Double = 0.0, outerRadius: Double = 1.0,
|
||||||
random: Random = Random.Default): List<Vector2> =
|
random: Random = Random.Default): List<Vector2> =
|
||||||
|
|||||||
29
orx-noise/src/demo/kotlin/DemoPoissonDiskSampling02.kt
Normal file
29
orx-noise/src/demo/kotlin/DemoPoissonDiskSampling02.kt
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import org.openrndr.application
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.extra.noise.poissonDiskSampling
|
||||||
|
import org.openrndr.math.Vector2
|
||||||
|
import org.openrndr.math.mod_
|
||||||
|
import org.openrndr.shape.Circle
|
||||||
|
import org.openrndr.shape.Ellipse
|
||||||
|
import kotlin.math.cos
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
application {
|
||||||
|
program {
|
||||||
|
extend {
|
||||||
|
val shape = Ellipse(Vector2(width/2.0, height/2.0), 200.0, 150.0 + cos(seconds)*125.0).shape
|
||||||
|
val points = shape.poissonDiskSampling(10.0)
|
||||||
|
drawer.clear(ColorRGBa.BLACK)
|
||||||
|
drawer.stroke = null
|
||||||
|
drawer.fill = ColorRGBa.PINK
|
||||||
|
drawer.circles(points, 4.0)
|
||||||
|
|
||||||
|
if (seconds.mod_(2.0) < 1.0) {
|
||||||
|
drawer.stroke = ColorRGBa.PINK
|
||||||
|
drawer.fill = null
|
||||||
|
drawer.shape(shape)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user