void DRC::testTexts() { std::vector<wxPoint> textShape; // a buffer to store the text shape (set of segments) std::vector<D_PAD*> padList = m_pcb->GetPads(); // Test text areas for vias, tracks and pads inside text areas for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() ) { // Drc test only items on copper layers if( ! IsCopperLayer( item->GetLayer() ) ) continue; // only texts on copper layers are tested if( item->Type() != PCB_TEXT_T ) continue; textShape.clear(); // So far the bounding box makes up the text-area TEXTE_PCB* text = (TEXTE_PCB*) item; text->TransformTextShapeToSegmentList( textShape ); if( textShape.size() == 0 ) // Should not happen (empty text?) continue; for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() ) { if( ! track->IsOnLayer( item->GetLayer() ) ) continue; // Test the distance between each segment and the current track/via int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 + track->GetClearance(NULL); if( track->Type() == PCB_TRACE_T ) { SEG segref( track->GetStart(), track->GetEnd() ); // Error condition: Distance between text segment and track segment is // smaller than the clearance of the segment for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); int dist = segref.Distance( segtest ); if( dist < min_dist ) { addMarkerToPcb( fillMarker( track, text, DRCE_TRACK_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } else if( track->Type() == PCB_VIA_T ) { // Error condition: Distance between text segment and via is // smaller than the clearance of the via for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { SEG segtest( textShape[jj], textShape[jj+1] ); if( segtest.PointCloserThan( track->GetPosition(), min_dist ) ) { addMarkerToPcb( fillMarker( track, text, DRCE_VIA_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } // Test pads for( unsigned ii = 0; ii < padList.size(); ii++ ) { D_PAD* pad = padList[ii]; if( ! pad->IsOnLayer( item->GetLayer() ) ) continue; wxPoint shape_pos = pad->ShapePos(); for( unsigned jj = 0; jj < textShape.size(); jj += 2 ) { /* In order to make some calculations more easier or faster, * pads and tracks coordinates will be made relative * to the segment origin */ wxPoint origin = textShape[jj]; // origin will be the origin of other coordinates m_segmEnd = textShape[jj+1] - origin; wxPoint delta = m_segmEnd; m_segmAngle = 0; // for a non horizontal or vertical segment Compute the segment angle // in tenths of degrees and its length if( delta.x || delta.y ) // delta.x == delta.y == 0 for vias { // 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; m_padToTestPos = shape_pos - origin; if( !checkClearanceSegmToPad( pad, text->GetThickness(), pad->GetClearance(NULL) ) ) { addMarkerToPcb( fillMarker( pad, text, DRCE_PAD_INSIDE_TEXT, m_currentMarker ) ); m_currentMarker = nullptr; break; } } } } }
bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads ) { TRACK* track; wxPoint delta; // length 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; } if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_TOO_SMALL_MICROVIA_DRILL, m_currentMarker ); return false; } } else { if( refvia->GetWidth() < dsnSettings.m_ViasMinSize ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_TOO_SMALL_VIA, m_currentMarker ); return false; } if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill ) { m_currentMarker = fillMarker( refvia, NULL, DRCE_TOO_SMALL_VIA_DRILL, 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; // 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; // 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 length: delta.x = length 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 pad and a hole, * because a hole is like a round or oval 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 // 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 if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE ) { // pad ref shape is here oval, rect, roundrect, trapezoid or custom switch( aPad->GetShape() ) { case PAD_SHAPE_CIRCLE: swap_pads = true; break; case PAD_SHAPE_OVAL: swap_pads = true; break; case PAD_SHAPE_RECT: case PAD_SHAPE_ROUNDRECT: if( aRefPad->GetShape() != PAD_SHAPE_OVAL ) swap_pads = true; break; default: break; } } if( swap_pads ) { std::swap( aRefPad, aPad ); relativePadPos = -relativePadPos; } // 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; /* 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_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID, * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT 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_TRAPEZOID: case PAD_SHAPE_ROUNDRECT: 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( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT ) { int padRadius = aRefPad->GetRoundRectCornerRadius(); dist_min += padRadius; GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(), aRefPad->GetOrientation() ); } else aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() ); switch( aPad->GetShape() ) { case PAD_SHAPE_ROUNDRECT: case PAD_SHAPE_RECT: case PAD_SHAPE_TRAPEZOID: if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT ) { int padRadius = aPad->GetRoundRectCornerRadius(); dist_min += padRadius; GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(), aPad->GetOrientation() ); } else { 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( polysetref.OutlineCount() ) { const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 ); // And now test polygons: if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(), polycompare, 4, dist_min ) ) diag = false; } else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) ) diag = false; break; default: wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() ); break; } 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; } default: wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) ); break; } return diag; }
/* 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 clerance 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_CIRCLE or PAD_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_CIRCLE ) { // pad ref shape is here oval, rect or trapezoid switch( aPad->GetShape() ) { case PAD_CIRCLE: swap_pads = true; break; case PAD_OVAL: swap_pads = true; break; case PAD_RECT: if( aRefPad->GetShape() != PAD_OVAL ) swap_pads = true; break; default: break; } } if( swap_pads ) { EXCHG( aRefPad, aPad ); relativePadPos = -relativePadPos; } /* Because pad exchange, aRefPad shape is PAD_CIRCLE or PAD_OVAL, * if one of the 2 pads was a PAD_CIRCLE or PAD_OVAL. * Therefore, if aRefPad is a PAD_RECT or a PAD_TRAPEZOID, * aPad is also a PAD_RECT or a PAD_TRAPEZOID */ bool diag = true; switch( aRefPad->GetShape() ) { case PAD_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_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_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) ) { EXCHG( 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_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_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_TRAPEZOID: // at this point, aPad is also a trapezoid, because all other shapes // have priority, and are already tested wxASSERT( aPad->GetShape() == PAD_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; }