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();
}
BOARD_ITEM* MODULE::DuplicateAndAddItem( const BOARD_ITEM* aItem,
                                         bool aIncrementPadNumbers )
{
    BOARD_ITEM* new_item = NULL;

    switch( aItem->Type() )
    {
    case PCB_PAD_T:
    {
        D_PAD* new_pad = new D_PAD( *static_cast<const D_PAD*>( aItem ) );

        Pads().PushBack( new_pad );
        new_item = new_pad;
        break;
    }

    case PCB_MODULE_TEXT_T:
    {
        const TEXTE_MODULE* old_text = static_cast<const TEXTE_MODULE*>( aItem );

        // do not duplicate value or reference fields
        // (there can only be one of each)
        if( old_text->GetType() == TEXTE_MODULE::TEXT_is_DIVERS )
        {
            TEXTE_MODULE* new_text = new TEXTE_MODULE( *old_text );

            GraphicalItems().PushBack( new_text );
            new_item = new_text;
        }
        break;
    }

    case PCB_MODULE_EDGE_T:
    {
        EDGE_MODULE* new_edge = new EDGE_MODULE(
                *static_cast<const EDGE_MODULE*>(aItem) );

        GraphicalItems().PushBack( new_edge );
        new_item = new_edge;
        break;
    }

    case PCB_MODULE_T:
        // Ignore the module itself
        break;

    default:
        // Un-handled item for duplication
        wxASSERT_MSG( false, "Duplication not supported for items of class "
                      + aItem->GetClass() );
        break;
    }

    if( aIncrementPadNumbers && new_item )
    {
        new_item->IncrementItemReference();
    }

    return new_item;
}
void MODULE::ClearAllNets()
{
    // Force the ORPHANED dummy net info for all pads.
    // ORPHANED dummy net does not depend on a board
    for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
        pad->SetNetCode( NETINFO_LIST::ORPHANED );
}
void MODULE::CopyNetlistSettings( MODULE* aModule )
{
    // Don't do anything foolish like trying to copy to yourself.
    wxCHECK_RET( aModule != NULL && aModule != this, wxT( "Cannot copy to NULL or yourself." ) );

    // Not sure what to do with the value field.  Use netlist for now.
    aModule->SetPosition( GetPosition() );

    if( aModule->GetLayer() != GetLayer() )
        aModule->Flip( aModule->GetPosition() );

    if( aModule->GetOrientation() != GetOrientation() )
        aModule->Rotate( aModule->GetPosition(), GetOrientation() );

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

    for( D_PAD* pad = Pads();  pad;  pad = pad->Next() )
    {
        D_PAD* newPad = aModule->FindPadByName( pad->GetPadName() );

        if( newPad )
            pad->CopyNetlistSettings( newPad );
    }

    // Not sure about copying description, keywords, 3D models or any other
    // local user changes to footprint.  Stick with the new footprint settings
    // called out in the footprint loaded in the netlist.
    aModule->CalculateBoundingBox();
}
void MODULE::MoveAnchorPosition( const wxPoint& aMoveVector )
{
    /* Move the reference point of the footprint
     * the footprints elements (pads, outlines, edges .. ) are moved
     * but:
     * - the footprint position is not modified.
     * - the relative (local) coordinates of these items are modified
     */

    wxPoint footprintPos = GetPosition();

    /* Update the relative coordinates:
     * The coordinates are relative to the anchor point.
     * Calculate deltaX and deltaY from the anchor. */
    wxPoint moveVector = aMoveVector;
    RotatePoint( &moveVector, -GetOrientation() );

    // Update of the reference and value.
    m_Reference->SetPos0( m_Reference->GetPos0() + moveVector );
    m_Reference->SetDrawCoord();
    m_Value->SetPos0( m_Value->GetPos0() + moveVector );
    m_Value->SetDrawCoord();

    // Update the pad local coordinates.
    for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
    {
        pad->SetPos0( pad->GetPos0() + moveVector );
        pad->SetPosition( pad->GetPos0() + footprintPos );
    }

    // Update the draw element coordinates.
    for( EDA_ITEM* item = GraphicalItems(); item; item = item->Next() )
    {
        switch( item->Type() )
        {
        case PCB_MODULE_EDGE_T:
            {
                EDGE_MODULE* edge = static_cast<EDGE_MODULE*>( item );
                edge->m_Start0 += moveVector;
                edge->m_End0   += moveVector;
                edge->SetDrawCoord();
                break;
            }

        case PCB_MODULE_TEXT_T:
            {
                TEXTE_MODULE* text = static_cast<TEXTE_MODULE*>( item );
                text->SetPos0( text->GetPos0() + moveVector );
                text->SetDrawCoord();
                break;
            }

        default:
            break;
        }
    }

    CalculateBoundingBox();
}
wxString MODULE::GetNextPadName( bool aFillSequenceGaps ) const
{
    std::set<int> usedNumbers;

    // Create a set of used pad numbers
    for( D_PAD* pad = Pads(); pad; pad = pad->Next() )
    {
        int padNumber = getTrailingInt( pad->GetPadName() );
        usedNumbers.insert( padNumber );
    }

    const int nextNum = getNextNumberInSequence( usedNumbers, aFillSequenceGaps );

    return wxString::Format( wxT( "%i" ), nextNum );
}
/* generate pads shapes on layer aLayer as polygons,
 * and adds these polygons to aCornerBuffer
 * aCornerBuffer = the buffer to store polygons
 * aInflateValue = an additionnal size to add to pad shapes
 * aCircleToSegmentsCount = number of segments to approximate a circle
 * aCorrectionFactor = the correction to apply to a circle radius
 *  to generate the polygon.
 *  if aCorrectionFactor = 1.0, the polygon is inside the circle
 *  the radius of circle approximated by segments is
 *  initial radius * aCorrectionFactor
 */
void MODULE::TransformPadsShapesWithClearanceToPolygon( LAYER_NUM aLayer,
                        CPOLYGONS_LIST& aCornerBuffer,
                        int                    aInflateValue,
                        int                    aCircleToSegmentsCount,
                        double                 aCorrectionFactor )
{
    D_PAD* pad = Pads();

    wxSize margin;
    for( ; pad != NULL; pad = pad->Next() )
    {
        if( !pad->IsOnLayer(aLayer) )
            continue;


        switch( aLayer )
        {
        case SOLDERMASK_N_FRONT:
        case SOLDERMASK_N_BACK:
            margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue;
            break;

        case SOLDERPASTE_N_FRONT:
        case SOLDERPASTE_N_BACK:
            margin = pad->GetSolderPasteMargin();
            margin.x += aInflateValue;
            margin.y += aInflateValue;
            break;

        default:
            margin.x = margin.y = aInflateValue;
            break;
        }

        pad->BuildPadShapePolygon( aCornerBuffer, margin,
                                   aCircleToSegmentsCount, aCorrectionFactor );
    }
}
void MODULE::TransformPadsShapesWithClearanceToPolygon( LAYER_ID aLayer,
                        SHAPE_POLY_SET& aCornerBuffer,
                        int                    aInflateValue,
                        int                    aCircleToSegmentsCount,
                        double                 aCorrectionFactor,
                        bool                   aSkipNPTHPadsWihNoCopper )
{
    D_PAD* pad = Pads();

    wxSize margin;
    for( ; pad != NULL; pad = pad->Next() )
    {
        if( !pad->IsOnLayer(aLayer) )
            continue;

        // NPTH pads are not drawn on layers if the shape size and pos is the same
        // as their hole:
        if( aSkipNPTHPadsWihNoCopper && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
        {
            if( pad->GetDrillSize() == pad->GetSize() && pad->GetOffset() == wxPoint( 0, 0 ) )
            {
                switch( pad->GetShape() )
                {
                case PAD_SHAPE_CIRCLE:
                    if( pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
                        continue;
                    break;

                case PAD_SHAPE_OVAL:
                    if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
                        continue;
                    break;

                default:
                    break;
                }
            }
        }

        switch( aLayer )
        {
        case F_Mask:
        case B_Mask:
            margin.x = margin.y = pad->GetSolderMaskMargin() + aInflateValue;
            break;

        case F_Paste:
        case B_Paste:
            margin = pad->GetSolderPasteMargin();
            margin.x += aInflateValue;
            margin.y += aInflateValue;
            break;

        default:
            margin.x = margin.y = aInflateValue;
            break;
        }

        pad->BuildPadShapePolygon( aCornerBuffer, margin,
                                   aCircleToSegmentsCount, aCorrectionFactor );
    }
}