Return individual custom pad shapes instead of a SHAPE_POLY_SET.

Also implements an optional pointer to return the actual distance
from all the SHAPE collision routines.

Fixes https://gitlab.com/kicad/code/kicad/issues/4774
This commit is contained in:
Jeff Young 2020-07-02 17:06:09 +01:00
parent af8f05d570
commit 441dfa30f0
26 changed files with 728 additions and 711 deletions

View File

@ -193,7 +193,7 @@ public:
return Intersect( aSeg, false, true ); 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; ecoord SquaredDistance( const SEG& aSeg ) const;
@ -341,8 +341,6 @@ public:
bool Contains( const VECTOR2I& aP ) const; bool Contains( const VECTOR2I& aP ) const;
bool PointCloserThan( const VECTOR2I& aP, int aDist ) const;
void Reverse() void Reverse()
{ {
std::swap( A, B ); std::swap( A, B );

View File

@ -118,9 +118,11 @@ public:
* *
* Checks if the boundary of shape (this) lies closer to the point aP than aClearance, * Checks if the boundary of shape (this) lies closer to the point aP than aClearance,
* indicating a collision. * 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. * @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 ); return Collide( SEG( aP, aP ), aClearance );
} }
@ -133,19 +135,23 @@ public:
* @param aShape shape to check collision against * @param aShape shape to check collision against
* @param aClearance minimum clearance * @param aClearance minimum clearance
* @param aMTV minimum translation vector * @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. * @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, VECTOR2I* aMTV ) const;
virtual bool Collide( const SHAPE* aShape, int aClearance = 0 ) const; virtual bool Collide( const SHAPE* aShape, int aClearance = 0, int* aActual = nullptr ) const;
/** /**
* Function Collide() * Function Collide()
* *
* Checks if the boundary of shape (this) lies closer to the segment aSeg than aClearance, * Checks if the boundary of shape (this) lies closer to the segment aSeg than aClearance,
* indicating a collision. * 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. * @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() * Function BBox()
@ -169,7 +175,14 @@ public:
return BBox( 0 ).Centre(); // if nothing better is available.... 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; virtual bool IsSolid() const = 0;
@ -182,7 +195,7 @@ protected:
SHAPE_TYPE m_type; SHAPE_TYPE m_type;
}; };
bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, bool CollideShapes( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
bool aNeedMTV, VECTOR2I& aMTV ); VECTOR2I* aMTV );
#endif // __SHAPE_H #endif // __SHAPE_H

View File

@ -76,8 +76,8 @@ public:
const BOX2I BBox( int aClearance = 0 ) const override; const BOX2I BBox( int aClearance = 0 ) const override;
bool Collide( const SEG& aSeg, 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 ) const override; bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override;
void SetWidth( int aWidth ) void SetWidth( int aWidth )
{ {
@ -102,7 +102,7 @@ public:
* @param aCenter is the rotation center * @param aCenter is the rotation center
* @param aAngle rotation angle in radians * @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 } ); void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aVector = { 0, 0 } );

View File

@ -63,11 +63,20 @@ public:
return BOX2I( m_center - rc, rc * 2 ); 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 ) void SetRadius( int aRadius )
@ -95,6 +104,11 @@ public:
m_center += aVector; m_center += aVector;
} }
void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override
{
// That was easy....
}
bool IsSolid() const override bool IsSolid() const override
{ {
return true; return true;

View File

@ -386,9 +386,11 @@ public:
* Checks if point aP lies closer to us than aClearance. * Checks if point aP lies closer to us than aClearance.
* @param aP the point to check for collisions with * @param aP the point to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision. * @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 * @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() * Function Collide()
@ -396,9 +398,11 @@ public:
* Checks if segment aSeg lies closer to us than aClearance. * Checks if segment aSeg lies closer to us than aClearance.
* @param aSeg the segment to check for collisions with * @param aSeg the segment to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision. * @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 * @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() * Function Distance()
@ -408,6 +412,7 @@ public:
* @return minimum distance. * @return minimum distance.
*/ */
int Distance( const VECTOR2I& aP, bool aOutlineOnly = false ) const; int Distance( const VECTOR2I& aP, bool aOutlineOnly = false ) const;
SEG::ecoord SquaredDistance( const VECTOR2I& aP, bool aOutlineOnly = false ) const;
/** /**
* Function Reverse() * Function Reverse()
@ -747,7 +752,7 @@ public:
* @param aCenter is the rotation center * @param aCenter is the rotation center
* @param aAngle rotation angle in radians * @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 bool IsSolid() const override
{ {

View File

@ -975,7 +975,7 @@ class SHAPE_POLY_SET : public SHAPE
* @param aCenter is the rotation center * @param aCenter is the rotation center
* @param aAngle rotation angle in radians * @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() /// @copydoc SHAPE::IsSolid()
bool IsSolid() const override bool IsSolid() const override
@ -1003,9 +1003,11 @@ class SHAPE_POLY_SET : public SHAPE
* will be tested. * will be tested.
* @param aClearance is the security distance; if the point lies closer to the polygon * @param aClearance is the security distance; if the point lies closer to the polygon
* than aClearance distance, then there is a collision. * 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. * @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 * Function Collide
@ -1016,10 +1018,12 @@ class SHAPE_POLY_SET : public SHAPE
* will be tested. * will be tested.
* @param aClearance is the security distance; if the segment passes closer to the polygon * @param aClearance is the security distance; if the segment passes closer to the polygon
* than aClearance distance, then there is a collision. * 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; * @return bool - true if the segment aSeg collides with the polygon;
* false in any other case. * 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 * Function CollideVertex

View File

@ -91,9 +91,7 @@ public:
} }
/// @copydoc SHAPE::Collide() /// @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;
bool DoCollide( const SEG& aSeg, int aClearance, int* aActualDist ) const;
/** /**
* Function GetPosition() * Function GetPosition()
@ -140,6 +138,16 @@ public:
m_p0 += aVector; 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 bool IsSolid() const override
{ {
return true; return true;

View File

@ -54,14 +54,36 @@ public:
return BOX2I( m_seg.A, m_seg.B - m_seg.A ).Inflate( aClearance + ( m_width + 1 ) / 2 ); 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 ) void SetSeg( const SEG& aSeg )
@ -89,6 +111,18 @@ public:
return true; 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 void Move( const VECTOR2I& aVector ) override
{ {
m_seg.A += aVector; m_seg.A += aVector;

View File

@ -159,9 +159,14 @@ public:
} }
/// @copydoc SHAPE::Collide() /// @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 void Move( const VECTOR2I& aVector ) override

View File

@ -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 SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const
{ {
// fixme: rather inefficient.... // 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 // check for intersection
// fixme: move to a method // fixme: move to a method
if( ccw( A, aSeg.A, aSeg.B ) != ccw( B, aSeg.A, aSeg.B ) && if( ccw( A, aSeg.A, aSeg.B ) != ccw( B, aSeg.A, aSeg.B ) &&
ccw( A, B, aSeg.A ) != ccw( A, B, aSeg.B ) ) ccw( A, B, aSeg.A ) != ccw( A, B, aSeg.B ) )
{
if( aActual )
*aActual = 0;
return true; return true;
}
#define CHK( _seg, _pt ) \ ecoord dist_sq = VECTOR2I::ECOORD_MAX;
if( (_seg).PointCloserThan( _pt, aClearance ) ) return true;
CHK( *this, aSeg.A ); dist_sq = std::min( dist_sq, SquaredDistance( aSeg.A ) );
CHK( *this, aSeg.B ); dist_sq = std::min( dist_sq, SquaredDistance( aSeg.B ) );
CHK( aSeg, A ); dist_sq = std::min( dist_sq, aSeg.SquaredDistance( A ) );
CHK( aSeg, B ); dist_sq = std::min( dist_sq, aSeg.SquaredDistance( B ) );
#undef CHK
if( dist_sq < (ecoord) aClearance * aClearance )
{
if( aActual )
*aActual = sqrt( dist_sq );
return true;
}
return false; return false;
} }
@ -199,5 +159,5 @@ bool SEG::Collide( const SEG& aSeg, int aClearance ) const
bool SEG::Contains( const VECTOR2I& aP ) const bool SEG::Contains( const VECTOR2I& aP ) const
{ {
return PointCloserThan( aP, 1 ); return SquaredDistance( aP ) < 1; // 1 * 1 to be pedantic
} }

View File

@ -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; int minDist = aClearance + m_width / 2;
auto center = GetCenter(); VECTOR2I center = GetCenter();
auto centerDist = aSeg.Distance( center ); ecoord dist_sq = VECTOR2I::ECOORD_MAX;
auto p1 = GetP1();
if( centerDist < minDist ) VECTOR2I ab = ( aSeg.B - aSeg.A );
return true; VECTOR2I ac = ( center - aSeg.A );
auto ab = (aSeg.B - aSeg.A );
auto ac = ( center - aSeg.A );
auto lenAbSq = ab.SquaredEuclideanNorm();
auto lambda = (double) ac.Dot( ab ) / (double) lenAbSq;
ecoord lenAbSq = ab.SquaredEuclideanNorm();
double lambda = (double) ac.Dot( ab ) / (double) lenAbSq;
if( lambda >= 0.0 && lambda <= 1.0 ) 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.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); p.y = (double) aSeg.A.y * lambda + (double) aSeg.B.y * (1.0 - lambda);
auto p0pdist = ( m_start - p ).EuclideanNorm(); dist_sq = std::min( dist_sq, ( m_start - p ).SquaredEuclideanNorm() );
dist_sq = std::min( dist_sq, ( m_end - p ).SquaredEuclideanNorm() );
if( p0pdist < minDist )
return true;
auto p1pdist = ( p1 - p ).EuclideanNorm();
if( p1pdist < minDist )
return true;
} }
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; return true;
}
auto p1dist = aSeg.Distance( p1 ); return false;
if( p1dist > minDist )
return false;
return true;
} }
@ -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; int minDist = aClearance + m_width / 2;
auto bbox = BBox( minDist ); auto bbox = BBox( minDist );
@ -184,9 +171,22 @@ bool SHAPE_ARC::Collide( const VECTOR2I& aP, int aClearance ) const
if( !bbox.Contains( aP ) ) if( !bbox.Contains( aP ) )
return false; 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;
} }

View File

@ -26,7 +26,6 @@
#include <assert.h> // for assert #include <assert.h> // for assert
#include <cmath> #include <cmath>
#include <limits.h> // for INT_MAX #include <limits.h> // for INT_MAX
#include <stdlib.h> // for abs
#include <geometry/seg.h> // for SEG #include <geometry/seg.h> // for SEG
#include <geometry/shape.h> #include <geometry/shape.h>
@ -36,13 +35,12 @@
#include <geometry/shape_rect.h> #include <geometry/shape_rect.h>
#include <geometry/shape_segment.h> #include <geometry/shape_segment.h>
#include <geometry/shape_simple.h> #include <geometry/shape_simple.h>
#include <math/box2.h> // for BOX2I
#include <math/vector2d.h> #include <math/vector2d.h>
typedef VECTOR2I::extended_type ecoord; typedef VECTOR2I::extended_type ecoord;
static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_CIRCLE& aB, int aClearance, 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 = aClearance + aA.GetRadius() + aB.GetRadius();
ecoord min_dist_sq = min_dist * min_dist; 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 ) if( dist_sq >= min_dist_sq )
return false; return false;
if( aNeedMTV ) if( aActual )
aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error *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; return true;
} }
static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_CIRCLE& aB, int aClearance, 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 c = aB.GetCenter();
const VECTOR2I p0 = aA.GetPosition(); const VECTOR2I p0 = aA.GetPosition();
const VECTOR2I size = aA.GetSize(); const VECTOR2I size = aA.GetSize();
const int r = aB.GetRadius(); const int r = aB.GetRadius();
const int min_dist = aClearance + r; const int min_dist = aClearance + r;
const ecoord min_dist_sq = (ecoord) min_dist * min_dist;
const VECTOR2I vts[] = 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 ) VECTOR2I( p0.x, p0.y )
}; };
int nearest_seg_dist = INT_MAX; ecoord nearest_side_dist_sq = VECTOR2I::ECOORD_MAX;
VECTOR2I nearest; VECTOR2I nearest;
bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x ) bool inside = c.x >= p0.x && c.x <= ( p0.x + size.x )
&& c.y >= p0.y && c.y <= ( p0.y + size.y ); && c.y >= p0.y && c.y <= ( p0.y + size.y );
if( inside && !aMTV )
{
if( aActual )
*aActual = 0;
if( !aNeedMTV && inside )
return true; return true;
}
for( int i = 0; i < 4; i++ ) 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(( side_dist_sq < min_dist_sq ) && !aMTV && !aActual )
if( ( d < min_dist ) && !aNeedMTV )
return true; return true;
if( d < nearest_seg_dist ) if( side_dist_sq < nearest_side_dist_sq )
{ {
nearest = pn; 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; return false;
VECTOR2I delta = c - nearest; VECTOR2I delta = c - nearest;
if( !aNeedMTV ) if( aActual )
return true; *aActual = std::max( 0, (int) sqrt( nearest_side_dist_sq ) - r );
if( aMTV )
if( inside ) {
aMTV = -delta.Resize( abs( min_dist + 1 + nearest_seg_dist ) + 1 ); if( inside )
else *aMTV = -delta.Resize( abs( min_dist + 1 + sqrt( nearest_side_dist_sq ) ) + 1 );
aMTV = delta.Resize( abs( min_dist + 1 - nearest_seg_dist ) + 1 ); else
*aMTV = delta.Resize( abs( min_dist + 1 - sqrt( nearest_side_dist_sq ) ) + 1 );
}
return true; 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, 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; bool found = false;
for( int s = 0; s < aB.SegmentCount(); s++ ) 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; found = true;
break; break;
} }
} }
if( !aNeedMTV || !found ) if( !found )
return found; return false;
SHAPE_CIRCLE cmoved( aA ); if( aMTV )
VECTOR2I f_total( 0, 0 );
for( int s = 0; s < aB.SegmentCount(); s++ )
{ {
VECTOR2I f = pushoutForce( cmoved, aB.CSegment( s ), aClearance ); SHAPE_CIRCLE cmoved( aA );
cmoved.SetCenter( cmoved.GetCenter() + f ); VECTOR2I f_total( 0, 0 );
f_total += f;
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 true;
return found;
} }
static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SIMPLE& aB, int aClearance, static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SIMPLE& aB, int aClearance,
bool aNeedMTV, VECTOR2I& aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
bool found; int min_dist = aClearance + aA.GetRadius();
const SHAPE_LINE_CHAIN& lc( aB.Vertices() ); 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 ) if( aActual )
return found; *aActual = std::max( 0, (int) sqrt( dist_sq ) - aA.GetRadius() );
SHAPE_CIRCLE cmoved( aA ); if( aMTV )
VECTOR2I f_total( 0, 0 );
for( int s = 0; s < lc.SegmentCount(); s++ )
{ {
VECTOR2I f = pushoutForce( cmoved, lc.CSegment( s ), aClearance ); SHAPE_CIRCLE cmoved( aA );
cmoved.SetCenter( cmoved.GetCenter() + f ); VECTOR2I f_total( 0, 0 );
f_total += f;
}
aMTV = f_total; for( int s = 0; s < aB.Vertices().SegmentCount(); s++ )
return found; {
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, 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; return col;
} }
static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, 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++ ) 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 true;
}
return false; return false;
} }
static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_SIMPLE& aB, int aClearance, 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, 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, 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++ ) for( int s = 0; s < aB.SegmentCount(); s++ )
{ {
if( aA.Collide( aB.CSegment( s ), aClearance ) ) if( aA.Collide( aB.CSegment( s ), aClearance, &actual ) )
return true; {
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; 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, 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, 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 true;
}
return false; return false;
} }
static inline bool Collide( const SHAPE_SIMPLE& aA, const SHAPE_SEGMENT& aB, int aClearance, 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, 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, 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(); 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, 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(); const auto lc = aA.ConvertToPolyline();
bool rv = Collide( aB, lc, aClearance, aNeedMTV, aMTV ); bool rv = Collide( aB, lc, aClearance, aActual, aMTV );
if( rv && aNeedMTV ) if( rv && aMTV )
aMTV = -aMTV; *aMTV = - *aMTV ;
return rv; return rv;
} }
static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, 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(); 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, 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(); 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, 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(); 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, 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 lcA = aA.ConvertToPolyline();
const auto lcB = aB.ConvertToPolyline(); const auto lcB = aB.ConvertToPolyline();
return Collide( lcA, lcB, aClearance, aNeedMTV, aMTV ); return Collide( lcA, lcB, aClearance, aActual, aMTV );
} }
template<class ShapeAType, class ShapeBType> template<class T_a, class T_b>
inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) inline bool CollCase( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
VECTOR2I* aMTV )
{ {
return Collide (*static_cast<const ShapeAType*>( aA ), return Collide( *static_cast<const T_a*>( aA ), *static_cast<const T_b*>( aB ),
*static_cast<const ShapeBType*>( aB ), aClearance, aActual, aMTV);
aClearance, aNeedMTV, aMTV);
} }
template<class ShapeAType, class ShapeBType> template<class T_a, class T_b>
inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, bool aNeedMTV, VECTOR2I& aMTV ) inline bool CollCaseReversed ( const SHAPE* aA, const SHAPE* aB, int aClearance, int* aActual,
VECTOR2I* aMTV )
{ {
bool rv = Collide (*static_cast<const ShapeBType*>( aB ), bool rv = Collide( *static_cast<const T_b*>( aB ), *static_cast<const T_a*>( aA ),
*static_cast<const ShapeAType*>( aA ), aClearance, aActual, aMTV);
aClearance, aNeedMTV, aMTV);
if(rv && aNeedMTV) if( rv && aMTV)
aMTV = -aMTV; *aMTV = - *aMTV;
return rv; 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() ) switch( aA->Type() )
{ {
case SH_RECT:
switch( aB->Type() )
{
case SH_RECT: case SH_RECT:
switch( aB->Type() ) return CollCase<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aActual, aMTV );
{
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_RECT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_RECT, SHAPE_SIMPLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( aA, aB, aClearance, aNeedMTV, aMTV );
default:
break;
}
break;
case SH_CIRCLE: case SH_CIRCLE:
switch( aB->Type() ) return CollCase<SHAPE_RECT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
{
case SH_RECT:
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_RECT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_CIRCLE, SHAPE_SIMPLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( aA, aB, aClearance, aNeedMTV, aMTV );
default:
break;
}
break;
case SH_LINE_CHAIN: case SH_LINE_CHAIN:
switch( aB->Type() ) return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
{
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SIMPLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_LINE_CHAIN, SHAPE_ARC>( aA, aB, aClearance, aNeedMTV, aMTV );
default:
break;
}
break;
case SH_SEGMENT: case SH_SEGMENT:
switch( aB->Type() ) return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
{
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_CIRCLE:
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_SIMPLE, SHAPE_SEGMENT>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( aA, aB, aClearance, aNeedMTV, aMTV );
default:
break;
}
break;
case SH_SIMPLE: case SH_SIMPLE:
switch( aB->Type() ) return CollCase<SHAPE_RECT, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV );
{
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_SIMPLE>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_SIMPLE>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SIMPLE>( aB, aA, aClearance, aNeedMTV, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_SIMPLE, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_SIMPLE, SHAPE_SIMPLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_SIMPLE, SHAPE_ARC>( aA, aB, aClearance, aNeedMTV, aMTV );
default:
break;
}
break;
case SH_ARC: case SH_ARC:
switch( aB->Type() ) return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
{
case SH_RECT:
return CollCase<SHAPE_ARC, SHAPE_RECT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_ARC, SHAPE_CIRCLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_ARC, SHAPE_SIMPLE>( aA, aB, aClearance, aNeedMTV, aMTV );
case SH_ARC:
return CollCase<SHAPE_ARC, SHAPE_ARC>( aA, aB, aClearance, aNeedMTV, aMTV );
default:
break;
}
break;
default: default:
break; break;
}
break;
case SH_CIRCLE:
switch( aB->Type() )
{
case SH_RECT:
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_RECT>( aA, aB, aClearance, aActual, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_CIRCLE, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
default:
break;
}
break;
case SH_LINE_CHAIN:
switch( aB->Type() )
{
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN>( aB, aA, aClearance, aActual, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_LINE_CHAIN, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
default:
break;
}
break;
case SH_SEGMENT:
switch( aB->Type() )
{
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV );
case SH_CIRCLE:
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_SIMPLE, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
default:
break;
}
break;
case SH_SIMPLE:
switch( aB->Type() )
{
case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_SIMPLE>( aB, aA, aClearance, aActual, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_SIMPLE>( aB, aA, aClearance, aActual, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SIMPLE>( aB, aA, aClearance, aActual, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_SIMPLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_SIMPLE, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC:
return CollCaseReversed<SHAPE_SIMPLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
default:
break;
}
break;
case SH_ARC:
switch( aB->Type() )
{
case SH_RECT:
return CollCase<SHAPE_ARC, SHAPE_RECT>( aA, aB, aClearance, aActual, aMTV );
case SH_CIRCLE:
return CollCase<SHAPE_ARC, SHAPE_CIRCLE>( aA, aB, aClearance, aActual, aMTV );
case SH_LINE_CHAIN:
return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN>( aA, aB, aClearance, aActual, aMTV );
case SH_SEGMENT:
return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE:
return CollCase<SHAPE_ARC, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC:
return CollCase<SHAPE_ARC, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
default:
break;
}
break;
default:
break;
} }
bool unsupported_collision = true; 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, aActual, nullptr );
return CollideShapes( this, aShape, aClearance, false, dummy );
} }
bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance ) const bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual ) const
{
int dummy;
return DoCollide( aSeg, aClearance, &dummy );
}
bool SHAPE_RECT::DoCollide( const SEG& aSeg, int aClearance, int* aActualDist ) const
{ {
if( BBox( 0 ).Contains( aSeg.A ) || BBox( 0 ).Contains( aSeg.B ) ) if( BBox( 0 ).Contains( aSeg.A ) || BBox( 0 ).Contains( aSeg.B ) )
{ {
*aActualDist = 0; *aActual = 0;
return true; return true;
} }
VECTOR2I vts[] = { 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_p0.y + m_h ),
VECTOR2I( m_p0.x + m_w, 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_w, m_p0.y ),
VECTOR2I( m_p0.x, 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 ); SEG::ecoord dist_squared = s.SquaredDistance( aSeg );
for( int i = 1; i < 4; i++ ) 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 ) ); 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; return true;
} }

View File

@ -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::ecoord dist_sq = VECTOR2I::ECOORD_MAX;
SEG s( aP, aP ); SEG::ecoord clearance_sq = SEG::Square( aClearance );
return this->Collide( s, 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 ); SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX;
BOX2I::ecoord_type dist_sq = (BOX2I::ecoord_type) aClearance * aClearance; SEG::ecoord clearance_sq = SEG::Square( aClearance );
for( int i = 0; i < SegmentCount(); i++ ) for( int i = 0; i < SegmentCount(); i++ )
{ {
const SEG& s = CSegment( 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( dist_sq < clearance_sq )
{ {
if( s.Collide( aSeg, aClearance ) ) if( aActual )
return true; *aActual = sqrt( dist_sq );
}
return true;
} }
return false; 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 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 ) if( IsClosed() && PointInside( aP ) && !aOutlineOnly )
return 0; return 0;
for( int s = 0; s < SegmentCount(); s++ ) 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; return d;
} }

View File

@ -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 ); if( dist_sq < (ecoord) aClearance * aClearance )
// Inflate the polygon if necessary.
if( aClearance > 0 )
{ {
// fixme: the number of arc segments should not be hardcoded if( aActual )
polySet.Inflate( aClearance, 8 ); *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; return true;
for( CONST_SEGMENT_ITERATOR it = CIterateSegmentsWithHoles(); it; it++ )
{
const SEG polygonEdge = *it;
if( polygonEdge.Intersect( aSeg, true ) )
return true;
} }
return false; 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( dist_sq < (ecoord) aClearance * aClearance )
if( aClearance > 0 )
{ {
// fixme: the number of arc segments should not be hardcoded if( aActual )
polySet.Inflate( aClearance, 8 ); *aActual = sqrt( dist_sq );
return true;
} }
// There is a collision if and only if the point is inside of the polygon. return false;
return polySet.Contains( aP );
} }

View File

@ -155,7 +155,7 @@ bool TestSegmentHit( const wxPoint &aRefPoint, wxPoint aStart, wxPoint aEnd, int
return std::abs( delta.y ) <= aDist; return std::abs( delta.y ) <= aDist;
SEG segment( aStart, aEnd ); SEG segment( aStart, aEnd );
return segment.PointCloserThan( aRefPoint, aDist + 1 ); return segment.SquaredDistance( aRefPoint ) < SEG::Square( aDist + 1 );
} }

View File

@ -370,7 +370,7 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB
{ {
case S_CIRCLE: case S_CIRCLE:
if( width == 0 ) if( width == 0 )
TransformCircleToPolygon( aCornerBuffer, GetCenter(), GetRadius() + width / 2, aError ); TransformCircleToPolygon( aCornerBuffer, GetCenter(), GetRadius(), aError );
else else
TransformRingToPolygon( aCornerBuffer, GetCenter(), GetRadius(), aError, width ); TransformRingToPolygon( aCornerBuffer, GetCenter(), GetRadius(), aError, width );
break; break;

View File

@ -34,6 +34,9 @@
#include <class_module.h> #include <class_module.h>
#include <class_drawsegment.h> #include <class_drawsegment.h>
#include <base_units.h> #include <base_units.h>
#include <geometry/shape_simple.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_circle.h>
#include <settings/color_settings.h> #include <settings/color_settings.h>
#include <settings/settings_manager.h> #include <settings/settings_manager.h>
@ -590,16 +593,13 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
int radius = GetRadius(); int radius = GetRadius();
int dist = KiROUND( EuclideanNorm( aPosition - GetCenter() ) ); 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 ) if( dist <= radius + maxdist )
return true; return true;
} }
else // Ring hit-test
if( m_Width > 0 )
{ {
// Ring hit-test
if( abs( radius - dist ) <= maxdist ) if( abs( radius - dist ) <= maxdist )
return true; return true;
} }
@ -666,7 +666,7 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
std::vector<wxPoint> pts; std::vector<wxPoint> pts;
GetRectCorners( &pts ); GetRectCorners( &pts );
if( m_Width == 0 ) if( m_Width == 0 ) // Filled rect hit-test
{ {
SHAPE_POLY_SET poly; SHAPE_POLY_SET poly;
poly.NewOutline(); poly.NewOutline();
@ -677,8 +677,7 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
if( poly.Collide( VECTOR2I( aPosition ), maxdist ) ) if( poly.Collide( VECTOR2I( aPosition ), maxdist ) )
return true; return true;
} }
else // Open rect hit-test
if( m_Width > 0 )
{ {
if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist ) if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
|| TestSegmentHit( aPosition, pts[1], pts[2], maxdist ) || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
@ -828,7 +827,7 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
} }
break; break;
case S_CURVE: // not yet handled case S_CURVE:
if( aContained ) if( aContained )
{ {
return arect.Contains( bb ); return arect.Contains( bb );
@ -1015,6 +1014,112 @@ void DRAWSEGMENT::SetPolyPoints( const std::vector<wxPoint>& aPoints )
} }
std::vector<SHAPE*> DRAWSEGMENT::MakeEffectiveShapes()
{
std::vector<SHAPE*> 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<wxPoint> 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<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const const std::vector<wxPoint> DRAWSEGMENT::BuildPolyPointsList() const
{ {
std::vector<wxPoint> rv; std::vector<wxPoint> rv;

View File

@ -36,9 +36,9 @@
#include <math/util.h> // for KiROUND #include <math/util.h> // for KiROUND
#include <math_for_graphics.h> #include <math_for_graphics.h>
#include <trigo.h> #include <trigo.h>
#include <geometry/shape_poly_set.h> #include <geometry/shape_poly_set.h>
class LINE_READER; class LINE_READER;
class EDA_DRAW_FRAME; class EDA_DRAW_FRAME;
class MODULE; class MODULE;
@ -237,6 +237,11 @@ public:
void SetPolyPoints( const std::vector<wxPoint>& aPoints ); void SetPolyPoints( const std::vector<wxPoint>& aPoints );
/**
* Makes a set of SHAPE objects representing the DRAWSEGMENT. Caller owns the objects.
*/
std::vector<SHAPE*> MakeEffectiveShapes();
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override; void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
const EDA_RECT GetBoundingBox() const override; const EDA_RECT GetBoundingBox() const override;

View File

@ -327,32 +327,28 @@ void D_PAD::BuildEffectiveShapes() const
if( GetShape() == PAD_SHAPE_CUSTOM ) if( GetShape() == PAD_SHAPE_CUSTOM )
{ {
SHAPE_POLY_SET* poly = new SHAPE_POLY_SET(); for( const std::shared_ptr<DRAWSEGMENT>& primitive : m_editPrimitives )
MergePrimitivesAsPolygon( poly ); {
poly->Rotate( -DECIDEG2RAD( m_Orient ) ); for( SHAPE* shape : primitive->MakeEffectiveShapes() )
poly->Move( shapePos ); {
add( poly ); shape->Rotate( -DECIDEG2RAD( m_Orient ) );
shape->Move( shapePos );
add( shape );
}
}
} }
// Bounding box and radius // Bounding box and radius
// //
m_effectiveBoundingRadius = calcBoundingRadius(); m_effectiveBoundingRadius = calcBoundingRadius();
bool first_shape = true; m_effectiveBoundingBox = EDA_RECT(); // reset to prepare for merging
for( const std::shared_ptr<SHAPE>& shape : m_effectiveShapes ) for( const std::shared_ptr<SHAPE>& shape : m_effectiveShapes )
{ {
BOX2I r = shape->BBox(); BOX2I r = shape->BBox();
m_effectiveBoundingBox.Merge( EDA_RECT( (wxPoint) r.GetOrigin(),
if( first_shape ) wxSize( r.GetWidth(), r.GetHeight() ) ) );
{
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() ) ) );
} }
// Hole shape // Hole shape

View File

@ -7,7 +7,7 @@
* This program source code file is part of KiCad, a free EDA CAD application. * 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) 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -32,13 +32,10 @@
#include <fctsys.h> #include <fctsys.h>
#include <common.h> #include <common.h>
#include <confirm.h> #include <confirm.h>
#include <pcbnew.h>
#include <trigo.h> #include <trigo.h>
#include <macros.h>
#include <pcb_base_frame.h> #include <pcb_base_frame.h>
#include <base_units.h> #include <base_units.h>
#include <widgets/wx_grid.h> #include <widgets/wx_grid.h>
#include <class_board.h>
#include <class_module.h> #include <class_module.h>
#include <math/util.h> // for KiROUND #include <math/util.h> // for KiROUND
@ -224,8 +221,11 @@ DIALOG_PAD_PRIMITIVE_POLY_PROPS::DIALOG_PAD_PRIMITIVE_POLY_PROPS( wxWindow* aPar
m_shape( aShape ), m_shape( aShape ),
m_thickness( aFrame, m_thicknessLabel, m_thicknessCtrl, m_thicknessUnits, true ) m_thickness( aFrame, m_thicknessLabel, m_thicknessCtrl, m_thicknessUnits, true )
{ {
for( const VECTOR2I& pt : m_shape->GetPolyShape().Outline( 0 ).CPoints() ) if( !m_shape->GetPolyShape().IsEmpty() )
m_currPoints.emplace_back( pt ); {
for( const VECTOR2I& pt : m_shape->GetPolyShape().Outline( 0 ).CPoints() )
m_currPoints.emplace_back( pt );
}
m_addButton->SetBitmap( KiBitmap( small_plus_xpm ) ); m_addButton->SetBitmap( KiBitmap( small_plus_xpm ) );
m_deleteButton->SetBitmap( KiBitmap( trash_xpm ) ); m_deleteButton->SetBitmap( KiBitmap( trash_xpm ) );

View File

@ -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() ) ); 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 ) if( center2center - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius() >= aMinClearance )
return true; return true;
// JEY TODO: int actual = INT_MAX;
// 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;
for( const std::shared_ptr<SHAPE>& aShape : aRefPad->GetEffectiveShapes() ) for( const std::shared_ptr<SHAPE>& aShape : aRefPad->GetEffectiveShapes() )
{ {
for( const std::shared_ptr<SHAPE>& bShape : aPad->GetEffectiveShapes() ) for( const std::shared_ptr<SHAPE>& bShape : aPad->GetEffectiveShapes() )
{ {
if( aShape->Collide( bShape.get(), aMinClearance, mtv ) ) int this_dist;
{
shapes_collide = true;
if( mtv.SquaredEuclideanNorm() > maxMtv.SquaredEuclideanNorm() ) if( aShape->Collide( bShape.get(), aMinClearance, &this_dist ) )
maxMtv = mtv; actual = std::min( actual, this_dist );
}
} }
} }
if( shapes_collide ) if( actual < INT_MAX )
{ {
// returns the actual clearance (clearance < aMinClearance) for diags: // returns the actual clearance (clearance < aMinClearance) for diags:
if( aActualDist ) if( aActual )
*aActualDist = std::max( 0, aMinClearance - maxMtv.EuclideanNorm() ); *aActual = std::max( 0, actual );
return false; 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() ); SHAPE_RECT padShape( padBBox.GetPosition(), padBBox.GetWidth(), padBBox.GetHeight() );
int actual; int actual;
if( padShape.DoCollide( refSeg, minClearance + widths, &actual ) ) if( padShape.Collide( refSeg, minClearance + widths, &actual ) )
{ {
*aActualDist = std::max( 0, actual - widths ); *aActualDist = std::max( 0, actual - widths );
return false; return false;

View File

@ -29,18 +29,12 @@
#include <fctsys.h> #include <fctsys.h>
#include <trigo.h> #include <trigo.h>
#include <pcbnew.h> #include <pcbnew.h>
#include <bezier_curves.h>
#include <class_board.h> #include <class_board.h>
#include <class_board_item.h> #include <class_board_item.h>
#include <class_drawsegment.h> #include <class_drawsegment.h>
#include <class_edge_mod.h>
#include <class_pad.h> #include <class_pad.h>
#include <convert_basic_shapes_to_polygon.h> #include <convert_basic_shapes_to_polygon.h>
#include <geometry/convex_hull.h>
#include <geometry/geometry_utils.h>
#include <geometry/shape_rect.h> #include <geometry/shape_rect.h>
@ -184,107 +178,7 @@ void D_PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aErro
SHAPE_POLY_SET polyset; SHAPE_POLY_SET polyset;
for( const std::shared_ptr<DRAWSEGMENT>& primitive : m_editPrimitives ) for( const std::shared_ptr<DRAWSEGMENT>& primitive : m_editPrimitives )
{ primitive->TransformShapeWithClearanceToPolygon( polyset, 0, aError );
int lineWidth = primitive->GetWidth();
switch( primitive->GetShape() )
{
case S_CURVE:
{
std::vector<wxPoint> 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;
}
}
polyset.Simplify( SHAPE_POLY_SET::PM_FAST ); polyset.Simplify( SHAPE_POLY_SET::PM_FAST );

View File

@ -39,7 +39,7 @@ bool ITEM::collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VEC
return false; return false;
if( aNeedMTV ) if( aNeedMTV )
return Shape()->Collide( aOther->Shape(), aClearance, *aMTV ); return Shape()->Collide( aOther->Shape(), aClearance, aMTV );
else else
return Shape()->Collide( aOther->Shape(), aClearance ); return Shape()->Collide( aOther->Shape(), aClearance );
} }

View File

@ -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 ) bool PNS_KICAD_IFACE_BASE::syncGraphicalItem( PNS::NODE* aWorld, DRAWSEGMENT* aItem )
{ {
std::vector<SHAPE_SEGMENT*> segs;
if( aItem->GetLayer() != Edge_Cuts && !IsCopperLayer( aItem->GetLayer() ) ) if( aItem->GetLayer() != Edge_Cuts && !IsCopperLayer( aItem->GetLayer() ) )
return false; return false;
switch( aItem->GetShape() ) // TODO: where do we handle filled polygons on copper layers?
{ if( aItem->GetShape() == S_POLYGON && aItem->IsPolygonFilled() )
case S_ARC: return false;
{
SHAPE_ARC arc( aItem->GetCenter(), aItem->GetArcStart(), aItem->GetAngle() / 10.0 );
auto l = arc.ConvertToPolyline();
for( int i = 0; i < l.SegmentCount(); i++ ) for( SHAPE* shape : aItem->MakeEffectiveShapes() )
{
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<wxPoint> 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 )
{ {
std::unique_ptr< PNS::SOLID > solid( new PNS::SOLID ); 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->SetNet( -1 );
solid->SetParent( nullptr ); solid->SetParent( nullptr );
solid->SetShape( seg ); solid->SetShape( shape );
solid->SetRoutable( false ); solid->SetRoutable( false );
aWorld->Add( std::move( solid ) ); aWorld->Add( std::move( solid ) );

View File

@ -814,7 +814,7 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in
*/ */
SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia ) 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; LINE_PAIR_VEC draggedLines;
bool lineCollision = false; bool lineCollision = false;
bool viaCollision = false; bool viaCollision = false;
@ -835,9 +835,9 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia )
#endif #endif
currentLine = (LINE*) aCurrent; currentLine = (LINE*) aCurrent;
lineCollision = CollideShapes( aObstacleVia->Shape(), currentLine->Shape(), lineCollision = aObstacleVia->Shape()->Collide( currentLine->Shape(),
clearance + currentLine->Width() / 2 + PNS_HULL_MARGIN, clearance + currentLine->Width() / 2,
true, mtvLine ); &mtvLine );
if( currentLine->EndsWithVia() ) if( currentLine->EndsWithVia() )
{ {
@ -846,8 +846,8 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia )
if( currentNet != obstacleNet && currentNet >= 0 && obstacleNet >= 0 ) if( currentNet != obstacleNet && currentNet >= 0 && obstacleNet >= 0 )
{ {
viaCollision = CollideShapes( currentLine->Via().Shape(), aObstacleVia->Shape(), viaCollision = currentLine->Via().Shape()->Collide( aObstacleVia->Shape(),
clearance + PNS_HULL_MARGIN, true, mtvVia ); clearance, &mtvVia );
} }
// hole-to-hole is a mechanical constraint (broken drill bits), not an electrical // 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 ) ) else if( aCurrent->OfKind( ITEM::SOLID_T ) )
{ {
CollideShapes( aObstacleVia->Shape(), aCurrent->Shape(), aObstacleVia->Shape()->Collide( aCurrent->Shape(), clearance, &mtvSolid );
clearance + PNS_HULL_MARGIN, true, mtvSolid );
mtv = -mtvSolid; mtv = -mtvSolid;
rank = aCurrent->Rank() + 10000; rank = aCurrent->Rank() + 10000;
} }

View File

@ -34,12 +34,12 @@ bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForc
{ {
int iter = 0; int iter = 0;
VIA mv( *this ); VIA mv( *this );
VECTOR2I force, totalForce, force2; VECTOR2I force, totalForce;
while( iter < aMaxIterations ) while( iter < aMaxIterations )
{ {
NODE::OPT_OBSTACLE obs = aNode->CheckColliding( &mv, NODE::OPT_OBSTACLE obs = aNode->CheckColliding( &mv, aSolidsOnly ? ITEM::SOLID_T
aSolidsOnly ? ITEM::SOLID_T : ITEM::ANY_T ); : ITEM::ANY_T );
if( !obs ) if( !obs )
break; break;
@ -53,11 +53,10 @@ bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForc
mv.SetPos( mv.Pos() + l ); mv.SetPos( mv.Pos() + l );
} }
bool col = CollideShapes( obs->m_item->Shape(), mv.Shape(), clearance, true, force2 ); if( obs->m_item->Shape()->Collide( mv.Shape(), clearance, &force ) )
{
if( col ) { totalForce += force;
totalForce += force2; mv.SetPos( mv.Pos() + force );
mv.SetPos( mv.Pos() + force2 );
} }
iter++; iter++;