/* Plot a copper layer or mask.
 * Silk screen layers are not plotted here.
 */
void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
                        LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
{

    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
    itemplotter.SetLayerMask( aLayerMask );

    EDA_DRAW_MODE_T plotMode = aPlotOpt.GetMode();

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

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

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

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

            default:
                break;
            }
        }
    }

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

            wxSize margin;
            double width_adj = 0;

            if( aLayerMask & ALL_CU_LAYERS )
                width_adj =  itemplotter.getFineWidthAdj();

            switch( aLayerMask &
                   ( SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT |
                     SOLDERPASTE_LAYER_BACK | SOLDERPASTE_LAYER_FRONT ) )
            {
            case SOLDERMASK_LAYER_FRONT:
            case SOLDERMASK_LAYER_BACK:
                margin.x = margin.y = pad->GetSolderMaskMargin();
                break;

            case SOLDERPASTE_LAYER_FRONT:
            case SOLDERPASTE_LAYER_BACK:
                margin = pad->GetSolderPasteMargin();
                break;

            default:
                break;
            }

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

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

            EDA_COLOR_T color = BLACK;

            if( (pad->GetLayerMask() & LAYER_BACK) )
               color = aBoard->GetVisibleElementColor( PAD_BK_VISIBLE );

            if((pad->GetLayerMask() & LAYER_FRONT ) )
                color = ColorFromInt( color | aBoard->GetVisibleElementColor( PAD_FR_VISIBLE ) );

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

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

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

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

        if( !Via )
            continue;

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

        if( aPlotOpt.GetPlotViaOnMaskLayer() )
        {
            if( via_mask_layer & LAYER_BACK )
                via_mask_layer |= SOLDERMASK_LAYER_BACK;

            if( via_mask_layer & LAYER_FRONT )
                via_mask_layer |= SOLDERMASK_LAYER_FRONT;
        }

        if( ( via_mask_layer & aLayerMask ) == 0 )
            continue;

        int via_margin = 0;
        double width_adj = 0;

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

        if( aLayerMask & ALL_CU_LAYERS )
            width_adj = itemplotter.getFineWidthAdj();

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

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

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

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

        if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 )
            continue;

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

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

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

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

        if( ( GetLayerMask(zone->GetLayer() )  & aLayerMask ) == 0 )
            continue;

        itemplotter.PlotFilledAreas( zone );
    }

    // Adding drill marks, if required and if the plotter is able to plot them:
    if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
        itemplotter.PlotDrillMarks();
}
/* Plot a solder mask layer.
 * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers,
 * unless the minimum thickness is 0.
 * Currently the algo is:
 * 1 - build all pad shapes as polygons with a size inflated by
 *      mask clearance + (min width solder mask /2)
 * 2 - Merge shapes
 * 3 - deflate result by (min width solder mask /2)
 * 4 - oring result by all pad shapes as polygons with a size inflated by
 *      mask clearance only (because deflate sometimes creates shape artifacts)
 * 5 - draw result as polygons
 *
 * TODO:
 * make this calculation only for shapes with clearance near than (min width solder mask)
 * (using DRC algo)
 * plot all other shapes by flashing the basing shape
 * (shapes will be better, and calculations faster)
 */
void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
                          LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
                          int aMinThickness )
{
    LAYER_NUM layer = ( aLayerMask & SOLDERMASK_LAYER_BACK ) ?
                 SOLDERMASK_N_BACK : SOLDERMASK_N_FRONT;
    int inflate = aMinThickness/2;

    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
    itemplotter.SetLayerMask( aLayerMask );

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

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

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

            default:
                break;
            }
        }
    }

    // Build polygons for each pad shape.
    // the size of the shape on solder mask should be:
    // size of pad + clearance around the pad.
    // clearance = solder mask clearance + extra margin
    // extra margin is half the min width for solder mask
    // This extra margin is used to merge too close shapes
    // (distance < aMinThickness), and will be removed when creating
    // the actual shapes
    CPOLYGONS_LIST bufferPolys;   // Contains shapes to plot
    CPOLYGONS_LIST initialPolys;  // Contains exact shapes to plot

    /* calculates the coeff to compensate radius reduction of holes clearance
     * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount )
     */
    int circleToSegmentsCount = 32;
    double correction = 1.0 / cos( M_PI / circleToSegmentsCount );

    // Plot pads
    for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
    {
        // add shapes with exact size
        module->TransformPadsShapesWithClearanceToPolygon( layer,
                        initialPolys, 0,
                        circleToSegmentsCount, correction );
        // add shapes inflated by aMinThickness/2
        module->TransformPadsShapesWithClearanceToPolygon( layer,
                        bufferPolys, inflate,
                        circleToSegmentsCount, correction );
    }

    // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
    if( aPlotOpt.GetPlotViaOnMaskLayer() )
    {
        // The current layer is a solder mask,
        // use the global mask clearance for vias
        int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
        int via_margin = via_clearance + inflate;
        for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
        {
            const VIA* via = dynamic_cast<const VIA*>( track );

            if( !via )
                continue;

            // vias are plotted only if they are on the corresponding
            // external copper layer
            LAYER_MSK via_mask_layer = via->GetLayerMask();

            if( via_mask_layer & LAYER_BACK )
                via_mask_layer |= SOLDERMASK_LAYER_BACK;

            if( via_mask_layer & LAYER_FRONT )
                via_mask_layer |= SOLDERMASK_LAYER_FRONT;

            if( ( via_mask_layer & aLayerMask ) == 0 )
                continue;

            via->TransformShapeWithClearanceToPolygon( bufferPolys, via_margin,
                    circleToSegmentsCount,
                    correction );
            via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance,
                    circleToSegmentsCount,
                    correction );
        }
    }

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

        if( zone->GetLayer() != layer )
            continue;

        zone->TransformOutlinesShapeWithClearanceToPolygon( bufferPolys,
                    inflate, true );
    }

    // Now:
    // 1 - merge areas which are intersecting, i.e. remove gaps
    //     having a thickness < aMinThickness
    // 2 - deflate resulting areas by aMinThickness/2
    KI_POLYGON_SET areasToMerge;
    bufferPolys.ExportTo( areasToMerge );
    KI_POLYGON_SET initialAreas;
    initialPolys.ExportTo( initialAreas );

    // Merge polygons: because each shape was created with an extra margin
    // = aMinThickness/2, shapes too close ( dist < aMinThickness )
    // will be merged, because they are overlapping
    KI_POLYGON_SET areas;
    areas |= areasToMerge;

    // Deflate: remove the extra margin, to create the actual shapes
    // Here I am using polygon:resize, because this function creates better shapes
    // than deflate algo.
    // Use here deflate with arc creation and 18 segments per circle to create arcs
    // In boost polygon (at least v 1.54 and previous) in very rare cases resize crashes
    // with 16 segments (perhaps related to 45 degrees pads). So using 18 segments
    // is a workaround to try to avoid these crashes
    areas = resize( areas, -inflate , true, 18 );

    // Resize slightly changes shapes. So *ensure* initial shapes are kept
    areas |= initialAreas;

    // To avoid a lot of code, use a ZONE_CONTAINER
    // to plot polygons, because they are exactly like
    // filled areas in zones
    ZONE_CONTAINER zone( aBoard );
    zone.SetArcSegmentCount( 32 );
    zone.SetMinThickness( 0 );      // trace polygons only
    zone.SetLayer ( layer );

    zone.CopyPolygonsFromKiPolygonListToFilledPolysList( areas );
    itemplotter.PlotFilledAreas( &zone );
}
Пример #3
0
/* Plot a solder mask layer.
 * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers,
 * unless the minimum thickness is 0.
 * Currently the algo is:
 * 1 - build all pad shapes as polygons with a size inflated by
 *      mask clearance + (min width solder mask /2)
 * 2 - Merge shapes
 * 3 - deflate result by (min width solder mask /2)
 * 4 - oring result by all pad shapes as polygons with a size inflated by
 *      mask clearance only (because deflate sometimes creates shape artifacts)
 * 5 - draw result as polygons
 *
 * TODO:
 * make this calculation only for shapes with clearance near than (min width solder mask)
 * (using DRC algo)
 * plot all other shapes by flashing the basing shape
 * (shapes will be better, and calculations faster)
 */
void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
                          LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
                          int aMinThickness )
{
    LAYER_ID    layer = aLayerMask[B_Mask] ? B_Mask : F_Mask;
    int         inflate = aMinThickness/2;

    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
    itemplotter.SetLayerSet( aLayerMask );

    // Plot edge layer and graphic items
    // They do not have a solder Mask margin, because they are only graphic items
    // on this layer (like logos), not actually areas around pads.
    itemplotter.PlotBoardGraphicItems();

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

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

            default:
                break;
            }
        }
    }

    // Build polygons for each pad shape.
    // the size of the shape on solder mask should be:
    // size of pad + clearance around the pad.
    // clearance = solder mask clearance + extra margin
    // extra margin is half the min width for solder mask
    // This extra margin is used to merge too close shapes
    // (distance < aMinThickness), and will be removed when creating
    // the actual shapes
    SHAPE_POLY_SET areas;           // Contains shapes to plot
    SHAPE_POLY_SET initialPolys;    // Contains exact shapes to plot

    /* calculates the coeff to compensate radius reduction of holes clearance
     * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount )
     */
    int circleToSegmentsCount = 32;
    double correction = 1.0 / cos( M_PI / circleToSegmentsCount );

    // Plot pads
    for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
    {
        // add shapes with exact size
        module->TransformPadsShapesWithClearanceToPolygon( layer,
                initialPolys, 0,
                circleToSegmentsCount, correction );
        // add shapes inflated by aMinThickness/2
        module->TransformPadsShapesWithClearanceToPolygon( layer,
                areas, inflate,
                circleToSegmentsCount, correction );
    }

    // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
    if( aPlotOpt.GetPlotViaOnMaskLayer() )
    {
        // The current layer is a solder mask,
        // use the global mask clearance for vias
        int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
        int via_margin = via_clearance + inflate;

        for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
        {
            const VIA* via = dyn_cast<const VIA*>( track );

            if( !via )
                continue;

            // vias are plotted only if they are on the corresponding
            // external copper layer
            LSET via_set = via->GetLayerSet();

            if( via_set[B_Cu] )
                via_set.set( B_Mask );

            if( via_set[F_Cu] )
                via_set.set( F_Mask );

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

            via->TransformShapeWithClearanceToPolygon( areas, via_margin,
                    circleToSegmentsCount,
                    correction );
            via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance,
                    circleToSegmentsCount,
                    correction );
        }
    }

    // Add filled zone areas.
#if 0   // Set to 1 if a solder mask margin must be applied to zones on solder mask
    int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
#else
    int zone_margin = 0;
#endif

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

        if( zone->GetLayer() != layer )
            continue;

        zone->TransformOutlinesShapeWithClearanceToPolygon( areas,
                inflate+zone_margin, false );
        zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys,
                zone_margin, false );
    }

    // To avoid a lot of code, use a ZONE_CONTAINER
    // to handle and plot polygons, because our polygons look exactly like
    // filled areas in zones
    // Note, also this code is not optimized: it creates a lot of copy/duplicate data
    // However it is not complex, and fast enough for plot purposes (copy/convert data
    // is only a very small calculation time for these calculations)
    ZONE_CONTAINER zone( aBoard );
    zone.SetArcSegmentCount( 32 );
    zone.SetMinThickness( 0 );      // trace polygons only
    zone.SetLayer ( layer );

    areas.BooleanAdd( initialPolys );
    areas.Inflate( -inflate, circleToSegmentsCount );

    // Combine the current areas to initial areas. This is mandatory because
    // inflate/deflate transform is not perfect, and we want the initial areas perfectly kept
    areas.BooleanAdd( initialPolys );
    areas.Fracture();

    zone.AddFilledPolysList( areas );

    itemplotter.PlotFilledAreas( &zone );
}