Refactor execution flow (again)

This commit is contained in:
Shadowfacts 2019-08-11 20:05:09 -04:00
parent 2c2c330db6
commit a941679197
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
17 changed files with 202 additions and 76 deletions

View File

@ -34,8 +34,8 @@ class ManagerBlockEntity: BlockEntity(ASMR.managerEntityType) {
it.position = Point(240.0, 0.0) it.position = Point(240.0, 0.0)
} }
program.startBlock.executionFlow.link(divide) program.startBlock.link(divide)
divide.executionFlow.link(print) divide.link(print)
} }
fun activate() { fun activate() {

View File

@ -1,14 +0,0 @@
package net.shadowfacts.asmr.program
import net.minecraft.util.Identifier
/**
* @author shadowfacts
*/
abstract class ExecutableBlock(identifier: Identifier): ProgramBlock(identifier) {
abstract val executionFlow: ExecutionFlow
abstract fun execute()
}

View File

@ -1,22 +0,0 @@
package net.shadowfacts.asmr.program
/**
* @author shadowfacts
*/
interface ExecutionFlow {
val block: ExecutableBlock
val next: ExecutableBlock?
val prev: ExecutableBlock?
}
class DirectExecutionFlow(override val block: ExecutableBlock): ExecutionFlow {
override var next: ExecutableBlock? = null
override var prev: ExecutableBlock? = null
fun link(next: ExecutableBlock) {
this.next = next
(next.executionFlow as? DirectExecutionFlow)?.let {
it.prev = block
}
}
}

View File

@ -1,6 +1,7 @@
package net.shadowfacts.asmr.program package net.shadowfacts.asmr.program
import net.shadowfacts.asmr.program.blocks.StartBlock import net.shadowfacts.asmr.program.blocks.StartBlock
import net.shadowfacts.asmr.program.execution.ExecutableBlock
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
/** /**
@ -14,11 +15,11 @@ class Program {
val blocks = mutableListOf<ProgramBlock>(startBlock) val blocks = mutableListOf<ProgramBlock>(startBlock)
fun execute() { fun execute() {
var currentBlock: ExecutableBlock? = startBlock.executionFlow.next var currentBlock: ExecutableBlock? = startBlock.next()
while (currentBlock != null) { while (currentBlock != null) {
currentBlock.execute() currentBlock.execute()
currentBlock = currentBlock.executionFlow.next currentBlock = currentBlock.next()
} }
} }

View File

@ -3,7 +3,7 @@ package net.shadowfacts.asmr.program
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class ProgramType<Type> { class ProgramType<Type: Any> {
companion object { companion object {
val INT = ProgramType<Int>() val INT = ProgramType<Int>()
val FLOAT = ProgramType<Float>() val FLOAT = ProgramType<Float>()

View File

@ -3,14 +3,14 @@ package net.shadowfacts.asmr.program.blocks
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.* import net.shadowfacts.asmr.program.*
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.asmr.program.execution.SimpleExecutableBlock
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class PrintBlock<Type: Any>( class PrintBlock<Type: Any>(
val type: ProgramType<Type> val type: ProgramType<Type>
): ExecutableBlock( ): SimpleExecutableBlock(
Identifier(ASMR.modid, "print") Identifier(ASMR.modid, "print")
) { ) {
@ -19,8 +19,6 @@ class PrintBlock<Type: Any>(
override val inputs: Array<ProgramBlockInput<*>> = arrayOf(input) override val inputs: Array<ProgramBlockInput<*>> = arrayOf(input)
override val outputs: Array<ProgramBlockOutput<*>> = arrayOf() override val outputs: Array<ProgramBlockOutput<*>> = arrayOf()
override val executionFlow = DirectExecutionFlow(this)
override fun execute() { override fun execute() {
println(input.value) println(input.value)
} }

View File

@ -3,19 +3,18 @@ package net.shadowfacts.asmr.program.blocks
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.* import net.shadowfacts.asmr.program.*
import net.shadowfacts.asmr.program.execution.SimpleExecutableBlock
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class StartBlock: ExecutableBlock( class StartBlock: SimpleExecutableBlock(
Identifier(ASMR.modid, "start") Identifier(ASMR.modid, "start")
) { ) {
override val inputs: Array<ProgramBlockInput<*>> = arrayOf() override val inputs: Array<ProgramBlockInput<*>> = arrayOf()
override val outputs: Array<ProgramBlockOutput<*>> = arrayOf() override val outputs: Array<ProgramBlockOutput<*>> = arrayOf()
override val executionFlow = DirectExecutionFlow(this)
override fun execute() { override fun execute() {
} }

View File

@ -3,6 +3,7 @@ package net.shadowfacts.asmr.program.blocks.math
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.* import net.shadowfacts.asmr.program.*
import net.shadowfacts.asmr.program.execution.SimpleExecutableBlock
import java.lang.RuntimeException import java.lang.RuntimeException
/** /**
@ -11,7 +12,7 @@ import java.lang.RuntimeException
class BinaryOperatorBlock<Type: Any>( class BinaryOperatorBlock<Type: Any>(
val type: ProgramType<Type>, val type: ProgramType<Type>,
val operation: Operation val operation: Operation
): ExecutableBlock( ): SimpleExecutableBlock(
Identifier(ASMR.modid, "binary_operator") Identifier(ASMR.modid, "binary_operator")
) { ) {
@ -29,8 +30,6 @@ class BinaryOperatorBlock<Type: Any>(
override val inputs: Array<ProgramBlockInput<*>> = arrayOf(left, right) override val inputs: Array<ProgramBlockInput<*>> = arrayOf(left, right)
override val outputs: Array<ProgramBlockOutput<*>> = arrayOf(output) override val outputs: Array<ProgramBlockOutput<*>> = arrayOf(output)
override val executionFlow = DirectExecutionFlow(this)
override fun execute() { override fun execute() {
if (type == ProgramType.INT) { if (type == ProgramType.INT) {
val left = left.value as Int val left = left.value as Int

View File

@ -0,0 +1,20 @@
package net.shadowfacts.asmr.program.execution
import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.ProgramBlock
/**
* @author shadowfacts
*/
abstract class ExecutableBlock(identifier: Identifier): ProgramBlock(identifier) {
val incoming = IncomingExecutionFlow(Identifier(ASMR.modid, "incoming"), this)
abstract val outgoing: Array<OutgoingExecutionFlow>
abstract fun execute()
abstract fun next(): ExecutableBlock?
}

View File

@ -0,0 +1,29 @@
package net.shadowfacts.asmr.program.execution
import net.minecraft.util.Identifier
import net.minecraft.util.Language
/**
* @author shadowfacts
*/
class IncomingExecutionFlow(
val identifier: Identifier,
val block: ExecutableBlock
) {
var source: OutgoingExecutionFlow? = null
internal set
fun link(from: OutgoingExecutionFlow) {
from.link(to = this)
}
fun unlink() {
source?.unlink()
}
fun translateName(): String {
return Language.getInstance().translate("programblock.execution.${identifier.namespace}.${identifier.path}")
}
}

View File

@ -0,0 +1,31 @@
package net.shadowfacts.asmr.program.execution
import net.minecraft.util.Identifier
import net.minecraft.util.Language
/**
* @author shadowfacts
*/
class OutgoingExecutionFlow(
val identifier: Identifier,
val block: ExecutableBlock
) {
var destination: IncomingExecutionFlow? = null
private set
fun link(to: IncomingExecutionFlow) {
destination = to
to.source = this
}
fun unlink() {
destination?.source = null
destination = null
}
fun translateName(): String {
return Language.getInstance().translate("programblock.execution.${identifier.namespace}.${identifier.path}")
}
}

View File

@ -0,0 +1,22 @@
package net.shadowfacts.asmr.program.execution
import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR
/**
* @author shadowfacts
*/
abstract class SimpleExecutableBlock(identifier: Identifier): ExecutableBlock(identifier) {
val outgoingFlow = OutgoingExecutionFlow(Identifier(ASMR.modid, "outgoing"), this)
override val outgoing = arrayOf(outgoingFlow)
override fun next(): ExecutableBlock? {
return outgoingFlow.destination?.block
}
fun link(to: ExecutableBlock) {
outgoingFlow.link(to.incoming)
}
}

View File

@ -1,6 +1,7 @@
package net.shadowfacts.asmr.ui package net.shadowfacts.asmr.ui
import net.shadowfacts.asmr.program.* import net.shadowfacts.asmr.program.*
import net.shadowfacts.asmr.program.execution.ExecutableBlock
import net.shadowfacts.asmr.ui.block.ProgramBlockParamView import net.shadowfacts.asmr.ui.block.ProgramBlockParamView
import net.shadowfacts.asmr.ui.block.ProgramBlockView import net.shadowfacts.asmr.ui.block.ProgramBlockView
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
@ -35,13 +36,14 @@ class ProgramCanvasView(val program: Program): View() {
private fun updateBlockLinks() { private fun updateBlockLinks() {
for ((startBlock, startView) in blocks) { for ((startBlock, startView) in blocks) {
(startBlock as? ExecutableBlock)?.executionFlow?.next?.let { endBlock -> (startBlock as? ExecutableBlock)?.let { startBlock ->
blocks[endBlock]?.let { endView -> for (outgoing in startBlock.outgoing) {
val outgoing = startView.outgoingExeuction!! val outgoingView = startView.outgoingViews[outgoing] ?: continue
val incoming = endView.incomingExecution!! val start = outgoingView.convert(outgoingView.bounds.center, to = this)
val incoming = outgoing.destination ?: continue
val start = outgoing.convert(outgoing.bounds.center, to = this) val incomingBlockView = blocks[incoming.block] ?: continue
val end = incoming.convert(outgoing.bounds.center, to = this) val incomingView = incomingBlockView.incomingView!!
val end = incomingView.convert(incomingView.bounds.center, to = this)
executionFlowStartLinks[startBlock] = start to end executionFlowStartLinks[startBlock] = start to end
} }

View File

@ -0,0 +1,53 @@
package net.shadowfacts.asmr.ui.block
import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.execution.IncomingExecutionFlow
import net.shadowfacts.asmr.program.execution.OutgoingExecutionFlow
import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.View
import net.shadowfacts.kiwidsl.dsl
import java.lang.RuntimeException
/**
* @author shadowfacts
*/
class ProgramBlockExecutionView(val incoming: IncomingExecutionFlow?, val outgoing: OutgoingExecutionFlow?): View() {
companion object {
val emptyConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 0, v = 0)
val executionConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 7, v = 0)
}
lateinit var textureView: TextureView
constructor(incoming: IncomingExecutionFlow?): this(incoming, null)
constructor(outgoing: OutgoingExecutionFlow?): this(null, outgoing)
init {
if (incoming == null && outgoing == null) {
throw RuntimeException("One of incoming or outgoing must be non-null")
}
}
override fun wasAdded() {
super.wasAdded()
textureView = addSubview(TextureView(emptyConnection))
updateTexture()
solver.dsl {
textureView.leftAnchor equalTo leftAnchor
textureView.rightAnchor equalTo rightAnchor
textureView.topAnchor equalTo topAnchor
textureView.bottomAnchor equalTo bottomAnchor
}
}
fun updateTexture() {
val active = if (incoming != null) incoming.source != null else outgoing!!.destination != null
textureView.texture = if (active) executionConnection else emptyConnection
}
}

View File

@ -2,10 +2,11 @@ package net.shadowfacts.asmr.ui.block
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.ExecutableBlock import net.shadowfacts.asmr.program.execution.ExecutableBlock
import net.shadowfacts.asmr.program.ProgramBlock import net.shadowfacts.asmr.program.ProgramBlock
import net.shadowfacts.asmr.program.ProgramBlockInput import net.shadowfacts.asmr.program.ProgramBlockInput
import net.shadowfacts.asmr.program.ProgramBlockOutput import net.shadowfacts.asmr.program.ProgramBlockOutput
import net.shadowfacts.asmr.program.execution.OutgoingExecutionFlow
import net.shadowfacts.cacao.geometry.Axis import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
@ -13,7 +14,6 @@ import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.texture.Texture import net.shadowfacts.cacao.util.texture.Texture
import net.shadowfacts.cacao.view.Label import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.StackView import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.view.View
import net.shadowfacts.kiwidsl.dsl import net.shadowfacts.kiwidsl.dsl
import no.birkett.kiwi.Constraint import no.birkett.kiwi.Constraint
@ -30,8 +30,8 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
val parameterConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 14, v = 0) val parameterConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 14, v = 0)
} }
var incomingExecution: View? = null var incomingView: ProgramBlockExecutionView? = null
var outgoingExeuction: View? = null val outgoingViews = mutableMapOf<OutgoingExecutionFlow, ProgramBlockExecutionView>()
val inputViews = mutableMapOf<ProgramBlockInput<*>, ProgramBlockParamView>() val inputViews = mutableMapOf<ProgramBlockInput<*>, ProgramBlockParamView>()
val outputViews = mutableMapOf<ProgramBlockOutput<*>, ProgramBlockParamView>() val outputViews = mutableMapOf<ProgramBlockOutput<*>, ProgramBlockParamView>()
@ -49,23 +49,33 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
zIndex = 10.0 zIndex = 10.0
val titleStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER)) val titleStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER))
val title = titleStack.addArrangedSubview(Label(block.translateName(), wrappingMode = Label.WrappingMode.NO_WRAP))
if (block is ExecutableBlock) { if (block is ExecutableBlock) {
val incomingTexture = if (block.executionFlow.prev == null) emptyConnection else executionConnection incomingView = titleStack.addArrangedSubview(ProgramBlockExecutionView(block.incoming))
incomingExecution = titleStack.addArrangedSubview(TextureView(incomingTexture), index = 0)
val outgoingTexture = if (block.executionFlow.next == null) emptyConnection else executionConnection
outgoingExeuction = titleStack.addArrangedSubview(TextureView(outgoingTexture))
} }
val title = titleStack.addArrangedSubview(Label(block.translateName(), wrappingMode = Label.WrappingMode.NO_WRAP))
solver.dsl { solver.dsl {
// we have to constrain the titleStack height, because it's distribution is set to CENTER, so it doesn't do it itself // we have to constrain the titleStack height, because it's distribution is set to CENTER, so it doesn't do it itself
titleStack.heightAnchor equalTo title.heightAnchor titleStack.heightAnchor equalTo title.heightAnchor
if (block is ExecutableBlock) { if (block is ExecutableBlock) {
incomingExecution!!.widthAnchor equalTo incomingExecution!!.heightAnchor incomingView!!.widthAnchor equalTo incomingView!!.heightAnchor
incomingExecution!!.widthAnchor equalTo 7 incomingView!!.widthAnchor equalTo 7
outgoingExeuction!!.widthAnchor equalTo outgoingExeuction!!.heightAnchor }
outgoingExeuction!!.widthAnchor equalTo 7 }
if (block is ExecutableBlock) {
for (outgoing in block.outgoing) {
val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER))
val outgoingLabel = hStack.addArrangedSubview(Label(outgoing.translateName(), textAlignment = Label.TextAlignment.RIGHT))
val outgoingView = hStack.addArrangedSubview(ProgramBlockExecutionView(outgoing))
outgoingViews[outgoing] = outgoingView
solver.dsl {
hStack.heightAnchor equalTo outgoingLabel.heightAnchor
outgoingView.widthAnchor equalTo outgoingView.heightAnchor
outgoingView.widthAnchor equalTo 7
}
} }
} }
@ -73,8 +83,6 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER)) val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER))
block.inputs.getOrNull(i)?.let { input -> block.inputs.getOrNull(i)?.let { input ->
// val inputTexture = if (input.source == null) emptyConnection else parameterConnection
// val inputView = hStack.addArrangedSubview(TextureView(inputTexture))
val inputView = hStack.addArrangedSubview(ProgramBlockParamView(input)) val inputView = hStack.addArrangedSubview(ProgramBlockParamView(input))
inputViews[input] = inputView inputViews[input] = inputView
val inputLabel = hStack.addArrangedSubview(Label(input.translateName())) val inputLabel = hStack.addArrangedSubview(Label(input.translateName()))
@ -93,8 +101,6 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
block.outputs.getOrNull(i)?.let { output -> block.outputs.getOrNull(i)?.let { output ->
val outputLabel = hStack.addArrangedSubview(Label(output.translateName(), textAlignment = Label.TextAlignment.RIGHT)) val outputLabel = hStack.addArrangedSubview(Label(output.translateName(), textAlignment = Label.TextAlignment.RIGHT))
// val outputTexture = if (output.destinations.isEmpty()) emptyConnection else parameterConnection
// val outputView = hStack.addArrangedSubview(TextureView(outputTexture))
val outputView = hStack.addArrangedSubview(ProgramBlockParamView(output)) val outputView = hStack.addArrangedSubview(ProgramBlockParamView(output))
outputViews[output] = outputView outputViews[output] = outputView
solver.dsl { solver.dsl {

View File

@ -1,4 +1,6 @@
{ {
"programblock.execution.asmr.incoming": "Incoming",
"programblock.execution.asmr.outgoing": "Outgoing",
"programblock.asmr.start": "Start", "programblock.asmr.start": "Start",
"programblock.asmr.constant": "Constant", "programblock.asmr.constant": "Constant",
"programblock.param.asmr.constant.output": "Output", "programblock.param.asmr.constant.output": "Output",

View File

@ -20,7 +20,7 @@ class ProgramTests {
it.right.link(from = two.output) it.right.link(from = two.output)
} }
program.startBlock.executionFlow.link(multiply) program.startBlock.link(multiply)
program.execute() program.execute()