diff --git a/.gitignore b/.gitignore index 58b9fbd7..88ae9a95 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ out/ target/ build/ *.iml/ -.idea/ gradle.properties /hs_err_pid*.log /gui-parameters/ /ShaderError.glsl +/.kotlin diff --git a/orx-color/src/commonMain/kotlin/colormaps/ColormapPhrases.kt b/orx-color/src/commonMain/kotlin/colormaps/ColormapPhrases.kt new file mode 100644 index 00000000..92603d45 --- /dev/null +++ b/orx-color/src/commonMain/kotlin/colormaps/ColormapPhrases.kt @@ -0,0 +1,91 @@ +package org.openrndr.extra.color.colormaps + +import org.openrndr.extra.shaderphrases.ShaderPhrase +import org.openrndr.extra.shaderphrases.ShaderPhraseBook + +/** + * Colormaps represent a class of functions taking a value in the range `0.0..1.0` and returning particular RGB color + * values. Colormaps can be used in data visualization for representing additional information/dimension of the data + * e.g: + * - depth + * - elevation + * - heat + * + * Note: the [ShaderPhrase] GLSL functions gathered in this [ShaderPhraseBook] also have respective Kotlin + * implementations. + * + * @see org.openrndr.extra.color.colormaps.turboColormap + * @see org.openrndr.extra.color.colormaps.spectralZucconi6 + */ +object ColormapPhraseBook : ShaderPhraseBook("colormap") { + + // Copyright 2019 Google LLC. + // SPDX-License-Identifier: Apache-2.0 + /** + * Polynomial approximation in GLSL for the Turbo colormap. + * + * See [Turbo, An Improved Rainbow Colormap for Visualization](https://research.google/blog/turbo-an-improved-rainbow-colormap-for-visualization/), + * [the source of this code](https://gist.github.com/mikhailov-work/0d177465a8151eb6ede1768d51d476c7), + * + * @author Anton Mikhailov (mikhailov@google.com) - Colormap Design + * @author Ruofei Du (ruofei@google.com) - GLSL Approximation + * @see org.openrndr.extra.color.colormaps.turboColormap + */ + val turboColormap = ShaderPhrase(""" + |vec3 turbo_colormap(in float x) { + | const vec4 kRedVec4 = vec4(0.13572138, 4.61539260, -42.66032258, 132.13108234); + | const vec4 kGreenVec4 = vec4(0.09140261, 2.19418839, 4.84296658, -14.18503333); + | const vec4 kBlueVec4 = vec4(0.10667330, 12.64194608, -60.58204836, 110.36276771); + | const vec2 kRedVec2 = vec2(-152.94239396, 59.28637943); + | const vec2 kGreenVec2 = vec2(4.27729857, 2.82956604); + | const vec2 kBlueVec2 = vec2(-89.90310912, 27.34824973); + | + | x = clamp(x, 0.0, 1.0); + | vec4 v4 = vec4( 1.0, x, x * x, x * x * x); + | vec2 v2 = v4.zw * v4.z; + | return vec3( + | dot(v4, kRedVec4) + dot(v2, kRedVec2), + | dot(v4, kGreenVec4) + dot(v2, kGreenVec2), + | dot(v4, kBlueVec4) + dot(v2, kBlueVec2) + | ); + |}""".trimMargin()) + + /** + * Accurate spectral colormap developed by Alan Zucconi. + * + * See [Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) article, + * [the source of this code](https://www.shadertoy.com/view/ls2Bz1) + * + * @author Alan Zucconi + * @see org.openrndr.extra.color.colormaps.spectralZucconi6 + */ + val spectralZucconi6 = ShaderPhrase(""" + |#pragma import colormap.bump3y + | + |vec3 spectral_zucconi6(in float x) { + | + | const vec3 c1 = vec3(3.54585104, 2.93225262, 2.41593945); + | const vec3 x1 = vec3(0.69549072, 0.49228336, 0.27699880); + | const vec3 y1 = vec3(0.02312639, 0.15225084, 0.52607955); + | + | const vec3 c2 = vec3(3.90307140, 3.21182957, 3.96587128); + | const vec3 x2 = vec3(0.11748627, 0.86755042, 0.66077860); + | const vec3 y2 = vec3(0.84897130, 0.88445281, 0.73949448); + | + | return + | bump3y(c1 * (x - x1), y1) + + | bump3y(c2 * (x - x2), y2) ; + |}""".trimMargin()) + + /** + * A function used internally by [spectralZucconi6]. + * + * @author Alan Zucconi + */ + val bump3y = ShaderPhrase(""" + |vec3 bump3y(in vec3 x, in vec3 yoffset) { + | vec3 y = vec3(1.0) - x * x; + | return clamp(y - yoffset, vec3(0.0), vec3(1.0)); + |}""".trimMargin()) + +} diff --git a/orx-color/src/commonMain/kotlin/colormaps/SpectralZucconiColormap.kt b/orx-color/src/commonMain/kotlin/colormaps/SpectralZucconiColormap.kt new file mode 100644 index 00000000..38698264 --- /dev/null +++ b/orx-color/src/commonMain/kotlin/colormaps/SpectralZucconiColormap.kt @@ -0,0 +1,40 @@ +package org.openrndr.extra.color.colormaps + +import org.openrndr.color.ColorRGBa +import org.openrndr.math.Vector3 +import org.openrndr.math.saturate + +/** + * Accurate spectral colormap developed by Alan Zucconi. + * + * @see spectralZucconi6Vector + * @see ColormapPhraseBook.spectralZucconi6 + */ +fun spectralZucconi6( + x: Double +): ColorRGBa = ColorRGBa.fromVector( + spectralZucconi6Vector(x) +) + +/** + * Accurate spectral colormap developed by Alan Zucconi. + * + * @see ColormapPhraseBook.spectralZucconi6 + */ +fun spectralZucconi6Vector(x: Double): Vector3 { + val v = Vector3(x) + return bump3y(c1 * (v - x1), y1) + bump3y(c2 * (v - x2), y2) +} + +private fun bump3y( + x: Vector3, + yOffset: Vector3 +) = (Vector3.ONE - x * x - yOffset).saturate() + +private val c1 = Vector3(3.54585104, 2.93225262, 2.41593945) +private val x1 = Vector3(0.69549072, 0.49228336, 0.27699880) +private val y1 = Vector3(0.02312639, 0.15225084, 0.52607955) + +private val c2 = Vector3(3.90307140, 3.21182957, 3.96587128) +private val x2 = Vector3(0.11748627, 0.86755042, 0.66077860) +private val y2 = Vector3(0.84897130, 0.88445281, 0.73949448) diff --git a/orx-color/src/commonMain/kotlin/colormaps/TurboColormap.kt b/orx-color/src/commonMain/kotlin/colormaps/TurboColormap.kt new file mode 100644 index 00000000..af423c79 --- /dev/null +++ b/orx-color/src/commonMain/kotlin/colormaps/TurboColormap.kt @@ -0,0 +1,39 @@ +package org.openrndr.extra.color.colormaps + +import org.openrndr.color.ColorRGBa +import org.openrndr.math.* + +/** + * Polynomial approximation in GLSL for the Turbo colormap. + * + * @see turboColormapVector + * @see ColormapPhraseBook.turboColormap + */ +fun turboColormap( + x: Double +): ColorRGBa = ColorRGBa.fromVector( + turboColormapVector(x) +) + +/** + * Polynomial approximation in GLSL for the Turbo colormap. + * + * @see ColormapPhraseBook.turboColormap + */ +fun turboColormapVector(x: Double): Vector3 { + val v = saturate(x) + val v4 = Vector4( 1.0, v, v * v, v * v * v) + val v2 = Vector2(v4.z, v4.w) * v4.z + return Vector3( + v4.dot(kRedVec4) + v2.dot(kRedVec2), + v4.dot(kGreenVec4) + v2.dot(kGreenVec2), + v4.dot(kBlueVec4) + v2.dot(kBlueVec2) + ) +} + +private val kRedVec4 = Vector4(0.13572138, 4.61539260, -42.66032258, 132.13108234) +private val kGreenVec4 = Vector4(0.09140261, 2.19418839, 4.84296658, -14.18503333) +private val kBlueVec4 = Vector4(0.10667330, 12.64194608, -60.58204836, 110.36276771) +private val kRedVec2 = Vector2(-152.94239396, 59.28637943) +private val kGreenVec2 = Vector2(4.27729857, 2.82956604) +private val kBlueVec2 = Vector2(-89.90310912, 27.34824973) diff --git a/orx-color/src/commonTest/kotlin/ColorTest.kt b/orx-color/src/commonTest/kotlin/ColorTest.kt new file mode 100644 index 00000000..91eaf885 --- /dev/null +++ b/orx-color/src/commonTest/kotlin/ColorTest.kt @@ -0,0 +1,5 @@ +// keeping this file here will stop IntelliJ from showing warning in nested relative packages +/** + * orx-color + */ +package org.openrndr.extra.color diff --git a/orx-color/src/commonTest/kotlin/colormaps/TestSpectralZucconi6Colormap.kt b/orx-color/src/commonTest/kotlin/colormaps/TestSpectralZucconi6Colormap.kt new file mode 100644 index 00000000..fecf3a92 --- /dev/null +++ b/orx-color/src/commonTest/kotlin/colormaps/TestSpectralZucconi6Colormap.kt @@ -0,0 +1,29 @@ +package org.openrndr.extra.color.colormaps + +import io.kotest.matchers.shouldBe +import org.openrndr.color.ColorRGBa +import org.openrndr.color.Linearity +import org.openrndr.math.Vector3 +import kotlin.test.Test + +class TestSpectralZucconi6Colormap { + + @Test + fun testSpectralZucconi6Vector() { + spectralZucconi6Vector(0.0) shouldBe Vector3(0.0, 0.0, 0.026075309353279508) + spectralZucconi6Vector(0.5) shouldBe Vector3(0.49637374891706215, 0.8472371726323733, 0.18366091774095827) + spectralZucconi6Vector(1.0) shouldBe Vector3(0.0, 0.0, 0.0) + spectralZucconi6Vector(-0.1) shouldBe Vector3(0.0, 0.0, 0.0) + spectralZucconi6Vector(1.1) shouldBe Vector3(0.0, 0.0, 0.0) + } + + @Test + fun testSpectralZucconi6() { + spectralZucconi6(0.0) shouldBe ColorRGBa(0.0, 0.0, 0.026075309353279508, linearity = Linearity.SRGB) + spectralZucconi6(0.5) shouldBe ColorRGBa(0.49637374891706215, 0.8472371726323733, 0.18366091774095827, linearity = Linearity.SRGB) + spectralZucconi6(1.0) shouldBe ColorRGBa(0.0, 0.0, 0.0, linearity = Linearity.SRGB) + spectralZucconi6(-0.1) shouldBe ColorRGBa(0.0, 0.0, 0.0, linearity = Linearity.SRGB) + spectralZucconi6(1.1) shouldBe ColorRGBa(0.0, 0.0, 0.0, linearity = Linearity.SRGB) + } + +} diff --git a/orx-color/src/commonTest/kotlin/colormaps/TestTurboColormap.kt b/orx-color/src/commonTest/kotlin/colormaps/TestTurboColormap.kt new file mode 100644 index 00000000..3673b00a --- /dev/null +++ b/orx-color/src/commonTest/kotlin/colormaps/TestTurboColormap.kt @@ -0,0 +1,29 @@ +package org.openrndr.extra.color.colormaps + +import io.kotest.matchers.shouldBe +import org.openrndr.color.ColorRGBa +import org.openrndr.color.Linearity +import org.openrndr.math.Vector3 +import kotlin.test.Test + +class TestTurboColormap { + + @Test + fun testTurboColormapVector() { + turboColormapVector(0.0) shouldBe Vector3(0.13572138, 0.09140261, 0.1066733) + turboColormapVector(0.5) shouldBe Vector3(0.5885220621875007, 0.981864383125, 0.31316869781249856) + turboColormapVector(1.0) shouldBe Vector3(0.5658592099999993, 0.05038885999999998, -0.025520659999997974) + turboColormapVector(-0.1) shouldBe Vector3(0.13572138, 0.09140261, 0.1066733) + turboColormapVector(1.1) shouldBe Vector3(0.5658592099999993, 0.05038885999999998, -0.025520659999997974) + } + + @Test + fun testTurboColormap() { + turboColormap(0.0) shouldBe ColorRGBa(0.13572138, 0.09140261, 0.1066733, linearity = Linearity.SRGB) + turboColormap(0.5) shouldBe ColorRGBa(0.5885220621875007, 0.981864383125, 0.31316869781249856, linearity = Linearity.SRGB) + turboColormap(1.0) shouldBe ColorRGBa(0.5658592099999993, 0.05038885999999998, -0.025520659999997974, linearity = Linearity.SRGB) + turboColormap(-0.1) shouldBe ColorRGBa(0.13572138, 0.09140261, 0.1066733, linearity = Linearity.SRGB) + turboColormap(1.1) shouldBe ColorRGBa(0.5658592099999993, 0.05038885999999998, -0.025520659999997974, linearity = Linearity.SRGB) + } + +} diff --git a/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt index 62b77faf..264087b3 100644 --- a/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt +++ b/orx-color/src/commonTest/kotlin/spaces/TestHSLUVa.kt @@ -1,7 +1,6 @@ -package spaces +package org.openrndr.extra.color.spaces import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.toHSLUVa import kotlin.test.Test import kotlin.test.assertTrue diff --git a/orx-color/src/commonTest/kotlin/spaces/TestOKHSLa.kt b/orx-color/src/commonTest/kotlin/spaces/TestOKHSLa.kt index 80bfda14..ff65c7e6 100644 --- a/orx-color/src/commonTest/kotlin/spaces/TestOKHSLa.kt +++ b/orx-color/src/commonTest/kotlin/spaces/TestOKHSLa.kt @@ -1,7 +1,6 @@ -package spaces +package org.openrndr.extra.color.spaces import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.toOKHSLa import kotlin.test.Test import kotlin.test.assertTrue diff --git a/orx-color/src/commonTest/kotlin/spaces/TestOKHSVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestOKHSVa.kt index e9244348..95629126 100644 --- a/orx-color/src/commonTest/kotlin/spaces/TestOKHSVa.kt +++ b/orx-color/src/commonTest/kotlin/spaces/TestOKHSVa.kt @@ -1,8 +1,6 @@ -package spaces +package org.openrndr.extra.color.spaces import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.toOKHSLa -import org.openrndr.extra.color.spaces.toOKHSVa import kotlin.test.Test import kotlin.test.assertTrue diff --git a/orx-color/src/commonTest/kotlin/spaces/TestOKLCHa.kt b/orx-color/src/commonTest/kotlin/spaces/TestOKLCHa.kt index 6fd6bb34..186564c3 100644 --- a/orx-color/src/commonTest/kotlin/spaces/TestOKLCHa.kt +++ b/orx-color/src/commonTest/kotlin/spaces/TestOKLCHa.kt @@ -1,8 +1,6 @@ -package spaces +package org.openrndr.extra.color.spaces import org.openrndr.color.ColorRGBa -import org.openrndr.extra.color.spaces.OKLCH -import org.openrndr.extra.color.spaces.toOKLCHa import org.openrndr.extra.color.tools.chroma import org.openrndr.extra.color.tools.withLuminosity import kotlin.test.Test diff --git a/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt b/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt index bdad7853..c95d95b2 100644 --- a/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt +++ b/orx-color/src/commonTest/kotlin/spaces/TestXSLUVa.kt @@ -1,10 +1,6 @@ -package spaces +package org.openrndr.extra.color.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 diff --git a/orx-color/src/commonTest/kotlin/tools/TestChromaColorExtensions.kt b/orx-color/src/commonTest/kotlin/tools/TestChromaColorExtensions.kt index 9ac07291..944557ab 100644 --- a/orx-color/src/commonTest/kotlin/tools/TestChromaColorExtensions.kt +++ b/orx-color/src/commonTest/kotlin/tools/TestChromaColorExtensions.kt @@ -1,9 +1,7 @@ -package tools +package org.openrndr.extra.color.tools import org.openrndr.color.ColorRGBa import org.openrndr.extra.color.spaces.toOKLCHa -import org.openrndr.extra.color.tools.findMaxChroma -import org.openrndr.extra.color.tools.isOutOfGamut import kotlin.test.Test class TestChromaColorExtensions { diff --git a/orx-color/src/commonTest/kotlin/tools/TestColorRGBaExtensions.kt b/orx-color/src/commonTest/kotlin/tools/TestColorRGBaExtensions.kt index b15d1ba4..be5f6cc1 100644 --- a/orx-color/src/commonTest/kotlin/tools/TestColorRGBaExtensions.kt +++ b/orx-color/src/commonTest/kotlin/tools/TestColorRGBaExtensions.kt @@ -1,10 +1,7 @@ -package tools +package org.openrndr.extra.color.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 diff --git a/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormap.kt b/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormap.kt new file mode 100644 index 00000000..cf2cc372 --- /dev/null +++ b/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormap.kt @@ -0,0 +1,22 @@ +import org.openrndr.application +import org.openrndr.extra.color.colormaps.spectralZucconi6 +import org.openrndr.extra.noise.fastFloor +import kotlin.math.sin + +fun main() = application { + program { + extend { + drawer.stroke = null + val stripeCount = 32 + (sin(seconds) * 16.0).fastFloor() + repeat(stripeCount) { i -> + drawer.fill = spectralZucconi6(i / stripeCount.toDouble()) + drawer.rectangle( + x = i * width / stripeCount.toDouble(), + y = 0.0, + width = width / stripeCount.toDouble(), + height = height.toDouble(), + ) + } + } + } +} diff --git a/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormapPhrase.kt b/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormapPhrase.kt new file mode 100644 index 00000000..df98801e --- /dev/null +++ b/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormapPhrase.kt @@ -0,0 +1,20 @@ +import org.openrndr.application +import org.openrndr.draw.shadeStyle +import org.openrndr.extra.color.colormaps.ColormapPhraseBook +import org.openrndr.extra.shaderphrases.preprocess + +fun main() = application { + program { + ColormapPhraseBook.register() + val style = shadeStyle { + fragmentPreamble = "#pragma import colormap.spectral_zucconi6".preprocess() + fragmentTransform = "x_fill.rgb = spectral_zucconi6(c_boundsPosition.x);" + } + extend { + drawer.run { + shadeStyle = style + rectangle(bounds) + } + } + } +} diff --git a/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormapPlot.kt b/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormapPlot.kt new file mode 100644 index 00000000..58d4e145 --- /dev/null +++ b/orx-color/src/jvmDemo/kotlin/DemoSpectralZucconiColormapPlot.kt @@ -0,0 +1,44 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.shadeStyle +import org.openrndr.extra.color.colormaps.ColormapPhraseBook +import org.openrndr.extra.color.colormaps.spectralZucconi6 +import org.openrndr.extra.shaderphrases.preprocess +import org.openrndr.math.Vector2 + +fun main() = application { + program { + ColormapPhraseBook.register() + val backgroundStyle = shadeStyle { + fragmentPreamble = "#pragma import colormap.spectral_zucconi6".preprocess() + fragmentTransform = "x_fill.rgb = spectral_zucconi6(c_boundsPosition.x);" + } + fun getColormapPoints( + block: ColorRGBa.() -> Double + ) = List(width) { x -> + Vector2( + x = x.toDouble(), + y = height.toDouble() + - block(spectralZucconi6(x / width.toDouble())) + * height.toDouble() + ) + } + val redPoints = getColormapPoints { r } + val greenPoints = getColormapPoints { g } + val bluePoints = getColormapPoints { b } + extend { + drawer.run { + shadeStyle = backgroundStyle + rectangle(bounds) + shadeStyle = null + strokeWeight = 1.0 + stroke = ColorRGBa.RED + lineStrip(redPoints) + stroke = ColorRGBa.GREEN + lineStrip(greenPoints) + stroke = ColorRGBa.BLUE + lineStrip(bluePoints) + } + } + } +} diff --git a/orx-color/src/jvmDemo/kotlin/DemoTurboColormap.kt b/orx-color/src/jvmDemo/kotlin/DemoTurboColormap.kt new file mode 100644 index 00000000..4417fdd2 --- /dev/null +++ b/orx-color/src/jvmDemo/kotlin/DemoTurboColormap.kt @@ -0,0 +1,22 @@ +import org.openrndr.application +import org.openrndr.extra.color.colormaps.turboColormap +import org.openrndr.extra.noise.fastFloor +import kotlin.math.sin + +fun main() = application { + program { + extend { + drawer.stroke = null + val stripeCount = 32 + (sin(seconds) * 16.0).fastFloor() + repeat(stripeCount) { i -> + drawer.fill = turboColormap(i / stripeCount.toDouble()) + drawer.rectangle( + x = i * width / stripeCount.toDouble(), + y = 0.0, + width = width / stripeCount.toDouble(), + height = height.toDouble(), + ) + } + } + } +} diff --git a/orx-color/src/jvmDemo/kotlin/DemoTurboColormapPhrase.kt b/orx-color/src/jvmDemo/kotlin/DemoTurboColormapPhrase.kt new file mode 100644 index 00000000..44ee5512 --- /dev/null +++ b/orx-color/src/jvmDemo/kotlin/DemoTurboColormapPhrase.kt @@ -0,0 +1,20 @@ +import org.openrndr.application +import org.openrndr.draw.shadeStyle +import org.openrndr.extra.color.colormaps.ColormapPhraseBook +import org.openrndr.extra.shaderphrases.preprocess + +fun main() = application { + program { + ColormapPhraseBook.register() + val style = shadeStyle { + fragmentPreamble = "#pragma import colormap.turbo_colormap".preprocess() + fragmentTransform = "x_fill.rgb = turbo_colormap(c_boundsPosition.x);" + } + extend { + drawer.run { + shadeStyle = style + rectangle(bounds) + } + } + } +} diff --git a/orx-color/src/jvmDemo/kotlin/DemoTurboColormapPlot.kt b/orx-color/src/jvmDemo/kotlin/DemoTurboColormapPlot.kt new file mode 100644 index 00000000..8862573c --- /dev/null +++ b/orx-color/src/jvmDemo/kotlin/DemoTurboColormapPlot.kt @@ -0,0 +1,44 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.shadeStyle +import org.openrndr.extra.color.colormaps.ColormapPhraseBook +import org.openrndr.extra.color.colormaps.turboColormap +import org.openrndr.extra.shaderphrases.preprocess +import org.openrndr.math.Vector2 + +fun main() = application { + program { + ColormapPhraseBook.register() + val backgroundStyle = shadeStyle { + fragmentPreamble = "#pragma import colormap.turbo_colormap".preprocess() + fragmentTransform = "x_fill.rgb = turbo_colormap(c_boundsPosition.x);" + } + fun getColormapPoints( + block: ColorRGBa.() -> Double + ) = List(width) { x -> + Vector2( + x = x.toDouble(), + y = height.toDouble() + - block(turboColormap(x / width.toDouble())) + * height.toDouble() + ) + } + val redPoints = getColormapPoints { r } + val greenPoints = getColormapPoints { g } + val bluePoints = getColormapPoints { b } + extend { + drawer.run { + shadeStyle = backgroundStyle + rectangle(bounds) + shadeStyle = null + strokeWeight = 1.0 + stroke = ColorRGBa.RED + lineStrip(redPoints) + stroke = ColorRGBa.GREEN + lineStrip(greenPoints) + stroke = ColorRGBa.BLUE + lineStrip(bluePoints) + } + } + } +} diff --git a/orx-fx/src/commonMain/kotlin/colormap/ColormapFilter.kt b/orx-fx/src/commonMain/kotlin/colormap/ColormapFilter.kt index 80e29827..1600905e 100644 --- a/orx-fx/src/commonMain/kotlin/colormap/ColormapFilter.kt +++ b/orx-fx/src/commonMain/kotlin/colormap/ColormapFilter.kt @@ -2,7 +2,6 @@ package org.openrndr.extra.fx.colormap -import org.openrndr.draw.Filter import org.openrndr.draw.Filter1to1 import org.openrndr.extra.fx.mppFilterShader import org.openrndr.extra.parameters.DoubleParameter diff --git a/orx-fx/src/commonMain/kotlin/colormap/SpectralZucconiColormap.kt b/orx-fx/src/commonMain/kotlin/colormap/SpectralZucconiColormap.kt index 4343f526..81fd81ee 100644 --- a/orx-fx/src/commonMain/kotlin/colormap/SpectralZucconiColormap.kt +++ b/orx-fx/src/commonMain/kotlin/colormap/SpectralZucconiColormap.kt @@ -2,14 +2,25 @@ package org.openrndr.extra.fx.colormap +import org.openrndr.extra.color.colormaps.ColormapPhraseBook import org.openrndr.extra.fx.fx_spectral_zucconi_colormap import org.openrndr.extra.parameters.Description +import org.openrndr.extra.shaderphrases.preprocess /** * Maps values of the RED color channel to natural light dispersion spectrum as described * by Alan Zucconi in the * [Improving the Rainbow](https://www.alanzucconi.com/2017/07/15/improving-the-rainbow/) * article. + * + * @see ColormapPhraseBook.spectralZucconi6 + * @see org.openrndr.extra.color.colormaps.spectralZucconi6 */ @Description("spectral colormap") -class SpectralZucconiColormap : ColormapFilter(fx_spectral_zucconi_colormap, "spectral-zucconi-colormap") +class SpectralZucconiColormap : ColormapFilter( + code = run { + ColormapPhraseBook.register() + fx_spectral_zucconi_colormap.preprocess() + }, + name = "spectral-zucconi-colormap" +) diff --git a/orx-fx/src/commonMain/kotlin/colormap/TurboColormap.kt b/orx-fx/src/commonMain/kotlin/colormap/TurboColormap.kt index 5581fba1..771e5677 100644 --- a/orx-fx/src/commonMain/kotlin/colormap/TurboColormap.kt +++ b/orx-fx/src/commonMain/kotlin/colormap/TurboColormap.kt @@ -2,13 +2,24 @@ package org.openrndr.extra.fx.colormap +import org.openrndr.extra.color.colormaps.ColormapPhraseBook import org.openrndr.extra.fx.fx_turbo_colormap import org.openrndr.extra.parameters.Description +import org.openrndr.extra.shaderphrases.preprocess /** * Maps values of the RED color channel to Turbo Colormap according to * [Turbo, An Improved Rainbow Colormap for Visualization](https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html) * by Google. + * + * @see ColormapPhraseBook.spectralZucconi6 + * @see org.openrndr.extra.color.colormaps.spectralZucconi6 */ @Description("turbo colormap") -open class TurboColormap : ColormapFilter(fx_turbo_colormap, "turbo-colormap") +class TurboColormap : ColormapFilter( + code = run { + ColormapPhraseBook.register() + fx_turbo_colormap.preprocess() + }, + name = "turbo-colormap" +) diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColormapGrayscale.kt b/orx-fx/src/jvmDemo/kotlin/DemoColormapGrayscale.kt new file mode 100644 index 00000000..b8da8cd5 --- /dev/null +++ b/orx-fx/src/jvmDemo/kotlin/DemoColormapGrayscale.kt @@ -0,0 +1,21 @@ +import org.openrndr.application +import org.openrndr.draw.ColorType +import org.openrndr.draw.createEquivalent +import org.openrndr.draw.loadImage +import org.openrndr.extra.fx.colormap.GrayscaleColormap +import kotlin.math.sin + +fun main() { + application { + program { + val colormap = GrayscaleColormap() + val image = loadImage("demo-data/images/image-001.png") + val colormapImage = image.createEquivalent(type = ColorType.FLOAT32) + extend { + colormap.curve = 1.0 + sin(seconds) * .5 + colormap.apply(image, colormapImage) + drawer.image(colormapImage) + } + } + } +} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColormapSpectralZucconi.kt b/orx-fx/src/jvmDemo/kotlin/DemoColormapSpectralZucconi.kt new file mode 100644 index 00000000..4af42dad --- /dev/null +++ b/orx-fx/src/jvmDemo/kotlin/DemoColormapSpectralZucconi.kt @@ -0,0 +1,21 @@ +import org.openrndr.application +import org.openrndr.draw.ColorType +import org.openrndr.draw.createEquivalent +import org.openrndr.draw.loadImage +import org.openrndr.extra.fx.colormap.SpectralZucconiColormap +import kotlin.math.sin + +fun main() { + application { + program { + val colormap = SpectralZucconiColormap() + val image = loadImage("demo-data/images/image-001.png") + val colormapImage = image.createEquivalent(type = ColorType.FLOAT32) + extend { + colormap.curve = 1.0 + sin(seconds) * .5 + colormap.apply(image, colormapImage) + drawer.image(colormapImage) + } + } + } +} \ No newline at end of file diff --git a/orx-fx/src/jvmDemo/kotlin/DemoColormapTurbo.kt b/orx-fx/src/jvmDemo/kotlin/DemoColormapTurbo.kt new file mode 100644 index 00000000..b60f94d6 --- /dev/null +++ b/orx-fx/src/jvmDemo/kotlin/DemoColormapTurbo.kt @@ -0,0 +1,21 @@ +import org.openrndr.application +import org.openrndr.draw.ColorType +import org.openrndr.draw.createEquivalent +import org.openrndr.draw.loadImage +import org.openrndr.extra.fx.colormap.TurboColormap +import kotlin.math.sin + +fun main() { + application { + program { + val colormap = TurboColormap() + val image = loadImage("demo-data/images/image-001.png") + val colormapImage = image.createEquivalent(type = ColorType.FLOAT32) + extend { + colormap.curve = 1.0 + sin(seconds) * .5 + colormap.apply(image, colormapImage) + drawer.image(colormapImage) + } + } + } +} \ No newline at end of file diff --git a/orx-fx/src/shaders/glsl/colormap/spectral-zucconi-colormap.frag b/orx-fx/src/shaders/glsl/colormap/spectral-zucconi-colormap.frag index 4d7bc393..5d562f01 100644 --- a/orx-fx/src/shaders/glsl/colormap/spectral-zucconi-colormap.frag +++ b/orx-fx/src/shaders/glsl/colormap/spectral-zucconi-colormap.frag @@ -1,18 +1,4 @@ -// Spectral Colour Schemes -// By Alan Zucconi -// Website: www.alanzucconi.com -// Twitter: @AlanZucconi - -// Example of different spectral colour schemes -// to convert visible wavelengths of light (400-700 nm) to RGB colours. - -// The function "spectral_zucconi6" provides the best approximation -// without including any branching. -// Its faster version, "spectral_zucconi", is advised for mobile applications. - - -// Read "Improving the Rainbow" for more information -// http://www.alanzucconi.com/?p=6703 +#pragma import colormap.spectral_zucconi6 #ifdef OR_IN_OUT in vec2 v_texCoord0; @@ -29,47 +15,6 @@ uniform float curve; out vec4 o_color; #endif -float saturate (float x) -{ - return min(1.0, max(0.0,x)); -} -vec3 saturate (vec3 x) -{ - return min(vec3(1.,1.,1.), max(vec3(0.,0.,0.),x)); -} - -// --- Spectral Zucconi -------------------------------------------- -// By Alan Zucconi -// Based on GPU Gems: https://developer.nvidia.com/sites/all/modules/custom/gpugems/books/GPUGems/gpugems_ch08.html -// But with values optimised to match as close as possible the visible spectrum -// Fits this: https://commons.wikimedia.org/wiki/File:Linear_visible_spectrum.svg -// With weighter MSE (RGB weights: 0.3, 0.59, 0.11) -vec3 bump3y (vec3 x, vec3 yoffset) -{ - vec3 y = vec3(1.,1.,1.) - x * x; - y = saturate(y-yoffset); - return y; -} - -// --- Spectral Zucconi 6 -------------------------------------------- - -// Based on GPU Gems -// Optimised by Alan Zucconi -vec3 spectral_zucconi6 (float x) -{ - const vec3 c1 = vec3(3.54585104, 2.93225262, 2.41593945); - const vec3 x1 = vec3(0.69549072, 0.49228336, 0.27699880); - const vec3 y1 = vec3(0.02312639, 0.15225084, 0.52607955); - - const vec3 c2 = vec3(3.90307140, 3.21182957, 3.96587128); - const vec3 x2 = vec3(0.11748627, 0.86755042, 0.66077860); - const vec3 y2 = vec3(0.84897130, 0.88445281, 0.73949448); - - return - bump3y(c1 * (x - x1), y1) + - bump3y(c2 * (x - x2), y2); -} - void main() { #ifndef OR_GL_TEXTURE2D float red = texture(tex0, v_texCoord0).r; diff --git a/orx-fx/src/shaders/glsl/colormap/turbo-colormap.frag b/orx-fx/src/shaders/glsl/colormap/turbo-colormap.frag index 3edb22d5..0d2c8c7e 100644 --- a/orx-fx/src/shaders/glsl/colormap/turbo-colormap.frag +++ b/orx-fx/src/shaders/glsl/colormap/turbo-colormap.frag @@ -1,13 +1,4 @@ -// TurboColormap -// Copyright 2019 Google LLC. -// SPDX-License-Identifier: Apache-2.0 - -// Polynomial approximation in GLSL for the Turbo colormap -// Original LUT: https://gist.github.com/mikhailov-work/ee72ba4191942acecc03fe6da94fc73f - -// Authors: -// Colormap Design: Anton Mikhailov (mikhailov@google.com) -// GLSL Approximation: Ruofei Du (ruofei@google.com) +#pragma import colormap.turbo_colormap #ifdef OR_IN_OUT in vec2 v_texCoord0; @@ -24,28 +15,6 @@ uniform float curve; out vec4 o_color; #endif -float saturate(in float x) { - return max(0, min(1, x)); -} - -vec3 TurboColormap(in float x) { - const vec4 kRedVec4 = vec4(0.13572138, 4.61539260, -42.66032258, 132.13108234); - const vec4 kGreenVec4 = vec4(0.09140261, 2.19418839, 4.84296658, -14.18503333); - const vec4 kBlueVec4 = vec4(0.10667330, 12.64194608, -60.58204836, 110.36276771); - const vec2 kRedVec2 = vec2(-152.94239396, 59.28637943); - const vec2 kGreenVec2 = vec2(4.27729857, 2.82956604); - const vec2 kBlueVec2 = vec2(-89.90310912, 27.34824973); - - x = saturate(x); - vec4 v4 = vec4( 1.0, x, x * x, x * x * x); - vec2 v2 = v4.zw * v4.z; - return vec3( - dot(v4, kRedVec4) + dot(v2, kRedVec2), - dot(v4, kGreenVec4) + dot(v2, kGreenVec2), - dot(v4, kBlueVec4) + dot(v2, kBlueVec2) - ); -} - void main() { #ifndef OR_GL_TEXTURE2D float red = texture(tex0, v_texCoord0).r; @@ -53,7 +22,7 @@ void main() { float red = texture2D(tex0, v_texCoord0).r; #endif float value = (red - minValue) / (maxValue - minValue); - vec3 color = TurboColormap(pow(value, curve)); + vec3 color = turbo_colormap(pow(value, curve)); color *= step(value, 1.) * step(0., value); vec4 result = vec4(color, 1.); #ifdef OR_GL_FRAGCOLOR