pcbnew: Implement consistent graphical snapping
This creates a standard snapping framework in the GRID_HELPER class that allows snapping to items on the same layer as the object being created/moved as well as consistent toggling of this using the Shift key modifier. Fixes: lp:806260 * https://bugs.launchpad.net/kicad/+bug/806260 Fixes: lp:1604616 * https://bugs.launchpad.net/kicad/+bug/1604616
This commit is contained in:
parent
f714d2fa64
commit
03e642a8db
|
@ -975,6 +975,7 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
|
|||
assert( aShape == S_SEGMENT || aShape == S_CIRCLE );
|
||||
|
||||
DRAWSEGMENT line45;
|
||||
GRID_HELPER grid( m_frame );
|
||||
|
||||
m_frame->SetActiveLayer( getDrawingLayer() );
|
||||
|
||||
|
@ -998,9 +999,11 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
|
|||
// Init the new item attributes
|
||||
aGraphic->SetShape( (STROKE_T) aShape );
|
||||
aGraphic->SetWidth( m_lineWidth );
|
||||
aGraphic->SetStart( wxPoint( aStartingPoint->x, aStartingPoint->y ) );
|
||||
aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
|
||||
aGraphic->SetLayer( getDrawingLayer() );
|
||||
aGraphic->SetStart( wxPoint( aStartingPoint->x, aStartingPoint->y ) );
|
||||
|
||||
cursorPos = grid.BestSnapAnchor( cursorPos, aGraphic );
|
||||
aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
|
||||
|
||||
if( aShape == S_SEGMENT )
|
||||
line45 = *aGraphic; // used only for direction 45 mode with lines
|
||||
|
@ -1018,7 +1021,9 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
|
|||
// Main loop: keep receiving events
|
||||
while( OPT_TOOL_EVENT evt = Wait() )
|
||||
{
|
||||
cursorPos = m_controls->GetCursorPosition();
|
||||
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
||||
|
||||
cursorPos = grid.BestSnapAnchor( evt->Position(), aGraphic );
|
||||
|
||||
// 45 degree angle constraint enabled with an option and toggled with Ctrl
|
||||
const bool limit45 = ( frame()->Settings().m_use45DegreeGraphicSegments != !!( evt->Modifier( MD_CTRL ) ) );
|
||||
|
@ -1030,7 +1035,7 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
|
|||
if( direction45 )
|
||||
{
|
||||
preview.Add( &line45 );
|
||||
make45DegLine( aGraphic, &line45 );
|
||||
make45DegLine( aGraphic, &line45, cursorPos );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1121,7 +1126,7 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
|
|||
{
|
||||
// 45 degree lines
|
||||
if( direction45 && aShape == S_SEGMENT )
|
||||
make45DegLine( aGraphic, &line45 );
|
||||
make45DegLine( aGraphic, &line45, cursorPos );
|
||||
else
|
||||
aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
|
||||
|
||||
|
@ -1198,6 +1203,7 @@ bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
|
|||
SELECTION preview;
|
||||
m_view->Add( &preview );
|
||||
m_view->Add( &arcAsst );
|
||||
GRID_HELPER grid( m_frame );
|
||||
|
||||
m_controls->ShowCursor( true );
|
||||
m_controls->SetSnapping( true );
|
||||
|
@ -1209,7 +1215,11 @@ bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
|
|||
// Main loop: keep receiving events
|
||||
while( OPT_TOOL_EVENT evt = Wait() )
|
||||
{
|
||||
const VECTOR2I cursorPos = m_controls->GetCursorPosition();
|
||||
PCB_LAYER_ID layer = getDrawingLayer();
|
||||
aGraphic->SetLayer( layer );
|
||||
|
||||
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
||||
const VECTOR2I cursorPos = grid.BestSnapAnchor( evt->Position(), aGraphic );
|
||||
|
||||
if( evt->IsClick( BUT_LEFT ) )
|
||||
{
|
||||
|
@ -1218,13 +1228,10 @@ bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
|
|||
m_controls->SetAutoPan( true );
|
||||
m_controls->CaptureCursor( true );
|
||||
|
||||
PCB_LAYER_ID layer = getDrawingLayer();
|
||||
|
||||
// Init the new item attributes
|
||||
// (non-geometric, those are handled by the manager)
|
||||
aGraphic->SetShape( S_ARC );
|
||||
aGraphic->SetWidth( m_lineWidth );
|
||||
aGraphic->SetLayer( layer );
|
||||
|
||||
preview.Add( aGraphic );
|
||||
firstPoint = true;
|
||||
|
@ -1342,13 +1349,16 @@ void DRAWING_TOOL::runPolygonEventLoop( POLYGON_GEOM_MANAGER& polyGeomMgr )
|
|||
auto& controls = *getViewControls();
|
||||
bool started = false;
|
||||
|
||||
GRID_HELPER grid( m_frame );
|
||||
STATUS_TEXT_POPUP status( m_frame );
|
||||
status.SetTextColor( wxColour( 255, 0, 0 ) );
|
||||
status.SetText( _( "Self-intersecting polygons are not allowed" ) );
|
||||
|
||||
while( OPT_TOOL_EVENT evt = Wait() )
|
||||
{
|
||||
VECTOR2I cursorPos = controls.GetCursorPosition();
|
||||
LSET layers( m_frame->GetActiveLayer() );
|
||||
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
||||
VECTOR2I cursorPos = grid.BestSnapAnchor( evt->Position(), layers );
|
||||
|
||||
if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
|
||||
{
|
||||
|
@ -1489,12 +1499,12 @@ int DRAWING_TOOL::drawZone( bool aKeepout, ZONE_MODE aMode )
|
|||
}
|
||||
|
||||
|
||||
void DRAWING_TOOL::make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper ) const
|
||||
void DRAWING_TOOL::make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper,
|
||||
VECTOR2I& aPos ) const
|
||||
{
|
||||
VECTOR2I cursorPos = m_controls->GetCursorPosition();
|
||||
VECTOR2I origin( aSegment->GetStart() );
|
||||
DIRECTION_45 direction( origin - cursorPos );
|
||||
SHAPE_LINE_CHAIN newChain = direction.BuildInitialTrace( origin, cursorPos );
|
||||
DIRECTION_45 direction( origin - aPos );
|
||||
SHAPE_LINE_CHAIN newChain = direction.BuildInitialTrace( origin, aPos );
|
||||
|
||||
if( newChain.PointCount() > 2 )
|
||||
{
|
||||
|
@ -1504,9 +1514,9 @@ void DRAWING_TOOL::make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper )
|
|||
}
|
||||
else
|
||||
{
|
||||
aSegment->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
|
||||
aHelper->SetStart( wxPoint( cursorPos.x, cursorPos.y ) );
|
||||
aHelper->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
|
||||
aSegment->SetEnd( wxPoint( aPos.x, aPos.y ) );
|
||||
aHelper->SetStart( wxPoint( aPos.x, aPos.y ) );
|
||||
aHelper->SetEnd( wxPoint( aPos.x, aPos.y ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1593,24 +1603,17 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
|
|||
|
||||
// bool do_snap = ( m_frame->Settings().m_magneticTracks == CAPTURE_CURSOR_IN_TRACK_TOOL
|
||||
// || m_frame->Settings().m_magneticTracks == CAPTURE_ALWAYS );
|
||||
bool do_snap = true;
|
||||
m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
|
||||
auto via = static_cast<VIA*>( aItem );
|
||||
wxPoint pos = via->GetPosition();
|
||||
TRACK* track = findTrack( via );
|
||||
|
||||
if( m_modifiers & MD_SHIFT )
|
||||
do_snap = !do_snap;
|
||||
|
||||
if( do_snap )
|
||||
if( track )
|
||||
{
|
||||
auto via = static_cast<VIA*>( aItem );
|
||||
wxPoint pos = via->GetPosition();
|
||||
TRACK* track = findTrack( via );
|
||||
SEG trackSeg( track->GetStart(), track->GetEnd() );
|
||||
VECTOR2I snap = m_gridHelper.AlignToSegment( pos, trackSeg );
|
||||
|
||||
if( track )
|
||||
{
|
||||
SEG trackSeg( track->GetStart(), track->GetEnd() );
|
||||
VECTOR2I snap = m_gridHelper.AlignToSegment( pos, trackSeg );
|
||||
|
||||
aItem->SetPosition( wxPoint( snap.x, snap.y ) );
|
||||
}
|
||||
aItem->SetPosition( wxPoint( snap.x, snap.y ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -251,8 +251,9 @@ private:
|
|||
* the end of the aSegment is modified according to the current cursor position.
|
||||
* @param aSegment is the segment that is currently drawn.
|
||||
* @param aHelper is a helper line that shows the next possible segment.
|
||||
* @param aPos is the position of the cursor for this event
|
||||
*/
|
||||
void make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper ) const;
|
||||
void make45DegLine( DRAWSEGMENT* aSegment, DRAWSEGMENT* aHelper, VECTOR2I& aPos ) const;
|
||||
|
||||
/**
|
||||
* Function constrainDimension()
|
||||
|
|
|
@ -372,6 +372,8 @@ int EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
|
|||
// Main loop: keep receiving events
|
||||
do
|
||||
{
|
||||
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
||||
|
||||
if( evt->IsAction( &PCB_ACTIONS::editActivate ) ||
|
||||
evt->IsAction( &PCB_ACTIONS::move ) ||
|
||||
evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
|
||||
|
|
|
@ -46,6 +46,8 @@ GRID_HELPER::GRID_HELPER( PCB_BASE_FRAME* aFrame ) :
|
|||
m_frame( aFrame )
|
||||
{
|
||||
m_diagonalAuxAxesEnable = true;
|
||||
m_enableSnap = true;
|
||||
m_snapSize = 100;
|
||||
KIGFX::VIEW* view = m_frame->GetGalCanvas()->GetView();
|
||||
|
||||
m_viewAxis.SetSize( 20000 );
|
||||
|
@ -141,6 +143,9 @@ VECTOR2I GRID_HELPER::AlignToSegment( const VECTOR2I& aPoint, const SEG& aSeg )
|
|||
{
|
||||
OPT_VECTOR2I pts[6];
|
||||
|
||||
if( !m_enableSnap )
|
||||
return aPoint;
|
||||
|
||||
const VECTOR2D gridOffset( GetOrigin() );
|
||||
const VECTOR2D gridSize( GetGrid() );
|
||||
|
||||
|
@ -238,9 +243,22 @@ std::set<BOARD_ITEM*> GRID_HELPER::queryVisible( const BOX2I& aArea ) const
|
|||
|
||||
|
||||
VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDraggedItem )
|
||||
{
|
||||
LSET layers;
|
||||
|
||||
if( aDraggedItem )
|
||||
layers = aDraggedItem->GetLayer();
|
||||
else
|
||||
layers = LSET::AllLayersMask();
|
||||
|
||||
return BestSnapAnchor( aOrigin, layers );
|
||||
}
|
||||
|
||||
|
||||
VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, LSET& aLayers )
|
||||
{
|
||||
double worldScale = m_frame->GetGalCanvas()->GetGAL()->GetWorldScale();
|
||||
int snapRange = (int) ( 100.0 / worldScale );
|
||||
int snapRange = (int) ( m_snapSize / worldScale );
|
||||
|
||||
BOX2I bb( VECTOR2I( aOrigin.x - snapRange / 2, aOrigin.y - snapRange / 2 ), VECTOR2I( snapRange, snapRange ) );
|
||||
|
||||
|
@ -251,19 +269,12 @@ VECTOR2I GRID_HELPER::BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDrag
|
|||
computeAnchors( item, aOrigin );
|
||||
}
|
||||
|
||||
LSET layers;
|
||||
|
||||
if( aDraggedItem )
|
||||
layers = aDraggedItem->GetLayer();
|
||||
else
|
||||
layers = LSET::AllLayersMask();
|
||||
|
||||
ANCHOR* nearest = nearestAnchor( aOrigin, CORNER | SNAPPABLE, layers );
|
||||
ANCHOR* nearest = nearestAnchor( aOrigin, CORNER | SNAPPABLE, aLayers );
|
||||
|
||||
VECTOR2I nearestGrid = Align( aOrigin );
|
||||
double gridDist = ( nearestGrid - aOrigin ).EuclideanNorm();
|
||||
|
||||
if( nearest )
|
||||
if( nearest && m_enableSnap )
|
||||
{
|
||||
double snapDist = nearest->Distance( aOrigin );
|
||||
|
||||
|
|
|
@ -57,6 +57,12 @@ public:
|
|||
|
||||
VECTOR2I BestDragOrigin( const VECTOR2I& aMousePos, BOARD_ITEM* aItem );
|
||||
VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, BOARD_ITEM* aDraggedItem );
|
||||
VECTOR2I BestSnapAnchor( const VECTOR2I& aOrigin, LSET& aLayers );
|
||||
|
||||
void SetSnap( bool aSnap )
|
||||
{
|
||||
m_enableSnap = aSnap;
|
||||
}
|
||||
|
||||
private:
|
||||
enum ANCHOR_FLAGS {
|
||||
|
@ -79,8 +85,6 @@ private:
|
|||
{
|
||||
return ( aP - pos ).EuclideanNorm();
|
||||
}
|
||||
|
||||
//bool CanSnapItem( const BOARD_ITEM* aItem ) const;
|
||||
};
|
||||
|
||||
std::vector<ANCHOR> m_anchors;
|
||||
|
@ -103,8 +107,13 @@ private:
|
|||
|
||||
PCB_BASE_FRAME* m_frame;
|
||||
OPT<VECTOR2I> m_auxAxis;
|
||||
bool m_diagonalAuxAxesEnable;
|
||||
KIGFX::ORIGIN_VIEWITEM m_viewSnapPoint, m_viewAxis;
|
||||
|
||||
bool m_diagonalAuxAxesEnable; ///< If true, use the aux axis for snapping as well
|
||||
bool m_enableSnap; ///< If true, allow snapping to other items on the layers
|
||||
int m_snapSize; ///< Sets the radius in screen units for snapping to items
|
||||
|
||||
KIGFX::ORIGIN_VIEWITEM m_viewSnapPoint;
|
||||
KIGFX::ORIGIN_VIEWITEM m_viewAxis;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -329,6 +329,8 @@ int POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
|
|||
if( revert )
|
||||
break;
|
||||
|
||||
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
|
||||
|
||||
if( !m_editPoints ||
|
||||
evt->Matches( m_selectionTool->ClearedEvent ) ||
|
||||
evt->Matches( m_selectionTool->UnselectedEvent ) ||
|
||||
|
@ -346,7 +348,6 @@ int POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
|
|||
{
|
||||
commit.StageItems( selection, CHT_MODIFY );
|
||||
|
||||
controls->ForceCursorPosition( false );
|
||||
m_original = *m_editedPoint; // Save the original position
|
||||
controls->SetAutoPan( true );
|
||||
modified = true;
|
||||
|
@ -358,14 +359,14 @@ int POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
|
|||
if( enableAltConstraint != (bool) m_altConstraint ) // alternative constraint
|
||||
setAltConstraint( enableAltConstraint );
|
||||
|
||||
m_editedPoint->SetPosition( controls->GetCursorPosition() );
|
||||
|
||||
if( m_altConstraint )
|
||||
m_altConstraint->Apply();
|
||||
else
|
||||
m_editedPoint->ApplyConstraint();
|
||||
|
||||
m_editedPoint->SetPosition( grid.Align( m_editedPoint->GetPosition() ) );
|
||||
|
||||
m_editedPoint->SetPosition( grid.BestSnapAnchor( evt->Position(),
|
||||
static_cast<BOARD_ITEM*>( item ) ) );
|
||||
updateItem();
|
||||
updatePoints();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue