00001
00002
00003
00004
00005
00006
00007
00008
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
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
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
00151
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
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
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
00260
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
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
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
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
00353 if ( b_pointsAdded == add ) return;
00354
00355 b_pointsAdded = add;
00356
00357
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
00370
00371
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
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
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
00549
00550
00551
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
00563
00564 #include "connector.moc"