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
|
* It is purposely not named "polyline" to avoid confusion with the existing CPolyLine
|
||||||
* in Pcbnew.
|
* in Pcbnew.
|
||||||
|
@ -187,7 +191,8 @@ public:
|
||||||
{
|
{
|
||||||
m_points = aArc.ConvertToPolyline().CPoints();
|
m_points = aArc.ConvertToPolyline().CPoints();
|
||||||
m_arcs.emplace_back( aArc );
|
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,
|
SHAPE_LINE_CHAIN( const ClipperLib::Path& aPath,
|
||||||
|
@ -197,6 +202,36 @@ public:
|
||||||
virtual ~SHAPE_LINE_CHAIN()
|
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_LINE_CHAIN& operator=( const SHAPE_LINE_CHAIN& ) = default;
|
||||||
|
|
||||||
SHAPE* Clone() const override;
|
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;
|
int collision_dist = 0;
|
||||||
VECTOR2I pn;
|
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,
|
if( aA.Collide( aB.GetSegment( i ), aClearance,
|
||||||
aActual || aLocation ? &collision_dist : nullptr,
|
aActual || aLocation ? &collision_dist : nullptr,
|
||||||
aLocation ? &pn : nullptr ) )
|
aLocation ? &pn : nullptr ) )
|
||||||
|
@ -326,6 +335,22 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_LINE_CH
|
||||||
break;
|
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 )
|
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(),
|
aA.Type(),
|
||||||
aB.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 );
|
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(),
|
aA.Type(),
|
||||||
aB.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 );
|
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(),
|
aA.Type(),
|
||||||
aB.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 )
|
// ignore arcs - we will collide these separately
|
||||||
*aActual = std::max( 0, *aActual - aA.GetWidth() / 2 );
|
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(),
|
aA.Type(),
|
||||||
aB.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 );
|
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 )
|
void SHAPE_LINE_CHAIN::Rotate( double aAngle, const VECTOR2I& aCenter )
|
||||||
{
|
{
|
||||||
for( auto& pt : m_points )
|
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
|
const SHAPE_LINE_CHAIN SHAPE_LINE_CHAIN::Reverse() const
|
||||||
{
|
{
|
||||||
SHAPE_LINE_CHAIN a( *this );
|
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 ?
|
// @todo should the below 4 LOC be moved to SHAPE_ARC::ConvertToPolyline ?
|
||||||
chain.m_arcs.push_back( aArc );
|
chain.m_arcs.push_back( aArc );
|
||||||
|
chain.m_arcs.back().SetWidth( 0 );
|
||||||
|
|
||||||
for( auto& sh : chain.m_shapes )
|
for( auto& sh : chain.m_shapes )
|
||||||
sh.first = 0;
|
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
|
/// Step 2: Add the arc polyline points to the chain
|
||||||
//@todo need to check we aren't creating duplicate points at start or end
|
//@todo need to check we aren't creating duplicate points at start or end
|
||||||
|
|
Loading…
Reference in New Issue