diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt index 315d23e0..7651bc6a 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorHSLUVa.kt @@ -151,20 +151,20 @@ internal fun map(x: Double, a: Double, b: Double, c: Double, d: Double): Double return ((x - a) / (b - a)) * (d - c) + c } -private fun hueToX(hue:Double): Double { - val h = ((hue % 360.0) + 360.0) % 360.0 +fun hueToX(hue:Double): Double { + val h = hue.mod(360.0) return if (0 <= h && h < 35) { - map(h, 0.0, 35.0, 0.0, 60.0) + h.map(0.0, 35.0, 0.0, 60.0) } else if (35 <= h && h < 60) { - map(h, 35.0, 60.0, 60.0, 120.0) + h.map(35.0, 60.0, 60.0, 120.0) } else if (60 <= h && h < 135.0) { - map(h, 60.0, 135.0, 120.0, 180.0) + h.map(60.0, 135.0, 120.0, 180.0) } else if (135.0 <= h && h < 225.0) { - map(h, 135.0, 225.0, 180.0, 240.0) + h.map(135.0, 225.0, 180.0, 240.0) } else if (225.0 <= h && h < 275.0) { - map(h, 225.0, 275.0, 240.0, 300.0) + h.map( 225.0, 275.0, 240.0, 300.0) } else { - map(h, 276.0, 360.0, 300.0, 360.0) + h.map( 275.0, 360.0, 300.0, 360.0) } } diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt index d7920203..b80df8c9 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorXSLUVa.kt @@ -3,6 +3,7 @@ package org.openrndr.extra.color.spaces import kotlinx.serialization.Serializable import org.openrndr.color.* import org.openrndr.math.Vector4 +import org.openrndr.math.map import org.openrndr.math.mixAngle @Serializable @@ -28,9 +29,11 @@ data class ColorXSLUVa(val x: Double, val s: Double, val l: Double, override val 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 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 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) @@ -39,20 +42,20 @@ data class ColorXSLUVa(val x: Double, val s: Double, val l: Double, override val override fun toVector4(): Vector4 = Vector4(x, s, l, alpha) } -private fun xToHue(x:Double) : Double { +fun xToHue(x: Double): Double { @Suppress("NAME_SHADOWING") val x = x.mod(360.0) return if (0.0 <= x && x < 60.0) { - map(x, 0.0, 60.0, 0.0, 35.0) + x.map(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) + x.map(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) + x.map(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) + x.map(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) + x.map(240.0, 300.0, 225.0, 275.0) } else { - map(x, 300.0, 360.0, 276.0, 360.0) + x.map( 300.0, 360.0, 275.0, 360.0) } } @@ -62,7 +65,8 @@ fun mix(left: ColorXSLUVa, right: ColorXSLUVa, x: Double): 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) + (1.0 - sx) * left.alpha + sx * right.alpha + ) } fun ColorRGBa.toXSLUVa() = toHSLUVa().toXSLUVa() diff --git a/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt new file mode 100644 index 00000000..62b77faf --- /dev/null +++ b/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt @@ -0,0 +1,24 @@ +package spaces + +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.color.spaces.toHSLUVa +import kotlin.test.Test +import kotlin.test.assertTrue + +class TestHSLUVa { + + @Test + fun testConversions() { + val testColors = listOf(ColorRGBa.RED, ColorRGBa.BLUE, ColorRGBa.GREEN, ColorRGBa.GRAY, ColorRGBa.YELLOW) + val error = (-1E-3 .. 1E-3) + testColors.forEach { + val testColor = it + val toColor = it.toHSLUVa() + val restoreColor = toColor.toRGBa().toSRGB() + assertTrue("color $testColor, $toColor, $restoreColor") { + testColor.r - restoreColor.r in error && testColor.g - restoreColor.g in error && testColor.b - restoreColor.b in error + } + } + } + +} \ No newline at end of file diff --git a/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt new file mode 100644 index 00000000..bdad7853 --- /dev/null +++ b/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt @@ -0,0 +1,44 @@ +package spaces + +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.color.spaces.hueToX +import org.openrndr.extra.color.spaces.toHSLUVa +import org.openrndr.extra.color.spaces.toXSLUVa +import org.openrndr.extra.color.spaces.xToHue +import kotlin.math.abs +import kotlin.test.Test +import kotlin.test.assertTrue + +class TestXSLUVa { + + @Test + fun testHueConversions() { + for (i in 0 until 3600) { + val inputHue = i/10.0 + val x = hueToX(inputHue) + val recoveredHue = xToHue(x) + assertTrue( abs(recoveredHue-inputHue) < 1E-8, "$inputHue $recoveredHue") + } + } + + @Test + fun testConversions() { + val testColors = listOf(ColorRGBa.RED, ColorRGBa.BLUE, ColorRGBa.GREEN, ColorRGBa.GRAY, ColorRGBa.YELLOW) + val error = (-1E-3..1E-3) + testColors.forEach { + val testColor = it + val hsluvColor = it.toHSLUVa() + val xsluvColor = it.toXSLUVa() + + val restoredHsluvColor = xsluvColor.toHSLUVa() + + val dh = restoredHsluvColor.h - hsluvColor.h + val dl = restoredHsluvColor.l - hsluvColor.l + val ds = restoredHsluvColor.s - hsluvColor.s + + assertTrue(abs(dh) < 1E-7) + + } + } + +} \ No newline at end of file