diff --git a/orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt b/orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt index e7be4821..ddb154f0 100644 --- a/orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt +++ b/orx-fcurve/src/commonMain/kotlin/CompoundFCurve.kt @@ -3,41 +3,75 @@ package org.openrndr.extra.fcurve import org.openrndr.color.ColorRGBa import org.openrndr.math.* -abstract class CompoundFCurve(val compounds: List) { +abstract class CompoundFCurve(val compounds: List, val compoundNames: List) { val duration: Double get() { return compounds.maxOf { it?.duration ?: 0.0 } } + abstract fun value(t: Double, overrides: Map? = null): T abstract fun sampler(normalized: Boolean = false): (Double) -> T } -class BooleanFCurve(value: FCurve?, val default: Boolean = true) : - CompoundFCurve(listOf(value)) { +class BooleanFCurve(value: Pair, val default: Boolean = true) : + CompoundFCurve(listOf(value.second), listOf(value.first)) { + override fun value(t: Double, overrides: Map?): Boolean { + val d = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) + return if (d != null) { + d >= 1.0 + } else { + default + } + } + 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(listOf(value)) { +class DoubleFCurve(value: Pair, val default: Double = 0.0) : + CompoundFCurve(listOf(value.second), listOf(value.first)) { + + override fun value(t: Double, overrides: Map?): Double { + return overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default + } + 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(listOf(value)) { +class IntFCurve(value: Pair, val default: Int = 0) : + CompoundFCurve(listOf(value.second), listOf(value.first)) { + + override fun value(t: Double, overrides: Map?): Int { + val d = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) + return if (d != null) { + d.toInt() + } else { + default + } + } + 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(listOf(x, y)) { +class Vector2FCurve( + x: Pair, y: Pair, + val default: Vector2 = Vector2.ZERO +) : + CompoundFCurve(listOf(x.second, y.second), listOf(x.first, y.first)) { + override fun value(t: Double, overrides: Map?): Vector2 { + val x = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.x + val y = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.y + return Vector2(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 } @@ -45,8 +79,20 @@ class Vector2FCurve(x: FCurve?, y: FCurve?, val default: Vector2 = Vector2.ZERO) } } -class Vector3FCurve(x: FCurve?, y: FCurve?, z: FCurve?, val default: Vector3 = Vector3.ZERO) : - CompoundFCurve(listOf(x, y, z)) { +class Vector3FCurve( + x: Pair, + y: Pair, + z: Pair, + val default: Vector3 = Vector3.ZERO +) : + CompoundFCurve(listOf(x.second, y.second, z.second), listOf(x.first, y.first, z.first)) { + override fun value(t: Double, overrides: Map?): Vector3 { + val x = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.x + val y = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.y + val z = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.z + return Vector3(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 } @@ -55,8 +101,26 @@ class Vector3FCurve(x: FCurve?, y: FCurve?, z: FCurve?, val default: Vector3 = V } } -class Vector4FCurve(x: FCurve?, y: FCurve?, z: FCurve?, w: FCurve?, val default: Vector4 = Vector4.ZERO) : - CompoundFCurve(listOf(x, y, z, w)) { +class Vector4FCurve( + x: Pair, + y: Pair, + z: Pair, + w: Pair, + val default: Vector4 = Vector4.ZERO +) : + CompoundFCurve( + listOf(x.second, y.second, z.second, w.second), + listOf(x.first, y.first, z.first, w.first) + ) { + + override fun value(t: Double, overrides: Map?): Vector4 { + val x = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.x + val y = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.y + val z = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.z + val w = overrides?.get(compoundNames[3]) ?: compounds[3]?.value(t) ?: default.w + return Vector4(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 } @@ -66,8 +130,21 @@ class Vector4FCurve(x: FCurve?, y: FCurve?, z: FCurve?, w: FCurve?, val default: } } -class RgbFCurve(r: FCurve?, g: FCurve?, b: FCurve?, val default: ColorRGBa = ColorRGBa.WHITE) : - CompoundFCurve(listOf(r, g, b)) { +class RgbFCurve( + r: Pair, + g: Pair, + b: Pair, + val default: ColorRGBa = ColorRGBa.WHITE +) : + CompoundFCurve(listOf(r.second, g.second, b.second), listOf(r.first, g.first, b.first)) { + + override fun value(t: Double, overrides: Map?): ColorRGBa { + val r = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.r + val g = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.g + val b = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.g + return ColorRGBa(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 } @@ -76,8 +153,26 @@ class RgbFCurve(r: FCurve?, g: FCurve?, b: FCurve?, val default: ColorRGBa = Col } } -class RgbaFCurve(r: FCurve?, g: FCurve?, b: FCurve?, a: FCurve?, val default: ColorRGBa = ColorRGBa.WHITE) : - CompoundFCurve(listOf(r, g, b, a)) { +class RgbaFCurve( + r: Pair, + g: Pair, + b: Pair, + a: Pair, + val default: ColorRGBa = ColorRGBa.WHITE +) : + CompoundFCurve( + listOf(r.second, g.second, b.second, a.second), + listOf(r.first, g.first, b.first, a.first) + ) { + + override fun value(t: Double, overrides: Map?): ColorRGBa { + val r = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.r + val g = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.g + val b = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.g + val a = overrides?.get(compoundNames[3]) ?: compounds[3]?.value(t) ?: default.alpha + return ColorRGBa(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 } @@ -87,8 +182,19 @@ class RgbaFCurve(r: FCurve?, g: FCurve?, b: FCurve?, a: FCurve?, val default: Co } } -class PolarFCurve(angleInDegrees: FCurve?, radius: FCurve?, val default: Polar = Polar(0.0, 1.0)) : - CompoundFCurve(listOf(angleInDegrees, radius)) { +class PolarFCurve( + angleInDegrees: Pair, + radius: Pair, + val default: Polar = Polar(0.0, 1.0) +) : + CompoundFCurve(listOf(angleInDegrees.second, radius.second), listOf(angleInDegrees.first, radius.first)) { + + override fun value(t: Double, overrides: Map?): Polar { + val theta = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.theta + val radius = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.radius + return Polar(theta, radius) + } + override fun sampler(normalized: Boolean): (Double) -> Polar { val angleSampler = compounds[0]?.sampler(normalized) ?: { default.theta } val radiusSampler = compounds[1]?.sampler(normalized) ?: { default.radius } @@ -97,12 +203,22 @@ class PolarFCurve(angleInDegrees: FCurve?, radius: FCurve?, val default: Polar = } class SphericalFCurve( - thetaInDegrees: FCurve?, - phiInDegrees: FCurve?, - radius: FCurve?, + thetaInDegrees: Pair, + phiInDegrees: Pair, + radius: Pair, val default: Spherical = Spherical(0.0, 1.0, 1.0) ) : - CompoundFCurve(listOf(thetaInDegrees, phiInDegrees, radius)) { + CompoundFCurve( + listOf(thetaInDegrees.second, phiInDegrees.second, radius.second), + listOf(thetaInDegrees.first, phiInDegrees.first, radius.first) + ) { + override fun value(t: Double, overrides: Map?): Spherical { + val theta = overrides?.get(compoundNames[0]) ?: compounds[0]?.value(t) ?: default.theta + val phi = overrides?.get(compoundNames[1]) ?: compounds[1]?.value(t) ?: default.phi + val radius = overrides?.get(compoundNames[2]) ?: compounds[2]?.value(t) ?: default.radius + return Spherical(theta, phi, radius) + } + override fun sampler(normalized: Boolean): (Double) -> Spherical { val thetaSampler = compounds[0]?.sampler(normalized) ?: { default.theta } val phiSampler = compounds[0]?.sampler(normalized) ?: { default.theta } diff --git a/orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt b/orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt index 545684c8..96bfc4e5 100644 --- a/orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt +++ b/orx-fcurve/src/commonMain/kotlin/MultiFCurveExtensions.kt @@ -3,32 +3,37 @@ 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.boolean(value: String, default: Boolean = true) = BooleanFCurve(value to this[value], default) +fun MultiFCurve.double(value: String, default: Double = 0.0) = DoubleFCurve(value to this[value], default) -fun MultiFCurve.int(value: String, default: Int = 0) = IntFCurve(this[value], default) +fun MultiFCurve.int(value: String, default: Int = 0) = IntFCurve(value to this[value], default) fun MultiFCurve.vector2(x: String, y: String, default: Vector2 = Vector2.ZERO) = - Vector2FCurve(this[x], this[y], default) + Vector2FCurve(x to this[x], y to this[y], default) fun MultiFCurve.vector3(x: String, y: String, z: String, default: Vector3 = Vector3.ZERO) = - Vector3FCurve(this[x], this[y], this[z], default) + Vector3FCurve(x to this[x], y to this[y], z to 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) + Vector4FCurve(x to this[x], y to this[y], z to this[z], w to this[w], default) fun MultiFCurve.rgb(r: String, g: String, b: String, default: ColorRGBa = ColorRGBa.WHITE) = - RgbFCurve(this[r], this[g], this[b], default) + RgbFCurve(r to this[r], g to this[g], b to 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) + RgbaFCurve(r to this[r], g to this[g], b to this[b], a to this[a], default) fun MultiFCurve.polar(angleInDegrees: String, radius: String, default: Polar = Polar(0.0, 1.0)) = - PolarFCurve(this[angleInDegrees], this[radius], default) + PolarFCurve(angleInDegrees to this[angleInDegrees], radius to 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) \ No newline at end of file +) = SphericalFCurve( + thetaInDegrees to this[thetaInDegrees], + phiInDegrees to this[phiInDegrees], + radius to this[radius], + default +) \ No newline at end of file