Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
router.cpp
Go to the documentation of this file.
1/*
2 * vim: ts=4 sw=4 et tw=0 wm=0
3 *
4 * libavoid - Fast, Incremental, Object-avoiding Line Router
5 *
6 * Copyright (C) 2004-2014 Monash University
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 * See the file LICENSE.LGPL distributed with the library.
13 *
14 * Licensees holding a valid commercial license may use this file in
15 * accordance with the commercial license agreement provided with the
16 * library.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21 *
22 * Author(s): Michael Wybrow
23*/
24
25
26#include <algorithm>
27#include <cmath>
28#include <cfloat>
29
30#include "libavoid/shape.h"
31#include "libavoid/router.h"
32#include "libavoid/visibility.h"
33#include "libavoid/connector.h"
34#include "libavoid/junction.h"
35#include "libavoid/viscluster.h"
36#include "libavoid/connend.h"
37#include "libavoid/debug.h"
38#include "libavoid/orthogonal.h"
39#include "libavoid/assertions.h"
41
42
43namespace Avoid {
44
45
46Router::Router(const unsigned int flags)
47 : visOrthogGraph(),
48 PartialTime(false),
49 SimpleRouting(false),
50 ClusteredRouting(true),
51 // Poly-line algorithm options:
52 IgnoreRegions(true),
53 UseLeesAlgorithm(true),
54 InvisibilityGrph(true),
55 // General algorithm options:
56 SelectiveReroute(true),
57 PartialFeedback(false),
58 RubberBandRouting(false),
59 // Instrumentation:
60 st_checked_edges(0),
61 m_largest_assigned_id(0),
62 m_consolidate_actions(true),
63 m_currently_calling_destructors(false),
64 m_topology_addon(new TopologyAddonInterface()),
65 // Mode options:
66 m_allows_polyline_routing(false),
67 m_allows_orthogonal_routing(false),
68 m_static_orthogonal_graph_invalidated(true),
69 m_in_crossing_rerouting_stage(false),
70 m_settings_changes(false),
71 m_debug_handler(nullptr)
72{
73 // At least one of the Routing modes must be set.
74 COLA_ASSERT(flags & (PolyLineRouting | OrthogonalRouting));
75
76 if (flags & PolyLineRouting)
77 {
79 }
80 if (flags & OrthogonalRouting)
81 {
83 }
84
85 for (size_t p = 0; p < lastRoutingParameterMarker; ++p)
86 {
87 m_routing_parameters[p] = 0.0;
88 }
92
99 false;
101
104}
105
106
108{
110
111 // Delete remaining connectors.
112 ConnRefList::iterator conn = connRefs.begin();
113 while (conn != connRefs.end())
114 {
115 db_printf("Deleting connector %u in ~Router()\n", (*conn)->id());
116 delete *conn;
117 conn = connRefs.begin();
118 }
119
120 // Remove remaining obstacles (shapes and junctions).
121 ObstacleList::iterator obstacle = m_obstacles.begin();
122 while (obstacle != m_obstacles.end())
123 {
124 Obstacle *obstaclePtr = *obstacle;
125 ShapeRef *shape = dynamic_cast<ShapeRef *> (obstaclePtr);
126 db_printf("Deleting %s %u in ~Router()\n",
127 (shape) ? "shape" : "junction", obstaclePtr->id());
128 if (obstaclePtr->isActive())
129 {
130 obstaclePtr->removeFromGraph();
131 obstaclePtr->makeInactive();
132 }
133 delete obstaclePtr;
134 obstacle = m_obstacles.begin();
135 }
137
138 // Cleanup orphaned orthogonal graph vertices.
140
141 COLA_ASSERT(m_obstacles.size() == 0);
142 COLA_ASSERT(connRefs.size() == 0);
143 COLA_ASSERT(visGraph.size() == 0);
144
145 delete m_topology_addon;
146}
147
149{
150 m_debug_handler = handler;
151}
152
154{
155 return m_debug_handler;
156}
157
159{
160 // Count points on the border as being inside.
161 bool countBorder = true;
162
163 // Compute enclosing shapes.
164 ObstacleList::const_iterator finish = m_obstacles.end();
165 for (ObstacleList::const_iterator i = m_obstacles.begin(); i != finish; ++i)
166 {
167 ShapeRef *shape = dynamic_cast<ShapeRef *>(*i);
168 if (shape && inPoly(shape->routingPolygon(), point, countBorder))
169 {
170 return shape;
171 }
172 }
173 return nullptr;
174}
175
176void Router::modifyConnector(ConnRef *conn, const unsigned int type,
177 const ConnEnd& connEnd, bool connPinMoveUpdate)
178{
179 ActionInfo modInfo(ConnChange, conn);
180
181 ActionInfoList::iterator found =
182 find(actionList.begin(), actionList.end(), modInfo);
183 if (found == actionList.end())
184 {
185 // Matching action not found, so add.
186 modInfo.conns.push_back(std::make_pair(type, connEnd));
187 actionList.push_back(modInfo);
188 }
189 else
190 {
191 // Update the found action as necessary.
192 found->addConnEndUpdate(type, connEnd, connPinMoveUpdate);
193 }
194
196 {
198 }
199}
200
201
203{
204 ActionInfo modInfo(ConnChange, conn);
205
206 ActionInfoList::iterator found =
207 find(actionList.begin(), actionList.end(), modInfo);
208 if (found == actionList.end())
209 {
210 actionList.push_back(modInfo);
211 }
212
214 {
216 }
217}
218
219
221{
222 ActionInfo modInfo(ConnectionPinChange, pin);
223
224 ActionInfoList::iterator found =
225 find(actionList.begin(), actionList.end(), modInfo);
226 if (found == actionList.end())
227 {
228 actionList.push_back(modInfo);
229 }
230
232 {
234 }
235}
236
237
239{
240 for (ActionInfoList::iterator curr = actionList.begin();
241 curr != actionList.end(); )
242 {
243 if (curr->objPtr == object)
244 {
245 curr = actionList.erase(curr);
246 }
247 else
248 {
249 ++curr;
250 }
251 }
252}
253
254
256{
257 // There shouldn't be remove events or move events for the same shape
258 // already in the action list.
259 // XXX: Possibly we could handle this by ordering them intelligently.
260 COLA_ASSERT(find(actionList.begin(), actionList.end(),
261 ActionInfo(ShapeRemove, shape)) == actionList.end());
262 COLA_ASSERT(find(actionList.begin(), actionList.end(),
263 ActionInfo(ShapeMove, shape)) == actionList.end());
264
265 ActionInfo addInfo(ShapeAdd, shape);
266
267 ActionInfoList::iterator found =
268 find(actionList.begin(), actionList.end(), addInfo);
269 if (found == actionList.end())
270 {
271 actionList.push_back(addInfo);
272 }
273
275 {
277 }
278}
279
280
282{
283 // There shouldn't be add events events for the same shape already
284 // in the action list.
285 // XXX: Possibly we could handle this by ordering them intelligently.
286 COLA_ASSERT(find(actionList.begin(), actionList.end(),
287 ActionInfo(ShapeAdd, shape)) == actionList.end());
288
289 // Delete any ShapeMove entries for this shape in the action list.
290 ActionInfoList::iterator found = find(actionList.begin(),
291 actionList.end(), ActionInfo(ShapeMove, shape));
292 if (found != actionList.end())
293 {
294 actionList.erase(found);
295 }
296
297 // Add the ShapeRemove entry.
298 ActionInfo remInfo(ShapeRemove, shape);
299 found = find(actionList.begin(), actionList.end(), remInfo);
300 if (found == actionList.end())
301 {
302 actionList.push_back(remInfo);
303 }
304
306 {
308 }
309}
310
311
313{
315 delete connector;
317}
318
319void Router::moveShape(ShapeRef *shape, const double xDiff, const double yDiff)
320{
321 ActionInfo moveInfo(ShapeMove, shape, Polygon(), false);
322 ActionInfoList::iterator found =
323 find(actionList.begin(), actionList.end(), moveInfo);
324
325 Polygon newPoly;
326 if (found != actionList.end())
327 {
328 // The shape already has a queued move, so use that shape position.
329 newPoly = found->newPoly;
330 }
331 else
332 {
333 // Just use the existing position.
334 newPoly = shape->polygon();
335 }
336 newPoly.translate(xDiff, yDiff);
337
338 moveShape(shape, newPoly);
339}
340
341
343{
344 for (ObstacleList::iterator obstacleIt = m_obstacles.begin();
345 obstacleIt != m_obstacles.end(); ++obstacleIt)
346 {
347 ShapeRef *shape = dynamic_cast<ShapeRef *> (*obstacleIt);
348 JunctionRef *junction = dynamic_cast<JunctionRef *> (*obstacleIt);
349 if (shape)
350 {
351 moveShape(shape, 0, 0);
352 }
353 else if (junction)
354 {
355 moveJunction(junction, 0, 0);
356 }
357 }
358}
359
360void Router::moveShape(ShapeRef *shape, const Polygon& newPoly,
361 const bool first_move)
362{
363 // There shouldn't be remove events or add events for the same shape
364 // already in the action list.
365 // XXX: Possibly we could handle this by ordering them intelligently.
366 COLA_ASSERT(find(actionList.begin(), actionList.end(),
367 ActionInfo(ShapeRemove, shape)) == actionList.end());
368
369 ActionInfoList::iterator found = find(actionList.begin(),
370 actionList.end(), ActionInfo(ShapeAdd, shape));
371 if (found != actionList.end())
372 {
373 // The Add is enough, no need for the Move action too.
374 // The shape will be added with it's existing polygon,
375 // so set this to be the newPoly passed for the move.
376 found->shape()->setNewPoly(newPoly);
377 return;
378 }
379
380 ActionInfo moveInfo(ShapeMove, shape, newPoly, first_move);
381 // Sanely cope with the case where the user requests moving the same
382 // shape multiple times before rerouting connectors.
383 found = find(actionList.begin(), actionList.end(), moveInfo);
384
385 if (found != actionList.end())
386 {
387 // Just update the ActionInfo with the second polygon, but
388 // leave the firstMove setting alone.
389 found->newPoly = newPoly;
390 }
391 else
392 {
393 actionList.push_back(moveInfo);
394 }
395
397 {
399 }
400}
401
402
403void Router::setStaticGraphInvalidated(const bool invalidated)
404{
406}
407
408
410{
411 // Remove orthogonal visibility graph edges.
413
414 // Remove the now orphaned vertices.
415 VertInf *curr = vertices.shapesBegin();
416 while (curr)
417 {
418 if (curr->orphaned() && (curr->id == dummyOrthogID))
419 {
420 VertInf *following = vertices.removeVertex(curr);
421 delete curr;
422 curr = following;
423 continue;
424 }
425 curr = curr->lstNext;
426 }
427}
428
429
431{
432 // Here we do talks involved in updating the static-built visibility
433 // graph (if necessary) before we do any routing.
435 {
437 {
439
440 TIMER_START(this, tmOrthogGraph);
441 // Regenerate a new visibility graph.
443
444 TIMER_STOP(this);
445 }
447 }
448}
449
450
452{
454}
455
456
457void Router::setTransactionUse(const bool transactions)
458{
459 m_consolidate_actions = transactions;
460}
461
462
463// Processes the action list.
465{
466 bool notPartialTime = !(PartialFeedback && PartialTime);
467 bool seenShapeMovesOrDeletes = false;
468
469 m_transaction_start_time = clock();
470 m_abort_transaction = false;
471
472 std::list<unsigned int> deletedObstacles;
473 actionList.sort();
474 ActionInfoList::iterator curr;
475 ActionInfoList::iterator finish = actionList.end();
476 for (curr = actionList.begin(); curr != finish; ++curr)
477 {
478 ActionInfo& actInf = *curr;
479 if (!((actInf.type == ShapeRemove) || (actInf.type == ShapeMove) ||
480 (actInf.type == JunctionRemove) || (actInf.type == JunctionMove)))
481 {
482 // Not a move or remove action, so don't do anything.
483 continue;
484 }
485 seenShapeMovesOrDeletes = true;
486
487 Obstacle *obstacle = actInf.obstacle();
488 ShapeRef *shape = actInf.shape();
489 JunctionRef *junction = actInf.junction();
490 bool isMove = (actInf.type == ShapeMove) ||
491 (actInf.type == JunctionMove);;
492 bool first_move = actInf.firstMove;
493
494 unsigned int pid = obstacle->id();
495
496 // o Remove entries related to this shape's vertices
497 obstacle->removeFromGraph();
498
499 if (SelectiveReroute && (!isMove || notPartialTime || first_move))
500 {
502 }
503
505
506 if (isMove)
507 {
508 if (shape)
509 {
510 shape->moveAttachedConns(actInf.newPoly);
511 }
512 else if (junction)
513 {
514 junction->moveAttachedConns(actInf.newPosition);
515 }
516 }
517
518 // Ignore this shape for visibility.
519 // XXX: We don't really need to do this if we're not using Partial
520 // Feedback. Without this the blocked edges still route
521 // around the shape until it leaves the connector.
522 obstacle->makeInactive();
523
524 if (!isMove)
525 {
526 // Free deleted obstacle.
528 deletedObstacles.push_back(obstacle->id());
529 delete obstacle;
531 }
532 }
533
534 if (seenShapeMovesOrDeletes && m_allows_polyline_routing)
535 {
537 {
538 // Check edges for obstacles that were moved or removed.
539 for (curr = actionList.begin(); curr != finish; ++curr)
540 {
541 ActionInfo& actInf = *curr;
542 if ((actInf.type == ShapeMove) || (actInf.type == JunctionMove))
543 {
544 // o Check all edges that were blocked by moved obstacle.
545 checkAllBlockedEdges(actInf.obstacle()->id());
546 }
547 }
548
549 for (std::list<unsigned int>::iterator it = deletedObstacles.begin();
550 it != deletedObstacles.end(); ++it)
551 {
552 // o Check all edges that were blocked by deleted obstacle.
554 }
555 }
556 else
557 {
558 // check all edges not in graph
560 }
561 }
562
563 for (curr = actionList.begin(); curr != finish; ++curr)
564 {
565 ActionInfo& actInf = *curr;
566 if (!((actInf.type == ShapeAdd) || (actInf.type == ShapeMove) ||
567 (actInf.type == JunctionAdd) || (actInf.type == JunctionMove)))
568 {
569 // Not a move or add action, so don't do anything.
570 continue;
571 }
572
573 Obstacle *obstacle = actInf.obstacle();
574 ShapeRef *shape = actInf.shape();
575 JunctionRef *junction = actInf.junction();
576 Polygon& newPoly = actInf.newPoly;
577 bool isMove = (actInf.type == ShapeMove) ||
578 (actInf.type == JunctionMove);
579
580 unsigned int pid = obstacle->id();
581
582 // Restore this shape for visibility.
583 obstacle->makeActive();
584
585 if (isMove)
586 {
587 if (shape)
588 {
589 shape->setNewPoly(newPoly);
590 }
591 else
592 {
593 junction->setPosition(actInf.newPosition);
594 }
595 }
596 const Polygon& shapePoly = obstacle->routingPolygon();
597
598 adjustContainsWithAdd(shapePoly, pid);
599
601 {
602 // o Check all visibility edges to see if this one shape
603 // blocks them.
604 if (!isMove || notPartialTime)
605 {
606 newBlockingShape(shapePoly, pid);
607 }
608
609 // o Calculate visibility for the new vertices.
611 {
612 obstacle->computeVisibilitySweep();
613 }
614 else
615 {
616 obstacle->computeVisibilityNaive();
617 }
618 obstacle->updatePinPolyLineVisibility();
619 }
620 }
621
622 // Update connector endpoints.
623 for (curr = actionList.begin(); curr != finish; ++curr)
624 {
625 ActionInfo& actInf = *curr;
626 if (actInf.type != ConnChange)
627 {
628 continue;
629 }
630 for (ConnUpdateList::iterator conn = actInf.conns.begin();
631 conn != actInf.conns.end(); ++conn)
632 {
633 actInf.conn()->updateEndPoint(conn->first, conn->second);
634 }
635 }
636 // Clear the actionList.
637 actionList.clear();
638}
639
641{
642 // If SimpleRouting, then don't update here.
643 if ((actionList.empty() && (m_hyperedge_rerouter.count() == 0) &&
644 (m_settings_changes == false)) || SimpleRouting)
645 {
646 return false;
647 }
648 m_settings_changes = false;
649
651
654
655 return true;
656}
657
658
660{
661 // There shouldn't be remove events or move events for the same junction
662 // already in the action list.
663 // XXX: Possibly we could handle this by ordering them intelligently.
664 COLA_ASSERT(find(actionList.begin(), actionList.end(),
665 ActionInfo(JunctionRemove, junction)) == actionList.end());
666 COLA_ASSERT(find(actionList.begin(), actionList.end(),
667 ActionInfo(JunctionMove, junction)) == actionList.end());
668
669 ActionInfo addInfo(JunctionAdd, junction);
670
671 ActionInfoList::iterator found =
672 find(actionList.begin(), actionList.end(), addInfo);
673 if (found == actionList.end())
674 {
675 actionList.push_back(addInfo);
676 }
677
679 {
681 }
682}
683
684
686{
687 // There shouldn't be add events events for the same junction already
688 // in the action list.
689 // XXX: Possibly we could handle this by ordering them intelligently.
690 COLA_ASSERT(find(actionList.begin(), actionList.end(),
691 ActionInfo(JunctionAdd, junction)) == actionList.end());
692
693 // Delete any ShapeMove entries for this shape in the action list.
694 ActionInfoList::iterator found = find(actionList.begin(),
695 actionList.end(), ActionInfo(JunctionMove, junction));
696 if (found != actionList.end())
697 {
698 actionList.erase(found);
699 }
700
701 // Add the ShapeRemove entry.
702 ActionInfo remInfo(JunctionRemove, junction);
703 found = find(actionList.begin(), actionList.end(), remInfo);
704 if (found == actionList.end())
705 {
706 actionList.push_back(remInfo);
707 }
708
710 {
712 }
713}
714
715
716void Router::moveJunction(JunctionRef *junction, const double xDiff,
717 const double yDiff)
718{
719 ActionInfo moveInfo(JunctionMove, junction, Point());
720 ActionInfoList::iterator found =
721 find(actionList.begin(), actionList.end(), moveInfo);
722
723 Point newPosition;
724 if (found != actionList.end())
725 {
726 // The junction already has a queued move, so use that position.
727 newPosition = found->newPosition;
728 }
729 else
730 {
731 // Just use the existing position.
732 newPosition = junction->position();
733 }
734 newPosition.x += xDiff;
735 newPosition.y += yDiff;
736
737 moveJunction(junction, newPosition);
738}
739
740
741void Router::moveJunction(JunctionRef *junction, const Point& newPosition)
742{
743 // There shouldn't be remove events or add events for the same junction
744 // already in the action list.
745 // XXX: Possibly we could handle this by ordering them intelligently.
746 COLA_ASSERT(find(actionList.begin(), actionList.end(),
747 ActionInfo(JunctionRemove, junction)) == actionList.end());
748
749 ActionInfoList::iterator found = find(actionList.begin(),
750 actionList.end(), ActionInfo(JunctionAdd, junction));
751 if (found != actionList.end())
752 {
753 // The Add is enough, no need for the Move action too.
754 // The junction will be added with the new position.
755 found->junction()->setPosition(newPosition);
756 return;
757 }
758
759 ActionInfo moveInfo(JunctionMove, junction, newPosition);
760 // Sanely cope with the case where the user requests moving the same
761 // shape multiple times before rerouting connectors.
762 found = find(actionList.begin(), actionList.end(), moveInfo);
763
764 if (found != actionList.end())
765 {
766 // Just update the ActionInfo with the second position.
767 found->newPosition = newPosition;
768 }
769 else
770 {
771 actionList.push_back(moveInfo);
772 }
773
775 {
777 }
778}
779
781{
782 cluster->makeActive();
783
784 unsigned int pid = cluster->id();
785 ReferencingPolygon& poly = cluster->polygon();
786
787 adjustClustersWithAdd(poly, pid);
788}
789
790
792{
793 cluster->makeInactive();
794
795 unsigned int pid = cluster->id();
796
798}
799
800
801unsigned int Router::newObjectId(void) const
802{
803 return m_largest_assigned_id + 1;
804}
805
806
807unsigned int Router::assignId(const unsigned int suggestedId)
808{
809 // If the suggestedId is zero, then we assign the object the next
810 // smallest unassigned ID, otherwise we trust the ID given is unique.
811 unsigned int assignedId = (suggestedId == 0) ? newObjectId() : suggestedId;
812
813 // If assertions are enabled, then we check that this ID really is unique.
814 COLA_ASSERT(objectIdIsUnused(assignedId));
815
816 // Have the router record if this ID is larger than the largest assigned ID.
817 m_largest_assigned_id = std::max(m_largest_assigned_id, assignedId);
818
819 return assignedId;
820}
821
822
823 // Returns whether the given ID is unique among all objects known by the
824 // router. It is expected this is only going to be called from assertions
825 // while debugging, so efficiency is not an issue and we just iterate over
826 // all objects.
827bool Router::objectIdIsUnused(const unsigned int id) const
828{
829 // Examine shapes/junctions.
830 for (ObstacleList::const_iterator i = m_obstacles.begin();
831 i != m_obstacles.end(); ++i)
832 {
833 if ((*i)->id() == id)
834 {
835 return false;
836 }
837 }
838
839 // Examine connectors.
840 for (ConnRefList::const_iterator i = connRefs.begin();
841 i != connRefs.end(); ++i)
842 {
843 if ((*i)->id() == id)
844 {
845 return false;
846 }
847 }
848
849 // Examine clusters.
850 for (ClusterRefList::const_iterator i = clusterRefs.begin();
851 i != clusterRefs.end(); ++i)
852 {
853 if ((*i)->id() == id)
854 {
855 return false;
856 }
857 }
858
859 return true;
860}
861
862
863//----------------------------------------------------------------------------
864
865 // Returns a list of connector Ids of all the connectors of type
866 // 'type' attached to the shape with the ID 'shapeId'.
867void Router::attachedConns(IntList &conns, const unsigned int shapeId,
868 const unsigned int type)
869{
870 ConnRefList::const_iterator fin = connRefs.end();
871 for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
872 {
873 std::pair<Obstacle *, Obstacle *> anchors = (*i)->endpointAnchors();
874
875 if ((type & runningTo) &&
876 (anchors.second && (anchors.second->id() == shapeId)))
877 {
878 conns.push_back((*i)->id());
879 }
880 else if ((type & runningFrom) &&
881 (anchors.first && (anchors.first->id() == shapeId)))
882 {
883 conns.push_back((*i)->id());
884 }
885 }
886}
887
888
889 // Returns a list of shape Ids of all the shapes attached to the
890 // shape with the ID 'shapeId' with connection type 'type'.
891void Router::attachedShapes(IntList &shapes, const unsigned int shapeId,
892 const unsigned int type)
893{
894 ConnRefList::const_iterator fin = connRefs.end();
895 for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
896 {
897 std::pair<Obstacle *, Obstacle *> anchors = (*i)->endpointAnchors();
898
899 if ((type & runningTo) &&
900 (anchors.second && (anchors.second->id() == shapeId)))
901 {
902 if (anchors.first)
903 {
904 // Only if there is a shape attached to the other end.
905 shapes.push_back(anchors.first->id());
906 }
907 }
908 else if ((type & runningFrom) &&
909 (anchors.first && (anchors.first->id() == shapeId)))
910 {
911 if (anchors.second)
912 {
913 // Only if there is a shape attached to the other end.
914 shapes.push_back(anchors.second->id());
915 }
916 }
917 }
918}
919
920
921 // It's intended this function is called after visibility changes
922 // resulting from shape movement have happened. It will alert
923 // rerouted connectors (via a callback) that they need to be redrawn.
925{
926 ConnRefList reroutedConns;
927 ConnRefList::const_iterator fin = connRefs.end();
928
930
931 // Updating the orthogonal visibility graph if necessary.
933
934 for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
935 {
936 (*i)->freeActivePins();
937 }
938
939 // Calculate and return connectors that are part of hyperedges and will
940 // be completely rerouted by that code and thus don't need to have routes
941 // generated here.
942 ConnRefSet hyperedgeConns =
944
945 // TODO: It might be worth sorting connectors and routing them from
946 // smallest to largest estimated cost. This way we likely get
947 // better exclusive pin assignment during initial routing.
948
949 size_t totalConns = connRefs.size();
950 size_t numOfReroutedConns = 0;
951 for (ConnRefList::const_iterator i = connRefs.begin(); i != fin; ++i)
952 {
953 // Progress reporting and continuation check.
955 numOfReroutedConns, totalConns);
956 ++numOfReroutedConns;
957
958 ConnRef *connector = *i;
959 if (hyperedgeConns.find(connector) != hyperedgeConns.end())
960 {
961 // This will be rerouted by the hyperedge code, so do nothing.
962 continue;
963 }
964
965 if (connector->hasFixedRoute())
966 {
967 // We don't reroute connectors with fixed routes.
968 continue;
969 }
970
971 TIMER_START(this, tmOrthogRoute);
972 connector->m_needs_repaint = false;
973 bool rerouted = connector->generatePath();
974 if (rerouted)
975 {
976 reroutedConns.push_back(connector);
977 }
978 TIMER_STOP(this);
979 }
980
981
982 // Perform any complete hyperedge rerouting that has been requested.
984
985 // Find and reroute crossing connectors if crossing penalties are set.
987
988 bool withMinorImprovements = routingOption(
990 bool withMajorImprovements = routingOption(
992 if (withMinorImprovements || withMajorImprovements)
993 {
995 m_hyperedge_improver.execute(withMajorImprovements);
996 }
997
998 // Perform centring and nudging for orthogonal routes.
1000
1001 // Find a list of all the deleted connectors in hyperedges.
1002 HyperedgeNewAndDeletedObjectLists changedHyperedgeObjs =
1004 ConnRefList deletedConns = changedHyperedgeObjs.deletedConnectorList;
1005 for (size_t index = 0; index < m_hyperedge_rerouter.count(); ++index)
1006 {
1007 changedHyperedgeObjs =
1009 deletedConns.merge(changedHyperedgeObjs.deletedConnectorList);
1010 }
1011
1012 // Alert connectors that they need redrawing.
1013 fin = reroutedConns.end();
1014 for (ConnRefList::const_iterator i = reroutedConns.begin(); i != fin; ++i)
1015 {
1016 ConnRef *conn = *i;
1017
1018 // Skip hyperedge connectors which have been deleted.
1019 ConnRefList::iterator findIt = std::find(
1020 deletedConns.begin(), deletedConns.end(), conn);
1021 if (findIt != deletedConns.end())
1022 {
1023 // Connector deleted, skip.
1024 continue;
1025 }
1026
1027 conn->m_needs_repaint = true;
1028 conn->performCallback();
1029 }
1030
1031 // Progress reporting.
1033}
1034
1035// Type holding a cost estimate and ConnRef.
1036typedef std::pair<double, ConnRef *> ConnCostRef;
1037
1038// A comparison class used to order a set of ConnCostRefs.
1039class CmpConnCostRef
1040{
1041 public:
1042 CmpConnCostRef()
1043 {
1044 }
1045 bool operator() (const ConnCostRef& u, const ConnCostRef& v) const
1046 {
1047 return (u.second->id() < v.second->id());
1048 }
1049};
1050
1051typedef std::set<ConnCostRef, CmpConnCostRef> ConnCostRefSet;
1052typedef std::list<ConnCostRefSet> ConnCostRefSetList;
1053
1054
1055static double cheapEstimatedCost(ConnRef *lineRef)
1056{
1057 // We use an estimate of overall connector length, reduced by a count
1058 // of the number of segments. In the case of equal length, This causes
1059 // straight line connectors to be left alone and connectors with more
1060 // complex paths to be rerouted.
1061 bool isPolyLine = (lineRef->routingType() == ConnType_PolyLine);
1062 const PolyLine& route = lineRef->displayRoute();
1063 double length = 0;
1064
1065 for (size_t i = 1; i < route.size(); ++i)
1066 {
1067 const Point& a = route.ps[i - 1];
1068 const Point& b = route.ps[i];
1069
1070 double segmentLength = (isPolyLine) ?
1071 euclideanDist(a, b) : manhattanDist(a, b);
1072 length += segmentLength;
1073 }
1074 return length - (route.size() + 1);
1075}
1076
1077// A map of connectors to the set of connectors that cross them.
1078typedef std::map<ConnRef *, std::set<ConnRef *> > CrossingConnectorsMap;
1079
1080// A list of connector crossing maps that don't interact with each other.
1081typedef std::list<CrossingConnectorsMap> CrossingConnectorsMapList;
1082
1083// This class maintains the list of connector crossing maps and provides
1084// methods for adding crossing connector pairs and getting the minimal sets
1085// of connectors that can be removed from each group to prevent crossings.
1086class CrossingConnectorsInfo
1087{
1088 public:
1089
1090 // Add information that a pair of connectors cross.
1091 void addCrossing(ConnRef *conn1, ConnRef *conn2)
1092 {
1093 // Find the existing or new group that this crossing
1094 // should be recorded in.
1095 CrossingConnectorsMapList::iterator it =
1096 groupForCrossingConns(conn1, conn2);
1097 CrossingConnectorsMap& pairsSet = *it;
1098
1099 // Record the crossing.
1100 pairsSet[conn1].insert(conn2);
1101 pairsSet[conn2].insert(conn1);
1102 }
1103
1104 // This method builds and returns a list of non-interacting sets of
1105 // connectors (with crossing counts) that need to be removed so there
1106 // are no crossings. These are the connectors we reroute. Where
1107 // these connectors attach to exclusive connection pins, we return
1108 // and thus reroute all connectors attached to the exlcusive pins. We
1109 // do this so we are no so committed to possible bad pin assignemnts.
1110 ConnCostRefSetList crossingSetsListToRemoveCrossingsFromGroups(void)
1111 {
1112 // A list of the minimal sets of connectors that cause crossings
1113 // in all groups of crossingconnectors.
1114 ConnCostRefSetList crossingSetsList;
1115
1116 // For each group of crossing connectors.
1117 for (CrossingConnectorsMapList::iterator it = pairsSetList.begin();
1118 it != pairsSetList.end(); ++it)
1119 {
1120 // The minimal set of crossing-causing connectors.
1121 // We will build this.
1122 ConnCostRefSet crossingSet;
1123
1124 // Set of exclusive pins that the crossing-causing connectors
1125 // attach to.
1126 std::set<ConnectionPinIds> exclusivePins;
1127
1128 // The group of all crossing pairs.
1129 CrossingConnectorsMap& pairsSet = *it;
1130
1131 // For each crossing-causing connector.
1132 ConnCostRef crossingCausingConnector;
1133 while ( (crossingCausingConnector =
1134 removeConnectorWithMostCrossings(pairsSet)).second != nullptr )
1135 {
1136 // Add it to our crossing-causing set.
1137 crossingSet.insert(crossingCausingConnector);
1138
1139 // Determine if it attaches to any exclusive pins and
1140 // record these.
1141 std::pair<ConnEnd, ConnEnd> ends =
1142 crossingCausingConnector.second->endpointConnEnds();
1143 // Look at one end.
1144 ShapeConnectionPin *pin = ends.first.m_active_pin;
1145 if (pin && pin->isExclusive())
1146 {
1147 exclusivePins.insert(pin->ids());
1148 }
1149 // Look at other end.
1150 pin = ends.second.m_active_pin;
1151 if (pin && pin->isExclusive())
1152 {
1153 exclusivePins.insert(pin->ids());
1154 }
1155 }
1156
1157 // For each of the remaining connectors which are not
1158 // crossing-causing, add them to our crossing set if they
1159 // attach to one of the exclusive pin classes which the
1160 // crossing-causing connectors attached to.
1161 for (CrossingConnectorsMap::const_iterator it2 =
1162 pairsSet.begin(); it2 != pairsSet.end(); ++it2)
1163 {
1164 ConnRef *conn = it2->first;
1165 std::pair<ConnEnd, ConnEnd> ends = conn->endpointConnEnds();
1166 // Look at one end.
1167 ShapeConnectionPin *pin = ends.first.m_active_pin;
1168 if (pin && pin->isExclusive())
1169 {
1170 if (exclusivePins.count(pin->ids()) > 0)
1171 {
1172 // Attaches to an exclusive pin and it matches
1173 // one attached to by the crossing-causing
1174 // connectors. So add the conn to the
1175 // crossing set and continue..
1176 crossingSet.insert(std::make_pair(0, conn));
1177 continue;
1178 }
1179 }
1180 // Look at other end.
1181 pin = ends.second.m_active_pin;
1182 if (pin && pin->isExclusive())
1183 {
1184 if (exclusivePins.count(pin->ids()) > 0)
1185 {
1186 // Attaches to an exclusive pin and it matches
1187 // one attached to by the crossing-causing
1188 // connectors. So add the conn to the
1189 // crossing set.
1190 crossingSet.insert(std::make_pair(0, conn));
1191 }
1192 }
1193 }
1194
1195 if (!crossingSet.empty())
1196 {
1197 crossingSetsList.push_back(crossingSet);
1198 }
1199 }
1200 return crossingSetsList;
1201 }
1202
1203 // Returns whether this pair of connector is already known to cross.
1204 bool connsKnownToCross(ConnRef *conn1, ConnRef *conn2)
1205 {
1206 CrossingConnectorsMapList::iterator it1 = groupForConn(conn1);
1207 CrossingConnectorsMapList::iterator it2 = groupForConn(conn2);
1208
1209 // The connectors cross if
1210 if ((it1 == it2) && (it1 != pairsSetList.end()))
1211 {
1212 // They are in the same group and conn2 is in crossing
1213 // connectors set of conn1.
1214 CrossingConnectorsMap& pairsSet = *it1;
1215 return ((pairsSet.count(conn1) > 0) &&
1216 (pairsSet[conn1].count(conn2) > 0));
1217 }
1218 return false;
1219 }
1220
1221 private:
1222
1223 // Given a particular group (pairsSet), removes the connector with
1224 // the most crossings withing the group and returns it along with its
1225 // crossing count.
1226 ConnCostRef removeConnectorWithMostCrossings(
1227 CrossingConnectorsMap& pairsSet)
1228 {
1229 // Tracking of the greatest number of crossings.
1230 ConnRef *candidateConnector = nullptr;
1231 size_t candidateCrossingCount = 0;
1232 double candidateEstimatedCost = 0;
1233
1234 // For each connector in the group...
1235 for (CrossingConnectorsMap::const_iterator it = pairsSet.begin();
1236 it != pairsSet.end(); ++it)
1237 {
1238 // ... check if it has any crossings.
1239 size_t crossings = it->second.size();
1240 if (crossings == 0)
1241 {
1242 // If not, skip.
1243 continue;
1244 }
1245
1246 // It has crossings. Determine if it has the most crossings
1247 // of the connectors we've seen, or if it is tied but has
1248 // a greater estimated cost. If so, it is our new candidate.
1249 double cost = cheapEstimatedCost(it->first);
1250 if ((crossings > candidateCrossingCount) ||
1251 ((crossings == candidateCrossingCount) &&
1252 (cost > candidateEstimatedCost)))
1253 {
1254 // Update with new candidate.
1255 candidateConnector = it->first;
1256 candidateCrossingCount = crossings;
1257 candidateEstimatedCost = cost;
1258 }
1259 }
1260
1261 if (candidateConnector == nullptr)
1262 {
1263 // If no candidate, return nullptr connector.
1264 return std::make_pair(0, (ConnRef *) nullptr);
1265 }
1266
1267 // Remove the candidate from the group. To do this we find the
1268 // set of all connectors it crosses.
1269 std::set<ConnRef *>& connSet = pairsSet[candidateConnector];
1270 // For each of these
1271 for (std::set<ConnRef *>::const_iterator it = connSet.begin();
1272 it != connSet.end(); ++it)
1273 {
1274 // we remove the candidate from their crossing lists
1275 pairsSet[*it].erase(candidateConnector);
1276 }
1277 // And then clear the crossing list for the candidate itself.
1278 connSet.clear();
1279
1280 // Return the candidate connector and its original crossing count.
1281 return std::make_pair(static_cast<double>(candidateCrossingCount), candidateConnector);
1282 }
1283
1284 // Returns the iterator to the group that the given conn is in,
1285 // if it is part of any crossing group.
1286 CrossingConnectorsMapList::iterator groupForConn(ConnRef *conn)
1287 {
1288 // For each group...
1289 for (CrossingConnectorsMapList::iterator it = pairsSetList.begin();
1290 it != pairsSetList.end(); ++it)
1291 {
1292 // ... check if the connector is in it.
1293 if (it->count(conn) > 0)
1294 {
1295 // If so, return it.
1296 return it;
1297 }
1298 }
1299 // Connector was not in any existing group.
1300 return pairsSetList.end();
1301 }
1302
1303 // Given a pair of crossing connectors, returns an iterator to the
1304 // crossing connector group they are part of. If neither are part
1305 // of any group, one is created and returned. If one connector is
1306 // part of an exisitng group, that group is returned. If they are
1307 // part of two different groups, the groups are merged and the
1308 // merged group is returned.
1309 CrossingConnectorsMapList::iterator groupForCrossingConns(
1310 ConnRef *conn1, ConnRef *conn2)
1311 {
1312 CrossingConnectorsMapList::iterator it1 = groupForConn(conn1);
1313 CrossingConnectorsMapList::iterator it2 = groupForConn(conn2);
1314
1315 // groupIt will be the iterator to the appropriate group.
1316 CrossingConnectorsMapList::iterator groupIt = pairsSetList.end();
1317
1318 if ((it1 == pairsSetList.end()) && (it2 == pairsSetList.end()))
1319 {
1320 // Neither are part of a group. Create one.
1321 CrossingConnectorsMap newSet;
1322 groupIt = pairsSetList.insert(pairsSetList.end(), newSet);
1323 }
1324 else if ((it1 != pairsSetList.end()) && (it2 == pairsSetList.end()))
1325 {
1326 // it1 is the appropriate existing group.
1327 groupIt = it1;
1328 }
1329 else if ((it1 == pairsSetList.end()) && (it2 != pairsSetList.end()))
1330 {
1331 // it2 is the appropriate exisitng group.
1332 groupIt = it2;
1333 }
1334 else if (it1 != it2)
1335 {
1336 // There are two different existing groups, so merge them.
1337 COLA_ASSERT(it1 != pairsSetList.end());
1338 COLA_ASSERT(it2 != pairsSetList.end());
1339 it1->insert(it2->begin(), it2->end());
1340 pairsSetList.erase(it2);
1341 groupIt = it1;
1342 }
1343 else
1344 {
1345 // These are already part of the same group. Do nothing.
1346 COLA_ASSERT(it1 == it2);
1347 groupIt = it1;
1348 }
1349 return groupIt;
1350 }
1351
1352 CrossingConnectorsMapList pairsSetList;
1353};
1354
1355
1356void Router::performContinuationCheck(unsigned int phaseNumber,
1357 size_t stepNumber, size_t totalSteps)
1358{
1359 // Compute the elapsed time in msec since the beginning of the transaction.
1360 unsigned int elapsedMsec = (unsigned int)
1361 ((clock() - m_transaction_start_time) /
1362 (CLOCKS_PER_SEC / (double) 1000));
1363
1364 bool shouldContinue = shouldContinueTransactionWithProgress(elapsedMsec,
1365 phaseNumber, TransactionPhaseCompleted,
1366 stepNumber / (double)totalSteps);
1367 if (shouldContinue == false)
1368 {
1369 // Host program has asked us not to continue the transaction.
1370 m_abort_transaction = true;
1371 }
1372}
1373
1374
1376 unsigned int phaseNumber, unsigned int totalPhases,
1377 double proportion)
1378{
1379 COLA_UNUSED(elapsedTime);
1380 COLA_UNUSED(phaseNumber);
1381 COLA_UNUSED(totalPhases);
1382 COLA_UNUSED(proportion);
1383
1384#if 0
1385 printf("Progress: %8u, phase %u of %u... %.2f%%\n", elapsedTime,
1386 phaseNumber, totalPhases, proportion * 100);
1387#endif
1388
1389 // We always continue. Subclasses can override this behaviour.
1390 return true;
1391}
1392
1393
1394class CmpOrderedConnCostRef
1395{
1396 public:
1397 CmpOrderedConnCostRef()
1398 {
1399 }
1400 bool operator() (const ConnCostRef& u, const ConnCostRef& v) const
1401 {
1402 return (u.first < v.first);
1403 }
1404};
1405typedef std::list<ConnCostRef> ConnCostRefList;
1406
1407
1409{
1410 const double crossing_penalty = routingParameter(crossingPenalty);
1411 const double shared_path_penalty = routingParameter(fixedSharedPathPenalty);
1412 if ((crossing_penalty == 0) && (shared_path_penalty == 0))
1413 {
1414 // No penalties, return.
1415 return;
1416 }
1417
1418 // Information on crossing connector groups.
1419 CrossingConnectorsInfo crossingConnInfo;
1420
1421 size_t numOfConns = connRefs.size();
1422 size_t numOfConnsChecked = 0;
1423
1424 // Find crossings and reroute connectors.
1426 ConnRefList::iterator fin = connRefs.end();
1427 for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
1428 {
1429 // Progress reporting and continuation check.
1430 ++numOfConnsChecked;
1432 numOfConnsChecked, numOfConns);
1434 {
1436 return;
1437 }
1438
1439 Avoid::Polygon& iRoute = (*i)->routeRef();
1440 if (iRoute.size() == 0)
1441 {
1442 // Rerouted hyperedges will have an empty route.
1443 // We can't reroute these.
1444 continue;
1445 }
1446 ConnRefList::iterator j = i;
1447 for (++j; j != fin; ++j)
1448 {
1449 if (crossingConnInfo.connsKnownToCross(*i, *j))
1450 {
1451 // We already know both these have crossings.
1452 continue;
1453 }
1454
1455 // Determine if this pair cross.
1456 Avoid::Polygon& jRoute = (*j)->routeRef();
1457 ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
1458 for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
1459 {
1460 const bool finalSegment = ((jInd + 1) == jRoute.size());
1461 cross.countForSegment(jInd, finalSegment);
1462
1463 if ((shared_path_penalty > 0) &&
1464 (cross.crossingFlags & CROSSING_SHARES_PATH) &&
1465 (cross.crossingFlags & CROSSING_SHARES_FIXED_SEGMENT) &&
1467 !(cross.crossingFlags & CROSSING_SHARES_PATH_AT_END)))
1468 {
1469 // We are penalising fixedSharedPaths and there is a
1470 // fixedSharedPath.
1471 crossingConnInfo.addCrossing(*i, *j);
1472 break;
1473 }
1474 else if ((crossing_penalty > 0) && (cross.crossingCount > 0))
1475 {
1476 // We are penalising crossings and this is a crossing.
1477 crossingConnInfo.addCrossing(*i, *j);
1478 break;
1479 }
1480 }
1481 }
1482 }
1483
1484 // Find the list of connector sets that need to be removed to avoid any
1485 // crossings in all crossing groups. This is our candidate set for
1486 // rerouting. Where these connectors connect to exlusive pins, all
1487 // connectors attached to exclusive pins with the same IDs will also
1488 // be rerouted, starting with the shortest.
1489 ConnCostRefSetList crossingConnsGroups =
1490 crossingConnInfo.crossingSetsListToRemoveCrossingsFromGroups();
1491
1492 // At this point we have a list containing crossings for rerouting.
1493 // We do this rerouting via two passes, for each group of interacting
1494 // crossing connectors:
1495 // 1) clear existing routes and free pin assignments, and
1496 // 2) compute new routes.
1497 unsigned int numOfConnsToReroute = 1;
1498 unsigned int numOfConnsRerouted = 1;
1499 for (ConnCostRefSetList::iterator setIt = crossingConnsGroups.begin();
1500 setIt != crossingConnsGroups.end(); ++setIt)
1501 {
1502 // Sort the connectors we will be rerouting from lowest to
1503 // highest cost.
1504 ConnCostRefList orderedConnList(setIt->begin(), setIt->end());
1505 orderedConnList.sort(CmpOrderedConnCostRef());
1506
1507 // Perform rerouting of this set of connectors.
1508 for (int pass = 0; pass < 2; ++pass)
1509 {
1510 for (ConnCostRefList::iterator connIt = orderedConnList.begin();
1511 connIt != orderedConnList.end(); ++connIt)
1512 {
1513 ConnRef *conn = connIt->second;
1514 if (pass == 0)
1515 {
1516 ++numOfConnsToReroute;
1517
1518 // Mark the fixed shared path as being invalid.
1519 conn->makePathInvalid();
1520
1521 // Free the previous path, so it is not noticed by other
1522 // connectors during rerouting.
1523 conn->freeRoutes();
1524
1525 // Free pin assignments.
1526 conn->freeActivePins();
1527 }
1528 else if (pass == 1)
1529 {
1530 // Progress reporting and continuation check.
1532 numOfConnsRerouted, numOfConnsToReroute);
1534 {
1536 return;
1537 }
1538 ++numOfConnsRerouted;
1539
1540 // Recompute this path.
1541 conn->generatePath();
1542 }
1543 }
1544 }
1545 }
1547}
1548
1549
1550void Router::newBlockingShape(const Polygon& poly, int pid)
1551{
1552 // o Check all visibility edges to see if this one shape
1553 // blocks them.
1554 EdgeInf *finish = visGraph.end();
1555 for (EdgeInf *iter = visGraph.begin(); iter != finish ; )
1556 {
1557 EdgeInf *tmp = iter;
1558 iter = iter->lstNext;
1559
1560 if (tmp->getDist() != 0)
1561 {
1562 std::pair<VertID, VertID> ids(tmp->ids());
1563 VertID eID1 = ids.first;
1564 VertID eID2 = ids.second;
1565 std::pair<Point, Point> points(tmp->points());
1566 Point e1 = points.first;
1567 Point e2 = points.second;
1568 bool blocked = false;
1569
1570 bool countBorder = false;
1571 bool ep_in_poly1 = (eID1.isConnPt()) ?
1572 inPoly(poly, e1, countBorder) : false;
1573 bool ep_in_poly2 = (eID2.isConnPt()) ?
1574 inPoly(poly, e2, countBorder) : false;
1575 if (ep_in_poly1 || ep_in_poly2)
1576 {
1577 // Don't check edges that have a connector endpoint
1578 // and are inside the shape being added.
1579 continue;
1580 }
1581
1582 bool seenIntersectionAtEndpoint = false;
1583 for (size_t pt_i = 0; pt_i < poly.size(); ++pt_i)
1584 {
1585 size_t pt_n = (pt_i == (poly.size() - 1)) ? 0 : pt_i + 1;
1586 const Point& pi = poly.ps[pt_i];
1587 const Point& pn = poly.ps[pt_n];
1588 if (segmentShapeIntersect(e1, e2, pi, pn,
1589 seenIntersectionAtEndpoint))
1590 {
1591 blocked = true;
1592 break;
1593 }
1594 }
1595 if (blocked)
1596 {
1597 db_printf("\tRemoving newly blocked edge (by shape %3d)"
1598 "... \n\t\t", pid);
1599 tmp->alertConns();
1600 tmp->db_print();
1601 if (InvisibilityGrph)
1602 {
1603 tmp->addBlocker(pid);
1604 }
1605 else
1606 {
1607 delete tmp;
1608 }
1609 }
1610 }
1611 }
1612}
1613
1614
1616{
1617 COLA_ASSERT(InvisibilityGrph);
1618
1619 for (EdgeInf *iter = invisGraph.begin(); iter != invisGraph.end() ; )
1620 {
1621 EdgeInf *tmp = iter;
1622 iter = iter->lstNext;
1623
1624 if (tmp->blocker() == -1)
1625 {
1626 tmp->alertConns();
1627 tmp->checkVis();
1628 }
1629 else if (tmp->blocker() == pid)
1630 {
1631 tmp->checkVis();
1632 }
1633 }
1634}
1635
1636
1638{
1639 COLA_ASSERT(!InvisibilityGrph);
1640
1641 VertInf *first = vertices.connsBegin();
1642
1643 VertInf *pend = vertices.end();
1644 for (VertInf *i = first; i != pend; i = i->lstNext)
1645 {
1646 VertID iID = i->id;
1647
1648 // Check remaining, earlier vertices
1649 for (VertInf *j = first ; j != i; j = j->lstNext)
1650 {
1651 VertID jID = j->id;
1652 if (iID.isConnPt() && !iID.isConnectionPin() &&
1653 (iID.objID != jID.objID))
1654 {
1655 // Don't keep visibility between edges of different conns
1656 continue;
1657 }
1658
1659 // See if the edge is already there?
1660 bool found = (EdgeInf::existingEdge(i, j) != nullptr);
1661
1662 if (!found)
1663 {
1664 // Didn't already exist, check.
1665 bool knownNew = true;
1666 EdgeInf::checkEdgeVisibility(i, j, knownNew);
1667 }
1668 }
1669 }
1670}
1671
1672
1674{
1675 contains[pt->id].clear();
1676 enclosingClusters[pt->id].clear();
1677
1678 // Don't count points on the border as being inside.
1679 bool countBorder = false;
1680
1681 // Compute enclosing shapes.
1682 ObstacleList::const_iterator finish = m_obstacles.end();
1683 for (ObstacleList::const_iterator i = m_obstacles.begin(); i != finish; ++i)
1684 {
1685 if (inPoly((*i)->routingPolygon(), pt->point, countBorder))
1686 {
1687 contains[pt->id].insert((*i)->id());
1688 }
1689 }
1690
1691 // Computer enclosing Clusters
1692 ClusterRefList::const_iterator clFinish = clusterRefs.end();
1693 for (ClusterRefList::const_iterator i = clusterRefs.begin();
1694 i != clFinish; ++i)
1695 {
1696 if (inPolyGen((*i)->polygon(), pt->point))
1697 {
1698 enclosingClusters[pt->id].insert((*i)->id());
1699 }
1700 }
1701}
1702
1703
1705 const int p_cluster)
1706{
1707 for (VertInf *k = vertices.connsBegin(); k != vertices.shapesBegin();
1708 k = k->lstNext)
1709 {
1710 if (inPolyGen(poly, k->point))
1711 {
1712 enclosingClusters[k->id].insert(p_cluster);
1713 }
1714 }
1715}
1716
1717
1718void Router::adjustClustersWithDel(const int p_cluster)
1719{
1720 for (ContainsMap::iterator k = enclosingClusters.begin();
1721 k != enclosingClusters.end(); ++k)
1722 {
1723 (*k).second.erase(p_cluster);
1724 }
1725}
1726
1727
1728void Router::adjustContainsWithAdd(const Polygon& poly, const int p_shape)
1729{
1730 // Don't count points on the border as being inside.
1731 bool countBorder = false;
1732
1733 for (VertInf *k = vertices.connsBegin(); k != vertices.shapesBegin();
1734 k = k->lstNext)
1735 {
1736 if (inPoly(poly, k->point, countBorder))
1737 {
1738 contains[k->id].insert(p_shape);
1739 }
1740 }
1741}
1742
1743
1744void Router::adjustContainsWithDel(const int p_shape)
1745{
1746 for (ContainsMap::iterator k = contains.begin(); k != contains.end(); ++k)
1747 {
1748 (*k).second.erase(p_shape);
1749 }
1750}
1751
1752
1753#ifdef SELECTIVE_DEBUG
1754static double AngleAFromThreeSides(const double a, const double b,
1755 const double c)
1756{
1757 // returns angle A, the angle opposite from side a, in radians
1758 return acos((pow(b, 2) + pow(c, 2) - pow(a, 2)) / (2 * b * c));
1759}
1760#endif
1761
1762// Given an deleted obstacle, uses a simple heauristic to determine polyline
1763// connectors that may now have a better path through the region occupied by
1764// the shape and mark them as needing to be rerouted.
1765// See the "Incremental Connector Routing" paper which explains this code.
1766//
1768 Obstacle *obstacle)
1769{
1771 {
1772 // When rubber-band routing, we do not reroute connectors that
1773 // may have a better route, only invalid connectors.
1774 return;
1775 }
1776
1777 COLA_ASSERT(SelectiveReroute);
1778
1779 // For each connector...
1780 ConnRefList::const_iterator end = connRefs.end();
1781 for (ConnRefList::const_iterator it = connRefs.begin(); it != end; ++it)
1782 {
1783 ConnRef *conn = (*it);
1784
1785 if (conn->m_route.empty())
1786 {
1787 // Ignore uninitialised connectors.
1788 continue;
1789 }
1790 else if (conn->m_needs_reroute_flag)
1791 {
1792 // Already marked, so skip.
1793 continue;
1794 }
1795 else if (conn->routingType() != ConnType_PolyLine)
1796 {
1797 // This test only works for polyline connectors, so skip others.
1798 continue;
1799 }
1800
1801 Point start = conn->m_route.ps[0];
1802 Point end = conn->m_route.ps[conn->m_route.size() - 1];
1803
1804 double conndist = conn->m_route_dist;
1805
1806 double estdist;
1807 double e1, e2;
1808
1809 // For each vertex of the obstacle...
1810 VertInf *beginV = obstacle->firstVert();
1811 VertInf *endV = obstacle->lastVert()->lstNext;
1812 for (VertInf *i = beginV; i != endV; i = i->lstNext)
1813 {
1814 const Point& p1 = i->point;
1815 const Point& p2 = i->shNext->point;
1816
1817 double offy;
1818 double a;
1819 double b;
1820 double c;
1821 double d;
1822
1823 double min;
1824 double max;
1825
1826 if (p1.y == p2.y)
1827 {
1828 // Standard case
1829 offy = p1.y;
1830 a = start.x;
1831 b = start.y - offy;
1832 c = end.x;
1833 d = end.y - offy;
1834
1835 min = std::min(p1.x, p2.x);
1836 max = std::max(p1.x, p2.x);
1837 }
1838 else if (p1.x == p2.x)
1839 {
1840 // Other Standard case
1841 offy = p1.x;
1842 a = start.y;
1843 b = start.x - offy;
1844 c = end.y;
1845 d = end.x - offy;
1846
1847 min = std::min(p1.y, p2.y);
1848 max = std::max(p1.y, p2.y);
1849 }
1850 else
1851 {
1852 // Need to do rotation
1853 Point n_p2(p2.x - p1.x, p2.y - p1.y);
1854 Point n_start(start.x - p1.x, start.y - p1.y);
1855 Point n_end(end.x - p1.x, end.y - p1.y);
1856 //db_printf("n_p2: (%.1f, %.1f)\n", n_p2.x, n_p2.y);
1857 //db_printf("n_start: (%.1f, %.1f)\n", n_start.x, n_start.y);
1858 //db_printf("n_end: (%.1f, %.1f)\n", n_end.x, n_end.y);
1859
1860 double theta = 0 - atan2(n_p2.y, n_p2.x);
1861 //db_printf("theta = %.2f\n", theta * (180 / PI));
1862
1863 Point r_p1(0, 0);
1864 Point r_p2 = n_p2;
1865 start = n_start;
1866 end = n_end;
1867
1868 double cosv = cos(theta);
1869 double sinv = sin(theta);
1870
1871 r_p2.x = cosv * n_p2.x - sinv * n_p2.y;
1872 r_p2.y = cosv * n_p2.y + sinv * n_p2.x;
1873 start.x = cosv * n_start.x - sinv * n_start.y;
1874 start.y = cosv * n_start.y + sinv * n_start.x;
1875 end.x = cosv * n_end.x - sinv * n_end.y;
1876 end.y = cosv * n_end.y + sinv * n_end.x;
1877 //db_printf("r_p2: (%.1f, %.1f)\n", r_p2.x, r_p2.y);
1878 //db_printf("r_start: (%.1f, %.1f)\n", start.x, start.y);
1879 //db_printf("r_end: (%.1f, %.1f)\n", end.x, end.y);
1880
1881 // This might be slightly off.
1882 if (fabs(r_p2.y) > 0.0001)
1883 {
1884 db_printf("r_p2.y: %f != 0\n", r_p2.y);
1885 }
1886 r_p2.y = 0;
1887
1888 offy = r_p1.y;
1889 a = start.x;
1890 b = start.y - offy;
1891 c = end.x;
1892 d = end.y - offy;
1893
1894 min = std::min(r_p1.x, r_p2.x);
1895 max = std::max(r_p1.x, r_p2.x);
1896
1897 }
1898
1899 double x;
1900 if ((b + d) == 0)
1901 {
1902 db_printf("WARNING: (b + d) == 0\n");
1903 d = d * -1;
1904 }
1905
1906 if ((b == 0) && (d == 0))
1907 {
1908 db_printf("WARNING: b == d == 0\n");
1909 if (((a < min) && (c < min)) ||
1910 ((a > max) && (c > max)))
1911 {
1912 // It's going to get adjusted.
1913 x = a;
1914 }
1915 else
1916 {
1917 continue;
1918 }
1919 }
1920 else
1921 {
1922 x = ((b*c) + (a*d)) / (b + d);
1923 }
1924
1925 //db_printf("%.1f, %.1f, %.1f, %.1f\n", a, b, c, d);
1926 //db_printf("x = %.1f\n", x);
1927
1928 x = std::max(min, x);
1929 x = std::min(max, x);
1930
1931 //db_printf("x = %.1f\n", x);
1932
1933 Point xp;
1934 if (p1.x == p2.x)
1935 {
1936 xp.x = offy;
1937 xp.y = x;
1938 }
1939 else
1940 {
1941 xp.x = x;
1942 xp.y = offy;
1943 }
1944 //db_printf("(%.1f, %.1f)\n", xp.x, xp.y);
1945
1946 e1 = euclideanDist(start, xp);
1947 e2 = euclideanDist(xp, end);
1948 estdist = e1 + e2;
1949
1950
1951 //db_printf("is %.1f < %.1f\n", estdist, conndist);
1952 if (estdist < conndist)
1953 {
1954#ifdef SELECTIVE_DEBUG
1955 //double angle = AngleAFromThreeSides(dist(start, end),
1956 // e1, e2);
1957 db_printf("[%3d] - Possible better path found (%.1f < %.1f)\n",
1958 conn->_id, estdist, conndist);
1959#endif
1960 conn->m_needs_reroute_flag = true;
1961 break;
1962 }
1963
1964 }
1965 }
1966}
1967
1968
1970{
1971 if (select != ConnType_None)
1972 {
1974 {
1975 return ConnType_Orthogonal;
1976 }
1977 else if ((select == ConnType_PolyLine) && m_allows_polyline_routing)
1978 {
1979 return ConnType_PolyLine;
1980 }
1981 }
1982
1984 {
1985 return ConnType_PolyLine;
1986 }
1988 {
1989 return ConnType_Orthogonal;
1990 }
1991 return ConnType_None;
1992}
1993
1994
1996 const double value)
1997{
1998 COLA_ASSERT(parameter < lastRoutingParameterMarker);
1999 if (value < 0)
2000 {
2001 // Set some sensible parameter value for the parameter being 'active'.
2002 switch (parameter)
2003 {
2004 case segmentPenalty:
2005 m_routing_parameters[parameter] = 50;
2006 break;
2008 m_routing_parameters[parameter] = 110;
2009 break;
2010 case anglePenalty:
2011 m_routing_parameters[parameter] = 50;
2012 break;
2013 case crossingPenalty:
2014 m_routing_parameters[parameter] = 200;
2015 break;
2017 m_routing_parameters[parameter] = 4000;
2018 break;
2020 m_routing_parameters[parameter] = 4.0;
2021 break;
2023 m_routing_parameters[parameter] = 100;
2024 break;
2025 default:
2026 m_routing_parameters[parameter] = 50;
2027 break;
2028 }
2029 }
2030 else
2031 {
2032 m_routing_parameters[parameter] = value;
2033 }
2034 m_settings_changes = true;
2035}
2036
2037
2038double Router::routingParameter(const RoutingParameter parameter) const
2039{
2040 COLA_ASSERT(parameter < lastRoutingParameterMarker);
2041 return m_routing_parameters[parameter];
2042}
2043
2044
2045void Router::setRoutingOption(const RoutingOption option, const bool value)
2046{
2047 COLA_ASSERT(option < lastRoutingOptionMarker);
2048 m_routing_options[option] = value;
2049 m_settings_changes = true;
2050}
2051
2052
2054{
2055 COLA_ASSERT(option < lastRoutingOptionMarker);
2056 return m_routing_options[option];
2057}
2058
2059
2061 const double penValue)
2062{
2063 setRoutingParameter(penType, penValue);
2064}
2065
2067{
2068 m_settings_changes = true;
2069}
2070
2075
2076
2081
2082
2084{
2085 FILE *fp = stdout;
2086 fprintf(fp, "\nVisibility Graph info:\n");
2087 fprintf(fp, "----------------------\n");
2088
2089 unsigned int currshape = 0;
2090 int st_shapes = 0;
2091 int st_vertices = 0;
2092 int st_endpoints = 0;
2093 int st_valid_shape_visedges = 0;
2094 int st_valid_endpt_visedges = 0;
2095 int st_orthogonal_visedges = 0;
2096 int st_invalid_visedges = 0;
2097 VertInf *finish = vertices.end();
2098 for (VertInf *t = vertices.connsBegin(); t != finish; t = t->lstNext)
2099 {
2100 VertID pID = t->id;
2101
2102 if (!(pID.isConnPt()) && (pID.objID != currshape))
2103 {
2104 currshape = pID.objID;
2105 st_shapes++;
2106 }
2107 if (!(pID.isConnPt()))
2108 {
2109 st_vertices++;
2110 }
2111 else
2112 {
2113 // The shape 0 ones are temporary and not considered.
2114 st_endpoints++;
2115 }
2116 }
2117 for (EdgeInf *t = visGraph.begin(); t != visGraph.end();
2118 t = t->lstNext)
2119 {
2120 std::pair<VertID, VertID> idpair = t->ids();
2121
2122 if (idpair.first.isConnPt() || idpair.second.isConnPt())
2123 {
2124 st_valid_endpt_visedges++;
2125 }
2126 else
2127 {
2128 st_valid_shape_visedges++;
2129 }
2130 }
2131 for (EdgeInf *t = invisGraph.begin(); t != invisGraph.end();
2132 t = t->lstNext)
2133 {
2134 st_invalid_visedges++;
2135 }
2136 for (EdgeInf *t = visOrthogGraph.begin(); t != visOrthogGraph.end();
2137 t = t->lstNext)
2138 {
2139 st_orthogonal_visedges++;
2140 }
2141 fprintf(fp, "Number of shapes: %d\n", st_shapes);
2142 fprintf(fp, "Number of vertices: %d (%d real, %d endpoints)\n",
2143 st_vertices + st_endpoints, st_vertices, st_endpoints);
2144 fprintf(fp, "Number of orthog_vis_edges: %d\n", st_orthogonal_visedges);
2145 fprintf(fp, "Number of vis_edges: %d (%d valid [%d normal, %d endpt], "
2146 "%d invalid)\n", st_valid_shape_visedges + st_invalid_visedges +
2147 st_valid_endpt_visedges, st_valid_shape_visedges +
2148 st_valid_endpt_visedges, st_valid_shape_visedges,
2149 st_valid_endpt_visedges, st_invalid_visedges);
2150 fprintf(fp, "----------------------\n");
2151 fprintf(fp, "checkVisEdge tally: %d\n", st_checked_edges);
2152 fprintf(fp, "----------------------\n");
2153
2154#ifdef AVOID_PROFILE
2155 timers.printAll(fp);
2156 timers.reset();
2157#endif
2158}
2159
2160
2161static const double LIMIT = 100000000;
2162
2163static void reduceRange(double& val)
2164{
2165 val = std::min(val, LIMIT);
2166 val = std::max(val, -LIMIT);
2167}
2168
2169
2170//=============================================================================
2171// The following methods are for testing and debugging.
2172
2173
2175{
2176 ConnRefList::iterator fin = connRefs.end();
2177 for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
2178 {
2179 Avoid::Polygon iRoute = (*i)->displayRoute();
2180 ConnRefList::iterator j = i;
2181 for (++j; j != fin; ++j)
2182 {
2183 // Determine if this pair overlap
2184 Avoid::Polygon jRoute = (*j)->displayRoute();
2185 ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
2186 cross.checkForBranchingSegments = true;
2187 for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
2188 {
2189 const bool finalSegment = ((jInd + 1) == jRoute.size());
2190 cross.countForSegment(jInd, finalSegment);
2191
2192 if ((cross.crossingFlags & CROSSING_SHARES_PATH) &&
2193 (atEnds ||
2194 !(cross.crossingFlags & CROSSING_SHARES_PATH_AT_END)))
2195 {
2196 // We are looking for fixedSharedPaths and there is a
2197 // fixedSharedPath.
2198 return true;
2199 }
2200 }
2201 }
2202 }
2203 return false;
2204}
2205
2206
2208{
2209 ConnRefList::iterator fin = connRefs.end();
2210 for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
2211 {
2212 Avoid::Polygon iRoute = (*i)->displayRoute();
2213 ConnRefList::iterator j = i;
2214 for (++j; j != fin; ++j)
2215 {
2216 // Determine if this pair overlap
2217 Avoid::Polygon jRoute = (*j)->displayRoute();
2218 ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
2219 cross.checkForBranchingSegments = true;
2220 for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
2221 {
2222 const bool finalSegment = ((jInd + 1) == jRoute.size());
2223 cross.countForSegment(jInd, finalSegment);
2224
2225 if ((cross.crossingFlags & CROSSING_SHARES_PATH) &&
2226 (cross.crossingFlags & CROSSING_SHARES_FIXED_SEGMENT) &&
2227 (atEnds ||
2228 !(cross.crossingFlags & CROSSING_SHARES_PATH_AT_END)))
2229 {
2230 // We are looking for fixedSharedPaths and there is a
2231 // fixedSharedPath.
2232 return true;
2233 }
2234 }
2235 }
2236 }
2237 return false;
2238}
2239
2240
2242{
2243 ConnRefList::iterator fin = connRefs.end();
2244 for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
2245 {
2246 Avoid::Polygon iRoute = (*i)->displayRoute();
2247 ConnRefList::iterator j = i;
2248 for (++j; j != fin; ++j)
2249 {
2250 // Determine if this pair overlap
2251 Avoid::Polygon jRoute = (*j)->displayRoute();
2252 ConnectorCrossings cross(iRoute, true, jRoute, *i, *j);
2253 cross.checkForBranchingSegments = true;
2254 for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
2255 {
2256 const bool finalSegment = ((jInd + 1) == jRoute.size());
2257 cross.countForSegment(jInd, finalSegment);
2258
2259 if (cross.crossingFlags & CROSSING_TOUCHES)
2260 {
2261 return true;
2262 }
2263 }
2264 }
2265 }
2266 return false;
2267}
2268
2269
2270// Counts the number of crossings between all connectors.
2271//
2272// If optimisedForConnectorType is set, This will only count crossings
2273// between orthogonal connectors if they appear at segment endpoints.
2274// Thus, it can be used on raw connectors but not on simplified orthogonal
2275// connectors.
2276//
2277int Router::existsCrossings(const bool optimisedForConnectorType)
2278{
2279 int count = 0;
2280 ConnRefList::iterator fin = connRefs.end();
2281 for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
2282 {
2283 Avoid::Polygon iRoute = (*i)->displayRoute();
2284 ConnRefList::iterator j = i;
2285 for (++j; j != fin; ++j)
2286 {
2287 // Determine if this pair overlap
2288 Avoid::Polygon jRoute = (*j)->displayRoute();
2289 ConnRef *iConn = (optimisedForConnectorType) ? *i : nullptr;
2290 ConnRef *jConn = (optimisedForConnectorType) ? *j : nullptr;
2291 ConnectorCrossings cross(iRoute, true, jRoute, iConn, jConn);
2292 cross.checkForBranchingSegments = true;
2293 for (size_t jInd = 1; jInd < jRoute.size(); ++jInd)
2294 {
2295 const bool finalSegment = ((jInd + 1) == jRoute.size());
2296
2297 // Normal crossings aren't counted if we pass the pointers
2298 // for the connectors, so don't pass them.
2299 cross.countForSegment(jInd, finalSegment);
2300
2301 count += cross.crossingCount;
2302 }
2303 }
2304 }
2305 return count;
2306}
2307
2308// Looks for non-orthogonal segments in orthogonal connectors and
2309// returns true if it finds any.
2311{
2312 // For each connector...
2313 ConnRefList::iterator fin = connRefs.end();
2314 for (ConnRefList::iterator i = connRefs.begin(); i != fin; ++i)
2315 {
2316 // If it is an orthogonal connector...
2317 if ((*i)->routingType() == Avoid::ConnType_Orthogonal)
2318 {
2319 // Check each segment of the path...
2320 Avoid::Polygon iRoute = (*i)->displayRoute();
2321 for (size_t iInd = 1; iInd < iRoute.size(); ++iInd)
2322 {
2323 // And if it isn't either vertical or horizontal...
2324 if ( (iRoute.at(iInd - 1).x != iRoute.at(iInd).x) &&
2325 (iRoute.at(iInd - 1).y != iRoute.at(iInd).y) )
2326 {
2327 // Then we've found an invalid path.
2328 return true;
2329 }
2330 }
2331 }
2332 }
2333 return false;
2334}
2335
2336
2338{
2339 COLA_ASSERT(m_topology_addon);
2340 delete m_topology_addon;
2341 if (topologyAddon)
2342 {
2343 m_topology_addon = topologyAddon->clone();
2344 }
2345 else
2346 {
2348 }
2350}
2351
2357
2358void Router::outputInstanceToSVG(std::string instanceName)
2359{
2360 std::string filename;
2361 if (!instanceName.empty())
2362 {
2363 filename = instanceName;
2364 }
2365 else
2366 {
2367 filename = "libavoid-debug";
2368 }
2369 filename += ".svg";
2370 FILE *fp = fopen(filename.c_str(), "w");
2371
2372 if (fp == nullptr)
2373 {
2374 return;
2375 }
2376
2377 double minX = LIMIT;
2378 double minY = LIMIT;
2379 double maxX = -LIMIT;
2380 double maxY = -LIMIT;
2381
2382 VertInf *curr = vertices.connsBegin();
2383 while (curr)
2384 {
2385 Point p = curr->point;
2386
2387 reduceRange(p.x);
2388 reduceRange(p.y);
2389
2390 if (p.x > -LIMIT)
2391 {
2392 minX = std::min(minX, p.x);
2393 }
2394 if (p.x < LIMIT)
2395 {
2396 maxX = std::max(maxX, p.x);
2397 }
2398 if (p.y > -LIMIT)
2399 {
2400 minY = std::min(minY, p.y);
2401 }
2402 if (p.y < LIMIT)
2403 {
2404 maxY = std::max(maxY, p.y);
2405 }
2406 curr = curr->lstNext;
2407 }
2408 minX -= 8;
2409 minY -= 8;
2410 maxX += 8;
2411 maxY += 8;
2412
2413 fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2414 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);
2415
2416 // Output source code to generate this instance of the router.
2417 fprintf(fp, "<!-- Source code to generate this instance:\n");
2418 fprintf(fp, "#include \"libavoid/libavoid.h\"\n");
2419 if (m_topology_addon->outputCode(nullptr))
2420 {
2421 fprintf(fp, "#include \"libcola/cola.h\"\n");
2422 fprintf(fp, "#include \"libtopology/orthogonal_topology.h\"\n");
2423 fprintf(fp, "using namespace cola;\n");
2424 }
2425 fprintf(fp, "using namespace Avoid;\n");
2426 fprintf(fp, "int main(void) {\n");
2427 fprintf(fp, " Router *router = new Router(");
2429 {
2430 fprintf(fp, "PolyLineRouting");
2431 }
2433 {
2434 fprintf(fp, " | ");
2435 }
2437 {
2438 fprintf(fp, "OrthogonalRouting");
2439 }
2440 fprintf(fp, ");\n");
2441 for (size_t p = 0; p < lastRoutingParameterMarker; ++p)
2442 {
2443 fprintf(fp, " router->setRoutingParameter((RoutingParameter)%lu, %g);\n",
2444 (unsigned long)p, m_routing_parameters[p]);
2445 }
2446 for (size_t p = 0; p < lastRoutingOptionMarker; ++p)
2447 {
2448 fprintf(fp, " router->setRoutingOption((RoutingOption)%lu, %s);\n",
2449 (unsigned long)p, (m_routing_options[p]) ? "true" : "false");
2450 }
2451 fprintf(fp, " Polygon polygon;\n");
2452 fprintf(fp, " ConnRef *connRef = nullptr;\n");
2453 fprintf(fp, " ConnEnd srcPt;\n");
2454 fprintf(fp, " ConnEnd dstPt;\n");
2455 fprintf(fp, " ConnEnd heConnPt;\n");
2456 fprintf(fp, " PolyLine newRoute;\n");
2457 fprintf(fp, " ShapeConnectionPin *connPin = nullptr;\n");
2458 fprintf(fp, "\n");
2459 ClusterRefList::reverse_iterator revClusterRefIt = clusterRefs.rbegin();
2460 while (revClusterRefIt != clusterRefs.rend())
2461 {
2462 ClusterRef *cRef = *revClusterRefIt;
2463 fprintf(fp, " polygon = Polygon(%lu);\n",
2464 (unsigned long)cRef->polygon().size());
2465 for (size_t i = 0; i <cRef->polygon().size(); ++i)
2466 {
2467 fprintf(fp, " polygon.ps[%lu] = Point(%g, %g);\n",
2468 (unsigned long)i, cRef->polygon().at(i).x,
2469 cRef->polygon().at(i).y);
2470 }
2471 fprintf(fp, " new ClusterRef(router, polygon, %u);\n", cRef->id());
2472 ++revClusterRefIt;
2473 }
2474 ObstacleList::reverse_iterator revObstacleIt = m_obstacles.rbegin();
2475 while (revObstacleIt != m_obstacles.rend())
2476 {
2477 Obstacle *obstacle = *revObstacleIt;
2478 obstacle->outputCode(fp);
2479 ++revObstacleIt;
2480 }
2481 ConnRefList::reverse_iterator revConnRefIt = connRefs.rbegin();
2482 while (revConnRefIt != connRefs.rend())
2483 {
2484 ConnRef *connRef = *revConnRefIt;
2485 connRef->outputCode(fp);
2486 ++revConnRefIt;
2487 }
2488
2490
2491 fprintf(fp, " router->processTransaction();\n");
2492 fprintf(fp, " router->outputInstanceToSVG();\n");
2493
2495 fprintf(fp, " delete router;\n");
2496 fprintf(fp, " return 0;\n");
2497 fprintf(fp, "};\n");
2498 fprintf(fp, "-->\n");
2499
2500 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2501 "inkscape:label=\"Clusters\">\n");
2502 revClusterRefIt = clusterRefs.rbegin();
2503 while (revClusterRefIt != clusterRefs.rend())
2504 {
2505 ClusterRef *cRef = *revClusterRefIt;
2506 fprintf(fp, "<path id=\"cluster-%u\" style=\"stroke-width: 1px; "
2507 "stroke: black; fill: green; opacity: 0.1;\" d=\"",
2508 cRef->id());
2509 for (size_t i = 0; i < cRef->polygon().size(); ++i)
2510 {
2511 fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
2512 cRef->polygon().at(i).x, cRef->polygon().at(i).y);
2513 }
2514 fprintf(fp, "Z\" />\n");
2515
2516 fprintf(fp, "<path id=\"cluster-%u-rect\" style=\"stroke-width: 1px; "
2517 "stroke: black; fill: green; opacity: 0.1;\" d=\"",
2518 cRef->id());
2519 for (size_t i = 0; i < cRef->rectangularPolygon().size(); ++i)
2520 {
2521 fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
2522 cRef->rectangularPolygon().at(i).x,
2523 cRef->rectangularPolygon().at(i).y);
2524 }
2525 fprintf(fp, "Z\" />\n");
2526 ++revClusterRefIt;
2527 }
2528 fprintf(fp, "</g>\n");
2529
2530 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2531 "style=\"display: none;\" "
2532 "inkscape:label=\"ShapePolygons\">\n");
2533 ObstacleList::iterator obstacleIt = m_obstacles.begin();
2534 while (obstacleIt != m_obstacles.end())
2535 {
2536 Obstacle *obstacle = *obstacleIt;
2537 bool isShape = (nullptr != dynamic_cast<ShapeRef *> (obstacle));
2538
2539 if ( ! isShape )
2540 {
2541 // Don't output obstacles here, for now.
2542 ++obstacleIt;
2543 continue;
2544 }
2545
2546 fprintf(fp, "<path id=\"poly-%u\" style=\"stroke-width: 1px; "
2547 "stroke: black; fill: %s; fill-opacity: 0.3;\" d=\"",
2548 obstacle->id(), (isShape) ? "grey" : "red");
2549 for (size_t i = 0; i < obstacle->polygon().size(); ++i)
2550 {
2551 fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
2552 obstacle->polygon().at(i).x, obstacle->polygon().at(i).y);
2553 }
2554 fprintf(fp, "Z\" />\n");
2555 ++obstacleIt;
2556 }
2557 fprintf(fp, "</g>\n");
2558
2559 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2560 "style=\"display: none;\" "
2561 "inkscape:label=\"ObstaclePolygons\">\n");
2562 obstacleIt = m_obstacles.begin();
2563 while (obstacleIt != m_obstacles.end())
2564 {
2565 Obstacle *obstacle = *obstacleIt;
2566 bool isShape = (nullptr != dynamic_cast<ShapeRef *> (obstacle));
2567
2568 if ( ! isShape )
2569 {
2570 // Don't output obstacles here, for now.
2571 ++obstacleIt;
2572 continue;
2573 }
2574
2575 Polygon polygon = obstacle->routingPolygon();
2576 fprintf(fp, "<path id=\"poly-%u\" style=\"stroke-width: 1px; "
2577 "stroke: black; fill: %s; fill-opacity: 0.3;\" d=\"",
2578 obstacle->id(), (isShape) ? "grey" : "red");
2579 for (size_t i = 0; i < polygon.size(); ++i)
2580 {
2581 fprintf(fp, "%c %g %g ", ((i == 0) ? 'M' : 'L'),
2582 polygon.at(i).x, polygon.at(i).y);
2583 }
2584 fprintf(fp, "Z\" />\n");
2585 ++obstacleIt;
2586 }
2587 fprintf(fp, "</g>\n");
2588
2589 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2590 "style=\"display: none;\" "
2591 "inkscape:label=\"IdealJunctions\">\n");
2592 for (ObstacleList::iterator obstacleIt = m_obstacles.begin();
2593 obstacleIt != m_obstacles.end(); ++obstacleIt)
2594 {
2595 JunctionRef *junction = dynamic_cast<JunctionRef *> (*obstacleIt);
2596 if (junction)
2597 {
2598 fprintf(fp, "<circle id=\"idealJunction-%u\" cx=\"%g\" cy=\"%g\" "
2599 "r=\"8\" style=\"stroke: none; fill: %s; "
2600 "fill-opacity: 0.5;\" />\n", junction->id(),
2601 junction->recommendedPosition().x,
2602 junction->recommendedPosition().y, "green");
2603 }
2604
2605 }
2606 fprintf(fp, "</g>\n");
2607
2608 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2609 "inkscape:label=\"ObstacleRects\">\n");
2610 obstacleIt = m_obstacles.begin();
2611 while (obstacleIt != m_obstacles.end())
2612 {
2613 Obstacle *obstacle = *obstacleIt;
2614 bool isShape = (nullptr != dynamic_cast<ShapeRef *> (obstacle));
2615
2616 if ( ! isShape )
2617 {
2618 // Don't output obstacles here, for now.
2619 ++obstacleIt;
2620 continue;
2621 }
2622
2623 Box bBox = obstacle->routingBox();
2624
2625 fprintf(fp, "<rect id=\"rect-%u\" x=\"%g\" y=\"%g\" width=\"%g\" "
2626 "height=\"%g\" style=\"stroke-width: 1px; stroke: black; "
2627 "fill: grey; stroke-opacity: 0.1; fill-opacity: 0.1;\" />\n",
2628 obstacle->id(), bBox.min.x, bBox.min.y,
2629 bBox.max.x - bBox.min.x, bBox.max.y - bBox.min.y);
2630 ++obstacleIt;
2631 }
2632 fprintf(fp, "</g>\n");
2633
2634 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2635 "inkscape:label=\"VisGraph\""
2636 ">\n");
2637 EdgeInf *finish = nullptr;
2638 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2639 "style=\"display: none;\" "
2640 "inkscape:label=\"VisGraph-shape\""
2641 ">\n");
2642 finish = visGraph.end();
2643 for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext)
2644 {
2645 std::pair<VertID, VertID> ids = t->ids();
2646 bool isConn = ids.first.isConnPt() || ids.second.isConnPt();
2647 if (isConn)
2648 {
2649 continue;
2650 }
2651 std::pair<Point, Point> ptpair = t->points();
2652 Point p1 = ptpair.first;
2653 Point p2 = ptpair.second;
2654
2655 reduceRange(p1.x);
2656 reduceRange(p1.y);
2657 reduceRange(p2.x);
2658 reduceRange(p2.y);
2659
2660 fprintf(fp, "<path d=\"M %g %g L %g %g\" "
2661 "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
2662 p1.x, p1.y, p2.x, p2.y,
2663 (ids.first.isConnPt() || ids.second.isConnPt()) ? "blue" :
2664 "red");
2665 }
2666 fprintf(fp, "</g>\n");
2667 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2668 "style=\"display: none;\" "
2669 "inkscape:label=\"VisGraph-conn\""
2670 ">\n");
2671 finish = visGraph.end();
2672 for (EdgeInf *t = visGraph.begin(); t != finish; t = t->lstNext)
2673 {
2674 std::pair<VertID, VertID> ids = t->ids();
2675 bool isConn = ids.first.isConnPt() || ids.second.isConnPt();
2676 if (!isConn)
2677 {
2678 continue;
2679 }
2680 std::pair<Point, Point> ptpair = t->points();
2681 Point p1 = ptpair.first;
2682 Point p2 = ptpair.second;
2683
2684 reduceRange(p1.x);
2685 reduceRange(p1.y);
2686 reduceRange(p2.x);
2687 reduceRange(p2.y);
2688
2689 fprintf(fp, "<path d=\"M %g %g L %g %g\" "
2690 "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
2691 p1.x, p1.y, p2.x, p2.y,
2692 (ids.first.isConnPt() || ids.second.isConnPt()) ? "blue" :
2693 "red");
2694 }
2695 fprintf(fp, "</g>\n");
2696 fprintf(fp, "</g>\n");
2697
2698 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2699 "style=\"display: none;\" "
2700 "inkscape:label=\"OrthogVisGraph\">\n");
2701 finish = visOrthogGraph.end();
2702 for (EdgeInf *t = visOrthogGraph.begin(); t != finish; t = t->lstNext)
2703 {
2704 std::pair<Point, Point> ptpair = t->points();
2705 Point p1 = ptpair.first;
2706 Point p2 = ptpair.second;
2707
2708 reduceRange(p1.x);
2709 reduceRange(p1.y);
2710 reduceRange(p2.x);
2711 reduceRange(p2.y);
2712
2713 std::pair<VertID, VertID> ids = t->ids();
2714
2715 fprintf(fp, "<path d=\"M %g %g L %g %g\" "
2716 "style=\"fill: none; stroke: %s; stroke-width: 1px;\" />\n",
2717 p1.x, p1.y, p2.x, p2.y,
2718 (ids.first.isConnPt() || ids.second.isConnPt()) ? "green" :
2719 "red");
2720 }
2721 fprintf(fp, "</g>\n");
2722
2723 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2724 "style=\"display: none;\" "
2725 "inkscape:label=\"RawConnectors\""
2726 ">\n");
2727 ConnRefList::iterator connRefIt = connRefs.begin();
2728 while (connRefIt != connRefs.end())
2729 {
2730 ConnRef *connRef = *connRefIt;
2731
2732 PolyLine route = connRef->route();
2733 if (!route.empty())
2734 {
2735 fprintf(fp, "<path id=\"raw-%u\" d=\"M %g %g ", connRef->id(),
2736 route.ps[0].x, route.ps[0].y);
2737 for (size_t i = 1; i < route.size(); ++i)
2738 {
2739 fprintf(fp, "L %g %g ", route.ps[i].x, route.ps[i].y);
2740 }
2741 fprintf(fp, "\" ");
2742 if (connRef->src() && connRef->dst())
2743 {
2744 fprintf(fp, "debug=\"src: %d dst: %d\" ",
2745 connRef->src()->visDirections,
2746 connRef->dst()->visDirections);
2747 }
2748 fprintf(fp, "style=\"fill: none; stroke: black; "
2749 "stroke-width: 1px;\" />\n");
2750 }
2751
2752 ++connRefIt;
2753 }
2754 fprintf(fp, "</g>\n");
2755
2756 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2757 "style=\"display: none;\" "
2758 "inkscape:label=\"CurvedDisplayConnectors\""
2759 ">\n");
2760 connRefIt = connRefs.begin();
2761 while (connRefIt != connRefs.end())
2762 {
2763 ConnRef *connRef = *connRefIt;
2764
2765 PolyLine route = connRef->displayRoute().curvedPolyline(8);
2766 if (!route.empty())
2767 {
2768 fprintf(fp, "<path id=\"curved-%u\" d=\"M %g %g ", connRef->id(),
2769 route.ps[0].x, route.ps[0].y);
2770 for (size_t i = 1; i < route.size(); ++i)
2771 {
2772 if (route.ts[i] == 'C')
2773 {
2774 fprintf(fp, "%c %g %g %g %g %g %g", route.ts[i],
2775 route.ps[i].x, route.ps[i].y,
2776 route.ps[i+1].x, route.ps[i+1].y,
2777 route.ps[i+2].x, route.ps[i+2].y);
2778 i += 2;
2779 }
2780 else
2781 {
2782 fprintf(fp, "%c %g %g ", route.ts[i],
2783 route.ps[i].x, route.ps[i].y);
2784 }
2785 }
2786 fprintf(fp, "\" ");
2787 if (connRef->src() && connRef->dst())
2788 {
2789 fprintf(fp, "debug=\"src: %d dst: %d\" ",
2790 connRef->src()->visDirections,
2791 connRef->dst()->visDirections);
2792 }
2793 fprintf(fp, "style=\"fill: none; stroke: black; "
2794 "stroke-width: 1px;\" />\n");
2795 }
2796
2797 ++connRefIt;
2798 }
2799 fprintf(fp, "</g>\n");
2800
2801
2802 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2803 "inkscape:label=\"DisplayConnectors\""
2804 ">\n");
2805 connRefIt = connRefs.begin();
2806 while (connRefIt != connRefs.end())
2807 {
2808 ConnRef *connRef = *connRefIt;
2809
2810 PolyLine route = connRef->displayRoute();
2811 if (!route.empty())
2812 {
2813 fprintf(fp, "<path id=\"disp-%u\" d=\"M %g %g ", connRef->id(),
2814 route.ps[0].x, route.ps[0].y);
2815 for (size_t i = 1; i < route.size(); ++i)
2816 {
2817 fprintf(fp, "L %g %g ", route.ps[i].x, route.ps[i].y);
2818 }
2819 fprintf(fp, "\" ");
2820 if (connRef->src() && connRef->dst())
2821 {
2822 fprintf(fp, "debug=\"src: %d dst: %d\" ",
2823 connRef->src()->visDirections,
2824 connRef->dst()->visDirections);
2825 }
2826 fprintf(fp, "style=\"fill: none; stroke: black; "
2827 "stroke-width: 1px;\" />\n");
2828 }
2829
2830 ++connRefIt;
2831 }
2832 fprintf(fp, "</g>\n");
2833
2834 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2835 "inkscape:label=\"ConnectorCheckpoints\""
2836 ">\n");
2837 connRefIt = connRefs.begin();
2838 while (connRefIt != connRefs.end())
2839 {
2840 ConnRef *connRef = *connRefIt;
2841
2842 for (size_t i = 0; i < connRef->m_checkpoints.size(); ++i)
2843 {
2844 fprintf(fp, "<circle id=\"checkpoint-%u-%d\" cx=\"%g\" cy=\"%g\" "
2845 "r=\"8\" style=\"stroke: none; fill: red; "
2846 "fill-opacity: 0.25;\" />\n", connRef->id(), (int) i,
2847 connRef->m_checkpoints[i].point.x,
2848 connRef->m_checkpoints[i].point.y);
2849 }
2850
2851 ++connRefIt;
2852 }
2853 fprintf(fp, "</g>\n");
2854
2855
2856 fprintf(fp, "</svg>\n");
2857 fclose(fp);
2858 //printInfo();
2859}
2860
2861void Router::outputDiagramSVG(std::string instanceName, LineReps *lineReps)
2862{
2863 std::string filename;
2864 if (!instanceName.empty())
2865 {
2866 filename = instanceName;
2867 }
2868 else
2869 {
2870 filename = "libavoid-diagram";
2871 }
2872 filename += ".svg";
2873 FILE *fp = fopen(filename.c_str(), "w");
2874
2875 if (fp == nullptr)
2876 {
2877 return;
2878 }
2879
2880 double minX = LIMIT;
2881 double minY = LIMIT;
2882 double maxX = -LIMIT;
2883 double maxY = -LIMIT;
2884
2885 VertInf *curr = vertices.connsBegin();
2886 while (curr)
2887 {
2888 Point p = curr->point;
2889
2890 reduceRange(p.x);
2891 reduceRange(p.y);
2892
2893 if (p.x > -LIMIT)
2894 {
2895 minX = std::min(minX, p.x);
2896 }
2897 if (p.x < LIMIT)
2898 {
2899 maxX = std::max(maxX, p.x);
2900 }
2901 if (p.y > -LIMIT)
2902 {
2903 minY = std::min(minY, p.y);
2904 }
2905 if (p.y < LIMIT)
2906 {
2907 maxY = std::max(maxY, p.y);
2908 }
2909 curr = curr->lstNext;
2910 }
2911 minX -= 8;
2912 minY -= 8;
2913 maxX += 8;
2914 maxY += 8;
2915
2916 fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2917 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);
2918
2919 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2920 "inkscape:label=\"ShapesRect\">\n");
2921 ObstacleList::iterator obstacleIt = m_obstacles.begin();
2922 while (obstacleIt != m_obstacles.end())
2923 {
2924 Obstacle *obstacle = *obstacleIt;
2925 bool isShape = (nullptr != dynamic_cast<ShapeRef *> (obstacle));
2926
2927 if ( ! isShape )
2928 {
2929 // Don't output non-shape obstacles here, for now.
2930 ++obstacleIt;
2931 continue;
2932 }
2933
2934 Box bBox = obstacle->polygon().offsetBoundingBox(0.0);
2935
2936 fprintf(fp, "<rect id=\"rect-%u\" x=\"%g\" y=\"%g\" width=\"%g\" "
2937 "height=\"%g\" style=\"stroke-width: 1px; stroke: black; "
2938 "fill: grey; stroke-opacity: 0.5; fill-opacity: 0.4;\" />\n",
2939 obstacle->id(), bBox.min.x, bBox.min.y,
2940 bBox.max.x - bBox.min.x, bBox.max.y - bBox.min.y);
2941 ++obstacleIt;
2942 }
2943 fprintf(fp, "</g>\n");
2944
2945 fprintf(fp, "<g inkscape:groupmode=\"layer\" "
2946 "inkscape:label=\"DisplayConnectors\""
2947 ">\n");
2948 ConnRefList::iterator connRefIt = connRefs.begin();
2949 while (connRefIt != connRefs.end())
2950 {
2951 ConnRef *connRef = *connRefIt;
2952
2953 PolyLine route = connRef->displayRoute();
2954 if (!route.empty())
2955 {
2956 fprintf(fp, "<path id=\"disp-%u\" d=\"M %g %g ", connRef->id(),
2957 route.ps[0].x, route.ps[0].y);
2958 for (size_t i = 1; i < route.size(); ++i)
2959 {
2960 fprintf(fp, "L %g %g ", route.ps[i].x, route.ps[i].y);
2961 }
2962 fprintf(fp, "\" ");
2963 fprintf(fp, "style=\"fill: none; stroke: black; "
2964 "stroke-width: 1px;\" />\n");
2965
2966 /*
2967 for (size_t i = 1; i < route.size(); ++i)
2968 {
2969 if (route.segmentHasCheckpoint[i - 1])
2970 {
2971 fprintf(fp, "<path d=\"M %g %g ",
2972 route.ps[i - 1].x, route.ps[i - 1].y);
2973 fprintf(fp, "L %g %g\" ", route.ps[i].x, route.ps[i].y);
2974 fprintf(fp, "style=\"fill: none; stroke: red; "
2975 "stroke-width: 1px; stroke-opacity: 1;\" />\n");
2976 }
2977 }
2978 */
2979 }
2980
2981 ++connRefIt;
2982 }
2983 fprintf(fp, "</g>\n");
2984
2985 if (lineReps)
2986 {
2987
2988 for (LineReps::iterator it = lineReps->begin(); it != lineReps->end();
2989 ++it)
2990 {
2991 fprintf(fp, "<path d=\"M %g %g ",
2992 it->begin.x, it->begin.y);
2993 fprintf(fp, "L %g %g\" ", it->end.x, it->end.y);
2994 fprintf(fp, "style=\"fill: none; stroke: red; "
2995 "stroke-width: 1px; stroke-opacity: 0.7;\" />\n");
2996 }
2997 }
2998
2999 fprintf(fp, "</svg>\n");
3000 fclose(fp);
3001}
3002
3003void Router::outputDiagram(std::string instanceName)
3004{
3005 outputDiagramText(instanceName);
3006#ifdef SVG_OUTPUT
3007 outputInstanceToSVG(instanceName);
3008#endif
3009}
3010
3011void Router::outputDiagramText(std::string instanceName)
3012{
3013 std::string filename;
3014 if (!instanceName.empty())
3015 {
3016 filename = instanceName;
3017 }
3018 else
3019 {
3020 filename = "libavoid-diagram";
3021 }
3022 filename += ".txt";
3023 FILE *fp = fopen(filename.c_str(), "w");
3024
3025 if (fp == nullptr)
3026 {
3027 return;
3028 }
3029
3030 ObstacleList::iterator obstacleIt = m_obstacles.begin();
3031 while (obstacleIt != m_obstacles.end())
3032 {
3033 Obstacle *obstacle = *obstacleIt;
3034 bool isShape = (nullptr != dynamic_cast<ShapeRef *> (obstacle));
3035
3036 if ( ! isShape )
3037 {
3038 // Don't output non-shape obstacles here, for now.
3039 ++obstacleIt;
3040 continue;
3041 }
3042
3043 Box bBox = obstacle->polygon().offsetBoundingBox(0.0);
3044
3045 fprintf(fp, "rect\n");
3046 fprintf(fp, "id=%u\n", obstacle->id());
3047 fprintf(fp, "x=%g\n", bBox.min.x);
3048 fprintf(fp, "y=%g\n", bBox.min.y);
3049 fprintf(fp, "width=%g\n", bBox.max.x - bBox.min.x);
3050 fprintf(fp, "height=%g\n", bBox.max.y - bBox.min.y);
3051 fprintf(fp, "\n");
3052
3053 ++obstacleIt;
3054 }
3055
3056 ConnRefList::iterator connRefIt = connRefs.begin();
3057 while (connRefIt != connRefs.end())
3058 {
3059 ConnRef *connRef = *connRefIt;
3060
3061 PolyLine route = connRef->displayRoute();
3062 if (!route.empty())
3063 {
3064 fprintf(fp, "path\n");
3065 fprintf(fp, "id=%u\n", connRef->id());
3066 for (size_t i = 0; i < route.size(); ++i)
3067 {
3068 fprintf(fp, "p%zu: %g %g ", i, route.ps[i].x, route.ps[i].y);
3069 fprintf(fp, "\n");
3070 }
3071 fprintf(fp, "\n");
3072 }
3073
3074 ++connRefIt;
3075 }
3076
3077 fprintf(fp, "\n");
3078
3079 fclose(fp);
3080}
3081
3087
3088
3092
3096
3098{
3099 m_mapping.push_back(std::make_pair(conn, false));
3100 return &(m_mapping.back().second);
3101}
3102
3104{
3105 std::list<std::pair<ConnRef *, bool> >::iterator it;
3106 for (it = m_mapping.begin(); it != m_mapping.end(); ++it)
3107 {
3108 if (it->first == conn)
3109 {
3110 it->first = nullptr;
3111 }
3112 }
3113}
3114
3115
3117{
3118 std::list<std::pair<ConnRef *, bool> >::iterator it;
3119 for (it = m_mapping.begin(); it != m_mapping.end(); ++it)
3120 {
3121 if ((it->first != nullptr) && (it->second == true))
3122 {
3123 it->second = false;
3124 it->first->m_needs_reroute_flag = true;
3125 }
3126 }
3127}
3128
3129
3130}
3131
SimpleSnap option
Obstacle * obstacle(void) const
ShapeRef * shape(void) const
ConnRef * conn(void) const
JunctionRef * junction(void) const
ConnUpdateList conns
Definition actioninfo.h:82
ActionType type
Definition actioninfo.h:77
A bounding box, represented by the top-left and bottom-right corners.
Definition geomtypes.h:134
Point min
The top-left point.
Definition geomtypes.h:137
Point max
The bottom-right point.
Definition geomtypes.h:139
The ClusterRef class represents a cluster object.
Definition viscluster.h:56
void makeInactive(void)
unsigned int id(void) const
Returns the ID of this cluster.
void makeActive(void)
ReferencingPolygon & polygon(void)
Returns a reference to the polygon boundary of this cluster.
Polygon & rectangularPolygon(void)
Returns a reference to the rectangular boundary of this cluster.
The ConnEnd class represents different possible endpoints for connectors.
Definition connend.h:111
The ConnRef class represents a connector object.
Definition connector.h:132
void outputCode(FILE *fp) const
unsigned int id(void) const
Returns the ID of this connector.
void freeActivePins(void)
const PolyLine & route(void) const
Returns a reference to the current raw "debug" route for the connector.
VertInf * dst(void) const
ConnType routingType(void) const
Returns the type of routing performed for this connector.
bool m_needs_repaint
Definition connector.h:449
bool m_needs_reroute_flag
Definition connector.h:447
PolyLine m_route
Definition connector.h:454
bool hasFixedRoute(void) const
Returns whether the connector route is marked as fixed.
std::vector< Checkpoint > m_checkpoints
Definition connector.h:465
void makePathInvalid(void)
bool generatePath(void)
void performCallback(void)
VertInf * src(void) const
PolyLine & displayRoute(void)
Returns a reference to the current display version of the route for the connector.
std::pair< ConnEnd, ConnEnd > endpointConnEnds(void) const
Returns ConnEnds specifying what this connector is attached to.
void freeRoutes(void)
double m_route_dist
Definition connector.h:456
void updateEndPoint(const unsigned int type, const ConnEnd &connEnd)
std::list< std::pair< ConnRef *, bool > > m_mapping
Definition router.h:340
bool * addConn(ConnRef *conn)
Definition router.cpp:3097
void removeConn(ConnRef *conn)
Definition router.cpp:3103
std::pair< Point, Point > points(void) const
Definition graph.cpp:367
static EdgeInf * checkEdgeVisibility(VertInf *i, VertInf *j, bool knownNew=false)
Definition graph.cpp:586
void db_print(void)
Definition graph.cpp:373
std::pair< VertID, VertID > ids(void) const
Definition graph.cpp:361
void checkVis(void)
Definition graph.cpp:383
EdgeInf * lstNext
Definition graph.h:82
static EdgeInf * existingEdge(VertInf *i, VertInf *j)
Definition graph.cpp:622
void addBlocker(int b)
Definition graph.cpp:342
double getDist(void)
Definition graph.h:51
int blocker(void) const
Definition graph.cpp:313
void alertConns(void)
Definition graph.cpp:318
void clear(void)
Definition graph.cpp:687
int size(void) const
Definition graph.cpp:701
EdgeInf * end(void)
Definition graph.cpp:777
EdgeInf * begin(void)
Definition graph.cpp:771
void execute(bool canMakeMajorChanges)
void setRouter(Router *router)
HyperedgeNewAndDeletedObjectLists newAndDeletedObjectLists(void) const
The HyperedgeRerouter class is a convenience object that can be used to register hyperedges to be rer...
Definition hyperedge.h:130
ConnRefSet calcHyperedgeConnectors(void)
HyperedgeNewAndDeletedObjectLists newAndDeletedObjectLists(size_t index) const
Returns a HyperedgeNewAndDeletedObjectLists detailing the lists of junctions and connectors created a...
Definition hyperedge.cpp:74
size_t count(void) const
Definition hyperedge.cpp:69
void setRouter(Router *router)
Definition hyperedge.cpp:46
The JunctionRef class represents a fixed or free-floating point that connectors can be attached to.
Definition junction.h:58
Point recommendedPosition(void) const
Returns a recommended position for the junction based on improving hyperedge routes.
Definition junction.cpp:136
void moveAttachedConns(const Point &newPosition)
Definition junction.cpp:170
Point position(void) const
Returns the position of this junction.
Definition junction.cpp:121
void setPosition(const Point &position)
Definition junction.cpp:127
unsigned int id(void) const
Returns the ID of this obstacle.
Definition obstacle.cpp:249
Box routingBox(void) const
Definition obstacle.cpp:267
void removeFromGraph(void)
Definition obstacle.cpp:298
void updatePinPolyLineVisibility(void)
Definition obstacle.cpp:187
VertInf * lastVert(void)
Definition obstacle.cpp:243
void computeVisibilitySweep(void)
virtual void outputCode(FILE *fp) const =0
void makeActive(void)
Definition obstacle.cpp:134
void computeVisibilityNaive(void)
VertInf * firstVert(void)
Definition obstacle.cpp:237
void setNewPoly(const Polygon &poly)
Definition obstacle.cpp:100
bool isActive(void) const
Definition obstacle.cpp:231
void makeInactive(void)
Definition obstacle.cpp:157
Polygon routingPolygon(void) const
Definition obstacle.cpp:277
const Polygon & polygon(void) const
Returns a reference to the polygon boundary of this obstacle.
Definition obstacle.cpp:255
The Point class defines a point in the plane.
Definition geomtypes.h:53
double y
The y position.
Definition geomtypes.h:109
double x
The x position.
Definition geomtypes.h:107
A common interface used by the Polygon classes.
Definition geomtypes.h:151
Box offsetBoundingBox(double offset) const
Returns the bounding rectangle that contains this polygon with optionally some buffer space around it...
A dynamic Polygon, to which points can be easily added and removed.
Definition geomtypes.h:208
size_t size(void) const
Returns the number of points in this polygon.
Polygon curvedPolyline(const double curve_amount, const bool closed=false) const
Returns a curved approximation of this multi-segment PolyLine, with the corners replaced by smooth Be...
std::vector< char > ts
If used, denotes whether the corresponding point in ps is a move-to operation or a Bezier curve-to.
Definition geomtypes.h:301
bool empty(void) const
Returns true if this polygon is empty.
void translate(const double xDist, const double yDist)
Translates the polygon position by a relative amount.
std::vector< Point > ps
A vector of the points that make up the Polygon.
Definition geomtypes.h:285
const Point & at(size_t index) const
Returns a specific point in the polygon.
A Polygon which just references its points from other Polygons.
Definition geomtypes.h:337
const Point & at(size_t index) const
Returns a specific point in the polygon.
size_t size(void) const
Returns the number of points in this polygon.
bool PartialFeedback
Definition router.h:423
unsigned int assignId(const unsigned int suggestedId)
Definition router.cpp:807
void moveJunction(JunctionRef *junction, const Point &newPosition)
Move an existing junction within the router scene.
Definition router.cpp:741
void rerouteAndCallbackConnectors(void)
Definition router.cpp:924
void setRoutingOption(const RoutingOption option, const bool value)
Turn specific routing options on or off.
Definition router.cpp:2045
bool SimpleRouting
Definition router.h:412
void newBlockingShape(const Polygon &poly, int pid)
Definition router.cpp:1550
TopologyAddonInterface * m_topology_addon
Definition router.h:867
void checkAllMissingEdges(void)
Definition router.cpp:1637
int st_checked_edges
Definition router.h:431
void deleteShape(ShapeRef *shape)
Delete a shape from the router scene.
Definition router.cpp:281
virtual unsigned int newObjectId(void) const
Returns the object ID used for automatically generated objects, such as during hyperedge routing.
Definition router.cpp:801
HyperedgeNewAndDeletedObjectLists newAndDeletedObjectListsFromHyperedgeImprovement(void) const
Returns a HyperedgeNewAndDeletedObjectLists detailing the lists of junctions and connectors created a...
Definition router.cpp:3083
bool InvisibilityGrph
Definition router.h:418
bool m_static_orthogonal_graph_invalidated
Definition router.h:873
bool routingOption(const RoutingOption option) const
Returns the current state for a specific routing option.
Definition router.cpp:2053
ObstacleList m_obstacles
Definition router.h:401
HyperedgeImprover m_hyperedge_improver
Definition router.h:878
bool PartialTime
Definition router.h:411
void setTopologyAddon(TopologyAddonInterface *topologyAddon)
Set an addon for doing orthogonal topology improvement.
Definition router.cpp:2337
void addJunction(JunctionRef *junction)
Definition router.cpp:659
virtual bool shouldContinueTransactionWithProgress(unsigned int elapsedTime, unsigned int phaseNumber, unsigned int totalPhases, double proportion)
A method called at regular intervals during transaction processing to report progress and ask if the ...
Definition router.cpp:1375
void generateContains(VertInf *pt)
Definition router.cpp:1673
VertInfList vertices
Definition router.h:408
void deleteConnector(ConnRef *connector)
Remove a connector from the router scene.
Definition router.cpp:312
void modifyConnector(ConnRef *conn)
Definition router.cpp:202
void adjustContainsWithAdd(const Polygon &poly, const int p_shape)
Definition router.cpp:1728
void improveCrossings(void)
Definition router.cpp:1408
DebugHandler * debugHandler(void) const
Definition router.cpp:153
HyperedgeRerouter * hyperedgeRerouter(void)
Returns a pointer to the hyperedge rerouter for the router.
Definition router.cpp:2071
virtual ~Router()
Destructor for router instance.
Definition router.cpp:107
void outputInstanceToSVG(std::string filename=std::string())
Generates an SVG file containing debug output and code that can be used to regenerate the instance.
Definition router.cpp:2358
unsigned int m_largest_assigned_id
Definition router.h:854
DebugHandler * m_debug_handler
Definition router.h:880
EdgeList invisGraph
Definition router.h:405
ConnType validConnType(const ConnType select=ConnType_None) const
Definition router.cpp:1969
void setRoutingParameter(const RoutingParameter parameter, const double value=chooseSensibleParamValue)
Sets values for routing parameters, including routing penalties.
Definition router.cpp:1995
void deleteJunction(JunctionRef *junction)
Remove a junction from the router scene.
Definition router.cpp:685
void setStaticGraphInvalidated(const bool invalidated)
Definition router.cpp:403
ClusterRefList clusterRefs
Definition router.h:403
bool UseLeesAlgorithm
Definition router.h:417
bool m_consolidate_actions
Definition router.h:855
bool m_abort_transaction
Definition router.h:865
void setDebugHandler(DebugHandler *handler)
Definition router.cpp:148
void setTransactionUse(const bool transactions)
Allows setting of the behaviour of the router in regard to transactions.
Definition router.cpp:457
void moveShape(ShapeRef *shape, const Polygon &newPoly, const bool first_move=false)
Move or resize an existing shape within the router scene.
Definition router.cpp:360
void addShape(ShapeRef *shape)
Definition router.cpp:255
bool m_allows_orthogonal_routing
Definition router.h:871
bool m_in_crossing_rerouting_stage
Definition router.h:874
ContainsMap enclosingClusters
Definition router.h:409
void registerSettingsChange(void)
Definition router.cpp:2066
bool SelectiveReroute
Definition router.h:421
ConnRerouteFlagDelegate m_conn_reroute_flags
Definition router.h:860
bool existsOrthogonalTouchingPaths(void)
Definition router.cpp:2241
bool objectIdIsUnused(const unsigned int id) const
Returns whether or not the given ID is already used.
Definition router.cpp:827
ContainsMap contains
Definition router.h:407
void attachedShapes(IntList &shapes, const unsigned int shapeId, const unsigned int type)
Definition router.cpp:891
bool existsOrthogonalSegmentOverlap(const bool atEnds=false)
Definition router.cpp:2174
bool m_settings_changes
Definition router.h:876
ActionInfoList actionList
Definition router.h:853
void addCluster(ClusterRef *cluster)
Definition router.cpp:780
void improveOrthogonalTopology(void)
Definition router.cpp:2352
void regenerateStaticBuiltGraph(void)
Definition router.cpp:430
void checkAllBlockedEdges(int pid)
Definition router.cpp:1615
void markPolylineConnectorsNeedingReroutingForDeletedObstacle(Obstacle *obstacle)
Definition router.cpp:1767
ShapeRef * shapeContainingPoint(const Point &point)
Definition router.cpp:158
clock_t m_transaction_start_time
Definition router.h:864
Router(const unsigned int flags)
Constructor for router instance.
Definition router.cpp:46
double routingParameter(const RoutingParameter parameter) const
Returns the current value for a particular routing parameter of a given type.
Definition router.cpp:2038
void outputDiagram(std::string instanceName=std::string())
Definition router.cpp:3003
int existsCrossings(const bool optimisedForConnectorType=false)
Definition router.cpp:2277
void destroyOrthogonalVisGraph(void)
Definition router.cpp:409
void attachedConns(IntList &conns, const unsigned int shapeId, const unsigned int type)
Definition router.cpp:867
bool m_routing_options[lastRoutingOptionMarker]
Definition router.h:858
ConnRefList connRefs
Definition router.h:402
void performContinuationCheck(unsigned int phaseNumber, size_t stepNumber, size_t totalSteps)
Definition router.cpp:1356
double m_routing_parameters[lastRoutingParameterMarker]
Definition router.h:857
bool processTransaction(void)
Finishes the current transaction and processes all the queued object changes efficiently.
Definition router.cpp:640
void printInfo(void)
Definition router.cpp:2083
void outputDiagramSVG(std::string instanceName=std::string(), LineReps *lineReps=nullptr)
Definition router.cpp:2861
void processActions(void)
Definition router.cpp:464
bool m_allows_polyline_routing
Definition router.h:870
void modifyConnectionPin(ShapeConnectionPin *pin)
Definition router.cpp:220
void adjustClustersWithAdd(const PolygonInterface &poly, const int p_cluster)
Definition router.cpp:1704
bool isInCrossingPenaltyReroutingStage(void) const
Definition router.cpp:2077
void setRoutingPenalty(const RoutingParameter penType, const double penVal=chooseSensibleParamValue)
Sets or removes penalty values that are applied during connector routing.
Definition router.cpp:2060
void adjustContainsWithDel(const int p_shape)
Definition router.cpp:1744
bool existsOrthogonalFixedSegmentOverlap(const bool atEnds=false)
Definition router.cpp:2207
bool m_currently_calling_destructors
Definition router.h:856
void outputDiagramText(std::string instanceName=std::string())
Definition router.cpp:3011
EdgeList visOrthogGraph
Definition router.h:406
EdgeList visGraph
Definition router.h:404
HyperedgeRerouter m_hyperedge_rerouter
Definition router.h:861
bool existsInvalidOrthogonalPaths(void)
Definition router.cpp:2310
Timer timers
Definition router.h:429
bool RubberBandRouting
Definition router.h:424
bool transactionUse(void) const
Reports whether the router groups actions into transactions.
Definition router.cpp:451
void deleteCluster(ClusterRef *cluster)
Definition router.cpp:791
void adjustClustersWithDel(const int p_cluster)
Definition router.cpp:1718
void removeObjectFromQueuedActions(const void *object)
Definition router.cpp:238
void markAllObstaclesAsMoved(void)
Definition router.cpp:342
The ShapeConnectionPin class represents a fixed point or "pin" on a shape that can be connected to.
ConnectionPinIds ids(void) const
bool isExclusive(void) const
Returns whether the connection pin is exclusive, i.e., only one connector can attach to it.
The ShapeRef class represents a shape object.
Definition shape.h:82
const Polygon & polygon(void) const
Returns a reference to the polygon boundary of this shape.
Definition shape.cpp:228
void moveAttachedConns(const Polygon &newPoly)
Definition shape.cpp:57
void reset(void)
Definition timer.cpp:44
void printAll(FILE *fp)
Definition timer.cpp:126
virtual bool outputDeletionCode(FILE *fp) const
Definition router.h:373
virtual void improveOrthogonalTopology(Router *router)
Definition router.h:364
virtual TopologyAddonInterface * clone(void) const
Definition router.h:359
virtual bool outputCode(FILE *fp) const
Definition router.h:368
bool isConnPt(void) const
Definition vertices.h:87
bool isConnectionPin(void) const
Definition vertices.h:91
unsigned int objID
Definition vertices.h:54
VertInf * end(void)
Definition vertices.cpp:719
VertInf * shapesBegin(void)
Definition vertices.cpp:702
VertInf * removeVertex(VertInf *vert)
Definition vertices.cpp:557
VertInf * connsBegin(void)
Definition vertices.cpp:708
ConnDirFlags visDirections
Definition vertices.h:166
bool orphaned(void)
Definition vertices.cpp:222
VertInf * lstNext
Definition vertices.h:146
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
Contains the interface for the ShapeConnectionPin class.
Contains the interface for the ConnRef class.
Contains the interface for the ConnEnd class.
Geom::IntPoint size
double c[8][4]
Contains the interface for the JunctionRef class.
Geom::Point start
Geom::Point end
libavoid: Object-avoiding orthogonal and polyline connector routing library.
std::list< CrossingConnectorsMap > CrossingConnectorsMapList
Definition router.cpp:1081
void db_printf(const char *fmt,...)
Definition debug.h:53
double manhattanDist(const Point &a, const Point &b)
Definition geometry.cpp:302
static double cost(ConnRef *lineRef, const double dist, VertInf *inf2, VertInf *inf3, ANode *inf1Node)
Definition makepath.cpp:308
@ tmOrthogRoute
Definition timer.h:58
@ tmOrthogGraph
Definition timer.h:57
bool inPoly(const Polygon &poly, const Point &q, bool countBorder)
Definition geometry.cpp:349
static const unsigned int runningTo
Definition router.h:81
std::list< ConnCostRefSet > ConnCostRefSetList
Definition router.cpp:1052
ConnType
Describes the type of routing that is performed for each connector.
Definition connector.h:53
@ ConnType_Orthogonal
The connector path will be a shortest-path orthogonal poly-line (only vertical and horizontal line se...
Definition connector.h:61
@ ConnType_PolyLine
The connector path will be a shortest-path poly-line that routes around obstacles.
Definition connector.h:57
@ ConnType_None
Definition connector.h:54
std::pair< double, ConnRef * > ConnCostRef
Definition router.cpp:1036
@ TransactionPhaseRouteSearch
Initial routes are searched for in the visibility graph.
Definition router.h:308
@ TransactionPhaseCrossingDetection
With crossing penalties enabled, crossing detection is performed to find all crossings.
Definition router.h:311
@ TransactionPhaseCompleted
Not a real phase, but represents the router is finished (or has aborted) the transaction and you may ...
Definition router.h:320
@ TransactionPhaseRerouteSearch
Crossing connectors are rerouted to search for better routes.
Definition router.h:313
std::list< unsigned int > IntList
Definition router.h:58
const unsigned int CROSSING_SHARES_PATH_AT_END
Definition connector.h:504
std::list< LineRep > LineReps
Definition router.h:55
static const double LIMIT
Definition router.cpp:2161
const unsigned int CROSSING_SHARES_PATH
Definition connector.h:503
std::set< ConnRef * > ConnRefSet
Definition hyperedge.h:56
bool segmentShapeIntersect(const Point &e1, const Point &e2, const Point &s1, const Point &s2, bool &seenIntersectionAtEndpoint)
Definition geometry.cpp:166
std::list< ConnCostRef > ConnCostRefList
Definition router.cpp:1405
static const unsigned int runningFrom
Definition router.h:82
static void reduceRange(double &val)
Definition router.cpp:2163
double euclideanDist(const Point &a, const Point &b)
Definition geometry.cpp:292
RoutingParameter
Types of routing parameters and penalties that can be used to tailor the style and improve the qualit...
Definition router.h:89
@ lastRoutingParameterMarker
Definition router.h:166
@ crossingPenalty
This penalty is applied whenever a connector path crosses another connector path.
Definition router.h:114
@ clusterCrossingPenalty
This penalty is applied whenever a connector path crosses a cluster boundary.
Definition router.h:123
@ portDirectionPenalty
This penalty is applied to port selection choice when the other end of the connector being routed doe...
Definition router.h:142
@ fixedSharedPathPenalty
This penalty is applied whenever a connector path shares some segments with an immovable portion of a...
Definition router.h:131
@ anglePenalty
This penalty is applied in its full amount to tight acute bends in the connector path.
Definition router.h:106
@ segmentPenalty
This penalty is applied for each segment in the connector path beyond the first.
Definition router.h:97
@ idealNudgingDistance
This parameter defines the spacing distance that will be used for nudging apart overlapping corners a...
Definition router.h:154
@ OrthogonalRouting
This option specifies that the router should maintain the structures necessary to allow orthogonal co...
Definition router.h:77
@ PolyLineRouting
This option specifies that the router should maintain the structures necessary to allow poly-line con...
Definition router.h:74
static double cheapEstimatedCost(ConnRef *lineRef)
Definition router.cpp:1055
void generateStaticOrthogonalVisGraph(Router *router)
std::list< ConnRef * > ConnRefList
A list of ConnRef objects.
Definition connector.h:48
static const VertID dummyOrthogID(0, 0)
static double AngleAFromThreeSides(const double a, const double b, const double c)
Definition router.cpp:1754
const unsigned int CROSSING_SHARES_FIXED_SEGMENT
Definition connector.h:505
std::map< ConnRef *, std::set< ConnRef * > > CrossingConnectorsMap
Definition router.cpp:1078
const unsigned int CROSSING_TOUCHES
Definition connector.h:502
RoutingOption
Types of routing options that can be enabled.
Definition router.h:175
@ performUnifyingNudgingPreprocessingStep
This option can be used to control whether the router performs a preprocessing step before orthogonal...
Definition router.h:249
@ lastRoutingOptionMarker
Definition router.h:290
@ improveHyperedgeRoutesMovingJunctions
This option causes hyperedge routes to be locally improved fixing obviously bad paths.
Definition router.h:210
@ improveHyperedgeRoutesMovingAddingAndDeletingJunctions
This option causes hyperedge routes to be locally improved fixing obviously bad paths.
Definition router.h:275
@ nudgeOrthogonalTouchingColinearSegments
This option can be used to control whether collinear line segments that touch just at their ends will...
Definition router.h:237
@ nudgeSharedPathsWithCommonEndPoint
This option determines whether intermediate segments of connectors that are attached to common endpoi...
Definition router.h:285
@ penaliseOrthogonalSharedPathsAtConnEnds
This option penalises and attempts to reroute orthogonal shared connector paths terminating at a comm...
Definition router.h:228
@ nudgeOrthogonalSegmentsConnectedToShapes
This option causes the final segments of connectors, which are attached to shapes,...
Definition router.h:190
void improveOrthogonalRoutes(Router *router)
std::set< ConnCostRef, CmpConnCostRef > ConnCostRefSet
Definition router.cpp:1051
bool inPolyGen(const PolygonInterface &argpoly, const Point &q)
Definition geometry.cpp:380
@ ConnChange
Definition actioninfo.h:53
@ JunctionMove
Definition actioninfo.h:50
@ ShapeAdd
Definition actioninfo.h:48
@ ShapeRemove
Definition actioninfo.h:49
@ JunctionRemove
Definition actioninfo.h:52
@ ShapeMove
Definition actioninfo.h:47
@ JunctionAdd
Definition actioninfo.h:51
@ ConnectionPinChange
Definition actioninfo.h:54
Contains the interface for the Router class.
Contains the interface for the ShapeRef class.
The HyperedgeNewAndDeletedObjectLists class stores lists of objects created and deleted during hypere...
Definition hyperedge.h:80
ConnRefList deletedConnectorList
A list of deleted connectors.
Definition hyperedge.h:91
int index
Contains the interface for the ClusterRef class.