Use polygonal hit testing for module selection

This commit is contained in:
Jon Evans 2018-02-18 19:00:29 -05:00 committed by Wayne Stambaugh
parent c8458bc8ed
commit 01ab8b0584
6 changed files with 83 additions and 23 deletions

View File

@ -1402,19 +1402,19 @@ bool SHAPE_POLY_SET::CollideEdge( const VECTOR2I& aPoint,
}
bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex ) const
bool SHAPE_POLY_SET::Contains( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles ) const
{
if( m_polys.size() == 0 ) // empty set?
return false;
// If there is a polygon specified, check the condition against that polygon
if( aSubpolyIndex >= 0 )
return containsSingle( aP, aSubpolyIndex );
return containsSingle( aP, aSubpolyIndex, aIgnoreHoles );
// In any other case, check it against all polygons in the set
for( int polygonIdx = 0; polygonIdx < OutlineCount(); polygonIdx++ )
{
if( containsSingle( aP, polygonIdx ) )
if( containsSingle( aP, polygonIdx, aIgnoreHoles ) )
return true;
}
@ -1440,20 +1440,23 @@ void SHAPE_POLY_SET::RemoveVertex( VERTEX_INDEX aIndex )
}
bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) const
bool SHAPE_POLY_SET::containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles ) const
{
// Check that the point is inside the outline
if( pointInPolygon( aP, m_polys[aSubpolyIndex][0] ) )
{
// Check that the point is not in any of the holes
for( int holeIdx = 0; holeIdx < HoleCount( aSubpolyIndex ); holeIdx++ )
if( !aIgnoreHoles )
{
const SHAPE_LINE_CHAIN hole = CHole( aSubpolyIndex, holeIdx );
// Check that the point is not in any of the holes
for( int holeIdx = 0; holeIdx < HoleCount( aSubpolyIndex ); holeIdx++ )
{
const SHAPE_LINE_CHAIN hole = CHole( aSubpolyIndex, holeIdx );
// If the point is inside a hole (and not on its edge),
// it is outside of the polygon
if( pointInPolygon( aP, hole ) && !hole.PointOnEdge( aP ) )
return false;
// If the point is inside a hole (and not on its edge),
// it is outside of the polygon
if( pointInPolygon( aP, hole ) && !hole.PointOnEdge( aP ) )
return false;
}
}
return true;

View File

@ -933,9 +933,15 @@ class SHAPE_POLY_SET : public SHAPE
bool CollideEdge( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex,
int aClearance = 0 );
///> Returns true if a given subpolygon contains the point aP. If aSubpolyIndex < 0
///> (default value), checks all polygons in the set
bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1 ) const;
/**
* Returns true if a given subpolygon contains the point aP
*
* @param aP is the point to check
* @param aSubpolyIndex is the subpolygon to check, or -1 to check all
* @param aIgnoreHoles controls whether or not internal holes are considered
* @return true if the polygon contains the point
*/
bool Contains( const VECTOR2I& aP, int aSubpolyIndex = -1, bool aIgnoreHoles = false ) const;
///> Returns true if the set is empty (no polygons at all)
bool IsEmpty() const
@ -1112,10 +1118,11 @@ class SHAPE_POLY_SET : public SHAPE
* the aSubpolyIndex-th polygon will be tested.
* @param aSubpolyIndex is an integer specifying which polygon in the set has to be
* checked.
* @param aIgnoreHoles can be set to true to ignore internal holes in the polygon
* @return bool - true if aP is inside aSubpolyIndex-th polygon; false in any other
* case.
*/
bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex ) const;
bool containsSingle( const VECTOR2I& aP, int aSubpolyIndex, bool aIgnoreHoles = false ) const;
/**
* Operations ChamferPolygon and FilletPolygon are computed under the private chamferFillet

View File

@ -138,7 +138,7 @@ void MODULE::TransformPadsShapesWithClearanceToPolygon( PCB_LAYER_ID aLayer,
wxSize margin;
for( ; pad != NULL; pad = pad->Next() )
{
if( !pad->IsOnLayer(aLayer) )
if( aLayer != UNDEFINED_LAYER && !pad->IsOnLayer(aLayer) )
continue;
// NPTH pads are not drawn on layers if the shape size and pos is the same
@ -206,7 +206,8 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet(
int aInflateValue,
int aCircleToSegmentsCount,
double aCorrectionFactor,
int aCircleToSegmentsCountForTexts ) const
int aCircleToSegmentsCountForTexts,
bool aIncludeText ) const
{
std::vector<TEXTE_MODULE *> texts; // List of TEXTE_MODULE to convert
EDGE_MODULE* outline;
@ -219,7 +220,8 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet(
{
TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( item );
if( text->GetLayer() == aLayer && text->IsVisible() )
if( ( aLayer != UNDEFINED_LAYER && text->GetLayer() == aLayer )
&& text->IsVisible() )
texts.push_back( text );
break;
@ -228,7 +230,7 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet(
case PCB_MODULE_EDGE_T:
outline = (EDGE_MODULE*) item;
if( outline->GetLayer() != aLayer )
if( aLayer != UNDEFINED_LAYER && outline->GetLayer() != aLayer )
break;
outline->TransformShapeWithClearanceToPolygon( aCornerBuffer, 0,
@ -240,6 +242,9 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet(
}
}
if( !aIncludeText )
return;
// Convert texts sur modules
if( Reference().GetLayer() == aLayer && Reference().IsVisible() )
texts.push_back( &Reference() );

View File

@ -512,6 +512,25 @@ const EDA_RECT MODULE::GetBoundingBox() const
}
SHAPE_POLY_SET MODULE::GetBoundingPoly() const
{
const int segcountforcircle = 8;
double correctionFactor = 1.0 / cos( M_PI / (segcountforcircle * 2) );
SHAPE_POLY_SET poly;
TransformPadsShapesWithClearanceToPolygon( UNDEFINED_LAYER,
poly, 0, segcountforcircle, correctionFactor );
TransformGraphicShapesWithClearanceToPolygonSet( UNDEFINED_LAYER,
poly, 0, segcountforcircle, correctionFactor, 0, false );
poly.NormalizeAreaOutlines();
poly.Inflate( Millimeter2iu( 0.01 ), segcountforcircle );
return poly;
}
void MODULE::GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList )
{
int nbpad;
@ -607,6 +626,13 @@ bool MODULE::HitTest( const wxPoint& aPosition ) const
}
bool MODULE::HitTestAccurate( const wxPoint& aPosition ) const
{
auto shape = GetBoundingPoly();
return shape.Contains( aPosition, -1, true );
}
bool MODULE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
{
EDA_RECT arect = aRect;

View File

@ -148,6 +148,12 @@ public:
*/
EDA_RECT GetFootprintRect() const;
/**
* Returns a bounding polygon for the shapes and pads in the module
* This operation is slower but more accurate than calculating a bounding box
*/
SHAPE_POLY_SET GetBoundingPoly() const;
// Virtual function
const EDA_RECT GetBoundingBox() const override;
@ -336,7 +342,7 @@ public:
* and adds these polygons to aCornerBuffer
* Useful to generate a polygonal representation of a footprint
* in 3D view and plot functions, when a full polygonal approach is needed
* @param aLayer = the current layer: pads on this layer are considered
* @param aLayer = the layer to consider, or UNDEFINED_LAYER to consider all
* @param aCornerBuffer = the buffer to store polygons
* @param aInflateValue = an additionnal size to add to pad shapes
* aInflateValue = 0 to have the exact pad size
@ -366,7 +372,7 @@ public:
* and adds these polygons to aCornerBuffer
* Useful to generate a polygonal representation of a footprint
* in 3D view and plot functions, when a full polygonal approach is needed
* @param aLayer = the current layer: items on this layer are considered
* @param aLayer = the layer to consider, or UNDEFINED_LAYER to consider all
* @param aCornerBuffer = the buffer to store polygons
* @param aInflateValue = a value to inflate shapes
* aInflateValue = 0 to have the exact shape size
@ -385,7 +391,8 @@ public:
int aInflateValue,
int aCircleToSegmentsCount,
double aCorrectionFactor,
int aCircleToSegmentsCountForTexts = 0 ) const;
int aCircleToSegmentsCountForTexts = 0,
bool aIncludeText = true ) const;
/**
* @brief TransformGraphicTextWithClearanceToPolygonSet
@ -430,6 +437,17 @@ public:
bool HitTest( const wxPoint& aPosition ) const override;
/**
* Tests if a point is inside the bounding polygon of the module
*
* The other hit test methods are just checking the bounding box, which
* can be quite inaccurate for rotated or oddly-shaped footprints.
*
* @param aPosition is the point to test
* @return true if aPosition is inside the bounding polygon
*/
bool HitTestAccurate( const wxPoint& aPosition ) const;
bool HitTest( const EDA_RECT& aRect, bool aContained = true, int aAccuracy = 0 ) const override;
/**

View File

@ -399,7 +399,8 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
{
if( item->HitTest( m_RefPos ) )
{
Append( item );
if( !module || module->HitTestAccurate( m_RefPos ) )
Append( item );
goto exit;
}
}