geometry: derive SHAPE_LINE_CHAIN, SHAPE_SIMPLE and SHAPE_POLY_SET::TRIANGULATED_POLYGON::TRI from a common base class allowing to simplify collision detection

This commit is contained in:
Tomasz Wlostowski 2020-09-05 01:08:18 +02:00
parent eb6e1c4f90
commit 74623b8c98
6 changed files with 279 additions and 178 deletions

View File

@ -30,6 +30,7 @@
#include <math/vector2d.h> #include <math/vector2d.h>
#include <math/box2.h> #include <math/box2.h>
class SHAPE_LINE_CHAIN;
/** /**
* Enum SHAPE_TYPE * Enum SHAPE_TYPE
@ -46,7 +47,8 @@ enum SHAPE_TYPE
SH_POLY_SET, ///> set of polygons (with holes, etc.) SH_POLY_SET, ///> set of polygons (with holes, etc.)
SH_COMPOUND, ///> compound shape, consisting of multiple simple shapes SH_COMPOUND, ///> compound shape, consisting of multiple simple shapes
SH_ARC, ///> circular arc SH_ARC, ///> circular arc
SH_NULL ///> empty shape (no shape...) SH_NULL, ///> empty shape (no shape...),
SH_POLY_SET_TRIANGLE ///> a single triangle belonging to a POLY_SET triangulation
}; };
static inline wxString SHAPE_TYPE_asString( SHAPE_TYPE a ) static inline wxString SHAPE_TYPE_asString( SHAPE_TYPE a )
@ -62,22 +64,15 @@ static inline wxString SHAPE_TYPE_asString( SHAPE_TYPE a )
case SH_COMPOUND: return "SH_COMPOUND"; case SH_COMPOUND: return "SH_COMPOUND";
case SH_ARC: return "SH_ARC"; case SH_ARC: return "SH_ARC";
case SH_NULL: return "SH_NULL"; case SH_NULL: return "SH_NULL";
case SH_POLY_SET_TRIANGLE: return "SH_POLY_SET_TRIANGLE";
} }
return wxEmptyString; // Just to quiet GCC. return wxEmptyString; // Just to quiet GCC.
} }
/** class SHAPE_BASE
* SHAPE
*
* Represents an abstract shape on 2D plane.
*/
class SHAPE
{ {
protected:
typedef VECTOR2I::extended_type ecoord;
public: public:
/** /**
* Constructor * Constructor
@ -85,11 +80,11 @@ public:
* Creates an empty shape of type aType * Creates an empty shape of type aType
*/ */
SHAPE( SHAPE_TYPE aType ) : m_type( aType ) SHAPE_BASE( SHAPE_TYPE aType ) : m_type( aType )
{} {}
// Destructor // Destructor
virtual ~SHAPE() virtual ~SHAPE_BASE()
{} {}
/** /**
@ -103,6 +98,45 @@ public:
return m_type; return m_type;
} }
virtual bool HasIndexableSubshapes() const
{
return false;
}
virtual size_t GetIndexableSubshapeCount() { return 0; }
virtual void GetIndexableSubshape( SHAPE_BASE& aSubshape ) const {};
protected:
///> type of our shape
SHAPE_TYPE m_type;
};
/**
* SHAPE
*
* Represents an abstract shape on 2D plane.
*/
class SHAPE : public SHAPE_BASE
{
protected:
typedef VECTOR2I::extended_type ecoord;
public:
/**
* Constructor
*
* Creates an empty shape of type aType
*/
SHAPE( SHAPE_TYPE aType ) : SHAPE_BASE( aType )
{}
// Destructor
virtual ~SHAPE()
{}
/** /**
* Function Clone() * Function Clone()
* *
@ -202,11 +236,82 @@ public:
virtual bool Parse( std::stringstream& aStream ); virtual bool Parse( std::stringstream& aStream );
virtual const std::string Format( ) const; virtual const std::string Format( ) const;
protected:
///> type of our shape
SHAPE_TYPE m_type;
}; };
class SHAPE_LINE_CHAIN_BASE : public SHAPE
{
public:
SHAPE_LINE_CHAIN_BASE( SHAPE_TYPE aType ) : SHAPE( aType )
{
}
// Destructor
virtual ~SHAPE_LINE_CHAIN_BASE()
{
}
/**
* Function Collide()
*
* Checks if point aP lies closer to us than aClearance.
* @param aP the point to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision.
* @param aActual an optional pointer to an int to store the actual distance in the event
* of a collision.
* @return true, when a collision has been found
*/
virtual bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override;
/**
* Function Collide()
*
* Checks if segment aSeg lies closer to us than aClearance.
* @param aSeg the segment to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision.
* @param aActual an optional pointer to an int to store the actual distance in the event
* of a collision.
* @return true, when a collision has been found
*/
virtual bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override;
SEG::ecoord SquaredDistance( const VECTOR2I& aP, bool aOutlineOnly = false ) const;
/**
* Function PointInside()
*
* Checks if point aP lies inside a polygon (any type) defined by the line chain.
* For closed shapes only.
* @param aPt point to check
* @param aUseBBoxCache gives better peformance if the bounding box caches have been
* generated.
* @return true if the point is inside the shape (edge is not treated as being inside).
*/
bool PointInside( const VECTOR2I& aPt, int aAccuracy = 0, bool aUseBBoxCache = false ) const;
/**
* Function PointOnEdge()
*
* Checks if point aP lies on an edge or vertex of the line chain.
* @param aP point to check
* @return true if the point lies on the edge.
*/
bool PointOnEdge( const VECTOR2I& aP, int aAccuracy = 0 ) const;
/**
* Function EdgeContainingPoint()
*
* Checks if point aP lies on an edge or vertex of the line chain.
* @param aP point to check
* @return index of the first edge containing the point, otherwise negative
*/
int EdgeContainingPoint( const VECTOR2I& aP, int aAccuracy = 0 ) const;
virtual const VECTOR2I GetPoint( int aIndex ) const = 0;
virtual const SEG GetSegment( int aIndex ) const = 0;
virtual size_t GetPointCount() const = 0;
virtual size_t GetSegmentCount() const = 0;
virtual bool IsClosed() const = 0;
};
#endif // __SHAPE_H #endif // __SHAPE_H

View File

@ -43,7 +43,7 @@
* *
* SHAPE_LINE_CHAIN class shall not be used for polygons! * SHAPE_LINE_CHAIN class shall not be used for polygons!
*/ */
class SHAPE_LINE_CHAIN : public SHAPE class SHAPE_LINE_CHAIN : public SHAPE_LINE_CHAIN_BASE
{ {
private: private:
typedef std::vector<VECTOR2I>::iterator point_iter; typedef std::vector<VECTOR2I>::iterator point_iter;
@ -99,7 +99,7 @@ public:
* Constructor * Constructor
* Initializes an empty line chain. * Initializes an empty line chain.
*/ */
SHAPE_LINE_CHAIN() : SHAPE( SH_LINE_CHAIN ), m_closed( false ), m_width( 0 ) SHAPE_LINE_CHAIN() : SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), m_closed( false ), m_width( 0 )
{} {}
/** /**
@ -107,7 +107,7 @@ public:
*/ */
SHAPE_LINE_CHAIN( const SHAPE_LINE_CHAIN& aShape ) SHAPE_LINE_CHAIN( const SHAPE_LINE_CHAIN& aShape )
: SHAPE( SH_LINE_CHAIN ), : SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ),
m_points( aShape.m_points ), m_points( aShape.m_points ),
m_shapes( aShape.m_shapes ), m_shapes( aShape.m_shapes ),
m_arcs( aShape.m_arcs ), m_arcs( aShape.m_arcs ),
@ -119,7 +119,7 @@ public:
SHAPE_LINE_CHAIN( const std::vector<int>& aV); SHAPE_LINE_CHAIN( const std::vector<int>& aV);
SHAPE_LINE_CHAIN( const std::vector<wxPoint>& aV, bool aClosed = false ) SHAPE_LINE_CHAIN( const std::vector<wxPoint>& aV, bool aClosed = false )
: SHAPE( SH_LINE_CHAIN ), m_closed( aClosed ), m_width( 0 ) : SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), m_closed( aClosed ), m_width( 0 )
{ {
m_points.reserve( aV.size() ); m_points.reserve( aV.size() );
@ -130,14 +130,14 @@ public:
} }
SHAPE_LINE_CHAIN( const std::vector<VECTOR2I>& aV, bool aClosed = false ) SHAPE_LINE_CHAIN( const std::vector<VECTOR2I>& aV, bool aClosed = false )
: SHAPE( SH_LINE_CHAIN ), m_closed( aClosed ), m_width( 0 ) : SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), m_closed( aClosed ), m_width( 0 )
{ {
m_points = aV; m_points = aV;
m_shapes = std::vector<ssize_t>( aV.size(), ssize_t( SHAPE_IS_PT ) ); m_shapes = std::vector<ssize_t>( aV.size(), ssize_t( SHAPE_IS_PT ) );
} }
SHAPE_LINE_CHAIN( const SHAPE_ARC& aArc, bool aClosed = false ) SHAPE_LINE_CHAIN( const SHAPE_ARC& aArc, bool aClosed = false )
: SHAPE( SH_LINE_CHAIN ), : SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ),
m_closed( aClosed ), m_closed( aClosed ),
m_width( 0 ) m_width( 0 )
{ {
@ -147,7 +147,7 @@ public:
} }
SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) : SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath ) :
SHAPE( SH_LINE_CHAIN ), SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ),
m_closed( true ), m_closed( true ),
m_width( 0 ) m_width( 0 )
{ {
@ -194,7 +194,7 @@ public:
* *
* @return aClosed: true, when our line is closed. * @return aClosed: true, when our line is closed.
*/ */
bool IsClosed() const bool IsClosed() const override
{ {
return m_closed; return m_closed;
} }
@ -372,30 +372,6 @@ public:
return m_bbox; return m_bbox;
} }
/**
* Function Collide()
*
* Checks if point aP lies closer to us than aClearance.
* @param aP the point to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision.
* @param aActual an optional pointer to an int to store the actual distance in the event
* of a collision.
* @return true, when a collision has been found
*/
bool Collide( const VECTOR2I& aP, int aClearance = 0, int* aActual = nullptr ) const override;
/**
* Function Collide()
*
* Checks if segment aSeg lies closer to us than aClearance.
* @param aSeg the segment to check for collisions with
* @param aClearance minimum distance that does not qualify as a collision.
* @param aActual an optional pointer to an int to store the actual distance in the event
* of a collision.
* @return true, when a collision has been found
*/
bool Collide( const SEG& aSeg, int aClearance = 0, int* aActual = nullptr ) const override;
/** /**
* Function Distance() * Function Distance()
* *
@ -404,7 +380,6 @@ 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()
@ -600,37 +575,6 @@ public:
*/ */
int PathLength( const VECTOR2I& aP ) const; int PathLength( const VECTOR2I& aP ) const;
/**
* Function PointInside()
*
* Checks if point aP lies inside a polygon (any type) defined by the line chain.
* For closed shapes only.
* @param aPt point to check
* @param aUseBBoxCache gives better peformance if the bounding box caches have been
* generated.
* @return true if the point is inside the shape (edge is not treated as being inside).
*/
bool PointInside( const VECTOR2I& aPt, int aAccuracy = 0, bool aUseBBoxCache = false ) const;
/**
* Function PointOnEdge()
*
* Checks if point aP lies on an edge or vertex of the line chain.
* @param aP point to check
* @return true if the point lies on the edge.
*/
bool PointOnEdge( const VECTOR2I& aP, int aAccuracy = 0 ) const;
/**
* Function EdgeContainingPoint()
*
* Checks if point aP lies on an edge or vertex of the line chain.
* @param aP point to check
* @return index of the first edge containing the point, otherwise negative
*/
int EdgeContainingPoint( const VECTOR2I& aP, int aAccuracy = 0 ) const;
/** /**
* Function CheckClearance() * Function CheckClearance()
* *
@ -775,6 +719,11 @@ public:
return aSegment < m_shapes.size() && m_shapes[aSegment] != SHAPE_IS_PT; return aSegment < m_shapes.size() && m_shapes[aSegment] != SHAPE_IS_PT;
} }
virtual const VECTOR2I GetPoint( int aIndex ) const override { return CPoint(aIndex); }
virtual const SEG GetSegment( int aIndex ) const override { return CSegment(aIndex); }
virtual size_t GetPointCount() const override { return PointCount(); }
virtual size_t GetSegmentCount() const override { return SegmentCount(); }
private: private:
constexpr static ssize_t SHAPE_IS_PT = -1; constexpr static ssize_t SHAPE_IS_PT = -1;

View File

@ -71,13 +71,64 @@ class SHAPE_POLY_SET : public SHAPE
class TRIANGULATED_POLYGON class TRIANGULATED_POLYGON
{ {
public: public:
struct TRI struct TRI : public SHAPE_LINE_CHAIN_BASE
{ {
TRI( int _a = 0, int _b = 0, int _c = 0 ) : a( _a ), b( _b ), c( _c ) TRI( int _a = 0, int _b = 0, int _c = 0, TRIANGULATED_POLYGON* aParent = nullptr ) :
SHAPE_LINE_CHAIN_BASE( SH_POLY_SET_TRIANGLE ),
a( _a ), b( _b ), c( _c ), parent( aParent )
{ {
} }
virtual void Rotate( double aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override {};
virtual void Move( const VECTOR2I& aVector ) override {};
virtual bool IsSolid() const override { return true; }
virtual bool IsClosed() const override { return true; }
virtual const BOX2I BBox( int aClearance = 0 ) const override
{
BOX2I bbox( parent->m_vertices[a] );
bbox.Merge( parent->m_vertices[b] );
bbox.Merge( parent->m_vertices[c] );
if( aClearance != 0 )
bbox.Inflate( aClearance );
return bbox;
}
virtual const VECTOR2I GetPoint( int aIndex ) const override
{
switch(aIndex)
{
case 0: return parent->m_vertices[a];
case 1: return parent->m_vertices[b];
case 2: return parent->m_vertices[c];
default: assert(false);
}
return VECTOR2I(0, 0);
}
virtual const SEG GetSegment( int aIndex ) const override
{
switch(aIndex)
{
case 0: return SEG( parent->m_vertices[a], parent->m_vertices[b] );
case 1: return SEG( parent->m_vertices[b], parent->m_vertices[c] );
case 2: return SEG( parent->m_vertices[c], parent->m_vertices[a] );
default: assert(false);
}
return SEG();
}
virtual size_t GetPointCount() const override { return 3; }
virtual size_t GetSegmentCount() const override { return 3; }
int a, b, c; int a, b, c;
TRIANGULATED_POLYGON* parent;
}; };
void Clear() void Clear()
@ -101,7 +152,7 @@ class SHAPE_POLY_SET : public SHAPE
void AddTriangle( int a, int b, int c ) void AddTriangle( int a, int b, int c )
{ {
m_triangles.emplace_back( a, b, c ); m_triangles.emplace_back( a, b, c, this );
} }
void AddVertex( const VECTOR2I& aP ) void AddVertex( const VECTOR2I& aP )

View File

@ -39,7 +39,8 @@
* Internally the vertices are held in a SHAPE_LINE_CHAIN, please note that * Internally the vertices are held in a SHAPE_LINE_CHAIN, please note that
* there is a "virtual" line segment between the last and first vertex. * there is a "virtual" line segment between the last and first vertex.
*/ */
class SHAPE_SIMPLE : public SHAPE
class SHAPE_SIMPLE : public SHAPE_LINE_CHAIN_BASE
{ {
public: public:
/** /**
@ -47,20 +48,20 @@ public:
* Creates an empty polygon * Creates an empty polygon
*/ */
SHAPE_SIMPLE() : SHAPE_SIMPLE() :
SHAPE( SH_SIMPLE ) SHAPE_LINE_CHAIN_BASE( SH_SIMPLE )
{ {
m_points.SetClosed( true ); m_points.SetClosed( true );
} }
SHAPE_SIMPLE( const SHAPE_LINE_CHAIN& aPoly ) : SHAPE_SIMPLE( const SHAPE_LINE_CHAIN& aPoly ) :
SHAPE( SH_SIMPLE ), SHAPE_LINE_CHAIN_BASE( SH_SIMPLE ),
m_points( aPoly ) m_points( aPoly )
{ {
m_points.SetClosed( true ); m_points.SetClosed( true );
} }
SHAPE_SIMPLE( const SHAPE_SIMPLE& aOther ) : SHAPE_SIMPLE( const SHAPE_SIMPLE& aOther ) :
SHAPE( SH_SIMPLE ), m_points( aOther.m_points ) SHAPE_LINE_CHAIN_BASE( SH_SIMPLE ), m_points( aOther.m_points )
{} {}
SHAPE* Clone() const override SHAPE* Clone() const override
@ -179,6 +180,16 @@ public:
return true; return true;
} }
virtual const VECTOR2I GetPoint( int aIndex ) const override { return m_points.CPoint(aIndex); }
virtual const SEG GetSegment( int aIndex ) const override { return m_points.CSegment(aIndex); }
virtual size_t GetPointCount() const override { return m_points.PointCount(); }
virtual size_t GetSegmentCount() const override { return m_points.SegmentCount(); }
bool IsClosed() const override
{
return true;
}
private: private:
// vertices // vertices
SHAPE_LINE_CHAIN m_points; SHAPE_LINE_CHAIN m_points;

View File

@ -162,15 +162,15 @@ static VECTOR2I pushoutForce( const SHAPE_CIRCLE& aA, const SEG& aB, int aCleara
return f; return f;
} }
#if 0
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_BASE& aB, int aClearance,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
bool collided = false; bool collided = false;
for( int s = 0; s < aB.SegmentCount(); s++ ) for( int s = 0; s < aB.GetSegmentCount(); s++ )
{ {
if( aA.Collide( aB.CSegment( s ), aClearance, aActual ) ) if( aA.Collide( aB.GetSegment( s ), aClearance, aActual ) )
{ {
collided = true; collided = true;
break; break;
@ -185,9 +185,9 @@ static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN& aB,
SHAPE_CIRCLE cmoved( aA ); SHAPE_CIRCLE cmoved( aA );
VECTOR2I f_total( 0, 0 ); VECTOR2I f_total( 0, 0 );
for( int s = 0; s < aB.SegmentCount(); s++ ) for( int s = 0; s < aB.GetSegmentCount(); s++ )
{ {
VECTOR2I f = pushoutForce( cmoved, aB.CSegment( s ), aClearance ); VECTOR2I f = pushoutForce( cmoved, aB.GetSegment( s ), aClearance );
cmoved.SetCenter( cmoved.GetCenter() + f ); cmoved.SetCenter( cmoved.GetCenter() + f );
f_total += f; f_total += f;
} }
@ -197,13 +197,13 @@ static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN& aB,
return true; return true;
} }
#endif
static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SIMPLE& aB, int aClearance,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
int min_dist = aClearance + aA.GetRadius(); int min_dist = aClearance + aA.GetRadius();
ecoord dist_sq = aB.Vertices().SquaredDistance( aA.GetCenter() ); ecoord dist_sq = aB.SquaredDistance( aA.GetCenter() );
if( dist_sq > (ecoord) min_dist * min_dist ) if( dist_sq > (ecoord) min_dist * min_dist )
return false; return false;
@ -216,9 +216,9 @@ static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SIMPLE& aB, int
SHAPE_CIRCLE cmoved( aA ); SHAPE_CIRCLE cmoved( aA );
VECTOR2I f_total( 0, 0 ); VECTOR2I f_total( 0, 0 );
for( int s = 0; s < aB.Vertices().SegmentCount(); s++ ) for( int s = 0; s < aB.GetSegmentCount(); s++ )
{ {
VECTOR2I f = pushoutForce( cmoved, aB.Vertices().CSegment( s ), aClearance ); VECTOR2I f = pushoutForce( cmoved, aB.GetSegment( s ), aClearance );
cmoved.SetCenter( cmoved.GetCenter() + f ); cmoved.SetCenter( cmoved.GetCenter() + f );
f_total += f; f_total += f;
} }
@ -242,14 +242,15 @@ static inline bool Collide( const SHAPE_CIRCLE& aA, const SHAPE_SEGMENT& aSeg, i
} }
static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_LINE_CHAIN& aB, int aClearance, static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
// TODO: why doesn't this handle MTV? // TODO: why doesn't this handle MTV?
// TODO: worse, why this doesn't handle closed shapes?
for( int i = 0; i < aB.SegmentCount(); i++ ) for( int i = 0; i < aB.GetSegmentCount(); i++ )
{ {
if( aA.Collide( aB.CSegment( i ), aClearance, aActual ) ) if( aA.Collide( aB.GetSegment( i ), aClearance, aActual ) )
return true; return true;
} }
@ -257,29 +258,15 @@ static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_LINE_CHAIN&
} }
static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_SIMPLE& aB, int aClearance, static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_LINE_CHAIN_BASE& aB, int aClearance,
int* aActual, VECTOR2I* aMTV )
{
return Collide( aA, aB.Vertices(), aClearance, aActual, aMTV );
}
static inline bool Collide( const SHAPE_SIMPLE& aA, const SHAPE_SIMPLE& aB, int aClearance,
int* aActual, VECTOR2I* 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,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
int minActual = INT_MAX; int minActual = INT_MAX;
int actual; int actual;
for( int s = 0; s < aB.SegmentCount(); s++ ) for( int s = 0; s < aB.GetSegmentCount(); s++ )
{ {
if( aA.Collide( aB.CSegment( s ), aClearance, &actual ) ) if( aA.Collide( aB.GetSegment( s ), aClearance, &actual ) )
{ {
minActual = std::min( minActual, actual ); minActual = std::min( minActual, actual );
@ -298,13 +285,6 @@ static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_LINE_CHAIN& aB, in
} }
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, static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_SEGMENT& aSeg, int aClearance,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
@ -343,7 +323,7 @@ static inline bool Collide( const SHAPE_SEGMENT& aA, const SHAPE_SEGMENT& aB, in
} }
static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_SEGMENT& aB, int aClearance, static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT& aB, int aClearance,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
int actual; int actual;
@ -362,12 +342,6 @@ static inline bool Collide( const SHAPE_LINE_CHAIN& aA, const SHAPE_SEGMENT& aB,
} }
static inline bool Collide( const SHAPE_SIMPLE& aA, const SHAPE_SEGMENT& aB, int aClearance,
int* aActual, VECTOR2I* 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,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
@ -407,12 +381,12 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_SEGMENT& aB, int aC
return Collide( lc, aB, aClearance, aActual, 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_LINE_CHAIN_BASE& aB, int aClearance,
int* aActual, VECTOR2I* aMTV ) int* aActual, VECTOR2I* aMTV )
{ {
const auto lc = aA.ConvertToPolyline(); const auto lc = aA.ConvertToPolyline();
return Collide( lc, aB.Vertices(), aClearance, aActual, aMTV ); return Collide( lc, aB, 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,
@ -454,6 +428,7 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc
{ {
case SH_NULL: case SH_NULL:
return false; return false;
case SH_RECT: case SH_RECT:
switch( aB->Type() ) switch( aB->Type() )
{ {
@ -470,7 +445,8 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc
return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV ); return CollCase<SHAPE_RECT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE: case SH_SIMPLE:
return CollCase<SHAPE_RECT, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV ); case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC: case SH_ARC:
return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV ); return CollCaseReversed<SHAPE_RECT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
@ -499,7 +475,8 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc
return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV ); return CollCase<SHAPE_CIRCLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE: case SH_SIMPLE:
return CollCase<SHAPE_CIRCLE, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV ); case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC: case SH_ARC:
return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV ); return CollCaseReversed<SHAPE_CIRCLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
@ -528,7 +505,8 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV ); return CollCase<SHAPE_LINE_CHAIN, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE: case SH_SIMPLE:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV ); case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC: 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, aMTV );
@ -557,7 +535,8 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc
return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV ); return CollCase<SHAPE_SEGMENT, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE: case SH_SIMPLE:
return CollCase<SHAPE_SIMPLE, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV ); case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aB, aA, aClearance, aActual, aMTV );
case SH_ARC: case SH_ARC:
return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV ); return CollCaseReversed<SHAPE_SEGMENT, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
@ -571,25 +550,27 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc
break; break;
case SH_SIMPLE: case SH_SIMPLE:
case SH_POLY_SET_TRIANGLE:
switch( aB->Type() ) switch( aB->Type() )
{ {
case SH_RECT: case SH_RECT:
return CollCase<SHAPE_RECT, SHAPE_SIMPLE>( aB, aA, aClearance, aActual, aMTV ); return CollCase<SHAPE_RECT, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aMTV );
case SH_CIRCLE: case SH_CIRCLE:
return CollCase<SHAPE_CIRCLE, SHAPE_SIMPLE>( aB, aA, aClearance, aActual, aMTV ); return CollCase<SHAPE_CIRCLE, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aMTV );
case SH_LINE_CHAIN: case SH_LINE_CHAIN:
return CollCase<SHAPE_LINE_CHAIN, SHAPE_SIMPLE>( aB, aA, aClearance, aActual, aMTV ); return CollCase<SHAPE_LINE_CHAIN, SHAPE_LINE_CHAIN_BASE>( aB, aA, aClearance, aActual, aMTV );
case SH_SEGMENT: case SH_SEGMENT:
return CollCase<SHAPE_SIMPLE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV ); return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE: case SH_SIMPLE:
return CollCase<SHAPE_SIMPLE, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV ); case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_LINE_CHAIN_BASE, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC: case SH_ARC:
return CollCaseReversed<SHAPE_SIMPLE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV ); return CollCaseReversed<SHAPE_LINE_CHAIN_BASE, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );
case SH_NULL: case SH_NULL:
return false; return false;
@ -615,7 +596,8 @@ static bool collideSingleShapes( const SHAPE* aA, const SHAPE* aB, int aClearanc
return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV ); return CollCase<SHAPE_ARC, SHAPE_SEGMENT>( aA, aB, aClearance, aActual, aMTV );
case SH_SIMPLE: case SH_SIMPLE:
return CollCase<SHAPE_ARC, SHAPE_SIMPLE>( aA, aB, aClearance, aActual, aMTV ); case SH_POLY_SET_TRIANGLE:
return CollCase<SHAPE_ARC, SHAPE_LINE_CHAIN_BASE>( aA, aB, aClearance, aActual, aMTV );
case SH_ARC: case SH_ARC:
return CollCase<SHAPE_ARC, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV ); return CollCase<SHAPE_ARC, SHAPE_ARC>( aA, aB, aClearance, aActual, aMTV );

View File

@ -37,7 +37,7 @@
class SHAPE; class SHAPE;
SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<int>& aV) SHAPE_LINE_CHAIN::SHAPE_LINE_CHAIN( const std::vector<int>& aV)
: SHAPE( SH_LINE_CHAIN ), m_closed( false ), m_width( 0 ) : SHAPE_LINE_CHAIN_BASE( SH_LINE_CHAIN ), m_closed( false ), m_width( 0 )
{ {
for(size_t i = 0; i < aV.size(); i+= 2 ) for(size_t i = 0; i < aV.size(); i+= 2 )
{ {
@ -85,14 +85,16 @@ void SHAPE_LINE_CHAIN::convertArc( ssize_t aArcIndex )
} }
bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const bool SHAPE_LINE_CHAIN_BASE::Collide( const VECTOR2I& aP, int aClearance, int* aActual ) const
{ {
SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX; SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX;
SEG::ecoord clearance_sq = SEG::Square( aClearance ); SEG::ecoord clearance_sq = SEG::Square( aClearance );
for( int i = 0; i < SegmentCount(); i++ ) // fixme: why this only checks open curves?
for( int i = 0; i < GetSegmentCount(); i++ )
{ {
const SEG& s = CSegment( i ); const SEG& s = GetSegment( i );
dist_sq = std::min( dist_sq, s.SquaredDistance( aP ) ); dist_sq = std::min( dist_sq, s.SquaredDistance( aP ) );
if( ( dist_sq == 0 || dist_sq < clearance_sq ) && !aActual ) if( ( dist_sq == 0 || dist_sq < clearance_sq ) && !aActual )
@ -125,14 +127,14 @@ void SHAPE_LINE_CHAIN::Rotate( double aAngle, const VECTOR2I& aCenter )
} }
bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance, int* aActual ) const bool SHAPE_LINE_CHAIN_BASE::Collide( const SEG& aSeg, int aClearance, int* aActual ) const
{ {
SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX; SEG::ecoord dist_sq = VECTOR2I::ECOORD_MAX;
SEG::ecoord clearance_sq = SEG::Square( aClearance ); SEG::ecoord clearance_sq = SEG::Square( aClearance );
for( int i = 0; i < SegmentCount(); i++ ) for( int i = 0; i < GetSegmentCount(); i++ )
{ {
const SEG& s = CSegment( i ); const SEG& s = GetSegment( i );
dist_sq = std::min( dist_sq, s.SquaredDistance( aSeg ) ); dist_sq = std::min( dist_sq, s.SquaredDistance( aSeg ) );
if( ( dist_sq == 0 || dist_sq < clearance_sq ) && !aActual ) if( ( dist_sq == 0 || dist_sq < clearance_sq ) && !aActual )
@ -292,15 +294,15 @@ int SHAPE_LINE_CHAIN::Distance( const VECTOR2I& aP, bool aOutlineOnly ) const
} }
SEG::ecoord SHAPE_LINE_CHAIN::SquaredDistance( const VECTOR2I& aP, bool aOutlineOnly ) const SEG::ecoord SHAPE_LINE_CHAIN_BASE::SquaredDistance( const VECTOR2I& aP, bool aOutlineOnly ) const
{ {
ecoord d = VECTOR2I::ECOORD_MAX; 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 < GetSegmentCount(); s++ )
d = std::min( d, CSegment( s ).SquaredDistance( aP ) ); d = std::min( d, GetSegment( s ).SquaredDistance( aP ) );
return d; return d;
} }
@ -601,16 +603,18 @@ int SHAPE_LINE_CHAIN::PathLength( const VECTOR2I& aP ) const
} }
bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUseBBoxCache ) const bool SHAPE_LINE_CHAIN_BASE::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUseBBoxCache ) const
{ {
/* /*
* Don't check the bounding box unless it's cached. Building it is about the same speed as * Don't check the bounding box unless it's cached. Building it is about the same speed as
* the rigorous test below and so just slows things down by doing potentially two tests. * the rigorous test below and so just slows things down by doing potentially two tests.
*/ */
if( aUseBBoxCache && !m_bbox.Contains( aPt ) ) //if( aUseBBoxCache && !m_bbox.Contains( aPt ) )
return false; //return false;
if( !m_closed || PointCount() < 3 ) // fixme: bbox cache...
if( !IsClosed() || GetPointCount() < 3 )
return false; return false;
bool inside = false; bool inside = false;
@ -626,13 +630,12 @@ bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUs
* Note: we open-code CPoint() here so that we don't end up calculating the size of the * Note: we open-code CPoint() here so that we don't end up calculating the size of the
* vector number-of-points times. This has a non-trivial impact on zone fill times. * vector number-of-points times. This has a non-trivial impact on zone fill times.
*/ */
const std::vector<VECTOR2I>& points = CPoints(); int pointCount = GetPointCount();
int pointCount = points.size();
for( int i = 0; i < pointCount; ) for( int i = 0; i < pointCount; )
{ {
const auto p1 = points[ i++ ]; const auto p1 = GetPoint( i++ );
const auto p2 = points[ i == pointCount ? 0 : i ]; const auto p2 = GetPoint( i == pointCount ? 0 : i );
const auto diff = p2 - p1; const auto diff = p2 - p1;
if( diff.y != 0 ) if( diff.y != 0 )
@ -653,25 +656,25 @@ bool SHAPE_LINE_CHAIN::PointInside( const VECTOR2I& aPt, int aAccuracy, bool aUs
} }
bool SHAPE_LINE_CHAIN::PointOnEdge( const VECTOR2I& aPt, int aAccuracy ) const bool SHAPE_LINE_CHAIN_BASE::PointOnEdge( const VECTOR2I& aPt, int aAccuracy ) const
{ {
return EdgeContainingPoint( aPt, aAccuracy ) >= 0; return EdgeContainingPoint( aPt, aAccuracy ) >= 0;
} }
int SHAPE_LINE_CHAIN::EdgeContainingPoint( const VECTOR2I& aPt, int aAccuracy ) const int SHAPE_LINE_CHAIN_BASE::EdgeContainingPoint( const VECTOR2I& aPt, int aAccuracy ) const
{ {
if( !PointCount() ) if( !GetPointCount() )
return -1; return -1;
else if( PointCount() == 1 ) else if( GetPointCount() == 1 )
{ {
VECTOR2I dist = m_points[0] - aPt; VECTOR2I dist = GetPoint(0) - aPt;
return ( hypot( dist.x, dist.y ) <= aAccuracy + 1 ) ? 0 : -1; return ( hypot( dist.x, dist.y ) <= aAccuracy + 1 ) ? 0 : -1;
} }
for( int i = 0; i < SegmentCount(); i++ ) for( int i = 0; i < GetSegmentCount(); i++ )
{ {
const SEG s = CSegment( i ); const SEG s = GetSegment( i );
if( s.A == aPt || s.B == aPt ) if( s.A == aPt || s.B == aPt )
return i; return i;