diff --git a/src/main/kotlin/net/shadowfacts/asmr/manager/ManagerBlockEntity.kt b/src/main/kotlin/net/shadowfacts/asmr/manager/ManagerBlockEntity.kt index cb5902d..fdd5491 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/manager/ManagerBlockEntity.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/manager/ManagerBlockEntity.kt @@ -25,17 +25,17 @@ class ManagerBlockEntity: BlockEntity(ASMR.managerEntityType) { it.position = Point(0.0, 120.0) } val divide = program.addBlock(BinaryOperatorBlock(INT, DIVIDE)) { - it.left.source = left.output - it.right.source = right.output + it.left.link(from = left.output) + it.right.link(from = right.output) it.position = Point(120.0, 0.0) } val print = program.addBlock(PrintBlock(INT)) { - it.input.source = divide.output + it.input.link(from = divide.output) it.position = Point(240.0, 0.0) } - program.startBlock.linkNext(divide) - divide.linkNext(print) + program.startBlock.executionFlow.link(divide) + divide.executionFlow.link(print) } fun activate() { diff --git a/src/main/kotlin/net/shadowfacts/asmr/program/ExecutableBlock.kt b/src/main/kotlin/net/shadowfacts/asmr/program/ExecutableBlock.kt index c01213c..00aa969 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/program/ExecutableBlock.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/program/ExecutableBlock.kt @@ -5,13 +5,8 @@ package net.shadowfacts.asmr.program */ abstract class ExecutableBlock: ProgramBlock() { - open val executionFlow = ExecutionFlow() + open val executionFlow = ExecutionFlow(this) abstract fun execute() - fun linkNext(next: ExecutableBlock) { - executionFlow.next = next - next.executionFlow.prev = this - } - } \ No newline at end of file diff --git a/src/main/kotlin/net/shadowfacts/asmr/program/ExecutionFlow.kt b/src/main/kotlin/net/shadowfacts/asmr/program/ExecutionFlow.kt index 932c2df..7ddf41e 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/program/ExecutionFlow.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/program/ExecutionFlow.kt @@ -3,9 +3,14 @@ package net.shadowfacts.asmr.program /** * @author shadowfacts */ -open class ExecutionFlow { +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 + } + } \ No newline at end of file diff --git a/src/main/kotlin/net/shadowfacts/asmr/program/Program.kt b/src/main/kotlin/net/shadowfacts/asmr/program/Program.kt index c5ac8ea..88b3c0f 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/program/Program.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/program/Program.kt @@ -11,7 +11,7 @@ class Program { var startBlock = StartBlock().apply { position = Point.ORIGIN } - val blocks = mutableListOf() + val blocks = mutableListOf(startBlock) fun execute() { var currentBlock: ExecutableBlock? = startBlock.executionFlow.next diff --git a/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockInput.kt b/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockInput.kt index 89bc2fd..8cbaa00 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockInput.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockInput.kt @@ -9,10 +9,21 @@ class ProgramBlockInput( val identifier: Identifier, val type: ProgramType, val block: ProgramBlock, - var source: ProgramBlockOutput? = null + source: ProgramBlockOutput? = null ) { + var source: ProgramBlockOutput? = source + internal set + val value: Type? get() = source?.value + fun link(from: ProgramBlockOutput) { + from.link(to = this) + } + + fun unlink() { + source?.unlink(to = this) + } + } diff --git a/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockOutput.kt b/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockOutput.kt index c4cc0c6..4229dbe 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockOutput.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/program/ProgramBlockOutput.kt @@ -13,4 +13,17 @@ class ProgramBlockOutput( lateinit var value: Type + private val _destinations = mutableListOf>() + val destinations: List> = _destinations + + fun link(to: ProgramBlockInput) { + to.source = this + _destinations.add(to) + } + + fun unlink(to: ProgramBlockInput) { + to.source = null + _destinations.remove(to) + } + } \ No newline at end of file diff --git a/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramBlockView.kt b/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramBlockView.kt index d1fc21f..522fce8 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramBlockView.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramBlockView.kt @@ -4,6 +4,8 @@ import net.minecraft.util.Identifier import net.shadowfacts.asmr.ASMR import net.shadowfacts.asmr.program.ExecutableBlock import net.shadowfacts.asmr.program.ProgramBlock +import net.shadowfacts.asmr.program.ProgramBlockInput +import net.shadowfacts.asmr.program.ProgramBlockOutput import net.shadowfacts.cacao.geometry.Axis import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.util.Color @@ -15,6 +17,7 @@ import net.shadowfacts.cacao.view.TextureView import net.shadowfacts.cacao.view.View import net.shadowfacts.kiwidsl.dsl import no.birkett.kiwi.Constraint +import kotlin.math.max /** * @author shadowfacts @@ -22,13 +25,17 @@ import no.birkett.kiwi.Constraint class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distribution.CENTER, spacing = 4.0) { companion object { - val executionFlowInactiveTexture = Texture(Identifier(ASMR.modid, "textures/gui/programmer/execution_flow.png"), u = 0, v = 0) - val executionFlowActiveTexture = Texture(Identifier(ASMR.modid, "textures/gui/programmer/execution_flow.png"), u = 7, v = 0) + 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) + val parameterConnection = Texture(Identifier(ASMR.modid, "textures/gui/programmer/program_connections.png"), u = 14, v = 0) } var incomingExecution: View? = null var outgoingExeuction: View? = null + val inputViews = mutableMapOf, View>() + val outputViews = mutableMapOf, View>() + var xConstraint: Constraint? = null var yConstraint: Constraint? = null @@ -43,27 +50,12 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri val titleStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER)) val title = titleStack.addArrangedSubview(Label(block.javaClass.simpleName)) if (block is ExecutableBlock) { - val incomingTexture = if (block.executionFlow.prev == null) executionFlowInactiveTexture else executionFlowActiveTexture + val incomingTexture = if (block.executionFlow.prev == null) emptyConnection else executionConnection incomingExecution = titleStack.addArrangedSubview(TextureView(incomingTexture), index = 0) - val outgoingTexture = if (block.executionFlow.next == null) executionFlowInactiveTexture else executionFlowActiveTexture + val outgoingTexture = if (block.executionFlow.next == null) emptyConnection else executionConnection outgoingExeuction = titleStack.addArrangedSubview(TextureView(outgoingTexture)) } - val pairs = block.inputs.zip(block.outputs) - val remainingInputs = if (block.inputs.size > block.outputs.size) block.inputs.drop(block.outputs.size) else listOf() - val remainingOutputs = if (block.outputs.size > block.inputs.size) block.outputs.drop(block.inputs.size) else listOf() - for ((input, output) in pairs) { - val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, spacing = 8.0)) - hStack.addArrangedSubview(Label(input.identifier.toString())) - hStack.addArrangedSubview(Label(output.identifier.toString())) - } - for (input in remainingInputs) { - addArrangedSubview(Label(input.identifier.toString())) - } - for (output in remainingOutputs) { - addArrangedSubview(Label(output.identifier.toString(), textAlignment = Label.TextAlignment.RIGHT)) - } - 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 @@ -76,11 +68,82 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri outgoingExeuction!!.widthAnchor equalTo 7 } + +// for (paramView in inputViews.values) { +// paramView.widthAnchor equalTo paramView.heightAnchor +// paramView.widthAnchor equalTo 7 +// } + } + + for (i in 0 until max(block.inputs.size, block.outputs.size)) { + val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER)) + + block.inputs.getOrNull(i)?.let { input -> + val inputView = hStack.addArrangedSubview(TextureView(if (input.source == null) emptyConnection else parameterConnection)) + inputViews[input] = inputView + val inputLabel = hStack.addArrangedSubview(Label(input.identifier.toString())) + solver.dsl { + hStack.heightAnchor equalTo inputLabel.heightAnchor + + inputView.widthAnchor equalTo inputView.heightAnchor + inputView.widthAnchor equalTo 7 + } + } + + val spacingView = hStack.addArrangedSubview(View()) + solver.dsl { + spacingView.widthAnchor equalTo 8 + } + + block.outputs.getOrNull(i)?.let { output -> + val outputLabel = hStack.addArrangedSubview(Label(output.identifier.toString(), textAlignment = Label.TextAlignment.RIGHT)) + val outputView = hStack.addArrangedSubview(TextureView(if (output.destinations.isEmpty()) emptyConnection else parameterConnection)) + outputViews[output] = outputView + solver.dsl { + hStack.heightAnchor equalTo outputLabel.heightAnchor + + outputView.widthAnchor equalTo outputView.heightAnchor + outputView.widthAnchor equalTo 7 + } + } + } + +// val pairs = block.inputs.zip(block.outputs) +// val remainingInputs = if (block.inputs.size > block.outputs.size) block.inputs.drop(block.outputs.size) else listOf() +// val remainingOutputs = if (block.outputs.size > block.inputs.size) block.outputs.drop(block.inputs.size) else listOf() +// for ((input, output) in pairs) { +// val hStack = addArrangedSubview(StackView(Axis.HORIZONTAL, Distribution.CENTER)) +// val inputView = hStack.addArrangedSubview(TextureView(if (input.source == null) emptyConnection else parameterConnection)) +// inputViews[input] = inputView +// val inputLabel = hStack.addArrangedSubview(Label(input.identifier.toString())) +// val spacingView = hStack.addArrangedSubview(View()) +// hStack.addArrangedSubview(Label(output.identifier.toString())) +//// outputViews[output] = hStack.addArrangedSubview(TextureView(if (output.destinations.isEmpty) emptyConnection else parameterConnection)) +// +// solver.dsl { +// hStack.heightAnchor equalTo inputLabel.heightAnchor +// +// inputView.widthAnchor equalTo inputView.heightAnchor +// inputView.widthAnchor equalTo 7 +// +// spacingView.widthAnchor equalTo 8 +// } +// } + +// for (input in remainingInputs) { +// addArrangedSubview(Label(input.identifier.toString())) +// } +// for (output in remainingOutputs) { +// addArrangedSubview(Label(output.identifier.toString(), textAlignment = Label.TextAlignment.RIGHT)) +// } + + solver.dsl { for (view in arrangedSubviews.drop(1)) { widthAnchor.equalTo(view.widthAnchor, strength = STRONG) } } + updateDraggingConstraints() } diff --git a/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramCanvasView.kt b/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramCanvasView.kt index 4547f68..804f9a7 100644 --- a/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramCanvasView.kt +++ b/src/main/kotlin/net/shadowfacts/asmr/ui/ProgramCanvasView.kt @@ -39,19 +39,34 @@ class ProgramCanvasView(val program: Program): View() { val start = outgoing.convert(outgoing.bounds.center, to = this) val end = incoming.convert(outgoing.bounds.center, to = this) - RenderHelper.drawLine(start, end, zIndex, 5f, Color(0x00ff00)) + RenderHelper.drawLine(start, end, zIndex, 5f, Color.GREEN) } } -// for (input in block.inputs) { -// if (input.source == null) continue + for (input in block.inputs) { + if (input.source == null) continue + val source = input.source!! + + blocks[source.block]?.let { sourceView -> + view.inputViews[input]?.let { inputView -> + sourceView.outputViews[source]?.let { outputView -> + val start = inputView.convert(inputView.bounds.center, to = this) + val end = outputView.convert(outputView.bounds.center, to = this) + + RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED) + } + } + } + // val start = block.position -// val source = input.source!! // val sourcePosition = source.block.position // val end = Point(sourcePosition.x + blocks[source.block]!!.bounds.width, sourcePosition.y) + +// val inputView = view.inputViews[input] +// val outputView = // -// RenderHelper.drawLine(start, end, zIndex, 5f, Color.WHITE) -// } +// RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED) + } } } diff --git a/src/main/kotlin/net/shadowfacts/cacao/util/Color.kt b/src/main/kotlin/net/shadowfacts/cacao/util/Color.kt index c0c9116..f7775d2 100644 --- a/src/main/kotlin/net/shadowfacts/cacao/util/Color.kt +++ b/src/main/kotlin/net/shadowfacts/cacao/util/Color.kt @@ -26,7 +26,9 @@ data class Color(val red: Int, val green: Int, val blue: Int, val alpha: Int = 2 val CLEAR = Color(0, alpha = 0) val WHITE = Color(0xffffff) val BLACK = Color(0) + val RED = Color(0xff0000) val GREEN = Color(0x00ff00) + val BLUE = Color(0x0000ff) } } \ No newline at end of file diff --git a/src/main/resources/assets/asmr/textures/gui/programmer/execution_flow.png b/src/main/resources/assets/asmr/textures/gui/programmer/program_connections.png similarity index 66% rename from src/main/resources/assets/asmr/textures/gui/programmer/execution_flow.png rename to src/main/resources/assets/asmr/textures/gui/programmer/program_connections.png index 61a9449..6cceeff 100644 Binary files a/src/main/resources/assets/asmr/textures/gui/programmer/execution_flow.png and b/src/main/resources/assets/asmr/textures/gui/programmer/program_connections.png differ diff --git a/src/test/kotlin/net/shadowfacts/asmr/program/ProgramTests.kt b/src/test/kotlin/net/shadowfacts/asmr/program/ProgramTests.kt index 1c723fe..8c52c91 100644 --- a/src/test/kotlin/net/shadowfacts/asmr/program/ProgramTests.kt +++ b/src/test/kotlin/net/shadowfacts/asmr/program/ProgramTests.kt @@ -16,11 +16,11 @@ class ProgramTests { val one = program.addBlock(ConstantBlock(ProgramType.INT, 3)) val two = program.addBlock(ConstantBlock(ProgramType.INT, 7)) val multiply = program.addBlock(BinaryOperatorBlock(ProgramType.INT, BinaryOperatorBlock.Operation.MULTIPLY)) { - it.left.source = one.output - it.right.source = two.output + it.left.link(from = one.output) + it.right.link(from = two.output) } - program.startBlock.linkNext(multiply) + program.startBlock.executionFlow.link(multiply) program.execute()