polygon_primitive_offset.cpp
Go to the documentation of this file.
1 /**************************************************************************************************
2  Software License Agreement (BSD License)
3 
4  Copyright (c) 2011-2013, LAR toolkit developers - University of Aveiro - http://lars.mec.ua.pt
5  All rights reserved.
6 
7  Redistribution and use in source and binary forms, with or without modification, are permitted
8  provided that the following conditions are met:
9 
10  *Redistributions of source code must retain the above copyright notice, this list of
11  conditions and the following disclaimer.
12  *Redistributions in binary form must reproduce the above copyright notice, this list of
13  conditions and the following disclaimer in the documentation and/or other materials provided
14  with the distribution.
15  *Neither the name of the University of Aveiro nor the names of its contributors may be used to
16  endorse or promote products derived from this software without specific prior written permission.
17 
18  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
19  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
21  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
24  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 ***************************************************************************************************/
36 #ifndef _polygon_primitive_offsetpolygon_CPP_
37 #define _polygon_primitive_offsetpolygon_CPP_
38 
39 #include "polygon_primitive.h"
40 
41 #include<CGAL/basic.h>
42 #include<CGAL/Cartesian.h>
43 #include<CGAL/Polygon_2.h>
44 #include<CGAL/Exact_predicates_inexact_constructions_kernel.h>
45 #include<CGAL/Straight_skeleton_builder_2.h>
46 #include<CGAL/Polygon_offset_builder_2.h>
47 #include<CGAL/compute_outer_frame_margin.h>
48 #include <CGAL/Cartesian.h>
49 #include <CGAL/Polygon_2.h>
50 
51 // This is the recommended kernel
52 typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
53 
55 typedef CGAL::Polygon_2<Kernel> Contour;
56 typedef boost::shared_ptr<Contour> ContourPtr;
57 typedef std::vector<ContourPtr> ContourSequence ;
58 
59 typedef CGAL::Straight_skeleton_2<Kernel> Ss;
60 
64 
65 typedef CGAL::Straight_skeleton_builder_traits_2<Kernel> SsBuilderTraits;
66 typedef CGAL::Straight_skeleton_builder_2<SsBuilderTraits,Ss> SsBuilder;
67 
68 typedef CGAL::Polygon_offset_builder_traits_2<Kernel> OffsetBuilderTraits;
69 typedef CGAL::Polygon_offset_builder_2<Ss,OffsetBuilderTraits,Contour> OffsetBuilder;
70 
71 
80 void c_polygon_primitive::offset_polygon(double offset, const pcl::PointCloud<pcl::PointXYZ>::Ptr pcin, pcl::PointCloud<pcl::PointXYZ>::Ptr pcout, tf::Transform *tr)
81  //void c_polygon_primitive::offset_polygon(double val)
82 {
83  //Adapting demo from http://www.cgal.org/Manual/3.2/doc_html/cgal_manual/Straight_skeleton_2/Chapter_main.html#Section_16.3
84 
85  pcl::PointCloud<pcl::PointXYZ>::Ptr pc_local = pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
86  pcl::PointCloud<pcl::PointXYZ>::Ptr pc_local_extended = pcl::PointCloud<pcl::PointXYZ>::Ptr(new pcl::PointCloud<pcl::PointXYZ>);
87 
88  //To offset a polygon the following steps are required:
89  //1. transform the polygon pcin to local coordinates (i.e., where z=0)
90  //2. Compute the offset in this local reference system
91  //3. transform the offseted polygon to the global reference system
92 
93  //the given transform goes from local to global. We want to change from global to local. Hence we use the inverse transform
94  pcl_ros::transformPointCloud(*pcin, *pc_local, data.frames.current.transform.inverse());
95 
96  //we assume there are no values for z. The polygon is defined in the XY plane. Lets check if its true. If not send error and exit
97  for (int i=0; i< (int) pc_local->size(); i++)
98  {
99  if (pc_local->points[i].z>0.01 || pc_local->points[i].z<-0.01)
100  {
101  //ROS_ERROR("offset polygon: %s has non zero z values in local frame. \n Are you sure that pcin is a projection to the plane?",data.misc.name);
102  //ROS_ERROR("pt%d x=%f y=%f z=%f",i,pc_local->points[i].x, pc_local->points[i].y, pc_local->points[i].z);
103  //exit(0);
104  }
105  }
106 
107  //now we must be sure that the polygon is oriented couter clockwise (CGAL only works for couterclockwise oriented polygons)
108  Contour mypol;
109 
110 
111  for (int i=0; i< (int) pc_local->size(); i++) //copy the pc to a Polygon_2 cgal data struct
112  {
113  mypol.push_back(Point_2(pc_local->points[i].x, pc_local->points[i].y));
114  }
115 
116 
117 
118 
119  if(!mypol.is_simple())
120  {
121  int keep_trying=1;
122 
123  while(keep_trying)
124  {
125 
126  int tt=0;
127  double prev_x=-999999, prev_y=-999999;
128  //ROS_ERROR("offset polygon: polygon is not simple. Printing all vertexes");
129 
130  int tttt=0;
131  for (Contour::Vertex_const_iterator j = mypol.vertices_begin(); j != mypol.vertices_end(); ++ j )
132  {
133  //ROS_ERROR("pt%d x=%f y=%f",tttt,j->x(), j->y());
134  tttt++;
135  }
136 
137 
138  Contour::Vertex_const_iterator begin = mypol.vertices_begin();
139  Contour::Vertex_const_iterator end = --mypol.vertices_end();
140  if ( begin->x() == end->x() && begin->y() == end->y())
141  {
142  //ROS_ERROR("Final and start point are equal. Erasing final point");
143  mypol.erase(end);
144  }
145 
146 
147  for (Contour::Vertex_const_iterator j = mypol.vertices_begin(); j != mypol.vertices_end(); ++ j )
148  {
149  if(tt!=0)
150  {
151  if (prev_x==j->x() && prev_y==j->y())
152  {
153  //ROS_INFO("Erasing vertex %d",tt);
154  mypol.erase(j);
155  break;
156  }
157  else
158  {
159  prev_x = j->x();
160  prev_y = j->y();
161  }
162 
163  }
164  else
165  {
166  prev_x = j->x();
167  prev_y = j->y();
168  }
169 
170 
171  //ROS_ERROR("pt%d x=%f y=%f",tt,j->x(), j->y());
172  tt++;
173  }
174 
175 
176  //ROS_ERROR("offset polygon: result of fix: %d", mypol.is_simple());
177 
178  int ttt=0;
179  for (Contour::Vertex_const_iterator j = mypol.vertices_begin(); j != mypol.vertices_end(); ++ j )
180  {
181  //ROS_ERROR("pt%d x=%f y=%f",ttt,j->x(), j->y());
182  ttt++;
183  }
184 
185  if (mypol.is_simple()) keep_trying=0;
186 
187 
188  }
189  }
190 
191 
192  if(!mypol.is_counterclockwise_oriented()) //check if its counterclockwise. If not reverse
193  {
194  //ROS_INFO("Offset Polygon: ch (%d points) is not counterclockwise oriented. Reversing orientation",(int)mypol.size());
195  mypol.reverse_orientation();
196  //ROS_INFO("Result Polygon: ch (%d points)",(int)mypol.size());
197  }
198 
199 
200  //Now we copy the polygon mypol (which was used just to check the orientation) to a std::vector
201  std::vector<Point_2> star; //The polygon
202 
203 
204  int t=0;
205  for (Contour::Vertex_const_iterator j = mypol.vertices_begin(); j != mypol.vertices_end(); ++ j )
206  {
207 
208  pc_local->points[t].x = j->x();
209  pc_local->points[t].y = j->y();
210  pc_local->points[t].z = 0;
211  t++;
212  }
213 
214 
215 
216 
217  //Setup the polygon with the points from pc.ch_l
218  for (int i=0; i< (int) pc_local->size(); i++)
219  {
220  star.push_back(Point_2(pc_local->points[i].x, pc_local->points[i].y));
221  }
222 
223 
224 
225  // We want an offset contour in the outside.
226  // Since the package doesn't support that operation directly, we use the following trick:
227  // (1) Place the polygon as a hole of a big outer frame.
228  // (2) Construct the skeleton on the interior of that frame (with the polygon as a hole)
229  // (3) Construc the offset contours
230  // (4) Identify the offset contour that corresponds to the frame and remove it from the result
231 
232  // First we need to determine the proper separation between the polygon and the frame.
233  // We use this helper function provided in the package.
234  boost::optional<double> margin = CGAL::compute_outer_frame_margin(star.begin(),star.end(), offset);
235 
236 
237 
238  // Proceed only if the margin was computed (an extremely sharp corner might cause overflow)
239  if ( margin )
240  {
241  // Get the bbox of the polygon
242  CGAL::Bbox_2 bbox = CGAL::bbox_2(star.begin(),star.end());
243 
244  // Compute the boundaries of the frame
245  double fxmin = bbox.xmin() - *margin ;
246  double fxmax = bbox.xmax() + *margin ;
247  double fymin = bbox.ymin() - *margin ;
248  double fymax = bbox.ymax() + *margin ;
249 
250  // Create the rectangular frame
251  Point_2 frame[4]= { Point_2(fxmin,fymin)
252  , Point_2(fxmax,fymin)
253  , Point_2(fxmax,fymax)
254  , Point_2(fxmin,fymax)
255  } ;
256 
257 
258 
259  // Instantiate the skeleton builder
260  SsBuilder ssb ;
261 
262 
263  // Enter the frame
264  ssb.enter_contour(frame,frame+4);
265 
266 
267 
268  // Enter the polygon as a hole of the frame (NOTE: as it is a hole we insert it in the opposite orientation)
269  ssb.enter_contour(star.rbegin(),star.rend());
270 
271 
272 
273  // Construct the skeleton
274  boost::shared_ptr<Ss> ss = ssb.construct_skeleton();
275 
276 
277 
278  // Proceed only if the skeleton was correctly constructed.
279  if ( ss )
280  {
281  // Instantiate the container of offset contours
282  ContourSequence offset_contours ;
283 
284 
285  // Instantiate the offset builder with the skeleton
286  OffsetBuilder ob(*ss);
287 
288 
289  // Obtain the offset contours
290  ob.construct_offset_contours(offset, std::back_inserter(offset_contours));
291 
292 
293  // Locate the offset contour that corresponds to the frame
294  // That must be the outmost offset contour, which in turn must be the one
295  // with the largetst unsigned area.
296  ContourSequence::iterator f = offset_contours.end();
297  double lLargestArea = 0.0 ;
298 
299  for (ContourSequence::iterator i = offset_contours.begin(); i != offset_contours.end(); ++ i )
300  {
301 
302  double lArea = CGAL_NTS abs( (*i)->area() ) ; //Take abs() as Polygon_2::area() is signed.
303  if ( lArea > lLargestArea )
304  {
305  f = i ;
306  lLargestArea = lArea ;
307  }
308  }
309 
310 
311  // Remove the offset contour that corresponds to the frame.
312  offset_contours.erase(f);
313 
314 
315  // Print out the skeleton
316  Halfedge_handle null_halfedge ;
317  Vertex_handle null_vertex ;
318 
319 
320 
321  // Dump the edges of the skeleton
322  //for ( Halfedge_iterator i = ss->halfedges_begin(); i != ss->halfedges_end(); ++i )
323  //{
324  //std::string edge_type = (i->is_bisector())? "bisector" : "contour";
325  //Vertex_handle s = i->opposite()->vertex();
326  //Vertex_handle t = i->vertex();
327  //std::cout << "(" << s->point() << ")->(" << t->point() << ") " << edge_type << std::endl;
328  //}
329 
330  // Dump the generated offset polygons
331 
332  //std::cout << offset_contours.size() << " offset contours obtained\n" ;
333 
334 
335  for (ContourSequence::const_iterator i = offset_contours.begin(); i != offset_contours.end(); ++ i )
336  {
337  // Each element in the offset_contours sequence is a shared pointer to a Polygon_2 instance.
338  //std::cout << (*i)->size() << " vertices in offset contour\n" ;
339  pc_local_extended->points.resize((*i)->size());
340  pc_local_extended->height=1;
341  pc_local_extended->width=(*i)->size();
342  int t=0;
343 
344  for (Contour::Vertex_const_iterator j = (*i)->vertices_begin(); j != (*i)->vertices_end(); ++ j )
345  {
346  //std::cout << "(" << j->x() << "," << j->y() << ")" << std::endl ;
347  pc_local_extended->points[t].x = j->x();
348  pc_local_extended->points[t].y = j->y();
349  pc_local_extended->points[t].z = 0;
350  t++;
351  }
352 
353  break; //Use only first offset contour
354  }
355  }
356  }
357 
358 
359 
360  //STEP 3. Transform pc local extended to pcout
361  //tf::Transform tr_inv = (*tr).inverse(); //the given transform goes from local to global. We want to change from global to local. Hence we use the inverse transform
362  pcl_ros::transformPointCloud(*pc_local_extended, *pcout, data.frames.current.transform);
363 
364 
365  //Delete temporary pc
366  pc_local.reset();
367  pc_local_extended.reset();
368 
369 }
370 
371 
372 
373 
374 
375 #endif
376 
379 /*Previous 3 lines appended automatically on Sun Feb 5 19:40:16 WET 2012 */
boost::shared_ptr< Contour > ContourPtr
Ss::Halfedge_handle Halfedge_handle
pcl::ModelCoefficients::Ptr current
CGAL::Straight_skeleton_2< Kernel > Ss
CGAL::Straight_skeleton_builder_2< SsBuilderTraits, Ss > SsBuilder
CGAL::Polygon_offset_builder_2< Ss, OffsetBuilderTraits, Contour > OffsetBuilder
CGAL::Polygon_offset_builder_traits_2< Kernel > OffsetBuilderTraits
Ss::Vertex_handle Vertex_handle
struct t_polygon_primitive_data::@5 frames
the axis frames
std::vector< ContourPtr > ContourSequence
CGAL::Polygon_2< Kernel > Contour
t_polygon_primitive_data data
A class c_polygon_primitive that contains information about a detected polygon primitive as well as t...
CGAL::Exact_predicates_inexact_constructions_kernel Kernel
void offset_polygon(double val, const pcl::PointCloud< pcl::PointXYZ >::Ptr pcin, pcl::PointCloud< pcl::PointXYZ >::Ptr pcout, tf::Transform *tr)
Offsets a given polygon. The offseting is produced along the XoY plane defined by the points in pcin...
Kernel::Point_2 Point_2
Ss::Halfedge_iterator Halfedge_iterator
CGAL::Straight_skeleton_builder_traits_2< Kernel > SsBuilderTraits


polygon_primitives_extraction
Author(s): Miguel Oliveira
autogenerated on Mon Mar 2 2015 01:32:42