summaryrefslogtreecommitdiffstats
path: root/src/libavoid/router.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libavoid/router.cpp')
-rw-r--r--src/libavoid/router.cpp2225
1 files changed, 1750 insertions, 475 deletions
diff --git a/src/libavoid/router.cpp b/src/libavoid/router.cpp
index 55098b9d8..84f919f3d 100644
--- a/src/libavoid/router.cpp
+++ b/src/libavoid/router.cpp
@@ -3,7 +3,7 @@
*
* libavoid - Fast, Incremental, Object-avoiding Line Router
*
- * Copyright (C) 2004-2009 Monash University
+ * Copyright (C) 2004-2014 Monash University
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -12,102 +12,39 @@
* 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
+ * 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.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
- * Author(s): Michael Wybrow <mjwybrow@users.sourceforge.net>
+ * Author(s): Michael Wybrow
*/
#include <algorithm>
#include <cmath>
+#include <cfloat>
#include "libavoid/shape.h"
#include "libavoid/router.h"
#include "libavoid/visibility.h"
#include "libavoid/connector.h"
+#include "libavoid/junction.h"
+#include "libavoid/viscluster.h"
+#include "libavoid/connend.h"
#include "libavoid/debug.h"
#include "libavoid/orthogonal.h"
#include "libavoid/assertions.h"
+#include "libavoid/connectionpin.h"
-namespace Avoid {
-
-
-enum ActionType {
- ShapeMove,
- ShapeAdd,
- ShapeRemove,
- ConnChange
-};
-
-typedef std::list<std::pair<unsigned int, ConnEnd> > ConnUpdateList;
-class ActionInfo {
- public:
- ActionInfo(ActionType t, ShapeRef *s, const Polygon& p, bool fM)
- : type(t),
- objPtr(s),
- newPoly(p),
- firstMove(fM)
- {
- COLA_ASSERT(type == ShapeMove);
- }
- ActionInfo(ActionType t, ShapeRef *s)
- : type(t),
- objPtr(s),
- newPoly(),
- firstMove(0)
- {
- COLA_ASSERT(type != ConnChange);
- }
- ActionInfo(ActionType t, ConnRef *c)
- : type(t),
- objPtr(c),
- newPoly(),
- firstMove(0)
- {
- COLA_ASSERT(type == ConnChange);
- }
- ~ActionInfo()
- {
- }
- ShapeRef *shape(void) const
- {
- COLA_ASSERT((type == ShapeMove) || (type == ShapeAdd) ||
- (type == ShapeRemove));
- return (static_cast<ShapeRef *> (objPtr));
- }
- ConnRef *conn(void) const
- {
- COLA_ASSERT(type == ConnChange);
- return (static_cast<ConnRef *> (objPtr));
- }
- bool operator==(const ActionInfo& rhs) const
- {
- return (type == rhs.type) && (objPtr == rhs.objPtr);
- }
- bool operator<(const ActionInfo& rhs) const
- {
- if (type != rhs.type)
- {
- return type < rhs.type;
- }
- return objPtr < rhs.objPtr;
- }
- ActionType type;
- void *objPtr;
- Polygon newPoly;
- bool firstMove;
- ConnUpdateList conns;
-};
+namespace Avoid {
Router::Router(const unsigned int flags)
- : visOrthogGraph(true),
+ : visOrthogGraph(),
PartialTime(false),
SimpleRouting(false),
ClusteredRouting(true),
@@ -121,40 +58,56 @@ Router::Router(const unsigned int flags)
RubberBandRouting(false),
// Instrumentation:
st_checked_edges(0),
-#ifdef LIBAVOID_SDL
- avoid_screen(NULL),
-#endif
- _largestAssignedId(0),
- _consolidateActions(true),
- _orthogonalNudgeDistance(4.0),
+ m_largest_assigned_id(0),
+ m_consolidate_actions(true),
+ m_currently_calling_destructors(false),
+ m_topology_addon(new TopologyAddonInterface()),
// Mode options:
- _polyLineRouting(false),
- _orthogonalRouting(false),
- _staticGraphInvalidated(true),
- _inCrossingPenaltyReroutingStage(false)
+ m_allows_polyline_routing(false),
+ m_allows_orthogonal_routing(false),
+ m_static_orthogonal_graph_invalidated(true),
+ m_in_crossing_rerouting_stage(false),
+ m_settings_changes(false),
+ m_debug_handler(NULL)
{
// At least one of the Routing modes must be set.
COLA_ASSERT(flags & (PolyLineRouting | OrthogonalRouting));
if (flags & PolyLineRouting)
{
- _polyLineRouting = true;
+ m_allows_polyline_routing = true;
}
if (flags & OrthogonalRouting)
{
- _orthogonalRouting = true;
+ m_allows_orthogonal_routing = true;
}
- for (size_t p = 0; p < lastPenaltyMarker; ++p)
+ for (size_t p = 0; p < lastRoutingParameterMarker; ++p)
{
- _routingPenalties[p] = 0.0;
+ m_routing_parameters[p] = 0.0;
}
- _routingPenalties[clusterCrossingPenalty] = 4000;
+ m_routing_parameters[segmentPenalty] = 10;
+ m_routing_parameters[clusterCrossingPenalty] = 4000;
+ m_routing_parameters[idealNudgingDistance] = 4.0;
+
+ m_routing_options[nudgeOrthogonalSegmentsConnectedToShapes] = false;
+ m_routing_options[improveHyperedgeRoutesMovingJunctions] = true;
+ m_routing_options[penaliseOrthogonalSharedPathsAtConnEnds] = false;
+ m_routing_options[nudgeOrthogonalTouchingColinearSegments] = false;
+ m_routing_options[performUnifyingNudgingPreprocessingStep] = true;
+ m_routing_options[improveHyperedgeRoutesMovingAddingAndDeletingJunctions] =
+ false;
+ m_routing_options[nudgeSharedPathsWithCommonEndPoint] = true;
+
+ m_hyperedge_improver.setRouter(this);
+ m_hyperedge_rerouter.setRouter(this);
}
Router::~Router()
{
+ m_currently_calling_destructors = true;
+
// Delete remaining connectors.
ConnRefList::iterator conn = connRefs.begin();
while (conn != connRefs.end())
@@ -164,49 +117,82 @@ Router::~Router()
conn = connRefs.begin();
}
- // Remove remaining shapes.
- ShapeRefList::iterator shape = shapeRefs.begin();
- while (shape != shapeRefs.end())
+ // Remove remaining obstacles (shapes and junctions).
+ ObstacleList::iterator obstacle = m_obstacles.begin();
+ while (obstacle != m_obstacles.end())
{
- ShapeRef *shapePtr = *shape;
- db_printf("Deleting shape %u in ~Router()\n", shapePtr->id());
- if (shapePtr->isActive())
+ Obstacle *obstaclePtr = *obstacle;
+ ShapeRef *shape = dynamic_cast<ShapeRef *> (obstaclePtr);
+ db_printf("Deleting %s %u in ~Router()\n",
+ (shape) ? "shape" : "junction", obstaclePtr->id());
+ if (obstaclePtr->isActive())
{
- shapePtr->removeFromGraph();
- shapePtr->makeInactive();
+ obstaclePtr->removeFromGraph();
+ obstaclePtr->makeInactive();
}
- delete shapePtr;
- shape = shapeRefs.begin();
+ delete obstaclePtr;
+ obstacle = m_obstacles.begin();
}
+ m_currently_calling_destructors = false;
// Cleanup orphaned orthogonal graph vertices.
destroyOrthogonalVisGraph();
+ COLA_ASSERT(m_obstacles.size() == 0);
COLA_ASSERT(connRefs.size() == 0);
- COLA_ASSERT(shapeRefs.size() == 0);
COLA_ASSERT(visGraph.size() == 0);
- COLA_ASSERT(invisGraph.size() == 0);
+
+ delete m_topology_addon;
+}
+
+void Router::setDebugHandler(DebugHandler *handler)
+{
+ m_debug_handler = handler;
}
+DebugHandler *Router::debugHandler(void) const
+{
+ return m_debug_handler;
+}
+
+ShapeRef *Router::shapeContainingPoint(const Point& point)
+{
+ // Count points on the border as being inside.
+ bool countBorder = true;
+
+ // Compute enclosing shapes.
+ ObstacleList::const_iterator finish = m_obstacles.end();
+ for (ObstacleList::const_iterator i = m_obstacles.begin(); i != finish; ++i)
+ {
+ ShapeRef *shape = dynamic_cast<ShapeRef *>(*i);
+ if (shape && inPoly(shape->routingPolygon(), point, countBorder))
+ {
+ return shape;
+ }
+ }
+ return NULL;
+}
void Router::modifyConnector(ConnRef *conn, const unsigned int type,
- const ConnEnd& connEnd)
+ const ConnEnd& connEnd, bool connPinMoveUpdate)
{
ActionInfo modInfo(ConnChange, conn);
-
- ActionInfoList::iterator found =
+
+ ActionInfoList::iterator found =
find(actionList.begin(), actionList.end(), modInfo);
if (found == actionList.end())
{
+ // Matching action not found, so add.
modInfo.conns.push_back(std::make_pair(type, connEnd));
actionList.push_back(modInfo);
}
else
{
- found->conns.push_back(std::make_pair(type, connEnd));
+ // Update the found action as necessary.
+ found->addConnEndUpdate(type, connEnd, connPinMoveUpdate);
}
- if (!_consolidateActions)
+ if (!m_consolidate_actions)
{
processTransaction();
}
@@ -216,30 +202,52 @@ void Router::modifyConnector(ConnRef *conn, const unsigned int type,
void Router::modifyConnector(ConnRef *conn)
{
ActionInfo modInfo(ConnChange, conn);
-
- ActionInfoList::iterator found =
+
+ ActionInfoList::iterator found =
find(actionList.begin(), actionList.end(), modInfo);
if (found == actionList.end())
{
actionList.push_back(modInfo);
}
- if (!_consolidateActions)
+ if (!m_consolidate_actions)
{
processTransaction();
}
}
-void Router::removeQueuedConnectorActions(ConnRef *conn)
+void Router::modifyConnectionPin(ShapeConnectionPin *pin)
{
- ActionInfo modInfo(ConnChange, conn);
-
- ActionInfoList::iterator found =
+ ActionInfo modInfo(ConnectionPinChange, pin);
+
+ ActionInfoList::iterator found =
find(actionList.begin(), actionList.end(), modInfo);
- if (found != actionList.end())
+ if (found == actionList.end())
{
- actionList.erase(found);
+ actionList.push_back(modInfo);
+ }
+
+ if (!m_consolidate_actions)
+ {
+ processTransaction();
+ }
+}
+
+
+void Router::removeObjectFromQueuedActions(const void *object)
+{
+ for (ActionInfoList::iterator curr = actionList.begin();
+ curr != actionList.end(); )
+ {
+ if (curr->objPtr == object)
+ {
+ curr = actionList.erase(curr);
+ }
+ else
+ {
+ ++curr;
+ }
}
}
@@ -249,37 +257,37 @@ void Router::addShape(ShapeRef *shape)
// There shouldn't be remove events or move events for the same shape
// already in the action list.
// XXX: Possibly we could handle this by ordering them intelligently.
- COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
ActionInfo(ShapeRemove, shape)) == actionList.end());
- COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
ActionInfo(ShapeMove, shape)) == actionList.end());
ActionInfo addInfo(ShapeAdd, shape);
-
- ActionInfoList::iterator found =
+
+ ActionInfoList::iterator found =
find(actionList.begin(), actionList.end(), addInfo);
if (found == actionList.end())
{
actionList.push_back(addInfo);
}
- if (!_consolidateActions)
+ if (!m_consolidate_actions)
{
processTransaction();
}
}
-void Router::removeShape(ShapeRef *shape)
+void Router::deleteShape(ShapeRef *shape)
{
- // There shouldn't be add events events for the same shape already
+ // There shouldn't be add events events for the same shape already
// in the action list.
// XXX: Possibly we could handle this by ordering them intelligently.
- COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
ActionInfo(ShapeAdd, shape)) == actionList.end());
// Delete any ShapeMove entries for this shape in the action list.
- ActionInfoList::iterator found = find(actionList.begin(),
+ ActionInfoList::iterator found = find(actionList.begin(),
actionList.end(), ActionInfo(ShapeMove, shape));
if (found != actionList.end())
{
@@ -294,61 +302,98 @@ void Router::removeShape(ShapeRef *shape)
actionList.push_back(remInfo);
}
- if (!_consolidateActions)
+ if (!m_consolidate_actions)
{
processTransaction();
}
}
+void Router::deleteConnector(ConnRef *connector)
+{
+ m_currently_calling_destructors = true;
+ delete connector;
+ m_currently_calling_destructors = false;
+}
+
void Router::moveShape(ShapeRef *shape, const double xDiff, const double yDiff)
{
- Polygon newPoly = shape->polygon();
+ ActionInfo moveInfo(ShapeMove, shape, Polygon(), false);
+ ActionInfoList::iterator found =
+ find(actionList.begin(), actionList.end(), moveInfo);
+
+ Polygon newPoly;
+ if (found != actionList.end())
+ {
+ // The shape already has a queued move, so use that shape position.
+ newPoly = found->newPoly;
+ }
+ else
+ {
+ // Just use the existing position.
+ newPoly = shape->polygon();
+ }
newPoly.translate(xDiff, yDiff);
moveShape(shape, newPoly);
}
-void Router::moveShape(ShapeRef *shape, const Polygon& newPoly,
+void Router::markAllObstaclesAsMoved(void)
+{
+ for (ObstacleList::iterator obstacleIt = m_obstacles.begin();
+ obstacleIt != m_obstacles.end(); ++obstacleIt)
+ {
+ ShapeRef *shape = dynamic_cast<ShapeRef *> (*obstacleIt);
+ JunctionRef *junction = dynamic_cast<JunctionRef *> (*obstacleIt);
+ if (shape)
+ {
+ moveShape(shape, 0, 0);
+ }
+ else if (junction)
+ {
+ moveJunction(junction, 0, 0);
+ }
+ }
+}
+
+void Router::moveShape(ShapeRef *shape, const Polygon& newPoly,
const bool first_move)
{
// There shouldn't be remove events or add events for the same shape
// already in the action list.
// XXX: Possibly we could handle this by ordering them intelligently.
- COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
ActionInfo(ShapeRemove, shape)) == actionList.end());
-
- if (find(actionList.begin(), actionList.end(),
- ActionInfo(ShapeAdd, shape)) != actionList.end())
+
+ ActionInfoList::iterator found = find(actionList.begin(),
+ actionList.end(), ActionInfo(ShapeAdd, shape));
+ if (found != actionList.end())
{
// The Add is enough, no need for the Move action too.
+ // The shape will be added with it's existing polygon,
+ // so set this to be the newPoly passed for the move.
+ found->shape()->setNewPoly(newPoly);
return;
}
ActionInfo moveInfo(ShapeMove, shape, newPoly, first_move);
// Sanely cope with the case where the user requests moving the same
// shape multiple times before rerouting connectors.
- ActionInfoList::iterator found =
- find(actionList.begin(), actionList.end(), moveInfo);
+ found = find(actionList.begin(), actionList.end(), moveInfo);
if (found != actionList.end())
{
- if (!SimpleRouting)
- {
- db_printf("warning: multiple moves requested for shape %d "
- "within a single transaction.\n", (int) shape->id());
- }
// Just update the ActionInfo with the second polygon, but
// leave the firstMove setting alone.
found->newPoly = newPoly;
}
- else
+ else
{
actionList.push_back(moveInfo);
}
- if (!_consolidateActions)
+ if (!m_consolidate_actions)
{
processTransaction();
}
@@ -357,7 +402,7 @@ void Router::moveShape(ShapeRef *shape, const Polygon& newPoly,
void Router::setStaticGraphInvalidated(const bool invalidated)
{
- _staticGraphInvalidated = invalidated;
+ m_static_orthogonal_graph_invalidated = invalidated;
}
@@ -384,113 +429,128 @@ void Router::destroyOrthogonalVisGraph(void)
void Router::regenerateStaticBuiltGraph(void)
{
- // Here we do talks involved in updating the static-built visibility
+ // Here we do talks involved in updating the static-built visibility
// graph (if necessary) before we do any routing.
- if (_staticGraphInvalidated)
+ if (m_static_orthogonal_graph_invalidated)
{
- if (_orthogonalRouting)
+ if (m_allows_orthogonal_routing)
{
destroyOrthogonalVisGraph();
- timers.Register(tmOrthogGraph, timerStart);
+ TIMER_START(this, tmOrthogGraph);
// Regenerate a new visibility graph.
generateStaticOrthogonalVisGraph(this);
-
- timers.Stop();
+
+ TIMER_STOP(this);
}
- _staticGraphInvalidated = false;
+ m_static_orthogonal_graph_invalidated = false;
}
}
-bool Router::shapeInQueuedActionList(ShapeRef *shape) const
-{
- bool foundAdd = find(actionList.begin(), actionList.end(),
- ActionInfo(ShapeAdd, shape)) != actionList.end();
- bool foundRem = find(actionList.begin(), actionList.end(),
- ActionInfo(ShapeRemove, shape)) != actionList.end();
- bool foundMove = find(actionList.begin(), actionList.end(),
- ActionInfo(ShapeMove, shape)) != actionList.end();
-
- return (foundAdd || foundRem || foundMove);
-}
-
-
bool Router::transactionUse(void) const
{
- return _consolidateActions;
+ return m_consolidate_actions;
}
void Router::setTransactionUse(const bool transactions)
{
- _consolidateActions = transactions;
+ m_consolidate_actions = transactions;
}
-bool Router::processTransaction(void)
+// Processes the action list.
+void Router::processActions(void)
{
bool notPartialTime = !(PartialFeedback && PartialTime);
bool seenShapeMovesOrDeletes = false;
- // If SimpleRouting, then don't update here.
- if (actionList.empty() || SimpleRouting)
- {
- return false;
- }
+ m_transaction_start_time = clock();
+ m_abort_transaction = false;
+ std::list<unsigned int> deletedObstacles;
actionList.sort();
ActionInfoList::iterator curr;
ActionInfoList::iterator finish = actionList.end();
for (curr = actionList.begin(); curr != finish; ++curr)
{
ActionInfo& actInf = *curr;
- if (!((actInf.type == ShapeRemove) || (actInf.type == ShapeMove)))
+ if (!((actInf.type == ShapeRemove) || (actInf.type == ShapeMove) ||
+ (actInf.type == JunctionRemove) || (actInf.type == JunctionMove)))
{
// Not a move or remove action, so don't do anything.
continue;
}
seenShapeMovesOrDeletes = true;
+ Obstacle *obstacle = actInf.obstacle();
ShapeRef *shape = actInf.shape();
- bool isMove = (actInf.type == ShapeMove);
+ JunctionRef *junction = actInf.junction();
+ bool isMove = (actInf.type == ShapeMove) ||
+ (actInf.type == JunctionMove);;
bool first_move = actInf.firstMove;
- unsigned int pid = shape->id();
+ unsigned int pid = obstacle->id();
// o Remove entries related to this shape's vertices
- shape->removeFromGraph();
+ obstacle->removeFromGraph();
if (SelectiveReroute && (!isMove || notPartialTime || first_move))
{
- markConnectors(shape);
+ markPolylineConnectorsNeedingReroutingForDeletedObstacle(obstacle);
}
adjustContainsWithDel(pid);
+ if (isMove)
+ {
+ if (shape)
+ {
+ shape->moveAttachedConns(actInf.newPoly);
+ }
+ else if (junction)
+ {
+ junction->moveAttachedConns(actInf.newPosition);
+ }
+ }
+
// Ignore this shape for visibility.
// XXX: We don't really need to do this if we're not using Partial
// Feedback. Without this the blocked edges still route
// around the shape until it leaves the connector.
- shape->makeInactive();
+ obstacle->makeInactive();
+
+ if (!isMove)
+ {
+ // Free deleted obstacle.
+ m_currently_calling_destructors = true;
+ deletedObstacles.push_back(obstacle->id());
+ delete obstacle;
+ m_currently_calling_destructors = false;
+ }
}
- if (seenShapeMovesOrDeletes && _polyLineRouting)
+ if (seenShapeMovesOrDeletes && m_allows_polyline_routing)
{
if (InvisibilityGrph)
{
+ // Check edges for obstacles that were moved or removed.
for (curr = actionList.begin(); curr != finish; ++curr)
{
ActionInfo& actInf = *curr;
- if (!((actInf.type == ShapeRemove) ||
- (actInf.type == ShapeMove)))
+ if ((actInf.type == ShapeMove) || (actInf.type == JunctionMove))
{
- // Not a move or remove action, so don't do anything.
- continue;
+ // o Check all edges that were blocked by moved obstacle.
+ checkAllBlockedEdges(actInf.obstacle()->id());
}
+ }
- // o Check all edges that were blocked by this shape.
- checkAllBlockedEdges(actInf.shape()->id());
+ for (std::list<unsigned int>::iterator it = deletedObstacles.begin();
+ it != deletedObstacles.end(); ++it)
+ {
+ // o Check all edges that were blocked by deleted obstacle.
+ checkAllBlockedEdges(*it);
}
}
else
@@ -503,30 +563,41 @@ bool Router::processTransaction(void)
for (curr = actionList.begin(); curr != finish; ++curr)
{
ActionInfo& actInf = *curr;
- if (!((actInf.type == ShapeAdd) || (actInf.type == ShapeMove)))
+ if (!((actInf.type == ShapeAdd) || (actInf.type == ShapeMove) ||
+ (actInf.type == JunctionAdd) || (actInf.type == JunctionMove)))
{
// Not a move or add action, so don't do anything.
continue;
}
+ Obstacle *obstacle = actInf.obstacle();
ShapeRef *shape = actInf.shape();
+ JunctionRef *junction = actInf.junction();
Polygon& newPoly = actInf.newPoly;
- bool isMove = (actInf.type == ShapeMove);
+ bool isMove = (actInf.type == ShapeMove) ||
+ (actInf.type == JunctionMove);
- unsigned int pid = shape->id();
+ unsigned int pid = obstacle->id();
// Restore this shape for visibility.
- shape->makeActive();
+ obstacle->makeActive();
if (isMove)
{
- shape->setNewPoly(newPoly);
+ if (shape)
+ {
+ shape->setNewPoly(newPoly);
+ }
+ else
+ {
+ junction->setPosition(actInf.newPosition);
+ }
}
- const Polygon& shapePoly = shape->polygon();
+ const Polygon& shapePoly = obstacle->routingPolygon();
adjustContainsWithAdd(shapePoly, pid);
- if (_polyLineRouting)
+ if (m_allows_polyline_routing)
{
// o Check all visibility edges to see if this one shape
// blocks them.
@@ -538,12 +609,13 @@ bool Router::processTransaction(void)
// o Calculate visibility for the new vertices.
if (UseLeesAlgorithm)
{
- shapeVisSweep(shape);
+ obstacle->computeVisibilitySweep();
}
else
{
- shapeVis(shape);
+ obstacle->computeVisibilityNaive();
}
+ obstacle->updatePinPolyLineVisibility();
}
}
@@ -563,45 +635,172 @@ bool Router::processTransaction(void)
}
// Clear the actionList.
actionList.clear();
+}
- _staticGraphInvalidated = true;
+bool Router::processTransaction(void)
+{
+ // If SimpleRouting, then don't update here.
+ if ((actionList.empty() && (m_hyperedge_rerouter.count() == 0) &&
+ (m_settings_changes == false)) || SimpleRouting)
+ {
+ return false;
+ }
+ m_settings_changes = false;
+
+ processActions();
+
+ m_static_orthogonal_graph_invalidated = true;
rerouteAndCallbackConnectors();
return true;
}
-void Router::addCluster(ClusterRef *cluster)
+void Router::addJunction(JunctionRef *junction)
{
- cluster->makeActive();
+ // There shouldn't be remove events or move events for the same junction
+ // already in the action list.
+ // XXX: Possibly we could handle this by ordering them intelligently.
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ ActionInfo(JunctionRemove, junction)) == actionList.end());
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ ActionInfo(JunctionMove, junction)) == actionList.end());
+
+ ActionInfo addInfo(JunctionAdd, junction);
+
+ ActionInfoList::iterator found =
+ find(actionList.begin(), actionList.end(), addInfo);
+ if (found == actionList.end())
+ {
+ actionList.push_back(addInfo);
+ }
- unsigned int pid = cluster->id();
- ReferencingPolygon& poly = cluster->polygon();
+ if (!m_consolidate_actions)
+ {
+ processTransaction();
+ }
+}
- adjustClustersWithAdd(poly, pid);
+
+void Router::deleteJunction(JunctionRef *junction)
+{
+ // There shouldn't be add events events for the same junction already
+ // in the action list.
+ // XXX: Possibly we could handle this by ordering them intelligently.
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ ActionInfo(JunctionAdd, junction)) == actionList.end());
+
+ // Delete any ShapeMove entries for this shape in the action list.
+ ActionInfoList::iterator found = find(actionList.begin(),
+ actionList.end(), ActionInfo(JunctionMove, junction));
+ if (found != actionList.end())
+ {
+ actionList.erase(found);
+ }
+
+ // Add the ShapeRemove entry.
+ ActionInfo remInfo(JunctionRemove, junction);
+ found = find(actionList.begin(), actionList.end(), remInfo);
+ if (found == actionList.end())
+ {
+ actionList.push_back(remInfo);
+ }
+
+ if (!m_consolidate_actions)
+ {
+ processTransaction();
+ }
}
-void Router::delCluster(ClusterRef *cluster)
+void Router::moveJunction(JunctionRef *junction, const double xDiff,
+ const double yDiff)
{
- cluster->makeInactive();
+ ActionInfo moveInfo(JunctionMove, junction, Point());
+ ActionInfoList::iterator found =
+ find(actionList.begin(), actionList.end(), moveInfo);
+
+ Point newPosition;
+ if (found != actionList.end())
+ {
+ // The junction already has a queued move, so use that position.
+ newPosition = found->newPosition;
+ }
+ else
+ {
+ // Just use the existing position.
+ newPosition = junction->position();
+ }
+ newPosition.x += xDiff;
+ newPosition.y += yDiff;
+ moveJunction(junction, newPosition);
+}
+
+
+void Router::moveJunction(JunctionRef *junction, const Point& newPosition)
+{
+ // There shouldn't be remove events or add events for the same junction
+ // already in the action list.
+ // XXX: Possibly we could handle this by ordering them intelligently.
+ COLA_ASSERT(find(actionList.begin(), actionList.end(),
+ ActionInfo(JunctionRemove, junction)) == actionList.end());
+
+ ActionInfoList::iterator found = find(actionList.begin(),
+ actionList.end(), ActionInfo(JunctionAdd, junction));
+ if (found != actionList.end())
+ {
+ // The Add is enough, no need for the Move action too.
+ // The junction will be added with the new position.
+ found->junction()->setPosition(newPosition);
+ return;
+ }
+
+ ActionInfo moveInfo(JunctionMove, junction, newPosition);
+ // Sanely cope with the case where the user requests moving the same
+ // shape multiple times before rerouting connectors.
+ found = find(actionList.begin(), actionList.end(), moveInfo);
+
+ if (found != actionList.end())
+ {
+ // Just update the ActionInfo with the second position.
+ found->newPosition = newPosition;
+ }
+ else
+ {
+ actionList.push_back(moveInfo);
+ }
+
+ if (!m_consolidate_actions)
+ {
+ processTransaction();
+ }
+}
+
+void Router::addCluster(ClusterRef *cluster)
+{
+ cluster->makeActive();
+
unsigned int pid = cluster->id();
+ ReferencingPolygon& poly = cluster->polygon();
- adjustClustersWithDel(pid);
+ adjustClustersWithAdd(poly, pid);
}
-void Router::setOrthogonalNudgeDistance(const double dist)
+void Router::deleteCluster(ClusterRef *cluster)
{
- COLA_ASSERT(dist >= 0);
- _orthogonalNudgeDistance = dist;
+ cluster->makeInactive();
+
+ unsigned int pid = cluster->id();
+
+ adjustClustersWithDel(pid);
}
-double Router::orthogonalNudgeDistance(void) const
+unsigned int Router::newObjectId(void) const
{
- return _orthogonalNudgeDistance;
+ return m_largest_assigned_id + 1;
}
@@ -609,89 +808,79 @@ unsigned int Router::assignId(const unsigned int suggestedId)
{
// If the suggestedId is zero, then we assign the object the next
// smallest unassigned ID, otherwise we trust the ID given is unique.
- unsigned int assignedId = (suggestedId == 0) ?
- (_largestAssignedId + 1) : suggestedId;
-
- // Have the router record if this ID is larger than the _largestAssignedId.
- _largestAssignedId = std::max(_largestAssignedId, assignedId);
-
+ unsigned int assignedId = (suggestedId == 0) ? newObjectId() : suggestedId;
+
// If assertions are enabled, then we check that this ID really is unique.
- COLA_ASSERT(idIsUnique(assignedId));
+ COLA_ASSERT(objectIdIsUnused(assignedId));
+
+ // Have the router record if this ID is larger than the largest assigned ID.
+ m_largest_assigned_id = std::max(m_largest_assigned_id, assignedId);
return assignedId;
}
// Returns whether the given ID is unique among all objects known by the
- // router. Outputs a warning if the ID is found ore than once.
- // It is expected this is only going to be called from assertions while
- // debugging, so efficiency is not an issue and we just iterate over all
- // objects.
-bool Router::idIsUnique(const unsigned int id) const
+ // router. It is expected this is only going to be called from assertions
+ // while debugging, so efficiency is not an issue and we just iterate over
+ // all objects.
+bool Router::objectIdIsUnused(const unsigned int id) const
{
- unsigned int count = 0;
-
- // Examine shapes.
- for (ShapeRefList::const_iterator i = shapeRefs.begin();
- i != shapeRefs.end(); ++i)
+ // Examine shapes/junctions.
+ for (ObstacleList::const_iterator i = m_obstacles.begin();
+ i != m_obstacles.end(); ++i)
{
if ((*i)->id() == id)
{
- count++;
+ return false;
}
}
// Examine connectors.
- for (ConnRefList::const_iterator i = connRefs.begin();
- i != connRefs.end(); ++i)
+ for (ConnRefList::const_iterator i = connRefs.begin();
+ i != connRefs.end(); ++i)
{
if ((*i)->id() == id)
{
- count++;
+ return false;
}
}
// Examine clusters.
- for (ClusterRefList::const_iterator i = clusterRefs.begin();
- i != clusterRefs.end(); ++i)
+ for (ClusterRefList::const_iterator i = clusterRefs.begin();
+ i != clusterRefs.end(); ++i)
{
if ((*i)->id() == id)
{
- count++;
+ return false;
}
}
- if (count > 1)
- {
- db_printf("Warning:\tlibavoid object ID %d not unique.\n", id);
- return false;
- }
return true;
}
//----------------------------------------------------------------------------
-// XXX: attachedShapes and attachedConns both need to be rewritten
-// for constant time lookup of attached objects once this info
-// is stored better within libavoid. Also they shouldn't need to
-// be friends of ConnRef.
-
// Returns a list of connector Ids of all the connectors of type
// 'type' attached to the shape with the ID 'shapeId'.
void Router::attachedConns(IntList &conns, const unsigned int shapeId,
const unsigned int type)
{
ConnRefList::const_iterator fin = connRefs.end();
- for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
+ for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
{
- if ((type & runningTo) && ((*i)->_dstId == shapeId))
+ std::pair<Obstacle *, Obstacle *> anchors = (*i)->endpointAnchors();
+
+ if ((type & runningTo) &&
+ (anchors.second && (anchors.second->id() == shapeId)))
{
- conns.push_back((*i)->_id);
+ conns.push_back((*i)->id());
}
- else if ((type & runningFrom) && ((*i)->_srcId == shapeId))
+ else if ((type & runningFrom) &&
+ (anchors.first && (anchors.first->id() == shapeId)))
{
- conns.push_back((*i)->_id);
+ conns.push_back((*i)->id());
}
}
}
@@ -703,144 +892,658 @@ void Router::attachedShapes(IntList &shapes, const unsigned int shapeId,
const unsigned int type)
{
ConnRefList::const_iterator fin = connRefs.end();
- for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
+ for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
{
- if ((type & runningTo) && ((*i)->_dstId == shapeId))
+ std::pair<Obstacle *, Obstacle *> anchors = (*i)->endpointAnchors();
+
+ if ((type & runningTo) &&
+ (anchors.second && (anchors.second->id() == shapeId)))
{
- if ((*i)->_srcId != 0)
+ if (anchors.first)
{
// Only if there is a shape attached to the other end.
- shapes.push_back((*i)->_srcId);
+ shapes.push_back(anchors.first->id());
}
}
- else if ((type & runningFrom) && ((*i)->_srcId == shapeId))
+ else if ((type & runningFrom) &&
+ (anchors.first && (anchors.first->id() == shapeId)))
{
- if ((*i)->_dstId != 0)
+ if (anchors.second)
{
// Only if there is a shape attached to the other end.
- shapes.push_back((*i)->_dstId);
+ shapes.push_back(anchors.second->id());
}
}
}
}
- // It's intended this function is called after visibility changes
- // resulting from shape movement have happened. It will alert
+ // It's intended this function is called after visibility changes
+ // resulting from shape movement have happened. It will alert
// rerouted connectors (via a callback) that they need to be redrawn.
void Router::rerouteAndCallbackConnectors(void)
{
- std::set<ConnRef *> reroutedConns;
+ ConnRefList reroutedConns;
ConnRefList::const_iterator fin = connRefs.end();
+
+ this->m_conn_reroute_flags.alertConns();
- // Updating the orthogonal visibility graph if necessary.
+ // Updating the orthogonal visibility graph if necessary.
regenerateStaticBuiltGraph();
- timers.Register(tmOrthogRoute, timerStart);
- for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
+ for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
+ {
+ (*i)->freeActivePins();
+ }
+
+ // Calculate and return connectors that are part of hyperedges and will
+ // be completely rerouted by that code and thus don't need to have routes
+ // generated here.
+ ConnRefSet hyperedgeConns =
+ m_hyperedge_rerouter.calcHyperedgeConnectors();
+
+ // TODO: It might be worth sorting connectors and routing them from
+ // smallest to largest estimated cost. This way we likely get
+ // better exclusive pin assignment during initial routing.
+
+ size_t totalConns = connRefs.size();
+ size_t numOfReroutedConns = 0;
+ for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
{
- (*i)->_needs_repaint = false;
- bool rerouted = (*i)->generatePath();
+ // Progress reporting and continuation check.
+ performContinuationCheck(TransactionPhaseRouteSearch,
+ numOfReroutedConns, totalConns);
+ ++numOfReroutedConns;
+
+ ConnRef *connector = *i;
+ if (hyperedgeConns.find(connector) != hyperedgeConns.end())
+ {
+ // This will be rerouted by the hyperedge code, so do nothing.
+ continue;
+ }
+
+ if (connector->hasFixedRoute())
+ {
+ // We don't reroute connectors with fixed routes.
+ continue;
+ }
+
+ TIMER_START(this, tmOrthogRoute);
+ connector->m_needs_repaint = false;
+ bool rerouted = connector->generatePath();
if (rerouted)
{
- reroutedConns.insert(*i);
+ reroutedConns.push_back(connector);
}
+ TIMER_STOP(this);
}
- timers.Stop();
+
+
+ // Perform any complete hyperedge rerouting that has been requested.
+ m_hyperedge_rerouter.performRerouting();
// Find and reroute crossing connectors if crossing penalties are set.
improveCrossings();
- // Perform centring and nudging for othogonal routes.
+ bool withMinorImprovements = routingOption(
+ improveHyperedgeRoutesMovingJunctions);
+ bool withMajorImprovements = routingOption(
+ improveHyperedgeRoutesMovingAddingAndDeletingJunctions);
+ if (withMinorImprovements || withMajorImprovements)
+ {
+ m_hyperedge_improver.clear();
+ m_hyperedge_improver.execute(withMajorImprovements);
+ }
+
+ // Perform centring and nudging for orthogonal routes.
improveOrthogonalRoutes(this);
+ // Find a list of all the deleted connectors in hyperedges.
+ HyperedgeNewAndDeletedObjectLists changedHyperedgeObjs =
+ m_hyperedge_improver.newAndDeletedObjectLists();
+ ConnRefList deletedConns = changedHyperedgeObjs.deletedConnectorList;
+ for (size_t index = 0; index < m_hyperedge_rerouter.count(); ++index)
+ {
+ changedHyperedgeObjs =
+ m_hyperedge_rerouter.newAndDeletedObjectLists(index);
+ deletedConns.merge(changedHyperedgeObjs.deletedConnectorList);
+ }
+
// Alert connectors that they need redrawing.
- for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
+ fin = reroutedConns.end();
+ for (ConnRefList::const_iterator i = reroutedConns.begin(); i != fin; ++i)
{
- (*i)->_needs_repaint = true;
- (*i)->performCallback();
+ ConnRef *conn = *i;
+
+ // Skip hyperedge connectors which have been deleted.
+ ConnRefList::iterator findIt = std::find(
+ deletedConns.begin(), deletedConns.end(), conn);
+ if (findIt != deletedConns.end())
+ {
+ // Connector deleted, skip.
+ continue;
+ }
+
+ conn->m_needs_repaint = true;
+ conn->performCallback();
}
+
+ // Progress reporting.
+ performContinuationCheck(TransactionPhaseCompleted, 1, 1);
}
+// Type holding a cost estimate and ConnRef.
+typedef std::pair<double, ConnRef *> ConnCostRef;
+
+// A comparison class used to order a set of ConnCostRefs.
+class CmpConnCostRef
+{
+ public:
+ CmpConnCostRef()
+ {
+ }
+ bool operator() (const ConnCostRef& u, const ConnCostRef& v) const
+ {
+ return (u.second->id() < v.second->id());
+ }
+};
+
+typedef std::set<ConnCostRef, CmpConnCostRef> ConnCostRefSet;
+typedef std::list<ConnCostRefSet> ConnCostRefSetList;
+
+
+static double cheapEstimatedCost(ConnRef *lineRef)
+{
+ // We use an estimate of overall connector length, reduced by a count
+ // of the number of segments. In the case of equal length, This causes
+ // straight line connectors to be left alone and connectors with more
+ // complex paths to be rerouted.
+ bool isPolyLine = (lineRef->routingType() == ConnType_PolyLine);
+ const PolyLine& route = lineRef->displayRoute();
+ double length = 0;
+
+ for (size_t i = 1; i < route.size(); ++i)
+ {
+ const Point& a = route.ps[i - 1];
+ const Point& b = route.ps[i];
+
+ double segmentLength = (isPolyLine) ?
+ euclideanDist(a, b) : manhattanDist(a, b);
+ length += segmentLength;
+ }
+ return length - (route.size() + 1);
+}
+
+// A map of connectors to the set of connectors that cross them.
+typedef std::map<ConnRef *, std::set<ConnRef *> > CrossingConnectorsMap;
+
+// A list of connector crossing maps that don't interact with each other.
+typedef std::list<CrossingConnectorsMap> CrossingConnectorsMapList;
+
+// This class maintains the list of connector crossing maps and provides
+// methods for adding crossing connector pairs and getting the minimal sets
+// of connectors that can be removed from each group to prevent crossings.
+class CrossingConnectorsInfo
+{
+ public:
+
+ // Add information that a pair of connectors cross.
+ void addCrossing(ConnRef *conn1, ConnRef *conn2)
+ {
+ // Find the existing or new group that this crossing
+ // should be recorded in.
+ CrossingConnectorsMapList::iterator it =
+ groupForCrossingConns(conn1, conn2);
+ CrossingConnectorsMap& pairsSet = *it;
+
+ // Record the crossing.
+ pairsSet[conn1].insert(conn2);
+ pairsSet[conn2].insert(conn1);
+ }
+
+ // This method builds and returns a list of non-interacting sets of
+ // connectors (with crossing counts) that need to be removed so there
+ // are no crossings. These are the connectors we reroute. Where
+ // these connectors attach to exclusive connection pins, we return
+ // and thus reroute all connectors attached to the exlcusive pins. We
+ // do this so we are no so committed to possible bad pin assignemnts.
+ ConnCostRefSetList crossingSetsListToRemoveCrossingsFromGroups(void)
+ {
+ // A list of the minimal sets of connectors that cause crossings
+ // in all groups of crossingconnectors.
+ ConnCostRefSetList crossingSetsList;
+
+ // For each group of crossing connectors.
+ for (CrossingConnectorsMapList::iterator it = pairsSetList.begin();
+ it != pairsSetList.end(); ++it)
+ {
+ // The minimal set of crossing-causing connectors.
+ // We will build this.
+ ConnCostRefSet crossingSet;
+
+ // Set of exclusive pins that the crossing-causing connectors
+ // attach to.
+ std::set<ConnectionPinIds> exclusivePins;
+
+ // The group of all crossing pairs.
+ CrossingConnectorsMap& pairsSet = *it;
+
+ // For each crossing-causing connector.
+ ConnCostRef crossingCausingConnector;
+ while ( (crossingCausingConnector =
+ removeConnectorWithMostCrossings(pairsSet)).second != NULL )
+ {
+ // Add it to our crossing-causing set.
+ crossingSet.insert(crossingCausingConnector);
+
+ // Determine if it attaches to any exclusive pins and
+ // record these.
+ std::pair<ConnEnd, ConnEnd> ends =
+ crossingCausingConnector.second->endpointConnEnds();
+ // Look at one end.
+ ShapeConnectionPin *pin = ends.first.m_active_pin;
+ if (pin && pin->isExclusive())
+ {
+ exclusivePins.insert(pin->ids());
+ }
+ // Look at other end.
+ pin = ends.second.m_active_pin;
+ if (pin && pin->isExclusive())
+ {
+ exclusivePins.insert(pin->ids());
+ }
+ }
+
+ // For each of the remaining connectors which are not
+ // crossing-causing, add them to our crossing set if they
+ // attach to one of the exclusive pin classes which the
+ // crossing-causing connectors attached to.
+ for (CrossingConnectorsMap::const_iterator it2 =
+ pairsSet.begin(); it2 != pairsSet.end(); ++it2)
+ {
+ ConnRef *conn = it2->first;
+ std::pair<ConnEnd, ConnEnd> ends = conn->endpointConnEnds();
+ // Look at one end.
+ ShapeConnectionPin *pin = ends.first.m_active_pin;
+ if (pin && pin->isExclusive())
+ {
+ if (exclusivePins.count(pin->ids()) > 0)
+ {
+ // Attaches to an exclusive pin and it matches
+ // one attached to by the crossing-causing
+ // connectors. So add the conn to the
+ // crossing set and continue..
+ crossingSet.insert(std::make_pair(0, conn));
+ continue;
+ }
+ }
+ // Look at other end.
+ pin = ends.second.m_active_pin;
+ if (pin && pin->isExclusive())
+ {
+ if (exclusivePins.count(pin->ids()) > 0)
+ {
+ // Attaches to an exclusive pin and it matches
+ // one attached to by the crossing-causing
+ // connectors. So add the conn to the
+ // crossing set.
+ crossingSet.insert(std::make_pair(0, conn));
+ }
+ }
+ }
+
+ if (!crossingSet.empty())
+ {
+ crossingSetsList.push_back(crossingSet);
+ }
+ }
+ return crossingSetsList;
+ }
+
+ // Returns whether this pair of connector is already known to cross.
+ bool connsKnownToCross(ConnRef *conn1, ConnRef *conn2)
+ {
+ CrossingConnectorsMapList::iterator it1 = groupForConn(conn1);
+ CrossingConnectorsMapList::iterator it2 = groupForConn(conn2);
+
+ // The connectors cross if
+ if ((it1 == it2) && (it1 != pairsSetList.end()))
+ {
+ // They are in the same group and conn2 is in crossing
+ // connectors set of conn1.
+ CrossingConnectorsMap& pairsSet = *it1;
+ return ((pairsSet.count(conn1) > 0) &&
+ (pairsSet[conn1].count(conn2) > 0));
+ }
+ return false;
+ }
+
+ private:
+
+ // Given a particular group (pairsSet), removes the connector with
+ // the most crossings withing the group and returns it along with its
+ // crossing count.
+ ConnCostRef removeConnectorWithMostCrossings(
+ CrossingConnectorsMap& pairsSet)
+ {
+ // Tracking of the greatest number of crossings.
+ ConnRef *candidateConnector = NULL;
+ size_t candidateCrossingCount = 0;
+ double candidateEstimatedCost = 0;
+
+ // For each connector in the group...
+ for (CrossingConnectorsMap::const_iterator it = pairsSet.begin();
+ it != pairsSet.end(); ++it)
+ {
+ // ... check if it has any crossings.
+ size_t crossings = it->second.size();
+ if (crossings == 0)
+ {
+ // If not, skip.
+ continue;
+ }
+
+ // It has crossings. Determine if it has the most crossings
+ // of the connectors we've seen, or if it is tied but has
+ // a greater estimated cost. If so, it is our new candidate.
+ double cost = cheapEstimatedCost(it->first);
+ if ((crossings > candidateCrossingCount) ||
+ ((crossings == candidateCrossingCount) &&
+ (cost > candidateEstimatedCost)))
+ {
+ // Update with new candidate.
+ candidateConnector = it->first;
+ candidateCrossingCount = crossings;
+ candidateEstimatedCost = cost;
+ }
+ }
+
+ if (candidateConnector == NULL)
+ {
+ // If no candidate, return NULL connector.
+ return std::make_pair(0, (ConnRef *) NULL);
+ }
+
+ // Remove the candidate from the group. To do this we find the
+ // set of all connectors it crosses.
+ std::set<ConnRef *>& connSet = pairsSet[candidateConnector];
+ // For each of these
+ for (std::set<ConnRef *>::const_iterator it = connSet.begin();
+ it != connSet.end(); ++it)
+ {
+ // we remove the candidate from their crossing lists
+ pairsSet[*it].erase(candidateConnector);
+ }
+ // And then clear the crossing list for the candidate itself.
+ connSet.clear();
+
+ // Return the candidate connector and its original crossing count.
+ return std::make_pair(candidateCrossingCount, candidateConnector);
+ }
+
+ // Returns the iterator to the group that the given conn is in,
+ // if it is part of any crossing group.
+ CrossingConnectorsMapList::iterator groupForConn(ConnRef *conn)
+ {
+ // For each group...
+ for (CrossingConnectorsMapList::iterator it = pairsSetList.begin();
+ it != pairsSetList.end(); ++it)
+ {
+ // ... check if the connector is in it.
+ if (it->count(conn) > 0)
+ {
+ // If so, return it.
+ return it;
+ }
+ }
+ // Connector was not in any existing group.
+ return pairsSetList.end();
+ }
+
+ // Given a pair of crossing connectors, returns an iterator to the
+ // crossing connector group they are part of. If neither are part
+ // of any group, one is created and returned. If one connector is
+ // part of an exisitng group, that group is returned. If they are
+ // part of two different groups, the groups are merged and the
+ // merged group is returned.
+ CrossingConnectorsMapList::iterator groupForCrossingConns(
+ ConnRef *conn1, ConnRef *conn2)
+ {
+ CrossingConnectorsMapList::iterator it1 = groupForConn(conn1);
+ CrossingConnectorsMapList::iterator it2 = groupForConn(conn2);
+
+ // groupIt will be the iterator to the appropriate group.
+ CrossingConnectorsMapList::iterator groupIt = pairsSetList.end();
+
+ if ((it1 == pairsSetList.end()) && (it2 == pairsSetList.end()))
+ {
+ // Neither are part of a group. Create one.
+ CrossingConnectorsMap newSet;
+ groupIt = pairsSetList.insert(pairsSetList.end(), newSet);
+ }
+ else if ((it1 != pairsSetList.end()) && (it2 == pairsSetList.end()))
+ {
+ // it1 is the appropriate existing group.
+ groupIt = it1;
+ }
+ else if ((it1 == pairsSetList.end()) && (it2 != pairsSetList.end()))
+ {
+ // it2 is the appropriate exisitng group.
+ groupIt = it2;
+ }
+ else if (it1 != it2)
+ {
+ // There are two different existing groups, so merge them.
+ COLA_ASSERT(it1 != pairsSetList.end());
+ COLA_ASSERT(it2 != pairsSetList.end());
+ it1->insert(it2->begin(), it2->end());
+ pairsSetList.erase(it2);
+ groupIt = it1;
+ }
+ else
+ {
+ // These are already part of the same group. Do nothing.
+ COLA_ASSERT(it1 == it2);
+ groupIt = it1;
+ }
+ return groupIt;
+ }
+
+ CrossingConnectorsMapList pairsSetList;
+};
+
+
+void Router::performContinuationCheck(unsigned int phaseNumber,
+ size_t stepNumber, size_t totalSteps)
+{
+ // Compute the elapsed time in msec since the beginning of the transaction.
+ unsigned int elapsedMsec = (unsigned int)
+ ((clock() - m_transaction_start_time) /
+ (CLOCKS_PER_SEC / (double) 1000));
+
+ bool shouldContinue = shouldContinueTransactionWithProgress(elapsedMsec,
+ phaseNumber, TransactionPhaseCompleted,
+ stepNumber / (double)totalSteps);
+ if (shouldContinue == false)
+ {
+ // Host program has asked us not to continue the transaction.
+ m_abort_transaction = true;
+ }
+}
+
+
+bool Router::shouldContinueTransactionWithProgress(unsigned int elapsedTime,
+ unsigned int phaseNumber, unsigned int totalPhases,
+ double proportion)
+{
+ COLA_UNUSED(elapsedTime);
+ COLA_UNUSED(phaseNumber);
+ COLA_UNUSED(totalPhases);
+ COLA_UNUSED(proportion);
+
+#if 0
+ printf("Progress: %8u, phase %u of %u... %.2f%%\n", elapsedTime,
+ phaseNumber, totalPhases, proportion * 100);
+#endif
+
+ // We always continue. Subclasses can override this behaviour.
+ return true;
+}
+
+
+class CmpOrderedConnCostRef
+{
+ public:
+ CmpOrderedConnCostRef()
+ {
+ }
+ bool operator() (const ConnCostRef& u, const ConnCostRef& v) const
+ {
+ return (u.first < v.first);
+ }
+};
+typedef std::list<ConnCostRef> ConnCostRefList;
-typedef std::set<ConnRef *> ConnRefSet;
void Router::improveCrossings(void)
{
- const double crossing_penalty = routingPenalty(crossingPenalty);
- const double shared_path_penalty = routingPenalty(fixedSharedPathPenalty);
+ const double crossing_penalty = routingParameter(crossingPenalty);
+ const double shared_path_penalty = routingParameter(fixedSharedPathPenalty);
if ((crossing_penalty == 0) && (shared_path_penalty == 0))
{
// No penalties, return.
return;
}
+ // Information on crossing connector groups.
+ CrossingConnectorsInfo crossingConnInfo;
+
+ size_t numOfConns = connRefs.size();
+ size_t numOfConnsChecked = 0;
+
// Find crossings and reroute connectors.
- _inCrossingPenaltyReroutingStage = true;
- ConnRefSet crossingConns;
+ m_in_crossing_rerouting_stage = true;
ConnRefList::iterator fin = connRefs.end();
- for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
+ for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
{
+ // Progress reporting and continuation check.
+ ++numOfConnsChecked;
+ performContinuationCheck(TransactionPhaseCrossingDetection,
+ numOfConnsChecked, numOfConns);
+ if (m_abort_transaction)
+ {
+ m_in_crossing_rerouting_stage = false;
+ return;
+ }
+
Avoid::Polygon& iRoute = (*i)->routeRef();
+ if (iRoute.size() == 0)
+ {
+ // Rerouted hyperedges will have an empty route.
+ // We can't reroute these.
+ continue;
+ }
ConnRefList::iterator j = i;
- for (++j; j != fin; ++j)
+ for (++j; j != fin; ++j)
{
- if ((crossingConns.find(*i) != crossingConns.end()) &&
- (crossingConns.find(*j) != crossingConns.end()))
+ if (crossingConnInfo.connsKnownToCross(*i, *j))
{
// We already know both these have crossings.
continue;
}
+
// Determine if this pair cross.
Avoid::Polygon& jRoute = (*j)->routeRef();
- bool meetsPenaltyCriteria = false;
+ ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
{
const bool finalSegment = ((jInd + 1) == jRoute.size());
- CrossingsInfoPair crossingInfo = countRealCrossings(
- iRoute, true, jRoute, jInd, false,
- finalSegment, NULL, NULL, *i, *j);
-
- if ((shared_path_penalty > 0) &&
- (crossingInfo.second & CROSSING_SHARES_PATH) &&
- (crossingInfo.second & CROSSING_SHARES_FIXED_SEGMENT) &&
- !(crossingInfo.second & CROSSING_SHARES_PATH_AT_END))
+ cross.countForSegment(jInd, finalSegment);
+
+ if ((shared_path_penalty > 0) &&
+ (cross.crossingFlags & CROSSING_SHARES_PATH) &&
+ (cross.crossingFlags & CROSSING_SHARES_FIXED_SEGMENT) &&
+ (m_routing_options[penaliseOrthogonalSharedPathsAtConnEnds] ||
+ !(cross.crossingFlags & CROSSING_SHARES_PATH_AT_END)))
{
// We are penalising fixedSharedPaths and there is a
// fixedSharedPath.
- meetsPenaltyCriteria = true;
+ crossingConnInfo.addCrossing(*i, *j);
break;
}
- else if ((crossing_penalty > 0) && (crossingInfo.first > 0))
+ else if ((crossing_penalty > 0) && (cross.crossingCount > 0))
{
// We are penalising crossings and this is a crossing.
- meetsPenaltyCriteria = true;
+ crossingConnInfo.addCrossing(*i, *j);
break;
}
}
- if (meetsPenaltyCriteria)
- {
- crossingConns.insert(*i);
- crossingConns.insert(*j);
- }
}
}
- for (ConnRefSet::iterator i = crossingConns.begin();
- i != crossingConns.end(); ++i)
- {
- ConnRef *conn = *i;
- conn->makePathInvalid();
- // XXX: Could we free these routes here for extra savings?
- // conn->freeRoutes();
- }
- for (ConnRefSet::iterator i = crossingConns.begin();
- i != crossingConns.end(); ++i)
+ // Find the list of connector sets that need to be removed to avoid any
+ // crossings in all crossing groups. This is our candidate set for
+ // rerouting. Where these connectors connect to exlusive pins, all
+ // connectors attached to exclusive pins with the same IDs will also
+ // be rerouted, starting with the shortest.
+ ConnCostRefSetList crossingConnsGroups =
+ crossingConnInfo.crossingSetsListToRemoveCrossingsFromGroups();
+
+ // At this point we have a list containing crossings for rerouting.
+ // We do this rerouting via two passes, for each group of interacting
+ // crossing connectors:
+ // 1) clear existing routes and free pin assignments, and
+ // 2) compute new routes.
+ unsigned int numOfConnsToReroute = 1;
+ unsigned int numOfConnsRerouted = 1;
+ for (ConnCostRefSetList::iterator setIt = crossingConnsGroups.begin();
+ setIt != crossingConnsGroups.end(); ++setIt)
{
- ConnRef *conn = *i;
- conn->generatePath();
+ // Sort the connectors we will be rerouting from lowest to
+ // highest cost.
+ ConnCostRefList orderedConnList(setIt->begin(), setIt->end());
+ orderedConnList.sort(CmpOrderedConnCostRef());
+
+ // Perform rerouting of this set of connectors.
+ for (int pass = 0; pass < 2; ++pass)
+ {
+ for (ConnCostRefList::iterator connIt = orderedConnList.begin();
+ connIt != orderedConnList.end(); ++connIt)
+ {
+ ConnRef *conn = connIt->second;
+ if (pass == 0)
+ {
+ ++numOfConnsToReroute;
+
+ // Mark the fixed shared path as being invalid.
+ conn->makePathInvalid();
+
+ // Free the previous path, so it is not noticed by other
+ // connectors during rerouting.
+ conn->freeRoutes();
+
+ // Free pin assignments.
+ conn->freeActivePins();
+ }
+ else if (pass == 1)
+ {
+ // Progress reporting and continuation check.
+ performContinuationCheck(TransactionPhaseRerouteSearch,
+ numOfConnsRerouted, numOfConnsToReroute);
+ if (m_abort_transaction)
+ {
+ m_in_crossing_rerouting_stage = false;
+ return;
+ }
+ ++numOfConnsRerouted;
+
+ // Recompute this path.
+ conn->generatePath();
+ }
+ }
+ }
}
- _inCrossingPenaltyReroutingStage = false;
+ m_in_crossing_rerouting_stage = false;
}
@@ -865,9 +1568,9 @@ void Router::newBlockingShape(const Polygon& poly, int pid)
bool blocked = false;
bool countBorder = false;
- bool ep_in_poly1 = !(eID1.isShape) ?
+ bool ep_in_poly1 = (eID1.isConnPt()) ?
inPoly(poly, e1, countBorder) : false;
- bool ep_in_poly2 = !(eID2.isShape) ?
+ bool ep_in_poly2 = (eID2.isConnPt()) ?
inPoly(poly, e2, countBorder) : false;
if (ep_in_poly1 || ep_in_poly2)
{
@@ -882,7 +1585,7 @@ void Router::newBlockingShape(const Polygon& poly, int pid)
size_t pt_n = (pt_i == (poly.size() - 1)) ? 0 : pt_i + 1;
const Point& pi = poly.ps[pt_i];
const Point& pn = poly.ps[pt_n];
- if (segmentShapeIntersect(e1, e2, pi, pn,
+ if (segmentShapeIntersect(e1, e2, pi, pn,
seenIntersectionAtEndpoint))
{
blocked = true;
@@ -918,12 +1621,12 @@ void Router::checkAllBlockedEdges(int pid)
EdgeInf *tmp = iter;
iter = iter->lstNext;
- if (tmp->_blocker == -1)
+ if (tmp->blocker() == -1)
{
tmp->alertConns();
tmp->checkVis();
}
- else if (tmp->_blocker == pid)
+ else if (tmp->blocker() == pid)
{
tmp->checkVis();
}
@@ -946,7 +1649,8 @@ void Router::checkAllMissingEdges(void)
for (VertInf *j = first ; j != i; j = j->lstNext)
{
VertID jID = j->id;
- if (!(iID.isShape) && (iID.objID != jID.objID))
+ if (iID.isConnPt() && !iID.isConnectionPin() &&
+ (iID.objID != jID.objID))
{
// Don't keep visibility between edges of different conns
continue;
@@ -975,10 +1679,10 @@ void Router::generateContains(VertInf *pt)
bool countBorder = false;
// Compute enclosing shapes.
- ShapeRefList::const_iterator finish = shapeRefs.end();
- for (ShapeRefList::const_iterator i = shapeRefs.begin(); i != finish; ++i)
+ ObstacleList::const_iterator finish = m_obstacles.end();
+ for (ObstacleList::const_iterator i = m_obstacles.begin(); i != finish; ++i)
{
- if (inPoly((*i)->polygon(), pt->point, countBorder))
+ if (inPoly((*i)->routingPolygon(), pt->point, countBorder))
{
contains[pt->id].insert((*i)->id());
}
@@ -986,7 +1690,7 @@ void Router::generateContains(VertInf *pt)
// Computer enclosing Clusters
ClusterRefList::const_iterator clFinish = clusterRefs.end();
- for (ClusterRefList::const_iterator i = clusterRefs.begin();
+ for (ClusterRefList::const_iterator i = clusterRefs.begin();
i != clFinish; ++i)
{
if (inPolyGen((*i)->polygon(), pt->point))
@@ -997,7 +1701,7 @@ void Router::generateContains(VertInf *pt)
}
-void Router::adjustClustersWithAdd(const PolygonInterface& poly,
+void Router::adjustClustersWithAdd(const PolygonInterface& poly,
const int p_cluster)
{
for (VertInf *k = vertices.connsBegin(); k != vertices.shapesBegin();
@@ -1055,43 +1759,56 @@ static double AngleAFromThreeSides(const double a, const double b,
}
#endif
-void Router::markConnectors(ShapeRef *shape)
+// Given an deleted obstacle, uses a simple heauristic to determine polyline
+// connectors that may now have a better path through the region occupied by
+// the shape and mark them as needing to be rerouted.
+// See the "Incremental Connector Routing" paper which explains this code.
+//
+void Router::markPolylineConnectorsNeedingReroutingForDeletedObstacle(
+ Obstacle *obstacle)
{
if (RubberBandRouting)
{
- // When rubber-band routing, we do no reroute connectors that
+ // When rubber-band routing, we do not reroute connectors that
// may have a better route, only invalid connectors.
return;
}
COLA_ASSERT(SelectiveReroute);
+ // For each connector...
ConnRefList::const_iterator end = connRefs.end();
for (ConnRefList::const_iterator it = connRefs.begin(); it != end; ++it)
{
ConnRef *conn = (*it);
- if (conn->_route.empty())
+ if (conn->m_route.empty())
{
// Ignore uninitialised connectors.
continue;
}
- else if (conn->_needs_reroute_flag)
+ else if (conn->m_needs_reroute_flag)
{
// Already marked, so skip.
continue;
}
+ else if (conn->routingType() != ConnType_PolyLine)
+ {
+ // This test only works for polyline connectors, so skip others.
+ continue;
+ }
- Point start = conn->_route.ps[0];
- Point end = conn->_route.ps[conn->_route.size() - 1];
+ Point start = conn->m_route.ps[0];
+ Point end = conn->m_route.ps[conn->m_route.size() - 1];
- double conndist = conn->_route_dist;
+ double conndist = conn->m_route_dist;
double estdist;
double e1, e2;
- VertInf *beginV = shape->firstVert();
- VertInf *endV = shape->lastVert()->lstNext;
+ // For each vertex of the obstacle...
+ VertInf *beginV = obstacle->firstVert();
+ VertInf *endV = obstacle->lastVert()->lstNext;
for (VertInf *i = beginV; i != endV; i = i->lstNext)
{
const Point& p1 = i->point;
@@ -1240,7 +1957,7 @@ void Router::markConnectors(ShapeRef *shape)
db_printf("[%3d] - Possible better path found (%.1f < %.1f)\n",
conn->_id, estdist, conndist);
#endif
- conn->_needs_reroute_flag = true;
+ conn->m_needs_reroute_flag = true;
break;
}
@@ -1253,21 +1970,21 @@ ConnType Router::validConnType(const ConnType select) const
{
if (select != ConnType_None)
{
- if ((select == ConnType_Orthogonal) && _orthogonalRouting)
+ if ((select == ConnType_Orthogonal) && m_allows_orthogonal_routing)
{
return ConnType_Orthogonal;
}
- else if ((select == ConnType_PolyLine) && _polyLineRouting)
+ else if ((select == ConnType_PolyLine) && m_allows_polyline_routing)
{
return ConnType_PolyLine;
}
}
- if (_polyLineRouting)
+ if (m_allows_polyline_routing)
{
return ConnType_PolyLine;
}
- else if (_orthogonalRouting)
+ else if (m_allows_orthogonal_routing)
{
return ConnType_Orthogonal;
}
@@ -1275,52 +1992,91 @@ ConnType Router::validConnType(const ConnType select) const
}
-void Router::setRoutingPenalty(const PenaltyType penType, const double penVal)
+void Router::setRoutingParameter(const RoutingParameter parameter,
+ const double value)
{
- COLA_ASSERT(penType < lastPenaltyMarker);
- if (penVal < 0)
+ COLA_ASSERT(parameter < lastRoutingParameterMarker);
+ if (value < 0)
{
- // Set some sensible penalty.
- switch (penType)
+ // Set some sensible parameter value for the parameter being 'active'.
+ switch (parameter)
{
case segmentPenalty:
- _routingPenalties[penType] = 50;
+ m_routing_parameters[parameter] = 50;
break;
case fixedSharedPathPenalty:
- _routingPenalties[penType] = 110;
+ m_routing_parameters[parameter] = 110;
break;
case anglePenalty:
- _routingPenalties[penType] = 50;
+ m_routing_parameters[parameter] = 50;
break;
case crossingPenalty:
- _routingPenalties[penType] = 200;
+ m_routing_parameters[parameter] = 200;
break;
case clusterCrossingPenalty:
- _routingPenalties[penType] = 4000;
+ m_routing_parameters[parameter] = 4000;
+ break;
+ case idealNudgingDistance:
+ m_routing_parameters[parameter] = 4.0;
+ break;
+ case portDirectionPenalty:
+ m_routing_parameters[parameter] = 100;
break;
default:
- _routingPenalties[penType] = 50;
+ m_routing_parameters[parameter] = 50;
break;
}
}
else
{
- _routingPenalties[penType] = penVal;
+ m_routing_parameters[parameter] = value;
}
+ m_settings_changes = true;
+}
+
+
+double Router::routingParameter(const RoutingParameter parameter) const
+{
+ COLA_ASSERT(parameter < lastRoutingParameterMarker);
+ return m_routing_parameters[parameter];
+}
+
+
+void Router::setRoutingOption(const RoutingOption option, const bool value)
+{
+ COLA_ASSERT(option < lastRoutingOptionMarker);
+ m_routing_options[option] = value;
+ m_settings_changes = true;
+}
+
+
+bool Router::routingOption(const RoutingOption option) const
+{
+ COLA_ASSERT(option < lastRoutingOptionMarker);
+ return m_routing_options[option];
+}
+
+
+void Router::setRoutingPenalty(const RoutingParameter penType,
+ const double penValue)
+{
+ setRoutingParameter(penType, penValue);
}
+void Router::registerSettingsChange(void)
+{
+ m_settings_changes = true;
+}
-double Router::routingPenalty(const PenaltyType penType) const
+HyperedgeRerouter *Router::hyperedgeRerouter(void)
{
- COLA_ASSERT(penType < lastPenaltyMarker);
- return _routingPenalties[penType];
+ return &m_hyperedge_rerouter;
}
-double& Router::penaltyRef(const PenaltyType penType)
+bool Router::isInCrossingPenaltyReroutingStage(void) const
{
- COLA_ASSERT(penType < lastPenaltyMarker);
- return _routingPenalties[penType];
+ return m_in_crossing_rerouting_stage;
}
@@ -1343,12 +2099,12 @@ void Router::printInfo(void)
{
VertID pID = t->id;
- if ((pID.isShape) && (pID.objID != currshape))
+ if (!(pID.isConnPt()) && (pID.objID != currshape))
{
currshape = pID.objID;
st_shapes++;
}
- if (pID.isShape)
+ if (!(pID.isConnPt()))
{
st_vertices++;
}
@@ -1363,7 +2119,7 @@ void Router::printInfo(void)
{
std::pair<VertID, VertID> idpair = t->ids();
- if (!(idpair.first.isShape) || !(idpair.second.isShape))
+ if (idpair.first.isConnPt() || idpair.second.isConnPt())
{
st_valid_endpt_visedges++;
}
@@ -1385,7 +2141,7 @@ void Router::printInfo(void)
fprintf(fp, "Number of shapes: %d\n", st_shapes);
fprintf(fp, "Number of vertices: %d (%d real, %d endpoints)\n",
st_vertices + st_endpoints, st_vertices, st_endpoints);
- fprintf(fp, "Number of orhtog_vis_edges: %d\n", st_orthogonal_visedges);
+ fprintf(fp, "Number of orthog_vis_edges: %d\n", st_orthogonal_visedges);
fprintf(fp, "Number of vis_edges: %d (%d valid [%d normal, %d endpt], "
"%d invalid)\n", st_valid_shape_visedges + st_invalid_visedges +
st_valid_endpt_visedges, st_valid_shape_visedges +
@@ -1395,17 +2151,10 @@ void Router::printInfo(void)
fprintf(fp, "checkVisEdge tally: %d\n", st_checked_edges);
fprintf(fp, "----------------------\n");
- fprintf(fp, "ADDS: "); timers.Print(tmAdd, fp);
- fprintf(fp, "DELS: "); timers.Print(tmDel, fp);
- fprintf(fp, "MOVS: "); timers.Print(tmMov, fp);
- fprintf(fp, "***S: "); timers.Print(tmSev, fp);
- fprintf(fp, "PTHS: "); timers.Print(tmPth, fp);
- fprintf(fp, "OrthogGraph: "); timers.Print(tmOrthogGraph, fp);
- fprintf(fp, "OrthogRoute: "); timers.Print(tmOrthogRoute, fp);
- fprintf(fp, "OrthogCentre: "); timers.Print(tmOrthogCentre, fp);
- fprintf(fp, "OrthogNudge: "); timers.Print(tmOrthogNudge, fp);
- fprintf(fp, "\n");
- timers.Reset();
+#ifdef AVOID_PROFILE
+ timers.printAll(fp);
+ timers.reset();
+#endif
}
@@ -1422,29 +2171,63 @@ static void reduceRange(double& val)
// The following methods are for testing and debugging.
-bool Router::existsOrthogonalPathOverlap(void)
+bool Router::existsOrthogonalSegmentOverlap(const bool atEnds)
{
ConnRefList::iterator fin = connRefs.end();
- for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
+ for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
{
Avoid::Polygon iRoute = (*i)->displayRoute();
ConnRefList::iterator j = i;
- for (++j; j != fin; ++j)
+ for (++j; j != fin; ++j)
{
// Determine if this pair overlap
Avoid::Polygon jRoute = (*j)->displayRoute();
+ ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
+ cross.checkForBranchingSegments = true;
for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
{
const bool finalSegment = ((jInd + 1) == jRoute.size());
- CrossingsInfoPair crossingInfo = countRealCrossings(
- iRoute, true, jRoute, jInd, true,
- finalSegment, NULL, NULL, *i, *j);
+ cross.countForSegment(jInd, finalSegment);
+
+ if ((cross.crossingFlags & CROSSING_SHARES_PATH) &&
+ (atEnds ||
+ !(cross.crossingFlags & CROSSING_SHARES_PATH_AT_END)))
+ {
+ // We are looking for fixedSharedPaths and there is a
+ // fixedSharedPath.
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
- if ((crossingInfo.second & CROSSING_SHARES_PATH) &&
- (crossingInfo.second & CROSSING_SHARES_FIXED_SEGMENT) &&
- !(crossingInfo.second & CROSSING_SHARES_PATH_AT_END))
+bool Router::existsOrthogonalFixedSegmentOverlap(const bool atEnds)
+{
+ ConnRefList::iterator fin = connRefs.end();
+ for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
+ {
+ Avoid::Polygon iRoute = (*i)->displayRoute();
+ ConnRefList::iterator j = i;
+ for (++j; j != fin; ++j)
+ {
+ // Determine if this pair overlap
+ Avoid::Polygon jRoute = (*j)->displayRoute();
+ ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
+ cross.checkForBranchingSegments = true;
+ for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
+ {
+ const bool finalSegment = ((jInd + 1) == jRoute.size());
+ cross.countForSegment(jInd, finalSegment);
+
+ if ((cross.crossingFlags & CROSSING_SHARES_PATH) &&
+ (cross.crossingFlags & CROSSING_SHARES_FIXED_SEGMENT) &&
+ (atEnds ||
+ !(cross.crossingFlags & CROSSING_SHARES_PATH_AT_END)))
{
- // We looking for fixedSharedPaths and there is a
+ // We are looking for fixedSharedPaths and there is a
// fixedSharedPath.
return true;
}
@@ -1455,26 +2238,93 @@ bool Router::existsOrthogonalPathOverlap(void)
}
-bool Router::existsOrthogonalTouchingCorners(void)
+bool Router::existsOrthogonalTouchingPaths(void)
{
ConnRefList::iterator fin = connRefs.end();
- for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
+ for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
{
Avoid::Polygon iRoute = (*i)->displayRoute();
ConnRefList::iterator j = i;
- for (++j; j != fin; ++j)
+ for (++j; j != fin; ++j)
{
// Determine if this pair overlap
Avoid::Polygon jRoute = (*j)->displayRoute();
+ ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
+ cross.checkForBranchingSegments = true;
for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
{
const bool finalSegment = ((jInd + 1) == jRoute.size());
- CrossingsInfoPair crossingInfo = countRealCrossings(
- iRoute, true, jRoute, jInd, true,
- finalSegment, NULL, NULL, *i, *j);
+ cross.countForSegment(jInd, finalSegment);
+
+ if (cross.crossingFlags & CROSSING_TOUCHES)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
- if (crossingInfo.second & CROSSING_TOUCHES)
+
+// Counts the number of crossings between all connectors.
+//
+// If optimisedForConnectorType is set, This will only count crossings
+// between orthogonal connectors if they appear at segment endpoints.
+// Thus, it can be used on raw connectors but not on simplified orthogonal
+// connectors.
+//
+int Router::existsCrossings(const bool optimisedForConnectorType)
+{
+ int count = 0;
+ ConnRefList::iterator fin = connRefs.end();
+ for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
+ {
+ Avoid::Polygon iRoute = (*i)->displayRoute();
+ ConnRefList::iterator j = i;
+ for (++j; j != fin; ++j)
+ {
+ // Determine if this pair overlap
+ Avoid::Polygon jRoute = (*j)->displayRoute();
+ ConnRef *iConn = (optimisedForConnectorType) ? *i : NULL;
+ ConnRef *jConn = (optimisedForConnectorType) ? *j : NULL;
+ ConnectorCrossings cross(iRoute, true, jRoute, iConn, jConn);
+ cross.checkForBranchingSegments = true;
+ for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
+ {
+ const bool finalSegment = ((jInd + 1) == jRoute.size());
+
+ // Normal crossings aren't counted if we pass the pointers
+ // for the connectors, so don't pass them.
+ cross.countForSegment(jInd, finalSegment);
+
+ count += cross.crossingCount;
+ }
+ }
+ }
+ return count;
+}
+
+// Looks for non-orthogonal segments in orthogonal connectors and
+// returns true if it finds any.
+bool Router::existsInvalidOrthogonalPaths(void)
+{
+ // For each connector...
+ ConnRefList::iterator fin = connRefs.end();
+ for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
+ {
+ // If it is an orthogonal connector...
+ if ((*i)->routingType() == Avoid::ConnType_Orthogonal)
+ {
+ // Check each segment of the path...
+ Avoid::Polygon iRoute = (*i)->displayRoute();
+ for (size_t iInd = 1; iInd < iRoute.size(); ++iInd)
+ {
+ // And if it isn't either vertical or horizontal...
+ if ( (iRoute.at(iInd - 1).x != iRoute.at(iInd).x) &&
+ (iRoute.at(iInd - 1).y != iRoute.at(iInd).y) )
{
+ // Then we've found an invalid path.
return true;
}
}
@@ -1484,6 +2334,27 @@ bool Router::existsOrthogonalTouchingCorners(void)
}
+void Router::setTopologyAddon(TopologyAddonInterface *topologyAddon)
+{
+ COLA_ASSERT(m_topology_addon);
+ delete m_topology_addon;
+ if (topologyAddon)
+ {
+ m_topology_addon = topologyAddon->clone();
+ }
+ else
+ {
+ m_topology_addon = new TopologyAddonInterface();
+ }
+ registerSettingsChange();
+}
+
+void Router::improveOrthogonalTopology(void)
+{
+ COLA_ASSERT(m_topology_addon);
+ m_topology_addon->improveOrthogonalTopology(this);
+}
+
void Router::outputInstanceToSVG(std::string instanceName)
{
std::string filename;
@@ -1515,7 +2386,7 @@ void Router::outputInstanceToSVG(std::string instanceName)
reduceRange(p.x);
reduceRange(p.y);
-
+
if (p.x > -LIMIT)
{
minX = std::min(minX, p.x);
@@ -1534,10 +2405,10 @@ void Router::outputInstanceToSVG(std::string instanceName)
}
curr = curr->lstNext;
}
- minX -= 50;
- minY -= 50;
- maxX += 50;
- maxY += 50;
+ minX -= 8;
+ minY -= 8;
+ maxX += 8;
+ maxY += 8;
fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
fprintf(fp, "<svg xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns=\"http://www.w3.org/2000/svg\" width=\"100%%\" height=\"100%%\" viewBox=\"%g %g %g %g\">\n", minX, minY, maxX - minX, maxY - minY);
@@ -1545,105 +2416,220 @@ void Router::outputInstanceToSVG(std::string instanceName)
// Output source code to generate this instance of the router.
fprintf(fp, "<!-- Source code to generate this instance:\n");
fprintf(fp, "#include \"libavoid/libavoid.h\"\n");
+ if (m_topology_addon->outputCode(NULL))
+ {
+ fprintf(fp, "#include \"libcola/cola.h\"\n");
+ fprintf(fp, "#include \"libtopology/orthogonal_topology.h\"\n");
+ fprintf(fp, "using namespace cola;\n");
+ }
fprintf(fp, "using namespace Avoid;\n");
fprintf(fp, "int main(void) {\n");
- fprintf(fp, " Router *router = new Router(\n");
- fprintf(fp, " PolyLineRouting | OrthogonalRouting);\n");
- for (size_t p = 0; p < lastPenaltyMarker; ++p)
+ fprintf(fp, " Router *router = new Router(");
+ if (m_allows_polyline_routing)
+ {
+ fprintf(fp, "PolyLineRouting");
+ }
+ if (m_allows_polyline_routing && m_allows_orthogonal_routing)
{
- fprintf(fp, " router->setRoutingPenalty((PenaltyType)%lu, %g);\n",
- static_cast<long unsigned int>(p), _routingPenalties[p]);
+ fprintf(fp, " | ");
}
- fprintf(fp, " router->setOrthogonalNudgeDistance(%g);\n\n",
- orthogonalNudgeDistance());
- ShapeRefList::iterator shRefIt = shapeRefs.begin();
- while (shRefIt != shapeRefs.end())
+ if (m_allows_orthogonal_routing)
{
- ShapeRef *shRef = *shRefIt;
- fprintf(fp, " Polygon poly%u(%lu);\n",
- shRef->id(), static_cast<long unsigned int>(shRef->polygon().size()));
- for (size_t i = 0; i < shRef->polygon().size(); ++i)
+ fprintf(fp, "OrthogonalRouting");
+ }
+ fprintf(fp, ");\n");
+ for (size_t p = 0; p < lastRoutingParameterMarker; ++p)
+ {
+ fprintf(fp, " router->setRoutingParameter((RoutingParameter)%lu, %g);\n",
+ (unsigned long)p, m_routing_parameters[p]);
+ }
+ for (size_t p = 0; p < lastRoutingOptionMarker; ++p)
+ {
+ fprintf(fp, " router->setRoutingOption((RoutingOption)%lu, %s);\n",
+ (unsigned long)p, (m_routing_options[p]) ? "true" : "false");
+ }
+ fprintf(fp, " Polygon polygon;\n");
+ fprintf(fp, " ConnRef *connRef = NULL;\n");
+ fprintf(fp, " ConnEnd srcPt;\n");
+ fprintf(fp, " ConnEnd dstPt;\n");
+ fprintf(fp, " ConnEnd heConnPt;\n");
+ fprintf(fp, " PolyLine newRoute;\n");
+ fprintf(fp, " ShapeConnectionPin *connPin = NULL;\n");
+ fprintf(fp, "\n");
+ ClusterRefList::reverse_iterator revClusterRefIt = clusterRefs.rbegin();
+ while (revClusterRefIt != clusterRefs.rend())
+ {
+ ClusterRef *cRef = *revClusterRefIt;
+ fprintf(fp, " polygon = Polygon(%lu);\n",
+ (unsigned long)cRef->polygon().size());
+ for (size_t i = 0; i <cRef->polygon().size(); ++i)
{
- fprintf(fp, " poly%u.ps[%lu] = Point(%g, %g);\n",
- shRef->id(), static_cast<long unsigned int>(i), shRef->polygon().at(i).x,
- shRef->polygon().at(i).y);
+ fprintf(fp, " polygon.ps[%lu] = Point(%g, %g);\n",
+ (unsigned long)i, cRef->polygon().at(i).x,
+ cRef->polygon().at(i).y);
}
- fprintf(fp, " ShapeRef *shapeRef%u = new ShapeRef(router, poly%u, "
- "%u);\n", shRef->id(), shRef->id(), shRef->id());
- fprintf(fp, " router->addShape(shapeRef%u);\n\n", shRef->id());
- ++shRefIt;
+ fprintf(fp, " new ClusterRef(router, polygon, %u);\n", cRef->id());
+ ++revClusterRefIt;
+ }
+ ObstacleList::reverse_iterator revObstacleIt = m_obstacles.rbegin();
+ while (revObstacleIt != m_obstacles.rend())
+ {
+ Obstacle *obstacle = *revObstacleIt;
+ obstacle->outputCode(fp);
+ ++revObstacleIt;
}
ConnRefList::reverse_iterator revConnRefIt = connRefs.rbegin();
while (revConnRefIt != connRefs.rend())
{
ConnRef *connRef = *revConnRefIt;
- fprintf(fp, " ConnRef *connRef%u = new ConnRef(router, %u);\n",
- connRef->id(), connRef->id());
- if (connRef->src())
- {
- fprintf(fp, " ConnEnd srcPt%u(Point(%g, %g), %u);\n",
- connRef->id(), connRef->src()->point.x,
- connRef->src()->point.y, connRef->src()->visDirections);
- fprintf(fp, " connRef%u->setSourceEndpoint(srcPt%u);\n",
- connRef->id(), connRef->id());
- }
- if (connRef->dst())
- {
- fprintf(fp, " ConnEnd dstPt%u(Point(%g, %g), %u);\n",
- connRef->id(), connRef->dst()->point.x,
- connRef->dst()->point.y, connRef->dst()->visDirections);
- fprintf(fp, " connRef%u->setDestEndpoint(dstPt%u);\n",
- connRef->id(), connRef->id());
- }
- fprintf(fp, " connRef%u->setRoutingType((ConnType)%u);\n\n",
- connRef->id(), connRef->routingType());
+ connRef->outputCode(fp);
++revConnRefIt;
}
+
+ m_topology_addon->outputCode(fp);
+
fprintf(fp, " router->processTransaction();\n");
fprintf(fp, " router->outputInstanceToSVG();\n");
+
+ m_topology_addon->outputDeletionCode(fp);
fprintf(fp, " delete router;\n");
fprintf(fp, " return 0;\n");
fprintf(fp, "};\n");
fprintf(fp, "-->\n");
+ fprintf(fp, "<g inkscape:groupmode=\"layer\" "
+ "inkscape:label=\"Clusters\">\n");
+ revClusterRefIt = clusterRefs.rbegin();
+ while (revClusterRefIt != clusterRefs.rend())
+ {
+ ClusterRef *cRef = *revClusterRefIt;
+ fprintf(fp, "<path id=\"cluster-%u\" style=\"stroke-width: 1px; "
+ "stroke: black; fill: green; opacity: 0.1;\" d=\"",
+ cRef->id());
+ for (size_t i = 0; i < cRef->polygon().size(); ++i)
+ {
+ fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
+ cRef->polygon().at(i).x, cRef->polygon().at(i).y);
+ }
+ fprintf(fp, "Z\" />\n");
+
+ fprintf(fp, "<path id=\"cluster-%u-rect\" style=\"stroke-width: 1px; "
+ "stroke: black; fill: green; opacity: 0.1;\" d=\"",
+ cRef->id());
+ for (size_t i = 0; i < cRef->rectangularPolygon().size(); ++i)
+ {
+ fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
+ cRef->rectangularPolygon().at(i).x,
+ cRef->rectangularPolygon().at(i).y);
+ }
+ fprintf(fp, "Z\" />\n");
+ ++revClusterRefIt;
+ }
+ fprintf(fp, "</g>\n");
fprintf(fp, "<g inkscape:groupmode=\"layer\" "
- "inkscape:label=\"ShapesPoly\">\n");
- shRefIt = shapeRefs.begin();
- while (shRefIt != shapeRefs.end())
+ "style=\"display: none;\" "
+ "inkscape:label=\"ShapePolygons\">\n");
+ ObstacleList::iterator obstacleIt = m_obstacles.begin();
+ while (obstacleIt != m_obstacles.end())
{
- ShapeRef *shRef = *shRefIt;
+ Obstacle *obstacle = *obstacleIt;
+ bool isShape = (NULL != dynamic_cast<ShapeRef *> (obstacle));
+
+ if ( ! isShape )
+ {
+ // Don't output obstacles here, for now.
+ ++obstacleIt;
+ continue;
+ }
fprintf(fp, "<path id=\"poly-%u\" style=\"stroke-width: 1px; "
- "stroke: black; fill: blue; fill-opacity: 0.3;\" d=\"",
- shRef->id());
- for (size_t i = 0; i < shRef->polygon().size(); ++i)
+ "stroke: black; fill: %s; fill-opacity: 0.3;\" d=\"",
+ obstacle->id(), (isShape) ? "grey" : "red");
+ for (size_t i = 0; i < obstacle->polygon().size(); ++i)
{
- fprintf(fp, "%c %g,%g ", ((i == 0) ? 'M' : 'L'),
- shRef->polygon().at(i).x, shRef->polygon().at(i).y);
+ fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
+ obstacle->polygon().at(i).x, obstacle->polygon().at(i).y);
}
fprintf(fp, "Z\" />\n");
- ++shRefIt;
+ ++obstacleIt;
+ }
+ fprintf(fp, "</g>\n");
+
+ fprintf(fp, "<g inkscape:groupmode=\"layer\" "
+ "style=\"display: none;\" "
+ "inkscape:label=\"ObstaclePolygons\">\n");
+ obstacleIt = m_obstacles.begin();
+ while (obstacleIt != m_obstacles.end())
+ {
+ Obstacle *obstacle = *obstacleIt;
+ bool isShape = (NULL != dynamic_cast<ShapeRef *> (obstacle));
+
+ if ( ! isShape )
+ {
+ // Don't output obstacles here, for now.
+ ++obstacleIt;
+ continue;
+ }
+
+ Polygon polygon = obstacle->routingPolygon();
+ fprintf(fp, "<path id=\"poly-%u\" style=\"stroke-width: 1px; "
+ "stroke: black; fill: %s; fill-opacity: 0.3;\" d=\"",
+ obstacle->id(), (isShape) ? "grey" : "red");
+ for (size_t i = 0; i < polygon.size(); ++i)
+ {
+ fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
+ polygon.at(i).x, polygon.at(i).y);
+ }
+ fprintf(fp, "Z\" />\n");
+ ++obstacleIt;
}
fprintf(fp, "</g>\n");
fprintf(fp, "<g inkscape:groupmode=\"layer\" "
"style=\"display: none;\" "
- "inkscape:label=\"ShapesRect\">\n");
- shRefIt = shapeRefs.begin();
- while (shRefIt != shapeRefs.end())
+ "inkscape:label=\"IdealJunctions\">\n");
+ for (ObstacleList::iterator obstacleIt = m_obstacles.begin();
+ obstacleIt != m_obstacles.end(); ++obstacleIt)
{
- ShapeRef *shRef = *shRefIt;
- double minX, minY, maxX, maxY;
- shRef->polygon().getBoundingRect(&minX, &minY, &maxX, &maxY);
+ JunctionRef *junction = dynamic_cast<JunctionRef *> (*obstacleIt);
+ if (junction)
+ {
+ fprintf(fp, "<circle id=\"idealJunction-%u\" cx=\"%g\" cy=\"%g\" "
+ "r=\"8\" style=\"stroke: none; fill: %s; "
+ "fill-opacity: 0.5;\" />\n", junction->id(),
+ junction->recommendedPosition().x,
+ junction->recommendedPosition().y, "green");
+ }
- fprintf(fp, "<rect id=\"rect-%u\" x=\"%g\" y=\"%g\" width=\"%g\" height=\"%g\" "
- "style=\"stroke-width: 1px; stroke: black; fill: blue; fill-opacity: 0.3;\" />\n",
- shRef->id(), minX, minY, maxX - minX, maxY - minY);
- ++shRefIt;
}
fprintf(fp, "</g>\n");
+ fprintf(fp, "<g inkscape:groupmode=\"layer\" "
+ "inkscape:label=\"ObstacleRects\">\n");
+ obstacleIt = m_obstacles.begin();
+ while (obstacleIt != m_obstacles.end())
+ {
+ Obstacle *obstacle = *obstacleIt;
+ bool isShape = (NULL != dynamic_cast<ShapeRef *> (obstacle));
+
+ if ( ! isShape )
+ {
+ // Don't output obstacles here, for now.
+ ++obstacleIt;
+ continue;
+ }
+
+ Box bBox = obstacle->routingBox();
+
+ fprintf(fp, "<rect id=\"rect-%u\" x=\"%g\" y=\"%g\" width=\"%g\" "
+ "height=\"%g\" style=\"stroke-width: 1px; stroke: black; "
+ "fill: grey; stroke-opacity: 0.1; fill-opacity: 0.1;\" />\n",
+ obstacle->id(), bBox.min.x, bBox.min.y,
+ bBox.max.x - bBox.min.x, bBox.max.y - bBox.min.y);
+ ++obstacleIt;
+ }
+ fprintf(fp, "</g>\n");
fprintf(fp, "<g inkscape:groupmode=\"layer\" "
"inkscape:label=\"VisGraph\""
@@ -1657,28 +2643,27 @@ void Router::outputInstanceToSVG(std::string instanceName)
for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext)
{
std::pair<VertID, VertID> ids = t->ids();
- bool isShape = (ids.first.isShape) && (ids.second.isShape);
- if (!isShape)
+ bool isConn = ids.first.isConnPt() || ids.second.isConnPt();
+ if (isConn)
{
continue;
}
std::pair<Point, Point> ptpair = t->points();
Point p1 = ptpair.first;
Point p2 = ptpair.second;
-
+
reduceRange(p1.x);
reduceRange(p1.y);
reduceRange(p2.x);
reduceRange(p2.y);
-
- fprintf(fp, "<path d=\"M %g,%g L %g,%g\" "
- "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
+
+ fprintf(fp, "<path d=\"M %g %g L %g %g\" "
+ "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
p1.x, p1.y, p2.x, p2.y,
- (!(ids.first.isShape) || !(ids.second.isShape)) ? "green" :
+ (ids.first.isConnPt() || ids.second.isConnPt()) ? "blue" :
"red");
}
fprintf(fp, "</g>\n");
-
fprintf(fp, "<g inkscape:groupmode=\"layer\" "
"style=\"display: none;\" "
"inkscape:label=\"VisGraph-conn\""
@@ -1687,24 +2672,24 @@ void Router::outputInstanceToSVG(std::string instanceName)
for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext)
{
std::pair<VertID, VertID> ids = t->ids();
- bool isShape = (ids.first.isShape) && (ids.second.isShape);
- if (isShape)
+ bool isConn = ids.first.isConnPt() || ids.second.isConnPt();
+ if (!isConn)
{
continue;
}
std::pair<Point, Point> ptpair = t->points();
Point p1 = ptpair.first;
Point p2 = ptpair.second;
-
+
reduceRange(p1.x);
reduceRange(p1.y);
reduceRange(p2.x);
reduceRange(p2.y);
-
- fprintf(fp, "<path d=\"M %g,%g L %g,%g\" "
- "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
+
+ fprintf(fp, "<path d=\"M %g %g L %g %g\" "
+ "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
p1.x, p1.y, p2.x, p2.y,
- (!(ids.first.isShape) || !(ids.second.isShape)) ? "green" :
+ (ids.first.isConnPt() || ids.second.isConnPt()) ? "blue" :
"red");
}
fprintf(fp, "</g>\n");
@@ -1719,18 +2704,18 @@ void Router::outputInstanceToSVG(std::string instanceName)
std::pair<Point, Point> ptpair = t->points();
Point p1 = ptpair.first;
Point p2 = ptpair.second;
-
+
reduceRange(p1.x);
reduceRange(p1.y);
reduceRange(p2.x);
reduceRange(p2.y);
-
+
std::pair<VertID, VertID> ids = t->ids();
- fprintf(fp, "<path d=\"M %g,%g L %g,%g\" "
- "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
+ fprintf(fp, "<path d=\"M %g %g L %g %g\" "
+ "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
p1.x, p1.y, p2.x, p2.y,
- (!(ids.first.isShape) || !(ids.second.isShape)) ? "green" :
+ (ids.first.isConnPt() || ids.second.isConnPt()) ? "green" :
"red");
}
fprintf(fp, "</g>\n");
@@ -1743,15 +2728,15 @@ void Router::outputInstanceToSVG(std::string instanceName)
while (connRefIt != connRefs.end())
{
ConnRef *connRef = *connRefIt;
-
+
PolyLine route = connRef->route();
if (!route.empty())
{
- fprintf(fp, "<path id=\"raw-%u\" d=\"M %g,%g ", connRef->id(),
+ fprintf(fp, "<path id=\"raw-%u\" d=\"M %g %g ", connRef->id(),
route.ps[0].x, route.ps[0].y);
for (size_t i = 1; i < route.size(); ++i)
{
- fprintf(fp, "L %g,%g ", route.ps[i].x, route.ps[i].y);
+ fprintf(fp, "L %g %g ", route.ps[i].x, route.ps[i].y);
}
fprintf(fp, "\" ");
if (connRef->src() && connRef->dst())
@@ -1776,17 +2761,17 @@ void Router::outputInstanceToSVG(std::string instanceName)
while (connRefIt != connRefs.end())
{
ConnRef *connRef = *connRefIt;
-
+
PolyLine route = connRef->displayRoute().curvedPolyline(8);
if (!route.empty())
{
- fprintf(fp, "<path id=\"curved-%u\" d=\"M %g,%g ", connRef->id(),
+ fprintf(fp, "<path id=\"curved-%u\" d=\"M %g %g ", connRef->id(),
route.ps[0].x, route.ps[0].y);
for (size_t i = 1; i < route.size(); ++i)
{
if (route.ts[i] == 'C')
{
- fprintf(fp, "%c %g,%g %g,%g %g,%g", route.ts[i],
+ fprintf(fp, "%c %g %g %g %g %g %g", route.ts[i],
route.ps[i].x, route.ps[i].y,
route.ps[i+1].x, route.ps[i+1].y,
route.ps[i+2].x, route.ps[i+2].y);
@@ -1794,7 +2779,7 @@ void Router::outputInstanceToSVG(std::string instanceName)
}
else
{
- fprintf(fp, "%c %g,%g ", route.ts[i],
+ fprintf(fp, "%c %g %g ", route.ts[i],
route.ps[i].x, route.ps[i].y);
}
}
@@ -1808,7 +2793,7 @@ void Router::outputInstanceToSVG(std::string instanceName)
fprintf(fp, "style=\"fill: none; stroke: black; "
"stroke-width: 1px;\" />\n");
}
-
+
++connRefIt;
}
fprintf(fp, "</g>\n");
@@ -1821,15 +2806,15 @@ void Router::outputInstanceToSVG(std::string instanceName)
while (connRefIt != connRefs.end())
{
ConnRef *connRef = *connRefIt;
-
+
PolyLine route = connRef->displayRoute();
if (!route.empty())
{
- fprintf(fp, "<path id=\"disp-%u\" d=\"M %g,%g ", connRef->id(),
+ fprintf(fp, "<path id=\"disp-%u\" d=\"M %g %g ", connRef->id(),
route.ps[0].x, route.ps[0].y);
for (size_t i = 1; i < route.size(); ++i)
{
- fprintf(fp, "L %g,%g ", route.ps[i].x, route.ps[i].y);
+ fprintf(fp, "L %g %g ", route.ps[i].x, route.ps[i].y);
}
fprintf(fp, "\" ");
if (connRef->src() && connRef->dst())
@@ -1841,16 +2826,306 @@ void Router::outputInstanceToSVG(std::string instanceName)
fprintf(fp, "style=\"fill: none; stroke: black; "
"stroke-width: 1px;\" />\n");
}
+
+ ++connRefIt;
+ }
+ fprintf(fp, "</g>\n");
+ fprintf(fp, "<g inkscape:groupmode=\"layer\" "
+ "inkscape:label=\"ConnectorCheckpoints\""
+ ">\n");
+ connRefIt = connRefs.begin();
+ while (connRefIt != connRefs.end())
+ {
+ ConnRef *connRef = *connRefIt;
+
+ for (size_t i = 0; i < connRef->m_checkpoints.size(); ++i)
+ {
+ fprintf(fp, "<circle id=\"checkpoint-%u-%d\" cx=\"%g\" cy=\"%g\" "
+ "r=\"8\" style=\"stroke: none; fill: red; "
+ "fill-opacity: 0.25;\" />\n", connRef->id(), (int) i,
+ connRef->m_checkpoints[i].point.x,
+ connRef->m_checkpoints[i].point.y);
+ }
+
+ ++connRefIt;
+ }
+ fprintf(fp, "</g>\n");
+
+
+ fprintf(fp, "</svg>\n");
+ fclose(fp);
+ //printInfo();
+}
+
+void Router::outputDiagramSVG(std::string instanceName, LineReps *lineReps)
+{
+ std::string filename;
+ if (!instanceName.empty())
+ {
+ filename = instanceName;
+ }
+ else
+ {
+ filename = "libavoid-diagram";
+ }
+ filename += ".svg";
+ FILE *fp = fopen(filename.c_str(), "w");
+
+ if (fp == NULL)
+ {
+ return;
+ }
+
+ double minX = LIMIT;
+ double minY = LIMIT;
+ double maxX = -LIMIT;
+ double maxY = -LIMIT;
+
+ VertInf *curr = vertices.connsBegin();
+ while (curr)
+ {
+ Point p = curr->point;
+
+ reduceRange(p.x);
+ reduceRange(p.y);
+
+ if (p.x > -LIMIT)
+ {
+ minX = std::min(minX, p.x);
+ }
+ if (p.x < LIMIT)
+ {
+ maxX = std::max(maxX, p.x);
+ }
+ if (p.y > -LIMIT)
+ {
+ minY = std::min(minY, p.y);
+ }
+ if (p.y < LIMIT)
+ {
+ maxY = std::max(maxY, p.y);
+ }
+ curr = curr->lstNext;
+ }
+ minX -= 8;
+ minY -= 8;
+ maxX += 8;
+ maxY += 8;
+
+ fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ fprintf(fp, "<svg xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns=\"http://www.w3.org/2000/svg\" width=\"100%%\" height=\"100%%\" viewBox=\"%g %g %g %g\">\n", minX, minY, maxX - minX, maxY - minY);
+
+ fprintf(fp, "<g inkscape:groupmode=\"layer\" "
+ "inkscape:label=\"ShapesRect\">\n");
+ ObstacleList::iterator obstacleIt = m_obstacles.begin();
+ while (obstacleIt != m_obstacles.end())
+ {
+ Obstacle *obstacle = *obstacleIt;
+ bool isShape = (NULL != dynamic_cast<ShapeRef *> (obstacle));
+
+ if ( ! isShape )
+ {
+ // Don't output non-shape obstacles here, for now.
+ ++obstacleIt;
+ continue;
+ }
+
+ Box bBox = obstacle->polygon().offsetBoundingBox(0.0);
+
+ fprintf(fp, "<rect id=\"rect-%u\" x=\"%g\" y=\"%g\" width=\"%g\" "
+ "height=\"%g\" style=\"stroke-width: 1px; stroke: black; "
+ "fill: grey; stroke-opacity: 0.5; fill-opacity: 0.4;\" />\n",
+ obstacle->id(), bBox.min.x, bBox.min.y,
+ bBox.max.x - bBox.min.x, bBox.max.y - bBox.min.y);
+ ++obstacleIt;
+ }
+ fprintf(fp, "</g>\n");
+
+ fprintf(fp, "<g inkscape:groupmode=\"layer\" "
+ "inkscape:label=\"DisplayConnectors\""
+ ">\n");
+ ConnRefList::iterator connRefIt = connRefs.begin();
+ while (connRefIt != connRefs.end())
+ {
+ ConnRef *connRef = *connRefIt;
+
+ PolyLine route = connRef->displayRoute();
+ if (!route.empty())
+ {
+ fprintf(fp, "<path id=\"disp-%u\" d=\"M %g %g ", connRef->id(),
+ route.ps[0].x, route.ps[0].y);
+ for (size_t i = 1; i < route.size(); ++i)
+ {
+ fprintf(fp, "L %g %g ", route.ps[i].x, route.ps[i].y);
+ }
+ fprintf(fp, "\" ");
+ fprintf(fp, "style=\"fill: none; stroke: black; "
+ "stroke-width: 1px;\" />\n");
+
+ /*
+ for (size_t i = 1; i < route.size(); ++i)
+ {
+ if (route.segmentHasCheckpoint[i - 1])
+ {
+ fprintf(fp, "<path d=\"M %g %g ",
+ route.ps[i - 1].x, route.ps[i - 1].y);
+ fprintf(fp, "L %g %g\" ", route.ps[i].x, route.ps[i].y);
+ fprintf(fp, "style=\"fill: none; stroke: red; "
+ "stroke-width: 1px; stroke-opacity: 1;\" />\n");
+ }
+ }
+ */
+ }
+
++connRefIt;
}
fprintf(fp, "</g>\n");
+ if (lineReps)
+ {
+
+ for (LineReps::iterator it = lineReps->begin(); it != lineReps->end();
+ ++it)
+ {
+ fprintf(fp, "<path d=\"M %g %g ",
+ it->begin.x, it->begin.y);
+ fprintf(fp, "L %g %g\" ", it->end.x, it->end.y);
+ fprintf(fp, "style=\"fill: none; stroke: red; "
+ "stroke-width: 1px; stroke-opacity: 0.7;\" />\n");
+ }
+ }
fprintf(fp, "</svg>\n");
fclose(fp);
}
+void Router::outputDiagram(std::string instanceName)
+{
+ outputDiagramText(instanceName);
+#ifdef SVG_OUTPUT
+ outputInstanceToSVG(instanceName);
+#endif
+}
+
+void Router::outputDiagramText(std::string instanceName)
+{
+ std::string filename;
+ if (!instanceName.empty())
+ {
+ filename = instanceName;
+ }
+ else
+ {
+ filename = "libavoid-diagram";
+ }
+ filename += ".txt";
+ FILE *fp = fopen(filename.c_str(), "w");
+
+ if (fp == NULL)
+ {
+ return;
+ }
+
+ ObstacleList::iterator obstacleIt = m_obstacles.begin();
+ while (obstacleIt != m_obstacles.end())
+ {
+ Obstacle *obstacle = *obstacleIt;
+ bool isShape = (NULL != dynamic_cast<ShapeRef *> (obstacle));
+
+ if ( ! isShape )
+ {
+ // Don't output non-shape obstacles here, for now.
+ ++obstacleIt;
+ continue;
+ }
+
+ Box bBox = obstacle->polygon().offsetBoundingBox(0.0);
+
+ fprintf(fp, "rect\n");
+ fprintf(fp, "id=%u\n", obstacle->id());
+ fprintf(fp, "x=%g\n", bBox.min.x);
+ fprintf(fp, "y=%g\n", bBox.min.y);
+ fprintf(fp, "width=%g\n", bBox.max.x - bBox.min.x);
+ fprintf(fp, "height=%g\n", bBox.max.y - bBox.min.y);
+ fprintf(fp, "\n");
+
+ ++obstacleIt;
+ }
+
+ ConnRefList::iterator connRefIt = connRefs.begin();
+ while (connRefIt != connRefs.end())
+ {
+ ConnRef *connRef = *connRefIt;
+
+ PolyLine route = connRef->displayRoute();
+ if (!route.empty())
+ {
+ fprintf(fp, "path\n");
+ fprintf(fp, "id=%u\n", connRef->id());
+ for (size_t i = 0; i < route.size(); ++i)
+ {
+ fprintf(fp, "p%lu: %g %g ", i, route.ps[i].x, route.ps[i].y);
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, "\n");
+ }
+
+ ++connRefIt;
+ }
+
+ fprintf(fp, "\n");
+
+ fclose(fp);
+}
+
+HyperedgeNewAndDeletedObjectLists
+ Router::newAndDeletedObjectListsFromHyperedgeImprovement(void) const
+{
+ return m_hyperedge_improver.newAndDeletedObjectLists();
+}
+
+
+ConnRerouteFlagDelegate::ConnRerouteFlagDelegate()
+{
+}
+
+ConnRerouteFlagDelegate::~ConnRerouteFlagDelegate()
+{
+}
+
+bool *ConnRerouteFlagDelegate::addConn(ConnRef *conn)
+{
+ m_mapping.push_back(std::make_pair(conn, false));
+ return &(m_mapping.back().second);
+}
+
+void ConnRerouteFlagDelegate::removeConn(ConnRef *conn)
+{
+ std::list<std::pair<ConnRef *, bool> >::iterator it;
+ for (it = m_mapping.begin(); it != m_mapping.end(); ++it)
+ {
+ if (it->first == conn)
+ {
+ it->first = NULL;
+ }
+ }
+}
+
+
+void ConnRerouteFlagDelegate::alertConns(void)
+{
+ std::list<std::pair<ConnRef *, bool> >::iterator it;
+ for (it = m_mapping.begin(); it != m_mapping.end(); ++it)
+ {
+ if ((it->first != NULL) && (it->second == true))
+ {
+ it->second = false;
+ it->first->m_needs_reroute_flag = true;
+ }
+ }
+}
+
}