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