Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

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 
44 namespace ogdf {
45 namespace internal {
46 namespace gcm {
47 namespace geometry {
48 
49 template<typename Kernel, bool parallel = false>
50 class BloatedDual {
51 private:
52  using Point = geometry::Point_t<Kernel>;
53  using Segment = LineSegment_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)
162  && !has_point_on_segment(_segment, 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()
187  && consider_equal(intersections[intersections_along_segment[ref_intersection]],
188  intersections[intersections_along_segment[j]])) { //TODO make robust?
189 
190  unsigned int intersection_id = intersections_along_segment[j];
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) {
200  self_intersection_id = intersection_id;
201  } else if (!is_left_turn && !is_right_turn) { //is collinear
202 
203  left_intersections.push_back(intersection_id);
204  right_intersections.push_back(intersection_id);
205  } else {
206  if (is_left_turn) {
207  left_intersections.push_back(intersection_id);
208  }
209 
210  if (is_right_turn) {
211  right_intersections.push_back(intersection_id);
212  }
213  }
214 
215  ++j;
216  }
217 
218  if (left_intersections.empty() && right_intersections.empty()) {
219  left_intersections.push_back(self_intersection_id);
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 
270  OGDF_ASSERT(!left_intersections.empty() || !right_intersections.empty());
271  LocalIntersection li;
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 
283  clean_intersecting_segments.push_back(li);
284  }
285  }
286 
287  void handle_non_proper_intersection(BD& bd, unsigned int ref_seg, LocalIntersection& li) {
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 
300  bool intersection_is_source =
301  consider_equal(bd.segments[ref_seg].source(), bd.get_intersection_point(li));
302  bool intersection_is_target =
303  consider_equal(bd.segments[ref_seg].target(), bd.get_intersection_point(li));
304  bool proper = !intersection_is_source && !intersection_is_target;
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 
329  if (proper || intersection_is_target) {
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 
344  if (proper || intersection_is_source) {
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) {
396  right_segment_id[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 
409  if (proper || intersection_is_target) {
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 
423  if (proper || intersection_is_source) {
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;
473  (void)nof_threads;
474  if (parallel) {
475  nof_threads = omp_get_max_threads();
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 
485 public:
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
499  segment_to_intersections.clear();
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]);
523  OGDF_ASSERT(segment_to_intersections[i].size() <= 1
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 
571  construct_graph(bd);
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
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:54
ogdf::gml::Key::Node
@ Node
ogdf::gml::Key::Edge
@ Edge
ogdf::tlp::Attribute::size
@ size
Logger.h
Contains logging functionality.
Minisat::Internal::sort
void sort(T *array, int size, LessThan lt)
Definition: Sort.h:57
ogdf::gml::Key::Point
@ Point
BloatedDual.h