コード例 #1
0
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;
}
コード例 #4
0
/* 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;
}