All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.teamwizardry.librarianlib.math.BasicAnimation.kt Maven / Gradle / Ivy

There is a newer version: 5.0.0-alpha.10
Show newest version
package com.teamwizardry.librarianlib.math

public abstract class BasicAnimation(
    /**
     * The duration of a single loop of the animation. The actual animation may last longer, depending on the value of
     * [repeatCount].
     */
    public open val duration: Float
): Animation {
    /**
     * The number of times this animation should loop. [Int.MAX_VALUE] is interpreted as infinity, and non-positive
     * values will be interpreted as 1.
     */
    public var repeatCount: Int = 1
    /**
     * If true, the animation will "bounce", reversing direction on every loop
     */
    public var reverseOnRepeat: Boolean = false
    /**
     * A callback to run when the animation completes or is interrupted
     */
    private var completionCallback: Runnable? = null

    /**
     * Get the value of the animation at the passed fractional time, which has been adjusted based on the duration and
     * repeat settings to be in the range `[0, 1]`.
     *
     * @param time The time to sample in the range `[0, 1]`
     * @param loopCount The number of times the animation wrapped around between the previous sample and this sample
     */
    protected abstract fun getValueAt(time: Float, loopCount: Int): T

    override val end: Float
        get() = if(repeatCount == Int.MAX_VALUE)
            Float.POSITIVE_INFINITY
        else
            duration * repeatCount.clamp(1, Int.MAX_VALUE)

    /**
     * Get the value at the specified [time]. Time should increase monotonically for each instance. Some animations may
     * behave erratically if [time] ever decreases.
     */
    override fun animate(time: Float): T {
        if(duration == 0f)
            return if(time == 0f) getValueAt(0f, 0) else getValueAt(1f, 0)

        var loopCount = (time / duration).toInt()
        var loopedTime = time % duration

        // If we don't do this check the animation will snap back to its initial value exactly at the end of its loop.
        if(time != 0f && loopedTime == 0f) {
            loopedTime = duration
            loopCount--
        }

        if(reverseOnRepeat && loopCount % 2 == 1) {
            loopedTime = duration - loopedTime
        }

        return getValueAt(loopedTime / duration, loopCount)
    }

    override fun onStarted() {
        // nop
    }

    override fun onStopped(time: Float): T {
        val finalValue = getValueAt(1f, 0)
        completionCallback?.run()
        return finalValue
    }

    //region Builders
    public fun repeat(repeatCount: Int): BasicAnimation = build {
        this.repeatCount = repeatCount
    }

    public fun repeat(): BasicAnimation = build {
        this.repeatCount = Int.MAX_VALUE
    }

    public fun reverseOnRepeat(): BasicAnimation = build {
        this.reverseOnRepeat = true
    }

    public fun reverseOnRepeat(reverseOnRepeat: Boolean): BasicAnimation = build {
        this.reverseOnRepeat = reverseOnRepeat
    }

    public fun onComplete(completionCallback: Runnable?): BasicAnimation = build {
        this.completionCallback = completionCallback
    }

    private fun build(cb: () -> Unit): BasicAnimation {
        cb()
        return this
    }
    //endregion
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy