Implement true arc collisions for arcs inside a SHAPE_LINE_CHAIN
This commit is contained in:
parent
ad3b4f25c2
commit
0c3da0f072
|
@ -66,7 +66,11 @@ struct CLIPPER_Z_VALUE
|
|||
|
||||
|
||||
/**
|
||||
* Represent a polyline (an zero-thickness chain of connected line segments).
|
||||
* Represent a polyline containing arcs as well as line segments: A chain of connected line and/or
|
||||
* arc segments.
|
||||
*
|
||||
* The arc shapes are piecewise approximated for the purpose of boolean operations but are used as
|
||||
* arcs when doing collision checks.
|
||||
*
|
||||
* It is purposely not named "polyline" to avoid confusion with the existing CPolyLine
|
||||
* in Pcbnew.
|
||||
|
@ -187,7 +191,8 @@ public:
|
|||
{
|
||||
m_points = aArc.ConvertToPolyline().CPoints();
|
||||
m_arcs.emplace_back( aArc );
|
||||
m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( m_points.size(), SHAPES_ARE_PT );
|
||||
m_arcs.back().SetWidth( 0 );
|
||||
m_shapes = std::vector<std::pair<ssize_t, ssize_t>>( m_points.size(), { 0, SHAPE_IS_PT } );
|
||||
}
|
||||
|
||||
SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath,
|
||||
|
@ -197,6 +202,36 @@ public:
|
|||
virtual ~SHAPE_LINE_CHAIN()
|
||||
{}
|
||||
|
||||
/**
|
||||
* Check if point \a aP lies closer to us than \a aClearance.
|
||||
*
|
||||
* Note: This is overridden as we want to ensure we test collisions with the arcs in this chain
|
||||
* as true arcs rather than segment approximations.
|
||||
*
|
||||
* @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,
|
||||
VECTOR2I* aLocation = nullptr ) const override;
|
||||
|
||||
/**
|
||||
* Check if segment \a aSeg lies closer to us than \a aClearance.
|
||||
*
|
||||
* Note: This is overridden as we want to ensure we test collisions with the arcs in this chain
|
||||
* as true arcs rather than segment approximations.
|
||||
*
|
||||
* @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,
|
||||
VECTOR2I* aLocation = nullptr ) const override;
|
||||
|
||||
SHAPE_LINE_CHAIN& operator=( const SHAPE_LINE_CHAIN& ) = default;
|
||||
|
||||
SHAPE* Clone() const override;
|
||||
|
|
|
@ -308,6 +308,15 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CH
|
|||
int collision_dist = 0;
|
||||
VECTOR2I pn;
|
||||
|
||||
if( aB.Type() == SH_LINE_CHAIN )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN* aB_line_chain = static_cast<const SHAPE_LINE_CHAIN*>( &aB );
|
||||
|
||||
// ignore arcs - we will collide these separately
|
||||
if( aB_line_chain->IsArcSegment( i ) )
|
||||
continue;
|
||||
}
|
||||
|
||||
if( aA.Collide( aB.GetSegment( i ), aClearance,
|
||||
aActual || aLocation ? &collision_dist : nullptr,
|
||||
aLocation ? &pn : nullptr ) )
|
||||
|
@ -326,6 +335,22 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CH
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( aB.Type() == SH_LINE_CHAIN )
|
||||
{
|
||||
const SHAPE_LINE_CHAIN* aB_line_chain = static_cast<const SHAPE_LINE_CHAIN*>( &aB );
|
||||
|
||||
for( size_t i = 0; i < aB_line_chain->ArcCount(); i++ )
|
||||
{
|
||||
const SHAPE_ARC& arc = aB_line_chain->Arc( i );
|
||||
|
||||
// The arcs in the chain should have zero width
|
||||
wxASSERT_MSG( arc.GetWidth() == 0, "Invalid arc width - should be zero" );
|
||||
|
||||
if( arc.Collide( &aA, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( closest_dist == 0 || closest_dist < aClearance )
|
||||
|
@ -462,7 +487,7 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_RECT& aB, int aClea
|
|||
aA.Type(),
|
||||
aB.Type() ) );
|
||||
|
||||
const SHAPE_LINE_CHAIN lc = aA.ConvertToPolyline();
|
||||
const SHAPE_LINE_CHAIN lc( aA );
|
||||
|
||||
bool rv = Collide( lc, aB.Outline(), aClearance + aA.GetWidth() / 2, aActual, aLocation, aMTV );
|
||||
|
||||
|
@ -480,7 +505,7 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_CIRCLE& aB, int aCl
|
|||
aA.Type(),
|
||||
aB.Type() ) );
|
||||
|
||||
const SHAPE_LINE_CHAIN lc = aA.ConvertToPolyline();
|
||||
const SHAPE_LINE_CHAIN lc( aA );
|
||||
|
||||
bool rv = Collide( aB, lc, aClearance + aA.GetWidth() / 2, aActual, aLocation, aMTV );
|
||||
|
||||
|
@ -498,14 +523,68 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_LINE_CHAIN& aB, int
|
|||
aA.Type(),
|
||||
aB.Type() ) );
|
||||
|
||||
const SHAPE_LINE_CHAIN lc = aA.ConvertToPolyline();
|
||||
int closest_dist = INT_MAX;
|
||||
VECTOR2I nearest;
|
||||
|
||||
bool rv = Collide( lc, aB, aClearance + aA.GetWidth() / 2, aActual, aLocation, aMTV );
|
||||
if( aB.IsClosed() && aB.PointInside( aA.GetP0() ) )
|
||||
{
|
||||
closest_dist = 0;
|
||||
nearest = aA.GetP0();
|
||||
}
|
||||
else
|
||||
{
|
||||
for( size_t i = 0; i < aB.GetSegmentCount(); i++ )
|
||||
{
|
||||
int collision_dist = 0;
|
||||
VECTOR2I pn;
|
||||
|
||||
if( rv && aActual )
|
||||
*aActual = std::max( 0, *aActual - aA.GetWidth() / 2 );
|
||||
// ignore arcs - we will collide these separately
|
||||
if( aB.IsArcSegment( i ) )
|
||||
continue;
|
||||
|
||||
return rv;
|
||||
if( aA.Collide( aB.GetSegment( i ), aClearance,
|
||||
aActual || aLocation ? &collision_dist : nullptr,
|
||||
aLocation ? &pn : nullptr ) )
|
||||
{
|
||||
if( collision_dist < closest_dist )
|
||||
{
|
||||
nearest = pn;
|
||||
closest_dist = collision_dist;
|
||||
}
|
||||
|
||||
if( closest_dist == 0 )
|
||||
break;
|
||||
|
||||
// If we're not looking for aActual then any collision will do
|
||||
if( !aActual )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < aB.ArcCount(); i++ )
|
||||
{
|
||||
const SHAPE_ARC& arc = aB.Arc( i );
|
||||
|
||||
// The arcs in the chain should have zero width
|
||||
wxASSERT_MSG( arc.GetWidth() == 0, "Invalid arc width - should be zero" );
|
||||
|
||||
if( arc.Collide( &aA, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( closest_dist == 0 || closest_dist < aClearance )
|
||||
{
|
||||
if( aLocation )
|
||||
*aLocation = nearest;
|
||||
|
||||
if( aActual )
|
||||
*aActual = closest_dist;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -516,7 +595,7 @@ static inline bool Collide( const SHAPE_ARC& aA, const SHAPE_SEGMENT& aB, int aC
|
|||
aA.Type(),
|
||||
aB.Type() ) );
|
||||
|
||||
const SHAPE_LINE_CHAIN lc = aA.ConvertToPolyline();
|
||||
const SHAPE_LINE_CHAIN lc( aA );
|
||||
|
||||
bool rv = Collide( lc, aB, aClearance + aA.GetWidth() / 2, aActual, aLocation, aMTV );
|
||||
|
||||
|
|
|
@ -300,6 +300,75 @@ bool SHAPE_LINE_CHAIN_BASE::Collide( const VECTOR2I& aP, int aClearance, int* aA
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_LINE_CHAIN::Collide( const VECTOR2I& aP, int aClearance, int* aActual,
|
||||
VECTOR2I* aLocation ) const
|
||||
{
|
||||
if( IsClosed() && PointInside( aP, aClearance ) )
|
||||
{
|
||||
if( aLocation )
|
||||
*aLocation = aP;
|
||||
|
||||
if( aActual )
|
||||
*aActual = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX;
|
||||
SEG::ecoord clearance_sq = SEG::Square( aClearance );
|
||||
VECTOR2I nearest;
|
||||
|
||||
// Collide line segments
|
||||
for( size_t i = 0; i < GetSegmentCount(); i++ )
|
||||
{
|
||||
if( IsArcSegment( i ) )
|
||||
continue;
|
||||
|
||||
const SEG& s = GetSegment( i );
|
||||
VECTOR2I pn = s.NearestPoint( aP );
|
||||
SEG::ecoord dist_sq = ( pn - aP ).SquaredEuclideanNorm();
|
||||
|
||||
if( dist_sq < closest_dist_sq )
|
||||
{
|
||||
nearest = pn;
|
||||
closest_dist_sq = dist_sq;
|
||||
|
||||
if( closest_dist_sq == 0 )
|
||||
break;
|
||||
|
||||
// If we're not looking for aActual then any collision will do
|
||||
if( closest_dist_sq < clearance_sq && !aActual )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
|
||||
{
|
||||
if( aLocation )
|
||||
*aLocation = nearest;
|
||||
|
||||
if( aActual )
|
||||
*aActual = sqrt( closest_dist_sq );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Collide arc segments
|
||||
for( size_t i = 0; i < ArcCount(); i++ )
|
||||
{
|
||||
const SHAPE_ARC& arc = Arc( i );
|
||||
|
||||
// The arcs in the chain should have zero width
|
||||
wxASSERT_MSG( arc.GetWidth() == 0, "Invalid arc width - should be zero" );
|
||||
|
||||
if( arc.Collide( aP, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SHAPE_LINE_CHAIN::Rotate( double aAngle, const VECTOR2I& aCenter )
|
||||
{
|
||||
for( auto& pt : m_points )
|
||||
|
@ -368,6 +437,76 @@ bool SHAPE_LINE_CHAIN_BASE::Collide( const SEG& aSeg, int aClearance, int* aActu
|
|||
}
|
||||
|
||||
|
||||
bool SHAPE_LINE_CHAIN::Collide( const SEG& aSeg, int aClearance, int* aActual,
|
||||
VECTOR2I* aLocation ) const
|
||||
{
|
||||
if( IsClosed() && PointInside( aSeg.A ) )
|
||||
{
|
||||
if( aLocation )
|
||||
*aLocation = aSeg.A;
|
||||
|
||||
if( aActual )
|
||||
*aActual = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SEG::ecoord closest_dist_sq = VECTOR2I::ECOORD_MAX;
|
||||
SEG::ecoord clearance_sq = SEG::Square( aClearance );
|
||||
VECTOR2I nearest;
|
||||
|
||||
// Collide line segments
|
||||
for( size_t i = 0; i < GetSegmentCount(); i++ )
|
||||
{
|
||||
if( IsArcSegment( i ) )
|
||||
continue;
|
||||
|
||||
const SEG& s = GetSegment( i );
|
||||
SEG::ecoord dist_sq = s.SquaredDistance( aSeg );
|
||||
|
||||
if( dist_sq < closest_dist_sq )
|
||||
{
|
||||
if( aLocation )
|
||||
nearest = s.NearestPoint( aSeg );
|
||||
|
||||
closest_dist_sq = dist_sq;
|
||||
|
||||
if( closest_dist_sq == 0 )
|
||||
break;
|
||||
|
||||
// If we're not looking for aActual then any collision will do
|
||||
if( closest_dist_sq < clearance_sq && !aActual )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( closest_dist_sq == 0 || closest_dist_sq < clearance_sq )
|
||||
{
|
||||
if( aLocation )
|
||||
*aLocation = nearest;
|
||||
|
||||
if( aActual )
|
||||
*aActual = sqrt( closest_dist_sq );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Collide arc segments
|
||||
for( size_t i = 0; i < ArcCount(); i++ )
|
||||
{
|
||||
const SHAPE_ARC& arc = Arc( i );
|
||||
|
||||
// The arcs in the chain should have zero width
|
||||
wxASSERT_MSG( arc.GetWidth() == 0, "Invalid arc width - should be zero" );
|
||||
|
||||
if( arc.Collide( aSeg, aClearance, aActual, aLocation ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
|
||||
{
|
||||
SHAPE_LINE_CHAIN a( *this );
|
||||
|
@ -1066,6 +1205,7 @@ void SHAPE_LINE_CHAIN::Append( const SHAPE_ARC& aArc )
|
|||
|
||||
// @todo should the below 4 LOC be moved to SHAPE_ARC::ConvertToPolyline ?
|
||||
chain.m_arcs.push_back( aArc );
|
||||
chain.m_arcs.back().SetWidth( 0 );
|
||||
|
||||
for( auto& sh : chain.m_shapes )
|
||||
sh.first = 0;
|
||||
|
@ -1130,7 +1270,9 @@ void SHAPE_LINE_CHAIN::Insert( size_t aVertex, const SHAPE_ARC& aArc )
|
|||
} );
|
||||
}
|
||||
|
||||
m_arcs.insert( m_arcs.begin() + arc_pos, aArc );
|
||||
SHAPE_ARC arcCopy( aArc );
|
||||
arcCopy.SetWidth( 0 );
|
||||
m_arcs.insert( m_arcs.begin() + arc_pos, arcCopy );
|
||||
|
||||
/// Step 2: Add the arc polyline points to the chain
|
||||
//@todo need to check we aren't creating duplicate points at start or end
|
||||
|
|
Loading…
Reference in New Issue