34#include <boost/test/unit_test.hpp>
43 auto symbol = std::make_unique<LIB_SYMBOL>( wxT(
"TestResistor" ) );
46 symbol->SetPinNameOffset( 0 );
49 auto pin1 = std::make_unique<SCH_PIN>( symbol.get() );
53 pin1->SetNumber( wxT(
"[1-5]" ) );
54 pin1->SetName( wxT(
"A" ) );
59 auto pin2 = std::make_unique<SCH_PIN>( symbol.get() );
63 pin2->SetNumber( wxT(
"[6,7,9-11]" ) );
64 pin2->SetName( wxT(
"B" ) );
69 symbol->AddDrawItem(
pin1.release() );
70 symbol->AddDrawItem( pin2.release() );
83 int length =
pin->GetLength();
85 switch(
pin->PinDrawOrient( transform ) )
117 std::vector<SCH_PIN*> pins;
119 for(
SCH_ITEM& item : symbol->GetDrawItems() )
122 pins.push_back(
static_cast<SCH_PIN*
>( &item ) );
125 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
128 std::vector<TRANSFORM> rotations = {
135 std::vector<wxString> rotationNames = { wxT(
"0°"), wxT(
"90°"), wxT(
"180°"), wxT(
"270°") };
137 for(
size_t r = 0; r < rotations.size(); r++ )
139 const TRANSFORM& transform = rotations[r];
140 const wxString& rotName = rotationNames[r];
146 for(
size_t p = 0; p < pins.size(); p++ )
157 BOOST_REQUIRE_MESSAGE( numberBox.has_value(),
158 "Pin '" <<
pin->GetNumber() <<
"' has no number box at rotation " << rotName );
163 BOOST_CHECK_MESSAGE( !numberBox->Intersects( pinStart, pinEnd ),
164 "Pin number '" <<
pin->GetNumber() <<
"' overlaps pin geometry at rotation "
169 BOX2I grown = *numberBox;
172 BOOST_CHECK_MESSAGE( grown.
Intersects( pinStart, pinEnd ),
173 "Inflated number box for '" <<
pin->GetNumber()
174 <<
"' should reach the pin line at rotation " << rotName );
193 std::vector<SCH_PIN*> pins;
195 for(
SCH_ITEM& item : symbol->GetDrawItems() )
198 pins.push_back(
static_cast<SCH_PIN*
>( &item ) );
201 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
204 std::vector<TRANSFORM> rotations = {
211 std::vector<wxString> rotationNames = { wxT(
"0°"), wxT(
"90°"), wxT(
"180°"), wxT(
"270°") };
213 for(
size_t r = 0; r < rotations.size(); r++ )
215 const TRANSFORM& transform = rotations[r];
216 const wxString& rotName = rotationNames[r];
231 std::vector<PinTextInfo> pinInfos;
236 info.pinPos =
pin->GetPosition();
237 info.pinNumber =
pin->GetNumber();
243 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> numberInfoOpt = cache.
GetPinNumberInfo( 0 );
245 if( numberInfoOpt.has_value() )
249 info.isMultiline = numberInfo.
m_Text.Contains(
'\n' );
253 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> nameInfoOpt = cache.
GetPinNameInfo( 0 );
255 if( nameInfoOpt.has_value() )
261 pinInfos.push_back(
info );
263 wxLogTrace(
"KICAD_PINS",
"Rotation %s, Pin %s: pos=(%d,%d) numberPos=(%d,%d) namePos=(%d,%d) multiline=%s",
264 rotName,
info.pinNumber,
268 info.isMultiline ? wxT(
"YES") : wxT(
"NO") );
271 BOOST_REQUIRE_EQUAL( pinInfos.size(), 2 );
280 for(
const PinTextInfo& inf : pinInfos )
282 BOOST_CHECK_MESSAGE( inf.numberPos.x > inf.pinPos.x,
283 "At rotation " << rotName <<
", number for pin " << inf.pinNumber <<
" not right of vertical pin." );
284 BOOST_CHECK_MESSAGE( inf.namePos.x < inf.pinPos.x,
285 "At rotation " << rotName <<
", name for pin " << inf.pinNumber <<
" not left of vertical pin." );
290 for(
const PinTextInfo& inf : pinInfos )
292 BOOST_CHECK_MESSAGE( inf.numberPos.y > inf.pinPos.y,
293 "At rotation " << rotName <<
", number for pin " << inf.pinNumber <<
" not below horizontal pin." );
294 BOOST_CHECK_MESSAGE( inf.namePos.y < inf.pinPos.y,
295 "At rotation " << rotName <<
", name for pin " << inf.pinNumber <<
" not above horizontal pin." );
323 std::vector<SCH_PIN*> pins;
325 for(
SCH_ITEM& item : symbol->GetDrawItems() )
328 pins.push_back(
static_cast<SCH_PIN*
>( &item ) );
331 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
333 const std::vector<TRANSFORM> rotations = {
340 const std::vector<wxString> rotationNames = { wxT(
"0" ), wxT(
"90" ), wxT(
"180" ), wxT(
"270" ) };
342 const int tolerance = 100;
346 for(
size_t r = 0; r < rotations.size(); r++ )
358 BOOST_REQUIRE_MESSAGE( numberBox.has_value() && nameBox.has_value(),
359 "Missing text box for pin " <<
pin->GetNumber() <<
" at rotation "
360 << rotationNames[r] );
366 numberRef = numberClearance;
369 nameRef = nameClearance;
371 BOOST_CHECK_MESSAGE(
std::abs( numberClearance - numberRef ) <= tolerance,
372 "Pin " <<
pin->GetNumber() <<
" number clearance " << numberClearance
373 <<
" at rotation " << rotationNames[r] <<
" differs from " << numberRef
374 <<
" (tolerance " << tolerance <<
")" );
376 BOOST_CHECK_MESSAGE(
std::abs( nameClearance - nameRef ) <= tolerance,
377 "Pin " <<
pin->GetNumber() <<
" name clearance " << nameClearance <<
" at rotation "
378 << rotationNames[r] <<
" differs from " << nameRef <<
" (tolerance "
379 << tolerance <<
")" );
389 auto symbol = std::make_unique<LIB_SYMBOL>( wxT(
"TestNumberOnly" ) );
391 symbol->SetShowPinNames(
false );
392 symbol->SetShowPinNumbers(
true );
395 auto pin1 = std::make_unique<SCH_PIN>( symbol.get() );
399 pin1->SetNumber( wxT(
"12" ) );
403 auto pin2 = std::make_unique<SCH_PIN>( symbol.get() );
407 pin2->SetNumber( wxT(
"[6,7,9-11]" ) );
411 symbol->AddDrawItem(
pin1.release() );
412 symbol->AddDrawItem( pin2.release() );
420 const std::vector<TRANSFORM> rotations = {
427 std::vector<int> gaps;
429 for(
const TRANSFORM& transform : rotations )
435 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> numberInfo = cache.
GetPinNumberInfo( 0 );
437 BOOST_REQUIRE_MESSAGE( numberInfo.has_value(),
"Missing pin number info for pin " << aPin->
GetNumber() );
444 gap =
std::abs( numberInfo->m_TextPosition.y - pinPos.
y );
446 gap =
std::abs( numberInfo->m_TextPosition.x - pinPos.
x );
448 gaps.push_back( gap );
463 std::vector<SCH_PIN*> pins;
465 for(
SCH_ITEM& item : symbol->GetDrawItems() )
468 pins.push_back(
static_cast<SCH_PIN*
>( &item ) );
471 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
474 const int tolerance = 2;
480 int minGap = *std::min_element( gaps.begin(), gaps.end() );
481 int maxGap = *std::max_element( gaps.begin(), gaps.end() );
483 BOOST_CHECK_MESSAGE( ( maxGap - minGap ) <= tolerance,
484 "Pin " <<
pin->GetNumber() <<
" number gap changes with rotation. "
485 <<
"0=" << gaps[0] <<
" 90=" << gaps[1] <<
" 180=" << gaps[2] <<
" 270=" << gaps[3]
486 <<
" (tolerance " << tolerance <<
")" );
496 std::vector<SCH_PIN*> pins;
498 for(
SCH_ITEM& item : symbol->GetDrawItems() )
501 pins.push_back(
static_cast<SCH_PIN*
>( &item ) );
504 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
506 const int tolerance = 2;
512 int minGap = *std::min_element( gaps.begin(), gaps.end() );
513 int maxGap = *std::max_element( gaps.begin(), gaps.end() );
515 BOOST_CHECK_MESSAGE( ( maxGap - minGap ) <= tolerance,
516 "Pin " <<
pin->GetNumber() <<
" number gap changes with rotation. "
517 <<
"0=" << gaps[0] <<
" 90=" << gaps[1] <<
" 180=" << gaps[2] <<
" 270=" << gaps[3]
518 <<
" (tolerance " << tolerance <<
")" );
527 auto symbol = std::make_unique<LIB_SYMBOL>( wxT(
"TestMirror" ) );
529 symbol->SetShowPinNames(
false );
530 symbol->SetShowPinNumbers(
true );
532 auto vertPin = std::make_unique<SCH_PIN>( symbol.get() );
535 vertPin->SetLength(
schIUScale.MilsToIU( 50 ) );
536 vertPin->SetNumber( wxT(
"[8,11,16,19]" ) );
538 vertPin->SetUnit( 1 );
540 auto horizPin = std::make_unique<SCH_PIN>( symbol.get() );
543 horizPin->SetLength(
schIUScale.MilsToIU( 50 ) );
544 horizPin->SetNumber( wxT(
"[1,2,3,4]" ) );
546 horizPin->SetUnit( 1 );
548 auto plainPin = std::make_unique<SCH_PIN>( symbol.get() );
551 plainPin->SetLength(
schIUScale.MilsToIU( 100 ) );
552 plainPin->SetNumber( wxT(
"12" ) );
554 plainPin->SetUnit( 1 );
556 symbol->AddDrawItem( vertPin.release() );
557 symbol->AddDrawItem( horizPin.release() );
558 symbol->AddDrawItem( plainPin.release() );
574 BOOST_REQUIRE_MESSAGE(
info.has_value(),
"Missing pin number info for pin " << aPin->
GetNumber() );
582 auto symbol = std::make_unique<LIB_SYMBOL>( wxT(
"TestFlip" ) );
584 auto pin = std::make_unique<SCH_PIN>( symbol.get() );
586 symbol->AddDrawItem(
pin.release() );
600 const std::vector<FLIP_CASE> cases = {
624 for(
const FLIP_CASE& c : cases )
629 "Orientation " << (
int) c.orient <<
" transform (" << c.transform.x1 <<
","
630 << c.transform.y1 <<
"," << c.transform.x2 <<
"," << c.transform.y2
631 <<
") expected " << c.expected );
648 for(
SCH_ITEM& item : symbol->GetDrawItems() )
655 if(
pin->GetNumber() == wxT(
"[8,11,16,19]" ) )
657 else if(
pin->GetNumber() == wxT(
"[1,2,3,4]" ) )
659 else if(
pin->GetNumber() == wxT(
"12" ) )
674 BOOST_REQUIRE_MESSAGE( base.
m_Text.Contains(
'\n' ),
"Stacked number did not wrap to a block" );
679 BOOST_CHECK_MESSAGE( baseOffset < 0,
"Unmirrored block not left of the vertical pin" );
680 BOOST_CHECK_MESSAGE( mirroredOffset > 0,
"Mirrored block not right of the vertical pin" );
689 BOOST_REQUIRE_MESSAGE( base.
m_Text.Contains(
'\n' ),
"Stacked number did not wrap to a block" );
694 BOOST_CHECK_MESSAGE( baseOffset < 0,
"Unmirrored block not above the horizontal pin" );
695 BOOST_CHECK_MESSAGE( mirroredOffset > 0,
"Mirrored block not below the horizontal pin" );
704 BOOST_REQUIRE_MESSAGE( !base.
m_Text.Contains(
'\n' ),
"Plain number unexpectedly wrapped" );
708 "Single-line number moved off the classic side" );
constexpr EDA_IU_SCALE schIUScale
std::optional< BOX2I > OPT_BOX2I
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr coord_type GetLeft() const
constexpr coord_type GetRight() const
constexpr coord_type GetTop() const
constexpr bool Intersects(const BOX2< Vec > &aRect) const
constexpr coord_type GetBottom() const
Define a library symbol object.
A pin layout helper is a class that manages the layout of the parts of a pin on a schematic symbol:
OPT_BOX2I GetPinNumberBBox()
Get the bounding box of the pin number, if there is one.
std::optional< TEXT_INFO > GetPinNameInfo(int aShadowWidth)
Get the text info for the pin name.
std::optional< TEXT_INFO > GetPinNumberInfo(int aShadowWidth)
OPT_BOX2I GetPinNameBBox()
Get the bounding box of the pin name, if there is one.
Base class for any item which can be embedded within the SCHEMATIC container class,...
PIN_ORIENTATION PinDrawOrient(const TRANSFORM &aTransform) const
Return the pin real orientation (PIN_UP, PIN_DOWN, PIN_RIGHT, PIN_LEFT), according to its orientation...
void SetOrientation(PIN_ORIENTATION aOrientation)
void SetFlipStackedTextSide(bool aFlip)
VECTOR2I GetPosition() const override
bool StackedTextSideFlipped(const TRANSFORM &aTransform) const
Return true when aTransform maps the side the stacked number block is drawn on (above horizontal pins...
const wxString & GetNumber() const
TRANSFORM DefaultTransform
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
PIN_ORIENTATION
The symbol library pin object orientations.
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
@ PIN_RIGHT
The pin extends rightwards from the connection point.
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
static int nearEdgeClearance(const SCH_PIN *aPin, const TRANSFORM &aTransform, const BOX2I &aBox)
static std::unique_ptr< LIB_SYMBOL > createNumberOnlySymbol()
static VECTOR2I getPinLineEnd(const SCH_PIN *pin, const TRANSFORM &transform)
Get pin geometry (line segment from connection point to pin end)
static std::unique_ptr< LIB_SYMBOL > createTestResistorSymbol()
Create a test symbol with stacked pin numbers for rotation testing.
static std::unique_ptr< LIB_SYMBOL > createMirrorTestSymbol()
static PIN_LAYOUT_CACHE::TEXT_INFO numberInfoWithTransform(SCH_PIN *aPin, const TRANSFORM &aTransform)
static std::vector< int > collectNumberGapsPerRotation(SCH_PIN *aPin)
BOOST_AUTO_TEST_CASE(PinNumbersNoOverlapAllRotations)
Test that pin numbers don't overlap with pin geometry across all rotations.
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I