diff --git a/libs/kimath/include/geometry/seg.h b/libs/kimath/include/geometry/seg.h index 9103db760e..75287c4152 100644 --- a/libs/kimath/include/geometry/seg.h +++ b/libs/kimath/include/geometry/seg.h @@ -193,7 +193,7 @@ public: return Intersect( aSeg, false, true ); } - bool Collide( const SEG& aSeg, int aClearance ) const; + bool Collide( const SEG& aSeg, int aClearance, int* aActual = nullptr ) const; ecoord SquaredDistance( const SEG& aSeg ) const; @@ -341,8 +341,6 @@ public: bool Contains( const VECTOR2I& aP ) const; - bool PointCloserThan( const VECTOR2I& aP, int aDist ) const; - void Reverse() { std::swap( A, B ); diff --git a/libs/kimath/include/geometry/shape.h b/libs/kimath/include/geometry/shape.h index 4e55f03d8f..7bb582f528 100644 --- a/libs/kimath/include/geometry/shape.h +++ b/libs/kimath/include/geometry/shape.h @@ -118,9 +118,11 @@ 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. * @return true, if there is a collision. */ - virtual bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const + virtual bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const { return Collide( SEG( aP, aP ), aClearance ); } @@ -133,19 +135,23 @@ 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. * @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 ) const; + virtual bool Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const; + virtual bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = 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. * @return true, if there is a collision. */ - virtual bool Collide( const SEG& aSeg, int aClearance = 0 ) const = 0; + virtual bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const = 0; /** * Function BBox() @@ -169,7 +175,14 @@ public: return BBox( 0 ).Centre(); // if nothing better is available.... } - virtual void Move ( const VECTOR2I& aVector ) = 0; + /** + * Function Rotate + * @param aCenter is the rotation center + * @param aAngle rotation angle in radians + */ + virtual void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) = 0; + + virtual void Move( const VECTOR2I& aVector ) = 0; virtual bool IsSolid() const = 0; @@ -182,7 +195,7 @@ protected: SHAPE_TYPE m_type; }; -bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ); +bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, + VECTOR2I* aMTV ); #endif // __SHAPE_H diff --git a/libs/kimath/include/geometry/shape_arc.h b/libs/kimath/include/geometry/shape_arc.h index 0da8db97fb..570d5fd28b 100644 --- a/libs/kimath/include/geometry/shape_arc.h +++ b/libs/kimath/include/geometry/shape_arc.h @@ -76,8 +76,8 @@ public: const BOX2I BBox( int aClearance = 0 ) const override; - bool Collide( const SEG& aSeg, int aClearance = 0 ) const override; - bool Collide( const VECTOR2I& aP, 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; void SetWidth( int aWidth ) { @@ -102,7 +102,7 @@ public: * @param aCenter is the rotation center * @param aAngle rotation angle in radians */ - void Rotate( double aAngle, const VECTOR2I& aCenter ); + void Rotate( double aAngle, const VECTOR2I& aCenter ) override; void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aVector = { 0, 0 } ); diff --git a/libs/kimath/include/geometry/shape_circle.h b/libs/kimath/include/geometry/shape_circle.h index 34450fc738..0884f320e1 100644 --- a/libs/kimath/include/geometry/shape_circle.h +++ b/libs/kimath/include/geometry/shape_circle.h @@ -63,11 +63,20 @@ public: return BOX2I( m_center - rc, rc * 2 ); } - bool Collide( const SEG& aSeg, int aClearance = 0 ) const override + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override { - int rc = aClearance + m_radius; + int minDist = aClearance + m_radius; + ecoord dist_sq = aSeg.SquaredDistance( m_center ); - return aSeg.Distance( m_center ) < rc; + if( dist_sq < (ecoord) minDist * minDist ) + { + if( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_sq ) - m_radius ); + + return true; + } + + return false; } void SetRadius( int aRadius ) @@ -95,6 +104,11 @@ public: m_center += aVector; } + void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override + { + // That was easy.... + } + bool IsSolid() const override { return true; diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h index d7175ab741..536735de2f 100644 --- a/libs/kimath/include/geometry/shape_line_chain.h +++ b/libs/kimath/include/geometry/shape_line_chain.h @@ -386,9 +386,11 @@ public: * Checks if point aP lies closer to us than aClearance. * @param aP the point to check for collisions with * @param aClearance minimum distance that does not qualify as a collision. + * @param aActual an optional pointer to an int to store the actual distance in the event + * of a collision. * @return true, when a collision has been found */ - bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override; + bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override; /** * Function Collide() @@ -396,9 +398,11 @@ public: * Checks if segment aSeg lies closer to us than aClearance. * @param aSeg the segment to check for collisions with * @param aClearance minimum distance that does not qualify as a collision. + * @param aActual an optional pointer to an int to store the actual distance in the event + * of a collision. * @return true, when a collision has been found */ - bool Collide( const SEG& aSeg, int aClearance = 0 ) const override; + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; /** * Function Distance() @@ -408,6 +412,7 @@ public: * @return minimum distance. */ int Distance( const VECTOR2I& aP, bool aOutlineOnly = false ) const; + SEG::ecoord SquaredDistance( const VECTOR2I& aP, bool aOutlineOnly = false ) const; /** * Function Reverse() @@ -747,7 +752,7 @@ public: * @param aCenter is the rotation center * @param aAngle rotation angle in radians */ - void Rotate( double aAngle, const VECTOR2I& aCenter = VECTOR2I( 0, 0 ) ); + void Rotate( double aAngle, const VECTOR2I& aCenter = VECTOR2I( 0, 0 ) ) override; bool IsSolid() const override { diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index a0c081fc5e..1dd14e3860 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -975,7 +975,7 @@ class SHAPE_POLY_SET : public SHAPE * @param aCenter is the rotation center * @param aAngle rotation angle in radians */ - void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ); + void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override; /// @copydoc SHAPE::IsSolid() bool IsSolid() const override @@ -1003,9 +1003,11 @@ class SHAPE_POLY_SET : public SHAPE * will be tested. * @param aClearance is the security distance; if the point lies closer to the polygon * than aClearance distance, then there is a collision. + * @param aActual an optional pointer to an int to store the actual distance in the event + * 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 ) const override; + bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override; /** * Function Collide @@ -1016,10 +1018,12 @@ class SHAPE_POLY_SET : public SHAPE * will be tested. * @param aClearance is the security distance; if the segment passes closer to the polygon * than aClearance distance, then there is a collision. + * @param aActual an optional pointer to an int to store the actual distance in the event + * of a collision. * @return bool - true if the segment aSeg collides with the polygon; * false in any other case. */ - bool Collide( const SEG& aSeg, int aClearance = 0 ) const override; + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; /** * Function CollideVertex diff --git a/libs/kimath/include/geometry/shape_rect.h b/libs/kimath/include/geometry/shape_rect.h index f1a2fdcda2..6a8b279c46 100644 --- a/libs/kimath/include/geometry/shape_rect.h +++ b/libs/kimath/include/geometry/shape_rect.h @@ -91,9 +91,7 @@ public: } /// @copydoc SHAPE::Collide() - bool Collide( const SEG& aSeg, int aClearance = 0 ) const override; - - bool DoCollide( const SEG& aSeg, int aClearance, int* aActualDist ) const; + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override; /** * Function GetPosition() @@ -140,6 +138,16 @@ public: m_p0 += aVector; } + void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override + { + m_p0 -= aCenter; + m_p0 = m_p0.Rotate( aAngle ); + m_p0 += aCenter; + + if( abs( sin( aAngle ) ) == 1 ) + std::swap( m_h, m_w ); + } + bool IsSolid() const override { return true; diff --git a/libs/kimath/include/geometry/shape_segment.h b/libs/kimath/include/geometry/shape_segment.h index 9ca3cf13f0..584f0291fc 100644 --- a/libs/kimath/include/geometry/shape_segment.h +++ b/libs/kimath/include/geometry/shape_segment.h @@ -54,14 +54,36 @@ public: return BOX2I( m_seg.A, m_seg.B - m_seg.A ).Inflate( aClearance + ( m_width + 1 ) / 2 ); } - bool Collide( const SEG& aSeg, int aClearance = 0 ) const override + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override { - return m_seg.Distance( aSeg ) < ( m_width + 1 ) / 2 + aClearance; + int min_dist = ( m_width + 1 ) / 2 + aClearance; + ecoord dist_sq = m_seg.SquaredDistance( aSeg ); + + if( dist_sq < (ecoord) min_dist * min_dist ) + { + if( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_sq ) - ( m_width + 1 ) / 2 ); + + return true; + } + + return false; } - bool Collide( const VECTOR2I& aP, int aClearance = 0 ) const override + bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override { - return m_seg.Distance( aP ) < ( m_width + 1 ) / 2 + aClearance; + int min_dist = ( m_width + 1 ) / 2 + aClearance; + ecoord dist_sq = m_seg.SquaredDistance( aP ); + + if( dist_sq < (ecoord) min_dist * min_dist ) + { + if( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_sq ) - ( m_width + 1 ) / 2 ); + + return true; + } + + return false; } void SetSeg( const SEG& aSeg ) @@ -89,6 +111,18 @@ public: return true; } + void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override + { + m_seg.A -= aCenter; + m_seg.B -= aCenter; + + m_seg.A = m_seg.A.Rotate( aAngle ); + m_seg.B = m_seg.B.Rotate( aAngle ); + + m_seg.A += aCenter; + m_seg.B += aCenter; + } + void Move( const VECTOR2I& aVector ) override { m_seg.A += aVector; diff --git a/libs/kimath/include/geometry/shape_simple.h b/libs/kimath/include/geometry/shape_simple.h index 2b8d6dcf24..eb1488bedb 100644 --- a/libs/kimath/include/geometry/shape_simple.h +++ b/libs/kimath/include/geometry/shape_simple.h @@ -159,9 +159,14 @@ public: } /// @copydoc SHAPE::Collide() - bool Collide( const SEG& aSeg, int aClearance = 0 ) const override + bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override { - return m_points.Collide( aSeg, aClearance ); + return m_points.Collide( aSeg, aClearance, aActual ); + } + + void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override + { + m_points.Rotate( aAngle, aCenter ); } void Move( const VECTOR2I& aVector ) override diff --git a/libs/kimath/src/geometry/seg.cpp b/libs/kimath/src/geometry/seg.cpp index cb919368fe..9fabbc53ea 100644 --- a/libs/kimath/src/geometry/seg.cpp +++ b/libs/kimath/src/geometry/seg.cpp @@ -34,57 +34,6 @@ int sgn( T aVal ) } -bool SEG::PointCloserThan( const VECTOR2I& aP, int aDist ) const -{ - // See http://geomalgorithms.com/a02-_lines.html for some explanations and ideas. - VECTOR2I d = B - A; - ecoord dist_sq = (ecoord) aDist * aDist; - - SEG::ecoord l_squared = d.Dot( d ); - SEG::ecoord t = d.Dot( aP - A ); - - if( t <= 0 || !l_squared ) - return ( aP - A ).SquaredEuclideanNorm() < dist_sq; - else if( t >= l_squared ) - return ( aP - B ).SquaredEuclideanNorm() < dist_sq; - - // JPC: This code is not trivial and is not commented - // and does not work for d.x or d.y = -1...1 - // I am guessing it is here for calculation time optimization. - // if someone can understand it, please fix it. - // It can be tested with a segment having d.x or d.y value - // is -1 or +1 ("this" is a quasi vertical or horizontal segment) - int dxdy = std::abs( d.x ) - std::abs( d.y ); - - if( ( dxdy >= -1 && dxdy <= 1 ) // quasi 45 deg segment - /*|| std::abs( d.x ) <= 1 // quasi horizontal segment - || std::abs( d.y ) <= 1 // quasi vertical segment */ ) - { - int ca = -sgn( d.y ); - int cb = sgn( d.x ); - int cc = -ca * A.x - cb * A.y; - - ecoord num = (ecoord) ca * aP.x + (ecoord) cb * aP.y + cc; - num *= num; - - if( ca && cb ) - num >>= 1; - - if( num > ( dist_sq + 100 ) ) - return false; - - else if( num < ( dist_sq - 100 ) ) - return true; - } - - VECTOR2I nearest; - nearest.x = A.x + rescale( t, (ecoord) d.x, l_squared ); - nearest.y = A.y + rescale( t, (ecoord) d.y, l_squared ); - - return ( nearest - aP ).SquaredEuclideanNorm() <= dist_sq; -} - - SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const { // fixme: rather inefficient.... @@ -176,22 +125,33 @@ bool SEG::ccw( const VECTOR2I& aA, const VECTOR2I& aB, const VECTOR2I& aC ) cons } -bool SEG::Collide( const SEG& aSeg, int aClearance ) const +bool SEG::Collide( const SEG& aSeg, int aClearance, int* aActual ) const { // check for intersection // fixme: move to a method if( ccw( A, aSeg.A, aSeg.B ) != ccw( B, aSeg.A, aSeg.B ) && ccw( A, B, aSeg.A ) != ccw( A, B, aSeg.B ) ) + { + if( aActual ) + *aActual = 0; + return true; + } -#define CHK( _seg, _pt ) \ - if( (_seg).PointCloserThan( _pt, aClearance ) ) return true; + ecoord dist_sq = VECTOR2I::ECOORD_MAX; - CHK( *this, aSeg.A ); - CHK( *this, aSeg.B ); - CHK( aSeg, A ); - CHK( aSeg, B ); -#undef CHK + dist_sq = std::min( dist_sq, SquaredDistance( aSeg.A ) ); + dist_sq = std::min( dist_sq, SquaredDistance( aSeg.B ) ); + dist_sq = std::min( dist_sq, aSeg.SquaredDistance( A ) ); + dist_sq = std::min( dist_sq, aSeg.SquaredDistance( B ) ); + + if( dist_sq < (ecoord) aClearance * aClearance ) + { + if( aActual ) + *aActual = sqrt( dist_sq ); + + return true; + } return false; } @@ -199,5 +159,5 @@ bool SEG::Collide( const SEG& aSeg, int aClearance ) const bool SEG::Contains( const VECTOR2I& aP ) const { - return PointCloserThan( aP, 1 ); + return SquaredDistance( aP ) < 1; // 1 * 1 to be pedantic } diff --git a/libs/kimath/src/geometry/shape_arc.cpp b/libs/kimath/src/geometry/shape_arc.cpp index 0e02f9e2f8..6a9d7e06a9 100644 --- a/libs/kimath/src/geometry/shape_arc.cpp +++ b/libs/kimath/src/geometry/shape_arc.cpp @@ -72,23 +72,17 @@ SHAPE_ARC::SHAPE_ARC( const SHAPE_ARC& aOther ) } -bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance ) const +bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance, int* aActual ) const { - int minDist = aClearance + m_width / 2; - auto center = GetCenter(); - auto centerDist = aSeg.Distance( center ); - auto p1 = GetP1(); + int minDist = aClearance + m_width / 2; + VECTOR2I center = GetCenter(); + ecoord dist_sq = VECTOR2I::ECOORD_MAX; - if( centerDist < minDist ) - return true; - - auto ab = (aSeg.B - aSeg.A ); - auto ac = ( center - aSeg.A ); - - auto lenAbSq = ab.SquaredEuclideanNorm(); - - auto lambda = (double) ac.Dot( ab ) / (double) lenAbSq; + VECTOR2I ab = ( aSeg.B - aSeg.A ); + VECTOR2I ac = ( center - aSeg.A ); + ecoord lenAbSq = ab.SquaredEuclideanNorm(); + double lambda = (double) ac.Dot( ab ) / (double) lenAbSq; if( lambda >= 0.0 && lambda <= 1.0 ) { @@ -97,29 +91,22 @@ bool SHAPE_ARC::Collide( const SEG& aSeg, int aClearance ) 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); - auto p0pdist = ( m_start - p ).EuclideanNorm(); - - if( p0pdist < minDist ) - return true; - - auto p1pdist = ( p1 - p ).EuclideanNorm(); - - if( p1pdist < minDist ) - return true; + dist_sq = std::min( dist_sq, ( m_start - p ).SquaredEuclideanNorm() ); + dist_sq = std::min( dist_sq, ( m_end - p ).SquaredEuclideanNorm() ); } - auto p0dist = aSeg.Distance( m_start ); + dist_sq = std::min( dist_sq, aSeg.SquaredDistance( m_start ) ); + dist_sq = std::min( dist_sq, aSeg.SquaredDistance( m_end ) ); + + if( dist_sq < (ecoord) minDist * minDist ) + { + if( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_sq ) - m_width / 2 ); - if( p0dist > minDist ) return true; + } - auto p1dist = aSeg.Distance( p1 ); - - if( p1dist > minDist ) - return false; - - - return true; + return false; } @@ -176,7 +163,7 @@ const BOX2I SHAPE_ARC::BBox( int aClearance ) const } -bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance ) const +bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const { int minDist = aClearance + m_width / 2; auto bbox = BBox( minDist ); @@ -184,9 +171,22 @@ bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance ) const if( !bbox.Contains( aP ) ) return false; - auto dist = ( aP - GetCenter() ).EuclideanNorm(); + ecoord min_dist_sq = (ecoord) minDist * minDist; + ecoord r = GetRadius(); + ecoord r_sq = r * r; - return dist <= ( GetRadius() + minDist ) && dist >= ( GetRadius() - minDist ); + 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( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_to_edge_sq ) - m_width / 2 ); + + return true; + } + + return false; } diff --git a/libs/kimath/src/geometry/shape_collisions.cpp b/libs/kimath/src/geometry/shape_collisions.cpp index d22c036d51..e67cbc0420 100644 --- a/libs/kimath/src/geometry/shape_collisions.cpp +++ b/libs/kimath/src/geometry/shape_collisions.cpp @@ -26,7 +26,6 @@ #include // for assert #include #include // for INT_MAX -#include // for abs #include // for SEG #include @@ -36,13 +35,12 @@ #include #include #include -#include // for BOX2I #include typedef VECTOR2I::extended_type ecoord; static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CIRCLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { ecoord min_dist = aClearance + aA.GetRadius() + aB.GetRadius(); ecoord min_dist_sq = min_dist * min_dist; @@ -54,21 +52,25 @@ static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CIRCLE& aB, int if( dist_sq >= min_dist_sq ) return false; - if( aNeedMTV ) - aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error + if( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() - aB.GetRadius() ); + + if( aMTV ) + *aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error return true; } static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, 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 VECTOR2I vts[] = { @@ -79,47 +81,52 @@ static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aC VECTOR2I( p0.x, p0.y ) }; - int nearest_seg_dist = INT_MAX; + ecoord nearest_side_dist_sq = VECTOR2I::ECOORD_MAX; VECTOR2I nearest; bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x ) && c.y >= p0.y && c.y <= ( p0.y + size.y ); + if( inside && !aMTV ) + { + if( aActual ) + *aActual = 0; - if( !aNeedMTV && inside ) return true; + } for( int i = 0; i < 4; i++ ) { - const SEG seg( vts[i], vts[i + 1] ); + const SEG side( vts[i], vts[ i + 1] ); - VECTOR2I pn = seg.NearestPoint( c ); + VECTOR2I pn = side.NearestPoint( c ); + int side_dist_sq = ( pn - c ).SquaredEuclideanNorm(); - int d = ( pn - c ).EuclideanNorm(); - - if( ( d < min_dist ) && !aNeedMTV ) + if(( side_dist_sq < min_dist_sq ) && !aMTV && !aActual ) return true; - if( d < nearest_seg_dist ) + if( side_dist_sq < nearest_side_dist_sq ) { nearest = pn; - nearest_seg_dist = d; + nearest_side_dist_sq = side_dist_sq; } } - if( nearest_seg_dist >= min_dist && !inside ) + if( !inside && nearest_side_dist_sq >= min_dist_sq ) return false; VECTOR2I delta = c - nearest; - if( !aNeedMTV ) - return true; + if( aActual ) + *aActual = std::max( 0, (int) sqrt( nearest_side_dist_sq ) - r ); - - if( inside ) - aMTV = -delta.Resize( abs( min_dist + 1 + nearest_seg_dist ) + 1 ); - else - aMTV = delta.Resize( abs( min_dist + 1 - nearest_seg_dist ) + 1 ); + if( aMTV ) + { + 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; @@ -154,387 +161,438 @@ static VECTOR2I pushoutForce( const SHAPE_CIRCLE& aA, const SEG& aB, int aCleara static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { bool found = false; for( int s = 0; s < aB.SegmentCount(); s++ ) { - if( aA.Collide( aB.CSegment( s ), aClearance ) ) + if( aA.Collide( aB.CSegment( s ), aClearance, aActual ) ) { found = true; break; } } - if( !aNeedMTV || !found ) - return found; + if( !found ) + return false; - SHAPE_CIRCLE cmoved( aA ); - VECTOR2I f_total( 0, 0 ); - - for( int s = 0; s < aB.SegmentCount(); s++ ) + if( aMTV ) { - VECTOR2I f = pushoutForce( cmoved, aB.CSegment( 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.SegmentCount(); s++ ) + { + VECTOR2I f = pushoutForce( cmoved, aB.CSegment( s ), aClearance ); + cmoved.SetCenter( cmoved.GetCenter() + f ); + f_total += f; + } + + *aMTV = f_total; } - aMTV = f_total; - return found; + return true; } static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SIMPLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - bool found; - const SHAPE_LINE_CHAIN& lc( aB.Vertices() ); + int min_dist = aClearance + aA.GetRadius(); + ecoord dist_sq = aB.Vertices().SquaredDistance( aA.GetCenter() ); - found = lc.Distance( aA.GetCenter() ) <= aClearance + aA.GetRadius(); + if( dist_sq > (ecoord) min_dist * min_dist ) + return false; - if( !aNeedMTV || !found ) - return found; + if( aActual ) + *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() ); - SHAPE_CIRCLE cmoved( aA ); - VECTOR2I f_total( 0, 0 ); - - for( int s = 0; s < lc.SegmentCount(); s++ ) + if( aMTV ) { - VECTOR2I f = pushoutForce( cmoved, lc.CSegment( s ), aClearance ); - cmoved.SetCenter( cmoved.GetCenter() + f ); - f_total += f; - } + SHAPE_CIRCLE cmoved( aA ); + VECTOR2I f_total( 0, 0 ); - aMTV = f_total; - return found; + for( int s = 0; s < aB.Vertices().SegmentCount(); s++ ) + { + VECTOR2I f = pushoutForce( cmoved, aB.Vertices().CSegment( s ), aClearance ); + cmoved.SetCenter( cmoved.GetCenter() + f ); + f_total += f; + } + + *aMTV = f_total; + } + return true; } static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SEGMENT& aSeg, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - bool col = aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2); + bool col = aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, aActual ); + + if( col && aMTV ) + *aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2); - if( col && aNeedMTV ) - { - aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2); - } return col; } static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { for( int i = 0; i < aB.SegmentCount(); i++ ) - if( aA.Collide( aB.CSegment( i ), aClearance ) ) + { + if( aA.Collide( aB.CSegment( i ), aClearance, aActual ) ) return true; + } return false; } static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_SIMPLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - return Collide( aA, aB.Vertices(), aClearance, aNeedMTV, aMTV ); + return Collide( aA, aB.Vertices(), aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_SIMPLE& aA, const SHAPE_SIMPLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - return Collide( aA.Vertices(), aB.Vertices(), aClearance, aNeedMTV, aMTV ); + return Collide( aA.Vertices(), aB.Vertices(), aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { + int minActual = INT_MAX; + int actual; + for( int s = 0; s < aB.SegmentCount(); s++ ) { - if( aA.Collide( aB.CSegment( s ), aClearance ) ) - return true; + if( aA.Collide( aB.CSegment( s ), aClearance, &actual ) ) + { + minActual = std::min( minActual, actual ); + + if( !aActual ) + return true; + } + } + + if( aActual ) + *aActual = std::max( 0, minActual ); + + return minActual < INT_MAX; +} + + +static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SIMPLE& aB, int aClearance, + int* aActual, VECTOR2I* aMTV ) +{ + return Collide( aA, aB.Vertices(), aClearance, aActual, aMTV ); +} + + +static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aSeg, int aClearance, + int* aActual, VECTOR2I* aMTV ) +{ + int actual; + + if( aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, &actual ) ) + { + if( aActual ) + *aActual = std::max( 0, actual - aSeg.GetWidth() / 2 ); + + return true; } return false; } -static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SIMPLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) -{ - return Collide( aA, aB.Vertices(), aClearance, aNeedMTV, aMTV ); -} - - -static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aSeg, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) -{ - return aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2 ); -} - - static inline bool Collide( const SHAPE_SEGMENT& aA, const SHAPE_SEGMENT& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - return aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2 ); + int actual; + + if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, &actual ) ) + { + if( aActual ) + *aActual = std::max( 0, actual - aB.GetWidth() / 2 ); + + return true; + } + + return false; } static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_SEGMENT& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2 ) ) + int actual; + + if( aA.Collide( aB.GetSeg(), aClearance + aB.GetWidth() / 2, &actual ) ) + { + if( aActual ) + *aActual = std::max( 0, actual - aB.GetWidth() / 2 ); + return true; + } return false; } static inline bool Collide( const SHAPE_SIMPLE& aA, const SHAPE_SEGMENT& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - return Collide( aA.Vertices(), aB, aClearance, aNeedMTV, aMTV ); + return Collide( aA.Vertices(), aB, aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { - return Collide( aA.Outline(), aB.Outline(), aClearance, aNeedMTV, aMTV ); + return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_RECT& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB.Outline(), aClearance, aNeedMTV, aMTV ); + return Collide( lc, aB.Outline(), aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_CIRCLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - bool rv = Collide( aB, lc, aClearance, aNeedMTV, aMTV ); + bool rv = Collide( aB, lc, aClearance, aActual, aMTV ); - if( rv && aNeedMTV ) - aMTV = -aMTV; + if( rv && aMTV ) + *aMTV = - *aMTV ; return rv; } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB, aClearance, aNeedMTV, aMTV ); + return Collide( lc, aB, aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_SEGMENT& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB, aClearance, aNeedMTV, aMTV ); + return Collide( lc, aB, aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_SIMPLE& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { const auto lc = aA.ConvertToPolyline(); - return Collide( lc, aB.Vertices(), aClearance, aNeedMTV, aMTV ); + + return Collide( lc, aB.Vertices(), aClearance, aActual, aMTV ); } static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_ARC& aB, int aClearance, - bool aNeedMTV, VECTOR2I& aMTV ) + int* aActual, VECTOR2I* aMTV ) { const auto lcA = aA.ConvertToPolyline(); const auto lcB = aB.ConvertToPolyline(); - return Collide( lcA, lcB, aClearance, aNeedMTV, aMTV ); + return Collide( lcA, lcB, aClearance, aActual, aMTV ); } -template -inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) +template +inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, + VECTOR2I* aMTV ) + { - return Collide (*static_cast( aA ), - *static_cast( aB ), - aClearance, aNeedMTV, aMTV); + return Collide( *static_cast( aA ), *static_cast( aB ), + aClearance, aActual, aMTV); } -template -inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) +template +inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, + VECTOR2I* aMTV ) { - bool rv = Collide (*static_cast( aB ), - *static_cast( aA ), - aClearance, aNeedMTV, aMTV); - if(rv && aNeedMTV) - aMTV = -aMTV; + bool rv = Collide( *static_cast( aB ), *static_cast( aA ), + aClearance, aActual, aMTV); + + if( rv && aMTV) + *aMTV = - *aMTV; + return rv; } -bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) +bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual, VECTOR2I* aMTV ) { switch( aA->Type() ) { + case SH_RECT: + switch( aB->Type() ) + { case SH_RECT: - switch( aB->Type() ) - { - case SH_RECT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SIMPLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return CollCase( aA, aB, aClearance, aActual, aMTV ); case SH_CIRCLE: - switch( aB->Type() ) - { - case SH_RECT: - return CollCaseReversed( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SIMPLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return CollCase( aA, aB, aClearance, aActual, aMTV ); case SH_LINE_CHAIN: - switch( aB->Type() ) - { - case SH_RECT: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SIMPLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return CollCase( aA, aB, aClearance, aActual, aMTV ); case SH_SEGMENT: - switch( aB->Type() ) - { - case SH_RECT: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return CollCaseReversed( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SIMPLE: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return CollCase( aA, aB, aClearance, aActual, aMTV ); case SH_SIMPLE: - switch( aB->Type() ) - { - case SH_RECT: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return CollCase( aB, aA, aClearance, aNeedMTV, aMTV ); - - case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SIMPLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_ARC: - return CollCaseReversed( aA, aB, aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return CollCase( aA, aB, aClearance, aActual, aMTV ); case SH_ARC: - switch( aB->Type() ) - { - case SH_RECT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_CIRCLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_LINE_CHAIN: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SEGMENT: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_SIMPLE: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - case SH_ARC: - return CollCase( aA, aB, aClearance, aNeedMTV, aMTV ); - - default: - break; - } - break; + return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); default: break; + } + break; + + case SH_CIRCLE: + switch( aB->Type() ) + { + case SH_RECT: + return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + + case SH_CIRCLE: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_LINE_CHAIN: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SEGMENT: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SIMPLE: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_ARC: + return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + + default: + break; + } + break; + + case SH_LINE_CHAIN: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_CIRCLE: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_LINE_CHAIN: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SEGMENT: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SIMPLE: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_ARC: + return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + + default: + break; + } + break; + + case SH_SEGMENT: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_CIRCLE: + return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + + case SH_LINE_CHAIN: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_SEGMENT: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SIMPLE: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_ARC: + return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + + default: + break; + } + break; + + case SH_SIMPLE: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_CIRCLE: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_LINE_CHAIN: + return CollCase( aB, aA, aClearance, aActual, aMTV ); + + case SH_SEGMENT: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SIMPLE: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_ARC: + return CollCaseReversed( aA, aB, aClearance, aActual, aMTV ); + + default: + break; + } + break; + + case SH_ARC: + switch( aB->Type() ) + { + case SH_RECT: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_CIRCLE: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_LINE_CHAIN: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SEGMENT: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_SIMPLE: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + case SH_ARC: + return CollCase( aA, aB, aClearance, aActual, aMTV ); + + default: + break; + } + break; + + default: + break; } bool unsupported_collision = true; @@ -546,53 +604,46 @@ bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeed } -bool SHAPE::Collide( const SHAPE* aShape, int aClearance, VECTOR2I& aMTV ) const +bool SHAPE::Collide( const SHAPE* aShape, int aClearance, VECTOR2I* aMTV ) const { - return CollideShapes( this, aShape, aClearance, true, aMTV ); + return CollideShapes( this, aShape, aClearance, nullptr, aMTV ); } -bool SHAPE::Collide( const SHAPE* aShape, int aClearance ) const +bool SHAPE::Collide( const SHAPE* aShape, int aClearance, int* aActual ) const { - VECTOR2I dummy; - - return CollideShapes( this, aShape, aClearance, false, dummy ); + return CollideShapes( this, aShape, aClearance, aActual, nullptr ); } -bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance ) const -{ - int dummy; - return DoCollide( aSeg, aClearance, &dummy ); -} - - -bool SHAPE_RECT::DoCollide( const SEG& aSeg, int aClearance, int* aActualDist ) const +bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual ) const { if( BBox( 0 ).Contains( aSeg.A ) || BBox( 0 ).Contains( aSeg.B ) ) { - *aActualDist = 0; + *aActual = 0; return true; } - VECTOR2I vts[] = { VECTOR2I( m_p0.x, m_p0.y ), - VECTOR2I( m_p0.x, m_p0.y + m_h ), - VECTOR2I( m_p0.x + m_w, m_p0.y + m_h ), - VECTOR2I( m_p0.x + m_w, m_p0.y ), - VECTOR2I( m_p0.x, m_p0.y ) }; + VECTOR2I corners[] = { VECTOR2I( m_p0.x, m_p0.y ), + VECTOR2I( m_p0.x, m_p0.y + m_h ), + VECTOR2I( m_p0.x + m_w, m_p0.y + m_h ), + VECTOR2I( m_p0.x + m_w, m_p0.y ), + VECTOR2I( m_p0.x, m_p0.y ) }; - SEG s( vts[0], vts[1] ); + SEG s( corners[0], corners[1] ); SEG::ecoord dist_squared = s.SquaredDistance( aSeg ); for( int i = 1; i < 4; i++ ) { - s = SEG( vts[i], vts[i + 1] ); + s = SEG( corners[i], corners[ i + 1] ); dist_squared = std::min( dist_squared, s.SquaredDistance( aSeg ) ); } - if( dist_squared < SEG::ecoord( aClearance ) * SEG::ecoord( aClearance ) ) + if( dist_squared < (ecoord) aClearance * aClearance ) { - *aActualDist = sqrt( dist_squared ); + if( aActual ) + *aActual = sqrt( dist_squared ); + return true; } diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp index e507391bb4..e72927827e 100644 --- a/libs/kimath/src/geometry/shape_line_chain.cpp +++ b/libs/kimath/src/geometry/shape_line_chain.cpp @@ -85,11 +85,29 @@ void SHAPE_LINE_CHAIN::convertArc( ssize_t aArcIndex ) } -bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance ) const +bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const { - // fixme: ugly! - SEG s( aP, aP ); - return this->Collide( s, aClearance ); + SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX; + SEG::ecoord clearance_sq = SEG::Square( aClearance ); + + for( int i = 0; i < SegmentCount(); i++ ) + { + const SEG& s = CSegment( i ); + dist_sq = std::min( dist_sq, s.SquaredDistance( aP ) ); + + if( !aActual && dist_sq < clearance_sq ) + return true; + } + + if( dist_sq < clearance_sq ) + { + if( aActual ) + *aActual = sqrt( dist_sq ); + + return true; + } + + return false; } @@ -107,23 +125,26 @@ void SHAPE_LINE_CHAIN::Rotate( double aAngle, const VECTOR2I& aCenter ) } -bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance ) const +bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance, int* aActual ) const { - BOX2I box_a( aSeg.A, aSeg.B - aSeg.A ); - BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance; + SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX; + SEG::ecoord clearance_sq = SEG::Square( aClearance ); for( int i = 0; i < SegmentCount(); i++ ) { const SEG& s = CSegment( i ); - BOX2I box_b( s.A, s.B - s.A ); + dist_sq = std::min( dist_sq, s.SquaredDistance( aSeg ) ); - BOX2I::ecoord_type d = box_a.SquaredDistance( box_b ); + if( !aActual && dist_sq < clearance_sq ) + return true; + } - if( d < dist_sq ) - { - if( s.Collide( aSeg, aClearance ) ) - return true; - } + if( dist_sq < clearance_sq ) + { + if( aActual ) + *aActual = sqrt( dist_sq ); + + return true; } return false; @@ -267,13 +288,19 @@ void SHAPE_LINE_CHAIN::Remove( int aStartIndex, int aEndIndex ) int SHAPE_LINE_CHAIN::Distance( const VECTOR2I& aP, bool aOutlineOnly ) const { - int d = INT_MAX; + return sqrt( SquaredDistance( aP, aOutlineOnly ) ); +} + + +SEG::ecoord SHAPE_LINE_CHAIN::SquaredDistance( const VECTOR2I& aP, bool aOutlineOnly ) const +{ + ecoord d = VECTOR2I::ECOORD_MAX; if( IsClosed() && PointInside( aP ) && !aOutlineOnly ) return 0; for( int s = 0; s < SegmentCount(); s++ ) - d = std::min( d, CSegment( s ).Distance( aP ) ); + d = std::min( d, CSegment( s ).SquaredDistance( aP ) ); return d; } diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index 2567bfe871..013b63284a 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -1224,50 +1224,35 @@ bool SHAPE_POLY_SET::PointOnEdge( const VECTOR2I& aP ) const } -bool SHAPE_POLY_SET::Collide( const SEG& aSeg, int aClearance ) const +bool SHAPE_POLY_SET::Collide( const SEG& aSeg, int aClearance, int* aActual ) const { + ecoord dist_sq = SquaredDistance( aSeg ); - SHAPE_POLY_SET polySet = SHAPE_POLY_SET( *this ); - - // Inflate the polygon if necessary. - if( aClearance > 0 ) + if( dist_sq < (ecoord) aClearance * aClearance ) { - // fixme: the number of arc segments should not be hardcoded - polySet.Inflate( aClearance, 8 ); - } + if( aActual ) + *aActual = sqrt( dist_sq ); - // We are going to check to see if the segment crosses an external - // boundary. However, if the full segment is inside the polyset, this - // will not be true. So we first test to see if one of the points is - // inside. If true, then we collide - if( polySet.Contains( aSeg.A ) ) return true; - - for( CONST_SEGMENT_ITERATOR it = CIterateSegmentsWithHoles(); it; it++ ) - { - const SEG polygonEdge = *it; - - if( polygonEdge.Intersect( aSeg, true ) ) - return true; } return false; } -bool SHAPE_POLY_SET::Collide( const VECTOR2I& aP, int aClearance ) const +bool SHAPE_POLY_SET::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const { - SHAPE_POLY_SET polySet = SHAPE_POLY_SET( *this ); + ecoord dist_sq = SquaredDistance( aP ); - // Inflate the polygon if necessary. - if( aClearance > 0 ) + if( dist_sq < (ecoord) aClearance * aClearance ) { - // fixme: the number of arc segments should not be hardcoded - polySet.Inflate( aClearance, 8 ); + if( aActual ) + *aActual = sqrt( dist_sq ); + + return true; } - // There is a collision if and only if the point is inside of the polygon. - return polySet.Contains( aP ); + return false; } diff --git a/libs/kimath/src/trigo.cpp b/libs/kimath/src/trigo.cpp index 58b44a11dd..662882e04b 100644 --- a/libs/kimath/src/trigo.cpp +++ b/libs/kimath/src/trigo.cpp @@ -155,7 +155,7 @@ bool TestSegmentHit( const wxPoint &aRefPoint, wxPoint aStart, wxPoint aEnd, int return std::abs( delta.y ) <= aDist; SEG segment( aStart, aEnd ); - return segment.PointCloserThan( aRefPoint, aDist + 1 ); + return segment.SquaredDistance( aRefPoint ) < SEG::Square( aDist + 1 ); } diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index 3dcc9f5698..50dd42984c 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -370,7 +370,7 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB { case S_CIRCLE: if( width == 0 ) - TransformCircleToPolygon( aCornerBuffer, GetCenter(), GetRadius() + width / 2, aError ); + TransformCircleToPolygon( aCornerBuffer, GetCenter(), GetRadius(), aError ); else TransformRingToPolygon( aCornerBuffer, GetCenter(), GetRadius(), aError, width ); break; diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp index 1865b0d430..828539f675 100644 --- a/pcbnew/class_drawsegment.cpp +++ b/pcbnew/class_drawsegment.cpp @@ -34,6 +34,9 @@ #include #include #include +#include +#include +#include #include #include @@ -590,16 +593,13 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const int radius = GetRadius(); int dist = KiROUND( EuclideanNorm( aPosition - GetCenter() ) ); - if( m_Width == 0 ) + if( m_Width == 0 ) // Filled circle hit-test { - // Filled circle hit-test if( dist <= radius + maxdist ) return true; } - - if( m_Width > 0 ) + else // Ring hit-test { - // Ring hit-test if( abs( radius - dist ) <= maxdist ) return true; } @@ -666,7 +666,7 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const std::vector pts; GetRectCorners( &pts ); - if( m_Width == 0 ) + if( m_Width == 0 ) // Filled rect hit-test { SHAPE_POLY_SET poly; poly.NewOutline(); @@ -677,8 +677,7 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const if( poly.Collide( VECTOR2I( aPosition ), maxdist ) ) return true; } - - if( m_Width > 0 ) + else // Open rect hit-test { if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist ) || TestSegmentHit( aPosition, pts[1], pts[2], maxdist ) @@ -828,7 +827,7 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy } break; - case S_CURVE: // not yet handled + case S_CURVE: if( aContained ) { return arect.Contains( bb ); @@ -1015,6 +1014,112 @@ void DRAWSEGMENT::SetPolyPoints( const std::vector& aPoints ) } +std::vector DRAWSEGMENT::MakeEffectiveShapes() +{ + std::vector effectiveShapes; + + switch( m_Shape ) + { + case S_ARC: + { + SHAPE_ARC arc( GetCenter(), GetArcStart(), (double) GetAngle() / 10.0 ); + SHAPE_LINE_CHAIN l = arc.ConvertToPolyline(); + + for( int i = 0; i < l.SegmentCount(); i++ ) + { + effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A, + l.Segment( i ).B, m_Width ) ); + } + + break; + } + + case S_SEGMENT: + effectiveShapes.emplace_back( new SHAPE_SEGMENT( GetStart(), GetEnd(), m_Width ) ); + break; + + case S_RECT: + { + std::vector pts; + GetRectCorners( &pts ); + + if( m_Width == 0 ) + { + effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) ); + } + else + { + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], m_Width ) ); + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], m_Width ) ); + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], m_Width ) ); + effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], m_Width ) ); + } + } + break; + + case S_CIRCLE: + { + if( m_Width == 0 ) + { + effectiveShapes.emplace_back( new SHAPE_CIRCLE( GetCenter(), GetRadius() ) ); + } + else + { + // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC + SHAPE_ARC circle( GetCenter(), GetEnd(), 360.0 ); + SHAPE_LINE_CHAIN l = circle.ConvertToPolyline(); + + for( int i = 0; i < l.SegmentCount(); i++ ) + { + effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ).A, + l.Segment( i ).B, m_Width ) ); + } + } + + break; + } + + case S_CURVE: + { + RebuildBezierToSegmentsPointsList( GetWidth() ); + wxPoint start_pt = GetBezierPoints()[0]; + + for( unsigned int jj = 1; jj < GetBezierPoints().size(); jj++ ) + { + wxPoint end_pt = GetBezierPoints()[jj]; + effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, m_Width ) ); + start_pt = end_pt; + } + + break; + } + + case S_POLYGON: + { + SHAPE_LINE_CHAIN l = GetPolyShape().Outline( 0 ); + + if( IsPolygonFilled() ) + { + effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) ); + } + else + { + for( int i = 0; i < l.SegmentCount(); i++ ) + effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ) ) ); + } + } + break; + + default: + wxFAIL_MSG( "DRAWSEGMENT::MakeEffectiveShapes unsupported DRAWSEGMENT shape: " + + STROKE_T_asString( m_Shape ) ); + break; + } + + return effectiveShapes; +} + + const std::vector DRAWSEGMENT::BuildPolyPointsList() const { std::vector rv; diff --git a/pcbnew/class_drawsegment.h b/pcbnew/class_drawsegment.h index 73c9b15038..a6c8a21a13 100644 --- a/pcbnew/class_drawsegment.h +++ b/pcbnew/class_drawsegment.h @@ -36,9 +36,9 @@ #include // for KiROUND #include #include - #include + class LINE_READER; class EDA_DRAW_FRAME; class MODULE; @@ -237,6 +237,11 @@ public: void SetPolyPoints( const std::vector& aPoints ); + /** + * Makes a set of SHAPE objects representing the DRAWSEGMENT. Caller owns the objects. + */ + std::vector MakeEffectiveShapes(); + void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) override; const EDA_RECT GetBoundingBox() const override; diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 96c40b45e9..4d0186a7b7 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -327,32 +327,28 @@ void D_PAD::BuildEffectiveShapes() const if( GetShape() == PAD_SHAPE_CUSTOM ) { - SHAPE_POLY_SET* poly = new SHAPE_POLY_SET(); - MergePrimitivesAsPolygon( poly ); - poly->Rotate( -DECIDEG2RAD( m_Orient ) ); - poly->Move( shapePos ); - add( poly ); + for( const std::shared_ptr& primitive : m_editPrimitives ) + { + for( SHAPE* shape : primitive->MakeEffectiveShapes() ) + { + shape->Rotate( -DECIDEG2RAD( m_Orient ) ); + shape->Move( shapePos ); + add( shape ); + } + } } // Bounding box and radius // m_effectiveBoundingRadius = calcBoundingRadius(); - bool first_shape = true; + m_effectiveBoundingBox = EDA_RECT(); // reset to prepare for merging for( const std::shared_ptr& shape : m_effectiveShapes ) { BOX2I r = shape->BBox(); - - if( first_shape ) - { - m_effectiveBoundingBox.SetOrigin( (wxPoint) r.GetOrigin() ); - m_effectiveBoundingBox.SetEnd( (wxPoint) r.GetEnd() ); - first_shape = false; - } - else - m_effectiveBoundingBox.Merge( EDA_RECT( (wxPoint) r.GetOrigin(), - wxSize( r.GetWidth(), r.GetHeight() ) ) ); + m_effectiveBoundingBox.Merge( EDA_RECT( (wxPoint) r.GetOrigin(), + wxSize( r.GetWidth(), r.GetHeight() ) ) ); } // Hole shape diff --git a/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp b/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp index 3070cf4693..e5e4f02ec3 100644 --- a/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp +++ b/pcbnew/dialogs/dialog_pad_basicshapes_properties.cpp @@ -7,7 +7,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-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 @@ -32,13 +32,10 @@ #include #include #include -#include #include -#include #include #include #include -#include #include #include // for KiROUND @@ -224,8 +221,11 @@ DIALOG_PAD_PRIMITIVE_POLY_PROPS::DIALOG_PAD_PRIMITIVE_POLY_PROPS( wxWindow* aPar m_shape( aShape ), m_thickness( aFrame, m_thicknessLabel, m_thicknessCtrl, m_thicknessUnits, true ) { - for( const VECTOR2I& pt : m_shape->GetPolyShape().Outline( 0 ).CPoints() ) - m_currPoints.emplace_back( pt ); + if( !m_shape->GetPolyShape().IsEmpty() ) + { + for( const VECTOR2I& pt : m_shape->GetPolyShape().Outline( 0 ).CPoints() ) + m_currPoints.emplace_back( pt ); + } m_addButton->SetBitmap( KiBitmap( small_plus_xpm ) ); m_deleteButton->SetBitmap( KiBitmap( trash_xpm ) ); diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp index 20c5e19bf5..f9c198e18f 100644 --- a/pcbnew/drc/drc_clearance_test_functions.cpp +++ b/pcbnew/drc/drc_clearance_test_functions.cpp @@ -626,7 +626,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS } -bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActualDist ) +bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance, int* aActual ) { int center2center = KiROUND( EuclideanNorm( aPad->ShapePos() - aRefPad->ShapePos() ) ); @@ -634,33 +634,24 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad, int aMinClearance if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance ) return true; - // JEY TODO: - // TOM TODO: MTV only works as a proxy for actual-distance for convex shapes - - VECTOR2I mtv; // minimum translation vector calculated by Collide() - VECTOR2I maxMtv( 0, 0 ); // is the move distance between 2 shapes calculated - // by Collide to do not have a collision - bool shapes_collide = false; + int actual = INT_MAX; for( const std::shared_ptr& aShape : aRefPad->GetEffectiveShapes() ) { for( const std::shared_ptr& bShape : aPad->GetEffectiveShapes() ) { - if( aShape->Collide( bShape.get(), aMinClearance, mtv ) ) - { - shapes_collide = true; + int this_dist; - if( mtv.SquaredEuclideanNorm() > maxMtv.SquaredEuclideanNorm() ) - maxMtv = mtv; - } + if( aShape->Collide( bShape.get(), aMinClearance, &this_dist ) ) + actual = std::min( actual, this_dist ); } } - if( shapes_collide ) + if( actual < INT_MAX ) { // returns the actual clearance (clearance < aMinClearance) for diags: - if( aActualDist ) - *aActualDist = std::max( 0, aMinClearance - maxMtv.EuclideanNorm() ); + if( aActual ) + *aActual = std::max( 0, actual ); return false; } @@ -719,7 +710,7 @@ bool DRC::checkClearanceSegmToPad( const SEG& refSeg, int refSegWidth, const D_P SHAPE_RECT padShape( padBBox.GetPosition(), padBBox.GetWidth(), padBBox.GetHeight() ); int actual; - if( padShape.DoCollide( refSeg, minClearance + widths, &actual ) ) + if( padShape.Collide( refSeg, minClearance + widths, &actual ) ) { *aActualDist = std::max( 0, actual - widths ); return false; diff --git a/pcbnew/pad_custom_shape_functions.cpp b/pcbnew/pad_custom_shape_functions.cpp index 4149d52f40..0d250c82b2 100644 --- a/pcbnew/pad_custom_shape_functions.cpp +++ b/pcbnew/pad_custom_shape_functions.cpp @@ -29,18 +29,12 @@ #include #include - #include - -#include #include #include #include -#include #include #include -#include -#include #include @@ -184,107 +178,7 @@ void D_PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aErro SHAPE_POLY_SET polyset; for( const std::shared_ptr& primitive : m_editPrimitives ) - { - int lineWidth = primitive->GetWidth(); - - switch( primitive->GetShape() ) - { - case S_CURVE: - { - std::vector ctrlPoints = { primitive->GetStart(), primitive->GetBezControl1(), - primitive->GetBezControl2(), primitive->GetEnd() }; - BEZIER_POLY converter( ctrlPoints ); - std::vector< wxPoint> poly; - converter.GetPoly( poly, lineWidth ); - - for( unsigned ii = 1; ii < poly.size(); ii++ ) - TransformSegmentToPolygon( polyset, poly[ ii - 1 ], poly[ ii ], aError, lineWidth ); - break; - } - - case S_SEGMENT: // usual segment : line with rounded ends - { - TransformSegmentToPolygon( polyset, primitive->GetStart(), primitive->GetEnd(), - aError, lineWidth ); - break; - } - - case S_ARC: // Arc with rounded ends - { - TransformArcToPolygon( polyset, primitive->GetStart(), primitive->GetEnd(), - primitive->GetAngle(), aError, lineWidth ); - break; - } - - case S_CIRCLE: - { - if( lineWidth ) // Ring - { - TransformRingToPolygon( polyset, primitive->GetStart(), primitive->GetRadius(), - aError, lineWidth ); - } - else // Filled circle - { - TransformCircleToPolygon( polyset, primitive->GetStart(), primitive->GetRadius(), - aError ); - } - break; - } - - case S_RECT: - { - wxPoint corners[4]; - - corners[0] = primitive->GetStart(); - corners[1] = wxPoint( primitive->GetEnd().x, primitive->GetStart().y ); - corners[2] = primitive->GetEnd(); - corners[3] = wxPoint( primitive->GetStart().x, primitive->GetEnd().y ); - - if( lineWidth ) // Rect boundary - { - TransformSegmentToPolygon( polyset, corners[0], corners[1], aError, lineWidth ); - TransformSegmentToPolygon( polyset, corners[1], corners[2], aError, lineWidth ); - TransformSegmentToPolygon( polyset, corners[2], corners[3], aError, lineWidth ); - TransformSegmentToPolygon( polyset, corners[3], corners[0], aError, lineWidth ); - } - else // Filled rect - { - // Insert the polygon: - polyset.NewOutline(); - - for( const wxPoint& corner : corners ) - polyset.Append( corner ); - } - } - break; - - case S_POLYGON: - { - SHAPE_POLY_SET poly; - poly.NewOutline(); - - for( const VECTOR2I& pt : primitive->GetPolyShape().Outline( 0 ).CPoints() ) - poly.Append( pt ); - - if( primitive->GetWidth() > 0 ) - { - int numSegs = std::max( GetArcToSegmentCount( lineWidth / 2, aError, 360.0 ), 6 ); - poly.Inflate( lineWidth / 2, numSegs ); - } - - // Insert the polygon: - polyset.NewOutline(); - polyset.Append( poly ); - } - break; - - default: - // un-handled primitive - wxASSERT_MSG( false, "D_PAD::addPadPrimitivesToPolygon not implemented for " - + BOARD_ITEM::ShowShape( primitive->GetShape() ) ); - break; - } - } + primitive->TransformShapeWithClearanceToPolygon( polyset, 0, aError ); polyset.Simplify( SHAPE_POLY_SET::PM_FAST ); diff --git a/pcbnew/router/pns_item.cpp b/pcbnew/router/pns_item.cpp index 19f5bbb7dc..ea9e35e012 100644 --- a/pcbnew/router/pns_item.cpp +++ b/pcbnew/router/pns_item.cpp @@ -39,7 +39,7 @@ bool ITEM::collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VEC return false; if( aNeedMTV ) - return Shape()->Collide( aOther->Shape(), aClearance, *aMTV ); + return Shape()->Collide( aOther->Shape(), aClearance, aMTV ); else return Shape()->Collide( aOther->Shape(), aClearance ); } diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 5703bf06a0..15cdeb0024 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -826,90 +826,14 @@ bool PNS_KICAD_IFACE_BASE::syncTextItem( PNS::NODE* aWorld, EDA_TEXT* aText, PCB bool PNS_KICAD_IFACE_BASE::syncGraphicalItem( PNS::NODE* aWorld, DRAWSEGMENT* aItem ) { - std::vector segs; - if( aItem->GetLayer() != Edge_Cuts && !IsCopperLayer( aItem->GetLayer() ) ) return false; - switch( aItem->GetShape() ) - { - case S_ARC: - { - SHAPE_ARC arc( aItem->GetCenter(), aItem->GetArcStart(), aItem->GetAngle() / 10.0 ); - auto l = arc.ConvertToPolyline(); + // TODO: where do we handle filled polygons on copper layers? + if( aItem->GetShape() == S_POLYGON && aItem->IsPolygonFilled() ) + return false; - for( int i = 0; i < l.SegmentCount(); i++ ) - { - SHAPE_SEGMENT* seg = new SHAPE_SEGMENT( l.CSegment( i ), aItem->GetWidth() ); - segs.push_back( seg ); - } - - break; - } - - case S_SEGMENT: - segs.push_back( - new SHAPE_SEGMENT( aItem->GetStart(), aItem->GetEnd(), aItem->GetWidth() ) ); - - break; - - case S_RECT: - { - std::vector pts; - aItem->GetRectCorners( &pts ); - - segs.push_back( new SHAPE_SEGMENT( pts[0], pts[1], aItem->GetWidth() ) ); - segs.push_back( new SHAPE_SEGMENT( pts[1], pts[2], aItem->GetWidth() ) ); - segs.push_back( new SHAPE_SEGMENT( pts[2], pts[3], aItem->GetWidth() ) ); - segs.push_back( new SHAPE_SEGMENT( pts[3], pts[0], aItem->GetWidth() ) ); - } - break; - - case S_CIRCLE: - { - // SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC - SHAPE_ARC circle( aItem->GetCenter(), aItem->GetEnd(), 360.0 ); - auto l = circle.ConvertToPolyline(); - - for( int i = 0; i < l.SegmentCount(); i++ ) - segs.push_back( new SHAPE_SEGMENT( l.CSegment( i ), aItem->GetWidth() ) ); - - break; - } - - case S_CURVE: - { - aItem->RebuildBezierToSegmentsPointsList( aItem->GetWidth() ); - auto pts = aItem->GetBezierPoints(); - - for( size_t ii = 1; ii < pts.size(); ii++ ) - { - segs.push_back( new SHAPE_SEGMENT( - VECTOR2I( pts[ii - 1] ), VECTOR2I( pts[ii] ), aItem->GetWidth() ) ); - } - break; - } - - case S_POLYGON: - if( !aItem->IsPolygonFilled() ) - { - auto poly = aItem->BuildPolyPointsList(); - for( size_t ii = 1; ii < poly.size(); ii++ ) - { - segs.push_back( new SHAPE_SEGMENT( - VECTOR2I( poly[ii - 1] ), VECTOR2I( poly[ii] ), aItem->GetWidth() ) ); - } - - segs.push_back( new SHAPE_SEGMENT( - VECTOR2I( poly.back() ), VECTOR2I( poly.front() ), aItem->GetWidth() ) ); - } - break; - - default: - break; - } - - for( auto seg : segs ) + for( SHAPE* shape : aItem->MakeEffectiveShapes() ) { std::unique_ptr< PNS::SOLID > solid( new PNS::SOLID ); @@ -920,7 +844,7 @@ bool PNS_KICAD_IFACE_BASE::syncGraphicalItem( PNS::NODE* aWorld, DRAWSEGMENT* aI solid->SetNet( -1 ); solid->SetParent( nullptr ); - solid->SetShape( seg ); + solid->SetShape( shape ); solid->SetRoutable( false ); aWorld->Add( std::move( solid ) ); diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp index b750866880..7c3a3b2834 100644 --- a/pcbnew/router/pns_shove.cpp +++ b/pcbnew/router/pns_shove.cpp @@ -814,7 +814,7 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in */ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia ) { - int clearance = getClearance( aCurrent, aObstacleVia ) ; + int clearance = getClearance( aCurrent, aObstacleVia ) + PNS_HULL_MARGIN; LINE_PAIR_VEC draggedLines; bool lineCollision = false; bool viaCollision = false; @@ -835,9 +835,9 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia ) #endif currentLine = (LINE*) aCurrent; - lineCollision = CollideShapes( aObstacleVia->Shape(), currentLine->Shape(), - clearance + currentLine->Width() / 2 + PNS_HULL_MARGIN, - true, mtvLine ); + lineCollision = aObstacleVia->Shape()->Collide( currentLine->Shape(), + clearance + currentLine->Width() / 2, + &mtvLine ); if( currentLine->EndsWithVia() ) { @@ -846,8 +846,8 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia ) if( currentNet != obstacleNet && currentNet >= 0 && obstacleNet >= 0 ) { - viaCollision = CollideShapes( currentLine->Via().Shape(), aObstacleVia->Shape(), - clearance + PNS_HULL_MARGIN, true, mtvVia ); + viaCollision = currentLine->Via().Shape()->Collide( aObstacleVia->Shape(), + clearance, &mtvVia ); } // hole-to-hole is a mechanical constraint (broken drill bits), not an electrical @@ -875,8 +875,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia ) } else if( aCurrent->OfKind( ITEM::SOLID_T ) ) { - CollideShapes( aObstacleVia->Shape(), aCurrent->Shape(), - clearance + PNS_HULL_MARGIN, true, mtvSolid ); + aObstacleVia->Shape()->Collide( aCurrent->Shape(), clearance, &mtvSolid ); mtv = -mtvSolid; rank = aCurrent->Rank() + 10000; } diff --git a/pcbnew/router/pns_via.cpp b/pcbnew/router/pns_via.cpp index cbdb76d04d..5f7b69862e 100644 --- a/pcbnew/router/pns_via.cpp +++ b/pcbnew/router/pns_via.cpp @@ -34,12 +34,12 @@ bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForc { int iter = 0; VIA mv( *this ); - VECTOR2I force, totalForce, force2; + VECTOR2I force, totalForce; while( iter < aMaxIterations ) { - NODE::OPT_OBSTACLE obs = aNode->CheckColliding( &mv, - aSolidsOnly ? ITEM::SOLID_T : ITEM::ANY_T ); + NODE::OPT_OBSTACLE obs = aNode->CheckColliding( &mv, aSolidsOnly ? ITEM::SOLID_T + : ITEM::ANY_T ); if( !obs ) break; @@ -53,11 +53,10 @@ bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForc mv.SetPos( mv.Pos() + l ); } - bool col = CollideShapes( obs->m_item->Shape(), mv.Shape(), clearance, true, force2 ); - - if( col ) { - totalForce += force2; - mv.SetPos( mv.Pos() + force2 ); + if( obs->m_item->Shape()->Collide( mv.Shape(), clearance, &force ) ) + { + totalForce += force; + mv.SetPos( mv.Pos() + force ); } iter++;