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.

1238 lines
43 KiB

5 years ago
4 years ago
4 years ago
  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. #include <algorithm> // for min
  24. #include <bitset> // for bitset, operator&, __bi...
  25. #include <math.h> // for abs
  26. #include <geometry/seg.h> // for SEG
  27. #include <geometry/shape_circle.h>
  28. #include <geometry/shape_line_chain.h> // for SHAPE_LINE_CHAIN
  29. #include <geometry/shape_poly_set.h> // for SHAPE_POLY_SET, SHAPE_P...
  30. #include <geometry/shape_segment.h>
  31. #include <string_utils.h>
  32. #include <macros.h>
  33. #include <math/util.h> // for KiROUND
  34. #include <math/vector2d.h> // for VECTOR2I
  35. #include <plotters/plotter_gerber.h>
  36. #include <trigo.h>
  37. #include <font/stroke_font.h>
  38. #include <gal/gal_display_options.h>
  39. #include <callback_gal.h>
  40. #include <core/typeinfo.h> // for dyn_cast, PCB_DIMENSION_T
  41. #include <gbr_metadata.h>
  42. #include <gbr_netlist_metadata.h> // for GBR_NETLIST_METADATA
  43. #include <layer_ids.h> // for LSET, IsCopperLayer
  44. #include <lset.h>
  45. #include <pcbplot.h>
  46. #include <pcb_plot_params.h> // for PCB_PLOT_PARAMS, PCB_PL...
  47. #include <advanced_config.h>
  48. #include <pcb_dimension.h>
  49. #include <pcb_shape.h>
  50. #include <footprint.h>
  51. #include <pcb_track.h>
  52. #include <pad.h>
  53. #include <pcb_target.h>
  54. #include <pcb_text.h>
  55. #include <pcb_textbox.h>
  56. #include <pcb_tablecell.h>
  57. #include <pcb_table.h>
  58. #include <zone.h>
  59. #include <wx/debug.h> // for wxASSERT_MSG
  60. COLOR4D BRDITEMS_PLOTTER::getColor( int aLayer ) const
  61. {
  62. COLOR4D color = ColorSettings()->GetColor( aLayer );
  63. // A hack to avoid plotting a white item in white color on white paper
  64. if( color == COLOR4D::WHITE )
  65. color = COLOR4D( LIGHTGRAY );
  66. return color;
  67. }
  68. void BRDITEMS_PLOTTER::PlotPadNumber( const PAD* aPad, const COLOR4D& aColor )
  69. {
  70. wxString padNumber = UnescapeString( aPad->GetNumber() );
  71. if( padNumber.IsEmpty() )
  72. return;
  73. BOX2I padBBox = aPad->GetBoundingBox();
  74. VECTOR2I position = padBBox.Centre();
  75. VECTOR2I padsize = padBBox.GetSize();
  76. // TODO(JE) padstacks
  77. if( aPad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CUSTOM )
  78. {
  79. // See if we have a number box
  80. for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives( PADSTACK::ALL_LAYERS ) )
  81. {
  82. if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::RECTANGLE )
  83. {
  84. position = primitive->GetCenter();
  85. RotatePoint( position, aPad->GetOrientation() );
  86. position += aPad->ShapePos( PADSTACK::ALL_LAYERS );
  87. padsize.x = abs( primitive->GetBotRight().x - primitive->GetTopLeft().x );
  88. padsize.y = abs( primitive->GetBotRight().y - primitive->GetTopLeft().y );
  89. break;
  90. }
  91. }
  92. }
  93. if( aPad->GetShape( PADSTACK::ALL_LAYERS ) != PAD_SHAPE::CUSTOM )
  94. {
  95. // Don't allow a 45° rotation to bloat a pad's bounding box unnecessarily
  96. int limit = KiROUND( std::min( aPad->GetSize( PADSTACK::ALL_LAYERS ).x,
  97. aPad->GetSize( PADSTACK::ALL_LAYERS ).y ) * 1.1 );
  98. if( padsize.x > limit && padsize.y > limit )
  99. {
  100. padsize.x = limit;
  101. padsize.y = limit;
  102. }
  103. }
  104. TEXT_ATTRIBUTES textAttrs;
  105. if( padsize.x < ( padsize.y * 0.95 ) )
  106. {
  107. textAttrs.m_Angle = ANGLE_90;
  108. std::swap( padsize.x, padsize.y );
  109. }
  110. // approximate the size of the pad number text:
  111. // We use a size for at least 3 chars, to give a good look even for short numbers
  112. int tsize = KiROUND( padsize.x / std::max( PrintableCharCount( padNumber ), 3 ) );
  113. tsize = std::min( tsize, padsize.y );
  114. // enforce a max size
  115. tsize = std::min( tsize, pcbIUScale.mmToIU( 5.0 ) );
  116. textAttrs.m_Size = VECTOR2I( tsize, tsize );
  117. // use a somewhat spindly font to go with the outlined pads
  118. textAttrs.m_StrokeWidth = KiROUND( tsize / 12.0 );
  119. m_plotter->PlotText( position, aColor, padNumber, textAttrs );
  120. }
  121. void BRDITEMS_PLOTTER::PlotPad( const PAD* aPad, PCB_LAYER_ID aLayer, const COLOR4D& aColor,
  122. OUTLINE_MODE aPlotMode )
  123. {
  124. VECTOR2I shape_pos = aPad->ShapePos( aLayer );
  125. GBR_METADATA metadata;
  126. bool plotOnCopperLayer = ( m_layerMask & LSET::AllCuMask() ).any();
  127. bool plotOnExternalCopperLayer = ( m_layerMask & LSET::ExternalCuMask() ).any();
  128. // Pad not on the solder mask layer cannot be soldered.
  129. // therefore it can have a specific aperture attribute.
  130. // Not yet in use.
  131. // bool isPadOnBoardTechLayers = ( aPad->GetLayerSet() & LSET::AllBoardTechMask() ).any();
  132. metadata.SetCmpReference( aPad->GetParentFootprint()->GetReference() );
  133. if( plotOnCopperLayer )
  134. {
  135. metadata.SetNetAttribType( GBR_NETINFO_ALL );
  136. metadata.SetCopper( true );
  137. // Gives a default attribute, for instance for pads used as tracks in net ties:
  138. // Connector pads and SMD pads are on external layers
  139. // if on internal layers, they are certainly used as net tie
  140. // and are similar to tracks: just conductor items
  141. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  142. const bool useUTF8 = false;
  143. const bool useQuoting = false;
  144. metadata.SetPadName( aPad->GetNumber(), useUTF8, useQuoting );
  145. if( !aPad->GetNumber().IsEmpty() )
  146. metadata.SetPadPinFunction( aPad->GetPinFunction(), useUTF8, useQuoting );
  147. metadata.SetNetName( aPad->GetNetname() );
  148. // Some pads are mechanical pads ( through hole or smd )
  149. // when this is the case, they have no pad name and/or are not plated.
  150. // In this case gerber files have slightly different attributes.
  151. if( aPad->GetAttribute() == PAD_ATTRIB::NPTH || aPad->GetNumber().IsEmpty() )
  152. metadata.m_NetlistMetadata.m_NotInNet = true;
  153. if( !plotOnExternalCopperLayer )
  154. {
  155. // the .P object attribute (GBR_NETLIST_METADATA::GBR_NETINFO_PAD)
  156. // is used on outer layers, unless the component is embedded
  157. // or a "etched" component (fp only drawn, not a physical component)
  158. // Currently, Pcbnew does not handle embedded component, so we disable the .P
  159. // attribute on internal layers
  160. // Note the Gerber doc is not really clear about through holes pads about the .P
  161. metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET |
  162. GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
  163. }
  164. // Some attributes are reserved to the external copper layers:
  165. // GBR_APERTURE_ATTRIB_CONNECTORPAD and GBR_APERTURE_ATTRIB_SMDPAD_CUDEF
  166. // for instance.
  167. // Pad with type PAD_ATTRIB::CONN or PAD_ATTRIB::SMD that is not on outer layer
  168. // has its aperture attribute set to GBR_APERTURE_ATTRIB_CONDUCTOR
  169. switch( aPad->GetAttribute() )
  170. {
  171. case PAD_ATTRIB::NPTH: // Mechanical pad through hole
  172. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_WASHERPAD );
  173. break;
  174. case PAD_ATTRIB::PTH : // Pad through hole, a hole is also expected
  175. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_COMPONENTPAD );
  176. break;
  177. case PAD_ATTRIB::CONN: // Connector pads, no solder paste but with solder mask.
  178. if( plotOnExternalCopperLayer )
  179. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONNECTORPAD );
  180. break;
  181. case PAD_ATTRIB::SMD: // SMD pads (on external copper layer only)
  182. // with solder paste and mask
  183. if( plotOnExternalCopperLayer )
  184. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_SMDPAD_CUDEF );
  185. break;
  186. }
  187. // Fabrication properties can have specific GBR_APERTURE_METADATA options
  188. // that replace previous aperture attribute:
  189. switch( aPad->GetProperty() )
  190. {
  191. case PAD_PROP::BGA: // Only applicable to outer layers
  192. if( plotOnExternalCopperLayer )
  193. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_BGAPAD_CUDEF );
  194. break;
  195. case PAD_PROP::FIDUCIAL_GLBL:
  196. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_FIDUCIAL_GLBL );
  197. break;
  198. case PAD_PROP::FIDUCIAL_LOCAL:
  199. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_FIDUCIAL_LOCAL );
  200. break;
  201. case PAD_PROP::TESTPOINT: // Only applicable to outer layers
  202. if( plotOnExternalCopperLayer )
  203. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_TESTPOINT );
  204. break;
  205. case PAD_PROP::HEATSINK:
  206. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_HEATSINKPAD );
  207. break;
  208. case PAD_PROP::CASTELLATED:
  209. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CASTELLATEDPAD );
  210. break;
  211. case PAD_PROP::NONE:
  212. case PAD_PROP::MECHANICAL:
  213. break;
  214. }
  215. // Ensure NPTH pads have *always* the GBR_APERTURE_ATTRIB_WASHERPAD attribute
  216. if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
  217. metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_WASHERPAD );
  218. }
  219. else
  220. {
  221. metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
  222. }
  223. // Set plot color (change WHITE to LIGHTGRAY because
  224. // the white items are not seen on a white paper or screen
  225. m_plotter->SetColor( aColor != WHITE ? aColor : LIGHTGRAY );
  226. if( aPlotMode == SKETCH )
  227. m_plotter->SetCurrentLineWidth( GetSketchPadLineWidth(), &metadata );
  228. switch( aPad->GetShape( aLayer ) )
  229. {
  230. case PAD_SHAPE::CIRCLE:
  231. m_plotter->FlashPadCircle( shape_pos, aPad->GetSize( aLayer ).x,
  232. aPlotMode, &metadata );
  233. break;
  234. case PAD_SHAPE::OVAL:
  235. m_plotter->FlashPadOval( shape_pos, aPad->GetSize( aLayer ),
  236. aPad->GetOrientation(), aPlotMode, &metadata );
  237. break;
  238. case PAD_SHAPE::RECTANGLE:
  239. m_plotter->FlashPadRect( shape_pos, aPad->GetSize( aLayer ),
  240. aPad->GetOrientation(), aPlotMode, &metadata );
  241. break;
  242. case PAD_SHAPE::ROUNDRECT:
  243. m_plotter->FlashPadRoundRect( shape_pos, aPad->GetSize( aLayer ),
  244. aPad->GetRoundRectCornerRadius( aLayer ),
  245. aPad->GetOrientation(), aPlotMode, &metadata );
  246. break;
  247. case PAD_SHAPE::TRAPEZOID:
  248. {
  249. // Build the pad polygon in coordinates relative to the pad
  250. // (i.e. for a pad at pos 0,0, rot 0.0). Needed to use aperture macros,
  251. // to be able to create a pattern common to all trapezoid pads having the same shape
  252. VECTOR2I coord[4];
  253. // Order is lower left, lower right, upper right, upper left.
  254. VECTOR2I half_size = aPad->GetSize( aLayer ) / 2;
  255. VECTOR2I trap_delta = aPad->GetDelta( aLayer ) / 2;
  256. coord[0] = VECTOR2I( -half_size.x - trap_delta.y, half_size.y + trap_delta.x );
  257. coord[1] = VECTOR2I( half_size.x + trap_delta.y, half_size.y - trap_delta.x );
  258. coord[2] = VECTOR2I( half_size.x - trap_delta.y, -half_size.y + trap_delta.x );
  259. coord[3] = VECTOR2I( -half_size.x + trap_delta.y, -half_size.y - trap_delta.x );
  260. m_plotter->FlashPadTrapez( shape_pos, coord, aPad->GetOrientation(), aPlotMode, &metadata );
  261. }
  262. break;
  263. case PAD_SHAPE::CHAMFERED_RECT:
  264. if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
  265. {
  266. GERBER_PLOTTER* gerberPlotter = static_cast<GERBER_PLOTTER*>( m_plotter );
  267. gerberPlotter->FlashPadChamferRoundRect( shape_pos,
  268. aPad->GetSize( aLayer ),
  269. aPad->GetRoundRectCornerRadius( aLayer ),
  270. aPad->GetChamferRectRatio( aLayer ),
  271. aPad->GetChamferPositions( aLayer ), aPad->GetOrientation(),
  272. aPlotMode, &metadata );
  273. break;
  274. }
  275. KI_FALLTHROUGH;
  276. default:
  277. case PAD_SHAPE::CUSTOM:
  278. {
  279. const std::shared_ptr<SHAPE_POLY_SET>& polygons =
  280. aPad->GetEffectivePolygon( aLayer, ERROR_INSIDE );
  281. if( polygons->OutlineCount() )
  282. {
  283. m_plotter->FlashPadCustom( shape_pos, aPad->GetSize( aLayer ), aPad->GetOrientation(),
  284. polygons.get(), aPlotMode, &metadata );
  285. }
  286. }
  287. break;
  288. }
  289. }
  290. void BRDITEMS_PLOTTER::PlotFootprintTextItems( const FOOTPRINT* aFootprint )
  291. {
  292. if( !GetPlotFPText() )
  293. return;
  294. const PCB_TEXT* reference = &aFootprint->Reference();
  295. PCB_LAYER_ID refLayer = reference->GetLayer();
  296. // Reference and value have special controls for forcing their plotting
  297. if( GetPlotReference()
  298. && m_layerMask[refLayer]
  299. && reference->IsVisible()
  300. && !( aFootprint->IsDNP() && hideDNPItems( refLayer ) ) )
  301. {
  302. PlotText( reference, refLayer, reference->IsKnockout(), reference->GetFontMetrics(),
  303. aFootprint->IsDNP() && crossoutDNPItems( refLayer ) );
  304. }
  305. const PCB_TEXT* value = &aFootprint->Value();
  306. PCB_LAYER_ID valueLayer = value->GetLayer();
  307. if( GetPlotValue()
  308. && m_layerMask[valueLayer]
  309. && value->IsVisible()
  310. && !( aFootprint->IsDNP() && hideDNPItems( valueLayer ) ) )
  311. {
  312. PlotText( value, valueLayer, value->IsKnockout(), value->GetFontMetrics(),
  313. false );
  314. }
  315. std::vector<PCB_TEXT*> texts;
  316. // Skip the reference and value texts that are handled specially
  317. for( PCB_FIELD* field : aFootprint->GetFields() )
  318. {
  319. if( field->IsReference() || field->IsValue() )
  320. continue;
  321. if( field->IsVisible() )
  322. texts.push_back( field );
  323. }
  324. for( BOARD_ITEM* item : aFootprint->GraphicalItems() )
  325. {
  326. if( PCB_TEXT* textItem = dynamic_cast<PCB_TEXT*>( item ) )
  327. texts.push_back( textItem );
  328. }
  329. for( const PCB_TEXT* text : texts )
  330. {
  331. PCB_LAYER_ID textLayer = text->GetLayer();
  332. bool strikeout = false;
  333. if( textLayer == Edge_Cuts || textLayer >= PCB_LAYER_ID_COUNT )
  334. continue;
  335. if( aFootprint->IsDNP() && hideDNPItems( textLayer ) )
  336. continue;
  337. if( !m_layerMask[textLayer] || aFootprint->GetPrivateLayers().test( textLayer ) )
  338. continue;
  339. if( text->GetText() == wxT( "${REFERENCE}" ) )
  340. {
  341. if( !GetPlotReference() )
  342. continue;
  343. strikeout = aFootprint->IsDNP() && crossoutDNPItems( textLayer );
  344. }
  345. if( text->GetText() == wxT( "${VALUE}" ) )
  346. {
  347. if( !GetPlotValue() )
  348. continue;
  349. }
  350. PlotText( text, textLayer, text->IsKnockout(), text->GetFontMetrics(), strikeout );
  351. }
  352. }
  353. void BRDITEMS_PLOTTER::PlotBoardGraphicItem( const BOARD_ITEM* item )
  354. {
  355. switch( item->Type() )
  356. {
  357. case PCB_SHAPE_T:
  358. PlotShape( static_cast<const PCB_SHAPE*>( item ) );
  359. break;
  360. case PCB_TEXT_T:
  361. {
  362. const PCB_TEXT* text = static_cast<const PCB_TEXT*>( item );
  363. PlotText( text, text->GetLayer(), text->IsKnockout(), text->GetFontMetrics() );
  364. break;
  365. }
  366. case PCB_TEXTBOX_T:
  367. {
  368. m_plotter->SetTextMode( PLOT_TEXT_MODE::STROKE );
  369. const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( item );
  370. PlotText( textbox, textbox->GetLayer(), textbox->IsKnockout(), textbox->GetFontMetrics() );
  371. if( textbox->IsBorderEnabled() )
  372. PlotShape( textbox );
  373. m_plotter->SetTextMode( GetTextMode() );
  374. break;
  375. }
  376. case PCB_TABLE_T:
  377. {
  378. const PCB_TABLE* table = static_cast<const PCB_TABLE*>( item );
  379. m_plotter->SetTextMode( PLOT_TEXT_MODE::STROKE );
  380. for( const PCB_TABLECELL* cell : table->GetCells() )
  381. PlotText( cell, cell->GetLayer(), cell->IsKnockout(), cell->GetFontMetrics() );
  382. PlotTableBorders( table );
  383. m_plotter->SetTextMode( GetTextMode() );
  384. break;
  385. }
  386. case PCB_DIM_ALIGNED_T:
  387. case PCB_DIM_CENTER_T:
  388. case PCB_DIM_RADIAL_T:
  389. case PCB_DIM_ORTHOGONAL_T:
  390. case PCB_DIM_LEADER_T:
  391. m_plotter->SetTextMode( PLOT_TEXT_MODE::STROKE );
  392. PlotDimension( static_cast<const PCB_DIMENSION_BASE*>( item ) );
  393. m_plotter->SetTextMode( GetTextMode() );
  394. break;
  395. case PCB_TARGET_T:
  396. PlotPcbTarget( static_cast<const PCB_TARGET*>( item ) );
  397. break;
  398. default:
  399. break;
  400. }
  401. }
  402. void BRDITEMS_PLOTTER::PlotDimension( const PCB_DIMENSION_BASE* aDim )
  403. {
  404. if( !m_layerMask[aDim->GetLayer()] )
  405. return;
  406. COLOR4D color = ColorSettings()->GetColor( aDim->GetLayer() );
  407. // Set plot color (change WHITE to LIGHTGRAY because
  408. // the white items are not seen on a white paper or screen
  409. m_plotter->SetColor( color != WHITE ? color : LIGHTGRAY);
  410. PlotText( aDim, aDim->GetLayer(), false, aDim->GetFontMetrics() );
  411. PCB_SHAPE temp_item;
  412. temp_item.SetStroke( STROKE_PARAMS( aDim->GetLineThickness(), LINE_STYLE::SOLID ) );
  413. temp_item.SetLayer( aDim->GetLayer() );
  414. for( const std::shared_ptr<SHAPE>& shape : aDim->GetShapes() )
  415. {
  416. switch( shape->Type() )
  417. {
  418. case SH_SEGMENT:
  419. {
  420. const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
  421. temp_item.SetShape( SHAPE_T::SEGMENT );
  422. temp_item.SetStart( seg.A );
  423. temp_item.SetEnd( seg.B );
  424. PlotShape( &temp_item );
  425. break;
  426. }
  427. case SH_CIRCLE:
  428. {
  429. VECTOR2I start( shape->Centre() );
  430. int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
  431. temp_item.SetShape( SHAPE_T::CIRCLE );
  432. temp_item.SetFilled( false );
  433. temp_item.SetStart( start );
  434. temp_item.SetEnd( VECTOR2I( start.x + radius, start.y ) );
  435. PlotShape( &temp_item );
  436. break;
  437. }
  438. default:
  439. break;
  440. }
  441. }
  442. }
  443. void BRDITEMS_PLOTTER::PlotPcbTarget( const PCB_TARGET* aMire )
  444. {
  445. int dx1, dx2, dy1, dy2, radius;
  446. if( !m_layerMask[aMire->GetLayer()] )
  447. return;
  448. m_plotter->SetColor( getColor( aMire->GetLayer() ) );
  449. PCB_SHAPE temp_item;
  450. temp_item.SetShape( SHAPE_T::CIRCLE );
  451. temp_item.SetFilled( false );
  452. temp_item.SetStroke( STROKE_PARAMS( aMire->GetWidth(), LINE_STYLE::SOLID ) );
  453. temp_item.SetLayer( aMire->GetLayer() );
  454. temp_item.SetStart( aMire->GetPosition() );
  455. radius = aMire->GetSize() / 3;
  456. if( aMire->GetShape() ) // temp_item X
  457. radius = aMire->GetSize() / 2;
  458. // Draw the circle
  459. temp_item.SetEnd( VECTOR2I( temp_item.GetStart().x + radius, temp_item.GetStart().y ) );
  460. PlotShape( &temp_item );
  461. temp_item.SetShape( SHAPE_T::SEGMENT );
  462. radius = aMire->GetSize() / 2;
  463. dx1 = radius;
  464. dy1 = 0;
  465. dx2 = 0;
  466. dy2 = radius;
  467. if( aMire->GetShape() ) // Shape X
  468. {
  469. dx1 = dy1 = radius;
  470. dx2 = dx1;
  471. dy2 = -dy1;
  472. }
  473. VECTOR2I mirePos( aMire->GetPosition() );
  474. // Draw the X or + temp_item:
  475. temp_item.SetStart( VECTOR2I( mirePos.x - dx1, mirePos.y - dy1 ) );
  476. temp_item.SetEnd( VECTOR2I( mirePos.x + dx1, mirePos.y + dy1 ) );
  477. PlotShape( &temp_item );
  478. temp_item.SetStart( VECTOR2I( mirePos.x - dx2, mirePos.y - dy2 ) );
  479. temp_item.SetEnd( VECTOR2I( mirePos.x + dx2, mirePos.y + dy2 ) );
  480. PlotShape( &temp_item );
  481. }
  482. void BRDITEMS_PLOTTER::PlotFootprintGraphicItems( const FOOTPRINT* aFootprint )
  483. {
  484. for( const BOARD_ITEM* item : aFootprint->GraphicalItems() )
  485. {
  486. PCB_LAYER_ID itemLayer = item->GetLayer();
  487. if( aFootprint->GetPrivateLayers().test( itemLayer ) )
  488. continue;
  489. if( aFootprint->IsDNP() && hideDNPItems( itemLayer ) )
  490. continue;
  491. if( !( m_layerMask & item->GetLayerSet() ).any() )
  492. continue;
  493. switch( item->Type() )
  494. {
  495. case PCB_SHAPE_T:
  496. PlotShape( static_cast<const PCB_SHAPE*>( item ) );
  497. break;
  498. case PCB_TEXTBOX_T:
  499. {
  500. const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( item );
  501. m_plotter->SetTextMode( PLOT_TEXT_MODE::STROKE );
  502. PlotText( textbox, textbox->GetLayer(), textbox->IsKnockout(),
  503. textbox->GetFontMetrics() );
  504. if( textbox->IsBorderEnabled() )
  505. PlotShape( textbox );
  506. m_plotter->SetTextMode( GetTextMode() );
  507. break;
  508. }
  509. case PCB_DIM_ALIGNED_T:
  510. case PCB_DIM_CENTER_T:
  511. case PCB_DIM_RADIAL_T:
  512. case PCB_DIM_ORTHOGONAL_T:
  513. case PCB_DIM_LEADER_T:
  514. PlotDimension( static_cast<const PCB_DIMENSION_BASE*>( item ) );
  515. break;
  516. case PCB_TEXT_T:
  517. // Plotted in PlotFootprintTextItems()
  518. break;
  519. default:
  520. UNIMPLEMENTED_FOR( item->GetClass() );
  521. }
  522. }
  523. }
  524. void BRDITEMS_PLOTTER::PlotText( const EDA_TEXT* aText, PCB_LAYER_ID aLayer, bool aIsKnockout,
  525. const KIFONT::METRICS& aFontMetrics, bool aStrikeout )
  526. {
  527. KIFONT::FONT* font = aText->GetFont();
  528. if( !font )
  529. {
  530. wxString defaultFontName; // empty string is the KiCad stroke font
  531. if( m_plotter->RenderSettings() )
  532. defaultFontName = m_plotter->RenderSettings()->GetDefaultFont();
  533. font = KIFONT::FONT::GetFont( defaultFontName, aText->IsBold(), aText->IsItalic() );
  534. }
  535. wxString shownText( aText->GetShownText( true ) );
  536. if( shownText.IsEmpty() )
  537. return;
  538. if( !m_layerMask[aLayer] )
  539. return;
  540. GBR_METADATA gbr_metadata;
  541. if( IsCopperLayer( aLayer ) )
  542. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR );
  543. COLOR4D color = getColor( aLayer );
  544. m_plotter->SetColor( color );
  545. VECTOR2I pos = aText->GetTextPos();
  546. TEXT_ATTRIBUTES attrs = aText->GetAttributes();
  547. attrs.m_StrokeWidth = aText->GetEffectiveTextPenWidth();
  548. attrs.m_Angle = aText->GetDrawRotation();
  549. attrs.m_Multiline = false;
  550. m_plotter->SetCurrentLineWidth( attrs.m_StrokeWidth );
  551. auto strikeoutText =
  552. [&]( const PCB_TEXT* text )
  553. {
  554. SHAPE_POLY_SET textPoly;
  555. text->TransformTextToPolySet( textPoly, 0, ARC_LOW_DEF, ERROR_INSIDE );
  556. textPoly.Rotate( -text->GetDrawRotation(), text->GetDrawPos() );
  557. BOX2I rect = textPoly.BBox();
  558. VECTOR2I start( rect.GetLeft() - attrs.m_StrokeWidth,
  559. ( rect.GetTop() + rect.GetBottom() ) / 2 );
  560. VECTOR2I end( rect.GetRight() + attrs.m_StrokeWidth,
  561. ( rect.GetTop() + rect.GetBottom() ) / 2 );
  562. RotatePoint( start, text->GetDrawPos(), text->GetDrawRotation() );
  563. RotatePoint( end, text->GetDrawPos(), text->GetDrawRotation() );
  564. m_plotter->ThickSegment( start, end, attrs.m_StrokeWidth, FILLED, nullptr );
  565. };
  566. if( aIsKnockout )
  567. {
  568. SHAPE_POLY_SET finalPoly;
  569. if( const PCB_TEXT* text = dynamic_cast<const PCB_TEXT*>( aText) )
  570. {
  571. text->TransformTextToPolySet( finalPoly, 0, m_board->GetDesignSettings().m_MaxError,
  572. ERROR_INSIDE );
  573. }
  574. else if( const PCB_TEXTBOX* textbox = dynamic_cast<const PCB_TEXTBOX*>( aText ) )
  575. {
  576. textbox->TransformTextToPolySet( finalPoly, 0, m_board->GetDesignSettings().m_MaxError,
  577. ERROR_INSIDE );
  578. }
  579. finalPoly.Fracture();
  580. for( int ii = 0; ii < finalPoly.OutlineCount(); ++ii )
  581. m_plotter->PlotPoly( finalPoly.Outline( ii ), FILL_T::FILLED_SHAPE, 0, &gbr_metadata );
  582. }
  583. else
  584. {
  585. if( font->IsOutline() && !m_board->GetEmbeddedFiles()->GetAreFontsEmbedded() )
  586. {
  587. KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
  588. CALLBACK_GAL callback_gal( empty_opts,
  589. // Stroke callback
  590. [&]( const VECTOR2I& aPt1, const VECTOR2I& aPt2 )
  591. {
  592. m_plotter->ThickSegment( aPt1, aPt2, attrs.m_StrokeWidth, FILLED, nullptr );
  593. },
  594. // Polygon callback
  595. [&]( const SHAPE_LINE_CHAIN& aPoly )
  596. {
  597. m_plotter->PlotPoly( aPoly, FILL_T::FILLED_SHAPE, 0, &gbr_metadata );
  598. } );
  599. callback_gal.DrawGlyphs( *aText->GetRenderCache( font, shownText ) );
  600. }
  601. else if( aText->IsMultilineAllowed() )
  602. {
  603. std::vector<VECTOR2I> positions;
  604. wxArrayString strings_list;
  605. wxStringSplit( shownText, strings_list, '\n' );
  606. positions.reserve( strings_list.Count() );
  607. aText->GetLinePositions( positions, (int) strings_list.Count() );
  608. for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
  609. {
  610. wxString& txt = strings_list.Item( ii );
  611. m_plotter->PlotText( positions[ii], color, txt, attrs, font, aFontMetrics,
  612. &gbr_metadata );
  613. }
  614. if( aStrikeout && strings_list.Count() == 1 )
  615. strikeoutText( static_cast<const PCB_TEXT*>( aText ) );
  616. }
  617. else
  618. {
  619. m_plotter->PlotText( pos, color, shownText, attrs, font, aFontMetrics, &gbr_metadata );
  620. if( aStrikeout )
  621. strikeoutText( static_cast<const PCB_TEXT*>( aText ) );
  622. }
  623. }
  624. }
  625. void BRDITEMS_PLOTTER::PlotZone( const ZONE* aZone, PCB_LAYER_ID aLayer,
  626. const SHAPE_POLY_SET& aPolysList )
  627. {
  628. if( aPolysList.IsEmpty() )
  629. return;
  630. GBR_METADATA gbr_metadata;
  631. if( aZone->IsOnCopperLayer() )
  632. {
  633. gbr_metadata.SetNetName( aZone->GetNetname() );
  634. gbr_metadata.SetCopper( true );
  635. // Zones with no net name can exist.
  636. // they are not used to connect items, so the aperture attribute cannot
  637. // be set as conductor
  638. if( aZone->GetNetname().IsEmpty() )
  639. {
  640. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR );
  641. }
  642. else
  643. {
  644. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  645. gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET );
  646. }
  647. }
  648. m_plotter->SetColor( getColor( aLayer ) );
  649. m_plotter->StartBlock( nullptr ); // Clean current object attributes
  650. /*
  651. * In non filled mode the outline is plotted, but not the filling items
  652. */
  653. for( int idx = 0; idx < aPolysList.OutlineCount(); ++idx )
  654. {
  655. const SHAPE_LINE_CHAIN& outline = aPolysList.Outline( idx );
  656. // Plot the current filled area (as region for Gerber plotter to manage attributes)
  657. if( GetPlotMode() == FILLED )
  658. {
  659. if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
  660. {
  661. static_cast<GERBER_PLOTTER*>( m_plotter )->PlotGerberRegion( outline,
  662. &gbr_metadata );
  663. }
  664. else
  665. {
  666. m_plotter->PlotPoly( outline, FILL_T::FILLED_SHAPE, 0, &gbr_metadata );
  667. }
  668. }
  669. else
  670. {
  671. m_plotter->SetCurrentLineWidth( -1 );
  672. }
  673. }
  674. m_plotter->EndBlock( nullptr ); // Clear object attributes
  675. }
  676. void BRDITEMS_PLOTTER::PlotShape( const PCB_SHAPE* aShape )
  677. {
  678. if( !( m_layerMask & aShape->GetLayerSet() ).any() )
  679. return;
  680. OUTLINE_MODE plotMode = GetPlotMode();
  681. int thickness = aShape->GetWidth();
  682. int margin = thickness; // unclamped thickness (can be negative)
  683. LINE_STYLE lineStyle = aShape->GetStroke().GetLineStyle();
  684. bool onCopperLayer = ( LSET::AllCuMask() & m_layerMask ).any();
  685. bool onSolderMaskLayer = ( LSET( { F_Mask, B_Mask } ) & m_layerMask ).any();
  686. if( onSolderMaskLayer
  687. && aShape->HasSolderMask()
  688. && IsExternalCopperLayer( aShape->GetLayer() ) )
  689. {
  690. margin += 2 * aShape->GetSolderMaskExpansion();
  691. thickness = std::max( margin, 0 );
  692. }
  693. m_plotter->SetColor( getColor( aShape->GetLayer() ) );
  694. const FOOTPRINT* parentFP = aShape->GetParentFootprint();
  695. GBR_METADATA gbr_metadata;
  696. if( parentFP )
  697. {
  698. gbr_metadata.SetCmpReference( parentFP->GetReference() );
  699. gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
  700. }
  701. if( parentFP && parentFP->IsDNP() && GetSketchDNPFPsOnFabLayers() )
  702. {
  703. if( aShape->GetLayer() == F_Fab || aShape->GetLayer() == B_Fab )
  704. plotMode = SKETCH;
  705. }
  706. if( aShape->GetLayer() == Edge_Cuts )
  707. {
  708. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_EDGECUT );
  709. }
  710. else if( onCopperLayer )
  711. {
  712. if( parentFP )
  713. {
  714. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_ETCHEDCMP );
  715. gbr_metadata.SetCopper( true );
  716. }
  717. else if( aShape->GetNetCode() > 0 )
  718. {
  719. gbr_metadata.SetCopper( true );
  720. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  721. gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET );
  722. gbr_metadata.SetNetName( aShape->GetNetname() );
  723. }
  724. else
  725. {
  726. // Graphic items (PCB_SHAPE, TEXT) having no net have the NonConductor attribute
  727. // Graphic items having a net have the Conductor attribute, but are not (yet?)
  728. // supported in Pcbnew
  729. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_NONCONDUCTOR );
  730. }
  731. }
  732. if( lineStyle <= LINE_STYLE::FIRST_TYPE )
  733. {
  734. switch( aShape->GetShape() )
  735. {
  736. case SHAPE_T::SEGMENT:
  737. m_plotter->ThickSegment( aShape->GetStart(), aShape->GetEnd(), thickness, plotMode,
  738. &gbr_metadata );
  739. break;
  740. case SHAPE_T::CIRCLE:
  741. if( aShape->IsSolidFill() )
  742. {
  743. int diameter = aShape->GetRadius() * 2 + thickness;
  744. if( margin < 0 )
  745. {
  746. diameter += margin;
  747. diameter = std::max( diameter, 0 );
  748. }
  749. m_plotter->FilledCircle( aShape->GetStart(), diameter, plotMode, &gbr_metadata );
  750. }
  751. else
  752. {
  753. m_plotter->ThickCircle( aShape->GetStart(), aShape->GetRadius() * 2, thickness,
  754. plotMode, &gbr_metadata );
  755. }
  756. break;
  757. case SHAPE_T::ARC:
  758. {
  759. // when startAngle == endAngle ThickArc() doesn't know whether it's 0 deg and 360 deg
  760. // but it is a circle
  761. if( std::abs( aShape->GetArcAngle().AsDegrees() ) == 360.0 )
  762. {
  763. m_plotter->ThickCircle( aShape->GetCenter(), aShape->GetRadius() * 2, thickness,
  764. plotMode, &gbr_metadata );
  765. }
  766. else
  767. {
  768. m_plotter->ThickArc( *aShape, plotMode, &gbr_metadata, thickness );
  769. }
  770. break;
  771. }
  772. case SHAPE_T::BEZIER:
  773. m_plotter->BezierCurve( aShape->GetStart(), aShape->GetBezierC1(),
  774. aShape->GetBezierC2(), aShape->GetEnd(), 0, thickness );
  775. break;
  776. case SHAPE_T::POLY:
  777. if( aShape->IsPolyShapeValid() )
  778. {
  779. if( plotMode == SKETCH )
  780. {
  781. for( auto it = aShape->GetPolyShape().CIterateSegments( 0 ); it; it++ )
  782. {
  783. const SEG& seg = it.Get();
  784. m_plotter->ThickSegment( seg.A, seg.B, thickness, SKETCH, &gbr_metadata );
  785. }
  786. }
  787. else
  788. {
  789. m_plotter->SetCurrentLineWidth( thickness, &gbr_metadata );
  790. // Draw the polygon: only one polygon is expected
  791. // However we provide a multi polygon shape drawing
  792. // ( for the future or to show a non expected shape )
  793. // This must be simplified and fractured to prevent overlapping polygons
  794. // from generating invalid Gerber files
  795. SHAPE_POLY_SET tmpPoly = aShape->GetPolyShape().CloneDropTriangulation();
  796. tmpPoly.Fracture();
  797. if( margin < 0 )
  798. {
  799. tmpPoly.Inflate( margin / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
  800. m_board->GetDesignSettings().m_MaxError );
  801. }
  802. FILL_T fill = aShape->IsSolidFill() ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
  803. for( int jj = 0; jj < tmpPoly.OutlineCount(); ++jj )
  804. {
  805. SHAPE_LINE_CHAIN& poly = tmpPoly.Outline( jj );
  806. // Ensure the polygon is closed:
  807. poly.SetClosed( true );
  808. // Plot the current filled area
  809. // (as region for Gerber plotter to manage attributes)
  810. if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
  811. {
  812. GERBER_PLOTTER* gbr_plotter = static_cast<GERBER_PLOTTER*>( m_plotter );
  813. gbr_plotter->PlotPolyAsRegion( poly, fill, thickness, &gbr_metadata );
  814. }
  815. else
  816. {
  817. m_plotter->PlotPoly( poly, fill, thickness, &gbr_metadata );
  818. }
  819. }
  820. }
  821. }
  822. break;
  823. case SHAPE_T::RECTANGLE:
  824. {
  825. std::vector<VECTOR2I> pts = aShape->GetRectCorners();
  826. if( plotMode == SKETCH )
  827. {
  828. m_plotter->ThickSegment( pts[0], pts[1], thickness, SKETCH, &gbr_metadata );
  829. m_plotter->ThickSegment( pts[1], pts[2], thickness, SKETCH, &gbr_metadata );
  830. m_plotter->ThickSegment( pts[2], pts[3], thickness, SKETCH, &gbr_metadata );
  831. m_plotter->ThickSegment( pts[3], pts[0], thickness, SKETCH, &gbr_metadata );
  832. }
  833. else
  834. {
  835. SHAPE_POLY_SET poly;
  836. poly.NewOutline();
  837. for( const VECTOR2I& pt : pts )
  838. poly.Append( pt );
  839. if( margin < 0 )
  840. {
  841. poly.Inflate( margin / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
  842. m_board->GetDesignSettings().m_MaxError );
  843. }
  844. FILL_T fill_mode = aShape->IsSolidFill() ? FILL_T::FILLED_SHAPE : FILL_T::NO_FILL;
  845. if( poly.OutlineCount() > 0 )
  846. {
  847. if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
  848. {
  849. GERBER_PLOTTER* gbr_plotter = static_cast<GERBER_PLOTTER*>( m_plotter );
  850. gbr_plotter->PlotPolyAsRegion( poly.COutline( 0 ), fill_mode, thickness,
  851. &gbr_metadata );
  852. }
  853. else
  854. {
  855. m_plotter->PlotPoly( poly.COutline( 0 ), fill_mode, thickness,
  856. &gbr_metadata );
  857. }
  858. }
  859. }
  860. break;
  861. }
  862. default:
  863. UNIMPLEMENTED_FOR( aShape->SHAPE_T_asString() );
  864. }
  865. }
  866. else
  867. {
  868. std::vector<SHAPE*> shapes = aShape->MakeEffectiveShapes( true );
  869. for( SHAPE* shape : shapes )
  870. {
  871. STROKE_PARAMS::Stroke( shape, lineStyle, aShape->GetWidth(),
  872. m_plotter->RenderSettings(),
  873. [&]( const VECTOR2I& a, const VECTOR2I& b )
  874. {
  875. m_plotter->ThickSegment( a, b, thickness, plotMode,
  876. &gbr_metadata );
  877. } );
  878. }
  879. for( SHAPE* shape : shapes )
  880. delete shape;
  881. }
  882. if( aShape->IsHatchedFill() )
  883. {
  884. for( int ii = 0; ii < aShape->GetHatching().OutlineCount(); ++ii )
  885. {
  886. if( m_plotter->GetPlotterType() == PLOT_FORMAT::GERBER )
  887. {
  888. GERBER_PLOTTER* gbr_plotter = static_cast<GERBER_PLOTTER*>( m_plotter );
  889. gbr_plotter->PlotPolyAsRegion( aShape->GetHatching().Outline( ii ),
  890. FILL_T::FILLED_SHAPE, 0, &gbr_metadata );
  891. }
  892. else
  893. {
  894. m_plotter->PlotPoly( aShape->GetHatching().Outline( ii ), FILL_T::FILLED_SHAPE,
  895. 0, &gbr_metadata );
  896. }
  897. }
  898. }
  899. }
  900. void BRDITEMS_PLOTTER::PlotTableBorders( const PCB_TABLE* aTable )
  901. {
  902. if( !m_layerMask[aTable->GetLayer()] )
  903. return;
  904. GBR_METADATA gbr_metadata;
  905. if( const FOOTPRINT* parentFP = aTable->GetParentFootprint() )
  906. {
  907. gbr_metadata.SetCmpReference( parentFP->GetReference() );
  908. gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_CMP );
  909. }
  910. aTable->DrawBorders(
  911. [&]( const VECTOR2I& ptA, const VECTOR2I& ptB, const STROKE_PARAMS& stroke )
  912. {
  913. int lineWidth = stroke.GetWidth();
  914. LINE_STYLE lineStyle = stroke.GetLineStyle();
  915. if( lineStyle <= LINE_STYLE::FIRST_TYPE )
  916. {
  917. m_plotter->ThickSegment( ptA, ptB, lineWidth, GetPlotMode(), &gbr_metadata );
  918. }
  919. else
  920. {
  921. SHAPE_SEGMENT seg( ptA, ptB );
  922. STROKE_PARAMS::Stroke( &seg, lineStyle, lineWidth, m_plotter->RenderSettings(),
  923. [&]( const VECTOR2I& a, const VECTOR2I& b )
  924. {
  925. m_plotter->ThickSegment( a, b, lineWidth, GetPlotMode(),
  926. &gbr_metadata );
  927. } );
  928. }
  929. } );
  930. }
  931. void BRDITEMS_PLOTTER::plotOneDrillMark( PAD_DRILL_SHAPE aDrillShape, const VECTOR2I& aDrillPos,
  932. const VECTOR2I& aDrillSize, const VECTOR2I& aPadSize,
  933. const EDA_ANGLE& aOrientation, int aSmallDrill )
  934. {
  935. VECTOR2I drillSize = aDrillSize;
  936. // Small drill marks have no significance when applied to slots
  937. if( aSmallDrill && aDrillShape == PAD_DRILL_SHAPE::CIRCLE )
  938. drillSize.x = std::min( aSmallDrill, drillSize.x );
  939. // Round holes only have x diameter, slots have both
  940. drillSize.x -= getFineWidthAdj();
  941. drillSize.x = std::clamp( drillSize.x, 1, aPadSize.x - 1 );
  942. if( aDrillShape == PAD_DRILL_SHAPE::OBLONG )
  943. {
  944. drillSize.y -= getFineWidthAdj();
  945. drillSize.y = std::clamp( drillSize.y, 1, aPadSize.y - 1 );
  946. m_plotter->FlashPadOval( aDrillPos, drillSize, aOrientation, GetPlotMode(), nullptr );
  947. }
  948. else
  949. {
  950. m_plotter->FlashPadCircle( aDrillPos, drillSize.x, GetPlotMode(), nullptr );
  951. }
  952. }
  953. void BRDITEMS_PLOTTER::PlotDrillMarks()
  954. {
  955. bool onCopperLayer = ( LSET::AllCuMask() & m_layerMask ).any();
  956. int smallDrill = 0;
  957. if( GetDrillMarksType() == DRILL_MARKS::SMALL_DRILL_SHAPE )
  958. smallDrill = pcbIUScale.mmToIU( ADVANCED_CFG::GetCfg().m_SmallDrillMarkSize );
  959. /* In the filled trace mode drill marks are drawn white-on-black to knock-out the underlying
  960. pad. This works only for drivers supporting color change, obviously... it means that:
  961. - PS, SVG and PDF output is correct (i.e. you have a 'donut' pad)
  962. - In HPGL you can't see them
  963. - In gerbers you can't see them, too. This is arguably the right thing to do since having
  964. drill marks and high speed drill stations is a sure recipe for broken tools and angry
  965. manufacturers. If you *really* want them you could start a layer with negative polarity
  966. to knock-out the film.
  967. - In DXF they go into the 'WHITE' layer. This could be useful.
  968. */
  969. if( GetPlotMode() == FILLED && onCopperLayer )
  970. m_plotter->SetColor( WHITE );
  971. for( PCB_TRACK* track : m_board->Tracks() )
  972. {
  973. if( track->Type() == PCB_VIA_T )
  974. {
  975. const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
  976. // Via are not always on all layers
  977. if( ( via->GetLayerSet() & m_layerMask ).none() )
  978. continue;
  979. plotOneDrillMark( PAD_DRILL_SHAPE::CIRCLE, via->GetStart(),
  980. VECTOR2I( via->GetDrillValue(), 0 ),
  981. VECTOR2I( via->GetWidth( PADSTACK::ALL_LAYERS ), 0 ),
  982. ANGLE_0, smallDrill );
  983. }
  984. }
  985. for( FOOTPRINT* footprint : m_board->Footprints() )
  986. {
  987. for( PAD* pad : footprint->Pads() )
  988. {
  989. if( pad->GetDrillSize().x == 0 )
  990. continue;
  991. plotOneDrillMark( pad->GetDrillShape(), pad->GetPosition(), pad->GetDrillSize(),
  992. pad->GetSize( PADSTACK::ALL_LAYERS ), pad->GetOrientation(), smallDrill );
  993. }
  994. }
  995. if( GetPlotMode() == FILLED && onCopperLayer )
  996. m_plotter->SetColor( BLACK );
  997. }