VIA* TRACK::GetVia( TRACK* aEndTrace, const wxPoint& aPosition, LAYER_MSK aLayerMask )
{
    for( VIA *via = GetFirstVia( this, aEndTrace ); via; via = GetFirstVia( via->Next() ) )
    {
        if( via->HitTest( aPosition ) &&
                !via->GetState( BUSY | IS_DELETED ) &&
                (aLayerMask & via->GetLayerMask()) )
            return via;
    }

    return NULL;
}
VIA* TRACK::GetVia( const wxPoint& aPosition, LAYER_NUM aLayer)
{
    for( VIA *via = GetFirstVia( this ); via; via = GetFirstVia( via->Next() ) )
    {
        if( via->HitTest( aPosition ) &&
                !via->GetState( BUSY | IS_DELETED ) &&
                ((aLayer == UNDEFINED_LAYER) || (via->IsOnLayer( aLayer ))) )
            return via;
    }

    return NULL;
}
bool TRACKS_CLEANER::clean_vias()
{
    bool modified = false;

    for( VIA* via = GetFirstVia( m_Brd->m_Track ); via != NULL;
            via = GetFirstVia( via->Next() ) )
    {
        // Correct via m_End defects (if any), should never happen
        if( via->GetStart() != via->GetEnd() )
        {
            wxFAIL_MSG( wxT( "Via with mismatching ends" ) );
            via->SetEnd( via->GetStart() );
        }

        /* Important: these cleanups only do thru hole vias, they don't
         * (yet) handle high density interconnects */
        if( via->GetViaType() != VIA_THROUGH )
        {
            modified |= remove_duplicates_of_via( via );

            /* To delete through Via on THT pads at same location
             * Examine the list of connected pads:
             * if one through pad is found, the via can be removed */
            for( unsigned ii = 0; ii < via->m_PadsConnected.size(); ++ii )
            {
                const D_PAD *pad = via->m_PadsConnected[ii];

                const LSET all_cu = LSET::AllCuMask();

                if( (pad->GetLayerSet() & all_cu) == all_cu )
                {
                    // redundant: delete the via
                    m_Brd->GetRatsnest()->Remove( via );
                    via->ViewRelease();
                    via->DeleteStructure();
                    modified = true;
                    break;
                }
            }
        }
    }

    return modified;
}
void EXCELLON_WRITER::BuildHolesList( LAYER_PAIR aLayerPair,
                                      bool aGenerateNPTH_list )
{
    HOLE_INFO new_hole;

    m_holeListBuffer.clear();
    m_toolListBuffer.clear();

    wxASSERT(  aLayerPair.first < aLayerPair.second );  // fix the caller

    // build hole list for vias
    if( ! aGenerateNPTH_list )  // vias are always plated !
    {
        for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) )
        {
            int hole_sz = via->GetDrillValue();

            if( hole_sz == 0 )   // Should not occur.
                continue;

            new_hole.m_Tool_Reference = -1;         // Flag value for Not initialized
            new_hole.m_Hole_Orient    = 0;
            new_hole.m_Hole_Diameter  = hole_sz;
            new_hole.m_Hole_NotPlated = false;
            new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;

            new_hole.m_Hole_Shape = 0;              // hole shape: round
            new_hole.m_Hole_Pos = via->GetStart();

            via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );

            // LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
            // Remember: top layer = 0 and bottom layer = 31 for through hole vias
            // Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
            if( new_hole.m_Hole_Top_Layer    != aLayerPair.first ||
                new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
                continue;

            m_holeListBuffer.push_back( new_hole );
        }
    }

    if( aLayerPair == LAYER_PAIR( F_Cu, B_Cu ) )
    {
        // add holes for thru hole pads
        for( MODULE* module = m_pcb->m_Modules;  module;  module = module->Next() )
        {
            for( D_PAD* pad = module->Pads();  pad;  pad = pad->Next() )
            {
                if( !m_merge_PTH_NPTH )
                {
                    if( !aGenerateNPTH_list && pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
                        continue;

                    if( aGenerateNPTH_list && pad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
                        continue;
                }

                if( pad->GetDrillSize().x == 0 )
                    continue;

                new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED);
                new_hole.m_Tool_Reference = -1;         // Flag is: Not initialized
                new_hole.m_Hole_Orient    = pad->GetOrientation();
                new_hole.m_Hole_Shape     = 0;           // hole shape: round
                new_hole.m_Hole_Diameter  = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
                new_hole.m_Hole_Size.x    = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;

                if( pad->GetDrillShape() != PAD_DRILL_SHAPE_CIRCLE )
                    new_hole.m_Hole_Shape = 1; // oval flag set

                new_hole.m_Hole_Size         = pad->GetDrillSize();
                new_hole.m_Hole_Pos          = pad->GetPosition();  // hole position
                new_hole.m_Hole_Bottom_Layer = B_Cu;
                new_hole.m_Hole_Top_Layer    = F_Cu;    // pad holes are through holes
                m_holeListBuffer.push_back( new_hole );
            }
        }
    }

    // Sort holes per increasing diameter value
    sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleSettings );

    // build the tool list
    int last_hole = -1;     // Set to not initialized (this is a value not used
                            // for m_holeListBuffer[ii].m_Hole_Diameter)
    bool last_notplated_opt = false;

    DRILL_TOOL new_tool( 0, false );
    unsigned   jj;

    for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
    {
        if( m_holeListBuffer[ii].m_Hole_Diameter != last_hole ||
            m_holeListBuffer[ii].m_Hole_NotPlated != last_notplated_opt )
        {
            new_tool.m_Diameter = m_holeListBuffer[ii].m_Hole_Diameter;
            new_tool.m_Hole_NotPlated = m_holeListBuffer[ii].m_Hole_NotPlated;
            m_toolListBuffer.push_back( new_tool );
            last_hole = new_tool.m_Diameter;
            last_notplated_opt = new_tool.m_Hole_NotPlated;
        }

        jj = m_toolListBuffer.size();

        if( jj == 0 )
            continue;                                        // Should not occurs

        m_holeListBuffer[ii].m_Tool_Reference = jj;          // Tool value Initialized (value >= 1)

        m_toolListBuffer.back().m_TotalCount++;

        if( m_holeListBuffer[ii].m_Hole_Shape )
            m_toolListBuffer.back().m_OvalCount++;
    }
}
void EXCELLON_WRITER::BuildHolesList( int aFirstLayer,
                                      int aLastLayer,
                                      bool aExcludeThroughHoles,
                                      bool aGenerateNPTH_list,
                                      bool aMerge_PTH_NPTH )
{
    HOLE_INFO new_hole;
    int       hole_value;

    m_holeListBuffer.clear();
    m_toolListBuffer.clear();

    if( (aFirstLayer >= 0) && (aLastLayer >= 0) )
    {
        if( aFirstLayer > aLastLayer )
            std::swap( aFirstLayer, aLastLayer );
    }

    if ( aGenerateNPTH_list && aMerge_PTH_NPTH )
    {
        return;
    }

    // build hole list for vias
    if( ! aGenerateNPTH_list )  // vias are always plated !
    {
        for( VIA* via = GetFirstVia( m_pcb->m_Track ); via; via = GetFirstVia( via->Next() ) )
        {
            hole_value = via->GetDrillValue();

            if( hole_value == 0 )   // Should not occur.
                continue;

            new_hole.m_Tool_Reference = -1;         // Flag value for Not initialized
            new_hole.m_Hole_Orient    = 0;
            new_hole.m_Hole_Diameter  = hole_value;
            new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;

            new_hole.m_Hole_Shape = 0;              // hole shape: round
            new_hole.m_Hole_Pos = via->GetStart();

            via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );

            // LayerPair return params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
            // Remember: top layer = 0 and bottom layer = 31 for through hole vias
            // the via should be at least from aFirstLayer to aLastLayer
            if( (new_hole.m_Hole_Top_Layer > aFirstLayer) && (aFirstLayer >= 0) )
                continue;   // via above the first layer

            if( (new_hole.m_Hole_Bottom_Layer < aLastLayer) && (aLastLayer >= 0) )
                continue;   // via below the last layer

            if( aExcludeThroughHoles && (new_hole.m_Hole_Bottom_Layer == B_Cu)
               && (new_hole.m_Hole_Top_Layer == F_Cu) )
                continue;

            m_holeListBuffer.push_back( new_hole );
        }
    }

    // build hole list for pads (assumed always through holes)
    if( !aExcludeThroughHoles || aGenerateNPTH_list )
    {
        for( MODULE* module = m_pcb->m_Modules;  module;  module = module->Next() )
        {
            // Read and analyse pads
            for( D_PAD* pad = module->Pads();  pad;  pad = pad->Next() )
            {
                if( ! aGenerateNPTH_list &&
                    pad->GetAttribute() == PAD_HOLE_NOT_PLATED &&
                    ! aMerge_PTH_NPTH )
                    continue;

                if( aGenerateNPTH_list && pad->GetAttribute() != PAD_HOLE_NOT_PLATED )
                    continue;

                if( pad->GetDrillSize().x == 0 )
                    continue;

                new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_HOLE_NOT_PLATED);
                new_hole.m_Tool_Reference = -1;         // Flag is: Not initialized
                new_hole.m_Hole_Orient    = pad->GetOrientation();
                new_hole.m_Hole_Shape     = 0;           // hole shape: round
                new_hole.m_Hole_Diameter  = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
                new_hole.m_Hole_Size.x    = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;

                if( pad->GetDrillShape() != PAD_DRILL_CIRCLE )
                    new_hole.m_Hole_Shape = 1; // oval flag set

                new_hole.m_Hole_Size         = pad->GetDrillSize();
                new_hole.m_Hole_Pos          = pad->GetPosition();               // hole position
                new_hole.m_Hole_Bottom_Layer = B_Cu;
                new_hole.m_Hole_Top_Layer    = F_Cu;// pad holes are through holes
                m_holeListBuffer.push_back( new_hole );
            }
        }
    }

    // Sort holes per increasing diameter value
    sort( m_holeListBuffer.begin(), m_holeListBuffer.end(), CmpHoleDiameterValue );

    // build the tool list
    int        LastHole = -1; /* Set to not initialized (this is a value not used
                               * for m_holeListBuffer[ii].m_Hole_Diameter) */
    DRILL_TOOL new_tool( 0 );
    unsigned   jj;

    for( unsigned ii = 0; ii < m_holeListBuffer.size(); ii++ )
    {
        if( m_holeListBuffer[ii].m_Hole_Diameter != LastHole )
        {
            new_tool.m_Diameter = ( m_holeListBuffer[ii].m_Hole_Diameter );
            m_toolListBuffer.push_back( new_tool );
            LastHole = new_tool.m_Diameter;
        }

        jj = m_toolListBuffer.size();

        if( jj == 0 )
            continue;                                        // Should not occurs

        m_holeListBuffer[ii].m_Tool_Reference = jj;          // Tool value Initialized (value >= 1)

        m_toolListBuffer.back().m_TotalCount++;

        if( m_holeListBuffer[ii].m_Hole_Shape )
            m_toolListBuffer.back().m_OvalCount++;
    }
}
// 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 );
}