void build_skeleton(const char* fname) { typedef typename Kernel::Point_2 Point_2; typedef CGAL::Polygon_2<Kernel> Polygon_2; typedef CGAL::Straight_skeleton_builder_traits_2<Kernel> SsBuilderTraits; typedef CGAL::Straight_skeleton_2<Kernel> Ss; typedef CGAL::Straight_skeleton_builder_2<SsBuilderTraits,Ss> SsBuilder; Polygon_2 pgn; std::ifstream input(fname); FT x,y; while(input) { input >> x; if (!input) break; input >> y; if (!input) break; pgn.push_back( Point_2( typename Kernel::FT(x), typename Kernel::FT(y) ) ); } input.close(); std::cout << "Polygon has " << pgn.size() << " points\n"; if(!pgn.is_counterclockwise_oriented()) { std::cerr << "Polygon is not CCW Oriented" << std::endl; } if(!pgn.is_simple()) { std::cerr << "Polygon is not simple" << std::endl; } CGAL::Timer time; time.start(); SsBuilder ssb; ssb.enter_contour(pgn.vertices_begin(), pgn.vertices_end()); boost::shared_ptr<Ss> straight_ske = ssb.construct_skeleton(); time.stop(); std::cout << "Time spent to build skeleton " << time.time() << "\n"; if(!straight_ske->is_valid()) { std::cerr << "Straight skeleton is not valid" << std::endl; } std::cerr.precision(60); print_straight_skeleton(*straight_ske); }
int main() { // A start-shaped polygon, oriented counter-clockwise as required for outer contours. Point_2 pts[] = { Point_2(-1,-1) , Point_2(0,-12) , Point_2(1,-1) , Point_2(12,0) , Point_2(1,1) , Point_2(0,12) , Point_2(-1,1) , Point_2(-12,0) } ; std::vector<Point_2> star(pts,pts+8); // We want an offset contour in the outside. // Since the package doesn't support that operation directly, we use the following trick: // (1) Place the polygon as a hole of a big outer frame. // (2) Construct the skeleton on the interior of that frame (with the polygon as a hole) // (3) Construc the offset contours // (4) Identify the offset contour that corresponds to the frame and remove it from the result double offset = 3 ; // The offset distance // First we need to determine the proper separation between the polygon and the frame. // We use this helper function provided in the package. boost::optional<double> margin = CGAL::compute_outer_frame_margin(star.begin(),star.end(),offset); // Proceed only if the margin was computed (an extremely sharp corner might cause overflow) if ( margin ) { // Get the bbox of the polygon CGAL::Bbox_2 bbox = CGAL::bbox_2(star.begin(),star.end()); // Compute the boundaries of the frame double fxmin = bbox.xmin() - *margin ; double fxmax = bbox.xmax() + *margin ; double fymin = bbox.ymin() - *margin ; double fymax = bbox.ymax() + *margin ; // Create the rectangular frame Point_2 frame[4]= { Point_2(fxmin,fymin) , Point_2(fxmax,fymin) , Point_2(fxmax,fymax) , Point_2(fxmin,fymax) } ; // Instantiate the skeleton builder SsBuilder ssb ; // Enter the frame ssb.enter_contour(frame,frame+4); // Enter the polygon as a hole of the frame (NOTE: as it is a hole we insert it in the opposite orientation) ssb.enter_contour(star.rbegin(),star.rend()); // Construct the skeleton boost::shared_ptr<Ss> ss = ssb.construct_skeleton(); // Proceed only if the skeleton was correctly constructed. if ( ss ) { print_straight_skeleton(*ss); // Instantiate the container of offset contours ContourSequence offset_contours ; // Instantiate the offset builder with the skeleton OffsetBuilder ob(*ss); // Obtain the offset contours ob.construct_offset_contours(offset, std::back_inserter(offset_contours)); // Locate the offset contour that corresponds to the frame // That must be the outmost offset contour, which in turn must be the one // with the largetst unsigned area. ContourSequence::iterator f = offset_contours.end(); double lLargestArea = 0.0 ; for (ContourSequence::iterator i = offset_contours.begin(); i != offset_contours.end(); ++ i ) { double lArea = CGAL_NTS abs( (*i)->area() ) ; //Take abs() as Polygon_2::area() is signed. if ( lArea > lLargestArea ) { f = i ; lLargestArea = lArea ; } } // Remove the offset contour that corresponds to the frame. offset_contours.erase(f); print_polygons(offset_contours); } } return 0; }
PwhPtr CstmCGAL::applyOffset(double offset, const Polygon_with_holes_2& poly) { // This code is inspired from the CGAL example Straight_skeleton_2/Low_level_API // As the offset can only produce an interior polygon, we need to produce a frame // that encloses the polygon and is big enough so that the offset of the contour // does not interfere with the one ot the polygon. See CGAL doc page for more info boost::optional<double> margin = CGAL::compute_outer_frame_margin( poly.outer_boundary().vertices_begin(),poly.outer_boundary().vertices_end(),offset); if ( margin ) { CGAL::Bbox_2 bbox = CGAL::bbox_2(poly.outer_boundary().vertices_begin(),poly.outer_boundary().vertices_end()); double fxmin = bbox.xmin() - *margin ; double fxmax = bbox.xmax() + *margin ; double fymin = bbox.ymin() - *margin ; double fymax = bbox.ymax() + *margin ; // Create the rectangular frame Point_2 frame[4]= { Point_2(fxmin,fymin) , Point_2(fxmax,fymin) , Point_2(fxmax,fymax) , Point_2(fxmin,fymax) } ; SsBuilder ssb ; ssb.enter_contour(frame,frame+4); // We have to revert the orientation of the polygon std::vector<Point_2> outerBoundary = std::vector<Point_2>( poly.outer_boundary().vertices_begin(),poly.outer_boundary().vertices_end()); ssb.enter_contour(outerBoundary.rbegin(), outerBoundary.rend()); SsPtr ss = ssb.construct_skeleton(); if ( ss ) { std::vector<Polygon_2Ptr> offset_contours ; OffsetBuilder ob(*ss); ob.construct_offset_contours(offset, std::back_inserter(offset_contours)); // Locate the offset contour that corresponds to the frame // That must be the outmost offset contour, which in turn must be the one // with the largest unsigned area. std::vector<Polygon_2Ptr>::iterator f = offset_contours.end(); double lLargestArea = 0.0 ; for (std::vector<Polygon_2Ptr>::iterator i = offset_contours.begin(); i != offset_contours.end(); ++ i) { double lArea = CGAL_NTS abs( (*i)->area() ) ; //Take abs() as Polygon_2::area() is signed. if ( lArea > lLargestArea ) { f = i ; lLargestArea = lArea ; } } offset_contours.erase(f); // Construct result polygon std::vector<Point_2> newOuterBoundary = std::vector<Point_2>( offset_contours.front()->vertices_begin(), offset_contours.front()->vertices_end()); Polygon_with_holes_2 result = Polygon_with_holes_2(Polygon_2(newOuterBoundary.rbegin(), newOuterBoundary.rend())); // We have to handle the holes separately for (auto it = poly.holes_begin() ; it != poly.holes_end() ; it++) { std::vector<Point_2> hole = std::vector<Point_2>(it->vertices_begin(),it->vertices_end()); std::vector<PwhPtr> holeOffsets = CGAL::create_interior_skeleton_and_offset_polygons_with_holes_2(offset, Polygon_with_holes_2(Polygon_2(hole.begin(), hole.end()))); for (auto it2 = holeOffsets.begin() ; it2 != holeOffsets.end() ; it++) { std::vector<Point_2> revertNewHoles = std::vector<Point_2>( (*it2)->outer_boundary().vertices_begin(),(*it2)->outer_boundary().vertices_end()); result.add_hole(Polygon_2(revertNewHoles.rbegin(), revertNewHoles.rend())); } } return boost::make_shared<Polygon_with_holes_2>(result); } } return NULL; }