connector.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2003-2005 by David Saxton                               *
00003  *   david@bluehaze.org                                                    *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  ***************************************************************************/
00010 
00011 #include "circuitdocument.h"
00012 #include "connector.h"
00013 #include "conrouter.h"
00014 #include "cnitem.h"
00015 #include "ecnode.h"
00016 #include "itemdocumentdata.h"
00017 #include "wire.h"
00018 
00019 #include <kdebug.h>
00020 #include <cmath>
00021 
00022 inline static int toCanvas( int pos )
00023 {
00024         return (pos<<3) | 4;
00025 }
00026 
00027 inline static int fromCanvas( int pos )
00028 {
00029         return (pos-4) >> 3;
00030 }
00031 
00032 inline static QPoint toCanvas( const QPoint * const pos )
00033 {
00034         return QPoint( toCanvas(pos->x()), toCanvas(pos->y()) );
00035 }
00036 
00037 inline static QPoint fromCanvas( const QPoint * const pos )
00038 {
00039         return QPoint( fromCanvas(pos->x()), fromCanvas(pos->y()) );
00040 }
00041 
00042 inline static QPoint toCanvas( const QPoint &pos )
00043 {
00044         return QPoint( toCanvas(pos.x()), toCanvas(pos.y()) );
00045 }
00046 
00047 inline static QPoint fromCanvas( const QPoint &pos )
00048 {
00049         return QPoint( fromCanvas(pos.x()), fromCanvas(pos.y()) );
00050 }
00051 
00052 //BEGIN class Connector
00053 Connector::Connector( Node * startNode, Node * endNode, ICNDocument *icnDocument, QString *id )
00054         : QObject(icnDocument),
00055           QCanvasPolygon( icnDocument->canvas() )
00056 {
00057         p_icnDocument = icnDocument;
00058         m_conRouter = new ConRouter(p_icnDocument);
00059         p_parentContainer = 0;
00060         m_startNode = startNode;
00061         m_endNode = endNode;
00062         p_nodeGroup = 0;
00063         b_semiHidden = false;
00064         b_deleted = false;
00065         b_pointsAdded = false;
00066         b_manualPoints = false;
00067         m_bIsSyncingWires = false;
00068 
00069         if (id) {
00070                 m_id = *id;
00071                 if ( !p_icnDocument->registerUID(*id) ) {
00072 //                      kdDebug() << k_funcinfo << "KTechlab: Connector attempted to register given ID, but ID already in use"<<endl;
00073                 }
00074         } else m_id = p_icnDocument->generateUID("connector");
00075 
00076         p_icnDocument->registerItem(this);
00077 
00078         p_icnDocument->requestRerouteInvalidatedConnectors();
00079         setVisible(true);
00080 
00081         ECNode * startECNode = dynamic_cast<ECNode*>(startNode);
00082         ECNode * endECNode = dynamic_cast<ECNode*>(endNode);
00083         if ( startECNode && endECNode ) {
00084                 connect( startECNode, SIGNAL(numPinsChanged(unsigned)), this, SLOT(syncWiresWithNodes()) );
00085                 connect( endECNode, SIGNAL(numPinsChanged(unsigned)), this, SLOT(syncWiresWithNodes()) );
00086                 syncWiresWithNodes();
00087         }
00088 }
00089 
00090 Connector::~Connector()
00091 {
00092         p_icnDocument->unregisterUID( id() );
00093         delete m_conRouter;
00094 
00095         for( unsigned i = 0; i < m_wires.size(); i++ ) delete m_wires[i];
00096 
00097         m_wires.resize(0);
00098 }
00099 
00100 int Connector::rtti() const
00101 {
00102         return ItemDocument::RTTI::Connector;
00103 }
00104 
00105 void Connector::syncWiresWithNodes()
00106 {
00107         ECNode *startECNode = dynamic_cast<ECNode*>((Node*)m_startNode);
00108         ECNode *endECNode = dynamic_cast<ECNode*>((Node*)m_endNode);
00109 
00110         if(!startECNode || !endECNode ) return;
00111 
00112         unsigned newNumWires = 0;
00113 
00114         if(startECNode->type() == Node::ec_junction ||
00115                         endECNode->type() == Node::ec_junction )
00116                 newNumWires = QMAX( startECNode->numPins(), endECNode->numPins() );
00117         else newNumWires = QMIN( startECNode->numPins(), endECNode->numPins() );
00118 
00119         unsigned oldNumWires = m_wires.size();
00120 
00121         if(newNumWires == oldNumWires ) return;
00122 
00123         m_bIsSyncingWires = true;
00124 
00125         if(startECNode->type() == Node::ec_junction )
00126                 startECNode->setNumPins(newNumWires);
00127         if(endECNode->type() == Node::ec_junction )
00128                 endECNode->setNumPins(newNumWires);
00129 
00130         m_bIsSyncingWires = false;
00131         
00132         if(newNumWires > oldNumWires ) {
00133                 m_wires.resize(newNumWires);
00134                 for ( unsigned i = oldNumWires; i < newNumWires; i++ ) {
00135                         if ( startECNode->pin(i) && endECNode->pin(i) )
00136                                 m_wires[i] = new Wire( startECNode->pin(i), endECNode->pin(i) );
00137                 }
00138         } else {
00139                 for ( unsigned i = newNumWires; i < oldNumWires; i++ )
00140                         delete m_wires[i];
00141                 m_wires.resize(newNumWires);
00142         }
00143         
00144         updateConnectorLines();
00145         emit numWiresChanged(newNumWires);
00146 }
00147 
00148 void Connector::setParentContainer( const QString &cnItemId )
00149 {
00150 //      // We only allow the node to be parented once
00151 //      if ( p_parentContainer || !ICNDocument->itemWithID(cnItemId) ) return;
00152         p_parentContainer = p_icnDocument->cnItemWithID(cnItemId);
00153 }
00154 
00155 void Connector::removeConnector( Node* )
00156 {
00157         if (b_deleted) return;
00158 
00159         b_deleted = true;
00160 
00161         // Remove 'penalty' points for this connector from the ICNDocument
00162         updateConnectorPoints(false);
00163 
00164         emit selected(false);
00165         emit removed(this);
00166         if(m_startNode) m_startNode->removeConnector(this);
00167         if(m_endNode) m_endNode->removeConnector(this);
00168         p_icnDocument->appendDeleteList(this);
00169 }
00170 
00171 int getSlope( float x1, float y1, float x2, float y2 )
00172 {
00173         enum slope {
00174                 s_n = 0,//      .
00175                 s_v,    //      |
00176                 s_h,    //      -
00177                 s_s,    //      /
00178                 s_d             //      \ (backwards slash)
00179         };
00180 
00181         if ( x1 == x2 ) {
00182                 if ( y1 == y2 ) return s_n;
00183                 return s_v;
00184         };
00185 
00186         if ( y1 == y2 ) return s_h;
00187         if((y2-y1)/(x2-x1) > 0)return s_s;
00188 
00189         return s_d;
00190 }
00191 
00192 void Connector::updateDrawList()
00193 {
00194         if(!m_startNode || !m_endNode || !canvas()) return;
00195 
00196 
00197         QPointList drawLineList;
00198 
00199         int prevX = (*m_conRouter->cellPointList()->begin()).x();
00200         int prevY = (*m_conRouter->cellPointList()->begin()).y();
00201 
00202         int prevX_canvas = toCanvas(prevX);
00203         int prevY_canvas = toCanvas(prevY);
00204 
00205         Cells *cells = p_icnDocument->cells();
00206 
00207         bool bumpNow = false;
00208         const QPointList::const_iterator cplEnd = m_conRouter->cellPointList()->end();
00209         for ( QPointList::const_iterator it = m_conRouter->cellPointList()->begin(); it != cplEnd; ++it ) {
00210                 const int x = (*it).x();
00211                 const int y = (*it).y();
00212                 const int numCon = p_icnDocument->isValidCellReference(x,y) ? (*cells)[x][y].numCon : 0;
00213 
00214                 const int y_canvas = toCanvas(y);
00215                 const int x_canvas = toCanvas(x);
00216 
00217                 const bool bumpNext = ( prevX == x &&
00218                         numCon > 1 &&
00219                         std::abs(y_canvas-m_startNode->y())>8 &&
00220                         std::abs(y_canvas-m_endNode->y())>8 );
00221 
00222                 int x0 = prevX_canvas;
00223                 int x2 = x_canvas;
00224                 int x1 = (x0+x2)/2;
00225                 
00226                 int y0 = prevY_canvas;
00227                 int y3 = y_canvas;
00228                 int y1 = ( y0 == y3 ) ? y0 : ((y0<y3) ? y0+3 : y0-3);
00229                 int y2 = ( y0 == y3 ) ? y3 : ((y0<y3) ? y3-3 : y3+3);
00230                 
00231                 if (bumpNow) x0 += 3;
00232                 if (bumpNext) x2 += 3;
00233                 
00234                 if(!bumpNow && !bumpNext) {
00235                         drawLineList += QPoint( x0, y0 );
00236                         drawLineList += QPoint( x2, y3 );
00237                 } else if (bumpNow) {
00238                         drawLineList += QPoint( x0, y0 );
00239                         drawLineList += QPoint( x1, y1 );
00240                         drawLineList += QPoint( x2, y3 );
00241                 } else if (bumpNext) {
00242                         drawLineList += QPoint( x0, y0 );
00243                         drawLineList += QPoint( x1, y2 );
00244                         drawLineList += QPoint( x2, y3 );
00245                 } else {
00246                         drawLineList += QPoint( x0, y0 );
00247                         drawLineList += QPoint( x1, y1 );
00248                         drawLineList += QPoint( x1, y2 );
00249                         drawLineList += QPoint( x2, y3 );
00250                 }
00251                 
00252                 prevX = x;
00253                 prevY = y;
00254                 prevY_canvas = y_canvas;
00255                 prevX_canvas = x_canvas;
00256                 bumpNow = bumpNext;
00257         }
00258         
00259         // Now, remove redundant points (i.e. those that are either repeated or are
00260         // in the same direction as the previous points)
00261 
00262         if(drawLineList.size() < 3) return;
00263 
00264         const QPointList::iterator dllEnd = drawLineList.end();
00265 
00266         QPointList::iterator previous = drawLineList.begin();
00267 
00268         QPointList::iterator current = previous;
00269         current++;
00270 
00271         QPointList::const_iterator next = current;
00272         next++;
00273 
00274         while(previous != dllEnd && current != dllEnd && next != dllEnd) {
00275                 const int slope1 = getSlope( (*previous).x(), (*previous).y(), (*current).x(), (*current).y() );
00276                 const int slope2  = getSlope( (*current).x(), (*current).y(), (*next).x(), (*next).y() );
00277 
00278                 if(slope1 == slope2 || slope1 == 0 || slope2 == 0) {
00279                         *current = QPoint( -1, -1 );
00280                 } else {
00281                         previous = current;
00282                 }
00283 
00284                 current++;
00285                 next++;
00286         }
00287 
00288         drawLineList.remove( QPoint( -1, -1 ) );
00289 
00290         // Find the bounding rect
00291         {
00292                 int x1=-1, y1=-1, x2=-1, y2=-1;
00293                 const QPointList::iterator end = drawLineList.end();
00294                 for ( QPointList::iterator it = drawLineList.begin(); it != end; ++it ) {
00295                         const QPoint p = *it;
00296                         if ( p.x() < x1 || x1 == -1 ) {
00297                                 x1 = p.x();
00298                         }
00299                         if ( p.x() > x2 || x2 == -1 ) {
00300                                 x2 = p.x();
00301                         }
00302                         if ( p.y() < y1 || y1 == -1 ) {
00303                                 y1 = p.y();
00304                         }
00305                         if ( p.y() > y2 || y2 == -1 ) {
00306                                 y2 = p.y();
00307                         }
00308                 }
00309 
00310                 QRect boundRect( x1, y1, x2-x1, y2-y1 );
00311                 if(boundRect != m_oldBoundRect) {
00312                         canvas()->setChanged( boundRect | m_oldBoundRect );
00313                         m_oldBoundRect = boundRect;
00314                 }
00315         }
00316         
00317         //BEGIN build up ConnectorLine list
00318         const ConnectorLineList::iterator ConnectorLineEnd = m_connectorLineList.end();
00319         for ( ConnectorLineList::iterator it = m_connectorLineList.begin(); it != ConnectorLineEnd; ++it )
00320                 delete *it;
00321         m_connectorLineList.clear();
00322         
00323         if ( drawLineList.size() > 1 ) {
00324                 QPoint prev = drawLineList.first();
00325                 const QPointList::iterator end = drawLineList.end();
00326                 for(QPointList::iterator it = ++drawLineList.begin(); it != end; ++it) {
00327                         const QPoint next = *it;
00328                         ConnectorLine *line = new ConnectorLine(this);
00329                         line->setPoints( prev.x(), prev.y(), next.x(), next.y() );
00330                         m_connectorLineList.append(line);
00331                         prev = next;
00332                 }
00333         }
00334         updateConnectorLines();
00335         //END build up ConnectorPoint list
00336 }
00337 
00338 void Connector::setSemiHidden( bool semiHidden )
00339 {
00340         if(!canvas() || semiHidden == b_semiHidden) return;
00341 
00342         b_semiHidden = semiHidden;
00343         updateConnectorLines();
00344 }
00345 
00346 void Connector::updateConnectorPoints( bool add )
00347 {
00348         if (!canvas()) return;
00349 
00350         if(b_deleted || !isVisible()) add = false;
00351 
00352         // Check we haven't already added/removed the points...
00353         if ( b_pointsAdded == add ) return;
00354 
00355         b_pointsAdded = add;
00356 
00357         // We don't include the end points in the mapping
00358         if(m_conRouter->cellPointList()->size() < 3) {
00359                 return;
00360         }
00361 
00362         const int mult = (add)?1:-1;
00363         const QPointList::iterator end = --m_conRouter->cellPointList()->end();
00364         for ( QPointList::iterator it = ++m_conRouter->cellPointList()->begin(); it != end; ++it )
00365         {
00366                 int x = (*it).x();
00367                 int y = (*it).y();
00368                 
00369                 // Add the points of this connector to the cell array in the ICNDocument,
00370                 // so that other connectors still to calculate their points know to try
00371                 // and avoid this connector
00372 
00373                 p_icnDocument->addCPenalty( x,  y-1,    mult*ICNDocument::hs_connector/2 );
00374                 p_icnDocument->addCPenalty( x-1, y,             mult*ICNDocument::hs_connector/2 );
00375                 p_icnDocument->addCPenalty( x,  y,              mult*ICNDocument::hs_connector );
00376                 p_icnDocument->addCPenalty( x+1, y,             mult*ICNDocument::hs_connector/2 );
00377                 p_icnDocument->addCPenalty( x,  y+1,    mult*ICNDocument::hs_connector/2 );
00378                 
00379                 if ( p_icnDocument->isValidCellReference( x, y ) ) {
00380                         (*p_icnDocument->cells())[x][y].numCon += mult;
00381                 }
00382         }
00383 
00384 //      updateDrawList();
00385 }
00386 
00387 
00388 void Connector::setRoutePoints( QPointList pointList, bool setManual, bool checkEndPoints )
00389 {
00390         if (!canvas()) return;
00391 
00392         updateConnectorPoints(false);
00393         
00394         bool reversed = pointsAreReverse(pointList);
00395         if (checkEndPoints)
00396         {
00397                 if (reversed) {
00398                         pointList.prepend( QPoint( int(m_endNode->x()), int(m_endNode->y()) ) );
00399                         pointList.append( QPoint( int(m_startNode->x()), int(m_startNode->y()) ) );
00400                 } else {
00401                         pointList.prepend( QPoint( int(m_startNode->x()), int(m_startNode->y()) ) );
00402                         pointList.append( QPoint( int(m_endNode->x()), int(m_endNode->y()) ) );
00403                 }
00404         }
00405         
00406         m_conRouter->setPoints( pointList, reversed );
00407         b_manualPoints = setManual;
00408         updateConnectorPoints(true);
00409 }
00410 
00411 
00412 bool Connector::pointsAreReverse( const QPointList &pointList ) const
00413 {
00414         if(!m_startNode || !m_endNode) {
00415                 kdWarning() << k_funcinfo << "Cannot determine orientation as no start and end nodes" << endl;
00416                 return false;
00417         }
00418 
00419         if(pointList.isEmpty()) return false;
00420 
00421         int plsx = pointList.first().x();
00422         int plsy = pointList.first().y();
00423         int plex = pointList.last().x();
00424         int pley = pointList.last().y();
00425         
00426         double nsx = m_startNode->x();
00427         double nsy = m_startNode->y();
00428         double nex = m_endNode->x();
00429         double ney = m_endNode->y();
00430         
00431         double dist_normal =  (nsx-plsx)*(nsx-plsx) + (nsy-plsy)*(nsy-plsy) + (nex-plex)*(nex-plex) + (ney-pley)*(ney-pley);
00432         double dist_reverse = (nsx-plex)*(nsx-plex) + (nsy-pley)*(nsy-pley) + (nex-plsx)*(nex-plsx) + (ney-plsy)*(ney-plsy);
00433         
00434         return dist_reverse < dist_normal;
00435 }
00436 
00437 void Connector::rerouteConnector()
00438 {
00439         if(!isVisible()) return;
00440 
00441         if(nodeGroup()) {
00442                 kdWarning() << k_funcinfo << "Connector is controlled by a NodeGroup! Use that to reroute the connector" << endl;
00443                 return;
00444         }
00445 
00446         if(!startNode() || !endNode()) return;
00447 
00448         updateConnectorPoints(false);
00449         m_conRouter->mapRoute( int(startNode()->x()), int(startNode()->y()), int(endNode()->x()), int(endNode()->y()) );
00450         b_manualPoints = false;
00451         updateConnectorPoints(true);
00452 }
00453 
00454 void Connector::translateRoute( int dx, int dy )
00455 {
00456         updateConnectorPoints(false);
00457         m_conRouter->translateRoute( dx, dy );
00458         updateConnectorPoints(true);
00459         updateDrawList();
00460 }
00461 
00462 void Connector::restoreFromConnectorData( const ConnectorData &connectorData )
00463 {
00464         updateConnectorPoints(false);
00465         b_manualPoints = connectorData.manualRoute;
00466         m_conRouter->setRoutePoints( connectorData.route );
00467         updateConnectorPoints(true);
00468         updateDrawList();
00469 }
00470 
00471 ConnectorData Connector::connectorData() const
00472 {
00473         ConnectorData connectorData;
00474         if(!m_startNode || !m_endNode) {
00475                 kdDebug() << k_funcinfo << " m_startNode="<<m_startNode<<" m_endNode="<<m_endNode<<endl;
00476                 return connectorData;
00477         }
00478 
00479         connectorData.manualRoute = usesManualPoints();
00480         connectorData.route = *m_conRouter->cellPointList();
00481 
00482         if(m_startNode->isChildNode()) {
00483                 connectorData.startNodeIsChild = true;
00484                 connectorData.startNodeCId = m_startNode->childId();
00485                 connectorData.startNodeParent = m_startNode->parentItem()->id();
00486         } else {
00487                 connectorData.startNodeIsChild = false;
00488                 connectorData.startNodeId = m_startNode->id();
00489         }
00490 
00491         if(m_endNode->isChildNode()) {
00492                 connectorData.endNodeIsChild = true;
00493                 connectorData.endNodeCId = m_endNode->childId();
00494                 connectorData.endNodeParent = m_endNode->parentItem()->id();
00495         } else {
00496                 connectorData.endNodeIsChild = false;
00497                 connectorData.endNodeId = m_endNode->id();
00498         }
00499         
00500         return connectorData;
00501 }
00502 
00503 void Connector::setVisible( bool yes )
00504 {
00505         if(!canvas() || isVisible() == yes) return;
00506 
00507         QCanvasPolygon::setVisible(yes);
00508         updateConnectorLines();
00509 }
00510 
00511 void Connector::setSelected( bool yes )
00512 {
00513         if(!canvas() || isSelected() == yes) return;
00514 
00515         QCanvasPolygon::setSelected(yes);
00516         updateConnectorLines();
00517         emit selected(yes);
00518 }
00519 
00520 void Connector::updateConnectorLines()
00521 {
00522         const QColor color = b_semiHidden ? Qt::gray : (isSelected() ? QColor( 101, 134, 192 ) : Qt::black);
00523 //      const QColor color = b_semiHidden ? Qt::gray : (isSelected() ? QColor( 0x7f, 0x7f, 0xff ) : Qt::black);
00524         const int z = ICNDocument::Z::Connector + (isSelected() ? 5 : 0);
00525 
00526         QPen pen(color, (numWires() > 1) ? 2 : 1 );
00527 
00528         const ConnectorLineList::iterator end = m_connectorLineList.end();
00529         for(ConnectorLineList::iterator it = m_connectorLineList.begin(); it != end; ++it) {
00530                 QCanvasPolygonalItem *item = static_cast<QCanvasPolygonalItem*>(*it);
00531                 item->setZ(z);
00532                 item->setPen(pen);
00533                 item->setBrush(color);
00534                 item->setVisible( isVisible() );
00535         }
00536 }
00537 
00538 QValueList<QPointList> Connector::splitConnectorPoints(const QPoint &pos) const
00539 {
00540         return m_conRouter->splitPoints(pos);
00541 }
00542 
00543 QPointList Connector::connectorPoints( bool reverse ) const
00544 {
00545         bool doReverse = (reverse != pointsAreReverse( m_conRouter->pointList(false) ));
00546         return m_conRouter->pointList(doReverse);
00547 }
00548 //END class Connector
00549 
00550 
00551 //BEGIN class ConnectorLine
00552 ConnectorLine::ConnectorLine( Connector *connector )
00553         : QObject(connector), QCanvasLine( connector->canvas())
00554 {
00555         p_connector = connector;
00556 }
00557 
00558 int ConnectorLine::rtti() const
00559 {
00560         return ICNDocument::RTTI::ConnectorLine;
00561 }
00562 //END class ConnectorLine
00563 
00564 #include "connector.moc"

Generated on Tue May 8 17:05:28 2007 for KTechLab by  doxygen 1.5.1