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
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

View File

@ -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 );
}
}

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() )
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;

View File

@ -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 );

View File

@ -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

View File

@ -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;
}
}
}
}

View File

@ -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).

View File

@ -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();

View File

@ -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 &lt; 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 );
}

View File

@ -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;
}

View File

@ -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() )