2010-09-20 16:21:47 +00:00
|
|
|
/*
|
2011-09-30 18:15:37 +00:00
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
2010-09-20 16:21:47 +00:00
|
|
|
*
|
2019-06-06 11:10:24 +00:00
|
|
|
* Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
2010-09-20 16:21:47 +00:00
|
|
|
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
|
2019-06-04 10:45:43 +00:00
|
|
|
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
|
2010-09-20 16:21:47 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <fctsys.h>
|
2018-01-29 20:58:58 +00:00
|
|
|
#include <pcb_edit_frame.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <trigo.h>
|
|
|
|
#include <pcbnew.h>
|
2019-06-03 13:49:17 +00:00
|
|
|
#include <tools/drc.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <class_board.h>
|
|
|
|
#include <class_module.h>
|
|
|
|
#include <class_track.h>
|
|
|
|
#include <class_zone.h>
|
2019-06-04 11:52:03 +00:00
|
|
|
#include <class_drawsegment.h>
|
2012-01-23 04:33:36 +00:00
|
|
|
#include <class_marker_pcb.h>
|
2012-07-30 07:40:25 +00:00
|
|
|
#include <math_for_graphics.h>
|
2012-07-31 17:51:58 +00:00
|
|
|
#include <polygon_test_point_inside.h>
|
2016-04-06 18:15:49 +00:00
|
|
|
#include <convert_basic_shapes_to_polygon.h>
|
2018-03-21 00:37:35 +00:00
|
|
|
#include <board_commit.h>
|
2011-09-23 13:57:12 +00:00
|
|
|
|
|
|
|
|
2019-06-06 11:10:24 +00:00
|
|
|
/**
|
|
|
|
* compare 2 convex polygons and return true if distance > aDist (if no error DRC)
|
2010-09-20 16:21:47 +00:00
|
|
|
* i.e if for each edge of the first polygon distance from each edge of the other polygon
|
|
|
|
* is >= aDist
|
|
|
|
*/
|
2019-06-04 10:45:43 +00:00
|
|
|
bool poly2polyDRC( wxPoint* aTref, int aTrefCount, wxPoint* aTtest, int aTtestCount, int aDist )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
/* Test if one polygon is contained in the other and thus the polygon overlap.
|
2016-04-06 18:15:49 +00:00
|
|
|
* This case is not covered by the following check if one polygone is
|
2010-09-20 16:21:47 +00:00
|
|
|
* completely contained in the other (because edges don't intersect)!
|
|
|
|
*/
|
2019-06-04 10:45:43 +00:00
|
|
|
if( TestPointInsidePolygon( aTref, aTrefCount, aTtest[0] ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2019-06-04 10:45:43 +00:00
|
|
|
if( TestPointInsidePolygon( aTtest, aTtestCount, aTref[0] ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
|
2016-04-06 18:15:49 +00:00
|
|
|
for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ )
|
2019-06-04 10:45:43 +00:00
|
|
|
{
|
|
|
|
// for all edges in aTref
|
|
|
|
for( int kk = 0, ll = aTtestCount - 1; kk < aTtestCount; ll = kk, kk++ )
|
|
|
|
{
|
|
|
|
// for all edges in aTtest
|
2010-09-20 16:21:47 +00:00
|
|
|
double d;
|
2016-04-06 18:15:49 +00:00
|
|
|
int intersect = TestForIntersectionOfStraightLineSegments(
|
2019-06-04 10:45:43 +00:00
|
|
|
aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
|
|
|
|
aTtest[kk].x, aTtest[kk].y, aTtest[ll].x, aTtest[ll].y,
|
|
|
|
nullptr, nullptr, &d );
|
2016-04-06 18:15:49 +00:00
|
|
|
|
2017-01-13 17:51:22 +00:00
|
|
|
if( intersect || ( d < aDist ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-04 10:45:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* compare a trapezoid (can be rectangle) and a segment and return true if distance > aDist
|
2010-09-20 16:21:47 +00:00
|
|
|
*/
|
2016-04-06 18:15:49 +00:00
|
|
|
bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, int aDist )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
/* Test if the segment is contained in the polygon.
|
|
|
|
* This case is not covered by the following check if the segment is
|
|
|
|
* completely contained in the polygon (because edges don't intersect)!
|
|
|
|
*/
|
2016-04-06 18:15:49 +00:00
|
|
|
if( TestPointInsidePolygon( aTref, aTrefCount, aSegStart ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
|
2016-04-06 18:15:49 +00:00
|
|
|
for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ )
|
|
|
|
{ // for all edges in polygon
|
2010-09-20 16:21:47 +00:00
|
|
|
double d;
|
2016-04-06 18:15:49 +00:00
|
|
|
int intersect = TestForIntersectionOfStraightLineSegments(
|
2019-06-04 10:45:43 +00:00
|
|
|
aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
|
|
|
|
aSegStart.x, aSegStart.y, aSegEnd.x, aSegEnd.y,
|
|
|
|
NULL, NULL, &d );
|
2016-04-06 18:15:49 +00:00
|
|
|
|
|
|
|
if( intersect || ( d < aDist) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-04 10:45:43 +00:00
|
|
|
#define PUSH_NEW_MARKER_3( a, b, c ) push_back( m_markerFactory.NewMarker( a, b, c ) )
|
|
|
|
#define PUSH_NEW_MARKER_4( a, b, c, d ) push_back( m_markerFactory.NewMarker( a, b, c, d ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
|
2019-05-31 02:30:28 +00:00
|
|
|
bool DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt,
|
2019-06-25 02:19:27 +00:00
|
|
|
bool aTestZones )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
TRACK* track;
|
2016-07-11 03:09:18 +00:00
|
|
|
wxPoint delta; // length on X and Y axis of segments
|
2010-09-20 16:21:47 +00:00
|
|
|
wxPoint shape_pos;
|
2018-03-21 00:37:35 +00:00
|
|
|
|
|
|
|
std::vector<MARKER_PCB*> markers;
|
|
|
|
|
|
|
|
auto commitMarkers = [&]()
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
BOARD_COMMIT commit( m_pcbEditorFrame );
|
2018-03-21 00:37:35 +00:00
|
|
|
|
2019-06-04 10:45:43 +00:00
|
|
|
for( MARKER_PCB* marker : markers )
|
|
|
|
commit.Add( marker );
|
2018-03-21 00:37:35 +00:00
|
|
|
|
2019-06-04 10:45:43 +00:00
|
|
|
commit.Push( wxEmptyString, false, false );
|
2018-03-21 00:37:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Returns false if we should return false from call site, or true to continue
|
|
|
|
auto handleNewMarker = [&]() -> bool
|
|
|
|
{
|
|
|
|
if( !m_reportAllTrackErrors )
|
|
|
|
{
|
|
|
|
if( markers.size() > 0 )
|
|
|
|
commitMarkers();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
};
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2014-05-20 09:29:37 +00:00
|
|
|
NETCLASSPTR netclass = aRefSeg->GetNetClass();
|
2014-05-13 09:22:51 +00:00
|
|
|
BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings();
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
/* In order to make some calculations more easier or faster,
|
|
|
|
* pads and tracks coordinates will be made relative to the reference segment origin
|
|
|
|
*/
|
2013-01-13 00:04:00 +00:00
|
|
|
wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2013-01-13 00:04:00 +00:00
|
|
|
m_segmEnd = delta = aRefSeg->GetEnd() - origin;
|
2010-09-20 16:21:47 +00:00
|
|
|
m_segmAngle = 0;
|
|
|
|
|
2019-06-25 02:19:27 +00:00
|
|
|
LSET layerMask = aRefSeg->GetLayerSet();
|
|
|
|
int net_code_ref = aRefSeg->GetNetCode();
|
|
|
|
int ref_seg_clearance = netclass->GetClearance();
|
|
|
|
int ref_seg_width = aRefSeg->GetWidth();
|
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-08-23 22:41:57 +00:00
|
|
|
/******************************************/
|
|
|
|
/* Phase 0 : via DRC tests : */
|
|
|
|
/******************************************/
|
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
if( aRefSeg->Type() == PCB_VIA_T )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2018-01-28 09:35:33 +00:00
|
|
|
VIA *refvia = static_cast<VIA*>( aRefSeg );
|
2018-08-27 13:50:50 +00:00
|
|
|
wxPoint refviaPos = refvia->GetPosition();
|
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
// test if the via size is smaller than minimum
|
2014-04-25 06:00:04 +00:00
|
|
|
if( refvia->GetViaType() == VIA_MICROVIA )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2014-05-13 09:22:51 +00:00
|
|
|
if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
2018-03-20 02:54:16 +00:00
|
|
|
|
2016-08-28 19:26:51 +00:00
|
|
|
if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill )
|
2016-08-25 01:16:27 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA_DRILL );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2016-08-25 01:16:27 +00:00
|
|
|
}
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-05-13 09:22:51 +00:00
|
|
|
if( refvia->GetWidth() < dsnSettings.m_ViasMinSize )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_VIA );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
2018-03-20 02:54:16 +00:00
|
|
|
|
2016-08-28 19:26:51 +00:00
|
|
|
if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill )
|
2016-08-25 01:16:27 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_VIA_DRILL );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2016-08-25 01:16:27 +00:00
|
|
|
}
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// test if via's hole is bigger than its diameter
|
|
|
|
// This test is necessary since the via hole size and width can be modified
|
|
|
|
// and a default via hole can be bigger than some vias sizes
|
2014-04-25 06:00:04 +00:00
|
|
|
if( refvia->GetDrillValue() > refvia->GetWidth() )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_VIA_HOLE_BIGGER );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
2018-01-06 23:37:24 +00:00
|
|
|
// test if the type of via is allowed due to design rules
|
2018-08-27 13:50:50 +00:00
|
|
|
if( refvia->GetViaType() == VIA_MICROVIA && !dsnSettings.m_MicroViasAllowed )
|
2018-01-06 23:37:24 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_MICRO_VIA_NOT_ALLOWED );
|
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2018-01-06 23:37:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// test if the type of via is allowed due to design rules
|
2018-08-27 13:50:50 +00:00
|
|
|
if( refvia->GetViaType() == VIA_BLIND_BURIED && !dsnSettings.m_BlindBuriedViaAllowed )
|
2018-01-06 23:37:24 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_BURIED_VIA_NOT_ALLOWED );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2018-01-06 23:37:24 +00:00
|
|
|
}
|
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
// For microvias: test if they are blind vias and only between 2 layers
|
|
|
|
// because they are used for very small drill size and are drill by laser
|
|
|
|
// and **only one layer** can be drilled
|
2014-04-25 06:00:04 +00:00
|
|
|
if( refvia->GetViaType() == VIA_MICROVIA )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2017-03-13 03:19:33 +00:00
|
|
|
PCB_LAYER_ID layer1, layer2;
|
2014-06-24 16:17:18 +00:00
|
|
|
bool err = true;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2014-04-25 06:00:04 +00:00
|
|
|
refvia->LayerPair( &layer1, &layer2 );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
|
|
|
if( layer1 > layer2 )
|
2015-06-26 13:41:56 +00:00
|
|
|
std::swap( layer1, layer2 );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-08-27 13:50:50 +00:00
|
|
|
if( layer2 == B_Cu && layer1 == dsnSettings.GetCopperLayerCount() - 2 )
|
2014-06-24 16:17:18 +00:00
|
|
|
err = false;
|
|
|
|
else if( layer1 == F_Cu && layer2 == In1_Cu )
|
2010-09-20 16:21:47 +00:00
|
|
|
err = false;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( err )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
2018-01-06 23:37:24 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
else // This is a track segment
|
|
|
|
{
|
2019-06-25 02:19:27 +00:00
|
|
|
if( ref_seg_width < dsnSettings.m_TrackMinWidth )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2018-08-27 13:50:50 +00:00
|
|
|
wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
|
|
|
|
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_3( refsegMiddle, aRefSeg, DRCE_TOO_SMALL_TRACK_WIDTH );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// for a non horizontal or vertical segment Compute the segment angle
|
|
|
|
// in tenths of degrees and its length
|
|
|
|
if( delta.x || delta.y )
|
|
|
|
{
|
|
|
|
// Compute the segment angle in 0,1 degrees
|
|
|
|
m_segmAngle = ArcTangente( delta.y, delta.x );
|
|
|
|
|
|
|
|
// Compute the segment length: we build an equivalent rotated segment,
|
|
|
|
// this segment is horizontal, therefore dx = length
|
|
|
|
RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
m_segmLength = delta.x;
|
|
|
|
|
|
|
|
/******************************************/
|
|
|
|
/* Phase 1 : test DRC track to pads : */
|
|
|
|
/******************************************/
|
|
|
|
|
2010-10-28 13:02:07 +00:00
|
|
|
/* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
|
|
|
|
* but having a hole
|
|
|
|
* This dummy pad has the size and shape of the hole
|
|
|
|
* to test tracks to pad hole DRC, using checkClearanceSegmToPad test function.
|
|
|
|
* Therefore, this dummy pad is a circle or an oval.
|
|
|
|
* A pad must have a parent because some functions expect a non null parent
|
|
|
|
* to find the parent board, and some other data
|
|
|
|
*/
|
2014-06-24 16:17:18 +00:00
|
|
|
MODULE dummymodule( m_pcb ); // Creates a dummy parent
|
|
|
|
D_PAD dummypad( &dummymodule );
|
2012-02-19 04:02:19 +00:00
|
|
|
|
2014-06-24 16:17:18 +00:00
|
|
|
dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
// Compute the min distance to pads
|
2019-06-25 02:19:27 +00:00
|
|
|
for( MODULE* mod : m_pcb->Modules() )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-25 02:19:27 +00:00
|
|
|
for( D_PAD* pad : mod->Pads() )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2018-08-27 13:50:50 +00:00
|
|
|
SEG padSeg( pad->GetPosition(), pad->GetPosition() );
|
|
|
|
|
2019-06-25 02:19:27 +00:00
|
|
|
// No problem if pads are on another layer, but if a drill hole exists (a pad on
|
|
|
|
// a single layer can have a hole!) we must test the hole
|
2014-06-24 16:17:18 +00:00
|
|
|
if( !( pad->GetLayerSet() & layerMask ).any() )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-25 02:19:27 +00:00
|
|
|
// We must test the pad hole. In order to use checkClearanceSegmToPad(), a
|
|
|
|
// pseudo pad is used, with a shape and a size like the hole
|
2012-02-19 04:02:19 +00:00
|
|
|
if( pad->GetDrillSize().x == 0 )
|
2010-09-20 16:21:47 +00:00
|
|
|
continue;
|
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
dummypad.SetSize( pad->GetDrillSize() );
|
2010-09-20 16:21:47 +00:00
|
|
|
dummypad.SetPosition( pad->GetPosition() );
|
2018-03-21 00:37:35 +00:00
|
|
|
dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
|
2015-08-23 19:40:33 +00:00
|
|
|
PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
|
2012-02-19 04:02:19 +00:00
|
|
|
dummypad.SetOrientation( pad->GetOrientation() );
|
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
m_padToTestPos = dummypad.GetPosition() - origin;
|
|
|
|
|
2019-06-25 02:19:27 +00:00
|
|
|
if( !checkClearanceSegmToPad( &dummypad, ref_seg_width, ref_seg_clearance ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, pad, padSeg, DRCE_TRACK_NEAR_THROUGH_HOLE );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
// The pad must be in a net (i.e pt_pad->GetNet() != 0 )
|
|
|
|
// but no problem if the pad netcode is the current netcode (same net)
|
2014-02-25 10:40:34 +00:00
|
|
|
if( pad->GetNetCode() // the pad must be connected
|
|
|
|
&& net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok
|
2010-09-20 16:21:47 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// DRC for the pad
|
* KIWAY Milestone A): Make major modules into DLL/DSOs.
! The initial testing of this commit should be done using a Debug build so that
all the wxASSERT()s are enabled. Also, be sure and keep enabled the
USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it
off is senseless anyways. If you want stable code, go back to a prior version,
the one tagged with "stable".
* Relocate all functionality out of the wxApp derivative into more finely
targeted purposes:
a) DLL/DSO specific
b) PROJECT specific
c) EXE or process specific
d) configuration file specific data
e) configuration file manipulations functions.
All of this functionality was blended into an extremely large wxApp derivative
and that was incompatible with the desire to support multiple concurrently
loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects.
An amazing amount of organization come from simply sorting each bit of
functionality into the proper box.
* Switch to wxConfigBase from wxConfig everywhere except instantiation.
* Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD,
PGM_SINGLE_TOP,
* Remove "Return" prefix on many function names.
* Remove obvious comments from CMakeLists.txt files, and from else() and endif()s.
* Fix building boost for use in a DSO on linux.
* Remove some of the assumptions in the CMakeLists.txt files that windows had
to be the host platform when building windows binaries.
* Reduce the number of wxStrings being constructed at program load time via
static construction.
* Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that
these functions are useful even when the wxConfigBase comes from another
source, as is the case in the KICAD_MANAGER_FRAME.
* Move the setting of the KIPRJMOD environment variable into class PROJECT,
so that it can be moved into a project variable soon, and out of FP_LIB_TABLE.
* Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all
its child wxFrames and wxDialogs now have a Kiway() member function which
returns a KIWAY& that that window tree branch is in support of. This is like
wxWindows DNA in that child windows get this member with proper value at time
of construction.
* Anticipate some of the needs for milestones B) and C) and make code
adjustments now in an effort to reduce work in those milestones.
* No testing has been done for python scripting, since milestone C) has that
being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
|
|
|
shape_pos = pad->ShapePos();
|
2010-09-20 16:21:47 +00:00
|
|
|
m_padToTestPos = shape_pos - origin;
|
2019-06-25 02:19:27 +00:00
|
|
|
int segToPadClearance = std::max( ref_seg_clearance, pad->GetClearance() );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2019-06-25 02:19:27 +00:00
|
|
|
if( !checkClearanceSegmToPad( pad, ref_seg_width, segToPadClearance ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, pad, padSeg, DRCE_TRACK_NEAR_PAD );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************/
|
|
|
|
/* Phase 2: test DRC with other track segments */
|
|
|
|
/***********************************************/
|
|
|
|
|
|
|
|
// At this point the reference segment is the X axis
|
|
|
|
|
|
|
|
// Test the reference segment with other track segments
|
2011-11-10 08:21:11 +00:00
|
|
|
wxPoint segStartPoint;
|
|
|
|
wxPoint segEndPoint;
|
2018-03-20 02:54:16 +00:00
|
|
|
|
2019-05-31 02:30:28 +00:00
|
|
|
for( auto it = aStartIt; it != aEndIt; it++ )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-05-31 02:30:28 +00:00
|
|
|
track = *it;
|
2010-09-20 16:21:47 +00:00
|
|
|
// No problem if segments have the same net code:
|
2014-02-25 10:40:34 +00:00
|
|
|
if( net_code_ref == track->GetNetCode() )
|
2010-09-20 16:21:47 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// No problem if segment are on different layers :
|
2014-06-25 17:01:50 +00:00
|
|
|
if( !( layerMask & track->GetLayerSet() ).any() )
|
2010-09-20 16:21:47 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// the minimum distance = clearance plus half the reference track
|
|
|
|
// width plus half the other track's width
|
2019-06-25 02:19:27 +00:00
|
|
|
int w_dist = std::max( ref_seg_clearance, track->GetClearance() );
|
|
|
|
w_dist += ( ref_seg_width + track->GetWidth() ) / 2;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2017-03-11 17:09:18 +00:00
|
|
|
// Due to many double to int conversions during calculations, which
|
|
|
|
// create rounding issues,
|
|
|
|
// the exact clearance margin cannot be really known.
|
|
|
|
// To avoid false bad DRC detection due to these rounding issues,
|
|
|
|
// slightly decrease the w_dist (remove one nanometer is enough !)
|
|
|
|
w_dist -= 1;
|
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
// If the reference segment is a via, we test it here
|
2011-10-01 19:24:27 +00:00
|
|
|
if( aRefSeg->Type() == PCB_VIA_T )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2013-01-13 00:04:00 +00:00
|
|
|
delta = track->GetEnd() - track->GetStart();
|
|
|
|
segStartPoint = aRefSeg->GetStart() - track->GetStart();
|
2018-08-27 13:50:50 +00:00
|
|
|
wxPoint pos = aRefSeg->GetPosition();
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
if( track->Type() == PCB_VIA_T )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
// Test distance between two vias, i.e. two circles, trivial case
|
2013-05-01 17:32:36 +00:00
|
|
|
if( EuclideanNorm( segStartPoint ) < w_dist )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( pos, aRefSeg, track, DRCE_VIA_NEAR_VIA );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else // test via to segment
|
|
|
|
{
|
2013-05-05 07:17:48 +00:00
|
|
|
// Compute l'angle du segment a tester;
|
|
|
|
double angle = ArcTangente( delta.y, delta.x );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
// Compute new coordinates ( the segment become horizontal)
|
|
|
|
RotatePoint( &delta, angle );
|
|
|
|
RotatePoint( &segStartPoint, angle );
|
|
|
|
|
|
|
|
if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( pos, aRefSeg, track, DRCE_VIA_NEAR_TRACK );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We compute segStartPoint, segEndPoint = starting and ending point coordinates for
|
|
|
|
* the segment to test in the new axis : the new X axis is the
|
|
|
|
* reference segment. We must translate and rotate the segment to test
|
|
|
|
*/
|
2013-01-13 00:04:00 +00:00
|
|
|
segStartPoint = track->GetStart() - origin;
|
|
|
|
segEndPoint = track->GetEnd() - origin;
|
2010-09-20 16:21:47 +00:00
|
|
|
RotatePoint( &segStartPoint, m_segmAngle );
|
|
|
|
RotatePoint( &segEndPoint, m_segmAngle );
|
2018-03-21 00:37:35 +00:00
|
|
|
|
2018-08-27 13:50:50 +00:00
|
|
|
SEG seg( segStartPoint, segEndPoint );
|
|
|
|
|
2011-10-01 19:24:27 +00:00
|
|
|
if( track->Type() == PCB_VIA_T )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
|
|
|
|
continue;
|
|
|
|
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_NEAR_VIA );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We have changed axis:
|
|
|
|
* the reference segment is Horizontal.
|
2018-04-08 10:28:59 +00:00
|
|
|
* 3 cases : the segment to test can be parallel, perpendicular or have another direction
|
2010-09-20 16:21:47 +00:00
|
|
|
*/
|
|
|
|
if( segStartPoint.y == segEndPoint.y ) // parallel segments
|
|
|
|
{
|
|
|
|
if( abs( segStartPoint.y ) >= w_dist )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Ensure segStartPoint.x <= segEndPoint.x
|
|
|
|
if( segStartPoint.x > segEndPoint.x )
|
2015-06-26 13:41:56 +00:00
|
|
|
std::swap( segStartPoint.x, segEndPoint.x );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( segStartPoint.x > ( -w_dist ) && segStartPoint.x < ( m_segmLength + w_dist ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2013-09-11 15:30:21 +00:00
|
|
|
// the start point is inside the reference range
|
|
|
|
// X........
|
|
|
|
// O--REF--+
|
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
// Fine test : we consider the rounded shape of each end of the track segment:
|
|
|
|
if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS1 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS2 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( segEndPoint.x > ( -w_dist ) && segEndPoint.x < ( m_segmLength + w_dist ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2013-09-11 15:30:21 +00:00
|
|
|
// the end point is inside the reference range
|
|
|
|
// .....X
|
|
|
|
// O--REF--+
|
|
|
|
// Fine test : we consider the rounded shape of the ends
|
2010-09-20 16:21:47 +00:00
|
|
|
if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS3 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS4 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( segStartPoint.x <= 0 && segEndPoint.x >= 0 )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2018-08-27 13:50:50 +00:00
|
|
|
// the segment straddles the reference range (this actually only
|
|
|
|
// checks if it straddles the origin, because the other cases where already
|
|
|
|
// handled)
|
|
|
|
// X.............X
|
|
|
|
// O--REF--+
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_SEGMENTS_TOO_CLOSE );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
|
|
|
|
{
|
2018-08-27 13:50:50 +00:00
|
|
|
if( segStartPoint.x <= -w_dist || segStartPoint.x >= m_segmLength + w_dist )
|
2010-09-20 16:21:47 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Test if segments are crossing
|
|
|
|
if( segStartPoint.y > segEndPoint.y )
|
2015-06-26 13:41:56 +00:00
|
|
|
std::swap( segStartPoint.y, segEndPoint.y );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2018-03-20 02:54:16 +00:00
|
|
|
if( ( segStartPoint.y < 0 ) && ( segEndPoint.y > 0 ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2019-05-25 19:01:54 +00:00
|
|
|
MARKER_PCB* m = m_markerFactory.NewMarker( aRefSeg, track, seg,
|
|
|
|
DRCE_TRACKS_CROSSING );
|
|
|
|
m->SetPosition( wxPoint( track->GetStart().x, aRefSeg->GetStart().y ) );
|
|
|
|
markers.push_back( m );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// At this point the drc error is due to an end near a reference segm end
|
|
|
|
if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM1 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM2 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else // segments quelconques entre eux
|
|
|
|
{
|
|
|
|
// calcul de la "surface de securite du segment de reference
|
|
|
|
// First rought 'and fast) test : the track segment is like a rectangle
|
|
|
|
|
|
|
|
m_xcliplo = m_ycliplo = -w_dist;
|
|
|
|
m_xcliphi = m_segmLength + w_dist;
|
|
|
|
m_ycliphi = w_dist;
|
|
|
|
|
|
|
|
// A fine test is needed because a serment is not exactly a
|
|
|
|
// rectangle, it has rounded ends
|
|
|
|
if( !checkLine( segStartPoint, segEndPoint ) )
|
|
|
|
{
|
|
|
|
/* 2eme passe : the track has rounded ends.
|
|
|
|
* we must a fine test for each rounded end and the
|
|
|
|
* rectangular zone
|
|
|
|
*/
|
|
|
|
|
|
|
|
m_xcliplo = 0;
|
|
|
|
m_xcliphi = m_segmLength;
|
|
|
|
|
|
|
|
if( !checkLine( segStartPoint, segEndPoint ) )
|
|
|
|
{
|
2019-05-25 19:01:54 +00:00
|
|
|
wxPoint failurePoint;
|
|
|
|
MARKER_PCB* m;
|
|
|
|
|
|
|
|
if( SegmentIntersectsSegment( aRefSeg->GetStart(), aRefSeg->GetEnd(),
|
|
|
|
track->GetStart(), track->GetEnd(),
|
|
|
|
&failurePoint ) )
|
|
|
|
{
|
|
|
|
m = m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACKS_CROSSING );
|
|
|
|
m->SetPosition( failurePoint );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m = m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM3 );
|
|
|
|
}
|
|
|
|
|
|
|
|
markers.push_back( m );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
else // The drc error is due to the starting or the ending point of the reference segment
|
|
|
|
{
|
|
|
|
// Test the starting and the ending point
|
2013-01-13 00:04:00 +00:00
|
|
|
segStartPoint = track->GetStart();
|
|
|
|
segEndPoint = track->GetEnd();
|
2010-09-20 16:21:47 +00:00
|
|
|
delta = segEndPoint - segStartPoint;
|
|
|
|
|
2013-05-01 17:32:36 +00:00
|
|
|
// Compute the segment orientation (angle) en 0,1 degre
|
2013-05-05 07:17:48 +00:00
|
|
|
double angle = ArcTangente( delta.y, delta.x );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2016-07-11 03:09:18 +00:00
|
|
|
// Compute the segment length: delta.x = length after rotation
|
2010-09-20 16:21:47 +00:00
|
|
|
RotatePoint( &delta, angle );
|
|
|
|
|
|
|
|
/* Comute the reference segment coordinates relatives to a
|
|
|
|
* X axis = current tested segment
|
|
|
|
*/
|
2013-01-13 00:04:00 +00:00
|
|
|
wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint;
|
|
|
|
wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
RotatePoint( &relStartPos, angle );
|
|
|
|
RotatePoint( &relEndPos, angle );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM4 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
|
|
|
|
{
|
2019-06-04 10:45:43 +00:00
|
|
|
markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM5 );
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( !handleNewMarker() )
|
2018-03-20 02:54:16 +00:00
|
|
|
return false;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-19 16:46:46 +00:00
|
|
|
/***************************************/
|
|
|
|
/* Phase 3: test DRC with copper zones */
|
|
|
|
/***************************************/
|
|
|
|
// Can be *very* time consumming.
|
|
|
|
if( aTestZones )
|
2018-08-23 22:41:57 +00:00
|
|
|
{
|
2019-03-19 16:46:46 +00:00
|
|
|
SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
|
2018-08-23 22:41:57 +00:00
|
|
|
|
2019-03-19 16:46:46 +00:00
|
|
|
for( ZONE_CONTAINER* zone : m_pcb->Zones() )
|
|
|
|
{
|
|
|
|
if( zone->GetFilledPolysList().IsEmpty() || zone->GetIsKeepout() )
|
|
|
|
continue;
|
2018-08-23 22:41:57 +00:00
|
|
|
|
2019-03-19 16:46:46 +00:00
|
|
|
if( !( layerMask & zone->GetLayerSet() ).any() )
|
|
|
|
continue;
|
2018-08-27 13:50:50 +00:00
|
|
|
|
2019-03-19 16:46:46 +00:00
|
|
|
if( zone->GetNetCode() && zone->GetNetCode() == net_code_ref )
|
|
|
|
continue;
|
|
|
|
|
2019-06-25 02:19:27 +00:00
|
|
|
int clearance = std::max( ref_seg_clearance, zone->GetClearance() );
|
2019-03-19 16:46:46 +00:00
|
|
|
SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
|
2018-08-23 22:41:57 +00:00
|
|
|
|
2019-06-25 02:19:27 +00:00
|
|
|
if( outline->Distance( refSeg, ref_seg_width ) < clearance )
|
2019-03-19 16:46:46 +00:00
|
|
|
addMarkerToPcb( m_markerFactory.NewMarker( aRefSeg, zone, DRCE_TRACK_NEAR_ZONE ) );
|
|
|
|
}
|
2018-08-23 22:41:57 +00:00
|
|
|
}
|
|
|
|
|
2018-11-28 12:11:43 +00:00
|
|
|
/***********************************************/
|
|
|
|
/* Phase 4: test DRC with to board edge */
|
|
|
|
/***********************************************/
|
|
|
|
{
|
|
|
|
SEG test_seg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
|
|
|
|
|
2019-06-25 02:19:27 +00:00
|
|
|
int clearance = std::max( ref_seg_clearance, dsnSettings.m_CopperEdgeClearance );
|
2019-04-05 16:10:59 +00:00
|
|
|
|
|
|
|
// the minimum distance = clearance plus half the reference track width
|
2019-06-25 02:19:27 +00:00
|
|
|
SEG::ecoord w_dist = clearance + ref_seg_width / 2;
|
2019-06-04 11:52:03 +00:00
|
|
|
SEG::ecoord w_dist_sq = w_dist * w_dist;
|
2018-11-28 12:11:43 +00:00
|
|
|
|
|
|
|
for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
|
|
|
|
{
|
2019-06-04 11:52:03 +00:00
|
|
|
if( test_seg.SquaredDistance( *it ) < w_dist_sq )
|
2018-11-28 12:11:43 +00:00
|
|
|
{
|
2019-06-04 11:52:03 +00:00
|
|
|
VECTOR2I pt = test_seg.NearestPoint( *it );
|
|
|
|
|
|
|
|
KICAD_T types[] = { PCB_LINE_T, EOT };
|
|
|
|
DRAWSEGMENT* edge = nullptr;
|
|
|
|
INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
|
|
|
|
{
|
|
|
|
DRAWSEGMENT* test_edge = dynamic_cast<DRAWSEGMENT*>( item );
|
|
|
|
|
|
|
|
if( !test_edge || test_edge->GetLayer() != Edge_Cuts )
|
|
|
|
return SEARCH_CONTINUE;
|
|
|
|
|
|
|
|
if( test_edge->HitTest((wxPoint) pt, w_dist ) )
|
|
|
|
{
|
|
|
|
edge = test_edge;
|
|
|
|
return SEARCH_QUIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SEARCH_CONTINUE;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Best-efforts search for edge segment
|
|
|
|
BOARD::IterateForward<BOARD_ITEM*>( m_pcb->Drawings(), inspector, nullptr, types );
|
|
|
|
|
|
|
|
if( edge )
|
|
|
|
markers.PUSH_NEW_MARKER_4( (wxPoint) pt, aRefSeg, edge, DRCE_TRACK_NEAR_EDGE );
|
|
|
|
else
|
|
|
|
markers.PUSH_NEW_MARKER_3( (wxPoint) pt, aRefSeg, DRCE_TRACK_NEAR_EDGE );
|
2018-11-28 12:11:43 +00:00
|
|
|
|
|
|
|
if( !handleNewMarker() )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-21 00:37:35 +00:00
|
|
|
if( markers.size() > 0 )
|
|
|
|
{
|
|
|
|
commitMarkers();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return true;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
|
|
|
|
{
|
|
|
|
int dist;
|
2016-04-06 18:15:49 +00:00
|
|
|
double pad_angle;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
// Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
|
2010-09-20 16:21:47 +00:00
|
|
|
int dist_min = aRefPad->GetClearance( aPad );
|
|
|
|
|
|
|
|
// relativePadPos is the aPad shape position relative to the aRefPad shape position
|
* KIWAY Milestone A): Make major modules into DLL/DSOs.
! The initial testing of this commit should be done using a Debug build so that
all the wxASSERT()s are enabled. Also, be sure and keep enabled the
USE_KIWAY_DLLs option. The tree won't likely build without it. Turning it
off is senseless anyways. If you want stable code, go back to a prior version,
the one tagged with "stable".
* Relocate all functionality out of the wxApp derivative into more finely
targeted purposes:
a) DLL/DSO specific
b) PROJECT specific
c) EXE or process specific
d) configuration file specific data
e) configuration file manipulations functions.
All of this functionality was blended into an extremely large wxApp derivative
and that was incompatible with the desire to support multiple concurrently
loaded DLL/DSO's ("KIFACE")s and multiple concurrently open projects.
An amazing amount of organization come from simply sorting each bit of
functionality into the proper box.
* Switch to wxConfigBase from wxConfig everywhere except instantiation.
* Add classes KIWAY, KIFACE, KIFACE_I, SEARCH_STACK, PGM_BASE, PGM_KICAD,
PGM_SINGLE_TOP,
* Remove "Return" prefix on many function names.
* Remove obvious comments from CMakeLists.txt files, and from else() and endif()s.
* Fix building boost for use in a DSO on linux.
* Remove some of the assumptions in the CMakeLists.txt files that windows had
to be the host platform when building windows binaries.
* Reduce the number of wxStrings being constructed at program load time via
static construction.
* Pass wxConfigBase* to all SaveSettings() and LoadSettings() functions so that
these functions are useful even when the wxConfigBase comes from another
source, as is the case in the KICAD_MANAGER_FRAME.
* Move the setting of the KIPRJMOD environment variable into class PROJECT,
so that it can be moved into a project variable soon, and out of FP_LIB_TABLE.
* Add the KIWAY_PLAYER which is associated with a particular PROJECT, and all
its child wxFrames and wxDialogs now have a Kiway() member function which
returns a KIWAY& that that window tree branch is in support of. This is like
wxWindows DNA in that child windows get this member with proper value at time
of construction.
* Anticipate some of the needs for milestones B) and C) and make code
adjustments now in an effort to reduce work in those milestones.
* No testing has been done for python scripting, since milestone C) has that
being largely reworked and re-thought-out.
2014-03-20 00:42:08 +00:00
|
|
|
wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2013-05-01 17:32:36 +00:00
|
|
|
dist = KiROUND( EuclideanNorm( relativePadPos ) );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
// Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
|
2017-01-13 17:51:22 +00:00
|
|
|
int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius();
|
|
|
|
|
|
|
|
if( delta >= dist_min )
|
2010-09-20 16:21:47 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
/* Here, pads are near and DRC depend on the pad shapes
|
|
|
|
* We must compare distance using a fine shape analysis
|
|
|
|
* Because a circle or oval shape is the easier shape to test, try to have
|
2015-08-23 19:40:33 +00:00
|
|
|
* aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
|
2010-09-20 16:21:47 +00:00
|
|
|
* if aRefPad = TRAP. and aPad = RECT, also swap pads
|
|
|
|
* Swap aRefPad and aPad if needed
|
|
|
|
*/
|
|
|
|
bool swap_pads;
|
|
|
|
swap_pads = false;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2013-01-26 17:49:48 +00:00
|
|
|
// swap pads to make comparisons easier
|
2016-04-06 18:15:49 +00:00
|
|
|
// Note also a ROUNDRECT pad with a corner radius = r can be considered as
|
|
|
|
// a smaller RECT (size - 2*r) with a clearance increased by r
|
|
|
|
// priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
|
2015-08-23 19:40:33 +00:00
|
|
|
if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
|
2013-01-26 17:49:48 +00:00
|
|
|
{
|
2018-08-29 07:13:07 +00:00
|
|
|
// pad ref shape is here oval, rect, roundrect, chamfered rect, trapezoid or custom
|
2013-01-26 17:49:48 +00:00
|
|
|
switch( aPad->GetShape() )
|
|
|
|
{
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_CIRCLE:
|
2013-01-26 17:49:48 +00:00
|
|
|
swap_pads = true;
|
|
|
|
break;
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_OVAL:
|
2013-01-26 17:49:48 +00:00
|
|
|
swap_pads = true;
|
|
|
|
break;
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_RECT:
|
2016-04-06 18:15:49 +00:00
|
|
|
case PAD_SHAPE_ROUNDRECT:
|
2015-08-23 19:40:33 +00:00
|
|
|
if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
|
2013-01-26 17:49:48 +00:00
|
|
|
swap_pads = true;
|
|
|
|
break;
|
|
|
|
|
2017-01-13 17:51:22 +00:00
|
|
|
case PAD_SHAPE_TRAPEZOID:
|
2018-08-29 07:13:07 +00:00
|
|
|
case PAD_SHAPE_CHAMFERED_RECT:
|
2017-01-13 17:51:22 +00:00
|
|
|
case PAD_SHAPE_CUSTOM:
|
2013-01-26 17:49:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
if( swap_pads )
|
|
|
|
{
|
2015-06-26 13:41:56 +00:00
|
|
|
std::swap( aRefPad, aPad );
|
2010-09-20 16:21:47 +00:00
|
|
|
relativePadPos = -relativePadPos;
|
|
|
|
}
|
|
|
|
|
2016-04-06 18:15:49 +00:00
|
|
|
// corners of aRefPad (used only for rect/roundrect/trap pad)
|
|
|
|
wxPoint polyref[4];
|
|
|
|
// corners of aRefPad (used only for custom pad)
|
|
|
|
SHAPE_POLY_SET polysetref;
|
|
|
|
|
|
|
|
// corners of aPad (used only for rect/roundrect/trap pad)
|
|
|
|
wxPoint polycompare[4];
|
|
|
|
// corners of aPad (used only custom pad)
|
|
|
|
SHAPE_POLY_SET polysetcompare;
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
/* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
|
|
|
|
* if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
|
2016-04-06 18:15:49 +00:00
|
|
|
* Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
|
|
|
|
* aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
|
2010-09-20 16:21:47 +00:00
|
|
|
*/
|
|
|
|
bool diag = true;
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
switch( aRefPad->GetShape() )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_CIRCLE:
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
/* One can use checkClearanceSegmToPad to test clearance
|
2012-02-19 04:02:19 +00:00
|
|
|
* aRefPad is like a track segment with a null length and a witdth = GetSize().x
|
2010-09-20 16:21:47 +00:00
|
|
|
*/
|
|
|
|
m_segmLength = 0;
|
|
|
|
m_segmAngle = 0;
|
|
|
|
|
|
|
|
m_segmEnd.x = m_segmEnd.y = 0;
|
|
|
|
|
|
|
|
m_padToTestPos = relativePadPos;
|
2012-02-19 04:02:19 +00:00
|
|
|
diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
|
2010-09-20 16:21:47 +00:00
|
|
|
break;
|
|
|
|
|
2016-04-06 18:15:49 +00:00
|
|
|
case PAD_SHAPE_TRAPEZOID:
|
|
|
|
case PAD_SHAPE_ROUNDRECT:
|
2018-08-29 07:13:07 +00:00
|
|
|
case PAD_SHAPE_CHAMFERED_RECT:
|
2016-04-06 18:15:49 +00:00
|
|
|
case PAD_SHAPE_RECT:
|
2017-01-13 17:51:22 +00:00
|
|
|
case PAD_SHAPE_CUSTOM:
|
2016-04-06 18:15:49 +00:00
|
|
|
// pad_angle = pad orient relative to the aRefPad orient
|
|
|
|
pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
|
|
|
|
NORMALIZE_ANGLE_POS( pad_angle );
|
|
|
|
|
|
|
|
if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
|
|
|
|
{
|
|
|
|
int padRadius = aRefPad->GetRoundRectCornerRadius();
|
|
|
|
dist_min += padRadius;
|
2019-06-06 11:10:24 +00:00
|
|
|
GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(),
|
2019-06-04 10:45:43 +00:00
|
|
|
aRefPad->GetOrientation() );
|
2016-04-06 18:15:49 +00:00
|
|
|
}
|
2018-08-29 07:13:07 +00:00
|
|
|
else if( aRefPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
|
|
|
|
{
|
2019-05-22 14:47:38 +00:00
|
|
|
auto board = aRefPad->GetBoard();
|
|
|
|
int maxError = ARC_HIGH_DEF;
|
|
|
|
|
|
|
|
if( board )
|
|
|
|
maxError = board->GetDesignSettings().m_MaxError;
|
|
|
|
|
2018-08-29 07:13:07 +00:00
|
|
|
// The reference pad can be rotated. calculate the rotated
|
|
|
|
// coordinates ( note, the ref pad position is the origin of
|
|
|
|
// coordinates for this drc test)
|
|
|
|
int padRadius = aRefPad->GetRoundRectCornerRadius();
|
|
|
|
TransformRoundChamferedRectToPolygon( polysetref, wxPoint( 0, 0 ), aRefPad->GetSize(),
|
2019-06-04 10:45:43 +00:00
|
|
|
aRefPad->GetOrientation(),
|
|
|
|
padRadius, aRefPad->GetChamferRectRatio(),
|
|
|
|
aRefPad->GetChamferPositions(), maxError );
|
2018-08-29 07:13:07 +00:00
|
|
|
}
|
2017-01-13 17:51:22 +00:00
|
|
|
else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
|
|
|
|
{
|
|
|
|
polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
|
|
|
|
|
|
|
|
// The reference pad can be rotated. calculate the rotated
|
|
|
|
// coordiantes ( note, the ref pad position is the origin of
|
|
|
|
// coordinates for this drc test)
|
2019-06-06 11:10:24 +00:00
|
|
|
aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref, wxPoint( 0, 0 ),
|
2019-06-04 10:45:43 +00:00
|
|
|
aRefPad->GetOrientation() );
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
2016-04-06 18:15:49 +00:00
|
|
|
else
|
2017-01-13 17:51:22 +00:00
|
|
|
{
|
|
|
|
// BuildPadPolygon has meaning for rect a trapeziod shapes
|
|
|
|
// and returns the 4 corners
|
2016-04-06 18:15:49 +00:00
|
|
|
aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
2016-04-06 18:15:49 +00:00
|
|
|
|
|
|
|
switch( aPad->GetShape() )
|
|
|
|
{
|
|
|
|
case PAD_SHAPE_ROUNDRECT:
|
|
|
|
case PAD_SHAPE_RECT:
|
2018-08-29 07:13:07 +00:00
|
|
|
case PAD_SHAPE_CHAMFERED_RECT:
|
2016-04-06 18:15:49 +00:00
|
|
|
case PAD_SHAPE_TRAPEZOID:
|
2017-01-13 17:51:22 +00:00
|
|
|
case PAD_SHAPE_CUSTOM:
|
2016-04-06 18:15:49 +00:00
|
|
|
if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
|
|
|
|
{
|
|
|
|
int padRadius = aPad->GetRoundRectCornerRadius();
|
|
|
|
dist_min += padRadius;
|
2019-06-06 11:10:24 +00:00
|
|
|
GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(),
|
2019-06-04 10:45:43 +00:00
|
|
|
aPad->GetOrientation() );
|
2016-04-06 18:15:49 +00:00
|
|
|
}
|
2018-08-29 07:13:07 +00:00
|
|
|
else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
|
|
|
|
{
|
2019-05-22 14:47:38 +00:00
|
|
|
auto board = aRefPad->GetBoard();
|
|
|
|
int maxError = ARC_HIGH_DEF;
|
|
|
|
|
|
|
|
if( board )
|
|
|
|
maxError = board->GetDesignSettings().m_MaxError;
|
|
|
|
|
2018-08-29 07:13:07 +00:00
|
|
|
// The reference pad can be rotated. calculate the rotated
|
|
|
|
// coordinates ( note, the ref pad position is the origin of
|
|
|
|
// coordinates for this drc test)
|
|
|
|
int padRadius = aPad->GetRoundRectCornerRadius();
|
2019-06-06 11:10:24 +00:00
|
|
|
TransformRoundChamferedRectToPolygon( polysetcompare, relativePadPos,
|
2019-06-04 10:45:43 +00:00
|
|
|
aPad->GetSize(), aPad->GetOrientation(),
|
|
|
|
padRadius, aPad->GetChamferRectRatio(),
|
|
|
|
aPad->GetChamferPositions(), maxError );
|
2018-08-29 07:13:07 +00:00
|
|
|
}
|
2017-01-13 17:51:22 +00:00
|
|
|
else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
|
|
|
|
{
|
|
|
|
polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
|
|
|
|
|
|
|
|
// The pad to compare can be rotated. calculate the rotated
|
|
|
|
// coordinattes ( note, the pad to compare position
|
|
|
|
// is the relativePadPos for this drc test
|
2019-06-06 11:10:24 +00:00
|
|
|
aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare, relativePadPos,
|
2019-06-04 10:45:43 +00:00
|
|
|
aPad->GetOrientation() );
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
2016-04-06 18:15:49 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
|
|
|
|
|
|
|
|
// Move aPad shape to relativePadPos
|
|
|
|
for( int ii = 0; ii < 4; ii++ )
|
|
|
|
polycompare[ii] += relativePadPos;
|
|
|
|
}
|
2017-01-13 17:51:22 +00:00
|
|
|
// And now test polygons: We have 3 cases:
|
|
|
|
// one poly is complex and the other is basic (has only 4 corners)
|
|
|
|
// both polys are complex
|
|
|
|
// both polys are basic (have only 4 corners) the most usual case
|
|
|
|
if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
|
2016-04-06 18:15:49 +00:00
|
|
|
{
|
|
|
|
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
|
|
|
|
// And now test polygons:
|
2019-06-06 11:10:24 +00:00
|
|
|
if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
|
|
|
|
polycompare, 4, dist_min ) ) // Therefore error
|
|
|
|
diag = false;
|
2016-04-06 18:15:49 +00:00
|
|
|
}
|
2017-01-13 17:51:22 +00:00
|
|
|
else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
|
|
|
|
{
|
|
|
|
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
|
|
|
|
// And now test polygons:
|
2019-06-06 11:10:24 +00:00
|
|
|
if( !poly2polyDRC( (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
|
|
|
|
polyref, 4, dist_min ) ) // Therefore error
|
|
|
|
diag = false;
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
|
|
|
else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
|
|
|
|
{
|
|
|
|
const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
|
|
|
|
const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
|
|
|
|
|
|
|
|
// And now test polygons:
|
2019-06-06 11:10:24 +00:00
|
|
|
if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
|
|
|
|
(wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
|
|
|
|
dist_min ) ) // Therefore error
|
|
|
|
diag = false;
|
2019-06-04 10:45:43 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-06-06 11:10:24 +00:00
|
|
|
if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) ) // Therefore error
|
|
|
|
diag = false;
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
2016-04-06 18:15:49 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
/* Create a track segment with same dimensions as the oval aRefPad
|
|
|
|
* and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
|
|
|
|
*/
|
|
|
|
int segm_width;
|
2012-02-19 04:02:19 +00:00
|
|
|
m_segmAngle = aRefPad->GetOrientation(); // Segment orient.
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2012-02-19 04:02:19 +00:00
|
|
|
if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2012-02-19 04:02:19 +00:00
|
|
|
segm_width = aRefPad->GetSize().y;
|
|
|
|
m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
|
|
|
|
{
|
2012-02-19 04:02:19 +00:00
|
|
|
segm_width = aRefPad->GetSize().x;
|
|
|
|
m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x;
|
2010-09-20 16:21:47 +00:00
|
|
|
m_segmAngle += 900;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the start point must be 0,0 and currently relativePadPos
|
|
|
|
* is relative the center of pad coordinate */
|
|
|
|
wxPoint segstart;
|
|
|
|
segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
|
|
|
|
|
2011-05-12 11:06:32 +00:00
|
|
|
RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment
|
|
|
|
// Calculate segment end position relative to the segment origin
|
|
|
|
m_segmEnd.x = -2 * segstart.x;
|
|
|
|
m_segmEnd.y = -2 * segstart.y;
|
|
|
|
|
|
|
|
// Recalculate the equivalent segment angle in 0,1 degrees
|
|
|
|
// to prepare a call to checkClearanceSegmToPad()
|
|
|
|
m_segmAngle = ArcTangente( m_segmEnd.y, m_segmEnd.x );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
// move pad position relative to the segment origin
|
|
|
|
m_padToTestPos = relativePadPos - segstart;
|
|
|
|
|
2011-05-12 11:06:32 +00:00
|
|
|
// Use segment to pad check to test the second pad:
|
2010-09-20 16:21:47 +00:00
|
|
|
diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
2019-01-02 04:43:13 +00:00
|
|
|
wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
|
2010-09-20 16:21:47 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return diag;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* test if distance between a segment is > aMinDist
|
|
|
|
* segment start point is assumed in (0,0) and segment start point in m_segmEnd
|
2011-05-12 11:06:32 +00:00
|
|
|
* and its orientation is m_segmAngle (m_segmAngle must be already initialized)
|
2010-09-20 16:21:47 +00:00
|
|
|
* and have aSegmentWidth.
|
|
|
|
*/
|
|
|
|
bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
|
|
|
|
{
|
2017-01-13 17:51:22 +00:00
|
|
|
// Note:
|
|
|
|
// we are using a horizontal segment for test, because we know here
|
|
|
|
// only the length and orientation+ of the segment
|
|
|
|
// Therefore the coordinates of the shape of pad to compare
|
|
|
|
// must be calculated in a axis system rotated by m_segmAngle
|
|
|
|
// and centered to the segment origin, before they can be tested
|
|
|
|
// against the segment
|
|
|
|
// We are using:
|
|
|
|
// m_padToTestPos the position of the pad shape in this axis system
|
|
|
|
// m_segmAngle the axis system rotation
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
int segmHalfWidth = aSegmentWidth / 2;
|
|
|
|
int distToLine = segmHalfWidth + aMinDist;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2017-01-13 17:51:22 +00:00
|
|
|
wxSize padHalfsize; // half dimension of the pad
|
|
|
|
|
|
|
|
if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
|
|
|
|
{
|
|
|
|
// For a custom pad, the pad size has no meaning, we only can
|
|
|
|
// use the bounding radius
|
|
|
|
padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-03-20 05:07:34 +00:00
|
|
|
padHalfsize = aPad->GetSize() / 2;
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2012-09-22 11:19:37 +00:00
|
|
|
padHalfsize.x += std::abs(aPad->GetDelta().y) / 2; // Remember: GetDelta().y is the GetSize().x change
|
|
|
|
padHalfsize.y += std::abs(aPad->GetDelta().x) / 2; // Remember: GetDelta().x is the GetSize().y change
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
/* Easy case: just test the distance between segment and pad centre
|
|
|
|
* calculate pad coordinates in the X,Y axis with X axis = segment to test
|
|
|
|
*/
|
|
|
|
RotatePoint( &m_padToTestPos, m_segmAngle );
|
2015-05-17 10:58:23 +00:00
|
|
|
return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength );
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate the bounding box of the pad, including the clearance and the segment width
|
|
|
|
* if the line from 0 to m_segmEnd does not intersect this bounding box,
|
|
|
|
* the clearance is always OK
|
|
|
|
* But if intersect, a better analysis of the pad shape must be done.
|
|
|
|
*/
|
2015-05-17 10:58:23 +00:00
|
|
|
m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
|
|
|
|
m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y;
|
|
|
|
m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
|
|
|
|
m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-03-20 05:07:34 +00:00
|
|
|
wxPoint startPoint( 0, 0 );
|
2016-04-06 18:15:49 +00:00
|
|
|
wxPoint endPoint = m_segmEnd;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2013-05-05 07:17:48 +00:00
|
|
|
double orient = aPad->GetOrientation();
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
RotatePoint( &startPoint, m_padToTestPos, -orient );
|
|
|
|
RotatePoint( &endPoint, m_padToTestPos, -orient );
|
|
|
|
|
|
|
|
if( checkLine( startPoint, endPoint ) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* segment intersects the bounding box. But there is not always a DRC error.
|
|
|
|
* A fine analysis of the pad shape must be done.
|
|
|
|
*/
|
2012-02-19 04:02:19 +00:00
|
|
|
switch( aPad->GetShape() )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2017-01-13 17:51:22 +00:00
|
|
|
case PAD_SHAPE_CIRCLE:
|
|
|
|
// This case was already tested, so it cannot be found here.
|
|
|
|
// it is here just to avoid compil warning, and to remember
|
|
|
|
// it is already tested.
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_OVAL:
|
2015-05-17 10:58:23 +00:00
|
|
|
{
|
2010-09-20 16:21:47 +00:00
|
|
|
/* an oval is a complex shape, but is a rectangle and 2 circles
|
|
|
|
* these 3 basic shapes are more easy to test.
|
2015-05-17 10:58:23 +00:00
|
|
|
*
|
2018-01-23 10:48:04 +00:00
|
|
|
* In calculations we are using a vertical or horizontal oval shape
|
|
|
|
* (i.e. a vertical or horizontal rounded segment)
|
2010-09-20 16:21:47 +00:00
|
|
|
*/
|
2018-01-23 10:48:04 +00:00
|
|
|
wxPoint cstart = m_padToTestPos;
|
|
|
|
wxPoint cend = m_padToTestPos; // center of each circle
|
|
|
|
int delta = std::abs( padHalfsize.y - padHalfsize.x );
|
|
|
|
int radius = std::min( padHalfsize.y, padHalfsize.x );
|
|
|
|
|
|
|
|
if( padHalfsize.x > padHalfsize.y ) // horizontal equivalent segment
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
2018-01-23 10:48:04 +00:00
|
|
|
cstart.x -= delta;
|
|
|
|
cend.x += delta;
|
|
|
|
// Build the rectangular clearance area between the two circles
|
|
|
|
// the rect starts at cstart.x and ends at cend.x and its height
|
|
|
|
// is (radius + distToLine)*2
|
|
|
|
m_xcliplo = cstart.x;
|
|
|
|
m_ycliplo = cstart.y - radius - distToLine;
|
|
|
|
m_xcliphi = cend.x;
|
|
|
|
m_ycliphi = cend.y + radius + distToLine;
|
|
|
|
}
|
|
|
|
else // vertical equivalent segment
|
|
|
|
{
|
|
|
|
cstart.y -= delta;
|
|
|
|
cend.y += delta;
|
|
|
|
// Build the rectangular clearance area between the two circles
|
|
|
|
// the rect starts at cstart.y and ends at cend.y and its width
|
|
|
|
// is (radius + distToLine)*2
|
|
|
|
m_xcliplo = cstart.x - distToLine - radius;
|
|
|
|
m_ycliplo = cstart.y;
|
2018-03-20 05:07:34 +00:00
|
|
|
m_xcliphi = cend.x + distToLine + radius;
|
2018-01-23 10:48:04 +00:00
|
|
|
m_ycliphi = cend.y;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2018-01-23 10:48:04 +00:00
|
|
|
// Test the rectangular clearance area between the two circles (the rounded ends)
|
2018-03-20 05:07:34 +00:00
|
|
|
// If the segment legth is zero, only check the endpoints, skip the rectangle
|
|
|
|
if( m_segmLength && !checkLine( startPoint, endPoint ) )
|
2011-05-12 11:06:32 +00:00
|
|
|
{
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
2011-05-12 11:06:32 +00:00
|
|
|
}
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-01-23 10:48:04 +00:00
|
|
|
// test the first end
|
2010-09-20 16:21:47 +00:00
|
|
|
// Calculate the actual position of the circle, given the pad orientation:
|
2018-01-23 10:48:04 +00:00
|
|
|
RotatePoint( &cstart, m_padToTestPos, orient );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-01-23 10:48:04 +00:00
|
|
|
// Calculate the actual position of the circle in the new X,Y axis, relative
|
|
|
|
// to the segment:
|
|
|
|
RotatePoint( &cstart, m_segmAngle );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2018-01-23 10:48:04 +00:00
|
|
|
if( !checkMarginToCircle( cstart, radius + distToLine, m_segmLength ) )
|
2011-05-12 11:06:32 +00:00
|
|
|
{
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
2011-05-12 11:06:32 +00:00
|
|
|
}
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-01-23 10:48:04 +00:00
|
|
|
// test the second end
|
|
|
|
RotatePoint( &cend, m_padToTestPos, orient );
|
|
|
|
RotatePoint( &cend, m_segmAngle );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-01-23 10:48:04 +00:00
|
|
|
if( !checkMarginToCircle( cend, radius + distToLine, m_segmLength ) )
|
2011-05-12 11:06:32 +00:00
|
|
|
{
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
2011-05-12 11:06:32 +00:00
|
|
|
}
|
2015-05-17 10:58:23 +00:00
|
|
|
}
|
2010-09-20 16:21:47 +00:00
|
|
|
break;
|
|
|
|
|
2016-04-06 18:15:49 +00:00
|
|
|
case PAD_SHAPE_ROUNDRECT:
|
2017-01-13 17:51:22 +00:00
|
|
|
{
|
2016-04-06 18:15:49 +00:00
|
|
|
// a round rect is a smaller rect, with a clearance augmented by the corners radius
|
2017-01-13 17:51:22 +00:00
|
|
|
int r = aPad->GetRoundRectCornerRadius();
|
2016-04-06 18:15:49 +00:00
|
|
|
padHalfsize.x -= r;
|
|
|
|
padHalfsize.y -= r;
|
|
|
|
distToLine += r;
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
2016-04-06 18:15:49 +00:00
|
|
|
// Fall through
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_RECT:
|
2015-05-17 10:58:23 +00:00
|
|
|
// the area to test is a rounded rectangle.
|
|
|
|
// this can be done by testing 2 rectangles and 4 circles (the corners)
|
|
|
|
|
|
|
|
// Testing the first rectangle dimx + distToLine, dimy:
|
|
|
|
m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine;
|
2010-09-20 16:21:47 +00:00
|
|
|
m_ycliplo = m_padToTestPos.y - padHalfsize.y;
|
2015-05-17 10:58:23 +00:00
|
|
|
m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine;
|
2010-09-20 16:21:47 +00:00
|
|
|
m_ycliphi = m_padToTestPos.y + padHalfsize.y;
|
|
|
|
|
|
|
|
if( !checkLine( startPoint, endPoint ) )
|
|
|
|
return false;
|
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
// Testing the second rectangle dimx , dimy + distToLine
|
2010-09-20 16:21:47 +00:00
|
|
|
m_xcliplo = m_padToTestPos.x - padHalfsize.x;
|
2015-05-17 10:58:23 +00:00
|
|
|
m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine;
|
2010-09-20 16:21:47 +00:00
|
|
|
m_xcliphi = m_padToTestPos.x + padHalfsize.x;
|
2015-05-17 10:58:23 +00:00
|
|
|
m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
|
|
|
if( !checkLine( startPoint, endPoint ) )
|
|
|
|
return false;
|
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
// testing the 4 circles which are the clearance area of each corner:
|
|
|
|
|
|
|
|
// testing the left top corner of the rectangle
|
2010-09-20 16:21:47 +00:00
|
|
|
startPoint.x = m_padToTestPos.x - padHalfsize.x;
|
|
|
|
startPoint.y = m_padToTestPos.y - padHalfsize.y;
|
|
|
|
RotatePoint( &startPoint, m_padToTestPos, orient );
|
|
|
|
RotatePoint( &startPoint, m_segmAngle );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
// testing the right top corner of the rectangle
|
2010-09-20 16:21:47 +00:00
|
|
|
startPoint.x = m_padToTestPos.x + padHalfsize.x;
|
|
|
|
startPoint.y = m_padToTestPos.y - padHalfsize.y;
|
|
|
|
RotatePoint( &startPoint, m_padToTestPos, orient );
|
|
|
|
RotatePoint( &startPoint, m_segmAngle );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
// testing the left bottom corner of the rectangle
|
2010-09-20 16:21:47 +00:00
|
|
|
startPoint.x = m_padToTestPos.x - padHalfsize.x;
|
|
|
|
startPoint.y = m_padToTestPos.y + padHalfsize.y;
|
|
|
|
RotatePoint( &startPoint, m_padToTestPos, orient );
|
|
|
|
RotatePoint( &startPoint, m_segmAngle );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
// testing the right bottom corner of the rectangle
|
2010-09-20 16:21:47 +00:00
|
|
|
startPoint.x = m_padToTestPos.x + padHalfsize.x;
|
|
|
|
startPoint.y = m_padToTestPos.y + padHalfsize.y;
|
|
|
|
RotatePoint( &startPoint, m_padToTestPos, orient );
|
|
|
|
RotatePoint( &startPoint, m_segmAngle );
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2015-05-17 10:58:23 +00:00
|
|
|
if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
2015-08-23 19:40:33 +00:00
|
|
|
case PAD_SHAPE_TRAPEZOID:
|
2017-01-13 17:51:22 +00:00
|
|
|
{
|
2010-09-20 16:21:47 +00:00
|
|
|
wxPoint poly[4];
|
|
|
|
aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );
|
|
|
|
|
|
|
|
// Move shape to m_padToTestPos
|
|
|
|
for( int ii = 0; ii < 4; ii++ )
|
|
|
|
{
|
|
|
|
poly[ii] += m_padToTestPos;
|
|
|
|
RotatePoint( &poly[ii], m_segmAngle );
|
|
|
|
}
|
|
|
|
|
2017-01-13 17:51:22 +00:00
|
|
|
if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ),
|
|
|
|
wxPoint(m_segmLength,0), distToLine ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
return false;
|
2017-01-13 17:51:22 +00:00
|
|
|
}
|
|
|
|
break;
|
2016-04-06 18:15:49 +00:00
|
|
|
|
2017-01-13 17:51:22 +00:00
|
|
|
case PAD_SHAPE_CUSTOM:
|
|
|
|
{
|
|
|
|
SHAPE_POLY_SET polyset;
|
|
|
|
polyset.Append( aPad->GetCustomShapeAsPolygon() );
|
|
|
|
// The pad can be rotated. calculate the coordinates
|
|
|
|
// relatives to the segment being tested
|
|
|
|
// Note, the pad position relative to the segment origin
|
|
|
|
// is m_padToTestPos
|
2017-09-20 08:28:52 +00:00
|
|
|
aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
|
2017-01-13 17:51:22 +00:00
|
|
|
m_padToTestPos, orient );
|
|
|
|
|
|
|
|
// Rotate all coordinates by m_segmAngle, because the segment orient
|
|
|
|
// is m_segmAngle
|
|
|
|
// we are using a horizontal segment for test, because we know here
|
|
|
|
// only the lenght and orientation+ of the segment
|
|
|
|
// therefore all coordinates of the pad to test must be rotated by
|
|
|
|
// m_segmAngle (they are already relative to the segment origin)
|
2017-09-20 08:28:52 +00:00
|
|
|
aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
|
2017-01-13 17:51:22 +00:00
|
|
|
wxPoint( 0, 0 ), m_segmAngle );
|
|
|
|
|
|
|
|
const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
|
|
|
|
|
2018-08-29 07:13:07 +00:00
|
|
|
if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
|
|
|
|
refpoly.PointCount(),
|
|
|
|
wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
|
|
|
|
distToLine ) )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PAD_SHAPE_CHAMFERED_RECT:
|
|
|
|
{
|
2019-05-22 14:47:38 +00:00
|
|
|
auto board = aPad->GetBoard();
|
|
|
|
int maxError = ARC_HIGH_DEF;
|
|
|
|
|
|
|
|
if( board )
|
|
|
|
maxError = board->GetDesignSettings().m_MaxError;
|
|
|
|
|
2018-08-29 07:13:07 +00:00
|
|
|
SHAPE_POLY_SET polyset;
|
|
|
|
// The pad can be rotated. calculate the coordinates
|
|
|
|
// relatives to the segment being tested
|
|
|
|
// Note, the pad position relative to the segment origin
|
|
|
|
// is m_padToTestPos
|
|
|
|
int padRadius = aPad->GetRoundRectCornerRadius();
|
|
|
|
TransformRoundChamferedRectToPolygon( polyset, m_padToTestPos, aPad->GetSize(),
|
|
|
|
aPad->GetOrientation(),
|
|
|
|
padRadius, aPad->GetChamferRectRatio(),
|
2019-05-22 14:47:38 +00:00
|
|
|
aPad->GetChamferPositions(), maxError );
|
2018-08-29 07:13:07 +00:00
|
|
|
// Rotate also coordinates by m_segmAngle, because the segment orient
|
|
|
|
// is m_segmAngle.
|
|
|
|
// we are using a horizontal segment for test, because we know here
|
|
|
|
// only the lenght and orientation of the segment
|
|
|
|
// therefore all coordinates of the pad to test must be rotated by
|
|
|
|
// m_segmAngle (they are already relative to the segment origin)
|
|
|
|
polyset.Rotate( DECIDEG2RAD( -m_segmAngle ), VECTOR2I( 0, 0 ) );
|
|
|
|
|
|
|
|
const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
|
|
|
|
|
2017-01-13 17:51:22 +00:00
|
|
|
if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
|
|
|
|
refpoly.PointCount(),
|
|
|
|
wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
|
|
|
|
distToLine ) )
|
|
|
|
return false;
|
|
|
|
}
|
2016-04-06 18:15:49 +00:00
|
|
|
break;
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function checkMarginToCircle
|
|
|
|
* Check the distance between a circle (round pad, via or round end of track)
|
|
|
|
* and a segment. the segment is expected starting at 0,0, and on the X axis
|
|
|
|
* return true if distance >= aRadius
|
|
|
|
*/
|
|
|
|
bool DRC::checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength )
|
|
|
|
{
|
2017-03-11 17:09:18 +00:00
|
|
|
if( abs( aCentre.y ) >= aRadius ) // trivial case
|
2010-09-20 16:21:47 +00:00
|
|
|
return true;
|
|
|
|
|
2011-05-12 11:06:32 +00:00
|
|
|
// Here, distance between aCentre and X axis is < aRadius
|
2017-03-11 17:09:18 +00:00
|
|
|
if( (aCentre.x > -aRadius ) && ( aCentre.x < (aLength + aRadius) ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
if( (aCentre.x >= 0) && (aCentre.x <= aLength) )
|
|
|
|
return false; // aCentre is between the starting point and the ending point of the segm
|
|
|
|
|
|
|
|
if( aCentre.x > aLength ) // aCentre is after the ending point
|
|
|
|
aCentre.x -= aLength; // move aCentre to the starting point of the segment
|
|
|
|
|
2013-05-01 17:32:36 +00:00
|
|
|
if( EuclideanNorm( aCentre ) < aRadius )
|
2010-09-20 16:21:47 +00:00
|
|
|
// distance between aCentre and the starting point or the ending point is < aRadius
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper function used in checkLine::
|
|
|
|
static inline int USCALE( unsigned arg, unsigned num, unsigned den )
|
|
|
|
{
|
|
|
|
int ii;
|
2018-03-20 05:07:34 +00:00
|
|
|
double result;
|
|
|
|
|
|
|
|
// Trivial check first
|
|
|
|
if( !arg || !num)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// If arg and num are both non-zero but den is zero, we return effective infinite
|
|
|
|
if( !den )
|
|
|
|
return INT_MAX;
|
|
|
|
|
|
|
|
result = ( (double) arg * num ) / den;
|
|
|
|
|
|
|
|
// Ensure that our result doesn't overflow into the sign bit
|
|
|
|
if( result > INT_MAX )
|
|
|
|
return INT_MAX;
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2013-05-04 11:57:09 +00:00
|
|
|
ii = KiROUND( ( (double) arg * num ) / den );
|
2010-09-20 16:21:47 +00:00
|
|
|
return ii;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Helper function checkLine
|
|
|
|
* Test if a line intersects a bounding box (a rectangle)
|
|
|
|
* The rectangle is defined by m_xcliplo, m_ycliplo and m_xcliphi, m_ycliphi
|
|
|
|
* return true if the line from aSegStart to aSegEnd is outside the bounding box
|
|
|
|
*/
|
|
|
|
bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd )
|
|
|
|
{
|
|
|
|
#define WHEN_OUTSIDE return true
|
|
|
|
#define WHEN_INSIDE
|
|
|
|
int temp;
|
|
|
|
|
|
|
|
if( aSegStart.x > aSegEnd.x )
|
2015-06-26 13:41:56 +00:00
|
|
|
std::swap( aSegStart, aSegEnd );
|
2010-09-20 16:21:47 +00:00
|
|
|
|
2018-03-20 05:07:34 +00:00
|
|
|
if( (aSegEnd.x <= m_xcliplo) || (aSegStart.x >= m_xcliphi) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
WHEN_OUTSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegStart.y < aSegEnd.y )
|
|
|
|
{
|
2018-03-20 05:07:34 +00:00
|
|
|
if( (aSegEnd.y <= m_ycliplo) || (aSegStart.y >= m_ycliphi) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
WHEN_OUTSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegStart.y < m_ycliplo )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y),
|
|
|
|
(aSegEnd.y - aSegStart.y) );
|
|
|
|
|
2018-03-20 05:07:34 +00:00
|
|
|
if( (aSegStart.x += temp) >= m_xcliphi )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
WHEN_OUTSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegStart.y = m_ycliplo;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegEnd.y > m_ycliphi )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi),
|
|
|
|
(aSegEnd.y - aSegStart.y) );
|
|
|
|
|
2018-03-20 05:07:34 +00:00
|
|
|
if( (aSegEnd.x -= temp) <= m_xcliplo )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
WHEN_OUTSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegEnd.y = m_ycliphi;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegStart.x < m_xcliplo )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x),
|
|
|
|
(aSegEnd.x - aSegStart.x) );
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegStart.y += temp;
|
|
|
|
aSegStart.x = m_xcliplo;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegEnd.x > m_xcliphi )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi),
|
|
|
|
(aSegEnd.x - aSegStart.x) );
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegEnd.y -= temp;
|
|
|
|
aSegEnd.x = m_xcliphi;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-03-20 05:07:34 +00:00
|
|
|
if( (aSegStart.y <= m_ycliplo) || (aSegEnd.y >= m_ycliphi) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
WHEN_OUTSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegStart.y > m_ycliphi )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi),
|
|
|
|
(aSegStart.y - aSegEnd.y) );
|
|
|
|
|
2018-03-20 05:07:34 +00:00
|
|
|
if( (aSegStart.x += temp) >= m_xcliphi )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
WHEN_OUTSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegStart.y = m_ycliphi;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegEnd.y < m_ycliplo )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y),
|
|
|
|
(aSegStart.y - aSegEnd.y) );
|
|
|
|
|
2018-03-20 05:07:34 +00:00
|
|
|
if( (aSegEnd.x -= temp) <= m_xcliplo )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
WHEN_OUTSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegEnd.y = m_ycliplo;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegStart.x < m_xcliplo )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x),
|
|
|
|
(aSegEnd.x - aSegStart.x) );
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegStart.y -= temp;
|
|
|
|
aSegStart.x = m_xcliplo;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
2011-09-07 19:41:04 +00:00
|
|
|
|
2010-09-20 16:21:47 +00:00
|
|
|
if( aSegEnd.x > m_xcliphi )
|
|
|
|
{
|
2011-09-07 19:41:04 +00:00
|
|
|
temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi),
|
|
|
|
(aSegEnd.x - aSegStart.x) );
|
2010-09-20 16:21:47 +00:00
|
|
|
aSegEnd.y += temp;
|
|
|
|
aSegEnd.x = m_xcliphi;
|
|
|
|
WHEN_INSIDE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-20 05:07:34 +00:00
|
|
|
// Do not divide here to avoid rounding errors
|
|
|
|
if( ( (aSegEnd.x + aSegStart.x) < m_xcliphi * 2 )
|
|
|
|
&& ( (aSegEnd.x + aSegStart.x) > m_xcliplo * 2) \
|
|
|
|
&& ( (aSegEnd.y + aSegStart.y) < m_ycliphi * 2 )
|
|
|
|
&& ( (aSegEnd.y + aSegStart.y) > m_ycliplo * 2 ) )
|
2010-09-20 16:21:47 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
2011-09-07 19:41:04 +00:00
|
|
|
{
|
2010-09-20 16:21:47 +00:00
|
|
|
return true;
|
2011-09-07 19:41:04 +00:00
|
|
|
}
|
2010-09-20 16:21:47 +00:00
|
|
|
}
|