[orx-keyframer] Add value dicts for per channel easing and duration

This commit is contained in:
Edwin Jakobs
2020-09-21 09:41:01 +02:00
parent 54e9579a6c
commit 6ef5c03780
3 changed files with 118 additions and 62 deletions

View File

@@ -47,7 +47,10 @@ What this allows you to do:
"easing": "cubic-in-out", "easing": "cubic-in-out",
"x": 100.0, "x": 100.0,
"y": 320.0, "y": 320.0,
"radius": 40 "radius": {
"value": 50.0,
"easing": "linear"
}
} }
] ]
``` ```
@@ -135,20 +138,6 @@ Supported functions in expressions:
[Parameters and prototypes](src/demo/resources/demo-full-01.json) [Parameters and prototypes](src/demo/resources/demo-full-01.json)
<!-- __demos__ >
# Demos
[DemoFull01Kt](src/demo/kotlin/DemoFull01Kt.kt
![DemoFull01Kt](https://github.com/openrndr/orx/blob/media/orx-keyframer/images/DemoFull01Kt.png
[DemoScrub01Kt](src/demo/kotlin/DemoScrub01Kt.kt
![DemoScrub01Kt](https://github.com/openrndr/orx/blob/media/orx-keyframer/images/DemoScrub01Kt.png
[DemoSimple01Kt](src/demo/kotlin/DemoSimple01Kt.kt
![DemoSimple01Kt](https://github.com/openrndr/orx/blob/media/orx-keyframer/images/DemoSimple01Kt.png
[DemoSimple02Kt](src/demo/kotlin/DemoSimple02Kt.kt
![DemoSimple02Kt](https://github.com/openrndr/orx/blob/media/orx-keyframer/images/DemoSimple02Kt.png
[DemoSimpleExpressions01Kt](src/demo/kotlin/DemoSimpleExpressions01Kt.kt
![DemoSimpleExpressions01Kt](https://github.com/openrndr/orx/blob/media/orx-keyframer/images/DemoSimpleExpressions01Kt.png
[DemoSimpleRepetitions01Kt](src/demo/kotlin/DemoSimpleRepetitions01Kt.kt
![DemoSimpleRepetitions01Kt](https://github.com/openrndr/orx/blob/media/orx-keyframer/images/DemoSimpleRepetitions01Kt.png
<!-- __demos__ --> <!-- __demos__ -->
## Demos ## Demos
### DemoFull01 ### DemoFull01

View File

@@ -33,6 +33,10 @@ class KeyframerChannel {
return keys.lastOrNull()?.value return keys.lastOrNull()?.value
} }
fun lastTime(): Double? {
return keys.lastOrNull()?.time
}
fun duration(): Double { fun duration(): Double {
return keys.last().time return keys.last().time
} }

View File

@@ -5,6 +5,7 @@ import com.google.gson.JsonSyntaxException
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import org.openrndr.color.ColorRGBa import org.openrndr.color.ColorRGBa
import org.openrndr.extras.easing.Easing import org.openrndr.extras.easing.Easing
import org.openrndr.extras.easing.EasingFunction
import org.openrndr.math.Vector2 import org.openrndr.math.Vector2
import org.openrndr.math.Vector3 import org.openrndr.math.Vector3
import org.openrndr.math.Vector4 import org.openrndr.math.Vector4
@@ -12,6 +13,7 @@ import java.io.File
import java.lang.IllegalStateException import java.lang.IllegalStateException
import java.lang.NullPointerException import java.lang.NullPointerException
import java.net.URL import java.net.URL
import kotlin.math.max
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
@@ -257,6 +259,45 @@ open class Keyframer {
expressionContext["t"] = 0.0 expressionContext["t"] = 0.0
fun easingFunctionFromName(easingCandidate: String): EasingFunction {
return when (easingCandidate) {
"linear" -> Easing.Linear.function
"back-in" -> Easing.BackIn.function
"back-out" -> Easing.BackOut.function
"back-in-out" -> Easing.BackInOut.function
"bounce-in" -> Easing.BounceIn.function
"bounce-out" -> Easing.BounceOut.function
"bounce-in-out" -> Easing.BackInOut.function
"circ-in" -> Easing.CircIn.function
"circ-out" -> Easing.CircOut.function
"circ-in-out" -> Easing.CircInOut.function
"cubic-in" -> Easing.CubicIn.function
"cubic-out" -> Easing.CubicOut.function
"cubic-in-out" -> Easing.CubicInOut.function
"elastic-in" -> Easing.ElasticIn.function
"elastic-out" -> Easing.ElasticInOut.function
"elastic-in-out" -> Easing.ElasticOut.function
"expo-in" -> Easing.ExpoIn.function
"expo-out" -> Easing.ExpoOut.function
"expo-in-out" -> Easing.ExpoInOut.function
"quad-in" -> Easing.QuadIn.function
"quad-out" -> Easing.QuadOut.function
"quad-in-out" -> Easing.QuadInOut.function
"quart-in" -> Easing.QuartIn.function
"quart-out" -> Easing.QuartOut.function
"quart-in-out" -> Easing.QuartInOut.function
"quint-in" -> Easing.QuintIn.function
"quint-out" -> Easing.QuintOut.function
"quint-in-out" -> Easing.QuintInOut.function
"sine-in" -> Easing.SineIn.function
"sine-out" -> Easing.SineOut.function
"sine-in-out" -> Easing.SineInOut.function
"one" -> Easing.One.function
"zero" -> Easing.Zero.function
else -> error("unknown easing name '$easingCandidate'")
}
}
fun handleKey(key: Map<String, Any>, path: String) { fun handleKey(key: Map<String, Any>, path: String) {
val prototype = (key["prototypes"] as? String)?.let { val prototype = (key["prototypes"] as? String)?.let {
@@ -298,42 +339,7 @@ open class Keyframer {
val easing = try { val easing = try {
when (val easingCandidate = computed["easing"]) { when (val easingCandidate = computed["easing"]) {
null -> Easing.Linear.function null -> Easing.Linear.function
is String -> when (easingCandidate) { is String -> easingFunctionFromName(easingCandidate)
"linear" -> Easing.Linear.function
"back-in" -> Easing.BackIn.function
"back-out" -> Easing.BackOut.function
"back-in-out" -> Easing.BackInOut.function
"bounce-in" -> Easing.BounceIn.function
"bounce-out" -> Easing.BounceOut.function
"bounce-in-out" -> Easing.BackInOut.function
"circ-in" -> Easing.CircIn.function
"circ-out" -> Easing.CircOut.function
"circ-in-out" -> Easing.CircInOut.function
"cubic-in" -> Easing.CubicIn.function
"cubic-out" -> Easing.CubicOut.function
"cubic-in-out" -> Easing.CubicInOut.function
"elastic-in" -> Easing.ElasticIn.function
"elastic-out" -> Easing.ElasticInOut.function
"elastic-in-out" -> Easing.ElasticOut.function
"expo-in" -> Easing.ExpoIn.function
"expo-out" -> Easing.ExpoOut.function
"expo-in-out" -> Easing.ExpoInOut.function
"quad-in" -> Easing.QuadIn.function
"quad-out" -> Easing.QuadOut.function
"quad-in-out" -> Easing.QuadInOut.function
"quart-in" -> Easing.QuartIn.function
"quart-out" -> Easing.QuartOut.function
"quart-in-out" -> Easing.QuartInOut.function
"quint-in" -> Easing.QuintIn.function
"quint-out" -> Easing.QuintOut.function
"quint-in-out" -> Easing.QuintInOut.function
"sine-in" -> Easing.SineIn.function
"sine-out" -> Easing.SineOut.function
"sine-in-out" -> Easing.SineInOut.function
"one" -> Easing.One.function
"zero" -> Easing.Zero.function
else -> error("unknown easing name '$easingCandidate'")
}
else -> error("unknown easing for '$easingCandidate'") else -> error("unknown easing for '$easingCandidate'")
} }
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
@@ -349,19 +355,76 @@ open class Keyframer {
val channel = channels.getOrPut(channelCandidate.key) { val channel = channels.getOrPut(channelCandidate.key) {
KeyframerChannel() KeyframerChannel()
} }
expressionContext["v"] = channel.lastValue() ?: 0.0
val value = try { val lastValue = channel.lastValue() ?: 0.0
when (val candidate = channelCandidate.value) { expressionContext["v"] = lastValue
is Double -> candidate
is String -> evaluateExpression(candidate, expressionContext, functions) val lastTime = (channel.lastTime()) ?: 0.0
?: error("unknown value format for key '${channelCandidate.key}' : $candidate") expressionContext["d"] = time - lastTime
is Int -> candidate.toDouble()
else -> error("unknown value type for key '${channelCandidate.key}' : $candidate") if (channelCandidate.value is Map<*, *>) {
@Suppress("UNCHECKED_CAST")
val valueMap = channelCandidate.value as Map<String, Any>
val value = try {
when (val candidate = valueMap["value"]) {
null -> error("no value for '${channelCandidate.key}'")
is Double -> candidate
is String -> evaluateExpression(candidate, expressionContext, functions)
?: error("unknown value format for key '${channelCandidate.key}' : $candidate")
is Int -> candidate.toDouble()
else -> error("unknown value type for key '${channelCandidate.key}' : $candidate")
}
} catch (e: ExpressionException) {
throw ExpressionException("error in $path.'${channelCandidate.key}': ${e.message ?: ""}")
} }
} catch (e: ExpressionException) {
throw ExpressionException("error in $path.'${channelCandidate.key}': ${e.message ?: ""}") val dictEasing = when (val candidate = valueMap["easing"]) {
null -> easing
is String -> easingFunctionFromName(candidate)
else -> error("unknown easing for '$candidate'")
}
val dictDuration = try {
when (val candidate = valueMap["duration"]) {
null -> null
is Double -> candidate
is String -> evaluateExpression(candidate, expressionContext, functions)
?: error("unknown value format for key '${channelCandidate.key}' : $candidate")
is Int -> candidate.toDouble()
else -> error("unknown value type for key '${channelCandidate.key}' : $candidate")
}
} catch (e: ExpressionException) {
throw ExpressionException("error in $path.'${channelCandidate.key}': ${e.message ?: ""}")
}
if (dictDuration != null) {
if (dictDuration <= 0.0) {
println(lastTime)
println(time + dictDuration)
println(lastValue)
channel.add(max(lastTime, time + dictDuration), lastValue, Easing.Linear.function, hold)
channel.add(time, value, dictEasing, hold)
} else {
channel.add(time, lastValue, Easing.Linear.function, hold)
channel.add(time + dictDuration, value, dictEasing, hold)
}
}
} else {
val value = try {
when (val candidate = channelCandidate.value) {
is Double -> candidate
is String -> evaluateExpression(candidate, expressionContext, functions)
?: error("unknown value format for key '${channelCandidate.key}' : $candidate")
is Int -> candidate.toDouble()
else -> error("unknown value type for key '${channelCandidate.key}' : $candidate")
}
} catch (e: ExpressionException) {
throw ExpressionException("error in $path.'${channelCandidate.key}': ${e.message ?: ""}")
}
channel.add(time, value, easing, hold)
} }
channel.add(time, value, easing, hold)
} }
} }
lastTime = time + duration lastTime = time + duration