summaryrefslogtreecommitdiffstats
path: root/src/connector.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/connector.cpp')
-rw-r--r--src/connector.cpp652
1 files changed, 652 insertions, 0 deletions
diff --git a/src/connector.cpp b/src/connector.cpp
new file mode 100644
index 0000000..03a304c
--- /dev/null
+++ b/src/connector.cpp
@@ -0,0 +1,652 @@
+/***************************************************************************
+ * Copyright (C) 2003-2005 by David Saxton *
+ * david@bluehaze.org *
+ * *
+ * 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. *
+ ***************************************************************************/
+
+#include "circuitdocument.h"
+#include "connector.h"
+#include "conrouter.h"
+#include "cnitem.h"
+#include "ecnode.h"
+#include "itemdocumentdata.h"
+#include "wire.h"
+
+#include <kdebug.h>
+
+
+#include <cmath>
+
+inline static int toCanvas( int pos )
+{
+ return pos*8+4;
+}
+inline static int fromCanvas( int pos )
+{
+ return (pos-4)/8;
+// return (pos>0) ? int((pos-3)/8) : int((pos-5)/8);
+}
+
+inline static QPoint toCanvas( const QPoint * const pos )
+{
+ return QPoint( toCanvas(pos->x()), toCanvas(pos->y()) );
+}
+inline static QPoint fromCanvas( const QPoint * const pos )
+{
+ return QPoint( fromCanvas(pos->x()), fromCanvas(pos->y()) );
+}
+
+inline static QPoint toCanvas( const QPoint &pos )
+{
+ return QPoint( toCanvas(pos.x()), toCanvas(pos.y()) );
+}
+inline static QPoint fromCanvas( const QPoint &pos )
+{
+ return QPoint( fromCanvas(pos.x()), fromCanvas(pos.y()) );
+}
+
+
+//BEGIN class Connector
+Connector::Connector( Node * startNode, Node * endNode, ICNDocument *icnDocument, QString *id )
+ : QObject(icnDocument),
+ QCanvasPolygon( icnDocument->canvas() )
+{
+ p_icnDocument = icnDocument;
+ m_conRouter = new ConRouter(p_icnDocument);
+ p_parentContainer = 0l;
+ m_startNode = startNode;
+ m_endNode = endNode;
+ p_nodeGroup = 0l;
+ b_semiHidden = false;
+ b_deleted = false;
+ b_pointsAdded = false;
+ b_manualPoints = false;
+ m_bIsSyncingWires = false;
+
+ if (id)
+ {
+ m_id = *id;
+ if ( !p_icnDocument->registerUID(*id) )
+ {
+// kdDebug() << k_funcinfo << "KTechlab: Connector attempted to register given ID, but ID already in use"<<endl;
+ }
+ }
+ else
+ m_id = p_icnDocument->generateUID("connector");
+
+ p_icnDocument->registerItem(this);
+
+ p_icnDocument->requestRerouteInvalidatedConnectors();
+ setVisible(true);
+
+ ECNode * startECNode = dynamic_cast<ECNode*>(startNode);
+ ECNode * endECNode = dynamic_cast<ECNode*>(endNode);
+ if ( startECNode && endECNode )
+ {
+ connect( startECNode, SIGNAL(numPinsChanged(unsigned)), this, SLOT(syncWiresWithNodes()) );
+ connect( endECNode, SIGNAL(numPinsChanged(unsigned)), this, SLOT(syncWiresWithNodes()) );
+ syncWiresWithNodes();
+ }
+}
+
+
+Connector::~Connector()
+{
+ p_icnDocument->unregisterUID( id() );
+
+ delete m_conRouter;
+ m_conRouter = 0l;
+
+ for ( unsigned i = 0; i < m_wires.size(); i++ )
+ delete m_wires[i];
+ m_wires.resize(0);
+}
+
+
+int Connector::rtti() const
+{
+ return ItemDocument::RTTI::Connector;
+}
+
+
+void Connector::syncWiresWithNodes()
+{
+ ECNode * startECNode = dynamic_cast<ECNode*>((Node*)m_startNode);
+ ECNode * endECNode = dynamic_cast<ECNode*>((Node*)m_endNode);
+
+ if ( !startECNode || !endECNode )
+ return;
+
+ unsigned newNumWires = 0;
+
+ if ( startECNode->type() == Node::ec_junction ||
+ endECNode->type() == Node::ec_junction )
+ newNumWires = QMAX( startECNode->numPins(), endECNode->numPins() );
+
+ else
+ newNumWires = QMIN( startECNode->numPins(), endECNode->numPins() );
+
+ unsigned oldNumWires = m_wires.size();
+
+ if ( newNumWires == oldNumWires )
+ return;
+
+ m_bIsSyncingWires = true;
+ if ( startECNode->type() == Node::ec_junction )
+ startECNode->setNumPins(newNumWires);
+ if ( endECNode->type() == Node::ec_junction )
+ endECNode->setNumPins(newNumWires);
+ m_bIsSyncingWires = false;
+
+ if ( newNumWires > oldNumWires )
+ {
+ m_wires.resize(newNumWires);
+ for ( unsigned i = oldNumWires; i < newNumWires; i++ )
+ {
+ if ( startECNode->pin(i) && endECNode->pin(i) )
+ m_wires[i] = new Wire( startECNode->pin(i), endECNode->pin(i) );
+ }
+ }
+ else
+ {
+ for ( unsigned i = newNumWires; i < oldNumWires; i++ )
+ delete m_wires[i];
+ m_wires.resize(newNumWires);
+ }
+
+ updateConnectorLines();
+ emit numWiresChanged(newNumWires);
+}
+
+
+void Connector::setParentContainer( const QString &cnItemId )
+{
+// // We only allow the node to be parented once
+// if ( p_parentContainer || !ICNDocument->itemWithID(cnItemId) ) return;
+ p_parentContainer = p_icnDocument->cnItemWithID(cnItemId);
+}
+
+
+void Connector::removeConnector( Node* )
+{
+ if (b_deleted)
+ return;
+ b_deleted = true;
+
+ // Remove 'penalty' points for this connector from the ICNDocument
+ updateConnectorPoints(false);
+
+ emit selected(false);
+ emit removed(this);
+ if ( m_startNode )
+ m_startNode->removeConnector(this);
+ if ( m_endNode )
+ m_endNode->removeConnector(this);
+ p_icnDocument->appendDeleteList(this);
+}
+
+
+int getSlope( float x1, float y1, float x2, float y2 )
+{
+ enum slope
+ {
+ s_n = 0,// .
+ s_v, // |
+ s_h, // -
+ s_s, // /
+ s_d // \ (backwards slash)
+
+ };
+
+ if ( x1 == x2 )
+ {
+ if ( y1 == y2 ) {
+ return s_n;
+ }
+ return s_v;
+ }
+ else if ( y1 == y2 ) {
+ return s_h;
+ }
+ else if ( (y2-y1)/(x2-x1) > 0 ) {
+ return s_s;
+ }
+ else {
+ return s_d;
+ }
+}
+
+
+void Connector::updateDrawList()
+{
+ if ( !m_startNode || !m_endNode || !canvas() ) {
+ return;
+ }
+
+ QPointList drawLineList;
+
+ int prevX = (*m_conRouter->cellPointList()->begin()).x();
+ int prevY = (*m_conRouter->cellPointList()->begin()).y();
+
+ int prevX_canvas = toCanvas(prevX);
+ int prevY_canvas = toCanvas(prevY);
+
+ Cells *cells = p_icnDocument->cells();
+
+ bool bumpNow = false;
+ const QPointList::const_iterator cplEnd = m_conRouter->cellPointList()->end();
+ for ( QPointList::const_iterator it = m_conRouter->cellPointList()->begin(); it != cplEnd; ++it )
+ {
+ const int x = (*it).x();
+ const int y = (*it).y();
+ const int numCon = p_icnDocument->isValidCellReference(x,y) ? (*cells)[x][y].numCon : 0;
+
+ const int y_canvas = toCanvas(y);
+ const int x_canvas = toCanvas(x);
+
+ const bool bumpNext = ( prevX == x &&
+ numCon > 1 &&
+ std::abs(y_canvas-m_startNode->y())>8 &&
+ std::abs(y_canvas-m_endNode->y())>8 );
+
+ int x0 = prevX_canvas;
+ int x2 = x_canvas;
+ int x1 = (x0+x2)/2;
+
+ int y0 = prevY_canvas;
+ int y3 = y_canvas;
+ int y1 = ( y0 == y3 ) ? y0 : ((y0<y3) ? y0+3 : y0-3);
+ int y2 = ( y0 == y3 ) ? y3 : ((y0<y3) ? y3-3 : y3+3);
+
+ if (bumpNow) x0 += 3;
+ if (bumpNext) x2 += 3;
+
+ if ( !bumpNow && !bumpNext )
+ {
+ drawLineList += QPoint( x0, y0 );
+ drawLineList += QPoint( x2, y3 );
+ }
+ else if (bumpNow)
+ {
+ drawLineList += QPoint( x0, y0 );
+ drawLineList += QPoint( x1, y1 );
+ drawLineList += QPoint( x2, y3 );
+ }
+ else if (bumpNext)
+ {
+ drawLineList += QPoint( x0, y0 );
+ drawLineList += QPoint( x1, y2 );
+ drawLineList += QPoint( x2, y3 );
+ }
+ else
+ {
+ drawLineList += QPoint( x0, y0 );
+ drawLineList += QPoint( x1, y1 );
+ drawLineList += QPoint( x1, y2 );
+ drawLineList += QPoint( x2, y3 );
+ }
+
+ prevX = x;
+ prevY = y;
+ prevY_canvas = y_canvas;
+ prevX_canvas = x_canvas;
+ bumpNow = bumpNext;
+ }
+
+ // Now, remove redundant points (i.e. those that are either repeated or are
+ // in the same direction as the previous points)
+
+ if ( drawLineList.size() < 3 ) {
+ return;
+ }
+
+ const QPointList::iterator dllEnd = drawLineList.end();
+
+ QPointList::iterator previous = drawLineList.begin();
+
+ QPointList::iterator current = previous;
+ current++;
+
+ QPointList::const_iterator next = current;
+ next++;
+
+ while ( previous != dllEnd && current != dllEnd && next != dllEnd )
+ {
+ const int slope1 = getSlope( (*previous).x(), (*previous).y(), (*current).x(), (*current).y() );
+ const int slope2 = getSlope( (*current).x(), (*current).y(), (*next).x(), (*next).y() );
+
+ if ( slope1 == slope2 || slope1 == 0 || slope2 == 0 )
+ {
+ *current = QPoint( -1, -1 );
+ }
+ else
+ {
+ previous = current;
+ }
+
+ current++;
+ next++;
+ }
+
+ drawLineList.remove( QPoint( -1, -1 ) );
+
+ // Find the bounding rect
+ {
+ int x1=-1, y1=-1, x2=-1, y2=-1;
+ const QPointList::iterator end = drawLineList.end();
+ for ( QPointList::iterator it = drawLineList.begin(); it != end; ++it )
+ {
+ const QPoint p = *it;
+ if ( p.x() < x1 || x1 == -1 ) {
+ x1 = p.x();
+ }
+ if ( p.x() > x2 || x2 == -1 ) {
+ x2 = p.x();
+ }
+ if ( p.y() < y1 || y1 == -1 ) {
+ y1 = p.y();
+ }
+ if ( p.y() > y2 || y2 == -1 ) {
+ y2 = p.y();
+ }
+ }
+
+ QRect boundRect( x1, y1, x2-x1, y2-y1 );
+ if ( boundRect != m_oldBoundRect )
+ {
+ canvas()->setChanged( boundRect | m_oldBoundRect );
+ m_oldBoundRect = boundRect;
+ }
+ }
+
+ //BEGIN build up ConnectorLine list
+ const ConnectorLineList::iterator ConnectorLineEnd = m_connectorLineList.end();
+ for ( ConnectorLineList::iterator it = m_connectorLineList.begin(); it != ConnectorLineEnd; ++it )
+ delete *it;
+ m_connectorLineList.clear();
+
+ if ( drawLineList.size() > 1 )
+ {
+ QPoint prev = drawLineList.first();
+ const QPointList::iterator end = drawLineList.end();
+ for ( QPointList::iterator it = ++drawLineList.begin(); it != end; ++it )
+ {
+ const QPoint next = *it;
+ ConnectorLine *line = new ConnectorLine(this);
+ line->setPoints( prev.x(), prev.y(), next.x(), next.y() );
+ m_connectorLineList.append(line);
+ prev = next;
+ }
+ }
+ updateConnectorLines();
+ //END build up ConnectorPoint list
+}
+
+
+void Connector::setSemiHidden( bool semiHidden )
+{
+ if ( !canvas() || semiHidden == b_semiHidden )
+ return;
+
+ b_semiHidden = semiHidden;
+ updateConnectorLines();
+}
+
+
+void Connector::updateConnectorPoints( bool add )
+{
+ if (!canvas()) {
+ return;
+ }
+
+ if ( b_deleted || !isVisible() )
+ add = false;
+
+ // Check we haven't already added/removed the points...
+ if ( b_pointsAdded == add )
+ return;
+
+ b_pointsAdded = add;
+
+ // We don't include the end points in the mapping
+ if ( m_conRouter->cellPointList()->size() < 3 ) {
+ return;
+ }
+
+ const int mult = (add)?1:-1;
+ const QPointList::iterator end = --m_conRouter->cellPointList()->end();
+ for ( QPointList::iterator it = ++m_conRouter->cellPointList()->begin(); it != end; ++it )
+ {
+ int x = (*it).x();
+ int y = (*it).y();
+
+ // Add the points of this connector to the cell array in the ICNDocument,
+ // so that other connectors still to calculate their points know to try
+ // and avoid this connector
+
+ p_icnDocument->addCPenalty( x, y-1, mult*ICNDocument::hs_connector/2 );
+ p_icnDocument->addCPenalty( x-1, y, mult*ICNDocument::hs_connector/2 );
+ p_icnDocument->addCPenalty( x, y, mult*ICNDocument::hs_connector );
+ p_icnDocument->addCPenalty( x+1, y, mult*ICNDocument::hs_connector/2 );
+ p_icnDocument->addCPenalty( x, y+1, mult*ICNDocument::hs_connector/2 );
+
+ if ( p_icnDocument->isValidCellReference( x, y ) ) {
+ (*p_icnDocument->cells())[x][y].numCon += mult;
+ }
+ }
+
+// updateDrawList();
+}
+
+
+void Connector::setRoutePoints( QPointList pointList, bool setManual, bool checkEndPoints )
+{
+ if (!canvas()) {
+ return;
+ }
+ updateConnectorPoints(false);
+
+ bool reversed = pointsAreReverse(pointList);
+ if (checkEndPoints)
+ {
+ if (reversed)
+ {
+ pointList.prepend( QPoint( int(m_endNode->x()), int(m_endNode->y()) ) );
+ pointList.append( QPoint( int(m_startNode->x()), int(m_startNode->y()) ) );
+ }
+ else
+ {
+ pointList.prepend( QPoint( int(m_startNode->x()), int(m_startNode->y()) ) );
+ pointList.append( QPoint( int(m_endNode->x()), int(m_endNode->y()) ) );
+ }
+ }
+
+ m_conRouter->setPoints( pointList, reversed );
+ b_manualPoints = setManual;
+ updateConnectorPoints(true);
+}
+
+
+bool Connector::pointsAreReverse( const QPointList &pointList ) const
+{
+ if ( !m_startNode || !m_endNode )
+ {
+ kdWarning() << k_funcinfo << "Cannot determine orientation as no start and end nodes" << endl;
+ return false;
+ }
+
+ if ( pointList.isEmpty() )
+ return false;
+
+
+ int plsx = pointList.first().x();
+ int plsy = pointList.first().y();
+ int plex = pointList.last().x();
+ int pley = pointList.last().y();
+
+ double nsx = m_startNode->x();
+ double nsy = m_startNode->y();
+ double nex = m_endNode->x();
+ double ney = m_endNode->y();
+
+ double dist_normal = (nsx-plsx)*(nsx-plsx) + (nsy-plsy)*(nsy-plsy) + (nex-plex)*(nex-plex) + (ney-pley)*(ney-pley);
+ double dist_reverse = (nsx-plex)*(nsx-plex) + (nsy-pley)*(nsy-pley) + (nex-plsx)*(nex-plsx) + (ney-plsy)*(ney-plsy);
+
+ return dist_reverse < dist_normal;
+}
+
+
+void Connector::rerouteConnector()
+{
+ if (!isVisible())
+ return;
+
+ if ( nodeGroup() )
+ {
+ kdWarning() << k_funcinfo << "Connector is controlled by a NodeGroup! Use that to reroute the connector" << endl;
+ return;
+ }
+
+ if ( !startNode() || !endNode() )
+ return;
+
+ updateConnectorPoints(false);
+ m_conRouter->mapRoute( int(startNode()->x()), int(startNode()->y()), int(endNode()->x()), int(endNode()->y()) );
+ b_manualPoints = false;
+ updateConnectorPoints(true);
+}
+
+
+void Connector::translateRoute( int dx, int dy )
+{
+ updateConnectorPoints(false);
+ m_conRouter->translateRoute( dx, dy );
+ updateConnectorPoints(true);
+ updateDrawList();
+}
+
+
+void Connector::restoreFromConnectorData( const ConnectorData &connectorData )
+{
+ updateConnectorPoints(false);
+ b_manualPoints = connectorData.manualRoute;
+ m_conRouter->setRoutePoints( connectorData.route );
+ updateConnectorPoints(true);
+ updateDrawList();
+}
+
+
+ConnectorData Connector::connectorData() const
+{
+ ConnectorData connectorData;
+ if ( !m_startNode || !m_endNode )
+ {
+ kdDebug() << k_funcinfo << " m_startNode="<<m_startNode<<" m_endNode="<<m_endNode<<endl;
+ return connectorData;
+ }
+
+ connectorData.manualRoute = usesManualPoints();
+ connectorData.route = *m_conRouter->cellPointList();
+
+ if ( m_startNode->isChildNode() )
+ {
+ connectorData.startNodeIsChild = true;
+ connectorData.startNodeCId = m_startNode->childId();
+ connectorData.startNodeParent = m_startNode->parentItem()->id();
+ }
+ else
+ {
+ connectorData.startNodeIsChild = false;
+ connectorData.startNodeId = m_startNode->id();
+ }
+
+ if ( m_endNode->isChildNode() )
+ {
+ connectorData.endNodeIsChild = true;
+ connectorData.endNodeCId = m_endNode->childId();
+ connectorData.endNodeParent = m_endNode->parentItem()->id();
+ }
+ else
+ {
+ connectorData.endNodeIsChild = false;
+ connectorData.endNodeId = m_endNode->id();
+ }
+
+ return connectorData;
+}
+
+
+void Connector::setVisible( bool yes )
+{
+ if ( !canvas() || isVisible() == yes )
+ return;
+
+ QCanvasPolygon::setVisible(yes);
+ updateConnectorLines();
+}
+
+
+void Connector::setSelected( bool yes )
+{
+ if ( !canvas() || isSelected() == yes )
+ return;
+
+ QCanvasPolygon::setSelected(yes);
+ updateConnectorLines();
+ emit selected(yes);
+}
+
+
+void Connector::updateConnectorLines()
+{
+ const QColor color = b_semiHidden ? Qt::gray : (isSelected() ? QColor( 101, 134, 192 ) : Qt::black);
+// const QColor color = b_semiHidden ? Qt::gray : (isSelected() ? QColor( 0x7f, 0x7f, 0xff ) : Qt::black);
+ const int z = ICNDocument::Z::Connector + (isSelected() ? 5 : 0);
+
+ QPen pen( color, (numWires() > 1) ? 2 : 1 );
+
+ const ConnectorLineList::iterator end = m_connectorLineList.end();
+ for ( ConnectorLineList::iterator it = m_connectorLineList.begin(); it != end; ++it )
+ {
+ QCanvasPolygonalItem *item = static_cast<QCanvasPolygonalItem*>(*it);
+ item->setZ(z);
+ item->setPen(pen);
+ item->setBrush(color);
+ item->setVisible( isVisible() );
+ }
+}
+
+
+QValueList<QPointList> Connector::splitConnectorPoints( const QPoint & pos ) const
+{
+ return m_conRouter->splitPoints(pos);
+}
+
+
+QPointList Connector::connectorPoints( bool reverse ) const
+{
+ bool doReverse = (reverse != pointsAreReverse( m_conRouter->pointList(false) ));
+ return m_conRouter->pointList(doReverse);
+}
+//END class Connector
+
+
+//BEGIN class ConnectorLine
+ConnectorLine::ConnectorLine( Connector *connector )
+ : QObject(connector), QCanvasLine( connector->canvas() )
+{
+ p_connector = connector;
+}
+
+
+int ConnectorLine::rtti() const
+{
+ return ICNDocument::RTTI::ConnectorLine;
+}
+//END class ConnectorLine
+
+#include "connector.moc"