Implement DRC checks for copper graphic items.

Also greatly improves the location of many DRC markers.

Fixes: lp:1619133
* https://bugs.launchpad.net/kicad/+bug/1619133
This commit is contained in:
Jeff Young 2018-08-27 14:50:50 +01:00
parent 35a31a9787
commit 7b4f9cef50
5 changed files with 386 additions and 429 deletions

View File

@ -32,7 +32,8 @@
#include <trigo.h>
#include <base_units.h>
#include <board_design_settings.h>
#include <class_edge_mod.h>
#include <class_drawsegment.h>
#include <class_module.h>
#include <class_track.h>
#include <class_pad.h>
@ -42,7 +43,7 @@
#include <view/view.h>
#include <geometry/seg.h>
#include <math_for_graphics.h>
#include <geometry/geometry_utils.h>
#include <connectivity_data.h>
#include <connectivity_algo.h>
@ -55,6 +56,8 @@
#include <dialog_drc.h>
#include <wx/progdlg.h>
#include <board_commit.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_arc.h>
void DRC::ShowDRCDialog( wxWindow* aParent )
{
@ -177,7 +180,11 @@ int DRC::DrcOnCreatingTrack( TRACK* aRefSegm, TRACK* aList )
if( !doTrackDrc( aRefSegm, aList, true ) )
{
if( m_currentMarker )
{
m_pcbEditorFrame->SetMsgPanel( m_currentMarker );
delete m_currentMarker;
m_currentMarker = nullptr;
}
m_drcInLegacyRoutingMode = drc_state;
m_reportAllTrackErrors = rpt_state;
@ -186,11 +193,13 @@ int DRC::DrcOnCreatingTrack( TRACK* aRefSegm, TRACK* aList )
if( !doTrackKeepoutDrc( aRefSegm ) )
{
wxASSERT( m_currentMarker );
if( m_currentMarker )
{
m_pcbEditorFrame->SetMsgPanel( m_currentMarker );
delete m_currentMarker;
m_currentMarker = nullptr;
}
m_drcInLegacyRoutingMode = drc_state;
m_reportAllTrackErrors = rpt_state;
return BAD_DRC;
@ -496,7 +505,7 @@ void DRC::RunTests( wxTextCtrl* aMessages )
wxSafeYield();
}
testTexts();
testCopperTextAndGraphics();
// find overlapping courtyard ares.
if( m_pcb->GetDesignSettings().m_ProhibitOverlappingCourtyards
@ -589,8 +598,7 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg )
FmtVal( g.m_TrackMinWidth )
);
addMarkerToPcb( fillMarker( DRCE_NETCLASS_TRACKWIDTH, msg, m_currentMarker ) );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( DRCE_NETCLASS_TRACKWIDTH, msg ) );
ret = false;
}
@ -602,8 +610,7 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg )
FmtVal( g.m_ViasMinSize )
);
addMarkerToPcb( fillMarker( DRCE_NETCLASS_VIASIZE, msg, m_currentMarker ) );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( DRCE_NETCLASS_VIASIZE, msg ) );
ret = false;
}
@ -615,8 +622,7 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg )
FmtVal( g.m_ViasMinDrill )
);
addMarkerToPcb( fillMarker( DRCE_NETCLASS_VIADRILLSIZE, msg, m_currentMarker ) );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( DRCE_NETCLASS_VIADRILLSIZE, msg ) );
ret = false;
}
@ -625,11 +631,9 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg )
msg.Printf( _( "NETCLASS: \"%s\" has uVia Dia:%s which is less than global:%s" ),
GetChars( nc->GetName() ),
FmtVal( nc->GetuViaDiameter() ),
FmtVal( g.m_MicroViasMinSize )
);
FmtVal( g.m_MicroViasMinSize ) );
addMarkerToPcb( fillMarker( DRCE_NETCLASS_uVIASIZE, msg, m_currentMarker ) );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( DRCE_NETCLASS_uVIASIZE, msg ) );
ret = false;
}
@ -638,11 +642,9 @@ bool DRC::doNetClass( const NETCLASSPTR& nc, wxString& msg )
msg.Printf( _( "NETCLASS: \"%s\" has uVia Drill:%s which is less than global:%s" ),
GetChars( nc->GetName() ),
FmtVal( nc->GetuViaDrill() ),
FmtVal( g.m_MicroViasMinDrill )
);
FmtVal( g.m_MicroViasMinDrill ) );
addMarkerToPcb( fillMarker( DRCE_NETCLASS_uVIADRILLSIZE, msg, m_currentMarker ) );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( DRCE_NETCLASS_uVIADRILLSIZE, msg ) );
ret = false;
}
@ -889,24 +891,21 @@ void DRC::testZones()
// if it differs from the net name from net code, there is a DRC issue
for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
{
ZONE_CONTAINER* test_area = m_pcb->GetArea( ii );
ZONE_CONTAINER* zone = m_pcb->GetArea( ii );
if( !test_area->IsOnCopperLayer() )
if( !zone->IsOnCopperLayer() )
continue;
int netcode = test_area->GetNetCode();
int netcode = zone->GetNetCode();
// a netcode < 0 or > 0 and no pad in net is a error or strange
// perhaps a "dead" net, which happens when all pads in this net were removed
// Remark: a netcode < 0 should not happen (this is more a bug somewhere)
int pads_in_net = (test_area->GetNetCode() > 0) ?
m_pcb->GetConnectivity()->GetPadCount( test_area->GetNetCode() ) : 1;
int pads_in_net = ( netcode > 0 ) ? m_pcb->GetConnectivity()->GetPadCount( netcode ) : 1;
if( ( netcode < 0 ) || pads_in_net == 0 )
{
addMarkerToPcb( fillMarker( test_area, test_area->GetPosition(),
DRCE_SUSPICIOUS_NET_FOR_ZONE_OUTLINE, m_currentMarker ) );
m_currentMarker = nullptr;
wxPoint markerPos = zone->GetPosition();
addMarkerToPcb( newMarker( markerPos, zone, DRCE_SUSPICIOUS_NET_FOR_ZONE_OUTLINE ) );
}
}
@ -938,13 +937,10 @@ void DRC::testKeepoutAreas()
if( !area->IsOnLayer( segm->GetLayer() ) )
continue;
if( area->Outline()->Distance( SEG( segm->GetStart(), segm->GetEnd() ),
segm->GetWidth() ) == 0 )
{
addMarkerToPcb( fillMarker( segm, area,
DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker ) );
m_currentMarker = nullptr;
}
SEG trackSeg( segm->GetStart(), segm->GetEnd() );
if( area->Outline()->Distance( trackSeg, segm->GetWidth() ) == 0 )
addMarkerToPcb( newMarker( segm, area, DRCE_TRACK_INSIDE_KEEPOUT ) );
}
else if( segm->Type() == PCB_VIA_T )
{
@ -957,11 +953,7 @@ void DRC::testKeepoutAreas()
continue;
if( area->Outline()->Distance( segm->GetPosition() ) < segm->GetWidth()/2 )
{
addMarkerToPcb( fillMarker( segm, NULL,
DRCE_VIA_INSIDE_KEEPOUT, m_currentMarker ) );
m_currentMarker = nullptr;
}
addMarkerToPcb( newMarker( segm, area, DRCE_VIA_INSIDE_KEEPOUT ) );
}
}
// Test pads: TODO
@ -969,38 +961,151 @@ void DRC::testKeepoutAreas()
}
void DRC::testTexts()
void DRC::testCopperTextAndGraphics()
{
// Test text items for vias, tracks and pads inside text areas
// Test copper items for clearance violations with vias, tracks and pads
for( BOARD_ITEM* brdItem : m_pcb->Drawings() )
{
if( brdItem->Type() == PCB_TEXT_T && IsCopperLayer( brdItem->GetLayer() ) )
doText( brdItem );
if( IsCopperLayer( brdItem->GetLayer() ) )
{
if( brdItem->Type() == PCB_TEXT_T )
testCopperTextItem( brdItem );
else if( brdItem->Type() == PCB_LINE_T )
testCopperDrawItem( static_cast<DRAWSEGMENT*>( brdItem ));
}
}
for( MODULE* module : m_pcb->Modules() )
{
if( IsCopperLayer( module->Reference().GetLayer() ) )
doText( &module->Reference() );
testCopperTextItem( &module->Reference());
if( IsCopperLayer( module->Value().GetLayer() ) )
doText( &module->Value() );
testCopperTextItem( &module->Value());
for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() )
{
if( item->Type() == PCB_MODULE_TEXT_T && IsCopperLayer( item->GetLayer() ) )
doText( item );
if( IsCopperLayer( item->GetLayer() ) )
{
if( item->Type() == PCB_MODULE_TEXT_T )
testCopperTextItem( item );
else if( item->Type() == PCB_MODULE_EDGE_T )
testCopperDrawItem( static_cast<DRAWSEGMENT*>( item ));
}
}
}
}
void DRC::doText( BOARD_ITEM* aTextItem )
void DRC::testCopperDrawItem( DRAWSEGMENT* aItem )
{
std::vector<SEG> itemShape;
int itemWidth = aItem->GetWidth();
switch( aItem->GetShape() )
{
case S_ARC:
{
SHAPE_ARC arc( aItem->GetCenter(), aItem->GetArcStart(), (double) aItem->GetAngle() / 10.0 );
auto l = arc.ConvertToPolyline();
for( int i = 0; i < l.SegmentCount(); i++ )
itemShape.push_back( l.CSegment(i) );
break;
}
case S_SEGMENT:
itemShape.push_back( SEG( aItem->GetStart(), aItem->GetEnd() ) );
break;
case S_CIRCLE:
{
// SHAPE_CIRCLE has no ConvertToPolyline() method, so use a 360.0 SHAPE_ARC
SHAPE_ARC circle( aItem->GetCenter(), aItem->GetEnd(), 360.0 );
auto l = circle.ConvertToPolyline();
for( int i = 0; i < l.SegmentCount(); i++ )
itemShape.push_back( l.CSegment(i) );
break;
}
case S_CURVE:
{
aItem->RebuildBezierToSegmentsPointsList( aItem->GetWidth() );
wxPoint start_pt = aItem->GetBezierPoints()[0];
for( unsigned int jj = 1; jj < aItem->GetBezierPoints().size(); jj++ )
{
wxPoint end_pt = aItem->GetBezierPoints()[jj];
itemShape.push_back( SEG( start_pt, end_pt ) );
start_pt = end_pt;
}
break;
}
default:
break;
}
// Test tracks and vias
for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() )
{
if( !track->IsOnLayer( aItem->GetLayer() ) )
continue;
int minDist = ( track->GetWidth() + itemWidth ) / 2 + track->GetClearance( NULL );
SEG trackAsSeg( track->GetStart(), track->GetEnd() );
for( const auto& itemSeg : itemShape )
{
if( trackAsSeg.Distance( itemSeg ) < minDist )
{
if( track->Type() == PCB_VIA_T )
addMarkerToPcb( newMarker( track, aItem, itemSeg, DRCE_VIA_NEAR_COPPER ) );
else
addMarkerToPcb( newMarker( track, aItem, itemSeg, DRCE_TRACK_NEAR_COPPER ) );
break;
}
}
}
// Test pads
for( auto pad : m_pcb->GetPads() )
{
if( !pad->IsOnLayer( aItem->GetLayer() ) )
continue;
const int segmentCount = 18;
double correctionFactor = GetCircletoPolyCorrectionFactor( segmentCount );
SHAPE_POLY_SET padOutline;
// We incorporate "minDist" into the pad's outline
pad->TransformShapeWithClearanceToPolygon( padOutline, pad->GetClearance( NULL ),
segmentCount, correctionFactor );
for( const auto& itemSeg : itemShape )
{
if( padOutline.Distance( itemSeg, itemWidth ) == 0 )
{
addMarkerToPcb( newMarker( pad, aItem, DRCE_PAD_NEAR_COPPER ) );
break;
}
}
}
}
void DRC::testCopperTextItem( BOARD_ITEM* aTextItem )
{
EDA_TEXT* text = dynamic_cast<EDA_TEXT*>( aTextItem );
std::vector<wxPoint> textShape; // a buffer to store the text shape (set of segments)
std::vector<D_PAD*> padList = m_pcb->GetPads();
int textWidth = text->GetThickness();
// So far the bounding box makes up the text-area
text->TransformTextShapeToSegmentList( textShape );
@ -1008,103 +1113,51 @@ void DRC::doText( BOARD_ITEM* aTextItem )
if( textShape.size() == 0 ) // Should not happen (empty text?)
return;
// Test tracks and vias
for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() )
{
if( !track->IsOnLayer( aTextItem->GetLayer() ) )
continue;
// Test the distance between each segment and the current track/via
int min_dist = ( track->GetWidth() + text->GetThickness() ) / 2 +
track->GetClearance( NULL );
int minDist = ( track->GetWidth() + textWidth ) / 2 + track->GetClearance( NULL );
SEG trackAsSeg( track->GetStart(), track->GetEnd() );
if( track->Type() == PCB_TRACE_T )
{
SEG segref( track->GetStart(), track->GetEnd() );
wxPoint markerPos;
int closestApproach = INT_MAX;
// Error condition: Distance between text segment and track segment is
// smaller than the clearance of the track
for( unsigned jj = 0; jj < textShape.size() && closestApproach > 0; jj += 2 )
{
SEG segtest( textShape[jj], textShape[jj+1] );
int dist = segref.Distance( segtest );
if( dist < closestApproach )
{
markerPos = textShape[jj];
closestApproach = dist;
}
}
if( closestApproach < min_dist )
{
addMarkerToPcb( fillMarker( markerPos, track, aTextItem,
DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) );
m_currentMarker = nullptr;
break;
}
}
else if( track->Type() == PCB_VIA_T )
{
// Error condition: Distance between text segment and via is
// smaller than the clearance of the via
for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
{
SEG segtest( textShape[jj], textShape[jj+1] );
SEG textSeg( textShape[jj], textShape[jj+1] );
if( segtest.PointCloserThan( track->GetPosition(), min_dist ) )
if( trackAsSeg.Distance( textSeg ) < minDist )
{
addMarkerToPcb( fillMarker( track->GetPosition(), track, aTextItem,
DRCE_VIA_INSIDE_TEXT, m_currentMarker ) );
m_currentMarker = nullptr;
if( track->Type() == PCB_VIA_T )
addMarkerToPcb( newMarker( track, aTextItem, textSeg, DRCE_VIA_NEAR_COPPER ) );
else
addMarkerToPcb( newMarker( track, aTextItem, textSeg, DRCE_TRACK_NEAR_COPPER ) );
break;
}
}
}
}
// Test pads
for( unsigned ii = 0; ii < padList.size(); ii++ )
for( auto pad : m_pcb->GetPads() )
{
D_PAD* pad = padList[ii];
if( !pad->IsOnLayer( aTextItem->GetLayer() ) )
continue;
wxPoint shape_pos = pad->ShapePos();
const int segmentCount = 18;
double correctionFactor = GetCircletoPolyCorrectionFactor( segmentCount );
SHAPE_POLY_SET padOutline;
// We incorporate "minDist" into the pad's outline
pad->TransformShapeWithClearanceToPolygon( padOutline, pad->GetClearance( NULL ),
segmentCount, correctionFactor );
for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
{
/* In order to make some calculations more easier or faster,
* pads and tracks coordinates will be made relative
* to the segment origin
*/
wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates
m_segmEnd = textShape[jj+1] - origin;
wxPoint delta = m_segmEnd;
m_segmAngle = 0;
SEG textSeg( textShape[jj], textShape[jj+1] );
// for a non horizontal or vertical segment Compute the segment angle
// in tenths of degrees and its length
if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias
if( padOutline.Distance( textSeg, textWidth ) == 0 )
{
// Compute the segment angle in 0,1 degrees
m_segmAngle = ArcTangente( delta.y, delta.x );
// Compute the segment length: we build an equivalent rotated segment,
// this segment is horizontal, therefore dx = length
RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
}
m_segmLength = delta.x;
m_padToTestPos = shape_pos - origin;
if( !checkClearanceSegmToPad( pad, text->GetThickness(), pad->GetClearance(NULL) ) )
{
addMarkerToPcb( fillMarker( pad, aTextItem, DRCE_PAD_INSIDE_TEXT,
m_currentMarker ) );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( pad, aTextItem, DRCE_PAD_NEAR_COPPER ) );
break;
}
}
@ -1120,10 +1173,7 @@ void DRC::testDisabledLayers()
auto createMarker = [&]( BOARD_ITEM* aItem )
{
m_currentMarker = fillMarker( aItem, aItem->GetPosition(), DRCE_DISABLED_LAYER_ITEM,
m_currentMarker );
addMarkerToPcb( m_currentMarker );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( aItem->GetPosition(), aItem, DRCE_DISABLED_LAYER_ITEM ) );
};
for( auto track : board->Tracks() )
@ -1170,8 +1220,7 @@ bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg )
if( area->Outline()->Distance( SEG( aRefSeg->GetStart(), aRefSeg->GetEnd() ),
aRefSeg->GetWidth() ) == 0 )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker );
m_currentMarker = newMarker( aRefSeg, area, DRCE_TRACK_INSIDE_KEEPOUT );
return false;
}
}
@ -1187,8 +1236,7 @@ bool DRC::doTrackKeepoutDrc( TRACK* aRefSeg )
if( area->Outline()->Distance( aRefSeg->GetPosition() ) < aRefSeg->GetWidth()/2 )
{
m_currentMarker = fillMarker( aRefSeg, NULL,
DRCE_VIA_INSIDE_KEEPOUT, m_currentMarker );
m_currentMarker = newMarker( aRefSeg, area, DRCE_VIA_INSIDE_KEEPOUT );
return false;
}
}
@ -1270,8 +1318,7 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li
if( !checkClearancePadToPad( aRefPad, &dummypad ) )
{
// here we have a drc error on pad!
m_currentMarker = fillMarker( pad, aRefPad,
DRCE_HOLE_NEAR_PAD, m_currentMarker );
m_currentMarker = newMarker( pad, aRefPad, DRCE_HOLE_NEAR_PAD );
return false;
}
}
@ -1287,8 +1334,7 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li
if( !checkClearancePadToPad( pad, &dummypad ) )
{
// here we have a drc error on aRefPad!
m_currentMarker = fillMarker( aRefPad, pad,
DRCE_HOLE_NEAR_PAD, m_currentMarker );
m_currentMarker = newMarker( aRefPad, pad, DRCE_HOLE_NEAR_PAD );
return false;
}
}
@ -1323,7 +1369,7 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li
if( !checkClearancePadToPad( aRefPad, pad ) )
{
// here we have a drc error!
m_currentMarker = fillMarker( aRefPad, pad, DRCE_PAD_NEAR_PAD1, m_currentMarker );
m_currentMarker = newMarker( aRefPad, pad, DRCE_PAD_NEAR_PAD1 );
return false;
}
}
@ -1342,15 +1388,12 @@ bool DRC::doFootprintOverlappingDrc()
// Update courtyard polygons, and test for missing courtyard definition:
for( MODULE* footprint = m_pcb->m_Modules; footprint; footprint = footprint->Next() )
{
wxPoint pos = footprint->GetPosition();
bool is_ok = footprint->BuildPolyCourtyard();
if( !is_ok && m_pcb->GetDesignSettings().m_ProhibitOverlappingCourtyards )
{
m_currentMarker = fillMarker( footprint, footprint->GetPosition(),
DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT,
m_currentMarker );
addMarkerToPcb( m_currentMarker );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( pos, footprint, DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT ) );
success = false;
}
@ -1361,11 +1404,7 @@ bool DRC::doFootprintOverlappingDrc()
footprint->GetPolyCourtyardBack().OutlineCount() == 0 &&
is_ok )
{
m_currentMarker = fillMarker( footprint, footprint->GetPosition(),
DRCE_MISSING_COURTYARD_IN_FOOTPRINT,
m_currentMarker );
addMarkerToPcb( m_currentMarker );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( pos, footprint, DRCE_MISSING_COURTYARD_IN_FOOTPRINT ) );
success = false;
}
}
@ -1399,10 +1438,8 @@ bool DRC::doFootprintOverlappingDrc()
{
//Overlap between footprint and candidate
VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
m_currentMarker = fillMarker( wxPoint( pos.x, pos.y ), footprint, candidate,
DRCE_OVERLAPPING_FOOTPRINTS, m_currentMarker );
addMarkerToPcb( m_currentMarker );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( wxPoint( pos.x, pos.y ), footprint, candidate,
DRCE_OVERLAPPING_FOOTPRINTS ) );
success = false;
}
}
@ -1432,10 +1469,8 @@ bool DRC::doFootprintOverlappingDrc()
{
//Overlap between footprint and candidate
VECTOR2I& pos = courtyard.Vertex( 0, 0, -1 );
m_currentMarker = fillMarker( wxPoint( pos.x, pos.y ), footprint, candidate,
DRCE_OVERLAPPING_FOOTPRINTS, m_currentMarker );
addMarkerToPcb( m_currentMarker );
m_currentMarker = nullptr;
addMarkerToPcb( newMarker( wxPoint( pos.x, pos.y ), footprint, candidate,
DRCE_OVERLAPPING_FOOTPRINTS ) );
success = false;
}
}

View File

@ -31,6 +31,7 @@
#include <vector>
#include <memory>
#include <geometry/seg.h>
#define OK_DRC 0
#define BAD_DRC 1
@ -81,18 +82,18 @@
#define DRCE_VIA_INSIDE_KEEPOUT 38 ///< Via in inside a keepout area
#define DRCE_TRACK_INSIDE_KEEPOUT 39 ///< Track in inside a keepout area
#define DRCE_PAD_INSIDE_KEEPOUT 40 ///< Pad in inside a keepout area
#define DRCE_VIA_INSIDE_TEXT 41 ///< Via in inside a text area
#define DRCE_TRACK_INSIDE_TEXT 42 ///< Track in inside a text area
#define DRCE_PAD_INSIDE_TEXT 43 ///< Pad in inside a text area
#define DRCE_OVERLAPPING_FOOTPRINTS 44 ///< footprint courtyards overlap
#define DRCE_MISSING_COURTYARD_IN_FOOTPRINT 45 ///< footprint has no courtyard defined
#define DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT 46 ///< footprint has a courtyard but malformed
#define DRCE_TRACK_NEAR_COPPER 41 ///< track & copper graphic collide or are too close
#define DRCE_VIA_NEAR_COPPER 42 ///< via and copper graphic collide or are too close
#define DRCE_PAD_NEAR_COPPER 43 ///< pad and copper graphic collide or are too close
#define DRCE_TRACK_NEAR_ZONE 44 ///< track & zone collide or are too close together
#define DRCE_OVERLAPPING_FOOTPRINTS 45 ///< footprint courtyards overlap
#define DRCE_MISSING_COURTYARD_IN_FOOTPRINT 46 ///< footprint has no courtyard defined
#define DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT 47 ///< footprint has a courtyard but malformed
///< (not convertible to a closed polygon with holes)
#define DRCE_MICRO_VIA_NOT_ALLOWED 47 ///< micro vias are not allowed
#define DRCE_BURIED_VIA_NOT_ALLOWED 48 ///< buried vias are not allowed
#define DRCE_DISABLED_LAYER_ITEM 49 ///< item on a disabled layer
#define DRCE_DRILLED_HOLES_TOO_CLOSE 50 ///< overlapping drilled holes break drill bits
#define DRCE_TRACK_NEAR_ZONE 51 ///< track & zone collide or are too close together
#define DRCE_MICRO_VIA_NOT_ALLOWED 48 ///< micro vias are not allowed
#define DRCE_BURIED_VIA_NOT_ALLOWED 49 ///< buried vias are not allowed
#define DRCE_DISABLED_LAYER_ITEM 50 ///< item on a disabled layer
#define DRCE_DRILLED_HOLES_TOO_CLOSE 51 ///< overlapping drilled holes break drill bits
class EDA_DRAW_PANEL;
@ -107,6 +108,10 @@ class MARKER_PCB;
class DRC_ITEM;
class NETCLASS;
class EDA_TEXT;
class DRAWSEGMENT;
class wxWindow;
class wxString;
class wxTextCtrl;
/**
@ -227,53 +232,39 @@ private:
/**
* Function fillMarker
* Optionally creates a marker and fills it in with information, but does not add it to
* the BOARD. Use this to report any kind of DRC problem, or unconnected pad problem.
* Function newMarker
* Creates a marker on a track, via or pad.
*
* @param aTrack/aPad The reference item.
* @param aItem Another item on the BOARD, such as a VIA, SEGZONE, or TRACK, which is
* in conflict with the reference item.
* @param aConflitItem Another item on the board which is in conflict with the
* reference item.
* @param aErrorCode An ID for the particular type of error that is being reported.
* @param fillMe A MARKER_PCB* which is to be filled in, or NULL if one is to be created.
*/
MARKER_PCB* fillMarker( TRACK* aTrack, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe );
MARKER_PCB* newMarker( TRACK* aTrack, BOARD_ITEM* aConflitItem, const SEG& aConflictSeg,
int aErrorCode );
MARKER_PCB* fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe );
MARKER_PCB* newMarker( TRACK* aTrack, ZONE_CONTAINER* aConflictZone, int aErrorCode );
MARKER_PCB* newMarker( D_PAD* aPad, BOARD_ITEM* aConflictItem, int aErrorCode );
/**
* Function fillMarker
* Optionally creates a marker and fills it in with information, but does not add it to
* the BOARD. Use this to report any kind of DRC problem, or unconnected pad problem.
* Function newMarker
* Creates a marker at a given location.
*
* @param aItem The reference item.
* @param aPos Usually the position of the item, but could be more specific for a zone.
* @param aErrorCode An ID for the particular type of error that is being reported.
* @param fillMe A MARKER_PCB* which is to be filled in, or NULL if one is to be created.
*/
MARKER_PCB* fillMarker( BOARD_ITEM* aItem, const wxPoint& aPos, int aErrorCode,
MARKER_PCB* fillMe );
MARKER_PCB* newMarker( const wxPoint& aPos, BOARD_ITEM* aItem, int aErrorCode );
MARKER_PCB* newMarker( const wxPoint& aPos, BOARD_ITEM* aItem, BOARD_ITEM* bItem,
int aErrorCode );
/**
* Function fillMarker
* Optionally creates a marker and fills it in with information, but does not add it to
* the BOARD. Use this to report any kind of DRC problem, or unconnected pad problem.
*
* @param aPos The reference location.
* @param aErrorCode An ID for the particular type of error that is being reported.
* @param aItem The first item in conflict.
* @param bItem (Optional) The second item in conflict or NULL.
* @param aErrorCode An ID for the particular type of error that is being reported.
* @param fillMe A MARKER_PCB* which is to be filled in, or NULL if one is to be created.
*/
MARKER_PCB* fillMarker( const wxPoint& aPos, BOARD_ITEM* aItem, BOARD_ITEM* bItem,
int aErrorCode, MARKER_PCB* fillMe );
/**
* Fill a MARKER which will report on a generic problem with the board which is
* Create a MARKER which will report on a generic problem with the board which is
* not geographically locatable.
*/
MARKER_PCB* fillMarker( int aErrorCode, const wxString& aMessage, MARKER_PCB* fillMe );
MARKER_PCB* newMarker( int aErrorCode, const wxString& aMessage );
/**
* Adds a DRC marker to the PCB through the COMMIT mechanism.
@ -314,9 +305,11 @@ private:
void testKeepoutAreas();
// aTextItem is type BOARD_ITEM* to accept either TEXTE_PCB or TEXTE_MODULE
void doText( BOARD_ITEM* aTextItem );
void testCopperTextItem( BOARD_ITEM* aTextItem );
void testTexts();
void testCopperDrawItem( DRAWSEGMENT* aDrawing );
void testCopperTextAndGraphics();
///> Tests for items placed on disabled layers (causing false connections).
void testDisabledLayers();

View File

@ -204,21 +204,23 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
if( aRefSeg->Type() == PCB_VIA_T )
{
VIA *refvia = static_cast<VIA*>( aRefSeg );
wxPoint refviaPos = refvia->GetPosition();
// test if the via size is smaller than minimum
if( refvia->GetViaType() == VIA_MICROVIA )
{
if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_TOO_SMALL_MICROVIA, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA ) );
if( !handleNewMarker() )
return false;
}
if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_TOO_SMALL_MICROVIA_DRILL, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA_DRILL ) );
if( !handleNewMarker() )
return false;
}
@ -227,16 +229,16 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
{
if( refvia->GetWidth() < dsnSettings.m_ViasMinSize )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_TOO_SMALL_VIA, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_TOO_SMALL_VIA ) );
if( !handleNewMarker() )
return false;
}
if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_TOO_SMALL_VIA_DRILL, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_TOO_SMALL_VIA_DRILL ) );
if( !handleNewMarker() )
return false;
}
@ -247,28 +249,25 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
// and a default via hole can be bigger than some vias sizes
if( refvia->GetDrillValue() > refvia->GetWidth() )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_VIA_HOLE_BIGGER, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_VIA_HOLE_BIGGER ) );
if( !handleNewMarker() )
return false;
}
// test if the type of via is allowed due to design rules
if( ( refvia->GetViaType() == VIA_MICROVIA ) &&
( m_pcb->GetDesignSettings().m_MicroViasAllowed == false ) )
if( refvia->GetViaType() == VIA_MICROVIA && !dsnSettings.m_MicroViasAllowed )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_MICRO_VIA_NOT_ALLOWED, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_MICRO_VIA_NOT_ALLOWED ) );
if( !handleNewMarker() )
return false;
}
// test if the type of via is allowed due to design rules
if( ( refvia->GetViaType() == VIA_BLIND_BURIED ) &&
( m_pcb->GetDesignSettings().m_BlindBuriedViaAllowed == false ) )
if( refvia->GetViaType() == VIA_BLIND_BURIED && !dsnSettings.m_BlindBuriedViaAllowed )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_BURIED_VIA_NOT_ALLOWED, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_BURIED_VIA_NOT_ALLOWED ) );
if( !handleNewMarker() )
return false;
}
@ -286,15 +285,15 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
if( layer1 > layer2 )
std::swap( layer1, layer2 );
if( layer2 == B_Cu && layer1 == m_pcb->GetDesignSettings().GetCopperLayerCount() - 2 )
if( layer2 == B_Cu && layer1 == dsnSettings.GetCopperLayerCount() - 2 )
err = false;
else if( layer1 == F_Cu && layer2 == In1_Cu )
err = false;
if( err )
{
markers.push_back( fillMarker( refvia, nullptr,
DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR, nullptr ) );
markers.push_back( newMarker( refviaPos, refvia, DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR ) );
if( !handleNewMarker() )
return false;
}
@ -305,8 +304,10 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
{
if( aRefSeg->GetWidth() < dsnSettings.m_TrackMinWidth )
{
markers.push_back( fillMarker( aRefSeg, nullptr,
DRCE_TOO_SMALL_TRACK_WIDTH, nullptr ) );
wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
markers.push_back( newMarker( refsegMiddle, aRefSeg, DRCE_TOO_SMALL_TRACK_WIDTH ) );
if( !handleNewMarker() )
return false;
}
@ -353,6 +354,8 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
for( unsigned ii = 0; ii < pad_count; ++ii )
{
D_PAD* pad = pads[ii];
SEG padSeg( pad->GetPosition(), pad->GetPosition() );
/* No problem if pads are on another layer,
* But if a drill hole exists (a pad on a single layer can have a hole!)
@ -378,8 +381,9 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(),
netclass->GetClearance() ) )
{
markers.push_back( fillMarker( aRefSeg, pad,
DRCE_TRACK_NEAR_THROUGH_HOLE, nullptr ) );
markers.push_back( newMarker( aRefSeg, pad, padSeg,
DRCE_TRACK_NEAR_THROUGH_HOLE ) );
if( !handleNewMarker() )
return false;
}
@ -397,11 +401,10 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
shape_pos = pad->ShapePos();
m_padToTestPos = shape_pos - origin;
if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(),
aRefSeg->GetClearance( pad ) ) )
if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) )
{
markers.push_back( fillMarker( aRefSeg, pad,
DRCE_TRACK_NEAR_PAD, nullptr ) );
markers.push_back( newMarker( aRefSeg, pad, padSeg, DRCE_TRACK_NEAR_PAD ) );
if( !handleNewMarker() )
return false;
}
@ -445,14 +448,15 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
{
delta = track->GetEnd() - track->GetStart();
segStartPoint = aRefSeg->GetStart() - track->GetStart();
wxPoint pos = aRefSeg->GetPosition();
if( track->Type() == PCB_VIA_T )
{
// Test distance between two vias, i.e. two circles, trivial case
if( EuclideanNorm( segStartPoint ) < w_dist )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_VIA_NEAR_VIA, nullptr ) );
markers.push_back( newMarker( pos, aRefSeg, track, DRCE_VIA_NEAR_VIA ) );
if( !handleNewMarker() )
return false;
}
@ -468,8 +472,8 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
{
markers.push_back( fillMarker( track, aRefSeg,
DRCE_VIA_NEAR_TRACK, nullptr ) );
markers.push_back( newMarker( pos, aRefSeg, track, DRCE_VIA_NEAR_TRACK ) );
if( !handleNewMarker() )
return false;
}
@ -487,13 +491,15 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
RotatePoint( &segStartPoint, m_segmAngle );
RotatePoint( &segEndPoint, m_segmAngle );
SEG seg( segStartPoint, segEndPoint );
if( track->Type() == PCB_VIA_T )
{
if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
continue;
markers.push_back( fillMarker( aRefSeg, track,
DRCE_TRACK_NEAR_VIA, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_TRACK_NEAR_VIA ) );
if( !handleNewMarker() )
return false;
}
@ -520,16 +526,16 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
// Fine test : we consider the rounded shape of each end of the track segment:
if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS1, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS1 ) );
if( !handleNewMarker() )
return false;
}
if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS2, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS2 ) );
if( !handleNewMarker() )
return false;
}
@ -543,16 +549,16 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
// Fine test : we consider the rounded shape of the ends
if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS3, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS3 ) );
if( !handleNewMarker() )
return false;
}
if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_TRACK_ENDS4, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_TRACK_ENDS4 ) );
if( !handleNewMarker() )
return false;
}
@ -565,15 +571,15 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
// handled)
// X.............X
// O--REF--+
markers.push_back( fillMarker( aRefSeg, track,
DRCE_TRACK_SEGMENTS_TOO_CLOSE, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_TRACK_SEGMENTS_TOO_CLOSE ) );
if( !handleNewMarker() )
return false;
}
}
else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
{
if( ( segStartPoint.x <= ( -w_dist ) ) || ( segStartPoint.x >= ( m_segmLength + w_dist ) ) )
if( segStartPoint.x <= -w_dist || segStartPoint.x >= m_segmLength + w_dist )
continue;
// Test if segments are crossing
@ -582,8 +588,8 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
if( ( segStartPoint.y < 0 ) && ( segEndPoint.y > 0 ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_TRACKS_CROSSING, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_TRACKS_CROSSING ) );
if( !handleNewMarker() )
return false;
}
@ -591,15 +597,15 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
// At this point the drc error is due to an end near a reference segm end
if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM1, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM1 ) );
if( !handleNewMarker() )
return false;
}
if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM2, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM2 ) );
if( !handleNewMarker() )
return false;
}
@ -627,8 +633,8 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
if( !checkLine( segStartPoint, segEndPoint ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM3, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM3 ) );
if( !handleNewMarker() )
return false;
}
@ -656,16 +662,16 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM4, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM4 ) );
if( !handleNewMarker() )
return false;
}
if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
{
markers.push_back( fillMarker( aRefSeg, track,
DRCE_ENDS_PROBLEM5, nullptr ) );
markers.push_back( newMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM5 ) );
if( !handleNewMarker() )
return false;
}
@ -682,26 +688,20 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
for( ZONE_CONTAINER* zone : m_pcb->Zones() )
{
if( zone->GetIsKeepout()
|| zone->GetLayer() != aRefSeg->GetLayer()
|| ( aRefSeg->GetNetCode() == zone->GetNetCode() && aRefSeg->GetNetCode() > 0 ) )
{
if( zone->GetFilledPolysList().IsEmpty() || zone->GetIsKeepout() )
continue;
if( !( layerMask & zone->GetLayerSet() ).any() )
continue;
if( zone->GetNetCode() && zone->GetNetCode() == net_code_ref )
continue;
}
int clearance = zone->GetClearance( aRefSeg );
SHAPE_POLY_SET* outline;
if( zone->IsFilled() )
outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
else
outline = zone->Outline();
SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
if( outline->Distance( refSeg, aRefSeg->GetWidth() ) < clearance )
{
addMarkerToPcb( fillMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE, m_currentMarker ) );
m_currentMarker = nullptr;
}
addMarkerToPcb( newMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE ) );
}
if( markers.size() > 0 )
@ -766,10 +766,8 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
// test for ending line inside area_to_test
if( area_to_test->Outline()->Contains( end ) )
{
// COPPERAREA_COPPERAREA error: corner inside copper area
m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
COPPERAREA_INSIDE_COPPERAREA,
m_currentMarker );
wxPoint pos( end.x, end.y );
m_currentMarker = newMarker( pos, aArea, area_to_test, COPPERAREA_INSIDE_COPPERAREA );
return false;
}
@ -801,9 +799,8 @@ bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
if( d < zone_clearance )
{
// COPPERAREA_COPPERAREA error : edge intersect or too close
m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
COPPERAREA_CLOSE_TO_COPPERAREA,
m_currentMarker );
m_currentMarker = newMarker( wxPoint( x, y ), aArea, area_to_test,
COPPERAREA_CLOSE_TO_COPPERAREA );
return false;
}

View File

@ -118,21 +118,17 @@ wxString DRC_ITEM::GetErrorText() const
case DRCE_VIA_INSIDE_KEEPOUT:
return wxString( _( "Via inside keepout area" ) );
case DRCE_TRACK_INSIDE_KEEPOUT:
return wxString( _( "Track inside keepout area" ) );
case DRCE_PAD_INSIDE_KEEPOUT:
return wxString( _( "Pad inside keepout area" ) );
case DRCE_VIA_INSIDE_TEXT:
return wxString( _( "Via inside text" ) );
case DRCE_TRACK_INSIDE_TEXT:
return wxString( _( "Track inside text" ) );
case DRCE_PAD_INSIDE_TEXT:
return wxString( _( "Pad inside text" ) );
case DRCE_VIA_NEAR_COPPER:
return wxString( _( "Via too close to copper item" ) );
case DRCE_TRACK_NEAR_COPPER:
return wxString( _( "Track too close to copper item" ) );
case DRCE_PAD_NEAR_COPPER:
return wxString( _( "Pad too close to copper item" ) );
case DRCE_OVERLAPPING_FOOTPRINTS:
return wxString( _( "Courtyards overlap" ) );

View File

@ -36,6 +36,7 @@
#include <common.h>
#include <pcbnew.h>
#include <board_design_settings.h>
#include <geometry/geometry_utils.h>
#include <pcb_edit_frame.h>
#include <drc.h>
#include <class_pad.h>
@ -48,161 +49,96 @@
#include <class_board_item.h>
MARKER_PCB* DRC::fillMarker( TRACK* aTrack, BOARD_ITEM* bItem, int aErrorCode, MARKER_PCB* fillMe )
{
EDA_UNITS_T units = m_pcbEditorFrame->GetUserUnits();
const int EPSILON = Mils2iu( 5 );
wxPoint posA;
wxPoint posB = wxPoint();
if( bItem ) // aItem might be NULL
MARKER_PCB* DRC::newMarker( TRACK* aTrack, ZONE_CONTAINER* aConflictZone, int aErrorCode )
{
if( bItem->Type() == PCB_PAD_T )
{
posB = posA = ((D_PAD*)bItem)->GetPosition();
}
else if( bItem->Type() == PCB_VIA_T )
{
posB = posA = ((VIA*)bItem)->GetPosition();
}
else if( bItem->Type() == PCB_TRACE_T )
{
TRACK* bTrack = (TRACK*) bItem;
SEG bTrackSeg( bTrack->GetPosition(), bTrack->GetEnd() );
wxPoint pt1 = aTrack->GetPosition();
wxPoint pt2 = aTrack->GetEnd();
// Do a binary search for a "good enough" marker location
while( GetLineLength( pt1, pt2 ) > EPSILON )
{
if( bTrackSeg.Distance( pt1 ) < bTrackSeg.Distance( pt2 ) )
pt2 = ( pt1 + pt2 ) / 2;
else
pt1 = ( pt1 + pt2 ) / 2;
}
// Once we're within EPSILON pt1 and pt2 are "equivalent"
posA = pt1;
posB = bTrack->GetPosition();
}
else if( bItem->Type() == PCB_ZONE_T || bItem->Type() == PCB_ZONE_AREA_T )
{
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( bItem );
SHAPE_POLY_SET* outline;
if( zone->IsFilled() )
outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
else
outline = zone->Outline();
auto conflictOutline = const_cast<SHAPE_POLY_SET*>( &aConflictZone->GetFilledPolysList() );
wxPoint markerPos;
wxPoint pt1 = aTrack->GetPosition();
wxPoint pt2 = aTrack->GetEnd();
// If the mid-point is in the zone, then that's a fine place for the marker
if( outline->Distance( ( pt1 + pt2 ) / 2 ) == 0 )
posA = ( pt1 + pt2 ) / 2;
if( conflictOutline->Distance( ( pt1 + pt2 ) / 2 ) == 0 )
markerPos = ( pt1 + pt2 ) / 2;
// Otherwise do a binary search for a "good enough" marker location
else
{
while( GetLineLength( pt1, pt2 ) > EPSILON )
{
if( outline->Distance( pt1 ) < outline->Distance( pt2 ) )
if( conflictOutline->Distance( pt1 ) < conflictOutline->Distance( pt2 ) )
pt2 = ( pt1 + pt2 ) / 2;
else
pt1 = ( pt1 + pt2 ) / 2;
}
// Once we're within EPSILON pt1 and pt2 are "equivalent"
posA = pt1;
markerPos = pt1;
}
posB = ((ZONE_CONTAINER*)bItem)->GetPosition();
}
return new MARKER_PCB( m_pcbEditorFrame->GetUserUnits(), aErrorCode, markerPos,
aTrack, aTrack->GetPosition(),
aConflictZone, aConflictZone->GetPosition() );
}
MARKER_PCB* DRC::newMarker( TRACK* aTrack, BOARD_ITEM* aConflitItem, const SEG& aConflictSeg,
int aErrorCode )
{
wxPoint markerPos;
wxPoint pt1 = aTrack->GetPosition();
wxPoint pt2 = aTrack->GetEnd();
// Do a binary search along the track for a "good enough" marker location
while( GetLineLength( pt1, pt2 ) > EPSILON )
{
if( aConflictSeg.Distance( pt1 ) < aConflictSeg.Distance( pt2 ) )
pt2 = ( pt1 + pt2 ) / 2;
else
posA = aTrack->GetPosition();
pt1 = ( pt1 + pt2 ) / 2;
}
if( fillMe )
fillMe->SetData( units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB );
else
fillMe = new MARKER_PCB( units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB );
// Once we're within EPSILON pt1 and pt2 are "equivalent"
markerPos = pt1;
return fillMe;
return new MARKER_PCB( m_pcbEditorFrame->GetUserUnits(), aErrorCode, markerPos,
aTrack, aTrack->GetPosition(),
aConflitItem, aConflitItem->GetPosition() );
}
MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe )
MARKER_PCB* DRC::newMarker( D_PAD* aPad, BOARD_ITEM* aConflictItem, int aErrorCode )
{
EDA_UNITS_T units = m_pcbEditorFrame->GetUserUnits();
return new MARKER_PCB( m_pcbEditorFrame->GetUserUnits(), aErrorCode, aPad->GetPosition(),
aPad, aPad->GetPosition(),
aConflictItem, aConflictItem->GetPosition() );
}
wxPoint posA = aPad->GetPosition();
wxPoint posB;
if( aItem )
MARKER_PCB* DRC::newMarker(const wxPoint &aPos, BOARD_ITEM *aItem, int aErrorCode )
{
switch( aItem->Type() )
return new MARKER_PCB( m_pcbEditorFrame->GetUserUnits(), aErrorCode, aPos,
aItem, aItem->GetPosition(), nullptr, wxPoint() );
}
MARKER_PCB* DRC::newMarker( const wxPoint &aPos, BOARD_ITEM* aItem, BOARD_ITEM* bItem,
int aErrorCode )
{
case PCB_PAD_T:
posB = ((D_PAD*)aItem)->GetPosition();
break;
case PCB_TEXT_T:
posB = ((TEXTE_PCB*)aItem)->GetPosition();
break;
case PCB_MODULE_TEXT_T:
posB = ((TEXTE_MODULE*)aItem)->GetPosition();
break;
default:
wxLogDebug( wxT("fillMarker: unsupported item") );
break;
}
}
if( fillMe )
fillMe->SetData( units, aErrorCode, posA, aPad, posA, aItem, posB );
else
fillMe = new MARKER_PCB( units, aErrorCode, posA, aPad, posA, aItem, posB );
return fillMe;
return new MARKER_PCB( m_pcbEditorFrame->GetUserUnits(), aErrorCode, aPos,
aItem, aItem->GetPosition(), bItem, bItem->GetPosition() );
}
MARKER_PCB* DRC::fillMarker(BOARD_ITEM *aItem, const wxPoint &aPos, int aErrorCode,
MARKER_PCB *fillMe)
MARKER_PCB* DRC::newMarker( int aErrorCode, const wxString& aMessage )
{
return fillMarker(aPos, aItem, nullptr, aErrorCode, fillMe );
}
MARKER_PCB* DRC::fillMarker( const wxPoint& aPos, BOARD_ITEM* aItem, BOARD_ITEM* bItem,
int aErrorCode, MARKER_PCB* fillMe )
{
EDA_UNITS_T units = m_pcbEditorFrame->GetUserUnits();
if( fillMe )
fillMe->SetData( units, aErrorCode, aPos, aItem, aPos, bItem, aPos );
else
fillMe = new MARKER_PCB( units, aErrorCode, aPos, aItem, aPos, bItem, aPos );
return fillMe;
}
MARKER_PCB* DRC::fillMarker( int aErrorCode, const wxString& aMessage, MARKER_PCB* fillMe )
{
wxPoint posA; // not displayed
if( fillMe )
fillMe->SetData( aErrorCode, posA, aMessage, posA );
else
fillMe = new MARKER_PCB( aErrorCode, posA, aMessage, posA );
fillMe->SetShowNoCoordinate();
return fillMe;
MARKER_PCB* marker = new MARKER_PCB( aErrorCode, wxPoint(), aMessage, wxPoint() );
marker->SetShowNoCoordinate();
return marker;
}