[orx-fcurve] Add polar and spherical compound curves, refactor, add tests
This commit is contained in:
113
orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt
Normal file
113
orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package org.openrndr.extra.fcurve
|
||||||
|
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.math.*
|
||||||
|
|
||||||
|
abstract class CompoundFCurve<T>(val compounds: List<FCurve?>) {
|
||||||
|
val duration: Double
|
||||||
|
get() {
|
||||||
|
return compounds.maxOf { it?.duration ?: 0.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun sampler(normalized: Boolean = false): (Double) -> T
|
||||||
|
}
|
||||||
|
|
||||||
|
class BooleanFCurve(value: FCurve?, val default: Boolean = true) :
|
||||||
|
CompoundFCurve<Boolean>(listOf(value)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Boolean {
|
||||||
|
val sampler = compounds[0]?.sampler(normalized) ?: { if (default) 1.0 else 0.0 }
|
||||||
|
return { t -> sampler(t) >= 1.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DoubleFCurve(value: FCurve?, val default: Double = 0.0) :
|
||||||
|
CompoundFCurve<Double>(listOf(value)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Double {
|
||||||
|
val sampler = compounds[0]?.sampler(normalized) ?: { default }
|
||||||
|
return { t -> sampler(t) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntFCurve(value: FCurve?, val default: Int = 0) :
|
||||||
|
CompoundFCurve<Int>(listOf(value)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Int {
|
||||||
|
val sampler = compounds[0]?.sampler(normalized) ?: { default.toDouble() }
|
||||||
|
return { t -> sampler(t).toInt() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vector2FCurve(x: FCurve?, y: FCurve?, val default: Vector2 = Vector2.ZERO) :
|
||||||
|
CompoundFCurve<Vector2>(listOf(x, y)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Vector2 {
|
||||||
|
val xSampler = compounds[0]?.sampler(normalized) ?: { default.x }
|
||||||
|
val ySampler = compounds[1]?.sampler(normalized) ?: { default.y }
|
||||||
|
return { t -> Vector2(xSampler(t), ySampler(t)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vector3FCurve(x: FCurve?, y: FCurve?, z: FCurve?, val default: Vector3 = Vector3.ZERO) :
|
||||||
|
CompoundFCurve<Vector3>(listOf(x, y, z)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Vector3 {
|
||||||
|
val xSampler = compounds[0]?.sampler(normalized) ?: { default.x }
|
||||||
|
val ySampler = compounds[1]?.sampler(normalized) ?: { default.y }
|
||||||
|
val zSampler = compounds[2]?.sampler(normalized) ?: { default.z }
|
||||||
|
return { t -> Vector3(xSampler(t), ySampler(t), zSampler(t)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Vector4FCurve(x: FCurve?, y: FCurve?, z: FCurve?, w: FCurve?, val default: Vector4 = Vector4.ZERO) :
|
||||||
|
CompoundFCurve<Vector4>(listOf(x, y, z, w)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Vector4 {
|
||||||
|
val xSampler = compounds[0]?.sampler(normalized) ?: { default.x }
|
||||||
|
val ySampler = compounds[1]?.sampler(normalized) ?: { default.y }
|
||||||
|
val zSampler = compounds[2]?.sampler(normalized) ?: { default.z }
|
||||||
|
val wSampler = compounds[3]?.sampler(normalized) ?: { default.w }
|
||||||
|
return { t -> Vector4(xSampler(t), ySampler(t), zSampler(t), wSampler(t)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RgbFCurve(r: FCurve?, g: FCurve?, b: FCurve?, val default: ColorRGBa = ColorRGBa.WHITE) :
|
||||||
|
CompoundFCurve<ColorRGBa>(listOf(r, g, b)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> ColorRGBa {
|
||||||
|
val rSampler = compounds[0]?.sampler(normalized) ?: { default.r }
|
||||||
|
val gSampler = compounds[1]?.sampler(normalized) ?: { default.g }
|
||||||
|
val bSampler = compounds[2]?.sampler(normalized) ?: { default.b }
|
||||||
|
return { t -> ColorRGBa(rSampler(t), gSampler(t), bSampler(t)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RgbaFCurve(r: FCurve?, g: FCurve?, b: FCurve?, a: FCurve?, val default: ColorRGBa = ColorRGBa.WHITE) :
|
||||||
|
CompoundFCurve<ColorRGBa>(listOf(r, g, b, a)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> ColorRGBa {
|
||||||
|
val rSampler = compounds[0]?.sampler(normalized) ?: { default.r }
|
||||||
|
val gSampler = compounds[1]?.sampler(normalized) ?: { default.g }
|
||||||
|
val bSampler = compounds[2]?.sampler(normalized) ?: { default.b }
|
||||||
|
val aSampler = compounds[3]?.sampler(normalized) ?: { default.alpha }
|
||||||
|
return { t -> ColorRGBa(rSampler(t), gSampler(t), bSampler(t), aSampler(t)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PolarFCurve(angleInDegrees: FCurve?, radius: FCurve?, val default: Polar = Polar(0.0, 1.0)) :
|
||||||
|
CompoundFCurve<Polar>(listOf(angleInDegrees, radius)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Polar {
|
||||||
|
val angleSampler = compounds[0]?.sampler(normalized) ?: { default.theta }
|
||||||
|
val radiusSampler = compounds[1]?.sampler(normalized) ?: { default.radius }
|
||||||
|
return { t -> Polar(angleSampler(t), radiusSampler(t)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SphericalFCurve(
|
||||||
|
thetaInDegrees: FCurve?,
|
||||||
|
phiInDegrees: FCurve?,
|
||||||
|
radius: FCurve?,
|
||||||
|
val default: Spherical = Spherical(0.0, 1.0, 1.0)
|
||||||
|
) :
|
||||||
|
CompoundFCurve<Spherical>(listOf(thetaInDegrees, phiInDegrees, radius)) {
|
||||||
|
override fun sampler(normalized: Boolean): (Double) -> Spherical {
|
||||||
|
val thetaSampler = compounds[0]?.sampler(normalized) ?: { default.theta }
|
||||||
|
val phiSampler = compounds[0]?.sampler(normalized) ?: { default.theta }
|
||||||
|
val radiusSampler = compounds[2]?.sampler(normalized) ?: { default.radius }
|
||||||
|
return { t -> Spherical(thetaSampler(t), phiSampler(t), radiusSampler(t)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,113 +1,20 @@
|
|||||||
package org.openrndr.extra.fcurve
|
package org.openrndr.extra.fcurve
|
||||||
import org.openrndr.color.ColorRGBa
|
|
||||||
import org.openrndr.math.Vector2
|
|
||||||
import org.openrndr.math.Vector3
|
|
||||||
|
|
||||||
abstract class CompoundFCurve<T>(val compounds: List<FCurve?>) {
|
|
||||||
val duration: Double
|
|
||||||
get() {
|
|
||||||
return compounds.maxOf { it?.duration ?: 0.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract fun sampler(normalized: Boolean = false): (Double) -> T
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class BooleanFCurve(value: FCurve?, val default: Boolean = true) :
|
|
||||||
CompoundFCurve<Boolean>(listOf(value)) {
|
|
||||||
override fun sampler(normalized: Boolean): (Double) -> Boolean {
|
|
||||||
val sampler = compounds[0]?.sampler(normalized) ?: { if (default) 1.0 else 0.0 }
|
|
||||||
return { t -> sampler(t) >= 1.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DoubleFCurve(value: FCurve?, val default: Double = 0.0) :
|
|
||||||
CompoundFCurve<Double>(listOf(value)) {
|
|
||||||
override fun sampler(normalized: Boolean): (Double) -> Double {
|
|
||||||
val sampler = compounds[0]?.sampler(normalized) ?: { default }
|
|
||||||
return { t -> sampler(t) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class IntFCurve(value: FCurve?, val default: Int = 0) :
|
|
||||||
CompoundFCurve<Int>(listOf(value)) {
|
|
||||||
override fun sampler(normalized: Boolean): (Double) -> Int {
|
|
||||||
val sampler = compounds[0]?.sampler(normalized) ?: { default.toDouble() }
|
|
||||||
return { t -> sampler(t).toInt() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Vector2FCurve(x: FCurve?, y: FCurve?, val default: Vector2 = Vector2.ZERO) :
|
|
||||||
CompoundFCurve<Vector2>(listOf(x, y)) {
|
|
||||||
override fun sampler(normalized: Boolean): (Double) -> Vector2 {
|
|
||||||
val xSampler = compounds[0]?.sampler(normalized) ?: { default.x }
|
|
||||||
val ySampler = compounds[1]?.sampler(normalized) ?: { default.y }
|
|
||||||
return { t -> Vector2(xSampler(t), ySampler(t)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Vector3FCurve(x: FCurve?, y: FCurve?, z: FCurve?, val default: Vector3 = Vector3.ZERO) :
|
|
||||||
CompoundFCurve<Vector3>(listOf(x, y, z)) {
|
|
||||||
override fun sampler(normalized: Boolean): (Double) -> Vector3 {
|
|
||||||
val xSampler = compounds[0]?.sampler(normalized) ?: { default.x }
|
|
||||||
val ySampler = compounds[1]?.sampler(normalized) ?: { default.y }
|
|
||||||
val zSampler = compounds[2]?.sampler(normalized) ?: { default.z }
|
|
||||||
return { t -> Vector3(xSampler(t), ySampler(t), zSampler(t)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RgbFCurve(r: FCurve?, g: FCurve?, b: FCurve?, val default: ColorRGBa = ColorRGBa.WHITE) :
|
|
||||||
CompoundFCurve<ColorRGBa>(listOf(r, g, b)) {
|
|
||||||
override fun sampler(normalized: Boolean): (Double) -> ColorRGBa {
|
|
||||||
val rSampler = compounds[0]?.sampler(normalized) ?: { default.r }
|
|
||||||
val gSampler = compounds[1]?.sampler(normalized) ?: { default.g }
|
|
||||||
val bSampler = compounds[2]?.sampler(normalized) ?: { default.b }
|
|
||||||
return { t -> ColorRGBa(rSampler(t), gSampler(t), bSampler(t)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RgbaFCurve(r: FCurve?, g: FCurve?, b: FCurve?, a: FCurve?, val default: ColorRGBa = ColorRGBa.WHITE) :
|
|
||||||
CompoundFCurve<ColorRGBa>(listOf(r, g, b, a)) {
|
|
||||||
override fun sampler(normalized: Boolean): (Double) -> ColorRGBa {
|
|
||||||
val rSampler = compounds[0]?.sampler(normalized) ?: { default.r }
|
|
||||||
val gSampler = compounds[1]?.sampler(normalized) ?: { default.g }
|
|
||||||
val bSampler = compounds[2]?.sampler(normalized) ?: { default.b }
|
|
||||||
val aSampler = compounds[3]?.sampler(normalized) ?: { default.alpha }
|
|
||||||
return { t -> ColorRGBa(rSampler(t), gSampler(t), bSampler(t), aSampler(t)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open class MultiFCurve(val compounds: Map<String, FCurve?>) {
|
open class MultiFCurve(val compounds: Map<String, FCurve?>) {
|
||||||
fun changeSpeed(speed: Double): MultiFCurve {
|
fun changeSpeed(speed: Double): MultiFCurve {
|
||||||
if (speed == 1.0) {
|
return if (speed == 1.0) {
|
||||||
return this
|
this
|
||||||
} else {
|
} else {
|
||||||
return MultiFCurve(compounds.mapValues { it.value?.changeSpeed(speed) })
|
MultiFCurve(compounds.mapValues { it.value?.changeSpeed(speed) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the [MultiFCurve]
|
||||||
|
*/
|
||||||
val duration by lazy { compounds.values.maxOfOrNull { it?.duration ?: 0.0 } ?: 0.0 }
|
val duration by lazy { compounds.values.maxOfOrNull { it?.duration ?: 0.0 } ?: 0.0 }
|
||||||
operator fun get(name: String): FCurve? {
|
operator fun get(name: String): FCurve? {
|
||||||
return compounds[name]
|
return compounds[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun boolean(value: String, default: Boolean = true) = BooleanFCurve(this[value], default)
|
|
||||||
fun double(value: String, default: Double = 0.0) = DoubleFCurve(this[value], default)
|
|
||||||
|
|
||||||
fun int(value: String, default: Int = 0) = IntFCurve(this[value], default)
|
|
||||||
|
|
||||||
fun vector2(x: String, y: String, default: Vector2 = Vector2.ZERO) = Vector2FCurve(this[x], this[y], default)
|
|
||||||
fun vector3(x: String, y: String, z: String, default: Vector3 = Vector3.ZERO) =
|
|
||||||
Vector3FCurve(this[x], this[y], this[z], default)
|
|
||||||
|
|
||||||
fun rgb(r: String, g: String, b: String, default: ColorRGBa = ColorRGBa.WHITE) =
|
|
||||||
RgbFCurve(this[r], this[g], this[b], default)
|
|
||||||
|
|
||||||
fun rgba(r: String, g: String, b: String, a: String, default: ColorRGBa = ColorRGBa.WHITE) =
|
|
||||||
RgbaFCurve(this[r], this[g], this[b], this[a], default)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
34
orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt
Normal file
34
orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package org.openrndr.extra.fcurve
|
||||||
|
|
||||||
|
import org.openrndr.color.ColorRGBa
|
||||||
|
import org.openrndr.math.*
|
||||||
|
|
||||||
|
fun MultiFCurve.boolean(value: String, default: Boolean = true) = BooleanFCurve(this[value], default)
|
||||||
|
fun MultiFCurve.double(value: String, default: Double = 0.0) = DoubleFCurve(this[value], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.int(value: String, default: Int = 0) = IntFCurve(this[value], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.vector2(x: String, y: String, default: Vector2 = Vector2.ZERO) =
|
||||||
|
Vector2FCurve(this[x], this[y], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.vector3(x: String, y: String, z: String, default: Vector3 = Vector3.ZERO) =
|
||||||
|
Vector3FCurve(this[x], this[y], this[z], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.vector4(x: String, y: String, z: String, w: String, default: Vector4 = Vector4.ZERO) =
|
||||||
|
Vector4FCurve(this[x], this[y], this[z], this[w], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.rgb(r: String, g: String, b: String, default: ColorRGBa = ColorRGBa.WHITE) =
|
||||||
|
RgbFCurve(this[r], this[g], this[b], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.rgba(r: String, g: String, b: String, a: String, default: ColorRGBa = ColorRGBa.WHITE) =
|
||||||
|
RgbaFCurve(this[r], this[g], this[b], this[a], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.polar(angleInDegrees: String, radius: String, default: Polar = Polar(0.0, 1.0)) =
|
||||||
|
PolarFCurve(this[angleInDegrees], this[radius], default)
|
||||||
|
|
||||||
|
fun MultiFCurve.spherical(
|
||||||
|
thetaInDegrees: String,
|
||||||
|
phiInDegrees: String,
|
||||||
|
radius: String,
|
||||||
|
default: Spherical = Spherical(0.0, 0.0, 1.0)
|
||||||
|
) = SphericalFCurve(this[thetaInDegrees], this[phiInDegrees], this[radius], default)
|
||||||
33
orx-fcurve/src/commonTest/kotlin/TestEFCurve.kt
Normal file
33
orx-fcurve/src/commonTest/kotlin/TestEFCurve.kt
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import org.openrndr.extra.fcurve.efcurve
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
class TestEFCurve {
|
||||||
|
@Test
|
||||||
|
fun comments() {
|
||||||
|
val text = """M1 |h5 m3|{
|
||||||
|
|10.3 # toch wel handig zo'n comment
|
||||||
|
|11.2
|
||||||
|
|14.5
|
||||||
|
|}
|
||||||
|
""".trimMargin()
|
||||||
|
assertEquals("M1 h5 m3 h5 m3 h5 m3", efcurve(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun expressions() {
|
||||||
|
assertEquals("M9.0", efcurve("M_4 + 5_"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun listExpansion() {
|
||||||
|
assertEquals("M0 L1.0, 3.0 L1.0, 6.0", efcurve("M0 |L1.0, _it_|{3, 6}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun repetition() {
|
||||||
|
assertEquals("M0 L1.0, 3.0 L1.0, 3.0", efcurve("M0 |L1.0, 3.0|[2]"))
|
||||||
|
assertEquals("M0 L1.0, 0.0 L1.0, 1.0", efcurve("M0 |L1.0, _it_|[2]"))
|
||||||
|
assertEquals("M0 L1.0, 0.0 L1.0, 1.0 L1.0, 0.0 L1.0, 1.0 L1.0, 0.0 L1.0, 1.0", efcurve("M0 ||L1.0, _it_|[2]|[3]"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import org.openrndr.application
|
import org.openrndr.application
|
||||||
import org.openrndr.extra.fcurve.MultiFCurve
|
import org.openrndr.extra.fcurve.MultiFCurve
|
||||||
import org.openrndr.extra.fcurve.fcurve
|
import org.openrndr.extra.fcurve.fcurve
|
||||||
|
import org.openrndr.extra.fcurve.vector2
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
application {
|
application {
|
||||||
|
|||||||
Reference in New Issue
Block a user