plot_board_layers.cpp 29.3 KB
Newer Older
1
/**
2 3 4 5
 * @file plot_board_layers.cpp
 * @brief Functions to plot one board layer (silkscreen layers or other layers).
 * Silkscreen layers have specific requirement for pads (not filled) and texts
 * (with option to remove them from some copper areas (pads...)
6
 */
7

8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */


Dick Hollenbeck's avatar
Dick Hollenbeck committed
32 33 34 35 36 37 38 39
#include <fctsys.h>
#include <common.h>
#include <plot_common.h>
#include <base_struct.h>
#include <drawtxt.h>
#include <trigo.h>
#include <wxBasePcbFrame.h>
#include <pcbcommon.h>
40
#include <macros.h>
Dick Hollenbeck's avatar
Dick Hollenbeck committed
41 42 43 44 45 46 47 48 49 50 51 52 53

#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_edge_mod.h>
#include <class_pcb_text.h>
#include <class_zone.h>
#include <class_drawsegment.h>
#include <class_mire.h>
#include <class_dimension.h>

#include <pcbnew.h>
#include <pcbplot.h>
54

55 56 57 58 59 60
// Local
/* 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.
 */
static void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
61
                                 LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
62 63
                                 int aMinThickness );

64
/* Creates the plot for silkscreen layers
65 66
 * Silkscreen layers have specific requirement for pads (not filled) and texts
 * (with option to remove them from some copper areas (pads...)
67
 */
68
void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, LAYER_MSK aLayerMask,
69 70 71 72 73 74 75
                     const PCB_PLOT_PARAMS& aPlotOpt )
{
    BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
    itemplotter.SetLayerMask( aLayerMask );

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

Dick Hollenbeck's avatar
Dick Hollenbeck committed
77
    // Plot footprint outlines :
78
    itemplotter.Plot_Edges_Modules();
79

Dick Hollenbeck's avatar
Dick Hollenbeck committed
80
    // Plot pads (creates pads outlines, for pads on silkscreen layers)
81
    int layersmask_plotpads = aLayerMask;
82
    // Calculate the mask layers of allowed layers for pads
83

84
    if( !aPlotOpt.GetPlotPadsOnSilkLayer() )       // Do not plot pads on silk screen layers
85
        layersmask_plotpads &= ~(SILKSCREEN_LAYER_BACK | SILKSCREEN_LAYER_FRONT );
86

87
    if( layersmask_plotpads )
88
    {
89
        for( MODULE* Module = aBoard->m_Modules; Module; Module = Module->Next() )
90
        {
91
            for( D_PAD * pad = Module->Pads(); pad != NULL; pad = pad->Next() )
92
            {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
93
                // See if the pad is on this layer
94
                LAYER_MSK masklayer = pad->GetLayerMask();
95
                if( (masklayer & layersmask_plotpads) == 0 )
96
                    continue;
97

98 99 100 101 102 103 104
                EDA_COLOR_T color = ColorFromInt(0);
                if( (layersmask_plotpads & SILKSCREEN_LAYER_BACK) )
                   color = aBoard->GetLayerColor( SILKSCREEN_N_BACK );

                if((layersmask_plotpads & SILKSCREEN_LAYER_FRONT ) )
                    color = ColorFromInt( color | aBoard->GetLayerColor( SILKSCREEN_N_FRONT ) );

105
                itemplotter.PlotPad( pad, color, LINE );
106 107
            }
        }
108
    }
109

Dick Hollenbeck's avatar
Dick Hollenbeck committed
110
    // Plot footprints fields (ref, value ...)
111
    for( MODULE* module = aBoard->m_Modules;  module;  module = module->Next() )
112
    {
113
        if( ! itemplotter.PlotAllTextsModule( module ) )
114
        {
115 116
             wxLogMessage( _( "Your BOARD has a bad layer number for module %s" ),
                           GetChars( module->GetReference() ) );
117 118
        }
    }
119

Dick Hollenbeck's avatar
Dick Hollenbeck committed
120
    // Plot filled areas
121
    for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
122
    {
123
        ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii );
124

125
        if( ( GetLayerMask( edge_zone->GetLayer() ) & aLayerMask ) == 0 )
126
            continue;
127

128
        itemplotter.PlotFilledAreas( edge_zone );
129 130
    }

131 132
    // Plot segments used to fill zone areas (outdated, but here for old boards
    // compatibility):
133
    for( SEGZONE* seg = aBoard->m_Zone; seg != NULL; seg = seg->Next() )
134
    {
135
        if( ( GetLayerMask( seg->GetLayer() ) & aLayerMask ) == 0 )
136
            continue;
137

138
        aPlotter->ThickSegment( seg->GetStart(), seg->GetEnd(), seg->GetWidth(),
139
                                itemplotter.GetMode() );
140
    }
141 142
}

143
void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, LAYER_NUM aLayer,
144
                     const PCB_PLOT_PARAMS& aPlotOpt )
145
{
146
    PCB_PLOT_PARAMS plotOpt = aPlotOpt;
147 148
    int soldermask_min_thickness = aBoard->GetDesignSettings().m_SolderMaskMinWidth;

149
    // Set a default color and the text mode for this layer
150 151
    aPlotter->SetColor( aPlotOpt.GetColor() );
    aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
152

153 154
    // Specify that the contents of the "Edges Pcb" layer are to be plotted
    // in addition to the contents of the currently specified layer.
155
    LAYER_MSK layer_mask = GetLayerMask( aLayer );
charras's avatar
charras committed
156

157
    if( !aPlotOpt.GetExcludeEdgeLayer() )
charras's avatar
charras committed
158
        layer_mask |= EDGE_LAYER;
159

160
    switch( aLayer )
charras's avatar
charras committed
161
    {
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
    case FIRST_COPPER_LAYER:
    case LAYER_N_2:
    case LAYER_N_3:
    case LAYER_N_4:
    case LAYER_N_5:
    case LAYER_N_6:
    case LAYER_N_7:
    case LAYER_N_8:
    case LAYER_N_9:
    case LAYER_N_10:
    case LAYER_N_11:
    case LAYER_N_12:
    case LAYER_N_13:
    case LAYER_N_14:
    case LAYER_N_15:
    case LAST_COPPER_LAYER:
178
        // Skip NPTH pads on copper layers ( only if hole size == pad size ):
179 180 181 182
        plotOpt.SetSkipPlotNPTH_Pads( true );
        // Drill mark will be plotted,
        // if drill mark is SMALL_DRILL_SHAPE  or FULL_DRILL_SHAPE
        PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
charras's avatar
charras committed
183
        break;
charras's avatar
charras committed
184

185 186
    case SOLDERMASK_N_BACK:
    case SOLDERMASK_N_FRONT:
187 188 189
        plotOpt.SetSkipPlotNPTH_Pads( false );
        // Disable plot pad holes
        plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
190 191 192 193 194 195 196 197

        // Plot solder mask:
        if( soldermask_min_thickness == 0 )
            PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
        else
            PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt,
                                 soldermask_min_thickness );

charras's avatar
charras committed
198
        break;
charras's avatar
charras committed
199

200 201
    case SOLDERPASTE_N_BACK:
    case SOLDERPASTE_N_FRONT:
202 203 204 205
        plotOpt.SetSkipPlotNPTH_Pads( false );
        // Disable plot pad holes
        plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
        PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
charras's avatar
charras committed
206
        break;
charras's avatar
charras committed
207

208 209
    case SILKSCREEN_N_FRONT:
    case SILKSCREEN_N_BACK:
210
        PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt );
211

212
        // Gerber: Subtract soldermask from silkscreen if enabled
213
        if( aPlotter->GetPlotterType() == PLOT_FORMAT_GERBER
214
            && plotOpt.GetSubtractMaskFromSilk() )
215
        {
216
            if( aLayer == SILKSCREEN_N_FRONT )
217
                layer_mask = GetLayerMask( SOLDERMASK_N_FRONT );
218
            else
219
                layer_mask = GetLayerMask( SOLDERMASK_N_BACK );
220

221
            // Create the mask to subtract by creating a negative layer polarity
222
            aPlotter->SetLayerPolarity( false );
223 224 225 226
            // Disable plot pad holes
            plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
            // Plot the mask
            PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
227
        }
228
        break;
229 230

    default:
231
        PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt );
232
        break;
233
    }
234 235
}

charras's avatar
charras committed
236

237 238
/* Plot a copper layer or mask.
 * Silk screen layers are not plotted here.
239
 */
240
void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
241
                        LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
242
{
243 244 245 246

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

247
    EDA_DRAW_MODE_T plotMode = aPlotOpt.GetMode();
248

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

Dick Hollenbeck's avatar
Dick Hollenbeck committed
252
    // Draw footprint shapes without pads (pads will plotted later)
253 254 255 256 257
    // 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() )
    {
258
        if( ! itemplotter.PlotAllTextsModule( module ) )
259 260 261 262 263 264
        {
            wxLogMessage( _( "Your BOARD has a bad layer number for module %s" ),
                           GetChars( module->GetReference() ) );
        }
    }

265
    for( MODULE* module = aBoard->m_Modules;  module;  module = module->Next() )
266
    {
267
        for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() )
268
        {
269 270 271
            if( ! (aLayerMask & GetLayerMask( item->GetLayer() ) ) )
                continue;

272 273
            switch( item->Type() )
            {
274
            case PCB_MODULE_EDGE_T:
275
                itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item );
276
                break;
277

278
            default:
charras's avatar
charras committed
279 280
                break;
            }
281 282
        }
    }
283

Dick Hollenbeck's avatar
Dick Hollenbeck committed
284
    // Plot footprint pads
285
    for( MODULE* module = aBoard->m_Modules;  module;  module = module->Next() )
286
    {
287
        for( D_PAD* pad = module->Pads();  pad;  pad = pad->Next() )
288
        {
Dick Hollenbeck's avatar
Dick Hollenbeck committed
289
            if( (pad->GetLayerMask() & aLayerMask) == 0 )
290
                continue;
291

292
            wxSize margin;
293
            double width_adj = 0;
294

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

298
            switch( aLayerMask &
299 300
                   ( SOLDERMASK_LAYER_BACK | SOLDERMASK_LAYER_FRONT |
                     SOLDERPASTE_LAYER_BACK | SOLDERPASTE_LAYER_FRONT ) )
301
            {
302 303
            case SOLDERMASK_LAYER_FRONT:
            case SOLDERMASK_LAYER_BACK:
304 305
                margin.x = margin.y = pad->GetSolderMaskMargin();
                break;
306

307 308
            case SOLDERPASTE_LAYER_FRONT:
            case SOLDERPASTE_LAYER_BACK:
309 310
                margin = pad->GetSolderPasteMargin();
                break;
311

312
            default:
313 314 315
                break;
            }

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

Dick Hollenbeck's avatar
Dick Hollenbeck committed
320
            // Don't draw a null size item :
321
            if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 )
322 323
                continue;

324
            EDA_COLOR_T color = BLACK;
325 326 327 328 329 330 331

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

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

332 333 334
            // Temporary set the pad size to the required plot size:
            wxSize tmppadsize = pad->GetSize();
            pad->SetSize( padPlotsSize );
Dick Hollenbeck's avatar
Dick Hollenbeck committed
335
            switch( pad->GetShape() )
charras's avatar
charras committed
336
            {
337 338
            case PAD_CIRCLE:
            case PAD_OVAL:
339
                if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
Dick Hollenbeck's avatar
Dick Hollenbeck committed
340 341
                    (pad->GetSize() == pad->GetDrillSize()) &&
                    (pad->GetAttribute() == PAD_HOLE_NOT_PLATED) )
342
                    break;
343

344
                // Fall through:
345 346 347
            case PAD_TRAPEZOID:
            case PAD_RECT:
            default:
348
                itemplotter.PlotPad( pad, color, plotMode );
charras's avatar
charras committed
349 350
                break;
            }
351 352

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

356 357 358
    // 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() )
359
    {
360 361
        if( track->Type() != PCB_VIA_T )
            continue;
362

363
        SEGVIA* Via = (SEGVIA*) track;
364

365 366 367
        // 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
368
        int via_mask_layer = Via->GetLayerMask();
369

370 371
        if( aPlotOpt.GetPlotViaOnMaskLayer() )
        {
372
            if( via_mask_layer & LAYER_BACK )
373
                via_mask_layer |= SOLDERMASK_LAYER_BACK;
374

375
            if( via_mask_layer & LAYER_FRONT )
376
                via_mask_layer |= SOLDERMASK_LAYER_FRONT;
377
        }
378

379 380
        if( ( via_mask_layer & aLayerMask ) == 0 )
            continue;
381

382 383
        int via_margin = 0;
        double width_adj = 0;
384

385 386 387 388
        // 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;
389

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

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

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

399
        EDA_COLOR_T color = aBoard->GetVisibleElementColor(VIAS_VISIBLE + Via->GetShape());
400 401 402
        // 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);
403
        aPlotter->FlashPadCircle( Via->GetStart(), diameter, plotMode );
404 405
    }

Dick Hollenbeck's avatar
Dick Hollenbeck committed
406
    // Plot tracks (not vias) :
407
    for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
408
    {
409
        if( track->Type() == PCB_VIA_T )
410
            continue;
411

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

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

Dick Hollenbeck's avatar
Dick Hollenbeck committed
420
    // Plot zones (outdated, for old boards compatibility):
421
    for( TRACK* track = aBoard->m_Zone; track; track = track->Next() )
422
    {
423
        if( (GetLayerMask( track->GetLayer() ) & aLayerMask) == 0 )
424
            continue;
425

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

Dick Hollenbeck's avatar
Dick Hollenbeck committed
431
    // Plot filled ares
432
    for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
433
    {
434
        ZONE_CONTAINER* zone = aBoard->GetArea( ii );
435

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

439
        itemplotter.PlotFilledAreas( zone );
charras's avatar
charras committed
440
    }
441 442 443 444

    // 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();
445 446
}

447 448 449
/* 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.
450 451 452 453 454 455 456
 * 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)
457
 * 5 - draw result as polygons
458 459 460 461 462 463
 *
 * 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)
464 465
 */
void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
466
                          LAYER_MSK aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
467 468
                          int aMinThickness )
{
469
    LAYER_NUM layer = ( aLayerMask & SOLDERMASK_LAYER_BACK ) ?
470 471 472 473 474 475 476 477 478 479 480
                 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() )
    {
481
        for( BOARD_ITEM* item = module->GraphicalItems(); item; item = item->Next() )
482
        {
483
            if( layer != item->GetLayer() )
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
                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
506 507
    CPOLYGONS_LIST bufferPolys;   // Contains shapes to plot
    CPOLYGONS_LIST initialPolys;  // Contains exact shapes to plot
508 509 510 511 512 513 514 515

    /* 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
516
    for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
517
    {
518 519 520 521 522 523 524 525
        // 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 );
526 527 528 529 530
    }

    // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
    if( aPlotOpt.GetPlotViaOnMaskLayer() )
    {
531 532 533 534
        // 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;
535 536 537 538 539 540 541 542 543
        for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
        {
            if( track->Type() != PCB_VIA_T )
                continue;

            SEGVIA* via = (SEGVIA*) track;

            // vias are plotted only if they are on the corresponding
            // external copper layer
544
            LAYER_MSK via_mask_layer = via->GetLayerMask();
545 546 547 548 549 550 551 552 553 554 555 556 557

            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 );
558 559 560
            via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance,
                                                       circleToSegmentsCount,
                                                       correction );
561 562 563 564 565 566 567 568 569 570 571
        }
    }

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

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

572 573
        zone->TransformOutlinesShapeWithClearanceToPolygon( bufferPolys,
                    inflate, true );
574 575 576 577 578 579 580
    }

    // 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;
581
    bufferPolys.ExportTo( areasToMerge );
582
    KI_POLYGON_SET initialAreas;
583
    initialPolys.ExportTo( initialAreas );
584 585 586 587 588 589 590 591

    // 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
592
    // Here I am using polygon:resize, because this function creates better shapes
593
    // than deflate algo.
594 595 596 597 598
    // 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 );
599

600
    // Resize slightly changes shapes. So *ensure* initial shapes are kept
601
    areas |= initialAreas;
602 603 604 605 606

    // 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 );
607
    zone.SetArcSegmentCount( 32 );
608 609 610 611 612 613 614 615
    zone.SetMinThickness( 0 );      // trace polygons only
    zone.SetLayer ( layer );

    zone.CopyPolygonsFromKiPolygonListToFilledPolysList( areas );
    itemplotter.PlotFilledAreas( &zone );
}


616 617 618 619 620 621

/** Set up most plot options for plotting a board (especially the viewport)
 * Important thing:
 *      page size is the 'drawing' page size,
 *      paper size is the physical page size
 */
622
static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
623
                               PCB_PLOT_PARAMS *aPlotOpts )
624 625
{
    PAGE_INFO pageA4( wxT( "A4" ) );
626
    const PAGE_INFO& pageInfo = aBoard->GetPageSettings();
627 628 629
    const PAGE_INFO* sheet_info;
    double paperscale; // Page-to-paper ratio
    wxSize paperSizeIU;
630
    wxSize pageSizeIU( pageInfo.GetSizeIU() );
631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
    bool autocenter = false;

    /* Special options: to fit the sheet to an A4 sheet replace
       the paper size. However there is a difference between
       the autoscale and the a4paper option:
       - Autoscale fits the board to the paper size
       - A4paper fits the original paper size to an A4 sheet
       - Both of them fit the board to an A4 sheet
     */
    if( aPlotOpts->GetA4Output() )      // Fit paper to A4
    {
        sheet_info  = &pageA4;
        paperSizeIU = pageA4.GetSizeIU();
        paperscale  = (double) paperSizeIU.x / pageSizeIU.x;
        autocenter  = true;
    }
    else
    {
649
        sheet_info  = &pageInfo;
650 651 652 653 654 655 656
        paperSizeIU = pageSizeIU;
        paperscale  = 1;

        // Need autocentering only if scale is not 1:1
        autocenter  = (aPlotOpts->GetScale() != 1.0);
    }

657 658 659
    EDA_RECT bbox = aBoard->ComputeBoundingBox();
    wxPoint boardCenter = bbox.Centre();
    wxSize boardSize = bbox.GetSize();
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688

    double compound_scale;

    /* Fit to 80% of the page if asked; it could be that the board is empty,
     * in this case regress to 1:1 scale */
    if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
    {
        double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
        double yscale = (paperSizeIU.y * 0.8) / boardSize.y;

        compound_scale = std::min( xscale, yscale ) * paperscale;
    }
    else
        compound_scale = aPlotOpts->GetScale() * paperscale;


    /* For the plot offset we have to keep in mind the auxiliary origin
       too: if autoscaling is off we check that plot option (i.e. autoscaling
       overrides auxiliary origin) */
    wxPoint offset( 0, 0);

    if( autocenter )
    {
        offset.x = KiROUND( boardCenter.x - ( paperSizeIU.x / 2.0 ) / compound_scale );
        offset.y = KiROUND( boardCenter.y - ( paperSizeIU.y / 2.0 ) / compound_scale );
    }
    else
    {
        if( aPlotOpts->GetUseAuxOrigin() )
689
            offset = aBoard->GetAuxOrigin();
690 691 692 693 694
    }

    /* Configure the plotter object with all the stuff computed and
       most of that taken from the options */
    aPlotter->SetPageSettings( *sheet_info );
695

696 697 698 699
    aPlotter->SetViewport( offset, IU_PER_DECIMILS, compound_scale,
                           aPlotOpts->GetMirror() );
    aPlotter->SetDefaultLineWidth( aPlotOpts->GetLineWidth() );
    aPlotter->SetCreator( wxT( "PCBNEW" ) );
700
    aPlotter->SetColorMode( false );        // default is plot in Black and White.
701 702 703 704 705
    aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
}

/** Prefill in black an area a little bigger than the board to prepare for the
 *  negative plot */
706
static void FillNegativeKnockout( PLOTTER *aPlotter, const EDA_RECT &aBbbox )
707
{
708
    const int margin = 5 * IU_PER_MM;   // Add a 5 mm margin around the board
709
    aPlotter->SetNegative( true );
710 711 712 713
    aPlotter->SetColor( WHITE );       // Which will be plotted as black
    EDA_RECT area = aBbbox;
    area.Inflate( margin );
    aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILLED_SHAPE );
714 715 716 717 718 719 720 721 722 723
    aPlotter->SetColor( BLACK );
}

/** Calculate the effective size of HPGL pens and set them in the
 * plotter object */
static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter,
                                   PCB_PLOT_PARAMS *aPlotOpts )
{
    /* Compute pen_dim (the value is given in mils) in pcb units,
       with plot scale (if Scale is 2, pen diameter value is always m_HPGLPenDiam
724
       so apparent pen diam is actually pen diam / Scale */
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
    int pen_diam = KiROUND( aPlotOpts->GetHPGLPenDiameter() * IU_PER_MILS /
                            aPlotOpts->GetScale() );

    // compute pen_overlay (value comes in mils) in pcb units with plot scale
    if( aPlotOpts->GetHPGLPenOverlay() < 0 )
        aPlotOpts->SetHPGLPenOverlay( 0 );

    if( aPlotOpts->GetHPGLPenOverlay() >= aPlotOpts->GetHPGLPenDiameter() )
        aPlotOpts->SetHPGLPenOverlay( aPlotOpts->GetHPGLPenDiameter() - 1 );

    int pen_overlay = KiROUND( aPlotOpts->GetHPGLPenOverlay() * IU_PER_MILS /
                               aPlotOpts->GetScale() );

    // Set HPGL-specific options and start
    aPlotter->SetPenSpeed( aPlotOpts->GetHPGLPenSpeed() );
    aPlotter->SetPenNumber( aPlotOpts->GetHPGLPenNum() );
    aPlotter->SetPenOverlap( pen_overlay );
    aPlotter->SetPenDiameter( pen_diam );
}

/** Open a new plotfile using the options (and especially the format)
 * specified in the options and prepare the page for plotting.
 * Return the plotter object if OK, NULL if the file is not created
 * (or has a problem)
 */
750
PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts,
751 752 753 754 755
                         const wxString& aFullFileName,
                         const wxString& aSheetDesc )
{
    // Create the plotter driver and set the few plotter specific
    // options
756 757
    PLOTTER*    plotter = NULL;

758 759 760
    switch( aPlotOpts->GetFormat() )
    {
    case PLOT_FORMAT_DXF:
761
        plotter = new DXF_PLOTTER();
762 763 764 765 766 767
        break;

    case PLOT_FORMAT_POST:
        PS_PLOTTER* PS_plotter;
        PS_plotter = new PS_PLOTTER();
        PS_plotter->SetScaleAdjust( aPlotOpts->GetFineScaleAdjustX(),
768
                                    aPlotOpts->GetFineScaleAdjustY() );
769
        plotter = PS_plotter;
770 771 772
        break;

    case PLOT_FORMAT_PDF:
773
        plotter = new PDF_PLOTTER();
774 775 776 777 778 779 780 781 782
        break;

    case PLOT_FORMAT_HPGL:
        HPGL_PLOTTER* HPGL_plotter;
        HPGL_plotter = new HPGL_PLOTTER();

        /* HPGL options are a little more convoluted to compute, so
           they're split in an other function */
        ConfigureHPGLPenSizes( HPGL_plotter, aPlotOpts );
783
        plotter = HPGL_plotter;
784 785 786
        break;

    case PLOT_FORMAT_GERBER:
787
        plotter = new GERBER_PLOTTER();
788 789
        break;

790
    case PLOT_FORMAT_SVG:
791
        plotter = new SVG_PLOTTER();
792 793
        break;

794 795
    default:
        wxASSERT( false );
796
        return NULL;
797 798
    }

799
    // Compute the viewport and set the other options
800 801 802 803 804 805 806 807 808

    // page layout is not mirrored, so temporary change mirror option
    // just to plot the page layout
    PCB_PLOT_PARAMS plotOpts = *aPlotOpts;

    if( plotOpts.GetPlotFrameRef() && plotOpts.GetMirror() )
        plotOpts.SetMirror( false );

    initializePlotter( plotter, aBoard, &plotOpts );
809

810
    if( plotter->OpenFile( aFullFileName ) )
811
    {
812
        plotter->StartPlot();
813

814 815
        // Plot the frame reference if requested
        if( aPlotOpts->GetPlotFrameRef() )
816
        {
817
            PlotWorkSheet( plotter, aBoard->GetTitleBlock(),
818 819 820 821
                           aBoard->GetPageSettings(),
                           1, 1, // Only one page
                           aSheetDesc, aBoard->GetFileName() );

822 823 824 825
            if( aPlotOpts->GetMirror() )
            initializePlotter( plotter, aBoard, aPlotOpts );
        }

826 827 828 829 830
        /* When plotting a negative board: draw a black rectangle
         * (background for plot board in white) and switch the current
         * color to WHITE; note the color inversion is actually done
         * in the driver (if supported) */
        if( aPlotOpts->GetNegative() )
831
        {
832
            EDA_RECT bbox = aBoard->ComputeBoundingBox();
833
            FillNegativeKnockout( plotter, bbox );
834
        }
835

836
        return plotter;
837 838
    }

839
    delete plotter;
840 841
    return NULL;
}