diff --git a/common/drc_rules.keywords b/common/drc_rules.keywords index 3172106092..ecb7d71bd0 100644 --- a/common/drc_rules.keywords +++ b/common/drc_rules.keywords @@ -4,6 +4,7 @@ board_edge buried_via clearance condition +connection_width constraint courtyard_clearance diff_pair_gap diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 932877dc28..7c8c23aa5a 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -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_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_ZoneKeepExternalFillets = false; diff --git a/pcbnew/dialogs/dialog_drc.cpp b/pcbnew/dialogs/dialog_drc.cpp index 54aba7da5d..5cbb51b9c8 100644 --- a/pcbnew/dialogs/dialog_drc.cpp +++ b/pcbnew/dialogs/dialog_drc.cpp @@ -577,7 +577,8 @@ void DIALOG_DRC::OnDRCItemRClick( wxDataViewEvent& aEvent ) || rcItem->GetErrorCode() == DRCE_VIA_DIAMETER || rcItem->GetErrorCode() == DRCE_ANNULAR_WIDTH || 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..." ) ); } diff --git a/pcbnew/dialogs/panel_setup_rules.cpp b/pcbnew/dialogs/panel_setup_rules.cpp index 239b0ebfb6..a3c8871955 100644 --- a/pcbnew/dialogs/panel_setup_rules.cpp +++ b/pcbnew/dialogs/panel_setup_rules.cpp @@ -368,6 +368,7 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent ) tokens = wxT( "annular_width|" "assertion|" "clearance|" + "connection_width|" "courtyard_clearance|" "diff_pair_gap|" "diff_pair_uncoupled|" diff --git a/pcbnew/dialogs/panel_setup_rules_help.md b/pcbnew/dialogs/panel_setup_rules_help.md index 896bc5503e..1ca4df4c07 100644 --- a/pcbnew/dialogs/panel_setup_rules_help.md +++ b/pcbnew/dialogs/panel_setup_rules_help.md @@ -24,6 +24,7 @@ * annular\_width * clearance + * connection\_width * courtyard_clearance * diff\_pair\_gap * diff\_pair\_uncoupled @@ -272,3 +273,10 @@ For the latter use a `(layer "layer_name")` clause in the rule. (layer "F.Courtyard") (constraint physical_clearance (min 3mm)) (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'")) \ No newline at end of file diff --git a/pcbnew/dialogs/panel_setup_rules_help_md.h b/pcbnew/dialogs/panel_setup_rules_help_md.h index e30df2cefb..defb493c5c 100644 --- a/pcbnew/dialogs/panel_setup_rules_help_md.h +++ b/pcbnew/dialogs/panel_setup_rules_help_md.h @@ -25,6 +25,7 @@ _HKI( "### Top-level Clauses\n" "\n" " * annular\\_width\n" " * clearance\n" +" * connection\\_width\n" " * courtyard_clearance\n" " * diff\\_pair\\_gap\n" " * diff\\_pair\\_uncoupled\n" @@ -273,4 +274,10 @@ _HKI( "### Top-level Clauses\n" " (layer \"F.Courtyard\")\n" " (constraint physical_clearance (min 3mm))\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'\"))" ); diff --git a/pcbnew/drc/drc_engine.cpp b/pcbnew/drc/drc_engine.cpp index 6d91180a6e..18b2f8f707 100644 --- a/pcbnew/drc/drc_engine.cpp +++ b/pcbnew/drc/drc_engine.cpp @@ -152,6 +152,10 @@ void DRC_ENGINE::loadImplicitRules() widthConstraint.Value().SetMin( bds.m_TrackMinWidth ); rule->AddConstraint( widthConstraint ); + DRC_CONSTRAINT connectionConstraint( CONNECTION_WIDTH_CONSTRAINT ); + connectionConstraint.Value().SetMin( bds.m_MinConn ); + rule->AddConstraint( connectionConstraint ); + DRC_CONSTRAINT drillConstraint( HOLE_SIZE_CONSTRAINT ); drillConstraint.Value().SetMin( bds.m_MinThroughDrill ); rule->AddConstraint( drillConstraint ); @@ -890,6 +894,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO case TEXT_THICKNESS_CONSTRAINT: case DIFF_PAIR_GAP_CONSTRAINT: case LENGTH_CONSTRAINT: + case CONNECTION_WIDTH_CONSTRAINT: { if( aReporter ) { @@ -958,12 +963,8 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO break; case TEXT_HEIGHT_CONSTRAINT: - REPORT( wxString::Format( _( "Checking %s: min %s." ), - EscapeHTML( c->constraint.GetName() ), - min ) ) - break; - case TEXT_THICKNESS_CONSTRAINT: + case CONNECTION_WIDTH_CONSTRAINT: REPORT( wxString::Format( _( "Checking %s: min %s." ), EscapeHTML( c->constraint.GetName() ), min ) ) @@ -1487,10 +1488,15 @@ bool DRC_ENGINE::IsErrorLimitExceeded( int error_code ) void DRC_ENGINE::ReportViolation( const std::shared_ptr& aItem, const VECTOR2I& aPos, PCB_LAYER_ID aMarkerLayer ) { + static std::mutex globalLock; + m_errorLimits[ aItem->GetErrorCode() ] -= 1; if( m_violationHandler ) + { + std::lock_guard guard( globalLock ); m_violationHandler( aItem, aPos, aMarkerLayer ); + } if( m_reporter ) { @@ -1605,6 +1611,20 @@ bool DRC_ENGINE::QueryWorstConstraint( DRC_CONSTRAINT_T aConstraintId, DRC_CONST } +std::set DRC_ENGINE::QueryDistinctConstraints( DRC_CONSTRAINT_T aConstraintId ) +{ + std::set 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? int DRC_ENGINE::MatchDpSuffix( const wxString& aNetName, wxString& aComplementNet, wxString& aBaseDpName ) diff --git a/pcbnew/drc/drc_engine.h b/pcbnew/drc/drc_engine.h index 3bb1405264..e824f0d31a 100644 --- a/pcbnew/drc/drc_engine.h +++ b/pcbnew/drc/drc_engine.h @@ -1,7 +1,7 @@ /* * 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 * modify it under the terms of the GNU General Public License @@ -176,6 +176,7 @@ public: bool IsCancelled() const; bool QueryWorstConstraint( DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT& aConstraint ); + std::set QueryDistinctConstraints( DRC_CONSTRAINT_T aConstraintId ); std::vector GetTestProviders() const { return m_testProviders; }; diff --git a/pcbnew/drc/drc_rule.h b/pcbnew/drc/drc_rule.h index b9e4a45c58..bddc2caa85 100644 --- a/pcbnew/drc/drc_rule.h +++ b/pcbnew/drc/drc_rule.h @@ -68,7 +68,8 @@ enum DRC_CONSTRAINT_T VIA_COUNT_CONSTRAINT, PHYSICAL_CLEARANCE_CONSTRAINT, PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, - ASSERTION_CONSTRAINT + ASSERTION_CONSTRAINT, + CONNECTION_WIDTH_CONSTRAINT }; diff --git a/pcbnew/drc/drc_rule_parser.cpp b/pcbnew/drc/drc_rule_parser.cpp index 73686a425a..331604f666 100644 --- a/pcbnew/drc/drc_rule_parser.cpp +++ b/pcbnew/drc/drc_rule_parser.cpp @@ -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_thickness: c.m_Type = TEXT_THICKNESS_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_via_diameter: c.m_Type = VIA_DIAMETER_CONSTRAINT; break; case T_zone_connection: c.m_Type = ZONE_CONNECTION_CONSTRAINT; break; diff --git a/pcbnew/drc/drc_test_provider_connection_width.cpp b/pcbnew/drc/drc_test_provider_connection_width.cpp index cac75b0097..44edd05dd7 100644 --- a/pcbnew/drc/drc_test_provider_connection_width.cpp +++ b/pcbnew/drc/drc_test_provider_connection_width.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -569,19 +570,24 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run() if( !reportPhase( _( "Checking nets for minimum connection width..." ) ) ) return false; // DRC cancelled - LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask(); - LSEQ copperLayers = copperLayerSet.Seq(); - - BOARD* board = m_drcEngine->GetBoard(); - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); + LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask(); + LSEQ copperLayers = copperLayerSet.Seq(); + BOARD* board = m_drcEngine->GetBoard(); + DRC_RTREE* tree = board->m_CopperItemRTreeCache.get(); + std::set distinctMinWidths + = m_drcEngine->QueryDistinctConstraints( CONNECTION_WIDTH_CONSTRAINT ); if( m_drcEngine->IsCancelled() ) return false; // DRC cancelled - std::map>> net_items; - std::atomic done( 1 ); + struct ITEMS_POLY + { + std::set items; + SHAPE_POLY_SET poly; + }; - DRC_RTREE* tree = board->m_CopperItemRTreeCache.get(); + std::map< std::pair, ITEMS_POLY > dataset; + std::atomic done( 1 ); auto calc_effort = [&]( const std::set& items, PCB_LAYER_ID aLayer ) -> size_t @@ -604,28 +610,38 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run() return effort; }; - auto min_checker = - [&](const std::set& aItems, PCB_LAYER_ID aLayer ) -> size_t + auto build_netlayer_polys = + [&]( int aNetcode, const PCB_LAYER_ID aLayer ) -> size_t { if( m_drcEngine->IsCancelled() ) 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, - ERROR_OUTSIDE ); + item->TransformShapeWithClearanceToPolygon( data.poly, aLayer, 0, + 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; - POLYGON_TEST test( minimum_width ); + done.fetch_add( calc_effort( data.items, aLayer ) ); - 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 ); 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 ) ); VECTOR2I location = ( span.A + span.B ) / 2; int dist = ( span.A - span.B ).EuclideanNorm(); - std::set items = tree->GetObjectsAt( location, aLayer, - minimum_width ); + std::set nearbyItems = tree->GetObjectsAt( location, aLayer, + aMinWidth ); - std::shared_ptr drce = DRC_ITEM::Create( DRCE_CONNECTION_WIDTH ); - wxString msg; + std::vector items; - msg.Printf( _( "Minimum connection width %s; actual %s" ), - MessageTextFromValue( userUnits(), minimum_width ), - MessageTextFromValue( userUnits(), dist ) ); - - drce->SetErrorMessage( msg + wxS( " " ) + layerDesc( aLayer ) ); - - for( BOARD_ITEM* item : items ) + for( BOARD_ITEM* item : nearbyItems ) { - if( item->HitTest( location, minimum_width ) ) - drce->AddItem( item ); + if( item->HitTest( location, aMinWidth ) ) + items.push_back( item ); } - if( !drce->GetIDs().empty() ) - reportViolation( drce, location, aLayer ); + if( !items.empty() ) + { + 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; }; for( PCB_LAYER_ID layer : copperLayers ) { - auto& layer_items = net_items[layer]; - for( ZONE* zone : board->m_DRCCopperZones ) { 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() ) @@ -678,11 +709,11 @@ bool DRC_TEST_PROVIDER_CONNECTION_WIDTH::Run() if( PCB_VIA* via = dynamic_cast( track ) ) { if( via->FlashLayer( static_cast( layer ) ) ) - layer_items[via->GetNetCode()].emplace( via ); + dataset[ { via->GetNetCode(), layer } ].items.emplace( via ); } 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() ) { if( pad->FlashLayer( static_cast( layer ) ) ) - layer_items[pad->GetNetCode()].emplace( pad ); + dataset[ { pad->GetNetCode(), layer } ].items.emplace( pad ); } // Footprint zones are also in the m_DRCCopperZones cache } } - thread_pool& tp = GetKiCadThreadPool(); + thread_pool& tp = GetKiCadThreadPool(); std::vector> returns; - size_t return_count = 0; - size_t total_effort = 0; + size_t total_effort = 0; - for( auto& layer_items : net_items ) - return_count += layer_items.second.size(); + for( const std::pair, ITEMS_POLY>& netLayer : dataset ) + 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, 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& retval : returns ) + { + std::future_status status; + + do { - returns.emplace_back( tp.submit( min_checker, items.second, layer_items.first ) ); - total_effort += calc_effort( items.second, layer_items.first ); + m_drcEngine->ReportProgress( static_cast( done ) / total_effort ); + 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, ITEMS_POLY>& netLayer : dataset ) + { + for( int minWidth : distinctMinWidths ) + { + returns.emplace_back( tp.submit( min_checker, netLayer.second, netLayer.first.second, + minWidth ) ); } } diff --git a/pcbnew/drc/drc_test_provider_sliver_checker.cpp b/pcbnew/drc/drc_test_provider_sliver_checker.cpp index 4be73711f2..b5264530e2 100644 --- a/pcbnew/drc/drc_test_provider_sliver_checker.cpp +++ b/pcbnew/drc/drc_test_provider_sliver_checker.cpp @@ -89,91 +89,186 @@ bool DRC_TEST_PROVIDER_SLIVER_CHECKER::Run() int testLength = widthTolerance / ( 2 * sin( DEG2RAD( angleTolerance / 2 ) ) ); LSET copperLayerSet = m_drcEngine->GetBoard()->GetEnabledLayers() & LSET::AllCuMask(); 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. - int zoneLayerCount = 0; - std::atomic 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() ) + if( m_drcEngine->IsCancelled() ) return false; // DRC cancelled std::vector layerPolys( layerCount ); + std::vector layerEfforts( layerCount ); + size_t total_effort = 0; + std::atomic done( 1 ); + + auto calc_effort = + [&]( BOARD_ITEM* item, PCB_LAYER_ID aLayer ) -> size_t + { + if( item->Type() == PCB_ZONE_T ) + { + ZONE* zone = static_cast( 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( item) ) + { + ZONE* zone = static_cast( 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 = - [&]( 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]; - SHAPE_POLY_SET& poly = layerPolys[aItem]; + const std::vector& pts = poly.Outline( jj ).CPoints(); + int ptCount = pts.size(); - if( m_drcEngine->IsCancelled() ) - return 0; + for( int kk = 0; kk < ptCount; ++kk ) + { + 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 ), - [&]( BOARD_ITEM* item ) -> bool - { - if( dynamic_cast( item) ) - { - ZONE* zone = static_cast( item ); + VECTOR2I ptAfter = pts[ ( kk + 1 ) % ptCount ]; + VECTOR2I vAfter = ( ptAfter - pt ); - if( !zone->GetIsRuleArea() ) - { - fill = zone->GetFill( layer )->CloneDropTriangulation(); - fill.Unfracture( SHAPE_POLY_SET::PM_FAST ); + if( std::abs( vAfter.x ) < min_len && std::abs( vAfter.y ) < min_len && ptCount > 5 ) + { + ptAfter = pts[ ( kk + 2 ) % ptCount ]; + vAfter = ( ptAfter - pt ); + } - for( int jj = 0; jj < fill.OutlineCount(); ++jj ) - poly.AddOutline( fill.Outline( jj ) ); + VECTOR2I vIncluded = vPrior.Resize( testLength ) - vAfter.Resize( testLength ); - // Report progress on board zones only. Everything - // else is in the noise. - done.fetch_add( 1 ); - } - } - else - { - item->TransformShapeWithClearanceToPolygon( poly, layer, 0, - ARC_LOW_DEF, - ERROR_OUTSIDE ); - } + if( vIncluded.SquaredEuclideanNorm() < SEG::Square( widthTolerance ) ) + { + std::shared_ptr drce = DRC_ITEM::Create( DRCE_COPPER_SLIVER ); + drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + layerDesc( layer ) ); + reportViolation( drce, pt, layer ); + } + } + } - if( m_drcEngine->IsCancelled() ) - return false; + done.fetch_add( layerEfforts[ layer ] ); - return true; - } ); + if( m_drcEngine->IsCancelled() ) + return 0; + return 1; + }; - if( m_drcEngine->IsCancelled() ) - return 0; + for( size_t ii = 0; ii < layerCount; ++ii ) + { + 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 - poly.Deflate( widthTolerance / 2, ARC_LOW_DEF, - SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS ); + total_effort += layerEfforts[ layer ]; + } - return 1; - }; + total_effort *= 2; // Once for building polys; once for checking slivers thread_pool& tp = GetKiCadThreadPool(); std::vector> 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( 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 ) ); for( auto& retval : returns ) @@ -182,66 +277,12 @@ bool DRC_TEST_PROVIDER_SLIVER_CHECKER::Run() do { - m_drcEngine->ReportProgress( static_cast( zoneLayerCount ) / done ); + m_drcEngine->ReportProgress( static_cast( done ) / total_effort ); status = retval.wait_for( std::chrono::milliseconds( 100 ) ); } 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& 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 drce = DRC_ITEM::Create( DRCE_COPPER_SLIVER ); - drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + layerDesc( layer ) ); - reportViolation( drce, pt, layer ); - } - } - } - } - return true; } diff --git a/pcbnew/tools/board_inspection_tool.cpp b/pcbnew/tools/board_inspection_tool.cpp index f2373af692..aefc814bd4 100644 --- a/pcbnew/tools/board_inspection_tool.cpp +++ b/pcbnew/tools/board_inspection_tool.cpp @@ -317,6 +317,20 @@ void BOARD_INSPECTION_TOOL::InspectDRCError( const std::shared_ptr& aDR reportMax( r->GetUnits(), constraint ) ) ); 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: r = m_inspectClearanceDialog->AddPage( _( "Via Diameter" ) ); reportHeader( _( "Via diameter resolution for:" ), a, r ); diff --git a/qa/data/pcbnew/connection_width_rules.kicad_dru b/qa/data/pcbnew/connection_width_rules.kicad_dru new file mode 100644 index 0000000000..08fee13684 --- /dev/null +++ b/qa/data/pcbnew/connection_width_rules.kicad_dru @@ -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')")) diff --git a/qa/data/pcbnew/connection_width_rules.kicad_pcb b/qa/data/pcbnew/connection_width_rules.kicad_pcb new file mode 100644 index 0000000000..b8089dca56 --- /dev/null +++ b/qa/data/pcbnew/connection_width_rules.kicad_pcb @@ -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) + ) + ) + ) +) diff --git a/qa/data/pcbnew/connection_width_rules.kicad_pro b/qa/data/pcbnew/connection_width_rules.kicad_pro new file mode 100644 index 0000000000..d9e6b177ed --- /dev/null +++ b/qa/data/pcbnew/connection_width_rules.kicad_pro @@ -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": {} +} diff --git a/qa/unittests/pcbnew/drc/test_drc_copper_conn.cpp b/qa/unittests/pcbnew/drc/test_drc_copper_conn.cpp index 3160696b35..d70196cf42 100644 --- a/qa/unittests/pcbnew/drc/test_drc_copper_conn.cpp +++ b/qa/unittests/pcbnew/drc/test_drc_copper_conn.cpp @@ -48,14 +48,15 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperConn, DRC_REGRESSION_TEST_FIXTURE ) { // Check for minimum copper connection errors - std::vector tests = + std::vector> tests = { - "issue9870" + { "issue9870", 12 }, + { "connection_width_rules", 3 } }; - for( const wxString& relPath : tests ) + for( const std::pair& 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() ); std::vector violations; @@ -82,10 +83,10 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperConn, DRC_REGRESSION_TEST_FIXTURE ) 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_TEST_MESSAGE( wxString::Format( "DRC connection width: %s, passed", relPath ) ); + BOOST_TEST_MESSAGE( wxString::Format( "DRC connection width: %s, passed", test.first ) ); } else { @@ -98,7 +99,7 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperConn, DRC_REGRESSION_TEST_FIXTURE ) itemMap ) ); } - BOOST_ERROR( wxString::Format( "DRC connection width: %s, failed", relPath ) ); + BOOST_ERROR( wxString::Format( "DRC connection width: %s, failed", test.first ) ); } } }