KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_eagle.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) 2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Alejandro GarcĂ­a Montoro <[email protected]>
8 * @author Maciej Suminski <[email protected]>
9 * @author Russell Oliver <[email protected]>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 3
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
22 * with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
26
27#include <algorithm>
28#include <memory>
29#include <wx/filename.h>
30#include <wx/string.h>
31#include <wx/tokenzr.h>
32#include <wx/wfstream.h>
33#include <wx/txtstrm.h>
34#include <wx/mstream.h>
35#include <wx/xml/xml.h>
36
37#include <font/fontconfig.h>
38#include <reporter.h>
40#include <lib_id.h>
41#include <progress_reporter.h>
42#include <project.h>
44#include <project_sch.h>
45#include <sch_bus_entry.h>
46#include <sch_edit_frame.h>
48#include <sch_junction.h>
49#include <sch_label.h>
50#include <sch_marker.h>
51#include <sch_pin.h>
52#include <sch_screen.h>
53#include <sch_shape.h>
54#include <sch_sheet.h>
55#include <sch_sheet_path.h>
56#include <sch_sheet_pin.h>
57#include <sch_symbol.h>
58#include <schematic.h>
59#include <string_utils.h>
62
63
64// Eagle schematic axes are aligned with x increasing left to right and Y increasing bottom to top
65// KiCad schematic axes are aligned with x increasing left to right and Y increasing top to bottom.
66
67using namespace std;
68
72static const std::map<wxString, ELECTRICAL_PINTYPE> pinDirectionsMap = {
73 { wxT( "sup" ), ELECTRICAL_PINTYPE::PT_POWER_IN },
74 { wxT( "pas" ), ELECTRICAL_PINTYPE::PT_PASSIVE },
75 { wxT( "out" ), ELECTRICAL_PINTYPE::PT_OUTPUT },
76 { wxT( "in" ), ELECTRICAL_PINTYPE::PT_INPUT },
77 { wxT( "nc" ), ELECTRICAL_PINTYPE::PT_NC },
78 { wxT( "io" ), ELECTRICAL_PINTYPE::PT_BIDI },
80 { wxT( "hiz" ), ELECTRICAL_PINTYPE::PT_TRISTATE },
81 { wxT( "pwr" ), ELECTRICAL_PINTYPE::PT_POWER_IN },
82};
83
84
86static BOX2I getSheetBbox( SCH_SHEET* aSheet )
87{
88 BOX2I bbox;
89
90 for( SCH_ITEM* item : aSheet->GetScreen()->Items() )
91 bbox.Merge( item->GetBoundingBox() );
92
93 return bbox;
94}
95
96
98static inline wxString extractNetName( const wxString& aPinName )
99{
100 return aPinName.BeforeFirst( '@' );
101}
102
103
108
109
111{
112 SCH_SHEET* currentSheet = m_sheetPath.Last();
113 wxCHECK( currentSheet, nullptr );
114 return currentSheet->GetScreen();
115}
116
117
119{
120 if( m_libName.IsEmpty() )
121 {
122 // Try to come up with a meaningful name
123 m_libName = m_schematic->Project().GetProjectName();
124
125 if( m_libName.IsEmpty() )
126 {
127 wxFileName fn( m_rootSheet->GetFileName() );
128 m_libName = fn.GetName();
129 }
130
131 if( m_libName.IsEmpty() )
132 m_libName = wxT( "noname" );
133
134 m_libName += wxT( "-eagle-import" );
136 }
137
138 return m_libName;
139}
140
141
143{
144 wxFileName fn;
145
146 wxCHECK( m_schematic, fn );
147
148 fn.Assign( m_schematic->Project().GetProjectPath(), getLibName(),
150
151 return fn;
152}
153
154
155void SCH_IO_EAGLE::loadLayerDefs( const std::vector<std::unique_ptr<ELAYER>>& aLayers )
156{
157 // match layers based on their names
158 for( const std::unique_ptr<ELAYER>& elayer : aLayers )
159 {
176
177 switch ( elayer->number)
178 {
179 case 91:
180 m_layerMap[elayer->number] = LAYER_WIRE;
181 break;
182 case 92:
183 m_layerMap[elayer->number] = LAYER_BUS;
184 break;
185 case 97:
186 case 98:
187 m_layerMap[elayer->number] = LAYER_NOTES;
188 break;
189
190 default:
191 break;
192 }
193 }
194}
195
196
198{
199 auto it = m_layerMap.find( aEagleLayer );
200 return it == m_layerMap.end() ? LAYER_NOTES : it->second;
201}
202
203
204// Return the KiCad symbol orientation based on eagle rotation degrees.
206{
207 int roti = int( eagleDegrees );
208
209 switch( roti )
210 {
211 case 0: return SYM_ORIENT_0;
212 case 90: return SYM_ORIENT_90;
213 case 180: return SYM_ORIENT_180;
214 case 270: return SYM_ORIENT_270;
215
216 default:
217 wxASSERT_MSG( false, wxString::Format( wxT( "Unhandled orientation (%d degrees)" ),
218 roti ) );
219 return SYM_ORIENT_0;
220 }
221}
222
223
224// Calculate text alignment based on the given Eagle text alignment parameters.
225static void eagleToKicadAlignment( EDA_TEXT* aText, int aEagleAlignment, int aRelDegress,
226 bool aMirror, bool aSpin, int aAbsDegress )
227{
228 int align = aEagleAlignment;
229
230 if( aRelDegress == 90 )
231 {
233 }
234 else if( aRelDegress == 180 )
235 {
236 align = -align;
237 }
238 else if( aRelDegress == 270 )
239 {
241 align = -align;
242 }
243
244 if( aMirror == true )
245 {
246 if( aAbsDegress == 90 || aAbsDegress == 270 )
247 {
248 if( align == ETEXT::BOTTOM_RIGHT )
249 align = ETEXT::TOP_RIGHT;
250 else if( align == ETEXT::BOTTOM_LEFT )
251 align = ETEXT::TOP_LEFT;
252 else if( align == ETEXT::TOP_LEFT )
253 align = ETEXT::BOTTOM_LEFT;
254 else if( align == ETEXT::TOP_RIGHT )
255 align = ETEXT::BOTTOM_RIGHT;
256 }
257 else if( aAbsDegress == 0 || aAbsDegress == 180 )
258 {
259 if( align == ETEXT::BOTTOM_RIGHT )
260 align = ETEXT::BOTTOM_LEFT;
261 else if( align == ETEXT::BOTTOM_LEFT )
262 align = ETEXT::BOTTOM_RIGHT;
263 else if( align == ETEXT::TOP_LEFT )
264 align = ETEXT::TOP_RIGHT;
265 else if( align == ETEXT::TOP_RIGHT )
266 align = ETEXT::TOP_LEFT;
267 else if( align == ETEXT::CENTER_LEFT )
268 align = ETEXT::CENTER_RIGHT;
269 else if( align == ETEXT::CENTER_RIGHT )
270 align = ETEXT::CENTER_LEFT;
271 }
272 }
273
274 switch( align )
275 {
276 case ETEXT::CENTER:
279 break;
280
284 break;
285
289 break;
290
294 break;
295
296 case ETEXT::TOP_LEFT:
299 break;
300
301 case ETEXT::TOP_RIGHT:
304 break;
305
309 break;
310
314 break;
315
319 break;
320
321 default:
324 break;
325 }
326}
327
328
329SCH_IO_EAGLE::SCH_IO_EAGLE() : SCH_IO( wxS( "EAGLE" ) ),
330 m_rootSheet( nullptr ),
331 m_schematic( nullptr ),
332 m_sheetIndex( 1 )
333{
335}
336
337
341
342
344{
345 return 0;
346}
347
348
349SCH_SHEET* SCH_IO_EAGLE::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
350 SCH_SHEET* aAppendToMe,
351 const std::map<std::string, UTF8>* aProperties )
352{
353 wxASSERT( !aFileName || aSchematic != nullptr );
354
355 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
357
358 m_filename = aFileName;
359 m_schematic = aSchematic;
360
362 {
363 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
364
365 if( !m_progressReporter->KeepRefreshing() )
366 THROW_IO_ERROR( ( "Open canceled by user." ) );
367 }
368
369 // Load the document
370 wxXmlDocument xmlDocument = loadXmlDocument( m_filename.GetFullPath() );
371
372 // Retrieve the root as current node
373 wxXmlNode* currentNode = xmlDocument.GetRoot();
374
376 m_progressReporter->SetNumPhases( static_cast<int>( GetNodeCount( currentNode ) ) );
377
378 wxFileName newFilename( m_filename );
379 newFilename.SetExt( FILEEXT::KiCadSchematicFileExtension );
380
381 // Owns the temporary VR for the non-append path so it is freed when this scope exits.
382 // The actual schematic VR will be created by SetTopLevelSheets() inside loadSchematic().
383 unique_ptr<SCH_SHEET> tempVROwner;
384
385 if( aAppendToMe )
386 {
387 wxCHECK_MSG( aSchematic->IsValid(), nullptr,
388 wxT( "Can't append to a schematic with no root!" ) );
389
390 m_rootSheet = &aSchematic->Root();
391
392 // We really should be passing the SCH_SHEET_PATH object to the aAppendToMe attribute
393 // instead of the SCH_SHEET. The full path is needed to properly generate instance
394 // data.
395 SCH_SHEET_LIST hierarchy( m_rootSheet );
396
397 for( const SCH_SHEET_PATH& sheetPath : hierarchy )
398 {
399 if( sheetPath.Last() == aAppendToMe )
400 {
401 m_sheetPath = sheetPath;
402 break;
403 }
404 }
405 }
406 else
407 {
408 // Create a temporary local VR used only to anchor m_sheetPath during loading.
409 // loadSchematic() will call SetTopLevelSheets() with the real Eagle pages, which
410 // creates the actual schematic VR and re-parents the pages to it.
411 tempVROwner = std::make_unique<SCH_SHEET>( aSchematic );
412 m_rootSheet = tempVROwner.get();
413 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
414 }
415
416 if( !m_rootSheet->GetScreen() )
417 {
418 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
419 screen->SetFileName( newFilename.GetFullPath() );
420 m_rootSheet->SetScreen( screen );
421
422 // Virtual root sheet UUID must be nil since all Eagle pages are loaded as subsheets.
423 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
424
425 // There is always at least a root sheet.
426 m_sheetPath.push_back( m_rootSheet );
427 }
428
430 LIBRARY_TABLE* table = adapter->ProjectTable().value_or( nullptr );
431 wxCHECK_MSG( table, nullptr, "Could not load symbol lib table." );
432
433 m_pi.reset( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
434
437 if( !table->HasRow( getLibName() ) )
438 {
439 // Create a new empty symbol library.
440 m_pi->CreateLibrary( getLibFileName().GetFullPath() );
441 wxString libTableUri = wxT( "${KIPRJMOD}/" ) + getLibFileName().GetFullName();
442
443 // Add the new library to the project symbol library table.
444 LIBRARY_TABLE_ROW& row = table->InsertRow();
445 row.SetNickname( getLibName() );
446 row.SetURI( libTableUri );
447 row.SetType( "KiCad" );
448
449 table->Save();
450
451 adapter->LoadOne( getLibName() );
452 }
453
454 m_eagleDoc = std::make_unique<EAGLE_DOC>( currentNode, this );
455
456 // If the attribute is found, store the Eagle version;
457 // otherwise, store the dummy "0.0" version.
458 m_version = ( m_eagleDoc->version.IsEmpty() ) ? wxString( wxS( "0.0" ) ) : m_eagleDoc->version;
459
460 // Load drawing
461 loadDrawing( m_eagleDoc->drawing );
462
463 if( !aAppendToMe )
464 m_rootSheet = &aSchematic->Root();
465
466 m_pi->SaveLibrary( getLibFileName().GetFullPath() );
467
468 SCH_SCREENS allSheets( m_rootSheet );
469 allSheets.UpdateSymbolLinks( &LOAD_INFO_REPORTER::GetInstance() ); // Update all symbol library links for all sheets.
470
471 return m_rootSheet;
472}
473
474
475void SCH_IO_EAGLE::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
476 const wxString& aLibraryPath,
477 const std::map<std::string, UTF8>* aProperties )
478{
479 m_filename = aLibraryPath;
480 m_libName = m_filename.GetName();
481
482 ensureLoadedLibrary( aLibraryPath );
483
484 auto it = m_eagleLibs.find( m_libName );
485
486 if( it != m_eagleLibs.end() )
487 {
488 for( const auto& [symName, libSymbol] : it->second.KiCadSymbols )
489 aSymbolNameList.push_back( symName );
490 }
491}
492
493
494void SCH_IO_EAGLE::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
495 const wxString& aLibraryPath,
496 const std::map<std::string, UTF8>* aProperties )
497{
498 m_filename = aLibraryPath;
499 m_libName = m_filename.GetName();
500
501 ensureLoadedLibrary( aLibraryPath );
502
503 auto it = m_eagleLibs.find( m_libName );
504
505 if( it != m_eagleLibs.end() )
506 {
507 for( const auto& [symName, libSymbol] : it->second.KiCadSymbols )
508 aSymbolList.push_back( libSymbol.get() );
509 }
510}
511
512
513LIB_SYMBOL* SCH_IO_EAGLE::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
514 const std::map<std::string, UTF8>* aProperties )
515{
516 m_filename = aLibraryPath;
517 m_libName = m_filename.GetName();
518
519 ensureLoadedLibrary( aLibraryPath );
520
521 auto it = m_eagleLibs.find( m_libName );
522
523 if( it != m_eagleLibs.end() )
524 {
525 auto it2 = it->second.KiCadSymbols.find( aAliasName );
526
527 if( it2 != it->second.KiCadSymbols.end() )
528 return it2->second.get();
529 }
530
531 return nullptr;
532}
533
534
535long long SCH_IO_EAGLE::getLibraryTimestamp( const wxString& aLibraryPath ) const
536{
537 wxFileName fn( aLibraryPath );
538
539 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
540 return fn.GetModificationTime().GetValue().GetValue();
541 else
542 return 0;
543}
544
545
546void SCH_IO_EAGLE::ensureLoadedLibrary( const wxString& aLibraryPath )
547{
548 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
549 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
550
551 if( m_eagleLibs.find( m_libName ) != m_eagleLibs.end() )
552 {
553 wxCHECK( m_timestamps.count( m_libName ), /*void*/ );
554
555 if( m_timestamps.at( m_libName ) == getLibraryTimestamp( aLibraryPath ) )
556 return;
557 }
558
560 {
561 m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aLibraryPath ) );
562
563 if( !m_progressReporter->KeepRefreshing() )
564 THROW_IO_ERROR( ( "Open canceled by user." ) );
565 }
566
567 // Load the document
568 wxXmlDocument xmlDocument = loadXmlDocument( m_filename.GetFullPath() );
569
570 // Retrieve the root as current node
571 std::unique_ptr<EAGLE_DOC> doc = std::make_unique<EAGLE_DOC>( xmlDocument.GetRoot(), this );
572
573 // If the attribute is found, store the Eagle version;
574 // otherwise, store the dummy "0.0" version.
575 m_version = ( doc->version.IsEmpty() ) ? wxString( wxS( "0.0" ) ) : doc->version;
576
577 // Load drawing
578 loadDrawing( doc->drawing );
579
580 // Remember timestamp
581 m_timestamps[m_libName] = getLibraryTimestamp( aLibraryPath );
582}
583
584
585wxXmlDocument SCH_IO_EAGLE::loadXmlDocument( const wxString& aFileName )
586{
587 wxXmlDocument xmlDocument;
588 wxFFileInputStream stream( m_filename.GetFullPath() );
589
590 if( !stream.IsOk() )
591 {
593 wxString::Format( _( "Unable to read file '%s'." ), m_filename.GetFullPath() ) );
594 }
595
596 // read first line to check for Eagle XML format file
597 wxTextInputStream text( stream );
598 wxString line = text.ReadLine();
599
600 if( !line.StartsWith( wxT( "<?xml" ) ) && !line.StartsWith( wxT( "<!--" ) )
601 && !line.StartsWith( wxT( "<eagle " ) ) )
602 {
603 THROW_IO_ERROR( wxString::Format( _( "'%s' is an Eagle binary-format file; "
604 "only Eagle XML-format files can be imported." ),
605 m_filename.GetFullPath() ) );
606 }
607
608#if wxCHECK_VERSION( 3, 3, 0 )
609 wxXmlParseError err;
610
611 if( !xmlDocument.Load( stream, wxXMLDOC_NONE, &err ) )
612 {
613 if( err.message == wxS( "no element found" ) )
614 {
615 // Some files don't have the correct header, throwing off the xml parser
616 // So prepend the correct header
617 wxMemoryOutputStream memOutput;
618
619 wxString header;
620 header << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
621 header << "<!DOCTYPE eagle SYSTEM \"eagle.dtd\">\n";
622
623 wxScopedCharBuffer headerBuf = header.utf8_str();
624 memOutput.Write( headerBuf.data(), headerBuf.length() );
625
626 wxFFileInputStream stream2( m_filename.GetFullPath() );
627 memOutput.Write( stream2 );
628
629 wxMemoryInputStream memInput( memOutput );
630
631 if( !xmlDocument.Load( memInput, wxXMLDOC_NONE, &err ) )
632 {
633 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ), m_filename.GetFullPath() ) );
634 }
635 }
636 else
637 {
638 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'.\n'%s' at line %d, column %d, offset %d" ),
639 m_filename.GetFullPath(), err.message, err.line, err.column,
640 err.offset ) );
641 }
642 }
643#else
644 if( !xmlDocument.Load( stream ) )
645 {
646 // Some files don't have the correct header, throwing off the xml parser
647 // So prepend the correct header
648 wxMemoryOutputStream memOutput;
649
650 wxString header;
651 header << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
652 header << "<!DOCTYPE eagle SYSTEM \"eagle.dtd\">\n";
653
654 wxScopedCharBuffer headerBuf = header.utf8_str();
655 memOutput.Write( headerBuf.data(), headerBuf.length() );
656
657 wxFFileInputStream stream2( m_filename.GetFullPath() );
658 memOutput.Write( stream2 );
659
660 wxMemoryInputStream memInput( memOutput );
661
662 if( !xmlDocument.Load( memInput ) )
663 {
664 THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ), m_filename.GetFullPath() ) );
665 }
666 }
667#endif
668
669 return xmlDocument;
670}
671
672
673void SCH_IO_EAGLE::loadDrawing( const std::unique_ptr<EDRAWING>& aDrawing )
674{
675 wxCHECK( aDrawing, /* void */ );
676
677 loadLayerDefs( aDrawing->layers );
678
679 if( aDrawing->library )
680 {
682 elib.name = m_libName;
683
684 loadLibrary( &aDrawing->library.value(), &elib );
685 }
686
687 if( aDrawing->schematic )
688 loadSchematic( *aDrawing->schematic );
689}
690
691
692void SCH_IO_EAGLE::countNets( const ESCHEMATIC& aSchematic )
693{
694 for( const std::unique_ptr<ESHEET>& esheet : aSchematic.sheets )
695 {
696 for( const std::unique_ptr<ENET>& enet : esheet->nets )
697 {
698 wxString netName = enet->netname;
699
700 if( m_netCounts.count( netName ) )
701 m_netCounts[netName] = m_netCounts[netName] + 1;
702 else
703 m_netCounts[netName] = 1;
704 }
705 }
706
707 for( const auto& [modname, emodule] : aSchematic.modules )
708 {
709 for( const std::unique_ptr<ESHEET>& esheet : emodule->sheets )
710 {
711 for( const std::unique_ptr<ENET>& enet : esheet->nets )
712 {
713 wxString netName = enet->netname;
714
715 if( m_netCounts.count( netName ) )
716 m_netCounts[netName] = m_netCounts[netName] + 1;
717 else
718 m_netCounts[netName] = 1;
719 }
720 }
721 }
722}
723
724
726{
727 // Map all children into a readable dictionary
728 if( aSchematic.sheets.empty() )
729 return;
730
731 // N.B. Eagle parts are case-insensitive in matching but we keep the display case
732 for( const auto& [name, epart] : aSchematic.parts )
733 m_partlist[name.Upper()] = epart.get();
734
735 for( const auto& [modName, emodule] : aSchematic.modules )
736 {
737 for( const auto& [partName, epart] : emodule->parts )
738 m_partlist[partName.Upper()] = epart.get();
739 }
740
741 if( !aSchematic.libraries.empty() )
742 {
743 for( const auto& [libName, elibrary] : aSchematic.libraries )
744 {
745 EAGLE_LIBRARY* elib = &m_eagleLibs[elibrary->GetName()];
746 elib->name = elibrary->GetName();
747
748 loadLibrary( elibrary.get(), &m_eagleLibs[elibrary->GetName()] );
749 }
750
751 m_pi->SaveLibrary( getLibFileName().GetFullPath() );
752 }
753
754 // Count how many sheets each named net appears on. Used by the fallback-label path
755 // in loadSegments to decide whether to add an extra label on otherwise-unlabelled
756 // segments of nets that span multiple sheets.
757 countNets( aSchematic );
758
759 // Create all Eagle pages as top-level sheets (direct children of the virtual root).
760 // Collect them first so we can atomically replace any spurious default sheet created
761 // during schematic construction with exactly the set of real Eagle pages.
762 std::vector<SCH_SHEET*> eaglePages;
763 eaglePages.reserve( aSchematic.sheets.size() );
764
765 for( const std::unique_ptr<ESHEET>& esheet : aSchematic.sheets )
766 {
767 // Eagle schematics are never more than one sheet deep so the parent sheet is
768 // always the root sheet.
769 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>( m_rootSheet );
770 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
771 sheet->SetScreen( screen );
772
773 wxCHECK2( sheet && screen, continue );
774
775 wxString pageNo = wxString::Format( wxT( "%d" ), m_sheetIndex );
776
777 m_sheetPath.push_back( sheet.get() );
778 loadSheet( esheet );
779
780 m_sheetPath.SetPageNumber( pageNo );
781 m_sheetPath.pop_back();
782
783 eaglePages.push_back( sheet.release() );
784
785 m_sheetIndex++;
786 }
787
788 if( !eaglePages.empty() )
789 {
790 // In the append path m_rootSheet is already the schematic's VR. Use
791 // AddTopLevelSheet to avoid discarding sheets already in the target.
792 // In the fresh-import path m_rootSheet is a temporary local VR, so we use
793 // SetTopLevelSheets to atomically replace any spurious default sheet with
794 // exactly the Eagle pages.
795 if( m_rootSheet == &m_schematic->Root() )
796 {
797 for( SCH_SHEET* page : eaglePages )
798 m_schematic->AddTopLevelSheet( page );
799 }
800 else
801 {
802 m_schematic->SetTopLevelSheets( eaglePages );
803 }
804 }
805
806 // Handle the missing symbol units that need to be instantiated
807 // to create the missing implicit connections
808
809 // Calculate the already placed items bounding box and the page size to determine
810 // placement for the new symbols
811 SCH_SHEET* schematicRoot = &m_schematic->Root();
812
813 struct MISSING_UNIT_PLACEMENT
814 {
815 VECTOR2I pageSizeIU;
816 BOX2I sheetBbox;
817 VECTOR2I newCmpPosition;
818 int maxY;
819 SCH_SHEET_PATH sheetpath;
820 SCH_SCREEN* screen;
821 };
822
823 std::map<SCH_SCREEN*, MISSING_UNIT_PLACEMENT> placements;
824
825 for( auto& cmp : m_missingCmps )
826 {
827 const SCH_SYMBOL* origSymbol = cmp.second.cmp;
828
829 for( auto& unitEntry : cmp.second.units )
830 {
831 if( unitEntry.second == false )
832 continue; // unit has been already processed
833
834 // Instantiate the missing symbol unit
835 int unit = unitEntry.first;
836 const wxString reference = origSymbol->GetField( FIELD_T::REFERENCE )->GetText();
837 std::unique_ptr<SCH_SYMBOL> symbol( (SCH_SYMBOL*) origSymbol->Duplicate( IGNORE_PARENT_GROUP ) );
838
839 SCH_SCREEN* targetScreen = cmp.second.screen;
840
841 if( !targetScreen )
842 {
843 SCH_SHEET* fallbackSheet = m_schematic->GetTopLevelSheet( 0 );
844
845 if( fallbackSheet )
846 targetScreen = fallbackSheet->GetScreen();
847 else
848 targetScreen = schematicRoot->GetScreen();
849 }
850
851 auto placementIt = placements.find( targetScreen );
852
853 if( placementIt == placements.end() )
854 {
855 MISSING_UNIT_PLACEMENT placement;
856 placement.screen = targetScreen;
857 placement.pageSizeIU = targetScreen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
858 schematicRoot->LocatePathOfScreen( targetScreen, &placement.sheetpath );
859
860 SCH_SHEET* targetSheet = placement.sheetpath.Last();
861
862 if( targetSheet )
863 placement.sheetBbox = getSheetBbox( targetSheet );
864
865 placement.newCmpPosition = VECTOR2I( placement.sheetBbox.GetLeft(),
866 placement.sheetBbox.GetBottom() );
867 placement.maxY = placement.sheetBbox.GetY();
868 placementIt = placements.emplace( targetScreen, placement ).first;
869 }
870
871 MISSING_UNIT_PLACEMENT& placement = placementIt->second;
872
873 symbol->SetUnitSelection( &placement.sheetpath, unit );
874 symbol->SetUnit( unit );
875 symbol->SetOrientation( 0 );
876 symbol->AddHierarchicalReference( placement.sheetpath.Path(), reference, unit );
877
878 // Calculate the placement position
879 BOX2I cmpBbox = symbol->GetBoundingBox();
880 int posY = placement.newCmpPosition.y + cmpBbox.GetHeight();
881 symbol->SetPosition( VECTOR2I( placement.newCmpPosition.x, posY ) );
882 placement.newCmpPosition.x += cmpBbox.GetWidth();
883 placement.maxY = std::max( placement.maxY, posY );
884
885 if( placement.newCmpPosition.x >= placement.pageSizeIU.x ) // reached the page boundary?
886 placement.newCmpPosition = VECTOR2I( placement.sheetBbox.GetLeft(),
887 placement.maxY ); // then start a new row
888
889 // Add the global net labels to recreate the implicit connections
890 addImplicitConnections( symbol.get(), placement.screen, false );
891 placement.screen->Append( symbol.release() );
892 }
893 }
894
895 m_missingCmps.clear();
896}
897
898
899void SCH_IO_EAGLE::loadSheet( const std::unique_ptr<ESHEET>& aSheet )
900{
901 SCH_SHEET* sheet = getCurrentSheet();
902 SCH_SCREEN* screen = getCurrentScreen();
903
904 wxCHECK( sheet && screen, /* void */ );
905
906 if( m_modules.empty() )
907 {
908 std::string filename;
909
910 filename = wxString::Format( wxT( "%s_%d" ), m_filename.GetName(), m_sheetIndex );
911
912 if( aSheet->description )
913 sheet->SetName( aSheet->description.value().text );
914 else
915 sheet->SetName( filename );
916
917 ReplaceIllegalFileNameChars( filename );
918 replace( filename.begin(), filename.end(), ' ', '_' );
919
920 // Use the project directory so saved pages land alongside the project file,
921 // not in the Eagle source directory.
922 wxFileName fn;
923 fn.SetPath( m_schematic->Project().GetProjectPath() );
924 fn.SetName( filename );
926
927 sheet->SetFileName( fn.GetFullName() );
928 screen->SetFileName( fn.GetFullPath() );
929 }
930
931 for( const auto& [name, moduleinst] : aSheet->moduleinsts )
932 loadModuleInstance( moduleinst );
933
934 sheet->AutoplaceFields( screen, AUTOPLACE_AUTO );
935
936 if( aSheet->plain )
937 {
938 for( const std::unique_ptr<EPOLYGON>& epoly : aSheet->plain->polygons )
939 screen->Append( loadPolyLine( epoly ) );
940
941 for( const std::unique_ptr<EWIRE>& ewire : aSheet->plain->wires )
942 {
943 SEG endpoints;
944 screen->Append( loadWire( ewire, endpoints ) );
945 }
946
947 for( const std::unique_ptr<ETEXT>& etext : aSheet->plain->texts )
948 screen->Append( loadPlainText( etext ) );
949
950 for( const std::unique_ptr<ECIRCLE>& ecircle : aSheet->plain->circles )
951 screen->Append( loadCircle( ecircle ) );
952
953 for( const std::unique_ptr<ERECT>& erectangle : aSheet->plain->rectangles )
954 screen->Append( loadRectangle( erectangle ) );
955
956 for( const std::unique_ptr<EFRAME>& eframe : aSheet->plain->frames )
957 {
958 std::vector<SCH_ITEM*> frameItems;
959
960 loadFrame( eframe, frameItems );
961
962 for( SCH_ITEM* item : frameItems )
963 screen->Append( item );
964 }
965
966 // Holes and splines currently not handled. Not sure hole has any meaning in scheamtics.
967 }
968
969 for( const std::unique_ptr<EINSTANCE>& einstance : aSheet->instances )
970 loadInstance( einstance, ( m_modules.size() ) ? m_modules.back()->parts
971 : m_eagleDoc->drawing->schematic->parts );
972
973 // Loop through all buses
974 // From the DTD: "Buses receive names which determine which signals they include.
975 // A bus is a drawing object. It does not create any electrical connections.
976 // These are always created by means of the nets and their names."
977 for( const std::unique_ptr<EBUS>& ebus : aSheet->busses )
978 {
979 // Get the bus name
980 wxString busName = translateEagleBusName( ebus->name );
981
982 // Load segments of this bus
983 loadSegments( ebus->segments, busName, wxString(), /* aIsBus */ true );
984 }
985
986 for( const std::unique_ptr<ENET>& enet : aSheet->nets )
987 {
988 // Get the net name and class
989 wxString netName = enet->netname;
990 wxString netClass = wxString::Format( wxS( "%i" ), enet->netcode );
991
992 // Load segments of this net
993 loadSegments( enet->segments, netName, netClass );
994 }
995
996 adjustNetLabels(); // needs to be called before addBusEntries()
998
999 // Calculate the new sheet size.
1000 BOX2I sheetBoundingBox = getSheetBbox( sheet );
1001 VECTOR2I targetSheetSize = sheetBoundingBox.GetSize();
1002 targetSheetSize += VECTOR2I( schIUScale.MilsToIU( 1500 ), schIUScale.MilsToIU( 1500 ) );
1003
1004 // Get current Eeschema sheet size.
1005 VECTOR2I pageSizeIU = screen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
1006 PAGE_INFO pageInfo = screen->GetPageSettings();
1007
1008 // Increase if necessary
1009 if( pageSizeIU.x < targetSheetSize.x )
1010 pageInfo.SetWidthMils( schIUScale.IUToMils( targetSheetSize.x ) );
1011
1012 if( pageSizeIU.y < targetSheetSize.y )
1013 pageInfo.SetHeightMils( schIUScale.IUToMils( targetSheetSize.y ) );
1014
1015 // Set the new sheet size.
1016 screen->SetPageSettings( pageInfo );
1017
1018 pageSizeIU = screen->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
1019 VECTOR2I sheetcentre( pageSizeIU.x / 2, pageSizeIU.y / 2 );
1020 VECTOR2I itemsCentre = sheetBoundingBox.Centre();
1021
1022 // round the translation to nearest 100mil to place it on the grid.
1023 VECTOR2I translation = sheetcentre - itemsCentre;
1024 translation.x = translation.x - translation.x % schIUScale.MilsToIU( 100 );
1025 translation.y = translation.y - translation.y % schIUScale.MilsToIU( 100 );
1026
1027 // Add global net labels for the named power input pins in this sheet
1028 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
1029 {
1030 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1031 addImplicitConnections( symbol, screen, true );
1032 }
1033
1034 m_connPoints.clear();
1035
1036 // Translate the items.
1037 std::vector<SCH_ITEM*> allItems;
1038
1039 std::copy( screen->Items().begin(), screen->Items().end(), std::back_inserter( allItems ) );
1040
1041 for( SCH_ITEM* item : allItems )
1042 {
1043 item->SetPosition( item->GetPosition() + translation );
1044
1045 // We don't read positions of Eagle label fields (primarily intersheet refs), so we
1046 // need to autoplace them after applying the translation.
1047 if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( item ) )
1048 label->AutoplaceFields( screen, AUTOPLACE_AUTO );
1049
1050 item->ClearFlags();
1051 screen->Update( item );
1052 }
1053}
1054
1055
1056void SCH_IO_EAGLE::loadModuleInstance( const std::unique_ptr<EMODULEINST>& aModuleInstance )
1057{
1058 SCH_SHEET* currentSheet = getCurrentSheet();
1059 SCH_SCREEN* currentScreen = getCurrentScreen();
1060
1061 wxCHECK( currentSheet &&currentScreen, /* void */ );
1062
1063 m_sheetIndex++;
1064
1065 // Eagle document has already be checked for drawing and schematic nodes so this
1066 // should not segfault.
1067 auto it = m_eagleDoc->drawing->schematic->modules.find( aModuleInstance->moduleinst );
1068
1069 // Find the module referenced by the module instance.
1070 if( it == m_eagleDoc->drawing->schematic->modules.end() )
1071 {
1072 THROW_IO_ERROR( wxString::Format( _( "No module instance '%s' found in schematic "
1073 "file:\n%s" ),
1074 aModuleInstance->name, m_filename.GetFullPath() ) );
1075 }
1076
1077 wxFileName fn = m_filename;
1078 fn.SetName( aModuleInstance->moduleinst );
1080
1081 VECTOR2I portExtWireEndpoint;
1082 VECTOR2I size( it->second->dx.ToSchUnits(), it->second->dy.ToSchUnits() );
1083
1084 int halfX = KiROUND( size.x / 2.0 );
1085 int halfY = KiROUND( size.y / 2.0 );
1086 int portExtWireLength = schIUScale.mmToIU( 5.08 );
1087 VECTOR2I pos( aModuleInstance->x.ToSchUnits() - halfX,
1088 -aModuleInstance->y.ToSchUnits() - halfY );
1089
1090 std::unique_ptr<SCH_SHEET> newSheet = std::make_unique<SCH_SHEET>( currentSheet, pos, size );
1091
1092 // The Eagle module for this instance (SCH_SCREEN in KiCad) may have already been loaded.
1093 SCH_SCREEN* newScreen = nullptr;
1094 SCH_SCREENS schFiles( m_rootSheet );
1095
1096 for( SCH_SCREEN* schFile = schFiles.GetFirst(); schFile; schFile = schFiles.GetNext() )
1097 {
1098 if( schFile->GetFileName() == fn.GetFullPath() )
1099 {
1100 newScreen = schFile;
1101 break;
1102 }
1103 }
1104
1105 bool isNewSchFile = ( newScreen == nullptr );
1106
1107 if( !newScreen )
1108 {
1109 newScreen = new SCH_SCREEN( m_schematic );
1110 newScreen->SetFileName( fn.GetFullPath() );
1111 }
1112
1113 wxCHECK( newSheet && newScreen, /* void */ );
1114
1115 newSheet->SetScreen( newScreen );
1116 newSheet->SetFileName( fn.GetFullName() );
1117 newSheet->SetName( aModuleInstance->name );
1118
1119 for( const auto& [portName, port] : it->second->ports )
1120 {
1121 VECTOR2I pinPos( 0, 0 );
1122 int pinOffset = port->coord.ToSchUnits();
1124
1125 if( port->side == "left" )
1126 {
1127 side = SHEET_SIDE::LEFT;
1128 pinPos.x = pos.x;
1129 pinPos.y = pos.y + halfY - pinOffset;
1130 portExtWireEndpoint = pinPos;
1131 portExtWireEndpoint.x -= portExtWireLength;
1132 }
1133 else if( port->side == "right" )
1134 {
1135 side = SHEET_SIDE::RIGHT;
1136 pinPos.x = pos.x + size.x;
1137 pinPos.y = pos.y + halfY - pinOffset;
1138 portExtWireEndpoint = pinPos;
1139 portExtWireEndpoint.x += portExtWireLength;
1140 }
1141 else if( port->side == "top" )
1142 {
1143 side = SHEET_SIDE::TOP;
1144 pinPos.x = pos.x + halfX + pinOffset;
1145 pinPos.y = pos.y;
1146 portExtWireEndpoint = pinPos;
1147 portExtWireEndpoint.y -= portExtWireLength;
1148 }
1149 else if( port->side == "bottom" )
1150 {
1151 side = SHEET_SIDE::BOTTOM;
1152 pinPos.x = pos.x + halfX + pinOffset;
1153 pinPos.y = pos.y + size.y;
1154 portExtWireEndpoint = pinPos;
1155 portExtWireEndpoint.y += portExtWireLength;
1156 }
1157
1158 SCH_LINE* portExtWire = new SCH_LINE( pinPos, LAYER_WIRE );
1159 portExtWire->SetEndPoint( portExtWireEndpoint );
1160 currentScreen->Append( portExtWire );
1161
1163
1164 if( port->direction )
1165 {
1166 if( *port->direction == "in" )
1167 pinType = LABEL_FLAG_SHAPE::L_INPUT;
1168 else if( *port->direction == "out" )
1170 else if( *port->direction == "io" )
1171 pinType = LABEL_FLAG_SHAPE::L_BIDI;
1172 else if( *port->direction == "hiz" )
1174 else
1176
1177 // KiCad does not support passive, power, open collector, or no-connect sheet
1178 // pins that Eagle ports support. They are set to unspecified to minimize
1179 // ERC issues.
1180 }
1181
1182 SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( newSheet.get(), VECTOR2I( 0, 0 ), portName );
1183
1184 sheetPin->SetShape( pinType );
1185 sheetPin->SetPosition( pinPos );
1186 sheetPin->SetSide( side );
1187 newSheet->AddPin( sheetPin );
1188 }
1189
1190 wxString pageNo = wxString::Format( wxT( "%d" ), m_sheetIndex );
1191
1192 newSheet->SetParent( currentSheet );
1193 m_sheetPath.push_back( newSheet.get() );
1194 m_sheetPath.SetPageNumber( pageNo );
1195 currentScreen->Append( newSheet.release() );
1196
1197 m_modules.push_back( it->second.get() );
1198 m_moduleInstances.push_back( aModuleInstance.get() );
1199
1200 // Do not reload shared modules that are already loaded.
1201 if( isNewSchFile )
1202 {
1203 for( const std::unique_ptr<ESHEET>& esheet : it->second->sheets )
1204 loadSheet( esheet );
1205 }
1206 else
1207 {
1208 // Add instances for shared schematics.
1209 wxString refPrefix;
1210
1211 for( const EMODULEINST* emoduleInst : m_moduleInstances )
1212 {
1213 wxCHECK2( emoduleInst, continue );
1214
1215 refPrefix += emoduleInst->name + wxS( ":" );
1216 }
1217
1218 SCH_SCREEN* sharedScreen = m_sheetPath.LastScreen();
1219
1220 if( sharedScreen )
1221 {
1222 for( SCH_ITEM* schItem : sharedScreen->Items().OfType( SCH_SYMBOL_T ) )
1223 {
1224 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( schItem );
1225
1226 wxCHECK2( symbol && !symbol->GetInstances().empty(), continue );
1227
1228 SCH_SYMBOL_INSTANCE inst = symbol->GetInstances().at( 0 );
1229 wxString newReference = refPrefix + inst.m_Reference.AfterLast( ':' );
1230
1231 symbol->AddHierarchicalReference( m_sheetPath.Path(), newReference, inst.m_Unit );
1232 }
1233 }
1234 }
1235
1236 m_moduleInstances.pop_back();
1237 m_modules.pop_back();
1238 m_sheetPath.pop_back();
1239}
1240
1241
1242void SCH_IO_EAGLE::loadFrame( const std::unique_ptr<EFRAME>& aFrame,
1243 std::vector<SCH_ITEM*>& aItems )
1244{
1245 int xMin = aFrame->x1.ToSchUnits();
1246 int xMax = aFrame->x2.ToSchUnits();
1247 int yMin = -aFrame->y1.ToSchUnits();
1248 int yMax = -aFrame->y2.ToSchUnits();
1249
1250 if( xMin > xMax )
1251 std::swap( xMin, xMax );
1252
1253 if( yMin > yMax )
1254 std::swap( yMin, yMax );
1255
1256 SCH_SHAPE* lines = new SCH_SHAPE( SHAPE_T::POLY );
1257 lines->AddPoint( VECTOR2I( xMin, yMin ) );
1258 lines->AddPoint( VECTOR2I( xMax, yMin ) );
1259 lines->AddPoint( VECTOR2I( xMax, yMax ) );
1260 lines->AddPoint( VECTOR2I( xMin, yMax ) );
1261 lines->AddPoint( VECTOR2I( xMin, yMin ) );
1262 aItems.push_back( lines );
1263
1264 if( !( aFrame->border_left == false ) )
1265 {
1266 lines = new SCH_SHAPE( SHAPE_T::POLY );
1267 lines->AddPoint( VECTOR2I( xMin + schIUScale.MilsToIU( 150 ),
1268 yMin + schIUScale.MilsToIU( 150 ) ) );
1269 lines->AddPoint( VECTOR2I( xMin + schIUScale.MilsToIU( 150 ),
1270 yMax - schIUScale.MilsToIU( 150 ) ) );
1271 aItems.push_back( lines );
1272
1273 int i;
1274 int height = yMax - yMin;
1275 int x1 = xMin;
1276 int x2 = x1 + schIUScale.MilsToIU( 150 );
1277 int legendPosX = xMin + schIUScale.MilsToIU( 75 );
1278 double rowSpacing = height / double( aFrame->rows );
1279 double legendPosY = yMin + ( rowSpacing / 2 );
1280
1281 for( i = 1; i < aFrame->rows; i++ )
1282 {
1283 int newY = KiROUND( yMin + ( rowSpacing * (double) i ) );
1284 lines = new SCH_SHAPE( SHAPE_T::POLY );
1285 lines->AddPoint( VECTOR2I( x1, newY ) );
1286 lines->AddPoint( VECTOR2I( x2, newY ) );
1287 aItems.push_back( lines );
1288 }
1289
1290 char legendChar = 'A';
1291
1292 for( i = 0; i < aFrame->rows; i++ )
1293 {
1294 SCH_TEXT* legendText = new SCH_TEXT();
1295 legendText->SetPosition( VECTOR2I( legendPosX, KiROUND( legendPosY ) ) );
1298 legendText->SetText( wxString( legendChar ) );
1299 legendText->SetTextSize( VECTOR2I( schIUScale.MilsToIU( 90 ),
1300 schIUScale.MilsToIU( 100 ) ) );
1301 aItems.push_back( legendText );
1302 legendChar++;
1303 legendPosY += rowSpacing;
1304 }
1305 }
1306
1307 if( !( aFrame->border_right == false ) )
1308 {
1309 lines = new SCH_SHAPE( SHAPE_T::POLY );
1310 lines->AddPoint( VECTOR2I( xMax - schIUScale.MilsToIU( 150 ),
1311 yMin + schIUScale.MilsToIU( 150 ) ) );
1312 lines->AddPoint( VECTOR2I( xMax - schIUScale.MilsToIU( 150 ),
1313 yMax - schIUScale.MilsToIU( 150 ) ) );
1314 aItems.push_back( lines );
1315
1316 int i;
1317 int height = yMax - yMin;
1318 int x1 = xMax - schIUScale.MilsToIU( 150 );
1319 int x2 = xMax;
1320 int legendPosX = xMax - schIUScale.MilsToIU( 75 );
1321 double rowSpacing = height / double( aFrame->rows );
1322 double legendPosY = yMin + ( rowSpacing / 2 );
1323
1324 for( i = 1; i < aFrame->rows; i++ )
1325 {
1326 int newY = KiROUND( yMin + ( rowSpacing * (double) i ) );
1327 lines = new SCH_SHAPE( SHAPE_T::POLY );
1328 lines->AddPoint( VECTOR2I( x1, newY ) );
1329 lines->AddPoint( VECTOR2I( x2, newY ) );
1330 aItems.push_back( lines );
1331 }
1332
1333 char legendChar = 'A';
1334
1335 for( i = 0; i < aFrame->rows; i++ )
1336 {
1337 SCH_TEXT* legendText = new SCH_TEXT();
1338 legendText->SetPosition( VECTOR2I( legendPosX, KiROUND( legendPosY ) ) );
1341 legendText->SetText( wxString( legendChar ) );
1342 legendText->SetTextSize( VECTOR2I( schIUScale.MilsToIU( 90 ),
1343 schIUScale.MilsToIU( 100 ) ) );
1344 aItems.push_back( legendText );
1345 legendChar++;
1346 legendPosY += rowSpacing;
1347 }
1348 }
1349
1350 if( !( aFrame->border_top == false ) )
1351 {
1352 lines = new SCH_SHAPE( SHAPE_T::POLY );
1353 lines->AddPoint( VECTOR2I( xMax - schIUScale.MilsToIU( 150 ),
1354 yMin + schIUScale.MilsToIU( 150 ) ) );
1355 lines->AddPoint( VECTOR2I( xMin + schIUScale.MilsToIU( 150 ),
1356 yMin + schIUScale.MilsToIU( 150 ) ) );
1357 aItems.push_back( lines );
1358
1359 int i;
1360 int width = xMax - xMin;
1361 int y1 = yMin;
1362 int y2 = yMin + schIUScale.MilsToIU( 150 );
1363 int legendPosY = yMin + schIUScale.MilsToIU( 75 );
1364 double columnSpacing = width / double( aFrame->columns );
1365 double legendPosX = xMin + ( columnSpacing / 2 );
1366
1367 for( i = 1; i < aFrame->columns; i++ )
1368 {
1369 int newX = KiROUND( xMin + ( columnSpacing * (double) i ) );
1370 lines = new SCH_SHAPE( SHAPE_T::POLY );
1371 lines->AddPoint( VECTOR2I( newX, y1 ) );
1372 lines->AddPoint( VECTOR2I( newX, y2 ) );
1373 aItems.push_back( lines );
1374 }
1375
1376 char legendChar = '1';
1377
1378 for( i = 0; i < aFrame->columns; i++ )
1379 {
1380 SCH_TEXT* legendText = new SCH_TEXT();
1381 legendText->SetPosition( VECTOR2I( KiROUND( legendPosX ), legendPosY ) );
1384 legendText->SetText( wxString( legendChar ) );
1385 legendText->SetTextSize( VECTOR2I( schIUScale.MilsToIU( 90 ),
1386 schIUScale.MilsToIU( 100 ) ) );
1387 aItems.push_back( legendText );
1388 legendChar++;
1389 legendPosX += columnSpacing;
1390 }
1391 }
1392
1393 if( !( aFrame->border_bottom == false ) )
1394 {
1395 lines = new SCH_SHAPE( SHAPE_T::POLY );
1396 lines->AddPoint( VECTOR2I( xMax - schIUScale.MilsToIU( 150 ),
1397 yMax - schIUScale.MilsToIU( 150 ) ) );
1398 lines->AddPoint( VECTOR2I( xMin + schIUScale.MilsToIU( 150 ),
1399 yMax - schIUScale.MilsToIU( 150 ) ) );
1400 aItems.push_back( lines );
1401
1402 int i;
1403 int width = xMax - xMin;
1404 int y1 = yMax - schIUScale.MilsToIU( 150 );
1405 int y2 = yMax;
1406 int legendPosY = yMax - schIUScale.MilsToIU( 75 );
1407 double columnSpacing = width / double( aFrame->columns );
1408 double legendPosX = xMin + ( columnSpacing / 2 );
1409
1410 for( i = 1; i < aFrame->columns; i++ )
1411 {
1412 int newX = KiROUND( xMin + ( columnSpacing * (double) i ) );
1413 lines = new SCH_SHAPE( SHAPE_T::POLY );
1414 lines->AddPoint( VECTOR2I( newX, y1 ) );
1415 lines->AddPoint( VECTOR2I( newX, y2 ) );
1416 aItems.push_back( lines );
1417 }
1418
1419 char legendChar = '1';
1420
1421 for( i = 0; i < aFrame->columns; i++ )
1422 {
1423 SCH_TEXT* legendText = new SCH_TEXT();
1424 legendText->SetPosition( VECTOR2I( KiROUND( legendPosX ), legendPosY ) );
1427 legendText->SetText( wxString( legendChar ) );
1428 legendText->SetTextSize( VECTOR2I( schIUScale.MilsToIU( 90 ),
1429 schIUScale.MilsToIU( 100 ) ) );
1430 aItems.push_back( legendText );
1431 legendChar++;
1432 legendPosX += columnSpacing;
1433 }
1434 }
1435}
1436
1437
1438void SCH_IO_EAGLE::loadSegments( const std::vector<std::unique_ptr<ESEGMENT>>& aSegments,
1439 const wxString& netName,
1440 const wxString& aNetClass,
1441 bool aIsBus )
1442{
1443 // Loop through all segments
1444 SCH_SCREEN* screen = getCurrentScreen();
1445
1446 wxCHECK( screen, /* void */ );
1447
1448 size_t segmentCount = aSegments.size();
1449
1450 for( const std::unique_ptr<ESEGMENT>& esegment : aSegments )
1451 {
1452 bool labelled = false; // has a label been added to this continuously connected segment
1453 bool firstWireFound = false;
1454 SEG firstWire;
1455
1456 m_segments.emplace_back();
1457 SEG_DESC& segDesc = m_segments.back();
1458
1459 for( const std::unique_ptr<EWIRE>& ewire : esegment->wires )
1460 {
1461 // TODO: Check how intersections used in adjustNetLabels should be
1462 // calculated - for now we pretend that all wires are line segments.
1463 SEG thisWire;
1464 SCH_ITEM* wire = loadWire( ewire, thisWire );
1465 m_connPoints[thisWire.A].emplace( wire );
1466 m_connPoints[thisWire.B].emplace( wire );
1467
1468 if( !firstWireFound )
1469 {
1470 firstWire = thisWire;
1471 firstWireFound = true;
1472 }
1473
1474 // Test for intersections with other wires
1475 for( SEG_DESC& desc : m_segments )
1476 {
1477 if( !desc.labels.empty() && desc.labels.front()->GetText() == netName )
1478 continue; // no point in saving intersections of the same net
1479
1480 for( const SEG& seg : desc.segs )
1481 {
1482 OPT_VECTOR2I intersection = thisWire.Intersect( seg, true );
1483
1484 if( intersection )
1485 m_wireIntersections.push_back( *intersection );
1486 }
1487 }
1488
1489 segDesc.segs.push_back( thisWire );
1490 screen->Append( wire );
1491 }
1492
1493 for( const std::unique_ptr<EJUNCTION>& ejunction : esegment->junctions )
1494 screen->Append( loadJunction( ejunction ) );
1495
1496 for( const std::unique_ptr<ELABEL>& elabel : esegment->labels )
1497 {
1498 SCH_LABEL_BASE* label = loadLabel( elabel, netName, aIsBus );
1499 screen->Append( label );
1500
1501 wxASSERT( segDesc.labels.empty()
1502 || segDesc.labels.front()->GetText() == label->GetText() );
1503
1504 segDesc.labels.push_back( label );
1505 labelled = true;
1506 }
1507
1508 for( const std::unique_ptr<EPINREF>& epinref : esegment->pinRefs )
1509 {
1510 wxString part = epinref->part;
1511 wxString pin = epinref->pin;
1512
1513 auto powerPort = m_powerPorts.find( wxT( "#" ) + part );
1514
1515 if( powerPort != m_powerPorts.end()
1516 && powerPort->second == EscapeString( pin, CTX_NETNAME ) )
1517 {
1518 labelled = true;
1519 }
1520 }
1521
1522 // Add a small label to the net segment if it hasn't been labeled already or is not
1523 // connect to a power symbol with a pin on the same net. This preserves the named net
1524 // feature of Eagle schematics.
1525 if( !labelled && firstWireFound )
1526 {
1527 std::unique_ptr<SCH_LABEL_BASE> label;
1528
1529 // Eagle uses a flat net namespace, so a named net should retain its name across
1530 // every segment and every sheet. The PCB importer carries Eagle signal names
1531 // through verbatim, so we must use a global label here too: a local SCH_LABEL
1532 // would prepend the sheet path (e.g. "/+24V_SWD") and split the net from the
1533 // matching PCB signal on net update.
1534 //
1535 // Two exceptions: (1) buses are conceptual groupings in Eagle, not electrical
1536 // signals, so a global bus label would join same-named buses project-wide where
1537 // Eagle only had visual grouping; (2) nets inside module instances are scoped
1538 // by the module's ports, so a global label would punch through the hierarchy.
1539 if( segmentCount > 1 || m_netCounts[netName] > 1 )
1540 {
1541 if( aIsBus || !m_modules.empty() )
1542 label.reset( new SCH_LABEL );
1543 else
1544 label.reset( new SCH_GLOBALLABEL );
1545 }
1546
1547 if( label )
1548 {
1549 label->SetPosition( firstWire.A );
1550 label->SetText( escapeName( netName ) );
1551 label->SetTextSize( VECTOR2I( schIUScale.MilsToIU( 40 ),
1552 schIUScale.MilsToIU( 40 ) ) );
1553
1554 if( firstWire.A.y == firstWire.B.y ) // Horizontal wire.
1555 {
1556 if( firstWire.B.x > firstWire.A.x )
1557 label->SetSpinStyle( SPIN_STYLE::LEFT );
1558 else
1559 label->SetSpinStyle( SPIN_STYLE::RIGHT );
1560 }
1561 else if( firstWire.A.x == firstWire.B.x ) // Vertical wire.
1562 {
1563 if( firstWire.B.y > firstWire.A.y )
1564 label->SetSpinStyle( SPIN_STYLE::BOTTOM );
1565 else
1566 label->SetSpinStyle( SPIN_STYLE::UP );
1567 }
1568
1569 screen->Append( label.release() );
1570 }
1571 }
1572 }
1573}
1574
1575
1576SCH_SHAPE* SCH_IO_EAGLE::loadPolyLine( const std::unique_ptr<EPOLYGON>& aPolygon )
1577{
1578 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY );
1579 VECTOR2I pt, prev_pt;
1580 opt_double prev_curve;
1581
1582 for( const std::unique_ptr<EVERTEX>& evertex : aPolygon->vertices )
1583 {
1584 pt = VECTOR2I( evertex->x.ToSchUnits(), -evertex->y.ToSchUnits() );
1585
1586 if( prev_curve )
1587 {
1588 SHAPE_ARC arc;
1589 arc.ConstructFromStartEndAngle( prev_pt, pt, -EDA_ANGLE( *prev_curve, DEGREES_T ) );
1590 poly->GetPolyShape().Append( arc, -1, -1, ARC_ACCURACY );
1591 }
1592 else
1593 {
1594 poly->AddPoint( pt );
1595 }
1596
1597 prev_pt = pt;
1598 prev_curve = evertex->curve;
1599 }
1600
1601 poly->SetLayer( kiCadLayer( aPolygon->layer ) );
1602 poly->SetStroke( STROKE_PARAMS( aPolygon->width.ToSchUnits(), LINE_STYLE::SOLID ) );
1603 poly->SetFillMode( FILL_T::FILLED_SHAPE );
1604
1605 return poly.release();
1606}
1607
1608
1609SCH_ITEM* SCH_IO_EAGLE::loadWire( const std::unique_ptr<EWIRE>& aWire, SEG& endpoints )
1610{
1611 VECTOR2I start, end;
1612
1613 start.x = aWire->x1.ToSchUnits();
1614 start.y = -aWire->y1.ToSchUnits();
1615 end.x = aWire->x2.ToSchUnits();
1616 end.y = -aWire->y2.ToSchUnits();
1617
1618 // For segment wires.
1619 endpoints = SEG( start, end );
1620
1621 int kicadLayer = kiCadLayer( aWire->layer );
1622
1623 // Don't process curved wires on an electrical layer into arcs, they aren't supported
1624 // in the rest of the code
1625 // TODO: When curved wires/buses are added, remove this restriction
1626 if( (kicadLayer == LAYER_NOTES) && aWire->curve )
1627 {
1628 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
1629
1630 VECTOR2I center = ConvertArcCenter( start, end, *aWire->curve );
1631 arc->SetCenter( center );
1632 arc->SetStart( start );
1633
1634 // KiCad rotates the other way.
1635 arc->SetArcAngleAndEnd( -EDA_ANGLE( *aWire->curve, DEGREES_T ), true );
1636 arc->SetLayer( kiCadLayer( aWire->layer ) );
1637 arc->SetStroke( STROKE_PARAMS( aWire->width.ToSchUnits(), LINE_STYLE::SOLID ) );
1638
1639 return arc.release();
1640 }
1641 else
1642 {
1643 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>();
1644
1645 line->SetStartPoint( start );
1646 line->SetEndPoint( end );
1647 line->SetLayer( kiCadLayer( aWire->layer ) );
1648 line->SetStroke( STROKE_PARAMS( aWire->width.ToSchUnits(), LINE_STYLE::SOLID ) );
1649
1650 return line.release();
1651 }
1652}
1653
1654
1655SCH_SHAPE* SCH_IO_EAGLE::loadCircle( const std::unique_ptr<ECIRCLE>& aCircle )
1656{
1657 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
1658 VECTOR2I center( aCircle->x.ToSchUnits(), -aCircle->y.ToSchUnits() );
1659
1660 circle->SetLayer( kiCadLayer( aCircle->layer ) );
1661 circle->SetPosition( center );
1662 circle->SetEnd( VECTOR2I( center.x + aCircle->radius.ToSchUnits(), center.y ) );
1663 circle->SetStroke( STROKE_PARAMS( aCircle->width.ToSchUnits(), LINE_STYLE::SOLID ) );
1664
1665 return circle.release();
1666}
1667
1668
1669SCH_SHAPE* SCH_IO_EAGLE::loadRectangle( const std::unique_ptr<ERECT>& aRectangle )
1670{
1671 std::unique_ptr<SCH_SHAPE> rectangle = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
1672
1673 rectangle->SetLayer( kiCadLayer( aRectangle->layer ) );
1674 rectangle->SetPosition( VECTOR2I( aRectangle->x1.ToSchUnits(), -aRectangle->y1.ToSchUnits() ) );
1675 rectangle->SetEnd( VECTOR2I( aRectangle->x2.ToSchUnits(), -aRectangle->y2.ToSchUnits() ) );
1676
1677 if( aRectangle->rot )
1678 {
1679 VECTOR2I pos( rectangle->GetPosition() );
1680 VECTOR2I end( rectangle->GetEnd() );
1681 VECTOR2I center( rectangle->GetCenter() );
1682
1683 RotatePoint( pos, center, EDA_ANGLE( aRectangle->rot->degrees, DEGREES_T ) );
1684 RotatePoint( end, center, EDA_ANGLE( aRectangle->rot->degrees, DEGREES_T ) );
1685
1686 rectangle->SetPosition( pos );
1687 rectangle->SetEnd( end );
1688 }
1689
1690 // Eagle rectangles are filled by definition.
1691 rectangle->SetFillMode( FILL_T::FILLED_SHAPE );
1692
1693 return rectangle.release();
1694}
1695
1696
1697SCH_JUNCTION* SCH_IO_EAGLE::loadJunction( const std::unique_ptr<EJUNCTION>& aJunction )
1698{
1699 std::unique_ptr<SCH_JUNCTION> junction = std::make_unique<SCH_JUNCTION>();
1700
1701 VECTOR2I pos( aJunction->x.ToSchUnits(), -aJunction->y.ToSchUnits() );
1702
1703 junction->SetPosition( pos );
1704
1705 return junction.release();
1706}
1707
1708
1709SCH_LABEL_BASE* SCH_IO_EAGLE::loadLabel( const std::unique_ptr<ELABEL>& aLabel,
1710 const wxString& aNetName, bool aIsBus )
1711{
1712 VECTOR2I elabelpos( aLabel->x.ToSchUnits(), -aLabel->y.ToSchUnits() );
1713
1714 // Label-kind decision mirrors loadSegments(): SCH_HIERLABEL for module ports,
1715 // SCH_LABEL for buses and module-internal nets, SCH_GLOBALLABEL otherwise so the
1716 // Eagle flat net namespace round-trips through the matching PCB signal name.
1717 std::unique_ptr<SCH_LABEL_BASE> label;
1718
1719 VECTOR2I textSize = KiROUND( aLabel->size.ToSchUnits() * 0.7, aLabel->size.ToSchUnits() * 0.7 );
1720
1721 auto findModulePort =
1722 [&]() -> const EPORT*
1723 {
1724 if( m_modules.empty() )
1725 return nullptr;
1726
1727 const auto& ports = m_modules.back()->ports;
1728 const auto it = ports.find( aNetName );
1729 return it == ports.end() ? nullptr : it->second.get();
1730 };
1731
1732 const EPORT* port = findModulePort();
1733
1734 if( port )
1735 {
1736 auto hierLabel = std::make_unique<SCH_HIERLABEL>();
1737
1738 if( port->direction )
1739 {
1740 wxString direction = *port->direction;
1741 LABEL_SHAPE type;
1742
1743 if( direction == "in" )
1745 else if( direction == "out" )
1747 else if( direction == "io" )
1749 else if( direction == "hiz" )
1751 else
1753
1754 // KiCad does not support passive, power, open collector, or no-connect sheet
1755 // pins that Eagle ports support. They are set to unspecified to minimize
1756 // ERC issues.
1757 hierLabel->SetLabelShape( type );
1758 }
1759
1760 label = std::move( hierLabel );
1761 }
1762 else if( aIsBus || !m_modules.empty() )
1763 {
1764 label = std::make_unique<SCH_LABEL>();
1765 }
1766 else
1767 {
1768 label = std::make_unique<SCH_GLOBALLABEL>();
1769 }
1770
1771 label->SetText( escapeName( aNetName ) );
1772 label->SetPosition( elabelpos );
1773 label->SetTextSize( textSize );
1774 label->SetSpinStyle( SPIN_STYLE::RIGHT );
1775
1776 if( aLabel->rot )
1777 {
1778 // According to the Eagle DTD, labels can only be rotated in 90 degree increments.
1779 int angle = KiROUND( aLabel->rot->degrees );
1780
1781 switch( angle )
1782 {
1783 case 90:
1784 label->SetSpinStyle( aLabel->rot->mirror ? SPIN_STYLE::BOTTOM : SPIN_STYLE::UP );
1785 break;
1786 case 180:
1787 label->SetSpinStyle( aLabel->rot->mirror ? SPIN_STYLE::RIGHT : SPIN_STYLE::LEFT );
1788 break;
1789 case 270:
1790 label->SetSpinStyle( aLabel->rot->mirror ? SPIN_STYLE::UP : SPIN_STYLE::BOTTOM );
1791 break;
1792 default:
1793 label->SetSpinStyle( aLabel->rot->mirror ? SPIN_STYLE::LEFT : SPIN_STYLE::RIGHT );
1794 break;
1795 }
1796 }
1797
1798 return label.release();
1799}
1800
1801
1802std::pair<VECTOR2I, const SEG*>
1804 const std::vector<SEG>& aLines ) const
1805{
1806 VECTOR2I nearestPoint;
1807 const SEG* nearestLine = nullptr;
1808
1809 double d, mindistance = std::numeric_limits<double>::max();
1810
1811 // Find the nearest start, middle or end of a line from the list of lines.
1812 for( const SEG& line : aLines )
1813 {
1814 VECTOR2I testpoint = line.A;
1815 d = aPoint.Distance( testpoint );
1816
1817 if( d < mindistance )
1818 {
1819 mindistance = d;
1820 nearestPoint = testpoint;
1821 nearestLine = &line;
1822 }
1823
1824 testpoint = line.Center();
1825 d = aPoint.Distance( testpoint );
1826
1827 if( d < mindistance )
1828 {
1829 mindistance = d;
1830 nearestPoint = testpoint;
1831 nearestLine = &line;
1832 }
1833
1834 testpoint = line.B;
1835 d = aPoint.Distance( testpoint );
1836
1837 if( d < mindistance )
1838 {
1839 mindistance = d;
1840 nearestPoint = testpoint;
1841 nearestLine = &line;
1842 }
1843 }
1844
1845 return std::make_pair( nearestPoint, nearestLine );
1846}
1847
1848
1849void SCH_IO_EAGLE::loadInstance( const std::unique_ptr<EINSTANCE>& aInstance,
1850 const std::map<wxString, std::unique_ptr<EPART>>& aParts )
1851{
1852 wxCHECK( aInstance, /* void */ );
1853
1854 SCH_SCREEN* screen = getCurrentScreen();
1855
1856 wxCHECK( screen, /* void */ );
1857
1858 const auto partIt = aParts.find( aInstance->part );
1859
1860 if( partIt == aParts.end() )
1861 {
1862 Report( wxString::Format( _( "Error parsing Eagle file. Could not find '%s' "
1863 "instance but it is referenced in the schematic." ),
1864 aInstance->part ),
1866
1867 return;
1868 }
1869
1870 const std::unique_ptr<EPART>& epart = partIt->second;
1871
1872 wxString libName = epart->library;
1873
1874 // Correctly handle versioned libraries.
1875 if( epart->libraryUrn )
1876 libName += wxS( "_" ) + epart->libraryUrn->assetId;
1877
1878 wxString gatename = epart->deviceset + wxS( "_" ) + epart->device + wxS( "_" ) +
1879 aInstance->gate;
1880 wxString symbolname = wxString( epart->deviceset + epart->device );
1881 wxString kiPackageName = epart->deviceset + epart->device;
1882
1883 if( epart->technology )
1884 symbolname += *epart->technology;
1885
1886 symbolname.Replace( wxT( "*" ), wxEmptyString );
1887 kiPackageName.Replace( wxT( "*" ), wxEmptyString );
1888
1889 wxString kisymbolname = EscapeString( symbolname, CTX_LIBID );
1890
1891 // Eagle schematics can have multiple libraries containing symbols with duplicate symbol
1892 // names. Because this parser stores all of the symbols in a single library, the
1893 // loadSymbol() function, prefixed the original Eagle library name to the symbol name
1894 // in case of a name clash. Check for the prefixed symbol first. This ensures that
1895 // the correct library symbol gets mapped on load.
1896 wxString altSymbolName = libName + wxT( "_" ) + symbolname;
1897 altSymbolName = EscapeString( altSymbolName, CTX_LIBID );
1898
1899 wxString libIdSymbolName = altSymbolName;
1900
1901 const auto libIt = m_eagleLibs.find( libName );
1902
1903 if( libIt == m_eagleLibs.end() )
1904 {
1905 Report( wxString::Format( wxS( "Eagle library '%s' not found while looking up symbol for "
1906 "deviceset '%s', device '%s', and gate '%s." ),
1907 libName, epart->deviceset, epart->device, aInstance->gate ) );
1908 return;
1909 }
1910
1911 const auto gateIt = libIt->second.GateToUnitMap.find( gatename );
1912
1913 if( gateIt == libIt->second.GateToUnitMap.end() )
1914 {
1915 Report( wxString::Format( wxS( "Symbol not found for deviceset '%s', device '%s', and "
1916 "gate '%s in library '%s'." ),
1917 epart->deviceset, epart->device, aInstance->gate, libName ) );
1918 return;
1919 }
1920
1921 int unit = gateIt->second;
1922
1923 wxString package;
1924 EAGLE_LIBRARY* elib = &m_eagleLibs[libName];
1925
1926 auto p = elib->package.find( kisymbolname );
1927
1928 if( p != elib->package.end() )
1929 {
1930 package = p->second;
1931 }
1932 else
1933 {
1934 p = elib->package.find( kiPackageName );
1935
1936 if( p != elib->package.end() )
1937 package = p->second;
1938 }
1939
1940 // set properties to prevent save file on every symbol save
1941 std::map<std::string, UTF8> properties;
1942 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, wxEmptyString );
1943
1944 LIB_SYMBOL* part = m_pi->LoadSymbol( getLibFileName().GetFullPath(), altSymbolName, &properties );
1945
1946 if( !part )
1947 {
1948 part = m_pi->LoadSymbol( getLibFileName().GetFullPath(), kisymbolname, &properties );
1949 libIdSymbolName = kisymbolname;
1950 }
1951
1952 if( !part )
1953 {
1954 Report( wxString::Format( _( "Could not find '%s' in the imported library." ),
1955 UnescapeString( kisymbolname ) ),
1957 return;
1958 }
1959
1960 LIB_ID libId( getLibName(), libIdSymbolName );
1961 std::unique_ptr<SCH_SYMBOL> symbol = std::make_unique<SCH_SYMBOL>();
1962 symbol->SetLibId( libId );
1963 symbol->SetUnit( unit );
1964 symbol->SetPosition( VECTOR2I( aInstance->x.ToSchUnits(), -aInstance->y.ToSchUnits() ) );
1965
1966 // assume that footprint library is identical to project name
1967 if( !package.IsEmpty() )
1968 {
1969 wxString footprint = m_schematic->Project().GetProjectName() + wxT( ":" ) + package;
1970 symbol->GetField( FIELD_T::FOOTPRINT )->SetText( footprint );
1971 }
1972
1973 if( aInstance->rot )
1974 {
1975 symbol->SetOrientation( kiCadComponentRotation( aInstance->rot->degrees ) );
1976
1977 if( aInstance->rot->mirror )
1978 symbol->MirrorHorizontally( aInstance->x.ToSchUnits() );
1979 }
1980
1981 std::vector<SCH_FIELD*> partFields;
1982 part->GetFields( partFields );
1983
1984 VECTOR2I nextFieldPosition = getLastSymbolFieldPosition( part ) + symbol->GetPosition();
1985
1986 for( const SCH_FIELD* partField : partFields )
1987 {
1988 SCH_FIELD* symbolField = nullptr;
1989
1990 if( partField->IsMandatory() )
1991 symbolField = symbol->GetField( partField->GetId() );
1992 else
1993 symbolField = symbol->GetField( partField->GetName() );
1994
1995 if( !symbolField )
1996 {
1997 SCH_FIELD newField( symbol.get(), FIELD_T::USER, partField->GetName() );
1998
1999 newField.SetVisible( false );
2000 newField.SetText( partField->GetText() );
2001
2002 nextFieldPosition.y += newField.GetTextHeight() + schIUScale.MilsToIU( 10 );
2003 newField.SetPosition( nextFieldPosition );
2004 symbol->AddField( newField );
2005 }
2006 else
2007 {
2008 symbolField->ImportValues( *partField );
2009 symbolField->SetTextPos( symbol->GetPosition() + partField->GetTextPos() );
2010 }
2011 }
2012
2013 // If there is no footprint assigned, then prepend the reference value
2014 // with a hash character to mute netlist updater complaints
2015 wxString reference = package.IsEmpty() ? '#' + aInstance->part : aInstance->part;
2016
2017 // reference must end with a number but EAGLE does not enforce this
2018 if( reference.find_last_not_of( wxT( "0123456789" ) ) == ( reference.Length()-1 ) )
2019 reference.Append( wxT( "0" ) );
2020
2021 // EAGLE allows references to be single digits. This breaks KiCad netlisting, which requires
2022 // parts to have non-digit + digit annotation. If the reference begins with a number,
2023 // we prepend 'UNK' (unknown) for the symbol designator
2024 if( reference.find_first_not_of( wxT( "0123456789" ) ) != 0 )
2025 reference.Prepend( wxT( "UNK" ) );
2026
2027 // EAGLE allows designator to start with # but that is used in KiCad
2028 // for symbols which do not have a footprint
2029 if( aInstance->part.find_first_not_of( wxT( "#" ) ) != 0 )
2030 reference.Prepend( wxT( "UNK" ) );
2031
2032 SCH_FIELD* referenceField = symbol->GetField( FIELD_T::REFERENCE );
2033 referenceField->SetText( reference );
2034
2035 SCH_FIELD* valueField = symbol->GetField( FIELD_T::VALUE );
2036 bool userValue = m_userValue.at( libIdSymbolName );
2037
2038 if( part->GetUnitCount() > 1 )
2039 {
2040 getEagleSymbolFieldAttributes( aInstance, wxS( ">NAME" ), referenceField );
2041 getEagleSymbolFieldAttributes( aInstance, wxS( ">VALUE" ), valueField );
2042 }
2043
2044 if( epart->value && !epart->value.CGet().IsEmpty() )
2045 {
2046 valueField->SetText( *epart->value );
2047 }
2048 else
2049 {
2050 valueField->SetText( kisymbolname );
2051
2052 if( userValue )
2053 valueField->SetVisible( false );
2054 }
2055
2056 for( const auto& [ attrName, attr ] : epart->attributes )
2057 {
2058 SCH_FIELD newField( symbol.get(), FIELD_T::USER );
2059
2060 newField.SetName( attrName );
2061
2062 if( !symbol->GetFields().empty() )
2063 newField.SetTextPos( symbol->GetFields().back().GetPosition() );
2064
2065 if( attr->value )
2066 newField.SetText( *attr->value );
2067
2068 newField.SetVisible( ( attr->display == EATTR::Off ) ? false : true );
2069
2070 symbol->AddField( newField );
2071 }
2072
2073 for( const auto& [variantName, variant] : epart->variants )
2074 {
2075 SCH_FIELD* field = symbol->AddField( *symbol->GetField( FIELD_T::VALUE ) );
2076 field->SetName( wxT( "VARIANT_" ) + variant->name );
2077
2078 if( variant->value )
2079 field->SetText( *variant->value );
2080
2081 field->SetVisible( false );
2082 }
2083
2084 bool valueAttributeFound = false;
2085 bool nameAttributeFound = false;
2086
2087 // Parse attributes for the instance
2088 for( auto& [name, eattr] : aInstance->attributes )
2089 {
2090 SCH_FIELD* field = nullptr;
2091
2092 if( eattr->name.Lower() == wxT( "name" ) )
2093 {
2094 field = symbol->GetField( FIELD_T::REFERENCE );
2095 nameAttributeFound = true;
2096 }
2097 else if( eattr->name.Lower() == wxT( "value" ) )
2098 {
2099 field = symbol->GetField( FIELD_T::VALUE );
2100 valueAttributeFound = true;
2101 }
2102 else
2103 {
2104 field = symbol->GetField( eattr->name );
2105 }
2106
2107 if( field )
2108 {
2109 field->SetVisible( true );
2110 field->SetPosition( VECTOR2I( eattr->x->ToSchUnits(), -eattr->y->ToSchUnits() ) );
2111 int align = eattr->align ? *eattr->align : ETEXT::BOTTOM_LEFT;
2112 int absdegrees = eattr->rot ? eattr->rot->degrees : 0;
2113 bool mirror = eattr->rot ? eattr->rot->mirror : false;
2114
2115 if( aInstance->rot && aInstance->rot->mirror )
2116 mirror = !mirror;
2117
2118 bool spin = eattr->rot ? eattr->rot->spin : false;
2119
2120 if( eattr->display == EATTR::Off || eattr->display == EATTR::NAME )
2121 field->SetVisible( false );
2122
2123 int rotation = aInstance->rot ? aInstance->rot->degrees : 0;
2124 int reldegrees = ( absdegrees - rotation + 360.0 );
2125 reldegrees %= 360;
2126
2127 eagleToKicadAlignment( field, align, reldegrees, mirror, spin, absdegrees );
2128 }
2129 }
2130
2131 // Use the instance attribute to determine the reference and value field visibility.
2132 if( aInstance->smashed && aInstance->smashed.Get() )
2133 {
2134 symbol->GetField( FIELD_T::VALUE )->SetVisible( valueAttributeFound );
2135 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( nameAttributeFound );
2136 }
2137
2138 // Eagle has a brain dead module reference scheme where the module names separated by colons
2139 // are prefixed to the symbol references. This will get blown away in KiCad the first time
2140 // any annotation is performed. It is required for the initial synchronization between the
2141 // schematic and the board.
2142 wxString refPrefix;
2143
2144 for( const EMODULEINST* emoduleInst : m_moduleInstances )
2145 {
2146 wxCHECK2( emoduleInst, continue );
2147
2148 refPrefix += emoduleInst->name + wxS( ":" );
2149 }
2150
2151 symbol->AddHierarchicalReference( m_sheetPath.Path(), refPrefix + reference, unit );
2152
2153 // Cache the lib symbol so pin positions are available for connection-point tracking.
2154 // Use the already-loaded `part` directly rather than re-fetching through the adapter,
2155 // because the .kicad_sym library is still buffered in m_pi and has not yet been saved to
2156 // disk at the time loadInstance runs.
2157 symbol->SetLibSymbol( part->Flatten().release() );
2158
2159 for( const SCH_PIN* pin : symbol->GetLibPins() )
2160 m_connPoints[symbol->GetPinPhysicalPosition( pin )].emplace( pin );
2161
2162 if( part->IsGlobalPower() )
2163 m_powerPorts[ reference ] = symbol->GetField( FIELD_T::VALUE )->GetText();
2164
2165 symbol->ClearFlags();
2166
2167 screen->Append( symbol.release() );
2168}
2169
2170
2172{
2173 wxCHECK( aLibrary && aEagleLibrary, nullptr );
2174
2175 // Loop through the device sets and load each of them
2176 for( const auto& [name, edeviceset] : aLibrary->devicesets )
2177 {
2178 // Get Device set information
2179 wxString prefix = edeviceset->prefix ? edeviceset->prefix.Get() : wxString( wxT( "" ) );
2180 wxString deviceSetDescr;
2181
2182 if( edeviceset->description )
2183 deviceSetDescr = convertDescription( UnescapeHTML( edeviceset->description->text ) );
2184
2185 // For each device in the device set:
2186 for( const std::unique_ptr<EDEVICE>& edevice : edeviceset->devices )
2187 {
2188 std::vector<std::unique_ptr<LIB_SYMBOL>> derivedSymbols;
2189
2190 // Create symbol name from deviceset and device names.
2191 wxString symbolName = edeviceset->name + edevice->name;
2192 symbolName.Replace( wxT( "*" ), wxEmptyString );
2193 wxASSERT( !symbolName.IsEmpty() );
2194 symbolName = EscapeString( symbolName, CTX_LIBID );
2195
2196 if( edevice->package )
2197 aEagleLibrary->package[symbolName] = edevice->package.Get();
2198
2199 // Create KiCad symbol.
2200 std::unique_ptr<LIB_SYMBOL> libSymbol = std::make_unique<LIB_SYMBOL>( symbolName );
2201
2202 // Process each gate in the deviceset for this device.
2203 int gate_count = static_cast<int>( edeviceset->gates.size() );
2204
2205 if( gate_count > 1 )
2206 libSymbol->SetUnitCount( gate_count, true );
2207
2208 libSymbol->LockUnits( true );
2209
2210 SCH_FIELD* reference = libSymbol->GetField( FIELD_T::REFERENCE );
2211
2212 if( prefix.length() == 0 )
2213 {
2214 reference->SetVisible( false );
2215 }
2216 else
2217 {
2218 // If there is no footprint assigned, then prepend the reference value
2219 // with a hash character to mute netlist updater complaints
2220 reference->SetText( edevice->package ? prefix : '#' + prefix );
2221 }
2222
2223 libSymbol->GetValueField().SetVisible( true );
2224
2225 int gateindex = 1;
2226 bool ispower = false;
2227
2228 for( const auto& [gateName, egate] : edeviceset->gates )
2229 {
2230 const auto it = aLibrary->symbols.find( egate->symbol );
2231
2232 if( it == aLibrary->symbols.end() )
2233 {
2234 Report( wxString::Format( wxS( "Eagle symbol '%s' not found in library '%s'." ),
2235 egate->symbol, aLibrary->GetName() ) );
2236 continue;
2237 }
2238
2239 wxString gateMapName = edeviceset->name + wxS( "_" ) + edevice->name +
2240 wxS( "_" ) + egate->name;
2241 aEagleLibrary->GateToUnitMap[gateMapName] = gateindex;
2242 ispower = loadSymbol( it->second, libSymbol, edevice, gateindex, egate->name );
2243
2244 gateindex++;
2245 }
2246
2247 VECTOR2I nextFieldPosition = getLastSymbolFieldPosition( libSymbol.get() );
2248
2249 for( const std::unique_ptr<ETECHNOLOGY>& technology : edevice->technologies )
2250 {
2251 std::unique_ptr<LIB_SYMBOL> derivedSymbol;
2252
2253 if( !technology->name.IsEmpty() )
2254 derivedSymbol = std::make_unique<LIB_SYMBOL>( symbolName + technology->name, libSymbol.get() );
2255
2256 for( const std::unique_ptr<EATTR>& attr : technology->attributes )
2257 {
2258 if( !attr->value )
2259 continue;
2260
2261 SCH_FIELD* field = nullptr;
2262
2263 if( !derivedSymbol )
2264 field = libSymbol->FindFieldCaseInsensitive( attr->name );
2265 else
2266 field = derivedSymbol->FindFieldCaseInsensitive( attr->name );
2267
2268 if( field )
2269 {
2270 field->SetText( *attr->value );
2271 }
2272 else
2273 {
2274 SCH_FIELD* newField = new SCH_FIELD( derivedSymbol ? derivedSymbol.get() : libSymbol.get(),
2275 FIELD_T::USER, attr->name );
2276
2277 nextFieldPosition.y += newField->GetTextHeight() + schIUScale.MilsToIU( 10 );
2278 newField->SetText( *attr->value );
2279 newField->SetVisible( false );
2280 newField->SetPosition( nextFieldPosition );
2281
2282 if( !derivedSymbol )
2283 libSymbol->AddField( newField );
2284 else
2285 derivedSymbol->AddField( newField );
2286 }
2287 }
2288
2289 if( derivedSymbol )
2290 derivedSymbols.push_back( std::move( derivedSymbol ) );
2291 }
2292
2293 if( gate_count > 1 )
2294 libSymbol->SetUnitCount( gate_count, true );
2295
2296 if( gate_count == 1 && ispower )
2297 libSymbol->SetGlobalPower();
2298
2299 // Don't set the footprint field if no package is defined in the Eagle schematic.
2300 if( edevice->package )
2301 {
2302 wxString libName;
2303
2304 if( m_schematic )
2305 {
2306 // assume that footprint library is identical to project name
2307 libName = m_schematic->Project().GetProjectName();
2308 }
2309 else
2310 {
2311 libName = m_libName;
2312 }
2313
2314 wxString packageString = libName + wxT( ":" ) + aEagleLibrary->package[symbolName];
2315
2316 libSymbol->GetFootprintField().SetText( packageString );
2317 }
2318
2319 wxString libName = libSymbol->GetName();
2320 libSymbol->SetName( libName );
2321 libSymbol->SetDescription( deviceSetDescr );
2322
2323 if( m_pi )
2324 {
2325 // If duplicate symbol names exist in multiple Eagle symbol libraries, prefix the
2326 // Eagle symbol library name to the symbol which should ensure that it is unique.
2327 try
2328 {
2329 if( m_pi->LoadSymbol( getLibFileName().GetFullPath(), libName ) )
2330 {
2331 libName = aEagleLibrary->name + wxT( "_" ) + libName;
2332 libName = EscapeString( libName, CTX_LIBID );
2333 libSymbol->SetName( libName );
2334 }
2335
2336 // set properties to prevent save file on every symbol save
2337 std::map<std::string, UTF8> properties;
2338 properties.emplace( SCH_IO_KICAD_SEXPR::PropBuffering, wxEmptyString );
2339
2340 LIB_SYMBOL* parentSymbol = new LIB_SYMBOL( *libSymbol.get() );
2341 m_pi->SaveSymbol( getLibFileName().GetFullPath(), parentSymbol, &properties );
2342
2343 for( std::unique_ptr<LIB_SYMBOL>& symbol : derivedSymbols )
2344 {
2345 if( m_pi->LoadSymbol( getLibFileName().GetFullPath(), symbol->GetName() ) )
2346 {
2347 wxString tmp = aEagleLibrary->name + wxT( "_" ) + symbol->GetName();
2348 tmp = EscapeString( tmp, CTX_LIBID );
2349 symbol->SetName( tmp );
2350 }
2351
2352 LIB_SYMBOL* derivedSymbol = new LIB_SYMBOL( *symbol.get() );
2353
2354 derivedSymbol->SetParent( parentSymbol );
2355 m_pi->SaveSymbol( getLibFileName().GetFullPath(), derivedSymbol, &properties );
2356 }
2357 }
2358 catch(...)
2359 {
2360 // A library symbol cannot be loaded for some reason.
2361 // Just skip this symbol creating an issue.
2362 // The issue will be reported later by the Reporter
2363 }
2364 }
2365
2366 aEagleLibrary->KiCadSymbols[ libName ] = std::move( libSymbol );
2367
2368 // Store information on whether the value of FIELD_T::VALUE for a part should be
2369 // part/@value or part/@deviceset + part/@device.
2370 m_userValue.emplace( std::make_pair( libName, edeviceset->uservalue == true ) );
2371
2372 for( std::unique_ptr<LIB_SYMBOL>& symbol : derivedSymbols )
2373 {
2374 m_userValue.emplace( std::make_pair( symbol->GetName(), edeviceset->uservalue == true ) );
2375 aEagleLibrary->KiCadSymbols[symbol->GetName()] = std::move( symbol );
2376 }
2377 }
2378 }
2379
2380 return aEagleLibrary;
2381}
2382
2383
2384bool SCH_IO_EAGLE::loadSymbol( const std::unique_ptr<ESYMBOL>& aEsymbol,
2385 std::unique_ptr<LIB_SYMBOL>& aSymbol,
2386 const std::unique_ptr<EDEVICE>& aDevice, int aGateNumber,
2387 const wxString& aGateName )
2388{
2389 wxCHECK( aEsymbol && aSymbol && aDevice, false );
2390
2391 std::vector<SCH_ITEM*> items;
2392
2393 bool showRefDes = false;
2394 bool showValue = false;
2395 bool ispower = false;
2396 int pincount = 0;
2397
2398 for( const std::unique_ptr<ECIRCLE>& ecircle : aEsymbol->circles )
2399 aSymbol->AddDrawItem( loadSymbolCircle( aSymbol, ecircle, aGateNumber ) );
2400
2401 for( const std::unique_ptr<EPIN>& epin : aEsymbol->pins )
2402 {
2403 std::unique_ptr<SCH_PIN> pin( loadPin( aSymbol, epin, aGateNumber ) );
2404 pincount++;
2405
2406 pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
2407
2408 if( epin->direction )
2409 {
2410 for( const auto& pinDir : pinDirectionsMap )
2411 {
2412 if( epin->direction->Lower() == pinDir.first )
2413 {
2414 pin->SetType( pinDir.second );
2415
2416 if( pinDir.first == wxT( "sup" ) ) // power supply symbol
2417 ispower = true;
2418
2419 break;
2420 }
2421 }
2422
2423 }
2424
2425 if( aDevice->connects.size() != 0 )
2426 {
2427 for( const std::unique_ptr<ECONNECT>& connect : aDevice->connects )
2428 {
2429 // Eagle <connect> references the full pin name including any "@<tag>"
2430 // linking hint, so match against the raw Eagle name rather than the
2431 // stripped display name set on the pin.
2432 if( connect->gate == aGateName && epin->name == connect->pin )
2433 {
2434 wxArrayString pads = wxSplit( wxString( connect->pad ), ' ' );
2435
2436 pin->SetUnit( aGateNumber );
2437 pin->SetName( escapeName( pin->GetName() ) );
2438
2439 if( pads.GetCount() > 1 )
2440 {
2441 pin->SetNumberTextSize( 0 );
2442 }
2443
2444 for( unsigned i = 0; i < pads.GetCount(); i++ )
2445 {
2446 SCH_PIN* apin = new SCH_PIN( *pin );
2447
2448 wxString padname( pads[i] );
2449 apin->SetNumber( padname );
2450 aSymbol->AddDrawItem( apin );
2451 }
2452
2453 break;
2454 }
2455 }
2456 }
2457 else
2458 {
2459 pin->SetUnit( aGateNumber );
2460 pin->SetNumber( wxString::Format( wxT( "%i" ), pincount ) );
2461 aSymbol->AddDrawItem( pin.release() );
2462 }
2463 }
2464
2465 for( const std::unique_ptr<EPOLYGON>& epolygon : aEsymbol->polygons )
2466 aSymbol->AddDrawItem( loadSymbolPolyLine( aSymbol, epolygon, aGateNumber ) );
2467
2468 for( const std::unique_ptr<ERECT>& erectangle : aEsymbol->rectangles )
2469 aSymbol->AddDrawItem( loadSymbolRectangle( aSymbol, erectangle, aGateNumber ) );
2470
2471 for( const std::unique_ptr<ETEXT>& etext : aEsymbol->texts )
2472 {
2473 std::unique_ptr<SCH_TEXT> libtext( loadSymbolText( aSymbol, etext, aGateNumber ) );
2474
2475 if( libtext->GetText() == wxT( "${REFERENCE}" ) )
2476 {
2477 // Move text & attributes to Reference field and discard LIB_TEXT item
2478 loadFieldAttributes( &aSymbol->GetReferenceField(), libtext.get() );
2479
2480 // Show Reference field if Eagle reference was uppercase
2481 showRefDes = etext->text == wxT( ">NAME" );
2482 }
2483 else if( libtext->GetText() == wxT( "${VALUE}" ) )
2484 {
2485 // Move text & attributes to Value field and discard LIB_TEXT item
2486 loadFieldAttributes( &aSymbol->GetValueField(), libtext.get() );
2487
2488 // Show Value field if Eagle reference was uppercase
2489 showValue = etext->text == wxT( ">VALUE" );
2490 }
2491 else if( etext->text.StartsWith( ">" ) )
2492 {
2493 // Text values that start with '>' are place holders for fields defined later
2494 // in library deviceset objects.
2495 wxString fieldName = etext->text.Mid( 1 );
2496
2497 if( !fieldName.IsEmpty() )
2498 {
2499 SCH_FIELD* field = new SCH_FIELD( aSymbol.get(), FIELD_T::USER, fieldName );
2500
2501 loadFieldAttributes( field, libtext.get() );
2502
2503 // Field visibility is determined by the symbol instance attributes.
2504 field->SetVisible( false );
2505 aSymbol->AddField( field );
2506 }
2507 }
2508 else
2509 {
2510 aSymbol->AddDrawItem( libtext.release() );
2511 }
2512 }
2513
2514 for( const std::unique_ptr<EWIRE>& ewire : aEsymbol->wires )
2515 aSymbol->AddDrawItem( loadSymbolWire( aSymbol, ewire, aGateNumber ) );
2516
2517 for( const std::unique_ptr<EFRAME>& eframe : aEsymbol->frames )
2518 {
2519 std::vector<SCH_ITEM*> frameItems;
2520
2521 loadFrame( eframe, frameItems );
2522
2523 for( SCH_ITEM* item : frameItems )
2524 {
2525 item->SetParent( aSymbol.get() );
2526 item->SetUnit( aGateNumber );
2527 aSymbol->AddDrawItem( item );
2528 }
2529 }
2530
2531 aSymbol->GetReferenceField().SetVisible( showRefDes );
2532 aSymbol->GetValueField().SetVisible( showValue );
2533
2534 return pincount == 1 ? ispower : false;
2535}
2536
2537
2538SCH_SHAPE* SCH_IO_EAGLE::loadSymbolCircle( std::unique_ptr<LIB_SYMBOL>& aSymbol,
2539 const std::unique_ptr<ECIRCLE>& aCircle,
2540 int aGateNumber )
2541{
2542 wxCHECK( aSymbol && aCircle, nullptr );
2543
2544 // Parse the circle properties
2546 VECTOR2I center( aCircle->x.ToSchUnits(), -aCircle->y.ToSchUnits() );
2547
2548 circle->SetParent( aSymbol.get() );
2549 circle->SetPosition( center );
2550 circle->SetEnd( VECTOR2I( center.x + aCircle->radius.ToSchUnits(), center.y ) );
2551 circle->SetStroke( STROKE_PARAMS( aCircle->width.ToSchUnits(), LINE_STYLE::SOLID ) );
2552 circle->SetUnit( aGateNumber );
2553
2554 return circle;
2555}
2556
2557
2558SCH_SHAPE* SCH_IO_EAGLE::loadSymbolRectangle( std::unique_ptr<LIB_SYMBOL>& aSymbol,
2559 const std::unique_ptr<ERECT>& aRectangle,
2560 int aGateNumber )
2561{
2562 wxCHECK( aSymbol && aRectangle, nullptr );
2563
2564 SCH_SHAPE* rectangle = new SCH_SHAPE( SHAPE_T::RECTANGLE );
2565
2566 rectangle->SetParent( aSymbol.get() );
2567 rectangle->SetPosition( VECTOR2I( aRectangle->x1.ToSchUnits(), -aRectangle->y1.ToSchUnits() ) );
2568 rectangle->SetEnd( VECTOR2I( aRectangle->x2.ToSchUnits(), -aRectangle->y2.ToSchUnits() ) );
2569
2570 if( aRectangle->rot )
2571 {
2572 VECTOR2I pos( rectangle->GetPosition() );
2573 VECTOR2I end( rectangle->GetEnd() );
2574 VECTOR2I center( rectangle->GetCenter() );
2575
2576 RotatePoint( pos, center, EDA_ANGLE( aRectangle->rot->degrees, DEGREES_T ) );
2577 RotatePoint( end, center, EDA_ANGLE( aRectangle->rot->degrees, DEGREES_T ) );
2578
2579 rectangle->SetPosition( pos );
2580 rectangle->SetEnd( end );
2581 }
2582
2583 rectangle->SetUnit( aGateNumber );
2584
2585 // Eagle rectangles are filled by definition.
2586 rectangle->SetFillMode( FILL_T::FILLED_SHAPE );
2587
2588 return rectangle;
2589}
2590
2591
2592SCH_ITEM* SCH_IO_EAGLE::loadSymbolWire( std::unique_ptr<LIB_SYMBOL>& aSymbol,
2593 const std::unique_ptr<EWIRE>& aWire, int aGateNumber )
2594{
2595 wxCHECK( aSymbol && aWire, nullptr );
2596
2597 VECTOR2I begin, end;
2598
2599 begin.x = aWire->x1.ToSchUnits();
2600 begin.y = -aWire->y1.ToSchUnits();
2601 end.x = aWire->x2.ToSchUnits();
2602 end.y = -aWire->y2.ToSchUnits();
2603
2604 if( begin == end )
2605 return nullptr;
2606
2607 // if the wire is an arc
2608 if( aWire->curve )
2609 {
2611 VECTOR2I center = ConvertArcCenter( begin, end, *aWire->curve );
2612 double radius = sqrt( ( ( center.x - begin.x ) * ( center.x - begin.x ) ) +
2613 ( ( center.y - begin.y ) * ( center.y - begin.y ) ) );
2614
2615 arc->SetParent( aSymbol.get() );
2616
2617 // this emulates the filled semicircles created by a thick arc with flat ends caps.
2618 if( aWire->cap == EWIRE::FLAT && aWire->width.ToSchUnits() >= 2 * radius )
2619 {
2620 VECTOR2I centerStartVector = ( begin - center ) *
2621 ( aWire->width.ToSchUnits() / radius );
2622 begin = center + centerStartVector;
2623
2626 }
2627 else
2628 {
2629 arc->SetStroke( STROKE_PARAMS( aWire->width.ToSchUnits(), LINE_STYLE::SOLID ) );
2630 }
2631
2632 arc->SetCenter( center );
2633 arc->SetStart( begin );
2634
2635 // KiCad rotates the other way.
2636 arc->SetArcAngleAndEnd( -EDA_ANGLE( *aWire->curve, DEGREES_T ), true );
2637 arc->SetUnit( aGateNumber );
2638
2639 return arc;
2640 }
2641 else
2642 {
2644
2645 poly->AddPoint( begin );
2646 poly->AddPoint( end );
2647 poly->SetUnit( aGateNumber );
2648 poly->SetStroke( STROKE_PARAMS( aWire->width.ToSchUnits(), LINE_STYLE::SOLID ) );
2649
2650 return poly;
2651 }
2652}
2653
2654
2655SCH_SHAPE* SCH_IO_EAGLE::loadSymbolPolyLine( std::unique_ptr<LIB_SYMBOL>& aSymbol,
2656 const std::unique_ptr<EPOLYGON>& aPolygon,
2657 int aGateNumber )
2658{
2659 wxCHECK( aSymbol && aPolygon, nullptr );
2660
2661 SCH_SHAPE* poly = new SCH_SHAPE( SHAPE_T::POLY );
2662 VECTOR2I pt, prev_pt;
2663 opt_double prev_curve;
2664
2665 poly->SetParent( aSymbol.get() );
2666
2667 for( const std::unique_ptr<EVERTEX>& evertex : aPolygon->vertices )
2668 {
2669 pt = VECTOR2I( evertex->x.ToSchUnits(), evertex->y.ToSchUnits() );
2670
2671 if( prev_curve )
2672 {
2673 SHAPE_ARC arc;
2674 arc.ConstructFromStartEndAngle( prev_pt, pt, -EDA_ANGLE( *prev_curve, DEGREES_T ) );
2675 poly->GetPolyShape().Append( arc, -1, -1, ARC_ACCURACY );
2676 }
2677 else
2678 {
2679 poly->AddPoint( pt );
2680 }
2681
2682 prev_pt = pt;
2683 prev_curve = evertex->curve;
2684 }
2685
2686 poly->SetStroke( STROKE_PARAMS( aPolygon->width.ToSchUnits(), LINE_STYLE::SOLID ) );
2688 poly->SetUnit( aGateNumber );
2689
2690 return poly;
2691}
2692
2693
2694SCH_PIN* SCH_IO_EAGLE::loadPin( std::unique_ptr<LIB_SYMBOL>& aSymbol,
2695 const std::unique_ptr<EPIN>& aPin, int aGateNumber )
2696{
2697 wxCHECK( aSymbol && aPin, nullptr );
2698
2699 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( aSymbol.get() );
2700 pin->SetPosition( VECTOR2I( aPin->x.ToSchUnits(), -aPin->y.ToSchUnits() ) );
2701
2702 // Eagle pin names may carry a trailing "@<tag>" linking hint that disambiguates
2703 // duplicate names within a symbol. It is metadata, not visible text, so strip it
2704 // from the displayed name. The full Eagle name is still used to match <connect>.
2705 pin->SetName( extractNetName( aPin->name ) );
2706 pin->SetUnit( aGateNumber );
2707
2708 int roti = aPin->rot ? aPin->rot->degrees : 0;
2709
2710 switch( roti )
2711 {
2712 case 0: pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); break;
2713 case 90: pin->SetOrientation( PIN_ORIENTATION::PIN_UP ); break;
2714 case 180: pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); break;
2715 case 270: pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); break;
2716 default: wxFAIL_MSG( wxString::Format( wxT( "Unhandled orientation (%d degrees)." ), roti ) );
2717 }
2718
2719 pin->SetLength( schIUScale.MilsToIU( 300 ) ); // Default pin length when not defined.
2720
2721 if( aPin->length )
2722 {
2723 wxString length = aPin->length.Get();
2724
2725 if( length == wxT( "short" ) )
2726 pin->SetLength( schIUScale.MilsToIU( 100 ) );
2727 else if( length == wxT( "middle" ) )
2728 pin->SetLength( schIUScale.MilsToIU( 200 ) );
2729 else if( length == wxT( "long" ) )
2730 pin->SetLength( schIUScale.MilsToIU( 300 ) );
2731 else if( length == wxT( "point" ) )
2732 pin->SetLength( schIUScale.MilsToIU( 0 ) );
2733 }
2734
2735 // Pin names and numbers are fixed size in Eagle.
2736 pin->SetNumberTextSize( schIUScale.MilsToIU( 60 ) );
2737 pin->SetNameTextSize( schIUScale.MilsToIU( 60 ) );
2738
2739 // emulate the visibility of pin elements
2740 if( aPin->visible )
2741 {
2742 wxString visible = aPin->visible.Get();
2743
2744 if( visible == wxT( "off" ) )
2745 {
2746 pin->SetNameTextSize( 0 );
2747 pin->SetNumberTextSize( 0 );
2748 }
2749 else if( visible == wxT( "pad" ) )
2750 {
2751 pin->SetNameTextSize( 0 );
2752 }
2753 else if( visible == wxT( "pin" ) )
2754 {
2755 pin->SetNumberTextSize( 0 );
2756 }
2757
2758 /*
2759 * else if( visible == wxT( "both" ) )
2760 * {
2761 * }
2762 */
2763 }
2764
2765 if( aPin->function )
2766 {
2767 wxString function = aPin->function.Get();
2768
2769 if( function == wxT( "dot" ) )
2770 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
2771 else if( function == wxT( "clk" ) )
2772 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
2773 else if( function == wxT( "dotclk" ) )
2775 }
2776
2777 return pin.release();
2778}
2779
2780
2781SCH_TEXT* SCH_IO_EAGLE::loadSymbolText( std::unique_ptr<LIB_SYMBOL>& aSymbol,
2782 const std::unique_ptr<ETEXT>& aText, int aGateNumber )
2783{
2784 wxCHECK( aSymbol && aText, nullptr );
2785
2786 std::unique_ptr<SCH_TEXT> libtext = std::make_unique<SCH_TEXT>();
2787
2788 libtext->SetParent( aSymbol.get() );
2789 libtext->SetUnit( aGateNumber );
2790 libtext->SetPosition( VECTOR2I( aText->x.ToSchUnits(), -aText->y.ToSchUnits() ) );
2791
2792 const wxString& eagleText = aText->text;
2793 wxString adjustedText;
2794 wxStringTokenizer tokenizer( eagleText, "\r\n" );
2795
2796 // Strip the whitespace from both ends of each line.
2797 while( tokenizer.HasMoreTokens() )
2798 {
2799 wxString tmp = interpretText( tokenizer.GetNextToken().Trim( true ).Trim( false ) );
2800
2801 if( tokenizer.HasMoreTokens() )
2802 tmp += wxT( "\n" );
2803
2804 adjustedText += tmp;
2805 }
2806
2807 libtext->SetText( adjustedText.IsEmpty() ? wxString( wxS( "~" ) ) : adjustedText );
2808
2809 loadTextAttributes( libtext.get(), aText );
2810
2811 return libtext.release();
2812}
2813
2814
2815SCH_TEXT* SCH_IO_EAGLE::loadPlainText( const std::unique_ptr<ETEXT>& aText )
2816{
2817 wxCHECK( aText, nullptr );
2818
2819 std::unique_ptr<SCH_TEXT> schtext = std::make_unique<SCH_TEXT>();
2820
2821 const wxString& eagleText = aText->text;
2822 wxString adjustedText;
2823 wxStringTokenizer tokenizer( eagleText, "\r\n" );
2824
2825 // Strip the whitespace from both ends of each line.
2826 while( tokenizer.HasMoreTokens() )
2827 {
2828 wxString tmp = interpretText( tokenizer.GetNextToken().Trim( true ).Trim( false ) );
2829
2830 if( tokenizer.HasMoreTokens() )
2831 tmp += wxT( "\n" );
2832
2833 adjustedText += tmp;
2834 }
2835
2836 schtext->SetText( adjustedText.IsEmpty() ? wxString( wxS( "\" \"" ) )
2837 : escapeName( adjustedText ) );
2838
2839 schtext->SetPosition( VECTOR2I( aText->x.ToSchUnits(), -aText->y.ToSchUnits() ) );
2840 loadTextAttributes( schtext.get(), aText );
2841 schtext->SetItalic( false );
2842
2843 return schtext.release();
2844}
2845
2846
2848 const std::unique_ptr<ETEXT>& aAttributes ) const
2849{
2850 wxCHECK( aText && aAttributes, /* void */ );
2851
2852 aText->SetTextSize( aAttributes->ConvertSize() );
2853
2854 // Must come after SetTextSize()
2855 if( aAttributes->ratio && aAttributes->ratio.CGet() > 12 )
2856 aText->SetBold( true );
2857
2858 int align = aAttributes->align ? *aAttributes->align : ETEXT::BOTTOM_LEFT;
2859 int degrees = aAttributes->rot ? aAttributes->rot->degrees : 0;
2860 bool mirror = aAttributes->rot ? aAttributes->rot->mirror : false;
2861 bool spin = aAttributes->rot ? aAttributes->rot->spin : false;
2862
2863 eagleToKicadAlignment( aText, align, degrees, mirror, spin, 0 );
2864}
2865
2866
2867void SCH_IO_EAGLE::loadFieldAttributes( SCH_FIELD* aField, const SCH_TEXT* aText ) const
2868{
2869 wxCHECK( aField && aText, /* void */ );
2870
2871 aField->SetTextPos( aText->GetPosition() );
2872 aField->SetTextSize( aText->GetTextSize() );
2873 aField->SetTextAngle( aText->GetTextAngle() );
2874
2875 // Must come after SetTextSize()
2876 aField->SetBold( aText->IsBold() );
2877 aField->SetItalic( false );
2878
2879 aField->SetVertJustify( aText->GetVertJustify() );
2880 aField->SetHorizJustify( aText->GetHorizJustify() );
2881}
2882
2883
2885{
2886 // Eagle supports detached labels, so a label does not need to be placed on a wire
2887 // to be associated with it. KiCad needs to move them, so the labels actually touch the
2888 // corresponding wires.
2889
2890 // Sort the intersection points to speed up the search process
2891 std::sort( m_wireIntersections.begin(), m_wireIntersections.end() );
2892
2893 auto onIntersection =
2894 [&]( const VECTOR2I& aPos )
2895 {
2896 return std::binary_search( m_wireIntersections.begin(),
2897 m_wireIntersections.end(), aPos );
2898 };
2899
2900 for( SEG_DESC& segDesc : m_segments )
2901 {
2902 for( SCH_LABEL_BASE* label : segDesc.labels )
2903 {
2904 VECTOR2I labelPos( label->GetPosition() );
2905 const SEG* segAttached = segDesc.LabelAttached( label );
2906
2907 if( segAttached && !onIntersection( labelPos ) )
2908 continue; // label is placed correctly
2909
2910 // Move the label to the nearest wire
2911 if( !segAttached )
2912 {
2913 std::tie( labelPos, segAttached ) = findNearestLinePoint( label->GetPosition(),
2914 segDesc.segs );
2915
2916 if( !segAttached ) // we cannot do anything
2917 continue;
2918 }
2919
2920 // Create a vector pointing in the direction of the wire, 50 mils long
2921 VECTOR2I wireDirection( segAttached->B - segAttached->A );
2922
2923 if( ( wireDirection.x == 0 ) && (wireDirection.y == 0 ) )
2924 continue;
2925
2926 wireDirection = wireDirection.Resize( schIUScale.MilsToIU( 50 ) );
2927 const VECTOR2I origPos( labelPos );
2928
2929 // Flags determining the search direction
2930 bool checkPositive = true;
2931 bool checkNegative = true;
2932 bool move = false;
2933 int trial = 0;
2934
2935 // Be sure the label is not placed on a wire intersection
2936 while( ( !move || onIntersection( labelPos ) ) && ( checkPositive || checkNegative ) )
2937 {
2938 move = false;
2939
2940 // Move along the attached wire to find the new label position
2941 if( trial % 2 == 1 )
2942 {
2943 labelPos = VECTOR2I( origPos + wireDirection * trial / 2 );
2944 move = checkPositive = segAttached->Contains( labelPos );
2945 }
2946 else
2947 {
2948 labelPos = VECTOR2I( origPos - wireDirection * trial / 2 );
2949 move = checkNegative = segAttached->Contains( labelPos );
2950 }
2951
2952 ++trial;
2953 }
2954
2955 if( move )
2956 {
2957 label->SetPosition( VECTOR2I( labelPos ) );
2958
2959 if( wireDirection.x == 0 ) // Moved vertically
2960 {
2961 if( wireDirection.y < 0 )
2962 label->SetSpinStyle( SPIN_STYLE::UP );
2963 else
2965 }
2966 else if( wireDirection.y == 0 ) // Moved horizontally
2967 {
2968 if( wireDirection.x < 0 )
2970 else
2972 }
2973 }
2974 }
2975 }
2976
2977 m_segments.clear();
2978 m_wireIntersections.clear();
2979}
2980
2981
2982bool SCH_IO_EAGLE::CanReadSchematicFile( const wxString& aFileName ) const
2983{
2984 if( !SCH_IO::CanReadSchematicFile( aFileName ) )
2985 return false;
2986
2987 return checkHeader( aFileName );
2988}
2989
2990
2991bool SCH_IO_EAGLE::CanReadLibrary( const wxString& aFileName ) const
2992{
2993 if( !SCH_IO::CanReadLibrary( aFileName ) )
2994 return false;
2995
2996 return checkHeader( aFileName );
2997}
2998
2999
3000bool SCH_IO_EAGLE::checkHeader( const wxString& aFileName ) const
3001{
3002 wxFileInputStream input( aFileName );
3003
3004 if( !input.IsOk() )
3005 return false;
3006
3007 wxTextInputStream text( input );
3008
3009 for( int i = 0; i < 8; i++ )
3010 {
3011 if( input.Eof() )
3012 return false;
3013
3014 if( text.ReadLine().Contains( wxS( "<eagle" ) ) )
3015 return true;
3016 }
3017
3018 return false;
3019}
3020
3021
3022void SCH_IO_EAGLE::moveLabels( SCH_LINE* aWire, const VECTOR2I& aNewEndPoint )
3023{
3024 wxCHECK( aWire, /* void */ );
3025
3026 SCH_SCREEN* screen = getCurrentScreen();
3027
3028 wxCHECK( screen, /* void */ );
3029
3030 for( SCH_ITEM* item : screen->Items().Overlapping( aWire->GetBoundingBox() ) )
3031 {
3032 if( !item->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
3033 continue;
3034
3035 if( TestSegmentHit( item->GetPosition(), aWire->GetStartPoint(), aWire->GetEndPoint(), 0 ) )
3036 item->SetPosition( aNewEndPoint );
3037 }
3038}
3039
3040
3042{
3043 // Add bus entry symbols
3044 // TODO: Cleanup this function and break into pieces
3045
3046 // for each wire segment, compare each end with all busses.
3047 // If the wire end is found to end on a bus segment, place a bus entry symbol.
3048
3049 std::vector<SCH_LINE*> buses;
3050 std::vector<SCH_LINE*> wires;
3051
3052 SCH_SCREEN* screen = getCurrentScreen();
3053
3054 wxCHECK( screen, /* void */ );
3055
3056 for( SCH_ITEM* ii : screen->Items().OfType( SCH_LINE_T ) )
3057 {
3058 SCH_LINE* line = static_cast<SCH_LINE*>( ii );
3059
3060 if( line->IsBus() )
3061 buses.push_back( line );
3062 else if( line->IsWire() )
3063 wires.push_back( line );
3064 }
3065
3066 for( SCH_LINE* wire : wires )
3067 {
3068 VECTOR2I wireStart = wire->GetStartPoint();
3069 VECTOR2I wireEnd = wire->GetEndPoint();
3070
3071 for( SCH_LINE* bus : buses )
3072 {
3073 VECTOR2I busStart = bus->GetStartPoint();
3074 VECTOR2I busEnd = bus->GetEndPoint();
3075
3076 auto entrySize =
3077 []( int signX, int signY ) -> VECTOR2I
3078 {
3079 return VECTOR2I( schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE ) * signX,
3080 schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE ) * signY );
3081 };
3082
3083 auto testBusHit =
3084 [&]( const VECTOR2I& aPt ) -> bool
3085 {
3086 return TestSegmentHit( aPt, busStart, busEnd, 0 );
3087 };
3088
3089 if( wireStart.y == wireEnd.y && busStart.x == busEnd.x )
3090 {
3091 // Horizontal wire and vertical bus
3092
3093 if( testBusHit( wireStart ) )
3094 {
3095 // Wire start is on the vertical bus
3096
3097 if( wireEnd.x < busStart.x )
3098 {
3099 /* the end of the wire is to the left of the bus
3100 * ⎥⎢
3101 * ——————⎥⎢
3102 * ⎥⎢
3103 */
3104 VECTOR2I p = wireStart + entrySize( -1, 0 );
3105
3106 if( testBusHit( wireStart + entrySize( 0, -1 ) ) )
3107 {
3108 /* there is room above the wire for the bus entry
3109 * ⎥⎢
3110 * _____/⎥⎢
3111 * ⎥⎢
3112 */
3113 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 1 );
3114 busEntry->SetFlags( IS_NEW );
3115 screen->Append( busEntry );
3116 moveLabels( wire, p );
3117 wire->SetStartPoint( p );
3118 }
3119 else if( testBusHit( wireStart + entrySize( 0, 1 ) ) )
3120 {
3121 /* there is room below the wire for the bus entry
3122 * _____ ⎥⎢
3123 * \⎥⎢
3124 * ⎥⎢
3125 */
3126 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 2 );
3127 busEntry->SetFlags( IS_NEW );
3128 screen->Append( busEntry );
3129 moveLabels( wire, p );
3130 wire->SetStartPoint( p );
3131 }
3132 else
3133 {
3134 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3135 screen->Append( new SCH_MARKER( std::move( ercItem ), wireStart ) );
3136 }
3137 }
3138 else
3139 {
3140 /* the wire end is to the right of the bus
3141 * ⎥⎢
3142 * ⎥⎢——————
3143 * ⎥⎢
3144 */
3145 VECTOR2I p = wireStart + entrySize( 1, 0 );
3146
3147 if( testBusHit( wireStart + entrySize( 0, -1 ) ) )
3148 {
3149 /* There is room above the wire for the bus entry
3150 * ⎥⎢
3151 * ⎥⎢\_____
3152 * ⎥⎢
3153 */
3154 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p , 4 );
3155 busEntry->SetFlags( IS_NEW );
3156 screen->Append( busEntry );
3157 moveLabels( wire, p );
3158 wire->SetStartPoint( p );
3159 }
3160 else if( testBusHit( wireStart + entrySize( 0, 1 ) ) )
3161 {
3162 /* There is room below the wire for the bus entry
3163 * ⎥⎢ _____
3164 * ⎥⎢/
3165 * ⎥⎢
3166 */
3167 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 3 );
3168 busEntry->SetFlags( IS_NEW );
3169 screen->Append( busEntry );
3170 moveLabels( wire, p );
3171 wire->SetStartPoint( p );
3172 }
3173 else
3174 {
3175 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3176 screen->Append( new SCH_MARKER( std::move( ercItem ), wireStart ) );
3177 }
3178 }
3179
3180 break;
3181 }
3182 else if( testBusHit( wireEnd ) )
3183 {
3184 // Wire end is on the vertical bus
3185
3186 if( wireStart.x < busStart.x )
3187 {
3188 /* start of the wire is to the left of the bus
3189 * ⎥⎢
3190 * ——————⎥⎢
3191 * ⎥⎢
3192 */
3193 VECTOR2I p = wireEnd + entrySize( -1, 0 );
3194
3195 if( testBusHit( wireEnd + entrySize( 0, -1 ) ) )
3196 {
3197 /* there is room above the wire for the bus entry
3198 * ⎥⎢
3199 * _____/⎥⎢
3200 * ⎥⎢
3201 */
3202 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 1 );
3203 busEntry->SetFlags( IS_NEW );
3204 screen->Append( busEntry );
3205 moveLabels( wire, p );
3206 wire->SetEndPoint( p );
3207 }
3208 else if( testBusHit( wireEnd + entrySize( 0, -1 ) ) )
3209 {
3210 /* there is room below the wire for the bus entry
3211 * _____ ⎥⎢
3212 * \⎥⎢
3213 * ⎥⎢
3214 */
3215 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 2 );
3216 busEntry->SetFlags( IS_NEW );
3217 screen->Append( busEntry );
3218 moveLabels( wire, wireEnd + entrySize( -1, 0 ) );
3219 wire->SetEndPoint( wireEnd + entrySize( -1, 0 ) );
3220 }
3221 else
3222 {
3223 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3224 screen->Append( new SCH_MARKER( std::move( ercItem ), wireEnd ) );
3225 }
3226 }
3227 else
3228 {
3229 /* the start of the wire is to the right of the bus
3230 * ⎥⎢
3231 * ⎥⎢——————
3232 * ⎥⎢
3233 */
3234 VECTOR2I p = wireEnd + entrySize( 1, 0 );
3235
3236 if( testBusHit( wireEnd + entrySize( 0, -1 ) ) )
3237 {
3238 /* There is room above the wire for the bus entry
3239 * ⎥⎢
3240 * ⎥⎢\_____
3241 * ⎥⎢
3242 */
3243 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 4 );
3244 busEntry->SetFlags( IS_NEW );
3245 screen->Append( busEntry );
3246 moveLabels( wire, p );
3247 wire->SetEndPoint( p );
3248 }
3249 else if( testBusHit( wireEnd + entrySize( 0, 1 ) ) )
3250 {
3251 /* There is room below the wire for the bus entry
3252 * ⎥⎢ _____
3253 * ⎥⎢/
3254 * ⎥⎢
3255 */
3256 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 3 );
3257 busEntry->SetFlags( IS_NEW );
3258 screen->Append( busEntry );
3259 moveLabels( wire, p );
3260 wire->SetEndPoint( p );
3261 }
3262 else
3263 {
3264 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3265 screen->Append( new SCH_MARKER( std::move( ercItem ), wireEnd ) );
3266 }
3267 }
3268
3269 break;
3270 }
3271 }
3272 else if( wireStart.x == wireEnd.x && busStart.y == busEnd.y )
3273 {
3274 // Vertical wire and horizontal bus
3275
3276 if( testBusHit( wireStart ) )
3277 {
3278 // Wire start is on the bus
3279
3280 if( wireEnd.y < busStart.y )
3281 {
3282 /* the end of the wire is above the bus
3283 * |
3284 * |
3285 * |
3286 * =======
3287 */
3288 VECTOR2I p = wireStart + entrySize( 0, -1 );
3289
3290 if( testBusHit( wireStart + entrySize( -1, 0 ) ) )
3291 {
3292 /* there is room to the left of the wire for the bus entry
3293 * |
3294 * |
3295 * /
3296 * =======
3297 */
3298 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 3 );
3299 busEntry->SetFlags( IS_NEW );
3300 screen->Append( busEntry );
3301 moveLabels( wire, p );
3302 wire->SetStartPoint( p );
3303 }
3304 else if( testBusHit( wireStart + entrySize( 1, 0 ) ) )
3305 {
3306 /* there is room to the right of the wire for the bus entry
3307 * |
3308 * |
3309 * \
3310 * =======
3311 */
3312 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 2 );
3313 busEntry->SetFlags( IS_NEW );
3314 screen->Append( busEntry );
3315 moveLabels( wire, p );
3316 wire->SetStartPoint( p );
3317 }
3318 else
3319 {
3320 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3321 screen->Append( new SCH_MARKER( std::move( ercItem ), wireStart ) );
3322 }
3323 }
3324 else
3325 {
3326 /* wire end is below the bus
3327 * =======
3328 * |
3329 * |
3330 * |
3331 */
3332 VECTOR2I p = wireStart + entrySize( 0, 1 );
3333
3334 if( testBusHit( wireStart + entrySize( -1, 0 ) ) )
3335 {
3336 /* there is room to the left of the wire for the bus entry
3337 * =======
3338 * \
3339 * |
3340 * |
3341 */
3342 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 4 );
3343 busEntry->SetFlags( IS_NEW );
3344 screen->Append( busEntry );
3345 moveLabels( wire, p );
3346 wire->SetStartPoint( p );
3347 }
3348 else if( testBusHit( wireStart + entrySize( 1, 0 ) ) )
3349 {
3350 /* there is room to the right of the wire for the bus entry
3351 * =======
3352 * /
3353 * |
3354 * |
3355 */
3356 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 1 );
3357 busEntry->SetFlags( IS_NEW );
3358 screen->Append( busEntry );
3359 moveLabels( wire, p );
3360 wire->SetStartPoint( p );
3361 }
3362 else
3363 {
3364 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3365 screen->Append( new SCH_MARKER( std::move( ercItem ), wireStart ) );
3366 }
3367 }
3368
3369 break;
3370 }
3371 else if( testBusHit( wireEnd ) )
3372 {
3373 // Wire end is on the bus
3374
3375 if( wireStart.y < busStart.y )
3376 {
3377 /* the start of the wire is above the bus
3378 * |
3379 * |
3380 * |
3381 * =======
3382 */
3383 VECTOR2I p = wireEnd + entrySize( 0, -1 );
3384
3385 if( testBusHit( wireEnd + entrySize( -1, 0 ) ) )
3386 {
3387 /* there is room to the left of the wire for the bus entry
3388 * |
3389 * |
3390 * /
3391 * =======
3392 */
3393 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 3 );
3394 busEntry->SetFlags( IS_NEW );
3395 screen->Append( busEntry );
3396 moveLabels( wire, p );
3397 wire->SetEndPoint( p );
3398 }
3399 else if( testBusHit( wireEnd + entrySize( 1, 0 ) ) )
3400 {
3401 /* there is room to the right of the wire for the bus entry
3402 * |
3403 * |
3404 * \
3405 * =======
3406 */
3407 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 2 );
3408 busEntry->SetFlags( IS_NEW );
3409 screen->Append( busEntry );
3410 moveLabels( wire, p );
3411 wire->SetEndPoint( p );
3412 }
3413 else
3414 {
3415 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3416 screen->Append( new SCH_MARKER( std::move( ercItem ), wireEnd ) );
3417 }
3418 }
3419 else
3420 {
3421 /* wire start is below the bus
3422 * =======
3423 * |
3424 * |
3425 * |
3426 */
3427 VECTOR2I p = wireEnd + entrySize( 0, 1 );
3428
3429 if( testBusHit( wireEnd + entrySize( -1, 0 ) ) )
3430 {
3431 /* there is room to the left of the wire for the bus entry
3432 * =======
3433 * \
3434 * |
3435 * |
3436 */
3437 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 4 );
3438 busEntry->SetFlags( IS_NEW );
3439 screen->Append( busEntry );
3440 moveLabels( wire, p );
3441 wire->SetEndPoint( p );
3442 }
3443 else if( testBusHit( wireEnd + entrySize( 1, 0 ) ) )
3444 {
3445 /* there is room to the right of the wire for the bus entry
3446 * =======
3447 * /
3448 * |
3449 * |
3450 */
3451 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 1 );
3452 busEntry->SetFlags( IS_NEW );
3453 screen->Append( busEntry );
3454 moveLabels( wire, p );
3455 wire->SetEndPoint( p );
3456 }
3457 else
3458 {
3459 std::shared_ptr<ERC_ITEM> ercItem = ERC_ITEM::Create( ERCE_BUS_ENTRY_NEEDED );
3460 screen->Append( new SCH_MARKER( std::move( ercItem ), wireEnd ) );
3461 }
3462 }
3463
3464 break;
3465 }
3466 }
3467 else
3468 {
3469 // Wire isn't horizontal or vertical
3470
3471 if( testBusHit( wireStart ) )
3472 {
3473 VECTOR2I wirevector = wireStart - wireEnd;
3474
3475 if( wirevector.x > 0 )
3476 {
3477 if( wirevector.y > 0 )
3478 {
3479 VECTOR2I p = wireStart + entrySize( -1, -1 );
3480 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 2 );
3481 busEntry->SetFlags( IS_NEW );
3482 screen->Append( busEntry );
3483
3484 moveLabels( wire, p );
3485 wire->SetStartPoint( p );
3486 }
3487 else
3488 {
3489 VECTOR2I p = wireStart + entrySize( -1, 1 );
3490 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 1 );
3491 busEntry->SetFlags( IS_NEW );
3492 screen->Append( busEntry );
3493
3494 moveLabels( wire, p );
3495 wire->SetStartPoint( p );
3496 }
3497 }
3498 else
3499 {
3500 if( wirevector.y > 0 )
3501 {
3502 VECTOR2I p = wireStart + entrySize( 1, -1 );
3503 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 3 );
3504 busEntry->SetFlags( IS_NEW );
3505 screen->Append( busEntry );
3506
3507 moveLabels( wire, p );
3508 wire->SetStartPoint( p );
3509 }
3510 else
3511 {
3512 VECTOR2I p = wireStart + entrySize( 1, 1 );
3513 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 4 );
3514 busEntry->SetFlags( IS_NEW );
3515 screen->Append( busEntry );
3516
3517 moveLabels( wire, p );
3518 wire->SetStartPoint( p );
3519 }
3520 }
3521
3522 break;
3523 }
3524 else if( testBusHit( wireEnd ) )
3525 {
3526 VECTOR2I wirevector = wireStart - wireEnd;
3527
3528 if( wirevector.x > 0 )
3529 {
3530 if( wirevector.y > 0 )
3531 {
3532 VECTOR2I p = wireEnd + entrySize( 1, 1 );
3533 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 4 );
3534 busEntry->SetFlags( IS_NEW );
3535 screen->Append( busEntry );
3536
3537 moveLabels( wire, p );
3538 wire->SetEndPoint( p );
3539 }
3540 else
3541 {
3542 VECTOR2I p = wireEnd + entrySize( 1, -1 );
3543 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 3 );
3544 busEntry->SetFlags( IS_NEW );
3545 screen->Append( busEntry );
3546
3547 moveLabels( wire, p );
3548 wire->SetEndPoint( p );
3549 }
3550 }
3551 else
3552 {
3553 if( wirevector.y > 0 )
3554 {
3555 VECTOR2I p = wireEnd + entrySize( -1, 1 );
3556 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 1 );
3557 busEntry->SetFlags( IS_NEW );
3558 screen->Append( busEntry );
3559
3560 moveLabels( wire, p );
3561 wire->SetEndPoint( p );
3562 }
3563 else
3564 {
3565 VECTOR2I p = wireEnd + entrySize( -1, -1 );
3566 SCH_BUS_WIRE_ENTRY* busEntry = new SCH_BUS_WIRE_ENTRY( p, 2 );
3567 busEntry->SetFlags( IS_NEW );
3568 screen->Append( busEntry );
3569
3570 moveLabels( wire, p );
3571 wire->SetEndPoint( p );
3572 }
3573 }
3574
3575 break;
3576 }
3577 }
3578 }
3579 }
3580}
3581
3582
3584{
3585 wxCHECK( aLabel, nullptr );
3586
3587 VECTOR2I labelPos( aLabel->GetPosition() );
3588
3589 for( const SEG& seg : segs )
3590 {
3591 if( seg.Contains( labelPos ) )
3592 return &seg;
3593 }
3594
3595 return nullptr;
3596}
3597
3598
3599// TODO could be used to place junctions, instead of IsJunctionNeeded()
3600// (see SCH_EDIT_FRAME::importFile())
3601bool SCH_IO_EAGLE::checkConnections( const SCH_SYMBOL* aSymbol, const SCH_PIN* aPin ) const
3602{
3603 wxCHECK( aSymbol && aPin, false );
3604
3605 VECTOR2I pinPosition = aSymbol->GetPinPhysicalPosition( aPin );
3606 auto pointIt = m_connPoints.find( pinPosition );
3607
3608 if( pointIt == m_connPoints.end() )
3609 return false;
3610
3611 const auto& items = pointIt->second;
3612
3613 wxCHECK( items.find( aPin ) != items.end(), false );
3614
3615 return items.size() > 1;
3616}
3617
3618
3620 bool aUpdateSet )
3621{
3622 wxCHECK( aSymbol && aScreen && aSymbol->GetLibSymbolRef(), /*void*/ );
3623
3624 // Normally power parts also have power input pins,
3625 // but they already force net names on the attached wires
3626 if( aSymbol->GetLibSymbolRef()->IsGlobalPower() )
3627 return;
3628
3629 int unit = aSymbol->GetUnit();
3630 const wxString reference = aSymbol->GetField( FIELD_T::REFERENCE )->GetText();
3631 std::vector<SCH_PIN*> pins = aSymbol->GetLibSymbolRef()->GetGraphicalPins( 0, 0 );
3632 std::set<int> missingUnits;
3633
3634 // Search all units for pins creating implicit connections
3635 for( const SCH_PIN* pin : pins )
3636 {
3637 if( pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN )
3638 {
3639 bool pinInUnit = !unit || pin->GetUnit() == unit; // pin belongs to the tested unit
3640
3641 // Create a global net label only if there are no other wires/pins attached
3642 if( pinInUnit )
3643 {
3644 if( !checkConnections( aSymbol, pin ) )
3645 {
3646 // Create a net label to force the net name on the pin
3647 SCH_GLOBALLABEL* netLabel = new SCH_GLOBALLABEL;
3648 netLabel->SetPosition( aSymbol->GetPinPhysicalPosition( pin ) );
3649 netLabel->SetText( extractNetName( pin->GetName() ) );
3650 netLabel->SetTextSize( VECTOR2I( schIUScale.MilsToIU( 40 ),
3651 schIUScale.MilsToIU( 40 ) ) );
3652
3653 switch( pin->GetOrientation() )
3654 {
3655 default:
3657 netLabel->SetSpinStyle( SPIN_STYLE::LEFT );
3658 break;
3660 netLabel->SetSpinStyle( SPIN_STYLE::RIGHT );
3661 break;
3663 netLabel->SetSpinStyle( SPIN_STYLE::UP );
3664 break;
3666 netLabel->SetSpinStyle( SPIN_STYLE::BOTTOM );
3667 break;
3668 }
3669
3670 aScreen->Append( netLabel );
3671 }
3672 }
3673 else if( aUpdateSet )
3674 {
3675 // Found a pin creating implicit connection information in another unit.
3676 // Such units will be instantiated if they do not appear in another sheet and
3677 // processed later.
3678 wxASSERT( pin->GetUnit() );
3679 missingUnits.insert( pin->GetUnit() );
3680 }
3681 }
3682 }
3683
3684 if( aUpdateSet && aSymbol->GetLibSymbolRef()->GetUnitCount() > 1 )
3685 {
3686 auto cmpIt = m_missingCmps.find( reference );
3687
3688 // The first unit found has always already been processed.
3689 if( cmpIt == m_missingCmps.end() )
3690 {
3691 EAGLE_MISSING_CMP& entry = m_missingCmps[reference];
3692 entry.cmp = aSymbol;
3693 entry.screen = aScreen;
3694 entry.units.emplace( unit, false );
3695 }
3696 else
3697 {
3698 // Set the flag indicating this unit has been processed.
3699 cmpIt->second.units[unit] = false;
3700 }
3701
3702 if( !missingUnits.empty() ) // Save the units that need later processing
3703 {
3704 EAGLE_MISSING_CMP& entry = m_missingCmps[reference];
3705 entry.cmp = aSymbol;
3706 entry.screen = aScreen;
3707
3708 // Add units that haven't already been processed.
3709 for( int i : missingUnits )
3710 {
3711 if( entry.units.find( i ) != entry.units.end() )
3712 entry.units.emplace( i, true );
3713 }
3714 }
3715 }
3716}
3717
3718
3719wxString SCH_IO_EAGLE::translateEagleBusName( const wxString& aEagleName ) const
3720{
3721 if( NET_SETTINGS::ParseBusVector( aEagleName, nullptr, nullptr ) )
3722 return aEagleName;
3723
3724 wxString ret = wxT( "{" );
3725
3726 wxStringTokenizer tokenizer( aEagleName, "," );
3727
3728 while( tokenizer.HasMoreTokens() )
3729 {
3730 wxString member = tokenizer.GetNextToken();
3731
3732 // In Eagle, overbar text is automatically stopped at the end of the net name, even when
3733 // that net name is part of a bus definition. In KiCad, we don't (currently) do that, so
3734 // if there is an odd number of overbar markers in this net name, we need to append one
3735 // to close it out before appending the space.
3736
3737 if( member.Freq( '!' ) % 2 > 0 )
3738 member << wxT( "!" );
3739
3740 ret << member << wxS( " " );
3741 }
3742
3743 ret.Trim( true );
3744 ret << wxT( "}" );
3745
3746 return ret;
3747}
3748
3749
3750const ESYMBOL* SCH_IO_EAGLE::getEagleSymbol( const std::unique_ptr<EINSTANCE>& aInstance )
3751{
3752 wxCHECK( m_eagleDoc && m_eagleDoc->drawing && m_eagleDoc->drawing->schematic && aInstance,
3753 nullptr );
3754
3755 std::unique_ptr<EPART>& epart = m_eagleDoc->drawing->schematic->parts[aInstance->part];
3756
3757 if( !epart || epart->deviceset.IsEmpty() )
3758 return nullptr;
3759
3760 std::unique_ptr<ELIBRARY>& elibrary = m_eagleDoc->drawing->schematic->libraries[epart->library];
3761
3762 if( !elibrary )
3763 return nullptr;
3764
3765 std::unique_ptr<EDEVICE_SET>& edeviceset = elibrary->devicesets[epart->deviceset];
3766
3767 if( !edeviceset )
3768 return nullptr;
3769
3770 std::unique_ptr<EGATE>& egate = edeviceset->gates[aInstance->gate];
3771
3772 if( !egate )
3773 return nullptr;
3774
3775 std::unique_ptr<ESYMBOL>& esymbol = elibrary->symbols[egate->symbol];
3776
3777 if( esymbol )
3778 return esymbol.get();
3779
3780 return nullptr;
3781}
3782
3783
3784void SCH_IO_EAGLE::getEagleSymbolFieldAttributes( const std::unique_ptr<EINSTANCE>& aInstance,
3785 const wxString& aEagleFieldName,
3786 SCH_FIELD* aField )
3787{
3788 wxCHECK( aField && !aEagleFieldName.IsEmpty(), /* void */ );
3789
3790 const ESYMBOL* esymbol = getEagleSymbol( aInstance );
3791
3792 if( esymbol )
3793 {
3794 for( const std::unique_ptr<ETEXT>& text : esymbol->texts )
3795 {
3796 if( text->text == aEagleFieldName )
3797 {
3798 aField->SetVisible( true );
3799 VECTOR2I pos( text->x.ToSchUnits() + aInstance->x.ToSchUnits(),
3800 -text->y.ToSchUnits() - aInstance->y.ToSchUnits() );
3801
3802 bool mirror = text->rot ? text->rot->mirror : false;
3803
3804 if( aInstance->rot && aInstance->rot->mirror )
3805 mirror = !mirror;
3806
3807 if( mirror )
3808 pos.y = -aInstance->y.ToSchUnits() + text->y.ToSchUnits();
3809
3810 aField->SetPosition( pos );
3811 }
3812 }
3813 }
3814}
3815
3816
3818{
3819 VECTOR2I retv;
3820
3821 std::vector<SCH_FIELD*> fields;
3822 aPart->GetFields( fields );
3823
3824 if( fields.size() )
3825 {
3826 retv = fields[0]->GetPosition();
3827
3828 for( size_t i = 1; i < fields.size(); i++ )
3829 {
3830 if( fields[i]->GetPosition().x > retv.x )
3831 retv.x = fields[i]->GetPosition().x;
3832
3833 if( fields[i]->GetPosition().y > retv.y )
3834 retv.y = fields[i]->GetPosition().y;
3835 }
3836 }
3837
3838 return retv;
3839}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:127
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr Vec Centre() const
Definition box2.h:97
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr const SizeVec & GetSize() const
Definition box2.h:206
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:156
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
void SetCenter(const VECTOR2I &aCenter)
SHAPE_POLY_SET & GetPolyShape()
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:236
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:198
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:240
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
void SetFillMode(FILL_T aFill)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:93
int GetTextHeight() const
Definition eda_text.h:292
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:172
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:536
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:114
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:580
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:416
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:225
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:385
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:334
bool IsBold() const
Definition eda_text.h:209
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:228
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:269
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:298
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:306
VECTOR2I GetTextSize() const
Definition eda_text.h:286
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:408
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:230
ee_rtree::Iterator begin() const
Return a read/write iterator that points to the first.
Definition sch_rtree.h:265
ee_rtree::Iterator end() const
Return a read/write iterator that points to one past the last element in the EE_RTREE.
Definition sch_rtree.h:273
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:225
static std::shared_ptr< ERC_ITEM > Create(int aErrorCode)
Constructs an ERC_ITEM for the given error code.
Definition erc_item.cpp:319
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:336
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
Definition io_base.h:237
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:240
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Definition io_base.cpp:71
virtual void Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Definition io_base.cpp:124
Definition kiid.h:48
std::optional< LIBRARY_TABLE * > ProjectTable() const
Retrieves the project library table for this adapter type, or nullopt if one doesn't exist.
void SetNickname(const wxString &aNickname)
void SetType(const wxString &aType)
void SetURI(const wxString &aUri)
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:192
Define a library symbol object.
Definition lib_symbol.h:83
std::vector< const SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
void GetFields(std::vector< SCH_FIELD * > &aList, bool aVisibleOnly=false) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
void SetParent(LIB_SYMBOL *aParent=nullptr)
bool IsGlobalPower() const override
int GetUnitCount() const override
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:249
static bool ParseBusVector(const wxString &aBus, wxString *aName, std::vector< wxString > *aMemberList)
Parse a bus vector (e.g.
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
void SetHeightMils(double aHeightInMils)
const VECTOR2D GetSizeIU(double aIUScale) const
Gets the page size in internal units.
Definition page_info.h:177
void SetWidthMils(double aWidthInMils)
static SYMBOL_LIBRARY_ADAPTER * SymbolLibAdapter(PROJECT *aProject)
Accessor for project symbol library manager adapter.
Holds all the data relating to one schematic.
Definition schematic.h:89
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition schematic.h:104
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:173
SCH_SHEET & Root() const
Definition schematic.h:133
Class for a wire to bus entry.
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:132
void ImportValues(const SCH_FIELD &aSource)
Copy parameters from a SCH_FIELD source.
bool IsEmpty()
Return true if both the name and value of the field are empty.
Definition sch_field.h:175
void SetPosition(const VECTOR2I &aPosition) override
void SetName(const wxString &aName)
void SetText(const wxString &aText) override
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
std::unique_ptr< EAGLE_DOC > m_eagleDoc
SCH_ITEM * loadWire(const std::unique_ptr< EWIRE > &aWire, SEG &endpoints)
void loadTextAttributes(EDA_TEXT *aText, const std::unique_ptr< ETEXT > &aAttributes) const
void loadSegments(const std::vector< std::unique_ptr< ESEGMENT > > &aSegments, const wxString &aNetName, const wxString &aNetClass, bool aIsBus=false)
void loadModuleInstance(const std::unique_ptr< EMODULEINST > &aModuleInstance)
void ensureLoadedLibrary(const wxString &aLibraryPath)
void loadSchematic(const ESCHEMATIC &aSchematic)
SCH_TEXT * loadPlainText(const std::unique_ptr< ETEXT > &aSchText)
void loadSheet(const std::unique_ptr< ESHEET > &aSheet)
void loadLayerDefs(const std::vector< std::unique_ptr< ELAYER > > &aLayers)
VECTOR2I getLastSymbolFieldPosition(const LIB_SYMBOL *aPart)
const ESYMBOL * getEagleSymbol(const std::unique_ptr< EINSTANCE > &aInstance)
EAGLE_LIBRARY * loadLibrary(const ELIBRARY *aLibrary, EAGLE_LIBRARY *aEagleLib)
wxXmlDocument loadXmlDocument(const wxString &aFileName)
wxString translateEagleBusName(const wxString &aEagleName) const
Translate an Eagle-style bus name into one that is KiCad-compatible.
void loadFieldAttributes(SCH_FIELD *aField, const SCH_TEXT *aText) const
Move net labels that are detached from any wire to the nearest wire.
std::map< wxString, wxString > m_powerPorts
map from symbol reference to global label equivalent
SCH_SHEET_PATH m_sheetPath
The current sheet path of the schematic being loaded.
wxString m_libName
Library name to save symbols.
SCH_TEXT * loadSymbolText(std::unique_ptr< LIB_SYMBOL > &aSymbol, const std::unique_ptr< ETEXT > &aText, int aGateNumber)
std::pair< VECTOR2I, const SEG * > findNearestLinePoint(const VECTOR2I &aPoint, const std::vector< SEG > &aLines) const
std::map< wxString, long long > m_timestamps
SCH_LABEL_BASE * loadLabel(const std::unique_ptr< ELABEL > &aLabel, const wxString &aNetName, bool aIsBus=false)
void loadInstance(const std::unique_ptr< EINSTANCE > &aInstance, const std::map< wxString, std::unique_ptr< EPART > > &aParts)
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const std::map< std::string, UTF8 > *aProperties) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
std::unordered_map< wxString, bool > m_userValue
deviceset/@uservalue for device.
int GetModifyHash() const override
Return the modification hash from the library cache.
std::map< wxString, int > m_netCounts
wxFileName m_filename
std::map< wxString, EAGLE_LIBRARY > m_eagleLibs
bool loadSymbol(const std::unique_ptr< ESYMBOL > &aEsymbol, std::unique_ptr< LIB_SYMBOL > &aSymbol, const std::unique_ptr< EDEVICE > &aDevice, int aGateNumber, const wxString &aGateName)
SCH_SHEET * getCurrentSheet()
void loadDrawing(const std::unique_ptr< EDRAWING > &aDrawing)
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
SCH_SHAPE * loadSymbolPolyLine(std::unique_ptr< LIB_SYMBOL > &aSymbol, const std::unique_ptr< EPOLYGON > &aPolygon, int aGateNumber)
std::vector< VECTOR2I > m_wireIntersections
Wires and labels of a single connection (segment in Eagle nomenclature)
std::map< VECTOR2I, std::set< const EDA_ITEM * > > m_connPoints
The fully parsed Eagle schematic file.
bool checkConnections(const SCH_SYMBOL *aSymbol, const SCH_PIN *aPin) const
IO_RELEASER< SCH_IO > m_pi
PI to create KiCad symbol library.
SCH_SHAPE * loadRectangle(const std::unique_ptr< ERECT > &aRect)
void addBusEntries()
This function finds best way to place a bus entry symbol for when an Eagle wire segment ends on an Ea...
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_IO can read the specified schematic file.
void addImplicitConnections(SCH_SYMBOL *aSymbol, SCH_SCREEN *aScreen, bool aUpdateSet)
Create net labels to emulate implicit connections in Eagle.
std::map< int, SCH_LAYER_ID > m_layerMap
SCH_LAYER_ID kiCadLayer(int aEagleLayer)
Return the matching layer or return LAYER_NOTES.
wxString getLibName()
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
SCH_PIN * loadPin(std::unique_ptr< LIB_SYMBOL > &aSymbol, const std::unique_ptr< EPIN > &aPin, int aGateNumber)
SCH_SHAPE * loadCircle(const std::unique_ptr< ECIRCLE > &aCircle)
wxFileName getLibFileName()
Checks if there are other wires or pins at the position of the tested pin.
SCH_SHAPE * loadSymbolRectangle(std::unique_ptr< LIB_SYMBOL > &aSymbol, const std::unique_ptr< ERECT > &aRectangle, int aGateNumber)
SCH_JUNCTION * loadJunction(const std::unique_ptr< EJUNCTION > &aJunction)
wxString m_version
Eagle file version.
void getEagleSymbolFieldAttributes(const std::unique_ptr< EINSTANCE > &aInstance, const wxString &aEagleFieldName, SCH_FIELD *aField)
std::map< wxString, const EPART * > m_partlist
void moveLabels(SCH_LINE *aWire, const VECTOR2I &aNewEndPoint)
Move any labels on the wire to the new end point of the wire.
bool checkHeader(const wxString &aFileName) const
SCHEMATIC * m_schematic
Passed to Load(), the schematic object being loaded.
void countNets(const ESCHEMATIC &aSchematic)
SCH_SHEET * m_rootSheet
The root sheet of the schematic being loaded.
SCH_SHAPE * loadPolyLine(const std::unique_ptr< EPOLYGON > &aPolygon)
SCH_SHAPE * loadSymbolCircle(std::unique_ptr< LIB_SYMBOL > &aSymbol, const std::unique_ptr< ECIRCLE > &aCircle, int aGateNumber)
SCH_SCREEN * getCurrentScreen()
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
std::map< wxString, EAGLE_MISSING_CMP > m_missingCmps
void loadFrame(const std::unique_ptr< EFRAME > &aFrame, std::vector< SCH_ITEM * > &aItems)
const int ARC_ACCURACY
std::vector< SEG_DESC > m_segments
Nets as defined in the <nets> sections of an Eagle schematic file.
std::vector< EMODULE * > m_modules
The current module stack being loaded.
std::vector< EMODULEINST * > m_moduleInstances
SCH_ITEM * loadSymbolWire(std::unique_ptr< LIB_SYMBOL > &aSymbol, const std::unique_ptr< EWIRE > &aWire, int aGateNumber)
long long getLibraryTimestamp(const wxString &aLibraryPath) const
static const char * PropBuffering
The property used internally by the plugin to enable cache buffering which prevents the library file ...
virtual bool CanReadSchematicFile(const wxString &aFileName) const
Checks if this SCH_IO can read the specified schematic file.
Definition sch_io.cpp:45
SCH_IO(const wxString &aName)
Definition sch_io.h:375
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:166
SCH_ITEM * Duplicate(bool addToParentGroup, SCH_COMMIT *aCommit=nullptr, bool doClone=false) const
Routine to create a new copy of given item.
Definition sch_item.cpp:164
int GetUnit() const
Definition sch_item.h:237
virtual void SetUnit(int aUnit)
Definition sch_item.h:236
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:183
void SetPosition(const VECTOR2I &aPosition) override
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
bool IsWire() const
Return true if the line is a wire.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition sch_line.cpp:276
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
bool IsBus() const
Return true if the line is a bus.
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:149
void SetNumber(const wxString &aNumber)
Definition sch_pin.cpp:725
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:750
SCH_SCREEN * GetNext()
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
SCH_SCREEN * GetFirst()
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:141
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:142
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:119
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
void Update(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Update aItem's bounding box in the tree.
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:89
void SetStroke(const STROKE_PARAMS &aStroke) override
VECTOR2I GetCenter() const
Definition sch_shape.h:96
void AddPoint(const VECTOR2I &aPosition)
VECTOR2I GetPosition() const override
Definition sch_shape.h:88
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Define a sheet pin (label) used in sheets to create hierarchical schematics.
void SetPosition(const VECTOR2I &aPosition) override
void SetSide(SHEET_SIDE aEdge)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:380
bool LocatePathOfScreen(SCH_SCREEN *aScreen, SCH_SHEET_PATH *aList)
Search the existing hierarchy for an instance of screen loaded from aFileName.
void SetName(const wxString &aName)
Definition sch_sheet.h:141
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:143
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
Schematic symbol object.
Definition sch_symbol.h:73
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:132
void AddHierarchicalReference(const KIID_PATH &aPath, const wxString &aRef, int aUnit)
Add a full hierarchical reference to this symbol.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:181
VECTOR2I GetPinPhysicalPosition(const SCH_PIN *Pin) const
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
VECTOR2I GetPosition() const override
Definition sch_text.h:150
void SetPosition(const VECTOR2I &aPosition) override
Definition sch_text.h:151
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition seg.cpp:446
bool Contains(const SEG &aSeg) const
Definition seg.h:324
SHAPE_ARC & ConstructFromStartEndAngle(const VECTOR2I &aStart, const VECTOR2I &aEnd, const EDA_ANGLE &aAngle, double aWidth=0)
Construct this arc from the given start, end and angle.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
Simple container to manage line stroke parameters.
An interface to the global shared library manager that is schematic-specific and linked to one projec...
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
wxString wx_str() const
Definition utf8.cpp:45
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition vector2d.h:553
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
static REPORTER & GetInstance()
Definition reporter.cpp:222
#define DEFAULT_SCH_ENTRY_SIZE
The default text size in mils. (can be changed in preference menu)
wxString escapeName(const wxString &aNetName)
Translates Eagle special characters to their counterparts in KiCad.
wxString interpretText(const wxString &aText)
Interprets special characters in Eagle text and converts them to KiCAD notation.
size_t GetNodeCount(const wxXmlNode *aNode)
Fetch the number of XML nodes within aNode.
VECTOR2I ConvertArcCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, double aAngle)
Convert an Eagle curve end to a KiCad center for S_ARC.
wxString convertDescription(wxString aDescr)
Converts Eagle's HTML description into KiCad description format.
OPTIONAL_XML_ATTRIBUTE< double > opt_double
#define _(s)
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
#define IGNORE_PARENT_GROUP
Definition eda_item.h:57
#define IS_NEW
New item, just created.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:51
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:65
@ ERCE_BUS_ENTRY_NEEDED
Importer failed to auto-place a bus entry.
static const std::string KiCadSchematicFileExtension
static const std::string KiCadSymbolLibFileExtension
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
KIID niluuid(0)
SCH_LAYER_ID
Eeschema drawing layers.
Definition layer_ids.h:451
@ LAYER_DEVICE
Definition layer_ids.h:468
@ LAYER_WIRE
Definition layer_ids.h:454
@ LAYER_NOTES
Definition layer_ids.h:469
@ LAYER_BUS
Definition layer_ids.h:455
STL namespace.
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:37
@ PT_NC
not connected (must be left open)
Definition pin_type.h:50
@ 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_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_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:43
@ 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
@ RPT_SEVERITY_ERROR
static wxString extractNetName(const wxString &aPinName)
static const std::map< wxString, ELECTRICAL_PINTYPE > pinDirectionsMap
Map of EAGLE pin type values to KiCad pin type values.
static SYMBOL_ORIENTATION_T kiCadComponentRotation(float eagleDegrees)
static void eagleToKicadAlignment(EDA_TEXT *aText, int aEagleAlignment, int aRelDegress, bool aMirror, bool aSpin, int aAbsDegress)
static BOX2I getSheetBbox(SCH_SHEET *aSheet)
Strip the Eagle "@<tag>" linking hint from a pin name (e.g. return 'GND' for 'GND@2')
@ AUTOPLACE_AUTO
Definition sch_item.h:71
LABEL_FLAG_SHAPE
Definition sch_label.h:101
@ L_BIDI
Definition sch_label.h:104
@ L_TRISTATE
Definition sch_label.h:105
@ L_UNSPECIFIED
Definition sch_label.h:106
@ L_OUTPUT
Definition sch_label.h:103
@ L_INPUT
Definition sch_label.h:102
LABEL_SHAPE
Definition sch_label.h:119
@ LABEL_BIDI
Definition sch_label.h:122
@ LABEL_INPUT
Definition sch_label.h:120
@ LABEL_OUTPUT
Definition sch_label.h:121
@ LABEL_PASSIVE
Definition sch_label.h:124
@ LABEL_TRISTATE
Definition sch_label.h:123
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
SHEET_SIDE
Define the edge of the sheet that the sheet pin is positioned.
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
wxString UnescapeString(const wxString &aSource)
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
@ CTX_LIBID
@ CTX_NETNAME
std::map< wxString, std::unique_ptr< LIB_SYMBOL > > KiCadSymbols
std::unordered_map< wxString, wxString > package
wxString name
std::unordered_map< wxString, int > GateToUnitMap
Map Eagle gate unit number (which are strings) to KiCad library symbol unit number.
std::map< wxString, std::unique_ptr< EDEVICE_SET > > devicesets
wxString GetName() const
Fetch the fully unique library name.
std::map< wxString, std::unique_ptr< ESYMBOL > > symbols
opt_wxString direction
std::map< wxString, std::unique_ptr< EMODULE > > modules
std::vector< std::unique_ptr< ESHEET > > sheets
std::map< wxString, std::unique_ptr< EPART > > parts
std::map< wxString, std::unique_ptr< ELIBRARY > > libraries
std::vector< std::unique_ptr< ETEXT > > texts
@ BOTTOM_CENTER
@ BOTTOM_RIGHT
@ CENTER_RIGHT
@ CENTER_LEFT
@ BOTTOM_LEFT
Map references to missing symbol units data.
const SCH_SYMBOL * cmp
Screen where the parent symbol is located.
std::map< int, bool > units
Segments representing wires for intersection checking.
std::vector< SEG > segs
std::vector< SCH_LABEL_BASE * > labels
const SEG * LabelAttached(const SCH_LABEL_BASE *aLabel) const
< Test if a particular label is attached to any of the stored segments
A simple container for schematic symbol instance information.
SYMBOL_ORIENTATION_T
enum used in RotationMiroir()
Definition symbol.h:35
@ SYM_ORIENT_270
Definition symbol.h:42
@ SYM_ORIENT_180
Definition symbol.h:41
@ SYM_ORIENT_90
Definition symbol.h:40
@ SYM_ORIENT_0
Definition symbol.h:39
@ 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
std::vector< std::string > header
std::vector< std::vector< std::string > > table
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition trigo.cpp:175
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
@ SCH_LINE_T
Definition typeinfo.h:164
@ SCH_SYMBOL_T
Definition typeinfo.h:173
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
Definition of file extensions used in Kicad.