KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pads_sch_schematic_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 <sch_line.h>
23#include <sch_junction.h>
24#include <sch_label.h>
25#include <sch_screen.h>
26#include <sch_sheet.h>
27#include <sch_sheet_path.h>
28#include <sch_sheet_pin.h>
29#include <sch_symbol.h>
30#include <schematic.h>
32#include <layer_ids.h>
33#include <template_fieldnames.h>
34#include <title_block.h>
36#include <io/pads/pads_common.h>
37
38#include <advanced_config.h>
39
40#include <algorithm>
41#include <cctype>
42#include <cmath>
43#include <map>
44#include <set>
45
46namespace PADS_SCH
47{
48
50 SCHEMATIC* aSchematic ) :
51 m_params( aParams ),
52 m_schematic( aSchematic ),
53 m_pageHeightIU( schIUScale.MilsToIU( aParams.sheet_size.height ) )
54{
55}
56
57
61
62
63int PADS_SCH_SCHEMATIC_BUILDER::toKiCadUnits( double aPadsValue ) const
64{
65 double milsValue = aPadsValue;
66
67 switch( m_params.units )
68 {
69 case UNIT_TYPE::MILS:
70 milsValue = aPadsValue;
71 break;
72
74 milsValue = aPadsValue * 39.3701;
75 break;
76
78 milsValue = aPadsValue * 1000.0;
79 break;
80 }
81
82 return schIUScale.MilsToIU( milsValue );
83}
84
85
86int PADS_SCH_SCHEMATIC_BUILDER::toKiCadY( double aPadsY ) const
87{
88 return m_pageHeightIU - toKiCadUnits( aPadsY );
89}
90
91
92wxString PADS_SCH_SCHEMATIC_BUILDER::convertNetName( const std::string& aName ) const
93{
95}
96
97
98int PADS_SCH_SCHEMATIC_BUILDER::CreateWires( const std::vector<SCH_SIGNAL>& aSignals,
99 SCH_SCREEN* aScreen )
100{
101 int wireCount = 0;
102
103 for( const auto& signal : aSignals )
104 {
105 for( const auto& wire : signal.wires )
106 {
107 SCH_LINE* schLine = CreateWire( wire );
108
109 if( schLine )
110 {
111 schLine->SetFlags( IS_NEW );
112 aScreen->Append( schLine );
113 wireCount++;
114 }
115 }
116 }
117
118 return wireCount;
119}
120
121
123{
124 VECTOR2I start( toKiCadUnits( aWire.start.x ), toKiCadY( aWire.start.y ) );
125 VECTOR2I end( toKiCadUnits( aWire.end.x ), toKiCadY( aWire.end.y ) );
126
127 SCH_LINE* line = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
128 line->SetEndPoint( end );
129
130 return line;
131}
132
133
134int PADS_SCH_SCHEMATIC_BUILDER::CreateJunctions( const std::vector<SCH_SIGNAL>& aSignals,
135 SCH_SCREEN* aScreen )
136{
137 std::vector<VECTOR2I> junctionPoints = findJunctionPoints( aSignals );
138
139 for( const auto& pt : junctionPoints )
140 {
141 SCH_JUNCTION* junction = new SCH_JUNCTION( pt );
142 junction->SetFlags( IS_NEW );
143 aScreen->Append( junction );
144 }
145
146 return static_cast<int>( junctionPoints.size() );
147}
148
149
151 const std::vector<SCH_SIGNAL>& aSignals )
152{
153 std::vector<VECTOR2I> junctions;
154
155 for( const auto& signal : aSignals )
156 {
157 // Count how many wire endpoints connect at each point
158 std::map<std::pair<int, int>, int> pointCount;
159
160 for( const auto& wire : signal.wires )
161 {
162 VECTOR2I start( toKiCadUnits( wire.start.x ), toKiCadY( wire.start.y ) );
163 VECTOR2I end( toKiCadUnits( wire.end.x ), toKiCadY( wire.end.y ) );
164
165 pointCount[{ start.x, start.y }]++;
166 pointCount[{ end.x, end.y }]++;
167 }
168
169 // Junction needed where 3+ wire endpoints meet
170 for( const auto& [coords, count] : pointCount )
171 {
172 if( count >= 3 )
173 {
174 junctions.emplace_back( coords.first, coords.second );
175 }
176 }
177 }
178
179 return junctions;
180}
181
182
183int PADS_SCH_SCHEMATIC_BUILDER::CreateNetLabels( const std::vector<SCH_SIGNAL>& aSignals,
184 SCH_SCREEN* aScreen,
185 const std::set<std::string>& aSignalOpcIds,
186 const std::set<std::string>& aSkipSignals )
187{
188 int labelCount = 0;
189
190 for( const auto& signal : aSignals )
191 {
192 if( signal.name.empty() || signal.name[0] == '$' )
193 continue;
194
195 if( aSkipSignals.count( signal.name ) )
196 continue;
197
198 if( signal.wires.empty() )
199 continue;
200
201 // Collect label placements from OPC wire endpoints. Each OPC produces one label.
202 std::vector<std::pair<VECTOR2I, VECTOR2I>> opcPlacements;
203
204 for( const auto& wire : signal.wires )
205 {
206 if( wire.vertices.size() < 2 )
207 continue;
208
209 // endpoint_a references first vertex, endpoint_b references last vertex
210 if( !wire.endpoint_a.empty() && wire.endpoint_a.substr( 0, 3 ) == "@@@"
211 && aSignalOpcIds.count( wire.endpoint_a ) )
212 {
213 VECTOR2I labelPos( toKiCadUnits( wire.vertices.front().x ),
214 toKiCadY( wire.vertices.front().y ) );
215 VECTOR2I adjPos( toKiCadUnits( wire.vertices[1].x ),
216 toKiCadY( wire.vertices[1].y ) );
217 opcPlacements.emplace_back( labelPos, adjPos );
218 }
219
220 if( !wire.endpoint_b.empty() && wire.endpoint_b.substr( 0, 3 ) == "@@@"
221 && aSignalOpcIds.count( wire.endpoint_b ) )
222 {
223 size_t last = wire.vertices.size() - 1;
224 VECTOR2I labelPos( toKiCadUnits( wire.vertices[last].x ),
225 toKiCadY( wire.vertices[last].y ) );
226 VECTOR2I adjPos( toKiCadUnits( wire.vertices[last - 1].x ),
227 toKiCadY( wire.vertices[last - 1].y ) );
228 opcPlacements.emplace_back( labelPos, adjPos );
229 }
230 }
231
232 for( const auto& [labelPos, adjPos] : opcPlacements )
233 {
234 SPIN_STYLE orient = computeLabelOrientation( labelPos, adjPos );
235 SCH_GLOBALLABEL* label = CreateNetLabel( signal, labelPos, orient );
236
237 if( label )
238 {
239 label->SetFlags( IS_NEW );
240 aScreen->Append( label );
241 labelCount++;
242 }
243 }
244 }
245
246 return labelCount;
247}
248
249
251 const VECTOR2I& aLabelPos, const VECTOR2I& aAdjacentPos )
252{
253 // The wire goes from aLabelPos toward aAdjacentPos. The label text extends
254 // in the opposite direction so it doesn't overlap the wire.
255 int dx = aAdjacentPos.x - aLabelPos.x;
256 int dy = aAdjacentPos.y - aLabelPos.y;
257
258 if( std::abs( dx ) >= std::abs( dy ) )
259 {
260 return ( dx > 0 ) ? SPIN_STYLE::LEFT : SPIN_STYLE::RIGHT;
261 }
262 else
263 {
264 return ( dy > 0 ) ? SPIN_STYLE::UP : SPIN_STYLE::BOTTOM;
265 }
266}
267
268
270 const VECTOR2I& aPosition,
271 SPIN_STYLE aOrientation )
272{
273 wxString labelName = convertNetName( aSignal.name );
274
275 SCH_GLOBALLABEL* label = new SCH_GLOBALLABEL( aPosition, labelName );
277 label->SetSpinStyle( aOrientation );
278
279 int labelSize = schIUScale.MilsToIU( 50 );
280 label->SetTextSize( VECTOR2I( labelSize, labelSize ) );
281
282 return label;
283}
284
285
287{
288 if( aSignal.wires.empty() )
289 return VECTOR2I( 0, 0 );
290
291 // Count how many wire segments share each endpoint. An endpoint referenced
292 // only once is a dangling wire end -- the correct place for a global label.
293 // Only first and last vertices are true endpoints; interior ones are bends.
294 std::map<std::pair<int, int>, int> endpointCount;
295
296 for( const auto& wire : aSignal.wires )
297 {
298 if( wire.vertices.size() < 2 )
299 continue;
300
301 auto first = wire.vertices.front();
302 auto last = wire.vertices.back();
303
304 endpointCount[{ static_cast<int>( first.x * 1000 ),
305 static_cast<int>( first.y * 1000 ) }]++;
306 endpointCount[{ static_cast<int>( last.x * 1000 ),
307 static_cast<int>( last.y * 1000 ) }]++;
308 }
309
310 // Also count pin connection positions so we can avoid placing on a pin
311 std::set<std::pair<int, int>> pinEndpoints;
312
313 for( const auto& wire : aSignal.wires )
314 {
315 if( wire.vertices.size() < 2 )
316 continue;
317
318 // endpoint_a/endpoint_b hold pin references (e.g. "R1.1"); if non-empty,
319 // that endpoint connects to a component pin. OPC references (starting
320 // with "@@@") are off-page connectors, not physical pins.
321 if( !wire.endpoint_a.empty() && wire.endpoint_a.substr( 0, 3 ) != "@@@" )
322 {
323 auto pt = wire.vertices.front();
324 pinEndpoints.insert( { static_cast<int>( pt.x * 1000 ),
325 static_cast<int>( pt.y * 1000 ) } );
326 }
327
328 if( !wire.endpoint_b.empty() && wire.endpoint_b.substr( 0, 3 ) != "@@@" )
329 {
330 auto pt = wire.vertices.back();
331 pinEndpoints.insert( { static_cast<int>( pt.x * 1000 ),
332 static_cast<int>( pt.y * 1000 ) } );
333 }
334 }
335
336 // Prefer a dangling endpoint that is NOT at a pin
337 for( const auto& wire : aSignal.wires )
338 {
339 if( wire.vertices.size() < 2 )
340 continue;
341
342 for( const auto* vtx : { &wire.vertices.front(), &wire.vertices.back() } )
343 {
344 auto key = std::make_pair( static_cast<int>( vtx->x * 1000 ),
345 static_cast<int>( vtx->y * 1000 ) );
346
347 if( endpointCount[key] == 1 && pinEndpoints.count( key ) == 0 )
348 return VECTOR2I( toKiCadUnits( vtx->x ), toKiCadY( vtx->y ) );
349 }
350 }
351
352 // Fallback: any dangling endpoint (even if at a pin)
353 for( const auto& wire : aSignal.wires )
354 {
355 if( wire.vertices.size() < 2 )
356 continue;
357
358 for( const auto* vtx : { &wire.vertices.front(), &wire.vertices.back() } )
359 {
360 auto key = std::make_pair( static_cast<int>( vtx->x * 1000 ),
361 static_cast<int>( vtx->y * 1000 ) );
362
363 if( endpointCount[key] == 1 )
364 return VECTOR2I( toKiCadUnits( vtx->x ), toKiCadY( vtx->y ) );
365 }
366 }
367
368 // Last resort: first endpoint of the first wire that has vertices
369 for( const auto& wire : aSignal.wires )
370 {
371 if( !wire.vertices.empty() )
372 {
373 const auto& vtx = wire.vertices[0];
374 return VECTOR2I( toKiCadUnits( vtx.x ), toKiCadY( vtx.y ) );
375 }
376 }
377
378 return VECTOR2I( 0, 0 );
379}
380
381
382int PADS_SCH_SCHEMATIC_BUILDER::CreateBusWires( const std::vector<SCH_SIGNAL>& aSignals,
383 SCH_SCREEN* aScreen )
384{
385 int busCount = 0;
386
387 for( const auto& signal : aSignals )
388 {
389 if( !IsBusSignal( signal.name ) )
390 continue;
391
392 for( const auto& wire : signal.wires )
393 {
394 SCH_LINE* busLine = CreateBusWire( wire );
395
396 if( busLine )
397 {
398 busLine->SetFlags( IS_NEW );
399 aScreen->Append( busLine );
400 busCount++;
401 }
402 }
403 }
404
405 return busCount;
406}
407
408
410{
411 VECTOR2I start( toKiCadUnits( aWire.start.x ), toKiCadY( aWire.start.y ) );
412 VECTOR2I end( toKiCadUnits( aWire.end.x ), toKiCadY( aWire.end.y ) );
413
414 SCH_LINE* line = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
415 line->SetEndPoint( end );
416
417 return line;
418}
419
420
421bool PADS_SCH_SCHEMATIC_BUILDER::IsBusSignal( const std::string& aName )
422{
423 if( aName.empty() )
424 return false;
425
426 // Check for bus naming patterns commonly used in PADS
427 // Pattern 1: NAME[n:m] or NAME[n..m] - range notation
428 size_t bracketPos = aName.find( '[' );
429
430 if( bracketPos != std::string::npos )
431 {
432 size_t closeBracket = aName.find( ']', bracketPos );
433
434 if( closeBracket != std::string::npos )
435 {
436 std::string range = aName.substr( bracketPos + 1, closeBracket - bracketPos - 1 );
437
438 if( range.find( ':' ) != std::string::npos ||
439 range.find( ".." ) != std::string::npos )
440 {
441 return true;
442 }
443 }
444 }
445
446 // Pattern 2: NAME<n:m> or NAME<n..m>
447 size_t anglePos = aName.find( '<' );
448
449 if( anglePos != std::string::npos )
450 {
451 size_t closeAngle = aName.find( '>', anglePos );
452
453 if( closeAngle != std::string::npos )
454 {
455 std::string range = aName.substr( anglePos + 1, closeAngle - anglePos - 1 );
456
457 if( range.find( ':' ) != std::string::npos ||
458 range.find( ".." ) != std::string::npos )
459 {
460 return true;
461 }
462 }
463 }
464
465 return false;
466}
467
468
470 const PART_PLACEMENT& aPlacement )
471{
472 if( !aSymbol || !m_schematic )
473 return;
474
475 // Set reference designator, stripping any gate suffix (e.g., "U17-A" → "U17")
476 if( !aPlacement.reference.empty() )
477 {
478 std::string ref = aPlacement.reference;
479 size_t sepPos = ref.rfind( '-' );
480
481 if( sepPos == std::string::npos )
482 sepPos = ref.rfind( '.' );
483
484 if( sepPos != std::string::npos && sepPos + 1 < ref.size()
485 && std::isalpha( static_cast<unsigned char>( ref[sepPos + 1] ) ) )
486 {
487 ref = ref.substr( 0, sepPos );
488 }
489
490 aSymbol->SetRef( &m_schematic->CurrentSheet(), wxString::FromUTF8( ref ) );
491 }
492
493 // Value field is always the PARTTYPE name. Parametric values like VALUE1
494 // flow through CreateCustomFields as user-defined fields.
495 if( !aPlacement.part_type.empty() )
496 {
497 aSymbol->SetValueFieldText( wxString::FromUTF8( aPlacement.part_type ) );
498 }
499
500 // Look for PCB DECAL attribute to set footprint
501 for( const auto& attr : aPlacement.attributes )
502 {
503 if( attr.name == "PCB DECAL" || attr.name == "PCB_DECAL" || attr.name == "FOOTPRINT" )
504 {
505 if( !attr.value.empty() )
506 {
507 aSymbol->SetFootprintFieldText( wxString::FromUTF8( attr.value ) );
508 }
509
510 break;
511 }
512 }
513
514 // Apply visibility and position settings
515 ApplyFieldSettings( aSymbol, aPlacement );
516}
517
518
520 const PART_PLACEMENT& aPlacement )
521{
522 if( !aSymbol )
523 return;
524
526
527 for( const auto& attr : aPlacement.attributes )
528 {
529 SCH_FIELD* field = nullptr;
530 bool isRefOrValue = false;
531
532 if( mapper.IsReferenceField( attr.name ) )
533 {
534 field = aSymbol->GetField( FIELD_T::REFERENCE );
535 isRefOrValue = true;
536 }
537 else if( mapper.IsValueField( attr.name ) )
538 {
539 field = aSymbol->GetField( FIELD_T::VALUE );
540 isRefOrValue = true;
541 }
542 else if( mapper.IsFootprintField( attr.name ) )
543 {
544 field = aSymbol->GetField( FIELD_T::FOOTPRINT );
545 }
546
547 if( field )
548 {
549 bool isRef = mapper.IsReferenceField( attr.name );
550
551 if( isRef )
552 field->SetVisible( true );
553 else
554 field->SetVisible( isRefOrValue ? attr.visible : false );
555
556 // PADS field positions are in CAEDECAL coordinates (pre-mirror).
557 // KiCad applies the symbol transform to field positions, so
558 // pre-compensate X for mirrored-Y symbols.
559 int fx = toKiCadUnits( attr.position.x );
560
561 if( aPlacement.mirror_flags & 1 )
562 fx = -fx;
563
564 VECTOR2I fieldPos( fx, -toKiCadUnits( attr.position.y ) );
565 field->SetPosition( aSymbol->GetPosition() + fieldPos );
566
567 if( attr.rotation != 0.0 )
568 {
569 field->SetTextAngleDegrees( attr.rotation );
570 }
571
572 if( attr.height > 0 )
573 {
574 int scaledH = static_cast<int>(
575 schIUScale.MilsToIU( attr.height )
576 * ADVANCED_CFG::GetCfg().m_PadsSchTextHeightScale );
577 int scaledW = static_cast<int>(
578 schIUScale.MilsToIU( attr.height )
579 * ADVANCED_CFG::GetCfg().m_PadsSchTextWidthScale );
580 field->SetTextSize( VECTOR2I( scaledW, scaledH ) );
581 }
582 else
583 {
584 int fieldTextSize = schIUScale.MilsToIU( 50 );
585 field->SetTextSize( VECTOR2I( fieldTextSize, fieldTextSize ) );
586 }
587
588 if( attr.width > 0 )
589 field->SetTextThickness( schIUScale.MilsToIU( attr.width ) );
590
593 }
594 }
595}
596
597
599 const PART_PLACEMENT& aPlacement )
600{
601 if( !aSymbol )
602 return 0;
603
604 int fieldsCreated = 0;
606
607 std::set<std::string> processedNames;
608
609 for( const auto& attr : aPlacement.attributes )
610 {
611 // Skip standard fields that are handled by ApplyPartAttributes
612 if( mapper.IsStandardField( attr.name ) )
613 continue;
614
615 // Skip empty attributes
616 if( attr.value.empty() )
617 continue;
618
619 processedNames.insert( attr.name );
620
621 // Get the mapped field name
622 std::string fieldName = mapper.GetKiCadFieldName( attr.name );
623
624 // Check if this field already exists on the symbol
625 SCH_FIELD* existingField = aSymbol->GetField( wxString::FromUTF8( fieldName ) );
626
627 if( existingField )
628 {
629 // Update existing field value and settings
630 existingField->SetText( wxString::FromUTF8( attr.value ) );
631 existingField->SetVisible( false );
632 }
633 else
634 {
635 // Create a new custom field using FIELD_T::USER for custom fields
636 SCH_FIELD newField( aSymbol, FIELD_T::USER, wxString::FromUTF8( fieldName ) );
637
638 newField.SetText( wxString::FromUTF8( attr.value ) );
639 newField.SetVisible( false );
640
641 // Apply position offset from attribute
642 VECTOR2I fieldPos( toKiCadUnits( attr.position.x ), -toKiCadUnits( attr.position.y ) );
643 newField.SetPosition( aSymbol->GetPosition() + fieldPos );
644
645 // Apply rotation if specified
646 if( attr.rotation != 0.0 )
647 {
648 newField.SetTextAngleDegrees( attr.rotation );
649 }
650
651 // Apply text size if specified
652 if( attr.size > 0.0 )
653 {
654 int textSize = toKiCadUnits( attr.size );
655 newField.SetTextSize( VECTOR2I( textSize, textSize ) );
656 }
657
658 aSymbol->GetFields().push_back( newField );
659 fieldsCreated++;
660 }
661 }
662
663 // Create fields from attr_overrides that weren't in the attributes vector
664 for( const auto& [name, value] : aPlacement.attr_overrides )
665 {
666 if( value.empty() || processedNames.count( name ) || mapper.IsStandardField( name ) )
667 continue;
668
669 std::string fieldName = mapper.GetKiCadFieldName( name );
670 SCH_FIELD* existingField = aSymbol->GetField( wxString::FromUTF8( fieldName ) );
671
672 if( existingField )
673 {
674 existingField->SetText( wxString::FromUTF8( value ) );
675 existingField->SetVisible( false );
676 }
677 else
678 {
679 SCH_FIELD newField( aSymbol, FIELD_T::USER, wxString::FromUTF8( fieldName ) );
680 newField.SetText( wxString::FromUTF8( value ) );
681 newField.SetVisible( false );
682 newField.SetPosition( aSymbol->GetPosition() );
683
684 aSymbol->GetFields().push_back( newField );
685 fieldsCreated++;
686 }
687 }
688
689 return fieldsCreated;
690}
691
692
694{
695 if( !aScreen )
696 return;
697
698 // Look up the first non-empty value from a list of candidate field names
699 auto findField = [this]( const std::initializer_list<const char*>& aCandidates ) -> std::string
700 {
701 for( const char* name : aCandidates )
702 {
703 auto it = m_params.fields.find( name );
704
705 if( it != m_params.fields.end() && !it->second.empty() )
706 return it->second;
707 }
708
709 return {};
710 };
711
712 TITLE_BLOCK tb;
713
714 std::string title = findField( { "Title", "TITLE1" } );
715
716 if( title.empty() )
717 title = m_params.job_name;
718
719 if( !title.empty() )
720 tb.SetTitle( wxString::FromUTF8( title ) );
721
722 std::string date = findField( { "DATE", "Release Date", "Drawn Date" } );
723
724 if( !date.empty() )
725 tb.SetDate( wxString::FromUTF8( date ) );
726
727 std::string revision = findField( { "Revision", "VER" } );
728
729 if( !revision.empty() )
730 tb.SetRevision( wxString::FromUTF8( revision ) );
731
732 std::string company = findField( { "Company Name" } );
733
734 if( !company.empty() )
735 tb.SetCompany( wxString::FromUTF8( company ) );
736
737 std::string drawingNumber = findField( { "DN", "Drawing Number" } );
738
739 if( !drawingNumber.empty() )
740 tb.SetComment( 0, wxString::FromUTF8( drawingNumber ) );
741
742 std::string designer = findField( { "DESIGNER" } );
743
744 if( !designer.empty() )
745 tb.SetComment( 1, wxString::FromUTF8( designer ) );
746
747 std::string drawnBy = findField( { "DRAWNBY", "Drawn By" } );
748
749 if( !drawnBy.empty() )
750 tb.SetComment( 2, wxString::FromUTF8( drawnBy ) );
751
752 std::string builtFor = findField( { "BUILTFOR" } );
753
754 if( !builtFor.empty() )
755 tb.SetComment( 3, wxString::FromUTF8( builtFor ) );
756
757 aScreen->SetTitleBlock( tb );
758}
759
760
762 SCH_SHEET* aParentSheet,
763 const wxString& aBaseFilename )
764{
765 if( !aParentSheet || !m_schematic )
766 return nullptr;
767
768 VECTOR2I pos = CalculateSheetPosition( aSheetNumber - 1, aTotalSheets );
770
771 SCH_SHEET* sheet = new SCH_SHEET( aParentSheet, pos, size );
772
773 // Create a screen for this sheet
774 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
775 sheet->SetScreen( screen );
776
777 // Generate sheet filename based on base filename and sheet number
778 wxFileName fn( aBaseFilename );
779 wxString sheetFilename = wxString::Format( wxT( "%s_sheet%d.%s" ),
780 fn.GetName(),
781 aSheetNumber,
783
784 // Set the sheet filename field
785 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( sheetFilename );
786
787 // Set sheet name
788 wxString sheetName = wxString::Format( wxT( "Sheet %d" ), aSheetNumber );
789 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( sheetName );
790
791 // Set full path for the screen if project is available
792 if( m_schematic->IsValid() )
793 {
794 wxFileName screenFn( m_schematic->Project().GetProjectPath(), sheetFilename );
795 screen->SetFileName( screenFn.GetFullPath() );
796 }
797 else
798 {
799 screen->SetFileName( sheetFilename );
800 }
801
802 sheet->SetFlags( IS_NEW );
803
804 // Add the sheet to the parent's screen
805 SCH_SCREEN* parentScreen = aParentSheet->GetScreen();
806
807 if( parentScreen )
808 {
809 parentScreen->Append( sheet );
810 }
811
812 return sheet;
813}
814
815
817{
818 // Default sheet symbol size in mils (approximately 2" x 1.5")
819 return VECTOR2I( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) );
820}
821
822
823VECTOR2I PADS_SCH_SCHEMATIC_BUILDER::CalculateSheetPosition( int aSheetIndex, int aTotalSheets ) const
824{
825 // Arrange sheet symbols in a grid on the root sheet
826 // Start position offset from origin
827 const int startX = schIUScale.MilsToIU( 500 );
828 const int startY = schIUScale.MilsToIU( 500 );
829
830 // Spacing between sheet symbols
831 const int spacingX = schIUScale.MilsToIU( 2500 );
832 const int spacingY = schIUScale.MilsToIU( 2000 );
833
834 // Calculate grid columns based on total sheets (aim for roughly square layout)
835 int columns = static_cast<int>( std::ceil( std::sqrt( static_cast<double>( aTotalSheets ) ) ) );
836
837 if( columns < 1 )
838 columns = 1;
839
840 int row = aSheetIndex / columns;
841 int col = aSheetIndex % columns;
842
843 return VECTOR2I( startX + col * spacingX, startY + row * spacingY );
844}
845
846
848 const std::string& aSignalName,
849 int aPinIndex )
850{
851 if( !aSheet )
852 return nullptr;
853
854 wxString name = wxString::FromUTF8( aSignalName );
855
856 // Position pins along the left edge of the sheet
857 VECTOR2I sheetPos = aSheet->GetPosition();
858
859 int pinSpacing = schIUScale.MilsToIU( 200 );
860 int yOffset = schIUScale.MilsToIU( 100 ) + aPinIndex * pinSpacing;
861
862 VECTOR2I pinPos( sheetPos.x, sheetPos.y + yOffset );
863
864 SCH_SHEET_PIN* pin = new SCH_SHEET_PIN( aSheet, pinPos, name );
865 pin->SetSide( SHEET_SIDE::LEFT );
867
868 aSheet->AddPin( pin );
869
870 return pin;
871}
872
873
875 const VECTOR2I& aPosition,
876 SCH_SCREEN* aScreen )
877{
878 if( !aScreen )
879 return nullptr;
880
881 wxString name = wxString::FromUTF8( aSignalName );
882
883 SCH_HIERLABEL* label = new SCH_HIERLABEL( aPosition, name );
885 label->SetFlags( IS_NEW );
886
887 aScreen->Append( label );
888
889 return label;
890}
891
892
893
894bool PADS_SCH_SCHEMATIC_BUILDER::IsGlobalSignal( const std::string& aSignalName,
895 const std::set<int>& aSheetNumbers )
896{
897 if( aSignalName.empty() )
898 return false;
899
900 // Signals appearing on multiple sheets should be global
901 if( aSheetNumbers.size() > 1 )
902 return true;
903
904 // Common power net names are always global
905 std::string upperName = aSignalName;
906 std::transform( upperName.begin(), upperName.end(), upperName.begin(), ::toupper );
907
908 static const std::set<std::string> globalPatterns = {
909 "VCC", "VDD", "VEE", "VSS", "GND", "AGND", "DGND", "PGND",
910 "V+", "V-", "VBAT", "VBUS", "VIN", "VOUT",
911 "+5V", "+3V3", "+3.3V", "+12V", "-12V", "+24V",
912 "0V", "EARTH", "CHASSIS"
913 };
914
915 if( globalPatterns.count( upperName ) > 0 )
916 return true;
917
918 // Check for voltage patterns like +1V8, +2V5, etc.
919 if( ( upperName[0] == '+' || upperName[0] == '-' ) && upperName.length() >= 3 )
920 {
921 bool hasDigit = false;
922 bool hasV = false;
923
924 for( char c : upperName.substr( 1 ) )
925 {
926 if( std::isdigit( c ) )
927 hasDigit = true;
928
929 if( c == 'V' )
930 hasV = true;
931 }
932
933 if( hasDigit && hasV )
934 return true;
935 }
936
937 return false;
938}
939
940} // namespace PADS_SCH
const char * name
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 SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:148
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:546
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:431
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:298
void SetTextAngleDegrees(double aOrientation)
Definition eda_text.h:150
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:423
Maps PADS attribute names to KiCad field names.
bool IsReferenceField(const std::string &aPadsAttr) const
Check if a PADS attribute maps to the Reference field.
std::string GetKiCadFieldName(const std::string &aPadsAttr) const
Get the KiCad field name for a PADS attribute.
bool IsFootprintField(const std::string &aPadsAttr) const
Check if a PADS attribute maps to the Footprint field.
bool IsStandardField(const std::string &aPadsAttr) const
Check if a PADS attribute maps to a standard KiCad field.
bool IsValueField(const std::string &aPadsAttr) const
Check if a PADS attribute maps to the Value field.
SCH_LINE * CreateWire(const WIRE_SEGMENT &aWire)
Create a single wire segment.
std::vector< VECTOR2I > findJunctionPoints(const std::vector< SCH_SIGNAL > &aSignals)
Find junction points where 3+ wire segments meet.
int CreateNetLabels(const std::vector< SCH_SIGNAL > &aSignals, SCH_SCREEN *aScreen, const std::set< std::string > &aSignalOpcIds, const std::set< std::string > &aSkipSignals={})
Create net labels for named signals.
int toKiCadY(double aPadsY) const
Convert PADS Y coordinate to KiCad Y, accounting for Y-axis inversion and page offset.
SCH_SHEET_PIN * CreateSheetPin(SCH_SHEET *aSheet, const std::string &aSignalName, int aPinIndex)
Create hierarchical sheet pin on a sheet symbol.
int CreateJunctions(const std::vector< SCH_SIGNAL > &aSignals, SCH_SCREEN *aScreen)
Create junctions at wire intersection points.
void ApplyPartAttributes(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Apply part attributes to a symbol instance.
PADS_SCH_SCHEMATIC_BUILDER(const PARAMETERS &aParams, SCHEMATIC *aSchematic)
void CreateTitleBlock(SCH_SCREEN *aScreen)
Create title block from parsed PADS parameters.
VECTOR2I CalculateSheetPosition(int aSheetIndex, int aTotalSheets) const
Calculate position for a sheet symbol on the parent sheet.
SPIN_STYLE computeLabelOrientation(const VECTOR2I &aLabelPos, const VECTOR2I &aAdjacentPos)
Compute label orientation from the wire direction at the label position.
int toKiCadUnits(double aPadsValue) const
Convert PADS coordinate to KiCad internal units.
int CreateCustomFields(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Create custom fields from non-standard PADS attributes.
VECTOR2I chooseLabelPosition(const SCH_SIGNAL &aSignal)
Choose best position for net label on a signal's wires.
SCH_LINE * CreateBusWire(const WIRE_SEGMENT &aWire)
Create a single bus wire segment.
SCH_HIERLABEL * CreateHierLabel(const std::string &aSignalName, const VECTOR2I &aPosition, SCH_SCREEN *aScreen)
Create hierarchical label in a sub-schematic.
void ApplyFieldSettings(SCH_SYMBOL *aSymbol, const PART_PLACEMENT &aPlacement)
Apply field visibility and position from PADS attribute settings.
int CreateWires(const std::vector< SCH_SIGNAL > &aSignals, SCH_SCREEN *aScreen)
Create wire segments from signal definitions and add to screen.
SCH_GLOBALLABEL * CreateNetLabel(const SCH_SIGNAL &aSignal, const VECTOR2I &aPosition, SPIN_STYLE aOrientation=SPIN_STYLE::RIGHT)
Create a global net label for a signal.
static bool IsGlobalSignal(const std::string &aSignalName, const std::set< int > &aSheetNumbers)
Check if a signal name represents a global signal.
int CreateBusWires(const std::vector< SCH_SIGNAL > &aSignals, SCH_SCREEN *aScreen)
Create bus wires and entries for bus signals.
wxString convertNetName(const std::string &aName) const
Convert a PADS net name for use as a KiCad label.
SCH_SHEET * CreateHierarchicalSheet(int aSheetNumber, int aTotalSheets, SCH_SHEET *aParentSheet, const wxString &aBaseFilename)
Create hierarchical sheet for a sub-schematic page.
VECTOR2I GetDefaultSheetSize() const
Get standard sheet size for a given sheet number.
static bool IsBusSignal(const std::string &aName)
Check if a signal name indicates a bus.
Holds all the data relating to one schematic.
Definition schematic.h:88
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:181
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:149
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:167
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void AddPin(SCH_SHEET_PIN *aSheetPin)
Add aSheetPin to the sheet.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
VECTOR2I GetPosition() const override
Definition sch_sheet.h:496
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Schematic symbol object.
Definition sch_symbol.h:76
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
void SetFootprintFieldText(const wxString &aFootprint)
VECTOR2I GetPosition() const override
Definition sch_symbol.h:867
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:41
void SetRevision(const wxString &aRevision)
Definition title_block.h:81
void SetComment(int aIdx, const wxString &aComment)
void SetTitle(const wxString &aTitle)
Definition title_block.h:58
void SetCompany(const wxString &aCompany)
Definition title_block.h:91
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:71
#define IS_NEW
New item, just created.
static const std::string KiCadSchematicFileExtension
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_BUS
Definition layer_ids.h:453
wxString ConvertInvertedNetName(const std::string &aNetName)
Convert a PADS net name to KiCad format, handling inverted signal notation.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
Common utilities and types for parsing PADS file formats.
@ L_BIDI
Definition sch_label.h:102
@ L_UNSPECIFIED
Definition sch_label.h:104
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
General schematic parameters from SCH and FIELDS sections.
Part instance from PART section.
std::map< std::string, std::string > attr_overrides
std::vector< PART_ATTRIBUTE > attributes
Signal (net) definition from CONNECTION and SIGNAL sections.
std::vector< WIRE_SEGMENT > wires
Wire segment connecting two endpoints through coordinate vertices.
@ USER
The field ID hasn't been set yet; field is invalid.
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
KIBIS_PIN * pin
VECTOR2I end
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.