From 798e13f70dc98ac05cdae3e561061ffca71fe825 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Mon, 20 Mar 2023 16:53:31 -0700 Subject: [PATCH] Clean up various simplify steps Adds the option to simplify the output of Clipper ops that remove minor detours from the output lines. These detours are not substantive, so removing them speeds up the rest of the system by removing unimportant vertices. This also prevents the introduction of inadvertant concave points when unioning two, closely-sized rounded shapes --- libs/kimath/include/geometry/shape_poly_set.h | 10 ++++- libs/kimath/src/geometry/shape_poly_set.cpp | 40 +++++++++++++++---- pcbnew/zone_filler.cpp | 2 +- 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index f8a7ce7a63..1f16ffd4e9 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -1046,6 +1046,12 @@ public: ///< For \a aFastMode meaning, see function booleanOp void Simplify( POLYGON_MODE aFastMode ); + + ///< Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) + ///< @param aMaxError Smooths the output such that points less than aMaxError away from the line through + /// the two adjacent points will be removed + void Simplify( size_t aMaxError ); + /** * Convert a self-intersecting polygon to one (or more) non self-intersecting polygon(s). * @@ -1422,10 +1428,10 @@ private: 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& aOtherShape, size_t aMaxError = 0 ); void booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SET& aShape, - const SHAPE_POLY_SET& aOtherShape ); + const SHAPE_POLY_SET& aOtherShape, size_t aMaxError = 0 ); /** * Check whether the point \a aP is inside the \a aSubpolyIndex-th polygon of the polyset. If diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index 7fbc3474d7..df50dc280b 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -721,14 +721,14 @@ void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET } -void SHAPE_POLY_SET::booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SET& aOtherShape ) +void SHAPE_POLY_SET::booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SET& aOtherShape, size_t aMaxError ) { - booleanOp( aType, *this, aOtherShape ); + booleanOp( aType, *this, aOtherShape, aMaxError ); } void SHAPE_POLY_SET::booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SET& aShape, - const SHAPE_POLY_SET& aOtherShape ) + const SHAPE_POLY_SET& aOtherShape, size_t aMaxError ) { if( ( aShape.OutlineCount() > 1 || aOtherShape.OutlineCount() > 0 ) && ( aShape.ArcCount() > 0 || aOtherShape.ArcCount() > 0 ) ) @@ -765,7 +765,8 @@ void SHAPE_POLY_SET::booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SE c.AddSubject( paths ); c.AddClip( clips ); - Clipper2Lib::PolyTree64 solution; + Clipper2Lib::Paths64 solution; + Clipper2Lib::PolyTree64 tree; Clipper2Lib::ZCallback64 callback = [&]( const Clipper2Lib::Point64 & e1bot, const Clipper2Lib::Point64 & e1top, @@ -828,10 +829,24 @@ void SHAPE_POLY_SET::booleanOp( Clipper2Lib::ClipType aType, const SHAPE_POLY_SE c.SetZCallback( callback ); // register callback - c.Execute( aType, Clipper2Lib::FillRule::NonZero, solution ); + if( aMaxError > 0 ) + { + c.Execute( aType, Clipper2Lib::FillRule::NonZero, solution ); + Clipper2Lib::Paths64 output = Clipper2Lib::SimplifyPaths( solution, aMaxError, false ); + Clipper2Lib::Clipper64 c2; - importTree( solution, zValues, arcBuffer ); - solution.Clear(); // Free used memory (not done in dtor) + c2.PreserveCollinear = false; + c2.ReverseSolution = false; + c2.AddSubject( output ); + c2.Execute( Clipper2Lib::ClipType::Union, Clipper2Lib::FillRule::Positive, tree); + } + else + { + c.Execute( aType, Clipper2Lib::FillRule::NonZero, tree ); + } + + importTree( tree, zValues, arcBuffer ); + tree.Clear(); // Free used memory (not done in dtor) } @@ -1597,6 +1612,17 @@ void SHAPE_POLY_SET::Unfracture( POLYGON_MODE aFastMode ) } +void SHAPE_POLY_SET::Simplify( size_t aMaxError ) +{ + SHAPE_POLY_SET empty; + + if( ADVANCED_CFG::GetCfg().m_UseClipper2 ) + booleanOp( Clipper2Lib::ClipType::Union, empty, aMaxError ); + else + booleanOp( ClipperLib::ctUnion, empty, PM_FAST ); +} + + void SHAPE_POLY_SET::Simplify( POLYGON_MODE aFastMode ) { SHAPE_POLY_SET empty; diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index b596ab5594..0b7fc4ddc4 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -1299,7 +1299,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa } } - aHoles.Simplify( SHAPE_POLY_SET::PM_FAST ); + aHoles.Simplify( m_maxError ); }