Add relays

This commit is contained in:
LeshaInc 2020-06-04 18:55:10 +03:00
parent d3642d0800
commit c8decda297
No known key found for this signature in database
GPG Key ID: B4855290FC36DE72
17 changed files with 334 additions and 107 deletions

@ -1 +1 @@
Subproject commit 69c4076ae6dd199c403bbfb564c5e54d5065fbcc
Subproject commit 2ea81581884e6f766dc8477af54eb645ab63a5a7

BIN
sprites/nodes/Relay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -1,6 +1,6 @@
BackgroundPattern 0 0 304 304
Empty 337 197 1 1
ShadowBorder 305 197 1 24
Empty 337 204 1 1
ShadowBorder 305 204 1 24
ShadowCorner 424 0 24 24
buttons/PowerOff 441 25 18 18
buttons/PowerOn 460 25 18 18
@ -78,18 +78,19 @@ nodes/ComputerActivityOverlay 356 180 16 16
nodes/ComputerErrorOverlay 373 180 16 16
nodes/ComputerOnOverlay 390 180 16 16
nodes/NewNode 407 180 16 16
nodes/Screen 424 180 16 16
nodes/ScreenOnOverlay 441 180 16 16
screen/BorderB 310 197 2 8
screen/BorderT 307 197 2 10
screen/CornerBL 476 180 8 8
screen/CornerBR 485 180 8 8
screen/CornerTL 458 180 8 10
screen/CornerTR 467 180 8 10
window/BorderDark 333 197 1 4
window/BorderLight 335 197 1 4
window/CloseButton 494 180 7 6
window/CornerBL 313 197 4 4
window/CornerBR 318 197 4 4
window/CornerTL 323 197 4 4
window/CornerTR 328 197 4 4
nodes/Relay 424 180 16 16
nodes/Screen 441 180 16 16
nodes/ScreenOnOverlay 458 180 16 16
screen/BorderB 310 204 2 8
screen/BorderT 307 204 2 10
screen/CornerBL 493 180 8 8
screen/CornerBR 502 180 8 8
screen/CornerTL 475 180 8 10
screen/CornerTR 484 180 8 10
window/BorderDark 333 204 1 4
window/BorderLight 335 204 1 4
window/CloseButton 305 197 7 6
window/CornerBL 313 204 4 4
window/CornerBR 318 204 4 4
window/CornerTL 323 204 4 4
window/CornerTR 328 204 4 4

View File

@ -88,4 +88,8 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
def manhattanDistanceTo(that: Rect2D): Float = {
((center - that.center).abs - (extent + that.extent)).max(Vector2D(0, 0)).manhattanLength
}
def mapX(f: Float => Float): Rect2D = copy(x = f(x))
def mapY(f: Float => Float): Rect2D = copy(y = f(y))
}

View File

@ -1,7 +1,7 @@
package ocelot.desktop.graphics
import ocelot.desktop.color.{Color, RGBAColorNorm}
import ocelot.desktop.geometry.{Size2D, Transform2D, Vector2D}
import ocelot.desktop.geometry.{Rect2D, Size2D, Transform2D, Vector2D}
import ocelot.desktop.graphics.mesh.{Mesh, MeshInstance}
import ocelot.desktop.graphics.render.InstanceRenderer
import ocelot.desktop.util.{Font, Logging, Spritesheet}
@ -227,6 +227,10 @@ class Graphics extends Logging {
_rect(x, y, width, height)
}
def rect(r: Rect2D, color: Color): Unit = {
rect(r.x, r.y, r.w, r.h, color)
}
def rect(x: Float, y: Float, width: Float, height: Float, color: Color = RGBAColorNorm(1f, 1f, 1f)): Unit = {
sprite("Empty", x, y, width, height, color)
}

View File

@ -1,18 +1,21 @@
package ocelot.desktop.node
import ocelot.desktop.color.{Color, RGBAColor}
import ocelot.desktop.geometry.{Size2D, Vector2D}
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.ui.event.{ClickEvent, DragEvent, HoverEvent, MouseEvent}
import ocelot.desktop.ui.event.handlers.{ClickHandler, DragHandler, HoverHandler}
import ocelot.desktop.ui.event.sources.KeyEvents
import ocelot.desktop.ui.widget.{Widget, WorkspaceView}
import ocelot.desktop.ui.event.{ClickEvent, DragEvent, HoverEvent, MouseEvent}
import ocelot.desktop.ui.widget.window.Window
import ocelot.desktop.ui.widget.{Widget, WorkspaceView}
import ocelot.desktop.util.DrawUtils
import ocelot.desktop.util.animation.ColorAnimation
import org.apache.commons.lang3.StringUtils
import org.lwjgl.input.Keyboard
import totoro.ocelot.brain.entity.traits.Environment
import totoro.ocelot.brain.network
import scala.collection.mutable.ArrayBuffer
trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
var workspaceView: WorkspaceView = _
@ -25,6 +28,8 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
private var isMoving = false
private var grabPoint: Vector2D = Vector2D(0, 0)
protected val _connections: ArrayBuffer[(NodePort, Node, NodePort)] = ArrayBuffer[(NodePort, Node, NodePort)]()
size = minimumSize
override def receiveMouseEvents = true
@ -37,6 +42,9 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
if (!KeyEvents.isDown(Keyboard.KEY_LSHIFT)) {
grabPoint = pos - position
startMoving()
} else {
val port = portsBounds.flatMap(p => p._2.map(a => (p._1, a))).minBy(p => (p._2.center - pos).lengthSquared)._1
workspaceView.newConnection = Some((this, port, pos))
}
case DragEvent(DragEvent.State.Drag, MouseEvent.Button.Left, pos) =>
@ -50,9 +58,8 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
workspaceView.resolveCollision(this)
if (workspaceView.collides(this) || (position - desiredPos).lengthSquared > 50 * 50)
position = oldPos
// UiHandler.cursor = Cursor.Hand
} else {
workspaceView.newConnection = Some((this, pos))
workspaceView.newConnection = Some((this, workspaceView.newConnection.get._2, pos))
}
case DragEvent(DragEvent.State.Stop, MouseEvent.Button.Left, _) =>
@ -78,6 +85,69 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
def iconColor: Color = RGBAColor(255, 255, 255)
def ports: Array[NodePort] = Array(NodePort())
def getNodeByPort(port: NodePort): network.Node = environment.node
def connections: Iterator[(NodePort, Node, NodePort)] = _connections.iterator
def connect(portA: NodePort, node: Node, portB: NodePort): Unit = {
this._connections.append((portA, node, portB))
node._connections.append((portB, this, portA))
this.onConnectionAdded(portA, node, portB)
node.onConnectionAdded(portB, this, portA)
}
def disconnect(portA: NodePort, node: Node, portB: NodePort): Unit = {
this._connections -= ((portA, node, portB))
node._connections -= ((portB, this, portA))
this.onConnectionRemoved(portA, node, portB)
node.onConnectionRemoved(portB, this, portA)
}
def isConnected(portA: NodePort, node: Node, portB: NodePort): Boolean = {
_connections.contains((portA, node, portB))
}
def onConnectionAdded(portA: NodePort, node: Node, portB: NodePort): Unit = {
getNodeByPort(portA).connect(node.getNodeByPort(portB))
}
def onConnectionRemoved(portA: NodePort, node: Node, portB: NodePort): Unit = {
getNodeByPort(portA).disconnect(node.getNodeByPort(portB))
}
def portsBounds: Iterator[(NodePort, Array[Rect2D])] = {
val length = -4
val thickness = 4
val stride = thickness + 4
val hsize = Size2D(length, thickness)
val vsize = Size2D(thickness, length)
val centers = bounds.edgeCenters
val ports = this.ports
val numPorts = ports.length
ports.sorted.iterator.zipWithIndex.map { case (port, portIdx) =>
val top = Rect2D(centers(0) + Vector2D(-thickness / 2, -length), vsize)
val right = Rect2D(centers(1) + Vector2D(0, -thickness / 2), hsize)
val bottom = Rect2D(centers(2) + Vector2D(-thickness / 2, 0), vsize)
val left = Rect2D(centers(3) + Vector2D(-length, -thickness / 2), hsize)
val centersBounds = Array[Rect2D](top, right, bottom, left)
val portBounds = (0 until 4).map(side => {
val offset = thickness - numPorts * stride / 2 + portIdx * stride
val rect = centersBounds(side)
side match {
case 0 | 2 => rect.mapX(_ + offset)
case 1 | 3 => rect.mapY(_ + offset)
}
})
(port, portBounds.toArray)
}
}
// noinspection VarCouldBeVal
var label: Option[String] = None
@ -98,7 +168,7 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
highlight.goto(NoHighlight)
}
protected def getShortLabel: String =
def getShortLabel: String =
StringUtils.substring(label.orElse(Option(environment.node.address)).getOrElse("unknown"), 0, 8)
override def draw(g: Graphics): Unit = {
@ -115,5 +185,13 @@ trait Node extends Widget with DragHandler with ClickHandler with HoverHandler {
g.setNormalFont()
}
def drawPorts(g: Graphics): Unit = {
for ((port, rects) <- portsBounds) {
val color = port.getColor
for (rect <- rects)
g.rect(rect, color)
}
}
lazy val window: Option[Window] = None
}

View File

@ -0,0 +1,18 @@
package ocelot.desktop.node
import ocelot.desktop.color.{Color, IntColor, RGBAColor}
import totoro.ocelot.brain.util.Direction
case class NodePort(direction: Option[Direction.Value] = None) extends Ordered[NodePort] {
def getColor: Color = direction match {
case Some(Direction.Down) => IntColor(0x8382d8)
case Some(Direction.Up) => IntColor(0x75bdc1)
case Some(Direction.North) => IntColor(0xc8ca5f)
case Some(Direction.East) => IntColor(0xdb7d75)
case Some(Direction.West) => IntColor(0x7ec95f)
case Some(Direction.South) => IntColor(0x990da3)
case None => IntColor(0x9b9b9b)
}
override def compare(that: NodePort): Int = this.direction.compare(that.direction)
}

View File

@ -1,6 +1,7 @@
package ocelot.desktop.node
import totoro.ocelot.brain.entity.{Case, Screen}
import ocelot.desktop.node.nodes.{ComputerNode, RelayNode, ScreenNode}
import totoro.ocelot.brain.entity.{Case, Relay, Screen}
import scala.collection.mutable
@ -11,6 +12,10 @@ object NodeRegistry {
types += t
}
register(NodeType("Relay", "nodes/Relay", -1, () => {
new RelayNode(new Relay)
}))
for (tier <- 0 to 2) {
register(NodeType("Screen" + tier, "nodes/Screen", tier, () => {
new ScreenNode(new Screen(tier))

View File

@ -1,11 +1,12 @@
package ocelot.desktop.node
package ocelot.desktop.node.nodes
import ocelot.desktop.OcelotDesktop
import ocelot.desktop.color.Color
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.node.Node
import ocelot.desktop.util.TierColor
import totoro.ocelot.brain.entity.traits.Computer
import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, GraphicsCard, HDDManaged, InternetCard, Memory}
import totoro.ocelot.brain.entity.{CPU, Case, EEPROM, GraphicsCard, HDDManaged, InternetCard, Memory, NetworkCard}
import totoro.ocelot.brain.loot.Loot
import totoro.ocelot.brain.util.Tier
@ -17,6 +18,7 @@ class ComputerNode(val computer: Case) extends Node {
computer.add(new InternetCard)
computer.add(new Memory(Tier.Six))
computer.add(new Memory(Tier.Six))
computer.add(new NetworkCard)
computer.add(new HDDManaged(java.util.UUID.randomUUID().toString, Tier.Three, "hello"))
private val eeprom = Loot.OpenOsEEPROM.create()
eeprom.asInstanceOf[EEPROM].readonly = false

View File

@ -1,4 +1,4 @@
package ocelot.desktop.node
package ocelot.desktop.node.nodes
import ocelot.desktop.geometry.Size2D
import ocelot.desktop.graphics.Graphics

View File

@ -0,0 +1,27 @@
package ocelot.desktop.node.nodes
import ocelot.desktop.OcelotDesktop
import ocelot.desktop.node.{Node, NodePort}
import totoro.ocelot.brain.entity.Relay
import totoro.ocelot.brain.network
import totoro.ocelot.brain.util.Direction
class RelayNode(relay: Relay) extends Node {
OcelotDesktop.workspace.add(relay)
override def environment: Relay = relay
override val icon: String = "nodes/Relay"
override def ports: Array[NodePort] = Array(
NodePort(Some(Direction.North)),
NodePort(Some(Direction.South)),
NodePort(Some(Direction.East)),
NodePort(Some(Direction.West)),
NodePort(Some(Direction.Up)),
NodePort(Some(Direction.Down)))
override def getShortLabel: String = ""
override def getNodeByPort(port: NodePort): network.Node = relay.sidedNode(port.direction.get)
}

View File

@ -1,8 +1,9 @@
package ocelot.desktop.node
package ocelot.desktop.node.nodes
import ocelot.desktop.OcelotDesktop
import ocelot.desktop.color.Color
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.node.Node
import ocelot.desktop.util.TierColor
import totoro.ocelot.brain.entity.traits.Environment
import totoro.ocelot.brain.entity.{Keyboard, Screen}

View File

@ -1,4 +1,4 @@
package ocelot.desktop.node
package ocelot.desktop.node.nodes
import ocelot.desktop.color.{IntColor, RGBAColor, RGBAColorNorm}
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}

View File

@ -3,7 +3,8 @@ package ocelot.desktop.ui.widget
import ocelot.desktop.color.{Color, RGBAColor, RGBAColorNorm}
import ocelot.desktop.geometry.{Rect2D, Size2D, Vector2D}
import ocelot.desktop.graphics.Graphics
import ocelot.desktop.node.{ComputerNode, Node, ScreenNode}
import ocelot.desktop.node.nodes.{ComputerNode, ScreenNode}
import ocelot.desktop.node.{Node, NodePort}
import ocelot.desktop.ui.event._
import ocelot.desktop.ui.event.handlers.{ClickHandler, DragHandler, HoverHandler}
import ocelot.desktop.ui.layout.{CopyLayout, Layout}
@ -23,10 +24,11 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
val nodeSelector = new NodeSelector
var cameraOffset: Vector2D = Vector2D(0, 0)
var newConnection: Option[(Node, Vector2D)] = None
var newConnection: Option[(Node, NodePort, Vector2D)] = None
private var newNodePos = Vector2D(0, 0)
private val gridAlpha = new ValueAnimation(0, 1f)
private val portsAlpha = new ValueAnimation(0, 7f)
override protected val layout: Layout = new CopyLayout(this)
@ -45,21 +47,22 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
}
def buildNewConnection(): Unit = {
val (start, endPoint) = newConnection.get
val end = nodes.find(_.bounds.contains(endPoint))
if (end.isDefined && end.get != start) {
if (end.get.environment.node.neighbors.exists(_ == start.environment.node))
end.get.environment.disconnect(start.environment)
val (node, port, _) = newConnection.get
newConnectionTarget match {
case Some((target, targetPort)) =>
if (node.isConnected(port, target, targetPort))
node.disconnect(port, target, targetPort)
else
end.get.environment.connect(start.environment)
node.connect(port, target, targetPort)
case None =>
}
newConnection = None
}
def createDefaultWorkspace(): Unit = {
addNode(new ComputerNode(new Case(Tier.Six)))
addNode(new ComputerNode(new Case(Tier.Four)))
addNode(new ScreenNode(new Screen(Tier.Two)), Vector2D(200, 100))
nodes(0).environment.connect(nodes(1).environment)
nodes(0).connect(NodePort(), nodes(1), NodePort())
}
override def receiveMouseEvents = true
@ -144,40 +147,39 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
!nodes.exists(_.bounds.collides(clearance))
}
private def findSuitableConnections(a: Rect2D, b: Rect2D, checkCollision: Boolean,
forceParallel: Boolean, clampMin: Boolean): Iterator[Array[Vector2D]] = {
var pairs =
for (aSide <- a.edgeCenters.iterator; bSide <- b.edgeCenters.iterator)
yield (aSide, bSide)
private def findSuitableConnections(a: Array[(Vector2D, Vector2D)],
b: Array[(Vector2D, Vector2D)],
checkCollision: Boolean,
forceParallel: Boolean,
clampMin: Boolean): Array[Array[Vector2D]] = {
val product = for (x <- a; y <- b) yield (x, y)
var iter = product.map { case ((aSide, aCenter), (bSide, bCenter)) =>
val (aLen, bLen) = connectorLen(aSide, aCenter, bSide, bCenter, clampMin)
((aSide, aCenter, aLen), (bSide, bCenter, bLen))
}
if (checkCollision)
pairs = pairs.filter(pair => {
val (aSide, bSide) = pair
val (aLen, bLen) = connectorLen(a.center, aSide, b.center, bSide, clampMin)
checkConnectorCollision(aSide, a.center, aLen) &&
checkConnectorCollision(bSide, b.center, bLen)
})
iter = iter.filter { case ((aSide, aCenter, aLen), (bSide, bCenter, bLen)) =>
checkConnectorCollision(aSide, aCenter, aLen) &&
checkConnectorCollision(bSide, bCenter, bLen)
}
var paths = pairs.map(p => {
val (aLen, bLen) = connectorLen(a.center, p._1, b.center, p._2, clampMin)
val aEnd = p._1 + (p._1 - a.center).normalizeAxisAligned * aLen
val bEnd = p._2 + (p._2 - b.center).normalizeAxisAligned * bLen
Array(p._1, aEnd, bEnd, p._2)
}).filter(DrawUtils.isValidPolyline)
var paths = iter.map { case ((aSide, aCenter, aLen), (bSide, bCenter, bLen)) =>
val aEnd = aSide + (aSide - aCenter).normalizeAxisAligned * aLen
val bEnd = bSide + (bSide - bCenter).normalizeAxisAligned * bLen
Array(aSide, aEnd, bEnd, bSide)
}.filter(DrawUtils.isValidPolyline)
if (forceParallel)
paths = paths.filter(p => {
val Array(aStart, aEnd, bEnd, bStart) = p
val aDir = aEnd - aStart
val bDir = bEnd - bStart
aDir.dot(bDir) != 0f
})
paths = paths.filter { case Array(aStart, aEnd, bEnd, bStart) =>
(aEnd - aStart).dot(bEnd - bStart) != 0f
}
paths
}
private def connectorLen(aCenter: Vector2D, aSide: Vector2D,
bCenter: Vector2D, bSide: Vector2D,
private def connectorLen(aSide: Vector2D, aCenter: Vector2D,
bSide: Vector2D, bCenter: Vector2D,
clampMin: Boolean): (Float, Float) = {
val aDir = aSide - aCenter
val bDir = bSide - bCenter
@ -191,32 +193,111 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
(aLen.min(40f), bLen.min(40f))
}
def drawConnection(g: Graphics, a: Rect2D, b: Rect2D,
clampMin: Boolean = true,
checkCollision: Boolean = true,
forceParallel: Boolean = false,
drawBorder: Boolean = true,
thickness: Float = 4, col: Color = RGBAColor(150, 150, 150)): Unit = {
if (a.collides(b)) return
private def portDirections(node: Node, port: NodePort): Array[(Vector2D, Vector2D)] = {
val rect = node.bounds
val sides = node.portsBounds.find(_._1 == port).get._2
.map(rects => rects.edgeCenters.maxBy(c => (c - rect.center).lengthSquared))
val paths = findSuitableConnections(a, b, checkCollision, forceParallel, clampMin)
sides.zipWithIndex.map { case (side, i) =>
val center = if (i % 2 == 0) rect.center.copy(x = side.x) else rect.center.copy(y = side.y)
(side, center)
}
}
private def drawConnection(g: Graphics, aNode: Node, aPort: NodePort, bNode: Node, bPort: NodePort,
color: Color = RGBAColor(150, 150, 150)): Unit = {
val (aRect, bRect) = (aNode.bounds, bNode.bounds)
if (aRect.collides(bRect)) return
if (aRect.x < bRect.x) {
drawConnection(g, bNode, bPort, aNode, aPort, color)
return
}
val paths = findSuitableConnections(portDirections(aNode, aPort),
portDirections(bNode, bPort), checkCollision = true, forceParallel = false, clampMin = true)
if (paths.isEmpty) {
val canGiveUp = checkCollision || clampMin
if (canGiveUp) {
if (drawBorder)
g.line(a.center, b.center, thickness + 4, RGBAColorNorm(0.1f, 0.1f, 0.1f))
g.line(a.center, b.center, thickness, col)
}
// give up
g.line(aRect.center, bRect.center, 8, RGBAColorNorm(0.1f, 0.1f, 0.1f))
g.line(aRect.center, bRect.center, 4, color)
} else {
val path = paths.minBy(p => {
val Array(_, aEnd, bEnd, _) = p
(aEnd - bEnd).lengthSquared
})
val path = paths.minBy { case Array(_, aEnd, bEnd, _) => (aEnd - bEnd).lengthSquared }
DrawUtils.polyline(g, path, 8, RGBAColorNorm(0.1f, 0.1f, 0.1f))
DrawUtils.polyline(g, path, 4, color)
if (drawBorder)
DrawUtils.polyline(g, path, thickness + 4, RGBAColorNorm(0.1f, 0.1f, 0.1f))
DrawUtils.polyline(g, path, thickness, col)
val aDir = (path(1) - path(0)).normalizeAxisAligned * 8
g.line(path(0), path(0) + aDir, 4, aPort.getColor)
val bDir = (path(2) - path(3)).normalizeAxisAligned * 8
g.line(path(3), path(3) + bDir, 4, bPort.getColor)
}
}
private def drawNewConnectionNoTarget(g: Graphics, node: Node, port: NodePort, endpoint: Vector2D): Unit = {
val color = RGBAColorNorm(0.3f, 0.5f, 0.4f)
val rect = node.bounds
val filtered = portDirections(node, port)
.map { case (side, center) =>
val (len, _) = connectorLen(side, side, endpoint, endpoint, clampMin = true)
(center, side, len)
}
.filter { case (center, side, len) => checkConnectorCollision(side, center, len) }
.map { case (center, side, len) =>
val end = side + (side - center).normalizeAxisAligned * len
(side, end, (end - endpoint).lengthSquared)
}
.filter { case (side, end, _) => DrawUtils.isValidPolyline(Array(side, end, endpoint)) }
if (filtered.isEmpty)
g.line(rect.center, endpoint, 4, color)
else {
val (side, end, _) = filtered.minBy(_._3)
DrawUtils.polyline(g, Array(side, end, endpoint), 4, color)
val dir = (end - side).normalizeAxisAligned * 8
g.line(side, side + dir, 4, port.getColor)
}
}
private def newConnectionTarget: Option[(Node, NodePort)] = {
val (node, _, endpoint) = newConnection.get
val validTargets = nodes.iterator.filter(n => n != node && n.bounds.inflate(20).contains(endpoint))
if (validTargets.nonEmpty) {
val target = validTargets.minBy(n => (n.bounds.center - endpoint).lengthSquared)
val targetPort = target.portsBounds.flatMap(p => p._2.map(a => (p._1, a)))
.minBy(p => (p._2.center - endpoint).lengthSquared)._1
Some((target, targetPort))
} else
None
}
private def drawNewConnection(g: Graphics): Unit = {
val (node, port, endpoint) = newConnection.get
if (node.bounds.contains(endpoint)) return
val color = RGBAColorNorm(0.3f, 0.5f, 0.4f)
newConnectionTarget match {
case Some((target, targetPort)) =>
drawConnection(g, node, port, target, targetPort, color = color)
case None =>
drawNewConnectionNoTarget(g, node, port, endpoint)
}
}
private def drawSelectorConnection(g: Graphics, aRect: Rect2D, bRect: Rect2D,
thickness: Float = 4,
color: Color = RGBAColor(150, 150, 150)): Unit = {
if (aRect.collides(bRect)) return
val (a, b) = if (aRect.x > bRect.x) (aRect, bRect) else (bRect, aRect)
val paths = findSuitableConnections(a.edgeCenters.map((_, a.center)),
b.edgeCenters.map((_, b.center)),
checkCollision = false, forceParallel = true, clampMin = false)
if (paths.nonEmpty) {
val path = paths.minBy { case Array(_, aEnd, bEnd, _) => (aEnd - bEnd).lengthSquared }
DrawUtils.polyline(g, path, thickness, color)
}
}
@ -263,37 +344,43 @@ class WorkspaceView extends Widget with DragHandler with ClickHandler with Hover
gridAlpha.goto(0f)
}
val drawn = mutable.HashSet[(Node, Node)]()
val drawn = mutable.HashSet[(Node, NodePort, Node, NodePort)]()
for (node <- nodes) {
for (neighbour <- node.environment.node.neighbors)
for (other <- nodes.find(_.environment.node == neighbour)) {
if (!drawn.contains((node, other)) && !drawn.contains((other, node))) {
val gray = ((node.environment.node.address + other.environment.node.address).hashCode % 25 + 133).toShort
for (nodeA <- nodes) {
nodeA.connections.foreach { case (portA, nodeB, portB) =>
if (!drawn.contains((nodeA, portA, nodeB, portB)) && !drawn.contains((nodeB, portB, nodeA, portA))) {
val gray = ((nodeA.getShortLabel + nodeB.getShortLabel).hashCode % 25 + 133).toShort
val color = RGBAColor(gray, gray, gray)
drawn.add((node, other))
drawConnection(g, node.bounds, other.bounds, col = color)
drawn += ((nodeA, portA, nodeB, portB))
drawConnection(g, nodeA, portA, nodeB, portB, color = color)
}
}
}
for ((start, endpoint) <- newConnection) {
drawConnection(g, start.bounds, Rect2D(endpoint - Vector2D(32, 32), Size2D(64, 64)),
col = RGBAColorNorm(0.3f, 0.5f, 0.4f))
if (newConnection.isDefined) {
drawNewConnection(g)
}
nodes.foreach(_.draw(g))
nodes.foreach(_.drawLabel(g))
val col = nodeSelector.ringColor
portsAlpha.update()
portsAlpha.goto(if (newConnection.isDefined) 1 else 0)
g.save()
g.alphaMultiplier *= portsAlpha.value
nodes.foreach(_.drawPorts(g))
g.restore()
val color = nodeSelector.ringColor
if (nodeSelector.isShown) {
g.sprite("nodes/NewNode", newNodePos.x + cameraOffset.x,
newNodePos.y + cameraOffset.y, 64, 64, col)
newNodePos.y + cameraOffset.y, 64, 64, color)
}
if (nodeSelector.isShown)
drawConnection(g, Rect2D(newNodePos.x + cameraOffset.x, newNodePos.y + cameraOffset.y, 64, 64),
nodeSelector.bounds, col = col, checkCollision = false, drawBorder = false, forceParallel = true, clampMin = false)
drawSelectorConnection(g, Rect2D(newNodePos.x + cameraOffset.x, newNodePos.y + cameraOffset.y, 64, 64),
nodeSelector.bounds, color = color)
drawChildren(g)
}

View File

@ -43,7 +43,7 @@ object DrawUtils {
def polyline(g: Graphics, points: Array[Vector2D], thickness: Float = 4, color: Color = RGBAColor(255, 255, 255)): Unit = {
if (points.length < 2) return
if (!isValidPolyline(points)) return
// if (!isValidPolyline(points)) return
var start = points(0)
for ((end, i) <- points.iterator.zipWithIndex.drop(1)) {
if (end != points.last) {

View File

@ -10,5 +10,5 @@ object TierColor {
val Tiers: Array[Color] = Array(Tier0, Tier1, Tier2, Tier3)
def get(tier: Int): Color = Tiers(tier.min(3))
def get(tier: Int): Color = if (tier >= 0 && tier <= 3) Tiers(tier.min(3)) else IntColor(0xFFFFFF)
}