示例#1
0
/* Function called to abort a track creation
 */
static void Abort_Create_Track( EDA_DRAW_PANEL* Panel, wxDC* DC )
{
    PCB_EDIT_FRAME* frame = (PCB_EDIT_FRAME*) Panel->GetParent();
    BOARD* pcb = frame->GetBoard();
    TRACK* track = (TRACK*) frame->GetCurItem();

    if( track && ( track->Type()==PCB_VIA_T || track->Type()==PCB_TRACE_T ) )
    {
        // Erase the current drawing
        ShowNewTrackWhenMovingCursor( Panel, DC, wxDefaultPosition, false );

        if( pcb->IsHighLightNetON() )
            frame->HighLight( DC );

        pcb->PopHighLight();

        if( pcb->IsHighLightNetON() )
            pcb->DrawHighLight( Panel, DC, pcb->GetHighLightNetCode() );

        frame->ClearMsgPanel();

        // Undo pending changes (mainly a lock point creation) and clear the
        // undo picker list:
        frame->PutDataInPreviousState( &s_ItemsListPicker, false, false );
        s_ItemsListPicker.ClearListAndDeleteItems();

        // Delete current (new) track
        g_CurrentTrackList.DeleteAll();
    }

    frame->SetCurItem( NULL );
}
示例#2
0
void DRC::testKeepoutAreas()
{
    // Test keepout areas for vias, tracks and pads inside keepout areas
    for( int ii = 0; ii < m_pcb->GetAreaCount(); ii++ )
    {
        ZONE_CONTAINER* area = m_pcb->GetArea( ii );

        if( !area->GetIsKeepout() )
            continue;

        for( TRACK* segm = m_pcb->m_Track; segm != NULL; segm = segm->Next() )
        {
            if( segm->Type() == PCB_TRACE_T )
            {
                if( ! area->GetDoNotAllowTracks()  )
                    continue;

                if( segm->GetLayer() != area->GetLayer() )
                    continue;

                if( area->Outline()->Distance( segm->GetStart(), segm->GetEnd(),
                                               segm->GetWidth() ) == 0 )
                {
                    m_currentMarker = fillMarker( segm, NULL,
                                                  DRCE_TRACK_INSIDE_KEEPOUT, m_currentMarker );
                    m_pcb->Add( m_currentMarker );
                    m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
                    m_currentMarker = 0;
                }
            }
            else if( segm->Type() == PCB_VIA_T )
            {
                if( ! area->GetDoNotAllowVias()  )
                    continue;

                if( ! ((VIA*)segm)->IsOnLayer( area->GetLayer() ) )
                    continue;

                if( area->Outline()->Distance( segm->GetPosition() ) < segm->GetWidth()/2 )
                {
                    m_currentMarker = fillMarker( segm, NULL,
                                                  DRCE_VIA_INSIDE_KEEPOUT, m_currentMarker );
                    m_pcb->Add( m_currentMarker );
                    m_mainWindow->GetGalCanvas()->GetView()->Add( m_currentMarker );
                    m_currentMarker = 0;
                }
            }
        }
        // Test pads: TODO
    }
}
示例#3
0
// Delete null length segments, and intermediate points ..
bool TRACKS_CLEANER::clean_segments()
{
    bool modified = false;

    // Easy things first
    modified |= delete_null_segments();

    // Delete redundant segments, i.e. segments having the same end points and layers
    for( TRACK *segment = m_Brd->m_Track; segment; segment = segment->Next() )
        modified |= remove_duplicates_of_track( segment );

    // merge collinear segments:
    TRACK *nextsegment;
    for( TRACK *segment = m_Brd->m_Track; segment; segment = nextsegment )
    {
        nextsegment = segment->Next();

        if( segment->Type() == PCB_TRACE_T )
        {
            bool merged_this = merge_collinear_of_track( segment );
            modified |= merged_this;

            if( merged_this ) // The current segment was modified, retry to merge it again
                nextsegment = segment->Next();
        }
    }

    return modified;
}
示例#4
0
bool TRACKS_CLEANER::remove_duplicates_of_track( const TRACK *aTrack )
{
    bool modified = false;

    TRACK *nextsegment;
    for( TRACK *other = aTrack->Next(); other; other = nextsegment )
    {
        nextsegment = other->Next();

        // New netcode, break out (can't be there any other)
        if( aTrack->GetNetCode() != other->GetNetCode() )
            break;

        // Must be of the same type, on the same layer and the endpoints
        // must be the same (maybe swapped)
        if( (aTrack->Type() != other->Type()) &&
            (aTrack->GetLayer() != other->GetLayer()) )
        {
            if( ((aTrack->GetStart() == other->GetStart()) &&
                 (aTrack->GetEnd() == other->GetEnd())) ||
                ((aTrack->GetStart() == other->GetEnd()) &&
                 (aTrack->GetEnd() == other->GetStart())))
            {
                m_Brd->GetRatsnest()->Remove( other );
                other->ViewRelease();
                other->DeleteStructure();
                modified = true;
            }
        }
    }
    return modified;
}
void PCB_EDIT_FRAME::Swap_Layers( wxCommandEvent& event )
{
    PCB_LAYER_ID new_layer[PCB_LAYER_ID_COUNT];

    DIALOG_SWAP_LAYERS dlg( this, new_layer );

    if( dlg.ShowModal() != wxID_OK )
        return;

    BOARD_COMMIT commit( this );
    bool hasChanges = false;

    // Change tracks.
    for( TRACK* segm = GetBoard()->m_Track;  segm;  segm = segm->Next() )
    {
        if( segm->Type() == PCB_VIA_T )
        {
            VIA*         via = (VIA*) segm;
            PCB_LAYER_ID top_layer, bottom_layer;

            if( via->GetViaType() == VIA_THROUGH )
                continue;

            via->LayerPair( &top_layer, &bottom_layer );

            if( new_layer[bottom_layer] != bottom_layer || new_layer[top_layer] != top_layer )
            {
                commit.Modify( via );
                via->SetLayerPair( new_layer[top_layer], new_layer[bottom_layer] );
                GetGalCanvas()->GetView()->Update( via, KIGFX::GEOMETRY );
                hasChanges = true;
            }
        }
        else
        {
            hasChanges |= processBoardItem( this, commit, segm, new_layer );
        }
    }

    for( BOARD_ITEM* zone : GetBoard()->Zones() )
    {
        hasChanges |= processBoardItem( this, commit, zone, new_layer );
    }

    for( BOARD_ITEM* drawing : GetBoard()->Drawings() )
    {
        hasChanges |= processBoardItem( this, commit, drawing, new_layer );
    }

    if( hasChanges )
    {
        OnModify();
        commit.Push( "Layers moved" );
        GetCanvas()->Refresh();
    }
}
示例#6
0
static void export_vrml_tracks( BOARD* pcb ) //{{{
{
    for( TRACK* track = pcb->m_Track; track != NULL; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
            export_vrml_via( pcb, (SEGVIA*) track );
        else
            export_vrml_line( track->GetLayer(), track->GetStart().x, track->GetStart().y,
                              track->GetEnd().x, track->GetEnd().y, track->GetWidth(), 4 );
    }
}
示例#7
0
// Redraw the moved node according to the mouse cursor position
static void Show_MoveNode( EDA_DRAW_PANEL* aPanel, wxDC* aDC, const wxPoint& aPosition,
                           bool aErase )
{
    auto displ_opts = (PCB_DISPLAY_OPTIONS*) aPanel->GetDisplayOptions();
    wxPoint      moveVector;
    int          tmp = displ_opts->m_DisplayPcbTrackFill;
    GR_DRAWMODE  draw_mode = GR_XOR | GR_HIGHLIGHT;

    displ_opts->m_DisplayPcbTrackFill = false;

#ifndef USE_WX_OVERLAY
    aErase = true;
#else
    aErase = false;
#endif

    // set the new track coordinates
    wxPoint Pos = aPanel->GetParent()->GetCrossHairPosition();

    moveVector = Pos - s_LastPos;
    s_LastPos  = Pos;

    TRACK *track = NULL;

    for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ )
    {
        track = g_DragSegmentList[ii].m_Track;

        if( aErase )
            track->Draw( aPanel, aDC, draw_mode );

        if( track->GetFlags() & STARTPOINT )
            track->SetStart( track->GetStart() + moveVector );

        if( track->GetFlags() & ENDPOINT )
            track->SetEnd( track->GetEnd() + moveVector );

        if( track->Type() == PCB_VIA_T )
            track->SetEnd( track->GetStart() );

        track->Draw( aPanel, aDC, draw_mode );
    }

    displ_opts->m_DisplayPcbTrackFill = tmp;

    // Display track length
    if( track )
    {
        PCB_BASE_FRAME* frame = (PCB_BASE_FRAME*) aPanel->GetParent();
        frame->SetMsgPanel( track );
    }
}
示例#8
0
bool TRACKS_CLEANER::merge_collinear_of_track( TRACK *aSegment )
{
    bool merged_this = false;

    // *WHY* doesn't C++ have prec and succ (or ++ --) like PASCAL?
    for( ENDPOINT_T endpoint = ENDPOINT_START; endpoint <= ENDPOINT_END;
            endpoint = ENDPOINT_T( endpoint + 1 ) )
    {
        // search for a possible segment connected to the current endpoint of the current one
        TRACK *other = aSegment->Next();
        if( other )
        {
            other = aSegment->GetTrack( other, NULL, endpoint, true, false );

            if( other )
            {
                // the two segments must have the same width and the other
                // cannot be a via
                if( (aSegment->GetWidth() == other->GetWidth()) &&
                        (other->Type() == PCB_TRACE_T) )
                {
                    // There can be only one segment connected
                    other->SetState( BUSY, true );
                    TRACK *yet_another = aSegment->GetTrack( m_Brd->m_Track, NULL,
                            endpoint, true, false );
                    other->SetState( BUSY, false );

                    if( !yet_another )
                    {
                        // Try to merge them
                        TRACK *segDelete = mergeCollinearSegmentIfPossible( aSegment,
                                other, endpoint );

                        // Merge succesful, the other one has to go away
                        if( segDelete )
                        {
                            m_Brd->GetRatsnest()->Remove( segDelete );
                            segDelete->ViewRelease();
                            segDelete->DeleteStructure();
                            merged_this = true;
                        }
                    }
                }
            }
        }
    }

    return merged_this;
}
示例#9
0
void PNS_ROUTER::SyncWorld()
{
    if( !m_board )
    {
        TRACEn( 0, "No board attached, aborting sync." );
        return;
    }

    ClearWorld();

    m_world = new PNS_NODE();

    for( MODULE* module = m_board->m_Modules; module; module = module->Next() )
    {
        for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
        {
            PNS_ITEM* solid = syncPad( pad );

            if( solid )
                m_world->Add( solid );
        }
    }

    for( TRACK* t = m_board->m_Track; t; t = t->Next() )
    {
        KICAD_T type = t->Type();
        PNS_ITEM* item = NULL;

        if( type == PCB_TRACE_T )
            item = syncTrack( t );
        else if( type == PCB_VIA_T )
            item = syncVia( static_cast<VIA*>( t ) );

        if( item )
            m_world->Add( item );
    }

    // TODO: Create resolvers in ctor, call resolver->SetBoard() in this->SetBoard() and delete it in dtor
    // TBD: Same remark as with m_sizes.SetMinimumTrackWidth in startRouting()
    int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
    m_clearanceResolver = new PNS_PCBNEW_CLEARANCE_RESOLVER( this );
    m_world->SetClearanceResolver( m_clearanceResolver );
    m_world->SetMaxClearance( 4 * worstClearance );

    m_pairingResolver = new PCBNEW_PAIRING_RESOLVER( m_board );
    m_world->SetPairingResolver( m_pairingResolver );
}
static void export_vrml_tracks( MODEL_VRML& aModel, BOARD* pcb )
{
    for( TRACK* track = pcb->m_Track; track; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
        {
            export_vrml_via( aModel, pcb, (const VIA*) track );
        }
        else if( track->GetLayer() == B_Cu || track->GetLayer() == F_Cu )
            export_vrml_line( aModel, track->GetLayer(),
                    track->GetStart().x * aModel.scale,
                    track->GetStart().y * aModel.scale,
                    track->GetEnd().x * aModel.scale,
                    track->GetEnd().y * aModel.scale,
                    track->GetWidth() * aModel.scale );
    }
}
示例#11
0
void PNS_KICAD_IFACE::SyncWorld( PNS::NODE *aWorld )
{
    if( !m_board )
    {
        wxLogTrace( "PNS", "No board attached, aborting sync." );
        return;
    }

    for( MODULE* module = m_board->m_Modules; module; module = module->Next() )
    {
        for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
        {
            std::unique_ptr< PNS::SOLID > solid = syncPad( pad );

            if( solid )
                aWorld->Add( std::move( solid ) );
        }
    }

    for( TRACK* t = m_board->m_Track; t; t = t->Next() )
    {
        KICAD_T type = t->Type();

        if( type == PCB_TRACE_T ) {
            std::unique_ptr< PNS::SEGMENT > segment = syncTrack( t );
            if( segment ) {
                aWorld->Add( std::move( segment ) );
            }
        } else if( type == PCB_VIA_T ) {
            std::unique_ptr< PNS::VIA > via = syncVia( static_cast<VIA*>( t ) );
            if( via ) {
                aWorld->Add( std::move( via ) );
            }
        }
    }

    int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue();

    delete m_ruleResolver;
    m_ruleResolver = new PNS_PCBNEW_RULE_RESOLVER( m_board, m_router );

    aWorld->SetRuleResolver( m_ruleResolver );
    aWorld->SetMaxClearance( 4 * worstClearance );
}
示例#12
0
void CONNECTIONS::BuildTracksCandidatesList( TRACK* aBegin, TRACK* aEnd)
{
    m_candidates.clear();
    m_firstTrack = m_lastTrack = aBegin;

    unsigned ii = 0;

    // Count candidates ( i.e. end points )
    for( const TRACK* track = aBegin; track; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
            ii++;
        else
            ii += 2;

        m_lastTrack = track;

        if( track == aEnd )
            break;
    }

    // Build candidate list
    m_candidates.reserve( ii );
    for( TRACK* track = aBegin; track; track = track->Next() )
    {
        CONNECTED_POINT candidate( track, track->GetStart() );

        m_candidates.push_back( candidate );
        if( track->Type() != PCB_VIA_T )
        {
            CONNECTED_POINT candidate2( track, track->GetEnd());
            m_candidates.push_back( candidate2 );
        }

        if( track == aEnd )
            break;
    }

    // Sort list by increasing X coordinate,
    // and for increasing Y coordinate when items have the same X coordinate
    // So candidates to the same location are consecutive in list.
    sort( m_candidates.begin(), m_candidates.end(), sortConnectedPointByXthenYCoordinates );
}
示例#13
0
void PNS_ROUTER::SyncWorld()
{
    if( !m_board )
    {
        TRACEn( 0, "No board attached, aborting sync." );
        return;
    }

    ClearWorld();

    m_world = new PNS_NODE();

    for( MODULE* module = m_board->m_Modules; module; module = module->Next() )
    {
        for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
        {
            PNS_ITEM* solid = syncPad( pad );

            if( solid )
                m_world->Add( solid );
        }
    }

    for( TRACK* t = m_board->m_Track; t; t = t->Next() )
    {
        KICAD_T type = t->Type();
        PNS_ITEM* item = NULL;

        if( type == PCB_TRACE_T )
            item = syncTrack( t );
        else if( type == PCB_VIA_T )
            item = syncVia( static_cast<VIA*>( t ) );

        if( item )
            m_world->Add( item );
    }

    int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
    m_clearanceFunc = new PNS_PCBNEW_CLEARANCE_FUNC( this );
    m_world->SetClearanceFunctor( m_clearanceFunc );
    m_world->SetMaxClearance( 4 * worstClearance );
}
示例#14
0
/* Extract the D356 record from the vias */
static void build_via_testpoints( BOARD *aPcb,
    std::vector <D356_RECORD>& aRecords )
{
    wxPoint origin = aPcb->GetAuxOrigin();

    // Enumerate all the track segments and keep the vias
    for( TRACK *track = aPcb->m_Track; track; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
        {
            VIA *via = (VIA*) track;
            NETINFO_ITEM *net = track->GetNet();

            D356_RECORD rk;
            rk.smd = false;
            rk.hole = true;
            if( net )
                rk.netname = net->GetNetname();
            else
                rk.netname = wxEmptyString;
            rk.refdes = wxT("VIA");
            rk.pin = wxT("");
            rk.midpoint = true; // Vias are always midpoints
            rk.drill = via->GetDrillValue();
            rk.mechanical = false;

            LAYER_ID top_layer, bottom_layer;

            via->LayerPair( &top_layer, &bottom_layer );

            rk.access = via_access_code( aPcb, top_layer, bottom_layer );
            rk.x_location = via->GetPosition().x - origin.x;
            rk.y_location = origin.y - via->GetPosition().y;
            rk.x_size = via->GetWidth();
            rk.y_size = 0; // Round so height = 0
            rk.rotation = 0;
            rk.soldermask = 3; // XXX always tented?

            aRecords.push_back( rk );
        }
    }
}
int PCBNEW_CONTROL::ViaDisplayMode( const TOOL_EVENT& aEvent )
{
    KIGFX::PCB_PAINTER* painter =
            static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
    KIGFX::PCB_RENDER_SETTINGS* settings =
            static_cast<KIGFX::PCB_RENDER_SETTINGS*>( painter->GetSettings() );
    DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)m_frame->GetDisplayOptions();

    // Apply new display options to the GAL canvas
    displ_opts->m_DisplayViaFill = !displ_opts->m_DisplayViaFill;
    settings->LoadDisplayOptions( displ_opts );

    for( TRACK* track = getModel<BOARD>()->m_Track; track; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
            track->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
    }

    m_frame->GetGalCanvas()->Refresh();

    return 0;
}
示例#16
0
int PCBNEW_CONTROL::ViaDisplayMode( TOOL_EVENT& aEvent )
{
    KIGFX::PCB_PAINTER* painter =
            static_cast<KIGFX::PCB_PAINTER*>( m_frame->GetGalCanvas()->GetView()->GetPainter() );
    KIGFX::PCB_RENDER_SETTINGS* settings =
            static_cast<KIGFX::PCB_RENDER_SETTINGS*> ( painter->GetSettings() );

    // Apply new display options to the GAL canvas
    DisplayOpt.DisplayViaFill = !DisplayOpt.DisplayViaFill;
    m_frame->m_DisplayViaFill = DisplayOpt.DisplayViaFill;
    settings->LoadDisplayOptions( DisplayOpt );

    BOARD* board = getModel<BOARD>( PCB_T );
    for( TRACK* track = board->m_Track; track; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
            track->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
    }

    setTransitions();

    return 0;
}
void EDA_3D_CANVAS::buildBoard3DView( GLuint aBoardList, GLuint aBodyOnlyList,
                                      REPORTER* aErrorMessages, REPORTER* aActivity  )
{
    BOARD* pcb = GetBoard();

    // If FL_RENDER_SHOW_HOLES_IN_ZONES is true, holes are correctly removed from copper zones areas.
    // If FL_RENDER_SHOW_HOLES_IN_ZONES is false, holes are not removed from copper zones areas,
    // but the calculation time is twice shorter.
    bool remove_Holes = isEnabled( FL_RENDER_SHOW_HOLES_IN_ZONES );

    bool realistic_mode = isRealisticMode();
    bool useTextures = isRealisticMode() && isEnabled( FL_RENDER_TEXTURES );

    // Number of segments to convert a circle to polygon
    // We use 2 values: the first gives a good shape (for instanes rond pads)
    // the second is used to speed up calculations, when a poor approximation is acceptable (holes)
    const int       segcountforcircle   = 18;
    double          correctionFactor    = 1.0 / cos( M_PI / (segcountforcircle * 2.0) );
    const int       segcountLowQuality  = 12;   // segments to draw a circle with low quality
                                                // to reduce time calculations
                                                // for holes and items which do not need
                                                // a fine representation
    double          correctionFactorLQ  = 1.0 / cos( M_PI / (segcountLowQuality * 2.0) );

    SHAPE_POLY_SET  bufferPolys;        // copper areas: tracks, pads and filled zones areas
                                        // when holes are removed from zones
    SHAPE_POLY_SET  bufferPcbOutlines;  // stores the board main outlines
    SHAPE_POLY_SET  bufferZonesPolys;   // copper filled zones areas
                                        // when holes are not removed from zones
    SHAPE_POLY_SET  currLayerHoles;     // Contains holes for the current layer
    SHAPE_POLY_SET  allLayerHoles;      // Contains holes for all layers

    // Build a polygon from edge cut items
    wxString msg;

    if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines, allLayerHoles, &msg ) )
    {
        if( aErrorMessages )
        {
            msg << wxT("\n") << _("Unable to calculate the board outlines.\n"
                                  "Therefore use the board boundary box.") << wxT("\n\n");

            aErrorMessages->Report( msg, REPORTER::RPT_WARNING );
        }
    }

    // Build board holes, with optimization of large holes shape.
    buildBoardThroughHolesPolygonList( allLayerHoles, segcountLowQuality, true );

    LSET            cu_set = LSET::AllCuMask( GetPrm3DVisu().m_CopperLayersCount );

    glNewList( aBoardList, GL_COMPILE );

    for( LSEQ cu = cu_set.CuStack();  cu;  ++cu )
    {
        LAYER_ID layer = *cu;

        // Skip non enabled layers in normal mode,
        // and internal layers in realistic mode
        if( !is3DLayerEnabled( layer ) )
            continue;

        if( aActivity )
            aActivity->Report( wxString::Format( _( "Build layer %s" ), LSET::Name( layer ) ) );

        bufferPolys.RemoveAllContours();
        bufferZonesPolys.RemoveAllContours();
        currLayerHoles.RemoveAllContours();

        // Draw track shapes:
        for( TRACK* track = pcb->m_Track;  track;  track = track->Next() )
        {
            if( !track->IsOnLayer( layer ) )
                continue;

            track->TransformShapeWithClearanceToPolygon( bufferPolys,
                                                         0, segcountforcircle,
                                                         correctionFactor );

            // Add blind/buried via holes
            if( track->Type() == PCB_VIA_T )
            {
                VIA *via = static_cast<VIA*>( track );

                if( via->GetViaType() == VIA_THROUGH )
                    continue;   // already done

                int holediameter = via->GetDrillValue();
                int thickness = GetPrm3DVisu().GetCopperThicknessBIU();
                int hole_outer_radius = (holediameter + thickness) / 2;

                TransformCircleToPolygon( currLayerHoles,
                                          via->GetStart(), hole_outer_radius,
                                          segcountLowQuality );
            }
        }

        // draw pad shapes
        for( MODULE* module = pcb->m_Modules;  module;  module = module->Next() )
        {
            // Note: NPTH pads are not drawn on copper layers when the pad
            // has same shape as its hole
            module->TransformPadsShapesWithClearanceToPolygon( layer,
                                                               bufferPolys,
                                                               0,
                                                               segcountforcircle,
                                                               correctionFactor, true );

            // Micro-wave modules may have items on copper layers
            module->TransformGraphicShapesWithClearanceToPolygonSet( layer,
                                                                     bufferPolys,
                                                                     0,
                                                                     segcountforcircle,
                                                                     correctionFactor );

            // pad holes are already in list.
        }

        // Draw copper zones. Note:
        // * if the holes are removed from copper zones
        // the polygons are stored in bufferPolys (which contains all other polygons)
        // * if the holes are NOT removed from copper zones
        // the polygons are stored in bufferZonesPolys
        if( isEnabled( FL_ZONE ) )
        {
            for( int ii = 0; ii < pcb->GetAreaCount(); ii++ )
            {
                ZONE_CONTAINER* zone = pcb->GetArea( ii );
                LAYER_NUM       zonelayer = zone->GetLayer();

                if( zonelayer == layer )
                {
                    zone->TransformSolidAreasShapesToPolygonSet(
                        remove_Holes ? bufferPolys : bufferZonesPolys,
                        segcountLowQuality, correctionFactorLQ );
                }
            }
        }

        // draw graphic items on copper layers (texts)
        for( BOARD_ITEM* item = pcb->m_Drawings; item; item = item->Next() )
        {
            if( !item->IsOnLayer( layer ) )
                continue;

            switch( item->Type() )
            {
            case PCB_LINE_T:    // should not exist on copper layers
                ( (DRAWSEGMENT*) item )->TransformShapeWithClearanceToPolygon(
                    bufferPolys, 0, segcountforcircle, correctionFactor );
                break;

            case PCB_TEXT_T:
                ( (TEXTE_PCB*) item )->TransformShapeWithClearanceToPolygonSet(
                    bufferPolys, 0, segcountLowQuality, correctionFactor );
                break;

            default:
                break;
            }
        }

        // bufferPolys contains polygons to merge. Many overlaps .
        // Calculate merged polygons
        if( bufferPolys.IsEmpty() )
            continue;

        // Use Clipper lib to subtract holes to copper areas
        if( currLayerHoles.OutlineCount() )
        {
            currLayerHoles.Append(allLayerHoles);
            currLayerHoles.Simplify();
            bufferPolys.BooleanSubtract( currLayerHoles );
        }
        else
            bufferPolys.BooleanSubtract( allLayerHoles );

        int thickness = GetPrm3DVisu().GetLayerObjectThicknessBIU( layer );
        int zpos = GetPrm3DVisu().GetLayerZcoordBIU( layer );

        float zNormal = 1.0f; // When using thickness it will draw first the top and then botton (with z inverted)

        // If we are not using thickness, then the z-normal has to match the layer direction
        // because just one plane will be drawn
        if( !thickness )
            zNormal = Get3DLayer_Z_Orientation( layer );

        if( realistic_mode )
        {
            setGLCopperColor();
        }
        else
        {
            EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( layer );
            SetGLColor( color );
        }

        // If holes are removed from copper zones, bufferPolys contains all polygons
        // to draw (tracks+zones+texts).
        Draw3D_SolidHorizontalPolyPolygons( bufferPolys, zpos, thickness,
                                            GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                            zNormal );

        // If holes are not removed from copper zones (for calculation time reasons,
        // the zone polygons are stored in bufferZonesPolys and have to be drawn now:
        if( !bufferZonesPolys.IsEmpty() )
        {
            Draw3D_SolidHorizontalPolyPolygons( bufferZonesPolys, zpos, thickness,
                                    GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                    zNormal );
        }
    }

    if( aActivity )
        aActivity->Report( _( "Build board body" ) );

    // Draw plated vertical holes inside the board, but not always. They are drawn:
    // - if the board body is not shown, to show the holes.
    // - or if the copper thickness is shown
    if( !isEnabled( FL_SHOW_BOARD_BODY ) || isEnabled( FL_USE_COPPER_THICKNESS ) )
    {
        // Draw vias holes (vertical cylinders)
        for( const TRACK* track = pcb->m_Track;  track;  track = track->Next() )
        {
            if( track->Type() == PCB_VIA_T )
            {
                const VIA *via = static_cast<const VIA*>(track);
                draw3DViaHole( via );
            }
        }

        // Draw pads holes (vertical cylinders)
        for( const MODULE* module = pcb->m_Modules;  module;  module = module->Next() )
        {
            for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
                if( pad->GetAttribute () != PAD_HOLE_NOT_PLATED )
                    draw3DPadHole( pad );
        }
    }

    glEndList();

    // Build the body board:
    glNewList( aBodyOnlyList, GL_COMPILE );

    if( isRealisticMode() )
    {
        setGLEpoxyColor( 1.00 );
    }
    else
    {
        EDA_COLOR_T color = g_ColorsSettings.GetLayerColor( Edge_Cuts );
        SetGLColor( color, 0.7 );
    }

    float copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU();

    // a small offset between substrate and external copper layer to avoid artifacts
    // when drawing copper items on board
    float epsilon = Millimeter2iu( 0.01 );
    float zpos = GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );
    float board_thickness = GetPrm3DVisu().GetLayerZcoordBIU( F_Cu )
                        - GetPrm3DVisu().GetLayerZcoordBIU( B_Cu );

    // items on copper layers and having a thickness = copper_thickness
    // are drawn from zpos - copper_thickness/2 to zpos + copper_thickness
    // therefore substrate position is copper_thickness/2 to
    // substrate_height - copper_thickness/2
    zpos += (copper_thickness + epsilon) / 2.0f;
    board_thickness -= copper_thickness + epsilon;

    bufferPcbOutlines.BooleanSubtract( allLayerHoles );

    if( !bufferPcbOutlines.IsEmpty() )
    {
        Draw3D_SolidHorizontalPolyPolygons( bufferPcbOutlines, zpos + board_thickness / 2.0,
                                            board_thickness, GetPrm3DVisu().m_BiuTo3Dunits, useTextures,
                                            1.0f );
    }

    glEndList();
}
示例#18
0
// Delete null length segments, and intermediate points ..
bool TRACKS_CLEANER::clean_segments()
{
    bool modified = false;
    TRACK*          segment, * nextsegment;
    TRACK*          other;
    int             flag, no_inc;


    // Delete null segments
    for( segment = m_Brd->m_Track; segment; segment = nextsegment )
    {
        nextsegment = segment->Next();

        if( segment->IsNull() )     // Length segment = 0; delete it
            segment->DeleteStructure();
    }

    // Delete redundant segments, i.e. segments having the same end points
    // and layers
    for( segment  = m_Brd->m_Track; segment; segment = segment->Next() )
    {
        for( other = segment->Next(); other; other = nextsegment )
        {
            nextsegment = other->Next();
            bool erase = false;

            if( segment->Type() != other->Type() )
                continue;

            if( segment->GetLayer() != other->GetLayer() )
                continue;

            if( segment->GetNetCode() != other->GetNetCode() )
                break;

            if( ( segment->GetStart() == other->GetStart() ) &&
                ( segment->GetEnd() == other->GetEnd() ) )
                erase = true;

            if( ( segment->GetStart() == other->GetEnd() ) &&
                ( segment->GetEnd() == other->GetStart() ) )
                erase = true;

            // Delete redundant point
            if( erase )
            {
                other->DeleteStructure();
                modified = true;
            }
        }
    }

    // merge collinear segments:
    for( segment = m_Brd->m_Track; segment; segment = nextsegment )
    {
        TRACK*  segStart;
        TRACK*  segEnd;
        TRACK*  segDelete;

        nextsegment = segment->Next();

        if( segment->Type() != PCB_TRACE_T )
            continue;

        flag = no_inc = 0;

        // search for a possible point connected to the START point of the current segment
        for( segStart = segment->Next(); ; )
        {
            segStart = segment->GetTrace( segStart, NULL, FLG_START );

            if( segStart )
            {
                // the two segments must have the same width
                if( segment->GetWidth() != segStart->GetWidth() )
                    break;

                // it cannot be a via
                if( segStart->Type() != PCB_TRACE_T )
                    break;

                // We must have only one segment connected
                segStart->SetState( BUSY, true );
                other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_START );
                segStart->SetState( BUSY, false );

                if( other == NULL )
                    flag = 1;           // OK

                break;
            }
            break;
        }

        if( flag )   // We have the starting point of the segment is connected to an other segment
        {
            segDelete = mergeCollinearSegmentIfPossible( segment, segStart, FLG_START );

            if( segDelete )
            {
                no_inc = 1;
                segDelete->DeleteStructure();
                modified = true;
            }
        }

        // search for a possible point connected to the END point of the current segment:
        for( segEnd = segment->Next(); ; )
        {
            segEnd = segment->GetTrace( segEnd, NULL, FLG_END );

            if( segEnd )
            {
                if( segment->GetWidth() != segEnd->GetWidth() )
                    break;

                if( segEnd->Type() != PCB_TRACE_T )
                    break;

                // We must have only one segment connected
                segEnd->SetState( BUSY, true );
                other = segment->GetTrace( m_Brd->m_Track, NULL, FLG_END );
                segEnd->SetState( BUSY, false );

                if( other == NULL )
                    flag |= 2;          // Ok

                break;
            }
            else
            {
                break;
            }
        }

        if( flag & 2 )  // We have the ending point of the segment is connected to an other segment
        {
            segDelete = mergeCollinearSegmentIfPossible( segment, segEnd, FLG_END );

            if( segDelete )
            {
                no_inc = 1;
                segDelete->DeleteStructure();
                modified = true;
            }
        }

        if( no_inc ) // The current segment was modified, retry to merge it
            nextsegment = segment->Next();
    }

    return modified;
}
示例#19
0
/*
 *  Delete dangling tracks
 *  Vias:
 *  If a via is only connected to a dangling track, it also will be removed
 */
bool TRACKS_CLEANER::deleteUnconnectedTracks()
{
    if( m_Brd->m_Track == NULL )
        return false;

    bool modified = false;
    bool item_erased = true;
    while( item_erased )    // Iterate when at least one track is deleted
    {
        item_erased = false;
        TRACK* next_track;
        for( TRACK * track = m_Brd->m_Track; track ; track = next_track )
        {
            next_track = track->Next();

            int flag_erase = 0; //Not connected indicator
            int type_end = 0;

            if( track->GetState( START_ON_PAD ) )
                type_end |= START_ON_PAD;

            if( track->GetState( END_ON_PAD ) )
                type_end |= END_ON_PAD;

            // if the track start point is not connected to a pad,
            // test if this track start point is connected to another track
            // For via test, an enhancement could be to test if connected
            // to 2 items on different layers.
            // Currently a via must be connected to 2 items, that can be on the same layer
            LAYER_NUM top_layer, bottom_layer;
            ZONE_CONTAINER* zone;

            if( (type_end & START_ON_PAD ) == 0 )
            {
                TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_START );

                if( other == NULL )     // Test a connection to zones
                {
                    if( track->Type() != PCB_VIA_T )
                    {
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
                                                               track->GetLayer(),
                                                               track->GetLayer(),
                                                               track->GetNetCode() );
                    }
                    else
                    {
                        ((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetStart(),
                                                               top_layer, bottom_layer,
                                                               track->GetNetCode() );
                    }
                }

                if( (other == NULL) && (zone == NULL) )
                {
                    flag_erase |= 1;
                }
                else    // segment, via or zone connected to this end
                {
                    track->start = other;
                    // If a via is connected to this end,
                    // test if this via has a second item connected.
                    // If no, remove it with the current segment

                    if( other && other->Type() == PCB_VIA_T )
                    {
                        // search for another segment following the via
                        track->SetState( BUSY, true );

                        SEGVIA* via = (SEGVIA*) other;
                        other = via->GetTrace( m_Brd->m_Track, NULL, FLG_START );

                        if( other == NULL )
                        {
                            via->LayerPair( &top_layer, &bottom_layer );
                            zone = m_Brd->HitTestForAnyFilledArea( via->GetStart(),
                                                                   bottom_layer,
                                                                   top_layer,
                                                                   via->GetNetCode() );
                        }

                        if( (other == NULL) && (zone == NULL) )
                            flag_erase |= 2;

                        track->SetState( BUSY, false );
                    }
                }
            }

            // if track end point is not connected to a pad,
            // test if this track end point is connected to an other track
            if( (type_end & END_ON_PAD ) == 0 )
            {
                TRACK* other = track->GetTrace( m_Brd->m_Track, NULL, FLG_END );

                if( other == NULL )     // Test a connection to zones
                {
                    if( track->Type() != PCB_VIA_T )
                    {
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
                                                               track->GetLayer(),
                                                               track->GetLayer(),
                                                               track->GetNetCode() );
                    }
                    else
                    {
                        ((SEGVIA*)track)->LayerPair( &top_layer, &bottom_layer );
                        zone = m_Brd->HitTestForAnyFilledArea( track->GetEnd(),
                                                               top_layer, bottom_layer,
                                                               track->GetNetCode() );
                    }
                }

                if ( (other == NULL) && (zone == NULL) )
                {
                    flag_erase |= 0x10;
                }
                else     // segment, via or zone connected to this end
                {
                    track->end = other;

                    // If a via is connected to this end, test if this via has a second item connected
                    // if no, remove it with the current segment

                    if( other && other->Type() == PCB_VIA_T )
                    {
                        // search for another segment following the via

                        track->SetState( BUSY, true );

                        SEGVIA* via = (SEGVIA*) other;
                        other = via->GetTrace( m_Brd->m_Track, NULL, FLG_END );

                        if( other == NULL )
                        {
                            via->LayerPair( &top_layer, &bottom_layer );
                            zone = m_Brd->HitTestForAnyFilledArea( via->GetEnd(),
                                                                   bottom_layer, top_layer,
                                                                   via->GetNetCode() );
                        }

                        if( (other == NULL) && (zone == NULL) )
                            flag_erase |= 0x20;

                        track->SetState( BUSY, false );
                    }
                }
            }

            if( flag_erase )
            {
                // remove segment from board
                track->DeleteStructure();
                // iterate, because a track connected to the deleted track
                // is now perhaps now not connected and should be deleted
                item_erased = true;
                modified = true;
            }
        }
    }

    return modified;
}
示例#20
0
bool TRACKS_CLEANER::clean_vias()
{
    TRACK* next_track;
    bool modified = false;

    for( TRACK* track = m_Brd->m_Track; track; track = track->Next() )
    {
        // Correct via m_End defects (if any)
        if( track->Type() == PCB_VIA_T )
        {
            if( track->GetStart() != track->GetEnd() )
                track->SetEnd( track->GetStart() );
        }

        if( track->GetShape() != VIA_THROUGH )
            continue;

        // Search and delete others vias at same location
        TRACK* alt_track = track->Next();

        for( ; alt_track != NULL; alt_track = next_track )
        {
            next_track = alt_track->Next();

            if( alt_track->GetShape() != VIA_THROUGH )
                continue;

            if( alt_track->GetStart() != track->GetStart() )
                continue;

            // delete via
            alt_track->UnLink();
            delete alt_track;
            modified = true;
        }
    }

    // Delete Via on pads at same location
    for( TRACK* track = m_Brd->m_Track; track != NULL; track = next_track )
    {
        next_track = track->Next();

        if( track->GetShape() != VIA_THROUGH )
            continue;

        // Examine the list of connected pads:
        // if one pad through is found, the via can be removed
        for( unsigned ii = 0; ii < track->m_PadsConnected.size(); ii++ )
        {
            D_PAD * pad = track->m_PadsConnected[ii];

            if( (pad->GetLayerMask() & ALL_CU_LAYERS) == ALL_CU_LAYERS )
            {
                // redundant: via delete it
                track->UnLink();
                delete track;
                modified = true;
                break;
            }
        }
    }

    return modified;
}
示例#21
0
void DRC::testTexts()
{
    std::vector<wxPoint> textShape;      // a buffer to store the text shape (set of segments)
    std::vector<D_PAD*> padList = m_pcb->GetPads();

    // Test text areas for vias, tracks and pads inside text areas
    for( BOARD_ITEM* item = m_pcb->m_Drawings; item; item = item->Next() )
    {
        // Drc test only items on copper layers
        if( ! IsCopperLayer( item->GetLayer() ) )
            continue;

        // only texts on copper layers are tested
        if( item->Type() !=  PCB_TEXT_T )
            continue;

        textShape.clear();

        // So far the bounding box makes up the text-area
        TEXTE_PCB* text = (TEXTE_PCB*) item;
        text->TransformTextShapeToSegmentList( textShape );

        if( textShape.size() == 0 )     // Should not happen (empty text?)
            continue;

        for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() )
        {
            if( ! track->IsOnLayer( item->GetLayer() ) )
                    continue;

            // Test the distance between each segment and the current track/via
            int min_dist = ( track->GetWidth() + text->GetThickness() ) /2 +
                           track->GetClearance(NULL);

            if( track->Type() == PCB_TRACE_T )
            {
                SEG segref( track->GetStart(), track->GetEnd() );

                // Error condition: Distance between text segment and track segment is
                // smaller than the clearance of the segment
                for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
                {
                    SEG segtest( textShape[jj], textShape[jj+1] );
                    int dist = segref.Distance( segtest );

                    if( dist < min_dist )
                    {
                        addMarkerToPcb( fillMarker( track, text,
                                                    DRCE_TRACK_INSIDE_TEXT,
                                                    m_currentMarker ) );
                        m_currentMarker = nullptr;
                        break;
                    }
                }
            }
            else if( track->Type() == PCB_VIA_T )
            {
                // Error condition: Distance between text segment and via is
                // smaller than the clearance of the via
                for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
                {
                    SEG segtest( textShape[jj], textShape[jj+1] );

                    if( segtest.PointCloserThan( track->GetPosition(), min_dist ) )
                    {
                        addMarkerToPcb( fillMarker( track, text,
                                                    DRCE_VIA_INSIDE_TEXT, m_currentMarker ) );
                        m_currentMarker = nullptr;
                        break;
                    }
                }
            }
        }

        // Test pads
        for( unsigned ii = 0; ii < padList.size(); ii++ )
        {
            D_PAD* pad = padList[ii];

            if( ! pad->IsOnLayer( item->GetLayer() ) )
                    continue;

            wxPoint shape_pos = pad->ShapePos();

            for( unsigned jj = 0; jj < textShape.size(); jj += 2 )
            {
                /* In order to make some calculations more easier or faster,
                 * pads and tracks coordinates will be made relative
                 * to the segment origin
                 */
                wxPoint origin = textShape[jj];  // origin will be the origin of other coordinates
                m_segmEnd = textShape[jj+1] - origin;
                wxPoint delta = m_segmEnd;
                m_segmAngle = 0;

                // for a non horizontal or vertical segment Compute the segment angle
                // in tenths of degrees and its length
                if( delta.x || delta.y )    // delta.x == delta.y == 0 for vias
                {
                    // Compute the segment angle in 0,1 degrees
                    m_segmAngle = ArcTangente( delta.y, delta.x );

                    // Compute the segment length: we build an equivalent rotated segment,
                    // this segment is horizontal, therefore dx = length
                    RotatePoint( &delta, m_segmAngle );    // delta.x = length, delta.y = 0
                }

                m_segmLength = delta.x;
                m_padToTestPos = shape_pos - origin;

                if( !checkClearanceSegmToPad( pad, text->GetThickness(),
                                              pad->GetClearance(NULL) ) )
                {
                    addMarkerToPcb( fillMarker( pad, text,
                                                DRCE_PAD_INSIDE_TEXT, m_currentMarker ) );
                    m_currentMarker = nullptr;
                    break;
                }
            }
        }
    }
}
void EXCELLON_WRITER::CreateDrillandMapFilesSet(  const wxString& aPlotDirectory,
                                            bool aGenDrill, bool aGenMap,
                                            REPORTER * aReporter )
{
    wxFileName fn;
    wxString msg;

    wxString    layername_extend;   // added to the board filefame to create a full filename
                                    //(board fileName + layer pair names)

    // If some buried/blind vias are found, drill files are created
    // layer pair by layer pair for buried vias
    bool hasBuriedVias = false;

    for( TRACK* track = m_pcb->m_Track; track != NULL; track = track->Next() )
    {
        if( track->Type() == PCB_VIA_T )
        {
            const VIA *via = static_cast<const VIA*>( track );

            if( via->GetViaType() == VIA_MICROVIA ||
                via->GetViaType() == VIA_BLIND_BURIED )
            {
                hasBuriedVias = true;
                break;
            }
        }
    }


    int        layer1 = F_Cu;
    int        layer2 = B_Cu;
    bool       gen_through_holes = true;
    bool       gen_NPTH_holes    = false;

    for( ; ; )
    {
        BuildHolesList( layer1, layer2, gen_through_holes ? false : true,
                                       gen_NPTH_holes, m_merge_PTH_NPTH );

        if( GetHolesCount() > 0 ) // has holes?
        {
            fn = m_pcb->GetFileName();
            layername_extend.Empty();

            if( gen_NPTH_holes )
            {
                layername_extend << wxT( "-NPTH" );
            }
            else if( !gen_through_holes )
            {
                if( layer1 == F_Cu )
                    layername_extend << wxT( "-front" );
                else
                    layername_extend << wxT( "-inner" ) << layer1;

                if( layer2 == B_Cu )
                    layername_extend << wxT( "-back" );
                else
                    layername_extend << wxT( "-inner" ) << layer2;
            }

            fn.SetName( fn.GetName() + layername_extend );

            fn.SetPath( aPlotDirectory );

            if( aGenDrill )
            {
                fn.SetExt( DrillFileExtension );
                wxString fullFilename = fn.GetFullPath();

                FILE* file = wxFopen( fullFilename, wxT( "w" ) );

                if( file == NULL )
                {
                    if( aReporter )
                    {
                        msg.Printf(  _( "** Unable to create %s **\n" ),
                                          GetChars( fullFilename ) );
                        aReporter->Report( msg );
                    }
                    break;
                }
                else
                {
                    if( aReporter )
                    {
                        msg.Printf( _( "Create file %s\n" ), GetChars( fullFilename ) );
                        aReporter->Report( msg );
                    }
                }

                CreateDrillFile( file );
            }

            if( aGenMap )
            {
                fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
                wxString fullfilename = fn.GetFullPath() + wxT( "-drl_map" );
                fullfilename << wxT(".") << GetDefaultPlotExtension( m_mapFileFmt );

                bool success = GenDrillMapFile( fullfilename, m_mapFileFmt );

                if( ! success )
                {
                    if( aReporter )
                    {
                        msg.Printf( _( "** Unable to create %s **\n" ), GetChars( fullfilename ) );
                        aReporter->Report( msg );
                    }

                    return;
                }
                else
                {
                    if( aReporter )
                    {
                        msg.Printf( _( "Create file %s\n" ), GetChars( fullfilename ) );
                        aReporter->Report( msg );
                    }
                }
            }
        }

        if( gen_NPTH_holes )    // The last drill file was created
            break;

        if( !hasBuriedVias )
            gen_NPTH_holes = true;
        else
        {
            if( gen_through_holes )
                layer2 = layer1 + 1;    // done with through-board holes, prepare generation of first layer pair
            else
            {
                if( layer2 >= B_Cu )    // no more layer pair to consider
                {
                    layer1 = F_Cu;
                    layer2 = B_Cu;
                    gen_NPTH_holes = true;
                    continue;
                }

                layer1++;
                layer2++;                      // use next layer pair

                if( layer2 == m_pcb->GetCopperLayerCount() - 1 )
                    layer2 = B_Cu;      // the last layer is always the back layer
            }

            gen_through_holes = false;
        }
    }
}
void EXCELLON_WRITER::BuildHolesList( int aFirstLayer,
                                      int aLastLayer,
                                      bool aExcludeThroughHoles,
                                      bool aGenerateNPTH_list,
                                      bool aMergePTHNPTH )
{
    HOLE_INFO new_hole;
    int       hole_value;

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

    if( (aFirstLayer >= 0) && (aLastLayer >= 0) )
    {
        if( aFirstLayer > aLastLayer )
            EXCHG( aFirstLayer, aLastLayer );
    }

    if ( aGenerateNPTH_list && aMergePTHNPTH )
    {
        return;
    }

    // build hole list for vias
    if( ! aGenerateNPTH_list )  // vias are always plated !
    {
        for( TRACK* track = m_pcb->m_Track;  track;  track = track->Next() )
        {
            if( track->Type() != PCB_VIA_T )
                continue;

            SEGVIA* via = (SEGVIA*) track;
            hole_value = via->GetDrillValue();

            if( hole_value == 0 )
                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
            if( (new_hole.m_Hole_Bottom_Layer > aFirstLayer) && (aFirstLayer >= 0) )
                continue;

            if( (new_hole.m_Hole_Top_Layer < aLastLayer) && (aLastLayer >= 0) )
                continue;

            if( aExcludeThroughHoles  && (new_hole.m_Hole_Bottom_Layer == LAYER_N_BACK)
               && (new_hole.m_Hole_Top_Layer == LAYER_N_FRONT) )
                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 && ! aMergePTHNPTH )
                    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 = LAYER_N_BACK;
                new_hole.m_Hole_Top_Layer    = LAYER_N_FRONT;// 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++;
    }
}
void EDA_3D_CANVAS::buildBoardThroughHolesPolygonList( SHAPE_POLY_SET& allBoardHoles,
                                                int aSegCountPerCircle, bool aOptimizeLargeCircles )
{
    // hole diameter value to change seg count by circle:
    int small_hole_limit = Millimeter2iu( 1.0 );
    int copper_thickness = GetPrm3DVisu().GetCopperThicknessBIU();

    BOARD* pcb = GetBoard();

    // Build holes of through vias:
    for( TRACK* track = pcb->m_Track;  track;  track = track->Next() )
    {
        if( track->Type() != PCB_VIA_T )
            continue;

        VIA *via = static_cast<VIA*>( track );

        if( via->GetViaType() != VIA_THROUGH )
            continue;

        int holediameter = via->GetDrillValue();
        int hole_outer_radius = (holediameter + copper_thickness) / 2;

        TransformCircleToPolygon( allBoardHoles,
                                  via->GetStart(), hole_outer_radius,
                                  aSegCountPerCircle );
    }

    // Build holes of through pads:
    for( MODULE* footprint = pcb->m_Modules; footprint; footprint = footprint->Next() )
    {
        for( D_PAD* pad = footprint->Pads(); pad; pad = pad->Next() )
        {
            // Calculate a factor to apply to segcount for large holes ( > 1 mm)
            // (bigger pad drill size -> more segments) because holes in pads can have
            // very different sizes and optimizing this segcount gives a better look
            // Mainly mounting holes have a size bigger than small_hole_limit
            wxSize padHole = pad->GetDrillSize();

            if( ! padHole.x )       // Not drilled pad like SMD pad
                continue;

            // we use the hole diameter to calculate the seg count.
            // for round holes, padHole.x == padHole.y
            // for oblong holes, the diameter is the smaller of (padHole.x, padHole.y)
            int diam = std::min( padHole.x, padHole.y );
            int segcount = aSegCountPerCircle;

            if( diam > small_hole_limit )
            {
                double segFactor = (double)diam / small_hole_limit;
                segcount = (int)(aSegCountPerCircle * segFactor);

                // limit segcount to 48. For a circle this is a very good approx.
                if( segcount > 48 )
                    segcount = 48;
            }

            // The hole in the body is inflated by copper thickness.
            int inflate = copper_thickness;

            // If not plated, no copper.
            if( pad->GetAttribute () == PAD_HOLE_NOT_PLATED )
                inflate = 0;

            pad->BuildPadDrillShapePolygon( allBoardHoles, inflate, segcount );
        }
    }

    allBoardHoles.Simplify();
}
示例#25
0
/* This function is used by Retrace and read the autorouting matrix data cells to create
 * the real track on the physical board
 */
static void OrCell_Trace( BOARD* pcb, int col, int row,
                          int side, int orient, int current_net_code )
{
    if( orient == HOLE )  // placement of a via
    {
        VIA *newVia = new VIA( pcb );

        g_CurrentTrackList.PushBack( newVia );

        g_CurrentTrackSegment->SetState( TRACK_AR, true );
        g_CurrentTrackSegment->SetLayer( F_Cu );

        g_CurrentTrackSegment->SetStart(wxPoint( pcb->GetBoundingBox().GetX() +
                                                ( RoutingMatrix.m_GridRouting * row ),
                                                pcb->GetBoundingBox().GetY() +
                                                ( RoutingMatrix.m_GridRouting * col )));
        g_CurrentTrackSegment->SetEnd( g_CurrentTrackSegment->GetStart() );

        g_CurrentTrackSegment->SetWidth( pcb->GetDesignSettings().GetCurrentViaSize() );
        newVia->SetViaType( pcb->GetDesignSettings().m_CurrentViaType );

        g_CurrentTrackSegment->SetNetCode( current_net_code );
    }
    else    // placement of a standard segment
    {
        TRACK *newTrack = new TRACK( pcb );
        int    dx0, dy0, dx1, dy1;


        g_CurrentTrackList.PushBack( newTrack );

        g_CurrentTrackSegment->SetLayer( g_Route_Layer_BOTTOM );

        if( side == TOP )
            g_CurrentTrackSegment->SetLayer( g_Route_Layer_TOP );

        g_CurrentTrackSegment->SetState( TRACK_AR, true );
        g_CurrentTrackSegment->SetEnd( wxPoint( pcb->GetBoundingBox().GetX() +
                                         ( RoutingMatrix.m_GridRouting * row ),
                                         pcb->GetBoundingBox().GetY() +
                                         ( RoutingMatrix.m_GridRouting * col )));
        g_CurrentTrackSegment->SetNetCode( current_net_code );

        if( g_CurrentTrackSegment->Back() == NULL ) // Start trace.
        {
            g_CurrentTrackSegment->SetStart( wxPoint( segm_fX, segm_fY ) );

            // Placement on the center of the pad if outside grid.
            dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
            dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;

            dx0 = pt_cur_ch->m_PadEnd->GetPosition().x - g_CurrentTrackSegment->GetStart().x;
            dy0 = pt_cur_ch->m_PadEnd->GetPosition().y - g_CurrentTrackSegment->GetStart().y;

            // If aligned, change the origin point.
            if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
            {
                g_CurrentTrackSegment->SetStart( pt_cur_ch->m_PadEnd->GetPosition() );
            }
            else    // Creation of a supplemental segment
            {
                g_CurrentTrackSegment->SetStart( pt_cur_ch->m_PadEnd->GetPosition() );

                newTrack = (TRACK*)g_CurrentTrackSegment->Clone();
                newTrack->SetStart( g_CurrentTrackSegment->GetEnd());

                g_CurrentTrackList.PushBack( newTrack );
            }
        }
        else
        {
            if( g_CurrentTrackSegment->Back() )
            {
                g_CurrentTrackSegment->SetStart( g_CurrentTrackSegment->Back()->GetEnd() );
            }
        }

        g_CurrentTrackSegment->SetWidth( pcb->GetDesignSettings().GetCurrentTrackWidth() );

        if( g_CurrentTrackSegment->GetStart() != g_CurrentTrackSegment->GetEnd() )
        {
            // Reduce aligned segments by one.
            TRACK* oldTrack = g_CurrentTrackSegment->Back();

            if( oldTrack &&  oldTrack->Type() != PCB_VIA_T )
            {
                dx1 = g_CurrentTrackSegment->GetEnd().x - g_CurrentTrackSegment->GetStart().x;
                dy1 = g_CurrentTrackSegment->GetEnd().y - g_CurrentTrackSegment->GetStart().y;

                dx0 = oldTrack->GetEnd().x - oldTrack->GetStart().x;
                dy0 = oldTrack->GetEnd().y - oldTrack->GetStart().y;

                if( abs( dx0 * dy1 ) == abs( dx1 * dy0 ) )
                {
                    oldTrack->SetEnd( g_CurrentTrackSegment->GetEnd() );

                    delete g_CurrentTrackList.PopBack();
                }
            }
        }
    }
}
bool PCB_EDIT_FRAME::Other_Layer_Route( TRACK* aTrack, wxDC* DC )
{
    unsigned    itmp;

    if( aTrack == NULL )
    {
        if( GetActiveLayer() != GetScreen()->m_Route_Layer_TOP )
            SetActiveLayer( GetScreen()->m_Route_Layer_TOP );
        else
            SetActiveLayer( GetScreen()->m_Route_Layer_BOTTOM );

        UpdateStatusBar();
        return true;
    }

    // Avoid more than one via on the current location:
    if( GetBoard()->GetViaByPosition( g_CurrentTrackSegment->GetEnd(),
                                      g_CurrentTrackSegment->GetLayer() ) )
        return false;

    for( TRACK* segm = g_FirstTrackSegment;  segm;  segm = segm->Next() )
    {
        if( segm->Type() == PCB_VIA_T && g_CurrentTrackSegment->GetEnd() == segm->GetStart() )
            return false;
    }

    // Is the current segment Ok (no DRC error) ?
    if( g_Drc_On )
    {
        if( BAD_DRC==m_drc->Drc( g_CurrentTrackSegment, GetBoard()->m_Track ) )
            // DRC error, the change layer is not made
            return false;

        // Handle 2 segments.
        if( g_TwoSegmentTrackBuild && g_CurrentTrackSegment->Back() )
        {
            if( BAD_DRC == m_drc->Drc( g_CurrentTrackSegment->Back(), GetBoard()->m_Track ) )
                return false;
        }
    }

    /* Save current state before placing a via.
     * If the via cannot be placed this current state will be reused
     */
    itmp = g_CurrentTrackList.GetCount();
    Begin_Route( g_CurrentTrackSegment, DC );

    m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );

    // create the via
    VIA* via = new VIA( GetBoard() );
    via->SetFlags( IS_NEW );
    via->SetViaType( GetDesignSettings().m_CurrentViaType );
    via->SetNetCode( GetBoard()->GetHighLightNetCode() );
    via->SetPosition( g_CurrentTrackSegment->GetEnd() );

    // for microvias, the size and hole will be changed later.
    via->SetWidth( GetDesignSettings().GetCurrentViaSize());
    via->SetDrill( GetDesignSettings().GetCurrentViaDrill() );

    // Usual via is from copper to component.
    // layer pair is B_Cu and F_Cu.
    via->SetLayerPair( B_Cu, F_Cu );

    LAYER_ID first_layer = GetActiveLayer();
    LAYER_ID last_layer;

    // prepare switch to new active layer:
    if( first_layer != GetScreen()->m_Route_Layer_TOP )
        last_layer = GetScreen()->m_Route_Layer_TOP;
    else
        last_layer = GetScreen()->m_Route_Layer_BOTTOM;

    // Adjust the actual via layer pair
    switch( via->GetViaType() )
    {
    case VIA_BLIND_BURIED:
        via->SetLayerPair( first_layer, last_layer );
        break;

    case VIA_MICROVIA:  // from external to the near neighbor inner layer
        {
            LAYER_ID last_inner_layer = ToLAYER_ID( ( GetBoard()->GetCopperLayerCount() - 2 ) );

            if( first_layer == B_Cu )
                last_layer = last_inner_layer;
            else if( first_layer == F_Cu )
                last_layer = In1_Cu;
            else if( first_layer == last_inner_layer )
                last_layer = B_Cu;
            else if( first_layer == In1_Cu )
                last_layer = F_Cu;
            // else error: will be removed later
            via->SetLayerPair( first_layer, last_layer );

            // Update diameter and hole size, which where set previously
            // for normal vias
            NETINFO_ITEM* net = via->GetNet();
            via->SetWidth( net->GetMicroViaSize() );
            via->SetDrill( net->GetMicroViaDrillSize() );
        }
        break;

    default:
        break;
    }

    if( g_Drc_On && BAD_DRC == m_drc->Drc( via, GetBoard()->m_Track ) )
    {
        // DRC fault: the Via cannot be placed here ...
        delete via;

        m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );

        // delete the track(s) added in Begin_Route()
        while( g_CurrentTrackList.GetCount() > itmp )
        {
            Delete_Segment( DC, g_CurrentTrackSegment );
        }

         SetCurItem( g_CurrentTrackSegment, false );

        // Refresh DRC diag, erased by previous calls
        if( m_drc->GetCurrentMarker() )
            SetMsgPanel( m_drc->GetCurrentMarker() );

        return false;
    }

    SetActiveLayer( last_layer );

    TRACK*  lastNonVia = g_CurrentTrackSegment;

    /* A new via was created. It was Ok.
     */
    g_CurrentTrackList.PushBack( via );

    /* The via is now in linked list and we need a new track segment
     * after the via, starting at via location.
     * it will become the new current segment (from via to the mouse cursor)
     */

    TRACK* track = (TRACK*)lastNonVia->Clone();

    /* the above creates a new segment from the last entered segment, with the
     * current width, flags, netcode, etc... values.
     * layer, start and end point are not correct,
     * and will be modified next
     */

    // set the layer to the new value
    track->SetLayer( GetActiveLayer() );

    /* the start point is the via position and the end point is the cursor
     * which also is on the via (will change when moving mouse)
     */
    track->SetEnd( via->GetStart() );
    track->SetStart( via->GetStart() );

    g_CurrentTrackList.PushBack( track );

    if( g_TwoSegmentTrackBuild )
    {
        // Create a second segment (we must have 2 track segments to adjust)
        g_CurrentTrackList.PushBack( (TRACK*)g_CurrentTrackSegment->Clone() );
    }

    m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
    SetMsgPanel( via );
    UpdateStatusBar();

    return true;
}
/**
 * Function Magnetize
 * tests to see if there are any magnetic items within near reach of the given
 * "curpos".  If yes, then curpos is adjusted appropriately according to that
 * near magnetic item and true is returned.
 * @param frame = the current frame
 * @param aCurrentTool = the current tool id (from vertical right toolbar)
 * @param aGridSize = the current grid size
 * @param on_grid = the on grid position near initial position ( often on_grid = curpos)
 * @param curpos The initial position, and what to adjust if a change is needed.
 * @return bool - true if the position was adjusted magnetically, else false.
 */
bool Magnetize( PCB_EDIT_FRAME* frame, int aCurrentTool, wxSize aGridSize,
                wxPoint on_grid, wxPoint* curpos )
{
    bool    doCheckNet = g_MagneticPadOption != capture_always && g_Drc_On;
    bool    doTrack = false;
    bool    doPad = false;
    bool    amMovingVia = false;

    BOARD* m_Pcb = frame->GetBoard();
    TRACK*      currTrack = g_CurrentTrackSegment;
    BOARD_ITEM* currItem  = frame->GetCurItem();
    PCB_SCREEN* screen = frame->GetScreen();
    wxPoint     pos = frame->RefPos( true );

    // D( printf( "currTrack=%p currItem=%p currTrack->Type()=%d currItem->Type()=%d\n",  currTrack, currItem, currTrack ? currTrack->Type() : 0, currItem ? currItem->Type() : 0 ); )

    if( !currTrack && currItem && currItem->Type()==PCB_VIA_T && currItem->GetFlags() )
    {
        // moving a VIA
        currTrack = (TRACK*) currItem;
        amMovingVia = true;

        return false;   // comment this return out and play with it.
    }
    else if( currItem != currTrack )
    {
        currTrack = NULL;
    }

    if( g_MagneticPadOption == capture_always )
        doPad = true;

    if( g_MagneticTrackOption == capture_always )
        doTrack = true;

    if( aCurrentTool == ID_TRACK_BUTT || amMovingVia )
    {
        int q = capture_cursor_in_track_tool;

        if( g_MagneticPadOption == q )
            doPad = true;

        if( g_MagneticTrackOption == q )
            doTrack = true;
    }

    // D(printf("doPad=%d doTrack=%d aCurrentTool=%d amMovingVia=%d\n", doPad, doTrack, aCurrentTool, amMovingVia );)

    //  The search precedence order is pads, then tracks/vias

    if( doPad )
    {
        LSET    layer_mask( screen->m_Active_Layer );
        D_PAD*  pad = m_Pcb->GetPad( pos, layer_mask );

        if( pad )
        {
            if( doCheckNet && currTrack && currTrack->GetNetCode() != pad->GetNetCode() )
                return false;

            *curpos = pad->GetPosition();
            return true;
        }
    }

    // after pads, only track & via tests remain, skip them if not desired
    if( doTrack )
    {
        LAYER_ID layer = screen->m_Active_Layer;

        for( TRACK* via = m_Pcb->m_Track;
                via && (via = via->GetVia( *curpos, layer )) != NULL;
                via = via->Next() )
        {
            if( via != currTrack )   // a via cannot influence itself
            {
                if( !doCheckNet || !currTrack || currTrack->GetNetCode() == via->GetNetCode() )
                {
                    *curpos = via->GetStart();
                    // D(printf("via hit\n");)
                    return true;
                }
            }
        }

        if( !currTrack )
        {
            LSET layers( layer );

            TRACK* track = m_Pcb->GetVisibleTrack( m_Pcb->m_Track, pos, layers );

            if( !track || track->Type() != PCB_TRACE_T )
            {
                // D(printf("!currTrack and track=%p not found, layer_mask=0x%X\n", track, layer_mask );)
                return false;
            }

            // D( printf( "Project\n" ); )
            return Project( curpos, on_grid, track );
        }

        /*
         * In two segment mode, ignore the final segment if it's inside a grid square.
         */
        if( !amMovingVia && currTrack && g_TwoSegmentTrackBuild && currTrack->Back()
            && currTrack->GetStart().x - aGridSize.x < currTrack->GetEnd().x
            && currTrack->GetStart().x + aGridSize.x > currTrack->GetEnd().x
            && currTrack->GetStart().y - aGridSize.y < currTrack->GetEnd().y
            && currTrack->GetStart().y + aGridSize.y > currTrack->GetEnd().y )
        {
            currTrack = currTrack->Back();
        }


        for( TRACK* track = m_Pcb->m_Track; track; track = track->Next() )
        {
            if( track->Type() != PCB_TRACE_T )
                continue;

            if( doCheckNet && currTrack && currTrack->GetNetCode() != track->GetNetCode() )
                continue;

            if( m_Pcb->IsLayerVisible( track->GetLayer() ) == false )
                continue;

            // omit the layer check if moving a via
            if( !amMovingVia && !track->IsOnLayer( layer ) )
                continue;

            if( !track->HitTest( *curpos ) )
                continue;

            // D(printf( "have track prospect\n");)

            if( Join( curpos, track->GetStart(), track->GetEnd(), currTrack->GetStart(), currTrack->GetEnd() ) )
            {
                // D(printf( "join currTrack->Type()=%d\n", currTrack->Type() );)
                return true;
            }

            if( aCurrentTool == ID_TRACK_BUTT || amMovingVia )
            {
                // At this point we have a drawing mouse on a track, we are drawing
                // a new track and that new track is parallel to the track the
                // mouse is on. Find the nearest end point of the track under mouse
                // to the mouse and return that.
                double distStart = GetLineLength( *curpos, track->GetStart() );
                double distEnd   = GetLineLength( *curpos, track->GetEnd() );

                // if track not via, or if its a via dragging but not with its adjacent track
                if( currTrack->Type() != PCB_VIA_T ||
                    ( currTrack->GetStart() != track->GetStart() && currTrack->GetStart() != track->GetEnd() ))
                {
                    double max_dist = currTrack->GetWidth() / 2.0f;

                    if( distStart <= max_dist )
                    {
                        // D(printf("nearest end is start\n");)
                        *curpos = track->GetStart();
                        return true;
                    }

                    if( distEnd <= max_dist )
                    {
                        // D(printf("nearest end is end\n");)
                        *curpos = track->GetEnd();
                        return true;
                    }

                    // @todo otherwise confine curpos such that it stays centered within "track"
                }
            }
        }
    }

    return false;
}
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 PCB_EDIT_FRAME::Start_DragTrackSegmentAndKeepSlope( TRACK* track, wxDC*  DC )
{
    TRACK* TrackToStartPoint = NULL;
    TRACK* TrackToEndPoint   = NULL;
    bool   error = false;

    if( !track )
        return;

    // TODO: Use clenup functions to merge collinear segments if track
    // is connected to a collinear segment.

    s_StartSegmentPresent = s_EndSegmentPresent = true;

    if( ( track->start == NULL ) || ( track->start->Type() == PCB_TRACE_T ) )
        TrackToStartPoint = track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, true, false );

    //  Test if more than one segment is connected to this point
    if( TrackToStartPoint )
    {
        TrackToStartPoint->SetState( BUSY, true );

        if( ( TrackToStartPoint->Type() == PCB_VIA_T )
           || track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_START, true, false ) )
            error = true;

        TrackToStartPoint->SetState( BUSY, false );
    }

    if( ( track->end == NULL ) || ( track->end->Type() == PCB_TRACE_T ) )
        TrackToEndPoint = track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, true, false );

    //  Test if more than one segment is connected to this point
    if( TrackToEndPoint )
    {
        TrackToEndPoint->SetState( BUSY, true );

        if( (TrackToEndPoint->Type() == PCB_VIA_T)
           || track->GetTrack( GetBoard()->m_Track, NULL, ENDPOINT_END, true, false ) )
            error = true;

        TrackToEndPoint->SetState( BUSY, false );
    }

    if( error )
    {
        DisplayError( this,
                      _( "Unable to drag this segment: too many segments connected" ) );
        return;
    }

    if( !TrackToStartPoint || ( TrackToStartPoint->Type() != PCB_TRACE_T ) )
        s_StartSegmentPresent = false;

    if( !TrackToEndPoint || ( TrackToEndPoint->Type() != PCB_TRACE_T ) )
        s_EndSegmentPresent = false;

    // Change high light net: the new one will be highlighted
    GetBoard()->PushHighLight();

    if( GetBoard()->IsHighLightNetON() )
        HighLight( DC );

    EraseDragList();

    track->SetFlags( IS_DRAGGED );

    if( TrackToStartPoint )
    {
        STATUS_FLAGS flag = STARTPOINT;

        if( track->GetStart() != TrackToStartPoint->GetStart() )
            flag = ENDPOINT;

        AddSegmentToDragList( flag, TrackToStartPoint );
        track->SetFlags( STARTPOINT );
    }

    if( TrackToEndPoint )
    {
        STATUS_FLAGS flag = STARTPOINT;

        if( track->GetEnd() != TrackToEndPoint->GetStart() )
            flag = ENDPOINT;

        AddSegmentToDragList( flag, TrackToEndPoint );
        track->SetFlags( ENDPOINT );
    }

    AddSegmentToDragList( track->GetFlags(), track );

    UndrawAndMarkSegmentsToDrag( m_canvas, DC );


    PosInit   = GetCrossHairPosition();
    s_LastPos = GetCrossHairPosition();
    m_canvas->SetMouseCapture( Show_Drag_Track_Segment_With_Cte_Slope, Abort_MoveTrack );

    GetBoard()->SetHighLightNet( track->GetNetCode() );
    GetBoard()->HighLightON();
    GetBoard()->DrawHighLight( m_canvas, DC, GetBoard()->GetHighLightNetCode() );

    // Prepare the Undo command
    ITEM_PICKER picker( NULL, UR_CHANGED );

    for( unsigned ii = 0; ii < g_DragSegmentList.size(); ii++ )
    {
        TRACK* draggedtrack = g_DragSegmentList[ii].m_Track;
        picker.SetItem( draggedtrack);
        picker.SetLink ( draggedtrack->Clone() );
        s_ItemsListPicker.PushItem( picker );
        draggedtrack = (TRACK*) picker.GetLink();
        draggedtrack->SetStatus( 0 );
        draggedtrack->ClearFlags();
    }

    if( !InitialiseDragParameters() )
    {
        DisplayError( this, _( "Unable to drag this segment: two collinear segments" ) );
        m_canvas->SetMouseCaptureCallback( NULL );
        Abort_MoveTrack( m_canvas, DC );
        return;
    }
}
void PCB_EDIT_FRAME::PrintPage( wxDC* aDC,
                                LSET  aPrintMask,
                                bool  aPrintMirrorMode,
                                void* aData)
{
    const GR_DRAWMODE drawmode = (GR_DRAWMODE) 0;
    DISPLAY_OPTIONS save_opt;
    BOARD*          Pcb   = GetBoard();
    int             defaultPenSize = Millimeter2iu( 0.2 );
    bool            onePagePerLayer = false;

    PRINT_PARAMETERS* printParameters = (PRINT_PARAMETERS*) aData; // can be null
    DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)GetDisplayOptions();

    if( printParameters && printParameters->m_OptionPrintPage == 0 )
        onePagePerLayer = true;

    PRINT_PARAMETERS::DrillShapeOptT drillShapeOpt = PRINT_PARAMETERS::FULL_DRILL_SHAPE;

    if( printParameters )
    {
        drillShapeOpt = printParameters->m_DrillShapeOpt;
        defaultPenSize = printParameters->m_PenDefaultSize;
    }

    save_opt = *displ_opts;

    LAYER_ID activeLayer = GetScreen()->m_Active_Layer;

    displ_opts->m_ContrastModeDisplay = false;
    displ_opts->m_DisplayPadFill = true;
    displ_opts->m_DisplayViaFill = true;

    if( !( aPrintMask & LSET::AllCuMask() ).any() )
    {
        if( onePagePerLayer )
        {
            // We can print mask layers (solder mask and solder paste) with the actual
            // pad sizes.  To do that, we must set ContrastModeDisplay to true and set
            // the GetScreen()->m_Active_Layer to the current printed layer
            displ_opts->m_ContrastModeDisplay = true;
            displ_opts->m_DisplayPadFill = true;

            // Calculate the active layer number to print from its mask layer:
            GetScreen()->m_Active_Layer = B_Cu;

            for( LAYER_NUM id = LAYER_ID_COUNT-1; id >= 0; --id )
            {
                if( aPrintMask[id] )
                {
                    GetScreen()->m_Active_Layer = LAYER_ID( id );
                    break;
                }
            }

            // pads on Silkscreen layer are usually plot in sketch mode:
            if( GetScreen()->m_Active_Layer == B_SilkS ||
                GetScreen()->m_Active_Layer == F_SilkS )
            {
                displ_opts->m_DisplayPadFill = false;
            }
        }
        else
        {
            displ_opts->m_DisplayPadFill = false;
        }
    }

    displ_opts->m_DisplayPadNum = false;

    bool nctmp = GetBoard()->IsElementVisible( NO_CONNECTS_VISIBLE );

    GetBoard()->SetElementVisibility( NO_CONNECTS_VISIBLE, false );

    bool anchorsTmp = GetBoard()->IsElementVisible( ANCHOR_VISIBLE );

    GetBoard()->SetElementVisibility( ANCHOR_VISIBLE, false );

    displ_opts->m_DisplayPadIsol = false;
    displ_opts->m_DisplayModEdgeFill = FILLED;
    displ_opts->m_DisplayModTextFill = FILLED;
    displ_opts->m_DisplayPcbTrackFill = true;
    displ_opts->m_ShowTrackClearanceMode = DO_NOT_SHOW_CLEARANCE;
    displ_opts->m_DisplayDrawItemsFill    = FILLED;
    displ_opts->m_DisplayZonesMode    = 0;
    displ_opts->m_DisplayNetNamesMode = 0;

    m_canvas->SetPrintMirrored( aPrintMirrorMode );

    for( BOARD_ITEM* item = Pcb->m_Drawings; item; item = item->Next() )
    {
        switch( item->Type() )
        {
        case PCB_LINE_T:
        case PCB_DIMENSION_T:
        case PCB_TEXT_T:
        case PCB_TARGET_T:
            if( aPrintMask[item->GetLayer()] )
                item->Draw( m_canvas, aDC, drawmode );
            break;

        case PCB_MARKER_T:
        default:
            break;
        }
    }

    // Print tracks
    for( TRACK* track = Pcb->m_Track; track; track = track->Next() )
    {
        if( !( aPrintMask & track->GetLayerSet() ).any() )
            continue;

        if( track->Type() == PCB_VIA_T ) // VIA encountered.
        {
            int         radius = track->GetWidth() / 2;
            const VIA*  via = static_cast<const VIA*>( track );

            EDA_COLOR_T color = g_ColorsSettings.GetItemColor( VIAS_VISIBLE + via->GetViaType() );

            GRFilledCircle( m_canvas->GetClipBox(), aDC,
                            via->GetStart().x,
                            via->GetStart().y,
                            radius,
                            0, color, color );
        }
        else
        {
            track->Draw( m_canvas, aDC, drawmode );
        }
    }

    // Outdated: only for compatibility to old boards
    for( TRACK* track = Pcb->m_Zone; track; track = track->Next() )
    {
        if( !( aPrintMask & track->GetLayerSet() ).any() )
            continue;

        track->Draw( m_canvas, aDC, drawmode );
    }

    // Draw filled areas (i.e. zones)
    for( int ii = 0; ii < Pcb->GetAreaCount(); ii++ )
    {
        ZONE_CONTAINER* zone = Pcb->GetArea( ii );

        if( aPrintMask[zone->GetLayer()] )
            zone->DrawFilledArea( m_canvas, aDC, drawmode );
    }

    // Draw footprints, this is done at last in order to print the pad holes in
    // white after the tracks and zones
    int tmp = D_PAD::m_PadSketchModePenSize;
    D_PAD::m_PadSketchModePenSize = defaultPenSize;

    for( MODULE* module = (MODULE*) Pcb->m_Modules; module;  module = module->Next() )
    {
        Print_Module( m_canvas, aDC, module, drawmode, aPrintMask, drillShapeOpt );
    }

    D_PAD::m_PadSketchModePenSize = tmp;

    /* Print via holes in bg color: Not sure it is good for buried or blind
     * vias */
    if( drillShapeOpt != PRINT_PARAMETERS::NO_DRILL_SHAPE )
    {
        TRACK*      track = Pcb->m_Track;
        EDA_COLOR_T color = WHITE;

        bool blackpenstate = GetGRForceBlackPenState();

        GRForceBlackPen( false );

        for( ; track; track = track->Next() )
        {
            if( !( aPrintMask & track->GetLayerSet() ).any() )
                continue;

            if( track->Type() == PCB_VIA_T ) // VIA encountered.
            {
                int diameter;
                const VIA *via = static_cast<const VIA*>( track );

                if( drillShapeOpt == PRINT_PARAMETERS::SMALL_DRILL_SHAPE )
                    diameter = std::min( SMALL_DRILL, via->GetDrillValue() );
                else
                    diameter = via->GetDrillValue();

                GRFilledCircle( m_canvas->GetClipBox(), aDC,
                                track->GetStart().x, track->GetStart().y,
                                diameter/2,
                                0, color, color );
            }
        }

        GRForceBlackPen( blackpenstate );
    }

    m_canvas->SetPrintMirrored( false );

    *displ_opts = save_opt;
    GetScreen()->m_Active_Layer = activeLayer;

    GetBoard()->SetElementVisibility( NO_CONNECTS_VISIBLE, nctmp );
    GetBoard()->SetElementVisibility( ANCHOR_VISIBLE, anchorsTmp );
}