voronoi_visual_utils.hpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Boost.Polygon library voronoi_graphic_utils.hpp header file
  2. // Copyright Andrii Sydorchuk 2010-2012.
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt or copy at
  5. // http://www.boost.org/LICENSE_1_0.txt)
  6. // See http://www.boost.org for updates, documentation, and revision history.
  7. #ifndef BOOST_POLYGON_VORONOI_VISUAL_UTILS
  8. #define BOOST_POLYGON_VORONOI_VISUAL_UTILS
  9. #include <stack>
  10. #include <vector>
  11. #include <boost/polygon/isotropy.hpp>
  12. #include <boost/polygon/point_concept.hpp>
  13. #include <boost/polygon/segment_concept.hpp>
  14. #include <boost/polygon/rectangle_concept.hpp>
  15. namespace boost {
  16. namespace polygon {
  17. // Utilities class, that contains set of routines handful for visualization.
  18. template <typename CT>
  19. class voronoi_visual_utils {
  20. public:
  21. // Discretize parabolic Voronoi edge.
  22. // Parabolic Voronoi edges are always formed by one point and one segment
  23. // from the initial input set.
  24. //
  25. // Args:
  26. // point: input point.
  27. // segment: input segment.
  28. // max_dist: maximum discretization distance.
  29. // discretization: point discretization of the given Voronoi edge.
  30. //
  31. // Template arguments:
  32. // InCT: coordinate type of the input geometries (usually integer).
  33. // Point: point type, should model point concept.
  34. // Segment: segment type, should model segment concept.
  35. //
  36. // Important:
  37. // discretization should contain both edge endpoints initially.
  38. template <class InCT1, class InCT2,
  39. template<class> class Point,
  40. template<class> class Segment>
  41. static
  42. typename enable_if<
  43. typename gtl_and<
  44. typename gtl_if<
  45. typename is_point_concept<
  46. typename geometry_concept< Point<InCT1> >::type
  47. >::type
  48. >::type,
  49. typename gtl_if<
  50. typename is_segment_concept<
  51. typename geometry_concept< Segment<InCT2> >::type
  52. >::type
  53. >::type
  54. >::type,
  55. void
  56. >::type discretize(
  57. const Point<InCT1>& point,
  58. const Segment<InCT2>& segment,
  59. const CT max_dist,
  60. std::vector< Point<CT> >* discretization) {
  61. // Apply the linear transformation to move start point of the segment to
  62. // the point with coordinates (0, 0) and the direction of the segment to
  63. // coincide the positive direction of the x-axis.
  64. CT segm_vec_x = cast(x(high(segment))) - cast(x(low(segment)));
  65. CT segm_vec_y = cast(y(high(segment))) - cast(y(low(segment)));
  66. CT sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y;
  67. // Compute x-coordinates of the endpoints of the edge
  68. // in the transformed space.
  69. CT projection_start = sqr_segment_length *
  70. get_point_projection((*discretization)[0], segment);
  71. CT projection_end = sqr_segment_length *
  72. get_point_projection((*discretization)[1], segment);
  73. // Compute parabola parameters in the transformed space.
  74. // Parabola has next representation:
  75. // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y).
  76. CT point_vec_x = cast(x(point)) - cast(x(low(segment)));
  77. CT point_vec_y = cast(y(point)) - cast(y(low(segment)));
  78. CT rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y;
  79. CT rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x;
  80. // Save the last point.
  81. Point<CT> last_point = (*discretization)[1];
  82. discretization->pop_back();
  83. // Use stack to avoid recursion.
  84. std::stack<CT> point_stack;
  85. point_stack.push(projection_end);
  86. CT cur_x = projection_start;
  87. CT cur_y = parabola_y(cur_x, rot_x, rot_y);
  88. // Adjust max_dist parameter in the transformed space.
  89. const CT max_dist_transformed = max_dist * max_dist * sqr_segment_length;
  90. while (!point_stack.empty()) {
  91. CT new_x = point_stack.top();
  92. CT new_y = parabola_y(new_x, rot_x, rot_y);
  93. // Compute coordinates of the point of the parabola that is
  94. // furthest from the current line segment.
  95. CT mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x;
  96. CT mid_y = parabola_y(mid_x, rot_x, rot_y);
  97. // Compute maximum distance between the given parabolic arc
  98. // and line segment that discretize it.
  99. CT dist = (new_y - cur_y) * (mid_x - cur_x) -
  100. (new_x - cur_x) * (mid_y - cur_y);
  101. dist = dist * dist / ((new_y - cur_y) * (new_y - cur_y) +
  102. (new_x - cur_x) * (new_x - cur_x));
  103. if (dist <= max_dist_transformed) {
  104. // Distance between parabola and line segment is less than max_dist.
  105. point_stack.pop();
  106. CT inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) /
  107. sqr_segment_length + cast(x(low(segment)));
  108. CT inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) /
  109. sqr_segment_length + cast(y(low(segment)));
  110. discretization->push_back(Point<CT>(inter_x, inter_y));
  111. cur_x = new_x;
  112. cur_y = new_y;
  113. } else {
  114. point_stack.push(mid_x);
  115. }
  116. }
  117. // Update last point.
  118. discretization->back() = last_point;
  119. }
  120. private:
  121. // Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b).
  122. static CT parabola_y(CT x, CT a, CT b) {
  123. return ((x - a) * (x - a) + b * b) / (b + b);
  124. }
  125. // Get normalized length of the distance between:
  126. // 1) point projection onto the segment
  127. // 2) start point of the segment
  128. // Return this length divided by the segment length. This is made to avoid
  129. // sqrt computation during transformation from the initial space to the
  130. // transformed one and vice versa. The assumption is made that projection of
  131. // the point lies between the start-point and endpoint of the segment.
  132. template <class InCT,
  133. template<class> class Point,
  134. template<class> class Segment>
  135. static
  136. typename enable_if<
  137. typename gtl_and<
  138. typename gtl_if<
  139. typename is_point_concept<
  140. typename geometry_concept< Point<int> >::type
  141. >::type
  142. >::type,
  143. typename gtl_if<
  144. typename is_segment_concept<
  145. typename geometry_concept< Segment<long> >::type
  146. >::type
  147. >::type
  148. >::type,
  149. CT
  150. >::type get_point_projection(
  151. const Point<CT>& point, const Segment<InCT>& segment) {
  152. CT segment_vec_x = cast(x(high(segment))) - cast(x(low(segment)));
  153. CT segment_vec_y = cast(y(high(segment))) - cast(y(low(segment)));
  154. CT point_vec_x = x(point) - cast(x(low(segment)));
  155. CT point_vec_y = y(point) - cast(y(low(segment)));
  156. CT sqr_segment_length =
  157. segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y;
  158. CT vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y;
  159. return vec_dot / sqr_segment_length;
  160. }
  161. template <typename InCT>
  162. static CT cast(const InCT& value) {
  163. return static_cast<CT>(value);
  164. }
  165. };
  166. }
  167. }
  168. #endif // BOOST_POLYGON_VORONOI_VISUAL_UTILS