diff --git a/orx-compute-graph-nodes/README.md b/orx-compute-graph-nodes/README.md deleted file mode 100644 index 824b45bac..000000000 --- a/orx-compute-graph-nodes/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# orx-compute-graph-nodes - -A collection of nodes that can be used with `orx-compute-graph`. - -## List of nodes - -### Multi-platform - -Name | Description | Inputs | Outputs -----------------|-----------------------|--------|--------- -`filterNode` | Wrap around a `Filter`| | `image` -`fitImageNode` | Fit image to window bounds | `image` | `image` - -### JVM only - -Name | Description | Inputs | Outputs -----------------|-------------------|--------|--------- -`drawCacheNode` | Cache drawing in an internal color buffer, commonly used as the final stage node | | `image` -`dropImageNode` | Listen for window file drop events | | `image` - - - diff --git a/orx-compute-graph-nodes/build.gradle.kts b/orx-compute-graph-nodes/build.gradle.kts deleted file mode 100644 index c96e0bae7..000000000 --- a/orx-compute-graph-nodes/build.gradle.kts +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - org.openrndr.extra.convention.`kotlin-multiplatform` -} - -kotlin { - sourceSets { - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - implementation(project(":orx-parameters")) - implementation(project(":orx-shader-phrases")) - implementation(project(":orx-compute-graph")) - implementation(project(":orx-image-fit")) - implementation(libs.openrndr.application) - implementation(libs.openrndr.draw) - implementation(libs.openrndr.filter) - implementation(libs.kotlin.reflect) - } - } - } -} \ No newline at end of file diff --git a/orx-compute-graph-nodes/src/commonMain/kotlin/FilterNode.kt b/orx-compute-graph-nodes/src/commonMain/kotlin/FilterNode.kt deleted file mode 100644 index 03d09b44a..000000000 --- a/orx-compute-graph-nodes/src/commonMain/kotlin/FilterNode.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.openrndr.extra.computegraph.nodes - -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.Filter -import org.openrndr.draw.createEquivalent -import org.openrndr.extra.computegraph.ComputeGraph -import org.openrndr.extra.computegraph.ComputeNode -import org.openrndr.extra.computegraph.withKey - -fun ComputeGraph.filterNode( - filter: T, input: ComputeNode, inputKey: String = "image", outputKey: String = "image", - config: ComputeNode.(f: Filter) -> Unit -): ComputeNode { - return node { - name = "filter-${filter::class.simpleName}" - inputs = filter.parameters - config(filter) - val inputImage by input.outputs.withKey(inputKey) - var outputImage by outputs.withKey(outputKey) - outputImage = inputImage.createEquivalent() - compute { - filter.apply(inputImage, outputImage) - } - dependOn(input) - - } -} \ No newline at end of file diff --git a/orx-compute-graph-nodes/src/commonMain/kotlin/FitImageNode.kt b/orx-compute-graph-nodes/src/commonMain/kotlin/FitImageNode.kt deleted file mode 100644 index 129204fe6..000000000 --- a/orx-compute-graph-nodes/src/commonMain/kotlin/FitImageNode.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.openrndr.extra.computegraph.nodes - -import org.openrndr.Program -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.isolatedWithTarget -import org.openrndr.draw.renderTarget -import org.openrndr.extra.computegraph.ComputeGraph -import org.openrndr.extra.computegraph.ComputeNode -import org.openrndr.extra.computegraph.withKey -import org.openrndr.extra.imageFit.imageFit - -fun ComputeGraph.fitImageNode(program: Program, input: ComputeNode) : ComputeNode { - return node { - name = "fit-image" - val rt = renderTarget(program.width, program.height) { - colorBuffer() - } - val inputImage: ColorBuffer by input.outputs.withKey("image") - var outputImage:ColorBuffer by outputs.withKey("image") - outputImage = rt.colorBuffer(0) - compute { - program.drawer.isolatedWithTarget(rt) { - ortho(rt) - imageFit(inputImage, bounds) - } - } - dependOn(input) - } -} \ No newline at end of file diff --git a/orx-compute-graph-nodes/src/jvmMain/kotlin/DrawCacheNode.kt b/orx-compute-graph-nodes/src/jvmMain/kotlin/DrawCacheNode.kt deleted file mode 100644 index e12382535..000000000 --- a/orx-compute-graph-nodes/src/jvmMain/kotlin/DrawCacheNode.kt +++ /dev/null @@ -1,100 +0,0 @@ -package org.openrndr.extra.computegraph.nodes - -import io.github.oshai.kotlinlogging.KotlinLogging -import org.openrndr.KEY_SPACEBAR -import org.openrndr.Program -import org.openrndr.RequestAssetsEvent -import org.openrndr.color.ColorRGBa -import org.openrndr.draw.* -import org.openrndr.extra.computegraph.ComputeGraph -import org.openrndr.extra.computegraph.ComputeNode -import org.openrndr.extra.computegraph.withKey -import java.io.File - -val logger = KotlinLogging.logger { } - -@JvmRecord -private data class RenderTargetDescription(val width: Int, val height: Int, val contentScale: Double) - -private fun RenderTarget.description() = RenderTargetDescription(width, height, contentScale) - - -fun ComputeGraph.drawCacheNode( - program: Program, - inputNodes: List, - draw: Program.(node: ComputeNode) -> Unit -): ComputeNode { - return node { - var producingAssets: Boolean by inputs - producingAssets = false - - program.keyboard.keyDown.listen { - if (!it.propagationCancelled) { - logger.info { "requesting assets" } - if (it.key == KEY_SPACEBAR) { - program.requestAssets.trigger(RequestAssetsEvent(this, program)) - } - } - } - - var screenshotTarget = "" - program.produceAssets.listen { - producingAssets = true - screenshotTarget = "screenshots/${it.assetMetadata.assetBaseName}.png" - } - - name = "draw-cache" - var rt = renderTarget(program.width, program.height) { - colorBuffer() - depthBuffer() - } - var outputImage: ColorBuffer by outputs.withKey("image") - outputImage = rt.colorBuffer(0) - - var description: RenderTargetDescription by inputs - description = RenderTarget.active.description() - - update { - description = RenderTarget.active.description() - } - val defaultContentScale = program.window.contentScale - - compute { - rt.colorBuffer(0).destroy() - rt.depthBuffer?.destroy() - rt.detachColorAttachments() - rt.detachDepthBuffer() - rt.destroy() - rt = renderTarget( - program.width, - program.height, - contentScale = if (producingAssets) 6.0 else defaultContentScale - ) { - colorBuffer() - depthBuffer() - } - program.drawer.isolatedWithTarget(rt) { - clear(ColorRGBa.WHITE) - - draw(program, this@node) - } - outputImage = rt.colorBuffer(0) - println(outputImage) - - if (producingAssets) { - logger.info { "saving draw cache to file" } - val directory = File("screenshots") - if (!directory.exists()) { - directory.mkdirs() - } - - outputImage.saveToFile(File(screenshotTarget), async = false) - producingAssets = false - screenshotTarget = "" - } - } - for (input in inputNodes) { - dependOn(input) - } - } -} \ No newline at end of file diff --git a/orx-compute-graph-nodes/src/jvmMain/kotlin/DropImageNode.kt b/orx-compute-graph-nodes/src/jvmMain/kotlin/DropImageNode.kt deleted file mode 100644 index a32384f69..000000000 --- a/orx-compute-graph-nodes/src/jvmMain/kotlin/DropImageNode.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.openrndr.extra.computegraph.nodes - -import org.openrndr.Program -import org.openrndr.draw.ColorBuffer -import org.openrndr.draw.colorBuffer -import org.openrndr.draw.loadImage -import org.openrndr.extra.computegraph.ComputeGraph -import org.openrndr.extra.computegraph.ComputeNode -import java.io.File - -fun ComputeGraph.dropImageNode(program: Program): ComputeNode { - return node { - name = "drop-image" - var file: File by inputs - file = File("data/images/cheeta.jpg") - program.window.drop.listen { - file = File(it.files.first()) - } - var image: ColorBuffer by outputs - - fun loadFileOrEmpty() = if (file.exists()) loadImage(file) else colorBuffer(256, 256) - image = loadFileOrEmpty() - compute { - image.destroy() - image = loadFileOrEmpty() - } - } -} \ No newline at end of file diff --git a/orx-compute-graph/README.md b/orx-compute-graph/README.md deleted file mode 100644 index 93cc6a813..000000000 --- a/orx-compute-graph/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# orx-compute-graph - -A graph for computation. - -## Status - -In development. Things may change without prior notice. - -## Usage - -```kotlin -fun main() = application { - program { - val linkNode: ComputeNode - val cg = computeGraph { - val randomNode = node { - var seed: Int by inputs - seed = 0 - var points: List by outputs - points = emptyList() - compute { - val r = Random(seed) - points = (0 until 1000).map { - val x = r.nextDouble(0.0, width.toDouble()) - val y = r.nextDouble(0.0, height.toDouble()) - Vector2(x, y) - } - } - } - linkNode = node { - var seed: Int by inputs - seed = 0 - val points: List by randomNode.outputs - var links: List by outputs - compute { - val r = Random(seed) - val shuffled = points.shuffled(r) - links = shuffled.windowed(2, 2).map { - LineSegment(it[0], it[1]) - } - } - } - randomNode.dependOn(root) - linkNode.dependOn(randomNode) - } - cg.dispatch(dispatcher) {} - extend { - drawer.clear(ColorRGBa.WHITE) - drawer.stroke = ColorRGBa.BLACK - val links: List by linkNode.outputs - drawer.lineSegments(links) - } - } -} -``` diff --git a/orx-compute-graph/build.gradle.kts b/orx-compute-graph/build.gradle.kts deleted file mode 100644 index 4bc40f08e..000000000 --- a/orx-compute-graph/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - org.openrndr.extra.convention.`kotlin-multiplatform` -} - -kotlin { - sourceSets { - all { - languageSettings { - optIn("kotlin.RequiresOptIn") - } - } - @Suppress("UNUSED_VARIABLE") - val commonMain by getting { - dependencies { - api(libs.openrndr.event) - implementation(libs.kotlin.coroutines) - } - } - } -} \ No newline at end of file diff --git a/orx-compute-graph/src/commonMain/kotlin/ComputeGraph.kt b/orx-compute-graph/src/commonMain/kotlin/ComputeGraph.kt deleted file mode 100644 index bc9d29324..000000000 --- a/orx-compute-graph/src/commonMain/kotlin/ComputeGraph.kt +++ /dev/null @@ -1,178 +0,0 @@ -package org.openrndr.extra.computegraph - -import io.github.oshai.kotlinlogging.KotlinLogging -import kotlinx.coroutines.* -import org.openrndr.events.Event -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.jvm.JvmRecord -import kotlin.reflect.KProperty - -private val logger = KotlinLogging.logger { } - -@JvmRecord -data class ComputeEvent(val source: ComputeNode) - -open class ComputeNode(val graph: ComputeGraph, var computeFunction: suspend () -> Unit = {}) { - internal var updateFunction = {} - - var inputs = mutableMapOf() - val outputs = mutableMapOf() - var name = "unnamed-node-${this.hashCode()}" - private var lastInputsHash = inputs.hashCode() - var receivedComputeRequest = true - private val computeFinished = Event("compute-finished") - - fun needsRecompute(): Boolean { - return receivedComputeRequest || (inputs.hashCode() != lastInputsHash) - } - - fun dependOn(node: ComputeNode) { - graph.nodes.add(this) - graph.inbound.getOrPut(this) { mutableSetOf() }.add(node) - graph.outbound.getOrPut(node) { mutableSetOf() }.add(node) - - node.computeFinished.listen { - receivedComputeRequest = true - graph.requireCompute.add(this) - computeFinished.trigger(Unit) - } - } - - /** - * Set an update function, this function is called unconditionally by the compute-graph processor. This update - * function can be used to change values of [inputs] to trigger compute of the node. - */ - fun update(updateFunction: () -> Unit) { - this.updateFunction = updateFunction - } - - fun compute(computeFunction: suspend () -> Unit) { - this.computeFunction = computeFunction - } - - suspend fun execute() { - receivedComputeRequest = false - lastInputsHash = inputs.hashCode() - computeFunction() - computeFinished.trigger(Unit) - } - - override fun toString(): String { - return "ComputeNode(name='$name', receivedComputeRequest=$receivedComputeRequest)" - } -} - -class ComputeGraph { - val root = ComputeNode(this, {}) - internal val requireCompute = ArrayDeque() - - val nodes = mutableListOf() - val inbound = mutableMapOf>() - val outbound = mutableMapOf>() - - var job: Job? = null - fun node(builder: ComputeNode.() -> Unit): ComputeNode { - val cn = ComputeNode(this) - cn.builder() - return cn - } - - private var computeHash = -1 - - /** - * Run the compute graph in [context]. - * - * Eventually we likely want to separate compute-graph definitions from the compute-graph processor. - */ - @OptIn(DelicateCoroutinesApi::class) - fun dispatch(context: CoroutineDispatcher, delayBeforeCompute: Long = 500) { - var firstRodeo = true - GlobalScope.launch(context, CoroutineStart.DEFAULT) { - while (true) { - for (node in nodes) { - node.updateFunction() - } - val testHash = nodes.map { it.inputs.hashCode() }.reduce { acc, computeNode -> acc * 31 + computeNode } - if (testHash != computeHash) { - logger.info { "canceling job $job" } - job?.cancel() - job = null - } - if (testHash != computeHash && job == null) { - computeHash = testHash - job = GlobalScope.launch(context) { - if (!firstRodeo) { - delay(delayBeforeCompute) - } - logger.info { "compute started" } - compute() - logger.info { "compute finished" } - firstRodeo = false - } - } - yield() - } - } - } - - suspend fun compute() { - for (node in nodes) { - if (node.needsRecompute()) { - if (node !in requireCompute) { - logger.info { "node '${node.name}' needs computation" } - requireCompute.add(node) - } - } - } - val processed = mutableListOf() - root.receivedComputeRequest = false - while (requireCompute.isNotEmpty()) { - val node = requireCompute.first { - val deps = (inbound[it] ?: emptyList()) - if (deps.isEmpty()) { - true - } else { - deps.none { dep -> dep in requireCompute } - } - } - requireCompute.remove(node) - if (node !in processed) { - logger.info { "computing ${node.name}" } - node.execute() - processed.add(node) - } - } - } -} - -@OptIn(ExperimentalContracts::class) -fun computeGraph(builder: ComputeGraph.() -> Unit): ComputeGraph { - contract { - callsInPlace(builder, InvocationKind.EXACTLY_ONCE) - } - val cg = ComputeGraph() - cg.builder() - return cg -} - - -class MutableMapKeyReference(private val map: MutableMap, private val key: String) { - operator fun getValue(any: Any?, property: KProperty<*>): T { - @Suppress("UNCHECKED_CAST") - return map[key] as T - } - - operator fun setValue(any: Any?, property: KProperty<*>, value: Any) { - @Suppress("UNCHECKED_CAST") - map[key] = value as T - } -} - -/** - * Create a map delegation by [key] - */ -fun MutableMap.withKey(key: String): MutableMapKeyReference { - return MutableMapKeyReference(this, key) -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 44838cc04..754754b91 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,8 +22,6 @@ include( "orx-color", "orx-composition", "orx-compositor", - "orx-compute-graph", - "orx-compute-graph-nodes", "orx-delegate-magic", "orx-jvm:orx-dnk3", "orx-easing",