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:
parent
8aa0886ce7
commit
0e3919e7aa
|
@ -36,6 +36,19 @@
|
|||
#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.
|
||||
// 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
|
||||
|
@ -251,6 +264,20 @@ public:
|
|||
*/
|
||||
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
|
||||
* Test if \a aPoint is within the bounds of this object.
|
||||
|
@ -343,6 +370,9 @@ public:
|
|||
protected:
|
||||
wxString m_Text;
|
||||
|
||||
// wxString isn't thread-safe, so make use of this in multi-threaded situations
|
||||
mutable UNIQUE_MUTEX m_mutex;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Function drawOneLineOfText
|
||||
|
|
|
@ -407,8 +407,8 @@ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet(
|
|||
* @param aCornerBuffer = a buffer to store the polygon
|
||||
* @param aClearanceValue = the clearance around the text bounding box
|
||||
*/
|
||||
void TEXTE_PCB::TransformBoundingBoxWithClearanceToPolygon(
|
||||
SHAPE_POLY_SET& aCornerBuffer,
|
||||
void EDA_TEXT::TransformBoundingBoxWithClearanceToPolygon(
|
||||
SHAPE_POLY_SET* aCornerBuffer,
|
||||
int aClearanceValue ) const
|
||||
{
|
||||
// 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].x = corners[0].x;
|
||||
|
||||
aCornerBuffer.NewOutline();
|
||||
aCornerBuffer->NewOutline();
|
||||
|
||||
for( int ii = 0; ii < 4; ii++ )
|
||||
{
|
||||
// Rotate polygon
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() )
|
||||
if( track == aWeakReference )
|
||||
|
@ -1057,24 +1057,24 @@ BOARD_ITEM* BOARD::GetItem( void* aWeakReference, bool includeDrawings )
|
|||
if( pad == aWeakReference )
|
||||
return pad;
|
||||
|
||||
if( includeDrawings )
|
||||
{
|
||||
for( BOARD_ITEM* drawing : module->GraphicalItems() )
|
||||
if( drawing == aWeakReference )
|
||||
return drawing;
|
||||
}
|
||||
if( &module->Reference() == aWeakReference )
|
||||
return &module->Reference();
|
||||
|
||||
if( &module->Value() == aWeakReference )
|
||||
return &module->Value();
|
||||
|
||||
for( BOARD_ITEM* drawing : module->GraphicalItems() )
|
||||
if( drawing == aWeakReference )
|
||||
return drawing;
|
||||
}
|
||||
|
||||
for( ZONE_CONTAINER* zone : Zones() )
|
||||
if( zone == aWeakReference )
|
||||
return zone;
|
||||
|
||||
if( includeDrawings )
|
||||
{
|
||||
for( BOARD_ITEM* drawing : Drawings() )
|
||||
if( drawing == aWeakReference )
|
||||
return drawing;
|
||||
}
|
||||
for( BOARD_ITEM* drawing : Drawings() )
|
||||
if( drawing == aWeakReference )
|
||||
return drawing;
|
||||
|
||||
// Not found; weak reference has been deleted.
|
||||
return &g_DeletedItem;
|
||||
|
|
|
@ -283,7 +283,7 @@ public:
|
|||
|
||||
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 );
|
||||
|
||||
|
|
|
@ -41,17 +41,6 @@ class EDA_DRAW_PANEL;
|
|||
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
|
||||
{
|
||||
public:
|
||||
|
@ -111,21 +100,6 @@ public:
|
|||
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
|
||||
* Convert the text shape to a set of polygons (one by segment)
|
||||
|
@ -154,8 +128,6 @@ public:
|
|||
|
||||
virtual void SwapData( BOARD_ITEM* aImage ) override;
|
||||
|
||||
mutable UNIQUE_MUTEX m_mutex;
|
||||
|
||||
#if defined(DEBUG)
|
||||
virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
|
||||
#endif
|
||||
|
|
221
pcbnew/drc.cpp
221
pcbnew/drc.cpp
|
@ -131,7 +131,6 @@ DRC::DRC( PCB_EDIT_FRAME* aPcbWindow )
|
|||
m_pcbEditorFrame = aPcbWindow;
|
||||
m_pcb = aPcbWindow->GetBoard();
|
||||
m_drcDialog = NULL;
|
||||
m_units = aPcbWindow->GetUserUnits();
|
||||
|
||||
// establish initial values for everything:
|
||||
m_drcInLegacyRoutingMode = false;
|
||||
|
@ -430,15 +429,6 @@ void DRC::RunTests( wxTextCtrl* aMessages )
|
|||
|
||||
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:
|
||||
wxWindow* caller = aMessages ? aMessages->GetParent() : m_pcbEditorFrame;
|
||||
|
||||
|
@ -457,6 +447,15 @@ void DRC::RunTests( wxTextCtrl* aMessages )
|
|||
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
|
||||
if( aMessages )
|
||||
{
|
||||
|
@ -942,7 +941,7 @@ void DRC::testKeepoutAreas()
|
|||
if( area->Outline()->Distance( SEG( segm->GetStart(), segm->GetEnd() ),
|
||||
segm->GetWidth() ) == 0 )
|
||||
{
|
||||
addMarkerToPcb( fillMarker( segm, NULL,
|
||||
addMarkerToPcb( fillMarker( segm, area,
|
||||
DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker ) );
|
||||
m_currentMarker = nullptr;
|
||||
}
|
||||
|
@ -972,124 +971,144 @@ void DRC::testKeepoutAreas()
|
|||
|
||||
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<D_PAD*> padList = m_pcb->GetPads();
|
||||
|
||||
// Test text areas for vias, tracks and pads inside text areas
|
||||
for( auto item : m_pcb->Drawings() )
|
||||
// So far the bounding box makes up the text-area
|
||||
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( !IsCopperLayer( item->GetLayer() ) )
|
||||
if( !track->IsOnLayer( aTextItem->GetLayer() ) )
|
||||
continue;
|
||||
|
||||
// only texts on copper layers are tested
|
||||
if( item->Type() != PCB_TEXT_T )
|
||||
continue;
|
||||
// Test the distance between each segment and the current track/via
|
||||
int min_dist = ( track->GetWidth() + text->GetThickness() ) / 2 +
|
||||
track->GetClearance( NULL );
|
||||
|
||||
textShape.clear();
|
||||
|
||||
// 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->Type() == PCB_TRACE_T )
|
||||
{
|
||||
if( !track->IsOnLayer( item->GetLayer() ) )
|
||||
continue;
|
||||
SEG segref( track->GetStart(), track->GetEnd() );
|
||||
wxPoint markerPos;
|
||||
int closestApproach = INT_MAX;
|
||||
|
||||
// Test the distance between each segment and the current track/via
|
||||
int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 +
|
||||
track->GetClearance(NULL);
|
||||
|
||||
if( track->Type() == PCB_TRACE_T )
|
||||
// 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 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
|
||||
// smaller than the clearance of the segment
|
||||
for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
|
||||
if( dist < closestApproach )
|
||||
{
|
||||
SEG segtest( textShape[jj], textShape[jj+1] );
|
||||
int dist = segref.Distance( segtest );
|
||||
|
||||
if( dist < min_dist )
|
||||
{
|
||||
addMarkerToPcb( fillMarker( track, text,
|
||||
DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) );
|
||||
m_currentMarker = nullptr;
|
||||
break;
|
||||
}
|
||||
markerPos = textShape[jj];
|
||||
closestApproach = dist;
|
||||
}
|
||||
}
|
||||
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 ) )
|
||||
{
|
||||
addMarkerToPcb( fillMarker( track, text,
|
||||
DRCE_VIA_INSIDE_TEXT, m_currentMarker ) );
|
||||
m_currentMarker = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( closestApproach < min_dist )
|
||||
{
|
||||
addMarkerToPcb( fillMarker( markerPos, track, aTextItem,
|
||||
DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) );
|
||||
m_currentMarker = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Test pads
|
||||
for( unsigned ii = 0; ii < padList.size(); ii++ )
|
||||
else if( track->Type() == PCB_VIA_T )
|
||||
{
|
||||
D_PAD* pad = padList[ii];
|
||||
|
||||
if( !pad->IsOnLayer( item->GetLayer() ) )
|
||||
continue;
|
||||
|
||||
wxPoint shape_pos = pad->ShapePos();
|
||||
|
||||
// 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 )
|
||||
{
|
||||
/* 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 segtest( 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( segtest.PointCloserThan( track->GetPosition(), min_dist ) )
|
||||
{
|
||||
// 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, text,
|
||||
DRCE_PAD_INSIDE_TEXT, m_currentMarker ) );
|
||||
addMarkerToPcb( fillMarker( track->GetPosition(), track, aTextItem,
|
||||
DRCE_VIA_INSIDE_TEXT, m_currentMarker ) );
|
||||
m_currentMarker = nullptr;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@
|
|||
#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
|
||||
|
||||
|
||||
class EDA_DRAW_PANEL;
|
||||
|
@ -105,6 +106,7 @@ class TRACK;
|
|||
class MARKER_PCB;
|
||||
class DRC_ITEM;
|
||||
class NETCLASS;
|
||||
class EDA_TEXT;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -214,7 +216,6 @@ private:
|
|||
PCB_EDIT_FRAME* m_pcbEditorFrame; ///< The pcb frame editor which owns the board
|
||||
BOARD* m_pcb;
|
||||
DIALOG_DRC_CONTROL* m_drcDialog;
|
||||
EDA_UNITS_T m_units;
|
||||
|
||||
DRC_LIST m_unconnected; ///< list of unconnected pads, as DRC_ITEMs
|
||||
|
||||
|
@ -312,6 +313,9 @@ private:
|
|||
|
||||
void testKeepoutAreas();
|
||||
|
||||
// aTextItem is type BOARD_ITEM* to accept either TEXTE_PCB or TEXTE_MODULE
|
||||
void doText( BOARD_ITEM* aTextItem );
|
||||
|
||||
void testTexts();
|
||||
|
||||
///> Tests for items placed on disabled layers (causing false connections).
|
||||
|
|
|
@ -197,7 +197,10 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
|
|||
layerMask = aRefSeg->GetLayerSet();
|
||||
net_code_ref = aRefSeg->GetNetCode();
|
||||
|
||||
// Phase 0 : Test vias
|
||||
/******************************************/
|
||||
/* Phase 0 : via DRC tests : */
|
||||
/******************************************/
|
||||
|
||||
if( aRefSeg->Type() == PCB_VIA_T )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
commitMarkers();
|
||||
|
|
|
@ -40,15 +40,15 @@ wxString DRC_ITEM::GetErrorText() const
|
|||
case DRCE_UNCONNECTED_ITEMS:
|
||||
return wxString( _( "Unconnected items" ) );
|
||||
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:
|
||||
return wxString( _( "Track near pad" ) );
|
||||
return wxString( _( "Track too close to pad" ) );
|
||||
case DRCE_TRACK_NEAR_VIA:
|
||||
return wxString( _( "Track near via" ) );
|
||||
return wxString( _( "Track too close to via" ) );
|
||||
case DRCE_VIA_NEAR_VIA:
|
||||
return wxString( _( "Via near via" ) );
|
||||
return wxString( _( "Via too close to via" ) );
|
||||
case DRCE_VIA_NEAR_TRACK:
|
||||
return wxString( _( "Via near track" ) );
|
||||
return wxString( _( "Via too close to track" ) );
|
||||
case DRCE_TRACK_ENDS1:
|
||||
case DRCE_TRACK_ENDS2:
|
||||
case DRCE_TRACK_ENDS3:
|
||||
|
@ -63,8 +63,10 @@ wxString DRC_ITEM::GetErrorText() const
|
|||
return wxString( _( "Two parallel track segments too close" ) );
|
||||
case DRCE_TRACKS_CROSSING:
|
||||
return wxString( _( "Tracks crossing" ) );
|
||||
case DRCE_TRACK_NEAR_ZONE:
|
||||
return wxString( _( "Track too close to copper area" ) );
|
||||
case DRCE_PAD_NEAR_PAD1:
|
||||
return wxString( _( "Pad near pad" ) );
|
||||
return wxString( _( "Pad too close to pad" ) );
|
||||
case DRCE_VIA_HOLE_BIGGER:
|
||||
return wxString( _( "Via hole > diameter" ) );
|
||||
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" ) );
|
||||
|
||||
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:
|
||||
return wxString( _( "Hole near pad" ) );
|
||||
return wxString( _( "Hole too close to pad" ) );
|
||||
case DRCE_HOLE_NEAR_TRACK:
|
||||
return wxString( _( "Hole near track" ) );
|
||||
return wxString( _( "Hole too close to track" ) );
|
||||
case DRCE_TOO_SMALL_TRACK_WIDTH:
|
||||
return wxString( _( "Too small track width" ) );
|
||||
return wxString( _( "Track width too small" ) );
|
||||
case DRCE_TOO_SMALL_VIA:
|
||||
return wxString( _( "Too small via size" ) );
|
||||
return wxString( _( "Via size too small" ) );
|
||||
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:
|
||||
return wxString( _( "Too small via drill" ) );
|
||||
return wxString( _( "Via drill too small" ) );
|
||||
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:
|
||||
return wxString( _( "Drilled holes too close together" ) );
|
||||
|
||||
|
@ -115,22 +117,22 @@ wxString DRC_ITEM::GetErrorText() const
|
|||
return wxString( _( "NetClass uVia Drill < global limit" ) );
|
||||
|
||||
case DRCE_VIA_INSIDE_KEEPOUT:
|
||||
return wxString( _( "Via inside a keepout area" ) );
|
||||
return wxString( _( "Via inside keepout area" ) );
|
||||
|
||||
case DRCE_TRACK_INSIDE_KEEPOUT:
|
||||
return wxString( _( "Track inside a keepout area" ) );
|
||||
return wxString( _( "Track inside keepout area" ) );
|
||||
|
||||
case DRCE_PAD_INSIDE_KEEPOUT:
|
||||
return wxString( _( "Pad inside a keepout area" ) );
|
||||
return wxString( _( "Pad inside keepout area" ) );
|
||||
|
||||
case DRCE_VIA_INSIDE_TEXT:
|
||||
return wxString( _( "Via inside a text" ) );
|
||||
return wxString( _( "Via inside text" ) );
|
||||
|
||||
case DRCE_TRACK_INSIDE_TEXT:
|
||||
return wxString( _( "Track inside a text" ) );
|
||||
return wxString( _( "Track inside text" ) );
|
||||
|
||||
case DRCE_PAD_INSIDE_TEXT:
|
||||
return wxString( _( "Pad inside a text" ) );
|
||||
return wxString( _( "Pad inside text" ) );
|
||||
|
||||
case DRCE_OVERLAPPING_FOOTPRINTS:
|
||||
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
|
||||
{
|
||||
return aBoard->GetItem( m_mainItemWeakRef, false /* copper only */ );
|
||||
return aBoard->GetItem( m_mainItemWeakRef );
|
||||
}
|
||||
|
||||
|
||||
BOARD_ITEM* DRC_ITEM::GetAuxiliaryItem( BOARD* aBoard ) const
|
||||
{
|
||||
return aBoard->GetItem( m_auxItemWeakRef, false /* copper only */ );
|
||||
return aBoard->GetItem( m_auxItemWeakRef );
|
||||
}
|
||||
|
||||
|
|
|
@ -36,18 +36,23 @@
|
|||
#include <common.h>
|
||||
#include <pcbnew.h>
|
||||
#include <board_design_settings.h>
|
||||
|
||||
#include <pcb_edit_frame.h>
|
||||
#include <drc.h>
|
||||
#include <class_pad.h>
|
||||
#include <class_track.h>
|
||||
#include <class_zone.h>
|
||||
#include <class_marker_pcb.h>
|
||||
#include <class_pcb_text.h>
|
||||
#include <class_text_mod.h>
|
||||
#include <class_edge_mod.h>
|
||||
#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();
|
||||
|
||||
|
@ -63,36 +68,65 @@ MARKER_PCB* DRC::fillMarker( TRACK* aTrack, BOARD_ITEM* bItem, int aErrorCode, M
|
|||
}
|
||||
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();
|
||||
|
||||
// either of aItem's start or end will be used for the marker position
|
||||
// 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;
|
||||
// Once we're within EPSILON pt1 and pt2 are "equivalent"
|
||||
posA = pt1;
|
||||
posB = bTrack->GetPosition();
|
||||
}
|
||||
else if( bItem->Type() == PCB_TEXT_T )
|
||||
else if( bItem->Type() == PCB_ZONE_T || bItem->Type() == PCB_ZONE_AREA_T )
|
||||
{
|
||||
posA = aTrack->GetPosition();
|
||||
posB = ((TEXTE_PCB*) bItem)->GetPosition();
|
||||
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();
|
||||
|
||||
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
|
||||
posA = aTrack->GetPosition();
|
||||
|
||||
if( fillMe )
|
||||
fillMe->SetData( m_units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB );
|
||||
fillMe->SetData( units, aErrorCode, posA, aTrack, aTrack->GetPosition(), bItem, posB );
|
||||
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;
|
||||
}
|
||||
|
@ -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 )
|
||||
{
|
||||
EDA_UNITS_T units = m_pcbEditorFrame->GetUserUnits();
|
||||
|
||||
wxPoint posA = aPad->GetPosition();
|
||||
wxPoint posB;
|
||||
|
||||
|
@ -115,6 +151,10 @@ MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MAR
|
|||
posB = ((TEXTE_PCB*)aItem)->GetPosition();
|
||||
break;
|
||||
|
||||
case PCB_MODULE_TEXT_T:
|
||||
posB = ((TEXTE_MODULE*)aItem)->GetPosition();
|
||||
break;
|
||||
|
||||
default:
|
||||
wxLogDebug( wxT("fillMarker: unsupported item") );
|
||||
break;
|
||||
|
@ -122,9 +162,9 @@ MARKER_PCB* DRC::fillMarker( D_PAD* aPad, BOARD_ITEM* aItem, int aErrorCode, MAR
|
|||
}
|
||||
|
||||
if( fillMe )
|
||||
fillMe->SetData( m_units, aErrorCode, posA, aPad, posA, aItem, posB );
|
||||
fillMe->SetData( units, aErrorCode, posA, aPad, posA, aItem, posB );
|
||||
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;
|
||||
}
|
||||
|
@ -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,
|
||||
int aErrorCode, MARKER_PCB* fillMe )
|
||||
{
|
||||
EDA_UNITS_T units = m_pcbEditorFrame->GetUserUnits();
|
||||
|
||||
if( fillMe )
|
||||
fillMe->SetData( m_units, aErrorCode, aPos, aItem, aPos, bItem, aPos );
|
||||
fillMe->SetData( units, aErrorCode, aPos, aItem, aPos, bItem, aPos );
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -186,15 +186,21 @@ bool ZONE_FILLER::Fill( std::vector<ZONE_CONTAINER*> aZones, bool aCheck )
|
|||
if( aCheck )
|
||||
{
|
||||
bool refill = false;
|
||||
wxCHECK( m_progressReporter, false );
|
||||
|
||||
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 );
|
||||
dlg.SetOKCancelLabels( _( "Refill" ), _( "Continue without Refill" ) );
|
||||
dlg.DoNotShowCheckbox();
|
||||
|
||||
refill = ( dlg.ShowModal() == wxID_OK );
|
||||
|
||||
m_progressReporter->Show();
|
||||
}
|
||||
|
||||
if( !refill )
|
||||
|
@ -512,77 +518,69 @@ void ZONE_FILLER::buildZoneFeatureHoleList( const ZONE_CONTAINER* aZone,
|
|||
{
|
||||
int clearance = std::max( zone_clearance, item_clearance );
|
||||
track->TransformShapeWithClearanceToPolygon( aFeatures,
|
||||
clearance,
|
||||
segsPerCircle,
|
||||
correctionFactor );
|
||||
clearance, segsPerCircle, correctionFactor );
|
||||
}
|
||||
}
|
||||
|
||||
/* Add module edge items that are on copper layers
|
||||
* Pcbnew allows these items to be on copper layers in microwave applictions
|
||||
* This is a bad thing, but must be handled here, until a better way is found
|
||||
/* Add graphic items that are on copper layers. These have no net, so we just
|
||||
* use the zone clearance (or edge clearance).
|
||||
*/
|
||||
for( auto module : m_board->Modules() )
|
||||
auto doGraphicItem = [&]( BOARD_ITEM* aItem )
|
||||
{
|
||||
for( auto item : module->GraphicalItems() )
|
||||
{
|
||||
if( !item->IsOnLayer( aZone->GetLayer() ) && !item->IsOnLayer( Edge_Cuts ) )
|
||||
continue;
|
||||
if( !aItem->IsOnLayer( aZone->GetLayer() ) )
|
||||
return;
|
||||
|
||||
if( item->Type() != PCB_MODULE_EDGE_T )
|
||||
continue;
|
||||
|
||||
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;
|
||||
if( !aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
|
||||
return;
|
||||
|
||||
int zclearance = zone_clearance;
|
||||
|
||||
if( item->GetLayer() == Edge_Cuts )
|
||||
if( aItem->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;
|
||||
|
||||
switch( item->Type() )
|
||||
switch( aItem->Type() )
|
||||
{
|
||||
case PCB_LINE_T:
|
||||
( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
|
||||
aFeatures,
|
||||
zclearance, segsPerCircle, correctionFactor );
|
||||
( (DRAWSEGMENT*) aItem )->TransformShapeWithClearanceToPolygon(
|
||||
aFeatures, zclearance, segsPerCircle, correctionFactor );
|
||||
break;
|
||||
|
||||
case PCB_TEXT_T:
|
||||
( (TEXTE_PCB*) item )->TransformBoundingBoxWithClearanceToPolygon(
|
||||
aFeatures, zclearance );
|
||||
( (TEXTE_PCB*) aItem )->TransformBoundingBoxWithClearanceToPolygon(
|
||||
&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;
|
||||
|
||||
default:
|
||||
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++ )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
|
||||
// Remove thermal symbols
|
||||
/* Remove thermal symbols
|
||||
*/
|
||||
for( auto module : m_board->Modules() )
|
||||
{
|
||||
for( auto pad : module->Pads() )
|
||||
|
|
Loading…
Reference in New Issue