Loading [MathJax]/extensions/tex2jax.js

Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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.