KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pads_sch_symbol_builder.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 (C) 2025 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 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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21
22#include <lib_symbol.h>
23#include <sch_shape.h>
24#include <sch_pin.h>
25#include <sch_text.h>
26#include <pin_type.h>
27#include <layer_ids.h>
28#include <sch_screen.h>
29#include <stroke_params.h>
30
31#include <advanced_config.h>
32#include <io/pads/pads_common.h>
33
34#include <algorithm>
35#include <cctype>
36#include <cmath>
37
38
39namespace PADS_SCH
40{
41
46
47
51
52
53int PADS_SCH_SYMBOL_BUILDER::toKiCadUnits( double aPadsValue ) const
54{
55 // Convert from PADS units to KiCad internal units (nanometers)
56 // PADS uses mils by default, KiCad schematic uses schIUScale.MilsToIU()
57
58 double milsValue = aPadsValue;
59
60 switch( m_params.units )
61 {
62 case UNIT_TYPE::MILS:
63 milsValue = aPadsValue;
64 break;
65
67 // mm to mils: 1 mm = 39.37 mils
68 milsValue = aPadsValue * 39.3701;
69 break;
70
72 // inches to mils
73 milsValue = aPadsValue * 1000.0;
74 break;
75 }
76
77 return schIUScale.MilsToIU( milsValue );
78}
79
80
82{
83 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxString::FromUTF8( aSymbolDef.name ) );
84
85 // Add graphics
86 for( const auto& graphic : aSymbolDef.graphics )
87 {
88 std::vector<SCH_SHAPE*> shapes = createShapes( graphic );
89
90 for( SCH_SHAPE* shape : shapes )
91 libSymbol->AddDrawItem( shape );
92 }
93
94 // Add pins
95 for( const auto& pin : aSymbolDef.pins )
96 {
97 SCH_PIN* schPin = createPin( pin, libSymbol );
98
99 if( schPin )
100 libSymbol->AddDrawItem( schPin );
101 }
102
103 // Add embedded text labels
104 for( const auto& text : aSymbolDef.texts )
105 {
106 if( text.content.empty() )
107 continue;
108
109 SCH_TEXT* schText = new SCH_TEXT(
110 VECTOR2I( toKiCadUnits( text.position.x ), -toKiCadUnits( text.position.y ) ),
111 wxString::FromUTF8( text.content ), LAYER_DEVICE );
112
113 if( text.size > 0.0 )
114 {
115 int scaledSize = toKiCadUnits( text.size );
116 int charHeight = static_cast<int>(
118 int charWidth = static_cast<int>(
120 schText->SetTextSize( VECTOR2I( charWidth, charHeight ) );
121 }
122
123 if( text.rotation != 0.0 )
124 schText->SetTextAngleDegrees( text.rotation );
125
126 libSymbol->AddDrawItem( schText );
127 }
128
129 libSymbol->SetShowPinNumbers( false );
130 libSymbol->SetShowPinNames( false );
131
132 return libSymbol;
133}
134
135
137{
138 auto it = m_symbolCache.find( aSymbolDef.name );
139
140 if( it != m_symbolCache.end() )
141 return it->second.get();
142
143 LIB_SYMBOL* newSymbol = BuildSymbol( aSymbolDef );
144 m_symbolCache[aSymbolDef.name] = std::unique_ptr<LIB_SYMBOL>( newSymbol );
145
146 return newSymbol;
147}
148
149
151 const PARTTYPE_DEF& aPartType, const std::vector<SYMBOL_DEF>& aSymbolDefs )
152{
153 // Build a lookup from CAEDECAL name to definition
154 std::map<std::string, const SYMBOL_DEF*> symDefByName;
155
156 for( const auto& sd : aSymbolDefs )
157 symDefByName[sd.name] = &sd;
158
159 int gateCount = static_cast<int>( aPartType.gates.size() );
160 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxString::FromUTF8( aPartType.name ) );
161 libSymbol->SetUnitCount( gateCount, false );
162 libSymbol->LockUnits( true );
163
164 for( int gi = 0; gi < gateCount; gi++ )
165 {
166 const GATE_DEF& gate = aPartType.gates[gi];
167 int unit = gi + 1;
168
169 // Resolve the CAEDECAL for this gate
170 std::string decalName;
171
172 if( !gate.decal_names.empty() )
173 decalName = gate.decal_names[0];
174
175 auto sdIt = symDefByName.find( decalName );
176
177 if( sdIt == symDefByName.end() )
178 continue;
179
180 const SYMBOL_DEF& symDef = *sdIt->second;
181
182 // Add graphics for this unit
183 for( const auto& graphic : symDef.graphics )
184 {
185 std::vector<SCH_SHAPE*> shapes = createShapes( graphic );
186
187 for( SCH_SHAPE* shape : shapes )
188 {
189 shape->SetUnit( unit );
190 libSymbol->AddDrawItem( shape );
191 }
192 }
193
194 // Add pins with PARTTYPE overrides
195 for( size_t p = 0; p < symDef.pins.size(); p++ )
196 {
197 SYMBOL_PIN pin = symDef.pins[p];
198
199 if( p < gate.pins.size() )
200 {
201 pin.name = gate.pins[p].pin_name;
202 pin.number = gate.pins[p].pin_id;
203
204 if( gate.pins[p].pin_type != 0 )
205 pin.type = PADS_SCH_PARSER::ParsePinTypeChar( gate.pins[p].pin_type );
206 }
207
208 SCH_PIN* schPin = createPin( pin, libSymbol );
209
210 if( schPin )
211 {
212 schPin->SetUnit( unit );
213 libSymbol->AddDrawItem( schPin );
214 }
215 }
216
217 // Add embedded text labels for this unit
218 for( const auto& text : symDef.texts )
219 {
220 if( text.content.empty() )
221 continue;
222
223 SCH_TEXT* schText = new SCH_TEXT(
224 VECTOR2I( toKiCadUnits( text.position.x ),
225 -toKiCadUnits( text.position.y ) ),
226 wxString::FromUTF8( text.content ), LAYER_DEVICE );
227
228 if( text.size > 0.0 )
229 {
230 int scaledSize = toKiCadUnits( text.size );
231 int charHeight = static_cast<int>(
233 int charWidth = static_cast<int>(
235 schText->SetTextSize( VECTOR2I( charWidth, charHeight ) );
236 }
237
238 if( text.rotation != 0.0 )
239 schText->SetTextAngleDegrees( text.rotation );
240
241 schText->SetUnit( unit );
242 libSymbol->AddDrawItem( schText );
243 }
244 }
245
246 libSymbol->SetShowPinNumbers( true );
247 libSymbol->SetShowPinNames( true );
248
249 return libSymbol;
250}
251
252
254 const PARTTYPE_DEF& aPartType, const std::vector<SYMBOL_DEF>& aSymbolDefs )
255{
256 auto it = m_symbolCache.find( aPartType.name );
257
258 if( it != m_symbolCache.end() )
259 return it->second.get();
260
261 LIB_SYMBOL* newSymbol = BuildMultiUnitSymbol( aPartType, aSymbolDefs );
262 m_symbolCache[aPartType.name] = std::unique_ptr<LIB_SYMBOL>( newSymbol );
263
264 return newSymbol;
265}
266
267
269 const PARTTYPE_DEF& aPartType, const SYMBOL_DEF& aSymbolDef )
270{
271 // Cache by PARTTYPE + CAEDECAL pair. A single-gate PARTTYPE with multiple decal
272 // variants (e.g. horizontal vs vertical resistor) needs a separate LIB_SYMBOL per
273 // variant because the graphics and pin positions differ.
274 std::string cacheKey = aPartType.name + ":" + aSymbolDef.name;
275 auto it = m_symbolCache.find( cacheKey );
276
277 if( it != m_symbolCache.end() )
278 return it->second.get();
279
280 if( aPartType.gates.empty() )
281 return nullptr;
282
283 // Build from the CAEDECAL then apply pin overrides from the PARTTYPE gate
284 LIB_SYMBOL* libSymbol = new LIB_SYMBOL( wxString::FromUTF8( aSymbolDef.name ) );
285
286 for( const auto& graphic : aSymbolDef.graphics )
287 {
288 std::vector<SCH_SHAPE*> shapes = createShapes( graphic );
289
290 for( SCH_SHAPE* shape : shapes )
291 libSymbol->AddDrawItem( shape );
292 }
293
294 const GATE_DEF& gate = aPartType.gates[0];
295
296 for( size_t p = 0; p < aSymbolDef.pins.size(); p++ )
297 {
298 SYMBOL_PIN pin = aSymbolDef.pins[p];
299
300 if( p < gate.pins.size() )
301 {
302 pin.name = gate.pins[p].pin_name;
303 pin.number = gate.pins[p].pin_id;
304
305 if( gate.pins[p].pin_type != 0 )
306 pin.type = PADS_SCH_PARSER::ParsePinTypeChar( gate.pins[p].pin_type );
307 }
308
309 SCH_PIN* schPin = createPin( pin, libSymbol );
310
311 if( schPin )
312 libSymbol->AddDrawItem( schPin );
313 }
314
315 for( const auto& text : aSymbolDef.texts )
316 {
317 if( text.content.empty() )
318 continue;
319
320 SCH_TEXT* schText = new SCH_TEXT(
321 VECTOR2I( toKiCadUnits( text.position.x ),
322 -toKiCadUnits( text.position.y ) ),
323 wxString::FromUTF8( text.content ), LAYER_DEVICE );
324
325 if( text.size > 0.0 )
326 {
327 int scaledSize = toKiCadUnits( text.size );
328 int charHeight = static_cast<int>(
330 int charWidth = static_cast<int>(
332 schText->SetTextSize( VECTOR2I( charWidth, charHeight ) );
333 }
334
335 if( text.rotation != 0.0 )
336 schText->SetTextAngleDegrees( text.rotation );
337
338 libSymbol->AddDrawItem( schText );
339 }
340
341 // Show pin names/numbers if any gate pin has an explicit name
342 bool hasPinNames = false;
343
344 for( const auto& pin : gate.pins )
345 {
346 if( !pin.pin_name.empty() )
347 {
348 hasPinNames = true;
349 break;
350 }
351 }
352
353 libSymbol->SetShowPinNumbers( hasPinNames );
354 libSymbol->SetShowPinNames( hasPinNames );
355
356 m_symbolCache[cacheKey] = std::unique_ptr<LIB_SYMBOL>( libSymbol );
357
358 return libSymbol;
359}
360
361
362bool PADS_SCH_SYMBOL_BUILDER::HasSymbol( const std::string& aName ) const
363{
364 return m_symbolCache.find( aName ) != m_symbolCache.end();
365}
366
367
368LIB_SYMBOL* PADS_SCH_SYMBOL_BUILDER::GetSymbol( const std::string& aName ) const
369{
370 auto it = m_symbolCache.find( aName );
371
372 if( it != m_symbolCache.end() )
373 return it->second.get();
374
375 return nullptr;
376}
377
378
380{
381 SCH_SHAPE* shape = nullptr;
382
383 switch( aGraphic.type )
384 {
387 {
388 bool hasArcs = false;
389
390 for( const auto& pt : aGraphic.points )
391 {
392 if( pt.arc.has_value() )
393 {
394 hasArcs = true;
395 break;
396 }
397 }
398
399 if( !hasArcs )
400 {
401 shape = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
402
403 for( const auto& pt : aGraphic.points )
404 shape->AddPoint( VECTOR2I( toKiCadUnits( pt.coord.x ),
405 -toKiCadUnits( pt.coord.y ) ) );
406 }
407 else
408 {
409 // Mixed line/arc path requires multiple shapes. Return nullptr here and let
410 // BuildSymbol handle this via createShapes() instead.
411 return nullptr;
412 }
413
414 break;
415 }
416
418 {
420
421 if( aGraphic.points.size() >= 2 )
422 {
423 VECTOR2I start( toKiCadUnits( aGraphic.points[0].coord.x ),
424 -toKiCadUnits( aGraphic.points[0].coord.y ) );
425 VECTOR2I end( toKiCadUnits( aGraphic.points[1].coord.x ),
426 -toKiCadUnits( aGraphic.points[1].coord.y ) );
427
428 shape->SetStart( start );
429 shape->SetEnd( end );
430 }
431
432 break;
433 }
434
436 {
437 shape = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
438
439 VECTOR2I center( toKiCadUnits( aGraphic.center.x ), -toKiCadUnits( aGraphic.center.y ) );
440 int radius = toKiCadUnits( aGraphic.radius );
441
442 shape->SetStart( center );
443 shape->SetEnd( VECTOR2I( center.x + radius, center.y ) );
444
445 break;
446 }
447
449 {
450 shape = new SCH_SHAPE( SHAPE_T::ARC, LAYER_DEVICE );
451
452 VECTOR2I center( toKiCadUnits( aGraphic.center.x ), -toKiCadUnits( aGraphic.center.y ) );
453 int radius = toKiCadUnits( aGraphic.radius );
454
455 // Convert angles from PADS format to KiCad
456 // PADS uses degrees, KiCad uses tenths of degrees for arc definition
457 double startAngle = aGraphic.start_angle * M_PI / 180.0;
458 double endAngle = aGraphic.end_angle * M_PI / 180.0;
459
460 VECTOR2I startPt( center.x + radius * cos( startAngle ),
461 center.y - radius * sin( startAngle ) );
462 VECTOR2I endPt( center.x + radius * cos( endAngle ),
463 center.y - radius * sin( endAngle ) );
464
465 shape->SetStart( startPt );
466 shape->SetEnd( endPt );
467 shape->SetCenter( center );
468
469 break;
470 }
471 }
472
473 if( shape )
474 {
475 int lineWidth = toKiCadUnits( aGraphic.line_width );
476
477 if( lineWidth == 0 )
478 lineWidth = toKiCadUnits( m_params.line_width );
479
480 shape->SetStroke( STROKE_PARAMS( lineWidth, PADS_COMMON::PadsLineStyleToKiCad( aGraphic.line_style ) ) );
481
482 if( aGraphic.filled )
484 }
485
486 return shape;
487}
488
489
490std::vector<SCH_SHAPE*> PADS_SCH_SYMBOL_BUILDER::createShapes( const SYMBOL_GRAPHIC& aGraphic )
491{
492 std::vector<SCH_SHAPE*> result;
493
494 // Try the simple single-shape path first
495 SCH_SHAPE* single = createShape( aGraphic );
496
497 if( single )
498 {
499 result.push_back( single );
500 return result;
501 }
502
503 // Mixed line/arc path: emit individual segments
504 int lineWidth = toKiCadUnits( aGraphic.line_width );
505
506 if( lineWidth == 0 )
507 lineWidth = toKiCadUnits( m_params.line_width );
508
510
511 for( size_t i = 0; i + 1 < aGraphic.points.size(); i++ )
512 {
513 const GRAPHIC_POINT& cur = aGraphic.points[i];
514 const GRAPHIC_POINT& next = aGraphic.points[i + 1];
515
516 VECTOR2I startPt( toKiCadUnits( cur.coord.x ), -toKiCadUnits( cur.coord.y ) );
517 VECTOR2I endPt( toKiCadUnits( next.coord.x ), -toKiCadUnits( next.coord.y ) );
518
519 if( cur.arc.has_value() )
520 {
521 const ARC_DATA& ad = *cur.arc;
522 double cx = ( ad.bbox_x1 + ad.bbox_x2 ) / 2.0;
523 double cy = ( ad.bbox_y1 + ad.bbox_y2 ) / 2.0;
524 VECTOR2I center( toKiCadUnits( cx ), -toKiCadUnits( cy ) );
525
526 // Compute the arc midpoint on the circle between start and end.
527 // Use vector math: midpoint of arc = center + R * normalize(midvector)
528 // where midvector = (start - center) + (end - center)
529 double sx = startPt.x - center.x;
530 double sy = startPt.y - center.y;
531 double ex = endPt.x - center.x;
532 double ey = endPt.y - center.y;
533 double radius = std::sqrt( sx * sx + sy * sy );
534
535 double mx = sx + ex;
536 double my = sy + ey;
537 double mlen = std::sqrt( mx * mx + my * my );
538
539 VECTOR2I midPt;
540
541 if( mlen > 0.001 )
542 {
543 midPt.x = center.x + static_cast<int>( radius * mx / mlen );
544 midPt.y = center.y + static_cast<int>( radius * my / mlen );
545 }
546 else
547 {
548 // Start and end are diametrically opposite, pick perpendicular direction
549 midPt.x = center.x + static_cast<int>( -sy * radius / std::max( radius, 1.0 ) );
550 midPt.y = center.y + static_cast<int>( sx * radius / std::max( radius, 1.0 ) );
551 }
552
553 // The initial midpoint is always on the minor arc side (between start
554 // and end radii). Flip to the major arc side when the sweep exceeds
555 // 180 degrees. The sign of the angle encodes CW/CCW direction in PADS
556 // but does not affect which semicircle the arc occupies.
557 if( std::abs( ad.angle ) > 1800 )
558 {
559 midPt.x = 2 * center.x - midPt.x;
560 midPt.y = 2 * center.y - midPt.y;
561 }
562
564 arc->SetArcGeometry( startPt, midPt, endPt );
565 arc->SetStroke( STROKE_PARAMS( lineWidth, lineStyle ) );
566
567 if( aGraphic.filled )
569
570 result.push_back( arc );
571 }
572 else
573 {
574 if( startPt == endPt )
575 continue;
576
578 line->AddPoint( startPt );
579 line->AddPoint( endPt );
580 line->SetStroke( STROKE_PARAMS( lineWidth, lineStyle ) );
581
582 if( aGraphic.filled )
584
585 result.push_back( line );
586 }
587 }
588
589 return result;
590}
591
592
594{
595 SCH_PIN* pin = new SCH_PIN( aParent );
596
597 // Set pin name and number
598 pin->SetName( wxString::FromUTF8( aPin.name ) );
599 pin->SetNumber( wxString::FromUTF8( aPin.number ) );
600
601 // Set pin position (end point where wire connects)
602 VECTOR2I pos( toKiCadUnits( aPin.position.x ), -toKiCadUnits( aPin.position.y ) );
603 pin->SetPosition( pos );
604
605 // Set pin length
606 int length = toKiCadUnits( aPin.length );
607 pin->SetLength( length );
608
609 // Determine pin orientation from the T-line angle and side fields.
610 // The angle indicates pin text rotation (0=horizontal, 90=vertical) while
611 // the side field indicates which edge of the symbol body the pin is on.
612 // Pin decal names containing "VRT" indicate perpendicular pins.
614 bool isVerticalDecal = ( aPin.pin_decal_name.find( "VRT" ) != std::string::npos );
615 int angle = static_cast<int>( aPin.rotation ) % 360;
616
617 if( isVerticalDecal )
618 {
619 orientation = ( aPin.side == 2 ) ? PIN_ORIENTATION::PIN_UP
621 }
622 else if( angle >= 45 && angle < 135 )
623 {
624 // Sides 0,1 (horizontal edges) point up; sides 2,3 (vertical edges) point down
625 orientation = ( aPin.side >= 2 ) ? PIN_ORIENTATION::PIN_DOWN
627 }
628 else if( angle >= 225 && angle < 315 )
629 {
630 orientation = ( aPin.side >= 2 ) ? PIN_ORIENTATION::PIN_UP
632 }
633 else if( angle >= 135 && angle < 225 )
634 {
635 orientation = ( aPin.side & 1 ) ? PIN_ORIENTATION::PIN_RIGHT
637 }
638 else
639 {
640 orientation = ( aPin.side & 1 ) ? PIN_ORIENTATION::PIN_LEFT
642 }
643
644 pin->SetOrientation( orientation );
645
646 // Set electrical type
647 ELECTRICAL_PINTYPE pinType = static_cast<ELECTRICAL_PINTYPE>( mapPinType( aPin.type ) );
648 pin->SetType( pinType );
649
650 // Set graphic style
652
653 if( aPin.inverted )
655 else if( aPin.clock )
656 pinShape = GRAPHIC_PINSHAPE::CLOCK;
657
658 pin->SetShape( pinShape );
659
660 int pinTextSize = schIUScale.MilsToIU( 50 );
661 pin->SetNumberTextSize( pinTextSize );
662 pin->SetNameTextSize( pinTextSize );
663
664 return pin;
665}
666
667
669{
670 // Convert mm coordinates from KiCad power symbol library to internal units
671 auto mm = [&]( double v ) { return schIUScale.mmToIU( v ); };
672
673 LIB_SYMBOL* sym = new LIB_SYMBOL( wxString::FromUTF8( aKiCadName ) );
674 sym->SetGlobalPower();
675 sym->SetShowPinNumbers( false );
676 sym->SetShowPinNames( false );
677
678 // Determine which visual style to use based on the KiCad symbol name
679 std::string upper = aKiCadName;
680 std::transform( upper.begin(), upper.end(), upper.begin(),
681 []( unsigned char c ) { return std::toupper( c ); } );
682
683 bool isGround = ( upper == "GND" || upper == "GNDA" || upper == "GNDPWR" );
684 bool isGNDD = ( upper == "GNDD" );
685 bool isPwrBar = ( upper == "PWR_BAR" );
686 bool isPwrTriangle = ( upper == "PWR_TRIANGLE" );
687 bool isVEE = ( upper == "VEE" || upper == "VSS" );
688 bool isEarth = ( upper == "EARTH" || upper == "CHASSIS" );
689
690 // Default to VCC style (open arrow up) for anything not matched above
691 bool isVCC = !isGround && !isGNDD && !isPwrBar && !isPwrTriangle
692 && !isVEE && !isEarth;
693
694 if( isGround )
695 {
696 // Standard GND chevron: polyline (0,0)→(0,-1.27)→(1.27,-1.27)→(0,-2.54)→(-1.27,-1.27)→(0,-1.27)
698 shape->AddPoint( VECTOR2I( mm( 0 ), mm( 0 ) ) );
699 shape->AddPoint( VECTOR2I( mm( 0 ), mm( -1.27 ) ) );
700 shape->AddPoint( VECTOR2I( mm( 1.27 ), mm( -1.27 ) ) );
701 shape->AddPoint( VECTOR2I( mm( 0 ), mm( -2.54 ) ) );
702 shape->AddPoint( VECTOR2I( mm( -1.27 ), mm( -1.27 ) ) );
703 shape->AddPoint( VECTOR2I( mm( 0 ), mm( -1.27 ) ) );
705 sym->AddDrawItem( shape );
706
707 SCH_PIN* pin = new SCH_PIN( sym );
708 pin->SetNumber( wxT( "1" ) );
709 pin->SetName( wxString::FromUTF8( aKiCadName ) );
711 pin->SetVisible( false );
712 pin->SetLength( 0 );
713 pin->SetPosition( VECTOR2I( 0, 0 ) );
714 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
715 sym->AddDrawItem( pin );
716 }
717 else if( isGNDD )
718 {
719 // GNDD: thick filled bar + vertical stem
721 bar->SetStart( VECTOR2I( mm( -1.27 ), mm( -1.524 ) ) );
722 bar->SetEnd( VECTOR2I( mm( 1.27 ), mm( -2.032 ) ) );
723 bar->SetStroke( STROKE_PARAMS( mm( 0.254 ), LINE_STYLE::SOLID ) );
725 sym->AddDrawItem( bar );
726
728 stem->AddPoint( VECTOR2I( mm( 0 ), mm( 0 ) ) );
729 stem->AddPoint( VECTOR2I( mm( 0 ), mm( -1.524 ) ) );
731 sym->AddDrawItem( stem );
732
733 SCH_PIN* pin = new SCH_PIN( sym );
734 pin->SetNumber( wxT( "1" ) );
735 pin->SetName( wxString::FromUTF8( aKiCadName ) );
737 pin->SetVisible( false );
738 pin->SetLength( 0 );
739 pin->SetPosition( VECTOR2I( 0, 0 ) );
740 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
741 sym->AddDrawItem( pin );
742 }
743 else if( isPwrBar )
744 {
745 // PWR_BAR: same bar-down shape as GNDD. Placed with 180° rotation for
746 // positive supplies (+V1) so the bar points up on the schematic.
747 // Negative supplies (-V1) use GNDD directly without rotation.
749 bar->SetStart( VECTOR2I( mm( -1.27 ), mm( -1.524 ) ) );
750 bar->SetEnd( VECTOR2I( mm( 1.27 ), mm( -2.032 ) ) );
751 bar->SetStroke( STROKE_PARAMS( mm( 0.254 ), LINE_STYLE::SOLID ) );
753 sym->AddDrawItem( bar );
754
756 stem->AddPoint( VECTOR2I( mm( 0 ), mm( 0 ) ) );
757 stem->AddPoint( VECTOR2I( mm( 0 ), mm( -1.524 ) ) );
759 sym->AddDrawItem( stem );
760
761 SCH_PIN* pin = new SCH_PIN( sym );
762 pin->SetNumber( wxT( "1" ) );
763 pin->SetName( wxString::FromUTF8( aKiCadName ) );
765 pin->SetVisible( false );
766 pin->SetLength( 0 );
767 pin->SetPosition( VECTOR2I( 0, 0 ) );
768 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
769 sym->AddDrawItem( pin );
770 }
771 else if( isPwrTriangle )
772 {
773 // PWR_TRIANGLE: filled triangle pointing UP (like -9V style)
775 tri->AddPoint( VECTOR2I( mm( 0.762 ), mm( 1.27 ) ) );
776 tri->AddPoint( VECTOR2I( mm( -0.762 ), mm( 1.27 ) ) );
777 tri->AddPoint( VECTOR2I( mm( 0 ), mm( 2.54 ) ) );
778 tri->AddPoint( VECTOR2I( mm( 0.762 ), mm( 1.27 ) ) );
781 sym->AddDrawItem( tri );
782
784 stem->AddPoint( VECTOR2I( mm( 0 ), mm( 0 ) ) );
785 stem->AddPoint( VECTOR2I( mm( 0 ), mm( 1.27 ) ) );
787 sym->AddDrawItem( stem );
788
789 SCH_PIN* pin = new SCH_PIN( sym );
790 pin->SetNumber( wxT( "1" ) );
791 pin->SetName( wxString::FromUTF8( aKiCadName ) );
793 pin->SetVisible( false );
794 pin->SetLength( 0 );
795 pin->SetPosition( VECTOR2I( 0, 0 ) );
796 pin->SetOrientation( PIN_ORIENTATION::PIN_UP );
797 sym->AddDrawItem( pin );
798 }
799 else if( isVEE )
800 {
801 // VEE: inverted arrow (pointing down), pin at bottom
803 arrow1->AddPoint( VECTOR2I( mm( -0.762 ), mm( -1.27 ) ) );
804 arrow1->AddPoint( VECTOR2I( mm( 0 ), mm( -2.54 ) ) );
806 sym->AddDrawItem( arrow1 );
807
809 arrow2->AddPoint( VECTOR2I( mm( 0 ), mm( -2.54 ) ) );
810 arrow2->AddPoint( VECTOR2I( mm( 0.762 ), mm( -1.27 ) ) );
812 sym->AddDrawItem( arrow2 );
813
814 SCH_SHAPE* stemLine = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
815 stemLine->AddPoint( VECTOR2I( mm( 0 ), mm( 0 ) ) );
816 stemLine->AddPoint( VECTOR2I( mm( 0 ), mm( -2.54 ) ) );
817 stemLine->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
818 sym->AddDrawItem( stemLine );
819
820 SCH_PIN* pin = new SCH_PIN( sym );
821 pin->SetNumber( wxT( "1" ) );
822 pin->SetName( wxString::FromUTF8( aKiCadName ) );
824 pin->SetVisible( false );
825 pin->SetLength( 0 );
826 pin->SetPosition( VECTOR2I( 0, 0 ) );
827 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
828 sym->AddDrawItem( pin );
829 }
830 else if( isEarth )
831 {
832 // Earth: horizontal bars descending in width + vertical stem
834 bar1->AddPoint( VECTOR2I( mm( -1.27 ), mm( -1.27 ) ) );
835 bar1->AddPoint( VECTOR2I( mm( 1.27 ), mm( -1.27 ) ) );
837 sym->AddDrawItem( bar1 );
838
840 bar2->AddPoint( VECTOR2I( mm( -0.762 ), mm( -1.778 ) ) );
841 bar2->AddPoint( VECTOR2I( mm( 0.762 ), mm( -1.778 ) ) );
843 sym->AddDrawItem( bar2 );
844
846 bar3->AddPoint( VECTOR2I( mm( -0.254 ), mm( -2.286 ) ) );
847 bar3->AddPoint( VECTOR2I( mm( 0.254 ), mm( -2.286 ) ) );
849 sym->AddDrawItem( bar3 );
850
851 SCH_SHAPE* stemLine = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
852 stemLine->AddPoint( VECTOR2I( mm( 0 ), mm( 0 ) ) );
853 stemLine->AddPoint( VECTOR2I( mm( 0 ), mm( -1.27 ) ) );
854 stemLine->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
855 sym->AddDrawItem( stemLine );
856
857 SCH_PIN* pin = new SCH_PIN( sym );
858 pin->SetNumber( wxT( "1" ) );
859 pin->SetName( wxString::FromUTF8( aKiCadName ) );
861 pin->SetVisible( false );
862 pin->SetLength( 0 );
863 pin->SetPosition( VECTOR2I( 0, 0 ) );
864 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
865 sym->AddDrawItem( pin );
866 }
867 else if( isVCC )
868 {
869 // VCC style: open arrow pointing up + vertical stem
871 arrow1->AddPoint( VECTOR2I( mm( -0.762 ), mm( 1.27 ) ) );
872 arrow1->AddPoint( VECTOR2I( mm( 0 ), mm( 2.54 ) ) );
874 sym->AddDrawItem( arrow1 );
875
877 arrow2->AddPoint( VECTOR2I( mm( 0 ), mm( 2.54 ) ) );
878 arrow2->AddPoint( VECTOR2I( mm( 0.762 ), mm( 1.27 ) ) );
880 sym->AddDrawItem( arrow2 );
881
882 SCH_SHAPE* stemLine = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
883 stemLine->AddPoint( VECTOR2I( mm( 0 ), mm( 0 ) ) );
884 stemLine->AddPoint( VECTOR2I( mm( 0 ), mm( 2.54 ) ) );
885 stemLine->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
886 sym->AddDrawItem( stemLine );
887
888 SCH_PIN* pin = new SCH_PIN( sym );
889 pin->SetNumber( wxT( "1" ) );
890 pin->SetName( wxString::FromUTF8( aKiCadName ) );
892 pin->SetVisible( false );
893 pin->SetLength( 0 );
894 pin->SetPosition( VECTOR2I( 0, 0 ) );
895 pin->SetOrientation( PIN_ORIENTATION::PIN_UP );
896 sym->AddDrawItem( pin );
897 }
898
899 sym->GetReferenceField().SetText( wxT( "#PWR" ) );
900 sym->GetReferenceField().SetVisible( false );
901
902 return sym;
903}
904
905
906std::string PADS_SCH_SYMBOL_BUILDER::GetPowerStyleFromVariant( const std::string& aDecalName,
907 const std::string& aPinType )
908{
909 std::string upper = aDecalName;
910 std::transform( upper.begin(), upper.end(), upper.begin(),
911 []( unsigned char c ) { return std::toupper( c ); } );
912
913 bool isPositive = !upper.empty() && upper[0] == '+';
914 bool isGround = ( aPinType == "G" );
915
916 if( upper.find( "RAIL" ) != std::string::npos )
917 return isPositive ? "PWR_BAR" : "GNDD";
918
919 if( upper.find( "ARROW" ) != std::string::npos )
920 return isPositive ? "PWR_TRIANGLE" : "VEE";
921
922 if( upper.find( "BUBBLE" ) != std::string::npos )
923 return isPositive ? "VCC" : "VEE";
924
925 if( isGround )
926 {
927 if( upper.find( "CH" ) != std::string::npos )
928 return "Chassis";
929
930 return "GND";
931 }
932
933 if( isPositive )
934 return "VCC";
935
936 return "VEE";
937}
938
939
941 LIB_SYMBOL* aSymbol, const std::vector<PARTTYPE_DEF::SIGPIN>& aSigpins )
942{
943 if( !aSymbol )
944 return;
945
946 // Collect existing pin numbers to avoid duplicates
947 std::set<wxString> existingPins;
948
949 for( const SCH_ITEM& item : aSymbol->GetDrawItems() )
950 {
951 if( item.Type() == SCH_PIN_T )
952 existingPins.insert( static_cast<const SCH_PIN&>( item ).GetNumber() );
953 }
954
955 for( const auto& sp : aSigpins )
956 {
957 wxString pinNum = wxString::FromUTF8( sp.pin_number );
958
959 if( existingPins.count( pinNum ) )
960 continue;
961
962 SCH_PIN* pin = new SCH_PIN( aSymbol );
963 pin->SetNumber( pinNum );
964 pin->SetName( wxString::FromUTF8( sp.net_name ) );
966 pin->SetVisible( false );
967 pin->SetLength( 0 );
968 pin->SetPosition( VECTOR2I( 0, 0 ) );
969 pin->SetShape( GRAPHIC_PINSHAPE::LINE );
970
971 aSymbol->AddDrawItem( pin );
972 existingPins.insert( pinNum );
973 }
974}
975
976
978{
979 switch( aPadsType )
980 {
981 case PIN_TYPE::INPUT: return static_cast<int>( ELECTRICAL_PINTYPE::PT_INPUT );
982 case PIN_TYPE::OUTPUT: return static_cast<int>( ELECTRICAL_PINTYPE::PT_OUTPUT );
983 case PIN_TYPE::BIDIRECTIONAL: return static_cast<int>( ELECTRICAL_PINTYPE::PT_BIDI );
984 case PIN_TYPE::TRISTATE: return static_cast<int>( ELECTRICAL_PINTYPE::PT_TRISTATE );
985 case PIN_TYPE::OPEN_COLLECTOR: return static_cast<int>( ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR );
986 case PIN_TYPE::OPEN_EMITTER: return static_cast<int>( ELECTRICAL_PINTYPE::PT_OPENEMITTER );
987 case PIN_TYPE::POWER: return static_cast<int>( ELECTRICAL_PINTYPE::PT_POWER_IN );
988 case PIN_TYPE::PASSIVE: return static_cast<int>( ELECTRICAL_PINTYPE::PT_PASSIVE );
990 default: return static_cast<int>( ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
991 }
992}
993
994
995bool PADS_SCH_SYMBOL_BUILDER::IsPowerSymbol( const std::string& aName )
996{
997 // Convert to uppercase for case-insensitive comparison
998 std::string upper = aName;
999 std::transform( upper.begin(), upper.end(), upper.begin(),
1000 []( unsigned char c ) { return std::toupper( c ); } );
1001
1002 // Check for ground variants
1003 if( upper == "GND" || upper == "AGND" || upper == "DGND" || upper == "PGND" ||
1004 upper == "EARTH" || upper == "CHASSIS" || upper == "VSS" || upper == "0V" )
1005 {
1006 return true;
1007 }
1008
1009 // Check for power supply variants
1010 if( upper == "VCC" || upper == "VDD" || upper == "VEE" || upper == "VPP" ||
1011 upper == "VBAT" || upper == "VBUS" || upper == "V+" || upper == "V-" )
1012 {
1013 return true;
1014 }
1015
1016 // Check for voltage patterns like +3V3, +5V, -12V, +V1, -V2, etc.
1017 if( upper.length() >= 2 && ( upper[0] == '+' || upper[0] == '-' ) )
1018 return true;
1019
1020 return false;
1021}
1022
1023
1024std::optional<LIB_ID> PADS_SCH_SYMBOL_BUILDER::GetKiCadPowerSymbolId( const std::string& aPadsName )
1025{
1026 // Convert to uppercase for case-insensitive comparison
1027 std::string upper = aPadsName;
1028 std::transform( upper.begin(), upper.end(), upper.begin(),
1029 []( unsigned char c ) { return std::toupper( c ); } );
1030
1031 // Map common power symbol names to KiCad power library symbols
1032 struct PowerMapping
1033 {
1034 const char* padsName;
1035 const char* kicadSymbol;
1036 };
1037
1038 static const PowerMapping mappings[] = {
1039 { "GND", "GND" },
1040 { "AGND", "GND" },
1041 { "DGND", "GNDD" },
1042 { "PGND", "GNDPWR" },
1043 { "EARTH", "Earth" },
1044 { "CHASSIS", "Chassis" },
1045 { "VSS", "VSS" },
1046 { "0V", "GND" },
1047 { "VCC", "VCC" },
1048 { "VDD", "VDD" },
1049 { "VEE", "VEE" },
1050 { "VPP", "VPP" },
1051 { "VBAT", "VBAT" },
1052 { "VBUS", "VBUS" },
1053 { "V+", "VCC" },
1054 { "V-", "VEE" },
1055 { "+5V", "+5V" },
1056 { "-5V", "-5V" },
1057 { "+3V3", "+3V3" },
1058 { "+3.3V", "+3V3" },
1059 { "+12V", "+12V" },
1060 { "-12V", "-12V" },
1061 { "+15V", "+15V" },
1062 { "-15V", "-15V" },
1063 { "+1V8", "+1V8" },
1064 { "+2V5", "+2V5" },
1065 { "+9V", "+9V" },
1066 { "+24V", "+24V" },
1067 };
1068
1069 for( const auto& mapping : mappings )
1070 {
1071 if( upper == mapping.padsName )
1072 {
1073 LIB_ID libId;
1074 libId.SetLibNickname( "power" );
1075 libId.SetLibItemName( mapping.kicadSymbol );
1076 return libId;
1077 }
1078 }
1079
1080 // Generic fallback for +/- prefixed names not in the table
1081 if( upper.length() >= 2 && upper[0] == '+' )
1082 {
1083 LIB_ID libId;
1084 libId.SetLibNickname( "power" );
1085 libId.SetLibItemName( "VCC" );
1086 return libId;
1087 }
1088
1089 if( upper.length() >= 2 && upper[0] == '-' )
1090 {
1091 LIB_ID libId;
1092 libId.SetLibNickname( "power" );
1093 libId.SetLibItemName( "VEE" );
1094 return libId;
1095 }
1096
1097 return std::nullopt;
1098}
1099
1100} // namespace PADS_SCH
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:114
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
void SetCenter(const VECTOR2I &aCenter)
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:178
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
void SetFillMode(FILL_T aFill)
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:546
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
void SetTextAngleDegrees(double aOrientation)
Definition eda_text.h:150
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition lib_id.cpp:111
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:100
Define a library symbol object.
Definition lib_symbol.h:83
void SetGlobalPower()
void LockUnits(bool aLockUnits)
Set interchangeable the property for symbol units.
Definition lib_symbol.h:281
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:712
void SetUnitCount(int aCount, bool aDuplicateDrawItems)
Set the units per symbol count.
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
SCH_FIELD & GetReferenceField()
Return reference to the reference designator field.
Definition lib_symbol.h:336
static PIN_TYPE ParsePinTypeChar(char aTypeChar)
int mapPinType(PIN_TYPE aPadsType)
Map PADS pin type to KiCad electrical type.
LIB_SYMBOL * BuildMultiUnitSymbol(const PARTTYPE_DEF &aPartType, const std::vector< SYMBOL_DEF > &aSymbolDefs)
Build a composite multi-unit LIB_SYMBOL from a multi-gate PARTTYPE.
bool HasSymbol(const std::string &aName) const
Check if a symbol with the given name already exists.
void AddHiddenPowerPins(LIB_SYMBOL *aSymbol, const std::vector< PARTTYPE_DEF::SIGPIN > &aSigpins)
Add hidden power pins from PARTTYPE SIGPIN entries to an existing symbol.
SCH_PIN * createPin(const SYMBOL_PIN &aPin, LIB_SYMBOL *aParent)
Create a SCH_PIN from a PADS pin definition.
LIB_SYMBOL * GetSymbol(const std::string &aName) const
Get a cached symbol by name.
int toKiCadUnits(double aPadsValue) const
Convert PADS coordinate to KiCad internal units.
LIB_SYMBOL * GetOrCreateMultiUnitSymbol(const PARTTYPE_DEF &aPartType, const std::vector< SYMBOL_DEF > &aSymbolDefs)
Get or create a multi-unit symbol for the given PARTTYPE.
static std::optional< LIB_ID > GetKiCadPowerSymbolId(const std::string &aPadsName)
Get KiCad power library symbol ID for a PADS power symbol.
std::vector< SCH_SHAPE * > createShapes(const SYMBOL_GRAPHIC &aGraphic)
Create one or more SCH_SHAPEs from a PADS graphic element.
LIB_SYMBOL * BuildKiCadPowerSymbol(const std::string &aKiCadName)
Build a power symbol using hard-coded KiCad-standard graphics.
PADS_SCH_SYMBOL_BUILDER(const PARAMETERS &aParams)
static bool IsPowerSymbol(const std::string &aName)
Check if a symbol name indicates a power symbol.
static std::string GetPowerStyleFromVariant(const std::string &aDecalName, const std::string &aPinType)
Map a PADS special_variant to a power symbol style name.
SCH_SHAPE * createShape(const SYMBOL_GRAPHIC &aGraphic)
Create a SCH_SHAPE from a PADS graphic element.
LIB_SYMBOL * GetOrCreateSymbol(const SYMBOL_DEF &aSymbolDef)
Get or create a symbol for the given definition.
std::map< std::string, std::unique_ptr< LIB_SYMBOL > > m_symbolCache
LIB_SYMBOL * BuildSymbol(const SYMBOL_DEF &aSymbolDef)
Build a KiCad LIB_SYMBOL from a PADS symbol definition.
LIB_SYMBOL * GetOrCreatePartTypeSymbol(const PARTTYPE_DEF &aPartType, const SYMBOL_DEF &aSymbolDef)
Get or create a single-gate symbol with PARTTYPE-specific pin remapping.
void SetText(const wxString &aText) override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
virtual void SetUnit(int aUnit)
Definition sch_item.h:238
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:63
void AddPoint(const VECTOR2I &aPosition)
Simple container to manage line stroke parameters.
virtual void SetShowPinNumbers(bool aShow)
Set or clear the pin number visibility flag.
Definition symbol.h:174
virtual void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
Definition symbol.h:168
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
double m_PadsSchTextWidthScale
PADS text width scale factor for schematic imports.
double m_PadsSchTextHeightScale
PADS text height scale factor for schematic imports.
@ LAYER_DEVICE
Definition layer_ids.h:466
LINE_STYLE PadsLineStyleToKiCad(int aPadsStyle)
Convert a PADS line style integer to a KiCad LINE_STYLE enum value.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
Common utilities and types for parsing PADS file formats.
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
@ PT_OUTPUT
usual output
Definition pin_type.h:38
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:40
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:39
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:49
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:48
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:46
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:45
@ 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
GRAPHIC_PINSHAPE
Definition pin_type.h:84
CITER next(CITER it)
Definition ptree.cpp:124
LINE_STYLE
Dashed line types.
Gate definition within a PARTTYPE.
std::vector< std::string > decal_names
std::vector< PARTTYPE_PIN > pins
std::optional< ARC_DATA > arc
General schematic parameters from SCH and FIELDS sections.
Part type definition from PARTTYPE section.
std::vector< GATE_DEF > gates
Symbol definition from CAEDECAL section.
std::vector< SYMBOL_GRAPHIC > graphics
std::vector< SYMBOL_PIN > pins
std::vector< SYMBOL_TEXT > texts
Graphic primitive from CAEDECAL or LINES sections (OPEN, CLOSED, CIRCLE, COPCLS).
std::vector< GRAPHIC_POINT > points
Pin T/P line pair from CAEDECAL.
KIBIS_PIN * pin
VECTOR2I center
int radius
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.
#define M_PI
@ SCH_PIN_T
Definition typeinfo.h:157
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695