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.

3074 lines
102 KiB

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