From bf676485622c218b550a72b769fdef2c7fb2b841 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Mon, 28 Sep 2020 23:27:33 +0100 Subject: [PATCH] Support optional location reporting in SHAPE collisions. Also fixes a few bugs in the collision routines. --- libs/kimath/include/geometry/shape.h | 37 +- libs/kimath/include/geometry/shape_arc.h | 6 +- libs/kimath/include/geometry/shape_circle.h | 11 +- libs/kimath/include/geometry/shape_compound.h | 9 +- libs/kimath/include/geometry/shape_null.h | 3 +- libs/kimath/include/geometry/shape_poly_set.h | 25 +- libs/kimath/include/geometry/shape_rect.h | 8 +- libs/kimath/include/geometry/shape_segment.h | 21 +- libs/kimath/include/geometry/shape_simple.h | 5 +- libs/kimath/src/geometry/seg.cpp | 54 +- libs/kimath/src/geometry/shape_arc.cpp | 58 +- libs/kimath/src/geometry/shape_collisions.cpp | 503 ++++++++++-------- libs/kimath/src/geometry/shape_compound.cpp | 41 +- libs/kimath/src/geometry/shape_line_chain.cpp | 53 +- libs/kimath/src/geometry/shape_poly_set.cpp | 94 +++- libs/kimath/src/geometry/shape_rect.cpp | 47 +- pcbnew/CMakeLists.txt | 1 - pcbnew/class_pad.cpp | 19 - pcbnew/class_pad.h | 3 - pcbnew/drc/drc_item.cpp | 2 +- .../drc/drc_test_provider_clearance_base.cpp | 91 ---- pcbnew/drc/drc_test_provider_clearance_base.h | 4 - .../drc_test_provider_copper_clearance.cpp | 83 +-- .../drc/drc_test_provider_edge_clearance.cpp | 10 +- .../drc/drc_test_provider_hole_clearance.cpp | 4 +- pcbnew/drc/drc_test_provider_silk_to_pad.cpp | 54 +- pcbnew/drc/drc_test_provider_silk_to_silk.cpp | 124 ++--- qa/drc_proto/CMakeLists.txt | 1 - 28 files changed, 744 insertions(+), 627 deletions(-) delete mode 100644 pcbnew/drc/drc_test_provider_clearance_base.cpp diff --git a/libs/kimath/include/geometry/shape.h b/libs/kimath/include/geometry/shape.h index c789feeb4d..a013c45ba3 100644 --- a/libs/kimath/include/geometry/shape.h +++ b/libs/kimath/include/geometry/shape.h @@ -167,13 +167,16 @@ public: * * Checks if the boundary of shape (this) lies closer to the point aP than aClearance, * indicating a collision. - * @param aActual an optional pointer to an int to store the actual distance in the event - * of a collision. + * @param aActual [out] an optional pointer to an int to store the actual distance in the + * event of a collision. + * @param aLocation [out] an option pointer to a point to store a nearby location in the + * event of a collision. * @return true, if there is a collision. */ - virtual bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const + virtual bool Collide( const VECTOR2I& aP, int aClearance = 0, + int* aActual = nullptr, VECTOR2I* aLocation = nullptr ) const { - return Collide( SEG( aP, aP ), aClearance, aActual ); + return Collide( SEG( aP, aP ), aClearance, aActual, aLocation ); } /** @@ -184,23 +187,30 @@ public: * @param aShape shape to check collision against * @param aClearance minimum clearance * @param aMTV minimum translation vector - * @param aActual an optional pointer to an int to store the actual distance in the event - * of a collision. + * @param aActual [out] an optional pointer to an int to store the actual distance in the + * event of a collision. + * @param aLocation [out] an option pointer to a point to store a nearby location in the + * event of a collision. * @return true, if there is a collision. */ virtual bool Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const; - virtual bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const; + + virtual bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const; /** * Function Collide() * * Checks if the boundary of shape (this) lies closer to the segment aSeg than aClearance, * indicating a collision. - * @aActual an optional pointer to an int to be updated with the actual distance in the - * case of collision. + * @param aActual [out] an optional pointer to an int to be updated with the actual distance + * int the event of a collision. + * @param aLocation [out] an option pointer to a point to store a nearby location in the + * event of a collision. * @return true, if there is a collision. */ - virtual bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const = 0; + virtual bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const = 0; /** * Function BBox() @@ -263,7 +273,8 @@ public: * of a collision. * @return true, when a collision has been found */ - virtual bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override; + virtual bool Collide( const VECTOR2I& aP, int aClearance = 0, + int* aActual = nullptr, VECTOR2I* aLocation = nullptr ) const override; /** * Function Collide() @@ -276,7 +287,9 @@ public: * @return true, when a collision has been found */ - virtual bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; + virtual bool Collide( const SEG& aSeg, int aClearance = 0, + int* aActual = nullptr, VECTOR2I* aLocation = nullptr ) const override; + SEG::ecoord SquaredDistance( const VECTOR2I& aP, bool aOutlineOnly = false ) const; /** diff --git a/libs/kimath/include/geometry/shape_arc.h b/libs/kimath/include/geometry/shape_arc.h index 961a433d3a..ca0001786d 100644 --- a/libs/kimath/include/geometry/shape_arc.h +++ b/libs/kimath/include/geometry/shape_arc.h @@ -73,8 +73,10 @@ public: const BOX2I BBox( int aClearance = 0 ) const override; - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; - bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override; + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override; + bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override; void SetWidth( int aWidth ) { diff --git a/libs/kimath/include/geometry/shape_circle.h b/libs/kimath/include/geometry/shape_circle.h index a23c1f2ca9..f413d39218 100644 --- a/libs/kimath/include/geometry/shape_circle.h +++ b/libs/kimath/include/geometry/shape_circle.h @@ -65,13 +65,18 @@ public: return BOX2I( m_center - rc, rc * 2 ); } - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { int minDist = aClearance + m_radius; - ecoord dist_sq = aSeg.SquaredDistance( m_center ); + VECTOR2I pn = aSeg.NearestPoint( m_center ); + ecoord dist_sq = ( pn - m_center ).SquaredEuclideanNorm(); - if( dist_sq == 0 || dist_sq < (ecoord) minDist * minDist ) + if( dist_sq == 0 || dist_sq < SEG::Square( minDist ) ) { + if( aLocation ) + *aLocation = pn; + if( aActual ) *aActual = std::max( 0, (int) sqrt( dist_sq ) - m_radius ); diff --git a/libs/kimath/include/geometry/shape_compound.h b/libs/kimath/include/geometry/shape_compound.h index 7e990e8ca5..986fb4512a 100644 --- a/libs/kimath/include/geometry/shape_compound.h +++ b/libs/kimath/include/geometry/shape_compound.h @@ -47,19 +47,20 @@ class SHAPE_COMPOUND : public SHAPE SHAPE_COMPOUND* Clone() const override; const std::string Format() const override; - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override; bool Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const override { return SHAPE::Collide( aShape, aClearance, aMTV ); } - bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { - return SHAPE::Collide( aShape, aClearance, aActual ); + return SHAPE::Collide( aShape, aClearance, aActual, aLocation ); } - const std::vector& Shapes() const { return m_shapes; diff --git a/libs/kimath/include/geometry/shape_null.h b/libs/kimath/include/geometry/shape_null.h index 14e2eb569e..e9e39888cb 100644 --- a/libs/kimath/include/geometry/shape_null.h +++ b/libs/kimath/include/geometry/shape_null.h @@ -57,7 +57,8 @@ public: return BOX2I(); } - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { return false; } diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index d9fe7ea9a3..a02bc7927e 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -1070,7 +1070,8 @@ class SHAPE_POLY_SET : public SHAPE * of a collision. * @return bool - true if the point aP collides with the polygon; false in any other case. */ - bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override; + bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override; /** * Function Collide @@ -1092,7 +1093,8 @@ class SHAPE_POLY_SET : public SHAPE * @return bool - true if the segment aSeg collides with the polygon; * false in any other case. */ - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override; /** * Function CollideVertex @@ -1244,10 +1246,13 @@ class SHAPE_POLY_SET : public SHAPE * computes the minimum distance between the aIndex-th polygon and aPoint. * @param aPoint is the point whose distance to the aIndex-th polygon has to be measured. * @param aIndex is the index of the polygon whose distace to aPoint has to be measured. + * @param aNearest [out] an optional pointer to be filled in with the point on the + * polyset which is closest to aPoint. * @return int - The minimum distance between aPoint and all the segments of the aIndex-th * polygon. If the point is contained in the polygon, the distance is zero. */ - SEG::ecoord SquaredDistanceToPolygon( VECTOR2I aPoint, int aIndex ) const; + SEG::ecoord SquaredDistanceToPolygon( VECTOR2I aPoint, int aIndex, + VECTOR2I* aNearest ) const; /** * Function DistanceToPolygon @@ -1256,22 +1261,26 @@ class SHAPE_POLY_SET : public SHAPE * @param aSegment is the segment whose distance to the aIndex-th polygon has to be * measured. * @param aIndex is the index of the polygon whose distace to aPoint has to be measured. - * @param aSegmentWidth is the width of the segment; defaults to zero. + * @param aNearest [out] an optional pointer to be filled in with the point on the + * polyset which is closest to aSegment. * @return int - The minimum distance between aSegment and all the segments of the * aIndex-th polygon. If the point is contained in the polygon, the * distance is zero. */ - SEG::ecoord SquaredDistanceToPolygon( const SEG& aSegment, int aIndex ) const; + SEG::ecoord SquaredDistanceToPolygon( const SEG& aSegment, int aIndex, + VECTOR2I* aNearest) const; /** * Function SquaredDistance * computes the minimum distance squared between aPoint and all the polygons in the set. * Squared distances are used because they avoid the cost of doing square-roots. * @param aPoint is the point whose distance to the set has to be measured. + * @param aNearest [out] an optional pointer to be filled in with the point on the + * polyset which is closest to aPoint. * @return The minimum distance squared between aPoint and all the polygons in the set. * If the point is contained in any of the polygons, the distance is zero. */ - SEG::ecoord SquaredDistance( VECTOR2I aPoint ) const; + SEG::ecoord SquaredDistance( VECTOR2I aPoint, VECTOR2I* aNearest = nullptr ) const; /** * Function SquaredDistance @@ -1279,10 +1288,12 @@ class SHAPE_POLY_SET : public SHAPE * Squared distances are used because they avoid the cost of doing square-roots. * @param aSegment is the segment whose distance to the polygon set has to be measured. * @param aSegmentWidth is the width of the segment; defaults to zero. + * @param aNearest [out] an optional pointer to be filled in with the point on the + * polyset which is closest to aSegment. * @return The minimum distance squared between aSegment and all the polygons in the set. * If the point is contained in the polygon, the distance is zero. */ - SEG::ecoord SquaredDistance( const SEG& aSegment ) const; + SEG::ecoord SquaredDistance( const SEG& aSegment, VECTOR2I* aNearest = nullptr ) const; /** * Function IsVertexInHole. diff --git a/libs/kimath/include/geometry/shape_rect.h b/libs/kimath/include/geometry/shape_rect.h index e5c012db8c..dc968f9e3e 100644 --- a/libs/kimath/include/geometry/shape_rect.h +++ b/libs/kimath/include/geometry/shape_rect.h @@ -95,13 +95,15 @@ public: return SHAPE::Collide( aShape, aClearance, aMTV ); } - bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { - return SHAPE::Collide( aShape, aClearance, aActual ); + return SHAPE::Collide( aShape, aClearance, aActual, aLocation ); } /// @copydoc SHAPE::Collide() - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override; /** * Function GetPosition() diff --git a/libs/kimath/include/geometry/shape_segment.h b/libs/kimath/include/geometry/shape_segment.h index 7cc74cd00b..c05fe18f4b 100644 --- a/libs/kimath/include/geometry/shape_segment.h +++ b/libs/kimath/include/geometry/shape_segment.h @@ -61,18 +61,23 @@ public: return SHAPE::Collide( aShape, aClearance, aMTV ); } - bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { - return SHAPE::Collide( aShape, aClearance, aActual ); + return SHAPE::Collide( aShape, aClearance, aActual, aLocation ); } - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { int min_dist = ( m_width + 1 ) / 2 + aClearance; ecoord dist_sq = m_seg.SquaredDistance( aSeg ); - if( dist_sq == 0 || dist_sq < (ecoord) min_dist * min_dist ) + if( dist_sq == 0 || dist_sq < SEG::Square( min_dist ) ) { + if( aLocation ) + *aLocation = m_seg.NearestPoint( aSeg ); + if( aActual ) *aActual = std::max( 0, (int) sqrt( dist_sq ) - ( m_width + 1 ) / 2 ); @@ -82,13 +87,17 @@ public: return false; } - bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { int min_dist = ( m_width + 1 ) / 2 + aClearance; ecoord dist_sq = m_seg.SquaredDistance( aP ); - if( dist_sq == 0 || dist_sq < (ecoord) min_dist * min_dist ) + if( dist_sq == 0 || dist_sq < SEG::Square( min_dist ) ) { + if( aLocation ) + *aLocation = m_seg.NearestPoint( aP ); + if( aActual ) *aActual = std::max( 0, (int) sqrt( dist_sq ) - ( m_width + 1 ) / 2 ); diff --git a/libs/kimath/include/geometry/shape_simple.h b/libs/kimath/include/geometry/shape_simple.h index f4dda9d01f..57a6f26540 100644 --- a/libs/kimath/include/geometry/shape_simple.h +++ b/libs/kimath/include/geometry/shape_simple.h @@ -160,9 +160,10 @@ public: } /// @copydoc SHAPE::Collide() - bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr, + VECTOR2I* aLocation = nullptr ) const override { - return m_points.Collide( aSeg, aClearance, aActual ); + return m_points.Collide( aSeg, aClearance, aActual, aLocation ); } void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override diff --git a/libs/kimath/src/geometry/seg.cpp b/libs/kimath/src/geometry/seg.cpp index b0fd5215f6..701313c915 100644 --- a/libs/kimath/src/geometry/seg.cpp +++ b/libs/kimath/src/geometry/seg.cpp @@ -36,57 +36,27 @@ int sgn( T aVal ) SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const { - // fixme: rather inefficient.... - if( Intersect( aSeg ) ) - return 0; + VECTOR2I closestOnRef = NearestPoint( aSeg ); + VECTOR2I closestOnASeg = aSeg.NearestPoint( *this ); - const VECTOR2I pts[4] = - { - aSeg.NearestPoint( A ) - A, - aSeg.NearestPoint( B ) - B, - NearestPoint( aSeg.A ) - aSeg.A, - NearestPoint( aSeg.B ) - aSeg.B - }; - - ecoord m = VECTOR2I::ECOORD_MAX; - - for( int i = 0; i < 4; i++ ) - m = std::min( m, pts[i].SquaredEuclideanNorm() ); - - return m; + return ( closestOnRef - closestOnASeg ).SquaredEuclideanNorm(); } const VECTOR2I SEG::NearestPoint( const SEG& aSeg ) const { - if( auto p = Intersect( aSeg ) ) + if( OPT_VECTOR2I p = Intersect( aSeg ) ) return *p; - const VECTOR2I pts_origin[4] = - { - aSeg.NearestPoint( A ), - aSeg.NearestPoint( B ), - NearestPoint( aSeg.A ), - NearestPoint( aSeg.B ) - }; + VECTOR2I nearestA = NearestPoint( aSeg.A ); + VECTOR2I deltaA = nearestA - aSeg.A; + VECTOR2I nearestB = NearestPoint( aSeg.B ); + VECTOR2I deltaB = nearestB - aSeg.B; - const ecoord pts_dist[4] = - { - ( pts_origin[0] - A ).SquaredEuclideanNorm(), - ( pts_origin[1] - B ).SquaredEuclideanNorm(), - ( pts_origin[2] - aSeg.A ).SquaredEuclideanNorm(), - ( pts_origin[3] - aSeg.B ).SquaredEuclideanNorm() - }; - - int min_i = 0; - - for( int i = 0; i < 4; i++ ) - { - if( pts_dist[i] < pts_dist[min_i] ) - min_i = i; - } - - return pts_origin[min_i]; + if( deltaA.SquaredEuclideanNorm() < deltaB.SquaredEuclideanNorm() ) + return nearestA; + else + return nearestB; } diff --git a/libs/kimath/src/geometry/shape_arc.cpp b/libs/kimath/src/geometry/shape_arc.cpp index 178b97def1..92bb62453a 100644 --- a/libs/kimath/src/geometry/shape_arc.cpp +++ b/libs/kimath/src/geometry/shape_arc.cpp @@ -65,11 +65,13 @@ SHAPE_ARC::SHAPE_ARC( const SHAPE_ARC& aOther ) } -bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual ) const +bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I* aLocation ) const { int minDist = aClearance + m_width / 2; VECTOR2I center = GetCenter(); - ecoord dist_sq = VECTOR2I::ECOORD_MAX; + ecoord dist_sq; + ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX; + VECTOR2I nearest; VECTOR2I ab = ( aSeg.B - aSeg.A ); VECTOR2I ac = ( center - aSeg.A ); @@ -84,17 +86,46 @@ bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual ) const p.x = (double) aSeg.A.x * lambda + (double) aSeg.B.x * (1.0 - lambda); p.y = (double) aSeg.A.y * lambda + (double) aSeg.B.y * (1.0 - lambda); - dist_sq = std::min( dist_sq, ( m_start - p ).SquaredEuclideanNorm() ); - dist_sq = std::min( dist_sq, ( m_end - p ).SquaredEuclideanNorm() ); + dist_sq = ( m_start - p ).SquaredEuclideanNorm(); + + if( dist_sq < closest_dist_sq ) + { + closest_dist_sq = dist_sq; + nearest = p; + } + + dist_sq = ( m_end - p ).SquaredEuclideanNorm(); + + if( dist_sq < closest_dist_sq ) + { + closest_dist_sq = dist_sq; + nearest = p; + } } - dist_sq = std::min( dist_sq, aSeg.SquaredDistance( m_start ) ); - dist_sq = std::min( dist_sq, aSeg.SquaredDistance( m_end ) ); + dist_sq = aSeg.SquaredDistance( m_start ); - if( dist_sq == 0 || dist_sq < (ecoord) minDist * minDist ) + if( dist_sq < closest_dist_sq ) { + closest_dist_sq = dist_sq; + nearest = m_start; + } + + dist_sq = aSeg.SquaredDistance( m_end ); + + if( dist_sq < closest_dist_sq ) + { + closest_dist_sq = dist_sq; + nearest = m_end; + } + + if( closest_dist_sq == 0 || closest_dist_sq < SEG::Square( minDist ) ) + { + if( aLocation ) + *aLocation = nearest; + if( aActual ) - *aActual = std::max( 0, (int) sqrt( dist_sq ) - m_width / 2 ); + *aActual = std::max( 0, (int) sqrt( closest_dist_sq ) - m_width / 2 ); return true; } @@ -156,7 +187,8 @@ const BOX2I SHAPE_ARC::BBox( int aClearance ) const } -bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const +bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance, int* aActual, + VECTOR2I* aLocation ) const { int minDist = aClearance + m_width / 2; auto bbox = BBox( minDist ); @@ -164,15 +196,17 @@ bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) cons if( !bbox.Contains( aP ) ) return false; - ecoord min_dist_sq = (ecoord) minDist * minDist; - ecoord r = GetRadius(); - ecoord r_sq = r * r; + ecoord min_dist_sq = SEG::Square( minDist ); + ecoord r_sq = SEG::Square( GetRadius() ); ecoord dist_sq = ( aP - GetCenter() ).SquaredEuclideanNorm(); ecoord dist_to_edge_sq = abs( dist_sq - r_sq ); if( dist_to_edge_sq < min_dist_sq ) { + if( aLocation ) + *aLocation = ( aP + GetCenter() ) / 2; + if( aActual ) *aActual = std::max( 0, (int) sqrt( dist_to_edge_sq ) - m_width / 2 ); diff --git a/libs/kimath/src/geometry/shape_collisions.cpp b/libs/kimath/src/geometry/shape_collisions.cpp index ad8e8585b7..6f150f514e 100644 --- a/libs/kimath/src/geometry/shape_collisions.cpp +++ b/libs/kimath/src/geometry/shape_collisions.cpp @@ -41,37 +41,41 @@ typedef VECTOR2I::extended_type ecoord; static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CIRCLE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { ecoord min_dist = aClearance + aA.GetRadius() + aB.GetRadius(); ecoord min_dist_sq = min_dist * min_dist; const VECTOR2I delta = aB.GetCenter() - aA.GetCenter(); - ecoord dist_sq = delta.SquaredEuclideanNorm(); - if( dist_sq >= min_dist_sq ) - return false; + if( dist_sq < min_dist_sq ) + { + if( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() - aB.GetRadius() ); - if( aActual ) - *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() - aB.GetRadius() ); + if( aLocation ) + *aLocation = ( aA.GetCenter() + aB.GetCenter() ) / 2; - if( aMTV ) - *aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error + if( aMTV ) + *aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error - return true; + return true; + } + + return false; } static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { const VECTOR2I c = aB.GetCenter(); const VECTOR2I p0 = aA.GetPosition(); const VECTOR2I size = aA.GetSize(); const int r = aB.GetRadius(); const int min_dist = aClearance + r; - const ecoord min_dist_sq = (ecoord) min_dist * min_dist; + const ecoord min_dist_sq = SEG::Square( min_dist ); const VECTOR2I vts[] = { @@ -88,14 +92,9 @@ static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aC bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x ) && c.y >= p0.y && c.y <= ( p0.y + size.y ); - // If we're not looking for MTV, short-circuit once we find a hard collision - if( !aMTV && inside ) - { - if( aActual ) - *aActual = 0; - + // If we're not looking for MTV or actual, short-circuit once we find a hard collision + if( inside && !aActual && !aLocation && !aMTV ) return true; - } for( int i = 0; i < 4; i++ ) { @@ -104,35 +103,39 @@ static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aC VECTOR2I pn = side.NearestPoint( c ); ecoord side_dist_sq = ( pn - c ).SquaredEuclideanNorm(); - // If we're not looking for MTV or actual, short-circuit once we find any collision - if( !aMTV && !aActual && ( side_dist_sq == 0 || side_dist_sq < min_dist_sq ) ) - return true; - if( side_dist_sq < nearest_side_dist_sq ) { nearest = pn; nearest_side_dist_sq = side_dist_sq; + + // If we're not looking for actual or MTV, short-circuit once we find any collision + if( ( nearest_side_dist_sq == 0 || !aActual ) && !aMTV ) + break; } } - if( !inside && nearest_side_dist_sq >= min_dist_sq ) - return false; - - VECTOR2I delta = c - nearest; - - if( aActual ) - *aActual = std::max( 0, (int) sqrt( nearest_side_dist_sq ) - r ); - - if( aMTV ) + if( inside || nearest_side_dist_sq < min_dist_sq ) { - if( inside ) - *aMTV = -delta.Resize( abs( min_dist + 1 + sqrt( nearest_side_dist_sq ) ) + 1 ); - else - *aMTV = delta.Resize( abs( min_dist + 1 - sqrt( nearest_side_dist_sq ) ) + 1 ); + if( aLocation ) + *aLocation = nearest; + + if( aActual ) + *aActual = std::max( 0, (int) sqrt( nearest_side_dist_sq ) - r ); + + if( aMTV ) + { + VECTOR2I delta = c - nearest; + + if( inside ) + *aMTV = -delta.Resize( abs( min_dist + 1 + sqrt( nearest_side_dist_sq ) ) + 1 ); + else + *aMTV = delta.Resize( abs( min_dist + 1 - sqrt( nearest_side_dist_sq ) ) + 1 ); + } + + return true; } - - return true; + return false; } @@ -162,96 +165,116 @@ static VECTOR2I pushoutForce( const SHAPE_CIRCLE& aA, const SEG& aB, int aCleara return f; } -#if 0 -static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + +static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE& aB, + int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { - bool collided = false; + int closest_dist = INT_MAX; + VECTOR2I nearest; for( int s = 0; s < aB.GetSegmentCount(); s++ ) { - if( aA.Collide( aB.GetSegment( s ), aClearance, aActual ) ) + int collision_dist = 0; + VECTOR2I pn; + + if( aA.Collide( aB.GetSegment( s ), aClearance, + aActual || aLocation ? &collision_dist : nullptr, + aLocation ? &pn : nullptr ) ) { - collided = true; - break; + if( collision_dist < closest_dist ) + { + nearest = pn; + closest_dist = collision_dist; + + if( closest_dist == 0 || !aActual ) + break; + } } } - if( !collided ) - return false; - - if( aMTV ) + if( closest_dist < aClearance ) { - SHAPE_CIRCLE cmoved( aA ); - VECTOR2I f_total( 0, 0 ); + if( aLocation ) + *aLocation = nearest; - for( int s = 0; s < aB.GetSegmentCount(); s++ ) + if( aActual ) + *aActual = closest_dist; + + if( aMTV ) { - VECTOR2I f = pushoutForce( cmoved, aB.GetSegment( s ), aClearance ); - cmoved.SetCenter( cmoved.GetCenter() + f ); - f_total += f; + SHAPE_CIRCLE cmoved( aA ); + VECTOR2I f_total( 0, 0 ); + + for( int s = 0; s < aB.GetSegmentCount(); s++ ) + { + VECTOR2I f = pushoutForce( cmoved, aB.GetSegment( s ), aClearance ); + cmoved.SetCenter( cmoved.GetCenter() + f ); + f_total += f; + } + + *aMTV = f_total; } - *aMTV = f_total; + return true; } - return true; -} -#endif - -static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) -{ - int min_dist = aClearance + aA.GetRadius(); - ecoord dist_sq = aB.SquaredDistance( aA.GetCenter() ); - - if( dist_sq > (ecoord) min_dist * min_dist ) - return false; - - if( aActual ) - *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() ); - - if( aMTV ) - { - SHAPE_CIRCLE cmoved( aA ); - VECTOR2I f_total( 0, 0 ); - - for( int s = 0; s < aB.GetSegmentCount(); s++ ) - { - VECTOR2I f = pushoutForce( cmoved, aB.GetSegment( s ), aClearance ); - cmoved.SetCenter( cmoved.GetCenter() + f ); - f_total += f; - } - - *aMTV = f_total; - } - return true; + return false; } static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SEGMENT& aSeg, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { - if( !aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, aActual ) ) - return false; + if( aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, aActual, aLocation ) ) + { + if( aMTV ) + *aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2); - if( aMTV ) - *aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2); + return true; + } - return true; + return false; } -static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) +static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CHAIN_BASE& aB, + int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { // TODO: why doesn't this handle MTV? // TODO: worse, why this doesn't handle closed shapes? + int closest_dist = INT_MAX; + VECTOR2I nearest; + for( int i = 0; i < aB.GetSegmentCount(); i++ ) { - if( aA.Collide( aB.GetSegment( i ), aClearance, aActual ) ) - return true; + int collision_dist = 0; + VECTOR2I pn; + + if( aA.Collide( aB.GetSegment( i ), aClearance, + aActual || aLocation ? &collision_dist : nullptr, + aLocation ? &pn : nullptr ) ) + { + if( collision_dist < closest_dist ) + { + nearest = pn; + closest_dist = collision_dist; + + if( closest_dist == 0 || !aActual ) + break; + } + } + } + + if( closest_dist < aClearance ) + { + if( aLocation ) + *aLocation = nearest; + + if( aActual ) + *aActual = closest_dist; + + return true; } return false; @@ -259,41 +282,66 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CH static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { - int minActual = INT_MAX; - int actual; + // TODO: why doesn't this handle MTV? + + if( aB.IsClosed() && aB.PointInside( aA.Centre() ) ) + { + if( aLocation ) + *aLocation = aA.Centre(); + + if( aActual ) + *aActual = 0; + + return true; + } + + int closest_dist = INT_MAX; + VECTOR2I nearest; for( int s = 0; s < aB.GetSegmentCount(); s++ ) { - if( aA.Collide( aB.GetSegment( s ), aClearance, &actual ) ) - { - minActual = std::min( minActual, actual ); + int collision_dist = 0; + VECTOR2I pn; - // If we're not looking for MTV or Actual, short-circuit after any collision - if( !aActual && !aMTV ) - return true; + if( aA.Collide( aB.GetSegment( s ), aClearance, + aActual || aLocation ? &collision_dist : nullptr, + aLocation ? &pn : nullptr ) ) + { + if( collision_dist < closest_dist ) + { + nearest = pn; + closest_dist = collision_dist; + + if( closest_dist == 0 || !aActual ) + break; + } } } - if( aActual ) - *aActual = std::max( 0, minActual ); + if( closest_dist < aClearance ) + { + if( aLocation ) + *aLocation = nearest; - // TODO: why doesn't this handle MTV? + if( aActual ) + *aActual = closest_dist; - return minActual < INT_MAX; + return true; + } + + return false; } static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aSeg, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { - int actual; - - if( aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, &actual ) ) + if( aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, aActual, aLocation ) ) { if( aActual ) - *aActual = std::max( 0, actual - aSeg.GetWidth() / 2 ); + *aActual = std::max( 0, *aActual - aSeg.GetWidth() / 2 ); // TODO: why doesn't this handle MTV? @@ -305,14 +353,12 @@ static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aSeg, int static inline bool Collide( const SHAPE_SEGMENT& aA, const SHAPE_SEGMENT& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { - int actual; - - if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, &actual ) ) + if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation ) ) { if( aActual ) - *aActual = std::max( 0, actual - aB.GetWidth() / 2 ); + *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 ); // TODO: why doesn't this handle MTV? @@ -323,15 +369,13 @@ static inline bool Collide( const SHAPE_SEGMENT& aA, const SHAPE_SEGMENT& aB, in } -static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) +static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT& aB, + int aClearance, int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { - int actual; - - if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, &actual ) ) + if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, aActual, aLocation ) ) { if( aActual ) - *aActual = std::max( 0, actual - aB.GetWidth() / 2 ); + *aActual = std::max( 0, *aActual - aB.GetWidth() / 2 ); // TODO: why doesn't this handle MTV? @@ -343,23 +387,23 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { - return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aMTV ); + return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aLocation, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_RECT& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB.Outline(), aClearance, aActual, aMTV ); + return Collide( lc, aB.Outline(), aClearance, aActual, aLocation, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_CIRCLE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - bool rv = Collide( aB, lc, aClearance, aActual, aMTV ); + bool rv = Collide( aB, lc, aClearance, aActual, aLocation, aMTV ); if( rv && aMTV ) *aMTV = - *aMTV ; @@ -368,50 +412,50 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_CIRCLE& aB, int aCl } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB, aClearance, aActual, aMTV ); + return Collide( lc, aB, aClearance, aActual, aLocation, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_SEGMENT& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB, aClearance, aActual, aMTV ); + return Collide( lc, aB, aClearance, aActual, aLocation, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB, aClearance, aActual, aMTV ); + return Collide( lc, aB, aClearance, aActual, aLocation, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_ARC& aB, int aClearance, - int* aActual, VECTOR2I* aMTV ) + int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV ) { const auto lcA = aA.ConvertToPolyline(); const auto lcB = aB.ConvertToPolyline(); - return Collide( lcA, lcB, aClearance, aActual, aMTV ); + return Collide( lcA, lcB, aClearance, aActual, aLocation, aMTV ); } template inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, - VECTOR2I* aMTV ) + VECTOR2I* aLocation, VECTOR2I* aMTV ) { return Collide( *static_cast( aA ), *static_cast( aB ), - aClearance, aActual, aMTV); + aClearance, aActual, aLocation, aMTV); } template inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, - VECTOR2I* aMTV ) + VECTOR2I* aLocation, VECTOR2I* aMTV ) { bool rv = Collide( *static_cast( aB ), *static_cast( aA ), - aClearance, aActual, aMTV); + aClearance, aActual, aLocation, aMTV); if( rv && aMTV) *aMTV = - *aMTV; @@ -420,10 +464,9 @@ inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, } -static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, VECTOR2I* aMTV ) +static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, + VECTOR2I* aLocation, VECTOR2I* aMTV ) { - - switch( aA->Type() ) { case SH_NULL: @@ -433,23 +476,23 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc switch( aB->Type() ) { case SH_RECT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_CIRCLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SIMPLE: case SH_POLY_SET_TRIANGLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + return CollCaseReversed( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_NULL: return false; @@ -463,23 +506,23 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc switch( aB->Type() ) { case SH_RECT: - return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + return CollCaseReversed( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_CIRCLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SIMPLE: case SH_POLY_SET_TRIANGLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + return CollCaseReversed( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_NULL: return false; @@ -493,23 +536,23 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc switch( aB->Type() ) { case SH_RECT: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_CIRCLE: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SIMPLE: case SH_POLY_SET_TRIANGLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + return CollCaseReversed( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_NULL: return false; @@ -523,23 +566,23 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc switch( aB->Type() ) { case SH_RECT: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_CIRCLE: - return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + return CollCaseReversed( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_LINE_CHAIN: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SIMPLE: case SH_POLY_SET_TRIANGLE: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + return CollCaseReversed( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_NULL: return false; @@ -554,23 +597,23 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc switch( aB->Type() ) { case SH_RECT: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_CIRCLE: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_LINE_CHAIN: - return CollCase( aB, aA, aClearance, aActual, aMTV ); + return CollCase( aB, aA, aClearance, aActual, aLocation, aMTV ); case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SIMPLE: case SH_POLY_SET_TRIANGLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + return CollCaseReversed( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_NULL: return false; @@ -584,23 +627,23 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc switch( aB->Type() ) { case SH_RECT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_CIRCLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_SIMPLE: case SH_POLY_SET_TRIANGLE: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_ARC: - return CollCase( aA, aB, aClearance, aActual, aMTV ); + return CollCase( aA, aB, aClearance, aActual, aLocation, aMTV ); case SH_NULL: return false; @@ -622,93 +665,123 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc return false; } -static bool collideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, VECTOR2I* aMTV ) +static bool collideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, + VECTOR2I* aLocation, VECTOR2I* aMTV ) { int currentActual = std::numeric_limits::max(); + VECTOR2I currentLocation; VECTOR2I currentMTV(0, 0); bool colliding = false; - bool exitOnFirstCollision = aActual == nullptr && aMTV == nullptr; + auto canExit = + [&]() + { + if( !colliding ) + return false; - auto collideCompoundSubshapes = [&] ( const SHAPE* elemA, const SHAPE* elemB, int clearance ) -> bool - { - int actual; + if( aActual && currentActual > 0 ) + return false; + + if( aMTV ) + return false; + + return true; + }; + + auto collideCompoundSubshapes = + [&]( const SHAPE* elemA, const SHAPE* elemB, int clearance ) -> bool + { + int actual = 0; + VECTOR2I location; VECTOR2I mtv; - bool c = collideSingleShapes( elemA, elemB, clearance, - aActual ? &actual : nullptr, - aMTV ? &mtv : nullptr ); - if(c) + if( collideSingleShapes( elemA, elemB, clearance, + aActual || aLocation ? &actual : nullptr, + aLocation ? &location : nullptr, + aMTV ? &mtv : nullptr ) ) { - if (aActual) + if( actual < currentActual ) { - currentActual = std::min( actual, currentActual ); + currentActual = actual; + currentLocation = location; } - if( aMTV ) + + if( aMTV && mtv.SquaredEuclideanNorm() > currentMTV.SquaredEuclideanNorm() ) { - if( mtv.SquaredEuclideanNorm() > currentMTV.SquaredEuclideanNorm() ) - currentMTV = mtv; + currentMTV = mtv; } + + return true; } - return c; - }; + return false; + }; if( aA->Type() == SH_COMPOUND && aB->Type() == SH_COMPOUND ) { - auto cmpA = static_cast( aA ); - auto cmpB = static_cast( aB ); + const SHAPE_COMPOUND* cmpA = static_cast( aA ); + const SHAPE_COMPOUND* cmpB = static_cast( aB ); - for( auto elemA : cmpA->Shapes() ) + for( const SHAPE* elemA : cmpA->Shapes() ) { - for( auto elemB : cmpB->Shapes() ) + for( const SHAPE* elemB : cmpB->Shapes() ) { if( collideCompoundSubshapes( elemA, elemB, aClearance ) ) { colliding = true; - if ( exitOnFirstCollision ) + + if( canExit() ) break; } } - if( colliding && exitOnFirstCollision ) + + if( canExit() ) break; } } else if( aA->Type() == SH_COMPOUND ) { - auto cmpA = static_cast( aA ); - for( auto elemA : cmpA->Shapes() ) + const SHAPE_COMPOUND* cmpA = static_cast( aA ); + + for( const SHAPE* elemA : cmpA->Shapes() ) { if( collideCompoundSubshapes( elemA, aB, aClearance ) ) { colliding = true; - if ( exitOnFirstCollision ) + + if( canExit() ) break; } } } - else if ( aB->Type() == SH_COMPOUND ) + else if( aB->Type() == SH_COMPOUND ) { - auto cmpB = static_cast( aB ); - for( auto elemB : cmpB->Shapes() ) + const SHAPE_COMPOUND* cmpB = static_cast( aB ); + + for( const SHAPE* elemB : cmpB->Shapes() ) { if( collideCompoundSubshapes( aA, elemB, aClearance ) ) { colliding = true; - if ( exitOnFirstCollision ) + + if( canExit() ) break; } } } else { - return collideSingleShapes( aA, aB, aClearance, aActual, aMTV ); + return collideSingleShapes( aA, aB, aClearance, aActual, aLocation, aMTV ); } if( colliding ) { + if( aLocation ) + *aLocation = currentLocation; + if( aActual ) *aActual = currentActual; + if( aMTV ) *aMTV = currentMTV; } @@ -718,13 +791,13 @@ static bool collideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int bool SHAPE::Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const { - return collideShapes( this, aShape, aClearance, nullptr, aMTV ); + return collideShapes( this, aShape, aClearance, nullptr, nullptr, aMTV ); } -bool SHAPE::Collide( const SHAPE* aShape, int aClearance, int* aActual ) const +bool SHAPE::Collide( const SHAPE* aShape, int aClearance, int* aActual, VECTOR2I* aLocation ) const { - return collideShapes( this, aShape, aClearance, aActual, nullptr ); + return collideShapes( this, aShape, aClearance, aActual, aLocation, nullptr ); } diff --git a/libs/kimath/src/geometry/shape_compound.cpp b/libs/kimath/src/geometry/shape_compound.cpp index 0292b79527..dad7483d5e 100644 --- a/libs/kimath/src/geometry/shape_compound.cpp +++ b/libs/kimath/src/geometry/shape_compound.cpp @@ -113,23 +113,42 @@ bool SHAPE_COMPOUND::IsSolid() const } -bool SHAPE_COMPOUND::Collide( const SEG& aSeg, int aClearance, int* aActual ) const +bool SHAPE_COMPOUND::Collide( const SEG& aSeg, int aClearance, int* aActual, + VECTOR2I* aLocation ) const { - int dist = std::numeric_limits::max(); + int closest_dist = std::numeric_limits::max(); + VECTOR2I nearest; - for( auto& item : m_shapes ) + for( SHAPE* item : m_shapes ) { - if( item->Collide( aSeg, aClearance, aActual ) ) - { - if( !aActual || *aActual == 0 ) - return true; + int actual = 0; + VECTOR2I pn; - dist = std::min( dist, *aActual ); + if( item->Collide( aSeg, aClearance, + aActual || aLocation ? &actual : nullptr, + aLocation ? &pn : nullptr ) ) + { + if( actual < closest_dist ) + { + nearest = pn; + closest_dist = actual; + + if( closest_dist == 0 || !aActual ) + break; + } } } - if( aActual ) - *aActual = dist; + if( closest_dist < aClearance ) + { + if( aLocation ) + *aLocation = nearest; - return dist != std::numeric_limits::max(); + if( aActual ) + *aActual = closest_dist; + + return true; + } + + return false; } diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp index fde6286ba2..f862e52fdf 100644 --- a/libs/kimath/src/geometry/shape_line_chain.cpp +++ b/libs/kimath/src/geometry/shape_line_chain.cpp @@ -85,26 +85,38 @@ void SHAPE_LINE_CHAIN::convertArc( ssize_t aArcIndex ) } -bool SHAPE_LINE_CHAIN_BASE::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const +bool SHAPE_LINE_CHAIN_BASE::Collide( const VECTOR2I& aP, int aClearance, int* aActual, + VECTOR2I* aLocation ) const { - SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX; + SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX; SEG::ecoord clearance_sq = SEG::Square( aClearance ); + VECTOR2I nearest; // fixme: why this only checks open curves? for( int i = 0; i < GetSegmentCount(); i++ ) { const SEG& s = GetSegment( i ); - dist_sq = std::min( dist_sq, s.SquaredDistance( aP ) ); + VECTOR2I pn = s.NearestPoint( aP ); + SEG::ecoord dist_sq = ( pn - aP ).SquaredEuclideanNorm(); - if( ( dist_sq == 0 || dist_sq < clearance_sq ) && !aActual ) - return true; + if( dist_sq < closest_dist_sq ) + { + nearest = pn; + closest_dist_sq = dist_sq; + + if( closest_dist_sq == 0 || !aActual ) + break; + } } - if( dist_sq == 0 || dist_sq < clearance_sq ) + if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq ) { + if( aLocation ) + *aLocation = nearest; + if( aActual ) - *aActual = sqrt( dist_sq ); + *aActual = sqrt( closest_dist_sq ); return true; } @@ -127,24 +139,37 @@ void SHAPE_LINE_CHAIN::Rotate( double aAngle, const VECTOR2I& aCenter ) } -bool SHAPE_LINE_CHAIN_BASE::Collide( const SEG& aSeg, int aClearance, int* aActual ) const +bool SHAPE_LINE_CHAIN_BASE::Collide( const SEG& aSeg, int aClearance, int* aActual, + VECTOR2I* aLocation ) const { - SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX; + SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX; SEG::ecoord clearance_sq = SEG::Square( aClearance ); + VECTOR2I nearest; for( int i = 0; i < GetSegmentCount(); i++ ) { const SEG& s = GetSegment( i ); - dist_sq = std::min( dist_sq, s.SquaredDistance( aSeg ) ); + SEG::ecoord dist_sq =s.SquaredDistance( aSeg ); - if( ( dist_sq == 0 || dist_sq < clearance_sq ) && !aActual ) - return true; + if( dist_sq < closest_dist_sq ) + { + if( aLocation ) + nearest = s.NearestPoint( aSeg ); + + closest_dist_sq = dist_sq; + + if( closest_dist_sq == 0 || !aActual ) + break; + } } - if( dist_sq == 0 || dist_sq < clearance_sq ) + if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq ) { + if( aLocation ) + *aLocation = nearest; + if( aActual ) - *aActual = sqrt( dist_sq ); + *aActual = sqrt( closest_dist_sq ); return true; } diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index 722dc40749..1c32126658 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -1224,12 +1224,17 @@ bool SHAPE_POLY_SET::PointOnEdge( const VECTOR2I& aP ) const } -bool SHAPE_POLY_SET::Collide( const SEG& aSeg, int aClearance, int* aActual ) const +bool SHAPE_POLY_SET::Collide( const SEG& aSeg, int aClearance, int* aActual, + VECTOR2I* aLocation ) const { - ecoord dist_sq = SquaredDistance( aSeg ); + VECTOR2I nearest; + ecoord dist_sq = SquaredDistance( aSeg, aLocation ? &nearest : nullptr ); - if( dist_sq == 0 || dist_sq < (ecoord) aClearance * aClearance ) + if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) ) { + if( aLocation ) + *aLocation = nearest; + if( aActual ) *aActual = sqrt( dist_sq ); @@ -1240,12 +1245,17 @@ bool SHAPE_POLY_SET::Collide( const SEG& aSeg, int aClearance, int* aActual ) co } -bool SHAPE_POLY_SET::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const +bool SHAPE_POLY_SET::Collide( const VECTOR2I& aP, int aClearance, int* aActual, + VECTOR2I* aLocation ) const { - ecoord dist_sq = SquaredDistance( aP ); + VECTOR2I nearest; + ecoord dist_sq = SquaredDistance( aP, aLocation ? &nearest : nullptr ); - if( dist_sq == 0 || dist_sq < (ecoord) aClearance * aClearance ) + if( dist_sq == 0 || dist_sq < SEG::Square( aClearance ) ) { + if( aLocation ) + *aLocation = nearest; + if( aActual ) *aActual = sqrt( dist_sq ); @@ -1571,14 +1581,20 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius, int } -SEG::ecoord SHAPE_POLY_SET::SquaredDistanceToPolygon( VECTOR2I aPoint, int aPolygonIndex ) const +SEG::ecoord SHAPE_POLY_SET::SquaredDistanceToPolygon( VECTOR2I aPoint, int aPolygonIndex, + VECTOR2I* aNearest ) const { // We calculate the min dist between the segment and each outline segment. However, if the // segment to test is inside the outline, and does not cross any edge, it can be seen outside // the polygon. Therefore test if a segment end is inside (testing only one end is enough). // Use an accuracy of "1" to say that we don't care if it's exactly on the edge or not. if( containsSingle( aPoint, aPolygonIndex, 1 ) ) + { + if( aNearest ) + *aNearest = aPoint; + return 0; + } CONST_SEGMENT_ITERATOR iterator = CIterateSegmentsWithHoles( aPolygonIndex ); @@ -1589,21 +1605,32 @@ SEG::ecoord SHAPE_POLY_SET::SquaredDistanceToPolygon( VECTOR2I aPoint, int aPoly SEG::ecoord currentDistance = (*iterator).SquaredDistance( aPoint ); if( currentDistance < minDistance ) + { + if( aNearest ) + *aNearest = (*iterator).NearestPoint( aPoint ); + minDistance = currentDistance; + } } return minDistance; } -SEG::ecoord SHAPE_POLY_SET::SquaredDistanceToPolygon( const SEG& aSegment, int aPolygonIndex ) const +SEG::ecoord SHAPE_POLY_SET::SquaredDistanceToPolygon( const SEG& aSegment, int aPolygonIndex, + VECTOR2I* aNearest ) const { // We calculate the min dist between the segment and each outline segment. However, if the // segment to test is inside the outline, and does not cross any edge, it can be seen outside // the polygon. Therefore test if a segment end is inside (testing only one end is enough). // Use an accuracy of "1" to say that we don't care if it's exactly on the edge or not. if( containsSingle( aSegment.A, aPolygonIndex, 1 ) ) + { + if( aNearest ) + *aNearest = ( aSegment.A + aSegment.B ) / 2; + return 0; + } CONST_SEGMENT_ITERATOR iterator = CIterateSegmentsWithHoles( aPolygonIndex ); SEG::ecoord minDistance = (*iterator).SquaredDistance( aSegment ); @@ -1613,7 +1640,12 @@ SEG::ecoord SHAPE_POLY_SET::SquaredDistanceToPolygon( const SEG& aSegment, int a SEG::ecoord currentDistance = (*iterator).SquaredDistance( aSegment ); if( currentDistance < minDistance ) + { + if( aNearest ) + *aNearest = (*iterator).NearestPoint( aSegment ); + minDistance = currentDistance; + } } // Return the maximum of minDistance and zero @@ -1621,39 +1653,53 @@ SEG::ecoord SHAPE_POLY_SET::SquaredDistanceToPolygon( const SEG& aSegment, int a } -SEG::ecoord SHAPE_POLY_SET::SquaredDistance( VECTOR2I aPoint ) const +SEG::ecoord SHAPE_POLY_SET::SquaredDistance( VECTOR2I aPoint, VECTOR2I* aNearest ) const { - SEG::ecoord currentDistance; - SEG::ecoord minDistance = SquaredDistanceToPolygon( aPoint, 0 ); + SEG::ecoord currentDistance_sq; + SEG::ecoord minDistance_sq = VECTOR2I::ECOORD_MAX; + VECTOR2I nearest; // Iterate through all the polygons and get the minimum distance. - for( unsigned int polygonIdx = 1; polygonIdx < m_polys.size(); polygonIdx++ ) + for( unsigned int polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ ) { - currentDistance = SquaredDistanceToPolygon( aPoint, polygonIdx ); + currentDistance_sq = SquaredDistanceToPolygon( aPoint, polygonIdx, + aNearest ? &nearest : nullptr ); - if( currentDistance < minDistance ) - minDistance = currentDistance; + if( currentDistance_sq < minDistance_sq ) + { + if( aNearest ) + *aNearest = nearest; + + minDistance_sq = currentDistance_sq; + } } - return minDistance; + return minDistance_sq; } -SEG::ecoord SHAPE_POLY_SET::SquaredDistance( const SEG& aSegment ) const +SEG::ecoord SHAPE_POLY_SET::SquaredDistance( const SEG& aSegment, VECTOR2I* aNearest ) const { - SEG::ecoord currentDistance; - SEG::ecoord minDistance = SquaredDistanceToPolygon( aSegment, 0 ); + SEG::ecoord currentDistance_sq; + SEG::ecoord minDistance_sq = VECTOR2I::ECOORD_MAX; + VECTOR2I nearest; // Iterate through all the polygons and get the minimum distance. - for( unsigned int polygonIdx = 1; polygonIdx < m_polys.size(); polygonIdx++ ) + for( unsigned int polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ ) { - currentDistance = SquaredDistanceToPolygon( aSegment, polygonIdx ); + currentDistance_sq = SquaredDistanceToPolygon( aSegment, polygonIdx, + aNearest ? &nearest : nullptr ); - if( currentDistance < minDistance ) - minDistance = currentDistance; + if( currentDistance_sq < minDistance_sq ) + { + if( aNearest ) + *aNearest = nearest; + + minDistance_sq = currentDistance_sq; + } } - return minDistance; + return minDistance_sq; } diff --git a/libs/kimath/src/geometry/shape_rect.cpp b/libs/kimath/src/geometry/shape_rect.cpp index 1f19ba665a..e4d07ccb8d 100644 --- a/libs/kimath/src/geometry/shape_rect.cpp +++ b/libs/kimath/src/geometry/shape_rect.cpp @@ -26,10 +26,25 @@ #include -bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual ) const +bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual, + VECTOR2I* aLocation ) const { - if( BBox( 0 ).Contains( aSeg.A ) || BBox( 0 ).Contains( aSeg.B ) ) + if( BBox( 0 ).Contains( aSeg.A ) ) { + if( aLocation ) + *aLocation = aSeg.A; + + if( aActual ) + *aActual = 0; + + return true; + } + + if( BBox( 0 ).Contains( aSeg.B ) ) + { + if( aLocation ) + *aLocation = aSeg.B; + if( aActual ) *aActual = 0; @@ -42,19 +57,33 @@ bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual ) const VECTOR2I( m_p0.x + m_w, m_p0.y ), VECTOR2I( m_p0.x, m_p0.y ) }; - SEG s( corners[0], corners[1] ); - SEG::ecoord dist_squared = s.SquaredDistance( aSeg ); + SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX; + VECTOR2I nearest; - for( int i = 1; i < 4; i++ ) + for( int i = 0; i < 4; i++ ) { - s = SEG( corners[i], corners[ i + 1] ); - dist_squared = std::min( dist_squared, s.SquaredDistance( aSeg ) ); + SEG side = SEG( corners[i], corners[ i + 1] ); + VECTOR2I pnA = side.NearestPoint( aSeg ); + VECTOR2I pnB = aSeg.NearestPoint( side ); + SEG::ecoord dist_sq = ( pnA - pnB ).SquaredEuclideanNorm(); + + if( dist_sq < closest_dist_sq ) + { + nearest = pnA; + closest_dist_sq = dist_sq; + + if( closest_dist_sq == 0 || !aActual ) + break; + } } - if( dist_squared < (ecoord) aClearance * aClearance ) + if( closest_dist_sq < SEG::Square( aClearance ) ) { + if( aLocation ) + *aLocation = nearest; + if( aActual ) - *aActual = sqrt( dist_squared ); + *aActual = sqrt( closest_dist_sq ); return true; } diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 36b1f1d790..b2cf4e5c9d 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -237,7 +237,6 @@ set( PCBNEW_MICROWAVE_SRCS set( PCBNEW_DRC_SRCS drc/drc_test_provider.cpp drc/drc_test_provider_annulus.cpp - drc/drc_test_provider_clearance_base.cpp drc/drc_test_provider_disallow.cpp drc/drc_test_provider_connectivity.cpp drc/drc_test_provider_copper_clearance.cpp diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 049aa7b686..60c1daf723 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -956,25 +956,6 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con } -bool D_PAD::Collide( const D_PAD* aPad, int aMinClearance, int* aActual ) -{ - int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - ShapePos() ) ); - - // Quick test: Clearance is OK if the bounding circles are further away than aMinClearance - if( center2center - GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance ) - return false; - - - return GetEffectiveShape()->Collide( aPad->GetEffectiveShape().get(), aMinClearance, aActual ); -} - - -bool D_PAD::Collide( const SHAPE_SEGMENT* aSeg, int aMinClearance, int* aActual ) -{ - return aSeg->Collide( GetEffectiveShape().get(), aMinClearance, aActual ); -} - - int D_PAD::Compare( const D_PAD* padref, const D_PAD* padcmp ) { int diff; diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index af137638f9..e7a0b9044c 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -536,9 +536,6 @@ public: bool HitTest( const wxPoint& aPosition, int aAccuracy = 0 ) const override; bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override; - bool Collide( const SHAPE_SEGMENT* aSeg, int aMinClearance, int* aActual = nullptr ); - bool Collide( const D_PAD* aPad, int aMinClearance, int* aActual = nullptr ); - wxString GetClass() const override { return wxT( "PAD" ); diff --git a/pcbnew/drc/drc_item.cpp b/pcbnew/drc/drc_item.cpp index f6678c517c..ee2e9a8d8b 100644 --- a/pcbnew/drc/drc_item.cpp +++ b/pcbnew/drc/drc_item.cpp @@ -167,7 +167,7 @@ DRC_ITEM DRC_ITEM::unresolvedVariable( DRCE_UNRESOLVED_VARIABLE, wxT( "unresolved_variable" ) ); DRC_ITEM DRC_ITEM::silkOverPad( DRCE_SILK_OVER_PAD, - _( "Silkscreen overlapping component pad(s)" ), + _( "Silkscreen overlapping pad" ), wxT( "silk_over_pad" ) ); DRC_ITEM DRC_ITEM::silkClearance( DRCE_SILK_CLEARANCE, diff --git a/pcbnew/drc/drc_test_provider_clearance_base.cpp b/pcbnew/drc/drc_test_provider_clearance_base.cpp deleted file mode 100644 index 68071fb1f6..0000000000 --- a/pcbnew/drc/drc_test_provider_clearance_base.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - - -#include -#include -#include -#include - -const int UI_EPSILON = Mils2iu( 5 ); - - -wxPoint DRC_TEST_PROVIDER_CLEARANCE_BASE::getLocation( PCB_LAYER_ID aLayer, TRACK* aTrack, - ZONE_CONTAINER* aZone ) -{ - SHAPE_POLY_SET* zonePoly = nullptr; - - if( aZone->IsFilled() && aZone->HasFilledPolysForLayer( aLayer ) ) - zonePoly = const_cast( &aZone->GetFilledPolysList( aLayer ) ); - - if( !zonePoly || zonePoly->IsEmpty() ) - zonePoly = aZone->Outline(); - - SEG trackSeg( aTrack->GetStart(), aTrack->GetEnd() ); - SEG::ecoord closestDist_sq = VECTOR2I::ECOORD_MAX; - SEG closestSeg; - - for( auto it = zonePoly->CIterateSegments( 0, -1, true ); it; it++ ) - { - SEG::ecoord dist_sq = trackSeg.SquaredDistance( *it ); - - if( dist_sq < closestDist_sq ) - { - closestDist_sq = dist_sq; - closestSeg = *it; - } - } - - VECTOR2I pt1 = closestSeg.A; - VECTOR2I pt2 = closestSeg.B; - - // Do a binary search for a "good enough" marker location - while( GetLineLength( (wxPoint) pt1, (wxPoint) pt2 ) > UI_EPSILON ) - { - if( trackSeg.SquaredDistance( pt1 ) < trackSeg.SquaredDistance( pt2 ) ) - pt2 = ( pt1 + pt2 ) / 2; - else - pt1 = ( pt1 + pt2 ) / 2; - } - - // Once we're within UI_EPSILON pt1 and pt2 are "equivalent" - return (wxPoint) pt1; -} - -wxPoint DRC_TEST_PROVIDER_CLEARANCE_BASE::getLocation( TRACK* aTrack, const SEG& aConflictSeg ) -{ - wxPoint pt1 = aTrack->GetPosition(); - wxPoint pt2 = aTrack->GetEnd(); - - // Do a binary search along the track for a "good enough" marker location - while( GetLineLength( pt1, pt2 ) > UI_EPSILON ) - { - if( aConflictSeg.SquaredDistance( pt1 ) < aConflictSeg.SquaredDistance( pt2 ) ) - pt2 = ( pt1 + pt2 ) / 2; - else - pt1 = ( pt1 + pt2 ) / 2; - } - - // Once we're within UI_EPSILON pt1 and pt2 are "equivalent" - return pt1; -} diff --git a/pcbnew/drc/drc_test_provider_clearance_base.h b/pcbnew/drc/drc_test_provider_clearance_base.h index 047221db05..d4cd3e7ca5 100644 --- a/pcbnew/drc/drc_test_provider_clearance_base.h +++ b/pcbnew/drc/drc_test_provider_clearance_base.h @@ -47,10 +47,6 @@ public: { } -protected: - wxPoint getLocation( TRACK* aTrack, const SEG& aConflictSeg ); - wxPoint getLocation( PCB_LAYER_ID aLayer, TRACK* aTrack, ZONE_CONTAINER* aZone ); - protected: BOARD* m_board; int m_largestClearance; diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index d9e2d4018a..324470a46e 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -212,11 +212,11 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem ) if( !track->IsOnLayer( aItem->GetLayer() ) ) continue; - auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, - aItem, track, layer ); - int minClearance = constraint.GetValue().Min(); - int actual = INT_MAX; - wxPoint pos; + auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, + aItem, track, layer ); + int minClearance = constraint.GetValue().Min(); + int actual = INT_MAX; + VECTOR2I pos; accountCheck( constraint ); @@ -226,11 +226,9 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem ) if( !bboxShape.Collide( &trackSeg, 0 ) ) continue; - if( !itemShape->Collide( &trackSeg, minClearance, &actual ) ) + if( !itemShape->Collide( &trackSeg, minClearance, &actual, &pos ) ) continue; - pos = (wxPoint) itemShape->Centre(); - if( actual < INT_MAX ) { std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); @@ -244,7 +242,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem ) drcItem->SetItems( track, aItem ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, pos ); + reportViolation( drcItem, (wxPoint) pos ); } } @@ -258,14 +256,14 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem ) if( aItem->Type() == PCB_MODULE_EDGE_T && pad->GetParent() == aItem->GetParent() ) continue; - auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, - aItem, pad, layer ); - int minClearance = constraint.GetValue().Min(); - int actual; + auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, + aItem, pad, layer ); + int minClearance = constraint.GetValue().Min(); + int actual; + VECTOR2I pos; accountCheck( constraint ); - // Fast test to detect a pad candidate inside the text bounding box // Finer test (time consuming) is made only for pads near the text. int bb_radius = pad->GetBoundingRadius() + minClearance; @@ -273,7 +271,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem ) if( !bboxShape.Collide( SEG( pad->GetPosition(), pad->GetPosition() ), bb_radius ) ) continue; - if( !pad->GetEffectiveShape()->Collide( itemShape.get(), minClearance, &actual ) ) + if( !itemShape->Collide( pad->GetEffectiveShape().get(), minClearance, &actual, &pos ) ) continue; std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); @@ -287,7 +285,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem ) drcItem->SetItems( pad, aItem ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, pad->GetPosition()); + reportViolation( drcItem, (wxPoint) pos ); } } @@ -315,6 +313,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances() } } + void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_ID aLayer, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt ) @@ -358,16 +357,17 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I if( pad->GetNetCode() && aRefSeg->GetNetCode() == pad->GetNetCode() ) continue; - auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, - aRefSeg, pad, aLayer ); - int minClearance = constraint.GetValue().Min(); - int actual; + auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, + aRefSeg, pad, aLayer ); + int minClearance = constraint.GetValue().Min(); + int actual; + VECTOR2I pos; accountCheck( constraint ); const std::shared_ptr& padShape = pad->GetEffectiveShape(); - if( padShape->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual ) ) + if( padShape->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual, &pos ) ) { std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); @@ -380,7 +380,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I drcItem->SetItems( aRefSeg, pad ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, pad->GetPosition()); + reportViolation( drcItem, (wxPoint) pos ); } } } @@ -421,6 +421,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I int minClearance = constraint.GetValue().Min(); int actual; SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() ); + VECTOR2I pos; accountCheck( constraint ); @@ -440,11 +441,10 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I drcItem->SetItems( aRefSeg, track ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, (wxPoint) intersection.get()); + reportViolation( drcItem, (wxPoint) intersection.get() ); } - else if( refSeg.Collide( &trackSeg, minClearance - bds.GetDRCEpsilon(), &actual ) ) + else if( refSeg.Collide( &trackSeg, minClearance - bds.GetDRCEpsilon(), &actual, &pos ) ) { - wxPoint pos = getLocation( aRefSeg, trackSeg.GetSeg() ); std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), @@ -456,7 +456,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I drcItem->SetItems( aRefSeg, track ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, pos ); + reportViolation( drcItem, (wxPoint) pos ); if( !m_drcEngine->GetReportAllTrackErrors() ) break; @@ -491,14 +491,17 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, aRefSeg, zone, aLayer ); - int minClearance = constraint.GetValue().Min(); - int halfWidth = refSegWidth / 2; - int allowedDist = minClearance + halfWidth - bds.GetDRCEpsilon(); - int actual; + int minClearance = constraint.GetValue().Min(); + int halfWidth = refSegWidth / 2; + int allowedDist = minClearance + halfWidth - bds.GetDRCEpsilon(); + + const SHAPE_POLY_SET& zonePoly = zone->GetFilledPolysList( aLayer ); + int actual; + VECTOR2I location; accountCheck( constraint ); - if( zone->GetFilledPolysList( aLayer ).Collide( testSeg, allowedDist, &actual ) ) + if( zonePoly.Collide( testSeg, allowedDist, &actual, &location ) ) { actual = std::max( 0, actual - halfWidth ); std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); @@ -512,7 +515,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I drcItem->SetItems( aRefSeg, zone ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, getLocation( aLayer, aRefSeg, zone )); + reportViolation( drcItem, (wxPoint) location ); } } } @@ -634,17 +637,19 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doPadToPadsDrc( int aRefPadIdx, if( exceedClearance ) break; - auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, - refPad, pad, layer ); - int minClearance = constraint.GetValue().Min(); - int clearanceAllowed = minClearance - bds.GetDRCEpsilon(); - int actual; + auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_CLEARANCE, + refPad, pad, layer ); + int minClearance = constraint.GetValue().Min(); + int clearanceAllowed = minClearance - bds.GetDRCEpsilon(); + int actual; + VECTOR2I pos; accountCheck( constraint ); std::shared_ptr refPadShape = refPad->GetEffectiveShape(); + std::shared_ptr padShape = pad->GetEffectiveShape(); - if( refPadShape->Collide( pad->GetEffectiveShape().get(), clearanceAllowed, &actual ) ) + if( refPadShape->Collide( padShape.get(), clearanceAllowed, &actual, &pos ) ) { std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_CLEARANCE ); @@ -657,7 +662,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doPadToPadsDrc( int aRefPadIdx, drcItem->SetItems( refPad, pad ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, refPad->GetPosition()); + reportViolation( drcItem, (wxPoint) pos ); break; } } diff --git a/pcbnew/drc/drc_test_provider_edge_clearance.cpp b/pcbnew/drc/drc_test_provider_edge_clearance.cpp index 15bc40c96d..3a2f7a482a 100644 --- a/pcbnew/drc/drc_test_provider_edge_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_edge_clearance.cpp @@ -140,12 +140,14 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run() auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_EDGE_CLEARANCE, outlineItem, boardItem ); - int minClearance = constraint.GetValue().Min(); - int actual; + + int minClearance = constraint.GetValue().Min(); + int actual; + VECTOR2I pos; accountCheck( constraint ); - if( refShape->Collide( shape.get(), minClearance, &actual ) ) + if( refShape->Collide( shape.get(), minClearance, &actual, &pos ) ) { std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_COPPER_EDGE_CLEARANCE ); @@ -158,7 +160,7 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run() drcItem->SetItems( outlineItem, boardItem ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, (wxPoint) refShape->Centre()); + reportViolation( drcItem, (wxPoint) pos ); } } } diff --git a/pcbnew/drc/drc_test_provider_hole_clearance.cpp b/pcbnew/drc/drc_test_provider_hole_clearance.cpp index 5794148709..ce38ce1885 100644 --- a/pcbnew/drc/drc_test_provider_hole_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_hole_clearance.cpp @@ -295,7 +295,7 @@ bool DRC_TEST_PROVIDER_HOLE_CLEARANCE::doPadToPadHoleDrc( int aRefPadIdx, drcItem->SetItems( pad, refPad ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, pad->GetPosition()); + reportViolation( drcItem, pad->GetPosition() ); return false; } } @@ -328,7 +328,7 @@ bool DRC_TEST_PROVIDER_HOLE_CLEARANCE::doPadToPadHoleDrc( int aRefPadIdx, drcItem->SetItems( refPad, pad ); drcItem->SetViolatingRule( constraint.GetParentRule() ); - reportViolation( drcItem, pad->GetPosition()); + reportViolation( drcItem, pad->GetPosition() ); return false; } } diff --git a/pcbnew/drc/drc_test_provider_silk_to_pad.cpp b/pcbnew/drc/drc_test_provider_silk_to_pad.cpp index c07f207a36..2ca2836812 100644 --- a/pcbnew/drc/drc_test_provider_silk_to_pad.cpp +++ b/pcbnew/drc/drc_test_provider_silk_to_pad.cpp @@ -119,45 +119,41 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_PAD::Run() return true; }; - auto checkClearance = [&]( const DRC_RTREE::LAYER_PAIR& aLayers, - DRC_RTREE::ITEM_WITH_SHAPE* aRefItem, - DRC_RTREE::ITEM_WITH_SHAPE* aTestItem ) -> bool { - auto constraint = m_drcEngine->EvalRulesForItems( - DRC_CONSTRAINT_TYPE_SILK_TO_PAD, aRefItem->parent, aTestItem->parent ); + auto checkClearance = + [&]( const DRC_RTREE::LAYER_PAIR& aLayers, + DRC_RTREE::ITEM_WITH_SHAPE* aRefItem, + DRC_RTREE::ITEM_WITH_SHAPE* aTestItem ) -> bool + { + auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_SILK_TO_PAD, + aRefItem->parent, + aTestItem->parent ); - int minClearance = constraint.GetValue().Min(); + int minClearance = constraint.GetValue().Min(); + int actual; + VECTOR2I pos; - accountCheck( constraint ); + accountCheck( constraint ); - int actual; + if( !aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual, &pos ) ) + return true; - if( ! aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual ) ) - return true; + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_SILK_OVER_PAD ); + wxString msg; - std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_SILK_OVER_PAD ); - wxString msg; + drcItem->SetItems( aRefItem->parent, aTestItem->parent ); + drcItem->SetViolatingRule( constraint.GetParentRule() ); - msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), - constraint.GetParentRule()->m_Name, - MessageTextFromValue( userUnits(), minClearance, true ), - MessageTextFromValue( userUnits(), actual, true ) ); - - drcItem->SetErrorMessage( msg ); - drcItem->SetItems( aRefItem->parent, aTestItem->parent ); - drcItem->SetViolatingRule( constraint.GetParentRule() ); - - reportViolation( drcItem, aRefItem->parent->GetPosition() ); + reportViolation( drcItem, (wxPoint) pos ); - return !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_OVER_PAD ); - }; + return !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_OVER_PAD ); + }; - int numPads = forEachGeometryItem( - { PCB_PAD_T }, LSET::AllTechMask() | LSET::AllCuMask(), addPadToTree ); + int numPads = forEachGeometryItem( { PCB_PAD_T }, LSET::AllTechMask() | LSET::AllCuMask(), + addPadToTree ); - int numSilk = - forEachGeometryItem( { PCB_LINE_T, PCB_MODULE_EDGE_T, PCB_TEXT_T, PCB_MODULE_TEXT_T }, - LSET( 2, F_SilkS, B_SilkS ), addSilkToTree ); + int numSilk = forEachGeometryItem( { PCB_LINE_T, PCB_MODULE_EDGE_T, PCB_TEXT_T, PCB_MODULE_TEXT_T }, + LSET( 2, F_SilkS, B_SilkS ), addSilkToTree ); reportAux( _("Testing %d pads against %d silkscreen features."), numPads, numSilk ); diff --git a/pcbnew/drc/drc_test_provider_silk_to_silk.cpp b/pcbnew/drc/drc_test_provider_silk_to_silk.cpp index 287523188a..8b86038176 100644 --- a/pcbnew/drc/drc_test_provider_silk_to_silk.cpp +++ b/pcbnew/drc/drc_test_provider_silk_to_silk.cpp @@ -24,12 +24,9 @@ #include #include #include -#include -#include #include #include -#include #include #include @@ -47,9 +44,7 @@ */ -namespace test { - -class DRC_TEST_PROVIDER_SILK_TO_SILK : public ::DRC_TEST_PROVIDER +class DRC_TEST_PROVIDER_SILK_TO_SILK : public DRC_TEST_PROVIDER { public: DRC_TEST_PROVIDER_SILK_TO_SILK () @@ -85,10 +80,8 @@ private: int m_largestClearance; }; -}; - -bool test::DRC_TEST_PROVIDER_SILK_TO_SILK::Run() +bool DRC_TEST_PROVIDER_SILK_TO_SILK::Run() { m_board = m_drcEngine->GetBoard(); @@ -113,77 +106,76 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_SILK::Run() return true; }; - auto checkClearance = [&]( const DRC_RTREE::LAYER_PAIR& aLayers, - DRC_RTREE::ITEM_WITH_SHAPE* aRefItem, - DRC_RTREE::ITEM_WITH_SHAPE* aTestItem ) -> bool { - auto constraint = m_drcEngine->EvalRulesForItems( - DRC_CONSTRAINT_TYPE_SILK_TO_SILK, aRefItem->parent, aTestItem->parent ); + auto checkClearance = + [&]( const DRC_RTREE::LAYER_PAIR& aLayers, + DRC_RTREE::ITEM_WITH_SHAPE* aRefItem, + DRC_RTREE::ITEM_WITH_SHAPE* aTestItem ) -> bool + { + auto constraint = m_drcEngine->EvalRulesForItems( DRC_CONSTRAINT_TYPE_SILK_TO_SILK, + aRefItem->parent, + aTestItem->parent ); + int minClearance = constraint.GetValue().Min(); + int actual; + VECTOR2I pos; - int minClearance = constraint.GetValue().Min(); + accountCheck( constraint ); - accountCheck( constraint ); + // only check for silkscreen collisions belonging to different modules or + // overlapping texts - int actual; + KICAD_T typeRef = aRefItem->parent->Type(); + KICAD_T typeTest = aTestItem->parent->Type(); - // only check for silkscreen collisions belonging to different modules or overlapping texts - + MODULE *parentModRef = nullptr; + MODULE *parentModTest = nullptr; - KICAD_T typeRef = aRefItem->parent->Type(); - KICAD_T typeTest = aTestItem->parent->Type(); - - MODULE *parentModRef = nullptr; - MODULE *parentModTest = nullptr; + if( typeRef == PCB_MODULE_EDGE_T || typeRef == PCB_MODULE_TEXT_T ) + { + parentModRef = static_cast ( aRefItem->parent->GetParent() ); + } - if( typeRef == PCB_MODULE_EDGE_T || typeRef == PCB_MODULE_TEXT_T ) - { - parentModRef = static_cast ( aRefItem->parent->GetParent() ); - } + if( typeTest == PCB_MODULE_EDGE_T || typeTest == PCB_MODULE_TEXT_T ) + { + parentModTest = static_cast ( aTestItem->parent->GetParent() ); + } - if( typeTest == PCB_MODULE_EDGE_T || typeTest == PCB_MODULE_TEXT_T ) - { - parentModTest = static_cast ( aTestItem->parent->GetParent() ); - } + // silkscreen drawings within the same module (or globally on the board) + // don't report clearance errors. Everything else does. + if( parentModRef && parentModRef == parentModTest ) + { + if( typeRef == PCB_MODULE_EDGE_T && typeTest == PCB_MODULE_EDGE_T ) + return true; + } - // silkscreen drawings within the same module (or globally on the board) - // don't report clearance errors. Everything else does. + if( !parentModRef && !parentModTest ) + { + if( typeRef == PCB_LINE_T && typeTest == PCB_LINE_T ) + return true; + } - if( parentModRef && parentModRef == parentModTest ) - { - if( typeRef == PCB_MODULE_EDGE_T && typeTest == PCB_MODULE_EDGE_T ) - return true; - } + if( ! aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual, &pos ) ) + return true; - if( !parentModRef && !parentModTest ) - { - if( typeRef == PCB_LINE_T && typeTest == PCB_LINE_T ) - return true; - } + std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_SILK_CLEARANCE ); + wxString msg; - if( ! aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual ) ) - return true; + msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), + constraint.GetParentRule()->m_Name, + MessageTextFromValue( userUnits(), minClearance, true ), + MessageTextFromValue( userUnits(), actual, true ) ); - std::shared_ptr drcItem = DRC_ITEM::Create( DRCE_SILK_CLEARANCE ); - wxString msg; + drcItem->SetErrorMessage( msg ); + drcItem->SetItems( aRefItem->parent, aTestItem->parent ); + drcItem->SetViolatingRule( constraint.GetParentRule() ); - msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ), - constraint.GetParentRule()->m_Name, - MessageTextFromValue( userUnits(), minClearance, true ), - MessageTextFromValue( userUnits(), actual, true ) ); + reportViolation( drcItem, (wxPoint) pos ); - drcItem->SetErrorMessage( msg ); - drcItem->SetItems( aRefItem->parent, aTestItem->parent ); - drcItem->SetViolatingRule( constraint.GetParentRule() ); + return !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ); + }; - reportViolation( drcItem, aRefItem->parent->GetPosition() ); - - - return !m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ); - }; - - int numSilk = - forEachGeometryItem( { PCB_LINE_T, PCB_MODULE_EDGE_T, PCB_TEXT_T, PCB_MODULE_TEXT_T }, - LSET( 2, F_SilkS, B_SilkS ), addToTree ); + int numSilk = forEachGeometryItem( { PCB_LINE_T, PCB_MODULE_EDGE_T, PCB_TEXT_T, PCB_MODULE_TEXT_T }, + LSET( 2, F_SilkS, B_SilkS ), addToTree ); reportAux( _("Testing %d silkscreen features."), numSilk ); @@ -201,7 +193,7 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_SILK::Run() } -std::set test::DRC_TEST_PROVIDER_SILK_TO_SILK::GetConstraintTypes() const +std::set DRC_TEST_PROVIDER_SILK_TO_SILK::GetConstraintTypes() const { return { DRC_CONSTRAINT_TYPE_SILK_TO_SILK }; } @@ -209,5 +201,5 @@ std::set test::DRC_TEST_PROVIDER_SILK_TO_SILK::GetConstra namespace detail { - static DRC_REGISTER_TEST_PROVIDER dummy; + static DRC_REGISTER_TEST_PROVIDER dummy; } \ No newline at end of file diff --git a/qa/drc_proto/CMakeLists.txt b/qa/drc_proto/CMakeLists.txt index b11bbc67ff..4748ba7dde 100644 --- a/qa/drc_proto/CMakeLists.txt +++ b/qa/drc_proto/CMakeLists.txt @@ -39,7 +39,6 @@ add_executable( drc_proto ../../pcbnew/drc/drc_rule_condition.cpp ../../pcbnew/drc/drc_rule_parser.cpp ../../pcbnew/drc/drc_test_provider.cpp - ../../pcbnew/drc/drc_test_provider_clearance_base.cpp ../../pcbnew/drc/drc_test_provider_copper_clearance.cpp ../../pcbnew/drc/drc_test_provider_hole_clearance.cpp ../../pcbnew/drc/drc_test_provider_edge_clearance.cpp