package org.openrndr.extra.shadestyles.fills.gradients import org.openrndr.color.AlgebraicColor import org.openrndr.color.ColorRGBa import org.openrndr.color.ConvertibleToColorRGBa import org.openrndr.draw.ObservableHashmap import org.openrndr.draw.ShadeStyle import org.openrndr.draw.StyleParameters import org.openrndr.extra.shadestyles.fills.FillFit import org.openrndr.extra.shadestyles.fills.FillUnits import org.openrndr.extra.shadestyles.fills.SpreadMethod import org.openrndr.math.CastableToVector4 import org.openrndr.math.Vector4 import kotlin.reflect.KClass class GradientBuilder(val colorType: KClass): StyleParameters where C : ConvertibleToColorRGBa, C : AlgebraicColor, C : CastableToVector4 { override var parameterTypes: ObservableHashmap = ObservableHashmap(mutableMapOf()) {} override var parameterValues: MutableMap = mutableMapOf() override var textureBaseIndex: Int = 2 var stops = mutableMapOf() var fillUnits = FillUnits.BOUNDS var fillFit = FillFit.STRETCH var spreadMethod = SpreadMethod.PAD var levelWarpFunction = """float levelWarp(vec2 coord, float level) { return level; }""" var domainWarpFunction = """vec2 domainWarp(vec2 coord) { return coord; }""" var gradientFunction = """float gradientFunction(vec2 coord) { return 0.0; }""" var quantization = 0 private fun setBaseParameters(style: GradientBase) { style.parameterTypes.putAll(parameterTypes) style.parameterValues.putAll(parameterValues) style.quantization = quantization style.spreadMethod = spreadMethod.ordinal style.fillUnits = fillUnits.ordinal style.fillFit = fillFit.ordinal } @PublishedApi internal var shadeStyleBuilder: GradientShadeStyleBuilder = LinearGradientBuilder(this) /** * Configures a linear gradient by applying the provided builder block. * * @param builder A lambda function used to define the properties of the linear gradient. * The builder block allows customization of attributes such as * start and end positions. */ fun linear(builder: LinearGradientBuilder.() -> Unit) { shadeStyleBuilder = LinearGradientBuilder(this).apply { builder() } gradientFunction = LinearGradient.gradientFunction } /** * Configures a radial gradient by applying the provided builder block. * * @param builder A lambda function used to define the properties of the radial gradient. * The builder block allows customization of attributes such as the center, * radius, focal center, and focal radius. */ fun radial(builder: RadialGradientBuilder.() -> Unit) { shadeStyleBuilder = RadialGradientBuilder(this).apply { builder() } gradientFunction = RadialGradient.gradientFunction } fun elliptic(builder: EllipticalGradientBuilder.() -> Unit) { shadeStyleBuilder = EllipticalGradientBuilder(this).apply { builder() } gradientFunction = EllipticalGradient.gradientFunction } /** * Configures a conic gradient by applying the provided builder block. * * @param builder A lambda function used to define the properties of the conic gradient. * The builder block allows customization of attributes such as the center, * angle, start angle, and rotation. */ fun conic(builder: ConicGradientBuilder.() -> Unit) { shadeStyleBuilder = ConicGradientBuilder(this).apply { builder() } gradientFunction = ConicGradient.gradientFunction } /** * Configures a stellar gradient by applying the provided builder block. * * @param builder A lambda function used to define the properties of the stellar gradient. * The builder block allows customization of attributes such as center, radius, * sharpness, rotation, and the number of sides. */ fun stellar(builder: StellarGradientBuilder.() -> Unit) { shadeStyleBuilder = StellarGradientBuilder(this).apply { builder() } gradientFunction = StellarGradient.gradientFunction } internal fun extractSteps(): List> { return stops.entries.sortedBy { it.key }.map { Pair(it.key, it.value) } } internal fun extractStepsUnzip(): Pair, Array> { val steps = extractSteps() val stopsArray = Array(steps.size) { steps[it].first } val colorsArray = Array(steps.size) { (steps[it].second.let { c -> if (c is ColorRGBa) { c.toLinear() } else { c } }).toVector4() } return Pair(stopsArray, colorsArray) } internal fun structure(): GradientBaseStructure = GradientBaseStructure(gradientFunction, domainWarpFunction, levelWarpFunction) @PublishedApi internal fun build(): GradientBase { return this.shadeStyleBuilder.build().apply { setBaseParameters(this) } } } sealed interface GradientShadeStyleBuilder where C : ConvertibleToColorRGBa, C : AlgebraicColor, C : CastableToVector4 { /** * Constructs and returns a `GradientBase` object representing a gradient with the * desired configuration defined in the implementing class. * * @return An instance of `GradientBase` configured with the specified gradient parameters. */ fun build(): GradientBase } /** * Creates a gradient shade style using the specified configuration. * * The method allows for building a gradient using a DSL-like approach, * where different properties such as gradient stops, gradient type, and * other configurations can be set. * * @param builder A lambda function used to configure the gradient properties * through an instance of [GradientBuilder]. * @return A [ShadeStyle] instance representing the configured gradient. */ inline fun gradient(builder: GradientBuilder.() -> Unit): ShadeStyle where C : ConvertibleToColorRGBa, C : AlgebraicColor, C : CastableToVector4 { val gb = GradientBuilder(C::class) gb.builder() return gb.build() }