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.

1122 lines
38 KiB

14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
17 years ago
17 years ago
17 years ago
14 years ago
17 years ago
14 years ago
14 years ago
14 years ago
17 years ago
14 years ago
17 years ago
14 years ago
14 years ago
14 years ago
17 years ago
  1. /**
  2. * @file plot_board_layers.cpp
  3. * @brief Functions to plot one board layer (silkscreen layers or other layers).
  4. * Silkscreen layers have specific requirement for pads (not filled) and texts
  5. * (with option to remove them from some copper areas (pads...)
  6. */
  7. /*
  8. * This program source code file is part of KiCad, a free EDA CAD application.
  9. *
  10. * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
  11. *
  12. * This program is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU General Public License
  14. * as published by the Free Software Foundation; either version 2
  15. * of the License, or (at your option) any later version.
  16. *
  17. * This program is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program; if not, you may find one here:
  24. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  25. * or you may search the http://www.gnu.org website for the version 2 license,
  26. * or you may write to the Free Software Foundation, Inc.,
  27. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  28. */
  29. #include <fctsys.h>
  30. #include <common.h>
  31. #include <plot_common.h>
  32. #include <base_struct.h>
  33. #include <drawtxt.h>
  34. #include <trigo.h>
  35. #include <wxBasePcbFrame.h>
  36. #include <macros.h>
  37. #include <class_board.h>
  38. #include <class_module.h>
  39. #include <class_track.h>
  40. #include <class_edge_mod.h>
  41. #include <class_pcb_text.h>
  42. #include <class_zone.h>
  43. #include <class_drawsegment.h>
  44. #include <class_mire.h>
  45. #include <class_dimension.h>
  46. #include <pcbnew.h>
  47. #include <pcbplot.h>
  48. #include <plot_auxiliary_data.h>
  49. // Local
  50. /* Plot a solder mask layer.
  51. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers,
  52. * unless the minimum thickness is 0.
  53. */
  54. static void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
  55. LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
  56. int aMinThickness );
  57. /* Creates the plot for silkscreen layers
  58. * Silkscreen layers have specific requirement for pads (not filled) and texts
  59. * (with option to remove them from some copper areas (pads...)
  60. */
  61. void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
  62. const PCB_PLOT_PARAMS& aPlotOpt )
  63. {
  64. BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
  65. itemplotter.SetLayerSet( aLayerMask );
  66. // Plot edge layer and graphic items
  67. itemplotter.PlotBoardGraphicItems();
  68. // Plot footprint outlines :
  69. itemplotter.Plot_Edges_Modules();
  70. // Plot pads (creates pads outlines, for pads on silkscreen layers)
  71. LSET layersmask_plotpads = aLayerMask;
  72. // Calculate the mask layers of allowed layers for pads
  73. if( !aPlotOpt.GetPlotPadsOnSilkLayer() ) // Do not plot pads on silk screen layers
  74. layersmask_plotpads.set( B_SilkS, false ).set( F_SilkS, false );
  75. if( layersmask_plotpads.any() )
  76. {
  77. for( MODULE* Module = aBoard->m_Modules; Module; Module = Module->Next() )
  78. {
  79. aPlotter->StartBlock( NULL );
  80. for( D_PAD* pad = Module->PadsList(); pad; pad = pad->Next() )
  81. {
  82. // See if the pad is on this layer
  83. LSET masklayer = pad->GetLayerSet();
  84. if( !( masklayer & layersmask_plotpads ).any() )
  85. continue;
  86. COLOR4D color = COLOR4D::BLACK;
  87. if( layersmask_plotpads[B_SilkS] )
  88. color = aBoard->Colors().GetLayerColor( B_SilkS );
  89. if( layersmask_plotpads[F_SilkS] )
  90. color = ( color == COLOR4D::BLACK) ? aBoard->Colors().GetLayerColor( F_SilkS ) : color;
  91. itemplotter.PlotPad( pad, color, SKETCH );
  92. }
  93. aPlotter->EndBlock( NULL );
  94. }
  95. }
  96. // Plot footprints fields (ref, value ...)
  97. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  98. {
  99. if( ! itemplotter.PlotAllTextsModule( module ) )
  100. {
  101. wxLogMessage( _( "Your BOARD has a bad layer number for footprint %s" ),
  102. GetChars( module->GetReference() ) );
  103. }
  104. }
  105. // Plot filled areas
  106. aPlotter->StartBlock( NULL );
  107. for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
  108. {
  109. ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii );
  110. if( !aLayerMask[ edge_zone->GetLayer() ] )
  111. continue;
  112. itemplotter.PlotFilledAreas( edge_zone );
  113. }
  114. aPlotter->EndBlock( NULL );
  115. // Plot segments used to fill zone areas (outdated, but here for old boards
  116. // compatibility):
  117. for( SEGZONE* seg = aBoard->m_Zone; seg; seg = seg->Next() )
  118. {
  119. if( !aLayerMask[ seg->GetLayer() ] )
  120. continue;
  121. aPlotter->ThickSegment( seg->GetStart(), seg->GetEnd(), seg->GetWidth(),
  122. itemplotter.GetPlotMode(), NULL );
  123. }
  124. }
  125. void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
  126. const PCB_PLOT_PARAMS& aPlotOpt )
  127. {
  128. PCB_PLOT_PARAMS plotOpt = aPlotOpt;
  129. int soldermask_min_thickness = aBoard->GetDesignSettings().m_SolderMaskMinWidth;
  130. // Set a default color and the text mode for this layer
  131. aPlotter->SetColor( aPlotOpt.GetColor() );
  132. aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
  133. // Specify that the contents of the "Edges Pcb" layer are to be plotted
  134. // in addition to the contents of the currently specified layer.
  135. LSET layer_mask( aLayer );
  136. if( !aPlotOpt.GetExcludeEdgeLayer() )
  137. layer_mask.set( Edge_Cuts );
  138. if( IsCopperLayer( aLayer ) )
  139. {
  140. // Skip NPTH pads on copper layers ( only if hole size == pad size ):
  141. // Drill mark will be plotted,
  142. // if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
  143. if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
  144. {
  145. plotOpt.SetSkipPlotNPTH_Pads( false );
  146. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  147. }
  148. else
  149. {
  150. plotOpt.SetSkipPlotNPTH_Pads( true );
  151. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  152. }
  153. }
  154. else
  155. {
  156. switch( aLayer )
  157. {
  158. case B_Mask:
  159. case F_Mask:
  160. plotOpt.SetSkipPlotNPTH_Pads( false );
  161. // Disable plot pad holes
  162. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  163. // Plot solder mask:
  164. if( soldermask_min_thickness == 0 )
  165. {
  166. if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
  167. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  168. else
  169. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  170. }
  171. else
  172. PlotSolderMaskLayer( aBoard, aPlotter, layer_mask, plotOpt,
  173. soldermask_min_thickness );
  174. break;
  175. case B_Adhes:
  176. case F_Adhes:
  177. case B_Paste:
  178. case F_Paste:
  179. plotOpt.SetSkipPlotNPTH_Pads( false );
  180. // Disable plot pad holes
  181. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  182. if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
  183. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  184. else
  185. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  186. break;
  187. case F_SilkS:
  188. case B_SilkS:
  189. if( plotOpt.GetFormat() == PLOT_FORMAT_DXF && plotOpt.GetDXFPlotPolygonMode() )
  190. // PlotLayerOutlines() is designed only for DXF plotters.
  191. // and must not be used for other plot formats
  192. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  193. else
  194. PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt );
  195. // Gerber: Subtract soldermask from silkscreen if enabled
  196. if( aPlotter->GetPlotterType() == PLOT_FORMAT_GERBER
  197. && plotOpt.GetSubtractMaskFromSilk() )
  198. {
  199. if( aLayer == F_SilkS )
  200. layer_mask = LSET( F_Mask );
  201. else
  202. layer_mask = LSET( B_Mask );
  203. // Create the mask to subtract by creating a negative layer polarity
  204. aPlotter->SetLayerPolarity( false );
  205. // Disable plot pad holes
  206. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  207. // Plot the mask
  208. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  209. }
  210. break;
  211. // These layers are plotted like silk screen layers.
  212. // Mainly, pads on these layers are not filled.
  213. // This is not necessary the best choice.
  214. case Dwgs_User:
  215. case Cmts_User:
  216. case Eco1_User:
  217. case Eco2_User:
  218. case Edge_Cuts:
  219. case Margin:
  220. case F_CrtYd:
  221. case B_CrtYd:
  222. case F_Fab:
  223. case B_Fab:
  224. plotOpt.SetSkipPlotNPTH_Pads( false );
  225. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  226. if( plotOpt.GetFormat() == PLOT_FORMAT_DXF && plotOpt.GetDXFPlotPolygonMode() )
  227. // PlotLayerOutlines() is designed only for DXF plotters.
  228. // and must not be used for other plot formats
  229. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  230. else
  231. PlotSilkScreen( aBoard, aPlotter, layer_mask, plotOpt );
  232. break;
  233. default:
  234. plotOpt.SetSkipPlotNPTH_Pads( false );
  235. plotOpt.SetDrillMarksType( PCB_PLOT_PARAMS::NO_DRILL_SHAPE );
  236. if( plotOpt.GetFormat() == PLOT_FORMAT_DXF && plotOpt.GetDXFPlotPolygonMode() )
  237. // PlotLayerOutlines() is designed only for DXF plotters.
  238. // and must not be used for other plot formats
  239. PlotLayerOutlines( aBoard, aPlotter, layer_mask, plotOpt );
  240. else
  241. PlotStandardLayer( aBoard, aPlotter, layer_mask, plotOpt );
  242. break;
  243. }
  244. }
  245. }
  246. /* Plot a copper layer or mask.
  247. * Silk screen layers are not plotted here.
  248. */
  249. void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
  250. LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
  251. {
  252. BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
  253. itemplotter.SetLayerSet( aLayerMask );
  254. EDA_DRAW_MODE_T plotMode = aPlotOpt.GetPlotMode();
  255. // Plot edge layer and graphic items
  256. itemplotter.PlotBoardGraphicItems();
  257. // Draw footprint shapes without pads (pads will plotted later)
  258. // We plot here module texts, but they are usually on silkscreen layer,
  259. // so they are not plot here but plot by PlotSilkScreen()
  260. // Plot footprints fields (ref, value ...)
  261. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  262. {
  263. if( ! itemplotter.PlotAllTextsModule( module ) )
  264. {
  265. wxLogMessage( _( "Your BOARD has a bad layer number for footprint %s" ),
  266. GetChars( module->GetReference() ) );
  267. }
  268. }
  269. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  270. {
  271. for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() )
  272. {
  273. if( !aLayerMask[ item->GetLayer() ] )
  274. continue;
  275. switch( item->Type() )
  276. {
  277. case PCB_MODULE_EDGE_T:
  278. itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item );
  279. break;
  280. default:
  281. break;
  282. }
  283. }
  284. }
  285. // Plot footprint pads
  286. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  287. {
  288. aPlotter->StartBlock( NULL );
  289. for( D_PAD* pad = module->PadsList(); pad; pad = pad->Next() )
  290. {
  291. if( (pad->GetLayerSet() & aLayerMask) == 0 )
  292. continue;
  293. wxSize margin;
  294. double width_adj = 0;
  295. if( ( aLayerMask & LSET::AllCuMask() ).any() )
  296. width_adj = itemplotter.getFineWidthAdj();
  297. static const LSET speed( 4, B_Mask, F_Mask, B_Paste, F_Paste );
  298. LSET anded = ( speed & aLayerMask );
  299. if( anded == LSET( F_Mask ) || anded == LSET( B_Mask ) )
  300. {
  301. margin.x = margin.y = pad->GetSolderMaskMargin();
  302. }
  303. else if( anded == LSET( F_Paste ) || anded == LSET( B_Paste ) )
  304. {
  305. margin = pad->GetSolderPasteMargin();
  306. }
  307. // Now offset the pad size by margin + width_adj
  308. // this is easy for most shapes, but not for a trapezoid
  309. wxSize padPlotsSize;
  310. wxSize extraSize = margin * 2;
  311. extraSize.x += width_adj;
  312. extraSize.y += width_adj;
  313. wxSize deltaSize = pad->GetDelta(); // has meaning only for trapezoidal pads
  314. if( pad->GetShape() == PAD_SHAPE_TRAPEZOID )
  315. { // The easy way is to use BuildPadPolygon to calculate
  316. // size and delta of the trapezoidal pad after offseting:
  317. wxPoint coord[4];
  318. pad->BuildPadPolygon( coord, extraSize/2, 0.0 );
  319. // Calculate the size and delta from polygon corners coordinates:
  320. // coord[0] is the lower left
  321. // coord[1] is the upper left
  322. // coord[2] is the upper right
  323. // coord[3] is the lower right
  324. // the size is the distance between middle of segments
  325. // (left/right or top/bottom)
  326. // size X is the dist between left and right middle points:
  327. padPlotsSize.x = ( ( -coord[0].x + coord[3].x ) // the lower segment X length
  328. + ( -coord[1].x + coord[2].x ) ) // the upper segment X length
  329. / 2; // the Y size is the half sum
  330. // size Y is the dist between top and bottom middle points:
  331. padPlotsSize.y = ( ( coord[0].y - coord[1].y ) // the left segment Y lenght
  332. + ( coord[3].y - coord[2].y ) ) // the right segment Y lenght
  333. / 2; // the Y size is the half sum
  334. // calculate the delta ( difference of lenght between 2 opposite edges )
  335. // The delta.x is the delta along the X axis, therefore the delta of Y lenghts
  336. wxSize delta;
  337. if( coord[0].y != coord[3].y )
  338. delta.x = coord[0].y - coord[3].y;
  339. else
  340. delta.y = coord[1].x - coord[0].x;
  341. pad->SetDelta( delta );
  342. }
  343. else
  344. padPlotsSize = pad->GetSize() + extraSize;
  345. // Don't draw a null size item :
  346. if( padPlotsSize.x <= 0 || padPlotsSize.y <= 0 )
  347. continue;
  348. COLOR4D color = COLOR4D::BLACK;
  349. if( pad->GetLayerSet()[B_Cu] )
  350. color = aBoard->Colors().GetItemColor( LAYER_PAD_BK );
  351. if( pad->GetLayerSet()[F_Cu] )
  352. color = color.LegacyMix( aBoard->Colors().GetItemColor( LAYER_PAD_FR ) );
  353. // Temporary set the pad size to the required plot size:
  354. wxSize tmppadsize = pad->GetSize();
  355. pad->SetSize( padPlotsSize );
  356. switch( pad->GetShape() )
  357. {
  358. case PAD_SHAPE_CIRCLE:
  359. case PAD_SHAPE_OVAL:
  360. if( aPlotOpt.GetSkipPlotNPTH_Pads() &&
  361. (pad->GetSize() == pad->GetDrillSize()) &&
  362. (pad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED) )
  363. break;
  364. // Fall through:
  365. case PAD_SHAPE_TRAPEZOID:
  366. case PAD_SHAPE_RECT:
  367. case PAD_SHAPE_ROUNDRECT:
  368. default:
  369. itemplotter.PlotPad( pad, color, plotMode );
  370. break;
  371. }
  372. pad->SetSize( tmppadsize ); // Restore the pad size
  373. pad->SetDelta( deltaSize );
  374. }
  375. aPlotter->EndBlock( NULL );
  376. }
  377. // Plot vias on copper layers, and if aPlotOpt.GetPlotViaOnMaskLayer() is true,
  378. // plot them on solder mask
  379. GBR_METADATA gbr_metadata;
  380. bool isOnCopperLayer = ( aLayerMask & LSET::AllCuMask() ).any();
  381. if( isOnCopperLayer )
  382. {
  383. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_VIAPAD );
  384. gbr_metadata.SetNetAttribType( GBR_NETLIST_METADATA::GBR_NETINFO_NET );
  385. }
  386. aPlotter->StartBlock( NULL );
  387. for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
  388. {
  389. const VIA* Via = dyn_cast<const VIA*>( track );
  390. if( !Via )
  391. continue;
  392. // vias are not plotted if not on selected layer, but if layer
  393. // is SOLDERMASK_LAYER_BACK or SOLDERMASK_LAYER_FRONT,vias are drawn,
  394. // only if they are on the corresponding external copper layer
  395. LSET via_mask_layer = Via->GetLayerSet();
  396. if( aPlotOpt.GetPlotViaOnMaskLayer() )
  397. {
  398. if( via_mask_layer[B_Cu] )
  399. via_mask_layer.set( B_Mask );
  400. if( via_mask_layer[F_Cu] )
  401. via_mask_layer.set( F_Mask );
  402. }
  403. if( !( via_mask_layer & aLayerMask ).any() )
  404. continue;
  405. int via_margin = 0;
  406. double width_adj = 0;
  407. // If the current layer is a solder mask, use the global mask
  408. // clearance for vias
  409. if( aLayerMask[B_Mask] || aLayerMask[F_Mask] )
  410. via_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
  411. if( ( aLayerMask & LSET::AllCuMask() ).any() )
  412. width_adj = itemplotter.getFineWidthAdj();
  413. int diameter = Via->GetWidth() + 2 * via_margin + width_adj;
  414. // Don't draw a null size item :
  415. if( diameter <= 0 )
  416. continue;
  417. // Some vias can be not connected (no net).
  418. // Set the m_NotInNet for these vias to force a empty net name in gerber file
  419. gbr_metadata.m_NetlistMetadata.m_NotInNet = Via->GetNetname().IsEmpty();
  420. gbr_metadata.SetNetName( Via->GetNetname() );
  421. COLOR4D color = aBoard->Colors().GetItemColor( LAYER_VIAS + Via->GetViaType() );
  422. // Set plot color (change WHITE to LIGHTGRAY because
  423. // the white items are not seen on a white paper or screen
  424. aPlotter->SetColor( color != WHITE ? color : LIGHTGRAY);
  425. aPlotter->FlashPadCircle( Via->GetStart(), diameter, plotMode, &gbr_metadata );
  426. }
  427. aPlotter->EndBlock( NULL );
  428. aPlotter->StartBlock( NULL );
  429. gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_CONDUCTOR );
  430. // Plot tracks (not vias) :
  431. for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
  432. {
  433. if( track->Type() == PCB_VIA_T )
  434. continue;
  435. if( !aLayerMask[track->GetLayer()] )
  436. continue;
  437. // Some track segments can be not connected (no net).
  438. // Set the m_NotInNet for these segments to force a empty net name in gerber file
  439. gbr_metadata.m_NetlistMetadata.m_NotInNet = track->GetNetname().IsEmpty();
  440. gbr_metadata.SetNetName( track->GetNetname() );
  441. int width = track->GetWidth() + itemplotter.getFineWidthAdj();
  442. aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
  443. aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode, &gbr_metadata );
  444. }
  445. aPlotter->EndBlock( NULL );
  446. // Plot zones (outdated, for old boards compatibility):
  447. for( TRACK* track = aBoard->m_Zone; track; track = track->Next() )
  448. {
  449. if( !aLayerMask[track->GetLayer()] )
  450. continue;
  451. int width = track->GetWidth() + itemplotter.getFineWidthAdj();
  452. aPlotter->SetColor( itemplotter.getColor( track->GetLayer() ) );
  453. aPlotter->ThickSegment( track->GetStart(), track->GetEnd(), width, plotMode, NULL );
  454. }
  455. // Plot filled ares
  456. aPlotter->StartBlock( NULL );
  457. for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
  458. {
  459. ZONE_CONTAINER* zone = aBoard->GetArea( ii );
  460. if( !aLayerMask[zone->GetLayer()] )
  461. continue;
  462. itemplotter.PlotFilledAreas( zone );
  463. }
  464. aPlotter->EndBlock( NULL );
  465. // Adding drill marks, if required and if the plotter is able to plot them:
  466. if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
  467. itemplotter.PlotDrillMarks();
  468. }
  469. // Seems like we want to plot from back to front?
  470. static const PCB_LAYER_ID plot_seq[] = {
  471. B_Adhes, // 32
  472. F_Adhes,
  473. B_Paste,
  474. F_Paste,
  475. B_SilkS,
  476. B_Mask,
  477. F_Mask,
  478. Dwgs_User,
  479. Cmts_User,
  480. Eco1_User,
  481. Eco2_User,
  482. Edge_Cuts,
  483. Margin,
  484. F_CrtYd, // CrtYd & Body are footprint only
  485. B_CrtYd,
  486. F_Fab,
  487. B_Fab,
  488. B_Cu,
  489. In30_Cu,
  490. In29_Cu,
  491. In28_Cu,
  492. In27_Cu,
  493. In26_Cu,
  494. In25_Cu,
  495. In24_Cu,
  496. In23_Cu,
  497. In22_Cu,
  498. In21_Cu,
  499. In20_Cu,
  500. In19_Cu,
  501. In18_Cu,
  502. In17_Cu,
  503. In16_Cu,
  504. In15_Cu,
  505. In14_Cu,
  506. In13_Cu,
  507. In12_Cu,
  508. In11_Cu,
  509. In10_Cu,
  510. In9_Cu,
  511. In8_Cu,
  512. In7_Cu,
  513. In6_Cu,
  514. In5_Cu,
  515. In4_Cu,
  516. In3_Cu,
  517. In2_Cu,
  518. In1_Cu,
  519. F_Cu,
  520. F_SilkS,
  521. };
  522. /* Plot outlines of copper, for copper layer
  523. */
  524. void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
  525. LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
  526. {
  527. BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
  528. itemplotter.SetLayerSet( aLayerMask );
  529. SHAPE_POLY_SET outlines;
  530. for( LSEQ seq = aLayerMask.Seq( plot_seq, DIM( plot_seq ) ); seq; ++seq )
  531. {
  532. PCB_LAYER_ID layer = *seq;
  533. outlines.RemoveAllContours();
  534. aBoard->ConvertBrdLayerToPolygonalContours( layer, outlines );
  535. outlines.Simplify( SHAPE_POLY_SET::PM_FAST );
  536. // Plot outlines
  537. std::vector< wxPoint > cornerList;
  538. // Now we have one or more basic polygons: plot each polygon
  539. for( int ii = 0; ii < outlines.OutlineCount(); ii++ )
  540. {
  541. for(int kk = 0; kk <= outlines.HoleCount (ii); kk++ )
  542. {
  543. cornerList.clear();
  544. const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 );
  545. for( int jj = 0; jj < path.PointCount(); jj++ )
  546. cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) );
  547. // Ensure the polygon is closed
  548. if( cornerList[0] != cornerList[cornerList.size() - 1] )
  549. cornerList.push_back( cornerList[0] );
  550. aPlotter->PlotPoly( cornerList, NO_FILL );
  551. }
  552. }
  553. // Plot pad holes
  554. if( aPlotOpt.GetDrillMarksType() != PCB_PLOT_PARAMS::NO_DRILL_SHAPE )
  555. {
  556. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  557. {
  558. for( D_PAD* pad = module->PadsList(); pad; pad = pad->Next() )
  559. {
  560. wxSize hole = pad->GetDrillSize();
  561. if( hole.x == 0 || hole.y == 0 )
  562. continue;
  563. if( hole.x == hole.y )
  564. aPlotter->Circle( pad->GetPosition(), hole.x, NO_FILL );
  565. else
  566. {
  567. wxPoint drl_start, drl_end;
  568. int width;
  569. pad->GetOblongDrillGeometry( drl_start, drl_end, width );
  570. aPlotter->ThickSegment( pad->GetPosition() + drl_start,
  571. pad->GetPosition() + drl_end, width, SKETCH, NULL );
  572. }
  573. }
  574. }
  575. }
  576. // Plot vias holes
  577. for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
  578. {
  579. const VIA* via = dyn_cast<const VIA*>( track );
  580. if( via && via->IsOnLayer( layer ) ) // via holes can be not through holes
  581. {
  582. aPlotter->Circle( via->GetPosition(), via->GetDrillValue(), NO_FILL );
  583. }
  584. }
  585. }
  586. }
  587. /* Plot a solder mask layer.
  588. * Solder mask layers have a minimum thickness value and cannot be drawn like standard layers,
  589. * unless the minimum thickness is 0.
  590. * Currently the algo is:
  591. * 1 - build all pad shapes as polygons with a size inflated by
  592. * mask clearance + (min width solder mask /2)
  593. * 2 - Merge shapes
  594. * 3 - deflate result by (min width solder mask /2)
  595. * 4 - ORing result by all pad shapes as polygons with a size inflated by
  596. * mask clearance only (because deflate sometimes creates shape artifacts)
  597. * 5 - draw result as polygons
  598. *
  599. * TODO:
  600. * make this calculation only for shapes with clearance near than (min width solder mask)
  601. * (using DRC algo)
  602. * plot all other shapes by flashing the basing shape
  603. * (shapes will be better, and calculations faster)
  604. */
  605. void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
  606. LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
  607. int aMinThickness )
  608. {
  609. PCB_LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask;
  610. int inflate = aMinThickness/2;
  611. BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
  612. itemplotter.SetLayerSet( aLayerMask );
  613. // Plot edge layer and graphic items
  614. // They do not have a solder Mask margin, because they are only graphic items
  615. // on this layer (like logos), not actually areas around pads.
  616. itemplotter.PlotBoardGraphicItems();
  617. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  618. {
  619. for( BOARD_ITEM* item = module->GraphicalItemsList(); item; item = item->Next() )
  620. {
  621. if( layer != item->GetLayer() )
  622. continue;
  623. switch( item->Type() )
  624. {
  625. case PCB_MODULE_EDGE_T:
  626. itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item );
  627. break;
  628. default:
  629. break;
  630. }
  631. }
  632. }
  633. // Build polygons for each pad shape.
  634. // the size of the shape on solder mask should be:
  635. // size of pad + clearance around the pad.
  636. // clearance = solder mask clearance + extra margin
  637. // extra margin is half the min width for solder mask
  638. // This extra margin is used to merge too close shapes
  639. // (distance < aMinThickness), and will be removed when creating
  640. // the actual shapes
  641. SHAPE_POLY_SET areas; // Contains shapes to plot
  642. SHAPE_POLY_SET initialPolys; // Contains exact shapes to plot
  643. /* calculates the coeff to compensate radius reduction of holes clearance
  644. * due to the segment approx ( 1 /cos( PI/circleToSegmentsCount )
  645. */
  646. int circleToSegmentsCount = 32;
  647. double correction = 1.0 / cos( M_PI / circleToSegmentsCount );
  648. // Plot pads
  649. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  650. {
  651. // add shapes with exact size
  652. module->TransformPadsShapesWithClearanceToPolygon( layer,
  653. initialPolys, 0,
  654. circleToSegmentsCount, correction );
  655. // add shapes inflated by aMinThickness/2
  656. module->TransformPadsShapesWithClearanceToPolygon( layer,
  657. areas, inflate,
  658. circleToSegmentsCount, correction );
  659. }
  660. // Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
  661. if( aPlotOpt.GetPlotViaOnMaskLayer() )
  662. {
  663. // The current layer is a solder mask,
  664. // use the global mask clearance for vias
  665. int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
  666. int via_margin = via_clearance + inflate;
  667. for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
  668. {
  669. const VIA* via = dyn_cast<const VIA*>( track );
  670. if( !via )
  671. continue;
  672. // vias are plotted only if they are on the corresponding
  673. // external copper layer
  674. LSET via_set = via->GetLayerSet();
  675. if( via_set[B_Cu] )
  676. via_set.set( B_Mask );
  677. if( via_set[F_Cu] )
  678. via_set.set( F_Mask );
  679. if( !( via_set & aLayerMask ).any() )
  680. continue;
  681. via->TransformShapeWithClearanceToPolygon( areas, via_margin,
  682. circleToSegmentsCount,
  683. correction );
  684. via->TransformShapeWithClearanceToPolygon( initialPolys, via_clearance,
  685. circleToSegmentsCount,
  686. correction );
  687. }
  688. }
  689. // Add filled zone areas.
  690. #if 0 // Set to 1 if a solder mask margin must be applied to zones on solder mask
  691. int zone_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
  692. #else
  693. int zone_margin = 0;
  694. #endif
  695. for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
  696. {
  697. ZONE_CONTAINER* zone = aBoard->GetArea( ii );
  698. if( zone->GetLayer() != layer )
  699. continue;
  700. zone->TransformOutlinesShapeWithClearanceToPolygon( areas,
  701. inflate+zone_margin, false );
  702. zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys,
  703. zone_margin, false );
  704. }
  705. // To avoid a lot of code, use a ZONE_CONTAINER
  706. // to handle and plot polygons, because our polygons look exactly like
  707. // filled areas in zones
  708. // Note, also this code is not optimized: it creates a lot of copy/duplicate data
  709. // However it is not complex, and fast enough for plot purposes (copy/convert data
  710. // is only a very small calculation time for these calculations)
  711. ZONE_CONTAINER zone( aBoard );
  712. zone.SetArcSegmentCount( 32 );
  713. zone.SetMinThickness( 0 ); // trace polygons only
  714. zone.SetLayer ( layer );
  715. areas.BooleanAdd( initialPolys, SHAPE_POLY_SET::PM_FAST );
  716. areas.Inflate( -inflate, circleToSegmentsCount );
  717. // Combine the current areas to initial areas. This is mandatory because
  718. // inflate/deflate transform is not perfect, and we want the initial areas perfectly kept
  719. areas.BooleanAdd( initialPolys, SHAPE_POLY_SET::PM_FAST );
  720. areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  721. zone.AddFilledPolysList( areas );
  722. itemplotter.PlotFilledAreas( &zone );
  723. }
  724. /** Set up most plot options for plotting a board (especially the viewport)
  725. * Important thing:
  726. * page size is the 'drawing' page size,
  727. * paper size is the physical page size
  728. */
  729. static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
  730. PCB_PLOT_PARAMS *aPlotOpts )
  731. {
  732. PAGE_INFO pageA4( wxT( "A4" ) );
  733. const PAGE_INFO& pageInfo = aBoard->GetPageSettings();
  734. const PAGE_INFO* sheet_info;
  735. double paperscale; // Page-to-paper ratio
  736. wxSize paperSizeIU;
  737. wxSize pageSizeIU( pageInfo.GetSizeIU() );
  738. bool autocenter = false;
  739. /* Special options: to fit the sheet to an A4 sheet replace
  740. the paper size. However there is a difference between
  741. the autoscale and the a4paper option:
  742. - Autoscale fits the board to the paper size
  743. - A4paper fits the original paper size to an A4 sheet
  744. - Both of them fit the board to an A4 sheet
  745. */
  746. if( aPlotOpts->GetA4Output() ) // Fit paper to A4
  747. {
  748. sheet_info = &pageA4;
  749. paperSizeIU = pageA4.GetSizeIU();
  750. paperscale = (double) paperSizeIU.x / pageSizeIU.x;
  751. autocenter = true;
  752. }
  753. else
  754. {
  755. sheet_info = &pageInfo;
  756. paperSizeIU = pageSizeIU;
  757. paperscale = 1;
  758. // Need autocentering only if scale is not 1:1
  759. autocenter = (aPlotOpts->GetScale() != 1.0);
  760. }
  761. EDA_RECT bbox = aBoard->ComputeBoundingBox();
  762. wxPoint boardCenter = bbox.Centre();
  763. wxSize boardSize = bbox.GetSize();
  764. double compound_scale;
  765. /* Fit to 80% of the page if asked; it could be that the board is empty,
  766. * in this case regress to 1:1 scale */
  767. if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
  768. {
  769. double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
  770. double yscale = (paperSizeIU.y * 0.8) / boardSize.y;
  771. compound_scale = std::min( xscale, yscale ) * paperscale;
  772. }
  773. else
  774. compound_scale = aPlotOpts->GetScale() * paperscale;
  775. /* For the plot offset we have to keep in mind the auxiliary origin
  776. too: if autoscaling is off we check that plot option (i.e. autoscaling
  777. overrides auxiliary origin) */
  778. wxPoint offset( 0, 0);
  779. if( autocenter )
  780. {
  781. offset.x = KiROUND( boardCenter.x - ( paperSizeIU.x / 2.0 ) / compound_scale );
  782. offset.y = KiROUND( boardCenter.y - ( paperSizeIU.y / 2.0 ) / compound_scale );
  783. }
  784. else
  785. {
  786. if( aPlotOpts->GetUseAuxOrigin() )
  787. offset = aBoard->GetAuxOrigin();
  788. }
  789. /* Configure the plotter object with all the stuff computed and
  790. most of that taken from the options */
  791. aPlotter->SetPageSettings( *sheet_info );
  792. aPlotter->SetViewport( offset, IU_PER_MILS/10, compound_scale,
  793. aPlotOpts->GetMirror() );
  794. // has meaning only for gerber plotter. Must be called only after SetViewport
  795. aPlotter->SetGerberCoordinatesFormat( aPlotOpts->GetGerberPrecision() );
  796. aPlotter->SetDefaultLineWidth( aPlotOpts->GetLineWidth() );
  797. aPlotter->SetCreator( wxT( "PCBNEW" ) );
  798. aPlotter->SetColorMode( false ); // default is plot in Black and White.
  799. aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
  800. }
  801. /** Prefill in black an area a little bigger than the board to prepare for the
  802. * negative plot */
  803. static void FillNegativeKnockout( PLOTTER *aPlotter, const EDA_RECT &aBbbox )
  804. {
  805. const int margin = 5 * IU_PER_MM; // Add a 5 mm margin around the board
  806. aPlotter->SetNegative( true );
  807. aPlotter->SetColor( WHITE ); // Which will be plotted as black
  808. EDA_RECT area = aBbbox;
  809. area.Inflate( margin );
  810. aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILLED_SHAPE );
  811. aPlotter->SetColor( BLACK );
  812. }
  813. /** Calculate the effective size of HPGL pens and set them in the
  814. * plotter object */
  815. static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter,
  816. PCB_PLOT_PARAMS *aPlotOpts )
  817. {
  818. /* Compute pen_dim (the value is given in mils) in pcb units,
  819. with plot scale (if Scale is 2, pen diameter value is always m_HPGLPenDiam
  820. so apparent pen diam is actually pen diam / Scale */
  821. int pen_diam = KiROUND( aPlotOpts->GetHPGLPenDiameter() * IU_PER_MILS /
  822. aPlotOpts->GetScale() );
  823. // Set HPGL-specific options and start
  824. aPlotter->SetPenSpeed( aPlotOpts->GetHPGLPenSpeed() );
  825. aPlotter->SetPenNumber( aPlotOpts->GetHPGLPenNum() );
  826. aPlotter->SetPenDiameter( pen_diam );
  827. }
  828. /** Open a new plotfile using the options (and especially the format)
  829. * specified in the options and prepare the page for plotting.
  830. * Return the plotter object if OK, NULL if the file is not created
  831. * (or has a problem)
  832. */
  833. PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts,
  834. int aLayer,
  835. const wxString& aFullFileName,
  836. const wxString& aSheetDesc )
  837. {
  838. // Create the plotter driver and set the few plotter specific
  839. // options
  840. PLOTTER* plotter = NULL;
  841. switch( aPlotOpts->GetFormat() )
  842. {
  843. case PLOT_FORMAT_DXF:
  844. plotter = new DXF_PLOTTER();
  845. break;
  846. case PLOT_FORMAT_POST:
  847. PS_PLOTTER* PS_plotter;
  848. PS_plotter = new PS_PLOTTER();
  849. PS_plotter->SetScaleAdjust( aPlotOpts->GetFineScaleAdjustX(),
  850. aPlotOpts->GetFineScaleAdjustY() );
  851. plotter = PS_plotter;
  852. break;
  853. case PLOT_FORMAT_PDF:
  854. plotter = new PDF_PLOTTER();
  855. break;
  856. case PLOT_FORMAT_HPGL:
  857. HPGL_PLOTTER* HPGL_plotter;
  858. HPGL_plotter = new HPGL_PLOTTER();
  859. /* HPGL options are a little more convoluted to compute, so
  860. they're split in an other function */
  861. ConfigureHPGLPenSizes( HPGL_plotter, aPlotOpts );
  862. plotter = HPGL_plotter;
  863. break;
  864. case PLOT_FORMAT_GERBER:
  865. plotter = new GERBER_PLOTTER();
  866. break;
  867. case PLOT_FORMAT_SVG:
  868. plotter = new SVG_PLOTTER();
  869. break;
  870. default:
  871. wxASSERT( false );
  872. return NULL;
  873. }
  874. // Compute the viewport and set the other options
  875. // page layout is not mirrored, so temporary change mirror option
  876. // just to plot the page layout
  877. PCB_PLOT_PARAMS plotOpts = *aPlotOpts;
  878. if( plotOpts.GetPlotFrameRef() && plotOpts.GetMirror() )
  879. plotOpts.SetMirror( false );
  880. initializePlotter( plotter, aBoard, &plotOpts );
  881. if( plotter->OpenFile( aFullFileName ) )
  882. {
  883. plotter->ClearHeaderLinesList();
  884. // For the Gerber "file function" attribute, set the layer number
  885. if( plotter->GetPlotterType() == PLOT_FORMAT_GERBER )
  886. {
  887. bool useX2mode = plotOpts.GetUseGerberAttributes();
  888. if( useX2mode )
  889. {
  890. AddGerberX2Attribute( plotter, aBoard, aLayer, false );
  891. GERBER_PLOTTER* gbrplotter = static_cast <GERBER_PLOTTER*> ( plotter );
  892. gbrplotter->UseX2Attributes( true );
  893. gbrplotter->UseX2NetAttributes( plotOpts.GetIncludeGerberNetlistInfo() );
  894. }
  895. else
  896. {
  897. AddGerberX2Attribute( plotter, aBoard, aLayer, true );
  898. }
  899. }
  900. plotter->StartPlot();
  901. // Plot the frame reference if requested
  902. if( aPlotOpts->GetPlotFrameRef() )
  903. {
  904. PlotWorkSheet( plotter, aBoard->GetTitleBlock(),
  905. aBoard->GetPageSettings(),
  906. 1, 1, // Only one page
  907. aSheetDesc, aBoard->GetFileName() );
  908. if( aPlotOpts->GetMirror() )
  909. initializePlotter( plotter, aBoard, aPlotOpts );
  910. }
  911. /* When plotting a negative board: draw a black rectangle
  912. * (background for plot board in white) and switch the current
  913. * color to WHITE; note the color inversion is actually done
  914. * in the driver (if supported) */
  915. if( aPlotOpts->GetNegative() )
  916. {
  917. EDA_RECT bbox = aBoard->ComputeBoundingBox();
  918. FillNegativeKnockout( plotter, bbox );
  919. }
  920. return plotter;
  921. }
  922. delete plotter;
  923. return NULL;
  924. }