import io.data2viz.color.* import io.data2viz.geom.* import io.data2viz.math.* import io.data2viz.viz.* import kotlin.math.sin import kotlin.math.cos import kotlin.math.abs val numBalls = 30 val ballSize = 8.0 val spacing = 60.0 val vizWidth = 600.0 val vizHeight = 600.0 val speed = 0.5 lateinit var balls: List<Triple<LineNode, CircleNode, CircleNode>> fun main() { val yOffset = - (numBalls * ballSize) viz { size = size(vizWidth, vizHeight) group { transform { translate(vizWidth / 2.0, vizHeight / 2.0) } balls = (0 .. numBalls).map { index -> val h = (index * ballSize * 2) + yOffset Triple( line { y1 = h y2 = h strokeColor = Colors.Web.grey }, circle { y = h }, circle { y = h } ) } var frame = .0 animation { frame += speed transform { translate(300.0, 300.0) rotate(frame / 50.0) } balls.forEachIndexed { index, pair -> val sinus = sin(PI * ((index+frame) / numBalls.toDouble())) val cosinus = cos(PI * ((index+frame) / numBalls.toDouble())) val w = spacing * sinus pair.first.x1 = w pair.first.x2 = -w // swap circles so the smallest one is always behind the big one if (cosinus <= .0) { pair.second.fill = Colors.hsl((120 + frame + 6*index).deg, 100.pct, (40 + (20 * cosinus)).pct) pair.second.x = w pair.second.radius = 3.0 + 5.0 * (cosinus + 1.2) pair.third.fill = Colors.hsl((frame + 6*index).deg, 100.pct, (40 + (20 * -cosinus)).pct) pair.third.x = -w pair.third.radius = 3.0 + 5.0 * (-cosinus + 1.2) } else { pair.third.fill = Colors.hsl((120 + frame + 6*index).deg, 100.pct, (40 + (20 * cosinus)).pct) pair.third.x = w pair.third.radius = 3.0 + 5.0 * (cosinus + 1.2) pair.second.fill = Colors.hsl((frame + 6*index).deg, 100.pct, (40 + (20 * -cosinus)).pct) pair.second.x = -w pair.second.radius = 3.0 + 5.0 * (-cosinus + 1.2) } } } } }.bindRendererOnNewCanvas() }