KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_pin_stacked_layout.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, see AUTHORS.txt for contributors.
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 2
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 here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
28
30
31#include <sch_pin.h>
32#include <lib_symbol.h>
33#include <pin_layout_cache.h>
34#include <transform.h>
35#include <sch_io/sch_io_mgr.h>
36
37#include <wx/log.h>
38#include <boost/test/unit_test.hpp>
39
40BOOST_AUTO_TEST_SUITE( PinStackedLayout )
41
42
46{
47 auto symbol = std::make_unique<LIB_SYMBOL>( wxT( "TestResistor" ) );
48
49 // Set pin name offset to 0 so names are positioned outside (like numbers)
50 symbol->SetPinNameOffset( 0 );
51
52 // Create first pin with stacked numbers [1-5]
53 auto pin1 = std::make_unique<SCH_PIN>( symbol.get() );
54 pin1->SetPosition( VECTOR2I( 0, schIUScale.MilsToIU( 250 ) ) ); // top pin
55 pin1->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
56 pin1->SetLength( schIUScale.MilsToIU( 50 ) );
57 pin1->SetNumber( wxT( "[1-5]" ) );
58 pin1->SetName( wxT( "A" ) ); // Short name
59 pin1->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE );
60 pin1->SetUnit( 1 );
61
62 // Create second pin with stacked numbers [6,7,9-11]
63 auto pin2 = std::make_unique<SCH_PIN>( symbol.get() );
64 pin2->SetPosition( VECTOR2I( 0, schIUScale.MilsToIU( -340 ) ) ); // bottom pin
65 pin2->SetOrientation( PIN_ORIENTATION::PIN_UP );
66 pin2->SetLength( schIUScale.MilsToIU( 50 ) );
67 pin2->SetNumber( wxT( "[6,7,9-11]" ) );
68 pin2->SetName( wxT( "B" ) ); // Short name
69 pin2->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE );
70 pin2->SetUnit( 1 );
71
72 // Add pins to symbol
73 symbol->AddDrawItem( pin1.release() );
74 symbol->AddDrawItem( pin2.release() );
75
76 return symbol;
77}
78
82static VECTOR2I getPinLineEnd( const SCH_PIN* pin, const TRANSFORM& transform )
83{
84 VECTOR2I start = pin->GetPosition();
85 VECTOR2I end = start;
86
87 int length = pin->GetLength();
88
89 switch( pin->PinDrawOrient( transform ) )
90 {
92 end.y += length;
93 break;
95 end.y -= length;
96 break;
98 end.x -= length;
99 break;
101 end.x += length;
102 break;
104 default:
105 break;
106 }
107
108 return end;
109}
110
114static bool boxIntersectsLine( const BOX2I& box, const VECTOR2I& lineStart, const VECTOR2I& lineEnd )
115{
116 // Simple bbox vs line segment intersection
117 // First check if line bbox intersects text bbox
118 BOX2I lineBbox;
119 lineBbox.SetOrigin( std::min( lineStart.x, lineEnd.x ), std::min( lineStart.y, lineEnd.y ) );
120 lineBbox.SetEnd( std::max( lineStart.x, lineEnd.x ), std::max( lineStart.y, lineEnd.y ) );
121
122 if( !lineBbox.Intersects( box ) )
123 return false;
124
125 // For vertical/horizontal lines, do precise check
126 if( lineStart.x == lineEnd.x ) // vertical line
127 {
128 int lineX = lineStart.x;
129 return ( lineX >= box.GetLeft() && lineX <= box.GetRight() &&
130 box.GetTop() <= std::max( lineStart.y, lineEnd.y ) &&
131 box.GetBottom() >= std::min( lineStart.y, lineEnd.y ) );
132 }
133 else if( lineStart.y == lineEnd.y ) // horizontal line
134 {
135 int lineY = lineStart.y;
136 return ( lineY >= box.GetBottom() && lineY <= box.GetTop() &&
137 box.GetLeft() <= std::max( lineStart.x, lineEnd.x ) &&
138 box.GetRight() >= std::min( lineStart.x, lineEnd.x ) );
139 }
140
141 // For diagonal lines, use the bbox intersection as approximation
142 return true;
143}
144
148BOOST_AUTO_TEST_CASE( PinNumbersNoOverlapAllRotations )
149{
150 // Create test symbol
151 std::unique_ptr<LIB_SYMBOL> symbol = createTestResistorSymbol();
152 BOOST_REQUIRE( symbol );
153
154 // Get the pins
155 std::vector<SCH_PIN*> pins;
156
157 for( SCH_ITEM& item : symbol->GetDrawItems() )
158 {
159 if( item.Type() == SCH_PIN_T )
160 pins.push_back( static_cast<SCH_PIN*>( &item ) );
161 }
162
163 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
164
165 // Test rotations: 0°, 90°, 180°, 270°
166 std::vector<TRANSFORM> rotations = {
167 TRANSFORM( 1, 0, 0, 1 ), // 0° (identity)
168 TRANSFORM( 0, -1, 1, 0 ), // 90° CCW
169 TRANSFORM( -1, 0, 0, -1 ), // 180°
170 TRANSFORM( 0, 1, -1, 0 ) // 270° CCW (90° CW)
171 };
172
173 std::vector<wxString> rotationNames = { wxT("0°"), wxT("90°"), wxT("180°"), wxT("270°") };
174
175 for( size_t r = 0; r < rotations.size(); r++ )
176 {
177 const TRANSFORM& transform = rotations[r];
178 const wxString& rotName = rotationNames[r];
179
180 // Set global transform for this test
181 TRANSFORM oldTransform = DefaultTransform;
182 DefaultTransform = transform;
183
184 for( size_t p = 0; p < pins.size(); p++ )
185 {
186 SCH_PIN* pin = pins[p];
187
188 // Create layout cache for this pin
189 PIN_LAYOUT_CACHE cache( *pin );
190
191 // Get pin number text info (shadow width 0 for testing)
192 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> numberInfoOpt = cache.GetPinNumberInfo( 0 );
193
194 if( !numberInfoOpt.has_value() )
195 continue;
196
197 const PIN_LAYOUT_CACHE::TEXT_INFO& numberInfo = numberInfoOpt.value();
198
199 if( numberInfo.m_Text.IsEmpty() )
200 continue;
201
202 // Get pin line geometry
203 VECTOR2I pinStart = pin->GetPosition();
204 VECTOR2I pinEnd = getPinLineEnd( pin, transform );
205
206 // Get text bounding box - we need to estimate this since we don't have full font rendering
207 // For now, use a simple estimation based on text size and string length
208 int textHeight = numberInfo.m_TextSize;
209 int textWidth = numberInfo.m_Text.Length() * numberInfo.m_TextSize * 0.6; // rough char width
210
211 // Handle multi-line text
212 if( numberInfo.m_Text.Contains( '\n' ) )
213 {
214 wxArrayString lines;
215 wxStringSplit( numberInfo.m_Text, lines, '\n' );
216
217 if( numberInfo.m_Angle == ANGLE_VERTICAL )
218 {
219 // For vertical text, lines are spaced horizontally
220 int lineSpacing = textHeight * 1.3;
221 textWidth = lines.size() * lineSpacing;
222 // Find longest line for height
223 size_t maxLen = 0;
224
225 for( const wxString& line : lines )
226 maxLen = std::max( maxLen, line.Length() );
227
228 textHeight = maxLen * textHeight * 0.6;
229 }
230 else
231 {
232 // For horizontal text, lines are spaced vertically
233 int lineSpacing = textHeight * 1.3;
234 textHeight = lines.size() * lineSpacing;
235 // Find longest line for width
236 size_t maxLen = 0;
237
238 for( const wxString& line : lines )
239 maxLen = std::max( maxLen, line.Length() );
240
241 textWidth = maxLen * textHeight * 0.6;
242 }
243 }
244
245 // Create text bounding box around text position
246 BOX2I textBbox;
247 textBbox.SetOrigin( numberInfo.m_TextPosition.x - textWidth/2,
248 numberInfo.m_TextPosition.y - textHeight/2 );
249 textBbox.SetSize( textWidth, textHeight );
250
251 // Check for intersection
252 bool overlaps = boxIntersectsLine( textBbox, pinStart, pinEnd );
253
254 // Log detailed info for debugging
255 wxLogMessage( wxT("Rotation %s, Pin %s: pos=(%d,%d) textPos=(%d,%d) pinLine=(%d,%d)-(%d,%d) textBox=(%d,%d,%dx%d) overlap=%s"),
256 rotName, pin->GetNumber(),
257 pinStart.x, pinStart.y,
258 numberInfo.m_TextPosition.x, numberInfo.m_TextPosition.y,
259 pinStart.x, pinStart.y, pinEnd.x, pinEnd.y,
260 (int)textBbox.GetLeft(), (int)textBbox.GetTop(), (int)textBbox.GetWidth(), (int)textBbox.GetHeight(),
261 overlaps ? wxT("YES") : wxT("NO") );
262
263 // Test assertion
264 BOOST_CHECK_MESSAGE( !overlaps,
265 "Pin number '" << pin->GetNumber() << "' overlaps with pin geometry at rotation " << rotName );
266 }
267
268 // Restore original transform
269 DefaultTransform = oldTransform;
270 }
271}
272
277BOOST_AUTO_TEST_CASE( PinTextConsistentSidePlacement )
278{
279 // Create test symbol with both types of pins
280 std::unique_ptr<LIB_SYMBOL> symbol = createTestResistorSymbol();
281 BOOST_REQUIRE( symbol );
282
283 // Get the pins - one will be multiline formatted, one will not
284 std::vector<SCH_PIN*> pins;
285
286 for( SCH_ITEM& item : symbol->GetDrawItems() )
287 {
288 if( item.Type() == SCH_PIN_T )
289 pins.push_back( static_cast<SCH_PIN*>( &item ) );
290 }
291
292 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
293
294 // Test rotations
295 std::vector<TRANSFORM> rotations = {
296 TRANSFORM( 1, 0, 0, 1 ), // 0° (identity)
297 TRANSFORM( 0, -1, 1, 0 ), // 90° CCW
298 TRANSFORM( -1, 0, 0, -1 ), // 180°
299 TRANSFORM( 0, 1, -1, 0 ) // 270° CCW (90° CW)
300 };
301
302 std::vector<wxString> rotationNames = { wxT("0°"), wxT("90°"), wxT("180°"), wxT("270°") };
303
304 for( size_t r = 0; r < rotations.size(); r++ )
305 {
306 const TRANSFORM& transform = rotations[r];
307 const wxString& rotName = rotationNames[r];
308
309 // Set global transform for this test
310 TRANSFORM oldTransform = DefaultTransform;
311 DefaultTransform = transform;
312
313 // For each rotation, collect pin number and name positions relative to pin center
314 struct PinTextInfo {
315 VECTOR2I pinPos;
316 VECTOR2I numberPos;
317 VECTOR2I namePos;
318 wxString pinNumber;
319 bool isMultiline;
320 };
321
322 std::vector<PinTextInfo> pinInfos;
323
324 for( SCH_PIN* pin : pins )
325 {
326 PinTextInfo info;
327 info.pinPos = pin->GetPosition();
328 info.pinNumber = pin->GetNumber();
329
330 // Create layout cache for this pin
331 PIN_LAYOUT_CACHE cache( *pin );
332
333 // Get number position (shadow width 0 for testing)
334 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> numberInfoOpt = cache.GetPinNumberInfo( 0 );
335
336 if( numberInfoOpt.has_value() )
337 {
338 const PIN_LAYOUT_CACHE::TEXT_INFO& numberInfo = numberInfoOpt.value();
339 info.numberPos = numberInfo.m_TextPosition;
340 info.isMultiline = numberInfo.m_Text.Contains( '\n' );
341 }
342
343 // Get name position
344 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> nameInfoOpt = cache.GetPinNameInfo( 0 );
345
346 if( nameInfoOpt.has_value() )
347 {
348 const PIN_LAYOUT_CACHE::TEXT_INFO& nameInfo = nameInfoOpt.value();
349 info.namePos = nameInfo.m_TextPosition;
350 }
351
352 pinInfos.push_back( info );
353
354 wxLogDebug( "Rotation %s, Pin %s: pos=(%d,%d) numberPos=(%d,%d) namePos=(%d,%d) multiline=%s",
355 rotName, info.pinNumber,
356 info.pinPos.x, info.pinPos.y,
357 info.numberPos.x, info.numberPos.y,
358 info.namePos.x, info.namePos.y,
359 info.isMultiline ? wxT("YES") : wxT("NO") );
360 }
361
362 BOOST_REQUIRE_EQUAL( pinInfos.size(), 2 );
363
364 // New semantics:
365 // * Vertical pins (UP/DOWN): numbers and names must be LEFT (x < pin.x)
366 // * Horizontal pins (LEFT/RIGHT): numbers/names must be ABOVE (y < pin.y)
367 PIN_ORIENTATION orient = pins[0]->PinDrawOrient( DefaultTransform );
368
369 if( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN )
370 {
371 for( const PinTextInfo& inf : pinInfos )
372 {
373 BOOST_CHECK_MESSAGE( inf.numberPos.x < inf.pinPos.x,
374 "At rotation " << rotName << ", number for pin " << inf.pinNumber << " not left of vertical pin." );
375 BOOST_CHECK_MESSAGE( inf.namePos.x < inf.pinPos.x,
376 "At rotation " << rotName << ", name for pin " << inf.pinNumber << " not left of vertical pin." );
377 }
378 }
379 else if( orient == PIN_ORIENTATION::PIN_LEFT || orient == PIN_ORIENTATION::PIN_RIGHT )
380 {
381 for( const PinTextInfo& inf : pinInfos )
382 {
383 BOOST_CHECK_MESSAGE( inf.numberPos.y < inf.pinPos.y,
384 "At rotation " << rotName << ", number for pin " << inf.pinNumber << " not above horizontal pin." );
385 BOOST_CHECK_MESSAGE( inf.namePos.y < inf.pinPos.y,
386 "At rotation " << rotName << ", name for pin " << inf.pinNumber << " not above horizontal pin." );
387 }
388 }
389
390 // Restore original transform
391 DefaultTransform = oldTransform;
392 }
393}
394
399BOOST_AUTO_TEST_CASE( PinTextSameBottomCoordinate )
400{
401 // Create test symbol with both types of pins
402 std::unique_ptr<LIB_SYMBOL> symbol = createTestResistorSymbol();
403 BOOST_REQUIRE( symbol );
404
405 // Get the pins - one will be multiline formatted, one will not
406 std::vector<SCH_PIN*> pins;
407
408 for( SCH_ITEM& item : symbol->GetDrawItems() )
409 {
410 if( item.Type() == SCH_PIN_T )
411 pins.push_back( static_cast<SCH_PIN*>( &item ) );
412 }
413
414 BOOST_REQUIRE_EQUAL( pins.size(), 2 );
415
416 // Test rotations
417 std::vector<TRANSFORM> rotations = {
418 TRANSFORM( 1, 0, 0, 1 ), // 0° (identity)
419 TRANSFORM( 0, -1, 1, 0 ), // 90° CCW
420 TRANSFORM( -1, 0, 0, -1 ), // 180°
421 TRANSFORM( 0, 1, -1, 0 ) // 270° CCW (90° CW)
422 };
423
424 std::vector<wxString> rotationNames = { wxT("0°"), wxT("90°"), wxT("180°"), wxT("270°") };
425
426 for( size_t r = 0; r < rotations.size(); r++ )
427 {
428 const TRANSFORM& transform = rotations[r];
429 const wxString& rotName = rotationNames[r];
430
431 // Set global transform for this test
432 TRANSFORM oldTransform = DefaultTransform;
433 DefaultTransform = transform;
434
435 // For each rotation, collect pin and text position data
436 struct PinTextData {
437 VECTOR2I pinPos;
438 VECTOR2I numberPos;
439 VECTOR2I namePos;
440 wxString pinNumber;
441 bool isMultiline;
442 int numberBottomDistance;
443 int nameBottomDistance;
444 };
445
446 std::vector<PinTextData> pinData;
447
448 for( SCH_PIN* pin : pins )
449 {
450 PinTextData data;
451 data.pinPos = pin->GetPosition();
452 data.pinNumber = pin->GetNumber();
453
454 // Create layout cache for this pin
455 PIN_LAYOUT_CACHE cache( *pin );
456
457 // Get number position (shadow width 0 for testing)
458 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> numberInfoOpt = cache.GetPinNumberInfo( 0 );
459 PIN_LAYOUT_CACHE::TEXT_INFO numberInfo; // store for later heuristics
460
461 if( numberInfoOpt.has_value() )
462 {
463 numberInfo = numberInfoOpt.value();
464 data.numberPos = numberInfo.m_TextPosition;
465 data.isMultiline = numberInfo.m_Text.Contains( '\n' );
466 }
467 else
468 {
469 BOOST_FAIL( "Expected pin number text info" );
470 }
471
472 // Get name position
473 std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> nameInfoOpt = cache.GetPinNameInfo( 0 );
474 PIN_LAYOUT_CACHE::TEXT_INFO nameInfo; // store for width/height heuristic
475
476 if( nameInfoOpt.has_value() )
477 {
478 nameInfo = nameInfoOpt.value();
479 data.namePos = nameInfo.m_TextPosition;
480 }
481 else
482 {
483 BOOST_FAIL( "Expected pin name text info" );
484 }
485
486 // Calculate bottom distance (closest distance to pin along pin-text axis)
487 PIN_ORIENTATION orient = pin->PinDrawOrient( DefaultTransform );
488
489 if( orient == PIN_ORIENTATION::PIN_UP || orient == PIN_ORIENTATION::PIN_DOWN )
490 {
491 // Vertical pins: measure clearance from pin (at pin.x) to RIGHT edge of text box.
492 // We approximate half width from text length heuristic.
493 int textWidth = data.isMultiline ? 0 : (int)( data.pinNumber.Length() * numberInfo.m_TextSize * 0.6 );
494
495 // (Multiline case: numberInfo.m_Text already contains \n; heuristic in earlier section)
496 if( data.isMultiline )
497 {
498 wxArrayString lines; wxStringSplit( numberInfo.m_Text, lines, '\n' );
499 int lineSpacing = numberInfo.m_TextSize * 1.3;
500 textWidth = lines.size() * lineSpacing; // when vertical orientation text is rotated
501 }
502
503 int rightEdge = data.numberPos.x + textWidth / 2;
504 data.numberBottomDistance = data.pinPos.x - rightEdge; // positive gap
505 int nameWidth = (int)( nameInfo.m_Text.Length() * nameInfo.m_TextSize * 0.6 );
506 int nameRightEdge = data.namePos.x + nameWidth / 2;
507 data.nameBottomDistance = data.pinPos.x - nameRightEdge; // expect similar across pins
508 }
509 else
510 {
511 // Horizontal pins: we align centers at a fixed offset above the pin. Measure center gap.
512 data.numberBottomDistance = data.pinPos.y - data.numberPos.y; // center gap constant
513 data.nameBottomDistance = data.pinPos.y - data.namePos.y; // center gap for names
514 }
515
516 pinData.push_back( data );
517
518 wxLogDebug( "Rotation %s, Pin %s: pos=(%d,%d) numberPos=(%d,%d) namePos=(%d,%d) multiline=%s numberBottomDist=%d nameBottomDist=%d",
519 rotName, data.pinNumber,
520 data.pinPos.x, data.pinPos.y,
521 data.numberPos.x, data.numberPos.y,
522 data.namePos.x, data.namePos.y,
523 data.isMultiline ? wxT("YES") : wxT("NO"),
524 data.numberBottomDistance, data.nameBottomDistance );
525 }
526
527 BOOST_REQUIRE_EQUAL( pinData.size(), 2 );
528
529 // Check that both pins have their numbers at the same bottom distance from pin
530 // Allow small tolerance for rounding differences
531 const int tolerance = 100; // 100 internal units tolerance
532
533 int bottomDist1 = pinData[0].numberBottomDistance;
534 int bottomDist2 = pinData[1].numberBottomDistance;
535 int distanceDiff = abs( bottomDist1 - bottomDist2 );
536
537 BOOST_CHECK_MESSAGE( distanceDiff <= tolerance,
538 "At rotation " << rotName << ", pin numbers have different bottom distances from pin. "
539 << "Pin " << pinData[0].pinNumber << " distance=" << bottomDist1
540 << ", Pin " << pinData[1].pinNumber << " distance=" << bottomDist2
541 << ", difference=" << distanceDiff << " (tolerance=" << tolerance << ")" );
542
543 // Check that both pins have their names at the same bottom distance from pin
544 int nameBottomDist1 = pinData[0].nameBottomDistance;
545 int nameBottomDist2 = pinData[1].nameBottomDistance;
546 int nameDistanceDiff = abs( nameBottomDist1 - nameBottomDist2 );
547
548 BOOST_CHECK_MESSAGE( nameDistanceDiff <= tolerance,
549 "At rotation " << rotName << ", pin names have different bottom distances from pin. "
550 << "Pin " << pinData[0].pinNumber << " name distance=" << nameBottomDist1
551 << ", Pin " << pinData[1].pinNumber << " name distance=" << nameBottomDist2
552 << ", difference=" << nameDistanceDiff << " (tolerance=" << tolerance << ")" );
553
554 // Restore original transform
555 DefaultTransform = oldTransform;
556 }
557}
558
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:237
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr void SetSize(const SizeVec &size)
Definition box2.h:248
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:297
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
constexpr coord_type GetBottom() const
Definition box2.h:222
Define a library symbol object.
Definition lib_symbol.h:85
A pin layout helper is a class that manages the layout of the parts of a pin on a schematic symbol:
std::optional< TEXT_INFO > GetPinNameInfo(int aShadowWidth)
Get the text info for the pin name.
std::optional< TEXT_INFO > GetPinNumberInfo(int aShadowWidth)
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:167
for transforming drawing coordinates for a wxDC device context.
Definition transform.h:46
STL class.
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
TRANSFORM DefaultTransform
Definition transform.cpp:32
STL namespace.
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:43
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
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
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.
BOOST_AUTO_TEST_CASE(PinNumbersNoOverlapAllRotations)
Test that pin numbers don't overlap with pin geometry across all rotations.
static bool boxIntersectsLine(const BOX2I &box, const VECTOR2I &lineStart, const VECTOR2I &lineEnd)
Check if a box intersects with a line segment.
VECTOR2I end
@ SCH_PIN_T
Definition typeinfo.h:155
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695