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.

563 lines
17 KiB

8 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017 Jon Evans <jon@craftyjon.com>
  5. * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <colors_design_settings.h>
  21. #include <gerbview_painter.h>
  22. #include <gal/graphics_abstraction_layer.h>
  23. #include <convert_basic_shapes_to_polygon.h>
  24. #include <convert_to_biu.h>
  25. #include <gerbview.h>
  26. #include <gerber_draw_item.h>
  27. #include <gerber_file_image.h>
  28. using namespace KIGFX;
  29. GERBVIEW_RENDER_SETTINGS::GERBVIEW_RENDER_SETTINGS()
  30. {
  31. m_backgroundColor = COLOR4D( 0.0, 0.0, 0.0, 1.0 );
  32. m_spotFill = true;
  33. m_lineFill = true;
  34. m_polygonFill = true;
  35. m_showNegativeItems = false;
  36. m_showCodes = false;
  37. m_diffMode = true;
  38. m_componentHighlightString = "";
  39. m_netHighlightString = "";
  40. m_attributeHighlightString = "";
  41. update();
  42. }
  43. void GERBVIEW_RENDER_SETTINGS::ImportLegacyColors( const COLORS_DESIGN_SETTINGS* aSettings )
  44. {
  45. for( int i = GERBVIEW_LAYER_ID_START;
  46. i < GERBVIEW_LAYER_ID_START + GERBER_DRAWLAYERS_COUNT; i++ )
  47. {
  48. COLOR4D baseColor = aSettings->GetLayerColor( i );
  49. if( m_diffMode )
  50. baseColor.a = 0.75;
  51. m_layerColors[i] = baseColor;
  52. m_layerColorsHi[i] = baseColor.Brightened( 0.5 );
  53. m_layerColorsSel[i] = baseColor.Brightened( 0.8 );
  54. m_layerColorsDark[i] = baseColor.Darkened( 0.25 );
  55. }
  56. for( int i = LAYER_DCODES; i < GERBVIEW_LAYER_ID_END; i++ )
  57. m_layerColors[i] = aSettings->GetLayerColor( i );
  58. for( int i = GAL_LAYER_ID_START; i < GAL_LAYER_ID_END; i++ )
  59. m_layerColors[i] = aSettings->GetLayerColor( i );
  60. update();
  61. }
  62. void GERBVIEW_RENDER_SETTINGS::LoadDisplayOptions( const GBR_DISPLAY_OPTIONS* aOptions )
  63. {
  64. if( aOptions == NULL )
  65. return;
  66. m_spotFill = aOptions->m_DisplayFlashedItemsFill;
  67. m_lineFill = aOptions->m_DisplayLinesFill;
  68. m_polygonFill = aOptions->m_DisplayPolygonsFill;
  69. m_showNegativeItems = aOptions->m_DisplayNegativeObjects;
  70. m_showCodes = aOptions->m_DisplayDCodes;
  71. m_diffMode = aOptions->m_DiffMode;
  72. m_hiContrastEnabled = aOptions->m_HighContrastMode;
  73. m_showPageLimits = aOptions->m_DisplayPageLimits;
  74. update();
  75. }
  76. const COLOR4D& GERBVIEW_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) const
  77. {
  78. const GERBER_DRAW_ITEM* item = static_cast<const GERBER_DRAW_ITEM*>( aItem );
  79. static const COLOR4D transparent = COLOR4D( 0, 0, 0, 0 );
  80. // All DCODE layers stored under a single color setting
  81. if( IsDCodeLayer( aLayer ) )
  82. return m_layerColors[ LAYER_DCODES ];
  83. if( aLayer == LAYER_WORKSHEET )
  84. return m_layerColors[ LAYER_WORKSHEET ];
  85. if( item )
  86. {
  87. if( item->IsSelected() )
  88. return m_layerColorsSel[aLayer];
  89. if( item->GetLayerPolarity() )
  90. {
  91. if( m_showNegativeItems )
  92. return m_layerColors[LAYER_NEGATIVE_OBJECTS];
  93. else
  94. return transparent;
  95. }
  96. }
  97. if( !m_netHighlightString.IsEmpty() && item &&
  98. m_netHighlightString == item->GetNetAttributes().m_Netname )
  99. return m_layerColorsHi[aLayer];
  100. if( !m_componentHighlightString.IsEmpty() && item &&
  101. m_componentHighlightString == item->GetNetAttributes().m_Cmpref )
  102. return m_layerColorsHi[aLayer];
  103. if( !m_attributeHighlightString.IsEmpty() && item && item->GetDcodeDescr() &&
  104. m_attributeHighlightString == item->GetDcodeDescr()->m_AperFunction )
  105. return m_layerColorsHi[aLayer];
  106. // Return grayish color for non-highlighted layers in the high contrast mode
  107. if( m_hiContrastEnabled && m_activeLayers.count( aLayer ) == 0)
  108. return m_hiContrastColor;
  109. // Catch the case when highlight and high-contraste modes are enabled
  110. // and we are drawing a not highlighted track
  111. if( m_highlightEnabled )
  112. return m_layerColorsDark[aLayer];
  113. // No special modificators enabled
  114. return m_layerColors[aLayer];
  115. }
  116. GERBVIEW_PAINTER::GERBVIEW_PAINTER( GAL* aGal ) :
  117. PAINTER( aGal )
  118. {
  119. }
  120. // TODO(JE): Pull up to PAINTER?
  121. int GERBVIEW_PAINTER::getLineThickness( int aActualThickness ) const
  122. {
  123. // if items have 0 thickness, draw them with the outline
  124. // width, otherwise respect the set value (which, no matter
  125. // how small will produce something)
  126. if( aActualThickness == 0 )
  127. return m_gerbviewSettings.m_outlineWidth;
  128. return aActualThickness;
  129. }
  130. bool GERBVIEW_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
  131. {
  132. const EDA_ITEM* item = static_cast<const EDA_ITEM*>( aItem );
  133. // the "cast" applied in here clarifies which overloaded draw() is called
  134. switch( item->Type() )
  135. {
  136. case GERBER_DRAW_ITEM_T:
  137. draw( static_cast<GERBER_DRAW_ITEM*>( const_cast<EDA_ITEM*>( item ) ), aLayer );
  138. break;
  139. default:
  140. // Painter does not know how to draw the object
  141. return false;
  142. }
  143. return true;
  144. }
  145. // TODO(JE) aItem can't be const because of GetDcodeDescr()
  146. // Probably that can be refactored in GERBER_DRAW_ITEM to allow const here.
  147. void GERBVIEW_PAINTER::draw( /*const*/ GERBER_DRAW_ITEM* aItem, int aLayer )
  148. {
  149. VECTOR2D start( aItem->GetABPosition( aItem->m_Start ) ); // TODO(JE) Getter
  150. VECTOR2D end( aItem->GetABPosition( aItem->m_End ) ); // TODO(JE) Getter
  151. int width = aItem->m_Size.x; // TODO(JE) Getter
  152. bool isFilled = true;
  153. COLOR4D color;
  154. // TODO(JE) This doesn't actually work properly for ImageNegative
  155. bool isNegative = ( aItem->GetLayerPolarity() ^ aItem->m_GerberImageFile->m_ImageNegative );
  156. // Draw DCODE overlay text
  157. if( IsDCodeLayer( aLayer ) )
  158. {
  159. wxString codeText;
  160. VECTOR2D textPosition;
  161. double textSize;
  162. double orient;
  163. if( !aItem->GetTextD_CodePrms( textSize, textPosition, orient ) )
  164. return;
  165. color = m_gerbviewSettings.GetColor( aItem, aLayer );
  166. codeText.Printf( "D%d", aItem->m_DCode );
  167. m_gal->SetIsStroke( true );
  168. m_gal->SetIsFill( false );
  169. m_gal->SetStrokeColor( color );
  170. m_gal->SetFillColor( COLOR4D( 0, 0, 0, 0 ) );
  171. m_gal->SetLineWidth( 2 );
  172. m_gal->SetFontBold( false );
  173. m_gal->SetFontItalic( false );
  174. m_gal->SetTextMirrored( false );
  175. m_gal->SetGlyphSize( VECTOR2D( textSize, textSize) );
  176. m_gal->SetHorizontalJustify( GR_TEXT_HJUSTIFY_CENTER );
  177. m_gal->SetVerticalJustify( GR_TEXT_VJUSTIFY_CENTER );
  178. m_gal->BitmapText( codeText, textPosition, orient );
  179. return;
  180. }
  181. color = m_gerbviewSettings.GetColor( aItem, aLayer );
  182. // TODO: Should brightened color be a preference?
  183. if( aItem->IsBrightened() )
  184. color = COLOR4D( 0.0, 1.0, 0.0, 0.75 );
  185. m_gal->SetNegativeDrawMode( isNegative );
  186. m_gal->SetStrokeColor( color );
  187. m_gal->SetFillColor( color );
  188. m_gal->SetIsFill( isFilled );
  189. m_gal->SetIsStroke( !isFilled );
  190. switch( aItem->m_Shape )
  191. {
  192. case GBR_POLYGON:
  193. {
  194. isFilled = m_gerbviewSettings.m_polygonFill;
  195. m_gal->SetIsFill( isFilled );
  196. m_gal->SetIsStroke( !isFilled );
  197. if( isNegative && !isFilled )
  198. {
  199. m_gal->SetNegativeDrawMode( false );
  200. m_gal->SetStrokeColor( GetSettings()->GetColor( aItem, aLayer ) );
  201. }
  202. if( !isFilled )
  203. m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
  204. SHAPE_POLY_SET absolutePolygon = aItem->m_Polygon;
  205. for( auto it = absolutePolygon.Iterate( 0 ); it; ++it )
  206. *it = aItem->GetABPosition( *it );
  207. if( !isFilled )
  208. m_gal->DrawPolyline( absolutePolygon.COutline( 0 ) );
  209. else
  210. m_gal->DrawPolygon( absolutePolygon );
  211. break;
  212. }
  213. case GBR_CIRCLE:
  214. {
  215. isFilled = m_gerbviewSettings.m_lineFill;
  216. double radius = GetLineLength( aItem->m_Start, aItem->m_End );
  217. m_gal->DrawCircle( start, radius );
  218. break;
  219. }
  220. case GBR_ARC:
  221. {
  222. isFilled = m_gerbviewSettings.m_lineFill;
  223. // These are swapped because wxDC fills arcs counterclockwise and GAL
  224. // fills them clockwise.
  225. wxPoint arcStart = aItem->m_End;
  226. wxPoint arcEnd = aItem->m_Start;
  227. // Gerber arcs are 3-point (start, center, end)
  228. // GAL needs center, radius, start angle, end angle
  229. double radius = GetLineLength( arcStart, aItem->m_ArcCentre );
  230. VECTOR2D center = aItem->GetABPosition( aItem->m_ArcCentre );
  231. VECTOR2D startVec = VECTOR2D( aItem->GetABPosition( arcStart ) ) - center;
  232. VECTOR2D endVec = VECTOR2D( aItem->GetABPosition( arcEnd ) ) - center;
  233. m_gal->SetIsFill( isFilled );
  234. m_gal->SetIsStroke( !isFilled );
  235. m_gal->SetLineWidth( isFilled ? width : m_gerbviewSettings.m_outlineWidth );
  236. double startAngle = startVec.Angle();
  237. double endAngle = endVec.Angle();
  238. // GAL fills in direction of increasing angle, so we have to convert
  239. // the angle from the -PI to PI domain of atan2() to ensure that
  240. // the arc goes in the right direction
  241. if( startAngle > endAngle )
  242. endAngle += (2 * M_PI);
  243. // 360-degree arcs are stored in the file with start equal to end
  244. if( arcStart == arcEnd )
  245. {
  246. startAngle = 0;
  247. endAngle = 2 * M_PI;
  248. }
  249. m_gal->DrawArcSegment( center, radius, startAngle, endAngle, width );
  250. // Arc Debugging
  251. // m_gal->SetLineWidth( 5 );
  252. // m_gal->SetStrokeColor( COLOR4D( 0.0, 1.0, 0.0, 1.0 ) );
  253. // m_gal->DrawLine( center, aItem->GetABPosition( arcStart ) );
  254. // m_gal->SetStrokeColor( COLOR4D( 1.0, 0.0, 0.0, 1.0 ) );
  255. // m_gal->DrawLine( center, aItem->GetABPosition( arcEnd ) );
  256. break;
  257. }
  258. case GBR_SPOT_CIRCLE:
  259. case GBR_SPOT_RECT:
  260. case GBR_SPOT_OVAL:
  261. case GBR_SPOT_POLY:
  262. case GBR_SPOT_MACRO:
  263. {
  264. isFilled = m_gerbviewSettings.m_spotFill;
  265. drawFlashedShape( aItem, isFilled );
  266. break;
  267. }
  268. case GBR_SEGMENT:
  269. {
  270. /* Plot a line from m_Start to m_End.
  271. * Usually, a round pen is used, but some gerber files use a rectangular pen
  272. * In fact, any aperture can be used to plot a line.
  273. * currently: only a square pen is handled (I believe using a polygon gives a strange plot).
  274. */
  275. isFilled = m_gerbviewSettings.m_lineFill;
  276. m_gal->SetIsFill( isFilled );
  277. m_gal->SetIsStroke( !isFilled );
  278. if( isNegative && !isFilled )
  279. m_gal->SetStrokeColor( GetSettings()->GetColor( aItem, aLayer ) );
  280. // TODO(JE) Refactor this to allow const aItem
  281. D_CODE* code = aItem->GetDcodeDescr();
  282. if( code && code->m_Shape == APT_RECT )
  283. {
  284. if( aItem->m_Polygon.OutlineCount() == 0 )
  285. aItem->ConvertSegmentToPolygon();
  286. drawPolygon( aItem, aItem->m_Polygon, isFilled );
  287. }
  288. else
  289. {
  290. if( !isFilled )
  291. m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
  292. m_gal->DrawSegment( start, end, width );
  293. }
  294. break;
  295. }
  296. default:
  297. wxASSERT_MSG( false, wxT( "GERBER_DRAW_ITEM shape is unknown!" ) );
  298. break;
  299. }
  300. // Enable for bounding box debugging
  301. #if 0
  302. const BOX2I& bb = aItem->ViewBBox();
  303. m_gal->SetIsStroke( true );
  304. m_gal->SetIsFill( true );
  305. m_gal->SetLineWidth( 3 );
  306. m_gal->SetStrokeColor( COLOR4D(0.9, 0.9, 0, 0.4) );
  307. m_gal->SetFillColor( COLOR4D(0.9, 0.9, 0, 0.1) );
  308. m_gal->DrawRectangle( bb.GetOrigin(), bb.GetEnd() );
  309. #endif
  310. }
  311. void GERBVIEW_PAINTER::drawPolygon( GERBER_DRAW_ITEM* aParent,
  312. SHAPE_POLY_SET& aPolygon,
  313. bool aFilled )
  314. {
  315. for( auto it = aPolygon.Iterate( 0 ); it; ++it )
  316. *it = aParent->GetABPosition( *it );
  317. if( !m_gerbviewSettings.m_polygonFill )
  318. m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
  319. if( !aFilled )
  320. {
  321. for( int i = 0; i < aPolygon.OutlineCount(); i++ )
  322. m_gal->DrawPolyline( aPolygon.COutline( i ) );
  323. }
  324. else
  325. m_gal->DrawPolygon( aPolygon );
  326. }
  327. void GERBVIEW_PAINTER::drawFlashedShape( GERBER_DRAW_ITEM* aItem, bool aFilled )
  328. {
  329. D_CODE* code = aItem->GetDcodeDescr();
  330. wxASSERT_MSG( code, wxT( "drawFlashedShape: Item has no D_CODE!" ) );
  331. if( !code )
  332. return;
  333. m_gal->SetIsFill( aFilled );
  334. m_gal->SetIsStroke( !aFilled );
  335. m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
  336. switch( aItem->m_Shape )
  337. {
  338. case GBR_SPOT_CIRCLE:
  339. {
  340. int radius = code->m_Size.x >> 1;
  341. VECTOR2D start( aItem->GetABPosition( aItem->m_Start ) );
  342. if( !aFilled )
  343. {
  344. m_gal->DrawCircle( start, radius );
  345. }
  346. else
  347. {
  348. if( code->m_DrillShape == APT_DEF_NO_HOLE )
  349. {
  350. m_gal->DrawCircle( start, radius );
  351. }
  352. else // rectangular hole
  353. {
  354. if( code->m_Polygon.OutlineCount() == 0 )
  355. code->ConvertShapeToPolygon();
  356. SHAPE_POLY_SET poly = code->m_Polygon;
  357. poly.Move( aItem->m_Start );
  358. drawPolygon( aItem, poly, aFilled );
  359. }
  360. }
  361. break;
  362. }
  363. case GBR_SPOT_RECT:
  364. {
  365. wxPoint codeStart;
  366. wxPoint aShapePos = aItem->m_Start;
  367. codeStart.x = aShapePos.x - code->m_Size.x / 2;
  368. codeStart.y = aShapePos.y - code->m_Size.y / 2;
  369. wxPoint codeEnd = codeStart + code->m_Size;
  370. codeStart = aItem->GetABPosition( codeStart );
  371. codeEnd = aItem->GetABPosition( codeEnd );
  372. if( !aFilled || code->m_DrillShape == APT_DEF_NO_HOLE )
  373. {
  374. m_gal->DrawRectangle( VECTOR2D( codeStart ), VECTOR2D( codeEnd ) );
  375. }
  376. else
  377. {
  378. if( code->m_Polygon.OutlineCount() == 0 )
  379. code->ConvertShapeToPolygon();
  380. SHAPE_POLY_SET poly = code->m_Polygon;
  381. poly.Move( aItem->m_Start );
  382. drawPolygon( aItem, poly, aFilled );
  383. }
  384. break;
  385. }
  386. case GBR_SPOT_OVAL:
  387. {
  388. int radius = 0;
  389. wxPoint codeStart = aItem->m_Start;
  390. wxPoint codeEnd = aItem->m_Start;
  391. if( code->m_Size.x > code->m_Size.y ) // horizontal oval
  392. {
  393. int delta = (code->m_Size.x - code->m_Size.y) / 2;
  394. codeStart.x -= delta;
  395. codeEnd.x += delta;
  396. radius = code->m_Size.y;
  397. }
  398. else // horizontal oval
  399. {
  400. int delta = (code->m_Size.y - code->m_Size.x) / 2;
  401. codeStart.y -= delta;
  402. codeEnd.y += delta;
  403. radius = code->m_Size.x;
  404. }
  405. codeStart = aItem->GetABPosition( codeStart );
  406. codeEnd = aItem->GetABPosition( codeEnd );
  407. if( !aFilled || code->m_DrillShape == APT_DEF_NO_HOLE )
  408. {
  409. m_gal->DrawSegment( codeStart, codeEnd, radius );
  410. }
  411. else
  412. {
  413. if( code->m_Polygon.OutlineCount() == 0 )
  414. code->ConvertShapeToPolygon();
  415. SHAPE_POLY_SET poly = code->m_Polygon;
  416. poly.Move( aItem->m_Start );
  417. drawPolygon( aItem, poly, aFilled );
  418. }
  419. break;
  420. }
  421. case GBR_SPOT_POLY:
  422. {
  423. if( code->m_Polygon.OutlineCount() == 0 )
  424. code->ConvertShapeToPolygon();
  425. SHAPE_POLY_SET poly = code->m_Polygon;
  426. poly.Move( aItem->m_Start );
  427. drawPolygon( aItem, poly, aFilled );
  428. break;
  429. }
  430. case GBR_SPOT_MACRO:
  431. drawApertureMacro( aItem, aFilled );
  432. break;
  433. default:
  434. wxASSERT_MSG( false, wxT( "Unknown Gerber flashed shape!" ) );
  435. break;
  436. }
  437. }
  438. void GERBVIEW_PAINTER::drawApertureMacro( GERBER_DRAW_ITEM* aParent, bool aFilled )
  439. {
  440. D_CODE* code = aParent->GetDcodeDescr();
  441. APERTURE_MACRO* macro = code->GetMacro();
  442. SHAPE_POLY_SET* macroShape = macro->GetApertureMacroShape( aParent, aParent->m_Start );
  443. if( !m_gerbviewSettings.m_polygonFill )
  444. m_gal->SetLineWidth( m_gerbviewSettings.m_outlineWidth );
  445. if( !aFilled )
  446. {
  447. for( int i = 0; i < macroShape->OutlineCount(); i++ )
  448. m_gal->DrawPolyline( macroShape->COutline( i ) );
  449. }
  450. else
  451. m_gal->DrawPolygon( *macroShape );
  452. }
  453. const double GERBVIEW_RENDER_SETTINGS::MAX_FONT_SIZE = Millimeter2iu( 10.0 );