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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package net.shadowfacts.asmr.program.blocks.math
import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.*
import net.shadowfacts.asmr.program.execution.SimpleExecutableBlock
import java.lang.RuntimeException
/**
@ -11,7 +12,7 @@ import java.lang.RuntimeException
class BinaryOperatorBlock<Type: Any>(
val type: ProgramType<Type>,
val operation: Operation
): ExecutableBlock(
): SimpleExecutableBlock(
Identifier(ASMR.modid, "binary_operator")
) {
@ -29,8 +30,6 @@ class BinaryOperatorBlock<Type: Any>(
override val inputs: Array<ProgramBlockInput<*>> = arrayOf(left, right)
override val outputs: Array<ProgramBlockOutput<*>> = arrayOf(output)
override val executionFlow = DirectExecutionFlow(this)
override fun execute() {
if (type == ProgramType.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
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.ProgramBlockView
import net.shadowfacts.cacao.geometry.Point
@ -35,13 +36,14 @@ class ProgramCanvasView(val program: Program): View() {
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)
(startBlock as? ExecutableBlock)?.let { startBlock ->
for (outgoing in startBlock.outgoing) {
val outgoingView = startView.outgoingViews[outgoing] ?: continue
val start = outgoingView.convert(outgoingView.bounds.center, to = this)
val incoming = outgoing.destination ?: continue
val incomingBlockView = blocks[incoming.block] ?: continue
val incomingView = incomingBlockView.incomingView!!
val end = incomingView.convert(incomingView.bounds.center, to = this)
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.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.ProgramBlockInput
import net.shadowfacts.asmr.program.ProgramBlockOutput
import net.shadowfacts.asmr.program.execution.OutgoingExecutionFlow
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.geometry.Point
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.view.Label
import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.TextureView
import net.shadowfacts.cacao.view.View
import net.shadowfacts.kiwidsl.dsl
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)
}
var incomingExecution: View? = null
var outgoingExeuction: View? = null
var incomingView: ProgramBlockExecutionView? = null
val outgoingViews = mutableMapOf<OutgoingExecutionFlow, ProgramBlockExecutionView>()
val inputViews = mutableMapOf<ProgramBlockInput<*>, ProgramBlockParamView>()
val outputViews = mutableMapOf<ProgramBlockOutput<*>, ProgramBlockParamView>()
@ -49,23 +49,33 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
zIndex = 10.0
val titleStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER))
val title = titleStack.addArrangedSubview(Label(block.translateName(), wrappingMode = Label.WrappingMode.NO_WRAP))
if (block is ExecutableBlock) {
val incomingTexture = if (block.executionFlow.prev == null) emptyConnection else executionConnection
incomingExecution = titleStack.addArrangedSubview(TextureView(incomingTexture), index = 0)
val outgoingTexture = if (block.executionFlow.next == null) emptyConnection else executionConnection
outgoingExeuction = titleStack.addArrangedSubview(TextureView(outgoingTexture))
incomingView = titleStack.addArrangedSubview(ProgramBlockExecutionView(block.incoming))
}
val title = titleStack.addArrangedSubview(Label(block.translateName(), wrappingMode = Label.WrappingMode.NO_WRAP))
solver.dsl {
// 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
if (block is ExecutableBlock) {
incomingExecution!!.widthAnchor equalTo incomingExecution!!.heightAnchor
incomingExecution!!.widthAnchor equalTo 7
outgoingExeuction!!.widthAnchor equalTo outgoingExeuction!!.heightAnchor
outgoingExeuction!!.widthAnchor equalTo 7
incomingView!!.widthAnchor equalTo incomingView!!.heightAnchor
incomingView!!.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))
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))
inputViews[input] = inputView
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 ->
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))
outputViews[output] = outputView
solver.dsl {

View File

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

View File

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