[orx-shade-styles] Add pattern shade styles
This commit is contained in:
@@ -25,6 +25,7 @@ kotlin {
|
||||
implementation(project(":orx-noise"))
|
||||
implementation(project(":orx-shapes"))
|
||||
implementation(project(":orx-image-fit"))
|
||||
implementation(project(":orx-camera"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.openrndr.extra.shadestyles.fills.patterns
|
||||
|
||||
import org.openrndr.draw.ShadeStyle
|
||||
|
||||
|
||||
class PatternBaseStructure(
|
||||
val patternFunction: String,
|
||||
val domainWarpFunction: String,
|
||||
)
|
||||
|
||||
class PatternBase(structure: PatternBaseStructure) : ShadeStyle() {
|
||||
|
||||
var patternUnits: Int by Parameter()
|
||||
var patternFit: Int by Parameter()
|
||||
|
||||
|
||||
init {
|
||||
fragmentPreamble = """
|
||||
${structure.domainWarpFunction}
|
||||
${structure.patternFunction}
|
||||
""".trimIndent()
|
||||
|
||||
fragmentTransform = """vec2 coord = vec2(0.0);
|
||||
if (p_patternUnits == 0) { // BOUNDS
|
||||
coord = c_boundsPosition.xy;
|
||||
|
||||
if (p_patternFit == 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_patternFit == 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_patternUnits == 1) { // WORLD
|
||||
coord = v_worldPosition.xy;
|
||||
} else if (p_patternUnits == 2) { // VIEW
|
||||
coord = v_viewPosition.xy;
|
||||
} else if (p_patternUnits == 3) { // SCREEN
|
||||
coord = c_screenPosition.xy;
|
||||
coord.y = u_viewDimensions.y - coord.y;
|
||||
}
|
||||
|
||||
vec2 dx = dFdx(coord);
|
||||
vec2 dy = dFdy(coord);
|
||||
|
||||
int window = p_patternFilterWindow;
|
||||
float filterSpread = p_patternFilterSpread;
|
||||
float mask = 0.0;
|
||||
for (int v = 0; v < window; v++) {
|
||||
for (int u = 0; u < window; u++) {
|
||||
float fv = filterSpread * float(v) / (float(window) - 1.0) - 0.5;
|
||||
float fu = filterSpread * float(u) / (float(window) - 1.0) - 0.5;
|
||||
vec2 scoord = coord + dx * fu + dy * fv;
|
||||
vec2 wcoord = patternDomainWarp(scoord);
|
||||
wcoord = (p_patternTransform * vec4(wcoord, 0.0, 1.0)).xy;
|
||||
mask += clamp(pattern(wcoord * p_patternScale), 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
mask /= (float(window) * float(window));
|
||||
|
||||
if (p_patternInvert) {
|
||||
mask = 1.0 - mask;
|
||||
}
|
||||
|
||||
vec4 color = mix(p_patternBackgroundColor, p_patternForegroundColor, mask);
|
||||
|
||||
x_fill *= color;
|
||||
x_stroke *= color;
|
||||
""".trimIndent()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package org.openrndr.extra.shadestyles.fills.patterns
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.ObservableHashmap
|
||||
import org.openrndr.draw.StyleParameters
|
||||
import org.openrndr.extra.shadestyles.fills.FillFit
|
||||
import org.openrndr.extra.shadestyles.fills.FillUnits
|
||||
import org.openrndr.math.Matrix44
|
||||
|
||||
class PatternBuilder : StyleParameters {
|
||||
override var parameterTypes: ObservableHashmap<String, String> = ObservableHashmap(mutableMapOf()) {}
|
||||
override var parameterValues: MutableMap<String, Any> = mutableMapOf()
|
||||
override var textureBaseIndex: Int = 2
|
||||
|
||||
var filterWindow: Int by Parameter("patternFilterWindow", 5)
|
||||
var filterSpread: Double by Parameter("patternFilterSpread", 1.0)
|
||||
|
||||
var foregroundColor: ColorRGBa by Parameter("patternForegroundColor", ColorRGBa.BLACK)
|
||||
var backgroundColor: ColorRGBa by Parameter("patternBackgroundColor", ColorRGBa.WHITE)
|
||||
|
||||
var patternUnits: FillUnits = FillUnits.BOUNDS
|
||||
var patternFit: FillFit = FillFit.STRETCH
|
||||
var patternFunction = """float pattern(vec2 coord) { return 1.0; }"""
|
||||
var domainWarpFunction = """vec2 patternDomainWarp(vec2 coord) { return coord; }"""
|
||||
var patternTransform: Matrix44 by Parameter("patternTransform", Matrix44.IDENTITY)
|
||||
var invert: Boolean by Parameter("patternInvert", false)
|
||||
var scale: Double by Parameter("patternScale", 1.0)
|
||||
|
||||
fun build(): PatternBase {
|
||||
val structure = PatternBaseStructure(patternFunction, domainWarpFunction)
|
||||
val patternBase = PatternBase(structure)
|
||||
patternBase.parameterTypes.putAll(parameterTypes)
|
||||
patternBase.parameterValues.putAll(parameterValues)
|
||||
patternBase.patternUnits = patternUnits.ordinal
|
||||
patternBase.patternFit = patternFit.ordinal
|
||||
return patternBase
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures and applies the checkers pattern to the current pattern builder.
|
||||
*
|
||||
* @param builder The lambda that defines customization for the CheckerPatternBuilder.
|
||||
*/
|
||||
fun checkers(builder: CheckerPatternBuilder.() -> Unit) {
|
||||
val checkerBuilder = CheckerPatternBuilder(this)
|
||||
checkerBuilder.builder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures and applies the XOR Modulation pattern to the current pattern builder.
|
||||
*
|
||||
* @param builder A lambda scope that defines customization for the XorModPatternBuilder.
|
||||
*/
|
||||
fun xorMod(builder: XorModPatternBuilder.() -> Unit) {
|
||||
val xorModBuilder = XorModPatternBuilder(this)
|
||||
xorModBuilder.builder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures and applies the XOR Modulation 2 pattern to the current pattern builder.
|
||||
*
|
||||
* @param builder A lambda scope that defines customization for the XorMod2PatternBuilder.
|
||||
*/
|
||||
fun xorMod2(builder: XorMod2PatternBuilder.() -> Unit) {
|
||||
val xorModBuilder = XorMod2PatternBuilder(this)
|
||||
xorModBuilder.builder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures and applies the dots pattern to the current pattern builder.
|
||||
*
|
||||
* @param builder A lambda scope that defines customization for the DotsPatternBuilder.
|
||||
*/
|
||||
fun dots(builder: DotsPatternBuilder.() -> Unit) {
|
||||
val dotsPatternBuilder = DotsPatternBuilder(this)
|
||||
dotsPatternBuilder.builder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures and applies the boxes pattern to the current pattern builder.
|
||||
*
|
||||
* @param builder A lambda scope that defines customization for the BoxPatternBuilder.
|
||||
*/
|
||||
fun boxes(builder: BoxPatternBuilder.() -> Unit) {
|
||||
val boxPatternBuilder = BoxPatternBuilder(this)
|
||||
boxPatternBuilder.builder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures and applies the crosses pattern to the current pattern builder.
|
||||
*
|
||||
* @param builder A lambda scope that defines customization for the CrossPatternBuilder.
|
||||
*/
|
||||
fun crosses(builder: CrossPatternBuilder.() -> Unit) {
|
||||
val crossPatternBuilder = CrossPatternBuilder(this)
|
||||
crossPatternBuilder.builder()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CheckerPatternBuilder(builder: PatternBuilder) {
|
||||
init {
|
||||
builder.patternFunction = """float pattern(vec2 coord) { return mod(floor(coord.x)+floor(coord.y), 2.0);}"""
|
||||
}
|
||||
}
|
||||
|
||||
class XorModPatternBuilder(builder: PatternBuilder) {
|
||||
var patternMod: Int by builder.Parameter("patternMod", 9)
|
||||
var patternMask: Int by builder.Parameter("patternMask", 3)
|
||||
init {
|
||||
builder.patternFunction = """float pattern(vec2 coord) {
|
||||
ivec2 icoord = ivec2(floor(coord * p_patternScale));
|
||||
int i = ((icoord.x ^ icoord.y) % p_patternMod) & p_patternMask;
|
||||
return i == 0 ? 0.0 : 1.0;
|
||||
}""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
class XorMod2PatternBuilder(builder: PatternBuilder) {
|
||||
var patternMod: Int by builder.Parameter("patternMod", 9)
|
||||
var patternMask: Int by builder.Parameter("patternMask", 3)
|
||||
var patternOffset: Int by builder.Parameter("patternOffset", 0)
|
||||
init {
|
||||
builder.patternFunction = """float pattern(vec2 coord) {
|
||||
ivec2 icoord = ivec2(floor(coord * p_patternScale));
|
||||
int i = (icoord.x + icoord.y) ^ (icoord.x - icoord.y);
|
||||
int i3 = i * i * i;
|
||||
int i6 = i3 * i3;
|
||||
int i7 = i * i6;
|
||||
return (( (i7 + p_patternOffset) % int(p_patternMod)) & int(p_patternMask)) == 0 ? 0.0 : 1.0;
|
||||
}""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
class DotsPatternBuilder(builder: PatternBuilder) {
|
||||
var dotSize: Double by builder.Parameter("patternDotSize", 0.25)
|
||||
var strokeWeight: Double by builder.Parameter("patternStrokeWeight", 1E10)
|
||||
init {
|
||||
builder.patternFunction = """float pattern(vec2 coord) {
|
||||
vec2 scoord = coord * p_patternScale;
|
||||
vec2 mcoord = mod(scoord + vec2(0.5), vec2(1.0)) - vec2(0.5);
|
||||
float d = length(mcoord) - p_patternDotSize;
|
||||
float dw = fwidth(d);
|
||||
return smoothstep(dw/2.0, -dw/2.0, d) * smoothstep(-dw/2.0, dw/2.0, d+p_patternStrokeWeight);;
|
||||
}""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
class BoxPatternBuilder(builder: PatternBuilder) {
|
||||
var width: Double by builder.Parameter("patternBoxWidth", 0.5)
|
||||
var height: Double by builder.Parameter("patternBoxHeight", 0.5)
|
||||
var rounding: Double by builder.Parameter("patternBoxRounding", 0.0)
|
||||
var rotation: Double by builder.Parameter("patternBoxRotation", 0.0)
|
||||
var strokeWeight: Double by builder.Parameter("patternStrokeWeight", 1E10)
|
||||
init {
|
||||
builder.patternFunction = """float pattern(vec2 coord) {
|
||||
float phi = p_patternBoxRotation / 180.0 * 3.141592654;
|
||||
mat2 rm = mat2(cos(phi), sin(phi), -sin(phi), cos(phi));
|
||||
vec2 mcoord = mod(coord * p_patternScale + vec2(0.5), vec2(1.0)) - vec2(0.5);
|
||||
mcoord = rm * mcoord;
|
||||
vec2 d2 = abs(mcoord) - vec2(p_patternBoxWidth - p_patternBoxRounding, p_patternBoxHeight - p_patternBoxRounding)*0.5;
|
||||
float d = length(max(d2,0.0)) + min(max(d2.x,d2.y),0.0) - p_patternBoxRounding;
|
||||
float dw = fwidth(d);
|
||||
return smoothstep(dw/2.0, -dw/2.0, d) * smoothstep(-dw/2.0, dw/2.0, d+p_patternStrokeWeight);;
|
||||
}""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
class CrossPatternBuilder(builder: PatternBuilder) {
|
||||
var width: Double by builder.Parameter("patternCrossWidth", 0.5)
|
||||
var weight: Double by builder.Parameter("patternCrossRounding", 0.1)
|
||||
var rotation: Double by builder.Parameter("patternCrossRotation", 0.0)
|
||||
var strokeWeight: Double by builder.Parameter("patternStrokeWeight", 1E10)
|
||||
init {
|
||||
builder.patternFunction = """float pattern(vec2 coord) {
|
||||
float phi = p_patternCrossRotation / 180.0 * 3.141592654;
|
||||
mat2 rm = mat2(cos(phi), sin(phi), -sin(phi), cos(phi));
|
||||
vec2 mcoord = mod(coord * p_patternScale + vec2(0.5), vec2(1.0)) - vec2(0.5);
|
||||
mcoord = rm * mcoord;
|
||||
vec2 p = abs(mcoord);
|
||||
float d = length(p-min(p.x+p.y, p_patternCrossWidth)*0.5) - p_patternCrossRounding;
|
||||
|
||||
float dw = fwidth(d);
|
||||
return smoothstep(dw/2.0, -dw/2.0, d) * smoothstep(-dw/2.0, dw/2.0, d+p_patternStrokeWeight);;
|
||||
}""".trimIndent()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new `PatternBase` instance configured using the provided builder function.
|
||||
*
|
||||
* @param builder A lambda that operates on a `PatternBuilder` instance to configure the pattern's properties.
|
||||
* @return A `PatternBase` instance, representing the configured pattern with the applied settings.
|
||||
*/
|
||||
fun pattern(builder: PatternBuilder.() -> Unit): PatternBase {
|
||||
val patternBuilder = PatternBuilder()
|
||||
patternBuilder.builder()
|
||||
return patternBuilder.build()
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package patterns
|
||||
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.loadFont
|
||||
import org.openrndr.draw.loadImage
|
||||
import org.openrndr.extra.camera.Camera2D
|
||||
import org.openrndr.extra.color.presets.NAVY
|
||||
import org.openrndr.extra.imageFit.imageFit
|
||||
import org.openrndr.extra.shadestyles.fills.FillUnits
|
||||
import org.openrndr.extra.shadestyles.fills.patterns.pattern
|
||||
|
||||
fun main() = application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
}
|
||||
program {
|
||||
extend(Camera2D())
|
||||
val image = loadImage("demo-data/images/image-001.png")
|
||||
extend {
|
||||
drawer.shadeStyle = pattern {
|
||||
backgroundColor = ColorRGBa.NAVY
|
||||
foregroundColor = ColorRGBa.WHITE
|
||||
patternUnits = FillUnits.WORLD
|
||||
parameter("time", seconds*0.1)
|
||||
// domainWarpFunction = """vec2 patternDomainWarp(vec2 uv) { return uv + vec2(cos(uv.y * 0.1 + p_time), sin(uv.x * 0.1 + p_time)) * 30.05; }"""
|
||||
scale = 0.4
|
||||
|
||||
checkers {
|
||||
}
|
||||
}
|
||||
|
||||
//drawer.rectangle(drawer.bounds)
|
||||
drawer.imageFit(image, drawer.bounds)
|
||||
|
||||
drawer.shadeStyle = pattern {
|
||||
backgroundColor = ColorRGBa.NAVY
|
||||
foregroundColor = ColorRGBa.WHITE
|
||||
patternUnits = FillUnits.WORLD
|
||||
parameter("time", seconds)
|
||||
domainWarpFunction = """vec2 patternDomainWarp(vec2 uv) { return uv + vec2(cos(uv.y * 0.1 + p_time), sin(uv.x * 0.1 + p_time)) * 30.05; }"""
|
||||
scale = 0.2
|
||||
checkers {
|
||||
}
|
||||
}
|
||||
drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 196.0)
|
||||
drawer.text("Patterns", 10.0, height / 2.0)
|
||||
//drawer.circle(drawer.bounds.center, 300.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package patterns
|
||||
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.loadFont
|
||||
import org.openrndr.draw.loadImage
|
||||
import org.openrndr.extra.camera.Camera2D
|
||||
import org.openrndr.extra.color.presets.NAVY
|
||||
import org.openrndr.extra.color.presets.PEACH_PUFF
|
||||
import org.openrndr.extra.imageFit.imageFit
|
||||
import org.openrndr.extra.shadestyles.fills.FillUnits
|
||||
import org.openrndr.extra.shadestyles.fills.patterns.pattern
|
||||
|
||||
fun main() = application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
}
|
||||
program {
|
||||
extend(Camera2D())
|
||||
extend {
|
||||
drawer.shadeStyle = pattern {
|
||||
backgroundColor = ColorRGBa.NAVY
|
||||
foregroundColor = ColorRGBa.PEACH_PUFF
|
||||
patternUnits = FillUnits.WORLD
|
||||
parameter("time", seconds*0.1)
|
||||
scale = 1.0
|
||||
xorMod2 {
|
||||
patternMod = 13
|
||||
patternOffset = (seconds*1).toInt()
|
||||
patternMask = 1
|
||||
}
|
||||
}
|
||||
drawer.rectangle(drawer.bounds.scaledBy(100.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package patterns
|
||||
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.loadFont
|
||||
import org.openrndr.draw.loadImage
|
||||
import org.openrndr.extra.camera.Camera2D
|
||||
import org.openrndr.extra.color.presets.DARK_GRAY
|
||||
import org.openrndr.extra.color.presets.NAVY
|
||||
import org.openrndr.extra.color.presets.PEACH_PUFF
|
||||
import org.openrndr.extra.imageFit.imageFit
|
||||
import org.openrndr.extra.shadestyles.fills.FillUnits
|
||||
import org.openrndr.extra.shadestyles.fills.clip.clip
|
||||
import org.openrndr.extra.shadestyles.fills.gradients.gradient
|
||||
import org.openrndr.extra.shadestyles.fills.patterns.pattern
|
||||
import kotlin.math.cos
|
||||
|
||||
fun main() = application {
|
||||
configure {
|
||||
width = 720
|
||||
height = 720
|
||||
}
|
||||
program {
|
||||
extend(Camera2D())
|
||||
extend {
|
||||
drawer.shadeStyle = pattern {
|
||||
backgroundColor = ColorRGBa.DARK_GRAY
|
||||
foregroundColor = ColorRGBa.PEACH_PUFF
|
||||
patternUnits = FillUnits.WORLD
|
||||
parameter("time", seconds*0.1)
|
||||
scale = 0.2
|
||||
crosses {
|
||||
width = 1.0
|
||||
weight = 0.2
|
||||
rotation = seconds * 45.0
|
||||
strokeWeight = cos(seconds) * 0.3 + 0.31
|
||||
}
|
||||
} + gradient<ColorRGBa> {
|
||||
stops[1.0] = ColorRGBa.BLACK
|
||||
stops[0.5] = ColorRGBa.WHITE
|
||||
stops[0.0] = ColorRGBa.WHITE
|
||||
conic { }
|
||||
} + clip {
|
||||
star {
|
||||
sides = 36
|
||||
sharpness = 0.1
|
||||
clipOuter = 0.05
|
||||
clipInner = -0.1
|
||||
radius = 0.4
|
||||
}
|
||||
}
|
||||
|
||||
drawer.rectangle(drawer.bounds.offsetEdges(-50.0))
|
||||
|
||||
// drawer.fill = ColorRGBa.WHITE
|
||||
// drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 196.0)
|
||||
// drawer.text("Patterns", 10.0, height / 2.0)
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user