import io.data2viz.color.*
import io.data2viz.geom.*
import io.data2viz.math.*
import io.data2viz.force.*
import io.data2viz.viz.*
import io.data2viz.random.*
import kotlin.math.sqrt
import kotlin.math.max
fun main() {
val vizSize = 600.0
val randPos = RandomDistribution.uniform(.0, vizSize)
val randRadius = RandomDistribution.uniform(6.0, 35.0)
val randAngle = RandomDistribution.uniform(.0, 360.0)
data class ColoredParticle(var position:Point, val radius:Double, val color:Color)
val items = (0..100).map {
ColoredParticle(
point(randPos(), randPos()),
randRadius(),
Colors.hsl(randAngle().deg, 100.pct, 50.pct)
)
}
var drag = false
lateinit var viz:Viz
var clickedNode:ForceNode<ColoredParticle>? = null
var clickedNodePos = point(0,0)
val viewCenter = point(vizSize / 2, vizSize / 2)
val simulation = forceSimulation<ColoredParticle> {
// initForceNode use the pre-populated domain object to set starting position
initForceNode = {
// initForceNode: ForceNode<D>.() -> Unit
position = domain.position
}
// radiusGet use the domain object to retrieve properties (ie. the radius of the particle)
forceCollision {
// radiusGet: ForceNode<D>.() -> Double
radiusGet = { domain.radius }
iterations = 6
}
// target position and force strength are the same for all nodes, use constants
forcePoint {
// pointGet: ForceNode<D>.() -> Point
pointGet = { viewCenter }
// strengthGet: ForceNode<D>.() -> Percent
strengthGet = { 1.pct }
}
domainObjects = items
intensityMin = 1.pct
intensityDecay = 0.pct
on(SimulationEvent.END, "End of simulation", { viz.stopAnimations() })
}
val particles = mutableListOf<CircleNode>()
viz = viz {
size = size(vizSize, vizSize)
text {
textContent = "Click and drag!"
x = 10.0
y = 20.0
}
simulation.nodes.forEach { forceNode ->
particles += circle {
// use the domain object contained in the node to set properties
radius = forceNode.domain.radius
fill = forceNode.domain.color
}
}
animation {
if (drag) {
clickedNode!!.position = clickedNodePos
}
simulation.nodes.forEach { forceNode ->
particles[forceNode.index].apply {
x = forceNode.x
y = forceNode.y
radius = forceNode.domain.radius
fill = forceNode.domain.color
}
}
}
on(KMouseDown) { event ->
if (!drag) {
// find the clicked node
clickedNode = simulation.nodes.firstOrNull { node ->
val diffX = event.pos.x - node.x
val diffY = event.pos.y - node.y
sqrt((diffX * diffX) + (diffY * diffY)) < items[node.index].radius
}
clickedNodePos = event.pos
drag = clickedNode != null
}
}
on(KMouseUp) { drag = false }
on(KMouseMove) { event ->
if (drag) {
clickedNodePos = event.pos
clickedNode!!.position = clickedNodePos
}
}
}
viz.bindRendererOnNewCanvas()
}
comments