Hook up EE_GRID_HELPER to some more tools.

Also implements EE_GRID_HELPER layers so that connectable things
snap to connectable things and graphics snap to graphics.

Fixes https://gitlab.com/kicad/code/kicad/issues/5641
This commit is contained in:
Jeff Young 2020-12-03 15:10:23 +00:00
parent e79df4a3ed
commit a19028a396
7 changed files with 105 additions and 38 deletions

View File

@ -75,4 +75,7 @@
///< The intersheets references suffix string ///< The intersheets references suffix string
#define DEFAULT_IREF_SUFFIX "]" #define DEFAULT_IREF_SUFFIX "]"
///< Radius of snap "gravity well"
#define SNAP_RANGE 55
#endif #endif

View File

@ -23,6 +23,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
/**
* EE_GRID_HELPER
*
* A helper class for doing grid and object snapping.
*
* It shares its roots with PCBNew's GRID_HELPER, but uses the layers architecture to split
* connectable items from graphic items.
*/
#include <functional> #include <functional>
using namespace std::placeholders; using namespace std::placeholders;
@ -174,7 +183,8 @@ VECTOR2I EE_GRID_HELPER::AlignToWire( const VECTOR2I& aPoint, const SEG& aSeg )
return nearest; return nearest;
} }
VECTOR2I EE_GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos, const EE_SELECTION& aItems ) VECTOR2I EE_GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos, int aLayer,
const EE_SELECTION& aItems )
{ {
clearAnchors(); clearAnchors();
@ -184,9 +194,9 @@ VECTOR2I EE_GRID_HELPER::BestDragOrigin( const VECTOR2I &aMousePos, const EE_SEL
double worldScale = m_toolMgr->GetView()->GetGAL()->GetWorldScale(); double worldScale = m_toolMgr->GetView()->GetGAL()->GetWorldScale();
double lineSnapMinCornerDistance = 50.0 / worldScale; double lineSnapMinCornerDistance = 50.0 / worldScale;
ANCHOR* nearestOutline = nearestAnchor( aMousePos, OUTLINE, LSET::AllLayersMask() ); ANCHOR* nearestOutline = nearestAnchor( aMousePos, OUTLINE, aLayer );
ANCHOR* nearestCorner = nearestAnchor( aMousePos, CORNER, LSET::AllLayersMask() ); ANCHOR* nearestCorner = nearestAnchor( aMousePos, CORNER, aLayer );
ANCHOR* nearestOrigin = nearestAnchor( aMousePos, ORIGIN, LSET::AllLayersMask() ); ANCHOR* nearestOrigin = nearestAnchor( aMousePos, ORIGIN, aLayer );
ANCHOR* best = NULL; ANCHOR* best = NULL;
double minDist = std::numeric_limits<double>::max(); double minDist = std::numeric_limits<double>::max();
@ -245,20 +255,20 @@ std::set<SCH_ITEM*> EE_GRID_HELPER::queryVisible( const BOX2I& aArea,
} }
VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, SCH_ITEM* aDraggedItem ) VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, int aLayer, SCH_ITEM* aSkip )
{ {
EE_SELECTION draggedItems; EE_SELECTION skipItems;
draggedItems.Add( aDraggedItem ); skipItems.Add( aSkip );
return BestSnapAnchor( aOrigin, LSET::AllLayersMask(), draggedItems ); return BestSnapAnchor( aOrigin, aLayer, skipItems );
} }
VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& aLayers, VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, int aLayer,
const EE_SELECTION& aSkip ) const EE_SELECTION& aSkip )
{ {
int snapDist = GetGrid().x; int snapDist = GetGrid().x;
int snapRange = snapDist; int snapRange = SNAP_RANGE * IU_PER_MILS;
BOX2I bb( VECTOR2I( aOrigin.x - snapRange / 2, aOrigin.y - snapRange / 2 ), BOX2I bb( VECTOR2I( aOrigin.x - snapRange / 2, aOrigin.y - snapRange / 2 ),
VECTOR2I( snapRange, snapRange ) ); VECTOR2I( snapRange, snapRange ) );
@ -268,7 +278,7 @@ VECTOR2I EE_GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& aL
for( SCH_ITEM* item : queryVisible( bb, aSkip ) ) for( SCH_ITEM* item : queryVisible( bb, aSkip ) )
computeAnchors( item, aOrigin ); computeAnchors( item, aOrigin );
ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE, aLayers ); ANCHOR* nearest = nearestAnchor( aOrigin, SNAPPABLE, aLayer );
VECTOR2I nearestGrid = m_enableGrid ? Align( aOrigin ) : aOrigin; VECTOR2I nearestGrid = m_enableGrid ? Align( aOrigin ) : aOrigin;
if( nearest ) if( nearest )
@ -358,7 +368,7 @@ void EE_GRID_HELPER::computeAnchors( SCH_ITEM* aItem, const VECTOR2I& aRefPos, b
{ {
std::vector<wxPoint> pts = aItem->GetConnectionPoints(); std::vector<wxPoint> pts = aItem->GetConnectionPoints();
for( auto pt : pts ) for( const wxPoint& pt : pts )
addAnchor( VECTOR2I( pt ), SNAPPABLE | CORNER, aItem ); addAnchor( VECTOR2I( pt ), SNAPPABLE | CORNER, aItem );
break; break;
@ -371,7 +381,7 @@ void EE_GRID_HELPER::computeAnchors( SCH_ITEM* aItem, const VECTOR2I& aRefPos, b
EE_GRID_HELPER::ANCHOR* EE_GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int aFlags, EE_GRID_HELPER::ANCHOR* EE_GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int aFlags,
LSET aMatchLayers ) int aMatchLayer )
{ {
double minDist = std::numeric_limits<double>::max(); double minDist = std::numeric_limits<double>::max();
ANCHOR* best = NULL; ANCHOR* best = NULL;
@ -381,6 +391,11 @@ EE_GRID_HELPER::ANCHOR* EE_GRID_HELPER::nearestAnchor( const VECTOR2I& aPos, int
if( ( aFlags & a.flags ) != aFlags ) if( ( aFlags & a.flags ) != aFlags )
continue; continue;
if( aMatchLayer == LAYER_CONNECTABLE && !a.item->IsConnectable() )
continue;
else if( aMatchLayer == LAYER_GRAPHICS && a.item->IsConnectable() )
continue;
double dist = a.Distance( aPos ); double dist = a.Distance( aPos );
if( dist < minDist ) if( dist < minDist )

View File

@ -23,6 +23,15 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
/**
* EE_GRID_HELPER
*
* A helper class for doing grid and object snapping.
*
* It shares its roots with PCBNew's GRID_HELPER, but uses the layers architecture to split
* connectable items from graphic items.
*/
#ifndef __GRID_HELPER_H #ifndef __GRID_HELPER_H
#define __GRID_HELPER_H #define __GRID_HELPER_H
@ -35,7 +44,17 @@ class LSET;
class SCH_ITEM; class SCH_ITEM;
class SEG; class SEG;
class EE_GRID_HELPER {
enum EE_GRID_HELPER_LAYERS : int
{
LAYER_ANY = SCH_LAYER_ID_END + 1,
LAYER_CONNECTABLE,
LAYER_GRAPHICS
};
class EE_GRID_HELPER
{
public: public:
EE_GRID_HELPER( TOOL_MANAGER* aToolMgr ); EE_GRID_HELPER( TOOL_MANAGER* aToolMgr );
@ -61,11 +80,10 @@ public:
VECTOR2I AlignToWire( const VECTOR2I& aPoint, const SEG& aSeg ); VECTOR2I AlignToWire( const VECTOR2I& aPoint, const SEG& aSeg );
VECTOR2I BestDragOrigin( const VECTOR2I& aMousePos, const EE_SELECTION& aItems ); VECTOR2I BestDragOrigin( const VECTOR2I& aMousePos, int aLayer, const EE_SELECTION& aItems );
VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, SCH_ITEM* aDraggedItem ); VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, int aLayer, SCH_ITEM* aDraggedItem );
VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, const LSET& aLayers, VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, int aLayer, const EE_SELECTION& aSkip = {} );
const EE_SELECTION& aSkip = {} );
void SetSkipPoint( const VECTOR2I& aPoint ) void SetSkipPoint( const VECTOR2I& aPoint )
{ {
@ -119,7 +137,7 @@ private:
m_anchors.emplace_back( ANCHOR( aPos, aFlags, aItem ) ); m_anchors.emplace_back( ANCHOR( aPos, aFlags, aItem ) );
} }
ANCHOR* nearestAnchor( const VECTOR2I& aPos, int aFlags, LSET aMatchLayers ); ANCHOR* nearestAnchor( const VECTOR2I& aPos, int aFlags, int aMatchLayer );
/** /**
* computeAnchors inserts the local anchor points in to the grid helper for the specified * computeAnchors inserts the local anchor points in to the grid helper for the specified

View File

@ -342,7 +342,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( collector.GetCount() == 1 && !m_isSymbolEditor && !modifier_enabled ) if( collector.GetCount() == 1 && !m_isSymbolEditor && !modifier_enabled )
{ {
// Check if we want to auto start wires // Check if we want to auto start wires
VECTOR2I snappedCursorPos = grid.BestSnapAnchor( evt->Position(), nullptr ); VECTOR2I snappedCursorPos = grid.BestSnapAnchor( evt->Position(),
LAYER_CONNECTABLE, nullptr );
if( m_frame->eeconfig()->m_Drawing.auto_start_wires if( m_frame->eeconfig()->m_Drawing.auto_start_wires
&& collector[0]->IsPointClickableAnchor( (wxPoint) snappedCursorPos ) ) && collector[0]->IsPointClickableAnchor( (wxPoint) snappedCursorPos ) )
@ -507,7 +508,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( collector.GetCount() == 1 && !modifier_enabled ) if( collector.GetCount() == 1 && !modifier_enabled )
{ {
VECTOR2I snappedCursorPos = grid.BestSnapAnchor( evt->Position(), nullptr ); VECTOR2I snappedCursorPos = grid.BestSnapAnchor( evt->Position(),
LAYER_CONNECTABLE, nullptr );
if( m_frame->eeconfig()->m_Drawing.auto_start_wires if( m_frame->eeconfig()->m_Drawing.auto_start_wires
&& collector[0]->IsPointClickableAnchor( (wxPoint) snappedCursorPos ) ) && collector[0]->IsPointClickableAnchor( (wxPoint) snappedCursorPos ) )
@ -864,10 +866,12 @@ void EE_SELECTION_TOOL::GuessSelectionCandidates( EE_COLLECTOR& collector, const
// pin should select the symbol with this setting // pin should select the symbol with this setting
// To avoid conflict with the auto-start wires option // To avoid conflict with the auto-start wires option
EE_GRID_HELPER grid( m_toolMgr ); EE_GRID_HELPER grid( m_toolMgr );
wxPoint cursorPos = wxPoint( grid.BestSnapAnchor( aPos, nullptr ) ); wxPoint cursorPos = wxPoint( grid.BestSnapAnchor( aPos, LAYER_CONNECTABLE,
nullptr ) );
if( !m_isSymbolEditor && m_frame->eeconfig()->m_Selection.select_pin_selects_symbol if( !m_isSymbolEditor
&& !other->IsPointClickableAnchor( cursorPos ) ) && m_frame->eeconfig()->m_Selection.select_pin_selects_symbol
&& !other->IsPointClickableAnchor( cursorPos ) )
{ {
collector.Transfer( other ); collector.Transfer( other );
} }

View File

@ -24,6 +24,7 @@
#include "sch_drawing_tools.h" #include "sch_drawing_tools.h"
#include "ee_selection_tool.h" #include "ee_selection_tool.h"
#include "ee_grid_helper.h"
#include <ee_actions.h> #include <ee_actions.h>
#include <sch_edit_frame.h> #include <sch_edit_frame.h>
#include <project.h> #include <project.h>
@ -806,16 +807,20 @@ SCH_SHEET_PIN* SCH_DRAWING_TOOLS::createSheetPin( SCH_SHEET* aSheet, SCH_HIERLAB
int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent ) int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
{ {
SCH_ITEM* item = nullptr; SCH_ITEM* item = nullptr;
KIGFX::VIEW_CONTROLS* controls = getViewControls();
EE_GRID_HELPER grid( m_toolMgr );
bool isImportMode = aEvent.IsAction( &EE_ACTIONS::importSheetPin ); bool isImportMode = aEvent.IsAction( &EE_ACTIONS::importSheetPin );
bool isText = aEvent.IsAction( &EE_ACTIONS::placeSchematicText ); bool isText = aEvent.IsAction( &EE_ACTIONS::placeSchematicText );
bool isGlobalLabel = aEvent.IsAction( &EE_ACTIONS::placeGlobalLabel ); bool isGlobalLabel = aEvent.IsAction( &EE_ACTIONS::placeGlobalLabel );
bool isHierLabel = aEvent.IsAction( &EE_ACTIONS::placeHierLabel ); bool isHierLabel = aEvent.IsAction( &EE_ACTIONS::placeHierLabel );
bool isNetLabel = aEvent.IsAction( &EE_ACTIONS::placeLabel ); bool isNetLabel = aEvent.IsAction( &EE_ACTIONS::placeLabel );
KICAD_T type = aEvent.Parameter<KICAD_T>(); KICAD_T type = aEvent.Parameter<KICAD_T>();
int snapLayer = isText ? LAYER_GRAPHICS : LAYER_CONNECTABLE;
m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true ); m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
getViewControls()->ShowCursor( true ); controls->ShowCursor( true );
std::string tool = aEvent.GetCommandStr().get(); std::string tool = aEvent.GetCommandStr().get();
m_frame->PushTool( tool ); m_frame->PushTool( tool );
@ -849,8 +854,11 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
while( TOOL_EVENT* evt = Wait() ) while( TOOL_EVENT* evt = Wait() )
{ {
setCursor(); setCursor();
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
VECTOR2I cursorPos = getViewControls()->GetCursorPosition( !evt->Modifier( MD_ALT ) ); VECTOR2I cursorPos = grid.BestSnapAnchor( controls->GetCursorPosition( false ), snapLayer,
item );
auto cleanup = auto cleanup =
[&] () [&] ()
@ -956,7 +964,7 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
} }
// Restore cursor after dialog // Restore cursor after dialog
getViewControls()->WarpCursor( getViewControls()->GetCursorPosition(), true ); controls->WarpCursor( controls->GetCursorPosition(), true );
if( item ) if( item )
{ {
@ -969,7 +977,7 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
setCursor(); setCursor();
} }
getViewControls()->SetCursorPosition( cursorPos, false ); controls->SetCursorPosition( cursorPos, false );
} }
// ... and second click places: // ... and second click places:
@ -1020,8 +1028,8 @@ int SCH_DRAWING_TOOLS::TwoClickPlace( const TOOL_EVENT& aEvent )
} }
// Enable autopanning and cursor capture only when there is a footprint to be placed // Enable autopanning and cursor capture only when there is a footprint to be placed
getViewControls()->SetAutoPan( item != nullptr ); controls->SetAutoPan( item != nullptr );
getViewControls()->CaptureCursor( item != nullptr ); controls->CaptureCursor( item != nullptr );
} }
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW ); m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );

View File

@ -406,7 +406,7 @@ const SCH_SHEET_PIN* SCH_LINE_WIRE_BUS_TOOL::getSheetPin( const wxPoint& aPositi
void SCH_LINE_WIRE_BUS_TOOL::computeBreakPoint( const std::pair<SCH_LINE*, SCH_LINE*>& aSegments, void SCH_LINE_WIRE_BUS_TOOL::computeBreakPoint( const std::pair<SCH_LINE*, SCH_LINE*>& aSegments,
wxPoint& aPosition ) wxPoint& aPosition )
{ {
wxCHECK_RET( aSegments.first && aSegments.second, wxCHECK_RET( aSegments.first && aSegments.second,
wxT( "Cannot compute break point of NULL line segment." ) ); wxT( "Cannot compute break point of NULL line segment." ) );
@ -503,10 +503,13 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const std::string& aTool, int aType,
while( TOOL_EVENT* evt = Wait() ) while( TOOL_EVENT* evt = Wait() )
{ {
setCursor(); setCursor();
grid.SetSnap( !evt->Modifier( MD_SHIFT ) ); grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
wxPoint cursorPos = wxPoint( grid.BestSnapAnchor( grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
evt->IsPrime() ? evt->Position() : controls->GetMousePosition(), nullptr ) );
wxPoint cursorPos = evt->IsPrime() ? (wxPoint) evt->Position()
: (wxPoint) controls->GetMousePosition();
cursorPos = (wxPoint) grid.BestSnapAnchor( cursorPos, LAYER_CONNECTABLE, nullptr );
controls->ForceCursorPosition( true, cursorPos ); controls->ForceCursorPosition( true, cursorPos );
bool forceHV = m_frame->eeconfig()->m_Drawing.hv_lines_only; bool forceHV = m_frame->eeconfig()->m_Drawing.hv_lines_only;
@ -516,7 +519,7 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const std::string& aTool, int aType,
{ {
m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true ); m_toolMgr->RunAction( EE_ACTIONS::clearSelection, true );
for( auto wire : m_wires ) for( SCH_LINE* wire : m_wires )
delete wire; delete wire;
m_wires.clear(); m_wires.clear();

View File

@ -172,6 +172,7 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
bool chain_commands = false; bool chain_commands = false;
TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent ); TOOL_EVENT* evt = const_cast<TOOL_EVENT*>( &aEvent );
VECTOR2I prevPos; VECTOR2I prevPos;
int snapLayer = UNDEFINED_LAYER;
m_cursor = controls->GetCursorPosition(); m_cursor = controls->GetCursorPosition();
@ -247,6 +248,21 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
// //
for( EDA_ITEM* item : selection ) for( EDA_ITEM* item : selection )
{ {
if( static_cast<SCH_ITEM*>( item )->IsConnectable() )
{
if( snapLayer == LAYER_GRAPHICS )
snapLayer = LAYER_ANY;
else
snapLayer = LAYER_CONNECTABLE;
}
else
{
if( snapLayer == LAYER_CONNECTABLE )
snapLayer = LAYER_ANY;
else
snapLayer = LAYER_GRAPHICS;
}
if( item->IsNew() ) if( item->IsNew() )
{ {
if( item->HasFlag( TEMP_SELECTED ) && m_isDragOperation ) if( item->HasFlag( TEMP_SELECTED ) && m_isDragOperation )
@ -317,7 +333,7 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
if( m_frame->GetMoveWarpsCursor() ) if( m_frame->GetMoveWarpsCursor() )
{ {
// User wants to warp the mouse // User wants to warp the mouse
m_cursor = grid.BestDragOrigin( m_cursor, selection ); m_cursor = grid.BestDragOrigin( m_cursor, snapLayer, selection );
} }
else else
{ {
@ -338,7 +354,7 @@ int SCH_MOVE_TOOL::Main( const TOOL_EVENT& aEvent )
// Follow the mouse // Follow the mouse
// //
m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ), m_cursor = grid.BestSnapAnchor( controls->GetCursorPosition( false ),
LSET::AllLayersMask(), selection ); snapLayer, selection );
VECTOR2I delta( m_cursor - prevPos ); VECTOR2I delta( m_cursor - prevPos );
m_anchorPos = m_cursor; m_anchorPos = m_cursor;