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