import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.loadFont
import org.openrndr.extra.shapes.grid
import org.openrndr.extra.easing.Easing
import org.openrndr.math.Vector2
import org.openrndr.math.map

/**
 * # Visualizes Easing types as a graph and as motion.
 *
 * [grid] is used to layout graphs on rows and columns.
 *
 */
fun main() {
    application {
        configure {
            width = 1280
            height = 1080
        }
        program {
            val font = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 20.0)

            // grid `columns * rows` must be >= Easing.values().size
            val grid = drawer.bounds.grid(
                3, 11, 10.0, 10.0, 10.0, 10.0
            ).flatten()

            // make pairs of (easing function, grid rectangle)
            val pairs = Easing.values() zip grid

            extend {
                // ~4 seconds animation loop
                val animT = (frameCount % 240) / 60.0

                pairs.forEach { (easing, gridRect) ->

                    // background rectangle
                    drawer.stroke = null
                    drawer.fill = ColorRGBa.WHITE.opacify(0.3)
                    drawer.rectangle(gridRect)

                    // graph
                    drawer.stroke = ColorRGBa.PINK
                    val points = List(40) {
                        val curveT = it / 39.0
                        gridRect.position(
                            curveT, easing.function(curveT, 1.0, -1.0, 1.0)
                        )
                    }
                    drawer.lineStrip(points)

                    // label
                    drawer.fill = ColorRGBa.WHITE
                    drawer.stroke = null
                    drawer.fontMap = font
                    drawer.text(
                        easing.name,
                        // text position rounded for crisp font rendering
                        gridRect.position(0.02, 0.25).toInt().vector2
                    )

                    // animation
                    drawer.fill = ColorRGBa.WHITE.opacify(
                        when {                         // 4-stage opacity
                            animT > 3.0 -> 0.0         // invisible
                            animT > 2.0 -> 3.0 - animT // fade-out
                            animT < 1.0 -> animT       // fade-in
                            else -> 1.0                // visible
                        }
                    )
                    // move only while visible (when loop time in 1.0..2.0)
                    val t = animT.map(1.0, 2.0, 0.0, 1.0, true)
                    val xy = Vector2(1.0, easing.function(t, 1.0, -1.0, 1.0))
                    drawer.circle(gridRect.position(xy), 5.0)
                }
            }
        }
    }
}