Move hole testing to DRC client and improve performance.
This commit is contained in:
parent
0658d297e5
commit
75b139c7b4
|
@ -233,6 +233,7 @@ set( PCBNEW_MICROWAVE_SRCS
|
||||||
|
|
||||||
set( PCBNEW_DRC_SRCS
|
set( PCBNEW_DRC_SRCS
|
||||||
drc/drc_courtyard_tester.cpp
|
drc/drc_courtyard_tester.cpp
|
||||||
|
drc/drc_drilled_hole_tester.cpp
|
||||||
drc/drc.cpp
|
drc/drc.cpp
|
||||||
drc/drc_clearance_test_functions.cpp
|
drc/drc_clearance_test_functions.cpp
|
||||||
drc/drc_rule_parser.cpp
|
drc/drc_rule_parser.cpp
|
||||||
|
|
|
@ -24,7 +24,13 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file class_track.h
|
* @file class_track.h
|
||||||
* @brief Definitions for tracks, vias and zones.
|
* @brief A single base class (TRACK) represents both tracks and vias, with subclasses
|
||||||
|
* for curved tracks (ARC) and vias (VIA). All told there are three KICAD_Ts:
|
||||||
|
* PCB_TRACK_T, PCB_ARC_T, and PCB_VIA_T.
|
||||||
|
*
|
||||||
|
* For vias there is a further VIATYPE which indicates THROUGH, BLIND_BURIED, or
|
||||||
|
* MICROVIA, which are supported by the synthetic KICAD_Ts PCB_LOCATE_STDVIA_T,
|
||||||
|
* PCB_LOCATE_BBVIA_T, and PCB_LOCATE_UVIA_T.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CLASS_TRACK_H
|
#ifndef CLASS_TRACK_H
|
||||||
|
@ -367,6 +373,9 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VIATYPE GetViaType() const { return m_ViaType; }
|
||||||
|
void SetViaType( VIATYPE aViaType ) { m_ViaType = aViaType; }
|
||||||
|
|
||||||
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
|
bool IsOnLayer( PCB_LAYER_ID aLayer ) const override;
|
||||||
|
|
||||||
virtual LSET GetLayerSet() const override;
|
virtual LSET GetLayerSet() const override;
|
||||||
|
@ -429,16 +438,6 @@ public:
|
||||||
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
VIATYPE GetViaType() const
|
|
||||||
{
|
|
||||||
return m_ViaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetViaType( VIATYPE aViaType )
|
|
||||||
{
|
|
||||||
m_ViaType = aViaType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function SetDrill
|
* Function SetDrill
|
||||||
* sets the drill value for vias.
|
* sets the drill value for vias.
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <tool/tool_manager.h>
|
#include <tool/tool_manager.h>
|
||||||
#include <tools/pcb_actions.h>
|
#include <tools/pcb_actions.h>
|
||||||
#include <tools/pcb_tool_base.h>
|
#include <tools/pcb_tool_base.h>
|
||||||
|
#include <tools/zone_filler_tool.h>
|
||||||
#include <kiface_i.h>
|
#include <kiface_i.h>
|
||||||
#include <pcbnew.h>
|
#include <pcbnew.h>
|
||||||
#include <drc/drc.h>
|
#include <drc/drc.h>
|
||||||
|
@ -53,7 +54,7 @@
|
||||||
#include <geometry/shape_arc.h>
|
#include <geometry/shape_arc.h>
|
||||||
#include <drc/drc_item.h>
|
#include <drc/drc_item.h>
|
||||||
#include <drc/drc_courtyard_tester.h>
|
#include <drc/drc_courtyard_tester.h>
|
||||||
#include <tools/zone_filler_tool.h>
|
#include <drc/drc_drilled_hole_tester.h>
|
||||||
#include <confirm.h>
|
#include <confirm.h>
|
||||||
#include "drc_rule_parser.h"
|
#include "drc_rule_parser.h"
|
||||||
|
|
||||||
|
@ -776,192 +777,9 @@ void DRC::testPad2Pad()
|
||||||
|
|
||||||
void DRC::testDrilledHoles()
|
void DRC::testDrilledHoles()
|
||||||
{
|
{
|
||||||
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
|
DRC_DRILLED_HOLE_TESTER tester( [&]( MARKER_PCB* aMarker ) { addMarkerToPcb( aMarker ); } );
|
||||||
|
|
||||||
// Test drilled holes to minimize drill bit breakage.
|
tester.RunDRC( userUnits(), *m_pcb );
|
||||||
//
|
|
||||||
// Check pad & std. via circular holes for hole-to-hole-min (non-circular holes are milled)
|
|
||||||
// Check pad & std. via holes for via-min-drill (minimum hole classification)
|
|
||||||
// Check uvia holes for uvia-min-drill (laser drill classification)
|
|
||||||
|
|
||||||
struct DRILLED_HOLE
|
|
||||||
{
|
|
||||||
wxPoint m_location;
|
|
||||||
int m_drillRadius = 0;
|
|
||||||
BOARD_ITEM* m_owner = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<DRILLED_HOLE> holes;
|
|
||||||
DRILLED_HOLE hole;
|
|
||||||
wxString msg;
|
|
||||||
|
|
||||||
for( MODULE* mod : m_pcb->Modules() )
|
|
||||||
{
|
|
||||||
for( D_PAD* pad : mod->Pads( ) )
|
|
||||||
{
|
|
||||||
int holeSize = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
|
|
||||||
|
|
||||||
if( holeSize == 0 )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
NETCLASS* netclass = pad->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
|
|
||||||
: pad->GetNetClass().get();
|
|
||||||
int minHole = bds.m_MinThroughDrill;
|
|
||||||
wxString minHoleSource = _( "board" );
|
|
||||||
|
|
||||||
std::vector<DRC_SELECTOR*> matched;
|
|
||||||
|
|
||||||
MatchSelectors( bds.m_DRCRuleSelectors, pad, netclass, nullptr, nullptr, &matched );
|
|
||||||
|
|
||||||
for( DRC_SELECTOR* selector : matched )
|
|
||||||
{
|
|
||||||
if( selector->m_Rule->m_Hole > minHole )
|
|
||||||
{
|
|
||||||
minHole = selector->m_Rule->m_Hole;
|
|
||||||
minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !bds.Ignore( DRCE_TOO_SMALL_PAD_DRILL ) && holeSize < minHole )
|
|
||||||
{
|
|
||||||
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_PAD_DRILL );
|
|
||||||
|
|
||||||
msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ),
|
|
||||||
minHoleSource,
|
|
||||||
MessageTextFromValue( userUnits(), minHole, true ),
|
|
||||||
MessageTextFromValue( userUnits(), holeSize, true ) );
|
|
||||||
|
|
||||||
drcItem->SetErrorMessage( msg );
|
|
||||||
drcItem->SetItems( pad );
|
|
||||||
|
|
||||||
MARKER_PCB* marker = new MARKER_PCB( drcItem, pad->GetPosition() );
|
|
||||||
addMarkerToPcb( marker );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
|
||||||
{
|
|
||||||
hole.m_location = pad->GetPosition();
|
|
||||||
hole.m_drillRadius = pad->GetDrillSize().x / 2;
|
|
||||||
hole.m_owner = pad;
|
|
||||||
holes.push_back( hole );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for( TRACK* track : m_pcb->Tracks() )
|
|
||||||
{
|
|
||||||
VIA* via = dynamic_cast<VIA*>( track );
|
|
||||||
|
|
||||||
if( !via )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
NETCLASS* netclass = via->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
|
|
||||||
: via->GetNetClass().get();
|
|
||||||
int minHole = 0;
|
|
||||||
wxString minHoleSource;
|
|
||||||
|
|
||||||
std::vector<DRC_SELECTOR*> matched;
|
|
||||||
|
|
||||||
MatchSelectors( bds.m_DRCRuleSelectors, via, netclass, nullptr, nullptr, &matched );
|
|
||||||
|
|
||||||
for( DRC_SELECTOR* selector : matched )
|
|
||||||
{
|
|
||||||
if( selector->m_Rule->m_Hole > minHole )
|
|
||||||
{
|
|
||||||
minHole = selector->m_Rule->m_Hole;
|
|
||||||
minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( via->GetViaType() == VIATYPE::MICROVIA )
|
|
||||||
{
|
|
||||||
if( bds.m_MicroViasMinDrill > minHole )
|
|
||||||
{
|
|
||||||
minHole = bds.m_MicroViasMinDrill;
|
|
||||||
minHoleSource = _( "board" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !bds.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) && via->GetDrillValue() < minHole )
|
|
||||||
{
|
|
||||||
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA_DRILL );
|
|
||||||
|
|
||||||
msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ),
|
|
||||||
minHoleSource,
|
|
||||||
MessageTextFromValue( userUnits(), minHole, true ),
|
|
||||||
MessageTextFromValue( userUnits(), via->GetDrillValue(), true ) );
|
|
||||||
|
|
||||||
drcItem->SetErrorMessage( msg );
|
|
||||||
drcItem->SetItems( via );
|
|
||||||
|
|
||||||
MARKER_PCB* marker = new MARKER_PCB( drcItem, via->GetPosition() );
|
|
||||||
addMarkerToPcb( marker );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( bds.m_MinThroughDrill > minHole )
|
|
||||||
{
|
|
||||||
minHole = bds.m_MinThroughDrill;
|
|
||||||
minHoleSource = _( "board" );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !bds.Ignore( DRCE_TOO_SMALL_VIA_DRILL ) && via->GetDrillValue() < minHole )
|
|
||||||
{
|
|
||||||
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_DRILL );
|
|
||||||
|
|
||||||
msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ),
|
|
||||||
minHoleSource,
|
|
||||||
MessageTextFromValue( userUnits(), minHole, true ),
|
|
||||||
MessageTextFromValue( userUnits(), via->GetDrillValue(), true ) );
|
|
||||||
|
|
||||||
drcItem->SetErrorMessage( msg );
|
|
||||||
drcItem->SetItems( via );
|
|
||||||
|
|
||||||
MARKER_PCB* marker = new MARKER_PCB( drcItem, via->GetPosition() );
|
|
||||||
addMarkerToPcb( marker );
|
|
||||||
}
|
|
||||||
|
|
||||||
hole.m_location = via->GetPosition();
|
|
||||||
hole.m_drillRadius = via->GetDrillValue() / 2;
|
|
||||||
hole.m_owner = via;
|
|
||||||
holes.push_back( hole );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( bds.m_HoleToHoleMin == 0 || bds.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) )
|
|
||||||
return;
|
|
||||||
|
|
||||||
for( size_t ii = 0; ii < holes.size(); ++ii )
|
|
||||||
{
|
|
||||||
const DRILLED_HOLE& refHole = holes[ ii ];
|
|
||||||
|
|
||||||
for( size_t jj = ii + 1; jj < holes.size(); ++jj )
|
|
||||||
{
|
|
||||||
const DRILLED_HOLE& checkHole = holes[ jj ];
|
|
||||||
|
|
||||||
// Holes with identical locations are allowable
|
|
||||||
if( checkHole.m_location == refHole.m_location )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int actual = KiROUND( GetLineLength( checkHole.m_location, refHole.m_location ) );
|
|
||||||
actual = std::max( 0, actual - checkHole.m_drillRadius - refHole.m_drillRadius );
|
|
||||||
|
|
||||||
if( actual < bds.m_HoleToHoleMin )
|
|
||||||
{
|
|
||||||
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_DRILLED_HOLES_TOO_CLOSE );
|
|
||||||
|
|
||||||
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
|
|
||||||
MessageTextFromValue( userUnits(), bds.m_HoleToHoleMin, true ),
|
|
||||||
MessageTextFromValue( userUnits(), actual, true ) );
|
|
||||||
|
|
||||||
drcItem->SetErrorMessage( msg );
|
|
||||||
drcItem->SetItems( refHole.m_owner, checkHole.m_owner );
|
|
||||||
|
|
||||||
MARKER_PCB* marker = new MARKER_PCB( drcItem, refHole.m_location );
|
|
||||||
addMarkerToPcb( marker );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1890,9 +1708,9 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li
|
||||||
|
|
||||||
void DRC::doCourtyardsDrc()
|
void DRC::doCourtyardsDrc()
|
||||||
{
|
{
|
||||||
DRC_COURTYARD_TESTER drc_overlap( [&]( MARKER_PCB* aMarker ) { addMarkerToPcb( aMarker ); } );
|
DRC_COURTYARD_TESTER tester( [&]( MARKER_PCB* aMarker ) { addMarkerToPcb( aMarker ); } );
|
||||||
|
|
||||||
drc_overlap.RunDRC( *m_pcb );
|
tester.RunDRC( userUnits(), *m_pcb );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,26 +33,15 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to enable courtyard DRC debug tracing.
|
|
||||||
*
|
|
||||||
* Use "KICAD_DRC_COURTYARD" to enable.
|
|
||||||
*
|
|
||||||
* @ingroup trace_env_vars
|
|
||||||
*/
|
|
||||||
static const wxChar* DRC_COURTYARD_TRACE = wxT( "KICAD_DRC_COURTYARD" );
|
|
||||||
|
|
||||||
|
|
||||||
DRC_COURTYARD_TESTER::DRC_COURTYARD_TESTER( MARKER_HANDLER aMarkerHandler ) :
|
DRC_COURTYARD_TESTER::DRC_COURTYARD_TESTER( MARKER_HANDLER aMarkerHandler ) :
|
||||||
DRC_TEST_PROVIDER( aMarkerHandler )
|
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
|
bool DRC_COURTYARD_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
|
||||||
{
|
{
|
||||||
wxLogTrace( DRC_COURTYARD_TRACE, "Running DRC: Courtyard" );
|
|
||||||
|
|
||||||
// Detects missing (or malformed) footprint courtyards and courtyard incursions (for those
|
// Detects missing (or malformed) footprint courtyards and courtyard incursions (for those
|
||||||
// with a courtyard).
|
// with a courtyard).
|
||||||
wxString msg;
|
wxString msg;
|
||||||
|
@ -96,8 +85,6 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
|
||||||
|
|
||||||
if( !aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) )
|
if( !aBoard.GetDesignSettings().Ignore( DRCE_OVERLAPPING_FOOTPRINTS ) )
|
||||||
{
|
{
|
||||||
wxLogTrace( DRC_COURTYARD_TRACE, "Checking for courtyard overlap" );
|
|
||||||
|
|
||||||
for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ )
|
for( auto it1 = aBoard.Modules().begin(); it1 != aBoard.Modules().end(); it1++ )
|
||||||
{
|
{
|
||||||
MODULE* footprint = *it1;
|
MODULE* footprint = *it1;
|
||||||
|
@ -162,8 +149,6 @@ bool DRC_COURTYARD_TESTER::RunDRC( BOARD& aBoard ) const
|
||||||
if( !aBoard.GetDesignSettings().Ignore( DRCE_PTH_IN_COURTYARD )
|
if( !aBoard.GetDesignSettings().Ignore( DRCE_PTH_IN_COURTYARD )
|
||||||
|| !aBoard.GetDesignSettings().Ignore( DRCE_NPTH_IN_COURTYARD ) )
|
|| !aBoard.GetDesignSettings().Ignore( DRCE_NPTH_IN_COURTYARD ) )
|
||||||
{
|
{
|
||||||
wxLogTrace( DRC_COURTYARD_TRACE, "Checking for through-holes in courtyards" );
|
|
||||||
|
|
||||||
for( MODULE* footprint : aBoard.Modules() )
|
for( MODULE* footprint : aBoard.Modules() )
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
|
SHAPE_POLY_SET& footprintFront = footprint->GetPolyCourtyardFront();
|
||||||
|
|
|
@ -25,13 +25,11 @@
|
||||||
#ifndef DRC_COURTYARD_OVERLAP__H
|
#ifndef DRC_COURTYARD_OVERLAP__H
|
||||||
#define DRC_COURTYARD_OVERLAP__H
|
#define DRC_COURTYARD_OVERLAP__H
|
||||||
|
|
||||||
#include <class_board.h>
|
|
||||||
|
|
||||||
#include <drc/drc_provider.h>
|
#include <drc/drc_provider.h>
|
||||||
|
|
||||||
/**
|
|
||||||
* A class that provides the courtyard-based DRC checks.
|
class BOARD;
|
||||||
*/
|
|
||||||
class DRC_COURTYARD_TESTER : public DRC_TEST_PROVIDER
|
class DRC_COURTYARD_TESTER : public DRC_TEST_PROVIDER
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -39,7 +37,7 @@ public:
|
||||||
|
|
||||||
virtual ~DRC_COURTYARD_TESTER() {};
|
virtual ~DRC_COURTYARD_TESTER() {};
|
||||||
|
|
||||||
bool RunDRC( BOARD& aBoard ) const override;
|
bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // DRC_COURTYARD_OVERLAP__H
|
#endif // DRC_COURTYARD_OVERLAP__H
|
||||||
|
|
|
@ -0,0 +1,308 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, you may find one here:
|
||||||
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||||
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <drc/drc_drilled_hole_tester.h>
|
||||||
|
|
||||||
|
#include <class_module.h>
|
||||||
|
#include <drc/drc.h>
|
||||||
|
|
||||||
|
#include <widgets/ui_common.h>
|
||||||
|
|
||||||
|
|
||||||
|
DRC_DRILLED_HOLE_TESTER::DRC_DRILLED_HOLE_TESTER( MARKER_HANDLER aMarkerHandler ) :
|
||||||
|
DRC_TEST_PROVIDER( std::move( aMarkerHandler ) ),
|
||||||
|
m_units( EDA_UNITS::MILLIMETRES ),
|
||||||
|
m_board( nullptr ),
|
||||||
|
m_largestRadius( 0 )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DRC_DRILLED_HOLE_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
// Test drilled holes to minimize drill bit breakage.
|
||||||
|
//
|
||||||
|
// Check pad & std. via circular holes for hole-to-hole-min (non-circular holes are milled)
|
||||||
|
// Check pad & std. via holes for via-min-drill (minimum hole classification)
|
||||||
|
// Check uvia holes for uvia-min-drill (laser drill classification)
|
||||||
|
|
||||||
|
m_units = aUnits;
|
||||||
|
m_board = &aBoard;
|
||||||
|
m_holes.clear();
|
||||||
|
m_largestRadius = 0;
|
||||||
|
|
||||||
|
for( MODULE* mod : aBoard.Modules() )
|
||||||
|
{
|
||||||
|
for( D_PAD* pad : mod->Pads( ) )
|
||||||
|
success |= checkPad( pad );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( TRACK* track : aBoard.Tracks() )
|
||||||
|
{
|
||||||
|
VIA* via = dynamic_cast<VIA*>( track );
|
||||||
|
|
||||||
|
if( via )
|
||||||
|
{
|
||||||
|
if( via->GetViaType() == VIATYPE::MICROVIA )
|
||||||
|
success |= checkMicroVia( via );
|
||||||
|
else
|
||||||
|
success |= checkVia( via );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
success |= checkHoles();
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DRC_DRILLED_HOLE_TESTER::checkPad( D_PAD* aPad )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
bool success = true;
|
||||||
|
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||||
|
|
||||||
|
int holeSize = std::min( aPad->GetDrillSize().x, aPad->GetDrillSize().y );
|
||||||
|
|
||||||
|
if( holeSize == 0 )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if( !bds.Ignore( DRCE_TOO_SMALL_PAD_DRILL ) )
|
||||||
|
{
|
||||||
|
NETCLASS* netclass = aPad->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
|
||||||
|
: aPad->GetNetClass().get();
|
||||||
|
int minHole = bds.m_MinThroughDrill;
|
||||||
|
wxString minHoleSource = _( "board" );
|
||||||
|
|
||||||
|
std::vector<DRC_SELECTOR*> matched;
|
||||||
|
MatchSelectors( bds.m_DRCRuleSelectors, aPad, netclass, nullptr, nullptr, &matched );
|
||||||
|
|
||||||
|
for( DRC_SELECTOR* selector : matched )
|
||||||
|
{
|
||||||
|
if( selector->m_Rule->m_Hole > minHole )
|
||||||
|
{
|
||||||
|
minHole = selector->m_Rule->m_Hole;
|
||||||
|
minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( holeSize < minHole )
|
||||||
|
{
|
||||||
|
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_PAD_DRILL );
|
||||||
|
|
||||||
|
msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ),
|
||||||
|
minHoleSource,
|
||||||
|
MessageTextFromValue( m_units, minHole, true ),
|
||||||
|
MessageTextFromValue( m_units, holeSize, true ) );
|
||||||
|
|
||||||
|
drcItem->SetErrorMessage( msg );
|
||||||
|
drcItem->SetItems( aPad );
|
||||||
|
|
||||||
|
HandleMarker( new MARKER_PCB( drcItem, aPad->GetPosition() ) );
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !bds.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) && bds.m_HoleToHoleMin != 0 )
|
||||||
|
{
|
||||||
|
if( aPad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
|
||||||
|
addHole( aPad->GetPosition(), aPad->GetDrillSize().x / 2, aPad );
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DRC_DRILLED_HOLE_TESTER::checkVia( VIA* via )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
bool success = true;
|
||||||
|
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||||
|
|
||||||
|
if( !bds.Ignore( DRCE_TOO_SMALL_VIA_DRILL ) )
|
||||||
|
{
|
||||||
|
NETCLASS* netclass = via->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
|
||||||
|
: via->GetNetClass().get();
|
||||||
|
int minHole = bds.m_MinThroughDrill;
|
||||||
|
wxString minHoleSource = _( "board" );
|
||||||
|
|
||||||
|
std::vector<DRC_SELECTOR*> matched;
|
||||||
|
MatchSelectors( bds.m_DRCRuleSelectors, via, netclass, nullptr, nullptr, &matched );
|
||||||
|
|
||||||
|
for( DRC_SELECTOR* selector : matched )
|
||||||
|
{
|
||||||
|
if( selector->m_Rule->m_Hole > minHole )
|
||||||
|
{
|
||||||
|
minHole = selector->m_Rule->m_Hole;
|
||||||
|
minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( via->GetDrillValue() < minHole )
|
||||||
|
{
|
||||||
|
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_VIA_DRILL );
|
||||||
|
|
||||||
|
msg.Printf( drcItem->GetErrorText() + _( " (%s min hole %s; actual %s)" ),
|
||||||
|
minHoleSource,
|
||||||
|
MessageTextFromValue( m_units, minHole, true ),
|
||||||
|
MessageTextFromValue( m_units, via->GetDrillValue(), true ) );
|
||||||
|
|
||||||
|
drcItem->SetErrorMessage( msg );
|
||||||
|
drcItem->SetItems( via );
|
||||||
|
|
||||||
|
HandleMarker( new MARKER_PCB( drcItem, via->GetPosition() ) );
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !bds.Ignore( DRCE_DRILLED_HOLES_TOO_CLOSE ) && bds.m_HoleToHoleMin != 0 )
|
||||||
|
{
|
||||||
|
addHole( via->GetPosition(), via->GetDrillValue() / 2, via );
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DRC_DRILLED_HOLE_TESTER::checkMicroVia( VIA* via )
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
bool success = true;
|
||||||
|
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||||
|
|
||||||
|
if( !bds.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) )
|
||||||
|
{
|
||||||
|
NETCLASS* netclass = via->GetNet()->GetNet() == 0 ? bds.GetDefault().get()
|
||||||
|
: via->GetNetClass().get();
|
||||||
|
int minHole = bds.m_MicroViasMinDrill;
|
||||||
|
wxString minHoleSource = _( "board" );
|
||||||
|
|
||||||
|
std::vector<DRC_SELECTOR*> matched;
|
||||||
|
MatchSelectors( bds.m_DRCRuleSelectors, via, netclass, nullptr, nullptr, &matched );
|
||||||
|
|
||||||
|
for( DRC_SELECTOR* selector : matched )
|
||||||
|
{
|
||||||
|
if( selector->m_Rule->m_Hole > minHole )
|
||||||
|
{
|
||||||
|
minHole = selector->m_Rule->m_Hole;
|
||||||
|
minHoleSource = wxString::Format( _( "'%s' rule" ), selector->m_Rule->m_Name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( via->GetDrillValue() < minHole )
|
||||||
|
{
|
||||||
|
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TOO_SMALL_MICROVIA_DRILL );
|
||||||
|
|
||||||
|
msg.Printf( drcItem->GetErrorText() + _( " (%s minimum %s; actual %s)" ),
|
||||||
|
minHoleSource,
|
||||||
|
MessageTextFromValue( m_units, minHole, true ),
|
||||||
|
MessageTextFromValue( m_units, via->GetDrillValue(), true ) );
|
||||||
|
|
||||||
|
drcItem->SetErrorMessage( msg );
|
||||||
|
drcItem->SetItems( via );
|
||||||
|
|
||||||
|
HandleMarker( new MARKER_PCB( drcItem, via->GetPosition() ) );
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DRC_DRILLED_HOLE_TESTER::addHole( const wxPoint& aLocation, int aRadius, BOARD_ITEM* aOwner )
|
||||||
|
{
|
||||||
|
DRILLED_HOLE hole;
|
||||||
|
|
||||||
|
hole.m_location = aLocation;
|
||||||
|
hole.m_drillRadius = aRadius;
|
||||||
|
hole.m_owner = aOwner;
|
||||||
|
|
||||||
|
m_largestRadius = std::max( m_largestRadius, aRadius );
|
||||||
|
|
||||||
|
m_holes.push_back( hole );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DRC_DRILLED_HOLE_TESTER::checkHoles()
|
||||||
|
{
|
||||||
|
wxString msg;
|
||||||
|
bool success = true;
|
||||||
|
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||||
|
|
||||||
|
// No need to check if we're ignoring DRCE_DRILLED_HOLES_TOO_CLOSE; if we are then we
|
||||||
|
// won't have collected any holes to test.
|
||||||
|
|
||||||
|
// Sort holes by X for performance. In the nested iteration we then need to look at
|
||||||
|
// following holes only while they are within the refHole's neighborhood as defined by
|
||||||
|
// the refHole radius + the minimum hole-to-hole clearance + the largest radius any of
|
||||||
|
// the following holes can have.
|
||||||
|
std::sort( m_holes.begin(), m_holes.end(),
|
||||||
|
[]( const DRILLED_HOLE& a, const DRILLED_HOLE& b )
|
||||||
|
{
|
||||||
|
if( a.m_location.x == b.m_location.x )
|
||||||
|
return a.m_location.y < b.m_location.y;
|
||||||
|
else
|
||||||
|
return a.m_location.x < b.m_location.x;
|
||||||
|
} );
|
||||||
|
|
||||||
|
for( size_t ii = 0; ii < m_holes.size(); ++ii )
|
||||||
|
{
|
||||||
|
const DRILLED_HOLE& refHole = m_holes[ ii ];
|
||||||
|
int neighborhood = refHole.m_drillRadius + bds.m_HoleToHoleMin + m_largestRadius;
|
||||||
|
|
||||||
|
for( size_t jj = ii + 1; jj < m_holes.size(); ++jj )
|
||||||
|
{
|
||||||
|
const DRILLED_HOLE& checkHole = m_holes[ jj ];
|
||||||
|
|
||||||
|
if( refHole.m_location.x + neighborhood < checkHole.m_location.x )
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Holes with identical locations are allowable
|
||||||
|
if( checkHole.m_location == refHole.m_location )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int actual = KiROUND( GetLineLength( checkHole.m_location, refHole.m_location ) );
|
||||||
|
actual = std::max( 0, actual - checkHole.m_drillRadius - refHole.m_drillRadius );
|
||||||
|
|
||||||
|
if( actual < bds.m_HoleToHoleMin )
|
||||||
|
{
|
||||||
|
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_DRILLED_HOLES_TOO_CLOSE );
|
||||||
|
|
||||||
|
msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
|
||||||
|
MessageTextFromValue( m_units, bds.m_HoleToHoleMin, true ),
|
||||||
|
MessageTextFromValue( m_units, actual, true ) );
|
||||||
|
|
||||||
|
drcItem->SetErrorMessage( msg );
|
||||||
|
drcItem->SetItems( refHole.m_owner, checkHole.m_owner );
|
||||||
|
|
||||||
|
HandleMarker( new MARKER_PCB( drcItem, refHole.m_location ) );
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 KiCad Developers, see change_log.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, you may find one here:
|
||||||
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||||
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DRC_DRILLED_HOLE_TESTER__H
|
||||||
|
#define DRC_DRILLED_HOLE_TESTER__H
|
||||||
|
|
||||||
|
#include <drc/drc_provider.h>
|
||||||
|
|
||||||
|
|
||||||
|
class BOARD;
|
||||||
|
class BOARD_ITEM;
|
||||||
|
|
||||||
|
|
||||||
|
class DRC_DRILLED_HOLE_TESTER : public DRC_TEST_PROVIDER
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DRC_DRILLED_HOLE_TESTER( MARKER_HANDLER aMarkerHandler );
|
||||||
|
|
||||||
|
virtual ~DRC_DRILLED_HOLE_TESTER() {};
|
||||||
|
|
||||||
|
bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool checkPad( D_PAD* aPad );
|
||||||
|
bool checkVia( VIA* aVia );
|
||||||
|
bool checkMicroVia( VIA* aVia );
|
||||||
|
|
||||||
|
void addHole( const wxPoint& aLocation, int aRadius, BOARD_ITEM* aOwner );
|
||||||
|
bool checkHoles();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct DRILLED_HOLE
|
||||||
|
{
|
||||||
|
wxPoint m_location;
|
||||||
|
int m_drillRadius = 0;
|
||||||
|
BOARD_ITEM* m_owner = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
EDA_UNITS m_units;
|
||||||
|
BOARD* m_board;
|
||||||
|
std::vector<DRILLED_HOLE> m_holes;
|
||||||
|
int m_largestRadius;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DRC_DRILLED_HOLE_TESTER__H
|
|
@ -50,7 +50,7 @@ public:
|
||||||
* Note: Board is non-const, as some DRC functions modify the board (e.g. zone fill
|
* Note: Board is non-const, as some DRC functions modify the board (e.g. zone fill
|
||||||
* or polygon coalescing)
|
* or polygon coalescing)
|
||||||
*/
|
*/
|
||||||
virtual bool RunDRC( BOARD& aBoard ) const = 0;
|
virtual bool RunDRC( EDA_UNITS aUnits, BOARD& aBoard ) = 0;
|
||||||
|
|
||||||
virtual ~DRC_TEST_PROVIDER() {}
|
virtual ~DRC_TEST_PROVIDER() {}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include <widgets/ui_common.h>
|
#include <widgets/ui_common.h>
|
||||||
|
|
||||||
#include "../board_test_utils.h"
|
#include "../board_test_utils.h"
|
||||||
#include "drc_test_utils.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct COURTYARD_TEST_FIXTURE
|
struct COURTYARD_TEST_FIXTURE
|
||||||
|
@ -294,8 +293,8 @@ static void CheckInvalidsMatchExpected( BOARD& aBoard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DoCourtyardInvalidTest(
|
void DoCourtyardInvalidTest( const COURTYARD_INVALID_CASE& aCase,
|
||||||
const COURTYARD_INVALID_CASE& aCase, const KI_TEST::BOARD_DUMPER& aDumper )
|
const KI_TEST::BOARD_DUMPER& aDumper )
|
||||||
{
|
{
|
||||||
auto board = MakeBoard( aCase.m_mods );
|
auto board = MakeBoard( aCase.m_mods );
|
||||||
|
|
||||||
|
@ -313,7 +312,7 @@ void DoCourtyardInvalidTest(
|
||||||
markers.push_back( std::unique_ptr<MARKER_PCB>( aMarker ) );
|
markers.push_back( std::unique_ptr<MARKER_PCB>( aMarker ) );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
drc_overlap.RunDRC( *board );
|
drc_overlap.RunDRC( EDA_UNITS::MILLIMETRES, *board );
|
||||||
|
|
||||||
CheckInvalidsMatchExpected( *board, markers, aCase.m_exp_errors );
|
CheckInvalidsMatchExpected( *board, markers, aCase.m_exp_errors );
|
||||||
}
|
}
|
||||||
|
|
|
@ -460,8 +460,8 @@ static BOARD_DESIGN_SETTINGS GetOverlapCheckDesignSettings()
|
||||||
* Run a single courtyard overlap testcase
|
* Run a single courtyard overlap testcase
|
||||||
* @param aCase The testcase to run.
|
* @param aCase The testcase to run.
|
||||||
*/
|
*/
|
||||||
static void DoCourtyardOverlapTest(
|
static void DoCourtyardOverlapTest( const COURTYARD_OVERLAP_TEST_CASE& aCase,
|
||||||
const COURTYARD_OVERLAP_TEST_CASE& aCase, const KI_TEST::BOARD_DUMPER& aDumper )
|
const KI_TEST::BOARD_DUMPER& aDumper )
|
||||||
{
|
{
|
||||||
auto board = MakeBoard( aCase.m_mods );
|
auto board = MakeBoard( aCase.m_mods );
|
||||||
|
|
||||||
|
@ -479,7 +479,7 @@ static void DoCourtyardOverlapTest(
|
||||||
markers.push_back( std::unique_ptr<MARKER_PCB>( aMarker ) );
|
markers.push_back( std::unique_ptr<MARKER_PCB>( aMarker ) );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
drc_overlap.RunDRC( *board );
|
drc_overlap.RunDRC( EDA_UNITS::MILLIMETRES, *board );
|
||||||
|
|
||||||
CheckCollisionsMatchExpected( *board, markers, aCase.m_collisions );
|
CheckCollisionsMatchExpected( *board, markers, aCase.m_collisions );
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public:
|
||||||
DRC_DURATION duration;
|
DRC_DURATION duration;
|
||||||
{
|
{
|
||||||
SCOPED_PROF_COUNTER<DRC_DURATION> timer( duration );
|
SCOPED_PROF_COUNTER<DRC_DURATION> timer( duration );
|
||||||
drc_prov->RunDRC( aBoard );
|
drc_prov->RunDRC( EDA_UNITS::MILLIMETRES, aBoard );
|
||||||
}
|
}
|
||||||
|
|
||||||
// report results
|
// report results
|
||||||
|
|
Loading…
Reference in New Issue