Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

Loading...
Searching...
No Matches
BloatedDual.h
Go to the documentation of this file.
1
31#pragma once
32
33#ifdef OGDF_INCLUDE_CGAL
34
36
37# ifdef OGDF_GEOMETRIC_CR_MIN_DEBUG
38# include <ogdf/basic/Logger.h>
39# define OGDF_GEOMETRIC_BD_ECHO(x) Logger::slout() << "[BD] " << x << std::endl;
40# else
41# define OGDF_GEOMETRIC_BD_ECHO(x)
42# endif
43
44namespace ogdf {
45namespace internal {
46namespace gcm {
47namespace geometry {
48
49template<typename Kernel, bool parallel = false>
50class BloatedDual {
51private:
52 using Point = geometry::Point_t<Kernel>;
54 using ExactKernel = CGAL::Simple_cartesian<CGAL::Gmpq>;
55
56 using BD = graph::BloatedDualGraph<Kernel>;
57 using Node = typename BD::Node;
58 using Edge = typename BD::Edge;
59 using LocalIntersection = typename BD::LocalIntersection;
60
61 inline bool consider_equal(const Point& p, const Point& q) const { return p == q; }
62
63 bool has_point_on_segment(const Segment& reference, const Point& p) {
64 if (reference.has_on(p)) {
65 return true;
66 } else if (consider_equal(p, reference.supporting_line().projection(p))) {
67 auto ex_p = geometry::cast<ExactKernel>(p);
68 auto ex_segment = geometry::cast<ExactKernel>(reference);
69 return ex_segment.has_on(ex_p);
70 }
71
72 return false;
73 }
74
75 inline bool has_endpoint_on_segment(const Segment& reference, const Segment& opposite) {
76 return has_point_on_segment(reference, opposite.source())
77 || has_point_on_segment(reference, opposite.target());
78 }
79
80 std::set<std::pair<unsigned int, unsigned int>> seen;
81
82 void assign_intersections_to_segment(const std::vector<Segment>& segments,
83 const std::vector<Intersection>& intersecting_segments,
84 std::vector<std::vector<unsigned int>>& _segment_to_intersections,
85 std::vector<Point>& intersections) {
86 //TODO set really required?
87 seen.clear();
88 for (unsigned int i = 0; i < intersecting_segments.size(); ++i) {
89 auto& pair = intersecting_segments[i];
90 if (seen.find({pair.seg_a, pair.seg_b}) == seen.end()
91 && seen.find({pair.seg_b, pair.seg_a}) == seen.end()) {
92 _segment_to_intersections[pair.seg_a].push_back(i);
93 if (pair.seg_a != pair.seg_b) {
94 _segment_to_intersections[pair.seg_b].push_back(i);
95 }
96
97 auto& seg_a = segments[pair.seg_a];
98 auto& seg_b = segments[pair.seg_b];
99 if (pair.is_source) {
100 OGDF_ASSERT(pair.seg_a == pair.seg_b);
101 intersections.push_back(seg_a.source());
102 } else if (pair.is_target) {
103 OGDF_ASSERT(pair.seg_a == pair.seg_b);
104 intersections.push_back(seg_a.target());
105 } else if (geometry::have_common_endpoints(seg_a, seg_b)) {
106 intersections.push_back(geometry::get_common_endpoint(seg_a, seg_b));
107 } else {
108 Point p_a = geometry::intersect(seg_a, seg_b);
109 intersections.push_back(p_a);
110 }
111 }
112 }
113 }
114
115 void sort_intersections_along_segment(const std::vector<Segment>& segments,
116 const std::vector<Intersection>& intersecting_segments,
117 std::vector<std::vector<unsigned int>>& _segment_to_intersections,
118 std::vector<Point>& intersections) {
119 for (unsigned int i = 0; i < _segment_to_intersections.size(); ++i) {
120 auto& is = _segment_to_intersections[i];
121
122 auto compare = [&](const unsigned int a, const unsigned int b) {
123 const Point& p_a = intersections[a];
124
125 const Point& p_b = intersections[b];
126
127 auto d_a = CGAL::squared_distance(segments[i].source(), p_a);
128 auto d_b = CGAL::squared_distance(segments[i].source(),
129 p_b); //order a long line with respect to source
130
131 bool comp = d_a < d_b;
132 return comp;
133 };
134
135 if (parallel) {
136 //boost::sort::block_indirect_sort(is.begin(), is.end(), compare);
137 std::sort(is.begin(), is.end(), compare);
138 } else {
139 std::sort(is.begin(), is.end(), compare);
140 }
141 }
142 }
143
144 std::vector<unsigned int> left_intersections;
145 std::vector<unsigned int> right_intersections;
146
147 /* If several segments intersect in on point on @segment only two segments
148 * are necessary to construct the bloated dual. This function filters
149 * all non-relevant segments.
150 */
151 void clean_up(unsigned int segment, const std::vector<Segment>& segments,
152 const std::vector<Intersection>& intersecting_segments //maps intersection id to pair of segments
153 ,
154 const std::vector<Point>& intersections // maps intersection id to intersections
155 ,
156 const std::vector<unsigned int>& intersections_along_segment //maps to intersection_id
157 ,
158 std::vector<LocalIntersection>& clean_intersecting_segments // filtered intersection_id's
159 ) {
160 auto is_proper = [&](const Segment& _segment, const Point& p) {
161 return !consider_equal(_segment.source(), p) && !consider_equal(_segment.target(), p)
163 };
164
165 auto is_proper_left_turn = [&](const Segment& _segment, const Point& p) {
166 return is_proper(_segment, p) && geometry::left_turn(_segment, p);
167 };
168
169
170 auto is_proper_right_turn = [&](const Segment& _segment, const Point& p) {
171 return is_proper(_segment, p) && geometry::right_turn(_segment, p);
172 };
173
174
175 for (unsigned int j = 0; j
176 < intersections_along_segment.size();) { //incrementation is done in the next while loop
177 left_intersections.clear();
178 right_intersections.clear();
179
180 const unsigned int ref_intersection = j;
181
182 int self_intersection_id = -1;
183
184 OGDF_GEOMETRIC_BD_ECHO("reference: " << segment);
185
186 while (j < intersections_along_segment.size()
188 intersections[intersections_along_segment[j]])) { //TODO make robust?
189
191 unsigned int opp = intersecting_segments[intersection_id].opposite(segment);
192
193 bool is_left_turn = is_proper_left_turn(segments[segment], segments[opp].source())
194 || is_proper_left_turn(segments[segment], segments[opp].target());
195 bool is_right_turn = is_proper_right_turn(segments[segment], segments[opp].source())
196 || is_proper_right_turn(segments[segment], segments[opp].target());
197
198
199 if (segment == opp) {
201 } else if (!is_left_turn && !is_right_turn) { //is collinear
202
205 } else {
206 if (is_left_turn) {
208 }
209
210 if (is_right_turn) {
212 }
213 }
214
215 ++j;
216 }
217
218 if (left_intersections.empty() && right_intersections.empty()) {
220 }
221
222 if (left_intersections.size() > 1) {
223 std::sort(left_intersections.begin(), left_intersections.end(),
224 [&](const unsigned int a, const unsigned int b) {
225 auto& seg_a = segments[intersecting_segments[a].opposite(segment)];
226 auto& seg_b = segments[intersecting_segments[b].opposite(segment)];
227
228 int sign_a = 1;
229 int sign_b = 1;
230 if (is_proper_left_turn(segments[segment], seg_a.source())
231 || segments[segment].source() == seg_a.target()) {
232 sign_a = -1;
233 }
234
235 if (is_proper_left_turn(segments[segment], seg_b.source())
236 || segments[segment].source() == seg_b.target()) {
237 sign_b = -1;
238 }
239
240 return geometry::right_turn(seg_a.to_vector() * sign_a,
241 seg_b.to_vector() * sign_b);
242 });
243 }
244
245
246 if (right_intersections.size() > 1) {
247 std::sort(right_intersections.begin(), right_intersections.end(),
248 [&](const unsigned int a, const unsigned int b) {
249 auto& seg_a = segments[intersecting_segments[a].opposite(segment)];
250 auto& seg_b = segments[intersecting_segments[b].opposite(segment)];
251
252 int sign_a = 1;
253 int sign_b = 1;
254
255 if (is_proper_right_turn(segments[segment], seg_a.source())
256 || segments[segment].source() == seg_a.target()) {
257 sign_a = -1;
258 }
259
260 if (is_proper_right_turn(segments[segment], seg_b.source())
261 || segments[segment].source() == seg_b.target()) {
262 sign_b = -1;
263 }
264
265 return geometry::left_turn(seg_a.to_vector() * sign_a,
266 seg_b.to_vector() * sign_b);
267 });
268 }
269
272 li.is_end_point = self_intersection_id >= 0;
273 if (!left_intersections.empty()) {
274 li.left_intersection_ids[0] = left_intersections.front();
275 li.left_intersection_ids[1] = left_intersections.back();
276 }
277
278 if (!right_intersections.empty()) {
279 li.right_intersection_ids[0] = right_intersections.front();
280 li.right_intersection_ids[1] = right_intersections.back();
281 }
282
284 }
285 }
286
288 if (li.left_is_valid() //self intersection is stored on the left side
289 && (bd.intersecting_segments[li.left_intersection_ids[0]].is_source
290 || bd.intersecting_segments[li.left_intersection_ids[0]].is_target)) {
291 return;
292 }
293
294 OGDF_GEOMETRIC_BD_ECHO("---------------------------");
295 OGDF_GEOMETRIC_BD_ECHO("reference segment " << ref_seg);
296
297 // assign segments to left / right, depending on
298 // whether they have an endpoint to left / right of the reference segments;
299
301 consider_equal(bd.segments[ref_seg].source(), bd.get_intersection_point(li));
303 consider_equal(bd.segments[ref_seg].target(), bd.get_intersection_point(li));
305
306 bool source_is_left[2] = {false, false};
307 bool source_is_right[2] = {false, false};
308
309 if (li.left_is_valid()) {
310 unsigned int left_segment_id[2];
311 OGDF_GEOMETRIC_BD_ECHO("ref: " << ref_seg << "; " << bd.segments[ref_seg]);
312 OGDF_GEOMETRIC_BD_ECHO("intersection is source: " << intersection_is_source)
313 OGDF_GEOMETRIC_BD_ECHO("intersection is target: " << intersection_is_target)
314 OGDF_GEOMETRIC_BD_ECHO("proper: " << proper)
315 for (unsigned int i = 0; i < 2; ++i) {
316 left_segment_id[i] =
317 bd.intersecting_segments[li.left_intersection_ids[i]].opposite(ref_seg);
318
319 source_is_left[i] = !consider_equal(bd.segments[left_segment_id[i]].source(),
320 bd.get_intersection_point(li))
321 && geometry::left_turn(bd.segments[ref_seg],
322 bd.segments[left_segment_id[i]].source());
323
324 OGDF_GEOMETRIC_BD_ECHO("\t l" << left_segment_id[i] << "; "
325 << bd.segments[left_segment_id[i]] << ": "
326 << source_is_left[i]);
327 }
328
330 Node u1 = bd.first_left(ref_seg, li.left_intersection_ids[0]);
331 Node v1 = -1;
332
333 if (source_is_left[0]
334 || consider_equal(bd.segments[left_segment_id[0]].target(),
335 bd.get_intersection_point(li))) {
336 v1 = bd.first_right(left_segment_id[0], li.left_intersection_ids[0]);
337 } else {
338 v1 = bd.second_left(left_segment_id[0], li.left_intersection_ids[0]);
339 }
340
341 bd.add_edge(u1, v1, true);
342 }
343
345 Node u2 = bd.second_left(ref_seg, li.left_intersection_ids[0]);
346 Node v2 = -1;
347
348 if (source_is_left[1]
349 || consider_equal(bd.segments[left_segment_id[1]].target(),
350 bd.get_intersection_point(li))) {
351 v2 = bd.first_left(left_segment_id[1], li.left_intersection_ids[1]);
352 } else {
353 v2 = bd.second_right(left_segment_id[1], li.left_intersection_ids[1]);
354 }
355
356 bd.add_edge(u2, v2, true);
357 }
358
359 } else if (proper) {
360 Node u1 = bd.first_left(ref_seg, li.right_intersection_ids[0]);
361 Node v1 = bd.second_left(ref_seg, li.right_intersection_ids[0]);
362 bd.add_edge(u1, v1, true);
363 } else if (intersection_is_source) {
364 auto id = li.right_intersection_ids[0];
365
366 Node u1 = bd.second_left(ref_seg, id);
367 Node v1 = -1;
368 unsigned int opp = bd.intersecting_segments[id].opposite(ref_seg);
369 if (consider_equal(bd.segments[opp].target(), bd.get_intersection_point(li))) {
370 v1 = bd.first_left(opp, id);
371 } else {
372 v1 = bd.second_right(opp, id);
373 }
374
375 bd.add_edge(u1, v1, true);
376
377 } else if (intersection_is_target) {
378 auto id = li.right_intersection_ids[1];
379
380 Node u1 = bd.first_left(ref_seg, id);
381 Node v1 = -1;
382 unsigned int opp = bd.intersecting_segments[id].opposite(ref_seg);
383 if (consider_equal(bd.segments[opp].target(), bd.get_intersection_point(li))) {
384 v1 = bd.first_right(opp, id);
385 } else {
386 v1 = bd.second_left(opp, id);
387 }
388
389 bd.add_edge(u1, v1, true);
390 }
391
392 if (li.right_is_valid()) {
393 unsigned int right_segment_id[2];
394 OGDF_GEOMETRIC_BD_ECHO("ref: " << ref_seg << "; " << bd.segments[ref_seg]);
395 for (unsigned int i = 0; i < 2; ++i) {
397 bd.intersecting_segments[li.right_intersection_ids[i]].opposite(ref_seg);
398
399 source_is_right[i] = !consider_equal(bd.segments[right_segment_id[i]].source(),
400 bd.get_intersection_point(li))
401 && geometry::right_turn(bd.segments[ref_seg],
402 bd.segments[right_segment_id[i]].source());
403
404 OGDF_GEOMETRIC_BD_ECHO("\t r" << right_segment_id[i] << "; "
405 << bd.segments[right_segment_id[i]] << ": "
406 << source_is_right[i]);
407 }
408
410 Node u1 = bd.first_right(ref_seg, li.right_intersection_ids[0]);
411 Node v1 = -1;
412 if (source_is_right[0]
413 || consider_equal(bd.segments[right_segment_id[0]].target(),
414 bd.get_intersection_point(li))) {
415 v1 = bd.first_left(right_segment_id[0], li.right_intersection_ids[0]);
416 } else {
417 v1 = bd.second_right(right_segment_id[0], li.right_intersection_ids[0]);
418 }
419
420 bd.add_edge(u1, v1, true);
421 }
422
424 Node u2 = bd.second_right(ref_seg, li.right_intersection_ids[0]);
425 Node v2 = -1;
426
427 if (source_is_right[1]
428 || consider_equal(bd.segments[right_segment_id[1]].target(),
429 bd.get_intersection_point(li))) {
430 v2 = bd.first_right(right_segment_id[1], li.right_intersection_ids[1]);
431 } else {
432 v2 = bd.second_left(right_segment_id[1], li.right_intersection_ids[1]);
433 }
434
435 bd.add_edge(u2, v2, true);
436 }
437 } else if (proper) {
438 Node u1 = bd.first_right(ref_seg, li.left_intersection_ids[0]);
439 Node v1 = bd.second_right(ref_seg, li.left_intersection_ids[0]);
440 bd.add_edge(u1, v1, true);
441
442 } else if (intersection_is_source) {
443 auto id = li.left_intersection_ids[0];
444
445 Node u1 = bd.second_right(ref_seg, id);
446 Node v1 = -1;
447 unsigned int opp = bd.intersecting_segments[id].opposite(ref_seg);
448 if (consider_equal(bd.segments[opp].target(), bd.get_intersection_point(li))) {
449 v1 = bd.first_right(opp, id);
450 } else {
451 v1 = bd.second_left(opp, id);
452 }
453
454 bd.add_edge(u1, v1, true);
455 } else if (intersection_is_target) {
456 auto id = li.left_intersection_ids[1];
457
458 Node u1 = bd.first_right(ref_seg, id);
459 Node v1 = -1;
460 unsigned int opp = bd.intersecting_segments[id].opposite(ref_seg);
461 if (consider_equal(bd.segments[opp].target(), bd.get_intersection_point(li))) {
462 v1 = bd.first_left(opp, id);
463 } else {
464 v1 = bd.second_right(opp, id);
465 }
466
467 bd.add_edge(u1, v1, true);
468 }
469 }
470
471 inline void construct_graph(BD& bd) {
472 int nof_threads = 1;
474 if (parallel) {
476 }
477# pragma omp parallel for num_threads(nof_threads)
478 for (unsigned int i = 0; i < bd.segments.size(); ++i) {
479 for (unsigned int j = 0; j < bd.segment_to_intersections[i].size(); ++j) {
480 handle_non_proper_intersection(bd, i, bd.segment_to_intersections[i][j]);
481 }
482 }
483 }
484
485public:
486 // a bloated dual has a number of vertices in each face corresponding to the number of edges on the face
487 // vertices within a face are connected via circle corresponding to order of the edges of the face
488 // functions assumes that endpoints of semgents are in lexicographical order
489 // @ASSUMPTION: no overlapping segments
490 // @ASSUMPTION: no segment contains end endpoint of another segment in its interior....
491 // @return a vector containg a mapping an edge to pair of segment ids. if the edge crosses a segment,
492 // then the entries coincide.
493
494
495 std::vector<std::vector<unsigned int>> segment_to_intersections;
496
497 void construct(BD& bd) {
498 //sort intersections along segment
500 segment_to_intersections.resize(bd.segments.size());
501
502 for (unsigned int i = 0; i < bd.segments.size(); ++i) {
503 Intersection s(i, i);
504
505 s.is_source = true;
506 bd.intersecting_segments.push_back(s);
507 s.is_source = false;
508
509 s.is_target = true;
510 bd.intersecting_segments.push_back(s);
511 }
512
513 assign_intersections_to_segment(bd.segments, bd.intersecting_segments,
514 segment_to_intersections, bd.intersections);
515
516 sort_intersections_along_segment(bd.segments, bd.intersecting_segments,
517 segment_to_intersections, bd.intersections);
518
519 bd.segment_to_intersections.resize(bd.segments.size());
520 for (unsigned int i = 0; i < bd.segments.size(); ++i) {
521 clean_up(i, bd.segments, bd.intersecting_segments, bd.intersections,
522 segment_to_intersections[i], bd.segment_to_intersections[i]);
524 || bd.segment_to_intersections[i].size() >= 1);
525 }
526
527 for (unsigned int i = 0; i < bd.segment_to_intersections.size(); ++i) { // i: i-th segment
528 auto& is = bd.segment_to_intersections[i];
529 //assign positions
530 for (unsigned int j = 0; j < is.size(); ++j) { // j: j-th intersection on segment i
531 LocalIntersection& x = is[j];
532
533 auto set_pos = [&](unsigned int intersection_id) {
534 if (intersection_id < bd.intersecting_segments.size()) {
535 auto& y = bd.intersecting_segments[intersection_id];
536 if (y.seg_a == i) {
537 y.pos_on_a = j;
538 }
539 if (y.seg_b == i) {
540 y.pos_on_b = j;
541 }
542 }
543 };
544
545 set_pos(x.left_intersection_ids[0]);
546 set_pos(x.left_intersection_ids[1]);
547 set_pos(x.right_intersection_ids[0]);
548 set_pos(x.right_intersection_ids[1]);
549 }
550 }
551
552 bd.segm_to_node_range.reserve(bd.intersections.size());
553 bd.edges.reserve(3 * bd.intersections.size());
554 bd.node_to_segment.reserve(bd.intersections.size());
555 ;
556
557 //add all vertices
558 for (unsigned int i = 0; i < bd.segments.size(); ++i) {
559 bd.segm_to_node_range.push_back(bd.number_of_nodes());
560 //add #is + 1 nodes
561 auto& is = bd.segment_to_intersections[i];
562
563 for (unsigned int j = 0; j + 1 < is.size(); ++j) {
564 Node left = bd.add_node(i);
565 Node right = bd.add_node(i);
566 bd.add_edge(left, right, false);
567 }
568 }
569 bd.segm_to_node_range.push_back(bd.number_of_nodes());
570
572 }
573
574 static std::vector<Segment> compute_drawing(graph::BloatedDualGraph<Kernel>& bd) {
575 std::vector<Point> node_to_point(bd.number_of_nodes());
576 std::vector<Segment> edge_segments;
577
578 for (unsigned int i = 0; i < bd.segments.size(); ++i) {
579 auto left_dir = geometry::normalize(bd.segments[i].to_vector())
580 .perpendicular(CGAL::COUNTERCLOCKWISE);
581 auto right_dir =
582 geometry::normalize(bd.segments[i].to_vector()).perpendicular(CGAL::CLOCKWISE);
583 if (i >= bd.segment_to_intersections.size()) {
584 continue;
585 }
586
587 auto& is = bd.segment_to_intersections[i];
588 OGDF_GEOMETRIC_BD_ECHO(bd.segments[i]);
589
590 // source and target should be intersections
591 OGDF_ASSERT(is.size() >= 2);
592 Point last = bd.intersections[is[0].intersection_id()];
593
594 for (unsigned int j = 0; j + 1 < is.size(); j = j + 1) {
595 Point current = bd.intersections[is[j + 1].intersection_id()];
596
597 auto mid = last + (current - last) * 0.5;
598
599 auto left = bd.segm_to_node_range[i] + 2 * j;
600 auto right = bd.segm_to_node_range[i] + 2 * j + 1;
601
602 double c = std::max(
603 CGAL::sqrt(CGAL::to_double(CGAL::squared_distance(current, mid))) / 10, 0.0);
604 node_to_point[left] = mid + left_dir * c;
605 node_to_point[right] = mid + right_dir * c;
606 last = current;
607 }
608 }
609
610 auto add_segment = [&](Node u, Node v) {
611 if (u != v) {
612 edge_segments.push_back({node_to_point[u], node_to_point[v]});
613 }
614 };
615
616 for (unsigned int v = 0; v < bd.number_of_nodes(); ++v) {
617 add_segment(v, bd.edges[3 * v]);
618 add_segment(v, bd.edges[3 * v + 1]);
619 add_segment(v, bd.edges[3 * v + 2]);
620 }
621
622 return edge_segments;
623 }
624};
625
626}
627}
628}
629}
630
631#endif
Contains logging functionality.
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition basic.h:41
static MultilevelBuilder * getDoubleFactoredZeroAdjustedMerger()
The namespace for all OGDF objects.