Scale zone hit area by zoom.

Fixes: lp:1803362
* https://bugs.launchpad.net/kicad/+bug/1803362
This commit is contained in:
Jeff Young 2018-11-17 00:34:12 +00:00
parent 0de9cb345c
commit 208622699f
8 changed files with 101 additions and 53 deletions

View File

@ -651,16 +651,21 @@ void ZONE_CONTAINER::SetCornerRadius( unsigned int aRadius )
bool ZONE_CONTAINER::HitTest( const wxPoint& aPosition ) const bool ZONE_CONTAINER::HitTest( const wxPoint& aPosition ) const
{ {
return HitTestForCorner( aPosition ) || HitTestForEdge( aPosition ); // Normally accuracy is zoom-relative, but for the generic HitTest we just use
// a fixed (small) value.
int accuracy = Millimeter2iu( 0.05 );
return HitTestForCorner( aPosition, accuracy * 2 ) || HitTestForEdge( aPosition, accuracy );
} }
void ZONE_CONTAINER::SetSelectedCorner( const wxPoint& aPosition ) void ZONE_CONTAINER::SetSelectedCorner( const wxPoint& aPosition, int aAccuracy )
{ {
SHAPE_POLY_SET::VERTEX_INDEX corner; SHAPE_POLY_SET::VERTEX_INDEX corner;
// If there is some corner to be selected, assign it to m_CornerSelection // If there is some corner to be selected, assign it to m_CornerSelection
if( HitTestForCorner( aPosition, corner ) || HitTestForEdge( aPosition, corner ) ) if( HitTestForCorner( aPosition, aAccuracy * 2, corner )
|| HitTestForEdge( aPosition, aAccuracy, corner ) )
{ {
if( m_CornerSelection == nullptr ) if( m_CornerSelection == nullptr )
m_CornerSelection = new SHAPE_POLY_SET::VERTEX_INDEX; m_CornerSelection = new SHAPE_POLY_SET::VERTEX_INDEX;
@ -669,40 +674,31 @@ void ZONE_CONTAINER::SetSelectedCorner( const wxPoint& aPosition )
} }
} }
// Zones outlines have no thickness, so it Hit Test functions bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos, int aAccuracy,
// we must have a default distance between the test point
// and a corner or a zone edge:
#define MAX_DIST_IN_MM 0.25
bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos,
SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const
{ {
int distmax = Millimeter2iu( MAX_DIST_IN_MM ); return m_Poly->CollideVertex( VECTOR2I( refPos ), aCornerHit, aAccuracy );
return m_Poly->CollideVertex( VECTOR2I( refPos ), aCornerHit, distmax );
} }
bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) const bool ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos, int aAccuracy ) const
{ {
SHAPE_POLY_SET::VERTEX_INDEX dummy; SHAPE_POLY_SET::VERTEX_INDEX dummy;
return HitTestForCorner( refPos, dummy ); return HitTestForCorner( refPos, aAccuracy, dummy );
} }
bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos, bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos, int aAccuracy,
SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const
{ {
int distmax = Millimeter2iu( MAX_DIST_IN_MM ); return m_Poly->CollideEdge( VECTOR2I( refPos ), aCornerHit, aAccuracy );
return m_Poly->CollideEdge( VECTOR2I( refPos ), aCornerHit, distmax );
} }
bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) const bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos, int aAccuracy ) const
{ {
SHAPE_POLY_SET::VERTEX_INDEX dummy; SHAPE_POLY_SET::VERTEX_INDEX dummy;
return HitTestForEdge( refPos, dummy ); return HitTestForEdge( refPos, aAccuracy, dummy );
} }

View File

@ -231,7 +231,7 @@ public:
/// ///
// Like HitTest but selects the current corner to be operated on // Like HitTest but selects the current corner to be operated on
void SetSelectedCorner( const wxPoint& aPosition ); void SetSelectedCorner( const wxPoint& aPosition, int aAccuracy );
int GetLocalFlags() const { return m_localFlgs; } int GetLocalFlags() const { return m_localFlgs; }
void SetLocalFlags( int aFlags ) { m_localFlgs = aFlags; } void SetLocalFlags( int aFlags ) { m_localFlgs = aFlags; }
@ -329,39 +329,45 @@ public:
* Function HitTestForCorner * Function HitTestForCorner
* tests if the given wxPoint is near a corner. * tests if the given wxPoint is near a corner.
* @param refPos is the wxPoint to test. * @param refPos is the wxPoint to test.
* @param aAccuracy increase the item bounding box by this amount.
* @param aCornerHit [out] is the index of the closest vertex found, useless when return * @param aCornerHit [out] is the index of the closest vertex found, useless when return
* value is false. * value is false.
* @return bool - true if some corner was found to be closer to refPos than aClearance; false * @return bool - true if some corner was found to be closer to refPos than aClearance; false
* otherwise. * otherwise.
*/ */
bool HitTestForCorner( const wxPoint& refPos, SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const; bool HitTestForCorner( const wxPoint& refPos, int aAccuracy,
SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const;
/** /**
* Function HitTestForCorner * Function HitTestForCorner
* tests if the given wxPoint is near a corner. * tests if the given wxPoint is near a corner.
* @param refPos is the wxPoint to test. * @param refPos is the wxPoint to test.
* @param aAccuracy increase the item bounding box by this amount.
* @return bool - true if some corner was found to be closer to refPos than aClearance; false * @return bool - true if some corner was found to be closer to refPos than aClearance; false
* otherwise. * otherwise.
*/ */
bool HitTestForCorner( const wxPoint& refPos ) const; bool HitTestForCorner( const wxPoint& refPos, int aAccuracy ) const;
/** /**
* Function HitTestForEdge * Function HitTestForEdge
* tests if the given wxPoint is near a segment defined by 2 corners. * tests if the given wxPoint is near a segment defined by 2 corners.
* @param refPos is the wxPoint to test. * @param refPos is the wxPoint to test.
* @param aAccuracy increase the item bounding box by this amount.
* @param aCornerHit [out] is the index of the closest vertex found, useless when return * @param aCornerHit [out] is the index of the closest vertex found, useless when return
* value is false. * value is false.
* @return bool - true if some edge was found to be closer to refPos than aClearance. * @return bool - true if some edge was found to be closer to refPos than aClearance.
*/ */
bool HitTestForEdge( const wxPoint& refPos, SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const; bool HitTestForEdge( const wxPoint& refPos, int aAccuracy,
SHAPE_POLY_SET::VERTEX_INDEX& aCornerHit ) const;
/** /**
* Function HitTestForEdge * Function HitTestForEdge
* tests if the given wxPoint is near a segment defined by 2 corners. * tests if the given wxPoint is near a segment defined by 2 corners.
* @param refPos is the wxPoint to test. * @param refPos is the wxPoint to test.
* @param aAccuracy increase the item bounding box by this amount.
* @return bool - true if some edge was found to be closer to refPos than aClearance. * @return bool - true if some edge was found to be closer to refPos than aClearance.
*/ */
bool HitTestForEdge( const wxPoint& refPos ) const; bool HitTestForEdge( const wxPoint& refPos, int aAccuracy ) const;
/** @copydoc BOARD_ITEM::HitTest(const EDA_RECT& aRect, /** @copydoc BOARD_ITEM::HitTest(const EDA_RECT& aRect,
* bool aContained = true, int aAccuracy ) const * bool aContained = true, int aAccuracy ) const

View File

@ -393,12 +393,10 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
{ {
PCB_LAYER_ID layer = item->GetLayer(); PCB_LAYER_ID layer = item->GetLayer();
/* Modules and their subcomponents: reference, value and pads // Modules and their subcomponents: reference, value and pads are not sensitive
* are not sensitive to the layer visibility controls. They all // to the layer visibility controls. They all have their own separate visibility
* have their own separate visibility controls for vias, // controls for vias, GetLayer() has no meaning, but IsOnLayer() works fine. User
* GetLayer() has no meaning, but IsOnLayer() works fine. User // text in module *is* sensitive to layer visibility but that was already handled.
* text in module *is* sensitive to layer visibility but that
* was already handled */
if( via || module || pad || m_Guide->IsLayerVisible( layer ) if( via || module || pad || m_Guide->IsLayerVisible( layer )
|| !m_Guide->IgnoreNonVisibleLayers() ) || !m_Guide->IgnoreNonVisibleLayers() )
@ -407,13 +405,28 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
{ {
if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() ) if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() )
{ {
if( item->HitTest( m_RefPos ) ) if( zone )
{
bool testFill = !m_Guide->IgnoreZoneFills();
int accuracy = KiROUND( 5 * m_Guide->OnePixelInIU() );
if( zone->HitTestForCorner( m_RefPos, accuracy * 2 )
|| zone->HitTestForEdge( m_RefPos, accuracy )
|| ( testFill && zone->HitTestFilledArea( m_RefPos ) ) )
{ {
if( !module || module->HitTestAccurate( m_RefPos ) )
Append( item ); Append( item );
goto exit; goto exit;
} }
else if( zone && !m_Guide->IgnoreZoneFills() && zone->HitTestFilledArea( m_RefPos ) ) }
else if( module )
{
if( module->HitTest( m_RefPos ) && module->HitTestAccurate( m_RefPos ) )
{
Append( item );
goto exit;
}
}
else if( item->HitTest( m_RefPos ) )
{ {
Append( item ); Append( item );
goto exit; goto exit;
@ -432,11 +445,10 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
PCB_LAYER_ID layer = item->GetLayer(); PCB_LAYER_ID layer = item->GetLayer();
/* Modules and their subcomponents: reference, value and pads // Modules and their subcomponents: reference, value and pads are not sensitive
* are not sensitive to the layer visibility controls. They all // to the layer visibility controls. They all have their own separate visibility
* have their own separate visibility controls. User texts // controls for vias, GetLayer() has no meaning, but IsOnLayer() works fine. User
* follows layer visibility controls (but that was already // text in module *is* sensitive to layer visibility but that was already handled.
* checked) */
if( via || module || pad || m_Guide->IsLayerVisible( layer ) if( via || module || pad || m_Guide->IsLayerVisible( layer )
|| !m_Guide->IgnoreNonVisibleLayers() ) || !m_Guide->IgnoreNonVisibleLayers() )
@ -445,14 +457,30 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
{ {
if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() ) if( !item->IsLocked() || !m_Guide->IgnoreLockedItems() )
{ {
if( item->HitTest( m_RefPos ) ) if( zone )
{
bool testFill = !m_Guide->IgnoreZoneFills();
int accuracy = KiROUND( 5 * m_Guide->OnePixelInIU() );
if( zone->HitTestForCorner( m_RefPos, accuracy * 2 )
|| zone->HitTestForEdge( m_RefPos, accuracy )
|| ( testFill && zone->HitTestFilledArea( m_RefPos ) ) )
{ {
Append2nd( item ); Append2nd( item );
goto exit; goto exit;
} }
else if( zone && !m_Guide->IgnoreZoneFills() && zone->HitTestFilledArea( m_RefPos ) ) }
else if( module )
{ {
Append( item ); if( module->HitTest( m_RefPos ) && module->HitTestAccurate( m_RefPos ) )
{
Append2nd( item );
goto exit;
}
}
else if( item->HitTest( m_RefPos ) )
{
Append2nd( item );
goto exit; goto exit;
} }
} }

View File

@ -37,6 +37,7 @@
#include <collector.h> #include <collector.h>
#include <layers_id_colors_and_visibility.h> // LAYER_COUNT, layer defs #include <layers_id_colors_and_visibility.h> // LAYER_COUNT, layer defs
#include <view/view.h>
class BOARD_ITEM; class BOARD_ITEM;
@ -193,6 +194,8 @@ public:
*/ */
virtual bool IgnoreZoneFills() const = 0; virtual bool IgnoreZoneFills() const = 0;
virtual double OnePixelInIU() const = 0;
/** /**
* @return bool - true if Inspect() should use BOARD_ITEM::HitTest() * @return bool - true if Inspect() should use BOARD_ITEM::HitTest()
* or false if Inspect() should use BOARD_ITEM::BoundsTest(). * or false if Inspect() should use BOARD_ITEM::BoundsTest().
@ -346,6 +349,8 @@ public:
*/ */
void SetGuide( const COLLECTORS_GUIDE* aGuide ) { m_Guide = aGuide; } void SetGuide( const COLLECTORS_GUIDE* aGuide ) { m_Guide = aGuide; }
const COLLECTORS_GUIDE* GetGuide() { return m_Guide; }
/** /**
* @return int - The number if items which met the primary search criteria * @return int - The number if items which met the primary search criteria
*/ */
@ -417,6 +422,8 @@ private:
bool m_IgnoreTracks; bool m_IgnoreTracks;
bool m_IgnoreZoneFills; bool m_IgnoreZoneFills;
double m_OnePixelInIU;
public: public:
/** /**
@ -427,8 +434,11 @@ public:
* @param aVisibleLayerMask = current visible layers (bit mask) * @param aVisibleLayerMask = current visible layers (bit mask)
* @param aPreferredLayer = the layer to search first * @param aPreferredLayer = the layer to search first
*/ */
GENERAL_COLLECTORS_GUIDE( LSET aVisibleLayerMask, PCB_LAYER_ID aPreferredLayer ) GENERAL_COLLECTORS_GUIDE( LSET aVisibleLayerMask, PCB_LAYER_ID aPreferredLayer,
KIGFX::VIEW* aView )
{ {
VECTOR2I one( 1, 1 );
m_PreferredLayer = aPreferredLayer; m_PreferredLayer = aPreferredLayer;
m_IgnorePreferredLayer = false; m_IgnorePreferredLayer = false;
m_LayerVisible = aVisibleLayerMask; m_LayerVisible = aVisibleLayerMask;
@ -460,6 +470,8 @@ public:
m_IgnoreMicroVias = false; m_IgnoreMicroVias = false;
m_IgnoreTracks = false; m_IgnoreTracks = false;
m_IgnoreZoneFills = true; m_IgnoreZoneFills = true;
m_OnePixelInIU = aView->ToWorld( one, false ).x;
} }
/** /**
@ -602,6 +614,8 @@ public:
bool IgnoreZoneFills() const override { return m_IgnoreZoneFills; } bool IgnoreZoneFills() const override { return m_IgnoreZoneFills; }
void SetIgnoreZoneFills( bool ignore ) { m_IgnoreZoneFills = ignore; } void SetIgnoreZoneFills( bool ignore ) { m_IgnoreZoneFills = ignore; }
double OnePixelInIU() const override { return m_OnePixelInIU; }
}; };

View File

@ -188,7 +188,8 @@ BOARD_ITEM* PCB_BASE_FRAME::PcbGeneralLocateAndDisplay( int aHotKeyCode )
/* We need to do the selection now because the menu text /* We need to do the selection now because the menu text
* depends on it */ * depends on it */
ZONE_CONTAINER *zone = static_cast<ZONE_CONTAINER*>( item ); ZONE_CONTAINER *zone = static_cast<ZONE_CONTAINER*>( item );
zone->SetSelectedCorner( RefPos( true ) ); int accuracy = KiROUND( 5 * guide.OnePixelInIU() );
zone->SetSelectedCorner( RefPos( true ), accuracy );
} }
break; break;

View File

@ -628,6 +628,7 @@ void PCB_EDIT_FRAME::createPopupMenuForTracks( TRACK* Track, wxMenu* PopMenu )
void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu* aPopMenu ) void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu* aPopMenu )
{ {
wxString msg; wxString msg;
GENERAL_COLLECTORS_GUIDE guide = GetCollectorsGuide();
if( edge_zone->GetFlags() == IS_DRAGGED ) if( edge_zone->GetFlags() == IS_DRAGGED )
{ {
@ -646,19 +647,20 @@ void PCB_EDIT_FRAME::createPopUpMenuForZones( ZONE_CONTAINER* edge_zone, wxMenu*
else else
{ {
wxMenu* zones_menu = new wxMenu(); wxMenu* zones_menu = new wxMenu();
int accuracy = KiROUND( 5 * guide.OnePixelInIU() );
AddMenuItem( aPopMenu, zones_menu, -1, AddMenuItem( aPopMenu, zones_menu, -1,
edge_zone->GetIsKeepout() ? _("Keepout Area") : _( "Zones" ), edge_zone->GetIsKeepout() ? _("Keepout Area") : _( "Zones" ),
KiBitmap( add_zone_xpm ) ); KiBitmap( add_zone_xpm ) );
if( edge_zone->HitTestForCorner( RefPos( true ) ) ) if( edge_zone->HitTestForCorner( RefPos( true ), accuracy * 2 ) )
{ {
AddMenuItem( zones_menu, ID_POPUP_PCB_MOVE_ZONE_CORNER, AddMenuItem( zones_menu, ID_POPUP_PCB_MOVE_ZONE_CORNER,
_( "Move" ), KiBitmap( move_xpm ) ); _( "Move" ), KiBitmap( move_xpm ) );
AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CORNER, AddMenuItem( zones_menu, ID_POPUP_PCB_DELETE_ZONE_CORNER,
_( "Delete" ), KiBitmap( delete_xpm ) ); _( "Delete" ), KiBitmap( delete_xpm ) );
} }
else if( edge_zone->HitTestForEdge( RefPos( true ) ) ) else if( edge_zone->HitTestForEdge( RefPos( true ), accuracy ) )
{ {
AddMenuItem( zones_menu, ID_POPUP_PCB_ADD_ZONE_CORNER, AddMenuItem( zones_menu, ID_POPUP_PCB_ADD_ZONE_CORNER,
_( "Create Corner" ), KiBitmap( add_corner_xpm ) ); _( "Create Corner" ), KiBitmap( add_corner_xpm ) );

View File

@ -760,7 +760,8 @@ BOARD_ITEM* PCB_BASE_FRAME::GetCurItem()
GENERAL_COLLECTORS_GUIDE PCB_BASE_FRAME::GetCollectorsGuide() GENERAL_COLLECTORS_GUIDE PCB_BASE_FRAME::GetCollectorsGuide()
{ {
GENERAL_COLLECTORS_GUIDE guide( m_Pcb->GetVisibleLayers(), GetActiveLayer() ); GENERAL_COLLECTORS_GUIDE guide( m_Pcb->GetVisibleLayers(), GetActiveLayer(),
GetGalCanvas()->GetView() );
// account for the globals // account for the globals
guide.SetIgnoreMTextsMarkedNoShow( ! m_Pcb->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) ); guide.SetIgnoreMTextsMarkedNoShow( ! m_Pcb->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) );

View File

@ -459,7 +459,7 @@ void SELECTION_TOOL::toggleSelection( BOARD_ITEM* aItem )
const GENERAL_COLLECTORS_GUIDE SELECTION_TOOL::getCollectorsGuide() const const GENERAL_COLLECTORS_GUIDE SELECTION_TOOL::getCollectorsGuide() const
{ {
GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(), GENERAL_COLLECTORS_GUIDE guide( board()->GetVisibleLayers(),
(PCB_LAYER_ID) view()->GetTopLayer() ); (PCB_LAYER_ID) view()->GetTopLayer(), view() );
// account for the globals // account for the globals
guide.SetIgnoreMTextsMarkedNoShow( ! board()->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) ); guide.SetIgnoreMTextsMarkedNoShow( ! board()->IsElementVisible( LAYER_MOD_TEXT_INVISIBLE ) );
@ -2036,7 +2036,7 @@ void SELECTION_TOOL::guessSelectionCandidates( GENERAL_COLLECTOR& aCollector,
{ {
auto zone = static_cast<ZONE_CONTAINER*>( aCollector[i] ); auto zone = static_cast<ZONE_CONTAINER*>( aCollector[i] );
if( zone->HitTestForEdge( where ) ) if( zone->HitTestForEdge( where, 5 * aCollector.GetGuide()->OnePixelInIU() ) )
preferred.insert( zone ); preferred.insert( zone );
else else
rejected.insert( zone ); rejected.insert( zone );