From c63f6aa3c5ce7739df8f42ffe9c137acc23918c0 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 16 Aug 2015 14:07:58 +0200 Subject: [PATCH] Polygon boolean operations on SHAPE_POLY_SET: Add a parameter (aFastMode) to speed up calculations. By default, the transforms use an option to create stricly simple polygons. In 3D viewer, this option can take a *very long* calculation time (minutes instead of seconds, or hangs) and they are now using aFastMode = true to do polygon calculations, especially for zones inside zones belong to the same net but having different priority (which creates weak polygons). aFastMode = false is the default for other calculations (zone filling) as before. --- 3d-viewer/3d_draw.cpp | 3 +- 3d-viewer/3d_draw_board_body.cpp | 25 ++++-- common/geometry/shape_poly_set.cpp | 26 +++--- include/geometry/shape_poly_set.h | 131 +++++++++++++++++------------ 4 files changed, 108 insertions(+), 77 deletions(-) diff --git a/3d-viewer/3d_draw.cpp b/3d-viewer/3d_draw.cpp index 6bd5f35257..eca5bf6b34 100644 --- a/3d-viewer/3d_draw.cpp +++ b/3d-viewer/3d_draw.cpp @@ -61,6 +61,7 @@ #include +extern bool useFastModeForPolygons; /* returns the Z orientation parameter 1.0 or -1.0 for aLayer * Z orientation is 1.0 for all layers but "back" layers: @@ -702,7 +703,7 @@ void EDA_3D_CANVAS::buildBoard3DAuxLayers( REPORTER* aErrorMessages, REPORTER* a if( bufferPolys.IsEmpty() ) continue; - bufferPolys.Fracture(); + bufferPolys.Simplify( useFastModeForPolygons ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); diff --git a/3d-viewer/3d_draw_board_body.cpp b/3d-viewer/3d_draw_board_body.cpp index e9a30bc213..0251c10855 100644 --- a/3d-viewer/3d_draw_board_body.cpp +++ b/3d-viewer/3d_draw_board_body.cpp @@ -63,6 +63,15 @@ #include #include +// An option for all operations on polygons: +// when useFastModeForPolygons = true, calculations can be *a lot* faster. +// but created polygons can be not stricty simple (can share edges) +// Although stricty simple are better for glu tesselation functions, I do not see +// any issue when allowing not stricty simple polygons. +// But I see *very* long calculations when setting useFastMode to false. +// So, be careful if changing thie option +bool useFastModeForPolygons = true; + /* returns the Z orientation parameter 1.0 or -1.0 for aLayer * Z orientation is 1.0 for all layers but "back" layers: * B_Cu , B_Adhes, B_Paste ), B_SilkS @@ -139,7 +148,7 @@ void EDA_3D_CANVAS::buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardH } } - allBoardHoles.Simplify(); + allBoardHoles.Simplify( useFastModeForPolygons ); } @@ -314,11 +323,11 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, if( currLayerHoles.OutlineCount() ) { currLayerHoles.Append(allLayerHoles); - currLayerHoles.Simplify(); - bufferPolys.BooleanSubtract( currLayerHoles ); + currLayerHoles.Simplify( useFastModeForPolygons ); + bufferPolys.BooleanSubtract( currLayerHoles, useFastModeForPolygons ); } else - bufferPolys.BooleanSubtract( allLayerHoles ); + bufferPolys.BooleanSubtract( allLayerHoles, useFastModeForPolygons ); int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer ); int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer ); @@ -414,7 +423,7 @@ void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList, zpos += (copper_thickness + epsilon) / 2.0f; board_thickness -= copper_thickness + epsilon; - bufferPcbOutlines.BooleanSubtract( allLayerHoles ); + bufferPcbOutlines.BooleanSubtract( allLayerHoles, useFastModeForPolygons ); if( !bufferPcbOutlines.IsEmpty() ) { @@ -574,15 +583,15 @@ void EDA_3D_CANVAS::buildTechLayers3DView( REPORTER* aErrorMessages, REPORTER* a bufferPolys = bufferPcbOutlines; cuts.Append(allLayerHoles); - cuts.Simplify(); + cuts.Simplify( useFastModeForPolygons ); - bufferPolys.BooleanSubtract( cuts ); + bufferPolys.BooleanSubtract( cuts, useFastModeForPolygons ); } // Remove holes from Solder paste layers and silkscreen else if( layer == B_Paste || layer == F_Paste || layer == B_SilkS || layer == F_SilkS ) { - bufferPolys.BooleanSubtract( allLayerHoles ); + bufferPolys.BooleanSubtract( allLayerHoles, useFastModeForPolygons ); } int thickness = 0; diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index 179cfe349c..1d42a41b70 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -208,11 +208,13 @@ const SHAPE_LINE_CHAIN SHAPE_POLY_SET::convertFromClipper( const Path& aPath ) #include #include -void SHAPE_POLY_SET::booleanOp( ClipType type, const SHAPE_POLY_SET& b ) +void SHAPE_POLY_SET::booleanOp( ClipType aType, const SHAPE_POLY_SET& aOtherShape, + bool aFastMode ) { Clipper c; - c.StrictlySimple( true ); + if( !aFastMode ) + c.StrictlySimple( true ); BOOST_FOREACH( const POLYGON& poly, m_polys ) { @@ -220,7 +222,7 @@ void SHAPE_POLY_SET::booleanOp( ClipType type, const SHAPE_POLY_SET& b ) c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptSubject, true ); } - BOOST_FOREACH( const POLYGON& poly, b.m_polys ) + BOOST_FOREACH( const POLYGON& poly, aOtherShape.m_polys ) { for( unsigned int i = 0; i < poly.size(); i++ ) c.AddPath( convertToClipper( poly[i], i > 0 ? false : true ), ptClip, true ); @@ -228,21 +230,21 @@ void SHAPE_POLY_SET::booleanOp( ClipType type, const SHAPE_POLY_SET& b ) PolyTree solution; - c.Execute( type, solution, pftNonZero, pftNonZero ); + c.Execute( aType, solution, pftNonZero, pftNonZero ); importTree( &solution ); } -void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& b ) +void SHAPE_POLY_SET::BooleanAdd( const SHAPE_POLY_SET& b, bool aFastMode ) { - booleanOp( ctUnion, b ); + booleanOp( ctUnion, b, aFastMode ); } -void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& b ) +void SHAPE_POLY_SET::BooleanSubtract( const SHAPE_POLY_SET& b, bool aFastMode ) { - booleanOp( ctDifference, b ); + booleanOp( ctDifference, b, aFastMode ); } @@ -496,9 +498,9 @@ void SHAPE_POLY_SET::fractureSingle( POLYGON& paths ) } -void SHAPE_POLY_SET::Fracture() +void SHAPE_POLY_SET::Fracture( bool aFastMode ) { - Simplify(); // remove overlapping holes/degeneracy + Simplify( aFastMode ); // remove overlapping holes/degeneracy BOOST_FOREACH( POLYGON& paths, m_polys ) { @@ -507,11 +509,11 @@ void SHAPE_POLY_SET::Fracture() } -void SHAPE_POLY_SET::Simplify() +void SHAPE_POLY_SET::Simplify( bool aFastMode ) { SHAPE_POLY_SET empty; - booleanOp( ctUnion, empty ); + booleanOp( ctUnion, empty, aFastMode ); } diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index 58f7e46beb..37c181df30 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -54,68 +54,69 @@ class SHAPE_POLY_SET : public SHAPE * Base class for iterating over all vertices in a given SHAPE_POLY_SET */ template - class ITERATOR_TEMPLATE { - public: + class ITERATOR_TEMPLATE + { + public: - bool IsEndContour() const + bool IsEndContour() const + { + return m_currentVertex + 1 == m_poly->CPolygon( m_currentOutline )[0].PointCount(); + } + + bool IsLastContour() const + { + return m_currentOutline == m_lastOutline; + } + + operator bool() const + { + return m_currentOutline <= m_lastOutline; + } + + void Advance() + { + m_currentVertex ++; + + if( m_currentVertex >= m_poly->CPolygon( m_currentOutline )[0].PointCount() ) { - return m_currentVertex + 1 == m_poly->CPolygon( m_currentOutline )[0].PointCount(); + m_currentVertex = 0; + m_currentOutline++; } + } - bool IsLastContour() const - { - return m_currentOutline == m_lastOutline; - } + void operator++( int dummy ) + { + Advance(); + } - operator bool() const - { - return m_currentOutline <= m_lastOutline; - } + void operator++() + { + Advance(); + } - void Advance() - { - m_currentVertex ++; + T& Get() + { + return m_poly->Polygon( m_currentOutline )[0].Point( m_currentVertex ); + } - if( m_currentVertex >= m_poly->CPolygon( m_currentOutline )[0].PointCount() ) - { - m_currentVertex = 0; - m_currentOutline++; - } - } + T& operator*() + { + return Get(); + } - void operator++( int dummy ) - { - Advance(); - } - - void operator++() - { - Advance(); - } - - T& Get() - { - return m_poly->Polygon( m_currentOutline )[0].Point( m_currentVertex ); - } - - T& operator*() - { - return Get(); - } - - T* operator->() - { - return &Get(); - } + T* operator->() + { + return &Get(); + } - private: - friend class SHAPE_POLY_SET; + private: + friend class SHAPE_POLY_SET; - SHAPE_POLY_SET* m_poly; - int m_currentOutline; - int m_lastOutline; - int m_currentVertex; + SHAPE_POLY_SET* m_poly; + int m_currentOutline; + int m_lastOutline; + int m_currentVertex; }; typedef ITERATOR_TEMPLATE ITERATOR; @@ -245,17 +246,21 @@ class SHAPE_POLY_SET : public SHAPE ///> Performs boolean polyset union - void BooleanAdd( const SHAPE_POLY_SET& b ); + ///> For aFastMode meaning, see function booleanOp + + void BooleanAdd( const SHAPE_POLY_SET& b, bool aFastMode = false ); ///> Performs boolean polyset difference - void BooleanSubtract( const SHAPE_POLY_SET& b ); + ///> For aFastMode meaning, see function booleanOp + void BooleanSubtract( const SHAPE_POLY_SET& b, bool aFastMode = false ); ///> Performs outline inflation/deflation, using round corners. void Inflate( int aFactor, int aCircleSegmentsCount ); ///> Converts a set of polygons with holes to a singe outline with "slits"/"fractures" connecting the outer ring ///> to the inner holes - void Fracture(); + ///> For aFastMode meaning, see function booleanOp + void Fracture( bool aFastMode = false); ///> Converts a set of slitted polygons to a set of polygons with holes void Unfracture(); @@ -264,7 +269,8 @@ class SHAPE_POLY_SET : public SHAPE bool HasHoles() const; ///> Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) - void Simplify(); + ///> For aFastMode meaning, see function booleanOp + void Simplify( bool aFastMode = false); /// @copydoc SHAPE::Format() const std::string Format() const; @@ -316,7 +322,20 @@ class SHAPE_POLY_SET : public SHAPE void fractureSingle( POLYGON& paths ); void importTree( ClipperLib::PolyTree* tree ); - void booleanOp( ClipperLib::ClipType type, const SHAPE_POLY_SET& b ); + + /** Function booleanOp + * this is the engine to execute all polygon boolean transforms + * (AND, OR, ... and polygon simplification (merging overlaping polygons) + * @param aType is the transform type ( see ClipperLib::ClipType ) + * @param aOtherShape is the SHAPE_LINE_CHAIN to combine with me. + * @param aFastMode is an option to choos if the result is a weak polygon + * or a stricty simple polygon. + * if aFastMode is true (default) the result can be a weak polygon + * if aFastMode is true (default) the result is (theorically) a strictly + * simple polygon, but calculations can be really significantly time consuming + */ + void booleanOp( ClipperLib::ClipType aType, + const SHAPE_POLY_SET& aOtherShape, bool aFastMode = false ); bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath ) const;