From 7413bcacc34a61c4bdc37a399c6b9a21addf4a9f Mon Sep 17 00:00:00 2001 From: Edwin Jakobs Date: Sat, 9 Dec 2023 10:49:46 +0100 Subject: [PATCH] [orx-color] Match new HueShiftableColor / SaturatableColor interfaces --- .../commonMain/kotlin/spaces/ColorHPLUVa.kt | 8 +- .../commonMain/kotlin/spaces/ColorHSLUVa.kt | 7 +- .../commonMain/kotlin/spaces/ColorOKHSLa.kt | 7 +- .../commonMain/kotlin/spaces/ColorOKHSVa.kt | 9 +- .../commonMain/kotlin/spaces/ColorOKLCHa.kt | 9 +- .../commonMain/kotlin/spaces/ColorXSLUVa.kt | 7 +- .../kotlin/tools/ColorRGBaExtensions.kt | 85 +++++++++---------- .../src/jvmDemo/kotlin/DemoColorRange04.kt | 1 - .../src/jvmDemo/kotlin/DemoHueTools01.kt | 44 ++++++++++ 9 files changed, 116 insertions(+), 61 deletions(-) create mode 100644 orx-color/src/jvmDemo/kotlin/DemoHueTools01.kt diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt index 58cf28f5..292a489b 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt @@ -26,11 +26,15 @@ data class ColorHPLUVa(val h: Double, val s: Double, val l: Double, override val return ColorLCHUVa(l100, c100, h) } - override fun shiftHue(shiftInDegrees: Double): ColorHPLUVa = copy(h = h + (shiftInDegrees)) + override val hue: Double = h + override fun withHue(hue: Double) = copy(h = hue) + override fun shade(factor: Double): ColorHPLUVa = copy(l = l * factor) - override fun saturate(factor: Double): ColorHPLUVa = copy(s = s * factor) + override val saturation: Double = s + + override fun withSaturation(saturation: Double): ColorHPLUVa = copy(s = saturation) override fun toRGBa(): ColorRGBa = toLCHUVa().toRGBa() diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt index 7651bc6a..3afc9caf 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt @@ -114,11 +114,12 @@ data class ColorHSLUVa(val h: Double, val s: Double, val l: Double, override val return ColorXSLUVa(hueToX(h), s, l, alpha) } - override fun shiftHue(shiftInDegrees: Double) = copy(h = h + (shiftInDegrees)) + override val hue: Double =h + override fun withHue(hue: Double): ColorHSLUVa = copy(h = hue) override fun shade(factor: Double) = copy(l = l * factor) - - override fun saturate(factor: Double) = copy(s = s * factor) + override val saturation: Double = s + override fun withSaturation(saturation: Double): ColorHSLUVa = copy(s = saturation) override fun toRGBa(): ColorRGBa { return toLCHUVa().toRGBa() diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSLa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSLa.kt index 7abf922c..3cdd10a7 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSLa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSLa.kt @@ -95,11 +95,14 @@ data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, override val ).toRGBa().toSRGB() } - override fun shiftHue(shiftInDegrees: Double): ColorOKHSLa = copy(h = h + shiftInDegrees) + override val hue: Double = h + + override fun withHue(hue: Double): ColorOKHSLa = copy(h = hue) override fun opacify(factor: Double): ColorOKHSLa = copy(alpha = alpha * factor) + override val saturation: Double = s - override fun saturate(factor: Double): ColorOKHSLa = copy(s = s * factor) + override fun withSaturation(saturation: Double): ColorOKHSLa = copy(s = saturation) override fun shade(factor: Double): ColorOKHSLa = copy(l = l * factor) diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSVa.kt index fa5345cd..2fd5cb18 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSVa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSVa.kt @@ -102,9 +102,14 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, override val ).toRGBa().toSRGB() } - override fun shiftHue(shiftInDegrees: Double): ColorOKHSVa = copy(h = h + shiftInDegrees) + override val hue: Double = h + + override fun withHue(hue: Double): ColorOKHSVa = copy(h = hue) + override fun opacify(factor: Double): ColorOKHSVa = copy(alpha = alpha * factor) - override fun saturate(factor: Double): ColorOKHSVa = copy(s = s * factor) + override val saturation: Double = s + override fun withSaturation(saturation: Double): ColorOKHSVa = copy(s = saturation) + override fun shade(factor: Double): ColorOKHSVa = copy(v = v * factor) override fun minus(right: ColorOKHSVa) = copy(h = h - right.h, s = s - right.s, v = v - right.v, alpha = alpha - right.alpha) diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt index e8dc27ff..008f4be9 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt @@ -12,8 +12,8 @@ import kotlin.math.* data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, override val alpha: Double = 1.0) : ColorModel, ShadableColor, + ChromaColor, HueShiftableColor, - SaturatableColor, AlgebraicColor { companion object { @@ -35,8 +35,6 @@ data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, override val override fun opacify(factor: Double) = copy(alpha = alpha * factor) override fun shade(factor: Double) = copy(l = l * factor) - override fun shiftHue(shiftInDegrees: Double) = copy(h = h + shiftInDegrees) - override fun saturate(factor: Double) = copy(c = c * factor) override fun plus(right: ColorOKLCHa) = copy(l = l + right.l, c = c + right.c, h = h + right.h, alpha = alpha + right.alpha) override fun minus(right: ColorOKLCHa) = copy(l = l - right.l, c = c - right.c, h = h - right.h, alpha = alpha - right.alpha) @@ -51,6 +49,11 @@ data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, override val override fun toRGBa(): ColorRGBa = toOKLABa().toRGBa() override fun toVector4(): Vector4 = Vector4(l, c, h, alpha) + override val chroma: Double = c + override fun withChroma(chroma: Double): ColorOKLCHa = copy(c = chroma) + override val hue: Double = h + + override fun withHue(hue: Double): ColorOKLCHa = copy(h = hue) } fun mix(left: ColorOKLCHa, right: ColorOKLCHa, x: Double): ColorOKLCHa { diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt index b80df8c9..f8d850f7 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt @@ -16,12 +16,13 @@ data class ColorXSLUVa(val x: Double, val s: Double, val l: Double, override val @Deprecated("Legacy alpha parameter name", ReplaceWith("alpha")) val a = alpha - - override fun shiftHue(shiftInDegrees: Double) = copy(x = x + (shiftInDegrees)) + override val hue: Double = x + override fun withHue(hue: Double): ColorXSLUVa = copy(x = hue) override fun shade(factor: Double) = copy(l = l * factor) + override val saturation: Double = s - override fun saturate(factor: Double) = copy(s = s * factor) + override fun withSaturation(saturation: Double): ColorXSLUVa = copy(s = saturation) override fun toRGBa(): ColorRGBa = toHSLUVa().toRGBa() diff --git a/orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt b/orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt index f807bad9..ed6a0a05 100644 --- a/orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt +++ b/orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt @@ -3,9 +3,27 @@ package org.openrndr.extra.color.tools import org.openrndr.color.* import org.openrndr.extra.color.spaces.* +fun ColorRGBa.matchLinearity(other: ColorRGBa): ColorRGBa { + return if (other.linearity.isEquivalent(linearity)) { + this + } else { + if (other.linearity.isEquivalent(Linearity.LINEAR)) { + toLinear() + } else if (other.linearity.isEquivalent(Linearity.SRGB)) { + toSRGB() + } else { + this + } + } +} + +inline fun ColorRGBa.hue(): Double + where T : HueShiftableColor, + T : ColorModel = convertTo().hue + inline fun ColorRGBa.blendWith(other: ColorRGBa, steps: Int): Sequence where T : AlgebraicColor, - T: ColorModel { + T : ColorModel { return sequence { for (step in 0 until steps) { yield(mixedWith(other, step / (steps - 1.0))) @@ -13,6 +31,7 @@ inline fun ColorRGBa.blendWith(other: ColorRGBa, steps: Int): Sequen } } + inline fun > ColorRGBa.convertTo(): T { val converted = when (T::class) { ColorHSLa::class -> this.toHSLa() @@ -38,61 +57,37 @@ inline fun > ColorRGBa.convertTo(): T { return converted as T } +inline fun ColorRGBa.mixHue(hue: Double, factor: Double): ColorRGBa + where T : HueShiftableColor, + T : ColorModel, + T : ConvertibleToColorRGBa = convertTo().mixHue(hue, factor).toRGBa().matchLinearity(this) + +inline fun ColorRGBa.withHue(hue: Double): ColorRGBa + where T : HueShiftableColor, + T : ColorModel, + T : ConvertibleToColorRGBa = convertTo().withHue(hue).toRGBa().matchLinearity(this) + +inline fun ColorRGBa.mixSaturation(saturation: Double, factor: Double): ColorRGBa + where T : SaturatableColor, + T : ColorModel, + T : ConvertibleToColorRGBa = + convertTo().mixSaturation(saturation, factor).toRGBa().matchLinearity(this) + + inline fun ColorRGBa.mixedWith(other: ColorRGBa, factor: Double): ColorRGBa where T : AlgebraicColor, T : ColorModel { - val source = convertTo() val target = other.convertTo() - val mixed = source.mix(target, factor).toRGBa() - - return if (mixed.linearity.isEquivalent(linearity)) { - mixed - } else { - if (linearity.isEquivalent(Linearity.LINEAR)) { - mixed.toLinear() - } else if (linearity.isEquivalent(Linearity.SRGB)) { - mixed.toSRGB() - } else { - mixed - } - } + return source.mix(target, factor).toRGBa().matchLinearity(this) } inline fun ColorRGBa.saturate(factor: Double): ColorRGBa where T : SaturatableColor, T : ColorModel, - T : ConvertibleToColorRGBa { - - val saturated = convertTo().saturate(factor).toRGBa() - return if (saturated.linearity.isEquivalent(linearity)) { - saturated - } else { - if (linearity.isEquivalent(Linearity.LINEAR)) { - saturated.toLinear() - } else if (linearity.isEquivalent(Linearity.SRGB)) { - saturated.toSRGB() - } else { - saturated - } - } -} + T : ConvertibleToColorRGBa = convertTo().saturate(factor).toRGBa().matchLinearity(this) inline fun ColorRGBa.shiftHue(degrees: Double): ColorRGBa where T : HueShiftableColor, T : ColorModel, - T : ConvertibleToColorRGBa { - val converted = convertTo() - val shifted = (converted.shiftHue(degrees) as ConvertibleToColorRGBa).toRGBa() - return if (shifted.linearity.isEquivalent(linearity)) { - shifted - } else { - if (linearity.isEquivalent(Linearity.LINEAR)) { - shifted.toLinear() - } else if (linearity.isEquivalent(Linearity.SRGB)) { - shifted.toSRGB() - } else { - shifted - } - } -} + T : ConvertibleToColorRGBa = convertTo().shiftHue(degrees).toRGBa().matchLinearity(this) diff --git a/orx-color/src/jvmDemo/kotlin/DemoColorRange04.kt b/orx-color/src/jvmDemo/kotlin/DemoColorRange04.kt index b5010c12..836ae957 100644 --- a/orx-color/src/jvmDemo/kotlin/DemoColorRange04.kt +++ b/orx-color/src/jvmDemo/kotlin/DemoColorRange04.kt @@ -18,7 +18,6 @@ fun main() { configure { width = 800 height = 800 - } program { val mesh = sphereMesh(8, 8, radius = 0.1) diff --git a/orx-color/src/jvmDemo/kotlin/DemoHueTools01.kt b/orx-color/src/jvmDemo/kotlin/DemoHueTools01.kt new file mode 100644 index 00000000..7e428aaa --- /dev/null +++ b/orx-color/src/jvmDemo/kotlin/DemoHueTools01.kt @@ -0,0 +1,44 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.isolated +import org.openrndr.extra.color.presets.NAVY +import org.openrndr.extra.color.spaces.OKHSV +import org.openrndr.extra.color.tools.hue +import org.openrndr.extra.color.tools.mixHue +import org.openrndr.extra.color.tools.withHue +import kotlin.math.cos + +fun main() { + application { + configure { + width = 800 + height = 800 + } + program { + extend { + val seedColor = ColorRGBa.PINK + val targetHue = seconds*100.0 + + val rows = 10 + val columns = 12 + + val cellWidth = width / columns.toDouble() + val cellHeight = height / rows.toDouble() + + drawer.stroke = null + for (j in 0 until 10) { + drawer.isolated { + for (i in 0 until columns) { + drawer.fill = seedColor + .withHue(i * 360.0 / columns) + .mixHue(targetHue, j / (rows.toDouble()-1.0)) + drawer.rectangle(0.0, 0.0, cellWidth, cellHeight) + drawer.translate(cellWidth, 0.0) + } + } + drawer.translate(0.0, cellHeight) + } + } + } + } +} \ No newline at end of file