1 /**
2 * SFCGAL
3 *
4 * Copyright (C) 2012-2013 Oslandia <infos@oslandia.com>
5 * Copyright (C) 2012-2013 IGN (http://www.ign.fr)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <SFCGAL/algorithm/minkowskiSum.h>
22 #include <SFCGAL/algorithm/isValid.h>
23
24 #include <SFCGAL/Point.h>
25 #include <SFCGAL/LineString.h>
26 #include <SFCGAL/Polygon.h>
27 #include <SFCGAL/Triangle.h>
28 #include <SFCGAL/PolyhedralSurface.h>
29 #include <SFCGAL/TriangulatedSurface.h>
30 #include <SFCGAL/Solid.h>
31 #include <SFCGAL/GeometryCollection.h>
32
33 #include <SFCGAL/detail/polygonSetToMultiPolygon.h>
34
35 #include <CGAL/minkowski_sum_2.h>
36 #include <CGAL/Polygon_2.h>
37 #include <CGAL/Polygon_with_holes_2.h>
38 #include <CGAL/Polygon_set_2.h>
39
40 #include <CGAL/Aff_transformation_2.h>
41
42
43 typedef CGAL::Polygon_2< SFCGAL::Kernel > Polygon_2 ;
44 typedef CGAL::Polygon_with_holes_2< SFCGAL::Kernel > Polygon_with_holes_2 ;
45 typedef CGAL::Polygon_set_2< SFCGAL::Kernel > Polygon_set_2 ;
46
47
48 namespace SFCGAL {
49 namespace algorithm {
50
51 //-- private interface
52
53 /**
54 * dispatch gA+gB sum
55 */
56 void minkowskiSum( const Geometry& gA, const Polygon_2& gB, CGAL::Polygon_set_2< Kernel >& polygonSet ) ;
57 /*
58 * append gA+gB into the polygonSet
59 */
60 void minkowskiSum( const Point& gA, const Polygon_2& gB, Polygon_set_2& polygonSet ) ;
61 /*
62 * append gA+gB into the polygonSet
63 */
64 void minkowskiSum( const LineString& gA, const Polygon_2& gB, Polygon_set_2& polygonSet ) ;
65 /*
66 * append gA+gB into the polygonSet
67 */
68 void minkowskiSum( const Polygon& gA, const Polygon_2& gB, Polygon_set_2& polygonSet ) ;
69 /*
70 * append gA+gB into the polygonSet
71 */
72 void minkowskiSum( const Solid& gA, const Polygon_2& gB, Polygon_set_2& polygonSet ) ;
73 /*
74 * append gA+gB into the polygonSet
75 */
76 void minkowskiSumCollection( const Geometry& gA, const Polygon_2& gB, Polygon_set_2& polygonSet ) ;
77
78 //-- private interface implementation
79
80 ///
81 ///
82 ///
minkowskiSum(const Geometry & gA,const Polygon_2 & gB,CGAL::Polygon_set_2<Kernel> & polygonSet)83 void minkowskiSum( const Geometry& gA, const Polygon_2& gB, CGAL::Polygon_set_2< Kernel >& polygonSet )
84 {
85 if ( gA.isEmpty() ) {
86 return ;
87 }
88
89 switch ( gA.geometryTypeId() ) {
90 case TYPE_POINT:
91 return minkowskiSum( gA.as< Point >(), gB, polygonSet ) ;
92
93 case TYPE_LINESTRING:
94 return minkowskiSum( gA.as< LineString >(), gB, polygonSet ) ;
95
96 case TYPE_POLYGON:
97 return minkowskiSum( gA.as< Polygon >(), gB, polygonSet ) ;
98
99 case TYPE_TRIANGLE:
100 return minkowskiSum( gA.as< Triangle >().toPolygon(), gB, polygonSet ) ;
101
102 case TYPE_SOLID:
103 return minkowskiSum( gA.as< Solid >(), gB, polygonSet ) ;
104
105 case TYPE_MULTIPOINT:
106 case TYPE_MULTILINESTRING:
107 case TYPE_MULTIPOLYGON:
108 case TYPE_MULTISOLID:
109 case TYPE_GEOMETRYCOLLECTION:
110 case TYPE_TRIANGULATEDSURFACE:
111 case TYPE_POLYHEDRALSURFACE:
112 return minkowskiSumCollection( gA, gB, polygonSet ) ;
113 }
114
115 BOOST_THROW_EXCEPTION( Exception(
116 ( boost::format( "minkowskiSum( %s, 'Polygon' ) is not defined" )
117 % gA.geometryType() ).str()
118 ) );
119 }
120
121 /*
122 * append gA+gB into the polygonSet
123 */
minkowskiSum(const Point & gA,const Polygon_2 & gB,Polygon_set_2 & polygonSet)124 void minkowskiSum( const Point& gA, const Polygon_2& gB, Polygon_set_2& polygonSet )
125 {
126 BOOST_ASSERT( gB.size() );
127
128 CGAL::Aff_transformation_2< Kernel > translate(
129 CGAL::TRANSLATION,
130 gA.toVector_2()
131 );
132
133 Polygon_2 sum ;
134
135 for ( Polygon_2::Vertex_const_iterator it = gB.vertices_begin();
136 it != gB.vertices_end(); ++it ) {
137 sum.push_back( translate.transform( *it ) );
138 }
139
140 if ( sum.is_clockwise_oriented() ) {
141 sum.reverse_orientation() ;
142 }
143
144 if ( polygonSet.is_empty() ) {
145 polygonSet.insert( sum );
146 }
147 else {
148 polygonSet.join( sum );
149 }
150 }
151
152
153 ///
154 ///
155 ///
minkowskiSum(const LineString & gA,const Polygon_2 & gB,Polygon_set_2 & polygonSet)156 void minkowskiSum( const LineString& gA, const Polygon_2& gB, Polygon_set_2& polygonSet )
157 {
158 if ( gA.isEmpty() ) {
159 return ;
160 }
161
162 int npt = gA.numPoints() ;
163
164 for ( int i = 0; i < npt - 1 ; i++ ) {
165 Polygon_2 P;
166 P.push_back( gA.pointN( i ).toPoint_2() );
167 P.push_back( gA.pointN( i+1 ).toPoint_2() );
168
169 //
170 // We want to compute the "minkowski sum" on each segment of the line string
171 // This is not very well defined. But it appears CGAL supports it.
172 // However we must use the explicit "full convolution" method for that particular case in CGAL >= 4.7
173 #if CGAL_VERSION_NR < 1040701000 // version 4.7
174 Polygon_with_holes_2 part = minkowski_sum_2( P, gB );
175 #else
176 Polygon_with_holes_2 part = minkowski_sum_by_full_convolution_2( P, gB );
177 #endif
178
179 // merge into a polygon set
180 if ( polygonSet.is_empty() ) {
181 polygonSet.insert( part );
182 }
183 else {
184 polygonSet.join( part );
185 }
186 }
187 }
188
189 ///
190 ///
191 ///
minkowskiSum(const Polygon & gA,const Polygon_2 & gB,Polygon_set_2 & polygonSet)192 void minkowskiSum( const Polygon& gA, const Polygon_2& gB, Polygon_set_2& polygonSet )
193 {
194 if ( gA.isEmpty() ) {
195 return ;
196 }
197
198 /*
199 * Invoke minkowski_sum_2 for exterior ring
200 */
201 {
202 Polygon_with_holes_2 sum = minkowski_sum_2( gA.exteriorRing().toPolygon_2(), gB ) ;
203
204 if ( polygonSet.is_empty() ) {
205 polygonSet.insert( sum );
206 }
207 else {
208 polygonSet.join( sum );
209 }
210 }
211
212 /*
213 * Compute the Minkowski sum for each segment of the interior rings
214 * and perform the union of the result. The result is a polygon, and its holes
215 * correspond to the inset.
216 *
217 */
218 if ( gA.hasInteriorRings() ) {
219 Polygon_set_2 sumInteriorRings ;
220
221 for ( size_t i = 0; i < gA.numInteriorRings(); i++ ) {
222 minkowskiSum( gA.interiorRingN( i ), gB, sumInteriorRings ) ;
223 }
224
225 /*
226 * compute the difference for each hole of the resulting polygons
227 */
228 std::list<Polygon_with_holes_2> interiorPolygons ;
229 sumInteriorRings.polygons_with_holes( std::back_inserter( interiorPolygons ) ) ;
230
231 for ( std::list<Polygon_with_holes_2>::iterator it_p = interiorPolygons.begin();
232 it_p != interiorPolygons.end(); ++it_p ) {
233
234 for ( Polygon_with_holes_2::Hole_iterator it_hole = it_p->holes_begin();
235 it_hole != it_p->holes_end(); ++it_hole ) {
236
237 it_hole->reverse_orientation() ;
238 polygonSet.difference( *it_hole ) ;
239 } // foreach hole
240 }// foreach polygon
241 }
242 }
243
244 ///
245 ///
246 ///
minkowskiSum(const Solid & gA,const Polygon_2 & gB,Polygon_set_2 & polygonSet)247 void minkowskiSum( const Solid& gA, const Polygon_2& gB, Polygon_set_2& polygonSet )
248 {
249 //use only the projection of exterior shell
250 minkowskiSumCollection( gA.exteriorShell(), gB, polygonSet );
251 }
252
253
254 ///
255 ///
256 ///
minkowskiSumCollection(const Geometry & gA,const Polygon_2 & gB,Polygon_set_2 & polygonSet)257 void minkowskiSumCollection( const Geometry& gA, const Polygon_2& gB, Polygon_set_2& polygonSet )
258 {
259 for ( size_t i = 0; i < gA.numGeometries(); i++ ) {
260 minkowskiSum( gA.geometryN( i ), gB, polygonSet );
261 }
262 }
263
264
minkowskiSum(const Geometry & gA,const Polygon & gB,NoValidityCheck)265 std::unique_ptr< Geometry > minkowskiSum( const Geometry& gA, const Polygon& gB, NoValidityCheck )
266 {
267 if ( gB.isEmpty() ) {
268 return std::unique_ptr< Geometry >( gA.clone() );
269 }
270
271 Polygon_set_2 polygonSet ;
272 minkowskiSum( gA, gB.toPolygon_2(), polygonSet ) ;
273 return std::unique_ptr< Geometry >( detail::polygonSetToMultiPolygon( polygonSet ).release() ) ;
274 }
275
276 //-- public interface implementation
277
278 ///
279 ///
280 ///
minkowskiSum(const Geometry & gA,const Polygon & gB)281 std::unique_ptr< Geometry > minkowskiSum( const Geometry& gA, const Polygon& gB )
282 {
283 SFCGAL_ASSERT_GEOMETRY_VALIDITY_2D( gA );
284 SFCGAL_ASSERT_GEOMETRY_VALIDITY_2D( gB );
285
286 std::unique_ptr<Geometry> result( minkowskiSum( gA, gB, NoValidityCheck() ) );
287 propagateValidityFlag( *result, true );
288 return result;
289 }
290
291 } // namespace algorithm
292 } // namespace SFCGAL
293