00001
00002
00003
00004
00005
00006
00007
00008
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
00101 bool found = false;
00102 QPointList::const_iterator end = m_cellPointList.end();
00103
00104 double dl[] = { 0.0, 1.1, 1.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
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
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
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
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
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
00202
00203
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
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
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
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
00355
00356
00357
00358
00359
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
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
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
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
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
00415
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
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
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 }