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.

1154 lines
40 KiB

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