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

commonMain.ru.casperix.spine.animation.Curve.kt Maven / Gradle / Ivy

There is a newer version: 0.9.0
Show newest version
package ru.casperix.spine.animation

import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import ru.casperix.math.BezierCalculation
import ru.casperix.math.curve.float32.BezierCubic2f
import ru.casperix.math.vector.float32.Vector2f
import ru.casperix.misc.ceilToInt
import ru.casperix.spine.json.CurveSerializer
import kotlin.math.roundToInt

@Serializable(with = CurveSerializer::class)
interface Curve

@Serializable
object LinearCurve : Curve

@Serializable
object SteppedCurve : Curve

@Serializable
object UnknownCurve : Curve

@Serializable
class BezierCurve(val v1: Vector2f, val v2: Vector2f) : Curve {
    companion object {
        private val INITIAL_ERROR = 0.001f
        private val STEPS = 128
    }

    @Transient
    private var cache: BezierCurveCache? = null

    fun getValue(lastFrame: ChannelFrame, nextFrame: ChannelFrame, currentTime: Float): Float {
        getOrBuildCache(lastFrame, nextFrame).run {
            val startTime = lastFrame.time
            val finishTime = nextFrame.time

            val pointIndex = ((STEPS - 1).toFloat() * (currentTime - startTime) / (finishTime - startTime)).ceilToInt().coerceIn(0..points.lastIndex)
            val next = points[pointIndex]
            return if (pointIndex == 0) {
                next.y
            } else {
                val last = points[pointIndex - 1]
                (currentTime - last.x) / (next.x - last.x) * (next.y - last.y) + last.y
            }
        }
    }

    private fun getOrBuildCache(lastFrame: ChannelFrame, nextFrame: ChannelFrame): BezierCurveCache {
        if (cache == null) {
            val curve = BezierCubic2f(
                Vector2f(lastFrame.time, lastFrame.value),
                v1,
                v2,
                Vector2f(nextFrame.time, nextFrame.value),
            )
            val startTime = lastFrame.time
            val finishTime = nextFrame.time
            val points = (0..STEPS).map { step ->
                val t = step / STEPS.toFloat()
                val time = startTime + t * (finishTime - startTime)
                val value = BezierCalculation.getApproximateErrorHeightPrecision(curve, time, INITIAL_ERROR)
                Vector2f(time, value)
            }
            cache = BezierCurveCache(
                curve, points
            )
        }
        return cache!!
    }

    class BezierCurveCache(val curve: BezierCubic2f, val points: List)
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy