[orx-parameters, orx-property-watchers, orx-file-watcher, orx-gui] Add @PathParameter, file watcher delegates and property delegates
This commit is contained in:
3
orx-property-watchers/README.md
Normal file
3
orx-property-watchers/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# orx-property-watchers
|
||||
|
||||
Tools for setting up property watcher based pipelines
|
||||
34
orx-property-watchers/build.gradle.kts
Normal file
34
orx-property-watchers/build.gradle.kts
Normal file
@@ -0,0 +1,34 @@
|
||||
plugins {
|
||||
org.openrndr.extra.convention.`kotlin-multiplatform`
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
testRuns["test"].executionTask {
|
||||
useJUnitPlatform {
|
||||
includeEngines("spek2")
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(libs.openrndr.application)
|
||||
implementation(libs.openrndr.math)
|
||||
implementation(libs.kotlin.reflect)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation(libs.kluent)
|
||||
implementation(libs.spek.dsl)
|
||||
runtimeOnly(libs.spek.junit5)
|
||||
runtimeOnly(libs.kotlin.reflect)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package org.openrndr.extra.propertywatchers
|
||||
|
||||
import org.openrndr.events.Event
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KProperty0
|
||||
|
||||
/**
|
||||
* Property watcher delegate
|
||||
* @see watchingProperty
|
||||
*/
|
||||
class PropertyWatcherDelegate<V, R>(
|
||||
private val property: KProperty0<V>,
|
||||
private val valueChangedEvent: Event<V>,
|
||||
private val cleaner: ((R) -> Unit)? = null,
|
||||
val function: (V) -> R
|
||||
) {
|
||||
private var watchValue: V? = null
|
||||
private var value: R? = null
|
||||
operator fun getValue(any: Any, property: KProperty<*>): R {
|
||||
val ref = this.property.get()
|
||||
if (watchValue != ref) {
|
||||
watchValue = ref
|
||||
value?.let {
|
||||
cleaner?.invoke(it)
|
||||
}
|
||||
value = function(ref)
|
||||
}
|
||||
return value ?: error("no value?")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property watcher delegate
|
||||
* @see watchingProperties
|
||||
*/
|
||||
class PropertyWatcherDelegate2<V0, V1, R>(
|
||||
private val toWatch0: KProperty0<V0>,
|
||||
private val toWatch1: KProperty0<V1>,
|
||||
private val cleaner: ((R) -> Unit)? = null,
|
||||
private val function: (V0, V1) -> R
|
||||
) {
|
||||
private var watchValue0: V0? = null
|
||||
private var watchValue1: V1? = null
|
||||
private var value: R? = null
|
||||
operator fun getValue(any: Any, property: KProperty<*>): R {
|
||||
val ref0 = toWatch0.get()
|
||||
val ref1 = toWatch1.get()
|
||||
if (watchValue0 != ref0 || watchValue1 != ref1) {
|
||||
watchValue0 = ref0
|
||||
watchValue1 = ref1
|
||||
|
||||
value?.let {
|
||||
cleaner?.invoke(it)
|
||||
}
|
||||
value = function(ref0, ref1)
|
||||
}
|
||||
return value ?: error("no value?")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Property watcher delegate
|
||||
* @see watchingProperties
|
||||
*/
|
||||
class PropertyWatcherDelegate3<V0, V1, V2, R>(
|
||||
private val toWatch0: KProperty0<V0>,
|
||||
private val toWatch1: KProperty0<V1>,
|
||||
private val toWatch2: KProperty0<V2>,
|
||||
private val cleaner: ((R) -> Unit)? = null,
|
||||
private val function: (V0, V1, V2) -> R
|
||||
) {
|
||||
private var watchValue0: V0? = null
|
||||
private var watchValue1: V1? = null
|
||||
private var watchValue2: V2? = null
|
||||
private var value: R? = null
|
||||
operator fun getValue(any: Any, property: KProperty<*>): R {
|
||||
val ref0 = toWatch0.get()
|
||||
val ref1 = toWatch1.get()
|
||||
val ref2 = toWatch2.get()
|
||||
if (watchValue0 != ref0 || watchValue1 != ref1 || watchValue2 != ref2) {
|
||||
watchValue0 = ref0
|
||||
watchValue1 = ref1
|
||||
value?.let {
|
||||
cleaner?.invoke(it)
|
||||
}
|
||||
value = function(ref0, ref1, ref2)
|
||||
}
|
||||
return value ?: error("no value?")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegate property value to a function for which the value of a single property is watched
|
||||
* @param property the property for which to watch for value changes
|
||||
* @param function a function that maps the property value to a new value
|
||||
*/
|
||||
fun <V, R> watchingProperty(
|
||||
property: KProperty0<V>,
|
||||
cleaner: ((R) -> Unit)? = null,
|
||||
function: (value: V) -> R
|
||||
): PropertyWatcherDelegate<V, R> {
|
||||
return PropertyWatcherDelegate(property, Event("value-changed-${property.name}"), cleaner, function)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate property value to a function for which the values of 2 properties are watched
|
||||
* @param property0 the first property for which to watch for value changes
|
||||
* @param property1 the second property which to watch for value changes
|
||||
* @param function a function that maps the two property values to a new value
|
||||
*/
|
||||
fun <V0, V1, R> watchingProperties(
|
||||
property0: KProperty0<V0>,
|
||||
property1: KProperty0<V1>,
|
||||
cleaner: ((R) -> Unit)?,
|
||||
function: (value0: V0, value1: V1) -> R
|
||||
): PropertyWatcherDelegate2<V0, V1, R> {
|
||||
return PropertyWatcherDelegate2(property0, property1, cleaner, function)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate property value to a function for which the values of 3 properties are watched
|
||||
* @param property0 the first property for which to watch for value changes
|
||||
* @param property1 the second property which to watch for value changes
|
||||
* @param property2 the third property which to watch for value changes
|
||||
* @param function a function that maps the three property values to a new value
|
||||
*/
|
||||
fun <V0, V1, V2, R> watchingProperties(
|
||||
property0: KProperty0<V0>,
|
||||
property1: KProperty0<V1>,
|
||||
property2: KProperty0<V2>,
|
||||
cleaner: ((R) -> Unit)? = null,
|
||||
function: (value0: V0, value1: V1, value2: V2) -> R
|
||||
): PropertyWatcherDelegate3<V0, V1, V2, R> {
|
||||
return PropertyWatcherDelegate3(property0, property1, property2, cleaner, function)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import org.openrndr.application
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.grayscale
|
||||
import org.openrndr.draw.tint
|
||||
import org.openrndr.drawImage
|
||||
import org.openrndr.extra.propertywatchers.watchingImagePath
|
||||
import org.openrndr.extra.propertywatchers.watchingProperty
|
||||
|
||||
fun main() {
|
||||
application {
|
||||
program {
|
||||
val state = object {
|
||||
var path = "demo-data/images/image-001.png"
|
||||
val image by watchingImagePath(::path) {
|
||||
drawImage(it.width, it.height) {
|
||||
drawer.drawStyle.colorMatrix = grayscale()
|
||||
drawer.image(it)
|
||||
}
|
||||
}
|
||||
val redImage by watchingProperty(::image, cleaner = { it.destroy() }) {
|
||||
drawImage(it.width, it.height) {
|
||||
drawer.drawStyle.colorMatrix = tint(ColorRGBa.RED)
|
||||
drawer.image(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extend {
|
||||
drawer.image(state.redImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import org.openrndr.application
|
||||
import org.openrndr.extra.propertywatchers.watchingProperty
|
||||
|
||||
fun main() {
|
||||
application {
|
||||
program {
|
||||
val state = object {
|
||||
val x by watchingProperty(mouse::position) {
|
||||
it.x
|
||||
}
|
||||
|
||||
val xx by watchingProperty(::x) {
|
||||
it * it
|
||||
}
|
||||
}
|
||||
|
||||
extend {
|
||||
state.x
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
orx-property-watchers/src/jvmMain/kotlin/ImagePath.kt
Normal file
20
orx-property-watchers/src/jvmMain/kotlin/ImagePath.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
package org.openrndr.extra.propertywatchers
|
||||
|
||||
import org.openrndr.Program
|
||||
import org.openrndr.draw.ColorBuffer
|
||||
import org.openrndr.draw.loadImage
|
||||
import java.io.File
|
||||
import kotlin.reflect.KProperty0
|
||||
|
||||
fun Program.watchingImagePath(pathProperty: KProperty0<String>, imageTransform: (ColorBuffer) -> ColorBuffer = { it }) =
|
||||
watchingProperty(pathProperty, cleaner = { it.destroy() }) {
|
||||
val file = File(it)
|
||||
require(file.exists()) { "$it does not exist" }
|
||||
require(file.isFile) { "$it is not a file" }
|
||||
val image = loadImage(file)
|
||||
val transformedImage = imageTransform(image)
|
||||
if (image != transformedImage) {
|
||||
image.destroy()
|
||||
}
|
||||
transformedImage
|
||||
}
|
||||
Reference in New Issue
Block a user