Compare commits

...

4 Commits

11 changed files with 115 additions and 84 deletions

View File

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

View File

@ -1,15 +1,21 @@
package net.shadowfacts.asmr.program package net.shadowfacts.asmr.program
import net.minecraft.util.Identifier
import net.minecraft.util.Language
import net.shadowfacts.cacao.geometry.Point import net.shadowfacts.cacao.geometry.Point
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
abstract class ProgramBlock { abstract class ProgramBlock(val identifier: Identifier) {
var position: Point = Point.ORIGIN var position: Point = Point.ORIGIN
abstract val inputs: Array<ProgramBlockInput<*>> abstract val inputs: Array<ProgramBlockInput<*>>
abstract val outputs: Array<ProgramBlockOutput<*>> abstract val outputs: Array<ProgramBlockOutput<*>>
fun translateName(): String {
return Language.getInstance().translate("programblock.${identifier.namespace}.${identifier.path}")
}
} }

View File

@ -1,6 +1,7 @@
package net.shadowfacts.asmr.program package net.shadowfacts.asmr.program
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.Language
/** /**
* @author shadowfacts * @author shadowfacts
@ -26,4 +27,8 @@ class ProgramBlockInput<Type: Any>(
source?.unlink(to = this) source?.unlink(to = this)
} }
fun translateName(): String {
return Language.getInstance().translate("programblock.param.${identifier.namespace}.${identifier.path}")
}
} }

View File

@ -1,6 +1,7 @@
package net.shadowfacts.asmr.program package net.shadowfacts.asmr.program
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.Language
/** /**
* @author shadowfacts * @author shadowfacts
@ -26,4 +27,8 @@ class ProgramBlockOutput<Type: Any>(
_destinations.remove(to) _destinations.remove(to)
} }
fun translateName(): String {
return Language.getInstance().translate("programblock.param.${identifier.namespace}.${identifier.path}")
}
} }

View File

@ -13,9 +13,11 @@ import net.shadowfacts.asmr.program.ProgramType
class ConstantBlock<Type: Any>( class ConstantBlock<Type: Any>(
val type: ProgramType<Type>, val type: ProgramType<Type>,
val value: Type val value: Type
): ProgramBlock() { ): ProgramBlock(
Identifier(ASMR.modid, "constant")
) {
val output = ProgramBlockOutput(Identifier(ASMR.modid, "constant_output"), type, this).apply { val output = ProgramBlockOutput(Identifier(ASMR.modid, "constant.output"), type, this).apply {
value = this@ConstantBlock.value value = this@ConstantBlock.value
} }

View File

@ -10,9 +10,11 @@ import net.shadowfacts.cacao.geometry.Point
*/ */
class PrintBlock<Type: Any>( class PrintBlock<Type: Any>(
val type: ProgramType<Type> val type: ProgramType<Type>
): ExecutableBlock() { ): ExecutableBlock(
Identifier(ASMR.modid, "print")
) {
val input = ProgramBlockInput(Identifier(ASMR.modid, "print_input"), type, this) val input = ProgramBlockInput(Identifier(ASMR.modid, "print.input"), type, this)
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()

View File

@ -1,5 +1,7 @@
package net.shadowfacts.asmr.program.blocks package net.shadowfacts.asmr.program.blocks
import net.minecraft.util.Identifier
import net.shadowfacts.asmr.ASMR
import net.shadowfacts.asmr.program.ExecutableBlock import net.shadowfacts.asmr.program.ExecutableBlock
import net.shadowfacts.asmr.program.ProgramBlockInput import net.shadowfacts.asmr.program.ProgramBlockInput
import net.shadowfacts.asmr.program.ProgramBlockOutput import net.shadowfacts.asmr.program.ProgramBlockOutput
@ -7,7 +9,9 @@ import net.shadowfacts.asmr.program.ProgramBlockOutput
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class StartBlock: ExecutableBlock() { class StartBlock: ExecutableBlock(
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()

View File

@ -14,7 +14,9 @@ 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() { ): ExecutableBlock(
Identifier(ASMR.modid, "binary_operator")
) {
init { init {
if (type != ProgramType.INT && type != ProgramType.FLOAT) { if (type != ProgramType.INT && type != ProgramType.FLOAT) {
@ -22,10 +24,10 @@ class BinaryOperatorBlock<Type: Any>(
} }
} }
val left = ProgramBlockInput(Identifier(ASMR.modid, "left_operand"), type, this) val left = ProgramBlockInput(Identifier(ASMR.modid, "binary_operator.left_operand"), type, this)
val right = ProgramBlockInput(Identifier(ASMR.modid, "right_operand"), type, this) val right = ProgramBlockInput(Identifier(ASMR.modid, "binary_operator.right_operand"), type, this)
val output = ProgramBlockOutput(Identifier(ASMR.modid, "result"), type, this) val output = ProgramBlockOutput(Identifier(ASMR.modid, "binary_operator.result"), type, this)
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)

View File

@ -38,6 +38,7 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
var xConstraint: Constraint? = null var xConstraint: Constraint? = null
var yConstraint: Constraint? = null var yConstraint: Constraint? = null
var didCreateWidthConstraint = false
override fun wasAdded() { override fun wasAdded() {
super.wasAdded() super.wasAdded()
@ -48,7 +49,7 @@ 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.javaClass.simpleName)) 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 val incomingTexture = if (block.executionFlow.prev == null) emptyConnection else executionConnection
incomingExecution = titleStack.addArrangedSubview(TextureView(incomingTexture), index = 0) incomingExecution = titleStack.addArrangedSubview(TextureView(incomingTexture), index = 0)
@ -59,7 +60,6 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
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
widthAnchor greaterThanOrEqualTo titleStack.widthAnchor
if (block is ExecutableBlock) { if (block is ExecutableBlock) {
incomingExecution!!.widthAnchor equalTo incomingExecution!!.heightAnchor incomingExecution!!.widthAnchor equalTo incomingExecution!!.heightAnchor
@ -67,21 +67,16 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
outgoingExeuction!!.widthAnchor equalTo outgoingExeuction!!.heightAnchor outgoingExeuction!!.widthAnchor equalTo outgoingExeuction!!.heightAnchor
outgoingExeuction!!.widthAnchor equalTo 7 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)) { for (i in 0 until max(block.inputs.size, block.outputs.size)) {
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(TextureView(if (input.source == null) emptyConnection else parameterConnection)) 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.identifier.toString())) val inputLabel = hStack.addArrangedSubview(Label(input.translateName()))
solver.dsl { solver.dsl {
hStack.heightAnchor equalTo inputLabel.heightAnchor hStack.heightAnchor equalTo inputLabel.heightAnchor
@ -96,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.identifier.toString(), textAlignment = Label.TextAlignment.RIGHT)) val outputLabel = hStack.addArrangedSubview(Label(output.translateName(), textAlignment = Label.TextAlignment.RIGHT))
val outputView = hStack.addArrangedSubview(TextureView(if (output.destinations.isEmpty()) emptyConnection else parameterConnection)) 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
@ -108,35 +104,6 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
} }
} }
// 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 { solver.dsl {
for (view in arrangedSubviews.drop(1)) { for (view in arrangedSubviews.drop(1)) {
widthAnchor.equalTo(view.widthAnchor, strength = STRONG) widthAnchor.equalTo(view.widthAnchor, strength = STRONG)
@ -147,9 +114,27 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
updateDraggingConstraints() updateDraggingConstraints()
} }
override fun didLayout() {
super.didLayout()
// we want to constrain this view's width to be the same as the widest view in the vertical stack,
// but we don't know what view that is until after all subviews have been laid-out
if (!didCreateWidthConstraint) {
didCreateWidthConstraint = true
arrangedSubviews.maxBy { it.bounds.width }?.let { widestSubview ->
solver.dsl {
widthAnchor equalTo widestSubview.widthAnchor
}
window!!.layout()
}
}
}
override fun mouseDragged(startPoint: Point, delta: Point, mouseButton: MouseButton): Boolean { override fun mouseDragged(startPoint: Point, delta: Point, mouseButton: MouseButton): Boolean {
block.position += delta block.position += delta
updateDraggingConstraints() updateDraggingConstraints()
window!!.layout()
return true return true
} }
@ -160,7 +145,6 @@ class ProgramBlockView(val block: ProgramBlock): StackView(Axis.VERTICAL, Distri
xConstraint = (leftAnchor equalTo superview!!.leftAnchor + block.position.x) xConstraint = (leftAnchor equalTo superview!!.leftAnchor + block.position.x)
yConstraint = (topAnchor equalTo superview!!.topAnchor + block.position.y) yConstraint = (topAnchor equalTo superview!!.topAnchor + block.position.y)
} }
window!!.layout()
} }
override fun drawContent(mouse: Point, delta: Float) { override fun drawContent(mouse: Point, delta: Float) {

View File

@ -13,7 +13,11 @@ import net.shadowfacts.cacao.view.View
*/ */
class ProgramCanvasView(val program: Program): View() { class ProgramCanvasView(val program: Program): View() {
lateinit var blocks: Map<ProgramBlock, ProgramBlockView> private lateinit var blocks: Map<ProgramBlock, ProgramBlockView>
// map of starting block to (start, end) points
private val executionFlowStartLinks = mutableMapOf<ExecutableBlock, Pair<Point, Point>>()
private val parameterLinks = mutableMapOf<ProgramBlock, Pair<Point, Point>>()
override fun wasAdded() { override fun wasAdded() {
super.wasAdded() super.wasAdded()
@ -26,48 +30,52 @@ class ProgramCanvasView(val program: Program): View() {
} }
} }
override fun drawContent(mouse: Point, delta: Float) { private fun updateBlockLinks() {
super.drawContent(mouse, delta) for ((startBlock, startView) in blocks) {
(startBlock as? ExecutableBlock)?.executionFlow?.next?.let { endBlock ->
blocks[endBlock]?.let { endView ->
for ((block, view) in blocks) { val outgoing = startView.outgoingExeuction!!
(block as? ExecutableBlock)?.executionFlow?.next?.let { next -> val incoming = endView.incomingExecution!!
blocks[next]?.let { nextView ->
val outgoing = view.outgoingExeuction!!
val incoming = nextView.incomingExecution!!
val start = outgoing.convert(outgoing.bounds.center, to = this) val start = outgoing.convert(outgoing.bounds.center, to = this)
val end = incoming.convert(outgoing.bounds.center, to = this) val end = incoming.convert(outgoing.bounds.center, to = this)
RenderHelper.drawLine(start, end, zIndex, 5f, Color.GREEN) executionFlowStartLinks[startBlock] = start to end
} }
} }
for (input in block.inputs) { for (output in startBlock.outputs) {
if (input.source == null) continue val outputView = startView.outputViews[output] ?: continue
val source = input.source!! val start = outputView.convert(outputView.bounds.center, to = this)
blocks[source.block]?.let { sourceView -> for (destination in output.destinations) {
view.inputViews[input]?.let { inputView -> val endView = blocks[destination.block] ?: continue
sourceView.outputViews[source]?.let { outputView -> val inputView = endView.inputViews[destination] ?: continue
val start = inputView.convert(inputView.bounds.center, to = this) val end = inputView.convert(inputView.bounds.center, to = this)
val end = outputView.convert(outputView.bounds.center, to = this)
RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED) parameterLinks[startBlock] = start to end
}
}
} }
// val start = block.position
// 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.RED)
} }
} }
} }
override fun didLayout() {
super.didLayout()
// we can't calculate the link points until layout is completed and all of the views have concrete positions/sizes
updateBlockLinks()
}
override fun drawContent(mouse: Point, delta: Float) {
super.drawContent(mouse, delta)
for ((start, end) in executionFlowStartLinks.values) {
RenderHelper.drawLine(start, end, zIndex, 5f, Color.GREEN)
}
for ((start, end) in parameterLinks.values) {
RenderHelper.drawLine(start, end, zIndex, 5f, Color.RED)
}
}
} }

View File

@ -0,0 +1,11 @@
{
"programblock.asmr.start": "Start",
"programblock.asmr.constant": "Constant",
"programblock.param.asmr.constant.output": "Output",
"programblock.asmr.print": "Print",
"programblock.param.asmr.print.input": "Print Value",
"programblock.asmr.binary_operator": "Binary Math Operator",
"programblock.param.asmr.binary_operator.left_operand": "Left Operand",
"programblock.param.asmr.binary_operator.right_operand": "Right Operand",
"programblock.param.asmr.binary_operator.result": "Result"
}