/* * vim: ts=4 sw=4 et tw=0 wm=0 * * libavoid - Fast, Incremental, Object-avoiding Line Router * * Copyright (C) 2004-2009 Monash University * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * See the file LICENSE.LGPL distributed with the library. * * Licensees holding a valid commercial license may use this file in * accordance with the commercial license agreement provided with the * library. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Author(s): Michael Wybrow */ #include #include "libavoid/debug.h" #include "libavoid/graph.h" #include "libavoid/connector.h" #include "libavoid/geometry.h" #include "libavoid/timer.h" #include "libavoid/vertices.h" #include "libavoid/router.h" #include "libavoid/assertions.h" using std::pair; namespace Avoid { EdgeInf::EdgeInf(VertInf *v1, VertInf *v2, const bool orthogonal) : lstPrev(NULL), lstNext(NULL), _blocker(0), _router(NULL), _added(false), _visible(false), _orthogonal(orthogonal), _v1(v1), _v2(v2), _dist(-1) { // Not passed NULL values. COLA_ASSERT(v1 && v2); // We are in the same instance COLA_ASSERT(_v1->_router == _v2->_router); _router = _v1->_router; _conns.clear(); } EdgeInf::~EdgeInf() { if (_added) { makeInactive(); } } // Gives an order value between 0 and 3 for the point c, given the last // segment was from a to b. Returns the following value: // 0 : Point c is directly backwards from point b. // 1 : Point c is a left-hand 90 degree turn. // 2 : Point c is a right-hand 90 degree turn. // 3 : Point c is straight ahead (collinear). // static inline int orthogTurnOrder(const Point& a, const Point& b, const Point& c) { // We should only be calling this with orthogonal points, COLA_ASSERT((c.x == b.x) || (c.y == b.y)); COLA_ASSERT((a.x == b.x) || (a.y == b.y)); int direction = vecDir(a, b, c); if (direction > 0) { // Counterclockwise := left return 1; } else if (direction < 0) { // Clockwise := right return 2; } if (b.x == c.x) { if ( ((a.y < b.y) && (c.y < b.y)) || ((a.y > b.y) && (c.y > b.y)) ) { // Behind. return 0; } } else { if ( ((a.x < b.x) && (c.x < b.x)) || ((a.x > b.x) && (c.x > b.x)) ) { // Behind. return 0; } } // Ahead. return 3; } // Returns a less than operation for a set exploration order for orthogonal // searching. Forward, then left, then right. Or if there is no previous // point, then the order is north, east, south, then west. // Note: This method assumes the two Edges that share a common point. bool EdgeInf::rotationLessThan(const VertInf *lastV, const EdgeInf *rhs) const { assert(_v1 == rhs->_v1 || _v1 == rhs->_v2 || _v2 == rhs->_v1 || _v2 == rhs->_v2 ); if ((_v1 == rhs->_v1) && (_v2 == rhs->_v2)) { // Effectively the same visibility edge, so they are equal. return false; } VertInf *lhsV = NULL, *rhsV = NULL, *commonV = NULL; // Determine common Point and the comparison point on the left- and // the right-hand-side. if (_v1 == rhs->_v1) { commonV = _v1; lhsV = _v2; rhsV = rhs->_v2; } else if (_v1 == rhs->_v2) { commonV = _v1; lhsV = _v2; rhsV = rhs->_v1; } else if (_v2 == rhs->_v1) { commonV = _v2; lhsV = _v1; rhsV = rhs->_v2; } else if (_v2 == rhs->_v2) { commonV = _v2; lhsV = _v1; rhsV = rhs->_v1; } const Point& lhsPt = lhsV->point; const Point& rhsPt = rhsV->point; const Point& commonPt = commonV->point; // If no lastPt, use one directly to the left; Point lastPt = (lastV) ? lastV->point : Point(commonPt.x - 10, commonPt.y); int lhsVal = orthogTurnOrder(lastPt, commonPt, lhsPt); int rhsVal = orthogTurnOrder(lastPt, commonPt, rhsPt); return lhsVal < rhsVal; } void EdgeInf::makeActive(void) { COLA_ASSERT(_added == false); if (_orthogonal) { COLA_ASSERT(_visible); _router->visOrthogGraph.addEdge(this); _pos1 = _v1->orthogVisList.insert(_v1->orthogVisList.begin(), this); _v1->orthogVisListSize++; _pos2 = _v2->orthogVisList.insert(_v2->orthogVisList.begin(), this); _v2->orthogVisListSize++; } else { if (_visible) { _router->visGraph.addEdge(this); _pos1 = _v1->visList.insert(_v1->visList.begin(), this); _v1->visListSize++; _pos2 = _v2->visList.insert(_v2->visList.begin(), this); _v2->visListSize++; } else // if (invisible) { _router->invisGraph.addEdge(this); _pos1 = _v1->invisList.insert(_v1->invisList.begin(), this); _v1->invisListSize++; _pos2 = _v2->invisList.insert(_v2->invisList.begin(), this); _v2->invisListSize++; } } _added = true; } void EdgeInf::makeInactive(void) { COLA_ASSERT(_added == true); if (_orthogonal) { COLA_ASSERT(_visible); _router->visOrthogGraph.removeEdge(this); _v1->orthogVisList.erase(_pos1); _v1->orthogVisListSize--; _v2->orthogVisList.erase(_pos2); _v2->orthogVisListSize--; } else { if (_visible) { _router->visGraph.removeEdge(this); _v1->visList.erase(_pos1); _v1->visListSize--; _v2->visList.erase(_pos2); _v2->visListSize--; } else // if (invisible) { _router->invisGraph.removeEdge(this); _v1->invisList.erase(_pos1); _v1->invisListSize--; _v2->invisList.erase(_pos2); _v2->invisListSize--; } } _blocker = 0; _conns.clear(); _added = false; } void EdgeInf::setDist(double dist) { //COLA_ASSERT(dist != 0); if (_added && !_visible) { makeInactive(); COLA_ASSERT(!_added); } if (!_added) { _visible = true; makeActive(); } _dist = dist; _blocker = 0; } bool EdgeInf::added(void) { return _added; } void EdgeInf::alertConns(void) { FlagList::iterator finish = _conns.end(); for (FlagList::iterator i = _conns.begin(); i != finish; ++i) { *(*i) = true; } _conns.clear(); } void EdgeInf::addConn(bool *flag) { _conns.push_back(flag); } void EdgeInf::addCycleBlocker(void) { // Needs to be in invisibility graph. addBlocker(-1); } void EdgeInf::addBlocker(int b) { COLA_ASSERT(_router->InvisibilityGrph); if (_added && _visible) { makeInactive(); COLA_ASSERT(!_added); } if (!_added) { _visible = false; makeActive(); } _dist = 0; _blocker = b; } pair EdgeInf::ids(void) { return std::make_pair(_v1->id, _v2->id); } pair EdgeInf::points(void) { return std::make_pair(_v1->point, _v2->point); } void EdgeInf::db_print(void) { db_printf("Edge("); _v1->id.db_print(); db_printf(","); _v2->id.db_print(); db_printf(")\n"); } void EdgeInf::checkVis(void) { if (_added && !_visible) { db_printf("\tChecking visibility for existing invisibility edge..." "\n\t\t"); db_print(); } else if (_added && _visible) { db_printf("\tChecking visibility for existing visibility edge..." "\n\t\t"); db_print(); } int blocker = 0; bool cone1 = true; bool cone2 = true; VertInf *i = _v1; VertInf *j = _v2; const VertID& iID = i->id; const VertID& jID = j->id; const Point& iPoint = i->point; const Point& jPoint = j->point; _router->st_checked_edges++; if (iID.isShape) { cone1 = inValidRegion(_router->IgnoreRegions, i->shPrev->point, iPoint, i->shNext->point, jPoint); } else if (_router->IgnoreRegions == false) { // If Ignoring regions then this case is already caught by // the invalid regions, so only check it when not ignoring // regions. ShapeSet& ss = _router->contains[iID]; if ((jID.isShape) && (ss.find(jID.objID) != ss.end())) { db_printf("1: Edge of bounding shape\n"); // Don't even check this edge, it should be zero, // since a point in a shape can't see it's corners cone1 = false; } } if (cone1) { // If outside the first cone, don't even bother checking. if (jID.isShape) { cone2 = inValidRegion(_router->IgnoreRegions, j->shPrev->point, jPoint, j->shNext->point, iPoint); } else if (_router->IgnoreRegions == false) { // If Ignoring regions then this case is already caught by // the invalid regions, so only check it when not ignoring // regions. ShapeSet& ss = _router->contains[jID]; if ((iID.isShape) && (ss.find(iID.objID) != ss.end())) { db_printf("2: Edge of bounding shape\n"); // Don't even check this edge, it should be zero, // since a point in a shape can't see it's corners cone2 = false; } } } if (cone1 && cone2 && ((blocker = firstBlocker()) == 0)) { // if i and j see each other, add edge db_printf("\tSetting visibility edge... \n\t\t"); db_print(); double d = euclideanDist(iPoint, jPoint); setDist(d); } else if (_router->InvisibilityGrph) { #if 0 db_printf("%d, %d, %d\n", cone1, cone2, blocker); db_printf("\t(%d, %d)--(%d, %d)\n", (int) iInfo.point.x, (int) iInfo.point.y, (int) jInfo.point.x, (int) jInfo.point.y); #endif // if i and j can't see each other, add blank edge db_printf("\tSetting invisibility edge... \n\t\t"); db_print(); addBlocker(blocker); } } int EdgeInf::firstBlocker(void) { ShapeSet ss = ShapeSet(); Point& pti = _v1->point; Point& ptj = _v2->point; VertID& iID = _v1->id; VertID& jID = _v2->id; ContainsMap &contains = _router->contains; if (!(iID.isShape)) { ss.insert(contains[iID].begin(), contains[iID].end()); } if (!(jID.isShape)) { ss.insert(contains[jID].begin(), contains[jID].end()); } VertInf *last = _router->vertices.end(); unsigned int lastId = 0; bool seenIntersectionAtEndpoint = false; for (VertInf *k = _router->vertices.shapesBegin(); k != last; ) { VertID kID = k->id; if (k->id == dummyOrthogID) { // Don't include orthogonal dummy vertices. k = k->lstNext; continue; } if (kID.objID != lastId) { if ((ss.find(kID.objID) != ss.end())) { unsigned int shapeID = kID.objID; db_printf("Endpoint is inside shape %u so ignore shape " "edges.\n", kID.objID); // One of the endpoints is inside this shape so ignore it. while ((k != last) && (k->id.objID == shapeID)) { // And skip the other vertices from this shape. k = k->lstNext; } continue; } seenIntersectionAtEndpoint = false; lastId = kID.objID; } Point& kPoint = k->point; Point& kPrevPoint = k->shPrev->point; if (segmentShapeIntersect(pti, ptj, kPrevPoint, kPoint, seenIntersectionAtEndpoint)) { ss.clear(); return kID.objID; } k = k->lstNext; } ss.clear(); return 0; } bool EdgeInf::isBetween(VertInf *i, VertInf *j) { if ( ((i == _v1) && (j == _v2)) || ((i == _v2) && (j == _v1)) ) { return true; } return false; } // Returns true if this edge is a vertical or horizontal line segment. bool EdgeInf::isOrthogonal(void) const { return ((_v1->point.x == _v2->point.x) || (_v1->point.y == _v2->point.y)); } VertInf *EdgeInf::otherVert(VertInf *vert) { COLA_ASSERT((vert == _v1) || (vert == _v2)); if (vert == _v1) { return _v2; } return _v1; } EdgeInf *EdgeInf::checkEdgeVisibility(VertInf *i, VertInf *j, bool knownNew) { // This is for polyline routing, so check we're not // considering orthogonal vertices. COLA_ASSERT(i->id != dummyOrthogID); COLA_ASSERT(j->id != dummyOrthogID); Router *router = i->_router; EdgeInf *edge = NULL; if (knownNew) { COLA_ASSERT(existingEdge(i, j) == NULL); edge = new EdgeInf(i, j); } else { edge = existingEdge(i, j); if (edge == NULL) { edge = new EdgeInf(i, j); } } edge->checkVis(); if (!(edge->_added) && !(router->InvisibilityGrph)) { delete edge; edge = NULL; } return edge; } // XXX: This function is ineffecient, and shouldn't even really be // required. EdgeInf *EdgeInf::existingEdge(VertInf *i, VertInf *j) { VertInf *selected = NULL; // Look through poly-line visibility edges. selected = (i->visListSize <= j->visListSize) ? i : j; EdgeInfList& visList = selected->visList; EdgeInfList::const_iterator finish = visList.end(); for (EdgeInfList::const_iterator edge = visList.begin(); edge != finish; ++edge) { if ((*edge)->isBetween(i, j)) { return (*edge); } } // Look through orthogonal visbility edges. selected = (i->orthogVisListSize <= j->orthogVisListSize) ? i : j; EdgeInfList& orthogVisList = selected->orthogVisList; finish = orthogVisList.end(); for (EdgeInfList::const_iterator edge = orthogVisList.begin(); edge != finish; ++edge) { if ((*edge)->isBetween(i, j)) { return (*edge); } } // Look through poly-line invisbility edges. selected = (i->invisListSize <= j->invisListSize) ? i : j; EdgeInfList& invisList = selected->invisList; finish = invisList.end(); for (EdgeInfList::const_iterator edge = invisList.begin(); edge != finish; ++edge) { if ((*edge)->isBetween(i, j)) { return (*edge); } } return NULL; } //=========================================================================== EdgeList::EdgeList(bool orthogonal) : _orthogonal(orthogonal), _firstEdge(NULL), _lastEdge(NULL), _count(0) { } EdgeList::~EdgeList() { clear(); } void EdgeList::clear(void) { while (_firstEdge) { delete _firstEdge; } COLA_ASSERT(_count == 0); _lastEdge = NULL; } int EdgeList::size(void) const { return _count; } void EdgeList::addEdge(EdgeInf *edge) { COLA_ASSERT(!_orthogonal || edge->isOrthogonal()); if (_firstEdge == NULL) { COLA_ASSERT(_lastEdge == NULL); _lastEdge = edge; _firstEdge = edge; edge->lstPrev = NULL; edge->lstNext = NULL; } else { COLA_ASSERT(_lastEdge != NULL); _lastEdge->lstNext = edge; edge->lstPrev = _lastEdge; _lastEdge = edge; edge->lstNext = NULL; } _count++; } void EdgeList::removeEdge(EdgeInf *edge) { if (edge->lstPrev) { edge->lstPrev->lstNext = edge->lstNext; } if (edge->lstNext) { edge->lstNext->lstPrev = edge->lstPrev; } if (edge == _lastEdge) { _lastEdge = edge->lstPrev; if (edge == _firstEdge) { _firstEdge = NULL; } } else if (edge == _firstEdge) { _firstEdge = edge->lstNext; } edge->lstPrev = NULL; edge->lstNext = NULL; _count--; } EdgeInf *EdgeList::begin(void) { return _firstEdge; } EdgeInf *EdgeList::end(void) { return NULL; } }