Commit 2b0d438e authored by Michael Büsch's avatar Michael Büsch

gui: Detect colliding wire lines

Signed-off-by: Michael Büsch's avatarMichael Buesch <m@bues.ch>
parent 91639eb0
......@@ -2,7 +2,7 @@
#
# AWL simulator - FUP - Grid classes
#
# Copyright 2016 Michael Buesch <m@bues.ch>
# Copyright 2016-2017 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -24,6 +24,7 @@ from awlsim.common.compat import *
from awlsim.common.xmlfactory import *
from awlsim.gui.geo2d import *
from awlsim.gui.fup.fup_base import *
from awlsim.gui.fup.fup_wire import *
from awlsim.gui.fup.fup_elem import *
......@@ -80,7 +81,8 @@ class FupGrid_factory(XmlFactory):
]
class FupGrid(object):
"""FUP/FBD element grid"""
"""FUP/FBD element grid.
"""
factory = FupGrid_factory
......@@ -88,7 +90,31 @@ class FupGrid(object):
# If this is a callable, it it called with (width, height) on resize events.
resizeEvent = None
class Line(object):
"""Inter-element line descriptor.
This is used for describing drawn wires.
"""
# Inter2D() line intersection object, if any.
inter = None
def __init__(self, wire, lineSeg):
"""wire => FupWire() that belongs to this line.
lineSeg => LineSeg2D() line segment information.
"""
self.wire = wire
self.lineSeg = lineSeg
def dup(self):
"""Make a shallow copy of this Line.
"""
return self.__class__(self.wire, self.lineSeg)
def __init__(self, drawWidget, width, height):
"""drawWidget => FupDrawWidget() instance.
width => The grid width.
height => The grid height.
"""
self.__drawWidget = drawWidget
self.width = width
self.height = height
......@@ -102,12 +128,21 @@ class FupGrid(object):
self.clickedConn = None # The recently clicked connection in this grid
self.clickedArea = None # The recently clicked area in this grid
self.clearLineCache()
def clear(self):
for wire in self.wires:
wire.disconnectAll()
self.wires.clear()
self.elems = []
self.selectedElems.clear()
self.clearLineCache()
def clearLineCache(self):
"""Clear the cache of drawn inter-element lines.
"""
# __lines is a list of Line() instances.
self.__lines = []
def resize(self, width, height):
"""Resize the grid.
......@@ -152,6 +187,9 @@ class FupGrid(object):
"""
with contextlib.suppress(KeyError):
self.wires.remove(wire)
# Remove Line()s that belong to 'wire'.
self.__lines = [ line for line in self.__lines
if line.wire is not wire ]
def getWireById(self, wireIdNum):
"""Get a wire by its idNum.
......@@ -178,16 +216,37 @@ class FupGrid(object):
for i, wire in enumerate(self.wires):
wire.idNum = i
def drawWireLine(self, painter, x0, y0, x1, y1, force=False):
def checkWireLine(self, painter, excludeWires, lineSeg):
"""Checks if a wire line would be drawable and does not collide
with another wire line.
excludeWires => Iterable if FupWire()s to exclude from the check.
lineSeg => The LineSeg2D() that should be drawn.
Returns a list of colliding self.Line() instances.
"""
collisions = []
for line in self.__lines:
if line.wire in excludeWires:
continue
inter = lineSeg.intersection(line.lineSeg)
if not inter:
continue
# We have a collision.
# Make a shallow copy of Line and add the Inter2D.
line = line.dup()
line.inter = inter
collisions.append(line)
return collisions
def drawWireLine(self, painter, wire, lineSeg):
"""Draw a wire line on 'painter'.
x0, y0 => Start pixel coordinates.
x1, y1 => End pixel coordinates.
force => Draw, even if there are collisions.
Returns True, if there were no collisions.
wire => The FupWire() this line segment belongs to.
lineSeg => The LineSeg2D() that describes the line to draw.
"""
#TODO
painter.drawLine(x0, y0, x1, y1)
return True
if not lineSeg:
return # Zero length line
painter.drawLine(lineSeg.pointA.x, lineSeg.pointA.y,
lineSeg.pointB.x, lineSeg.pointB.y)
self.__lines.append(self.Line(wire, lineSeg))
@property
def cellPixWidth(self):
......
......@@ -2,7 +2,7 @@
#
# AWL simulator - FUP - Wire classes
#
# Copyright 2016 Michael Buesch <m@bues.ch>
# Copyright 2016-2017 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -24,6 +24,7 @@ from awlsim.common.compat import *
from awlsim.common.xmlfactory import *
from awlsim.gui.geo2d import *
from awlsim.gui.util import *
from awlsim.gui.fup.fup_base import *
......@@ -85,6 +86,8 @@ class FupWire(FupBaseClass):
self.__wirePen = QPen(QColor("#000000"))
self.__wirePen.setWidth(2)
self.__wireCollidingPen = QPen(QColor("#000000"))
self.__wireCollidingPen.setWidth(2)
self.__wireBranchPen = QPen(QColor("#000000"))
self.__wireBranchPen.setWidth(1)
self.__wireBranchBrush = QBrush(QColor("#000000"))
......@@ -145,12 +148,24 @@ class FupWire(FupBaseClass):
assert(inConn.IN)
# Draw the wire from out to in
xAbs1, yAbs1 = inConn.pixCoords
painter.setPen(self.__wirePen)
x = (xAbs0 // cellPixWidth) * cellPixWidth + cellPixWidth
grid.drawWireLine(painter, xAbs0, yAbs0, x, yAbs0, force=True)
grid.drawWireLine(painter, x, yAbs0, x, yAbs1)
grid.drawWireLine(painter, x, yAbs1, xAbs1, yAbs1, force=True)
seg0 = LineSeg2D.fromCoords(xAbs0, yAbs0, x, yAbs0)
seg1 = LineSeg2D.fromCoords(x, yAbs0, x, yAbs1)
seg2 = LineSeg2D.fromCoords(x, yAbs1, xAbs1, yAbs1)
segDirect = LineSeg2D.fromCoords(x, yAbs0, xAbs1, yAbs1)
grid.drawWireLine(painter, self, seg0)
if not grid.checkWireLine(painter, {self}, seg1) and\
not grid.checkWireLine(painter, {self}, seg2):
grid.drawWireLine(painter, self, seg1)
grid.drawWireLine(painter, self, seg2)
else:
painter.setPen(self.__wireCollidingPen)
grid.drawWireLine(painter, self, segDirect)
# Draw the branch circles
painter.setPen(self.__wireBranchPen)
......
......@@ -331,6 +331,9 @@ class FupDrawWidget(QWidget):
if not grid:
return
# Build a new line cache.
grid.clearLineCache()
size = self.size()
width, height = size.width(), size.height()
p = QPainter(self)
......@@ -418,6 +421,10 @@ class FupDrawWidget(QWidget):
selWidth, selHeight,
r, r)
# We don't need the line cache anymore.
# The next paint event will build a new one.
grid.clearLineCache()
def posToGridCoords(self, pixX, pixY):
"""Convert pixel coordinates to grid coordinates.
"""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment