/*************************************************************************** * 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 #include 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 TQPoint toCanvas( const TQPoint * const pos ) { return TQPoint( toCanvas(pos->x()), toCanvas(pos->y()) ); } inline static TQPoint fromCanvas( const TQPoint * const pos ) { return TQPoint( fromCanvas(pos->x()), fromCanvas(pos->y()) ); } inline static TQPoint toCanvas( const TQPoint &pos ) { return TQPoint( toCanvas(pos.x()), toCanvas(pos.y()) ); } inline static TQPoint fromCanvas( const TQPoint &pos ) { return TQPoint( fromCanvas(pos.x()), fromCanvas(pos.y()) ); } //BEGIN class Connector Connector::Connector( Node * startNode, Node * endNode, ICNDocument *icnDocument, TQString *id ) : TQObject(icnDocument), TQCanvasPolygon( 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"<generateUID("connector"); p_icnDocument->registerItem(this); p_icnDocument->requestRerouteInvalidatedConnectors(); setVisible(true); ECNode * startECNode = dynamic_cast(startNode); ECNode * endECNode = dynamic_cast(endNode); if ( startECNode && endECNode ) { connect( startECNode, TQT_SIGNAL(numPinsChanged(unsigned)), this, TQT_SLOT(syncWiresWithNodes()) ); connect( endECNode, TQT_SIGNAL(numPinsChanged(unsigned)), this, TQT_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((Node*)m_startNode); ECNode * endECNode = dynamic_cast((Node*)m_endNode); if ( !startECNode || !endECNode ) return; unsigned newNumWires = 0; if ( startECNode->type() == Node::ec_junction || endECNode->type() == Node::ec_junction ) newNumWires = TQMAX( startECNode->numPins(), endECNode->numPins() ); else newNumWires = TQMIN( 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 TQString &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; } TQPointList 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 TQPointList::const_iterator cplEnd = m_conRouter->cellPointList()->end(); for ( TQPointList::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 x2 || x2 == -1 ) { x2 = p.x(); } if ( p.y() < y1 || y1 == -1 ) { y1 = p.y(); } if ( p.y() > y2 || y2 == -1 ) { y2 = p.y(); } } TQRect 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 ) { TQPoint prev = drawLineList.first(); const TQPointList::iterator end = drawLineList.end(); for ( TQPointList::iterator it = ++drawLineList.begin(); it != end; ++it ) { const TQPoint 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 TQPointList::iterator end = --m_conRouter->cellPointList()->end(); for ( TQPointList::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( TQPointList pointList, bool setManual, bool checkEndPoints ) { if (!canvas()) { return; } updateConnectorPoints(false); bool reversed = pointsAreReverse(pointList); if (checkEndPoints) { if (reversed) { pointList.prepend( TQPoint( int(m_endNode->x()), int(m_endNode->y()) ) ); pointList.append( TQPoint( int(m_startNode->x()), int(m_startNode->y()) ) ); } else { pointList.prepend( TQPoint( int(m_startNode->x()), int(m_startNode->y()) ) ); pointList.append( TQPoint( int(m_endNode->x()), int(m_endNode->y()) ) ); } } m_conRouter->setPoints( pointList, reversed ); b_manualPoints = setManual; updateConnectorPoints(true); } bool Connector::pointsAreReverse( const TQPointList &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="<