/* Creates the footprint shape list.
 * Since module shape is customizable after the placement we cannot share them;
 * instead we opt for the one-module-one-shape-one-component-one-device approach
 */
static void CreateShapesSection( FILE* aFile, BOARD* aPcb )
{
    MODULE*     module;
    D_PAD*      pad;
    const char* layer;
    wxString    pinname;
    const char* mirror = "0";

    fputs( "$SHAPES\n", aFile );

    const LSET all_cu = LSET::AllCuMask();

    for( module = aPcb->m_Modules; module; module = module->Next() )
    {
        FootprintWriteShape( aFile, module );

        for( pad = module->Pads(); pad; pad = pad->Next() )
        {
            /* Funny thing: GenCAD requires the pad side even if you use
             *  padstacks (which are theorically optional but gerbtools
             *requires* them). Now the trouble thing is that 'BOTTOM'
             *  is interpreted by someone as a padstack flip even
             *  if the spec explicitly says it's not... */
            layer = "ALL";

            if( ( pad->GetLayerSet() & all_cu ) == LSET( B_Cu ) )
            {
                layer = module->GetFlag() ? "TOP" : "BOTTOM";
            }
            else if( ( pad->GetLayerSet() & all_cu ) == LSET( F_Cu ) )
            {
                layer = module->GetFlag() ? "BOTTOM" : "TOP";
            }

            pad->StringPadName( pinname );

            if( pinname.IsEmpty() )
                pinname = wxT( "none" );

            double orient = pad->GetOrientation() - module->GetOrientation();
            NORMALIZE_ANGLE_POS( orient );

            // Bottom side modules use the flipped padstack
            fprintf( aFile, (module->GetFlag()) ?
                     "PIN %s PAD%dF %g %g %s %g %s\n" :
                     "PIN %s PAD%d %g %g %s %g %s\n",
                     TO_UTF8( pinname ), pad->GetSubRatsnest(),
                     pad->GetPos0().x / SCALE_FACTOR,
                     -pad->GetPos0().y / SCALE_FACTOR,
                     layer, orient / 10.0, mirror );
        }
    }

    fputs( "$ENDSHAPES\n\n", aFile );
}
/* Extract the D356 record from the modules (pads) */
static void build_pad_testpoints( BOARD *aPcb,
    std::vector <D356_RECORD>& aRecords )
{
    wxPoint origin = aPcb->GetAuxOrigin();

    for( MODULE *module = aPcb->m_Modules;
        module; module = module->Next() )
    {
        for( D_PAD *pad = module->Pads();  pad; pad = pad->Next() )
        {
            D356_RECORD rk;
            rk.access = compute_pad_access_code( aPcb, pad->GetLayerSet() );

            // It could be a mask only pad, we only handle pads with copper here
            if( rk.access != -1 )
            {
                rk.netname = pad->GetNetname();
                rk.refdes = module->GetReference();
                pad->StringPadName( rk.pin );
                rk.midpoint = false; // XXX MAYBE need to be computed (how?)
                const wxSize& drill = pad->GetDrillSize();
                rk.drill = std::min( drill.x, drill.y );
                rk.hole = (rk.drill != 0);
                rk.smd = pad->GetAttribute() == PAD_ATTRIB_SMD;
                rk.mechanical = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED);
                rk.x_location = pad->GetPosition().x - origin.x;
                rk.y_location = origin.y - pad->GetPosition().y;
                rk.x_size = pad->GetSize().x;

                // Rule: round pads have y = 0
                if( pad->GetShape() == PAD_SHAPE_CIRCLE )
                    rk.y_size = 0;
                else
                    rk.y_size = pad->GetSize().y;

                rk.rotation = -KiROUND( pad->GetOrientation() ) / 10;
                if( rk.rotation < 0 ) rk.rotation += 360;

                // the value indicates which sides are *not* accessible
                rk.soldermask = 3;
                if( pad->GetLayerSet()[F_Mask] )
                    rk.soldermask &= ~1;
                if( pad->GetLayerSet()[B_Mask] )
                    rk.soldermask &= ~2;

                aRecords.push_back( rk );
            }
        }
    }
}
/* Explores the list of pads and adds to m_PadsConnected member
 * of each pad pads connected to
 * Here, connections are due to intersecting pads, not tracks
 */
void CONNECTIONS::SearchConnectionsPadsToIntersectingPads()
{
    std::vector<CONNECTED_POINT*> candidates;

    BuildPadsCandidatesList();

    for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
    {
        D_PAD* pad = m_sortedPads[ii];

        pad->m_PadsConnected.clear();
        candidates.clear();

        CollectItemsNearTo( candidates, pad->ShapePos(), pad->GetBoundingRadius() );

        // add pads to pad.m_PadsConnected, if they are connected
        for( unsigned jj = 0; jj < candidates.size(); jj++ )
        {
            CONNECTED_POINT* item = candidates[jj];

            D_PAD* candidate_pad = item->GetPad();

            if( pad == candidate_pad )
                continue;

            if( !( pad->GetLayerSet() & candidate_pad->GetLayerSet() ).any() )
                continue;
            if( pad->HitTest( item->GetPoint() ) )
            {
                pad->m_PadsConnected.push_back( candidate_pad );
            }
        }
    }
}
/* Explores the list of pads
 * Adds to m_PadsConnected member of each track the pad(s) connected to
 * Adds to m_TracksConnected member of each pad the track(s) connected to
 * D_PAD::m_TracksConnected is cleared before adding items
 * TRACK::m_PadsConnected is not cleared
 */
void CONNECTIONS::SearchTracksConnectedToPads( bool add_to_padlist, bool add_to_tracklist)
{
    std::vector<CONNECTED_POINT*> candidates;

    for( unsigned ii = 0; ii < m_sortedPads.size(); ii++ )
    {
        D_PAD * pad = m_sortedPads[ii];
        pad->m_TracksConnected.clear();
        candidates.clear();

        CollectItemsNearTo( candidates, pad->GetPosition(), pad->GetBoundingRadius() );

        // add this pad to track.m_PadsConnected, if it is connected
        for( unsigned jj = 0; jj < candidates.size(); jj++ )
        {
            CONNECTED_POINT* cp_item = candidates[jj];

            if( !( pad->GetLayerSet() & cp_item->GetTrack()->GetLayerSet() ).any() )
                continue;

            if( pad->HitTest( cp_item->GetPoint() ) )
            {
                if( add_to_padlist )
                    cp_item->GetTrack()->m_PadsConnected.push_back( pad );

                if( add_to_tracklist )
                    pad->m_TracksConnected.push_back( cp_item->GetTrack() );
            }
        }
    }
}
unsigned MODULE::GetUniquePadCount( INCLUDE_NPTH_T aIncludeNPTH ) const
{
    std::set<wxUint32> usedNames;

    // Create a set of used pad numbers
    for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
    {
        // Skip pads not on copper layers (used to build complex
        // solder paste shapes for instance)
        if( ( pad->GetLayerSet() & LSET::AllCuMask() ).none() )
            continue;

        // Skip pads with no name, because they are usually "mechanical"
        // pads, not "electrical" pads
        if( pad->GetPadName().IsEmpty() )
            continue;

        if( !aIncludeNPTH )
        {
            // skip NPTH
            if( pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
            {
                continue;
            }
        }

        usedNames.insert( pad->GetPackedPadName() );
    }

    return usedNames.size();
}
Example #6
0
void D_PAD::ImportSettingsFromMaster( const D_PAD& aMasterPad )
{
    SetShape( aMasterPad.GetShape() );
    SetLayerSet( aMasterPad.GetLayerSet() );
    SetAttribute( aMasterPad.GetAttribute() );

    // The pad orientation, for historical reasons is the
    // pad rotation + parent rotation.
    // So we have to manage this parent rotation
    double pad_rot = aMasterPad.GetOrientation();

    if( aMasterPad.GetParent() )
        pad_rot -= aMasterPad.GetParent()->GetOrientation();

    if( GetParent() )
        pad_rot += GetParent()->GetOrientation();

    SetOrientation( pad_rot );

    SetSize( aMasterPad.GetSize() );
    SetDelta( wxSize( 0, 0 ) );
    SetOffset( aMasterPad.GetOffset() );
    SetDrillSize( aMasterPad.GetDrillSize() );
    SetDrillShape( aMasterPad.GetDrillShape() );
    SetRoundRectRadiusRatio( aMasterPad.GetRoundRectRadiusRatio() );

    switch( aMasterPad.GetShape() )
    {
    case PAD_SHAPE_TRAPEZOID:
        SetDelta( aMasterPad.GetDelta() );
        break;

    case PAD_SHAPE_CIRCLE:
        // ensure size.y == size.x
        SetSize( wxSize( GetSize().x, GetSize().x ) );
        break;

    default:
        ;
    }

    switch( aMasterPad.GetAttribute() )
    {
    case PAD_ATTRIB_SMD:
    case PAD_ATTRIB_CONN:
        // These pads do not have hole (they are expected to be only on one
        // external copper layer)
        SetDrillSize( wxSize( 0, 0 ) );
        break;

    default:
        ;
    }

    // Add or remove custom pad shapes:
    SetPrimitives( aMasterPad.GetPrimitives() );
    SetAnchorPadShape( aMasterPad.GetAnchorPadShape() );
    MergePrimitivesAsPolygon();
}
D_PAD* MODULE::GetPad( const wxPoint& aPosition, LSET aLayerMask )
{
    for( D_PAD* pad = m_Pads;  pad;  pad = pad->Next() )
    {
        // ... and on the correct layer.
        if( !( pad->GetLayerSet() & aLayerMask ).any() )
            continue;

        if( pad->HitTest( aPosition ) )
            return pad;
    }

    return NULL;
}
/* Trace the pads of a module in sketch mode.
 * Used to display pads when when the module visibility is set to not visible
 * and we want to see pad through.
 * The pads must appear on the layers selected in LayerMask
 */
static void Trace_Pads_Only( EDA_DRAW_PANEL* panel, wxDC* DC, MODULE* aModule,
                             int ox, int oy, LSET aLayerMask, GR_DRAWMODE draw_mode )
{
    auto displ_opts = (PCB_DISPLAY_OPTIONS*)( panel->GetDisplayOptions() );

    int tmp = displ_opts->m_DisplayPadFill;

    displ_opts->m_DisplayPadFill = false;

    // Draw pads.
    for( D_PAD* pad = aModule->PadsList(); pad; pad = pad->Next() )
    {
        if( (pad->GetLayerSet() & aLayerMask) == 0 )
            continue;

        pad->Draw( panel, DC, draw_mode, wxPoint( ox, oy ) );
    }

    displ_opts->m_DisplayPadFill = tmp;
}
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;
}
void DIALOG_PAD_PROPERTIES::PadPropertiesAccept( wxCommandEvent& event )
{
    if( !padValuesOK() )
        return;

    bool rastnestIsChanged = false;
    int  isign = m_isFlipped ? -1 : 1;

    transferDataToPad( m_padMaster );
    // m_padMaster is a pattern: ensure there is no net for this pad:
    m_padMaster->SetNetCode( NETINFO_LIST::UNCONNECTED );

    if( m_currentPad )   // Set current Pad parameters
    {
        wxSize  size;
        MODULE* module = m_currentPad->GetParent();

        m_parent->SaveCopyInUndoList( module, UR_CHANGED );
        module->SetLastEditTime();

        // redraw the area where the pad was, without pad (delete pad on screen)
        m_currentPad->SetFlags( DO_NOT_DRAW );
        m_parent->GetCanvas()->RefreshDrawingRect( m_currentPad->GetBoundingBox() );
        m_currentPad->ClearFlags( DO_NOT_DRAW );

        // Update values
        m_currentPad->SetShape( m_padMaster->GetShape() );
        m_currentPad->SetAttribute( m_padMaster->GetAttribute() );

        if( m_currentPad->GetPosition() != m_padMaster->GetPosition() )
        {
            m_currentPad->SetPosition( m_padMaster->GetPosition() );
            rastnestIsChanged = true;
        }

        // compute the pos 0 value, i.e. pad position for module with orientation = 0
        // i.e. relative to module origin (module position)
        wxPoint pt = m_currentPad->GetPosition() - module->GetPosition();

        RotatePoint( &pt, -module->GetOrientation() );

        m_currentPad->SetPos0( pt );

        m_currentPad->SetOrientation( m_padMaster->GetOrientation() * isign + module->GetOrientation() );

        m_currentPad->SetSize( m_padMaster->GetSize() );

        size = m_padMaster->GetDelta();
        size.y *= isign;
        m_currentPad->SetDelta( size );

        m_currentPad->SetDrillSize( m_padMaster->GetDrillSize() );
        m_currentPad->SetDrillShape( m_padMaster->GetDrillShape() );

        wxPoint offset = m_padMaster->GetOffset();
        offset.y *= isign;
        m_currentPad->SetOffset( offset );

        m_currentPad->SetPadToDieLength( m_padMaster->GetPadToDieLength() );

        if( m_currentPad->GetLayerSet() != m_padMaster->GetLayerSet() )
        {
            rastnestIsChanged = true;
            m_currentPad->SetLayerSet( m_padMaster->GetLayerSet() );
        }

        if( m_isFlipped )
            m_currentPad->SetLayerSet( FlipLayerMask( m_currentPad->GetLayerSet() ) );

        m_currentPad->SetPadName( m_padMaster->GetPadName() );

        wxString padNetname;

        // For PAD_HOLE_NOT_PLATED, ensure there is no net name selected
        if( m_padMaster->GetAttribute() != PAD_HOLE_NOT_PLATED  )
            padNetname = m_PadNetNameCtrl->GetValue();

        if( m_currentPad->GetNetname() != padNetname )
        {
            const NETINFO_ITEM* netinfo = m_board->FindNet( padNetname );

            if( !padNetname.IsEmpty() &&  netinfo == NULL )
            {
                DisplayError( NULL, _( "Unknown netname, netname not changed" ) );
            }
            else
            {
                rastnestIsChanged = true;
                m_currentPad->SetNetCode( netinfo->GetNet() );
            }
        }

        m_currentPad->SetLocalClearance( m_padMaster->GetLocalClearance() );
        m_currentPad->SetLocalSolderMaskMargin( m_padMaster->GetLocalSolderMaskMargin() );
        m_currentPad->SetLocalSolderPasteMargin( m_padMaster->GetLocalSolderPasteMargin() );
        m_currentPad->SetLocalSolderPasteMarginRatio( m_padMaster->GetLocalSolderPasteMarginRatio() );
        m_currentPad->SetZoneConnection( m_padMaster->GetZoneConnection() );
        m_currentPad->SetThermalWidth( m_padMaster->GetThermalWidth() );
        m_currentPad->SetThermalGap( m_padMaster->GetThermalGap() );

        module->CalculateBoundingBox();
        m_parent->SetMsgPanel( m_currentPad );

        // redraw the area where the pad was
        m_parent->GetCanvas()->RefreshDrawingRect( m_currentPad->GetBoundingBox() );
        m_parent->OnModify();
    }

    EndModal( wxID_OK );

    if( rastnestIsChanged )  // The net ratsnest must be recalculated
        m_board->m_Status_Pcb = 0;
}
// Emit PADS and PADSTACKS. They are sorted and emitted uniquely.
// Via name is synthesized from their attributes, pads are numbered
static void CreatePadsShapesSection( FILE* aFile, BOARD* aPcb )
{
    std::vector<D_PAD*> pads;
    std::vector<D_PAD*> padstacks;
    std::vector<VIA*>   vias;
    std::vector<VIA*>   viastacks;

    padstacks.resize( 1 ); // We count pads from 1

    // The master layermask (i.e. the enabled layers) for padstack generation
    LSET    master_layermask = aPcb->GetDesignSettings().GetEnabledLayers();
    int     cu_count = aPcb->GetCopperLayerCount();

    fputs( "$PADS\n", aFile );

    // Enumerate and sort the pads
    if( aPcb->GetPadCount() > 0 )
    {
        pads = aPcb->GetPads();
        qsort( &pads[0], aPcb->GetPadCount(), sizeof( D_PAD* ),
               PadListSortByShape );
    }

    // The same for vias
    for( VIA* via = GetFirstVia( aPcb->m_Track ); via;
            via = GetFirstVia( via->Next() ) )
    {
        vias.push_back( via );
    }

    qsort( &vias[0], vias.size(), sizeof(VIA*), ViaSort );

    // Emit vias pads
    TRACK* old_via = 0;

    for( unsigned i = 0; i < vias.size(); i++ )
    {
        VIA* via = vias[i];

        if( old_via && 0 == ViaSort( &old_via, &via ) )
            continue;

        old_via = via;
        viastacks.push_back( via );
        fprintf( aFile, "PAD V%d.%d.%s ROUND %g\nCIRCLE 0 0 %g\n",
                via->GetWidth(), via->GetDrillValue(),
                fmt_mask( via->GetLayerSet() ).c_str(),
                via->GetDrillValue() / SCALE_FACTOR,
                via->GetWidth() / (SCALE_FACTOR * 2) );
    }

    // Emit component pads
    D_PAD* old_pad = 0;
    int    pad_name_number = 0;

    for( unsigned i = 0; i<pads.size(); ++i )
    {
        D_PAD* pad = pads[i];

        pad->SetSubRatsnest( pad_name_number );

        if( old_pad && 0==D_PAD::Compare( old_pad, pad ) )
            continue;  // already created

        old_pad = pad;

        pad_name_number++;
        pad->SetSubRatsnest( pad_name_number );

        fprintf( aFile, "PAD P%d", pad->GetSubRatsnest() );

        padstacks.push_back( pad ); // Will have its own padstack later
        int dx = pad->GetSize().x / 2;
        int dy = pad->GetSize().y / 2;

        switch( pad->GetShape() )
        {
        default:
        case PAD_SHAPE_CIRCLE:
            fprintf( aFile, " ROUND %g\n",
                     pad->GetDrillSize().x / SCALE_FACTOR );
            /* Circle is center, radius */
            fprintf( aFile, "CIRCLE %g %g %g\n",
                    pad->GetOffset().x / SCALE_FACTOR,
                    -pad->GetOffset().y / SCALE_FACTOR,
                    pad->GetSize().x / (SCALE_FACTOR * 2) );
            break;

        case PAD_SHAPE_RECT:
            fprintf( aFile, " RECTANGULAR %g\n",
                     pad->GetDrillSize().x / SCALE_FACTOR );

            // Rectangle is begin, size *not* begin, end!
            fprintf( aFile, "RECTANGLE %g %g %g %g\n",
                    (-dx + pad->GetOffset().x ) / SCALE_FACTOR,
                    (-dy - pad->GetOffset().y ) / SCALE_FACTOR,
                    dx / (SCALE_FACTOR / 2), dy / (SCALE_FACTOR / 2) );
            break;

        case PAD_SHAPE_OVAL:     // Create outline by 2 lines and 2 arcs
            {
                // OrCAD Layout call them OVAL or OBLONG - GenCAD call them FINGERs
                fprintf( aFile, " FINGER %g\n",
                         pad->GetDrillSize().x / SCALE_FACTOR );
                int dr = dx - dy;

                if( dr >= 0 )       // Horizontal oval
                {
                    int radius = dy;
                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR,
                             (dr + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR );

                    // GenCAD arcs are (start, end, center)
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR,
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR,
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             -pad->GetOffset().y / SCALE_FACTOR );

                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR,
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR );
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + radius) / SCALE_FACTOR,
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - radius) / SCALE_FACTOR,
                             (-dr + pad->GetOffset().x) / SCALE_FACTOR,
                             -pad->GetOffset().y / SCALE_FACTOR );
                }
                else        // Vertical oval
                {
                    dr = -dr;
                    int radius = dx;
                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (-radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR,
                             (-radius + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR );
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (-radius + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR,
                             (radius + pad->GetOffset().x ) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR,
                             pad->GetOffset().x / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR );

                    fprintf( aFile, "LINE %g %g %g %g\n",
                             (radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y + dr) / SCALE_FACTOR,
                             (radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR );
                    fprintf( aFile, "ARC %g %g %g %g %g %g\n",
                             (radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR,
                             (-radius + pad->GetOffset().x) / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR,
                             pad->GetOffset().x / SCALE_FACTOR,
                             (-pad->GetOffset().y - dr) / SCALE_FACTOR );
                }
            }
            break;

        case PAD_SHAPE_TRAPEZOID:
            fprintf( aFile, " POLYGON %g\n",
                     pad->GetDrillSize().x / SCALE_FACTOR );

            // XXX TO BE IMPLEMENTED! and I don't know if it could be actually imported by something
            break;
        }
    }

    fputs( "\n$ENDPADS\n\n", aFile );

    // Now emit the padstacks definitions, using the combined layer masks
    fputs( "$PADSTACKS\n", aFile );

    // Via padstacks
    for( unsigned i = 0; i < viastacks.size(); i++ )
    {
        VIA* via = viastacks[i];

        LSET mask = via->GetLayerSet() & master_layermask;

        fprintf( aFile, "PADSTACK VIA%d.%d.%s %g\n",
                 via->GetWidth(), via->GetDrillValue(),
                 fmt_mask( mask ).c_str(),
                 via->GetDrillValue() / SCALE_FACTOR );

        for( LSEQ seq = mask.Seq( gc_seq, DIM( gc_seq ) );  seq;  ++seq )
        {
            LAYER_ID layer = *seq;

            fprintf( aFile, "PAD V%d.%d.%s %s 0 0\n",
                    via->GetWidth(), via->GetDrillValue(),
                    fmt_mask( mask ).c_str(),
                    GenCADLayerName( cu_count, layer ).c_str()
                    );
        }
    }

    /* Component padstacks
     *  CAM350 don't apply correctly the FLIP semantics for padstacks, i.e. doesn't
     *  swap the top and bottom layers... so I need to define the shape as MIRRORX
     *  and define a separate 'flipped' padstack... until it appears yet another
     *  noncompliant importer */
    for( unsigned i = 1; i < padstacks.size(); i++ )
    {
        D_PAD* pad = padstacks[i];

        // Straight padstack
        fprintf( aFile, "PADSTACK PAD%u %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );

        LSET pad_set = pad->GetLayerSet() & master_layermask;

        // the special gc_seq
        for( LSEQ seq = pad_set.Seq( gc_seq, DIM( gc_seq ) );  seq;  ++seq )
        {
            LAYER_ID layer = *seq;

            fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerName( cu_count, layer ).c_str() );
        }

        // Flipped padstack
        fprintf( aFile, "PADSTACK PAD%uF %g\n", i, pad->GetDrillSize().x / SCALE_FACTOR );

        // the normal LAYER_ID sequence is inverted from gc_seq[]
        for( LSEQ seq = pad_set.Seq();  seq;  ++seq )
        {
            LAYER_ID layer = *seq;

            fprintf( aFile, "PAD P%u %s 0 0\n", i, GenCADLayerNameFlipped( cu_count, layer ).c_str() );
        }
    }

    fputs( "$ENDPADSTACKS\n\n", aFile );
}
void DIALOG_PAD_PROPERTIES::initValues()
{
    wxString    msg;
    double      angle;

    // Disable pad net name wxTextCtrl if the caller is the footprint editor
    // because nets are living only in the board managed by the board editor
    m_canEditNetName = m_parent->IsType( FRAME_PCB );


    // Setup layers names from board
    // Should be made first, before calling m_rbCopperLayersSel->SetSelection()
    m_rbCopperLayersSel->SetString( 0, m_board->GetLayerName( F_Cu ) );
    m_rbCopperLayersSel->SetString( 1, m_board->GetLayerName( B_Cu ) );

    m_PadLayerAdhCmp->SetLabel( m_board->GetLayerName( F_Adhes ) );
    m_PadLayerAdhCu->SetLabel( m_board->GetLayerName( B_Adhes ) );
    m_PadLayerPateCmp->SetLabel( m_board->GetLayerName( F_Paste ) );
    m_PadLayerPateCu->SetLabel( m_board->GetLayerName( B_Paste ) );
    m_PadLayerSilkCmp->SetLabel( m_board->GetLayerName( F_SilkS ) );
    m_PadLayerSilkCu->SetLabel( m_board->GetLayerName( B_SilkS ) );
    m_PadLayerMaskCmp->SetLabel( m_board->GetLayerName( F_Mask ) );
    m_PadLayerMaskCu->SetLabel( m_board->GetLayerName( B_Mask ) );
    m_PadLayerECO1->SetLabel( m_board->GetLayerName( Eco1_User ) );
    m_PadLayerECO2->SetLabel( m_board->GetLayerName( Eco2_User ) );
    m_PadLayerDraft->SetLabel( m_board->GetLayerName( Dwgs_User ) );

    m_isFlipped = false;

    if( m_currentPad )
    {
        MODULE* module = m_currentPad->GetParent();

        if( module->GetLayer() == B_Cu )
        {
            m_isFlipped = true;
            m_staticModuleSideValue->SetLabel( _( "Back side (footprint is mirrored)" ) );
        }

        msg.Printf( wxT( "%.1f" ), module->GetOrientation() / 10.0 );
        m_staticModuleRotValue->SetLabel( msg );
    }

    if( m_isFlipped )
    {
        wxPoint pt = m_dummyPad->GetOffset();
        NEGATE( pt.y );
        m_dummyPad->SetOffset( pt );

        wxSize sz = m_dummyPad->GetDelta();
        NEGATE( sz.y );
        m_dummyPad->SetDelta( sz );

        // flip pad's layers
        m_dummyPad->SetLayerSet( FlipLayerMask( m_dummyPad->GetLayerSet() ) );
    }

    m_staticTextWarningPadFlipped->Show(m_isFlipped);

    m_PadNumCtrl->SetValue( m_dummyPad->GetPadName() );
    m_PadNetNameCtrl->SetValue( m_dummyPad->GetNetname() );

    // Display current unit name in dialog:
    m_PadPosX_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadPosY_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadDrill_X_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadDrill_Y_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadShapeSizeX_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadShapeSizeY_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadShapeOffsetX_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadShapeOffsetY_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadShapeDelta_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_PadLengthDie_Unit->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );

    // Display current pad masks clearances units
    m_NetClearanceUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_SolderMaskMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_SolderPasteMarginUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_ThermalWidthUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );
    m_ThermalGapUnits->SetLabel( GetAbbreviatedUnitsLabel( g_UserUnit ) );

    // Display current pad parameters units:
    PutValueInLocalUnits( *m_PadPosition_X_Ctrl, m_dummyPad->GetPosition().x );
    PutValueInLocalUnits( *m_PadPosition_Y_Ctrl, m_dummyPad->GetPosition().y );

    PutValueInLocalUnits( *m_PadDrill_X_Ctrl, m_dummyPad->GetDrillSize().x );
    PutValueInLocalUnits( *m_PadDrill_Y_Ctrl, m_dummyPad->GetDrillSize().y );

    PutValueInLocalUnits( *m_ShapeSize_X_Ctrl, m_dummyPad->GetSize().x );
    PutValueInLocalUnits( *m_ShapeSize_Y_Ctrl, m_dummyPad->GetSize().y );

    PutValueInLocalUnits( *m_ShapeOffset_X_Ctrl, m_dummyPad->GetOffset().x );
    PutValueInLocalUnits( *m_ShapeOffset_Y_Ctrl, m_dummyPad->GetOffset().y );

    if( m_dummyPad->GetDelta().x )
    {
        PutValueInLocalUnits( *m_ShapeDelta_Ctrl, m_dummyPad->GetDelta().x );
        m_trapDeltaDirChoice->SetSelection( 0 );
    }
    else
    {
        PutValueInLocalUnits( *m_ShapeDelta_Ctrl, m_dummyPad->GetDelta().y );
        m_trapDeltaDirChoice->SetSelection( 1 );
    }

    PutValueInLocalUnits( *m_LengthPadToDieCtrl, m_dummyPad->GetPadToDieLength() );

    PutValueInLocalUnits( *m_NetClearanceValueCtrl, m_dummyPad->GetLocalClearance() );
    PutValueInLocalUnits( *m_SolderMaskMarginCtrl, m_dummyPad->GetLocalSolderMaskMargin() );
    PutValueInLocalUnits( *m_ThermalWidthCtrl, m_dummyPad->GetThermalWidth() );
    PutValueInLocalUnits( *m_ThermalGapCtrl, m_dummyPad->GetThermalGap() );

    // These 2 parameters are usually < 0, so prepare entering a negative value, if current is 0
    PutValueInLocalUnits( *m_SolderPasteMarginCtrl, m_dummyPad->GetLocalSolderPasteMargin() );

    if( m_dummyPad->GetLocalSolderPasteMargin() == 0 )
        m_SolderPasteMarginCtrl->SetValue( wxT( "-" ) + m_SolderPasteMarginCtrl->GetValue() );

    msg.Printf( wxT( "%f" ), m_dummyPad->GetLocalSolderPasteMarginRatio() * 100.0 );

    if( m_dummyPad->GetLocalSolderPasteMarginRatio() == 0.0 && msg[0] == '0' )
        // Sometimes Printf adds a sign if the value is small
        m_SolderPasteMarginRatioCtrl->SetValue( wxT( "-" ) + msg );
    else
        m_SolderPasteMarginRatioCtrl->SetValue( msg );

    switch( m_dummyPad->GetZoneConnection() )
    {
    default:
    case UNDEFINED_CONNECTION:
        m_ZoneConnectionChoice->SetSelection( 0 );
        break;

    case PAD_IN_ZONE:
        m_ZoneConnectionChoice->SetSelection( 1 );
        break;

    case THERMAL_PAD:
        m_ZoneConnectionChoice->SetSelection( 2 );
        break;

    case PAD_NOT_IN_ZONE:
        m_ZoneConnectionChoice->SetSelection( 3 );
        break;
    }

    if( m_currentPad )
    {
        MODULE* module = m_currentPad->GetParent();

        angle = m_currentPad->GetOrientation() - module->GetOrientation();

        if( m_isFlipped )
            NEGATE( angle );

        m_dummyPad->SetOrientation( angle );
    }

    angle = m_dummyPad->GetOrientation();

    NORMALIZE_ANGLE_180( angle );    // ? normalizing is in D_PAD::SetOrientation()

    // Set layers used by this pad: :
    setPadLayersList( m_dummyPad->GetLayerSet() );

    // Pad Orient
    switch( int( angle ) )
    {
    case 0:
        m_PadOrient->SetSelection( 0 );
        break;

    case 900:
        m_PadOrient->SetSelection( 1 );
        break;

    case -900:
        m_PadOrient->SetSelection( 2 );
        break;

    case 1800:
    case -1800:
        m_PadOrient->SetSelection( 3 );
        break;

    default:
        m_PadOrient->SetSelection( 4 );
        break;
    }

    switch( m_dummyPad->GetShape() )
    {
    default:
    case PAD_CIRCLE:
        m_PadShape->SetSelection( 0 );
        break;

    case PAD_OVAL:
        m_PadShape->SetSelection( 1 );
        break;

    case PAD_RECT:
        m_PadShape->SetSelection( 2 );
        break;

    case PAD_TRAPEZOID:
        m_PadShape->SetSelection( 3 );
        break;
    }

    msg.Printf( wxT( "%g" ), angle );
    m_PadOrientCtrl->SetValue( msg );

    // Type of pad selection
    m_PadType->SetSelection( 0 );

    for( unsigned ii = 0; ii < DIM( code_type ); ii++ )
    {
        if( code_type[ii] == m_dummyPad->GetAttribute() )
        {
            m_PadType->SetSelection( ii );
            break;
        }
    }

    // Enable/disable Pad name,and pad length die
    // (disable for NPTH pads (mechanical pads)
    bool enable = m_dummyPad->GetAttribute() != PAD_HOLE_NOT_PLATED;

    m_PadNumCtrl->Enable( enable );
    m_PadNetNameCtrl->Enable( m_canEditNetName && enable && m_currentPad != NULL );
    m_LengthPadToDieCtrl->Enable( enable );

    if( m_dummyPad->GetDrillShape() != PAD_DRILL_OBLONG )
        m_DrillShapeCtrl->SetSelection( 0 );
    else
        m_DrillShapeCtrl->SetSelection( 1 );

    // Update some dialog widgets state (Enable/disable options):
    wxCommandEvent cmd_event;
    setPadLayersList( m_dummyPad->GetLayerSet() );
    OnDrillShapeSelected( cmd_event );
    OnPadShapeSelection( cmd_event );
}
// test if all values are acceptable for the pad
bool DIALOG_PAD_PROPERTIES::padValuesOK()
{
    bool error = transferDataToPad( m_dummyPad );

    wxArrayString error_msgs;
    wxString msg;

    // Test for incorrect values
    if( (m_dummyPad->GetSize().x < m_dummyPad->GetDrillSize().x) ||
        (m_dummyPad->GetSize().y < m_dummyPad->GetDrillSize().y) )
    {
        error_msgs.Add(  _( "Incorrect value for pad drill: pad drill bigger than pad size" ) );
    }

    LSET padlayers_mask = m_dummyPad->GetLayerSet();

    if( padlayers_mask == 0 )
        error_msgs.Add( _( "Error: pad has no layer" ) );

    if( !padlayers_mask[F_Cu] && !padlayers_mask[B_Cu] )
    {
        if( m_dummyPad->GetDrillSize().x || m_dummyPad->GetDrillSize().y )
        {
            // Note: he message is shown in an HTML window
            msg = _( "Error: the pad is not on a copper layer and has a hole" );

            if( m_dummyPad->GetAttribute() == PAD_HOLE_NOT_PLATED )
            {
                msg += wxT("<br><br><i>");
                msg += _(   "For NPTH pad, set pad size value to pad drill value,"
                            " if you do not want this pad plotted in gerber files"
                            );
            }

            error_msgs.Add( msg );
        }
    }

    wxPoint max_size;
    max_size.x = std::abs( m_dummyPad->GetOffset().x );
    max_size.y = std::abs( m_dummyPad->GetOffset().y );
    max_size.x += m_dummyPad->GetDrillSize().x / 2;
    max_size.y += m_dummyPad->GetDrillSize().y / 2;

    if( ( m_dummyPad->GetSize().x / 2 < max_size.x ) ||
        ( m_dummyPad->GetSize().y / 2 < max_size.y ) )
    {
        error_msgs.Add( _( "Incorrect value for pad offset" ) );
    }

    if( error )
    {
        error_msgs.Add(  _( "Too large value for pad delta size" ) );
    }

    switch( m_dummyPad->GetAttribute() )
    {
    case PAD_HOLE_NOT_PLATED:   // Not plated, but through hole, a hole is expected
    case PAD_STANDARD :         // Pad through hole, a hole is also expected
        if( m_dummyPad->GetDrillSize().x <= 0 )
            error_msgs.Add( _( "Error: Through hole pad: drill diameter set to 0" ) );
        break;

    case PAD_CONN:      // Connector pads are smd pads, just they do not have solder paste.
        if( padlayers_mask[B_Paste] || padlayers_mask[F_Paste] )
            error_msgs.Add( _( "Error: Connector pads are not on the solder paste layer\n"
                               "Use SMD pads instead" ) );
        // Fall trough
    case PAD_SMD:       // SMD and Connector pads (One external copper layer only)
/*
        if( padlayers_mask[B_Cu] && padlayers_mask[F_Cu] )
            error_msgs.Add( _( "Error: only one copper layer allowed for SMD or Connector pads" ) );
*/
        break;
    }

    if( error_msgs.GetCount() )
    {
        HTML_MESSAGE_BOX dlg( this, _("Pad setup errors list" ) );
        dlg.ListSet( error_msgs );
        dlg.ShowModal();
    }

    return error_msgs.GetCount() == 0;
}
void DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event )
{
    wxPaintDC    dc( m_panelShowPad );
    PAD_DRAWINFO drawInfo;

    EDA_COLOR_T color = BLACK;

    if( m_dummyPad->GetLayerSet()[F_Cu] )
    {
        color = m_board->GetVisibleElementColor( PAD_FR_VISIBLE );
    }

    if( m_dummyPad->GetLayerSet()[B_Cu] )
    {
        color = ColorMix( color, m_board->GetVisibleElementColor( PAD_BK_VISIBLE ) );
    }

    // What could happen: the pad color is *actually* black, or no
    // copper was selected
    if( color == BLACK )
        color = LIGHTGRAY;

    drawInfo.m_Color     = color;
    drawInfo.m_HoleColor = DARKGRAY;
    drawInfo.m_Offset    = m_dummyPad->GetPosition();
    drawInfo.m_Display_padnum  = true;
    drawInfo.m_Display_netname = true;

    if( m_dummyPad->GetAttribute() == PAD_HOLE_NOT_PLATED )
        drawInfo.m_ShowNotPlatedHole = true;

    // Shows the local pad clearance
    drawInfo.m_PadClearance = m_dummyPad->GetLocalClearance();

    wxSize dc_size = dc.GetSize();
    dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );

    // Calculate a suitable scale to fit the available draw area
    int dim = m_dummyPad->GetSize().x + std::abs( m_dummyPad->GetDelta().y);

    if( m_dummyPad->GetLocalClearance() > 0 )
        dim += m_dummyPad->GetLocalClearance() * 2;

    double scale = (double) dc_size.x / dim;

    dim = m_dummyPad->GetSize().y + std::abs( m_dummyPad->GetDelta().x);
    if( m_dummyPad->GetLocalClearance() > 0 )
        dim += m_dummyPad->GetLocalClearance() * 2;

    double altscale = (double) dc_size.y / dim;
    scale = std::min( scale, altscale );

    // Give a margin
    scale *= 0.7;
    dc.SetUserScale( scale, scale );

    GRResetPenAndBrush( &dc );
    m_dummyPad->DrawShape( NULL, &dc, drawInfo );

    // Draw X and Y axis.
    // this is particularly useful to show the reference position of pads
    // with offset and no hole
    GRLine( NULL, &dc, -dim, 0, dim, 0, 0, BLUE );   // X axis
    GRLine( NULL, &dc, 0, -dim, 0, dim, 0, BLUE );   // Y axis

    event.Skip();
}
Example #15
0
/* Creates the plot for silkscreen layers
 * Silkscreen layers have specific requirement for pads (not filled) and texts
 * (with option to remove them from some copper areas (pads...)
 */
void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
                     const PCB_PLOT_PARAMS& aPlotOpt )
{
    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
    itemplotter.SetLayerSet( aLayerMask );

    // Plot edge layer and graphic items
    itemplotter.PlotBoardGraphicItems();

    // Plot footprint outlines :
    itemplotter.Plot_Edges_Modules();

    // Plot pads (creates pads outlines, for pads on silkscreen layers)
    LSET layersmask_plotpads = aLayerMask;

    // Calculate the mask layers of allowed layers for pads

    if( !aPlotOpt.GetPlotPadsOnSilkLayer() )       // Do not plot pads on silk screen layers
        layersmask_plotpads.set( B_SilkS, false ).set( F_SilkS, false );

    if( layersmask_plotpads.any() )
    {
        for( MODULE* Module = aBoard->m_Modules; Module; Module = Module->Next() )
        {
            for( D_PAD * pad = Module->Pads(); pad; pad = pad->Next() )
            {
                // See if the pad is on this layer
                LSET masklayer = pad->GetLayerSet();
                if( !( masklayer & layersmask_plotpads ).any() )
                    continue;

                EDA_COLOR_T color = ColorFromInt( 0 );

                if( layersmask_plotpads[B_SilkS] )
                    color = aBoard->GetLayerColor( B_SilkS );

                if( layersmask_plotpads[F_SilkS] )
                    color = ColorFromInt( color | aBoard->GetLayerColor( F_SilkS ) );

                itemplotter.PlotPad( pad, color, SKETCH );
            }
        }
    }

    // Plot footprints fields (ref, value ...)
    for( MODULE* module = aBoard->m_Modules;  module;  module = module->Next() )
    {
        if( ! itemplotter.PlotAllTextsModule( module ) )
        {
            wxLogMessage( _( "Your BOARD has a bad layer number for module %s" ),
                          GetChars( module->GetReference() ) );
        }
    }

    // Plot filled areas
    for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
    {
        ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii );

        if( !aLayerMask[ edge_zone->GetLayer() ] )
            continue;

        itemplotter.PlotFilledAreas( edge_zone );
    }

    // Plot segments used to fill zone areas (outdated, but here for old boards
    // compatibility):
    for( SEGZONE* seg = aBoard->m_Zone; seg; seg = seg->Next() )
    {
        if( !aLayerMask[ seg->GetLayer() ] )
            continue;

        aPlotter->ThickSegment( seg->GetStart(), seg->GetEnd(), seg->GetWidth(),
                                itemplotter.GetPlotMode() );
    }
}
bool DRC::doPadToPadsDrc( D_PAD* aRefPad, D_PAD** aStart, D_PAD** aEnd, int x_limit )
{
    const static LSET all_cu = LSET::AllCuMask();

    LSET layerMask = aRefPad->GetLayerSet() & all_cu;

    /* used to test DRC pad to holes: this dummy pad has the size and shape of the hole
     * to test pad to pad hole DRC, using the pad to pad DRC 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 );

    // Ensure the hole is on all copper layers
    dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() );

    // Use the minimal local clearance value for the dummy pad.
    // The clearance of the active pad will be used as minimum distance to a hole
    // (a value = 0 means use netclass value)
    dummypad.SetLocalClearance( 1 );

    for( D_PAD** pad_list = aStart;  pad_list<aEnd;  ++pad_list )
    {
        D_PAD* pad = *pad_list;

        if( pad == aRefPad )
            continue;

        // We can stop the test when pad->GetPosition().x > x_limit
        // because the list is sorted by X values
        if( pad->GetPosition().x > x_limit )
            break;

        // No problem if pads which are on copper layers are on different copper layers,
        // (pads can be only on a technical layer, to build complex pads)
        // but their hole (if any ) can create DRC error because they are on all
        // copper layers, so we test them
        if( ( pad->GetLayerSet() & layerMask ) == 0 &&
            ( pad->GetLayerSet() & all_cu ) != 0 &&
            ( aRefPad->GetLayerSet() & all_cu ) != 0 )
        {
            // if holes are in the same location and have the same size and shape,
            // this can be accepted
            if( pad->GetPosition() == aRefPad->GetPosition()
                && pad->GetDrillSize() == aRefPad->GetDrillSize()
                && pad->GetDrillShape() == aRefPad->GetDrillShape() )
            {
                if( aRefPad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
                    continue;

                // for oval holes: must also have the same orientation
                if( pad->GetOrientation() == aRefPad->GetOrientation() )
                    continue;
            }

            /* Here, we must test clearance between holes and pads
             * dummy pad size and shape is adjusted to pad drill size and shape
             */
            if( pad->GetDrillSize().x )
            {
                // pad under testing has a hole, test this hole against pad reference
                dummypad.SetPosition( pad->GetPosition() );
                dummypad.SetSize( pad->GetDrillSize() );
                dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
                                                           PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
                dummypad.SetOrientation( pad->GetOrientation() );

                if( !checkClearancePadToPad( aRefPad, &dummypad ) )
                {
                    // here we have a drc error on pad!
                    m_currentMarker = fillMarker( pad, aRefPad,
                                                  DRCE_HOLE_NEAR_PAD, m_currentMarker );
                    return false;
                }
            }

            if( aRefPad->GetDrillSize().x ) // pad reference has a hole
            {
                dummypad.SetPosition( aRefPad->GetPosition() );
                dummypad.SetSize( aRefPad->GetDrillSize() );
                dummypad.SetShape( aRefPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
                                                               PAD_SHAPE_OVAL : PAD_SHAPE_CIRCLE );
                dummypad.SetOrientation( aRefPad->GetOrientation() );

                if( !checkClearancePadToPad( pad, &dummypad ) )
                {
                    // here we have a drc error on aRefPad!
                    m_currentMarker = fillMarker( aRefPad, pad,
                                                  DRCE_HOLE_NEAR_PAD, m_currentMarker );
                    return false;
                }
            }

            continue;
        }

        // The pad must be in a net (i.e pt_pad->GetNet() != 0 ),
        // But no problem if pads have the same netcode (same net)
        if( pad->GetNetCode() && ( aRefPad->GetNetCode() == pad->GetNetCode() ) )
            continue;

        // if pads are from the same footprint
        if( pad->GetParent() == aRefPad->GetParent() )
        {
            // and have the same pad number ( equivalent pads  )

            // one can argue that this 2nd test is not necessary, that any
            // two pads from a single module are acceptable.  This 2nd test
            // should eventually be a configuration option.
            if( pad->PadNameEqual( aRefPad ) )
                continue;
        }

        // if either pad has no drill and is only on technical layers, not a clearance violation
        if( ( ( pad->GetLayerSet() & layerMask ) == 0 && !pad->GetDrillSize().x ) ||
            ( ( aRefPad->GetLayerSet() & layerMask ) == 0 && !aRefPad->GetDrillSize().x ) )
        {
            continue;
        }

        if( !checkClearancePadToPad( aRefPad, pad ) )
        {
            // here we have a drc error!
            m_currentMarker = fillMarker( aRefPad, pad, DRCE_PAD_NEAR_PAD1, m_currentMarker );
            return false;
        }
    }

    return true;
}
Example #17
0
/*
 * Function DoPushPadProperties
 * Function to change pad properties for the given footprint or all identical footprints
 * aPad is the pattern. The given footprint is the parent of this pad
 * aSameFootprints: if true, make changes on all identical footprints
 * aPadShapeFilter: if true, make changes only on pads having the same shape as aPad
 * aPadOrientFilter: if true, make changes only on pads having the same orientation as aPad
 * aPadLayerFilter: if true, make changes only on pads having the same layers as aPad
 * aSaveForUndo: if true: create an entry in the Undo/Redo list
 *        (usually: true in Schematic editor, false in Module editor)
 */
void PCB_BASE_FRAME::DoPushPadProperties( D_PAD* aPad, bool aSameFootprints,
                                          bool aPadShapeFilter,
                                          bool aPadOrientFilter,
                                          bool aPadLayerFilter,
                                          bool aSaveForUndo )
{
    MODULE* Module_Ref = aPad->GetParent();
    double pad_orient = aPad->GetOrientation() - Module_Ref->GetOrientation();

    // Prepare an undo list:
    if( aSaveForUndo )
    {
        PICKED_ITEMS_LIST itemsList;

        if( aSameFootprints )
        {
            for( MODULE* module = m_Pcb->m_Modules;  module;  module = module->Next() )
            {
                if( module->GetFPID() == Module_Ref->GetFPID() )
                {
                    ITEM_PICKER itemWrapper( module, UR_CHANGED );
                    itemsList.PushItem( itemWrapper );
                }
            }
        }
        else
        {
            ITEM_PICKER itemWrapper( Module_Ref, UR_CHANGED );
            itemsList.PushItem( itemWrapper );
        }

        SaveCopyInUndoList( itemsList, UR_CHANGED );
    }

    // Update the current module and same others modules if requested.
    for( MODULE* module = m_Pcb->m_Modules;  module;  module = module->Next() )
    {
        if( !aSameFootprints && (module != Module_Ref) )
            continue;

        if( module->GetFPID() != Module_Ref->GetFPID() )
            continue;

        // Erase module on screen
        module->SetFlags( DO_NOT_DRAW );
        m_canvas->RefreshDrawingRect( module->GetBoundingBox() );
        module->ClearFlags( DO_NOT_DRAW );

        for( D_PAD* pad = module->PadsList();  pad;  pad = pad->Next() )
        {
            if( aPadShapeFilter && ( pad->GetShape() != aPad->GetShape() ) )
                continue;

            double currpad_orient = pad->GetOrientation() - module->GetOrientation();

            if( aPadOrientFilter && ( currpad_orient != pad_orient ) )
                continue;

            if( aPadLayerFilter && ( pad->GetLayerSet() != aPad->GetLayerSet() ) )
                continue;

            // Do not copy pad to itself, it can create issues with custom pad primitives.
            if( pad == aPad )
                continue;

            pad->ImportSettingsFromMaster( *aPad );
        }

        module->CalculateBoundingBox();
        m_canvas->RefreshDrawingRect( module->GetBoundingBox() );
    }

    OnModify();
}
void DIALOG_PAD_PROPERTIES::OnPaintShowPanel( wxPaintEvent& event )
{
    wxPaintDC    dc( m_panelShowPad );
    PAD_DRAWINFO drawInfo;

    EDA_COLOR_T color = BLACK;

    if( m_dummyPad->GetLayerSet()[F_Cu] )
    {
        color = m_board->GetVisibleElementColor( PAD_FR_VISIBLE );
    }

    if( m_dummyPad->GetLayerSet()[B_Cu] )
    {
        color = ColorMix( color, m_board->GetVisibleElementColor( PAD_BK_VISIBLE ) );
    }

    // What could happen: the pad color is *actually* black, or no
    // copper was selected
    if( color == BLACK )
        color = LIGHTGRAY;

    drawInfo.m_Color     = color;
    drawInfo.m_HoleColor = DARKGRAY;
    drawInfo.m_Offset    = m_dummyPad->GetPosition();
    drawInfo.m_Display_padnum  = true;
    drawInfo.m_Display_netname = true;

    if( m_dummyPad->GetAttribute() == PAD_HOLE_NOT_PLATED )
        drawInfo.m_ShowNotPlatedHole = true;

    // Shows the local pad clearance
    drawInfo.m_PadClearance = m_dummyPad->GetLocalClearance();

    wxSize dc_size = dc.GetSize();
    dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );

    // Calculate a suitable scale to fit the available draw area
    int dim = m_dummyPad->GetSize().x + std::abs( m_dummyPad->GetDelta().y );

    // Invalid x size. User could enter zero, or have deleted all text prior to
    // entering a new value; this is also treated as zero. If dim is left at
    // zero, the drawing scale is zero and we get a crash.
    if( dim == 0 )
    {
        // If drill size has been set, use that. Otherwise default to 1mm.
        dim = m_dummyPad->GetDrillSize().x;
        if( dim == 0 )
            dim = 1000000;
    }

    if( m_dummyPad->GetLocalClearance() > 0 )
        dim += m_dummyPad->GetLocalClearance() * 2;

    double scale = (double) dc_size.x / dim;

    // If the pad is a circle, use the x size here instead.
    int ysize;
    if( m_dummyPad->GetShape() == PAD_CIRCLE )
        ysize = m_dummyPad->GetSize().x;
    else
        ysize = m_dummyPad->GetSize().y;

    dim = ysize + std::abs( m_dummyPad->GetDelta().x );

    // Invalid y size. See note about x size above.
    if( dim == 0 )
    {
        dim = m_dummyPad->GetDrillSize().y;
        if( dim == 0 )
            dim = 1000000;
    }

    if( m_dummyPad->GetLocalClearance() > 0 )
        dim += m_dummyPad->GetLocalClearance() * 2;

    double altscale = (double) dc_size.y / dim;
    scale = std::min( scale, altscale );

    // Give a margin
    scale *= 0.7;
    dc.SetUserScale( scale, scale );

    GRResetPenAndBrush( &dc );
    m_dummyPad->DrawShape( NULL, &dc, drawInfo );

    // Draw X and Y axis.
    // this is particularly useful to show the reference position of pads
    // with offset and no hole
    GRLine( NULL, &dc, -dim, 0, dim, 0, 0, BLUE );   // X axis
    GRLine( NULL, &dc, 0, -dim, 0, dim, 0, BLUE );   // Y axis

    event.Skip();
}
static void Print_Module( EDA_DRAW_PANEL* aPanel, wxDC* aDC, MODULE* aModule,
                          GR_DRAWMODE aDraw_mode, LSET aMask,
                          PRINT_PARAMETERS::DrillShapeOptT aDrillShapeOpt )
{
    // Print pads
    for( D_PAD* pad = aModule->Pads();  pad;  pad = pad->Next() )
    {
        if( !( pad->GetLayerSet() & aMask ).any() )
            continue;

        // Manage hole according to the print drill option
        wxSize drill_tmp = pad->GetDrillSize();

        switch( aDrillShapeOpt )
        {
        case PRINT_PARAMETERS::NO_DRILL_SHAPE:
            pad->SetDrillSize( wxSize(0,0) );
            break;

        case PRINT_PARAMETERS::SMALL_DRILL_SHAPE:
            {
                wxSize sz(  std::min( SMALL_DRILL, pad->GetDrillSize().x ),
                            std::min( SMALL_DRILL, pad->GetDrillSize().y ) );

                pad->SetDrillSize( sz );
            }
            break;

        case PRINT_PARAMETERS::FULL_DRILL_SHAPE:
            // Do nothing
            break;
        }

        pad->Draw( aPanel, aDC, aDraw_mode );
        pad->SetDrillSize( drill_tmp );
    }

    // Print footprint graphic shapes
    LSET mlayer( aModule->GetLayer() );

    if( aModule->GetLayer() == B_Cu )
        mlayer = LSET( B_SilkS );
    else if( aModule->GetLayer() == F_Cu )
        mlayer = LSET( F_SilkS );

    if( ( mlayer & aMask ).any() )
    {
        if( aModule->Reference().IsVisible() )
            aModule->Reference().Draw( aPanel, aDC, aDraw_mode );

        if( aModule->Value().IsVisible() )
            aModule->Value().Draw( aPanel, aDC, aDraw_mode );
    }

    for( EDA_ITEM* item = aModule->GraphicalItems();  item;  item = item->Next() )
    {
        switch( item->Type() )
        {
        case PCB_MODULE_TEXT_T:
            {
                if( !( mlayer & aMask ).any() )
                    break;

                TEXTE_MODULE* textMod = static_cast<TEXTE_MODULE*>( item );
                textMod->Draw( aPanel, aDC, aDraw_mode );
                break;
            }

        case PCB_MODULE_EDGE_T:
            {
                EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( item );

                if( !aMask[edge->GetLayer()] )
                    break;

                edge->Draw( aPanel, aDC, aDraw_mode );
            }
            break;

        default:
            break;
        }
    }
}
/*
 * Function GlobalChange_PadSettings
 * Function to change pad caracteristics for the given footprint
 * or alls footprints which look like the given footprint
 * aPad is the pattern. The given footprint is the parent of this pad
 * aSameFootprints: if true, make changes on all identical footprints
 * aPadShapeFilter: if true, make changes only on pads having the same shape as aPad
 * aPadOrientFilter: if true, make changes only on pads having the same orientation as aPad
 * aPadLayerFilter: if true, make changes only on pads having the same layers as aPad
 * aRedraw: if true: redraws the footprint
 * aSaveForUndo: if true: create an entry in the Undo/Redo list
 *        (usually: true in Schematic editor, false in Module editor)
 */
void PCB_BASE_FRAME::GlobalChange_PadSettings( D_PAD* aPad,
                                               bool aSameFootprints,
                                               bool aPadShapeFilter,
                                               bool aPadOrientFilter,
                                               bool aPadLayerFilter,
                                               bool aRedraw, bool aSaveForUndo )
{
    if( aPad == NULL )
        aPad = &GetDesignSettings().m_Pad_Master;

    MODULE* module = aPad->GetParent();

    if( module == NULL )
    {
        DisplayError( this, wxT( "Global_Import_Pad_Settings() Error: NULL module" ) );
        return;
    }

    // Search and copy the name of library reference.
    MODULE* Module_Ref = module;
    double pad_orient = aPad->GetOrientation() - Module_Ref->GetOrientation();

    // Prepare an undo list:
    if( aSaveForUndo )
    {
        PICKED_ITEMS_LIST itemsList;

        for( module = m_Pcb->m_Modules;  module;  module = module->Next() )
        {
            if( !aSameFootprints && (module != Module_Ref) )
                continue;

            if( module->GetFPID() != Module_Ref->GetFPID() )
                continue;

            bool   saveMe = false;

            for( D_PAD* pad = module->Pads();  pad;  pad = pad->Next() )
            {
                // Filters changes prohibited.
                if( aPadShapeFilter && ( pad->GetShape() != aPad->GetShape() ) )
                    continue;

                double currpad_orient = pad->GetOrientation() - module->GetOrientation();

                if( aPadOrientFilter && ( currpad_orient != pad_orient ) )
                    continue;

                if( aPadLayerFilter  &&  pad->GetLayerSet() != aPad->GetLayerSet() )
                    continue;

                saveMe = true;
            }

            if( saveMe )
            {
                ITEM_PICKER itemWrapper( module, UR_CHANGED );

                itemsList.PushItem( itemWrapper );
            }
        }

        SaveCopyInUndoList( itemsList, UR_CHANGED );
    }

    // Update the current module and same others modules if requested.
    for( module = m_Pcb->m_Modules;  module;  module = module->Next() )
    {
        if( !aSameFootprints && (module != Module_Ref) )
            continue;

        if( module->GetFPID() != Module_Ref->GetFPID() )
            continue;

        // Erase module on screen
        if( aRedraw )
        {
            module->SetFlags( DO_NOT_DRAW );
            m_canvas->RefreshDrawingRect( module->GetBoundingBox() );
            module->ClearFlags( DO_NOT_DRAW );
        }

        for( D_PAD* pad = module->Pads();  pad;  pad = pad->Next() )
        {
            // Filters changes prohibited.
            if( aPadShapeFilter && ( pad->GetShape() != aPad->GetShape() ) )
                continue;

            if( aPadOrientFilter &&  (pad->GetOrientation() - module->GetOrientation()) != pad_orient )
                continue;

            if( aPadLayerFilter )
            {
                if( pad->GetLayerSet() != aPad->GetLayerSet() )
                    continue;
                else
                    m_Pcb->m_Status_Pcb &= ~( LISTE_RATSNEST_ITEM_OK | CONNEXION_OK);
            }

            // Change characteristics:
            pad->SetAttribute( aPad->GetAttribute() );
            pad->SetShape( aPad->GetShape() );

            pad->SetLayerSet( aPad->GetLayerSet() );

            pad->SetSize( aPad->GetSize() );
            pad->SetDelta( aPad->GetDelta() );
            pad->SetOffset( aPad->GetOffset() );

            pad->SetDrillSize( aPad->GetDrillSize() );
            pad->SetDrillShape( aPad->GetDrillShape() );

            pad->SetOrientation( pad_orient + module->GetOrientation() );

            // copy also local mask margins, because these parameters usually depend on
            // pad sizes and layers
            pad->SetLocalSolderMaskMargin( aPad->GetLocalSolderMaskMargin() );
            pad->SetLocalSolderPasteMargin( aPad->GetLocalSolderPasteMargin() );
            pad->SetLocalSolderPasteMarginRatio( aPad->GetLocalSolderPasteMarginRatio() );

            if( pad->GetShape() != PAD_TRAPEZOID )
            {
                pad->SetDelta( wxSize( 0, 0 ) );
            }

            if( pad->GetShape() == PAD_CIRCLE )
            {
                // Ensure pad size.y = pad size.x
                int size = pad->GetSize().x;
                pad->SetSize( wxSize( size, size ) );
            }

            switch( pad->GetAttribute() )
            {
            case PAD_SMD:
            case PAD_CONN:
                pad->SetDrillSize( wxSize( 0, 0 ) );
                pad->SetOffset( wxPoint( 0, 0 ) );
                break;

            default:
                break;
            }
        }

        module->CalculateBoundingBox();

        if( aRedraw )
            m_canvas->RefreshDrawingRect( module->GetBoundingBox() );
    }

    OnModify();
}
int getOptimalModulePlacement( PCB_EDIT_FRAME* aFrame, MODULE* aModule, wxDC* aDC )
{
    int     error = 1;
    wxPoint LastPosOK;
    double  min_cost, curr_cost, Score;
    bool    TstOtherSide;
    DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)aFrame->GetDisplayOptions();
    BOARD*  brd = aFrame->GetBoard();

    aModule->CalculateBoundingBox();

    bool showRats = displ_opts->m_Show_Module_Ratsnest;
    displ_opts->m_Show_Module_Ratsnest = false;

    brd->m_Status_Pcb &= ~RATSNEST_ITEM_LOCAL_OK;
    aFrame->SetMsgPanel( aModule );

    LastPosOK = RoutingMatrix.m_BrdBox.GetOrigin();

    wxPoint     mod_pos = aModule->GetPosition();
    EDA_RECT    fpBBox  = aModule->GetFootprintRect();

    // Move fpBBox to have the footprint position at (0,0)
    fpBBox.Move( -mod_pos );
    wxPoint fpBBoxOrg = fpBBox.GetOrigin();

    // Calculate the limit of the footprint position, relative
    // to the routing matrix area
    wxPoint xylimit = RoutingMatrix.m_BrdBox.GetEnd() - fpBBox.GetEnd();

    wxPoint initialPos = RoutingMatrix.m_BrdBox.GetOrigin() - fpBBoxOrg;

    // Stay on grid.
    initialPos.x    -= initialPos.x % RoutingMatrix.m_GridRouting;
    initialPos.y    -= initialPos.y % RoutingMatrix.m_GridRouting;

    CurrPosition = initialPos;

    // Undraw the current footprint
    aModule->DrawOutlinesWhenMoving( aFrame->GetCanvas(), aDC, wxPoint( 0, 0 ) );

    g_Offset_Module = mod_pos - CurrPosition;

    /* Examine pads, and set TstOtherSide to true if a footprint
     * has at least 1 pad through.
     */
    TstOtherSide = false;

    if( RoutingMatrix.m_RoutingLayersCount > 1 )
    {
        LSET    other( aModule->GetLayer() == B_Cu  ? F_Cu : B_Cu );

        for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
        {
            if( !( pad->GetLayerSet() & other ).any() )
                continue;

            TstOtherSide = true;
            break;
        }
    }

    // Draw the initial bounding box position
    EDA_COLOR_T color = BROWN;
    fpBBox.SetOrigin( fpBBoxOrg + CurrPosition );
    draw_FootprintRect(aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color);

    min_cost = -1.0;
    aFrame->SetStatusText( wxT( "Score ??, pos ??" ) );

    for( ; CurrPosition.x < xylimit.x; CurrPosition.x += RoutingMatrix.m_GridRouting )
    {
        wxYield();

        if( aFrame->GetCanvas()->GetAbortRequest() )
        {
            if( IsOK( aFrame, _( "OK to abort?" ) ) )
            {
                displ_opts->m_Show_Module_Ratsnest = showRats;
                return ESC;
            }
            else
                aFrame->GetCanvas()->SetAbortRequest( false );
        }

        CurrPosition.y = initialPos.y;

        for( ; CurrPosition.y < xylimit.y; CurrPosition.y += RoutingMatrix.m_GridRouting )
        {
            // Erase traces.
            draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color );

            fpBBox.SetOrigin( fpBBoxOrg + CurrPosition );
            g_Offset_Module = mod_pos - CurrPosition;
            int keepOutCost = TstModuleOnBoard( brd, aModule, TstOtherSide );

            // Draw at new place
            color = keepOutCost >= 0 ? BROWN : RED;
            draw_FootprintRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, color );

            if( keepOutCost >= 0 )    // i.e. if the module can be put here
            {
                error = 0;
                aFrame->build_ratsnest_module( aModule );
                curr_cost   = compute_Ratsnest_PlaceModule( brd );
                Score       = curr_cost + keepOutCost;

                if( (min_cost >= Score ) || (min_cost < 0 ) )
                {
                    LastPosOK   = CurrPosition;
                    min_cost    = Score;
                    wxString msg;
                    msg.Printf( wxT( "Score %g, pos %s, %s" ),
                                min_cost,
                                GetChars( ::CoordinateToString( LastPosOK.x ) ),
                                GetChars( ::CoordinateToString( LastPosOK.y ) ) );
                    aFrame->SetStatusText( msg );
                }
            }
        }
    }

    // erasing the last traces
    GRRect( aFrame->GetCanvas()->GetClipBox(), aDC, fpBBox, 0, BROWN );

    displ_opts->m_Show_Module_Ratsnest = showRats;

    // Regeneration of the modified variable.
    CurrPosition = LastPosOK;

    brd->m_Status_Pcb &= ~( RATSNEST_ITEM_LOCAL_OK | LISTE_PAD_OK );

    MinCout = min_cost;
    return error;
}
Example #22
0
/* Plot a copper layer or mask.
 * Silk screen layers are not plotted here.
 */
void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
                        LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
{
    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );

    itemplotter.SetLayerSet( aLayerMask );

    EDA_DRAW_MODE_T plotMode = aPlotOpt.GetPlotMode();

    // Plot edge layer and graphic items
    itemplotter.PlotBoardGraphicItems();

    // Draw footprint shapes without pads (pads will plotted later)
    // We plot here module texts, but they are usually on silkscreen layer,
    // so they are not plot here but plot by PlotSilkScreen()
    // Plot footprints fields (ref, value ...)
    for( MODULE* module = aBoard->m_Modules;  module;  module = module->Next() )
    {
        if( ! itemplotter.PlotAllTextsModule( module ) )
        {
            wxLogMessage( _( "Your BOARD has a bad layer number for module %s" ),
                          GetChars( module->GetReference() ) );
        }
    }

    for( MODULE* module = aBoard->m_Modules;  module;  module = module->Next() )
    {
        for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() )
        {
            if( !aLayerMask[ item->GetLayer() ] )
                continue;

            switch( item->Type() )
            {
            case PCB_MODULE_EDGE_T:
                itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item );
                break;

            default:
                break;
            }
        }
    }

    // Plot footprint pads
    for( MODULE* module = aBoard->m_Modules;  module;  module = module->Next() )
    {
        for( D_PAD* pad = module->Pads();  pad;  pad = pad->Next() )
        {
            if( (pad->GetLayerSet() & aLayerMask) == 0 )
                continue;

            wxSize margin;
            double width_adj = 0;

            if( ( aLayerMask & LSET::AllCuMask() ).any() )
                width_adj =  itemplotter.getFineWidthAdj();

            static const LSET speed( 4, B_Mask, F_Mask, B_Paste, F_Paste );

            LSET anded = ( speed & aLayerMask );

            if( anded == LSET( F_Mask ) || anded == LSET( B_Mask ) )
            {
                margin.x = margin.y = pad->GetSolderMaskMargin();
            }
            else if( anded == LSET( F_Paste ) || anded == LSET( B_Paste ) )
            {
                margin = pad->GetSolderPasteMargin();
            }

            wxSize padPlotsSize;
            padPlotsSize.x = pad->GetSize().x + ( 2 * margin.x ) + width_adj;
            padPlotsSize.y = pad->GetSize().y + ( 2 * margin.y ) + width_adj;

            // Don't draw a null size item :
            if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 )
                continue;

            EDA_COLOR_T color = BLACK;

            if( pad->GetLayerSet()[B_Cu] )
                color = aBoard->GetVisibleElementColor( PAD_BK_VISIBLE );

            if( pad->GetLayerSet()[F_Cu] )
                color = ColorFromInt( color | aBoard->GetVisibleElementColor( PAD_FR_VISIBLE ) );

            // Temporary set the pad size to the required plot size:
            wxSize tmppadsize = pad->GetSize();
            pad->SetSize( padPlotsSize );
            switch( pad->GetShape() )
            {
            case PAD_SHAPE_CIRCLE:
            case PAD_SHAPE_OVAL:
                if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
                        (pad->GetSize() == pad->GetDrillSize()) &&
                        (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED) )
                    break;

            // Fall through:
            case PAD_SHAPE_TRAPEZOID:
            case PAD_SHAPE_RECT:
            default:
                itemplotter.PlotPad( pad, color, plotMode );
                break;
            }

            pad->SetSize( tmppadsize );     // Restore the pad size
        }
    }

    // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true,
    // plot them on solder mask
    for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
    {
        const VIA* Via = dyn_cast<const VIA*>( track );

        if( !Via )
            continue;

        // vias are not plotted if not on selected layer, but if layer
        // is SOLDERMASK_LAYER_BACK or SOLDERMASK_LAYER_FRONT,vias are drawn,
        // only if they are on the corresponding external copper layer
        LSET via_mask_layer = Via->GetLayerSet();

        if( aPlotOpt.GetPlotViaOnMaskLayer() )
        {
            if( via_mask_layer[B_Cu] )
                via_mask_layer.set( B_Mask );

            if( via_mask_layer[F_Cu] )
                via_mask_layer.set( F_Mask );
        }

        if( !( via_mask_layer & aLayerMask ).any() )
            continue;

        int via_margin = 0;
        double width_adj = 0;

        // If the current layer is a solder mask, use the global mask
        // clearance for vias
        if( aLayerMask[B_Mask] || aLayerMask[F_Mask] )
            via_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;

        if( ( aLayerMask & LSET::AllCuMask() ).any() )
            width_adj = itemplotter.getFineWidthAdj();

        int diameter = Via->GetWidth() + 2 * via_margin + width_adj;

        // Don't draw a null size item :
        if( diameter <= 0 )
            continue;

        EDA_COLOR_T color = aBoard->GetVisibleElementColor(VIAS_VISIBLE + Via->GetViaType());
        // Set plot color (change WHITE to LIGHTGRAY because
        // the white items are not seen on a white paper or screen
        aPlotter->SetColor( color != WHITE ? color : LIGHTGRAY);
        aPlotter->FlashPadCircle( Via->GetStart(), diameter, plotMode );
    }

    // Plot tracks (not vias) :
    for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
            continue;

        if( !aLayerMask[track->GetLayer()] )
            continue;

        int width = track->GetWidth() + itemplotter.getFineWidthAdj();
        aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
        aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode );
    }

    // Plot zones (outdated, for old boards compatibility):
    for( TRACK* track = aBoard->m_Zone; track; track = track->Next() )
    {
        if( !aLayerMask[track->GetLayer()] )
            continue;

        int width = track->GetWidth() + itemplotter.getFineWidthAdj();
        aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
        aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode );
    }

    // Plot filled ares
    for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
    {
        ZONE_CONTAINER* zone = aBoard->GetArea( ii );

        if( !aLayerMask[zone->GetLayer()] )
            continue;

        itemplotter.PlotFilledAreas( zone );
    }

    // Adding drill marks, if required and if the plotter is able to plot them:
    if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
        itemplotter.PlotDrillMarks();
}