diff options
Diffstat (limited to '')
-rw-r--r-- | src/3rdparty/adaptagrams/libavoid/visibility.cpp | 676 |
1 files changed, 676 insertions, 0 deletions
diff --git a/src/3rdparty/adaptagrams/libavoid/visibility.cpp b/src/3rdparty/adaptagrams/libavoid/visibility.cpp new file mode 100644 index 0000000..3adadd4 --- /dev/null +++ b/src/3rdparty/adaptagrams/libavoid/visibility.cpp @@ -0,0 +1,676 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 2004-2009 Monash University + * + * -------------------------------------------------------------------- + * The Visibility Sweep technique is based upon the method described + * in Section 5.2 of: + * Lee, D.-T. (1978). Proximity and reachability in the plane., + * PhD thesis, Department of Electrical Engineering, + * University of Illinois, Urbana, IL. + * -------------------------------------------------------------------- + * + * 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 <algorithm> +#include <cfloat> + +#include "libavoid/shape.h" +#include "libavoid/debug.h" +#include "libavoid/visibility.h" +#include "libavoid/vertices.h" +#include "libavoid/graph.h" +#include "libavoid/geometry.h" +#include "libavoid/router.h" +#include "libavoid/assertions.h" + + +namespace Avoid { + + +static void vertexSweep(VertInf *vert); + +void Obstacle::computeVisibilityNaive(void) +{ + if ( !(router()->InvisibilityGrph) ) + { + // Clear shape from graph. + removeFromGraph(); + } + + VertInf *shapeBegin = firstVert(); + VertInf *shapeEnd = lastVert()->lstNext; + + VertInf *pointsBegin = router()->vertices.connsBegin(); + for (VertInf *curr = shapeBegin; curr != shapeEnd; curr = curr->lstNext) + { + bool knownNew = true; + + db_printf("-- CONSIDERING --\n"); + curr->id.db_print(); + + db_printf("\tFirst Half:\n"); + for (VertInf *j = pointsBegin ; j != curr; j = j->lstNext) + { + if (j->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + continue; + } + EdgeInf::checkEdgeVisibility(curr, j, knownNew); + } + + db_printf("\tSecond Half:\n"); + VertInf *pointsEnd = router()->vertices.end(); + for (VertInf *k = shapeEnd; k != pointsEnd; k = k->lstNext) + { + if (k->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + continue; + } + EdgeInf::checkEdgeVisibility(curr, k, knownNew); + } + } +} + + +void Obstacle::computeVisibilitySweep(void) +{ + if ( !(router()->InvisibilityGrph) ) + { + // Clear shape from graph. + removeFromGraph(); + } + + VertInf *startIter = firstVert(); + VertInf *endIter = lastVert()->lstNext; + + for (VertInf *i = startIter; i != endIter; i = i->lstNext) + { + vertexSweep(i); + } +} + + +void vertexVisibility(VertInf *point, VertInf *partner, bool knownNew, + const bool gen_contains) +{ + Router *router = point->_router; + const VertID& pID = point->id; + + // Make sure we're only doing ptVis for endpoints. + COLA_ASSERT(pID.isConnPt()); + + if ( !(router->InvisibilityGrph) ) + { + point->removeFromGraph(); + } + + if (gen_contains && pID.isConnPt()) + { + router->generateContains(point); + } + + if (router->UseLeesAlgorithm) + { + vertexSweep(point); + } + else + { + VertInf *shapesEnd = router->vertices.end(); + for (VertInf *k = router->vertices.connsBegin(); k != shapesEnd; + k = k->lstNext) + { + if (k->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + continue; + } + else if (k->id.isConnPt() && !k->id.isConnectionPin() && + !(k->id.isConnCheckpoint() && k->id.objID == pID.objID)) + { + // Include connection pins, but not connectors. + // Also include checkpoints with same ID as sweep point. + continue; + } + EdgeInf::checkEdgeVisibility(point, k, knownNew); + } + if (partner) + { + EdgeInf::checkEdgeVisibility(point, partner, knownNew); + } + } +} + + +//============================================================================ +// SWEEP CODE +// + + +class PointPair +{ + public: + PointPair(const Point& centerPoint, VertInf *inf) + : vInf(inf), + centerPoint(centerPoint) + { + angle = rotationalAngle(vInf->point - centerPoint); + distance = euclideanDist(centerPoint, vInf->point); + } + bool operator<(const PointPair& rhs) const + { + // Firstly order by angle. + if (angle == rhs.angle) + { + // If the points are collinear, then order them in increasing + // distance from the point we are sweeping around. + if (distance == rhs.distance) + { + // XXX: Add this assertion back if we require that + // connector endpoints have unique IDs. For the + // moment it is okay for them to have the same ID. + //COLA_ASSERT(vInf->id != rhs.vInf->id); + + // If comparing two points at the same physical + // position, then order them by their VertIDs. + return vInf->id < rhs.vInf->id; + } + return distance < rhs.distance; + } + return angle < rhs.angle; + } + + VertInf *vInf; + double angle; + double distance; + Point centerPoint; +}; + +typedef std::set<PointPair > VertSet; + + +class EdgePair +{ + public: + EdgePair() + : vInf1(nullptr), + vInf2(nullptr), + dist1(0.0), + dist2(0.0), + angle(0.0), + angleDist(0.0) + { + // The default constuctor should never be called. + // This is defined to appease the MSVC compiler. + COLA_ASSERT(false); + } + EdgePair(const PointPair& p1, VertInf *v) + : vInf1(p1.vInf), + vInf2(v), + dist1(p1.distance), + dist2(euclideanDist(vInf2->point, p1.centerPoint)), + angle(p1.angle), + angleDist(p1.distance), + centerPoint(p1.centerPoint) + { + } + bool operator<(const EdgePair& rhs) const + { + COLA_ASSERT(angle == rhs.angle); + if (angleDist == rhs.angleDist) + { + return (dist2 < rhs.dist2); + } + return (angleDist < rhs.angleDist); + } + bool operator==(const EdgePair& rhs) const + { + if (((vInf1->id == rhs.vInf1->id) && + (vInf2->id == rhs.vInf2->id)) || + ((vInf1->id == rhs.vInf2->id) && + (vInf2->id == rhs.vInf1->id))) + { + return true; + } + return false; + } + bool operator!=(const EdgePair& rhs) const + { + if (((vInf1->id == rhs.vInf1->id) && + (vInf2->id == rhs.vInf2->id)) || + ((vInf1->id == rhs.vInf2->id) && + (vInf2->id == rhs.vInf1->id))) + { + return false; + } + return true; + } + void setNegativeAngle(void) + { + angle = -1.0; + } + double setCurrAngle(const PointPair& p) + { + if (p.vInf->point == vInf1->point) + { + angleDist = dist1; + angle = p.angle; + } + else if (p.vInf->point == vInf2->point) + { + angleDist = dist2; + angle = p.angle; + } + else if (p.angle != angle) + { + COLA_ASSERT(p.angle > angle); + angle = p.angle; + Point pp; + int result = rayIntersectPoint(vInf1->point, vInf2->point, + centerPoint, p.vInf->point, &(pp.x), &(pp.y)); + if (result != DO_INTERSECT) + { + // This can happen with points that appear to have the + // same angle but at are at slightly different positions + angleDist = std::min(dist1, dist2); + } + else + { + angleDist = euclideanDist(pp, centerPoint); + } + } + + return angleDist; + } + + VertInf *vInf1; + VertInf *vInf2; + double dist1; + double dist2; + double angle; + double angleDist; + Point centerPoint; +}; + +typedef std::list<EdgePair> SweepEdgeList; + + +#define AHEAD 1 +#define BEHIND -1 + +class isBoundingShape +{ + public: + // Class instance remembers the ShapeSet. + isBoundingShape(ShapeSet& set) : + ss(set) + { } + // The following is an overloading of the function call operator. + bool operator () (const PointPair& pp) + { + if (!(pp.vInf->id.isConnPt()) && + (ss.find(pp.vInf->id.objID) != ss.end())) + { + return true; + } + return false; + } + private: + // MSVC wants to generate the assignment operator and the default + // constructor, but fails. Therefore we declare them private and + // don't implement them. + isBoundingShape & operator=(isBoundingShape const &); + isBoundingShape(); + + ShapeSet& ss; +}; + + +static bool sweepVisible(SweepEdgeList& T, const PointPair& point, + std::set<unsigned int>& onBorderIDs, int *blocker) +{ + if (T.empty()) + { + // No blocking edges. + return true; + } + + Router *router = point.vInf->_router; + bool visible = true; + + SweepEdgeList::const_iterator closestIt = T.begin(); + SweepEdgeList::const_iterator end = T.end(); + while (closestIt != end) + { + if ((point.vInf->point == closestIt->vInf1->point) || + (point.vInf->point == closestIt->vInf2->point)) + { + // If the ray intersects just the endpoint of a + // blocking edge then ignore that edge. + ++closestIt; + continue; + } + break; + } + if (closestIt == end) + { + return true; + } + + if (point.vInf->id.isConnPt()) + { + // It's a connector endpoint, so we have to ignore + // edges of containing shapes for determining visibility. + ShapeSet& rss = router->contains[point.vInf->id]; + while (closestIt != end) + { + if (rss.find(closestIt->vInf1->id.objID) == rss.end()) + { + // This is not a containing edge so do the normal + // test and then stop. + if (point.distance > closestIt->angleDist) + { + visible = false; + } + else if ((point.distance == closestIt->angleDist) && + onBorderIDs.find(closestIt->vInf1->id.objID) != + onBorderIDs.end()) + { + // Touching, but centerPoint is on another edge of + // shape shape, so count as blocking. + visible = false; + } + break; + } + // This was a containing edge, so consider the next along. + ++closestIt; + } + } + else + { + // Just test to see if this point is closer than the closest + // edge blocking this ray. + if (point.distance > closestIt->angleDist) + { + visible = false; + } + else if ((point.distance == closestIt->angleDist) && + onBorderIDs.find(closestIt->vInf1->id.objID) != + onBorderIDs.end()) + { + // Touching, but centerPoint is on another edge of + // shape shape, so count as blocking. + visible = false; + } + } + + if (!visible) + { + *blocker = (*closestIt).vInf1->id.objID; + } + return visible; +} + + +static void vertexSweep(VertInf *vert) +{ + Router *router = vert->_router; + VertID& pID = vert->id; + Point& pPoint = vert->point; + + VertInf *centerInf = vert; + VertID centerID = pID; + Point centerPoint = pPoint; + + // List of shape (and maybe endpt) vertices, except p + // Sort list, around + VertSet v; + + // Initialise the vertex list + ShapeSet& ss = router->contains[centerID]; + VertInf *beginVert = router->vertices.connsBegin(); + VertInf *endVert = router->vertices.end(); + for (VertInf *inf = beginVert; inf != endVert; inf = inf->lstNext) + { + if (inf == centerInf) + { + // Don't include the center point itself. + continue; + } + else if (inf->id == dummyOrthogID) + { + // Don't include orthogonal dummy vertices. + continue; + } + + if (centerID.isConnPt() && (ss.find(inf->id.objID) != ss.end()) && + !inf->id.isConnPt()) + { + // Don't include edge points of containing shapes. + unsigned int shapeID = inf->id.objID; + db_printf("Center is inside shape %u so ignore shape edges.\n", + shapeID); + continue; + } + + if (inf->id.isConnPt()) + { + // Add connector endpoint. + if (centerID.isConnPt()) + { + if (inf->id.isConnectionPin()) + { + v.insert(PointPair(centerPoint, inf)); + } + else if (centerID.isConnectionPin()) + { + // Connection pins have visibility to everything. + v.insert(PointPair(centerPoint, inf)); + } + else if (inf->id.objID == centerID.objID) + { + // Center is an endpoint, so only include the other + // endpoints or checkpoints from the matching connector. + v.insert(PointPair(centerPoint, inf)); + } + } + else + { + // Center is a shape vertex, so add all endpoint vertices. + v.insert(PointPair(centerPoint, inf)); + } + } + else + { + // Add shape vertex. + v.insert(PointPair(centerPoint, inf)); + } + } + std::set<unsigned int> onBorderIDs; + + // Add edges to T that intersect the initial ray. + SweepEdgeList e; + VertSet::const_iterator vbegin = v.begin(); + VertSet::const_iterator vend = v.end(); + const Point xaxis(DBL_MAX, centerInf->point.y); + for (VertSet::const_iterator t = vbegin; t != vend; ++t) + { + VertInf *k = t->vInf; + + COLA_ASSERT(centerInf != k); + + VertInf *kPrev = k->shPrev; + VertInf *kNext = k->shNext; + if (kPrev && (kPrev != centerInf) && + (vecDir(centerInf->point, xaxis, kPrev->point) == AHEAD)) + { + if (segmentIntersect(centerInf->point, xaxis, kPrev->point, + k->point)) + { + EdgePair intPair = EdgePair(*t, kPrev); + e.push_back(intPair); + } + if (pointOnLine(kPrev->point, k->point, centerInf->point)) + { + // Record that centerPoint is on an obstacle line. + onBorderIDs.insert(k->id.objID); + } + } + else if (kNext && (kNext != centerInf) && + (vecDir(centerInf->point, xaxis, kNext->point) == AHEAD)) + { + if (segmentIntersect(centerInf->point, xaxis, kNext->point, + k->point)) + { + EdgePair intPair = EdgePair(*t, kNext); + e.push_back(intPair); + } + if (pointOnLine(kNext->point, k->point, centerInf->point)) + { + // Record that centerPoint is on an obstacle line. + onBorderIDs.insert(k->id.objID); + } + } + } + for (SweepEdgeList::iterator c = e.begin(); c != e.end(); ++c) + { + (*c).setNegativeAngle(); + } + + + // Start the actual sweep. + db_printf("SWEEP: "); centerID.db_print(); db_printf("\n"); + + isBoundingShape isBounding(ss); + for (VertSet::const_iterator t = vbegin; t != vend; ++t) + { + VertInf *currInf = (*t).vInf; + VertID& currID = currInf->id; + Point& currPt = currInf->point; + + const double& currDist = (*t).distance; + + EdgeInf *edge = EdgeInf::existingEdge(centerInf, currInf); + if (edge == nullptr) + { + edge = new EdgeInf(centerInf, currInf); + } + + for (SweepEdgeList::iterator c = e.begin(); c != e.end(); ++c) + { + (*c).setCurrAngle(*t); + } + e.sort(); + + // Check visibility. + int blocker = 0; + bool currVisible = sweepVisible(e, *t, onBorderIDs, &blocker); + + bool cone1 = true, cone2 = true; + if (!(centerID.isConnPt())) + { + cone1 = inValidRegion(router->IgnoreRegions, + centerInf->shPrev->point, centerPoint, + centerInf->shNext->point, currInf->point); + } + if (!(currInf->id.isConnPt())) + { + cone2 = inValidRegion(router->IgnoreRegions, + currInf->shPrev->point, currInf->point, + currInf->shNext->point, centerPoint); + } + + if (!cone1 || !cone2) + { + if (router->InvisibilityGrph) + { + db_printf("\tSetting invisibility edge... \n\t\t"); + edge->addBlocker(0); + edge->db_print(); + } + } + else + { + if (currVisible) + { + db_printf("\tSetting visibility edge... \n\t\t"); + edge->setDist(currDist); + edge->db_print(); + } + else if (router->InvisibilityGrph) + { + db_printf("\tSetting invisibility edge... \n\t\t"); + edge->addBlocker(blocker); + edge->db_print(); + } + } + + if (!(edge->added()) && !(router->InvisibilityGrph)) + { + delete edge; + edge = nullptr; + } + + if (!(currID.isConnPt())) + { + // This is a shape edge + + if (currInf->shPrev != centerInf) + { + Point& prevPt = currInf->shPrev->point; + int prevDir = vecDir(centerPoint, currPt, prevPt); + EdgePair prevPair = EdgePair(*t, currInf->shPrev); + + if (prevDir == BEHIND) + { + e.remove(prevPair); + } + else if (prevDir == AHEAD) + { + e.push_front(prevPair); + } + } + + if (currInf->shNext != centerInf) + { + Point& nextPt = currInf->shNext->point; + int nextDir = vecDir(centerPoint, currPt, nextPt); + EdgePair nextPair = EdgePair(*t, currInf->shNext); + + if (nextDir == BEHIND) + { + e.remove(nextPair); + } + else if (nextDir == AHEAD) + { + e.push_front(nextPair); + } + } + } + } +} + + +} + |