conrouter.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2003-2004 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 "conrouter.h"
00012 #include "icndocument.h"
00013 
00014 #include <kdebug.h>
00015 
00016 #include <cassert>
00017 #include <cmath>
00018 
00019 inline static int toCanvas( int pos )
00020 {
00021         return pos*8+4;
00022 }
00023 
00024 inline static int fromCanvas( int pos )
00025 {
00026         return (pos-4)/8;
00027 }
00028 
00029 inline static QPoint toCanvas( const QPoint * const pos )
00030 {
00031         return QPoint( toCanvas(pos->x()), toCanvas(pos->y()) );
00032 }
00033 
00034 inline static QPoint fromCanvas( const QPoint * const pos )
00035 {
00036         return QPoint( fromCanvas(pos->x()), fromCanvas(pos->y()) );
00037 }
00038 
00039 inline static QPoint toCanvas( const QPoint &pos )
00040 {
00041         return QPoint( toCanvas(pos.x()), toCanvas(pos.y()) );
00042 }
00043 
00044 inline static QPoint fromCanvas( const QPoint &pos )
00045 {
00046         return QPoint( fromCanvas(pos.x()), fromCanvas(pos.y()) );
00047 }
00048 
00049 static inline int roundDouble( const double x )
00050 {
00051         return int(std::floor(x+0.5));
00052 }
00053 
00054 ConRouter::ConRouter( ICNDocument *cv )
00055 {
00056         p_icnDocument = cv;
00057         m_lcx = m_lcy = 0;
00058 }
00059 
00060 ConRouter::~ConRouter()
00061 {
00062 }
00063 
00064 QPointList ConRouter::pointList( bool reverse ) const
00065 {
00066         QPointList pointList;
00067         
00068         if (reverse) {
00069                 bool notDone = m_cellPointList.size() > 0;
00070                 for ( QPointList::const_iterator it = m_cellPointList.fromLast(); notDone; --it )
00071                 {
00072                         pointList.append( toCanvas(&*it) );
00073                         if ( it == m_cellPointList.begin() ) notDone = false;
00074                 }
00075         } else {
00076                 const QPointList::const_iterator end = m_cellPointList.end();
00077                 for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end; ++it )
00078                 {
00079                         pointList.append( toCanvas(&*it) );
00080                 }
00081         }
00082         
00083         return pointList;
00084 }
00085 
00086 static double qpoint_distance( const QPoint & p1, const QPoint & p2 )
00087 {
00088         double dx = p1.x() - p2.x();
00089         double dy = p1.y() - p2.y();
00090         
00091         return std::sqrt( dx*dx + dy*dy );
00092 }
00093 
00094 QPointListList ConRouter::splitPoints( const QPoint &pos ) const
00095 {
00096         const QPoint split = fromCanvas(&pos);
00097         
00098         QValueList<QPointList> list;
00099         
00100         // Check that the point is in the connector points, and not at the start or end
00101         bool found = false;
00102         QPointList::const_iterator end = m_cellPointList.end();
00103         
00104         double dl[] = { 0.0, 1.1, 1.5 }; // sqrt(2) < 1.5 < sqrt(5)
00105         for ( unsigned i = 0; (i < 3) && !found; ++i )
00106         {
00107                 for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end && !found; ++it )
00108                 {
00109                         if ( qpoint_distance( *it, split ) <= dl[i] && it != m_cellPointList.begin() && it != m_cellPointList.fromLast() )
00110                                 found = true;
00111                 }
00112         }
00113         
00114         QPointList first;
00115         QPointList second;
00116         
00117         if (!found) {
00118                 kdWarning() << "ConRouter::splitConnectorPoints: Could not find point ("<<pos.x()<<", "<<pos.y()<<") in connector points"<<endl;
00119                 kdWarning() << "ConRouter::splitConnectorPoints: Returning generic list"<<endl;
00120                 
00121                 first.append( toCanvas(m_cellPointList.first()) );
00122                 first.append(pos);
00123                 second.append(pos);
00124                 second.append( toCanvas(m_cellPointList.last()) );
00125                 
00126                 list.append(first);
00127                 list.append(second);
00128                 
00129                 return list;
00130         }
00131         
00132         // Now add the points to the two lists
00133         bool gotToSplit = false;
00134         for ( QPointList::const_iterator it = m_cellPointList.begin(); it != end; ++it )
00135         {
00136                 QPoint canvasPoint = toCanvas(&*it);
00137                 if ( *it == split )
00138                 {
00139                         gotToSplit = true;
00140                         first.append(canvasPoint);
00141                         second.prepend(canvasPoint);
00142                 }
00143                 else if (!gotToSplit)
00144                 {
00145                         first.append(canvasPoint);
00146                 }
00147                 else /*if (gotToSplit)*/
00148                 {
00149                         second.append(canvasPoint);
00150                 }
00151         }
00152         
00153         list.append(first);
00154         list.append(second);
00155         
00156         return list;
00157 }
00158 
00159 QPointListList ConRouter::dividePoints( uint n ) const
00160 {
00161         // Divide the points up into n pieces...
00162         
00163         QPointList points = m_cellPointList;
00164         assert( n != 0 );
00165         if ( points.size() == 0 ) {
00166                 points += QPoint( toCanvas(m_lcx), toCanvas(m_lcy) );
00167         }
00168         
00169         const float avgLength = float(points.size()-1)/float(n);
00170         
00171         QPointListList pll;
00172         for ( uint i=0; i<n; ++i )
00173         {
00174                 QPointList pl;
00175                 // Get the points between (pos) and (pos+avgLength)
00176                 const int endPos = roundDouble( avgLength*(i+1) );
00177                 const int startPos = roundDouble( avgLength*i );
00178                 const QPointList::iterator end = ++points.at(endPos);
00179                 for ( QPointList::iterator it = points.at(startPos); it != end; ++it )
00180                 {
00181                         pl += toCanvas(*it);
00182                 }
00183                 pll += pl;
00184         }
00185         return pll;
00186 }
00187 
00188 void ConRouter::checkACell( int x, int y, Cell *prev, int prevX, int prevY, int nextScore )
00189 {
00190         if ( !p_icnDocument->isValidCellReference(x,y) ) return;
00191         Cell * const c = &(*cellsPtr)[x][y];
00192         if ( c->permanent ) return;
00193         int newScore = nextScore + c->CIpenalty + c->Cpenalty;
00194         
00195         // Check for changing direction
00196         if              ( x != prevX && prev->prevX == prevX ) newScore += 5;
00197         else if ( y != prevY && prev->prevY == prevY ) newScore += 5;
00198         
00199         if ( c->bestScore < newScore ) return;
00200         
00201         // We only want to change the previous cell if the score is different,
00202         // or the score is the same but this cell allows the connector
00203         // to travel in the same direction
00204         
00205         if ( c->bestScore == newScore &&
00206                  x != prevX &&
00207                  y != prevY ) return;
00208         
00209         c->bestScore = newScore;
00210         c->prevX = prevX;
00211         c->prevY = prevY;
00212 
00213         if ( !c->addedToLabels ) {
00214                 c->addedToLabels = true;
00215                 Point point;
00216                 point.x = x;
00217                 point.y = y;
00218                 point.prevX = prevX;
00219                 point.prevY = prevY;
00220                 TempLabelMap::iterator it = tempLabels.insert( std::make_pair(newScore,point) );
00221                 c->point = &it->second;
00222         } else {
00223                 c->point->prevX = prevX;
00224                 c->point->prevY = prevY;
00225         }
00226 }
00227 
00228 void ConRouter::checkCell( int x, int y )
00229 {
00230         Cell * const c = &(*cellsPtr)[x][y];
00231         
00232         c->permanent = true;
00233         const int nextScore = c->bestScore+1;
00234         
00235         // Check the surrounding cells (up, left, right, down)
00236         if ( y > 0 )            checkACell( x, y-1, c, x, y, nextScore );
00237         if ( x > 0 )            checkACell( x-1, y, c, x, y, nextScore );
00238         if ( x+1 < xcells ) checkACell( x+1, y, c, x, y, nextScore );
00239         if ( y+1 < ycells ) checkACell( x, y+1, c, x, y, nextScore );
00240 }
00241 
00242 
00243 bool ConRouter::needsRouting( int sx, int sy, int ex, int ey ) const
00244 {
00245         if ( m_cellPointList.size() < 2 )
00246         {
00247                 // Better be on the safe side...
00248                 return true;
00249         }
00250         
00251         const int scx = fromCanvas(sx);
00252         const int scy = fromCanvas(sy);
00253         const int ecx = fromCanvas(ex);
00254         const int ecy = fromCanvas(ey);
00255         
00256         const int psx = m_cellPointList.first().x();
00257         const int psy = m_cellPointList.first().y();
00258         const int pex = m_cellPointList.last().x();
00259         const int pey = m_cellPointList.last().y();
00260         
00261         return (psx != scx || psy != scy || pex != ecx || pey != ecy ) &&
00262                    (pex != scx || pey != scy || psx != ecx || psy != ecy );
00263 }
00264 
00265 void ConRouter::setRoutePoints( const QPointList &pointList )
00266 {
00267         m_cellPointList = pointList;
00268         removeDuplicatePoints();
00269 }
00270 
00271 void ConRouter::setPoints( const QPointList &pointList, bool reverse  )
00272 {
00273         if (  pointList.size() == 0 ) return;
00274 
00275         QPointList cellPointList;
00276 
00277         QPoint prevCellPoint = fromCanvas(*pointList.begin());
00278         cellPointList.append(prevCellPoint);
00279         const QPointList::const_iterator end = pointList.end();
00280         for ( QPointList::const_iterator it = pointList.begin(); it != end; ++it )
00281         {
00282                 QPoint cellPoint = fromCanvas(*it);
00283                 
00284                 while ( prevCellPoint != cellPoint )
00285                 {
00286                         cellPointList.append(prevCellPoint);
00287                         
00288                         if              ( prevCellPoint.x() < cellPoint.x() ) prevCellPoint.setX( prevCellPoint.x()+1 );
00289                         else if ( prevCellPoint.x() > cellPoint.x() ) prevCellPoint.setX( prevCellPoint.x()-1 );
00290                         if              ( prevCellPoint.y() < cellPoint.y() ) prevCellPoint.setY( prevCellPoint.y()+1 );
00291                         else if ( prevCellPoint.y() > cellPoint.y() ) prevCellPoint.setY( prevCellPoint.y()-1 );
00292                 };
00293                 
00294                 prevCellPoint = cellPoint;
00295         }
00296         cellPointList.append(prevCellPoint);
00297         
00298         if (reverse) {
00299                 m_cellPointList.clear();
00300                 const QPointList::iterator begin = cellPointList.begin();
00301                 for ( QPointList::iterator it = cellPointList.fromLast(); it != begin; --it )
00302                 {
00303                         m_cellPointList += *it;
00304                 }
00305                 m_cellPointList += *begin;
00306         } else {
00307                 m_cellPointList = cellPointList;
00308         }
00309         
00310         removeDuplicatePoints();
00311 }
00312 
00313 
00314 void ConRouter::translateRoute( int dx, int dy )
00315 {
00316         if ( dx == 0 && dy == 0 ) {
00317                 return;
00318         }
00319         
00320         m_lcx += dx;
00321         m_lcy += dy;
00322         
00323 //      const QPoint ds = QPoint( fromCanvas(dx), fromCanvas(dy) );
00324         const QPoint ds = QPoint( dx/8, dy/8 );
00325         
00326         QPointList::iterator end = m_cellPointList.end();
00327         for ( QPointList::iterator it = m_cellPointList.begin(); it != end; ++it )
00328         {
00329                 (*it) += ds;
00330         }
00331         
00332         removeDuplicatePoints();
00333 }
00334 
00335 
00336 void ConRouter::mapRoute( int sx, int sy, int ex, int ey )
00337 {
00338         const int scx = fromCanvas(sx);
00339         const int scy = fromCanvas(sy);
00340         const int ecx = fromCanvas(ex);
00341         const int ecy = fromCanvas(ey);
00342         
00343         if ( !p_icnDocument->isValidCellReference( scx, scy ) ||
00344                  !p_icnDocument->isValidCellReference( ecx, ecy ) )
00345         {
00346                 return;
00347         }
00348         
00349         m_cellPointList.clear();
00350         m_lcx = ecx;
00351         m_lcy = ecy;
00352         
00353         
00354         // First, lets try some common connector routes (which will not necesssarily
00355         // be shortest, but they will be neat, and cut down on overall CPU usage)
00356         // If that fails, we will resort to a shortest-route algorithm to find an
00357         // appropriate route.
00358         
00359         // Connector configuration: Line
00360         {
00361                 bool ok = checkLineRoute( scx, scy, ecx, ecy, 4*ICNDocument::hs_connector, 0 );
00362                 if (ok) {
00363                         return;
00364                 } else {
00365                         m_cellPointList.clear();
00366                 }
00367         }
00368 
00369         // Corner 1
00370         {
00371                 bool ok = checkLineRoute( scx, scy, ecx, ecy, 2*ICNDocument::hs_connector, 0 );
00372                 if (!ok) m_cellPointList.clear();
00373                 else {
00374                         ok = checkLineRoute( scx, scy, ecx, ecy, ICNDocument::hs_connector-1, 0 );
00375 
00376                         if(ok)  return;
00377                         else m_cellPointList.clear();
00378                 }
00379         }
00380 
00381         // Corner 2
00382         {
00383                 bool ok = checkLineRoute( scx, scy, ecx, ecy, 2*ICNDocument::hs_connector, 0 );
00384                 if (!ok) {
00385                         m_cellPointList.clear();
00386                 } else {
00387                         ok = checkLineRoute( scx, scy, ecx, ecy, ICNDocument::hs_connector-1, 0 );
00388                         if (ok) {
00389                                 return;
00390                         } else {
00391                                 m_cellPointList.clear();
00392                         }
00393                 }
00394         }
00395         
00396         // It seems we must resort to brute-force route-checking
00397         {       
00398                 cellsPtr = p_icnDocument->cells();
00399                 cellsPtr->reset();
00400         
00401                 xcells = p_icnDocument->canvas()->width()/8;
00402                 ycells = p_icnDocument->canvas()->height()/8;
00403         
00404                 // Now to map out the shortest routes to the cells
00405                 Cell * const startCell = &(*cellsPtr)[ecx][ecy];
00406                 startCell->permanent = true;
00407                 startCell->bestScore = 0;
00408                 startCell->prevX = -1;
00409                 startCell->prevY = -1;
00410                 
00411                 tempLabels.clear();
00412                 checkCell( ecx, ecy );
00413                 
00414                 // Daniel: I changed it from a do while to a while otherwise
00415                 // in rare cases the iterator can end up as end().
00416                 while ( tempLabels.size() > 0 && !(*cellsPtr)[scx][scy].permanent )
00417                 {
00418                         TempLabelMap::iterator it = tempLabels.begin();
00419                         checkCell( it->second.x, it->second.y );
00420                         tempLabels.erase(it);
00421                 }
00422                 
00423                 // Now, retrace the shortest route from the endcell to get out points :)
00424                 int x = scx, y = scy;
00425                 bool ok = true;
00426 
00427                 do {
00428                         m_cellPointList.append( QPoint( x, y ) );
00429                         int newx = (*cellsPtr)[x][y].prevX;
00430                         int newy = (*cellsPtr)[x][y].prevY;
00431                         if ( newx == x && newy == y ) {
00432                                 ok = false;
00433                         }
00434                         x = newx;
00435                         y = newy;
00436                 } while ( p_icnDocument->isValidCellReference(x,y) && x != -1 && y != -1 && ok );
00437                 
00438                 // And append the last point...
00439                 m_cellPointList.append( QPoint( ecx, ecy ) );
00440         }
00441         
00442         removeDuplicatePoints();
00443 }
00444 
00445 
00446 bool ConRouter::checkLineRoute( int scx, int scy, int ecx, int ecy, int maxConScore, int maxCIScore )
00447 {
00448         if ( (scx != ecx) && (scy != ecy) ) {
00449                 return false;
00450         }
00451         
00452         const bool isHorizontal = scy == ecy;
00453         
00454         int start=0, end=0, x=0, y=0, dd=0;
00455         if (isHorizontal)
00456         {
00457                 dd = (scx<ecx)?1:-1;
00458                 start = scx;
00459                 end = ecx+dd;
00460                 y = scy;
00461         } else {
00462                 dd = (scy<ecy)?1:-1;
00463                 start = scy;
00464                 end = ecy+dd;
00465                 x = scx;
00466         }
00467         
00468         Cells *cells = p_icnDocument->cells();
00469         
00470         if (isHorizontal)
00471         {
00472                 for ( int x = start; x!=end; x+=dd )
00473                 {
00474                         if ( std::abs(x-start)>1 && std::abs(x-end)>1 && ((*cells)[x][y].CIpenalty > maxCIScore || (*cells)[x][y].Cpenalty > maxConScore) )
00475                         {
00476                                 return false;
00477                         } else {
00478                                 m_cellPointList.append( QPoint( x, y ) );
00479                         }
00480                 }
00481         } else {
00482                 for ( int y = start; y!=end; y+=dd )
00483                 {
00484                         if ( std::abs(y-start)>1 && std::abs(y-end)>1 && ((*cells)[x][y].CIpenalty > maxCIScore || (*cells)[x][y].Cpenalty > maxConScore) )
00485                         {
00486                                 return false;
00487                         } else {
00488                                 m_cellPointList.append( QPoint( x, y ) );
00489                         }
00490                 }
00491         }
00492         
00493         m_cellPointList.prepend( QPoint( scx, scy ) );
00494         m_cellPointList.append( QPoint( ecx, ecy ) );
00495         removeDuplicatePoints();
00496         return true;
00497 }
00498 
00499 void ConRouter::removeDuplicatePoints()
00500 {
00501         QPoint prev(-1,-1);
00502         
00503         const QPointList::iterator end = m_cellPointList.end();
00504         for ( QPointList::iterator it = m_cellPointList.begin(); it != end; ++it )
00505         {
00506                 if ( *it == prev ) {
00507                         *it = QPoint(-1,-1);
00508                 } else {
00509                         prev = *it;
00510                 }
00511         }
00512         m_cellPointList.remove( QPoint(-1,-1) );
00513 }

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