KiCad PCB EDA Suite
Loading...
Searching...
No Matches
plot_board_layers.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <wx/log.h>
21#include <eda_item.h>
22#include <layer_ids.h>
23#include <lset.h>
26#include <trigo.h>
27#include <pcb_base_frame.h>
28#include <math/util.h> // for KiROUND
29#include <board.h>
30#include <footprint.h>
31#include <pcb_track.h>
32#include <pad.h>
33#include <zone.h>
34#include <pcb_shape.h>
35#include <pcb_target.h>
36#include <pcb_textbox.h>
37#include <pcb_dimension.h>
38#include <pcbplot.h>
39#include <plotters/plotter.h>
45#include <pcb_painter.h>
46#include <gbr_metadata.h>
47#include <advanced_config.h>
48
49void GenerateLayerPoly( SHAPE_POLY_SET* aResult, BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
50 bool aPlotFPText, bool aPlotReferences, bool aPlotValues );
51
52
53void PlotLayer( BOARD* aBoard, PLOTTER* aPlotter, const LSET& layerMask,
54 const PCB_PLOT_PARAMS& plotOpts )
55{
56 // PlotLayerOutlines() is designed only for DXF plotters.
57 if( plotOpts.GetFormat() == PLOT_FORMAT::DXF && plotOpts.GetDXFPlotPolygonMode() )
58 PlotLayerOutlines( aBoard, aPlotter, layerMask, plotOpts );
59 else
60 PlotStandardLayer( aBoard, aPlotter, layerMask, plotOpts );
61};
62
63
64void PlotPolySet( BOARD* aBoard, PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpt,
65 SHAPE_POLY_SET* aPolySet, PCB_LAYER_ID aLayer )
66{
67 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
68 LSET layers = { aLayer };
69
70 itemplotter.SetLayerSet( layers );
71
72 // To avoid a lot of code, use a ZONE to handle and plot polygons, because our polygons look
73 // exactly like filled areas in zones.
74 // Note, also this code is not optimized: it creates a lot of copy/duplicate data.
75 // However it is not complex, and fast enough for plot purposes (copy/convert data is only a
76 // very small calculation time for these calculations).
77 ZONE zone( aBoard );
78 zone.SetMinThickness( 0 );
79 zone.SetLayer( aLayer );
80
81 aPolySet->Fracture();
82 itemplotter.PlotZone( &zone, aLayer, *aPolySet );
83}
84
85
92void PlotSolderMaskLayer( BOARD* aBoard, PLOTTER* aPlotter, const LSET& aLayerMask,
93 const PCB_PLOT_PARAMS& aPlotOpt )
94{
95 if( aBoard->GetDesignSettings().m_SolderMaskMinWidth == 0 )
96 {
97 PlotLayer( aBoard, aPlotter, aLayerMask, aPlotOpt );
98 return;
99 }
100
101 SHAPE_POLY_SET solderMask;
102 PCB_LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask;
103
104 GenerateLayerPoly( &solderMask, aBoard, aPlotter, layer, aPlotOpt.GetPlotFPText(),
105 aPlotOpt.GetPlotReference(), aPlotOpt.GetPlotValue() );
106
107 PlotPolySet( aBoard, aPlotter, aPlotOpt, &solderMask, layer );
108}
109
110
111void PlotClippedSilkLayer( BOARD* aBoard, PLOTTER* aPlotter, const LSET& aLayerMask,
112 const PCB_PLOT_PARAMS& aPlotOpt )
113{
114 SHAPE_POLY_SET silkscreen, solderMask;
115 PCB_LAYER_ID silkLayer = aLayerMask[F_SilkS] ? F_SilkS : B_SilkS;
116 PCB_LAYER_ID maskLayer = aLayerMask[F_SilkS] ? F_Mask : B_Mask;
117
118 GenerateLayerPoly( &silkscreen, aBoard, aPlotter, silkLayer, aPlotOpt.GetPlotFPText(),
119 aPlotOpt.GetPlotReference(), aPlotOpt.GetPlotValue() );
120 GenerateLayerPoly( &solderMask, aBoard, aPlotter, maskLayer, aPlotOpt.GetPlotFPText(),
121 aPlotOpt.GetPlotReference(), aPlotOpt.GetPlotValue() );
122
123 silkscreen.BooleanSubtract( solderMask );
124 PlotPolySet( aBoard, aPlotter, aPlotOpt, &silkscreen, silkLayer );
125}
126
127
128void PlotBoardLayers( BOARD* aBoard, PLOTTER* aPlotter, const LSEQ& aLayers,
129 const PCB_PLOT_PARAMS& aPlotOptions )
130{
131 if( !aBoard || !aPlotter || aLayers.empty() )
132 return;
133
134 PCB_PLOT_PARAMS plotOptions = aPlotOptions;
135
136 // Gerber carries no colour, so a drill mark lands as ink instead of knocking out its pad
137 // The plot dialog forces them off for gerber but API and CLI callers arrive here directly
138 if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER )
140
141 for( PCB_LAYER_ID layer : aLayers )
142 PlotOneBoardLayer( aBoard, aPlotter, layer, plotOptions, layer == aLayers[0] );
143
144 // Drill symbols go after the normal layers but before the physical marks, so a symbol is
145 // never sitting under a knockout
146 LSET mapLayers = aBoard->DrillSymbolLayers() & LSET( aLayers );
147
148 if( mapLayers.any() )
149 {
150 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, plotOptions );
151 itemplotter.SetLayerSet( aLayers );
152
153 for( PCB_LAYER_ID layer : mapLayers.Seq() )
154 itemplotter.PlotDrillSymbols( layer );
155 }
156
157 // Drill marks are plotted in white to knockout the pad if any layers of the pad are
158 // being plotted, and in black if the pad is not being plotted. For the former, this
159 // must happen after all other layers are plotted.
160
161 // One global knockout pass, so a plotted layer carrying a map skips them rather than
162 // punching through the symbols they would annotate
163 if( plotOptions.GetDrillMarksType() != DRILL_MARKS::NO_DRILL_SHAPE && !mapLayers.any() )
164 {
165 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, plotOptions );
166 itemplotter.SetLayerSet( aLayers );
167 itemplotter.PlotDrillMarks();
168 }
169}
170
171
172void PlotInteractiveLayer( BOARD* aBoard, PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpt )
173{
174 for( const FOOTPRINT* fp : aBoard->Footprints() )
175 {
176 if( fp->GetLayer() == F_Cu && !aPlotOpt.m_PDFFrontFPPropertyPopups )
177 continue;
178
179 if( fp->GetLayer() == B_Cu && !aPlotOpt.m_PDFBackFPPropertyPopups )
180 continue;
181
182 std::vector<wxString> properties;
183
184 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
185 _( "Reference designator" ),
186 fp->Reference().GetShownText( FOR_GUI ) ) );
187
188 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
189 _( "Value" ),
190 fp->Value().GetShownText( FOR_GUI ) ) );
191
192 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
193 _( "Footprint" ),
194 fp->GetFPID().GetUniStringLibItemName() ) );
195
196 for( const PCB_FIELD* field : fp->GetFields() )
197 {
198 wxCHECK2( field, continue );
199
200 if( field->IsReference() || field->IsValue() )
201 continue;
202
203 if( field->GetText().IsEmpty() )
204 continue;
205
206 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
207 field->GetName(),
208 field->GetText() ) );
209 }
210
211 // These 2 properties are not very useful in a plot file (like a PDF)
212#if 0
213 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Library Description" ),
214 fp->GetLibDescription() ) );
215
216 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Keywords" ),
217 fp->GetKeywords() ) );
218#endif
219 // Draw items are plotted with a position offset. So we need to move
220 // our boxes (which are not plotted) by the same offset.
221 VECTOR2I offset = -aPlotter->GetPlotOffsetUserUnits();
222
223 // Use a footprint bbox without texts to create the hyperlink area
224 BOX2I bbox = fp->GetBoundingBox( false );
225 bbox.Move( offset );
226 aPlotter->HyperlinkMenu( bbox, properties );
227
228 // Use a footprint bbox with visible texts only to create the bookmark area
229 // which is the area to zoom on ft selection
230 // However the bbox need to be inflated for a better look.
231 bbox = fp->GetBoundingBox( true );
232 bbox.Move( offset );
233 bbox.Inflate( bbox.GetWidth() /2, bbox.GetHeight() /2 );
234 aPlotter->Bookmark( bbox, fp->GetReference(), _( "Footprints" ) );
235 }
236}
237
238
239void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
240 const PCB_PLOT_PARAMS& aPlotOpt, bool isPrimaryLayer )
241{
242 PCB_PLOT_PARAMS plotOpt = aPlotOpt;
243
244 // Set a default color and the text mode for this layer
245 aPlotter->SetColor( BLACK );
246 aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
247
248 // Specify that the contents of the "Edges Pcb" layer are to be plotted in addition to the
249 // contents of the currently specified layer.
250 LSET layer_mask( { aLayer } );
251
252 if( IsCopperLayer( aLayer ) )
253 {
254 // Skip NPTH pads on copper layers ( only if hole size == pad size ):
255 // Drill mark will be plotted if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
256 if( plotOpt.GetFormat() == PLOT_FORMAT::DXF )
257 plotOpt.SetDXFPlotPolygonMode( true );
258 else
259 plotOpt.SetSkipPlotNPTH_Pads( true );
260
261 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
262 }
263 else
264 {
265 switch( aLayer )
266 {
267 case B_Mask:
268 case F_Mask:
269 // Use outline mode for DXF
270 plotOpt.SetDXFPlotPolygonMode( true );
271
272 // Plot solder mask:
273 PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt );
274
275 break;
276
277 case B_Adhes:
278 case F_Adhes:
279 case B_Paste:
280 case F_Paste:
281 // Disable plot pad holes
283
284 // Use outline mode for DXF
285 plotOpt.SetDXFPlotPolygonMode( true );
286
287 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
288
289 break;
290
291 case F_SilkS:
292 case B_SilkS:
293 if( plotOpt.GetSubtractMaskFromSilk() )
294 {
295 if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER && isPrimaryLayer )
296 {
297 // Use old-school, positive/negative mask plotting which preserves utilization
298 // of Gerber aperture masks. This method can only be used when the given silk
299 // layer is the primary layer as the negative mask will also knockout any other
300 // (non-silk) layers that were plotted before the silk layer.
301
302 PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
303
304 // Create the mask to subtract by creating a negative layer polarity
305 aPlotter->SetLayerPolarity( false );
306
307 // Disable plot pad holes
309
310 // Plot the mask
311 layer_mask = ( aLayer == F_SilkS ) ? LSET( { F_Mask } ) : LSET( { B_Mask } );
312 PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt );
313
314 // Disable the negative polarity
315 aPlotter->SetLayerPolarity( true );
316 }
317 else
318 {
319 PlotClippedSilkLayer( aBoard, aPlotter, layer_mask, plotOpt );
320 }
321
322 break;
323 }
324
325 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
326 break;
327
328 case Dwgs_User:
329 case Cmts_User:
330 case Eco1_User:
331 case Eco2_User:
332 case Edge_Cuts:
333 case Margin:
334 case F_CrtYd:
335 case B_CrtYd:
336 case F_Fab:
337 case B_Fab:
338 default:
339 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
340 break;
341 }
342 }
343}
344
345
349void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, const LSET& aLayerMask,
350 const PCB_PLOT_PARAMS& aPlotOpt )
351{
352 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
353 int maxError = aBoard->GetDesignSettings().m_MaxError;
354
355 itemplotter.SetLayerSet( aLayerMask );
356
357 bool onCopperLayer = ( LSET::AllCuMask() & aLayerMask ).any();
358 bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & aLayerMask ).any();
359 bool onSolderPasteLayer = ( LSET( { F_Paste, B_Paste } ) & aLayerMask ).any();
360 bool onFrontFab = ( LSET( { F_Fab } ) & aLayerMask ).any();
361 bool onBackFab = ( LSET( { B_Fab } ) & aLayerMask ).any();
362 bool sketchPads = ( onFrontFab || onBackFab ) && aPlotOpt.GetSketchPadsOnFabLayers();
363 const wxString variantName = aBoard->GetCurrentVariant();
364
365 // Plot edge layer and graphic items
366 for( const BOARD_ITEM* item : aBoard->Drawings() )
367 itemplotter.PlotBoardGraphicItem( item );
368
369 // Draw footprint texts:
370 for( const FOOTPRINT* footprint : aBoard->Footprints() )
371 itemplotter.PlotFootprintTextItems( footprint );
372
373 // Draw footprint other graphic items:
374 for( const FOOTPRINT* footprint : aBoard->Footprints() )
375 itemplotter.PlotFootprintGraphicItems( footprint );
376
377 // Plot footprint pads
378 for( FOOTPRINT* footprint : aBoard->Footprints() )
379 {
380 const bool dnp = footprint->GetDNPForVariant( variantName );
381
382 aPlotter->StartBlock( nullptr );
383
384 for( PAD* pad : footprint->Pads() )
385 {
386 bool doSketchPads = false;
387
388 if( !( pad->GetLayerSet() & aLayerMask ).any() )
389 {
390 if( sketchPads && ( ( onFrontFab && pad->GetLayerSet().Contains( F_Cu ) )
391 || ( onBackFab && pad->GetLayerSet().Contains( B_Cu ) ) ) )
392 {
393 doSketchPads = true;
394 }
395 else
396 {
397 continue;
398 }
399 }
400
401 if( onCopperLayer && !pad->IsOnCopperLayer() )
402 continue;
403
405 if( onCopperLayer && !pad->FlashLayer( aLayerMask ) )
406 continue;
407
408 // TODO(JE) padstacks - different behavior for single layer or multilayer
409
410 COLOR4D color = COLOR4D::BLACK;
411
412 // If we're plotting a single layer, the color for that layer can be used directly.
413 if( aLayerMask.count() == 1 )
414 {
415 color = aPlotOpt.ColorSettings()->GetColor( aLayerMask.Seq()[0] );
416 }
417 else
418 {
419 if( ( pad->GetLayerSet() & aLayerMask )[B_Cu] )
420 color = aPlotOpt.ColorSettings()->GetColor( B_Cu );
421
422 if( ( pad->GetLayerSet() & aLayerMask )[F_Cu] )
423 color = color.LegacyMix( aPlotOpt.ColorSettings()->GetColor( F_Cu ) );
424
425 if( sketchPads && aLayerMask[F_Fab] )
426 color = aPlotOpt.ColorSettings()->GetColor( F_Fab );
427 else if( sketchPads && aLayerMask[B_Fab] )
428 color = aPlotOpt.ColorSettings()->GetColor( B_Fab );
429 }
430
431 if( sketchPads && ( ( onFrontFab && pad->GetLayerSet().Contains( F_Cu ) )
432 || ( onBackFab && pad->GetLayerSet().Contains( B_Cu ) ) ) )
433 {
434 if( aPlotOpt.GetPlotPadNumbers() )
435 itemplotter.PlotPadNumber( pad, color );
436 }
437
438 auto plotPadLayer =
439 [&]( PCB_LAYER_ID aLayer )
440 {
441 VECTOR2I margin;
442 int width_adj = 0;
443
444 if( onCopperLayer )
445 width_adj = itemplotter.getFineWidthAdj();
446
447 if( onSolderMaskLayer )
448 margin.x = margin.y = pad->GetSolderMaskExpansion( aLayer );
449
450 if( onSolderPasteLayer )
451 margin = pad->GetSolderPasteMargin( aLayer );
452
453 // not all shapes can have a different margin for x and y axis
454 // in fact only oval and rect shapes can have different values.
455 // Round shape have always the same x,y margin
456 // so define a unique value for other shapes that do not support different values
457 int mask_clearance = margin.x;
458 // When clearance is same for x and y pad axis, calculations are more easy
459 bool sameXYClearance = margin.x == margin.y;
460
461 // Now offset the pad size by margin + width_adj
462 VECTOR2I padPlotsSize = pad->GetSize( aLayer ) + margin * 2 + VECTOR2I( width_adj, width_adj );
463
464 // Store these parameters that can be modified to plot inflated/deflated pads shape
465 PAD_SHAPE padShape = pad->GetShape( aLayer );
466 VECTOR2I padSize = pad->GetSize( aLayer );
467 VECTOR2I padDelta = pad->GetDelta( aLayer ); // has meaning only for trapezoidal pads
468 // CornerRadius and CornerRadiusRatio can be modified
469 // the radius is built from the ratio, so saving/restoring the ratio is enough
470 double padCornerRadiusRatio = pad->GetRoundRectRadiusRatio( aLayer );
471
472 // Don't draw a 0 sized pad.
473 // Note: a custom pad can have its pad anchor with size = 0
474 if( padShape != PAD_SHAPE::CUSTOM
475 && ( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) )
476 {
477 return;
478 }
479
480 switch( padShape )
481 {
483 case PAD_SHAPE::OVAL:
484 pad->SetSize( aLayer, padPlotsSize );
485
486 if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
488 ( pad->GetSize(aLayer ) == pad->GetDrillSize() ) &&
489 ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
490 {
491 break;
492 }
493
494 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
495 break;
496
498 pad->SetSize( aLayer, padPlotsSize );
499
500 if( mask_clearance > 0 )
501 {
502 pad->SetShape( aLayer, PAD_SHAPE::ROUNDRECT );
503 pad->SetRoundRectCornerRadius( aLayer, mask_clearance );
504 }
505
506 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
507 break;
508
510 // inflate/deflate a trapezoid is a bit complex.
511 // so if the margin is not null, build a similar polygonal pad shape,
512 // and inflate/deflate the polygonal shape
513 // because inflating/deflating using different values for y and y
514 // we are using only margin.x as inflate/deflate value
515 if( mask_clearance == 0 )
516 {
517 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
518 }
519 else
520 {
521 PAD dummy( *pad );
522 dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
523 dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
524 SHAPE_POLY_SET outline;
525 outline.NewOutline();
526 int dx = padSize.x / 2;
527 int dy = padSize.y / 2;
528 int ddx = padDelta.x / 2;
529 int ddy = padDelta.y / 2;
530
531 outline.Append( -dx - ddy, dy + ddx );
532 outline.Append( dx + ddy, dy - ddx );
533 outline.Append( dx - ddy, -dy + ddx );
534 outline.Append( -dx + ddy, -dy - ddx );
535
536 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
537 // which can create bad shapes if margin.x is < 0
539 maxError );
540 dummy.DeletePrimitivesList();
541 dummy.AddPrimitivePoly( aLayer, outline, 0, true );
542
543 // Be sure the anchor pad is not bigger than the deflated shape because this
544 // anchor will be added to the pad shape when plotting the pad. So now the
545 // polygonal shape is built, we can clamp the anchor size
546 dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
547
548 itemplotter.PlotPad( &dummy, aLayer, color, doSketchPads );
549 }
550
551 break;
552
554 {
555 // The Minkowski sum of a rounded rectangle with a disk of radius R is
556 // another rounded rectangle whose sides grow by 2R and whose corner
557 // radius grows by R. Preserving the original radius_ratio instead
558 // produces visibly inconsistent expansion at the corners (issue 24327).
559 if( sameXYClearance )
560 {
561 int originalRadius = pad->GetRoundRectCornerRadius( aLayer );
562 int newRadius = std::max( 0, originalRadius + mask_clearance );
563 pad->SetSize( aLayer, padPlotsSize );
564 pad->SetRoundRectCornerRadius( aLayer, newRadius );
565 }
566 else
567 {
568 // Asymmetric X/Y clearance (e.g. solder paste ratio on a
569 // non-square pad) is not a Minkowski sum with a disk. Fall back
570 // to the historical behavior of scaling both axes by the per-axis
571 // margin while keeping the radius_ratio. This is approximate at
572 // the corners but preserves the bounding box, which is the
573 // dimension users rely on for paste apertures.
574 double radiusRatio = pad->GetRoundRectRadiusRatio( aLayer );
575 pad->SetSize( aLayer, padPlotsSize );
576 pad->SetRoundRectRadiusRatio( aLayer, radiusRatio );
577 }
578
579 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
580 break;
581 }
582
584 // for smaller/same rect size than initial shape (i.e. mask_clearance <= 0)
585 // use the rect with size set to padPlotsSize. It gives a good shape
586 if( mask_clearance <= 0 )
587 {
588 // the size can be slightly inflated by width_adj (PS/PDF only)
589 pad->SetSize( aLayer, padPlotsSize );
590 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
591 }
592 else
593 {
594 // Due to the polygonal shape of a CHAMFERED_RECT pad, the best way is to
595 // convert the pad shape to a full polygon and inflate it
596 // and use a dummy CUSTOM pad to plot the final shape.
597 // However one can inflate polygon only if X,Y has same inflate value
598 // if not the case, just use a rectangle having the padPlotsSize new size
599 PAD dummy( *pad );
600 // Build the dummy pad outline with coordinates relative to the pad position
601 // pad offset and orientation 0. The actual pos, offset and rotation will be
602 // taken in account later by the plot function
603 dummy.SetPosition( VECTOR2I( 0, 0 ) );
604 dummy.SetOffset( aLayer, VECTOR2I( 0, 0 ) );
605
606 if( !sameXYClearance )
607 dummy.SetSize( aLayer, padPlotsSize );
608
609 dummy.SetOrientation( ANGLE_0 );
610 SHAPE_POLY_SET outline;
611 dummy.TransformShapeToPolygon( outline, aLayer, 0, maxError, ERROR_INSIDE );
612
613 if( sameXYClearance )
615 maxError );
616
617 // Initialize the dummy pad shape:
618 dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
619 dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
620 dummy.DeletePrimitivesList();
621 dummy.AddPrimitivePoly( aLayer, outline, 0, true );
622
623 // Be sure the anchor pad is not bigger than the deflated shape because this
624 // anchor will be added to the pad shape when plotting the pad.
625 // So we set the anchor size to 0
626 dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
627 // Restore pad position and offset
628 dummy.SetPosition( pad->GetPosition() );
629 dummy.SetOffset( aLayer, pad->GetOffset( aLayer ) );
630 dummy.SetOrientation( pad->GetOrientation() );
631
632 itemplotter.PlotPad( &dummy, aLayer, color, doSketchPads );
633 }
634
635 break;
636
638 {
639 // inflate/deflate a custom shape is a bit complex.
640 // so build a similar pad shape, and inflate/deflate the polygonal shape
641 PAD dummy( *pad );
642 dummy.SetParentGroup( nullptr );
643
644 SHAPE_POLY_SET shape;
645 pad->MergePrimitivesAsPolygon( aLayer, &shape );
646
647 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
648 // which can create bad shapes if margin.x is < 0
649 shape.InflateWithLinkedHoles( mask_clearance,
651 dummy.DeletePrimitivesList();
652 dummy.AddPrimitivePoly( aLayer, shape, 0, true );
653
654 // Be sure the anchor pad is not bigger than the deflated shape because this
655 // anchor will be added to the pad shape when plotting the pad. So now the
656 // polygonal shape is built, we can clamp the anchor size
657 if( mask_clearance < 0 ) // we expect margin.x = margin.y for custom pads
658 {
659 dummy.SetSize( aLayer, VECTOR2I( std::max( 0, padPlotsSize.x ),
660 std::max( 0, padPlotsSize.y ) ) );
661 }
662
663 itemplotter.PlotPad( &dummy, aLayer, color, doSketchPads );
664 break;
665 }
666 }
667
668 // Restore the pad parameters modified by the plot code
669 pad->SetSize( aLayer, padSize );
670 pad->SetDelta( aLayer, padDelta );
671 pad->SetShape( aLayer, padShape );
672 pad->SetRoundRectRadiusRatio( aLayer, padCornerRadiusRatio );
673 };
674
675 for( PCB_LAYER_ID layer : aLayerMask.SeqStackupForPlotting() )
676 plotPadLayer( layer );
677 }
678
679 if( dnp
680 && !itemplotter.GetHideDNPFPsOnFabLayers()
681 && itemplotter.GetCrossoutDNPFPsOnFabLayers()
682 && ( ( onFrontFab && footprint->GetLayer() == F_Cu )
683 || ( onBackFab && footprint->GetLayer() == B_Cu ) ) )
684 {
685 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( footprint->GetLayer() );
686 VECTOR2I center = footprint->GetPosition();
687 EDA_ANGLE orient = footprint->GetOrientation();
688
689 // Compute a tight oriented bounding box by un-rotating the shape into the
690 // footprint's local frame, taking the axis-aligned BBox there, then rotating
691 // the four corners back into world coordinates.
692 BOX2I localRect;
693
694 if( courtyard.IsEmpty() )
695 {
696 std::shared_ptr<SHAPE> shape = footprint->GetEffectiveShape();
697 shape->Rotate( -orient, center );
698 localRect = shape->BBox();
699 }
700 else
701 {
702 SHAPE_POLY_SET temp( courtyard );
703 temp.Rotate( -orient, center );
704 localRect = temp.BBox();
705 }
706
707 VECTOR2I corner1( localRect.GetLeft(), localRect.GetTop() );
708 VECTOR2I corner2( localRect.GetRight(), localRect.GetTop() );
709 VECTOR2I corner3( localRect.GetRight(), localRect.GetBottom() );
710 VECTOR2I corner4( localRect.GetLeft(), localRect.GetBottom() );
711
712 RotatePoint( corner1, center, orient );
713 RotatePoint( corner2, center, orient );
714 RotatePoint( corner3, center, orient );
715 RotatePoint( corner4, center, orient );
716
717 int width = aBoard->GetDesignSettings().m_LineThickness[ LAYER_CLASS_FAB ];
718
719 // Use DNP cross color from color scheme
720 COLOR4D dnpMarkerColor = aPlotOpt.ColorSettings()->GetColor( LAYER_DNP_MARKER );
721
722 if( dnpMarkerColor != COLOR4D::UNSPECIFIED )
723 aPlotter->SetColor( dnpMarkerColor );
724 else
725 aPlotter->SetColor( aPlotOpt.ColorSettings()->GetColor( onFrontFab ? F_Fab : B_Fab ) );
726
727 aPlotter->ThickSegment( corner1, corner3, width, nullptr );
728 aPlotter->ThickSegment( corner2, corner4, width, nullptr );
729 }
730
731 aPlotter->EndBlock( nullptr );
732 }
733
734 // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true,
735
736 GBR_METADATA gbr_metadata;
737
738 if( onCopperLayer )
739 {
742 }
743
744 auto getMetadata =
745 [&]()
746 {
747 if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER )
748 return (void*) &gbr_metadata;
749 else if( aPlotter->GetPlotterType() == PLOT_FORMAT::DXF )
750 return (void*) &aPlotOpt;
751 else
752 return (void*) nullptr;
753 };
754
755 aPlotter->StartBlock( nullptr );
756
757 for( const PCB_TRACK* track : aBoard->Tracks() )
758 {
759 if( track->Type() != PCB_VIA_T )
760 continue;
761
762 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
763
764 // vias are not plotted if not on selected layer
765 LSET via_mask_layer = via->GetLayerSet();
766
767 if( !( via_mask_layer & aLayerMask ).any() )
768 continue;
769
770 int via_margin = 0;
771 double width_adj = 0;
772
773 // TODO(JE) padstacks - separate top/bottom margin
774 if( onSolderMaskLayer )
775 via_margin = via->GetSolderMaskExpansion();
776
777 if( ( aLayerMask & LSET::AllCuMask() ).any() )
778 width_adj = itemplotter.getFineWidthAdj();
779
781 if( onCopperLayer && !via->FlashLayer( aLayerMask ) )
782 continue;
783
784 int diameter = 0;
785
786 for( PCB_LAYER_ID layer : aLayerMask )
787 diameter = std::max( diameter, via->GetWidth( layer ) );
788
789 diameter += 2 * via_margin + width_adj;
790
791 // Don't draw a null size item :
792 if( diameter <= 0 )
793 continue;
794
795 // Some vias can be not connected (no net).
796 // Set the m_NotInNet for these vias to force a empty net name in gerber file
797 gbr_metadata.m_NetlistMetadata.m_NotInNet = via->GetNetname().IsEmpty();
798
799 gbr_metadata.SetNetName( via->GetNetname() );
800
801 COLOR4D color;
802
803 // If we're plotting a single layer, the color for that layer can be used directly.
804 if( aLayerMask.count() == 1 )
805 color = aPlotOpt.ColorSettings()->GetColor( aLayerMask.Seq()[0] );
806 else
807 color = aPlotOpt.ColorSettings()->GetColor( LAYER_VIAS + static_cast<int>( via->GetViaType() ) );
808
809 // Change UNSPECIFIED or WHITE to LIGHTGRAY because the white items are not seen on a
810 // white paper or screen
811 if( color == COLOR4D::UNSPECIFIED || color == WHITE )
812 color = LIGHTGRAY;
813
814 aPlotter->SetColor( color );
815 aPlotter->FlashPadCircle( via->GetStart(), diameter, getMetadata() );
816 }
817
818 aPlotter->EndBlock( nullptr );
819 aPlotter->StartBlock( nullptr );
820
821 if( onCopperLayer )
822 {
825 }
826 else
827 {
828 // Reset attributes if non-copper (soldermask) layer
831 }
832
833 // Plot tracks (not vias) :
834 for( const PCB_TRACK* track : aBoard->Tracks() )
835 {
836 if( track->Type() == PCB_VIA_T )
837 continue;
838
839 if( !( aLayerMask & track->GetLayerSet() ).any() )
840 continue;
841
842 // Some track segments can be not connected (no net).
843 // Set the m_NotInNet for these segments to force a empty net name in gerber file
844 gbr_metadata.m_NetlistMetadata.m_NotInNet = track->GetNetname().IsEmpty();
845
846 gbr_metadata.SetNetName( track->GetNetname() );
847
848 int margin = 0;
849
850 if( onSolderMaskLayer )
851 margin = track->GetSolderMaskExpansion();
852
853 int width = track->GetWidth() + 2 * margin + itemplotter.getFineWidthAdj();
854
855 aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
856
857 if( track->Type() == PCB_ARC_T )
858 {
859 const PCB_ARC* arc = static_cast<const PCB_ARC*>( track );
860
861 // Too small arcs cannot be really handled: arc center (and arc radius)
862 // cannot be safely computed
863 if( !arc->IsDegenerated( 10 /* in IU */ ) )
864 {
865 aPlotter->ThickArc( arc->GetCenter(), arc->GetArcAngleStart(), arc->GetAngle(),
866 arc->GetRadius(), width, getMetadata() );
867 }
868 else
869 {
870 // Approximate this very small arc by a segment.
871 aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, getMetadata() );
872 }
873 }
874 else
875 {
876 aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, getMetadata() );
877 }
878 }
879
880 aPlotter->EndBlock( nullptr );
881
882 // Plot filled ares
883 aPlotter->StartBlock( nullptr );
884
885 NETINFO_ITEM nonet( aBoard );
886
887 for( const ZONE* zone : aBoard->Zones() )
888 {
889 if( zone->GetIsRuleArea() )
890 continue;
891
892 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
893 {
894 if( !aLayerMask[layer] )
895 continue;
896
897 SHAPE_POLY_SET mainArea = zone->GetFilledPolysList( layer )->CloneDropTriangulation();
898 SHAPE_POLY_SET islands;
899
900 for( int i = mainArea.OutlineCount() - 1; i >= 0; i-- )
901 {
902 if( zone->IsIsland( layer, i ) )
903 {
904 islands.AddOutline( mainArea.CPolygon( i )[0] );
905 mainArea.DeletePolygon( i );
906 }
907 }
908
909 itemplotter.PlotZone( zone, layer, mainArea );
910
911 if( !islands.IsEmpty() )
912 {
913 ZONE dummy( *zone );
914 dummy.SetNet( &nonet );
915 itemplotter.PlotZone( &dummy, layer, islands );
916 }
917 }
918 }
919
920 aPlotter->EndBlock( nullptr );
921}
922
923
927void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, const LSET& aLayerMask,
928 const PCB_PLOT_PARAMS& aPlotOpt )
929{
930 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
931 itemplotter.SetLayerSet( aLayerMask );
932
933 int smallDrill = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_SmallDrillMarkSize );
934
935 SHAPE_POLY_SET outlines;
936
937 for( PCB_LAYER_ID layer : aLayerMask.Seq( aLayerMask.SeqStackupForPlotting() ) )
938 {
939 outlines.RemoveAllContours();
940 aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines, aPlotter->RenderSettings() );
941
942 outlines.Simplify();
943
944 // Plot outlines
945 std::vector<VECTOR2I> cornerList;
946
947 // Now we have one or more basic polygons: plot each polygon
948 for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
949 {
950 for( int kk = 0; kk <= outlines.HoleCount(ii); kk++ )
951 {
952 cornerList.clear();
953 const SHAPE_LINE_CHAIN& path = ( kk == 0 ) ? outlines.COutline( ii )
954 : outlines.CHole( ii, kk - 1 );
955
957 }
958 }
959
960 // Plot pad holes
962 {
963 for( FOOTPRINT* footprint : aBoard->Footprints() )
964 {
965 for( PAD* pad : footprint->Pads() )
966 {
967 if( pad->HasHole() )
968 {
969 if( pad->GetDrillSizeX() == pad->GetDrillSizeY() )
970 {
971 int drill = pad->GetDrillSizeX();
972
974 drill = std::min( smallDrill, drill );
975
976 aPlotter->ThickCircle( pad->ShapePos( layer ), drill,
978 }
979 else
980 {
981 // Note: small drill marks have no significance when applied to slots
982
983 aPlotter->ThickOval( pad->ShapePos( layer ), pad->GetSize( layer ),
984 pad->GetOrientation(), PLOTTER::USE_DEFAULT_LINE_WIDTH,
985 nullptr );
986 }
987 }
988 }
989 }
990 }
991
992 // Plot vias holes
993 for( PCB_TRACK* track : aBoard->Tracks() )
994 {
995 if( track->Type() != PCB_VIA_T )
996 continue;
997
998 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
999
1000 if( via->GetLayerSet().Contains( layer ) ) // via holes can be not through holes
1001 {
1002 aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), FILL_T::NO_FILL,
1004 }
1005 }
1006 }
1007}
1008
1009
1013void GenerateLayerPoly( SHAPE_POLY_SET* aResult, BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
1014 bool aPlotFPText, bool aPlotReferences, bool aPlotValues )
1015{
1016 int maxError = aBoard->GetDesignSettings().m_MaxError;
1017 SHAPE_POLY_SET buffer;
1018 int inflate = 0;
1019
1020 if( aLayer == F_Mask || aLayer == B_Mask )
1021 {
1022 // We remove 1nm as we expand both sides of the shapes, so allowing for a strictly greater
1023 // than or equal comparison in the shape separation (boolean add)
1024 inflate = aBoard->GetDesignSettings().m_SolderMaskMinWidth / 2 - 1;
1025 }
1026
1027 // Build polygons for each pad shape. The size of the shape on solder mask should be size
1028 // of pad + clearance around the pad, where clearance = solder mask clearance + extra margin.
1029 // Extra margin is half the min width for solder mask, which is used to merge too-close shapes
1030 // (distance < SolderMaskMinWidth).
1031
1032 // Will contain exact shapes of all items on solder mask. We add this back in at the end just
1033 // to make sure that any artefacts introduced by the inflate/deflate don't remove parts of the
1034 // individual shapes.
1035 SHAPE_POLY_SET exactPolys;
1036
1037 // Plot a non filled basic PCB_SHAPE, according to the line style
1038 auto handlePcbShape =
1039 [&]( const PCB_SHAPE& aPcbShape )
1040 {
1041 LINE_STYLE lineStyle = aPcbShape.GetStroke().GetLineStyle();
1042
1043 if( lineStyle <= LINE_STYLE::FIRST_TYPE )
1044 {
1045 if( inflate != 0 )
1046 aPcbShape.TransformShapeToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE );
1047
1048 aPcbShape.TransformShapeToPolySet( *aResult, aLayer, inflate, maxError, ERROR_OUTSIDE );
1049 }
1050 else
1051 {
1052 int thickness = aPcbShape.GetWidth();
1053 std::vector<SHAPE*> shapes = aPcbShape.MakeEffectiveShapesForStroking( thickness );
1054 int minSegCount = 8;
1055
1056 for( SHAPE* shape : shapes )
1057 {
1058 STROKE_PARAMS::Stroke( shape, lineStyle, thickness, aPlotter->RenderSettings(),
1059 [&]( const VECTOR2I& a, const VECTOR2I& b )
1060 {
1061 if( inflate != 0 )
1062 {
1063 TransformOvalToPolygon( exactPolys, a, b,
1064 thickness, maxError, ERROR_OUTSIDE, minSegCount );
1065 }
1066
1067 TransformOvalToPolygon( *aResult, a, b,
1068 thickness+inflate, maxError, ERROR_OUTSIDE, minSegCount );
1069 } );
1070 }
1071
1072 for( SHAPE* shape : shapes )
1073 delete shape;
1074 }
1075 };
1076
1077 auto handleFPTextItem =
1078 [&]( const PCB_TEXT& aText )
1079 {
1080 if( !aPlotFPText )
1081 return;
1082
1083 if( aText.GetText() == wxT( "${REFERENCE}" ) && !aPlotReferences )
1084 return;
1085
1086 if( aText.GetText() == wxT( "${VALUE}" ) && !aPlotValues )
1087 return;
1088
1089 if( inflate != 0 )
1090 aText.TransformTextToPolySet( exactPolys, 0, maxError, ERROR_OUTSIDE );
1091
1092 aText.TransformTextToPolySet( *aResult, inflate, maxError, ERROR_OUTSIDE );
1093 };
1094
1095 auto handleTextBoxItem =
1096 [&]( const PCB_TEXTBOX& aTextBox )
1097 {
1098 if( inflate != 0 )
1099 aTextBox.TransformTextToPolySet( exactPolys, 0, maxError, ERROR_OUTSIDE );
1100
1101 aTextBox.TransformTextToPolySet( *aResult, inflate, maxError, ERROR_OUTSIDE );
1102
1103 if( !aTextBox.IsKnockout() ) // must plot box outline around text
1104 {
1105 PCB_SHAPE shapebox = aTextBox.GetPolygonalBoxShape();
1106 handlePcbShape( shapebox );
1107 }
1108 };
1109
1110 // Generate polygons with arcs inside the shape or exact shape to minimize shape changes
1111 // created by arc to segment size correction.
1113 {
1114 // Plot footprint pads and graphics
1115 for( const FOOTPRINT* footprint : aBoard->Footprints() )
1116 {
1117 if( inflate != 0 )
1118 footprint->TransformPadsToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE );
1119
1120 footprint->TransformPadsToPolySet( *aResult, aLayer, inflate, maxError, ERROR_OUTSIDE );
1121
1122 for( const PCB_FIELD* field : footprint->GetFields() )
1123 {
1124 wxCHECK2( field, continue );
1125
1126 if( field->IsReference() && !aPlotReferences )
1127 continue;
1128
1129 if( field->IsValue() && !aPlotValues )
1130 continue;
1131
1132 if( field->IsVisible() && field->IsOnLayer( aLayer ) )
1133 handleFPTextItem( static_cast<const PCB_TEXT&>( *field ) );
1134 }
1135
1136 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
1137 {
1138 if( item->IsOnLayer( aLayer ) )
1139 {
1140 if( item->Type() == PCB_TEXT_T )
1141 {
1142 handleFPTextItem( static_cast<const PCB_TEXT&>( *item ) );
1143 }
1144 else if( item->Type() == PCB_TEXTBOX_T )
1145 {
1146 handleTextBoxItem( static_cast<const PCB_TEXTBOX&>( *item ) );
1147 }
1148 else
1149 {
1150 if( const PCB_SHAPE* pcb_shape = dynamic_cast< const PCB_SHAPE*>( item ) )
1151 handlePcbShape( *pcb_shape );
1152 else // Shold not occur
1153 {
1154 if( inflate != 0 )
1155 item->TransformShapeToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE );
1156
1157 item->TransformShapeToPolySet( *aResult, aLayer, inflate, maxError, ERROR_OUTSIDE );
1158 }
1159 }
1160 }
1161 }
1162 }
1163
1164 // Plot untented vias and tracks
1165 for( const PCB_TRACK* track : aBoard->Tracks() )
1166 {
1167 // Note: IsOnLayer() checks relevant mask layers of untented vias and tracks
1168 if( !track->IsOnLayer( aLayer ) )
1169 continue;
1170
1171 int clearance = track->GetSolderMaskExpansion();
1172
1173 if( inflate != 0 )
1174 track->TransformShapeToPolygon( exactPolys, aLayer, clearance, maxError, ERROR_OUTSIDE );
1175
1176 track->TransformShapeToPolygon( *aResult, aLayer, clearance + inflate, maxError, ERROR_OUTSIDE );
1177 }
1178
1179 for( const BOARD_ITEM* item : aBoard->Drawings() )
1180 {
1181 if( item->IsOnLayer( aLayer ) )
1182 {
1183 if( item->Type() == PCB_TEXT_T )
1184 {
1185 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
1186
1187 if( inflate != 0 )
1188 text->TransformTextToPolySet( exactPolys, 0, maxError, ERROR_OUTSIDE );
1189
1190 text->TransformTextToPolySet( *aResult, inflate, maxError, ERROR_OUTSIDE );
1191 }
1192 else if( item->Type() == PCB_TEXTBOX_T )
1193 {
1194 handleTextBoxItem( static_cast<const PCB_TEXTBOX&>( *item ) );
1195 }
1196 else
1197 {
1198 if( const PCB_SHAPE* pcb_shape = dynamic_cast< const PCB_SHAPE*>( item ) )
1199 handlePcbShape( *pcb_shape );
1200 else // Shold not occur
1201 {
1202 if( inflate != 0 )
1203 item->TransformShapeToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE,
1204 aPlotter->RenderSettings() );
1205
1206 item->TransformShapeToPolySet( *aResult, aLayer, inflate, maxError,
1207 ERROR_OUTSIDE, aPlotter->RenderSettings() );
1208 }
1209 }
1210 }
1211 }
1212
1213 // Add filled zone areas.
1214 for( ZONE* zone : aBoard->Zones() )
1215 {
1216 if( zone->GetIsRuleArea() )
1217 continue;
1218
1219 if( !zone->IsOnLayer( aLayer ) )
1220 continue;
1221
1222 SHAPE_POLY_SET* fillData = zone->GetFill( aLayer );
1223
1224 if( !fillData )
1225 continue;
1226
1227 SHAPE_POLY_SET area = *fillData;
1228
1229 if( inflate != 0 )
1230 exactPolys.Append( area );
1231
1232 area.Inflate( inflate, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
1233 aResult->Append( area );
1234 }
1235 }
1236
1237 // Merge all polygons
1238 aResult->Simplify();
1239
1240 if( inflate != 0 )
1241 {
1242 aResult->Deflate( inflate, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
1243 // Add back in the exact polys. This is mandatory because inflate/deflate transform is
1244 // not perfect, and we want the initial areas perfectly kept.
1245 aResult->BooleanAdd( exactPolys );
1246 }
1247#undef ERROR
1248}
1249
1250
1257static void initializePlotter( PLOTTER* aPlotter, const BOARD* aBoard, const PCB_PLOT_PARAMS* aPlotOpts )
1258{
1259 PAGE_INFO pageA4( PAGE_SIZE_TYPE::A4 );
1260 const PAGE_INFO& pageInfo = aBoard->GetPageSettings();
1261 const PAGE_INFO* sheet_info;
1262 double paperscale; // Page-to-paper ratio
1263 VECTOR2I paperSizeIU;
1264 VECTOR2I pageSizeIU( pageInfo.GetSizeIU( pcbIUScale.IU_PER_MILS ) );
1265 bool autocenter = false;
1266
1267 // Special options: to fit the sheet to an A4 sheet replace the paper size. However there
1268 // is a difference between the autoscale and the a4paper option:
1269 // - Autoscale fits the board to the paper size
1270 // - A4paper fits the original paper size to an A4 sheet
1271 // - Both of them fit the board to an A4 sheet
1272 if( aPlotOpts->GetA4Output() )
1273 {
1274 sheet_info = &pageA4;
1275 paperSizeIU = pageA4.GetSizeIU( pcbIUScale.IU_PER_MILS );
1276 paperscale = (double) paperSizeIU.x / pageSizeIU.x;
1277 autocenter = true;
1278 }
1279 else
1280 {
1281 sheet_info = &pageInfo;
1282 paperSizeIU = pageSizeIU;
1283 paperscale = 1;
1284
1285 // Need autocentering only if scale is not 1:1
1286 autocenter = (aPlotOpts->GetScale() != 1.0) || aPlotOpts->GetAutoScale();
1287 }
1288
1289 BOX2I bbox = aBoard->ComputeBoundingBox( false, false );
1290 VECTOR2I boardCenter = bbox.Centre();
1291 VECTOR2I boardSize = bbox.GetSize();
1292
1293 double compound_scale;
1294
1295 // Fit to 80% of the page if asked; it could be that the board is empty, in this case
1296 // regress to 1:1 scale
1297 if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
1298 {
1299 double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
1300 double yscale = (paperSizeIU.y * 0.8) / boardSize.y;
1301
1302 compound_scale = std::min( xscale, yscale ) * paperscale;
1303 }
1304 else
1305 {
1306 compound_scale = aPlotOpts->GetScale() * paperscale;
1307 }
1308
1309 // For the plot offset we have to keep in mind the auxiliary origin too: if autoscaling is
1310 // off we check that plot option (i.e. autoscaling overrides auxiliary origin)
1311 VECTOR2I offset( 0, 0);
1312
1313 if( autocenter )
1314 {
1315 offset.x = KiROUND( boardCenter.x - ( paperSizeIU.x / 2.0 ) / compound_scale );
1316 offset.y = KiROUND( boardCenter.y - ( paperSizeIU.y / 2.0 ) / compound_scale );
1317 }
1318 else
1319 {
1320 if( aPlotOpts->GetUseAuxOrigin() )
1321 offset = aBoard->GetDesignSettings().GetAuxOrigin();
1322 }
1323
1324 aPlotter->SetPageSettings( *sheet_info );
1325
1326 aPlotter->SetViewport( offset, pcbIUScale.IU_PER_MILS/10, compound_scale, aPlotOpts->GetMirror() );
1327
1328 // For SVG fit-to-board plots the page is the board bounding box and the origin is at
1329 // (0,0), so the SVG viewBox must be that bounding box (it can extend to negative
1330 // coordinates relative to the origin, and the origin doesn't need to be on the page).
1331 if( aPlotOpts->GetFormat() == PLOT_FORMAT::SVG && aPlotOpts->GetSvgFitPagetoBoard() )
1332 aPlotter->SetPlotBBox( bbox );
1333
1334 // Has meaning only for gerber plotter. Must be called only after SetViewport
1335 aPlotter->SetGerberCoordinatesFormat( aPlotOpts->GetGerberPrecision() );
1336
1337 // Has meaning only for SVG plotter. Must be called only after SetViewport
1338 aPlotter->SetSvgCoordinatesFormat( aPlotOpts->GetSvgPrecision() );
1339
1340 aPlotter->SetCreator( wxT( "PCBNEW" ) );
1341 aPlotter->SetColorMode( !aPlotOpts->GetBlackAndWhite() ); // default is plot in Black and White.
1342 aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
1343}
1344
1345
1349static void FillNegativeKnockout( PLOTTER *aPlotter, const BOX2I &aBbbox )
1350{
1351 const int margin = 5 * pcbIUScale.IU_PER_MM; // Add a 5 mm margin around the board
1352 aPlotter->SetNegative( true );
1353 aPlotter->SetColor( WHITE ); // Which will be plotted as black
1354
1355 BOX2I area = aBbbox;
1356 area.Inflate( margin );
1357 aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILL_T::FILLED_SHAPE, 0, 0 );
1358 aPlotter->SetColor( BLACK );
1359}
1360
1361
1362static void plotBackgroundColor( BOARD* aBoard, const PCB_PLOT_PARAMS* aPlotOpts, PLOTTER* aPlotter )
1363{
1364 const PAGE_INFO& pageInfo = aPlotter->PageSettings();
1365 const VECTOR2I plotOffset = aPlotter->GetPlotOffsetUserUnits();
1366 const VECTOR2I pageSizeIU( pageInfo.GetWidthIU( pcbIUScale.IU_PER_MILS ),
1367 pageInfo.GetHeightIU( pcbIUScale.IU_PER_MILS ) );
1368
1369 if( aPlotter->GetColorMode()
1370 && aPlotOpts->GetBackgroundColor() != COLOR4D::UNSPECIFIED )
1371 {
1372 aPlotter->SetColor( aPlotOpts->GetBackgroundColor() );
1373
1374 // Undo the viewport scale so the background fills the physical page.
1375 VECTOR2I end = plotOffset + pageSizeIU / aPlotter->GetPlotScale();
1376
1377 aPlotter->Rect( plotOffset, end, FILL_T::FILLED_SHAPE, 1.0 );
1378 }
1379}
1380
1381
1388PLOTTER* StartPlotBoard( BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aLayer,
1389 const wxString& aLayerName, const wxString& aFullFileName,
1390 const wxString& aSheetName, const wxString& aSheetPath,
1391 const wxString& aPageName, const wxString& aPageNumber,
1392 const int aPageCount )
1393{
1394 wxCHECK( aBoard && aPlotOpts, nullptr );
1395
1396 // Create the plotter driver and set the few plotter specific options
1397 PLOTTER* plotter = nullptr;
1398
1399 switch( aPlotOpts->GetFormat() )
1400 {
1401 case PLOT_FORMAT::DXF:
1402 DXF_PLOTTER* DXF_plotter;
1403 DXF_plotter = new DXF_PLOTTER();
1404 DXF_plotter->SetUnits( aPlotOpts->GetDXFPlotUnits() );
1405
1406 plotter = DXF_plotter;
1407
1408 if( !aPlotOpts->GetLayersToExport().empty() )
1409 plotter->SetLayersToExport( aPlotOpts->GetLayersToExport() );
1410 break;
1411
1412 case PLOT_FORMAT::POST:
1413 PS_PLOTTER* PS_plotter;
1414 PS_plotter = new PS_PLOTTER();
1415 PS_plotter->SetScaleAdjust( aPlotOpts->GetFineScaleAdjustX(),
1416 aPlotOpts->GetFineScaleAdjustY() );
1417 plotter = PS_plotter;
1418 break;
1419
1420 case PLOT_FORMAT::PDF:
1421 plotter = new PDF_PLOTTER( aBoard->GetProject() );
1422 break;
1423
1424 case PLOT_FORMAT::HPGL:
1425 wxLogError( _( "HPGL plotting is no longer supported as of KiCad 10.0" ) );
1426 return nullptr;
1427
1429 // For Gerber plotter, a valid board layer must be set, in order to create a valid
1430 // Gerber header, especially the TF.FileFunction and .FilePolarity data
1431 if( aLayer < PCBNEW_LAYER_ID_START || aLayer >= PCB_LAYER_ID_COUNT )
1432 {
1433 wxLogError( wxString::Format( "Invalid board layer %d, cannot build a valid Gerber file header",
1434 aLayer ) );
1435 }
1436
1437 plotter = new GERBER_PLOTTER();
1438 break;
1439
1440 case PLOT_FORMAT::SVG:
1441 plotter = new SVG_PLOTTER();
1442 break;
1443
1444 case PLOT_FORMAT::PNG:
1445 {
1446 PNG_PLOTTER* pngPlotter = new PNG_PLOTTER();
1447
1448 PAGE_INFO pageInfo = aBoard->GetPageSettings();
1449 VECTOR2D sizeIU = pageInfo.GetSizeIU( pcbIUScale.IU_PER_MILS );
1450 int dpi = aPlotOpts->GetPngDPI();
1451 double iuPerInch = pcbIUScale.IU_PER_MILS * 1000.0;
1452
1453 pngPlotter->SetPixelSize( KiROUND( sizeIU.x * dpi / iuPerInch ),
1454 KiROUND( sizeIU.y * dpi / iuPerInch ) );
1455 pngPlotter->SetResolution( dpi );
1456 pngPlotter->SetAntialias( aPlotOpts->GetPngAntialias() );
1457 plotter = pngPlotter;
1458 break;
1459 }
1460
1461 default:
1462 wxASSERT( false );
1463 return nullptr;
1464 }
1465
1467 renderSettings->LoadColors( aPlotOpts->ColorSettings() );
1468 renderSettings->SetDefaultPenWidth( pcbIUScale.mmToIU( 0.0212 ) ); // Hairline at 1200dpi
1469 renderSettings->SetLayerName( aLayerName );
1470 renderSettings->SetDashLengthRatio( aPlotOpts->GetDashedLineDashRatio() );
1471 renderSettings->SetGapLengthRatio( aPlotOpts->GetDashedLineGapRatio() );
1472
1473 plotter->SetRenderSettings( renderSettings );
1474
1475 // Compute the viewport and set the other options
1476
1477 // page layout is not mirrored, so temporarily change mirror option for the page layout
1478 PCB_PLOT_PARAMS plotOpts = *aPlotOpts;
1479
1480 if( plotOpts.GetPlotFrameRef() )
1481 {
1482 if( plotOpts.GetMirror() )
1483 plotOpts.SetMirror( false );
1484 if( plotOpts.GetScale() != 1.0 )
1485 plotOpts.SetScale( 1.0 );
1486 if( plotOpts.GetAutoScale() )
1487 plotOpts.SetAutoScale( false );
1488 }
1489
1490 initializePlotter( plotter, aBoard, &plotOpts );
1491
1492 if( plotter->OpenFile( aFullFileName ) )
1493 {
1494 plotter->ClearHeaderLinesList();
1495
1496 // For the Gerber "file function" attribute, set the layer number
1497 if( plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
1498 {
1499 bool useX2mode = plotOpts.GetUseGerberX2format();
1500
1501 GERBER_PLOTTER* gbrplotter = static_cast <GERBER_PLOTTER*> ( plotter );
1502 gbrplotter->DisableApertMacros( plotOpts.GetDisableGerberMacros() );
1503 gbrplotter->UseX2format( useX2mode );
1504 gbrplotter->UseX2NetAttributes( plotOpts.GetIncludeGerberNetlistInfo() );
1505
1506 // Attributes can be added using X2 format or as comment (X1 format)
1507 AddGerberX2Attribute( plotter, aBoard, aLayer, not useX2mode );
1508 }
1509
1510 bool startPlotSuccess = false;
1511 try
1512 {
1513 if( plotter->GetPlotterType() == PLOT_FORMAT::PDF )
1514 startPlotSuccess = static_cast<PDF_PLOTTER*>( plotter )->StartPlot( aPageNumber, aPageName );
1515 else
1516 startPlotSuccess = plotter->StartPlot( aPageName );
1517 }
1518 catch( ... )
1519 {
1520 startPlotSuccess = false;
1521 }
1522
1523
1524 if( startPlotSuccess )
1525 {
1526 if( aPlotOpts->GetFormat() == PLOT_FORMAT::PDF
1527 || aPlotOpts->GetFormat() == PLOT_FORMAT::PNG )
1528 {
1529 plotBackgroundColor( aBoard, aPlotOpts, plotter );
1530 }
1531
1532 // Plot the frame reference if requested
1533 if( aPlotOpts->GetPlotFrameRef() )
1534 {
1535 wxString variantName = aBoard->GetCurrentVariant();
1536 wxString variantDesc = aBoard->GetVariantDescription( variantName );
1537
1538 PlotDrawingSheet( plotter, aBoard->GetProject(), aBoard->GetTitleBlock(), aBoard->GetPageSettings(),
1539 &aBoard->GetProperties(), aPageNumber, aPageCount, aSheetName, aSheetPath,
1540 aBoard->GetFileName(), renderSettings->GetLayerColor( LAYER_DRAWINGSHEET ), true,
1541 variantName, variantDesc );
1542
1543 if( aPlotOpts->GetMirror() || aPlotOpts->GetScale() != 1.0 || aPlotOpts->GetAutoScale() )
1544 initializePlotter( plotter, aBoard, aPlotOpts );
1545 }
1546
1547 // When plotting a negative board: draw a black rectangle (background for plot board
1548 // in white) and switch the current color to WHITE; note the color inversion is actually
1549 // done in the driver (if supported)
1550 if( aPlotOpts->GetNegative() )
1551 {
1552 BOX2I bbox = aBoard->ComputeBoundingBox( false, false );
1553 FillNegativeKnockout( plotter, bbox );
1554 }
1555
1556 return plotter;
1557 }
1558 }
1559
1560 delete plotter->RenderSettings();
1561 delete plotter;
1562 return nullptr;
1563}
1564
1565
1566void setupPlotterNewPDFPage( PLOTTER* aPlotter, BOARD* aBoard, PCB_PLOT_PARAMS* aPlotOpts,
1567 const wxString& aLayerName, const wxString& aSheetName,
1568 const wxString& aSheetPath, const wxString& aPageNumber,
1569 int aPageCount )
1570{
1571 plotBackgroundColor( aBoard, aPlotOpts, aPlotter );
1572
1573 aPlotter->RenderSettings()->SetLayerName( aLayerName );
1574
1575 // Plot the frame reference if requested
1576 if( aPlotOpts->GetPlotFrameRef() )
1577 {
1578 // Mirror and scale shouldn't be applied to the drawing sheet
1579 bool revertOps = false;
1580 bool oldMirror = aPlotOpts->GetMirror();
1581 bool oldAutoScale = aPlotOpts->GetAutoScale();
1582 double oldScale = aPlotOpts->GetScale();
1583
1584 if( oldMirror || oldAutoScale || oldScale != 1.0 )
1585 {
1586 aPlotOpts->SetMirror( false );
1587 aPlotOpts->SetScale( 1.0 );
1588 aPlotOpts->SetAutoScale( false );
1589 initializePlotter( aPlotter, aBoard, aPlotOpts );
1590 revertOps = true;
1591 }
1592
1593 wxString variantName = aBoard->GetCurrentVariant();
1594 wxString variantDesc = aBoard->GetVariantDescription( variantName );
1595
1596 PlotDrawingSheet( aPlotter, aBoard->GetProject(), aBoard->GetTitleBlock(), aBoard->GetPageSettings(),
1597 &aBoard->GetProperties(), aPageNumber, aPageCount, aSheetName, aSheetPath,
1598 aBoard->GetFileName(), aPlotter->RenderSettings()->GetLayerColor( LAYER_DRAWINGSHEET ), true,
1599 variantName, variantDesc );
1600
1601 if( revertOps )
1602 {
1603 aPlotOpts->SetMirror( oldMirror );
1604 aPlotOpts->SetScale( oldScale );
1605 aPlotOpts->SetAutoScale( oldAutoScale );
1606 initializePlotter( aPlotter, aBoard, aPlotOpts );
1607 }
1608 }
1609}
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
@ LAYER_CLASS_FAB
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
const VECTOR2I & GetAuxOrigin() const
int m_LineThickness[LAYER_CLASS_COUNT]
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
void ConvertBrdLayerToPolygonalContours(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aOutlines, KIGFX::RENDER_SETTINGS *aRenderSettings=nullptr) const
Build a set of polygons which are the outlines of copper items (pads, tracks, vias,...
Definition board.cpp:4268
const PAGE_INFO & GetPageSettings() const
Definition board.h:1018
const ZONES & Zones() const
Definition board.h:468
const LSET & DrillSymbolLayers() const
Layers that currently have a drill map on them.
Definition board.h:612
TITLE_BLOCK & GetTitleBlock()
Definition board.h:1024
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:525
const FOOTPRINTS & Footprints() const
Definition board.h:464
const TRACKS & Tracks() const
Definition board.h:462
const wxString & GetFileName() const
Definition board.h:453
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:3327
wxString GetCurrentVariant() const
Definition board.h:529
PROJECT * GetProject() const
Definition board.h:775
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2742
const DRAWINGS & Drawings() const
Definition board.h:466
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition box2.h:135
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr const SizeVec & GetSize() const
Definition box2.h:203
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
void SetLayerSet(const LSET &aLayerMask)
Definition pcbplot.h:84
void PlotDrillMarks()
Draw a drill mark for pads and vias.
void PlotZone(const ZONE *aZone, PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
void PlotPadNumber(const PAD *aPad, const COLOR4D &aColor)
void PlotBoardGraphicItem(const BOARD_ITEM *item)
Plot items like text and graphics but not tracks and footprints.
void PlotPad(const PAD *aPad, PCB_LAYER_ID aLayer, const COLOR4D &aColor, bool aSketchMode)
Plot a pad.
COLOR4D getColor(int aLayer) const
White color is special because it cannot be seen on a white paper in B&W mode.
void PlotFootprintTextItems(const FOOTPRINT *aFootprint)
int getFineWidthAdj() const
Definition pcbplot.h:75
void PlotDrillSymbols(PCB_LAYER_ID aLayer)
Draw each hole's chart symbol on aLayer, from the board's shared symbol profile.
void PlotFootprintGraphicItems(const FOOTPRINT *aFootprint)
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
static const COLOR4D BLACK
Definition color4d.h:404
COLOR4D GetColor(int aLayer) const
When creating polygons to create a clearance polygonal area, the polygon must be same or bigger than ...
void SetUnits(DXF_UNITS aUnit)
Set the units to use for plotting the DXF file.
@ GBR_APERTURE_ATTRIB_CONDUCTOR
Aperture used for connected items like tracks (not vias).
@ GBR_APERTURE_ATTRIB_VIAPAD
Aperture used for vias.
@ GBR_APERTURE_ATTRIB_NONE
uninitialized attribute.
Metadata which can be added in a gerber file as attribute in X2 format.
void SetNetName(const wxString &aNetname)
void SetApertureAttrib(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB aApertAttribute)
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
void SetNetAttribType(int aNetAttribType)
@ GBR_NETINFO_NET
print info associated to a net (TO.N attribute)
@ GBR_NETINFO_UNSPECIFIED
idle command (no command)
bool m_NotInNet
true if a pad of a footprint cannot be connected (for instance a mechanical NPTH, ot a not named pad)...
void UseX2format(bool aEnable)
void UseX2NetAttributes(bool aEnable)
void DisableApertMacros(bool aDisable)
Disable Aperture Macro (AM) command, only for broken Gerber Readers.
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
COLOR4D LegacyMix(const COLOR4D &aColor) const
Mix this COLOR4D with an input COLOR4D using the OR-mixing of legacy canvas.
Definition color4d.cpp:232
PCB specific render settings.
Definition pcb_painter.h:84
void LoadColors(const COLOR_SETTINGS *aSettings) override
void SetDefaultPenWidth(int aWidth)
void SetGapLengthRatio(double aRatio)
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
void SetLayerName(const wxString &aLayerName)
void SetDashLengthRatio(double aRatio)
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
Definition lseq.h:47
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
LSEQ SeqStackupForPlotting() const
Return the sequence that is typical for a bottom-to-top stack-up.
Definition lset.cpp:400
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
Handle the data for a net.
Definition netinfo.h:50
Definition pad.h:61
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
int GetHeightIU(double aIUScale) const
Get the page height in IU.
Definition page_info.h:163
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition page_info.h:172
int GetWidthIU(double aIUScale) const
Get the page width in IU.
Definition page_info.h:154
bool IsDegenerated(int aThreshold=5) const
EDA_ANGLE GetArcAngleStart() const
double GetRadius() const
EDA_ANGLE GetAngle() const
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h:302
Parameters and options when plotting/printing a board.
bool GetNegative() const
PLOT_FORMAT GetFormat() const
bool GetSkipPlotNPTH_Pads() const
bool GetPngAntialias() const
void SetDrillMarksType(DRILL_MARKS aVal)
bool GetUseAuxOrigin() const
bool GetHideDNPFPsOnFabLayers() const
void SetSkipPlotNPTH_Pads(bool aSkip)
bool GetMirror() const
DXF_UNITS GetDXFPlotUnits() const
bool GetAutoScale() const
bool GetCrossoutDNPFPsOnFabLayers() const
void SetDXFPlotPolygonMode(bool aFlag)
void SetAutoScale(bool aFlag)
unsigned GetSvgPrecision() const
double GetScale() const
PLOT_TEXT_MODE GetTextMode() const override
bool GetDXFPlotPolygonMode() const
bool GetSvgFitPagetoBoard() const
bool GetPlotReference() const
bool m_PDFFrontFPPropertyPopups
Generate PDF property popup menus for footprints.
void SetScale(double aVal)
void SetMirror(bool aFlag)
bool GetSketchPadsOnFabLayers() const
bool GetSubtractMaskFromSilk() const
COLOR4D GetBackgroundColor() const
int GetGerberPrecision() const
double GetFineScaleAdjustY() const
bool GetPlotPadNumbers() const
bool GetA4Output() const
DRILL_MARKS GetDrillMarksType() const
bool GetUseGerberX2format() const
bool GetPlotValue() const
bool GetIncludeGerberNetlistInfo() const
int GetPngDPI() const
double GetFineScaleAdjustX() const
bool GetBlackAndWhite() const
double GetDashedLineGapRatio() const
bool m_PDFBackFPPropertyPopups
on front and/or back of board
bool GetPlotFPText() const
double GetDashedLineDashRatio() const
bool GetPlotFrameRef() const
bool GetDisableGerberMacros() const
std::vector< std::pair< PCB_LAYER_ID, wxString > > GetLayersToExport() const
COLOR_SETTINGS * ColorSettings() const
Base plotter engine class.
Definition plotter.h:136
virtual void Circle(const VECTOR2I &pos, int diametre, FILL_T fill, int width)=0
virtual bool OpenFile(const wxString &aFullFilename)
Open or create the plot file aFullFilename.
Definition plotter.cpp:75
virtual void SetNegative(bool aNegative)
Definition plotter.h:156
virtual void SetSvgCoordinatesFormat(unsigned aPrecision)
Set the number of digits for mantissa in coordinates in mm for SVG plotter.
Definition plotter.h:583
virtual void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition plotter.h:169
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:166
static const int USE_DEFAULT_LINE_WIDTH
Definition plotter.h:140
double GetPlotScale() const
Definition plotter.h:625
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:388
virtual bool StartPlot(const wxString &aPageNumber)=0
void SetLayersToExport(const std::vector< std::pair< PCB_LAYER_ID, wxString > > &aLayersToExport)
Sets the list of layers to export to the specified vector.
Definition plotter.h:234
RENDER_SETTINGS * RenderSettings()
Definition plotter.h:167
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false)
Definition plotter.h:577
virtual void Bookmark(const BOX2I &aBox, const wxString &aName, const wxString &aGroupName=wxEmptyString)
Create a bookmark to a symbol.
Definition plotter.h:533
virtual PLOT_FORMAT GetPlotterType() const =0
Return the effective plot engine in use.
virtual void ThickArc(const EDA_SHAPE &aArcShape, void *aData, int aWidth)
Definition plotter.cpp:483
virtual void SetTextMode(PLOT_TEXT_MODE mode)
Change the current text mode.
Definition plotter.h:572
virtual void Rect(const VECTOR2I &p1, const VECTOR2I &p2, FILL_T fill, int width, int aCornerRadius=0)=0
virtual void SetCreator(const wxString &aCreator)
Definition plotter.h:188
VECTOR2I GetPlotOffsetUserUnits()
Definition plotter.h:620
void ClearHeaderLinesList()
Remove all lines from the list of free lines to print at the beginning of the file.
Definition plotter.h:206
bool GetColorMode() const
Definition plotter.h:164
PAGE_INFO & PageSettings()
Definition plotter.h:170
virtual void SetViewport(const VECTOR2I &aOffset, double aIusPerDecimil, double aScale, bool aMirror)=0
Set the plot offset and scaling for the current plot.
virtual void SetColorMode(bool aColorMode)
Plot in B/W or color.
Definition plotter.h:163
virtual void StartBlock(void *aData)
calling this function allows one to define the beginning of a group of drawing items,...
Definition plotter.h:606
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData)
Definition plotter.cpp:441
virtual void PlotPoly(const std::vector< VECTOR2I > &aCornerList, FILL_T aFill, int aWidth, void *aData)=0
Draw a polygon ( filled or not ).
virtual void FlashPadCircle(const VECTOR2I &aPadPos, int aDiameter, void *aData)=0
virtual void SetPlotBBox(const BOX2I &aBBoxIU)
Set an explicit bounding box for the plotted content (in IUs).
Definition plotter.h:594
virtual void HyperlinkMenu(const BOX2I &aBox, const std::vector< wxString > &aDestURLs)
Create a clickable hyperlink menu with a rectangular click area.
Definition plotter.h:521
virtual void SetLayerPolarity(bool aPositive)
Set the current Gerber layer polarity to positive or negative by writing %LPD*% or %LPC*% to the Gerb...
Definition plotter.h:562
virtual void ThickCircle(const VECTOR2I &pos, int diametre, int width, void *aData)
Definition plotter.cpp:514
virtual void SetColor(const COLOR4D &color)=0
virtual void EndBlock(void *aData)
calling this function allows one to define the end of a group of drawing items for instance in SVG or...
Definition plotter.h:615
PNG rasterization plotter using Cairo graphics library.
Definition plotter_png.h:40
void SetPixelSize(int aWidth, int aHeight)
Set the output image dimensions in pixels.
Definition plotter_png.h:64
void SetResolution(int aDPI)
Set the output resolution in dots per inch.
Definition plotter_png.h:56
void SetAntialias(bool aEnable)
Enable or disable anti-aliasing.
Definition plotter_png.h:84
void SetScaleAdjust(double scaleX, double scaleY)
Set the 'fine' scaling for the postscript engine.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
void DeletePolygon(int aIdx)
Delete aIdx-th polygon from the set.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
int OutlineCount() const
Return the number of outlines in the set.
void InflateWithLinkedHoles(int aFactor, CORNER_STRATEGY aCornerStrategy, int aMaxError)
Perform outline inflation/deflation, using round corners.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
SHAPE_POLY_SET CloneDropTriangulation() const
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const POLYGON & CPolygon(int aIndex) const
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
An abstract shape on 2D plane.
Definition shape.h:124
static void Stroke(const SHAPE *aShape, LINE_STYLE aLineStyle, int aWidth, const KIGFX::RENDER_SETTINGS *aRenderSettings, const std::function< void(const VECTOR2I &a, const VECTOR2I &b)> &aStroker)
Handle a list of polygons defining a copper zone.
Definition zone.h:70
void SetMinThickness(int aMinThickness)
Definition zone.h:316
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:640
A type-safe container of any type.
Definition ki_any.h:92
constexpr any() noexcept
Default constructor, creates an empty object.
Definition ki_any.h:155
@ WHITE
Definition color4d.h:44
@ LIGHTGRAY
Definition color4d.h:43
@ BLACK
Definition color4d.h:40
@ FOR_GUI
Definition common.h:91
void PlotDrawingSheet(PLOTTER *plotter, const PROJECT *aProject, const TITLE_BLOCK &aTitleBlock, const PAGE_INFO &aPageInfo, const std::map< wxString, wxString > *aProperties, const wxString &aSheetNumber, int aSheetCount, const wxString &aSheetName, const wxString &aSheetPath, const wxString &aFilename, COLOR4D aColor, bool aIsFirstPage, const wxString &aVariantName, const wxString &aVariantDesc)
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ ROUND_ALL_CORNERS
All angles are rounded.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
@ NO_FILL
Definition eda_fill.h:30
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
Handle special data (items attributes) during plot.
a few functions useful in geometry calculations.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
@ LAYER_DRAWINGSHEET
Sheet frame and title block.
Definition layer_ids.h:274
@ LAYER_VIAS
Meta control for all vias opacity/visibility.
Definition layer_ids.h:228
@ LAYER_DNP_MARKER
Definition layer_ids.h:500
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_Adhes
Definition layer_ids.h:99
@ Edge_Cuts
Definition layer_ids.h:108
@ Dwgs_User
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:100
@ Cmts_User
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:98
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ Eco1_User
Definition layer_ids.h:105
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ F_Fab
Definition layer_ids.h:115
@ Margin
Definition layer_ids.h:109
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ Eco2_User
Definition layer_ids.h:106
@ B_SilkS
Definition layer_ids.h:97
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition padstack.h:51
@ CHAMFERED_RECT
Definition padstack.h:59
@ ROUNDRECT
Definition padstack.h:56
@ TRAPEZOID
Definition padstack.h:55
@ RECTANGLE
Definition padstack.h:53
void AddGerberX2Attribute(PLOTTER *aPlotter, const BOARD *aBoard, int aLayer, bool aUseX1CompatibilityMode)
Calculate some X2 attributes as defined in the Gerber file format specification and add them to the g...
Definition pcbplot.cpp:356
void GenerateLayerPoly(SHAPE_POLY_SET *aResult, BOARD *aBoard, PLOTTER *aPlotter, PCB_LAYER_ID aLayer, bool aPlotFPText, bool aPlotReferences, bool aPlotValues)
Generates a SHAPE_POLY_SET representing the plotted items on a layer.
static void FillNegativeKnockout(PLOTTER *aPlotter, const BOX2I &aBbbox)
Prefill in black an area a little bigger than the board to prepare for the negative plot.
void PlotClippedSilkLayer(BOARD *aBoard, PLOTTER *aPlotter, const LSET &aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
void PlotBoardLayers(BOARD *aBoard, PLOTTER *aPlotter, const LSEQ &aLayers, const PCB_PLOT_PARAMS &aPlotOptions)
Plot a sequence of board layer IDs.
void PlotStandardLayer(BOARD *aBoard, PLOTTER *aPlotter, const LSET &aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
Plot any layer EXCEPT a solder-mask with an enforced minimum width.
PLOTTER * StartPlotBoard(BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aLayer, const wxString &aLayerName, const wxString &aFullFileName, const wxString &aSheetName, const wxString &aSheetPath, const wxString &aPageName, const wxString &aPageNumber, const int aPageCount)
Open a new plotfile using the options (and especially the format) specified in the options and prepar...
void PlotPolySet(BOARD *aBoard, PLOTTER *aPlotter, const PCB_PLOT_PARAMS &aPlotOpt, SHAPE_POLY_SET *aPolySet, PCB_LAYER_ID aLayer)
void setupPlotterNewPDFPage(PLOTTER *aPlotter, BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts, const wxString &aLayerName, const wxString &aSheetName, const wxString &aSheetPath, const wxString &aPageNumber, int aPageCount)
void PlotSolderMaskLayer(BOARD *aBoard, PLOTTER *aPlotter, const LSET &aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
Plot a solder mask layer.
static void initializePlotter(PLOTTER *aPlotter, const BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts)
Set up most plot options for plotting a board (especially the viewport) Important thing: page size is...
void PlotInteractiveLayer(BOARD *aBoard, PLOTTER *aPlotter, const PCB_PLOT_PARAMS &aPlotOpt)
Plot interactive items (hypertext links, properties, etc.).
static void plotBackgroundColor(BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, PLOTTER *aPlotter)
void PlotOneBoardLayer(BOARD *aBoard, PLOTTER *aPlotter, PCB_LAYER_ID aLayer, const PCB_PLOT_PARAMS &aPlotOpt, bool isPrimaryLayer)
Plot one copper or technical layer.
void PlotLayer(BOARD *aBoard, PLOTTER *aPlotter, const LSET &layerMask, const PCB_PLOT_PARAMS &plotOpts)
void PlotLayerOutlines(BOARD *aBoard, PLOTTER *aPlotter, const LSET &aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
Plot outlines.
#define getMetadata()
Plotting engines similar to ps (PostScript, Gerber, svg)
std::vector< FAB_LAYER_COLOR > dummy
LINE_STYLE
Dashed line types.
std::string path
VECTOR2I center
VECTOR2I end
int clearance
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707