From 5d9333cb509b25980a6774ce21becc3abaecca34 Mon Sep 17 00:00:00 2001 From: Abe Pazos Date: Tue, 21 Jul 2020 18:19:36 +0200 Subject: [PATCH] Add more n-point gradients (#139) --- .../src/demo/kotlin/DemoAllGradients01.kt | 10 ++-- .../src/demo/kotlin/DemoNPointGradient01.kt | 2 +- .../demo/kotlin/DemoNPointLinearGradient01.kt | 59 ++++++++++++++++++ .../demo/kotlin/DemoNPointRadialGradient01.kt | 47 +++++++++++++++ .../src/main/kotlin/LinearGradient.kt | 5 +- .../src/main/kotlin/NPointGradient.kt | 5 +- .../src/main/kotlin/NPointLinearGradient.kt | 57 ++++++++++++++++++ .../src/main/kotlin/NPointRadialGradient.kt | 60 +++++++++++++++++++ 8 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt create mode 100644 orx-shade-styles/src/demo/kotlin/DemoNPointRadialGradient01.kt create mode 100644 orx-shade-styles/src/main/kotlin/NPointLinearGradient.kt create mode 100644 orx-shade-styles/src/main/kotlin/NPointRadialGradient.kt diff --git a/orx-shade-styles/src/demo/kotlin/DemoAllGradients01.kt b/orx-shade-styles/src/demo/kotlin/DemoAllGradients01.kt index 41b6e59c2..dc76f9545 100644 --- a/orx-shade-styles/src/demo/kotlin/DemoAllGradients01.kt +++ b/orx-shade-styles/src/demo/kotlin/DemoAllGradients01.kt @@ -1,13 +1,15 @@ import org.openrndr.application import org.openrndr.color.ColorRGBa -import org.openrndr.color.ColorXSVa import org.openrndr.draw.isolated import org.openrndr.extensions.SingleScreenshot import org.openrndr.extra.shadestyles.* import org.openrndr.math.Polar import org.openrndr.shape.Rectangle -import kotlin.random.Random +/** + * Example of 5 gradient styles. + * NPointLinear and NPoingGradient have separate demos. + */ fun main() { application { configure { @@ -25,7 +27,7 @@ fun main() { val gradients = listOf( RadialGradient(ColorRGBa.PINK, ColorRGBa.WHITE), AngularGradient(ColorRGBa.PINK, ColorRGBa.WHITE), - NPointGradient(4, Array(4) { + NPointGradient(Array(4) { ColorRGBa.PINK.shade(it / 3.0) }), LinearGradient(ColorRGBa.PINK, ColorRGBa.WHITE), @@ -72,7 +74,7 @@ fun main() { is NPointGradient -> { // Animate points. // We could also animate colors. - gradient.points = Array(gradient.numPoints) { + gradient.points = Array(gradient.colors.size) { rect.center + Polar(it * 90.0 + column * 36 - seconds * 10, 40.0).cartesian diff --git a/orx-shade-styles/src/demo/kotlin/DemoNPointGradient01.kt b/orx-shade-styles/src/demo/kotlin/DemoNPointGradient01.kt index 4be207a00..a5a5fe17d 100644 --- a/orx-shade-styles/src/demo/kotlin/DemoNPointGradient01.kt +++ b/orx-shade-styles/src/demo/kotlin/DemoNPointGradient01.kt @@ -27,7 +27,7 @@ fun main() { } val numPoints = 8 - val gradient = NPointGradient(numPoints, Array(numPoints) { + val gradient = NPointGradient(Array(numPoints) { ColorXSVa(it * 360.0 / numPoints, 1.0, 1.0).toRGBa() }) diff --git a/orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt b/orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt new file mode 100644 index 000000000..bdaaec595 --- /dev/null +++ b/orx-shade-styles/src/demo/kotlin/DemoNPointLinearGradient01.kt @@ -0,0 +1,59 @@ +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.shadestyles.NPointLinearGradient +import org.openrndr.shape.Rectangle +import kotlin.math.pow +import kotlin.math.sin + +/** + * Demonstrate using a multi color 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 + * the ends over time using pow() and sin(seconds). + */ +fun main() { + application { + program { + if (System.getProperty("takeScreenshot") == "true") { + extend(SingleScreenshot()) { + this.outputFile = System.getProperty("screenshotPath") + } + } + + val numPoints = 8 + val gradient = NPointLinearGradient(Array(numPoints) { + ColorXSVa(it * 360.0 / numPoints, 1.0, 1.0).toRGBa() + }) + + extend { + 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) + + gradient.rotation += 90 + rectangle(Rectangle.fromCenter( + bounds.position(0.655, 0.5), 200.0, 200.0)) + } + } + } + } +} \ No newline at end of file diff --git a/orx-shade-styles/src/demo/kotlin/DemoNPointRadialGradient01.kt b/orx-shade-styles/src/demo/kotlin/DemoNPointRadialGradient01.kt new file mode 100644 index 000000000..61a4dd628 --- /dev/null +++ b/orx-shade-styles/src/demo/kotlin/DemoNPointRadialGradient01.kt @@ -0,0 +1,47 @@ +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.color.rgb +import org.openrndr.extensions.SingleScreenshot +import org.openrndr.extra.shadestyles.NPointRadialGradient +import org.openrndr.shape.Circle +import kotlin.random.Random + +/** + * Demonstrate using a multi color radial gradient. + * The gradient has 5 colors (first and last ones are transparent). + * Any of the properties can be animated, including colors and points. + * See DemoNPointLinearGradient01.kt for an example of animated properties. + */ +fun main() { + application { + program { + if (System.getProperty("takeScreenshot") == "true") { + extend(SingleScreenshot()) { + this.outputFile = System.getProperty("screenshotPath") + } + } + + val gradient = NPointRadialGradient(arrayOf( + ColorRGBa.PINK.opacify(0.0), + ColorRGBa.PINK, ColorRGBa.WHITE, ColorRGBa.PINK, + ColorRGBa.PINK.opacify(0.0) + ), arrayOf(0.0, 0.4, 0.5, 0.6, 1.0)) + + val circles = List(25) { + Circle(Random.nextDouble() * drawer.width, + Random.nextDouble() * drawer.height, + Random.nextDouble() * 150.0) + } + + extend { + drawer.run { + clear(rgb(0.2)) + shadeStyle = gradient + fill = ColorRGBa.WHITE + stroke = null + circles(circles) + } + } + } + } +} \ No newline at end of file diff --git a/orx-shade-styles/src/main/kotlin/LinearGradient.kt b/orx-shade-styles/src/main/kotlin/LinearGradient.kt index 316084202..3bcf61c73 100644 --- a/orx-shade-styles/src/main/kotlin/LinearGradient.kt +++ b/orx-shade-styles/src/main/kotlin/LinearGradient.kt @@ -17,11 +17,14 @@ class LinearGradient( @ColorParameter("start color", order = 0) var color0: ColorRGBa by Parameter() + @ColorParameter("end color", order = 1) var color1: ColorRGBa by Parameter() var offset: Vector2 by Parameter() + @DoubleParameter("rotation", -180.0, 180.0, order = 2) var rotation: Double by Parameter() + @DoubleParameter("exponent", 0.01, 10.0, order = 3) var exponent: Double by Parameter() @@ -65,6 +68,6 @@ fun linearGradient( offset: Vector2 = Vector2.ZERO, rotation: Double = 0.0, exponent: Double = 1.0 -) : ShadeStyle { +): ShadeStyle { return LinearGradient(color0, color1, offset, rotation, exponent) } \ No newline at end of file diff --git a/orx-shade-styles/src/main/kotlin/NPointGradient.kt b/orx-shade-styles/src/main/kotlin/NPointGradient.kt index f045be51d..efac14bc7 100644 --- a/orx-shade-styles/src/main/kotlin/NPointGradient.kt +++ b/orx-shade-styles/src/main/kotlin/NPointGradient.kt @@ -8,23 +8,20 @@ import org.openrndr.math.Vector3 @Description("N-Point gradient") class NPointGradient( - numPoints: Int, colors: Array, points: Array = arrayOf(Vector2.ZERO)) : ShadeStyle() { - var numPoints: Int by Parameter() var colors: Array by Parameter() var points: Array by Parameter() init { - this.numPoints = numPoints this.colors = colors this.points = points fragmentTransform = """ float sum = 0; vec4 rgba = vec4(0.0); - for(int i=0; i, + points: Array = Array(colors.size) { it / (colors.size - 1.0) }, + offset: Vector2 = Vector2.ZERO, + rotation: Double = 0.0) : ShadeStyle() { + + var colors: Array by Parameter() + // Sorted normalized values defining relative positions of colors + var points: Array by Parameter() + var offset: Vector2 by Parameter() + var rotation: Double by Parameter() + + init { + this.colors = colors + this.points = points + this.offset = offset + this.rotation = rotation + + fragmentTransform = """ + vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset); + + float cr = cos(radians(p_rotation)); + float sr = sin(radians(p_rotation)); + mat2 rm = mat2(cr, -sr, sr, cr); + vec2 rc = rm * coord; + float f = clamp(rc.y + 0.5, 0.0, 1.0); + + int i=0; + while(i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; } + + vec4 color0 = p_colors[i]; + color0.rgb *= color0.a; + + vec4 color1 = p_colors[i+1]; + color1.rgb *= color1.a; + + float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]); + vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0)); + + vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a; + + x_fill = fn * gradient; + if (x_fill.a != 0) { + x_fill.rgb /= x_fill.a; + } + + """ + } +} diff --git a/orx-shade-styles/src/main/kotlin/NPointRadialGradient.kt b/orx-shade-styles/src/main/kotlin/NPointRadialGradient.kt new file mode 100644 index 000000000..1e0e55293 --- /dev/null +++ b/orx-shade-styles/src/main/kotlin/NPointRadialGradient.kt @@ -0,0 +1,60 @@ +package org.openrndr.extra.shadestyles + +import org.openrndr.color.ColorRGBa +import org.openrndr.draw.ShadeStyle +import org.openrndr.extra.parameters.Description +import org.openrndr.math.Vector2 + +@Description("Multicolor radial gradient") +class NPointRadialGradient( + colors: Array, + points: Array = Array(colors.size) { it / (colors.size - 1.0) }, + offset: Vector2 = Vector2.ZERO, + rotation: Double = 0.0, + length: Double = 1.0) : ShadeStyle() { + + var colors: Array by Parameter() + + // Sorted normalized values defining relative positions of colors + var points: Array by Parameter() + var offset: Vector2 by Parameter() + var rotation: Double by Parameter() + var length: Double by Parameter() + + init { + this.colors = colors + this.points = points + this.offset = offset + this.rotation = rotation + this.length = length + + fragmentTransform = """ + vec2 coord = (c_boundsPosition.xy - 0.5 + p_offset/2.0) * 2.0; + + float cr = cos(radians(p_rotation)); + float sr = sin(radians(p_rotation)); + mat2 rm = mat2(cr, -sr, sr, cr); + vec2 rc = rm * coord; + float f = clamp(p_length * length(rc), 0.0, 1.0); + + int i=0; + while(i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; } + + vec4 color0 = p_colors[i]; + color0.rgb *= color0.a; + + vec4 color1 = p_colors[i+1]; + color1.rgb *= color1.a; + + float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]); + vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0)); + + vec4 fn = vec4(x_fill.rgb, 1.0) * x_fill.a; + + x_fill = fn * gradient; + if (x_fill.a != 0) { + x_fill.rgb /= x_fill.a; + } + """ + } +}