Redo the board-edge-building logic to return better error reports.
Fixes https://gitlab.com/kicad/code/kicad/issues/6119
This commit is contained in:
parent
41fd8293e8
commit
cce557b9ad
|
@ -443,8 +443,9 @@ void BOARD_ADAPTER::InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningR
|
|||
|
||||
|
||||
extern bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||
wxString* aErrorText, unsigned int aTolerance,
|
||||
wxPoint* aErrorLocation );
|
||||
unsigned int aTolerance, wxString* aErrorText,
|
||||
std::vector<wxPoint>* aDiscontinuities = nullptr,
|
||||
std::vector<wxPoint>* aIntersections = nullptr );
|
||||
|
||||
|
||||
bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
|
||||
|
@ -456,9 +457,8 @@ bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
|
|||
|
||||
if( m_board->IsFootprintHolder() )
|
||||
{
|
||||
success = BuildFootprintPolygonOutlines( m_board, m_board_poly, &msg,
|
||||
m_board->GetDesignSettings().m_MaxError,
|
||||
nullptr );
|
||||
success = BuildFootprintPolygonOutlines( m_board, m_board_poly,
|
||||
m_board->GetDesignSettings().m_MaxError, &msg );
|
||||
|
||||
// Make polygon strictly simple to avoid issues (especially in 3D viewer)
|
||||
m_board_poly.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
|
|
|
@ -1845,15 +1845,17 @@ bool BOARD::NormalizeAreaPolygon( PICKED_ITEMS_LIST * aNewZonesList, ZONE_CONTAI
|
|||
* 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,
|
||||
wxPoint* aErrorLocation = nullptr );
|
||||
unsigned int aTolerance, wxString* aErrorText,
|
||||
std::vector<wxPoint>* aDiscontinuities = nullptr,
|
||||
std::vector<wxPoint>* aIntersections = nullptr );
|
||||
|
||||
|
||||
bool BOARD::GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText,
|
||||
wxPoint* aErrorLocation )
|
||||
std::vector<wxPoint>* aDiscontinuities,
|
||||
std::vector<wxPoint>* aIntersections )
|
||||
{
|
||||
bool success = BuildBoardPolygonOutlines( this, aOutlines, aErrorText,
|
||||
GetDesignSettings().m_MaxError, aErrorLocation );
|
||||
bool success = BuildBoardPolygonOutlines( this, aOutlines, GetDesignSettings().m_MaxError,
|
||||
aErrorText, aDiscontinuities, aIntersections );
|
||||
|
||||
// Make polygon strictly simple to avoid issues (especially in 3D viewer)
|
||||
aOutlines.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
|
|
|
@ -586,16 +586,18 @@ public:
|
|||
* Any closed outline inside the main outline is a hole
|
||||
* All contours should be closed, i.e. have valid vertices to build a closed polygon
|
||||
* @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 = 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
|
||||
* @param aErrorText = an optional wxString for an error message with the coordinate of the
|
||||
* first discontinuity or self-intersection
|
||||
* @param aDiscontinuities = an optional array of wxPoint giving the locations of
|
||||
* discontinuities in the outline
|
||||
* @param aIntersections = an optional array of wxPoint giving the locations of self-
|
||||
* intersections in the outline
|
||||
*
|
||||
* @return true if success, false if a contour is not valid
|
||||
*/
|
||||
bool GetBoardPolygonOutlines( SHAPE_POLY_SET& aOutlines, wxString* aErrorText = nullptr,
|
||||
wxPoint* aErrorLocation = nullptr );
|
||||
std::vector<wxPoint>* aDiscontinuities = nullptr,
|
||||
std::vector<wxPoint>* aIntersections = nullptr );
|
||||
|
||||
/**
|
||||
* Build a set of polygons which are the outlines of copper items (pads, tracks, vias, texts,
|
||||
|
|
|
@ -1668,8 +1668,9 @@ double MODULE::CoverageRatio( const GENERAL_COLLECTOR& aCollector ) const
|
|||
|
||||
// see convert_drawsegment_list_to_polygon.cpp:
|
||||
extern bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
|
||||
wxString* aErrorText, unsigned int aTolerance,
|
||||
wxPoint* aErrorLocation = nullptr );
|
||||
unsigned int aTolerance, wxString* aErrorText,
|
||||
std::vector<wxPoint>* aDiscontinuities = nullptr,
|
||||
std::vector<wxPoint>* aIntersections = nullptr );
|
||||
|
||||
|
||||
std::shared_ptr<SHAPE> MODULE::GetEffectiveShape( PCB_LAYER_ID aLayer ) const
|
||||
|
@ -1709,20 +1710,19 @@ bool MODULE::BuildPolyCourtyard()
|
|||
|
||||
#define ARC_ERROR_MAX 0.02 /* error max in mm to approximate a arc by segments */
|
||||
bool success = ConvertOutlineToPolygon( list_front, m_poly_courtyard_front,
|
||||
&error_msg,
|
||||
(unsigned) Millimeter2iu( ARC_ERROR_MAX ) );
|
||||
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg );
|
||||
|
||||
if( success )
|
||||
{
|
||||
success = ConvertOutlineToPolygon( list_back, m_poly_courtyard_back,
|
||||
&error_msg,
|
||||
(unsigned) Millimeter2iu( ARC_ERROR_MAX ) );
|
||||
(unsigned) Millimeter2iu( ARC_ERROR_MAX ), &error_msg );
|
||||
}
|
||||
|
||||
if( !error_msg.IsEmpty() )
|
||||
{
|
||||
wxLogMessage( wxString::Format(
|
||||
_( "Processing courtyard of \"%s\": %s" ), GetFPID().Format().wx_str(), error_msg ) );
|
||||
wxLogMessage( wxString::Format( _( "Processing courtyard of \"%s\": %s" ),
|
||||
GetFPID().Format().wx_str(),
|
||||
error_msg ) );
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
|
@ -105,16 +105,16 @@ inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wx
|
|||
|
||||
|
||||
/**
|
||||
* Searches for a PCB_SHAPE matching a given end point or start point in a list, and
|
||||
* if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.
|
||||
* Searches for a PCB_SHAPE matching a given end point or start point in a list.
|
||||
* @param aShape The starting shape.
|
||||
* @param aPoint The starting or ending point to search for.
|
||||
* @param aList The list to remove from.
|
||||
* @param aLimit is the distance from \a aPoint that still constitutes a valid find.
|
||||
* @return PCB_SHAPE* - The first PCB_SHAPE that has a start or end point matching
|
||||
* aPoint, otherwise NULL if none.
|
||||
*/
|
||||
static PCB_SHAPE* findPoint( const wxPoint& aPoint, std::vector< PCB_SHAPE* >& aList,
|
||||
unsigned aLimit )
|
||||
static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint,
|
||||
const std::vector<PCB_SHAPE*>& aList, unsigned aLimit )
|
||||
{
|
||||
unsigned min_d = INT_MAX;
|
||||
int ndx_min = 0;
|
||||
|
@ -123,6 +123,10 @@ static PCB_SHAPE* findPoint( const wxPoint& aPoint, std::vector< PCB_SHAPE* >& a
|
|||
for( size_t i = 0; i < aList.size(); ++i )
|
||||
{
|
||||
PCB_SHAPE* graphic = aList[i];
|
||||
|
||||
if( graphic == aShape )
|
||||
continue;
|
||||
|
||||
unsigned d;
|
||||
|
||||
switch( graphic->GetShape() )
|
||||
|
@ -130,7 +134,6 @@ static PCB_SHAPE* findPoint( const wxPoint& aPoint, std::vector< PCB_SHAPE* >& a
|
|||
case S_ARC:
|
||||
if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
|
||||
{
|
||||
aList.erase( aList.begin() + i );
|
||||
return graphic;
|
||||
}
|
||||
|
||||
|
@ -152,7 +155,6 @@ static PCB_SHAPE* findPoint( const wxPoint& aPoint, std::vector< PCB_SHAPE* >& a
|
|||
default:
|
||||
if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
|
||||
{
|
||||
aList.erase( aList.begin() + i );
|
||||
return graphic;
|
||||
}
|
||||
|
||||
|
@ -174,9 +176,7 @@ static PCB_SHAPE* findPoint( const wxPoint& aPoint, std::vector< PCB_SHAPE* >& a
|
|||
|
||||
if( min_d <= aLimit )
|
||||
{
|
||||
PCB_SHAPE* graphic = aList[ndx_min];
|
||||
aList.erase( aList.begin() + ndx_min );
|
||||
return graphic;
|
||||
return aList[ndx_min];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -193,34 +193,37 @@ static PCB_SHAPE* findPoint( const wxPoint& aPoint, std::vector< PCB_SHAPE* >& a
|
|||
* @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
|
||||
* @param aDiscontinuities = an optional array of wxPoint giving the locations of
|
||||
* discontinuities in the outline
|
||||
* @param aIntersections = an optional array of wxPoint giving the locations of self-
|
||||
* intersections in the outline
|
||||
*/
|
||||
bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
|
||||
wxString* aErrorText, unsigned int aTolerance,
|
||||
wxPoint* aErrorLocation )
|
||||
unsigned int aTolerance, wxString* aErrorText,
|
||||
std::vector<wxPoint>* aDiscontinuities,
|
||||
std::vector<wxPoint>* aIntersections )
|
||||
{
|
||||
if( aSegList.size() == 0 )
|
||||
return true;
|
||||
|
||||
// Return value
|
||||
bool polygonComplete = true;
|
||||
|
||||
wxString msg;
|
||||
|
||||
// Make a working copy of aSegList, because the list is modified during calculations
|
||||
std::vector<PCB_SHAPE*> segList = aSegList;
|
||||
bool polygonComplete = false;
|
||||
bool selfIntersecting = false;
|
||||
|
||||
wxString msg;
|
||||
PCB_SHAPE* graphic;
|
||||
wxPoint prevPt;
|
||||
wxPoint prevPt;
|
||||
|
||||
std::set<PCB_SHAPE*> startCandidates( aSegList.begin(), aSegList.end() );
|
||||
|
||||
// Find edge point with minimum x, this should be in the outer polygon
|
||||
// which will define the perimeter polygon polygon.
|
||||
wxPoint xmin = wxPoint( INT_MAX, 0 );
|
||||
int xmini = 0;
|
||||
|
||||
for( size_t i = 0; i < segList.size(); i++ )
|
||||
for( size_t i = 0; i < aSegList.size(); i++ )
|
||||
{
|
||||
graphic = (PCB_SHAPE*) segList[i];
|
||||
graphic = (PCB_SHAPE*) aSegList[i];
|
||||
graphic->ClearFlags( SKIP_STRUCT );
|
||||
|
||||
switch( graphic->GetShape() )
|
||||
{
|
||||
|
@ -330,10 +333,10 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
// can put enough graphics together by matching endpoints to formulate a cohesive
|
||||
// polygon.
|
||||
|
||||
graphic = (PCB_SHAPE*) segList[xmini];
|
||||
graphic = (PCB_SHAPE*) aSegList[xmini];
|
||||
|
||||
// The first PCB_SHAPE is in 'graphic', ok to remove it from 'items'
|
||||
segList.erase( segList.begin() + xmini );
|
||||
graphic->SetFlags( SKIP_STRUCT );
|
||||
startCandidates.erase( graphic );
|
||||
|
||||
// Output the outline perimeter as polygon.
|
||||
if( graphic->GetShape() == S_CIRCLE )
|
||||
|
@ -412,6 +415,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
double angle = -graphic->GetAngle();
|
||||
double radius = graphic->GetRadius();
|
||||
int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
|
||||
double delta = angle / steps;
|
||||
|
||||
if( !close_enough( prevPt, pstart, aTolerance ) )
|
||||
{
|
||||
|
@ -421,18 +425,17 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
std::swap( pstart, pend );
|
||||
}
|
||||
|
||||
wxPoint nextPt;
|
||||
|
||||
for( int step = 1; step<=steps; ++step )
|
||||
for( double rotation = delta; rotation < angle; rotation += delta )
|
||||
{
|
||||
double rotation = ( angle * step ) / steps;
|
||||
nextPt = pstart;
|
||||
RotatePoint( &nextPt, pcenter, rotation );
|
||||
wxPoint pt = pstart;
|
||||
RotatePoint( &pt, pcenter, rotation );
|
||||
|
||||
aPolygons.Append( nextPt );
|
||||
aPolygons.Append( pt );
|
||||
}
|
||||
|
||||
prevPt = nextPt;
|
||||
aPolygons.Append( pend );
|
||||
|
||||
prevPt = pend;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -471,51 +474,49 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
break;
|
||||
|
||||
default:
|
||||
if( aErrorText )
|
||||
{
|
||||
msg.Printf( "Unsupported PCB_SHAPE type %s.",
|
||||
BOARD_ITEM::ShowShape( graphic->GetShape() ) );
|
||||
|
||||
*aErrorText << msg << "\n";
|
||||
}
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = graphic->GetPosition();
|
||||
|
||||
wxFAIL_MSG( "Unsupported PCB_SHAPE type "
|
||||
+ BOARD_ITEM::ShowShape( graphic->GetShape() ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get next closest segment.
|
||||
|
||||
graphic = findPoint( prevPt, segList, aTolerance );
|
||||
graphic = findNext( graphic, prevPt, aSegList, aTolerance );
|
||||
|
||||
// If there are no more close segments, check if the board
|
||||
// outline polygon can be closed.
|
||||
|
||||
if( !graphic )
|
||||
if( graphic && !( graphic->GetFlags() & SKIP_STRUCT ) )
|
||||
{
|
||||
if( close_enough( startPt, prevPt, aTolerance ) )
|
||||
graphic->SetFlags( SKIP_STRUCT );
|
||||
startCandidates.erase( graphic );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finished, or ran into trouble...
|
||||
|
||||
if( close_enough( startPt, prevPt, aTolerance ) )
|
||||
{
|
||||
polygonComplete = true;
|
||||
break;
|
||||
}
|
||||
else if( graphic ) // encountered already-used segment, but not at the start
|
||||
{
|
||||
polygonComplete = false;
|
||||
break;
|
||||
}
|
||||
else // encountered discontinuity
|
||||
{
|
||||
if( aErrorText )
|
||||
{
|
||||
// Close the polygon back to start point
|
||||
// aPolygons.Append( startPt ); // not needed
|
||||
msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y ) );
|
||||
|
||||
*aErrorText << msg << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aErrorText )
|
||||
{
|
||||
msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y ) );
|
||||
|
||||
*aErrorText << msg << "\n";
|
||||
}
|
||||
if( aDiscontinuities )
|
||||
aDiscontinuities->emplace_back( prevPt );
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = prevPt;
|
||||
|
||||
polygonComplete = false;
|
||||
break;
|
||||
}
|
||||
polygonComplete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -523,14 +524,14 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
|
||||
int holeNum = -1;
|
||||
|
||||
while( segList.size() )
|
||||
while( startCandidates.size() )
|
||||
{
|
||||
// emit a signal layers keepout for every interior polygon left...
|
||||
int hole = aPolygons.NewHole();
|
||||
holeNum++;
|
||||
|
||||
graphic = (PCB_SHAPE*) segList[0];
|
||||
segList.erase( segList.begin() );
|
||||
graphic = (PCB_SHAPE*) *startCandidates.begin();
|
||||
graphic->SetFlags( SKIP_STRUCT );
|
||||
startCandidates.erase( startCandidates.begin() );
|
||||
|
||||
// Both circles and polygons on the edge cuts layer are closed items that
|
||||
// do not connect to other elements, so we process them independently
|
||||
|
@ -619,6 +620,7 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
double angle = -graphic->GetAngle();
|
||||
int radius = graphic->GetRadius();
|
||||
int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
|
||||
double delta = angle / steps;
|
||||
|
||||
if( !close_enough( prevPt, pstart, aTolerance ) )
|
||||
{
|
||||
|
@ -628,19 +630,17 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
std::swap( pstart, pend );
|
||||
}
|
||||
|
||||
wxPoint nextPt;
|
||||
|
||||
for( int step = 1; step <= steps; ++step )
|
||||
for( double rotation = delta; rotation < angle; rotation += delta )
|
||||
{
|
||||
double rotation = ( angle * step ) / steps;
|
||||
wxPoint pt = pstart;
|
||||
RotatePoint( &pt, pcenter, rotation );
|
||||
|
||||
nextPt = pstart;
|
||||
RotatePoint( &nextPt, pcenter, rotation );
|
||||
|
||||
aPolygons.Append( nextPt, -1, hole );
|
||||
aPolygons.Append( pt, -1, hole );
|
||||
}
|
||||
|
||||
prevPt = nextPt;
|
||||
aPolygons.Append( pend );
|
||||
|
||||
prevPt = pend;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -680,57 +680,58 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
break;
|
||||
|
||||
default:
|
||||
if( aErrorText )
|
||||
{
|
||||
msg.Printf( "Unsupported PCB_SHAPE type %s.",
|
||||
BOARD_ITEM::ShowShape( graphic->GetShape() ) );
|
||||
|
||||
*aErrorText << msg << "\n";
|
||||
}
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = graphic->GetPosition();
|
||||
wxFAIL_MSG( "Unsupported PCB_SHAPE type "
|
||||
+ BOARD_ITEM::ShowShape( graphic->GetShape() ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get next closest segment.
|
||||
|
||||
graphic = findPoint( prevPt, segList, aTolerance );
|
||||
graphic = findNext( graphic, prevPt, aSegList, aTolerance );
|
||||
|
||||
// If there are no more close segments, check if polygon
|
||||
// can be closed.
|
||||
|
||||
if( !graphic )
|
||||
if( graphic && !( graphic->GetFlags() & SKIP_STRUCT ) )
|
||||
{
|
||||
if( close_enough( startPt, prevPt, aTolerance ) )
|
||||
graphic->SetFlags( SKIP_STRUCT );
|
||||
startCandidates.erase( graphic );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finished, or ran into trouble...
|
||||
|
||||
if( close_enough( startPt, prevPt, aTolerance ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if( graphic ) // encountered already-used segment, but not at the start
|
||||
{
|
||||
polygonComplete = false;
|
||||
break;
|
||||
}
|
||||
else // encountered discontinuity
|
||||
{
|
||||
if( aErrorText )
|
||||
{
|
||||
// Close the polygon back to start point
|
||||
// aPolygons.Append( startPt, -1, hole ); // not needed
|
||||
msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y ) );
|
||||
|
||||
*aErrorText << msg << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aErrorText )
|
||||
{
|
||||
msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x ),
|
||||
StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y ) );
|
||||
|
||||
*aErrorText << msg << "\n";
|
||||
}
|
||||
if( aDiscontinuities )
|
||||
aDiscontinuities->emplace_back( prevPt );
|
||||
|
||||
if( aErrorLocation )
|
||||
*aErrorLocation = prevPt;
|
||||
|
||||
aPolygons.Hole( 0, holeNum ).SetClosed( false );
|
||||
polygonComplete = false;
|
||||
}
|
||||
polygonComplete = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !polygonComplete )
|
||||
return false;
|
||||
|
||||
// All of the silliness that follows is to work around the segment iterator
|
||||
// while checking for collisions.
|
||||
// TODO: Implement proper segment and point iterators that follow std
|
||||
|
@ -745,29 +746,23 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
if( *seg1 == *seg2 ||
|
||||
( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
|
||||
{
|
||||
if( aErrorLocation )
|
||||
{
|
||||
aErrorLocation->x = ( *seg1 ).A.x;
|
||||
aErrorLocation->y = ( *seg1 ).A.y;
|
||||
}
|
||||
if( aIntersections )
|
||||
aIntersections->emplace_back( ( *seg1 ).A.x, ( *seg1 ).A.y );
|
||||
|
||||
return false;
|
||||
selfIntersecting = true;
|
||||
}
|
||||
|
||||
if( boost::optional<VECTOR2I> pt = seg1.Get().Intersect( seg2.Get(), true ) )
|
||||
{
|
||||
if( aErrorLocation )
|
||||
{
|
||||
aErrorLocation->x = pt->x;
|
||||
aErrorLocation->y = pt->y;
|
||||
}
|
||||
if( aIntersections )
|
||||
aIntersections->emplace_back( (wxPoint) pt.get() );
|
||||
|
||||
return false;
|
||||
selfIntersecting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return polygonComplete;
|
||||
return !selfIntersecting;
|
||||
}
|
||||
|
||||
#include <class_board.h>
|
||||
|
@ -777,8 +772,9 @@ bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET&
|
|||
* Any closed outline inside the main outline is a hole
|
||||
* All contours should be closed, i.e. valid closed polygon vertices
|
||||
*/
|
||||
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, wxString* aErrorText,
|
||||
unsigned int aTolerance, wxPoint* aErrorLocation )
|
||||
bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, unsigned int aTolerance,
|
||||
wxString* aErrorText, std::vector<wxPoint>* aDiscontinuities,
|
||||
std::vector<wxPoint>* aIntersections )
|
||||
{
|
||||
PCB_TYPE_COLLECTOR items;
|
||||
bool success = false;
|
||||
|
@ -799,8 +795,8 @@ bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, wxStri
|
|||
|
||||
if( segList.size() )
|
||||
{
|
||||
success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance,
|
||||
aErrorLocation );
|
||||
success = ConvertOutlineToPolygon( segList, aOutlines, aTolerance, aErrorText,
|
||||
aDiscontinuities, aIntersections );
|
||||
}
|
||||
else if( aErrorText )
|
||||
{
|
||||
|
@ -997,8 +993,9 @@ int findEndSegments( SHAPE_LINE_CHAIN& aChain, SEG& aStartSeg, SEG& aEndSeg )
|
|||
* as a hole, and the outer edge will be formed using the bounding box.
|
||||
*/
|
||||
bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||
wxString* aErrorText, unsigned int aTolerance,
|
||||
wxPoint* aErrorLocation )
|
||||
unsigned int aTolerance, wxString* aErrorText,
|
||||
std::vector<wxPoint>* aDiscontinuities,
|
||||
std::vector<wxPoint>* aIntersections )
|
||||
{
|
||||
PCB_TYPE_COLLECTOR items;
|
||||
|
||||
|
@ -1018,7 +1015,8 @@ bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
|||
segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
|
||||
}
|
||||
|
||||
bool success = ConvertOutlineToPolygon( segList, outlines, aErrorText, aTolerance, aErrorLocation );
|
||||
bool success = ConvertOutlineToPolygon( segList, outlines, aTolerance, aErrorText,
|
||||
aDiscontinuities, aIntersections );
|
||||
|
||||
MODULE* boardMod = aBoard->GetFirstModule();
|
||||
|
||||
|
|
|
@ -235,8 +235,9 @@ void DIALOG_EXPORT_STEP::onUpdateYPos( wxUpdateUIEvent& aEvent )
|
|||
}
|
||||
|
||||
extern bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines,
|
||||
wxString* aErrorText, unsigned int aTolerance,
|
||||
wxPoint* aErrorLocation = nullptr );
|
||||
unsigned int aTolerance, wxString* aErrorText,
|
||||
std::vector<wxPoint>* aDiscontinuities = nullptr,
|
||||
std::vector<wxPoint>* aIntersections = nullptr );
|
||||
|
||||
void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
|
||||
{
|
||||
|
@ -246,7 +247,7 @@ void DIALOG_EXPORT_STEP::onExportButton( wxCommandEvent& aEvent )
|
|||
wxString msg;
|
||||
|
||||
// Check if the board outline is continuous
|
||||
if( !BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, &msg, Millimeter2iu( 0.01 ) ) )
|
||||
if( !BuildBoardPolygonOutlines( m_parent->GetBoard(), outline, Millimeter2iu( 0.01 ), &msg ) )
|
||||
{
|
||||
DisplayErrorMessage( this, _( "Cannot determine the board outline." ), msg );
|
||||
return;
|
||||
|
|
|
@ -80,21 +80,40 @@ private:
|
|||
|
||||
void DRC_TEST_PROVIDER_MISC::testOutline()
|
||||
{
|
||||
wxPoint error_loc( m_board->GetBoardEdgesBoundingBox().GetPosition() );
|
||||
std::vector<wxPoint> discontinuities;
|
||||
std::vector<wxPoint> intersections;
|
||||
|
||||
SHAPE_POLY_SET boardOutlines;
|
||||
|
||||
if( m_board->GetBoardPolygonOutlines( boardOutlines, nullptr, &error_loc ) )
|
||||
if( m_board->GetBoardPolygonOutlines( boardOutlines, nullptr, &discontinuities,
|
||||
&intersections ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
|
||||
for( wxPoint pt : discontinuities )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(not a closed shape)" ) );
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(not a closed shape)" ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( m_board );
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( m_board );
|
||||
|
||||
reportViolation( drcItem, error_loc );
|
||||
reportViolation( drcItem, pt );
|
||||
}
|
||||
|
||||
for( wxPoint pt : intersections )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_INVALID_OUTLINE );
|
||||
|
||||
m_msg.Printf( drcItem->GetErrorText() + wxS( " " ) + _( "(self-intersecting)" ) );
|
||||
|
||||
drcItem->SetErrorMessage( m_msg );
|
||||
drcItem->SetItems( m_board );
|
||||
|
||||
reportViolation( drcItem, pt );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -293,12 +293,10 @@ void HYPERLYNX_EXPORTER::writeSinglePadStack( HYPERLYNX_PAD_STACK& aStack )
|
|||
bool HYPERLYNX_EXPORTER::writeBoardInfo()
|
||||
{
|
||||
SHAPE_POLY_SET outlines;
|
||||
wxString errText;
|
||||
wxPoint errLoc;
|
||||
|
||||
m_out->Print( 0, "{BOARD \"%s\"\n", (const char*) m_board->GetFileName().c_str() );
|
||||
|
||||
if( !m_board->GetBoardPolygonOutlines( outlines, &errText, &errLoc ) )
|
||||
if( !m_board->GetBoardPolygonOutlines( outlines ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue