Fix DRC & zone filling for copper text in footprints.

Also improves handling of copper edges in footprints and of text
and track locations in DRC markers.

Also adds DRC for tracks & zones.

Fixes: lp:1762474
* https://bugs.launchpad.net/kicad/+bug/1762474

Fixes: lp:1788268
* https://bugs.launchpad.net/kicad/+bug/1788268
This commit is contained in:
Jeff Young 2018-08-23 23:41:57 +01:00
parent 8aa0886ce7
commit 0e3919e7aa
11 changed files with 344 additions and 243 deletions

View File

@ -36,6 +36,19 @@
#include <base_struct.h> // EDA_RECT #include <base_struct.h> // EDA_RECT
class SHAPE_POLY_SET;
// A mutex which is unique to each instance it appears in (ie: a new std::mutex is allocated
// on copy or assignment).
class UNIQUE_MUTEX : public std::mutex
{
public:
UNIQUE_MUTEX() : std::mutex() {}
UNIQUE_MUTEX( const UNIQUE_MUTEX& ) : std::mutex() {}
UNIQUE_MUTEX& operator= (const UNIQUE_MUTEX& ) { return *this; }
};
// part of the kicad_plugin.h family of defines. // part of the kicad_plugin.h family of defines.
// See kicad_plugin.h for the choice of the value // See kicad_plugin.h for the choice of the value
// When set when calling EDA_TEXT::Format, disable writing the "hide" keyword in save file // When set when calling EDA_TEXT::Format, disable writing the "hide" keyword in save file
@ -251,6 +264,20 @@ public:
*/ */
void TransformTextShapeToSegmentList( std::vector<wxPoint>& aCornerBuffer ) const; void TransformTextShapeToSegmentList( std::vector<wxPoint>& aCornerBuffer ) const;
/**
* Function TransformBoundingBoxWithClearanceToPolygon
* Convert the text bounding box to a rectangular polygon
* depending on the text orientation, the bounding box
* is not always horizontal or vertical
* Used in filling zones calculations
* Circles and arcs are approximated by segments
* @param aCornerBuffer = a buffer to store the polygon
* @param aClearanceValue = the clearance around the text bounding box
* to the real clearance value (usually near from 1.0)
*/
void TransformBoundingBoxWithClearanceToPolygon( SHAPE_POLY_SET* aCornerBuffer,
int aClearanceValue ) const;
/** /**
* Function TextHitTest * Function TextHitTest
* Test if \a aPoint is within the bounds of this object. * Test if \a aPoint is within the bounds of this object.
@ -343,6 +370,9 @@ public:
protected: protected:
wxString m_Text; wxString m_Text;
// wxString isn't thread-safe, so make use of this in multi-threaded situations
mutable UNIQUE_MUTEX m_mutex;
private: private:
/** /**
* Function drawOneLineOfText * Function drawOneLineOfText

View File

@ -407,8 +407,8 @@ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet(
* @param aCornerBuffer = a buffer to store the polygon * @param aCornerBuffer = a buffer to store the polygon
* @param aClearanceValue = the clearance around the text bounding box * @param aClearanceValue = the clearance around the text bounding box
*/ */
void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon( void EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon(
SHAPE_POLY_SET& aCornerBuffer, SHAPE_POLY_SET* aCornerBuffer,
int aClearanceValue ) const int aClearanceValue ) const
{ {
// Oh dear. When in UTF-8 mode, wxString puts string iterators in a linked list, and // Oh dear. When in UTF-8 mode, wxString puts string iterators in a linked list, and
@ -431,13 +431,13 @@ void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon(
corners[3].y = corners[2].y; corners[3].y = corners[2].y;
corners[3].x = corners[0].x; corners[3].x = corners[0].x;
aCornerBuffer.NewOutline(); aCornerBuffer->NewOutline();
for( int ii = 0; ii < 4; ii++ ) for( int ii = 0; ii < 4; ii++ )
{ {
// Rotate polygon // Rotate polygon
RotatePoint( &corners[ii].x, &corners[ii].y, GetTextPos().x, GetTextPos().y, GetTextAngle() ); RotatePoint( &corners[ii].x, &corners[ii].y, GetTextPos().x, GetTextPos().y, GetTextAngle() );
aCornerBuffer.Append( corners[ii].x, corners[ii].y ); aCornerBuffer->Append( corners[ii].x, corners[ii].y );
} }
} }

View File

@ -1042,7 +1042,7 @@ void BOARD::DeleteZONEOutlines()
} }
BOARD_ITEM* BOARD::GetItem( void* aWeakReference, bool includeDrawings ) BOARD_ITEM* BOARD::GetItem( void* aWeakReference )
{ {
for( TRACK* track : Tracks() ) for( TRACK* track : Tracks() )
if( track == aWeakReference ) if( track == aWeakReference )
@ -1057,24 +1057,24 @@ BOARD_ITEM* BOARD::GetItem( void* aWeakReference, bool includeDrawings )
if( pad == aWeakReference ) if( pad == aWeakReference )
return pad; return pad;
if( includeDrawings ) if( &module->Reference() == aWeakReference )
{ return &module->Reference();
for( BOARD_ITEM* drawing : module->GraphicalItems() )
if( drawing == aWeakReference ) if( &module->Value() == aWeakReference )
return drawing; return &module->Value();
}
for( BOARD_ITEM* drawing : module->GraphicalItems() )
if( drawing == aWeakReference )
return drawing;
} }
for( ZONE_CONTAINER* zone : Zones() ) for( ZONE_CONTAINER* zone : Zones() )
if( zone == aWeakReference ) if( zone == aWeakReference )
return zone; return zone;
if( includeDrawings ) for( BOARD_ITEM* drawing : Drawings() )
{ if( drawing == aWeakReference )
for( BOARD_ITEM* drawing : Drawings() ) return drawing;
if( drawing == aWeakReference )
return drawing;
}
// Not found; weak reference has been deleted. // Not found; weak reference has been deleted.
return &g_DeletedItem; return &g_DeletedItem;

View File

@ -283,7 +283,7 @@ public:
void Remove( BOARD_ITEM* aBoardItem ) override; void Remove( BOARD_ITEM* aBoardItem ) override;
BOARD_ITEM* GetItem( void* aWeakReference, bool includeDrawings = true ); BOARD_ITEM* GetItem( void* aWeakReference );
BOARD_ITEM* Duplicate( const BOARD_ITEM* aItem, bool aAddToBoard = false ); BOARD_ITEM* Duplicate( const BOARD_ITEM* aItem, bool aAddToBoard = false );

View File

@ -41,17 +41,6 @@ class EDA_DRAW_PANEL;
class MSG_PANEL_ITEM; class MSG_PANEL_ITEM;
// A mutex which is unique to each instance it appears in (ie: a new std::mutex is allocated
// on copy or assignment).
class UNIQUE_MUTEX : public std::mutex
{
public:
UNIQUE_MUTEX() : std::mutex() {}
UNIQUE_MUTEX( const UNIQUE_MUTEX& ) : std::mutex() {}
UNIQUE_MUTEX& operator= (const UNIQUE_MUTEX& ) { return *this; }
};
class TEXTE_PCB : public BOARD_ITEM, public EDA_TEXT class TEXTE_PCB : public BOARD_ITEM, public EDA_TEXT
{ {
public: public:
@ -111,21 +100,6 @@ public:
return wxT( "PTEXT" ); return wxT( "PTEXT" );
} }
/**
* Function TransformBoundingBoxWithClearanceToPolygon
* Convert the text bounding box to a rectangular polygon
* depending on the text orientation, the bounding box
* is not always horizontal or vertical
* Used in filling zones calculations
* Circles and arcs are approximated by segments
* @param aCornerBuffer = a buffer to store the polygon
* @param aClearanceValue = the clearance around the text bounding box
* to the real clearance value (usually near from 1.0)
*/
void TransformBoundingBoxWithClearanceToPolygon(
SHAPE_POLY_SET& aCornerBuffer,
int aClearanceValue ) const;
/** /**
* Function TransformShapeWithClearanceToPolygonSet * Function TransformShapeWithClearanceToPolygonSet
* Convert the text shape to a set of polygons (one by segment) * Convert the text shape to a set of polygons (one by segment)
@ -154,8 +128,6 @@ public:
virtual void SwapData( BOARD_ITEM* aImage ) override; virtual void SwapData( BOARD_ITEM* aImage ) override;
mutable UNIQUE_MUTEX m_mutex;
#if defined(DEBUG) #if defined(DEBUG)
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif #endif

View File

@ -131,7 +131,6 @@ DRC::DRC( PCB_EDIT_FRAME* aPcbWindow )
m_pcbEditorFrame = aPcbWindow; m_pcbEditorFrame = aPcbWindow;
m_pcb = aPcbWindow->GetBoard(); m_pcb = aPcbWindow->GetBoard();
m_drcDialog = NULL; m_drcDialog = NULL;
m_units = aPcbWindow->GetUserUnits();
// establish initial values for everything: // establish initial values for everything:
m_drcInLegacyRoutingMode = false; m_drcInLegacyRoutingMode = false;
@ -430,15 +429,6 @@ void DRC::RunTests( wxTextCtrl* aMessages )
testDrilledHoles(); testDrilledHoles();
// test track and via clearances to other tracks, pads, and vias
if( aMessages )
{
aMessages->AppendText( _( "Track clearances...\n" ) );
wxSafeYield();
}
testTracks( aMessages ? aMessages->GetParent() : m_pcbEditorFrame, true );
// caller (a wxTopLevelFrame) is the wxDialog or the Pcb Editor frame that call DRC: // caller (a wxTopLevelFrame) is the wxDialog or the Pcb Editor frame that call DRC:
wxWindow* caller = aMessages ? aMessages->GetParent() : m_pcbEditorFrame; wxWindow* caller = aMessages ? aMessages->GetParent() : m_pcbEditorFrame;
@ -457,6 +447,15 @@ void DRC::RunTests( wxTextCtrl* aMessages )
m_pcbEditorFrame->Check_All_Zones( caller ); m_pcbEditorFrame->Check_All_Zones( caller );
} }
// test track and via clearances to other tracks, pads, and vias
if( aMessages )
{
aMessages->AppendText( _( "Track clearances...\n" ) );
wxSafeYield();
}
testTracks( aMessages ? aMessages->GetParent() : m_pcbEditorFrame, true );
// test zone clearances to other zones // test zone clearances to other zones
if( aMessages ) if( aMessages )
{ {
@ -942,7 +941,7 @@ void DRC::testKeepoutAreas()
if( area->Outline()->Distance( SEG( segm->GetStart(), segm->GetEnd() ), if( area->Outline()->Distance( SEG( segm->GetStart(), segm->GetEnd() ),
segm->GetWidth() ) == 0 ) segm->GetWidth() ) == 0 )
{ {
addMarkerToPcb( fillMarker( segm, NULL, addMarkerToPcb( fillMarker( segm, area,
DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker ) ); DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker ) );
m_currentMarker = nullptr; m_currentMarker = nullptr;
} }
@ -972,124 +971,144 @@ void DRC::testKeepoutAreas()
void DRC::testTexts() void DRC::testTexts()
{ {
// Test text items for vias, tracks and pads inside text areas
for( BOARD_ITEM* brdItem : m_pcb->Drawings() )
{
if( brdItem->Type() == PCB_TEXT_T && IsCopperLayer( brdItem->GetLayer() ) )
doText( brdItem );
}
for( MODULE* module : m_pcb->Modules() )
{
if( IsCopperLayer( module->Reference().GetLayer() ) )
doText( &module->Reference() );
if( IsCopperLayer( module->Value().GetLayer() ) )
doText( &module->Value() );
for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() )
{
if( item->Type() == PCB_MODULE_TEXT_T && IsCopperLayer( item->GetLayer() ) )
doText( item );
}
}
}
void DRC::doText( 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<wxPoint> textShape; // a buffer to store the text shape (set of segments)
std::vector<D_PAD*> padList = m_pcb->GetPads(); std::vector<D_PAD*> padList = m_pcb->GetPads();
// Test text areas for vias, tracks and pads inside text areas // So far the bounding box makes up the text-area
for( auto item : m_pcb->Drawings() ) text->TransformTextShapeToSegmentList( textShape );
if( textShape.size() == 0 ) // Should not happen (empty text?)
return;
for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() )
{ {
// Drc test only items on copper layers if( !track->IsOnLayer( aTextItem->GetLayer() ) )
if( !IsCopperLayer( item->GetLayer() ) )
continue; continue;
// only texts on copper layers are tested // Test the distance between each segment and the current track/via
if( item->Type() != PCB_TEXT_T ) int min_dist = ( track->GetWidth() + text->GetThickness() ) / 2 +
continue; track->GetClearance( NULL );
textShape.clear(); if( track->Type() == PCB_TRACE_T )
// So far the bounding box makes up the text-area
TEXTE_PCB* text = (TEXTE_PCB*) item;
text->TransformTextShapeToSegmentList( textShape );
if( textShape.size() == 0 ) // Should not happen (empty text?)
continue;
for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() )
{ {
if( !track->IsOnLayer( item->GetLayer() ) ) SEG segref( track->GetStart(), track->GetEnd() );
continue; wxPoint markerPos;
int closestApproach = INT_MAX;
// Test the distance between each segment and the current track/via // Error condition: Distance between text segment and track segment is
int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 + // smaller than the clearance of the track
track->GetClearance(NULL); for( unsigned jj = 0; jj < textShape.size() && closestApproach > 0; jj += 2 )
if( track->Type() == PCB_TRACE_T )
{ {
SEG segref( track->GetStart(), track->GetEnd() ); SEG segtest( textShape[jj], textShape[jj+1] );
int dist = segref.Distance( segtest );
// Error condition: Distance between text segment and track segment is if( dist < closestApproach )
// smaller than the clearance of the segment
for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
{ {
SEG segtest( textShape[jj], textShape[jj+1] ); markerPos = textShape[jj];
int dist = segref.Distance( segtest ); closestApproach = dist;
if( dist < min_dist )
{
addMarkerToPcb( fillMarker( track, text,
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] );
if( segtest.PointCloserThan( track->GetPosition(), min_dist ) ) if( closestApproach < min_dist )
{ {
addMarkerToPcb( fillMarker( track, text, addMarkerToPcb( fillMarker( markerPos, track, aTextItem,
DRCE_VIA_INSIDE_TEXT, m_currentMarker ) ); DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) );
m_currentMarker = nullptr; m_currentMarker = nullptr;
break; break;
}
}
} }
} }
else if( track->Type() == PCB_VIA_T )
// Test pads
for( unsigned ii = 0; ii < padList.size(); ii++ )
{ {
D_PAD* pad = padList[ii]; // Error condition: Distance between text segment and via is
// smaller than the clearance of the via
if( !pad->IsOnLayer( item->GetLayer() ) )
continue;
wxPoint shape_pos = pad->ShapePos();
for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
{ {
/* In order to make some calculations more easier or faster, SEG segtest( textShape[jj], textShape[jj+1] );
* 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;
// for a non horizontal or vertical segment Compute the segment angle if( segtest.PointCloserThan( track->GetPosition(), min_dist ) )
// in tenths of degrees and its length
if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias
{ {
// Compute the segment angle in 0,1 degrees addMarkerToPcb( fillMarker( track->GetPosition(), track, aTextItem,
m_segmAngle = ArcTangente( delta.y, delta.x ); DRCE_VIA_INSIDE_TEXT, m_currentMarker ) );
// 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, text,
DRCE_PAD_INSIDE_TEXT, m_currentMarker ) );
m_currentMarker = nullptr; m_currentMarker = nullptr;
break; break;
} }
} }
} }
} }
// Test pads
for( unsigned ii = 0; ii < padList.size(); ii++ )
{
D_PAD* pad = padList[ii];
if( !pad->IsOnLayer( aTextItem->GetLayer() ) )
continue;
wxPoint shape_pos = pad->ShapePos();
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;
// 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
{
// 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;
break;
}
}
}
} }

View File

@ -92,6 +92,7 @@
#define DRCE_BURIED_VIA_NOT_ALLOWED 48 ///< buried 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_DISABLED_LAYER_ITEM 49 ///< item on a disabled layer
#define DRCE_DRILLED_HOLES_TOO_CLOSE 50 ///< overlapping drilled holes break drill bits #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
class EDA_DRAW_PANEL; class EDA_DRAW_PANEL;
@ -105,6 +106,7 @@ class TRACK;
class MARKER_PCB; class MARKER_PCB;
class DRC_ITEM; class DRC_ITEM;
class NETCLASS; class NETCLASS;
class EDA_TEXT;
/** /**
@ -214,7 +216,6 @@ private:
PCB_EDIT_FRAME* m_pcbEditorFrame; ///< The pcb frame editor which owns the board PCB_EDIT_FRAME* m_pcbEditorFrame; ///< The pcb frame editor which owns the board
BOARD* m_pcb; BOARD* m_pcb;
DIALOG_DRC_CONTROL* m_drcDialog; DIALOG_DRC_CONTROL* m_drcDialog;
EDA_UNITS_T m_units;
DRC_LIST m_unconnected; ///< list of unconnected pads, as DRC_ITEMs DRC_LIST m_unconnected; ///< list of unconnected pads, as DRC_ITEMs
@ -312,6 +313,9 @@ private:
void testKeepoutAreas(); void testKeepoutAreas();
// aTextItem is type BOARD_ITEM* to accept either TEXTE_PCB or TEXTE_MODULE
void doText( BOARD_ITEM* aTextItem );
void testTexts(); void testTexts();
///> Tests for items placed on disabled layers (causing false connections). ///> Tests for items placed on disabled layers (causing false connections).

View File

@ -197,7 +197,10 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
layerMask = aRefSeg->GetLayerSet(); layerMask = aRefSeg->GetLayerSet();
net_code_ref = aRefSeg->GetNetCode(); net_code_ref = aRefSeg->GetNetCode();
// Phase 0 : Test vias /******************************************/
/* Phase 0 : via DRC tests : */
/******************************************/
if( aRefSeg->Type() == PCB_VIA_T ) if( aRefSeg->Type() == PCB_VIA_T )
{ {
VIA *refvia = static_cast<VIA*>( aRefSeg ); VIA *refvia = static_cast<VIA*>( aRefSeg );
@ -671,6 +674,36 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
} }
} }
/***********************************************/
/* Phase 3: test DRC with zones */
/***********************************************/
SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
for( ZONE_CONTAINER* zone : m_pcb->Zones() )
{
if( zone->GetIsKeepout()
|| zone->GetLayer() != aRefSeg->GetLayer()
|| ( aRefSeg->GetNetCode() == zone->GetNetCode() && aRefSeg->GetNetCode() > 0 ) )
{
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();
if( outline->Distance( refSeg, aRefSeg->GetWidth() ) < clearance )
{
addMarkerToPcb( fillMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE, m_currentMarker ) );
m_currentMarker = nullptr;
}
}
if( markers.size() > 0 ) if( markers.size() > 0 )
{ {
commitMarkers(); commitMarkers();

View File

@ -40,15 +40,15 @@ wxString DRC_ITEM::GetErrorText() const
case DRCE_UNCONNECTED_ITEMS: case DRCE_UNCONNECTED_ITEMS:
return wxString( _( "Unconnected items" ) ); return wxString( _( "Unconnected items" ) );
case DRCE_TRACK_NEAR_THROUGH_HOLE: case DRCE_TRACK_NEAR_THROUGH_HOLE:
return wxString( _( "Track near thru-hole" ) ); return wxString( _( "Track too close to thru-hole" ) );
case DRCE_TRACK_NEAR_PAD: case DRCE_TRACK_NEAR_PAD:
return wxString( _( "Track near pad" ) ); return wxString( _( "Track too close to pad" ) );
case DRCE_TRACK_NEAR_VIA: case DRCE_TRACK_NEAR_VIA:
return wxString( _( "Track near via" ) ); return wxString( _( "Track too close to via" ) );
case DRCE_VIA_NEAR_VIA: case DRCE_VIA_NEAR_VIA:
return wxString( _( "Via near via" ) ); return wxString( _( "Via too close to via" ) );
case DRCE_VIA_NEAR_TRACK: case DRCE_VIA_NEAR_TRACK:
return wxString( _( "Via near track" ) ); return wxString( _( "Via too close to track" ) );
case DRCE_TRACK_ENDS1: case DRCE_TRACK_ENDS1:
case DRCE_TRACK_ENDS2: case DRCE_TRACK_ENDS2:
case DRCE_TRACK_ENDS3: case DRCE_TRACK_ENDS3:
@ -63,8 +63,10 @@ wxString DRC_ITEM::GetErrorText() const
return wxString( _( "Two parallel track segments too close" ) ); return wxString( _( "Two parallel track segments too close" ) );
case DRCE_TRACKS_CROSSING: case DRCE_TRACKS_CROSSING:
return wxString( _( "Tracks crossing" ) ); return wxString( _( "Tracks crossing" ) );
case DRCE_TRACK_NEAR_ZONE:
return wxString( _( "Track too close to copper area" ) );
case DRCE_PAD_NEAR_PAD1: case DRCE_PAD_NEAR_PAD1:
return wxString( _( "Pad near pad" ) ); return wxString( _( "Pad too close to pad" ) );
case DRCE_VIA_HOLE_BIGGER: case DRCE_VIA_HOLE_BIGGER:
return wxString( _( "Via hole > diameter" ) ); return wxString( _( "Via hole > diameter" ) );
case DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR: case DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR:
@ -81,22 +83,22 @@ wxString DRC_ITEM::GetErrorText() const
return wxString( _( "Copper areas intersect or are too close" ) ); return wxString( _( "Copper areas intersect or are too close" ) );
case DRCE_SUSPICIOUS_NET_FOR_ZONE_OUTLINE: case DRCE_SUSPICIOUS_NET_FOR_ZONE_OUTLINE:
return wxString( _( "Copper area belongs a net which has no pads. This is strange" ) ); return wxString( _( "Copper area belongs to a net which has no pads" ) );
case DRCE_HOLE_NEAR_PAD: case DRCE_HOLE_NEAR_PAD:
return wxString( _( "Hole near pad" ) ); return wxString( _( "Hole too close to pad" ) );
case DRCE_HOLE_NEAR_TRACK: case DRCE_HOLE_NEAR_TRACK:
return wxString( _( "Hole near track" ) ); return wxString( _( "Hole too close to track" ) );
case DRCE_TOO_SMALL_TRACK_WIDTH: case DRCE_TOO_SMALL_TRACK_WIDTH:
return wxString( _( "Too small track width" ) ); return wxString( _( "Track width too small" ) );
case DRCE_TOO_SMALL_VIA: case DRCE_TOO_SMALL_VIA:
return wxString( _( "Too small via size" ) ); return wxString( _( "Via size too small" ) );
case DRCE_TOO_SMALL_MICROVIA: case DRCE_TOO_SMALL_MICROVIA:
return wxString( _( "Too small micro via size" ) ); return wxString( _( "Micro via size too small" ) );
case DRCE_TOO_SMALL_VIA_DRILL: case DRCE_TOO_SMALL_VIA_DRILL:
return wxString( _( "Too small via drill" ) ); return wxString( _( "Via drill too small" ) );
case DRCE_TOO_SMALL_MICROVIA_DRILL: case DRCE_TOO_SMALL_MICROVIA_DRILL:
return wxString( _( "Too small micro via drill" ) ); return wxString( _( "Micro via drill too small" ) );
case DRCE_DRILLED_HOLES_TOO_CLOSE: case DRCE_DRILLED_HOLES_TOO_CLOSE:
return wxString( _( "Drilled holes too close together" ) ); return wxString( _( "Drilled holes too close together" ) );
@ -115,22 +117,22 @@ wxString DRC_ITEM::GetErrorText() const
return wxString( _( "NetClass uVia Drill &lt; global limit" ) ); return wxString( _( "NetClass uVia Drill &lt; global limit" ) );
case DRCE_VIA_INSIDE_KEEPOUT: case DRCE_VIA_INSIDE_KEEPOUT:
return wxString( _( "Via inside a keepout area" ) ); return wxString( _( "Via inside keepout area" ) );
case DRCE_TRACK_INSIDE_KEEPOUT: case DRCE_TRACK_INSIDE_KEEPOUT:
return wxString( _( "Track inside a keepout area" ) ); return wxString( _( "Track inside keepout area" ) );
case DRCE_PAD_INSIDE_KEEPOUT: case DRCE_PAD_INSIDE_KEEPOUT:
return wxString( _( "Pad inside a keepout area" ) ); return wxString( _( "Pad inside keepout area" ) );
case DRCE_VIA_INSIDE_TEXT: case DRCE_VIA_INSIDE_TEXT:
return wxString( _( "Via inside a text" ) ); return wxString( _( "Via inside text" ) );
case DRCE_TRACK_INSIDE_TEXT: case DRCE_TRACK_INSIDE_TEXT:
return wxString( _( "Track inside a text" ) ); return wxString( _( "Track inside text" ) );
case DRCE_PAD_INSIDE_TEXT: case DRCE_PAD_INSIDE_TEXT:
return wxString( _( "Pad inside a text" ) ); return wxString( _( "Pad inside text" ) );
case DRCE_OVERLAPPING_FOOTPRINTS: case DRCE_OVERLAPPING_FOOTPRINTS:
return wxString( _( "Courtyards overlap" ) ); return wxString( _( "Courtyards overlap" ) );
@ -225,12 +227,12 @@ wxString DRC_ITEM::ShowReport( EDA_UNITS_T aUnits ) const
BOARD_ITEM* DRC_ITEM::GetMainItem( BOARD* aBoard ) const BOARD_ITEM* DRC_ITEM::GetMainItem( BOARD* aBoard ) const
{ {
return aBoard->GetItem( m_mainItemWeakRef, false /* copper only */ ); return aBoard->GetItem( m_mainItemWeakRef );
} }
BOARD_ITEM* DRC_ITEM::GetAuxiliaryItem( BOARD* aBoard ) const BOARD_ITEM* DRC_ITEM::GetAuxiliaryItem( BOARD* aBoard ) const
{ {
return aBoard->GetItem( m_auxItemWeakRef, false /* copper only */ ); return aBoard->GetItem( m_auxItemWeakRef );
} }

View File

@ -36,18 +36,23 @@
#include <common.h> #include <common.h>
#include <pcbnew.h> #include <pcbnew.h>
#include <board_design_settings.h> #include <board_design_settings.h>
#include <pcb_edit_frame.h>
#include <drc.h> #include <drc.h>
#include <class_pad.h> #include <class_pad.h>
#include <class_track.h> #include <class_track.h>
#include <class_zone.h> #include <class_zone.h>
#include <class_marker_pcb.h> #include <class_marker_pcb.h>
#include <class_pcb_text.h> #include <class_pcb_text.h>
#include <class_text_mod.h>
#include <class_edge_mod.h>
#include <class_board_item.h> #include <class_board_item.h>
MARKER_PCB* DRC::fillMarker( TRACK* aTrack, BOARD_ITEM* bItem, int aErrorCode, MARKER_PCB* fillMe ) 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 posA;
wxPoint posB = wxPoint(); wxPoint posB = wxPoint();
@ -63,36 +68,65 @@ MARKER_PCB* DRC::fillMarker( TRACK* aTrack, BOARD_ITEM* bItem, int aErrorCode, M
} }
else if( bItem->Type() == PCB_TRACE_T ) else if( bItem->Type() == PCB_TRACE_T )
{ {
TRACK* track = (TRACK*) bItem; TRACK* bTrack = (TRACK*) bItem;
SEG bTrackSeg( bTrack->GetPosition(), bTrack->GetEnd() );
wxPoint pt1 = aTrack->GetPosition();
wxPoint pt2 = aTrack->GetEnd();
posB = track->GetPosition(); // 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;
}
wxPoint endPos = track->GetEnd(); // Once we're within EPSILON pt1 and pt2 are "equivalent"
posA = pt1;
// either of aItem's start or end will be used for the marker position posB = bTrack->GetPosition();
// first assume start, then switch at end if needed. decision made on
// distance from end of aTrack.
posA = track->GetStart();
double dToEnd = GetLineLength( endPos, aTrack->GetEnd() );
double dToStart = GetLineLength( posA, aTrack->GetEnd() );
if( dToEnd < dToStart )
posA = endPos;
} }
else if( bItem->Type() == PCB_TEXT_T ) else if( bItem->Type() == PCB_ZONE_T || bItem->Type() == PCB_ZONE_AREA_T )
{ {
posA = aTrack->GetPosition(); ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( bItem );
posB = ((TEXTE_PCB*) bItem)->GetPosition(); SHAPE_POLY_SET* outline;
if( zone->IsFilled() )
outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
else
outline = zone->Outline();
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;
// Otherwise do a binary search for a "good enough" marker location
else
{
while( GetLineLength( pt1, pt2 ) > EPSILON )
{
if( outline->Distance( pt1 ) < outline->Distance( pt2 ) )
pt2 = ( pt1 + pt2 ) / 2;
else
pt1 = ( pt1 + pt2 ) / 2;
}
// Once we're within EPSILON pt1 and pt2 are "equivalent"
posA = pt1;
}
posB = ((ZONE_CONTAINER*)bItem)->GetPosition();
} }
} }
else else
posA = aTrack->GetPosition(); posA = aTrack->GetPosition();
if( fillMe ) if( fillMe )
fillMe->SetData( m_units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB ); fillMe->SetData( units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB );
else else
fillMe = new MARKER_PCB( m_units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB ); fillMe = new MARKER_PCB( units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB );
return fillMe; return fillMe;
} }
@ -100,6 +134,8 @@ MARKER_PCB* DRC::fillMarker( TRACK* aTrack, BOARD_ITEM* bItem, int aErrorCode, M
MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe ) MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MARKER_PCB* fillMe )
{ {
EDA_UNITS_T units = m_pcbEditorFrame->GetUserUnits();
wxPoint posA = aPad->GetPosition(); wxPoint posA = aPad->GetPosition();
wxPoint posB; wxPoint posB;
@ -115,6 +151,10 @@ MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MAR
posB = ((TEXTE_PCB*)aItem)->GetPosition(); posB = ((TEXTE_PCB*)aItem)->GetPosition();
break; break;
case PCB_MODULE_TEXT_T:
posB = ((TEXTE_MODULE*)aItem)->GetPosition();
break;
default: default:
wxLogDebug( wxT("fillMarker: unsupported item") ); wxLogDebug( wxT("fillMarker: unsupported item") );
break; break;
@ -122,9 +162,9 @@ MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MAR
} }
if( fillMe ) if( fillMe )
fillMe->SetData( m_units, aErrorCode, posA, aPad, posA, aItem, posB ); fillMe->SetData( units, aErrorCode, posA, aPad, posA, aItem, posB );
else else
fillMe = new MARKER_PCB( m_units, aErrorCode, posA, aPad, posA, aItem, posB ); fillMe = new MARKER_PCB( units, aErrorCode, posA, aPad, posA, aItem, posB );
return fillMe; return fillMe;
} }
@ -140,10 +180,12 @@ MARKER_PCB* DRC::fillMarker(BOARD_ITEM *aItem, const wxPoint &aPos, int aErrorCo
MARKER_PCB* DRC::fillMarker( const wxPoint& aPos, BOARD_ITEM* aItem, BOARD_ITEM* bItem, MARKER_PCB* DRC::fillMarker( const wxPoint& aPos, BOARD_ITEM* aItem, BOARD_ITEM* bItem,
int aErrorCode, MARKER_PCB* fillMe ) int aErrorCode, MARKER_PCB* fillMe )
{ {
EDA_UNITS_T units = m_pcbEditorFrame->GetUserUnits();
if( fillMe ) if( fillMe )
fillMe->SetData( m_units, aErrorCode, aPos, aItem, aPos, bItem, aPos ); fillMe->SetData( units, aErrorCode, aPos, aItem, aPos, bItem, aPos );
else else
fillMe = new MARKER_PCB( m_units, aErrorCode, aPos, aItem, aPos, bItem, aPos ); fillMe = new MARKER_PCB( units, aErrorCode, aPos, aItem, aPos, bItem, aPos );
return fillMe; return fillMe;
} }

View File

@ -186,15 +186,21 @@ bool ZONE_FILLER::Fill( std::vector<ZONE_CONTAINER*> aZones, bool aCheck )
if( aCheck ) if( aCheck )
{ {
bool refill = false; bool refill = false;
wxCHECK( m_progressReporter, false );
if( outOfDate ) if( outOfDate )
{ {
KIDIALOG dlg( m_progressReporter, _( "Zone fills are out-of-date. Refill?" ), m_progressReporter->Hide();
KIDIALOG dlg( m_progressReporter->GetParent(),
_( "Zone fills are out-of-date. Refill?" ),
_( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING ); _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
dlg.SetOKCancelLabels( _( "Refill" ), _( "Continue without Refill" ) ); dlg.SetOKCancelLabels( _( "Refill" ), _( "Continue without Refill" ) );
dlg.DoNotShowCheckbox(); dlg.DoNotShowCheckbox();
refill = ( dlg.ShowModal() == wxID_OK ); refill = ( dlg.ShowModal() == wxID_OK );
m_progressReporter->Show();
} }
if( !refill ) if( !refill )
@ -512,77 +518,69 @@ void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone,
{ {
int clearance = std::max( zone_clearance, item_clearance ); int clearance = std::max( zone_clearance, item_clearance );
track->TransformShapeWithClearanceToPolygon( aFeatures, track->TransformShapeWithClearanceToPolygon( aFeatures,
clearance, clearance, segsPerCircle, correctionFactor );
segsPerCircle,
correctionFactor );
} }
} }
/* Add module edge items that are on copper layers /* Add graphic items that are on copper layers. These have no net, so we just
* Pcbnew allows these items to be on copper layers in microwave applictions * use the zone clearance (or edge clearance).
* This is a bad thing, but must be handled here, until a better way is found
*/ */
for( auto module : m_board->Modules() ) auto doGraphicItem = [&]( BOARD_ITEM* aItem )
{ {
for( auto item : module->GraphicalItems() ) if( !aItem->IsOnLayer( aZone->GetLayer() ) )
{ return;
if( !item->IsOnLayer( aZone->GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) )
continue;
if( item->Type() != PCB_MODULE_EDGE_T ) if( !aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
continue; return;
item_boundingbox = item->GetBoundingBox();
if( item_boundingbox.Intersects( zone_boundingbox ) )
{
int zclearance = zone_clearance;
if( item->IsOnLayer( Edge_Cuts ) )
// use only the m_ZoneClearance, not the clearance using
// the netclass value, because we do not have a copper item
zclearance = zone_to_edgecut_clearance;
( (EDGE_MODULE*) item )->TransformShapeWithClearanceToPolygon(
aFeatures, zclearance, segsPerCircle, correctionFactor );
}
}
}
// Add graphic items (copper texts) and board edges
// Currently copper texts have no net, so only the zone_clearance
// is used.
for( auto item : m_board->Drawings() )
{
if( item->GetLayer() != aZone->GetLayer() && item->GetLayer() != Edge_Cuts )
continue;
int zclearance = zone_clearance; int zclearance = zone_clearance;
if( item->GetLayer() == Edge_Cuts ) if( aItem->IsOnLayer( Edge_Cuts ) )
// use only the m_ZoneClearance, not the clearance using // use only the m_ZoneClearance, not the clearance using
// the netclass value, because we do not have a copper item // the netclass value, because we do not have a copper item
zclearance = zone_to_edgecut_clearance; zclearance = zone_to_edgecut_clearance;
switch( item->Type() ) switch( aItem->Type() )
{ {
case PCB_LINE_T: case PCB_LINE_T:
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon( ( (DRAWSEGMENT*) aItem )->TransformShapeWithClearanceToPolygon(
aFeatures, aFeatures, zclearance, segsPerCircle, correctionFactor );
zclearance, segsPerCircle, correctionFactor );
break; break;
case PCB_TEXT_T: case PCB_TEXT_T:
( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon( ( (TEXTE_PCB*) aItem )->TransformBoundingBoxWithClearanceToPolygon(
aFeatures, zclearance ); &aFeatures, zclearance );
break;
case PCB_MODULE_EDGE_T:
( (EDGE_MODULE*) aItem )->TransformShapeWithClearanceToPolygon(
aFeatures, zclearance, segsPerCircle, correctionFactor );
break;
case PCB_MODULE_TEXT_T:
( (TEXTE_PCB*) aItem )->TransformBoundingBoxWithClearanceToPolygon(
&aFeatures, zclearance );
break; break;
default: default:
break; break;
} }
};
for( auto module : m_board->Modules() )
{
doGraphicItem( &module->Reference() );
doGraphicItem( &module->Value() );
for( auto item : module->GraphicalItems() )
doGraphicItem( item );
} }
// Add zones outlines having an higher priority and keepout for( auto item : m_board->Drawings() )
doGraphicItem( item );
/* Add zones outlines having an higher priority and keepout
*/
for( int ii = 0; ii < m_board->GetAreaCount(); ii++ ) for( int ii = 0; ii < m_board->GetAreaCount(); ii++ )
{ {
ZONE_CONTAINER* zone = m_board->GetArea( ii ); ZONE_CONTAINER* zone = m_board->GetArea( ii );
@ -631,7 +629,8 @@ void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone,
aFeatures, min_clearance, use_net_clearance ); aFeatures, min_clearance, use_net_clearance );
} }
// Remove thermal symbols /* Remove thermal symbols
*/
for( auto module : m_board->Modules() ) for( auto module : m_board->Modules() )
{ {
for( auto pad : module->Pads() ) for( auto pad : module->Pads() )