First version of courtyard overlap detection
This commit is contained in:
parent
b365f17e9f
commit
2637835a1e
|
@ -371,6 +371,7 @@ set( PCB_COMMON_SRCS
|
||||||
../pcbnew/class_zone.cpp
|
../pcbnew/class_zone.cpp
|
||||||
../pcbnew/class_zone_settings.cpp
|
../pcbnew/class_zone_settings.cpp
|
||||||
../pcbnew/classpcb.cpp
|
../pcbnew/classpcb.cpp
|
||||||
|
../pcbnew/convert_drawsegment_list_to_polygon.cpp
|
||||||
../pcbnew/ratsnest_data.cpp
|
../pcbnew/ratsnest_data.cpp
|
||||||
../pcbnew/ratsnest_viewitem.cpp
|
../pcbnew/ratsnest_viewitem.cpp
|
||||||
../pcbnew/collectors.cpp
|
../pcbnew/collectors.cpp
|
||||||
|
|
|
@ -130,7 +130,10 @@ wxString DRC_ITEM::GetErrorText() const
|
||||||
return wxString( _( "Courtyards overlap" ) );
|
return wxString( _( "Courtyards overlap" ) );
|
||||||
|
|
||||||
case DRCE_MISSING_COURTYARD_IN_FOOTPRINT:
|
case DRCE_MISSING_COURTYARD_IN_FOOTPRINT:
|
||||||
return wxString( _( "Footprint has no courtard defined" ) );
|
return wxString( _( "Footprint has no courtyard defined" ) );
|
||||||
|
|
||||||
|
case DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT:
|
||||||
|
return wxString( _( "Footprint has incorect courtyard (not a closed shape)" ) );
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return wxString::Format( wxT( "Unknown DRC error code %d" ), m_ErrorCode );
|
return wxString::Format( wxT( "Unknown DRC error code %d" ), m_ErrorCode );
|
||||||
|
|
|
@ -1221,75 +1221,39 @@ double MODULE::PadCoverageRatio() const
|
||||||
return std::min( ratio, 1.0 );
|
return std::min( ratio, 1.0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see convert_drawsegment_list_to_polygon.cpp:
|
||||||
|
extern bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList,
|
||||||
|
SHAPE_POLY_SET& aPolygons);
|
||||||
|
|
||||||
bool MODULE::BuildPolyCourtyard()
|
bool MODULE::BuildPolyCourtyard()
|
||||||
{
|
{
|
||||||
m_poly_courtyard.RemoveAllContours();
|
m_poly_courtyard_front.RemoveAllContours();
|
||||||
|
m_poly_courtyard_back.RemoveAllContours();
|
||||||
// Build the courtyard area from graphic items on the courtyard.
|
// Build the courtyard area from graphic items on the courtyard.
|
||||||
// Only PCB_MODULE_EDGE_T have meaning, graphic texts are ignored.
|
// Only PCB_MODULE_EDGE_T have meaning, graphic texts are ignored.
|
||||||
// Collect items:
|
// Collect items:
|
||||||
std::vector< EDGE_MODULE* > list;
|
std::vector< DRAWSEGMENT* > list_front;
|
||||||
|
std::vector< DRAWSEGMENT* > list_back;
|
||||||
|
|
||||||
for( BOARD_ITEM* item = GraphicalItems(); item; item = item->Next() )
|
for( BOARD_ITEM* item = GraphicalItems(); item; item = item->Next() )
|
||||||
{
|
{
|
||||||
if( item->GetLayer() == B_CrtYd && item->Type() == PCB_MODULE_EDGE_T )
|
if( item->GetLayer() == B_CrtYd && item->Type() == PCB_MODULE_EDGE_T )
|
||||||
list.push_back( static_cast< EDGE_MODULE* > ( item ) );
|
list_back.push_back( static_cast< DRAWSEGMENT* > ( item ) );
|
||||||
|
|
||||||
if( item->GetLayer() == F_CrtYd && item->Type() == PCB_MODULE_EDGE_T )
|
if( item->GetLayer() == F_CrtYd && item->Type() == PCB_MODULE_EDGE_T )
|
||||||
list.push_back( static_cast< EDGE_MODULE* > ( item ) );
|
list_front.push_back( static_cast< DRAWSEGMENT* > ( item ) );
|
||||||
}
|
|
||||||
|
|
||||||
if( !list.size() )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Build the coutyard
|
|
||||||
const int circleToSegmentsCount = 16;
|
|
||||||
EDA_RECT rect; // the bouding box of segments
|
|
||||||
bool has_segments = false;
|
|
||||||
|
|
||||||
for( EDGE_MODULE* item : list )
|
|
||||||
{
|
|
||||||
switch( item->GetShape() )
|
|
||||||
{
|
|
||||||
case S_SEGMENT:
|
|
||||||
if( !has_segments )
|
|
||||||
{
|
|
||||||
rect.Move( item->GetStart() );
|
|
||||||
has_segments = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
rect.Merge( item->GetStart() );
|
|
||||||
|
|
||||||
rect.Merge( item->GetEnd() );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_CIRCLE:
|
|
||||||
TransformCircleToPolygon( m_poly_courtyard, item->GetCenter(),
|
|
||||||
item->GetRadius(), circleToSegmentsCount );
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_ARC:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case S_POLYGON:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( has_segments ) // Build the polygon from bounding box
|
|
||||||
{
|
|
||||||
m_poly_courtyard.NewOutline();
|
|
||||||
m_poly_courtyard.Append( rect.GetOrigin().x, rect.GetOrigin().y );
|
|
||||||
m_poly_courtyard.Append( rect.GetOrigin().x + rect.GetWidth(),
|
|
||||||
rect.GetOrigin().y );
|
|
||||||
m_poly_courtyard.Append( rect.GetOrigin().x + rect.GetWidth(),
|
|
||||||
rect.GetOrigin().y + rect.GetHeight() );
|
|
||||||
m_poly_courtyard.Append( rect.GetOrigin().x,
|
|
||||||
rect.GetOrigin().y + rect.GetHeight() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: if no item found on courtyard layers, return true.
|
||||||
|
// false is returned only when the shape defined on courtyard layers
|
||||||
|
// is not convertible to a polygon
|
||||||
|
if( !list_front.size() && !list_back.size() )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
bool success = ConvertOutlineToPolygon( list_front, m_poly_courtyard_front );
|
||||||
|
|
||||||
|
if( success )
|
||||||
|
success = ConvertOutlineToPolygon( list_back, m_poly_courtyard_back );
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -643,11 +643,13 @@ public:
|
||||||
/** Used in DRC to test the courtyard area (a complex polygon)
|
/** Used in DRC to test the courtyard area (a complex polygon)
|
||||||
* @return the courtyard polygon
|
* @return the courtyard polygon
|
||||||
*/
|
*/
|
||||||
SHAPE_POLY_SET& GetPolyCourtyard() { return m_poly_courtyard; }
|
SHAPE_POLY_SET& GetPolyCourtyardFront() { return m_poly_courtyard_front; }
|
||||||
|
SHAPE_POLY_SET& GetPolyCourtyardBack() { return m_poly_courtyard_back; }
|
||||||
|
|
||||||
/** Used in DRC to build the courtyard area (a complex polygon)
|
/** Used in DRC to build the courtyard area (a complex polygon)
|
||||||
* from graphic items put on the courtyard
|
* from graphic items put on the courtyard
|
||||||
* @return true if OK, false if the polygon cannot be built
|
* @return true if OK, or no courtyard defined,
|
||||||
|
* false only if the polygon cannot be built due to amalformed courtyard shape
|
||||||
* The polygon cannot be built if segments/arcs on courtyard layers
|
* The polygon cannot be built if segments/arcs on courtyard layers
|
||||||
* cannot be grouped in a polygon.
|
* cannot be grouped in a polygon.
|
||||||
*/
|
*/
|
||||||
|
@ -697,7 +699,9 @@ private:
|
||||||
///< lazily allocated only if needed for speed
|
///< lazily allocated only if needed for speed
|
||||||
|
|
||||||
/// Used in DRC to test the courtyard area (a polygon which can be not basic
|
/// Used in DRC to test the courtyard area (a polygon which can be not basic
|
||||||
SHAPE_POLY_SET m_poly_courtyard;
|
/// Note also a footprint can have courtyards on bot board sides
|
||||||
|
SHAPE_POLY_SET m_poly_courtyard_front;
|
||||||
|
SHAPE_POLY_SET m_poly_courtyard_back;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MODULE_H_
|
#endif // MODULE_H_
|
||||||
|
|
|
@ -0,0 +1,557 @@
|
||||||
|
/*
|
||||||
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
||||||
|
* Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
|
||||||
|
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, you may find one here:
|
||||||
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||||
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||||
|
* or you may write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file convert_drawsegment_list_to_polygon.cpp
|
||||||
|
* @brief functions to convert a shape built with DRAWSEGMENTS to a polygon.
|
||||||
|
* expecting the shape describes shape similar to a polygon
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <trigo.h>
|
||||||
|
#include <macros.h>
|
||||||
|
|
||||||
|
#include <class_drawsegment.h>
|
||||||
|
#include <base_units.h>
|
||||||
|
#include <convert_basic_shapes_to_polygon.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function close_ness
|
||||||
|
* is a non-exact distance (also called Manhattan distance) used to approximate
|
||||||
|
* the distance between two points.
|
||||||
|
* The distance is very in-exact, but can be helpful when used
|
||||||
|
* to pick between alternative neighboring points.
|
||||||
|
* @param aLeft is the first point
|
||||||
|
* @param aRight is the second point
|
||||||
|
* @return unsigned - a measure of proximity that the caller knows about, in BIU,
|
||||||
|
* but remember it is only an approximation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static unsigned close_ness( const wxPoint& aLeft, const wxPoint& aRight )
|
||||||
|
{
|
||||||
|
// Don't need an accurate distance calculation, just something
|
||||||
|
// approximating it, for relative ordering.
|
||||||
|
return unsigned( std::abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function close_enough
|
||||||
|
* is a local and tunable method of qualifying the proximity of two points.
|
||||||
|
*
|
||||||
|
* @param aLeft is the first point
|
||||||
|
* @param aRight is the second point
|
||||||
|
* @param aLimit is a measure of proximity that the caller knows about.
|
||||||
|
* @return bool - true if the two points are close enough, else false.
|
||||||
|
*/
|
||||||
|
inline bool close_enough( const wxPoint& aLeft, const wxPoint& aRight, unsigned aLimit )
|
||||||
|
{
|
||||||
|
// We don't use an accurate distance calculation, just something
|
||||||
|
// approximating it, since aLimit is non-exact anyway except when zero.
|
||||||
|
return close_ness( aLeft, aRight ) <= aLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function close_st
|
||||||
|
* is a local method of qualifying if either the start of end point of a segment is closest to a point.
|
||||||
|
*
|
||||||
|
* @param aReference is the reference point
|
||||||
|
* @param aFirst is the first point
|
||||||
|
* @param aSecond is the second point
|
||||||
|
* @return bool - true if the the first point is closest to the reference, otherwise false.
|
||||||
|
*/
|
||||||
|
inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wxPoint& aSecond )
|
||||||
|
{
|
||||||
|
// We don't use an accurate distance calculation, just something
|
||||||
|
// approximating to find the closest to the reference.
|
||||||
|
return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function findPoint
|
||||||
|
* searches for a DRAWSEGMENT with an end point or start point of aPoint, and
|
||||||
|
* if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.
|
||||||
|
* @param aPoint The starting or ending point to search for.
|
||||||
|
* @param items The list to remove from.
|
||||||
|
* @param aLimit is the distance from \a aPoint that still constitutes a valid find.
|
||||||
|
* @return DRAWSEGMENT* - The first DRAWSEGMENT that has a start or end point matching
|
||||||
|
* aPoint, otherwise NULL if none.
|
||||||
|
*/
|
||||||
|
static DRAWSEGMENT* findPoint( const wxPoint& aPoint, std::vector< DRAWSEGMENT* >& aList, unsigned aLimit )
|
||||||
|
{
|
||||||
|
unsigned min_d = INT_MAX;
|
||||||
|
int ndx_min = 0;
|
||||||
|
|
||||||
|
// find the point closest to aPoint and perhaps exactly matching aPoint.
|
||||||
|
for( size_t i = 0; i < aList.size(); ++i )
|
||||||
|
{
|
||||||
|
DRAWSEGMENT* graphic = aList[i];
|
||||||
|
unsigned d;
|
||||||
|
|
||||||
|
switch( graphic->GetShape() )
|
||||||
|
{
|
||||||
|
case S_ARC:
|
||||||
|
if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
|
||||||
|
{
|
||||||
|
aList.erase( aList.begin() + i );
|
||||||
|
return graphic;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = close_ness( aPoint, graphic->GetArcStart() );
|
||||||
|
if( d < min_d )
|
||||||
|
{
|
||||||
|
min_d = d;
|
||||||
|
ndx_min = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = close_ness( aPoint, graphic->GetArcEnd() );
|
||||||
|
if( d < min_d )
|
||||||
|
{
|
||||||
|
min_d = d;
|
||||||
|
ndx_min = i;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
|
||||||
|
{
|
||||||
|
aList.erase( aList.begin() + i );
|
||||||
|
return graphic;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = close_ness( aPoint, graphic->GetStart() );
|
||||||
|
if( d < min_d )
|
||||||
|
{
|
||||||
|
min_d = d;
|
||||||
|
ndx_min = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = close_ness( aPoint, graphic->GetEnd() );
|
||||||
|
if( d < min_d )
|
||||||
|
{
|
||||||
|
min_d = d;
|
||||||
|
ndx_min = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( min_d <= aLimit )
|
||||||
|
{
|
||||||
|
DRAWSEGMENT* graphic = aList[ndx_min];
|
||||||
|
aList.erase( aList.begin() + ndx_min );
|
||||||
|
return graphic;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function ConvertOutlineToPolygon
|
||||||
|
* build a polygon (with holes) from a DRAWSEGMENT list, which is expected to be
|
||||||
|
* a outline, therefore a closed main outline with perhaps closed inner outlines.
|
||||||
|
* These closed inner outlines are considered as holes in the main outline
|
||||||
|
* @param aSegList the initial list of drawsegments (only lines, circles and arcs).
|
||||||
|
* @param aPolygons will contain the complex polygon.
|
||||||
|
*/
|
||||||
|
bool ConvertOutlineToPolygon( std::vector< DRAWSEGMENT* >& aSegList, SHAPE_POLY_SET& aPolygons)
|
||||||
|
{
|
||||||
|
|
||||||
|
if( aSegList.size() == 0 )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Make a working copy of aSegList, because the list is modified during calculations
|
||||||
|
std::vector< DRAWSEGMENT* > segList = aSegList;
|
||||||
|
|
||||||
|
unsigned prox; // a proximity BIU metric, not an accurate distance
|
||||||
|
const int STEPS = 16; // for a segmentation of an arc of 360 degrees
|
||||||
|
DRAWSEGMENT* graphic;
|
||||||
|
wxPoint prevPt;
|
||||||
|
|
||||||
|
// Find edge point with minimum x, this should be in the outer polygon
|
||||||
|
// which will define the perimeter Edge.Cuts polygon.
|
||||||
|
wxPoint xmin = wxPoint( INT_MAX, 0 );
|
||||||
|
int xmini = 0;
|
||||||
|
|
||||||
|
for( size_t i = 0; i < segList.size(); i++ )
|
||||||
|
{
|
||||||
|
graphic = (DRAWSEGMENT*) segList[i];
|
||||||
|
|
||||||
|
switch( graphic->GetShape() )
|
||||||
|
{
|
||||||
|
case S_SEGMENT:
|
||||||
|
{
|
||||||
|
if( graphic->GetStart().x < xmin.x )
|
||||||
|
{
|
||||||
|
xmin = graphic->GetStart();
|
||||||
|
xmini = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( graphic->GetEnd().x < xmin.x )
|
||||||
|
{
|
||||||
|
xmin = graphic->GetEnd();
|
||||||
|
xmini = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_ARC:
|
||||||
|
// Freerouter does not yet understand arcs, so approximate
|
||||||
|
// an arc with a series of short lines and put those
|
||||||
|
// line segments into the !same! PATH.
|
||||||
|
{
|
||||||
|
wxPoint pstart = graphic->GetArcStart();
|
||||||
|
wxPoint center = graphic->GetCenter();
|
||||||
|
double angle = -graphic->GetAngle();
|
||||||
|
int steps = STEPS * fabs(angle) /3600.0;
|
||||||
|
|
||||||
|
if( steps == 0 )
|
||||||
|
steps = 1;
|
||||||
|
|
||||||
|
wxPoint pt;
|
||||||
|
|
||||||
|
for( int step = 1; step<=steps; ++step )
|
||||||
|
{
|
||||||
|
double rotation = ( angle * step ) / steps;
|
||||||
|
|
||||||
|
pt = pstart;
|
||||||
|
|
||||||
|
RotatePoint( &pt, center, rotation );
|
||||||
|
|
||||||
|
if( pt.x < xmin.x )
|
||||||
|
{
|
||||||
|
xmin = pt;
|
||||||
|
xmini = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_CIRCLE:
|
||||||
|
{
|
||||||
|
wxPoint pt = graphic->GetCenter();
|
||||||
|
|
||||||
|
// pt has minimum x point
|
||||||
|
pt.x -= graphic->GetRadius();
|
||||||
|
|
||||||
|
// when the radius <= 0, this is a mal-formed circle. Skip it
|
||||||
|
if( graphic->GetRadius() > 0 && pt.x < xmin.x )
|
||||||
|
{
|
||||||
|
xmin = pt;
|
||||||
|
xmini = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the left most point, assume its on the board's perimeter, and see if we
|
||||||
|
// can put enough graphics together by matching endpoints to formulate a cohesive
|
||||||
|
// polygon.
|
||||||
|
|
||||||
|
graphic = (DRAWSEGMENT*) segList[xmini];
|
||||||
|
|
||||||
|
// The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items'
|
||||||
|
segList.erase( segList.begin() + xmini );
|
||||||
|
|
||||||
|
// Set maximum proximity threshold for point to point nearness metric for
|
||||||
|
// board perimeter only, not interior keepouts yet.
|
||||||
|
prox = Millimeter2iu( 0.01 ); // should be enough to fix rounding issues
|
||||||
|
// is arc start and end point calculations
|
||||||
|
|
||||||
|
// Output the Edge.Cuts perimeter as circle or polygon.
|
||||||
|
if( graphic->GetShape() == S_CIRCLE )
|
||||||
|
{
|
||||||
|
TransformCircleToPolygon( aPolygons, graphic->GetCenter(),
|
||||||
|
graphic->GetRadius(), STEPS );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Polygon start point. Arbitrarily chosen end of the
|
||||||
|
// segment and build the poly from here.
|
||||||
|
|
||||||
|
wxPoint startPt = wxPoint( graphic->GetEnd() );
|
||||||
|
prevPt = graphic->GetEnd();
|
||||||
|
aPolygons.NewOutline();
|
||||||
|
aPolygons.Append( prevPt );
|
||||||
|
|
||||||
|
// Do not append the other end point yet of this 'graphic', this first
|
||||||
|
// 'graphic' might be an arc.
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
switch( graphic->GetShape() )
|
||||||
|
{
|
||||||
|
case S_SEGMENT:
|
||||||
|
{
|
||||||
|
wxPoint nextPt;
|
||||||
|
|
||||||
|
// Use the line segment end point furthest away from
|
||||||
|
// prevPt as we assume the other end to be ON prevPt or
|
||||||
|
// very close to it.
|
||||||
|
|
||||||
|
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
|
||||||
|
{
|
||||||
|
nextPt = graphic->GetEnd();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextPt = graphic->GetStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
aPolygons.Append( nextPt );
|
||||||
|
prevPt = nextPt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_ARC:
|
||||||
|
// We do not support arcs in polygons, so approximate
|
||||||
|
// an arc with a series of short lines and put those
|
||||||
|
// line segments into the !same! PATH.
|
||||||
|
{
|
||||||
|
wxPoint pstart = graphic->GetArcStart();
|
||||||
|
wxPoint pend = graphic->GetArcEnd();
|
||||||
|
wxPoint pcenter = graphic->GetCenter();
|
||||||
|
double angle = -graphic->GetAngle();
|
||||||
|
int steps = STEPS * fabs(angle) /3600.0;
|
||||||
|
|
||||||
|
if( steps == 0 )
|
||||||
|
steps = 1;
|
||||||
|
|
||||||
|
if( !close_enough( prevPt, pstart, prox ) )
|
||||||
|
{
|
||||||
|
wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), prox ) );
|
||||||
|
|
||||||
|
angle = -angle;
|
||||||
|
std::swap( pstart, pend );
|
||||||
|
}
|
||||||
|
|
||||||
|
wxPoint nextPt;
|
||||||
|
|
||||||
|
for( int step = 1; step<=steps; ++step )
|
||||||
|
{
|
||||||
|
double rotation = ( angle * step ) / steps;
|
||||||
|
nextPt = pstart;
|
||||||
|
RotatePoint( &nextPt, pcenter, rotation );
|
||||||
|
|
||||||
|
aPolygons.Append( nextPt );
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPt = nextPt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
wxLogMessage( _( "Unsupported DRAWSEGMENT type %s" ),
|
||||||
|
GetChars( BOARD_ITEM::ShowShape( graphic->GetShape() ) ) );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get next closest segment.
|
||||||
|
|
||||||
|
graphic = findPoint( prevPt, segList, prox );
|
||||||
|
|
||||||
|
// If there are no more close segments, check if the board
|
||||||
|
// outline polygon can be closed.
|
||||||
|
|
||||||
|
if( !graphic )
|
||||||
|
{
|
||||||
|
if( close_enough( startPt, prevPt, prox ) )
|
||||||
|
{
|
||||||
|
// Close the polygon back to start point
|
||||||
|
// aPolygons.Append( startPt ); // not needed
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxLogMessage(
|
||||||
|
_( "Unable to find the next boundary segment with an endpoint of (%s mm, %s mm). "
|
||||||
|
"graphic outline must form a contiguous, closed polygon." ),
|
||||||
|
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ),
|
||||||
|
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output the interior Edge.Cuts graphics as keepouts, using same nearness
|
||||||
|
// metric as the board edge as otherwise we have trouble completing complex
|
||||||
|
// polygons.
|
||||||
|
prox = Millimeter2iu( 0.05 );
|
||||||
|
|
||||||
|
while( segList.size() )
|
||||||
|
{
|
||||||
|
// emit a signal layers keepout for every interior polygon left...
|
||||||
|
int hole = aPolygons.NewHole();
|
||||||
|
|
||||||
|
graphic = (DRAWSEGMENT*) segList[0];
|
||||||
|
segList.erase( segList.begin() );
|
||||||
|
|
||||||
|
if( graphic->GetShape() == S_CIRCLE )
|
||||||
|
{
|
||||||
|
// make a circle by segments;
|
||||||
|
wxPoint center = graphic->GetCenter();
|
||||||
|
double angle = 3600.0;
|
||||||
|
wxPoint start = center;
|
||||||
|
int radius = graphic->GetRadius();
|
||||||
|
start.x += radius;
|
||||||
|
|
||||||
|
wxPoint nextPt;
|
||||||
|
|
||||||
|
for( int step = 0; step<STEPS; ++step )
|
||||||
|
{
|
||||||
|
double rotation = ( angle * step ) / STEPS;
|
||||||
|
nextPt = start;
|
||||||
|
RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
|
||||||
|
aPolygons.Append( nextPt, -1, hole );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Polygon start point. Arbitrarily chosen end of the
|
||||||
|
// segment and build the poly from here.
|
||||||
|
|
||||||
|
wxPoint startPt( graphic->GetEnd() );
|
||||||
|
prevPt = graphic->GetEnd();
|
||||||
|
aPolygons.Append( prevPt, -1, hole );
|
||||||
|
|
||||||
|
// do not append the other end point yet, this first 'graphic' might be an arc
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
switch( graphic->GetShape() )
|
||||||
|
{
|
||||||
|
case S_SEGMENT:
|
||||||
|
{
|
||||||
|
wxPoint nextPt;
|
||||||
|
|
||||||
|
// Use the line segment end point furthest away from
|
||||||
|
// prevPt as we assume the other end to be ON prevPt or
|
||||||
|
// very close to it.
|
||||||
|
|
||||||
|
if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
|
||||||
|
{
|
||||||
|
nextPt = graphic->GetEnd();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextPt = graphic->GetStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPt = nextPt;
|
||||||
|
aPolygons.Append( prevPt, -1, hole );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case S_ARC:
|
||||||
|
// Freerouter does not yet understand arcs, so approximate
|
||||||
|
// an arc with a series of short lines and put those
|
||||||
|
// line segments into the !same! PATH.
|
||||||
|
{
|
||||||
|
wxPoint pstart = graphic->GetArcStart();
|
||||||
|
wxPoint pend = graphic->GetArcEnd();
|
||||||
|
wxPoint pcenter = graphic->GetCenter();
|
||||||
|
double angle = -graphic->GetAngle();
|
||||||
|
int steps = STEPS * fabs(angle) /3600.0;
|
||||||
|
|
||||||
|
if( steps == 0 )
|
||||||
|
steps = 1;
|
||||||
|
|
||||||
|
if( !close_enough( prevPt, pstart, prox ) )
|
||||||
|
{
|
||||||
|
wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), prox ) );
|
||||||
|
|
||||||
|
angle = -angle;
|
||||||
|
std::swap( pstart, pend );
|
||||||
|
}
|
||||||
|
|
||||||
|
wxPoint nextPt;
|
||||||
|
|
||||||
|
for( int step = 1; step<=steps; ++step )
|
||||||
|
{
|
||||||
|
double rotation = ( angle * step ) / steps;
|
||||||
|
|
||||||
|
nextPt = pstart;
|
||||||
|
RotatePoint( &nextPt, pcenter, rotation );
|
||||||
|
|
||||||
|
aPolygons.Append( nextPt, -1, hole );
|
||||||
|
}
|
||||||
|
|
||||||
|
prevPt = nextPt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
wxLogMessage(
|
||||||
|
_( "Unsupported DRAWSEGMENT type %s" ),
|
||||||
|
GetChars( BOARD_ITEM::ShowShape( graphic->GetShape() ) ) );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get next closest segment.
|
||||||
|
|
||||||
|
graphic = findPoint( prevPt, segList, prox );
|
||||||
|
|
||||||
|
// If there are no more close segments, check if polygon
|
||||||
|
// can be closed.
|
||||||
|
|
||||||
|
if( !graphic )
|
||||||
|
{
|
||||||
|
if( close_enough( startPt, prevPt, prox ) )
|
||||||
|
{
|
||||||
|
// Close the polygon back to start point
|
||||||
|
// aPolygons.Append( startPt, -1, hole ); // not needed
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wxLogMessage(
|
||||||
|
_( "Unable to find the next graphic segment with an endpoint of (%s mm, %s mm).\n"
|
||||||
|
"Edit graphics, making them contiguous polygons each." ),
|
||||||
|
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ),
|
||||||
|
GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -986,16 +986,34 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li
|
||||||
|
|
||||||
bool DRC::doFootprintOverlappingDrc()
|
bool DRC::doFootprintOverlappingDrc()
|
||||||
{
|
{
|
||||||
// Detects missing footprint courtyards, and for others, courtyard overlap.
|
// Detects missing (or malformed) footprint courtyard,
|
||||||
|
// and for footprint with courtyard, courtyards overlap.
|
||||||
wxString msg;
|
wxString msg;
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
// Update courtyard polygons, and test for missing courtyard definition:
|
// Update courtyard polygons, and test for missing courtyard definition:
|
||||||
for( MODULE* footprint = m_pcb->m_Modules; footprint; footprint = footprint->Next() )
|
for( MODULE* footprint = m_pcb->m_Modules; footprint; footprint = footprint->Next() )
|
||||||
{
|
{
|
||||||
footprint->BuildPolyCourtyard();
|
bool diag = footprint->BuildPolyCourtyard();
|
||||||
|
|
||||||
if( footprint->GetPolyCourtyard().OutlineCount() == 0 && m_doNoCourtyardDefined )
|
if( !diag && m_doFootprintOverlapping )
|
||||||
|
{
|
||||||
|
msg.Printf( _( "footprint '%s' has malformed courtyard" ),
|
||||||
|
footprint->GetReference().GetData() );
|
||||||
|
m_currentMarker = fillMarker( footprint->GetPosition(),
|
||||||
|
DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT,
|
||||||
|
msg, m_currentMarker );
|
||||||
|
addMarkerToPcb( m_currentMarker );
|
||||||
|
m_currentMarker = nullptr;
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !m_doNoCourtyardDefined )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 &&
|
||||||
|
footprint->GetPolyCourtyardBack().OutlineCount() == 0 &&
|
||||||
|
!diag )
|
||||||
{
|
{
|
||||||
msg.Printf( _( "footprint '%s' has no courtyard defined" ),
|
msg.Printf( _( "footprint '%s' has no courtyard defined" ),
|
||||||
footprint->GetReference().GetData() );
|
footprint->GetReference().GetData() );
|
||||||
|
@ -1011,29 +1029,66 @@ bool DRC::doFootprintOverlappingDrc()
|
||||||
if( !m_doFootprintOverlapping )
|
if( !m_doFootprintOverlapping )
|
||||||
return success;
|
return success;
|
||||||
|
|
||||||
// Now test for overlapping:
|
// Now test for overlapping on top layer:
|
||||||
|
SHAPE_POLY_SET courtyard; // temporary storage of the courtyard of current footprint
|
||||||
|
|
||||||
for( MODULE* footprint = m_pcb->m_Modules; footprint; footprint = footprint->Next() )
|
for( MODULE* footprint = m_pcb->m_Modules; footprint; footprint = footprint->Next() )
|
||||||
{
|
{
|
||||||
if( footprint->GetPolyCourtyard().OutlineCount() == 0 )
|
if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 )
|
||||||
continue; // No courtyard defined
|
continue; // No courtyard defined
|
||||||
|
|
||||||
for( MODULE* candidate = footprint->Next(); candidate; candidate = candidate->Next() )
|
for( MODULE* candidate = footprint->Next(); candidate; candidate = candidate->Next() )
|
||||||
{
|
{
|
||||||
if( candidate->GetPolyCourtyard().OutlineCount() == 0 )
|
if( candidate->GetPolyCourtyardFront().OutlineCount() == 0 )
|
||||||
continue; // No courtyard defined
|
continue; // No courtyard defined
|
||||||
|
|
||||||
SHAPE_POLY_SET courtyard;
|
courtyard.RemoveAllContours();
|
||||||
courtyard.Append( footprint->GetPolyCourtyard() );
|
courtyard.Append( footprint->GetPolyCourtyardFront() );
|
||||||
|
|
||||||
// Build the common area between footprint and the candidate:
|
// Build the common area between footprint and the candidate:
|
||||||
courtyard.BooleanIntersection( candidate->GetPolyCourtyard(), SHAPE_POLY_SET::PM_FAST );
|
courtyard.BooleanIntersection( candidate->GetPolyCourtyardFront(), SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
// If no overlap, courtyard is empty (no common area).
|
// If no overlap, courtyard is empty (no common area).
|
||||||
// Therefore if a common polygon exists, this is a DRC error
|
// Therefore if a common polygon exists, this is a DRC error
|
||||||
if( courtyard.OutlineCount() )
|
if( courtyard.OutlineCount() )
|
||||||
{
|
{
|
||||||
//Overlap between footprint and candidate
|
//Overlap between footprint and candidate
|
||||||
msg.Printf( _( "footprints '%s' and '%s' overlap" ),
|
msg.Printf( _( "footprints '%s' and '%s' overlap on front (top) layer" ),
|
||||||
|
footprint->GetReference().GetData(),
|
||||||
|
candidate->GetReference().GetData() );
|
||||||
|
VECTOR2I& pos = courtyard.Vertex( 0, 0 );
|
||||||
|
wxPoint loc( pos.x, pos.y );
|
||||||
|
m_currentMarker = fillMarker( loc, DRCE_OVERLAPPING_FOOTPRINTS, msg, m_currentMarker );
|
||||||
|
addMarkerToPcb( m_currentMarker );
|
||||||
|
m_currentMarker = nullptr;
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for overlapping on bottom layer:
|
||||||
|
for( MODULE* footprint = m_pcb->m_Modules; footprint; footprint = footprint->Next() )
|
||||||
|
{
|
||||||
|
if( footprint->GetPolyCourtyardBack().OutlineCount() == 0 )
|
||||||
|
continue; // No courtyard defined
|
||||||
|
|
||||||
|
for( MODULE* candidate = footprint->Next(); candidate; candidate = candidate->Next() )
|
||||||
|
{
|
||||||
|
if( candidate->GetPolyCourtyardBack().OutlineCount() == 0 )
|
||||||
|
continue; // No courtyard defined
|
||||||
|
|
||||||
|
courtyard.RemoveAllContours();
|
||||||
|
courtyard.Append( footprint->GetPolyCourtyardBack() );
|
||||||
|
|
||||||
|
// Build the common area between footprint and the candidate:
|
||||||
|
courtyard.BooleanIntersection( candidate->GetPolyCourtyardBack(), SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
|
// If no overlap, courtyard is empty (no common area).
|
||||||
|
// Therefore if a common polygon exists, this is a DRC error
|
||||||
|
if( courtyard.OutlineCount() )
|
||||||
|
{
|
||||||
|
//Overlap between footprint and candidate
|
||||||
|
msg.Printf( _( "footprints '%s' and '%s' overlap on back (bottom) layer" ),
|
||||||
footprint->GetReference().GetData(),
|
footprint->GetReference().GetData(),
|
||||||
candidate->GetReference().GetData() );
|
candidate->GetReference().GetData() );
|
||||||
VECTOR2I& pos = courtyard.Vertex( 0, 0 );
|
VECTOR2I& pos = courtyard.Vertex( 0, 0 );
|
||||||
|
|
|
@ -82,6 +82,8 @@
|
||||||
#define DRCE_PAD_INSIDE_TEXT 43 ///< Pad in inside a text area
|
#define DRCE_PAD_INSIDE_TEXT 43 ///< Pad in inside a text area
|
||||||
#define DRCE_OVERLAPPING_FOOTPRINTS 44 ///< footprint courtyards overlap
|
#define DRCE_OVERLAPPING_FOOTPRINTS 44 ///< footprint courtyards overlap
|
||||||
#define DRCE_MISSING_COURTYARD_IN_FOOTPRINT 45 ///< footprint has no courtyard defined
|
#define DRCE_MISSING_COURTYARD_IN_FOOTPRINT 45 ///< footprint has no courtyard defined
|
||||||
|
#define DRCE_MALFORMED_COURTYARD_IN_FOOTPRINT 46 ///< footprint has a courtyard but malformed
|
||||||
|
///< (not convetrible to polygon)
|
||||||
|
|
||||||
|
|
||||||
class EDA_DRAW_PANEL;
|
class EDA_DRAW_PANEL;
|
||||||
|
|
Loading…
Reference in New Issue