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 aPlotter->ClearPlottedFootprints();
135
136 PCB_PLOT_PARAMS plotOptions = aPlotOptions;
137
138 // Gerber carries no colour, so a drill mark lands as ink instead of knocking out its pad
139 // The plot dialog forces them off for gerber but API and CLI callers arrive here directly
140 if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER )
142
143 for( PCB_LAYER_ID layer : aLayers )
144 PlotOneBoardLayer( aBoard, aPlotter, layer, plotOptions, layer == aLayers[0] );
145
146 // Drill symbols go after the normal layers but before the physical marks, so a symbol is
147 // never sitting under a knockout
148 LSET mapLayers = aBoard->DrillSymbolLayers() & LSET( aLayers );
149
150 if( mapLayers.any() )
151 {
152 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, plotOptions );
153 itemplotter.SetLayerSet( aLayers );
154
155 for( PCB_LAYER_ID layer : mapLayers.Seq() )
156 itemplotter.PlotDrillSymbols( layer );
157 }
158
159 // Drill marks are plotted in white to knockout the pad if any layers of the pad are
160 // being plotted, and in black if the pad is not being plotted. For the former, this
161 // must happen after all other layers are plotted.
162
163 // One global knockout pass, so a plotted layer carrying a map skips them rather than
164 // punching through the symbols they would annotate
165 if( plotOptions.GetDrillMarksType() != DRILL_MARKS::NO_DRILL_SHAPE && !mapLayers.any() )
166 {
167 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, plotOptions );
168 itemplotter.SetLayerSet( aLayers );
169 itemplotter.PlotDrillMarks();
170 }
171}
172
173
174void PlotInteractiveLayer( BOARD* aBoard, PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpt )
175{
176 for( const FOOTPRINT* fp : aBoard->Footprints() )
177 {
178 bool onBack = fp->GetLayer() == B_Cu;
179
180 if( !onBack && !aPlotOpt.m_PDFFrontFPPropertyPopups )
181 continue;
182
183 if( onBack && !aPlotOpt.m_PDFBackFPPropertyPopups )
184 continue;
185
186 // A bookmark or a popup where the footprint draws nothing would point at empty space
187 if( !aPlotter->WasFootprintPlotted( fp ) )
188 continue;
189
190 std::vector<wxString> properties;
191
192 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
193 _( "Reference designator" ),
194 fp->Reference().GetShownText( FOR_GUI ) ) );
195
196 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
197 _( "Value" ),
198 fp->Value().GetShownText( FOR_GUI ) ) );
199
200 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
201 _( "Footprint" ),
202 fp->GetFPID().GetUniStringLibItemName() ) );
203
204 for( const PCB_FIELD* field : fp->GetFields() )
205 {
206 wxCHECK2( field, continue );
207
208 if( field->IsReference() || field->IsValue() )
209 continue;
210
211 if( field->GetText().IsEmpty() )
212 continue;
213
214 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
215 field->GetName(),
216 field->GetText() ) );
217 }
218
219 // These 2 properties are not very useful in a plot file (like a PDF)
220#if 0
221 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Library Description" ),
222 fp->GetLibDescription() ) );
223
224 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Keywords" ),
225 fp->GetKeywords() ) );
226#endif
227 // Draw items are plotted with a position offset. So we need to move
228 // our boxes (which are not plotted) by the same offset.
229 VECTOR2I offset = -aPlotter->GetPlotOffsetUserUnits();
230
231 // Use a footprint bbox without texts to create the hyperlink area
232 BOX2I bbox = fp->GetBoundingBox( false );
233 bbox.Move( offset );
234 aPlotter->HyperlinkMenu( bbox, properties );
235
236 // Use a footprint bbox with visible texts only to create the bookmark area
237 // which is the area to zoom on ft selection
238 // However the bbox need to be inflated for a better look.
239 bbox = fp->GetBoundingBox( true );
240 bbox.Move( offset );
241 bbox.Inflate( bbox.GetWidth() /2, bbox.GetHeight() /2 );
242 aPlotter->Bookmark( bbox, fp->GetReference(), _( "Footprints" ) );
243 }
244}
245
246
247void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
248 const PCB_PLOT_PARAMS& aPlotOpt, bool isPrimaryLayer )
249{
250 PCB_PLOT_PARAMS plotOpt = aPlotOpt;
251
252 // Set a default color and the text mode for this layer
253 aPlotter->SetColor( BLACK );
254 aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
255
256 // Specify that the contents of the "Edges Pcb" layer are to be plotted in addition to the
257 // contents of the currently specified layer.
258 LSET layer_mask( { aLayer } );
259
260 if( IsCopperLayer( aLayer ) )
261 {
262 // Skip NPTH pads on copper layers ( only if hole size == pad size ):
263 // Drill mark will be plotted if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
264 if( plotOpt.GetFormat() == PLOT_FORMAT::DXF )
265 plotOpt.SetDXFPlotPolygonMode( true );
266 else
267 plotOpt.SetSkipPlotNPTH_Pads( true );
268
269 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
270 }
271 else
272 {
273 switch( aLayer )
274 {
275 case B_Mask:
276 case F_Mask:
277 // Use outline mode for DXF
278 plotOpt.SetDXFPlotPolygonMode( true );
279
280 // Plot solder mask:
281 PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt );
282
283 break;
284
285 case B_Adhes:
286 case F_Adhes:
287 case B_Paste:
288 case F_Paste:
289 // Disable plot pad holes
291
292 // Use outline mode for DXF
293 plotOpt.SetDXFPlotPolygonMode( true );
294
295 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
296
297 break;
298
299 case F_SilkS:
300 case B_SilkS:
301 if( plotOpt.GetSubtractMaskFromSilk() )
302 {
303 if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER && isPrimaryLayer )
304 {
305 // Use old-school, positive/negative mask plotting which preserves utilization
306 // of Gerber aperture masks. This method can only be used when the given silk
307 // layer is the primary layer as the negative mask will also knockout any other
308 // (non-silk) layers that were plotted before the silk layer.
309
310 PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
311
312 // Create the mask to subtract by creating a negative layer polarity
313 aPlotter->SetLayerPolarity( false );
314
315 // Disable plot pad holes
317
318 // Plot the mask
319 layer_mask = ( aLayer == F_SilkS ) ? LSET( { F_Mask } ) : LSET( { B_Mask } );
320 PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt );
321
322 // Disable the negative polarity
323 aPlotter->SetLayerPolarity( true );
324 }
325 else
326 {
327 PlotClippedSilkLayer( aBoard, aPlotter, layer_mask, plotOpt );
328 }
329
330 break;
331 }
332
333 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
334 break;
335
336 case Dwgs_User:
337 case Cmts_User:
338 case Eco1_User:
339 case Eco2_User:
340 case Edge_Cuts:
341 case Margin:
342 case F_CrtYd:
343 case B_CrtYd:
344 case F_Fab:
345 case B_Fab:
346 default:
347 PlotLayer( aBoard, aPlotter, layer_mask, plotOpt );
348 break;
349 }
350 }
351}
352
353
357void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, const LSET& aLayerMask,
358 const PCB_PLOT_PARAMS& aPlotOpt )
359{
360 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
361 int maxError = aBoard->GetDesignSettings().m_MaxError;
362
363 itemplotter.SetLayerSet( aLayerMask );
364
365 bool onCopperLayer = ( LSET::AllCuMask() & aLayerMask ).any();
366 bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & aLayerMask ).any();
367 bool onSolderPasteLayer = ( LSET( { F_Paste, B_Paste } ) & aLayerMask ).any();
368 bool onFrontFab = ( LSET( { F_Fab } ) & aLayerMask ).any();
369 bool onBackFab = ( LSET( { B_Fab } ) & aLayerMask ).any();
370 bool sketchPads = ( onFrontFab || onBackFab ) && aPlotOpt.GetSketchPadsOnFabLayers();
371 const wxString variantName = aBoard->GetCurrentVariant();
372
373 // Plot edge layer and graphic items
374 for( const BOARD_ITEM* item : aBoard->Drawings() )
375 itemplotter.PlotBoardGraphicItem( item );
376
377 // Draw footprint texts:
378 for( const FOOTPRINT* footprint : aBoard->Footprints() )
379 itemplotter.PlotFootprintTextItems( footprint );
380
381 // Draw footprint other graphic items:
382 for( const FOOTPRINT* footprint : aBoard->Footprints() )
383 itemplotter.PlotFootprintGraphicItems( footprint );
384
385 // Plot footprint pads
386 for( FOOTPRINT* footprint : aBoard->Footprints() )
387 {
388 const bool dnp = footprint->GetDNPForVariant( variantName );
389
390 aPlotter->StartBlock( nullptr );
391
392 for( PAD* pad : footprint->Pads() )
393 {
394 bool doSketchPads = false;
395
396 if( !( pad->GetLayerSet() & aLayerMask ).any() )
397 {
398 if( sketchPads && ( ( onFrontFab && pad->GetLayerSet().Contains( F_Cu ) )
399 || ( onBackFab && pad->GetLayerSet().Contains( B_Cu ) ) ) )
400 {
401 doSketchPads = true;
402 }
403 else
404 {
405 continue;
406 }
407 }
408
409 if( onCopperLayer && !pad->IsOnCopperLayer() )
410 continue;
411
413 if( onCopperLayer && !pad->FlashLayer( aLayerMask ) )
414 continue;
415
416 // TODO(JE) padstacks - different behavior for single layer or multilayer
417
418 COLOR4D color = COLOR4D::BLACK;
419
420 // If we're plotting a single layer, the color for that layer can be used directly.
421 if( aLayerMask.count() == 1 )
422 {
423 color = aPlotOpt.ColorSettings()->GetColor( aLayerMask.Seq()[0] );
424 }
425 else
426 {
427 if( ( pad->GetLayerSet() & aLayerMask )[B_Cu] )
428 color = aPlotOpt.ColorSettings()->GetColor( B_Cu );
429
430 if( ( pad->GetLayerSet() & aLayerMask )[F_Cu] )
431 color = color.LegacyMix( aPlotOpt.ColorSettings()->GetColor( F_Cu ) );
432
433 if( sketchPads && aLayerMask[F_Fab] )
434 color = aPlotOpt.ColorSettings()->GetColor( F_Fab );
435 else if( sketchPads && aLayerMask[B_Fab] )
436 color = aPlotOpt.ColorSettings()->GetColor( B_Fab );
437 }
438
439 if( sketchPads && ( ( onFrontFab && pad->GetLayerSet().Contains( F_Cu ) )
440 || ( onBackFab && pad->GetLayerSet().Contains( B_Cu ) ) ) )
441 {
442 if( aPlotOpt.GetPlotPadNumbers() )
443 itemplotter.PlotPadNumber( pad, color );
444 }
445
446 auto plotPadLayer =
447 [&]( PCB_LAYER_ID aLayer )
448 {
449 VECTOR2I margin;
450 int width_adj = 0;
451
452 if( onCopperLayer )
453 width_adj = itemplotter.getFineWidthAdj();
454
455 if( onSolderMaskLayer )
456 margin.x = margin.y = pad->GetSolderMaskExpansion( aLayer );
457
458 if( onSolderPasteLayer )
459 margin = pad->GetSolderPasteMargin( aLayer );
460
461 // not all shapes can have a different margin for x and y axis
462 // in fact only oval and rect shapes can have different values.
463 // Round shape have always the same x,y margin
464 // so define a unique value for other shapes that do not support different values
465 int mask_clearance = margin.x;
466 // When clearance is same for x and y pad axis, calculations are more easy
467 bool sameXYClearance = margin.x == margin.y;
468
469 // Now offset the pad size by margin + width_adj
470 VECTOR2I padPlotsSize = pad->GetSize( aLayer ) + margin * 2 + VECTOR2I( width_adj, width_adj );
471
472 // Store these parameters that can be modified to plot inflated/deflated pads shape
473 PAD_SHAPE padShape = pad->GetShape( aLayer );
474 VECTOR2I padSize = pad->GetSize( aLayer );
475 VECTOR2I padDelta = pad->GetDelta( aLayer ); // has meaning only for trapezoidal pads
476 // CornerRadius and CornerRadiusRatio can be modified
477 // the radius is built from the ratio, so saving/restoring the ratio is enough
478 double padCornerRadiusRatio = pad->GetRoundRectRadiusRatio( aLayer );
479
480 // Don't draw a 0 sized pad.
481 // Note: a custom pad can have its pad anchor with size = 0
482 if( padShape != PAD_SHAPE::CUSTOM
483 && ( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) )
484 {
485 return;
486 }
487
488 switch( padShape )
489 {
491 case PAD_SHAPE::OVAL:
492 pad->SetSize( aLayer, padPlotsSize );
493
494 if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
496 ( pad->GetSize(aLayer ) == pad->GetDrillSize() ) &&
497 ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
498 {
499 break;
500 }
501
502 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
503 break;
504
506 pad->SetSize( aLayer, padPlotsSize );
507
508 if( mask_clearance > 0 )
509 {
510 pad->SetShape( aLayer, PAD_SHAPE::ROUNDRECT );
511 pad->SetRoundRectCornerRadius( aLayer, mask_clearance );
512 }
513
514 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
515 break;
516
518 // inflate/deflate a trapezoid is a bit complex.
519 // so if the margin is not null, build a similar polygonal pad shape,
520 // and inflate/deflate the polygonal shape
521 // because inflating/deflating using different values for y and y
522 // we are using only margin.x as inflate/deflate value
523 if( mask_clearance == 0 )
524 {
525 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
526 }
527 else
528 {
529 PAD dummy( *pad );
530 dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
531 dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
532 SHAPE_POLY_SET outline;
533 outline.NewOutline();
534 int dx = padSize.x / 2;
535 int dy = padSize.y / 2;
536 int ddx = padDelta.x / 2;
537 int ddy = padDelta.y / 2;
538
539 outline.Append( -dx - ddy, dy + ddx );
540 outline.Append( dx + ddy, dy - ddx );
541 outline.Append( dx - ddy, -dy + ddx );
542 outline.Append( -dx + ddy, -dy - ddx );
543
544 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
545 // which can create bad shapes if margin.x is < 0
547 maxError );
548 dummy.DeletePrimitivesList();
549 dummy.AddPrimitivePoly( aLayer, outline, 0, true );
550
551 // Be sure the anchor pad is not bigger than the deflated shape because this
552 // anchor will be added to the pad shape when plotting the pad. So now the
553 // polygonal shape is built, we can clamp the anchor size
554 dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
555
556 itemplotter.PlotPad( &dummy, aLayer, color, doSketchPads );
557 }
558
559 break;
560
562 {
563 // The Minkowski sum of a rounded rectangle with a disk of radius R is
564 // another rounded rectangle whose sides grow by 2R and whose corner
565 // radius grows by R. Preserving the original radius_ratio instead
566 // produces visibly inconsistent expansion at the corners (issue 24327).
567 if( sameXYClearance )
568 {
569 int originalRadius = pad->GetRoundRectCornerRadius( aLayer );
570 int newRadius = std::max( 0, originalRadius + mask_clearance + width_adj / 2 );
571 pad->SetSize( aLayer, padPlotsSize );
572 pad->SetRoundRectCornerRadius( aLayer, newRadius );
573 }
574 else
575 {
576 // Asymmetric X/Y clearance (e.g. solder paste ratio on a
577 // non-square pad) is not a Minkowski sum with a disk. Fall back
578 // to the historical behavior of scaling both axes by the per-axis
579 // margin while keeping the radius_ratio. This is approximate at
580 // the corners but preserves the bounding box, which is the
581 // dimension users rely on for paste apertures.
582 double radiusRatio = pad->GetRoundRectRadiusRatio( aLayer );
583 pad->SetSize( aLayer, padPlotsSize );
584 pad->SetRoundRectRadiusRatio( aLayer, radiusRatio );
585 }
586
587 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
588 break;
589 }
590
592 // for smaller/same rect size than initial shape (i.e. mask_clearance <= 0)
593 // use the rect with size set to padPlotsSize. It gives a good shape
594 if( mask_clearance <= 0 )
595 {
596 // the size can be slightly inflated by width_adj (PS/PDF only)
597 pad->SetSize( aLayer, padPlotsSize );
598 itemplotter.PlotPad( pad, aLayer, color, doSketchPads );
599 }
600 else
601 {
602 // Due to the polygonal shape of a CHAMFERED_RECT pad, the best way is to
603 // convert the pad shape to a full polygon and inflate it
604 // and use a dummy CUSTOM pad to plot the final shape.
605 // However one can inflate polygon only if X,Y has same inflate value
606 // if not the case, just use a rectangle having the padPlotsSize new size
607 PAD dummy( *pad );
608 // Build the dummy pad outline with coordinates relative to the pad position
609 // pad offset and orientation 0. The actual pos, offset and rotation will be
610 // taken in account later by the plot function
611 dummy.SetPosition( VECTOR2I( 0, 0 ) );
612 dummy.SetOffset( aLayer, VECTOR2I( 0, 0 ) );
613
614 if( !sameXYClearance )
615 dummy.SetSize( aLayer, padPlotsSize );
616
617 dummy.SetOrientation( ANGLE_0 );
618 SHAPE_POLY_SET outline;
619 dummy.TransformShapeToPolygon( outline, aLayer, 0, maxError, ERROR_INSIDE );
620
621 if( sameXYClearance )
623 maxError );
624
625 // Initialize the dummy pad shape:
626 dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
627 dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
628 dummy.DeletePrimitivesList();
629 dummy.AddPrimitivePoly( aLayer, outline, 0, true );
630
631 // Be sure the anchor pad is not bigger than the deflated shape because this
632 // anchor will be added to the pad shape when plotting the pad.
633 // So we set the anchor size to 0
634 dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
635 // Restore pad position and offset
636 dummy.SetPosition( pad->GetPosition() );
637 dummy.SetOffset( aLayer, pad->GetOffset( aLayer ) );
638 dummy.SetOrientation( pad->GetOrientation() );
639
640 itemplotter.PlotPad( &dummy, aLayer, color, doSketchPads );
641 }
642
643 break;
644
646 {
647 // inflate/deflate a custom shape is a bit complex.
648 // so build a similar pad shape, and inflate/deflate the polygonal shape
649 PAD dummy( *pad );
650 dummy.SetParentGroup( nullptr );
651
652 SHAPE_POLY_SET shape;
653 pad->MergePrimitivesAsPolygon( aLayer, &shape );
654
655 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
656 // which can create bad shapes if margin.x is < 0
657 shape.InflateWithLinkedHoles( mask_clearance,
659 dummy.DeletePrimitivesList();
660 dummy.AddPrimitivePoly( aLayer, shape, 0, true );
661
662 // Be sure the anchor pad is not bigger than the deflated shape because this
663 // anchor will be added to the pad shape when plotting the pad. So now the
664 // polygonal shape is built, we can clamp the anchor size
665 if( mask_clearance < 0 ) // we expect margin.x = margin.y for custom pads
666 {
667 dummy.SetSize( aLayer, VECTOR2I( std::max( 0, padPlotsSize.x ),
668 std::max( 0, padPlotsSize.y ) ) );
669 }
670
671 itemplotter.PlotPad( &dummy, aLayer, color, doSketchPads );
672 break;
673 }
674 }
675
676 // Restore the pad parameters modified by the plot code
677 pad->SetSize( aLayer, padSize );
678 pad->SetDelta( aLayer, padDelta );
679 pad->SetShape( aLayer, padShape );
680 pad->SetRoundRectRadiusRatio( aLayer, padCornerRadiusRatio );
681 };
682
683 for( PCB_LAYER_ID layer : aLayerMask.SeqStackupForPlotting() )
684 plotPadLayer( layer );
685 }
686
687 if( dnp
688 && !itemplotter.GetHideDNPFPsOnFabLayers()
689 && itemplotter.GetCrossoutDNPFPsOnFabLayers()
690 && ( ( onFrontFab && footprint->GetLayer() == F_Cu )
691 || ( onBackFab && footprint->GetLayer() == B_Cu ) ) )
692 {
693 aPlotter->MarkFootprintPlotted( footprint );
694
695 const SHAPE_POLY_SET& courtyard = footprint->GetCourtyard( footprint->GetLayer() );
696 VECTOR2I center = footprint->GetPosition();
697 EDA_ANGLE orient = footprint->GetOrientation();
698
699 // Compute a tight oriented bounding box by un-rotating the shape into the
700 // footprint's local frame, taking the axis-aligned BBox there, then rotating
701 // the four corners back into world coordinates.
702 BOX2I localRect;
703
704 if( courtyard.IsEmpty() )
705 {
706 std::shared_ptr<SHAPE> shape = footprint->GetEffectiveShape();
707 shape->Rotate( -orient, center );
708 localRect = shape->BBox();
709 }
710 else
711 {
712 SHAPE_POLY_SET temp( courtyard );
713 temp.Rotate( -orient, center );
714 localRect = temp.BBox();
715 }
716
717 VECTOR2I corner1( localRect.GetLeft(), localRect.GetTop() );
718 VECTOR2I corner2( localRect.GetRight(), localRect.GetTop() );
719 VECTOR2I corner3( localRect.GetRight(), localRect.GetBottom() );
720 VECTOR2I corner4( localRect.GetLeft(), localRect.GetBottom() );
721
722 RotatePoint( corner1, center, orient );
723 RotatePoint( corner2, center, orient );
724 RotatePoint( corner3, center, orient );
725 RotatePoint( corner4, center, orient );
726
727 int width = aBoard->GetDesignSettings().m_LineThickness[ LAYER_CLASS_FAB ];
728
729 // Use DNP cross color from color scheme
730 COLOR4D dnpMarkerColor = aPlotOpt.ColorSettings()->GetColor( LAYER_DNP_MARKER );
731
732 if( dnpMarkerColor != COLOR4D::UNSPECIFIED )
733 aPlotter->SetColor( dnpMarkerColor );
734 else
735 aPlotter->SetColor( aPlotOpt.ColorSettings()->GetColor( onFrontFab ? F_Fab : B_Fab ) );
736
737 aPlotter->ThickSegment( corner1, corner3, width, nullptr );
738 aPlotter->ThickSegment( corner2, corner4, width, nullptr );
739 }
740
741 aPlotter->EndBlock( nullptr );
742 }
743
744 // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true,
745
746 GBR_METADATA gbr_metadata;
747
748 if( onCopperLayer )
749 {
752 }
753
754 auto getMetadata =
755 [&]()
756 {
757 if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER )
758 return (void*) &gbr_metadata;
759 else if( aPlotter->GetPlotterType() == PLOT_FORMAT::DXF )
760 return (void*) &aPlotOpt;
761 else
762 return (void*) nullptr;
763 };
764
765 aPlotter->StartBlock( nullptr );
766
767 for( const PCB_TRACK* track : aBoard->Tracks() )
768 {
769 if( track->Type() != PCB_VIA_T )
770 continue;
771
772 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
773
774 // vias are not plotted if not on selected layer
775 LSET via_mask_layer = via->GetLayerSet();
776
777 if( !( via_mask_layer & aLayerMask ).any() )
778 continue;
779
780 int via_margin = 0;
781 double width_adj = 0;
782
783 // TODO(JE) padstacks - separate top/bottom margin
784 if( onSolderMaskLayer )
785 via_margin = via->GetSolderMaskExpansion();
786
787 if( ( aLayerMask & LSET::AllCuMask() ).any() )
788 width_adj = itemplotter.getFineWidthAdj();
789
791 if( onCopperLayer && !via->FlashLayer( aLayerMask ) )
792 continue;
793
794 int diameter = 0;
795
796 for( PCB_LAYER_ID layer : aLayerMask )
797 diameter = std::max( diameter, via->GetWidth( layer ) );
798
799 diameter += 2 * via_margin + width_adj;
800
801 // Don't draw a null size item :
802 if( diameter <= 0 )
803 continue;
804
805 // Some vias can be not connected (no net).
806 // Set the m_NotInNet for these vias to force a empty net name in gerber file
807 gbr_metadata.m_NetlistMetadata.m_NotInNet = via->GetNetname().IsEmpty();
808
809 gbr_metadata.SetNetName( via->GetNetname() );
810
811 COLOR4D color;
812
813 // If we're plotting a single layer, the color for that layer can be used directly.
814 if( aLayerMask.count() == 1 )
815 color = aPlotOpt.ColorSettings()->GetColor( aLayerMask.Seq()[0] );
816 else
817 color = aPlotOpt.ColorSettings()->GetColor( LAYER_VIAS + static_cast<int>( via->GetViaType() ) );
818
819 // Change UNSPECIFIED or WHITE to LIGHTGRAY because the white items are not seen on a
820 // white paper or screen
821 if( color == COLOR4D::UNSPECIFIED || color == WHITE )
822 color = LIGHTGRAY;
823
824 aPlotter->SetColor( color );
825 aPlotter->FlashPadCircle( via->GetStart(), diameter, getMetadata() );
826 }
827
828 aPlotter->EndBlock( nullptr );
829 aPlotter->StartBlock( nullptr );
830
831 if( onCopperLayer )
832 {
835 }
836 else
837 {
838 // Reset attributes if non-copper (soldermask) layer
841 }
842
843 // Plot tracks (not vias) :
844 for( const PCB_TRACK* track : aBoard->Tracks() )
845 {
846 if( track->Type() == PCB_VIA_T )
847 continue;
848
849 if( !( aLayerMask & track->GetLayerSet() ).any() )
850 continue;
851
852 // Some track segments can be not connected (no net).
853 // Set the m_NotInNet for these segments to force a empty net name in gerber file
854 gbr_metadata.m_NetlistMetadata.m_NotInNet = track->GetNetname().IsEmpty();
855
856 gbr_metadata.SetNetName( track->GetNetname() );
857
858 int margin = 0;
859
860 if( onSolderMaskLayer )
861 margin = track->GetSolderMaskExpansion();
862
863 int width = track->GetWidth() + 2 * margin + itemplotter.getFineWidthAdj();
864
865 aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
866
867 if( track->Type() == PCB_ARC_T )
868 {
869 const PCB_ARC* arc = static_cast<const PCB_ARC*>( track );
870
871 // Too small arcs cannot be really handled: arc center (and arc radius)
872 // cannot be safely computed
873 if( !arc->IsDegenerated( 10 /* in IU */ ) )
874 {
875 aPlotter->ThickArc( arc->GetCenter(), arc->GetArcAngleStart(), arc->GetAngle(),
876 arc->GetRadius(), width, getMetadata() );
877 }
878 else
879 {
880 // Approximate this very small arc by a segment.
881 aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, getMetadata() );
882 }
883 }
884 else
885 {
886 aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, getMetadata() );
887 }
888 }
889
890 aPlotter->EndBlock( nullptr );
891
892 // Plot filled ares
893 aPlotter->StartBlock( nullptr );
894
895 NETINFO_ITEM nonet( aBoard );
896
897 for( const ZONE* zone : aBoard->Zones() )
898 {
899 if( zone->GetIsRuleArea() )
900 continue;
901
902 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
903 {
904 if( !aLayerMask[layer] )
905 continue;
906
907 SHAPE_POLY_SET mainArea = zone->GetFilledPolysList( layer )->CloneDropTriangulation();
908 SHAPE_POLY_SET islands;
909
910 for( int i = mainArea.OutlineCount() - 1; i >= 0; i-- )
911 {
912 if( zone->IsIsland( layer, i ) )
913 {
914 islands.AddOutline( mainArea.CPolygon( i )[0] );
915 mainArea.DeletePolygon( i );
916 }
917 }
918
919 itemplotter.PlotZone( zone, layer, mainArea );
920
921 if( !islands.IsEmpty() )
922 {
923 ZONE dummy( *zone );
924 dummy.SetNet( &nonet );
925 itemplotter.PlotZone( &dummy, layer, islands );
926 }
927 }
928 }
929
930 aPlotter->EndBlock( nullptr );
931}
932
933
937void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, const LSET& aLayerMask,
938 const PCB_PLOT_PARAMS& aPlotOpt )
939{
940 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
941 itemplotter.SetLayerSet( aLayerMask );
942
943 int smallDrill = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_SmallDrillMarkSize );
944
945 SHAPE_POLY_SET outlines;
946
947 for( PCB_LAYER_ID layer : aLayerMask.Seq( aLayerMask.SeqStackupForPlotting() ) )
948 {
949 outlines.RemoveAllContours();
950 aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines, aPlotter->RenderSettings() );
951
952 outlines.Simplify();
953
954 // Plot outlines
955 std::vector<VECTOR2I> cornerList;
956
957 // Now we have one or more basic polygons: plot each polygon
958 for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
959 {
960 for( int kk = 0; kk <= outlines.HoleCount(ii); kk++ )
961 {
962 cornerList.clear();
963 const SHAPE_LINE_CHAIN& path = ( kk == 0 ) ? outlines.COutline( ii )
964 : outlines.CHole( ii, kk - 1 );
965
967 }
968 }
969
970 // Plot pad holes
972 {
973 for( FOOTPRINT* footprint : aBoard->Footprints() )
974 {
975 for( PAD* pad : footprint->Pads() )
976 {
977 if( pad->HasHole() )
978 {
979 if( pad->GetDrillSize().x == pad->GetDrillSize().y )
980 {
981 int drill = pad->GetDrillSize().x;
982
984 drill = std::min( smallDrill, drill );
985
986 aPlotter->ThickCircle( pad->ShapePos( layer ), drill,
988 }
989 else
990 {
991 // Note: small drill marks have no significance when applied to slots
992
993 aPlotter->ThickOval( pad->ShapePos( layer ), pad->GetSize( layer ),
994 pad->GetOrientation(), PLOTTER::USE_DEFAULT_LINE_WIDTH,
995 nullptr );
996 }
997 }
998 }
999 }
1000 }
1001
1002 // Plot vias holes
1003 for( PCB_TRACK* track : aBoard->Tracks() )
1004 {
1005 if( track->Type() != PCB_VIA_T )
1006 continue;
1007
1008 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1009
1010 if( via->GetLayerSet().Contains( layer ) ) // via holes can be not through holes
1011 {
1012 aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), FILL_T::NO_FILL,
1014 }
1015 }
1016 }
1017}
1018
1019
1023void GenerateLayerPoly( SHAPE_POLY_SET* aResult, BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
1024 bool aPlotFPText, bool aPlotReferences, bool aPlotValues )
1025{
1026 int maxError = aBoard->GetDesignSettings().m_MaxError;
1027 SHAPE_POLY_SET buffer;
1028 int inflate = 0;
1029
1030 if( aLayer == F_Mask || aLayer == B_Mask )
1031 {
1032 // We remove 1nm as we expand both sides of the shapes, so allowing for a strictly greater
1033 // than or equal comparison in the shape separation (boolean add)
1034 inflate = aBoard->GetDesignSettings().m_SolderMaskMinWidth / 2 - 1;
1035 }
1036
1037 // Build polygons for each pad shape. The size of the shape on solder mask should be size
1038 // of pad + clearance around the pad, where clearance = solder mask clearance + extra margin.
1039 // Extra margin is half the min width for solder mask, which is used to merge too-close shapes
1040 // (distance < SolderMaskMinWidth).
1041
1042 // Will contain exact shapes of all items on solder mask. We add this back in at the end just
1043 // to make sure that any artefacts introduced by the inflate/deflate don't remove parts of the
1044 // individual shapes.
1045 SHAPE_POLY_SET exactPolys;
1046
1047 // Plot a non filled basic PCB_SHAPE, according to the line style
1048 auto handlePcbShape =
1049 [&]( const PCB_SHAPE& aPcbShape )
1050 {
1051 LINE_STYLE lineStyle = aPcbShape.GetStroke().GetLineStyle();
1052
1053 if( lineStyle <= LINE_STYLE::FIRST_TYPE )
1054 {
1055 if( inflate != 0 )
1056 aPcbShape.TransformShapeToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE );
1057
1058 aPcbShape.TransformShapeToPolySet( *aResult, aLayer, inflate, maxError, ERROR_OUTSIDE );
1059 }
1060 else
1061 {
1062 int thickness = aPcbShape.GetWidth();
1063 std::vector<SHAPE*> shapes = aPcbShape.MakeEffectiveShapesForStroking( thickness );
1064 int minSegCount = 8;
1065
1066 for( SHAPE* shape : shapes )
1067 {
1068 STROKE_PARAMS::Stroke( shape, lineStyle, thickness, aPlotter->RenderSettings(),
1069 [&]( const VECTOR2I& a, const VECTOR2I& b )
1070 {
1071 if( inflate != 0 )
1072 {
1073 TransformOvalToPolygon( exactPolys, a, b,
1074 thickness, maxError, ERROR_OUTSIDE, minSegCount );
1075 }
1076
1077 TransformOvalToPolygon( *aResult, a, b,
1078 thickness+inflate, maxError, ERROR_OUTSIDE, minSegCount );
1079 } );
1080 }
1081
1082 for( SHAPE* shape : shapes )
1083 delete shape;
1084 }
1085 };
1086
1087 auto handleFPTextItem =
1088 [&]( const PCB_TEXT& aText )
1089 {
1090 if( !aPlotFPText )
1091 return;
1092
1093 if( aText.GetText() == wxT( "${REFERENCE}" ) && !aPlotReferences )
1094 return;
1095
1096 if( aText.GetText() == wxT( "${VALUE}" ) && !aPlotValues )
1097 return;
1098
1099 if( inflate != 0 )
1100 aText.TransformTextToPolySet( exactPolys, 0, maxError, ERROR_OUTSIDE );
1101
1102 aText.TransformTextToPolySet( *aResult, inflate, maxError, ERROR_OUTSIDE );
1103 };
1104
1105 auto handleTextBoxItem =
1106 [&]( const PCB_TEXTBOX& aTextBox )
1107 {
1108 if( inflate != 0 )
1109 aTextBox.TransformTextToPolySet( exactPolys, 0, maxError, ERROR_OUTSIDE );
1110
1111 aTextBox.TransformTextToPolySet( *aResult, inflate, maxError, ERROR_OUTSIDE );
1112
1113 // plot box outline around text
1114 if( aTextBox.IsBorderEnabled() && !aTextBox.IsKnockout() )
1115 {
1116 PCB_SHAPE shapebox = aTextBox.GetPolygonalBoxShape();
1117 handlePcbShape( shapebox );
1118 }
1119 };
1120
1121 // Generate polygons with arcs inside the shape or exact shape to minimize shape changes
1122 // created by arc to segment size correction.
1124 {
1125 // Plot footprint pads and graphics
1126 for( const FOOTPRINT* footprint : aBoard->Footprints() )
1127 {
1128 if( inflate != 0 )
1129 footprint->TransformPadsToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE );
1130
1131 int outlinesBefore = aResult->OutlineCount();
1132
1133 footprint->TransformPadsToPolySet( *aResult, aLayer, inflate, maxError, ERROR_OUTSIDE );
1134
1135 if( aResult->OutlineCount() > outlinesBefore )
1136 aPlotter->MarkFootprintPlotted( footprint );
1137
1138 for( const PCB_FIELD* field : footprint->GetFields() )
1139 {
1140 wxCHECK2( field, continue );
1141
1142 if( field->IsReference() && !aPlotReferences )
1143 continue;
1144
1145 if( field->IsValue() && !aPlotValues )
1146 continue;
1147
1148 if( field->IsVisible() && field->IsOnLayer( aLayer ) )
1149 {
1150 aPlotter->MarkFootprintPlotted( footprint );
1151 handleFPTextItem( static_cast<const PCB_TEXT&>( *field ) );
1152 }
1153 }
1154
1155 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
1156 {
1157 if( item->IsOnLayer( aLayer ) )
1158 {
1159 aPlotter->MarkFootprintPlotted( footprint );
1160
1161 if( item->Type() == PCB_TEXT_T )
1162 {
1163 handleFPTextItem( static_cast<const PCB_TEXT&>( *item ) );
1164 }
1165 else if( item->Type() == PCB_TEXTBOX_T )
1166 {
1167 handleTextBoxItem( static_cast<const PCB_TEXTBOX&>( *item ) );
1168 }
1169 else
1170 {
1171 if( const PCB_SHAPE* pcb_shape = dynamic_cast< const PCB_SHAPE*>( item ) )
1172 {
1173 handlePcbShape( *pcb_shape );
1174 }
1175 else // Shold not occur
1176 {
1177 if( inflate != 0 )
1178 item->TransformShapeToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE );
1179
1180 item->TransformShapeToPolySet( *aResult, aLayer, inflate, maxError, ERROR_OUTSIDE );
1181 }
1182 }
1183 }
1184 }
1185 }
1186
1187 // Plot untented vias and tracks
1188 for( const PCB_TRACK* track : aBoard->Tracks() )
1189 {
1190 // Note: IsOnLayer() checks relevant mask layers of untented vias and tracks
1191 if( !track->IsOnLayer( aLayer ) )
1192 continue;
1193
1194 int clearance = track->GetSolderMaskExpansion();
1195
1196 if( inflate != 0 )
1197 track->TransformShapeToPolygon( exactPolys, aLayer, clearance, maxError, ERROR_OUTSIDE );
1198
1199 track->TransformShapeToPolygon( *aResult, aLayer, clearance + inflate, maxError, ERROR_OUTSIDE );
1200 }
1201
1202 for( const BOARD_ITEM* item : aBoard->Drawings() )
1203 {
1204 if( item->IsOnLayer( aLayer ) )
1205 {
1206 if( item->Type() == PCB_TEXT_T )
1207 {
1208 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
1209
1210 if( inflate != 0 )
1211 text->TransformTextToPolySet( exactPolys, 0, maxError, ERROR_OUTSIDE );
1212
1213 text->TransformTextToPolySet( *aResult, inflate, maxError, ERROR_OUTSIDE );
1214 }
1215 else if( item->Type() == PCB_TEXTBOX_T )
1216 {
1217 handleTextBoxItem( static_cast<const PCB_TEXTBOX&>( *item ) );
1218 }
1219 else
1220 {
1221 if( const PCB_SHAPE* pcb_shape = dynamic_cast< const PCB_SHAPE*>( item ) )
1222 {
1223 handlePcbShape( *pcb_shape );
1224 }
1225 else // Shold not occur
1226 {
1227 if( inflate != 0 )
1228 {
1229 item->TransformShapeToPolySet( exactPolys, aLayer, 0, maxError, ERROR_OUTSIDE,
1230 aPlotter->RenderSettings() );
1231 }
1232
1233 item->TransformShapeToPolySet( *aResult, aLayer, inflate, maxError, ERROR_OUTSIDE,
1234 aPlotter->RenderSettings() );
1235 }
1236 }
1237 }
1238 }
1239
1240 // Add filled zone areas.
1241 for( ZONE* zone : aBoard->Zones() )
1242 {
1243 if( zone->GetIsRuleArea() )
1244 continue;
1245
1246 if( !zone->IsOnLayer( aLayer ) )
1247 continue;
1248
1249 SHAPE_POLY_SET* fillData = zone->GetFill( aLayer );
1250
1251 if( !fillData )
1252 continue;
1253
1254 SHAPE_POLY_SET area = *fillData;
1255
1256 if( inflate != 0 )
1257 exactPolys.Append( area );
1258
1259 area.Inflate( inflate, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
1260 aResult->Append( area );
1261 }
1262 }
1263
1264 // Merge all polygons
1265 aResult->Simplify();
1266
1267 if( inflate != 0 )
1268 {
1269 aResult->Deflate( inflate, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
1270 // Add back in the exact polys. This is mandatory because inflate/deflate transform is
1271 // not perfect, and we want the initial areas perfectly kept.
1272 aResult->BooleanAdd( exactPolys );
1273 }
1274#undef ERROR
1275}
1276
1277
1284static void initializePlotter( PLOTTER* aPlotter, const BOARD* aBoard, const PCB_PLOT_PARAMS* aPlotOpts )
1285{
1286 PAGE_INFO pageA4( PAGE_SIZE_TYPE::A4 );
1287 const PAGE_INFO& pageInfo = aBoard->GetPageSettings();
1288 const PAGE_INFO* sheet_info;
1289 double paperscale; // Page-to-paper ratio
1290 VECTOR2I paperSizeIU;
1291 VECTOR2I pageSizeIU( pageInfo.GetSizeIU( pcbIUScale.IU_PER_MILS ) );
1292 bool autocenter = false;
1293
1294 // Special options: to fit the sheet to an A4 sheet replace the paper size. However there
1295 // is a difference between the autoscale and the a4paper option:
1296 // - Autoscale fits the board to the paper size
1297 // - A4paper fits the original paper size to an A4 sheet
1298 // - Both of them fit the board to an A4 sheet
1299 if( aPlotOpts->GetA4Output() )
1300 {
1301 sheet_info = &pageA4;
1302 paperSizeIU = pageA4.GetSizeIU( pcbIUScale.IU_PER_MILS );
1303 paperscale = (double) paperSizeIU.x / pageSizeIU.x;
1304 autocenter = true;
1305 }
1306 else
1307 {
1308 sheet_info = &pageInfo;
1309 paperSizeIU = pageSizeIU;
1310 paperscale = 1;
1311
1312 // Need autocentering only if scale is not 1:1
1313 autocenter = (aPlotOpts->GetScale() != 1.0) || aPlotOpts->GetAutoScale();
1314 }
1315
1316 BOX2I bbox = aBoard->ComputeBoundingBox( false, false );
1317 VECTOR2I boardCenter = bbox.Centre();
1318 VECTOR2I boardSize = bbox.GetSize();
1319
1320 double compound_scale;
1321
1322 // Fit to 80% of the page if asked; it could be that the board is empty, in this case
1323 // regress to 1:1 scale
1324 if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
1325 {
1326 double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
1327 double yscale = (paperSizeIU.y * 0.8) / boardSize.y;
1328
1329 compound_scale = std::min( xscale, yscale ) * paperscale;
1330 }
1331 else
1332 {
1333 compound_scale = aPlotOpts->GetScale() * paperscale;
1334 }
1335
1336 // For the plot offset we have to keep in mind the auxiliary origin too: if autoscaling is
1337 // off we check that plot option (i.e. autoscaling overrides auxiliary origin)
1338 VECTOR2I offset( 0, 0);
1339
1340 if( autocenter )
1341 {
1342 offset.x = KiROUND( boardCenter.x - ( paperSizeIU.x / 2.0 ) / compound_scale );
1343 offset.y = KiROUND( boardCenter.y - ( paperSizeIU.y / 2.0 ) / compound_scale );
1344 }
1345 else
1346 {
1347 if( aPlotOpts->GetUseAuxOrigin() )
1348 offset = aBoard->GetDesignSettings().GetAuxOrigin();
1349 }
1350
1351 aPlotter->SetPageSettings( *sheet_info );
1352
1353 aPlotter->SetViewport( offset, pcbIUScale.IU_PER_MILS/10, compound_scale, aPlotOpts->GetMirror() );
1354
1355 // For SVG fit-to-board plots the page is the board bounding box and the origin is at
1356 // (0,0), so the SVG viewBox must be that bounding box (it can extend to negative
1357 // coordinates relative to the origin, and the origin doesn't need to be on the page).
1358 if( aPlotOpts->GetFormat() == PLOT_FORMAT::SVG && aPlotOpts->GetSvgFitPagetoBoard() )
1359 aPlotter->SetPlotBBox( bbox );
1360
1361 // Has meaning only for gerber plotter. Must be called only after SetViewport
1362 aPlotter->SetGerberCoordinatesFormat( aPlotOpts->GetGerberPrecision() );
1363
1364 // Has meaning only for SVG plotter. Must be called only after SetViewport
1365 aPlotter->SetSvgCoordinatesFormat( aPlotOpts->GetSvgPrecision() );
1366
1367 aPlotter->SetCreator( wxT( "PCBNEW" ) );
1368 aPlotter->SetColorMode( !aPlotOpts->GetBlackAndWhite() ); // default is plot in Black and White.
1369 aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
1370}
1371
1372
1376static void FillNegativeKnockout( PLOTTER *aPlotter, const BOX2I &aBbbox )
1377{
1378 const int margin = 5 * pcbIUScale.IU_PER_MM; // Add a 5 mm margin around the board
1379 aPlotter->SetNegative( true );
1380 aPlotter->SetColor( WHITE ); // Which will be plotted as black
1381
1382 BOX2I area = aBbbox;
1383 area.Inflate( margin );
1384 aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILL_T::FILLED_SHAPE, 0, 0 );
1385 aPlotter->SetColor( BLACK );
1386}
1387
1388
1389static void plotBackgroundColor( BOARD* aBoard, const PCB_PLOT_PARAMS* aPlotOpts, PLOTTER* aPlotter )
1390{
1391 const PAGE_INFO& pageInfo = aPlotter->PageSettings();
1392 const VECTOR2I plotOffset = aPlotter->GetPlotOffsetUserUnits();
1393 const VECTOR2I pageSizeIU( pageInfo.GetWidthIU( pcbIUScale.IU_PER_MILS ),
1394 pageInfo.GetHeightIU( pcbIUScale.IU_PER_MILS ) );
1395
1396 if( aPlotter->GetColorMode()
1397 && aPlotOpts->GetBackgroundColor() != COLOR4D::UNSPECIFIED )
1398 {
1399 aPlotter->SetColor( aPlotOpts->GetBackgroundColor() );
1400
1401 // Undo the viewport scale so the background fills the physical page.
1402 VECTOR2I end = plotOffset + pageSizeIU / aPlotter->GetPlotScale();
1403
1404 aPlotter->Rect( plotOffset, end, FILL_T::FILLED_SHAPE, 1.0 );
1405 }
1406}
1407
1408
1415PLOTTER* StartPlotBoard( BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aLayer,
1416 const wxString& aLayerName, const wxString& aFullFileName,
1417 const wxString& aSheetName, const wxString& aSheetPath,
1418 const wxString& aPageName, const wxString& aPageNumber,
1419 const int aPageCount )
1420{
1421 wxCHECK( aBoard && aPlotOpts, nullptr );
1422
1423 // Create the plotter driver and set the few plotter specific options
1424 PLOTTER* plotter = nullptr;
1425
1426 switch( aPlotOpts->GetFormat() )
1427 {
1428 case PLOT_FORMAT::DXF:
1429 DXF_PLOTTER* DXF_plotter;
1430 DXF_plotter = new DXF_PLOTTER();
1431 DXF_plotter->SetUnits( aPlotOpts->GetDXFPlotUnits() );
1432
1433 plotter = DXF_plotter;
1434
1435 if( !aPlotOpts->GetLayersToExport().empty() )
1436 plotter->SetLayersToExport( aPlotOpts->GetLayersToExport() );
1437 break;
1438
1439 case PLOT_FORMAT::POST:
1440 PS_PLOTTER* PS_plotter;
1441 PS_plotter = new PS_PLOTTER();
1442 PS_plotter->SetScaleAdjust( aPlotOpts->GetFineScaleAdjustX(),
1443 aPlotOpts->GetFineScaleAdjustY() );
1444 plotter = PS_plotter;
1445 break;
1446
1447 case PLOT_FORMAT::PDF:
1448 plotter = new PDF_PLOTTER( aBoard->GetProject() );
1449 break;
1450
1451 case PLOT_FORMAT::HPGL:
1452 wxLogError( _( "HPGL plotting is no longer supported as of KiCad 10.0" ) );
1453 return nullptr;
1454
1456 // For Gerber plotter, a valid board layer must be set, in order to create a valid
1457 // Gerber header, especially the TF.FileFunction and .FilePolarity data
1458 if( aLayer < PCBNEW_LAYER_ID_START || aLayer >= PCB_LAYER_ID_COUNT )
1459 {
1460 wxLogError( wxString::Format( "Invalid board layer %d, cannot build a valid Gerber file header",
1461 aLayer ) );
1462 }
1463
1464 plotter = new GERBER_PLOTTER();
1465 break;
1466
1467 case PLOT_FORMAT::SVG:
1468 plotter = new SVG_PLOTTER();
1469 break;
1470
1471 case PLOT_FORMAT::PNG:
1472 {
1473 PNG_PLOTTER* pngPlotter = new PNG_PLOTTER();
1474
1475 PAGE_INFO pageInfo = aBoard->GetPageSettings();
1476 VECTOR2D sizeIU = pageInfo.GetSizeIU( pcbIUScale.IU_PER_MILS );
1477 int dpi = aPlotOpts->GetPngDPI();
1478 double iuPerInch = pcbIUScale.IU_PER_MILS * 1000.0;
1479
1480 pngPlotter->SetPixelSize( KiROUND( sizeIU.x * dpi / iuPerInch ),
1481 KiROUND( sizeIU.y * dpi / iuPerInch ) );
1482 pngPlotter->SetResolution( dpi );
1483 pngPlotter->SetAntialias( aPlotOpts->GetPngAntialias() );
1484 plotter = pngPlotter;
1485 break;
1486 }
1487
1488 default:
1489 wxASSERT( false );
1490 return nullptr;
1491 }
1492
1494 renderSettings->LoadColors( aPlotOpts->ColorSettings() );
1495 renderSettings->SetDefaultPenWidth( pcbIUScale.mmToIU( 0.0212 ) ); // Hairline at 1200dpi
1496 renderSettings->SetLayerName( aLayerName );
1497 renderSettings->SetDashLengthRatio( aPlotOpts->GetDashedLineDashRatio() );
1498 renderSettings->SetGapLengthRatio( aPlotOpts->GetDashedLineGapRatio() );
1499
1500 plotter->SetRenderSettings( renderSettings );
1501
1502 // Compute the viewport and set the other options
1503
1504 // page layout is not mirrored, so temporarily change mirror option for the page layout
1505 PCB_PLOT_PARAMS plotOpts = *aPlotOpts;
1506
1507 if( plotOpts.GetPlotFrameRef() )
1508 {
1509 if( plotOpts.GetMirror() )
1510 plotOpts.SetMirror( false );
1511 if( plotOpts.GetScale() != 1.0 )
1512 plotOpts.SetScale( 1.0 );
1513 if( plotOpts.GetAutoScale() )
1514 plotOpts.SetAutoScale( false );
1515 }
1516
1517 initializePlotter( plotter, aBoard, &plotOpts );
1518
1519 if( plotter->OpenFile( aFullFileName ) )
1520 {
1521 plotter->ClearHeaderLinesList();
1522
1523 // For the Gerber "file function" attribute, set the layer number
1524 if( plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
1525 {
1526 bool useX2mode = plotOpts.GetUseGerberX2format();
1527
1528 GERBER_PLOTTER* gbrplotter = static_cast <GERBER_PLOTTER*> ( plotter );
1529 gbrplotter->DisableApertMacros( plotOpts.GetDisableGerberMacros() );
1530 gbrplotter->UseX2format( useX2mode );
1531 gbrplotter->UseX2NetAttributes( plotOpts.GetIncludeGerberNetlistInfo() );
1532
1533 // Attributes can be added using X2 format or as comment (X1 format)
1534 AddGerberX2Attribute( plotter, aBoard, aLayer, not useX2mode );
1535 }
1536
1537 bool startPlotSuccess = false;
1538 try
1539 {
1540 if( plotter->GetPlotterType() == PLOT_FORMAT::PDF )
1541 startPlotSuccess = static_cast<PDF_PLOTTER*>( plotter )->StartPlot( aPageNumber, aPageName );
1542 else
1543 startPlotSuccess = plotter->StartPlot( aPageName );
1544 }
1545 catch( ... )
1546 {
1547 startPlotSuccess = false;
1548 }
1549
1550
1551 if( startPlotSuccess )
1552 {
1553 if( aPlotOpts->GetFormat() == PLOT_FORMAT::PDF
1554 || aPlotOpts->GetFormat() == PLOT_FORMAT::PNG )
1555 {
1556 plotBackgroundColor( aBoard, aPlotOpts, plotter );
1557 }
1558
1559 // Plot the frame reference if requested
1560 if( aPlotOpts->GetPlotFrameRef() )
1561 {
1562 wxString variantName = aBoard->GetCurrentVariant();
1563 wxString variantDesc = aBoard->GetVariantDescription( variantName );
1564
1565 PlotDrawingSheet( plotter, aBoard->GetProject(), aBoard->GetTitleBlock(), aBoard->GetPageSettings(),
1566 &aBoard->GetProperties(), aPageNumber, aPageCount, aSheetName, aSheetPath,
1567 aBoard->GetFileName(), renderSettings->GetLayerColor( LAYER_DRAWINGSHEET ), true,
1568 variantName, variantDesc );
1569
1570 if( aPlotOpts->GetMirror() || aPlotOpts->GetScale() != 1.0 || aPlotOpts->GetAutoScale() )
1571 initializePlotter( plotter, aBoard, aPlotOpts );
1572 }
1573
1574 // When plotting a negative board: draw a black rectangle (background for plot board
1575 // in white) and switch the current color to WHITE; note the color inversion is actually
1576 // done in the driver (if supported)
1577 if( aPlotOpts->GetNegative() )
1578 {
1579 BOX2I bbox = aBoard->ComputeBoundingBox( false, false );
1580 FillNegativeKnockout( plotter, bbox );
1581 }
1582
1583 return plotter;
1584 }
1585 }
1586
1587 delete plotter->RenderSettings();
1588 delete plotter;
1589 return nullptr;
1590}
1591
1592
1593void setupPlotterNewPDFPage( PLOTTER* aPlotter, BOARD* aBoard, PCB_PLOT_PARAMS* aPlotOpts,
1594 const wxString& aLayerName, const wxString& aSheetName,
1595 const wxString& aSheetPath, const wxString& aPageNumber,
1596 int aPageCount )
1597{
1598 plotBackgroundColor( aBoard, aPlotOpts, aPlotter );
1599
1600 aPlotter->RenderSettings()->SetLayerName( aLayerName );
1601
1602 // Plot the frame reference if requested
1603 if( aPlotOpts->GetPlotFrameRef() )
1604 {
1605 // Mirror and scale shouldn't be applied to the drawing sheet
1606 bool revertOps = false;
1607 bool oldMirror = aPlotOpts->GetMirror();
1608 bool oldAutoScale = aPlotOpts->GetAutoScale();
1609 double oldScale = aPlotOpts->GetScale();
1610
1611 if( oldMirror || oldAutoScale || oldScale != 1.0 )
1612 {
1613 aPlotOpts->SetMirror( false );
1614 aPlotOpts->SetScale( 1.0 );
1615 aPlotOpts->SetAutoScale( false );
1616 initializePlotter( aPlotter, aBoard, aPlotOpts );
1617 revertOps = true;
1618 }
1619
1620 wxString variantName = aBoard->GetCurrentVariant();
1621 wxString variantDesc = aBoard->GetVariantDescription( variantName );
1622
1623 PlotDrawingSheet( aPlotter, aBoard->GetProject(), aBoard->GetTitleBlock(), aBoard->GetPageSettings(),
1624 &aBoard->GetProperties(), aPageNumber, aPageCount, aSheetName, aSheetPath,
1625 aBoard->GetFileName(), aPlotter->RenderSettings()->GetLayerColor( LAYER_DRAWINGSHEET ), true,
1626 variantName, variantDesc );
1627
1628 if( revertOps )
1629 {
1630 aPlotOpts->SetMirror( oldMirror );
1631 aPlotOpts->SetScale( oldScale );
1632 aPlotOpts->SetAutoScale( oldAutoScale );
1633 initializePlotter( aPlotter, aBoard, aPlotOpts );
1634 }
1635 }
1636}
@ 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:85
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
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:4084
const PAGE_INFO & GetPageSettings() const
Definition board.h:1020
const ZONES & Zones() const
Definition board.h:469
const LSET & DrillSymbolLayers() const
Layers that currently have a drill map on them.
Definition board.h:613
TITLE_BLOCK & GetTitleBlock()
Definition board.h:1026
const std::map< wxString, wxString > & GetProperties() const
Definition board.h:526
const FOOTPRINTS & Footprints() const
Definition board.h:465
const TRACKS & Tracks() const
Definition board.h:463
const wxString & GetFileName() const
Definition board.h:454
wxString GetVariantDescription(const wxString &aVariantName) const
Definition board.cpp:3125
wxString GetCurrentVariant() const
Definition board.h:530
PROJECT * GetProject() const
Definition board.h:774
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2538
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
const DRAWINGS & Drawings() const
Definition board.h:467
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 PlotFootprintGraphicItems(const FOOTPRINT *aFootprint) const
void PlotBoardGraphicItem(const BOARD_ITEM *item) const
Plot items like text and graphics but not tracks and footprints.
void PlotPadNumber(const PAD *aPad, const COLOR4D &aColor) const
void PlotDrillSymbols(PCB_LAYER_ID aLayer) const
Draw each hole's chart symbol on aLayer, from the board's shared symbol profile.
void PlotZone(const ZONE *aZone, PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList) const
void PlotFootprintTextItems(const FOOTPRINT *aFootprint) const
COLOR4D getColor(int aLayer) const
White color is special because it cannot be seen on a white paper in B&W mode.
void PlotDrillMarks() const
Draw a drill mark for pads and vias.
int getFineWidthAdj() const
Definition pcbplot.h:75
void PlotPad(const PAD *aPad, PCB_LAYER_ID aLayer, const COLOR4D &aColor, bool aSketchMode) const
Plot a pad.
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:94
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
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h:307
double GetRadius() const
EDA_ANGLE GetAngle() const
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:138
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:158
virtual void SetSvgCoordinatesFormat(unsigned aPrecision)
Set the number of digits for mantissa in coordinates in mm for SVG plotter.
Definition plotter.h:598
void MarkFootprintPlotted(const FOOTPRINT *aFootprint)
Record that aFootprint was drawn on the page being plotted, so that hyperlinks and bookmarks can cove...
Definition plotter.h:263
virtual void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition plotter.h:171
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition plotter.h:168
static const int USE_DEFAULT_LINE_WIDTH
Definition plotter.h:142
double GetPlotScale() const
Definition plotter.h:640
virtual void ThickOval(const VECTOR2I &aPos, const VECTOR2I &aSize, const EDA_ANGLE &aOrient, int aWidth, void *aData)
Definition plotter.cpp:389
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:236
RENDER_SETTINGS * RenderSettings()
Definition plotter.h:169
void ClearPlottedFootprints()
Definition plotter.h:270
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false)
Definition plotter.h:592
virtual void Bookmark(const BOX2I &aBox, const wxString &aName, const wxString &aGroupName=wxEmptyString)
Create a bookmark to a symbol.
Definition plotter.h:548
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:484
virtual void SetTextMode(PLOT_TEXT_MODE mode)
Change the current text mode.
Definition plotter.h:587
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:190
VECTOR2I GetPlotOffsetUserUnits()
Definition plotter.h:635
void ClearHeaderLinesList()
Remove all lines from the list of free lines to print at the beginning of the file.
Definition plotter.h:208
bool GetColorMode() const
Definition plotter.h:166
PAGE_INFO & PageSettings()
Definition plotter.h:172
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:165
virtual void StartBlock(void *aData)
calling this function allows one to define the beginning of a group of drawing items,...
Definition plotter.h:621
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, void *aData)
Definition plotter.cpp:442
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:609
virtual void HyperlinkMenu(const BOX2I &aBox, const std::vector< wxString > &aDestURLs)
Create a clickable hyperlink menu with a rectangular click area.
Definition plotter.h:536
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:577
bool WasFootprintPlotted(const FOOTPRINT *aFootprint) const
Definition plotter.h:265
virtual void ThickCircle(const VECTOR2I &pos, int diametre, int width, void *aData)
Definition plotter.cpp:515
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:630
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:675
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:93
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:741
@ 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:506
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.) for the footprints the page drew.
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