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 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 wxString formatted = FormatStackedPinForDisplay( number, m_pin.GetLength(), m_pin.GetNumberTextSize(), font, metrics );
51
52 std::optional<TEXT_INFO> info = TEXT_INFO();
53 info->m_Text = formatted;
54 info->m_TextSize = m_pin.GetNumberTextSize();
55 info->m_Thickness = m_numberThickness;
56 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
57 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
58
59 PIN_ORIENTATION orient = m_pin.PinDrawOrient( DefaultTransform );
60
61 auto estimateQABox = [&]( const wxString& txt, int size, bool isVertical ) -> VECTOR2I
62 {
63 int h = size;
64 int w = (int) ( txt.Length() * size * 0.6 );
65 if( txt.Contains( '\n' ) )
66 {
67 wxArrayString lines; wxStringSplit( txt, lines, '\n' );
68 if( isVertical )
69 {
70 int lineSpacing = KiROUND( size * 1.3 );
71 w = (int) lines.size() * lineSpacing;
72 size_t maxLen = 0; for( const wxString& l : lines ) maxLen = std::max( maxLen, l.Length() );
73 h = (int) ( maxLen * size * 0.6 );
74 }
75 else
76 {
77 int lineSpacing = KiROUND( size * 1.3 );
78 h = (int) lines.size() * lineSpacing;
79 size_t maxLen = 0; for( const wxString& l : lines ) maxLen = std::max( maxLen, l.Length() );
80 w = (int) ( maxLen * size * 0.6 );
81 }
82 }
83 return VECTOR2I( w, h );
84 };
85
86 // Pass 1: determine maximum perpendicular half span among all pin numbers to ensure
87 // a single distance from the pin center that avoids overlap for every pin.
88 const SYMBOL* parentSym = m_pin.GetParentSymbol();
89 int maxHalfHeight = 0; // vertical half span across all numbers
90 int maxHalfWidth = 0; // horizontal half span across all numbers (for vertical pins overlap avoidance)
91 int maxFullHeight = 0; // full height (for dynamic clearance)
92 if( parentSym )
93 {
94 for( const SCH_PIN* p : parentSym->GetPins() )
95 {
96 wxString raw = p->GetShownNumber();
97 if( raw.IsEmpty() )
98 continue;
99 wxString fmt = FormatStackedPinForDisplay( raw, p->GetLength(), p->GetNumberTextSize(), font, p->GetFontMetrics() );
100 // Determine true max height regardless of rotation: use isVertical=false path for multiline height
101 VECTOR2I box = estimateQABox( fmt, p->GetNumberTextSize(), false );
102 maxHalfHeight = std::max( maxHalfHeight, box.y / 2 );
103 maxFullHeight = std::max( maxFullHeight, box.y );
104 maxHalfWidth = std::max( maxHalfWidth, box.x / 2 );
105 }
106 }
108 VECTOR2I pinPos = m_pin.GetPosition();
109 const int halfLength = m_pin.GetLength() / 2;
110 bool verticalOrient = ( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN );
111
112 // We need the per-pin bounding width for vertical placement (rotated text). For vertical
113 // pins we anchor by the RIGHT edge of the text box so the gap from the pin to text is
114 // constant (clearance) independent of text width (multi-line vs single-line).
115 auto currentBox = estimateQABox( formatted, info->m_TextSize, verticalOrient );
116
117 if( verticalOrient )
118 {
119 // Vertical pins: text is placed to the LEFT (negative X) and rotated vertical so that it
120 // reads bottom->top when the schematic is in its canonical orientation. We right-edge
121 // align the text box at (pin.x - clearance) to keep a constant gap regardless of text width.
122 int boxWidth = currentBox.x;
123 int centerX = pinPos.x - clearance - boxWidth / 2;
124 info->m_TextPosition.x = centerX;
125
126 if( orient == PIN_ORIENTATION::PIN_DOWN )
127 info->m_TextPosition.y = pinPos.y + halfLength;
128 else
129 info->m_TextPosition.y = pinPos.y - halfLength;
130
131 info->m_Angle = ANGLE_VERTICAL;
132 }
133 else
134 {
135 // Horizontal pins: "above" means negative Y direction. All numbers are centered on the
136 // pin X and share a Y offset derived from the maximum half height across all numbers so
137 // that multi-line and single-line numbers align cleanly.
138 int centerY = pinPos.y - ( maxHalfHeight + clearance );
139
140 if( orient == PIN_ORIENTATION::PIN_LEFT )
141 info->m_TextPosition.x = pinPos.x - halfLength; // centered horizontally along pin
142 else
143 info->m_TextPosition.x = pinPos.x + halfLength; // centered horizontally along pin
144
145 info->m_TextPosition.y = centerY;
146 info->m_Angle = ANGLE_HORIZONTAL;
147 }
148
149 return info;
150}
151// (Removed duplicate license & namespace with second PIN_TEXT_MARGIN to avoid ambiguity)
152
153// NOTE: The real implementation of FormatStackedPinForDisplay lives in sch_pin.cpp.
154// The accidental, partial duplicate that was here has been removed.
155
156// Reintroduce small helper functions (previously inside an anonymous namespace) needed later.
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
164static int internalPinDecoSize( const SCHEMATIC_SETTINGS* aSettings, const SCH_PIN& aPin )
165{
166 if( aSettings && aSettings->m_PinSymbolSize > 0 )
167 return aSettings->m_PinSymbolSize;
168 return aPin.GetNameTextSize() != 0 ? aPin.GetNameTextSize() / 2 : aPin.GetNumberTextSize() / 2;
169}
170
171
173 m_pin( aPin ), m_schSettings( nullptr ), m_dirtyFlags( DIRTY_FLAGS::ALL )
174{
175 // Resolve the schematic (can be null, e.g. in previews)
176 const SCHEMATIC* schematic = aPin.Schematic();
177
178 if( schematic )
179 {
180 m_schSettings = &schematic->Settings();
181 }
182}
183
184
185void PIN_LAYOUT_CACHE::MarkDirty( int aDirtyFlags )
186{
187 m_dirtyFlags |= aDirtyFlags;
188}
189
190
191void PIN_LAYOUT_CACHE::SetRenderParameters( int aNameThickness, int aNumberThickness,
192 bool aShowElectricalType, bool aShowAltIcons )
193{
194 if( aNameThickness != m_nameThickness )
195 {
197 m_nameThickness = aNameThickness;
198 }
199
200 if( aNumberThickness != m_numberThickness )
201 {
203 m_numberThickness = aNumberThickness;
204 }
205
206 if( aShowElectricalType != m_showElectricalType )
207 {
209 m_showElectricalType = aShowElectricalType;
210 }
211
212 // Not (yet?) cached
213 m_showAltIcons = aShowAltIcons;
214}
215
216
217void PIN_LAYOUT_CACHE::recomputeExtentsCache( bool aDefinitelyDirty, KIFONT::FONT* aFont, int aSize,
218 const wxString& aText,
219 const KIFONT::METRICS& aFontMetrics,
220 TEXT_EXTENTS_CACHE& aCache )
221{
222 // Even if not definitely dirty, verify no font changes
223 if( !aDefinitelyDirty && aCache.m_Font == aFont && aCache.m_FontSize == aSize )
224 {
225 return;
226 }
227
228 aCache.m_Font = aFont;
229 aCache.m_FontSize = aSize;
230
231 VECTOR2D fontSize( aSize, aSize );
232 int penWidth = GetPenSizeForNormal( aSize );
233
234 // Handle multi-line text bounds properly
235 if( aText.StartsWith( "[" ) && aText.EndsWith( "]" ) && aText.Contains( "\n" ) )
236 {
237 // Extract content between braces and split into lines
238 wxString content = aText.Mid( 1, aText.Length() - 2 );
239 wxArrayString lines;
240 wxStringSplit( content, lines, '\n' );
241
242 if( lines.size() > 1 )
243 {
244 int lineSpacing = KiROUND( aSize * 1.3 ); // Same as drawMultiLineText
245 int maxWidth = 0;
246
247 // Find the widest line
248 for( const wxString& line : lines )
249 {
250 wxString trimmedLine = line;
251 trimmedLine.Trim( true ).Trim( false );
252 VECTOR2I lineExtents = aFont->StringBoundaryLimits( trimmedLine, fontSize, penWidth, false, false, aFontMetrics );
253 maxWidth = std::max( maxWidth, lineExtents.x );
254 }
255
256 // Calculate total dimensions - width is max line width, height accounts for all lines
257 int totalHeight = aSize + ( lines.size() - 1 ) * lineSpacing;
258
259 // Add space for braces
260 int braceWidth = aSize / 3;
261 maxWidth += braceWidth * 2; // Space for braces on both sides
262 totalHeight += aSize / 3; // Extra height for brace extensions
263
264 aCache.m_Extents = VECTOR2I( maxWidth, totalHeight );
265 return;
266 }
267 }
268
269 // Single line text (normal case)
270 aCache.m_Extents = aFont->StringBoundaryLimits( aText, fontSize, penWidth, false, false, aFontMetrics );
271}
272
273
275{
277 KIFONT::FONT* font = KIFONT::FONT::GetFont( cfg ? cfg->m_Appearance.default_font : wxString( "" ) );
278 const KIFONT::METRICS& metrics = m_pin.GetFontMetrics();
279
280 // Due to the fact a shadow text in position INSIDE or OUTSIDE is drawn left or right aligned,
281 // it needs an offset = shadowWidth/2 to be drawn at the same place as normal text
282 // texts drawn as GR_TEXT_H_ALIGN_CENTER do not need a specific offset.
283 // this offset is shadowWidth/2 but for some reason we need to slightly modify this offset
284 // for a better look (better alignment of shadow shape), for KiCad font only
285 if( !font->IsOutline() )
286 m_shadowOffsetAdjust = 1.2f; // Value chosen after tests
287 else
289
290 {
291 const bool dirty = isDirty( DIRTY_FLAGS::NUMBER );
292 const wxString number = m_pin.GetShownNumber();
293 recomputeExtentsCache( dirty, font, m_pin.GetNumberTextSize(), number, metrics, m_numExtentsCache );
294 }
295
296 {
297 const bool dirty = isDirty( DIRTY_FLAGS::NAME );
298 const wxString name = m_pin.GetShownName();
299 recomputeExtentsCache( dirty, font, m_pin.GetNameTextSize(), name, metrics, m_nameExtentsCache );
300 }
301
302 {
303 double fontSize = std::max( m_pin.GetNameTextSize() * 3 / 4, schIUScale.mmToIU( 0.7 ) );
305 m_pin.GetElectricalTypeName(), metrics, m_typeExtentsCache );
306 }
307
309}
310
311
313{
314 // Now, calculate boundary box corners position for the actual pin orientation
315 switch( m_pin.PinDrawOrient( DefaultTransform ) )
316 {
318 {
319 // Pin is rotated and texts positions are mirrored
320 VECTOR2I c1{ aBox.GetLeft(), aBox.GetTop() };
321 VECTOR2I c2{ aBox.GetRight(), aBox.GetBottom() };
322
323 RotatePoint( c1, VECTOR2I( 0, 0 ), ANGLE_90 );
324 RotatePoint( c2, VECTOR2I( 0, 0 ), ANGLE_90 );
325
326 aBox = BOX2I::ByCorners( c1, c2 );
327 break;
328 }
330 {
331 VECTOR2I c1{ aBox.GetLeft(), aBox.GetTop() };
332 VECTOR2I c2{ aBox.GetRight(), aBox.GetBottom() };
333
334 RotatePoint( c1, VECTOR2I( 0, 0 ), -ANGLE_90 );
335 RotatePoint( c2, VECTOR2I( 0, 0 ), -ANGLE_90 );
336
337 c1.x = -c1.x;
338 c2.x = -c2.x;
339
340 aBox = BOX2I::ByCorners( c1, c2 );
341 break;
342 }
344 // Flip it around
345 aBox.Move( { -aBox.GetCenter().x * 2, 0 } );
346 break;
347
348 default:
350 // Already in this form
351 break;
352 }
353
354 aBox.Move( m_pin.GetPosition() );
355}
356
357
359{
360 // Local nominal position for a PIN_RIGHT orientation.
361 const VECTOR2I baseLocal = aInfo.m_TextPosition;
362
363 // We apply a rotation/mirroring depending on the pin orientation so that the text anchor
364 // maintains a constant perpendicular offset from the pin origin regardless of rotation.
365 VECTOR2I rotated = baseLocal;
366 EDA_ANGLE finalAngle = aInfo.m_Angle;
367
368 switch( m_pin.PinDrawOrient( DefaultTransform ) )
369 {
370 case PIN_ORIENTATION::PIN_RIGHT: // identity
371 break;
373 rotated.x = -rotated.x;
374 rotated.y = -rotated.y;
375 aInfo.m_HAlign = GetFlippedAlignment( aInfo.m_HAlign );
376 break;
377 case PIN_ORIENTATION::PIN_UP: // rotate +90 (x,y)->(y,-x) and vertical text
378 rotated = { baseLocal.y, -baseLocal.x };
379 finalAngle = ANGLE_VERTICAL;
380 break;
381 case PIN_ORIENTATION::PIN_DOWN: // rotate -90 (x,y)->(-y,x) and vertical text, flip h-align
382 rotated = { -baseLocal.y, baseLocal.x };
383 finalAngle = ANGLE_VERTICAL;
384 aInfo.m_HAlign = GetFlippedAlignment( aInfo.m_HAlign );
385 break;
386 default:
387 break;
388 }
389
390 aInfo.m_TextPosition = rotated + m_pin.GetPosition();
391 aInfo.m_Angle = finalAngle;
392}
393
394
395BOX2I PIN_LAYOUT_CACHE::GetPinBoundingBox( bool aIncludeLabelsOnInvisiblePins,
396 bool aIncludeNameAndNumber, bool aIncludeElectricalType )
397{
398 if( const SCH_SYMBOL* symbol = dynamic_cast<const SCH_SYMBOL*>( m_pin.GetParentSymbol() ) )
399 {
400 SCH_PIN* const libPin = m_pin.GetLibPin();
401 wxCHECK( libPin, BOX2I() );
402
403 BOX2I r = libPin->GetBoundingBox( aIncludeLabelsOnInvisiblePins, aIncludeNameAndNumber,
404 aIncludeElectricalType );
405
406 r = symbol->GetTransform().TransformCoordinate( r );
407 r.Offset( symbol->GetPosition() );
408 r.Normalize();
409
410 return r;
411 }
412
413 bool includeName = aIncludeNameAndNumber && !m_pin.GetShownName().IsEmpty();
414 bool includeNumber = aIncludeNameAndNumber && !m_pin.GetShownNumber().IsEmpty();
415 bool includeType = aIncludeElectricalType;
416
417 if( !aIncludeLabelsOnInvisiblePins && !m_pin.IsVisible() )
418 {
419 includeName = false;
420 includeNumber = false;
421 includeType = false;
422 }
423
424 if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
425 {
426 if( !parentSymbol->GetShowPinNames() )
427 includeName = false;
428
429 if( !parentSymbol->GetShowPinNumbers() )
430 includeNumber = false;
431 }
432
434
435 const int pinLength = m_pin.GetLength();
436
437 // Creating and merging all the boxes is pretty quick, if cached we'd have
438 // to track many variables here, which is possible, but unlikely to be worth it.
439 BOX2I bbox;
440
441 // Untransformed pin box
442 {
443 BOX2I pinBox = BOX2I::ByCorners( { 0, 0 }, { pinLength, 0 } );
444 pinBox.Inflate( m_pin.GetPenWidth() / 2 );
445 bbox.Merge( pinBox );
446 }
447
449 {
450 bbox.Merge( *decoBox );
451 }
452
453 if( includeName )
454 {
455 if( OPT_BOX2I nameBox = getUntransformedPinNameBox() )
456 {
457 bbox.Merge( *nameBox );
458 }
459
460 if( OPT_BOX2I altIconBox = getUntransformedAltIconBox() )
461 {
462 bbox.Merge( *altIconBox );
463 }
464 }
465
466 if( includeNumber )
467 {
469 {
470 bbox.Merge( *numBox );
471 }
472 }
473
474 if( includeType )
475 {
476 if( OPT_BOX2I typeBox = getUntransformedPinTypeBox() )
477 {
478 bbox.Merge( *typeBox );
479 }
480 }
481
482 transformBoxForPin( bbox );
483
484 if( m_pin.IsDangling() )
485 {
486 // Not much point caching this, but we could
487 const CIRCLE c = GetDanglingIndicator();
488
489 BOX2I cBox = BOX2I::ByCenter( c.Center, { c.Radius * 2, c.Radius * 2 } );
490 // TODO: need some way to find the thickness...?
491 // cBox.Inflate( ??? );
492
493 bbox.Merge( cBox );
494 }
495
496 bbox.Normalize();
497 bbox.Inflate( ( m_pin.GetPenWidth() / 2 ) + 1 );
498
499 return bbox;
500}
501
502
504{
505 return CIRCLE{
506 m_pin.GetPosition(),
508 };
509}
510
511
513{
514 const float offsetRatio =
516 return schIUScale.MilsToIU( KiROUND( 24 * offsetRatio ) );
517}
518
519
521{
522 int pinNameOffset = 0;
523 if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
524 {
525 if( parentSymbol->GetShowPinNames() )
526 pinNameOffset = parentSymbol->GetPinNameOffset();
527 }
528
529 // We're considering the PIN_RIGHT scenario
530 // TEXT
531 // X-------| TEXT
532 // TEXT
533 //
534 // We'll rotate it later.
535
536 OPT_BOX2I box;
537 const int pinLength = m_pin.GetLength();
538
539 if( pinNameOffset > 0 )
540 {
541 // This means name inside the pin
542 box = BOX2I::ByCenter( { pinLength, 0 }, m_nameExtentsCache.m_Extents );
543
544 // Bump over to be left aligned just inside the pin
545 box->Move( { m_nameExtentsCache.m_Extents.x / 2 + pinNameOffset, 0 } );
546 }
547 else
548 {
549 // The pin name is always over the pin
550 box = BOX2I::ByCenter( { pinLength / 2, 0 }, m_nameExtentsCache.m_Extents );
551
552 // Bump it up
553 box->Move( { 0, -m_nameExtentsCache.m_Extents.y / 2 - getPinTextOffset() } );
554 }
555
556 return box;
557}
558
559
561{
562 int pinNameOffset = 0;
563
564 if( const SYMBOL* parentSymbol = m_pin.GetParentSymbol() )
565 {
566 if( parentSymbol->GetShowPinNames() )
567 pinNameOffset = parentSymbol->GetPinNameOffset();
568 }
569
570 const int pinLength = m_pin.GetLength();
571
572 // The pin name is always over the pin
573 OPT_BOX2I box = BOX2I::ByCenter( { pinLength / 2, 0 }, m_numExtentsCache.m_Extents );
574
575 int textPos = -m_numExtentsCache.m_Extents.y / 2 - getPinTextOffset();
576
577 // The number goes below, if there is a name outside
578 if( pinNameOffset == 0 && !m_pin.GetShownName().empty()
579 && m_pin.GetParentSymbol()->GetShowPinNames() )
580 textPos *= -1;
581
582 // Bump it up (or down)
583 box->Move( { 0, textPos } );
584
585 return box;
586}
587
588
590{
592 return std::nullopt;
593
594 BOX2I box{
595 { -m_typeExtentsCache.m_Extents.x, -m_typeExtentsCache.m_Extents.y / 2 },
596 m_typeExtentsCache.m_Extents,
597 };
598
599 // Jog left
600 box.Move( { -schIUScale.MilsToIU( PIN_TEXT_MARGIN ) - TARGET_PIN_RADIUS, 0 } );
601
602 return box;
603}
604
605
607{
608 const OPT_BOX2I nameBox = getUntransformedPinNameBox();
609
610 if( !nameBox || m_pin.GetAlternates().empty() || !m_showAltIcons )
611 return std::nullopt;
612
613 const int iconSize = std::min( m_pin.GetNameTextSize(), schIUScale.mmToIU( 1.5 ) );
614
615 VECTOR2I c{ 0, ( nameBox->GetTop() + nameBox->GetBottom() ) / 2 };
616 if( m_pin.GetParentSymbol()->GetPinNameOffset() > 0 )
617 {
618 // name inside, so icon more inside
619 c.x = nameBox->GetRight() + iconSize * 0.75;
620 }
621 else
622 {
623 c.x = nameBox->GetLeft() - iconSize * 0.75;
624 }
625
626 return BOX2I::ByCenter( c, { iconSize, iconSize } );
627}
628
629
631{
632 const GRAPHIC_PINSHAPE shape = m_pin.GetShape();
633 const int decoSize = externalPinDecoSize( m_schSettings, m_pin );
634 const int intDecoSize = internalPinDecoSize( m_schSettings, m_pin );
635
636 const auto makeInvertBox = [&]()
637 {
638 return BOX2I::ByCenter( { -decoSize, 0 }, { decoSize * 2, decoSize * 2 } );
639 };
640
641 const auto makeLowBox = [&]()
642 {
643 return BOX2I::ByCorners( { -decoSize * 2, -decoSize * 2 }, { 0, 0 } );
644 };
645
646 const auto makeClockBox = [&]()
647 {
648 return BOX2I::ByCorners( { 0, -intDecoSize }, { intDecoSize, intDecoSize } );
649 };
650
651 OPT_BOX2I box;
652
653 switch( shape )
654 {
656 {
657 box = makeInvertBox();
658 break;
659 }
661 {
662 box = makeClockBox();
663 break;
664 }
666 {
667 box = makeInvertBox();
668 box->Merge( makeClockBox() );
669 break;
670 }
672 {
673 box = makeLowBox();
674 break;
675 }
678 {
679 box = makeLowBox();
680 box->Merge( makeClockBox() );
681 break;
682 }
684 {
685 box = BOX2I::ByCenter( { 0, 0 }, { decoSize * 2, decoSize * 2 } );
686 break;
687 }
689 default:
690 {
691 // No decoration
692 break;
693 }
694 }
695
696 if( box )
697 {
698 // Put the box at the root of the pin
699 box->Move( { m_pin.GetLength(), 0 } );
700 box->Inflate( m_pin.GetPenWidth() / 2 );
701 }
702
703 return box;
704}
705
706
708{
711
712 if( box )
713 transformBoxForPin( *box );
714
715 return box;
716}
717
718
720{
723
724 if( box )
725 transformBoxForPin( *box );
726
727 return box;
728}
729
730
732{
734
735 if( box )
736 transformBoxForPin( *box );
737
738 return box;
739}
740
741
742std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> PIN_LAYOUT_CACHE::GetPinNameInfo( int aShadowWidth )
743{
745 wxString name = m_pin.GetShownName();
746
747 // TODO - work out exactly what we need to do to cache this
748 // (or if it's worth the memory/complexity)
749 // But it's not hugely expensive to recompute, and that's what's always been
750 // done to now
751 //
752 // Because pins are very likely to share a lot of characteristics, a global
753 // cache might make more sense than a per-pin cache.
754
755 if( name.IsEmpty() || !m_pin.GetParentSymbol()->GetShowPinNames() )
756 return std::nullopt;
757
758 std::optional<TEXT_INFO> info = TEXT_INFO();
759 info->m_Text = std::move( name );
760 info->m_TextSize = m_pin.GetNameTextSize();
761 info->m_Thickness = m_nameThickness;
762 info->m_Angle = ANGLE_HORIZONTAL;
763
764 bool nameInside = m_pin.GetParentSymbol()->GetPinNameOffset() > 0;
765
766 if( nameInside )
767 {
768 // This means name inside the pin
769 VECTOR2I pos = { m_pin.GetLength() + m_pin.GetParentSymbol()->GetPinNameOffset(), 0 };
770 const int shadowOffset = KiROUND( aShadowWidth * m_shadowOffsetAdjust ) / 2;
771
772 info->m_TextPosition = pos + VECTOR2I{ -shadowOffset, 0 };
773 info->m_HAlign = GR_TEXT_H_ALIGN_LEFT;
774 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
776 }
777 else
778 {
779 // The pin name is always over the pin
780 VECTOR2I pos = { m_pin.GetLength() / 2, -getPinTextOffset() - info->m_Thickness / 2 };
781
782 info->m_TextPosition = pos;
783 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
784 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
785
786 // New policy: names follow same positioning semantics as numbers except when
787 // specified as inside. When names are inside, they should not overlap with the
788 // number position.
789 const SYMBOL* parentSym = m_pin.GetParentSymbol();
790 if( parentSym )
791 {
792 int maxHalfHeight = 0;
793 for( const SCH_PIN* p : parentSym->GetPins() )
794 {
795 wxString n = p->GetShownName();
796
797 if( n.IsEmpty() )
798 continue;
799
800 maxHalfHeight = std::max( maxHalfHeight, p->GetNameTextSize() / 2 );
801 }
802
804 VECTOR2I pinPos = m_pin.GetPosition();
805 const int halfLength = m_pin.GetLength() / 2;
806 PIN_ORIENTATION orient = m_pin.PinDrawOrient( DefaultTransform );
807 bool verticalOrient = ( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN );
808
809 if( verticalOrient )
810 {
811 // Vertical pins: name mirrors number placement (left + rotated) for visual consistency.
812 int boxWidth = info->m_TextSize * (int) info->m_Text.Length() * 0.6; // heuristic width
813 int centerX = pinPos.x - clearance - boxWidth / 2;
814 info->m_TextPosition = { centerX, pinPos.y };
815
816 if( orient == PIN_ORIENTATION::PIN_DOWN )
817 info->m_TextPosition.y += halfLength;
818 else
819 info->m_TextPosition.y -= halfLength;
820
821 info->m_Angle = ANGLE_VERTICAL;
822 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
823 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
824 }
825 else
826 {
827 // Horizontal pins: name above (negative Y) aligned to same Y offset logic as numbers.
828 info->m_TextPosition = { pinPos.x, pinPos.y - ( maxHalfHeight + clearance ) };
829
830 if( orient == PIN_ORIENTATION::PIN_LEFT )
831 info->m_TextPosition.x -= halfLength;
832 else
833 info->m_TextPosition.x += halfLength;
834
835 info->m_Angle = ANGLE_HORIZONTAL;
836 info->m_HAlign = GR_TEXT_H_ALIGN_CENTER;
837 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
838 }
839 }
840 }
841 return info;
842}
843
844
845// (Removed duplicate later GetPinNumberInfo – earlier definition retained at top of file.)
846
847
848std::optional<PIN_LAYOUT_CACHE::TEXT_INFO>
850{
852
854 return std::nullopt;
855
856 std::optional<TEXT_INFO> info = TEXT_INFO();
857 info->m_Text = m_pin.GetElectricalTypeName();
858 info->m_TextSize = std::max( m_pin.GetNameTextSize() * 3 / 4, schIUScale.mmToIU( 0.7 ) );
859 info->m_Angle = ANGLE_HORIZONTAL;
860 info->m_Thickness = info->m_TextSize / 8;
861 info->m_TextPosition = { -getPinTextOffset() - info->m_Thickness / 2
862 + KiROUND( aShadowWidth * m_shadowOffsetAdjust ) / 2,
863 0 };
864 info->m_HAlign = GR_TEXT_H_ALIGN_RIGHT;
865 info->m_VAlign = GR_TEXT_V_ALIGN_CENTER;
866
867 info->m_TextPosition.x -= TARGET_PIN_RADIUS;
868
869 if( m_pin.IsDangling() )
870 {
871 info->m_TextPosition.x -= TARGET_PIN_RADIUS / 2;
872 }
873
875 return info;
876}
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:217
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
virtual std::vector< SCH_PIN * > GetPins() const =0
#define DEFAULT_TEXT_OFFSET_RATIO
Ratio of the font height to space around global labels.
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