You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1061 lines
						
					
					
						
							37 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							1061 lines
						
					
					
						
							37 KiB
						
					
					
				| /* | |
|  * This program source code file is part of KiCad, a free EDA CAD application. | |
|  * | |
|  * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. | |
|  * | |
|  * This program is free software; you can redistribute it and/or | |
|  * modify it under the terms of the GNU General Public License | |
|  * as published by the Free Software Foundation; either version 2 | |
|  * of the License, or (at your option) any later version. | |
|  * | |
|  * This program is distributed in the hope that it will be useful, | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | |
|  * GNU General Public License for more details. | |
|  * | |
|  * You should have received a copy of the GNU General Public License | |
|  * along with this program; if not, you may find one here: | |
|  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html | |
|  * or you may search the http://www.gnu.org website for the version 2 license, | |
|  * or you may write to the Free Software Foundation, Inc., | |
|  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | |
|  */ | |
| 
 | |
| #include <algorithm>                          // for min | |
| #include <bitset>                             // for bitset, operator&, __bi... | |
| #include <math.h>                             // for abs | |
| #include <stddef.h>                           // for NULL, size_t | |
| #include <vector>                             // for vector, __vector_base<>... | |
|  | |
| #include <eda_item.h> | |
| #include <geometry/seg.h>                     // for SEG | |
| #include <geometry/shape_circle.h> | |
| #include <geometry/shape_line_chain.h>        // for SHAPE_LINE_CHAIN | |
| #include <geometry/shape_poly_set.h>          // for SHAPE_POLY_SET, SHAPE_P... | |
| #include <geometry/shape_segment.h> | |
| #include <kicad_string.h> | |
| #include <math/util.h>                        // for KiROUND, Clamp | |
| #include <math/vector2d.h>                    // for VECTOR2I | |
| #include <plotter.h> | |
| #include <plotters_specific.h> | |
| #include <trigo.h> | |
|  | |
| #include <board_design_settings.h>            // for BOARD_DESIGN_SETTINGS | |
| #include <core/typeinfo.h>                    // for dyn_cast, PCB_DIMENSION_T | |
| #include <outline_mode.h> | |
| #include <gal/color4d.h>                      // for COLOR4D, operator!= | |
| #include <gbr_metadata.h> | |
| #include <gbr_netlist_metadata.h>             // for GBR_NETLIST_METADATA | |
| #include <layers_id_colors_and_visibility.h>  // for LSET, IsCopperLayer | |
| #include <pad_shapes.h>                       // for PAD_ATTRIB_NPTH | |
| #include <pcbplot.h> | |
| #include <pcb_plot_params.h>                  // for PCB_PLOT_PARAMS, PCB_PL... | |
| #include <advanced_config.h> | |
|  | |
| #include <board.h> | |
| #include <board_item.h>                       // for BOARD_ITEM, S_CIRCLE | |
| #include <dimension.h> | |
| #include <pcb_shape.h> | |
| #include <fp_shape.h> | |
| #include <footprint.h> | |
| #include <fp_text.h> | |
| #include <track.h> | |
| #include <pad.h> | |
| #include <pcb_target.h> | |
| #include <pcb_text.h> | |
| #include <zone.h> | |
|  | |
| #include <wx/debug.h>                         // for wxASSERT_MSG | |
| #include <wx/wx.h>                            // for wxPoint, wxSize, wxArra... | |
|  | |
| 
 | |
| /* class BRDITEMS_PLOTTER is a helper class to plot board items | |
|  * and a group of board items | |
|  */ | |
| 
 | |
| COLOR4D BRDITEMS_PLOTTER::getColor( LAYER_NUM aLayer ) | |
| { | |
|     COLOR4D color = ColorSettings()->GetColor( aLayer ); | |
| 
 | |
|     // A hack to avoid plotting a white item in white color, expecting the paper | |
|     // is also white: use a non white color: | |
|     if( color == COLOR4D::WHITE ) | |
|         color = COLOR4D( LIGHTGRAY ); | |
| 
 | |
|     return color; | |
| } | |
| 
 | |
| 
 | |
| void BRDITEMS_PLOTTER::PlotPad( PAD* aPad, COLOR4D aColor, OUTLINE_MODE aPlotMode ) | |
| { | |
|     wxPoint shape_pos = aPad->ShapePos(); | |
|     GBR_METADATA gbr_metadata; | |
| 
 | |
|     bool plotOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any(); | |
|     bool plotOnExternalCopperLayer = ( m_layerMask & LSET::ExternalCuMask() ).any(); | |
| 
 | |
|     // Pad not on the solder mask layer cannot be soldered. | |
|     // therefore it can have a specific aperture attribute. | |
|     // Not yet in use. | |
|     // bool isPadOnBoardTechLayers = ( aPad->GetLayerSet() & LSET::AllBoardTechMask() ).any(); | |
|  | |
|     gbr_metadata.SetCmpReference( aPad->GetParent()->GetReference() ); | |
| 
 | |
|     if( plotOnCopperLayer ) | |
|     { | |
|         gbr_metadata.SetNetAttribType( GBR_NETINFO_ALL ); | |
|         gbr_metadata.SetCopper( true ); | |
|         // Gives a default attribute, for instance for pads used as tracks in net ties: | |
|         // Connector pads and SMD pads are on external layers | |
|         // if on internal layers, they are certainly used as net tie | |
|         // and are similar to tracks: just conductor items | |
|         gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); | |
| 
 | |
|         const bool useUTF8 = false; | |
|         const bool useQuoting = false; | |
|         gbr_metadata.SetPadName( aPad->GetName(), useUTF8, useQuoting ); | |
| 
 | |
|         if( !aPad->GetName().IsEmpty() ) | |
|             gbr_metadata.SetPadPinFunction( aPad->GetPinFunction(), useUTF8, useQuoting ); | |
| 
 | |
|         gbr_metadata.SetNetName( aPad->GetNetname() ); | |
| 
 | |
|         // Some pads are mechanical pads ( through hole or smd ) | |
|         // when this is the case, they have no pad name and/or are not plated. | |
|         // In this case gerber files have slightly different attributes. | |
|         if( aPad->GetAttribute() == PAD_ATTRIB_NPTH || aPad->GetName().IsEmpty() ) | |
|             gbr_metadata.m_NetlistMetadata.m_NotInNet = true; | |
| 
 | |
|         if( !plotOnExternalCopperLayer ) | |
|         { | |
|             // the .P object attribute (GBR_NETLIST_METADATA::GBR_NETINFO_PAD) | |
|             // is used on outer layers, unless the component is embedded | |
|             // or a "etched" component (fp only drawn, not a physical component) | |
|             // Currently, Pcbnew does not handle embedded component, so we disable the .P | |
|             // attribute on internal layers | |
|             // Note the Gerber doc is not really clear about through holes pads about the .P | |
|             gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET | | |
|                                            GBR_NETLIST_METADATA::GBR_NETINFO_CMP ); | |
| 
 | |
|         } | |
| 
 | |
|         // Some attributes are reserved to the external copper layers: | |
|         // GBR_APERTURE_ATTRIB_CONNECTORPAD and GBR_APERTURE_ATTRIB_SMDPAD_CUDEF | |
|         // for instance. | |
|         // Pad with type PAD_ATTRIB_CONN or PAD_ATTRIB_SMD that is not on outer layer | |
|         // has its aperture attribute set to GBR_APERTURE_ATTRIB_CONDUCTOR | |
|         switch( aPad->GetAttribute() ) | |
|         { | |
|         case PAD_ATTRIB_NPTH:       // Mechanical pad through hole | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_WASHERPAD ); | |
|             break; | |
| 
 | |
|         case PAD_ATTRIB_PTH :       // Pad through hole, a hole is also expected | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_COMPONENTPAD ); | |
|             break; | |
| 
 | |
|         case PAD_ATTRIB_CONN:       // Connector pads, no solder paste but with solder mask. | |
|             if( plotOnExternalCopperLayer ) | |
|                 gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONNECTORPAD ); | |
|             break; | |
| 
 | |
|         case PAD_ATTRIB_SMD:        // SMD pads (on external copper layer only) | |
|                                     // with solder paste and mask | |
|             if( plotOnExternalCopperLayer ) | |
|                 gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_SMDPAD_CUDEF ); | |
|             break; | |
|         } | |
| 
 | |
|         // Fabrication properties can have specific GBR_APERTURE_METADATA options | |
|         // that replace previous aperture attribute: | |
|         switch( aPad->GetProperty() ) | |
|         { | |
|         case PAD_PROP_BGA:          // Only applicable to outer layers | |
|             if( plotOnExternalCopperLayer ) | |
|                 gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_BGAPAD_CUDEF ); | |
|             break; | |
| 
 | |
|         case PAD_PROP_FIDUCIAL_GLBL: | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_FIDUCIAL_GLBL ); | |
|             break; | |
| 
 | |
|         case PAD_PROP_FIDUCIAL_LOCAL: | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_FIDUCIAL_LOCAL ); | |
|             break; | |
| 
 | |
|         case PAD_PROP_TESTPOINT:    // Only applicable to outer layers | |
|             if( plotOnExternalCopperLayer ) | |
|                 gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_TESTPOINT ); | |
|             break; | |
| 
 | |
|         case PAD_PROP_HEATSINK: | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_HEATSINKPAD ); | |
|             break; | |
| 
 | |
|         case PAD_PROP_CASTELLATED: | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CASTELLATEDPAD ); | |
|             break; | |
| 
 | |
|         case PAD_PROP_NONE: | |
|             break; | |
|         } | |
| 
 | |
|         // Ensure NPTH pads have *always* the GBR_APERTURE_ATTRIB_WASHERPAD attribute | |
|         if( aPad->GetAttribute() == PAD_ATTRIB_NPTH ) | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_WASHERPAD ); | |
|     } | |
|     else | |
|     { | |
|         gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP ); | |
|     } | |
| 
 | |
|     // Set plot color (change WHITE to LIGHTGRAY because | |
|     // the white items are not seen on a white paper or screen | |
|     m_plotter->SetColor( aColor != WHITE ? aColor : LIGHTGRAY); | |
| 
 | |
|     if( aPlotMode == SKETCH ) | |
|         m_plotter->SetCurrentLineWidth( GetSketchPadLineWidth(), &gbr_metadata ); | |
| 
 | |
|     switch( aPad->GetShape() ) | |
|     { | |
|     case PAD_SHAPE_CIRCLE: | |
|         m_plotter->FlashPadCircle( shape_pos, aPad->GetSize().x, aPlotMode, &gbr_metadata ); | |
|         break; | |
| 
 | |
|     case PAD_SHAPE_OVAL: | |
|         m_plotter->FlashPadOval( shape_pos, aPad->GetSize(), aPad->GetOrientation(), aPlotMode, | |
|                                  &gbr_metadata ); | |
|         break; | |
| 
 | |
|     case PAD_SHAPE_RECT: | |
|         m_plotter->FlashPadRect( shape_pos, aPad->GetSize(), aPad->GetOrientation(), aPlotMode, | |
|                                  &gbr_metadata ); | |
|         break; | |
| 
 | |
|     case PAD_SHAPE_ROUNDRECT: | |
|         m_plotter->FlashPadRoundRect( shape_pos, aPad->GetSize(), aPad->GetRoundRectCornerRadius(), | |
|                                       aPad->GetOrientation(), aPlotMode, &gbr_metadata ); | |
|         break; | |
| 
 | |
|     case PAD_SHAPE_TRAPEZOID: | |
|     { | |
|         // Build the pad polygon in coordinates relative to the pad | |
|         // (i.e. for a pad at pos 0,0, rot 0.0). Needed to use aperture macros, | |
|         // to be able to create a pattern common to all trapezoid pads having the same shape | |
|         wxPoint coord[4]; | |
|         // Order is lower left, lower right, upper right, upper left | |
|         wxSize half_size = aPad->GetSize()/2; | |
|         wxSize trap_delta = aPad->GetDelta()/2; | |
| 
 | |
|         coord[0] = wxPoint( -half_size.x - trap_delta.y,  half_size.y + trap_delta.x ); | |
|         coord[1] = wxPoint( half_size.x + trap_delta.y,  half_size.y - trap_delta.x ); | |
|         coord[2] = wxPoint( half_size.x - trap_delta.y, -half_size.y + trap_delta.x ); | |
|         coord[3] = wxPoint( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x ); | |
| 
 | |
|         m_plotter->FlashPadTrapez( shape_pos, coord, aPad->GetOrientation(), aPlotMode, | |
|                                    &gbr_metadata ); | |
|     } | |
|         break; | |
| 
 | |
|     case PAD_SHAPE_CHAMFERED_RECT: | |
|         if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER ) | |
|         { | |
|             static_cast<GERBER_PLOTTER*>( m_plotter )->FlashPadChamferRoundRect( | |
|                                     shape_pos, aPad->GetSize(), | |
|                                     aPad->GetRoundRectCornerRadius(), | |
|                                     aPad->GetChamferRectRatio(), | |
|                                     aPad->GetChamferPositions(), | |
|                                     aPad->GetOrientation(), aPlotMode, &gbr_metadata ); | |
|             break; | |
|         } | |
|         KI_FALLTHROUGH; | |
| 
 | |
|     default: | |
|     case PAD_SHAPE_CUSTOM: | |
|     { | |
|         const std::shared_ptr<SHAPE_POLY_SET>& polygons = aPad->GetEffectivePolygon(); | |
| 
 | |
|         if( polygons->OutlineCount() ) | |
|         { | |
|             m_plotter->FlashPadCustom( shape_pos, aPad->GetSize(), polygons.get(), aPlotMode, | |
|                                        &gbr_metadata ); | |
|         } | |
|     } | |
|         break; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void BRDITEMS_PLOTTER::PlotFootprintTextItems( FOOTPRINT* aFootprint ) | |
| { | |
|     FP_TEXT*  textItem = &aFootprint->Reference(); | |
|     LAYER_NUM textLayer = textItem->GetLayer(); | |
| 
 | |
|     // Reference and value are specfic items, not in graphic items list | |
|     if( GetPlotReference() && m_layerMask[textLayer] | |
|         && ( textItem->IsVisible() || GetPlotInvisibleText() ) ) | |
|     { | |
|         PlotFootprintTextItem( textItem, getColor( textLayer ) ); | |
|     } | |
| 
 | |
|     textItem = &aFootprint->Value(); | |
|     textLayer = textItem->GetLayer(); | |
| 
 | |
|     if( GetPlotValue() && m_layerMask[textLayer] | |
|         && ( textItem->IsVisible() || GetPlotInvisibleText() ) ) | |
|     { | |
|         PlotFootprintTextItem( textItem, getColor( textLayer ) ); | |
|     } | |
| 
 | |
|     for( BOARD_ITEM* item : aFootprint->GraphicalItems() ) | |
|     { | |
|         textItem = dyn_cast<FP_TEXT*>( item ); | |
| 
 | |
|         if( !textItem ) | |
|             continue; | |
| 
 | |
|         if( !textItem->IsVisible() ) | |
|             continue; | |
| 
 | |
|         textLayer = textItem->GetLayer(); | |
| 
 | |
|         if( textLayer == Edge_Cuts || textLayer >= PCB_LAYER_ID_COUNT ) | |
|             continue; | |
| 
 | |
|         if( !m_layerMask[textLayer] ) | |
|             continue; | |
| 
 | |
|         if( textItem->GetText() == wxT( "${REFERENCE}" ) && !GetPlotReference() ) | |
|             continue; | |
| 
 | |
|         if( textItem->GetText() == wxT( "${VALUE}" ) && !GetPlotValue() ) | |
|             continue; | |
| 
 | |
|         PlotFootprintTextItem( textItem, getColor( textLayer ) ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| // plot items like text and graphics, but not tracks and footprints | |
| void BRDITEMS_PLOTTER::PlotBoardGraphicItems() | |
| { | |
|     for( BOARD_ITEM* item : m_board->Drawings() ) | |
|     { | |
|         switch( item->Type() ) | |
|         { | |
|         case PCB_SHAPE_T: | |
|             PlotPcbShape( (PCB_SHAPE*) item ); | |
|             break; | |
| 
 | |
|         case PCB_TEXT_T: | |
|             if( item->GetLayer() != Edge_Cuts ) | |
|                 PlotPcbText( (PCB_TEXT*) item ); | |
| 
 | |
|             break; | |
| 
 | |
|         case PCB_DIM_ALIGNED_T: | |
|         case PCB_DIM_CENTER_T: | |
|         case PCB_DIM_ORTHOGONAL_T: | |
|         case PCB_DIM_LEADER_T: | |
|             if( item->GetLayer() != Edge_Cuts ) | |
|                 PlotDimension( (DIMENSION_BASE*) item ); | |
| 
 | |
|             break; | |
| 
 | |
|         case PCB_TARGET_T: | |
|             PlotPcbTarget( (PCB_TARGET*) item ); | |
|             break; | |
| 
 | |
|         default: | |
|             break; | |
|         } | |
|     } | |
| } | |
| 
 | |
| void BRDITEMS_PLOTTER::PlotFootprintTextItem( FP_TEXT* aTextMod, COLOR4D aColor ) | |
| { | |
|     if( aColor == COLOR4D::WHITE ) | |
|         aColor = COLOR4D( LIGHTGRAY ); | |
| 
 | |
|     m_plotter->SetColor( aColor ); | |
| 
 | |
|     // calculate some text parameters : | |
|     wxSize  size = aTextMod->GetTextSize(); | |
|     wxPoint pos = aTextMod->GetTextPos(); | |
|     double  orient = aTextMod->GetDrawRotation(); | |
|     int     thickness = aTextMod->GetEffectiveTextPenWidth(); | |
| 
 | |
|     if( aTextMod->IsMirrored() ) | |
|         size.x = -size.x;  // Text is mirrored | |
|  | |
|     // Non bold texts thickness is clamped at 1/6 char size by the low level draw function. | |
|     // but in Pcbnew we do not manage bold texts and thickness up to 1/4 char size | |
|     // (like bold text) and we manage the thickness. | |
|     // So we set bold flag to true | |
|     bool allow_bold = true; | |
| 
 | |
|     GBR_METADATA gbr_metadata; | |
|     gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP ); | |
|     FOOTPRINT* parent = static_cast<FOOTPRINT*> ( aTextMod->GetParent() ); | |
|     gbr_metadata.SetCmpReference( parent->GetReference() ); | |
| 
 | |
|     m_plotter->SetCurrentLineWidth( thickness ); | |
| 
 | |
|     m_plotter->Text( pos, aColor, aTextMod->GetShownText(), orient, size, | |
|                      aTextMod->GetHorizJustify(), aTextMod->GetVertJustify(), thickness, | |
|                      aTextMod->IsItalic(), allow_bold, false, &gbr_metadata ); | |
| } | |
| 
 | |
| 
 | |
| void BRDITEMS_PLOTTER::PlotDimension( DIMENSION_BASE* aDim ) | |
| { | |
|     if( !m_layerMask[aDim->GetLayer()] ) | |
|         return; | |
| 
 | |
|     PCB_SHAPE draw; | |
| 
 | |
|     draw.SetWidth( aDim->GetLineThickness() ); | |
|     draw.SetLayer( aDim->GetLayer() ); | |
| 
 | |
|     COLOR4D color = ColorSettings()->GetColor( aDim->GetLayer() ); | |
| 
 | |
|     // Set plot color (change WHITE to LIGHTGRAY because | |
|     // the white items are not seen on a white paper or screen | |
|     m_plotter->SetColor( color != WHITE ? color : LIGHTGRAY); | |
| 
 | |
|     PlotPcbText( &aDim->Text() ); | |
| 
 | |
|     for( const std::shared_ptr<SHAPE>& shape : aDim->GetShapes() ) | |
|     { | |
|         switch( shape->Type() ) | |
|         { | |
|         case SH_SEGMENT: | |
|         { | |
|             const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg(); | |
| 
 | |
|             draw.SetShape( S_SEGMENT ); | |
|             draw.SetStart( wxPoint( seg.A ) ); | |
|             draw.SetEnd( wxPoint( seg.B ) ); | |
| 
 | |
|             PlotPcbShape( &draw ); | |
|             break; | |
|         } | |
| 
 | |
|         case SH_CIRCLE: | |
|         { | |
|             wxPoint start( shape->Centre() ); | |
|             int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius(); | |
| 
 | |
|             draw.SetShape( S_CIRCLE ); | |
|             draw.SetFilled( false ); | |
|             draw.SetStart( start ); | |
|             draw.SetEnd( wxPoint( start.x + radius, start.y ) ); | |
| 
 | |
|             PlotPcbShape( &draw ); | |
|             break; | |
|         } | |
| 
 | |
|         default: | |
|             break; | |
|         } | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void BRDITEMS_PLOTTER::PlotPcbTarget( PCB_TARGET* aMire ) | |
| { | |
|     int     dx1, dx2, dy1, dy2, radius; | |
| 
 | |
|     if( !m_layerMask[aMire->GetLayer()] ) | |
|         return; | |
| 
 | |
|     m_plotter->SetColor( getColor( aMire->GetLayer() ) ); | |
| 
 | |
|     PCB_SHAPE  draw; | |
| 
 | |
|     draw.SetShape( S_CIRCLE ); | |
|     draw.SetFilled( false ); | |
|     draw.SetWidth( aMire->GetWidth() ); | |
|     draw.SetLayer( aMire->GetLayer() ); | |
|     draw.SetStart( aMire->GetPosition() ); | |
|     radius = aMire->GetSize() / 3; | |
| 
 | |
|     if( aMire->GetShape() )   // shape X | |
|         radius = aMire->GetSize() / 2; | |
| 
 | |
|     // Draw the circle | |
|     draw.SetEnd( wxPoint( draw.GetStart().x + radius, draw.GetStart().y ) ); | |
| 
 | |
|     PlotPcbShape( &draw ); | |
| 
 | |
|     draw.SetShape( S_SEGMENT ); | |
| 
 | |
|     radius = aMire->GetSize() / 2; | |
|     dx1    = radius; | |
|     dy1    = 0; | |
|     dx2    = 0; | |
|     dy2    = radius; | |
| 
 | |
|     if( aMire->GetShape() )    // Shape X | |
|     { | |
|         dx1 = dy1 = radius; | |
|         dx2 = dx1; | |
|         dy2 = -dy1; | |
|     } | |
| 
 | |
|     wxPoint mirePos( aMire->GetPosition() ); | |
| 
 | |
|     // Draw the X or + shape: | |
|     draw.SetStart( wxPoint( mirePos.x - dx1, mirePos.y - dy1 ) ); | |
|     draw.SetEnd(   wxPoint( mirePos.x + dx1, mirePos.y + dy1 ) ); | |
|     PlotPcbShape( &draw ); | |
| 
 | |
|     draw.SetStart( wxPoint( mirePos.x - dx2, mirePos.y - dy2 ) ); | |
|     draw.SetEnd(   wxPoint( mirePos.x + dx2, mirePos.y + dy2 ) ); | |
|     PlotPcbShape( &draw ); | |
| } | |
| 
 | |
| 
 | |
| // Plot footprints graphic items (outlines) | |
| void BRDITEMS_PLOTTER::PlotFootprintGraphicItems( FOOTPRINT* aFootprint ) | |
| { | |
|     for( BOARD_ITEM* item : aFootprint->GraphicalItems() ) | |
|     { | |
|         FP_SHAPE* shape = dynamic_cast<FP_SHAPE*>( item ); | |
| 
 | |
|         if( shape && m_layerMask[ shape->GetLayer() ] ) | |
|             PlotFootprintGraphicItem( shape ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| //* Plot a graphic item (outline) relative to a footprint | |
| void BRDITEMS_PLOTTER::PlotFootprintGraphicItem( FP_SHAPE* aShape ) | |
| { | |
|     if( aShape->Type() != PCB_FP_SHAPE_T ) | |
|         return; | |
| 
 | |
|     m_plotter->SetColor( getColor( aShape->GetLayer() ) ); | |
| 
 | |
|     bool    sketch = GetPlotMode() == SKETCH; | |
|     int     thickness = aShape->GetWidth(); | |
|     wxPoint pos( aShape->GetStart() ); | |
|     wxPoint end( aShape->GetEnd() ); | |
| 
 | |
|     GBR_METADATA gbr_metadata; | |
|     gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP ); | |
|     FOOTPRINT* parent = static_cast<FOOTPRINT*> ( aShape->GetParent() ); | |
|     gbr_metadata.SetCmpReference( parent->GetReference() ); | |
| 
 | |
|     bool isOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any(); | |
| 
 | |
|     if( isOnCopperLayer ) | |
|     { | |
|         gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_ETCHEDCMP ); | |
|         gbr_metadata.SetCopper( true ); | |
|     } | |
|     else if( aShape->GetLayer() == Edge_Cuts )   // happens also when plotting copper layers | |
|     { | |
|         gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_EDGECUT ); | |
|     } | |
| 
 | |
|     int     radius;             // Circle/arc radius. | |
|  | |
|     switch( aShape->GetShape() ) | |
|     { | |
|     case S_SEGMENT: | |
|         m_plotter->ThickSegment( pos, end, thickness, GetPlotMode(), &gbr_metadata ); | |
|         break; | |
| 
 | |
|     case S_RECT: | |
|     { | |
|         std::vector<wxPoint> pts = aShape->GetRectCorners(); | |
| 
 | |
|         if( sketch || thickness > 0 ) | |
|         { | |
|             m_plotter->ThickSegment( pts[0], pts[1], thickness, GetPlotMode(), &gbr_metadata ); | |
|             m_plotter->ThickSegment( pts[1], pts[2], thickness, GetPlotMode(), &gbr_metadata ); | |
|             m_plotter->ThickSegment( pts[2], pts[3], thickness, GetPlotMode(), &gbr_metadata ); | |
|             m_plotter->ThickSegment( pts[3], pts[0], thickness, GetPlotMode(), &gbr_metadata ); | |
|         } | |
| 
 | |
|         if( !sketch && aShape->IsFilled() ) | |
|         { | |
|             SHAPE_LINE_CHAIN poly; | |
| 
 | |
|             for( const wxPoint& pt : pts ) | |
|                 poly.Append( pt ); | |
| 
 | |
|             m_plotter->PlotPoly( poly, FILL_TYPE::FILLED_SHAPE, -1, &gbr_metadata ); | |
|         } | |
|     } | |
|         break; | |
| 
 | |
|     case S_CIRCLE: | |
|         radius = KiROUND( GetLineLength( end, pos ) ); | |
| 
 | |
|         if( aShape->IsFilled() ) | |
|             m_plotter->FilledCircle( pos, radius * 2 + thickness, GetPlotMode(), &gbr_metadata ); | |
|         else | |
|             m_plotter->ThickCircle( pos, radius * 2, thickness, GetPlotMode(), &gbr_metadata ); | |
| 
 | |
|         break; | |
| 
 | |
|     case S_ARC: | |
|     { | |
|         radius = KiROUND( GetLineLength( end, pos ) ); | |
|         double startAngle  = ArcTangente( end.y - pos.y, end.x - pos.x ); | |
|         double endAngle = startAngle + aShape->GetAngle(); | |
| 
 | |
|         // when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg | |
|         if( std::abs( aShape->GetAngle() ) == 3600.0 ) | |
|         { | |
|             m_plotter->ThickCircle( pos, radius * 2, thickness, GetPlotMode(), &gbr_metadata ); | |
|         } | |
|         else | |
|         { | |
|             m_plotter->ThickArc( pos, -endAngle, -startAngle, radius, thickness, GetPlotMode(), | |
|                                  &gbr_metadata ); | |
|         } | |
|     } | |
|         break; | |
| 
 | |
|     case S_POLYGON: | |
|         if( aShape->IsPolyShapeValid() ) | |
|         { | |
|             const std::vector<wxPoint> &polyPoints = aShape->BuildPolyPointsList(); | |
| 
 | |
|             // We must compute board coordinates from m_PolyList which are relative to the parent | |
|             // position at orientation 0 | |
|             FOOTPRINT *parentFootprint = aShape->GetParentFootprint(); | |
| 
 | |
|             std::vector<wxPoint> cornerList; | |
| 
 | |
|             cornerList.reserve( polyPoints.size() ); | |
| 
 | |
|             for( wxPoint corner : polyPoints ) | |
|             { | |
|                 if( parentFootprint ) | |
|                 { | |
|                     RotatePoint( &corner, parentFootprint->GetOrientation() ); | |
|                     corner += parentFootprint->GetPosition(); | |
|                 } | |
| 
 | |
|                 cornerList.push_back( corner ); | |
|             } | |
| 
 | |
|             if( sketch || thickness > 0 ) | |
|             { | |
|                 for( size_t i = 1; i < cornerList.size(); i++ ) | |
|                 { | |
|                     m_plotter->ThickSegment( cornerList[i - 1], cornerList[i], thickness, | |
|                                              GetPlotMode(), &gbr_metadata ); | |
|                 } | |
| 
 | |
|                 m_plotter->ThickSegment( cornerList.back(), cornerList.front(), thickness, | |
|                                          GetPlotMode(), &gbr_metadata ); | |
| 
 | |
|             } | |
| 
 | |
|             if( !sketch && aShape->IsFilled() ) | |
|             { | |
|                 // This must be simplified and fractured to prevent overlapping polygons | |
|                 // from generating invalid Gerber files | |
|  | |
|                 SHAPE_LINE_CHAIN line( cornerList ); | |
|                 SHAPE_POLY_SET tmpPoly; | |
| 
 | |
|                 line.SetClosed( true ); | |
|                 tmpPoly.AddOutline( line ); | |
|                 tmpPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); | |
| 
 | |
|                 for( int jj = 0; jj < tmpPoly.OutlineCount(); ++jj ) | |
|                 { | |
|                     SHAPE_LINE_CHAIN &poly = tmpPoly.Outline( jj ); | |
|                     m_plotter->PlotPoly( poly, FILL_TYPE::FILLED_SHAPE, thickness, &gbr_metadata ); | |
|                 } | |
|             } | |
|         } | |
|         break; | |
| 
 | |
|     case S_CURVE: | |
|         m_plotter->BezierCurve( aShape->GetStart(), aShape->GetBezControl1(), | |
|                                 aShape->GetBezControl2(), aShape->GetEnd(), 0, thickness ); | |
|         break; | |
| 
 | |
|     default: | |
|         wxASSERT_MSG( false, "Unhandled FP_SHAPE shape" ); | |
|         break; | |
|     } | |
| } | |
| 
 | |
| 
 | |
| // Plot a PCB Text, i.e. a text found on a copper or technical layer | |
| void BRDITEMS_PLOTTER::PlotPcbText( PCB_TEXT* aText ) | |
| { | |
|     wxString shownText( aText->GetShownText() ); | |
| 
 | |
|     if( shownText.IsEmpty() ) | |
|         return; | |
| 
 | |
|     if( !m_layerMask[aText->GetLayer()] ) | |
|         return; | |
| 
 | |
|     GBR_METADATA gbr_metadata; | |
| 
 | |
|     if( IsCopperLayer( aText->GetLayer() ) ) | |
|         gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR ); | |
| 
 | |
|     COLOR4D color = getColor( aText->GetLayer() ); | |
|     m_plotter->SetColor( color ); | |
| 
 | |
|     wxSize  size      = aText->GetTextSize(); | |
|     wxPoint pos       = aText->GetTextPos(); | |
|     double  orient    = aText->GetTextAngle(); | |
|     int     thickness = aText->GetEffectiveTextPenWidth(); | |
| 
 | |
|     if( aText->IsMirrored() ) | |
|         size.x = -size.x; | |
| 
 | |
|     // Non bold texts thickness is clamped at 1/6 char size by the low level draw function. | |
|     // but in Pcbnew we do not manage bold texts and thickness up to 1/4 char size | |
|     // (like bold text) and we manage the thickness. | |
|     // So we set bold flag to true | |
|     bool allow_bold = true; | |
| 
 | |
|     m_plotter->SetCurrentLineWidth( thickness ); | |
| 
 | |
|     if( aText->IsMultilineAllowed() ) | |
|     { | |
|         std::vector<wxPoint> positions; | |
|         wxArrayString strings_list; | |
|         wxStringSplit( shownText, strings_list, '\n' ); | |
|         positions.reserve(  strings_list.Count() ); | |
| 
 | |
|         aText->GetLinePositions( positions, strings_list.Count() ); | |
| 
 | |
|         for( unsigned ii = 0; ii <  strings_list.Count(); ii++ ) | |
|         { | |
|             wxString& txt =  strings_list.Item( ii ); | |
|             m_plotter->Text( positions[ii], color, txt, orient, size, aText->GetHorizJustify(), | |
|                              aText->GetVertJustify(), thickness, aText->IsItalic(), | |
|                              allow_bold, false, &gbr_metadata ); | |
|         } | |
|     } | |
|     else | |
|     { | |
|         m_plotter->Text( pos, color, shownText, orient, size, aText->GetHorizJustify(), | |
|                          aText->GetVertJustify(), thickness, aText->IsItalic(), allow_bold, | |
|                          false, &gbr_metadata ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE* aZone, SHAPE_POLY_SET& polysList ) | |
| { | |
|     if( polysList.IsEmpty() ) | |
|         return; | |
| 
 | |
|     GBR_METADATA gbr_metadata; | |
| 
 | |
|     bool isOnCopperLayer = aZone->IsOnCopperLayer(); | |
| 
 | |
|     if( isOnCopperLayer ) | |
|     { | |
|         gbr_metadata.SetNetName( aZone->GetNetname() ); | |
|         gbr_metadata.SetCopper( true ); | |
| 
 | |
|         // Zones with no net name can exist. | |
|         // they are not used to connect items, so the aperture attribute cannot | |
|         // be set as conductor | |
|         if( aZone->GetNetname().IsEmpty() ) | |
|         { | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR ); | |
|         } | |
|         else | |
|         { | |
|             gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR ); | |
|             gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET ); | |
|         } | |
|     } | |
| 
 | |
|     // We need a buffer to store corners coordinates: | |
|     std::vector< wxPoint > cornerList; | |
| 
 | |
|     m_plotter->SetColor( getColor( aZone->GetLayer() ) ); | |
| 
 | |
|     m_plotter->StartBlock( nullptr );    // Clean current object attributes | |
|  | |
|     /* Plot all filled areas: filled areas have a filled area and a thick | |
|      * outline (depending on the fill area option we must plot the filled area itself | |
|      * and plot the thick outline itself, if the thickness has meaning (at least is > 1) | |
|      * | |
|      * in non filled mode the outline is plotted, but not the filling items | |
|      */ | |
|     int outline_thickness = aZone->GetFilledPolysUseThickness() ? aZone->GetMinThickness() : 0; | |
| 
 | |
|     for( int idx = 0; idx < polysList.OutlineCount(); ++idx ) | |
|     { | |
|         SHAPE_LINE_CHAIN& outline = polysList.Outline( idx ); | |
| 
 | |
|         cornerList.clear(); | |
|         cornerList.reserve( outline.PointCount() ); | |
| 
 | |
|         for( int ic = 0; ic < outline.PointCount(); ++ic ) | |
|         { | |
|             cornerList.emplace_back( wxPoint( outline.CPoint( ic ) ) ); | |
|         } | |
| 
 | |
|         if( cornerList.size() )   // Plot the current filled area outline | |
|         { | |
|             // First, close the outline | |
|             if( cornerList[0] != cornerList[cornerList.size() - 1] ) | |
|                 cornerList.push_back( cornerList[0] ); | |
| 
 | |
|             // Plot the current filled area (as region for Gerber plotter | |
|             // to manage attributes) and its outline for thick outline | |
|             if( GetPlotMode() == FILLED ) | |
|             { | |
|                 if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER ) | |
|                 { | |
|                     if( outline_thickness > 0 ) | |
|                     { | |
|                         m_plotter->PlotPoly( cornerList, FILL_TYPE::NO_FILL, outline_thickness, | |
|                                              &gbr_metadata ); | |
|                     } | |
| 
 | |
|                     static_cast<GERBER_PLOTTER*>( m_plotter )->PlotGerberRegion( cornerList, | |
|                                                                                  &gbr_metadata ); | |
|                 } | |
|                 else | |
|                 { | |
|                     m_plotter->PlotPoly( cornerList, FILL_TYPE::FILLED_SHAPE, outline_thickness, | |
|                                          &gbr_metadata ); | |
|                 } | |
|             } | |
|             else | |
|             { | |
|                 if( outline_thickness ) | |
|                 { | |
|                     for( unsigned jj = 1; jj < cornerList.size(); jj++ ) | |
|                     { | |
|                         m_plotter->ThickSegment( cornerList[jj -1], cornerList[jj], | |
|                                                  outline_thickness, GetPlotMode(), &gbr_metadata ); | |
|                     } | |
|                 } | |
| 
 | |
|                 m_plotter->SetCurrentLineWidth( -1 ); | |
|             } | |
|         } | |
|     } | |
| 
 | |
|     m_plotter->EndBlock( nullptr );    // Clear object attributes | |
| } | |
| 
 | |
| 
 | |
| /* Plot items type PCB_SHAPE on layers allowed by aLayerMask | |
|  */ | |
| void BRDITEMS_PLOTTER::PlotPcbShape( PCB_SHAPE* aShape ) | |
| { | |
|     if( !m_layerMask[aShape->GetLayer()] ) | |
|         return; | |
| 
 | |
|     int     radius = 0; | |
|     double  StAngle = 0, EndAngle = 0; | |
|     bool    sketch = GetPlotMode() == SKETCH; | |
|     int     thickness = aShape->GetWidth(); | |
| 
 | |
|     m_plotter->SetColor( getColor( aShape->GetLayer() ) ); | |
| 
 | |
|     wxPoint start( aShape->GetStart() ); | |
|     wxPoint end( aShape->GetEnd() ); | |
| 
 | |
|     GBR_METADATA gbr_metadata; | |
| 
 | |
|     if( aShape->GetLayer() == Edge_Cuts ) | |
|         gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_EDGECUT ); | |
| 
 | |
|     if( IsCopperLayer( aShape->GetLayer() ) ) | |
|         // Graphic items (PCB_SHAPE, TEXT) having no net have the NonConductor attribute | |
|         // Graphic items having a net have the Conductor attribute, but are not (yet?) | |
|         // supported in Pcbnew | |
|         gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR ); | |
| 
 | |
|     switch( aShape->GetShape() ) | |
|     { | |
|     case S_SEGMENT: | |
|         m_plotter->ThickSegment( start, end, thickness, GetPlotMode(), &gbr_metadata ); | |
|         break; | |
| 
 | |
|     case S_CIRCLE: | |
|         radius = KiROUND( GetLineLength( end, start ) ); | |
| 
 | |
|         if( aShape->IsFilled() ) | |
|             m_plotter->FilledCircle( start, radius * 2 + thickness, GetPlotMode(), &gbr_metadata ); | |
|         else | |
|             m_plotter->ThickCircle( start, radius * 2, thickness, GetPlotMode(), &gbr_metadata ); | |
| 
 | |
|         break; | |
| 
 | |
|     case S_ARC: | |
|         radius = KiROUND( GetLineLength( end, start ) ); | |
|         StAngle  = ArcTangente( end.y - start.y, end.x - start.x ); | |
|         EndAngle = StAngle + aShape->GetAngle(); | |
| 
 | |
|         // when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg | |
|         if( std::abs( aShape->GetAngle() ) == 3600.0 ) | |
|         { | |
|             m_plotter->ThickCircle( start, radius * 2, thickness, GetPlotMode(), &gbr_metadata ); | |
|         } | |
|         else | |
|         { | |
|             m_plotter->ThickArc( start, -EndAngle, -StAngle, radius, thickness, GetPlotMode(), | |
|                                  &gbr_metadata ); | |
|         } | |
|         break; | |
| 
 | |
|     case S_CURVE: | |
|         m_plotter->BezierCurve( aShape->GetStart(), aShape->GetBezControl1(), | |
|                                 aShape->GetBezControl2(), aShape->GetEnd(), 0, thickness ); | |
|         break; | |
| 
 | |
|     case S_POLYGON: | |
|         if( aShape->IsPolyShapeValid() ) | |
|         { | |
|             if( sketch || thickness > 0 ) | |
|             { | |
|                 for( auto it = aShape->GetPolyShape().CIterateSegments( 0 ); it; it++ ) | |
|                 { | |
|                     auto seg = it.Get(); | |
|                     m_plotter->ThickSegment( wxPoint( seg.A ), wxPoint( seg.B ), | |
|                                              thickness, GetPlotMode(), &gbr_metadata ); | |
|                 } | |
|             } | |
| 
 | |
|             if( !sketch && aShape->IsFilled() ) | |
|             { | |
|                 m_plotter->SetCurrentLineWidth( thickness, &gbr_metadata ); | |
|                 // Draw the polygon: only one polygon is expected | |
|                 // However we provide a multi polygon shape drawing | |
|                 // ( for the future or to show a non expected shape ) | |
|                 // This must be simplified and fractured to prevent overlapping polygons | |
|                 // from generating invalid Gerber files | |
|                 auto tmpPoly = SHAPE_POLY_SET( aShape->GetPolyShape() ); | |
|                 tmpPoly.Fracture( SHAPE_POLY_SET::PM_FAST ); | |
| 
 | |
|                 for( int jj = 0; jj < tmpPoly.OutlineCount(); ++jj ) | |
|                 { | |
|                     SHAPE_LINE_CHAIN& poly = tmpPoly.Outline( jj ); | |
|                     m_plotter->PlotPoly( poly, FILL_TYPE::FILLED_SHAPE, thickness, &gbr_metadata ); | |
|                 } | |
|             } | |
|         } | |
|         break; | |
| 
 | |
|     case S_RECT: | |
|     { | |
|         std::vector<wxPoint> pts = aShape->GetRectCorners(); | |
| 
 | |
|         if( sketch || thickness > 0 ) | |
|         { | |
|             m_plotter->ThickSegment( pts[0], pts[1], thickness, GetPlotMode(), &gbr_metadata ); | |
|             m_plotter->ThickSegment( pts[1], pts[2], thickness, GetPlotMode(), &gbr_metadata ); | |
|             m_plotter->ThickSegment( pts[2], pts[3], thickness, GetPlotMode(), &gbr_metadata ); | |
|             m_plotter->ThickSegment( pts[3], pts[0], thickness, GetPlotMode(), &gbr_metadata ); | |
|         } | |
| 
 | |
|         if( !sketch && aShape->IsFilled() ) | |
|         { | |
|             SHAPE_LINE_CHAIN poly; | |
| 
 | |
|             for( const wxPoint& pt : pts ) | |
|                 poly.Append( pt ); | |
| 
 | |
|             m_plotter->PlotPoly( poly, FILL_TYPE::FILLED_SHAPE, -1, &gbr_metadata ); | |
|         } | |
|     } | |
|         break; | |
| 
 | |
|     default: | |
|         wxASSERT_MSG( false, "Unhandled PCB_SHAPE shape" ); | |
|         m_plotter->ThickSegment( start, end, thickness, GetPlotMode(), &gbr_metadata ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| /** Helper function to plot a single drill mark. It compensate and clamp | |
|  *   the drill mark size depending on the current plot options | |
|  */ | |
| void BRDITEMS_PLOTTER::plotOneDrillMark( PAD_DRILL_SHAPE_T aDrillShape, const wxPoint &aDrillPos, | |
|                                          wxSize aDrillSize, const wxSize &aPadSize, | |
|                                          double aOrientation, int aSmallDrill ) | |
| { | |
|     // Small drill marks have no significance when applied to slots | |
|     if( aSmallDrill && aDrillShape == PAD_DRILL_SHAPE_CIRCLE ) | |
|         aDrillSize.x = std::min( aSmallDrill, aDrillSize.x ); | |
| 
 | |
|     // Round holes only have x diameter, slots have both | |
|     aDrillSize.x -= getFineWidthAdj(); | |
|     aDrillSize.x = Clamp( 1, aDrillSize.x, aPadSize.x - 1 ); | |
| 
 | |
|     if( aDrillShape == PAD_DRILL_SHAPE_OBLONG ) | |
|     { | |
|         aDrillSize.y -= getFineWidthAdj(); | |
|         aDrillSize.y = Clamp( 1, aDrillSize.y, aPadSize.y - 1 ); | |
|         m_plotter->FlashPadOval( aDrillPos, aDrillSize, aOrientation, GetPlotMode(), NULL ); | |
|     } | |
|     else | |
|     { | |
|         m_plotter->FlashPadCircle( aDrillPos, aDrillSize.x, GetPlotMode(), NULL ); | |
|     } | |
| } | |
| 
 | |
| 
 | |
| void BRDITEMS_PLOTTER::PlotDrillMarks() | |
| { | |
|     /* If small drills marks were requested prepare a clamp value to pass | |
|        to the helper function */ | |
|     int smallDrill = GetDrillMarksType() == PCB_PLOT_PARAMS::SMALL_DRILL_SHAPE  | |
|                     ? Millimeter2iu( ADVANCED_CFG::GetCfg().m_SmallDrillMarkSize ) : 0; | |
| 
 | |
|     /* In the filled trace mode drill marks are drawn white-on-black to scrape | |
|        the underlying pad. This works only for drivers supporting color change, | |
|        obviously... it means that: | |
|        - PS, SVG and PDF output is correct (i.e. you have a 'donut' pad) | |
|        - In HPGL you can't see them | |
|        - In gerbers you can't see them, too. This is arguably the right thing to | |
|          do since having drill marks and high speed drill stations is a sure | |
|          recipe for broken tools and angry manufacturers. If you *really* want them | |
|          you could start a layer with negative polarity to scrape the film. | |
|        - In DXF they go into the 'WHITE' layer. This could be useful. | |
|      */ | |
|     if( GetPlotMode() == FILLED ) | |
|          m_plotter->SetColor( WHITE ); | |
| 
 | |
|     for( TRACK* tracks : m_board->Tracks() ) | |
|     { | |
|         const VIA* via = dyn_cast<const VIA*>( tracks ); | |
| 
 | |
|         if( via ) | |
|         { | |
|             plotOneDrillMark( PAD_DRILL_SHAPE_CIRCLE, via->GetStart(), | |
|                               wxSize( via->GetDrillValue(), 0 ), | |
|                               wxSize( via->GetWidth(), 0 ), 0, smallDrill ); | |
|         } | |
|     } | |
| 
 | |
|     for( FOOTPRINT* footprint : m_board->Footprints() ) | |
|     { | |
|         for( PAD* pad : footprint->Pads() ) | |
|         { | |
|             if( pad->GetDrillSize().x == 0 ) | |
|                 continue; | |
| 
 | |
|             plotOneDrillMark( pad->GetDrillShape(), pad->GetPosition(), pad->GetDrillSize(), | |
|                               pad->GetSize(), pad->GetOrientation(), smallDrill ); | |
|         } | |
|     } | |
| 
 | |
|     if( GetPlotMode() == FILLED ) | |
|         m_plotter->SetColor( BLACK ); | |
| }
 |