kicad/pcbnew/drc/drc_clearance_test_function...

547 lines
21 KiB
C++
Raw Normal View History

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
2019-06-04 10:45:43 +00:00
* Copyright (C) 2019 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 <fctsys.h>
#include <trigo.h>
#include <drc/drc.h>
#include <class_board.h>
#include <class_track.h>
#include <class_drawsegment.h>
#include <class_marker_pcb.h>
#include <geometry/polygon_test_point_inside.h>
2020-04-26 14:08:11 +00:00
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h>
void DRC::doSingleViaDRC( BOARD_COMMIT& aCommit, VIA* aRefVia )
{
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
// test if the via size is smaller than minimum
if( aRefVia->GetViaType() == VIATYPE::MICROVIA )
{
if( aRefVia->GetWidth() < bds.m_MicroViasMinSize )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ANNULUS );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), bds.m_MicroViasMinSize, true ),
MessageTextFromValue( userUnits(), aRefVia->GetWidth(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefVia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefVia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
}
else
{
if( aRefVia->GetWidth() < bds.m_ViasMinSize )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ANNULUS );
m_msg.Printf( drcItem->GetErrorText() + _( " (board minimum %s; actual %s)" ),
MessageTextFromValue( userUnits(), bds.m_ViasMinSize, true ),
MessageTextFromValue( userUnits(), aRefVia->GetWidth(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefVia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefVia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
}
// test if via's hole is bigger than its diameter
// This test is necessary since the via hole size and width can be modified
// and a default via hole can be bigger than some vias sizes
if( aRefVia->GetDrillValue() > aRefVia->GetWidth() )
{
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_VIA_HOLE_BIGGER );
m_msg.Printf( drcItem->GetErrorText() + _( " (diameter %s; drill %s)" ),
MessageTextFromValue( userUnits(), aRefVia->GetWidth(), true ),
MessageTextFromValue( userUnits(), aRefVia->GetDrillValue(), true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefVia );
2020-05-18 00:20:16 +00:00
MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefVia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
// test if the type of via is allowed due to design rules
if( aRefVia->GetViaType() == VIATYPE::MICROVIA && !bds.m_MicroViasAllowed )
{
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ALLOWED_ITEMS );
m_msg.Printf( _( "Microvia not allowed (board design rule constraints)" ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefVia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefVia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
// test if the type of via is allowed due to design rules
if( aRefVia->GetViaType() == VIATYPE::BLIND_BURIED && !bds.m_BlindBuriedViaAllowed )
{
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ALLOWED_ITEMS );
m_msg.Printf( _( "Blind/buried via not allowed (board design rule constraints)" ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefVia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefVia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
// For microvias: test if they are blind vias and only between 2 layers
// because they are used for very small drill size and are drill by laser
// and **only one layer** can be drilled
if( aRefVia->GetViaType() == VIATYPE::MICROVIA )
{
PCB_LAYER_ID layer1, layer2;
bool err = true;
aRefVia->LayerPair( &layer1, &layer2 );
if( layer1 > layer2 )
std::swap( layer1, layer2 );
if( layer2 == B_Cu && layer1 == bds.GetCopperLayerCount() - 2 )
err = false;
else if( layer1 == F_Cu && layer2 == In1_Cu )
err = false;
else if ( layer1 == layer2 - 1 )
err = false;
if( err )
{
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_PADSTACK );
m_msg.Printf( _( "Microvia through too many layers (%s and %s not adjacent)" ),
m_pcb->GetLayerName( layer1 ),
m_pcb->GetLayerName( layer2 ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefVia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefVia->GetPosition() );
addMarkerToPcb( aCommit, marker );
}
}
}
void DRC::doSingleTrackDRC( BOARD_COMMIT& aCommit, TRACK* aRefSeg )
{
SHAPE_SEGMENT refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd(), aRefSeg->GetWidth() );
EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
int refSegWidth = aRefSeg->GetWidth();
int minWidth, maxWidth;
aRefSeg->GetWidthConstraints( &minWidth, &maxWidth, &m_clearanceSource );
int errorCode = 0;
int constraintWidth;
if( refSegWidth < minWidth )
{
errorCode = DRCE_TRACK_WIDTH;
constraintWidth = minWidth;
}
else if( refSegWidth > maxWidth )
{
errorCode = DRCE_TRACK_WIDTH;
constraintWidth = maxWidth;
}
if( errorCode )
{
wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( errorCode );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), constraintWidth, true ),
MessageTextFromValue( userUnits(), refSegWidth, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refsegMiddle );
addMarkerToPcb( aCommit, marker );
}
}
void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aStartIt,
TRACKS::iterator aEndIt, bool aTestZones, PCB_LAYER_ID aLayer )
{
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
SHAPE_SEGMENT refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd(), aRefSeg->GetWidth() );
EDA_RECT refSegBB = aRefSeg->GetBoundingBox();
SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
int halfWidth = ( aRefSeg->GetWidth() + 1 ) / 2;
/******************************************/
/* Phase 0 : via DRC tests : */
/******************************************/
if( aRefSeg->Type() == PCB_VIA_T )
{
VIA* refvia = static_cast<VIA*>( aRefSeg );
2020-05-18 00:20:16 +00:00
int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2;
int minAnnulus = refvia->GetMinAnnulus( aLayer, &m_clearanceSource );
if( !refvia->IsPadOnLayer( aLayer ) )
{
halfWidth = ( refvia->GetDrillValue() + 1 ) / 2;
refSegBB = EDA_RECT( refvia->GetStart(),
wxSize( refvia->GetDrillValue(), refvia->GetDrillValue() ) );
}
// test if the via size is smaller than minimum
2019-12-28 00:55:11 +00:00
if( refvia->GetViaType() == VIATYPE::MICROVIA )
{
if( viaAnnulus < minAnnulus )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ANNULUS );
m_msg.Printf( _( "Via annulus too small (%s %s; actual %s)" ),
2020-05-18 00:20:16 +00:00
m_clearanceSource,
MessageTextFromValue( userUnits(), minAnnulus, true ),
MessageTextFromValue( userUnits(), viaAnnulus, true ) );
2020-05-18 00:20:16 +00:00
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
2020-05-18 00:20:16 +00:00
addMarkerToPcb( aCommit, marker );
}
}
else
{
if( bds.m_ViasMinAnnulus > minAnnulus )
{
minAnnulus = bds.m_ViasMinAnnulus;
m_clearanceSource = _( "board minimum" );
}
2020-05-11 19:39:30 +00:00
if( viaAnnulus < minAnnulus )
2020-05-11 19:39:30 +00:00
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ANNULUS );
2020-05-11 19:39:30 +00:00
2020-07-06 11:47:42 +00:00
m_msg.Printf( _( "Via annulus too small (%s %s; actual %s)" ),
2020-05-18 00:20:16 +00:00
m_clearanceSource,
MessageTextFromValue( userUnits(), minAnnulus, true ),
MessageTextFromValue( userUnits(), viaAnnulus, true ) );
2020-05-11 19:39:30 +00:00
2020-05-18 00:20:16 +00:00
drcItem->SetErrorMessage( m_msg );
2020-05-11 19:39:30 +00:00
drcItem->SetItems( refvia );
MARKER_PCB* marker = new MARKER_PCB( drcItem, refvia->GetPosition() );
2020-05-18 00:20:16 +00:00
addMarkerToPcb( aCommit, marker );
2020-05-11 19:39:30 +00:00
}
}
}
/******************************************/
/* Phase 1 : test DRC track to pads : */
/******************************************/
// Compute the min distance to pads
for( MODULE* mod : m_pcb->Modules() )
{
// Don't preflight at the module level. Getting a module's bounding box goes
// through all its pads anyway (so it's no faster), and also all its drawings
// (so it's in fact slower).
for( D_PAD* pad : mod->Pads() )
{
// Preflight based on bounding boxes.
EDA_RECT inflatedBB = refSegBB;
2020-05-18 00:20:16 +00:00
inflatedBB.Inflate( pad->GetBoundingRadius() + m_largestClearance );
if( !inflatedBB.Contains( pad->GetPosition() ) )
continue;
if( !( pad->IsOnLayer( aLayer ) ) )
2020-04-27 17:03:38 +00:00
continue;
// No need to check pads with the same net as the refSeg.
if( pad->GetNetCode() && aRefSeg->GetNetCode() == pad->GetNetCode() )
continue;
if( pad->GetDrillSize().x > 0 )
{
const SHAPE_SEGMENT* slot = pad->GetEffectiveHoleShape();
const DRC_CONSTRAINT* constraint = GetConstraint( aRefSeg, pad,
DRC_CONSTRAINT_TYPE_CLEARANCE,
aLayer, &m_clearanceSource );
int minClearance;
int actual;
if( constraint )
{
m_clearanceSource = wxString::Format( _( "'%s' rule" ), m_clearanceSource );
minClearance = constraint->m_Value.Min();
}
else
{
minClearance = aRefSeg->GetClearance( aLayer, nullptr, &m_clearanceSource );
}
if( pad->GetAttribute() == PAD_ATTRIB_STANDARD )
minClearance += bds.GetHolePlatingThickness();
if( slot->Collide( &refSeg, minClearance + bds.GetDRCEpsilon(), &actual ) )
{
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
2020-05-18 00:20:16 +00:00
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
2020-05-18 00:20:16 +00:00
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, pad );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pad->GetPosition() );
2020-05-18 00:20:16 +00:00
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
}
/// Skip checking pad copper when it has been removed
if( !pad->IsPadOnLayer( aLayer ) )
continue;
int minClearance = aRefSeg->GetClearance( aLayer, pad, &m_clearanceSource );
2020-05-18 00:20:16 +00:00
int actual;
if( pad->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual ) )
{
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
2020-05-18 00:20:16 +00:00
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
2020-05-18 00:20:16 +00:00
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, pad );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pad->GetPosition() );
2020-05-18 00:20:16 +00:00
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
}
}
/***********************************************/
/* Phase 2: test DRC with other track segments */
/***********************************************/
// Test the reference segment with other track segments
2019-05-31 02:30:28 +00:00
for( auto it = aStartIt; it != aEndIt; it++ )
{
TRACK* track = *it;
// No problem if segments have the same net code:
if( aRefSeg->GetNetCode() == track->GetNetCode() )
continue;
if( !track->GetLayerSet().test( aLayer ) )
continue;
2020-05-18 00:20:16 +00:00
// Preflight based on worst-case inflated bounding boxes:
EDA_RECT trackBB = track->GetBoundingBox();
2020-05-18 00:20:16 +00:00
trackBB.Inflate( m_largestClearance );
if( !trackBB.Intersects( refSegBB ) )
continue;
int minClearance = aRefSeg->GetClearance( aLayer, track, &m_clearanceSource );
int actual;
SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() );
/// Check to see if the via has a pad on this layer
if( track->Type() == PCB_VIA_T )
{
VIA* via = static_cast<VIA*>( track );
if( !via->IsPadOnLayer( aLayer ) )
trackSeg.SetWidth( via->GetDrillValue() );
}
// Check two tracks crossing first as it reports a DRCE without distances
if( OPT_VECTOR2I intersection = refSeg.GetSeg().Intersect( trackSeg.GetSeg() ) )
{
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
2020-05-18 00:20:16 +00:00
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, track );
MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) intersection.get() );
2020-05-18 00:20:16 +00:00
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
else if( refSeg.Collide( &trackSeg, minClearance - bds.GetDRCEpsilon(), &actual ) )
{
wxPoint pos = GetLocation( aRefSeg, trackSeg.GetSeg() );
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
2020-05-18 00:20:16 +00:00
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
2020-05-18 00:20:16 +00:00
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, track );
MARKER_PCB* marker = new MARKER_PCB( drcItem, pos );
2020-05-18 00:20:16 +00:00
addMarkerToPcb( aCommit, marker );
if( !m_reportAllTrackErrors )
return;
}
}
/***************************************/
/* Phase 3: test DRC with copper zones */
/***************************************/
// Can be *very* time consumming.
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
2020-09-10 23:05:20 +00:00
if( aTestZones )
{
for( ZONE_CONTAINER* zone : m_pcb->Zones() )
{
if( !zone->GetLayerSet().test( aLayer ) || zone->GetIsKeepout() )
continue;
if( zone->GetNetCode() && zone->GetNetCode() == aRefSeg->GetNetCode() )
continue;
if( zone->GetFilledPolysList( aLayer ).IsEmpty() )
continue;
int minClearance = aRefSeg->GetClearance( aLayer, zone, &m_clearanceSource );
int allowedDist = minClearance + halfWidth - bds.GetDRCEpsilon();
int actual;
if( zone->GetFilledPolysList( aLayer ).Collide( testSeg, allowedDist, &actual ) )
{
actual = std::max( 0, actual - halfWidth );
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, zone );
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
2020-09-10 23:05:20 +00:00
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aLayer, aRefSeg, zone ) );
addMarkerToPcb( aCommit, marker );
}
}
}
/***********************************************/
/* Phase 4: test DRC with to board edge */
/***********************************************/
if( m_board_outline_valid )
{
int minClearance = bds.m_CopperEdgeClearance;
m_clearanceSource = _( "board edge" );
static DRAWSEGMENT dummyEdge;
dummyEdge.SetParent( m_pcb );
dummyEdge.SetLayer( Edge_Cuts );
aRefSeg->GetRuleClearance( &dummyEdge, aLayer, &minClearance, &m_clearanceSource );
int center2centerAllowed = minClearance + halfWidth - bds.GetDRCEpsilon();
for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
{
SEG::ecoord center2center_squared = testSeg.SquaredDistance( *it );
if( center2center_squared < SEG::Square( center2centerAllowed ) )
{
VECTOR2I pt = testSeg.NearestPoint( *it );
KICAD_T types[] = { PCB_LINE_T, EOT };
DRAWSEGMENT* edge = nullptr;
INSPECTOR_FUNC inspector =
[&] ( EDA_ITEM* item, void* testData )
{
DRAWSEGMENT* test_edge = dynamic_cast<DRAWSEGMENT*>( item );
if( !test_edge || test_edge->GetLayer() != Edge_Cuts )
return SEARCH_RESULT::CONTINUE;
if( test_edge->HitTest( (wxPoint) pt, minClearance + halfWidth ) )
{
edge = test_edge;
return SEARCH_RESULT::QUIT;
}
return SEARCH_RESULT::CONTINUE;
};
// Best-efforts search for edge segment
BOARD::IterateForward<BOARD_ITEM*>( m_pcb->Drawings(), inspector, nullptr, types );
int actual = std::max( 0.0, sqrt( center2center_squared ) - halfWidth );
2020-08-11 13:33:16 +00:00
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_COPPER_EDGE_CLEARANCE );
2020-05-18 00:20:16 +00:00
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
2020-05-18 00:20:16 +00:00
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, edge );
MARKER_PCB* marker = new MARKER_PCB( drcItem, (wxPoint) pt );
2020-05-18 00:20:16 +00:00
addMarkerToPcb( aCommit, marker );
}
}
}
}