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-2011  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 <cmath>
27 
28 #include "libavoid/debug.h"
29 #include "libavoid/graph.h"
30 #include "libavoid/connector.h"
31 #include "libavoid/geometry.h"
32 #include "libavoid/timer.h"
33 #include "libavoid/vertices.h"
34 #include "libavoid/router.h"
35 #include "libavoid/assertions.h"
36 
37 
38 using std::pair;
39 
40 namespace Avoid {
41 
42 
EdgeInf(VertInf * v1,VertInf * v2,const bool orthogonal)43 EdgeInf::EdgeInf(VertInf *v1, VertInf *v2, const bool orthogonal)
44     : lstPrev(nullptr),
45       lstNext(nullptr),
46       m_router(nullptr),
47       m_blocker(0),
48       m_added(false),
49       m_visible(false),
50       m_orthogonal(orthogonal),
51       m_isHyperedgeSegment(false),
52       m_disabled(false),
53       m_vert1(v1),
54       m_vert2(v2),
55       m_dist(-1)
56 {
57     // Not passed nullptr values.
58     COLA_ASSERT(v1 && v2);
59 
60     // We are in the same instance
61     COLA_ASSERT(m_vert1->_router == m_vert2->_router);
62     m_router = m_vert1->_router;
63 
64     m_conns.clear();
65 }
66 
67 
~EdgeInf()68 EdgeInf::~EdgeInf()
69 {
70     if (m_added)
71     {
72         makeInactive();
73     }
74 }
75 
76 
77 // Gives an order value between 0 and 3 for the point c, given the last
78 // segment was from a to b.  Returns the following value:
79 //    0 : Point c is directly backwards from point b.
80 //    1 : Point c is a left-hand 90 degree turn.
81 //    2 : Point c is a right-hand 90 degree turn.
82 //    3 : Point c is straight ahead (collinear).
83 //    4 : Point c is not orthogonally positioned.
84 //
orthogTurnOrder(const Point & a,const Point & b,const Point & c)85 static inline int orthogTurnOrder(const Point& a, const Point& b,
86         const Point& c)
87 {
88     if ( ((c.x != b.x) && (c.y != b.y)) || ((a.x != b.x) && (a.y != b.y)) )
89     {
90         // Not orthogonally positioned.
91         return 4;
92     }
93 
94     int direction = vecDir(a, b, c);
95 
96     if (direction > 0)
97     {
98         // Counterclockwise := left
99         return 1;
100     }
101     else if (direction < 0)
102     {
103         // Clockwise := right
104         return 2;
105     }
106 
107     if (b.x == c.x)
108     {
109         if ( ((a.y < b.y) && (c.y < b.y)) ||
110              ((a.y > b.y) && (c.y > b.y)) )
111         {
112             // Behind.
113             return 0;
114         }
115     }
116     else
117     {
118         if ( ((a.x < b.x) && (c.x < b.x)) ||
119              ((a.x > b.x) && (c.x > b.x)) )
120         {
121             // Behind.
122             return 0;
123         }
124     }
125 
126     // Ahead.
127     return 3;
128 }
129 
130 
131 // Returns a less than operation for a set exploration order for orthogonal
132 // searching.  Forward, then left, then right.  Or if there is no previous
133 // point, then the order is north, east, south, then west.
134 // Note: This method assumes the two Edges that share a common point.
rotationLessThan(const VertInf * lastV,const EdgeInf * rhs) const135 bool EdgeInf::rotationLessThan(const VertInf *lastV, const EdgeInf *rhs) const
136 {
137     if ((m_vert1 == rhs->m_vert1) && (m_vert2 == rhs->m_vert2))
138     {
139         // Effectively the same visibility edge, so they are equal.
140         return false;
141     }
142     VertInf *lhsV = nullptr, *rhsV = nullptr, *commonV = nullptr;
143 
144     // Determine common Point and the comparison point on the left- and
145     // the right-hand-side.
146     if (m_vert1 == rhs->m_vert1)
147     {
148         commonV = m_vert1;
149         lhsV = m_vert2;
150         rhsV = rhs->m_vert2;
151     }
152     else if (m_vert1 == rhs->m_vert2)
153     {
154         commonV = m_vert1;
155         lhsV = m_vert2;
156         rhsV = rhs->m_vert1;
157     }
158     else if (m_vert2 == rhs->m_vert1)
159     {
160         commonV = m_vert2;
161         lhsV = m_vert1;
162         rhsV = rhs->m_vert2;
163     }
164     else if (m_vert2 == rhs->m_vert2)
165     {
166         commonV = m_vert2;
167         lhsV = m_vert1;
168         rhsV = rhs->m_vert1;
169     }
170 
171     const Point& lhsPt = lhsV->point;
172     const Point& rhsPt = rhsV->point;
173     const Point& commonPt = commonV->point;
174 
175     // If no lastPt, use one directly to the left;
176     Point lastPt = (lastV) ? lastV->point : Point(commonPt.x - 10,  commonPt.y);
177 
178     int lhsVal = orthogTurnOrder(lastPt, commonPt, lhsPt);
179     int rhsVal = orthogTurnOrder(lastPt, commonPt, rhsPt);
180 
181     return lhsVal < rhsVal;
182 }
183 
184 
makeActive(void)185 void EdgeInf::makeActive(void)
186 {
187     COLA_ASSERT(m_added == false);
188 
189     if (m_orthogonal)
190     {
191         COLA_ASSERT(m_visible);
192         m_router->visOrthogGraph.addEdge(this);
193         m_pos1 = m_vert1->orthogVisList.insert(m_vert1->orthogVisList.begin(), this);
194         m_vert1->orthogVisListSize++;
195         m_pos2 = m_vert2->orthogVisList.insert(m_vert2->orthogVisList.begin(), this);
196         m_vert2->orthogVisListSize++;
197     }
198     else
199     {
200         if (m_visible)
201         {
202             m_router->visGraph.addEdge(this);
203             m_pos1 = m_vert1->visList.insert(m_vert1->visList.begin(), this);
204             m_vert1->visListSize++;
205             m_pos2 = m_vert2->visList.insert(m_vert2->visList.begin(), this);
206             m_vert2->visListSize++;
207         }
208         else // if (invisible)
209         {
210             m_router->invisGraph.addEdge(this);
211             m_pos1 = m_vert1->invisList.insert(m_vert1->invisList.begin(), this);
212             m_vert1->invisListSize++;
213             m_pos2 = m_vert2->invisList.insert(m_vert2->invisList.begin(), this);
214             m_vert2->invisListSize++;
215         }
216     }
217     m_added = true;
218 }
219 
220 
makeInactive(void)221 void EdgeInf::makeInactive(void)
222 {
223     COLA_ASSERT(m_added == true);
224 
225     if (m_orthogonal)
226     {
227         COLA_ASSERT(m_visible);
228         m_router->visOrthogGraph.removeEdge(this);
229         m_vert1->orthogVisList.erase(m_pos1);
230         m_vert1->orthogVisListSize--;
231         m_vert2->orthogVisList.erase(m_pos2);
232         m_vert2->orthogVisListSize--;
233     }
234     else
235     {
236         if (m_visible)
237         {
238             m_router->visGraph.removeEdge(this);
239             m_vert1->visList.erase(m_pos1);
240             m_vert1->visListSize--;
241             m_vert2->visList.erase(m_pos2);
242             m_vert2->visListSize--;
243         }
244         else // if (invisible)
245         {
246             m_router->invisGraph.removeEdge(this);
247             m_vert1->invisList.erase(m_pos1);
248             m_vert1->invisListSize--;
249             m_vert2->invisList.erase(m_pos2);
250             m_vert2->invisListSize--;
251         }
252     }
253     m_blocker = 0;
254     m_conns.clear();
255     m_added = false;
256 }
257 
258 
setDist(double dist)259 void EdgeInf::setDist(double dist)
260 {
261     //COLA_ASSERT(dist != 0);
262 
263     if (m_added && !m_visible)
264     {
265         makeInactive();
266         COLA_ASSERT(!m_added);
267     }
268     if (!m_added)
269     {
270         m_visible = true;
271         makeActive();
272     }
273     m_dist = dist;
274     m_blocker = 0;
275 }
276 
277 
setMtstDist(const double joinCost)278 void EdgeInf::setMtstDist(const double joinCost)
279 {
280     m_mtst_dist = joinCost;
281 }
282 
mtstDist(void) const283 double EdgeInf::mtstDist(void) const
284 {
285     return m_mtst_dist;
286 }
287 
isHyperedgeSegment(void) const288 bool EdgeInf::isHyperedgeSegment(void) const
289 {
290     return m_isHyperedgeSegment;
291 }
292 
isDisabled(void) const293 bool EdgeInf::isDisabled(void) const
294 {
295     return m_disabled;
296 }
297 
setDisabled(const bool disabled)298 void EdgeInf::setDisabled(const bool disabled)
299 {
300     m_disabled = disabled;
301 }
302 
setHyperedgeSegment(const bool hyperedge)303 void EdgeInf::setHyperedgeSegment(const bool hyperedge)
304 {
305     m_isHyperedgeSegment = hyperedge;
306 }
307 
added(void)308 bool EdgeInf::added(void)
309 {
310     return m_added;
311 }
312 
blocker(void) const313 int EdgeInf::blocker(void) const
314 {
315     return m_blocker;
316 }
317 
alertConns(void)318 void EdgeInf::alertConns(void)
319 {
320     FlagList::iterator finish = m_conns.end();
321     for (FlagList::iterator i = m_conns.begin(); i != finish; ++i)
322     {
323         *(*i) = true;
324     }
325     m_conns.clear();
326 }
327 
328 
addConn(bool * flag)329 void EdgeInf::addConn(bool *flag)
330 {
331     m_conns.push_back(flag);
332 }
333 
334 
addCycleBlocker(void)335 void EdgeInf::addCycleBlocker(void)
336 {
337     // Needs to be in invisibility graph.
338     addBlocker(-1);
339 }
340 
341 
addBlocker(int b)342 void EdgeInf::addBlocker(int b)
343 {
344     COLA_ASSERT(m_router->InvisibilityGrph);
345 
346     if (m_added && m_visible)
347     {
348         makeInactive();
349         COLA_ASSERT(!m_added);
350     }
351     if (!m_added)
352     {
353         m_visible = false;
354         makeActive();
355     }
356     m_dist = 0;
357     m_blocker = b;
358 }
359 
360 
ids(void) const361 pair<VertID, VertID> EdgeInf::ids(void) const
362 {
363     return std::make_pair(m_vert1->id, m_vert2->id);
364 }
365 
366 
points(void) const367 pair<Point, Point> EdgeInf::points(void) const
368 {
369     return std::make_pair(m_vert1->point, m_vert2->point);
370 }
371 
372 
db_print(void)373 void EdgeInf::db_print(void)
374 {
375     db_printf("Edge(");
376     m_vert1->id.db_print();
377     db_printf(",");
378     m_vert2->id.db_print();
379     db_printf(")\n");
380 }
381 
382 
checkVis(void)383 void EdgeInf::checkVis(void)
384 {
385     if (m_added && !m_visible)
386     {
387         db_printf("\tChecking visibility for existing invisibility edge..."
388                 "\n\t\t");
389         db_print();
390     }
391     else if (m_added && m_visible)
392     {
393         db_printf("\tChecking visibility for existing visibility edge..."
394                 "\n\t\t");
395         db_print();
396     }
397 
398     int blocker = 0;
399     bool cone1 = true;
400     bool cone2 = true;
401 
402     VertInf *i = m_vert1;
403     VertInf *j = m_vert2;
404     const VertID& iID = i->id;
405     const VertID& jID = j->id;
406     const Point& iPoint = i->point;
407     const Point& jPoint = j->point;
408 
409     m_router->st_checked_edges++;
410 
411     if (!(iID.isConnPt()))
412     {
413         cone1 = inValidRegion(m_router->IgnoreRegions, i->shPrev->point,
414                 iPoint, i->shNext->point, jPoint);
415     }
416     else if (m_router->IgnoreRegions == false)
417     {
418         // If Ignoring regions then this case is already caught by
419         // the invalid regions, so only check it when not ignoring
420         // regions.
421         ShapeSet& ss = m_router->contains[iID];
422 
423         if (!(jID.isConnPt()) && (ss.find(jID.objID) != ss.end()))
424         {
425             db_printf("1: Edge of bounding shape\n");
426             // Don't even check this edge, it should be zero,
427             // since a point in a shape can't see it's corners
428             cone1 = false;
429         }
430     }
431 
432     if (cone1)
433     {
434         // If outside the first cone, don't even bother checking.
435         if (!(jID.isConnPt()))
436         {
437             cone2 = inValidRegion(m_router->IgnoreRegions, j->shPrev->point,
438                     jPoint, j->shNext->point, iPoint);
439         }
440         else if (m_router->IgnoreRegions == false)
441         {
442             // If Ignoring regions then this case is already caught by
443             // the invalid regions, so only check it when not ignoring
444             // regions.
445             ShapeSet& ss = m_router->contains[jID];
446 
447             if (!(iID.isConnPt()) && (ss.find(iID.objID) != ss.end()))
448             {
449                 db_printf("2: Edge of bounding shape\n");
450                 // Don't even check this edge, it should be zero,
451                 // since a point in a shape can't see it's corners
452                 cone2 = false;
453             }
454         }
455     }
456 
457     if (cone1 && cone2 && ((blocker = firstBlocker()) == 0))
458     {
459 
460         // if i and j see each other, add edge
461         db_printf("\tSetting visibility edge... \n\t\t");
462         db_print();
463 
464         double d = euclideanDist(iPoint, jPoint);
465 
466         setDist(d);
467 
468     }
469     else if (m_router->InvisibilityGrph)
470     {
471 #if 0
472         db_printf("%d, %d, %d\n", cone1, cone2, blocker);
473         db_printf("\t(%d, %d)--(%d, %d)\n", (int) iInfo.point.x,
474                 (int) iInfo.point.y, (int) jInfo.point.x,
475                 (int) jInfo.point.y);
476 #endif
477 
478         // if i and j can't see each other, add blank edge
479         db_printf("\tSetting invisibility edge... \n\t\t");
480         db_print();
481         addBlocker(blocker);
482     }
483 }
484 
485 
firstBlocker(void)486 int EdgeInf::firstBlocker(void)
487 {
488     ShapeSet ss = ShapeSet();
489 
490     Point& pti = m_vert1->point;
491     Point& ptj = m_vert2->point;
492     VertID& iID = m_vert1->id;
493     VertID& jID = m_vert2->id;
494 
495     ContainsMap &contains = m_router->contains;
496     if (iID.isConnPt())
497     {
498         ss.insert(contains[iID].begin(), contains[iID].end());
499     }
500     if (jID.isConnPt())
501     {
502         ss.insert(contains[jID].begin(), contains[jID].end());
503     }
504 
505     VertInf *last = m_router->vertices.end();
506     unsigned int lastId = 0;
507     bool seenIntersectionAtEndpoint = false;
508     for (VertInf *k = m_router->vertices.shapesBegin(); k != last; )
509     {
510         VertID kID = k->id;
511         if (k->id == dummyOrthogID)
512         {
513             // Don't include orthogonal dummy vertices.
514             k = k->lstNext;
515             continue;
516         }
517         if (kID.objID != lastId)
518         {
519             if ((ss.find(kID.objID) != ss.end()))
520             {
521                 unsigned int shapeID = kID.objID;
522                 db_printf("Endpoint is inside shape %u so ignore shape "
523                         "edges.\n", kID.objID);
524                 // One of the endpoints is inside this shape so ignore it.
525                 while ((k != last) && (k->id.objID == shapeID))
526                 {
527                     // And skip the other vertices from this shape.
528                     k = k->lstNext;
529                 }
530                 continue;
531             }
532             seenIntersectionAtEndpoint = false;
533             lastId = kID.objID;
534         }
535         Point& kPoint = k->point;
536         Point& kPrevPoint = k->shPrev->point;
537         if (segmentShapeIntersect(pti, ptj, kPrevPoint, kPoint,
538                     seenIntersectionAtEndpoint))
539         {
540             ss.clear();
541             return kID.objID;
542         }
543         k = k->lstNext;
544     }
545     ss.clear();
546     return 0;
547 }
548 
549 
isBetween(VertInf * i,VertInf * j)550 bool EdgeInf::isBetween(VertInf *i, VertInf *j)
551 {
552     if ( ((i == m_vert1) && (j == m_vert2)) ||
553          ((i == m_vert2) && (j == m_vert1)) )
554     {
555         return true;
556     }
557     return false;
558 }
559 
560 
561     // Returns true if this edge is a vertical or horizontal line segment.
isOrthogonal(void) const562 bool EdgeInf::isOrthogonal(void) const
563 {
564     return ((m_vert1->point.x == m_vert2->point.x) ||
565             (m_vert1->point.y == m_vert2->point.y));
566 }
567 
568 
isDummyConnection(void) const569 bool EdgeInf::isDummyConnection(void) const
570 {
571     // This is a dummy edge from a shape centre to
572     // a set of its ShapeConnectionPins.
573     return ((m_vert1->id.isConnectionPin() && m_vert2->id.isConnPt()) ||
574             (m_vert2->id.isConnectionPin() && m_vert1->id.isConnPt()));
575 }
576 
577 
otherVert(const VertInf * vert) const578 VertInf *EdgeInf::otherVert(const VertInf *vert) const
579 {
580     COLA_ASSERT((vert == m_vert1) || (vert == m_vert2));
581 
582     return (vert == m_vert1) ? m_vert2 : m_vert1;
583 }
584 
585 
checkEdgeVisibility(VertInf * i,VertInf * j,bool knownNew)586 EdgeInf *EdgeInf::checkEdgeVisibility(VertInf *i, VertInf *j, bool knownNew)
587 {
588     // This is for polyline routing, so check we're not
589     // considering orthogonal vertices.
590     COLA_ASSERT(i->id != dummyOrthogID);
591     COLA_ASSERT(j->id != dummyOrthogID);
592 
593     Router *router = i->_router;
594     EdgeInf *edge = nullptr;
595 
596     if (knownNew)
597     {
598         COLA_ASSERT(existingEdge(i, j) == nullptr);
599         edge = new EdgeInf(i, j);
600     }
601     else
602     {
603         edge = existingEdge(i, j);
604         if (edge == nullptr)
605         {
606             edge = new EdgeInf(i, j);
607         }
608     }
609     edge->checkVis();
610     if (!(edge->m_added) && !(router->InvisibilityGrph))
611     {
612         delete edge;
613         edge = nullptr;
614     }
615 
616     return edge;
617 }
618 
619 
620     // XXX: This function is inefficient, and shouldn't even really be
621     //      required.
existingEdge(VertInf * i,VertInf * j)622 EdgeInf *EdgeInf::existingEdge(VertInf *i, VertInf *j)
623 {
624     VertInf *selected = nullptr;
625 
626     // Look through poly-line visibility edges.
627     selected = (i->visListSize <= j->visListSize) ? i : j;
628     EdgeInfList& visList = selected->visList;
629     EdgeInfList::const_iterator finish = visList.end();
630     for (EdgeInfList::const_iterator edge = visList.begin(); edge != finish;
631             ++edge)
632     {
633         if ((*edge)->isBetween(i, j))
634         {
635             return (*edge);
636         }
637     }
638 
639     // Look through orthogonal visibility edges.
640     selected = (i->orthogVisListSize <= j->orthogVisListSize) ? i : j;
641     EdgeInfList& orthogVisList = selected->orthogVisList;
642     finish = orthogVisList.end();
643     for (EdgeInfList::const_iterator edge = orthogVisList.begin();
644             edge != finish; ++edge)
645     {
646         if ((*edge)->isBetween(i, j))
647         {
648             return (*edge);
649         }
650     }
651 
652     // Look through poly-line invisibility edges.
653     selected = (i->invisListSize <= j->invisListSize) ? i : j;
654     EdgeInfList& invisList = selected->invisList;
655     finish = invisList.end();
656     for (EdgeInfList::const_iterator edge = invisList.begin(); edge != finish;
657             ++edge)
658     {
659         if ((*edge)->isBetween(i, j))
660         {
661             return (*edge);
662         }
663     }
664 
665     return nullptr;
666 }
667 
668 
669 //===========================================================================
670 
671 
EdgeList(bool orthogonal)672 EdgeList::EdgeList(bool orthogonal)
673     : m_orthogonal(orthogonal),
674       m_first_edge(nullptr),
675       m_last_edge(nullptr),
676       m_count(0)
677 {
678 }
679 
680 
~EdgeList()681 EdgeList::~EdgeList()
682 {
683     clear();
684 }
685 
686 
clear(void)687 void EdgeList::clear(void)
688 {
689     while (m_first_edge)
690     {
691         // The Edge destructor results in EdgeList:::removeEdge() being called
692         // for this edge and m_first_edge being updated to the subsequent edge
693         // in the EdgeList.
694         delete m_first_edge;
695     }
696     COLA_ASSERT(m_count == 0);
697     m_last_edge = nullptr;
698 }
699 
700 
size(void) const701 int EdgeList::size(void) const
702 {
703     return m_count;
704 }
705 
706 
addEdge(EdgeInf * edge)707 void EdgeList::addEdge(EdgeInf *edge)
708 {
709     // Dummy connections for ShapeConnectionPins won't be orthogonal,
710     // even in the orthogonal visibility graph.
711     COLA_UNUSED(m_orthogonal);
712     COLA_ASSERT(!m_orthogonal || edge->isOrthogonal() ||
713             edge->isDummyConnection());
714 
715     if (m_first_edge == nullptr)
716     {
717         COLA_ASSERT(m_last_edge == nullptr);
718 
719         m_last_edge = edge;
720         m_first_edge = edge;
721 
722         edge->lstPrev = nullptr;
723         edge->lstNext = nullptr;
724     }
725     else
726     {
727         COLA_ASSERT(m_last_edge != nullptr);
728 
729         m_last_edge->lstNext = edge;
730         edge->lstPrev = m_last_edge;
731 
732         m_last_edge = edge;
733 
734         edge->lstNext = nullptr;
735     }
736     m_count++;
737 }
738 
739 
removeEdge(EdgeInf * edge)740 void EdgeList::removeEdge(EdgeInf *edge)
741 {
742     if (edge->lstPrev)
743     {
744         edge->lstPrev->lstNext = edge->lstNext;
745     }
746     if (edge->lstNext)
747     {
748         edge->lstNext->lstPrev = edge->lstPrev;
749     }
750     if (edge == m_last_edge)
751     {
752         m_last_edge = edge->lstPrev;
753         if (edge == m_first_edge)
754         {
755             m_first_edge = nullptr;
756         }
757     }
758     else if (edge == m_first_edge)
759     {
760         m_first_edge = edge->lstNext;
761     }
762 
763 
764     edge->lstPrev = nullptr;
765     edge->lstNext = nullptr;
766 
767     m_count--;
768 }
769 
770 
begin(void)771 EdgeInf *EdgeList::begin(void)
772 {
773     return m_first_edge;
774 }
775 
776 
end(void)777 EdgeInf *EdgeList::end(void)
778 {
779     return nullptr;
780 }
781 
782 
783 }
784 
785 
786