From 4247e32cd6e9bfc00bb54a948e9f71f6799823ef Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Wed, 23 Jan 2019 07:18:44 -0800 Subject: [PATCH] pcbnew: Disallow new via where DRC is violated Prior to placing the via, we check if the resulting via will pass too close to different nets on different layers. Fixes: lp:1718827 * https://bugs.launchpad.net/kicad/+bug/1718827 --- pcbnew/tools/drawing_tool.cpp | 84 ++++++++++++++++++++++--- pcbnew/tools/footprint_editor_tools.cpp | 5 +- pcbnew/tools/pcb_tool.cpp | 13 +++- pcbnew/tools/pcb_tool.h | 2 +- 4 files changed, 91 insertions(+), 13 deletions(-) diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index 728ff16e0a..c849b6abff 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -1504,20 +1504,86 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) TRACK* findTrack( VIA* aVia ) { const LSET lset = aVia->GetLayerSet(); + std::vector items; + BOX2I bbox = aVia->GetBoundingBox(); + auto view = m_frame->GetGalCanvas()->GetView(); - for( TRACK* track : m_board->Tracks() ) + view->Query( bbox, items ); + + for( auto it : items ) { - if( !(track->GetLayerSet() & lset ).any() ) + BOARD_ITEM* item = static_cast( it.first ); + + if( !(item->GetLayerSet() & lset ).any() ) continue; - if( TestSegmentHit( aVia->GetPosition(), track->GetStart(), track->GetEnd(), - ( track->GetWidth() + aVia->GetWidth() ) / 2 ) ) - return track; + if( auto track = dyn_cast( item ) ) + { + if( TestSegmentHit( aVia->GetPosition(), track->GetStart(), track->GetEnd(), + ( track->GetWidth() + aVia->GetWidth() ) / 2 ) ) + return track; + } } return nullptr; } + + bool hasDRCViolation( VIA* aVia ) + { + const LSET lset = aVia->GetLayerSet(); + std::vector items; + int net = 0; + int clearance = 0; + BOX2I bbox = aVia->GetBoundingBox(); + auto view = m_frame->GetGalCanvas()->GetView(); + + view->Query( bbox, items ); + + for( auto it : items ) + { + BOARD_ITEM* item = static_cast( it.first ); + + if( !(item->GetLayerSet() & lset ).any() ) + continue; + + if( auto track = dyn_cast( item ) ) + { + int max_clearance = std::max( clearance, track->GetClearance() ); + + if( TestSegmentHit( aVia->GetPosition(), track->GetStart(), track->GetEnd(), + ( track->GetWidth() + aVia->GetWidth() ) / 2 + max_clearance ) ) + { + if( net && track->GetNetCode() != net ) + return true; + + net = track->GetNetCode(); + clearance = track->GetClearance(); + } + } + + if( auto mod = dyn_cast( item ) ) + { + for( auto pad : mod->Pads() ) + { + int max_clearance = std::max( clearance, pad->GetClearance() ); + + if( pad->HitTest( aVia->GetBoundingBox(), false, max_clearance ) ) + { + if( net && pad->GetNetCode() != net ) + return true; + + net = pad->GetNetCode(); + clearance = pad->GetClearance(); + } + } + } + } + + return false; + } + + int findStitchedZoneNet( VIA* aVia ) { const auto pos = aVia->GetPosition(); @@ -1572,8 +1638,6 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) // That's not ideal, and is in fact probably worse than forcing snap in // this situation. -// bool do_snap = ( m_frame->Settings().m_magneticTracks == CAPTURE_CURSOR_IN_TRACK_TOOL -// || m_frame->Settings().m_magneticTracks == CAPTURE_ALWAYS ); m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) ); auto via = static_cast( aItem ); wxPoint pos = via->GetPosition(); @@ -1588,12 +1652,15 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) } } - void PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override + bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override { auto via = static_cast( aItem ); int newNet; TRACK* track = findTrack( via ); + if( hasDRCViolation( via ) ) + return false; + if( track ) { aCommit.Modify( track ); @@ -1611,6 +1678,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) via->SetNetCode( newNet ); aCommit.Add( aItem ); + return true; } std::unique_ptr CreateItem() override diff --git a/pcbnew/tools/footprint_editor_tools.cpp b/pcbnew/tools/footprint_editor_tools.cpp index 99bb770d24..73d714a9a5 100644 --- a/pcbnew/tools/footprint_editor_tools.cpp +++ b/pcbnew/tools/footprint_editor_tools.cpp @@ -109,7 +109,7 @@ int MODULE_EDITOR_TOOLS::PlacePad( const TOOL_EVENT& aEvent ) return std::unique_ptr( pad ); } - void PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override + bool PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override { D_PAD* pad = dynamic_cast( aItem ); @@ -118,7 +118,10 @@ int MODULE_EDITOR_TOOLS::PlacePad( const TOOL_EVENT& aEvent ) m_frame->Export_Pad_Settings( pad ); pad->SetLocalCoord(); aCommit.Add( aItem ); + return true; } + + return false; } }; diff --git a/pcbnew/tools/pcb_tool.cpp b/pcbnew/tools/pcb_tool.cpp index fd1dfa4492..91dca71323 100644 --- a/pcbnew/tools/pcb_tool.cpp +++ b/pcbnew/tools/pcb_tool.cpp @@ -132,10 +132,16 @@ void PCB_TOOL::doInteractiveItemPlacement( INTERACTIVE_PLACER_BASE* aPlacer, } else { + auto oldFlags = newItem->GetFlags(); newItem->ClearFlags(); - preview.Remove( newItem.get() ); - aPlacer->PlaceItem( newItem.get(), commit ); + if( !aPlacer->PlaceItem( newItem.get(), commit ) ) + { + newItem->SetFlags( oldFlags ); + continue; + } + + preview.Remove( newItem.get() ); if( newItem->Type() == PCB_MODULE_T ) { @@ -262,7 +268,8 @@ void INTERACTIVE_PLACER_BASE::SnapItem( BOARD_ITEM *aItem ) // Base implementation performs no snapping } -void INTERACTIVE_PLACER_BASE::PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) +bool INTERACTIVE_PLACER_BASE::PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) { aCommit.Add( aItem ); + return true; } diff --git a/pcbnew/tools/pcb_tool.h b/pcbnew/tools/pcb_tool.h index d10582a3e5..aab18833d7 100644 --- a/pcbnew/tools/pcb_tool.h +++ b/pcbnew/tools/pcb_tool.h @@ -53,7 +53,7 @@ struct INTERACTIVE_PLACER_BASE { virtual std::unique_ptr CreateItem() = 0; virtual void SnapItem( BOARD_ITEM *aItem ); - virtual void PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ); + virtual bool PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ); PCB_EDIT_FRAME* m_frame; BOARD* m_board;