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