diff options
author | saurabhb17 | 2020-02-26 15:57:49 +0530 |
---|---|---|
committer | saurabhb17 | 2020-02-26 15:57:49 +0530 |
commit | aa35045840b78d3f48212db45da59a2e5c69b223 (patch) | |
tree | 6acee185a4dc19113fcbf0f9a3d6941085dedaf7 /pcbnew/drc_clearance_test_functions.cpp | |
parent | 0db48f6533517ecebfd9f0693f89deca28408b76 (diff) | |
download | KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.gz KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.tar.bz2 KiCad-eSim-aa35045840b78d3f48212db45da59a2e5c69b223.zip |
Added main execs
Diffstat (limited to 'pcbnew/drc_clearance_test_functions.cpp')
-rw-r--r-- | pcbnew/drc_clearance_test_functions.cpp | 1191 |
1 files changed, 1191 insertions, 0 deletions
diff --git a/pcbnew/drc_clearance_test_functions.cpp b/pcbnew/drc_clearance_test_functions.cpp new file mode 100644 index 0000000..39d1b9d --- /dev/null +++ b/pcbnew/drc_clearance_test_functions.cpp @@ -0,0 +1,1191 @@ +/** + * @file drc_clearance_test_functions.cpp + */ + +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2004-2015 Jean-Pierre Charras, jean-pierre.charras@gipsa-lab.inpg.fr + * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com + * Copyright (C) 2015 KiCad Developers, see change_log.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 + */ + +/****************************/ +/* DRC control */ +/****************************/ + +#include <fctsys.h> +#include <wxPcbStruct.h> +#include <trigo.h> + +#include <pcbnew.h> +#include <drc_stuff.h> + +#include <class_board.h> +#include <class_module.h> +#include <class_track.h> +#include <class_zone.h> +#include <class_marker_pcb.h> +#include <math_for_graphics.h> +#include <polygon_test_point_inside.h> + + +/* compare 2 trapezoids (can be rectangle) and return true if distance > aDist + * i.e if for each edge of the first polygon distance from each edge of the other polygon + * is >= aDist + */ +bool trapezoid2trapezoidDRC( wxPoint aTref[4], wxPoint aTcompare[4], int aDist ) +{ + /* Test if one polygon is contained in the other and thus the polygon overlap. + * This case is not covered by the following check if one polygond is + * completely contained in the other (because edges don't intersect)! + */ + if( TestPointInsidePolygon( aTref, 4, aTcompare[0] ) ) + return false; + + if( TestPointInsidePolygon( aTcompare, 4, aTref[0] ) ) + return false; + + int ii, jj, kk, ll; + + for( ii = 0, jj = 3; ii<4; jj = ii, ii++ ) // for all edges in aTref + { + for( kk = 0, ll = 3; kk < 4; ll = kk, kk++ ) // for all edges in aTcompare + { + double d; + int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x, + aTref[ii].y, + aTref[jj].x, + aTref[jj].y, + aTcompare[kk].x, + aTcompare[kk].y, + aTcompare[ll].x, + aTcompare[ll].y, + NULL, NULL, &d ); + if( intersect || (d< aDist) ) + return false; + } + } + + return true; +} + + +/* compare a trapezoids (can be rectangle) and a segment and return true if distance > aDist + */ +bool trapezoid2segmentDRC( wxPoint aTref[4], wxPoint aSegStart, wxPoint aSegEnd, int aDist ) +{ + /* 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)! + */ + if( TestPointInsidePolygon( aTref, 4, aSegStart ) ) + return false; + + int ii, jj; + + for( ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edges in aTref + { + double d; + int intersect = TestForIntersectionOfStraightLineSegments( aTref[ii].x, + aTref[ii].y, + aTref[jj].x, + aTref[jj].y, + aSegStart.x, + aSegStart.y, + aSegEnd.x, + aSegEnd.y, + NULL, NULL, &d ); + if( intersect || (d< aDist) ) + return false; + } + + return true; +} + + +/* compare a trapezoid to a point and return true if distance > aDist + * do not use this function for horizontal or vertical rectangles + * because there is a faster an easier way to compare the distance + */ +bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist ) +{ + /* Test if aPcompare point is contained in the polygon. + * This case is not covered by the following check if this point is inside the polygon + */ + if( TestPointInsidePolygon( aTref, 4, aPcompare ) ) + { + return false; + } + + // Test distance between aPcompare and each segment of the polygon: + for( int ii = 0, jj = 3; ii < 4; jj = ii, ii++ ) // for all edge in polygon + { + if( TestSegmentHit( aTref[ii], aTref[jj], aPcompare, aDist ) ) + return false; + } + + return true; +} + + +bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads ) +{ + TRACK* track; + wxPoint delta; // lenght on X and Y axis of segments + LSET layerMask; + int net_code_ref; + wxPoint shape_pos; + + NETCLASSPTR netclass = aRefSeg->GetNetClass(); + BOARD_DESIGN_SETTINGS& dsnSettings = m_pcb->GetDesignSettings(); + + /* In order to make some calculations more easier or faster, + * pads and tracks coordinates will be made relative to the reference segment origin + */ + wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates + + m_segmEnd = delta = aRefSeg->GetEnd() - origin; + m_segmAngle = 0; + + layerMask = aRefSeg->GetLayerSet(); + net_code_ref = aRefSeg->GetNetCode(); + + // Phase 0 : Test vias + if( aRefSeg->Type() == PCB_VIA_T ) + { + const VIA *refvia = static_cast<const VIA*>( aRefSeg ); + // test if the via size is smaller than minimum + if( refvia->GetViaType() == VIA_MICROVIA ) + { + if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize ) + { + m_currentMarker = fillMarker( refvia, NULL, + DRCE_TOO_SMALL_MICROVIA, m_currentMarker ); + return false; + } + } + else + { + if( refvia->GetWidth() < dsnSettings.m_ViasMinSize ) + { + m_currentMarker = fillMarker( refvia, NULL, + DRCE_TOO_SMALL_VIA, m_currentMarker ); + return false; + } + } + + // 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 + if( refvia->GetDrillValue() > refvia->GetWidth() ) + { + m_currentMarker = fillMarker( refvia, NULL, + DRCE_VIA_HOLE_BIGGER, m_currentMarker ); + return false; + } + + // 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 + if( refvia->GetViaType() == VIA_MICROVIA ) + { + LAYER_ID layer1, layer2; + bool err = true; + + refvia->LayerPair( &layer1, &layer2 ); + + if( layer1 > layer2 ) + std::swap( layer1, layer2 ); + + if( layer2 == B_Cu && layer1 == m_pcb->GetDesignSettings().GetCopperLayerCount() - 2 ) + err = false; + else if( layer1 == F_Cu && layer2 == In1_Cu ) + err = false; + + if( err ) + { + m_currentMarker = fillMarker( refvia, NULL, + DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR, m_currentMarker ); + return false; + } + } + } + else // This is a track segment + { + if( aRefSeg->GetWidth() < dsnSettings.m_TrackMinWidth ) + { + m_currentMarker = fillMarker( aRefSeg, NULL, + DRCE_TOO_SMALL_TRACK_WIDTH, m_currentMarker ); + return false; + } + } + + // 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 : */ + /******************************************/ + + /* 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 + */ + MODULE dummymodule( m_pcb ); // Creates a dummy parent + D_PAD dummypad( &dummymodule ); + + dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers + + // Compute the min distance to pads + if( testPads ) + { + unsigned pad_count = m_pcb->GetPadCount(); + + for( unsigned ii = 0; ii<pad_count; ++ii ) + { + D_PAD* pad = m_pcb->GetPad( ii ); + + /* No problem if pads are on an other layer, + * But if a drill hole exists (a pad on a single layer can have a hole!) + * we must test the hole + */ + if( !( pad->GetLayerSet() & layerMask ).any() ) + { + /* We must test the pad hole. In order to use the function + * checkClearanceSegmToPad(),a pseudo pad is used, with a shape and a + * size like the hole + */ + if( pad->GetDrillSize().x == 0 ) + continue; + + dummypad.SetSize( pad->GetDrillSize() ); + dummypad.SetPosition( pad->GetPosition() ); + dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ? + PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE ); + dummypad.SetOrientation( pad->GetOrientation() ); + + m_padToTestPos = dummypad.GetPosition() - origin; + + if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(), + netclass->GetClearance() ) ) + { + m_currentMarker = fillMarker( aRefSeg, pad, + DRCE_TRACK_NEAR_THROUGH_HOLE, m_currentMarker ); + return false; + } + + continue; + } + + // 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) + if( pad->GetNetCode() // the pad must be connected + && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok + continue; + + // DRC for the pad + shape_pos = pad->ShapePos(); + m_padToTestPos = shape_pos - origin; + + if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) ) + { + m_currentMarker = fillMarker( aRefSeg, pad, + DRCE_TRACK_NEAR_PAD, m_currentMarker ); + return false; + } + } + } + + /***********************************************/ + /* 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 + wxPoint segStartPoint; + wxPoint segEndPoint; + for( track = aStart; track; track = track->Next() ) + { + // No problem if segments have the same net code: + if( net_code_ref == track->GetNetCode() ) + continue; + + // No problem if segment are on different layers : + if( !( layerMask & track->GetLayerSet() ).any() ) + continue; + + // the minimum distance = clearance plus half the reference track + // width plus half the other track's width + int w_dist = aRefSeg->GetClearance( track ); + w_dist += (aRefSeg->GetWidth() + track->GetWidth()) / 2; + + // If the reference segment is a via, we test it here + if( aRefSeg->Type() == PCB_VIA_T ) + { + delta = track->GetEnd() - track->GetStart(); + segStartPoint = aRefSeg->GetStart() - track->GetStart(); + + if( track->Type() == PCB_VIA_T ) + { + // Test distance between two vias, i.e. two circles, trivial case + if( EuclideanNorm( segStartPoint ) < w_dist ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_VIA_NEAR_VIA, m_currentMarker ); + return false; + } + } + else // test via to segment + { + // Compute l'angle du segment a tester; + double angle = ArcTangente( delta.y, delta.x ); + + // Compute new coordinates ( the segment become horizontal) + RotatePoint( &delta, angle ); + RotatePoint( &segStartPoint, angle ); + + if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) ) + { + m_currentMarker = fillMarker( track, aRefSeg, + DRCE_VIA_NEAR_TRACK, m_currentMarker ); + return false; + } + } + + 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 + */ + segStartPoint = track->GetStart() - origin; + segEndPoint = track->GetEnd() - origin; + RotatePoint( &segStartPoint, m_segmAngle ); + RotatePoint( &segEndPoint, m_segmAngle ); + if( track->Type() == PCB_VIA_T ) + { + if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) + continue; + + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_TRACK_NEAR_VIA, m_currentMarker ); + return false; + } + + /* We have changed axis: + * the reference segment is Horizontal. + * 3 cases : the segment to test can be parallel, perpendicular or have an other direction + */ + if( segStartPoint.y == segEndPoint.y ) // parallel segments + { + if( abs( segStartPoint.y ) >= w_dist ) + continue; + + // Ensure segStartPoint.x <= segEndPoint.x + if( segStartPoint.x > segEndPoint.x ) + std::swap( segStartPoint.x, segEndPoint.x ); + + if( segStartPoint.x > (-w_dist) && segStartPoint.x < (m_segmLength + w_dist) ) /* possible error drc */ + { + // the start point is inside the reference range + // X........ + // O--REF--+ + + // Fine test : we consider the rounded shape of each end of the track segment: + if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_TRACK_ENDS1, m_currentMarker ); + return false; + } + + if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_TRACK_ENDS2, m_currentMarker ); + return false; + } + } + + if( segEndPoint.x > (-w_dist) && segEndPoint.x < (m_segmLength + w_dist) ) + { + // the end point is inside the reference range + // .....X + // O--REF--+ + // Fine test : we consider the rounded shape of the ends + if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_TRACK_ENDS3, m_currentMarker ); + return false; + } + + if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_TRACK_ENDS4, m_currentMarker ); + return false; + } + } + + if( segStartPoint.x <=0 && segEndPoint.x >= 0 ) + { + // 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--+ + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_TRACK_SEGMENTS_TOO_CLOSE, m_currentMarker ); + return false; + } + } + else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments + { + if( ( segStartPoint.x <= (-w_dist) ) || ( segStartPoint.x >= (m_segmLength + w_dist) ) ) + continue; + + // Test if segments are crossing + if( segStartPoint.y > segEndPoint.y ) + std::swap( segStartPoint.y, segEndPoint.y ); + + if( (segStartPoint.y < 0) && (segEndPoint.y > 0) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_TRACKS_CROSSING, m_currentMarker ); + return false; + } + + // At this point the drc error is due to an end near a reference segm end + if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_ENDS_PROBLEM1, m_currentMarker ); + return false; + } + if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_ENDS_PROBLEM2, m_currentMarker ); + return false; + } + } + 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 ) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_ENDS_PROBLEM3, m_currentMarker ); + return false; + } + else // The drc error is due to the starting or the ending point of the reference segment + { + // Test the starting and the ending point + segStartPoint = track->GetStart(); + segEndPoint = track->GetEnd(); + delta = segEndPoint - segStartPoint; + + // Compute the segment orientation (angle) en 0,1 degre + double angle = ArcTangente( delta.y, delta.x ); + + // Compute the segment lenght: delta.x = lenght after rotation + RotatePoint( &delta, angle ); + + /* Comute the reference segment coordinates relatives to a + * X axis = current tested segment + */ + wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint; + wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint; + + RotatePoint( &relStartPos, angle ); + RotatePoint( &relEndPos, angle ); + + if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_ENDS_PROBLEM4, m_currentMarker ); + return false; + } + + if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) ) + { + m_currentMarker = fillMarker( aRefSeg, track, + DRCE_ENDS_PROBLEM5, m_currentMarker ); + return false; + } + } + } + } + } + + return true; +} + + +/* test DRC between 2 pads. + * this function can be also used to test DRC between a pas and a hole, + * because a hole is like a round pad. + */ +bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad ) +{ + int dist; + + double pad_angle; + + // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad + int dist_min = aRefPad->GetClearance( aPad ); + + // relativePadPos is the aPad shape position relative to the aRefPad shape position + wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos(); + + dist = KiROUND( EuclideanNorm( relativePadPos ) ); + + // Quick test: Clearance is OK if the bounding circles are further away than "dist_min" + if( (dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius()) >= dist_min ) + 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 + * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL. + * if aRefPad = TRAP. and aPad = RECT, also swap pads + * Swap aRefPad and aPad if needed + */ + bool swap_pads; + swap_pads = false; + + // swap pads to make comparisons easier + // priority is aRefPad = ROUND then OVAL then RECT then other + if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE ) + { + // pad ref shape is here oval, rect or trapezoid + switch( aPad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + swap_pads = true; + break; + + case PAD_SHAPE_OVAL: + swap_pads = true; + break; + + case PAD_SHAPE_RECT: + if( aRefPad->GetShape() != PAD_SHAPE_OVAL ) + swap_pads = true; + break; + + default: + break; + } + } + + if( swap_pads ) + { + std::swap( aRefPad, aPad ); + relativePadPos = -relativePadPos; + } + + /* 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. + * Therefore, if aRefPad is a PAD_SHAPE_SHAPE_RECT or a PAD_SHAPE_TRAPEZOID, + * aPad is also a PAD_SHAPE_RECT or a PAD_SHAPE_TRAPEZOID + */ + bool diag = true; + + switch( aRefPad->GetShape() ) + { + case PAD_SHAPE_CIRCLE: + + /* One can use checkClearanceSegmToPad to test clearance + * aRefPad is like a track segment with a null length and a witdth = GetSize().x + */ + m_segmLength = 0; + m_segmAngle = 0; + + m_segmEnd.x = m_segmEnd.y = 0; + + m_padToTestPos = relativePadPos; + diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min ); + break; + + case PAD_SHAPE_RECT: + // pad_angle = pad orient relative to the aRefPad orient + pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation(); + NORMALIZE_ANGLE_POS( pad_angle ); + + if( aPad->GetShape() == PAD_SHAPE_RECT ) + { + wxSize size = aPad->GetSize(); + + // The trivial case is if both rects are rotated by multiple of 90 deg + // Most of time this is the case, and the test is fast + if( ( (aRefPad->GetOrientation() == 0) || (aRefPad->GetOrientation() == 900) + || (aRefPad->GetOrientation() == 1800) || (aRefPad->GetOrientation() == 2700) ) + && ( (aPad->GetOrientation() == 0) || (aPad->GetOrientation() == 900) || (aPad->GetOrientation() == 1800) + || (aPad->GetOrientation() == 2700) ) ) + { + if( (pad_angle == 900) || (pad_angle == 2700) ) + { + std::swap( size.x, size.y ); + } + + // Test DRC: + diag = false; + RotatePoint( &relativePadPos, aRefPad->GetOrientation() ); + relativePadPos.x = std::abs( relativePadPos.x ); + relativePadPos.y = std::abs( relativePadPos.y ); + + if( ( relativePadPos.x - ( (size.x + aRefPad->GetSize().x) / 2 ) ) >= dist_min ) + diag = true; + + if( ( relativePadPos.y - ( (size.y + aRefPad->GetSize().y) / 2 ) ) >= dist_min ) + diag = true; + } + else // at least one pad has any other orient. Test is more tricky + { // Use the trapezoid2trapezoidDRC which also compare 2 rectangles with any orientation + wxPoint polyref[4]; // Shape of aRefPad + wxPoint polycompare[4]; // Shape of aPad + aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); + aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); + + // Move aPad shape to relativePadPos + for( int ii = 0; ii < 4; ii++ ) + polycompare[ii] += relativePadPos; + + // And now test polygons: + if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) ) + diag = false; + } + } + else if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) + { + wxPoint polyref[4]; // Shape of aRefPad + wxPoint polycompare[4]; // Shape of aPad + aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); + aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); + + // Move aPad shape to relativePadPos + for( int ii = 0; ii < 4; ii++ ) + polycompare[ii] += relativePadPos; + + // And now test polygons: + if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) ) + diag = false; + } + else + { + // Should not occur, because aPad and aRefPad are swapped + // to have only aPad shape RECT or TRAP and aRefPad shape TRAP or RECT. + wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad ref RECT @ %d, %d to pad shape %d @ %d, %d"), + aRefPad->GetPosition().x, aRefPad->GetPosition().y, + aPad->GetShape(), aPad->GetPosition().x, aPad->GetPosition().y ); + } + break; + + case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */ + { + /* Create a track segment with same dimensions as the oval aRefPad + * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance + */ + int segm_width; + m_segmAngle = aRefPad->GetOrientation(); // Segment orient. + + if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment + { + segm_width = aRefPad->GetSize().y; + m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y; + } + else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg + { + segm_width = aRefPad->GetSize().x; + m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x; + 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 + + 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 ); + + // move pad position relative to the segment origin + m_padToTestPos = relativePadPos - segstart; + + // Use segment to pad check to test the second pad: + diag = checkClearanceSegmToPad( aPad, segm_width, dist_min ); + break; + } + + case PAD_SHAPE_TRAPEZOID: + + // at this point, aPad is also a trapezoid, because all other shapes + // have priority, and are already tested + wxASSERT( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ); + { + wxPoint polyref[4]; // Shape of aRefPad + wxPoint polycompare[4]; // Shape of aPad + aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); + aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() ); + + // Move aPad shape to relativePadPos + for( int ii = 0; ii < 4; ii++ ) + polycompare[ii] += relativePadPos; + + // And now test polygons: + if( !trapezoid2trapezoidDRC( polyref, polycompare, dist_min ) ) + diag = false; + } + break; + + default: + wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape" ) ); + 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 + * and its orientation is m_segmAngle (m_segmAngle must be already initialized) + * and have aSegmentWidth. + */ +bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist ) +{ + wxSize padHalfsize; // half dimension of the pad + wxPoint startPoint, endPoint; + + int segmHalfWidth = aSegmentWidth / 2; + int distToLine = segmHalfWidth + aMinDist; + + padHalfsize.x = aPad->GetSize().x >> 1; + padHalfsize.y = aPad->GetSize().y >> 1; + + if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size + { + 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 + } + + if( aPad->GetShape() == PAD_SHAPE_CIRCLE ) + { + /* 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 ); + return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength ); + } + + /* 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. + */ + 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; + + startPoint.x = startPoint.y = 0; + endPoint = m_segmEnd; + + double orient = aPad->GetOrientation(); + + 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. + */ + switch( aPad->GetShape() ) + { + default: + return false; + + case PAD_SHAPE_OVAL: + { + /* an oval is a complex shape, but is a rectangle and 2 circles + * these 3 basic shapes are more easy to test. + * + * In calculations we are using a vertical oval shape + * (i.e. a vertical rounded segment) + * for horizontal oval shapes, swap x and y size and rotate the shape + */ + if( padHalfsize.x > padHalfsize.y ) + { + std::swap( padHalfsize.x, padHalfsize.y ); + orient = AddAngles( orient, 900 ); + } + + // here, padHalfsize.x is the radius of rounded ends. + + int deltay = padHalfsize.y - padHalfsize.x; + // here: padHalfsize.x = radius, + // deltay = dist between the centre pad and the centre of a rounded end + + // Test the rectangular area between the two circles (the rounded ends) + m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x; + m_ycliplo = m_padToTestPos.y - deltay; + m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x; + m_ycliphi = m_padToTestPos.y + deltay; + + if( !checkLine( startPoint, endPoint ) ) + { + return false; + } + + // test the first circle + startPoint.x = m_padToTestPos.x; // startPoint = centre of the upper circle of the oval shape + startPoint.y = m_padToTestPos.y + deltay; + + // Calculate the actual position of the circle, given the pad orientation: + RotatePoint( &startPoint, m_padToTestPos, orient ); + + // Calculate the actual position of the circle in the new X,Y axis: + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, padHalfsize.x + distToLine, m_segmLength ) ) + { + return false; + } + + // test the second circle + startPoint.x = m_padToTestPos.x; // startPoint = centre of the lower circle of the oval shape + startPoint.y = m_padToTestPos.y - deltay; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, padHalfsize.x + distToLine, m_segmLength ) ) + { + return false; + } + } + break; + + case PAD_SHAPE_RECT: + // 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; + m_ycliplo = m_padToTestPos.y - padHalfsize.y; + m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine; + m_ycliphi = m_padToTestPos.y + padHalfsize.y; + + if( !checkLine( startPoint, endPoint ) ) + return false; + + // Testing the second rectangle dimx , dimy + distToLine + m_xcliplo = m_padToTestPos.x - padHalfsize.x; + m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine; + m_xcliphi = m_padToTestPos.x + padHalfsize.x; + m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine; + + if( !checkLine( startPoint, endPoint ) ) + return false; + + // testing the 4 circles which are the clearance area of each corner: + + // testing the left top corner of the rectangle + startPoint.x = m_padToTestPos.x - padHalfsize.x; + startPoint.y = m_padToTestPos.y - padHalfsize.y; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) ) + return false; + + // testing the right top corner of the rectangle + startPoint.x = m_padToTestPos.x + padHalfsize.x; + startPoint.y = m_padToTestPos.y - padHalfsize.y; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) ) + return false; + + // testing the left bottom corner of the rectangle + startPoint.x = m_padToTestPos.x - padHalfsize.x; + startPoint.y = m_padToTestPos.y + padHalfsize.y; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) ) + return false; + + // testing the right bottom corner of the rectangle + startPoint.x = m_padToTestPos.x + padHalfsize.x; + startPoint.y = m_padToTestPos.y + padHalfsize.y; + RotatePoint( &startPoint, m_padToTestPos, orient ); + RotatePoint( &startPoint, m_segmAngle ); + + if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) ) + return false; + + break; + + case PAD_SHAPE_TRAPEZOID: + { + 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 ); + } + + if( !trapezoid2segmentDRC( poly, wxPoint( 0, 0 ), wxPoint(m_segmLength,0), distToLine ) ) + return false; + } + break; + } + + 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 ) +{ + if( abs( aCentre.y ) > aRadius ) // trivial case + return true; + + // Here, distance between aCentre and X axis is < aRadius + if( (aCentre.x >= -aRadius ) && ( aCentre.x <= (aLength + aRadius) ) ) + { + 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 + + if( EuclideanNorm( aCentre ) < aRadius ) + // 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; + + ii = KiROUND( ( (double) arg * num ) / den ); + 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 ) + std::swap( aSegStart, aSegEnd ); + + if( (aSegEnd.x < m_xcliplo) || (aSegStart.x > m_xcliphi) ) + { + WHEN_OUTSIDE; + } + + if( aSegStart.y < aSegEnd.y ) + { + if( (aSegEnd.y < m_ycliplo) || (aSegStart.y > m_ycliphi) ) + { + WHEN_OUTSIDE; + } + + if( aSegStart.y < m_ycliplo ) + { + temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y), + (aSegEnd.y - aSegStart.y) ); + + if( (aSegStart.x += temp) > m_xcliphi ) + { + WHEN_OUTSIDE; + } + + aSegStart.y = m_ycliplo; + WHEN_INSIDE; + } + + if( aSegEnd.y > m_ycliphi ) + { + temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi), + (aSegEnd.y - aSegStart.y) ); + + if( (aSegEnd.x -= temp) < m_xcliplo ) + { + WHEN_OUTSIDE; + } + + aSegEnd.y = m_ycliphi; + WHEN_INSIDE; + } + + if( aSegStart.x < m_xcliplo ) + { + temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x), + (aSegEnd.x - aSegStart.x) ); + aSegStart.y += temp; + aSegStart.x = m_xcliplo; + WHEN_INSIDE; + } + + if( aSegEnd.x > m_xcliphi ) + { + temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi), + (aSegEnd.x - aSegStart.x) ); + aSegEnd.y -= temp; + aSegEnd.x = m_xcliphi; + WHEN_INSIDE; + } + } + else + { + if( (aSegStart.y < m_ycliplo) || (aSegEnd.y > m_ycliphi) ) + { + WHEN_OUTSIDE; + } + + if( aSegStart.y > m_ycliphi ) + { + temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi), + (aSegStart.y - aSegEnd.y) ); + + if( (aSegStart.x += temp) > m_xcliphi ) + { + WHEN_OUTSIDE; + } + + aSegStart.y = m_ycliphi; + WHEN_INSIDE; + } + + if( aSegEnd.y < m_ycliplo ) + { + temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y), + (aSegStart.y - aSegEnd.y) ); + + if( (aSegEnd.x -= temp) < m_xcliplo ) + { + WHEN_OUTSIDE; + } + + aSegEnd.y = m_ycliplo; + WHEN_INSIDE; + } + + if( aSegStart.x < m_xcliplo ) + { + temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x), + (aSegEnd.x - aSegStart.x) ); + aSegStart.y -= temp; + aSegStart.x = m_xcliplo; + WHEN_INSIDE; + } + + if( aSegEnd.x > m_xcliphi ) + { + temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi), + (aSegEnd.x - aSegStart.x) ); + aSegEnd.y += temp; + aSegEnd.x = m_xcliphi; + WHEN_INSIDE; + } + } + + if( ( (aSegEnd.x + aSegStart.x) / 2 <= m_xcliphi ) + && ( (aSegEnd.x + aSegStart.x) / 2 >= m_xcliplo ) \ + && ( (aSegEnd.y + aSegStart.y) / 2 <= m_ycliphi ) + && ( (aSegEnd.y + aSegStart.y) / 2 >= m_ycliplo ) ) + { + return false; + } + else + { + return true; + } +} |