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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <wx/log.h>
25#include <eda_item.h>
26#include <layer_ids.h>
27#include <lset.h>
30#include <pcb_base_frame.h>
31#include <math/util.h> // for KiROUND
32#include <board.h>
33#include <footprint.h>
34#include <pcb_track.h>
35#include <pad.h>
36#include <zone.h>
37#include <pcb_shape.h>
38#include <pcb_target.h>
39#include <pcb_dimension.h>
40#include <pcbplot.h>
45#include <pcb_painter.h>
46#include <gbr_metadata.h>
47#include <advanced_config.h>
48
49/*
50 * Plot a solder mask layer. Solder mask layers have a minimum thickness value and cannot be
51 * drawn like standard layers, unless the minimum thickness is 0.
52 */
53static void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
54 const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness );
55
56
57void PlotBoardLayers( BOARD* aBoard, PLOTTER* aPlotter, const LSEQ& aLayers,
58 const PCB_PLOT_PARAMS& aPlotOptions )
59{
60 if( !aBoard || !aPlotter || aLayers.empty() )
61 return;
62
63 // if a drill mark must be plotted, the copper layer needs to be plotted
64 // after other layers because the drill mark must be plotted as a filled
65 // white shape *after* all other shapes are plotted
66 bool plot_mark = aPlotOptions.GetDrillMarksType() != DRILL_MARKS::NO_DRILL_SHAPE;
67
68 for( PCB_LAYER_ID layer : aLayers )
69 {
70 // copper layers with drill marks will be plotted after all other layers
71 if( IsCopperLayer( layer ) && plot_mark )
72 continue;
73
74 PlotOneBoardLayer( aBoard, aPlotter, layer, aPlotOptions );
75 }
76
77 if( !plot_mark )
78 return;
79
80 for( PCB_LAYER_ID layer : aLayers )
81 {
82 if( !IsCopperLayer( layer ) )
83 continue;
84
85 PlotOneBoardLayer( aBoard, aPlotter, layer, aPlotOptions );
86 }
87}
88
89
90void PlotInteractiveLayer( BOARD* aBoard, PLOTTER* aPlotter, const PCB_PLOT_PARAMS& aPlotOpt )
91{
92 for( const FOOTPRINT* fp : aBoard->Footprints() )
93 {
94 if( fp->GetLayer() == F_Cu && !aPlotOpt.m_PDFFrontFPPropertyPopups )
95 continue;
96
97 if( fp->GetLayer() == B_Cu && !aPlotOpt.m_PDFBackFPPropertyPopups )
98 continue;
99
100 std::vector<wxString> properties;
101
102 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
103 _( "Reference designator" ),
104 fp->Reference().GetShownText( false ) ) );
105
106 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
107 _( "Value" ),
108 fp->Value().GetShownText( false ) ) );
109
110 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
111 _( "Footprint" ),
112 fp->GetFPID().GetUniStringLibItemName() ) );
113
114 for( const PCB_FIELD* field : fp->GetFields() )
115 {
116 if( field->IsReference() || field->IsValue() )
117 continue;
118
119 if( field->GetText().IsEmpty() )
120 continue;
121
122 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
123 field->GetName(),
124 field->GetText() ) );
125 }
126
127 // These 2 properties are not very useful in a plot file (like a PDF)
128#if 0
129 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Library Description" ),
130 fp->GetLibDescription() ) );
131
132 properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
133 _( "Keywords" ),
134 fp->GetKeywords() ) );
135#endif
136 // Draw items are plotted with a position offset. So we need to move
137 // our boxes (which are not plotted) by the same offset.
138 VECTOR2I offset = -aPlotter->GetPlotOffsetUserUnits();
139
140 // Use a footprint bbox without texts to create the hyperlink area
141 BOX2I bbox = fp->GetBoundingBox( false );
142 bbox.Move( offset );
143 aPlotter->HyperlinkMenu( bbox, properties );
144
145 // Use a footprint bbox with visible texts only to create the bookmark area
146 // which is the area to zoom on ft selection
147 // However the bbox need to be inflated for a better look.
148 bbox = fp->GetBoundingBox( true );
149 bbox.Move( offset );
150 bbox.Inflate( bbox.GetWidth() /2, bbox.GetHeight() /2 );
151 aPlotter->Bookmark( bbox, fp->GetReference(), _( "Footprints" ) );
152 }
153}
154
155
156void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
157 const PCB_PLOT_PARAMS& aPlotOpt )
158{
159 auto plotLayer =
160 [&]( LSET layerMask, PCB_PLOT_PARAMS& plotOpts )
161 {
162 // PlotLayerOutlines() is designed only for DXF plotters.
163 if( plotOpts.GetFormat() == PLOT_FORMAT::DXF && plotOpts.GetDXFPlotPolygonMode() )
164 PlotLayerOutlines( aBoard, aPlotter, layerMask, plotOpts );
165 else
166 PlotStandardLayer( aBoard, aPlotter, layerMask, plotOpts );
167 };
168
169 PCB_PLOT_PARAMS plotOpt = aPlotOpt;
170 int soldermask_min_thickness = aBoard->GetDesignSettings().m_SolderMaskMinWidth;
171
172 // Set a default color and the text mode for this layer
173 aPlotter->SetColor( BLACK );
174 aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
175
176 // Specify that the contents of the "Edges Pcb" layer are to be plotted in addition to the
177 // contents of the currently specified layer.
178 LSET layer_mask( { aLayer } );
179
180 if( IsCopperLayer( aLayer ) )
181 {
182 // Skip NPTH pads on copper layers ( only if hole size == pad size ):
183 // Drill mark will be plotted if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
184 if( plotOpt.GetFormat() == PLOT_FORMAT::DXF )
185 plotOpt.SetDXFPlotPolygonMode( true );
186 else
187 plotOpt.SetSkipPlotNPTH_Pads( true );
188
189 plotLayer( layer_mask, plotOpt );
190 }
191 else
192 {
193 switch( aLayer )
194 {
195 case B_Mask:
196 case F_Mask:
197 // Disable plot pad holes
199
200 // Use outline mode for DXF
201 plotOpt.SetDXFPlotPolygonMode( true );
202
203 // Plot solder mask:
204 if( soldermask_min_thickness == 0 )
205 {
206 plotLayer( layer_mask, plotOpt );
207 }
208 else
209 {
210 PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt,
211 soldermask_min_thickness );
212 }
213
214 break;
215
216 case B_Adhes:
217 case F_Adhes:
218 case B_Paste:
219 case F_Paste:
220 // Disable plot pad holes
222
223 // Use outline mode for DXF
224 plotOpt.SetDXFPlotPolygonMode( true );
225
226 plotLayer( layer_mask, plotOpt );
227
228 break;
229
230 case F_SilkS:
231 case B_SilkS:
232 plotLayer( layer_mask, plotOpt );
233
234 // Gerber: Subtract soldermask from silkscreen if enabled
235 if( aPlotter->GetPlotterType() == PLOT_FORMAT::GERBER
236 && plotOpt.GetSubtractMaskFromSilk() )
237 {
238 if( aLayer == F_SilkS )
239 layer_mask = LSET( { F_Mask } );
240 else
241 layer_mask = LSET( { B_Mask } );
242
243 // Create the mask to subtract by creating a negative layer polarity
244 aPlotter->SetLayerPolarity( false );
245
246 // Disable plot pad holes
248
249 // Plot the mask
250 PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
251
252 // Disable the negative polarity
253 aPlotter->SetLayerPolarity( true );
254 }
255
256 break;
257
258 case Dwgs_User:
259 case Cmts_User:
260 case Eco1_User:
261 case Eco2_User:
262 case Edge_Cuts:
263 case Margin:
264 case F_CrtYd:
265 case B_CrtYd:
266 case F_Fab:
267 case B_Fab:
268 default:
269 plotLayer( layer_mask, plotOpt );
270 break;
271 }
272 }
273}
274
275
279void PlotStandardLayer( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
280 const PCB_PLOT_PARAMS& aPlotOpt )
281{
282 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
283 int maxError = aBoard->GetDesignSettings().m_MaxError;
284
285 itemplotter.SetLayerSet( aLayerMask );
286
287 OUTLINE_MODE plotMode = aPlotOpt.GetPlotMode();
288 bool onCopperLayer = ( LSET::AllCuMask() & aLayerMask ).any();
289 bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & aLayerMask ).any();
290 bool onSolderPasteLayer = ( LSET( { F_Paste, B_Paste } ) & aLayerMask ).any();
291 bool onFrontFab = ( LSET( { F_Fab } ) & aLayerMask ).any();
292 bool onBackFab = ( LSET( { B_Fab } ) & aLayerMask ).any();
293 bool sketchPads = ( onFrontFab || onBackFab ) && aPlotOpt.GetSketchPadsOnFabLayers();
294
295 // Plot edge layer and graphic items
296 for( const BOARD_ITEM* item : aBoard->Drawings() )
297 itemplotter.PlotBoardGraphicItem( item );
298
299 // Draw footprint texts:
300 for( const FOOTPRINT* footprint : aBoard->Footprints() )
301 itemplotter.PlotFootprintTextItems( footprint );
302
303 // Draw footprint other graphic items:
304 for( const FOOTPRINT* footprint : aBoard->Footprints() )
305 itemplotter.PlotFootprintGraphicItems( footprint );
306
307 // Plot footprint pads
308 for( FOOTPRINT* footprint : aBoard->Footprints() )
309 {
310 aPlotter->StartBlock( nullptr );
311
312 for( PAD* pad : footprint->Pads() )
313 {
314 OUTLINE_MODE padPlotMode = plotMode;
315
316 if( !( pad->GetLayerSet() & aLayerMask ).any() )
317 {
318 if( sketchPads &&
319 ( ( onFrontFab && pad->GetLayerSet().Contains( F_Cu ) ) ||
320 ( onBackFab && pad->GetLayerSet().Contains( B_Cu ) ) ) )
321 {
322 padPlotMode = SKETCH;
323 }
324 else
325 {
326 continue;
327 }
328 }
329
330 if( onCopperLayer && !pad->IsOnCopperLayer() )
331 continue;
332
334 if( onCopperLayer && !pad->FlashLayer( aLayerMask ) )
335 continue;
336
337 // TODO(JE) padstacks - different behavior for single layer or multilayer
338
340
341 // If we're plotting a single layer, the color for that layer can be used directly.
342 if( aLayerMask.count() == 1 )
343 {
344 color = aPlotOpt.ColorSettings()->GetColor( aLayerMask.Seq()[0] );
345 }
346 else
347 {
348 if( ( pad->GetLayerSet() & aLayerMask )[B_Cu] )
349 color = aPlotOpt.ColorSettings()->GetColor( B_Cu );
350
351 if( ( pad->GetLayerSet() & aLayerMask )[F_Cu] )
352 color = color.LegacyMix( aPlotOpt.ColorSettings()->GetColor( F_Cu ) );
353
354 if( sketchPads && aLayerMask[F_Fab] )
355 color = aPlotOpt.ColorSettings()->GetColor( F_Fab );
356 else if( sketchPads && aLayerMask[B_Fab] )
357 color = aPlotOpt.ColorSettings()->GetColor( B_Fab );
358 }
359
360 if( sketchPads &&
361 ( ( onFrontFab && pad->GetLayerSet().Contains( F_Cu ) ) ||
362 ( onBackFab && pad->GetLayerSet().Contains( B_Cu ) ) ) )
363 {
364 if( aPlotOpt.GetPlotPadNumbers() )
365 itemplotter.PlotPadNumber( pad, color );
366 }
367
368 auto plotPadLayer =
369 [&]( PCB_LAYER_ID aLayer )
370 {
371 VECTOR2I margin;
372 int width_adj = 0;
373
374 if( onCopperLayer )
375 width_adj = itemplotter.getFineWidthAdj();
376
377 if( onSolderMaskLayer )
378 margin.x = margin.y = pad->GetSolderMaskExpansion( aLayer );
379
380 if( onSolderPasteLayer )
381 margin = pad->GetSolderPasteMargin( aLayer );
382
383 // not all shapes can have a different margin for x and y axis
384 // in fact only oval and rect shapes can have different values.
385 // Round shape have always the same x,y margin
386 // so define a unique value for other shapes that do not support different values
387 int mask_clearance = margin.x;
388
389 // Now offset the pad size by margin + width_adj
390 VECTOR2I padPlotsSize =
391 pad->GetSize( aLayer ) + margin * 2 + VECTOR2I( width_adj, width_adj );
392
393 // Store these parameters that can be modified to plot inflated/deflated pads shape
394 PAD_SHAPE padShape = pad->GetShape( aLayer );
395 VECTOR2I padSize = pad->GetSize( aLayer );
396 VECTOR2I padDelta = pad->GetDelta( aLayer ); // has meaning only for trapezoidal pads
397 // CornerRadius and CornerRadiusRatio can be modified
398 // the radius is built from the ratio, so saving/restoring the ratio is enough
399 double padCornerRadiusRatio = pad->GetRoundRectRadiusRatio( aLayer );
400
401 // Don't draw a 0 sized pad.
402 // Note: a custom pad can have its pad anchor with size = 0
403 if( padShape != PAD_SHAPE::CUSTOM
404 && ( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 ) )
405 {
406 return;
407 }
408
409 switch( padShape )
410 {
412 case PAD_SHAPE::OVAL:
413 pad->SetSize( aLayer, padPlotsSize );
414
415 if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
417 ( pad->GetSize(aLayer ) == pad->GetDrillSize() ) &&
418 ( pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
419 {
420 break;
421 }
422
423 itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
424 break;
425
427 pad->SetSize( aLayer, padPlotsSize );
428
429 if( mask_clearance > 0 )
430 {
431 pad->SetShape( aLayer, PAD_SHAPE::ROUNDRECT );
432 pad->SetRoundRectCornerRadius( aLayer, mask_clearance );
433 }
434
435 itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
436 break;
437
439 // inflate/deflate a trapezoid is a bit complex.
440 // so if the margin is not null, build a similar polygonal pad shape,
441 // and inflate/deflate the polygonal shape
442 // because inflating/deflating using different values for y and y
443 // we are using only margin.x as inflate/deflate value
444 if( mask_clearance == 0 )
445 {
446 itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
447 }
448 else
449 {
450 PAD dummy( *pad );
451 dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
452 dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
453 SHAPE_POLY_SET outline;
454 outline.NewOutline();
455 int dx = padSize.x / 2;
456 int dy = padSize.y / 2;
457 int ddx = padDelta.x / 2;
458 int ddy = padDelta.y / 2;
459
460 outline.Append( -dx - ddy, dy + ddx );
461 outline.Append( dx + ddy, dy - ddx );
462 outline.Append( dx - ddy, -dy + ddx );
463 outline.Append( -dx + ddy, -dy - ddx );
464
465 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
466 // which can create bad shapes if margin.x is < 0
467 outline.InflateWithLinkedHoles( mask_clearance,
469 dummy.DeletePrimitivesList();
470 dummy.AddPrimitivePoly( aLayer, outline, 0, true );
471
472 // Be sure the anchor pad is not bigger than the deflated shape because this
473 // anchor will be added to the pad shape when plotting the pad. So now the
474 // polygonal shape is built, we can clamp the anchor size
475 dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
476
477 itemplotter.PlotPad( &dummy, aLayer, color, padPlotMode );
478 }
479
480 break;
481
483 {
484 // rounding is stored as a percent, but we have to update this ratio
485 // to force recalculation of other values after size changing (we do not
486 // really change the rounding percent value)
487 double radius_ratio = pad->GetRoundRectRadiusRatio( aLayer );
488 pad->SetSize( aLayer, padPlotsSize );
489 pad->SetRoundRectRadiusRatio( aLayer, radius_ratio );
490
491 itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
492 break;
493 }
494
496 if( mask_clearance == 0 )
497 {
498 // the size can be slightly inflated by width_adj (PS/PDF only)
499 pad->SetSize( aLayer, padPlotsSize );
500 itemplotter.PlotPad( pad, aLayer, color, padPlotMode );
501 }
502 else
503 {
504 // Due to the polygonal shape of a CHAMFERED_RECT pad, the best way is to
505 // convert the pad shape to a full polygon, inflate/deflate the polygon
506 // and use a dummy CUSTOM pad to plot the final shape.
507 PAD dummy( *pad );
508 // Build the dummy pad outline with coordinates relative to the pad position
509 // pad offset and orientation 0. The actual pos, offset and rotation will be
510 // taken in account later by the plot function
511 dummy.SetPosition( VECTOR2I( 0, 0 ) );
512 dummy.SetOffset( aLayer, VECTOR2I( 0, 0 ) );
513 dummy.SetOrientation( ANGLE_0 );
514 SHAPE_POLY_SET outline;
515 dummy.TransformShapeToPolygon( outline, UNDEFINED_LAYER, 0, maxError,
516 ERROR_INSIDE );
517 outline.InflateWithLinkedHoles( mask_clearance,
519
520 // Initialize the dummy pad shape:
521 dummy.SetAnchorPadShape( aLayer, PAD_SHAPE::CIRCLE );
522 dummy.SetShape( aLayer, PAD_SHAPE::CUSTOM );
523 dummy.DeletePrimitivesList();
524 dummy.AddPrimitivePoly( aLayer, outline, 0, true );
525
526 // Be sure the anchor pad is not bigger than the deflated shape because this
527 // anchor will be added to the pad shape when plotting the pad.
528 // So we set the anchor size to 0
529 dummy.SetSize( aLayer, VECTOR2I( 0, 0 ) );
530 // Restore pad position and offset
531 dummy.SetPosition( pad->GetPosition() );
532 dummy.SetOffset( aLayer, pad->GetOffset( aLayer ) );
533 dummy.SetOrientation( pad->GetOrientation() );
534
535 itemplotter.PlotPad( &dummy, aLayer, color, padPlotMode );
536 }
537
538 break;
539
541 {
542 // inflate/deflate a custom shape is a bit complex.
543 // so build a similar pad shape, and inflate/deflate the polygonal shape
544 PAD dummy( *pad );
545 dummy.SetParentGroup( nullptr );
546
547 SHAPE_POLY_SET shape;
548 pad->MergePrimitivesAsPolygon( aLayer, &shape );
549
550 // Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
551 // which can create bad shapes if margin.x is < 0
552 shape.InflateWithLinkedHoles( mask_clearance,
554 dummy.DeletePrimitivesList();
555 dummy.AddPrimitivePoly( aLayer, shape, 0, true );
556
557 // Be sure the anchor pad is not bigger than the deflated shape because this
558 // anchor will be added to the pad shape when plotting the pad. So now the
559 // polygonal shape is built, we can clamp the anchor size
560 if( mask_clearance < 0 ) // we expect margin.x = margin.y for custom pads
561 dummy.SetSize( aLayer, VECTOR2I( std::max( 0, padPlotsSize.x ),
562 std::max( 0, padPlotsSize.y ) ) );
563
564 itemplotter.PlotPad( &dummy, aLayer, color, padPlotMode );
565 break;
566 }
567 }
568
569 // Restore the pad parameters modified by the plot code
570 pad->SetSize( aLayer, padSize );
571 pad->SetDelta( aLayer, padDelta );
572 pad->SetShape( aLayer, padShape );
573 pad->SetRoundRectRadiusRatio( aLayer, padCornerRadiusRatio );
574 };
575
576 for( PCB_LAYER_ID layer : aLayerMask.SeqStackupForPlotting() )
577 plotPadLayer( layer );
578 }
579
580 if( footprint->IsDNP()
581 && !itemplotter.GetHideDNPFPsOnFabLayers()
582 && itemplotter.GetCrossoutDNPFPsOnFabLayers()
583 && ( onFrontFab || onBackFab ) )
584 {
585 BOX2I rect = footprint->GetBoundingHull().BBox();
586 int width = aBoard->GetDesignSettings().m_LineThickness[ LAYER_CLASS_FAB ];
587
588 aPlotter->ThickSegment( rect.GetOrigin(), rect.GetEnd(), width, FILLED, nullptr );
589 aPlotter->ThickSegment( VECTOR2I( rect.GetLeft(), rect.GetBottom() ),
590 VECTOR2I( rect.GetRight(), rect.GetTop() ),
591 width, FILLED, nullptr );
592 }
593
594 aPlotter->EndBlock( nullptr );
595 }
596
597 // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true,
598
599 GBR_METADATA gbr_metadata;
600
601 if( onCopperLayer )
602 {
605 }
606
607 aPlotter->StartBlock( nullptr );
608
609 for( const PCB_TRACK* track : aBoard->Tracks() )
610 {
611 if( track->Type() != PCB_VIA_T )
612 continue;
613
614 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
615
616 // vias are not plotted if not on selected layer
617 LSET via_mask_layer = via->GetLayerSet();
618
619 if( !( via_mask_layer & aLayerMask ).any() )
620 continue;
621
622 int via_margin = 0;
623 double width_adj = 0;
624
625 // TODO(JE) padstacks - separate top/bottom margin
626 if( onSolderMaskLayer )
627 via_margin = via->GetSolderMaskExpansion();
628
629 if( ( aLayerMask & LSET::AllCuMask() ).any() )
630 width_adj = itemplotter.getFineWidthAdj();
631
633 if( onCopperLayer && !via->FlashLayer( aLayerMask ) )
634 continue;
635
636 int diameter = 0;
637
638 for( PCB_LAYER_ID layer : aLayerMask.Seq() )
639 diameter = std::max( diameter, via->GetWidth( layer ) );
640
641 diameter += 2 * via_margin + width_adj;
642
643 // Don't draw a null size item :
644 if( diameter <= 0 )
645 continue;
646
647 // Some vias can be not connected (no net).
648 // Set the m_NotInNet for these vias to force a empty net name in gerber file
649 gbr_metadata.m_NetlistMetadata.m_NotInNet = via->GetNetname().IsEmpty();
650
651 gbr_metadata.SetNetName( via->GetNetname() );
652
654
655 // If we're plotting a single layer, the color for that layer can be used directly.
656 if( aLayerMask.count() == 1 )
657 {
658 color = aPlotOpt.ColorSettings()->GetColor( aLayerMask.Seq()[0] );
659 }
660 else
661 {
662 color = aPlotOpt.ColorSettings()->GetColor(
663 LAYER_VIAS + static_cast<int>( via->GetViaType() ) );
664 }
665
666 // Change UNSPECIFIED or WHITE to LIGHTGRAY because the white items are not seen on a
667 // white paper or screen
670
671 aPlotter->SetColor( color );
672 aPlotter->FlashPadCircle( via->GetStart(), diameter, plotMode, &gbr_metadata );
673 }
674
675 aPlotter->EndBlock( nullptr );
676 aPlotter->StartBlock( nullptr );
677
678 if( onCopperLayer )
679 {
682 }
683 else
684 {
685 // Reset attributes if non-copper (soldermask) layer
688 }
689
690 // Plot tracks (not vias) :
691 for( const PCB_TRACK* track : aBoard->Tracks() )
692 {
693 if( track->Type() == PCB_VIA_T )
694 continue;
695
696 if( !( aLayerMask & track->GetLayerSet() ).any() )
697 continue;
698
699 // Some track segments can be not connected (no net).
700 // Set the m_NotInNet for these segments to force a empty net name in gerber file
701 gbr_metadata.m_NetlistMetadata.m_NotInNet = track->GetNetname().IsEmpty();
702
703 gbr_metadata.SetNetName( track->GetNetname() );
704
705 int margin = 0;
706
707 if( onSolderMaskLayer )
708 margin = track->GetSolderMaskExpansion();
709
710 int width = track->GetWidth() + 2 * margin + itemplotter.getFineWidthAdj();
711
712 aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
713
714 if( track->Type() == PCB_ARC_T )
715 {
716 const PCB_ARC* arc = static_cast<const PCB_ARC*>( track );
717
718 // Too small arcs cannot be really handled: arc center (and arc radius)
719 // cannot be safely computed
720 if( !arc->IsDegenerated( 10 /* in IU */ ) )
721 {
722 aPlotter->ThickArc( arc->GetCenter(), arc->GetArcAngleStart(), arc->GetAngle(),
723 arc->GetRadius(), width, plotMode, &gbr_metadata );
724 }
725 else
726 {
727 // Approximate this very small arc by a segment.
728 aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode,
729 &gbr_metadata );
730 }
731 }
732 else
733 {
734 aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode,
735 &gbr_metadata );
736 }
737 }
738
739 aPlotter->EndBlock( nullptr );
740
741 // Plot filled ares
742 aPlotter->StartBlock( nullptr );
743
744 NETINFO_ITEM nonet( aBoard );
745
746 for( const ZONE* zone : aBoard->Zones() )
747 {
748 if( zone->GetIsRuleArea() )
749 continue;
750
751 for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
752 {
753 if( !aLayerMask[layer] )
754 continue;
755
756 SHAPE_POLY_SET mainArea = zone->GetFilledPolysList( layer )->CloneDropTriangulation();
757 SHAPE_POLY_SET islands;
758
759 for( int i = mainArea.OutlineCount() - 1; i >= 0; i-- )
760 {
761 if( zone->IsIsland( layer, i ) )
762 {
763 islands.AddOutline( mainArea.CPolygon( i )[0] );
764 mainArea.DeletePolygon( i );
765 }
766 }
767
768 itemplotter.PlotZone( zone, layer, mainArea );
769
770 if( !islands.IsEmpty() )
771 {
772 ZONE dummy( *zone );
773 dummy.SetNet( &nonet );
774 itemplotter.PlotZone( &dummy, layer, islands );
775 }
776 }
777 }
778
779 aPlotter->EndBlock( nullptr );
780
781 // Adding drill marks, if required and if the plotter is able to plot them:
783 itemplotter.PlotDrillMarks();
784}
785
786
790void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
791 const PCB_PLOT_PARAMS& aPlotOpt )
792{
793 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
794 itemplotter.SetLayerSet( aLayerMask );
795
796 SHAPE_POLY_SET outlines;
797
798 for( PCB_LAYER_ID layer : aLayerMask.Seq( aLayerMask.SeqStackupForPlotting() ) )
799 {
800 outlines.RemoveAllContours();
801 aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines );
802
803 outlines.Simplify();
804
805 // Plot outlines
806 std::vector<VECTOR2I> cornerList;
807
808 // Now we have one or more basic polygons: plot each polygon
809 for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
810 {
811 for( int kk = 0; kk <= outlines.HoleCount(ii); kk++ )
812 {
813 cornerList.clear();
814 const SHAPE_LINE_CHAIN& path = ( kk == 0 ) ? outlines.COutline( ii )
815 : outlines.CHole( ii, kk - 1 );
816
817 aPlotter->PlotPoly( path, FILL_T::NO_FILL );
818 }
819 }
820
821 // Plot pad holes
823 {
824 int smallDrill = ( aPlotOpt.GetDrillMarksType() == DRILL_MARKS::SMALL_DRILL_SHAPE )
826 : INT_MAX;
827
828 for( FOOTPRINT* footprint : aBoard->Footprints() )
829 {
830 for( PAD* pad : footprint->Pads() )
831 {
832 if( pad->HasHole() )
833 {
834 std::shared_ptr<SHAPE_SEGMENT> slot = pad->GetEffectiveHoleShape();
835
836 if( slot->GetSeg().A == slot->GetSeg().B ) // circular hole
837 {
838 int drill = std::min( smallDrill, slot->GetWidth() );
839 aPlotter->Circle( pad->GetPosition(), drill, FILL_T::NO_FILL );
840 }
841 else
842 {
843 // Note: small drill marks have no significance when applied to slots
844 aPlotter->ThickSegment( slot->GetSeg().A, slot->GetSeg().B,
845 slot->GetWidth(), SKETCH, nullptr );
846 }
847 }
848 }
849 }
850 }
851
852 // Plot vias holes
853 for( PCB_TRACK* track : aBoard->Tracks() )
854 {
855 if( track->Type() != PCB_VIA_T )
856 continue;
857
858 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
859
860 if( via->GetLayerSet().Contains( layer ) ) // via holes can be not through holes
861 aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), FILL_T::NO_FILL );
862 }
863 }
864}
865
866
884void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
885 const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness )
886{
887 int maxError = aBoard->GetDesignSettings().m_MaxError;
888 PCB_LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask;
889 SHAPE_POLY_SET buffer;
890 SHAPE_POLY_SET* boardOutline = nullptr;
891
892 if( aBoard->GetBoardPolygonOutlines( buffer ) )
893 boardOutline = &buffer;
894
895 // We remove 1nm as we expand both sides of the shapes, so allowing for a strictly greater
896 // than or equal comparison in the shape separation (boolean add)
897 int inflate = aMinThickness / 2 - 1;
898
899 BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
900 itemplotter.SetLayerSet( aLayerMask );
901
902 // Build polygons for each pad shape. The size of the shape on solder mask should be size
903 // of pad + clearance around the pad, where clearance = solder mask clearance + extra margin.
904 // Extra margin is half the min width for solder mask, which is used to merge too-close shapes
905 // (distance < aMinThickness), and will be removed when creating the actual shapes.
906
907 // Will contain shapes inflated by inflate value that will be merged and deflated by inflate
908 // value to build final polygons
909 SHAPE_POLY_SET areas;
910
911 // Will contain exact shapes of all items on solder mask
912 SHAPE_POLY_SET initialPolys;
913
914 auto plotFPTextItem =
915 [&]( const PCB_TEXT& aText )
916 {
917 if( !itemplotter.GetPlotFPText() )
918 return;
919
920 if( !aText.IsVisible() && !itemplotter.GetPlotInvisibleText() )
921 return;
922
923 if( aText.GetText() == wxT( "${REFERENCE}" ) && !itemplotter.GetPlotReference() )
924 return;
925
926 if( aText.GetText() == wxT( "${VALUE}" ) && !itemplotter.GetPlotValue() )
927 return;
928
929 // add shapes with their exact mask layer size in initialPolys
930 aText.TransformTextToPolySet( initialPolys, 0, maxError, ERROR_OUTSIDE );
931
932 // add shapes inflated by aMinThickness/2 in areas
933 aText.TransformTextToPolySet( areas, inflate, maxError, ERROR_OUTSIDE );
934 };
935
936 // Generate polygons with arcs inside the shape or exact shape to minimize shape changes
937 // created by arc to segment size correction.
939 {
940 // Plot footprint pads and graphics
941 for( const FOOTPRINT* footprint : aBoard->Footprints() )
942 {
943 // add shapes with their exact mask layer size in initialPolys
944 footprint->TransformPadsToPolySet( initialPolys, layer, 0, maxError, ERROR_OUTSIDE );
945 // add shapes inflated by aMinThickness/2 in areas
946 footprint->TransformPadsToPolySet( areas, layer, inflate, maxError, ERROR_OUTSIDE );
947
948 for( const PCB_FIELD* field : footprint->GetFields() )
949 {
950 if( field->IsReference() && !itemplotter.GetPlotReference() )
951 continue;
952
953 if( field->IsValue() && !itemplotter.GetPlotValue() )
954 continue;
955
956 if( field->IsOnLayer( layer ) )
957 plotFPTextItem( static_cast<const PCB_TEXT&>( *field ) );
958 }
959
960 for( const BOARD_ITEM* item : footprint->GraphicalItems() )
961 {
962 if( item->IsOnLayer( layer ) )
963 {
964 if( item->Type() == PCB_TEXT_T )
965 {
966 plotFPTextItem( static_cast<const PCB_TEXT&>( *item ) );
967 }
968 else
969 {
970 // add shapes with their exact mask layer size in initialPolys
971 item->TransformShapeToPolygon( initialPolys, layer, 0, maxError,
973
974 // add shapes inflated by aMinThickness/2 in areas
975 item->TransformShapeToPolygon( areas, layer, inflate, maxError,
977 }
978 }
979 }
980 }
981
982 // Plot (untented) vias
983 for( const PCB_TRACK* track : aBoard->Tracks() )
984 {
985 if( track->Type() != PCB_VIA_T )
986 continue;
987
988 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
989
990 // Note: IsOnLayer() checks relevant mask layers of untented vias
991 if( !via->IsOnLayer( layer ) )
992 continue;
993
994 int clearance = via->GetSolderMaskExpansion();
995
996 // add shapes with their exact mask layer size in initialPolys
997 via->TransformShapeToPolygon( initialPolys, layer, clearance, maxError, ERROR_OUTSIDE );
998
999 // add shapes inflated by aMinThickness/2 in areas
1000 clearance += inflate;
1001 via->TransformShapeToPolygon( areas, layer, clearance, maxError, ERROR_OUTSIDE );
1002 }
1003
1004 // Add filled zone areas.
1005#if 0 // Set to 1 if a solder mask expansion must be applied to zones on solder mask
1006 int zone_margin = aBoard->GetDesignSettings().m_SolderMaskExpansion;
1007#else
1008 int zone_margin = 0;
1009#endif
1010
1011 for( const BOARD_ITEM* item : aBoard->Drawings() )
1012 {
1013 if( item->IsOnLayer( layer ) )
1014 {
1015 if( item->Type() == PCB_TEXT_T )
1016 {
1017 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
1018
1019 // add shapes with their exact mask layer size in initialPolys
1020 text->TransformTextToPolySet( initialPolys, 0, maxError, ERROR_OUTSIDE );
1021
1022 // add shapes inflated by aMinThickness/2 in areas
1023 text->TransformTextToPolySet( areas, inflate, maxError, ERROR_OUTSIDE );
1024 }
1025 else
1026 {
1027 // add shapes with their exact mask layer size in initialPolys
1028 item->TransformShapeToPolygon( initialPolys, layer, 0, maxError,
1029 ERROR_OUTSIDE );
1030
1031 // add shapes inflated by aMinThickness/2 in areas
1032 item->TransformShapeToPolygon( areas, layer, inflate, maxError, ERROR_OUTSIDE );
1033 }
1034 }
1035 }
1036
1037 for( ZONE* zone : aBoard->Zones() )
1038 {
1039 if( zone->GetIsRuleArea() )
1040 continue;
1041
1042 if( !zone->IsOnLayer( layer ) )
1043 continue;
1044
1045 // add shapes inflated by aMinThickness/2 in areas
1046 zone->TransformSmoothedOutlineToPolygon( areas, inflate + zone_margin, maxError,
1047 ERROR_OUTSIDE, boardOutline );
1048
1049 // add shapes with their exact mask layer size in initialPolys
1050 zone->TransformSmoothedOutlineToPolygon( initialPolys, zone_margin, maxError,
1051 ERROR_OUTSIDE, boardOutline );
1052 }
1053 }
1054
1055 // Merge all polygons: After deflating, not merged (not overlapping) polygons will have the
1056 // initial shape (with perhaps small changes due to deflating transform)
1057 areas.Simplify();
1058 areas.Deflate( inflate, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, maxError );
1059
1060 // To avoid a lot of code, use a ZONE to handle and plot polygons, because our polygons look
1061 // exactly like filled areas in zones.
1062 // Note, also this code is not optimized: it creates a lot of copy/duplicate data.
1063 // However it is not complex, and fast enough for plot purposes (copy/convert data is only a
1064 // very small calculation time for these calculations).
1065 ZONE zone( aBoard );
1066 zone.SetMinThickness( 0 ); // trace polygons only
1067 zone.SetLayer( layer );
1068
1069 // Combine the current areas to initial areas. This is mandatory because inflate/deflate
1070 // transform is not perfect, and we want the initial areas perfectly kept
1071 areas.BooleanAdd( initialPolys );
1072 areas.Fracture();
1073
1074 itemplotter.PlotZone( &zone, layer, areas );
1075}
1076
1077
1084static void initializePlotter( PLOTTER* aPlotter, const BOARD* aBoard,
1085 const PCB_PLOT_PARAMS* aPlotOpts )
1086{
1087 PAGE_INFO pageA4( wxT( "A4" ) );
1088 const PAGE_INFO& pageInfo = aBoard->GetPageSettings();
1089 const PAGE_INFO* sheet_info;
1090 double paperscale; // Page-to-paper ratio
1091 VECTOR2I paperSizeIU;
1092 VECTOR2I pageSizeIU( pageInfo.GetSizeIU( pcbIUScale.IU_PER_MILS ) );
1093 bool autocenter = false;
1094
1095 // Special options: to fit the sheet to an A4 sheet replace the paper size. However there
1096 // is a difference between the autoscale and the a4paper option:
1097 // - Autoscale fits the board to the paper size
1098 // - A4paper fits the original paper size to an A4 sheet
1099 // - Both of them fit the board to an A4 sheet
1100 if( aPlotOpts->GetA4Output() )
1101 {
1102 sheet_info = &pageA4;
1103 paperSizeIU = pageA4.GetSizeIU( pcbIUScale.IU_PER_MILS );
1104 paperscale = (double) paperSizeIU.x / pageSizeIU.x;
1105 autocenter = true;
1106 }
1107 else
1108 {
1109 sheet_info = &pageInfo;
1110 paperSizeIU = pageSizeIU;
1111 paperscale = 1;
1112
1113 // Need autocentering only if scale is not 1:1
1114 autocenter = (aPlotOpts->GetScale() != 1.0);
1115 }
1116
1117 BOX2I bbox = aBoard->ComputeBoundingBox( false );
1118 VECTOR2I boardCenter = bbox.Centre();
1119 VECTOR2I boardSize = bbox.GetSize();
1120
1121 double compound_scale;
1122
1123 // Fit to 80% of the page if asked; it could be that the board is empty, in this case
1124 // regress to 1:1 scale
1125 if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
1126 {
1127 double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
1128 double yscale = (paperSizeIU.y * 0.8) / boardSize.y;
1129
1130 compound_scale = std::min( xscale, yscale ) * paperscale;
1131 }
1132 else
1133 {
1134 compound_scale = aPlotOpts->GetScale() * paperscale;
1135 }
1136
1137 // For the plot offset we have to keep in mind the auxiliary origin too: if autoscaling is
1138 // off we check that plot option (i.e. autoscaling overrides auxiliary origin)
1139 VECTOR2I offset( 0, 0);
1140
1141 if( autocenter )
1142 {
1143 offset.x = KiROUND( boardCenter.x - ( paperSizeIU.x / 2.0 ) / compound_scale );
1144 offset.y = KiROUND( boardCenter.y - ( paperSizeIU.y / 2.0 ) / compound_scale );
1145 }
1146 else
1147 {
1148 if( aPlotOpts->GetUseAuxOrigin() )
1149 offset = aBoard->GetDesignSettings().GetAuxOrigin();
1150 }
1151
1152 aPlotter->SetPageSettings( *sheet_info );
1153
1154 aPlotter->SetViewport( offset, pcbIUScale.IU_PER_MILS/10, compound_scale, aPlotOpts->GetMirror() );
1155
1156 // Has meaning only for gerber plotter. Must be called only after SetViewport
1157 aPlotter->SetGerberCoordinatesFormat( aPlotOpts->GetGerberPrecision() );
1158
1159 // Has meaning only for SVG plotter. Must be called only after SetViewport
1160 aPlotter->SetSvgCoordinatesFormat( aPlotOpts->GetSvgPrecision() );
1161
1162 aPlotter->SetCreator( wxT( "PCBNEW" ) );
1163 aPlotter->SetColorMode( !aPlotOpts->GetBlackAndWhite() ); // default is plot in Black and White.
1164 aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
1165}
1166
1167
1171static void FillNegativeKnockout( PLOTTER *aPlotter, const BOX2I &aBbbox )
1172{
1173 const int margin = 5 * pcbIUScale.IU_PER_MM; // Add a 5 mm margin around the board
1174 aPlotter->SetNegative( true );
1175 aPlotter->SetColor( WHITE ); // Which will be plotted as black
1176
1177 BOX2I area = aBbbox;
1178 area.Inflate( margin );
1179 aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILL_T::FILLED_SHAPE );
1180 aPlotter->SetColor( BLACK );
1181}
1182
1183
1187static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter, const PCB_PLOT_PARAMS *aPlotOpts )
1188{
1189 // Compute penDiam (the value is given in mils) in pcb units, with plot scale (if Scale is 2,
1190 // penDiam value is always m_HPGLPenDiam so apparent penDiam is actually penDiam / Scale
1191 int penDiam = KiROUND( aPlotOpts->GetHPGLPenDiameter() * pcbIUScale.IU_PER_MILS / aPlotOpts->GetScale() );
1192
1193 // Set HPGL-specific options and start
1194 aPlotter->SetPenSpeed( aPlotOpts->GetHPGLPenSpeed() );
1195 aPlotter->SetPenNumber( aPlotOpts->GetHPGLPenNum() );
1196 aPlotter->SetPenDiameter( penDiam );
1197}
1198
1199
1206PLOTTER* StartPlotBoard( BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, int aLayer,
1207 const wxString& aLayerName, const wxString& aFullFileName,
1208 const wxString& aSheetName, const wxString& aSheetPath,
1209 const wxString& aPageName, const wxString& aPageNumber,
1210 const int aPageCount )
1211{
1212 wxCHECK( aBoard && aPlotOpts, nullptr );
1213
1214 // Create the plotter driver and set the few plotter specific options
1215 PLOTTER* plotter = nullptr;
1216
1217 switch( aPlotOpts->GetFormat() )
1218 {
1219 case PLOT_FORMAT::DXF:
1220 DXF_PLOTTER* DXF_plotter;
1221 DXF_plotter = new DXF_PLOTTER();
1222 DXF_plotter->SetUnits( aPlotOpts->GetDXFPlotUnits() );
1223
1224 plotter = DXF_plotter;
1225 break;
1226
1227 case PLOT_FORMAT::POST:
1228 PS_PLOTTER* PS_plotter;
1229 PS_plotter = new PS_PLOTTER();
1230 PS_plotter->SetScaleAdjust( aPlotOpts->GetFineScaleAdjustX(),
1231 aPlotOpts->GetFineScaleAdjustY() );
1232 plotter = PS_plotter;
1233 break;
1234
1235 case PLOT_FORMAT::PDF:
1236 plotter = new PDF_PLOTTER( aBoard->GetProject() );
1237 break;
1238
1239 case PLOT_FORMAT::HPGL:
1240 HPGL_PLOTTER* HPGL_plotter;
1241 HPGL_plotter = new HPGL_PLOTTER();
1242
1243 // HPGL options are a little more convoluted to compute, so they get their own function
1244 ConfigureHPGLPenSizes( HPGL_plotter, aPlotOpts );
1245 plotter = HPGL_plotter;
1246 break;
1247
1249 // For Gerber plotter, a valid board layer must be set, in order to create a valid
1250 // Gerber header, especially the TF.FileFunction and .FilePolarity data
1251 if( aLayer < PCBNEW_LAYER_ID_START || aLayer >= PCB_LAYER_ID_COUNT )
1252 {
1253 wxLogError( wxString::Format(
1254 "Invalid board layer %d, cannot build a valid Gerber file header",
1255 aLayer ) );
1256 }
1257
1258 plotter = new GERBER_PLOTTER();
1259 break;
1260
1261 case PLOT_FORMAT::SVG:
1262 plotter = new SVG_PLOTTER();
1263 break;
1264
1265 default:
1266 wxASSERT( false );
1267 return nullptr;
1268 }
1269
1271 renderSettings->LoadColors( aPlotOpts->ColorSettings() );
1272 renderSettings->SetDefaultPenWidth( pcbIUScale.mmToIU( 0.0212 ) ); // Hairline at 1200dpi
1273 renderSettings->SetLayerName( aLayerName );
1274
1275 plotter->SetRenderSettings( renderSettings );
1276
1277 // Compute the viewport and set the other options
1278
1279 // page layout is not mirrored, so temporarily change mirror option for the page layout
1280 PCB_PLOT_PARAMS plotOpts = *aPlotOpts;
1281
1282 if( plotOpts.GetPlotFrameRef() && plotOpts.GetMirror() )
1283 plotOpts.SetMirror( false );
1284
1285 initializePlotter( plotter, aBoard, &plotOpts );
1286
1287 if( plotter->OpenFile( aFullFileName ) )
1288 {
1289 plotter->ClearHeaderLinesList();
1290
1291 // For the Gerber "file function" attribute, set the layer number
1292 if( plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
1293 {
1294 bool useX2mode = plotOpts.GetUseGerberX2format();
1295
1296 GERBER_PLOTTER* gbrplotter = static_cast <GERBER_PLOTTER*> ( plotter );
1297 gbrplotter->DisableApertMacros( plotOpts.GetDisableGerberMacros() );
1298 gbrplotter->UseX2format( useX2mode );
1299 gbrplotter->UseX2NetAttributes( plotOpts.GetIncludeGerberNetlistInfo() );
1300
1301 // Attributes can be added using X2 format or as comment (X1 format)
1302 AddGerberX2Attribute( plotter, aBoard, aLayer, not useX2mode );
1303 }
1304
1305 bool startPlotSuccess = false;
1306 if (plotter->GetPlotterType() == PLOT_FORMAT::PDF)
1307 {
1308 startPlotSuccess =
1309 static_cast<PDF_PLOTTER*>( plotter )->StartPlot( aPageNumber, aPageName );
1310 }
1311 else
1312 {
1313 startPlotSuccess = plotter->StartPlot( aPageName );
1314 }
1315
1316
1317 if( startPlotSuccess )
1318 {
1319 // Plot the frame reference if requested
1320 if( aPlotOpts->GetPlotFrameRef() )
1321 {
1322 PlotDrawingSheet( plotter, aBoard->GetProject(), aBoard->GetTitleBlock(),
1323 aBoard->GetPageSettings(), &aBoard->GetProperties(), aPageNumber,
1324 aPageCount, aSheetName, aSheetPath, aBoard->GetFileName(),
1325 renderSettings->GetLayerColor( LAYER_DRAWINGSHEET ) );
1326
1327 if( aPlotOpts->GetMirror() )
1328 initializePlotter( plotter, aBoard, aPlotOpts );
1329 }
1330
1331 // When plotting a negative board: draw a black rectangle (background for plot board
1332 // in white) and switch the current color to WHITE; note the color inversion is actually
1333 // done in the driver (if supported)
1334 if( aPlotOpts->GetNegative() )
1335 {
1336 BOX2I bbox = aBoard->ComputeBoundingBox( false );
1337 FillNegativeKnockout( plotter, bbox );
1338 }
1339
1340 return plotter;
1341 }
1342 }
1343
1344 delete plotter->RenderSettings();
1345 delete plotter;
1346 return nullptr;
1347}
1348
1350 BOARD* aBoard,
1351 const PCB_PLOT_PARAMS* aPlotOpts,
1352 const wxString& aSheetName, const wxString& aSheetPath,
1353 const wxString& aPageNumber, int aPageCount )
1354{
1355 // Plot the frame reference if requested
1356 if( aPlotOpts->GetPlotFrameRef() )
1357 {
1358 PlotDrawingSheet( aPlotter, aBoard->GetProject(), aBoard->GetTitleBlock(),
1359 aBoard->GetPageSettings(), &aBoard->GetProperties(), aPageNumber,
1360 aPageCount,
1361 aSheetName, aSheetPath, aBoard->GetFileName(),
1363
1364 if( aPlotOpts->GetMirror() )
1365 initializePlotter( aPlotter, aBoard, aPlotOpts );
1366 }
1367}
int color
Definition: DXF_plotter.cpp:60
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
@ LAYER_CLASS_FAB
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
const VECTOR2I & GetAuxOrigin()
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:79
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:295
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false, bool aIncludeNPTHAsOutlines=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:2536
const PAGE_INFO & GetPageSettings() const
Definition: board.h:697
const ZONES & Zones() const
Definition: board.h:340
void ConvertBrdLayerToPolygonalContours(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aOutlines) const
Build a set of polygons which are the outlines of copper items (pads, tracks, vias,...
Definition: board.cpp:2972
TITLE_BLOCK & GetTitleBlock()
Definition: board.h:703
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1713
const std::map< wxString, wxString > & GetProperties() const
Definition: board.h:367
const FOOTPRINTS & Footprints() const
Definition: board.h:336
const TRACKS & Tracks() const
Definition: board.h:334
const wxString & GetFileName() const
Definition: board.h:332
PROJECT * GetProject() const
Definition: board.h:499
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:934
const DRAWINGS & Drawings() const
Definition: board.h:338
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr const Vec GetEnd() const
Definition: box2.h:212
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr Vec Centre() const
Definition: box2.h:97
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition: box2.h:138
constexpr const Vec & GetOrigin() const
Definition: box2.h:210
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr const SizeVec & GetSize() const
Definition: box2.h:206
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
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 SetLayerSet(LSET aLayerMask)
Definition: pcbplot.h:86
void PlotPad(const PAD *aPad, PCB_LAYER_ID aLayer, const COLOR4D &aColor, OUTLINE_MODE aPlotMode)
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:77
void PlotFootprintGraphicItems(const FOOTPRINT *aFootprint)
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).
Definition: gbr_metadata.h:98
@ GBR_APERTURE_ATTRIB_VIAPAD
Aperture used for vias.
Definition: gbr_metadata.h:103
@ GBR_APERTURE_ATTRIB_NONE
uninitialized attribute.
Definition: gbr_metadata.h:94
Metadata which can be added in a gerber file as attribute in X2 format.
Definition: gbr_metadata.h:206
void SetNetName(const wxString &aNetname)
Definition: gbr_metadata.h:230
void SetApertureAttrib(GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB aApertAttribute)
Definition: gbr_metadata.h:210
GBR_NETLIST_METADATA m_NetlistMetadata
An item to handle object attribute.
Definition: gbr_metadata.h:263
void SetNetAttribType(int aNetAttribType)
Definition: gbr_metadata.h:220
@ 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.
virtual void SetPenSpeed(int speed)
Definition: plotter_hpgl.h:88
virtual void SetPenNumber(int number)
Definition: plotter_hpgl.h:93
virtual void SetPenDiameter(double diameter)
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition: color4d.h:398
static const COLOR4D BLACK
Definition: color4d.h:402
PCB specific render settings.
Definition: pcb_painter.h:78
void LoadColors(const COLOR_SETTINGS *aSettings) override
void SetDefaultPenWidth(int aWidth)
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
void SetLayerName(const wxString &aLayerName)
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
LSEQ SeqStackupForPlotting() const
Return the sequence that is typical for a bottom-to-top stack-up.
Definition: lset.cpp:386
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:562
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:295
Handle the data for a net.
Definition: netinfo.h:56
Definition: pad.h:54
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:59
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition: page_info.h:171
bool IsDegenerated(int aThreshold=5) const
Definition: pcb_track.cpp:1901
EDA_ANGLE GetArcAngleStart() const
Definition: pcb_track.cpp:1883
double GetRadius() const
Definition: pcb_track.cpp:1866
EDA_ANGLE GetAngle() const
Definition: pcb_track.cpp:1873
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:312
Parameters and options when plotting/printing a board.
bool GetNegative() const
PLOT_FORMAT GetFormat() const
bool GetSkipPlotNPTH_Pads() 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 GetPlotInvisibleText() const
PLOT_TEXT_MODE GetTextMode() const
bool GetCrossoutDNPFPsOnFabLayers() const
void SetDXFPlotPolygonMode(bool aFlag)
double GetHPGLPenDiameter() const
unsigned GetSvgPrecision() const
unsigned GetBlackAndWhite() const
double GetScale() const
bool GetPlotReference() const
bool m_PDFFrontFPPropertyPopups
Generate PDF property popup menus for footprints.
void SetMirror(bool aFlag)
bool GetSketchPadsOnFabLayers() const
bool GetSubtractMaskFromSilk() const
int GetGerberPrecision() const
int GetHPGLPenSpeed() const
double GetFineScaleAdjustY() const
bool GetPlotPadNumbers() const
bool GetA4Output() const
int GetHPGLPenNum() const
DRILL_MARKS GetDrillMarksType() const
bool GetUseGerberX2format() const
bool GetPlotValue() const
bool GetIncludeGerberNetlistInfo() const
double GetFineScaleAdjustX() const
bool m_PDFBackFPPropertyPopups
on front and/or back of board
bool GetPlotFPText() const
bool GetPlotFrameRef() const
bool GetDisableGerberMacros() const
OUTLINE_MODE GetPlotMode() const
COLOR_SETTINGS * ColorSettings() const
Base plotter engine class.
Definition: plotter.h:105
virtual void ThickArc(const EDA_SHAPE &aArcShape, OUTLINE_MODE aTraceMode, void *aData, int aWidth)
Definition: plotter.cpp:596
virtual void ThickSegment(const VECTOR2I &start, const VECTOR2I &end, int width, OUTLINE_MODE tracemode, void *aData)
Definition: plotter.cpp:553
virtual void Circle(const VECTOR2I &pos, int diametre, FILL_T fill, int width=USE_DEFAULT_LINE_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:125
virtual void SetSvgCoordinatesFormat(unsigned aPrecision)
Set the number of digits for mantissa in coordinates in mm for SVG plotter.
Definition: plotter.h:528
virtual void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: plotter.h:138
void SetRenderSettings(RENDER_SETTINGS *aSettings)
Definition: plotter.h:135
virtual bool StartPlot(const wxString &aPageNumber)=0
RENDER_SETTINGS * RenderSettings()
Definition: plotter.h:136
virtual void SetGerberCoordinatesFormat(int aResolution, bool aUseInches=false)
Definition: plotter.h:522
virtual void Bookmark(const BOX2I &aBox, const wxString &aName, const wxString &aGroupName=wxEmptyString)
Create a bookmark to a symbol.
Definition: plotter.h:478
virtual PLOT_FORMAT GetPlotterType() const =0
Return the effective plot engine in use.
virtual void SetTextMode(PLOT_TEXT_MODE mode)
Change the current text mode.
Definition: plotter.h:517
virtual void SetCreator(const wxString &aCreator)
Definition: plotter.h:154
VECTOR2I GetPlotOffsetUserUnits()
Definition: plotter.h:554
void ClearHeaderLinesList()
Remove all lines from the list of free lines to print at the beginning of the file.
Definition: plotter.h:172
virtual void FlashPadCircle(const VECTOR2I &aPadPos, int aDiameter, OUTLINE_MODE aTraceMode, void *aData)=0
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:132
virtual void StartBlock(void *aData)
calling this function allows one to define the beginning of a group of drawing items,...
Definition: plotter.h:540
virtual void PlotPoly(const std::vector< VECTOR2I > &aCornerList, FILL_T aFill, int aWidth=USE_DEFAULT_LINE_WIDTH, void *aData=nullptr)=0
Draw a polygon ( filled or not ).
virtual void HyperlinkMenu(const BOX2I &aBox, const std::vector< wxString > &aDestURLs)
Create a clickable hyperlink menu with a rectangular click area.
Definition: plotter.h:467
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:507
virtual void Rect(const VECTOR2I &p1, const VECTOR2I &p2, FILL_T fill, int width=USE_DEFAULT_LINE_WIDTH)=0
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:549
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 RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
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 Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
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.
SHAPE_POLY_SET CloneDropTriangulation() const
const POLYGON & CPolygon(int aIndex) const
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
void SetMinThickness(int aMinThickness)
Definition: zone.h:274
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:486
@ WHITE
Definition: color4d.h:48
@ LIGHTGRAY
Definition: color4d.h:47
@ BLACK
Definition: color4d.h:44
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)
@ 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:401
@ FILLED_SHAPE
Fill with object color.
Handle special data (items attributes) during plot.
a few functions useful in geometry calculations.
static const bool FILLED
Definition: gr_basic.cpp:30
double m_SmallDrillMarkSize
The diameter of the drill marks on print and plot outputs (in mm) when the "Drill marks" option is se...
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:581
@ LAYER_DRAWINGSHEET
Sheet frame and title block.
Definition: layer_ids.h:240
@ LAYER_VIAS
Meta control for all vias opacity/visibility.
Definition: layer_ids.h:195
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ B_Adhes
Definition: layer_ids.h:103
@ Edge_Cuts
Definition: layer_ids.h:112
@ Dwgs_User
Definition: layer_ids.h:107
@ F_Paste
Definition: layer_ids.h:104
@ Cmts_User
Definition: layer_ids.h:108
@ F_Adhes
Definition: layer_ids.h:102
@ B_Mask
Definition: layer_ids.h:98
@ B_Cu
Definition: layer_ids.h:65
@ Eco1_User
Definition: layer_ids.h:109
@ F_Mask
Definition: layer_ids.h:97
@ B_Paste
Definition: layer_ids.h:105
@ F_Fab
Definition: layer_ids.h:119
@ Margin
Definition: layer_ids.h:113
@ F_SilkS
Definition: layer_ids.h:100
@ B_CrtYd
Definition: layer_ids.h:115
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ Eco2_User
Definition: layer_ids.h:110
@ B_SilkS
Definition: layer_ids.h:101
@ PCB_LAYER_ID_COUNT
Definition: layer_ids.h:135
@ F_Cu
Definition: layer_ids.h:64
@ B_Fab
Definition: layer_ids.h:118
OUTLINE_MODE
Definition: outline_mode.h:25
@ SKETCH
Definition: outline_mode.h:26
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition: padstack.h:52
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:360
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 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, LSET aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
Plot any layer EXCEPT a solder-mask with an enforced minimum width.
static void PlotSolderMaskLayer(BOARD *aBoard, PLOTTER *aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt, int aMinThickness)
Plot a solder mask layer.
void PlotOneBoardLayer(BOARD *aBoard, PLOTTER *aPlotter, PCB_LAYER_ID aLayer, const PCB_PLOT_PARAMS &aPlotOpt)
Plot one copper or technical layer.
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 PlotLayerOutlines(BOARD *aBoard, PLOTTER *aPlotter, LSET aLayerMask, const PCB_PLOT_PARAMS &aPlotOpt)
Plot outlines.
static void ConfigureHPGLPenSizes(HPGL_PLOTTER *aPlotter, const PCB_PLOT_PARAMS *aPlotOpts)
Calculate the effective size of HPGL pens and set them in the plotter object.
void setupPlotterNewPDFPage(PLOTTER *aPlotter, BOARD *aBoard, const PCB_PLOT_PARAMS *aPlotOpts, const wxString &aSheetName, const wxString &aSheetPath, const wxString &aPageNumber, int aPageCount)
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.).
Plotting engine (HPGL)
Plotting engines similar to ps (PostScript, Gerber, svg)
std::vector< FAB_LAYER_COLOR > dummy
const double IU_PER_MM
Definition: base_units.h:76
const double IU_PER_MILS
Definition: base_units.h:77
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695