[orx-shade-styles] Add pattern shade styles
This commit is contained in:
@@ -25,6 +25,7 @@ kotlin {
|
|||||||
implementation(project(":orx-noise"))
|
implementation(project(":orx-noise"))
|
||||||
implementation(project(":orx-shapes"))
|
implementation(project(":orx-shapes"))
|
||||||
implementation(project(":orx-image-fit"))
|
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