This commit is contained in:
Moritz Ruth 2023-05-26 01:28:37 +02:00
parent 74f113e670
commit 926a52e34e
Signed by: moritzruth
GPG key ID: C9BBAB79405EE56D
14 changed files with 105 additions and 99 deletions

View file

@ -9,7 +9,6 @@ import de.moritzruth.theaterdsl.dmx.EnttecOpenDmxUsb
import de.moritzruth.theaterdsl.show.createShow import de.moritzruth.theaterdsl.show.createShow
import de.moritzruth.theaterdsl.show.runShow import de.moritzruth.theaterdsl.show.runShow
@Suppress("DuplicatedCode")
val show = createShow { val show = createShow {
firstAct() firstAct()
secondAct() secondAct()

View file

@ -4,6 +4,7 @@ import de.moritzruth.lampenfieber.device.devices
import de.moritzruth.theaterdsl.dmx.EnttecOpenDmxUsb import de.moritzruth.theaterdsl.dmx.EnttecOpenDmxUsb
import de.moritzruth.theaterdsl.dmx.PerDeviceDmxDataWriter import de.moritzruth.theaterdsl.dmx.PerDeviceDmxDataWriter
import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
suspend fun main() { suspend fun main() {
EnttecOpenDmxUsb.start() EnttecOpenDmxUsb.start()
@ -23,6 +24,6 @@ suspend fun main() {
awaitCancellation() awaitCancellation()
} }
fun test() { suspend fun test() = coroutineScope {
} }

View file

@ -353,6 +353,7 @@ fun ShowBuilderContext.fourthAct() = act("Vierter Akt") {
step(StepCue.MusicEnd) { step(StepCue.MusicEnd) {
actors { actors {
-"Paula" -"Paula"
-"David"
-"Theaterlehrer" -"Theaterlehrer"
-"Sven" -"Sven"
-"Heike" -"Heike"
@ -403,10 +404,6 @@ fun ShowBuilderContext.fourthAct() = act("Vierter Akt") {
scene("10") { scene("10") {
step(StepCue.Text("Kai", "Hilfe, los, runter!")) { step(StepCue.Text("Kai", "Hilfe, los, runter!")) {
actors {
+"Paula / steht bei Markierung"
}
curtainState = CurtainState.OPEN curtainState = CurtainState.OPEN
onRun { onRun {
@ -415,6 +412,10 @@ fun ShowBuilderContext.fourthAct() = act("Vierter Akt") {
} }
step(StepCue.Curtain(CurtainState.OPEN, false)) { step(StepCue.Curtain(CurtainState.OPEN, false)) {
actors {
+"Paula / steht bei Markierung"
}
onRun { onRun {
Tops.both.forEach { it.brightness.fade(50.percent, 1.5.seconds) } Tops.both.forEach { it.brightness.fade(50.percent, 1.5.seconds) }
bar.color.static(Color.WHITE) bar.color.static(Color.WHITE)

View file

@ -19,33 +19,32 @@ interface DynamicValue<T> {
} }
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
abstract class FloatDV<T>(private val initialStaticValue: Float) : DynamicValue<T> { abstract class DoubleDV<T>(private val initialStaticValue: Double) : DynamicValue<T> {
private sealed interface State { private sealed interface State {
data class Static(val value: Float) : State data class Static(val value: Double) : State
data class Fade(val start: Float, val end: Float, val duration: Duration) : State { data class Fade(val start: Double, val end: Double, val duration: Duration) : State {
val delta = end - start val delta = end - start
} }
data class Sine( data class Sine(
val offset: Double, val minimum: Double,
val minimum: Float, val maximum: Double,
val maximum: Float, val b: Double,
val period: Duration val c: Double
) : State { ) : State {
companion object { companion object {
fun calculateA(minimum: Float, maximum: Float) = (maximum - minimum) * 0.5 fun calculateB(period: Duration) = period.inWholeMilliseconds / (2 * PI)
fun calculateX(progress: Double, offset: Double) = 2 * PI * progress - offset fun calculateX(elapsedTime: Duration, b: Double, c: Double) = b * elapsedTime.inWholeMilliseconds + c
fun calculateD(minimum: Float) = 0.5 + minimum
} }
} }
data class Step(val steps: ImmutableList<Float>, val interval: Duration, val startIndex: Int) : State data class Step(val steps: ImmutableList<Double>, val interval: Duration, val startIndex: Int) : State
data class PulseOnce(val rampUpDuration: Duration, val rampDownDuration: Duration, val peakValue: Float, val start: Float, val end: Float) : State data class PulseOnce(val rampUpDuration: Duration, val rampDownDuration: Duration, val peakValue: Double, val start: Double, val end: Double) : State
} }
protected abstract fun toDomain(value: Float): T protected abstract fun toDomain(value: Double): T
protected abstract fun fromDomain(value: T): Float protected abstract fun fromDomain(value: T): Double
protected abstract val minimumValue: T protected abstract val minimumValue: T
protected abstract val maximumValue: T protected abstract val maximumValue: T
@ -63,15 +62,12 @@ abstract class FloatDV<T>(private val initialStaticValue: Float) : DynamicValue<
override fun getCurrentValue(): T { override fun getCurrentValue(): T {
val elapsedTime = stateChangeMark.elapsedNow() val elapsedTime = stateChangeMark.elapsedNow()
val float = when (val s = state) { val double = when (val s = state) {
is State.Static -> s.value is State.Static -> s.value
is State.Fade -> (min(elapsedTime / s.duration, 1.0) * s.delta + s.start).toFloat() is State.Fade -> (min(elapsedTime / s.duration, 1.0) * s.delta + s.start)
is State.Sine -> { is State.Sine -> {
val a = State.Sine.calculateA(s.minimum, s.maximum) val fromZeroToOne = (sin(State.Sine.calculateX(elapsedTime, s.b, s.c)) + 1) / 2
val x = State.Sine.calculateX(elapsedTime / s.period, s.offset) (fromZeroToOne * (s.maximum - s.minimum) + s.minimum)
val d = State.Sine.calculateD(s.minimum)
(a * sin(x) + d).toFloat()
} }
is State.Step -> { is State.Step -> {
@ -83,16 +79,16 @@ abstract class FloatDV<T>(private val initialStaticValue: Float) : DynamicValue<
if (elapsedTime <= s.rampUpDuration) { if (elapsedTime <= s.rampUpDuration) {
val progress = elapsedTime / s.rampUpDuration val progress = elapsedTime / s.rampUpDuration
val delta = s.peakValue - s.start val delta = s.peakValue - s.start
(progress * delta + s.start).toFloat() (progress * delta + s.start)
} else { } else {
val progress = min(elapsedTime / s.rampDownDuration, 1.0) val progress = min(elapsedTime / s.rampDownDuration, 1.0)
val delta = s.peakValue - s.end val delta = s.peakValue - s.end
(s.peakValue - progress * delta).toFloat() (s.peakValue - progress * delta)
} }
} }
} }
return toDomain(float) return toDomain(double)
} }
fun static(value: T) { fun static(value: T) {
@ -108,9 +104,14 @@ abstract class FloatDV<T>(private val initialStaticValue: Float) : DynamicValue<
} }
fun sine(period: Duration, minimum: T = minimumValue, maximum: T = maximumValue, start: T = getCurrentValue()) { fun sine(period: Duration, minimum: T = minimumValue, maximum: T = maximumValue, start: T = getCurrentValue()) {
val coercedStart = fromDomain(start).coerceIn(fromDomain(minimum), fromDomain(maximum)) val b = State.Sine.calculateB(period)
val offset = asin((coercedStart - State.Sine.calculateD(fromDomain(minimum))) / State.Sine.calculateA(fromDomain(minimum), fromDomain(maximum))) val doubleMinimum = fromDomain(minimum)
state = State.Sine(offset, fromDomain(minimum), fromDomain(maximum), period) val doubleMaximum = fromDomain(maximum)
val delta = doubleMaximum - doubleMinimum
val coercedStart = fromDomain(start).coerceIn(doubleMinimum, fromDomain(maximum))
val c = asin(2 * ((coercedStart - doubleMinimum) / delta) - 1)
state = State.Sine(fromDomain(minimum), fromDomain(maximum), b, c)
} }
fun steps(steps: List<T>, interval: Duration, startIndex: Int = 0) { fun steps(steps: List<T>, interval: Duration, startIndex: Int = 0) {
@ -132,16 +133,16 @@ abstract class FloatDV<T>(private val initialStaticValue: Float) : DynamicValue<
} }
} }
class PercentageDV(initialStaticValue: Percentage = 0.percent) : FloatDV<Percentage>(initialStaticValue.value) { class PercentageDV(initialStaticValue: Percentage = 0.percent) : DoubleDV<Percentage>(initialStaticValue.value) {
override fun fromDomain(value: Percentage): Float = value.value override fun fromDomain(value: Percentage): Double = value.value
override fun toDomain(value: Float): Percentage = Percentage(value) override fun toDomain(value: Double): Percentage = Percentage(value)
override val minimumValue: Percentage = 0.percent override val minimumValue: Percentage = 0.percent
override val maximumValue: Percentage = 100.percent override val maximumValue: Percentage = 100.percent
} }
class AngleDV(initialStaticValue: Angle = 0.degrees) : FloatDV<Angle>(initialStaticValue.degrees) { class AngleDV(initialStaticValue: Angle = 0.degrees) : DoubleDV<Angle>(initialStaticValue.degrees) {
override fun fromDomain(value: Angle): Float = value.degrees override fun fromDomain(value: Angle): Double = value.degrees
override fun toDomain(value: Float): Angle = Angle(value) override fun toDomain(value: Double): Angle = Angle(value)
override val minimumValue: Angle = 0.degrees override val minimumValue: Angle = 0.degrees
override val maximumValue: Angle = 360.degrees override val maximumValue: Angle = 360.degrees
} }
@ -199,9 +200,9 @@ class ColorDV(private val initialStaticValue: Color = Color.WHITE) : DynamicValu
val progress = min(elapsedTime / s.duration, 1.0) val progress = min(elapsedTime / s.duration, 1.0)
Color( Color(
hue = Angle((s.start.hue.degrees + s.deltaHue * progress).toFloat()), hue = Angle((s.start.hue.degrees + s.deltaHue * progress)),
saturation = Percentage((s.start.saturation.value + s.deltaSaturation * progress).toFloat()), saturation = Percentage((s.start.saturation.value + s.deltaSaturation * progress)),
brightness = Percentage((s.start.brightness.value + s.deltaBrightness * progress).toFloat()) brightness = Percentage((s.start.brightness.value + s.deltaBrightness * progress))
) )
} }
@ -209,11 +210,11 @@ class ColorDV(private val initialStaticValue: Color = Color.WHITE) : DynamicValu
val startRandom = Random((elapsedTime / s.interval).toInt() - 1) val startRandom = Random((elapsedTime / s.interval).toInt() - 1)
val endRandom = Random((elapsedTime / s.interval).toInt()) val endRandom = Random((elapsedTime / s.interval).toInt())
val progress = (elapsedTime / s.interval).mod(1.0) val progress = (elapsedTime / s.interval).mod(1.0)
val startHue = s.hue.degrees - s.deviation.degrees + startRandom.nextFloat() * s.deviation.degrees * 2 val startHue = s.hue.degrees - s.deviation.degrees + startRandom.nextDouble() * s.deviation.degrees * 2
val endHue = s.hue.degrees - s.deviation.degrees + endRandom.nextFloat() * s.deviation.degrees * 2 val endHue = s.hue.degrees - s.deviation.degrees + endRandom.nextDouble() * s.deviation.degrees * 2
val delta = endHue - startHue val delta = endHue - startHue
Color(hue = Angle((progress * delta).toFloat())) Color(hue = Angle((progress * delta)))
} }
} }
} }

View file

@ -10,7 +10,7 @@ interface DmxDataWriter {
/** /**
* @param startAtOne Whether the written value is in `1..255` or in `0..255`. * @param startAtOne Whether the written value is in `1..255` or in `0..255`.
*/ */
fun writeInRange(range: ClosedFloatingPointRange<Float>, value: Float, startAtOne: Boolean = false) = fun writeInRange(range: ClosedFloatingPointRange<Double>, value: Double, startAtOne: Boolean = false) =
writeRaw(Percentage((value - range.start) / (range.endInclusive - range.start)).roundToDmxValue(startAtOne)) writeRaw(Percentage((value - range.start) / (range.endInclusive - range.start)).roundToDmxValue(startAtOne))
fun writeHighByte(value: UShort) = writeRaw(DmxValue(value.toUInt().shr(8).toUByte())) fun writeHighByte(value: UShort) = writeRaw(DmxValue(value.toUInt().shr(8).toUByte()))

View file

@ -16,5 +16,5 @@ value class DmxValue(val value: UByte) : Comparable<UByte> {
* @param startAtOne Whether the output range is `1..255` or `0..255`. * @param startAtOne Whether the output range is `1..255` or `0..255`.
*/ */
fun Percentage.roundToDmxValue(startAtOne: Boolean = false): DmxValue = fun Percentage.roundToDmxValue(startAtOne: Boolean = false): DmxValue =
if (startAtOne) DmxValue(((value * (DmxValue.VALUE_RANGE.last.toFloat() - 1f)).roundToInt() + 1).toUByte()) if (startAtOne) DmxValue(((value * (DmxValue.VALUE_RANGE.last.toDouble() - 1f)).roundToInt() + 1).toUByte())
else DmxValue((value * DmxValue.VALUE_RANGE.last.toFloat()).roundToInt().toUByte()) else DmxValue((value * DmxValue.VALUE_RANGE.last.toDouble()).roundToInt().toUByte())

View file

@ -88,7 +88,7 @@ private fun buildAct(actIndex: Int, actName: String, build: ActBuilderContext.()
var leftSpotTarget: String? var leftSpotTarget: String?
var rightSpotTarget: String? var rightSpotTarget: String?
var curtainState = CurtainState.CLOSED var curtainState_ = CurtainState.CLOSED
object : ActBuilderContext { object : ActBuilderContext {
override fun scene(@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") sceneName: String, build: SceneBuilderContext.() -> Unit) { override fun scene(@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") sceneName: String, build: SceneBuilderContext.() -> Unit) {
@ -111,7 +111,11 @@ private fun buildAct(actIndex: Int, actName: String, build: ActBuilderContext.()
override val props = PropsBuilderMap(changedProps) override val props = PropsBuilderMap(changedProps)
override var rightSpotTarget: String? = null override var rightSpotTarget: String? = null
override var leftSpotTarget: String? = null override var leftSpotTarget: String? = null
override var curtainState: CurtainState by ::curtainState override var curtainState: CurtainState
get() = curtainState_
set(value) {
curtainState_ = value
}
override fun actors(build: ActorsBuildContext.() -> Unit) { override fun actors(build: ActorsBuildContext.() -> Unit) {
ActorsBuildContext(actorEntrances, actorExits).build() ActorsBuildContext(actorEntrances, actorExits).build()
@ -155,7 +159,7 @@ private fun buildAct(actIndex: Int, actName: String, build: ActBuilderContext.()
logger.warn("These actors cannot enter because they are already on the stage: ${addedActorsAlreadyOnStage.joinToString()}") logger.warn("These actors cannot enter because they are already on the stage: ${addedActorsAlreadyOnStage.joinToString()}")
} }
actorsOnStage.removeAll(actorExitsNames) actorsOnStage.removeAll(actorExitsNames.toSet())
actorsOnStage.addAll(actorEntrancesNames) actorsOnStage.addAll(actorEntrancesNames)
changedProps.forEach { (k, v) -> props[k] = v } changedProps.forEach { (k, v) -> props[k] = v }
@ -171,7 +175,7 @@ private fun buildAct(actIndex: Int, actName: String, build: ActBuilderContext.()
changedProps.isNotEmpty(), changedProps.isNotEmpty(),
leftSpotTarget, leftSpotTarget,
rightSpotTarget, rightSpotTarget,
curtainState, curtainState_,
runner runner
) )
) )

View file

@ -1,13 +1,13 @@
package de.moritzruth.theaterdsl.value package de.moritzruth.theaterdsl.value
@JvmInline @JvmInline
value class Angle(val degrees: Float) : Comparable<Angle> { value class Angle(val degrees: Double) : Comparable<Angle> {
override fun toString(): String = "${degrees}°" override fun toString(): String = "${degrees}°"
override fun compareTo(other: Angle): Int = degrees.compareTo(other.degrees) override fun compareTo(other: Angle): Int = degrees.compareTo(other.degrees)
fun inSingleRotation(): Angle = Angle( fun inSingleRotation(): Angle = Angle(
if (degrees == 360f || degrees == -360f) degrees if (degrees == 360.0 || degrees == -360.0) degrees
else degrees.mod(360f) else degrees.mod(360f)
) )
@ -17,18 +17,18 @@ value class Angle(val degrees: Float) : Comparable<Angle> {
): ClosedFloatingPointRange<Angle> { ): ClosedFloatingPointRange<Angle> {
override fun lessThanOrEquals(a: Angle, b: Angle): Boolean = a.degrees <= b.degrees override fun lessThanOrEquals(a: Angle, b: Angle): Boolean = a.degrees <= b.degrees
fun asFloatRange(): ClosedFloatingPointRange<Float> = start.degrees..endInclusive.degrees fun asDoubleRange(): ClosedFloatingPointRange<Double> = start.degrees..endInclusive.degrees
companion object { companion object {
val FULL_ROTATION: Range = Range(Angle(0f), Angle(360f)) val FULL_ROTATION: Range = Range(Angle(0.0), Angle(360.0))
} }
} }
} }
inline val Int.degrees: Angle inline val Int.degrees: Angle
get() = Angle(this.toFloat()) get() = Angle(this.toDouble())
inline val Double.degrees: Angle inline val Double.degrees: Angle
get() = Angle(this.toFloat()) get() = Angle(this)
fun Float.asAngle(): Angle = Angle(this) fun Double.asAngle(): Angle = Angle(this)

View file

@ -13,44 +13,44 @@ data class Color(
val x = c * (1 - abs(h.mod(2f) - 1)) val x = c * (1 - abs(h.mod(2f) - 1))
val m = brightness.value - c val m = brightness.value - c
val r: Float val r: Double
val g: Float val g: Double
val b: Float val b: Double
when { when {
h < 1 -> { h < 1 -> {
r = c r = c
g = x g = x
b = 0f b = 0.0
} }
h < 2 -> { h < 2 -> {
r = x r = x
g = c g = c
b = 0f b = 0.0
} }
h < 3 -> { h < 3 -> {
r = 0f r = 0.0
g = c g = c
b = x b = x
} }
h < 4 -> { h < 4 -> {
r = 0f r = 0.0
g = x g = x
b = c b = c
} }
h < 5 -> { h < 5 -> {
r = x r = x
g = 0f g = 0.0
b = c b = c
} }
else /* h < 6 */ -> { else /* h < 6 */ -> {
r = c r = c
g = 0f g = 0.0
b = x b = x
} }
} }

View file

@ -1,10 +1,10 @@
package de.moritzruth.theaterdsl.value package de.moritzruth.theaterdsl.value
@JvmInline @JvmInline
value class Frequency(val hertz: Float) : Comparable<Frequency> { value class Frequency(val hertz: Double) : Comparable<Frequency> {
companion object { companion object {
val ZERO: Frequency = Frequency(0f) val ZERO: Frequency = Frequency(0.0)
val INFINITY: Frequency = Frequency(Float.POSITIVE_INFINITY) val INFINITY: Frequency = Frequency(Double.POSITIVE_INFINITY)
} }
init { init {
@ -21,7 +21,7 @@ value class Frequency(val hertz: Float) : Comparable<Frequency> {
) : ClosedFloatingPointRange<Frequency> { ) : ClosedFloatingPointRange<Frequency> {
override fun lessThanOrEquals(a: Frequency, b: Frequency): Boolean = a.hertz <= b.hertz override fun lessThanOrEquals(a: Frequency, b: Frequency): Boolean = a.hertz <= b.hertz
fun asFloatRange(): ClosedFloatingPointRange<Float> = start.hertz..endInclusive.hertz fun asDoubleRange(): ClosedFloatingPointRange<Double> = start.hertz..endInclusive.hertz
override fun toString(): String = "$start..$endInclusive" override fun toString(): String = "$start..$endInclusive"
} }

View file

@ -4,15 +4,15 @@ import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
val IntProgression.delta: Int get() = last - first val IntProgression.delta: Int get() = last - first
val ClosedFloatingPointRange<Float>.delta: Float get() = endInclusive - start val ClosedFloatingPointRange<Double>.delta: Double get() = endInclusive - start
fun Int.transfer(from: IntRange, to: IntRange): Int = fun Int.transfer(from: IntRange, to: IntRange): Int =
((this - from.first) / from.delta) * to.delta + to.first ((this - from.first) / from.delta) * to.delta + to.first
fun Float.transfer(from: ClosedFloatingPointRange<Float>, to: ClosedFloatingPointRange<Float>): Float = fun Double.transfer(from: ClosedFloatingPointRange<Double>, to: ClosedFloatingPointRange<Double>): Double =
((this - from.start) / from.delta) * to.delta + to.start ((this - from.start) / from.delta) * to.delta + to.start
fun Float.toString(decimalPlaces: Int): String { fun Double.toString(decimalPlaces: Int): String {
val s = (this * (10f.pow(decimalPlaces))).roundToInt().toString().padStart(decimalPlaces + 1, '0') val s = (this * (10f.pow(decimalPlaces))).roundToInt().toString().padStart(decimalPlaces + 1, '0')
return s.dropLast(decimalPlaces) + "." + s.takeLast(decimalPlaces) return s.dropLast(decimalPlaces) + "." + s.takeLast(decimalPlaces)
} }

View file

@ -1,16 +1,16 @@
package de.moritzruth.theaterdsl.value package de.moritzruth.theaterdsl.value
@JvmInline @JvmInline
value class Percentage(val value: Float) : Comparable<Percentage> { value class Percentage(val value: Double) : Comparable<Percentage> {
companion object { companion object {
val VALUE_RANGE: ClosedFloatingPointRange<Float> = 0f..1f val VALUE_RANGE: ClosedFloatingPointRange<Double> = 0.0..1.0
} }
init { init {
require(value in VALUE_RANGE) { "value ($value) must be in 0..1" } require(value in VALUE_RANGE) { "value ($value) must be in 0..1" }
} }
fun ofRange(range: ClosedFloatingPointRange<Float>): Float = value.transfer(VALUE_RANGE, range) fun ofRange(range: ClosedFloatingPointRange<Double>): Double = value.transfer(VALUE_RANGE, range)
override fun compareTo(other: Percentage): Int = value.compareTo(other.value) override fun compareTo(other: Percentage): Int = value.compareTo(other.value)
@ -22,7 +22,7 @@ value class Percentage(val value: Float) : Comparable<Percentage> {
) : ClosedFloatingPointRange<Percentage> { ) : ClosedFloatingPointRange<Percentage> {
override fun lessThanOrEquals(a: Percentage, b: Percentage): Boolean = a.value <= b.value override fun lessThanOrEquals(a: Percentage, b: Percentage): Boolean = a.value <= b.value
fun asFloatRange(): ClosedFloatingPointRange<Float> = start.value..endInclusive.value fun asDoubleRange(): ClosedFloatingPointRange<Double> = start.value..endInclusive.value
companion object { companion object {
val FULL: Range = Range(VALUE_RANGE.start.asPercentage(), VALUE_RANGE.endInclusive.asPercentage()) val FULL: Range = Range(VALUE_RANGE.start.asPercentage(), VALUE_RANGE.endInclusive.asPercentage())
@ -32,16 +32,16 @@ value class Percentage(val value: Float) : Comparable<Percentage> {
inline val Int.percent: Percentage inline val Int.percent: Percentage
get() = try { get() = try {
Percentage(this / 100f) Percentage(this / 100.0)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
throw IllegalArgumentException("must be in 0..100") throw IllegalArgumentException("must be in 0..100")
} }
inline val Double.percent: Percentage inline val Double.percent: Percentage
get() = try { get() = try {
Percentage(this.toFloat() / 100f) Percentage(this / 100f)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
throw IllegalArgumentException("must be in 0..100") throw IllegalArgumentException("must be in 0..100")
} }
fun Float.asPercentage(): Percentage = Percentage(this) fun Double.asPercentage(): Percentage = Percentage(this)

View file

@ -3,31 +3,31 @@ package de.moritzruth.theaterdsl.value
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import kotlin.math.abs import kotlin.math.abs
data class FloatRangesAndValues(val ranges: ImmutableSet<ClosedFloatingPointRange<Float>>, val values: ImmutableSet<Float>) { data class DoubleRangesAndValues(val ranges: ImmutableSet<ClosedFloatingPointRange<Double>>, val values: ImmutableSet<Double>) {
init { init {
require(ranges.isNotEmpty() || values.isNotEmpty()) { "at least one range or value is required" } require(ranges.isNotEmpty() || values.isNotEmpty()) { "at least one range or value is required" }
} }
val highest: Float val highest: Double
val highestBeforeInfinity: Float val highestBeforeInfinity: Double
val lowest: Float val lowest: Double
val lowestAfterInfinity: Float val lowestAfterInfinity: Double
init { init {
var l = Float.POSITIVE_INFINITY var l = Double.POSITIVE_INFINITY
var lai = Float.POSITIVE_INFINITY var lai = Double.POSITIVE_INFINITY
var h = Float.NEGATIVE_INFINITY var h = Double.NEGATIVE_INFINITY
var hbi = Float.NEGATIVE_INFINITY var hbi = Double.NEGATIVE_INFINITY
for (v in values + ranges.flatMap { listOf(it.start, it.endInclusive) }) { for (v in values + ranges.flatMap { listOf(it.start, it.endInclusive) }) {
if (v < lai) { if (v < lai) {
l = v l = v
if (v != Float.NEGATIVE_INFINITY) lai = v if (v != Double.NEGATIVE_INFINITY) lai = v
} }
if (v > hbi) { if (v > hbi) {
h = v h = v
if (v != Float.POSITIVE_INFINITY) hbi = v if (v != Double.POSITIVE_INFINITY) hbi = v
} }
} }
@ -37,12 +37,12 @@ data class FloatRangesAndValues(val ranges: ImmutableSet<ClosedFloatingPointRang
lowestAfterInfinity = lai lowestAfterInfinity = lai
} }
fun getNearestTo(value: Float): Float { fun getNearestTo(value: Double): Double {
var nearestValue = 0f var nearestValue = 0.0
var nearestDistance = 0f var nearestDistance = 0.0
var isFirst = true var isFirst = true
fun update(v: Float) { fun update(v: Double) {
if (isFirst) { if (isFirst) {
nearestValue = v nearestValue = v
nearestDistance = v - value nearestDistance = v - value
@ -71,5 +71,5 @@ data class FloatRangesAndValues(val ranges: ImmutableSet<ClosedFloatingPointRang
return nearestValue return nearestValue
} }
operator fun contains(value: Float): Boolean = getNearestTo(value) == value operator fun contains(value: Double): Boolean = getNearestTo(value) == value
} }

View file

@ -1,7 +1,7 @@
package de.moritzruth.theaterdsl.value package de.moritzruth.theaterdsl.value
@JvmInline @JvmInline
value class Temperature(val kelvin: Float) : Comparable<Temperature> { value class Temperature(val kelvin: Double) : Comparable<Temperature> {
override fun toString(): String = "${kelvin}K" override fun toString(): String = "${kelvin}K"
override fun compareTo(other: Temperature): Int = kelvin.compareTo(other.kelvin) override fun compareTo(other: Temperature): Int = kelvin.compareTo(other.kelvin)