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