Support optional location reporting in SHAPE collisions.

Also fixes a few bugs in the collision routines.
This commit is contained in:
Jeff Young 2020-09-28 23:27:33 +01:00
parent 39d2a46b30
commit bf67648562
28 changed files with 744 additions and 627 deletions

View File

@ -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;
/**

View File

@ -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 )
{

View File

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

View File

@ -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<SHAPE*>& Shapes() const
{
return m_shapes;

View File

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

View File

@ -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.

View File

@ -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()

View File

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

View File

@ -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

View File

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

View File

@ -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 = std::min( dist_sq, aSeg.SquaredDistance( m_start ) );
dist_sq = std::min( dist_sq, aSeg.SquaredDistance( m_end ) );
dist_sq = ( m_end - p ).SquaredEuclideanNorm();
if( dist_sq == 0 || dist_sq < (ecoord) minDist * minDist )
if( dist_sq < closest_dist_sq )
{
closest_dist_sq = dist_sq;
nearest = p;
}
}
dist_sq = aSeg.SquaredDistance( m_start );
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 );

View File

@ -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( aLocation )
*aLocation = ( aA.GetCenter() + aB.GetCenter() ) / 2;
if( aMTV )
*aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error
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,37 +103,41 @@ 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( inside || nearest_side_dist_sq < min_dist_sq )
{
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 false;
}
static VECTOR2I pushoutForce( const SHAPE_CIRCLE& aA, const SEG& aB, int aClearance )
{
@ -162,54 +165,40 @@ 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;
if( collision_dist < closest_dist )
{
nearest = pn;
closest_dist = collision_dist;
if( closest_dist == 0 || !aActual )
break;
}
}
if( !collided )
return false;
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;
}
#endif
static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
int* aActual, VECTOR2I* aMTV )
if( closest_dist < aClearance )
{
int min_dist = aClearance + aA.GetRadius();
ecoord dist_sq = aB.SquaredDistance( aA.GetCenter() );
if( dist_sq > (ecoord) min_dist * min_dist )
return false;
if( aLocation )
*aLocation = nearest;
if( aActual )
*aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() );
*aActual = closest_dist;
if( aMTV )
{
@ -225,32 +214,66 @@ static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE&
*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, aLocation ) )
{
if( !aA.Collide( aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2, aActual ) )
return false;
if( aMTV )
*aMTV = -pushoutForce( aA, aSeg.GetSeg(), aClearance + aSeg.GetWidth() / 2);
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 ) )
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;
}
@ -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( closest_dist < aClearance )
{
if( aLocation )
*aLocation = nearest;
if( aActual )
*aActual = std::max( 0, minActual );
*aActual = closest_dist;
// TODO: why doesn't this handle MTV?
return true;
}
return minActual < INT_MAX;
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<class T_a, class T_b>
inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
VECTOR2I* aMTV )
VECTOR2I* aLocation, VECTOR2I* aMTV )
{
return Collide( *static_cast<const T_a*>( aA ), *static_cast<const T_b*>( aB ),
aClearance, aActual, aMTV);
aClearance, aActual, aLocation, aMTV);
}
template<class T_a, class T_b>
inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
VECTOR2I* aMTV )
VECTOR2I* aLocation, VECTOR2I* aMTV )
{
bool rv = Collide( *static_cast<const T_b*>( aB ), *static_cast<const T_a*>( 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<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SIMPLE:
case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( 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<SHAPE_CIRCLE, SHAPE_RECT>( aA, aB, aClearance, aActual, aMTV );
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_CIRCLE, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SIMPLE:
case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( 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<SHAPE_RECT, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SIMPLE:
case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_LINE_CHAIN, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
return CollCaseReversed<SHAPE_LINE_CHAIN, SHAPE_ARC>( 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<SHAPE_RECT, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_CIRCLE:
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SIMPLE:
case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( 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<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aLocation, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SIMPLE:
case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_LINE_CHAIN_BASE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
return CollCaseReversed<SHAPE_LINE_CHAIN_BASE, SHAPE_ARC>( 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<SHAPE_ARC, SHAPE_RECT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_ARC, SHAPE_RECT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_ARC, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_ARC, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_SIMPLE:
case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aLocation, aMTV );
case SH_ARC:
return CollCase<SHAPE_ARC, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
return CollCase<SHAPE_ARC, SHAPE_ARC>( 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<int>::max();
VECTOR2I currentLocation;
VECTOR2I currentMTV(0, 0);
bool colliding = false;
bool exitOnFirstCollision = aActual == nullptr && aMTV == nullptr;
auto collideCompoundSubshapes = [&] ( const SHAPE* elemA, const SHAPE* elemB, int clearance ) -> bool
auto canExit =
[&]()
{
int actual;
if( !colliding )
return false;
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 );
}
if( aMTV )
{
if( mtv.SquaredEuclideanNorm() > currentMTV.SquaredEuclideanNorm() )
currentMTV = mtv;
}
currentActual = actual;
currentLocation = location;
}
return c;
if( aMTV && mtv.SquaredEuclideanNorm() > currentMTV.SquaredEuclideanNorm() )
{
currentMTV = mtv;
}
return true;
}
return false;
};
if( aA->Type() == SH_COMPOUND && aB->Type() == SH_COMPOUND )
{
auto cmpA = static_cast<const SHAPE_COMPOUND*>( aA );
auto cmpB = static_cast<const SHAPE_COMPOUND*>( aB );
const SHAPE_COMPOUND* cmpA = static_cast<const SHAPE_COMPOUND*>( aA );
const SHAPE_COMPOUND* cmpB = static_cast<const SHAPE_COMPOUND*>( 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<const SHAPE_COMPOUND*>( aA );
for( auto elemA : cmpA->Shapes() )
const SHAPE_COMPOUND* cmpA = static_cast<const SHAPE_COMPOUND*>( 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 )
{
auto cmpB = static_cast<const SHAPE_COMPOUND*>( aB );
for( auto elemB : cmpB->Shapes() )
const SHAPE_COMPOUND* cmpB = static_cast<const SHAPE_COMPOUND*>( 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 );
}

View File

@ -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<int>::max();
int closest_dist = std::numeric_limits<int>::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( closest_dist < aClearance )
{
if( aLocation )
*aLocation = nearest;
if( aActual )
*aActual = dist;
*aActual = closest_dist;
return dist != std::numeric_limits<int>::max();
return true;
}
return false;
}

View File

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

View File

@ -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,47 +1640,66 @@ 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
return minDistance < 0 ? 0 : minDistance;
}
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;
}

View File

@ -26,10 +26,25 @@
#include <geometry/shape_rect.h>
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;
}

View File

@ -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

View File

@ -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;

View File

@ -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" );

View File

@ -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,

View File

@ -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 <class_board.h>
#include <class_track.h>
#include <geometry/seg.h>
#include <drc/drc_test_provider_clearance_base.h>
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<SHAPE_POLY_SET*>( &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;
}

View File

@ -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;

View File

@ -216,7 +216,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem )
aItem, track, layer );
int minClearance = constraint.GetValue().Min();
int actual = INT_MAX;
wxPoint pos;
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<DRC_ITEM> 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 );
}
}
@ -262,10 +260,10 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem )
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<DRC_ITEM> 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 )
@ -362,12 +361,13 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I
aRefSeg, pad, aLayer );
int minClearance = constraint.GetValue().Min();
int actual;
VECTOR2I pos;
accountCheck( constraint );
const std::shared_ptr<SHAPE>& padShape = pad->GetEffectiveShape();
if( padShape->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual ) )
if( padShape->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> 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 );
@ -442,9 +443,8 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I
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<DRC_ITEM> 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;
@ -494,11 +494,14 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doTrackDrc( TRACK* aRefSeg, PCB_LAYER_I
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<DRC_ITEM> 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 );
}
}
}
@ -639,12 +642,14 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::doPadToPadsDrc( int aRefPadIdx,
int minClearance = constraint.GetValue().Min();
int clearanceAllowed = minClearance - bds.GetDRCEpsilon();
int actual;
VECTOR2I pos;
accountCheck( constraint );
std::shared_ptr<SHAPE> refPadShape = refPad->GetEffectiveShape();
std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape();
if( refPadShape->Collide( pad->GetEffectiveShape().get(), clearanceAllowed, &actual ) )
if( refPadShape->Collide( padShape.get(), clearanceAllowed, &actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> 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;
}
}

View File

@ -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;
VECTOR2I pos;
accountCheck( constraint );
if( refShape->Collide( shape.get(), minClearance, &actual ) )
if( refShape->Collide( shape.get(), minClearance, &actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> 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 );
}
}
}

View File

@ -119,44 +119,40 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_PAD::Run()
return true;
};
auto checkClearance = [&]( const DRC_RTREE::LAYER_PAIR& aLayers,
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 );
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 actual;
VECTOR2I pos;
accountCheck( constraint );
int actual;
if( ! aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual ) )
if( !aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual, &pos ) )
return true;
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SILK_OVER_PAD );
wxString msg;
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 );
};
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 },
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 );

View File

@ -24,12 +24,9 @@
#include <common.h>
#include <class_board.h>
#include <class_drawsegment.h>
#include <class_pad.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/polygon_test_point_inside.h>
#include <geometry/seg.h>
#include <geometry/shape_poly_set.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h>
@ -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,20 +106,22 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_SILK::Run()
return true;
};
auto checkClearance = [&]( const DRC_RTREE::LAYER_PAIR& aLayers,
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 );
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;
accountCheck( constraint );
int actual;
// only check for silkscreen collisions belonging to different modules or overlapping texts
// only check for silkscreen collisions belonging to different modules or
// overlapping texts
KICAD_T typeRef = aRefItem->parent->Type();
KICAD_T typeTest = aTestItem->parent->Type();
@ -144,7 +139,6 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_SILK::Run()
parentModTest = static_cast<MODULE*> ( aTestItem->parent->GetParent() );
}
// silkscreen drawings within the same module (or globally on the board)
// don't report clearance errors. Everything else does.
@ -160,7 +154,7 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_SILK::Run()
return true;
}
if( ! aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual ) )
if( ! aRefItem->shape->Collide( aTestItem->shape, minClearance, &actual, &pos ) )
return true;
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SILK_CLEARANCE );
@ -175,14 +169,12 @@ bool test::DRC_TEST_PROVIDER_SILK_TO_SILK::Run()
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_CLEARANCE );
};
int numSilk =
forEachGeometryItem( { PCB_LINE_T, PCB_MODULE_EDGE_T, PCB_TEXT_T, PCB_MODULE_TEXT_T },
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<DRC_CONSTRAINT_TYPE_T> test::DRC_TEST_PROVIDER_SILK_TO_SILK::GetConstraintTypes() const
std::set<DRC_CONSTRAINT_TYPE_T> DRC_TEST_PROVIDER_SILK_TO_SILK::GetConstraintTypes() const
{
return { DRC_CONSTRAINT_TYPE_SILK_TO_SILK };
}
@ -209,5 +201,5 @@ std::set<DRC_CONSTRAINT_TYPE_T> test::DRC_TEST_PROVIDER_SILK_TO_SILK::GetConstra
namespace detail
{
static DRC_REGISTER_TEST_PROVIDER<test::DRC_TEST_PROVIDER_SILK_TO_SILK> dummy;
static DRC_REGISTER_TEST_PROVIDER<DRC_TEST_PROVIDER_SILK_TO_SILK> dummy;
}

View File

@ -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