diff --git a/3d-viewer/3d_canvas/board_adapter.cpp b/3d-viewer/3d_canvas/board_adapter.cpp index 4e0c5ddffa..77b19e85fe 100644 --- a/3d-viewer/3d_canvas/board_adapter.cpp +++ b/3d-viewer/3d_canvas/board_adapter.cpp @@ -947,9 +947,7 @@ bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg ) success = BuildFootprintPolygonOutlines( m_board, m_board_poly, m_board->GetDesignSettings().m_MaxError, chainingEpsilon ); - - // Make polygon strictly simple to avoid issues (especially in 3D viewer) - m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + m_board_poly.Simplify(); if( !success && aErrorMsg ) { diff --git a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp index ab11cccadc..08e2e19952 100644 --- a/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp +++ b/3d-viewer/3d_canvas/create_3Dgraphic_brd_items.cpp @@ -650,7 +650,7 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta aShape->TransformShapeToPolygon( polyList, UNDEFINED_LAYER, 0, ARC_HIGH_DEF, ERROR_INSIDE ); - polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyList.Simplify(); if( margin != 0 ) { @@ -700,7 +700,7 @@ void BOARD_ADAPTER::addShape( const PCB_SHAPE* aShape, CONTAINER_2D_BASE* aConta // Some polygons can be a bit complex (especially when coming from a // picture ot a text converted to a polygon // So call Simplify before calling ConvertPolygonToTriangles, just in case. - polyList.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyList.Simplify(); if( polyList.IsEmpty() ) // Just for caution break; diff --git a/3d-viewer/3d_canvas/create_layer_items.cpp b/3d-viewer/3d_canvas/create_layer_items.cpp index 7345bb5f11..5918a2b047 100644 --- a/3d-viewer/3d_canvas/create_layer_items.cpp +++ b/3d-viewer/3d_canvas/create_layer_items.cpp @@ -780,10 +780,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) // End Build Copper layers // This will make a union of all added contours - m_TH_ODPolys.Simplify( SHAPE_POLY_SET::PM_FAST ); - m_NPTH_ODPolys.Simplify( SHAPE_POLY_SET::PM_FAST ); - m_viaTH_ODPolys.Simplify( SHAPE_POLY_SET::PM_FAST ); - m_viaAnnuliPolys.Simplify( SHAPE_POLY_SET::PM_FAST ); + m_TH_ODPolys.Simplify(); + m_NPTH_ODPolys.Simplify(); + m_viaTH_ODPolys.Simplify(); + m_viaAnnuliPolys.Simplify(); // Build Tech layers // Based on: @@ -1022,7 +1022,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) } // This will make a union of all added contours - layerPoly->Simplify( SHAPE_POLY_SET::PM_FAST ); + layerPoly->Simplify(); } } // End Build Tech layers @@ -1061,14 +1061,12 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) // TRIM PLATED COPPER TO SOLDERMASK if( m_layers_poly.find( F_Mask ) != m_layers_poly.end() ) { - m_frontPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( F_Mask ), - SHAPE_POLY_SET::PM_FAST ); + m_frontPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( F_Mask ) ); } if( m_layers_poly.find( B_Mask ) != m_layers_poly.end() ) { - m_backPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( B_Mask ), - SHAPE_POLY_SET::PM_FAST ); + m_backPlatedCopperPolys->BooleanIntersection( *m_layers_poly.at( B_Mask ) ); } // Subtract plated copper from unplated copper @@ -1077,15 +1075,15 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) if( m_layers_poly.find( F_Cu ) != m_layers_poly.end() ) { - m_layers_poly[F_Cu]->BooleanSubtract( *m_frontPlatedPadAndGraphicPolys, SHAPE_POLY_SET::PM_FAST ); - m_layers_poly[F_Cu]->BooleanSubtract( *m_frontPlatedCopperPolys, SHAPE_POLY_SET::PM_FAST ); + m_layers_poly[F_Cu]->BooleanSubtract( *m_frontPlatedPadAndGraphicPolys ); + m_layers_poly[F_Cu]->BooleanSubtract( *m_frontPlatedCopperPolys ); hasF_Cu = true; } if( m_layers_poly.find( B_Cu ) != m_layers_poly.end() ) { - m_layers_poly[B_Cu]->BooleanSubtract( *m_backPlatedPadAndGraphicPolys, SHAPE_POLY_SET::PM_FAST ); - m_layers_poly[B_Cu]->BooleanSubtract( *m_backPlatedCopperPolys, SHAPE_POLY_SET::PM_FAST ); + m_layers_poly[B_Cu]->BooleanSubtract( *m_backPlatedPadAndGraphicPolys ); + m_layers_poly[B_Cu]->BooleanSubtract( *m_backPlatedCopperPolys ); hasB_Cu = true; } @@ -1096,10 +1094,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) if( hasB_Cu && m_backPlatedCopperPolys->OutlineCount() ) m_backPlatedPadAndGraphicPolys->Append( *m_backPlatedCopperPolys ); - m_frontPlatedPadAndGraphicPolys->Simplify( SHAPE_POLY_SET::PM_FAST ); - m_backPlatedPadAndGraphicPolys->Simplify( SHAPE_POLY_SET::PM_FAST ); - m_frontPlatedCopperPolys->Simplify( SHAPE_POLY_SET::PM_FAST ); - m_backPlatedCopperPolys->Simplify( SHAPE_POLY_SET::PM_FAST ); + m_frontPlatedPadAndGraphicPolys->Simplify(); + m_backPlatedPadAndGraphicPolys->Simplify(); + m_frontPlatedCopperPolys->Simplify(); + m_backPlatedCopperPolys->Simplify(); // ADD PLATED PADS for( FOOTPRINT* footprint : m_board->Footprints() ) @@ -1168,7 +1166,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) { // This will make a union of all added contours layerPoly->second->ClearArcs(); - layerPoly->second->Simplify( SHAPE_POLY_SET::PM_FAST ); + layerPoly->second->Simplify(); } } @@ -1193,12 +1191,12 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter ) { // found SHAPE_POLY_SET *polyLayer = m_layerHoleOdPolys[layer]; - polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST ); + polyLayer->Simplify(); wxASSERT( m_layerHoleIdPolys.find( layer ) != m_layerHoleIdPolys.end() ); polyLayer = m_layerHoleIdPolys[layer]; - polyLayer->Simplify( SHAPE_POLY_SET::PM_FAST ); + polyLayer->Simplify(); } } diff --git a/3d-viewer/3d_rendering/opengl/create_scene.cpp b/3d-viewer/3d_rendering/opengl/create_scene.cpp index ad8dcbe14f..55162b7222 100644 --- a/3d-viewer/3d_rendering/opengl/create_scene.cpp +++ b/3d-viewer/3d_rendering/opengl/create_scene.cpp @@ -476,13 +476,12 @@ void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningRepo m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, INT_MAX/2 ) ); m_antiBoardPolys.Outline( 0 ).SetClosed( true ); - m_antiBoardPolys.BooleanSubtract( m_boardAdapter.GetBoardPoly(), - SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + m_antiBoardPolys.BooleanSubtract( m_boardAdapter.GetBoardPoly() ); m_antiBoard = createBoard( m_antiBoardPolys ); SHAPE_POLY_SET board_poly_with_holes = m_boardAdapter.GetBoardPoly().CloneDropTriangulation(); - board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetTH_ODPolys(), SHAPE_POLY_SET::PM_FAST ); - board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys(), SHAPE_POLY_SET::PM_FAST ); + board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() ); + board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() ); m_boardWithHoles = createBoard( board_poly_with_holes ); @@ -495,8 +494,7 @@ void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningRepo SHAPE_POLY_SET outerPolyTHT = m_boardAdapter.GetTH_ODPolys().CloneDropTriangulation(); - outerPolyTHT.BooleanIntersection( m_boardAdapter.GetBoardPoly(), - SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + outerPolyTHT.BooleanIntersection( m_boardAdapter.GetBoardPoly() ); m_outerThroughHoles = generateHoles( m_boardAdapter.GetTH_ODs().GetList(), outerPolyTHT, 1.0f, 0.0f, false, &m_boardAdapter.GetTH_IDs() ); @@ -575,29 +573,24 @@ void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningRepo if( LSET::PhysicalLayersMask().test( layer ) ) { - polyListSubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly(), - SHAPE_POLY_SET::PM_FAST ); + polyListSubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly() ); } if( layer != B_Mask && layer != F_Mask ) { - polyListSubtracted.BooleanSubtract( m_boardAdapter.GetTH_ODPolys(), - SHAPE_POLY_SET::PM_FAST ); - polyListSubtracted.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys(), - SHAPE_POLY_SET::PM_FAST ); + polyListSubtracted.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() ); + polyListSubtracted.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() ); } if( m_boardAdapter.m_Cfg->m_Render.subtract_mask_from_silk ) { if( layer == B_SilkS && map_poly.find( B_Mask ) != map_poly.end() ) { - polyListSubtracted.BooleanSubtract( *map_poly.at( B_Mask ), - SHAPE_POLY_SET::PM_FAST ); + polyListSubtracted.BooleanSubtract( *map_poly.at( B_Mask ) ); } else if( layer == F_SilkS && map_poly.find( F_Mask ) != map_poly.end() ) { - polyListSubtracted.BooleanSubtract( *map_poly.at( F_Mask ), - SHAPE_POLY_SET::PM_FAST ); + polyListSubtracted.BooleanSubtract( *map_poly.at( F_Mask ) ); } } @@ -620,9 +613,9 @@ void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningRepo if( frontPlatedPadAndGraphicPolys ) { SHAPE_POLY_SET poly = frontPlatedPadAndGraphicPolys->CloneDropTriangulation(); - poly.BooleanIntersection( m_boardAdapter.GetBoardPoly(), SHAPE_POLY_SET::PM_FAST ); - poly.BooleanSubtract( m_boardAdapter.GetTH_ODPolys(), SHAPE_POLY_SET::PM_FAST ); - poly.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys(), SHAPE_POLY_SET::PM_FAST ); + poly.BooleanIntersection( m_boardAdapter.GetBoardPoly() ); + poly.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() ); + poly.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() ); m_platedPadsFront = generateLayerList( m_boardAdapter.GetPlatedPadsFront(), &poly, F_Cu ); @@ -634,9 +627,9 @@ void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningRepo if( backPlatedPadAndGraphicPolys ) { SHAPE_POLY_SET poly = backPlatedPadAndGraphicPolys->CloneDropTriangulation(); - poly.BooleanIntersection( m_boardAdapter.GetBoardPoly(), SHAPE_POLY_SET::PM_FAST ); - poly.BooleanSubtract( m_boardAdapter.GetTH_ODPolys(), SHAPE_POLY_SET::PM_FAST ); - poly.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys(), SHAPE_POLY_SET::PM_FAST ); + poly.BooleanIntersection( m_boardAdapter.GetBoardPoly() ); + poly.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() ); + poly.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() ); m_platedPadsBack = generateLayerList( m_boardAdapter.GetPlatedPadsBack(), &poly, B_Cu ); @@ -839,9 +832,9 @@ void RENDER_3D_OPENGL::generateViasAndPads() } // Subtract the holes - tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly, SHAPE_POLY_SET::PM_FAST ); + tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly ); - tht_outer_holes_poly.BooleanSubtract( m_antiBoardPolys, SHAPE_POLY_SET::PM_FAST ); + tht_outer_holes_poly.BooleanSubtract( m_antiBoardPolys ); CONTAINER_2D holesContainer; diff --git a/3d-viewer/3d_rendering/raytracing/create_scene.cpp b/3d-viewer/3d_rendering/raytracing/create_scene.cpp index 43ee1140c8..51e6306f60 100644 --- a/3d-viewer/3d_rendering/raytracing/create_scene.cpp +++ b/3d-viewer/3d_rendering/raytracing/create_scene.cpp @@ -415,8 +415,8 @@ void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarn buildBoardBoundingBoxPoly( m_boardAdapter.GetBoard(), antiboardPoly ); - antiboardPoly.BooleanSubtract( boardPolyCopy, SHAPE_POLY_SET::PM_FAST ); - antiboardPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); + antiboardPoly.BooleanSubtract( boardPolyCopy ); + antiboardPoly.Fracture(); for( int ii = 0; ii < antiboardPoly.OutlineCount(); ii++ ) { @@ -427,7 +427,7 @@ void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarn m_antioutlineBoard2dObjects->BuildBVH(); - boardPolyCopy.Fracture( SHAPE_POLY_SET::PM_FAST ); + boardPolyCopy.Fracture(); for( int ii = 0; ii < outlineCount; ii++ ) { diff --git a/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.cpp b/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.cpp index bcba4dff31..04edd43fe6 100644 --- a/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.cpp +++ b/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.cpp @@ -612,10 +612,9 @@ void ConvertPolygonToBlocks( const SHAPE_POLY_SET& aMainPath, CONTAINER_2D_BASE& subBlockPoly.AddOutline( sb ); - // We need here a strictly simple polygon with outlines and holes + // We need here a simple polygon with outlines and holes SHAPE_POLY_SET solution; - solution.BooleanIntersection( aMainPath, subBlockPoly, - SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + solution.BooleanIntersection( aMainPath, subBlockPoly ); OUTERS_AND_HOLES outersAndHoles; @@ -659,68 +658,3 @@ void ConvertPolygonToBlocks( const SHAPE_POLY_SET& aMainPath, CONTAINER_2D_BASE& topToBottom += topToBottom_inc; } } - - -#ifdef DEBUG -static void polygon_Convert( const ClipperLib::Path& aPath, SEGMENTS& aOutSegment, - float aBiuTo3dUnitsScale ) -{ - aOutSegment.resize( aPath.size() ); - - for( unsigned i = 0; i < aPath.size(); i++ ) - { - aOutSegment[i].m_Start = SFVEC2F( - (float) aPath[i].X * aBiuTo3dUnitsScale, (float) -aPath[i].Y * aBiuTo3dUnitsScale ); - } - - unsigned int i; - unsigned int j = aOutSegment.size() - 1; - - for( i = 0; i < aOutSegment.size(); j = i++ ) - { - // Calculate constants for each segment - aOutSegment[i].m_inv_JY_minus_IY = - 1.0f / ( aOutSegment[j].m_Start.y - aOutSegment[i].m_Start.y ); - aOutSegment[i].m_JX_minus_IX = ( aOutSegment[j].m_Start.x - aOutSegment[i].m_Start.x ); - } -} - - -void Polygon2d_TestModule() -{ - // "This structure contains a sequence of IntPoint vertices defining a single contour" - ClipperLib::Path aPath; - - SEGMENTS aSegments; - - aPath.resize( 4 ); - - aPath[0] = ClipperLib::IntPoint( -2, -2 ); - aPath[1] = ClipperLib::IntPoint( 2, -2 ); - aPath[2] = ClipperLib::IntPoint( 2, 2 ); - aPath[3] = ClipperLib::IntPoint( -2, 2 ); - - // It must be an outer polygon - wxASSERT( ClipperLib::Orientation( aPath ) ); - - polygon_Convert( aPath, aSegments, 1.0f ); - - wxASSERT( aPath.size() == aSegments.size() ); - - wxASSERT( aSegments[0].m_Start == SFVEC2F( -2.0f, 2.0f ) ); - wxASSERT( aSegments[1].m_Start == SFVEC2F( 2.0f, 2.0f ) ); - wxASSERT( aSegments[2].m_Start == SFVEC2F( 2.0f, -2.0f ) ); - wxASSERT( aSegments[3].m_Start == SFVEC2F( -2.0f, -2.0f ) ); - - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 0.0f, 0.0f ) ) ); - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f, -1.9f ) ) ); - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -1.9f, 1.9f ) ) ); - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 1.9f, 1.9f ) ) ); - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 1.9f, -1.9f ) ) ); - - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f, -2.0f ) ) == false ); - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( -2.1f, 2.0f ) ) == false ); - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 2.1f, 2.0f ) ) == false ); - wxASSERT( polygon_IsPointInside( aSegments, SFVEC2F( 2.1f, -2.0f ) ) == false ); -} -#endif diff --git a/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.h b/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.h index 8132dbf991..7582343731 100644 --- a/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.h +++ b/3d-viewer/3d_rendering/raytracing/shapes2D/polygon_2d.h @@ -152,6 +152,5 @@ void ConvertPolygonToBlocks( const SHAPE_POLY_SET& aMainPath, CONTAINER_2D_BASE& float aBiuTo3dUnitsScale, float aDivFactor, const BOARD_ITEM& aBoardItem, int aPolyIndex ); -void Polygon2d_TestModule(); #endif // _CPOLYGON2D_H_ diff --git a/3d-viewer/3d_rendering/raytracing/shapes2D/triangle_2d.h b/3d-viewer/3d_rendering/raytracing/shapes2D/triangle_2d.h index e3b7ffe25f..cb28ff1b58 100644 --- a/3d-viewer/3d_rendering/raytracing/shapes2D/triangle_2d.h +++ b/3d-viewer/3d_rendering/raytracing/shapes2D/triangle_2d.h @@ -33,7 +33,6 @@ #include "../accelerators/container_2d.h" #include <geometry/shape_line_chain.h> #include <geometry/shape_poly_set.h> -#include <clipper.hpp> class TRIANGLE_2D : public OBJECT_2D { diff --git a/bitmap2component/bitmap2component.cpp b/bitmap2component/bitmap2component.cpp index c43645f146..2a63ab8668 100644 --- a/bitmap2component/bitmap2component.cpp +++ b/bitmap2component/bitmap2component.cpp @@ -524,15 +524,15 @@ void BITMAPCONV_INFO::createOutputData( BMP2CMP_MOD_LAYER aModLayer ) // at the end of a group of a positive path and its negative children, fill. if( paths->next == nullptr || paths->next->sign == '+' ) { - polyset_areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - polyset_holes.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - polyset_areas.BooleanSubtract( polyset_holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + polyset_areas.Simplify(); + polyset_holes.Simplify(); + polyset_areas.BooleanSubtract( polyset_holes ); // Ensure there are no self intersecting polygons if( polyset_areas.NormalizeAreaOutlines() ) { // Convert polygon with holes to a unique polygon - polyset_areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + polyset_areas.Fracture(); // Output current resulting polygon(s) for( int ii = 0; ii < polyset_areas.OutlineCount(); ii++ ) diff --git a/common/advanced_config.cpp b/common/advanced_config.cpp index c406889c90..43e98c0aa4 100644 --- a/common/advanced_config.cpp +++ b/common/advanced_config.cpp @@ -98,7 +98,6 @@ static const wxChar AllowManualCanvasScale[] = wxT( "AllowManualCanvasScale" ); static const wxChar UpdateUIEventInterval[] = wxT( "UpdateUIEventInterval" ); static const wxChar V3DRT_BevelHeight_um[] = wxT( "V3DRT_BevelHeight_um" ); static const wxChar V3DRT_BevelExtentFactor[] = wxT( "V3DRT_BevelExtentFactor" ); -static const wxChar UseClipper2[] = wxT( "UseClipper2" ); static const wxChar EnableDesignBlocks[] = wxT( "EnableDesignBlocks" ); static const wxChar EnableGenerators[] = wxT( "EnableGenerators" ); static const wxChar EnableGit[] = wxT( "EnableGit" ); @@ -263,7 +262,6 @@ ADVANCED_CFG::ADVANCED_CFG() m_3DRT_BevelHeight_um = 30; m_3DRT_BevelExtentFactor = 1.0 / 16.0; - m_UseClipper2 = true; m_EnableAPILogging = false; m_Use3DConnexionDriver = true; @@ -464,9 +462,6 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg ) m_3DRT_BevelExtentFactor, 0.0, 100.0, AC_GROUPS::V3D_RayTracing ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::UseClipper2, - &m_UseClipper2, m_UseClipper2 ) ); - configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::Use3DConnexionDriver, &m_Use3DConnexionDriver, m_Use3DConnexionDriver ) ); diff --git a/common/callback_gal.cpp b/common/callback_gal.cpp index 9441d3edf6..542b5f734c 100644 --- a/common/callback_gal.cpp +++ b/common/callback_gal.cpp @@ -71,7 +71,7 @@ void CALLBACK_GAL::DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal KIFONT::OUTLINE_GLYPH glyph = static_cast<const KIFONT::OUTLINE_GLYPH&>( aGlyph ); if( glyph.HasHoles() ) - glyph.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + glyph.Fracture(); for( int ii = 0; ii < glyph.OutlineCount(); ++ii ) m_outlineCallback( glyph.Outline( ii ) ); diff --git a/common/eda_draw_frame.cpp b/common/eda_draw_frame.cpp index 8b672445c5..264ebc7945 100644 --- a/common/eda_draw_frame.cpp +++ b/common/eda_draw_frame.cpp @@ -1022,9 +1022,9 @@ void EDA_DRAW_FRAME::FocusOnLocation( const VECTOR2I& aPos ) { GetCanvas()->GetView()->SetCenter( aPos, dialogScreenRects ); } - catch( const ClipperLib::clipperException& e ) + catch( const Clipper2Lib::Clipper2Exception& e ) { - wxFAIL_MSG( wxString::Format( wxT( "Clipper exception occurred centering object: %s" ), + wxFAIL_MSG( wxString::Format( wxT( "Clipper2 exception occurred centering object: %s" ), e.what() ) ); } } diff --git a/common/import_gfx/graphics_importer_buffer.cpp b/common/import_gfx/graphics_importer_buffer.cpp index 55fd4ab655..4cc36ee4ab 100644 --- a/common/import_gfx/graphics_importer_buffer.cpp +++ b/common/import_gfx/graphics_importer_buffer.cpp @@ -250,9 +250,9 @@ static void convertPolygon( std::list<std::unique_ptr<IMPORTED_SHAPE>>& aShapes, upscaledPaths.push_back( lc ); } - SHAPE_POLY_SET result = SHAPE_POLY_SET::BuildPolysetFromOrientedPaths( - upscaledPaths, false, aFillRule == GRAPHICS_IMPORTER::PF_EVEN_ODD ); - result.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + SHAPE_POLY_SET result; + result.BuildPolysetFromOrientedPaths( upscaledPaths, aFillRule == GRAPHICS_IMPORTER::PF_EVEN_ODD ); + result.Fracture(); for( int outl = 0; outl < result.OutlineCount(); outl++ ) { diff --git a/common/plotters/DXF_plotter.cpp b/common/plotters/DXF_plotter.cpp index 8f76970361..6f893f3677 100644 --- a/common/plotters/DXF_plotter.cpp +++ b/common/plotters/DXF_plotter.cpp @@ -574,8 +574,8 @@ void DXF_PLOTTER::PlotPoly( const std::vector<VECTOR2I>& aCornerList, FILL_T aFi // polygon and its thick outline // create the outline which contains thick outline: - bufferPolybase.BooleanAdd( bufferOutline, SHAPE_POLY_SET::PM_FAST ); - bufferPolybase.Fracture( SHAPE_POLY_SET::PM_FAST ); + bufferPolybase.BooleanAdd( bufferOutline ); + bufferPolybase.Fracture(); if( bufferPolybase.OutlineCount() < 1 ) // should not happen return; diff --git a/common/view/view.cpp b/common/view/view.cpp index cc664c347a..61f1504c45 100644 --- a/common/view/view.cpp +++ b/common/view/view.cpp @@ -622,7 +622,7 @@ void VIEW::SetCenter( const VECTOR2D& aCenter, const std::vector<BOX2D>& obscuri for( const BOX2D& obscuringScreenRect : obscuringScreenRects ) { SHAPE_POLY_SET obscuringPoly( obscuringScreenRect ); - unobscuredPoly.BooleanSubtract( obscuringPoly, SHAPE_POLY_SET::PM_FAST ); + unobscuredPoly.BooleanSubtract( obscuringPoly ); } /* diff --git a/eeschema/gfx_import_utils.cpp b/eeschema/gfx_import_utils.cpp index 7f96567eee..f7ee1ce763 100644 --- a/eeschema/gfx_import_utils.cpp +++ b/eeschema/gfx_import_utils.cpp @@ -91,7 +91,7 @@ std::unordered_map<uint32_t, SHAPE_POLY_SET> ConvertImageToPolygons( wxImage im for( auto& [color, polySet] : colorPolys ) { - polySet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + polySet.Simplify(); for( int i = 0; i < polySet.OutlineCount(); i++ ) { @@ -114,7 +114,7 @@ void ConvertImageToLibShapes( LIB_SYMBOL* aSymbol, int unit, wxImage img, VECTOR for( auto& [color, polySet] : colorPolys ) { - polySet.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + polySet.Fracture(); for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() ) { diff --git a/eeschema/sch_painter.cpp b/eeschema/sch_painter.cpp index 8afe0082ed..ae4fe4b044 100644 --- a/eeschema/sch_painter.cpp +++ b/eeschema/sch_painter.cpp @@ -664,8 +664,8 @@ static void knockoutText( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2 finalPoly.Append( bbox.GetRight(), bbox.GetBottom() ); finalPoly.Append( bbox.GetLeft(), bbox.GetBottom() ); - finalPoly.BooleanSubtract( knockouts, SHAPE_POLY_SET::PM_FAST ); - finalPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); + finalPoly.BooleanSubtract( knockouts ); + finalPoly.Fracture(); aGal.SetIsStroke( false ); aGal.SetIsFill( true ); diff --git a/gerbview/aperture_macro.cpp b/gerbview/aperture_macro.cpp index 45f6d841bb..060818f4f8 100644 --- a/gerbview/aperture_macro.cpp +++ b/gerbview/aperture_macro.cpp @@ -124,19 +124,19 @@ SHAPE_POLY_SET* APERTURE_MACRO::GetApertureMacroShape( const GERBER_DRAW_ITEM* a if( holeBuffer.OutlineCount() ) // we have a new hole in shape: remove the hole { - m_shape.BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST ); + m_shape.BooleanSubtract( holeBuffer ); holeBuffer.RemoveAllContours(); } } } // Merge and cleanup basic shape polygons - m_shape.Simplify( SHAPE_POLY_SET::PM_FAST ); + m_shape.Simplify(); // A hole can be is defined inside a polygon, or the polygons themselve can create // a hole when merged, so we must fracture the polygon to be able to drawn it // (i.e link holes by overlapping edges) - m_shape.Fracture( SHAPE_POLY_SET::PM_FAST ); + m_shape.Fracture(); // Move m_shape to the actual draw position: for( int icnt = 0; icnt < m_shape.OutlineCount(); icnt++ ) diff --git a/gerbview/dcode.cpp b/gerbview/dcode.cpp index 22ae7b0b41..ac44c5a3d4 100644 --- a/gerbview/dcode.cpp +++ b/gerbview/dcode.cpp @@ -448,6 +448,6 @@ static void addHoleToPolygon( SHAPE_POLY_SET* aPolygon, APERTURE_DEF_HOLETYPE aH holeBuffer.Append( VECTOR2I( currpos ) ); // close hole } - aPolygon->BooleanSubtract( holeBuffer, SHAPE_POLY_SET::PM_FAST ); - aPolygon->Fracture( SHAPE_POLY_SET::PM_FAST ); + aPolygon->BooleanSubtract( holeBuffer ); + aPolygon->Fracture(); } diff --git a/gerbview/export_to_pcbnew.cpp b/gerbview/export_to_pcbnew.cpp index b5045ada3b..49f7363edb 100644 --- a/gerbview/export_to_pcbnew.cpp +++ b/gerbview/export_to_pcbnew.cpp @@ -574,7 +574,7 @@ void GBR_TO_PCB_EXPORTER::writePcbPolygon( const SHAPE_POLY_SET& aPolys, int aLa void GBR_TO_PCB_EXPORTER::writePcbZoneItem( const GERBER_DRAW_ITEM* aGbrItem, int aLayer ) { SHAPE_POLY_SET polys = aGbrItem->m_ShapeAsPolygon.CloneDropTriangulation(); - polys.Simplify( SHAPE_POLY_SET::PM_FAST ); + polys.Simplify(); if( polys.OutlineCount() == 0 ) return; diff --git a/include/advanced_config.h b/include/advanced_config.h index 508ba79b69..370101bc09 100644 --- a/include/advanced_config.h +++ b/include/advanced_config.h @@ -428,15 +428,6 @@ public: */ double m_3DRT_BevelExtentFactor; - /** - * Use Clipper2 instead of Clipper1. - * - * Setting name: "UseClipper2" - * Valid values: 0 or 1 - * Default value: 1 - */ - bool m_UseClipper2; - /** * Use the 3DConnexion Driver. * diff --git a/include/eda_shape.h b/include/eda_shape.h index c7102a74f9..dd56e7bf79 100644 --- a/include/eda_shape.h +++ b/include/eda_shape.h @@ -299,7 +299,7 @@ public: { if( m_poly.HoleCount( ii ) ) { - m_poly.Fracture( SHAPE_POLY_SET::PM_FAST ); + m_poly.Fracture(); break; } } diff --git a/libs/kimath/CMakeLists.txt b/libs/kimath/CMakeLists.txt index 5311ff6399..7aa20b991e 100644 --- a/libs/kimath/CMakeLists.txt +++ b/libs/kimath/CMakeLists.txt @@ -51,7 +51,6 @@ add_library( kimath STATIC target_link_libraries( kimath core - clipper clipper2 othermath rtree diff --git a/libs/kimath/include/geometry/polygon_triangulation.h b/libs/kimath/include/geometry/polygon_triangulation.h index fd5ea3e9df..651958f424 100644 --- a/libs/kimath/include/geometry/polygon_triangulation.h +++ b/libs/kimath/include/geometry/polygon_triangulation.h @@ -51,7 +51,6 @@ #include <cmath> #include <advanced_config.h> -#include <clipper.hpp> #include <geometry/shape_line_chain.h> #include <geometry/shape_poly_set.h> #include <geometry/vertex_set.h> diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h index da0e09d08f..bd8b123059 100644 --- a/libs/kimath/include/geometry/shape_line_chain.h +++ b/libs/kimath/include/geometry/shape_line_chain.h @@ -27,7 +27,6 @@ #define __SHAPE_LINE_CHAIN -#include <clipper.hpp> #include <clipper2/clipper.h> #include <geometry/seg.h> #include <geometry/shape.h> @@ -37,7 +36,7 @@ /** * Holds information on each point of a SHAPE_LINE_CHAIN that is retrievable - * after an operation with ClipperLib + * after an operation with Clipper2Lib */ struct CLIPPER_Z_VALUE { @@ -176,10 +175,6 @@ public: SHAPE_LINE_CHAIN( const SHAPE_ARC& aArc, bool aClosed = false ); - SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath, - const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, - const std::vector<SHAPE_ARC>& aArcBuffer ); - SHAPE_LINE_CHAIN( const Clipper2Lib::Path64& aPath, const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, const std::vector<SHAPE_ARC>& aArcBuffer ); @@ -920,13 +915,6 @@ protected: return m_shapes[aSegment].second; } - /** - * Create a new Clipper path from the SHAPE_LINE_CHAIN in a given orientation - */ - ClipperLib::Path convertToClipper( bool aRequiredOrientation, - std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, - std::vector<SHAPE_ARC>& aArcBuffer ) const; - /** * Create a new Clipper2 path from the SHAPE_LINE_CHAIN in a given orientation */ diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index 0222f144ed..5ee469a011 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -39,7 +39,6 @@ #include <stdlib.h> // for abs #include <vector> -#include <clipper.hpp> // for ClipType, PolyTree (ptr only) #include <clipper2/clipper.h> #include <core/mirror.h> // for FLIP_DIRECTION #include <geometry/corner_strategy.h> @@ -986,55 +985,30 @@ public: return CIterateSegments( aOutline, aOutline, true ); } - /** - * Operations on polygons use a \a aFastMode param - * if aFastMode is #PM_FAST (true) the result can be a weak polygon - * if aFastMode is #PM_STRICTLY_SIMPLE (false) (default) the result is (theoretically) a - * strictly simple polygon, but calculations can be really significantly time consuming - * Most of time #PM_FAST is preferable. - * #PM_STRICTLY_SIMPLE can be used in critical cases (Gerber output for instance) - */ - enum POLYGON_MODE - { - PM_FAST = true, - PM_STRICTLY_SIMPLE = false - }; /// Perform boolean polyset union - /// For \a aFastMode meaning, see function booleanOp - void BooleanAdd( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ); + void BooleanAdd( const SHAPE_POLY_SET& b ); /// Perform boolean polyset difference - /// For \a aFastMode meaning, see function booleanOp - void BooleanSubtract( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ); + void BooleanSubtract( const SHAPE_POLY_SET& b ); /// Perform boolean polyset intersection - /// For \a aFastMode meaning, see function booleanOp - void BooleanIntersection( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ); + void BooleanIntersection( const SHAPE_POLY_SET& b ); /// Perform boolean polyset exclusive or - /// For \a aFastMode meaning, see function booleanOp - void BooleanXor( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ); + void BooleanXor( const SHAPE_POLY_SET& b ); /// Perform boolean polyset union between a and b, store the result in it self - /// For \a aFastMode meaning, see function booleanOp - void BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ); + void BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ); /// Perform boolean polyset difference between a and b, store the result in it self - /// For \a aFastMode meaning, see function booleanOp - void BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ); + void BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ); /// Perform boolean polyset intersection between a and b, store the result in it self - /// For \a aFastMode meaning, see function booleanOp - void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ); + void BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ); /// Perform boolean polyset exclusive or between a and b, store the result in it self - /// For \a aFastMode meaning, see function booleanOp - void BooleanXor( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ); + void BooleanXor( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ); /** * Extract all contours from this polygon set, then recreate polygons with holes. @@ -1085,20 +1059,17 @@ public: * Perform outline inflation/deflation, using round corners. * * Polygons can have holes and/or linked holes with main outlines. The resulting - * polygons are also polygons with linked holes to main outlines. For \a aFastMode - * meaning, see function booleanOp . + * polygons are also polygons with linked holes to main outlines. */ - void InflateWithLinkedHoles( int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError, - POLYGON_MODE aFastMode ); + void InflateWithLinkedHoles( int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError ); /// Convert a set of polygons with holes to a single outline with "slits"/"fractures" /// connecting the outer ring to the inner holes - /// For \a aFastMode meaning, see function booleanOp - void Fracture( POLYGON_MODE aFastMode ); + void Fracture(); /// Convert a single outline slitted ("fractured") polygon into a set ouf outlines /// with holes. - void Unfracture( POLYGON_MODE aFastMode ); + void Unfracture(); /// Return true if the polygon set has any holes. bool HasHoles() const; @@ -1108,8 +1079,7 @@ public: /// Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) - /// For \a aFastMode meaning, see function booleanOp - void Simplify( POLYGON_MODE aFastMode ); + void Simplify(); /** * Simplifies the lines in the polyset. This checks intermediate points to see if they are @@ -1464,11 +1434,9 @@ public: * Build a SHAPE_POLY_SET from a bunch of outlines in provided in random order. * * @param aPath set of closed outlines forming the polygon. Positive orientation = outline, negative = hole - * @param aReverseOrientation inverts the sign of the orientation of aPaths (so negative = outline) * @param aEvenOdd forces the even-off fill rule (default is non zero) - * @return the constructed poly set */ - static const SHAPE_POLY_SET BuildPolysetFromOrientedPaths( const std::vector<SHAPE_LINE_CHAIN>& aPaths, bool aReverseOrientation = false, bool aEvenOdd = false ); + void BuildPolysetFromOrientedPaths( const std::vector<SHAPE_LINE_CHAIN>& aPaths, bool aEvenOdd = false ); void TransformToPolygon( SHAPE_POLY_SET& aBuffer, int aError, ERROR_LOC aErrorLoc ) const override @@ -1487,9 +1455,6 @@ private: void fractureSingle( POLYGON& paths ); void unfractureSingle ( POLYGON& path ); - void importTree( ClipperLib::PolyTree* tree, - const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, - const std::vector<SHAPE_ARC>& aArcBuffe ); void importTree( Clipper2Lib::PolyTree64& tree, const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, const std::vector<SHAPE_ARC>& aArcBuffe ); @@ -1500,7 +1465,6 @@ private: const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, const std::vector<SHAPE_ARC>& aArcBuffer ); - void inflate1( int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy ); void inflate2( int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy, bool aSimplify = false ); void inflateLine2( const SHAPE_LINE_CHAIN& aLine, int aAmount, int aCircleSegCount, @@ -1510,20 +1474,9 @@ private: * This is the engine to execute all polygon boolean transforms (AND, OR, ... and polygon * simplification (merging overlapping polygons). * - * @param aType is the transform type ( see ClipperLib::ClipType ) + * @param aType is the transform type ( see Clipper2Lib::ClipType ) * @param aOtherShape is the SHAPE_LINE_CHAIN to combine with me. - * @param aFastMode is an option to choose if the result can be a weak polygon - * or a strictly simple polygon. - * if aFastMode is PM_FAST the result can be a weak polygon - * if aFastMode is PM_STRICTLY_SIMPLE (default) the result is (theoretically) a strictly - * simple polygon, but calculations can be really significantly time consuming */ - void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape, - POLYGON_MODE aFastMode ); - - void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aShape, - const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode ); - void booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SET& aOtherShape ); void booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SET& aShape, diff --git a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp index c6ebe4ae9e..aabe133fdd 100644 --- a/libs/kimath/src/convert_basic_shapes_to_polygon.cpp +++ b/libs/kimath/src/convert_basic_shapes_to_polygon.cpp @@ -214,7 +214,7 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aStart, co bbox.Append( corner.x, corner.y ); // Now, clamp the shape - polyshape.BooleanIntersection( bbox, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + polyshape.BooleanIntersection( bbox ); // Note the final polygon is a simple, convex polygon with no hole // due to the shape of initial polygons @@ -663,6 +663,6 @@ void TransformRingToPolygon( SHAPE_POLY_SET& aBuffer, const VECTOR2I& aCentre, i TransformCircleToPolygon( buffer.Hole( 0, 0 ), aCentre, inner_radius, aError, inner_err_loc ); - buffer.Fracture( SHAPE_POLY_SET::PM_FAST ); + buffer.Fracture(); aBuffer.Append( buffer ); } diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp index 412048edf6..4264a99b9a 100644 --- a/libs/kimath/src/geometry/shape_line_chain.cpp +++ b/libs/kimath/src/geometry/shape_line_chain.cpp @@ -29,7 +29,6 @@ #include <map> #include <string> // for basic_string -#include <clipper.hpp> #include <clipper2/clipper.h> #include <core/kicad_algo.h> // for alg::run_on_pair #include <geometry/circle.h> @@ -79,50 +78,6 @@ SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const SHAPE_ARC& aArc, bool aClosed ) : } -SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath, - const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, - const std::vector<SHAPE_ARC>& aArcBuffer ) : - SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), - m_closed( true ), m_width( 0 ) -{ - std::map<ssize_t, ssize_t> loadedArcs; - m_points.reserve( aPath.size() ); - m_shapes.reserve( aPath.size() ); - - auto loadArc = - [&]( ssize_t aArcIndex ) -> ssize_t - { - if( aArcIndex == SHAPE_IS_PT ) - { - return SHAPE_IS_PT; - } - else if( loadedArcs.count( aArcIndex ) == 0 ) - { - loadedArcs.insert( { aArcIndex, m_arcs.size() } ); - m_arcs.push_back( aArcBuffer.at( aArcIndex ) ); - } - - return loadedArcs.at( aArcIndex ); - }; - - for( size_t ii = 0; ii < aPath.size(); ++ii ) - { - Append( aPath[ii].X, aPath[ii].Y ); - - m_shapes[ii].first = loadArc( aZValueBuffer[aPath[ii].Z].m_FirstArcIdx ); - m_shapes[ii].second = loadArc( aZValueBuffer[aPath[ii].Z].m_SecondArcIdx ); - } - - // Clipper shouldn't return duplicate contiguous points. if it did, these would be - // removed during Append() and we would have different number of shapes to points - wxASSERT( m_shapes.size() == m_points.size() ); - - // Clipper might mess up the rotation of the indices such that an arc can be split between - // the end point and wrap around to the start point. Lets fix the indices up now - fixIndicesRotation(); -} - - SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const Clipper2Lib::Path64& aPath, const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, const std::vector<SHAPE_ARC>& aArcBuffer ) : @@ -167,40 +122,6 @@ SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const Clipper2Lib::Path64& aPath, } -ClipperLib::Path SHAPE_LINE_CHAIN::convertToClipper( bool aRequiredOrientation, - std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, - std::vector<SHAPE_ARC>& aArcBuffer ) const -{ - ClipperLib::Path c_path; - SHAPE_LINE_CHAIN input; - bool orientation = Area( false ) >= 0; - ssize_t shape_offset = aArcBuffer.size(); - - if( orientation != aRequiredOrientation ) - input = Reverse(); - else - input = *this; - - int pointCount = input.PointCount(); - c_path.reserve( pointCount ); - - for( int i = 0; i < pointCount; i++ ) - { - const VECTOR2I& vertex = input.CPoint( i ); - - CLIPPER_Z_VALUE z_value( input.m_shapes[i], shape_offset ); - size_t z_value_ptr = aZValueBuffer.size(); - aZValueBuffer.push_back( z_value ); - - c_path.emplace_back( vertex.x, vertex.y, z_value_ptr ); - } - - aArcBuffer.insert( aArcBuffer.end(), input.m_arcs.begin(), input.m_arcs.end() ); - - return c_path; -} - - Clipper2Lib::Path64 SHAPE_LINE_CHAIN::convertToClipper2( bool aRequiredOrientation, std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, std::vector<SHAPE_ARC>& aArcBuffer ) const diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index 1a9045b410..630059b131 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -744,118 +744,6 @@ void SHAPE_POLY_SET::RebuildHolesFromContours() } -void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape, - POLYGON_MODE aFastMode ) -{ - booleanOp( aType, *this, aOtherShape, aFastMode ); -} - - -void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aShape, - const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode ) -{ - if( ( aShape.OutlineCount() > 1 || aOtherShape.OutlineCount() > 0 ) - && ( aShape.ArcCount() > 0 || aOtherShape.ArcCount() > 0 ) ) - { - wxFAIL_MSG( wxT( "Boolean ops on curved polygons are not supported. You should call " - "ClearArcs() before carrying out the boolean operation." ) ); - } - - ClipperLib::Clipper c; - - c.StrictlySimple( aFastMode == PM_STRICTLY_SIMPLE ); - - std::vector<CLIPPER_Z_VALUE> zValues; - std::vector<SHAPE_ARC> arcBuffer; - std::map<VECTOR2I, CLIPPER_Z_VALUE> newIntersectPoints; - - for( const POLYGON& poly : aShape.m_polys ) - { - for( size_t i = 0; i < poly.size(); i++ ) - { - c.AddPath( poly[i].convertToClipper( i == 0, zValues, arcBuffer ), - ClipperLib::ptSubject, true ); - } - } - - for( const POLYGON& poly : aOtherShape.m_polys ) - { - for( size_t i = 0; i < poly.size(); i++ ) - { - c.AddPath( poly[i].convertToClipper( i == 0, zValues, arcBuffer ), - ClipperLib::ptClip, true ); - } - } - - ClipperLib::PolyTree solution; - - ClipperLib::ZFillCallback callback = - [&]( ClipperLib::IntPoint & e1bot, ClipperLib::IntPoint & e1top, - ClipperLib::IntPoint & e2bot, ClipperLib::IntPoint & e2top, - ClipperLib::IntPoint & pt ) - { - auto arcIndex = - [&]( const ssize_t& aZvalue, const ssize_t& aCompareVal = -1 ) -> ssize_t - { - ssize_t retval; - - retval = zValues.at( aZvalue ).m_SecondArcIdx; - - if( retval == -1 || ( aCompareVal > 0 && retval != aCompareVal ) ) - retval = zValues.at( aZvalue ).m_FirstArcIdx; - - return retval; - }; - - auto arcSegment = - [&]( const ssize_t& aBottomZ, const ssize_t aTopZ ) -> ssize_t - { - ssize_t retval = arcIndex( aBottomZ ); - - if( retval != -1 ) - { - if( retval != arcIndex( aTopZ, retval ) ) - retval = -1; // Not an arc segment as the two indices do not match - } - - return retval; - }; - - ssize_t e1ArcSegmentIndex = arcSegment( e1bot.Z, e1top.Z ); - ssize_t e2ArcSegmentIndex = arcSegment( e2bot.Z, e2top.Z ); - - CLIPPER_Z_VALUE newZval; - - if( e1ArcSegmentIndex != -1 ) - { - newZval.m_FirstArcIdx = e1ArcSegmentIndex; - newZval.m_SecondArcIdx = e2ArcSegmentIndex; - } - else - { - newZval.m_FirstArcIdx = e2ArcSegmentIndex; - newZval.m_SecondArcIdx = -1; - } - - size_t z_value_ptr = zValues.size(); - zValues.push_back( newZval ); - - // Only worry about arc segments for later processing - if( newZval.m_FirstArcIdx != -1 ) - newIntersectPoints.insert( { VECTOR2I( pt.X, pt.Y ), newZval } ); - - pt.Z = z_value_ptr; - //@todo amend X,Y values to true intersection between arcs or arc and segment - }; - - c.ZFillFunction( std::move( callback ) ); // register callback - - c.Execute( aType, solution, ClipperLib::pftNonZero, ClipperLib::pftNonZero ); - - importTree( &solution, zValues, arcBuffer ); -} - - void SHAPE_POLY_SET::booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SET& aOtherShape ) { booleanOp( aType, *this, aOtherShape ); @@ -970,155 +858,60 @@ void SHAPE_POLY_SET::booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SE } -void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Union, b ); } -void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Difference, b ); } -void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Intersection, b ); } -void SHAPE_POLY_SET::BooleanXor( const SHAPE_POLY_SET& b, POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanXor( const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Xor, b ); } -void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Union, a, b ); } -void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Difference, a, b ); } -void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanIntersection( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Intersection, a, b ); } -void SHAPE_POLY_SET::BooleanXor( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b, - POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::BooleanXor( const SHAPE_POLY_SET& a, const SHAPE_POLY_SET& b ) { booleanOp( Clipper2Lib::ClipType::Xor, a, b ); } void SHAPE_POLY_SET::InflateWithLinkedHoles( int aFactor, CORNER_STRATEGY aCornerStrategy, - int aMaxError, POLYGON_MODE aFastMode ) + int aMaxError ) { - Unfracture( aFastMode ); + Unfracture(); Inflate( aFactor, aCornerStrategy, aMaxError ); - Fracture( aFastMode ); -} - - -void SHAPE_POLY_SET::inflate1( int aAmount, int aCircleSegCount, CORNER_STRATEGY aCornerStrategy ) -{ - using namespace ClipperLib; - // A static table to avoid repetitive calculations of the coefficient - // 1.0 - cos( M_PI / aCircleSegCount ) - // aCircleSegCount is most of time <= 64 and usually 8, 12, 16, 32 - #define SEG_CNT_MAX 64 - static double arc_tolerance_factor[SEG_CNT_MAX + 1]; - - ClipperOffset c; - - // N.B. see the Clipper documentation for jtSquare/jtMiter/jtRound. They are poorly named - // and are not what you'd think they are. - // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Types/JoinType.htm - JoinType joinType = jtRound; // The way corners are offsetted - double miterLimit = 2.0; // Smaller value when using jtMiter for joinType - JoinType miterFallback = jtSquare; - - switch( aCornerStrategy ) - { - case CORNER_STRATEGY::ALLOW_ACUTE_CORNERS: - joinType = jtMiter; - miterLimit = 10; // Allows large spikes - miterFallback = jtSquare; - break; - - case CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS: // Acute angles are chamfered - joinType = jtMiter; - miterFallback = jtRound; - break; - - case CORNER_STRATEGY::ROUND_ACUTE_CORNERS: // Acute angles are rounded - joinType = jtMiter; - miterFallback = jtSquare; - break; - - case CORNER_STRATEGY::CHAMFER_ALL_CORNERS: // All angles are chamfered. - joinType = jtSquare; - miterFallback = jtSquare; - break; - - case CORNER_STRATEGY::ROUND_ALL_CORNERS: // All angles are rounded. - joinType = jtRound; - miterFallback = jtSquare; - break; - } - - std::vector<CLIPPER_Z_VALUE> zValues; - std::vector<SHAPE_ARC> arcBuffer; - - for( const POLYGON& poly : m_polys ) - { - for( size_t i = 0; i < poly.size(); i++ ) - { - c.AddPath( poly[i].convertToClipper( i == 0, zValues, arcBuffer ), - joinType, etClosedPolygon ); - } - } - - PolyTree solution; - - // Calculate the arc tolerance (arc error) from the seg count by circle. The seg count is - // nn = M_PI / acos(1.0 - c.ArcTolerance / abs(aAmount)) - // http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/Properties/ArcTolerance.htm - - if( aCircleSegCount < 6 ) // avoid incorrect aCircleSegCount values - aCircleSegCount = 6; - - double coeff; - - if( aCircleSegCount > SEG_CNT_MAX || arc_tolerance_factor[aCircleSegCount] == 0 ) - { - coeff = 1.0 - cos( M_PI / aCircleSegCount ); - - if( aCircleSegCount <= SEG_CNT_MAX ) - arc_tolerance_factor[aCircleSegCount] = coeff; - } - else - { - coeff = arc_tolerance_factor[aCircleSegCount]; - } - - c.ArcTolerance = std::abs( aAmount ) * coeff; - c.MiterLimit = miterLimit; - c.MiterFallback = miterFallback; - c.Execute( solution, aAmount ); - - importTree( &solution, zValues, arcBuffer ); + Fracture(); } @@ -1341,30 +1134,6 @@ void SHAPE_POLY_SET::OffsetLineChain( const SHAPE_LINE_CHAIN& aLine, int aAmount } -void SHAPE_POLY_SET::importTree( ClipperLib::PolyTree* tree, - const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, - const std::vector<SHAPE_ARC>& aArcBuffer ) -{ - m_polys.clear(); - - for( ClipperLib::PolyNode* n = tree->GetFirst(); n; n = n->GetNext() ) - { - if( !n->IsHole() ) - { - POLYGON paths; - paths.reserve( n->Childs.size() + 1 ); - - paths.emplace_back( n->Contour, aZValueBuffer, aArcBuffer ); - - for( unsigned int i = 0; i < n->Childs.size(); i++ ) - paths.emplace_back( n->Childs[i]->Contour, aZValueBuffer, aArcBuffer ); - - m_polys.push_back( paths ); - } - } -} - - void SHAPE_POLY_SET::importPolyPath( const std::unique_ptr<Clipper2Lib::PolyPath64>& aPolyPath, const std::vector<CLIPPER_Z_VALUE>& aZValueBuffer, const std::vector<SHAPE_ARC>& aArcBuffer ) @@ -1886,9 +1655,9 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) } -void SHAPE_POLY_SET::Fracture( POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::Fracture() { - Simplify( aFastMode ); // remove overlapping holes/degeneracy + Simplify(); // remove overlapping holes/degeneracy for( POLYGON& paths : m_polys ) fractureSingle( paths ); @@ -2077,16 +1846,16 @@ bool SHAPE_POLY_SET::HasHoles() const } -void SHAPE_POLY_SET::Unfracture( POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::Unfracture() { for( POLYGON& path : m_polys ) unfractureSingle( path ); - Simplify( aFastMode ); // remove overlapping holes/degeneracy + Simplify(); // remove overlapping holes/degeneracy } -void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode ) +void SHAPE_POLY_SET::Simplify() { SHAPE_POLY_SET empty; @@ -2110,7 +1879,7 @@ int SHAPE_POLY_SET::NormalizeAreaOutlines() { // We are expecting only one main outline, but this main outline can have holes // if holes: combine holes and remove them from the main outline. - // Note also we are using SHAPE_POLY_SET::PM_STRICTLY_SIMPLE in polygon + // Note also we are usingin polygon // calculations, but it is not mandatory. It is used mainly // because there is usually only very few vertices in area outlines SHAPE_POLY_SET::POLYGON& outline = Polygon( 0 ); @@ -2124,13 +1893,13 @@ int SHAPE_POLY_SET::NormalizeAreaOutlines() outline.pop_back(); } - Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + Simplify(); // If any hole, subtract it to main outline if( holesBuffer.OutlineCount() ) { - holesBuffer.Simplify( SHAPE_POLY_SET::PM_FAST ); - BooleanSubtract( holesBuffer, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + holesBuffer.Simplify(); + BooleanSubtract( holesBuffer ); } // In degenerate cases, simplify might return no outlines @@ -3210,10 +2979,10 @@ static SHAPE_POLY_SET partitionPolyIntoRegularCellGrid( const SHAPE_POLY_SET& aP } } - ps1.BooleanIntersection( maskSetOdd, SHAPE_POLY_SET::PM_FAST ); - ps2.BooleanIntersection( maskSetEven, SHAPE_POLY_SET::PM_FAST ); - ps1.Fracture( SHAPE_POLY_SET::PM_FAST ); - ps2.Fracture( SHAPE_POLY_SET::PM_FAST ); + ps1.BooleanIntersection( maskSetOdd ); + ps2.BooleanIntersection( maskSetEven ); + ps1.Fracture(); + ps2.Fracture(); for( int i = 0; i < ps2.OutlineCount(); i++ ) ps1.AddOutline( ps2.COutline( i ) ); @@ -3269,10 +3038,6 @@ void SHAPE_POLY_SET::cacheTriangulation( bool aPartition, bool aSimplify, ++pass; if( pass == 1 ) - { - polySet.Fracture( PM_FAST ); - } - else if( pass == 2 ) { polySet.SimplifyOutlines( TRIANGULATESIMPLIFICATIONLEVEL ); } @@ -3310,9 +3075,9 @@ void SHAPE_POLY_SET::cacheTriangulation( bool aPartition, bool aSimplify, flattened.ClearArcs(); if( flattened.HasHoles() || flattened.IsSelfIntersecting() ) - flattened.Fracture( PM_FAST ); + flattened.Fracture(); else if( aSimplify ) - flattened.Simplify( PM_FAST ); + flattened.Simplify(); SHAPE_POLY_SET partitions = partitionPolyIntoRegularCellGrid( flattened, 1e7 ); @@ -3336,8 +3101,7 @@ void SHAPE_POLY_SET::cacheTriangulation( bool aPartition, bool aSimplify, SHAPE_POLY_SET tmpSet( *this ); tmpSet.ClearArcs(); - - tmpSet.Fracture( PM_FAST ); + tmpSet.Fracture(); if( !triangulate( tmpSet, -1, m_triangulatedPolys, aHintData ) ) { @@ -3498,55 +3262,35 @@ SHAPE_POLY_SET::TRIANGULATED_POLYGON::~TRIANGULATED_POLYGON() } -const SHAPE_POLY_SET +void SHAPE_POLY_SET::BuildPolysetFromOrientedPaths( const std::vector<SHAPE_LINE_CHAIN>& aPaths, - bool aReverseOrientation, bool aEvenOdd ) + bool aEvenOdd ) { - ClipperLib::Clipper clipper; - ClipperLib::PolyTree tree; - - // fixme: do we need aReverseOrientation? + Clipper2Lib::Clipper64 clipper; + Clipper2Lib::PolyTree64 tree; + Clipper2Lib::Paths64 paths; for( const SHAPE_LINE_CHAIN& path : aPaths ) { - ClipperLib::Path lc; + Clipper2Lib::Path64 lc; + lc.reserve( path.PointCount() ); for( int i = 0; i < path.PointCount(); i++ ) - { lc.emplace_back( path.CPoint( i ).x, path.CPoint( i ).y ); - } - clipper.AddPath( lc, ClipperLib::ptSubject, true ); + paths.push_back( lc ); } - clipper.StrictlySimple( true ); - clipper.Execute( ClipperLib::ctUnion, tree, - aEvenOdd ? ClipperLib::pftEvenOdd : ClipperLib::pftNonZero, - ClipperLib::pftNonZero ); - SHAPE_POLY_SET result; + clipper.AddSubject( paths ); + clipper.Execute( Clipper2Lib::ClipType::Union, aEvenOdd ? Clipper2Lib::FillRule::EvenOdd + : Clipper2Lib::FillRule::NonZero, tree ); - for( ClipperLib::PolyNode* n = tree.GetFirst(); n; n = n->GetNext() ) - { - if( !n->IsHole() ) - { - int outl = result.NewOutline(); - for( unsigned int i = 0; i < n->Contour.size(); i++ ) - result.Outline( outl ).Append( n->Contour[i].X, n->Contour[i].Y ); + std::vector<CLIPPER_Z_VALUE> zValues; + std::vector<SHAPE_ARC> arcBuffer; - for( unsigned int i = 0; i < n->Childs.size(); i++ ) - { - int outh = result.NewHole( outl ); - for( unsigned int j = 0; j < n->Childs[i]->Contour.size(); j++ ) - { - result.Hole( outl, outh ) - .Append( n->Childs[i]->Contour[j].X, n->Childs[i]->Contour[j].Y ); - } - } - } - } - - return result; + importTree( tree, zValues, arcBuffer ); + tree.Clear(); // Free used memory (not done in dtor) } diff --git a/pcbnew/autorouter/ar_autoplacer.cpp b/pcbnew/autorouter/ar_autoplacer.cpp index 8312495d0a..f64d107e92 100644 --- a/pcbnew/autorouter/ar_autoplacer.cpp +++ b/pcbnew/autorouter/ar_autoplacer.cpp @@ -146,7 +146,7 @@ bool AR_AUTOPLACER::fillMatrix() // Create a single board outline: SHAPE_POLY_SET brd_shape = m_boardShape.CloneDropTriangulation(); - brd_shape.Fracture( SHAPE_POLY_SET::PM_FAST ); + brd_shape.Fracture(); const SHAPE_LINE_CHAIN& outline = brd_shape.Outline(0); const BOX2I& rect = outline.BBox(); @@ -393,8 +393,8 @@ void AR_AUTOPLACER::genModuleOnRoutingMatrix( FOOTPRINT* Module ) buildFpAreas( Module, margin ); // Substract the shape to free areas - m_topFreeArea.BooleanSubtract( m_fpAreaTop, SHAPE_POLY_SET::PM_FAST ); - m_bottomFreeArea.BooleanSubtract( m_fpAreaBottom, SHAPE_POLY_SET::PM_FAST ); + m_topFreeArea.BooleanSubtract( m_fpAreaTop ); + m_bottomFreeArea.BooleanSubtract( m_fpAreaBottom ); } @@ -802,7 +802,7 @@ void AR_AUTOPLACER::drawPlacementRoutingMatrix( ) m_overlay->SetIsStroke( false ); SHAPE_POLY_SET freeArea = m_topFreeArea.CloneDropTriangulation(); - freeArea.Fracture( SHAPE_POLY_SET::PM_FAST ); + freeArea.Fracture(); // Draw the free polygon areas, top side: if( freeArea.OutlineCount() > 0 ) @@ -814,7 +814,7 @@ void AR_AUTOPLACER::drawPlacementRoutingMatrix( ) } freeArea = m_bottomFreeArea; - freeArea.Fracture( SHAPE_POLY_SET::PM_FAST ); + freeArea.Fracture(); // Draw the free polygon areas, bottom side: if( freeArea.OutlineCount() > 0 ) diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp index 1d70be6244..04ae430d7b 100644 --- a/pcbnew/board.cpp +++ b/pcbnew/board.cpp @@ -2541,7 +2541,7 @@ bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, } // Make polygon strictly simple to avoid issues (especially in 3D viewer) - aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + aOutlines.Simplify(); return success; } diff --git a/pcbnew/convert_shape_list_to_polygon.cpp b/pcbnew/convert_shape_list_to_polygon.cpp index c85901b9ee..cb388d23b9 100644 --- a/pcbnew/convert_shape_list_to_polygon.cpp +++ b/pcbnew/convert_shape_list_to_polygon.cpp @@ -164,8 +164,7 @@ static bool isCopperOutside( const FOOTPRINT* aFootprint, SHAPE_POLY_SET& aShape poly.ClearArcs(); - poly.BooleanIntersection( *pad->GetEffectivePolygon( aLayer, ERROR_INSIDE ), - SHAPE_POLY_SET::PM_FAST ); + poly.BooleanIntersection( *pad->GetEffectivePolygon( aLayer, ERROR_INSIDE ) ); if( poly.OutlineCount() == 0 ) { diff --git a/pcbnew/drc/drc_test_provider_annular_width.cpp b/pcbnew/drc/drc_test_provider_annular_width.cpp index df06c610a1..cf3ead8f78 100644 --- a/pcbnew/drc/drc_test_provider_annular_width.cpp +++ b/pcbnew/drc/drc_test_provider_annular_width.cpp @@ -283,7 +283,7 @@ bool DRC_TEST_PROVIDER_ANNULAR_WIDTH::Run() otherPadHoles, 0, maxError, ERROR_INSIDE ); } - otherPadOutline.BooleanSubtract( otherPadHoles, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + otherPadOutline.BooleanSubtract( otherPadHoles ); // If the pad hole under test intersects with another pad outline, // the annular width calculated above is used. diff --git a/pcbnew/drc/drc_test_provider_connection_width.cpp b/pcbnew/drc/drc_test_provider_connection_width.cpp index 53ed29562e..537b863555 100644 --- a/pcbnew/drc/drc_test_provider_connection_width.cpp +++ b/pcbnew/drc/drc_test_provider_connection_width.cpp @@ -384,7 +384,7 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run() ERROR_OUTSIDE ); } - itemsPoly.Poly.Fracture( SHAPE_POLY_SET::PM_FAST ); + itemsPoly.Poly.Fracture(); done.fetch_add( calc_effort( itemsPoly.Items, aLayer ) ); diff --git a/pcbnew/drc/drc_test_provider_disallow.cpp b/pcbnew/drc/drc_test_provider_disallow.cpp index e21b881f7a..4129448252 100644 --- a/pcbnew/drc/drc_test_provider_disallow.cpp +++ b/pcbnew/drc/drc_test_provider_disallow.cpp @@ -132,7 +132,7 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run() // exclude it. This is particularly important for detecting copper fills as // they will be exactly touching along the entire exclusion border. SHAPE_POLY_SET areaPoly = ruleArea->Outline()->CloneDropTriangulation(); - areaPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); + areaPoly.Fracture(); areaPoly.Deflate( epsilon, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS, ARC_LOW_DEF ); DRC_RTREE* zoneRTree = board->m_CopperZoneRTreeCache[ copperZone ].get(); diff --git a/pcbnew/drc/drc_test_provider_physical_clearance.cpp b/pcbnew/drc/drc_test_provider_physical_clearance.cpp index cc5db8f2fe..aa844c04be 100644 --- a/pcbnew/drc/drc_test_provider_physical_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_physical_clearance.cpp @@ -555,7 +555,7 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER SHAPE_POLY_SET fill = aZone->GetFilledPolysList( aLayer )->CloneDropTriangulation(); // Turn fractured fill into outlines and holes - fill.Simplify( SHAPE_POLY_SET::PM_FAST ); + fill.Simplify(); for( int outlineIdx = 0; outlineIdx < fill.OutlineCount(); ++outlineIdx ) { diff --git a/pcbnew/drc/drc_test_provider_sliver_checker.cpp b/pcbnew/drc/drc_test_provider_sliver_checker.cpp index 344de09212..765a692782 100644 --- a/pcbnew/drc/drc_test_provider_sliver_checker.cpp +++ b/pcbnew/drc/drc_test_provider_sliver_checker.cpp @@ -155,7 +155,7 @@ bool DRC_TEST_PROVIDER_SLIVER_CHECKER::Run() if( m_drcEngine->IsCancelled() ) return 0; - poly.Simplify( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + poly.Simplify(); return 1; }; diff --git a/pcbnew/drc/drc_test_provider_solder_mask.cpp b/pcbnew/drc/drc_test_provider_solder_mask.cpp index 77118bf2f5..285362f847 100644 --- a/pcbnew/drc/drc_test_provider_solder_mask.cpp +++ b/pcbnew/drc/drc_test_provider_solder_mask.cpp @@ -122,8 +122,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* aItem ) { if( zone->IsOnLayer( layer ) ) { - solderMask->GetFill( layer )->BooleanAdd( *zone->GetFilledPolysList( layer ), - SHAPE_POLY_SET::PM_FAST ); + solderMask->GetFill( layer )->BooleanAdd( *zone->GetFilledPolysList( layer ) ); } } } @@ -222,8 +221,8 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees() return true; } ); - solderMask->GetFill( F_Mask )->Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - solderMask->GetFill( B_Mask )->Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + solderMask->GetFill( F_Mask )->Simplify(); + solderMask->GetFill( B_Mask )->Simplify(); solderMask->GetFill( F_Mask )->Deflate( m_webWidth / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, m_maxError ); diff --git a/pcbnew/drc/drc_test_provider_text_dims.cpp b/pcbnew/drc/drc_test_provider_text_dims.cpp index 7e56ff9a14..1570ae6b69 100644 --- a/pcbnew/drc/drc_test_provider_text_dims.cpp +++ b/pcbnew/drc/drc_test_provider_text_dims.cpp @@ -177,7 +177,7 @@ bool DRC_TEST_PROVIDER_TEXT_DIMS::Run() SHAPE_POLY_SET poly = outlineGlyph->CloneDropTriangulation(); poly.Deflate( constraint.Value().Min() / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, ARC_LOW_DEF ); - poly.Simplify( SHAPE_POLY_SET::PM_FAST ); + poly.Simplify(); int resultingOutlineCount = poly.OutlineCount(); int resultingHoleCount = 0; diff --git a/pcbnew/exporters/export_hyperlynx.cpp b/pcbnew/exporters/export_hyperlynx.cpp index 04e22dab22..3a4262ad82 100644 --- a/pcbnew/exporters/export_hyperlynx.cpp +++ b/pcbnew/exporters/export_hyperlynx.cpp @@ -502,7 +502,7 @@ bool HYPERLYNX_EXPORTER::writeNetObjects( const std::vector<BOARD_ITEM*>& aObjec const wxString layerName = m_board->GetLayerName( layer ); SHAPE_POLY_SET fill = zone->GetFilledPolysList( layer )->CloneDropTriangulation(); - fill.Simplify( SHAPE_POLY_SET::PM_FAST ); + fill.Simplify(); for( int i = 0; i < fill.OutlineCount(); i++ ) { diff --git a/pcbnew/exporters/exporter_vrml.cpp b/pcbnew/exporters/exporter_vrml.cpp index 4ea84054fb..66d793359d 100644 --- a/pcbnew/exporters/exporter_vrml.cpp +++ b/pcbnew/exporters/exporter_vrml.cpp @@ -394,8 +394,8 @@ void EXPORTER_PCB_VRML::ExportVrmlSolderMask() outlines = m_pcbOutlines; m_board->ConvertBrdLayerToPolygonalContours( pcb_layer, holes ); - outlines.BooleanSubtract( holes, SHAPE_POLY_SET::PM_FAST ); - outlines.Fracture( SHAPE_POLY_SET::PM_FAST ); + outlines.BooleanSubtract( holes ); + outlines.Fracture(); ExportVrmlPolygonSet( vrmllayer, outlines ); pcb_layer = B_Mask; @@ -426,8 +426,8 @@ void EXPORTER_PCB_VRML::ExportStandardLayers() outlines.RemoveAllContours(); m_board->ConvertBrdLayerToPolygonalContours( pcb_layer[lcnt], outlines ); - outlines.BooleanIntersection( m_pcbOutlines, SHAPE_POLY_SET::PM_FAST ); - outlines.Fracture( SHAPE_POLY_SET::PM_FAST ); + outlines.BooleanIntersection( m_pcbOutlines ); + outlines.Fracture(); ExportVrmlPolygonSet( vrmllayer[lcnt], outlines ); } diff --git a/pcbnew/exporters/step/exporter_step.cpp b/pcbnew/exporters/step/exporter_step.cpp index fdb518fa50..2fe828941b 100644 --- a/pcbnew/exporters/step/exporter_step.cpp +++ b/pcbnew/exporters/step/exporter_step.cpp @@ -463,7 +463,7 @@ void EXPORTER_STEP::buildZones3DShape( VECTOR2D aOrigin ) { SHAPE_POLY_SET fill_shape; zone->TransformSolidAreasShapesToPolygon( layer, fill_shape ); - fill_shape.Unfracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fill_shape.Unfracture(); fill_shape.SimplifyOutlines( ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel ); @@ -640,31 +640,31 @@ bool EXPORTER_STEP::buildBoard3DShapes() for( PCB_LAYER_ID pcblayer : m_layersToExport.Seq() ) { SHAPE_POLY_SET poly = m_poly_shapes[pcblayer]; - poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + poly.Simplify(); poly.SimplifyOutlines( pcbIUScale.mmToIU( 0.003 ) ); - poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + poly.Simplify(); SHAPE_POLY_SET holes = m_poly_holes[pcblayer]; - holes.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + holes.Simplify(); // Mask layer is negative if( pcblayer == F_Mask || pcblayer == B_Mask ) { SHAPE_POLY_SET mask = pcbOutlinesNoArcs; - mask.BooleanSubtract( poly, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - mask.BooleanSubtract( holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + mask.BooleanSubtract( poly ); + mask.BooleanSubtract( holes ); poly = mask; } else { // Subtract holes - poly.BooleanSubtract( holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + poly.BooleanSubtract( holes ); // Clip to board outline - poly.BooleanIntersection( pcbOutlinesNoArcs, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + poly.BooleanIntersection( pcbOutlinesNoArcs ); } m_pcbModel->AddPolygonShapes( &poly, pcblayer, origin ); diff --git a/pcbnew/exporters/step/step_pcb_model.cpp b/pcbnew/exporters/step/step_pcb_model.cpp index 1ca5fee1cd..a37c667bd6 100644 --- a/pcbnew/exporters/step/step_pcb_model.cpp +++ b/pcbnew/exporters/step/step_pcb_model.cpp @@ -1561,7 +1561,7 @@ bool STEP_PCB_MODEL::MakeShapes( std::vector<TopoDS_Shape>& aShapes, const SHAPE double aThickness, double aZposition, const VECTOR2D& aOrigin ) { SHAPE_POLY_SET workingPoly = aPolySet; - workingPoly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + workingPoly.Simplify(); SHAPE_POLY_SET fallbackPoly = workingPoly; diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp index 750050354d..561984373e 100644 --- a/pcbnew/footprint.cpp +++ b/pcbnew/footprint.cpp @@ -2777,7 +2777,7 @@ double FOOTPRINT::GetCoverageArea( const BOARD_ITEM* aItem, const GENERAL_COLLEC { SHAPE_POLY_SET padPoly; aItem->TransformShapeToPolygon( padPoly, aLayer, 0, ARC_LOW_DEF, ERROR_OUTSIDE ); - poly.BooleanAdd( padPoly, SHAPE_POLY_SET::PM_FAST ); + poly.BooleanAdd( padPoly ); } ); } else @@ -2837,7 +2837,7 @@ double FOOTPRINT::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const } } - coveredRegion.BooleanIntersection( footprintRegion, SHAPE_POLY_SET::PM_FAST ); + coveredRegion.BooleanIntersection( footprintRegion ); double footprintRegionArea = polygonArea( footprintRegion ); double uncoveredRegionArea = footprintRegionArea - polygonArea( coveredRegion ); @@ -3327,7 +3327,7 @@ void FOOTPRINT::CheckNetTies( const std::function<void( const BOARD_ITEM* aItem, } } - copperOutlines.Simplify( SHAPE_POLY_SET::PM_FAST ); + copperOutlines.Simplify(); // Index each pad to the outline in the set that it is part of. diff --git a/pcbnew/generators/pcb_tuning_pattern.cpp b/pcbnew/generators/pcb_tuning_pattern.cpp index e445bdad6a..27ebe860c1 100644 --- a/pcbnew/generators/pcb_tuning_pattern.cpp +++ b/pcbnew/generators/pcb_tuning_pattern.cpp @@ -1804,7 +1804,7 @@ SHAPE_LINE_CHAIN PCB_TUNING_PATTERN::getOutline() const } SHAPE_POLY_SET merged; - merged.BooleanAdd( chain1, chain2, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + merged.BooleanAdd( chain1, chain2 ); if( merged.OutlineCount() > 0 ) return merged.Outline( 0 ); @@ -1832,7 +1832,7 @@ SHAPE_LINE_CHAIN PCB_TUNING_PATTERN::getOutline() const CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_LOW_DEF, false ); SHAPE_POLY_SET merged; - merged.BooleanAdd( poly, polyCoupled, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + merged.BooleanAdd( poly, polyCoupled ); if( merged.OutlineCount() > 0 ) return merged.Outline( 0 ); diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp index 4c96065e4e..04b7b871cd 100644 --- a/pcbnew/pad.cpp +++ b/pcbnew/pad.cpp @@ -1956,7 +1956,7 @@ void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, aClearance += aMaxError; outline.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aMaxError ); - outline.Fracture( SHAPE_POLY_SET::PM_FAST ); + outline.Fracture(); } else if( aClearance < 0 ) { @@ -1965,7 +1965,7 @@ void PAD::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, // aClearance is negative so this is actually a deflate outline.Inflate( aClearance, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS, aMaxError ); - outline.Fracture( SHAPE_POLY_SET::PM_FAST ); + outline.Fracture(); } aBuffer.Append( outline ); @@ -2008,7 +2008,7 @@ std::vector<PCB_SHAPE*> PAD::Recombine( bool aIsDryRun, int maxError ) SHAPE_POLY_SET drawPoly; shape->TransformShapeToPolygon( drawPoly, aLayer, 0, maxError, ERROR_INSIDE ); - drawPoly.BooleanIntersection( padPoly, SHAPE_POLY_SET::PM_FAST ); + drawPoly.BooleanIntersection( padPoly ); if( !drawPoly.IsEmpty() ) return shape; @@ -2275,7 +2275,7 @@ void PAD::doCheckPad( PCB_LAYER_ID aLayer, UNITS_PROVIDER* aUnitsProvider, hole->GetWidth(), ARC_HIGH_DEF, ERROR_OUTSIDE ); SHAPE_POLY_SET copper = padOutline; - copper.BooleanSubtract( holeOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + copper.BooleanSubtract( holeOutline ); if( copper.IsEmpty() ) { @@ -2284,7 +2284,7 @@ void PAD::doCheckPad( PCB_LAYER_ID aLayer, UNITS_PROVIDER* aUnitsProvider, else { // Test if the pad hole is fully inside the copper area - holeOutline.BooleanSubtract( padOutline, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + holeOutline.BooleanSubtract( padOutline ); if( !holeOutline.IsEmpty() ) aErrorHandler( DRCE_PADSTACK, _( "(PTH pad hole non fully inside copper)" ) ); diff --git a/pcbnew/pad_custom_shape_functions.cpp b/pcbnew/pad_custom_shape_functions.cpp index 7b770fe008..68d6814e4b 100644 --- a/pcbnew/pad_custom_shape_functions.cpp +++ b/pcbnew/pad_custom_shape_functions.cpp @@ -45,7 +45,7 @@ void PAD::AddPrimitivePoly( PCB_LAYER_ID aLayer, const SHAPE_POLY_SET& aPoly, in poly_no_hole.Append( aPoly ); if( poly_no_hole.HasHoles() ) - poly_no_hole.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + poly_no_hole.Fracture(); // There should never be multiple shapes, but if there are, we split them into // primitives so that we can edit them both. @@ -142,13 +142,13 @@ void PAD::addPadPrimitivesToPolygon( PCB_LAYER_ID aLayer, SHAPE_POLY_SET* aMerge primitive->TransformShapeToPolygon( polyset, UNDEFINED_LAYER, 0, aError, aErrorLoc ); } - polyset.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyset.Simplify(); // Merge all polygons with the initial pad anchor shape if( polyset.OutlineCount() ) { - aMergedPolygon->BooleanAdd( polyset, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); - aMergedPolygon->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + aMergedPolygon->BooleanAdd( polyset ); + aMergedPolygon->Fracture(); } } diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp index 214d4990ce..13480a4343 100644 --- a/pcbnew/pcb_base_frame.cpp +++ b/pcbnew/pcb_base_frame.cpp @@ -320,7 +320,7 @@ void PCB_BASE_FRAME::FocusOnItems( std::vector<BOARD_ITEM*> aItems, PCB_LAYER_ID try { - viewportPoly.BooleanSubtract( dialogPoly, SHAPE_POLY_SET::PM_FAST ); + viewportPoly.BooleanSubtract( dialogPoly ); } catch( const std::exception& e ) { @@ -426,7 +426,7 @@ void PCB_BASE_FRAME::FocusOnItems( std::vector<BOARD_ITEM*> aItems, PCB_LAYER_ID try { - clippedPoly.BooleanIntersection( itemPoly, viewportPoly, SHAPE_POLY_SET::PM_FAST ); + clippedPoly.BooleanIntersection( itemPoly, viewportPoly ); } catch( const std::exception& e ) { diff --git a/pcbnew/pcb_io/altium/altium_pcb.cpp b/pcbnew/pcb_io/altium/altium_pcb.cpp index f4d106a4f3..09773fe58d 100644 --- a/pcbnew/pcb_io/altium/altium_pcb.cpp +++ b/pcbnew/pcb_io/altium/altium_pcb.cpp @@ -598,7 +598,7 @@ void ALTIUM_PCB::Parse( const ALTIUM_PCB_COMPOUND_FILE& altiumP if( !zone->HasFilledPolysForLayer( layer ) ) continue; - zone->GetFilledPolysList( layer )->Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + zone->GetFilledPolysList( layer )->Fracture(); } } @@ -2824,9 +2824,9 @@ void ALTIUM_PCB::ParseRegions6Data( const ALTIUM_PCB_COMPOUND_FILE& aAltiumP } if( zone->HasFilledPolysForLayer( klayer ) ) - fill.BooleanAdd( *zone->GetFill( klayer ), SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fill.BooleanAdd( *zone->GetFill( klayer ) ); - fill.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fill.Fracture(); zone->SetFilledPolysList( klayer, fill ); zone->SetIsFilled( true ); diff --git a/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp index c1e618f079..99adba6a47 100644 --- a/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp +++ b/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp @@ -2173,7 +2173,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers() } poly.ClearArcs(); - fill.BooleanAdd( poly, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fill.BooleanAdd( poly ); } } @@ -2186,11 +2186,10 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers() if( pouredZone->HasFilledPolysForLayer( getKiCadLayer( csCopper.LayerID ) ) ) { - fill.BooleanAdd( *pouredZone->GetFill( getKiCadLayer( csCopper.LayerID ) ), - SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fill.BooleanAdd( *pouredZone->GetFill( getKiCadLayer( csCopper.LayerID ) ) ); } - fill.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fill.Fracture(); pouredZone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fill ); pouredZone->SetIsFilled( true ); @@ -2270,7 +2269,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers() zone->SetAssignedPriority( m_zonesMap.size() + 1 ); // Highest priority (always fill first) SHAPE_POLY_SET fill( *zone->Outline() ); - fill.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE ); + fill.Fracture(); zone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fill ); } @@ -2822,7 +2821,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarShape( const SHAPE& aCadstarShape, aScalingFactor, aTransformCentre, aMirrorInvert ); - shapePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE ); + shapePolys.Fracture(); shape->SetPolyShape( shapePolys ); shape->SetStroke( STROKE_PARAMS( aLineThickness, LINE_STYLE::SOLID ) ); @@ -3770,9 +3769,9 @@ bool CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities( PCB_LAYER_ID& aLayer ) SHAPE_POLY_SET lowerZoneFill( *aLowerZone->GetFilledPolysList( aLayer ) ); SHAPE_POLY_SET lowerZoneOutline( *aLowerZone->Outline() ); - lowerZoneOutline.BooleanSubtract( intersectShape, SHAPE_POLY_SET::PM_FAST ); + lowerZoneOutline.BooleanSubtract( intersectShape ); - lowerZoneFill.BooleanSubtract( lowerZoneOutline, SHAPE_POLY_SET::PM_FAST ); + lowerZoneFill.BooleanSubtract( lowerZoneOutline ); double leftOverArea = lowerZoneFill.Area(); @@ -3790,7 +3789,7 @@ bool CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities( PCB_LAYER_ID& aLayer ) outLineB.Inflate( inflateValue( aZoneA, aZoneB ), CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_HIGH_DEF ); - outLineA.BooleanIntersection( outLineB, SHAPE_POLY_SET::PM_FAST ); + outLineA.BooleanIntersection( outLineB ); return outLineA.Area(); }; diff --git a/pcbnew/pcb_io/easyeda/pcb_io_easyeda_parser.cpp b/pcbnew/pcb_io/easyeda/pcb_io_easyeda_parser.cpp index ed8730fc26..971e138b54 100644 --- a/pcbnew/pcb_io/easyeda/pcb_io_easyeda_parser.cpp +++ b/pcbnew/pcb_io/easyeda/pcb_io_easyeda_parser.cpp @@ -572,7 +572,7 @@ void PCB_IO_EASYEDA_PARSER::ParseToBoardItemContainer( } } - fillPolySet.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fillPolySet.Fracture(); zone->SetFilledPolysList( layer, fillPolySet ); zone->SetIsFilled( true ); diff --git a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp index 7c2e4c536a..68dc1928e5 100644 --- a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp +++ b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp @@ -880,7 +880,7 @@ FOOTPRINT* PCB_IO_EASYEDAPRO_PARSER::ParseFootprint( const nlohmann::json& true ); } - polySet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + polySet.Simplify(); std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( footprint ); @@ -1158,7 +1158,7 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard( zoneFillPoly.AddOutline( contour ); zoneFillPoly.RebuildHolesFromContours(); - zoneFillPoly.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + zoneFillPoly.Fracture(); std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard ); @@ -1771,7 +1771,7 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard( // The contour can be self-intersecting SHAPE_POLY_SET simple( contour ); - simple.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + simple.Simplify(); if( dataId == 0 ) { @@ -1779,7 +1779,7 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard( } else { - thisPoly.BooleanSubtract( simple, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + thisPoly.BooleanSubtract( simple ); } } else @@ -1801,16 +1801,16 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard( if( !fillPolySet.IsEmpty() ) { - fillPolySet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fillPolySet.Simplify(); const int strokeWidth = pcbIUScale.MilsToIU( 8 ); // Seems to be 8 mils fillPolySet.Inflate( strokeWidth / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_HIGH_DEF, false ); - fillPolySet.BooleanAdd( thermalSpokes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fillPolySet.BooleanAdd( thermalSpokes ); - fillPolySet.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + fillPolySet.Fracture(); zone->SetFilledPolysList( zone->GetFirstLayer(), fillPolySet ); zone->SetNeedRefill( false ); diff --git a/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp b/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp index 5b7f453917..c7f8068f51 100644 --- a/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp +++ b/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp @@ -2724,7 +2724,7 @@ bool FABMASTER::loadFootprints( BOARD* aBoard ) } else { - poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + poly_outline.Fracture(); poly_outline.Move( -newpad->GetPosition() ); @@ -3324,7 +3324,7 @@ bool FABMASTER::loadPolygon( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRA SHAPE_POLY_SET poly_outline = loadShapePolySet( aLine->segment ); - poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + poly_outline.Fracture(); if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 ) return false; @@ -3552,7 +3552,7 @@ bool FABMASTER::loadGraphics( BOARD* aBoard ) { SHAPE_POLY_SET poly_outline = loadShapePolySet( *( geom.elements ) ); - poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + poly_outline.Fracture(); if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 ) continue; diff --git a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp index 5b23395466..4aeadf7570 100644 --- a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp +++ b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp @@ -606,7 +606,7 @@ void PCB_IO_IPC2581::addKnockoutText( wxXmlNode* aContentNode, PCB_TEXT* aText ) SHAPE_POLY_SET finalPoly; aText->TransformTextToPolySet( finalPoly, 0, ARC_HIGH_DEF, ERROR_INSIDE ); - finalPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); + finalPoly.Fracture(); for( int ii = 0; ii < finalPoly.OutlineCount(); ++ii ) addContourNode( aContentNode, finalPoly, ii ); @@ -889,7 +889,7 @@ void PCB_IO_IPC2581::addShape( wxXmlNode* aContentNode, const PAD& aPad, PCB_LAY if( expansion.x ) { outline.InflateWithLinkedHoles( expansion.x, CORNER_STRATEGY::ROUND_ALL_CORNERS, - maxError, SHAPE_POLY_SET::PM_FAST ); + maxError ); } addContourNode( entry_node, outline ); @@ -910,8 +910,7 @@ void PCB_IO_IPC2581::addShape( wxXmlNode* aContentNode, const PAD& aPad, PCB_LAY if( expansion != VECTOR2I( 0, 0 ) ) { shape.InflateWithLinkedHoles( std::max( expansion.x, expansion.y ), - CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError, - SHAPE_POLY_SET::PM_FAST ); + CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError ); } addContourNode( entry_node, shape ); diff --git a/pcbnew/pcb_io/kicad_legacy/pcb_io_kicad_legacy.cpp b/pcbnew/pcb_io/kicad_legacy/pcb_io_kicad_legacy.cpp index c60978949c..b8555012c7 100644 --- a/pcbnew/pcb_io/kicad_legacy/pcb_io_kicad_legacy.cpp +++ b/pcbnew/pcb_io/kicad_legacy/pcb_io_kicad_legacy.cpp @@ -2627,8 +2627,7 @@ void PCB_IO_KICAD_LEGACY::loadZONE_CONTAINER() inflatedFill.InflateWithLinkedHoles( zc->GetMinThickness() / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, - ARC_HIGH_DEF / 2, - SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + ARC_HIGH_DEF / 2 ); zc->SetFilledPolysList( layer, inflatedFill ); } diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp index 883f99b8bd..39b73b75f5 100644 --- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp +++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr_parser.cpp @@ -7231,8 +7231,7 @@ ZONE* PCB_IO_KICAD_SEXPR_PARSER::parseZONE( BOARD_ITEM_CONTAINER* aParent ) { polyset.InflateWithLinkedHoles( zone->GetMinThickness() / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, - ARC_HIGH_DEF / 2, - SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + ARC_HIGH_DEF / 2 ); } } } @@ -7271,7 +7270,7 @@ ZONE* PCB_IO_KICAD_SEXPR_PARSER::parseZONE( BOARD_ITEM_CONTAINER* aParent ) TransformOvalToPolygon( segPolygon, seg.A, seg.B, zone->GetMinThickness(), ARC_HIGH_DEF, ERROR_OUTSIDE ); - layerFill.BooleanAdd( segPolygon, SHAPE_POLY_SET::PM_FAST ); + layerFill.BooleanAdd( segPolygon ); } diff --git a/pcbnew/pcb_io/odbpp/odb_feature.cpp b/pcbnew/pcb_io/odbpp/odb_feature.cpp index 24f76f32e2..df4f639b80 100644 --- a/pcbnew/pcb_io/odbpp/odb_feature.cpp +++ b/pcbnew/pcb_io/odbpp/odb_feature.cpp @@ -141,7 +141,7 @@ void FEATURES_MANAGER::AddShape( const PCB_SHAPE& aShape, PCB_LAYER_ID aLayer ) if( soldermask_min_thickness == 0 ) { poly_set = aShape.GetPolyShape().CloneDropTriangulation(); - poly_set.Fracture( SHAPE_POLY_SET::PM_FAST ); + poly_set.Fracture(); } else { @@ -152,11 +152,11 @@ void FEATURES_MANAGER::AddShape( const PCB_SHAPE& aShape, PCB_LAYER_ID aLayer ) aShape.TransformShapeToPolygon( poly_set, aLayer, soldermask_min_thickness / 2 - 1, maxError, ERROR_OUTSIDE ); - poly_set.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + poly_set.Simplify(); poly_set.Deflate( soldermask_min_thickness / 2 - 1, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError ); - poly_set.BooleanAdd( initialPolys, SHAPE_POLY_SET::PM_FAST ); - poly_set.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + poly_set.BooleanAdd( initialPolys ); + poly_set.Fracture(); } int strokeWidth = aShape.GetStroke().GetWidth(); @@ -338,7 +338,7 @@ void FEATURES_MANAGER::AddPadShape( const PAD& aPad, PCB_LAYER_ID aLayer ) if( mask_clearance ) { outline.InflateWithLinkedHoles( expansion.x, CORNER_STRATEGY::ROUND_ALL_CORNERS, - maxError, SHAPE_POLY_SET::PM_FAST ); + maxError ); } for( int ii = 0; ii < outline.OutlineCount(); ++ii ) @@ -360,8 +360,7 @@ void FEATURES_MANAGER::AddPadShape( const PAD& aPad, PCB_LAYER_ID aLayer ) if( expansion != VECTOR2I( 0, 0 ) ) { shape.InflateWithLinkedHoles( std::max( expansion.x, expansion.y ), - CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError, - SHAPE_POLY_SET::PM_FAST ); + CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError ); } for( int ii = 0; ii < shape.OutlineCount(); ++ii ) @@ -644,7 +643,7 @@ void FEATURES_MANAGER::InitFeatureList( PCB_LAYER_ID aLayer, std::vector<BOARD_I text->TransformTextToPolySet( finalpolyset, 0, m_board->GetDesignSettings().m_MaxError, ERROR_INSIDE ); - finalpolyset.Fracture( SHAPE_POLY_SET::PM_FAST ); + finalpolyset.Fracture(); for( int ii = 0; ii < finalpolyset.OutlineCount(); ++ii ) { diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp index 43e2ccb410..9255793f26 100644 --- a/pcbnew/pcb_painter.cpp +++ b/pcbnew/pcb_painter.cpp @@ -2238,7 +2238,7 @@ void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer ) { SHAPE_POLY_SET finalPoly; aText->TransformTextToPolySet( finalPoly, 0, m_maxError, ERROR_INSIDE ); - finalPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); + finalPoly.Fracture(); m_gal->SetIsStroke( false ); m_gal->SetIsFill( true ); diff --git a/pcbnew/pcb_text.cpp b/pcbnew/pcb_text.cpp index b2005b00db..b038c00353 100644 --- a/pcbnew/pcb_text.cpp +++ b/pcbnew/pcb_text.cpp @@ -538,7 +538,7 @@ void PCB_TEXT::TransformTextToPolySet( SHAPE_POLY_SET& aBuffer, int aClearance, } ); font->Draw( &callback_gal, GetShownText( true ), GetTextPos(), attrs, GetFontMetrics() ); - textShape.Simplify( SHAPE_POLY_SET::PM_FAST ); + textShape.Simplify(); if( IsKnockout() ) { @@ -546,7 +546,7 @@ void PCB_TEXT::TransformTextToPolySet( SHAPE_POLY_SET& aBuffer, int aClearance, int margin = GetKnockoutTextMargin( attrs.m_Size, penWidth ); buildBoundingHull( &finalPoly, textShape, margin + aClearance ); - finalPoly.BooleanSubtract( textShape, SHAPE_POLY_SET::PM_FAST ); + finalPoly.BooleanSubtract( textShape ); aBuffer.Append( finalPoly ); } diff --git a/pcbnew/pcb_textbox.cpp b/pcbnew/pcb_textbox.cpp index 3feb0ca9e8..29e199940a 100644 --- a/pcbnew/pcb_textbox.cpp +++ b/pcbnew/pcb_textbox.cpp @@ -665,7 +665,7 @@ void PCB_TEXTBOX::TransformTextToPolySet( SHAPE_POLY_SET& aBuffer, int aClearanc } else { - buffer.Simplify( SHAPE_POLY_SET::PM_FAST ); + buffer.Simplify(); } aBuffer.Append( buffer ); diff --git a/pcbnew/pcbexpr_functions.cpp b/pcbnew/pcbexpr_functions.cpp index 582ca53580..8a946702dc 100644 --- a/pcbnew/pcbexpr_functions.cpp +++ b/pcbnew/pcbexpr_functions.cpp @@ -860,8 +860,7 @@ static void enclosedByAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self ) } else { - itemShape.BooleanSubtract( *aArea->Outline(), - SHAPE_POLY_SET::PM_FAST ); + itemShape.BooleanSubtract( *aArea->Outline() ); enclosedByArea = itemShape.IsEmpty(); } diff --git a/pcbnew/plot_board_layers.cpp b/pcbnew/plot_board_layers.cpp index fbedde61d7..100e503869 100644 --- a/pcbnew/plot_board_layers.cpp +++ b/pcbnew/plot_board_layers.cpp @@ -467,8 +467,7 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate() // which can create bad shapes if margin.x is < 0 outline.InflateWithLinkedHoles( mask_clearance, - CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError, - SHAPE_POLY_SET::PM_FAST ); + CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError ); dummy.DeletePrimitivesList(); dummy.AddPrimitivePoly( aLayer, outline, 0, true ); @@ -518,8 +517,7 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, dummy.TransformShapeToPolygon( outline, UNDEFINED_LAYER, 0, maxError, ERROR_INSIDE ); outline.InflateWithLinkedHoles( mask_clearance, - CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError, - SHAPE_POLY_SET::PM_FAST ); + CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError ); // Initialize the dummy pad shape: dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE ); @@ -554,8 +552,7 @@ void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate() // which can create bad shapes if margin.x is < 0 shape.InflateWithLinkedHoles( mask_clearance, - CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError, - SHAPE_POLY_SET::PM_FAST ); + CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError ); dummy.DeletePrimitivesList(); dummy.AddPrimitivePoly( aLayer, shape, 0, true ); @@ -792,7 +789,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask, outlines.RemoveAllContours(); aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines ); - outlines.Simplify( SHAPE_POLY_SET::PM_FAST ); + outlines.Simplify(); // Plot outlines std::vector<VECTOR2I> cornerList; @@ -1046,7 +1043,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, // Merge all polygons: After deflating, not merged (not overlapping) polygons will have the // initial shape (with perhaps small changes due to deflating transform) - areas.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + areas.Simplify(); areas.Deflate( inflate, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError ); // To avoid a lot of code, use a ZONE to handle and plot polygons, because our polygons look @@ -1060,8 +1057,8 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask, // Combine the current areas to initial areas. This is mandatory because inflate/deflate // transform is not perfect, and we want the initial areas perfectly kept - areas.BooleanAdd( initialPolys, SHAPE_POLY_SET::PM_FAST ); - areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + areas.BooleanAdd( initialPolys ); + areas.Fracture(); itemplotter.PlotZone( &zone, layer, areas ); } diff --git a/pcbnew/plot_brditems_plotter.cpp b/pcbnew/plot_brditems_plotter.cpp index af3edf8253..1b08ba02ab 100644 --- a/pcbnew/plot_brditems_plotter.cpp +++ b/pcbnew/plot_brditems_plotter.cpp @@ -733,7 +733,7 @@ void BRDITEMS_PLOTTER::PlotText( const EDA_TEXT* aText, PCB_LAYER_ID aLayer, boo text->TransformTextToPolySet( finalPoly, 0, m_board->GetDesignSettings().m_MaxError, ERROR_INSIDE ); - finalPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); + finalPoly.Fracture(); for( int ii = 0; ii < finalPoly.OutlineCount(); ++ii ) m_plotter->PlotPoly( finalPoly.Outline( ii ), FILL_T::FILLED_SHAPE, 0, &gbr_metadata ); @@ -966,7 +966,7 @@ void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape ) // This must be simplified and fractured to prevent overlapping polygons // from generating invalid Gerber files SHAPE_POLY_SET tmpPoly = aShape->GetPolyShape().CloneDropTriangulation(); - tmpPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); + tmpPoly.Fracture(); if( margin < 0 ) { diff --git a/pcbnew/router/pns_hole.cpp b/pcbnew/router/pns_hole.cpp index 7e927fe8d8..7fbb857da5 100644 --- a/pcbnew/router/pns_hole.cpp +++ b/pcbnew/router/pns_hole.cpp @@ -89,7 +89,7 @@ const SHAPE_LINE_CHAIN HOLE::Hull( int aClearance, int aWalkaroundThickness, int BuildHullForPrimitiveShape( shape, aClearance, aWalkaroundThickness ) ); } - hullSet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + hullSet.Simplify(); return hullSet.Outline( 0 ); } } diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h index 615079e70e..51b197752d 100644 --- a/pcbnew/router/pns_item.h +++ b/pcbnew/router/pns_item.h @@ -24,6 +24,7 @@ #define __PNS_ITEM_H #include <memory> +#include <set> #include <unordered_set> #include <math/vector2d.h> diff --git a/pcbnew/router/pns_solid.cpp b/pcbnew/router/pns_solid.cpp index e9fba10479..09f9481e0b 100644 --- a/pcbnew/router/pns_solid.cpp +++ b/pcbnew/router/pns_solid.cpp @@ -60,7 +60,7 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in aWalkaroundThickness ) ); } - hullSet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + hullSet.Simplify(); return hullSet.Outline( 0 ); } } diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp index 094d8d96f8..cc4b6bffa7 100644 --- a/pcbnew/specctra_import_export/specctra_export.cpp +++ b/pcbnew/specctra_import_export/specctra_export.cpp @@ -843,7 +843,7 @@ IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, FOOTPRINT* aFootprint ) TransformCircleToPolygon( polyBuffer, graphic->GetEnd() - move, graphic->GetWidth() / 2, ARC_HIGH_DEF, ERROR_INSIDE ); - polyBuffer.Simplify( SHAPE_POLY_SET::PM_FAST ); + polyBuffer.Simplify(); SHAPE_LINE_CHAIN& poly = polyBuffer.Outline( 0 ); for( int ii = 0; ii < poly.PointCount(); ++ii ) diff --git a/pcbnew/teardrop/teardrop_utils.cpp b/pcbnew/teardrop/teardrop_utils.cpp index 9625103300..9cceceefbe 100644 --- a/pcbnew/teardrop/teardrop_utils.cpp +++ b/pcbnew/teardrop/teardrop_utils.cpp @@ -388,7 +388,7 @@ bool TEARDROP_MANAGER::computeAnchorPoints( const TEARDROP_PARAMETERS& aParams, clipping_rect.Move( ref_on_track ); // Clip the shape to the max allowed teadrop area - c_buffer.BooleanIntersection( clipping_rect, SHAPE_POLY_SET::PM_FAST ); + c_buffer.BooleanIntersection( clipping_rect ); } /* in aPts: diff --git a/pcbnew/tools/board_editor_control.cpp b/pcbnew/tools/board_editor_control.cpp index 08f4f41e1e..9ed804f0b4 100644 --- a/pcbnew/tools/board_editor_control.cpp +++ b/pcbnew/tools/board_editor_control.cpp @@ -1320,11 +1320,10 @@ static bool mergeZones( EDA_DRAW_FRAME* aFrame, BOARD_COMMIT& aCommit, for( unsigned int i = 1; i < aOriginZones.size(); i++ ) { - aOriginZones[0]->Outline()->BooleanAdd( *aOriginZones[i]->Outline(), - SHAPE_POLY_SET::PM_FAST ); + aOriginZones[0]->Outline()->BooleanAdd( *aOriginZones[i]->Outline() ); } - aOriginZones[0]->Outline()->Simplify( SHAPE_POLY_SET::PM_FAST ); + aOriginZones[0]->Outline()->Simplify(); // We should have one polygon, possibly with holes. If we end up with two polygons (either // because the intersection was a single point or because the intersection was within one of diff --git a/pcbnew/tools/convert_tool.cpp b/pcbnew/tools/convert_tool.cpp index 9d1a67d143..fc7d4c90a2 100644 --- a/pcbnew/tools/convert_tool.cpp +++ b/pcbnew/tools/convert_tool.cpp @@ -373,7 +373,7 @@ int CONVERT_TOOL::CreatePolys( const TOOL_EVENT& aEvent ) polySet.Append( makePolysFromOpenGraphics( selection.GetItems(), 0 ) ); polySet.ClearArcs(); - polySet.Simplify( SHAPE_POLY_SET::PM_FAST ); + polySet.Simplify(); // Now inflate the bounding hull by cfg.m_Gap polySet.Inflate( cfg.m_Gap, CORNER_STRATEGY::ROUND_ALL_CORNERS, bds.m_MaxError, diff --git a/pcbnew/tools/item_modification_routine.cpp b/pcbnew/tools/item_modification_routine.cpp index 967352ad07..38248d7dd1 100644 --- a/pcbnew/tools/item_modification_routine.cpp +++ b/pcbnew/tools/item_modification_routine.cpp @@ -571,8 +571,7 @@ std::optional<wxString> POLYGON_MERGE_ROUTINE::GetStatusMessage() const bool POLYGON_MERGE_ROUTINE::ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) { - const SHAPE_POLY_SET::POLYGON_MODE poly_mode = SHAPE_POLY_SET::POLYGON_MODE::PM_FAST; - GetWorkingPolygons().BooleanAdd( aPolygon, poly_mode ); + GetWorkingPolygons().BooleanAdd( aPolygon ); return true; } @@ -599,11 +598,9 @@ std::optional<wxString> POLYGON_SUBTRACT_ROUTINE::GetStatusMessage() const bool POLYGON_SUBTRACT_ROUTINE::ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) { - const SHAPE_POLY_SET::POLYGON_MODE poly_mode = SHAPE_POLY_SET::POLYGON_MODE::PM_FAST; - SHAPE_POLY_SET& working_polygons = GetWorkingPolygons(); SHAPE_POLY_SET working_copy = working_polygons; - working_copy.BooleanSubtract( aPolygon, poly_mode ); + working_copy.BooleanSubtract( aPolygon ); // Subtraction can create holes or delete the polygon // In theory we can allow holes as the EDA_SHAPE will fracture for us, but that's @@ -642,11 +639,9 @@ std::optional<wxString> POLYGON_INTERSECT_ROUTINE::GetStatusMessage() const bool POLYGON_INTERSECT_ROUTINE::ProcessSubsequentPolygon( const SHAPE_POLY_SET& aPolygon ) { - const SHAPE_POLY_SET::POLYGON_MODE poly_mode = SHAPE_POLY_SET::POLYGON_MODE::PM_FAST; - SHAPE_POLY_SET& working_polygons = GetWorkingPolygons(); SHAPE_POLY_SET working_copy = working_polygons; - working_copy.BooleanIntersection( aPolygon, poly_mode ); + working_copy.BooleanIntersection( aPolygon ); // Is there anything left? if( working_copy.OutlineCount() == 0 ) diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index 4161b6b554..d3a915e085 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -36,7 +36,6 @@ using namespace std::placeholders; #include <board.h> #include <board_design_settings.h> #include <board_item.h> -#include <clipper.hpp> #include <pcb_reference_image.h> #include <pcb_track.h> #include <footprint.h> diff --git a/pcbnew/tools/zone_create_helper.cpp b/pcbnew/tools/zone_create_helper.cpp index 7f08e11314..6a71f0d853 100644 --- a/pcbnew/tools/zone_create_helper.cpp +++ b/pcbnew/tools/zone_create_helper.cpp @@ -171,7 +171,7 @@ void ZONE_CREATE_HELPER::performZoneCutout( ZONE& aZone, const ZONE& aCutout ) toolMgr->RunAction( PCB_ACTIONS::selectionClear ); SHAPE_POLY_SET originalOutline( *aZone.Outline() ); - originalOutline.BooleanSubtract( *aCutout.Outline(), SHAPE_POLY_SET::PM_FAST ); + originalOutline.BooleanSubtract( *aCutout.Outline() ); // After substracting the hole, originalOutline can have more than one main outline. // But a zone can have only one main outline, so create as many zones as originalOutline diff --git a/pcbnew/tracks_cleaner.cpp b/pcbnew/tracks_cleaner.cpp index fd818565a8..b8c0e8d1d7 100644 --- a/pcbnew/tracks_cleaner.cpp +++ b/pcbnew/tracks_cleaner.cpp @@ -344,8 +344,7 @@ void TRACKS_CLEANER::deleteTracksInPads() track->TransformShapeToPolygon( poly, track->GetLayer(), 0, ARC_HIGH_DEF, ERROR_INSIDE ); - poly.BooleanSubtract( *pad->GetEffectivePolygon( track->GetLayer(), ERROR_INSIDE ), - SHAPE_POLY_SET::PM_FAST ); + poly.BooleanSubtract( *pad->GetEffectivePolygon( track->GetLayer(), ERROR_INSIDE ) ); if( poly.IsEmpty() ) { diff --git a/pcbnew/zone.cpp b/pcbnew/zone.cpp index 9f0c2c3b77..ac967a8e6d 100644 --- a/pcbnew/zone.cpp +++ b/pcbnew/zone.cpp @@ -991,7 +991,7 @@ void ZONE::RemoveCutout( int aOutlineIdx, int aHoleIdx ) SHAPE_POLY_SET cutPoly( m_Poly->Hole( aOutlineIdx, aHoleIdx ) ); // Add the cutout back to the zone - m_Poly->BooleanAdd( cutPoly, SHAPE_POLY_SET::PM_FAST ); + m_Poly->BooleanAdd( cutPoly ); SetNeedRefill( true ); } @@ -1434,7 +1434,7 @@ bool ZONE::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer { withFillets = flattened; smooth( withFillets ); - withFillets.BooleanAdd( flattened, SHAPE_POLY_SET::PM_FAST ); + withFillets.BooleanAdd( flattened ); maxExtents = &withFillets; } @@ -1466,7 +1466,7 @@ bool ZONE::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer if( diffNetZone->HigherPriority( sameNetZone ) && diffNetZone->GetBoundingBox().Intersects( sameNetBoundingBox ) ) { - diffNetPoly.BooleanAdd( *diffNetZone->Outline(), SHAPE_POLY_SET::PM_FAST ); + diffNetPoly.BooleanAdd( *diffNetZone->Outline() ); } } @@ -1479,19 +1479,19 @@ bool ZONE::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer { SHAPE_POLY_SET thisPoly = Outline()->CloneDropTriangulation(); - thisPoly.BooleanSubtract( diffNetPoly, SHAPE_POLY_SET::PM_FAST ); + thisPoly.BooleanSubtract( diffNetPoly ); isolated = thisPoly.OutlineCount() == 0; } if( !isolated ) { sameNetPoly.ClearArcs(); - aSmoothedPoly.BooleanAdd( sameNetPoly, SHAPE_POLY_SET::PM_FAST ); + aSmoothedPoly.BooleanAdd( sameNetPoly ); } } if( aBoardOutline ) - aSmoothedPoly.BooleanIntersection( *aBoardOutline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + aSmoothedPoly.BooleanIntersection( *aBoardOutline ); SHAPE_POLY_SET withSameNetIntersectingZones = aSmoothedPoly.CloneDropTriangulation(); @@ -1508,13 +1508,13 @@ bool ZONE::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer poly.Inflate( m_ZoneMinThickness, CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError ); if( !keepExternalFillets ) - poly.BooleanIntersection( withSameNetIntersectingZones, SHAPE_POLY_SET::PM_FAST ); + poly.BooleanIntersection( withSameNetIntersectingZones ); *aSmoothedPolyWithApron = aSmoothedPoly; - aSmoothedPolyWithApron->BooleanIntersection( poly, SHAPE_POLY_SET::PM_FAST ); + aSmoothedPolyWithApron->BooleanIntersection( poly ); } - aSmoothedPoly.BooleanIntersection( *maxExtents, SHAPE_POLY_SET::PM_FAST ); + aSmoothedPoly.BooleanIntersection( *maxExtents ); return true; } @@ -1576,7 +1576,7 @@ void ZONE::TransformSmoothedOutlineToPolygon( SHAPE_POLY_SET& aBuffer, int aClea polybuffer.Inflate( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, maxError ); } - polybuffer.Fracture( SHAPE_POLY_SET::PM_FAST ); + polybuffer.Fracture(); aBuffer.Append( polybuffer ); } @@ -1612,8 +1612,7 @@ void ZONE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer if( aErrorLoc == ERROR_OUTSIDE ) aClearance += aError; - temp_buf.InflateWithLinkedHoles( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError, - SHAPE_POLY_SET::PM_FAST ); + temp_buf.InflateWithLinkedHoles( aClearance, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError ); } aBuffer.Append( temp_buf ); diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index c90dd8066a..0c75226799 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -796,8 +796,7 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE*>& aZones, bool aCheck, wxWindow* island.AddOutline( test_poly ); - intersection.BooleanIntersection( m_boardOutline, island, - SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + intersection.BooleanIntersection( m_boardOutline, island ); // Nominally, all of these areas should be either inside or outside the // board outline. So this test should be able to just compare areas (if @@ -1118,7 +1117,7 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer } } - aFill.BooleanSubtract( holes, SHAPE_POLY_SET::PM_FAST ); + aFill.BooleanSubtract( holes ); } @@ -1505,7 +1504,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa } } - aHoles.Simplify( SHAPE_POLY_SET::PM_FAST ); + aHoles.Simplify(); } @@ -1533,7 +1532,7 @@ void ZONE_FILLER::subtractHigherPriorityZones( const ZONE* aZone, PCB_LAYER_ID a SHAPE_POLY_SET outline = aKnockout->Outline()->CloneDropTriangulation(); outline.ClearArcs(); - aRawFill.BooleanSubtract( outline, SHAPE_POLY_SET::PM_FAST ); + aRawFill.BooleanSubtract( outline ); } }; @@ -1617,7 +1616,7 @@ void ZONE_FILLER::connect_nearby_polys( SHAPE_POLY_SET& aPolys, double aDistance { \ m_board->SetLayerName( b, c ); \ SHAPE_POLY_SET d = a; \ - d.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); \ + d.Fracture(); \ aFillPolys = d; \ return false; \ } \ @@ -1699,7 +1698,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, PCB_LA // because the "real" subtract-clearance-holes has to be done after the spokes are added. static const bool USE_BBOX_CACHES = true; SHAPE_POLY_SET testAreas = aFillPolys.CloneDropTriangulation(); - testAreas.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST ); + testAreas.BooleanSubtract( clearanceHoles ); DUMP_POLYS_TO_COPPER_LAYER( testAreas, In4_Cu, wxT( "minus-clearance-holes" ) ); // Prune features that don't meet minimum-width criteria @@ -1767,7 +1766,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, PCB_LA if( m_progressReporter && m_progressReporter->IsCancelled() ) return false; - aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST ); + aFillPolys.BooleanSubtract( clearanceHoles ); DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In8_Cu, wxT( "after-spoke-trimming" ) ); /* ------------------------------------------------------------------------------------- @@ -1818,7 +1817,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, PCB_LA * Connect nearby polygons with zero-width lines in order to ensure correct * re-inflation. */ - aFillPolys.Fracture( SHAPE_POLY_SET::PM_FAST ); + aFillPolys.Fracture(); connect_nearby_polys( aFillPolys, aZone->GetMinThickness() ); DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In10_Cu, wxT( "connected-nearby-polys" ) ); @@ -1845,9 +1844,9 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, PCB_LA for( PAD* pad : thermalConnectionPads ) addHoleKnockout( pad, 0, clearanceHoles ); - aFillPolys.BooleanIntersection( aMaxExtents, SHAPE_POLY_SET::PM_FAST ); + aFillPolys.BooleanIntersection( aMaxExtents ); DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In16_Cu, wxT( "after-trim-to-outline" ) ); - aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST ); + aFillPolys.BooleanSubtract( clearanceHoles ); DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In17_Cu, wxT( "after-trim-to-clearance-holes" ) ); /* ------------------------------------------------------------------------------------- @@ -1857,7 +1856,7 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, PCB_LA subtractHigherPriorityZones( aZone, aLayer, aFillPolys ); DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In18_Cu, wxT( "minus-higher-priority-zones" ) ); - aFillPolys.Fracture( SHAPE_POLY_SET::PM_FAST ); + aFillPolys.Fracture(); return true; } @@ -1911,7 +1910,7 @@ bool ZONE_FILLER::fillNonCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, } aFillPolys = aSmoothedOutline; - aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST ); + aFillPolys.BooleanSubtract( clearanceHoles ); for( ZONE* keepout : m_board->Zones() ) { @@ -1927,13 +1926,13 @@ bool ZONE_FILLER::fillNonCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, { if( keepout->Outline()->ArcCount() == 0 ) { - aFillPolys.BooleanSubtract( *keepout->Outline(), SHAPE_POLY_SET::PM_FAST ); + aFillPolys.BooleanSubtract( *keepout->Outline() ); } else { SHAPE_POLY_SET keepoutOutline( *keepout->Outline() ); keepoutOutline.ClearArcs(); - aFillPolys.BooleanSubtract( keepoutOutline, SHAPE_POLY_SET::PM_FAST ); + aFillPolys.BooleanSubtract( keepoutOutline ); } } } @@ -1958,7 +1957,7 @@ bool ZONE_FILLER::fillNonCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, if( half_min_width - epsilon > epsilon ) aFillPolys.Inflate( half_min_width - epsilon, CORNER_STRATEGY::ROUND_ALL_CORNERS, m_maxError ); - aFillPolys.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + aFillPolys.Fracture(); return true; } @@ -2375,12 +2374,12 @@ bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer SHAPE_POLY_SET deflatedFilledPolys = aFillPolys.CloneDropTriangulation(); deflatedFilledPolys.Deflate( outline_margin - aZone->GetMinThickness(), CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError ); - holes.BooleanIntersection( deflatedFilledPolys, SHAPE_POLY_SET::PM_FAST ); + holes.BooleanIntersection( deflatedFilledPolys ); DUMP_POLYS_TO_COPPER_LAYER( holes, In11_Cu, wxT( "fill-clipped-hatch-holes" ) ); SHAPE_POLY_SET deflatedOutline = aZone->Outline()->CloneDropTriangulation(); deflatedOutline.Deflate( outline_margin, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError ); - holes.BooleanIntersection( deflatedOutline, SHAPE_POLY_SET::PM_FAST ); + holes.BooleanIntersection( deflatedOutline ); DUMP_POLYS_TO_COPPER_LAYER( holes, In12_Cu, wxT( "outline-clipped-hatch-holes" ) ); if( aZone->GetNetCode() != 0 ) @@ -2438,7 +2437,7 @@ bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer } } - holes.BooleanSubtract( aprons, SHAPE_POLY_SET::PM_FAST ); + holes.BooleanSubtract( aprons ); } DUMP_POLYS_TO_COPPER_LAYER( holes, In13_Cu, wxT( "pad-via-clipped-hatch-holes" ) ); @@ -2454,9 +2453,9 @@ bool ZONE_FILLER::addHatchFillTypeOnZone( const ZONE* aZone, PCB_LAYER_ID aLayer ++ii; } - // create grid. Use SHAPE_POLY_SET::PM_STRICTLY_SIMPLE to + // create grid. Useto // generate strictly simple polygons needed by Gerber files and Fracture() - aFillPolys.BooleanSubtract( aFillPolys, holes, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + aFillPolys.BooleanSubtract( aFillPolys, holes ); DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In14_Cu, wxT( "after-hatching" ) ); return true; diff --git a/qa/tests/libs/kimath/geometry/test_shape_line_chain.cpp b/qa/tests/libs/kimath/geometry/test_shape_line_chain.cpp index fb0b5dde48..a3dab0d439 100644 --- a/qa/tests/libs/kimath/geometry/test_shape_line_chain.cpp +++ b/qa/tests/libs/kimath/geometry/test_shape_line_chain.cpp @@ -131,21 +131,6 @@ BOOST_AUTO_TEST_CASE( ClipperConstructorCase1 ) // Case of an arc followed by a segment // The clipper path is not in order (on purpose), to simulate the typical return from clipper - ClipperLib::Path pathClipper1 = { - { { 125663951, 120099260, 24 }, { 125388111, 120170850, 25 }, { 125124975, 120280270, 26 }, - { 124879705, 120425376, 27 }, { 124657110, 120603322, 28 }, { 124461556, 120810617, 29 }, - { 124296876, 121043198, 30 }, { 124166301, 121296503, 31 }, { 124072391, 121565564, 32 }, - { 124016988, 121845106, 33 }, { 124001177, 122129646, 34 }, { 124025270, 122413605, 35 }, - { 124088794, 122691414, 36 }, { 124190502, 122957625, 37 }, { 124328401, 123207018, 38 }, - { 124499787, 123434703, 39 }, { 124598846, 123537154, 40 }, { 127171000, 123786000, 4 }, - { 127287862, 123704439, 5 }, { 127499716, 123513831, 6 }, { 127682866, 123295498, 7 }, - { 127833720, 123053722, 8 }, { 127949321, 122793242, 9 }, { 128027402, 122519168, 10 }, - { 128066430, 122236874, 11 }, { 128065642, 121951896, 12 }, { 128025053, 121669823, 13 }, - { 127945457, 121396185, 14 }, { 127828417, 121136349, 15 }, { 127676227, 120895410, 16 }, - { 127491873, 120678094, 17 }, { 127278968, 120488661, 18 }, { 127041689, 120330827, 19 }, - { 126784688, 120207687, 20 }, { 126513005, 120121655, 21 }, { 126231968, 120074419, 22 }, - { 125947087, 120066905, 23 } } - }; Clipper2Lib::Path64 pathClipper2 = { { { 125663951, 120099260, 24 }, { 125388111, 120170850, 25 }, { 125124975, 120280270, 26 }, { 124879705, 120425376, 27 }, { 124657110, 120603322, 28 }, { 124461556, 120810617, 29 }, @@ -180,22 +165,16 @@ BOOST_AUTO_TEST_CASE( ClipperConstructorCase1 ) SHAPE_ARC( { 127171000, 123786000 }, { 126231718, 120077003 }, { 124598846, 123537154 }, 0 ) }; - SHAPE_LINE_CHAIN clipper1chain( pathClipper1, z_values, arcs ); SHAPE_LINE_CHAIN clipper2chain( pathClipper2, z_values, arcs ); - BOOST_CHECK( GEOM_TEST::IsOutlineValid( clipper1chain ) ); BOOST_CHECK( GEOM_TEST::IsOutlineValid( clipper2chain ) ); - BOOST_CHECK_EQUAL( clipper1chain.PointCount(), 37 ); BOOST_CHECK_EQUAL( clipper2chain.PointCount(), 37 ); - BOOST_CHECK_EQUAL( clipper1chain.ArcCount(), 1 ); BOOST_CHECK_EQUAL( clipper2chain.ArcCount(), 1 ); - BOOST_CHECK_EQUAL( clipper1chain.ShapeCount(), 2 ); BOOST_CHECK_EQUAL( clipper2chain.ShapeCount(), 2 ); - BOOST_CHECK_EQUAL( clipper1chain.IsClosed(), true ); BOOST_CHECK_EQUAL( clipper2chain.IsClosed(), true ); } diff --git a/qa/tests/libs/kimath/geometry/test_shape_poly_set_arcs.cpp b/qa/tests/libs/kimath/geometry/test_shape_poly_set_arcs.cpp index b5a6fc356e..0139434fb6 100644 --- a/qa/tests/libs/kimath/geometry/test_shape_poly_set_arcs.cpp +++ b/qa/tests/libs/kimath/geometry/test_shape_poly_set_arcs.cpp @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE( TestSimplify ) { BOOST_TEST_CONTEXT( "Simplify Iteration " << i ) { - testPoly.Simplify( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST ); + testPoly.Simplify(); std::vector<SHAPE_ARC> foundArcs; testPoly.GetArcs( foundArcs ); @@ -119,14 +119,14 @@ BOOST_AUTO_TEST_CASE( TestIntersectUnion ) double opPolyArea = opPoly.Area(); SHAPE_POLY_SET intersectionPoly = testPoly; - intersectionPoly.BooleanIntersection( opPoly, SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE ); + intersectionPoly.BooleanIntersection( opPoly ); double intersectArea = intersectionPoly.Area(); BOOST_CHECK( GEOM_TEST::IsPolySetValid( intersectionPoly ) ); SHAPE_POLY_SET unionPoly = testPoly; - unionPoly.BooleanAdd( opPoly, SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE ); + unionPoly.BooleanAdd( opPoly ); double unionArea = unionPoly.Area(); BOOST_CHECK( GEOM_TEST::IsPolySetValid( unionPoly ) ); diff --git a/qa/tests/pcbnew/test_zone_filler.cpp b/qa/tests/pcbnew/test_zone_filler.cpp index c002a604af..a03026fb0e 100644 --- a/qa/tests/pcbnew/test_zone_filler.cpp +++ b/qa/tests/pcbnew/test_zone_filler.cpp @@ -158,8 +158,7 @@ BOOST_FIXTURE_TEST_CASE( NotchedZones, ZONE_FILL_TEST_FIXTURE ) { if( zone->GetLayerSet().Contains( F_Cu ) ) { - frontCopper.BooleanAdd( *zone->GetFilledPolysList( F_Cu ), - SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + frontCopper.BooleanAdd( *zone->GetFilledPolysList( F_Cu ) ); } } diff --git a/qa/tools/drc_proto/drc_proto.cpp b/qa/tools/drc_proto/drc_proto.cpp index 0ee7a4e749..09ee7ad092 100644 --- a/qa/tools/drc_proto/drc_proto.cpp +++ b/qa/tools/drc_proto/drc_proto.cpp @@ -135,7 +135,7 @@ int runDRCProto( PROJECT_CONTEXT project, std::shared_ptr<KIGFX::VIEW_OVERLAY> a { drcEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false ); } - catch( const ClipperLib::clipperException& e ) + catch( const Clipper2Lib::Clipper2Exception& e ) { consoleLog.Print( wxString::Format( "Clipper exception %s occurred.", e.what() ) ); } diff --git a/qa/tools/pcbnew_tools/tools/polygon_triangulation/polygon_triangulation.cpp b/qa/tools/pcbnew_tools/tools/polygon_triangulation/polygon_triangulation.cpp index bc17c2b5dc..ac4c5a22c0 100644 --- a/qa/tools/pcbnew_tools/tools/polygon_triangulation/polygon_triangulation.cpp +++ b/qa/tools/pcbnew_tools/tools/polygon_triangulation/polygon_triangulation.cpp @@ -246,7 +246,7 @@ int polygon_triangulation_main( int argc, char *argv[] ) ignore_unused( poly ); #if 0 PROF_TIMER unfrac("unfrac"); - poly.Unfracture( SHAPE_POLY_SET::PM_FAST ); + poly.Unfracture(); unfrac.Show(); PROF_TIMER triangulate("triangulate"); diff --git a/qa/tools/pns/playground.cpp b/qa/tools/pns/playground.cpp index 4937cdf67d..a217b4db92 100644 --- a/qa/tools/pns/playground.cpp +++ b/qa/tools/pns/playground.cpp @@ -307,12 +307,12 @@ int playground_main_func( int argc, char* argv[] ) SHAPE_POLY_SET xorPad1ToPad2 = pad1Outline; - xorPad1ToPad2.BooleanXor( pad2Outline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + xorPad1ToPad2.BooleanXor( pad2Outline ); xorPad1ToPad2.Move( VECTOR2I( pcbIUScale.mmToIU( 10 ), 0 ) ); SHAPE_POLY_SET andPad1ToPad2 = pad2Outline; - andPad1ToPad2.BooleanIntersection( pad1Outline, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); + andPad1ToPad2.BooleanIntersection( pad1Outline ); andPad1ToPad2.Move( VECTOR2I( pcbIUScale.mmToIU( 20 ), 0 ) ); std::shared_ptr<SHAPE_SEGMENT> slot = pad2.GetEffectiveHoleShape(); diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index bb51237f6b..3d16c01838 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -34,7 +34,6 @@ set( CMAKE_POLICY_DEFAULT_CMP0077 NEW ) set( ARGPARSE_INSTALL OFF ) add_subdirectory( argparse ) -add_subdirectory( clipper ) add_subdirectory( clipper2 ) add_subdirectory( compoundfilereader ) add_subdirectory( delaunator ) diff --git a/thirdparty/clipper/CMakeLists.txt b/thirdparty/clipper/CMakeLists.txt deleted file mode 100644 index 8dc47eefad..0000000000 --- a/thirdparty/clipper/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ - -add_library( clipper OBJECT - clipper.cpp -) - -target_include_directories( clipper - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} -) diff --git a/thirdparty/clipper/LICENSE.BOOSTv1_0 b/thirdparty/clipper/LICENSE.BOOSTv1_0 deleted file mode 100644 index 36b7cd93cd..0000000000 --- a/thirdparty/clipper/LICENSE.BOOSTv1_0 +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/clipper/README.txt b/thirdparty/clipper/README.txt deleted file mode 100644 index 1e7557825e..0000000000 --- a/thirdparty/clipper/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -This directory contains the Clipper library for interacting with polygons. -https://sourceforge.net/projects/polyclipping/ - -It is licensed under the Boost Software License, with the license text in this directory. diff --git a/thirdparty/clipper/clipper.cpp b/thirdparty/clipper/clipper.cpp deleted file mode 100644 index 0f6aac8f3a..0000000000 --- a/thirdparty/clipper/clipper.cpp +++ /dev/null @@ -1,5999 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.2 * -* Date : 27 February 2017 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2017 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -/******************************************************************************* -* * -* This is a translation of the Delphi Clipper library and the naming style * -* used has retained a Delphi flavour. * -* * -*******************************************************************************/ - -#include "clipper.hpp" -#include <cmath> -#include <vector> -#include <algorithm> -#include <stdexcept> -#include <cstring> -#include <cstdlib> -#include <ostream> -#include <functional> - -namespace ClipperLib { -static double const pi = 3.141592653589793238; -static double const two_pi = pi * 2; -static double const def_arc_tolerance = 0.25; - -enum Direction -{ - dRightToLeft, dLeftToRight -}; - -static int const Unassigned = -1; // edge not currently 'owning' a solution -static int const Skip = -2; // edge that would otherwise close a path - -#define HORIZONTAL (-1.0E+40) -#define TOLERANCE (1.0e-20) -#define NEAR_ZERO( val ) ( ( (val) > -TOLERANCE ) && ( (val) < TOLERANCE ) ) - -struct TEdge -{ - IntPoint Bot; - IntPoint Curr; // current (updated for every new scanbeam) - IntPoint Top; - double Dx; - PolyType PolyTyp; - EdgeSide Side; // side only refers to current side of solution poly - int WindDelta; // 1 or -1 depending on winding direction - int WindCnt; - int WindCnt2; // winding count of the opposite polytype - int OutIdx; - TEdge* Next; - TEdge* Prev; - TEdge* NextInLML; - TEdge* NextInAEL; - TEdge* PrevInAEL; - TEdge* NextInSEL; - TEdge* PrevInSEL; -}; - -struct IntersectNode -{ - TEdge* Edge1; - TEdge* Edge2; - IntPoint Pt; -}; - -struct LocalMinimum -{ - cInt Y; - TEdge* LeftBound; - TEdge* RightBound; -}; - -struct OutPt; - -// OutRec: contains a path in the clipping solution. Edges in the AEL will -// carry a pointer to an OutRec when they are part of the clipping solution. -struct OutRec -{ - int Idx; - bool IsHole; - bool IsOpen; - OutRec* FirstLeft; // see comments in clipper.pas - PolyNode* PolyNd; - OutPt* Pts; - OutPt* BottomPt; -}; - -struct OutPt -{ - int Idx; - IntPoint Pt; - OutPt* Next; - OutPt* Prev; -}; - -struct Join -{ - OutPt* OutPt1; - OutPt* OutPt2; - IntPoint OffPt; -}; - -struct LocMinSorter -{ - inline bool operator()( const LocalMinimum& locMin1, const LocalMinimum& locMin2 ) - { - return locMin2.Y < locMin1.Y; - } -}; - -// ------------------------------------------------------------------------------ -// ------------------------------------------------------------------------------ - -inline cInt Round( double val ) -{ - if( (val < 0) ) - return static_cast<cInt>(val - 0.5); - else - return static_cast<cInt>(val + 0.5); -} - - -// ------------------------------------------------------------------------------ - -inline cInt Abs( cInt val ) -{ - return val < 0 ? -val : val; -} - - -// ------------------------------------------------------------------------------ -// PolyTree methods ... -// ------------------------------------------------------------------------------ - -void PolyTree::Clear() -{ - for( PolyNodes::size_type i = 0; i < AllNodes.size(); ++i ) - delete AllNodes[i]; - - AllNodes.resize( 0 ); - Childs.resize( 0 ); -} - - -// ------------------------------------------------------------------------------ - -PolyNode* PolyTree::GetFirst() const -{ - if( !Childs.empty() ) - return Childs[0]; - else - return 0; -} - - -// ------------------------------------------------------------------------------ - -int PolyTree::Total() const -{ - int result = (int) AllNodes.size(); - - // with negative offsets, ignore the hidden outer polygon ... - if( result > 0 && Childs[0] != AllNodes[0] ) - result--; - - return result; -} - - -// ------------------------------------------------------------------------------ -// PolyNode methods ... -// ------------------------------------------------------------------------------ - -PolyNode::PolyNode() : Parent( 0 ), Index( 0 ), m_IsOpen( false ), - m_jointype( jtSquare ), m_endtype( etClosedPolygon ) -{ -} - - -// ------------------------------------------------------------------------------ - -int PolyNode::ChildCount() const -{ - return (int) Childs.size(); -} - - -// ------------------------------------------------------------------------------ - -void PolyNode::AddChild( PolyNode& child ) -{ - unsigned cnt = (unsigned) Childs.size(); - - Childs.push_back( &child ); - child.Parent = this; - child.Index = cnt; -} - - -// ------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNext() const -{ - if( !Childs.empty() ) - return Childs[0]; - else - return GetNextSiblingUp(); -} - - -// ------------------------------------------------------------------------------ - -PolyNode* PolyNode::GetNextSiblingUp() const -{ - if( !Parent ) // protects against PolyTree.GetNextSiblingUp() - return 0; - else if( Index == Parent->Childs.size() - 1 ) - return Parent->GetNextSiblingUp(); - else - return Parent->Childs[Index + 1]; -} - - -// ------------------------------------------------------------------------------ - -bool PolyNode::IsHole() const -{ - bool result = true; - PolyNode* node = Parent; - - while( node ) - { - result = !result; - node = node->Parent; - } - - return result; -} - - -// ------------------------------------------------------------------------------ - -bool PolyNode::IsOpen() const -{ - return m_IsOpen; -} - - -// ------------------------------------------------------------------------------ - -#ifndef use_int32 - -// ------------------------------------------------------------------------------ -// Int128 class (enables safe math on signed 64bit integers) -// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 -// Int128 val2((long64)9223372036854775807); -// Int128 val3 = val1 * val2; -// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) -// ------------------------------------------------------------------------------ - -class Int128 -{ -public: - ulong64 lo; - long64 hi; - - Int128( long64 _lo = 0 ) - { - lo = (ulong64) _lo; - - if( _lo < 0 ) hi = -1; else hi = 0; - } - - Int128( const Int128& val ) : lo( val.lo ), hi( val.hi ) {} - - Int128( const long64& _hi, const ulong64& _lo ) : lo( _lo ), hi( _hi ) {} - - Int128& operator =( const long64& val ) - { - lo = (ulong64) val; - - if( val < 0 ) hi = -1; else hi = 0; - - return *this; - } - - bool operator ==( const Int128& val ) const - { return hi == val.hi && lo == val.lo; } - - bool operator !=( const Int128& val ) const - { return !(*this == val); } - - bool operator >( const Int128& val ) const - { - if( hi != val.hi ) - return hi > val.hi; - else - return lo > val.lo; - } - - bool operator <( const Int128& val ) const - { - if( hi != val.hi ) - return hi < val.hi; - else - return lo < val.lo; - } - - bool operator >=( const Int128& val ) const - { return !(*this < val); } - - bool operator <=( const Int128& val ) const - { return !(*this > val); } - - Int128& operator +=( const Int128& rhs ) - { - hi += rhs.hi; - lo += rhs.lo; - - if( lo < rhs.lo ) hi++; - - return *this; - } - - Int128 operator +( const Int128& rhs ) const - { - Int128 result( *this ); - - result += rhs; - return result; - } - - Int128& operator -=( const Int128& rhs ) - { - *this += -rhs; - return *this; - } - - Int128 operator -( const Int128& rhs ) const - { - Int128 result( *this ); - - result -= rhs; - return result; - } - - Int128 operator-() const // unary negation - { - if( lo == 0 ) - return Int128( -hi, 0 ); - else - return Int128( ~hi, ~lo + 1 ); - } - - operator double() const - { - const double shift64 = 18446744073709551616.0; // 2^64 - - if( hi < 0 ) - { - if( lo == 0 ) return (double) hi * shift64; - else return -(double) (~lo + ~hi * shift64); - } - else - return (double) (lo + hi * shift64); - } -}; -// ------------------------------------------------------------------------------ - -Int128 Int128Mul( long64 lhs, long64 rhs ) -{ - bool negate = (lhs < 0) != (rhs < 0); - - if( lhs < 0 ) - lhs = -lhs; - - ulong64 int1Hi = ulong64( lhs ) >> 32; - ulong64 int1Lo = ulong64( lhs & 0xFFFFFFFF ); - - if( rhs < 0 ) - rhs = -rhs; - - ulong64 int2Hi = ulong64( rhs ) >> 32; - ulong64 int2Lo = ulong64( rhs & 0xFFFFFFFF ); - - // nb: see comments in clipper.pas - ulong64 a = int1Hi * int2Hi; - ulong64 b = int1Lo * int2Lo; - ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; - - Int128 tmp; - tmp.hi = long64( a + (c >> 32) ); - tmp.lo = long64( c << 32 ); - tmp.lo += long64( b ); - - if( tmp.lo < b ) - tmp.hi++; - - if( negate ) - tmp = -tmp; - - return tmp; -} -#endif - -// ------------------------------------------------------------------------------ -// Miscellaneous global functions -// ------------------------------------------------------------------------------ - -bool Orientation( const Path& poly ) -{ - return Area( poly ) >= 0; -} - - -// ------------------------------------------------------------------------------ - -double Area( const Path& poly ) -{ - int size = (int) poly.size(); - - if( size < 3 ) - return 0; - - double a = 0; - - for( int i = 0, j = size - 1; i < size; ++i ) - { - a += ( (double) poly[j].X + poly[i].X ) * ( (double) poly[j].Y - poly[i].Y ); - j = i; - } - - return -a * 0.5; -} - - -// ------------------------------------------------------------------------------ - -double Area( const OutPt* op ) -{ - const OutPt* startOp = op; - - if( !op ) - return 0; - - double a = 0; - - do { - a += (double) (op->Prev->Pt.X + op->Pt.X) * (double) (op->Prev->Pt.Y - op->Pt.Y); - op = op->Next; - } while( op != startOp ); - - return a * 0.5; -} - - -// ------------------------------------------------------------------------------ - -double Area( const OutRec& outRec ) -{ - return Area( outRec.Pts ); -} - - -// ------------------------------------------------------------------------------ - -bool PointIsVertex( const IntPoint& Pt, OutPt* pp ) -{ - OutPt* pp2 = pp; - - do - { - if( pp2->Pt == Pt ) - return true; - - pp2 = pp2->Next; - } while( pp2 != pp ); - - return false; -} - - -// ------------------------------------------------------------------------------ - -// See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos -// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf -int PointInPolygon( const IntPoint& pt, const Path& path ) -{ - // returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - size_t cnt = path.size(); - - if( cnt < 3 ) - return 0; - - IntPoint ip = path[0]; - - for( size_t i = 1; i <= cnt; ++i ) - { - IntPoint ipNext = (i == cnt ? path[0] : path[i]); - - if( ipNext.Y == pt.Y ) - { - if( (ipNext.X == pt.X) || ( ip.Y == pt.Y - && ( (ipNext.X > pt.X) == (ip.X < pt.X) ) ) ) - return -1; - } - - if( (ip.Y < pt.Y) != (ipNext.Y < pt.Y) ) - { - if( ip.X >= pt.X ) - { - if( ipNext.X > pt.X ) - result = 1 - result; - else - { - double d = (double) (ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double) (ipNext.X - pt.X) * (ip.Y - pt.Y); - - if( !d ) - return -1; - - if( (d > 0) == (ipNext.Y > ip.Y) ) - result = 1 - result; - } - } - else - { - if( ipNext.X > pt.X ) - { - double d = (double) (ip.X - pt.X) * (ipNext.Y - pt.Y) - - (double) (ipNext.X - pt.X) * (ip.Y - pt.Y); - - if( !d ) - return -1; - - if( (d > 0) == (ipNext.Y > ip.Y) ) - result = 1 - result; - } - } - } - - ip = ipNext; - } - - return result; -} - - -// ------------------------------------------------------------------------------ - -int PointInPolygon( const IntPoint& pt, OutPt* op ) -{ - // returns 0 if false, +1 if true, -1 if pt ON polygon boundary - int result = 0; - OutPt* startOp = op; - - for( ; ; ) - { - if( op->Next->Pt.Y == pt.Y ) - { - if( (op->Next->Pt.X == pt.X) || ( op->Pt.Y == pt.Y - && ( (op->Next->Pt.X > pt.X) == - (op->Pt.X < pt.X) ) ) ) - return -1; - } - - if( (op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y) ) - { - if( op->Pt.X >= pt.X ) - { - if( op->Next->Pt.X > pt.X ) - result = 1 - result; - else - { - double d = (double) (op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double) (op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - - if( !d ) - return -1; - - if( (d > 0) == (op->Next->Pt.Y > op->Pt.Y) ) - result = 1 - result; - } - } - else - { - if( op->Next->Pt.X > pt.X ) - { - double d = (double) (op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - - (double) (op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); - - if( !d ) - return -1; - - if( (d > 0) == (op->Next->Pt.Y > op->Pt.Y) ) - result = 1 - result; - } - } - } - - op = op->Next; - - if( startOp == op ) - break; - } - - return result; -} - - -// ------------------------------------------------------------------------------ - -bool Poly2ContainsPoly1( OutPt* OutPt1, OutPt* OutPt2 ) -{ - OutPt* op = OutPt1; - - do - { - // nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon - int res = PointInPolygon( op->Pt, OutPt2 ); - - if( res >= 0 ) - return res > 0; - - op = op->Next; - } while( op != OutPt1 ); - - return true; -} - - -// ---------------------------------------------------------------------- - -bool SlopesEqual( const TEdge& e1, const TEdge& e2, bool UseFullInt64Range ) -{ -#ifndef use_int32 - - if( UseFullInt64Range ) - return Int128Mul( e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X ) == - Int128Mul( e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y ); - else -#endif - return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == - (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); -} - - -// ------------------------------------------------------------------------------ - -bool SlopesEqual( const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, bool UseFullInt64Range ) -{ -#ifndef use_int32 - - if( UseFullInt64Range ) - return Int128Mul( pt1.Y - pt2.Y, - pt2.X - pt3.X ) == Int128Mul( pt1.X - pt2.X, pt2.Y - pt3.Y ); - else -#endif - return (pt1.Y - pt2.Y) * (pt2.X - pt3.X) == (pt1.X - pt2.X) * (pt2.Y - pt3.Y); -} - - -// ------------------------------------------------------------------------------ - -bool SlopesEqual( const IntPoint pt1, const IntPoint pt2, - const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range ) -{ -#ifndef use_int32 - - if( UseFullInt64Range ) - return Int128Mul( pt1.Y - pt2.Y, - pt3.X - pt4.X ) == Int128Mul( pt1.X - pt2.X, pt3.Y - pt4.Y ); - else -#endif - return (pt1.Y - pt2.Y) * (pt3.X - pt4.X) == (pt1.X - pt2.X) * (pt3.Y - pt4.Y); -} - - -// ------------------------------------------------------------------------------ - -inline bool IsHorizontal( TEdge& e ) -{ - return e.Dx == HORIZONTAL; -} - - -// ------------------------------------------------------------------------------ - -inline double GetDx( const IntPoint pt1, const IntPoint pt2 ) -{ - return (pt1.Y == pt2.Y) ? - HORIZONTAL : (double) (pt2.X - pt1.X) / (pt2.Y - pt1.Y); -} - - -// --------------------------------------------------------------------------- - -inline void SetDx( TEdge& e ) -{ - cInt dy = (e.Top.Y - e.Bot.Y); - - if( dy == 0 ) - e.Dx = HORIZONTAL; - else - e.Dx = (double) (e.Top.X - e.Bot.X) / dy; -} - - -// --------------------------------------------------------------------------- - -inline void SwapSides( TEdge& Edge1, TEdge& Edge2 ) -{ - EdgeSide Side = Edge1.Side; - - Edge1.Side = Edge2.Side; - Edge2.Side = Side; -} - - -// ------------------------------------------------------------------------------ - -inline void SwapPolyIndexes( TEdge& Edge1, TEdge& Edge2 ) -{ - int OutIdx = Edge1.OutIdx; - - Edge1.OutIdx = Edge2.OutIdx; - Edge2.OutIdx = OutIdx; -} - - -// ------------------------------------------------------------------------------ - -inline cInt TopX( TEdge& edge, const cInt currentY ) -{ - return ( currentY == edge.Top.Y ) ? - edge.Top.X : edge.Bot.X + Round( edge.Dx * (currentY - edge.Bot.Y) ); -} - - -// ------------------------------------------------------------------------------ - -void IntersectPoint( TEdge& Edge1, TEdge& Edge2, IntPoint& ip ) -{ -#ifdef use_xyz - ip.Z = 0; -#endif - - double b1, b2; - - if( Edge1.Dx == Edge2.Dx ) - { - ip.Y = Edge1.Curr.Y; - ip.X = TopX( Edge1, ip.Y ); - return; - } - else if( Edge1.Dx == 0 ) - { - ip.X = Edge1.Bot.X; - - if( IsHorizontal( Edge2 ) ) - ip.Y = Edge2.Bot.Y; - else - { - b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); - ip.Y = Round( ip.X / Edge2.Dx + b2 ); - } - } - else if( Edge2.Dx == 0 ) - { - ip.X = Edge2.Bot.X; - - if( IsHorizontal( Edge1 ) ) - ip.Y = Edge1.Bot.Y; - else - { - b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); - ip.Y = Round( ip.X / Edge1.Dx + b1 ); - } - } - else - { - b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; - b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; - double q = (b2 - b1) / (Edge1.Dx - Edge2.Dx); - ip.Y = Round( q ); - - if( std::fabs( Edge1.Dx ) < std::fabs( Edge2.Dx ) ) - ip.X = Round( Edge1.Dx * q + b1 ); - else - ip.X = Round( Edge2.Dx * q + b2 ); - } - - if( ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y ) - { - if( Edge1.Top.Y > Edge2.Top.Y ) - ip.Y = Edge1.Top.Y; - else - ip.Y = Edge2.Top.Y; - - if( std::fabs( Edge1.Dx ) < std::fabs( Edge2.Dx ) ) - ip.X = TopX( Edge1, ip.Y ); - else - ip.X = TopX( Edge2, ip.Y ); - } - - // finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... - if( ip.Y > Edge1.Curr.Y ) - { - ip.Y = Edge1.Curr.Y; - - // use the more vertical edge to derive X ... - if( std::fabs( Edge1.Dx ) > std::fabs( Edge2.Dx ) ) - ip.X = TopX( Edge2, ip.Y ); - else - ip.X = TopX( Edge1, ip.Y ); - } -} - - -// ------------------------------------------------------------------------------ - -void ReversePolyPtLinks( OutPt* pp ) -{ - if( !pp ) - return; - - OutPt* pp1, * pp2; - pp1 = pp; - - do { - pp2 = pp1->Next; - pp1->Next = pp1->Prev; - pp1->Prev = pp2; - pp1 = pp2; - } while( pp1 != pp ); -} - - -// ------------------------------------------------------------------------------ - -void DisposeOutPts( OutPt*& pp ) -{ - if( pp == 0 ) - return; - - pp->Prev->Next = 0; - - while( pp ) - { - OutPt* tmpPp = pp; - pp = pp->Next; - delete tmpPp; - } -} - - -// ------------------------------------------------------------------------------ - -inline void InitEdge( TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt ) -{ - // This clears the C++ way - *e = TEdge( { 0 } ); - - e->Next = eNext; - e->Prev = ePrev; - e->Curr = Pt; - e->OutIdx = Unassigned; -} - - -// ------------------------------------------------------------------------------ - -void InitEdge2( TEdge& e, PolyType Pt ) -{ - if( e.Curr.Y >= e.Next->Curr.Y ) - { - e.Bot = e.Curr; - e.Top = e.Next->Curr; - } - else - { - e.Top = e.Curr; - e.Bot = e.Next->Curr; - } - - SetDx( e ); - e.PolyTyp = Pt; -} - - -// ------------------------------------------------------------------------------ - -TEdge* RemoveEdge( TEdge* e ) -{ - // removes e from double_linked_list (but without removing from memory) - e->Prev->Next = e->Next; - e->Next->Prev = e->Prev; - TEdge* result = e->Next; - e->Prev = 0; // flag as removed (see ClipperBase.Clear) - return result; -} - - -// ------------------------------------------------------------------------------ - -inline void ReverseHorizontal( TEdge& e ) -{ - // swap horizontal edges' Top and Bottom x's so they follow the natural - // progression of the bounds - ie so their xbots will align with the - // adjoining lower edge. [Helpful in the ProcessHorizontal() method.] - std::swap( e.Top.X, e.Bot.X ); - -#ifdef use_xyz - std::swap( e.Top.Z, e.Bot.Z ); -#endif -} - - -// ------------------------------------------------------------------------------ - -void SwapPoints( IntPoint& pt1, IntPoint& pt2 ) -{ - IntPoint tmp = pt1; - - pt1 = pt2; - pt2 = tmp; -} - - -// ------------------------------------------------------------------------------ - -bool GetOverlapSegment( IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, - IntPoint pt2b, IntPoint& pt1, IntPoint& pt2 ) -{ - // precondition: segments are Collinear. - if( Abs( pt1a.X - pt1b.X ) > Abs( pt1a.Y - pt1b.Y ) ) - { - if( pt1a.X > pt1b.X ) - SwapPoints( pt1a, pt1b ); - - if( pt2a.X > pt2b.X ) - SwapPoints( pt2a, pt2b ); - - if( pt1a.X > pt2a.X ) - pt1 = pt1a; - else - pt1 = pt2a; - - if( pt1b.X < pt2b.X ) - pt2 = pt1b; - else - pt2 = pt2b; - - return pt1.X < pt2.X; - } - else - { - if( pt1a.Y < pt1b.Y ) - SwapPoints( pt1a, pt1b ); - - if( pt2a.Y < pt2b.Y ) - SwapPoints( pt2a, pt2b ); - - if( pt1a.Y < pt2a.Y ) - pt1 = pt1a; - else - pt1 = pt2a; - - if( pt1b.Y > pt2b.Y ) - pt2 = pt1b; - else - pt2 = pt2b; - - return pt1.Y > pt2.Y; - } -} - - -// ------------------------------------------------------------------------------ - -bool FirstIsBottomPt( const OutPt* btmPt1, const OutPt* btmPt2 ) -{ - OutPt* p = btmPt1->Prev; - - while( (p->Pt == btmPt1->Pt) && (p != btmPt1) ) - p = p->Prev; - - double dx1p = std::fabs( GetDx( btmPt1->Pt, p->Pt ) ); - p = btmPt1->Next; - - while( (p->Pt == btmPt1->Pt) && (p != btmPt1) ) - p = p->Next; - - double dx1n = std::fabs( GetDx( btmPt1->Pt, p->Pt ) ); - - p = btmPt2->Prev; - - while( (p->Pt == btmPt2->Pt) && (p != btmPt2) ) - p = p->Prev; - - double dx2p = std::fabs( GetDx( btmPt2->Pt, p->Pt ) ); - p = btmPt2->Next; - - while( (p->Pt == btmPt2->Pt) && (p != btmPt2) ) - p = p->Next; - - double dx2n = std::fabs( GetDx( btmPt2->Pt, p->Pt ) ); - - if( std::max( dx1p, dx1n ) == std::max( dx2p, dx2n ) - && std::min( dx1p, dx1n ) == std::min( dx2p, dx2n ) ) - return Area( btmPt1 ) > 0; // if otherwise identical use orientation - else - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); -} - - -// ------------------------------------------------------------------------------ - -OutPt* GetBottomPt( OutPt* pp ) -{ - OutPt* dups = 0; - OutPt* p = pp->Next; - - while( p != pp ) - { - if( p->Pt.Y > pp->Pt.Y ) - { - pp = p; - dups = 0; - } - else if( p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X ) - { - if( p->Pt.X < pp->Pt.X ) - { - dups = 0; - pp = p; - } - else - { - if( p->Next != pp && p->Prev != pp ) - dups = p; - } - } - - p = p->Next; - } - - if( dups ) - { - // there appears to be at least 2 vertices at BottomPt so ... - while( dups != p ) - { - if( !FirstIsBottomPt( p, dups ) ) - pp = dups; - - dups = dups->Next; - - while( dups->Pt != pp->Pt ) - dups = dups->Next; - } - } - - return pp; -} - - -// ------------------------------------------------------------------------------ - -bool Pt2IsBetweenPt1AndPt3( const IntPoint pt1, - const IntPoint pt2, const IntPoint pt3 ) -{ - if( (pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2) ) - return false; - else if( pt1.X != pt3.X ) - return (pt2.X > pt1.X) == (pt2.X < pt3.X); - else - return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); -} - - -// ------------------------------------------------------------------------------ - -bool HorzSegmentsOverlap( cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b ) -{ - if( seg1a > seg1b ) - std::swap( seg1a, seg1b ); - - if( seg2a > seg2b ) - std::swap( seg2a, seg2b ); - - return (seg1a < seg2b) && (seg2a < seg1b); -} - - -// ------------------------------------------------------------------------------ -// ClipperBase class methods ... -// ------------------------------------------------------------------------------ - -ClipperBase::ClipperBase() // constructor -{ - m_CurrentLM = m_MinimaList.begin(); // begin() == end() here - m_UseFullRange = false; - - // Avoid uninitialized vars: - m_PreserveCollinear = false; - m_HasOpenPaths = false; - m_ActiveEdges = nullptr; -} - - -// ------------------------------------------------------------------------------ - -ClipperBase::~ClipperBase() // destructor -{ - Clear(); -} - - -// ------------------------------------------------------------------------------ - -void RangeTest( const IntPoint& Pt, bool& useFullRange ) -{ - if( useFullRange ) - { - if( Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange ) - throw clipperException( "Coordinate outside allowed range" ); - } - else if( Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange ) - { - useFullRange = true; - RangeTest( Pt, useFullRange ); - } -} - - -// ------------------------------------------------------------------------------ - -TEdge* FindNextLocMin( TEdge* E ) -{ - for( ; ; ) - { - while( E->Bot != E->Prev->Bot || E->Curr == E->Top ) - E = E->Next; - - if( !IsHorizontal( *E ) && !IsHorizontal( *E->Prev ) ) - break; - - while( IsHorizontal( *E->Prev ) ) - E = E->Prev; - - TEdge* E2 = E; - - while( IsHorizontal( *E ) ) - E = E->Next; - - if( E->Top.Y == E->Prev->Bot.Y ) - continue; // ie just an intermediate horz. - - if( E2->Prev->Bot.X < E->Bot.X ) - E = E2; - - break; - } - - return E; -} - - -// ------------------------------------------------------------------------------ - -TEdge* ClipperBase::ProcessBound( TEdge* E, bool NextIsForward ) -{ - TEdge* Result = E; - TEdge* Horz = 0; - - if( E->OutIdx == Skip ) - { - // if edges still remain in the current bound beyond the skip edge then - // create another LocMin and call ProcessBound once more - if( NextIsForward ) - { - while( E->Top.Y == E->Next->Bot.Y ) - E = E->Next; - - // don't include top horizontals when parsing a bound a second time, - // they will be contained in the opposite bound ... - while( E != Result && IsHorizontal( *E ) ) - E = E->Prev; - } - else - { - while( E->Top.Y == E->Prev->Bot.Y ) - E = E->Prev; - - while( E != Result && IsHorizontal( *E ) ) - E = E->Next; - } - - if( E == Result ) - { - if( NextIsForward ) - Result = E->Next; - else - Result = E->Prev; - } - else - { - // there are more edges in the bound beyond result starting with E - if( NextIsForward ) - E = Result->Next; - else - E = Result->Prev; - - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - E->WindDelta = 0; - Result = ProcessBound( E, NextIsForward ); - m_MinimaList.push_back( locMin ); - } - - return Result; - } - - TEdge* EStart; - - if( IsHorizontal( *E ) ) - { - // We need to be careful with open paths because this may not be a - // true local minima (ie E may be following a skip edge). - // Also, consecutive horz. edges may start heading left before going right. - if( NextIsForward ) - EStart = E->Prev; - else - EStart = E->Next; - - if( IsHorizontal( *EStart ) ) // ie an adjoining horizontal skip edge - { - if( EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X ) - ReverseHorizontal( *E ); - } - else if( EStart->Bot.X != E->Bot.X ) - ReverseHorizontal( *E ); - } - - EStart = E; - - if( NextIsForward ) - { - while( Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip ) - Result = Result->Next; - - if( IsHorizontal( *Result ) && Result->Next->OutIdx != Skip ) - { - // nb: at the top of a bound, horizontals are added to the bound - // only when the preceding edge attaches to the horizontal's left vertex - // unless a Skip edge is encountered when that becomes the top divide - Horz = Result; - - while( IsHorizontal( *Horz->Prev ) ) - Horz = Horz->Prev; - - if( Horz->Prev->Top.X > Result->Next->Top.X ) - Result = Horz->Prev; - } - - while( E != Result ) - { - E->NextInLML = E->Next; - - if( IsHorizontal( *E ) && E != EStart - && E->Bot.X != E->Prev->Top.X ) - ReverseHorizontal( *E ); - - E = E->Next; - } - - if( IsHorizontal( *E ) && E != EStart && E->Bot.X != E->Prev->Top.X ) - ReverseHorizontal( *E ); - - Result = Result->Next; // move to the edge just beyond current bound - } - else - { - while( Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip ) - Result = Result->Prev; - - if( IsHorizontal( *Result ) && Result->Prev->OutIdx != Skip ) - { - Horz = Result; - - while( IsHorizontal( *Horz->Next ) ) - Horz = Horz->Next; - - if( Horz->Next->Top.X == Result->Prev->Top.X - || Horz->Next->Top.X > Result->Prev->Top.X ) - Result = Horz->Next; - } - - while( E != Result ) - { - E->NextInLML = E->Prev; - - if( IsHorizontal( *E ) && E != EStart && E->Bot.X != E->Next->Top.X ) - ReverseHorizontal( *E ); - - E = E->Prev; - } - - if( IsHorizontal( *E ) && E != EStart && E->Bot.X != E->Next->Top.X ) - ReverseHorizontal( *E ); - - Result = Result->Prev; // move to the edge just beyond current bound - } - - return Result; -} - - -// ------------------------------------------------------------------------------ - -bool ClipperBase::AddPath( const Path& pg, PolyType PolyTyp, bool Closed ) -{ -#ifdef use_lines - - if( !Closed && PolyTyp == ptClip ) - throw clipperException( "AddPath: Open paths must be subject." ); - -#else - - if( !Closed ) - throw clipperException( "AddPath: Open paths have been disabled." ); - -#endif - - int highI = (int) pg.size() - 1; - - if( Closed ) - while( highI > 0 && (pg[highI] == pg[0]) ) - --highI; - - - while( highI > 0 && (pg[highI] == pg[highI - 1]) ) - --highI; - - if( (Closed && highI < 2) || (!Closed && highI < 1) ) - return false; - - // create a new edge array ... - TEdge* edges = new TEdge[highI + 1]; - - bool IsFlat = true; - // 1. Basic (first) edge initialization ... - try - { - edges[1].Curr = pg[1]; - RangeTest( pg[0], m_UseFullRange ); - RangeTest( pg[highI], m_UseFullRange ); - InitEdge( &edges[0], &edges[1], &edges[highI], pg[0] ); - InitEdge( &edges[highI], &edges[0], &edges[highI - 1], pg[highI] ); - - for( int i = highI - 1; i >= 1; --i ) - { - RangeTest( pg[i], m_UseFullRange ); - InitEdge( &edges[i], &edges[i + 1], &edges[i - 1], pg[i] ); - } - } - catch( ... ) - { - delete [] edges; - throw; // range test fails - } - TEdge* eStart = &edges[0]; - - // 2. Remove duplicate vertices, and (when closed) collinear edges ... - TEdge* E = eStart, * eLoopStop = eStart; - - for( ; ; ) - { - // nb: allows matching start and end points when not Closed ... - if( E->Curr == E->Next->Curr && (Closed || E->Next != eStart) ) - { - if( E == E->Next ) - break; - - if( E == eStart ) - eStart = E->Next; - - E = RemoveEdge( E ); - eLoopStop = E; - continue; - } - - if( E->Prev == E->Next ) - break; // only two vertices - else if( Closed - && SlopesEqual( E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange ) - && ( !m_PreserveCollinear - || !Pt2IsBetweenPt1AndPt3( E->Prev->Curr, E->Curr, E->Next->Curr ) ) ) - { - // Collinear edges are allowed for open paths but in closed paths - // the default is to merge adjacent collinear edges into a single edge. - // However, if the PreserveCollinear property is enabled, only overlapping - // collinear edges (ie spikes) will be removed from closed paths. - if( E == eStart ) - eStart = E->Next; - - E = RemoveEdge( E ); - E = E->Prev; - eLoopStop = E; - continue; - } - - E = E->Next; - - if( (E == eLoopStop) || (!Closed && E->Next == eStart) ) - break; - } - - if( ( !Closed && (E == E->Next) ) || ( Closed && (E->Prev == E->Next) ) ) - { - delete [] edges; - return false; - } - - if( !Closed ) - { - m_HasOpenPaths = true; - eStart->Prev->OutIdx = Skip; - } - - // 3. Do second stage of edge initialization ... - E = eStart; - - do - { - InitEdge2( *E, PolyTyp ); - E = E->Next; - - if( IsFlat && E->Curr.Y != eStart->Curr.Y ) - IsFlat = false; - } while( E != eStart ); - - // 4. Finally, add edge bounds to LocalMinima list ... - - // Totally flat paths must be handled differently when adding them - // to LocalMinima list to avoid endless loops etc ... - if( IsFlat ) - { - if( Closed ) - { - delete [] edges; - return false; - } - - E->Prev->OutIdx = Skip; - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - locMin.LeftBound = 0; - locMin.RightBound = E; - locMin.RightBound->Side = esRight; - locMin.RightBound->WindDelta = 0; - - for( ; ; ) - { - if( E->Bot.X != E->Prev->Top.X ) - ReverseHorizontal( *E ); - - if( E->Next->OutIdx == Skip ) - break; - - E->NextInLML = E->Next; - E = E->Next; - } - - m_MinimaList.push_back( locMin ); - m_edges.push_back( edges ); - return true; - } - - m_edges.push_back( edges ); - bool leftBoundIsForward; - TEdge* EMin = 0; - - // workaround to avoid an endless loop in the while loop below when - // open paths have matching start and end points ... - if( E->Prev->Bot == E->Prev->Top ) - E = E->Next; - - for( ; ; ) - { - E = FindNextLocMin( E ); - - if( E == EMin ) - break; - else if( !EMin ) - EMin = E; - - // E and E.Prev now share a local minima (left aligned if horizontal). - // Compare their slopes to find which starts which bound ... - MinimaList::value_type locMin; - locMin.Y = E->Bot.Y; - - if( E->Dx < E->Prev->Dx ) - { - locMin.LeftBound = E->Prev; - locMin.RightBound = E; - leftBoundIsForward = false; // Q.nextInLML = Q.prev - } - else - { - locMin.LeftBound = E; - locMin.RightBound = E->Prev; - leftBoundIsForward = true; // Q.nextInLML = Q.next - } - - if( !Closed ) - locMin.LeftBound->WindDelta = 0; - else if( locMin.LeftBound->Next == locMin.RightBound ) - locMin.LeftBound->WindDelta = -1; - else - locMin.LeftBound->WindDelta = 1; - - locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; - - E = ProcessBound( locMin.LeftBound, leftBoundIsForward ); - - if( E->OutIdx == Skip ) - E = ProcessBound( E, leftBoundIsForward ); - - TEdge* E2 = ProcessBound( locMin.RightBound, !leftBoundIsForward ); - - if( E2->OutIdx == Skip ) - E2 = ProcessBound( E2, !leftBoundIsForward ); - - if( locMin.LeftBound->OutIdx == Skip ) - locMin.LeftBound = 0; - else if( locMin.RightBound->OutIdx == Skip ) - locMin.RightBound = 0; - - m_MinimaList.push_back( locMin ); - - if( !leftBoundIsForward ) - E = E2; - } - - return true; -} - - -// ------------------------------------------------------------------------------ - -bool ClipperBase::AddPaths( const Paths& ppg, PolyType PolyTyp, bool Closed ) -{ - bool result = false; - - for( Paths::size_type i = 0; i < ppg.size(); ++i ) - if( AddPath( ppg[i], PolyTyp, Closed ) ) - result = true; - - - return result; -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::Clear() -{ - DisposeLocalMinimaList(); - - for( EdgeList::size_type i = 0; i < m_edges.size(); ++i ) - { - TEdge* edges = m_edges[i]; - delete [] edges; - } - - m_edges.clear(); - m_UseFullRange = false; - m_HasOpenPaths = false; -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::Reset() -{ - m_CurrentLM = m_MinimaList.begin(); - - if( m_CurrentLM == m_MinimaList.end() ) - return; // ie nothing to process - - std::sort( m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter() ); - - m_Scanbeam = ScanbeamList(); // clears/resets priority_queue - - // reset all edges ... - for( MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm ) - { - InsertScanbeam( lm->Y ); - TEdge* e = lm->LeftBound; - - if( e ) - { - e->Curr = e->Bot; - e->Side = esLeft; - e->OutIdx = Unassigned; - } - - e = lm->RightBound; - - if( e ) - { - e->Curr = e->Bot; - e->Side = esRight; - e->OutIdx = Unassigned; - } - } - - m_ActiveEdges = 0; - m_CurrentLM = m_MinimaList.begin(); -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::DisposeLocalMinimaList() -{ - m_MinimaList.clear(); - m_CurrentLM = m_MinimaList.begin(); -} - - -// ------------------------------------------------------------------------------ - -bool ClipperBase::PopLocalMinima( cInt Y, const LocalMinimum*& locMin ) -{ - if( m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y ) - return false; - - locMin = &(*m_CurrentLM); - ++m_CurrentLM; - return true; -} - - -// ------------------------------------------------------------------------------ - -IntRect ClipperBase::GetBounds() -{ - IntRect result; - MinimaList::iterator lm = m_MinimaList.begin(); - - if( lm == m_MinimaList.end() ) - { - result.left = result.top = result.right = result.bottom = 0; - return result; - } - - result.left = lm->LeftBound->Bot.X; - result.top = lm->LeftBound->Bot.Y; - result.right = lm->LeftBound->Bot.X; - result.bottom = lm->LeftBound->Bot.Y; - - while( lm != m_MinimaList.end() ) - { - // todo - needs fixing for open paths - result.bottom = std::max( result.bottom, lm->LeftBound->Bot.Y ); - TEdge* e = lm->LeftBound; - - for( ; ; ) - { - TEdge* bottomE = e; - - while( e->NextInLML ) - { - if( e->Bot.X < result.left ) - result.left = e->Bot.X; - - if( e->Bot.X > result.right ) - result.right = e->Bot.X; - - e = e->NextInLML; - } - - result.left = std::min( result.left, e->Bot.X ); - result.right = std::max( result.right, e->Bot.X ); - result.left = std::min( result.left, e->Top.X ); - result.right = std::max( result.right, e->Top.X ); - result.top = std::min( result.top, e->Top.Y ); - - if( bottomE == lm->LeftBound ) - e = lm->RightBound; - else - break; - } - - ++lm; - } - - return result; -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::InsertScanbeam( const cInt Y ) -{ - m_Scanbeam.push( Y ); -} - - -// ------------------------------------------------------------------------------ - -bool ClipperBase::PopScanbeam( cInt& Y ) -{ - if( m_Scanbeam.empty() ) - return false; - - Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - - while( !m_Scanbeam.empty() && Y == m_Scanbeam.top() ) - { - m_Scanbeam.pop(); - } // Pop duplicates. - - return true; -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::DisposeAllOutRecs() -{ - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i ) - DisposeOutRec( i ); - - m_PolyOuts.clear(); -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::DisposeOutRec( PolyOutList::size_type index ) -{ - OutRec* outRec = m_PolyOuts[index]; - - if( outRec->Pts ) - DisposeOutPts( outRec->Pts ); - - delete outRec; - m_PolyOuts[index] = 0; -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::DeleteFromAEL( TEdge* e ) -{ - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - - if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) - return; // already deleted - - if( AelPrev ) - AelPrev->NextInAEL = AelNext; - else - m_ActiveEdges = AelNext; - - if( AelNext ) - AelNext->PrevInAEL = AelPrev; - - e->NextInAEL = 0; - e->PrevInAEL = 0; -} - - -// ------------------------------------------------------------------------------ - -OutRec* ClipperBase::CreateOutRec() -{ - OutRec* result = new OutRec; - - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back( result ); - result->Idx = (int) m_PolyOuts.size() - 1; - return result; -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::SwapPositionsInAEL( TEdge* Edge1, TEdge* Edge2 ) -{ - // check that one or other edge hasn't already been removed from AEL ... - if( Edge1->NextInAEL == Edge1->PrevInAEL - || Edge2->NextInAEL == Edge2->PrevInAEL ) - return; - - if( Edge1->NextInAEL == Edge2 ) - { - TEdge* Next = Edge2->NextInAEL; - - if( Next ) - Next->PrevInAEL = Edge1; - - TEdge* Prev = Edge1->PrevInAEL; - - if( Prev ) - Prev->NextInAEL = Edge2; - - Edge2->PrevInAEL = Prev; - Edge2->NextInAEL = Edge1; - Edge1->PrevInAEL = Edge2; - Edge1->NextInAEL = Next; - } - else if( Edge2->NextInAEL == Edge1 ) - { - TEdge* Next = Edge1->NextInAEL; - - if( Next ) - Next->PrevInAEL = Edge2; - - TEdge* Prev = Edge2->PrevInAEL; - - if( Prev ) - Prev->NextInAEL = Edge1; - - Edge1->PrevInAEL = Prev; - Edge1->NextInAEL = Edge2; - Edge2->PrevInAEL = Edge1; - Edge2->NextInAEL = Next; - } - else - { - TEdge* Next = Edge1->NextInAEL; - TEdge* Prev = Edge1->PrevInAEL; - Edge1->NextInAEL = Edge2->NextInAEL; - - if( Edge1->NextInAEL ) - Edge1->NextInAEL->PrevInAEL = Edge1; - - Edge1->PrevInAEL = Edge2->PrevInAEL; - - if( Edge1->PrevInAEL ) - Edge1->PrevInAEL->NextInAEL = Edge1; - - Edge2->NextInAEL = Next; - - if( Edge2->NextInAEL ) - Edge2->NextInAEL->PrevInAEL = Edge2; - - Edge2->PrevInAEL = Prev; - - if( Edge2->PrevInAEL ) - Edge2->PrevInAEL->NextInAEL = Edge2; - } - - if( !Edge1->PrevInAEL ) - m_ActiveEdges = Edge1; - else if( !Edge2->PrevInAEL ) - m_ActiveEdges = Edge2; -} - - -// ------------------------------------------------------------------------------ - -void ClipperBase::UpdateEdgeIntoAEL( TEdge*& e ) -{ - if( !e->NextInLML ) - throw clipperException( "UpdateEdgeIntoAEL: invalid call" ); - - e->NextInLML->OutIdx = e->OutIdx; - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - - if( AelPrev ) - AelPrev->NextInAEL = e->NextInLML; - else - m_ActiveEdges = e->NextInLML; - - if( AelNext ) - AelNext->PrevInAEL = e->NextInLML; - - e->NextInLML->Side = e->Side; - e->NextInLML->WindDelta = e->WindDelta; - e->NextInLML->WindCnt = e->WindCnt; - e->NextInLML->WindCnt2 = e->WindCnt2; - e = e->NextInLML; - e->Curr = e->Bot; - e->PrevInAEL = AelPrev; - e->NextInAEL = AelNext; - - if( !IsHorizontal( *e ) ) - InsertScanbeam( e->Top.Y ); -} - - -// ------------------------------------------------------------------------------ - -bool ClipperBase::LocalMinimaPending() -{ - return m_CurrentLM != m_MinimaList.end(); -} - - -// ------------------------------------------------------------------------------ -// TClipper methods ... -// ------------------------------------------------------------------------------ - -Clipper::Clipper( int initOptions ) : ClipperBase() // constructor -{ - m_ExecuteLocked = false; - m_UseFullRange = false; - m_ReverseOutput = ( (initOptions & ioReverseSolution) != 0 ); - m_StrictSimple = ( (initOptions & ioStrictlySimple) != 0 ); - m_PreserveCollinear = ( (initOptions & ioPreserveCollinear) != 0 ); - m_HasOpenPaths = false; - - // Avoid uninitialized vars - m_ClipType = ctIntersection; - m_SortedEdges = nullptr; - m_ClipFillType = pftEvenOdd; - m_SubjFillType = pftEvenOdd; - m_UsingPolyTree = false; -#ifdef use_xyz - m_ZFill = 0; -#endif -} - - -// ------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::ZFillFunction( ZFillCallback zFillFunc ) -{ - m_ZFill = zFillFunc; -} - - -// ------------------------------------------------------------------------------ -#endif - -bool Clipper::Execute( ClipType clipType, Paths& solution, PolyFillType fillType ) -{ - return Execute( clipType, solution, fillType, fillType ); -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::Execute( ClipType clipType, PolyTree& polytree, PolyFillType fillType ) -{ - return Execute( clipType, polytree, fillType, fillType ); -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::Execute( ClipType clipType, Paths& solution, - PolyFillType subjFillType, PolyFillType clipFillType ) -{ - if( m_ExecuteLocked ) - return false; - - if( m_HasOpenPaths ) - throw clipperException( "Error: PolyTree struct is needed for open path clipping." ); - - m_ExecuteLocked = true; - solution.resize( 0 ); - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = false; - bool succeeded = ExecuteInternal(); - - if( succeeded ) - BuildResult( solution ); - - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::Execute( ClipType clipType, PolyTree& polytree, - PolyFillType subjFillType, PolyFillType clipFillType ) -{ - if( m_ExecuteLocked ) - return false; - - m_ExecuteLocked = true; - m_SubjFillType = subjFillType; - m_ClipFillType = clipFillType; - m_ClipType = clipType; - m_UsingPolyTree = true; - bool succeeded = ExecuteInternal(); - - if( succeeded ) - BuildResult2( polytree ); - - DisposeAllOutRecs(); - m_ExecuteLocked = false; - return succeeded; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::FixHoleLinkage( OutRec& outrec ) -{ - // skip OutRecs that (a) contain outermost polygons or - // (b) already have the correct owner/child linkage ... - if( !outrec.FirstLeft - || (outrec.IsHole != outrec.FirstLeft->IsHole - && outrec.FirstLeft->Pts) ) - return; - - OutRec* orfl = outrec.FirstLeft; - - while( orfl && ( (orfl->IsHole == outrec.IsHole) || !orfl->Pts ) ) - orfl = orfl->FirstLeft; - - outrec.FirstLeft = orfl; -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::ExecuteInternal() -{ - bool succeeded = true; - - try - { - Reset(); - m_Maxima = MaximaList(); - m_SortedEdges = 0; - - succeeded = true; - cInt botY, topY; - - if( !PopScanbeam( botY ) ) - return false; - - InsertLocalMinimaIntoAEL( botY ); - - while( PopScanbeam( topY ) || LocalMinimaPending() ) - { - ProcessHorizontals(); - ClearGhostJoins(); - - if( !ProcessIntersections( topY ) ) - { - succeeded = false; - break; - } - - ProcessEdgesAtTopOfScanbeam( topY ); - botY = topY; - InsertLocalMinimaIntoAEL( botY ); - } - } - catch( ... ) - { - succeeded = false; - } - - if( succeeded ) - { - // fix orientations ... - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i ) - { - OutRec* outRec = m_PolyOuts[i]; - - if( !outRec->Pts || outRec->IsOpen ) - continue; - - if( (outRec->IsHole ^ m_ReverseOutput) == (Area( *outRec ) > 0) ) - ReversePolyPtLinks( outRec->Pts ); - } - - if( !m_Joins.empty() ) - JoinCommonEdges(); - - // unfortunately FixupOutPolygon() must be done after JoinCommonEdges() - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i ) - { - OutRec* outRec = m_PolyOuts[i]; - - if( !outRec->Pts ) - continue; - - if( outRec->IsOpen ) - FixupOutPolyline( *outRec ); - else - FixupOutPolygon( *outRec ); - } - - if( m_StrictSimple ) - DoSimplePolygons(); - } - - ClearJoins(); - ClearGhostJoins(); - return succeeded; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::SetWindingCount( TEdge& edge ) -{ - TEdge* e = edge.PrevInAEL; - - // find the edge of the same polytype that immediately preceeds 'edge' in AEL - while( e && ( (e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0) ) ) - e = e->PrevInAEL; - - if( !e ) - { - if( edge.WindDelta == 0 ) - { - PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); - edge.WindCnt = (pft == pftNegative ? -1 : 1); - } - else - edge.WindCnt = edge.WindDelta; - - edge.WindCnt2 = 0; - e = m_ActiveEdges; // ie get ready to calc WindCnt2 - } - else if( edge.WindDelta == 0 && m_ClipType != ctUnion ) - { - edge.WindCnt = 1; - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; // ie get ready to calc WindCnt2 - } - else if( IsEvenOddFillType( edge ) ) - { - // EvenOdd filling ... - if( edge.WindDelta == 0 ) - { - // are we inside a subj polygon ... - bool Inside = true; - TEdge* e2 = e->PrevInAEL; - - while( e2 ) - { - if( e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0 ) - Inside = !Inside; - - e2 = e2->PrevInAEL; - } - - edge.WindCnt = (Inside ? 0 : 1); - } - else - { - edge.WindCnt = edge.WindDelta; - } - - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; // ie get ready to calc WindCnt2 - } - else - { - // nonZero, Positive or Negative filling ... - if( e->WindCnt * e->WindDelta < 0 ) - { - // prev edge is 'decreasing' WindCount (WC) toward zero - // so we're outside the previous polygon ... - if( Abs( e->WindCnt ) > 1 ) - { - // outside prev poly but still inside another. - // when reversing direction of prev poly use the same WC - if( e->WindDelta * edge.WindDelta < 0 ) - edge.WindCnt = e->WindCnt; - // otherwise continue to 'decrease' WC ... - else - edge.WindCnt = e->WindCnt + edge.WindDelta; - } - else - // now outside all polys of same polytype so set own WC ... - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); - } - else - { - // prev edge is 'increasing' WindCount (WC) away from zero - // so we're inside the previous polygon ... - if( edge.WindDelta == 0 ) - edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); - // if wind direction is reversing prev then use same WC - else if( e->WindDelta * edge.WindDelta < 0 ) - edge.WindCnt = e->WindCnt; - // otherwise add to WC ... - else - edge.WindCnt = e->WindCnt + edge.WindDelta; - } - - edge.WindCnt2 = e->WindCnt2; - e = e->NextInAEL; // ie get ready to calc WindCnt2 - } - - // update WindCnt2 ... - if( IsEvenOddAltFillType( edge ) ) - { - // EvenOdd filling ... - while( e != &edge ) - { - if( e->WindDelta != 0 ) - edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); - - e = e->NextInAEL; - } - } - else - { - // nonZero, Positive or Negative filling ... - while( e != &edge ) - { - edge.WindCnt2 += e->WindDelta; - e = e->NextInAEL; - } - } -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddFillType( const TEdge& edge ) const -{ - if( edge.PolyTyp == ptSubject ) - return m_SubjFillType == pftEvenOdd; - else - return m_ClipFillType == pftEvenOdd; -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::IsEvenOddAltFillType( const TEdge& edge ) const -{ - if( edge.PolyTyp == ptSubject ) - return m_ClipFillType == pftEvenOdd; - else - return m_SubjFillType == pftEvenOdd; -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::IsContributing( const TEdge& edge ) const -{ - PolyFillType pft, pft2; - - if( edge.PolyTyp == ptSubject ) - { - pft = m_SubjFillType; - pft2 = m_ClipFillType; - } - else - { - pft = m_ClipFillType; - pft2 = m_SubjFillType; - } - - switch( pft ) - { - case pftEvenOdd: - - // return false if a subj line has been flagged as inside a subj polygon - if( edge.WindDelta == 0 && edge.WindCnt != 1 ) - return false; - - break; - - case pftNonZero: - - if( Abs( edge.WindCnt ) != 1 ) - return false; - - break; - - case pftPositive: - - if( edge.WindCnt != 1 ) - return false; - - break; - - default: // pftNegative - - if( edge.WindCnt != -1 ) - return false; - } - - switch( m_ClipType ) - { - case ctIntersection: - - switch( pft2 ) - { - case pftEvenOdd: - case pftNonZero: - return edge.WindCnt2 != 0; - - case pftPositive: - return edge.WindCnt2 > 0; - - default: - return edge.WindCnt2 < 0; - } - - break; - - case ctUnion: - - switch( pft2 ) - { - case pftEvenOdd: - case pftNonZero: - return edge.WindCnt2 == 0; - - case pftPositive: - return edge.WindCnt2 <= 0; - - default: - return edge.WindCnt2 >= 0; - } - - break; - - case ctDifference: - - if( edge.PolyTyp == ptSubject ) - switch( pft2 ) - { - case pftEvenOdd: - case pftNonZero: - return edge.WindCnt2 == 0; - - case pftPositive: - return edge.WindCnt2 <= 0; - - default: - return edge.WindCnt2 >= 0; - } - - - else - switch( pft2 ) - { - case pftEvenOdd: - case pftNonZero: - return edge.WindCnt2 != 0; - - case pftPositive: - return edge.WindCnt2 > 0; - - default: - return edge.WindCnt2 < 0; - } - - - break; - - case ctXor: - - if( edge.WindDelta == 0 ) // XOr always contributing unless open - switch( pft2 ) - { - case pftEvenOdd: - case pftNonZero: - return edge.WindCnt2 == 0; - - case pftPositive: - return edge.WindCnt2 <= 0; - - default: - return edge.WindCnt2 >= 0; - } - - - else - return true; - - break; - - default: - return true; - } -} - - -// ------------------------------------------------------------------------------ - -OutPt* Clipper::AddLocalMinPoly( TEdge* e1, TEdge* e2, const IntPoint& Pt ) -{ - OutPt* result; - TEdge* e, * prevE; - - if( IsHorizontal( *e2 ) || ( e1->Dx > e2->Dx ) ) - { - result = AddOutPt( e1, Pt ); - e2->OutIdx = e1->OutIdx; - e1->Side = esLeft; - e2->Side = esRight; - e = e1; - - if( e->PrevInAEL == e2 ) - prevE = e2->PrevInAEL; - else - prevE = e->PrevInAEL; - } - else - { - result = AddOutPt( e2, Pt ); - e1->OutIdx = e2->OutIdx; - e1->Side = esRight; - e2->Side = esLeft; - e = e2; - - if( e->PrevInAEL == e1 ) - prevE = e1->PrevInAEL; - else - prevE = e->PrevInAEL; - } - - if( prevE && prevE->OutIdx >= 0 && prevE->Top.Y < Pt.Y && e->Top.Y < Pt.Y ) - { - cInt xPrev = TopX( *prevE, Pt.Y ); - cInt xE = TopX( *e, Pt.Y ); - - if( xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) - && SlopesEqual( IntPoint( xPrev, Pt.Y ), prevE->Top, IntPoint( xE, Pt.Y ), e->Top, - m_UseFullRange ) ) - { - OutPt* outPt = AddOutPt( prevE, Pt ); - AddJoin( result, outPt, e->Top ); - } - } - - return result; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::AddLocalMaxPoly( TEdge* e1, TEdge* e2, const IntPoint& Pt ) -{ - AddOutPt( e1, Pt ); - - if( e2->WindDelta == 0 ) - AddOutPt( e2, Pt ); - - if( e1->OutIdx == e2->OutIdx ) - { - e1->OutIdx = Unassigned; - e2->OutIdx = Unassigned; - } - else if( e1->OutIdx < e2->OutIdx ) - AppendPolygon( e1, e2 ); - else - AppendPolygon( e2, e1 ); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::AddEdgeToSEL( TEdge* edge ) -{ - // SEL pointers in PEdge are reused to build a list of horizontal edges. - // However, we don't need to worry about order with horizontal edge processing. - if( !m_SortedEdges ) - { - m_SortedEdges = edge; - edge->PrevInSEL = 0; - edge->NextInSEL = 0; - } - else - { - edge->NextInSEL = m_SortedEdges; - edge->PrevInSEL = 0; - m_SortedEdges->PrevInSEL = edge; - m_SortedEdges = edge; - } -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::PopEdgeFromSEL( TEdge*& edge ) -{ - if( !m_SortedEdges ) - return false; - - edge = m_SortedEdges; - DeleteFromSEL( m_SortedEdges ); - return true; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::CopyAELToSEL() -{ - TEdge* e = m_ActiveEdges; - - m_SortedEdges = e; - - while( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e = e->NextInAEL; - } -} - - -// ------------------------------------------------------------------------------ - -void Clipper::AddJoin( OutPt* op1, OutPt* op2, const IntPoint OffPt ) -{ - Join* j = new Join; - - j->OutPt1 = op1; - j->OutPt2 = op2; - j->OffPt = OffPt; - m_Joins.push_back( j ); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::ClearJoins() -{ - for( JoinList::size_type i = 0; i < m_Joins.size(); i++ ) - delete m_Joins[i]; - - m_Joins.resize( 0 ); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::ClearGhostJoins() -{ - for( JoinList::size_type i = 0; i < m_GhostJoins.size(); i++ ) - delete m_GhostJoins[i]; - - m_GhostJoins.resize( 0 ); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::AddGhostJoin( OutPt* op, const IntPoint OffPt ) -{ - Join* j = new Join; - - j->OutPt1 = op; - j->OutPt2 = 0; - j->OffPt = OffPt; - m_GhostJoins.push_back( j ); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::InsertLocalMinimaIntoAEL( const cInt botY ) -{ - const LocalMinimum* lm; - - while( PopLocalMinima( botY, lm ) ) - { - TEdge* lb = lm->LeftBound; - TEdge* rb = lm->RightBound; - - OutPt* Op1 = 0; - - if( !lb ) - { - // nb: don't insert LB into either AEL or SEL - InsertEdgeIntoAEL( rb, 0 ); - SetWindingCount( *rb ); - - if( IsContributing( *rb ) ) - Op1 = AddOutPt( rb, rb->Bot ); - } - else if( !rb ) - { - InsertEdgeIntoAEL( lb, 0 ); - SetWindingCount( *lb ); - - if( IsContributing( *lb ) ) - Op1 = AddOutPt( lb, lb->Bot ); - - InsertScanbeam( lb->Top.Y ); - } - else - { - InsertEdgeIntoAEL( lb, 0 ); - InsertEdgeIntoAEL( rb, lb ); - SetWindingCount( *lb ); - rb->WindCnt = lb->WindCnt; - rb->WindCnt2 = lb->WindCnt2; - - if( IsContributing( *lb ) ) - Op1 = AddLocalMinPoly( lb, rb, lb->Bot ); - - InsertScanbeam( lb->Top.Y ); - } - - if( rb ) - { - if( IsHorizontal( *rb ) ) - { - AddEdgeToSEL( rb ); - - if( rb->NextInLML ) - InsertScanbeam( rb->NextInLML->Top.Y ); - } - else - InsertScanbeam( rb->Top.Y ); - } - - if( !lb || !rb ) - continue; - - // if any output polygons share an edge, they'll need joining later ... - if( Op1 && IsHorizontal( *rb ) - && m_GhostJoins.size() > 0 && (rb->WindDelta != 0) ) - { - for( JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i ) - { - Join* jr = m_GhostJoins[i]; - - // if the horizontal Rb and a 'ghost' horizontal overlap, then convert - // the 'ghost' join to a real join ready for later ... - if( HorzSegmentsOverlap( jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X ) ) - AddJoin( jr->OutPt1, Op1, jr->OffPt ); - } - } - - if( lb->OutIdx >= 0 && lb->PrevInAEL - && lb->PrevInAEL->Curr.X == lb->Bot.X - && lb->PrevInAEL->OutIdx >= 0 - && SlopesEqual( lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, - m_UseFullRange ) - && (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0) ) - { - OutPt* Op2 = AddOutPt( lb->PrevInAEL, lb->Bot ); - AddJoin( Op1, Op2, lb->Top ); - } - - if( lb->NextInAEL != rb ) - { - if( rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 - && SlopesEqual( rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, - m_UseFullRange ) - && (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0) ) - { - OutPt* Op2 = AddOutPt( rb->PrevInAEL, rb->Bot ); - AddJoin( Op1, Op2, rb->Top ); - } - - TEdge* e = lb->NextInAEL; - - if( e ) - { - while( e != rb ) - { - // nb: For calculating winding counts etc, IntersectEdges() assumes - // that param1 will be to the Right of param2 ABOVE the intersection ... - IntersectEdges( rb, e, lb->Curr ); // order important here - e = e->NextInAEL; - } - } - } - } -} - - -// ------------------------------------------------------------------------------ - -void Clipper::DeleteFromSEL( TEdge* e ) -{ - TEdge* SelPrev = e->PrevInSEL; - TEdge* SelNext = e->NextInSEL; - - if( !SelPrev && !SelNext && (e != m_SortedEdges) ) - return; // already deleted - - if( SelPrev ) - SelPrev->NextInSEL = SelNext; - else - m_SortedEdges = SelNext; - - if( SelNext ) - SelNext->PrevInSEL = SelPrev; - - e->NextInSEL = 0; - e->PrevInSEL = 0; -} - - -// ------------------------------------------------------------------------------ - -#ifdef use_xyz -void Clipper::SetZ( IntPoint& pt, TEdge& e1, TEdge& e2 ) -{ - if( pt.Z != 0 || !m_ZFill ) - return; - else if( pt == e1.Bot ) - pt.Z = e1.Bot.Z; - else if( pt == e1.Top ) - pt.Z = e1.Top.Z; - else if( pt == e2.Bot ) - pt.Z = e2.Bot.Z; - else if( pt == e2.Top ) - pt.Z = e2.Top.Z; - else - m_ZFill( e1.Bot, e1.Top, e2.Bot, e2.Top, pt ); -} - - -// ------------------------------------------------------------------------------ -#endif - -void Clipper::IntersectEdges( TEdge* e1, TEdge* e2, IntPoint& Pt ) -{ - bool e1Contributing = ( e1->OutIdx >= 0 ); - bool e2Contributing = ( e2->OutIdx >= 0 ); - -#ifdef use_xyz - SetZ( Pt, *e1, *e2 ); -#endif - -#ifdef use_lines - - // if either edge is on an OPEN path ... - if( e1->WindDelta == 0 || e2->WindDelta == 0 ) - { - // ignore subject-subject open path intersections UNLESS they - // are both open paths, AND they are both 'contributing maximas' ... - if( e1->WindDelta == 0 && e2->WindDelta == 0 ) - return; - - // if intersecting a subj line with a subj poly ... - else if( e1->PolyTyp == e2->PolyTyp - && e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion ) - { - if( e1->WindDelta == 0 ) - { - if( e2Contributing ) - { - AddOutPt( e1, Pt ); - - if( e1Contributing ) - e1->OutIdx = Unassigned; - } - } - else - { - if( e1Contributing ) - { - AddOutPt( e2, Pt ); - - if( e2Contributing ) - e2->OutIdx = Unassigned; - } - } - } - else if( e1->PolyTyp != e2->PolyTyp ) - { - // toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... - if( (e1->WindDelta == 0) && abs( e2->WindCnt ) == 1 - && (m_ClipType != ctUnion || e2->WindCnt2 == 0) ) - { - AddOutPt( e1, Pt ); - - if( e1Contributing ) - e1->OutIdx = Unassigned; - } - else if( (e2->WindDelta == 0) && (abs( e1->WindCnt ) == 1) - && (m_ClipType != ctUnion || e1->WindCnt2 == 0) ) - { - AddOutPt( e2, Pt ); - - if( e2Contributing ) - e2->OutIdx = Unassigned; - } - } - - return; - } - -#endif - - // update winding counts... - // assumes that e1 will be to the Right of e2 ABOVE the intersection - if( e1->PolyTyp == e2->PolyTyp ) - { - if( IsEvenOddFillType( *e1 ) ) - { - int oldE1WindCnt = e1->WindCnt; - e1->WindCnt = e2->WindCnt; - e2->WindCnt = oldE1WindCnt; - } - else - { - if( e1->WindCnt + e2->WindDelta == 0 ) - e1->WindCnt = -e1->WindCnt; - else - e1->WindCnt += e2->WindDelta; - - if( e2->WindCnt - e1->WindDelta == 0 ) - e2->WindCnt = -e2->WindCnt; - else - e2->WindCnt -= e1->WindDelta; - } - } - else - { - if( !IsEvenOddFillType( *e2 ) ) - e1->WindCnt2 += e2->WindDelta; - else - e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; - - if( !IsEvenOddFillType( *e1 ) ) - e2->WindCnt2 -= e1->WindDelta; - else - e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; - } - - PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; - - if( e1->PolyTyp == ptSubject ) - { - e1FillType = m_SubjFillType; - e1FillType2 = m_ClipFillType; - } - else - { - e1FillType = m_ClipFillType; - e1FillType2 = m_SubjFillType; - } - - if( e2->PolyTyp == ptSubject ) - { - e2FillType = m_SubjFillType; - e2FillType2 = m_ClipFillType; - } - else - { - e2FillType = m_ClipFillType; - e2FillType2 = m_SubjFillType; - } - - cInt e1Wc, e2Wc; - - switch( e1FillType ) - { - case pftPositive: - e1Wc = e1->WindCnt; break; - - case pftNegative: - e1Wc = -e1->WindCnt; break; - - default: - e1Wc = Abs( e1->WindCnt ); - } - - switch( e2FillType ) - { - case pftPositive: - e2Wc = e2->WindCnt; break; - - case pftNegative: - e2Wc = -e2->WindCnt; break; - - default: - e2Wc = Abs( e2->WindCnt ); - } - - if( e1Contributing && e2Contributing ) - { - if( (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) - || (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) - { - AddLocalMaxPoly( e1, e2, Pt ); - } - else - { - AddOutPt( e1, Pt ); - AddOutPt( e2, Pt ); - SwapSides( *e1, *e2 ); - SwapPolyIndexes( *e1, *e2 ); - } - } - else if( e1Contributing ) - { - if( e2Wc == 0 || e2Wc == 1 ) - { - AddOutPt( e1, Pt ); - SwapSides( *e1, *e2 ); - SwapPolyIndexes( *e1, *e2 ); - } - } - else if( e2Contributing ) - { - if( e1Wc == 0 || e1Wc == 1 ) - { - AddOutPt( e2, Pt ); - SwapSides( *e1, *e2 ); - SwapPolyIndexes( *e1, *e2 ); - } - } - else if( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1) ) - { - // neither edge is currently contributing ... - - cInt e1Wc2, e2Wc2; - - switch( e1FillType2 ) - { - case pftPositive: - e1Wc2 = e1->WindCnt2; break; - - case pftNegative: - e1Wc2 = -e1->WindCnt2; break; - - default: - e1Wc2 = Abs( e1->WindCnt2 ); - } - - switch( e2FillType2 ) - { - case pftPositive: - e2Wc2 = e2->WindCnt2; break; - - case pftNegative: - e2Wc2 = -e2->WindCnt2; break; - - default: - e2Wc2 = Abs( e2->WindCnt2 ); - } - - if( e1->PolyTyp != e2->PolyTyp ) - { - AddLocalMinPoly( e1, e2, Pt ); - } - else if( e1Wc == 1 && e2Wc == 1 ) - switch( m_ClipType ) - { - case ctIntersection: - - if( e1Wc2 > 0 && e2Wc2 > 0 ) - AddLocalMinPoly( e1, e2, Pt ); - - break; - - case ctUnion: - - if( e1Wc2 <= 0 && e2Wc2 <= 0 ) - AddLocalMinPoly( e1, e2, Pt ); - - break; - - case ctDifference: - - if( ( (e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0) ) - || ( (e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0) ) ) - AddLocalMinPoly( e1, e2, Pt ); - - break; - - case ctXor: - AddLocalMinPoly( e1, e2, Pt ); - } - - - else - SwapSides( *e1, *e2 ); - } -} - - -// ------------------------------------------------------------------------------ - -void Clipper::SetHoleState( TEdge* e, OutRec* outrec ) -{ - TEdge* e2 = e->PrevInAEL; - TEdge* eTmp = 0; - - while( e2 ) - { - if( e2->OutIdx >= 0 && e2->WindDelta != 0 ) - { - if( !eTmp ) - eTmp = e2; - else if( eTmp->OutIdx == e2->OutIdx ) - eTmp = 0; - } - - e2 = e2->PrevInAEL; - } - - if( !eTmp ) - { - outrec->FirstLeft = 0; - outrec->IsHole = false; - } - else - { - outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; - outrec->IsHole = !outrec->FirstLeft->IsHole; - } -} - - -// ------------------------------------------------------------------------------ - -OutRec* GetLowermostRec( OutRec* outRec1, OutRec* outRec2 ) -{ - // work out which polygon fragment has the correct hole state ... - if( !outRec1->BottomPt ) - outRec1->BottomPt = GetBottomPt( outRec1->Pts ); - - if( !outRec2->BottomPt ) - outRec2->BottomPt = GetBottomPt( outRec2->Pts ); - - OutPt* OutPt1 = outRec1->BottomPt; - OutPt* OutPt2 = outRec2->BottomPt; - - if( OutPt1->Pt.Y > OutPt2->Pt.Y ) - return outRec1; - else if( OutPt1->Pt.Y < OutPt2->Pt.Y ) - return outRec2; - else if( OutPt1->Pt.X < OutPt2->Pt.X ) - return outRec1; - else if( OutPt1->Pt.X > OutPt2->Pt.X ) - return outRec2; - else if( OutPt1->Next == OutPt1 ) - return outRec2; - else if( OutPt2->Next == OutPt2 ) - return outRec1; - else if( FirstIsBottomPt( OutPt1, OutPt2 ) ) - return outRec1; - else - return outRec2; -} - - -// ------------------------------------------------------------------------------ - -bool OutRec1RightOfOutRec2( OutRec* outRec1, OutRec* outRec2 ) -{ - do - { - outRec1 = outRec1->FirstLeft; - - if( outRec1 == outRec2 ) - return true; - } while( outRec1 ); - - return false; -} - - -// ------------------------------------------------------------------------------ - -OutRec* Clipper::GetOutRec( int Idx ) -{ - OutRec* outrec = m_PolyOuts[Idx]; - - while( outrec != m_PolyOuts[outrec->Idx] ) - outrec = m_PolyOuts[outrec->Idx]; - - return outrec; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::AppendPolygon( TEdge* e1, TEdge* e2 ) -{ - // get the start and ends of both output polygons ... - OutRec* outRec1 = m_PolyOuts[e1->OutIdx]; - OutRec* outRec2 = m_PolyOuts[e2->OutIdx]; - - OutRec* holeStateRec; - - if( OutRec1RightOfOutRec2( outRec1, outRec2 ) ) - holeStateRec = outRec2; - else if( OutRec1RightOfOutRec2( outRec2, outRec1 ) ) - holeStateRec = outRec1; - else - holeStateRec = GetLowermostRec( outRec1, outRec2 ); - - // get the start and ends of both output polygons and - // join e2 poly onto e1 poly and delete pointers to e2 ... - - OutPt* p1_lft = outRec1->Pts; - OutPt* p1_rt = p1_lft->Prev; - OutPt* p2_lft = outRec2->Pts; - OutPt* p2_rt = p2_lft->Prev; - - // join e2 poly onto e1 poly and delete pointers to e2 ... - if( e1->Side == esLeft ) - { - if( e2->Side == esLeft ) - { - // z y x a b c - ReversePolyPtLinks( p2_lft ); - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - outRec1->Pts = p2_rt; - } - else - { - // x y z a b c - p2_rt->Next = p1_lft; - p1_lft->Prev = p2_rt; - p2_lft->Prev = p1_rt; - p1_rt->Next = p2_lft; - outRec1->Pts = p2_lft; - } - } - else - { - if( e2->Side == esRight ) - { - // a b c z y x - ReversePolyPtLinks( p2_lft ); - p1_rt->Next = p2_rt; - p2_rt->Prev = p1_rt; - p2_lft->Next = p1_lft; - p1_lft->Prev = p2_lft; - } - else - { - // a b c x y z - p1_rt->Next = p2_lft; - p2_lft->Prev = p1_rt; - p1_lft->Prev = p2_rt; - p2_rt->Next = p1_lft; - } - } - - outRec1->BottomPt = 0; - - if( holeStateRec == outRec2 ) - { - if( outRec2->FirstLeft != outRec1 ) - outRec1->FirstLeft = outRec2->FirstLeft; - - outRec1->IsHole = outRec2->IsHole; - } - - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->FirstLeft = outRec1; - - int OKIdx = e1->OutIdx; - int ObsoleteIdx = e2->OutIdx; - - e1->OutIdx = Unassigned; // nb: safe because we only get here via AddLocalMaxPoly - e2->OutIdx = Unassigned; - - TEdge* e = m_ActiveEdges; - - while( e ) - { - if( e->OutIdx == ObsoleteIdx ) - { - e->OutIdx = OKIdx; - e->Side = e1->Side; - break; - } - - e = e->NextInAEL; - } - - outRec2->Idx = outRec1->Idx; -} - - -// ------------------------------------------------------------------------------ - -OutPt* Clipper::AddOutPt( TEdge* e, const IntPoint& pt ) -{ - if( e->OutIdx < 0 ) - { - OutRec* outRec = CreateOutRec(); - outRec->IsOpen = (e->WindDelta == 0); - OutPt* newOp = new OutPt; - outRec->Pts = newOp; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = newOp; - newOp->Prev = newOp; - - if( !outRec->IsOpen ) - SetHoleState( e, outRec ); - - e->OutIdx = outRec->Idx; - return newOp; - } - else - { - OutRec* outRec = m_PolyOuts[e->OutIdx]; - // OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' - OutPt* op = outRec->Pts; - - bool ToFront = (e->Side == esLeft); - - if( ToFront && (pt == op->Pt) ) - return op; - else if( !ToFront && (pt == op->Prev->Pt) ) - return op->Prev; - - OutPt* newOp = new OutPt; - newOp->Idx = outRec->Idx; - newOp->Pt = pt; - newOp->Next = op; - newOp->Prev = op->Prev; - newOp->Prev->Next = newOp; - op->Prev = newOp; - - if( ToFront ) - outRec->Pts = newOp; - - return newOp; - } -} - - -// ------------------------------------------------------------------------------ - -OutPt* Clipper::GetLastOutPt( TEdge* e ) -{ - OutRec* outRec = m_PolyOuts[e->OutIdx]; - - if( e->Side == esLeft ) - return outRec->Pts; - else - return outRec->Pts->Prev; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::ProcessHorizontals() -{ - TEdge* horzEdge; - - while( PopEdgeFromSEL( horzEdge ) ) - ProcessHorizontal( horzEdge ); -} - - -// ------------------------------------------------------------------------------ - -inline bool IsMinima( TEdge* e ) -{ - return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); -} - - -// ------------------------------------------------------------------------------ - -inline bool IsMaxima( TEdge* e, const cInt Y ) -{ - return e && e->Top.Y == Y && !e->NextInLML; -} - - -// ------------------------------------------------------------------------------ - -inline bool IsIntermediate( TEdge* e, const cInt Y ) -{ - return e->Top.Y == Y && e->NextInLML; -} - - -// ------------------------------------------------------------------------------ - -TEdge* GetMaximaPair( TEdge* e ) -{ - if( (e->Next->Top == e->Top) && !e->Next->NextInLML ) - return e->Next; - else if( (e->Prev->Top == e->Top) && !e->Prev->NextInLML ) - return e->Prev; - else - return 0; -} - - -// ------------------------------------------------------------------------------ - -TEdge* GetMaximaPairEx( TEdge* e ) -{ - // as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) - TEdge* result = GetMaximaPair( e ); - - if( result && ( result->OutIdx == Skip - || ( result->NextInAEL == result->PrevInAEL && !IsHorizontal( *result ) ) ) ) - return 0; - - return result; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::SwapPositionsInSEL( TEdge* Edge1, TEdge* Edge2 ) -{ - if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) - return; - - if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) - return; - - if( Edge1->NextInSEL == Edge2 ) - { - TEdge* Next = Edge2->NextInSEL; - - if( Next ) - Next->PrevInSEL = Edge1; - - TEdge* Prev = Edge1->PrevInSEL; - - if( Prev ) - Prev->NextInSEL = Edge2; - - Edge2->PrevInSEL = Prev; - Edge2->NextInSEL = Edge1; - Edge1->PrevInSEL = Edge2; - Edge1->NextInSEL = Next; - } - else if( Edge2->NextInSEL == Edge1 ) - { - TEdge* Next = Edge1->NextInSEL; - - if( Next ) - Next->PrevInSEL = Edge2; - - TEdge* Prev = Edge2->PrevInSEL; - - if( Prev ) - Prev->NextInSEL = Edge1; - - Edge1->PrevInSEL = Prev; - Edge1->NextInSEL = Edge2; - Edge2->PrevInSEL = Edge1; - Edge2->NextInSEL = Next; - } - else - { - TEdge* Next = Edge1->NextInSEL; - TEdge* Prev = Edge1->PrevInSEL; - Edge1->NextInSEL = Edge2->NextInSEL; - - if( Edge1->NextInSEL ) - Edge1->NextInSEL->PrevInSEL = Edge1; - - Edge1->PrevInSEL = Edge2->PrevInSEL; - - if( Edge1->PrevInSEL ) - Edge1->PrevInSEL->NextInSEL = Edge1; - - Edge2->NextInSEL = Next; - - if( Edge2->NextInSEL ) - Edge2->NextInSEL->PrevInSEL = Edge2; - - Edge2->PrevInSEL = Prev; - - if( Edge2->PrevInSEL ) - Edge2->PrevInSEL->NextInSEL = Edge2; - } - - if( !Edge1->PrevInSEL ) - m_SortedEdges = Edge1; - else if( !Edge2->PrevInSEL ) - m_SortedEdges = Edge2; -} - - -// ------------------------------------------------------------------------------ - -TEdge* GetNextInAEL( TEdge* e, Direction dir ) -{ - return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; -} - - -// ------------------------------------------------------------------------------ - -void GetHorzDirection( TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right ) -{ - if( HorzEdge.Bot.X < HorzEdge.Top.X ) - { - Left = HorzEdge.Bot.X; - Right = HorzEdge.Top.X; - Dir = dLeftToRight; - } - else - { - Left = HorzEdge.Top.X; - Right = HorzEdge.Bot.X; - Dir = dRightToLeft; - } -} - - -// ------------------------------------------------------------------------ - -/******************************************************************************* -* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * -* Bottom of a scanbeam) are processed as if layered. The order in which HEs * -* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * -* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * -* and with other non-horizontal edges [*]. Once these intersections are * -* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * -* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * -*******************************************************************************/ - -void Clipper::ProcessHorizontal( TEdge* horzEdge ) -{ - Direction dir; - cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->WindDelta == 0); - - GetHorzDirection( *horzEdge, dir, horzLeft, horzRight ); - - TEdge* eLastHorz = horzEdge, * eMaxPair = 0; - - while( eLastHorz->NextInLML && IsHorizontal( *eLastHorz->NextInLML ) ) - eLastHorz = eLastHorz->NextInLML; - - if( !eLastHorz->NextInLML ) - eMaxPair = GetMaximaPair( eLastHorz ); - - MaximaList::const_iterator maxIt; - MaximaList::const_reverse_iterator maxRit; - - if( m_Maxima.size() > 0 ) - { - // get the first maxima in range (X) ... - if( dir == dLeftToRight ) - { - maxIt = m_Maxima.begin(); - - while( maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X ) - maxIt++; - - if( maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X ) - maxIt = m_Maxima.end(); - } - else - { - maxRit = m_Maxima.rbegin(); - - while( maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X ) - maxRit++; - - if( maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X ) - maxRit = m_Maxima.rend(); - } - } - - OutPt* op1 = 0; - - for( ; ; ) // loop through consec. horizontal edges - { - bool IsLastHorz = (horzEdge == eLastHorz); - TEdge* e = GetNextInAEL( horzEdge, dir ); - - while( e ) - { - // this code block inserts extra coords into horizontal edges (in output - // polygons) whereever maxima touch these horizontal edges. This helps - // 'simplifying' polygons (ie if the Simplify property is set). - if( m_Maxima.size() > 0 ) - { - if( dir == dLeftToRight ) - { - while( maxIt != m_Maxima.end() && *maxIt < e->Curr.X ) - { - if( horzEdge->OutIdx >= 0 && !IsOpen ) - AddOutPt( horzEdge, IntPoint( *maxIt, horzEdge->Bot.Y ) ); - - maxIt++; - } - } - else - { - while( maxRit != m_Maxima.rend() && *maxRit > e->Curr.X ) - { - if( horzEdge->OutIdx >= 0 && !IsOpen ) - AddOutPt( horzEdge, IntPoint( *maxRit, horzEdge->Bot.Y ) ); - - maxRit++; - } - } - } - - ; - - if( (dir == dLeftToRight && e->Curr.X > horzRight) - || (dir == dRightToLeft && e->Curr.X < horzLeft) ) - break; - - // Also break if we've got to the end of an intermediate horizontal edge ... - // nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. - if( e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML - && e->Dx < horzEdge->NextInLML->Dx ) - break; - - if( horzEdge->OutIdx >= 0 && !IsOpen ) // note: may be done multiple times - { -#ifdef use_xyz - - if( dir == dLeftToRight ) - SetZ( e->Curr, *horzEdge, *e ); - else - SetZ( e->Curr, *e, *horzEdge ); - -#endif - op1 = AddOutPt( horzEdge, e->Curr ); - TEdge* eNextHorz = m_SortedEdges; - - while( eNextHorz ) - { - if( eNextHorz->OutIdx >= 0 - && HorzSegmentsOverlap( horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X ) ) - { - OutPt* op2 = GetLastOutPt( eNextHorz ); - AddJoin( op2, op1, eNextHorz->Top ); - } - - eNextHorz = eNextHorz->NextInSEL; - } - - AddGhostJoin( op1, horzEdge->Bot ); - } - - // OK, so far we're still in range of the horizontal Edge but make sure - // we're at the last of consec. horizontals when matching with eMaxPair - if( e == eMaxPair && IsLastHorz ) - { - if( horzEdge->OutIdx >= 0 ) - AddLocalMaxPoly( horzEdge, eMaxPair, horzEdge->Top ); - - DeleteFromAEL( horzEdge ); - DeleteFromAEL( eMaxPair ); - return; - } - - if( dir == dLeftToRight ) - { - IntPoint Pt = IntPoint( e->Curr.X, horzEdge->Curr.Y ); - IntersectEdges( horzEdge, e, Pt ); - } - else - { - IntPoint Pt = IntPoint( e->Curr.X, horzEdge->Curr.Y ); - IntersectEdges( e, horzEdge, Pt ); - } - - TEdge* eNext = GetNextInAEL( e, dir ); - SwapPositionsInAEL( horzEdge, e ); - e = eNext; - } // end while(e) - - // Break out of loop if HorzEdge.NextInLML is not also horizontal ... - if( !horzEdge->NextInLML || !IsHorizontal( *horzEdge->NextInLML ) ) - break; - - UpdateEdgeIntoAEL( horzEdge ); - - if( horzEdge->OutIdx >= 0 ) - AddOutPt( horzEdge, horzEdge->Bot ); - - GetHorzDirection( *horzEdge, dir, horzLeft, horzRight ); - } // end for (;;) - - if( horzEdge->OutIdx >= 0 && !op1 ) - { - op1 = GetLastOutPt( horzEdge ); - TEdge* eNextHorz = m_SortedEdges; - - while( eNextHorz ) - { - if( eNextHorz->OutIdx >= 0 - && HorzSegmentsOverlap( horzEdge->Bot.X, - horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X ) ) - { - OutPt* op2 = GetLastOutPt( eNextHorz ); - AddJoin( op2, op1, eNextHorz->Top ); - } - - eNextHorz = eNextHorz->NextInSEL; - } - - AddGhostJoin( op1, horzEdge->Top ); - } - - if( horzEdge->NextInLML ) - { - if( horzEdge->OutIdx >= 0 ) - { - op1 = AddOutPt( horzEdge, horzEdge->Top ); - UpdateEdgeIntoAEL( horzEdge ); - - if( horzEdge->WindDelta == 0 ) - return; - - // nb: HorzEdge is no longer horizontal here - TEdge* ePrev = horzEdge->PrevInAEL; - TEdge* eNext = horzEdge->NextInAEL; - - if( ePrev && ePrev->Curr.X == horzEdge->Bot.X - && ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 - && ( ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y - && SlopesEqual( *horzEdge, *ePrev, m_UseFullRange ) ) ) - { - OutPt* op2 = AddOutPt( ePrev, horzEdge->Bot ); - AddJoin( op1, op2, horzEdge->Top ); - } - else if( eNext && eNext->Curr.X == horzEdge->Bot.X - && eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 - && eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y - && SlopesEqual( *horzEdge, *eNext, m_UseFullRange ) ) - { - OutPt* op2 = AddOutPt( eNext, horzEdge->Bot ); - AddJoin( op1, op2, horzEdge->Top ); - } - } - else - UpdateEdgeIntoAEL( horzEdge ); - } - else - { - if( horzEdge->OutIdx >= 0 ) - AddOutPt( horzEdge, horzEdge->Top ); - - DeleteFromAEL( horzEdge ); - } -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::ProcessIntersections( const cInt topY ) -{ - if( !m_ActiveEdges ) - return true; - - try - { - BuildIntersectList( topY ); - size_t IlSize = m_IntersectList.size(); - - if( IlSize == 0 ) - return true; - - if( IlSize == 1 || FixupIntersectionOrder() ) - ProcessIntersectList(); - else - return false; - } - catch( ... ) - { - m_SortedEdges = 0; - DisposeIntersectNodes(); - throw clipperException( "ProcessIntersections error" ); - } - m_SortedEdges = 0; - return true; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::DisposeIntersectNodes() -{ - for( size_t i = 0; i < m_IntersectList.size(); ++i ) - delete m_IntersectList[i]; - - m_IntersectList.clear(); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::BuildIntersectList( const cInt topY ) -{ - if( !m_ActiveEdges ) - return; - - // prepare for sorting ... - TEdge* e = m_ActiveEdges; - m_SortedEdges = e; - - while( e ) - { - e->PrevInSEL = e->PrevInAEL; - e->NextInSEL = e->NextInAEL; - e->Curr.X = TopX( *e, topY ); - e = e->NextInAEL; - } - - // bubblesort ... - bool isModified; - - do - { - isModified = false; - e = m_SortedEdges; - - while( e->NextInSEL ) - { - TEdge* eNext = e->NextInSEL; - IntPoint Pt; - - if( e->Curr.X > eNext->Curr.X ) - { - IntersectPoint( *e, *eNext, Pt ); - - if( Pt.Y < topY ) - Pt = IntPoint( TopX( *e, topY ), topY ); - - IntersectNode* newNode = new IntersectNode; - newNode->Edge1 = e; - newNode->Edge2 = eNext; - newNode->Pt = Pt; - m_IntersectList.push_back( newNode ); - - SwapPositionsInSEL( e, eNext ); - isModified = true; - } - else - e = eNext; - } - - if( e->PrevInSEL ) - e->PrevInSEL->NextInSEL = 0; - else - break; - } while( isModified ); - - m_SortedEdges = 0; // important -} - - -// ------------------------------------------------------------------------------ - - -void Clipper::ProcessIntersectList() -{ - for( size_t i = 0; i < m_IntersectList.size(); ++i ) - { - IntersectNode* iNode = m_IntersectList[i]; - { - IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt ); - SwapPositionsInAEL( iNode->Edge1, iNode->Edge2 ); - } - delete iNode; - } - - m_IntersectList.clear(); -} - - -// ------------------------------------------------------------------------------ - -bool IntersectListSort( IntersectNode* node1, IntersectNode* node2 ) -{ - return node2->Pt.Y < node1->Pt.Y; -} - - -// ------------------------------------------------------------------------------ - -inline bool EdgesAdjacent( const IntersectNode& inode ) -{ - return (inode.Edge1->NextInSEL == inode.Edge2) - || (inode.Edge1->PrevInSEL == inode.Edge2); -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::FixupIntersectionOrder() -{ - // pre-condition: intersections are sorted Bottom-most first. - // Now it's crucial that intersections are made only between adjacent edges, - // so to ensure this the order of intersections may need adjusting ... - CopyAELToSEL(); - std::sort( m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort ); - size_t cnt = m_IntersectList.size(); - - for( size_t i = 0; i < cnt; ++i ) - { - if( !EdgesAdjacent( *m_IntersectList[i] ) ) - { - size_t j = i + 1; - - while( j < cnt && !EdgesAdjacent( *m_IntersectList[j] ) ) - j++; - - if( j == cnt ) - return false; - - std::swap( m_IntersectList[i], m_IntersectList[j] ); - } - - SwapPositionsInSEL( m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2 ); - } - - return true; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::DoMaxima( TEdge* e ) -{ - TEdge* eMaxPair = GetMaximaPairEx( e ); - - if( !eMaxPair ) - { - if( e->OutIdx >= 0 ) - AddOutPt( e, e->Top ); - - DeleteFromAEL( e ); - return; - } - - TEdge* eNext = e->NextInAEL; - - while( eNext && eNext != eMaxPair ) - { - IntersectEdges( e, eNext, e->Top ); - SwapPositionsInAEL( e, eNext ); - eNext = e->NextInAEL; - } - - if( e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned ) - { - DeleteFromAEL( e ); - DeleteFromAEL( eMaxPair ); - } - else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) - { - if( e->OutIdx >= 0 ) - AddLocalMaxPoly( e, eMaxPair, e->Top ); - - DeleteFromAEL( e ); - DeleteFromAEL( eMaxPair ); - } - -#ifdef use_lines - else if( e->WindDelta == 0 ) - { - if( e->OutIdx >= 0 ) - { - AddOutPt( e, e->Top ); - e->OutIdx = Unassigned; - } - - DeleteFromAEL( e ); - - if( eMaxPair->OutIdx >= 0 ) - { - AddOutPt( eMaxPair, e->Top ); - eMaxPair->OutIdx = Unassigned; - } - - DeleteFromAEL( eMaxPair ); - } -#endif - else - throw clipperException( "DoMaxima error" ); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::ProcessEdgesAtTopOfScanbeam( const cInt topY ) -{ - TEdge* e = m_ActiveEdges; - - while( e ) - { - // 1. process maxima, treating them as if they're 'bent' horizontal edges, - // but exclude maxima with horizontal edges. nb: e can't be a horizontal. - bool IsMaximaEdge = IsMaxima( e, topY ); - - if( IsMaximaEdge ) - { - TEdge* eMaxPair = GetMaximaPairEx( e ); - IsMaximaEdge = ( !eMaxPair || !IsHorizontal( *eMaxPair ) ); - } - - if( IsMaximaEdge ) - { - if( m_StrictSimple ) - m_Maxima.push_back( e->Top.X ); - - TEdge* ePrev = e->PrevInAEL; - DoMaxima( e ); - - if( !ePrev ) - e = m_ActiveEdges; - else - e = ePrev->NextInAEL; - } - else - { - // 2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... - if( IsIntermediate( e, topY ) && IsHorizontal( *e->NextInLML ) ) - { - UpdateEdgeIntoAEL( e ); - - if( e->OutIdx >= 0 ) - AddOutPt( e, e->Bot ); - - AddEdgeToSEL( e ); - } - else - { - e->Curr.X = TopX( *e, topY ); - e->Curr.Y = topY; -#ifdef use_xyz - e->Curr.Z = topY == e->Top.Y ? e->Top.Z : (topY == e->Bot.Y ? e->Bot.Z : 0); -#endif - } - - // When StrictlySimple and 'e' is being touched by another edge, then - // make sure both edges have a vertex here ... - if( m_StrictSimple ) - { - TEdge* ePrev = e->PrevInAEL; - - if( (e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) - && (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0) ) - { - IntPoint pt = e->Curr; -#ifdef use_xyz - SetZ( pt, *ePrev, *e ); -#endif - OutPt* op = AddOutPt( ePrev, pt ); - OutPt* op2 = AddOutPt( e, pt ); - AddJoin( op, op2, pt ); // StrictlySimple (type-3) join - } - } - - e = e->NextInAEL; - } - } - - // 3. Process horizontals at the Top of the scanbeam ... - m_Maxima.sort(); - ProcessHorizontals(); - m_Maxima.clear(); - - // 4. Promote intermediate vertices ... - e = m_ActiveEdges; - - while( e ) - { - if( IsIntermediate( e, topY ) ) - { - OutPt* op = 0; - - if( e->OutIdx >= 0 ) - op = AddOutPt( e, e->Top ); - - UpdateEdgeIntoAEL( e ); - - // if output polygons share an edge, they'll need joining later ... - TEdge* ePrev = e->PrevInAEL; - TEdge* eNext = e->NextInAEL; - - if( ePrev && ePrev->Curr.X == e->Bot.X - && ePrev->Curr.Y == e->Bot.Y && op - && ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y - && SlopesEqual( e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange ) - && (e->WindDelta != 0) && (ePrev->WindDelta != 0) ) - { - OutPt* op2 = AddOutPt( ePrev, e->Bot ); - AddJoin( op, op2, e->Top ); - } - else if( eNext && eNext->Curr.X == e->Bot.X - && eNext->Curr.Y == e->Bot.Y && op - && eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y - && SlopesEqual( e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange ) - && (e->WindDelta != 0) && (eNext->WindDelta != 0) ) - { - OutPt* op2 = AddOutPt( eNext, e->Bot ); - AddJoin( op, op2, e->Top ); - } - } - - e = e->NextInAEL; - } -} - - -// ------------------------------------------------------------------------------ - -void Clipper::FixupOutPolyline( OutRec& outrec ) -{ - OutPt* pp = outrec.Pts; - OutPt* lastPP = pp->Prev; - - while( pp != lastPP ) - { - pp = pp->Next; - - if( pp->Pt == pp->Prev->Pt ) - { - if( pp == lastPP ) - lastPP = pp->Prev; - - OutPt* tmpPP = pp->Prev; - tmpPP->Next = pp->Next; - pp->Next->Prev = tmpPP; - delete pp; - pp = tmpPP; - } - } - - if( pp == pp->Prev ) - { - DisposeOutPts( pp ); - outrec.Pts = 0; - return; - } -} - - -// ------------------------------------------------------------------------------ - -void Clipper::FixupOutPolygon( OutRec& outrec ) -{ - // FixupOutPolygon() - removes duplicate points and simplifies consecutive - // parallel edges by removing the middle vertex. - OutPt* lastOK = 0; - - outrec.BottomPt = 0; - OutPt* pp = outrec.Pts; - bool preserveCol = m_PreserveCollinear || m_StrictSimple; - - for( ; ; ) - { - if( pp->Prev == pp || pp->Prev == pp->Next ) - { - DisposeOutPts( pp ); - outrec.Pts = 0; - return; - } - - // test for duplicate points and collinear edges ... - if( (pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) - || ( SlopesEqual( pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange ) - && ( !preserveCol - || !Pt2IsBetweenPt1AndPt3( pp->Prev->Pt, pp->Pt, pp->Next->Pt ) ) ) ) - { - lastOK = 0; - OutPt* tmp = pp; - pp->Prev->Next = pp->Next; - pp->Next->Prev = pp->Prev; - pp = pp->Prev; - delete tmp; - } - else if( pp == lastOK ) - break; - else - { - if( !lastOK ) - lastOK = pp; - - pp = pp->Next; - } - } - - outrec.Pts = pp; -} - - -// ------------------------------------------------------------------------------ - -int PointCount( OutPt* Pts ) -{ - if( !Pts ) - return 0; - - int result = 0; - OutPt* p = Pts; - - do - { - result++; - p = p->Next; - } while( p != Pts ); - - return result; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::BuildResult( Paths& polys ) -{ - polys.reserve( m_PolyOuts.size() ); - - for( PolyOutList::size_type ii = 0; ii < m_PolyOuts.size(); ++ii ) - { - if( !m_PolyOuts[ii]->Pts ) - continue; - - Path pg; - OutPt* p = m_PolyOuts[ii]->Pts->Prev; - int cnt = PointCount( p ); - - if( cnt < 2 ) - continue; - - pg.reserve( cnt ); - - for( int jj = 0; jj < cnt; ++jj ) - { - pg.push_back( p->Pt ); - p = p->Prev; - } - - polys.push_back( pg ); - } -} - - -// ------------------------------------------------------------------------------ - -void Clipper::BuildResult2( PolyTree& polytree ) -{ - polytree.Clear(); - polytree.AllNodes.reserve( m_PolyOuts.size() ); - - // add each output polygon/contour to polytree ... - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++ ) - { - OutRec* outRec = m_PolyOuts[i]; - int cnt = PointCount( outRec->Pts ); - - if( (outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3) ) - continue; - - FixHoleLinkage( *outRec ); - PolyNode* pn = new PolyNode(); - // nb: polytree takes ownership of all the PolyNodes - polytree.AllNodes.push_back( pn ); - outRec->PolyNd = pn; - pn->Parent = 0; - pn->Index = 0; - pn->Contour.reserve( cnt ); - OutPt* op = outRec->Pts->Prev; - - for( int j = 0; j < cnt; j++ ) - { - pn->Contour.push_back( op->Pt ); - op = op->Prev; - } - } - - // fixup PolyNode links etc ... - polytree.Childs.reserve( m_PolyOuts.size() ); - - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++ ) - { - OutRec* outRec = m_PolyOuts[i]; - - if( !outRec->PolyNd ) - continue; - - if( outRec->IsOpen ) - { - outRec->PolyNd->m_IsOpen = true; - polytree.AddChild( *outRec->PolyNd ); - } - else if( outRec->FirstLeft && outRec->FirstLeft->PolyNd ) - outRec->FirstLeft->PolyNd->AddChild( *outRec->PolyNd ); - else - polytree.AddChild( *outRec->PolyNd ); - } -} - - -// ------------------------------------------------------------------------------ - -void SwapIntersectNodes( IntersectNode& int1, IntersectNode& int2 ) -{ - // just swap the contents (because fIntersectNodes is a single-linked-list) - IntersectNode inode = int1; // gets a copy of Int1 - - int1.Edge1 = int2.Edge1; - int1.Edge2 = int2.Edge2; - int1.Pt = int2.Pt; - int2.Edge1 = inode.Edge1; - int2.Edge2 = inode.Edge2; - int2.Pt = inode.Pt; -} - - -// ------------------------------------------------------------------------------ - -inline bool E2InsertsBeforeE1( TEdge& e1, TEdge& e2 ) -{ - if( e2.Curr.X == e1.Curr.X ) - { - if( e2.Top.Y > e1.Top.Y ) - return e2.Top.X < TopX( e1, e2.Top.Y ); - else - return e1.Top.X > TopX( e2, e1.Top.Y ); - } - else - return e2.Curr.X < e1.Curr.X; -} - - -// ------------------------------------------------------------------------------ - -bool GetOverlap( const cInt a1, const cInt a2, const cInt b1, const cInt b2, - cInt& Left, cInt& Right ) -{ - if( a1 < a2 ) - { - if( b1 < b2 ) - { - Left = std::max( a1, b1 ); Right = std::min( a2, b2 ); - } - else - { - Left = std::max( a1, b2 ); Right = std::min( a2, b1 ); - } - } - else - { - if( b1 < b2 ) - { - Left = std::max( a2, b1 ); Right = std::min( a1, b2 ); - } - else - { - Left = std::max( a2, b2 ); Right = std::min( a1, b1 ); - } - } - - return Left < Right; -} - - -// ------------------------------------------------------------------------------ - -inline void UpdateOutPtIdxs( OutRec& outrec ) -{ - OutPt* op = outrec.Pts; - - do - { - op->Idx = outrec.Idx; - op = op->Prev; - } while( op != outrec.Pts ); -} - - -// ------------------------------------------------------------------------------ - -void Clipper::InsertEdgeIntoAEL( TEdge* edge, TEdge* startEdge ) -{ - if( !m_ActiveEdges ) - { - edge->PrevInAEL = 0; - edge->NextInAEL = 0; - m_ActiveEdges = edge; - } - else if( !startEdge && E2InsertsBeforeE1( *m_ActiveEdges, *edge ) ) - { - edge->PrevInAEL = 0; - edge->NextInAEL = m_ActiveEdges; - m_ActiveEdges->PrevInAEL = edge; - m_ActiveEdges = edge; - } - else - { - if( !startEdge ) - startEdge = m_ActiveEdges; - - while( startEdge->NextInAEL - && !E2InsertsBeforeE1( *startEdge->NextInAEL, *edge ) ) - startEdge = startEdge->NextInAEL; - - edge->NextInAEL = startEdge->NextInAEL; - - if( startEdge->NextInAEL ) - startEdge->NextInAEL->PrevInAEL = edge; - - edge->PrevInAEL = startEdge; - startEdge->NextInAEL = edge; - } -} - - -// ---------------------------------------------------------------------- - -OutPt* DupOutPt( OutPt* outPt, bool InsertAfter ) -{ - OutPt* result = new OutPt; - - result->Pt = outPt->Pt; - result->Idx = outPt->Idx; - - if( InsertAfter ) - { - result->Next = outPt->Next; - result->Prev = outPt; - outPt->Next->Prev = result; - outPt->Next = result; - } - else - { - result->Prev = outPt->Prev; - result->Next = outPt; - outPt->Prev->Next = result; - outPt->Prev = result; - } - - return result; -} - - -// ------------------------------------------------------------------------------ - -bool JoinHorz( OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, - const IntPoint Pt, bool DiscardLeft ) -{ - Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); - Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); - - if( Dir1 == Dir2 ) - return false; - - // When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we - // want Op1b to be on the Right. (And likewise with Op2 and Op2b.) - // So, to facilitate this while inserting Op1b and Op2b ... - // when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, - // otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) - if( Dir1 == dLeftToRight ) - { - while( op1->Next->Pt.X <= Pt.X - && op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y ) - op1 = op1->Next; - - if( DiscardLeft && (op1->Pt.X != Pt.X) ) - op1 = op1->Next; - - op1b = DupOutPt( op1, !DiscardLeft ); - - if( op1b->Pt != Pt ) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt( op1, !DiscardLeft ); - } - } - else - { - while( op1->Next->Pt.X >= Pt.X - && op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y ) - op1 = op1->Next; - - if( !DiscardLeft && (op1->Pt.X != Pt.X) ) - op1 = op1->Next; - - op1b = DupOutPt( op1, DiscardLeft ); - - if( op1b->Pt != Pt ) - { - op1 = op1b; - op1->Pt = Pt; - op1b = DupOutPt( op1, DiscardLeft ); - } - } - - if( Dir2 == dLeftToRight ) - { - while( op2->Next->Pt.X <= Pt.X - && op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y ) - op2 = op2->Next; - - if( DiscardLeft && (op2->Pt.X != Pt.X) ) - op2 = op2->Next; - - op2b = DupOutPt( op2, !DiscardLeft ); - - if( op2b->Pt != Pt ) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt( op2, !DiscardLeft ); - } - - ; - } - else - { - while( op2->Next->Pt.X >= Pt.X - && op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y ) - op2 = op2->Next; - - if( !DiscardLeft && (op2->Pt.X != Pt.X) ) - op2 = op2->Next; - - op2b = DupOutPt( op2, DiscardLeft ); - - if( op2b->Pt != Pt ) - { - op2 = op2b; - op2->Pt = Pt; - op2b = DupOutPt( op2, DiscardLeft ); - } - - ; - } - - ; - - if( (Dir1 == dLeftToRight) == DiscardLeft ) - { - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - } - else - { - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - } - - return true; -} - - -// ------------------------------------------------------------------------------ - -bool Clipper::JoinPoints( Join* j, OutRec* outRec1, OutRec* outRec2 ) -{ - OutPt* op1 = j->OutPt1, * op1b; - OutPt* op2 = j->OutPt2, * op2b; - - // There are 3 kinds of joins for output polygons ... - // 1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere - // along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). - // 2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same - // location at the Bottom of the overlapping segment (& Join.OffPt is above). - // 3. StrictSimple joins where edges touch but are not collinear and where - // Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. - bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); - - if( isHorizontal && (j->OffPt == j->OutPt1->Pt) - && (j->OffPt == j->OutPt2->Pt) ) - { - // Strictly Simple join ... - if( outRec1 != outRec2 ) - return false; - - op1b = j->OutPt1->Next; - - while( op1b != op1 && (op1b->Pt == j->OffPt) ) - op1b = op1b->Next; - - bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); - op2b = j->OutPt2->Next; - - while( op2b != op2 && (op2b->Pt == j->OffPt) ) - op2b = op2b->Next; - - bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); - - if( reverse1 == reverse2 ) - return false; - - if( reverse1 ) - { - op1b = DupOutPt( op1, false ); - op2b = DupOutPt( op2, true ); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - else - { - op1b = DupOutPt( op1, true ); - op2b = DupOutPt( op2, false ); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } - else if( isHorizontal ) - { - // treat horizontal joins differently to non-horizontal joins since with - // them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt - // may be anywhere along the horizontal edge. - op1b = op1; - - while( op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2 ) - op1 = op1->Prev; - - while( op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2 ) - op1b = op1b->Next; - - if( op1b->Next == op1 || op1b->Next == op2 ) - return false; // a flat 'polygon' - - op2b = op2; - - while( op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b ) - op2 = op2->Prev; - - while( op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1 ) - op2b = op2b->Next; - - if( op2b->Next == op2 || op2b->Next == op1 ) - return false; // a flat 'polygon' - - cInt Left, Right; - - // Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges - if( !GetOverlap( op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right ) ) - return false; - - // DiscardLeftSide: when overlapping edges are joined, a spike will created - // which needs to be cleaned up. However, we don't want Op1 or Op2 caught up - // on the discard Side as either may still be needed for other joins ... - IntPoint Pt; - bool DiscardLeftSide; - - if( op1->Pt.X >= Left && op1->Pt.X <= Right ) - { - Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); - } - else if( op2->Pt.X >= Left&& op2->Pt.X <= Right ) - { - Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); - } - else if( op1b->Pt.X >= Left && op1b->Pt.X <= Right ) - { - Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; - } - else - { - Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); - } - - j->OutPt1 = op1; j->OutPt2 = op2; - return JoinHorz( op1, op1b, op2, op2b, Pt, DiscardLeftSide ); - } - else - { - // nb: For non-horizontal joins ... - // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y - // 2. Jr.OutPt1.Pt > Jr.OffPt.Y - - // make sure the polygons are correctly oriented ... - op1b = op1->Next; - - while( (op1b->Pt == op1->Pt) && (op1b != op1) ) - op1b = op1b->Next; - - bool Reverse1 = ( (op1b->Pt.Y > op1->Pt.Y) - || !SlopesEqual( op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange ) ); - - if( Reverse1 ) - { - op1b = op1->Prev; - - while( (op1b->Pt == op1->Pt) && (op1b != op1) ) - op1b = op1b->Prev; - - if( (op1b->Pt.Y > op1->Pt.Y) - || !SlopesEqual( op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange ) ) - return false; - } - - ; - op2b = op2->Next; - - while( (op2b->Pt == op2->Pt) && (op2b != op2) ) - op2b = op2b->Next; - - bool Reverse2 = ( (op2b->Pt.Y > op2->Pt.Y) - || !SlopesEqual( op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange ) ); - - if( Reverse2 ) - { - op2b = op2->Prev; - - while( (op2b->Pt == op2->Pt) && (op2b != op2) ) - op2b = op2b->Prev; - - if( (op2b->Pt.Y > op2->Pt.Y) - || !SlopesEqual( op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange ) ) - return false; - } - - if( (op1b == op1) || (op2b == op2) || (op1b == op2b) - || ( (outRec1 == outRec2) && (Reverse1 == Reverse2) ) ) - return false; - - if( Reverse1 ) - { - op1b = DupOutPt( op1, false ); - op2b = DupOutPt( op2, true ); - op1->Prev = op2; - op2->Next = op1; - op1b->Next = op2b; - op2b->Prev = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - else - { - op1b = DupOutPt( op1, true ); - op2b = DupOutPt( op2, false ); - op1->Next = op2; - op2->Prev = op1; - op1b->Prev = op2b; - op2b->Next = op1b; - j->OutPt1 = op1; - j->OutPt2 = op1b; - return true; - } - } -} - - -// ---------------------------------------------------------------------- - -static OutRec* ParseFirstLeft( OutRec* FirstLeft ) -{ - while( FirstLeft && !FirstLeft->Pts ) - FirstLeft = FirstLeft->FirstLeft; - - return FirstLeft; -} - - -// ------------------------------------------------------------------------------ - -void Clipper::FixupFirstLefts1( OutRec* OldOutRec, OutRec* NewOutRec ) -{ - // tests if NewOutRec contains the polygon before reassigning FirstLeft - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i ) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft( outRec->FirstLeft ); - - if( outRec->Pts && firstLeft == OldOutRec ) - { - if( Poly2ContainsPoly1( outRec->Pts, NewOutRec->Pts ) ) - outRec->FirstLeft = NewOutRec; - } - } -} - - -// ---------------------------------------------------------------------- - -void Clipper::FixupFirstLefts2( OutRec* InnerOutRec, OutRec* OuterOutRec ) -{ - // A polygon has split into two such that one is now the inner of the other. - // It's possible that these polygons now wrap around other polygons, so check - // every polygon that's also contained by OuterOutRec's FirstLeft container - // (including 0) to see if they've become inner to the new inner polygon ... - OutRec* orfl = OuterOutRec->FirstLeft; - - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i ) - { - OutRec* outRec = m_PolyOuts[i]; - - if( !outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec ) - continue; - - OutRec* firstLeft = ParseFirstLeft( outRec->FirstLeft ); - - if( firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec ) - continue; - - if( Poly2ContainsPoly1( outRec->Pts, InnerOutRec->Pts ) ) - outRec->FirstLeft = InnerOutRec; - else if( Poly2ContainsPoly1( outRec->Pts, OuterOutRec->Pts ) ) - outRec->FirstLeft = OuterOutRec; - else if( outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec ) - outRec->FirstLeft = orfl; - } -} - - -// ---------------------------------------------------------------------- -void Clipper::FixupFirstLefts3( OutRec* OldOutRec, OutRec* NewOutRec ) -{ - // reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon - for( PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i ) - { - OutRec* outRec = m_PolyOuts[i]; - OutRec* firstLeft = ParseFirstLeft( outRec->FirstLeft ); - - if( outRec->Pts && firstLeft == OldOutRec ) - outRec->FirstLeft = NewOutRec; - } -} - - -// ---------------------------------------------------------------------- - -void Clipper::JoinCommonEdges() -{ - for( JoinList::size_type i = 0; i < m_Joins.size(); i++ ) - { - Join* join = m_Joins[i]; - - OutRec* outRec1 = GetOutRec( join->OutPt1->Idx ); - OutRec* outRec2 = GetOutRec( join->OutPt2->Idx ); - - if( !outRec1->Pts || !outRec2->Pts ) - continue; - - if( outRec1->IsOpen || outRec2->IsOpen ) - continue; - - // get the polygon fragment with the correct hole state (FirstLeft) - // before calling JoinPoints() ... - OutRec* holeStateRec; - - if( outRec1 == outRec2 ) - holeStateRec = outRec1; - else if( OutRec1RightOfOutRec2( outRec1, outRec2 ) ) - holeStateRec = outRec2; - else if( OutRec1RightOfOutRec2( outRec2, outRec1 ) ) - holeStateRec = outRec1; - else - holeStateRec = GetLowermostRec( outRec1, outRec2 ); - - if( !JoinPoints( join, outRec1, outRec2 ) ) - continue; - - if( outRec1 == outRec2 ) - { - // instead of joining two polygons, we've just created a new one by - // splitting one polygon into two. - outRec1->Pts = join->OutPt1; - outRec1->BottomPt = 0; - outRec2 = CreateOutRec(); - outRec2->Pts = join->OutPt2; - - // update all OutRec2.Pts Idx's ... - UpdateOutPtIdxs( *outRec2 ); - - if( Poly2ContainsPoly1( outRec2->Pts, outRec1->Pts ) ) - { - // outRec1 contains outRec2 ... - outRec2->IsHole = !outRec1->IsHole; - outRec2->FirstLeft = outRec1; - - if( m_UsingPolyTree ) - FixupFirstLefts2( outRec2, outRec1 ); - - if( (outRec2->IsHole ^ m_ReverseOutput) == (Area( *outRec2 ) > 0) ) - ReversePolyPtLinks( outRec2->Pts ); - } - else if( Poly2ContainsPoly1( outRec1->Pts, outRec2->Pts ) ) - { - // outRec2 contains outRec1 ... - outRec2->IsHole = outRec1->IsHole; - outRec1->IsHole = !outRec2->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - outRec1->FirstLeft = outRec2; - - if( m_UsingPolyTree ) - FixupFirstLefts2( outRec1, outRec2 ); - - if( (outRec1->IsHole ^ m_ReverseOutput) == (Area( *outRec1 ) > 0) ) - ReversePolyPtLinks( outRec1->Pts ); - } - else - { - // the 2 polygons are completely separate ... - outRec2->IsHole = outRec1->IsHole; - outRec2->FirstLeft = outRec1->FirstLeft; - - // fixup FirstLeft pointers that may need reassigning to OutRec2 - if( m_UsingPolyTree ) - FixupFirstLefts1( outRec1, outRec2 ); - } - } - else - { - // joined 2 polygons together ... - - outRec2->Pts = 0; - outRec2->BottomPt = 0; - outRec2->Idx = outRec1->Idx; - - outRec1->IsHole = holeStateRec->IsHole; - - if( holeStateRec == outRec2 ) - outRec1->FirstLeft = outRec2->FirstLeft; - - outRec2->FirstLeft = outRec1; - - if( m_UsingPolyTree ) - FixupFirstLefts3( outRec2, outRec1 ); - } - } -} - - -// ------------------------------------------------------------------------------ -// ClipperOffset support functions ... -// ------------------------------------------------------------------------------ - -DoublePoint GetUnitNormal( const IntPoint& pt1, const IntPoint& pt2 ) -{ - if( pt2.X == pt1.X && pt2.Y == pt1.Y ) - return DoublePoint( 0, 0 ); - - double Dx = (double) (pt2.X - pt1.X); - double dy = (double) (pt2.Y - pt1.Y); - double f = 1 * 1.0 / std::sqrt( Dx * Dx + dy * dy ); - Dx *= f; - dy *= f; - return DoublePoint( dy, -Dx ); -} - - -// ------------------------------------------------------------------------------ -// ClipperOffset class -// ------------------------------------------------------------------------------ - -ClipperOffset::ClipperOffset( double miterLimit, double arcTolerance ) -{ - this->MiterLimit = miterLimit; - this->ArcTolerance = arcTolerance; - m_lowest.X = -1; - - //Avoid uninitialized vars: - MiterFallback = jtSquare; - m_delta = 1.0; - m_sinA = 0.0; - m_sin = 0.0; - m_cos = 0.0; - m_miterLim = 1.0; - m_StepsPerRad = 1.0; -} - - -// ------------------------------------------------------------------------------ - -ClipperOffset::~ClipperOffset() -{ - Clear(); -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::Clear() -{ - for( int i = 0; i < m_polyNodes.ChildCount(); ++i ) - delete m_polyNodes.Childs[i]; - - m_polyNodes.Childs.clear(); - m_lowest.X = -1; -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::AddPath( const Path& path, JoinType joinType, EndType endType ) -{ - int highI = (int) path.size() - 1; - - if( highI < 0 ) - return; - - PolyNode* newNode = new PolyNode(); - newNode->m_jointype = joinType; - newNode->m_endtype = endType; - - // strip duplicate points from path and also get index to the lowest point ... - if( endType == etClosedLine || endType == etClosedPolygon ) - while( highI > 0 && path[0] == path[highI] ) - highI--; - - - newNode->Contour.reserve( highI + 1 ); - newNode->Contour.push_back( path[0] ); - int j = 0, k = 0; - - for( int i = 1; i <= highI; i++ ) - if( newNode->Contour[j] != path[i] ) - { - j++; - newNode->Contour.push_back( path[i] ); - - if( path[i].Y > newNode->Contour[k].Y - || (path[i].Y == newNode->Contour[k].Y - && path[i].X < newNode->Contour[k].X) ) - k = j; - } - - - if( endType == etClosedPolygon && j < 2 ) - { - delete newNode; - return; - } - - m_polyNodes.AddChild( *newNode ); - - // if this path's lowest pt is lower than all the others then update m_lowest - if( endType != etClosedPolygon ) - return; - - if( m_lowest.X < 0 ) - m_lowest = IntPoint( m_polyNodes.ChildCount() - 1, k ); - else - { - IntPoint ip = m_polyNodes.Childs[(int) m_lowest.X]->Contour[(int) m_lowest.Y]; - - if( newNode->Contour[k].Y > ip.Y - || (newNode->Contour[k].Y == ip.Y - && newNode->Contour[k].X < ip.X) ) - m_lowest = IntPoint( m_polyNodes.ChildCount() - 1, k ); - } -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::AddPaths( const Paths& paths, JoinType joinType, EndType endType ) -{ - for( Paths::size_type i = 0; i < paths.size(); ++i ) - AddPath( paths[i], joinType, endType ); -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::FixOrientations() -{ - // fixup orientations of all closed paths if the orientation of the - // closed path with the lowermost vertex is wrong ... - if( m_lowest.X >= 0 - && !Orientation( m_polyNodes.Childs[(int) m_lowest.X]->Contour ) ) - { - for( int i = 0; i < m_polyNodes.ChildCount(); ++i ) - { - PolyNode& node = *m_polyNodes.Childs[i]; - - if( node.m_endtype == etClosedPolygon - || ( node.m_endtype == etClosedLine && Orientation( node.Contour ) ) ) - ReversePath( node.Contour ); - } - } - else - { - for( int i = 0; i < m_polyNodes.ChildCount(); ++i ) - { - PolyNode& node = *m_polyNodes.Childs[i]; - - if( node.m_endtype == etClosedLine && !Orientation( node.Contour ) ) - ReversePath( node.Contour ); - } - } -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::Execute( Paths& solution, double delta ) -{ - solution.clear(); - FixOrientations(); - DoOffset( delta ); - - // now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths( m_destPolys, ptSubject, true ); - - if( delta > 0 ) - { - clpr.Execute( ctUnion, solution, pftPositive, pftPositive ); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer( 4 ); - outer[0] = IntPoint( r.left - 10, r.bottom + 10 ); - outer[1] = IntPoint( r.right + 10, r.bottom + 10 ); - outer[2] = IntPoint( r.right + 10, r.top - 10 ); - outer[3] = IntPoint( r.left - 10, r.top - 10 ); - - clpr.AddPath( outer, ptSubject, true ); - clpr.ReverseSolution( true ); - clpr.Execute( ctUnion, solution, pftNegative, pftNegative ); - - if( solution.size() > 0 ) - solution.erase( solution.begin() ); - } -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::Execute( PolyTree& solution, double delta ) -{ - solution.Clear(); - FixOrientations(); - DoOffset( delta ); - - // now clean up 'corners' ... - Clipper clpr; - clpr.AddPaths( m_destPolys, ptSubject, true ); - - if( delta > 0 ) - { - clpr.Execute( ctUnion, solution, pftPositive, pftPositive ); - } - else - { - IntRect r = clpr.GetBounds(); - Path outer( 4 ); - outer[0] = IntPoint( r.left - 10, r.bottom + 10 ); - outer[1] = IntPoint( r.right + 10, r.bottom + 10 ); - outer[2] = IntPoint( r.right + 10, r.top - 10 ); - outer[3] = IntPoint( r.left - 10, r.top - 10 ); - - clpr.AddPath( outer, ptSubject, true ); - clpr.ReverseSolution( true ); - clpr.Execute( ctUnion, solution, pftNegative, pftNegative ); - - // remove the outer PolyNode rectangle ... - if( solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0 ) - { - PolyNode* outerNode = solution.Childs[0]; - solution.Childs.reserve( outerNode->ChildCount() ); - solution.Childs[0] = outerNode->Childs[0]; - solution.Childs[0]->Parent = outerNode->Parent; - - for( int i = 1; i < outerNode->ChildCount(); ++i ) - solution.AddChild( *outerNode->Childs[i] ); - } - else - solution.Clear(); - } -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::DoOffset( double delta ) -{ - m_destPolys.clear(); - m_delta = delta; - - // if Zero offset, just copy any CLOSED polygons to m_p and return ... - if( NEAR_ZERO( delta ) ) - { - m_destPolys.reserve( m_polyNodes.ChildCount() ); - - for( int i = 0; i < m_polyNodes.ChildCount(); i++ ) - { - PolyNode& node = *m_polyNodes.Childs[i]; - - if( node.m_endtype == etClosedPolygon ) - m_destPolys.push_back( node.Contour ); - } - - return; - } - - // see offset_triginometry3.svg in the documentation folder ... - if( MiterLimit > 2 ) - m_miterLim = 2 / (MiterLimit * MiterLimit); - else - m_miterLim = 0.5; - - double y; - - if( ArcTolerance <= 0.0 ) - y = def_arc_tolerance; - else if( ArcTolerance > std::fabs( delta ) * def_arc_tolerance ) - y = std::fabs( delta ) * def_arc_tolerance; - else - y = ArcTolerance; - - // see offset_triginometry2.svg in the documentation folder ... - double steps = pi / std::acos( 1 - y / std::fabs( delta ) ); - - if( steps > std::fabs( delta ) * pi ) - steps = std::fabs( delta ) * pi; // ie excessive precision check - - m_sin = std::sin( two_pi / steps ); - m_cos = std::cos( two_pi / steps ); - m_StepsPerRad = steps / two_pi; - - if( delta < 0.0 ) - m_sin = -m_sin; - - m_destPolys.reserve( m_polyNodes.ChildCount() * 2 ); - - for( int i = 0; i < m_polyNodes.ChildCount(); i++ ) - { - PolyNode& node = *m_polyNodes.Childs[i]; - m_srcPoly = node.Contour; - - int len = (int) m_srcPoly.size(); - - if( len == 0 || ( delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon) ) ) - continue; - - m_destPoly.clear(); - - if( len == 1 ) - { - if( node.m_jointype == jtRound ) - { - double X = 1.0, Y = 0.0; - - for( cInt j = 1; j <= steps; j++ ) - { - m_destPoly.emplace_back( Round( m_srcPoly[0].X + X * delta ), - Round( m_srcPoly[0].Y + Y * delta ) ); - double X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - } - else - { - double X = -1.0, Y = -1.0; - - for( int j = 0; j < 4; ++j ) - { - m_destPoly.emplace_back( Round( m_srcPoly[0].X + X * delta ), - Round( m_srcPoly[0].Y + Y * delta ) ); - - if( X < 0 ) - X = 1; - else if( Y < 0 ) - Y = 1; - else - X = -1; - } - } - - m_destPolys.push_back( m_destPoly ); - continue; - } - - // build m_normals ... - m_normals.clear(); - m_normals.reserve( len ); - - for( int j = 0; j < len - 1; ++j ) - m_normals.push_back( GetUnitNormal( m_srcPoly[j], m_srcPoly[j + 1] ) ); - - if( node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon ) - m_normals.push_back( GetUnitNormal( m_srcPoly[len - 1], m_srcPoly[0] ) ); - else - m_normals.emplace_back( m_normals[len - 2] ); - - if( node.m_endtype == etClosedPolygon ) - { - int k = len - 1; - - for( int j = 0; j < len; ++j ) - OffsetPoint( j, k, node.m_jointype ); - - m_destPolys.push_back( m_destPoly ); - } - else if( node.m_endtype == etClosedLine ) - { - int k = len - 1; - - for( int j = 0; j < len; ++j ) - OffsetPoint( j, k, node.m_jointype ); - - m_destPolys.push_back( m_destPoly ); - m_destPoly.clear(); - // re-build m_normals ... - DoublePoint n = m_normals[len - 1]; - - for( int j = len - 1; j > 0; j-- ) - m_normals[j] = DoublePoint( -m_normals[j - 1].X, -m_normals[j - 1].Y ); - - m_normals[0] = DoublePoint( -n.X, -n.Y ); - k = 0; - - for( int j = len - 1; j >= 0; j-- ) - OffsetPoint( j, k, node.m_jointype ); - - m_destPolys.push_back( m_destPoly ); - } - else - { - int k = 0; - - for( int j = 1; j < len - 1; ++j ) - OffsetPoint( j, k, node.m_jointype ); - - IntPoint pt1; - - if( node.m_endtype == etOpenButt ) - { - int j = len - 1; - pt1 = IntPoint( (cInt) Round( m_srcPoly[j].X + m_normals[j].X * - delta ), (cInt) Round( m_srcPoly[j].Y + m_normals[j].Y * delta ) ); - m_destPoly.push_back( pt1 ); - pt1 = IntPoint( (cInt) Round( m_srcPoly[j].X - m_normals[j].X * - delta ), (cInt) Round( m_srcPoly[j].Y - m_normals[j].Y * delta ) ); - m_destPoly.push_back( pt1 ); - } - else - { - int j = len - 1; - k = len - 2; - m_sinA = 0; - m_normals[j] = DoublePoint( -m_normals[j].X, -m_normals[j].Y ); - - if( node.m_endtype == etOpenSquare ) - DoSquare( j, k ); - else - DoRound( j, k ); - } - - // re-build m_normals ... - for( int j = len - 1; j > 0; j-- ) - m_normals[j] = DoublePoint( -m_normals[j - 1].X, -m_normals[j - 1].Y ); - - m_normals[0] = DoublePoint( -m_normals[1].X, -m_normals[1].Y ); - - k = len - 1; - - for( int j = k - 1; j > 0; --j ) - OffsetPoint( j, k, node.m_jointype ); - - if( node.m_endtype == etOpenButt ) - { - pt1 = IntPoint( (cInt) Round( m_srcPoly[0].X - m_normals[0].X * delta ), - (cInt) Round( m_srcPoly[0].Y - m_normals[0].Y * delta ) ); - m_destPoly.push_back( pt1 ); - pt1 = IntPoint( (cInt) Round( m_srcPoly[0].X + m_normals[0].X * delta ), - (cInt) Round( m_srcPoly[0].Y + m_normals[0].Y * delta ) ); - m_destPoly.push_back( pt1 ); - } - else - { - k = 1; - m_sinA = 0; - - if( node.m_endtype == etOpenSquare ) - DoSquare( 0, 1 ); - else - DoRound( 0, 1 ); - } - - m_destPolys.push_back( m_destPoly ); - } - } -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::OffsetPoint( int j, int& k, JoinType jointype ) -{ - // cross product ... - m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); - - if( std::fabs( m_sinA * m_delta ) < 1.0 ) - { - // dot product ... - double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); - - if( cosA > 0 ) // angle => 0 degrees - { - m_destPoly.emplace_back( Round( m_srcPoly[j].X + m_normals[k].X * m_delta ), - Round( m_srcPoly[j].Y + m_normals[k].Y * m_delta ) ); - return; - } - - // else angle => 180 degrees - } - else if( m_sinA > 1.0 ) - m_sinA = 1.0; - else if( m_sinA < -1.0 ) - m_sinA = -1.0; - - if( m_sinA * m_delta < 0 ) - { - m_destPoly.emplace_back( Round( m_srcPoly[j].X + m_normals[k].X * m_delta ), - Round( m_srcPoly[j].Y + m_normals[k].Y * m_delta ) ); - m_destPoly.push_back( m_srcPoly[j] ); - m_destPoly.emplace_back( Round( m_srcPoly[j].X + m_normals[j].X * m_delta ), - Round( m_srcPoly[j].Y + m_normals[j].Y * m_delta ) ); - } - else - switch( jointype ) - { - case jtMiter: - { - double r = 1 + (m_normals[j].X * m_normals[k].X + - m_normals[j].Y * m_normals[k].Y); - - if( r >= m_miterLim ) - DoMiter( j, k, r ); - else if( MiterFallback == jtRound ) - DoRound( j, k ); - else - DoSquare( j, k ); - - break; - } - - case jtSquare: - DoSquare( j, k ); break; - - case jtRound: - DoRound( j, k ); break; - } - - - k = j; -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::DoSquare( int j, int k ) -{ - double dx = std::tan( std::atan2( m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y ) / 4 ); - - m_destPoly.push_back( IntPoint( - Round( m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx) ), - Round( m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx) ) ) ); - m_destPoly.push_back( IntPoint( - Round( m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx) ), - Round( m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx) ) ) ); -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::DoMiter( int j, int k, double r ) -{ - double q = m_delta / r; - - m_destPoly.emplace_back( Round( m_srcPoly[j].X + ( m_normals[k].X + m_normals[j].X ) * q ), - Round( m_srcPoly[j].Y + ( m_normals[k].Y + m_normals[j].Y ) * q ) ); -} - - -// ------------------------------------------------------------------------------ - -void ClipperOffset::DoRound( int j, int k ) -{ - double a = std::atan2( m_sinA, - m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y ); - double steps = m_StepsPerRad * std::fabs( a ); - int takenSteps = std::max((int) std::floor( steps ), 1 ); - - double X = m_normals[k].X, Y = m_normals[k].Y, X2; - - for( int i = 0; i < takenSteps; ++i ) - { - m_destPoly.emplace_back( Round( m_srcPoly[j].X + X * m_delta ), - Round( m_srcPoly[j].Y + Y * m_delta ) ); - X2 = X; - X = X * m_cos - m_sin * Y; - Y = X2 * m_sin + Y * m_cos; - } - - // A 10% error on chord length won't make much error difference, and it keeps us - // from generating geometrically noisy solutions. - if( steps > takenSteps + 0.1 ) - { - m_destPoly.emplace_back( Round( m_srcPoly[j].X + X * m_delta ), - Round( m_srcPoly[j].Y + Y * m_delta ) ); - } - - m_destPoly.emplace_back( Round( m_srcPoly[j].X + m_normals[j].X * m_delta ), - Round( m_srcPoly[j].Y + m_normals[j].Y * m_delta ) ); -} - - -// ------------------------------------------------------------------------------ -// Miscellaneous public functions -// ------------------------------------------------------------------------------ - -void Clipper::DoSimplePolygons() -{ - PolyOutList::size_type i = 0; - - while( i < m_PolyOuts.size() ) - { - OutRec* outrec = m_PolyOuts[i++]; - OutPt* op = outrec->Pts; - - if( !op || outrec->IsOpen ) - continue; - - do // for each Pt in Polygon until duplicate found do ... - { - OutPt* op2 = op->Next; - - while( op2 != outrec->Pts ) - { - if( (op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op ) - { - // split the polygon into two ... - OutPt* op3 = op->Prev; - OutPt* op4 = op2->Prev; - op->Prev = op4; - op4->Next = op; - op2->Prev = op3; - op3->Next = op2; - - outrec->Pts = op; - OutRec* outrec2 = CreateOutRec(); - outrec2->Pts = op2; - UpdateOutPtIdxs( *outrec2 ); - - if( Poly2ContainsPoly1( outrec2->Pts, outrec->Pts ) ) - { - // OutRec2 is contained by OutRec1 ... - outrec2->IsHole = !outrec->IsHole; - outrec2->FirstLeft = outrec; - - if( m_UsingPolyTree ) - FixupFirstLefts2( outrec2, outrec ); - } - else - if( Poly2ContainsPoly1( outrec->Pts, outrec2->Pts ) ) - { - // OutRec1 is contained by OutRec2 ... - outrec2->IsHole = outrec->IsHole; - outrec->IsHole = !outrec2->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - outrec->FirstLeft = outrec2; - - if( m_UsingPolyTree ) - FixupFirstLefts2( outrec, outrec2 ); - } - else - { - // the 2 polygons are separate ... - outrec2->IsHole = outrec->IsHole; - outrec2->FirstLeft = outrec->FirstLeft; - - if( m_UsingPolyTree ) - FixupFirstLefts1( outrec, outrec2 ); - } - - - op2 = op; // ie get ready for the Next iteration - } - - op2 = op2->Next; - } - - op = op->Next; - } while( op != outrec->Pts ); - } -} - - -// ------------------------------------------------------------------------------ - -void ReversePath( Path& p ) -{ - std::reverse( p.begin(), p.end() ); -} - - -// ------------------------------------------------------------------------------ - -void ReversePaths( Paths& p ) -{ - for( Paths::size_type i = 0; i < p.size(); ++i ) - ReversePath( p[i] ); -} - - -// ------------------------------------------------------------------------------ - -void SimplifyPolygon( const Path& in_poly, Paths& out_polys, PolyFillType fillType ) -{ - Clipper c; - - c.StrictlySimple( true ); - c.AddPath( in_poly, ptSubject, true ); - c.Execute( ctUnion, out_polys, fillType, fillType ); -} - - -// ------------------------------------------------------------------------------ - -void SimplifyPolygons( const Paths& in_polys, Paths& out_polys, PolyFillType fillType ) -{ - Clipper c; - - c.StrictlySimple( true ); - c.AddPaths( in_polys, ptSubject, true ); - c.Execute( ctUnion, out_polys, fillType, fillType ); -} - - -// ------------------------------------------------------------------------------ - -void SimplifyPolygons( Paths& polys, PolyFillType fillType ) -{ - SimplifyPolygons( polys, polys, fillType ); -} - - -// ------------------------------------------------------------------------------ - -inline double DistanceSqrd( const IntPoint& pt1, const IntPoint& pt2 ) -{ - double Dx = ( (double) pt1.X - pt2.X ); - double dy = ( (double) pt1.Y - pt2.Y ); - - return Dx * Dx + dy * dy; -} - - -// ------------------------------------------------------------------------------ - -double DistanceFromLineSqrd( const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2 ) -{ - // The equation of a line in general form (Ax + By + C = 0) - // given 2 points (x¹,y¹) & (x²,y²) is ... - // (y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 - // A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ - // perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) - // see http://en.wikipedia.org/wiki/Perpendicular_distance - double A = double(ln1.Y - ln2.Y); - double B = double(ln2.X - ln1.X); - double C = A * ln1.X + B * ln1.Y; - - C = A * pt.X + B * pt.Y - C; - return (C * C) / (A * A + B * B); -} - - -// --------------------------------------------------------------------------- - -bool SlopesNearCollinear( const IntPoint& pt1, - const IntPoint& pt2, const IntPoint& pt3, double distSqrd ) -{ - // this function is more accurate when the point that's geometrically - // between the other 2 points is the one that's tested for distance. - // ie makes it more likely to pick up 'spikes' ... - if( Abs( pt1.X - pt2.X ) > Abs( pt1.Y - pt2.Y ) ) - { - if( (pt1.X > pt2.X) == (pt1.X < pt3.X) ) - return DistanceFromLineSqrd( pt1, pt2, pt3 ) < distSqrd; - else if( (pt2.X > pt1.X) == (pt2.X < pt3.X) ) - return DistanceFromLineSqrd( pt2, pt1, pt3 ) < distSqrd; - else - return DistanceFromLineSqrd( pt3, pt1, pt2 ) < distSqrd; - } - else - { - if( (pt1.Y > pt2.Y) == (pt1.Y < pt3.Y) ) - return DistanceFromLineSqrd( pt1, pt2, pt3 ) < distSqrd; - else if( (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y) ) - return DistanceFromLineSqrd( pt2, pt1, pt3 ) < distSqrd; - else - return DistanceFromLineSqrd( pt3, pt1, pt2 ) < distSqrd; - } -} - - -// ------------------------------------------------------------------------------ - -bool PointsAreClose( IntPoint pt1, IntPoint pt2, double distSqrd ) -{ - double Dx = (double) pt1.X - pt2.X; - double dy = (double) pt1.Y - pt2.Y; - - return (Dx * Dx) + (dy * dy) <= distSqrd; -} - - -// ------------------------------------------------------------------------------ - -OutPt* ExcludeOp( OutPt* op ) -{ - OutPt* result = op->Prev; - - result->Next = op->Next; - op->Next->Prev = result; - result->Idx = 0; - return result; -} - - -// ------------------------------------------------------------------------------ - -void CleanPolygon( const Path& in_poly, Path& out_poly, double distance ) -{ - // distance = proximity in units/pixels below which vertices - // will be stripped. Default ~= sqrt(2). - - size_t size = in_poly.size(); - - if( size == 0 ) - { - out_poly.clear(); - return; - } - - OutPt* outPts = new OutPt[size]; - - for( size_t i = 0; i < size; ++i ) - { - outPts[i].Pt = in_poly[i]; - outPts[i].Next = &outPts[(i + 1) % size]; - outPts[i].Next->Prev = &outPts[i]; - outPts[i].Idx = 0; - } - - double distSqrd = distance * distance; - OutPt* op = &outPts[0]; - - while( op->Idx == 0 && op->Next != op->Prev ) - { - if( PointsAreClose( op->Pt, op->Prev->Pt, distSqrd ) ) - { - op = ExcludeOp( op ); - size--; - } - else if( PointsAreClose( op->Prev->Pt, op->Next->Pt, distSqrd ) ) - { - ExcludeOp( op->Next ); - op = ExcludeOp( op ); - size -= 2; - } - else if( SlopesNearCollinear( op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd ) ) - { - op = ExcludeOp( op ); - size--; - } - else - { - op->Idx = 1; - op = op->Next; - } - } - - if( size < 3 ) - size = 0; - - out_poly.resize( size ); - - for( size_t i = 0; i < size; ++i ) - { - out_poly[i] = op->Pt; - op = op->Next; - } - - delete [] outPts; -} - - -// ------------------------------------------------------------------------------ - -void CleanPolygon( Path& poly, double distance ) -{ - CleanPolygon( poly, poly, distance ); -} - - -// ------------------------------------------------------------------------------ - -void CleanPolygons( const Paths& in_polys, Paths& out_polys, double distance ) -{ - out_polys.resize( in_polys.size() ); - - for( Paths::size_type i = 0; i < in_polys.size(); ++i ) - CleanPolygon( in_polys[i], out_polys[i], distance ); -} - - -// ------------------------------------------------------------------------------ - -void CleanPolygons( Paths& polys, double distance ) -{ - CleanPolygons( polys, polys, distance ); -} - - -// ------------------------------------------------------------------------------ - -void Minkowski( const Path& poly, const Path& path, - Paths& solution, bool isSum, bool isClosed ) -{ - int delta = (isClosed ? 1 : 0); - size_t polyCnt = poly.size(); - size_t pathCnt = path.size(); - Paths pp; - - pp.reserve( pathCnt ); - - if( isSum ) - for( size_t i = 0; i < pathCnt; ++i ) - { - Path p; - p.reserve( polyCnt ); - - for( size_t j = 0; j < poly.size(); ++j ) - p.emplace_back( path[i].X + poly[j].X, path[i].Y + poly[j].Y ); - - pp.push_back( p ); - } - - - else - for( size_t i = 0; i < pathCnt; ++i ) - { - Path p; - p.reserve( polyCnt ); - - for( size_t j = 0; j < poly.size(); ++j ) - p.emplace_back( path[i].X - poly[j].X, path[i].Y - poly[j].Y ); - - pp.push_back( p ); - } - - - - solution.clear(); - solution.reserve( (pathCnt + delta) * (polyCnt + 1) ); - - for( size_t i = 0; i < pathCnt - 1 + delta; ++i ) - for( size_t j = 0; j < polyCnt; ++j ) - { - Path quad; - quad.reserve( 4 ); - quad.push_back( pp[i % pathCnt][j % polyCnt] ); - quad.push_back( pp[(i + 1) % pathCnt][j % polyCnt] ); - quad.push_back( pp[(i + 1) % pathCnt][(j + 1) % polyCnt] ); - quad.push_back( pp[i % pathCnt][(j + 1) % polyCnt] ); - - if( !Orientation( quad ) ) - ReversePath( quad ); - - solution.push_back( quad ); - } - - -} - - -// ------------------------------------------------------------------------------ - -void MinkowskiSum( const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed ) -{ - Minkowski( pattern, path, solution, true, pathIsClosed ); - Clipper c; - c.AddPaths( solution, ptSubject, true ); - c.Execute( ctUnion, solution, pftNonZero, pftNonZero ); -} - - -// ------------------------------------------------------------------------------ - -void TranslatePath( const Path& input, Path& output, const IntPoint delta ) -{ - // precondition: input != output - output.resize( input.size() ); - - for( size_t i = 0; i < input.size(); ++i ) - output[i] = IntPoint( input[i].X + delta.X, input[i].Y + delta.Y ); -} - - -// ------------------------------------------------------------------------------ - -void MinkowskiSum( const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed ) -{ - Clipper c; - - for( size_t i = 0; i < paths.size(); ++i ) - { - Paths tmp; - Minkowski( pattern, paths[i], tmp, true, pathIsClosed ); - c.AddPaths( tmp, ptSubject, true ); - - if( pathIsClosed ) - { - Path tmp2; - TranslatePath( paths[i], tmp2, pattern[0] ); - c.AddPath( tmp2, ptClip, true ); - } - } - - c.Execute( ctUnion, solution, pftNonZero, pftNonZero ); -} - - -// ------------------------------------------------------------------------------ - -void MinkowskiDiff( const Path& poly1, const Path& poly2, Paths& solution ) -{ - Minkowski( poly1, poly2, solution, false, true ); - Clipper c; - c.AddPaths( solution, ptSubject, true ); - c.Execute( ctUnion, solution, pftNonZero, pftNonZero ); -} - - -// ------------------------------------------------------------------------------ - -enum NodeType -{ - ntAny, ntOpen, ntClosed -}; - -void AddPolyNodeToPaths( const PolyNode& polynode, NodeType nodetype, Paths& paths ) -{ - bool match = true; - - if( nodetype == ntClosed ) - match = !polynode.IsOpen(); - else if( nodetype == ntOpen ) - return; - - if( !polynode.Contour.empty() && match ) - paths.push_back( polynode.Contour ); - - for( int i = 0; i < polynode.ChildCount(); ++i ) - AddPolyNodeToPaths( *polynode.Childs[i], nodetype, paths ); -} - - -// ------------------------------------------------------------------------------ - -void PolyTreeToPaths( const PolyTree& polytree, Paths& paths ) -{ - paths.resize( 0 ); - paths.reserve( polytree.Total() ); - AddPolyNodeToPaths( polytree, ntAny, paths ); -} - - -// ------------------------------------------------------------------------------ - -void ClosedPathsFromPolyTree( const PolyTree& polytree, Paths& paths ) -{ - paths.resize( 0 ); - paths.reserve( polytree.Total() ); - AddPolyNodeToPaths( polytree, ntClosed, paths ); -} - - -// ------------------------------------------------------------------------------ - -void OpenPathsFromPolyTree( PolyTree& polytree, Paths& paths ) -{ - paths.resize( 0 ); - paths.reserve( polytree.Total() ); - - // Open paths are top level only, so ... - for( int i = 0; i < polytree.ChildCount(); ++i ) - if( polytree.Childs[i]->IsOpen() ) - paths.push_back( polytree.Childs[i]->Contour ); - - -} - - -// ------------------------------------------------------------------------------ - -std::ostream& operator <<( std::ostream& s, const IntPoint& p ) -{ - s << "(" << p.X << "," << p.Y << ")"; - return s; -} - - -// ------------------------------------------------------------------------------ - -std::ostream& operator <<( std::ostream& s, const Path& p ) -{ - if( p.empty() ) - return s; - - Path::size_type last = p.size() - 1; - - for( Path::size_type i = 0; i < last; i++ ) - s << "(" << p[i].X << "," << p[i].Y << "), "; - - s << "(" << p[last].X << "," << p[last].Y << ")\n"; - return s; -} - - -// ------------------------------------------------------------------------------ - -std::ostream& operator <<( std::ostream& s, const Paths& p ) -{ - for( Paths::size_type i = 0; i < p.size(); i++ ) - s << p[i]; - - s << "\n"; - return s; -} - - -// ------------------------------------------------------------------------------ -} // ClipperLib namespace diff --git a/thirdparty/clipper/clipper.hpp b/thirdparty/clipper/clipper.hpp deleted file mode 100644 index b5d0ee945b..0000000000 --- a/thirdparty/clipper/clipper.hpp +++ /dev/null @@ -1,457 +0,0 @@ -/******************************************************************************* -* * -* Author : Angus Johnson * -* Version : 6.4.2 * -* Date : 27 February 2017 * -* Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2017 * -* * -* License: * -* Use, modification & distribution is subject to Boost Software License Ver 1. * -* http://www.boost.org/LICENSE_1_0.txt * -* * -* Attributions: * -* The code in this library is an extension of Bala Vatti's clipping algorithm: * -* "A generic solution to polygon clipping" * -* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * -* http://portal.acm.org/citation.cfm?id=129906 * -* * -* Computer graphics and geometric modeling: implementation and algorithms * -* By Max K. Agoston * -* Springer; 1 edition (January 4, 2005) * -* http://books.google.com/books?q=vatti+clipping+agoston * -* * -* See also: * -* "Polygon Offsetting by Computing Winding Numbers" * -* Paper no. DETC2005-85513 pp. 565-575 * -* ASME 2005 International Design Engineering Technical Conferences * -* and Computers and Information in Engineering Conference (IDETC/CIE2005) * -* September 24-28, 2005 , Long Beach, California, USA * -* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * -* * -*******************************************************************************/ - -#ifndef clipper_hpp -#define clipper_hpp - -#define CLIPPER_VERSION "6.4.2" - -// use_int32: When enabled 32bit ints are used instead of 64bit ints. This -// improve performance but coordinate values are limited to the range +/- 46340 -// #define use_int32 - -// use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. -#define use_xyz - -// use_lines: Enables line clipping. Adds a very minor cost to performance. -#define use_lines - -// use_deprecated: Enables temporary support for the obsolete functions -// #define use_deprecated - -#include <functional> -#include <vector> -#include <list> -#include <set> -#include <stdexcept> -#include <cstring> -#include <cstdlib> -#include <ostream> -#include <functional> -#include <queue> - -namespace ClipperLib { -enum ClipType -{ - ctIntersection, ctUnion, ctDifference, ctXor -}; -enum PolyType -{ - ptSubject, ptClip -}; -// By far the most widely used winding rules for polygon filling are -// EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) -// Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) -// see http://glprogramming.com/red/chapter11.html -enum PolyFillType -{ - pftEvenOdd, pftNonZero, pftPositive, pftNegative -}; - -#ifdef use_int32 -typedef int cInt; -static cInt const loRange = 0x7FFF; -static cInt const hiRange = 0x7FFF; -#else -typedef signed long long cInt; -static cInt const loRange = 0x3FFFFFFF; -static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; -typedef signed long long long64; // used by Int128 class -typedef unsigned long long ulong64; - -#endif - -struct IntPoint -{ - cInt X; - cInt Y; -#ifdef use_xyz - cInt Z; - IntPoint( cInt x = 0, cInt y = 0, cInt z = 0 ) : X( x ), Y( y ), Z( z ) {}; -#else - IntPoint( cInt x = 0, cInt y = 0 ) : X( x ), Y( y ) {}; -#endif - - friend inline bool operator==( const IntPoint& a, const IntPoint& b ) - { - return a.X == b.X && a.Y == b.Y; - } - - friend inline bool operator!=( const IntPoint& a, const IntPoint& b ) - { - return a.X != b.X || a.Y != b.Y; - } -}; -// ------------------------------------------------------------------------------ - -typedef std::vector<IntPoint> Path; -typedef std::vector<Path> Paths; - -inline Path& operator <<( Path& poly, const IntPoint& p ) -{ - poly.push_back( p ); return poly; -} - - -inline Paths& operator <<( Paths& polys, const Path& p ) -{ - polys.push_back( p ); return polys; -} - - -std::ostream& operator <<( std::ostream& s, const IntPoint& p ); -std::ostream& operator <<( std::ostream& s, const Path& p ); -std::ostream& operator <<( std::ostream& s, const Paths& p ); - -struct DoublePoint -{ - double X; - double Y; - DoublePoint( double x = 0, double y = 0 ) : X( x ), Y( y ) {} - DoublePoint( IntPoint ip ) : X( (double) ip.X ), Y( (double) ip.Y ) {} -}; -// ------------------------------------------------------------------------------ - -#ifdef use_xyz -typedef std::function<void( IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, - IntPoint& pt )> ZFillCallback; -#endif - -enum InitOptions -{ - ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4 -}; -enum JoinType -{ - jtSquare, jtRound, jtMiter -}; -enum EndType -{ - etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound -}; - -class PolyNode; -typedef std::vector<PolyNode*> PolyNodes; - -class PolyNode -{ -public: - PolyNode(); - virtual ~PolyNode() {}; - Path Contour; - PolyNodes Childs; - PolyNode* Parent; - PolyNode* GetNext() const; - bool IsHole() const; - bool IsOpen() const; - int ChildCount() const; - -private: - // PolyNode& operator =(PolyNode& other); - unsigned Index; // node index in Parent.Childs - bool m_IsOpen; - JoinType m_jointype; - EndType m_endtype; - PolyNode* GetNextSiblingUp() const; - void AddChild( PolyNode& child ); - - friend class Clipper; // to access Index - friend class ClipperOffset; -}; - -class PolyTree : public PolyNode -{ -public: - ~PolyTree() { Clear(); }; - PolyNode* GetFirst() const; - void Clear(); - int Total() const; - -private: - // PolyTree& operator =(PolyTree& other); - PolyNodes AllNodes; - friend class Clipper; // to access AllNodes -}; - -bool Orientation( const Path& poly ); -double Area( const Path& poly ); -int PointInPolygon( const IntPoint& pt, const Path& path ); - -void SimplifyPolygon( const Path& in_poly, Paths& out_polys, - PolyFillType fillType = pftEvenOdd ); -void SimplifyPolygons( const Paths& in_polys, - Paths& out_polys, - PolyFillType fillType = pftEvenOdd ); -void SimplifyPolygons( Paths& polys, PolyFillType fillType = pftEvenOdd ); - -void CleanPolygon( const Path& in_poly, Path& out_poly, double distance = 1.415 ); -void CleanPolygon( Path& poly, double distance = 1.415 ); -void CleanPolygons( const Paths& in_polys, Paths& out_polys, double distance = 1.415 ); -void CleanPolygons( Paths& polys, double distance = 1.415 ); - -void MinkowskiSum( const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed ); -void MinkowskiSum( const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed ); -void MinkowskiDiff( const Path& poly1, const Path& poly2, Paths& solution ); - -void PolyTreeToPaths( const PolyTree& polytree, Paths& paths ); -void ClosedPathsFromPolyTree( const PolyTree& polytree, Paths& paths ); -void OpenPathsFromPolyTree( PolyTree& polytree, Paths& paths ); - -void ReversePath( Path& p ); -void ReversePaths( Paths& p ); - -struct IntRect -{ - cInt left; cInt top; cInt right; cInt bottom; -}; - -// enums that are used internally ... -enum EdgeSide -{ - esLeft = 1, esRight = 2 -}; - -// forward declarations (for stuff used internally) ... -struct TEdge; -struct IntersectNode; -struct LocalMinimum; -struct OutPt; -struct OutRec; -struct Join; - -typedef std::vector <OutRec*> PolyOutList; -typedef std::vector <TEdge*> EdgeList; -typedef std::vector <Join*> JoinList; -typedef std::vector <IntersectNode*> IntersectList; - -// ------------------------------------------------------------------------------ - -// ClipperBase is the ancestor to the Clipper class. It should not be -// instantiated directly. This class simply abstracts the conversion of sets of -// polygon coordinates into edge objects that are stored in a LocalMinima list. -class ClipperBase -{ -public: - ClipperBase(); - virtual ~ClipperBase(); - virtual bool AddPath( const Path& pg, PolyType PolyTyp, bool Closed ); - bool AddPaths( const Paths& ppg, PolyType PolyTyp, bool Closed ); - virtual void Clear(); - IntRect GetBounds(); - - bool PreserveCollinear() { return m_PreserveCollinear; }; - void PreserveCollinear( bool value ) { m_PreserveCollinear = value; }; - -protected: - void DisposeLocalMinimaList(); - TEdge* AddBoundsToLML( TEdge* e, bool IsClosed ); - virtual void Reset(); - TEdge* ProcessBound( TEdge* E, bool IsClockwise ); - void InsertScanbeam( const cInt Y ); - bool PopScanbeam( cInt& Y ); - bool LocalMinimaPending(); - bool PopLocalMinima( cInt Y, const LocalMinimum*& locMin ); - OutRec* CreateOutRec(); - void DisposeAllOutRecs(); - void DisposeOutRec( PolyOutList::size_type index ); - void SwapPositionsInAEL( TEdge* edge1, TEdge* edge2 ); - void DeleteFromAEL( TEdge* e ); - void UpdateEdgeIntoAEL( TEdge*& e ); - - typedef std::vector<LocalMinimum> MinimaList; - MinimaList::iterator m_CurrentLM; - MinimaList m_MinimaList; - - bool m_UseFullRange; - EdgeList m_edges; - bool m_PreserveCollinear; - bool m_HasOpenPaths; - PolyOutList m_PolyOuts; - TEdge* m_ActiveEdges; - - typedef std::priority_queue<cInt> ScanbeamList; - ScanbeamList m_Scanbeam; -}; -// ------------------------------------------------------------------------------ - -class Clipper : public virtual ClipperBase -{ -public: - Clipper( int initOptions = 0 ); - bool Execute( ClipType clipType, - Paths& solution, - PolyFillType fillType = pftEvenOdd ); - bool Execute( ClipType clipType, - Paths& solution, - PolyFillType subjFillType, - PolyFillType clipFillType ); - bool Execute( ClipType clipType, - PolyTree& polytree, - PolyFillType fillType = pftEvenOdd ); - bool Execute( ClipType clipType, - PolyTree& polytree, - PolyFillType subjFillType, - PolyFillType clipFillType ); - - bool ReverseSolution() { return m_ReverseOutput; }; - void ReverseSolution( bool value ) { m_ReverseOutput = value; }; - bool StrictlySimple() { return m_StrictSimple; }; - void StrictlySimple( bool value ) { m_StrictSimple = value; }; - // set the callback function for z value filling on intersections (otherwise Z is 0) -#ifdef use_xyz - void ZFillFunction( ZFillCallback zFillFunc ); - -#endif - -protected: - virtual bool ExecuteInternal(); - -private: - JoinList m_Joins; - JoinList m_GhostJoins; - IntersectList m_IntersectList; - ClipType m_ClipType; - typedef std::list<cInt> MaximaList; - MaximaList m_Maxima; - TEdge* m_SortedEdges; - bool m_ExecuteLocked; - PolyFillType m_ClipFillType; - PolyFillType m_SubjFillType; - bool m_ReverseOutput; - bool m_UsingPolyTree; - bool m_StrictSimple; -#ifdef use_xyz - ZFillCallback m_ZFill; // custom callback -#endif - void SetWindingCount( TEdge& edge ); - bool IsEvenOddFillType( const TEdge& edge ) const; - bool IsEvenOddAltFillType( const TEdge& edge ) const; - void InsertLocalMinimaIntoAEL( const cInt botY ); - void InsertEdgeIntoAEL( TEdge* edge, TEdge* startEdge ); - void AddEdgeToSEL( TEdge* edge ); - bool PopEdgeFromSEL( TEdge*& edge ); - void CopyAELToSEL(); - void DeleteFromSEL( TEdge* e ); - void SwapPositionsInSEL( TEdge* edge1, TEdge* edge2 ); - bool IsContributing( const TEdge& edge ) const; - bool IsTopHorz( const cInt XPos ); - void DoMaxima( TEdge* e ); - void ProcessHorizontals(); - void ProcessHorizontal( TEdge* horzEdge ); - void AddLocalMaxPoly( TEdge* e1, TEdge* e2, const IntPoint& pt ); - OutPt* AddLocalMinPoly( TEdge* e1, TEdge* e2, const IntPoint& pt ); - OutRec* GetOutRec( int idx ); - void AppendPolygon( TEdge* e1, TEdge* e2 ); - void IntersectEdges( TEdge* e1, TEdge* e2, IntPoint& pt ); - OutPt* AddOutPt( TEdge* e, const IntPoint& pt ); - OutPt* GetLastOutPt( TEdge* e ); - bool ProcessIntersections( const cInt topY ); - void BuildIntersectList( const cInt topY ); - void ProcessIntersectList(); - void ProcessEdgesAtTopOfScanbeam( const cInt topY ); - void BuildResult( Paths& polys ); - void BuildResult2( PolyTree& polytree ); - void SetHoleState( TEdge* e, OutRec* outrec ); - void DisposeIntersectNodes(); - bool FixupIntersectionOrder(); - void FixupOutPolygon( OutRec& outrec ); - void FixupOutPolyline( OutRec& outrec ); - bool IsHole( TEdge* e ); - bool FindOwnerFromSplitRecs( OutRec& outRec, OutRec*& currOrfl ); - void FixHoleLinkage( OutRec& outrec ); - void AddJoin( OutPt* op1, OutPt* op2, const IntPoint offPt ); - void ClearJoins(); - void ClearGhostJoins(); - void AddGhostJoin( OutPt* op, const IntPoint offPt ); - bool JoinPoints( Join* j, OutRec* outRec1, OutRec* outRec2 ); - void JoinCommonEdges(); - void DoSimplePolygons(); - void FixupFirstLefts1( OutRec* OldOutRec, OutRec* NewOutRec ); - void FixupFirstLefts2( OutRec* InnerOutRec, OutRec* OuterOutRec ); - void FixupFirstLefts3( OutRec* OldOutRec, OutRec* NewOutRec ); - -#ifdef use_xyz - void SetZ( IntPoint& pt, TEdge& e1, TEdge& e2 ); - -#endif -}; -// ------------------------------------------------------------------------------ - -class ClipperOffset -{ -public: - ClipperOffset( double miterLimit = 2.0, double roundPrecision = 0.25 ); - ~ClipperOffset(); - void AddPath( const Path& path, JoinType joinType, EndType endType ); - void AddPaths( const Paths& paths, JoinType joinType, EndType endType ); - void Execute( Paths& solution, double delta ); - void Execute( PolyTree& solution, double delta ); - void Clear(); - - double MiterLimit; - JoinType MiterFallback; - double ArcTolerance; - -private: - Paths m_destPolys; - Path m_srcPoly; - Path m_destPoly; - std::vector<DoublePoint> m_normals; - double m_delta, m_sinA, m_sin, m_cos; - double m_miterLim, m_StepsPerRad; - IntPoint m_lowest; - PolyNode m_polyNodes; - - void FixOrientations(); - void DoOffset( double delta ); - void OffsetPoint( int j, int& k, JoinType jointype ); - void DoSquare( int j, int k ); - void DoMiter( int j, int k, double r ); - void DoRound( int j, int k ); -}; -// ------------------------------------------------------------------------------ - -class clipperException : public std::exception -{ -public: - clipperException( const char* description ) : m_descr( description ) {} - virtual ~clipperException() throw() {} - virtual const char* what() const throw()override { return m_descr.c_str(); } - -private: - std::string m_descr; -}; -// ------------------------------------------------------------------------------ -} // ClipperLib namespace - -#endif // clipper_hpp