KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pin_layout_cache.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers
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 3
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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "pin_layout_cache.h"
22#include <pgm_base.h>
24#include <sch_symbol.h>
25#include <eeschema_settings.h>
26#include <schematic_settings.h>
27#include <string_utils.h>
29
30// Small margin in internal units between the pin text and the pin line
31static const int PIN_TEXT_MARGIN = 4;
32
33// Forward declaration for helper implemented in sch_pin.cpp
34wxString FormatStackedPinForDisplay( const wxString& aPinNumber, int aPinLength, int aTextSize,
35 KIFONT::FONT* aFont, const KIFONT::METRICS& aFontMetrics );
36
37std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> PIN_LAYOUT_CACHE::GetPinNumberInfo( int aShadowWidth )
38{
40
41 wxString number = m_pin.GetShownNumber();
42
43 if( number.IsEmpty() || !m_pin.GetParentSymbol()->GetShowPinNumbers() )
44 return std::nullopt;
45
46 // Format stacked representation if necessary
48 KIFONT::FONT* font = KIFONT::FONT::GetFont( cfg ? cfg->m_Appearance.default_font : wxString( "" ) );
49 const KIFONT::METRICS& metrics = m_pin.GetFontMetrics();
50 int length = m_pin.GetLength();
51 int num_size = m_pin.GetNumberTextSize();
52 wxString formatted = FormatStackedPinForDisplay( number, length, num_size, font, metrics );
53
54 std::optional<TEXT_INFO> info = TEXT_INFO();
55 info->m_Text = formatted;
56 info->m_TextSize = num_size;
57 info->m_Thickness = m_numberThickness;
58 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
59 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
60
61 PIN_ORIENTATION orient = m_pin.PinDrawOrient( DefaultTransform );
62
63 const SYMBOL* parentSym = m_pin.GetParentSymbol();
64
66 VECTOR2I pinPos = m_pin.GetPosition();
67 const int halfLength = m_pin.GetLength() / 2;
68 bool verticalOrient = ( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN );
69
70 // Half the number height, which runs perpendicular to the pin in both orientations so the gap
71 // stays constant across rotations. A wrapped column spans several lines, use its full height.
72 int perpHeight = m_numExtentsCache.m_Extents.y;
73
74 if( formatted.Contains( '\n' ) )
75 {
76 wxArrayString lines;
77 wxStringSplit( formatted, lines, '\n' );
78 perpHeight = (int) lines.size() * KiROUND( num_size * 1.3 );
79 }
80
81 const int perpendicularHalf = perpHeight / 2;
82
83 // Keep a multi-line stacked block on the authored side of a mirrored symbol (issue 24894)
84 const bool flipStacked = formatted.Contains( '\n' )
85 && ( m_pin.GetFlipStackedTextSide() != m_pin.StackedTextSideFlipped( DefaultTransform ) );
86
87 if( verticalOrient )
88 {
89 // Vertical pins: text is rotated vertical so that it reads bottom->top.
90
91 // Check if both name and number are displayed
92 bool showBothNameAndNumber = !m_pin.GetShownName().IsEmpty()
93 && parentSym
94 && parentSym->GetShowPinNames()
95 && parentSym->GetPinNameOffset() == 0; // name is outside
96
97 int perpendicularOffset = clearance + perpendicularHalf + m_numberThickness;
98
99 int centerX;
100
101 if( showBothNameAndNumber )
102 {
103 // When both are shown: name goes to the left, number goes to the right
104 centerX = pinPos.x + perpendicularOffset;
105 }
106 else
107 {
108 // When only number is shown: place it to the left of the pin (right when mirrored)
109 centerX = flipStacked ? pinPos.x + perpendicularOffset : pinPos.x - perpendicularOffset;
110 }
111
112 info->m_TextPosition.x = centerX;
113
114 if( orient == PIN_ORIENTATION::PIN_DOWN )
115 info->m_TextPosition.y = pinPos.y + halfLength;
116 else
117 info->m_TextPosition.y = pinPos.y - halfLength;
118
119 info->m_Angle = ANGLE_VERTICAL;
120 }
121 else
122 {
123 // Horizontal pins: "above" means negative Y direction.
124
125 // Check if both name and number are displayed
126 bool showBothNameAndNumber = !m_pin.GetShownName().IsEmpty()
127 && parentSym
128 && parentSym->GetShowPinNames()
129 && parentSym->GetPinNameOffset() == 0; // name is outside
130
131 int centerY;
132 if( showBothNameAndNumber )
133 {
134 // When both are shown: name goes above, number goes below (top-aligned)
135 centerY = pinPos.y + clearance + perpendicularHalf + m_numberThickness;
136 }
137 else
138 {
139 // When only number is shown: place it above the pin (below when mirrored)
140 centerY = flipStacked ? pinPos.y + ( perpendicularHalf + clearance + m_numberThickness )
141 : pinPos.y - ( perpendicularHalf + clearance + m_numberThickness );
142 }
143
144 if( orient == PIN_ORIENTATION::PIN_LEFT )
145 info->m_TextPosition.x = pinPos.x - halfLength; // centered horizontally along pin
146 else
147 info->m_TextPosition.x = pinPos.x + halfLength; // centered horizontally along pin
148
149 info->m_TextPosition.y = centerY;
150 info->m_Angle = ANGLE_HORIZONTAL;
151 }
152
153 return info;
154}
155
156
157static int externalPinDecoSize( const SCHEMATIC_SETTINGS* aSettings, const SCH_PIN& aPin )
158{
159 if( aSettings && aSettings->m_PinSymbolSize )
160 return aSettings->m_PinSymbolSize;
161 return aPin.GetNumberTextSize() / 2;
162}
163
164
165static int internalPinDecoSize( const SCHEMATIC_SETTINGS* aSettings, const SCH_PIN& aPin )
166{
167 if( aSettings && aSettings->m_PinSymbolSize > 0 )
168 return aSettings->m_PinSymbolSize;
169 return aPin.GetNameTextSize() != 0 ? aPin.GetNameTextSize() / 2 : aPin.GetNumberTextSize() / 2;
170}
171
172
174 m_pin( aPin ), m_schSettings( nullptr ), m_dirtyFlags( DIRTY_FLAGS::ALL )
175{
176 // Resolve the schematic (can be null, e.g. in previews)
177 const SCHEMATIC* schematic = aPin.Schematic();
178
179 if( schematic )
180 m_schSettings = &schematic->Settings();
181}
182
183
184void PIN_LAYOUT_CACHE::MarkDirty( int aDirtyFlags )
185{
186 m_dirtyFlags |= aDirtyFlags;
187}
188
189
190void PIN_LAYOUT_CACHE::SetRenderParameters( int aNameThickness, int aNumberThickness,
191 bool aShowElectricalType, bool aShowAltIcons )
192{
193 if( aNameThickness != m_nameThickness )
194 {
196 m_nameThickness = aNameThickness;
197 }
198
199 if( aNumberThickness != m_numberThickness )
200 {
202 m_numberThickness = aNumberThickness;
203 }
204
205 if( aShowElectricalType != m_showElectricalType )
206 {
208 m_showElectricalType = aShowElectricalType;
209 }
210
211 // Not (yet?) cached
212 m_showAltIcons = aShowAltIcons;
213}
214
215
216void PIN_LAYOUT_CACHE::recomputeExtentsCache( bool aDefinitelyDirty, KIFONT::FONT* aFont, int aSize,
217 const wxString& aText,
218 const KIFONT::METRICS& aFontMetrics,
219 TEXT_EXTENTS_CACHE& aCache )
220{
221 // Even if not definitely dirty, verify no font changes
222 if( !aDefinitelyDirty && aCache.m_Font == aFont && aCache.m_FontSize == aSize )
223 {
224 return;
225 }
226
227 aCache.m_Font = aFont;
228 aCache.m_FontSize = aSize;
229
230 VECTOR2D fontSize( aSize, aSize );
231 int penWidth = GetPenSizeForNormal( aSize );
232
233 // Handle multi-line text bounds properly
234 if( aText.StartsWith( "[" ) && aText.EndsWith( "]" ) && aText.Contains( "\n" ) )
235 {
236 // Extract content between braces and split into lines
237 wxString content = aText.Mid( 1, aText.Length() - 2 );
238 wxArrayString lines;
239 wxStringSplit( content, lines, '\n' );
240
241 if( lines.size() > 1 )
242 {
243 int lineSpacing = KiROUND( aSize * 1.3 ); // Same as drawMultiLineText
244 int maxWidth = 0;
245
246 // Find the widest line
247 for( const wxString& line : lines )
248 {
249 wxString trimmedLine = line;
250 trimmedLine.Trim( true ).Trim( false );
251 VECTOR2I lineExtents = aFont->StringBoundaryLimits( trimmedLine, fontSize, penWidth, false, false, aFontMetrics );
252 maxWidth = std::max( maxWidth, lineExtents.x );
253 }
254
255 // Calculate total dimensions - width is max line width, height accounts for all lines
256 int totalHeight = aSize + ( lines.size() - 1 ) * lineSpacing;
257
258 // Add space for braces
259 int braceWidth = aSize / 3;
260 maxWidth += braceWidth * 2; // Space for braces on both sides
261 totalHeight += aSize / 3; // Extra height for brace extensions
262
263 aCache.m_Extents = VECTOR2I( maxWidth, totalHeight );
264 return;
265 }
266 }
267
268 // Single line text (normal case)
269 aCache.m_Extents = aFont->StringBoundaryLimits( aText, fontSize, penWidth, false, false, aFontMetrics );
270}
271
272
274{
276 KIFONT::FONT* font = KIFONT::FONT::GetFont( cfg ? cfg->m_Appearance.default_font : wxString( "" ) );
277 const KIFONT::METRICS& metrics = m_pin.GetFontMetrics();
278
279 // Due to the fact a shadow text in position INSIDE or OUTSIDE is drawn left or right aligned,
280 // it needs an offset = shadowWidth/2 to be drawn at the same place as normal text
281 // texts drawn as GR_TEXT_H_ALIGN_CENTER do not need a specific offset.
282 // this offset is shadowWidth/2 but for some reason we need to slightly modify this offset
283 // for a better look (better alignment of shadow shape), for KiCad font only
284 if( !font->IsOutline() )
285 m_shadowOffsetAdjust = 1.2f; // Value chosen after tests
286 else
288
289 {
290 const bool dirty = isDirty( DIRTY_FLAGS::NUMBER );
291 const wxString number = m_pin.GetShownNumber();
292 recomputeExtentsCache( dirty, font, m_pin.GetNumberTextSize(), number, metrics, m_numExtentsCache );
293 }
294
295 {
296 const bool dirty = isDirty( DIRTY_FLAGS::NAME );
297 const wxString name = m_pin.GetShownName();
298 recomputeExtentsCache( dirty, font, m_pin.GetNameTextSize(), name, metrics, m_nameExtentsCache );
299 }
300
301 {
302 double fontSize = std::max( m_pin.GetNameTextSize() * 3 / 4, schIUScale.mmToIU( 0.7 ) );
304 m_pin.GetElectricalTypeName(), metrics, m_typeExtentsCache );
305 }
306
308}
309
310
312{
313 // Now, calculate boundary box corners position for the actual pin orientation
314 switch( m_pin.PinDrawOrient( DefaultTransform ) )
315 {
317 {
318 // Pin is rotated and texts positions are mirrored
319 VECTOR2I c1{ aBox.GetLeft(), aBox.GetTop() };
320 VECTOR2I c2{ aBox.GetRight(), aBox.GetBottom() };
321
322 RotatePoint( c1, VECTOR2I( 0, 0 ), ANGLE_90 );
323 RotatePoint( c2, VECTOR2I( 0, 0 ), ANGLE_90 );
324
325 aBox = BOX2I::ByCorners( c1, c2 );
326 break;
327 }
329 {
330 VECTOR2I c1{ aBox.GetLeft(), aBox.GetTop() };
331 VECTOR2I c2{ aBox.GetRight(), aBox.GetBottom() };
332
333 RotatePoint( c1, VECTOR2I( 0, 0 ), -ANGLE_90 );
334 RotatePoint( c2, VECTOR2I( 0, 0 ), -ANGLE_90 );
335
336 c1.x = -c1.x;
337 c2.x = -c2.x;
338
339 aBox = BOX2I::ByCorners( c1, c2 );
340 break;
341 }
343 // Flip it around
344 aBox.Move( { -aBox.GetCenter().x * 2, 0 } );
345 break;
346
347 default:
349 // Already in this form
350 break;
351 }
352
353 aBox.Move( m_pin.GetPosition() );
354}
355
356
358{
359 // Local nominal position for a PIN_RIGHT orientation.
360 const VECTOR2I baseLocal = aInfo.m_TextPosition;
361
362 // We apply a rotation/mirroring depending on the pin orientation so that the text anchor
363 // maintains a constant perpendicular offset from the pin origin regardless of rotation.
364 VECTOR2I rotated = baseLocal;
365 EDA_ANGLE finalAngle = aInfo.m_Angle;
366
367 switch( m_pin.PinDrawOrient( DefaultTransform ) )
368 {
369 case PIN_ORIENTATION::PIN_RIGHT: // identity
370 break;
372 rotated.x = -rotated.x;
373 rotated.y = -rotated.y;
374 aInfo.m_HAlign = GetFlippedAlignment( aInfo.m_HAlign );
375 break;
376 case PIN_ORIENTATION::PIN_UP: // rotate +90 (x,y)->(y,-x) and vertical text
377 rotated = { baseLocal.y, -baseLocal.x };
378 finalAngle = ANGLE_VERTICAL;
379 break;
380 case PIN_ORIENTATION::PIN_DOWN: // rotate -90 (x,y)->(-y,x) and vertical text, flip h-align
381 rotated = { -baseLocal.y, baseLocal.x };
382 finalAngle = ANGLE_VERTICAL;
383 aInfo.m_HAlign = GetFlippedAlignment( aInfo.m_HAlign );
384 break;
385 default:
386 break;
387 }
388
389 aInfo.m_TextPosition = rotated + m_pin.GetPosition();
390 aInfo.m_Angle = finalAngle;
391}
392
393
394BOX2I PIN_LAYOUT_CACHE::GetPinBoundingBox( bool aIncludeLabelsOnInvisiblePins,
395 bool aIncludeNameAndNumber, bool aIncludeElectricalType )
396{
397 if( const SCH_SYMBOL* symbol = dynamic_cast<const SCH_SYMBOL*>( m_pin.GetParentSymbol() ) )
398 {
399 SCH_PIN* const libPin = m_pin.GetLibPin();
400 wxCHECK( libPin, BOX2I() );
401
402 BOX2I r = libPin->GetBoundingBox( aIncludeLabelsOnInvisiblePins, aIncludeNameAndNumber,
403 aIncludeElectricalType );
404
405 r = symbol->GetTransform().TransformCoordinate( r );
406 r.Offset( symbol->GetPosition() );
407 r.Normalize();
408
409 return r;
410 }
411
412 bool includeName = aIncludeNameAndNumber && !m_pin.GetShownName().IsEmpty();
413 bool includeNumber = aIncludeNameAndNumber && !m_pin.GetShownNumber().IsEmpty();
414 bool includeType = aIncludeElectricalType;
415
416 if( !aIncludeLabelsOnInvisiblePins && !m_pin.IsVisible() )
417 {
418 includeName = false;
419 includeNumber = false;
420 includeType = false;
421 }
422
423 if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
424 {
425 if( !parentSymbol->GetShowPinNames() )
426 includeName = false;
427
428 if( !parentSymbol->GetShowPinNumbers() )
429 includeNumber = false;
430 }
431
433
434 const int pinLength = m_pin.GetLength();
435
436 // Creating and merging all the boxes is pretty quick, if cached we'd have
437 // to track many variables here, which is possible, but unlikely to be worth it.
438 BOX2I bbox;
439
440 // Untransformed pin box
441 {
442 BOX2I pinBox = BOX2I::ByCorners( { 0, 0 }, { pinLength, 0 } );
443 pinBox.Inflate( m_pin.GetPenWidth() / 2 );
444 bbox.Merge( pinBox );
445 }
446
448 {
449 bbox.Merge( *decoBox );
450 }
451
452 if( includeName )
453 {
454 if( OPT_BOX2I nameBox = getUntransformedPinNameBox() )
455 {
456 bbox.Merge( *nameBox );
457 }
458
459 if( OPT_BOX2I altIconBox = getUntransformedAltIconBox() )
460 {
461 bbox.Merge( *altIconBox );
462 }
463 }
464
465 if( includeNumber )
466 {
468 {
469 bbox.Merge( *numBox );
470 }
471 }
472
473 if( includeType )
474 {
475 if( OPT_BOX2I typeBox = getUntransformedPinTypeBox() )
476 {
477 bbox.Merge( *typeBox );
478 }
479 }
480
481 transformBoxForPin( bbox );
482
483 if( m_pin.IsDangling() )
484 {
485 // Not much point caching this, but we could
486 const CIRCLE c = GetDanglingIndicator();
487
488 BOX2I cBox = BOX2I::ByCenter( c.Center, { c.Radius * 2, c.Radius * 2 } );
489 // TODO: need some way to find the thickness...?
490 // cBox.Inflate( ??? );
491
492 bbox.Merge( cBox );
493 }
494
495 bbox.Normalize();
496 bbox.Inflate( ( m_pin.GetPenWidth() / 2 ) + 1 );
497
498 return bbox;
499}
500
501
503{
504 return CIRCLE{
505 m_pin.GetPosition(),
507 };
508}
509
510
512{
513 const float offsetRatio =
515 return schIUScale.MilsToIU( KiROUND( 24 * offsetRatio ) );
516}
517
518
520{
521 int pinNameOffset = 0;
522 if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
523 {
524 if( parentSymbol->GetShowPinNames() )
525 pinNameOffset = parentSymbol->GetPinNameOffset();
526 }
527
528 // We're considering the PIN_RIGHT scenario
529 // TEXT
530 // X-------| TEXT
531 // TEXT
532 //
533 // We'll rotate it later.
534
535 OPT_BOX2I box;
536 const int pinLength = m_pin.GetLength();
537
538 if( pinNameOffset > 0 )
539 {
540 // This means name inside the pin
541 box = BOX2I::ByCenter( { pinLength, 0 }, m_nameExtentsCache.m_Extents );
542
543 // Bump over to be left aligned just inside the pin
544 box->Move( { m_nameExtentsCache.m_Extents.x / 2 + pinNameOffset, 0 } );
545 }
546 else
547 {
548 // The pin name is always over the pin
549 box = BOX2I::ByCenter( { pinLength / 2, 0 }, m_nameExtentsCache.m_Extents );
550
551 // Bump it up
552 box->Move( { 0, -m_nameExtentsCache.m_Extents.y / 2 - getPinTextOffset() } );
553 }
554
555 return box;
556}
557
558
560{
561 int pinNameOffset = 0;
562
563 if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
564 {
565 if( parentSymbol->GetShowPinNames() )
566 pinNameOffset = parentSymbol->GetPinNameOffset();
567 }
568
569 const int pinLength = m_pin.GetLength();
570
571 // The pin number is always over the pin (centered along its length)
572 OPT_BOX2I box = BOX2I::ByCenter( { pinLength / 2, 0 }, m_numExtentsCache.m_Extents );
573
574 // Check if both name and number are displayed (name outside the pin)
575 bool showBothNameAndNumber = ( pinNameOffset == 0
576 && !m_pin.GetShownName().empty()
577 && m_pin.GetParentSymbol()->GetShowPinNames() );
578
579 int textPos;
580 if( showBothNameAndNumber )
581 {
582 // When both are shown: name goes above, number goes below (top-aligned to bottom)
583 // Position the number below the pin, with its top edge at the clearance distance
584 textPos = m_numExtentsCache.m_Extents.y / 2 + getPinTextOffset();
585 }
586 else
587 {
588 // When only number is shown: place it above the pin
589 textPos = -m_numExtentsCache.m_Extents.y / 2 - getPinTextOffset();
590 }
591
592 // Bump it up (or down)
593 box->Move( { 0, textPos } );
594
595 return box;
596}
597
598
600{
602 return std::nullopt;
603
604 BOX2I box{
605 { -m_typeExtentsCache.m_Extents.x, -m_typeExtentsCache.m_Extents.y / 2 },
606 m_typeExtentsCache.m_Extents,
607 };
608
609 // Jog left
610 box.Move( { -schIUScale.MilsToIU( PIN_TEXT_MARGIN ) - TARGET_PIN_RADIUS, 0 } );
611
612 return box;
613}
614
615
617{
618 const OPT_BOX2I nameBox = getUntransformedPinNameBox();
619
620 if( !nameBox || m_pin.GetAlternates().empty() || !m_showAltIcons )
621 return std::nullopt;
622
623 const int iconSize = std::min( m_pin.GetNameTextSize(), schIUScale.mmToIU( 1.5 ) );
624
625 VECTOR2I c{ 0, ( nameBox->GetTop() + nameBox->GetBottom() ) / 2 };
626 if( m_pin.GetParentSymbol()->GetPinNameOffset() > 0 )
627 {
628 // name inside, so icon more inside
629 c.x = nameBox->GetRight() + iconSize * 0.75;
630 }
631 else
632 {
633 c.x = nameBox->GetLeft() - iconSize * 0.75;
634 }
635
636 return BOX2I::ByCenter( c, { iconSize, iconSize } );
637}
638
639
641{
642 const GRAPHIC_PINSHAPE shape = m_pin.GetShape();
643 const int decoSize = externalPinDecoSize( m_schSettings, m_pin );
644 const int intDecoSize = internalPinDecoSize( m_schSettings, m_pin );
645
646 const auto makeInvertBox =
647 [&]()
648 {
649 return BOX2I::ByCenter( { -decoSize, 0 }, { decoSize * 2, decoSize * 2 } );
650 };
651
652 const auto makeLowBox =
653 [&]()
654 {
655 return BOX2I::ByCorners( { -decoSize * 2, -decoSize * 2 }, { 0, 0 } );
656 };
657
658 const auto makeClockBox =
659 [&]()
660 {
661 return BOX2I::ByCorners( { 0, -intDecoSize }, { intDecoSize, intDecoSize } );
662 };
663
664 OPT_BOX2I box;
665
666 switch( shape )
667 {
669 box = makeInvertBox();
670 break;
671
673 box = makeClockBox();
674 break;
675
677 box = makeInvertBox();
678 box->Merge( makeClockBox() );
679 break;
680
682 box = makeLowBox();
683 break;
684
687 box = makeLowBox();
688 box->Merge( makeClockBox() );
689 break;
690
692 box = BOX2I::ByCenter( { 0, 0 }, { decoSize * 2, decoSize * 2 } );
693 break;
694
696 default:
697 // No decoration
698 break;
699 }
700
701 if( box )
702 {
703 // Put the box at the root of the pin
704 box->Move( { m_pin.GetLength(), 0 } );
705 box->Inflate( m_pin.GetPenWidth() / 2 );
706 }
707
708 return box;
709}
710
711
713{
716
717 if( box )
718 transformBoxForPin( *box );
719
720 return box;
721}
722
723
725{
728
729 if( box )
730 transformBoxForPin( *box );
731
732 return box;
733}
734
735
737{
739
740 if( box )
741 transformBoxForPin( *box );
742
743 return box;
744}
745
746
747std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> PIN_LAYOUT_CACHE::GetPinNameInfo( int aShadowWidth )
748{
750 wxString name = m_pin.GetShownName();
751
752 // TODO - work out exactly what we need to do to cache this
753 // (or if it's worth the memory/complexity)
754 // But it's not hugely expensive to recompute, and that's what's always been
755 // done to now
756 //
757 // Because pins are very likely to share a lot of characteristics, a global
758 // cache might make more sense than a per-pin cache.
759
760 if( name.IsEmpty() || !m_pin.GetParentSymbol()->GetShowPinNames() )
761 return std::nullopt;
762
763 std::optional<TEXT_INFO> info = TEXT_INFO();
764 info->m_Text = std::move( name );
765 info->m_TextSize = m_pin.GetNameTextSize();
766 info->m_Thickness = m_nameThickness;
767 info->m_Angle = ANGLE_HORIZONTAL;
768
769 bool nameInside = m_pin.GetParentSymbol()->GetPinNameOffset() > 0;
770
771 if( nameInside )
772 {
773 // This means name inside the pin
774 VECTOR2I pos = { m_pin.GetLength() + m_pin.GetParentSymbol()->GetPinNameOffset(), 0 };
775 const int shadowOffset = KiROUND( aShadowWidth * m_shadowOffsetAdjust ) / 2;
776
777 info->m_TextPosition = pos + VECTOR2I{ -shadowOffset, 0 };
778 info->m_HAlign = GR_TEXT_H_ALIGN_LEFT;
779 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
781 }
782 else
783 {
784 // The pin name is always over the pin
785 VECTOR2I pos = { m_pin.GetLength() / 2, -getPinTextOffset() - info->m_Thickness / 2 };
786
787 info->m_TextPosition = pos;
788 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
789 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
790
791 // New policy: names follow same positioning semantics as numbers except when
792 // specified as inside. When names are inside, they should not overlap with the
793 // number position.
794 const SYMBOL* parentSym = m_pin.GetParentSymbol();
795 if( parentSym )
796 {
797 int maxHalfHeight = 0;
798 for( const SCH_PIN* p : parentSym->GetPins() )
799 {
800 wxString n = p->GetShownName();
801
802 if( n.IsEmpty() )
803 continue;
804
805 maxHalfHeight = std::max( maxHalfHeight, p->GetNameTextSize() / 2 );
806 }
807
809 VECTOR2I pinPos = m_pin.GetPosition();
810 const int halfLength = m_pin.GetLength() / 2;
811 PIN_ORIENTATION orient = m_pin.PinDrawOrient( DefaultTransform );
812 bool verticalOrient = ( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN );
813
814 if( verticalOrient )
815 {
816 // Vertical pins: name mirrors number placement (left + rotated) for visual consistency.
817 // Use text HEIGHT (not width) for perpendicular offset - same as horizontal pins.
818 // This ensures consistent spacing between horizontal and vertical pin orientations.
819 int perpendicularOffset = clearance + info->m_TextSize / 2 + info->m_Thickness;
820 int centerX = pinPos.x - perpendicularOffset;
821 info->m_TextPosition = { centerX, pinPos.y };
822
823 if( orient == PIN_ORIENTATION::PIN_DOWN )
824 info->m_TextPosition.y += halfLength;
825 else
826 info->m_TextPosition.y -= halfLength;
827
828 info->m_Angle = ANGLE_VERTICAL;
829 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
830 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
831 }
832 else
833 {
834 // Horizontal pins: name above (negative Y) aligned to same Y offset logic as numbers.
835 info->m_TextPosition = { pinPos.x, pinPos.y - ( maxHalfHeight + clearance + info->m_Thickness ) };
836
837 if( orient == PIN_ORIENTATION::PIN_LEFT )
838 info->m_TextPosition.x -= halfLength;
839 else
840 info->m_TextPosition.x += halfLength;
841
842 info->m_Angle = ANGLE_HORIZONTAL;
843 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
844 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
845 }
846 }
847 }
848 return info;
849}
850
851
852// (Removed duplicate later GetPinNumberInfo – earlier definition retained at top of file.)
853
854
855std::optional<PIN_LAYOUT_CACHE::TEXT_INFO>
857{
859
861 return std::nullopt;
862
863 std::optional<TEXT_INFO> info = TEXT_INFO();
864 info->m_Text = m_pin.GetElectricalTypeName();
865 info->m_TextSize = std::max( m_pin.GetNameTextSize() * 3 / 4, schIUScale.mmToIU( 0.7 ) );
866 info->m_Angle = ANGLE_HORIZONTAL;
867 info->m_Thickness = info->m_TextSize / 8;
868 info->m_TextPosition = { -getPinTextOffset() - info->m_Thickness / 2
869 + KiROUND( aShadowWidth * m_shadowOffsetAdjust ) / 2,
870 0 };
871 info->m_HAlign = GR_TEXT_H_ALIGN_RIGHT;
872 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
873
874 info->m_TextPosition.x -= TARGET_PIN_RADIUS;
875
876 if( m_pin.IsDangling() )
877 {
878 info->m_TextPosition.x -= TARGET_PIN_RADIUS / 2;
879 }
880
882 return info;
883}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
static constexpr BOX2< VECTOR2I > ByCorners(const VECTOR2I &aCorner1, const VECTOR2I &aCorner2)
Definition box2.h:66
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
static constexpr BOX2< VECTOR2I > ByCenter(const VECTOR2I &aCenter, const SizeVec &aSize)
Definition box2.h:71
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr const Vec GetCenter() const
Definition box2.h:226
constexpr coord_type GetLeft() const
Definition box2.h:224
constexpr void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition box2.h:134
constexpr coord_type GetRight() const
Definition box2.h:213
constexpr coord_type GetTop() const
Definition box2.h:225
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:255
constexpr coord_type GetBottom() const
Definition box2.h:218
Represent basic circle geometry with utility geometry functions.
Definition circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition circle.h:150
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
virtual bool IsOutline() const
Definition font.h:102
VECTOR2I StringBoundaryLimits(const wxString &aText, const VECTOR2I &aSize, int aThickness, bool aBold, bool aItalic, const METRICS &aFontMetrics) const
Compute the boundary limits of aText (the bounding box of all shapes).
Definition font.cpp:447
OPT_BOX2I getUntransformedPinNameBox() const
Get the untransformd text box in the default orientation.
TEXT_EXTENTS_CACHE m_nameExtentsCache
void transformTextForPin(TEXT_INFO &aTextInfo) const
Transform text info to suit a pin's.
OPT_BOX2I GetAltIconBBox()
Get the box of the alt mode icon, if there is one.
void transformBoxForPin(BOX2I &aBox) const
Transform a box (in-place) to the pin's orientation.
void recomputeCaches()
Recompute all the caches that have become dirty.
const SCHEMATIC_SETTINGS * m_schSettings
OPT_BOX2I GetPinNumberBBox()
Get the bounding box of the pin number, if there is one.
PIN_LAYOUT_CACHE(const SCH_PIN &aPin)
TEXT_EXTENTS_CACHE m_typeExtentsCache
BOX2I GetPinBoundingBox(bool aIncludeLabelsOnInvisiblePins, bool aIncludeNameAndNumber, bool aIncludeElectricalType)
Get the bounding box of the pin itself.
TEXT_EXTENTS_CACHE m_numExtentsCache
void setClean(int aMask)
OPT_BOX2I getUntransformedPinTypeBox() const
std::optional< TEXT_INFO > GetPinNameInfo(int aShadowWidth)
Get the text info for the pin name.
static void recomputeExtentsCache(bool aDefinitelyDirty, KIFONT::FONT *aFont, int aSize, const wxString &aText, const KIFONT::METRICS &aFontMetrics, TEXT_EXTENTS_CACHE &aCache)
std::optional< TEXT_INFO > GetPinElectricalTypeInfo(int aShadowWidth)
bool isDirty(int aMask) const
CIRCLE GetDanglingIndicator() const
Gets the dangling indicator geometry for this pin, if the pin were to be dangling.
std::optional< TEXT_INFO > GetPinNumberInfo(int aShadowWidth)
OPT_BOX2I GetPinNameBBox()
Get the bounding box of the pin name, if there is one.
void MarkDirty(int aFlags)
Recompute all the layout information.
void SetRenderParameters(int aNameThickness, int aNumberThickness, bool aShowElectricalType, bool aShowAltIcons)
OPT_BOX2I getUntransformedAltIconBox() const
int getPinTextOffset() const
Get the current pin text offset.
OPT_BOX2I getUntransformedDecorationBox() const
Pin type decoration if any.
const SCH_PIN & m_pin
The pin in question.
OPT_BOX2I getUntransformedPinNumberBox() const
These are loaded from Eeschema settings but then overwritten by the project settings.
Holds all the data relating to one schematic.
Definition schematic.h:90
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:268
int GetNumberTextSize() const
Definition sch_pin.cpp:850
int GetNameTextSize() const
Definition sch_pin.cpp:824
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition sch_pin.h:264
Schematic symbol object.
Definition sch_symbol.h:69
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition symbol.h:59
int GetPinNameOffset() const
Definition symbol.h:159
virtual std::vector< SCH_PIN * > GetPins() const =0
virtual bool GetShowPinNames() const
Definition symbol.h:165
#define DEFAULT_TEXT_OFFSET_RATIO
Ratio of the font height to space around global labels.
static bool empty(const wxTextEntryBase *aCtrl)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
TRANSFORM DefaultTransform
Definition transform.cpp:28
int GetPenSizeForNormal(int aTextSize)
Definition gr_text.cpp:57
see class PGM_BASE
static int externalPinDecoSize(const SCHEMATIC_SETTINGS *aSettings, const SCH_PIN &aPin)
static int internalPinDecoSize(const SCHEMATIC_SETTINGS *aSettings, const SCH_PIN &aPin)
wxString FormatStackedPinForDisplay(const wxString &aPinNumber, int aPinLength, int aTextSize, KIFONT::FONT *aFont, const KIFONT::METRICS &aFontMetrics)
Definition sch_pin.cpp:49
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:101
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
GRAPHIC_PINSHAPE
Definition pin_type.h:80
#define PIN_TEXT_MARGIN
Definition sch_pin.cpp:107
#define TARGET_PIN_RADIUS
Definition sch_pin.h:40
T * GetAppSettings(const char *aFilename)
Utility functions for working with shapes.
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
Cached extent of a text item.
int clearance
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_CENTER
constexpr GR_TEXT_H_ALIGN_T GetFlippedAlignment(GR_TEXT_H_ALIGN_T aAlign)
Get the reverse alignment: left-right are swapped, others are unchanged.
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:225
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682