1 // Boost.Geometry (aka GGL, Generic Geometry Library)
4 // Copyright (c) 2012-2015 Barend Gehrels, Amsterdam, the Netherlands.
6 // Use, modification and distribution is subject to the Boost Software License,
7 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 // http://www.boost.org/LICENSE_1_0.txt)
10 #define BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX_FOR_INPUT
11 #define BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX "BOX(179 4, 180 5)"
14 #include <test_buffer.hpp>
16 #include <boost/geometry/algorithms/difference.hpp>
17 #include <boost/geometry/geometries/geometries.hpp>
19 #include <boost/random/linear_congruential.hpp>
20 #include <boost/random/uniform_int.hpp>
21 #include <boost/random/uniform_real.hpp>
22 #include <boost/random/variate_generator.hpp>
26 const int point_count
= 90; // points for a full circle
28 // Function to let buffer-distance depend on alpha, e.g.:
29 inline double corrected_distance(double distance
, double alpha
)
31 return distance
* 1.0 + 0.2 * sin(alpha
* 6.0);
34 class buffer_point_strategy_sample
42 typename DistanceStrategy
44 void apply(Point
const& point
,
45 DistanceStrategy
const& distance_strategy
,
46 OutputRange
& output_range
) const
48 double const distance
= distance_strategy
.apply(point
, point
,
49 bg::strategy::buffer::buffer_side_left
);
51 double const angle_increment
= 2.0 * M_PI
/ double(point_count
);
54 for (std::size_t i
= 0; i
<= point_count
; i
++, alpha
-= angle_increment
)
56 double const cd
= corrected_distance(distance
, alpha
);
58 typename
boost::range_value
<OutputRange
>::type output_point
;
59 bg::set
<0>(output_point
, bg::get
<0>(point
) + cd
* cos(alpha
));
60 bg::set
<1>(output_point
, bg::get
<1>(point
) + cd
* sin(alpha
));
61 output_range
.push_back(output_point
);
66 class buffer_join_strategy_sample
72 typename DistanceType
,
75 inline void generate_points(Point
const& vertex
,
76 Point
const& perp1
, Point
const& perp2
,
77 DistanceType
const& buffer_distance
,
78 RangeOut
& range_out
) const
80 double dx1
= bg::get
<0>(perp1
) - bg::get
<0>(vertex
);
81 double dy1
= bg::get
<1>(perp1
) - bg::get
<1>(vertex
);
82 double dx2
= bg::get
<0>(perp2
) - bg::get
<0>(vertex
);
83 double dy2
= bg::get
<1>(perp2
) - bg::get
<1>(vertex
);
85 // Assuming the corner is convex, angle2 < angle1
86 double const angle1
= atan2(dy1
, dx1
);
87 double angle2
= atan2(dy2
, dx2
);
89 while (angle2
> angle1
)
94 double const angle_increment
= 2.0 * M_PI
/ double(point_count
);
95 double alpha
= angle1
- angle_increment
;
97 for (int i
= 0; alpha
>= angle2
&& i
< point_count
; i
++, alpha
-= angle_increment
)
99 double cd
= corrected_distance(buffer_distance
, alpha
);
102 bg::set
<0>(p
, bg::get
<0>(vertex
) + cd
* cos(alpha
));
103 bg::set
<1>(p
, bg::get
<1>(vertex
) + cd
* sin(alpha
));
104 range_out
.push_back(p
);
110 template <typename Point
, typename DistanceType
, typename RangeOut
>
111 inline bool apply(Point
const& ip
, Point
const& vertex
,
112 Point
const& perp1
, Point
const& perp2
,
113 DistanceType
const& buffer_distance
,
114 RangeOut
& range_out
) const
116 generate_points(vertex
, perp1
, perp2
, buffer_distance
, range_out
);
120 template <typename NumericType
>
121 static inline NumericType
max_distance(NumericType
const& distance
)
128 class buffer_side_sample
134 typename OutputRange
,
135 typename DistanceStrategy
137 static inline void apply(
138 Point
const& input_p1
, Point
const& input_p2
,
139 bg::strategy::buffer::buffer_side_selector side
,
140 DistanceStrategy
const& distance
,
141 OutputRange
& output_range
)
143 // Generate a block along (left or right of) the segment
145 double const dx
= bg::get
<0>(input_p2
) - bg::get
<0>(input_p1
);
146 double const dy
= bg::get
<1>(input_p2
) - bg::get
<1>(input_p1
);
148 // For normalization [0,1] (=dot product d.d, sqrt)
149 double const length
= bg::math::sqrt(dx
* dx
+ dy
* dy
);
151 if (bg::math::equals(length
, 0))
156 // Generate the normalized perpendicular p, to the left (ccw)
157 double const px
= -dy
/ length
;
158 double const py
= dx
/ length
;
160 // Both vectors perpendicular to input p1 and input p2 have same angle
161 double const alpha
= atan2(py
, px
);
163 double const d
= distance
.apply(input_p1
, input_p2
, side
);
165 double const cd
= corrected_distance(d
, alpha
);
167 output_range
.resize(2);
169 bg::set
<0>(output_range
.front(), bg::get
<0>(input_p1
) + px
* cd
);
170 bg::set
<1>(output_range
.front(), bg::get
<1>(input_p1
) + py
* cd
);
171 bg::set
<0>(output_range
.back(), bg::get
<0>(input_p2
) + px
* cd
);
172 bg::set
<1>(output_range
.back(), bg::get
<1>(input_p2
) + py
* cd
);
177 template <typename Geometry1
, typename Geometry2
>
178 void create_svg(std::string
const& filename
, Geometry1
const& original
, Geometry2
const& buffer
, std::string
const& color
)
180 typedef typename
bg::point_type
<Geometry1
>::type point_type
;
181 std::ofstream
svg(filename
.c_str());
183 bg::svg_mapper
<point_type
> mapper(svg
, 800, 800);
186 mapper
.map(original
, "fill-opacity:0.3;fill:rgb(255,0,0);stroke:rgb(0,0,0);stroke-width:1");
188 std::string style
= "fill-opacity:0.3;fill:";
190 style
+= ";stroke:rgb(0,0,0);stroke-width:1";
191 mapper
.map(buffer
, style
);
196 void test_many_rings(int imn
, int jmx
, int count
,
197 double expected_area_exterior
,
198 double expected_area_interior
)
200 typedef bg::model::point
<double, 2, bg::cs::cartesian
> point
;
201 typedef bg::model::polygon
<point
> polygon_type
;
202 typedef bg::model::multi_polygon
<polygon_type
> multi_polygon_type
;
204 // Predefined strategies
205 bg::strategy::buffer::distance_symmetric
<double> distance_strategy(1.3);
206 bg::strategy::buffer::end_flat end_strategy
; // not effectively used
209 buffer_join_strategy_sample join_strategy
;
210 buffer_point_strategy_sample point_strategy
;
211 buffer_side_sample side_strategy
;
215 bg::model::multi_point
<point
> mp
;
217 // Use a bit of random disturbance in the otherwise too regular grid
218 typedef boost::minstd_rand base_generator_type
;
219 base_generator_type
generator(12345);
220 boost::uniform_real
<> random_range(0.0, 0.5);
221 boost::variate_generator
223 base_generator_type
&,
224 boost::uniform_real
<>
225 > random(generator
, random_range
);
227 for (int i
= 0; i
< count
; i
++)
229 for (int j
= 0; j
< count
; j
++)
231 double x
= i
* 3.0 + random();
232 double y
= j
* 3.0 + random();
233 //if (i > 30 && j < 30)
234 if (i
> imn
&& j
< jmx
)
242 multi_polygon_type many_rings
;
243 // Create the buffer of a multi-point
244 bg::buffer(mp
, many_rings
,
245 distance_strategy
, side_strategy
,
246 join_strategy
, end_strategy
, point_strategy
);
248 bg::model::box
<point
> envelope
;
249 bg::envelope(many_rings
, envelope
);
250 bg::buffer(envelope
, envelope
, 1.0);
252 multi_polygon_type many_interiors
;
253 bg::difference(envelope
, many_rings
, many_interiors
);
256 create_svg("/tmp/many_interiors.svg", mp
, many_interiors
, "rgb(51,51,153)");
257 create_svg("/tmp/buffer.svg", mp
, many_rings
, "rgb(51,51,153)");
260 bg::strategy::buffer::join_round
join_round(100);
261 bg::strategy::buffer::end_flat end_flat
;
264 std::ostringstream out
;
265 out
<< "many_rings_" << count
;
266 out
<< "_" << imn
<< "_" << jmx
;
267 std::ostringstream wkt
;
268 wkt
<< std::setprecision(12) << bg::wkt(many_rings
);
271 test_one
<multi_polygon_type
, polygon_type
>(out
.str(), wkt
.str(), join_round
, end_flat
, expected_area_exterior
, 0.3);
272 std::cout
<< "Exterior " << count
<< " " << t
.elapsed() << std::endl
;
277 std::ostringstream out
;
278 out
<< "many_interiors_" << count
;
279 std::ostringstream wkt
;
280 wkt
<< std::setprecision(12) << bg::wkt(many_interiors
);
283 test_one
<multi_polygon_type
, polygon_type
>(out
.str(), wkt
.str(), join_round
, end_flat
, expected_area_interior
, 0.3);
284 std::cout
<< "Interior " << count
<< " " << t
.elapsed() << std::endl
;
288 int test_main(int, char* [])
290 // test_many_rings(10, 795.70334, 806.7609);
291 // test_many_rings(30, 7136.7098, 6174.4496);
292 test_many_rings(30, 30, 70, 38764.2721, 31910.3280);
293 // for (int i = 30; i < 60; i++)
295 // for (int j = 5; j <= 30; j++)
297 // test_many_rings(i, j, 70, 38764.2721, 31910.3280);