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.

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