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.

722 lines
20 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include "pin_layout_cache.h"
  24. #include <geometry/direction45.h>
  25. #include <pgm_base.h>
  26. #include <settings/settings_manager.h>
  27. #include <sch_symbol.h>
  28. #include <eeschema_settings.h>
  29. #include <schematic_settings.h>
  30. #include <geometry/shape_utils.h>
  31. namespace
  32. {
  33. // small margin in internal units between the pin text and the pin line
  34. const int PIN_TEXT_MARGIN = 4;
  35. struct EXTENTS_CACHE
  36. {
  37. KIFONT::FONT* m_Font = nullptr;
  38. int m_FontSize = 0;
  39. VECTOR2I m_Extents;
  40. };
  41. /// Utility for getting the size of the 'external' pin decorators (as a radius)
  42. // i.e. the negation circle, the polarity 'slopes' and the nonlogic
  43. // marker
  44. int externalPinDecoSize( const SCHEMATIC_SETTINGS* aSettings, const SCH_PIN& aPin )
  45. {
  46. if( aSettings && aSettings->m_PinSymbolSize )
  47. return aSettings->m_PinSymbolSize;
  48. return aPin.GetNumberTextSize() / 2;
  49. }
  50. int internalPinDecoSize( const SCHEMATIC_SETTINGS* aSettings, const SCH_PIN& aPin )
  51. {
  52. if( aSettings && aSettings->m_PinSymbolSize > 0 )
  53. return aSettings->m_PinSymbolSize;
  54. return aPin.GetNameTextSize() != 0 ? aPin.GetNameTextSize() / 2 : aPin.GetNumberTextSize() / 2;
  55. }
  56. } // namespace
  57. PIN_LAYOUT_CACHE::PIN_LAYOUT_CACHE( const SCH_PIN& aPin ) :
  58. m_pin( aPin ), m_schSettings( nullptr ), m_dirtyFlags( DIRTY_FLAGS::ALL )
  59. {
  60. // Resolve the schematic (can be null, e.g. in previews)
  61. const SCHEMATIC* schematic = aPin.Schematic();
  62. if( schematic )
  63. {
  64. m_schSettings = &schematic->Settings();
  65. }
  66. }
  67. void PIN_LAYOUT_CACHE::MarkDirty( int aDirtyFlags )
  68. {
  69. m_dirtyFlags |= aDirtyFlags;
  70. }
  71. void PIN_LAYOUT_CACHE::SetRenderParameters( int aNameThickness, int aNumberThickness,
  72. bool aShowElectricalType, bool aShowAltIcons )
  73. {
  74. if( aNameThickness != m_nameThickness )
  75. {
  76. MarkDirty( DIRTY_FLAGS::NAME );
  77. m_nameThickness = aNameThickness;
  78. }
  79. if( aNumberThickness != m_numberThickness )
  80. {
  81. MarkDirty( DIRTY_FLAGS::NUMBER );
  82. m_numberThickness = aNumberThickness;
  83. }
  84. if( aShowElectricalType != m_showElectricalType )
  85. {
  86. MarkDirty( DIRTY_FLAGS::ELEC_TYPE );
  87. m_showElectricalType = aShowElectricalType;
  88. }
  89. // Not (yet?) cached
  90. m_showAltIcons = aShowAltIcons;
  91. }
  92. void PIN_LAYOUT_CACHE::recomputeExtentsCache( bool aDefinitelyDirty, KIFONT::FONT* aFont, int aSize,
  93. const wxString& aText,
  94. const KIFONT::METRICS& aFontMetrics,
  95. TEXT_EXTENTS_CACHE& aCache )
  96. {
  97. // Even if not definitely dirty, verify no font changes
  98. if( !aDefinitelyDirty && aCache.m_Font == aFont && aCache.m_FontSize == aSize )
  99. {
  100. return;
  101. }
  102. aCache.m_Font = aFont;
  103. aCache.m_FontSize = aSize;
  104. VECTOR2D fontSize( aSize, aSize );
  105. int penWidth = GetPenSizeForNormal( aSize );
  106. aCache.m_Extents =
  107. aFont->StringBoundaryLimits( aText, fontSize, penWidth, false, false, aFontMetrics );
  108. }
  109. void PIN_LAYOUT_CACHE::recomputeCaches()
  110. {
  111. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  112. EESCHEMA_SETTINGS* cfg = mgr.GetAppSettings<EESCHEMA_SETTINGS>( "eeschema" );
  113. KIFONT::FONT* font = KIFONT::FONT::GetFont( cfg->m_Appearance.default_font );
  114. const KIFONT::METRICS& metrics = m_pin.GetFontMetrics();
  115. // Due to the fact a shadow text in position INSIDE or OUTSIDE is drawn left or right aligned,
  116. // it needs an offset = shadowWidth/2 to be drawn at the same place as normal text
  117. // texts drawn as GR_TEXT_H_ALIGN_CENTER do not need a specific offset.
  118. // this offset is shadowWidth/2 but for some reason we need to slightly modify this offset
  119. // for a better look (better alignment of shadow shape), for KiCad font only
  120. if( !font->IsOutline() )
  121. m_shadowOffsetAdjust = 1.2f; // Value chosen after tests
  122. else
  123. m_shadowOffsetAdjust = 1.0f;
  124. {
  125. const bool dirty = isDirty( DIRTY_FLAGS::NUMBER );
  126. const wxString number = m_pin.GetShownNumber();
  127. recomputeExtentsCache( dirty, font, m_pin.GetNumberTextSize(), number, metrics,
  128. m_numExtentsCache );
  129. }
  130. {
  131. const bool dirty = isDirty( DIRTY_FLAGS::NAME );
  132. const wxString name = m_pin.GetShownName();
  133. recomputeExtentsCache( dirty, font, m_pin.GetNameTextSize(), name, metrics,
  134. m_nameExtentsCache );
  135. }
  136. {
  137. double fontSize = std::max( m_pin.GetNameTextSize() * 3 / 4, schIUScale.mmToIU( 0.7 ) );
  138. recomputeExtentsCache( isDirty( DIRTY_FLAGS::ELEC_TYPE ), font, fontSize,
  139. m_pin.GetElectricalTypeName(), metrics, m_typeExtentsCache );
  140. }
  141. setClean( DIRTY_FLAGS::NUMBER | DIRTY_FLAGS::NAME | DIRTY_FLAGS::ELEC_TYPE );
  142. }
  143. void PIN_LAYOUT_CACHE::transformBoxForPin( BOX2I& aBox ) const
  144. {
  145. // Now, calculate boundary box corners position for the actual pin orientation
  146. switch( m_pin.PinDrawOrient( DefaultTransform ) )
  147. {
  148. case PIN_ORIENTATION::PIN_UP:
  149. {
  150. // Pin is rotated and texts positions are mirrored
  151. VECTOR2I c1{ aBox.GetLeft(), aBox.GetTop() };
  152. VECTOR2I c2{ aBox.GetRight(), aBox.GetBottom() };
  153. RotatePoint( c1, VECTOR2I( 0, 0 ), ANGLE_90 );
  154. RotatePoint( c2, VECTOR2I( 0, 0 ), ANGLE_90 );
  155. aBox = BOX2I::ByCorners( c1, c2 );
  156. break;
  157. }
  158. case PIN_ORIENTATION::PIN_DOWN:
  159. {
  160. VECTOR2I c1{ aBox.GetLeft(), aBox.GetTop() };
  161. VECTOR2I c2{ aBox.GetRight(), aBox.GetBottom() };
  162. RotatePoint( c1, VECTOR2I( 0, 0 ), -ANGLE_90 );
  163. RotatePoint( c2, VECTOR2I( 0, 0 ), -ANGLE_90 );
  164. c1.x = -c1.x;
  165. c2.x = -c2.x;
  166. aBox = BOX2I::ByCorners( c1, c2 );
  167. break;
  168. }
  169. case PIN_ORIENTATION::PIN_LEFT:
  170. // Flip it around
  171. aBox.Move( { -aBox.GetCenter().x * 2, 0 } );
  172. break;
  173. default:
  174. case PIN_ORIENTATION::PIN_RIGHT:
  175. // Already in this form
  176. break;
  177. }
  178. aBox.Move( m_pin.GetPosition() );
  179. }
  180. void PIN_LAYOUT_CACHE::transformTextForPin( TEXT_INFO& aInfo ) const
  181. {
  182. // Now, calculate boundary box corners position for the actual pin orientation
  183. switch( m_pin.PinDrawOrient( DefaultTransform ) )
  184. {
  185. case PIN_ORIENTATION::PIN_LEFT:
  186. {
  187. aInfo.m_HAlign = GetFlippedAlignment( aInfo.m_HAlign );
  188. aInfo.m_TextPosition.x = -aInfo.m_TextPosition.x;
  189. break;
  190. }
  191. case PIN_ORIENTATION::PIN_UP:
  192. {
  193. aInfo.m_Angle = ANGLE_VERTICAL;
  194. aInfo.m_TextPosition = { aInfo.m_TextPosition.y, -aInfo.m_TextPosition.x };
  195. break;
  196. }
  197. case PIN_ORIENTATION::PIN_DOWN:
  198. {
  199. aInfo.m_Angle = ANGLE_VERTICAL;
  200. aInfo.m_TextPosition = { aInfo.m_TextPosition.y, aInfo.m_TextPosition.x };
  201. aInfo.m_HAlign = GetFlippedAlignment( aInfo.m_HAlign );
  202. break;
  203. }
  204. default:
  205. case PIN_ORIENTATION::PIN_RIGHT:
  206. // Already in this form
  207. break;
  208. }
  209. aInfo.m_TextPosition += m_pin.GetPosition();
  210. }
  211. BOX2I PIN_LAYOUT_CACHE::GetPinBoundingBox( bool aIncludeLabelsOnInvisiblePins,
  212. bool aIncludeNameAndNumber, bool aIncludeElectricalType )
  213. {
  214. if( const SCH_SYMBOL* symbol = dynamic_cast<const SCH_SYMBOL*>( m_pin.GetParentSymbol() ) )
  215. {
  216. SCH_PIN* const libPin = m_pin.GetLibPin();
  217. wxCHECK( libPin, BOX2I() );
  218. BOX2I r = libPin->GetBoundingBox( aIncludeLabelsOnInvisiblePins, aIncludeNameAndNumber,
  219. aIncludeElectricalType );
  220. r = symbol->GetTransform().TransformCoordinate( r );
  221. r.Offset( symbol->GetPosition() );
  222. r.Normalize();
  223. return r;
  224. }
  225. bool includeName = aIncludeNameAndNumber && !m_pin.GetShownName().IsEmpty();
  226. bool includeNumber = aIncludeNameAndNumber && !m_pin.GetShownNumber().IsEmpty();
  227. bool includeType = aIncludeElectricalType;
  228. if( !aIncludeLabelsOnInvisiblePins && !m_pin.IsVisible() )
  229. {
  230. includeName = false;
  231. includeNumber = false;
  232. includeType = false;
  233. }
  234. if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
  235. {
  236. if( !parentSymbol->GetShowPinNames() )
  237. includeName = false;
  238. if( !parentSymbol->GetShowPinNumbers() )
  239. includeNumber = false;
  240. }
  241. recomputeCaches();
  242. const int pinLength = m_pin.GetLength();
  243. // Creating and merging all the boxes is pretty quick, if cached we'd have
  244. // to track many variables here, which is possible, but unlikely to be worth it.
  245. BOX2I bbox;
  246. // Untransformed pin box
  247. {
  248. BOX2I pinBox = BOX2I::ByCorners( { 0, 0 }, { pinLength, 0 } );
  249. pinBox.Inflate( m_pin.GetPenWidth() / 2 );
  250. bbox.Merge( pinBox );
  251. }
  252. if( OPT_BOX2I decoBox = getUntransformedDecorationBox() )
  253. {
  254. bbox.Merge( *decoBox );
  255. }
  256. if( includeName )
  257. {
  258. if( OPT_BOX2I nameBox = getUntransformedPinNameBox() )
  259. {
  260. bbox.Merge( *nameBox );
  261. }
  262. if( OPT_BOX2I altIconBox = getUntransformedAltIconBox() )
  263. {
  264. bbox.Merge( *altIconBox );
  265. }
  266. }
  267. if( includeNumber )
  268. {
  269. if( OPT_BOX2I numBox = getUntransformedPinNumberBox() )
  270. {
  271. bbox.Merge( *numBox );
  272. }
  273. }
  274. if( includeType )
  275. {
  276. if( OPT_BOX2I typeBox = getUntransformedPinTypeBox() )
  277. {
  278. bbox.Merge( *typeBox );
  279. }
  280. }
  281. transformBoxForPin( bbox );
  282. if( m_pin.IsDangling() )
  283. {
  284. // Not much point caching this, but we could
  285. const CIRCLE c = GetDanglingIndicator();
  286. BOX2I cBox = BOX2I::ByCenter( c.Center, { c.Radius * 2, c.Radius * 2 } );
  287. // TODO: need some way to find the thickness...?
  288. // cBox.Inflate( ??? );
  289. bbox.Merge( cBox );
  290. }
  291. bbox.Normalize();
  292. bbox.Inflate( ( m_pin.GetPenWidth() / 2 ) + 1 );
  293. return bbox;
  294. }
  295. CIRCLE PIN_LAYOUT_CACHE::GetDanglingIndicator() const
  296. {
  297. return CIRCLE{
  298. m_pin.GetPosition(),
  299. TARGET_PIN_RADIUS,
  300. };
  301. }
  302. int PIN_LAYOUT_CACHE::getPinTextOffset() const
  303. {
  304. const float offsetRatio =
  305. m_schSettings ? m_schSettings->m_TextOffsetRatio : DEFAULT_TEXT_OFFSET_RATIO;
  306. return schIUScale.MilsToIU( KiROUND( 24 * offsetRatio ) );
  307. }
  308. OPT_BOX2I PIN_LAYOUT_CACHE::getUntransformedPinNameBox() const
  309. {
  310. int pinNameOffset = 0;
  311. if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
  312. {
  313. if( parentSymbol->GetShowPinNames() )
  314. pinNameOffset = parentSymbol->GetPinNameOffset();
  315. }
  316. // We're considering the PIN_RIGHT scenario
  317. // TEXT
  318. // X-------| TEXT
  319. // TEXT
  320. //
  321. // We'll rotate it later.
  322. OPT_BOX2I box;
  323. const int pinLength = m_pin.GetLength();
  324. if( pinNameOffset > 0 )
  325. {
  326. // This means name inside the pin
  327. box = BOX2I::ByCenter( { pinLength, 0 }, m_nameExtentsCache.m_Extents );
  328. // Bump over to be left aligned just inside the pin
  329. box->Move( { m_nameExtentsCache.m_Extents.x / 2 + pinNameOffset, 0 } );
  330. }
  331. else
  332. {
  333. // The pin name is always over the pin
  334. box = BOX2I::ByCenter( { pinLength / 2, 0 }, m_nameExtentsCache.m_Extents );
  335. // Bump it up
  336. box->Move( { 0, -m_nameExtentsCache.m_Extents.y / 2 - getPinTextOffset() } );
  337. }
  338. return box;
  339. }
  340. OPT_BOX2I PIN_LAYOUT_CACHE::getUntransformedPinNumberBox() const
  341. {
  342. int pinNameOffset = 0;
  343. if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
  344. {
  345. if( parentSymbol->GetShowPinNames() )
  346. pinNameOffset = parentSymbol->GetPinNameOffset();
  347. }
  348. const int pinLength = m_pin.GetLength();
  349. // The pin name is always over the pin
  350. OPT_BOX2I box = BOX2I::ByCenter( { pinLength / 2, 0 }, m_numExtentsCache.m_Extents );
  351. int textPos = -m_numExtentsCache.m_Extents.y / 2 - getPinTextOffset();
  352. // The number goes below, if there is a name outside
  353. if( pinNameOffset == 0 && !m_pin.GetShownName().empty()
  354. && m_pin.GetParentSymbol()->GetShowPinNames() )
  355. textPos *= -1;
  356. // Bump it up (or down)
  357. box->Move( { 0, textPos } );
  358. return box;
  359. }
  360. OPT_BOX2I PIN_LAYOUT_CACHE::getUntransformedPinTypeBox() const
  361. {
  362. if( !m_showElectricalType )
  363. return std::nullopt;
  364. BOX2I box{
  365. { -m_typeExtentsCache.m_Extents.x, -m_typeExtentsCache.m_Extents.y / 2 },
  366. m_typeExtentsCache.m_Extents,
  367. };
  368. // Jog left
  369. box.Move( { -schIUScale.MilsToIU( PIN_TEXT_MARGIN ) - TARGET_PIN_RADIUS, 0 } );
  370. return box;
  371. }
  372. OPT_BOX2I PIN_LAYOUT_CACHE::getUntransformedAltIconBox() const
  373. {
  374. const OPT_BOX2I nameBox = getUntransformedPinNameBox();
  375. if( !nameBox || m_pin.GetAlternates().empty() || !m_showAltIcons )
  376. return std::nullopt;
  377. const int iconSize = std::min( m_pin.GetNameTextSize(), schIUScale.mmToIU( 1.5 ) );
  378. VECTOR2I c{ 0, ( nameBox->GetTop() + nameBox->GetBottom() ) / 2 };
  379. if( m_pin.GetParentSymbol()->GetPinNameOffset() > 0 )
  380. {
  381. // name inside, so icon more inside
  382. c.x = nameBox->GetRight() + iconSize * 0.75;
  383. }
  384. else
  385. {
  386. c.x = nameBox->GetLeft() - iconSize * 0.75;
  387. }
  388. return BOX2I::ByCenter( c, { iconSize, iconSize } );
  389. }
  390. OPT_BOX2I PIN_LAYOUT_CACHE::getUntransformedDecorationBox() const
  391. {
  392. const GRAPHIC_PINSHAPE shape = m_pin.GetShape();
  393. const int decoSize = externalPinDecoSize( m_schSettings, m_pin );
  394. const int intDecoSize = internalPinDecoSize( m_schSettings, m_pin );
  395. const auto makeInvertBox = [&]()
  396. {
  397. return BOX2I::ByCenter( { -decoSize, 0 }, { decoSize * 2, decoSize * 2 } );
  398. };
  399. const auto makeLowBox = [&]()
  400. {
  401. return BOX2I::ByCorners( { -decoSize * 2, -decoSize * 2 }, { 0, 0 } );
  402. };
  403. const auto makeClockBox = [&]()
  404. {
  405. return BOX2I::ByCorners( { 0, -intDecoSize }, { intDecoSize, intDecoSize } );
  406. };
  407. OPT_BOX2I box;
  408. switch( shape )
  409. {
  410. case GRAPHIC_PINSHAPE::INVERTED:
  411. {
  412. box = makeInvertBox();
  413. break;
  414. }
  415. case GRAPHIC_PINSHAPE::CLOCK:
  416. {
  417. box = makeClockBox();
  418. break;
  419. }
  420. case GRAPHIC_PINSHAPE::INVERTED_CLOCK:
  421. {
  422. box = makeInvertBox();
  423. box->Merge( makeClockBox() );
  424. break;
  425. }
  426. case GRAPHIC_PINSHAPE::INPUT_LOW:
  427. {
  428. box = makeLowBox();
  429. break;
  430. }
  431. case GRAPHIC_PINSHAPE::FALLING_EDGE_CLOCK:
  432. case GRAPHIC_PINSHAPE::CLOCK_LOW:
  433. {
  434. box = makeLowBox();
  435. box->Merge( makeClockBox() );
  436. break;
  437. }
  438. case GRAPHIC_PINSHAPE::NONLOGIC:
  439. {
  440. box = BOX2I::ByCenter( { 0, 0 }, { decoSize * 2, decoSize * 2 } );
  441. break;
  442. }
  443. case GRAPHIC_PINSHAPE::LINE:
  444. default:
  445. {
  446. // No decoration
  447. break;
  448. }
  449. }
  450. if( box )
  451. {
  452. // Put the box at the root of the pin
  453. box->Move( { m_pin.GetLength(), 0 } );
  454. box->Inflate( m_pin.GetPenWidth() / 2 );
  455. }
  456. return box;
  457. }
  458. OPT_BOX2I PIN_LAYOUT_CACHE::GetPinNameBBox()
  459. {
  460. recomputeCaches();
  461. OPT_BOX2I box = getUntransformedPinNameBox();
  462. if( box )
  463. transformBoxForPin( *box );
  464. return box;
  465. }
  466. OPT_BOX2I PIN_LAYOUT_CACHE::GetPinNumberBBox()
  467. {
  468. recomputeCaches();
  469. OPT_BOX2I box = getUntransformedPinNumberBox();
  470. if( box )
  471. transformBoxForPin( *box );
  472. return box;
  473. }
  474. OPT_BOX2I PIN_LAYOUT_CACHE::GetAltIconBBox()
  475. {
  476. OPT_BOX2I box = getUntransformedAltIconBox();
  477. if( box )
  478. transformBoxForPin( *box );
  479. return box;
  480. }
  481. std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> PIN_LAYOUT_CACHE::GetPinNameInfo( int aShadowWidth )
  482. {
  483. recomputeCaches();
  484. wxString name = m_pin.GetShownName();
  485. // TODO - work out exactly what we need to do to cache this
  486. // (or if it's worth the memory/complexity)
  487. // But it's not hugely expensive to recompute, and that's what's always been
  488. // done to now
  489. //
  490. // Because pins are very likely to share a lot of characteristics, a global
  491. // cache might make more sense than a per-pin cache.
  492. if( name.IsEmpty() || !m_pin.GetParentSymbol()->GetShowPinNames() )
  493. return std::nullopt;
  494. std::optional<TEXT_INFO> info = TEXT_INFO();
  495. info->m_Text = std::move( name );
  496. info->m_TextSize = m_pin.GetNameTextSize();
  497. info->m_Thickness = m_nameThickness;
  498. info->m_Angle = ANGLE_HORIZONTAL;
  499. if( m_pin.GetParentSymbol()->GetPinNameOffset() > 0 )
  500. {
  501. // This means name inside the pin
  502. VECTOR2I pos = { m_pin.GetLength() + m_pin.GetParentSymbol()->GetPinNameOffset(), 0 };
  503. const int thickOffset =
  504. info->m_Thickness - KiROUND( aShadowWidth * m_shadowOffsetAdjust ) / 2;
  505. info->m_TextPosition = pos + VECTOR2I{ thickOffset, 0 };
  506. info->m_HAlign = GR_TEXT_H_ALIGN_LEFT;
  507. info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
  508. }
  509. else
  510. {
  511. // The pin name is always over the pin
  512. VECTOR2I pos = { m_pin.GetLength() / 2, -getPinTextOffset() - info->m_Thickness / 2 };
  513. info->m_TextPosition = pos;
  514. info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
  515. info->m_VAlign = GR_TEXT_V_ALIGN_BOTTOM;
  516. }
  517. transformTextForPin( *info );
  518. return info;
  519. }
  520. std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> PIN_LAYOUT_CACHE::GetPinNumberInfo( int aShadowWidth )
  521. {
  522. recomputeCaches();
  523. wxString number = m_pin.GetShownNumber();
  524. if( number.IsEmpty() || !m_pin.GetParentSymbol()->GetShowPinNumbers() )
  525. return std::nullopt;
  526. std::optional<TEXT_INFO> info;
  527. info = TEXT_INFO();
  528. info->m_Text = std::move( number );
  529. info->m_TextSize = m_pin.GetNumberTextSize();
  530. info->m_Thickness = m_numberThickness;
  531. info->m_Angle = ANGLE_HORIZONTAL;
  532. info->m_TextPosition = { m_pin.GetLength() / 2, 0 };
  533. info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
  534. // The pin number is above the pin if there's no name, or the name is inside
  535. const bool numAbove =
  536. m_pin.GetParentSymbol()->GetPinNameOffset() > 0
  537. || ( m_pin.GetShownName().empty() || !m_pin.GetParentSymbol()->GetShowPinNames() );
  538. if( numAbove )
  539. {
  540. info->m_TextPosition.y -= getPinTextOffset() + info->m_Thickness / 2;
  541. info->m_VAlign = GR_TEXT_V_ALIGN_BOTTOM;
  542. }
  543. else
  544. {
  545. info->m_TextPosition.y += getPinTextOffset() + info->m_Thickness / 2;
  546. info->m_VAlign = GR_TEXT_V_ALIGN_TOP;
  547. }
  548. transformTextForPin( *info );
  549. return info;
  550. }
  551. std::optional<PIN_LAYOUT_CACHE::TEXT_INFO>
  552. PIN_LAYOUT_CACHE::GetPinElectricalTypeInfo( int aShadowWidth )
  553. {
  554. recomputeCaches();
  555. if( !m_showElectricalType )
  556. return std::nullopt;
  557. std::optional<TEXT_INFO> info = TEXT_INFO();
  558. info->m_Text = m_pin.GetElectricalTypeName();
  559. info->m_TextSize = std::max( m_pin.GetNameTextSize() * 3 / 4, schIUScale.mmToIU( 0.7 ) );
  560. info->m_Angle = ANGLE_HORIZONTAL;
  561. info->m_Thickness = info->m_TextSize / 8;
  562. info->m_TextPosition = { -getPinTextOffset() - info->m_Thickness / 2
  563. + KiROUND( aShadowWidth * m_shadowOffsetAdjust ) / 2,
  564. 0 };
  565. info->m_HAlign = GR_TEXT_H_ALIGN_RIGHT;
  566. info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
  567. info->m_TextPosition.x -= TARGET_PIN_RADIUS;
  568. if( m_pin.IsDangling() )
  569. {
  570. info->m_TextPosition.x -= TARGET_PIN_RADIUS / 2;
  571. }
  572. transformTextForPin( *info );
  573. return info;
  574. }