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.

2917 lines
98 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014 CERN
  5. * Copyright (C) 2019-2024 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <trigo.h>
  27. #include <connection_graph.h>
  28. #include <gal/graphics_abstraction_layer.h>
  29. #include <callback_gal.h>
  30. #include <geometry/shape_segment.h>
  31. #include <geometry/shape_rect.h>
  32. #include <gr_text.h>
  33. #include <sch_pin.h>
  34. #include <math/util.h>
  35. #include <pgm_base.h>
  36. #include <sch_bitmap.h>
  37. #include <sch_bus_entry.h>
  38. #include <sch_symbol.h>
  39. #include <sch_edit_frame.h>
  40. #include <sch_field.h>
  41. #include <sch_junction.h>
  42. #include <sch_line.h>
  43. #include <sch_shape.h>
  44. #include <sch_marker.h>
  45. #include <sch_no_connect.h>
  46. #include <sch_sheet.h>
  47. #include <sch_sheet_pin.h>
  48. #include <sch_text.h>
  49. #include <sch_textbox.h>
  50. #include <sch_table.h>
  51. #include <schematic.h>
  52. #include <settings/color_settings.h>
  53. #include <view/view.h>
  54. #include <kiface_base.h>
  55. #include <default_values.h>
  56. #include <advanced_config.h>
  57. #include <settings/settings_manager.h>
  58. #include <stroke_params.h>
  59. #include "sch_painter.h"
  60. #include "common.h"
  61. #include "symb_transforms_utils.h"
  62. namespace KIGFX
  63. {
  64. EESCHEMA_SETTINGS* eeconfig()
  65. {
  66. return dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
  67. }
  68. std::vector<KICAD_T> SCH_PAINTER::g_ScaledSelectionTypes = {
  69. SCH_MARKER_T,
  70. SCH_JUNCTION_T,
  71. SCH_NO_CONNECT_T,
  72. SCH_BUS_WIRE_ENTRY_T,
  73. SCH_BUS_BUS_ENTRY_T,
  74. SCH_LINE_T,
  75. SCH_SHAPE_T,
  76. SCH_RULE_AREA_T,
  77. SCH_BITMAP_T,
  78. SCH_TEXT_T,
  79. SCH_GLOBAL_LABEL_T,
  80. SCH_DIRECTIVE_LABEL_T,
  81. SCH_FIELD_T,
  82. SCH_HIER_LABEL_T,
  83. SCH_SHEET_PIN_T,
  84. LIB_SYMBOL_T, SCH_SYMBOL_T,
  85. SCH_SHEET_T,
  86. SCH_PIN_T
  87. };
  88. /**
  89. * Used when a LIB_SYMBOL is not found in library to draw a dummy shape.
  90. * This symbol is a 400 mils square with the text "??"
  91. *
  92. * DEF DUMMY U 0 40 Y Y 1 0 N
  93. * F0 "U" 0 -350 60 H V
  94. * F1 "DUMMY" 0 350 60 H V
  95. * DRAW
  96. * T 0 0 0 150 0 0 0 ??
  97. * S -200 200 200 -200 0 1 0
  98. * ENDDRAW
  99. * ENDDEF
  100. */
  101. static LIB_SYMBOL* dummy()
  102. {
  103. static LIB_SYMBOL* symbol;
  104. if( !symbol )
  105. {
  106. symbol = new LIB_SYMBOL( wxEmptyString );
  107. SCH_SHAPE* square = new SCH_SHAPE( SHAPE_T::RECTANGLE, LAYER_DEVICE );
  108. square->SetPosition( VECTOR2I( schIUScale.MilsToIU( -200 ), schIUScale.MilsToIU( 200 ) ) );
  109. square->SetEnd( VECTOR2I( schIUScale.MilsToIU( 200 ), schIUScale.MilsToIU( -200 ) ) );
  110. symbol->AddDrawItem( square );
  111. SCH_TEXT* text = new SCH_TEXT( { 0, 0 }, wxT( "??" ), LAYER_DEVICE );
  112. text->SetTextSize( VECTOR2I( schIUScale.MilsToIU( 150 ), schIUScale.MilsToIU( 150 ) ) );
  113. symbol->AddDrawItem( text );
  114. }
  115. return symbol;
  116. }
  117. SCH_PAINTER::SCH_PAINTER( GAL* aGal ) :
  118. KIGFX::PAINTER( aGal ),
  119. m_schematic( nullptr )
  120. { }
  121. bool SCH_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
  122. {
  123. const EDA_ITEM* item = dynamic_cast<const EDA_ITEM*>( aItem );
  124. if( !item )
  125. return false;
  126. draw( item, aLayer, false );
  127. return false;
  128. }
  129. void SCH_PAINTER::draw( const EDA_ITEM* aItem, int aLayer, bool aDimmed )
  130. {
  131. #ifdef CONNECTIVITY_DEBUG
  132. auto sch_item = dynamic_cast<const SCH_ITEM*>( aItem );
  133. auto conn = sch_item ? sch_item->Connection( *g_CurrentSheet ) : nullptr;
  134. if( conn )
  135. {
  136. auto pos = aItem->GetBoundingBox().Centre();
  137. auto label = conn->Name( true );
  138. m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
  139. m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
  140. m_gal->SetStrokeColor( COLOR4D( LIGHTRED ) );
  141. m_gal->SetLineWidth( Mils2ui( 2 ) );
  142. m_gal->SetGlyphSize( VECTOR2D( Mils2ui( 20 ), Mils2ui( 20 ) ) );
  143. m_gal->StrokeText( conn->Name( true ), pos, 0.0, 0 );
  144. }
  145. #endif
  146. // Enable draw bounding box on request. Some bboxes are handled locally.
  147. bool drawBoundingBox = m_schSettings.GetDrawBoundingBoxes();
  148. switch( aItem->Type() )
  149. {
  150. case LIB_SYMBOL_T:
  151. draw( static_cast<const LIB_SYMBOL*>( aItem ), aLayer );
  152. break;
  153. case SCH_PIN_T:
  154. drawBoundingBox = false;
  155. draw( static_cast<const SCH_PIN*>( aItem ), aLayer, aDimmed );
  156. break;
  157. case SCH_SYMBOL_T:
  158. draw( static_cast<const SCH_SYMBOL*>( aItem ), aLayer );
  159. break;
  160. case SCH_JUNCTION_T:
  161. draw( static_cast<const SCH_JUNCTION*>( aItem ), aLayer );
  162. break;
  163. case SCH_LINE_T:
  164. draw( static_cast<const SCH_LINE*>( aItem ), aLayer );
  165. break;
  166. case SCH_SHAPE_T:
  167. draw( static_cast<const SCH_SHAPE*>( aItem ), aLayer, aDimmed );
  168. break;
  169. case SCH_RULE_AREA_T:
  170. draw( static_cast<const SCH_SHAPE*>( aItem ), aLayer, aDimmed );
  171. break;
  172. case SCH_TEXT_T:
  173. draw( static_cast<const SCH_TEXT*>( aItem ), aLayer, aDimmed );
  174. break;
  175. case SCH_TEXTBOX_T:
  176. draw( static_cast<const SCH_TEXTBOX*>( aItem ), aLayer, aDimmed );
  177. break;
  178. case SCH_TABLE_T:
  179. draw( static_cast<const SCH_TABLE*>( aItem ), aLayer, aDimmed );
  180. break;
  181. case SCH_LABEL_T:
  182. draw( static_cast<const SCH_LABEL*>( aItem ), aLayer );
  183. break;
  184. case SCH_DIRECTIVE_LABEL_T:
  185. draw( static_cast<const SCH_DIRECTIVE_LABEL*>( aItem ), aLayer );
  186. break;
  187. case SCH_FIELD_T:
  188. draw( static_cast<const SCH_FIELD*>( aItem ), aLayer, aDimmed );
  189. break;
  190. case SCH_HIER_LABEL_T:
  191. draw( static_cast<const SCH_HIERLABEL*>( aItem ), aLayer, aDimmed );
  192. break;
  193. case SCH_GLOBAL_LABEL_T:
  194. draw( static_cast<const SCH_GLOBALLABEL*>( aItem ), aLayer );
  195. break;
  196. case SCH_SHEET_T:
  197. draw( static_cast<const SCH_SHEET*>( aItem ), aLayer );
  198. break;
  199. case SCH_SHEET_PIN_T:
  200. draw( static_cast<const SCH_HIERLABEL*>( aItem ), aLayer, aDimmed );
  201. break;
  202. case SCH_NO_CONNECT_T:
  203. draw( static_cast<const SCH_NO_CONNECT*>( aItem ), aLayer );
  204. break;
  205. case SCH_BUS_WIRE_ENTRY_T:
  206. draw( static_cast<const SCH_BUS_ENTRY_BASE*>( aItem ), aLayer );
  207. break;
  208. case SCH_BUS_BUS_ENTRY_T:
  209. draw( static_cast<const SCH_BUS_ENTRY_BASE*>( aItem ), aLayer );
  210. break;
  211. case SCH_BITMAP_T:
  212. draw( static_cast<const SCH_BITMAP*>( aItem ), aLayer );
  213. break;
  214. case SCH_MARKER_T:
  215. draw( static_cast<const SCH_MARKER*>( aItem ), aLayer );
  216. break;
  217. default: return;
  218. }
  219. if( drawBoundingBox )
  220. drawItemBoundingBox( aItem );
  221. }
  222. void SCH_PAINTER::drawItemBoundingBox( const EDA_ITEM* aItem )
  223. {
  224. if( const SCH_ITEM* item = dynamic_cast<const SCH_ITEM*>( aItem ) )
  225. {
  226. if( item->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
  227. return;
  228. }
  229. BOX2I box = aItem->GetBoundingBox();
  230. if( aItem->Type() == SCH_SYMBOL_T )
  231. box = static_cast<const SCH_SYMBOL*>( aItem )->GetBodyBoundingBox();
  232. m_gal->SetIsFill( false );
  233. m_gal->SetIsStroke( true );
  234. m_gal->SetStrokeColor( aItem->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 )
  235. : COLOR4D( 0.2, 0.2, 0.2, 1 ) );
  236. m_gal->SetLineWidth( schIUScale.MilsToIU( 3 ) );
  237. m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
  238. }
  239. bool SCH_PAINTER::nonCached( const EDA_ITEM* aItem )
  240. {
  241. // TODO: it would be nice to have a more definitive test for this, but we've currently got
  242. // no access to the VIEW_GROUP to see if it's cached or not.
  243. return aItem->IsSelected();
  244. }
  245. bool SCH_PAINTER::isUnitAndConversionShown( const SCH_ITEM* aItem ) const
  246. {
  247. if( m_schSettings.m_ShowUnit // showing a specific unit
  248. && aItem->GetUnit() // item is unit-specific
  249. && aItem->GetUnit() != m_schSettings.m_ShowUnit )
  250. {
  251. return false;
  252. }
  253. if( m_schSettings.m_ShowBodyStyle // showing a specific body style
  254. && aItem->GetBodyStyle() // item is body-style-specific
  255. && aItem->GetBodyStyle() != m_schSettings.m_ShowBodyStyle )
  256. {
  257. return false;
  258. }
  259. return true;
  260. }
  261. KIFONT::FONT* SCH_PAINTER::getFont( const EDA_TEXT* aItem ) const
  262. {
  263. if( KIFONT::FONT* font = aItem->GetFont() )
  264. return font;
  265. return KIFONT::FONT::GetFont( m_schSettings.GetDefaultFont(), aItem->IsBold(),
  266. aItem->IsItalic() );
  267. }
  268. float SCH_PAINTER::getShadowWidth( bool aForHighlight ) const
  269. {
  270. const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
  271. int milsWidth = aForHighlight ? eeconfig()->m_Selection.highlight_thickness
  272. : eeconfig()->m_Selection.selection_thickness;
  273. // For best visuals the selection width must be a cross between the zoom level and the
  274. // default line width.
  275. return (float) std::fabs( matrix.GetScale().x * milsWidth ) + schIUScale.MilsToIU( milsWidth );
  276. }
  277. COLOR4D SCH_PAINTER::getRenderColor( const SCH_ITEM* aItem, int aLayer, bool aDrawingShadows,
  278. bool aDimmed ) const
  279. {
  280. COLOR4D color = m_schSettings.GetLayerColor( aLayer );
  281. // Graphic items of a SYMBOL frequently use the LAYER_DEVICE layer color
  282. // (i.e. when no specific color is set)
  283. bool isSymbolChild = aItem->GetParentSymbol() != nullptr;
  284. if( aItem->Type() == SCH_LINE_T )
  285. {
  286. color = static_cast<const SCH_LINE*>( aItem )->GetLineColor();
  287. }
  288. else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
  289. {
  290. color = static_cast<const SCH_BUS_WIRE_ENTRY*>( aItem )->GetBusEntryColor();
  291. }
  292. else if( aItem->Type() == SCH_JUNCTION_T )
  293. {
  294. color = static_cast<const SCH_JUNCTION*>( aItem )->GetJunctionColor();
  295. }
  296. else if( !m_schSettings.m_OverrideItemColors )
  297. {
  298. if( aItem->Type() == SCH_SHEET_T )
  299. {
  300. const SCH_SHEET* sheet = static_cast<const SCH_SHEET*>( aItem );
  301. if( aLayer == LAYER_SHEET_BACKGROUND )
  302. color = sheet->GetBackgroundColor();
  303. else
  304. color = sheet->GetBorderColor();
  305. }
  306. else if( aItem->Type() == SCH_SHAPE_T || aItem->Type() == SCH_RULE_AREA_T )
  307. {
  308. const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( aItem );
  309. if( aLayer == LAYER_DEVICE_BACKGROUND || aLayer == LAYER_NOTES_BACKGROUND )
  310. {
  311. if( !isSymbolChild || shape->GetFillColor() != COLOR4D::UNSPECIFIED )
  312. color = shape->GetFillColor();
  313. if( isSymbolChild )
  314. {
  315. if( shape->GetFillMode() == FILL_T::FILLED_SHAPE )
  316. color = shape->GetStroke().GetColor();
  317. else if( shape->GetFillMode() == FILL_T::FILLED_WITH_BG_BODYCOLOR )
  318. color = m_schSettings.GetLayerColor( LAYER_DEVICE_BACKGROUND );
  319. }
  320. }
  321. else
  322. {
  323. if( !isSymbolChild || shape->GetStroke().GetColor() != COLOR4D::UNSPECIFIED )
  324. color = shape->GetStroke().GetColor();
  325. }
  326. // A filled shape means filled; if they didn't specify a fill colour then use the
  327. // border colour.
  328. if( color == COLOR4D::UNSPECIFIED )
  329. {
  330. if( aItem->Type() == SCH_RULE_AREA_T )
  331. {
  332. color = m_schSettings.GetLayerColor( LAYER_RULE_AREAS );
  333. }
  334. else
  335. {
  336. color = m_schSettings.GetLayerColor( isSymbolChild ? LAYER_DEVICE
  337. : LAYER_NOTES );
  338. }
  339. }
  340. }
  341. else if( aItem->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
  342. {
  343. color = static_cast<const SCH_LABEL_BASE*>( aItem )->GetLabelColor();
  344. }
  345. else if( aItem->Type() == SCH_FIELD_T )
  346. {
  347. color = static_cast<const SCH_FIELD*>( aItem )->GetFieldColor();
  348. }
  349. else if( aItem->Type() == SCH_TEXTBOX_T || aItem->Type() == SCH_TABLECELL_T )
  350. {
  351. const SCH_TEXTBOX* textBox = dynamic_cast<const SCH_TEXTBOX*>( aItem );
  352. if( aLayer == LAYER_DEVICE_BACKGROUND || aLayer == LAYER_NOTES_BACKGROUND )
  353. color = textBox->GetFillColor();
  354. else if( !isSymbolChild || textBox->GetTextColor() != COLOR4D::UNSPECIFIED )
  355. color = textBox->GetTextColor();
  356. }
  357. else if( const EDA_TEXT* otherTextItem = dynamic_cast<const EDA_TEXT*>( aItem ) )
  358. {
  359. if( !isSymbolChild || otherTextItem->GetTextColor() != COLOR4D::UNSPECIFIED )
  360. color = otherTextItem->GetTextColor();
  361. }
  362. }
  363. if( color == COLOR4D::UNSPECIFIED )
  364. color = m_schSettings.GetLayerColor( aLayer );
  365. if( aItem->IsBrightened() ) // Selection disambiguation, net highlighting, etc.
  366. {
  367. color = m_schSettings.GetLayerColor( LAYER_BRIGHTENED );
  368. if( aDrawingShadows )
  369. {
  370. if( aItem->IsSelected() )
  371. color = m_schSettings.GetLayerColor( LAYER_SELECTION_SHADOWS );
  372. else
  373. color = color.WithAlpha( 0.15 );
  374. }
  375. else if( aLayer == LAYER_DEVICE_BACKGROUND || aLayer == LAYER_SHEET_BACKGROUND )
  376. {
  377. color = color.WithAlpha( 0.2 );
  378. }
  379. }
  380. else if( aItem->IsSelected() && aDrawingShadows )
  381. {
  382. color = m_schSettings.GetLayerColor( LAYER_SELECTION_SHADOWS );
  383. }
  384. else if( aItem->IsSelected() && ( aLayer == LAYER_DEVICE_BACKGROUND
  385. || aLayer == LAYER_SHEET_BACKGROUND
  386. || aLayer == LAYER_NOTES_BACKGROUND ) )
  387. {
  388. // Selected items will be painted over all other items, so make backgrounds translucent so
  389. // that non-selected overlapping objects are visible
  390. color = color.WithAlpha( 0.5 );
  391. }
  392. if( m_schSettings.m_ShowDisabled
  393. || ( m_schSettings.m_ShowGraphicsDisabled && aItem->Type() != SCH_FIELD_T ) )
  394. {
  395. color = color.Darken( 0.5f );
  396. }
  397. if( aDimmed && !( aItem->IsSelected() && aDrawingShadows ) )
  398. {
  399. COLOR4D sheetColour = m_schSettings.GetLayerColor( LAYER_SCHEMATIC_BACKGROUND );
  400. color.Desaturate();
  401. color = color.Mix( sheetColour, 0.5f );
  402. }
  403. if( aItem->GetForcedTransparency() > 0.0 )
  404. color = color.WithAlpha( color.a * ( 1.0 - aItem->GetForcedTransparency() ) );
  405. return color;
  406. }
  407. float SCH_PAINTER::getLineWidth( const SCH_ITEM* aItem, bool aDrawingShadows ) const
  408. {
  409. wxCHECK( aItem, static_cast<float>( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ) ) );
  410. int pen = aItem->GetEffectivePenWidth( &m_schSettings );
  411. float width = pen;
  412. if( aItem->IsBrightened() || aItem->IsSelected() )
  413. {
  414. if( aDrawingShadows && aItem->IsType( g_ScaledSelectionTypes ) )
  415. width += getShadowWidth( aItem->IsBrightened() );
  416. }
  417. return width;
  418. }
  419. float SCH_PAINTER::getTextThickness( const SCH_ITEM* aItem ) const
  420. {
  421. int pen = m_schSettings.GetDefaultPenWidth();
  422. switch( aItem->Type() )
  423. {
  424. case SCH_FIELD_T:
  425. pen = static_cast<const SCH_FIELD*>( aItem )->GetEffectiveTextPenWidth( pen );
  426. break;
  427. case SCH_TEXT_T:
  428. pen = static_cast<const SCH_TEXT*>( aItem )->GetEffectiveTextPenWidth( pen );
  429. break;
  430. case SCH_LABEL_T:
  431. case SCH_DIRECTIVE_LABEL_T:
  432. case SCH_GLOBAL_LABEL_T:
  433. case SCH_HIER_LABEL_T:
  434. case SCH_SHEET_PIN_T:
  435. pen = static_cast<const SCH_LABEL_BASE*>( aItem )->GetEffectiveTextPenWidth( pen );
  436. break;
  437. case SCH_TEXTBOX_T:
  438. case SCH_TABLECELL_T:
  439. pen = static_cast<const SCH_TEXTBOX*>( aItem )->GetEffectiveTextPenWidth( pen );
  440. break;
  441. default:
  442. UNIMPLEMENTED_FOR( aItem->GetClass() );
  443. }
  444. return (float) pen;
  445. }
  446. int SCH_PAINTER::getOperatingPointTextSize() const
  447. {
  448. int docTextSize = schIUScale.MilsToIU( 50 );
  449. int screenTextSize = std::abs( (int) m_gal->GetScreenWorldMatrix().GetScale().y * 7 );
  450. // 66% zoom-relative
  451. return KiROUND( ( docTextSize + screenTextSize * 2 ) / 3 );
  452. }
  453. static bool isFieldsLayer( int aLayer )
  454. {
  455. return aLayer == LAYER_REFERENCEPART
  456. || aLayer == LAYER_VALUEPART
  457. || aLayer == LAYER_INTERSHEET_REFS
  458. || aLayer == LAYER_NETCLASS_REFS
  459. || aLayer == LAYER_FIELDS
  460. || aLayer == LAYER_SHEETNAME
  461. || aLayer == LAYER_SHEETFILENAME
  462. || aLayer == LAYER_SHEETFIELDS;
  463. }
  464. void SCH_PAINTER::strokeText( const wxString& aText, const VECTOR2D& aPosition,
  465. const TEXT_ATTRIBUTES& aAttrs,
  466. const KIFONT::METRICS& aFontMetrics )
  467. {
  468. KIFONT::FONT* font = aAttrs.m_Font;
  469. if( !font )
  470. {
  471. font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font, aAttrs.m_Bold,
  472. aAttrs.m_Italic );
  473. }
  474. m_gal->SetIsFill( font->IsOutline() );
  475. m_gal->SetIsStroke( font->IsStroke() );
  476. font->Draw( m_gal, aText, aPosition, aAttrs, aFontMetrics );
  477. }
  478. void SCH_PAINTER::bitmapText( const wxString& aText, const VECTOR2D& aPosition,
  479. const TEXT_ATTRIBUTES& aAttrs )
  480. {
  481. // Bitmap font has different metrics from the stroke font so we compensate a bit before
  482. // stroking
  483. m_gal->SetGlyphSize( VECTOR2I( aAttrs.m_Size.x, KiROUND( aAttrs.m_Size.y * 1.05 ) ) );
  484. m_gal->SetLineWidth( (float) aAttrs.m_StrokeWidth * 1.35f );
  485. m_gal->SetHorizontalJustify( aAttrs.m_Halign );
  486. m_gal->SetVerticalJustify( aAttrs.m_Valign );
  487. m_gal->BitmapText( aText, aPosition, aAttrs.m_Angle );
  488. }
  489. void SCH_PAINTER::knockoutText( const wxString& aText, const VECTOR2D& aPosition,
  490. const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
  491. {
  492. TEXT_ATTRIBUTES attrs( aAttrs );
  493. KIFONT::FONT* font = aAttrs.m_Font;
  494. if( !font )
  495. {
  496. font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font, attrs.m_Bold,
  497. attrs.m_Italic );
  498. }
  499. KIGFX::GAL_DISPLAY_OPTIONS empty_opts;
  500. SHAPE_POLY_SET knockouts;
  501. CALLBACK_GAL callback_gal( empty_opts,
  502. // Polygon callback
  503. [&]( const SHAPE_LINE_CHAIN& aPoly )
  504. {
  505. knockouts.AddOutline( aPoly );
  506. } );
  507. callback_gal.SetIsFill( false );
  508. callback_gal.SetIsStroke( true );
  509. callback_gal.SetLineWidth( (float) attrs.m_StrokeWidth );
  510. font->Draw( &callback_gal, aText, aPosition, attrs, aFontMetrics );
  511. BOX2I bbox = knockouts.BBox( attrs.m_StrokeWidth * 2 );
  512. SHAPE_POLY_SET finalPoly;
  513. finalPoly.NewOutline();
  514. finalPoly.Append( bbox.GetLeft(), bbox.GetTop() );
  515. finalPoly.Append( bbox.GetRight(), bbox.GetTop() );
  516. finalPoly.Append( bbox.GetRight(), bbox.GetBottom() );
  517. finalPoly.Append( bbox.GetLeft(), bbox.GetBottom() );
  518. finalPoly.BooleanSubtract( knockouts, SHAPE_POLY_SET::PM_FAST );
  519. finalPoly.Fracture( SHAPE_POLY_SET::PM_FAST );
  520. m_gal->SetIsStroke( false );
  521. m_gal->SetIsFill( true );
  522. m_gal->SetFillColor( attrs.m_Color );
  523. m_gal->DrawPolygon( finalPoly );
  524. }
  525. void SCH_PAINTER::boxText( const wxString& aText, const VECTOR2D& aPosition,
  526. const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
  527. {
  528. KIFONT::FONT* font = aAttrs.m_Font;
  529. if( !font )
  530. {
  531. font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font, aAttrs.m_Bold,
  532. aAttrs.m_Italic );
  533. }
  534. VECTOR2I extents = font->StringBoundaryLimits( aText, aAttrs.m_Size, aAttrs.m_StrokeWidth,
  535. aAttrs.m_Bold, aAttrs.m_Italic, aFontMetrics );
  536. BOX2I box( aPosition, VECTOR2I( extents.x, aAttrs.m_Size.y ) );
  537. switch( aAttrs.m_Halign )
  538. {
  539. case GR_TEXT_H_ALIGN_LEFT: break;
  540. case GR_TEXT_H_ALIGN_CENTER: box.SetX( box.GetX() - box.GetWidth() / 2 ); break;
  541. case GR_TEXT_H_ALIGN_RIGHT: box.SetX( box.GetX() - box.GetWidth() ); break;
  542. case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Legal only in dialogs" ) ); break;
  543. }
  544. switch( aAttrs.m_Valign )
  545. {
  546. case GR_TEXT_V_ALIGN_TOP: break;
  547. case GR_TEXT_V_ALIGN_CENTER: box.SetY( box.GetY() - box.GetHeight() / 2 ); break;
  548. case GR_TEXT_V_ALIGN_BOTTOM: box.SetY( box.GetY() - box.GetHeight() ); break;
  549. case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Legal only in dialogs" ) ); break;
  550. }
  551. // Give the highlight a bit of margin.
  552. box.Inflate( 0, aAttrs.m_StrokeWidth * 2 );
  553. box.Normalize(); // Make h and v sizes always >= 0
  554. box = box.GetBoundingBoxRotated( aPosition, aAttrs.m_Angle );
  555. m_gal->SetIsFill( true );
  556. m_gal->SetIsStroke( false );
  557. m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
  558. }
  559. void SCH_PAINTER::triLine( const VECTOR2D &a, const VECTOR2D &b, const VECTOR2D &c )
  560. {
  561. m_gal->DrawLine( a, b );
  562. m_gal->DrawLine( b, c );
  563. }
  564. void SCH_PAINTER::draw( const LIB_SYMBOL* aSymbol, int aLayer, bool aDrawFields, int aUnit,
  565. int aBodyStyle, bool aDimmed )
  566. {
  567. if( !aUnit )
  568. aUnit = m_schSettings.m_ShowUnit;
  569. if( !aBodyStyle )
  570. aBodyStyle = m_schSettings.m_ShowBodyStyle;
  571. std::unique_ptr< LIB_SYMBOL > tmpSymbol;
  572. const LIB_SYMBOL* drawnSymbol = aSymbol;
  573. if( aSymbol->IsAlias() )
  574. {
  575. tmpSymbol = aSymbol->Flatten();
  576. drawnSymbol = tmpSymbol.get();
  577. }
  578. // The parent must exist on the union of all its children's draw layers. But that doesn't
  579. // mean we want to draw each child on the union.
  580. auto childOnLayer =
  581. []( const SCH_ITEM& item, int layer )
  582. {
  583. int layers[512], layers_count;
  584. item.ViewGetLayers( layers, layers_count );
  585. for( int ii = 0; ii < layers_count; ++ii )
  586. {
  587. if( layers[ii] == layer )
  588. return true;
  589. }
  590. return false;
  591. };
  592. for( const SCH_ITEM& item : drawnSymbol->GetDrawItems() )
  593. {
  594. if( !aDrawFields && item.Type() == SCH_FIELD_T )
  595. continue;
  596. if( !childOnLayer( item, aLayer ) )
  597. continue;
  598. if( aUnit && item.GetUnit() && aUnit != item.GetUnit() )
  599. continue;
  600. if( aBodyStyle && item.GetBodyStyle() && aBodyStyle != item.GetBodyStyle() )
  601. continue;
  602. draw( &item, aLayer, aDimmed );
  603. }
  604. }
  605. int SCH_PAINTER::internalPinDecoSize( const SCH_PIN &aPin )
  606. {
  607. if( m_schSettings.m_PinSymbolSize > 0 )
  608. return m_schSettings.m_PinSymbolSize;
  609. return aPin.GetNameTextSize() != 0 ? aPin.GetNameTextSize() / 2 : aPin.GetNumberTextSize() / 2;
  610. }
  611. // Utility for getting the size of the 'external' pin decorators (as a radius)
  612. // i.e. the negation circle, the polarity 'slopes' and the nonlogic marker
  613. int SCH_PAINTER::externalPinDecoSize( const SCH_PIN &aPin )
  614. {
  615. if( m_schSettings.m_PinSymbolSize > 0 )
  616. return m_schSettings.m_PinSymbolSize;
  617. return aPin.GetNumberTextSize() / 2;
  618. }
  619. // Draw the target (an open circle) for a pin which has no connection or is being moved.
  620. void SCH_PAINTER::drawPinDanglingIndicator( const VECTOR2I& aPos, const COLOR4D& aColor,
  621. bool aDrawingShadows, bool aBrightened )
  622. {
  623. // Dangling symbols must be drawn in a slightly different colour so they can be seen when
  624. // they overlap with a junction dot.
  625. m_gal->SetStrokeColor( aColor.Brightened( 0.3 ) );
  626. m_gal->SetIsFill( false );
  627. m_gal->SetIsStroke( true );
  628. m_gal->SetLineWidth( aDrawingShadows ? getShadowWidth( aBrightened )
  629. : m_schSettings.GetDanglingIndicatorThickness() );
  630. m_gal->DrawCircle( aPos, TARGET_PIN_RADIUS );
  631. }
  632. void SCH_PAINTER::draw( const SCH_PIN* aPin, int aLayer, bool aDimmed )
  633. {
  634. // Don't draw pins from a selection view-group. Pins in a schematic must always be drawn
  635. // from their parent symbol's m_part.
  636. if( dynamic_cast<const SCH_SYMBOL*>( aPin->GetParentSymbol() ) )
  637. return;
  638. if( !isUnitAndConversionShown( aPin ) )
  639. return;
  640. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  641. if( m_schSettings.IsPrinting() && drawingShadows )
  642. return;
  643. bool drawingDangling = aLayer == LAYER_DANGLING;
  644. bool drawingOP = aLayer == LAYER_OP_CURRENTS;
  645. bool isDangling = m_schSettings.m_IsSymbolEditor || aPin->HasFlag( IS_DANGLING );
  646. if( drawingShadows && !( aPin->IsBrightened() || aPin->IsSelected() ) )
  647. return;
  648. VECTOR2I pos = aPin->GetPosition();
  649. COLOR4D color = getRenderColor( aPin, LAYER_PIN, drawingShadows, aDimmed );
  650. if( !aPin->IsVisible() )
  651. {
  652. if( m_schSettings.IsPrinting() )
  653. return;
  654. bool force_show = m_schematic ? eeconfig()->m_Appearance.show_hidden_pins
  655. : m_schSettings.m_ShowHiddenPins;
  656. if( force_show )
  657. {
  658. color = getRenderColor( aPin, LAYER_HIDDEN, drawingShadows, aDimmed );
  659. }
  660. else
  661. {
  662. if( drawingDangling && isDangling && aPin->IsGlobalPower() )
  663. drawPinDanglingIndicator( pos, color, drawingShadows, aPin->IsBrightened() );
  664. return;
  665. }
  666. }
  667. if( drawingDangling )
  668. {
  669. if( isDangling )
  670. drawPinDanglingIndicator( pos, color, drawingShadows, aPin->IsBrightened() );
  671. return;
  672. }
  673. if( m_schSettings.GetDrawBoundingBoxes() )
  674. drawItemBoundingBox( aPin );
  675. VECTOR2I p0 = aPin->GetPinRoot();
  676. VECTOR2I dir( sign( pos.x - p0.x ), sign( pos.y - p0.y ) );
  677. int len = aPin->GetLength();
  678. if( drawingOP && !aPin->GetOperatingPoint().IsEmpty() )
  679. {
  680. int textSize = getOperatingPointTextSize();
  681. VECTOR2I mid = ( p0 + pos ) / 2;
  682. int textOffset = KiROUND( textSize * 0.22 );
  683. TEXT_ATTRIBUTES attrs;
  684. if( len > textSize )
  685. {
  686. if( dir.x == 0 )
  687. {
  688. mid.x += KiROUND( textOffset * 1.2 );
  689. attrs.m_Angle = ANGLE_HORIZONTAL;
  690. }
  691. else
  692. {
  693. mid.y -= KiROUND( textOffset * 1.2 );
  694. attrs.m_Angle = ANGLE_VERTICAL;
  695. }
  696. attrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  697. attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER;
  698. attrs.m_Font = KIFONT::FONT::GetFont(); // always use stroke font for performance
  699. attrs.m_Size = VECTOR2I( textSize, textSize );
  700. attrs.m_StrokeWidth = GetPenSizeForDemiBold( textSize );
  701. attrs.m_Color = m_schSettings.GetLayerColor( LAYER_OP_CURRENTS );
  702. knockoutText( aPin->GetOperatingPoint(), mid, attrs, aPin->GetFontMetrics() );
  703. }
  704. }
  705. if( drawingOP )
  706. return;
  707. VECTOR2D pc;
  708. m_gal->SetIsStroke( true );
  709. m_gal->SetIsFill( false );
  710. m_gal->SetLineWidth( getLineWidth( aPin, drawingShadows ) );
  711. m_gal->SetStrokeColor( color );
  712. m_gal->SetFontBold( false );
  713. m_gal->SetFontUnderlined( false );
  714. m_gal->SetFontItalic( false );
  715. const int radius = externalPinDecoSize( *aPin );
  716. const int diam = radius*2;
  717. const int clock_size = internalPinDecoSize( *aPin );
  718. if( aPin->GetType() == ELECTRICAL_PINTYPE::PT_NC ) // Draw a N.C. symbol
  719. {
  720. m_gal->DrawLine( p0, pos );
  721. m_gal->DrawLine( pos + VECTOR2D( -1, -1 ) * TARGET_PIN_RADIUS,
  722. pos + VECTOR2D( 1, 1 ) * TARGET_PIN_RADIUS );
  723. m_gal->DrawLine( pos + VECTOR2D( 1, -1 ) * TARGET_PIN_RADIUS ,
  724. pos + VECTOR2D( -1, 1 ) * TARGET_PIN_RADIUS );
  725. }
  726. else
  727. {
  728. switch( aPin->GetShape() )
  729. {
  730. default:
  731. case GRAPHIC_PINSHAPE::LINE:
  732. m_gal->DrawLine( p0, pos );
  733. break;
  734. case GRAPHIC_PINSHAPE::INVERTED:
  735. m_gal->DrawCircle( p0 + dir * radius, radius );
  736. m_gal->DrawLine( p0 + dir * ( diam ), pos );
  737. break;
  738. case GRAPHIC_PINSHAPE::INVERTED_CLOCK:
  739. pc = p0 - dir * clock_size ;
  740. triLine( p0 + VECTOR2D( dir.y, -dir.x) * clock_size,
  741. pc,
  742. p0 + VECTOR2D( -dir.y, dir.x) * clock_size );
  743. m_gal->DrawCircle( p0 + dir * radius, radius );
  744. m_gal->DrawLine( p0 + dir * ( diam ), pos );
  745. break;
  746. case GRAPHIC_PINSHAPE::CLOCK_LOW:
  747. case GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK:
  748. pc = p0 - dir * clock_size ;
  749. triLine( p0 + VECTOR2D( dir.y, -dir.x) * clock_size,
  750. pc,
  751. p0 + VECTOR2D( -dir.y, dir.x) * clock_size );
  752. if( !dir.y )
  753. {
  754. triLine( p0 + VECTOR2D(dir.x, 0) * diam,
  755. p0 + VECTOR2D(dir.x, -1) * diam,
  756. p0 );
  757. }
  758. else /* MapX1 = 0 */
  759. {
  760. triLine( p0 + VECTOR2D( 0, dir.y) * diam,
  761. p0 + VECTOR2D(-1, dir.y) * diam,
  762. p0 );
  763. }
  764. m_gal->DrawLine( p0, pos );
  765. break;
  766. case GRAPHIC_PINSHAPE::CLOCK:
  767. m_gal->DrawLine( p0, pos );
  768. if( !dir.y )
  769. {
  770. triLine( p0 + VECTOR2D( 0, clock_size ),
  771. p0 + VECTOR2D( -dir.x * clock_size, 0 ),
  772. p0 + VECTOR2D( 0, -clock_size ) );
  773. }
  774. else
  775. {
  776. triLine( p0 + VECTOR2D( clock_size, 0 ),
  777. p0 + VECTOR2D( 0, -dir.y * clock_size ),
  778. p0 + VECTOR2D( -clock_size, 0 ) );
  779. }
  780. break;
  781. case GRAPHIC_PINSHAPE::INPUT_LOW:
  782. m_gal->DrawLine( p0, pos );
  783. if( !dir.y )
  784. {
  785. triLine( p0 + VECTOR2D(dir.x, 0) * diam,
  786. p0 + VECTOR2D(dir.x, -1) * diam,
  787. p0 );
  788. }
  789. else /* MapX1 = 0 */
  790. {
  791. triLine( p0 + VECTOR2D( 0, dir.y) * diam,
  792. p0 + VECTOR2D(-1, dir.y) * diam,
  793. p0 );
  794. }
  795. break;
  796. case GRAPHIC_PINSHAPE::OUTPUT_LOW: // IEEE symbol "Active Low Output"
  797. m_gal->DrawLine( p0, pos );
  798. if( !dir.y ) // Horizontal pin
  799. m_gal->DrawLine( p0 - VECTOR2D( 0, diam ), p0 + VECTOR2D( dir.x, 0 ) * diam );
  800. else // Vertical pin
  801. m_gal->DrawLine( p0 - VECTOR2D( diam, 0 ), p0 + VECTOR2D( 0, dir.y ) * diam );
  802. break;
  803. case GRAPHIC_PINSHAPE::NONLOGIC: // NonLogic pin symbol
  804. m_gal->DrawLine( p0, pos );
  805. m_gal->DrawLine( p0 - VECTOR2D( dir.x + dir.y, dir.y - dir.x ) * radius,
  806. p0 + VECTOR2D( dir.x + dir.y, dir.y - dir.x ) * radius );
  807. m_gal->DrawLine( p0 - VECTOR2D( dir.x - dir.y, dir.x + dir.y ) * radius,
  808. p0 + VECTOR2D( dir.x - dir.y, dir.x + dir.y ) * radius );
  809. break;
  810. }
  811. }
  812. if( drawingShadows && !eeconfig()->m_Selection.draw_selected_children )
  813. return;
  814. // Draw the labels
  815. const SYMBOL* symbol = aPin->GetParentSymbol();
  816. float penWidth = (float) m_schSettings.GetDefaultPenWidth();
  817. int textOffset = symbol->GetPinNameOffset();
  818. float nameStrokeWidth = getLineWidth( aPin, false );
  819. float numStrokeWidth = getLineWidth( aPin, false );
  820. bool showPinNames = symbol->GetShowPinNames();
  821. bool showPinNumbers = m_schSettings.m_ShowPinNumbers || symbol->GetShowPinNumbers();
  822. nameStrokeWidth = Clamp_Text_PenSize( nameStrokeWidth, aPin->GetNameTextSize(), true );
  823. numStrokeWidth = Clamp_Text_PenSize( numStrokeWidth, aPin->GetNumberTextSize(), true );
  824. float PIN_TEXT_MARGIN = schIUScale.MilsToIU( KiROUND( 24 * m_schSettings.m_TextOffsetRatio ) );
  825. // Four locations around a pin where text can be drawn
  826. enum { INSIDE = 0, OUTSIDE, ABOVE, BELOW };
  827. int size[4] = { 0, 0, 0, 0 };
  828. float thickness[4] = { numStrokeWidth, numStrokeWidth, numStrokeWidth, numStrokeWidth };
  829. COLOR4D colour[4];
  830. wxString text[4];
  831. // TextOffset > 0 means pin NAMES on inside, pin NUMBERS above and nothing below
  832. if( textOffset )
  833. {
  834. size [INSIDE] = showPinNames ? aPin->GetNameTextSize() : 0;
  835. thickness[INSIDE] = nameStrokeWidth;
  836. colour [INSIDE] = getRenderColor( aPin, LAYER_PINNAM, drawingShadows, aDimmed );
  837. text [INSIDE] = aPin->GetShownName();
  838. size [ABOVE] = showPinNumbers ? aPin->GetNumberTextSize() : 0;
  839. thickness[ABOVE] = numStrokeWidth;
  840. colour [ABOVE] = getRenderColor( aPin, LAYER_PINNUM, drawingShadows, aDimmed );
  841. text [ABOVE] = aPin->GetShownNumber();
  842. }
  843. // Otherwise if both are shown pin NAMES go above and pin NUMBERS go below
  844. else if( showPinNames && showPinNumbers )
  845. {
  846. size [ABOVE] = aPin->GetNameTextSize();
  847. thickness[ABOVE] = nameStrokeWidth;
  848. colour [ABOVE] = getRenderColor( aPin, LAYER_PINNAM, drawingShadows, aDimmed );
  849. text [ABOVE] = aPin->GetShownName();
  850. size [BELOW] = aPin->GetNumberTextSize();
  851. thickness[BELOW] = numStrokeWidth;
  852. colour [BELOW] = getRenderColor( aPin, LAYER_PINNUM, drawingShadows, aDimmed );
  853. text [BELOW] = aPin->GetShownNumber();
  854. }
  855. else if( showPinNames )
  856. {
  857. size [ABOVE] = aPin->GetNameTextSize();
  858. thickness[ABOVE] = nameStrokeWidth;
  859. colour [ABOVE] = getRenderColor( aPin, LAYER_PINNAM, drawingShadows, aDimmed );
  860. text [ABOVE] = aPin->GetShownName();
  861. }
  862. else if( showPinNumbers )
  863. {
  864. size [ABOVE] = aPin->GetNumberTextSize();
  865. thickness[ABOVE] = numStrokeWidth;
  866. colour [ABOVE] = getRenderColor( aPin, LAYER_PINNUM, drawingShadows, aDimmed );
  867. text [ABOVE] = aPin->GetShownNumber();
  868. }
  869. if( m_schSettings.m_ShowPinsElectricalType )
  870. {
  871. size [OUTSIDE] = std::max( aPin->GetNameTextSize() * 3 / 4, schIUScale.mmToIU( 0.7 ) );
  872. thickness[OUTSIDE] = float( size[OUTSIDE] ) / 8.0f;
  873. colour [OUTSIDE] = getRenderColor( aPin, LAYER_PRIVATE_NOTES, drawingShadows, aDimmed );
  874. text [OUTSIDE] = aPin->GetElectricalTypeName();
  875. }
  876. // Rendering text is expensive (particularly when using outline fonts). At small effective
  877. // sizes (ie: zoomed out) the visual differences between outline and/or stroke fonts and the
  878. // bitmap font becomes immaterial, and there's often more to draw when zoomed out so the
  879. // performance gain becomes more significant.
  880. #define BITMAP_FONT_SIZE_THRESHOLD 3.5
  881. bool renderTextAsBitmap = size[0] * m_gal->GetWorldScale() < BITMAP_FONT_SIZE_THRESHOLD
  882. && size[1] * m_gal->GetWorldScale() < BITMAP_FONT_SIZE_THRESHOLD
  883. && size[2] * m_gal->GetWorldScale() < BITMAP_FONT_SIZE_THRESHOLD
  884. && size[3] * m_gal->GetWorldScale() < BITMAP_FONT_SIZE_THRESHOLD;
  885. if( !aPin->IsVisible() )
  886. {
  887. for( COLOR4D& c : colour )
  888. c = getRenderColor( aPin, LAYER_HIDDEN, drawingShadows, aDimmed );
  889. }
  890. float insideOffset = (float) textOffset - thickness[INSIDE] / 2.0f;
  891. float outsideOffset = PIN_TEXT_MARGIN + TARGET_PIN_RADIUS - thickness[OUTSIDE] / 2.0f;
  892. float aboveOffset = PIN_TEXT_MARGIN + penWidth / 2.0f + thickness[ABOVE] / 2.0f;
  893. float belowOffset = PIN_TEXT_MARGIN + penWidth / 2.0f + thickness[BELOW] / 2.0f;
  894. if( isDangling )
  895. outsideOffset += TARGET_PIN_RADIUS / 2.0f;
  896. if( drawingShadows )
  897. {
  898. float shadowWidth = getShadowWidth( aPin->IsBrightened() );
  899. for( float& t : thickness )
  900. t += shadowWidth;
  901. // Due to the fact a shadow text in position INSIDE or OUTSIDE is drawn left or right aligned,
  902. // it needs an offset = shadowWidth/2 to be drawn at the same place as normal text
  903. // texts drawn as GR_TEXT_H_ALIGN_CENTER do not need a specific offset.
  904. // this offset is shadowWidth/2 but for some reason we need to slightly modify this offset
  905. // for a better look (better alignment of shadow shape), for KiCad font only
  906. if( !KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font )->IsOutline() )
  907. {
  908. const float adjust = 1.2f; // Value chosen after tests
  909. float shadowOffset = shadowWidth/2.0f * adjust;
  910. insideOffset -= shadowOffset;
  911. outsideOffset -= shadowOffset;
  912. }
  913. }
  914. auto drawText =
  915. [&]( int i, const VECTOR2D& aPos, GR_TEXT_H_ALIGN_T hAlign, GR_TEXT_V_ALIGN_T vAlign,
  916. const EDA_ANGLE& aAngle )
  917. {
  918. if( text[i].IsEmpty() )
  919. return;
  920. // Which of these gets used depends on the font technology, so set both
  921. m_gal->SetStrokeColor( colour[i] );
  922. m_gal->SetFillColor( colour[i] );
  923. TEXT_ATTRIBUTES attrs;
  924. attrs.m_Font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font );
  925. attrs.m_Size = VECTOR2I( size[i], size[i] );
  926. attrs.m_Halign = hAlign;
  927. attrs.m_Valign = vAlign;
  928. attrs.m_Angle = aAngle;
  929. attrs.m_StrokeWidth = KiROUND( thickness[i] );
  930. if( drawingShadows && !attrs.m_Font->IsOutline() )
  931. {
  932. strokeText( text[i], aPos, attrs, aPin->GetFontMetrics() );
  933. }
  934. else if( drawingShadows )
  935. {
  936. boxText( text[i], aPos, attrs, aPin->GetFontMetrics() );
  937. }
  938. else if( nonCached( aPin ) && renderTextAsBitmap )
  939. {
  940. bitmapText( text[i], aPos, attrs );
  941. const_cast<SCH_PIN*>( aPin )->SetFlags( IS_SHOWN_AS_BITMAP );
  942. }
  943. else
  944. {
  945. strokeText( text[i], aPos, attrs, aPin->GetFontMetrics() );
  946. const_cast<SCH_PIN*>( aPin )->SetFlags( IS_SHOWN_AS_BITMAP );
  947. }
  948. };
  949. switch( aPin->GetOrientation() )
  950. {
  951. case PIN_ORIENTATION::PIN_LEFT:
  952. if( size[INSIDE] )
  953. {
  954. drawText( INSIDE, pos + VECTOR2D( -insideOffset - (float) len, 0 ),
  955. GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, ANGLE_HORIZONTAL );
  956. }
  957. if( size[OUTSIDE] )
  958. {
  959. drawText( OUTSIDE, pos + VECTOR2D( outsideOffset, 0 ),
  960. GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, ANGLE_HORIZONTAL );
  961. }
  962. if( size[ABOVE] )
  963. {
  964. drawText( ABOVE, pos + VECTOR2D( -len / 2.0, -aboveOffset ),
  965. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, ANGLE_HORIZONTAL );
  966. }
  967. if( size[BELOW] )
  968. {
  969. drawText( BELOW, pos + VECTOR2D( -len / 2.0, belowOffset ),
  970. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, ANGLE_HORIZONTAL );
  971. }
  972. break;
  973. case PIN_ORIENTATION::PIN_RIGHT:
  974. if( size[INSIDE] )
  975. {
  976. drawText( INSIDE, pos + VECTOR2D( insideOffset + (float) len, 0 ),
  977. GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, ANGLE_HORIZONTAL );
  978. }
  979. if( size[OUTSIDE] )
  980. {
  981. drawText( OUTSIDE, pos + VECTOR2D( -outsideOffset, 0 ),
  982. GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, ANGLE_HORIZONTAL );
  983. }
  984. if( size[ABOVE] )
  985. {
  986. drawText( ABOVE, pos + VECTOR2D( len / 2.0, -aboveOffset ),
  987. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, ANGLE_HORIZONTAL );
  988. }
  989. if( size[BELOW] )
  990. {
  991. drawText( BELOW, pos + VECTOR2D( len / 2.0, belowOffset ),
  992. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, ANGLE_HORIZONTAL );
  993. }
  994. break;
  995. case PIN_ORIENTATION::PIN_DOWN:
  996. if( size[INSIDE] )
  997. {
  998. drawText( INSIDE, pos + VECTOR2D( 0, insideOffset + (float) len ),
  999. GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, ANGLE_VERTICAL );
  1000. }
  1001. if( size[OUTSIDE] )
  1002. {
  1003. drawText( OUTSIDE, pos + VECTOR2D( 0, -outsideOffset ),
  1004. GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, ANGLE_VERTICAL );
  1005. }
  1006. if( size[ABOVE] )
  1007. {
  1008. drawText( ABOVE, pos + VECTOR2D( -aboveOffset, len / 2.0 ),
  1009. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, ANGLE_VERTICAL );
  1010. }
  1011. if( size[BELOW] )
  1012. {
  1013. drawText( BELOW, pos + VECTOR2D( belowOffset, len / 2.0 ),
  1014. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, ANGLE_VERTICAL );
  1015. }
  1016. break;
  1017. case PIN_ORIENTATION::PIN_UP:
  1018. if( size[INSIDE] )
  1019. {
  1020. drawText( INSIDE, pos + VECTOR2D( 0, -insideOffset - (float) len ),
  1021. GR_TEXT_H_ALIGN_LEFT, GR_TEXT_V_ALIGN_CENTER, ANGLE_VERTICAL );
  1022. }
  1023. if( size[OUTSIDE] )
  1024. {
  1025. drawText( OUTSIDE, pos + VECTOR2D( 0, outsideOffset ),
  1026. GR_TEXT_H_ALIGN_RIGHT, GR_TEXT_V_ALIGN_CENTER, ANGLE_VERTICAL );
  1027. }
  1028. if( size[ABOVE] )
  1029. {
  1030. drawText( ABOVE, pos + VECTOR2D( -aboveOffset, -len / 2.0 ),
  1031. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_BOTTOM, ANGLE_VERTICAL );
  1032. }
  1033. if( size[BELOW] )
  1034. {
  1035. drawText( BELOW, pos + VECTOR2D( belowOffset, -len / 2.0 ),
  1036. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_TOP, ANGLE_VERTICAL );
  1037. }
  1038. break;
  1039. default:
  1040. wxFAIL_MSG( "Unknown pin orientation" );
  1041. }
  1042. }
  1043. // Draw anchor indicating the anchor position of text objects, local labels, or fields.
  1044. void SCH_PAINTER::drawAnchor( const VECTOR2I& aPos, bool aDrawingShadows )
  1045. {
  1046. if( m_schSettings.IsPrinting() )
  1047. return;
  1048. // In order for the anchors to be visible but unobtrusive, their size must factor in the
  1049. // current zoom level.
  1050. const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
  1051. int radius = KiROUND( std::fabs( matrix.GetScale().x * TEXT_ANCHOR_SIZE ) / 25 )
  1052. + schIUScale.MilsToIU( TEXT_ANCHOR_SIZE );
  1053. COLOR4D color = aDrawingShadows ? m_schSettings.GetLayerColor( LAYER_SELECTION_SHADOWS )
  1054. : m_schSettings.GetLayerColor( LAYER_SCHEMATIC_ANCHOR );
  1055. m_gal->SetStrokeColor( color );
  1056. m_gal->SetIsStroke( true );
  1057. m_gal->SetLineWidth( aDrawingShadows ? getShadowWidth( false )
  1058. : m_schSettings.GetDanglingIndicatorThickness() );
  1059. m_gal->DrawLine( aPos - VECTOR2I( radius, 0 ), aPos + VECTOR2I( radius, 0 ) );
  1060. m_gal->DrawLine( aPos - VECTOR2I( 0, radius ), aPos + VECTOR2I( 0, radius ) );
  1061. }
  1062. // Draw the target (an open square) for a wire or label which has no connection or is
  1063. // being moved.
  1064. void SCH_PAINTER::drawDanglingIndicator( const VECTOR2I& aPos, const COLOR4D& aColor, int aWidth,
  1065. bool aDangling, bool aDrawingShadows, bool aBrightened )
  1066. {
  1067. if( m_schSettings.IsPrinting() )
  1068. return;
  1069. int size = aDangling ? DANGLING_SYMBOL_SIZE : UNSELECTED_END_SIZE;
  1070. if( !aDangling )
  1071. aWidth /= 2;
  1072. VECTOR2I radius( aWidth + schIUScale.MilsToIU( size / 2 ),
  1073. aWidth + schIUScale.MilsToIU( size / 2 ) );
  1074. // Dangling symbols must be drawn in a slightly different colour so they can be seen when
  1075. // they overlap with a junction dot.
  1076. m_gal->SetStrokeColor( aColor.Brightened( 0.3 ) );
  1077. m_gal->SetIsStroke( true );
  1078. m_gal->SetIsFill( false );
  1079. m_gal->SetLineWidth( aDrawingShadows ? getShadowWidth( aBrightened )
  1080. : m_schSettings.GetDanglingIndicatorThickness() );
  1081. m_gal->DrawRectangle( aPos - radius, aPos + radius );
  1082. }
  1083. void SCH_PAINTER::draw( const SCH_JUNCTION* aJct, int aLayer )
  1084. {
  1085. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  1086. if( m_schSettings.IsPrinting() && drawingShadows )
  1087. return;
  1088. if( drawingShadows && !( aJct->IsBrightened() || aJct->IsSelected() ) )
  1089. return;
  1090. COLOR4D color = getRenderColor( aJct, aJct->GetLayer(), drawingShadows );
  1091. int junctionSize = aJct->GetEffectiveDiameter() / 2;
  1092. if( junctionSize > 1 )
  1093. {
  1094. m_gal->SetIsStroke( drawingShadows );
  1095. m_gal->SetLineWidth( getLineWidth( aJct, drawingShadows ) );
  1096. m_gal->SetStrokeColor( color );
  1097. m_gal->SetIsFill( !drawingShadows );
  1098. m_gal->SetFillColor( color );
  1099. m_gal->DrawCircle( aJct->GetPosition(), junctionSize );
  1100. }
  1101. }
  1102. void SCH_PAINTER::draw( const SCH_LINE* aLine, int aLayer )
  1103. {
  1104. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  1105. if( m_schSettings.IsPrinting() && drawingShadows )
  1106. return;
  1107. bool drawingDangling = aLayer == LAYER_DANGLING;
  1108. bool drawingOP = aLayer == LAYER_OP_VOLTAGES;
  1109. if( drawingShadows && !( aLine->IsBrightened() || aLine->IsSelected() ) )
  1110. return;
  1111. // Line end dangling status isn't updated until the line is finished drawing, so don't warn
  1112. // them about ends that are probably connected
  1113. if( aLine->IsNew() && drawingDangling )
  1114. return;
  1115. COLOR4D color = getRenderColor( aLine, aLine->GetLayer(), drawingShadows );
  1116. float width = getLineWidth( aLine, drawingShadows );
  1117. LINE_STYLE lineStyle = aLine->GetEffectiveLineStyle();
  1118. if( ( drawingDangling || drawingShadows ) && !aLine->IsNew() )
  1119. {
  1120. if( ( aLine->IsWire() && aLine->IsStartDangling() )
  1121. || ( drawingShadows && aLine->IsSelected() && !aLine->HasFlag( STARTPOINT ) ) )
  1122. {
  1123. COLOR4D indicatorColor( color );
  1124. if( drawingShadows && !aLine->HasFlag( STARTPOINT ) )
  1125. indicatorColor.Invert();
  1126. drawDanglingIndicator( aLine->GetStartPoint(), indicatorColor, KiROUND( width ),
  1127. aLine->IsWire() && aLine->IsStartDangling(), drawingShadows,
  1128. aLine->IsBrightened() );
  1129. }
  1130. if( ( aLine->IsWire() && aLine->IsEndDangling() )
  1131. || ( drawingShadows && aLine->IsSelected() && !aLine->HasFlag( ENDPOINT ) ) )
  1132. {
  1133. COLOR4D indicatorColor( color );
  1134. if( drawingShadows && !aLine->HasFlag( ENDPOINT ) )
  1135. indicatorColor.Invert();
  1136. drawDanglingIndicator( aLine->GetEndPoint(), indicatorColor, KiROUND( width ),
  1137. aLine->IsWire() && aLine->IsEndDangling(), drawingShadows,
  1138. aLine->IsBrightened() );
  1139. }
  1140. }
  1141. if( drawingDangling )
  1142. return;
  1143. if( drawingOP && !aLine->GetOperatingPoint().IsEmpty() )
  1144. {
  1145. int textSize = getOperatingPointTextSize();
  1146. VECTOR2I pos = aLine->GetMidPoint();
  1147. int textOffset = KiROUND( textSize * 0.22 );
  1148. TEXT_ATTRIBUTES attrs;
  1149. if( aLine->GetStartPoint().y == aLine->GetEndPoint().y )
  1150. {
  1151. pos.y -= textOffset;
  1152. attrs.m_Halign = GR_TEXT_H_ALIGN_CENTER;
  1153. attrs.m_Valign = GR_TEXT_V_ALIGN_BOTTOM;
  1154. }
  1155. else
  1156. {
  1157. pos.x += KiROUND( textOffset * 1.2 );
  1158. attrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
  1159. attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER;
  1160. }
  1161. attrs.m_Font = KIFONT::FONT::GetFont(); // always use stroke font for performance
  1162. attrs.m_Size = VECTOR2I( textSize, textSize );
  1163. attrs.m_StrokeWidth = GetPenSizeForDemiBold( textSize );
  1164. attrs.m_Color = m_schSettings.GetLayerColor( LAYER_OP_VOLTAGES );
  1165. knockoutText( aLine->GetOperatingPoint(), pos, attrs, aLine->GetFontMetrics() );
  1166. }
  1167. if( drawingOP )
  1168. return;
  1169. m_gal->SetIsStroke( true );
  1170. m_gal->SetStrokeColor( color );
  1171. m_gal->SetLineWidth( width );
  1172. if( lineStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows )
  1173. {
  1174. m_gal->DrawLine( aLine->GetStartPoint(), aLine->GetEndPoint() );
  1175. }
  1176. else
  1177. {
  1178. SHAPE_SEGMENT line( aLine->GetStartPoint(), aLine->GetEndPoint() );
  1179. STROKE_PARAMS::Stroke( &line, lineStyle, KiROUND( width ), &m_schSettings,
  1180. [&]( const VECTOR2I& a, const VECTOR2I& b )
  1181. {
  1182. // DrawLine has problem with 0 length lines so enforce minimum
  1183. if( a == b )
  1184. m_gal->DrawLine( a+1, b );
  1185. else
  1186. m_gal->DrawLine( a, b );
  1187. } );
  1188. }
  1189. }
  1190. void SCH_PAINTER::draw( const SCH_SHAPE* aShape, int aLayer, bool aDimmed )
  1191. {
  1192. if( !isUnitAndConversionShown( aShape ) )
  1193. return;
  1194. if( aShape->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
  1195. return;
  1196. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  1197. if( m_schSettings.IsPrinting() && drawingShadows )
  1198. return;
  1199. LINE_STYLE lineStyle = aShape->GetEffectiveLineStyle();
  1200. COLOR4D color = getRenderColor( aShape, aLayer, drawingShadows );
  1201. if( drawingShadows && !( aShape->IsBrightened() || aShape->IsSelected() ) )
  1202. return;
  1203. auto drawShape =
  1204. [&]( const SCH_SHAPE* shape )
  1205. {
  1206. switch( shape->GetShape() )
  1207. {
  1208. case SHAPE_T::ARC:
  1209. {
  1210. VECTOR2D start = shape->GetStart();
  1211. VECTOR2D mid = shape->GetArcMid();
  1212. VECTOR2D end = shape->GetEnd();
  1213. VECTOR2D center = CalcArcCenter( start, mid, end );
  1214. EDA_ANGLE startAngle( start - center );
  1215. EDA_ANGLE midAngle( mid - center );
  1216. EDA_ANGLE endAngle( end - center );
  1217. EDA_ANGLE angle1 = midAngle - startAngle;
  1218. EDA_ANGLE angle2 = endAngle - midAngle;
  1219. EDA_ANGLE angle = angle1.Normalize180() + angle2.Normalize180();
  1220. m_gal->DrawArc( center, ( start - center ).EuclideanNorm(), startAngle, angle );
  1221. break;
  1222. }
  1223. case SHAPE_T::CIRCLE:
  1224. m_gal->DrawCircle( shape->GetPosition(), shape->GetRadius() );
  1225. break;
  1226. case SHAPE_T::RECTANGLE:
  1227. m_gal->DrawRectangle( shape->GetPosition(), shape->GetEnd() );
  1228. break;
  1229. case SHAPE_T::POLY:
  1230. {
  1231. const std::vector<SHAPE*> polySegments = shape->MakeEffectiveShapes( true );
  1232. if( !polySegments.empty() )
  1233. {
  1234. std::deque<VECTOR2D> pts;
  1235. for( SHAPE* polySegment : polySegments )
  1236. pts.push_back( static_cast<SHAPE_SEGMENT*>( polySegment )->GetSeg().A );
  1237. pts.push_back(
  1238. static_cast<SHAPE_SEGMENT*>( polySegments.back() )->GetSeg().B );
  1239. for( SHAPE* polySegment : polySegments )
  1240. delete polySegment;
  1241. m_gal->DrawPolygon( pts );
  1242. }
  1243. break;
  1244. }
  1245. case SHAPE_T::BEZIER:
  1246. {
  1247. m_gal->DrawCurve( shape->GetStart(), shape->GetBezierC1(),
  1248. shape->GetBezierC2(), shape->GetEnd() );
  1249. break;
  1250. }
  1251. default:
  1252. UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
  1253. }
  1254. };
  1255. if( aLayer == LAYER_SELECTION_SHADOWS )
  1256. {
  1257. if( eeconfig()->m_Selection.fill_shapes )
  1258. {
  1259. // Consider a NAND gate. We have no idea which side of the arc is "inside"
  1260. // so we can't reliably fill.
  1261. if( aShape->GetShape() == SHAPE_T::ARC )
  1262. m_gal->SetIsFill( aShape->IsFilled() );
  1263. else
  1264. m_gal->SetIsFill( true );
  1265. m_gal->SetIsStroke( false );
  1266. m_gal->SetFillColor( color );
  1267. }
  1268. else
  1269. {
  1270. m_gal->SetIsStroke( true );
  1271. m_gal->SetIsFill( false );
  1272. m_gal->SetLineWidth( getLineWidth( aShape, true ) );
  1273. m_gal->SetStrokeColor( color );
  1274. }
  1275. drawShape( aShape );
  1276. }
  1277. else if( aLayer == LAYER_DEVICE_BACKGROUND || aLayer == LAYER_NOTES_BACKGROUND )
  1278. {
  1279. switch( aShape->GetFillMode() )
  1280. {
  1281. case FILL_T::NO_FILL:
  1282. break;
  1283. case FILL_T::FILLED_SHAPE:
  1284. // Fill in the foreground layer
  1285. break;
  1286. case FILL_T::FILLED_WITH_COLOR:
  1287. case FILL_T::FILLED_WITH_BG_BODYCOLOR:
  1288. // Do not fill the shape in B&W print mode, to avoid to visible items inside the shape
  1289. if( !m_schSettings.PrintBlackAndWhiteReq() )
  1290. {
  1291. m_gal->SetIsFill( true );
  1292. m_gal->SetIsStroke( false );
  1293. m_gal->SetFillColor( color );
  1294. drawShape( aShape );
  1295. }
  1296. break;
  1297. }
  1298. }
  1299. else if( aLayer == LAYER_DEVICE || aLayer == LAYER_NOTES || aLayer == LAYER_PRIVATE_NOTES
  1300. || aLayer == LAYER_RULE_AREAS )
  1301. {
  1302. // Shapes filled with the device colour must be filled in the foreground
  1303. if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
  1304. {
  1305. m_gal->SetIsFill( true );
  1306. m_gal->SetIsStroke( false );
  1307. m_gal->SetFillColor( color );
  1308. drawShape( aShape );
  1309. }
  1310. float lineWidth = getLineWidth( aShape, drawingShadows );
  1311. if( lineWidth > 0 )
  1312. {
  1313. m_gal->SetIsFill( false );
  1314. m_gal->SetIsStroke( true );
  1315. m_gal->SetLineWidth( lineWidth );
  1316. m_gal->SetStrokeColor( color );
  1317. if( lineStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows )
  1318. {
  1319. drawShape( aShape );
  1320. }
  1321. else
  1322. {
  1323. std::vector<SHAPE*> shapes = aShape->MakeEffectiveShapes( true );
  1324. for( SHAPE* shape : shapes )
  1325. {
  1326. STROKE_PARAMS::Stroke( shape, lineStyle, KiROUND( lineWidth ), &m_schSettings,
  1327. [this]( const VECTOR2I& a, const VECTOR2I& b )
  1328. {
  1329. // DrawLine has problem with 0 length lines so enforce minimum
  1330. if( a == b )
  1331. m_gal->DrawLine( a+1, b );
  1332. else
  1333. m_gal->DrawLine( a, b );
  1334. } );
  1335. }
  1336. for( SHAPE* shape : shapes )
  1337. delete shape;
  1338. }
  1339. }
  1340. }
  1341. }
  1342. void SCH_PAINTER::draw( const SCH_TEXT* aText, int aLayer, bool aDimmed )
  1343. {
  1344. if( !isUnitAndConversionShown( aText ) )
  1345. return;
  1346. if( aText->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
  1347. return;
  1348. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  1349. if( m_schSettings.IsPrinting() && drawingShadows )
  1350. return;
  1351. if( drawingShadows && !( aText->IsBrightened() || aText->IsSelected() ) )
  1352. return;
  1353. switch( aText->Type() )
  1354. {
  1355. case SCH_SHEET_PIN_T: aLayer = LAYER_SHEETLABEL; break;
  1356. case SCH_HIER_LABEL_T: aLayer = LAYER_HIERLABEL; break;
  1357. case SCH_GLOBAL_LABEL_T: aLayer = LAYER_GLOBLABEL; break;
  1358. case SCH_DIRECTIVE_LABEL_T: aLayer = LAYER_NETCLASS_REFS; break;
  1359. case SCH_LABEL_T: aLayer = LAYER_LOCLABEL; break;
  1360. case SCH_TEXT_T: aLayer = aText->GetParentSymbol() ? LAYER_DEVICE
  1361. : LAYER_NOTES; break;
  1362. default: aLayer = LAYER_NOTES; break;
  1363. }
  1364. COLOR4D color = getRenderColor( aText, aLayer, drawingShadows, aDimmed );
  1365. if( m_schematic )
  1366. {
  1367. SCH_CONNECTION* conn = nullptr;
  1368. if( !aText->IsConnectivityDirty() )
  1369. conn = aText->Connection();
  1370. if( conn && conn->IsBus() )
  1371. color = getRenderColor( aText, LAYER_BUS, drawingShadows );
  1372. }
  1373. if( !( aText->IsVisible() || aText->IsForceVisible() ) )
  1374. {
  1375. if( m_schSettings.m_IsSymbolEditor || eeconfig()->m_Appearance.show_hidden_fields )
  1376. color = getRenderColor( aText, LAYER_HIDDEN, drawingShadows );
  1377. else
  1378. return;
  1379. }
  1380. m_gal->SetStrokeColor( color );
  1381. m_gal->SetFillColor( color );
  1382. wxString shownText( aText->GetShownText( true ) );
  1383. VECTOR2I text_offset = aText->GetSchematicTextOffset( &m_schSettings );
  1384. TEXT_ATTRIBUTES attrs = aText->GetAttributes();
  1385. KIFONT::FONT* font = getFont( aText );
  1386. attrs.m_Angle = aText->GetDrawRotation();
  1387. attrs.m_StrokeWidth = KiROUND( getTextThickness( aText ) );
  1388. if( drawingShadows && font->IsOutline() )
  1389. {
  1390. BOX2I bBox = aText->GetBoundingBox();
  1391. bBox.Inflate( KiROUND( getTextThickness( aText ) * 2 ) );
  1392. m_gal->SetIsStroke( false );
  1393. m_gal->SetIsFill( true );
  1394. m_gal->DrawRectangle( bBox.GetPosition(), bBox.GetEnd() );
  1395. }
  1396. else if( aText->GetLayer() == LAYER_DEVICE )
  1397. {
  1398. BOX2I bBox = aText->GetBoundingBox();
  1399. VECTOR2D pos = bBox.Centre();
  1400. // Due to the fact a shadow text can be drawn left or right aligned, it needs to be
  1401. // offset by shadowWidth/2 to be drawn at the same place as normal text.
  1402. // For some reason we need to slightly modify this offset for a better look (better
  1403. // alignment of shadow shape), for KiCad font only.
  1404. double shadowOffset = 0.0;
  1405. if( drawingShadows )
  1406. {
  1407. double shadowWidth = getShadowWidth( !aText->IsSelected() );
  1408. attrs.m_StrokeWidth += getShadowWidth( !aText->IsSelected() );
  1409. const double adjust = 1.2f; // Value chosen after tests
  1410. shadowOffset = shadowWidth/2.0f * adjust;
  1411. }
  1412. if( attrs.m_Angle == ANGLE_VERTICAL )
  1413. {
  1414. switch( attrs.m_Halign )
  1415. {
  1416. case GR_TEXT_H_ALIGN_LEFT:
  1417. pos.y = bBox.GetBottom() + shadowOffset;
  1418. break;
  1419. case GR_TEXT_H_ALIGN_CENTER:
  1420. pos.y = ( bBox.GetTop() + bBox.GetBottom() ) / 2.0;
  1421. break;
  1422. case GR_TEXT_H_ALIGN_RIGHT:
  1423. pos.y = bBox.GetTop() - shadowOffset;
  1424. break;
  1425. case GR_TEXT_H_ALIGN_INDETERMINATE:
  1426. wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
  1427. break;
  1428. }
  1429. }
  1430. else
  1431. {
  1432. switch( attrs.m_Halign )
  1433. {
  1434. case GR_TEXT_H_ALIGN_LEFT:
  1435. pos.x = bBox.GetLeft() - shadowOffset;
  1436. break;
  1437. case GR_TEXT_H_ALIGN_CENTER:
  1438. pos.x = ( bBox.GetLeft() + bBox.GetRight() ) / 2.0;
  1439. break;
  1440. case GR_TEXT_H_ALIGN_RIGHT:
  1441. pos.x = bBox.GetRight() + shadowOffset;
  1442. break;
  1443. case GR_TEXT_H_ALIGN_INDETERMINATE:
  1444. wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
  1445. break;
  1446. }
  1447. }
  1448. // Because the text vertical position is the bounding box center, the text is drawn as
  1449. // vertically centered.
  1450. attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER;
  1451. strokeText( shownText, pos, attrs, aText->GetFontMetrics() );
  1452. }
  1453. else if( drawingShadows )
  1454. {
  1455. m_gal->SetIsFill( false );
  1456. m_gal->SetIsStroke( true );
  1457. attrs.m_StrokeWidth += KiROUND( getShadowWidth( !aText->IsSelected() ) );
  1458. attrs.m_Underlined = false;
  1459. // Fudge factors to match 6.0 positioning
  1460. // New text stroking has width dependent offset but we need to center the shadow on the
  1461. // stroke. NB this offset is in font.cpp also.
  1462. int fudge = KiROUND( getShadowWidth( !aText->IsSelected() ) / 1.52 );
  1463. if( attrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && attrs.m_Angle == ANGLE_0 )
  1464. text_offset.x -= fudge;
  1465. else if( attrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && attrs.m_Angle == ANGLE_90 )
  1466. text_offset.y -= fudge;
  1467. else if( attrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && attrs.m_Angle == ANGLE_0 )
  1468. text_offset.x += fudge;
  1469. else if( attrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && attrs.m_Angle == ANGLE_90 )
  1470. text_offset.y += fudge;
  1471. strokeText( shownText, aText->GetDrawPos() + text_offset, attrs, aText->GetFontMetrics() );
  1472. }
  1473. else
  1474. {
  1475. if( aText->IsHypertext() && aText->IsRollover() )
  1476. {
  1477. m_gal->SetStrokeColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
  1478. m_gal->SetFillColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
  1479. attrs.m_Underlined = true;
  1480. }
  1481. // Adjust text drawn in an outline font to more closely mimic the positioning of
  1482. // SCH_FIELD text.
  1483. if( font->IsOutline() && aText->Type() == SCH_TEXT_T )
  1484. {
  1485. BOX2I firstLineBBox = aText->GetTextBox( 0 );
  1486. int sizeDiff = firstLineBBox.GetHeight() - aText->GetTextSize().y;
  1487. int adjust = KiROUND( sizeDiff * 0.4 );
  1488. VECTOR2I adjust_offset( 0, - adjust );
  1489. RotatePoint( adjust_offset, aText->GetDrawRotation() );
  1490. text_offset += adjust_offset;
  1491. }
  1492. if( nonCached( aText )
  1493. && aText->RenderAsBitmap( m_gal->GetWorldScale() )
  1494. && !shownText.Contains( wxT( "\n" ) ) )
  1495. {
  1496. bitmapText( shownText, aText->GetDrawPos() + text_offset, attrs );
  1497. const_cast<SCH_TEXT*>( aText )->SetFlags( IS_SHOWN_AS_BITMAP );
  1498. }
  1499. else
  1500. {
  1501. std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
  1502. if( !aText->IsHypertext() && font->IsOutline() )
  1503. cache = aText->GetRenderCache( font, shownText, text_offset );
  1504. if( cache )
  1505. {
  1506. m_gal->SetLineWidth( attrs.m_StrokeWidth );
  1507. m_gal->DrawGlyphs( *cache );
  1508. }
  1509. else
  1510. {
  1511. strokeText( shownText, aText->GetDrawPos() + text_offset, attrs,
  1512. aText->GetFontMetrics() );
  1513. }
  1514. const_cast<SCH_TEXT*>( aText )->ClearFlags( IS_SHOWN_AS_BITMAP );
  1515. }
  1516. }
  1517. // Draw anchor
  1518. if( aText->IsSelected() )
  1519. {
  1520. bool showAnchor;
  1521. switch( aText->Type() )
  1522. {
  1523. case SCH_TEXT_T:
  1524. showAnchor = true;
  1525. break;
  1526. case SCH_LABEL_T:
  1527. // Don't clutter things up if we're already showing a dangling indicator
  1528. showAnchor = !static_cast<const SCH_LABEL*>( aText )->IsDangling();
  1529. break;
  1530. case SCH_DIRECTIVE_LABEL_T:
  1531. case SCH_HIER_LABEL_T:
  1532. case SCH_GLOBAL_LABEL_T:
  1533. case SCH_SHEET_PIN_T:
  1534. // These all have shapes and so don't need anchors
  1535. showAnchor = false;
  1536. break;
  1537. default:
  1538. showAnchor = false;
  1539. break;
  1540. }
  1541. if( showAnchor )
  1542. drawAnchor( aText->GetPosition(), drawingShadows );
  1543. }
  1544. }
  1545. void SCH_PAINTER::draw( const SCH_TEXTBOX* aTextBox, int aLayer, bool aDimmed )
  1546. {
  1547. if( aTextBox->Type() == SCH_TABLECELL_T )
  1548. {
  1549. const SCH_TABLECELL* cell = static_cast<const SCH_TABLECELL*>( aTextBox );
  1550. if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
  1551. return;
  1552. }
  1553. if( !isUnitAndConversionShown( aTextBox ) )
  1554. return;
  1555. if( aTextBox->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
  1556. return;
  1557. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  1558. if( m_schSettings.IsPrinting() && drawingShadows )
  1559. return;
  1560. COLOR4D color = getRenderColor( aTextBox, aLayer, drawingShadows, aDimmed );
  1561. COLOR4D bg = m_schSettings.GetLayerColor( LAYER_SCHEMATIC_BACKGROUND );
  1562. float borderWidth = getLineWidth( aTextBox, drawingShadows );
  1563. KIFONT::FONT* font = getFont( aTextBox );
  1564. auto drawText =
  1565. [&]()
  1566. {
  1567. wxString shownText = aTextBox->GetShownText( true );
  1568. TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
  1569. attrs.m_Angle = aTextBox->GetDrawRotation();
  1570. attrs.m_StrokeWidth = KiROUND( getTextThickness( aTextBox ) );
  1571. if( aTextBox->IsHypertext() && aTextBox->IsRollover() )
  1572. {
  1573. m_gal->SetStrokeColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
  1574. m_gal->SetFillColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
  1575. attrs.m_Underlined = true;
  1576. }
  1577. std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
  1578. if( !aTextBox->IsHypertext() && font->IsOutline() )
  1579. cache = aTextBox->GetRenderCache( font, shownText );
  1580. if( cache )
  1581. {
  1582. m_gal->SetLineWidth( attrs.m_StrokeWidth );
  1583. m_gal->DrawGlyphs( *cache );
  1584. }
  1585. else
  1586. {
  1587. strokeText( shownText, aTextBox->GetDrawPos(), attrs,
  1588. aTextBox->GetFontMetrics() );
  1589. }
  1590. };
  1591. if( drawingShadows && !( aTextBox->IsBrightened() || aTextBox->IsSelected() ) )
  1592. return;
  1593. m_gal->SetFillColor( color );
  1594. m_gal->SetStrokeColor( color );
  1595. if( aLayer == LAYER_SELECTION_SHADOWS )
  1596. {
  1597. m_gal->SetIsFill( true );
  1598. m_gal->SetIsStroke( false );
  1599. m_gal->SetLineWidth( borderWidth );
  1600. m_gal->DrawRectangle( aTextBox->GetPosition(), aTextBox->GetEnd() );
  1601. }
  1602. else if( aLayer == LAYER_DEVICE_BACKGROUND || aLayer == LAYER_NOTES_BACKGROUND )
  1603. {
  1604. // Do not fill the shape in B&W print mode, to avoid to visible items
  1605. // inside the shape
  1606. if( aTextBox->IsFilled() && !m_schSettings.PrintBlackAndWhiteReq() )
  1607. {
  1608. m_gal->SetIsFill( true );
  1609. m_gal->SetIsStroke( false );
  1610. m_gal->SetLineWidth( borderWidth );
  1611. m_gal->DrawRectangle( aTextBox->GetPosition(), aTextBox->GetEnd() );
  1612. }
  1613. }
  1614. else if( aLayer == LAYER_DEVICE || aLayer == LAYER_NOTES || aLayer == LAYER_PRIVATE_NOTES )
  1615. {
  1616. drawText();
  1617. if( aTextBox->Type() != SCH_TABLECELL_T && borderWidth > 0 )
  1618. {
  1619. COLOR4D borderColor = aTextBox->GetStroke().GetColor();
  1620. LINE_STYLE borderStyle = aTextBox->GetEffectiveLineStyle();
  1621. double transparency = aTextBox->GetForcedTransparency();
  1622. if( m_schSettings.m_OverrideItemColors || aTextBox->IsBrightened()
  1623. || borderColor == COLOR4D::UNSPECIFIED )
  1624. {
  1625. borderColor = m_schSettings.GetLayerColor( aLayer );
  1626. }
  1627. if( transparency > 0.0 )
  1628. borderColor = borderColor.WithAlpha( borderColor.a * ( 1.0 - transparency ) );
  1629. if( aDimmed )
  1630. {
  1631. borderColor = borderColor.Mix( bg, 0.5f );
  1632. borderColor.Desaturate( );
  1633. }
  1634. m_gal->SetIsFill( false );
  1635. m_gal->SetIsStroke( true );
  1636. m_gal->SetStrokeColor( borderColor );
  1637. m_gal->SetLineWidth( borderWidth );
  1638. if( borderStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows )
  1639. {
  1640. m_gal->DrawRectangle( aTextBox->GetPosition(), aTextBox->GetEnd() );
  1641. }
  1642. else
  1643. {
  1644. std::vector<SHAPE*> shapes = aTextBox->MakeEffectiveShapes( true );
  1645. for( SHAPE* shape : shapes )
  1646. {
  1647. STROKE_PARAMS::Stroke( shape, borderStyle, KiROUND( borderWidth ),
  1648. &m_schSettings,
  1649. [this]( const VECTOR2I& a, const VECTOR2I& b )
  1650. {
  1651. // DrawLine has problem with 0 length lines so enforce minimum
  1652. if( a == b )
  1653. m_gal->DrawLine( a+1, b );
  1654. else
  1655. m_gal->DrawLine( a, b );
  1656. } );
  1657. }
  1658. for( SHAPE* shape : shapes )
  1659. delete shape;
  1660. }
  1661. }
  1662. }
  1663. }
  1664. void SCH_PAINTER::draw( const SCH_TABLE* aTable, int aLayer, bool aDimmed )
  1665. {
  1666. for( SCH_TABLECELL* cell : aTable->GetCells() )
  1667. draw( cell, aLayer, aDimmed );
  1668. if( aLayer == LAYER_SELECTION_SHADOWS )
  1669. return;
  1670. VECTOR2I pos = aTable->GetPosition();
  1671. VECTOR2I end = aTable->GetEnd();
  1672. int lineWidth;
  1673. COLOR4D color;
  1674. LINE_STYLE lineStyle;
  1675. auto setupStroke =
  1676. [&]( const STROKE_PARAMS& stroke )
  1677. {
  1678. lineWidth = stroke.GetWidth();
  1679. color = stroke.GetColor();
  1680. lineStyle = stroke.GetLineStyle();
  1681. if( lineWidth == 0 )
  1682. lineWidth = m_schSettings.GetDefaultPenWidth();
  1683. if( color == COLOR4D::UNSPECIFIED )
  1684. color = m_schSettings.GetLayerColor( LAYER_NOTES );
  1685. if( lineStyle == LINE_STYLE::DEFAULT )
  1686. lineStyle = LINE_STYLE::SOLID;
  1687. m_gal->SetIsFill( false );
  1688. m_gal->SetIsStroke( true );
  1689. m_gal->SetStrokeColor( color );
  1690. m_gal->SetLineWidth( (float) lineWidth );
  1691. };
  1692. auto strokeShape =
  1693. [&]( const SHAPE& shape )
  1694. {
  1695. STROKE_PARAMS::Stroke( &shape, lineStyle, lineWidth, &m_schSettings,
  1696. [&]( const VECTOR2I& a, const VECTOR2I& b )
  1697. {
  1698. // DrawLine has problem with 0 length lines so enforce minimum
  1699. if( a == b )
  1700. m_gal->DrawLine( a+1, b );
  1701. else
  1702. m_gal->DrawLine( a, b );
  1703. } );
  1704. };
  1705. auto strokeLine =
  1706. [&]( const VECTOR2I& ptA, const VECTOR2I& ptB )
  1707. {
  1708. if( lineStyle <= LINE_STYLE::FIRST_TYPE )
  1709. {
  1710. m_gal->DrawLine( ptA, ptB );
  1711. }
  1712. else
  1713. {
  1714. SHAPE_SEGMENT seg( ptA, ptB );
  1715. strokeShape( seg );
  1716. }
  1717. };
  1718. auto strokeRect =
  1719. [&]( const VECTOR2I& ptA, const VECTOR2I& ptB )
  1720. {
  1721. if( lineStyle <= LINE_STYLE::FIRST_TYPE )
  1722. {
  1723. m_gal->DrawRectangle( ptA, ptB );
  1724. }
  1725. else
  1726. {
  1727. SHAPE_RECT rect( BOX2I( ptA, ptB - ptA ) );
  1728. strokeShape( rect );
  1729. }
  1730. };
  1731. if( aTable->GetSeparatorsStroke().GetWidth() >= 0 )
  1732. {
  1733. setupStroke( aTable->GetSeparatorsStroke() );
  1734. if( aTable->StrokeColumns() )
  1735. {
  1736. for( int col = 0; col < aTable->GetColCount() - 1; ++col )
  1737. {
  1738. for( int row = 0; row < aTable->GetRowCount(); ++row )
  1739. {
  1740. SCH_TABLECELL* cell = aTable->GetCell( row, col );
  1741. VECTOR2I topRight( cell->GetEndX(), cell->GetStartY() );
  1742. if( cell->GetColSpan() > 0 && cell->GetRowSpan() > 0 )
  1743. strokeLine( topRight, cell->GetEnd() );
  1744. }
  1745. }
  1746. }
  1747. if( aTable->StrokeRows() )
  1748. {
  1749. for( int row = 0; row < aTable->GetRowCount() - 1; ++row )
  1750. {
  1751. for( int col = 0; col < aTable->GetColCount(); ++col )
  1752. {
  1753. SCH_TABLECELL* cell = aTable->GetCell( row, col );
  1754. VECTOR2I botLeft( cell->GetStartX(), cell->GetEndY() );
  1755. if( cell->GetColSpan() > 0 && cell->GetRowSpan() > 0 )
  1756. strokeLine( botLeft, cell->GetEnd() );
  1757. }
  1758. }
  1759. }
  1760. }
  1761. if( aTable->GetBorderStroke().GetWidth() >= 0 )
  1762. {
  1763. setupStroke( aTable->GetBorderStroke() );
  1764. if( aTable->StrokeHeader() )
  1765. {
  1766. SCH_TABLECELL* cell = aTable->GetCell( 0, 0 );
  1767. strokeLine( VECTOR2I( pos.x, cell->GetEndY() ), VECTOR2I( end.x, cell->GetEndY() ) );
  1768. }
  1769. if( aTable->StrokeExternal() )
  1770. strokeRect( pos, end );
  1771. }
  1772. }
  1773. wxString SCH_PAINTER::expandLibItemTextVars( const wxString& aSourceText,
  1774. const SCH_SYMBOL* aSymbolContext )
  1775. {
  1776. std::function<bool( wxString* )> symbolResolver =
  1777. [&]( wxString* token ) -> bool
  1778. {
  1779. return aSymbolContext->ResolveTextVar( &m_schematic->CurrentSheet(), token );
  1780. };
  1781. return ExpandTextVars( aSourceText, &symbolResolver );
  1782. }
  1783. void SCH_PAINTER::draw( const SCH_SYMBOL* aSymbol, int aLayer )
  1784. {
  1785. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  1786. bool DNP = aSymbol->GetDNP();
  1787. bool markExclusion = eeconfig()->m_Appearance.mark_sim_exclusions
  1788. && aSymbol->GetExcludedFromSim();
  1789. if( m_schSettings.IsPrinting() && drawingShadows )
  1790. return;
  1791. if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
  1792. {
  1793. for( const SCH_FIELD& field : aSymbol->GetFields() )
  1794. draw( &field, aLayer, DNP || markExclusion );
  1795. }
  1796. if( isFieldsLayer( aLayer ) )
  1797. return;
  1798. if( drawingShadows && !( aSymbol->IsBrightened() || aSymbol->IsSelected() ) )
  1799. {
  1800. // Don't exit here; symbol may still have selected pins
  1801. // return;
  1802. }
  1803. int unit = aSymbol->GetUnitSelection( &m_schematic->CurrentSheet() );
  1804. int bodyStyle = aSymbol->GetBodyStyle();
  1805. // Use dummy symbol if the actual couldn't be found (or couldn't be locked).
  1806. LIB_SYMBOL* originalSymbol = aSymbol->GetLibSymbolRef() ? aSymbol->GetLibSymbolRef().get()
  1807. : dummy();
  1808. std::vector<SCH_PIN*> originalPins = originalSymbol->GetPins( unit, bodyStyle );
  1809. // Copy the source so we can re-orient and translate it.
  1810. LIB_SYMBOL tempSymbol( *originalSymbol );
  1811. std::vector<SCH_PIN*> tempPins = tempSymbol.GetPins( unit, bodyStyle );
  1812. tempSymbol.SetFlags( aSymbol->GetFlags() );
  1813. OrientAndMirrorSymbolItems( &tempSymbol, aSymbol->GetOrientation() );
  1814. for( SCH_ITEM& tempItem : tempSymbol.GetDrawItems() )
  1815. {
  1816. tempItem.SetFlags( aSymbol->GetFlags() ); // SELECTED, HIGHLIGHTED, BRIGHTENED,
  1817. tempItem.Move( aSymbol->GetPosition() );
  1818. if( tempItem.Type() == SCH_TEXT_T )
  1819. {
  1820. SCH_TEXT* textItem = static_cast<SCH_TEXT*>( &tempItem );
  1821. if( textItem->HasTextVars() )
  1822. textItem->SetText( expandLibItemTextVars( textItem->GetText(), aSymbol ) );
  1823. }
  1824. else if( tempItem.Type() == SCH_TEXTBOX_T )
  1825. {
  1826. SCH_TEXTBOX* textboxItem = static_cast<SCH_TEXTBOX*>( &tempItem );
  1827. if( textboxItem->HasTextVars() )
  1828. textboxItem->SetText( expandLibItemTextVars( textboxItem->GetText(), aSymbol ) );
  1829. }
  1830. }
  1831. // Copy the pin info from the symbol to the temp pins
  1832. for( unsigned i = 0; i < tempPins.size(); ++ i )
  1833. {
  1834. SCH_PIN* symbolPin = aSymbol->GetPin( originalPins[ i ] );
  1835. SCH_PIN* tempPin = tempPins[ i ];
  1836. tempPin->ClearFlags();
  1837. tempPin->SetFlags( symbolPin->GetFlags() ); // SELECTED, HIGHLIGHTED, BRIGHTENED,
  1838. // IS_SHOWN_AS_BITMAP
  1839. tempPin->SetName( expandLibItemTextVars( symbolPin->GetShownName(), aSymbol ) );
  1840. tempPin->SetType( symbolPin->GetType() );
  1841. tempPin->SetShape( symbolPin->GetShape() );
  1842. if( symbolPin->IsDangling() )
  1843. tempPin->SetFlags( IS_DANGLING );
  1844. else
  1845. tempPin->ClearFlags( IS_DANGLING );
  1846. tempPin->SetOperatingPoint( symbolPin->GetOperatingPoint() );
  1847. }
  1848. draw( &tempSymbol, aLayer, false, aSymbol->GetUnit(), aSymbol->GetBodyStyle(),
  1849. DNP || markExclusion );
  1850. for( unsigned i = 0; i < tempPins.size(); ++i )
  1851. {
  1852. SCH_PIN* symbolPin = aSymbol->GetPin( originalPins[ i ] );
  1853. SCH_PIN* tempPin = tempPins[ i ];
  1854. symbolPin->ClearFlags();
  1855. tempPin->ClearFlags( IS_DANGLING ); // Clear this temporary flag
  1856. symbolPin->SetFlags( tempPin->GetFlags() ); // SELECTED, HIGHLIGHTED, BRIGHTENED,
  1857. // IS_SHOWN_AS_BITMAP
  1858. }
  1859. if( DNP || markExclusion )
  1860. {
  1861. int layer = DNP ? LAYER_DNP_MARKER : LAYER_EXCLUDED_FROM_SIM;
  1862. BOX2I bbox = aSymbol->GetBodyBoundingBox();
  1863. BOX2I pins = aSymbol->GetBodyAndPinsBoundingBox();
  1864. VECTOR2D margins( std::max( bbox.GetX() - pins.GetX(), pins.GetEnd().x - bbox.GetEnd().x ),
  1865. std::max( bbox.GetY() - pins.GetY(), pins.GetEnd().y - bbox.GetEnd().y ) );
  1866. int strokeWidth = 3 * schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
  1867. margins.x = std::max( margins.x * 0.6, margins.y * 0.3 );
  1868. margins.y = std::max( margins.y * 0.6, margins.x * 0.3 );
  1869. bbox.Inflate( KiROUND( margins.x ), KiROUND( margins.y ) );
  1870. VECTOR2I pt1 = bbox.GetOrigin();
  1871. VECTOR2I pt2 = bbox.GetEnd();
  1872. m_gal->PushDepth();
  1873. m_gal->AdvanceDepth();
  1874. m_gal->SetIsStroke( true );
  1875. m_gal->SetIsFill( true );
  1876. m_gal->SetStrokeColor( m_schSettings.GetLayerColor( layer ) );
  1877. m_gal->SetFillColor( m_schSettings.GetLayerColor( layer ) );
  1878. m_gal->DrawSegment( pt1, pt2, strokeWidth );
  1879. std::swap( pt1.x, pt2.x );
  1880. m_gal->DrawSegment( pt1, pt2, strokeWidth );
  1881. m_gal->PopDepth();
  1882. }
  1883. }
  1884. void SCH_PAINTER::draw( const SCH_FIELD* aField, int aLayer, bool aDimmed )
  1885. {
  1886. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  1887. if( m_schSettings.IsPrinting() && drawingShadows )
  1888. return;
  1889. if( drawingShadows && !( aField->IsBrightened() || aField->IsSelected() ) )
  1890. return;
  1891. if( !isUnitAndConversionShown( aField ) )
  1892. return;
  1893. // Must check layer as fields are sometimes drawn by their parent rather than directly
  1894. // from the view.
  1895. int layers[KIGFX::VIEW::VIEW_MAX_LAYERS];
  1896. int layers_count;
  1897. bool foundLayer = false;
  1898. aField->ViewGetLayers( layers, layers_count );
  1899. for( int i = 0; i < layers_count; ++i )
  1900. {
  1901. if( layers[i] == aLayer )
  1902. foundLayer = true;
  1903. }
  1904. if( !foundLayer )
  1905. return;
  1906. aLayer = aField->GetLayer();
  1907. COLOR4D color = getRenderColor( aField, aLayer, drawingShadows, aDimmed );
  1908. if( !( aField->IsVisible() || aField->IsForceVisible() ) )
  1909. {
  1910. bool force_show = m_schematic ? eeconfig()->m_Appearance.show_hidden_fields
  1911. : m_schSettings.m_ShowHiddenFields;
  1912. if( force_show )
  1913. color = getRenderColor( aField, LAYER_HIDDEN, drawingShadows, aDimmed );
  1914. else
  1915. return;
  1916. }
  1917. wxString shownText = aField->GetShownText( true );
  1918. if( shownText.IsEmpty() )
  1919. return;
  1920. // Calculate the text orientation according to the parent orientation.
  1921. EDA_ANGLE orient = aField->GetTextAngle();
  1922. if( aField->GetParent() && aField->GetParent()->Type() == SCH_SYMBOL_T )
  1923. {
  1924. if( static_cast<SCH_SYMBOL*>( aField->GetParent() )->GetTransform().y1 )
  1925. {
  1926. // Rotate symbol 90 degrees.
  1927. if( orient.IsHorizontal() )
  1928. orient = ANGLE_VERTICAL;
  1929. else
  1930. orient = ANGLE_HORIZONTAL;
  1931. }
  1932. }
  1933. /*
  1934. * Calculate the text justification, according to the symbol orientation/mirror.
  1935. * This is a bit complicated due to cumulative calculations:
  1936. * - numerous cases (mirrored or not, rotation)
  1937. * - the DrawGraphicText function recalculate also H and H justifications according to the
  1938. * text orientation.
  1939. * - when symbol is mirrored, the text is not mirrored and justifications are complicated
  1940. * to calculate so the easier way is to use no justifications (centered text) and use
  1941. * GetBoundingBox to know the text coordinate considered as centered
  1942. */
  1943. BOX2I bbox = aField->GetBoundingBox();
  1944. if( aField->GetParent() && aField->GetParent()->Type() == SCH_GLOBAL_LABEL_T )
  1945. {
  1946. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( aField->GetParent() );
  1947. bbox.Offset( label->GetSchematicTextOffset( &m_schSettings ) );
  1948. }
  1949. if( m_schSettings.GetDrawBoundingBoxes() )
  1950. drawItemBoundingBox( aField );
  1951. m_gal->SetStrokeColor( color );
  1952. m_gal->SetFillColor( color );
  1953. if( drawingShadows && getFont( aField )->IsOutline() )
  1954. {
  1955. BOX2I shadow_box = bbox;
  1956. shadow_box.Inflate( KiROUND( getTextThickness( aField ) * 2 ) );
  1957. m_gal->SetIsStroke( false );
  1958. m_gal->SetIsFill( true );
  1959. m_gal->DrawRectangle( shadow_box.GetPosition(), shadow_box.GetEnd() );
  1960. }
  1961. else
  1962. {
  1963. VECTOR2I textpos = bbox.Centre();
  1964. TEXT_ATTRIBUTES attributes = aField->GetAttributes();
  1965. attributes.m_Halign = GR_TEXT_H_ALIGN_CENTER;
  1966. attributes.m_Valign = GR_TEXT_V_ALIGN_CENTER;
  1967. attributes.m_StrokeWidth = KiROUND( getTextThickness( aField ) );
  1968. attributes.m_Angle = orient;
  1969. if( drawingShadows )
  1970. attributes.m_StrokeWidth += getShadowWidth( !aField->IsSelected() );
  1971. if( aField->IsHypertext() && aField->IsRollover() )
  1972. {
  1973. m_gal->SetStrokeColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
  1974. m_gal->SetFillColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
  1975. attributes.m_Underlined = true;
  1976. }
  1977. if( nonCached( aField ) && aField->RenderAsBitmap( m_gal->GetWorldScale() ) )
  1978. {
  1979. bitmapText( shownText, textpos, attributes );
  1980. const_cast<SCH_FIELD*>( aField )->SetFlags( IS_SHOWN_AS_BITMAP );
  1981. }
  1982. else
  1983. {
  1984. std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
  1985. if( !aField->IsHypertext() )
  1986. cache = aField->GetRenderCache( shownText, textpos, attributes );
  1987. if( cache )
  1988. {
  1989. m_gal->SetLineWidth( attributes.m_StrokeWidth );
  1990. m_gal->DrawGlyphs( *cache );
  1991. }
  1992. else
  1993. {
  1994. strokeText( shownText, textpos, attributes, aField->GetFontMetrics() );
  1995. }
  1996. const_cast<SCH_FIELD*>( aField )->ClearFlags( IS_SHOWN_AS_BITMAP );
  1997. }
  1998. }
  1999. // Draw anchor or umbilical line
  2000. if( aField->IsMoving() && m_schematic )
  2001. {
  2002. VECTOR2I parentPos = aField->GetParentPosition();
  2003. m_gal->SetLineWidth( m_schSettings.GetOutlineWidth() );
  2004. m_gal->SetStrokeColor( getRenderColor( aField, LAYER_SCHEMATIC_ANCHOR, drawingShadows ) );
  2005. m_gal->DrawLine( aField->GetPosition(), parentPos );
  2006. }
  2007. else if( aField->IsSelected() )
  2008. {
  2009. drawAnchor( aField->GetPosition(), drawingShadows );
  2010. }
  2011. }
  2012. void SCH_PAINTER::draw( const SCH_GLOBALLABEL* aLabel, int aLayer )
  2013. {
  2014. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2015. if( m_schSettings.IsPrinting() && drawingShadows )
  2016. return;
  2017. bool drawingDangling = aLayer == LAYER_DANGLING;
  2018. if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
  2019. {
  2020. for( const SCH_FIELD& field : aLabel->GetFields() )
  2021. draw( &field, aLayer, false );
  2022. }
  2023. if( isFieldsLayer( aLayer ) )
  2024. return;
  2025. if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
  2026. return;
  2027. COLOR4D color = getRenderColor( aLabel, LAYER_GLOBLABEL, drawingShadows );
  2028. if( drawingDangling )
  2029. {
  2030. if( aLabel->IsDangling() )
  2031. {
  2032. drawDanglingIndicator( aLabel->GetTextPos(), color,
  2033. schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
  2034. drawingShadows, aLabel->IsBrightened() );
  2035. }
  2036. return;
  2037. }
  2038. std::vector<VECTOR2I> pts;
  2039. std::deque<VECTOR2D> pts2;
  2040. aLabel->CreateGraphicShape( &m_schSettings, pts, aLabel->GetTextPos() );
  2041. for( const VECTOR2I& p : pts )
  2042. pts2.emplace_back( VECTOR2D( p.x, p.y ) );
  2043. m_gal->SetIsStroke( true );
  2044. m_gal->SetLineWidth( getLineWidth( aLabel, drawingShadows ) );
  2045. m_gal->SetStrokeColor( color );
  2046. if( drawingShadows )
  2047. {
  2048. m_gal->SetIsFill( eeconfig()->m_Selection.fill_shapes );
  2049. m_gal->SetFillColor( color );
  2050. m_gal->DrawPolygon( pts2 );
  2051. }
  2052. else
  2053. {
  2054. m_gal->SetIsFill( false );
  2055. m_gal->DrawPolyline( pts2 );
  2056. }
  2057. draw( static_cast<const SCH_TEXT*>( aLabel ), aLayer, false );
  2058. }
  2059. void SCH_PAINTER::draw( const SCH_LABEL* aLabel, int aLayer )
  2060. {
  2061. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2062. if( m_schSettings.IsPrinting() && drawingShadows )
  2063. return;
  2064. bool drawingDangling = aLayer == LAYER_DANGLING;
  2065. if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
  2066. {
  2067. for( const SCH_FIELD& field : aLabel->GetFields() )
  2068. draw( &field, aLayer, false );
  2069. }
  2070. if( isFieldsLayer( aLayer ) )
  2071. return;
  2072. if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
  2073. return;
  2074. COLOR4D color = getRenderColor( aLabel, LAYER_HIERLABEL, drawingShadows );
  2075. if( drawingDangling )
  2076. {
  2077. if( aLabel->IsDangling() )
  2078. {
  2079. drawDanglingIndicator( aLabel->GetTextPos(), color,
  2080. schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
  2081. drawingShadows, aLabel->IsBrightened() );
  2082. }
  2083. return;
  2084. }
  2085. draw( static_cast<const SCH_TEXT*>( aLabel ), aLayer, false );
  2086. }
  2087. void SCH_PAINTER::draw( const SCH_HIERLABEL* aLabel, int aLayer, bool aDimmed )
  2088. {
  2089. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2090. if( m_schSettings.IsPrinting() && drawingShadows )
  2091. return;
  2092. bool drawingDangling = aLayer == LAYER_DANGLING;
  2093. if( !( drawingShadows || drawingDangling ) || eeconfig()->m_Selection.draw_selected_children )
  2094. {
  2095. for( const SCH_FIELD& field : aLabel->GetFields() )
  2096. draw( &field, aLayer, false );
  2097. }
  2098. if( isFieldsLayer( aLayer ) )
  2099. return;
  2100. if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
  2101. return;
  2102. COLOR4D color = getRenderColor( aLabel, LAYER_HIERLABEL, drawingShadows, aDimmed );
  2103. if( drawingDangling )
  2104. {
  2105. if( aLabel->IsDangling() )
  2106. {
  2107. drawDanglingIndicator( aLabel->GetTextPos(), color,
  2108. schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
  2109. drawingShadows, aLabel->IsBrightened() );
  2110. }
  2111. return;
  2112. }
  2113. if( m_schematic )
  2114. {
  2115. SCH_CONNECTION* conn = nullptr;
  2116. if( !aLabel->IsConnectivityDirty() )
  2117. conn = aLabel->Connection();
  2118. if( conn && conn->IsBus() )
  2119. color = getRenderColor( aLabel, LAYER_BUS, drawingShadows, aDimmed );
  2120. }
  2121. std::vector<VECTOR2I> pts;
  2122. std::deque<VECTOR2D> pts2;
  2123. aLabel->CreateGraphicShape( &m_schSettings, pts, (VECTOR2I)aLabel->GetTextPos() );
  2124. for( const VECTOR2I& p : pts )
  2125. pts2.emplace_back( VECTOR2D( p.x, p.y ) );
  2126. m_gal->SetIsFill( true );
  2127. m_gal->SetFillColor( m_schSettings.GetLayerColor( LAYER_SCHEMATIC_BACKGROUND ) );
  2128. m_gal->SetIsStroke( true );
  2129. m_gal->SetLineWidth( getLineWidth( aLabel, drawingShadows ) );
  2130. m_gal->SetStrokeColor( color );
  2131. m_gal->DrawPolyline( pts2 );
  2132. draw( static_cast<const SCH_TEXT*>( aLabel ), aLayer, false );
  2133. }
  2134. void SCH_PAINTER::draw( const SCH_DIRECTIVE_LABEL* aLabel, int aLayer )
  2135. {
  2136. if( !eeconfig()->m_Appearance.show_directive_labels && !aLabel->IsSelected() )
  2137. return;
  2138. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2139. if( m_schSettings.IsPrinting() && drawingShadows )
  2140. return;
  2141. if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
  2142. {
  2143. for( const SCH_FIELD& field : aLabel->GetFields() )
  2144. draw( &field, aLayer, false );
  2145. }
  2146. if( isFieldsLayer( aLayer ) )
  2147. return;
  2148. if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
  2149. return;
  2150. COLOR4D color = getRenderColor( aLabel, LAYER_NETCLASS_REFS, drawingShadows );
  2151. if( aLayer == LAYER_DANGLING )
  2152. {
  2153. if( aLabel->IsDangling() )
  2154. {
  2155. drawDanglingIndicator( aLabel->GetTextPos(), color,
  2156. schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
  2157. drawingShadows, aLabel->IsBrightened() );
  2158. }
  2159. return;
  2160. }
  2161. std::vector<VECTOR2I> pts;
  2162. std::deque<VECTOR2D> pts2;
  2163. aLabel->CreateGraphicShape( &m_schSettings, pts, aLabel->GetTextPos() );
  2164. for( const VECTOR2I& p : pts )
  2165. pts2.emplace_back( VECTOR2D( p.x, p.y ) );
  2166. m_gal->SetIsFill( false );
  2167. m_gal->SetFillColor( color );
  2168. m_gal->SetIsStroke( true );
  2169. m_gal->SetLineWidth( getLineWidth( aLabel, drawingShadows ) );
  2170. m_gal->SetStrokeColor( color );
  2171. if( aLabel->GetShape() == LABEL_FLAG_SHAPE::F_DOT )
  2172. {
  2173. m_gal->DrawLine( pts2[0], pts2[1] );
  2174. m_gal->SetIsFill( true );
  2175. m_gal->DrawCircle( pts2[2], ( pts2[2] - pts2[1] ).EuclideanNorm() );
  2176. }
  2177. else if( aLabel->GetShape() == LABEL_FLAG_SHAPE::F_ROUND )
  2178. {
  2179. m_gal->DrawLine( pts2[0], pts2[1] );
  2180. m_gal->DrawCircle( pts2[2], ( pts2[2] - pts2[1] ).EuclideanNorm() );
  2181. }
  2182. else
  2183. {
  2184. m_gal->DrawPolyline( pts2 );
  2185. }
  2186. }
  2187. void SCH_PAINTER::draw( const SCH_SHEET* aSheet, int aLayer )
  2188. {
  2189. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2190. bool DNP = aSheet->GetDNP();
  2191. bool markExclusion = eeconfig()->m_Appearance.mark_sim_exclusions
  2192. && aSheet->GetExcludedFromSim();
  2193. if( m_schSettings.IsPrinting() && drawingShadows )
  2194. return;
  2195. if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
  2196. {
  2197. for( const SCH_FIELD& field : aSheet->GetFields() )
  2198. draw( &field, aLayer, DNP || markExclusion );
  2199. for( SCH_SHEET_PIN* sheetPin : aSheet->GetPins() )
  2200. draw( static_cast<SCH_HIERLABEL*>( sheetPin ), aLayer, DNP || markExclusion );
  2201. }
  2202. if( isFieldsLayer( aLayer ) )
  2203. return;
  2204. VECTOR2D pos = aSheet->GetPosition();
  2205. VECTOR2D size = aSheet->GetSize();
  2206. if( aLayer == LAYER_SHEET_BACKGROUND )
  2207. {
  2208. // Do not fill the shape in B&W print mode, to avoid to visible items
  2209. // inside the shape
  2210. if( !m_schSettings.PrintBlackAndWhiteReq() )
  2211. {
  2212. m_gal->SetFillColor( getRenderColor( aSheet, LAYER_SHEET_BACKGROUND, true ) );
  2213. m_gal->SetIsFill( true );
  2214. m_gal->SetIsStroke( false );
  2215. m_gal->DrawRectangle( pos, pos + size );
  2216. }
  2217. }
  2218. if( aLayer == LAYER_SHEET || aLayer == LAYER_SELECTION_SHADOWS )
  2219. {
  2220. m_gal->SetStrokeColor( getRenderColor( aSheet, LAYER_SHEET, drawingShadows ) );
  2221. m_gal->SetIsStroke( true );
  2222. m_gal->SetLineWidth( getLineWidth( aSheet, drawingShadows ) );
  2223. m_gal->SetIsFill( false );
  2224. m_gal->DrawRectangle( pos, pos + size );
  2225. }
  2226. if( DNP || markExclusion )
  2227. {
  2228. int layer = DNP ? LAYER_DNP_MARKER : LAYER_EXCLUDED_FROM_SIM;
  2229. BOX2I bbox = aSheet->GetBodyBoundingBox();
  2230. BOX2I pins = aSheet->GetBoundingBox();
  2231. VECTOR2D margins( std::max( bbox.GetX() - pins.GetX(), pins.GetEnd().x - bbox.GetEnd().x ),
  2232. std::max( bbox.GetY() - pins.GetY(), pins.GetEnd().y - bbox.GetEnd().y ) );
  2233. int strokeWidth = 3 * schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
  2234. margins.x = std::max( margins.x * 0.6, margins.y * 0.3 );
  2235. margins.y = std::max( margins.y * 0.6, margins.x * 0.3 );
  2236. bbox.Inflate( KiROUND( margins.x ), KiROUND( margins.y ) );
  2237. VECTOR2I pt1 = bbox.GetOrigin();
  2238. VECTOR2I pt2 = bbox.GetEnd();
  2239. m_gal->PushDepth();
  2240. m_gal->AdvanceDepth();
  2241. m_gal->SetIsStroke( true );
  2242. m_gal->SetIsFill( true );
  2243. m_gal->SetStrokeColor( m_schSettings.GetLayerColor( layer ) );
  2244. m_gal->SetFillColor( m_schSettings.GetLayerColor( layer ) );
  2245. m_gal->DrawSegment( pt1, pt2, strokeWidth );
  2246. std::swap( pt1.x, pt2.x );
  2247. m_gal->DrawSegment( pt1, pt2, strokeWidth );
  2248. m_gal->PopDepth();
  2249. }
  2250. }
  2251. void SCH_PAINTER::draw( const SCH_NO_CONNECT* aNC, int aLayer )
  2252. {
  2253. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2254. if( m_schSettings.IsPrinting() && drawingShadows )
  2255. return;
  2256. if( drawingShadows && !( aNC->IsBrightened() || aNC->IsSelected() ) )
  2257. return;
  2258. m_gal->SetIsStroke( true );
  2259. m_gal->SetLineWidth( getLineWidth( aNC, drawingShadows ) );
  2260. m_gal->SetStrokeColor( getRenderColor( aNC, LAYER_NOCONNECT, drawingShadows ) );
  2261. m_gal->SetIsFill( false );
  2262. VECTOR2D p = aNC->GetPosition();
  2263. int delta = std::max( aNC->GetSize(), m_schSettings.GetDefaultPenWidth() * 3 ) / 2;
  2264. m_gal->DrawLine( p + VECTOR2D( -delta, -delta ), p + VECTOR2D( delta, delta ) );
  2265. m_gal->DrawLine( p + VECTOR2D( -delta, delta ), p + VECTOR2D( delta, -delta ) );
  2266. }
  2267. void SCH_PAINTER::draw( const SCH_BUS_ENTRY_BASE *aEntry, int aLayer )
  2268. {
  2269. SCH_LAYER_ID layer = aEntry->Type() == SCH_BUS_WIRE_ENTRY_T ? LAYER_WIRE : LAYER_BUS;
  2270. SCH_LINE line( VECTOR2I(), layer );
  2271. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2272. if( m_schSettings.IsPrinting() && drawingShadows )
  2273. return;
  2274. bool drawingDangling = aLayer == LAYER_DANGLING;
  2275. if( drawingShadows && !( aEntry->IsBrightened() || aEntry->IsSelected() ) )
  2276. return;
  2277. if( aEntry->IsSelected() )
  2278. {
  2279. line.SetSelected();
  2280. // Never show unselected endpoints on bus entries
  2281. line.SetFlags( STARTPOINT | ENDPOINT );
  2282. }
  2283. else if( aEntry->IsBrightened() )
  2284. line.SetBrightened();
  2285. line.SetStartPoint( aEntry->GetPosition() );
  2286. line.SetEndPoint( aEntry->GetEnd() );
  2287. line.SetStroke( aEntry->GetStroke() );
  2288. line.SetLineWidth( KiROUND( getLineWidth( aEntry, false ) ) );
  2289. COLOR4D color = getRenderColor( aEntry, LAYER_WIRE, drawingShadows );
  2290. if( aEntry->Type() == SCH_BUS_BUS_ENTRY_T )
  2291. color = getRenderColor( aEntry, LAYER_BUS, drawingShadows );
  2292. if( drawingDangling )
  2293. {
  2294. m_gal->SetIsFill( false );
  2295. m_gal->SetIsStroke( true );
  2296. m_gal->SetStrokeColor( color.Brightened( 0.3 ) );
  2297. m_gal->SetLineWidth( m_schSettings.GetDanglingIndicatorThickness() );
  2298. if( aEntry->IsDanglingStart() )
  2299. {
  2300. m_gal->DrawCircle( aEntry->GetPosition(),
  2301. aEntry->GetPenWidth() + KiROUND( TARGET_BUSENTRY_RADIUS / 2.0 ) );
  2302. }
  2303. if( aEntry->IsDanglingEnd() )
  2304. {
  2305. m_gal->DrawCircle( aEntry->GetEnd(),
  2306. aEntry->GetPenWidth() + KiROUND( TARGET_BUSENTRY_RADIUS / 2.0 ) );
  2307. }
  2308. }
  2309. else
  2310. {
  2311. line.SetLineColor( color );
  2312. line.SetLineStyle( aEntry->GetLineStyle() );
  2313. draw( &line, aLayer );
  2314. }
  2315. }
  2316. void SCH_PAINTER::draw( const SCH_BITMAP* aBitmap, int aLayer )
  2317. {
  2318. m_gal->Save();
  2319. m_gal->Translate( aBitmap->GetPosition() );
  2320. // When the image scale factor is not 1.0, we need to modify the actual as the image scale
  2321. // factor is similar to a local zoom
  2322. double img_scale = aBitmap->GetImageScale();
  2323. if( img_scale != 1.0 )
  2324. m_gal->Scale( VECTOR2D( img_scale, img_scale ) );
  2325. if( aLayer == LAYER_DRAW_BITMAPS )
  2326. {
  2327. m_gal->DrawBitmap( *aBitmap->GetImage() );
  2328. }
  2329. if( aLayer == LAYER_SELECTION_SHADOWS )
  2330. {
  2331. if( aBitmap->IsSelected() || aBitmap->IsBrightened() )
  2332. {
  2333. COLOR4D color = getRenderColor( aBitmap, LAYER_DRAW_BITMAPS, true );
  2334. m_gal->SetIsStroke( true );
  2335. m_gal->SetStrokeColor( color );
  2336. m_gal->SetLineWidth ( getShadowWidth( aBitmap->IsBrightened() ) );
  2337. m_gal->SetIsFill( false );
  2338. // Draws a bounding box.
  2339. VECTOR2D bm_size( aBitmap->GetSize() );
  2340. // bm_size is the actual image size in UI.
  2341. // but m_gal scale was previously set to img_scale
  2342. // so recalculate size relative to this image size.
  2343. bm_size.x /= img_scale;
  2344. bm_size.y /= img_scale;
  2345. VECTOR2D origin( -bm_size.x / 2.0, -bm_size.y / 2.0 );
  2346. VECTOR2D end = origin + bm_size;
  2347. m_gal->DrawRectangle( origin, end );
  2348. }
  2349. }
  2350. m_gal->Restore();
  2351. }
  2352. void SCH_PAINTER::draw( const SCH_MARKER* aMarker, int aLayer )
  2353. {
  2354. bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
  2355. if( m_schSettings.IsPrinting() && drawingShadows )
  2356. return;
  2357. if( drawingShadows && !( aMarker->IsBrightened() || aMarker->IsSelected() ) )
  2358. return;
  2359. COLOR4D color = getRenderColor( aMarker, aMarker->GetColorLayer(), drawingShadows );
  2360. m_gal->Save();
  2361. m_gal->Translate( aMarker->GetPosition() );
  2362. m_gal->SetIsFill( !drawingShadows );
  2363. m_gal->SetFillColor( color );
  2364. m_gal->SetIsStroke( drawingShadows );
  2365. m_gal->SetLineWidth( getLineWidth( aMarker, drawingShadows ) );
  2366. m_gal->SetStrokeColor( color );
  2367. SHAPE_LINE_CHAIN polygon;
  2368. aMarker->ShapeToPolygon( polygon );
  2369. m_gal->DrawPolygon( polygon );
  2370. m_gal->Restore();
  2371. }
  2372. }; // namespace KIGFX