2013-07-16 19:04:14 +00:00
# include "ClipperUtils.hpp"
2013-11-23 22:21:59 +00:00
# include "Geometry.hpp"
2019-09-26 15:30:03 +00:00
# include "ShortestPath.hpp"
2013-07-16 19:04:14 +00:00
2016-09-13 11:30:00 +00:00
// #define CLIPPER_UTILS_DEBUG
# ifdef CLIPPER_UTILS_DEBUG
# include "SVG.hpp"
# endif /* CLIPPER_UTILS_DEBUG */
2020-08-27 10:14:49 +00:00
// Profiling support using the Shiny intrusive profiler
//#define CLIPPER_UTILS_PROFILE
# if defined(SLIC3R_PROFILE) && defined(CLIPPER_UTILS_PROFILE)
# include <Shiny/Shiny.h>
# define CLIPPERUTILS_PROFILE_FUNC() PROFILE_FUNC()
# define CLIPPERUTILS_PROFILE_BLOCK(name) PROFILE_BLOCK(name)
# else
# define CLIPPERUTILS_PROFILE_FUNC()
# define CLIPPERUTILS_PROFILE_BLOCK(name)
# endif
2016-11-16 21:12:58 +00:00
2017-04-04 09:17:25 +00:00
# define CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR (0.005f)
2013-07-16 19:04:14 +00:00
namespace Slic3r {
2016-11-28 16:33:17 +00:00
# ifdef CLIPPER_UTILS_DEBUG
// For debugging the Clipper library, for providing bug reports to the Clipper author.
bool export_clipper_input_polygons_bin ( const char * path , const ClipperLib : : Paths & input_subject , const ClipperLib : : Paths & input_clip )
{
2016-12-13 18:22:23 +00:00
FILE * pfile = fopen ( path , " wb " ) ;
if ( pfile = = NULL )
return false ;
uint32_t sz = uint32_t ( input_subject . size ( ) ) ;
fwrite ( & sz , 1 , sizeof ( sz ) , pfile ) ;
for ( size_t i = 0 ; i < input_subject . size ( ) ; + + i ) {
const ClipperLib : : Path & path = input_subject [ i ] ;
sz = uint32_t ( path . size ( ) ) ;
: : fwrite ( & sz , 1 , sizeof ( sz ) , pfile ) ;
: : fwrite ( path . data ( ) , sizeof ( ClipperLib : : IntPoint ) , sz , pfile ) ;
}
sz = uint32_t ( input_clip . size ( ) ) ;
: : fwrite ( & sz , 1 , sizeof ( sz ) , pfile ) ;
for ( size_t i = 0 ; i < input_clip . size ( ) ; + + i ) {
const ClipperLib : : Path & path = input_clip [ i ] ;
sz = uint32_t ( path . size ( ) ) ;
: : fwrite ( & sz , 1 , sizeof ( sz ) , pfile ) ;
: : fwrite ( path . data ( ) , sizeof ( ClipperLib : : IntPoint ) , sz , pfile ) ;
}
: : fclose ( pfile ) ;
return true ;
2016-11-28 16:33:17 +00:00
err :
2016-12-13 18:22:23 +00:00
: : fclose ( pfile ) ;
return false ;
2016-11-28 16:33:17 +00:00
}
# endif /* CLIPPER_UTILS_DEBUG */
2021-04-30 09:49:57 +00:00
namespace ClipperUtils {
2021-05-03 13:00:23 +00:00
Points EmptyPathsProvider : : s_empty_points ;
2021-04-30 09:49:57 +00:00
Points SinglePathProvider : : s_end ;
2016-10-20 11:04:23 +00:00
}
2021-04-30 09:49:57 +00:00
static ExPolygons PolyTreeToExPolygons ( ClipperLib : : PolyTree & & polytree )
2013-07-16 19:04:14 +00:00
{
2021-04-30 09:49:57 +00:00
struct Inner {
2021-05-03 09:55:23 +00:00
static void PolyTreeToExPolygonsRecursive ( ClipperLib : : PolyNode & & polynode , ExPolygons * expolygons )
2021-04-30 09:49:57 +00:00
{
size_t cnt = expolygons - > size ( ) ;
expolygons - > resize ( cnt + 1 ) ;
( * expolygons ) [ cnt ] . contour . points = std : : move ( polynode . Contour ) ;
( * expolygons ) [ cnt ] . holes . resize ( polynode . ChildCount ( ) ) ;
for ( int i = 0 ; i < polynode . ChildCount ( ) ; + + i ) {
( * expolygons ) [ cnt ] . holes [ i ] . points = std : : move ( polynode . Childs [ i ] - > Contour ) ;
// Add outer polygons contained by (nested within) holes.
for ( int j = 0 ; j < polynode . Childs [ i ] - > ChildCount ( ) ; + + j )
2021-05-03 09:55:23 +00:00
PolyTreeToExPolygonsRecursive ( std : : move ( * polynode . Childs [ i ] - > Childs [ j ] ) , expolygons ) ;
2021-04-30 09:49:57 +00:00
}
2013-07-16 19:04:14 +00:00
}
2016-11-28 16:33:17 +00:00
2021-04-30 09:49:57 +00:00
static size_t PolyTreeCountExPolygons ( const ClipperLib : : PolyNode & polynode )
{
size_t cnt = 1 ;
for ( int i = 0 ; i < polynode . ChildCount ( ) ; + + i ) {
for ( int j = 0 ; j < polynode . Childs [ i ] - > ChildCount ( ) ; + + j )
cnt + = PolyTreeCountExPolygons ( * polynode . Childs [ i ] - > Childs [ j ] ) ;
}
return cnt ;
2016-11-28 16:33:17 +00:00
}
2021-04-30 09:49:57 +00:00
} ;
2016-12-13 21:13:02 +00:00
2021-04-30 09:49:57 +00:00
ExPolygons retval ;
size_t cnt = 0 ;
for ( int i = 0 ; i < polytree . ChildCount ( ) ; + + i )
cnt + = Inner : : PolyTreeCountExPolygons ( * polytree . Childs [ i ] ) ;
retval . reserve ( cnt ) ;
for ( int i = 0 ; i < polytree . ChildCount ( ) ; + + i )
Inner : : PolyTreeToExPolygonsRecursive ( std : : move ( * polytree . Childs [ i ] ) , & retval ) ;
2016-12-13 18:22:23 +00:00
return retval ;
2013-07-16 22:48:29 +00:00
}
2021-04-30 09:49:57 +00:00
Polylines PolyTreeToPolylines ( ClipperLib : : PolyTree & & polytree )
2015-07-23 13:53:02 +00:00
{
2021-04-30 09:49:57 +00:00
struct Inner {
static void AddPolyNodeToPaths ( ClipperLib : : PolyNode & polynode , Polylines & out )
{
if ( ! polynode . Contour . empty ( ) )
out . emplace_back ( std : : move ( polynode . Contour ) ) ;
for ( ClipperLib : : PolyNode * child : polynode . Childs )
AddPolyNodeToPaths ( * child , out ) ;
}
} ;
2016-12-13 20:30:56 +00:00
2021-04-30 09:49:57 +00:00
Polylines out ;
out . reserve ( polytree . Total ( ) ) ;
Inner : : AddPolyNodeToPaths ( polytree , out ) ;
return out ;
2015-07-23 13:53:02 +00:00
}
2019-10-25 11:34:37 +00:00
ExPolygons ClipperPaths_to_Slic3rExPolygons ( const ClipperLib : : Paths & input )
2013-11-20 10:35:58 +00:00
{
2016-12-13 18:22:23 +00:00
ClipperLib : : Clipper clipper ;
clipper . AddPaths ( input , ClipperLib : : ptSubject , true ) ;
ClipperLib : : PolyTree polytree ;
clipper . Execute ( ClipperLib : : ctUnion , polytree , ClipperLib : : pftEvenOdd , ClipperLib : : pftEvenOdd ) ; // offset results work with both EvenOdd and NonZero
2021-04-30 09:49:57 +00:00
return PolyTreeToExPolygons ( std : : move ( polytree ) ) ;
2013-11-20 10:35:58 +00:00
}
2021-04-30 09:49:57 +00:00
// Offset outside by 10um, one by one.
template < typename PathsProvider >
static ClipperLib : : Paths safety_offset ( PathsProvider & & paths )
2013-11-20 10:35:58 +00:00
{
2021-04-30 09:49:57 +00:00
ClipperLib : : ClipperOffset co ;
ClipperLib : : Paths out ;
out . reserve ( paths . size ( ) ) ;
ClipperLib : : Paths out_this ;
for ( const ClipperLib : : Path & path : paths ) {
co . Clear ( ) ;
co . MiterLimit = 2. ;
2021-05-05 10:16:40 +00:00
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
// contours will be CCW oriented even though the input paths are CW oriented.
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
2021-04-30 09:49:57 +00:00
co . AddPath ( path , ClipperLib : : jtMiter , ClipperLib : : etClosedPolygon ) ;
2021-05-05 10:16:40 +00:00
bool ccw = ClipperLib : : Orientation ( path ) ;
co . Execute ( out_this , ccw ? ClipperSafetyOffset : - ClipperSafetyOffset ) ;
if ( ! ccw ) {
// Reverse the resulting contours.
for ( ClipperLib : : Path & path : out_this )
std : : reverse ( path . begin ( ) , path . end ( ) ) ;
}
2021-04-30 09:49:57 +00:00
append ( out , std : : move ( out_this ) ) ;
}
return out ;
2016-12-13 20:30:56 +00:00
}
2021-05-05 10:16:40 +00:00
// Only safe for a single path.
2021-04-30 09:49:57 +00:00
template < typename PathsProvider >
ClipperLib : : Paths _offset ( PathsProvider & & input , ClipperLib : : EndType endType , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
2016-12-13 18:22:23 +00:00
{
2013-07-16 22:48:29 +00:00
// perform offset
2016-12-13 18:22:23 +00:00
ClipperLib : : ClipperOffset co ;
if ( joinType = = jtRound )
co . ArcTolerance = miterLimit ;
else
co . MiterLimit = miterLimit ;
2021-04-13 09:31:54 +00:00
float delta_scaled = delta ;
2017-04-04 09:17:25 +00:00
co . ShortestEdgeLength = double ( std : : abs ( delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR ) ) ;
2021-04-30 09:49:57 +00:00
co . AddPaths ( std : : forward < PathsProvider > ( input ) , joinType , endType ) ;
2016-12-13 18:22:23 +00:00
ClipperLib : : Paths retval ;
2017-04-04 09:17:25 +00:00
co . Execute ( retval , delta_scaled ) ;
2016-12-13 18:22:23 +00:00
return retval ;
}
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons offset ( const Slic3r : : Polygon & polygon , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
2021-04-30 09:49:57 +00:00
{ return to_polygons ( _offset ( ClipperUtils : : SinglePathProvider ( polygon . points ) , ClipperLib : : etClosedPolygon , delta , joinType , miterLimit ) ) ; }
# ifdef CLIPPERUTILS_UNSAFE_OFFSET
Slic3r : : Polygons offset ( const Slic3r : : Polygons & polygons , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return to_polygons ( _offset ( ClipperUtils : : PolygonsProvider ( polygons ) , ClipperLib : : etClosedPolygon , delta , joinType , miterLimit ) ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons offset_ex ( const Slic3r : : Polygons & polygons , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return ClipperPaths_to_Slic3rExPolygons ( _offset ( ClipperUtils : : PolygonsProvider ( polygons ) , ClipperLib : : etClosedPolygon , delta , joinType , miterLimit ) ) ; }
2021-04-30 09:49:57 +00:00
# endif // CLIPPERUTILS_UNSAFE_OFFSET
Slic3r : : Polygons offset ( const Slic3r : : Polyline & polyline , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return to_polygons ( _offset ( ClipperUtils : : SinglePathProvider ( polyline . points ) , ClipperLib : : etOpenButt , delta , joinType , miterLimit ) ) ; }
Slic3r : : Polygons offset ( const Slic3r : : Polylines & polylines , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return to_polygons ( _offset ( ClipperUtils : : PolylinesProvider ( polylines ) , ClipperLib : : etOpenButt , delta , joinType , miterLimit ) ) ; }
2021-05-03 09:39:53 +00:00
// returns number of expolygons collected (0 or 1).
static int offset_expolygon_inner ( const Slic3r : : ExPolygon & expoly , const float delta , ClipperLib : : JoinType joinType , double miterLimit , ClipperLib : : Paths & out )
2016-10-20 11:04:23 +00:00
{
// 1) Offset the outer contour.
ClipperLib : : Paths contours ;
{
ClipperLib : : ClipperOffset co ;
if ( joinType = = jtRound )
2021-04-13 09:31:54 +00:00
co . ArcTolerance = miterLimit ;
2016-10-20 11:04:23 +00:00
else
co . MiterLimit = miterLimit ;
2021-05-03 09:39:53 +00:00
co . ShortestEdgeLength = double ( std : : abs ( delta * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR ) ) ;
co . AddPath ( expoly . contour . points , joinType , ClipperLib : : etClosedPolygon ) ;
co . Execute ( contours , delta ) ;
2016-10-20 11:04:23 +00:00
}
2021-05-03 09:39:53 +00:00
if ( contours . empty ( ) )
// No need to try to offset the holes.
return 0 ;
2016-10-20 11:04:23 +00:00
2021-05-03 09:39:53 +00:00
if ( expoly . holes . empty ( ) ) {
// No need to subtract holes from the offsetted expolygon, we are done.
append ( out , std : : move ( contours ) ) ;
} else {
// 2) Offset the holes one by one, collect the offsetted holes.
ClipperLib : : Paths holes ;
{
for ( const Polygon & hole : expoly . holes ) {
ClipperLib : : ClipperOffset co ;
if ( joinType = = jtRound )
co . ArcTolerance = miterLimit ;
else
co . MiterLimit = miterLimit ;
co . ShortestEdgeLength = double ( std : : abs ( delta * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR ) ) ;
co . AddPath ( hole . points , joinType , ClipperLib : : etClosedPolygon ) ;
ClipperLib : : Paths out2 ;
// Execute reorients the contours so that the outer most contour has a positive area. Thus the output
// contours will be CCW oriented even though the input paths are CW oriented.
// Offset is applied after contour reorientation, thus the signum of the offset value is reversed.
co . Execute ( out2 , - delta ) ;
append ( holes , std : : move ( out2 ) ) ;
}
2016-10-20 11:04:23 +00:00
}
2021-05-03 09:39:53 +00:00
// 3) Subtract holes from the contours.
if ( holes . empty ( ) ) {
// No hole remaining after an offset. Just copy the outer contour.
append ( out , std : : move ( contours ) ) ;
} else if ( delta < 0 ) {
// Negative offset. There is a chance, that the offsetted hole intersects the outer contour.
// Subtract the offsetted holes from the offsetted contours.
ClipperLib : : Clipper clipper ;
clipper . Clear ( ) ;
clipper . AddPaths ( contours , ClipperLib : : ptSubject , true ) ;
clipper . AddPaths ( holes , ClipperLib : : ptClip , true ) ;
ClipperLib : : Paths output ;
clipper . Execute ( ClipperLib : : ctDifference , output , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
if ( ! output . empty ( ) ) {
append ( out , std : : move ( output ) ) ;
} else {
// The offsetted holes have eaten up the offsetted outer contour.
return 0 ;
}
} else {
// Positive offset. As long as the Clipper offset does what one expects it to do, the offsetted hole will have a smaller
// area than the original hole or even disappear, therefore there will be no new intersections.
// Just collect the reversed holes.
out . reserve ( contours . size ( ) + holes . size ( ) ) ;
append ( out , std : : move ( contours ) ) ;
// Reverse the holes in place.
for ( size_t i = 0 ; i < holes . size ( ) ; + + i )
std : : reverse ( holes [ i ] . begin ( ) , holes [ i ] . end ( ) ) ;
append ( out , std : : move ( holes ) ) ;
}
2016-10-20 11:04:23 +00:00
}
2021-05-03 09:39:53 +00:00
return 1 ;
}
static int offset_expolygon_inner ( const Slic3r : : Surface & surface , const float delta , ClipperLib : : JoinType joinType , double miterLimit , ClipperLib : : Paths & out )
{ return offset_expolygon_inner ( surface . expolygon , delta , joinType , miterLimit , out ) ; }
static int offset_expolygon_inner ( const Slic3r : : Surface * surface , const float delta , ClipperLib : : JoinType joinType , double miterLimit , ClipperLib : : Paths & out )
{ return offset_expolygon_inner ( surface - > expolygon , delta , joinType , miterLimit , out ) ; }
ClipperLib : : Paths _offset ( const Slic3r : : ExPolygon & expolygon , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{
ClipperLib : : Paths out ;
offset_expolygon_inner ( expolygon , delta , joinType , miterLimit , out ) ;
return out ;
2016-11-07 21:49:11 +00:00
}
2017-01-02 15:51:43 +00:00
// This is a safe variant of the polygons offset, tailored for multiple ExPolygons.
// It is required, that the input expolygons do not overlap and that the holes of each ExPolygon don't intersect with their respective outer contours.
// Each ExPolygon is offsetted separately, then the offsetted ExPolygons are united.
2021-05-03 09:39:53 +00:00
template < typename ExPolygonVector >
ClipperLib : : Paths _offset ( const ExPolygonVector & expolygons , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
2016-11-07 21:49:11 +00:00
{
2017-01-02 15:51:43 +00:00
// Offsetted ExPolygons before they are united.
2021-05-03 09:39:53 +00:00
ClipperLib : : Paths output ;
output . reserve ( expolygons . size ( ) ) ;
// How many non-empty offsetted expolygons were actually collected into output?
2017-01-02 15:51:43 +00:00
// If only one, then there is no need to do a final union.
size_t expolygons_collected = 0 ;
2021-05-03 09:39:53 +00:00
for ( const auto & expoly : expolygons )
expolygons_collected + = offset_expolygon_inner ( expoly , delta , joinType , miterLimit , output ) ;
2016-11-07 21:49:11 +00:00
2017-01-02 15:51:43 +00:00
// 4) Unite the offsetted expolygons.
if ( expolygons_collected > 1 & & delta > 0 ) {
// There is a chance that the outwards offsetted expolygons may intersect. Perform a union.
2016-11-07 21:49:11 +00:00
ClipperLib : : Clipper clipper ;
2017-01-02 15:51:43 +00:00
clipper . Clear ( ) ;
2021-05-03 09:39:53 +00:00
clipper . AddPaths ( output , ClipperLib : : ptSubject , true ) ;
2017-01-02 15:51:43 +00:00
clipper . Execute ( ClipperLib : : ctUnion , output , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
} else {
// Negative offset. The shrunk expolygons shall not mutually intersect. Just copy the output.
2016-11-07 21:49:11 +00:00
}
2016-12-13 18:22:23 +00:00
return output ;
2016-11-07 21:49:11 +00:00
}
2021-05-03 09:39:53 +00:00
Slic3r : : Polygons offset ( const Slic3r : : ExPolygon & expolygon , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return to_polygons ( _offset ( expolygon , delta , joinType , miterLimit ) ) ; }
Slic3r : : Polygons offset ( const Slic3r : : ExPolygons & expolygons , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return to_polygons ( _offset ( expolygons , delta , joinType , miterLimit ) ) ; }
Slic3r : : Polygons offset ( const Slic3r : : Surfaces & surfaces , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return to_polygons ( _offset ( surfaces , delta , joinType , miterLimit ) ) ; }
Slic3r : : Polygons offset ( const Slic3r : : SurfacesPtr & surfaces , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return to_polygons ( _offset ( surfaces , delta , joinType , miterLimit ) ) ; }
Slic3r : : ExPolygons offset_ex ( const Slic3r : : ExPolygon & expolygon , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return ClipperPaths_to_Slic3rExPolygons ( _offset ( expolygon , delta , joinType , miterLimit ) ) ; }
Slic3r : : ExPolygons offset_ex ( const Slic3r : : ExPolygons & expolygons , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return ClipperPaths_to_Slic3rExPolygons ( _offset ( expolygons , delta , joinType , miterLimit ) ) ; }
Slic3r : : ExPolygons offset_ex ( const Slic3r : : Surfaces & surfaces , const float delta , ClipperLib : : JoinType joinType , double miterLimit )
{ return ClipperPaths_to_Slic3rExPolygons ( _offset ( surfaces , delta , joinType , miterLimit ) ) ; }
2021-05-05 10:16:40 +00:00
# ifdef CLIPPERUTILS_UNSAFE_OFFSET
Slic3r : : Polygons union_safety_offset ( const Slic3r : : Polygons & polygons )
{ return offset ( polygons , ClipperSafetyOffset ) ; }
Slic3r : : ExPolygons union_safety_offset_ex ( const Slic3r : : Polygons & polygons )
{ return offset_ex ( polygons , ClipperSafetyOffset ) ; }
# endif // CLIPPERUTILS_UNSAFE_OFFSET
Slic3r : : Polygons union_safety_offset ( const Slic3r : : ExPolygons & expolygons )
{ return offset ( expolygons , ClipperSafetyOffset ) ; }
Slic3r : : ExPolygons union_safety_offset_ex ( const Slic3r : : ExPolygons & expolygons )
{ return offset_ex ( expolygons , ClipperSafetyOffset ) ; }
2021-04-30 09:49:57 +00:00
ClipperLib : : Paths _offset2 ( const Polygons & polygons , const float delta1 , const float delta2 , const ClipperLib : : JoinType joinType , const double miterLimit )
2016-11-07 21:49:11 +00:00
{
2013-12-24 11:40:46 +00:00
// prepare ClipperOffset object
ClipperLib : : ClipperOffset co ;
if ( joinType = = jtRound ) {
2016-12-13 18:22:23 +00:00
co . ArcTolerance = miterLimit ;
2013-12-24 11:40:46 +00:00
} else {
co . MiterLimit = miterLimit ;
}
2021-04-13 09:31:54 +00:00
float delta_scaled1 = delta1 ;
float delta_scaled2 = delta2 ;
2017-04-04 09:17:25 +00:00
co . ShortestEdgeLength = double ( std : : max ( std : : abs ( delta_scaled1 ) , std : : abs ( delta_scaled2 ) ) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR ) ;
2013-07-16 19:04:14 +00:00
// perform first offset
2013-12-24 11:40:46 +00:00
ClipperLib : : Paths output1 ;
2021-04-30 09:49:57 +00:00
co . AddPaths ( ClipperUtils : : PolygonsProvider ( polygons ) , joinType , ClipperLib : : etClosedPolygon ) ;
2017-04-04 09:17:25 +00:00
co . Execute ( output1 , delta_scaled1 ) ;
2013-07-16 19:04:14 +00:00
// perform second offset
2013-12-24 11:40:46 +00:00
co . Clear ( ) ;
co . AddPaths ( output1 , joinType , ClipperLib : : etClosedPolygon ) ;
2016-12-13 18:22:23 +00:00
ClipperLib : : Paths retval ;
2017-04-04 09:17:25 +00:00
co . Execute ( retval , delta_scaled2 ) ;
2016-12-13 18:22:23 +00:00
return retval ;
2013-07-16 22:48:29 +00:00
}
2021-04-30 09:49:57 +00:00
Polygons offset2 ( const Polygons & polygons , const float delta1 , const float delta2 , const ClipperLib : : JoinType joinType , const double miterLimit )
2013-07-16 22:48:29 +00:00
{
2021-04-30 09:49:57 +00:00
return to_polygons ( _offset2 ( polygons , delta1 , delta2 , joinType , miterLimit ) ) ;
2015-07-23 13:53:02 +00:00
}
2021-04-30 09:49:57 +00:00
ExPolygons offset2_ex ( const Polygons & polygons , const float delta1 , const float delta2 , const ClipperLib : : JoinType joinType , const double miterLimit )
2013-07-16 22:48:29 +00:00
{
2021-04-30 09:49:57 +00:00
return ClipperPaths_to_Slic3rExPolygons ( _offset2 ( polygons , delta1 , delta2 , joinType , miterLimit ) ) ;
2015-07-23 13:53:02 +00:00
}
2018-05-18 07:52:09 +00:00
//FIXME Vojtech: This functon may likely be optimized to avoid some of the Slic3r to Clipper
2021-05-03 09:39:53 +00:00
// conversions and unnecessary Clipper calls. It is not that bad now as Clipper uses Slic3r's own Point / Polygon types directly.
Polygons offset2 ( const ExPolygons & expolygons , const float delta1 , const float delta2 , ClipperLib : : JoinType joinType , double miterLimit )
2018-05-18 07:52:09 +00:00
{
2021-05-03 09:39:53 +00:00
return offset ( offset_ex ( expolygons , delta1 , joinType , miterLimit ) , delta2 , joinType , miterLimit ) ;
}
ExPolygons offset2_ex ( const ExPolygons & expolygons , const float delta1 , const float delta2 , ClipperLib : : JoinType joinType , double miterLimit )
{
return offset_ex ( offset_ex ( expolygons , delta1 , joinType , miterLimit ) , delta2 , joinType , miterLimit ) ;
2018-05-18 07:52:09 +00:00
}
2021-04-30 09:49:57 +00:00
template < class TResult , class TSubj , class TClip >
TResult _clipper_do (
const ClipperLib : : ClipType clipType ,
TSubj & & subject ,
TClip & & clip ,
const ClipperLib : : PolyFillType fillType )
2013-07-16 19:04:14 +00:00
{
ClipperLib : : Clipper clipper ;
2021-04-30 09:49:57 +00:00
clipper . AddPaths ( std : : forward < TSubj > ( subject ) , ClipperLib : : ptSubject , true ) ;
clipper . AddPaths ( std : : forward < TClip > ( clip ) , ClipperLib : : ptClip , true ) ;
TResult retval ;
2016-12-13 18:22:23 +00:00
clipper . Execute ( clipType , retval , fillType , fillType ) ;
return retval ;
2013-07-16 23:21:30 +00:00
}
2021-04-30 09:49:57 +00:00
template < class TResult , class TSubj , class TClip >
TResult _clipper_do (
const ClipperLib : : ClipType clipType ,
TSubj & & subject ,
TClip & & clip ,
const ClipperLib : : PolyFillType fillType ,
2021-05-05 10:16:40 +00:00
const ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{
2021-05-05 10:16:40 +00:00
// Safety offset only allowed on intersection and difference.
assert ( do_safety_offset = = ApplySafetyOffset : : No | | clipType ! = ClipperLib : : ctUnion ) ;
return do_safety_offset = = ApplySafetyOffset : : Yes ?
_clipper_do < TResult > ( clipType , std : : forward < TSubj > ( subject ) , safety_offset ( std : : forward < TClip > ( clip ) ) , fillType ) :
2021-04-30 09:49:57 +00:00
_clipper_do < TResult > ( clipType , std : : forward < TSubj > ( subject ) , std : : forward < TClip > ( clip ) , fillType ) ;
}
2017-03-02 15:39:43 +00:00
// Fix of #117: A large fractal pyramid takes ages to slice
// The Clipper library has difficulties processing overlapping polygons.
2019-10-25 11:34:37 +00:00
// Namely, the function ClipperLib::JoinCommonEdges() has potentially a terrible time complexity if the output
2017-03-02 15:39:43 +00:00
// of the operation is of the PolyTree type.
2021-04-30 09:49:57 +00:00
// This function implemenets a following workaround:
2017-03-02 15:39:43 +00:00
// 1) Peform the Clipper operation with the output to Paths. This method handles overlaps in a reasonable time.
// 2) Run Clipper Union once again to extract the PolyTree from the result of 1).
2021-04-30 09:49:57 +00:00
template < typename PathProvider1 , typename PathProvider2 >
inline ClipperLib : : PolyTree _clipper_do_polytree2 (
const ClipperLib : : ClipType clipType ,
PathProvider1 & & subject ,
PathProvider2 & & clip ,
const ClipperLib : : PolyFillType fillType )
2017-03-02 15:39:43 +00:00
{
ClipperLib : : Clipper clipper ;
2021-04-30 09:49:57 +00:00
clipper . AddPaths ( std : : forward < PathProvider1 > ( subject ) , ClipperLib : : ptSubject , true ) ;
clipper . AddPaths ( std : : forward < PathProvider2 > ( clip ) , ClipperLib : : ptClip , true ) ;
2017-03-02 15:39:43 +00:00
// Perform the operation with the output to input_subject.
// This pass does not generate a PolyTree, which is a very expensive operation with the current Clipper library
// if there are overapping edges.
2021-04-30 09:49:57 +00:00
ClipperLib : : Paths input_subject ;
2017-03-02 15:39:43 +00:00
clipper . Execute ( clipType , input_subject , fillType , fillType ) ;
// Perform an additional Union operation to generate the PolyTree ordering.
clipper . Clear ( ) ;
clipper . AddPaths ( input_subject , ClipperLib : : ptSubject , true ) ;
ClipperLib : : PolyTree retval ;
clipper . Execute ( ClipperLib : : ctUnion , retval , fillType , fillType ) ;
return retval ;
}
2021-04-30 09:49:57 +00:00
template < typename PathProvider1 , typename PathProvider2 >
inline ClipperLib : : PolyTree _clipper_do_polytree2 (
const ClipperLib : : ClipType clipType ,
PathProvider1 & & subject ,
PathProvider2 & & clip ,
const ClipperLib : : PolyFillType fillType ,
2021-05-05 10:16:40 +00:00
const ApplySafetyOffset do_safety_offset )
2013-07-16 23:21:30 +00:00
{
2021-05-05 10:16:40 +00:00
assert ( do_safety_offset = = ApplySafetyOffset : : No | | clipType ! = ClipperLib : : ctUnion ) ;
return do_safety_offset = = ApplySafetyOffset : : Yes ?
_clipper_do_polytree2 ( clipType , std : : forward < PathProvider1 > ( subject ) , safety_offset ( std : : forward < PathProvider2 > ( clip ) ) , fillType ) :
2021-04-30 09:49:57 +00:00
_clipper_do_polytree2 ( clipType , std : : forward < PathProvider1 > ( subject ) , std : : forward < PathProvider2 > ( clip ) , fillType ) ;
2013-07-16 23:21:30 +00:00
}
2021-04-30 09:49:57 +00:00
template < class TSubj , class TClip >
2021-05-05 10:16:40 +00:00
static inline Polygons _clipper ( ClipperLib : : ClipType clipType , TSubj & & subject , TClip & & clip , ApplySafetyOffset do_safety_offset )
2013-07-16 23:21:30 +00:00
{
2021-04-30 09:49:57 +00:00
return to_polygons ( _clipper_do < ClipperLib : : Paths > ( clipType , std : : forward < TSubj > ( subject ) , std : : forward < TClip > ( clip ) , ClipperLib : : pftNonZero , do_safety_offset ) ) ;
2013-07-16 19:04:14 +00:00
}
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons diff ( const Slic3r : : Polygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper ( ClipperLib : : ctDifference , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-27 12:29:51 +00:00
Slic3r : : Polygons diff ( const Slic3r : : Polygons & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
{ return _clipper ( ClipperLib : : ctDifference , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons diff ( const Slic3r : : ExPolygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper ( ClipperLib : : ctDifference , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons diff ( const Slic3r : : ExPolygons & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper ( ClipperLib : : ctDifference , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons intersection ( const Slic3r : : Polygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper ( ClipperLib : : ctIntersection , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons intersection ( const Slic3r : : ExPolygon & subject , const Slic3r : : ExPolygon & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper ( ClipperLib : : ctIntersection , ClipperUtils : : ExPolygonProvider ( subject ) , ClipperUtils : : ExPolygonProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons intersection ( const Slic3r : : ExPolygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper ( ClipperLib : : ctIntersection , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons intersection ( const Slic3r : : ExPolygons & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper ( ClipperLib : : ctIntersection , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons intersection ( const Slic3r : : Surfaces & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper ( ClipperLib : : ctIntersection , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons intersection ( const Slic3r : : Surfaces & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper ( ClipperLib : : ctIntersection , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : Polygons union_ ( const Slic3r : : Polygons & subject )
{ return _clipper ( ClipperLib : : ctUnion , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : EmptyPathsProvider ( ) , ApplySafetyOffset : : No ) ; }
Slic3r : : Polygons union_ ( const Slic3r : : ExPolygons & subject )
{ return _clipper ( ClipperLib : : ctUnion , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : EmptyPathsProvider ( ) , ApplySafetyOffset : : No ) ; }
Slic3r : : Polygons union_ ( const Slic3r : : Polygons & subject , const Slic3r : : Polygons & subject2 )
{ return _clipper ( ClipperLib : : ctUnion , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( subject2 ) , ApplySafetyOffset : : No ) ; }
2021-04-30 09:49:57 +00:00
template < typename TSubject , typename TClip >
2021-06-01 10:33:14 +00:00
static ExPolygons _clipper_ex ( ClipperLib : : ClipType clipType , TSubject & & subject , TClip & & clip , ApplySafetyOffset do_safety_offset , ClipperLib : : PolyFillType fill_type = ClipperLib : : pftNonZero )
2021-06-01 09:10:12 +00:00
{ return PolyTreeToExPolygons ( _clipper_do_polytree2 ( clipType , std : : forward < TSubject > ( subject ) , std : : forward < TClip > ( clip ) , fill_type , do_safety_offset ) ) ; }
2021-04-30 09:49:57 +00:00
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : Polygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : Polygons & subject , const Slic3r : : Surfaces & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : SurfacesProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : Polygons & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : ExPolygon & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : ExPolygonProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : ExPolygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : ExPolygons & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : Surfaces & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : Surfaces & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : ExPolygons & subject , const Slic3r : : Surfaces & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : SurfacesProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : Surfaces & subject , const Slic3r : : Surfaces & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : SurfacesProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons diff_ex ( const Slic3r : : SurfacesPtr & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctDifference , ClipperUtils : : SurfacesPtrProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : Polygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : ExPolygon & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : ExPolygonProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : Polygons & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : ExPolygons & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : ExPolygons & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-04-30 09:49:57 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : Surfaces & subject , const Slic3r : : Polygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : Surfaces & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : Surfaces & subject , const Slic3r : : Surfaces & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : SurfacesProvider ( clip ) , do_safety_offset ) ; }
2021-05-05 10:16:40 +00:00
Slic3r : : ExPolygons intersection_ex ( const Slic3r : : SurfacesPtr & subject , const Slic3r : : ExPolygons & clip , ApplySafetyOffset do_safety_offset )
2021-05-03 09:39:53 +00:00
{ return _clipper_ex ( ClipperLib : : ctIntersection , ClipperUtils : : SurfacesPtrProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) , do_safety_offset ) ; }
2021-06-01 09:10:12 +00:00
// May be used to "heal" unusual models (3DLabPrints etc.) by providing fill_type (pftEvenOdd, pftNonZero, pftPositive, pftNegative).
Slic3r : : ExPolygons union_ex ( const Slic3r : : Polygons & subject , ClipperLib : : PolyFillType fill_type )
{ return _clipper_ex ( ClipperLib : : ctUnion , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : EmptyPathsProvider ( ) , ApplySafetyOffset : : No , fill_type ) ; }
Slic3r : : ExPolygons union_ex ( const Slic3r : : ExPolygons & subject )
2021-04-30 09:49:57 +00:00
{ return PolyTreeToExPolygons ( _clipper_do_polytree2 ( ClipperLib : : ctUnion , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : EmptyPathsProvider ( ) , ClipperLib : : pftNonZero ) ) ; }
2021-06-01 09:10:12 +00:00
Slic3r : : ExPolygons union_ex ( const Slic3r : : Surfaces & subject )
2021-04-30 09:49:57 +00:00
{ return PolyTreeToExPolygons ( _clipper_do_polytree2 ( ClipperLib : : ctUnion , ClipperUtils : : SurfacesProvider ( subject ) , ClipperUtils : : EmptyPathsProvider ( ) , ClipperLib : : pftNonZero ) ) ; }
2021-05-03 09:39:53 +00:00
template < typename PathsProvider1 , typename PathsProvider2 >
2021-05-05 10:16:40 +00:00
Polylines _clipper_pl_open ( ClipperLib : : ClipType clipType , PathsProvider1 & & subject , PathsProvider2 & & clip )
2013-11-21 13:15:38 +00:00
{
2021-04-30 09:49:57 +00:00
ClipperLib : : Clipper clipper ;
2021-05-03 09:39:53 +00:00
clipper . AddPaths ( std : : forward < PathsProvider1 > ( subject ) , ClipperLib : : ptSubject , false ) ;
2021-05-05 10:16:40 +00:00
clipper . AddPaths ( std : : forward < PathsProvider2 > ( clip ) , ClipperLib : : ptClip , true ) ;
2021-04-30 09:49:57 +00:00
ClipperLib : : PolyTree retval ;
clipper . Execute ( clipType , retval , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
return PolyTreeToPolylines ( std : : move ( retval ) ) ;
2014-11-15 21:41:22 +00:00
}
2021-05-03 09:39:53 +00:00
// If the split_at_first_point() call above happens to split the polygon inside the clipping area
// we would get two consecutive polylines instead of a single one, so we go through them in order
// to recombine continuous polylines.
static void _clipper_pl_recombine ( Polylines & polylines )
2014-05-21 13:03:31 +00:00
{
2021-05-03 09:39:53 +00:00
for ( size_t i = 0 ; i < polylines . size ( ) ; + + i ) {
for ( size_t j = i + 1 ; j < polylines . size ( ) ; + + j ) {
if ( polylines [ i ] . points . back ( ) = = polylines [ j ] . points . front ( ) ) {
2014-05-08 09:07:37 +00:00
/* If last point of i coincides with first point of j,
append points of j to i and delete j */
2021-05-03 09:39:53 +00:00
polylines [ i ] . points . insert ( polylines [ i ] . points . end ( ) , polylines [ j ] . points . begin ( ) + 1 , polylines [ j ] . points . end ( ) ) ;
polylines . erase ( polylines . begin ( ) + j ) ;
2014-05-08 09:07:37 +00:00
- - j ;
2021-05-03 09:39:53 +00:00
} else if ( polylines [ i ] . points . front ( ) = = polylines [ j ] . points . back ( ) ) {
2014-05-08 09:07:37 +00:00
/* If first point of i coincides with last point of j,
prepend points of j to i and delete j */
2021-05-03 09:39:53 +00:00
polylines [ i ] . points . insert ( polylines [ i ] . points . begin ( ) , polylines [ j ] . points . begin ( ) , polylines [ j ] . points . end ( ) - 1 ) ;
polylines . erase ( polylines . begin ( ) + j ) ;
2014-05-08 09:07:37 +00:00
- - j ;
2021-05-03 09:39:53 +00:00
} else if ( polylines [ i ] . points . front ( ) = = polylines [ j ] . points . front ( ) ) {
2014-05-08 09:07:37 +00:00
/* Since Clipper does not preserve orientation of polylines,
also check the case when first point of i coincides with first point of j . */
2021-05-03 09:39:53 +00:00
polylines [ j ] . reverse ( ) ;
polylines [ i ] . points . insert ( polylines [ i ] . points . begin ( ) , polylines [ j ] . points . begin ( ) , polylines [ j ] . points . end ( ) - 1 ) ;
polylines . erase ( polylines . begin ( ) + j ) ;
2014-05-08 09:07:37 +00:00
- - j ;
2021-05-03 09:39:53 +00:00
} else if ( polylines [ i ] . points . back ( ) = = polylines [ j ] . points . back ( ) ) {
2014-05-08 09:07:37 +00:00
/* Since Clipper does not preserve orientation of polylines,
also check the case when last point of i coincides with last point of j . */
2021-05-03 09:39:53 +00:00
polylines [ j ] . reverse ( ) ;
polylines [ i ] . points . insert ( polylines [ i ] . points . end ( ) , polylines [ j ] . points . begin ( ) + 1 , polylines [ j ] . points . end ( ) ) ;
polylines . erase ( polylines . begin ( ) + j ) ;
2014-05-08 09:07:37 +00:00
- - j ;
}
}
2014-12-24 09:20:55 +00:00
}
2021-05-03 09:39:53 +00:00
}
template < typename PathProvider1 , typename PathProvider2 >
2021-05-05 10:16:40 +00:00
Polylines _clipper_pl_closed ( ClipperLib : : ClipType clipType , PathProvider1 & & subject , PathProvider2 & & clip )
2021-05-03 09:39:53 +00:00
{
// Transform input polygons into open paths.
ClipperLib : : Paths paths ;
paths . reserve ( subject . size ( ) ) ;
for ( const Points & poly : subject ) {
// Emplace polygon, duplicate the 1st point.
paths . push_back ( { } ) ;
ClipperLib : : Path & path = paths . back ( ) ;
path . reserve ( poly . size ( ) + 1 ) ;
path = poly ;
path . emplace_back ( poly . front ( ) ) ;
}
// perform clipping
2021-05-05 10:16:40 +00:00
Polylines retval = _clipper_pl_open ( clipType , paths , std : : forward < PathProvider2 > ( clip ) ) ;
2021-05-03 09:39:53 +00:00
_clipper_pl_recombine ( retval ) ;
2016-12-13 18:22:23 +00:00
return retval ;
2014-11-09 14:27:34 +00:00
}
2021-05-05 10:16:40 +00:00
Slic3r : : Polylines diff_pl ( const Slic3r : : Polylines & subject , const Slic3r : : Polygons & clip )
{ return _clipper_pl_open ( ClipperLib : : ctDifference , ClipperUtils : : PolylinesProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) ) ; }
Slic3r : : Polylines diff_pl ( const Slic3r : : Polylines & subject , const Slic3r : : ExPolygon & clip )
{ return _clipper_pl_open ( ClipperLib : : ctDifference , ClipperUtils : : PolylinesProvider ( subject ) , ClipperUtils : : ExPolygonProvider ( clip ) ) ; }
Slic3r : : Polylines diff_pl ( const Slic3r : : Polylines & subject , const Slic3r : : ExPolygons & clip )
{ return _clipper_pl_open ( ClipperLib : : ctDifference , ClipperUtils : : PolylinesProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) ) ; }
Slic3r : : Polylines diff_pl ( const Slic3r : : Polygons & subject , const Slic3r : : Polygons & clip )
{ return _clipper_pl_closed ( ClipperLib : : ctDifference , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) ) ; }
Slic3r : : Polylines intersection_pl ( const Slic3r : : Polylines & subject , const Slic3r : : Polygons & clip )
{ return _clipper_pl_open ( ClipperLib : : ctIntersection , ClipperUtils : : PolylinesProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) ) ; }
Slic3r : : Polylines intersection_pl ( const Slic3r : : Polylines & subject , const Slic3r : : ExPolygons & clip )
{ return _clipper_pl_open ( ClipperLib : : ctIntersection , ClipperUtils : : PolylinesProvider ( subject ) , ClipperUtils : : ExPolygonsProvider ( clip ) ) ; }
Slic3r : : Polylines intersection_pl ( const Slic3r : : Polygons & subject , const Slic3r : : Polygons & clip )
{ return _clipper_pl_closed ( ClipperLib : : ctIntersection , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : PolygonsProvider ( clip ) ) ; }
Lines _clipper_ln ( ClipperLib : : ClipType clipType , const Lines & subject , const Polygons & clip )
2013-07-16 22:29:09 +00:00
{
2016-12-13 18:22:23 +00:00
// convert Lines to Polylines
Polylines polylines ;
polylines . reserve ( subject . size ( ) ) ;
Removed Point::scale(),translate(),coincides_with(),distance_to(),
distance_to_squared(),perp_distance_to(),negative(),vector_to(),
translate(), distance_to() etc,
replaced with the Eigen equivalents.
2018-08-17 12:14:24 +00:00
for ( const Line & line : subject )
polylines . emplace_back ( Polyline ( line . a , line . b ) ) ;
2016-12-13 18:22:23 +00:00
// perform operation
2021-05-05 10:16:40 +00:00
polylines = _clipper_pl_open ( clipType , ClipperUtils : : PolylinesProvider ( polylines ) , ClipperUtils : : PolygonsProvider ( clip ) ) ;
2016-12-13 18:22:23 +00:00
// convert Polylines to Lines
Lines retval ;
for ( Polylines : : const_iterator polyline = polylines . begin ( ) ; polyline ! = polylines . end ( ) ; + + polyline )
2019-04-03 09:17:15 +00:00
retval . emplace_back ( polyline - > operator Line ( ) ) ;
2016-12-13 18:22:23 +00:00
return retval ;
2015-12-02 18:32:57 +00:00
}
2021-04-30 09:49:57 +00:00
ClipperLib : : PolyTree union_pt ( const Polygons & subject )
2014-11-09 14:27:34 +00:00
{
2021-04-30 09:49:57 +00:00
return _clipper_do < ClipperLib : : PolyTree > ( ClipperLib : : ctUnion , ClipperUtils : : PolygonsProvider ( subject ) , ClipperUtils : : EmptyPathsProvider ( ) , ClipperLib : : pftEvenOdd ) ;
2014-11-09 14:27:34 +00:00
}
2021-04-30 09:49:57 +00:00
ClipperLib : : PolyTree union_pt ( const ExPolygons & subject )
2019-09-24 12:54:33 +00:00
{
2021-04-30 09:49:57 +00:00
return _clipper_do < ClipperLib : : PolyTree > ( ClipperLib : : ctUnion , ClipperUtils : : ExPolygonsProvider ( subject ) , ClipperUtils : : EmptyPathsProvider ( ) , ClipperLib : : pftEvenOdd ) ;
2019-09-24 12:54:33 +00:00
}
2019-12-19 10:27:01 +00:00
// Simple spatial ordering of Polynodes
ClipperLib : : PolyNodes order_nodes ( const ClipperLib : : PolyNodes & nodes )
2013-11-23 22:21:59 +00:00
{
// collect ordering points
Points ordering_points ;
ordering_points . reserve ( nodes . size ( ) ) ;
2019-09-24 12:54:33 +00:00
2019-12-19 10:27:01 +00:00
for ( const ClipperLib : : PolyNode * node : nodes )
ordering_points . emplace_back (
2021-04-14 07:22:51 +00:00
Point ( node - > Contour . front ( ) . x ( ) , node - > Contour . front ( ) . y ( ) ) ) ;
2019-09-26 15:30:03 +00:00
2019-12-19 10:27:01 +00:00
// perform the ordering
ClipperLib : : PolyNodes ordered_nodes =
chain_clipper_polynodes ( ordering_points , nodes ) ;
2019-09-24 12:54:33 +00:00
2019-12-19 10:27:01 +00:00
return ordered_nodes ;
2019-09-24 12:54:33 +00:00
}
2019-12-19 10:27:01 +00:00
static void traverse_pt_noholes ( const ClipperLib : : PolyNodes & nodes , Polygons * out )
2019-09-24 12:54:33 +00:00
{
2019-12-19 10:27:01 +00:00
foreach_node < e_ordering : : ON > ( nodes , [ & out ] ( const ClipperLib : : PolyNode * node )
{
traverse_pt_noholes ( node - > Childs , out ) ;
2021-04-30 09:49:57 +00:00
out - > emplace_back ( node - > Contour ) ;
2019-12-19 10:27:01 +00:00
if ( node - > IsHole ( ) ) out - > back ( ) . reverse ( ) ; // ccw
} ) ;
2019-09-24 12:54:33 +00:00
}
2021-01-06 10:05:22 +00:00
static void traverse_pt_outside_in ( const ClipperLib : : PolyNodes & nodes , Polygons * retval )
2019-09-24 12:54:33 +00:00
{
2019-12-19 10:27:01 +00:00
// collect ordering points
Points ordering_points ;
ordering_points . reserve ( nodes . size ( ) ) ;
2021-01-06 10:05:22 +00:00
for ( const ClipperLib : : PolyNode * node : nodes )
2021-04-14 07:22:51 +00:00
ordering_points . emplace_back ( node - > Contour . front ( ) . x ( ) , node - > Contour . front ( ) . y ( ) ) ;
2021-01-06 10:05:22 +00:00
// Perform the ordering, push results recursively.
//FIXME pass the last point to chain_clipper_polynodes?
for ( const ClipperLib : : PolyNode * node : chain_clipper_polynodes ( ordering_points , nodes ) ) {
2021-04-30 09:49:57 +00:00
retval - > emplace_back ( node - > Contour ) ;
2021-01-06 10:05:22 +00:00
if ( node - > IsHole ( ) )
// Orient a hole, which is clockwise oriented, to CCW.
retval - > back ( ) . reverse ( ) ;
2013-11-23 22:21:59 +00:00
// traverse the next depth
2021-01-06 10:05:22 +00:00
traverse_pt_outside_in ( node - > Childs , retval ) ;
2019-12-19 10:27:01 +00:00
}
2019-09-24 12:54:33 +00:00
}
2021-04-30 09:49:57 +00:00
Polygons union_pt_chained_outside_in ( const Polygons & subject )
2019-09-24 12:54:33 +00:00
{
2021-04-30 09:49:57 +00:00
ClipperLib : : PolyTree polytree = union_pt ( subject ) ;
2019-09-24 12:54:33 +00:00
2019-12-19 10:27:01 +00:00
Polygons retval ;
2021-01-06 10:05:22 +00:00
traverse_pt_outside_in ( polytree . Childs , & retval ) ;
2019-12-19 10:27:01 +00:00
return retval ;
2013-11-23 22:21:59 +00:00
}
2018-05-18 07:52:09 +00:00
Polygons simplify_polygons ( const Polygons & subject , bool preserve_collinear )
2013-08-08 00:10:34 +00:00
{
2016-12-13 18:22:23 +00:00
ClipperLib : : Paths output ;
2014-05-01 08:37:38 +00:00
if ( preserve_collinear ) {
ClipperLib : : Clipper c ;
c . PreserveCollinear ( true ) ;
c . StrictlySimple ( true ) ;
2021-04-30 09:49:57 +00:00
c . AddPaths ( ClipperUtils : : PolygonsProvider ( subject ) , ClipperLib : : ptSubject , true ) ;
2014-05-08 13:00:49 +00:00
c . Execute ( ClipperLib : : ctUnion , output , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
2014-05-01 08:37:38 +00:00
} else {
2021-04-30 09:49:57 +00:00
output = ClipperLib : : SimplifyPolygons ( ClipperUtils : : PolygonsProvider ( subject ) , ClipperLib : : pftNonZero ) ;
2014-05-01 08:37:38 +00:00
}
2013-08-08 00:10:34 +00:00
// convert into Slic3r polygons
2021-04-30 09:49:57 +00:00
return to_polygons ( std : : move ( output ) ) ;
2013-08-08 00:10:34 +00:00
}
2018-05-18 07:52:09 +00:00
ExPolygons simplify_polygons_ex ( const Polygons & subject , bool preserve_collinear )
2014-05-01 10:07:11 +00:00
{
2018-05-18 07:52:09 +00:00
if ( ! preserve_collinear )
return union_ex ( simplify_polygons ( subject , false ) ) ;
2021-04-30 09:49:57 +00:00
ClipperLib : : PolyTree polytree ;
2014-05-01 10:07:11 +00:00
ClipperLib : : Clipper c ;
c . PreserveCollinear ( true ) ;
c . StrictlySimple ( true ) ;
2021-04-30 09:49:57 +00:00
c . AddPaths ( ClipperUtils : : PolygonsProvider ( subject ) , ClipperLib : : ptSubject , true ) ;
2014-05-01 10:07:11 +00:00
c . Execute ( ClipperLib : : ctUnion , polytree , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
// convert into ExPolygons
2021-04-30 09:49:57 +00:00
return PolyTreeToExPolygons ( std : : move ( polytree ) ) ;
2013-08-26 15:58:37 +00:00
}
2016-11-23 14:51:47 +00:00
Polygons top_level_islands ( const Slic3r : : Polygons & polygons )
{
// init Clipper
ClipperLib : : Clipper clipper ;
clipper . Clear ( ) ;
// perform union
2021-04-30 09:49:57 +00:00
clipper . AddPaths ( ClipperUtils : : PolygonsProvider ( polygons ) , ClipperLib : : ptSubject , true ) ;
2016-11-23 14:51:47 +00:00
ClipperLib : : PolyTree polytree ;
clipper . Execute ( ClipperLib : : ctUnion , polytree , ClipperLib : : pftEvenOdd , ClipperLib : : pftEvenOdd ) ;
// Convert only the top level islands to the output.
Polygons out ;
out . reserve ( polytree . ChildCount ( ) ) ;
2016-12-13 18:22:23 +00:00
for ( int i = 0 ; i < polytree . ChildCount ( ) ; + + i )
2021-04-30 09:49:57 +00:00
out . emplace_back ( std : : move ( polytree . Childs [ i ] - > Contour ) ) ;
2016-11-23 14:51:47 +00:00
return out ;
2013-07-16 19:04:14 +00:00
}
2016-11-23 14:51:47 +00:00
2019-10-25 11:34:37 +00:00
// Outer offset shall not split the input contour into multiples. It is expected, that the solution will be non empty and it will contain just a single polygon.
2020-02-19 11:57:54 +00:00
ClipperLib : : Paths fix_after_outer_offset (
const ClipperLib : : Path & input ,
// combination of default prameters to correspond to void ClipperOffset::Execute(Paths& solution, double delta)
// to produce a CCW output contour from CCW input contour for a positive offset.
ClipperLib : : PolyFillType filltype , // = ClipperLib::pftPositive
bool reverse_result ) // = false
2019-10-25 11:34:37 +00:00
{
ClipperLib : : Paths solution ;
if ( ! input . empty ( ) ) {
ClipperLib : : Clipper clipper ;
clipper . AddPath ( input , ClipperLib : : ptSubject , true ) ;
clipper . ReverseSolution ( reverse_result ) ;
clipper . Execute ( ClipperLib : : ctUnion , solution , filltype , filltype ) ;
}
return solution ;
}
2020-02-19 11:57:54 +00:00
// Inner offset may split the source contour into multiple contours, but one resulting contour shall not lie inside the other.
ClipperLib : : Paths fix_after_inner_offset (
const ClipperLib : : Path & input ,
// combination of default prameters to correspond to void ClipperOffset::Execute(Paths& solution, double delta)
// to produce a CCW output contour from CCW input contour for a negative offset.
ClipperLib : : PolyFillType filltype , // = ClipperLib::pftNegative
bool reverse_result ) // = true
2019-10-25 11:34:37 +00:00
{
ClipperLib : : Paths solution ;
if ( ! input . empty ( ) ) {
ClipperLib : : Clipper clipper ;
clipper . AddPath ( input , ClipperLib : : ptSubject , true ) ;
ClipperLib : : IntRect r = clipper . GetBounds ( ) ;
r . left - = 10 ; r . top - = 10 ; r . right + = 10 ; r . bottom + = 10 ;
if ( filltype = = ClipperLib : : pftPositive )
clipper . AddPath ( { ClipperLib : : IntPoint ( r . left , r . bottom ) , ClipperLib : : IntPoint ( r . left , r . top ) , ClipperLib : : IntPoint ( r . right , r . top ) , ClipperLib : : IntPoint ( r . right , r . bottom ) } , ClipperLib : : ptSubject , true ) ;
else
clipper . AddPath ( { ClipperLib : : IntPoint ( r . left , r . bottom ) , ClipperLib : : IntPoint ( r . right , r . bottom ) , ClipperLib : : IntPoint ( r . right , r . top ) , ClipperLib : : IntPoint ( r . left , r . top ) } , ClipperLib : : ptSubject , true ) ;
clipper . ReverseSolution ( reverse_result ) ;
clipper . Execute ( ClipperLib : : ctUnion , solution , filltype , filltype ) ;
if ( ! solution . empty ( ) )
solution . erase ( solution . begin ( ) ) ;
}
return solution ;
}
ClipperLib : : Path mittered_offset_path_scaled ( const Points & contour , const std : : vector < float > & deltas , double miter_limit )
{
assert ( contour . size ( ) = = deltas . size ( ) ) ;
2019-11-04 10:26:36 +00:00
2019-10-25 11:34:37 +00:00
# ifndef NDEBUG
// Verify that the deltas are either all positive, or all negative.
bool positive = false ;
bool negative = false ;
for ( float delta : deltas )
if ( delta < 0.f )
negative = true ;
else if ( delta > 0.f )
positive = true ;
assert ( ! ( negative & & positive ) ) ;
# endif /* NDEBUG */
ClipperLib : : Path out ;
if ( deltas . size ( ) > 2 )
{
out . reserve ( contour . size ( ) * 2 ) ;
// Clamp miter limit to 2.
miter_limit = ( miter_limit > 2. ) ? 2. / ( miter_limit * miter_limit ) : 0.5 ;
// perpenduclar vector
auto perp = [ ] ( const Vec2d & v ) - > Vec2d { return Vec2d ( v . y ( ) , - v . x ( ) ) ; } ;
// Add a new point to the output, scale by CLIPPER_OFFSET_SCALE and round to ClipperLib::cInt.
auto add_offset_point = [ & out ] ( Vec2d pt ) {
2021-04-13 09:31:54 +00:00
pt + = Vec2d ( 0.5 - ( pt . x ( ) < 0 ) , 0.5 - ( pt . y ( ) < 0 ) ) ;
2019-10-25 11:34:37 +00:00
out . emplace_back ( ClipperLib : : cInt ( pt . x ( ) ) , ClipperLib : : cInt ( pt . y ( ) ) ) ;
} ;
// Minimum edge length, squared.
double lmin = * std : : max_element ( deltas . begin ( ) , deltas . end ( ) ) * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR ;
double l2min = lmin * lmin ;
// Minimum angle to consider two edges to be parallel.
2019-11-04 10:26:36 +00:00
// Vojtech's estimate.
// const double sin_min_parallel = EPSILON + 1. / double(CLIPPER_OFFSET_SCALE);
// Implementation equal to Clipper.
const double sin_min_parallel = 1. ;
2019-10-25 11:34:37 +00:00
// Find the last point further from pt by l2min.
Vec2d pt = contour . front ( ) . cast < double > ( ) ;
size_t iprev = contour . size ( ) - 1 ;
Vec2d ptprev ;
for ( ; iprev > 0 ; - - iprev ) {
ptprev = contour [ iprev ] . cast < double > ( ) ;
if ( ( ptprev - pt ) . squaredNorm ( ) > l2min )
break ;
}
if ( iprev ! = 0 ) {
size_t ilast = iprev ;
// Normal to the (pt - ptprev) segment.
Vec2d nprev = perp ( pt - ptprev ) . normalized ( ) ;
for ( size_t i = 0 ; ; ) {
// Find the next point further from pt by l2min.
size_t j = i + 1 ;
Vec2d ptnext ;
for ( ; j < = ilast ; + + j ) {
ptnext = contour [ j ] . cast < double > ( ) ;
double l2 = ( ptnext - pt ) . squaredNorm ( ) ;
if ( l2 > l2min )
break ;
}
2019-11-04 10:26:36 +00:00
if ( j > ilast ) {
assert ( i < = ilast ) ;
// If the last edge is too short, merge it with the previous edge.
i = ilast ;
2019-10-25 11:34:37 +00:00
ptnext = contour . front ( ) . cast < double > ( ) ;
2019-11-04 10:26:36 +00:00
}
2019-10-25 11:34:37 +00:00
// Normal to the (ptnext - pt) segment.
Vec2d nnext = perp ( ptnext - pt ) . normalized ( ) ;
double delta = deltas [ i ] ;
2021-04-22 09:41:26 +00:00
double sin_a = std : : clamp ( cross2 ( nprev , nnext ) , - 1. , 1. ) ;
2019-10-25 11:34:37 +00:00
double convex = sin_a * delta ;
if ( convex < = - sin_min_parallel ) {
// Concave corner.
add_offset_point ( pt + nprev * delta ) ;
add_offset_point ( pt ) ;
add_offset_point ( pt + nnext * delta ) ;
} else {
double dot = nprev . dot ( nnext ) ;
2019-11-04 10:26:36 +00:00
if ( convex < sin_min_parallel & & dot > 0. ) {
// Nearly parallel.
add_offset_point ( ( nprev . dot ( nnext ) > 0. ) ? ( pt + nprev * delta ) : pt ) ;
} else {
// Convex corner, possibly extremely sharp if convex < sin_min_parallel.
double r = 1. + dot ;
if ( r > = miter_limit )
add_offset_point ( pt + ( nprev + nnext ) * ( delta / r ) ) ;
else {
double dx = std : : tan ( std : : atan2 ( sin_a , dot ) / 4. ) ;
Vec2d newpt1 = pt + ( nprev - perp ( nprev ) * dx ) * delta ;
Vec2d newpt2 = pt + ( nnext + perp ( nnext ) * dx ) * delta ;
2019-10-25 11:34:37 +00:00
# ifndef NDEBUG
2019-11-04 10:26:36 +00:00
Vec2d vedge = 0.5 * ( newpt1 + newpt2 ) - pt ;
double dist_norm = vedge . norm ( ) ;
assert ( std : : abs ( dist_norm - std : : abs ( delta ) ) < SCALED_EPSILON ) ;
2019-10-25 11:34:37 +00:00
# endif /* NDEBUG */
2019-11-04 10:26:36 +00:00
add_offset_point ( newpt1 ) ;
add_offset_point ( newpt2 ) ;
}
}
2019-10-25 11:34:37 +00:00
}
if ( i = = ilast )
break ;
ptprev = pt ;
nprev = nnext ;
pt = ptnext ;
i = j ;
}
}
}
2019-11-01 18:59:09 +00:00
#if 0
{
ClipperLib : : Path polytmp ( out ) ;
unscaleClipperPolygon ( polytmp ) ;
2021-04-30 09:49:57 +00:00
Slic3r : : Polygon offsetted ( std : : move ( polytmp ) ) ;
2019-11-01 18:59:09 +00:00
BoundingBox bbox = get_extents ( contour ) ;
bbox . merge ( get_extents ( offsetted ) ) ;
static int iRun = 0 ;
SVG svg ( debug_out_path ( " mittered_offset_path_scaled-%d.svg " , iRun + + ) . c_str ( ) , bbox ) ;
svg . draw_outline ( Polygon ( contour ) , " blue " , scale_ ( 0.01 ) ) ;
svg . draw_outline ( offsetted , " red " , scale_ ( 0.01 ) ) ;
svg . draw ( contour , " blue " , scale_ ( 0.03 ) ) ;
svg . draw ( ( Points ) offsetted , " blue " , scale_ ( 0.03 ) ) ;
}
# endif
2019-10-25 11:34:37 +00:00
return out ;
}
Polygons variable_offset_inner ( const ExPolygon & expoly , const std : : vector < std : : vector < float > > & deltas , double miter_limit )
{
# ifndef NDEBUG
// Verify that the deltas are all non positive.
for ( const std : : vector < float > & ds : deltas )
for ( float delta : ds )
assert ( delta < = 0. ) ;
assert ( expoly . holes . size ( ) + 1 = = deltas . size ( ) ) ;
# endif /* NDEBUG */
// 1) Offset the outer contour.
ClipperLib : : Paths contours = fix_after_inner_offset ( mittered_offset_path_scaled ( expoly . contour . points , deltas . front ( ) , miter_limit ) , ClipperLib : : pftNegative , true ) ;
2020-02-19 11:57:54 +00:00
# ifndef NDEBUG
for ( auto & c : contours )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 2) Offset the holes one by one, collect the results.
ClipperLib : : Paths holes ;
holes . reserve ( expoly . holes . size ( ) ) ;
for ( const Polygon & hole : expoly . holes )
2020-11-24 15:00:46 +00:00
append ( holes , fix_after_outer_offset ( mittered_offset_path_scaled ( hole . points , deltas [ 1 + & hole - expoly . holes . data ( ) ] , miter_limit ) , ClipperLib : : pftNegative , false ) ) ;
2020-02-19 11:57:54 +00:00
# ifndef NDEBUG
for ( auto & c : holes )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 3) Subtract holes from the contours.
ClipperLib : : Paths output ;
if ( holes . empty ( ) )
output = std : : move ( contours ) ;
else {
ClipperLib : : Clipper clipper ;
clipper . Clear ( ) ;
clipper . AddPaths ( contours , ClipperLib : : ptSubject , true ) ;
clipper . AddPaths ( holes , ClipperLib : : ptClip , true ) ;
clipper . Execute ( ClipperLib : : ctDifference , output , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
}
2021-04-30 09:49:57 +00:00
return to_polygons ( std : : move ( output ) ) ;
2019-10-25 11:34:37 +00:00
}
Polygons variable_offset_outer ( const ExPolygon & expoly , const std : : vector < std : : vector < float > > & deltas , double miter_limit )
{
# ifndef NDEBUG
// Verify that the deltas are all non positive.
for ( const std : : vector < float > & ds : deltas )
for ( float delta : ds )
assert ( delta > = 0. ) ;
assert ( expoly . holes . size ( ) + 1 = = deltas . size ( ) ) ;
# endif /* NDEBUG */
// 1) Offset the outer contour.
ClipperLib : : Paths contours = fix_after_outer_offset ( mittered_offset_path_scaled ( expoly . contour . points , deltas . front ( ) , miter_limit ) , ClipperLib : : pftPositive , false ) ;
2020-02-19 11:57:54 +00:00
# ifndef NDEBUG
for ( auto & c : contours )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 2) Offset the holes one by one, collect the results.
ClipperLib : : Paths holes ;
holes . reserve ( expoly . holes . size ( ) ) ;
for ( const Polygon & hole : expoly . holes )
2020-11-24 15:00:46 +00:00
append ( holes , fix_after_inner_offset ( mittered_offset_path_scaled ( hole . points , deltas [ 1 + & hole - expoly . holes . data ( ) ] , miter_limit ) , ClipperLib : : pftPositive , true ) ) ;
2020-02-19 11:57:54 +00:00
# ifndef NDEBUG
for ( auto & c : holes )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 3) Subtract holes from the contours.
ClipperLib : : Paths output ;
if ( holes . empty ( ) )
output = std : : move ( contours ) ;
else {
ClipperLib : : Clipper clipper ;
clipper . Clear ( ) ;
clipper . AddPaths ( contours , ClipperLib : : ptSubject , true ) ;
clipper . AddPaths ( holes , ClipperLib : : ptClip , true ) ;
clipper . Execute ( ClipperLib : : ctDifference , output , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
}
2021-04-30 09:49:57 +00:00
return to_polygons ( std : : move ( output ) ) ;
2019-10-25 11:34:37 +00:00
}
ExPolygons variable_offset_outer_ex ( const ExPolygon & expoly , const std : : vector < std : : vector < float > > & deltas , double miter_limit )
{
# ifndef NDEBUG
// Verify that the deltas are all non positive.
for ( const std : : vector < float > & ds : deltas )
for ( float delta : ds )
assert ( delta > = 0. ) ;
assert ( expoly . holes . size ( ) + 1 = = deltas . size ( ) ) ;
# endif /* NDEBUG */
// 1) Offset the outer contour.
ClipperLib : : Paths contours = fix_after_outer_offset ( mittered_offset_path_scaled ( expoly . contour . points , deltas . front ( ) , miter_limit ) , ClipperLib : : pftPositive , false ) ;
2020-02-19 11:57:54 +00:00
# ifndef NDEBUG
for ( auto & c : contours )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 2) Offset the holes one by one, collect the results.
ClipperLib : : Paths holes ;
holes . reserve ( expoly . holes . size ( ) ) ;
for ( const Polygon & hole : expoly . holes )
2020-11-24 15:00:46 +00:00
append ( holes , fix_after_inner_offset ( mittered_offset_path_scaled ( hole . points , deltas [ 1 + & hole - expoly . holes . data ( ) ] , miter_limit ) , ClipperLib : : pftPositive , true ) ) ;
2020-02-19 11:57:54 +00:00
# ifndef NDEBUG
for ( auto & c : holes )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 3) Subtract holes from the contours.
ExPolygons output ;
if ( holes . empty ( ) ) {
output . reserve ( contours . size ( ) ) ;
for ( ClipperLib : : Path & path : contours )
2021-04-30 09:49:57 +00:00
output . emplace_back ( std : : move ( path ) ) ;
2019-10-25 11:34:37 +00:00
} else {
ClipperLib : : Clipper clipper ;
clipper . AddPaths ( contours , ClipperLib : : ptSubject , true ) ;
clipper . AddPaths ( holes , ClipperLib : : ptClip , true ) ;
ClipperLib : : PolyTree polytree ;
clipper . Execute ( ClipperLib : : ctDifference , polytree , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
2021-04-30 09:49:57 +00:00
output = PolyTreeToExPolygons ( std : : move ( polytree ) ) ;
2019-10-25 11:34:37 +00:00
}
return output ;
}
ExPolygons variable_offset_inner_ex ( const ExPolygon & expoly , const std : : vector < std : : vector < float > > & deltas , double miter_limit )
{
# ifndef NDEBUG
// Verify that the deltas are all non positive.
2019-11-14 16:02:32 +00:00
for ( const std : : vector < float > & ds : deltas )
2019-10-25 11:34:37 +00:00
for ( float delta : ds )
assert ( delta < = 0. ) ;
assert ( expoly . holes . size ( ) + 1 = = deltas . size ( ) ) ;
# endif /* NDEBUG */
// 1) Offset the outer contour.
2020-02-19 11:57:54 +00:00
ClipperLib : : Paths contours = fix_after_inner_offset ( mittered_offset_path_scaled ( expoly . contour . points , deltas . front ( ) , miter_limit ) , ClipperLib : : pftNegative , true ) ;
# ifndef NDEBUG
for ( auto & c : contours )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 2) Offset the holes one by one, collect the results.
ClipperLib : : Paths holes ;
holes . reserve ( expoly . holes . size ( ) ) ;
for ( const Polygon & hole : expoly . holes )
2020-11-24 15:00:46 +00:00
append ( holes , fix_after_outer_offset ( mittered_offset_path_scaled ( hole . points , deltas [ 1 + & hole - expoly . holes . data ( ) ] , miter_limit ) , ClipperLib : : pftNegative , false ) ) ;
2020-02-19 11:57:54 +00:00
# ifndef NDEBUG
for ( auto & c : holes )
assert ( ClipperLib : : Area ( c ) > 0. ) ;
# endif /* NDEBUG */
2019-10-25 11:34:37 +00:00
// 3) Subtract holes from the contours.
ExPolygons output ;
if ( holes . empty ( ) ) {
output . reserve ( contours . size ( ) ) ;
for ( ClipperLib : : Path & path : contours )
2021-04-30 09:49:57 +00:00
output . emplace_back ( std : : move ( path ) ) ;
2019-10-25 11:34:37 +00:00
} else {
ClipperLib : : Clipper clipper ;
clipper . AddPaths ( contours , ClipperLib : : ptSubject , true ) ;
clipper . AddPaths ( holes , ClipperLib : : ptClip , true ) ;
ClipperLib : : PolyTree polytree ;
clipper . Execute ( ClipperLib : : ctDifference , polytree , ClipperLib : : pftNonZero , ClipperLib : : pftNonZero ) ;
2021-04-30 09:49:57 +00:00
output = PolyTreeToExPolygons ( std : : move ( polytree ) ) ;
2019-10-25 11:34:37 +00:00
}
return output ;
}
2019-04-03 08:17:57 +00:00
}