Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
scanline.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) 2009-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#include <cfloat>
26#include <algorithm>
27
28#include "libavoid/scanline.h"
29#include "libavoid/obstacle.h"
30#include "libavoid/vertices.h"
31#include "libavoid/connector.h"
32#include "libavoid/junction.h"
33#include "libavoid/router.h"
34
35namespace Avoid {
36
37bool CmpNodePos::operator() (const Node* u, const Node* v) const
38{
39 if (u->pos != v->pos)
40 {
41 return u->pos < v->pos;
42 }
43
44 // Use the pointers to the base objects to differentiate them.
45 void *up = (u->v) ? (void *) u->v :
46 ((u->c) ? (void *) u->c : (void *) u->ss);
47 void *vp = (v->v) ? (void *) v->v :
48 ((v->c) ? (void *) v->c : (void *) v->ss);
49 return up < vp;
50}
51
52
53Node::Node(Obstacle *v, const double p)
54 : v(v),
55 c(nullptr),
56 ss(nullptr),
57 pos(p),
58 firstAbove(nullptr),
59 firstBelow(nullptr)
60{
61 Box bBox = v->routingBox();
62 min[XDIM] = bBox.min.x;
63 min[YDIM] = bBox.min.y;
64 max[XDIM] = bBox.max.x;
65 max[YDIM] = bBox.max.y;
66 //COLA_ASSERT(r->width()<1e40);
67}
68
69Node::Node(VertInf *c, const double p)
70 : v(nullptr),
71 c(c),
72 ss(nullptr),
73 pos(p),
74 firstAbove(nullptr),
75 firstBelow(nullptr)
76{
77 min[XDIM] = max[XDIM] = c->point.x;
78 min[YDIM] = max[YDIM] = c->point.y;
79}
80
81Node::Node(ShiftSegment *ss, const double p)
82 : v(nullptr),
83 c(nullptr),
84 ss(ss),
85 pos(p),
86 firstAbove(nullptr),
87 firstBelow(nullptr)
88{
89 // These values shouldn't ever be used, so they don't matter.
90 min[XDIM] = max[XDIM] = min[YDIM] = max[YDIM] = 0;
91}
92
94{
95}
96
97// Find the first Node above in the scanline that is a shape edge,
98// and does not have an open or close event at this position (meaning
99// it is just about to be removed).
100double Node::firstObstacleAbove(size_t dim)
101{
102 Node *curr = firstAbove;
103 while (curr && (curr->ss || (curr->max[dim] > pos)))
104 {
105 curr = curr->firstAbove;
106 }
107
108 if (curr)
109 {
110 return curr->max[dim];
111 }
112 return -DBL_MAX;
113}
114
115// Find the first Node below in the scanline that is a shape edge,
116// and does not have an open or close event at this position (meaning
117// it is just about to be removed).
118double Node::firstObstacleBelow(size_t dim)
119{
120 Node *curr = firstBelow;
121 while (curr && (curr->ss || (curr->min[dim] < pos)))
122 {
123 curr = curr->firstBelow;
124 }
125
126 if (curr)
127 {
128 return curr->min[dim];
129 }
130 return DBL_MAX;
131}
132
133// Mark all connector segments above in the scanline as being able
134// to see to this shape edge.
136{
137 Node *curr = firstAbove;
138 while (curr && (curr->ss || (curr->pos > min[dim])))
139 {
140 if (curr->ss && (curr->pos <= min[dim]))
141 {
142 curr->ss->maxSpaceLimit =
143 std::min(min[dim], curr->ss->maxSpaceLimit);
144 }
145 curr = curr->firstAbove;
146 }
147}
148
149// Mark all connector segments below in the scanline as being able
150// to see to this shape edge.
152{
153 Node *curr = firstBelow;
154 while (curr && (curr->ss || (curr->pos < max[dim])))
155 {
156 if (curr->ss && (curr->pos >= max[dim]))
157 {
158 curr->ss->minSpaceLimit =
159 std::max(max[dim], curr->ss->minSpaceLimit);
160 }
161 curr = curr->firstBelow;
162 }
163}
164
165void Node::findFirstPointAboveAndBelow(const size_t dim, const double linePos,
166 double& firstAbovePos, double& firstBelowPos,
167 double& lastAbovePos, double& lastBelowPos)
168{
169 firstAbovePos = -DBL_MAX;
170 firstBelowPos = DBL_MAX;
171 // We start looking left from the right side of the shape,
172 // and vice versa.
173 lastAbovePos = max[dim];
174 lastBelowPos = min[dim];
175
176 Node *curr = nullptr;
177 bool eventsAtSamePos = false;
178 for (int direction = 0; direction < 2; ++direction)
179 {
180 // Look for obstacles in one direction, then the other.
181 curr = (direction == 0) ? firstAbove: firstBelow;
182
183 while (curr)
184 {
185 // The events are at a shared beginning or end of a shape or
186 // connection point. Note, connection points have the same
187 // min and max value in the !dim dimension.
188 eventsAtSamePos =
189 (((linePos == max[!dim]) &&
190 (linePos == curr->max[!dim])) ||
191 ((linePos == min[!dim]) &&
192 (linePos == curr->min[!dim])));
193
194 if (curr->max[dim] <= min[dim])
195 {
196 // Curr shape is completely to the left,
197 // so add it's right side as a limit
198 firstAbovePos = std::max(curr->max[dim], firstAbovePos);
199 }
200 else if (curr->min[dim] >= max[dim])
201 {
202 // Curr shape is completely to the right,
203 // so add it's left side as a limit
204 firstBelowPos = std::min(curr->min[dim], firstBelowPos);
205 }
206 else if (!eventsAtSamePos)
207 {
208 // Shapes that open or close at the same position do not
209 // block visibility, so if they are not at same position
210 // determine where they overlap.
211 lastAbovePos = std::min(curr->min[dim], lastAbovePos);
212 lastBelowPos = std::max(curr->max[dim], lastBelowPos);
213 }
214 curr = (direction == 0) ? curr->firstAbove : curr->firstBelow;
215 }
216 }
217}
218
219double Node::firstPointAbove(size_t dim)
220{
221 // We are looking for the first obstacle above this position,
222 // though we ignore shape edges if this point is inline with
223 // the edge of the obstacle. That is, points have visibility
224 // along the edge of shapes.
225 size_t altDim = (dim + 1) % 2;
226 double result = -DBL_MAX;
227 Node *curr = firstAbove;
228 while (curr)
229 {
230 bool inLineWithEdge = (min[altDim] == curr->min[altDim]) ||
231 (min[altDim] == curr->max[altDim]);
232 if ( ! inLineWithEdge && (curr->max[dim] <= pos) )
233 {
234 result = std::max(curr->max[dim], result);
235 }
236 curr = curr->firstAbove;
237 }
238 return result;
239}
240
241double Node::firstPointBelow(size_t dim)
242{
243 // We are looking for the first obstacle below this position,
244 // though we ignore shape edges if this point is inline with
245 // the edge of the obstacle. That is, points have visibility
246 // along the edge of shapes.
247 size_t altDim = (dim + 1) % 2;
248 double result = DBL_MAX;
249 Node *curr = firstBelow;
250 while (curr)
251 {
252 bool inLineWithEdge = (min[altDim] == curr->min[altDim]) ||
253 (min[altDim] == curr->max[altDim]);
254 if ( ! inLineWithEdge && (curr->min[dim] >= pos) )
255 {
256 result = std::min(curr->min[dim], result);
257 }
258 curr = curr->firstBelow;
259 }
260 return result;
261}
262
263// This is a bit inefficient, but we won't need to do it once we have
264// connection points.
265bool Node::isInsideShape(size_t dimension)
266{
267 for (Node *curr = firstBelow; curr; curr = curr->firstBelow)
268 {
269 if ((curr->min[dimension] < pos) && (pos < curr->max[dimension]))
270 {
271 return true;
272 }
273 }
274 for (Node *curr = firstAbove; curr; curr = curr->firstAbove)
275 {
276 if ((curr->min[dimension] < pos) && (pos < curr->max[dimension]))
277 {
278 return true;
279 }
280 }
281 return false;
282}
283
284
285Event::Event(EventType t, Node *v, double p)
286 : type(t),
287 v(v),
288 pos(p)
289{
290}
291
292
293// Used for quicksort. Must return <0, 0, or >0.
294int compare_events(const void *a, const void *b)
295{
296 Event *ea = *(Event**) a;
297 Event *eb = *(Event**) b;
298 if (ea->pos != eb->pos)
299 {
300 return (ea->pos < eb->pos) ? -1 : 1;
301 }
302 if (ea->type != eb->type)
303 {
304 return ea->type - eb->type;
305 }
306 COLA_ASSERT(ea->v != eb->v);
307 return (int)(ea->v - eb->v);
308}
309
310
312{
313 for (ConnRefList::const_iterator curr = router->connRefs.begin();
314 curr != router->connRefs.end(); ++curr)
315 {
316 ConnRef *conn = *curr;
317 if (conn->routingType() != ConnType_Orthogonal)
318 {
319 continue;
320 }
321
322 PolyLine& displayRoute = conn->displayRoute();
323 std::vector<Checkpoint> checkpoints = conn->routingCheckpoints();
324
325 // Initialise checkpoint vector and set to false. There will be
326 // one entry for each *segment* in the path, and the value indicates
327 // whether the segment is affected by a checkpoint.
328 displayRoute.checkpointsOnRoute =
329 std::vector<std::pair<size_t, Point> >();
330
331 for (size_t ind = 0; ind < displayRoute.size(); ++ind)
332 {
333 if (ind > 0)
334 {
335 for (size_t cpi = 0; cpi < checkpoints.size(); ++cpi)
336 {
337 if (pointOnLine(displayRoute.ps[ind - 1],
338 displayRoute.ps[ind], checkpoints[cpi].point) )
339 {
340 // The checkpoint is on a segment.
341 displayRoute.checkpointsOnRoute.push_back(
342 std::make_pair((ind * 2) - 1,
343 checkpoints[cpi].point));
344 }
345 }
346 }
347
348 for (size_t cpi = 0; cpi < checkpoints.size(); ++cpi)
349 {
350 if (displayRoute.ps[ind].equals(checkpoints[cpi].point))
351 {
352 // The checkpoint is at a bendpoint.
353 displayRoute.checkpointsOnRoute.push_back(
354 std::make_pair(ind * 2, checkpoints[cpi].point));
355 }
356 }
357 }
358 }
359}
360
361
363{
364 for (ConnRefList::const_iterator curr = router->connRefs.begin();
365 curr != router->connRefs.end(); ++curr)
366 {
367 ConnRef *conn = *curr;
368 if (conn->routingType() != ConnType_Orthogonal)
369 {
370 continue;
371 }
372
373 // Clear the cache.
374 PolyLine& displayRoute = conn->displayRoute();
375 displayRoute.checkpointsOnRoute.clear();
376 }
377}
378
379
380// Processes sweep events used to determine each horizontal and vertical
381// line segment in a connector's channel of visibility.
382// Four calls to this function are made at each position by the scanline:
383// 1) Handle all Close event processing.
384// 2) Remove Close event objects from the scanline.
385// 3) Add Open event objects to the scanline.
386// 4) Handle all Open event processing.
387//
388static void processShiftEvent(NodeSet& scanline, Event *e, size_t dim,
389 unsigned int pass)
390{
391 Node *v = e->v;
392
393 if ( ((pass == 3) && (e->type == Open)) ||
394 ((pass == 3) && (e->type == SegOpen)) )
395 {
396 std::pair<NodeSet::iterator, bool> result = scanline.insert(v);
397 v->iter = result.first;
398 COLA_ASSERT(result.second);
399
400 NodeSet::iterator it = v->iter;
401 // Work out neighbours
402 if (it != scanline.begin())
403 {
404 Node *u = *(--it);
405 v->firstAbove = u;
406 u->firstBelow = v;
407 }
408 it = v->iter;
409 if (++it != scanline.end())
410 {
411 Node *u = *it;
412 v->firstBelow = u;
413 u->firstAbove = v;
414 }
415 }
416
417 if ( ((pass == 4) && (e->type == Open)) ||
418 ((pass == 4) && (e->type == SegOpen)) ||
419 ((pass == 1) && (e->type == SegClose)) ||
420 ((pass == 1) && (e->type == Close)) )
421 {
422 if (v->ss)
423 {
424 // As far as we can see.
425 double minLimit = v->firstObstacleAbove(dim);
426 double maxLimit = v->firstObstacleBelow(dim);
427
428 v->ss->minSpaceLimit =
429 std::max(minLimit, v->ss->minSpaceLimit);
430 v->ss->maxSpaceLimit =
431 std::min(maxLimit, v->ss->maxSpaceLimit);
432 }
433 else
434 {
435 v->markShiftSegmentsAbove(dim);
436 v->markShiftSegmentsBelow(dim);
437 }
438 }
439
440 if ( ((pass == 2) && (e->type == SegClose)) ||
441 ((pass == 2) && (e->type == Close)) )
442 {
443 // Clean up neighbour pointers.
444 Node *l = v->firstAbove, *r = v->firstBelow;
445 if (l != nullptr)
446 {
447 l->firstBelow = v->firstBelow;
448 }
449 if (r != nullptr)
450 {
451 r->firstAbove = v->firstAbove;
452 }
453
454 size_t result;
455 result = scanline.erase(v);
456 COLA_ASSERT(result == 1);
457 COLA_UNUSED(result); // Avoid warning.
458 delete v;
459 }
460}
461
463 const size_t dim, ShiftSegmentList& segmentList)
464{
465 if (segmentList.empty())
466 {
467 // There are no segments, so we can just return now.
468 return;
469 }
470
471 // Do a sweep to determine space for shifting segments.
472 size_t altDim = (dim + 1) % 2;
473 const size_t n = router->m_obstacles.size();
474 const size_t cpn = segmentList.size();
475 // Set up the events for the sweep.
476 size_t totalEvents = 2 * (n + cpn);
477 Event **events = new Event*[totalEvents];
478 unsigned ctr = 0;
479 ObstacleList::iterator obstacleIt = router->m_obstacles.begin();
480 for (unsigned i = 0; i < n; i++)
481 {
482 Obstacle *obstacle = *obstacleIt;
483 JunctionRef *junction = dynamic_cast<JunctionRef *> (obstacle);
484 if (junction && ! junction->positionFixed())
485 {
486 // Junctions that are free to move are not treated as obstacles.
487 ++obstacleIt;
488 totalEvents -= 2;
489 continue;
490 }
491 Box bBox = obstacle->routingBox();
492 Point min = bBox.min;
493 Point max = bBox.max;
494 double mid = min[dim] + ((max[dim] - min[dim]) / 2);
495 Node *v = new Node(obstacle, mid);
496 events[ctr++] = new Event(Open, v, min[altDim]);
497 events[ctr++] = new Event(Close, v, max[altDim]);
498
499 ++obstacleIt;
500 }
501 for (ShiftSegmentList::iterator curr = segmentList.begin();
502 curr != segmentList.end(); ++curr)
503 {
504 const Point& lowPt = (*curr)->lowPoint();
505 const Point& highPt = (*curr)->highPoint();
506
507 COLA_ASSERT(lowPt[dim] == highPt[dim]);
508 COLA_ASSERT(lowPt[altDim] < highPt[altDim]);
509 Node *v = new Node(*curr, lowPt[dim]);
510 events[ctr++] = new Event(SegOpen, v, lowPt[altDim]);
511 events[ctr++] = new Event(SegClose, v, highPt[altDim]);
512 }
513 qsort((Event*)events, (size_t) totalEvents, sizeof(Event*), compare_events);
514
515 // Process the sweep.
516 // We do multiple passes over sections of the list so we can add relevant
517 // entries to the scanline that might follow, before process them.
518 NodeSet scanline;
519 double thisPos = (totalEvents > 0) ? events[0]->pos : 0;
520 unsigned int posStartIndex = 0;
521 unsigned int posFinishIndex = 0;
522 for (unsigned i = 0; i <= totalEvents; ++i)
523 {
524 // If we have finished the current scanline or all events, then we
525 // process the events on the current scanline in a couple of passes.
526 if ((i == totalEvents) || (events[i]->pos != thisPos))
527 {
528 posFinishIndex = i;
529 for (int pass = 2; pass <= 4; ++pass)
530 {
531 for (unsigned j = posStartIndex; j < posFinishIndex; ++j)
532 {
533 processShiftEvent(scanline, events[j], dim, pass);
534 }
535 }
536
537 if (i == totalEvents)
538 {
539 // We have cleaned up, so we can now break out of loop.
540 break;
541 }
542
543 thisPos = events[i]->pos;
544 posStartIndex = i;
545 }
546
547 // Do the first sweep event handling -- building the correct
548 // structure of the scanline.
549 const int pass = 1;
550 processShiftEvent(scanline, events[i], dim, pass);
551 }
552 COLA_ASSERT(scanline.size() == 0);
553 for (unsigned i = 0; i < totalEvents; ++i)
554 {
555 delete events[i];
556 }
557 delete [] events;
558}
559
560
561}
562
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 ConnRef class represents a connector object.
Definition connector.h:132
ConnType routingType(void) const
Returns the type of routing performed for this connector.
std::vector< Checkpoint > routingCheckpoints(void) const
Returns the current set of routing checkpoints for this connector.
PolyLine & displayRoute(void)
Returns a reference to the current display version of the route for the connector.
The JunctionRef class represents a fixed or free-floating point that connectors can be attached to.
Definition junction.h:58
bool positionFixed(void) const
Returns whether this junction has a fixed position (that can't be moved by the Router during routing)...
Definition junction.cpp:90
void findFirstPointAboveAndBelow(const size_t dim, const double linePos, double &firstAbovePos, double &firstBelowPos, double &lastAbovePos, double &lastBelowPos)
Definition scanline.cpp:165
double min[2]
Definition scanline.h:86
double max[2]
Definition scanline.h:86
ShiftSegment * ss
Definition scanline.h:84
Obstacle * v
Definition scanline.h:82
double firstObstacleBelow(size_t dim)
Definition scanline.cpp:118
double firstPointBelow(size_t dim)
Definition scanline.cpp:241
double firstPointAbove(size_t dim)
Definition scanline.cpp:219
Node(Obstacle *v, const double p)
Definition scanline.cpp:53
Node * firstAbove
Definition scanline.h:87
Node * firstBelow
Definition scanline.h:87
double pos
Definition scanline.h:85
bool isInsideShape(size_t dimension)
Definition scanline.cpp:265
double firstObstacleAbove(size_t dim)
Definition scanline.cpp:100
void markShiftSegmentsAbove(size_t dim)
Definition scanline.cpp:135
VertInf * c
Definition scanline.h:83
virtual ~Node()
Definition scanline.cpp:93
void markShiftSegmentsBelow(size_t dim)
Definition scanline.cpp:151
Box routingBox(void) const
Definition obstacle.cpp:267
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 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.
std::vector< Point > ps
A vector of the points that make up the Polygon.
Definition geomtypes.h:285
std::vector< std::pair< size_t, Point > > checkpointsOnRoute
Definition geomtypes.h:314
The Router class represents a libavoid router instance.
Definition router.h:386
ObstacleList m_obstacles
Definition router.h:401
ConnRefList connRefs
Definition router.h:402
Contains the interface for the ConnRef class.
Css & result
double c[8][4]
Contains the interface for the JunctionRef class.
libavoid: Object-avoiding orthogonal and polyline connector routing library.
int compare_events(const void *a, const void *b)
Definition scanline.cpp:294
static void processShiftEvent(NodeSet &scanline, Event *e, size_t dim, unsigned int pass)
Definition scanline.cpp:388
void clearConnectorRouteCheckpointCache(Router *router)
Definition scanline.cpp:362
@ ConnType_Orthogonal
The connector path will be a shortest-path orthogonal poly-line (only vertical and horizontal line se...
Definition connector.h:61
static const size_t XDIM
Definition geomtypes.h:42
EventType
Definition scanline.h:108
@ SegOpen
Definition scanline.h:110
@ Open
Definition scanline.h:109
@ SegClose
Definition scanline.h:112
@ Close
Definition scanline.h:113
void buildOrthogonalChannelInfo(Router *router, const size_t dim, ShiftSegmentList &segmentList)
Definition scanline.cpp:462
std::set< Node *, CmpNodePos > NodeSet
Definition scanline.h:76
void buildConnectorRouteCheckpointCache(Router *router)
Definition scanline.cpp:311
std::list< ShiftSegment * > ShiftSegmentList
static const size_t YDIM
Definition geomtypes.h:43
bool pointOnLine(const Point &a, const Point &b, const Point &c, const double tolerance)
Definition geometry.cpp:105
Contains the interface for the Obstacle class, the superclass for ShapeRef and JunctionRef.
Contains the interface for the Router class.
bool operator()(const Node *u, const Node *v) const
Definition scanline.cpp:37
EventType type
Definition scanline.h:121
Event(EventType t, Node *v, double p)
Definition scanline.cpp:285
double pos
Definition scanline.h:123