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 );
 }