diff --git a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBase.kt b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBase.kt new file mode 100644 index 00000000..7fce1239 --- /dev/null +++ b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/GradientBase.kt @@ -0,0 +1,120 @@ +package org.openrndr.extra.shadestyles.fills.gradients + +import org.openrndr.color.AlgebraicColor +import org.openrndr.color.ConvertibleToColorRGBa +import org.openrndr.draw.ShadeStyle +import org.openrndr.extra.color.phrases.linearRgbToOklabPhrase +import org.openrndr.extra.color.phrases.oklabToLinearRgbPhrase +import org.openrndr.extra.shadestyles.fills.FillFit +import org.openrndr.extra.shadestyles.fills.FillUnits +import org.openrndr.extra.shadestyles.fills.SpreadMethod +import org.openrndr.extra.shadestyles.generateColorTransform +import org.openrndr.math.CastableToVector4 +import org.openrndr.math.Vector4 +import kotlin.math.PI +import kotlin.reflect.KClass + +class GradientBaseStructure( + val gradientFunction: String, + val domainWarpFunction: String, + val levelWarpFunction: String +) + +open class GradientBase( + colorType: KClass, + colors: Array, + points: Array = Array(colors.size) { it / (colors.size - 1.0) }, + structure: GradientBaseStructure +) : ShadeStyle() + where C : ConvertibleToColorRGBa, C : AlgebraicColor, C : CastableToVector4 { + + var quantization: Int by Parameter() + var colors: Array by Parameter() + var points: Array by Parameter() + var spreadMethod: Int by Parameter() + var fillUnits: Int by Parameter() + var fillFit: Int by Parameter() + + init { + this.quantization = 0 + this.colors = colors + this.points = points + this.fillUnits = FillUnits.BOUNDS.ordinal + this.spreadMethod = SpreadMethod.PAD.ordinal + this.fillFit = FillFit.STRETCH.ordinal + fragmentPreamble = """ + |vec2 rotate2D(vec2 x, float angle){ + | float rad = angle / 180.0 * $PI; + | mat2 m = mat2(cos(rad),-sin(rad), sin(rad),cos(rad)); + | return m * x; + |} + |$oklabToLinearRgbPhrase + |$linearRgbToOklabPhrase + |${structure.gradientFunction} + |${structure.domainWarpFunction} + |${structure.levelWarpFunction} + |""".trimMargin() + + fragmentTransform = """ + vec2 coord = vec2(0.0); + + if (p_fillUnits == 0) { // BOUNDS + coord = c_boundsPosition.xy; + if (p_fillFit == 1) { // COVER + float mx = max(c_boundsSize.x, c_boundsSize.y); + float ar = min(c_boundsSize.x, c_boundsSize.y) / mx; + if (c_boundsSize.x == mx) { + coord.y = (coord.y - 0.5) * ar + 0.5; + } else { + coord.x = (coord.x - 0.5) * ar + 0.5; + } + + } else if (p_fillFit == 2) { // CONTAIN + float mx = max(c_boundsSize.x, c_boundsSize.y); + float ar = mx / min(c_boundsSize.x, c_boundsSize.y); + if (c_boundsSize.y == mx) { + coord.y = (coord.y - 0.5) * ar + 0.5; + } else { + coord.x = (coord.x - 0.5) * ar + 0.5; + } + } + } else if (p_fillUnits == 1) { // WORLD + coord = v_worldPosition.xy; + } else if (p_fillUnits == 2) { // VIEW + coord = v_viewPosition.xy; + } else if (p_fillUnits == 3) { // SCREEN + coord = c_screenPosition.xy; + coord.y = u_viewDimensions.y - coord.y; + } + + coord = domainWarp(coord); + + float f = gradientFunction(coord); + f = levelWarp(coord, f); + + if (p_quantization != 0) { + f = floor(f * float(p_quantization)) / (float(p_quantization) - 1.0); + } + + if (p_spreadMethod == 0) { // PAD + f = clamp(f, 0.0, 1.0); + } else if (p_spreadMethod == 1) { // REFLECT + f = 2.0 * abs(f / 2.0 - floor(f / 2.0 + 0.5)); + } else if (p_spreadMethod == 2) { // REPEAT + f = mod(f, 1.0); + } + + int i = 0; + while (i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; } + + vec4 color0 = p_colors[i]; + vec4 color1 = p_colors[i+1]; + + float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]); + vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0)); + + ${generateColorTransform(colorType)} + x_fill *= gradient; + """ + } +} \ No newline at end of file diff --git a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/LinearGradient.kt b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/LinearGradient.kt index 1fc50d67..7005a3b0 100644 --- a/orx-shade-styles/src/commonMain/kotlin/fills/gradients/LinearGradient.kt +++ b/orx-shade-styles/src/commonMain/kotlin/fills/gradients/LinearGradient.kt @@ -2,124 +2,11 @@ package org.openrndr.extra.shadestyles.fills.gradients import org.openrndr.color.AlgebraicColor import org.openrndr.color.ConvertibleToColorRGBa -import org.openrndr.draw.ShadeStyle -import org.openrndr.extra.color.phrases.linearRgbToOklabPhrase -import org.openrndr.extra.color.phrases.oklabToLinearRgbPhrase -import org.openrndr.extra.shadestyles.fills.FillFit -import org.openrndr.extra.shadestyles.fills.FillUnits -import org.openrndr.extra.shadestyles.fills.SpreadMethod -import org.openrndr.extra.shadestyles.generateColorTransform import org.openrndr.math.CastableToVector4 import org.openrndr.math.Vector2 import org.openrndr.math.Vector4 -import kotlin.math.PI import kotlin.reflect.KClass - -class GradientBaseStructure( - val gradientFunction: String, - val domainWarpFunction: String, - val levelWarpFunction: String -) - -open class GradientBase( - colorType: KClass, - colors: Array, - points: Array = Array(colors.size) { it / (colors.size - 1.0) }, - structure: GradientBaseStructure -) : ShadeStyle() - where C : ConvertibleToColorRGBa, C : AlgebraicColor, C : CastableToVector4 { - - var quantization: Int by Parameter() - var colors: Array by Parameter() - var points: Array by Parameter() - var spreadMethod: Int by Parameter() - var fillUnits: Int by Parameter() - var fillFit: Int by Parameter() - - init { - this.quantization = 0 - this.colors = colors - this.points = points - this.fillUnits = FillUnits.BOUNDS.ordinal - this.spreadMethod = SpreadMethod.PAD.ordinal - this.fillFit = FillFit.STRETCH.ordinal - fragmentPreamble = """ - |vec2 rotate2D(vec2 x, float angle){ - | float rad = angle / 180.0 * $PI; - | mat2 m = mat2(cos(rad),-sin(rad), sin(rad),cos(rad)); - | return m * x; - |} - |$oklabToLinearRgbPhrase - |$linearRgbToOklabPhrase - |${structure.gradientFunction} - |${structure.domainWarpFunction} - |${structure.levelWarpFunction} - |""".trimMargin() - - fragmentTransform = """ - vec2 coord = vec2(0.0); - - if (p_fillUnits == 0) { // BOUNDS - coord = c_boundsPosition.xy; - if (p_fillFit == 1) { // COVER - float mx = max(c_boundsSize.x, c_boundsSize.y); - float ar = mx / min(c_boundsSize.x, c_boundsSize.y); - if (c_boundsSize.x == mx) { - coord.y = (coord.y - 0.5) * ar + 0.5; - } else { - coord.x = (coord.x - 0.5) * ar + 0.5; - } - } else if (p_fillFit == 2) { // CONTAIN - float mx = max(c_boundsSize.x, c_boundsSize.y); - float ar = min(c_boundsSize.x, c_boundsSize.y) / mx; - if (c_boundsSize.y == mx) { - coord.y = (coord.y - 0.5) * ar + 0.5; - } else { - coord.x = (coord.x - 0.5) * ar + 0.5; - } - } - } else if (p_fillUnits == 1) { // WORLD - coord = v_worldPosition.xy; - } else if (p_fillUnits == 2) { // VIEW - coord = v_viewPosition.xy; - } else if (p_fillUnits == 3) { // SCREEN - coord = c_screenPosition.xy; - coord.y = u_viewDimensions.y - coord.y; - } - - coord = domainWarp(coord); - - float f = gradientFunction(coord); - f = levelWarp(coord, f); - - if (p_quantization != 0) { - f = floor(f * float(p_quantization)) / (float(p_quantization) - 1.0); - } - - if (p_spreadMethod == 0) { // PAD - f = clamp(f, 0.0, 1.0); - } else if (p_spreadMethod == 1) { // REFLECT - f = 2.0 * abs(f / 2.0 - floor(f / 2.0 + 0.5)); - } else if (p_spreadMethod == 2) { // REPEAT - f = mod(f, 1.0); - } - - int i = 0; - while (i < p_points_SIZE - 1 && f >= p_points[i+1]) { i++; } - - vec4 color0 = p_colors[i]; - vec4 color1 = p_colors[i+1]; - - float g = (f - p_points[i]) / (p_points[i+1] - p_points[i]); - vec4 gradient = mix(color0, color1, clamp(g, 0.0, 1.0)); - - ${generateColorTransform(colorType)} - x_fill *= gradient; - """ - } -} - open class LinearGradient( colorType: KClass, start: Vector2 = Vector2.ZERO, @@ -127,7 +14,7 @@ open class LinearGradient( colors: Array, points: Array = Array(colors.size) { it / (colors.size - 1.0) }, structure: GradientBaseStructure, - ) : GradientBase( +) : GradientBase( colorType, colors, points, diff --git a/orx-shade-styles/src/jvmDemo/kotlin/gradients/DemoGradient04.kt b/orx-shade-styles/src/jvmDemo/kotlin/gradients/DemoGradient04.kt new file mode 100644 index 00000000..2d99f71b --- /dev/null +++ b/orx-shade-styles/src/jvmDemo/kotlin/gradients/DemoGradient04.kt @@ -0,0 +1,49 @@ +package gradients + +import org.openrndr.application +import org.openrndr.color.ColorRGBa +import org.openrndr.extra.color.spaces.OKHSV +import org.openrndr.extra.color.tools.shiftHue +import org.openrndr.extra.shadestyles.fills.FillFit +import org.openrndr.extra.shadestyles.fills.SpreadMethod +import org.openrndr.extra.shadestyles.fills.gradients.gradient +import org.openrndr.extra.shapes.primitives.grid +import org.openrndr.extra.shapes.primitives.placeIn +import org.openrndr.math.Vector2 + +fun main() { + application { + configure { + width = 720 + height = 720 + } + program { + extend { + val grid = drawer.bounds.grid(3, 3) + + for ((index, row) in grid.withIndex()) { + drawer.shadeStyle = gradient { + for (i in 0..10) { + stops[i / 10.0] = ColorRGBa.RED.shiftHue(i * 36.0) + + } + spreadMethod = SpreadMethod.PAD + this.fillFit = FillFit.entries[index] + radial { + center = Vector2(0.5, 0.5) + } + } + + for ((x, cell) in row.withIndex()) { + val paddedCell = cell.offsetEdges(-10.0) + when (x) { + 0 -> drawer.rectangle(paddedCell.sub(0.0..0.5, 0.0..1.0).placeIn(paddedCell)) + 1 -> drawer.rectangle(paddedCell) + 2 -> drawer.rectangle(paddedCell.sub(0.0..1.0, 0.0..0.5).placeIn(paddedCell)) + } + } + } + } + } + } +} \ No newline at end of file