Move connection width testing to rule system.
Also copies connection width progress reporting architecture over to the sliver checker.
This commit is contained in:
parent
927bc8141b
commit
0304ad4494
|
@ -4,6 +4,7 @@ board_edge
|
||||||
buried_via
|
buried_via
|
||||||
clearance
|
clearance
|
||||||
condition
|
condition
|
||||||
|
connection_width
|
||||||
constraint
|
constraint
|
||||||
courtyard_clearance
|
courtyard_clearance
|
||||||
diff_pair_gap
|
diff_pair_gap
|
||||||
|
|
|
@ -186,7 +186,8 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
|
||||||
m_DRCSeverities[ DRCE_LIB_FOOTPRINT_ISSUES ] = RPT_SEVERITY_WARNING;
|
m_DRCSeverities[ DRCE_LIB_FOOTPRINT_ISSUES ] = RPT_SEVERITY_WARNING;
|
||||||
m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = RPT_SEVERITY_WARNING;
|
m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = RPT_SEVERITY_WARNING;
|
||||||
|
|
||||||
m_DRCSeverities[ DRCE_CONNECTION_WIDTH ] = RPT_SEVERITY_WARNING;
|
// TODO: Change to warning after testing
|
||||||
|
m_DRCSeverities[ DRCE_CONNECTION_WIDTH ] = RPT_SEVERITY_IGNORE;
|
||||||
|
|
||||||
m_MaxError = ARC_HIGH_DEF;
|
m_MaxError = ARC_HIGH_DEF;
|
||||||
m_ZoneKeepExternalFillets = false;
|
m_ZoneKeepExternalFillets = false;
|
||||||
|
|
|
@ -577,7 +577,8 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent )
|
||||||
|| rcItem->GetErrorCode() == DRCE_VIA_DIAMETER
|
|| rcItem->GetErrorCode() == DRCE_VIA_DIAMETER
|
||||||
|| rcItem->GetErrorCode() == DRCE_ANNULAR_WIDTH
|
|| rcItem->GetErrorCode() == DRCE_ANNULAR_WIDTH
|
||||||
|| rcItem->GetErrorCode() == DRCE_DRILL_OUT_OF_RANGE
|
|| rcItem->GetErrorCode() == DRCE_DRILL_OUT_OF_RANGE
|
||||||
|| rcItem->GetErrorCode() == DRCE_MICROVIA_DRILL_OUT_OF_RANGE )
|
|| rcItem->GetErrorCode() == DRCE_MICROVIA_DRILL_OUT_OF_RANGE
|
||||||
|
|| rcItem->GetErrorCode() == DRCE_CONNECTION_WIDTH )
|
||||||
{
|
{
|
||||||
menu.Append( 3, _( "Run constraints resolution tool..." ) );
|
menu.Append( 3, _( "Run constraints resolution tool..." ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,6 +368,7 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
|
||||||
tokens = wxT( "annular_width|"
|
tokens = wxT( "annular_width|"
|
||||||
"assertion|"
|
"assertion|"
|
||||||
"clearance|"
|
"clearance|"
|
||||||
|
"connection_width|"
|
||||||
"courtyard_clearance|"
|
"courtyard_clearance|"
|
||||||
"diff_pair_gap|"
|
"diff_pair_gap|"
|
||||||
"diff_pair_uncoupled|"
|
"diff_pair_uncoupled|"
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
* annular\_width
|
* annular\_width
|
||||||
* clearance
|
* clearance
|
||||||
|
* connection\_width
|
||||||
* courtyard_clearance
|
* courtyard_clearance
|
||||||
* diff\_pair\_gap
|
* diff\_pair\_gap
|
||||||
* diff\_pair\_uncoupled
|
* diff\_pair\_uncoupled
|
||||||
|
@ -272,3 +273,10 @@ For the latter use a `(layer "layer_name")` clause in the rule.
|
||||||
(layer "F.Courtyard")
|
(layer "F.Courtyard")
|
||||||
(constraint physical_clearance (min 3mm))
|
(constraint physical_clearance (min 3mm))
|
||||||
(condition "B.Layer == 'Edge.Cuts'"))
|
(condition "B.Layer == 'Edge.Cuts'"))
|
||||||
|
|
||||||
|
|
||||||
|
# Check current-carrying capacity
|
||||||
|
(rule high-current
|
||||||
|
(constraint track_width (min 1.0mm))
|
||||||
|
(constraint connection_width (min 0.8mm))
|
||||||
|
(condition "A.NetClass == 'Power'"))
|
|
@ -25,6 +25,7 @@ _HKI( "### Top-level Clauses\n"
|
||||||
"\n"
|
"\n"
|
||||||
" * annular\\_width\n"
|
" * annular\\_width\n"
|
||||||
" * clearance\n"
|
" * clearance\n"
|
||||||
|
" * connection\\_width\n"
|
||||||
" * courtyard_clearance\n"
|
" * courtyard_clearance\n"
|
||||||
" * diff\\_pair\\_gap\n"
|
" * diff\\_pair\\_gap\n"
|
||||||
" * diff\\_pair\\_uncoupled\n"
|
" * diff\\_pair\\_uncoupled\n"
|
||||||
|
@ -273,4 +274,10 @@ _HKI( "### Top-level Clauses\n"
|
||||||
" (layer \"F.Courtyard\")\n"
|
" (layer \"F.Courtyard\")\n"
|
||||||
" (constraint physical_clearance (min 3mm))\n"
|
" (constraint physical_clearance (min 3mm))\n"
|
||||||
" (condition \"B.Layer == 'Edge.Cuts'\"))\n"
|
" (condition \"B.Layer == 'Edge.Cuts'\"))\n"
|
||||||
"" );
|
"\n"
|
||||||
|
"\n"
|
||||||
|
" # Check current-carrying capacity\n"
|
||||||
|
" (rule high-current\n"
|
||||||
|
" (constraint track_width (min 1.0mm))\n"
|
||||||
|
" (constraint connection_width (min 0.8mm))\n"
|
||||||
|
" (condition \"A.NetClass == 'Power'\"))" );
|
||||||
|
|
|
@ -152,6 +152,10 @@ void DRC_ENGINE::loadImplicitRules()
|
||||||
widthConstraint.Value().SetMin( bds.m_TrackMinWidth );
|
widthConstraint.Value().SetMin( bds.m_TrackMinWidth );
|
||||||
rule->AddConstraint( widthConstraint );
|
rule->AddConstraint( widthConstraint );
|
||||||
|
|
||||||
|
DRC_CONSTRAINT connectionConstraint( CONNECTION_WIDTH_CONSTRAINT );
|
||||||
|
connectionConstraint.Value().SetMin( bds.m_MinConn );
|
||||||
|
rule->AddConstraint( connectionConstraint );
|
||||||
|
|
||||||
DRC_CONSTRAINT drillConstraint( HOLE_SIZE_CONSTRAINT );
|
DRC_CONSTRAINT drillConstraint( HOLE_SIZE_CONSTRAINT );
|
||||||
drillConstraint.Value().SetMin( bds.m_MinThroughDrill );
|
drillConstraint.Value().SetMin( bds.m_MinThroughDrill );
|
||||||
rule->AddConstraint( drillConstraint );
|
rule->AddConstraint( drillConstraint );
|
||||||
|
@ -890,6 +894,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
|
||||||
case TEXT_THICKNESS_CONSTRAINT:
|
case TEXT_THICKNESS_CONSTRAINT:
|
||||||
case DIFF_PAIR_GAP_CONSTRAINT:
|
case DIFF_PAIR_GAP_CONSTRAINT:
|
||||||
case LENGTH_CONSTRAINT:
|
case LENGTH_CONSTRAINT:
|
||||||
|
case CONNECTION_WIDTH_CONSTRAINT:
|
||||||
{
|
{
|
||||||
if( aReporter )
|
if( aReporter )
|
||||||
{
|
{
|
||||||
|
@ -958,12 +963,8 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEXT_HEIGHT_CONSTRAINT:
|
case TEXT_HEIGHT_CONSTRAINT:
|
||||||
REPORT( wxString::Format( _( "Checking %s: min %s." ),
|
|
||||||
EscapeHTML( c->constraint.GetName() ),
|
|
||||||
min ) )
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEXT_THICKNESS_CONSTRAINT:
|
case TEXT_THICKNESS_CONSTRAINT:
|
||||||
|
case CONNECTION_WIDTH_CONSTRAINT:
|
||||||
REPORT( wxString::Format( _( "Checking %s: min %s." ),
|
REPORT( wxString::Format( _( "Checking %s: min %s." ),
|
||||||
EscapeHTML( c->constraint.GetName() ),
|
EscapeHTML( c->constraint.GetName() ),
|
||||||
min ) )
|
min ) )
|
||||||
|
@ -1487,10 +1488,15 @@ bool DRC_ENGINE::IsErrorLimitExceeded( int error_code )
|
||||||
void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
|
void DRC_ENGINE::ReportViolation( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos,
|
||||||
PCB_LAYER_ID aMarkerLayer )
|
PCB_LAYER_ID aMarkerLayer )
|
||||||
{
|
{
|
||||||
|
static std::mutex globalLock;
|
||||||
|
|
||||||
m_errorLimits[ aItem->GetErrorCode() ] -= 1;
|
m_errorLimits[ aItem->GetErrorCode() ] -= 1;
|
||||||
|
|
||||||
if( m_violationHandler )
|
if( m_violationHandler )
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard( globalLock );
|
||||||
m_violationHandler( aItem, aPos, aMarkerLayer );
|
m_violationHandler( aItem, aPos, aMarkerLayer );
|
||||||
|
}
|
||||||
|
|
||||||
if( m_reporter )
|
if( m_reporter )
|
||||||
{
|
{
|
||||||
|
@ -1605,6 +1611,20 @@ bool DRC_ENGINE::QueryWorstConstraint( DRC_CONSTRAINT_T aConstraintId, DRC_CONST
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::set<int> DRC_ENGINE::QueryDistinctConstraints( DRC_CONSTRAINT_T aConstraintId )
|
||||||
|
{
|
||||||
|
std::set<int> distinctMinimums;
|
||||||
|
|
||||||
|
if( m_constraintMap.count( aConstraintId ) )
|
||||||
|
{
|
||||||
|
for( DRC_ENGINE_CONSTRAINT* c : *m_constraintMap[aConstraintId] )
|
||||||
|
distinctMinimums.emplace( c->constraint.GetValue().Min() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return distinctMinimums;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// fixme: move two functions below to pcbcommon?
|
// fixme: move two functions below to pcbcommon?
|
||||||
int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
|
int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet,
|
||||||
wxString& aBaseDpName )
|
wxString& aBaseDpName )
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2019-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
* Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -176,6 +176,7 @@ public:
|
||||||
bool IsCancelled() const;
|
bool IsCancelled() const;
|
||||||
|
|
||||||
bool QueryWorstConstraint( DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT& aConstraint );
|
bool QueryWorstConstraint( DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT& aConstraint );
|
||||||
|
std::set<int> QueryDistinctConstraints( DRC_CONSTRAINT_T aConstraintId );
|
||||||
|
|
||||||
std::vector<DRC_TEST_PROVIDER*> GetTestProviders() const { return m_testProviders; };
|
std::vector<DRC_TEST_PROVIDER*> GetTestProviders() const { return m_testProviders; };
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,8 @@ enum DRC_CONSTRAINT_T
|
||||||
VIA_COUNT_CONSTRAINT,
|
VIA_COUNT_CONSTRAINT,
|
||||||
PHYSICAL_CLEARANCE_CONSTRAINT,
|
PHYSICAL_CLEARANCE_CONSTRAINT,
|
||||||
PHYSICAL_HOLE_CLEARANCE_CONSTRAINT,
|
PHYSICAL_HOLE_CLEARANCE_CONSTRAINT,
|
||||||
ASSERTION_CONSTRAINT
|
ASSERTION_CONSTRAINT,
|
||||||
|
CONNECTION_WIDTH_CONSTRAINT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -313,6 +313,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
|
||||||
case T_text_height: c.m_Type = TEXT_HEIGHT_CONSTRAINT; break;
|
case T_text_height: c.m_Type = TEXT_HEIGHT_CONSTRAINT; break;
|
||||||
case T_text_thickness: c.m_Type = TEXT_THICKNESS_CONSTRAINT; break;
|
case T_text_thickness: c.m_Type = TEXT_THICKNESS_CONSTRAINT; break;
|
||||||
case T_track_width: c.m_Type = TRACK_WIDTH_CONSTRAINT; break;
|
case T_track_width: c.m_Type = TRACK_WIDTH_CONSTRAINT; break;
|
||||||
|
case T_connection_width: c.m_Type = CONNECTION_WIDTH_CONSTRAINT; break;
|
||||||
case T_annular_width: c.m_Type = ANNULAR_WIDTH_CONSTRAINT; break;
|
case T_annular_width: c.m_Type = ANNULAR_WIDTH_CONSTRAINT; break;
|
||||||
case T_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break;
|
case T_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break;
|
||||||
case T_zone_connection: c.m_Type = ZONE_CONNECTION_CONSTRAINT; break;
|
case T_zone_connection: c.m_Type = ZONE_CONNECTION_CONSTRAINT; break;
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <drc/drc_item.h>
|
#include <drc/drc_item.h>
|
||||||
#include <drc/drc_test_provider.h>
|
#include <drc/drc_test_provider.h>
|
||||||
#include <drc/drc_rtree.h>
|
#include <drc/drc_rtree.h>
|
||||||
|
#include <drc/drc_rule_condition.h>
|
||||||
#include <footprint.h>
|
#include <footprint.h>
|
||||||
#include <geometry/seg.h>
|
#include <geometry/seg.h>
|
||||||
#include <geometry/shape_poly_set.h>
|
#include <geometry/shape_poly_set.h>
|
||||||
|
@ -569,19 +570,24 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
|
||||||
if( !reportPhase( _( "Checking nets for minimum connection width..." ) ) )
|
if( !reportPhase( _( "Checking nets for minimum connection width..." ) ) )
|
||||||
return false; // DRC cancelled
|
return false; // DRC cancelled
|
||||||
|
|
||||||
LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
|
LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
|
||||||
LSEQ copperLayers = copperLayerSet.Seq();
|
LSEQ copperLayers = copperLayerSet.Seq();
|
||||||
|
BOARD* board = m_drcEngine->GetBoard();
|
||||||
BOARD* board = m_drcEngine->GetBoard();
|
DRC_RTREE* tree = board->m_CopperItemRTreeCache.get();
|
||||||
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
|
std::set<int> distinctMinWidths
|
||||||
|
= m_drcEngine->QueryDistinctConstraints( CONNECTION_WIDTH_CONSTRAINT );
|
||||||
|
|
||||||
if( m_drcEngine->IsCancelled() )
|
if( m_drcEngine->IsCancelled() )
|
||||||
return false; // DRC cancelled
|
return false; // DRC cancelled
|
||||||
|
|
||||||
std::map<PCB_LAYER_ID, std::map<int, std::set<BOARD_ITEM*>>> net_items;
|
struct ITEMS_POLY
|
||||||
std::atomic<size_t> done( 1 );
|
{
|
||||||
|
std::set<BOARD_ITEM*> items;
|
||||||
|
SHAPE_POLY_SET poly;
|
||||||
|
};
|
||||||
|
|
||||||
DRC_RTREE* tree = board->m_CopperItemRTreeCache.get();
|
std::map< std::pair<int, PCB_LAYER_ID>, ITEMS_POLY > dataset;
|
||||||
|
std::atomic<size_t> done( 1 );
|
||||||
|
|
||||||
auto calc_effort =
|
auto calc_effort =
|
||||||
[&]( const std::set<BOARD_ITEM*>& items, PCB_LAYER_ID aLayer ) -> size_t
|
[&]( const std::set<BOARD_ITEM*>& items, PCB_LAYER_ID aLayer ) -> size_t
|
||||||
|
@ -604,28 +610,38 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
|
||||||
return effort;
|
return effort;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto min_checker =
|
auto build_netlayer_polys =
|
||||||
[&](const std::set<BOARD_ITEM*>& aItems, PCB_LAYER_ID aLayer ) -> size_t
|
[&]( int aNetcode, const PCB_LAYER_ID aLayer ) -> size_t
|
||||||
{
|
{
|
||||||
if( m_drcEngine->IsCancelled() )
|
if( m_drcEngine->IsCancelled() )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
SHAPE_POLY_SET poly;
|
ITEMS_POLY& data = dataset[ { aNetcode, aLayer } ];
|
||||||
|
|
||||||
for( BOARD_ITEM* item : aItems )
|
for( BOARD_ITEM* item : data.items )
|
||||||
{
|
{
|
||||||
item->TransformShapeWithClearanceToPolygon( poly, aLayer, 0, ARC_HIGH_DEF,
|
item->TransformShapeWithClearanceToPolygon( data.poly, aLayer, 0,
|
||||||
ERROR_OUTSIDE );
|
ARC_HIGH_DEF, ERROR_OUTSIDE );
|
||||||
}
|
}
|
||||||
|
|
||||||
poly.Fracture( SHAPE_POLY_SET::PM_FAST );
|
data.poly.Fracture( SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
int minimum_width = bds.m_MinConn;
|
done.fetch_add( calc_effort( data.items, aLayer ) );
|
||||||
POLYGON_TEST test( minimum_width );
|
|
||||||
|
|
||||||
for( int ii = 0; ii < poly.OutlineCount(); ++ii )
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto min_checker =
|
||||||
|
[&]( const ITEMS_POLY& aDataset, const PCB_LAYER_ID aLayer, int aMinWidth ) -> size_t
|
||||||
|
{
|
||||||
|
if( m_drcEngine->IsCancelled() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
POLYGON_TEST test( aMinWidth );
|
||||||
|
|
||||||
|
for( int ii = 0; ii < aDataset.poly.OutlineCount(); ++ii )
|
||||||
{
|
{
|
||||||
const SHAPE_LINE_CHAIN& chain = poly.COutline( ii );
|
const SHAPE_LINE_CHAIN& chain = aDataset.poly.COutline( ii );
|
||||||
|
|
||||||
test.FindPairs( chain );
|
test.FindPairs( chain );
|
||||||
auto& ret = test.GetVertices();
|
auto& ret = test.GetVertices();
|
||||||
|
@ -635,42 +651,57 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
|
||||||
SEG span( chain.CPoint( pt.first ), chain.CPoint( pt.second ) );
|
SEG span( chain.CPoint( pt.first ), chain.CPoint( pt.second ) );
|
||||||
VECTOR2I location = ( span.A + span.B ) / 2;
|
VECTOR2I location = ( span.A + span.B ) / 2;
|
||||||
int dist = ( span.A - span.B ).EuclideanNorm();
|
int dist = ( span.A - span.B ).EuclideanNorm();
|
||||||
std::set<BOARD_ITEM*> items = tree->GetObjectsAt( location, aLayer,
|
std::set<BOARD_ITEM*> nearbyItems = tree->GetObjectsAt( location, aLayer,
|
||||||
minimum_width );
|
aMinWidth );
|
||||||
|
|
||||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CONNECTION_WIDTH );
|
std::vector<BOARD_ITEM*> items;
|
||||||
wxString msg;
|
|
||||||
|
|
||||||
msg.Printf( _( "Minimum connection width %s; actual %s" ),
|
for( BOARD_ITEM* item : nearbyItems )
|
||||||
MessageTextFromValue( userUnits(), minimum_width ),
|
|
||||||
MessageTextFromValue( userUnits(), dist ) );
|
|
||||||
|
|
||||||
drce->SetErrorMessage( msg + wxS( " " ) + layerDesc( aLayer ) );
|
|
||||||
|
|
||||||
for( BOARD_ITEM* item : items )
|
|
||||||
{
|
{
|
||||||
if( item->HitTest( location, minimum_width ) )
|
if( item->HitTest( location, aMinWidth ) )
|
||||||
drce->AddItem( item );
|
items.push_back( item );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !drce->GetIDs().empty() )
|
if( !items.empty() )
|
||||||
reportViolation( drce, location, aLayer );
|
{
|
||||||
|
DRC_CONSTRAINT c = m_drcEngine->EvalRules( CONNECTION_WIDTH_CONSTRAINT,
|
||||||
|
items[0],
|
||||||
|
items.size() > 1 ? items[1]
|
||||||
|
: nullptr,
|
||||||
|
aLayer );
|
||||||
|
|
||||||
|
if( c.Value().Min() == aMinWidth )
|
||||||
|
{
|
||||||
|
auto drce = DRC_ITEM::Create( DRCE_CONNECTION_WIDTH );
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
msg.Printf( _( "Minimum connection width %s; actual %s" ),
|
||||||
|
MessageTextFromValue( userUnits(), aMinWidth ),
|
||||||
|
MessageTextFromValue( userUnits(), dist ) );
|
||||||
|
|
||||||
|
drce->SetErrorMessage( msg + wxS( " " ) + layerDesc( aLayer ) );
|
||||||
|
drce->SetViolatingRule( c.GetParentRule() );
|
||||||
|
|
||||||
|
for( BOARD_ITEM* item : items )
|
||||||
|
drce->AddItem( item );
|
||||||
|
|
||||||
|
reportViolation( drce, location, aLayer );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
done.fetch_add( calc_effort( aItems, aLayer ) );
|
done.fetch_add( calc_effort( aDataset.items, aLayer ) );
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
for( PCB_LAYER_ID layer : copperLayers )
|
for( PCB_LAYER_ID layer : copperLayers )
|
||||||
{
|
{
|
||||||
auto& layer_items = net_items[layer];
|
|
||||||
|
|
||||||
for( ZONE* zone : board->m_DRCCopperZones )
|
for( ZONE* zone : board->m_DRCCopperZones )
|
||||||
{
|
{
|
||||||
if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
|
if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
|
||||||
layer_items[zone->GetNetCode()].emplace( zone );
|
dataset[ { zone->GetNetCode(), layer } ].items.emplace( zone );
|
||||||
}
|
}
|
||||||
|
|
||||||
for( PCB_TRACK* track : board->Tracks() )
|
for( PCB_TRACK* track : board->Tracks() )
|
||||||
|
@ -678,11 +709,11 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
|
||||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( track ) )
|
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( track ) )
|
||||||
{
|
{
|
||||||
if( via->FlashLayer( static_cast<int>( layer ) ) )
|
if( via->FlashLayer( static_cast<int>( layer ) ) )
|
||||||
layer_items[via->GetNetCode()].emplace( via );
|
dataset[ { via->GetNetCode(), layer } ].items.emplace( via );
|
||||||
}
|
}
|
||||||
else if( track->IsOnLayer( layer ) )
|
else if( track->IsOnLayer( layer ) )
|
||||||
{
|
{
|
||||||
layer_items[track->GetNetCode()].emplace( track );
|
dataset[ { track->GetNetCode(), layer } ].items.emplace( track );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,29 +722,50 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run()
|
||||||
for( PAD* pad : fp->Pads() )
|
for( PAD* pad : fp->Pads() )
|
||||||
{
|
{
|
||||||
if( pad->FlashLayer( static_cast<int>( layer ) ) )
|
if( pad->FlashLayer( static_cast<int>( layer ) ) )
|
||||||
layer_items[pad->GetNetCode()].emplace( pad );
|
dataset[ { pad->GetNetCode(), layer } ].items.emplace( pad );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Footprint zones are also in the m_DRCCopperZones cache
|
// Footprint zones are also in the m_DRCCopperZones cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_pool& tp = GetKiCadThreadPool();
|
thread_pool& tp = GetKiCadThreadPool();
|
||||||
std::vector<std::future<size_t>> returns;
|
std::vector<std::future<size_t>> returns;
|
||||||
size_t return_count = 0;
|
size_t total_effort = 0;
|
||||||
size_t total_effort = 0;
|
|
||||||
|
|
||||||
for( auto& layer_items : net_items )
|
for( const std::pair<const std::pair<int, PCB_LAYER_ID>, ITEMS_POLY>& netLayer : dataset )
|
||||||
return_count += layer_items.second.size();
|
total_effort += calc_effort( netLayer.second.items, netLayer.first.second );
|
||||||
|
|
||||||
returns.reserve( return_count );
|
total_effort += total_effort * distinctMinWidths.size();
|
||||||
|
|
||||||
for( auto& layer_items : net_items )
|
returns.reserve( dataset.size() );
|
||||||
|
|
||||||
|
for( const std::pair<const std::pair<int, PCB_LAYER_ID>, ITEMS_POLY>& netLayer : dataset )
|
||||||
{
|
{
|
||||||
for( const auto& items : layer_items.second )
|
returns.emplace_back( tp.submit( build_netlayer_polys, netLayer.first.first,
|
||||||
|
netLayer.first.second ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( std::future<size_t>& retval : returns )
|
||||||
|
{
|
||||||
|
std::future_status status;
|
||||||
|
|
||||||
|
do
|
||||||
{
|
{
|
||||||
returns.emplace_back( tp.submit( min_checker, items.second, layer_items.first ) );
|
m_drcEngine->ReportProgress( static_cast<double>( done ) / total_effort );
|
||||||
total_effort += calc_effort( items.second, layer_items.first );
|
status = retval.wait_for( std::chrono::milliseconds( 100 ) );
|
||||||
|
} while( status != std::future_status::ready );
|
||||||
|
}
|
||||||
|
|
||||||
|
returns.clear();
|
||||||
|
returns.reserve( dataset.size() * distinctMinWidths.size() );
|
||||||
|
|
||||||
|
for( const std::pair<const std::pair<int, PCB_LAYER_ID>, ITEMS_POLY>& netLayer : dataset )
|
||||||
|
{
|
||||||
|
for( int minWidth : distinctMinWidths )
|
||||||
|
{
|
||||||
|
returns.emplace_back( tp.submit( min_checker, netLayer.second, netLayer.first.second,
|
||||||
|
minWidth ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,91 +89,186 @@ bool DRC_TEST_PROVIDER_SLIVER_CHECKER::Run()
|
||||||
int testLength = widthTolerance / ( 2 * sin( DEG2RAD( angleTolerance / 2 ) ) );
|
int testLength = widthTolerance / ( 2 * sin( DEG2RAD( angleTolerance / 2 ) ) );
|
||||||
LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
|
LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask();
|
||||||
LSEQ copperLayers = copperLayerSet.Seq();
|
LSEQ copperLayers = copperLayerSet.Seq();
|
||||||
int layerCount = copperLayers.size();
|
size_t layerCount = copperLayers.size();
|
||||||
|
|
||||||
// Report progress on board zones only. Everything else is in the noise.
|
if( m_drcEngine->IsCancelled() )
|
||||||
int zoneLayerCount = 0;
|
|
||||||
std::atomic<size_t> done( 1 );
|
|
||||||
|
|
||||||
for( PCB_LAYER_ID layer : copperLayers )
|
|
||||||
{
|
|
||||||
for( ZONE* zone : m_drcEngine->GetBoard()->Zones() )
|
|
||||||
{
|
|
||||||
if( !zone->GetIsRuleArea() && zone->IsOnLayer( layer ) )
|
|
||||||
zoneLayerCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PROGRESS_REPORTER* reporter = m_drcEngine->GetProgressReporter();
|
|
||||||
|
|
||||||
if( reporter && reporter->IsCancelled() )
|
|
||||||
return false; // DRC cancelled
|
return false; // DRC cancelled
|
||||||
|
|
||||||
std::vector<SHAPE_POLY_SET> layerPolys( layerCount );
|
std::vector<SHAPE_POLY_SET> layerPolys( layerCount );
|
||||||
|
std::vector<size_t> layerEfforts( layerCount );
|
||||||
|
size_t total_effort = 0;
|
||||||
|
std::atomic<size_t> done( 1 );
|
||||||
|
|
||||||
|
auto calc_effort =
|
||||||
|
[&]( BOARD_ITEM* item, PCB_LAYER_ID aLayer ) -> size_t
|
||||||
|
{
|
||||||
|
if( item->Type() == PCB_ZONE_T )
|
||||||
|
{
|
||||||
|
ZONE* zone = static_cast<ZONE*>( item );
|
||||||
|
return zone->GetFilledPolysList( aLayer )->FullPointCount();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto poly_builder =
|
||||||
|
[&]( size_t ii ) -> size_t
|
||||||
|
{
|
||||||
|
PCB_LAYER_ID layer = copperLayers[ii];
|
||||||
|
SHAPE_POLY_SET& poly = layerPolys[ii];
|
||||||
|
|
||||||
|
if( m_drcEngine->IsCancelled() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
SHAPE_POLY_SET fill;
|
||||||
|
|
||||||
|
forEachGeometryItem( s_allBasicItems, LSET().set( layer ),
|
||||||
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
|
{
|
||||||
|
if( dynamic_cast<ZONE*>( item) )
|
||||||
|
{
|
||||||
|
ZONE* zone = static_cast<ZONE*>( item );
|
||||||
|
|
||||||
|
if( !zone->GetIsRuleArea() )
|
||||||
|
{
|
||||||
|
fill = zone->GetFill( layer )->CloneDropTriangulation();
|
||||||
|
fill.Unfracture( SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
|
for( int jj = 0; jj < fill.OutlineCount(); ++jj )
|
||||||
|
poly.AddOutline( fill.Outline( jj ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item->TransformShapeWithClearanceToPolygon( poly, layer, 0,
|
||||||
|
ARC_LOW_DEF,
|
||||||
|
ERROR_OUTSIDE );
|
||||||
|
}
|
||||||
|
|
||||||
|
done.fetch_add( calc_effort( item, layer ) );
|
||||||
|
|
||||||
|
if( m_drcEngine->IsCancelled() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
if( m_drcEngine->IsCancelled() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
poly.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
|
// Sharpen corners
|
||||||
|
poly.Deflate( widthTolerance / 2, ARC_LOW_DEF,
|
||||||
|
SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS );
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
auto sliver_checker =
|
auto sliver_checker =
|
||||||
[&]( int aItem ) -> size_t
|
[&]( size_t ii ) -> size_t
|
||||||
|
{
|
||||||
|
PCB_LAYER_ID layer = copperLayers[ii];
|
||||||
|
SHAPE_POLY_SET& poly = layerPolys[ii];
|
||||||
|
|
||||||
|
if( m_drcEngine->IsErrorLimitExceeded( DRCE_COPPER_SLIVER ) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Frequently, in filled areas, some points of the polygons are very near (dist is
|
||||||
|
// only a few internal units, like 2 or 3 units).
|
||||||
|
// We skip very small vertices: one cannot really compute a valid orientation of
|
||||||
|
// such a vertex
|
||||||
|
// So skip points near than min_len (in internal units).
|
||||||
|
const int min_len = 3;
|
||||||
|
|
||||||
|
for( int jj = 0; jj < poly.OutlineCount(); ++jj )
|
||||||
{
|
{
|
||||||
PCB_LAYER_ID layer = copperLayers[aItem];
|
const std::vector<VECTOR2I>& pts = poly.Outline( jj ).CPoints();
|
||||||
SHAPE_POLY_SET& poly = layerPolys[aItem];
|
int ptCount = pts.size();
|
||||||
|
|
||||||
if( m_drcEngine->IsCancelled() )
|
for( int kk = 0; kk < ptCount; ++kk )
|
||||||
return 0;
|
{
|
||||||
|
VECTOR2I pt = pts[ kk ];
|
||||||
|
VECTOR2I ptPrior = pts[ ( ptCount + kk - 1 ) % ptCount ];
|
||||||
|
VECTOR2I vPrior = ( ptPrior - pt );
|
||||||
|
|
||||||
SHAPE_POLY_SET fill;
|
if( std::abs( vPrior.x ) < min_len && std::abs( vPrior.y ) < min_len && ptCount > 5)
|
||||||
|
{
|
||||||
|
ptPrior = pts[ ( ptCount + kk - 2 ) % ptCount ];
|
||||||
|
vPrior = ( ptPrior - pt );
|
||||||
|
}
|
||||||
|
|
||||||
forEachGeometryItem( s_allBasicItems, LSET().set( layer ),
|
VECTOR2I ptAfter = pts[ ( kk + 1 ) % ptCount ];
|
||||||
[&]( BOARD_ITEM* item ) -> bool
|
VECTOR2I vAfter = ( ptAfter - pt );
|
||||||
{
|
|
||||||
if( dynamic_cast<ZONE*>( item) )
|
|
||||||
{
|
|
||||||
ZONE* zone = static_cast<ZONE*>( item );
|
|
||||||
|
|
||||||
if( !zone->GetIsRuleArea() )
|
if( std::abs( vAfter.x ) < min_len && std::abs( vAfter.y ) < min_len && ptCount > 5 )
|
||||||
{
|
{
|
||||||
fill = zone->GetFill( layer )->CloneDropTriangulation();
|
ptAfter = pts[ ( kk + 2 ) % ptCount ];
|
||||||
fill.Unfracture( SHAPE_POLY_SET::PM_FAST );
|
vAfter = ( ptAfter - pt );
|
||||||
|
}
|
||||||
|
|
||||||
for( int jj = 0; jj < fill.OutlineCount(); ++jj )
|
VECTOR2I vIncluded = vPrior.Resize( testLength ) - vAfter.Resize( testLength );
|
||||||
poly.AddOutline( fill.Outline( jj ) );
|
|
||||||
|
|
||||||
// Report progress on board zones only. Everything
|
if( vIncluded.SquaredEuclideanNorm() < SEG::Square( widthTolerance ) )
|
||||||
// else is in the noise.
|
{
|
||||||
done.fetch_add( 1 );
|
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_COPPER_SLIVER );
|
||||||
}
|
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + layerDesc( layer ) );
|
||||||
}
|
reportViolation( drce, pt, layer );
|
||||||
else
|
}
|
||||||
{
|
}
|
||||||
item->TransformShapeWithClearanceToPolygon( poly, layer, 0,
|
}
|
||||||
ARC_LOW_DEF,
|
|
||||||
ERROR_OUTSIDE );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( m_drcEngine->IsCancelled() )
|
done.fetch_add( layerEfforts[ layer ] );
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
if( m_drcEngine->IsCancelled() )
|
||||||
} );
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
if( m_drcEngine->IsCancelled() )
|
for( size_t ii = 0; ii < layerCount; ++ii )
|
||||||
return 0;
|
{
|
||||||
|
PCB_LAYER_ID layer = copperLayers[ii];
|
||||||
|
|
||||||
poly.Simplify( SHAPE_POLY_SET::PM_FAST );
|
forEachGeometryItem( s_allBasicItems, LSET().set( layer ),
|
||||||
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
|
{
|
||||||
|
layerEfforts[ layer ] += calc_effort( item, layer );
|
||||||
|
return true;
|
||||||
|
} );
|
||||||
|
|
||||||
// Sharpen corners
|
total_effort += layerEfforts[ layer ];
|
||||||
poly.Deflate( widthTolerance / 2, ARC_LOW_DEF,
|
}
|
||||||
SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS );
|
|
||||||
|
|
||||||
return 1;
|
total_effort *= 2; // Once for building polys; once for checking slivers
|
||||||
};
|
|
||||||
|
|
||||||
thread_pool& tp = GetKiCadThreadPool();
|
thread_pool& tp = GetKiCadThreadPool();
|
||||||
std::vector<std::future<size_t>> returns;
|
std::vector<std::future<size_t>> returns;
|
||||||
|
|
||||||
returns.reserve( copperLayers.size() );
|
returns.reserve( layerCount );
|
||||||
|
|
||||||
for( size_t ii = 0; ii < copperLayers.size(); ++ii )
|
for( size_t ii = 0; ii < layerCount; ++ii )
|
||||||
|
returns.emplace_back( tp.submit( poly_builder, ii ) );
|
||||||
|
|
||||||
|
for( auto& retval : returns )
|
||||||
|
{
|
||||||
|
std::future_status status;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
m_drcEngine->ReportProgress( static_cast<double>( done ) / total_effort );
|
||||||
|
|
||||||
|
status = retval.wait_for( std::chrono::milliseconds( 100 ) );
|
||||||
|
} while( status != std::future_status::ready );
|
||||||
|
}
|
||||||
|
|
||||||
|
returns.clear();
|
||||||
|
returns.reserve( layerCount );
|
||||||
|
|
||||||
|
for( size_t ii = 0; ii < layerCount; ++ii )
|
||||||
returns.emplace_back( tp.submit( sliver_checker, ii ) );
|
returns.emplace_back( tp.submit( sliver_checker, ii ) );
|
||||||
|
|
||||||
for( auto& retval : returns )
|
for( auto& retval : returns )
|
||||||
|
@ -182,66 +277,12 @@ bool DRC_TEST_PROVIDER_SLIVER_CHECKER::Run()
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
m_drcEngine->ReportProgress( static_cast<double>( zoneLayerCount ) / done );
|
m_drcEngine->ReportProgress( static_cast<double>( done ) / total_effort );
|
||||||
|
|
||||||
status = retval.wait_for( std::chrono::milliseconds( 100 ) );
|
status = retval.wait_for( std::chrono::milliseconds( 100 ) );
|
||||||
} while( status != std::future_status::ready );
|
} while( status != std::future_status::ready );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for( int ii = 0; ii < layerCount; ++ii )
|
|
||||||
{
|
|
||||||
PCB_LAYER_ID layer = copperLayers[ii];
|
|
||||||
SHAPE_POLY_SET& poly = layerPolys[ii];
|
|
||||||
|
|
||||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_COPPER_SLIVER ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Frequently, in filled areas, some points of the polygons are very near (dist is only
|
|
||||||
// a few internal units, like 2 or 3 units.
|
|
||||||
// We skip very small vertices: one cannot really compute a valid orientation of
|
|
||||||
// such a vertex
|
|
||||||
// So skip points near than min_len (in internal units).
|
|
||||||
const int min_len = 3;
|
|
||||||
|
|
||||||
for( int jj = 0; jj < poly.OutlineCount(); ++jj )
|
|
||||||
{
|
|
||||||
const std::vector<VECTOR2I>& pts = poly.Outline( jj ).CPoints();
|
|
||||||
int ptCount = pts.size();
|
|
||||||
|
|
||||||
for( int kk = 0; kk < ptCount; ++kk )
|
|
||||||
{
|
|
||||||
VECTOR2I pt = pts[ kk ];
|
|
||||||
VECTOR2I ptPrior = pts[ ( ptCount + kk - 1 ) % ptCount ];
|
|
||||||
VECTOR2I vPrior = ( ptPrior - pt );
|
|
||||||
|
|
||||||
if( std::abs( vPrior.x ) < min_len && std::abs( vPrior.y ) < min_len && ptCount > 5)
|
|
||||||
{
|
|
||||||
ptPrior = pts[ ( ptCount + kk - 2 ) % ptCount ];
|
|
||||||
vPrior = ( ptPrior - pt );
|
|
||||||
}
|
|
||||||
|
|
||||||
VECTOR2I ptAfter = pts[ ( kk + 1 ) % ptCount ];
|
|
||||||
VECTOR2I vAfter = ( ptAfter - pt );
|
|
||||||
|
|
||||||
if( std::abs( vAfter.x ) < min_len && std::abs( vAfter.y ) < min_len && ptCount > 5 )
|
|
||||||
{
|
|
||||||
ptAfter = pts[ ( kk + 2 ) % ptCount ];
|
|
||||||
vAfter = ( ptAfter - pt );
|
|
||||||
}
|
|
||||||
|
|
||||||
VECTOR2I vIncluded = vPrior.Resize( testLength ) - vAfter.Resize( testLength );
|
|
||||||
|
|
||||||
if( vIncluded.SquaredEuclideanNorm() < SEG::Square( widthTolerance ) )
|
|
||||||
{
|
|
||||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_COPPER_SLIVER );
|
|
||||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + layerDesc( layer ) );
|
|
||||||
reportViolation( drce, pt, layer );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -317,6 +317,20 @@ void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr<RC_ITEM>& aDR
|
||||||
reportMax( r->GetUnits(), constraint ) ) );
|
reportMax( r->GetUnits(), constraint ) ) );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DRCE_CONNECTION_WIDTH:
|
||||||
|
r = m_inspectClearanceDialog->AddPage( _( "Connection Width" ) );
|
||||||
|
reportHeader( _( "Connection width resolution for:" ), a, b, r );
|
||||||
|
|
||||||
|
if( compileError )
|
||||||
|
reportCompileError( r );
|
||||||
|
|
||||||
|
constraint = drcEngine.EvalRules( CONNECTION_WIDTH_CONSTRAINT, a, b, layer, r );
|
||||||
|
|
||||||
|
r->Report( "" );
|
||||||
|
r->Report( wxString::Format( _( "Resolved min connection width constraint: %s." ),
|
||||||
|
reportMin( r->GetUnits(), constraint ) ) );
|
||||||
|
break;
|
||||||
|
|
||||||
case DRCE_VIA_DIAMETER:
|
case DRCE_VIA_DIAMETER:
|
||||||
r = m_inspectClearanceDialog->AddPage( _( "Via Diameter" ) );
|
r = m_inspectClearanceDialog->AddPage( _( "Via Diameter" ) );
|
||||||
reportHeader( _( "Via diameter resolution for:" ), a, r );
|
reportHeader( _( "Via diameter resolution for:" ), a, r );
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
(version 1)
|
||||||
|
(rule high_current_netclass
|
||||||
|
(constraint connection_width (min 0.16mm))
|
||||||
|
(condition "A.NetClass == 'High_current'"))
|
||||||
|
(rule high_current_area
|
||||||
|
(constraint connection_width (min 0.16mm))
|
||||||
|
(condition "A.insideArea('high_current')"))
|
|
@ -0,0 +1,141 @@
|
||||||
|
(kicad_pcb (version 20220621) (generator pcbnew)
|
||||||
|
|
||||||
|
(general
|
||||||
|
(thickness 1.6)
|
||||||
|
)
|
||||||
|
|
||||||
|
(paper "A4")
|
||||||
|
(layers
|
||||||
|
(0 "F.Cu" signal)
|
||||||
|
(31 "B.Cu" signal)
|
||||||
|
(32 "B.Adhes" user "B.Adhesive")
|
||||||
|
(33 "F.Adhes" user "F.Adhesive")
|
||||||
|
(34 "B.Paste" user)
|
||||||
|
(35 "F.Paste" user)
|
||||||
|
(36 "B.SilkS" user "B.Silkscreen")
|
||||||
|
(37 "F.SilkS" user "F.Silkscreen")
|
||||||
|
(38 "B.Mask" user)
|
||||||
|
(39 "F.Mask" user)
|
||||||
|
(40 "Dwgs.User" user "User.Drawings")
|
||||||
|
(41 "Cmts.User" user "User.Comments")
|
||||||
|
(42 "Eco1.User" user "User.Eco1")
|
||||||
|
(43 "Eco2.User" user "User.Eco2")
|
||||||
|
(44 "Edge.Cuts" user)
|
||||||
|
(45 "Margin" user)
|
||||||
|
(46 "B.CrtYd" user "B.Courtyard")
|
||||||
|
(47 "F.CrtYd" user "F.Courtyard")
|
||||||
|
(48 "B.Fab" user)
|
||||||
|
(49 "F.Fab" user)
|
||||||
|
(50 "User.1" user)
|
||||||
|
(51 "User.2" user)
|
||||||
|
(52 "User.3" user)
|
||||||
|
(53 "User.4" user)
|
||||||
|
(54 "User.5" user)
|
||||||
|
(55 "User.6" user)
|
||||||
|
(56 "User.7" user)
|
||||||
|
(57 "User.8" user)
|
||||||
|
(58 "User.9" user)
|
||||||
|
)
|
||||||
|
|
||||||
|
(setup
|
||||||
|
(stackup
|
||||||
|
(layer "F.SilkS" (type "Top Silk Screen"))
|
||||||
|
(layer "F.Paste" (type "Top Solder Paste"))
|
||||||
|
(layer "F.Mask" (type "Top Solder Mask") (thickness 0.01))
|
||||||
|
(layer "F.Cu" (type "copper") (thickness 0.035))
|
||||||
|
(layer "dielectric 1" (type "core") (thickness 1.51) (material "FR4") (epsilon_r 4.5) (loss_tangent 0.02))
|
||||||
|
(layer "B.Cu" (type "copper") (thickness 0.035))
|
||||||
|
(layer "B.Mask" (type "Bottom Solder Mask") (thickness 0.01))
|
||||||
|
(layer "B.Paste" (type "Bottom Solder Paste"))
|
||||||
|
(layer "B.SilkS" (type "Bottom Silk Screen"))
|
||||||
|
(copper_finish "None")
|
||||||
|
(dielectric_constraints no)
|
||||||
|
)
|
||||||
|
(pad_to_mask_clearance 0)
|
||||||
|
(pcbplotparams
|
||||||
|
(layerselection 0x00010fc_ffffffff)
|
||||||
|
(plot_on_all_layers_selection 0x0000000_00000000)
|
||||||
|
(disableapertmacros false)
|
||||||
|
(usegerberextensions false)
|
||||||
|
(usegerberattributes true)
|
||||||
|
(usegerberadvancedattributes true)
|
||||||
|
(creategerberjobfile true)
|
||||||
|
(dashed_line_dash_ratio 12.000000)
|
||||||
|
(dashed_line_gap_ratio 3.000000)
|
||||||
|
(svgprecision 4)
|
||||||
|
(plotframeref false)
|
||||||
|
(viasonmask false)
|
||||||
|
(mode 1)
|
||||||
|
(useauxorigin false)
|
||||||
|
(hpglpennumber 1)
|
||||||
|
(hpglpenspeed 20)
|
||||||
|
(hpglpendiameter 15.000000)
|
||||||
|
(dxfpolygonmode true)
|
||||||
|
(dxfimperialunits true)
|
||||||
|
(dxfusepcbnewfont true)
|
||||||
|
(psnegative false)
|
||||||
|
(psa4output false)
|
||||||
|
(plotreference true)
|
||||||
|
(plotvalue true)
|
||||||
|
(plotinvisibletext false)
|
||||||
|
(sketchpadsonfab false)
|
||||||
|
(subtractmaskfromsilk false)
|
||||||
|
(outputformat 1)
|
||||||
|
(mirror false)
|
||||||
|
(drillshape 1)
|
||||||
|
(scaleselection 1)
|
||||||
|
(outputdirectory "")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(net 0 "")
|
||||||
|
(net 1 "net_1")
|
||||||
|
(net 2 "net_HC")
|
||||||
|
(net 3 "net_2")
|
||||||
|
(net 4 "net_3")
|
||||||
|
|
||||||
|
(gr_rect (start 103.8606 72.2884) (end 121.1326 81.661)
|
||||||
|
(stroke (width 0.1) (type default)) (fill none) (layer "Edge.Cuts") (tstamp 0fed72c1-482a-43d6-acdf-98ed8c42a640))
|
||||||
|
(gr_text "<<- no error" (at 117.2972 78.6892) (layer "F.Cu") (tstamp 1cdd5a64-dfb1-4eda-890c-555989cf19dc)
|
||||||
|
(effects (font (size 0.2 0.2) (thickness 0.04) bold) (justify left bottom))
|
||||||
|
)
|
||||||
|
(gr_text "<<- error" (at 117.2972 74.3458) (layer "F.Cu") (tstamp 24703be7-31d2-4cc0-807d-d1af2764829c)
|
||||||
|
(effects (font (size 0.2 0.2) (thickness 0.04) bold) (justify left bottom))
|
||||||
|
)
|
||||||
|
(gr_text "<<- error" (at 117.348 77.6732) (layer "F.Cu") (tstamp 48240202-f51e-42b8-906f-77194bafa596)
|
||||||
|
(effects (font (size 0.2 0.2) (thickness 0.04) bold) (justify left bottom))
|
||||||
|
)
|
||||||
|
(gr_text "High_current rule area:" (at 105.5116 76.327) (layer "F.Cu") (tstamp 5a232032-2a77-4c5c-a932-e0dd3c3db8c9)
|
||||||
|
(effects (font (size 0.2 0.2) (thickness 0.04) bold) (justify left bottom))
|
||||||
|
)
|
||||||
|
(gr_text "High_current netclass:" (at 106.1466 73.9394) (layer "F.Cu") (tstamp dd7b0757-fb61-40bd-84e1-598677c91625)
|
||||||
|
(effects (font (size 0.2 0.2) (thickness 0.04) bold) (justify left bottom))
|
||||||
|
)
|
||||||
|
(gr_text "<<- error" (at 117.094 79.4766) (layer "F.Cu") (tstamp e8c4ea88-467d-4e67-8480-f5f76e9ac3cf)
|
||||||
|
(effects (font (size 0.2 0.2) (thickness 0.04) bold) (justify left bottom))
|
||||||
|
)
|
||||||
|
|
||||||
|
(segment (start 112.5474 77.5716) (end 106.1212 77.5716) (width 0.25) (layer "F.Cu") (net 1) (tstamp 07a05dc9-fd0c-4bac-8cfe-73f661e50f58))
|
||||||
|
(segment (start 112.7506 77.5716) (end 116.7384 77.5716) (width 0.25) (layer "F.Cu") (net 1) (tstamp b9c8bd88-3577-41fd-b8a3-f84c3ec54539))
|
||||||
|
(segment (start 112.8014 74.2442) (end 116.7892 74.2442) (width 0.25) (layer "F.Cu") (net 2) (tstamp 5fd40a5e-8558-46fc-9321-fcda035db40b))
|
||||||
|
(segment (start 112.5982 74.2442) (end 106.172 74.2442) (width 0.25) (layer "F.Cu") (net 2) (tstamp 7014ab67-9149-4546-9f6e-d99764e15f97))
|
||||||
|
(segment (start 112.7252 78.5622) (end 116.713 78.5622) (width 0.25) (layer "F.Cu") (net 3) (tstamp 7c6d2fd8-65cf-4c16-ba39-e8c3cd86d13d))
|
||||||
|
(segment (start 112.522 78.5622) (end 106.0958 78.5622) (width 0.25) (layer "F.Cu") (net 3) (tstamp a38443b8-d7a2-41c0-b6b5-5218a5114002))
|
||||||
|
(segment (start 108.3818 79.3496) (end 112.5474 79.3496) (width 0.25) (layer "F.Cu") (net 4) (tstamp db40c24a-6762-409a-acaf-cbcefb8e7d40))
|
||||||
|
(segment (start 112.776 79.3496) (end 116.4082 79.3496) (width 0.25) (layer "F.Cu") (net 4) (tstamp e8d7189c-1bf1-4f6c-abd7-bbbd5fd5f896))
|
||||||
|
|
||||||
|
(zone (net 0) (net_name "") (layer "F.Cu") (tstamp b0befc79-e815-4fdd-bd77-757b21ce75e2) (name "high_current") (hatch edge 0.508)
|
||||||
|
(connect_pads (clearance 0))
|
||||||
|
(min_thickness 0.254) (filled_areas_thickness no)
|
||||||
|
(keepout (tracks allowed) (vias allowed) (pads allowed) (copperpour allowed) (footprints allowed))
|
||||||
|
(fill (thermal_gap 0.508) (thermal_bridge_width 0.508) (island_removal_mode 2) (island_area_min 10))
|
||||||
|
(polygon
|
||||||
|
(pts
|
||||||
|
(xy 118.5418 77.9526)
|
||||||
|
(xy 105.4354 77.9526)
|
||||||
|
(xy 105.4354 76.454)
|
||||||
|
(xy 118.5418 76.454)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,262 @@
|
||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"3dviewports": [],
|
||||||
|
"design_settings": {
|
||||||
|
"defaults": {
|
||||||
|
"board_outline_line_width": 0.09999999999999999,
|
||||||
|
"copper_line_width": 0.19999999999999998,
|
||||||
|
"copper_text_italic": false,
|
||||||
|
"copper_text_size_h": 1.5,
|
||||||
|
"copper_text_size_v": 1.5,
|
||||||
|
"copper_text_thickness": 0.3,
|
||||||
|
"copper_text_upright": false,
|
||||||
|
"courtyard_line_width": 0.049999999999999996,
|
||||||
|
"dimension_precision": 4,
|
||||||
|
"dimension_units": 3,
|
||||||
|
"dimensions": {
|
||||||
|
"arrow_length": 1270000,
|
||||||
|
"extension_offset": 500000,
|
||||||
|
"keep_text_aligned": true,
|
||||||
|
"suppress_zeroes": false,
|
||||||
|
"text_position": 0,
|
||||||
|
"units_format": 1
|
||||||
|
},
|
||||||
|
"fab_line_width": 0.09999999999999999,
|
||||||
|
"fab_text_italic": false,
|
||||||
|
"fab_text_size_h": 1.0,
|
||||||
|
"fab_text_size_v": 1.0,
|
||||||
|
"fab_text_thickness": 0.15,
|
||||||
|
"fab_text_upright": false,
|
||||||
|
"other_line_width": 0.15,
|
||||||
|
"other_text_italic": false,
|
||||||
|
"other_text_size_h": 1.0,
|
||||||
|
"other_text_size_v": 1.0,
|
||||||
|
"other_text_thickness": 0.15,
|
||||||
|
"other_text_upright": false,
|
||||||
|
"pads": {
|
||||||
|
"drill": 0.762,
|
||||||
|
"height": 1.524,
|
||||||
|
"width": 1.524
|
||||||
|
},
|
||||||
|
"silk_line_width": 0.15,
|
||||||
|
"silk_text_italic": false,
|
||||||
|
"silk_text_size_h": 1.0,
|
||||||
|
"silk_text_size_v": 1.0,
|
||||||
|
"silk_text_thickness": 0.15,
|
||||||
|
"silk_text_upright": false,
|
||||||
|
"zones": {
|
||||||
|
"min_clearance": 0.508
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff_pair_dimensions": [
|
||||||
|
{
|
||||||
|
"gap": 0.0,
|
||||||
|
"via_gap": 0.0,
|
||||||
|
"width": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"drc_exclusions": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"rule_severities": {
|
||||||
|
"annular_width": "error",
|
||||||
|
"clearance": "error",
|
||||||
|
"connection_width": "error",
|
||||||
|
"copper_edge_clearance": "error",
|
||||||
|
"copper_sliver": "warning",
|
||||||
|
"courtyards_overlap": "error",
|
||||||
|
"diff_pair_gap_out_of_range": "error",
|
||||||
|
"diff_pair_uncoupled_length_too_long": "error",
|
||||||
|
"drill_out_of_range": "error",
|
||||||
|
"duplicate_footprints": "warning",
|
||||||
|
"extra_footprint": "warning",
|
||||||
|
"footprint_type_mismatch": "warning",
|
||||||
|
"hole_clearance": "error",
|
||||||
|
"hole_near_hole": "error",
|
||||||
|
"invalid_outline": "error",
|
||||||
|
"isolated_copper": "warning",
|
||||||
|
"item_on_disabled_layer": "error",
|
||||||
|
"items_not_allowed": "error",
|
||||||
|
"length_out_of_range": "error",
|
||||||
|
"lib_footprint_issues": "warning",
|
||||||
|
"lib_footprint_mismatch": "warning",
|
||||||
|
"malformed_courtyard": "error",
|
||||||
|
"microvia_drill_out_of_range": "error",
|
||||||
|
"missing_courtyard": "ignore",
|
||||||
|
"missing_footprint": "warning",
|
||||||
|
"net_conflict": "warning",
|
||||||
|
"npth_inside_courtyard": "ignore",
|
||||||
|
"overlapping_pads": "warning",
|
||||||
|
"padstack": "warning",
|
||||||
|
"pth_inside_courtyard": "ignore",
|
||||||
|
"shorting_items": "error",
|
||||||
|
"silk_edge_clearance": "warning",
|
||||||
|
"silk_over_copper": "warning",
|
||||||
|
"silk_overlap": "warning",
|
||||||
|
"skew_out_of_range": "error",
|
||||||
|
"solder_mask_bridge": "error",
|
||||||
|
"starved_thermal": "error",
|
||||||
|
"text_height": "warning",
|
||||||
|
"text_thickness": "warning",
|
||||||
|
"through_hole_pad_without_hole": "error",
|
||||||
|
"too_many_vias": "error",
|
||||||
|
"track_dangling": "ignore",
|
||||||
|
"track_width": "error",
|
||||||
|
"tracks_crossing": "error",
|
||||||
|
"unconnected_items": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"via_dangling": "warning",
|
||||||
|
"zones_intersect": "error"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"max_error": 0.005,
|
||||||
|
"min_clearance": 0.0,
|
||||||
|
"min_connection": 0.12,
|
||||||
|
"min_copper_edge_clearance": 0.0,
|
||||||
|
"min_hole_clearance": 0.25,
|
||||||
|
"min_hole_to_hole": 0.25,
|
||||||
|
"min_microvia_diameter": 0.19999999999999998,
|
||||||
|
"min_microvia_drill": 0.09999999999999999,
|
||||||
|
"min_resolved_spokes": 2,
|
||||||
|
"min_silk_clearance": 0.0,
|
||||||
|
"min_text_height": 0.7999999999999999,
|
||||||
|
"min_text_thickness": 0.12,
|
||||||
|
"min_through_hole_diameter": 0.3,
|
||||||
|
"min_track_width": 0.19999999999999998,
|
||||||
|
"min_via_annular_width": 0.049999999999999996,
|
||||||
|
"min_via_diameter": 0.39999999999999997,
|
||||||
|
"solder_mask_clearance": 0.0,
|
||||||
|
"solder_mask_min_width": 0.0,
|
||||||
|
"solder_mask_to_copper_clearance": 0.0,
|
||||||
|
"use_height_for_length_calcs": true
|
||||||
|
},
|
||||||
|
"teardrop_options": [
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 5,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_onpadsmd": true,
|
||||||
|
"td_onroundshapesonly": false,
|
||||||
|
"td_ontrackend": false,
|
||||||
|
"td_onviapad": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"teardrop_parameters": [
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_round_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_rect_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_track_end",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"track_widths": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"via_dimensions": [
|
||||||
|
{
|
||||||
|
"diameter": 0.0,
|
||||||
|
"drill": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zones_allow_external_fillets": false
|
||||||
|
},
|
||||||
|
"layer_presets": [],
|
||||||
|
"viewports": []
|
||||||
|
},
|
||||||
|
"boards": [],
|
||||||
|
"cvpcb": {
|
||||||
|
"equivalence_files": []
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"pinned_footprint_libs": [],
|
||||||
|
"pinned_symbol_libs": []
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"filename": "connection_width.kicad_pro",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_settings": {
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"bus_width": 12.0,
|
||||||
|
"clearance": 0.1,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.2,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "Default",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.1,
|
||||||
|
"via_diameter": 0.8,
|
||||||
|
"via_drill": 0.4,
|
||||||
|
"wire_width": 6.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bus_width": 12.0,
|
||||||
|
"clearance": 0.1,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.2,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "High_current",
|
||||||
|
"nets": [
|
||||||
|
"net_HC"
|
||||||
|
],
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.25,
|
||||||
|
"via_diameter": 0.8,
|
||||||
|
"via_drill": 0.4,
|
||||||
|
"wire_width": 6.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"net_colors": null
|
||||||
|
},
|
||||||
|
"pcbnew": {
|
||||||
|
"last_paths": {
|
||||||
|
"gencad": "",
|
||||||
|
"idf": "",
|
||||||
|
"netlist": "",
|
||||||
|
"specctra_dsn": "",
|
||||||
|
"step": "",
|
||||||
|
"vrml": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": ""
|
||||||
|
},
|
||||||
|
"schematic": {
|
||||||
|
"legacy_lib_dir": "",
|
||||||
|
"legacy_lib_list": []
|
||||||
|
},
|
||||||
|
"sheets": [],
|
||||||
|
"text_variables": {}
|
||||||
|
}
|
|
@ -48,14 +48,15 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperConn, DRC_REGRESSION_TEST_FIXTURE )
|
||||||
{
|
{
|
||||||
// Check for minimum copper connection errors
|
// Check for minimum copper connection errors
|
||||||
|
|
||||||
std::vector<wxString> tests =
|
std::vector<std::pair<wxString, int>> tests =
|
||||||
{
|
{
|
||||||
"issue9870"
|
{ "issue9870", 12 },
|
||||||
|
{ "connection_width_rules", 3 }
|
||||||
};
|
};
|
||||||
|
|
||||||
for( const wxString& relPath : tests )
|
for( const std::pair<wxString, int>& test : tests )
|
||||||
{
|
{
|
||||||
KI_TEST::LoadBoard( m_settingsManager, relPath, m_board );
|
KI_TEST::LoadBoard( m_settingsManager, test.first, m_board );
|
||||||
KI_TEST::FillZones( m_board.get() );
|
KI_TEST::FillZones( m_board.get() );
|
||||||
|
|
||||||
std::vector<DRC_ITEM> violations;
|
std::vector<DRC_ITEM> violations;
|
||||||
|
@ -82,10 +83,10 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperConn, DRC_REGRESSION_TEST_FIXTURE )
|
||||||
|
|
||||||
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
|
bds.m_DRCEngine->RunTests( EDA_UNITS::MILLIMETRES, true, false );
|
||||||
|
|
||||||
if( violations.size() == 12 )
|
if( violations.size() == test.second )
|
||||||
{
|
{
|
||||||
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
|
BOOST_CHECK_EQUAL( 1, 1 ); // quiet "did not check any assertions" warning
|
||||||
BOOST_TEST_MESSAGE( wxString::Format( "DRC connection width: %s, passed", relPath ) );
|
BOOST_TEST_MESSAGE( wxString::Format( "DRC connection width: %s, passed", test.first ) );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -98,7 +99,7 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperConn, DRC_REGRESSION_TEST_FIXTURE )
|
||||||
itemMap ) );
|
itemMap ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_ERROR( wxString::Format( "DRC connection width: %s, failed", relPath ) );
|
BOOST_ERROR( wxString::Format( "DRC connection width: %s, failed", test.first ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue