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.

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