diff --git a/pcbnew/drc/drc_engine.cpp b/pcbnew/drc/drc_engine.cpp index 0a07dcf092..a66b790d32 100644 --- a/pcbnew/drc/drc_engine.cpp +++ b/pcbnew/drc/drc_engine.cpp @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include void drcPrintDebugMessage( int level, const wxString& msg, const char *function, int line ) { @@ -1204,6 +1207,39 @@ bool DRC_ENGINE::IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, } +std::shared_ptr DRC_ENGINE::GetShape( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ) +{ + if( aItem->Type() == PCB_PAD_T && !static_cast( aItem )->FlashLayer( aLayer ) ) + { + PAD* aPad = static_cast( aItem ); + + if( aPad->GetAttribute() == PAD_ATTRIB_PTH ) + { + BOARD_DESIGN_SETTINGS& bds = aPad->GetBoard()->GetDesignSettings(); + + // Note: drill size represents finish size, which means the actual holes size is the + // plating thickness larger. + auto hole = static_cast( aPad->GetEffectiveHoleShape()->Clone() ); + hole->SetWidth( hole->GetWidth() + bds.GetHolePlatingThickness() ); + return std::make_shared( *hole ); + } + + return std::make_shared(); + } + + return aItem->GetEffectiveShape( aLayer ); +} + + +bool DRC_ENGINE::IsNetTie( BOARD_ITEM* aItem ) +{ + if( aItem->GetParent() && aItem->GetParent()->Type() == PCB_FOOTPRINT_T ) + return static_cast( aItem->GetParent() )->IsNetTie(); + + return false; +} + + DRC_TEST_PROVIDER* DRC_ENGINE::GetTestProvider( const wxString& name ) const { for( auto prov : m_testProviders ) diff --git a/pcbnew/drc/drc_engine.h b/pcbnew/drc/drc_engine.h index 23e7e97564..29b5e0b7a4 100644 --- a/pcbnew/drc/drc_engine.h +++ b/pcbnew/drc/drc_engine.h @@ -28,6 +28,8 @@ #include #include +#include + #include @@ -166,6 +168,10 @@ public: static bool IsNetADiffPair( BOARD* aBoard, NETINFO_ITEM* aNet, int& aNetP, int& aNetN ); + static bool IsNetTie( BOARD_ITEM* aItem ); + + static std::shared_ptr GetShape( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ); + private: void addRule( DRC_RULE* rule ) { diff --git a/pcbnew/drc/drc_test_provider_copper_clearance.cpp b/pcbnew/drc/drc_test_provider_copper_clearance.cpp index cb3d5fe87a..e7e8648543 100644 --- a/pcbnew/drc/drc_test_provider_copper_clearance.cpp +++ b/pcbnew/drc/drc_test_provider_copper_clearance.cpp @@ -233,39 +233,6 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::Run() } -static std::shared_ptr getShape( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ) -{ - if( aItem->Type() == PCB_PAD_T && !static_cast( aItem )->FlashLayer( aLayer ) ) - { - PAD* aPad = static_cast( aItem ); - - if( aPad->GetAttribute() == PAD_ATTRIB_PTH ) - { - BOARD_DESIGN_SETTINGS& bds = aPad->GetBoard()->GetDesignSettings(); - - // Note: drill size represents finish size, which means the actual holes size is the - // plating thickness larger. - auto hole = static_cast( aPad->GetEffectiveHoleShape()->Clone() ); - hole->SetWidth( hole->GetWidth() + bds.GetHolePlatingThickness() ); - return std::make_shared( *hole ); - } - - return std::make_shared(); - } - - return aItem->GetEffectiveShape( aLayer ); -} - - -static bool isNetTie( BOARD_ITEM* aItem ) -{ - if( aItem->GetParent() && aItem->GetParent()->Type() == PCB_FOOTPRINT_T ) - return static_cast( aItem->GetParent() )->IsNetTie(); - - return false; -} - - bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( TRACK* track, SHAPE* trackShape, PCB_LAYER_ID layer, BOARD_ITEM* other ) @@ -311,7 +278,7 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackAgainstItem( TRACK* track, SHA } } - std::shared_ptr otherShape = getShape( other, layer ); + std::shared_ptr otherShape = DRC_ENGINE::GetShape( other, layer ); if( trackShape->Collide( otherShape.get(), clearance - m_drcEpsilon, &actual, &pos ) ) { @@ -485,7 +452,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances() { // It would really be better to know what particular nets a nettie // should allow, but for now it is what it is. - if( isNetTie( other ) ) + if( DRC_ENGINE::IsNetTie( other ) ) return false; auto otherCItem = dynamic_cast( other ); @@ -556,7 +523,7 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadAgainstItem( PAD* pad, SHAPE* pa if( !testClearance && !testShorting && !testHoles ) return false; - std::shared_ptr otherShape = getShape( other, layer ); + std::shared_ptr otherShape = DRC_ENGINE::GetShape( other, layer ); DRC_CONSTRAINT constraint; int clearance; int actual; @@ -698,7 +665,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadClearances( ) for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() ) { - std::shared_ptr padShape = getShape( pad, layer ); + std::shared_ptr padShape = DRC_ENGINE::GetShape( pad, layer ); m_copperTree.QueryColliding( pad, layer, layer, // Filter: diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp index fa2b17c826..e34f776bc3 100644 --- a/pcbnew/tools/drawing_tool.cpp +++ b/pcbnew/tools/drawing_tool.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +55,7 @@ #include #include #include +#include using SCOPED_DRAW_MODE = SCOPED_SET_RESET; @@ -146,8 +147,11 @@ protected: DRAWING_TOOL::DRAWING_TOOL() : PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ), - m_view( nullptr ), m_controls( nullptr ), - m_board( nullptr ), m_frame( nullptr ), m_mode( MODE::NONE ), + m_view( nullptr ), + m_controls( nullptr ), + m_board( nullptr ), + m_frame( nullptr ), + m_mode( MODE::NONE ), m_lineWidth( 1 ) { } @@ -2067,11 +2071,43 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) { struct VIA_PLACER : public INTERACTIVE_PLACER_BASE { - PCB_GRID_HELPER m_gridHelper; + PCB_BASE_EDIT_FRAME* m_frame; + PCB_GRID_HELPER m_gridHelper; + std::shared_ptr m_drcEngine; + int m_drcEpsilon; + int m_worstClearance; + bool m_flaggedDRC; VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) : - m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ) - {} + m_frame( aFrame ), + m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ), + m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ), + m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ), + m_worstClearance( 0 ), + m_flaggedDRC( false ) + { + try + { + m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() ); + + DRC_CONSTRAINT constraint; + + if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) ) + m_worstClearance = constraint.GetValue().Min(); + + if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) ) + m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() ); + + for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() ) + { + for( PAD* pad : footprint->Pads() ) + m_worstClearance = std::max( m_worstClearance, pad->GetLocalClearance() ); + } + } + catch( PARSE_ERROR& pe ) + { + } + } virtual ~VIA_PLACER() { @@ -2096,7 +2132,7 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) if( !(item->GetLayerSet() & lset ).any() ) continue; - if( auto track = dyn_cast( item ) ) + if( TRACK* track = dyn_cast( item ) ) { if( TestSegmentHit( position, track->GetStart(), track->GetEnd(), ( track->GetWidth() + aVia->GetWidth() ) / 2 ) ) @@ -2106,10 +2142,11 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) TRACK* return_track = nullptr; int min_d = std::numeric_limits::max(); - for( auto track : possible_tracks ) + + for( TRACK* track : possible_tracks ) { SEG test( track->GetStart(), track->GetEnd() ); - auto dist = ( test.NearestPoint( position ) - position ).EuclideanNorm(); + int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm(); if( dist < min_d ) { @@ -2121,85 +2158,64 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) return return_track; } - - bool hasDRCViolation( VIA* aVia ) + bool hasDRCViolation( VIA* aVia, BOARD_ITEM* aOther ) { - const LSET lset = aVia->GetLayerSet(); - wxPoint position = aVia->GetPosition(); - int drillRadius = aVia->GetDrillValue() / 2; - BOX2I bbox = aVia->GetBoundingBox(); + // It would really be better to know what particular nets a nettie should allow, + // but for now it is what it is. + if( DRC_ENGINE::IsNetTie( aOther ) ) + return false; - std::vector items; - int net = 0; - int clearance = 0; - auto view = m_frame->GetCanvas()->GetView(); - int holeToHoleMin = m_frame->GetBoard()->GetDesignSettings().m_HoleToHoleMin; + BOARD_CONNECTED_ITEM* cItem = dynamic_cast( aOther ); - view->Query( bbox, items ); + if( cItem && cItem->GetNetCode() == aVia->GetNetCode() ) + return false; - for( std::pair it : items ) + for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() ) { - BOARD_ITEM* item = static_cast( it.first ); + DRC_CONSTRAINT constraint = m_drcEngine->EvalRulesForItems( CLEARANCE_CONSTRAINT, + aVia, aOther, layer ); + int clearance = constraint.GetValue().Min(); - if( !(item->GetLayerSet() & lset ).any() ) - continue; - - if( TRACK* track = dyn_cast( item ) ) + if( clearance >= 0 ) { - int max_clearance = std::max( clearance, - track->GetOwnClearance( track->GetLayer() ) ); + std::shared_ptr viaShape = DRC_ENGINE::GetShape( aVia, layer ); + std::shared_ptr otherShape = DRC_ENGINE::GetShape( aOther, layer ); - if( TestSegmentHit( position, track->GetStart(), track->GetEnd(), - ( track->GetWidth() + aVia->GetWidth() ) / 2 + max_clearance ) ) - { - if( net && track->GetNetCode() != net ) - return true; - - net = track->GetNetCode(); - clearance = track->GetOwnClearance( track->GetLayer() ); - } - } - - if( VIA* via = dyn_cast( item ) ) - { - int dist = KiROUND( GetLineLength( position, via->GetPosition() ) ); - - if( dist < drillRadius + via->GetDrillValue() / 2 + holeToHoleMin ) + if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) ) return true; } - - if( FOOTPRINT* footprint = dynamic_cast( item ) ) - { - for( PAD* pad : footprint->Pads() ) - { - for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() ) - { - int max_clearance = std::max( clearance, pad->GetOwnClearance( layer ) ); - - if( pad->HitTest( aVia->GetBoundingBox(), false, max_clearance ) ) - { - if( net && pad->GetNetCode() != net ) - return true; - - net = pad->GetNetCode(); - clearance = pad->GetOwnClearance( layer ); - } - - if( pad->GetDrillSize().x && pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE ) - { - int dist = KiROUND( GetLineLength( position, pad->GetPosition() ) ); - - if( dist < drillRadius + pad->GetDrillSize().x / 2 + holeToHoleMin ) - return true; - } - } - } - } } return false; } + bool hasDRCViolation( VIA* aVia ) + { + std::vector items; + std::set handled; + BOX2I bbox = aVia->GetBoundingBox(); + + bbox.Inflate( m_worstClearance ); + m_frame->GetCanvas()->GetView()->Query( bbox, items ); + + for( std::pair it : items ) + { + BOARD_ITEM* item = dynamic_cast( it.first ); + + if( !item || item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) + continue; + + if( handled.count( item ) ) + continue; + + if( hasDRCViolation( aVia, item ) ) + return true; + + handled.insert( item ); + } + + return false; + } int findStitchedZoneNet( VIA* aVia ) { @@ -2270,7 +2286,15 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent ) TRACK* track = findTrack( via ); if( hasDRCViolation( via ) ) + { + m_frame->ShowInfoBarError( _( "Via location violates DRC." ) ); + m_flaggedDRC = true; return false; + } + else if( m_flaggedDRC ) + { + m_frame->GetInfoBar()->Dismiss(); + } if( track ) {