From 03e642a8db445e59c8a9d31662e0f3ae3237f25c Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Wed, 3 Oct 2018 17:15:54 -0700 Subject: [PATCH] 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 --- pcbnew/tools/drawing_tool.cpp | 67 ++++++++++++++++++----------------- pcbnew/tools/drawing_tool.h | 3 +- pcbnew/tools/edit_tool.cpp | 2 ++ pcbnew/tools/grid_helper.cpp | 31 ++++++++++------ pcbnew/tools/grid_helper.h | 17 ++++++--- pcbnew/tools/point_editor.cpp | 9 ++--- 6 files changed, 78 insertions(+), 51 deletions(-) diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index b33aaf4ff1..90ccbd2225 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -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( 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( 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 ) ); } } diff --git a/pcbnew/tools/drawing_tool.h b/pcbnew/tools/drawing_tool.h index df5b883c9b..3f175ff04e 100644 --- a/pcbnew/tools/drawing_tool.h +++ b/pcbnew/tools/drawing_tool.h @@ -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() diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index 69a62363fc..0d6e73f85e 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -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 ) ) diff --git a/pcbnew/tools/grid_helper.cpp b/pcbnew/tools/grid_helper.cpp index b4e20ef04c..c383af8d9a 100644 --- a/pcbnew/tools/grid_helper.cpp +++ b/pcbnew/tools/grid_helper.cpp @@ -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 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 ); diff --git a/pcbnew/tools/grid_helper.h b/pcbnew/tools/grid_helper.h index dd5db21a53..d68fee50ed 100644 --- a/pcbnew/tools/grid_helper.h +++ b/pcbnew/tools/grid_helper.h @@ -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 m_anchors; @@ -103,8 +107,13 @@ private: PCB_BASE_FRAME* m_frame; OPT 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 diff --git a/pcbnew/tools/point_editor.cpp b/pcbnew/tools/point_editor.cpp index 02aac88eba..7cc39fa5e4 100644 --- a/pcbnew/tools/point_editor.cpp +++ b/pcbnew/tools/point_editor.cpp @@ -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( item ) ) ); updateItem(); updatePoints(); }