drc: Add board outline and edge crossing
This adds a check for contiguous board outlines to the DRC. It also uses the calculated outline to ensure that traces are not crossing the outlines. Fixes: lp:1648055 * https://bugs.launchpad.net/kicad/+bug/1648055
This commit is contained in:
parent
5eb5b5ce58
commit
26765161c1
|
@ -96,6 +96,39 @@ SEG::ecoord SEG::SquaredDistance( const SEG& aSeg ) const
|
|||
}
|
||||
|
||||
|
||||
const VECTOR2I SEG::NearestPoint( const SEG& aSeg ) const
|
||||
{
|
||||
if( auto p = Intersect( aSeg ) )
|
||||
return *p;
|
||||
|
||||
const VECTOR2I pts_origin[4] =
|
||||
{
|
||||
aSeg.NearestPoint( A ),
|
||||
aSeg.NearestPoint( B ),
|
||||
NearestPoint( aSeg.A ),
|
||||
NearestPoint( aSeg.B )
|
||||
};
|
||||
|
||||
const ecoord pts_dist[4] =
|
||||
{
|
||||
( pts_origin[0] - A ).SquaredEuclideanNorm(),
|
||||
( pts_origin[1] - B ).SquaredEuclideanNorm(),
|
||||
( pts_origin[2] - aSeg.A ).SquaredEuclideanNorm(),
|
||||
( pts_origin[3] - aSeg.B ).SquaredEuclideanNorm()
|
||||
};
|
||||
|
||||
int min_i = 0;
|
||||
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
if( pts_dist[i] < pts_dist[min_i] )
|
||||
min_i = i;
|
||||
}
|
||||
|
||||
return pts_origin[min_i];
|
||||
}
|
||||
|
||||
|
||||
OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const
|
||||
{
|
||||
const VECTOR2I e( B - A );
|
||||
|
|
|
@ -35,10 +35,8 @@ typedef OPT<VECTOR2I> OPT_VECTOR2I;
|
|||
|
||||
class SEG
|
||||
{
|
||||
private:
|
||||
typedef VECTOR2I::extended_type ecoord;
|
||||
|
||||
public:
|
||||
using ecoord = VECTOR2I::extended_type;
|
||||
friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg );
|
||||
|
||||
/* Start and the of the segment. Public, to make access simpler.
|
||||
|
@ -156,6 +154,12 @@ public:
|
|||
*/
|
||||
const VECTOR2I NearestPoint( const VECTOR2I &aP ) const;
|
||||
|
||||
/**
|
||||
* Computes a point on the segment (this) that is closest to any point on aSeg.
|
||||
* @return the nearest point
|
||||
*/
|
||||
const VECTOR2I NearestPoint( const SEG &aSeg ) const;
|
||||
|
||||
/**
|
||||
* Function Intersect()
|
||||
*
|
||||
|
|
|
@ -171,8 +171,6 @@ void BOARD::BuildConnectivity()
|
|||
|
||||
const wxPoint BOARD::GetPosition() const
|
||||
{
|
||||
wxLogWarning( wxT( "This should not be called on the BOARD object") );
|
||||
|
||||
return ZeroOffset;
|
||||
}
|
||||
|
||||
|
@ -1021,6 +1019,12 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem )
|
|||
}
|
||||
|
||||
|
||||
wxString BOARD::GetSelectMenuText( EDA_UNITS_T aUnits ) const
|
||||
{
|
||||
return wxString::Format( _( "PCB" ) );
|
||||
}
|
||||
|
||||
|
||||
void BOARD::DeleteMARKERs()
|
||||
{
|
||||
// the vector does not know how to delete the MARKER_PCB, it holds pointers
|
||||
|
@ -2949,12 +2953,14 @@ BOARD_ITEM* BOARD::Duplicate( const BOARD_ITEM* aItem,
|
|||
* return true if success, false if a contour is not valid
|
||||
*/
|
||||
extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||
wxString* aErrorText, unsigned int aTolerance );
|
||||
wxString* aErrorText, unsigned int aTolerance,
|
||||
wxPoint* aErrorLocation = nullptr );
|
||||
|
||||
|
||||
bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText )
|
||||
bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText, wxPoint* aErrorLocation )
|
||||
{
|
||||
bool success = BuildBoardPolygonOutlines( this, aOutlines, aErrorText, Millimeter2iu( 0.05 ) );
|
||||
bool success = BuildBoardPolygonOutlines( this, aOutlines, aErrorText,
|
||||
Millimeter2iu( 0.05 ), aErrorLocation );
|
||||
|
||||
// Make polygon strictly simple to avoid issues (especially in 3D viewer)
|
||||
aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
|
|
|
@ -562,6 +562,8 @@ public:
|
|||
const ZONE_SETTINGS& GetZoneSettings() const { return m_zoneSettings; }
|
||||
void SetZoneSettings( const ZONE_SETTINGS& aSettings ) { m_zoneSettings = aSettings; }
|
||||
|
||||
wxString GetSelectMenuText( EDA_UNITS_T aUnits ) const override;
|
||||
|
||||
/**
|
||||
* Function GetColorSettings
|
||||
* @return the current COLORS_DESIGN_SETTINGS in use
|
||||
|
@ -585,11 +587,14 @@ public:
|
|||
* @param aOutlines The SHAPE_POLY_SET to fill in with outlines/holes.
|
||||
* @param aErrorText = a wxString reference to display an error message
|
||||
* with the coordinate of the point which creates the error
|
||||
* (default = NULL , no message returned on error)
|
||||
* (default = nullptr , no message returned on error)
|
||||
* @param aErrorLocation = a wxPoint giving the location of the Error message on the board
|
||||
* if left null (default), no location is returned
|
||||
*
|
||||
* @return true if success, false if a contour is not valid
|
||||
*/
|
||||
bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
|
||||
wxString* aErrorText = NULL );
|
||||
wxString* aErrorText = nullptr, wxPoint* aErrorLocation = nullptr );
|
||||
|
||||
/**
|
||||
* Function ConvertBrdLayerToPolygonalContours
|
||||
|
|
|
@ -1445,7 +1445,7 @@ double MODULE::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
|
|||
|
||||
// see convert_drawsegment_list_to_polygon.cpp:
|
||||
extern bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SET& aPolygons,
|
||||
wxString* aErrorText, unsigned int aTolerance );
|
||||
wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation = nullptr );
|
||||
|
||||
bool MODULE::BuildPolyCourtyard()
|
||||
{
|
||||
|
|
|
@ -177,9 +177,10 @@ static DRAWSEGMENT* findPoint( const wxPoint& aPoint, std::vector< DRAWSEGMENT*
|
|||
* @param aPolygons will contain the complex polygon.
|
||||
* @param aTolerance is the max distance between points that is still accepted as connected (internal units)
|
||||
* @param aErrorText is a wxString to return error message.
|
||||
* @param aErrorLocation is the optional position of the error in the outline
|
||||
*/
|
||||
bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SET& aPolygons,
|
||||
wxString* aErrorText, unsigned int aTolerance )
|
||||
wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation )
|
||||
{
|
||||
if( aSegList.size() == 0 )
|
||||
return true;
|
||||
|
@ -415,6 +416,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
|||
*aErrorText << msg << "\n";
|
||||
}
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = graphic->GetPosition();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -443,6 +447,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
|||
*aErrorText << msg << "\n";
|
||||
}
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = prevPt;
|
||||
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -594,6 +601,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
|||
*aErrorText << msg << "\n";
|
||||
}
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = graphic->GetPosition();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -622,6 +632,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
|||
*aErrorText << msg << "\n";
|
||||
}
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = prevPt;
|
||||
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -641,7 +654,7 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
|||
* All contours should be closed, i.e. valid closed polygon vertices
|
||||
*/
|
||||
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||
wxString* aErrorText, unsigned int aTolerance )
|
||||
wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation )
|
||||
{
|
||||
PCB_TYPE_COLLECTOR items;
|
||||
|
||||
|
@ -659,7 +672,7 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
|||
segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) );
|
||||
}
|
||||
|
||||
bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance );
|
||||
bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance, aErrorLocation );
|
||||
|
||||
if( !success || !aOutlines.OutlineCount() )
|
||||
{
|
||||
|
|
|
@ -221,7 +221,8 @@ void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
|
|||
}
|
||||
|
||||
extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||
wxString* aErrorText, unsigned int aTolerance );
|
||||
wxString* aErrorText, unsigned int aTolerance,
|
||||
wxPoint* aErrorLocation = nullptr );
|
||||
|
||||
void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
|
||||
{
|
||||
|
|
|
@ -393,8 +393,15 @@ void DRC::RunTests( wxTextCtrl* aMessages )
|
|||
// ( the board can be reloaded )
|
||||
m_pcb = m_pcbEditorFrame->GetBoard();
|
||||
|
||||
// someone should have cleared the two lists before calling this.
|
||||
if( aMessages )
|
||||
{
|
||||
aMessages->AppendText( _( "Board Outline...\n" ) );
|
||||
wxSafeYield();
|
||||
}
|
||||
|
||||
testOutline();
|
||||
|
||||
// someone should have cleared the two lists before calling this.
|
||||
if( !testNetClasses() )
|
||||
{
|
||||
// testing the netclasses is a special case because if the netclasses
|
||||
|
@ -1165,6 +1172,17 @@ void DRC::testCopperTextItem( BOARD_ITEM* aTextItem )
|
|||
}
|
||||
|
||||
|
||||
void DRC::testOutline()
|
||||
{
|
||||
wxPoint error_loc( m_pcb->GetBoardEdgesBoundingBox().GetPosition() );
|
||||
if( !m_pcb->GetBoardPolygonOutlines( m_board_outlines, nullptr, &error_loc ) )
|
||||
{
|
||||
addMarkerToPcb( newMarker( error_loc, m_pcb, DRCE_INVALID_OUTLINE ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DRC::testDisabledLayers()
|
||||
{
|
||||
BOARD* board = m_pcbEditorFrame->GetBoard();
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <vector>
|
||||
#include <memory>
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
#define OK_DRC 0
|
||||
#define BAD_DRC 1
|
||||
|
@ -94,6 +95,8 @@
|
|||
#define DRCE_BURIED_VIA_NOT_ALLOWED 49 ///< buried vias are not allowed
|
||||
#define DRCE_DISABLED_LAYER_ITEM 50 ///< item on a disabled layer
|
||||
#define DRCE_DRILLED_HOLES_TOO_CLOSE 51 ///< overlapping drilled holes break drill bits
|
||||
#define DRCE_TRACK_NEAR_EDGE 53 ///< track too close to board edge
|
||||
#define DRCE_INVALID_OUTLINE 54 ///< invalid board outline
|
||||
|
||||
|
||||
class EDA_DRAW_PANEL;
|
||||
|
@ -220,6 +223,7 @@ private:
|
|||
|
||||
PCB_EDIT_FRAME* m_pcbEditorFrame; ///< The pcb frame editor which owns the board
|
||||
BOARD* m_pcb;
|
||||
SHAPE_POLY_SET m_board_outlines; ///< The board outline including cutouts
|
||||
DIALOG_DRC_CONTROL* m_drcDialog;
|
||||
|
||||
DRC_LIST m_unconnected; ///< list of unconnected pads, as DRC_ITEMs
|
||||
|
@ -314,6 +318,11 @@ private:
|
|||
///> Tests for items placed on disabled layers (causing false connections).
|
||||
void testDisabledLayers();
|
||||
|
||||
/**
|
||||
* Test that the board outline is contiguous and composed of valid elements
|
||||
*/
|
||||
void testOutline();
|
||||
|
||||
//-----<single "item" tests>-----------------------------------------
|
||||
|
||||
bool doNetClass( const std::shared_ptr<NETCLASS>& aNetClass, wxString& msg );
|
||||
|
|
|
@ -704,6 +704,32 @@ bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
|
|||
addMarkerToPcb( newMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE ) );
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
/* Phase 4: test DRC with to board edge */
|
||||
/***********************************************/
|
||||
{
|
||||
SEG test_seg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
|
||||
|
||||
// the minimum distance = clearance plus half the reference track
|
||||
// width. Board edges do not have width or clearance values, so we
|
||||
// look for simple crossing.
|
||||
SEG::ecoord w_dist = aRefSeg->GetClearance() + aRefSeg->GetWidth() / 2;
|
||||
w_dist *= w_dist;
|
||||
|
||||
for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
|
||||
{
|
||||
if( test_seg.SquaredDistance( *it ) < w_dist )
|
||||
{
|
||||
auto pt = test_seg.NearestPoint( *it );
|
||||
markers.push_back( newMarker( wxPoint( pt.x, pt.y ), aRefSeg, DRCE_TRACK_NEAR_EDGE ) );
|
||||
|
||||
if( !handleNewMarker() )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if( markers.size() > 0 )
|
||||
{
|
||||
commitMarkers();
|
||||
|
|
|
@ -101,6 +101,10 @@ wxString DRC_ITEM::GetErrorText() const
|
|||
return wxString( _( "Micro via drill too small" ) );
|
||||
case DRCE_DRILLED_HOLES_TOO_CLOSE:
|
||||
return wxString( _( "Drilled holes too close together" ) );
|
||||
case DRCE_TRACK_NEAR_EDGE:
|
||||
return wxString( _( "Track too close to board edge" ) );
|
||||
case DRCE_INVALID_OUTLINE:
|
||||
return wxString( _( "Board outline does not form a closed polygon" ) );
|
||||
|
||||
// use < since this is text ultimately embedded in HTML
|
||||
case DRCE_NETCLASS_TRACKWIDTH:
|
||||
|
|
Loading…
Reference in New Issue