KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_altium.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) 2020 Thomas Pointhuber <[email protected]>
5 * Copyright (C) 2021-2024 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <memory>
26
27#include "altium_parser_sch.h"
28#include <io/io_utils.h>
33
34#include <schematic.h>
35#include <project_sch.h>
38
39#include <lib_id.h>
40#include <sch_pin.h>
41#include <sch_bitmap.h>
42#include <sch_bus_entry.h>
43#include <sch_symbol.h>
44#include <sch_junction.h>
45#include <sch_line.h>
46#include <sch_shape.h>
47#include <sch_no_connect.h>
48#include <sch_screen.h>
49#include <sch_label.h>
50#include <sch_sheet.h>
51#include <sch_sheet_pin.h>
52#include <sch_textbox.h>
53#include <symbol_lib_table.h>
54
55#include <bezier_curves.h>
56#include <compoundfilereader.h>
57#include <font/fontconfig.h>
58#include <geometry/ellipse.h>
59#include <string_utils.h>
60#include <sch_edit_frame.h>
62#include <wx/log.h>
63#include <wx/dir.h>
64#include <wx/mstream.h>
65#include <wx/zstream.h>
66#include <wx/wfstream.h>
67#include <magic_enum.hpp>
68#include "sch_io_altium.h"
69
70
76static const wxChar traceAltiumSch[] = wxT( "KICAD_ALTIUM_SCH" );
77
78
79// Harness port object itself does not contain color information about itself
80// It seems altium is drawing harness ports using these colors
81#define HARNESS_PORT_COLOR_DEFAULT_BACKGROUND COLOR4D( 0.92941176470588238, \
82 0.94901960784313721, \
83 0.98431372549019602, 1.0 )
84
85#define HARNESS_PORT_COLOR_DEFAULT_OUTLINE COLOR4D( 0.56078431372549020, \
86 0.61960784313725492, \
87 0.78823529411764703, 1.0 )
88
89
90static const VECTOR2I GetRelativePosition( const VECTOR2I& aPosition, const SCH_SYMBOL* aSymbol )
91{
93 return t.TransformCoordinate( aPosition - aSymbol->GetPosition() );
94}
95
96
98{
99 int red = color & 0x0000FF;
100 int green = ( color & 0x00FF00 ) >> 8;
101 int blue = ( color & 0xFF0000 ) >> 16;
102
103 return COLOR4D().FromCSSRGBA( red, green, blue, 1.0 );
104}
105
106
108{
109 switch( linestyle )
110 {
115 default: return LINE_STYLE::DEFAULT;
116 }
117}
118
119
120static void SetSchShapeLine( const ASCH_BORDER_INTERFACE& elem, SCH_SHAPE* shape )
121{
123 GetColorFromInt( elem.Color ) ) );
124}
125
126static void SetSchShapeFillAndColor( const ASCH_FILL_INTERFACE& elem, SCH_SHAPE* shape )
127{
128
129 if( !elem.IsSolid )
130 {
132 }
133 else
134 {
136 shape->SetFillColor( GetColorFromInt( elem.AreaColor ) );
137 }
138
139 // Fixup small circles that had their widths set to 0
140 if( shape->GetShape() == SHAPE_T::CIRCLE && shape->GetStroke().GetWidth() == 0
141 && shape->GetRadius() <= schIUScale.MilsToIU( 10 ) )
142 {
144 }
145}
146
147
148static void SetLibShapeLine( const ASCH_BORDER_INTERFACE& elem, SCH_SHAPE* shape,
149 ALTIUM_SCH_RECORD aType )
150{
151 COLOR4D default_color;
152 COLOR4D alt_default_color = COLOR4D( PUREBLUE ); // PUREBLUE is used for many objects, so if
153 // it is used, we will assume that it should
154 // blend with the others
155 STROKE_PARAMS stroke;
156 stroke.SetColor( GetColorFromInt( elem.Color ) );
158
159 switch( aType )
160 {
161 case ALTIUM_SCH_RECORD::ARC: default_color = COLOR4D( PUREBLUE ); break;
162 case ALTIUM_SCH_RECORD::BEZIER: default_color = COLOR4D( PURERED ); break;
163 case ALTIUM_SCH_RECORD::ELLIPSE: default_color = COLOR4D( PUREBLUE ); break;
164 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: default_color = COLOR4D( PUREBLUE ); break;
165 case ALTIUM_SCH_RECORD::LINE: default_color = COLOR4D( PUREBLUE ); break;
166 case ALTIUM_SCH_RECORD::POLYGON: default_color = COLOR4D( PUREBLUE ); break;
167 case ALTIUM_SCH_RECORD::POLYLINE: default_color = COLOR4D( BLACK ); break;
168 case ALTIUM_SCH_RECORD::RECTANGLE: default_color = COLOR4D( 0.5, 0, 0, 1.0 ); break;
169 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: default_color = COLOR4D( PUREBLUE ); break;
170 default: default_color = COLOR4D( PUREBLUE ); break;
171 }
172
173 if( stroke.GetColor() == default_color || stroke.GetColor() == alt_default_color )
175
176 // In Altium libraries, you cannot change the width of the pins. So, to match pin width,
177 // if the line width of other elements is the default pin width (10 mil), we set the width
178 // to the KiCad default pin width ( represented by 0 )
179 if( elem.LineWidth == 2540 )
180 stroke.SetWidth( 0 );
181 else
182 stroke.SetWidth( elem.LineWidth );
183
184 shape->SetStroke( stroke );
185}
186
188 ALTIUM_SCH_RECORD aType, int aStrokeColor )
189{
190 COLOR4D bgcolor = GetColorFromInt( elem.AreaColor );
191 COLOR4D default_bgcolor;
192
193 switch (aType)
194 {
196 default_bgcolor = GetColorFromInt( 11599871 ); // Light Yellow
197 break;
198 default:
199 default_bgcolor = GetColorFromInt( 12632256 ); // Grey
200 break;
201 }
202
203 if( elem.IsTransparent )
204 bgcolor = bgcolor.WithAlpha( 0.5 );
205
206 if( !elem.IsSolid )
207 {
209 }
210 else if( elem.AreaColor == aStrokeColor )
211 {
212 bgcolor = shape->GetStroke().GetColor();
213
215 }
216 else if( bgcolor.WithAlpha( 1.0 ) == default_bgcolor )
217 {
219 }
220 else
221 {
223 }
224
225 shape->SetFillColor( bgcolor );
226
227 if( elem.AreaColor == aStrokeColor
228 && shape->GetStroke().GetWidth() == schIUScale.MilsToIU( 1 ) )
229 {
230 STROKE_PARAMS stroke = shape->GetStroke();
231 stroke.SetWidth( -1 );
232 shape->SetStroke( stroke );
233 }
234
235 // Fixup small circles that had their widths set to 0
236 if( shape->GetShape() == SHAPE_T::CIRCLE && shape->GetStroke().GetWidth() == 0
237 && shape->GetRadius() <= schIUScale.MilsToIU( 10 ) )
238 {
240 }
241}
242
243
245 SCH_IO( wxS( "Altium" ) )
246{
247 m_isIntLib = false;
248 m_rootSheet = nullptr;
249 m_schematic = nullptr;
252
254}
255
256
258{
259 for( auto& [libName, lib] : m_libCache )
260 {
261 for( auto& [name, symbol] : lib )
262 delete symbol;
263 }
264}
265
266
268{
269 return 0;
270}
271
272
273bool SCH_IO_ALTIUM::isBinaryFile( const wxString& aFileName )
274{
275 // Compound File Binary Format header
277}
278
279
280bool SCH_IO_ALTIUM::isASCIIFile( const wxString& aFileName )
281{
282 // ASCII file format
283 return IO_UTILS::fileStartsWithPrefix( aFileName, wxS( "|HEADER=" ), false );
284}
285
286
287bool SCH_IO_ALTIUM::checkFileHeader( const wxString& aFileName )
288{
289 return isBinaryFile( aFileName ) || isASCIIFile( aFileName );
290}
291
292
293bool SCH_IO_ALTIUM::CanReadSchematicFile( const wxString& aFileName ) const
294{
295 if( !SCH_IO::CanReadSchematicFile( aFileName ) )
296 return false;
297
298 return checkFileHeader( aFileName );
299}
300
301
302bool SCH_IO_ALTIUM::CanReadLibrary( const wxString& aFileName ) const
303{
304 if( !SCH_IO::CanReadLibrary( aFileName ) )
305 return false;
306
307 return checkFileHeader( aFileName );
308}
309
310
312{
313 std::vector<SCH_PIN*> pins;
314
315 if( LIB_SYMBOL* lib_sym = dyn_cast<LIB_SYMBOL*>( aSymbol ) )
316 pins = lib_sym->GetAllLibPins();
317
318 if( SCH_SYMBOL* sch_sym = dyn_cast<SCH_SYMBOL*>( aSymbol ) )
319 pins = sch_sym->GetPins();
320
321
322 bool names_visible = false;
323 bool numbers_visible = false;
324
325 for( SCH_PIN* pin : pins )
326 {
327 if( pin->GetNameTextSize() > 0 && !pin->GetName().empty() )
328 names_visible = true;
329
330 if( pin->GetNumberTextSize() > 0 && !pin->GetNumber().empty() )
331 numbers_visible = true;
332 }
333
334 if( !names_visible )
335 {
336 for( SCH_PIN* pin : pins )
337 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
338
339 aSymbol->SetShowPinNames( false );
340 }
341
342 if( !numbers_visible )
343 {
344 for( SCH_PIN* pin : pins )
345 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
346
347 aSymbol->SetShowPinNumbers( false );
348 }
349}
350
351
353{
354 if( m_libName.IsEmpty() )
355 {
356 // Try to come up with a meaningful name
358
359 if( m_libName.IsEmpty() )
360 {
361 wxFileName fn( m_rootSheet->GetFileName() );
362 m_libName = fn.GetName();
363 }
364
365 if( m_libName.IsEmpty() )
366 m_libName = "noname";
367
368 m_libName += "-altium-import";
370 }
371
372 return m_libName;
373}
374
375
377{
378 wxFileName fn( m_schematic->Prj().GetProjectPath(), getLibName(),
380
381 return fn;
382}
383
384
385SCH_SHEET* SCH_IO_ALTIUM::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
386 SCH_SHEET* aAppendToMe,
387 const std::map<std::string, UTF8>* aProperties )
388{
389 wxCHECK( !aFileName.IsEmpty() && aSchematic, nullptr );
390
391 wxFileName fileName( aFileName );
392 fileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
393 m_schematic = aSchematic;
394
395 // Show the font substitution warnings
397
398 // Delete on exception, if I own m_rootSheet, according to aAppendToMe
399 std::unique_ptr<SCH_SHEET> deleter( aAppendToMe ? nullptr : m_rootSheet );
400
401 if( aAppendToMe )
402 {
403 wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
404 m_rootSheet = &aSchematic->Root();
405 }
406 else
407 {
408 m_rootSheet = new SCH_SHEET( aSchematic );
409 m_rootSheet->SetFileName( fileName.GetFullPath() );
410
411 aSchematic->SetRoot( m_rootSheet );
412
413 SCH_SHEET_PATH sheetpath;
414 sheetpath.push_back( m_rootSheet );
415
416 // We'll update later if we find a pageNumber record for it.
417 sheetpath.SetPageNumber( "#" );
418 }
419
420 if( !m_rootSheet->GetScreen() )
421 {
422 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
423 screen->SetFileName( aFileName );
424 m_rootSheet->SetScreen( screen );
425 const_cast<KIID&>( m_rootSheet->m_Uuid ) = screen->GetUuid();
426 }
427
429
430 wxCHECK_MSG( libTable, nullptr, "Could not load symbol lib table." );
431
432 m_pi.reset( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
433
436 if( !libTable->HasLibrary( getLibName() ) )
437 {
438 // Create a new empty symbol library.
439 m_pi->CreateLibrary( getLibFileName().GetFullPath() );
440 wxString libTableUri = "${KIPRJMOD}/" + getLibFileName().GetFullName();
441
442 // Add the new library to the project symbol library table.
443 libTable->InsertRow( new SYMBOL_LIB_TABLE_ROW( getLibName(), libTableUri,
444 wxString( "KiCad" ) ) );
445
446 // Save project symbol library table.
447 wxFileName fn( m_schematic->Prj().GetProjectPath(),
449
450 // So output formatter goes out of scope and closes the file before reloading.
451 {
452 FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
453 libTable->Format( &formatter, 0 );
454 }
455
456 // Reload the symbol library table.
459 }
460
462
463 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
464 wxCHECK( rootScreen, nullptr );
465
466 SCH_SHEET_INSTANCE sheetInstance;
467
468 sheetInstance.m_Path = m_sheetPath.Path();
469 sheetInstance.m_PageNumber = wxT( "#" );
470
471 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
472
473 ParseAltiumSch( aFileName );
474
475 m_pi->SaveLibrary( getLibFileName().GetFullPath() );
476
477 SCH_SCREENS allSheets( m_rootSheet );
478 allSheets.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
479 allSheets.ClearEditFlags();
480
481 // Set up the default netclass wire & bus width based on imported wires & buses.
482 //
483
484 int minWireWidth = std::numeric_limits<int>::max();
485 int minBusWidth = std::numeric_limits<int>::max();
486
487 for( SCH_SCREEN* screen = allSheets.GetFirst(); screen != nullptr; screen = allSheets.GetNext() )
488 {
489 std::vector<SCH_MARKER*> markers;
490
491 for( SCH_ITEM* item : screen->Items().OfType( SCH_LINE_T ) )
492 {
493 SCH_LINE* line = static_cast<SCH_LINE*>( item );
494
495 if( line->IsWire() && line->GetLineWidth() > 0 )
496 minWireWidth = std::min( minWireWidth, line->GetLineWidth() );
497
498 if( line->IsBus() && line->GetLineWidth() > 0 )
499 minBusWidth = std::min( minBusWidth, line->GetLineWidth() );
500 }
501 }
502
503 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Prj().GetProjectFile().NetSettings();
504
505 if( minWireWidth < std::numeric_limits<int>::max() )
506 netSettings->GetDefaultNetclass()->SetWireWidth( minWireWidth );
507
508 if( minBusWidth < std::numeric_limits<int>::max() )
509 netSettings->GetDefaultNetclass()->SetBusWidth( minBusWidth );
510
511 return m_rootSheet;
512}
513
514
516{
517 return m_sheetPath.LastScreen();
518}
519
520
522{
523 return m_sheetPath.Last();
524}
525
526
527void SCH_IO_ALTIUM::ParseAltiumSch( const wxString& aFileName )
528{
529 // Load path may be different from the project path.
530 wxFileName parentFileName = aFileName;
531
532 if( isBinaryFile( aFileName ) )
533 {
534 ALTIUM_COMPOUND_FILE altiumSchFile( aFileName );
535
536 try
537 {
538 ParseStorage( altiumSchFile ); // we need this before parsing the FileHeader
539 ParseFileHeader( altiumSchFile );
540
541 // Parse "Additional" because sheet is set up during "FileHeader" parsing.
542 ParseAdditional( altiumSchFile );
543 }
544 catch( const CFB::CFBException& exception )
545 {
546 THROW_IO_ERROR( exception.what() );
547 }
548 catch( const std::exception& exc )
549 {
550 wxLogTrace( traceAltiumSch, wxS( "Unhandled exception in Altium schematic "
551 "parsers: %s." ), exc.what() );
552 throw;
553 }
554 }
555 else // ASCII
556 {
557 ParseASCIISchematic( aFileName );
558 }
559
560 SCH_SCREEN* currentScreen = getCurrentScreen();
561 wxCHECK( currentScreen, /* void */ );
562
563 // Descend the sheet hierarchy.
564 for( SCH_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
565 {
566 SCH_SCREEN* loadedScreen = nullptr;
567 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
568
569 wxCHECK2( sheet, continue );
570
571 // The assumption is that all of the Altium schematic files will be in the same
572 // path as the parent sheet path.
573 wxFileName loadAltiumFileName( parentFileName.GetPath(), sheet->GetFileName() );
574
575 if( !loadAltiumFileName.IsFileReadable() )
576 {
577 // Try case-insensitive search
578 wxArrayString files;
579 wxDir::GetAllFiles( parentFileName.GetPath(), &files, wxEmptyString,
580 wxDIR_FILES | wxDIR_HIDDEN );
581
582 for( const wxString& candidate : files )
583 {
584 wxFileName candidateFname( candidate );
585
586 if( candidateFname.GetFullName().IsSameAs( sheet->GetFileName(), false ) )
587 {
588 loadAltiumFileName = candidateFname;
589 break;
590 }
591 }
592 }
593
594 if( loadAltiumFileName.GetFullName().IsEmpty() || !loadAltiumFileName.IsFileReadable() )
595 {
596 wxString msg;
597
598 msg.Printf( _( "The file name for sheet %s is undefined, this is probably an"
599 " Altium signal harness that got converted to a sheet." ),
600 sheet->GetName() );
601 m_reporter->Report( msg );
602 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
603 continue;
604 }
605
606 m_rootSheet->SearchHierarchy( loadAltiumFileName.GetFullPath(), &loadedScreen );
607
608 if( loadedScreen )
609 {
610 sheet->SetScreen( loadedScreen );
611 // Do not need to load the sub-sheets - this has already been done.
612 }
613 else
614 {
615 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
616 SCH_SCREEN* screen = sheet->GetScreen();
617
618 if( sheet->GetName().Trim().empty() )
619 sheet->SetName( loadAltiumFileName.GetName() );
620
621 wxCHECK2( screen, continue );
622
623 m_sheetPath.push_back( sheet );
624 ParseAltiumSch( loadAltiumFileName.GetFullPath() );
625
626 // Map the loaded Altium file to the project file.
627 wxFileName projectFileName = loadAltiumFileName;
628 projectFileName.SetPath( m_schematic->Prj().GetProjectPath() );
629 projectFileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
630 sheet->SetFileName( projectFileName.GetFullName() );
631 screen->SetFileName( projectFileName.GetFullPath() );
632
634 }
635 }
636}
637
638
640{
641 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "Storage" } );
642
643 if( file == nullptr )
644 return;
645
646 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
647
648 std::map<wxString, wxString> properties = reader.ReadProperties();
649 wxString header = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
650 int weight = ALTIUM_PROPS_UTILS::ReadInt( properties, "WEIGHT", 0 );
651
652 if( weight < 0 )
653 THROW_IO_ERROR( "Storage weight is negative!" );
654
655 for( int i = 0; i < weight; i++ )
656 m_altiumStorage.emplace_back( reader );
657
658 if( reader.HasParsingError() )
659 THROW_IO_ERROR( "stream was not parsed correctly!" );
660
661 // TODO pointhi: is it possible to have multiple headers in one Storage file? Otherwise
662 // throw IO Error.
663 if( reader.GetRemainingBytes() != 0 )
664 {
665 m_reporter->Report( wxString::Format( _( "Storage file not fully parsed "
666 "(%d bytes remaining)." ),
667 reader.GetRemainingBytes() ),
669 }
670}
671
672
674{
675 wxString streamName = wxS( "Additional" );
676
677 const CFB::COMPOUND_FILE_ENTRY* file =
678 aAltiumSchFile.FindStream( { streamName.ToStdString() } );
679
680 if( file == nullptr )
681 return;
682
683 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
684
685 if( reader.GetRemainingBytes() <= 0 )
686 {
687 THROW_IO_ERROR( "Additional section does not contain any data" );
688 }
689 else
690 {
691 std::map<wxString, wxString> properties = reader.ReadProperties();
692
693 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
694 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
695
696 if( record != ALTIUM_SCH_RECORD::HEADER )
697 THROW_IO_ERROR( "Header expected" );
698 }
699
700 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
701 {
702 std::map<wxString, wxString> properties = reader.ReadProperties();
703
704 ParseRecord( index, properties, streamName );
705 }
706
707 // Handle harness Ports
709 ParseHarnessPort( port );
710
711 if( reader.HasParsingError() )
712 THROW_IO_ERROR( "stream was not parsed correctly!" );
713
714 if( reader.GetRemainingBytes() != 0 )
715 THROW_IO_ERROR( "stream is not fully parsed" );
716
718}
719
720
722{
723 wxString streamName = wxS( "FileHeader" );
724
725 const CFB::COMPOUND_FILE_ENTRY* file =
726 aAltiumSchFile.FindStream( { streamName.ToStdString() } );
727
728 if( file == nullptr )
729 THROW_IO_ERROR( "FileHeader not found" );
730
731 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
732
733 if( reader.GetRemainingBytes() <= 0 )
734 {
735 THROW_IO_ERROR( "FileHeader does not contain any data" );
736 }
737 else
738 {
739 std::map<wxString, wxString> properties = reader.ReadProperties();
740
741 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
742
743 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Binary File Version 5.0" ) )
744 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
745 }
746
747 // Prepare some local variables
748 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
749 wxCHECK( !m_currentTitleBlock, /* void */ );
750
751 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
752
753 // index is required to resolve OWNERINDEX
754 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
755 {
756 std::map<wxString, wxString> properties = reader.ReadProperties();
757
758 ParseRecord( index, properties, streamName );
759 }
760
761 if( reader.HasParsingError() )
762 THROW_IO_ERROR( "stream was not parsed correctly!" );
763
764 if( reader.GetRemainingBytes() != 0 )
765 THROW_IO_ERROR( "stream is not fully parsed" );
766
767 // assign LIB_SYMBOL -> COMPONENT
768 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
769 {
770 auto libSymbolIt = m_libSymbols.find( symbol.first );
771
772 if( libSymbolIt == m_libSymbols.end() )
773 THROW_IO_ERROR( "every symbol should have a symbol attached" );
774
775 fixupSymbolPinNameNumbers( symbol.second );
776 fixupSymbolPinNameNumbers( libSymbolIt->second );
777
778 m_pi->SaveSymbol( getLibFileName().GetFullPath(),
779 new LIB_SYMBOL( *( libSymbolIt->second ) ), m_properties.get() );
780
781 symbol.second->SetLibSymbol( libSymbolIt->second );
782 }
783
784 SCH_SCREEN* screen = getCurrentScreen();
785 wxCHECK( screen, /* void */ );
786
787 // Handle title blocks
789 m_currentTitleBlock.reset();
790
791 // Handle Ports
792 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
793 ParsePort( port );
794
796 m_altiumComponents.clear();
797 m_altiumTemplates.clear();
799
800 m_symbols.clear();
801 m_libSymbols.clear();
802
803 // Otherwise we cannot save the imported sheet?
804 SCH_SHEET* sheet = getCurrentSheet();
805
806 wxCHECK( sheet, /* void */ );
807
808 sheet->SetModified();
809}
810
811
812void SCH_IO_ALTIUM::ParseASCIISchematic( const wxString& aFileName )
813{
814 // Read storage content first
815 {
816 ALTIUM_ASCII_PARSER storageReader( aFileName );
817
818 while( storageReader.CanRead() )
819 {
820 std::map<wxString, wxString> properties = storageReader.ReadProperties();
821
822 // Binary data
823 if( properties.find( wxS( "BINARY" ) ) != properties.end() )
824 m_altiumStorage.emplace_back( properties );
825 }
826 }
827
828 // Read other data
829 ALTIUM_ASCII_PARSER reader( aFileName );
830
831 if( !reader.CanRead() )
832 {
833 THROW_IO_ERROR( "FileHeader does not contain any data" );
834 }
835 else
836 {
837 std::map<wxString, wxString> properties = reader.ReadProperties();
838
839 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
840
841 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Ascii File Version 5.0" ) )
842 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
843 }
844
845 // Prepare some local variables
846 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
847 wxCHECK( !m_currentTitleBlock, /* void */ );
848
849 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
850
851 // index is required to resolve OWNERINDEX
852 int index = 0;
853
854 while( reader.CanRead() )
855 {
856 std::map<wxString, wxString> properties = reader.ReadProperties();
857
858 // Reset index at headers
859 if( properties.find( wxS( "HEADER" ) ) != properties.end() )
860 {
861 index = 0;
862 continue;
863 }
864
865 if( properties.find( wxS( "RECORD" ) ) != properties.end() )
866 ParseRecord( index, properties, aFileName );
867
868 index++;
869 }
870
871 if( reader.HasParsingError() )
872 THROW_IO_ERROR( "stream was not parsed correctly!" );
873
874 if( reader.CanRead() )
875 THROW_IO_ERROR( "stream is not fully parsed" );
876
877 // assign LIB_SYMBOL -> COMPONENT
878 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
879 {
880 auto libSymbolIt = m_libSymbols.find( symbol.first );
881
882 if( libSymbolIt == m_libSymbols.end() )
883 THROW_IO_ERROR( "every symbol should have a symbol attached" );
884
885 fixupSymbolPinNameNumbers( symbol.second );
886 fixupSymbolPinNameNumbers( libSymbolIt->second );
887
888 m_pi->SaveSymbol( getLibFileName().GetFullPath(),
889 new LIB_SYMBOL( *( libSymbolIt->second ) ), m_properties.get() );
890
891 symbol.second->SetLibSymbol( libSymbolIt->second );
892 }
893
894 SCH_SCREEN* screen = getCurrentScreen();
895 wxCHECK( screen, /* void */ );
896
897 // Handle title blocks
899 m_currentTitleBlock.reset();
900
901 // Handle harness Ports
903 ParseHarnessPort( port );
904
905 // Handle Ports
906 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
907 ParsePort( port );
908
910 m_altiumComponents.clear();
911 m_altiumTemplates.clear();
913
914 m_symbols.clear();
915 m_libSymbols.clear();
916
917 // Otherwise we cannot save the imported sheet?
918 SCH_SHEET* sheet = getCurrentSheet();
919
920 wxCHECK( sheet, /* void */ );
921
922 sheet->SetModified();
923}
924
925
926void SCH_IO_ALTIUM::ParseRecord( int index, std::map<wxString, wxString>& properties,
927 const wxString& aSectionName )
928{
929 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", -1 );
930 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
931
932 // see: https://github.com/vadmium/python-altium/blob/master/format.md
933 switch( record )
934 {
935 // FileHeader section
936
937 case ALTIUM_SCH_RECORD::HEADER:
938 THROW_IO_ERROR( "Header already parsed" );
939
940 case ALTIUM_SCH_RECORD::COMPONENT:
941 ParseComponent( index, properties );
942 break;
943
944 case ALTIUM_SCH_RECORD::PIN:
945 ParsePin( properties );
946 break;
947
948 case ALTIUM_SCH_RECORD::IEEE_SYMBOL:
949 m_reporter->Report( _( "Record 'IEEE_SYMBOL' not handled." ), RPT_SEVERITY_INFO );
950 break;
951
952 case ALTIUM_SCH_RECORD::LABEL:
953 ParseLabel( properties );
954 break;
955
956 case ALTIUM_SCH_RECORD::BEZIER:
957 ParseBezier( properties );
958 break;
959
960 case ALTIUM_SCH_RECORD::POLYLINE:
961 ParsePolyline( properties );
962 break;
963
964 case ALTIUM_SCH_RECORD::POLYGON:
965 ParsePolygon( properties );
966 break;
967
968 case ALTIUM_SCH_RECORD::ELLIPSE:
969 ParseEllipse( properties );
970 break;
971
972 case ALTIUM_SCH_RECORD::PIECHART:
973 ParsePieChart( properties );
974 break;
975
976 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE:
977 ParseRoundRectangle( properties );
978 break;
979
980 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC:
981 case ALTIUM_SCH_RECORD::ARC:
982 ParseArc( properties );
983 break;
984
985 case ALTIUM_SCH_RECORD::LINE:
986 ParseLine( properties );
987 break;
988
989 case ALTIUM_SCH_RECORD::RECTANGLE:
990 ParseRectangle( properties );
991 break;
992
993 case ALTIUM_SCH_RECORD::SHEET_SYMBOL:
994 ParseSheetSymbol( index, properties );
995 break;
996
997 case ALTIUM_SCH_RECORD::SHEET_ENTRY:
998 ParseSheetEntry( properties );
999 break;
1000
1001 case ALTIUM_SCH_RECORD::POWER_PORT:
1002 ParsePowerPort( properties );
1003 break;
1004
1005 case ALTIUM_SCH_RECORD::PORT:
1006 // Ports are parsed after the sheet was parsed
1007 // This is required because we need all electrical connection points before placing.
1008 m_altiumPortsCurrentSheet.emplace_back( properties );
1009 break;
1010
1011 case ALTIUM_SCH_RECORD::NO_ERC:
1012 ParseNoERC( properties );
1013 break;
1014
1015 case ALTIUM_SCH_RECORD::NET_LABEL:
1016 ParseNetLabel( properties );
1017 break;
1018
1019 case ALTIUM_SCH_RECORD::BUS:
1020 ParseBus( properties );
1021 break;
1022
1023 case ALTIUM_SCH_RECORD::WIRE:
1024 ParseWire( properties );
1025 break;
1026
1027 case ALTIUM_SCH_RECORD::TEXT_FRAME:
1028 ParseTextFrame( properties );
1029 break;
1030
1031 case ALTIUM_SCH_RECORD::JUNCTION:
1032 ParseJunction( properties );
1033 break;
1034
1035 case ALTIUM_SCH_RECORD::IMAGE:
1036 ParseImage( properties );
1037 break;
1038
1039 case ALTIUM_SCH_RECORD::SHEET:
1040 ParseSheet( properties );
1041 break;
1042
1043 case ALTIUM_SCH_RECORD::SHEET_NAME:
1044 ParseSheetName( properties );
1045 break;
1046
1047 case ALTIUM_SCH_RECORD::FILE_NAME:
1048 ParseFileName( properties );
1049 break;
1050
1051 case ALTIUM_SCH_RECORD::DESIGNATOR:
1052 ParseDesignator( properties );
1053 break;
1054
1055 case ALTIUM_SCH_RECORD::BUS_ENTRY:
1056 ParseBusEntry( properties );
1057 break;
1058
1059 case ALTIUM_SCH_RECORD::TEMPLATE:
1060 ParseTemplate( index, properties );
1061 break;
1062
1063 case ALTIUM_SCH_RECORD::PARAMETER:
1064 ParseParameter( properties );
1065 break;
1066
1067 case ALTIUM_SCH_RECORD::PARAMETER_SET:
1068 m_reporter->Report( _( "Parameter Set not currently supported." ), RPT_SEVERITY_ERROR );
1069 break;
1070
1071 case ALTIUM_SCH_RECORD::IMPLEMENTATION_LIST:
1072 ParseImplementationList( index, properties );
1073 break;
1074
1075 case ALTIUM_SCH_RECORD::IMPLEMENTATION:
1076 ParseImplementation( properties );
1077 break;
1078
1079 case ALTIUM_SCH_RECORD::MAP_DEFINER_LIST:
1080 break;
1081
1082 case ALTIUM_SCH_RECORD::MAP_DEFINER:
1083 break;
1084
1085 case ALTIUM_SCH_RECORD::IMPL_PARAMS:
1086 break;
1087
1088 case ALTIUM_SCH_RECORD::NOTE:
1089 ParseNote( properties );
1090 break;
1091
1092 case ALTIUM_SCH_RECORD::COMPILE_MASK:
1093 m_reporter->Report( _( "Compile mask not currently supported." ), RPT_SEVERITY_ERROR );
1094 break;
1095
1096 case ALTIUM_SCH_RECORD::HYPERLINK:
1097 break;
1098
1099 // Additional section
1100
1101 case ALTIUM_SCH_RECORD::HARNESS_CONNECTOR:
1102 ParseHarnessConnector( index, properties );
1103 break;
1104
1105 case ALTIUM_SCH_RECORD::HARNESS_ENTRY:
1106 ParseHarnessEntry( properties );
1107 break;
1108
1109 case ALTIUM_SCH_RECORD::HARNESS_TYPE:
1110 ParseHarnessType( properties );
1111 break;
1112
1113 case ALTIUM_SCH_RECORD::SIGNAL_HARNESS:
1114 ParseSignalHarness( properties );
1115 break;
1116
1117 case ALTIUM_SCH_RECORD::BLANKET:
1118 m_reporter->Report( _( "Blanket not currently supported." ), RPT_SEVERITY_ERROR );
1119 break;
1120
1121 default:
1123 wxString::Format( _( "Unknown or unexpected record id %d found in %s." ), recordId,
1124 aSectionName ),
1126 break;
1127 }
1128
1130}
1131
1132
1134{
1135 const auto& component = m_altiumComponents.find( aElem.ownerindex );
1136 const auto& templ = m_altiumTemplates.find( aElem.ownerindex );
1137
1138 if( component != m_altiumComponents.end() )
1139 return component->second.displaymode == aElem.ownerpartdisplaymode;
1140
1141 if( templ != m_altiumTemplates.end() )
1142 return true;
1143
1144 return false;
1145}
1146
1147
1148const ASCH_STORAGE_FILE* SCH_IO_ALTIUM::GetFileFromStorage( const wxString& aFilename ) const
1149{
1150 const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
1151
1152 for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
1153 {
1154 if( file.filename.IsSameAs( aFilename ) )
1155 return &file;
1156
1157 if( file.filename.EndsWith( aFilename ) )
1158 nonExactMatch = &file;
1159 }
1160
1161 return nonExactMatch;
1162}
1163
1164
1165void SCH_IO_ALTIUM::ParseComponent( int aIndex, const std::map<wxString, wxString>& aProperties )
1166{
1167 SCH_SHEET* currentSheet = m_sheetPath.Last();
1168 wxCHECK( currentSheet, /* void */ );
1169
1170 wxString sheetName = currentSheet->GetName();
1171
1172 if( sheetName.IsEmpty() )
1173 sheetName = wxT( "root" );
1174
1175 ASCH_SYMBOL altiumSymbol( aProperties );
1176
1177 if( m_altiumComponents.count( aIndex ) )
1178 {
1179 const ASCH_SYMBOL& currentSymbol = m_altiumComponents.at( aIndex );
1180
1181 m_reporter->Report( wxString::Format( _( "Symbol \"%s\" in sheet \"%s\" at index %d "
1182 "replaced with symbol \"%s\"." ),
1183 currentSymbol.libreference,
1184 sheetName,
1185 aIndex,
1186 altiumSymbol.libreference ),
1188 }
1189
1190 auto pair = m_altiumComponents.insert( { aIndex, altiumSymbol } );
1191 const ASCH_SYMBOL& elem = pair.first->second;
1192
1193 // TODO: this is a hack until we correctly apply all transformations to every element
1194 wxString name = wxString::Format( "%s_%d%s_%s",
1195 sheetName,
1196 elem.orientation,
1197 elem.isMirrored ? "_mirrored" : "",
1198 elem.libreference );
1199
1200 if( elem.displaymodecount > 1 )
1201 name << '_' << elem.displaymode;
1202
1204
1205 LIB_SYMBOL* ksymbol = new LIB_SYMBOL( wxEmptyString );
1206 ksymbol->SetName( name );
1207 ksymbol->SetDescription( elem.componentdescription );
1208 ksymbol->SetLibId( libId );
1209 m_libSymbols.insert( { aIndex, ksymbol } );
1210
1211 // each component has its own symbol for now
1212 SCH_SYMBOL* symbol = new SCH_SYMBOL();
1213
1214 symbol->SetPosition( elem.location + m_sheetOffset );
1215
1216 for( SCH_FIELD& field : symbol->GetFields() )
1217 field.SetVisible( false );
1218
1219 // TODO: keep it simple for now, and only set position.
1220 // component->SetOrientation( elem.orientation );
1221 symbol->SetLibId( libId );
1222 symbol->SetUnit( std::max( 0, elem.currentpartid ) );
1224
1225 SCH_SCREEN* screen = getCurrentScreen();
1226 wxCHECK( screen, /* void */ );
1227
1228 screen->Append( symbol );
1229
1230 m_symbols.insert( { aIndex, symbol } );
1231}
1232
1233
1234void SCH_IO_ALTIUM::ParseTemplate( int aIndex, const std::map<wxString, wxString>& aProperties )
1235{
1236 SCH_SHEET* currentSheet = m_sheetPath.Last();
1237 wxCHECK( currentSheet, /* void */ );
1238
1239 wxString sheetName = currentSheet->GetName();
1240
1241 if( sheetName.IsEmpty() )
1242 sheetName = wxT( "root" );
1243
1244 ASCH_TEMPLATE altiumTemplate( aProperties );
1245
1246 // Extract base name from path
1247 wxString baseName = altiumTemplate.filename.AfterLast( '\\' ).BeforeLast( '.' );
1248
1249 if( baseName.IsEmpty() )
1250 baseName = wxS( "Template" );
1251
1252 m_altiumTemplates.insert( { aIndex, altiumTemplate } );
1253 // No need to create a symbol - graphics is put on the sheet
1254}
1255
1256
1257void SCH_IO_ALTIUM::ParsePin( const std::map<wxString, wxString>& aProperties,
1258 std::vector<LIB_SYMBOL*>& aSymbol )
1259{
1260 ASCH_PIN elem( aProperties );
1261
1262 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
1263 ? nullptr
1264 : aSymbol[elem.ownerpartdisplaymode];
1265 SCH_SYMBOL* schSymbol = nullptr;
1266
1267 if( !symbol )
1268 {
1269 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1270
1271 if( libSymbolIt == m_libSymbols.end() )
1272 {
1273 // TODO: e.g. can depend on Template (RECORD=39
1274 m_reporter->Report( wxString::Format( wxT( "Pin's owner (%d) not found." ),
1275 elem.ownerindex ),
1277 return;
1278 }
1279
1280 if( !IsComponentPartVisible( elem ) )
1281 return;
1282
1283 schSymbol = m_symbols.at( libSymbolIt->first );
1284 symbol = libSymbolIt->second;
1285 }
1286
1287 SCH_PIN* pin = new SCH_PIN( symbol );
1288
1289 // Make sure that these are visible when initializing the symbol
1290 // This may be overriden by the file data but not by the pin defaults
1291 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
1292 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
1293
1294 symbol->AddDrawItem( pin, false );
1295
1296 pin->SetUnit( std::max( 0, elem.ownerpartid ) );
1297
1298 pin->SetName( AltiumPinNamesToKiCad( elem.name ) );
1299 pin->SetNumber( elem.designator );
1300 pin->SetLength( elem.pinlength );
1301
1302 if( elem.hidden )
1303 pin->SetVisible( false );
1304
1305 if( !elem.showDesignator )
1306 pin->SetNumberTextSize( 0 );
1307
1308 if( !elem.showPinName )
1309 pin->SetNameTextSize( 0 );
1310
1311 VECTOR2I pinLocation = elem.location; // the location given is not the connection point!
1312
1313 switch( elem.orientation )
1314 {
1315 case ASCH_RECORD_ORIENTATION::RIGHTWARDS:
1316 pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT );
1317 pinLocation.x += elem.pinlength;
1318 break;
1319
1320 case ASCH_RECORD_ORIENTATION::UPWARDS:
1321 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
1322 pinLocation.y -= elem.pinlength;
1323 break;
1324
1325 case ASCH_RECORD_ORIENTATION::LEFTWARDS:
1326 pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT );
1327 pinLocation.x -= elem.pinlength;
1328 break;
1329
1330 case ASCH_RECORD_ORIENTATION::DOWNWARDS:
1331 pin->SetOrientation( PIN_ORIENTATION::PIN_UP );
1332 pinLocation.y += elem.pinlength;
1333 break;
1334
1335 default:
1336 m_reporter->Report( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
1337 break;
1338 }
1339
1340 // TODO: position can be sometimes off a little bit!
1341
1342 if( schSymbol )
1343 pinLocation = GetRelativePosition( pinLocation + m_sheetOffset, schSymbol );
1344
1345 pin->SetPosition( pinLocation );
1346
1347 switch( elem.electrical )
1348 {
1349 case ASCH_PIN_ELECTRICAL::INPUT:
1350 pin->SetType( ELECTRICAL_PINTYPE::PT_INPUT );
1351 break;
1352
1353 case ASCH_PIN_ELECTRICAL::BIDI:
1354 pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
1355 break;
1356
1357 case ASCH_PIN_ELECTRICAL::OUTPUT:
1358 pin->SetType( ELECTRICAL_PINTYPE::PT_OUTPUT );
1359 break;
1360
1361 case ASCH_PIN_ELECTRICAL::OPEN_COLLECTOR:
1362 pin->SetType( ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR );
1363 break;
1364
1365 case ASCH_PIN_ELECTRICAL::PASSIVE:
1366 pin->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE );
1367 break;
1368
1369 case ASCH_PIN_ELECTRICAL::TRISTATE:
1370 pin->SetType( ELECTRICAL_PINTYPE::PT_TRISTATE );
1371 break;
1372
1373 case ASCH_PIN_ELECTRICAL::OPEN_EMITTER:
1374 pin->SetType( ELECTRICAL_PINTYPE::PT_OPENEMITTER );
1375 break;
1376
1377 case ASCH_PIN_ELECTRICAL::POWER:
1378 pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
1379 break;
1380
1381 case ASCH_PIN_ELECTRICAL::UNKNOWN:
1382 default:
1383 pin->SetType( ELECTRICAL_PINTYPE::PT_UNSPECIFIED );
1384 m_reporter->Report( _( "Pin has unexpected electrical type." ), RPT_SEVERITY_WARNING );
1385 break;
1386 }
1387
1389 m_reporter->Report( _( "Pin has unexpected outer edge type." ), RPT_SEVERITY_WARNING );
1390
1392 m_reporter->Report( _( "Pin has unexpected inner edge type." ), RPT_SEVERITY_WARNING );
1393
1395 {
1396 switch( elem.symbolInnerEdge )
1397 {
1399 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED_CLOCK );
1400 break;
1401
1402 default:
1403 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
1404 break;
1405 }
1406 }
1408 {
1409 switch( elem.symbolInnerEdge )
1410 {
1412 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
1413 break;
1414
1415 default:
1416 pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
1417 break;
1418 }
1419 }
1421 {
1422 pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
1423 }
1424 else
1425 {
1426 switch( elem.symbolInnerEdge )
1427 {
1429 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
1430 break;
1431
1432 default:
1433 pin->SetShape( GRAPHIC_PINSHAPE::LINE ); // nothing to do
1434 break;
1435 }
1436 }
1437}
1438
1439
1441 ASCH_RECORD_ORIENTATION orientation )
1442{
1443 int vjustify, hjustify;
1445
1446 switch( justification )
1447 {
1448 default:
1453 vjustify = GR_TEXT_V_ALIGN_BOTTOM;
1454 break;
1455
1459 vjustify = GR_TEXT_V_ALIGN_CENTER;
1460 break;
1461
1465 vjustify = GR_TEXT_V_ALIGN_TOP;
1466 break;
1467 }
1468
1469 switch( justification )
1470 {
1471 default:
1476 hjustify = GR_TEXT_H_ALIGN_LEFT;
1477 break;
1478
1482 hjustify = GR_TEXT_H_ALIGN_CENTER;
1483 break;
1484
1488 hjustify = GR_TEXT_H_ALIGN_RIGHT;
1489 break;
1490 }
1491
1492 switch( orientation )
1493 {
1495 angle = ANGLE_HORIZONTAL;
1496 break;
1497
1499 hjustify *= -1;
1500 angle = ANGLE_HORIZONTAL;
1501 break;
1502
1504 angle = ANGLE_VERTICAL;
1505 break;
1506
1508 hjustify *= -1;
1509 angle = ANGLE_VERTICAL;
1510 break;
1511 }
1512
1513 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( vjustify ) );
1514 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( hjustify ) );
1515 text->SetTextAngle( angle );
1516}
1517
1518
1520{
1521 // No component assigned -> Put on sheet
1522 if( aOwnerindex == ALTIUM_COMPONENT_NONE )
1523 return true;
1524
1525 // For a template -> Put on sheet so we can resolve variables
1526 if( m_altiumTemplates.find( aOwnerindex ) != m_altiumTemplates.end() )
1527 return true;
1528
1529 return false;
1530}
1531
1532
1533void SCH_IO_ALTIUM::ParseLabel( const std::map<wxString, wxString>& aProperties,
1534 std::vector<LIB_SYMBOL*>& aSymbol, std::vector<int>& aFontSizes )
1535{
1536 ASCH_LABEL elem( aProperties );
1537
1538 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
1539 {
1540 static const std::map<wxString, wxString> variableMap = {
1541 { "APPLICATION_BUILDNUMBER", "KICAD_VERSION" },
1542 { "SHEETNUMBER", "#" },
1543 { "SHEETTOTAL", "##" },
1544 { "TITLE", "TITLE" }, // including 1:1 maps makes it easier
1545 { "REVISION", "REVISION" }, // to see that the list is complete
1546 { "DATE", "ISSUE_DATE" },
1547 { "CURRENTDATE", "CURRENT_DATE" },
1548 { "COMPANYNAME", "COMPANY" },
1549 { "DOCUMENTNAME", "FILENAME" },
1550 { "DOCUMENTFULLPATHANDNAME", "FILEPATH" },
1551 { "PROJECTNAME", "PROJECTNAME" },
1552 };
1553
1554 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
1555 SCH_TEXT* textItem = new SCH_TEXT( elem.location + m_sheetOffset, kicadText );
1556
1557 SetTextPositioning( textItem, elem.justification, elem.orientation );
1558
1559 size_t fontId = static_cast<int>( elem.fontId );
1560
1561 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
1562 {
1563 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
1564 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
1565
1566 // Must come after SetTextSize()
1567 textItem->SetBold( font.Bold );
1568 textItem->SetItalic( font.Italic );
1569 }
1570
1571 textItem->SetFlags(IS_NEW );
1572
1573 SCH_SCREEN* screen = getCurrentScreen();
1574 wxCHECK( screen, /* void */ );
1575
1576 screen->Append( textItem );
1577 }
1578 else
1579 {
1580 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
1581 ? nullptr
1582 : aSymbol[elem.ownerpartdisplaymode];
1583 SCH_SYMBOL* schsym = nullptr;
1584
1585 if( !symbol )
1586 {
1587 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1588
1589 if( libSymbolIt == m_libSymbols.end() )
1590 {
1591 // TODO: e.g. can depend on Template (RECORD=39
1592 m_reporter->Report( wxString::Format( wxT( "Label's owner (%d) not found." ),
1593 elem.ownerindex ),
1595 return;
1596 }
1597
1598 symbol = libSymbolIt->second;
1599 schsym = m_symbols.at( libSymbolIt->first );
1600 }
1601
1602 VECTOR2I pos = elem.location;
1603 SCH_TEXT* textItem = new SCH_TEXT( { 0, 0 }, elem.text, LAYER_DEVICE );
1604 symbol->AddDrawItem( textItem, false );
1605
1607 if( schsym )
1608 pos = GetRelativePosition( elem.location + m_sheetOffset, schsym );
1609
1610 textItem->SetPosition( pos );
1611 textItem->SetUnit( std::max( 0, elem.ownerpartid ) );
1612 SetTextPositioning( textItem, elem.justification, elem.orientation );
1613
1614 size_t fontId = elem.fontId;
1615
1616 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
1617 {
1618 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
1619 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
1620
1621 // Must come after SetTextSize()
1622 textItem->SetBold( font.Bold );
1623 textItem->SetItalic( font.Italic );
1624 }
1625 else if( fontId > 0 && fontId <= aFontSizes.size() )
1626 {
1627 int size = aFontSizes[fontId - 1];
1628 textItem->SetTextSize( { size, size } );
1629 }
1630 }
1631}
1632
1633
1634void SCH_IO_ALTIUM::ParseTextFrame( const std::map<wxString, wxString>& aProperties,
1635 std::vector<LIB_SYMBOL*>& aSymbol,
1636 std::vector<int>& aFontSizes )
1637{
1638 ASCH_TEXT_FRAME elem( aProperties );
1639
1640 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
1641 AddTextBox( &elem );
1642 else
1643 AddLibTextBox( &elem, aSymbol, aFontSizes );
1644}
1645
1646
1647void SCH_IO_ALTIUM::ParseNote( const std::map<wxString, wxString>& aProperties )
1648{
1649 ASCH_NOTE elem( aProperties );
1650 AddTextBox( static_cast<ASCH_TEXT_FRAME*>( &elem ) );
1651
1652 // TODO: need some sort of property system for storing author....
1653}
1654
1655
1657{
1658 SCH_TEXTBOX* textBox = new SCH_TEXTBOX();
1659
1660 VECTOR2I sheetTopRight = aElem->TopRight + m_sheetOffset;
1661 VECTOR2I sheetBottomLeft = aElem->BottomLeft +m_sheetOffset;
1662
1663 textBox->SetStart( sheetTopRight );
1664 textBox->SetEnd( sheetBottomLeft );
1665
1666 textBox->SetText( aElem->Text );
1667
1668 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
1669
1670 if( aElem->isSolid)
1671 textBox->SetFillMode( FILL_T::FILLED_WITH_COLOR );
1672 else
1673 textBox->SetFilled( false );
1674
1675 if( aElem->ShowBorder )
1676 textBox->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::DEFAULT,
1677 GetColorFromInt( aElem->BorderColor ) ) );
1678 else
1679 textBox->SetStroke( STROKE_PARAMS( -1, LINE_STYLE::DEFAULT,
1680 GetColorFromInt( aElem->BorderColor ) ) );
1681
1682 switch( aElem->Alignment )
1683 {
1684 default:
1685 case ASCH_TEXT_FRAME_ALIGNMENT::LEFT:
1687 break;
1688 case ASCH_TEXT_FRAME_ALIGNMENT::CENTER:
1690 break;
1691 case ASCH_TEXT_FRAME_ALIGNMENT::RIGHT:
1693 break;
1694 }
1695
1696 size_t fontId = static_cast<int>( aElem->FontID );
1697
1698 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
1699 {
1700 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
1701 textBox->SetTextSize( { font.Size / 2, font.Size / 2 } );
1702
1703 // Must come after SetTextSize()
1704 textBox->SetBold( font.Bold );
1705 textBox->SetItalic( font.Italic );
1706 //textBox->SetFont( //how to set font, we have a font name here: ( font.fontname );
1707 }
1708
1709 textBox->SetFlags( IS_NEW );
1710
1711 SCH_SCREEN* screen = getCurrentScreen();
1712 wxCHECK( screen, /* void */ );
1713
1714 screen->Append( textBox );
1715}
1716
1717
1718void SCH_IO_ALTIUM::AddLibTextBox( const ASCH_TEXT_FRAME *aElem, std::vector<LIB_SYMBOL*>& aSymbol,
1719 std::vector<int>& aFontSizes )
1720{
1721 LIB_SYMBOL* symbol = static_cast<int>( aSymbol.size() ) <= aElem->ownerpartdisplaymode
1722 ? nullptr
1723 : aSymbol[aElem->ownerpartdisplaymode];
1724 SCH_SYMBOL* schsym = nullptr;
1725
1726 if( !symbol )
1727 {
1728 const auto& libSymbolIt = m_libSymbols.find( aElem->ownerindex );
1729
1730 if( libSymbolIt == m_libSymbols.end() )
1731 {
1732 // TODO: e.g. can depend on Template (RECORD=39
1734 wxString::Format( wxT( "Label's owner (%d) not found." ), aElem->ownerindex ),
1736 return;
1737 }
1738
1739 symbol = libSymbolIt->second;
1740 schsym = m_symbols.at( libSymbolIt->first );
1741 }
1742
1743 SCH_TEXTBOX* textBox = new SCH_TEXTBOX( LAYER_DEVICE );
1744
1745 textBox->SetUnit( std::max( 0, aElem->ownerpartid ) );
1746 symbol->AddDrawItem( textBox, false );
1747
1749 if( !schsym )
1750 {
1751 textBox->SetStart( aElem->TopRight );
1752 textBox->SetEnd( aElem->BottomLeft );
1753 }
1754 else
1755 {
1756 textBox->SetStart( GetRelativePosition( aElem->TopRight + m_sheetOffset, schsym ) );
1757 textBox->SetEnd( GetRelativePosition( aElem->BottomLeft + m_sheetOffset, schsym ) );
1758 }
1759
1760 textBox->SetText( aElem->Text );
1761
1762 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
1763
1764 if( aElem->isSolid)
1765 textBox->SetFillMode( FILL_T::FILLED_WITH_COLOR );
1766 else
1767 textBox->SetFilled( false );
1768
1769 if( aElem->ShowBorder )
1770 textBox->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::DEFAULT,
1771 GetColorFromInt( aElem->BorderColor ) ) );
1772 else
1773 textBox->SetStroke( STROKE_PARAMS( -1 ) );
1774
1775 switch( aElem->Alignment )
1776 {
1777 default:
1778 case ASCH_TEXT_FRAME_ALIGNMENT::LEFT:
1780 break;
1781 case ASCH_TEXT_FRAME_ALIGNMENT::CENTER:
1783 break;
1784 case ASCH_TEXT_FRAME_ALIGNMENT::RIGHT:
1786 break;
1787 }
1788
1789 if( aElem->FontID > 0 && aElem->FontID <= static_cast<int>( aFontSizes.size() ) )
1790 {
1791 int size = aFontSizes[aElem->FontID - 1];
1792 textBox->SetTextSize( { size, size } );
1793 }
1794}
1795
1796
1797void SCH_IO_ALTIUM::ParseBezier( const std::map<wxString, wxString>& aProperties,
1798 std::vector<LIB_SYMBOL*>& aSymbol )
1799{
1800 ASCH_BEZIER elem( aProperties );
1801
1802 if( elem.points.size() < 2 )
1803 {
1804 m_reporter->Report( wxString::Format( _( "Bezier has %d control points. At least 2 are "
1805 "expected." ),
1806 static_cast<int>( elem.points.size() ) ),
1808 return;
1809 }
1810
1811 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
1812 {
1813 SCH_SCREEN* currentScreen = getCurrentScreen();
1814 wxCHECK( currentScreen, /* void */ );
1815
1816 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
1817 {
1818 if( i + 2 == elem.points.size() )
1819 {
1820 // special case: single line
1821 SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
1822 SCH_LAYER_ID::LAYER_NOTES );
1823
1824 line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
1825 line->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
1826
1827 line->SetFlags( IS_NEW );
1828
1829 currentScreen->Append( line );
1830 }
1831 else
1832 {
1833 // simulate Bezier using line segments
1834 std::vector<VECTOR2I> bezierPoints;
1835 std::vector<VECTOR2I> polyPoints;
1836
1837 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
1838 bezierPoints.push_back( elem.points.at( j ) );
1839
1840 BEZIER_POLY converter( bezierPoints );
1841 converter.GetPoly( polyPoints );
1842
1843 for( size_t k = 0; k + 1 < polyPoints.size(); k++ )
1844 {
1845 SCH_LINE* line = new SCH_LINE( polyPoints.at( k ) + m_sheetOffset,
1846 SCH_LAYER_ID::LAYER_NOTES );
1847
1848 line->SetEndPoint( polyPoints.at( k + 1 ) + m_sheetOffset );
1849 line->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
1850
1851 line->SetFlags( IS_NEW );
1852 currentScreen->Append( line );
1853 }
1854 }
1855 }
1856 }
1857 else
1858 {
1859 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
1860 ? nullptr
1861 : aSymbol[elem.ownerpartdisplaymode];
1862 SCH_SYMBOL* schsym = nullptr;
1863
1864 if( !symbol )
1865 {
1866 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1867
1868 if( libSymbolIt == m_libSymbols.end() )
1869 {
1870 // TODO: e.g. can depend on Template (RECORD=39
1871 m_reporter->Report( wxString::Format( wxT( "Bezier's owner (%d) not found." ),
1872 elem.ownerindex ),
1874 return;
1875 }
1876
1877 symbol = libSymbolIt->second;
1878 schsym = m_symbols.at( libSymbolIt->first );
1879 }
1880
1881 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
1882 return;
1883
1884 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
1885 {
1886 if( i + 2 == elem.points.size() )
1887 {
1888 // special case: single line
1889 SCH_SHAPE* line = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
1890 symbol->AddDrawItem( line, false );
1891
1892 line->SetUnit( std::max( 0, elem.ownerpartid ) );
1893
1894 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
1895 {
1896 VECTOR2I pos = elem.points.at( j );
1897
1898 if( schsym )
1899 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
1900
1901 line->AddPoint( pos );
1902 }
1903
1904 line->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
1905 }
1906 else if( i + 3 == elem.points.size() )
1907 {
1908 // TODO: special case of a single line with an extra point?
1909 // I haven't a clue what this is all about, but the sample document we have in
1910 // https://gitlab.com/kicad/code/kicad/-/issues/8974 responds best by treating it
1911 // as another single line special case.
1912 SCH_SHAPE* line = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
1913 symbol->AddDrawItem( line, false );
1914
1915 line->SetUnit( std::max( 0, elem.ownerpartid ) );
1916
1917 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
1918 {
1919 VECTOR2I pos = elem.points.at( j );
1920
1921 if( schsym )
1922 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
1923
1924 line->AddPoint( pos );
1925 }
1926
1927 line->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
1928 }
1929 else
1930 {
1931 // Bezier always has exactly 4 control points
1932 SCH_SHAPE* bezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
1933 symbol->AddDrawItem( bezier, false );
1934
1935 bezier->SetUnit( std::max( 0, elem.ownerpartid ) );
1936
1937 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
1938 {
1939 VECTOR2I pos = elem.points.at( j );
1940
1941 if( schsym )
1942 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
1943
1944 switch( j - i )
1945 {
1946 case 0: bezier->SetStart( pos ); break;
1947 case 1: bezier->SetBezierC1( pos ); break;
1948 case 2: bezier->SetBezierC2( pos ); break;
1949 case 3: bezier->SetEnd( pos ); break;
1950 default: break; // Can't get here but silence warnings
1951 }
1952 }
1953
1954 bezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
1955 bezier->RebuildBezierToSegmentsPointsList( bezier->GetWidth() / 2 );
1956 }
1957 }
1958 }
1959}
1960
1961
1962void SCH_IO_ALTIUM::ParsePolyline( const std::map<wxString, wxString>& aProperties,
1963 std::vector<LIB_SYMBOL*>& aSymbol )
1964{
1965 ASCH_POLYLINE elem( aProperties );
1966
1967 if( elem.Points.size() < 2 )
1968 return;
1969
1970 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
1971 {
1972 SCH_SCREEN* screen = getCurrentScreen();
1973 wxCHECK( screen, /* void */ );
1974
1975 for( size_t i = 1; i < elem.Points.size(); i++ )
1976 {
1977 SCH_LINE* line = new SCH_LINE;
1978
1979 line->SetStartPoint( elem.Points[i - 1] + m_sheetOffset );
1980 line->SetEndPoint( elem.Points[i] + m_sheetOffset );
1981
1983 GetColorFromInt( elem.Color ) ) );
1984
1985 line->SetFlags( IS_NEW );
1986
1987 screen->Append( line );
1988 }
1989 }
1990 else
1991 {
1992 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
1993 ? nullptr
1994 : aSymbol[elem.ownerpartdisplaymode];
1995 SCH_SYMBOL* schsym = nullptr;
1996
1997 if( !symbol )
1998 {
1999 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2000
2001 if( libSymbolIt == m_libSymbols.end() )
2002 {
2003 // TODO: e.g. can depend on Template (RECORD=39
2004 m_reporter->Report( wxString::Format( wxT( "Polyline's owner (%d) not found." ),
2005 elem.ownerindex ),
2007 return;
2008 }
2009
2010 symbol = libSymbolIt->second;
2011 schsym = m_symbols.at( libSymbolIt->first );
2012 }
2013
2014 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2015 return;
2016
2017 SCH_SHAPE* line = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
2018 symbol->AddDrawItem( line, false );
2019
2020 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2021
2022 for( VECTOR2I point : elem.Points )
2023 {
2024 if( schsym )
2025 point = GetRelativePosition( point + m_sheetOffset, schsym );
2026
2027 line->AddPoint( point );
2028 }
2029
2030 SetLibShapeLine( elem, line, ALTIUM_SCH_RECORD::POLYLINE );
2031 STROKE_PARAMS stroke = line->GetStroke();
2032 stroke.SetLineStyle( GetPlotDashType( elem.LineStyle ) );
2033
2034 line->SetStroke( stroke );
2035 }
2036}
2037
2038
2039void SCH_IO_ALTIUM::ParsePolygon( const std::map<wxString, wxString>& aProperties,
2040 std::vector<LIB_SYMBOL*>& aSymbol )
2041{
2042 ASCH_POLYGON elem( aProperties );
2043
2044 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2045 {
2046 SCH_SCREEN* screen = getCurrentScreen();
2047 wxCHECK( screen, /* void */ );
2048
2049 SCH_SHAPE* poly = new SCH_SHAPE( SHAPE_T::POLY );
2050
2051 for( VECTOR2I& point : elem.points )
2052 poly->AddPoint( point + m_sheetOffset );
2053 poly->AddPoint( elem.points.front() + m_sheetOffset );
2054
2055 SetSchShapeLine( elem, poly );
2056 SetSchShapeFillAndColor( elem, poly );
2057 poly->SetFlags( IS_NEW );
2058
2059 screen->Append( poly );
2060 }
2061 else
2062 {
2063 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2064 ? nullptr
2065 : aSymbol[elem.ownerpartdisplaymode];
2066 SCH_SYMBOL* schsym = nullptr;
2067
2068 if( !symbol )
2069 {
2070 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2071
2072 if( libSymbolIt == m_libSymbols.end() )
2073 {
2074 // TODO: e.g. can depend on Template (RECORD=39
2075 m_reporter->Report( wxString::Format( wxT( "Polygon's owner (%d) not found." ),
2076 elem.ownerindex ),
2078 return;
2079 }
2080
2081 symbol = libSymbolIt->second;
2082 schsym = m_symbols.at( libSymbolIt->first );
2083 }
2084
2085 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2086 return;
2087
2088 SCH_SHAPE* line = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
2089
2090 symbol->AddDrawItem( line, false );
2091 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2092
2093 for( VECTOR2I point : elem.points )
2094 {
2095 if( schsym )
2096 point = GetRelativePosition( point + m_sheetOffset, schsym );
2097
2098 line->AddPoint( point );
2099 }
2100
2101 VECTOR2I point = elem.points.front();
2102
2103 if( schsym )
2104 point = GetRelativePosition( elem.points.front() + m_sheetOffset, schsym );
2105
2106 line->AddPoint( point );
2107
2108 SetLibShapeLine( elem, line, ALTIUM_SCH_RECORD::POLYGON );
2109 SetLibShapeFillAndColor( elem, line, ALTIUM_SCH_RECORD::POLYGON, elem.Color );
2110
2111 if( line->GetFillColor() == line->GetStroke().GetColor()
2112 && line->GetFillMode() != FILL_T::NO_FILL )
2113 {
2114 STROKE_PARAMS stroke = line->GetStroke();
2115 stroke.SetWidth( -1 );
2116 line->SetStroke( stroke );
2117 }
2118 }
2119}
2120
2121
2122void SCH_IO_ALTIUM::ParseRoundRectangle( const std::map<wxString, wxString>& aProperties,
2123 std::vector<LIB_SYMBOL*>& aSymbol )
2124{
2125 ASCH_ROUND_RECTANGLE elem( aProperties );
2126
2127 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2128 {
2129 SCH_SCREEN* screen = getCurrentScreen();
2130 wxCHECK( screen, /* void */ );
2131
2132 // TODO: misses rounded edges
2133 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
2134
2135 rect->SetPosition( elem.TopRight + m_sheetOffset );
2136 rect->SetEnd( elem.BottomLeft + m_sheetOffset );
2137 SetSchShapeLine( elem, rect );
2138 SetSchShapeFillAndColor( elem, rect );
2139 rect->SetFlags( IS_NEW );
2140
2141 screen->Append( rect );
2142 }
2143 else
2144 {
2145 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2146 ? nullptr
2147 : aSymbol[elem.ownerpartdisplaymode];
2148 SCH_SYMBOL* schsym = nullptr;
2149
2150 if( !symbol )
2151 {
2152 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2153
2154 if( libSymbolIt == m_libSymbols.end() )
2155 {
2156 // TODO: e.g. can depend on Template (RECORD=39
2157 m_reporter->Report( wxString::Format( wxT( "Rounded rectangle's owner (%d) not "
2158 "found." ),
2159 elem.ownerindex ),
2161 return;
2162 }
2163
2164 symbol = libSymbolIt->second;
2165 schsym = m_symbols.at( libSymbolIt->first );
2166 }
2167
2168 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2169 return;
2170
2171 SCH_SHAPE* rect = nullptr;
2172
2173 int width = std::abs( elem.TopRight.x - elem.BottomLeft.x );
2174 int height = std::abs( elem.TopRight.y - elem.BottomLeft.y );
2175
2176 // If it is a circle, make it a circle
2177 if( std::abs( elem.CornerRadius.x ) >= width / 2
2178 && std::abs( elem.CornerRadius.y ) >= height / 2 )
2179 {
2180 rect = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
2181
2182 VECTOR2I center = ( elem.TopRight + elem.BottomLeft ) / 2;
2183 int radius = std::min( width / 2, height / 2 );
2184
2185 if( schsym )
2186 center = GetRelativePosition( center + m_sheetOffset, schsym );
2187
2188 rect->SetPosition( center );
2189 rect->SetEnd( VECTOR2I( rect->GetPosition().x + radius, rect->GetPosition().y ) );
2190 }
2191 else
2192 {
2193 rect = new SCH_SHAPE( SHAPE_T::RECTANGLE, LAYER_DEVICE );
2194
2195 if( !schsym )
2196 {
2197 rect->SetPosition( elem.TopRight );
2198 rect->SetEnd( elem.BottomLeft );
2199 }
2200 else
2201 {
2202 rect->SetPosition( GetRelativePosition( elem.TopRight + m_sheetOffset, schsym ) );
2203 rect->SetEnd( GetRelativePosition( elem.BottomLeft + m_sheetOffset, schsym ) );
2204 }
2205
2206 rect->Normalize();
2207 }
2208
2209 SetLibShapeLine( elem, rect, ALTIUM_SCH_RECORD::ROUND_RECTANGLE );
2210 SetLibShapeFillAndColor( elem, rect, ALTIUM_SCH_RECORD::ROUND_RECTANGLE, elem.Color );
2211
2212 symbol->AddDrawItem( rect, false );
2213 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
2214 }
2215}
2216
2217
2218void SCH_IO_ALTIUM::ParseArc( const std::map<wxString, wxString>& aProperties,
2219 std::vector<LIB_SYMBOL*>& aSymbol )
2220{
2221 ASCH_ARC elem( aProperties );
2222
2223 int arc_radius = elem.m_Radius;
2224 VECTOR2I center = elem.m_Center;
2225 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
2226 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
2227 VECTOR2I startOffset( KiROUND( arc_radius * startAngle.Cos() ),
2228 -KiROUND( arc_radius * startAngle.Sin() ) );
2229 VECTOR2I endOffset( KiROUND( arc_radius * endAngle.Cos() ),
2230 -KiROUND( arc_radius * endAngle.Sin() ) );
2231
2232 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2233 {
2234 SCH_SCREEN* currentScreen = getCurrentScreen();
2235 wxCHECK( currentScreen, /* void */ );
2236
2237 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
2238 {
2239 SCH_SHAPE* circle = new SCH_SHAPE( SHAPE_T::CIRCLE );
2240
2241 circle->SetPosition( elem.m_Center + m_sheetOffset );
2242 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
2243
2244 SetSchShapeLine( elem, circle );
2245 SetSchShapeFillAndColor( elem, circle );
2246
2247 currentScreen->Append( circle );
2248 }
2249 else
2250 {
2251 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
2252
2253 arc->SetCenter( elem.m_Center + m_sheetOffset );
2254 arc->SetStart( elem.m_Center + startOffset + m_sheetOffset );
2255 arc->SetEnd( elem.m_Center + endOffset + m_sheetOffset );
2256
2257 SetSchShapeLine( elem, arc );
2258 SetSchShapeFillAndColor( elem, arc );
2259
2260 currentScreen->Append( arc );
2261 }
2262 }
2263 else
2264 {
2265 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2266 ? nullptr
2267 : aSymbol[elem.ownerpartdisplaymode];
2268 SCH_SYMBOL* schsym = nullptr;
2269
2270 if( !symbol )
2271 {
2272 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2273
2274 if( libSymbolIt == m_libSymbols.end() )
2275 {
2276 // TODO: e.g. can depend on Template (RECORD=39
2277 m_reporter->Report( wxString::Format( wxT( "Arc's owner (%d) not found." ),
2278 elem.ownerindex ),
2280 return;
2281 }
2282
2283 symbol = libSymbolIt->second;
2284 schsym = m_symbols.at( libSymbolIt->first );
2285 }
2286
2287 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2288 return;
2289
2290 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
2291 {
2292 SCH_SHAPE* circle = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
2293 symbol->AddDrawItem( circle, false );
2294
2295 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
2296
2297 if( schsym )
2298 center = GetRelativePosition( center + m_sheetOffset, schsym );
2299
2300 circle->SetPosition( center );
2301
2302 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
2303 SetLibShapeLine( elem, circle, ALTIUM_SCH_RECORD::ARC );
2304 SetLibShapeFillAndColor( elem, circle, ALTIUM_SCH_RECORD::ARC, elem.Color );
2305 }
2306 else
2307 {
2308 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC, LAYER_DEVICE );
2309 symbol->AddDrawItem( arc, false );
2310 arc->SetUnit( std::max( 0, elem.ownerpartid ) );
2311
2312 if( schsym )
2313 center = GetRelativePosition( elem.m_Center + m_sheetOffset, schsym );
2314
2315 arc->SetCenter( center );
2316 arc->SetStart( center + startOffset );
2317 arc->SetEnd( center + endOffset );
2318
2319 SetLibShapeLine( elem, arc, ALTIUM_SCH_RECORD::ARC );
2320 SetLibShapeFillAndColor( elem, arc, ALTIUM_SCH_RECORD::ARC, elem.Color );
2321 }
2322 }
2323}
2324
2325
2326void SCH_IO_ALTIUM::ParseEllipticalArc( const std::map<wxString, wxString>& aProperties,
2327 std::vector<LIB_SYMBOL*>& aSymbol )
2328{
2329 ASCH_ARC elem( aProperties );
2330
2331 if( elem.m_Radius == elem.m_SecondaryRadius && elem.m_StartAngle == 0
2332 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
2333 {
2334 ParseCircle( aProperties, aSymbol );
2335 return;
2336 }
2337
2338 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2339 {
2340 SCH_SCREEN* currentScreen = getCurrentScreen();
2341 wxCHECK( currentScreen, /* void */ );
2342
2343 ELLIPSE<int> ellipse( elem.m_Center + m_sheetOffset, elem.m_Radius,
2346 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
2347 std::vector<BEZIER<int>> beziers;
2348
2349 TransformEllipseToBeziers( ellipse, beziers );
2350
2351 for( const BEZIER<int>& bezier : beziers )
2352 {
2353 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
2354 schbezier->SetStart( bezier.Start );
2355 schbezier->SetBezierC1( bezier.C1 );
2356 schbezier->SetBezierC2( bezier.C2 );
2357 schbezier->SetEnd( bezier.End );
2358 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
2359 schbezier->RebuildBezierToSegmentsPointsList( elem.LineWidth / 2 );
2360
2361 currentScreen->Append( schbezier );
2362 }
2363 }
2364 else
2365 {
2366 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2367 ? nullptr
2368 : aSymbol[elem.ownerpartdisplaymode];
2369 SCH_SYMBOL* schsym = nullptr;
2370
2371 if( !symbol )
2372 {
2373 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2374
2375 if( libSymbolIt == m_libSymbols.end() )
2376 {
2377 // TODO: e.g. can depend on Template (RECORD=39
2378 m_reporter->Report( wxString::Format( wxT( "Elliptical Arc's owner (%d) not found." ),
2379 elem.ownerindex ),
2381 return;
2382 }
2383
2384 symbol = libSymbolIt->second;
2385 schsym = m_symbols.at( libSymbolIt->first );
2386 }
2387
2388 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2389 return;
2390
2391 ELLIPSE<int> ellipse( elem.m_Center, elem.m_Radius,
2394 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
2395 std::vector<BEZIER<int>> beziers;
2396
2397 TransformEllipseToBeziers( ellipse, beziers );
2398
2399 for( const BEZIER<int>& bezier : beziers )
2400 {
2401 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
2402 symbol->AddDrawItem( schbezier, false );
2403
2404 schbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
2405
2406 if( schsym )
2407 {
2408 schbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
2409 schbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
2410 schbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
2411 schbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
2412 }
2413 else
2414 {
2415 schbezier->SetStart( bezier.Start );
2416 schbezier->SetBezierC1( bezier.C1 );
2417 schbezier->SetBezierC2( bezier.C2 );
2418 schbezier->SetEnd( bezier.End );
2419 }
2420
2421 SetLibShapeLine( elem, schbezier, ALTIUM_SCH_RECORD::ELLIPTICAL_ARC );
2422 schbezier->RebuildBezierToSegmentsPointsList( elem.LineWidth / 2 );
2423 }
2424 }
2425}
2426
2427
2428void SCH_IO_ALTIUM::ParsePieChart( const std::map<wxString, wxString>& aProperties,
2429 std::vector<LIB_SYMBOL*>& aSymbol )
2430{
2431 ParseArc( aProperties, aSymbol );
2432
2433 ASCH_PIECHART elem( aProperties );
2434
2435 int arc_radius = elem.m_Radius;
2436 VECTOR2I center = elem.m_Center;
2437 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
2438 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
2439 VECTOR2I startOffset( KiROUND( arc_radius * startAngle.Cos() ),
2440 -KiROUND( arc_radius * startAngle.Sin() ) );
2441 VECTOR2I endOffset( KiROUND( arc_radius * endAngle.Cos() ),
2442 -KiROUND( arc_radius * endAngle.Sin() ) );
2443
2444 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2445 {
2446 SCH_SCREEN* screen = getCurrentScreen();
2447 wxCHECK( screen, /* void */ );
2448
2449 // close polygon
2450 SCH_LINE* line = new SCH_LINE( center + m_sheetOffset, SCH_LAYER_ID::LAYER_NOTES );
2451 line->SetEndPoint( center + startOffset + m_sheetOffset );
2452 line->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
2453
2454 line->SetFlags( IS_NEW );
2455 screen->Append( line );
2456
2457 line = new SCH_LINE( center + m_sheetOffset, SCH_LAYER_ID::LAYER_NOTES );
2458 line->SetEndPoint( center + endOffset + m_sheetOffset );
2459 line->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
2460
2461 line->SetFlags( IS_NEW );
2462 screen->Append( line );
2463 }
2464 else
2465 {
2466 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2467 ? nullptr
2468 : aSymbol[elem.ownerpartdisplaymode];
2469 SCH_SYMBOL* schsym = nullptr;
2470
2471 if( !symbol )
2472 {
2473 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2474
2475 if( libSymbolIt == m_libSymbols.end() )
2476 {
2477 // TODO: e.g. can depend on Template (RECORD=39
2478 m_reporter->Report( wxString::Format( wxT( "Piechart's owner (%d) not found." ),
2479 elem.ownerindex ),
2481 return;
2482 }
2483
2484 symbol = libSymbolIt->second;
2485 schsym = m_symbols.at( libSymbolIt->first );
2486 }
2487
2488 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2489 return;
2490
2491 SCH_SHAPE* line = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
2492 symbol->AddDrawItem( line, false );
2493
2494 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2495
2496 if( !schsym )
2497 {
2498 line->AddPoint( center + startOffset );
2499 line->AddPoint( center );
2500 line->AddPoint( center + endOffset );
2501 }
2502 else
2503 {
2504 line->AddPoint( GetRelativePosition( center + startOffset + m_sheetOffset, schsym ) );
2505 line->AddPoint( GetRelativePosition( center + m_sheetOffset, schsym ) );
2506 line->AddPoint( GetRelativePosition( center + endOffset + m_sheetOffset, schsym ) );
2507 }
2508
2509 SetLibShapeLine( elem, line, ALTIUM_SCH_RECORD::LINE );
2510 }
2511}
2512
2513
2514void SCH_IO_ALTIUM::ParseEllipse( const std::map<wxString, wxString>& aProperties,
2515 std::vector<LIB_SYMBOL*>& aSymbol )
2516{
2517 ASCH_ELLIPSE elem( aProperties );
2518
2519 if( elem.Radius == elem.SecondaryRadius )
2520 {
2521 ParseCircle( aProperties, aSymbol );
2522 return;
2523 }
2524
2525 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2526 {
2527 SCH_SCREEN* screen = getCurrentScreen();
2528 wxCHECK( screen, /* void */ );
2529
2530 COLOR4D fillColor = GetColorFromInt( elem.AreaColor );
2531
2532 if( elem.IsTransparent )
2533 fillColor = fillColor.WithAlpha( 0.5 );
2534
2535 FILL_T fillMode = elem.IsSolid ? FILL_T::FILLED_WITH_COLOR : FILL_T::NO_FILL;
2536
2537 ELLIPSE<int> ellipse( elem.Center + m_sheetOffset, elem.Radius,
2538 KiROUND( elem.SecondaryRadius ), ANGLE_0 );
2539
2540 std::vector<BEZIER<int>> beziers;
2541 std::vector<VECTOR2I> polyPoints;
2542
2543 TransformEllipseToBeziers( ellipse, beziers );
2544
2545 for( const BEZIER<int>& bezier : beziers )
2546 {
2547 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
2548 schbezier->SetStart( bezier.Start );
2549 schbezier->SetBezierC1( bezier.C1 );
2550 schbezier->SetBezierC2( bezier.C2 );
2551 schbezier->SetEnd( bezier.End );
2552 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
2553 schbezier->SetFillColor( fillColor );
2554 schbezier->SetFillMode( fillMode );
2555
2556 schbezier->RebuildBezierToSegmentsPointsList( schbezier->GetWidth() / 2 );
2557 screen->Append( schbezier );
2558
2559 polyPoints.push_back( bezier.Start );
2560 }
2561
2562 if( fillMode != FILL_T::NO_FILL )
2563 {
2564 SCH_SHAPE* schpoly = new SCH_SHAPE( SHAPE_T::POLY );
2565 schpoly->SetFillColor( fillColor );
2566 schpoly->SetFillMode( fillMode );
2567 schpoly->SetWidth( -1 );
2568
2569 for( const VECTOR2I& point : polyPoints )
2570 schpoly->AddPoint( point );
2571
2572 schpoly->AddPoint( polyPoints[0] );
2573
2574 screen->Append( schpoly );
2575 }
2576 }
2577 else
2578 {
2579 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2580 ? nullptr
2581 : aSymbol[elem.ownerpartdisplaymode];
2582 SCH_SYMBOL* schsym = nullptr;
2583
2584 if( !symbol )
2585 {
2586 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2587
2588 if( libSymbolIt == m_libSymbols.end() )
2589 {
2590 // TODO: e.g. can depend on Template (RECORD=39
2591 m_reporter->Report( wxString::Format( wxT( "Ellipse's owner (%d) not found." ),
2592 elem.ownerindex ),
2594 return;
2595 }
2596
2597 symbol = libSymbolIt->second;
2598 schsym = m_symbols.at( libSymbolIt->first );
2599 }
2600
2601 ELLIPSE<int> ellipse( elem.Center, elem.Radius, KiROUND( elem.SecondaryRadius ),
2602 ANGLE_0 );
2603
2604 std::vector<BEZIER<int>> beziers;
2605 std::vector<VECTOR2I> polyPoints;
2606
2607 TransformEllipseToBeziers( ellipse, beziers );
2608
2609 for( const BEZIER<int>& bezier : beziers )
2610 {
2611 SCH_SHAPE* libbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
2612 symbol->AddDrawItem( libbezier, false );
2613 libbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
2614
2615 if( !schsym )
2616 {
2617 libbezier->SetStart( bezier.Start );
2618 libbezier->SetBezierC1( bezier.C1 );
2619 libbezier->SetBezierC2( bezier.C2 );
2620 libbezier->SetEnd( bezier.End );
2621 }
2622 else
2623 {
2624 libbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
2625 libbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
2626 libbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
2627 libbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
2628 }
2629
2630 SetLibShapeLine( elem, libbezier, ALTIUM_SCH_RECORD::ELLIPSE );
2631 SetLibShapeFillAndColor( elem, libbezier, ALTIUM_SCH_RECORD::ELLIPSE, elem.Color );
2632 libbezier->RebuildBezierToSegmentsPointsList( libbezier->GetWidth() / 2 );
2633
2634 polyPoints.push_back( libbezier->GetStart() );
2635 }
2636
2637 // A series of beziers won't fill the center, so if this is meant to be fully filled,
2638 // Add a polygon to fill the center
2639 if( elem.IsSolid )
2640 {
2641 SCH_SHAPE* libline = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
2642 symbol->AddDrawItem( libline, false );
2643 libline->SetUnit( std::max( 0, elem.ownerpartid ) );
2644
2645 for( const VECTOR2I& point : polyPoints )
2646 libline->AddPoint( point );
2647
2648 libline->AddPoint( polyPoints[0] );
2649
2650 libline->SetWidth( -1 );
2651 SetLibShapeFillAndColor( elem, libline, ALTIUM_SCH_RECORD::ELLIPSE, elem.Color );
2652 }
2653 }
2654}
2655
2656
2657void SCH_IO_ALTIUM::ParseCircle( const std::map<wxString, wxString>& aProperties,
2658 std::vector<LIB_SYMBOL*>& aSymbol )
2659{
2660 ASCH_ELLIPSE elem( aProperties );
2661
2662 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2663 {
2664 SCH_SCREEN* screen = getCurrentScreen();
2665 wxCHECK( screen, /* void */ );
2666
2667 SCH_SHAPE* circle = new SCH_SHAPE( SHAPE_T::CIRCLE );
2668
2669 circle->SetPosition( elem.Center + m_sheetOffset );
2670 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
2671 circle->SetStroke( STROKE_PARAMS( 1, LINE_STYLE::SOLID ) );
2672
2673 circle->SetFillColor( GetColorFromInt( elem.AreaColor ) );
2674
2675 if( elem.IsSolid )
2676 circle->SetFillMode( FILL_T::FILLED_WITH_COLOR );
2677 else
2678 circle->SetFilled( false );
2679
2680 screen->Append( circle );
2681 }
2682 else
2683 {
2684 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2685 ? nullptr
2686 : aSymbol[elem.ownerpartdisplaymode];
2687 SCH_SYMBOL* schsym = nullptr;
2688
2689 if( !symbol )
2690 {
2691 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2692
2693 if( libSymbolIt == m_libSymbols.end() )
2694 {
2695 // TODO: e.g. can depend on Template (RECORD=39
2696 m_reporter->Report( wxString::Format( wxT( "Ellipse's owner (%d) not found." ),
2697 elem.ownerindex ),
2699 return;
2700 }
2701
2702 symbol = libSymbolIt->second;
2703 schsym = m_symbols.at( libSymbolIt->first );
2704 }
2705
2706 VECTOR2I center = elem.Center;
2707 SCH_SHAPE* circle = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
2708 symbol->AddDrawItem( circle, false );
2709
2710 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
2711
2712 if( schsym )
2713 center = GetRelativePosition( center + m_sheetOffset, schsym );
2714
2715 circle->SetPosition( center );
2716 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
2717
2718 SetLibShapeLine( elem, circle, ALTIUM_SCH_RECORD::ELLIPSE );
2719 SetLibShapeFillAndColor( elem, circle, ALTIUM_SCH_RECORD::ELLIPSE, elem.Color );
2720 }
2721}
2722
2723
2724void SCH_IO_ALTIUM::ParseLine( const std::map<wxString, wxString>& aProperties,
2725 std::vector<LIB_SYMBOL*>& aSymbol )
2726{
2727 ASCH_LINE elem( aProperties );
2728
2729 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2730 {
2731
2732 SCH_SCREEN* screen = getCurrentScreen();
2733 wxCHECK( screen, /* void */ );
2734
2735 // close polygon
2736 SCH_LINE* line = new SCH_LINE( elem.point1 + m_sheetOffset, SCH_LAYER_ID::LAYER_NOTES );
2737 line->SetEndPoint( elem.point2 + m_sheetOffset );
2739 GetColorFromInt( elem.Color ) ) );
2740
2741 line->SetFlags( IS_NEW );
2742 screen->Append( line );
2743 }
2744 else
2745 {
2746 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2747 ? nullptr
2748 : aSymbol[elem.ownerpartdisplaymode];
2749 SCH_SYMBOL* schsym = nullptr;
2750
2751 if( !symbol )
2752 {
2753 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2754
2755 if( libSymbolIt == m_libSymbols.end() )
2756 {
2757 // TODO: e.g. can depend on Template (RECORD=39
2758 m_reporter->Report( wxString::Format( wxT( "Line's owner (%d) not found." ),
2759 elem.ownerindex ),
2761 return;
2762 }
2763
2764 symbol = libSymbolIt->second;
2765 schsym = m_symbols.at( libSymbolIt->first );
2766 }
2767
2768 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2769 return;
2770
2771 SCH_SHAPE* line = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
2772 symbol->AddDrawItem( line, false );
2773
2774 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2775
2776 if( !schsym )
2777 {
2778 line->AddPoint( elem.point1 );
2779 line->AddPoint( elem.point2 );
2780 }
2781 else
2782 {
2783 line->AddPoint( GetRelativePosition( elem.point1 + m_sheetOffset, schsym ) );
2784 line->AddPoint( GetRelativePosition( elem.point2 + m_sheetOffset, schsym ) );
2785 }
2786
2787 SetLibShapeLine( elem, line, ALTIUM_SCH_RECORD::LINE );
2788 line->SetLineStyle( GetPlotDashType( elem.LineStyle ) );
2789 }
2790}
2791
2792
2793void SCH_IO_ALTIUM::ParseSignalHarness( const std::map<wxString, wxString>& aProperties )
2794{
2795 ASCH_SIGNAL_HARNESS elem( aProperties );
2796
2797 if( ShouldPutItemOnSheet( elem.ownerindex ) )
2798 {
2799 SCH_SCREEN* screen = getCurrentScreen();
2800 wxCHECK( screen, /* void */ );
2801
2802 SCH_SHAPE* poly = new SCH_SHAPE( SHAPE_T::POLY );
2803
2804 for( VECTOR2I& point : elem.Points )
2805 poly->AddPoint( point + m_sheetOffset );
2806
2807 poly->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID,
2808 GetColorFromInt( elem.Color ) ) );
2809 poly->SetFlags( IS_NEW );
2810
2811 screen->Append( poly );
2812 }
2813 else
2814 {
2815 // No clue if this situation can ever exist
2816 m_reporter->Report( wxT( "Signal harness, belonging to the part is not currently "
2817 "supported." ), RPT_SEVERITY_DEBUG );
2818 }
2819}
2820
2821
2822void SCH_IO_ALTIUM::ParseHarnessConnector( int aIndex, const std::map<wxString,
2823 wxString>& aProperties )
2824{
2825 ASCH_HARNESS_CONNECTOR elem( aProperties );
2826
2827 if( ShouldPutItemOnSheet( elem.ownerindex ) )
2828 {
2829 SCH_SCREEN* currentScreen = getCurrentScreen();
2830 wxCHECK( currentScreen, /* void */ );
2831
2833 elem.Size );
2834
2835 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
2837 sheet->SetBorderColor( GetColorFromInt( elem.Color ) );
2838
2839 currentScreen->Append( sheet );
2840
2841 SCH_SHEET_PATH sheetpath = m_sheetPath;
2842 sheetpath.push_back( sheet );
2843
2844 sheetpath.SetPageNumber( "Harness #" );
2845
2847 m_sheets.insert( { m_harnessEntryParent, sheet } );
2848 }
2849 else
2850 {
2851 // I have no clue if this situation can ever exist
2852 m_reporter->Report( wxT( "Harness connector, belonging to the part is not currently "
2853 "supported." ),
2855 }
2856}
2857
2858
2859void SCH_IO_ALTIUM::ParseHarnessEntry( const std::map<wxString, wxString>& aProperties )
2860{
2861 ASCH_HARNESS_ENTRY elem( aProperties );
2862
2863 const auto& sheetIt = m_sheets.find( m_harnessEntryParent );
2864
2865 if( sheetIt == m_sheets.end() )
2866 {
2867 m_reporter->Report( wxString::Format( wxT( "Harness entry's parent (%d) not found." ),
2870 return;
2871 }
2872
2873 SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheetIt->second );
2874 sheetIt->second->AddPin( sheetPin );
2875
2876 sheetPin->SetText( elem.Name );
2877 sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED );
2878
2879 VECTOR2I pos = sheetIt->second->GetPosition();
2880 VECTOR2I size = sheetIt->second->GetSize();
2881
2882 switch( elem.Side )
2883 {
2884 default:
2885 case ASCH_SHEET_ENTRY_SIDE::LEFT:
2886 sheetPin->SetPosition( { pos.x, pos.y + elem.DistanceFromTop } );
2887 sheetPin->SetSpinStyle( SPIN_STYLE::LEFT );
2888 sheetPin->SetSide( SHEET_SIDE::LEFT );
2889 break;
2890 case ASCH_SHEET_ENTRY_SIDE::RIGHT:
2891 sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.DistanceFromTop } );
2892 sheetPin->SetSpinStyle( SPIN_STYLE::RIGHT );
2893 sheetPin->SetSide( SHEET_SIDE::RIGHT );
2894 break;
2895 case ASCH_SHEET_ENTRY_SIDE::TOP:
2896 sheetPin->SetPosition( { pos.x + elem.DistanceFromTop, pos.y } );
2897 sheetPin->SetSpinStyle( SPIN_STYLE::UP );
2898 sheetPin->SetSide( SHEET_SIDE::TOP );
2899 break;
2900 case ASCH_SHEET_ENTRY_SIDE::BOTTOM:
2901 sheetPin->SetPosition( { pos.x + elem.DistanceFromTop, pos.y + size.y } );
2902 sheetPin->SetSpinStyle( SPIN_STYLE::BOTTOM );
2903 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
2904 break;
2905 }
2906}
2907
2908
2909void SCH_IO_ALTIUM::ParseHarnessType( const std::map<wxString, wxString>& aProperties )
2910{
2911 ASCH_HARNESS_TYPE elem( aProperties );
2912
2913 const auto& sheetIt = m_sheets.find( m_harnessEntryParent );
2914
2915 if( sheetIt == m_sheets.end() )
2916 {
2917 m_reporter->Report( wxString::Format( wxT( "Harness type's parent (%d) not found." ),
2920 return;
2921 }
2922
2923 SCH_FIELD& sheetNameField = sheetIt->second->GetFields()[SHEETNAME];
2924
2925 sheetNameField.SetPosition( elem.Location + m_sheetOffset );
2926 sheetNameField.SetText( elem.Text );
2927
2928 // Always set as visible so user is aware about ( !elem.isHidden );
2929 sheetNameField.SetVisible( true );
2930 SetTextPositioning( &sheetNameField, ASCH_LABEL_JUSTIFICATION::BOTTOM_LEFT,
2931 ASCH_RECORD_ORIENTATION::RIGHTWARDS );
2932 sheetNameField.SetTextColor( GetColorFromInt( elem.Color ) );
2933
2934 SCH_FIELD& sheetFileName = sheetIt->second->GetFields()[SHEETFILENAME];
2935 sheetFileName.SetText( elem.Text + wxT( "." ) + FILEEXT::KiCadSchematicFileExtension );
2936
2937 wxFileName fn( m_schematic->Prj().GetProjectPath(), elem.Text,
2939 wxString fullPath = fn.GetFullPath();
2940
2941 fullPath.Replace( wxT( "\\" ), wxT( "/" ) );
2942
2943 SCH_SCREEN* screen = sheetIt->second->GetScreen();
2944
2945 wxCHECK( screen, /* void */ );
2946 screen->SetFileName( fullPath );
2947
2948 m_reporter->Report( wxString::Format( _( "Altium's harness connector (%s) was imported as a "
2949 "hierarchical sheet. Please review the imported "
2950 "schematic." ),
2951 elem.Text ),
2953}
2954
2955
2956void SCH_IO_ALTIUM::ParseRectangle( const std::map<wxString, wxString>& aProperties,
2957 std::vector<LIB_SYMBOL*>& aSymbol )
2958{
2959 ASCH_RECTANGLE elem( aProperties );
2960
2961 VECTOR2I sheetTopRight = elem.TopRight + m_sheetOffset;
2962 VECTOR2I sheetBottomLeft = elem.BottomLeft + m_sheetOffset;
2963
2964 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2965 {
2966 SCH_SCREEN* screen = getCurrentScreen();
2967 wxCHECK( screen, /* void */ );
2968
2969 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
2970
2971 rect->SetPosition( sheetTopRight );
2972 rect->SetEnd( sheetBottomLeft );
2973 SetSchShapeLine( elem, rect );
2974 SetSchShapeFillAndColor( elem, rect );
2975 rect->SetFlags( IS_NEW );
2976
2977 screen->Append( rect );
2978 }
2979 else
2980 {
2981 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode
2982 ? nullptr
2983 : aSymbol[elem.ownerpartdisplaymode];
2984 SCH_SYMBOL* schsym = nullptr;
2985
2986 if( !symbol )
2987 {
2988 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2989
2990 if( libSymbolIt == m_libSymbols.end() )
2991 {
2992 // TODO: e.g. can depend on Template (RECORD=39
2993 m_reporter->Report( wxString::Format( wxT( "Rectangle's owner (%d) not found." ),
2994 elem.ownerindex ),
2996 return;
2997 }
2998
2999 symbol = libSymbolIt->second;
3000 schsym = m_symbols.at( libSymbolIt->first );
3001 }
3002
3003 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
3004 return;
3005
3006 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE, LAYER_DEVICE );
3007 symbol->AddDrawItem( rect, false );
3008
3009 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
3010
3011 if( !schsym )
3012 {
3013 rect->SetPosition( sheetTopRight );
3014 rect->SetEnd( sheetBottomLeft );
3015 }
3016 else
3017 {
3018 rect->SetPosition( GetRelativePosition( sheetTopRight, schsym ) );
3019 rect->SetEnd( GetRelativePosition( sheetBottomLeft, schsym ) );
3020 }
3021
3022 SetLibShapeLine( elem, rect, ALTIUM_SCH_RECORD::RECTANGLE );
3023 SetLibShapeFillAndColor( elem, rect, ALTIUM_SCH_RECORD::RECTANGLE, elem.Color );
3024 }
3025}
3026
3027
3028void SCH_IO_ALTIUM::ParseSheetSymbol( int aIndex, const std::map<wxString, wxString>& aProperties )
3029{
3030 ASCH_SHEET_SYMBOL elem( aProperties );
3031
3032 SCH_SHEET* sheet = new SCH_SHEET( getCurrentSheet(), elem.location + m_sheetOffset, elem.size );
3033
3034 sheet->SetBorderColor( GetColorFromInt( elem.color ) );
3035
3036 if( elem.isSolid )
3038
3039 sheet->SetFlags( IS_NEW );
3040
3041 SCH_SCREEN* currentScreen = getCurrentScreen();
3042 wxCHECK( currentScreen, /* void */ );
3043 currentScreen->Append( sheet );
3044
3045 SCH_SHEET_PATH sheetpath = m_sheetPath;
3046 sheetpath.push_back( sheet );
3047
3048 // We'll update later if we find a pageNumber record for it.
3049 sheetpath.SetPageNumber( "#" );
3050
3051 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
3052 wxCHECK( rootScreen, /* void */ );
3053
3054 SCH_SHEET_INSTANCE sheetInstance;
3055
3056 sheetInstance.m_Path = sheetpath.Path();
3057 sheetInstance.m_PageNumber = wxT( "#" );
3058
3059 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
3060 m_sheets.insert( { aIndex, sheet } );
3061}
3062
3063
3064void SCH_IO_ALTIUM::ParseSheetEntry( const std::map<wxString, wxString>& aProperties )
3065{
3066 ASCH_SHEET_ENTRY elem( aProperties );
3067
3068 const auto& sheetIt = m_sheets.find( elem.ownerindex );
3069
3070 if( sheetIt == m_sheets.end() )
3071 {
3072 m_reporter->Report( wxString::Format( wxT( "Sheet entry's owner (%d) not found." ),
3073 elem.ownerindex ),
3075 return;
3076 }
3077
3078 SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheetIt->second );
3079 sheetIt->second->AddPin( sheetPin );
3080
3081 sheetPin->SetText( elem.name );
3082 sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED );
3083 //sheetPin->SetSpinStyle( getSpinStyle( term.OrientAngle, false ) );
3084 //sheetPin->SetPosition( getKiCadPoint( term.Position ) );
3085
3086 VECTOR2I pos = sheetIt->second->GetPosition();
3087 VECTOR2I size = sheetIt->second->GetSize();
3088
3089 switch( elem.side )
3090 {
3091 default:
3092 case ASCH_SHEET_ENTRY_SIDE::LEFT:
3093 sheetPin->SetPosition( { pos.x, pos.y + elem.distanceFromTop } );
3094 sheetPin->SetSpinStyle( SPIN_STYLE::LEFT );
3095 sheetPin->SetSide( SHEET_SIDE::LEFT );
3096 break;
3097
3098 case ASCH_SHEET_ENTRY_SIDE::RIGHT:
3099 sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.distanceFromTop } );
3100 sheetPin->SetSpinStyle( SPIN_STYLE::RIGHT );
3101 sheetPin->SetSide( SHEET_SIDE::RIGHT );
3102 break;
3103
3104 case ASCH_SHEET_ENTRY_SIDE::TOP:
3105 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y } );
3106 sheetPin->SetSpinStyle( SPIN_STYLE::UP );
3107 sheetPin->SetSide( SHEET_SIDE::TOP );
3108 break;
3109
3110 case ASCH_SHEET_ENTRY_SIDE::BOTTOM:
3111 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y + size.y } );
3112 sheetPin->SetSpinStyle( SPIN_STYLE::BOTTOM );
3113 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
3114 break;
3115 }
3116
3117 switch( elem.iotype )
3118 {
3119 default:
3120 case ASCH_PORT_IOTYPE::UNSPECIFIED:
3121 sheetPin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED );
3122 break;
3123
3124 case ASCH_PORT_IOTYPE::OUTPUT:
3125 sheetPin->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT );
3126 break;
3127
3128 case ASCH_PORT_IOTYPE::INPUT:
3129 sheetPin->SetShape( LABEL_FLAG_SHAPE::L_INPUT );
3130 break;
3131
3132 case ASCH_PORT_IOTYPE::BIDI:
3133 sheetPin->SetShape( LABEL_FLAG_SHAPE::L_BIDI );
3134 break;
3135 }
3136}
3137
3138
3140 REPORTER* aReporter )
3141{
3143 {
3146 line1->AddPoint( { 0, 0 } );
3147 line1->AddPoint( { 0, schIUScale.MilsToIU( 50 ) } );
3148 aKsymbol->AddDrawItem( line1, false );
3149
3150 if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE )
3151 {
3154 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 75 ) } );
3155 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 25 ), 0 ) );
3156 aKsymbol->AddDrawItem( circle, false );
3157 }
3158 else
3159 {
3162 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
3163 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
3164 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
3165 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
3166 aKsymbol->AddDrawItem( line2, false );
3167 }
3168
3169 return { 0, schIUScale.MilsToIU( 150 ) };
3170 }
3171 else if( aStyle == ASCH_POWER_PORT_STYLE::WAVE )
3172 {
3175 line->AddPoint( { 0, 0 } );
3176 line->AddPoint( { 0, schIUScale.MilsToIU( 72 ) } );
3177 aKsymbol->AddDrawItem( line, false );
3178
3181 bezier->SetStart( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 50 ) } );
3182 bezier->SetBezierC1( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 87 ) } );
3183 bezier->SetBezierC2( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 63 ) } );
3184 bezier->SetEnd( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 100 ) } );
3185 aKsymbol->AddDrawItem( bezier, false );
3186
3187 return { 0, schIUScale.MilsToIU( 150 ) };
3188 }
3189 else if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND
3191 || aStyle == ASCH_POWER_PORT_STYLE::EARTH
3193 {
3196 line1->AddPoint( { 0, 0 } );
3197 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
3198 aKsymbol->AddDrawItem( line1, false );
3199
3201 {
3204 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3205 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
3206 aKsymbol->AddDrawItem( line2, false );
3207
3210 line3->AddPoint( { schIUScale.MilsToIU( -70 ), schIUScale.MilsToIU( 130 ) } );
3211 line3->AddPoint( { schIUScale.MilsToIU( 70 ), schIUScale.MilsToIU( 130 ) } );
3212 aKsymbol->AddDrawItem( line3, false );
3213
3216 line4->AddPoint( { schIUScale.MilsToIU( -40 ), schIUScale.MilsToIU( 160 ) } );
3217 line4->AddPoint( { schIUScale.MilsToIU( 40 ), schIUScale.MilsToIU( 160 ) } );
3218 aKsymbol->AddDrawItem( line4, false );
3219
3222 line5->AddPoint( { schIUScale.MilsToIU( -10 ), schIUScale.MilsToIU( 190 ) } );
3223 line5->AddPoint( { schIUScale.MilsToIU( 10 ), schIUScale.MilsToIU( 190 ) } );
3224 aKsymbol->AddDrawItem( line5, false );
3225 }
3226 else if( aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND )
3227 {
3230 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3231 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
3232 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 200 ) } );
3233 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3234 aKsymbol->AddDrawItem( line2, false );
3235 }
3236 else if( aStyle == ASCH_POWER_PORT_STYLE::EARTH )
3237 {
3240 line2->AddPoint( { schIUScale.MilsToIU( -150 ), schIUScale.MilsToIU( 200 ) } );
3241 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3242 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
3243 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 200 ) } );
3244 aKsymbol->AddDrawItem( line2, false );
3245
3248 line3->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
3249 line3->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 200 ) } );
3250 aKsymbol->AddDrawItem( line3, false );
3251 }
3252 else // ASCH_POWER_PORT_STYLE::GOST_ARROW
3253 {
3256 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
3257 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
3258 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
3259 aKsymbol->AddDrawItem( line2, false );
3260
3261 return { 0, schIUScale.MilsToIU( 150 ) }; // special case
3262 }
3263
3264 return { 0, schIUScale.MilsToIU( 250 ) };
3265 }
3268 {
3271 line1->AddPoint( { 0, 0 } );
3272 line1->AddPoint( { 0, schIUScale.MilsToIU( 160 ) } );
3273 aKsymbol->AddDrawItem( line1, false );
3274
3277 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 160 ) } );
3278 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 160 ) } );
3279 aKsymbol->AddDrawItem( line2, false );
3280
3283 line3->AddPoint( { schIUScale.MilsToIU( -60 ), schIUScale.MilsToIU( 200 ) } );
3284 line3->AddPoint( { schIUScale.MilsToIU( 60 ), schIUScale.MilsToIU( 200 ) } );
3285 aKsymbol->AddDrawItem( line3, false );
3286
3289 line4->AddPoint( { schIUScale.MilsToIU( -20 ), schIUScale.MilsToIU( 240 ) } );
3290 line4->AddPoint( { schIUScale.MilsToIU( 20 ), schIUScale.MilsToIU( 240 ) } );
3291 aKsymbol->AddDrawItem( line4, false );
3292
3294 return { 0, schIUScale.MilsToIU( -300 ) };
3295
3298 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 160 ) } );
3299 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 120 ), 0 ) );
3300 aKsymbol->AddDrawItem( circle, false );
3301
3302 return { 0, schIUScale.MilsToIU( 350 ) };
3303 }
3304 else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_BAR )
3305 {
3308 line1->AddPoint( { 0, 0 } );
3309 line1->AddPoint( { 0, schIUScale.MilsToIU( 200 ) } );
3310 aKsymbol->AddDrawItem( line1, false );
3311
3314 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 200 ) } );
3315 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 200 ) } );
3316 aKsymbol->AddDrawItem( line2, false );
3317
3318 return { 0, schIUScale.MilsToIU( 250 ) };
3319 }
3320 else
3321 {
3322 if( aStyle != ASCH_POWER_PORT_STYLE::BAR )
3323 {
3324 aReporter->Report( _( "Power Port with unknown style imported as 'Bar' type." ),
3326 }
3327
3330 line1->AddPoint( { 0, 0 } );
3331 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
3332 aKsymbol->AddDrawItem( line1, false );
3333
3336 line2->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 100 ) } );
3337 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 100 ) } );
3338 aKsymbol->AddDrawItem( line2, false );
3339
3340 return { 0, schIUScale.MilsToIU( 150 ) };
3341 }
3342}
3343
3344
3345void SCH_IO_ALTIUM::ParsePowerPort( const std::map<wxString, wxString>& aProperties )
3346{
3347 ASCH_POWER_PORT elem( aProperties );
3348
3349 wxString symName( elem.text );
3350 std::string styleName( magic_enum::enum_name<ASCH_POWER_PORT_STYLE>( elem.style ) );
3351
3352 if( !styleName.empty() )
3353 symName << '_' << styleName;
3354
3355 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symName );
3356 LIB_SYMBOL* libSymbol = nullptr;
3357
3358 const auto& powerSymbolIt = m_powerSymbols.find( symName );
3359
3360 if( powerSymbolIt != m_powerSymbols.end() )
3361 {
3362 libSymbol = powerSymbolIt->second; // cache hit
3363 }
3364 else if( LIB_SYMBOL* alreadyLoaded = m_pi->LoadSymbol( getLibFileName().GetFullPath(), symName,
3365 m_properties.get() ) )
3366 {
3367 libSymbol = alreadyLoaded;
3368 }
3369 else
3370 {
3371 libSymbol = new LIB_SYMBOL( wxEmptyString );
3372 libSymbol->SetPower();
3373 libSymbol->SetName( elem.text );
3374 libSymbol->GetReferenceField().SetText( "#PWR" );
3375 libSymbol->GetReferenceField().SetVisible( false );
3376 libSymbol->GetValueField().SetText( elem.text );
3377 libSymbol->GetValueField().SetVisible( true );
3378 libSymbol->SetDescription( wxString::Format( _( "Power symbol creates a global "
3379 "label with name '%s'" ), elem.text ) );
3380 libSymbol->SetKeyWords( "power-flag" );
3381 libSymbol->SetLibId( libId );
3382
3383 // generate graphic
3384 SCH_PIN* pin = new SCH_PIN( libSymbol );
3385 libSymbol->AddDrawItem( pin, false );
3386
3387 pin->SetName( elem.text );
3388 pin->SetPosition( { 0, 0 } );
3389 pin->SetLength( 0 );
3390 pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
3391 pin->SetVisible( false );
3392
3393 VECTOR2I valueFieldPos = HelperGeneratePowerPortGraphics( libSymbol, elem.style,
3394 m_reporter );
3395
3396 libSymbol->GetValueField().SetPosition( valueFieldPos );
3397
3398 // this has to be done after parsing the LIB_SYMBOL!
3399 m_pi->SaveSymbol( getLibFileName().GetFullPath(), libSymbol, m_properties.get() );
3400 m_powerSymbols.insert( { symName, libSymbol } );
3401 }
3402
3403 SCH_SCREEN* screen = getCurrentScreen();
3404 wxCHECK( screen, /* void */ );
3405
3406 // each symbol has its own powerSymbolIt for now
3407 SCH_SYMBOL* symbol = new SCH_SYMBOL();
3408 symbol->SetRef( &m_sheetPath, "#PWR?" );
3409 symbol->GetField( REFERENCE_FIELD )->SetVisible( false );
3410 symbol->SetValueFieldText( elem.text );
3411 symbol->SetLibId( libId );
3412 symbol->SetLibSymbol( new LIB_SYMBOL( *libSymbol ) );
3413
3414 SCH_FIELD* valueField = symbol->GetField( VALUE_FIELD );
3415 valueField->SetVisible( elem.showNetName );
3416
3417 // TODO: Why do I need to set this a second time?
3418 valueField->SetPosition( libSymbol->GetValueField().GetPosition() );
3419
3420 symbol->SetPosition( elem.location + m_sheetOffset );
3421
3422 switch( elem.orientation )
3423 {
3424 case ASCH_RECORD_ORIENTATION::RIGHTWARDS:
3425 symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_90 );
3426 valueField->SetTextAngle( ANGLE_VERTICAL );
3428 break;
3429
3430 case ASCH_RECORD_ORIENTATION::UPWARDS:
3431 symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_180 );
3432 valueField->SetTextAngle( ANGLE_HORIZONTAL );
3434 break;
3435
3436 case ASCH_RECORD_ORIENTATION::LEFTWARDS:
3437 symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_270 );
3438 valueField->SetTextAngle( ANGLE_VERTICAL );
3440 break;
3441
3442 case ASCH_RECORD_ORIENTATION::DOWNWARDS:
3443 symbol->SetOrientation( SYMBOL_ORIENTATION_T::SYM_ORIENT_0 );
3444 valueField->SetTextAngle( ANGLE_HORIZONTAL );
3446 break;
3447
3448 default:
3449 m_reporter->Report( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
3450 break;
3451 }
3452
3453 screen->Append( symbol );
3454}
3455
3456
3458{
3459 SCH_TEXTBOX* textBox = new SCH_TEXTBOX();
3460
3461 textBox->SetText( aElem.Name );
3462 textBox->SetTextColor( GetColorFromInt( aElem.TextColor ) );
3463
3464 int height = aElem.Height;
3465 if( height <= 0 )
3466 height = schIUScale.MilsToIU( 100 ); // chose default 50 grid
3467
3468 textBox->SetStartX( ( aElem.Location + m_sheetOffset ).x );
3469 textBox->SetStartY( ( aElem.Location + m_sheetOffset ).y - ( height / 2 ) );
3470 textBox->SetEndX( ( aElem.Location + m_sheetOffset ).x + ( aElem.Width ) );
3471 textBox->SetEndY( ( aElem.Location + m_sheetOffset ).y + ( height / 2 ) );
3472
3474 textBox->SetFillMode( FILL_T::FILLED_WITH_COLOR );
3475
3476 textBox->SetStroke( STROKE_PARAMS( 2, LINE_STYLE::DEFAULT,
3478
3479 switch( aElem.Alignment )
3480 {
3481 default:
3482 case ASCH_TEXT_FRAME_ALIGNMENT::LEFT:
3484 break;
3485
3486 case ASCH_TEXT_FRAME_ALIGNMENT::CENTER:
3488 break;
3489
3490 case ASCH_TEXT_FRAME_ALIGNMENT::RIGHT:
3492 break;
3493 }
3494
3495 size_t fontId = static_cast<int>( aElem.FontID );
3496
3497 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
3498 {
3499 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
3500 textBox->SetTextSize( { font.Size / 2, font.Size / 2 } );
3501
3502 // Must come after SetTextSize()
3503 textBox->SetBold( font.Bold );
3504 textBox->SetItalic( font.Italic );
3505 //textBox->SetFont( //how to set font, we have a font name here: ( font.fontname );
3506 }
3507
3508 textBox->SetFlags( IS_NEW );
3509
3510 SCH_SCREEN* screen = getCurrentScreen();
3511 wxCHECK( screen, /* void */ );
3512
3513 screen->Append( textBox );
3514
3515 m_reporter->Report( wxString::Format( _( "Altium's harness port (%s) was imported as "
3516 "a text box. Please review the imported "
3517 "schematic." ),
3518 aElem.Name ),
3520}
3521
3522
3524{
3525 if( !aElem.HarnessType.IsEmpty() )
3526 {
3527 // Parse harness ports after "Additional" compound section is parsed
3528 m_altiumHarnessPortsCurrentSheet.emplace_back( aElem );
3529 return;
3530 }
3531
3532 VECTOR2I start = aElem.Location + m_sheetOffset;
3533 VECTOR2I end = start;
3534
3535 switch( aElem.Style )
3536 {
3537 default:
3538 case ASCH_PORT_STYLE::NONE_HORIZONTAL:
3539 case ASCH_PORT_STYLE::LEFT:
3540 case ASCH_PORT_STYLE::RIGHT:
3541 case ASCH_PORT_STYLE::LEFT_RIGHT:
3542 end.x += aElem.Width;
3543 break;
3544
3545 case ASCH_PORT_STYLE::NONE_VERTICAL:
3546 case ASCH_PORT_STYLE::TOP:
3547 case ASCH_PORT_STYLE::BOTTOM:
3548 case ASCH_PORT_STYLE::TOP_BOTTOM:
3549 end.y -= aElem.Width;
3550 break;
3551 }
3552
3553 // Check which connection points exists in the schematic
3554 SCH_SCREEN* screen = getCurrentScreen();
3555 wxCHECK( screen, /* void */ );
3556
3557 bool startIsWireTerminal = screen->IsTerminalPoint( start, LAYER_WIRE );
3558 bool startIsBusTerminal = screen->IsTerminalPoint( start, LAYER_BUS );
3559
3560 bool endIsWireTerminal = screen->IsTerminalPoint( end, LAYER_WIRE );
3561 bool endIsBusTerminal = screen->IsTerminalPoint( end, LAYER_BUS );
3562
3563 // check if any of the points is a terminal point
3564 // TODO: there seems a problem to detect approximated connections towards component pins?
3565 bool connectionFound = startIsWireTerminal
3566 || startIsBusTerminal
3567 || endIsWireTerminal
3568 || endIsBusTerminal;
3569
3570 if( !connectionFound )
3571 {
3572 m_reporter->Report( wxString::Format( _( "Port %s has no connections." ), aElem.Name ),
3574 }
3575
3576 // Select label position. In case both match, we will add a line later.
3577 VECTOR2I position = ( startIsWireTerminal || startIsBusTerminal ) ? start : end;
3578 SCH_LABEL_BASE* label;
3579
3580 // TODO: detect correct label type depending on sheet settings, etc.
3581#if 1 // Set to 1 to use SCH_HIERLABEL label, 0 to use SCH_GLOBALLABEL
3582 {
3583 label = new SCH_HIERLABEL( position, aElem.Name );
3584 }
3585#else
3586 label = new SCH_GLOBALLABEL( position, aElem.Name );
3587#endif
3588
3589 switch( aElem.IOtype )
3590 {
3591 default:
3592 case ASCH_PORT_IOTYPE::UNSPECIFIED: label->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
3593 case ASCH_PORT_IOTYPE::OUTPUT: label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT ); break;
3594 case ASCH_PORT_IOTYPE::INPUT: label->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
3595 case ASCH_PORT_IOTYPE::BIDI: label->SetShape( LABEL_FLAG_SHAPE::L_BIDI ); break;
3596 }
3597
3598 switch( aElem.Style )
3599 {
3600 default:
3601 case ASCH_PORT_STYLE::NONE_HORIZONTAL:
3602 case ASCH_PORT_STYLE::LEFT:
3603 case ASCH_PORT_STYLE::RIGHT:
3604 case ASCH_PORT_STYLE::LEFT_RIGHT:
3605 if( ( startIsWireTerminal || startIsBusTerminal ) )
3607 else
3609
3610 break;
3611
3612 case ASCH_PORT_STYLE::NONE_VERTICAL:
3613 case ASCH_PORT_STYLE::TOP:
3614 case ASCH_PORT_STYLE::BOTTOM:
3615 case ASCH_PORT_STYLE::TOP_BOTTOM:
3616 if( ( startIsWireTerminal || startIsBusTerminal ) )
3617 label->SetSpinStyle( SPIN_STYLE::UP );
3618 else
3620
3621 break;
3622 }
3623
3624 label->AutoplaceFields( screen, false );
3625
3626 // Default "Sheet References" field should be hidden, at least for now
3627 if( label->GetFields().size() > 0 )
3628 label->GetFields()[0].SetVisible( false );
3629
3630 label->SetFlags( IS_NEW );
3631
3632 screen->Append( label );
3633
3634 // This is a hack, for the case both connection points are valid: add a small wire
3635 if( ( startIsWireTerminal && endIsWireTerminal ) )
3636 {
3637 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
3638 wire->SetEndPoint( end );
3639 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
3640 wire->SetFlags( IS_NEW );
3641 screen->Append( wire );
3642 }
3643 else if( startIsBusTerminal && endIsBusTerminal )
3644 {
3645 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
3646 wire->SetEndPoint( end );
3647 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
3648 wire->SetFlags( IS_NEW );
3649 screen->Append( wire );
3650 }
3651}
3652
3653
3654void SCH_IO_ALTIUM::ParseNoERC( const std::map<wxString, wxString>& aProperties )
3655{
3656 ASCH_NO_ERC elem( aProperties );
3657
3658 SCH_SCREEN* screen = getCurrentScreen();
3659 wxCHECK( screen, /* void */ );
3660
3661 if( elem.isActive )
3662 {
3663 SCH_NO_CONNECT* noConnect = new SCH_NO_CONNECT( elem.location + m_sheetOffset );
3664
3665 noConnect->SetFlags( IS_NEW );
3666 screen->Append( noConnect );
3667 }
3668}
3669
3670
3671void SCH_IO_ALTIUM::ParseNetLabel( const std::map<wxString, wxString>& aProperties )
3672{
3673 ASCH_NET_LABEL elem( aProperties );
3674
3675 SCH_LABEL* label = new SCH_LABEL( elem.location + m_sheetOffset, elem.text );
3676
3677 SCH_SCREEN* screen = getCurrentScreen();
3678 wxCHECK( screen, /* void */ );
3679
3680 SetTextPositioning( label, elem.justification, elem.orientation );
3681
3682 label->SetFlags( IS_NEW );
3683 screen->Append( label );
3684}
3685
3686
3687void SCH_IO_ALTIUM::ParseBus( const std::map<wxString, wxString>& aProperties )
3688{
3689 ASCH_BUS elem( aProperties );
3690
3691 SCH_SCREEN* screen = getCurrentScreen();
3692 wxCHECK( screen, /* void */ );
3693
3694 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
3695 {
3696 SCH_LINE* bus = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
3697 SCH_LAYER_ID::LAYER_BUS );
3698 bus->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
3699 bus->SetLineWidth( elem.lineWidth );
3700
3701 bus->SetFlags( IS_NEW );
3702 screen->Append( bus );
3703 }
3704}
3705
3706
3707void SCH_IO_ALTIUM::ParseWire( const std::map<wxString, wxString>& aProperties )
3708{
3709 ASCH_WIRE elem( aProperties );
3710
3711 SCH_SCREEN* screen = getCurrentScreen();
3712 wxCHECK( screen, /* void */ );
3713
3714 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
3715 {
3716 SCH_LINE* wire = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
3717 SCH_LAYER_ID::LAYER_WIRE );
3718 wire->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
3719 // wire->SetLineWidth( elem.lineWidth );
3720
3721 wire->SetFlags( IS_NEW );
3722 screen->Append( wire );
3723 }
3724}
3725
3726
3727void SCH_IO_ALTIUM::ParseJunction( const std::map<wxString, wxString>& aProperties )
3728{
3729 SCH_SCREEN* screen = getCurrentScreen();
3730 wxCHECK( screen, /* void */ );
3731
3732 ASCH_JUNCTION elem( aProperties );
3733
3734 SCH_JUNCTION* junction = new SCH_JUNCTION( elem.location + m_sheetOffset );
3735
3736 junction->SetFlags( IS_NEW );
3737 screen->Append( junction );
3738}
3739
3740
3741void SCH_IO_ALTIUM::ParseImage( const std::map<wxString, wxString>& aProperties )
3742{
3743 ASCH_IMAGE elem( aProperties );
3744
3745 const auto& component = m_altiumComponents.find( elem.ownerindex );
3746
3747 //Hide the image if it is owned by a component but the part id do not match
3748 if( component != m_altiumComponents.end()
3749 && component->second.currentpartid != elem.ownerpartid )
3750 return;
3751
3752 VECTOR2I center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
3753 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
3754 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
3755
3756 SCH_SCREEN* screen = getCurrentScreen();
3757 wxCHECK( screen, /* void */ );
3758
3759 if( elem.embedimage )
3760 {
3761 const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
3762
3763 if( !storageFile )
3764 {
3765 wxString msg = wxString::Format( _( "Embedded file %s not found in storage." ),
3766 elem.filename );
3768 return;
3769 }
3770
3771 wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
3772
3773 // As wxZlibInputStream is not seekable, we need to write a temporary file
3774 wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
3775 wxZlibInputStream zlibInputStream( fileStream );
3776 wxFFileOutputStream outputStream( storagePath );
3777 outputStream.Write( zlibInputStream );
3778 outputStream.Close();
3779
3780 if( !refImage.ReadImageFile( storagePath ) )
3781 {
3782 m_reporter->Report( wxString::Format( _( "Error reading image %s." ), storagePath ),
3784 return;
3785 }
3786
3787 // Remove temporary file
3788 wxRemoveFile( storagePath );
3789 }
3790 else
3791 {
3792 if( !wxFileExists( elem.filename ) )
3793 {
3794 m_reporter->Report( wxString::Format( _( "File not found %s." ), elem.filename ),
3796 return;
3797 }
3798
3799 if( !refImage.ReadImageFile( elem.filename ) )
3800 {
3801 m_reporter->Report( wxString::Format( _( "Error reading image %s." ), elem.filename ),
3803 return;
3804 }
3805 }
3806
3807 // we only support one scale, thus we need to select one in case it does not keep aspect ratio
3808 const VECTOR2I currentImageSize = refImage.GetSize();
3809 const VECTOR2I expectedImageSize = elem.location - elem.corner;
3810 const double scaleX =
3811 std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
3812 const double scaleY =
3813 std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
3814 refImage.SetImageScale( std::min( scaleX, scaleY ) );
3815
3816 bitmap->SetFlags( IS_NEW );
3817 screen->Append( bitmap.release() );
3818}
3819
3820
3821void SCH_IO_ALTIUM::ParseSheet( const std::map<wxString, wxString>& aProperties )
3822{
3823 m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
3824
3825 SCH_SCREEN* screen = getCurrentScreen();
3826 wxCHECK( screen, /* void */ );
3827
3828 PAGE_INFO pageInfo;
3829
3830 bool isPortrait = m_altiumSheet->sheetOrientation == ASCH_SHEET_WORKSPACEORIENTATION::PORTRAIT;
3831
3832 if( m_altiumSheet->useCustomSheet )
3833 {
3836 pageInfo.SetType( PAGE_INFO::Custom, isPortrait );
3837 }
3838 else
3839 {
3840 switch( m_altiumSheet->sheetSize )
3841 {
3842 default:
3843 case ASCH_SHEET_SIZE::A4: pageInfo.SetType( "A4", isPortrait ); break;
3844 case ASCH_SHEET_SIZE::A3: pageInfo.SetType( "A3", isPortrait ); break;
3845 case ASCH_SHEET_SIZE::A2: pageInfo.SetType( "A2", isPortrait ); break;
3846 case ASCH_SHEET_SIZE::A1: pageInfo.SetType( "A1", isPortrait ); break;
3847 case ASCH_SHEET_SIZE::A0: pageInfo.SetType( "A0", isPortrait ); break;
3848 case ASCH_SHEET_SIZE::A: pageInfo.SetType( "A", isPortrait ); break;
3849 case ASCH_SHEET_SIZE::B: pageInfo.SetType( "B", isPortrait ); break;
3850 case ASCH_SHEET_SIZE::C: pageInfo.SetType( "C", isPortrait ); break;
3851 case ASCH_SHEET_SIZE::D: pageInfo.SetType( "D", isPortrait ); break;
3852 case ASCH_SHEET_SIZE::E: pageInfo.SetType( "E", isPortrait ); break;
3853 case ASCH_SHEET_SIZE::LETTER: pageInfo.SetType( "USLetter", isPortrait ); break;
3854 case ASCH_SHEET_SIZE::LEGAL: pageInfo.SetType( "USLegal", isPortrait ); break;
3855 case ASCH_SHEET_SIZE::TABLOID: pageInfo.SetType( "A3", isPortrait ); break;
3856 case ASCH_SHEET_SIZE::ORCAD_A: pageInfo.SetType( "A", isPortrait ); break;
3857 case ASCH_SHEET_SIZE::ORCAD_B: pageInfo.SetType( "B", isPortrait ); break;
3858 case ASCH_SHEET_SIZE::ORCAD_C: pageInfo.SetType( "C", isPortrait ); break;
3859 case ASCH_SHEET_SIZE::ORCAD_D: pageInfo.SetType( "D", isPortrait ); break;
3860 case ASCH_SHEET_SIZE::ORCAD_E: pageInfo.SetType( "E", isPortrait ); break;
3861 }
3862 }
3863
3864 screen->SetPageSettings( pageInfo );
3865
3866 m_sheetOffset = { 0, pageInfo.GetHeightIU( schIUScale.IU_PER_MILS ) };
3867}
3868
3869
3870void SCH_IO_ALTIUM::ParseSheetName( const std::map<wxString, wxString>& aProperties )
3871{
3872 ASCH_SHEET_NAME elem( aProperties );
3873
3874 const auto& sheetIt = m_sheets.find( elem.ownerindex );
3875
3876 if( sheetIt == m_sheets.end() )
3877 {
3878 m_reporter->Report( wxString::Format( wxT( "Sheetname's owner (%d) not found." ),
3879 elem.ownerindex ),
3881 return;
3882 }
3883
3884 SCH_FIELD& sheetNameField = sheetIt->second->GetFields()[SHEETNAME];
3885
3886 sheetNameField.SetPosition( elem.location + m_sheetOffset );
3887 sheetNameField.SetText( elem.text );
3888 sheetNameField.SetVisible( !elem.isHidden );
3889 SetTextPositioning( &sheetNameField, ASCH_LABEL_JUSTIFICATION::BOTTOM_LEFT, elem.orientation );
3890}
3891
3892
3893void SCH_IO_ALTIUM::ParseFileName( const std::map<wxString, wxString>& aProperties )
3894{
3895 ASCH_FILE_NAME elem( aProperties );
3896
3897 const auto& sheetIt = m_sheets.find( elem.ownerindex );
3898
3899 if( sheetIt == m_sheets.end() )
3900 {
3901 m_reporter->Report( wxString::Format( wxT( "Filename's owner (%d) not found." ),
3902 elem.ownerindex ),
3904 return;
3905 }
3906
3907 SCH_FIELD& filenameField = sheetIt->second->GetFields()[SHEETFILENAME];
3908
3909 filenameField.SetPosition( elem.location + m_sheetOffset );
3910
3911 // Keep the filename of the Altium file until after the file is actually loaded.
3912 filenameField.SetText( elem.text );
3913 filenameField.SetVisible( !elem.isHidden );
3914 SetTextPositioning( &filenameField, ASCH_LABEL_JUSTIFICATION::BOTTOM_LEFT, elem.orientation );
3915}
3916
3917
3918void SCH_IO_ALTIUM::ParseDesignator( const std::map<wxString, wxString>& aProperties )
3919{
3920 ASCH_DESIGNATOR elem( aProperties );
3921
3922 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3923
3924 if( libSymbolIt == m_libSymbols.end() )
3925 {
3926 // TODO: e.g. can depend on Template (RECORD=39
3927 m_reporter->Report( wxString::Format( wxT( "Designator's owner (%d) not found." ),
3928 elem.ownerindex ),
3930 return;
3931 }
3932
3933 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
3934 SCH_SHEET_PATH sheetpath;
3935
3936 SCH_SCREEN* screen = getCurrentScreen();
3937 wxCHECK( screen, /* void */ );
3938
3939 // Graphics symbols have no reference. '#GRAPHIC' allows them to not have footprint associated.
3940 // Note: not all unnamed imported symbols are necessarily graphics.
3941 bool emptyRef = elem.text.IsEmpty();
3942 symbol->SetRef( &m_sheetPath, emptyRef ? wxString( wxS( "#GRAPHIC" ) ) : elem.text );
3943
3944 // I am not sure value and ref should be invisible just because emptyRef is true
3945 // I have examples with this criteria fully incorrect.
3946 bool visible = !emptyRef;
3947
3948 symbol->GetField( VALUE_FIELD )->SetVisible( visible );
3949 symbol->GetField( REFERENCE_FIELD )->SetVisible( visible );
3950
3951 SCH_FIELD* field = symbol->GetField( REFERENCE_FIELD );
3952 field->SetPosition( elem.location + m_sheetOffset );
3953 SetTextPositioning( field, elem.justification, elem.orientation );
3954}
3955
3956
3957void SCH_IO_ALTIUM::ParseLibDesignator( const std::map<wxString, wxString>& aProperties,
3958 std::vector<LIB_SYMBOL*>& aSymbol,
3959 std::vector<int>& aFontSizes )
3960{
3961 ASCH_DESIGNATOR elem( aProperties );
3962
3963 // Designators are shared by everyone
3964 for( LIB_SYMBOL* symbol : aSymbol )
3965 {
3966 bool emptyRef = elem.text.IsEmpty();
3967 SCH_FIELD& refField = symbol->GetReferenceField();
3968
3969 if( emptyRef )
3970 refField.SetText( wxT( "X" ) );
3971 else
3972 refField.SetText( elem.text.BeforeLast( '?' ) ); // remove the '?' at the end for KiCad-style
3973
3974 refField.SetPosition( elem.location );
3975
3976 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
3977 {
3978 int size = aFontSizes[elem.fontId - 1];
3979 refField.SetTextSize( { size, size } );
3980 }
3981 }
3982}
3983
3984
3985void SCH_IO_ALTIUM::ParseBusEntry( const std::map<wxString, wxString>& aProperties )
3986{
3987 ASCH_BUS_ENTRY elem( aProperties );
3988
3989 SCH_SCREEN* screen = getCurrentScreen();
3990 wxCHECK( screen, /* void */ );
3991
3992 SCH_BUS_WIRE_ENTRY* busWireEntry = new SCH_BUS_WIRE_ENTRY( elem.location + m_sheetOffset );
3993
3994 VECTOR2I vector = elem.corner - elem.location;
3995 busWireEntry->SetSize( { vector.x, vector.y } );
3996
3997 busWireEntry->SetFlags( IS_NEW );
3998 screen->Append( busWireEntry );
3999}
4000
4001
4002void SCH_IO_ALTIUM::ParseParameter( const std::map<wxString, wxString>& aProperties )
4003{
4004 ASCH_PARAMETER elem( aProperties );
4005
4006 // TODO: fill in replacements from variant, sheet and project
4007 static const std::map<wxString, wxString> variableMap = {
4008 { "COMMENT", "VALUE" },
4009 { "VALUE", "ALTIUM_VALUE" },
4010 };
4011
4012 if( elem.ownerindex <= 0 )
4013 {
4014 // This is some sheet parameter
4015 if( elem.text == "*" )
4016 return; // indicates parameter not set?
4017
4018 wxString paramName = elem.name.Upper();
4019
4020 if( paramName == "SHEETNUMBER" )
4021 {
4023 }
4024 else if( paramName == "TITLE" )
4025 {
4026 m_currentTitleBlock->SetTitle( elem.text );
4027 }
4028 else if( paramName == "REVISION" )
4029 {
4030 m_currentTitleBlock->SetRevision( elem.text );
4031 }
4032 else if( paramName == "DATE" )
4033 {
4034 m_currentTitleBlock->SetDate( elem.text );
4035 }
4036 else if( paramName == "COMPANYNAME" )
4037 {
4038 m_currentTitleBlock->SetCompany( elem.text );
4039 }
4040 else
4041 {
4042 m_schematic->Prj().GetTextVars()[ paramName ] = elem.text;
4043 }
4044 }
4045 else
4046 {
4047 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4048
4049 if( libSymbolIt == m_libSymbols.end() )
4050 {
4051 // TODO: e.g. can depend on Template (RECORD=39
4052 return;
4053 }
4054
4055 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4056 SCH_FIELD* field = nullptr;
4057 wxString upperName = elem.name.Upper();
4058
4059 if( upperName == "COMMENT" )
4060 {
4061 field = symbol->GetField( VALUE_FIELD );
4062 }
4063 else
4064 {
4065 int fieldIdx = 0;
4066 wxString fieldName = elem.name.Upper();
4067
4068 fieldIdx = symbol->GetFieldCount();
4069
4070 if( fieldName.IsEmpty() )
4071 {
4072 int disambiguate = 1;
4073
4074 while( 1 )
4075 {
4076 fieldName = wxString::Format( "ALTIUM_UNNAMED_%d", disambiguate++ );
4077
4078 if( !symbol->FindField( fieldName ) )
4079 break;
4080
4081 }
4082 }
4083 else if( fieldName == "VALUE" )
4084 {
4085 fieldName = "ALTIUM_VALUE";
4086 }
4087
4088 field = symbol->AddField( SCH_FIELD( VECTOR2I(), fieldIdx, symbol, fieldName ) );
4089 }
4090
4091 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
4092 field->SetText( kicadText );
4093 field->SetPosition( elem.location + m_sheetOffset );
4094 field->SetVisible( !elem.isHidden );
4095 SetTextPositioning( field, elem.justification, elem.orientation );
4096 }
4097}
4098
4099
4100void SCH_IO_ALTIUM::ParseLibParameter( const std::map<wxString, wxString>& aProperties,
4101 std::vector<LIB_SYMBOL*>& aSymbol,
4102 std::vector<int>& aFontSizes )
4103{
4104 ASCH_PARAMETER elem( aProperties );
4105
4106 // Part ID 1 is the current library part.
4107 // Part ID ALTIUM_COMPONENT_NONE(-1) means all parts
4108 // If a parameter is assigned to a specific element such as a pin,
4109 // we will need to handle it here.
4110 // TODO: Handle HIDDENNETNAME property (others?)
4111 if( elem.ownerpartid != 1 && elem.ownerpartid != ALTIUM_COMPONENT_NONE )
4112 return;
4113
4114 // If ownerindex is populated, this is parameter belongs to a subelement (e.g. pin).
4115 // Ignore for now.
4116 // TODO: Update this when KiCad supports parameters for any object
4117 if( elem.ownerindex != ALTIUM_COMPONENT_NONE )
4118 return;
4119
4120 // TODO: fill in replacements from variant, sheet and project
4121 // N.B. We do not keep the Altium "VALUE" variable here because
4122 // we don't have a way to assign variables to specific symbols
4123 std::map<wxString, wxString> variableMap = {
4124 { "COMMENT", "VALUE" },
4125 };
4126
4127 for( LIB_SYMBOL* libSymbol : aSymbol )
4128 {
4129 SCH_FIELD* field = nullptr;
4130 wxString upperName = elem.name.Upper();
4131
4132 if( upperName == "COMMENT" )
4133 {
4134 field = &libSymbol->GetValueField();
4135 }
4136 else
4137 {
4138 int fieldIdx = libSymbol->GetFieldCount();
4139 wxString fieldNameStem = elem.name;
4140 wxString fieldName = fieldNameStem;
4141 int disambiguate = 1;
4142
4143 if( fieldName.IsEmpty() )
4144 {
4145 fieldNameStem = "ALTIUM_UNNAMED";
4146 fieldName = "ALTIUM_UNNAMED_1";
4147 disambiguate = 2;
4148 }
4149 else if( upperName == "VALUE" )
4150 {
4151 fieldNameStem = "ALTIUM_VALUE";
4152 fieldName = "ALTIUM_VALUE";
4153 }
4154
4155 // Avoid adding duplicate fields
4156 while( libSymbol->FindField( fieldName ) )
4157 fieldName = wxString::Format( "%s_%d", fieldNameStem, disambiguate++ );
4158
4159 SCH_FIELD* new_field = new SCH_FIELD( libSymbol, fieldIdx, fieldName );
4160 libSymbol->AddField( new_field );
4161 field = new_field;
4162 }
4163
4164 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
4165 field->SetText( kicadText );
4166
4167 field->SetTextPos( elem.location );
4168 SetTextPositioning( field, elem.justification, elem.orientation );
4169 field->SetVisible( !elem.isHidden );
4170
4171 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
4172 {
4173 int size = aFontSizes[elem.fontId - 1];
4174 field->SetTextSize( { size, size } );
4175 }
4176 else
4177 {
4179 field->SetTextSize( { size, size } );
4180 }
4181
4182 }
4183}
4184
4185
4187 const std::map<wxString, wxString>& aProperties )
4188{
4189 ASCH_IMPLEMENTATION_LIST elem( aProperties );
4190
4191 m_altiumImplementationList.emplace( aIndex, elem.ownerindex );
4192}
4193
4194
4195void SCH_IO_ALTIUM::ParseImplementation( const std::map<wxString, wxString>& aProperties,
4196 std::vector<LIB_SYMBOL*>& aSymbol )
4197{
4198 ASCH_IMPLEMENTATION elem( aProperties );
4199
4200 if( elem.type != wxS( "PCBLIB" ) )
4201 return;
4202
4203 // For schematic files, we need to check if the model is current.
4204 if( aSymbol.size() == 0 && !elem.isCurrent )
4205 return;
4206
4207 // For IntLibs we want to use the same lib name for footprints
4208 wxString libName = m_isIntLib ? m_libName : elem.libname;
4209
4210 wxArrayString fpFilters;
4211 fpFilters.Add( wxString::Format( wxS( "*%s*" ), elem.name ) );
4212
4213 // Parse the footprint fields for the library symbol
4214 if( !aSymbol.empty() )
4215 {
4216 for( LIB_SYMBOL* symbol : aSymbol )
4217 {
4218 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
4219
4220 symbol->SetFPFilters( fpFilters );
4221 SCH_FIELD& footprintField = symbol->GetFootprintField();
4222 footprintField.SetText( fpLibId.Format() );
4223 }
4224
4225 return;
4226 }
4227
4228 const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex );
4229
4230 if( implementationOwnerIt == m_altiumImplementationList.end() )
4231 {
4232 m_reporter->Report( wxString::Format( wxT( "Implementation's owner (%d) not found." ),
4233 elem.ownerindex ),
4235 return;
4236 }
4237
4238 const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second );
4239
4240 if( libSymbolIt == m_libSymbols.end() )
4241 {
4242 m_reporter->Report( wxString::Format( wxT( "Footprint's owner (%d) not found." ),
4243 implementationOwnerIt->second ),
4245 return;
4246 }
4247
4248 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
4249
4250 libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it
4251
4252 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4253
4254 symbol->SetFootprintFieldText( fpLibId.Format() );
4255}
4256
4257
4258
4259std::vector<LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibComponent( const std::map<wxString,
4260 wxString>& aProperties )
4261{
4262 ASCH_SYMBOL elem( aProperties );
4263
4264 std::vector<LIB_SYMBOL*> symbols;
4265
4266 symbols.reserve( elem.displaymodecount );
4267
4268 for( int i = 0; i < elem.displaymodecount; i++ )
4269 {
4270 LIB_SYMBOL* symbol = new LIB_SYMBOL( wxEmptyString );
4271
4272 if( elem.displaymodecount > 1 )
4273 symbol->SetName( wxString::Format( "%s (Altium Display %d)", elem.libreference,
4274 i + 1 ) );
4275 else
4276 symbol->SetName( elem.libreference );
4277
4278 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symbol->GetName() );
4279 symbol->SetDescription( elem.componentdescription );
4280 symbol->SetLibId( libId );
4281 symbol->SetUnitCount( elem.partcount - 1 );
4282 symbols.push_back( symbol );
4283 }
4284
4285 return symbols;
4286}
4287
4288
4289std::map<wxString,LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibFile( const ALTIUM_COMPOUND_FILE& aAltiumLibFile )
4290{
4291 std::map<wxString,LIB_SYMBOL*> ret;
4292 std::vector<int> fontSizes;
4293 struct SYMBOL_PIN_FRAC
4294 {
4295 int x_frac;
4296 int y_frac;
4297 int len_frac;
4298 };
4299
4300 ParseLibHeader( aAltiumLibFile, fontSizes );
4301
4302 std::map<wxString, ALTIUM_SYMBOL_DATA> syms = aAltiumLibFile.GetLibSymbols( nullptr );
4303
4304 for( auto& [name, entry] : syms )
4305 {
4306
4307 std::map<int, SYMBOL_PIN_FRAC> pinFracs;
4308
4309 if( entry.m_pinsFrac )
4310 {
4311 auto parse_binary_pin_frac =
4312 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
4313 {
4314 std::map<wxString, wxString> result;
4315 ALTIUM_COMPRESSED_READER cmpreader( binaryData );
4316
4317 std::pair<int, std::string*> pinFracData = cmpreader.ReadCompressedString();
4318
4319 ALTIUM_BINARY_READER binreader( *pinFracData.second );
4320 SYMBOL_PIN_FRAC pinFrac;
4321
4322 pinFrac.x_frac = binreader.ReadInt32();
4323 pinFrac.y_frac = binreader.ReadInt32();
4324 pinFrac.len_frac = binreader.ReadInt32();
4325 pinFracs.insert( { pinFracData.first, pinFrac } );
4326
4327 return result;
4328 };
4329
4330 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_pinsFrac );
4331
4332 while( reader.GetRemainingBytes() > 0 )
4333 {
4334 reader.ReadProperties( parse_binary_pin_frac );
4335 }
4336 }
4337
4338 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_symbol );
4339 std::vector<LIB_SYMBOL*> symbols;
4340 int pin_index = 0;
4341
4342 if( reader.GetRemainingBytes() <= 0 )
4343 {
4344 THROW_IO_ERROR( "LibSymbol does not contain any data" );
4345 }
4346
4347 {
4348 std::map<wxString, wxString> properties = reader.ReadProperties();
4349 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
4350 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
4351
4352 if( record != ALTIUM_SCH_RECORD::COMPONENT )
4353 THROW_IO_ERROR( "LibSymbol does not start with COMPONENT record" );
4354
4355 symbols = ParseLibComponent( properties );
4356 }
4357
4358 auto handleBinaryPinLambda =
4359 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
4360 {
4361 std::map<wxString, wxString> result;
4362
4363 ALTIUM_BINARY_READER binreader( binaryData );
4364
4365 int32_t recordId = binreader.ReadInt32();
4366
4367 if( recordId != static_cast<int32_t>( ALTIUM_SCH_RECORD::PIN ) )
4368 THROW_IO_ERROR( "Binary record missing PIN record" );
4369
4370 result["RECORD"] = wxString::Format( "%d", recordId );
4371 binreader.ReadByte(); // unknown
4372 result["OWNERPARTID"] = wxString::Format( "%d", binreader.ReadInt16() );
4373 result["OWNERPARTDISPLAYMODE"] = wxString::Format( "%d", binreader.ReadByte() );
4374 result["SYMBOL_INNEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
4375 result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
4376 result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() );
4377 result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() );
4378 result["TEXT"] = binreader.ReadShortPascalString();
4379 binreader.ReadByte(); // unknown
4380 result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() );
4381 result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() );
4382 result["PINLENGTH"] = wxString::Format( "%d", binreader.ReadInt16() );
4383 result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() );
4384 result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() );
4385 result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() );
4386 result["NAME"] = binreader.ReadShortPascalString();
4387 result["DESIGNATOR"] = binreader.ReadShortPascalString();
4388 result["SWAPIDGROUP"] = binreader.ReadShortPascalString();
4389
4390
4391 if( auto it = pinFracs.find( pin_index ); it != pinFracs.end() )
4392 {
4393 result["LOCATION.X_FRAC"] = wxString::Format( "%d", it->second.x_frac );
4394 result["LOCATION.Y_FRAC"] = wxString::Format( "%d", it->second.y_frac );
4395 result["PINLENGTH_FRAC"] = wxString::Format( "%d", it->second.len_frac );
4396 }
4397
4398 std::string partSeq = binreader.ReadShortPascalString(); // This is 'part|&|seq'
4399 std::vector<std::string> partSeqSplit = split( partSeq, "|" );
4400
4401 if( partSeqSplit.size() == 3 )
4402 {
4403 result["PART"] = partSeqSplit[0];
4404 result["SEQ"] = partSeqSplit[2];
4405 }
4406
4407 return result;
4408 };
4409
4410 while( reader.GetRemainingBytes() > 0 )
4411 {
4412 std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryPinLambda );
4413
4414 if( properties.empty() )
4415 continue;
4416
4417 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
4418 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
4419
4420 switch( record )
4421 {
4422 case ALTIUM_SCH_RECORD::PIN:
4423 {
4424 ParsePin( properties, symbols );
4425 pin_index++;
4426 break;
4427 }
4428
4429 case ALTIUM_SCH_RECORD::LABEL: ParseLabel( properties, symbols, fontSizes ); break;
4430
4431 case ALTIUM_SCH_RECORD::BEZIER: ParseBezier( properties, symbols ); break;
4432
4433 case ALTIUM_SCH_RECORD::POLYLINE: ParsePolyline( properties, symbols ); break;
4434
4435 case ALTIUM_SCH_RECORD::POLYGON: ParsePolygon( properties, symbols ); break;
4436
4437 case ALTIUM_SCH_RECORD::ELLIPSE: ParseEllipse( properties, symbols ); break;
4438
4439 case ALTIUM_SCH_RECORD::PIECHART: ParsePieChart( properties, symbols ); break;
4440
4441 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: ParseRoundRectangle( properties, symbols ); break;
4442
4443 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: ParseEllipticalArc( properties, symbols ); break;
4444
4445 case ALTIUM_SCH_RECORD::ARC: ParseArc( properties, symbols ); break;
4446
4447 case ALTIUM_SCH_RECORD::LINE: ParseLine( properties, symbols ); break;
4448
4449 case ALTIUM_SCH_RECORD::RECTANGLE: ParseRectangle( properties, symbols ); break;
4450
4451 case ALTIUM_SCH_RECORD::DESIGNATOR: ParseLibDesignator( properties, symbols, fontSizes ); break;
4452
4453 case ALTIUM_SCH_RECORD::PARAMETER: ParseLibParameter( properties, symbols, fontSizes ); break;
4454
4455 case ALTIUM_SCH_RECORD::TEXT_FRAME: ParseTextFrame( properties, symbols, fontSizes ); break;
4456
4457 // Nothing for now. TODO: Figure out how implementation lists are generted in libs
4458 case ALTIUM_SCH_RECORD::IMPLEMENTATION_LIST: break;
4459
4460 case ALTIUM_SCH_RECORD::IMPLEMENTATION: ParseImplementation( properties, symbols ); break;
4461
4462 case ALTIUM_SCH_RECORD::IMPL_PARAMS: break;
4463
4464 case ALTIUM_SCH_RECORD::MAP_DEFINER_LIST: break;
4465 case ALTIUM_SCH_RECORD::MAP_DEFINER: break;
4466
4467 // TODO: add support for these. They are just drawn symbols, so we can probably hardcode
4468 case ALTIUM_SCH_RECORD::IEEE_SYMBOL: break;
4469
4470 // TODO: Hanlde images once libedit supports them
4471 case ALTIUM_SCH_RECORD::IMAGE: break;
4472
4473 default:
4474 m_reporter->Report( wxString::Format( _( "Unknown or unexpected record id %d found "
4475 "in %s." ),
4476 recordId, symbols[0]->GetName() ),
4478 break;
4479 }
4480 }
4481
4482 if( reader.HasParsingError() )
4483 THROW_IO_ERROR( "stream was not parsed correctly!" );
4484
4485 if( reader.GetRemainingBytes() != 0 )
4486 THROW_IO_ERROR( "stream is not fully parsed" );
4487
4488 for( size_t ii = 0; ii < symbols.size(); ii++ )
4489 {
4490 LIB_SYMBOL* symbol = symbols[ii];
4491 symbol->FixupDrawItems();
4492 fixupSymbolPinNameNumbers( symbol );
4493
4494 SCH_FIELD& valField = symbol->GetValueField();
4495
4496 if( valField.GetText().IsEmpty() )
4497 valField.SetText( name );
4498
4499 if( symbols.size() == 1 )
4500 ret[name] = symbol;
4501 else
4502 ret[wxString::Format( "%s (Altium Display %zd)", name, ii + 1 )] = symbol;
4503 }
4504 }
4505
4506 return ret;
4507}
4508
4509
4510long long SCH_IO_ALTIUM::getLibraryTimestamp( const wxString& aLibraryPath ) const
4511{
4512 wxFileName fn( aLibraryPath );
4513
4514 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
4515 return fn.GetModificationTime().GetValue().GetValue();
4516 else
4517 return wxDateTime( 0.0 ).GetValue().GetValue();
4518}
4519
4520
4521void SCH_IO_ALTIUM::ensureLoadedLibrary( const wxString& aLibraryPath,
4522 const std::map<std::string, UTF8>* aProperties )
4523{
4524 // Suppress font substitution warnings
4526
4527 if( m_libCache.count( aLibraryPath ) )
4528 {
4529 wxCHECK( m_timestamps.count( aLibraryPath ), /*void*/ );
4530
4531 if( m_timestamps.at( aLibraryPath ) == getLibraryTimestamp( aLibraryPath ) )
4532 return;
4533 }
4534
4535 std::vector<std::unique_ptr<ALTIUM_COMPOUND_FILE>> compoundFiles;
4536
4537 wxFileName fileName( aLibraryPath );
4538 m_libName = fileName.GetName();
4539
4540 try
4541 {
4542 if( aLibraryPath.Lower().EndsWith( wxS( ".schlib" ) ) )
4543 {
4544 m_isIntLib = false;
4545
4546 compoundFiles.push_back( std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
4547 }
4548 else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) )
4549 {
4550 m_isIntLib = true;
4551
4552 std::unique_ptr<ALTIUM_COMPOUND_FILE> intCom =
4553 std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath );
4554
4555 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> schLibFiles =
4556 intCom->EnumDir( L"SchLib" );
4557
4558 for( const auto& [schLibName, cfe] : schLibFiles )
4559 compoundFiles.push_back( intCom->DecodeIntLibStream( *cfe ) );
4560 }
4561
4562 std::map<wxString, LIB_SYMBOL*>& cacheMapRef = m_libCache[aLibraryPath];
4563
4564 for( auto& altiumSchFilePtr : compoundFiles )
4565 {
4566 std::map<wxString, LIB_SYMBOL*> parsed = ParseLibFile( *altiumSchFilePtr );
4567 cacheMapRef.insert( parsed.begin(), parsed.end() );
4568 }
4569
4570 m_timestamps[aLibraryPath] = getLibraryTimestamp( aLibraryPath );
4571 }
4572 catch( const CFB::CFBException& exception )
4573 {
4574 THROW_IO_ERROR( exception.what() );
4575 }
4576 catch( const std::exception& exc )
4577 {
4578 wxFAIL_MSG( wxString::Format( wxT( "Unhandled exception in Altium schematic parsers: %s." ),
4579 exc.what() ) );
4580 throw;
4581 }
4582}
4583
4584
4586 std::vector<int>& aFontSizes )
4587{
4588 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "FileHeader" } );
4589
4590 if( file == nullptr )
4591 THROW_IO_ERROR( "FileHeader not found" );
4592
4593 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
4594
4595 if( reader.GetRemainingBytes() <= 0 )
4596 {
4597 THROW_IO_ERROR( "FileHeader does not contain any data" );
4598 }
4599
4600 std::map<wxString, wxString> properties = reader.ReadProperties();
4601
4602 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
4603
4604 if( libtype.CmpNoCase( "Protel for Windows - Schematic Library Editor Binary File Version 5.0" ) )
4605 THROW_IO_ERROR( _( "Expected Altium Schematic Library file version 5.0" ) );
4606
4607 for( auto& [key, value] : properties )
4608 {
4609 wxString upperKey = key.Upper();
4610 wxString remaining;
4611
4612 if( upperKey.StartsWith( "SIZE", &remaining ) )
4613 {
4614 if( !remaining.empty() )
4615 {
4616 int ind = wxAtoi( remaining );
4617
4618 if( static_cast<int>( aFontSizes.size() ) < ind )
4619 aFontSizes.resize( ind );
4620
4621 // Altium stores in pt. 1 pt = 1/72 inch. 1 mil = 1/1000 inch.
4622 int scaled = schIUScale.MilsToIU( wxAtoi( value ) * 72.0 / 10.0 );
4623 aFontSizes[ind - 1] = scaled;
4624 }
4625 }
4626 }
4627
4628}
4629
4630
4631void SCH_IO_ALTIUM::doEnumerateSymbolLib( const wxString& aLibraryPath,
4632 const std::map<std::string, UTF8>* aProperties,
4633 std::function<void(const wxString&, LIB_SYMBOL*)> aInserter )
4634{
4635 ensureLoadedLibrary( aLibraryPath, aProperties );
4636
4637 bool powerSymbolsOnly = ( aProperties &&
4638 aProperties->find( SYMBOL_LIB_TABLE::PropPowerSymsOnly )
4639 != aProperties->end() );
4640
4641 auto it = m_libCache.find( aLibraryPath );
4642
4643 if( it != m_libCache.end() )
4644 {
4645 for( auto& [libnameStr, libSymbol] : it->second )
4646 {
4647 if( powerSymbolsOnly && !libSymbol->IsPower() )
4648 continue;
4649
4650 aInserter( libnameStr, libSymbol );
4651 }
4652 }
4653}
4654
4655
4656void SCH_IO_ALTIUM::EnumerateSymbolLib( wxArrayString& aSymbolNameList,
4657 const wxString& aLibraryPath,
4658 const std::map<std::string, UTF8>* aProperties )
4659{
4660 doEnumerateSymbolLib( aLibraryPath, aProperties,
4661 [&]( const wxString& aStr, LIB_SYMBOL* )
4662 {
4663 aSymbolNameList.Add( aStr );
4664 } );
4665}
4666
4667
4668void SCH_IO_ALTIUM::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
4669 const wxString& aLibraryPath,
4670 const std::map<std::string, UTF8>* aProperties )
4671{
4672 doEnumerateSymbolLib( aLibraryPath, aProperties,
4673 [&]( const wxString&, LIB_SYMBOL* aSymbol )
4674 {
4675 aSymbolList.emplace_back( aSymbol );
4676 } );
4677}
4678
4679
4680LIB_SYMBOL* SCH_IO_ALTIUM::LoadSymbol( const wxString& aLibraryPath,
4681 const wxString& aAliasName,
4682 const std::map<std::string, UTF8>* aProperties )
4683{
4684 ensureLoadedLibrary( aLibraryPath, aProperties );
4685
4686 auto it = m_libCache.find( aLibraryPath );
4687
4688 if( it != m_libCache.end() )
4689 {
4690 auto it2 = it->second.find( aAliasName );
4691
4692 if( it2 != it->second.end() )
4693 return it2->second;
4694 }
4695
4696 return nullptr;
4697}
int color
Definition: DXF_plotter.cpp:58
const char * name
Definition: DXF_plotter.cpp:57
ALTIUM_SCH_RECORD
ASCH_RECORD_ORIENTATION
const int ALTIUM_COMPONENT_NONE
ASCH_LABEL_JUSTIFICATION
ASCH_POLYLINE_LINESTYLE
ASCH_POWER_PORT_STYLE
wxString AltiumSchSpecialStringsToKiCadVariables(const wxString &aString, const std::map< wxString, wxString > &aOverrides)
wxString AltiumPinNamesToKiCad(wxString &aString)
LIB_ID AltiumToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
void TransformEllipseToBeziers(const ELLIPSE< T > &aEllipse, std::vector< BEZIER< T > > &aBeziers)
Transforms an ellipse or elliptical arc into a set of quadratic Bezier curves that approximate it.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
std::map< wxString, wxString > ReadProperties()
size_t GetRemainingBytes() const
std::map< wxString, wxString > ReadProperties(std::function< std::map< wxString, wxString >(const std::string &)> handleBinaryData=[](const std::string &) { return std::map< wxString, wxString >();})
std::string ReadShortPascalString()
std::map< wxString, ALTIUM_SYMBOL_DATA > GetLibSymbols(const CFB::COMPOUND_FILE_ENTRY *aStart) const
const CFB::COMPOUND_FILE_ENTRY * FindStream(const std::vector< std::string > &aStreamPath) const
std::pair< int, std::string * > ReadCompressedString()
static int ReadInt(const std::map< wxString, wxString > &aProps, const wxString &aKey, int aDefault)
static wxString ReadString(const std::map< wxString, wxString > &aProps, const wxString &aKey, const wxString &aDefault)
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
Generic cubic Bezier representation.
Definition: bezier_curves.h:95
double Sin() const
Definition: eda_angle.h:170
double Cos() const
Definition: eda_angle.h:189
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
void SetModified()
Definition: eda_item.cpp:67
const KIID m_Uuid
Definition: eda_item.h:489
void SetStartX(int x)
Definition: eda_shape.h:146
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:205
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:527
FILL_T GetFillMode() const
Definition: eda_shape.h:107
void SetEndY(int aY)
Definition: eda_shape.h:177
void SetLineStyle(const LINE_STYLE aStyle)
Definition: eda_shape.cpp:1783
void SetStartY(int y)
Definition: eda_shape.h:140
void SetFilled(bool aFlag)
Definition: eda_shape.h:101
int GetRadius() const
Definition: eda_shape.cpp:575
SHAPE_T GetShape() const
Definition: eda_shape.h:125
void SetFillColor(const COLOR4D &aColor)
Definition: eda_shape.h:112
void SetEndX(int aX)
Definition: eda_shape.h:183
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:474
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:134
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
COLOR4D GetFillColor() const
Definition: eda_shape.h:111
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:171
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:202
virtual int GetWidth() const
Definition: eda_shape.h:115
void SetWidth(int aWidth)
Definition: eda_shape.h:114
void SetFillMode(FILL_T aFill)
Definition: eda_shape.h:106
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:79
void SetTextColor(const COLOR4D &aColor)
Definition: eda_text.h:249
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:404
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:94
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:449
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:275
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition: eda_text.cpp:240
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:182
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:204
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition: eda_text.cpp:212
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:298
EE_TYPE OfType(KICAD_T aType) const
Definition: sch_rtree.h:238
This class was created to handle importing ellipses from other file formats that support them nativel...
Definition: ellipse.h:34
Used for text file output.
Definition: richio.h:478
const wxString & GetName() const
Return a brief hard coded name for this IO interface.
Definition: io_base.h:75
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
Definition: io_base.h:218
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Definition: io_base.cpp:69
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition: color4d.h:311
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition: color4d.h:398
COLOR4D & FromCSSRGBA(int aRed, int aGreen, int aBlue, double aAlpha=1.0)
Initialize the color from a RGBA value with 0-255 red/green/blue and 0-1 alpha.
Definition: color4d.cpp:577
Definition: kiid.h:49
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
UTF8 Format() const
Definition: lib_id.cpp:118
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition: lib_id.cpp:191
Define a library symbol object.
Definition: lib_symbol.h:78
void SetUnitCount(int aCount, bool aDuplicateDrawItems=true)
Set the units per symbol count.
SCH_FIELD & GetValueField() const
Return reference to the value field.
void FixupDrawItems()
This function finds the filled draw items that are covering up smaller draw items and replaces their ...
Definition: lib_symbol.cpp:729
void SetPower()
Definition: lib_symbol.cpp:405
wxString GetName() const override
Definition: lib_symbol.h:137