[orx-color] Add ColorRGBaExtensions
This commit is contained in:
117
orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt
Normal file
117
orx-color/src/commonMain/kotlin/tools/ColorRGBaExtensions.kt
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package org.openrndr.extra.color.tools
|
||||||
|
|
||||||
|
import org.openrndr.color.*
|
||||||
|
import org.openrndr.extra.color.spaces.*
|
||||||
|
|
||||||
|
inline fun <reified T : AlgebraicColor<T>> ColorRGBa.blendWith(other: ColorRGBa, steps: Int): Sequence<ColorRGBa> {
|
||||||
|
return sequence {
|
||||||
|
for (step in 0 until steps) {
|
||||||
|
yield(mixedWith<T>(other, step / (steps - 1.0)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : AlgebraicColor<T>> ColorRGBa.mixedWith(other: ColorRGBa, factor: Double): ColorRGBa {
|
||||||
|
|
||||||
|
val mixed = when (T::class) {
|
||||||
|
ColorHSLa::class -> this.toHSLa().mix(other.toHSLa(), factor)
|
||||||
|
ColorHSVa::class -> this.toHSVa().mix(other.toHSVa(), factor)
|
||||||
|
ColorRGBa::class -> this.mix(other, factor)
|
||||||
|
|
||||||
|
ColorHSLUVa::class -> this.toHSLUVa().mix(other.toHSLUVa(), factor)
|
||||||
|
ColorOKLABa::class -> this.toOKLABa().mix(other.toOKLABa(), factor)
|
||||||
|
ColorOKLCHa::class -> this.toOKLCHa().mix(other.toOKLCHa(), factor)
|
||||||
|
ColorOKHSLa::class -> this.toOKHSLa().mix(other.toOKHSLa(), factor)
|
||||||
|
ColorOKHSVa::class -> this.toOKHSVa().mix(other.toOKHSVa(), factor)
|
||||||
|
|
||||||
|
ColorLABa::class -> this.toLABa().mix(other.toLABa(), factor)
|
||||||
|
ColorLUVa::class -> this.toLUVa().mix(other.toLUVa(), factor)
|
||||||
|
ColorLCHABa::class -> this.toLCHABa().mix(other.toLCHABa(), factor)
|
||||||
|
ColorLCHUVa::class -> this.toLCHUVa().mix(other.toLCHUVa(), factor)
|
||||||
|
ColorOKHSLa::class -> this.toOKHSLa().mix(other.toOKHSLa(), factor)
|
||||||
|
ColorXYZa::class -> this.toXYZa().mix(other.toXYZa(), factor)
|
||||||
|
ColorXSLUVa::class -> this.toXSLUVa().mix(other.toXSLUVa(), factor)
|
||||||
|
ColorXSVa::class -> this.toXSVa().mix(other.toXSVa(), factor)
|
||||||
|
ColorXSLa::class -> this.toXSLa().mix(other.toXSLa(), factor)
|
||||||
|
else -> error("color model ${T::class} not supported")
|
||||||
|
}.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 this
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> ColorRGBa.saturate(factor: Double): ColorRGBa
|
||||||
|
where T : SaturatableColor<T>,
|
||||||
|
T : ConvertibleToColorRGBa {
|
||||||
|
|
||||||
|
val converted = when (T::class) {
|
||||||
|
ColorHPLUVa::class -> toHPLUVa()
|
||||||
|
ColorHSLUVa::class -> toHSLUVa()
|
||||||
|
ColorHSLa::class -> toHSLa()
|
||||||
|
ColorHSVa::class -> toHSVa()
|
||||||
|
ColorXSLa::class -> toXSLa()
|
||||||
|
ColorXSVa::class -> toXSVa()
|
||||||
|
ColorOKLCHa::class -> toOKLCHa()
|
||||||
|
ColorOKHSLa::class -> toOKHSLa()
|
||||||
|
ColorOKHSVa::class -> toOKHSVa()
|
||||||
|
ColorXSLUVa::class -> toXSLUVa()
|
||||||
|
ColorOKLCHa::class -> toOKLCHa()
|
||||||
|
else -> error("Color space ${T::class} not supported")
|
||||||
|
}
|
||||||
|
val saturated = (converted.saturate(factor) as ConvertibleToColorRGBa).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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> ColorRGBa.shiftHue(degrees: Double): ColorRGBa where
|
||||||
|
T : HueShiftableColor<T>, T : ConvertibleToColorRGBa {
|
||||||
|
val converted = when (T::class) {
|
||||||
|
ColorHSLa::class -> toHSLa()
|
||||||
|
ColorHSVa::class -> toHSVa()
|
||||||
|
ColorXSLa::class -> toXSLa()
|
||||||
|
ColorXSVa::class -> toXSVa()
|
||||||
|
ColorOKLCHa::class -> toOKLCHa()
|
||||||
|
ColorLCHABa::class -> toLCHABa()
|
||||||
|
ColorLCHUVa::class -> toLCHABa()
|
||||||
|
ColorOKHSLa::class -> toOKHSLa()
|
||||||
|
ColorOKHSVa::class -> toOKHSVa()
|
||||||
|
ColorHPLUVa::class -> toHPLUVa()
|
||||||
|
ColorHSLUVa::class -> toHSLUVa()
|
||||||
|
ColorXSLUVa::class -> toXSLUVa()
|
||||||
|
else -> error("Color space ${T::class} not supported")
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package tools
|
||||||
|
|
||||||
|
import org.openrndr.color.*
|
||||||
|
import org.openrndr.extra.color.spaces.*
|
||||||
|
import org.openrndr.extra.color.tools.mixedWith
|
||||||
|
import org.openrndr.extra.color.tools.saturate
|
||||||
|
import org.openrndr.extra.color.tools.shiftHue
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class TestColorRGBaExtensions {
|
||||||
|
@Test
|
||||||
|
fun testShiftHue0() {
|
||||||
|
val seed = ColorRGBa.RED.shade(0.5)
|
||||||
|
val shifted = listOf(
|
||||||
|
seed.shiftHue<LCHab>(0.0),
|
||||||
|
seed.shiftHue<LCHuv>(0.0),
|
||||||
|
seed.shiftHue<OKLCH>(0.0),
|
||||||
|
seed.shiftHue<HSLuv>(0.0)
|
||||||
|
)
|
||||||
|
for (s in shifted) {
|
||||||
|
assertEquals(Linearity.SRGB, s.linearity)
|
||||||
|
assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testShiftHue0Linear() {
|
||||||
|
val seed = ColorRGBa.RED.shade(0.5).toLinear()
|
||||||
|
val shifted = listOf(
|
||||||
|
seed.shiftHue<LCHab>(0.0),
|
||||||
|
seed.shiftHue<LCHuv>(0.0),
|
||||||
|
seed.shiftHue<OKLCH>(0.0),
|
||||||
|
seed.shiftHue<HSLuv>(0.0)
|
||||||
|
)
|
||||||
|
for (s in shifted) {
|
||||||
|
assertEquals(Linearity.LINEAR, s.linearity)
|
||||||
|
assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSaturate1() {
|
||||||
|
val seed = ColorRGBa.RED.shade(0.5)
|
||||||
|
val shifted = listOf(
|
||||||
|
seed.saturate<OKLCH>(1.0),
|
||||||
|
seed.saturate<HSLuv>(1.0)
|
||||||
|
)
|
||||||
|
for (s in shifted) {
|
||||||
|
assertEquals(Linearity.SRGB, s.linearity)
|
||||||
|
assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSaturate1Linear() {
|
||||||
|
val seed = ColorRGBa.RED.shade(0.5).toLinear()
|
||||||
|
val shifted = listOf(
|
||||||
|
seed.saturate<OKLCH>(1.0),
|
||||||
|
seed.saturate<HSLuv>(1.0)
|
||||||
|
)
|
||||||
|
for (s in shifted) {
|
||||||
|
assertEquals(Linearity.LINEAR, s.linearity)
|
||||||
|
assertTrue(seed.toVector4().distanceTo(s.toVector4()) < 1E-4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMixedWith() {
|
||||||
|
val seed = ColorRGBa.RED
|
||||||
|
val mixed = listOf(
|
||||||
|
seed.mixedWith<HSLuv>(ColorRGBa.BLUE, 0.5),
|
||||||
|
seed.mixedWith<OKLab>(ColorRGBa.BLUE, 0.5),
|
||||||
|
seed.mixedWith<OKHSV>(ColorRGBa.BLUE, 0.5)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user