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