169 lines
6.1 KiB
Kotlin
169 lines
6.1 KiB
Kotlin
package net.shadowfacts.asmr.ui
|
|
|
|
import net.shadowfacts.asmr.program.*
|
|
import net.shadowfacts.asmr.ui.block.ProgramBlockParamView
|
|
import net.shadowfacts.asmr.ui.block.ProgramBlockView
|
|
import net.shadowfacts.cacao.geometry.Point
|
|
import net.shadowfacts.cacao.util.Color
|
|
import net.shadowfacts.cacao.util.MouseButton
|
|
import net.shadowfacts.cacao.util.RenderHelper
|
|
import net.shadowfacts.cacao.view.View
|
|
|
|
/**
|
|
* @author shadowfacts
|
|
*/
|
|
class ProgramCanvasView(val program: Program): View() {
|
|
|
|
private lateinit var blocks: Map<ProgramBlock, ProgramBlockView>
|
|
|
|
// map of starting block to (start, end) points
|
|
private val executionFlowStartLinks = mutableMapOf<ExecutableBlock, Pair<Point, Point>>()
|
|
private val parameterLinks = mutableMapOf<ProgramBlock, Pair<Point, Point>>()
|
|
|
|
private var currentPartialLink: PartialLink? = null
|
|
|
|
override fun wasAdded() {
|
|
super.wasAdded()
|
|
|
|
zIndex = 5.0
|
|
|
|
blocks = program.blocks.associateWith { ProgramBlockView(it) }
|
|
blocks.values.forEach {
|
|
addSubview(it)
|
|
}
|
|
}
|
|
|
|
private fun updateBlockLinks() {
|
|
for ((startBlock, startView) in blocks) {
|
|
(startBlock as? ExecutableBlock)?.executionFlow?.next?.let { endBlock ->
|
|
blocks[endBlock]?.let { endView ->
|
|
val outgoing = startView.outgoingExeuction!!
|
|
val incoming = endView.incomingExecution!!
|
|
|
|
val start = outgoing.convert(outgoing.bounds.center, to = this)
|
|
val end = incoming.convert(outgoing.bounds.center, to = this)
|
|
|
|
executionFlowStartLinks[startBlock] = start to end
|
|
}
|
|
}
|
|
|
|
for (output in startBlock.outputs) {
|
|
val outputView = startView.outputViews[output] ?: continue
|
|
val start = outputView.convert(outputView.bounds.center, to = this)
|
|
|
|
for (destination in output.destinations) {
|
|
val endView = blocks[destination.block] ?: continue
|
|
val inputView = endView.inputViews[destination] ?: continue
|
|
val end = inputView.convert(inputView.bounds.center, to = this)
|
|
|
|
parameterLinks[startBlock] = start to end
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun didLayout() {
|
|
super.didLayout()
|
|
|
|
// we can't calculate the link points until layout is completed and all of the views have concrete positions/sizes
|
|
updateBlockLinks()
|
|
}
|
|
|
|
override fun drawContent(mouse: Point, delta: Float) {
|
|
super.drawContent(mouse, delta)
|
|
|
|
for ((start, end) in executionFlowStartLinks.values) {
|
|
RenderHelper.drawLine(start, end, zIndex, 5f, Color.GREEN)
|
|
}
|
|
|
|
for ((start, end) in parameterLinks.values) {
|
|
RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED)
|
|
}
|
|
|
|
val currentPartialLink = currentPartialLink
|
|
when (currentPartialLink) {
|
|
is ParamInputPartialLink<*>, is ParamOutputPartialLink<*> -> {
|
|
RenderHelper.drawLine(currentPartialLink.startPoint, mouse, zIndex, 5f, Color.RED)
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
|
|
for ((block, blockView) in blocks) {
|
|
if (convert(point, to = blockView) !in blockView.bounds) continue
|
|
|
|
for ((input, inputView) in blockView.inputViews) {
|
|
if (convert(point, to = inputView) in inputView.bounds) {
|
|
return inputViewClicked(block, input, inputView)
|
|
}
|
|
}
|
|
for ((output, outputView) in blockView.outputViews) {
|
|
if (convert(point, to = outputView) in outputView.bounds) {
|
|
return outputViewClicked(block, output, outputView)
|
|
}
|
|
}
|
|
}
|
|
|
|
return super.mouseClicked(point, mouseButton)
|
|
}
|
|
|
|
private fun <Type: Any> inputViewClicked(block: ProgramBlock, input: ProgramBlockInput<Type>, inputView: ProgramBlockParamView): Boolean {
|
|
val currentPartialLink = currentPartialLink
|
|
val result = if (currentPartialLink == null) {
|
|
// no partial link in progress, begin a new one
|
|
this.currentPartialLink = ParamInputPartialLink(input, inputView, inputView.convert(inputView.bounds.center, to = this))
|
|
true
|
|
} else if (currentPartialLink is ParamOutputPartialLink<*> && currentPartialLink.output.type == input.type) {
|
|
// current link in progress originated with an output param of same type as clicked input, so complete it
|
|
// we can ignore the unchecked cast because we checked it ourselves above by comparing ProgramTypes, but the compiler doesn't know that
|
|
@Suppress("NAME_SHADOWING", "UNCHECKED_CAST")
|
|
val currentPartialLink = currentPartialLink as ParamOutputPartialLink<Type>
|
|
currentPartialLink.output.link(to = input)
|
|
currentPartialLink.view.updateTexture()
|
|
inputView.updateTexture()
|
|
this.currentPartialLink = null
|
|
true
|
|
} else {
|
|
// otherwise, click always fails
|
|
false
|
|
}
|
|
|
|
if (result) {
|
|
updateBlockLinks()
|
|
}
|
|
return result
|
|
}
|
|
|
|
private fun <Type: Any> outputViewClicked(block: ProgramBlock, output: ProgramBlockOutput<Type>, outputView: ProgramBlockParamView): Boolean {
|
|
val currentPartialLink = currentPartialLink
|
|
val result = if (currentPartialLink == null) {
|
|
this.currentPartialLink = ParamOutputPartialLink(output, outputView, outputView.convert(outputView.bounds.center, to = this))
|
|
true
|
|
} else if (currentPartialLink is ParamInputPartialLink<*> && currentPartialLink.input.type == output.type) {
|
|
// current link in progress originated with an input param of same type as clicked output, so complete it
|
|
// we can ignore the unchecked cast because we checked it ourselves above by comparing ProgramTypes, but the compiler doesn't know that
|
|
@Suppress("NAME_SHADOWING", "UNCHECKED_CAST")
|
|
val currentPartialLink = currentPartialLink as ParamInputPartialLink<Type>
|
|
currentPartialLink.input.link(from = output)
|
|
currentPartialLink.view.updateTexture()
|
|
outputView.updateTexture()
|
|
this.currentPartialLink = null
|
|
true
|
|
} else {
|
|
// otherwise, click always fails
|
|
false
|
|
}
|
|
|
|
if (result) {
|
|
updateBlockLinks()
|
|
}
|
|
return result
|
|
}
|
|
|
|
open class PartialLink(val view: ProgramBlockParamView, val startPoint: Point)
|
|
class ParamInputPartialLink<Type: Any>(val input: ProgramBlockInput<Type>, view: ProgramBlockParamView, startPoint: Point): PartialLink(view, startPoint)
|
|
class ParamOutputPartialLink<Type: Any>(val output: ProgramBlockOutput<Type>, view: ProgramBlockParamView, startPoint: Point): PartialLink(view, startPoint)
|
|
// class ExecutionStartPartialLink<Type: Any>(val start: ExecutableBlock): PartialLink
|
|
// class ExecutionEndPartialLink<Type: Any>(val end: ExecutableBlock): PartialLink
|
|
|
|
} |