From 5b7327eda16e9f8ef217bcc955212ae6aabd814d Mon Sep 17 00:00:00 2001 From: Vechro <6316604+Vechro@users.noreply.github.com> Date: Mon, 11 Jul 2022 16:40:05 +0300 Subject: [PATCH] [orx-color] Color model changes (#246) --- .../src/commonMain/kotlin/presets/Colors.kt | 2 - .../commonMain/kotlin/spaces/ColorHPLUVa.kt | 71 ++++++++ .../commonMain/kotlin/spaces/ColorHSLUVa.kt | 172 +++--------------- .../commonMain/kotlin/spaces/ColorOKHSL.kt | 40 ++-- .../commonMain/kotlin/spaces/ColorOKHSV.kt | 45 ++--- .../commonMain/kotlin/spaces/ColorOKLABa.kt | 7 +- .../commonMain/kotlin/spaces/ColorOKLCHa.kt | 22 ++- .../commonMain/kotlin/spaces/ColorXSLUVa.kt | 66 +++++++ orx-jvm/orx-gui/src/main/kotlin/Gui.kt | 6 +- .../src/main/kotlin/Keyframer.kt | 2 +- .../src/main/kotlin/RabbitControlServer.kt | 2 +- 11 files changed, 218 insertions(+), 217 deletions(-) create mode 100644 orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt create mode 100644 orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt diff --git a/orx-color/src/commonMain/kotlin/presets/Colors.kt b/orx-color/src/commonMain/kotlin/presets/Colors.kt index 1a687f2c..fbd25385 100644 --- a/orx-color/src/commonMain/kotlin/presets/Colors.kt +++ b/orx-color/src/commonMain/kotlin/presets/Colors.kt @@ -5,8 +5,6 @@ package org.openrndr.extras.color.presets import org.openrndr.color.ColorRGBa import org.openrndr.color.rgb -val ColorRGBa.Companion.CYAN by lazy { rgb(0.0, 1.0, 1.0) } -val ColorRGBa.Companion.MAGENTA by lazy { rgb(1.0, 0.0, 1.0) } val ColorRGBa.Companion.ALICE_BLUE by lazy { rgb(0.941176, 0.972549, 1.0) } val ColorRGBa.Companion.ANTIQUE_WHITE by lazy { rgb(0.980392, 0.921569, 0.843137) } val ColorRGBa.Companion.AQUA by lazy { rgb(0.0, 1.0, 1.0) } diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt new file mode 100644 index 00000000..a18f2474 --- /dev/null +++ b/orx-color/src/commonMain/kotlin/spaces/ColorHPLUVa.kt @@ -0,0 +1,71 @@ +package org.openrndr.extra.color.spaces + +import org.openrndr.color.* +import org.openrndr.math.Vector4 +import org.openrndr.math.mixAngle + +data class ColorHPLUVa(val h: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : + ColorModel, + HueShiftableColor, + SaturatableColor, + ShadableColor, + AlgebraicColor { + fun toLCHUVa(): ColorLCHUVa { + val l1 = l + if (l1 > 0.9999999) { + return ColorLCHUVa(100.0, 0.0, h) + } + if (l1 < 0.00000001) { + return ColorLCHUVa(0.0, 0.0, h) + } + val l100 = l1 * 100.0 + val max100 = maxSafeChromaForL(l100) + val c100 = max100 * s + return ColorLCHUVa(l100, c100, h) + } + + override fun shiftHue(shiftInDegrees: Double): ColorHPLUVa = copy(h = h + (shiftInDegrees)) + + override fun shade(factor: Double): ColorHPLUVa = copy(l = l * factor) + + override fun saturate(factor: Double): ColorHPLUVa = copy(s = s * factor) + + override fun toRGBa(): ColorRGBa = toLCHUVa().toRGBa() + + override fun opacify(factor: Double) = copy(alpha = alpha * factor) + + override fun minus(right: ColorHPLUVa) = copy(h = h - right.h, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) + + override fun plus(right: ColorHPLUVa) = copy(h = h + right.h, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) + + override fun times(scale: Double) = copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale) + + override fun mix(other: ColorHPLUVa, factor: Double) = mix(this, other, factor) + + override fun toVector4(): Vector4 = Vector4(h, s, l, alpha) +} + +fun mix(left: ColorHPLUVa, right: ColorHPLUVa, x: Double): ColorHPLUVa { + val sx = x.coerceIn(0.0, 1.0) + return ColorHPLUVa( + mixAngle(left.h, right.h, sx), + (1.0 - sx) * left.s + sx * right.s, + (1.0 - sx) * left.l + sx * right.l, + (1.0 - sx) * left.alpha + sx * right.alpha) +} + +fun ColorRGBa.toHPLUVa(): ColorHPLUVa = toLCHUVa().toHPLUVa() + +fun ColorLCHUVa.toHPLUVa(): ColorHPLUVa { + val l100 = l + if (l100 > 99.9999999) { + return ColorHPLUVa(h, 0.0, 1.0) + } + if (l100 < 0.00000001) { + return ColorHPLUVa(h, 0.0, 0.0) + + } + val max100 = maxSafeChromaForL(l) + val s1 = c / max100 + return ColorHPLUVa(h, s1, l100 / 100.0) +} diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt index 82d0041b..04e2dda1 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt @@ -1,6 +1,8 @@ package org.openrndr.extra.color.spaces import org.openrndr.color.* +import org.openrndr.math.Vector4 +import org.openrndr.math.map import org.openrndr.math.mixAngle import kotlin.math.* @@ -9,10 +11,10 @@ private val m = arrayOf( doubleArrayOf(-0.96924363628087, 1.87596750150772, 0.041555057407175), doubleArrayOf(0.055630079696993, -0.20397695888897, 1.056971514242878)) -private val kappa = 903.2962962 -private val epsilon = 0.0088564516 +private const val kappa = 903.2962962 +private const val epsilon = 0.0088564516 -private fun getBounds(L: Double): List? { +private fun getBounds(L: Double): List { val result = ArrayList() val sub1 = (L + 16).pow(3.0) / 1560896 val sub2 = if (sub1 > epsilon) sub1 else L / kappa @@ -47,11 +49,11 @@ private class Length(val length: Double) { val greaterEqualZero: Boolean = length >= 0 } -private fun maxSafeChromaForL(L100: Double): Double { +internal fun maxSafeChromaForL(L100: Double): Double { val bounds = getBounds(L100) var min = Double.MAX_VALUE for (i in 0..1) { - val m1 = bounds!![i][0] + val m1 = bounds[i][0] val b1 = bounds[i][1] val line = doubleArrayOf(m1, b1) val x = intersectLineLine(line, doubleArrayOf(-1 / m1, 0.0)) @@ -61,11 +63,11 @@ private fun maxSafeChromaForL(L100: Double): Double { return min } -fun maxChromaForLH(L100: Double, H: Double): Double { +private fun maxChromaForLH(L100: Double, H: Double): Double { val hrad = H / 360 * PI * 2 val bounds = getBounds(L100) var min = Double.MAX_VALUE - for (bound in bounds!!) { + for (bound in bounds) { val length: Length = lengthOfRayUntilIntersect(hrad, bound) if (length.greaterEqualZero) { min = min(min, length.length) @@ -77,35 +79,37 @@ fun maxChromaForLH(L100: Double, H: Double): Double { /** * HSLUV color space */ -data class ColorHSLUVa(val h: Double, val s: Double, val l: Double, val a: Double = 1.0) : - ConvertibleToColorRGBa, +data class ColorHSLUVa(val h: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : + ColorModel, HueShiftableColor, SaturatableColor, ShadableColor, - OpacifiableColor, AlgebraicColor { + @Deprecated("Legacy alpha parameter name", ReplaceWith("alpha")) + val a = alpha + fun toLCHUVa(): ColorLCHUVa { val l100 = l * 100.0 val s100 = s * 100.0 if (l100 > 99.9999999) { - ColorLCHUVa(100.0, 0.0, h, a) + ColorLCHUVa(100.0, 0.0, h, alpha) } if (l100 < 0.00000001) { - ColorLCHUVa(0.0, 0.0, h, a) + ColorLCHUVa(0.0, 0.0, h, alpha) } val max100 = maxChromaForLH(l100, h) val c: Double = max100 / 100 * s100 - return ColorLCHUVa(l100, c, h, a) + return ColorLCHUVa(l100, c, h, alpha) } fun toXSLUVa() : ColorXSLUVa { - return ColorXSLUVa(hueToX(h), s, l, a) + return ColorXSLUVa(hueToX(h), s, l, alpha) } override fun shiftHue(shiftInDegrees: Double) = copy(h = h + (shiftInDegrees)) @@ -118,16 +122,18 @@ data class ColorHSLUVa(val h: Double, val s: Double, val l: Double, val a: Doubl return toLCHUVa().toRGBa() } - override fun opacify(factor: Double) = copy(a = a * factor) + override fun opacify(factor: Double) = copy(alpha = alpha * factor) - override fun minus(right: ColorHSLUVa) = copy(h = h - right.h, s = s - right.s, l = l - right.l, a = a - right.a) + override fun minus(right: ColorHSLUVa) = copy(h = h - right.h, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) - override fun plus(right: ColorHSLUVa) = copy(h = h + right.h, s = s + right.s, l = l + right.l, a = a + right.a) + override fun plus(right: ColorHSLUVa) = copy(h = h + right.h, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) - override fun times(scale: Double) = copy(h = h * scale, s = s * scale, l = l * scale, a = a * scale) + override fun times(scale: Double) = copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale) override fun mix(other: ColorHSLUVa, factor: Double) = mix(this, other, factor) + override fun toVector4(): Vector4 = Vector4(h, s, l, alpha) + } fun mix(left: ColorHSLUVa, right: ColorHSLUVa, x: Double): ColorHSLUVa { @@ -136,51 +142,10 @@ fun mix(left: ColorHSLUVa, right: ColorHSLUVa, x: Double): ColorHSLUVa { mixAngle(left.h, right.h, sx), (1.0 - sx) * left.s + sx * right.s, (1.0 - sx) * left.l + sx * right.l, - (1.0 - sx) * left.a + sx * right.a) + (1.0 - sx) * left.alpha + sx * right.alpha) } -data class ColorXSLUVa(val x: Double, val s: Double, val l: Double, val a: Double): - ConvertibleToColorRGBa, - HueShiftableColor, - SaturatableColor, - ShadableColor, - OpacifiableColor, - AlgebraicColor { - override fun shiftHue(shiftInDegrees: Double) = copy(x = x + (shiftInDegrees)) - - override fun shade(factor: Double) = copy(l = l * factor) - - override fun saturate(factor: Double) = copy(s = s * factor) - - override fun toRGBa(): ColorRGBa { - return toHSLUVa().toRGBa() - } - - fun toHSLUVa(): ColorHSLUVa = ColorHSLUVa(xToHue(x), s, l, a) - - override fun opacify(factor: Double) = copy(a = a * factor) - - override fun minus(right: ColorXSLUVa) = copy(x = x - right.x, s = s - right.s, l = l - right.l, a = a - right.a) - - override fun plus(right: ColorXSLUVa) = copy(x = x + right.x, s = s + right.s, l = l + right.l, a = a + right.a) - - override fun times(scale: Double) = copy(x = x * scale, s = s * scale, l = l * scale, a = a * scale) - - override fun mix(other: ColorXSLUVa, factor: Double) = mix(this, other, factor) -} - -fun ColorRGBa.toXSLUVa() = toHSLUVa().toXSLUVa() - -fun mix(left: ColorXSLUVa, right: ColorXSLUVa, x: Double): ColorXSLUVa { - val sx = x.coerceIn(0.0, 1.0) - return ColorXSLUVa( - mixAngle(left.x, right.x, sx), - (1.0 - sx) * left.s + sx * right.s, - (1.0 - sx) * left.l + sx * right.l, - (1.0 - sx) * left.a + sx * right.a) -} - -private fun map(x: Double, a: Double, b: Double, c: Double, d: Double): Double { +internal fun map(x: Double, a: Double, b: Double, c: Double, d: Double): Double { return ((x - a) / (b - a)) * (d - c) + c } @@ -201,90 +166,6 @@ private fun hueToX(hue:Double): Double { } } -private fun xToHue(x:Double) : Double { - val x = x % 360.0 - return if (0.0 <= x && x < 60.0) { - map(x, 0.0, 60.0, 0.0, 35.0) - } else if (60.0 <= x && x < 120.0) { - map(x, 60.0, 120.0, 35.0, 60.0) - } else if (120.0 <= x && x < 180.0) { - map(x, 120.0, 180.0, 60.0, 135.0) - } else if (180.0 <= x && x < 240.0) { - map(x, 180.0, 240.0, 135.0, 225.0) - } else if (240.0 <= x && x < 300.0) { - map(x, 240.0, 300.0, 225.0, 275.0) - } else { - map(x, 300.0, 360.0, 276.0, 360.0) - } -} - -data class ColorHPLUVa(val h: Double, val s: Double, val l: Double, val a: Double = 1.0) : - ConvertibleToColorRGBa, - HueShiftableColor, - SaturatableColor, - ShadableColor, - OpacifiableColor, - AlgebraicColor { - fun toLCHUVa(): ColorLCHUVa { - val l1 = l - if (l1 > 0.9999999) { - return ColorLCHUVa(100.0, 0.0, h) - } - if (l1 < 0.00000001) { - return ColorLCHUVa(0.0, 0.0, h) - } - val l100 = l1 * 100.0 - val max100 = maxSafeChromaForL(l100) - val c100 = max100 * s - return ColorLCHUVa(l100, c100, h) - } - - override fun shiftHue(shiftInDegrees: Double): ColorHPLUVa { - return copy(h = h + (shiftInDegrees)) - } - - override fun shade(factor: Double): ColorHPLUVa = copy(l = l * factor) - - override fun saturate(factor: Double): ColorHPLUVa = copy(s = s * factor) - - override fun toRGBa(): ColorRGBa = toLCHUVa().toRGBa() - - override fun opacify(factor: Double) = copy(a = a * factor) - - override fun minus(right: ColorHPLUVa) = copy(h = h - right.h, s = s - right.s, l = l - right.l, a = a - right.a) - - override fun plus(right: ColorHPLUVa) = copy(h = h + right.h, s = s + right.s, l = l + right.l, a = a + right.a) - - override fun times(scale: Double) = copy(h = h * scale, s = s * scale, l = l * scale, a = a * scale) - - override fun mix(other: ColorHPLUVa, factor: Double) = mix(this, other, factor) - -} - -fun mix(left: ColorHPLUVa, right: ColorHPLUVa, x: Double): ColorHPLUVa { - val sx = x.coerceIn(0.0, 1.0) - return ColorHPLUVa( - mixAngle(left.h, right.h, sx), - (1.0 - sx) * left.s + sx * right.s, - (1.0 - sx) * left.l + sx * right.l, - (1.0 - sx) * left.a + sx * right.a) -} - - -fun ColorLCHUVa.toHPLUVa(): ColorHPLUVa { - val l100 = l - if (l100 > 99.9999999) { - return ColorHPLUVa(h, 0.0, 1.0) - } - if (l100 < 0.00000001) { - return ColorHPLUVa(h, 0.0, 0.0) - - } - val max100 = maxSafeChromaForL(l) - val s1 = c / max100 - return ColorHPLUVa(h, s1, l100 / 100.0) -} - fun ColorLCHUVa.toHSLUVa(): ColorHSLUVa { val l100 = l @@ -301,4 +182,3 @@ fun ColorLCHUVa.toHSLUVa(): ColorHSLUVa { } fun ColorRGBa.toHSLUVa(): ColorHSLUVa = toLCHUVa().toHSLUVa() -fun ColorRGBa.toHPLUVa(): ColorHPLUVa = toLCHUVa().toHPLUVa() \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSL.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSL.kt index b5a5770a..c20c3d98 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSL.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSL.kt @@ -1,16 +1,16 @@ package org.openrndr.extra.color.spaces import org.openrndr.color.* +import org.openrndr.math.Vector4 import org.openrndr.math.mixAngle import kotlin.math.* -data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, val a: Double = 1.0) : +data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : + ColorModel, HueShiftableColor, - OpacifiableColor, SaturatableColor, ShadableColor, - AlgebraicColor, - ConvertibleToColorRGBa { + AlgebraicColor { companion object { fun fromColorRGBa(c: ColorRGBa): ColorOKHSLa { @@ -48,11 +48,14 @@ data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, val a: Doubl } } + @Deprecated("Legacy alpha parameter name", ReplaceWith("alpha")) + val a = alpha + override fun toRGBa(): ColorRGBa { if (l == 1.0) { - ColorRGBa(1.0, 1.0, 1.0, a) + ColorRGBa(1.0, 1.0, 1.0, alpha) } else if (l == 0.0) { - ColorRGBa(0.0, 0.0, 0.0, a) + ColorRGBa(0.0, 0.0, 0.0, alpha) } val a_ = cos(2 * PI * h / 360.0); val b_ = sin(2 * PI * h / 360.0); @@ -97,29 +100,21 @@ data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, val a: Doubl return ColorOKLABa(if (L == L) L else 0.0, if (C == C) C * a_ else 0.0, if (C == C) C * b_ else 0.0).toRGBa().toSRGB() } - override fun shiftHue(shiftInDegrees: Double): ColorOKHSLa { - return copy(h = h + shiftInDegrees) - } + override fun shiftHue(shiftInDegrees: Double): ColorOKHSLa = copy(h = h + shiftInDegrees) - override fun opacify(factor: Double): ColorOKHSLa { - return copy(a = a * factor) - } + override fun opacify(factor: Double): ColorOKHSLa = copy(alpha = alpha * factor) - override fun saturate(factor: Double): ColorOKHSLa { - return copy(s = s * factor) - } + override fun saturate(factor: Double): ColorOKHSLa = copy(s = s * factor) - override fun shade(factor: Double): ColorOKHSLa { - return copy(l = l * factor) - } + override fun shade(factor: Double): ColorOKHSLa = copy(l = l * factor) override fun minus(right: ColorOKHSLa) = - copy(h = h - right.h, s = s - right.s, l = l - right.l, a = a - right.a) + copy(h = h - right.h, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) override fun plus(right: ColorOKHSLa) = - copy(h = h + right.h, s = s + right.s, l = l + right.l, a = a + right.a) + copy(h = h + right.h, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) - override fun times(scale: Double): ColorOKHSLa = copy(h = h * scale, s = s * scale, l = l * scale, a = a * scale) + override fun times(scale: Double): ColorOKHSLa = copy(h = h * scale, s = s * scale, l = l * scale, alpha = alpha * scale) override fun mix(other: ColorOKHSLa, factor: Double): ColorOKHSLa { val sx = factor.coerceIn(0.0, 1.0) @@ -127,10 +122,11 @@ data class ColorOKHSLa(val h: Double, val s: Double, val l: Double, val a: Doubl mixAngle(h, other.h, sx) / 360.0, (1.0 - sx) * s + sx * other.s, (1.0 - sx) * l + sx * other.l, - (1.0 - sx) * a + sx * other.a + (1.0 - sx) * alpha + sx * other.alpha ) } + override fun toVector4(): Vector4 = Vector4(h, s, l, alpha) } fun ColorRGBa.toOKHSLa(): ColorOKHSLa = ColorOKHSLa.fromColorRGBa(this) \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSV.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSV.kt index 91a9ca21..5e57d135 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKHSV.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKHSV.kt @@ -1,16 +1,16 @@ package org.openrndr.extra.color.spaces import org.openrndr.color.* +import org.openrndr.math.Vector4 import org.openrndr.math.mixAngle import kotlin.math.* -data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, val a: Double = 1.0) : +data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, override val alpha: Double = 1.0) : + ColorModel, HueShiftableColor, - OpacifiableColor, SaturatableColor, ShadableColor, - AlgebraicColor, - ConvertibleToColorRGBa { + AlgebraicColor { companion object { fun fromColorRGBa(c: ColorRGBa): ColorOKHSVa { @@ -51,6 +51,9 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, val a: Doubl } } + @Deprecated("Legacy alpha parameter name", ReplaceWith("alpha")) + val a = alpha + override fun toRGBa(): ColorRGBa { val a_ = cos(2 * PI * h / 360.0) val b_ = sin(2 * PI * h / 360.0) @@ -81,7 +84,7 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, val a: Doubl L = L_new; val rgb_scale = - ColorOKLABa(L_vt, a_ * C_vt, b_ * C_vt, a).toRGBa().toLinear()// oklab_to_linear_srgb(L_vt,a_*C_vt,b_*C_vt); + ColorOKLABa(L_vt, a_ * C_vt, b_ * C_vt, alpha).toRGBa().toLinear()// oklab_to_linear_srgb(L_vt,a_*C_vt,b_*C_vt); val scale_L = (1.0 / (max(rgb_scale.r, rgb_scale.g, rgb_scale.b, 0.0))).pow(1.0 / 3.0) // remove to see effect without rescaling @@ -94,29 +97,15 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, val a: Doubl if (C == C) C * b_ else 0.0).toRGBa().toSRGB() } - override fun shiftHue(shiftInDegrees: Double): ColorOKHSVa { - return copy(h = h + shiftInDegrees) - } - - override fun opacify(factor: Double): ColorOKHSVa { - return copy(a = a * factor) - } - - override fun saturate(factor: Double): ColorOKHSVa { - return copy(s = s * factor) - } - - override fun shade(factor: Double): ColorOKHSVa { - return copy(v = v * factor) - } - + override fun shiftHue(shiftInDegrees: Double): ColorOKHSVa = copy(h = h + shiftInDegrees) + override fun opacify(factor: Double): ColorOKHSVa = copy(alpha = alpha * factor) + override fun saturate(factor: Double): ColorOKHSVa = copy(s = s * factor) + 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, a = a - right.a) - + copy(h = h - right.h, s = s - right.s, v = v - right.v, alpha = alpha - right.alpha) override fun plus(right: ColorOKHSVa) = - copy(h = h + right.h, s = s + right.s, v = v + right.v, a = a + right.a) - - override fun times(scale: Double): ColorOKHSVa = copy(h = h * scale, s = s * scale, v = v * scale, a = a * scale) + copy(h = h + right.h, s = s + right.s, v = v + right.v, alpha = alpha + right.alpha) + override fun times(scale: Double): ColorOKHSVa = copy(h = h * scale, s = s * scale, v = v * scale, alpha = alpha * scale) override fun mix(other: ColorOKHSVa, factor: Double): ColorOKHSVa { val sx = factor.coerceIn(0.0, 1.0) @@ -124,9 +113,11 @@ data class ColorOKHSVa(val h: Double, val s: Double, val v: Double, val a: Doubl mixAngle(h, other.h, sx), (1.0 - sx) * s + sx * other.s, (1.0 - sx) * v + sx * other.v, - (1.0 - sx) * a + sx * other.a + (1.0 - sx) * alpha + sx * other.alpha ) } + + override fun toVector4(): Vector4 = Vector4(h, s, v, alpha) } fun ColorRGBa.toOKHSVa(): ColorOKHSVa = ColorOKHSVa.fromColorRGBa(this) \ No newline at end of file diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt index c12d93af..9a34b47b 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt @@ -1,7 +1,6 @@ package org.openrndr.extra.color.spaces import org.openrndr.color.* -import org.openrndr.math.CastableToVector4 import org.openrndr.math.Vector4 import kotlin.math.pow @@ -11,11 +10,9 @@ import kotlin.math.pow * [a] = red (-1.0) to green (1.0), * [b] = yellow (-1.0) to blue (1.0). */ -data class ColorOKLABa(val l: Double, val a: Double, val b: Double, val alpha: Double = 1.0) : - ConvertibleToColorRGBa, - CastableToVector4, +data class ColorOKLABa(val l: Double, val a: Double, val b: Double, override val alpha: Double = 1.0) : + ColorModel, ShadableColor, - OpacifiableColor, AlgebraicColor { companion object { diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt index ae72796a..d454cb6b 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKLCHa.kt @@ -1,16 +1,14 @@ package org.openrndr.extra.color.spaces import org.openrndr.color.* -import org.openrndr.math.asDegrees -import org.openrndr.math.asRadians -import org.openrndr.math.mixAngle +import org.openrndr.math.* import kotlin.math.* /** * Color in cylindrical OKLab space */ -data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, val a: Double = 1.0) : ConvertibleToColorRGBa, - OpacifiableColor, +data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, override val alpha: Double = 1.0) : + ColorModel, ShadableColor, HueShiftableColor, SaturatableColor, @@ -30,14 +28,17 @@ data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, val a: Doubl } } - override fun opacify(factor: Double) = copy(a = a * factor) + @Deprecated("Legacy alpha parameter name", ReplaceWith("alpha")) + val a = alpha + + override fun opacify(factor: Double) = copy(alpha = a * 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, a = a + right.a) - override fun minus(right: ColorOKLCHa) = copy(l = l - right.l, c = c - right.c, h = h - right.h, a = a - right.a) - override fun times(scale: Double) = copy(l = l * scale, c = c * scale, h = h * scale, a = a * scale) + override fun plus(right: ColorOKLCHa) = copy(l = l + right.l, c = c + right.c, h = h + right.h, alpha = a + right.a) + override fun minus(right: ColorOKLCHa) = copy(l = l - right.l, c = c - right.c, h = h - right.h, alpha = a - right.a) + override fun times(scale: Double) = copy(l = l * scale, c = c * scale, h = h * scale, alpha = a * scale) override fun mix(other: ColorOKLCHa, factor: Double) = mix(this, other, factor) fun toOKLABa(): ColorOKLABa { @@ -46,7 +47,8 @@ data class ColorOKLCHa(val l: Double, val c: Double, val h: Double, val a: Doubl return ColorOKLABa(l, a, b, alpha = this.a) } - override fun toRGBa() = toOKLABa().toRGBa() + override fun toRGBa(): ColorRGBa = toOKLABa().toRGBa() + override fun toVector4(): Vector4 = Vector4(l, c, h, alpha) } 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 new file mode 100644 index 00000000..384d202d --- /dev/null +++ b/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt @@ -0,0 +1,66 @@ +package org.openrndr.extra.color.spaces + +import org.openrndr.color.* +import org.openrndr.math.Vector4 +import org.openrndr.math.mixAngle + +data class ColorXSLUVa(val x: Double, val s: Double, val l: Double, override val alpha: Double = 1.0) : + ColorModel, + HueShiftableColor, + SaturatableColor, + ShadableColor, + AlgebraicColor { + + @Deprecated("Legacy alpha parameter name", ReplaceWith("alpha")) + val a = alpha + + override fun shiftHue(shiftInDegrees: Double) = copy(x = x + (shiftInDegrees)) + + override fun shade(factor: Double) = copy(l = l * factor) + + override fun saturate(factor: Double) = copy(s = s * factor) + + override fun toRGBa(): ColorRGBa = toHSLUVa().toRGBa() + + fun toHSLUVa(): ColorHSLUVa = ColorHSLUVa(xToHue(x), s, l, alpha) + + override fun opacify(factor: Double) = copy(alpha = alpha * factor) + + override fun minus(right: ColorXSLUVa) = copy(x = x - right.x, s = s - right.s, l = l - right.l, alpha = alpha - right.alpha) + + override fun plus(right: ColorXSLUVa) = copy(x = x + right.x, s = s + right.s, l = l + right.l, alpha = alpha + right.alpha) + + override fun times(scale: Double) = copy(x = x * scale, s = s * scale, l = l * scale, alpha = alpha * scale) + + override fun mix(other: ColorXSLUVa, factor: Double) = mix(this, other, factor) + + override fun toVector4(): Vector4 = Vector4(x, s, l, alpha) +} + +private fun xToHue(x:Double) : Double { + val x = x % 360.0 + return if (0.0 <= x && x < 60.0) { + map(x, 0.0, 60.0, 0.0, 35.0) + } else if (60.0 <= x && x < 120.0) { + map(x, 60.0, 120.0, 35.0, 60.0) + } else if (120.0 <= x && x < 180.0) { + map(x, 120.0, 180.0, 60.0, 135.0) + } else if (180.0 <= x && x < 240.0) { + map(x, 180.0, 240.0, 135.0, 225.0) + } else if (240.0 <= x && x < 300.0) { + map(x, 240.0, 300.0, 225.0, 275.0) + } else { + map(x, 300.0, 360.0, 276.0, 360.0) + } +} + +fun mix(left: ColorXSLUVa, right: ColorXSLUVa, x: Double): ColorXSLUVa { + val sx = x.coerceIn(0.0, 1.0) + return ColorXSLUVa( + mixAngle(left.x, right.x, sx), + (1.0 - sx) * left.s + sx * right.s, + (1.0 - sx) * left.l + sx * right.l, + (1.0 - sx) * left.alpha + sx * right.alpha) +} + +fun ColorRGBa.toXSLUVa() = toHSLUVa().toXSLUVa() diff --git a/orx-jvm/orx-gui/src/main/kotlin/Gui.kt b/orx-jvm/orx-gui/src/main/kotlin/Gui.kt index b4011b20..a3e42ca0 100644 --- a/orx-jvm/orx-gui/src/main/kotlin/Gui.kt +++ b/orx-jvm/orx-gui/src/main/kotlin/Gui.kt @@ -194,7 +194,7 @@ class GUI(val baseColor:ColorRGBa = ColorRGBa.GRAY, val defaultStyles: List @@ -881,7 +881,7 @@ class GUI(val baseColor:ColorRGBa = ColorRGBa.GRAY, val defaultStyles: List { val currentValue = (parameter.property as KMutableProperty1).get(labeledObject.obj) - val randomValue = ColorRGBa(Math.random(), Math.random(), Math.random(), currentValue.a) + val randomValue = ColorRGBa(Math.random(), Math.random(), Math.random(), currentValue.alpha) val newValue = ColorRGBa((1.0 - strength) * currentValue.r + randomValue.r * strength, (1.0 - strength) * currentValue.g + randomValue.g * strength, (1.0 - strength) * currentValue.b + randomValue.b * strength) diff --git a/orx-jvm/orx-keyframer/src/main/kotlin/Keyframer.kt b/orx-jvm/orx-keyframer/src/main/kotlin/Keyframer.kt index 064308a6..110af689 100644 --- a/orx-jvm/orx-keyframer/src/main/kotlin/Keyframer.kt +++ b/orx-jvm/orx-keyframer/src/main/kotlin/Keyframer.kt @@ -86,7 +86,7 @@ open class Keyframer { } inner class RGBaChannel(keys: Array, defaultValue: ColorRGBa = ColorRGBa.WHITE) : - CompoundChannel(keys, arrayOf(defaultValue.r, defaultValue.g, defaultValue.b, defaultValue.a)) { + CompoundChannel(keys, arrayOf(defaultValue.r, defaultValue.g, defaultValue.b, defaultValue.alpha)) { operator fun getValue(keyframer: Keyframer, property: KProperty<*>): ColorRGBa = ColorRGBa(getValue(0), getValue(1), getValue(2), getValue(3)) } diff --git a/orx-jvm/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt b/orx-jvm/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt index 4f293848..7752cfe6 100644 --- a/orx-jvm/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt +++ b/orx-jvm/orx-rabbit-control/src/main/kotlin/RabbitControlServer.kt @@ -217,7 +217,7 @@ class RabbitControlServer(private val showQRUntilClientConnects: Boolean = true, ParameterType.Color -> { val param = rabbitServer.createRGBAParameter(it.label) val c = (it.property as KMutableProperty1).get(objectWithParameters) - param.value = Color(c.r.toFloat(), c.g.toFloat(), c.b.toFloat(), c.a.toFloat()) + param.value = Color(c.r.toFloat(), c.g.toFloat(), c.b.toFloat(), c.alpha.toFloat()) param } ParameterType.Vector2 -> {