diff options
| author | Arcadie M. Cracan <acracan@gmail.com> | 2009-12-02 21:26:56 +0000 |
|---|---|---|
| committer | Arcadie M. Cracan <acracan@gmail.com> | 2009-12-02 21:26:56 +0000 |
| commit | 89a5d9d34b73249c357fffae17c2989b6fdd6846 (patch) | |
| tree | 7d8be8b419e57374e518b3e63c6b675ca82571d1 /src/libavoid/orthogonal.cpp | |
| parent | Merge GSoC2009 Connectors into trunk (diff) | |
| download | inkscape-89a5d9d34b73249c357fffae17c2989b6fdd6846.tar.gz inkscape-89a5d9d34b73249c357fffae17c2989b6fdd6846.zip | |
Add forgotten libavoid files
(bzr r8856)
Diffstat (limited to 'src/libavoid/orthogonal.cpp')
| -rw-r--r-- | src/libavoid/orthogonal.cpp | 2343 |
1 files changed, 2343 insertions, 0 deletions
diff --git a/src/libavoid/orthogonal.cpp b/src/libavoid/orthogonal.cpp new file mode 100644 index 000000000..747fd1f86 --- /dev/null +++ b/src/libavoid/orthogonal.cpp @@ -0,0 +1,2343 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * Copyright (C) 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 <mjwybrow@users.sourceforge.net> +*/ + + +#include <cstdlib> +#include <cfloat> +#include <cmath> +#include <set> +#include <list> +#include <algorithm> + +#include "libavoid/router.h" +#include "libavoid/geomtypes.h" +#include "libavoid/shape.h" +#include "libavoid/orthogonal.h" +#include "libavoid/connector.h" +#include "libavoid/vpsc.h" +#include "libavoid/assertions.h" + +#ifdef LIBAVOID_SDL + #include <SDL_gfxPrimitives.h> +#endif + + +namespace Avoid { + + +static const double CHANNEL_MAX = 100000000; + +static const size_t XDIM = 0; +static const size_t YDIM = 1; + + +class ShiftSegment +{ + public: + // For shiftable segments. + ShiftSegment(ConnRef *conn, const size_t low, const size_t high, + bool isSBend, const size_t dim, double minLim, double maxLim) + : connRef(conn), + indexLow(low), + indexHigh(high), + sBend(isSBend), + fixed(false), + dimension(dim), + variable(NULL), + minSpaceLimit(minLim), + maxSpaceLimit(maxLim) + { + } + // For fixed segments. + ShiftSegment(ConnRef *conn, const size_t low, const size_t high, + const size_t dim) + : connRef(conn), + indexLow(low), + indexHigh(high), + sBend(false), + fixed(true), + dimension(dim), + variable(NULL) + { + // This has no space to shift. + minSpaceLimit = lowPoint()[dim]; + maxSpaceLimit = lowPoint()[dim]; + } + Point& lowPoint(void) + { + return connRef->displayRoute().ps[indexLow]; + } + Point& highPoint(void) + { + return connRef->displayRoute().ps[indexHigh]; + } + const Point& lowPoint(void) const + { + return connRef->displayRoute().ps[indexLow]; + } + const Point& highPoint(void) const + { + return connRef->displayRoute().ps[indexHigh]; + } + const int fixedOrder(bool& isFixed) const + { + if (fixed) + { + isFixed = true; + return 0; + } + if (lowC()) + { + return 1; + } + else if (highC()) + { + return -1; + } + return 0; + } + const int order(void) const + { + if (lowC()) + { + return -1; + } + else if (highC()) + { + return 1; + } + return 0; + } + bool operator<(const ShiftSegment& rhs) const + { + const Point& lowPt = lowPoint(); + const Point& rhsLowPt = rhs.lowPoint(); + + if (lowPt[dimension] != rhsLowPt[dimension]) + { + return lowPt[dimension] < rhsLowPt[dimension]; + } + return this < &rhs; + } + // This counts segments that are colliear and share an endpoint as + // overlapping. This allows them to be nudged apart where possible. + bool overlapsWith(const ShiftSegment& rhs, const size_t dim) const + { + size_t altDim = (dim + 1) % 2; + const Point& lowPt = lowPoint(); + const Point& highPt = highPoint(); + const Point& rhsLowPt = rhs.lowPoint(); + const Point& rhsHighPt = rhs.highPoint(); + if ( (lowPt[altDim] <= rhsHighPt[altDim]) && + (rhsLowPt[altDim] <= highPt[altDim])) + { + if ( (minSpaceLimit <= rhs.maxSpaceLimit) && + (rhs.minSpaceLimit <= maxSpaceLimit)) + { + return true; + } + } + return false; + } + + ConnRef *connRef; + size_t indexLow; + size_t indexHigh; + bool sBend; + bool fixed; + size_t dimension; + Variable *variable; + double minSpaceLimit; + double maxSpaceLimit; + private: + const bool lowC(void) const + { + // This is true if this is a cBend and its adjoining points + // are at lower positions. + if (!sBend && !fixed && (minSpaceLimit == lowPoint()[dimension])) + { + return true; + } + return false; + } + const bool highC(void) const + { + // This is true if this is a cBend and its adjoining points + // are at higher positions. + if (!sBend && !fixed && (maxSpaceLimit == lowPoint()[dimension])) + { + return true; + } + return false; + } +}; +typedef std::list<ShiftSegment> ShiftSegmentList; + +bool cmpShiftSegment(const ShiftSegment& u, const ShiftSegment& v) +{ + return u < v; +} + + +struct Node; +struct CmpNodePos { bool operator()(const Node* u, const Node* v) const; }; + +typedef std::set<Node*,CmpNodePos> NodeSet; +struct Node +{ + ShapeRef *v; + VertInf *c; + ShiftSegment *ss; + double pos; + double min[2], max[2]; + Node *firstAbove, *firstBelow; + NodeSet::iterator iter; + + Node(ShapeRef *v, const double p) + : v(v), + c(NULL), + ss(NULL), + pos(p), + firstAbove(NULL), + firstBelow(NULL) + { + //COLA_ASSERT(r->width()<1e40); + v->polygon().getBoundingRect(&min[0], &min[1], &max[0], &max[1]); + } + Node(VertInf *c, const double p) + : v(NULL), + c(c), + ss(NULL), + pos(p), + firstAbove(NULL), + firstBelow(NULL) + { + min[0] = max[0] = c->point.x; + min[1] = max[1] = c->point.y; + } + Node(ShiftSegment *ss, const double p) + : v(NULL), + c(NULL), + ss(ss), + pos(p), + firstAbove(NULL), + firstBelow(NULL) + { + // These values shouldn't ever be used, so they don't matter. + min[0] = max[0] = min[1] = max[1] = 0; + } + ~Node() + { + } + // Find the first Node above in the scanline that is a shape edge, + // and does not have an open or close event at this position (meaning + // it is just about to be removed). + double firstObstacleAbove(size_t dim) + { + Node *curr = firstAbove; + while (curr && (curr->ss || (curr->max[dim] > pos))) + { + curr = curr->firstAbove; + } + + if (curr) + { + return curr->max[dim]; + } + return -DBL_MAX; + } + // Find the first Node below in the scanline that is a shape edge, + // and does not have an open or close event at this position (meaning + // it is just about to be removed). + double firstObstacleBelow(size_t dim) + { + Node *curr = firstBelow; + while (curr && (curr->ss || (curr->min[dim] < pos))) + { + curr = curr->firstBelow; + } + + if (curr) + { + return curr->min[dim]; + } + return DBL_MAX; + } + // Mark all connector segments above in the scanline as being able + // to see to this shape edge. + void markShiftSegmentsAbove(size_t dim) + { + Node *curr = firstAbove; + while (curr && (curr->ss || (curr->pos > min[dim]))) + { + if (curr->ss && (curr->pos <= min[dim])) + { + curr->ss->maxSpaceLimit = + std::min(min[dim], curr->ss->maxSpaceLimit); + } + curr = curr->firstAbove; + } + } + // Mark all connector segments below in the scanline as being able + // to see to this shape edge. + void markShiftSegmentsBelow(size_t dim) + { + Node *curr = firstBelow; + while (curr && (curr->ss || (curr->pos < max[dim]))) + { + if (curr->ss && (curr->pos >= max[dim])) + { + curr->ss->minSpaceLimit = + std::max(max[dim], curr->ss->minSpaceLimit); + } + curr = curr->firstBelow; + } + } + bool findFirstPointAboveAndBelow(const size_t dim, double& firstAbovePos, + double& firstBelowPos, double& lastAbovePos, double& lastBelowPos) + { + bool clearVisibility = true; + firstAbovePos = -DBL_MAX; + firstBelowPos = DBL_MAX; + // We start looking left from the right side of the shape, + // and vice versa. + lastAbovePos = max[dim]; + lastBelowPos = min[dim]; + + // Find the first blocking edge above this point. Don't count the + // edges as we are travelling out of shapes we are inside, but then + // mark clearVisibility as false. + Node *curr = firstAbove; + while (curr && (curr->max[dim] > min[dim])) + { + lastAbovePos = std::min(curr->min[dim], lastAbovePos); + if ((curr->max[dim] >= min[dim]) && (curr->max[dim] <= max[dim])) + { + lastAbovePos = std::min(curr->max[dim], lastAbovePos); + } + lastBelowPos = std::max(curr->max[dim], lastBelowPos); + clearVisibility = false; + curr = curr->firstAbove; + } + if (curr) + { + firstAbovePos = curr->max[dim]; + } + while (curr) + { + // There might be a larger shape after this one in the ordering. + if (curr->max[dim] < min[dim]) + { + firstAbovePos = std::max(curr->max[dim], firstAbovePos); + } + curr = curr->firstAbove; + } + + // Find the first blocking edge below this point. Don't count the + // edges as we are travelling out of shapes we are inside, but then + // mark clearVisibility as false. + curr = firstBelow; + while (curr && (curr->min[dim] < max[dim])) + { + lastBelowPos = std::max(curr->max[dim], lastBelowPos); + if ((curr->min[dim] >= min[dim]) && (curr->min[dim] <= max[dim])) + { + lastBelowPos = std::max(curr->min[dim], lastBelowPos); + } + lastAbovePos = std::min(curr->min[dim], lastAbovePos); + clearVisibility = false; + curr = curr->firstBelow; + } + if (curr) + { + firstBelowPos = curr->min[dim]; + } + while (curr) + { + // There might be a larger shape after this one in the ordering. + if (curr->min[dim] > max[dim]) + { + firstBelowPos = std::min(curr->min[dim], firstBelowPos); + } + curr = curr->firstBelow; + } + + return clearVisibility; + } + double firstPointAbove(size_t dim) + { + Node *curr = firstAbove; + while (curr && (curr->max[dim] >= pos)) + { + curr = curr->firstAbove; + } + + if (curr) + { + return curr->max[dim]; + } + return -DBL_MAX; + } + double firstPointBelow(size_t dim) + { + Node *curr = firstBelow; + while (curr && (curr->min[dim] <= pos)) + { + curr = curr->firstBelow; + } + + if (curr) + { + return curr->min[dim]; + } + return DBL_MAX; + } + // This is a bit inefficient, but we won't need to do it once we have + // connection points. + bool isInsideShape(size_t dimension) + { + for (Node *curr = firstBelow; curr; curr = curr->firstBelow) + { + if ((curr->min[dimension] < pos) && (pos < curr->max[dimension])) + { + return true; + } + } + for (Node *curr = firstAbove; curr; curr = curr->firstAbove) + { + if ((curr->min[dimension] < pos) && (pos < curr->max[dimension])) + { + return true; + } + } + return false; + } +}; + + +bool CmpNodePos::operator() (const Node* u, const Node* v) const +{ + if (u->pos != v->pos) + { + return u->pos < v->pos; + } + + // Use the pointers to the base objects to differentiate them. + void *up = (u->v) ? (void *) u->v : + ((u->c) ? (void *) u->c : (void *) u->ss); + void *vp = (v->v) ? (void *) v->v : + ((v->c) ? (void *) v->c : (void *) v->ss); + return up < vp; +} + + +// Note: Open must come first. +typedef enum { + Open = 1, + SegOpen = 2, + ConnPoint = 3, + SegClose = 4, + Close = 5 +} EventType; + + +struct Event +{ + Event(EventType t, Node *v, double p) + : type(t), + v(v), + pos(p) + {}; + EventType type; + Node *v; + double pos; +}; + +Event **events; + + +// Used for quicksort. Must return <0, 0, or >0. +int compare_events(const void *a, const void *b) +{ + Event *ea = *(Event**) a; + Event *eb = *(Event**) b; + if (ea->pos != eb->pos) + { + return (ea->pos < eb->pos) ? -1 : 1; + } + if (ea->type != eb->type) + { + return ea->type - eb->type; + } + COLA_ASSERT(ea->v != eb->v); + return ea->v - eb->v; +} + + +// Returns a bitfield of the direction of visibility (in this dimension) +// made up of ConnDirDown (for visibility towards lower position values) +// and ConnDirUp (for visibility towards higher position values). +// +static ConnDirFlags getPosVertInfDirection(VertInf *v, size_t dim) +{ + if (dim == XDIM) // X-dimension + { + unsigned int dirs = v->visDirections & (ConnDirLeft | ConnDirRight); + if (dirs == (ConnDirLeft | ConnDirRight)) + { + return (ConnDirDown | ConnDirUp); + } + else if (dirs == ConnDirLeft) + { + return ConnDirDown; + } + else if (dirs == ConnDirRight) + { + return ConnDirUp; + } + } + else if (dim == YDIM) // Y-dimension + { + unsigned int dirs = v->visDirections & (ConnDirDown | ConnDirUp); + if (dirs == (ConnDirDown | ConnDirUp)) + { + return (ConnDirDown | ConnDirUp); + } + else if (dirs == ConnDirDown) + { + // For libavoid the Y-axis points downwards, so in terms of + // smaller or larger position values, Down is Up and vice versa. + return ConnDirUp; + } + else if (dirs == ConnDirUp) + { + // For libavoid the Y-axis points downwards, so in terms of + // smaller or larger position values, Down is Up and vice versa. + return ConnDirDown; + } + } + + // Can occur for ConnDirNone visibility. + return ConnDirNone; +} + + +struct PosVertInf +{ + PosVertInf(double p, VertInf *vI, ConnDirFlags d = ConnDirNone) + : pos(p), + vert(vI), + dir(d) + { + } + + bool operator<(const PosVertInf& rhs) const + { + if (pos != rhs.pos) + { + return pos < rhs.pos; + } + return vert < rhs.vert; + } + + double pos; + VertInf *vert; + + // A bitfield marking the direction of visibility (in this dimension) + // made up of ConnDirDown (for visibility towards lower position values) + // and ConnDirUp (for visibility towards higher position values). + // + ConnDirFlags dir; +}; + + +struct CmpVertInf +{ + bool operator()(const VertInf* u, const VertInf* v) const + { + // Comparator for VertSet, an ordered set of VertInf pointers. + // It is assumed vertical sets of points will all have the same + // x position and horizontal sets all share a y position, so this + // method can be used to sort both these sets. + COLA_ASSERT((u->point.x == v->point.x) || (u->point.y == v->point.y)); + if (u->point.x != v->point.x) + { + return u->point.x < v->point.x; + } + else if (u->point.y != v->point.y) + { + return u->point.y < v->point.y; + } + return u < v; + } +}; + + +typedef std::set<VertInf *, CmpVertInf> VertSet; + +// A set of points to break the line segment, +// along with vertices for these points. +typedef std::set<PosVertInf> BreakpointSet; + +// Temporary structure used to store the possible horizontal visibility +// lines arising from the vertical sweep. +class LineSegment +{ +public: + LineSegment(const double& b, const double& f, const double& p, + bool ss = false, VertInf *bvi = NULL, VertInf *fvi = NULL) + : begin(b), + finish(f), + pos(p), + shapeSide(false) + { + COLA_ASSERT(begin < finish); + + if (bvi) + { + vertInfs.insert(bvi); + } + if (fvi) + { + vertInfs.insert(fvi); + } + } + LineSegment(const double& bf, const double& p, VertInf *bfvi = NULL) + : begin(bf), + finish(bf), + pos(p), + shapeSide(false) + { + if (bfvi) + { + vertInfs.insert(bfvi); + } + } + + // Order by begin, pos, finish. + bool operator<(const LineSegment& rhs) const + { + if (begin != rhs.begin) + { + return begin < rhs.begin; + } + if (pos != rhs.pos) + { + return pos < rhs.pos; + } + if (finish != rhs.finish) + { + return finish < rhs.finish; + } + COLA_ASSERT(shapeSide == rhs.shapeSide); + return false; + } + + bool overlaps(const LineSegment& rhs) const + { + if ((begin == rhs.begin) && (pos == rhs.pos) && + (finish == rhs.finish)) + { + // Lines are exactly equal. + return true; + } + + if (pos == rhs.pos) + { + if (((begin >= rhs.begin) && (begin <= rhs.finish)) || + ((rhs.begin >= begin) && (rhs.begin <= finish)) ) + { + // They are colinear and overlap by some amount. + return true; + } + } + return false; + } + + void mergeVertInfs(const LineSegment& segment) + { + begin = std::min(begin, segment.begin); + finish = std::max(finish, segment.finish); + vertInfs.insert(segment.vertInfs.begin(), segment.vertInfs.end()); + } + + VertInf *beginVertInf(void) const + { + if (vertInfs.empty()) + { + return NULL; + } + return *vertInfs.begin(); + } + VertInf *finishVertInf(void) const + { + if (vertInfs.empty()) + { + return NULL; + } + return *vertInfs.rbegin(); + } + + VertInf *commitPositionX(Router *router, double posX) + { + VertInf *found = NULL; + for (VertSet::iterator v = vertInfs.begin(); + v != vertInfs.end(); ++v) + { + if ((*v)->point.x == posX) + { + found = *v; + break; + } + } + if (!found) + { + found = new VertInf(router, dummyOrthogID, Point(posX, pos)); + vertInfs.insert(found); + } + return found; + } + // Set begin endpoint vertex if none has been assigned. + void commitBegin(Router *router, VertInf *vert = NULL) + { + if (vert) + { + vertInfs.insert(vert); + } + + if (vertInfs.empty() || + ((*vertInfs.begin())->point.x != begin)) + { + vertInfs.insert(new + VertInf(router, dummyOrthogID, Point(begin, pos))); + } + } + + // Set begin endpoint vertex if none has been assigned. + void commitFinish(Router *router, VertInf *vert = NULL) + { + if (vert) + { + vertInfs.insert(vert); + } + + if (vertInfs.empty() || + ((*vertInfs.rbegin())->point.x != finish)) + { + vertInfs.insert(new + VertInf(router, dummyOrthogID, Point(finish, pos))); + } + } + + // Converts a section of the points list to a set of breakPoints. + // Returns the first of the intersection points occuring at finishPos. + VertSet::iterator addSegmentsUpTo(Router *router, double finishPos) + { + VertSet::iterator firstIntersectionPt = vertInfs.end(); + for (VertSet::iterator vert = vertInfs.begin(); + vert != vertInfs.end(); ++vert) + { + if ((*vert)->point.x > finishPos) + { + // We're done. + break; + } + + breakPoints.insert(PosVertInf((*vert)->point.x, (*vert), + getPosVertInfDirection(*vert, XDIM))); + + if ((firstIntersectionPt == vertInfs.end()) && + ((*vert)->point.x == finishPos)) + { + firstIntersectionPt = vert; + } + } + // Returns the first of the intersection points at finishPos. + return firstIntersectionPt; + } + + // Add visibility edge(s) for this segment. There may be multiple if + // one of the endpoints is shared by multiple connector endpoints. + void addEdgeHorizontal(Router *router) + { + commitBegin(router); + commitFinish(router); + + addSegmentsUpTo(router, finish); + } + + // Add visibility edge(s) for this segment up until an intersection. + // Then, move the segment beginning to the intersection point, so we + // later only consider the remainder of the segment. + // There may be multiple segments added to the graph if the beginning + // endpoint of the segment is shared by multiple connector endpoints. + VertSet addEdgeHorizontalTillIntersection(Router *router, + LineSegment& vertLine) + { + VertSet intersectionSet; + + commitBegin(router); + + // Does a vertex already exist for this point. + commitPositionX(router, vertLine.pos); + + // Generate segments and set end iterator to the first point + // at the intersection position. + VertSet::iterator restBegin = addSegmentsUpTo(router, vertLine.pos); + + // Add the intersections points to intersectionSet. + VertSet::iterator restEnd = restBegin; + while ((restEnd != vertInfs.end()) && + (*restEnd)->point.x == vertLine.pos) + { + ++restEnd; + } + intersectionSet.insert(restBegin, restEnd); + + // Adjust segment to remove processed portion. + begin = vertLine.pos; + vertInfs.erase(vertInfs.begin(), restBegin); + + return intersectionSet; + } + + // Insert vertical breakpoints. + void insertBreakpointsBegin(Router *router, LineSegment& vertLine) + { + VertInf *vert = NULL; + if (pos == vertLine.begin && vertLine.beginVertInf()) + { + vert = vertLine.beginVertInf(); + } + else if (pos == vertLine.finish && vertLine.finishVertInf()) + { + vert = vertLine.finishVertInf(); + } + commitBegin(router, vert); + + for (VertSet::iterator v = vertInfs.begin(); + v != vertInfs.end(); ++v) + { + if ((*v)->point.x == begin) + { + vertLine.breakPoints.insert(PosVertInf(pos, *v, + getPosVertInfDirection(*v, YDIM))); + } + } + } + + // Insert vertical breakpoints. + void insertBreakpointsFinish(Router *router, LineSegment& vertLine) + { + VertInf *vert = NULL; + if (pos == vertLine.begin && vertLine.beginVertInf()) + { + vert = vertLine.beginVertInf(); + } + else if (pos == vertLine.finish && vertLine.finishVertInf()) + { + vert = vertLine.finishVertInf(); + } + commitFinish(router, vert); + + for (VertSet::iterator v = vertInfs.begin(); + v != vertInfs.end(); ++v) + { + if ((*v)->point.x == finish) + { + vertLine.breakPoints.insert(PosVertInf(pos, *v, + getPosVertInfDirection(*v, YDIM))); + } + } + } + void generateVisibilityEdgesFromBreakpointSet(Router *router, size_t dim) + { + if ((breakPoints.begin())->pos != begin) + { + if (!beginVertInf()) + { + Point point(pos, pos); + point[dim] = begin; + // Add begin point if it didn't intersect another line. + VertInf *vert = new VertInf(router, dummyOrthogID, point); + breakPoints.insert(PosVertInf(begin, vert)); + } + } + if ((breakPoints.rbegin())->pos != finish) + { + if (!finishVertInf()) + { + Point point(pos, pos); + point[dim] = finish; + // Add finish point if it didn't intersect another line. + VertInf *vert = new VertInf(router, dummyOrthogID, point); + breakPoints.insert(PosVertInf(finish, vert)); + } + } + + const bool orthogonal = true; + BreakpointSet::iterator vert, last; + for (vert = last = breakPoints.begin(); vert != breakPoints.end();) + { + BreakpointSet::iterator firstPrev = last; + while (last->vert->point[dim] != vert->vert->point[dim]) + { + COLA_ASSERT(vert != last); + // Assert points are not at the same position. + COLA_ASSERT(vert->vert->point != last->vert->point); + + if ( !(vert->vert->id.isShape || last->vert->id.isShape)) + { + // Here we have a pair of two endpoints that are both + // connector endpoints and both are inside a shape. + + // Give vert visibility back to the first non-connector + // endpoint vertex (i.e., the side of the shape). + BreakpointSet::iterator side = last; + while (!side->vert->id.isShape) + { + if (side == breakPoints.begin()) + { + break; + } + --side; + } + bool canSeeDown = (vert->dir & ConnDirDown); + if (canSeeDown && side->vert->id.isShape) + { + EdgeInf *edge = new + EdgeInf(side->vert, vert->vert, orthogonal); + edge->setDist(vert->vert->point[dim] - + side->vert->point[dim]); + } + + // Give last visibility back to the first non-connector + // endpoint vertex (i.e., the side of the shape). + side = vert; + while ((side != breakPoints.end()) && + !side->vert->id.isShape) + { + ++side; + } + bool canSeeUp = (last->dir & ConnDirUp); + if (canSeeUp && (side != breakPoints.end())) + { + EdgeInf *edge = new + EdgeInf(last->vert, side->vert, orthogonal); + edge->setDist(side->vert->point[dim] - + last->vert->point[dim]); + } + } + + // The normal case. + // + // Note: It's okay to give two connector endpoints visbility + // here since we only consider the partner endpoint as a + // candidate while searching if it is the other endpoint of + // the connector in question. + // + bool generateEdge = true; + if (!last->vert->id.isShape && !(last->dir & ConnDirUp)) + { + generateEdge = false; + } + else if (!vert->vert->id.isShape && !(vert->dir & ConnDirDown)) + { + generateEdge = false; + } + if (generateEdge) + { + EdgeInf *edge = + new EdgeInf(last->vert, vert->vert, orthogonal); + edge->setDist(vert->vert->point[dim] - + last->vert->point[dim]); + } + + ++last; + } + + ++vert; + + if ((vert != breakPoints.end()) && + (last->vert->point[dim] == vert->vert->point[dim])) + { + // Still looking at same pair, just reset prev number pointer. + last = firstPrev; + } + else + { + // vert has moved to the beginning of a number number group. + // Last is now in the right place, so do nothing. + } + } + } + + double begin; + double finish; + double pos; + bool shapeSide; + + VertSet vertInfs; + BreakpointSet breakPoints; +private: + // MSVC wants to generate the assignment operator and the default + // constructor, but fails. Therefore we declare them private and + // don't implement them. + LineSegment & operator=(LineSegment const &); + LineSegment(); +}; + +typedef std::list<LineSegment> SegmentList; + +class SegmentListWrapper +{ + public: + LineSegment *insert(LineSegment segment) + { + SegmentList::iterator found = _list.end(); + for (SegmentList::iterator curr = _list.begin(); + curr != _list.end(); ++curr) + { + if (curr->overlaps(segment)) + { + if (found != _list.end()) + { + // This is not the first segment that overlaps, + // so we need to merge and then delete an existing + // segment. + curr->mergeVertInfs(*found); + _list.erase(found); + found = curr; + } + else + { + // This is the first overlapping segment, so just + // merge the new segment with this one. + curr->mergeVertInfs(segment); + found = curr; + } + } + } + + if (found == _list.end()) + { + // Add this line. + _list.push_back(segment); + return &(_list.back()); + } + + return &(*found); + } + SegmentList& list(void) + { + return _list; + } + private: + SegmentList _list; +}; + + +// Given a router instance and a set of possible horizontal segments, and a +// possible vertical visibility segment, compute and add edges to the +// orthogonal visibility graph for all the visibility edges. +static void intersectSegments(Router *router, SegmentList& segments, + LineSegment& vertLine) +{ + COLA_ASSERT(vertLine.beginVertInf() == NULL); + COLA_ASSERT(vertLine.finishVertInf() == NULL); + for (SegmentList::iterator it = segments.begin(); it != segments.end(); ) + { + LineSegment& horiLine = *it; + + bool inVertSegRegion = ((vertLine.begin <= horiLine.pos) && + (vertLine.finish >= horiLine.pos)); + + if (horiLine.finish < vertLine.pos) + { + // Add horizontal visibility segment. + horiLine.addEdgeHorizontal(router); + + size_t dim = XDIM; // x-dimension + horiLine.generateVisibilityEdgesFromBreakpointSet(router, dim); + + // We've now swept past this horizontal segment, so delete. + it = segments.erase(it); + continue; + } + else if (horiLine.begin > vertLine.pos) + { + // We've yet to reach this segment in the sweep, so ignore. + ++it; + continue; + } + else if (horiLine.begin == vertLine.pos) + { + if (inVertSegRegion) + { + horiLine.insertBreakpointsBegin(router, vertLine); + } + } + else if (horiLine.finish == vertLine.pos) + { + if (inVertSegRegion) + { + // Add horizontal visibility segment. + horiLine.addEdgeHorizontal(router); + + horiLine.insertBreakpointsFinish(router, vertLine); + + size_t dim = XDIM; // x-dimension + horiLine.generateVisibilityEdgesFromBreakpointSet(router, dim); + + // And we've now finished with the segment, so delete. + it = segments.erase(it); + continue; + } + } + else + { + COLA_ASSERT(horiLine.begin < vertLine.pos); + COLA_ASSERT(horiLine.finish > vertLine.pos); + + if (inVertSegRegion) + { + // Add horizontal visibility segment. + VertSet intersectionVerts = + horiLine.addEdgeHorizontalTillIntersection( + router, vertLine); + + for (VertSet::iterator v = intersectionVerts.begin(); + v != intersectionVerts.end(); ++v) + { + vertLine.breakPoints.insert(PosVertInf(horiLine.pos, *v, + getPosVertInfDirection(*v, YDIM))); + } + } + } + ++it; + } + + // Split breakPoints set into visibility segments. + size_t dimension = YDIM; // y-dimension + vertLine.generateVisibilityEdgesFromBreakpointSet(router, dimension); +} + + +// Processes an event for the vertical sweep used for computing the static +// orthogonal visibility graph. This adds possible visibility sgments to +// the segments list. +// The first pass is adding the event to the scanline, the second is for +// processing the event and the third for removing it from the scanline. +static void processEventVert(Router *router, NodeSet& scanline, + SegmentListWrapper& segments, Event *e, unsigned int pass) +{ + Node *v = e->v; + + if ( ((pass == 1) && (e->type == Open)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + std::pair<NodeSet::iterator, bool> result = scanline.insert(v); + v->iter = result.first; + COLA_ASSERT(result.second); + + NodeSet::iterator it = v->iter; + // Work out neighbours + if (it != scanline.begin()) + { + Node *u = *(--it); + v->firstAbove = u; + u->firstBelow = v; + } + it = v->iter; + if (++it != scanline.end()) + { + Node *u = *it; + v->firstBelow = u; + u->firstAbove = v; + } + } + + if (pass == 2) + { + if ((e->type == Open) || (e->type == Close)) + { + // Shape edge positions. + double minShape = v->min[0]; + double maxShape = v->max[0]; + // As far as we can see. + double minLimit, maxLimit; + double minLimitMax, maxLimitMin; + v->findFirstPointAboveAndBelow(0, minLimit, maxLimit, + minLimitMax, maxLimitMin); + + // Only difference between Open and Close is whether the line + // segments are at the top or bottom of the shape. Decide here. + double lineY = (e->type == Open) ? v->min[1] : v->max[1]; + + if (minLimitMax >= maxLimitMin) + { + // Insert possible visibility segments. + VertInf *vI1 = new VertInf(router, dummyOrthogID, + Point(minShape, lineY)); + VertInf *vI2 = new VertInf(router, dummyOrthogID, + Point(maxShape, lineY)); + + // There are no overlapping shapes, so give full visibility. + if (minLimit < minShape) + { + segments.insert(LineSegment(minLimit, minShape, lineY, + true, NULL, vI1)); + } + segments.insert(LineSegment(minShape, maxShape, lineY, + true, vI1, vI2)); + if (maxShape < maxLimit) + { + segments.insert(LineSegment(maxShape, maxLimit, lineY, + true, vI2, NULL)); + } + } + else + { + if ((minLimitMax > minLimit) && (minLimitMax >= minShape)) + { + segments.insert(LineSegment(minLimit, minLimitMax, lineY, + true, NULL, NULL)); + } + if ((maxLimitMin < maxLimit) && (maxLimitMin <= maxShape)) + { + segments.insert(LineSegment(maxLimitMin, maxLimit, lineY, + true, NULL, NULL)); + } + } + } + else if (e->type == ConnPoint) + { + // Connection point. + VertInf *centreVert = e->v->c; + Point& cp = centreVert->point; + + // As far as we can see. + double minLimit = v->firstPointAbove(0); + double maxLimit = v->firstPointBelow(0); + bool inShape = v->isInsideShape(0); + + LineSegment *line1 = NULL, *line2 = NULL; + if (!inShape || (centreVert->visDirections & ConnDirLeft)) + { + line1 = segments.insert(LineSegment(minLimit, cp.x, e->pos, + true, NULL, centreVert)); + } + if (!inShape || (centreVert->visDirections & ConnDirRight)) + { + line2 = segments.insert(LineSegment(cp.x, maxLimit, e->pos, + true, centreVert, NULL)); + } + if (!line1 && !line2) + { + // Add a point segment for the centre point. + segments.insert(LineSegment(cp.x, e->pos, centreVert)); + } + + if (!inShape) + { + // This is not contained within a shape so add a normal + // visibility graph point here too (since paths won't route + // *through* connector endpoint vertices). + if (line1 || line2) + { + VertInf *cent = new VertInf(router, dummyOrthogID, cp); + if (line1) + { + line1->vertInfs.insert(cent); + } + if (line2) + { + line2->vertInfs.insert(cent); + } + } + } + } + } + + if ( ((pass == 3) && (e->type == Close)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + // Clean up neighbour pointers. + Node *l = v->firstAbove, *r = v->firstBelow; + if (l != NULL) + { + l->firstBelow = v->firstBelow; + } + if (r != NULL) + { + r->firstAbove = v->firstAbove; + } + + if (e->type == ConnPoint) + { + scanline.erase(v->iter); + delete v; + } + else // if (e->type == Close) + { + size_t result; + result = scanline.erase(v); + COLA_ASSERT(result == 1); + delete v; + } + } +} + + +// Processes an event for the vertical sweep used for computing the static +// orthogonal visibility graph. This adds possible visibility sgments to +// the segments list. +// The first pass is adding the event to the scanline, the second is for +// processing the event and the third for removing it from the scanline. +static void processEventHori(Router *router, NodeSet& scanline, + SegmentListWrapper& segments, Event *e, unsigned int pass) +{ + Node *v = e->v; + + if ( ((pass == 1) && (e->type == Open)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + std::pair<NodeSet::iterator, bool> result = scanline.insert(v); + v->iter = result.first; + COLA_ASSERT(result.second); + + NodeSet::iterator it = v->iter; + // Work out neighbours + if (it != scanline.begin()) + { + Node *u = *(--it); + v->firstAbove = u; + u->firstBelow = v; + } + it = v->iter; + if (++it != scanline.end()) + { + Node *u = *it; + v->firstBelow = u; + u->firstAbove = v; + } + } + + if (pass == 2) + { + if ((e->type == Open) || (e->type == Close)) + { + // Shape edge positions. + double minShape = v->min[1]; + double maxShape = v->max[1]; + // As far as we can see. + double minLimit, maxLimit; + double minLimitMax, maxLimitMin; + v->findFirstPointAboveAndBelow(1, minLimit, maxLimit, + minLimitMax, maxLimitMin); + + // Only difference between Open and Close is whether the line + // segments are at the left or right of the shape. Decide here. + double lineX = (e->type == Open) ? v->min[0] : v->max[0]; + + if (minLimitMax >= maxLimitMin) + { + LineSegment vertSeg = LineSegment(minLimit, maxLimit, lineX); + segments.insert(vertSeg); + } + else + { + if ((minLimitMax > minLimit) && (minLimitMax >= minShape)) + { + LineSegment vertSeg = + LineSegment(minLimit, minLimitMax, lineX); + segments.insert(vertSeg); + } + if ((maxLimitMin < maxLimit) && (maxLimitMin <= maxShape)) + { + LineSegment vertSeg = + LineSegment(maxLimitMin, maxLimit, lineX); + segments.insert(vertSeg); + } + } + } + else if (e->type == ConnPoint) + { + // Connection point. + VertInf *centreVert = e->v->c; + Point& cp = centreVert->point; + + // As far as we can see. + double minLimit = v->firstPointAbove(1); + double maxLimit = v->firstPointBelow(1); + bool inShape = v->isInsideShape(1); + + if (!inShape || (centreVert->visDirections & ConnDirUp)) + { + segments.insert(LineSegment(minLimit, cp.y, e->pos)); + } + if (!inShape || (centreVert->visDirections & ConnDirDown)) + { + segments.insert(LineSegment(cp.y, maxLimit, e->pos)); + } + } + } + + if ( ((pass == 3) && (e->type == Close)) || + ((pass == 2) && (e->type == ConnPoint)) ) + { + // Clean up neighbour pointers. + Node *l = v->firstAbove, *r = v->firstBelow; + if (l != NULL) + { + l->firstBelow = v->firstBelow; + } + if (r != NULL) + { + r->firstAbove = v->firstAbove; + } + + if (e->type == ConnPoint) + { + scanline.erase(v->iter); + delete v; + } + else // if (e->type == Close) + { + size_t result; + result = scanline.erase(v); + COLA_ASSERT(result == 1); + delete v; + } + } +} + + +extern void generateStaticOrthogonalVisGraph(Router *router) +{ + const size_t n = router->shapeRefs.size(); + const unsigned cpn = router->vertices.connsSize(); + // Set up the events for the vertical sweep. + size_t totalEvents = (2 * n) + cpn; + events = new Event*[totalEvents]; + unsigned ctr = 0; + ShapeRefList::iterator shRefIt = router->shapeRefs.begin(); + for (unsigned i = 0; i < n; i++) + { + ShapeRef *shRef = *shRefIt; + double minX, minY, maxX, maxY; + shRef->polygon().getBoundingRect(&minX, &minY, &maxX, &maxY); + double midX = minX + ((maxX - minX) / 2); + Node *v = new Node(shRef, midX); + events[ctr++] = new Event(Open, v, minY); + events[ctr++] = new Event(Close, v, maxY); + + ++shRefIt; + } + for (VertInf *curr = router->vertices.connsBegin(); + curr && (curr != router->vertices.shapesBegin()); + curr = curr->lstNext) + { + Point& point = curr->point; + + Node *v = new Node(curr, point.x); + events[ctr++] = new Event(ConnPoint, v, point.y); + } + qsort((Event*)events, (size_t) totalEvents, sizeof(Event*), compare_events); + + // Process the vertical sweep. + // We do multiple passes over sections of the list so we can add relevant + // entries to the scanline that might follow, before process them. + SegmentListWrapper segments; + NodeSet scanline; + double thisPos = (totalEvents > 0) ? events[0]->pos : 0; + unsigned int posStartIndex = 0; + unsigned int posFinishIndex = 0; + for (unsigned i = 0; i <= totalEvents; ++i) + { + // If we have finished the current scanline or all events, then we + // process the events on the current scanline in a couple of passes. + if ((i == totalEvents) || (events[i]->pos != thisPos)) + { + posFinishIndex = i; + for (int pass = 2; pass <= 3; ++pass) + { + for (unsigned j = posStartIndex; j < posFinishIndex; ++j) + { + processEventVert(router, scanline, segments, + events[j], pass); + } + } + + if (i == totalEvents) + { + // We have cleaned up, so we can now break out of loop. + break; + } + + thisPos = events[i]->pos; + posStartIndex = i; + } + + // Do the first sweep event handling -- building the correct + // structure of the scanline. + const int pass = 1; + processEventVert(router, scanline, segments, events[i], pass); + } + COLA_ASSERT(scanline.size() == 0); + for (unsigned i = 0; i < totalEvents; ++i) + { + delete events[i]; + } + + segments.list().sort(); + + // Set up the events for the horizontal sweep. + SegmentListWrapper vertSegments; + ctr = 0; + shRefIt = router->shapeRefs.begin(); + for (unsigned i = 0; i < n; i++) + { + ShapeRef *shRef = *shRefIt; + double minX, minY, maxX, maxY; + shRef->polygon().getBoundingRect(&minX, &minY, &maxX, &maxY); + double midY = minY + ((maxY - minY) / 2); + Node *v = new Node(shRef, midY); + events[ctr++] = new Event(Open, v, minX); + events[ctr++] = new Event(Close, v, maxX); + + ++shRefIt; + } + for (VertInf *curr = router->vertices.connsBegin(); + curr && (curr != router->vertices.shapesBegin()); + curr = curr->lstNext) + { + Point& point = curr->point; + + Node *v = new Node(curr, point.y); + events[ctr++] = new Event(ConnPoint, v, point.x); + } + qsort((Event*)events, (size_t) totalEvents, sizeof(Event*), compare_events); + + // Process the horizontal sweep + thisPos = (totalEvents > 0) ? events[0]->pos : 0; + posStartIndex = 0; + posFinishIndex = 0; + for (unsigned i = 0; i <= totalEvents; ++i) + { + // If we have finished the current scanline or all events, then we + // process the events on the current scanline in a couple of passes. + if ((i == totalEvents) || (events[i]->pos != thisPos)) + { + posFinishIndex = i; + for (int pass = 2; pass <= 3; ++pass) + { + for (unsigned j = posStartIndex; j < posFinishIndex; ++j) + { + processEventHori(router, scanline, vertSegments, + events[j], pass); + } + } + + // Process the merged line segments. + vertSegments.list().sort(); + for (SegmentList::iterator curr = vertSegments.list().begin(); + curr != vertSegments.list().end(); ++curr) + { + intersectSegments(router, segments.list(), *curr); + } + vertSegments.list().clear(); + + if (i == totalEvents) + { + // We have cleaned up, so we can now break out of loop. + break; + } + + thisPos = events[i]->pos; + posStartIndex = i; + } + + // Do the first sweep event handling -- building the correct + // structure of the scanline. + const int pass = 1; + processEventHori(router, scanline, vertSegments, events[i], pass); + } + COLA_ASSERT(scanline.size() == 0); + for (unsigned i = 0; i < totalEvents; ++i) + { + delete events[i]; + } + delete [] events; + + // Add portions of the horizontal line that are after the final vertical + // position we considered. + for (SegmentList::iterator it = segments.list().begin(); + it != segments.list().end(); ) + { + LineSegment& horiLine = *it; + + horiLine.addEdgeHorizontal(router); + + size_t dim = XDIM; // x-dimension + horiLine.generateVisibilityEdgesFromBreakpointSet(router, dim); + + it = segments.list().erase(it); + } +} + + +//============================================================================ +// Path Adjustment code +//============================================================================ + + + + +// Processes sweep events used to determine each horizontal and vertical +// line segment in a connector's channel of visibility. +// Four calls to this function are made at each position by the scanline: +// 1) Handle all Close event processing. +// 2) Remove Close event objects from the scanline. +// 3) Add Open event objects to the scanline. +// 4) Handle all Open event processing. +// +static void processShiftEvent(Router *router, NodeSet& scanline, + ShiftSegmentList& segments, Event *e, size_t dim, + unsigned int pass) +{ + Node *v = e->v; + + if ( ((pass == 3) && (e->type == Open)) || + ((pass == 3) && (e->type == SegOpen)) ) + { + std::pair<NodeSet::iterator, bool> result = scanline.insert(v); + v->iter = result.first; + COLA_ASSERT(result.second); + + NodeSet::iterator it = v->iter; + // Work out neighbours + if (it != scanline.begin()) + { + Node *u = *(--it); + v->firstAbove = u; + u->firstBelow = v; + } + it = v->iter; + if (++it != scanline.end()) + { + Node *u = *it; + v->firstBelow = u; + u->firstAbove = v; + } + } + + if ( ((pass == 4) && (e->type == Open)) || + ((pass == 4) && (e->type == SegOpen)) || + ((pass == 1) && (e->type == SegClose)) || + ((pass == 1) && (e->type == Close)) ) + { + if (v->ss) + { + // As far as we can see. + double minLimit = v->firstObstacleAbove(dim); + double maxLimit = v->firstObstacleBelow(dim); + + v->ss->minSpaceLimit = + std::max(minLimit, v->ss->minSpaceLimit); + v->ss->maxSpaceLimit = + std::min(maxLimit, v->ss->maxSpaceLimit); + } + else + { + v->markShiftSegmentsAbove(dim); + v->markShiftSegmentsBelow(dim); + } + } + + if ( ((pass == 2) && (e->type == SegClose)) || + ((pass == 2) && (e->type == Close)) ) + { + // Clean up neighbour pointers. + Node *l = v->firstAbove, *r = v->firstBelow; + if (l != NULL) + { + l->firstBelow = v->firstBelow; + } + if (r != NULL) + { + r->firstAbove = v->firstAbove; + } + + size_t result; + result = scanline.erase(v); + COLA_ASSERT(result == 1); + delete v; + } +} + + +static void buildOrthogonalChannelInfo(Router *router, + const size_t dim, ShiftSegmentList& segmentList) +{ + if (router->routingPenalty(segmentPenalty) == 0) + { + // This code assumes the routes are pretty optimal, so we don't + // do this adjustment if the routes have no segment penalty. + return; + } + + size_t altDim = (dim + 1) % 2; + // For each connector. + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + Polygon& displayRoute = (*curr)->displayRoute(); + // Determine all line segments that we are interested in shifting. + // We don't consider the first or last segment of a path. + for (size_t i = 1; i < displayRoute.size(); ++i) + { + if (displayRoute.ps[i - 1][dim] == displayRoute.ps[i][dim]) + { + // It's a segment in the dimension we are processing, + size_t indexLow = i - 1; + size_t indexHigh = i; + if (displayRoute.ps[i - 1][altDim] > displayRoute.ps[i][altDim]) + { + indexLow = i; + indexHigh = i - 1; + } + COLA_ASSERT(displayRoute.at(indexLow)[altDim] < + displayRoute.at(indexHigh)[altDim]); + + if ((i == 1) || ((i + 1) == displayRoute.size())) + { + // The first and last segment of a connector can't be + // shifted. We call them fixed segments. Note: this + // will change if we later allow connection channels. + segmentList.push_back( + ShiftSegment(*curr, indexLow, indexHigh, dim)); + continue; + } + + // The segment probably has space to be shifted. + double minLim = -CHANNEL_MAX; + double maxLim = CHANNEL_MAX; + bool isSBend = false; + + double prevPos = displayRoute.ps[i - 2][dim]; + double nextPos = displayRoute.ps[i + 1][dim]; + if (((prevPos < displayRoute.ps[i][dim]) && + (nextPos > displayRoute.ps[i][dim])) + || + ((prevPos > displayRoute.ps[i][dim]) && + (nextPos < displayRoute.ps[i][dim])) ) + { + isSBend = true; + + // Determine limits if the s-bend is not due to an + // obstacle. In this case we need to limit the channel + // to the span of the adjoining segments to this one. + if ((prevPos < displayRoute.ps[i][dim]) && + (nextPos > displayRoute.ps[i][dim])) + { + minLim = std::max(minLim, prevPos); + maxLim = std::min(maxLim, nextPos); + } + else + { + minLim = std::max(minLim, nextPos); + maxLim = std::min(maxLim, prevPos); + } + } + else + { + // isCBend: Both adjoining segments are in the same + // direction. We indicate this for later by setting + // the maxLim or minLim to the segment position. + if (prevPos < displayRoute.ps[i][dim]) + { + minLim = displayRoute.ps[i][dim]; + } + else + { + maxLim = displayRoute.ps[i][dim]; + } + } + + segmentList.push_back(ShiftSegment(*curr, indexLow, + indexHigh, isSBend, dim, minLim, maxLim)); + } + } + } + if (segmentList.empty()) + { + // There are no segments, so we can just return now. + return; + } + + // Do a sweep and shift these segments. + const size_t n = router->shapeRefs.size(); + const size_t cpn = segmentList.size(); + // Set up the events for the sweep. + size_t totalEvents = 2 * (n + cpn); + events = new Event*[totalEvents]; + unsigned ctr = 0; + ShapeRefList::iterator shRefIt = router->shapeRefs.begin(); + for (unsigned i = 0; i < n; i++) + { + ShapeRef *shRef = *shRefIt; + Point min, max; + shRef->polygon().getBoundingRect(&min.x, &min.y, &max.x, &max.y); + double mid = min[dim] + ((max[dim] - min[dim]) / 2); + Node *v = new Node(shRef, mid); + events[ctr++] = new Event(Open, v, min[altDim]); + events[ctr++] = new Event(Close, v, max[altDim]); + + ++shRefIt; + } + for (ShiftSegmentList::iterator curr = segmentList.begin(); + curr != segmentList.end(); ++curr) + { + const Point& lowPt = curr->lowPoint(); + const Point& highPt = curr->highPoint(); + + COLA_ASSERT(lowPt[dim] == highPt[dim]); + COLA_ASSERT(lowPt[altDim] < highPt[altDim]); + Node *v = new Node(&(*curr), lowPt[dim]); + events[ctr++] = new Event(SegOpen, v, lowPt[altDim]); + events[ctr++] = new Event(SegClose, v, highPt[altDim]); + } + qsort((Event*)events, (size_t) totalEvents, sizeof(Event*), compare_events); + + // Process the sweep. + // We do multiple passes over sections of the list so we can add relevant + // entries to the scanline that might follow, before process them. + NodeSet scanline; + double thisPos = (totalEvents > 0) ? events[0]->pos : 0; + unsigned int posStartIndex = 0; + unsigned int posFinishIndex = 0; + for (unsigned i = 0; i <= totalEvents; ++i) + { + // If we have finished the current scanline or all events, then we + // process the events on the current scanline in a couple of passes. + if ((i == totalEvents) || (events[i]->pos != thisPos)) + { + posFinishIndex = i; + for (int pass = 2; pass <= 4; ++pass) + { + for (unsigned j = posStartIndex; j < posFinishIndex; ++j) + { + processShiftEvent(router, scanline, segmentList, events[j], + dim, pass); + } + } + + if (i == totalEvents) + { + // We have cleaned up, so we can now break out of loop. + break; + } + + thisPos = events[i]->pos; + posStartIndex = i; + } + + // Do the first sweep event handling -- building the correct + // structure of the scanline. + const int pass = 1; + processShiftEvent(router, scanline, segmentList, events[i], + dim, pass); + } + COLA_ASSERT(scanline.size() == 0); + for (unsigned i = 0; i < totalEvents; ++i) + { + delete events[i]; + } + delete [] events; +} + + +static void simplifyOrthogonalRoutes(Router *router) +{ + // Simplify routes. + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + (*curr)->set_route((*curr)->displayRoute().simplify()); + } +} + + +static void buildOrthogonalNudgingOrderInfo(Router *router, + PtOrderMap& pointOrders) +{ + // Simplify routes. + simplifyOrthogonalRoutes(router); + + int crossingsN = 0; + + // Do segment splitting. + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn = *curr; + + for (ConnRefList::const_iterator curr2 = router->connRefs.begin(); + curr2 != router->connRefs.end(); ++curr2) + { + if ((*curr2)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn2 = *curr2; + + if (conn == conn2) + { + continue; + } + + Avoid::Polygon& route = conn->displayRoute(); + Avoid::Polygon& route2 = conn2->displayRoute(); + splitBranchingSegments(route2, true, route); + } + } + + for (ConnRefList::const_iterator curr = router->connRefs.begin(); + curr != router->connRefs.end(); ++curr) + { + if ((*curr)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn = *curr; + + for (ConnRefList::const_iterator curr2 = curr; + curr2 != router->connRefs.end(); ++curr2) + { + if ((*curr2)->routingType() != ConnType_Orthogonal) + { + continue; + } + ConnRef *conn2 = *curr2; + + if (conn == conn2) + { + continue; + } + + Avoid::Polygon& route = conn->displayRoute(); + Avoid::Polygon& route2 = conn2->displayRoute(); + bool checkForBranchingSegments = false; + int crossings = 0; + for (size_t i = 1; i < route.size(); ++i) + { + const bool finalSegment = ((i + 1) == route.size()); + crossings += countRealCrossings(route2, true, route, i, + checkForBranchingSegments, finalSegment, NULL, + &pointOrders, conn2, conn).first; + } + if (crossings > 0) + { + crossingsN += crossings; + } + } + } + + // Sort the point orders. + PtOrderMap::iterator finish = pointOrders.end(); + for (PtOrderMap::iterator it = pointOrders.begin(); it != finish; ++it) + { + //const VertID& ptID = it->first; + PtOrder& order = it->second; + + for (size_t dim = XDIM; dim <= YDIM; ++dim) + { + order.sort(dim); + } + } +} + + +class CmpLineOrder +{ + public: + CmpLineOrder(PtOrderMap& ord, const size_t dim) + : orders(ord), + dimension(dim) + { + } + bool operator()(const ShiftSegment& lhs, const ShiftSegment& rhs, + bool *comparable = NULL) const + { + if (comparable) + { + *comparable = true; + } + Point lhsLow = lhs.lowPoint(); + Point rhsLow = rhs.lowPoint(); +#ifndef NDEBUG + const Point& lhsHigh = lhs.highPoint(); + const Point& rhsHigh = rhs.highPoint(); +#endif + size_t altDim = (dimension + 1) % 2; + + COLA_ASSERT(lhsLow[dimension] == lhsHigh[dimension]); + COLA_ASSERT(rhsLow[dimension] == rhsHigh[dimension]); + + if (lhsLow[dimension] != rhsLow[dimension]) + { + return lhsLow[dimension] < rhsLow[dimension]; + } + + // If one of these is fixed, then determine order based on + // fixed segment, that is, order so the fixed segment doesn't + // block movement. + bool oneIsFixed = false; + const int lhsFixedOrder = lhs.fixedOrder(oneIsFixed); + const int rhsFixedOrder = rhs.fixedOrder(oneIsFixed); + if (oneIsFixed && (lhsFixedOrder != rhsFixedOrder)) + { + return lhsFixedOrder < rhsFixedOrder; + } + + // C-bends that did not have a clear order with s-bends might + // not have a good ordering here, so compare their order in + // terms of C-bend direction and S-bends and use that if it + // differs for the two segments. + const int lhsOrder = lhs.order(); + const int rhsOrder = rhs.order(); + if (lhsOrder != rhsOrder) + { + return lhsOrder < rhsOrder; + } + + // Need to index using the original point into the map, so find it. + Point& unchanged = (lhsLow[altDim] > rhsLow[altDim]) ? + lhsLow : rhsLow; + + PtOrder& lowOrder = orders[unchanged]; + int lhsPos = lowOrder.positionFor(lhs.connRef, dimension); + int rhsPos = lowOrder.positionFor(rhs.connRef, dimension); + if ((lhsPos == -1) || (rhsPos == -1)) + { + // A value for rhsPos or lhsPos mean the points are not directly + // comparable, meaning they are at the same position but cannot + // overlap (they are just collinear. The relative order for + // these segments is not important since we do not constrain + // them against each other. + //COLA_ASSERT(lhs.overlapsWith(rhs, dimension) == false); + // We do need to be consistent though. + if (comparable) + { + *comparable = false; + } + return lhsLow[altDim] < rhsLow[altDim]; + } + + return lhsPos < rhsPos; + } + + PtOrderMap& orders; + const size_t dimension; +}; + + +// We can use the normaal sort algorithm for lists since it is not possible +// to comapre all elements, but there will be an ordering defined between +// most of the elements. Hence we order these, using insertion sort, and +// the case of them not being able to be compared is handled by not setting +// up any constraints between such segments when doing the nudging. +// +static ShiftSegmentList linesort(ShiftSegmentList origList, + CmpLineOrder& comparison) +{ + ShiftSegmentList resultList; + + while (!origList.empty()) + { + // Get and remove the first element from the origList. + ShiftSegment segment = origList.front(); + origList.pop_front(); + + // Find the insertion point in the resultList. + ShiftSegmentList::iterator curr; + for (curr = resultList.begin(); curr != resultList.end(); ++curr) + { + bool comparable = false; + bool lessThan = comparison(segment, *curr, &comparable); + + if (comparable && lessThan) + { + // If it is comparable and lessThan, then we have found the + // insertion point. + break; + } + } + + // Insert the element into the reultList at the required point. + resultList.insert(curr, segment); + } + + return resultList; +} + + +typedef std::list<ShiftSegment *> ShiftSegmentPtrList; + + +static void nudgeOrthogonalRoutes(Router *router, size_t dimension, + PtOrderMap& pointOrders, ShiftSegmentList& segmentList) +{ + // Do the actual nudging. + ShiftSegmentList currentRegion; + while (!segmentList.empty()) + { + // Take a reference segment + ShiftSegment& currentSegment = segmentList.front(); + // Then, find the segments that overlap this one. + currentRegion.clear(); + currentRegion.push_back(currentSegment); + segmentList.erase(segmentList.begin()); + for (ShiftSegmentList::iterator curr = segmentList.begin(); + curr != segmentList.end(); ) + { + bool overlaps = false; + for (ShiftSegmentList::iterator curr2 = currentRegion.begin(); + curr2 != currentRegion.end(); ++curr2) + { + if (curr->overlapsWith(*curr2, dimension)) + { + overlaps = true; + break; + } + } + if (overlaps) + { + currentRegion.push_back(*curr); + segmentList.erase(curr); + // Consider segments from the beginning, since we mave have + // since passed segments that overlap with the new set. + curr = segmentList.begin(); + } + else + { + ++curr; + } + } + CmpLineOrder lineSortComp(pointOrders, dimension); + currentRegion = linesort(currentRegion, lineSortComp); + + if (currentRegion.size() == 1) + { + // Save creating the solver instance if there is just one + // immovable segment. + if (!currentRegion.front().sBend) + { + continue; + } + } + + // Process these segments. + Variables vs; + Constraints cs; + ShiftSegmentPtrList prevVars; + // IDs: + const int freeID = 0; + const int fixedID = 1; + // Weights: + double freeWeight = 0.00001; + double strongWeight = 0.001; + double fixedWeight = 100000; + //printf("-------------------------------------------------------\n"); + //printf("Nudge -- size: %d\n", (int) currentRegion.size()); + for (ShiftSegmentList::iterator currSegment = currentRegion.begin(); + currSegment != currentRegion.end(); ++currSegment) + { + Point& lowPt = currSegment->lowPoint(); + + // Create a solver variable for the position of this segment. + int varID = freeID; + double idealPos = lowPt[dimension]; + double weight = freeWeight; + if (currSegment->sBend) + { + COLA_ASSERT(currSegment->minSpaceLimit > -CHANNEL_MAX); + COLA_ASSERT(currSegment->maxSpaceLimit < CHANNEL_MAX); + + // For s-bends, take the middle as ideal. + idealPos = currSegment->minSpaceLimit + + ((currSegment->maxSpaceLimit - + currSegment->minSpaceLimit) / 2); + } + else if (currSegment->fixed) + { + // Fixed segments shouldn't get moved. + weight = fixedWeight; + varID = fixedID; + } + else + { + // Set a higher weight for c-bends to stop them sometimes + // getting pushed out into channels by more-free connectors + // to the "inner" side of them. + weight = strongWeight; + } + currSegment->variable = new Variable(varID, idealPos, weight); + vs.push_back(currSegment->variable); + size_t index = vs.size() - 1; + //printf("line %.15f pos: %g min: %g max: %g\n", + // lowPt[dimension], idealPos, currSegment->minSpaceLimit, + // currSegment->maxSpaceLimit); + + // Constrain position in relation to previously seen segments, + // if necessary (i.e. when they could overlap). + for (ShiftSegmentPtrList::iterator prevVarIt = prevVars.begin(); + prevVarIt != prevVars.end(); ) + { + ShiftSegment *prevSeg = *prevVarIt; + Variable *prevVar = prevSeg->variable; + + if (currSegment->overlapsWith(*prevSeg, dimension) && + (!(currSegment->fixed) || !(prevSeg->fixed))) + { + // If there is a previous segment to the left that + // could overlap this in the shift direction, then + // constrain the two segments to be separated. + // Though don't add the constraint if both the + // segments are fixed in place. + cs.push_back(new Constraint(prevVar, vs[index], + router->orthogonalNudgeDistance())); + prevVarIt = prevVars.erase(prevVarIt); + } + else + { + ++prevVarIt; + } + } + + if (!currSegment->fixed) + { + // If this segment sees a channel boundary to its left, + // then constrain its placement as such. + if (currSegment->minSpaceLimit > -CHANNEL_MAX) + { + vs.push_back(new Variable(fixedID, + currSegment->minSpaceLimit, fixedWeight)); + cs.push_back(new Constraint(vs[vs.size() - 1], vs[index], + 0.0)); + } + + // If this segment sees a channel boundary to its right, + // then constrain its placement as such. + if (currSegment->maxSpaceLimit < CHANNEL_MAX) + { + vs.push_back(new Variable(fixedID, + currSegment->maxSpaceLimit, fixedWeight)); + cs.push_back(new Constraint(vs[index], vs[vs.size() - 1], + 0.0)); + } + } + prevVars.push_back(&(*currSegment)); + } +#if 0 + for(unsigned i=0;i<vs.size();i++) { + printf("-vs[%d]=%f\n",i,vs[i]->desiredPosition); + } +#endif + IncSolver f(vs,cs); + f.solve(); + bool satisfied = true; + for (size_t i = 0; i < vs.size(); ++i) + { + if (vs[i]->id == fixedID) + { + if (fabs(vs[i]->finalPosition - vs[i]->desiredPosition) > 0.01) + { + satisfied = false; + break; + } + } + } + if (satisfied) + { + for (ShiftSegmentList::iterator currSegment = currentRegion.begin(); + currSegment != currentRegion.end(); ++currSegment) + { + Point& lowPt = currSegment->lowPoint(); + Point& highPt = currSegment->highPoint(); + double newPos = currSegment->variable->finalPosition; + //printf("Pos: %X, %g\n", (int) currSegment->connRef, newPos); + lowPt[dimension] = newPos; + highPt[dimension] = newPos; + } + } +#if 0 + for(unsigned i=0;i<vs.size();i++) { + printf("+vs[%d]=%f\n",i,vs[i]->finalPosition); + } +#endif + for_each(vs.begin(),vs.end(),delete_object()); + for_each(cs.begin(),cs.end(),delete_object()); + } +} + + +extern void improveOrthogonalRoutes(Router *router) +{ + router->timers.Register(tmOrthogNudge, timerStart); + for (size_t dimension = 0; dimension < 2; ++dimension) + { + // Build nudging info. + // XXX: We need to build the point orders separately in each + // dimension since things move. There is probably a more + // efficient way to do this. + PtOrderMap pointOrders; + buildOrthogonalNudgingOrderInfo(router, pointOrders); + + // Simplify routes. + simplifyOrthogonalRoutes(router); + + // Do the centring and nudging. + ShiftSegmentList segLists; + buildOrthogonalChannelInfo(router, dimension, segLists); + nudgeOrthogonalRoutes(router, dimension, pointOrders, segLists); + } + router->timers.Stop(); +} + + +} |
