From 6e294234481156d3c416ff02192749e9a7043bf8 Mon Sep 17 00:00:00 2001 From: Abe Pazos Date: Mon, 28 Feb 2022 21:00:25 +0100 Subject: [PATCH] OKLab color gradients were partially implemented (#230) in RadialGradient.kt and NPointLinearGradient.kt Add OKLab variation to DemoNPointLinearGradient.kt Mention OKLab (l, a, b) ranges and meaning. --- .../commonMain/kotlin/spaces/ColorOKLABa.kt | 5 +- .../commonMain/kotlin/NPointLinearGradient.kt | 8 ++- .../src/commonMain/kotlin/RadialGradient.kt | 6 ++ .../demo/kotlin/DemoNPointLinearGradient01.kt | 57 ++++++++++++------- 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt b/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt index b036af2e..c12d93af 100644 --- a/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt +++ b/orx-color/src/commonMain/kotlin/spaces/ColorOKLABa.kt @@ -6,7 +6,10 @@ import org.openrndr.math.Vector4 import kotlin.math.pow /** - * Color in OKLab color space + * Color in OKLab color space. + * [l] = lightness: black (0.0) to white (1.0), + * [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, diff --git a/orx-shade-styles/src/commonMain/kotlin/NPointLinearGradient.kt b/orx-shade-styles/src/commonMain/kotlin/NPointLinearGradient.kt index 39d5af11..1ab26e34 100644 --- a/orx-shade-styles/src/commonMain/kotlin/NPointLinearGradient.kt +++ b/orx-shade-styles/src/commonMain/kotlin/NPointLinearGradient.kt @@ -5,6 +5,8 @@ import org.openrndr.color.ColorRGBa import org.openrndr.color.ConvertibleToColorRGBa import org.openrndr.draw.ShadeStyle import org.openrndr.extra.parameters.Description +import org.openrndr.extra.shaderphrases.preprocess +import org.openrndr.extras.color.phrases.ColorPhraseBook import org.openrndr.extra.color.spaces.ColorOKLABa import org.openrndr.math.CastableToVector4 import org.openrndr.math.Vector2 @@ -16,7 +18,7 @@ open class NPointLinearGradientBase( offset: Vector2 = Vector2.ZERO, rotation: Double = 0.0 ) : ShadeStyle() - where C : ConvertibleToColorRGBa, C : AlgebraicColor, C : CastableToVector4 { + where C : ConvertibleToColorRGBa, C : AlgebraicColor, C: CastableToVector4 { var colors: Array by Parameter() @@ -26,11 +28,15 @@ open class NPointLinearGradientBase( var rotation: Double by Parameter() init { + ColorPhraseBook.register() this.colors = colors this.points = points this.offset = offset this.rotation = rotation + fragmentPreamble = """ + |#pragma import color.oklab_to_linear_rgb + |#pragma import color.linear_rgb_to_srgb""".trimMargin().preprocess() fragmentTransform = """ vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset); diff --git a/orx-shade-styles/src/commonMain/kotlin/RadialGradient.kt b/orx-shade-styles/src/commonMain/kotlin/RadialGradient.kt index e0e6e8ac..eac047c3 100644 --- a/orx-shade-styles/src/commonMain/kotlin/RadialGradient.kt +++ b/orx-shade-styles/src/commonMain/kotlin/RadialGradient.kt @@ -6,6 +6,8 @@ import org.openrndr.draw.shadeStyle import org.openrndr.extra.parameters.ColorParameter import org.openrndr.extra.parameters.Description import org.openrndr.extra.parameters.DoubleParameter +import org.openrndr.extra.shaderphrases.preprocess +import org.openrndr.extras.color.phrases.ColorPhraseBook import org.openrndr.extra.color.spaces.ColorOKLABa import org.openrndr.math.CastableToVector4 import org.openrndr.math.Vector2 @@ -33,6 +35,7 @@ where C : ConvertibleToColorRGBa, C : AlgebraicColor, C: CastableToVector4 { var exponent: Double by Parameter() init { + ColorPhraseBook.register() this.color0 = color0 this.color1 = color1 this.offset = offset @@ -40,6 +43,9 @@ where C : ConvertibleToColorRGBa, C : AlgebraicColor, C: CastableToVector4 { this.length = length this.exponent = exponent + fragmentPreamble = """ + |#pragma import color.oklab_to_linear_rgb + |#pragma import color.linear_rgb_to_srgb""".trimMargin().preprocess() fragmentTransform = """ vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset/2.0) * 2.0; diff --git a/orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt b/orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt index a9f7bf22..b159f661 100644 --- a/orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt +++ b/orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt @@ -1,15 +1,15 @@ import org.openrndr.application -import org.openrndr.color.ColorRGBa import org.openrndr.color.ColorXSVa import org.openrndr.color.rgb -import org.openrndr.extensions.SingleScreenshot +import org.openrndr.extra.color.spaces.toOKLABa import org.openrndr.extra.shadestyles.NPointLinearGradient +import org.openrndr.extra.shadestyles.NPointLinearGradientOKLab import org.openrndr.shape.Rectangle import kotlin.math.pow import kotlin.math.sin /** - * Demonstrate using a multi color linear gradient. + * Demonstrate using a multicolor linear gradient. * The gradient has 8 static saturated colors. * The positions of the colors are first distributed * uniformly between 0.0 and 1.0 and then animated towards one of @@ -19,33 +19,46 @@ fun main() { application { program { val numPoints = 8 - val gradient = NPointLinearGradient(Array(numPoints) { - ColorXSVa(it * 360.0 / numPoints, 1.0, 1.0).toRGBa() - }) + // Create gradients using two different color spaces + val gradients = listOf( + NPointLinearGradient(Array(numPoints) { + ColorXSVa(it * 360.0 / numPoints, 1.0, 1.0).toRGBa() + }), + // OKLab is better at maintaining luminosity across the gradient + NPointLinearGradientOKLab(Array(numPoints) { + ColorXSVa(it * 360.0 / numPoints, 1.0, 1.0).toRGBa() + .toOKLABa() + }) + ) extend { + // The points should be sorted values between 0.0 and 1.0 + val distribution = Array(numPoints) { + // uniform distribution + // (it / (numPoints - 1.0)) + + // skewed and animated distribution + (it / (numPoints - 1.0)).pow(1.0 + 0.5 * sin(seconds)) + } drawer.run { clear(rgb(0.2)) - // The points should be sorted values between 0.0 and 1.0 - gradient.points = Array(numPoints) { - // uniform distribution - // (it / (numPoints - 1.0)) - - // skewed and animated distribution - (it / (numPoints - 1.0)).pow(1.0 + 0.5 * sin(seconds)) - } - gradient.rotation = seconds * 10 - shadeStyle = gradient stroke = rgb(0.35) - fill = ColorRGBa.WHITE strokeWeight = 8.0 - gradient.rotation = seconds * 10 - circle(bounds.position(0.34, 0.5), 110.0) + gradients.forEachIndexed { i, gradient -> + shadeStyle = gradient + gradient.points = distribution - gradient.rotation += 90 - rectangle(Rectangle.fromCenter( - bounds.position(0.655, 0.5), 200.0, 200.0)) + gradient.rotation = seconds * 10 + circle(bounds.position(0.34, 0.29 + 0.44 * i), 110.0) + + gradient.rotation += 90 + rectangle( + Rectangle.fromCenter( + bounds.position(0.655, 0.29 + 0.44 * i), 200.0 + ) + ) + } } } }