many_ring_buffer.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test
  3. // Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands.
  4. // Use, modification and distribution is subject to the Boost Software License,
  5. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. #define BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX_FOR_INPUT
  8. #define BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX "BOX(179 4, 180 5)"
  9. #include <test_buffer.hpp>
  10. #include <boost/geometry/algorithms/difference.hpp>
  11. #include <boost/geometry/geometries/geometries.hpp>
  12. #include <boost/random/linear_congruential.hpp>
  13. #include <boost/random/uniform_int.hpp>
  14. #include <boost/random/uniform_real.hpp>
  15. #include <boost/random/variate_generator.hpp>
  16. const int point_count = 90; // points for a full circle
  17. // Function to let buffer-distance depend on alpha, e.g.:
  18. inline double corrected_distance(double distance, double alpha)
  19. {
  20. return distance * 1.0 + 0.2 * sin(alpha * 6.0);
  21. }
  22. class buffer_point_strategy_sample
  23. {
  24. public :
  25. template
  26. <
  27. typename Point,
  28. typename OutputRange,
  29. typename DistanceStrategy
  30. >
  31. void apply(Point const& point,
  32. DistanceStrategy const& distance_strategy,
  33. OutputRange& output_range) const
  34. {
  35. double const distance = distance_strategy.apply(point, point,
  36. bg::strategy::buffer::buffer_side_left);
  37. double const angle_increment = 2.0 * M_PI / double(point_count);
  38. double alpha = 0;
  39. for (std::size_t i = 0; i <= point_count; i++, alpha -= angle_increment)
  40. {
  41. double const cd = corrected_distance(distance, alpha);
  42. typename boost::range_value<OutputRange>::type output_point;
  43. bg::set<0>(output_point, bg::get<0>(point) + cd * cos(alpha));
  44. bg::set<1>(output_point, bg::get<1>(point) + cd * sin(alpha));
  45. output_range.push_back(output_point);
  46. }
  47. }
  48. };
  49. class buffer_join_strategy_sample
  50. {
  51. private :
  52. template
  53. <
  54. typename Point,
  55. typename DistanceType,
  56. typename RangeOut
  57. >
  58. inline void generate_points(Point const& vertex,
  59. Point const& perp1, Point const& perp2,
  60. DistanceType const& buffer_distance,
  61. RangeOut& range_out) const
  62. {
  63. double dx1 = bg::get<0>(perp1) - bg::get<0>(vertex);
  64. double dy1 = bg::get<1>(perp1) - bg::get<1>(vertex);
  65. double dx2 = bg::get<0>(perp2) - bg::get<0>(vertex);
  66. double dy2 = bg::get<1>(perp2) - bg::get<1>(vertex);
  67. // Assuming the corner is convex, angle2 < angle1
  68. double const angle1 = atan2(dy1, dx1);
  69. double angle2 = atan2(dy2, dx2);
  70. while (angle2 > angle1)
  71. {
  72. angle2 -= 2 * M_PI;
  73. }
  74. double const angle_increment = 2.0 * M_PI / double(point_count);
  75. double alpha = angle1 - angle_increment;
  76. for (int i = 0; alpha >= angle2 && i < point_count; i++, alpha -= angle_increment)
  77. {
  78. double cd = corrected_distance(buffer_distance, alpha);
  79. Point p;
  80. bg::set<0>(p, bg::get<0>(vertex) + cd * cos(alpha));
  81. bg::set<1>(p, bg::get<1>(vertex) + cd * sin(alpha));
  82. range_out.push_back(p);
  83. }
  84. }
  85. public :
  86. template <typename Point, typename DistanceType, typename RangeOut>
  87. inline bool apply(Point const& ip, Point const& vertex,
  88. Point const& perp1, Point const& perp2,
  89. DistanceType const& buffer_distance,
  90. RangeOut& range_out) const
  91. {
  92. generate_points(vertex, perp1, perp2, buffer_distance, range_out);
  93. return true;
  94. }
  95. template <typename NumericType>
  96. static inline NumericType max_distance(NumericType const& distance)
  97. {
  98. return distance;
  99. }
  100. };
  101. class buffer_side_sample
  102. {
  103. public :
  104. template
  105. <
  106. typename Point,
  107. typename OutputRange,
  108. typename DistanceStrategy
  109. >
  110. static inline void apply(
  111. Point const& input_p1, Point const& input_p2,
  112. bg::strategy::buffer::buffer_side_selector side,
  113. DistanceStrategy const& distance,
  114. OutputRange& output_range)
  115. {
  116. // Generate a block along (left or right of) the segment
  117. double const dx = bg::get<0>(input_p2) - bg::get<0>(input_p1);
  118. double const dy = bg::get<1>(input_p2) - bg::get<1>(input_p1);
  119. // For normalization [0,1] (=dot product d.d, sqrt)
  120. double const length = bg::math::sqrt(dx * dx + dy * dy);
  121. if (bg::math::equals(length, 0))
  122. {
  123. return;
  124. }
  125. // Generate the normalized perpendicular p, to the left (ccw)
  126. double const px = -dy / length;
  127. double const py = dx / length;
  128. // Both vectors perpendicular to input p1 and input p2 have same angle
  129. double const alpha = atan2(py, px);
  130. double const d = distance.apply(input_p1, input_p2, side);
  131. double const cd = corrected_distance(d, alpha);
  132. output_range.resize(2);
  133. bg::set<0>(output_range.front(), bg::get<0>(input_p1) + px * cd);
  134. bg::set<1>(output_range.front(), bg::get<1>(input_p1) + py * cd);
  135. bg::set<0>(output_range.back(), bg::get<0>(input_p2) + px * cd);
  136. bg::set<1>(output_range.back(), bg::get<1>(input_p2) + py * cd);
  137. }
  138. };
  139. #ifdef TEST_WITH_SVG
  140. template <typename Geometry1, typename Geometry2>
  141. void create_svg(std::string const& filename, Geometry1 const& original, Geometry2 const& buffer, std::string const& color)
  142. {
  143. typedef typename bg::point_type<Geometry1>::type point_type;
  144. std::ofstream svg(filename.c_str());
  145. bg::svg_mapper<point_type> mapper(svg, 800, 800);
  146. mapper.add(buffer);
  147. mapper.map(original, "fill-opacity:0.3;fill:rgb(255,0,0);stroke:rgb(0,0,0);stroke-width:1");
  148. std::string style = "fill-opacity:0.3;fill:";
  149. style += color;
  150. style += ";stroke:rgb(0,0,0);stroke-width:1";
  151. mapper.map(buffer, style);
  152. }
  153. #endif
  154. void test_many_rings(int imn, int jmx, int count,
  155. double expected_area_exterior,
  156. double expected_area_interior)
  157. {
  158. typedef bg::model::point<double, 2, bg::cs::cartesian> point;
  159. typedef bg::model::polygon<point> polygon_type;
  160. typedef bg::model::multi_polygon<polygon_type> multi_polygon_type;
  161. // Predefined strategies
  162. bg::strategy::buffer::distance_symmetric<double> distance_strategy(1.3);
  163. bg::strategy::buffer::end_flat end_strategy; // not effectively used
  164. // Own strategies
  165. buffer_join_strategy_sample join_strategy;
  166. buffer_point_strategy_sample point_strategy;
  167. buffer_side_sample side_strategy;
  168. // Declare output
  169. bg::model::multi_point<point> mp;
  170. // Use a bit of random disturbance in the otherwise too regular grid
  171. typedef boost::minstd_rand base_generator_type;
  172. base_generator_type generator(12345);
  173. boost::uniform_real<> random_range(0.0, 0.5);
  174. boost::variate_generator
  175. <
  176. base_generator_type&,
  177. boost::uniform_real<>
  178. > random(generator, random_range);
  179. for (int i = 0; i < count; i++)
  180. {
  181. for (int j = 0; j < count; j++)
  182. {
  183. double x = i * 3.0 + random();
  184. double y = j * 3.0 + random();
  185. //if (i > 30 && j < 30)
  186. if (i > imn && j < jmx)
  187. {
  188. point p(x, y);
  189. mp.push_back(p);
  190. }
  191. }
  192. }
  193. multi_polygon_type many_rings;
  194. // Create the buffer of a multi-point
  195. bg::buffer(mp, many_rings,
  196. distance_strategy, side_strategy,
  197. join_strategy, end_strategy, point_strategy);
  198. bg::model::box<point> envelope;
  199. bg::envelope(many_rings, envelope);
  200. bg::buffer(envelope, envelope, 1.0);
  201. multi_polygon_type many_interiors;
  202. bg::difference(envelope, many_rings, many_interiors);
  203. #ifdef TEST_WITH_SVG
  204. create_svg("/tmp/many_interiors.svg", mp, many_interiors, "rgb(51,51,153)");
  205. create_svg("/tmp/buffer.svg", mp, many_rings, "rgb(51,51,153)");
  206. #endif
  207. bg::strategy::buffer::join_round join_round(100);
  208. bg::strategy::buffer::end_flat end_flat;
  209. {
  210. std::ostringstream out;
  211. out << "many_rings_" << count;
  212. out << "_" << imn << "_" << jmx;
  213. std::ostringstream wkt;
  214. wkt << std::setprecision(12) << bg::wkt(many_rings);
  215. boost::timer t;
  216. test_one<multi_polygon_type, polygon_type>(out.str(), wkt.str(), join_round, end_flat, expected_area_exterior, 0.3);
  217. std::cout << "Exterior " << count << " " << t.elapsed() << std::endl;
  218. }
  219. return;
  220. {
  221. std::ostringstream out;
  222. out << "many_interiors_" << count;
  223. std::ostringstream wkt;
  224. wkt << std::setprecision(12) << bg::wkt(many_interiors);
  225. boost::timer t;
  226. test_one<multi_polygon_type, polygon_type>(out.str(), wkt.str(), join_round, end_flat, expected_area_interior, 0.3);
  227. std::cout << "Interior " << count << " " << t.elapsed() << std::endl;
  228. }
  229. }
  230. int test_main(int, char* [])
  231. {
  232. // test_many_rings(10, 795.70334, 806.7609);
  233. // test_many_rings(30, 7136.7098, 6174.4496);
  234. test_many_rings(30, 30, 70, 38764.2721, 31910.3280);
  235. // for (int i = 30; i < 60; i++)
  236. // {
  237. // for (int j = 5; j <= 30; j++)
  238. // {
  239. // test_many_rings(i, j, 70, 38764.2721, 31910.3280);
  240. // }
  241. // }
  242. return 0;
  243. }