Link program block parameters bidirectionally and add parameter link UI
This commit is contained in:
parent
a61a67cbf3
commit
6045800d11
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -11,7 +11,7 @@ class Program {
|
|||
var startBlock = StartBlock().apply {
|
||||
position = Point.ORIGIN
|
||||
}
|
||||
val blocks = mutableListOf<ProgramBlock>()
|
||||
val blocks = mutableListOf<ProgramBlock>(startBlock)
|
||||
|
||||
fun execute() {
|
||||
var currentBlock: ExecutableBlock? = startBlock.executionFlow.next
|
||||
|
|
|
@ -9,10 +9,21 @@ class ProgramBlockInput<Type: Any>(
|
|||
val identifier: Identifier,
|
||||
val type: ProgramType<Type>,
|
||||
val block: ProgramBlock,
|
||||
var source: ProgramBlockOutput<Type>? = null
|
||||
source: ProgramBlockOutput<Type>? = null
|
||||
) {
|
||||
|
||||
var source: ProgramBlockOutput<Type>? = source
|
||||
internal set
|
||||
|
||||
val value: Type?
|
||||
get() = source?.value
|
||||
|
||||
fun link(from: ProgramBlockOutput<Type>) {
|
||||
from.link(to = this)
|
||||
}
|
||||
|
||||
fun unlink() {
|
||||
source?.unlink(to = this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,4 +13,17 @@ class ProgramBlockOutput<Type: Any>(
|
|||
|
||||
lateinit var value: Type
|
||||
|
||||
private val _destinations = mutableListOf<ProgramBlockInput<Type>>()
|
||||
val destinations: List<ProgramBlockInput<Type>> = _destinations
|
||||
|
||||
fun link(to: ProgramBlockInput<Type>) {
|
||||
to.source = this
|
||||
_destinations.add(to)
|
||||
}
|
||||
|
||||
fun unlink(to: ProgramBlockInput<Type>) {
|
||||
to.source = null
|
||||
_destinations.remove(to)
|
||||
}
|
||||
|
||||
}
|
|
@ -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<ProgramBlockInput<*>, View>()
|
||||
val outputViews = mutableMapOf<ProgramBlockOutput<*>, 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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for (input in block.inputs) {
|
||||
// if (input.source == null) continue
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 896 B After Width: | Height: | Size: 923 B |
|
@ -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()
|
||||
|
||||
|
|
Loading…
Reference in New Issue