From 2637835a1e65dfa4707a108bce2d8db17e6f9e78 Mon Sep 17 00:00:00 2001 From: jean-pierre charras Date: Sun, 19 Mar 2017 16:38:30 +0100 Subject: [PATCH] First version of courtyard overlap detection --- common/CMakeLists.txt | 1 + pcbnew/class_drc_item.cpp | 5 +- pcbnew/class_module.cpp | 72 +-- pcbnew/class_module.h | 10 +- .../convert_drawsegment_list_to_polygon.cpp | 557 ++++++++++++++++++ pcbnew/drc.cpp | 75 ++- pcbnew/drc_stuff.h | 2 + 7 files changed, 654 insertions(+), 68 deletions(-) create mode 100644 pcbnew/convert_drawsegment_list_to_polygon.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 29e25daeec..42e5a50451 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -371,6 +371,7 @@ set( PCB_COMMON_SRCS ../pcbnew/class_zone.cpp ../pcbnew/class_zone_settings.cpp ../pcbnew/classpcb.cpp + ../pcbnew/convert_drawsegment_list_to_polygon.cpp ../pcbnew/ratsnest_data.cpp ../pcbnew/ratsnest_viewitem.cpp ../pcbnew/collectors.cpp diff --git a/pcbnew/class_drc_item.cpp b/pcbnew/class_drc_item.cpp index f9e7714d36..88e3eebf60 100644 --- a/pcbnew/class_drc_item.cpp +++ b/pcbnew/class_drc_item.cpp @@ -130,7 +130,10 @@ wxString DRC_ITEM::GetErrorText() const return wxString( _( "Courtyards overlap" ) ); 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: return wxString::Format( wxT( "Unknown DRC error code %d" ), m_ErrorCode ); diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp index 13fa828d68..f2246b3d53 100644 --- a/pcbnew/class_module.cpp +++ b/pcbnew/class_module.cpp @@ -1221,75 +1221,39 @@ double MODULE::PadCoverageRatio() const 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() { - m_poly_courtyard.RemoveAllContours(); + m_poly_courtyard_front.RemoveAllContours(); + m_poly_courtyard_back.RemoveAllContours(); // Build the courtyard area from graphic items on the courtyard. // Only PCB_MODULE_EDGE_T have meaning, graphic texts are ignored. // 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() ) { 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 ) - list.push_back( static_cast< EDGE_MODULE* > ( item ) ); + list_front.push_back( static_cast< DRAWSEGMENT* > ( item ) ); } - if( !list.size() ) - return false; + // 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; - // Build the coutyard - const int circleToSegmentsCount = 16; - EDA_RECT rect; // the bouding box of segments - bool has_segments = false; + bool success = ConvertOutlineToPolygon( list_front, m_poly_courtyard_front ); - 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() ); + if( success ) + success = ConvertOutlineToPolygon( list_back, m_poly_courtyard_back ); - 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() ); - } - - return true; + return success; } diff --git a/pcbnew/class_module.h b/pcbnew/class_module.h index 2ebfe97d95..a101749b5a 100644 --- a/pcbnew/class_module.h +++ b/pcbnew/class_module.h @@ -643,11 +643,13 @@ public: /** Used in DRC to test the courtyard area (a complex 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) * 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 * cannot be grouped in a polygon. */ @@ -697,7 +699,9 @@ private: ///< lazily allocated only if needed for speed /// 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_ diff --git a/pcbnew/convert_drawsegment_list_to_polygon.cpp b/pcbnew/convert_drawsegment_list_to_polygon.cpp new file mode 100644 index 0000000000..02236085cb --- /dev/null +++ b/pcbnew/convert_drawsegment_list_to_polygon.cpp @@ -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 + * 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 +#include + +#include +#include +#include + + +/** + * 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; stepGetEnd() ); + 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; +} \ No newline at end of file diff --git a/pcbnew/drc.cpp b/pcbnew/drc.cpp index 1b770a8675..86eeb7abaa 100644 --- a/pcbnew/drc.cpp +++ b/pcbnew/drc.cpp @@ -986,16 +986,34 @@ bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_li 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; bool success = true; // Update courtyard polygons, and test for missing courtyard definition: 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" ), footprint->GetReference().GetData() ); @@ -1011,29 +1029,66 @@ bool DRC::doFootprintOverlappingDrc() if( !m_doFootprintOverlapping ) 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() ) { - if( footprint->GetPolyCourtyard().OutlineCount() == 0 ) + if( footprint->GetPolyCourtyardFront().OutlineCount() == 0 ) continue; // No courtyard defined for( MODULE* candidate = footprint->Next(); candidate; candidate = candidate->Next() ) { - if( candidate->GetPolyCourtyard().OutlineCount() == 0 ) + if( candidate->GetPolyCourtyardFront().OutlineCount() == 0 ) continue; // No courtyard defined - SHAPE_POLY_SET courtyard; - courtyard.Append( footprint->GetPolyCourtyard() ); + courtyard.RemoveAllContours(); + courtyard.Append( footprint->GetPolyCourtyardFront() ); // 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). // 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" ), + 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(), candidate->GetReference().GetData() ); VECTOR2I& pos = courtyard.Vertex( 0, 0 ); diff --git a/pcbnew/drc_stuff.h b/pcbnew/drc_stuff.h index f00e532d9c..e8db31cc3e 100644 --- a/pcbnew/drc_stuff.h +++ b/pcbnew/drc_stuff.h @@ -82,6 +82,8 @@ #define DRCE_PAD_INSIDE_TEXT 43 ///< Pad in inside a text area #define DRCE_OVERLAPPING_FOOTPRINTS 44 ///< footprint courtyards overlap #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;