Hi
After learning more about Kotlin Iām concerned that the original example is a bit verbose so I decided to share an updated version with some changes:
- No need to name companion object
- Use
PVector,PVector.distinstead ofx,y - Use
Intfor colors, no need offloatArrayOf - Use destructuring declaration with
circleSizeCountsmap - Replace
circleOverlaps()by.all {} - Improve
weightedChoice:- No need for weights to sum 1.0
- Accept any collection as input, not only colors
- Use
circle()instead ofellipse()
Updated Kotlin + Processing code
import processing.core.PApplet
import processing.core.PConstants
import processing.core.PVector
data class Circle(val radius: Float, val pos: PVector, var color: Int = 0)
fun main() {
CirclePacking.start()
}
class CirclePacking : PApplet() {
companion object {
fun start() {
var art = CirclePacking()
art.setSize(500, 500)
art.runSketch()
}
}
private val circleSizeCounts = listOf(
65f to 19,
37f to 38,
20f to 75,
7f to 150,
3f to 300
)
override fun setup() {
colorMode(PConstants.HSB, 360f, 100f, 100f)
noStroke()
background(70)
val circles = mutableListOf<Circle>()
for ((circleRadius, circleCount) in circleSizeCounts) {
for (i in 1..circleCount) {
// allow up to 1000 collisions
for (c in 0..1000) {
// generate random point
// A. do not allow circles to overlap canvas
// val pos = PVector(random(circleRadius, width -
// circleRadius), random(circleRadius, height - circleRadius))
// B. allow circles overlapping canvas
val pos = PVector(random(width * 1f), random(height * 1f))
val newCircle = Circle(circleRadius, pos)
if (circles.all { otherCircle ->
newCircle.pos.dist(otherCircle.pos) >
newCircle.radius + otherCircle.radius
}) {
// get random color
newCircle.color = weightedChoice(
listOf(
color(0f, 0f, random(90f, 100f)),
color(random(180f, 220f), 50f, 50f),
color(random(0f, 20f), 80f, 80f)
), listOf(0.6f, 0.3f, 0.1f)
)
circles.add(newCircle)
break
}
}
}
}
circles.forEach {
fill(it.color)
circle(it.pos.x, it.pos.y, it.radius * 2)
}
}
override fun draw() {
}
private fun <T> weightedChoice(coll: Collection<T>, weights: Collection<Float>): T {
if (coll.size != weights.size) {
error("weightedChoice() requires two collections with the same number of elements")
}
val rnd = random(weights.sum())
var sum = 0.0
val index = weights.indexOfFirst { sum += it; sum > rnd }
return coll.toList()[index]
}
}