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
|
OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const
|
||||||
{
|
{
|
||||||
const VECTOR2I e( B - A );
|
const VECTOR2I e( B - A );
|
||||||
|
|
|
@ -35,10 +35,8 @@ typedef OPT<VECTOR2I> OPT_VECTOR2I;
|
||||||
|
|
||||||
class SEG
|
class SEG
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
typedef VECTOR2I::extended_type ecoord;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
using ecoord = VECTOR2I::extended_type;
|
||||||
friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg );
|
friend inline std::ostream& operator<<( std::ostream& aStream, const SEG& aSeg );
|
||||||
|
|
||||||
/* Start and the of the segment. Public, to make access simpler.
|
/* Start and the of the segment. Public, to make access simpler.
|
||||||
|
@ -156,6 +154,12 @@ public:
|
||||||
*/
|
*/
|
||||||
const VECTOR2I NearestPoint( const VECTOR2I &aP ) const;
|
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()
|
* Function Intersect()
|
||||||
*
|
*
|
||||||
|
|
|
@ -171,8 +171,6 @@ void BOARD::BuildConnectivity()
|
||||||
|
|
||||||
const wxPoint BOARD::GetPosition() const
|
const wxPoint BOARD::GetPosition() const
|
||||||
{
|
{
|
||||||
wxLogWarning( wxT( "This should not be called on the BOARD object") );
|
|
||||||
|
|
||||||
return ZeroOffset;
|
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()
|
void BOARD::DeleteMARKERs()
|
||||||
{
|
{
|
||||||
// the vector does not know how to delete the MARKER_PCB, it holds pointers
|
// 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
|
* return true if success, false if a contour is not valid
|
||||||
*/
|
*/
|
||||||
extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
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)
|
// Make polygon strictly simple to avoid issues (especially in 3D viewer)
|
||||||
aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||||
|
|
|
@ -562,6 +562,8 @@ public:
|
||||||
const ZONE_SETTINGS& GetZoneSettings() const { return m_zoneSettings; }
|
const ZONE_SETTINGS& GetZoneSettings() const { return m_zoneSettings; }
|
||||||
void SetZoneSettings( const ZONE_SETTINGS& aSettings ) { m_zoneSettings = aSettings; }
|
void SetZoneSettings( const ZONE_SETTINGS& aSettings ) { m_zoneSettings = aSettings; }
|
||||||
|
|
||||||
|
wxString GetSelectMenuText( EDA_UNITS_T aUnits ) const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function GetColorSettings
|
* Function GetColorSettings
|
||||||
* @return the current COLORS_DESIGN_SETTINGS in use
|
* @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 aOutlines The SHAPE_POLY_SET to fill in with outlines/holes.
|
||||||
* @param aErrorText = a wxString reference to display an error message
|
* @param aErrorText = a wxString reference to display an error message
|
||||||
* with the coordinate of the point which creates the error
|
* 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
|
* @return true if success, false if a contour is not valid
|
||||||
*/
|
*/
|
||||||
bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
|
bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines,
|
||||||
wxString* aErrorText = NULL );
|
wxString* aErrorText = nullptr, wxPoint* aErrorLocation = nullptr );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function ConvertBrdLayerToPolygonalContours
|
* Function ConvertBrdLayerToPolygonalContours
|
||||||
|
|
|
@ -1445,7 +1445,7 @@ double MODULE::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
|
||||||
|
|
||||||
// see convert_drawsegment_list_to_polygon.cpp:
|
// see convert_drawsegment_list_to_polygon.cpp:
|
||||||
extern bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SET& aPolygons,
|
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()
|
bool MODULE::BuildPolyCourtyard()
|
||||||
{
|
{
|
||||||
|
|
|
@ -177,9 +177,10 @@ static DRAWSEGMENT* findPoint( const wxPoint& aPoint, std::vector< DRAWSEGMENT*
|
||||||
* @param aPolygons will contain the complex polygon.
|
* @param aPolygons will contain the complex polygon.
|
||||||
* @param aTolerance is the max distance between points that is still accepted as connected (internal units)
|
* @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 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,
|
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 )
|
if( aSegList.size() == 0 )
|
||||||
return true;
|
return true;
|
||||||
|
@ -415,6 +416,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
||||||
*aErrorText << msg << "\n";
|
*aErrorText << msg << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( aErrorLocation )
|
||||||
|
*aErrorLocation = graphic->GetPosition();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,6 +447,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
||||||
*aErrorText << msg << "\n";
|
*aErrorText << msg << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( aErrorLocation )
|
||||||
|
*aErrorLocation = prevPt;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -594,6 +601,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
||||||
*aErrorText << msg << "\n";
|
*aErrorText << msg << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( aErrorLocation )
|
||||||
|
*aErrorLocation = graphic->GetPosition();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,6 +632,9 @@ bool ConvertOutlineToPolygon( std::vector<DRAWSEGMENT*>& aSegList, SHAPE_POLY_SE
|
||||||
*aErrorText << msg << "\n";
|
*aErrorText << msg << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( aErrorLocation )
|
||||||
|
*aErrorLocation = prevPt;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
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
|
* All contours should be closed, i.e. valid closed polygon vertices
|
||||||
*/
|
*/
|
||||||
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||||
wxString* aErrorText, unsigned int aTolerance )
|
wxString* aErrorText, unsigned int aTolerance, wxPoint* aErrorLocation )
|
||||||
{
|
{
|
||||||
PCB_TYPE_COLLECTOR items;
|
PCB_TYPE_COLLECTOR items;
|
||||||
|
|
||||||
|
@ -659,7 +672,7 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||||
segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) );
|
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() )
|
if( !success || !aOutlines.OutlineCount() )
|
||||||
{
|
{
|
||||||
|
|
|
@ -221,7 +221,8 @@ void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
|
||||||
}
|
}
|
||||||
|
|
||||||
extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
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 )
|
void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
|
||||||
{
|
{
|
||||||
|
|
|
@ -393,8 +393,15 @@ void DRC::RunTests( wxTextCtrl* aMessages )
|
||||||
// ( the board can be reloaded )
|
// ( the board can be reloaded )
|
||||||
m_pcb = m_pcbEditorFrame->GetBoard();
|
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() )
|
if( !testNetClasses() )
|
||||||
{
|
{
|
||||||
// testing the netclasses is a special case because if the netclasses
|
// 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()
|
void DRC::testDisabledLayers()
|
||||||
{
|
{
|
||||||
BOARD* board = m_pcbEditorFrame->GetBoard();
|
BOARD* board = m_pcbEditorFrame->GetBoard();
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <geometry/seg.h>
|
#include <geometry/seg.h>
|
||||||
|
#include <geometry/shape_poly_set.h>
|
||||||
|
|
||||||
#define OK_DRC 0
|
#define OK_DRC 0
|
||||||
#define BAD_DRC 1
|
#define BAD_DRC 1
|
||||||
|
@ -94,6 +95,8 @@
|
||||||
#define DRCE_BURIED_VIA_NOT_ALLOWED 49 ///< buried vias are not allowed
|
#define DRCE_BURIED_VIA_NOT_ALLOWED 49 ///< buried vias are not allowed
|
||||||
#define DRCE_DISABLED_LAYER_ITEM 50 ///< item on a disabled layer
|
#define DRCE_DISABLED_LAYER_ITEM 50 ///< item on a disabled layer
|
||||||
#define DRCE_DRILLED_HOLES_TOO_CLOSE 51 ///< overlapping drilled holes break drill bits
|
#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;
|
class EDA_DRAW_PANEL;
|
||||||
|
@ -220,6 +223,7 @@ 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;
|
||||||
|
SHAPE_POLY_SET m_board_outlines; ///< The board outline including cutouts
|
||||||
DIALOG_DRC_CONTROL* m_drcDialog;
|
DIALOG_DRC_CONTROL* m_drcDialog;
|
||||||
|
|
||||||
DRC_LIST m_unconnected; ///< list of unconnected pads, as DRC_ITEMs
|
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).
|
///> Tests for items placed on disabled layers (causing false connections).
|
||||||
void testDisabledLayers();
|
void testDisabledLayers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that the board outline is contiguous and composed of valid elements
|
||||||
|
*/
|
||||||
|
void testOutline();
|
||||||
|
|
||||||
//-----<single "item" tests>-----------------------------------------
|
//-----<single "item" tests>-----------------------------------------
|
||||||
|
|
||||||
bool doNetClass( const std::shared_ptr<NETCLASS>& aNetClass, wxString& msg );
|
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 ) );
|
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 )
|
if( markers.size() > 0 )
|
||||||
{
|
{
|
||||||
commitMarkers();
|
commitMarkers();
|
||||||
|
|
|
@ -101,6 +101,10 @@ wxString DRC_ITEM::GetErrorText() const
|
||||||
return wxString( _( "Micro via drill too small" ) );
|
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" ) );
|
||||||
|
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
|
// use < since this is text ultimately embedded in HTML
|
||||||
case DRCE_NETCLASS_TRACKWIDTH:
|
case DRCE_NETCLASS_TRACKWIDTH:
|
||||||
|
|
Loading…
Reference in New Issue