fup_wire.py 5.74 KB
Newer Older
1 2 3 4
# -*- coding: utf-8 -*-
#
# AWL simulator - FUP - Wire classes
#
5
# Copyright 2016-2017 Michael Buesch <m@bues.ch>
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#
# 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
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *

from awlsim.common.xmlfactory import *

27
from awlsim.gui.geo2d import *
28 29 30 31 32
from awlsim.gui.util import *
from awlsim.gui.fup.fup_base import *


class FupWire_factory(XmlFactory):
33
	def parser_open(self, tag=None):
34
		self.inWire = False
35
		XmlFactory.parser_open(self, tag)
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

	def parser_beginTag(self, tag):
		if not self.inWire:
			if tag.name == "wire":
				self.inWire = True
				idNum = tag.getAttrInt("id")
				if idNum in (w.idNum for w in self.grid.wires):
					raise self.Error("<wire id=%d> does "
						"already exist." % idNum)
				# Create wire and add it to the grid.
				FupWire(grid=self.grid, idNum=idNum)
				return
		XmlFactory.parser_beginTag(self, tag)

	def parser_endTag(self, tag):
		if self.inWire:
			if tag.name == "wire":
				self.inWire = False
				return
		else:
			if tag.name == "wires":
				self.parser_finish()
				return
		XmlFactory.parser_endTag(self, tag)

	def composer_getTags(self):
		return [
			self.Tag(name="wire",
				attrs={
					"id" : str(self.wire.idNum),
				}),
		]

class FupWire(FupBaseClass):
	"""FUP/FBD wire connecting two FupConn connections."""

	factory = FupWire_factory

	BRANCH_DIA = 4

	def __init__(self, grid, idNum=None):
		FupBaseClass.__init__(self)
		self.grid = grid
		self.connections = set()	# The connections this wire is connected to
		self.outConn = None		# The out-connection this is connected to

		if idNum is None:
			idNum = grid.getUnusedWireIdNum()
		self.idNum = idNum		# The ID number of this wire
		grid.addWire(self)

		self.__wirePen = QPen(QColor("#000000"))
		self.__wirePen.setWidth(2)
89
		self.__wireCollidingPen = QPen(QColor("#C02020"))
90
		self.__wireCollidingPen.setWidth(2)
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
		self.__wireBranchPen = QPen(QColor("#000000"))
		self.__wireBranchPen.setWidth(1)
		self.__wireBranchBrush = QBrush(QColor("#000000"))

	def connect(self, conn):
		"""Add a connection to this wire.
		"""
		if conn in self.connections:
			return
		if conn.OUT and\
		   self.outConn is not None and\
		   self.outConn is not conn:
			# We already have an output connection.
			raise ValueError
		self.connections.add(conn)
		conn.wire = self
		if conn.OUT:
			self.outConn = conn

	def disconnectAll(self):
		"""Disconenct all connections.
		"""
		for conn in self.connections:
			conn.wire = None
		self.connections.clear()
		self.outConn = None
		self.grid.removeWire(self)

	def disconnect(self, conn):
		"""Disconnect a connection from this wire.
		"""
		conn.wire = None
		self.connections.remove(conn)
		if self.outConn is conn:
			# Only inputs left. Remove them all.
			self.disconnectAll()
		if len(self.connections) == 1:
			# Only one connection left. Remove that, too.
			self.disconnectAll()
		if not self.connections and not self.outConn:
			self.grid.removeWire(self)

133 134 135 136 137 138 139
	class DrawInfo(object):
		__slots__ = ("segStart", "segments", "segDirect")
		def __init__(self, segStart, segments, segDirect):
			self.segStart = segStart
			self.segments = segments
			self.segDirect = segDirect

140 141
	def draw(self, painter):
		if self.outConn is None:
142
			return # Only inputs. Do not draw.
143
		grid = self.grid
144 145

		# Branch circles diameter
146
		branchR, branchD = self.BRANCH_DIA // 2, self.BRANCH_DIA
147
		painter.setBrush(self.__wireBranchBrush)
148

149 150
		# Calculate the coordinates of all wire lines.
		wireLines = [] # List of DrawInfo()s
151
		xAbs0, yAbs0 = self.outConn.pixCoords
152
		cellPixWidth = self.grid.cellPixWidth
153 154 155 156 157
		for inConn in self.connections:
			if inConn is self.outConn:
				continue
			assert(inConn.IN)

158
			# Construct line segments to draw the wire from out to in.
159

160
			xAbs1, yAbs1 = inConn.pixCoords
161
			x = (xAbs0 // cellPixWidth) * cellPixWidth + cellPixWidth
162

163 164 165
			segStart = LineSeg2D.fromCoords(xAbs0, yAbs0, x, yAbs0)
			seg0 = LineSeg2D.fromCoords(x, yAbs0, x, yAbs1)
			seg1 = LineSeg2D.fromCoords(x, yAbs1, xAbs1, yAbs1)
166 167
			segDirect = LineSeg2D.fromCoords(x, yAbs0, xAbs1, yAbs1)

168 169 170
			wireLines.append(self.DrawInfo(segStart,
						       (seg0, seg1),
						       segDirect))
171

172
		def drawBranch(x, y):
173
			painter.setPen(self.__wireBranchPen)
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
			painter.drawEllipse(x - branchR, y - branchR,
					    branchD, branchD)

		def drawSeg(seg, handleBranches=True, pen=self.__wirePen):
			painter.setPen(pen)
			grid.drawWireLine(painter, self, seg)
			if not handleBranches:
				return
			for otherDrawInfo in wireLines:
				if drawInfo is otherDrawInfo:
					continue
				for otherSeg in otherDrawInfo.segments:
					inter = seg.intersection(otherSeg)
					if not inter.intersects:
						continue
					drawBranch(inter.pointA.x, inter.pointA.y)
					drawBranch(inter.pointB.x, inter.pointB.y)

		# Draw wire from output to all inputs
		for drawInfo in wireLines:
			drawSeg(drawInfo.segStart, handleBranches=False)
			if all(not grid.checkWireLine(painter, {self}, seg)
			       for seg in drawInfo.segments):
				for seg in drawInfo.segments:
					drawSeg(seg)
			else:
				drawSeg(drawInfo.segDirect, handleBranches=False,
					pen=self.__wireCollidingPen)