From 7ee6afeace66d3c72558ee3ea47bab24c56d8cc8 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Fri, 5 Jul 2019 23:45:57 +0100 Subject: [PATCH] Performance improvement for zone filling. --- common/geometry/shape_line_chain.cpp | 8 +++++--- common/geometry/shape_poly_set.cpp | 26 +++++++++++++++++++------- include/geometry/shape_line_chain.h | 11 +++++++++-- include/geometry/shape_poly_set.h | 28 ++++++++++++++++------------ pcbnew/zone_filler.cpp | 9 +++++++-- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/common/geometry/shape_line_chain.cpp b/common/geometry/shape_line_chain.cpp index 527c868e8d..006b7b9e62 100644 --- a/common/geometry/shape_line_chain.cpp +++ b/common/geometry/shape_line_chain.cpp @@ -349,12 +349,14 @@ int SHAPE_LINE_CHAIN::PathLength( const VECTOR2I& aP ) const } -bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy ) const +bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUseBBoxCache ) const { /* - * Don't check the bounding box. Building it is about the same speed as the rigorous - * test below and so just slows things down by doing potentially two tests. + * Don't check the bounding box unless it's cached. Building it is about the same speed as + * the rigorous test below and so just slows things down by doing potentially two tests. */ + if( aUseBBoxCache && !m_bbox.Contains( aPt ) ) + return false; if( !m_closed || PointCount() < 3 ) return false; diff --git a/common/geometry/shape_poly_set.cpp b/common/geometry/shape_poly_set.cpp index a2b4691bff..4ed76bf0f2 100644 --- a/common/geometry/shape_poly_set.cpp +++ b/common/geometry/shape_poly_set.cpp @@ -1391,20 +1391,32 @@ bool SHAPE_POLY_SET::CollideEdge( const VECTOR2I& aPoint, } +void SHAPE_POLY_SET::BuildBBoxCaches() +{ + for( int polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ ) + { + Outline( polygonIdx ).GenerateBBoxCache(); + + for( int holeIdx = 0; holeIdx < HoleCount( polygonIdx ); holeIdx++ ) + Hole( polygonIdx, holeIdx ).GenerateBBoxCache(); + } +} + + bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles, - bool aIgnoreEdges ) const + bool aIgnoreEdges, bool aUseBBoxCaches ) const { if( m_polys.size() == 0 ) // empty set? return false; // If there is a polygon specified, check the condition against that polygon if( aSubpolyIndex >= 0 ) - return containsSingle( aP, aSubpolyIndex, aIgnoreHoles, aIgnoreEdges ); + return containsSingle( aP, aSubpolyIndex, aIgnoreHoles, aIgnoreEdges, aUseBBoxCaches ); // In any other case, check it against all polygons in the set for( int polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ ) { - if( containsSingle( aP, polygonIdx, aIgnoreHoles, aIgnoreEdges ) ) + if( containsSingle( aP, polygonIdx, aIgnoreHoles, aIgnoreEdges, aUseBBoxCaches ) ) return true; } @@ -1431,7 +1443,7 @@ void SHAPE_POLY_SET::RemoveVertex( VERTEX_INDEX aIndex ) bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles, - bool aIgnoreEdges ) const + bool aIgnoreEdges, bool aUseBBoxCaches ) const { // Check that the point is inside the outline if( pointInPolygon( aP, m_polys[aSubpolyIndex][0], aIgnoreEdges ) ) @@ -1445,7 +1457,7 @@ bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool // If the point is inside a hole (and not on its edge), // it is outside of the polygon - if( pointInPolygon( aP, hole, aIgnoreEdges ) ) + if( pointInPolygon( aP, hole, aIgnoreEdges, aUseBBoxCaches ) ) return false; } } @@ -1458,9 +1470,9 @@ bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool bool SHAPE_POLY_SET::pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath, - bool aIgnoreEdges ) const + bool aIgnoreEdges, bool aUseBBoxCaches ) const { - return aPath.PointInside( aP, aIgnoreEdges ? 1 : 0 ); + return aPath.PointInside( aP, aIgnoreEdges ? 1 : 0, aUseBBoxCaches ); } diff --git a/include/geometry/shape_line_chain.h b/include/geometry/shape_line_chain.h index 665a4e3463..843a1f3d68 100644 --- a/include/geometry/shape_line_chain.h +++ b/include/geometry/shape_line_chain.h @@ -3,7 +3,7 @@ * * Copyright (C) 2013 CERN * @author Tomasz Wlostowski - * Copyright (C) 2013-2017 + * Copyright (C) 2013-2019 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -305,6 +305,11 @@ public: return bbox; } + void GenerateBBoxCache() + { + m_bbox.Compute( m_points ); + } + /** * Function Collide() * @@ -552,9 +557,11 @@ public: * Checks if point aP lies inside a polygon (any type) defined by the line chain. * For closed shapes only. * @param aPt point to check + * @param aUseBBoxCache gives better peformance if the bounding boxe caches have been + * generated. * @return true if the point is inside the shape (edge is not treated as being inside). */ - bool PointInside( const VECTOR2I& aPt, int aAccuracy = 0 ) const; + bool PointInside( const VECTOR2I& aPt, int aAccuracy = 0, bool aUseBBoxCache = false ) const; /** * Function PointOnEdge() diff --git a/include/geometry/shape_poly_set.h b/include/geometry/shape_poly_set.h index affe21876b..21fe7db04f 100644 --- a/include/geometry/shape_poly_set.h +++ b/include/geometry/shape_poly_set.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015-2017 CERN + * Copyright (C) 2015-2019 CERN * @author Tomasz Wlostowski * @author Alejandro GarcĂ­a Montoro * @@ -971,6 +971,12 @@ class SHAPE_POLY_SET : public SHAPE bool CollideEdge( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex, int aClearance = 0 ); + /** + * Constructs BBoxCaches for Contains(), below. These caches MUST be built before a + * group of calls to Contains(). They are NOT kept up-to-date by editing actions. + */ + void BuildBBoxCaches(); + /** * Returns true if a given subpolygon contains the point aP * @@ -979,10 +985,13 @@ class SHAPE_POLY_SET : public SHAPE * @param aIgnoreHoles controls whether or not internal holes are considered * @param aIgnoreEdges controls whether or not a check for the point lying exactly on * the polygon edge is made + * @param aUseBBoxCaches gives faster performance when multiple calls are made with no + * editing in between, but the caller MUST cache the bbox caches + * before calling (via BuildBBoxCaches(), above) * @return true if the polygon contains the point */ bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1, bool aIgnoreHoles = false, - bool aIgnoreEdges = false ) const; + bool aIgnoreEdges = false, bool aUseBBoxCaches = false ) const; ///> Returns true if the set is empty (no polygons at all) bool IsEmpty() const @@ -1118,12 +1127,6 @@ class SHAPE_POLY_SET : public SHAPE bool IsVertexInHole( int aGlobalIdx ); private: - - SHAPE_LINE_CHAIN& getContourForCorner( int aCornerId, int& aIndexWithinContour ); - VECTOR2I& vertex( int aCornerId ); - const VECTOR2I& cvertex( int aCornerId ) const; - - void fractureSingle( POLYGON& paths ); void unfractureSingle ( POLYGON& path ); void importTree( ClipperLib::PolyTree* tree ); @@ -1146,7 +1149,7 @@ class SHAPE_POLY_SET : public SHAPE const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode ); bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath, - bool aIgnoreEdges ) const; + bool aIgnoreEdges, bool aUseBBoxCaches = false ) const; /** * containsSingle function @@ -1159,11 +1162,14 @@ class SHAPE_POLY_SET : public SHAPE * @param aIgnoreHoles can be set to true to ignore internal holes in the polygon * @param aIgnoreEdges can be set to true to skip checking whether or not the point * lies directly on the edge + * @param aUseBBoxCaches gives faster performance when multiple calls are made with no + * editing in between, but the caller MUST cache the bbox caches + * before calling (via BuildBBoxCaches(), above) * @return bool - true if aP is inside aSubpolyIndex-th polygon; false in any other * case. */ bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles = false, - bool aIgnoreEdges = false ) const; + bool aIgnoreEdges = false, bool aUseBBoxCaches = false ) const; /** * Operations ChamferPolygon and FilletPolygon are computed under the private chamferFillet @@ -1176,8 +1182,6 @@ class SHAPE_POLY_SET : public SHAPE FILLETED }; - - /** * Function chamferFilletPolygon * Returns the camfered or filleted version of the aIndex-th polygon in the set, depending diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp index 14202bd4a4..6b540ed453 100644 --- a/pcbnew/zone_filler.cpp +++ b/pcbnew/zone_filler.cpp @@ -701,12 +701,16 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, // Remove areas that don't meet minimum-width criteria testAreas.Inflate( -outline_half_thickness, numSegs, true ); testAreas.Inflate( outline_half_thickness, numSegs, true ); + testAreas.BuildBBoxCaches(); + static const bool USE_BBOX_CACHES = true; buildThermalSpokes( aZone, thermalSpokes ); for( const SHAPE_LINE_CHAIN& spoke : thermalSpokes ) { - if( testAreas.Contains( spoke.CPoint( 3 ), -1, false, true ) ) + const VECTOR2I& testPt = spoke.CPoint( 3 ); + + if( testAreas.Contains( testPt, -1, false, true, USE_BBOX_CACHES ) ) { solidAreas.AddOutline( spoke ); continue; @@ -714,7 +718,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, for( const SHAPE_LINE_CHAIN& other : thermalSpokes ) { - if( &other != &spoke && other.PointInside( spoke.CPoint( 3 ), 1 ) ) + if( &other != &spoke && other.PointInside( testPt, 1, USE_BBOX_CACHES ) ) { solidAreas.AddOutline( spoke ); break; @@ -914,6 +918,7 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone, } spoke.SetClosed( true ); + spoke.GenerateBBoxCache(); aSpokesList.push_back( std::move( spoke ) ); } }