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