diff options
Diffstat (limited to '')
-rw-r--r-- | src/3rdparty/adaptagrams/libavoid/router.cpp | 3131 |
1 files changed, 3131 insertions, 0 deletions
diff --git a/src/3rdparty/adaptagrams/libavoid/router.cpp b/src/3rdparty/adaptagrams/libavoid/router.cpp new file mode 100644 index 0000000..f10e933 --- /dev/null +++ b/src/3rdparty/adaptagrams/libavoid/router.cpp @@ -0,0 +1,3131 @@ +/* + * vim: ts=4 sw=4 et tw=0 wm=0 + * + * libavoid - Fast, Incremental, Object-avoiding Line Router + * + * 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 + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * See the file LICENSE.LGPL distributed with the library. + * + * Licensees holding a valid commercial license may use this file in + * accordance with the commercial license agreement provided with the + * library. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Author(s): Michael Wybrow +*/ + + +#include <algorithm> +#include <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 { + + +Router::Router(const unsigned int flags) + : visOrthogGraph(), + PartialTime(false), + SimpleRouting(false), + ClusteredRouting(true), + // Poly-line algorithm options: + IgnoreRegions(true), + UseLeesAlgorithm(true), + InvisibilityGrph(true), + // General algorithm options: + SelectiveReroute(true), + PartialFeedback(false), + RubberBandRouting(false), + // Instrumentation: + st_checked_edges(0), + m_largest_assigned_id(0), + m_consolidate_actions(true), + m_currently_calling_destructors(false), + m_topology_addon(new TopologyAddonInterface()), + // Mode options: + 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(nullptr) +{ + // At least one of the Routing modes must be set. + COLA_ASSERT(flags & (PolyLineRouting | OrthogonalRouting)); + + if (flags & PolyLineRouting) + { + m_allows_polyline_routing = true; + } + if (flags & OrthogonalRouting) + { + m_allows_orthogonal_routing = true; + } + + for (size_t p = 0; p < lastRoutingParameterMarker; ++p) + { + m_routing_parameters[p] = 0.0; + } + 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()) + { + db_printf("Deleting connector %u in ~Router()\n", (*conn)->id()); + delete *conn; + conn = connRefs.begin(); + } + + // Remove remaining obstacles (shapes and junctions). + ObstacleList::iterator obstacle = m_obstacles.begin(); + while (obstacle != m_obstacles.end()) + { + 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()) + { + obstaclePtr->removeFromGraph(); + obstaclePtr->makeInactive(); + } + 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(visGraph.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 nullptr; +} + +void Router::modifyConnector(ConnRef *conn, const unsigned int type, + const ConnEnd& connEnd, bool connPinMoveUpdate) +{ + ActionInfo modInfo(ConnChange, conn); + + 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 + { + // Update the found action as necessary. + found->addConnEndUpdate(type, connEnd, connPinMoveUpdate); + } + + if (!m_consolidate_actions) + { + processTransaction(); + } +} + + +void Router::modifyConnector(ConnRef *conn) +{ + ActionInfo modInfo(ConnChange, conn); + + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), modInfo); + if (found == actionList.end()) + { + actionList.push_back(modInfo); + } + + if (!m_consolidate_actions) + { + processTransaction(); + } +} + + +void Router::modifyConnectionPin(ShapeConnectionPin *pin) +{ + ActionInfo modInfo(ConnectionPinChange, pin); + + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), modInfo); + if (found == actionList.end()) + { + 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; + } + } +} + + +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(), + ActionInfo(ShapeRemove, shape)) == actionList.end()); + COLA_ASSERT(find(actionList.begin(), actionList.end(), + ActionInfo(ShapeMove, shape)) == actionList.end()); + + ActionInfo addInfo(ShapeAdd, shape); + + ActionInfoList::iterator found = + find(actionList.begin(), actionList.end(), addInfo); + if (found == actionList.end()) + { + actionList.push_back(addInfo); + } + + if (!m_consolidate_actions) + { + processTransaction(); + } +} + + +void Router::deleteShape(ShapeRef *shape) +{ + // 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(), + ActionInfo(ShapeAdd, shape)) == actionList.end()); + + // Delete any ShapeMove entries for this shape in the action list. + ActionInfoList::iterator found = find(actionList.begin(), + actionList.end(), ActionInfo(ShapeMove, shape)); + if (found != actionList.end()) + { + actionList.erase(found); + } + + // Add the ShapeRemove entry. + ActionInfo remInfo(ShapeRemove, shape); + found = find(actionList.begin(), actionList.end(), remInfo); + if (found == actionList.end()) + { + actionList.push_back(remInfo); + } + + 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) +{ + 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::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(), + ActionInfo(ShapeRemove, 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. + found = find(actionList.begin(), actionList.end(), moveInfo); + + if (found != actionList.end()) + { + // Just update the ActionInfo with the second polygon, but + // leave the firstMove setting alone. + found->newPoly = newPoly; + } + else + { + actionList.push_back(moveInfo); + } + + if (!m_consolidate_actions) + { + processTransaction(); + } +} + + +void Router::setStaticGraphInvalidated(const bool invalidated) +{ + m_static_orthogonal_graph_invalidated = invalidated; +} + + +void Router::destroyOrthogonalVisGraph(void) +{ + // Remove orthogonal visibility graph edges. + visOrthogGraph.clear(); + + // Remove the now orphaned vertices. + VertInf *curr = vertices.shapesBegin(); + while (curr) + { + if (curr->orphaned() && (curr->id == dummyOrthogID)) + { + VertInf *following = vertices.removeVertex(curr); + delete curr; + curr = following; + continue; + } + curr = curr->lstNext; + } +} + + +void Router::regenerateStaticBuiltGraph(void) +{ + // Here we do talks involved in updating the static-built visibility + // graph (if necessary) before we do any routing. + if (m_static_orthogonal_graph_invalidated) + { + if (m_allows_orthogonal_routing) + { + destroyOrthogonalVisGraph(); + + TIMER_START(this, tmOrthogGraph); + // Regenerate a new visibility graph. + generateStaticOrthogonalVisGraph(this); + + TIMER_STOP(this); + } + m_static_orthogonal_graph_invalidated = false; + } +} + + +bool Router::transactionUse(void) const +{ + return m_consolidate_actions; +} + + +void Router::setTransactionUse(const bool transactions) +{ + m_consolidate_actions = transactions; +} + + +// Processes the action list. +void Router::processActions(void) +{ + bool notPartialTime = !(PartialFeedback && PartialTime); + bool seenShapeMovesOrDeletes = 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) || + (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(); + JunctionRef *junction = actInf.junction(); + bool isMove = (actInf.type == ShapeMove) || + (actInf.type == JunctionMove);; + bool first_move = actInf.firstMove; + + unsigned int pid = obstacle->id(); + + // o Remove entries related to this shape's vertices + obstacle->removeFromGraph(); + + if (SelectiveReroute && (!isMove || notPartialTime || first_move)) + { + 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. + 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 && 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 == ShapeMove) || (actInf.type == JunctionMove)) + { + // o Check all edges that were blocked by moved obstacle. + checkAllBlockedEdges(actInf.obstacle()->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 + { + // check all edges not in graph + checkAllMissingEdges(); + } + } + + for (curr = actionList.begin(); curr != finish; ++curr) + { + ActionInfo& actInf = *curr; + 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) || + (actInf.type == JunctionMove); + + unsigned int pid = obstacle->id(); + + // Restore this shape for visibility. + obstacle->makeActive(); + + if (isMove) + { + if (shape) + { + shape->setNewPoly(newPoly); + } + else + { + junction->setPosition(actInf.newPosition); + } + } + const Polygon& shapePoly = obstacle->routingPolygon(); + + adjustContainsWithAdd(shapePoly, pid); + + if (m_allows_polyline_routing) + { + // o Check all visibility edges to see if this one shape + // blocks them. + if (!isMove || notPartialTime) + { + newBlockingShape(shapePoly, pid); + } + + // o Calculate visibility for the new vertices. + if (UseLeesAlgorithm) + { + obstacle->computeVisibilitySweep(); + } + else + { + obstacle->computeVisibilityNaive(); + } + obstacle->updatePinPolyLineVisibility(); + } + } + + // Update connector endpoints. + for (curr = actionList.begin(); curr != finish; ++curr) + { + ActionInfo& actInf = *curr; + if (actInf.type != ConnChange) + { + continue; + } + for (ConnUpdateList::iterator conn = actInf.conns.begin(); + conn != actInf.conns.end(); ++conn) + { + actInf.conn()->updateEndPoint(conn->first, conn->second); + } + } + // Clear the actionList. + actionList.clear(); +} + +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::addJunction(JunctionRef *junction) +{ + // 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); + } + + if (!m_consolidate_actions) + { + processTransaction(); + } +} + + +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::moveJunction(JunctionRef *junction, const double xDiff, + const double yDiff) +{ + 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(); + + adjustClustersWithAdd(poly, pid); +} + + +void Router::deleteCluster(ClusterRef *cluster) +{ + cluster->makeInactive(); + + unsigned int pid = cluster->id(); + + adjustClustersWithDel(pid); +} + + +unsigned int Router::newObjectId(void) const +{ + return m_largest_assigned_id + 1; +} + + +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) ? newObjectId() : suggestedId; + + // If assertions are enabled, then we check that this ID really is unique. + 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. 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 +{ + // Examine shapes/junctions. + for (ObstacleList::const_iterator i = m_obstacles.begin(); + i != m_obstacles.end(); ++i) + { + if ((*i)->id() == id) + { + return false; + } + } + + // Examine connectors. + for (ConnRefList::const_iterator i = connRefs.begin(); + i != connRefs.end(); ++i) + { + if ((*i)->id() == id) + { + return false; + } + } + + // Examine clusters. + for (ClusterRefList::const_iterator i = clusterRefs.begin(); + i != clusterRefs.end(); ++i) + { + if ((*i)->id() == id) + { + return false; + } + } + + return true; +} + + +//---------------------------------------------------------------------------- + + // 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) + { + std::pair<Obstacle *, Obstacle *> anchors = (*i)->endpointAnchors(); + + if ((type & runningTo) && + (anchors.second && (anchors.second->id() == shapeId))) + { + conns.push_back((*i)->id()); + } + else if ((type & runningFrom) && + (anchors.first && (anchors.first->id() == shapeId))) + { + conns.push_back((*i)->id()); + } + } +} + + + // Returns a list of shape Ids of all the shapes attached to the + // shape with the ID 'shapeId' with connection type 'type'. +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) + { + std::pair<Obstacle *, Obstacle *> anchors = (*i)->endpointAnchors(); + + if ((type & runningTo) && + (anchors.second && (anchors.second->id() == shapeId))) + { + if (anchors.first) + { + // Only if there is a shape attached to the other end. + shapes.push_back(anchors.first->id()); + } + } + else if ((type & runningFrom) && + (anchors.first && (anchors.first->id() == shapeId))) + { + if (anchors.second) + { + // Only if there is a shape attached to the other end. + 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 + // rerouted connectors (via a callback) that they need to be redrawn. +void Router::rerouteAndCallbackConnectors(void) +{ + ConnRefList reroutedConns; + ConnRefList::const_iterator fin = connRefs.end(); + + this->m_conn_reroute_flags.alertConns(); + + // Updating the orthogonal visibility graph if necessary. + regenerateStaticBuiltGraph(); + + 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) + { + // 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.push_back(connector); + } + TIMER_STOP(this); + } + + + // Perform any complete hyperedge rerouting that has been requested. + m_hyperedge_rerouter.performRerouting(); + + // Find and reroute crossing connectors if crossing penalties are set. + improveCrossings(); + + 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. + fin = reroutedConns.end(); + for (ConnRefList::const_iterator i = reroutedConns.begin(); i != fin; ++i) + { + 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 != nullptr ) + { + // 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 = nullptr; + 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 == nullptr) + { + // If no candidate, return nullptr connector. + return std::make_pair(0, (ConnRef *) nullptr); + } + + // 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(static_cast<double>(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; + + +void Router::improveCrossings(void) +{ + 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. + m_in_crossing_rerouting_stage = true; + ConnRefList::iterator fin = connRefs.end(); + 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) + { + if (crossingConnInfo.connsKnownToCross(*i, *j)) + { + // We already know both these have crossings. + continue; + } + + // Determine if this pair cross. + Avoid::Polygon& jRoute = (*j)->routeRef(); + ConnectorCrossings cross(iRoute, true, jRoute, *i, *j); + for (size_t jInd = 1; jInd < jRoute.size(); ++jInd) + { + const bool finalSegment = ((jInd + 1) == jRoute.size()); + 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. + crossingConnInfo.addCrossing(*i, *j); + break; + } + else if ((crossing_penalty > 0) && (cross.crossingCount > 0)) + { + // We are penalising crossings and this is a crossing. + crossingConnInfo.addCrossing(*i, *j); + break; + } + } + } + } + + // 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) + { + // 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(); + } + } + } + } + m_in_crossing_rerouting_stage = false; +} + + +void Router::newBlockingShape(const Polygon& poly, int pid) +{ + // o Check all visibility edges to see if this one shape + // blocks them. + EdgeInf *finish = visGraph.end(); + for (EdgeInf *iter = visGraph.begin(); iter != finish ; ) + { + EdgeInf *tmp = iter; + iter = iter->lstNext; + + if (tmp->getDist() != 0) + { + std::pair<VertID, VertID> ids(tmp->ids()); + VertID eID1 = ids.first; + VertID eID2 = ids.second; + std::pair<Point, Point> points(tmp->points()); + Point e1 = points.first; + Point e2 = points.second; + bool blocked = false; + + bool countBorder = false; + bool ep_in_poly1 = (eID1.isConnPt()) ? + inPoly(poly, e1, countBorder) : false; + bool ep_in_poly2 = (eID2.isConnPt()) ? + inPoly(poly, e2, countBorder) : false; + if (ep_in_poly1 || ep_in_poly2) + { + // Don't check edges that have a connector endpoint + // and are inside the shape being added. + continue; + } + + bool seenIntersectionAtEndpoint = false; + for (size_t pt_i = 0; pt_i < poly.size(); ++pt_i) + { + 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, + seenIntersectionAtEndpoint)) + { + blocked = true; + break; + } + } + if (blocked) + { + db_printf("\tRemoving newly blocked edge (by shape %3d)" + "... \n\t\t", pid); + tmp->alertConns(); + tmp->db_print(); + if (InvisibilityGrph) + { + tmp->addBlocker(pid); + } + else + { + delete tmp; + } + } + } + } +} + + +void Router::checkAllBlockedEdges(int pid) +{ + COLA_ASSERT(InvisibilityGrph); + + for (EdgeInf *iter = invisGraph.begin(); iter != invisGraph.end() ; ) + { + EdgeInf *tmp = iter; + iter = iter->lstNext; + + if (tmp->blocker() == -1) + { + tmp->alertConns(); + tmp->checkVis(); + } + else if (tmp->blocker() == pid) + { + tmp->checkVis(); + } + } +} + + +void Router::checkAllMissingEdges(void) +{ + COLA_ASSERT(!InvisibilityGrph); + + VertInf *first = vertices.connsBegin(); + + VertInf *pend = vertices.end(); + for (VertInf *i = first; i != pend; i = i->lstNext) + { + VertID iID = i->id; + + // Check remaining, earlier vertices + for (VertInf *j = first ; j != i; j = j->lstNext) + { + VertID jID = j->id; + if (iID.isConnPt() && !iID.isConnectionPin() && + (iID.objID != jID.objID)) + { + // Don't keep visibility between edges of different conns + continue; + } + + // See if the edge is already there? + bool found = (EdgeInf::existingEdge(i, j) != nullptr); + + if (!found) + { + // Didn't already exist, check. + bool knownNew = true; + EdgeInf::checkEdgeVisibility(i, j, knownNew); + } + } + } +} + + +void Router::generateContains(VertInf *pt) +{ + contains[pt->id].clear(); + enclosingClusters[pt->id].clear(); + + // Don't count points on the border as being inside. + bool countBorder = false; + + // Compute enclosing shapes. + ObstacleList::const_iterator finish = m_obstacles.end(); + for (ObstacleList::const_iterator i = m_obstacles.begin(); i != finish; ++i) + { + if (inPoly((*i)->routingPolygon(), pt->point, countBorder)) + { + contains[pt->id].insert((*i)->id()); + } + } + + // Computer enclosing Clusters + ClusterRefList::const_iterator clFinish = clusterRefs.end(); + for (ClusterRefList::const_iterator i = clusterRefs.begin(); + i != clFinish; ++i) + { + if (inPolyGen((*i)->polygon(), pt->point)) + { + enclosingClusters[pt->id].insert((*i)->id()); + } + } +} + + +void Router::adjustClustersWithAdd(const PolygonInterface& poly, + const int p_cluster) +{ + for (VertInf *k = vertices.connsBegin(); k != vertices.shapesBegin(); + k = k->lstNext) + { + if (inPolyGen(poly, k->point)) + { + enclosingClusters[k->id].insert(p_cluster); + } + } +} + + +void Router::adjustClustersWithDel(const int p_cluster) +{ + for (ContainsMap::iterator k = enclosingClusters.begin(); + k != enclosingClusters.end(); ++k) + { + (*k).second.erase(p_cluster); + } +} + + +void Router::adjustContainsWithAdd(const Polygon& poly, const int p_shape) +{ + // Don't count points on the border as being inside. + bool countBorder = false; + + for (VertInf *k = vertices.connsBegin(); k != vertices.shapesBegin(); + k = k->lstNext) + { + if (inPoly(poly, k->point, countBorder)) + { + contains[k->id].insert(p_shape); + } + } +} + + +void Router::adjustContainsWithDel(const int p_shape) +{ + for (ContainsMap::iterator k = contains.begin(); k != contains.end(); ++k) + { + (*k).second.erase(p_shape); + } +} + + +#ifdef SELECTIVE_DEBUG +static double AngleAFromThreeSides(const double a, const double b, + const double c) +{ + // returns angle A, the angle opposite from side a, in radians + return acos((pow(b, 2) + pow(c, 2) - pow(a, 2)) / (2 * b * c)); +} +#endif + +// 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 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->m_route.empty()) + { + // Ignore uninitialised connectors. + continue; + } + 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->m_route.ps[0]; + Point end = conn->m_route.ps[conn->m_route.size() - 1]; + + double conndist = conn->m_route_dist; + + double estdist; + double e1, e2; + + // 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; + const Point& p2 = i->shNext->point; + + double offy; + double a; + double b; + double c; + double d; + + double min; + double max; + + if (p1.y == p2.y) + { + // Standard case + offy = p1.y; + a = start.x; + b = start.y - offy; + c = end.x; + d = end.y - offy; + + min = std::min(p1.x, p2.x); + max = std::max(p1.x, p2.x); + } + else if (p1.x == p2.x) + { + // Other Standard case + offy = p1.x; + a = start.y; + b = start.x - offy; + c = end.y; + d = end.x - offy; + + min = std::min(p1.y, p2.y); + max = std::max(p1.y, p2.y); + } + else + { + // Need to do rotation + Point n_p2(p2.x - p1.x, p2.y - p1.y); + Point n_start(start.x - p1.x, start.y - p1.y); + Point n_end(end.x - p1.x, end.y - p1.y); + //db_printf("n_p2: (%.1f, %.1f)\n", n_p2.x, n_p2.y); + //db_printf("n_start: (%.1f, %.1f)\n", n_start.x, n_start.y); + //db_printf("n_end: (%.1f, %.1f)\n", n_end.x, n_end.y); + + double theta = 0 - atan2(n_p2.y, n_p2.x); + //db_printf("theta = %.2f\n", theta * (180 / PI)); + + Point r_p1(0, 0); + Point r_p2 = n_p2; + start = n_start; + end = n_end; + + double cosv = cos(theta); + double sinv = sin(theta); + + r_p2.x = cosv * n_p2.x - sinv * n_p2.y; + r_p2.y = cosv * n_p2.y + sinv * n_p2.x; + start.x = cosv * n_start.x - sinv * n_start.y; + start.y = cosv * n_start.y + sinv * n_start.x; + end.x = cosv * n_end.x - sinv * n_end.y; + end.y = cosv * n_end.y + sinv * n_end.x; + //db_printf("r_p2: (%.1f, %.1f)\n", r_p2.x, r_p2.y); + //db_printf("r_start: (%.1f, %.1f)\n", start.x, start.y); + //db_printf("r_end: (%.1f, %.1f)\n", end.x, end.y); + + // This might be slightly off. + if (fabs(r_p2.y) > 0.0001) + { + db_printf("r_p2.y: %f != 0\n", r_p2.y); + } + r_p2.y = 0; + + offy = r_p1.y; + a = start.x; + b = start.y - offy; + c = end.x; + d = end.y - offy; + + min = std::min(r_p1.x, r_p2.x); + max = std::max(r_p1.x, r_p2.x); + + } + + double x; + if ((b + d) == 0) + { + db_printf("WARNING: (b + d) == 0\n"); + d = d * -1; + } + + if ((b == 0) && (d == 0)) + { + db_printf("WARNING: b == d == 0\n"); + if (((a < min) && (c < min)) || + ((a > max) && (c > max))) + { + // It's going to get adjusted. + x = a; + } + else + { + continue; + } + } + else + { + x = ((b*c) + (a*d)) / (b + d); + } + + //db_printf("%.1f, %.1f, %.1f, %.1f\n", a, b, c, d); + //db_printf("x = %.1f\n", x); + + x = std::max(min, x); + x = std::min(max, x); + + //db_printf("x = %.1f\n", x); + + Point xp; + if (p1.x == p2.x) + { + xp.x = offy; + xp.y = x; + } + else + { + xp.x = x; + xp.y = offy; + } + //db_printf("(%.1f, %.1f)\n", xp.x, xp.y); + + e1 = euclideanDist(start, xp); + e2 = euclideanDist(xp, end); + estdist = e1 + e2; + + + //db_printf("is %.1f < %.1f\n", estdist, conndist); + if (estdist < conndist) + { +#ifdef SELECTIVE_DEBUG + //double angle = AngleAFromThreeSides(dist(start, end), + // e1, e2); + db_printf("[%3d] - Possible better path found (%.1f < %.1f)\n", + conn->_id, estdist, conndist); +#endif + conn->m_needs_reroute_flag = true; + break; + } + + } + } +} + + +ConnType Router::validConnType(const ConnType select) const +{ + if (select != ConnType_None) + { + if ((select == ConnType_Orthogonal) && m_allows_orthogonal_routing) + { + return ConnType_Orthogonal; + } + else if ((select == ConnType_PolyLine) && m_allows_polyline_routing) + { + return ConnType_PolyLine; + } + } + + if (m_allows_polyline_routing) + { + return ConnType_PolyLine; + } + else if (m_allows_orthogonal_routing) + { + return ConnType_Orthogonal; + } + return ConnType_None; +} + + +void Router::setRoutingParameter(const RoutingParameter parameter, + const double value) +{ + COLA_ASSERT(parameter < lastRoutingParameterMarker); + if (value < 0) + { + // Set some sensible parameter value for the parameter being 'active'. + switch (parameter) + { + case segmentPenalty: + m_routing_parameters[parameter] = 50; + break; + case fixedSharedPathPenalty: + m_routing_parameters[parameter] = 110; + break; + case anglePenalty: + m_routing_parameters[parameter] = 50; + break; + case crossingPenalty: + m_routing_parameters[parameter] = 200; + break; + case clusterCrossingPenalty: + m_routing_parameters[parameter] = 4000; + break; + case idealNudgingDistance: + m_routing_parameters[parameter] = 4.0; + break; + case portDirectionPenalty: + m_routing_parameters[parameter] = 100; + break; + default: + m_routing_parameters[parameter] = 50; + break; + } + } + else + { + 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; +} + +HyperedgeRerouter *Router::hyperedgeRerouter(void) +{ + return &m_hyperedge_rerouter; +} + + +bool Router::isInCrossingPenaltyReroutingStage(void) const +{ + return m_in_crossing_rerouting_stage; +} + + +void Router::printInfo(void) +{ + FILE *fp = stdout; + fprintf(fp, "\nVisibility Graph info:\n"); + fprintf(fp, "----------------------\n"); + + unsigned int currshape = 0; + int st_shapes = 0; + int st_vertices = 0; + int st_endpoints = 0; + int st_valid_shape_visedges = 0; + int st_valid_endpt_visedges = 0; + int st_orthogonal_visedges = 0; + int st_invalid_visedges = 0; + VertInf *finish = vertices.end(); + for (VertInf *t = vertices.connsBegin(); t != finish; t = t->lstNext) + { + VertID pID = t->id; + + if (!(pID.isConnPt()) && (pID.objID != currshape)) + { + currshape = pID.objID; + st_shapes++; + } + if (!(pID.isConnPt())) + { + st_vertices++; + } + else + { + // The shape 0 ones are temporary and not considered. + st_endpoints++; + } + } + for (EdgeInf *t = visGraph.begin(); t != visGraph.end(); + t = t->lstNext) + { + std::pair<VertID, VertID> idpair = t->ids(); + + if (idpair.first.isConnPt() || idpair.second.isConnPt()) + { + st_valid_endpt_visedges++; + } + else + { + st_valid_shape_visedges++; + } + } + for (EdgeInf *t = invisGraph.begin(); t != invisGraph.end(); + t = t->lstNext) + { + st_invalid_visedges++; + } + for (EdgeInf *t = visOrthogGraph.begin(); t != visOrthogGraph.end(); + t = t->lstNext) + { + st_orthogonal_visedges++; + } + 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 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 + + st_valid_endpt_visedges, st_valid_shape_visedges, + st_valid_endpt_visedges, st_invalid_visedges); + fprintf(fp, "----------------------\n"); + fprintf(fp, "checkVisEdge tally: %d\n", st_checked_edges); + fprintf(fp, "----------------------\n"); + +#ifdef AVOID_PROFILE + timers.printAll(fp); + timers.reset(); +#endif +} + + +static const double LIMIT = 100000000; + +static void reduceRange(double& val) +{ + val = std::min(val, LIMIT); + val = std::max(val, -LIMIT); +} + + +//============================================================================= +// The following methods are for testing and debugging. + + +bool Router::existsOrthogonalSegmentOverlap(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) && + (atEnds || + !(cross.crossingFlags & CROSSING_SHARES_PATH_AT_END))) + { + // We are looking for fixedSharedPaths and there is a + // fixedSharedPath. + return true; + } + } + } + } + return false; +} + + +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 are looking for fixedSharedPaths and there is a + // fixedSharedPath. + return true; + } + } + } + } + return false; +} + + +bool Router::existsOrthogonalTouchingPaths(void) +{ + 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_TOUCHES) + { + return true; + } + } + } + } + return false; +} + + +// 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 : nullptr; + ConnRef *jConn = (optimisedForConnectorType) ? *j : nullptr; + 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; + } + } + } + } + return false; +} + + +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; + if (!instanceName.empty()) + { + filename = instanceName; + } + else + { + filename = "libavoid-debug"; + } + filename += ".svg"; + FILE *fp = fopen(filename.c_str(), "w"); + + if (fp == nullptr) + { + 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); + + // 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(nullptr)) + { + 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("); + if (m_allows_polyline_routing) + { + fprintf(fp, "PolyLineRouting"); + } + if (m_allows_polyline_routing && m_allows_orthogonal_routing) + { + fprintf(fp, " | "); + } + if (m_allows_orthogonal_routing) + { + 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 = nullptr;\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 = nullptr;\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, " polygon.ps[%lu] = Point(%g, %g);\n", + (unsigned long)i, cRef->polygon().at(i).x, + cRef->polygon().at(i).y); + } + 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; + 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\" " + "style=\"display: none;\" " + "inkscape:label=\"ShapePolygons\">\n"); + ObstacleList::iterator obstacleIt = m_obstacles.begin(); + while (obstacleIt != m_obstacles.end()) + { + Obstacle *obstacle = *obstacleIt; + bool isShape = (nullptr != 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: %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'), + obstacle->polygon().at(i).x, obstacle->polygon().at(i).y); + } + fprintf(fp, "Z\" />\n"); + ++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 = (nullptr != 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=\"IdealJunctions\">\n"); + for (ObstacleList::iterator obstacleIt = m_obstacles.begin(); + obstacleIt != m_obstacles.end(); ++obstacleIt) + { + 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, "</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 = (nullptr != 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\"" + ">\n"); + EdgeInf *finish = nullptr; + fprintf(fp, "<g inkscape:groupmode=\"layer\" " + "style=\"display: none;\" " + "inkscape:label=\"VisGraph-shape\"" + ">\n"); + finish = visGraph.end(); + for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext) + { + std::pair<VertID, VertID> ids = t->ids(); + 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", + p1.x, p1.y, p2.x, p2.y, + (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\"" + ">\n"); + finish = visGraph.end(); + for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext) + { + std::pair<VertID, VertID> ids = t->ids(); + 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", + p1.x, p1.y, p2.x, p2.y, + (ids.first.isConnPt() || ids.second.isConnPt()) ? "blue" : + "red"); + } + fprintf(fp, "</g>\n"); + fprintf(fp, "</g>\n"); + + fprintf(fp, "<g inkscape:groupmode=\"layer\" " + "style=\"display: none;\" " + "inkscape:label=\"OrthogVisGraph\">\n"); + finish = visOrthogGraph.end(); + for (EdgeInf *t = visOrthogGraph.begin(); t != finish; t = t->lstNext) + { + 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", + p1.x, p1.y, p2.x, p2.y, + (ids.first.isConnPt() || ids.second.isConnPt()) ? "green" : + "red"); + } + fprintf(fp, "</g>\n"); + + fprintf(fp, "<g inkscape:groupmode=\"layer\" " + "style=\"display: none;\" " + "inkscape:label=\"RawConnectors\"" + ">\n"); + ConnRefList::iterator connRefIt = connRefs.begin(); + 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(), + 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, "\" "); + if (connRef->src() && connRef->dst()) + { + fprintf(fp, "debug=\"src: %d dst: %d\" ", + connRef->src()->visDirections, + connRef->dst()->visDirections); + } + fprintf(fp, "style=\"fill: none; stroke: black; " + "stroke-width: 1px;\" />\n"); + } + + ++connRefIt; + } + fprintf(fp, "</g>\n"); + + fprintf(fp, "<g inkscape:groupmode=\"layer\" " + "style=\"display: none;\" " + "inkscape:label=\"CurvedDisplayConnectors\"" + ">\n"); + connRefIt = connRefs.begin(); + 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(), + 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], + 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); + i += 2; + } + else + { + fprintf(fp, "%c %g %g ", route.ts[i], + route.ps[i].x, route.ps[i].y); + } + } + fprintf(fp, "\" "); + if (connRef->src() && connRef->dst()) + { + fprintf(fp, "debug=\"src: %d dst: %d\" ", + connRef->src()->visDirections, + connRef->dst()->visDirections); + } + fprintf(fp, "style=\"fill: none; stroke: black; " + "stroke-width: 1px;\" />\n"); + } + + ++connRefIt; + } + fprintf(fp, "</g>\n"); + + + fprintf(fp, "<g inkscape:groupmode=\"layer\" " + "inkscape:label=\"DisplayConnectors\"" + ">\n"); + 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, "\" "); + if (connRef->src() && connRef->dst()) + { + fprintf(fp, "debug=\"src: %d dst: %d\" ", + connRef->src()->visDirections, + connRef->dst()->visDirections); + } + 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 == nullptr) + { + 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 = (nullptr != 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 == nullptr) + { + return; + } + + ObstacleList::iterator obstacleIt = m_obstacles.begin(); + while (obstacleIt != m_obstacles.end()) + { + Obstacle *obstacle = *obstacleIt; + bool isShape = (nullptr != 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%zu: %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 = nullptr; + } + } +} + + +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 != nullptr) && (it->second == true)) + { + it->second = false; + it->first->m_needs_reroute_flag = true; + } + } +} + + +} + |