Compare commits

..

No commits in common. "3afc217a0bfc180b0796079c57cd2f9142a69235" and "971c41ed2fde1e1708fa9e8b5b80e9187791f85b" have entirely different histories.

20 changed files with 85 additions and 499 deletions

View File

@ -25,17 +25,17 @@ class ManagerBlockEntity: BlockEntity(ASMR.managerEntityType) {
it.position = Point(0.0, 120.0) it.position = Point(0.0, 120.0)
} }
val divide = program.addBlock(BinaryOperatorBlock(INT, DIVIDE)) { val divide = program.addBlock(BinaryOperatorBlock(INT, DIVIDE)) {
// it.left.link(from = left.output) it.left.link(from = left.output)
// it.right.link(from = right.output) it.right.link(from = right.output)
it.position = Point(120.0, 0.0) it.position = Point(120.0, 0.0)
} }
val print = program.addBlock(PrintBlock(INT)) { val print = program.addBlock(PrintBlock(INT)) {
// it.input.link(from = divide.output) it.input.link(from = divide.output)
it.position = Point(240.0, 0.0) it.position = Point(240.0, 0.0)
} }
program.startBlock.link(divide) program.startBlock.executionFlow.link(divide)
divide.link(print) divide.executionFlow.link(print)
} }
fun activate() { fun activate() {

View File

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

View File

@ -0,0 +1,16 @@
package net.shadowfacts.asmr.program
/**
* @author shadowfacts
*/
open class ExecutionFlow(val block: ExecutableBlock) {
open var next: ExecutableBlock? = null
open var prev: ExecutableBlock? = null
fun link(next: ExecutableBlock) {
this.next = next
next.executionFlow.prev = block
}
}

View File

@ -1,7 +1,6 @@
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
/** /**
@ -15,11 +14,11 @@ class Program {
val blocks = mutableListOf<ProgramBlock>(startBlock) val blocks = mutableListOf<ProgramBlock>(startBlock)
fun execute() { fun execute() {
var currentBlock: ExecutableBlock? = startBlock.next() var currentBlock: ExecutableBlock? = startBlock.executionFlow.next
while (currentBlock != null) { while (currentBlock != null) {
currentBlock.execute() currentBlock.execute()
currentBlock = currentBlock.next() currentBlock = currentBlock.executionFlow.next
} }
} }

View File

@ -27,13 +27,6 @@ class ProgramBlockOutput<Type: Any>(
_destinations.remove(to) _destinations.remove(to)
} }
fun unlinkAll() {
for (dest in destinations) {
dest.source = null
}
_destinations.clear()
}
fun translateName(): String { fun translateName(): String {
return Language.getInstance().translate("programblock.param.${identifier.namespace}.${identifier.path}") return Language.getInstance().translate("programblock.param.${identifier.namespace}.${identifier.path}")
} }

View File

@ -3,7 +3,7 @@ package net.shadowfacts.asmr.program
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class ProgramType<Type: Any> { class ProgramType<Type> {
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.asmr.program.execution.SimpleExecutableBlock import net.shadowfacts.cacao.geometry.Point
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class PrintBlock<Type: Any>( class PrintBlock<Type: Any>(
val type: ProgramType<Type> val type: ProgramType<Type>
): SimpleExecutableBlock( ): ExecutableBlock(
Identifier(ASMR.modid, "print") Identifier(ASMR.modid, "print")
) { ) {

View File

@ -2,13 +2,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.ExecutableBlock
import net.shadowfacts.asmr.program.execution.SimpleExecutableBlock import net.shadowfacts.asmr.program.ProgramBlockInput
import net.shadowfacts.asmr.program.ProgramBlockOutput
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class StartBlock: SimpleExecutableBlock( class StartBlock: ExecutableBlock(
Identifier(ASMR.modid, "start") Identifier(ASMR.modid, "start")
) { ) {

View File

@ -2,8 +2,10 @@ 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.ExecutableBlock
import net.shadowfacts.asmr.program.execution.SimpleExecutableBlock import net.shadowfacts.asmr.program.ProgramBlockInput
import net.shadowfacts.asmr.program.ProgramBlockOutput
import net.shadowfacts.asmr.program.ProgramType
import java.lang.RuntimeException import java.lang.RuntimeException
/** /**
@ -12,7 +14,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
): SimpleExecutableBlock( ): ExecutableBlock(
Identifier(ASMR.modid, "binary_operator") Identifier(ASMR.modid, "binary_operator")
) { ) {

View File

@ -1,20 +0,0 @@
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

@ -1,29 +0,0 @@
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

@ -1,31 +0,0 @@
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

@ -1,22 +0,0 @@
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,12 +1,11 @@
package net.shadowfacts.asmr.ui.block package net.shadowfacts.asmr.ui
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.execution.ExecutableBlock import net.shadowfacts.asmr.program.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
@ -14,6 +13,7 @@ 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,11 +30,11 @@ 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 incomingView: ProgramBlockExecutionView? = null var incomingExecution: View? = null
val outgoingViews = mutableMapOf<OutgoingExecutionFlow, ProgramBlockExecutionView>() var outgoingExeuction: View? = null
val inputViews = mutableMapOf<ProgramBlockInput<*>, ProgramBlockParamView>() val inputViews = mutableMapOf<ProgramBlockInput<*>, View>()
val outputViews = mutableMapOf<ProgramBlockOutput<*>, ProgramBlockParamView>() val outputViews = mutableMapOf<ProgramBlockOutput<*>, View>()
var xConstraint: Constraint? = null var xConstraint: Constraint? = null
var yConstraint: Constraint? = null var yConstraint: Constraint? = null
@ -49,33 +49,23 @@ 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))
if (block is ExecutableBlock) {
incomingView = titleStack.addArrangedSubview(ProgramBlockExecutionView(block.incoming))
}
val title = titleStack.addArrangedSubview(Label(block.translateName(), wrappingMode = Label.WrappingMode.NO_WRAP)) 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))
}
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) {
incomingView!!.widthAnchor equalTo incomingView!!.heightAnchor incomingExecution!!.widthAnchor equalTo incomingExecution!!.heightAnchor
incomingView!!.widthAnchor equalTo 7 incomingExecution!!.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(), wrappingMode = Label.WrappingMode.NO_WRAP, 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
}
} }
} }
@ -83,9 +73,10 @@ 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 inputView = hStack.addArrangedSubview(ProgramBlockParamView(input)) val inputTexture = if (input.source == null) emptyConnection else parameterConnection
val inputView = hStack.addArrangedSubview(TextureView(inputTexture))
inputViews[input] = inputView inputViews[input] = inputView
val inputLabel = hStack.addArrangedSubview(Label(input.translateName(), wrappingMode = Label.WrappingMode.NO_WRAP)) val inputLabel = hStack.addArrangedSubview(Label(input.translateName()))
solver.dsl { solver.dsl {
hStack.heightAnchor equalTo inputLabel.heightAnchor hStack.heightAnchor equalTo inputLabel.heightAnchor
@ -100,8 +91,9 @@ 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(), wrappingMode = Label.WrappingMode.NO_WRAP, textAlignment = Label.TextAlignment.RIGHT)) val outputLabel = hStack.addArrangedSubview(Label(output.translateName(), textAlignment = Label.TextAlignment.RIGHT))
val outputView = hStack.addArrangedSubview(ProgramBlockParamView(output)) val outputTexture = if (output.destinations.isEmpty()) emptyConnection else parameterConnection
val outputView = hStack.addArrangedSubview(TextureView(outputTexture))
outputViews[output] = outputView outputViews[output] = outputView
solver.dsl { solver.dsl {
hStack.heightAnchor equalTo outputLabel.heightAnchor hStack.heightAnchor equalTo outputLabel.heightAnchor
@ -132,7 +124,7 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
arrangedSubviews.maxBy { it.bounds.width }?.let { widestSubview -> arrangedSubviews.maxBy { it.bounds.width }?.let { widestSubview ->
solver.dsl { solver.dsl {
widthAnchor equalTo widestSubview.bounds.width widthAnchor equalTo widestSubview.widthAnchor
} }
window!!.layout() window!!.layout()
} }

View File

@ -1,16 +1,10 @@
package net.shadowfacts.asmr.ui package net.shadowfacts.asmr.ui
import net.shadowfacts.asmr.program.* import net.shadowfacts.asmr.program.ExecutableBlock
import net.shadowfacts.asmr.program.execution.ExecutableBlock import net.shadowfacts.asmr.program.Program
import net.shadowfacts.asmr.program.execution.IncomingExecutionFlow import net.shadowfacts.asmr.program.ProgramBlock
import net.shadowfacts.asmr.program.execution.OutgoingExecutionFlow
import net.shadowfacts.asmr.ui.block.ProgramBlockConnectionIndicatorView
import net.shadowfacts.asmr.ui.block.ProgramBlockExecutionView
import net.shadowfacts.asmr.ui.block.ProgramBlockParamView
import net.shadowfacts.asmr.ui.block.ProgramBlockView
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.Color import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.RenderHelper import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.view.View import net.shadowfacts.cacao.view.View
@ -25,8 +19,6 @@ class ProgramCanvasView(val program: Program): View() {
private val executionFlowStartLinks = mutableMapOf<ExecutableBlock, Pair<Point, Point>>() private val executionFlowStartLinks = mutableMapOf<ExecutableBlock, Pair<Point, Point>>()
private val parameterLinks = mutableMapOf<ProgramBlock, Pair<Point, Point>>() private val parameterLinks = mutableMapOf<ProgramBlock, Pair<Point, Point>>()
private var currentPartialLink: PartialLink? = null
override fun wasAdded() { override fun wasAdded() {
super.wasAdded() super.wasAdded()
@ -40,20 +32,15 @@ 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)?.let { startBlock -> (startBlock as? ExecutableBlock)?.executionFlow?.next?.let { endBlock ->
for (outgoing in startBlock.outgoing) { blocks[endBlock]?.let { endView ->
val outgoingView = startView.outgoingViews[outgoing] ?: continue val outgoing = startView.outgoingExeuction!!
val start = outgoingView.convert(outgoingView.bounds.center, to = this) val incoming = endView.incomingExecution!!
val incoming = outgoing.destination
if (incoming != null) {
val incomingBlockView = blocks[incoming.block] ?: continue
val incomingView = incomingBlockView.incomingView!!
val end = incomingView.convert(incomingView.bounds.center, to = this)
executionFlowStartLinks[startBlock] = start to end val start = outgoing.convert(outgoing.bounds.center, to = this)
} else { val end = incoming.convert(outgoing.bounds.center, to = this)
executionFlowStartLinks.remove(startBlock)
} executionFlowStartLinks[startBlock] = start to end
} }
} }
@ -89,202 +76,6 @@ class ProgramCanvasView(val program: Program): View() {
for ((start, end) in parameterLinks.values) { for ((start, end) in parameterLinks.values) {
RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED) RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED)
} }
val currentPartialLink = currentPartialLink
if (currentPartialLink != null) {
val color = if (currentPartialLink is ParamInputPartialLink<*> || currentPartialLink is ParamOutputPartialLink<*>) Color.RED else Color.GREEN
RenderHelper.drawLine(currentPartialLink.startPoint, mouse, zIndex, 5f, color)
}
} }
override fun mouseClicked(point: Point, mouseButton: MouseButton): Boolean {
if (mouseButton == MouseButton.RIGHT && currentPartialLink != null) {
this.currentPartialLink = null
return true
}
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, mouseButton)
}
}
for ((output, outputView) in blockView.outputViews) {
if (convert(point, to = outputView) in outputView.bounds) {
return outputViewClicked(block, output, outputView, mouseButton)
}
}
if (block is ExecutableBlock) {
if (convert(point, to = blockView.incomingView!!) in blockView.incomingView!!.bounds) {
return incomingExecutionViewClicked(block, block.incoming, blockView.incomingView!!, mouseButton)
}
for ((outgoing, outgoingView) in blockView.outgoingViews) {
if (convert(point, to = outgoingView) in outgoingView.bounds) {
return outgoingExecutionViewClicked(block, outgoing, outgoingView, mouseButton)
}
}
}
}
return super.mouseClicked(point, mouseButton)
}
private fun <Type: Any> inputViewClicked(block: ProgramBlock, input: ProgramBlockInput<Type>, inputView: ProgramBlockParamView, mouseButton: MouseButton): Boolean {
val currentPartialLink = currentPartialLink
return if (currentPartialLink == null) {
// no partial link in progress
when (mouseButton) {
MouseButton.LEFT -> {
// begin a new one
this.currentPartialLink = ParamInputPartialLink(input, inputView, inputView.convert(inputView.bounds.center, to = this))
true
}
MouseButton.RIGHT -> {
// clear clicked connection
val source = input.source ?: return false
val outputView = blocks[source.block]?.outputViews?.get(source) ?: return false
input.unlink()
inputView.updateTexture()
outputView.updateTexture()
updateBlockLinks()
true
}
else -> false
}
} 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()
updateBlockLinks()
this.currentPartialLink = null
true
} else {
// otherwise, click always fails
false
}
}
private fun <Type: Any> outputViewClicked(block: ProgramBlock, output: ProgramBlockOutput<Type>, outputView: ProgramBlockParamView, mouseButton: MouseButton): Boolean {
val currentPartialLink = currentPartialLink
return if (currentPartialLink == null) {
// no partial link in progress
when (mouseButton) {
MouseButton.LEFT -> {
// begin a new one
this.currentPartialLink = ParamOutputPartialLink(output, outputView, outputView.convert(outputView.bounds.center, to = this))
true
}
MouseButton.RIGHT -> {
// clear clicked connection
val inputViews = output.destinations.mapNotNull { blocks[it.block]?.inputViews?.get(it) }
output.unlinkAll()
outputView.updateTexture()
inputViews.forEach(ProgramBlockParamView::updateTexture)
updateBlockLinks()
true
}
else -> false
}
} 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()
updateBlockLinks()
this.currentPartialLink = null
true
} else {
// otherwise, click always fails
false
}
}
private fun incomingExecutionViewClicked(block: ProgramBlock, incoming: IncomingExecutionFlow, incomingView: ProgramBlockExecutionView, mouseButton: MouseButton): Boolean {
val currentPartialLink = currentPartialLink
return if (currentPartialLink == null) {
// no partial link in progress
when (mouseButton) {
MouseButton.LEFT -> {
// being a new one
this.currentPartialLink = IncomingExecutionPartialLink(incoming, incomingView, incomingView.convert(incomingView.bounds.center, to = this))
true
}
MouseButton.RIGHT -> {
// clear clicked connection
val outgoing = incoming.source ?: return false
val outgoingView = blocks[outgoing.block]?.outgoingViews?.get(outgoing) ?: return false
incoming.unlink()
incomingView.updateTexture()
outgoingView.updateTexture()
updateBlockLinks()
true
}
else -> false
}
} else if (currentPartialLink is OutgoingExecutionPartialLink) {
// current link in progress originated with an outgoing execution connection, so complete it
currentPartialLink.outgoing.link(incoming)
currentPartialLink.view.updateTexture()
incomingView.updateTexture()
updateBlockLinks()
this.currentPartialLink = null
true
} else {
// otherwise, click always fails
false
}
}
private fun outgoingExecutionViewClicked(block: ProgramBlock, outgoing: OutgoingExecutionFlow, outgoingView: ProgramBlockExecutionView, mouseButton: MouseButton): Boolean {
val currentPartialLink = currentPartialLink
return if (currentPartialLink == null) {
// no partial link in progress
when (mouseButton) {
MouseButton.LEFT -> {
// begin a new one
this.currentPartialLink = OutgoingExecutionPartialLink(outgoing, outgoingView, outgoingView.convert(outgoingView.bounds.center, to = this))
true
}
MouseButton.RIGHT -> {
// clear clicked connection
val incoming = outgoing.destination ?: return false
val incomingView = blocks[incoming.block]?.incomingView ?: return false
outgoing.unlink()
outgoingView.updateTexture()
incomingView.updateTexture()
updateBlockLinks()
true
}
else -> false
}
} else if (currentPartialLink is IncomingExecutionPartialLink) {
// current link in progress originated with an incoming execution connection, so complete it
currentPartialLink.incoming.link(outgoing)
currentPartialLink.view.updateTexture()
outgoingView.updateTexture()
updateBlockLinks()
this.currentPartialLink = null
true
} else {
// otherwise, click always fails
false
}
}
open class PartialLink(val view: ProgramBlockConnectionIndicatorView, 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 IncomingExecutionPartialLink(val incoming: IncomingExecutionFlow, view: ProgramBlockExecutionView, startPoint: Point): PartialLink(view, startPoint)
class OutgoingExecutionPartialLink(val outgoing: OutgoingExecutionFlow, view: ProgramBlockExecutionView, startPoint: Point): PartialLink(view, startPoint)
} }

View File

@ -1,12 +0,0 @@
package net.shadowfacts.asmr.ui.block
import net.shadowfacts.cacao.view.View
/**
* @author shadowfacts
*/
interface ProgramBlockConnectionIndicatorView {
fun updateTexture()
}

View File

@ -1,53 +0,0 @@
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(), ProgramBlockConnectionIndicatorView {
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
}
}
override fun updateTexture() {
val active = if (incoming != null) incoming.source != null else outgoing!!.destination != null
textureView.texture = if (active) executionConnection else emptyConnection
}
}

View File

@ -1,53 +0,0 @@
package net.shadowfacts.asmr.ui.block
import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.ProgramBlockInput
import net.shadowfacts.asmr.program.ProgramBlockOutput
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 ProgramBlockParamView(val input: ProgramBlockInput<*>?, val output: ProgramBlockOutput<*>?): View(), ProgramBlockConnectionIndicatorView {
companion object {
val emptyConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 0, v = 0)
val parameterConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 14, v = 0)
}
lateinit var textureView: TextureView
constructor(input: ProgramBlockInput<*>): this(input, null)
constructor(output: ProgramBlockOutput<*>): this(null, output)
init {
if (input == null && output == null) {
throw RuntimeException("One of input or output 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
}
}
override fun updateTexture() {
val active = if (input != null) input.source != null else output!!.destinations.isNotEmpty()
textureView.texture = if (active) parameterConnection else emptyConnection
}
}

View File

@ -1,6 +1,4 @@
{ {
"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.link(multiply) program.startBlock.executionFlow.link(multiply)
program.execute() program.execute()