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 The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <algorithm>
22#include <map>
23#include <memory>
24#include <optional>
25#include <set>
26
27#include "altium_parser_sch.h"
28#include <io/io_utils.h>
34
35#include <progress_reporter.h>
36#include <schematic.h>
37#include <project_sch.h>
41
42#include <lib_id.h>
43#include <sch_pin.h>
44#include <sch_bitmap.h>
45#include <sch_bus_entry.h>
46#include <connection_graph.h>
47#include <sch_connection.h>
48#include <sch_symbol.h>
49#include <sch_junction.h>
50#include <sch_line.h>
51#include <sch_shape.h>
52#include <sch_no_connect.h>
53#include <sch_screen.h>
54#include <sch_label.h>
55#include <sch_sheet.h>
56#include <sch_sheet_pin.h>
57#include <sch_textbox.h>
58
59#include <bezier_curves.h>
60#include <compoundfilereader.h>
61#include <font/fontconfig.h>
62#include <reporter.h>
63#include <geometry/ellipse.h>
65#include <string_utils.h>
66#include <sch_edit_frame.h>
68#include <wx/log.h>
69#include <wx/dir.h>
70#include <wx/mstream.h>
71#include <wx/zstream.h>
72#include <wx/wfstream.h>
73#include <magic_enum.hpp>
74#include "sch_io_altium.h"
75
76
77// Harness port object itself does not contain color information about itself
78// It seems altium is drawing harness ports using these colors
79#define HARNESS_PORT_COLOR_DEFAULT_BACKGROUND COLOR4D( 0.92941176470588238, \
80 0.94901960784313721, \
81 0.98431372549019602, 1.0 )
82
83#define HARNESS_PORT_COLOR_DEFAULT_OUTLINE COLOR4D( 0.56078431372549020, \
84 0.61960784313725492, \
85 0.78823529411764703, 1.0 )
86
87
88// Pure label/name transforms shared with the QA suite; see AltiumWrapBusLabel and
89// AltiumDeriveSheetName below.
90wxString AltiumWrapBusLabel( const wxString& aText );
91wxString AltiumDeriveSheetName( const wxString& aFilename, const std::set<wxString>& aExistingNames );
92
93
94// Normalized parse-stack key so a cycle through a dotted relative or differently-cased path
95// still matches
96static wxString AltiumParseKey( const wxFileName& aFileName )
97{
98 wxFileName fn = aFileName;
99 fn.Normalize( wxPATH_NORM_ABSOLUTE | wxPATH_NORM_DOTS | wxPATH_NORM_TILDE | wxPATH_NORM_CASE );
100 return fn.GetFullPath();
101}
102
103
104// Ref-counted RAII entry in m_parsingFiles so a block holds every sibling member open and a
105// member cross-referencing a sibling or itself reads as recursion
107{
108 PARSE_FILE_GUARD( SCH_IO_ALTIUM* aOwner, const wxString& aKey ) :
109 PARSE_FILE_GUARD( aOwner, std::vector<wxString>{ aKey } )
110 {
111 }
112
113 PARSE_FILE_GUARD( SCH_IO_ALTIUM* aOwner, std::vector<wxString> aKeys ) :
114 m_owner( aOwner ), m_keys( std::move( aKeys ) )
115 {
116 for( const wxString& key : m_keys )
117 m_owner->m_parsingFiles[key]++;
118 }
119
121 {
122 for( const wxString& key : m_keys )
123 {
124 auto it = m_owner->m_parsingFiles.find( key );
125
126 if( it != m_owner->m_parsingFiles.end() && --it->second <= 0 )
127 m_owner->m_parsingFiles.erase( it );
128 }
129 }
130
133
135 std::vector<wxString> m_keys;
136};
137
138
139static const VECTOR2I GetRelativePosition( const VECTOR2I& aPosition, const SCH_SYMBOL* aSymbol )
140{
141 TRANSFORM t = aSymbol->GetTransform().InverseTransform();
142 return t.TransformCoordinate( aPosition - aSymbol->GetPosition() );
143}
144
145
146static COLOR4D GetColorFromInt( int color )
147{
148 int red = color & 0x0000FF;
149 int green = ( color & 0x00FF00 ) >> 8;
150 int blue = ( color & 0xFF0000 ) >> 16;
151
152 return COLOR4D().FromCSSRGBA( red, green, blue, 1.0 );
153}
154
155
167
168
169static void SetSchShapeLine( const ASCH_BORDER_INTERFACE& elem, SCH_SHAPE* shape )
170{
172 GetColorFromInt( elem.Color ) ) );
173}
174
175static void SetSchShapeFillAndColor( const ASCH_FILL_INTERFACE& elem, SCH_SHAPE* shape )
176{
177
178 if( !elem.IsSolid )
179 {
181 }
182 else
183 {
185 shape->SetFillColor( GetColorFromInt( elem.AreaColor ) );
186 }
187
188 // Fixup small circles that had their widths set to 0
189 if( shape->GetShape() == SHAPE_T::CIRCLE && shape->GetStroke().GetWidth() == 0
190 && shape->GetRadius() <= schIUScale.MilsToIU( 10 ) )
191 {
193 }
194}
195
196
197static void SetLibShapeLine( const ASCH_BORDER_INTERFACE& elem, SCH_SHAPE* shape,
198 ALTIUM_SCH_RECORD aType )
199{
200 COLOR4D default_color;
201 COLOR4D alt_default_color = COLOR4D( PUREBLUE ); // PUREBLUE is used for many objects, so if
202 // it is used, we will assume that it should
203 // blend with the others
204 STROKE_PARAMS stroke;
205 stroke.SetColor( GetColorFromInt( elem.Color ) );
207
208 switch( aType )
209 {
210 case ALTIUM_SCH_RECORD::ARC: default_color = COLOR4D( PUREBLUE ); break;
211 case ALTIUM_SCH_RECORD::BEZIER: default_color = COLOR4D( PURERED ); break;
212 case ALTIUM_SCH_RECORD::ELLIPSE: default_color = COLOR4D( PUREBLUE ); break;
213 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: default_color = COLOR4D( PUREBLUE ); break;
214 case ALTIUM_SCH_RECORD::LINE: default_color = COLOR4D( PUREBLUE ); break;
215 case ALTIUM_SCH_RECORD::POLYGON: default_color = COLOR4D( PUREBLUE ); break;
216 case ALTIUM_SCH_RECORD::POLYLINE: default_color = COLOR4D( BLACK ); break;
217 case ALTIUM_SCH_RECORD::RECTANGLE: default_color = COLOR4D( 0.5, 0, 0, 1.0 ); break;
218 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: default_color = COLOR4D( PUREBLUE ); break;
219 default: default_color = COLOR4D( PUREBLUE ); break;
220 }
221
222 if( stroke.GetColor() == default_color || stroke.GetColor() == alt_default_color )
224
225 // In Altium libraries, you cannot change the width of the pins. So, to match pin width,
226 // if the line width of other elements is the default pin width (10 mil), we set the width
227 // to the KiCad default pin width ( represented by 0 )
228 if( elem.LineWidth == 2540 )
229 stroke.SetWidth( 0 );
230 else
231 stroke.SetWidth( elem.LineWidth );
232
233 shape->SetStroke( stroke );
234}
235
237 ALTIUM_SCH_RECORD aType, int aStrokeColor )
238{
239 COLOR4D bgcolor = GetColorFromInt( elem.AreaColor );
240 COLOR4D default_bgcolor;
241
242 switch (aType)
243 {
245 default_bgcolor = GetColorFromInt( 11599871 ); // Light Yellow
246 break;
247 default:
248 default_bgcolor = GetColorFromInt( 12632256 ); // Grey
249 break;
250 }
251
252 if( elem.IsTransparent )
253 bgcolor = bgcolor.WithAlpha( 0.5 );
254
255 if( !elem.IsSolid )
256 {
258 }
259 else if( elem.AreaColor == aStrokeColor )
260 {
261 bgcolor = shape->GetStroke().GetColor();
262
264 }
265 else if( bgcolor.WithAlpha( 1.0 ) == default_bgcolor )
266 {
268 }
269 else
270 {
272 }
273
274 shape->SetFillColor( bgcolor );
275
276 if( elem.AreaColor == aStrokeColor
277 && shape->GetStroke().GetWidth() == schIUScale.MilsToIU( 1 ) )
278 {
279 STROKE_PARAMS stroke = shape->GetStroke();
280 stroke.SetWidth( -1 );
281 shape->SetStroke( stroke );
282 }
283
284 // Fixup small circles that had their widths set to 0
285 if( shape->GetShape() == SHAPE_T::CIRCLE && shape->GetStroke().GetWidth() == 0
286 && shape->GetRadius() <= schIUScale.MilsToIU( 10 ) )
287 {
289 }
290}
291
292
294 SCH_IO( wxS( "Altium" ) )
295{
296 m_isIntLib = false;
297 m_rootSheet = nullptr;
298 m_schematic = nullptr;
301
303}
304
305
307{
308 for( auto& [libName, lib] : m_libCache )
309 {
310 for( auto& [name, symbol] : lib )
311 delete symbol;
312 }
313}
314
315
317{
318 return 0;
319}
320
321
322bool SCH_IO_ALTIUM::isBinaryFile( const wxString& aFileName )
323{
324 // Compound File Binary Format header
326}
327
328
329bool SCH_IO_ALTIUM::isASCIIFile( const wxString& aFileName )
330{
331 // ASCII file format
332 return IO_UTILS::fileStartsWithPrefix( aFileName, wxS( "|HEADER=" ), false );
333}
334
335
336bool SCH_IO_ALTIUM::checkFileHeader( const wxString& aFileName )
337{
338 return isBinaryFile( aFileName ) || isASCIIFile( aFileName );
339}
340
341
342bool SCH_IO_ALTIUM::CanReadSchematicFile( const wxString& aFileName ) const
343{
344 if( !SCH_IO::CanReadSchematicFile( aFileName ) )
345 return false;
346
347 return checkFileHeader( aFileName );
348}
349
350
351bool SCH_IO_ALTIUM::CanReadLibrary( const wxString& aFileName ) const
352{
353 if( !SCH_IO::CanReadLibrary( aFileName ) )
354 return false;
355
356 return checkFileHeader( aFileName );
357}
358
359
361{
362 std::vector<SCH_PIN*> pins;
363
364 if( aSymbol->Type() == SCH_SYMBOL_T )
365 pins = static_cast<SCH_SYMBOL*>( aSymbol )->GetPins( nullptr );
366 else if( aSymbol->Type() == LIB_SYMBOL_T )
367 pins = static_cast<LIB_SYMBOL*>( aSymbol )->GetGraphicalPins( 0, 0 );
368
369
370 bool names_visible = false;
371 bool numbers_visible = false;
372
373 for( SCH_PIN* pin : pins )
374 {
375 if( pin->GetNameTextSize() > 0 && !pin->GetName().empty() )
376 names_visible = true;
377
378 if( pin->GetNumberTextSize() > 0 && !pin->GetNumber().empty() )
379 numbers_visible = true;
380 }
381
382 if( !names_visible )
383 {
384 for( SCH_PIN* pin : pins )
385 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
386
387 aSymbol->SetShowPinNames( false );
388 }
389
390 if( !numbers_visible )
391 {
392 for( SCH_PIN* pin : pins )
393 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
394
395 aSymbol->SetShowPinNumbers( false );
396 }
397}
398
399
401{
402 if( m_libName.IsEmpty() )
403 {
404 // Try to come up with a meaningful name
405 m_libName = m_schematic->Project().GetProjectName();
406
407 if( m_libName.IsEmpty() )
408 {
409 wxFileName fn( m_rootSheet->GetFileName() );
410 m_libName = fn.GetName();
411 }
412
413 if( m_libName.IsEmpty() )
414 m_libName = "noname";
415
416 m_libName += "-altium-import";
418 }
419
420 return m_libName;
421}
422
423
425{
426 wxFileName fn( m_schematic->Project().GetProjectPath(), getLibName(),
428
429 return fn;
430}
431
432
433SCH_SHEET* SCH_IO_ALTIUM::LoadSchematicProject( SCHEMATIC* aSchematic, const std::map<std::string, UTF8>* aProperties )
434{
435 int x = 1;
436 int y = 1;
437 int page = 1;
438
439 std::map<wxString, SCH_SHEET*> sheets;
440 wxFileName project( aProperties->at( "project_file" ) );
441
442 for( auto& [key, filestring] : *aProperties )
443 {
444 if( key.starts_with( "sch" ) )
445 m_projectDocuments.emplace_back( filestring );
446 }
447
448 for( auto& [ key, filestring] : *aProperties )
449 {
450 if( !key.starts_with( "sch" ) )
451 continue;
452
453 wxFileName fn( filestring );
454
455 // Check if this file was already loaded as a subsheet of another sheet.
456 // This can happen when the project file lists sheets in an order where a parent
457 // sheet is processed before its subsheets. We need to handle potential case
458 // differences in filenames (e.g., LVDS.SCHDOC vs LVDS.SchDoc).
459 // The sources are compared, as a sheet file can take a suffix its source does not have
460 if( std::ranges::any_of( m_parsedFiles,
461 [&]( const wxString& aParsed )
462 {
463 return aParsed.IsSameAs( AltiumParseKey( fn ), false );
464 } ) )
465 {
466 continue;
467 }
468
469 VECTOR2I pos = VECTOR2I( x * schIUScale.MilsToIU( 1000 ),
470 y * schIUScale.MilsToIU( 1000 ) );
471
472 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos );
473 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
474 sheet->SetScreen( screen );
475
476 // The file uuid becomes the top-level sheet uuid on load, so instance paths must start with it
477 sheet->SyncUuidToScreen();
478
479 wxFileName kicad_fn( claimSheetFile( fn.GetName() ) );
480
481 // Sheet uses relative filename, screen uses full path
482 sheet->SetFileName( kicad_fn.GetFullName() );
483 screen->SetFileName( kicad_fn.GetFullPath() );
484
485 wxCHECK2( sheet && screen, continue );
486
487 wxString pageNo = wxString::Format( wxT( "%d" ), page++ );
488
489 m_sheetPath.push_back( sheet.get() );
490
491 // Parse from the original Altium file location
492 ParseAltiumSch( fn.GetFullPath() );
493
494 // A parent read later shows this screen rather than a second copy, so the cleanup below finds both
495 m_loadedScreens.emplace( std::vector<wxString>{ AltiumParseKey( fn ) }, screen );
496
497 // Sheets created here won't have names set by ParseSheetName (which only applies
498 // to sheet symbols within a parent). Derive a name from the Altium filename.
499 if( sheet->GetName().Trim().empty() )
500 {
501 std::set<wxString> existingNames;
502
503 for( auto& [path, existing] : sheets )
504 existingNames.insert( existing->GetName() );
505
506 sheet->SetName( AltiumDeriveSheetName( fn.GetFullPath(), existingNames ) );
507 }
508
509 m_sheetPath.SetPageNumber( pageNo );
510 m_sheetPath.pop_back();
511
512 SCH_SCREEN* currentScreen = m_rootSheet->GetScreen();
513
514 wxCHECK2( currentScreen, continue );
515
516 sheet->SetParent( m_sheetPath.Last() );
517 SCH_SHEET* sheetPtr = sheet.release();
518 currentScreen->Append( sheetPtr );
519
520 sheets[AltiumParseKey( fn ).Lower()] = sheetPtr;
521
522 x += 2;
523
524 if( x > 10 ) // Start next row of sheets.
525 {
526 x = 1;
527 y += 2;
528 }
529 }
530
531 // If any of the sheets in the project is a subsheet, then remove the sheet from the root sheet.
532 // The root sheet only contains sheets that are not referenced by any other sheet in a
533 // pseudo-flat structure.
534 for( auto& [ sourceKey, sheet ] : sheets )
535 {
536 if( m_subsheetFiles.contains( sourceKey ) )
537 getCurrentScreen()->Remove( sheet );
538 }
539
540 return m_rootSheet;
541}
542
543
544SCH_SHEET* SCH_IO_ALTIUM::LoadSchematicFile( const wxString& aFileName, SCHEMATIC* aSchematic,
545 SCH_SHEET* aAppendToMe,
546 const std::map<std::string, UTF8>* aProperties )
547{
548 wxCHECK( ( !aFileName.IsEmpty() || !aProperties->empty() ) && aSchematic, nullptr );
549
550 wxFileName fileName( aFileName );
551 fileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
552 m_schematic = aSchematic;
553
554 // Importer may be reused so reset per-import state or freed pointers leak into the new hierarchy
555 m_parsingFiles.clear();
556 m_loadedScreens.clear();
557 m_missingScreens.clear();
558 m_projectDocuments.clear();
559 m_sheetFiles.clear();
560 m_parsedFiles.clear();
561 m_subsheetFiles.clear();
562 m_rootSheetFile.clear();
563 m_sheetSpecialStrings.clear();
564 m_unsetSheetParams.clear();
565 m_sheets.clear();
567 m_rootFilepath.clear();
568
569 // The root, or the first sheet of a project, is saved as the project's schematic
570 if( !m_schematic->Project().GetProjectName().IsEmpty() )
571 {
572 m_rootSheetFile = wxFileName( m_schematic->Project().GetProjectPath(), m_schematic->Project().GetProjectName(),
574 }
575
576 // Collect the font substitution warnings (RAII - automatically reset on scope exit)
578
579 // Delete on exception, if I own m_rootSheet, according to aAppendToMe
580 std::unique_ptr<SCH_SHEET> deleter( aAppendToMe ? nullptr : m_rootSheet );
581
582 if( aAppendToMe )
583 {
584 wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
585 m_rootSheet = aAppendToMe;
586
587 // The appended sheets must not take a file the host schematic already saves to
588 SCH_SCREENS hostScreens( aSchematic->Root() );
589
590 for( SCH_SCREEN* screen = hostScreens.GetFirst(); screen; screen = hostScreens.GetNext() )
591 {
592 wxFileName saved( screen->GetFileName() );
594 m_sheetFiles.insert( saved.GetFullPath().Lower() );
595 }
596 }
597 else
598 {
599 m_rootSheet = new SCH_SHEET( aSchematic );
600 m_rootSheet->SetFileName( fileName.GetFullPath() );
601
602 // For project imports (empty filename), the root sheet becomes the virtual root
603 // container. Don't call SetTopLevelSheets yet - that will happen after
604 // LoadSchematicProject populates the sheet hierarchy.
605 if( aFileName.empty() )
606 {
607 const_cast<KIID&>( m_rootSheet->m_Uuid ) = niluuid;
608 }
609 else
610 {
611 // For single-file imports, set as top-level sheet immediately and assign
612 // a placeholder page number that will be updated if we find a pageNumber record.
613 aSchematic->SetTopLevelSheets( { m_rootSheet } );
614
615 SCH_SHEET_PATH sheetpath;
616 sheetpath.push_back( m_rootSheet );
617 sheetpath.SetPageNumber( "#" );
618 }
619 }
620
621 if( !m_rootSheet->GetScreen() )
622 {
623 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
624 screen->SetFileName( aFileName );
625 m_rootSheet->SetScreen( screen );
626
627 // For single-file import, use the screen's UUID for the root sheet
628 if( !aFileName.empty() )
629 m_rootSheet->SyncUuidToScreen();
630 }
631
632 m_sheetPath.push_back( m_rootSheet );
633
634 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
635 wxCHECK( rootScreen, nullptr );
636
637 SCH_SHEET_INSTANCE sheetInstance;
638
639 sheetInstance.m_Path = m_sheetPath.Path();
640 sheetInstance.m_PageNumber = wxT( "#" );
641
642 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
643
644 if( aFileName.empty() )
645 LoadSchematicProject( aSchematic, aProperties );
646 else
647 ParseAltiumSch( aFileName );
648
649 if( aFileName.empty() )
650 {
651 std::vector<SCH_SHEET*> topLevelSheets;
652
653 for( SCH_ITEM* item : rootScreen->Items().OfType( SCH_SHEET_T ) )
654 {
655 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
656
657 // Skip the temporary root sheet itself if it somehow ended up in its own screen
658 if( sheet != m_rootSheet )
659 topLevelSheets.push_back( sheet );
660 }
661
662 // Remove sheets from the temporary root screen before transferring ownership
663 // to the schematic. Otherwise the screen destructor will delete them.
664 for( SCH_SHEET* sheet : topLevelSheets )
665 rootScreen->Remove( sheet );
666
667 if( !topLevelSheets.empty() )
668 aSchematic->SetTopLevelSheets( topLevelSheets );
669
670 // Convert hierarchical labels to global labels on top-level sheets.
671 // Top-level sheets have no parent, so hierarchical labels don't make sense.
672 for( SCH_SHEET* sheet : topLevelSheets )
673 {
674 SCH_SCREEN* screen = sheet->GetScreen();
675
676 if( !screen )
677 continue;
678
679 std::vector<SCH_HIERLABEL*> hierLabels;
680
681 for( SCH_ITEM* item : screen->Items().OfType( SCH_HIER_LABEL_T ) )
682 hierLabels.push_back( static_cast<SCH_HIERLABEL*>( item ) );
683
684 for( SCH_HIERLABEL* hierLabel : hierLabels )
685 {
686 SCH_GLOBALLABEL* globalLabel = new SCH_GLOBALLABEL( hierLabel->GetPosition(),
687 hierLabel->GetText() );
688 globalLabel->SetShape( hierLabel->GetShape() );
689 globalLabel->SetSpinStyle( hierLabel->GetSpinStyle() );
690 globalLabel->GetField( FIELD_T::INTERSHEET_REFS )->SetVisible( false );
691
692 screen->Remove( hierLabel );
693 screen->Append( globalLabel );
694 delete hierLabel;
695 }
696 }
697
698 m_rootSheet = &aSchematic->Root();
699 }
700
701 if( !aAppendToMe )
703
704 if( m_reporter )
705 {
706 for( auto& [msg, severity] : m_errorMessages )
707 m_reporter->Report( msg, severity );
708 }
709
710 m_errorMessages.clear();
711
712 if( aProperties && aProperties->count( "project_file" ) )
713 {
715 ParseAltiumProjectParameters( aProperties->at( "project_file" ) ) );
716 }
717
718 // anything still undefined has no value, which Altium shows as nothing, and a sheet parameter that
719 // Altium leaves as "*" is undefined unless the project gives it a value
720 for( const wxString& name : m_sheetSpecialStrings )
721 m_schematic->Project().GetTextVars().emplace( name, wxString() );
722
723 for( const wxString& name : m_unsetSheetParams )
724 m_schematic->Project().GetTextVars().emplace( name, wxString() );
725
726 SCH_SCREENS allSheets( m_rootSheet );
727
728 // a Comment naming a parameter the component lacks shows empty in Altium
729 std::map<SCH_SYMBOL*, bool> resolves;
730
732 {
733 SCH_SCREEN* screen = path.LastScreen();
734
735 if( !screen )
736 continue;
737
738 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
739 {
740 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
741 const wxString& text = symbol->GetField( FIELD_T::VALUE )->GetText();
742
743 // only a Comment that is nothing but one variable reference
744 if( !text.StartsWith( wxS( "${" ) ) || !text.EndsWith( wxS( "}" ) )
745 || text.find( '}', 2 ) != text.length() - 1 || text.find( wxS( "${" ), 2 ) != wxString::npos )
746 {
747 continue;
748 }
749
750 wxString name = text.Mid( 2, text.length() - 3 );
751 bool known = m_schematic->Project().GetTextVars().count( name ) > 0;
752
753 for( const SCH_FIELD& field : symbol->GetFields() )
754 known |= field.GetName().IsSameAs( name, false );
755
756 known |= symbol->ResolveTextVar( &path, &name, wxEmptyString );
757 resolves[symbol] |= known;
758 }
759 }
760
761 for( const auto& [symbol, known] : resolves )
762 {
763 if( !known )
764 symbol->GetField( FIELD_T::VALUE )->SetText( wxEmptyString );
765 }
766
767 // symbols carry their own definitions, so a library that lacks them is not an error worth reporting
768 allSheets.UpdateSymbolLinks();
769 allSheets.ClearEditFlags();
770
771 // Apply Altium project variants to schematic symbols
772 if( aProperties && aProperties->count( "project_file" ) )
773 {
774 const wxString& projectFile = aProperties->at( "project_file" );
775
776 auto variants = ParseAltiumProjectVariants( projectFile );
777
778 if( !variants.empty() )
779 {
780 // Build lookups keyed by both UniqueId and designator. UniqueId is preferred
781 // because repeated-channel designs can have multiple components sharing a
782 // designator but with distinct UniqueIds.
783 using ENTRY_LIST =
784 std::vector<std::pair<wxString, const ALTIUM_VARIANT_ENTRY*>>;
785
786 std::map<wxString, ENTRY_LIST> variantsByUid;
787 std::map<wxString, ENTRY_LIST> variantsByDesignator;
788
789 for( const ALTIUM_PROJECT_VARIANT& pv : variants )
790 {
791 m_schematic->AddVariant( pv.name );
792
793 if( !pv.description.empty() && pv.description != pv.name )
794 m_schematic->SetVariantDescription( pv.name, pv.description );
795
796 for( const ALTIUM_VARIANT_ENTRY& entry : pv.variations )
797 {
798 if( !entry.uniqueId.empty() )
799 variantsByUid[entry.uniqueId].push_back( { pv.name, &entry } );
800
801 variantsByDesignator[entry.designator].push_back( { pv.name, &entry } );
802 }
803 }
804
805 SCH_SHEET_LIST sheetList( m_rootSheet );
806
807 for( const SCH_SHEET_PATH& path : sheetList )
808 {
809 SCH_SCREEN* screen = path.LastScreen();
810
811 if( !screen )
812 continue;
813
814 for( SCH_ITEM* item : screen->Items().OfType( SCH_SYMBOL_T ) )
815 {
816 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
817
818 ENTRY_LIST applicable;
819
820 const ENTRY_LIST* uidEntries = nullptr;
821 auto symUidIt = m_altiumSymbolToUid.find( symbol );
822
823 if( symUidIt != m_altiumSymbolToUid.end() )
824 {
825 auto varIt = variantsByUid.find( symUidIt->second );
826
827 if( varIt != variantsByUid.end() )
828 uidEntries = &varIt->second;
829 }
830
831 if( uidEntries && uidEntries->size() == 1 )
832 {
833 applicable = *uidEntries;
834 }
835 else if( uidEntries )
836 {
837 // A unique id shared by several variations (repeated channels) is
838 // ambiguous; disambiguate with the per-channel designator.
839 wxString ref = symbol->GetRef( &path );
840
841 for( const auto& namedEntry : *uidEntries )
842 {
843 if( namedEntry.second->designator == ref )
844 applicable.push_back( namedEntry );
845 }
846 }
847 else
848 {
849 wxString ref = symbol->GetRef( &path );
850 auto varIt = variantsByDesignator.find( ref );
851
852 if( varIt != variantsByDesignator.end() )
853 applicable = varIt->second;
854 }
855
856 if( applicable.empty() )
857 continue;
858
859 for( const auto& [variantName, entry] : applicable )
860 {
861 SCH_SYMBOL_VARIANT variant( variantName );
862 variant.InitializeAttributes( *symbol );
863
864 if( entry->kind == 1 )
865 {
866 variant.m_DNP = true;
867 variant.m_ExcludedFromBOM = true;
868 variant.m_ExcludedFromPosFiles = true;
869 }
870 else if( entry->kind == 0 )
871 {
872 for( const auto& [key, value] : entry->alternateFields )
873 {
874 if( key.CmpNoCase( wxS( "LibReference" ) ) == 0 )
875 variant.m_Fields[wxS( "Value" )] = value;
876 else if( key.CmpNoCase( wxS( "Description" ) ) == 0 )
877 variant.m_Fields[wxS( "Description" )] = value;
878 else if( key.CmpNoCase( wxS( "Footprint" ) ) == 0 )
879 variant.m_Fields[wxS( "Footprint" )] = value;
880 }
881 }
882
883 symbol->AddVariant( path, variant );
884 }
885 }
886 }
887 }
888 }
889
890 if( aProperties && aProperties->count( "project_file" )
891 && !AltiumPowerPortNamesTakePriority( aProperties->at( "project_file" ) ) )
892 {
894 }
895
896 // Set up the default netclass wire & bus width based on imported wires & buses.
897 //
898
899 int minWireWidth = std::numeric_limits<int>::max();
900 int minBusWidth = std::numeric_limits<int>::max();
901
902 for( SCH_SCREEN* screen = allSheets.GetFirst(); screen != nullptr; screen = allSheets.GetNext() )
903 {
904 std::vector<SCH_MARKER*> markers;
905
906 for( SCH_ITEM* item : screen->Items().OfType( SCH_LINE_T ) )
907 {
908 SCH_LINE* line = static_cast<SCH_LINE*>( item );
909
910 if( line->IsWire() && line->GetLineWidth() > 0 )
911 minWireWidth = std::min( minWireWidth, line->GetLineWidth() );
912
913 if( line->IsBus() && line->GetLineWidth() > 0 )
914 minBusWidth = std::min( minBusWidth, line->GetLineWidth() );
915 }
916 }
917
918 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Project().GetProjectFile().NetSettings();
919
920 if( minWireWidth < std::numeric_limits<int>::max() )
921 netSettings->GetDefaultNetclass()->SetWireWidth( minWireWidth );
922
923 if( minBusWidth < std::numeric_limits<int>::max() )
924 netSettings->GetDefaultNetclass()->SetBusWidth( minBusWidth );
925
926 return m_rootSheet;
927}
928
929
931{
932 return m_sheetPath.LastScreen();
933}
934
935
940
941
943{
944 SCH_SCREEN* screen = getCurrentScreen();
945 wxCHECK( screen, /* void */ );
946
947 std::vector<SCH_LINE*> busLines;
948 std::map<VECTOR2I, std::vector<SCH_LINE*>> busLineMap;
949
950 for( SCH_ITEM* elem : screen->Items().OfType( SCH_LINE_T) )
951 {
952 SCH_LINE* line = static_cast<SCH_LINE*>( elem );
953
954 if( line->IsBus() )
955 {
956 busLines.push_back( line );
957 busLineMap[ line->GetStartPoint() ].push_back( line );
958 busLineMap[ line->GetEndPoint() ].push_back( line );
959 }
960 }
961
962 std::function<SCH_LABEL*(VECTOR2I, std::set<SCH_LINE*>&)> walkBusLine =
963 [&]( const VECTOR2I& aStart, std::set<SCH_LINE*>& aVisited ) -> SCH_LABEL*
964 {
965 auto it = busLineMap.find( aStart );
966
967 if( it == busLineMap.end() )
968 return nullptr;
969
970 for( SCH_LINE* line : it->second )
971 {
972 // Skip lines we've already checked to avoid cycles
973 if( aVisited.count( line ) )
974 continue;
975
976 aVisited.insert( line );
977
978 for( SCH_ITEM* elem : screen->Items().Overlapping( SCH_LABEL_T, line->GetBoundingBox() ) )
979 {
980 SCH_LABEL* label = static_cast<SCH_LABEL*>( elem );
981
982 if( line->HitTest( label->GetPosition() ) )
983 return label;
984 }
985
986 SCH_LABEL* result = walkBusLine( KIGEOM::GetOtherEnd( line->GetSeg(), aStart ), aVisited );
987
988 if( result )
989 return result;
990 }
991
992 return nullptr;
993 };
994
995 for( auto& [_, harness] : m_altiumHarnesses )
996 {
997 std::shared_ptr<BUS_ALIAS> alias = std::make_shared<BUS_ALIAS>();
998 alias->SetName( harness.m_name );
999
1000 for( HARNESS::HARNESS_PORT& port : harness.m_ports )
1001 alias->AddMember( port.m_name );
1002
1003 screen->AddBusAlias( alias );
1004
1005 VECTOR2I pos;
1006 BOX2I box( harness.m_location, harness.m_size );
1007 SCH_LINE* busLine = nullptr;
1008
1009 for( SCH_ITEM* elem : screen->Items().Overlapping( SCH_LINE_T, box ) )
1010 {
1011 SCH_LINE* line = static_cast<SCH_LINE*>( elem );
1012
1013 if( !line->IsBus() )
1014 continue;
1015
1016 busLine = line;
1017
1018 for( const VECTOR2I& pt : line->GetConnectionPoints() )
1019 {
1020 if( box.Contains( pt ) )
1021 {
1022 pos = pt;
1023 break;
1024 }
1025 }
1026 }
1027
1028 if( !busLine )
1029 {
1030 for( SCH_ITEM* elem : screen->Items().Overlapping( SCH_HIER_LABEL_T, box ) )
1031 {
1032 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( elem );
1033
1034 pos = label->GetPosition();
1035 VECTOR2I center = box.GetCenter();
1036 int delta_x = center.x - pos.x;
1037 int delta_y = center.y - pos.y;
1038
1039 if( std::abs( delta_x ) > std::abs( delta_y ) )
1040 {
1041 busLine = new SCH_LINE( pos, SCH_LAYER_ID::LAYER_BUS );
1042 busLine->SetEndPoint( VECTOR2I( center.x, pos.y ) );
1043 busLine->SetFlags( IS_NEW );
1044 screen->Append( busLine );
1045 }
1046 else
1047 {
1048 busLine = new SCH_LINE( pos, SCH_LAYER_ID::LAYER_BUS );
1049 busLine->SetEndPoint( VECTOR2I( pos.x, center.y ) );
1050 busLine->SetFlags( IS_NEW );
1051 screen->Append( busLine );
1052 }
1053
1054 break;
1055 }
1056 }
1057
1058 if( !busLine )
1059 continue;
1060
1061 std::set<SCH_LINE*> visited;
1062 SCH_LABEL* label = walkBusLine( pos, visited );
1063
1064 // Altium supports two different naming conventions for harnesses. If there is a specific
1065 // harness name, then the nets inside the harness will be named harnessname.netname.
1066 // However, if there is no harness name, the nets will be named just netname.
1067
1068 // KiCad bus labels need some special handling to be recognized as bus labels
1069 if( label && !label->GetText().StartsWith( wxT( "{" ) ) )
1070 label->SetText( label->GetText() + wxT( "{" ) + harness.m_name + wxT( "}" ) );
1071
1072 if( !label )
1073 {
1074 label = new SCH_LABEL( busLine->GetStartPoint(), wxT( "{" ) + harness.m_name + wxT( "}" ) );
1076
1077 if( busLine->GetEndPoint().x < busLine->GetStartPoint().x )
1079 else
1081
1082 screen->Append( label );
1083 }
1084
1085 if( harness.m_ports.empty() )
1086 continue;
1087
1088 // Draw the bus line from the individual ports to the harness
1089
1090 bool isVertical = true;
1091
1092 if( harness.m_ports.size() > 1 )
1093 {
1094 VECTOR2I first = harness.m_ports.front().m_location;
1095 VECTOR2I last = harness.m_ports.back().m_location;
1096
1097 if( first.y == last.y )
1098 isVertical = false;
1099 }
1100
1101 if( isVertical )
1102 {
1103 VECTOR2I bottom = harness.m_ports.front().m_entryLocation;
1104 VECTOR2I top = harness.m_ports.front().m_entryLocation;
1105 int delta_space = EDA_UNIT_UTILS::Mils2IU( schIUScale, 100 );
1106
1107 for( HARNESS::HARNESS_PORT& port : harness.m_ports )
1108 {
1109 if( port.m_entryLocation.y > bottom.y )
1110 bottom = port.m_entryLocation;
1111
1112 if( port.m_entryLocation.y < top.y )
1113 top = port.m_entryLocation;
1114 }
1115
1116 VECTOR2I last_pt;
1117 SCH_LINE* line = new SCH_LINE( bottom, SCH_LAYER_ID::LAYER_BUS );
1118 line->SetStartPoint( bottom );
1119 line->SetEndPoint( top );
1120 line->SetLineWidth( busLine->GetLineWidth() );
1121 line->SetLineColor( busLine->GetLineColor() );
1122 screen->Append( line );
1123
1124 last_pt = ( busLine->GetStartPoint() - line->GetEndPoint() ).SquaredEuclideanNorm() <
1125 ( busLine->GetStartPoint() - line->GetStartPoint() ).SquaredEuclideanNorm()
1126 ? line->GetEndPoint()
1127 : line->GetStartPoint();
1128
1129 // If the busline is not on the save y coordinate as the bus/wire connectors, add a short
1130 // hop to bring the bus down to the level of the connectors
1131 if( last_pt.y != busLine->GetStartPoint().y )
1132 {
1133 line = new SCH_LINE( last_pt, SCH_LAYER_ID::LAYER_BUS );
1134 line->SetStartPoint( last_pt );
1135
1136 if( alg::signbit( busLine->GetStartPoint().x - last_pt.x ) )
1137 line->SetEndPoint( last_pt + VECTOR2I( -delta_space, 0 ) );
1138 else
1139 line->SetEndPoint( last_pt + VECTOR2I( delta_space, 0 ) );
1140
1141 line->SetLineWidth( busLine->GetLineWidth() );
1142 line->SetLineColor( busLine->GetLineColor() );
1143 screen->Append( line );
1144 last_pt = line->GetEndPoint();
1145
1146 line = new SCH_LINE( last_pt, SCH_LAYER_ID::LAYER_BUS );
1147 line->SetStartPoint( last_pt );
1148 line->SetEndPoint( last_pt + VECTOR2I( 0, busLine->GetStartPoint().y - last_pt.y ) );
1149 line->SetLineWidth( busLine->GetLineWidth() );
1150 line->SetLineColor( busLine->GetLineColor() );
1151 screen->Append( line );
1152 last_pt = line->GetEndPoint();
1153 }
1154
1155 line = new SCH_LINE( last_pt, SCH_LAYER_ID::LAYER_BUS );
1156 line->SetStartPoint( last_pt );
1157 line->SetEndPoint( busLine->GetStartPoint() );
1158 line->SetLineWidth( busLine->GetLineWidth() );
1159 line->SetLineColor( busLine->GetLineColor() );
1160 screen->Append( line );
1161 }
1162 }
1163}
1164
1165
1166wxString AltiumWrapBusLabel( const wxString& aText )
1167{
1168 // Altium marks bus membership by geometry alone (a scalar label placed on a bus line);
1169 // KiCad needs the same label expressed as a single-member bus group to make the
1170 // connection. Already-formatted bus labels are left untouched.
1171 if( SCH_CONNECTION::IsBusLabel( aText ) )
1172 return aText;
1173
1174 // Spaces and commas separate members inside a bus group, so a multi-word net name would
1175 // otherwise fan out into several members. Quote such names so the bus-group reader in
1176 // NET_SETTINGS::ParseBusGroup keeps the whole name as a single member.
1177 if( aText.Contains( wxT( " " ) ) || aText.Contains( wxT( "," ) ) )
1178 return wxT( "{\"" ) + EscapeString( aText, CTX_QUOTED_STR ) + wxT( "\"}" );
1179
1180 return wxT( "{" ) + aText + wxT( "}" );
1181}
1182
1183
1184wxString AltiumDeriveSheetName( const wxString& aFilename, const std::set<wxString>& aExistingNames )
1185{
1186 wxString baseName = wxFileName( aFilename ).GetName();
1187 baseName.Replace( wxT( "/" ), wxT( "_" ) );
1188
1189 if( baseName.Trim().empty() )
1190 baseName = wxT( "Sheet" );
1191
1192 wxString sheetName = baseName;
1193
1194 for( int ii = 1; aExistingNames.count( sheetName ); ++ii )
1195 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
1196
1197 return sheetName;
1198}
1199
1200
1202{
1203 SCH_SCREEN* screen = getCurrentScreen();
1204 wxCHECK( screen, /* void */ );
1205
1206 bool hasBusLines = false;
1207
1208 for( SCH_ITEM* item : screen->Items().OfType( SCH_LINE_T ) )
1209 {
1210 SCH_LINE* line = static_cast<SCH_LINE*>( item );
1211
1212 if( line->IsBus() )
1213 {
1214 hasBusLines = true;
1215 break;
1216 }
1217 }
1218
1219 if( !hasBusLines )
1220 return;
1221
1222 // Collect labels that need wrapping, then modify them after iteration to avoid
1223 // modifying the R-tree during traversal.
1224 std::vector<SCH_LABEL*> labelsToWrap;
1225
1226 for( SCH_ITEM* item : screen->Items().OfType( SCH_LABEL_T ) )
1227 {
1228 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
1229
1230 if( SCH_CONNECTION::IsBusLabel( label->GetText() ) )
1231 continue;
1232
1233 for( SCH_ITEM* busItem : screen->Items().Overlapping( SCH_LINE_T, label->GetBoundingBox() ) )
1234 {
1235 SCH_LINE* busLine = static_cast<SCH_LINE*>( busItem );
1236
1237 if( busLine->IsBus() && busLine->HitTest( label->GetPosition() ) )
1238 {
1239 labelsToWrap.push_back( label );
1240 break;
1241 }
1242 }
1243 }
1244
1245 for( SCH_LABEL* label : labelsToWrap )
1246 label->SetText( AltiumWrapBusLabel( label->GetText() ) );
1247}
1248
1249
1251{
1252 SCH_SCREEN* screen = getCurrentScreen();
1253 wxCHECK( screen, /* void */ );
1254
1255 std::set<wxString> existingNames;
1256
1257 for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
1258 {
1259 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1260
1261 if( !sheet->GetName().Trim().empty() )
1262 existingNames.insert( sheet->GetName() );
1263 }
1264
1265 for( auto& [idx, sheet] : m_sheets )
1266 {
1267 if( !sheet->GetName().Trim().empty() )
1268 continue;
1269
1270 SCH_FIELD* filenameField = sheet->GetField( FIELD_T::SHEET_FILENAME );
1271 wxString filename = filenameField ? filenameField->GetText() : wxString();
1272 wxString sheetName = AltiumDeriveSheetName( filename, existingNames );
1273
1274 sheet->SetName( sheetName );
1275 existingNames.insert( sheetName );
1276 }
1277}
1278
1279
1280wxFileName SCH_IO_ALTIUM::ResolveSheetFileName( const wxString& aParentPath,
1281 const wxString& aSheetFileName ) const
1282{
1283 wxString sheetFileName = aSheetFileName;
1284 sheetFileName.Replace( wxS( "\\" ), wxS( "/" ) );
1285
1286 wxFileName loadAltiumFileName( sheetFileName );
1287 loadAltiumFileName.MakeAbsolute( aParentPath );
1288
1289 if( loadAltiumFileName.IsFileReadable() )
1290 return loadAltiumFileName;
1291
1292 if( !loadAltiumFileName.HasExt() )
1293 {
1294 wxFileName withExt( loadAltiumFileName );
1295 withExt.SetExt( wxT( "SchDoc" ) );
1296
1297 if( withExt.IsFileReadable() )
1298 return withExt;
1299 }
1300
1301 bool extensionless = !loadAltiumFileName.HasExt();
1302
1303 wxArrayString files;
1304 wxDir::GetAllFiles( loadAltiumFileName.GetPath(), &files, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN );
1305
1306 for( const wxString& candidate : files )
1307 {
1308 wxFileName candidateFname( candidate );
1309
1310 if( candidateFname.GetFullName().IsSameAs( loadAltiumFileName.GetFullName(), false )
1311 || ( extensionless
1312 && !loadAltiumFileName.GetName().empty()
1313 && candidateFname.GetName().IsSameAs( loadAltiumFileName.GetName(), false )
1314 && candidateFname.GetExt().IsSameAs( wxT( "SchDoc" ), false ) ) )
1315 {
1316 return candidateFname;
1317 }
1318 }
1319
1320 // Altium finds a sheet symbol's file among the project documents by name, wherever either is
1321 for( const wxFileName& document : m_projectDocuments )
1322 {
1323 if( document.IsFileReadable()
1324 && ( document.GetFullName().IsSameAs( loadAltiumFileName.GetFullName(), false )
1325 || ( extensionless && document.GetName().IsSameAs( loadAltiumFileName.GetName(), false ) ) ) )
1326 {
1327 return document;
1328 }
1329 }
1330
1331 return loadAltiumFileName;
1332}
1333
1334
1336{
1337 wxCHECK( m_schematic, /* void */ );
1338
1339 m_schematic->RefreshHierarchy();
1340
1341 SCH_SHEET_LIST sheetList = m_schematic->Hierarchy();
1342 std::map<wxString, std::vector<SCH_SHEET_PATH>> pathsByFile;
1343
1344 for( const SCH_SHEET_PATH& sheetPath : sheetList )
1345 {
1346 if( sheetPath.size() < 2 || !sheetPath.LastScreen() )
1347 continue;
1348
1349 wxString sheetFileName = sheetPath.Last()->GetFileName().Lower();
1350
1351 if( sheetFileName.IsEmpty() )
1352 continue;
1353
1354 SCH_SHEET_PATH parentPath = sheetPath;
1355 parentPath.pop_back();
1356
1357 pathsByFile[parentPath.Path().AsString() + wxT( "|" ) + sheetFileName]
1358 .push_back( sheetPath );
1359 }
1360
1361 for( auto& [groupKey, sheetPaths] : pathsByFile )
1362 {
1363 if( sheetPaths.size() < 2 )
1364 continue;
1365
1366 std::sort( sheetPaths.begin(), sheetPaths.end(),
1367 []( const SCH_SHEET_PATH& aFirst, const SCH_SHEET_PATH& aSecond )
1368 {
1369 const VECTOR2I& firstPos = aFirst.Last()->GetPosition();
1370 const VECTOR2I& secondPos = aSecond.Last()->GetPosition();
1371
1372 if( firstPos.y != secondPos.y )
1373 return firstPos.y < secondPos.y;
1374
1375 return firstPos.x < secondPos.x;
1376 } );
1377
1378 long basePage = 0;
1379
1380 for( const SCH_SHEET_PATH& sheetPath : sheetPaths )
1381 {
1382 long page = 0;
1383
1384 if( sheetPath.GetPageNumber().ToLong( &page ) && page > 0 )
1385 basePage = basePage == 0 ? page : std::min( basePage, page );
1386 }
1387
1388 if( basePage == 0 )
1389 {
1390 wxString nextPage = sheetList.GetNextPageNumber();
1391 nextPage.ToLong( &basePage );
1392 }
1393
1394 if( basePage == 0 )
1395 basePage = 1;
1396
1397 for( size_t ii = 0; ii < sheetPaths.size(); ++ii )
1398 {
1399 SCH_SHEET_PATH& sheetPath = sheetPaths[ii];
1400
1401 sheetPath.SetPageNumber( wxString::Format( wxT( "%ld" ), basePage + (long) ii ) );
1402
1403 for( SCH_ITEM* item : sheetPath.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
1404 {
1405 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
1406 wxString baseRef = symbol->GetField( FIELD_T::REFERENCE )->GetText();
1407
1408 if( baseRef.StartsWith( wxT( "#" ) ) )
1409 {
1410 symbol->AddSheetPathReferenceEntryIfMissing( sheetPath.Path() );
1411 continue;
1412 }
1413
1414 if( !baseRef.IsEmpty() )
1415 symbol->SetRef( &sheetPath, baseRef + wxT( "_" ) + sheetPath.Last()->GetName() );
1416 }
1417 }
1418 }
1419
1420 m_schematic->RefreshHierarchy();
1421}
1422
1423
1424// Net code of every connected item on every sheet, a promoted label standing in for its original
1425using ITEM_NET_CODES = std::map<std::pair<KIID_PATH, const SCH_ITEM*>, int>;
1426
1428 const std::map<const SCH_ITEM*, const SCH_ITEM*>& aOriginals )
1429{
1430 ITEM_NET_CODES codes;
1431
1432 for( const auto& [key, subgraphs] : aGraph.GetNetMap() )
1433 {
1434 for( const CONNECTION_SUBGRAPH* subgraph : subgraphs )
1435 {
1436 for( const SCH_ITEM* item : subgraph->GetItems() )
1437 {
1438 auto original = aOriginals.find( item );
1439
1440 codes[{ subgraph->GetSheet().Path(), original != aOriginals.end() ? original->second : item }] =
1441 key.Netcode;
1442 }
1443 }
1444 }
1445
1446 return codes;
1447}
1448
1449
1450static bool sameNets( const ITEM_NET_CODES& aBefore, const ITEM_NET_CODES& aAfter )
1451{
1452 if( aBefore.size() != aAfter.size() )
1453 return false;
1454
1455 std::map<int, int> forward;
1456 std::map<int, int> backward;
1457
1458 for( const auto& [pin, code] : aBefore )
1459 {
1460 auto it = aAfter.find( pin );
1461
1462 if( it == aAfter.end() )
1463 return false;
1464
1465 if( forward.emplace( code, it->second ).first->second != it->second
1466 || backward.emplace( it->second, code ).first->second != code )
1467 {
1468 return false;
1469 }
1470 }
1471
1472 return true;
1473}
1474
1475
1477{
1478 SCH_SHEET_LIST hierarchy = m_schematic->Hierarchy();
1479 CONNECTION_GRAPH* graph = m_schematic->ConnectionGraph();
1480
1481 graph->Recalculate( hierarchy, true );
1482
1483 struct NET_IDENTIFIERS
1484 {
1485 wxString name;
1486 bool hasGlobalLabel = false;
1487 bool hasGlobalPower = false;
1488 std::set<wxString> labelNames;
1489 std::set<SCH_LABEL*> labels;
1490 };
1491
1492 std::map<int, NET_IDENTIFIERS> nets;
1493 std::map<wxString, std::set<int>> globalNameNets;
1494 std::map<SCH_LABEL*, std::set<int>> labelNets;
1495
1496 for( const auto& [key, subgraphs] : graph->GetNetMap() )
1497 {
1498 NET_IDENTIFIERS& net = nets[key.Netcode];
1499 net.name = key.Name;
1500
1501 for( CONNECTION_SUBGRAPH* subgraph : subgraphs )
1502 {
1503 for( SCH_ITEM* item : subgraph->GetItems() )
1504 {
1505 if( item->Type() == SCH_GLOBAL_LABEL_T )
1506 {
1507 net.hasGlobalLabel = true;
1508 globalNameNets[subgraph->GetNameForDriver( item )].insert( key.Netcode );
1509 }
1510 else if( item->Type() == SCH_PIN_T && static_cast<SCH_PIN*>( item )->IsGlobalPower() )
1511 {
1512 net.hasGlobalPower = true;
1513 globalNameNets[subgraph->GetNameForDriver( item )].insert( key.Netcode );
1514 }
1515 else if( item->Type() == SCH_LABEL_T
1516 && !wxString( static_cast<SCH_LABEL*>( item )->GetText() ).Trim().IsEmpty() )
1517 {
1518 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
1519
1520 net.labelNames.insert( label->GetText() );
1521 net.labels.insert( label );
1522 labelNets[label].insert( key.Netcode );
1523 }
1524 }
1525 }
1526 }
1527
1528 std::map<wxString, std::vector<int>> candidates;
1529
1530 for( const auto& [code, net] : nets )
1531 {
1532 // Altium's choice among several labels on one net is undocumented, so leave those alone
1533 if( net.hasGlobalLabel || !net.hasGlobalPower || net.labelNames.size() != 1 )
1534 continue;
1535
1536 const wxString& text = *net.labelNames.begin();
1537 wxString name = EscapeString( text, CTX_NETNAME );
1538
1539 if( name == net.name || text.Contains( wxS( "${" ) ) )
1540 continue;
1541
1542 // A global label joins every global net of its name
1543 if( auto it = globalNameNets.find( name );
1544 it != globalNameNets.end()
1545 && std::any_of( it->second.begin(), it->second.end(),
1546 [code]( int aCode )
1547 {
1548 return aCode != code;
1549 } ) )
1550 {
1551 continue;
1552 }
1553
1554 // A label on a screen shared by several instances must sit on this net in all of them
1555 if( std::all_of( net.labels.begin(), net.labels.end(),
1556 [&]( SCH_LABEL* aLabel )
1557 {
1558 return labelNets[aLabel].size() == 1;
1559 } ) )
1560 {
1561 candidates[name].push_back( code );
1562 }
1563 }
1564
1565 std::vector<std::pair<SCH_LABEL*, SCH_GLOBALLABEL*>> converted;
1566 std::vector<wxString> promoted;
1567
1568 for( const auto& [name, codes] : candidates )
1569 {
1570 // Two power nets labelled alike would merge once the label is global
1571 if( codes.size() != 1 )
1572 continue;
1573
1574 promoted.push_back( name );
1575
1576 for( SCH_LABEL* label : nets[codes.front()].labels )
1577 {
1578 SCH_GLOBALLABEL* globalLabel = new SCH_GLOBALLABEL( label->GetPosition(), label->GetText() );
1579
1581 globalLabel->SetAttributes( *label, false );
1582 globalLabel->SetSpinStyle( label->GetSpinStyle() );
1583 globalLabel->GetField( FIELD_T::INTERSHEET_REFS )->SetVisible( false );
1584
1585 // Same mirroring as the change-label-type tool so the text keeps its side
1586 if( label->GetSpinStyle() == SPIN_STYLE::SPIN::UP
1587 || label->GetSpinStyle() == SPIN_STYLE::SPIN::BOTTOM )
1588 {
1589 globalLabel->MirrorVertically( label->GetPosition().y );
1590 }
1591 else
1592 {
1593 globalLabel->MirrorHorizontally( label->GetPosition().x );
1594 }
1595
1596 converted.emplace_back( label, globalLabel );
1597 }
1598 }
1599
1600 if( converted.empty() )
1601 return;
1602
1603 auto screenOf = []( SCH_ITEM* aItem )
1604 {
1605 return static_cast<SCH_SCREEN*>( aItem->GetParent() );
1606 };
1607
1608 std::map<const SCH_ITEM*, const SCH_ITEM*> originals;
1609
1610 for( const auto& [label, globalLabel] : converted )
1611 originals[globalLabel] = label;
1612
1613 ITEM_NET_CODES before = itemNetCodes( *graph, {} );
1614
1615 for( auto& [label, globalLabel] : converted )
1616 {
1617 SCH_SCREEN* screen = screenOf( label );
1618 screen->Remove( label );
1619 screen->Append( globalLabel );
1620 }
1621
1622 graph->Recalculate( hierarchy, true );
1623
1624 if( sameNets( before, itemNetCodes( *graph, originals ) ) )
1625 {
1626 for( auto& [label, globalLabel] : converted )
1627 delete label;
1628
1629 if( m_reporter )
1630 {
1631 for( const wxString& name : promoted )
1632 {
1633 m_reporter->Report( wxString::Format( _( "Net label '%s' made global so it names its net "
1634 "ahead of the power port, as in Altium." ),
1635 UnescapeString( name ) ),
1637 }
1638 }
1639
1640 return;
1641 }
1642
1643 for( auto& [label, globalLabel] : converted )
1644 {
1645 SCH_SCREEN* screen = screenOf( globalLabel );
1646 screen->Remove( globalLabel );
1647 screen->Append( label );
1648 delete globalLabel;
1649 }
1650
1651 graph->Recalculate( hierarchy, true );
1652
1653 if( m_reporter )
1654 m_reporter->Report( wxS( "Net labels kept local, a global label would have changed connectivity" ),
1656}
1657
1658
1659void SCH_IO_ALTIUM::ParseAltiumSch( const wxString& aFileName )
1660{
1661 // Load path may be different from the project path.
1662 wxFileName parentFileName( aFileName );
1663
1664 // Mark on the parse stack so a sheet referencing an ancestor or itself reads as recursion
1665 PARSE_FILE_GUARD selfGuard( this, AltiumParseKey( parentFileName ) );
1666 m_parsedFiles.insert( AltiumParseKey( parentFileName ) );
1667
1668 // Fresh per-file owner-index map so a stale entry from a pruned sheet is not dereferenced
1669 // map insert keeps a dangling entry rather than replacing it
1670 m_sheets.clear();
1671
1672 if( m_rootFilepath.IsEmpty() )
1673 m_rootFilepath = parentFileName.GetPath();
1674
1675 if( m_progressReporter )
1676 {
1677 wxFileName relative = parentFileName;
1678 relative.MakeRelativeTo( m_rootFilepath );
1679
1680 m_progressReporter->Report( wxString::Format( _( "Importing %s" ), relative.GetFullPath() ) );
1681
1682 if( !m_progressReporter->KeepRefreshing() )
1684 }
1685
1686 if( isBinaryFile( aFileName ) )
1687 {
1688 ALTIUM_COMPOUND_FILE altiumSchFile( aFileName );
1689
1690 try
1691 {
1692 ParseStorage( altiumSchFile ); // we need this before parsing the FileHeader
1693 ParseFileHeader( altiumSchFile );
1694
1695 // Parse "Additional" because sheet is set up during "FileHeader" parsing.
1696 ParseAdditional( altiumSchFile );
1697 }
1698 catch( const CFB::CFBException& exception )
1699 {
1700 THROW_IO_ERROR( exception.what() );
1701 }
1702 catch( const std::exception& exc )
1703 {
1704 THROW_IO_ERROR( wxString::Format( _( "Error parsing Altium schematic: %s" ), exc.what() ) );
1705 }
1706 }
1707 else // ASCII
1708 {
1709 ParseASCIISchematic( aFileName );
1710 }
1711
1712 SCH_SCREEN* currentScreen = getCurrentScreen();
1713 wxCHECK( currentScreen, /* void */ );
1714
1715 // Descend the hierarchy. One symbol may list several semicolon-separated files (pages of a
1716 // multi-page block) merged into one screen; refs to a parse-stack file are cycles pruned later
1717 std::vector<SCH_SHEET*> cyclicSheets;
1718
1719 for( SCH_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
1720 {
1721 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
1722
1723 wxCHECK2( sheet, continue );
1724
1725 // Already given a screen by an earlier member pass
1726 if( sheet->GetScreen() )
1727 continue;
1728
1729 wxArrayString tokens = wxSplit( sheet->GetFileName(), ';' );
1730 std::vector<wxFileName> members;
1731 std::set<wxString> memberKeySet;
1732 bool skippedForRecursion = false;
1733
1734 for( wxString token : tokens )
1735 {
1736 token.Trim( true ).Trim( false );
1737
1738 if( token.IsEmpty() )
1739 continue;
1740
1741 wxFileName resolved = ResolveSheetFileName( parentFileName.GetPath(), token );
1742
1743 if( resolved.GetFullName().IsEmpty() || !resolved.IsFileReadable() )
1744 continue;
1745
1746 wxString key = AltiumParseKey( resolved );
1747
1748 if( m_parsingFiles.count( key ) )
1749 {
1750 skippedForRecursion = true;
1751 continue;
1752 }
1753
1754 // Dedup so a page listed twice is not parsed and tiled twice
1755 if( memberKeySet.insert( key ).second )
1756 members.push_back( resolved );
1757 }
1758
1759 if( members.empty() )
1760 {
1761 // Only parse-stack files means a cross-page or self reference to prune
1762 // otherwise an unresolved Altium signal harness
1763 if( skippedForRecursion )
1764 {
1765 cyclicSheets.push_back( sheet );
1766 }
1767 else
1768 {
1769 wxString missingFile;
1770
1771 for( wxString token : tokens )
1772 {
1773 if( !token.Trim( true ).Trim( false ).IsEmpty() )
1774 {
1775 missingFile = token;
1776 break;
1777 }
1778 }
1779
1780 if( missingFile.IsEmpty() )
1781 {
1782 m_errorMessages.emplace( wxString::Format( _( "The file name for sheet %s is undefined, "
1783 "this is probably an Altium signal harness "
1784 "that got converted to a sheet." ),
1785 sheet->GetName() ),
1787 }
1788 else
1789 {
1790 m_errorMessages.emplace( wxString::Format( _( "The file %s of sheet %s was not found; the "
1791 "sheet is empty." ),
1792 missingFile, sheet->GetName() ),
1794 }
1795
1796 attachEmptySheetScreen( sheet, missingFile );
1797 }
1798
1799 continue;
1800 }
1801
1802 // Repeated channels save one file so they must share one screen. Keyed on the sources, as
1803 // distinct sources can share a destination name, and set only once parsed so no ancestor matches
1804 std::vector<wxString> loadKey;
1805
1806 for( const wxFileName& member : members )
1807 {
1808 loadKey.push_back( AltiumParseKey( member ) );
1809 m_subsheetFiles.insert( loadKey.back().Lower() );
1810 }
1811
1812 SCH_SCREEN* loadedScreen = nullptr;
1813
1814 if( auto it = m_loadedScreens.find( loadKey ); it != m_loadedScreens.end() )
1815 loadedScreen = it->second;
1816
1817 if( loadedScreen )
1818 {
1819 sheet->SetScreen( loadedScreen );
1820 sheet->SetFileName( wxFileName( loadedScreen->GetFileName() ).GetFullName() );
1821
1822 // Sub-sheets already loaded
1823 continue;
1824 }
1825
1826 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
1827 SCH_SCREEN* screen = sheet->GetScreen();
1828
1829 wxCHECK2( screen, continue );
1830
1831 if( sheet->GetName().Trim().empty() )
1832 {
1833 std::set<wxString> sheetNames;
1834
1835 for( EDA_ITEM* otherItem : currentScreen->Items().OfType( SCH_SHEET_T ) )
1836 sheetNames.insert( static_cast<SCH_SHEET*>( otherItem )->GetName() );
1837
1838 sheet->SetName( AltiumDeriveSheetName( members[0].GetFullName(), sheetNames ) );
1839 }
1840
1841 // Hold every block member on the parse stack so a page cross-referencing a sibling or
1842 // itself reads as recursion below
1843 PARSE_FILE_GUARD blockGuard( this,
1844 std::vector<wxString>( memberKeySet.begin(),
1845 memberKeySet.end() ) );
1846
1847 m_sheetPath.push_back( sheet );
1848
1849 std::optional<int> nextPageTop;
1850
1851 for( const wxFileName& member : members )
1852 {
1853 std::set<const SCH_ITEM*> before;
1854
1855 for( SCH_ITEM* screenItem : screen->Items() )
1856 before.insert( screenItem );
1857
1858 ParseAltiumSch( member.GetFullPath() );
1859
1860 std::vector<SCH_ITEM*> added;
1861 BOX2I addedBox;
1862
1863 for( SCH_ITEM* screenItem : screen->Items() )
1864 {
1865 if( !before.count( screenItem ) )
1866 {
1867 added.push_back( screenItem );
1868 addedBox.Merge( screenItem->GetBoundingBox() );
1869 }
1870 }
1871
1872 if( added.empty() )
1873 continue;
1874
1875 // Tile each later page below the merged ones so shared source coordinates do not overlap
1876 if( nextPageTop )
1877 {
1878 VECTOR2I shift( 0, *nextPageTop - addedBox.GetTop() );
1879
1880 for( SCH_ITEM* addedItem : added )
1881 {
1882 addedItem->Move( shift );
1883 screen->Update( addedItem );
1884 }
1885
1886 addedBox.Offset( shift );
1887 }
1888
1889 nextPageTop = addedBox.GetBottom() + schIUScale.MilsToIU( 500 );
1890 }
1891
1892 m_sheetPath.pop_back();
1893
1894 wxFileName claimed( claimSheetFile( members[0].GetName() ) );
1895
1896 sheet->SetFileName( claimed.GetFullName() );
1897 screen->SetFileName( claimed.GetFullPath() );
1898 m_loadedScreens.emplace( std::move( loadKey ), screen );
1899 }
1900
1901 pruneCyclicSheets( cyclicSheets, currentScreen );
1902}
1903
1904
1905void SCH_IO_ALTIUM::attachEmptySheetScreen( SCH_SHEET* aSheet, const wxString& aMissingFile )
1906{
1907 wxString stem = aMissingFile;
1908 stem.Replace( wxS( "\\" ), wxS( "/" ) );
1909 stem = stem.IsEmpty() ? aSheet->GetName() : wxFileName( stem ).GetName();
1910 ReplaceIllegalFileNameChars( stem, '_' );
1911
1912 if( stem.IsEmpty() )
1913 stem = aSheet->m_Uuid.AsString();
1914
1915 // A screen with no file name is saved as ".kicad_sch", which every such sheet would then share
1916 wxFileName missing( m_schematic->Project().GetProjectPath(), stem, FILEEXT::KiCadSchematicFileExtension );
1917
1918 if( auto it = m_missingScreens.find( missing.GetFullPath() ); it != m_missingScreens.end() )
1919 {
1920 aSheet->SetScreen( it->second );
1921 }
1922 else
1923 {
1924 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
1925
1926 aSheet->SetScreen( screen );
1927 screen->SetFileName( claimSheetFile( stem ) );
1928 m_missingScreens.emplace( missing.GetFullPath(), screen );
1929 }
1930
1931 aSheet->SetFileName( wxFileName( aSheet->GetScreen()->GetFileName() ).GetFullName() );
1932}
1933
1934
1935wxString SCH_IO_ALTIUM::claimSheetFile( const wxString& aStem )
1936{
1937 wxFileName fn( m_schematic->Project().GetProjectPath(), aStem, FILEEXT::KiCadSchematicFileExtension );
1938
1939 for( int n = 1; fn.GetFullPath().IsSameAs( m_rootSheetFile, false )
1940 || !m_sheetFiles.insert( fn.GetFullPath().Lower() ).second;
1941 n++ )
1942 {
1943 fn.SetName( wxString::Format( wxS( "%s_%d" ), aStem, n ) );
1944 }
1945
1946 return fn.GetFullPath();
1947}
1948
1949
1950void SCH_IO_ALTIUM::pruneCyclicSheets( const std::vector<SCH_SHEET*>& aCyclicSheets,
1951 SCH_SCREEN* aScreen )
1952{
1953 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
1954
1955 for( SCH_SHEET* sheet : aCyclicSheets )
1956 {
1957 // Deleting the sheet drops its pins leaving wires dangling. Replace each pin with a hier
1958 // label at its location so the wire connects by name to the merged page port
1959 for( SCH_SHEET_PIN* pin : sheet->GetPins() )
1960 {
1961 SCH_HIERLABEL* label = new SCH_HIERLABEL( pin->GetPosition(), pin->GetText() );
1962
1963 label->SetShape( pin->GetShape() );
1964 label->SetSpinStyle( pin->GetSpinStyle() );
1965 aScreen->Append( label );
1966 }
1967
1968 // Drop the sheet-instance record on the root screen
1969 if( rootScreen )
1970 {
1971 std::erase_if( rootScreen->m_sheetInstances,
1972 [&]( const SCH_SHEET_INSTANCE& aInstance )
1973 {
1974 return !aInstance.m_Path.empty()
1975 && aInstance.m_Path.back() == sheet->m_Uuid;
1976 } );
1977 }
1978
1979 aScreen->DeleteItem( sheet );
1980 }
1981}
1982
1983
1985{
1986 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "Storage" } );
1987
1988 if( file == nullptr )
1989 return;
1990
1991 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
1992
1993 std::map<wxString, wxString> properties = reader.ReadProperties();
1994 ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
1995 int weight = ALTIUM_PROPS_UTILS::ReadInt( properties, "WEIGHT", 0 );
1996
1997 if( weight < 0 )
1998 THROW_IO_ERROR( "Storage weight is negative!" );
1999
2000 for( int i = 0; i < weight; i++ )
2001 m_altiumStorage.emplace_back( reader );
2002
2003 if( reader.HasParsingError() )
2004 THROW_IO_ERROR( "stream was not parsed correctly!" );
2005
2006 // TODO pointhi: is it possible to have multiple headers in one Storage file? Otherwise
2007 // throw IO Error.
2008 if( reader.GetRemainingBytes() != 0 )
2009 {
2010 m_errorMessages.emplace( wxString::Format( _( "Storage file not fully parsed (%d bytes remaining)." ),
2011 reader.GetRemainingBytes() ),
2013 }
2014}
2015
2016
2018{
2019 wxString streamName = wxS( "Additional" );
2020
2021 const CFB::COMPOUND_FILE_ENTRY* file =
2022 aAltiumSchFile.FindStream( { streamName.ToStdString() } );
2023
2024 if( file == nullptr )
2025 return;
2026
2027 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
2028
2029 if( reader.GetRemainingBytes() <= 0 )
2030 {
2031 THROW_IO_ERROR( "Additional section does not contain any data" );
2032 }
2033 else
2034 {
2035 std::map<wxString, wxString> properties = reader.ReadProperties();
2036
2037 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
2038 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
2039
2040 if( record != ALTIUM_SCH_RECORD::HEADER )
2041 THROW_IO_ERROR( "Header expected" );
2042 }
2043
2044 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
2045 {
2046 std::map<wxString, wxString> properties = reader.ReadProperties();
2047
2048 ParseRecord( index, properties, streamName );
2049 }
2050
2051 // Handle harness Ports
2052 for( const ASCH_PORT& port : m_altiumHarnessPortsCurrentSheet )
2053 ParseHarnessPort( port );
2054
2055 CreateAliases();
2056
2057 // Wrap any remaining net labels sitting on bus lines in curly braces
2059
2060 if( reader.HasParsingError() )
2061 THROW_IO_ERROR( "stream was not parsed correctly!" );
2062
2063 if( reader.GetRemainingBytes() != 0 )
2064 THROW_IO_ERROR( "stream is not fully parsed" );
2065
2066 m_altiumHarnesses.clear();
2068}
2069
2070
2072{
2073 wxString streamName = wxS( "FileHeader" );
2074
2075 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { streamName.ToStdString() } );
2076
2077 if( file == nullptr )
2078 THROW_IO_ERROR( "FileHeader not found" );
2079
2080 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
2081
2082 if( reader.GetRemainingBytes() <= 0 )
2083 {
2084 THROW_IO_ERROR( "FileHeader does not contain any data" );
2085 }
2086 else
2087 {
2088 std::map<wxString, wxString> properties = reader.ReadProperties();
2089
2090 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
2091
2092 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Binary File Version 5.0" ) )
2093 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
2094 }
2095
2096 // Prepare some local variables
2097 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
2098 wxCHECK( !m_currentTitleBlock, /* void */ );
2099
2100 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
2101
2102 // index is required to resolve OWNERINDEX
2103 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
2104 {
2105 std::map<wxString, wxString> properties = reader.ReadProperties();
2106
2107 ParseRecord( index, properties, streamName );
2108 }
2109
2110 if( reader.HasParsingError() )
2111 THROW_IO_ERROR( "stream was not parsed correctly!" );
2112
2113 if( reader.GetRemainingBytes() != 0 )
2114 THROW_IO_ERROR( "stream is not fully parsed" );
2115
2116 // assign LIB_SYMBOL -> COMPONENT
2117 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
2118 {
2119 auto libSymbolIt = m_libSymbols.find( symbol.first );
2120
2121 if( libSymbolIt == m_libSymbols.end() )
2122 THROW_IO_ERROR( "every symbol should have a symbol attached" );
2123
2124 fixupSymbolPinNameNumbers( symbol.second );
2125 fixupSymbolPinNameNumbers( libSymbolIt->second );
2126
2127 symbol.second->SetLibSymbol( libSymbolIt->second );
2128 }
2129
2130 SCH_SCREEN* screen = getCurrentScreen();
2131 wxCHECK( screen, /* void */ );
2132
2133 // Handle title blocks
2135 m_currentTitleBlock.reset();
2136
2137 // Handle Ports
2138 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
2139 ParsePort( port );
2140
2141 // Bus labels are wrapped in ParseAdditional() after CreateAliases() has had a chance to
2142 // append harness suffixes; doing it here would prematurely mark labels as bus groups.
2143
2144 // Assign default names to any sheet symbols that didn't get a SHEET_NAME record
2146
2148 m_altiumComponents.clear();
2149 m_altiumTemplates.clear();
2151
2152 m_symbols.clear();
2153 m_libSymbols.clear();
2154
2155 // Otherwise we cannot save the imported sheet?
2156 SCH_SHEET* sheet = getCurrentSheet();
2157
2158 wxCHECK( sheet, /* void */ );
2159
2160 sheet->SetModified();
2161}
2162
2163
2164void SCH_IO_ALTIUM::ParseASCIISchematic( const wxString& aFileName )
2165{
2166 // Read storage content first
2167 {
2168 ALTIUM_ASCII_PARSER storageReader( aFileName );
2169
2170 while( storageReader.CanRead() )
2171 {
2172 std::map<wxString, wxString> properties = storageReader.ReadProperties();
2173
2174 // Binary data
2175 if( properties.find( wxS( "BINARY" ) ) != properties.end() )
2176 m_altiumStorage.emplace_back( properties );
2177 }
2178 }
2179
2180 // Read other data
2181 ALTIUM_ASCII_PARSER reader( aFileName );
2182
2183 if( !reader.CanRead() )
2184 {
2185 THROW_IO_ERROR( "FileHeader does not contain any data" );
2186 }
2187 else
2188 {
2189 std::map<wxString, wxString> properties = reader.ReadProperties();
2190
2191 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
2192
2193 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Ascii File Version 5.0" ) )
2194 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
2195 }
2196
2197 // Prepare some local variables
2198 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
2199 wxCHECK( !m_currentTitleBlock, /* void */ );
2200
2201 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
2202
2203 // index is required to resolve OWNERINDEX
2204 int index = 0;
2205
2206 while( reader.CanRead() )
2207 {
2208 std::map<wxString, wxString> properties = reader.ReadProperties();
2209
2210 // Reset index at headers
2211 if( properties.find( wxS( "HEADER" ) ) != properties.end() )
2212 {
2213 index = 0;
2214 continue;
2215 }
2216
2217 if( properties.find( wxS( "RECORD" ) ) != properties.end() )
2218 ParseRecord( index, properties, aFileName );
2219
2220 index++;
2221 }
2222
2223 if( reader.HasParsingError() )
2224 THROW_IO_ERROR( "stream was not parsed correctly!" );
2225
2226 if( reader.CanRead() )
2227 THROW_IO_ERROR( "stream is not fully parsed" );
2228
2229 // assign LIB_SYMBOL -> COMPONENT
2230 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
2231 {
2232 auto libSymbolIt = m_libSymbols.find( symbol.first );
2233
2234 if( libSymbolIt == m_libSymbols.end() )
2235 THROW_IO_ERROR( "every symbol should have a symbol attached" );
2236
2237 fixupSymbolPinNameNumbers( symbol.second );
2238 fixupSymbolPinNameNumbers( libSymbolIt->second );
2239
2240 symbol.second->SetLibSymbol( libSymbolIt->second );
2241 }
2242
2243 SCH_SCREEN* screen = getCurrentScreen();
2244 wxCHECK( screen, /* void */ );
2245
2246 // Handle title blocks
2248 m_currentTitleBlock.reset();
2249
2250 // Handle harness Ports
2251 for( const ASCH_PORT& port : m_altiumHarnessPortsCurrentSheet )
2252 ParseHarnessPort( port );
2253
2254 // Handle Ports
2255 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
2256 ParsePort( port );
2257
2258 // Add the aliases used for harnesses
2259 CreateAliases();
2260
2261 // Wrap net labels sitting on bus lines in curly braces so KiCad recognizes them
2263
2264 // Assign default names to any sheet symbols that didn't get a SHEET_NAME record
2266
2267 m_altiumHarnesses.clear();
2269 m_altiumComponents.clear();
2270 m_altiumTemplates.clear();
2272
2273 m_symbols.clear();
2274 m_libSymbols.clear();
2275
2276 // Otherwise we cannot save the imported sheet?
2277 SCH_SHEET* sheet = getCurrentSheet();
2278
2279 wxCHECK( sheet, /* void */ );
2280
2281 sheet->SetModified();
2282}
2283
2284
2285void SCH_IO_ALTIUM::ParseRecord( int index, std::map<wxString, wxString>& properties,
2286 const wxString& aSectionName )
2287{
2288 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", -1 );
2289 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
2290
2291 // see: https://github.com/vadmium/python-altium/blob/master/format.md
2292 switch( record )
2293 {
2294 // FileHeader section
2295
2297 THROW_IO_ERROR( "Header already parsed" );
2298
2300 ParseComponent( index, properties );
2301 break;
2302
2304 ParsePin( properties );
2305 break;
2306
2308 m_errorMessages.emplace( _( "Record 'IEEE_SYMBOL' not handled." ), RPT_SEVERITY_INFO );
2309 break;
2310
2312 ParseLabel( properties );
2313 break;
2314
2316 ParseBezier( properties );
2317 break;
2318
2320 ParsePolyline( properties );
2321 break;
2322
2324 ParsePolygon( properties );
2325 break;
2326
2328 ParseEllipse( properties );
2329 break;
2330
2332 ParsePieChart( properties );
2333 break;
2334
2336 ParseRoundRectangle( properties );
2337 break;
2338
2341 ParseArc( properties );
2342 break;
2343
2345 ParseLine( properties );
2346 break;
2347
2349 ParseRectangle( properties );
2350 break;
2351
2353 ParseSheetSymbol( index, properties );
2354 break;
2355
2357 ParseSheetEntry( properties );
2358 break;
2359
2361 ParsePowerPort( properties );
2362 break;
2363
2365 // Ports are parsed after the sheet was parsed
2366 // This is required because we need all electrical connection points before placing.
2367 m_altiumPortsCurrentSheet.emplace_back( properties );
2368 break;
2369
2371 ParseNoERC( properties );
2372 break;
2373
2375 ParseNetLabel( properties );
2376 break;
2377
2379 ParseBus( properties );
2380 break;
2381
2383 ParseWire( properties );
2384 break;
2385
2387 ParseTextFrame( properties );
2388 break;
2389
2391 ParseJunction( properties );
2392 break;
2393
2395 ParseImage( properties );
2396 break;
2397
2399 ParseSheet( properties );
2400 break;
2401
2403 ParseSheetName( properties );
2404 break;
2405
2407 ParseFileName( properties );
2408 break;
2409
2411 ParseDesignator( properties );
2412 break;
2413
2415 ParseBusEntry( properties );
2416 break;
2417
2419 ParseTemplate( index, properties );
2420 break;
2421
2423 ParseParameter( properties );
2424 break;
2425
2427 m_errorMessages.emplace( _( "Parameter Set not currently supported." ), RPT_SEVERITY_ERROR );
2428 break;
2429
2431 ParseImplementationList( index, properties );
2432 break;
2433
2435 ParseImplementation( properties );
2436 break;
2437
2439 break;
2440
2442 break;
2443
2445 break;
2446
2448 ParseNote( properties );
2449 break;
2450
2452 m_errorMessages.emplace( _( "Compile mask not currently supported." ), RPT_SEVERITY_ERROR );
2453 break;
2454
2456 break;
2457
2458 // Additional section
2459
2461 ParseHarnessConnector( index, properties );
2462 break;
2463
2465 ParseHarnessEntry( properties );
2466 break;
2467
2469 ParseHarnessType( properties );
2470 break;
2471
2473 ParseSignalHarness( properties );
2474 break;
2475
2477 m_errorMessages.emplace( _( "Blanket not currently supported." ), RPT_SEVERITY_ERROR );
2478 break;
2479
2480 default:
2481 m_errorMessages.emplace(
2482 wxString::Format( _( "Unknown or unexpected record id %d found in %s." ), recordId,
2483 aSectionName ),
2485 break;
2486 }
2487
2489}
2490
2491
2492
2493const ASCH_STORAGE_FILE* SCH_IO_ALTIUM::GetFileFromStorage( const wxString& aFilename ) const
2494{
2495 const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
2496
2497 for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
2498 {
2499 if( file.filename.IsSameAs( aFilename ) )
2500 return &file;
2501
2502 if( file.filename.EndsWith( aFilename ) )
2503 nonExactMatch = &file;
2504 }
2505
2506 return nonExactMatch;
2507}
2508
2509
2510void SCH_IO_ALTIUM::ParseComponent( int aIndex, const std::map<wxString, wxString>& aProperties )
2511{
2512 SCH_SHEET* currentSheet = m_sheetPath.Last();
2513 wxCHECK( currentSheet, /* void */ );
2514
2515 wxString sheetName = currentSheet->GetName();
2516
2517 if( sheetName.IsEmpty() )
2518 sheetName = wxT( "root" );
2519
2520 ASCH_SYMBOL altiumSymbol( aProperties );
2521
2522 if( m_altiumComponents.count( aIndex ) )
2523 {
2524 const ASCH_SYMBOL& currentSymbol = m_altiumComponents.at( aIndex );
2525
2526 m_errorMessages.emplace( wxString::Format( _( "Symbol '%s' in sheet '%s' at index %d "
2527 "replaced with symbol \"%s\"." ),
2528 currentSymbol.libreference,
2529 sheetName,
2530 aIndex,
2531 altiumSymbol.libreference ),
2533 }
2534
2535 auto pair = m_altiumComponents.insert( { aIndex, altiumSymbol } );
2536 const ASCH_SYMBOL& elem = pair.first->second;
2537
2538 wxString name;
2539 LIB_ID libId;
2540
2541 if( !elem.sourcelibraryname.IsEmpty() && !elem.libreference.IsEmpty() )
2542 {
2543 // The part comes from an Altium library that project import registers in the symbol
2544 // library table, so address it by its real library id and let the placement transform
2545 // ride on the SCH_SYMBOL rather than baking it into a unique name. Altium stores Windows
2546 // paths, so split the source library name accordingly.
2547 name = elem.libreference;
2548 libId = AltiumToKiCadLibID( wxFileName( elem.sourcelibraryname, wxPATH_WIN ).GetName(),
2549 name );
2550 }
2551 else
2552 {
2553 // TODO: this is a hack until we correctly apply all transformations to every element
2554 name = wxString::Format( "%s_%d%s_%s_%s",
2555 sheetName,
2556 elem.orientation,
2557 elem.isMirrored ? "_mirrored" : "",
2558 elem.libreference,
2559 elem.sourcelibraryname );
2560
2561 libId = AltiumToKiCadLibID( getLibName(), name );
2562 }
2563
2564 LIB_SYMBOL* ksymbol = new LIB_SYMBOL( wxEmptyString );
2565 ksymbol->SetName( name );
2566 ksymbol->SetDescription( elem.componentdescription );
2567 ksymbol->SetLibId( libId );
2568
2569 // Altium PARTCOUNT is one more than the actual unit count. The property may be missing
2570 // (defaults to 0) or otherwise nonsensical, so clamp to a minimum of 1 unit.
2571 ksymbol->SetUnitCount( std::max( 1, elem.partcount - 1 ), true );
2572
2573 if( elem.displaymodecount > 1 )
2574 {
2575 std::vector<wxString> bodyStyleNames;
2576
2577 for( int i = 0; i < elem.displaymodecount; i++ )
2578 bodyStyleNames.push_back( wxString::Format( "Display %d", i + 1 ) );
2579
2580 ksymbol->SetBodyStyleNames( bodyStyleNames );
2581 }
2582
2583 m_libSymbols.insert( { aIndex, ksymbol } );
2584
2585 // each component has its own symbol for now
2586 SCH_SYMBOL* symbol = new SCH_SYMBOL();
2587
2588 symbol->SetPosition( elem.location + m_sheetOffset );
2589
2590 for( SCH_FIELD& field : symbol->GetFields() )
2591 field.SetVisible( false );
2592
2593 int orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
2594
2595 // Altium encodes symbol rotation as quarter turns CCW, matching KiCad's SYM_ORIENT_* angles
2596 // one for one. The stored value must equal the Altium angle so a later "Update Symbols from
2597 // Library" against a canonical upright symbol does not rotate the placement.
2598 switch( elem.orientation )
2599 {
2600 case 0: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0; break;
2601 case 1: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_90; break;
2602 case 2: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_180; break;
2603 case 3: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_270; break;
2604 default: break;
2605 }
2606
2607 if( elem.isMirrored )
2609
2610 symbol->SetOrientation( orientation );
2611
2612 symbol->SetLibId( libId );
2613
2614 if( ksymbol->GetUnitCount() > 1 )
2615 symbol->SetUnit( std::max( 1, elem.currentpartid ) );
2616 else
2617 symbol->SetUnit( 1 );
2618
2619 if( elem.displaymodecount > 1 )
2620 symbol->SetBodyStyle( elem.displaymode + 1 );
2621
2623
2624 SCH_SCREEN* screen = getCurrentScreen();
2625 wxCHECK( screen, /* void */ );
2626
2627 screen->Append( symbol );
2628
2629 m_symbols.insert( { aIndex, symbol } );
2630
2631 if( !elem.uniqueid.empty() )
2632 m_altiumSymbolToUid[symbol] = elem.uniqueid;
2633}
2634
2635
2636void SCH_IO_ALTIUM::ParseTemplate( int aIndex, const std::map<wxString, wxString>& aProperties )
2637{
2638 SCH_SHEET* currentSheet = m_sheetPath.Last();
2639 wxCHECK( currentSheet, /* void */ );
2640
2641 wxString sheetName = currentSheet->GetName();
2642
2643 if( sheetName.IsEmpty() )
2644 sheetName = wxT( "root" );
2645
2646 ASCH_TEMPLATE altiumTemplate( aProperties );
2647
2648 // Extract base name from path
2649 wxString baseName = altiumTemplate.filename.AfterLast( '\\' ).BeforeLast( '.' );
2650
2651 if( baseName.IsEmpty() )
2652 baseName = wxS( "Template" );
2653
2654 m_altiumTemplates.insert( { aIndex, altiumTemplate } );
2655 // No need to create a symbol - graphics is put on the sheet
2656}
2657
2658
2659void SCH_IO_ALTIUM::ParsePin( const std::map<wxString, wxString>& aProperties,
2660 std::vector<LIB_SYMBOL*>& aSymbol )
2661{
2662 ASCH_PIN elem( aProperties );
2663
2664 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2665 SCH_SYMBOL* schSymbol = nullptr;
2666
2667 if( !symbol )
2668 {
2669 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2670
2671 if( libSymbolIt == m_libSymbols.end() )
2672 {
2673 // TODO: e.g. can depend on Template (RECORD=39
2674 m_errorMessages.emplace( wxString::Format( wxT( "Pin's owner (%d) not found." ),
2675 elem.ownerindex ),
2677 return;
2678 }
2679
2680 schSymbol = m_symbols.at( libSymbolIt->first );
2681 symbol = libSymbolIt->second;
2682 }
2683
2684 SCH_PIN* pin = new SCH_PIN( symbol );
2685
2686 // Make sure that these are visible when initializing the symbol
2687 // This may be overriden by the file data but not by the pin defaults
2688 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
2689 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
2690
2691 symbol->AddDrawItem( pin, false );
2692
2693 pin->SetUnit( std::max( 0, elem.ownerpartid ) );
2694
2695 if( symbol->GetBodyStyleCount() > 1 )
2696 {
2697 if( !aSymbol.empty() )
2698 {
2699 pin->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2700 }
2701 else
2702 {
2703 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2704
2705 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2706 pin->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2707 }
2708 }
2709
2710 pin->SetName( AltiumPinNamesToKiCad( elem.name ) );
2711 pin->SetNumber( AltiumPinDesignatorToKiCad( elem.designator ) );
2712 pin->SetLength( elem.pinlength );
2713
2714 if( elem.hidden )
2715 pin->SetVisible( false );
2716
2717 if( !elem.showDesignator )
2718 pin->SetNumberTextSize( 0 );
2719
2720 if( !elem.showPinName )
2721 pin->SetNameTextSize( 0 );
2722
2723 // Altium gives the pin body end location (elem.location) and the pre-computed
2724 // electrical connection point (elem.kicadLocation) which accounts for pin length
2725 // with combined integer+fractional arithmetic to avoid rounding errors.
2726 VECTOR2I bodyEnd = elem.location;
2727 VECTOR2I pinLocation = elem.kicadLocation;
2728
2729 switch( elem.orientation )
2730 {
2732 pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT );
2733 break;
2734
2736 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
2737 break;
2738
2740 pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT );
2741 break;
2742
2744 pin->SetOrientation( PIN_ORIENTATION::PIN_UP );
2745 break;
2746
2747 default:
2748 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
2749 break;
2750 }
2751
2752 if( schSymbol )
2753 {
2754 // Both points are in absolute schematic coordinates. Transform them to library-local
2755 // space, then derive the pin orientation from the resulting direction vector.
2756 pinLocation = GetRelativePosition( pinLocation + m_sheetOffset, schSymbol );
2757 bodyEnd = GetRelativePosition( bodyEnd + m_sheetOffset, schSymbol );
2758
2759 VECTOR2I dir = bodyEnd - pinLocation;
2760
2761 if( std::abs( dir.x ) >= std::abs( dir.y ) )
2762 pin->SetOrientation( dir.x > 0 ? PIN_ORIENTATION::PIN_RIGHT : PIN_ORIENTATION::PIN_LEFT );
2763 else
2764 pin->SetOrientation( dir.y > 0 ? PIN_ORIENTATION::PIN_DOWN : PIN_ORIENTATION::PIN_UP );
2765 }
2766
2767 pin->SetPosition( pinLocation );
2768
2769 switch( elem.electrical )
2770 {
2773 break;
2774
2776 pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
2777 break;
2778
2781 break;
2782
2785 break;
2786
2789 break;
2790
2793 break;
2794
2797 break;
2798
2801 break;
2802
2804 default:
2806 m_errorMessages.emplace( _( "Pin has unexpected electrical type." ), RPT_SEVERITY_WARNING );
2807 break;
2808 }
2809
2811 m_errorMessages.emplace( _( "Pin has unexpected outer edge type." ), RPT_SEVERITY_WARNING );
2812
2814 m_errorMessages.emplace( _( "Pin has unexpected inner edge type." ), RPT_SEVERITY_WARNING );
2815
2817 {
2818 switch( elem.symbolInnerEdge )
2819 {
2822 break;
2823
2824 default:
2825 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
2826 break;
2827 }
2828 }
2830 {
2831 switch( elem.symbolInnerEdge )
2832 {
2834 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
2835 break;
2836
2837 default:
2838 pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
2839 break;
2840 }
2841 }
2843 {
2844 pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
2845 }
2846 else
2847 {
2848 switch( elem.symbolInnerEdge )
2849 {
2851 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
2852 break;
2853
2854 default:
2855 pin->SetShape( GRAPHIC_PINSHAPE::LINE ); // nothing to do
2856 break;
2857 }
2858 }
2859}
2860
2861
2863 ASCH_RECORD_ORIENTATION orientation )
2864{
2865 int vjustify, hjustify;
2867
2868 switch( justification )
2869 {
2870 default:
2875 vjustify = GR_TEXT_V_ALIGN_BOTTOM;
2876 break;
2877
2881 vjustify = GR_TEXT_V_ALIGN_CENTER;
2882 break;
2883
2887 vjustify = GR_TEXT_V_ALIGN_TOP;
2888 break;
2889 }
2890
2891 switch( justification )
2892 {
2893 default:
2898 hjustify = GR_TEXT_H_ALIGN_LEFT;
2899 break;
2900
2904 hjustify = GR_TEXT_H_ALIGN_CENTER;
2905 break;
2906
2910 hjustify = GR_TEXT_H_ALIGN_RIGHT;
2911 break;
2912 }
2913
2914 switch( orientation )
2915 {
2917 angle = ANGLE_HORIZONTAL;
2918 break;
2919
2921 hjustify *= -1;
2922 angle = ANGLE_HORIZONTAL;
2923 break;
2924
2926 angle = ANGLE_VERTICAL;
2927 break;
2928
2930 hjustify *= -1;
2931 angle = ANGLE_VERTICAL;
2932 break;
2933 }
2934
2935 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( vjustify ) );
2936 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( hjustify ) );
2937 text->SetTextAngle( angle );
2938}
2939
2940
2941// Altium text orientation and justification are in absolute (page) coordinates. KiCad stores
2942// field text properties relative to the parent symbol and applies the symbol's transform at
2943// render time. This function adjusts the field's stored text angle and justification to
2944// compensate for the symbol's orientation so that the final rendered appearance matches the
2945// original Altium layout.
2946//
2947// The compensation follows the same logic as SCH_FIELD::Rotate() but applied in the
2948// inverse direction to undo the symbol's rotation effect on text properties.
2950{
2951 bool isHorizontal = aField->GetTextAngle().IsHorizontal();
2952
2953 // Altium orientation 0 (RIGHTWARDS) maps to KiCad SYM_ORIENT_0 (identity). No rotation
2954 // compensation needed.
2955
2956 // Altium orientation 1 (UPWARDS) maps to KiCad SYM_ORIENT_90 (CCW). To compensate,
2957 // apply CW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CW rotation
2958 // of horizontal text flips justification; CW rotation of vertical text does not.
2959 if( aSymbol.orientation == 1 )
2960 {
2961 if( isHorizontal )
2962 {
2963 aField->SetHorizJustify(
2964 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2965 }
2966
2967 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2968 }
2969 // Altium orientation 2 (LEFTWARDS) maps to KiCad SYM_ORIENT_180 (two CCW rotations). The
2970 // transform [-1,0,0,-1] negates both X and Y, flipping horizontal justification. Apply
2971 // one correction for the full 180 degrees regardless of text angle.
2972 else if( aSymbol.orientation == 2 )
2973 {
2974 aField->SetHorizJustify(
2975 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2976 }
2977 // Altium orientation 3 (DOWNWARDS) maps to KiCad SYM_ORIENT_270 (CW). To compensate,
2978 // apply CCW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CCW rotation
2979 // of vertical text flips justification; CCW rotation of horizontal text does not.
2980 else if( aSymbol.orientation == 3 )
2981 {
2982 if( !isHorizontal )
2983 {
2984 aField->SetHorizJustify(
2985 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2986 }
2987
2988 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2989 }
2990
2991 // Mirror-Y in KiCad negates the X component of the bounding box, which effectively
2992 // flips horizontal justification. Compensate so the rendered text matches Altium.
2993 if( aSymbol.isMirrored )
2994 {
2995 aField->SetHorizJustify(
2996 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2997 }
2998}
2999
3000
3001// Altium text in symbols uses absolute orientation, but KiCad applies the symbol's transform
3002// to library body items via OrientAndMirrorSymbolItems at render time. This function pre-
3003// compensates the stored text angle and justification so that after the render-time transform,
3004// the final appearance matches the original Altium layout. Position is not adjusted here
3005// since GetRelativePosition already handles the positional component.
3007{
3008 int nRenderRotations = aSymbol.orientation % 4;
3009
3010 // Undo mirror first (reverse of render-time application order).
3011 // MirrorHorizontally on LAYER_DEVICE text flips H-justify when horizontal
3012 // and V-justify when vertical.
3013 if( aSymbol.isMirrored )
3014 {
3015 if( aText->GetTextAngle().IsHorizontal() )
3016 aText->FlipHJustify();
3017 else
3018 aText->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -aText->GetVertJustify() ) );
3019 }
3020
3021 // The render pipeline applies Rotate90(false) N times; undo with N inverse rotations.
3022 for( int i = 0; i < nRenderRotations; i++ )
3023 aText->Rotate90( true );
3024}
3025
3026
3028{
3029 // No component assigned -> Put on sheet
3030 if( aOwnerindex == ALTIUM_COMPONENT_NONE )
3031 return true;
3032
3033 // For a template -> Put on sheet so we can resolve variables
3034 if( m_altiumTemplates.find( aOwnerindex ) != m_altiumTemplates.end() )
3035 return true;
3036
3037 return false;
3038}
3039
3040
3041void SCH_IO_ALTIUM::ParseLabel( const std::map<wxString, wxString>& aProperties,
3042 std::vector<LIB_SYMBOL*>& aSymbol, std::vector<int>& aFontSizes )
3043{
3044 ASCH_LABEL elem( aProperties );
3045
3046 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3047 {
3048 static const std::map<wxString, wxString> variableMap = {
3049 { "SHEETNUMBER", "#" },
3050 { "SHEETTOTAL", "##" },
3051 { "TITLE", "TITLE" }, // including 1:1 maps makes it easier
3052 { "REVISION", "REVISION" }, // to see that the list is complete
3053 { "DATE", "ISSUE_DATE" },
3054 { "CURRENTDATE", "CURRENT_DATE" },
3055 { "CURRENTTIME", "CURRENT_TIME_LOCALE" },
3056 { "COMPANYNAME", "COMPANY" },
3057 { "DOCUMENTNAME", "FILENAME" },
3058 { "DOCUMENTFULLPATHANDNAME", "FILEPATH" },
3059 { "PROJECTNAME", "PROJECTNAME" },
3060 };
3061
3062 std::set<wxString> unmapped;
3063 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap, &unmapped );
3064 SCH_TEXT* textItem = new SCH_TEXT( elem.location + m_sheetOffset, kicadText );
3065
3066 // a name outside the map is Altium's own, KiCad cannot resolve it
3067 std::set<wxString> kicadNames;
3068
3069 for( const auto& [altiumName, kicadName] : variableMap )
3070 kicadNames.insert( kicadName );
3071
3072 for( const wxString& name : unmapped )
3073 {
3074 if( !kicadNames.count( name ) )
3075 m_sheetSpecialStrings.insert( name );
3076 }
3077
3078 SetTextPositioning( textItem, elem.justification, elem.orientation );
3079
3080 size_t fontId = static_cast<int>( elem.fontId );
3081
3082 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
3083 {
3084 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
3085 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
3086
3087 // Must come after SetTextSize()
3088 textItem->SetBold( font.Bold );
3089 textItem->SetItalic( font.Italic );
3090 }
3091
3092 textItem->SetFlags( IS_NEW );
3093
3094 SCH_SCREEN* screen = getCurrentScreen();
3095 wxCHECK( screen, /* void */ );
3096
3097 screen->Append( textItem );
3098 }
3099 else
3100 {
3101 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3102 SCH_SYMBOL* schsym = nullptr;
3103
3104 if( !symbol )
3105 {
3106 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3107
3108 if( libSymbolIt == m_libSymbols.end() )
3109 {
3110 // TODO: e.g. can depend on Template (RECORD=39
3111 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), elem.ownerindex ),
3113 return;
3114 }
3115
3116 symbol = libSymbolIt->second;
3117 schsym = m_symbols.at( libSymbolIt->first );
3118 }
3119
3120 VECTOR2I pos = elem.location;
3121 SCH_TEXT* textItem = new SCH_TEXT( { 0, 0 }, elem.text, LAYER_DEVICE );
3122 symbol->AddDrawItem( textItem, false );
3123
3124 if( symbol->GetBodyStyleCount() > 1 )
3125 {
3126 if( !aSymbol.empty() )
3127 {
3128 textItem->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3129 }
3130 else
3131 {
3132 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3133
3134 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3135 textItem->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3136 }
3137 }
3138
3140 if( schsym )
3141 pos = GetRelativePosition( elem.location + m_sheetOffset, schsym );
3142
3143 textItem->SetPosition( pos );
3144 textItem->SetUnit( std::max( 0, elem.ownerpartid ) );
3145 SetTextPositioning( textItem, elem.justification, elem.orientation );
3146
3147 if( schsym )
3148 {
3149 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
3150
3151 if( altiumSymIt != m_altiumComponents.end() )
3152 AdjustTextForSymbolOrientation( textItem, altiumSymIt->second );
3153 }
3154
3155 size_t fontId = elem.fontId;
3156
3157 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
3158 {
3159 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
3160 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
3161
3162 // Must come after SetTextSize()
3163 textItem->SetBold( font.Bold );
3164 textItem->SetItalic( font.Italic );
3165 }
3166 else if( fontId > 0 && fontId <= aFontSizes.size() )
3167 {
3168 int size = aFontSizes[fontId - 1];
3169 textItem->SetTextSize( { size, size } );
3170 }
3171 }
3172}
3173
3174
3175void SCH_IO_ALTIUM::ParseTextFrame( const std::map<wxString, wxString>& aProperties,
3176 std::vector<LIB_SYMBOL*>& aSymbol,
3177 std::vector<int>& aFontSizes )
3178{
3179 ASCH_TEXT_FRAME elem( aProperties );
3180
3181 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3182 AddTextBox( &elem );
3183 else
3184 AddLibTextBox( &elem, aSymbol, aFontSizes );
3185}
3186
3187
3188void SCH_IO_ALTIUM::ParseNote( const std::map<wxString, wxString>& aProperties )
3189{
3190 ASCH_NOTE elem( aProperties );
3191 AddTextBox( static_cast<ASCH_TEXT_FRAME*>( &elem ) );
3192
3193 // TODO: need some sort of property system for storing author....
3194}
3195
3196
3198{
3199 SCH_TEXTBOX* textBox = new SCH_TEXTBOX();
3200
3201 VECTOR2I sheetTopRight = aElem->TopRight + m_sheetOffset;
3202 VECTOR2I sheetBottomLeft = aElem->BottomLeft +m_sheetOffset;
3203
3204 textBox->SetStart( sheetTopRight );
3205 textBox->SetEnd( sheetBottomLeft );
3206
3207 textBox->SetText( AltiumEscapeTextVars( aElem->Text ) );
3208
3209 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
3210
3211 if( aElem->isSolid)
3213 else
3214 textBox->SetFilled( false );
3215
3216 if( aElem->ShowBorder )
3218 else
3220
3221 switch( aElem->Alignment )
3222 {
3223 default:
3226 break;
3229 break;
3232 break;
3233 }
3234
3235 size_t fontId = static_cast<int>( aElem->FontID );
3236
3237 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
3238 {
3239 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
3240 textBox->SetTextSize( { font.Size / 2, font.Size / 2 } );
3241
3242 // Must come after SetTextSize()
3243 textBox->SetBold( font.Bold );
3244 textBox->SetItalic( font.Italic );
3245 //textBox->SetFont( //how to set font, we have a font name here: ( font.fontname );
3246 }
3247
3248 textBox->SetFlags( IS_NEW );
3249
3250 SCH_SCREEN* screen = getCurrentScreen();
3251 wxCHECK( screen, /* void */ );
3252
3253 screen->Append( textBox );
3254}
3255
3256
3257void SCH_IO_ALTIUM::AddLibTextBox( const ASCH_TEXT_FRAME *aElem, std::vector<LIB_SYMBOL*>& aSymbol,
3258 std::vector<int>& aFontSizes )
3259{
3260 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3261 SCH_SYMBOL* schsym = nullptr;
3262
3263 if( !symbol )
3264 {
3265 const auto& libSymbolIt = m_libSymbols.find( aElem->ownerindex );
3266
3267 if( libSymbolIt == m_libSymbols.end() )
3268 {
3269 // TODO: e.g. can depend on Template (RECORD=39
3270 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), aElem->ownerindex ),
3272 return;
3273 }
3274
3275 symbol = libSymbolIt->second;
3276 schsym = m_symbols.at( libSymbolIt->first );
3277 }
3278
3279 SCH_TEXTBOX* textBox = new SCH_TEXTBOX( LAYER_DEVICE );
3280
3281 textBox->SetUnit( std::max( 0, aElem->ownerpartid ) );
3282 symbol->AddDrawItem( textBox, false );
3283
3284 if( symbol->GetBodyStyleCount() > 1 )
3285 {
3286 if( !aSymbol.empty() )
3287 {
3288 textBox->SetBodyStyle( aElem->ownerpartdisplaymode + 1 );
3289 }
3290 else
3291 {
3292 const auto& compIt = m_altiumComponents.find( aElem->ownerindex );
3293
3294 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3295 textBox->SetBodyStyle( aElem->ownerpartdisplaymode + 1 );
3296 }
3297 }
3298
3300 if( !schsym )
3301 {
3302 textBox->SetStart( aElem->TopRight );
3303 textBox->SetEnd( aElem->BottomLeft );
3304 }
3305 else
3306 {
3307 textBox->SetStart( GetRelativePosition( aElem->TopRight + m_sheetOffset, schsym ) );
3308 textBox->SetEnd( GetRelativePosition( aElem->BottomLeft + m_sheetOffset, schsym ) );
3309 }
3310
3311 textBox->SetText( AltiumEscapeTextVars( aElem->Text ) );
3312
3313 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
3314
3315 if( aElem->isSolid)
3317 else
3318 textBox->SetFilled( false );
3319
3320 if( aElem->ShowBorder )
3322 else
3323 textBox->SetStroke( STROKE_PARAMS( -1 ) );
3324
3325 switch( aElem->Alignment )
3326 {
3327 default:
3330 break;
3333 break;
3336 break;
3337 }
3338
3339 if( aElem->FontID > 0 && aElem->FontID <= static_cast<int>( aFontSizes.size() ) )
3340 {
3341 int size = aFontSizes[aElem->FontID - 1];
3342 textBox->SetTextSize( { size, size } );
3343 }
3344}
3345
3346
3347void SCH_IO_ALTIUM::ParseBezier( const std::map<wxString, wxString>& aProperties,
3348 std::vector<LIB_SYMBOL*>& aSymbol )
3349{
3350 ASCH_BEZIER elem( aProperties );
3351
3352 if( elem.points.size() < 2 )
3353 {
3354 m_errorMessages.emplace( wxString::Format( _( "Bezier has %d control points. At least 2 are expected." ),
3355 static_cast<int>( elem.points.size() ) ),
3357 return;
3358 }
3359
3360 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3361 {
3362 SCH_SCREEN* currentScreen = getCurrentScreen();
3363 wxCHECK( currentScreen, /* void */ );
3364
3365 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
3366 {
3367 if( i + 2 == elem.points.size() )
3368 {
3369 // special case: single line
3370 SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
3372
3373 line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
3375
3376 line->SetFlags( IS_NEW );
3377
3378 currentScreen->Append( line );
3379 }
3380 else
3381 {
3382 // simulate Bezier using line segments
3383 std::vector<VECTOR2I> bezierPoints;
3384 std::vector<VECTOR2I> polyPoints;
3385
3386 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
3387 bezierPoints.push_back( elem.points.at( j ) );
3388
3389 BEZIER_POLY converter( bezierPoints );
3390 converter.GetPoly( polyPoints );
3391
3392 for( size_t k = 0; k + 1 < polyPoints.size(); k++ )
3393 {
3394 SCH_LINE* line = new SCH_LINE( polyPoints.at( k ) + m_sheetOffset,
3396
3397 line->SetEndPoint( polyPoints.at( k + 1 ) + m_sheetOffset );
3399
3400 line->SetFlags( IS_NEW );
3401 currentScreen->Append( line );
3402 }
3403 }
3404 }
3405 }
3406 else
3407 {
3408 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3409 SCH_SYMBOL* schsym = nullptr;
3410
3411 if( !symbol )
3412 {
3413 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3414
3415 if( libSymbolIt == m_libSymbols.end() )
3416 {
3417 // TODO: e.g. can depend on Template (RECORD=39
3418 m_errorMessages.emplace( wxString::Format( wxT( "Bezier's owner (%d) not found." ),
3419 elem.ownerindex ),
3421 return;
3422 }
3423
3424 symbol = libSymbolIt->second;
3425 schsym = m_symbols.at( libSymbolIt->first );
3426 }
3427
3428 int bodyStyle = 0;
3429
3430 if( symbol->GetBodyStyleCount() > 1 )
3431 {
3432 if( !aSymbol.empty() )
3433 {
3434 bodyStyle = elem.ownerpartdisplaymode + 1;
3435 }
3436 else
3437 {
3438 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3439
3440 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3441 bodyStyle = elem.ownerpartdisplaymode + 1;
3442 }
3443 }
3444
3445 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
3446 {
3447 if( i + 2 == elem.points.size() )
3448 {
3449 // special case: single line
3451 symbol->AddDrawItem( line, false );
3452
3453 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3454
3455 if( bodyStyle > 0 )
3456 line->SetBodyStyle( bodyStyle );
3457
3458 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
3459 {
3460 VECTOR2I pos = elem.points.at( j );
3461
3462 if( schsym )
3463 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
3464
3465 line->AddPoint( pos );
3466 }
3467
3469 }
3470 else if( i + 3 == elem.points.size() )
3471 {
3472 // TODO: special case of a single line with an extra point?
3473 // I haven't a clue what this is all about, but the sample document we have in
3474 // https://gitlab.com/kicad/code/kicad/-/issues/8974 responds best by treating it
3475 // as another single line special case.
3477 symbol->AddDrawItem( line, false );
3478
3479 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3480
3481 if( bodyStyle > 0 )
3482 line->SetBodyStyle( bodyStyle );
3483
3484 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
3485 {
3486 VECTOR2I pos = elem.points.at( j );
3487
3488 if( schsym )
3489 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
3490
3491 line->AddPoint( pos );
3492 }
3493
3495 }
3496 else
3497 {
3498 // Bezier always has exactly 4 control points
3500 symbol->AddDrawItem( bezier, false );
3501
3502 bezier->SetUnit( std::max( 0, elem.ownerpartid ) );
3503
3504 if( bodyStyle > 0 )
3505 bezier->SetBodyStyle( bodyStyle );
3506
3507 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
3508 {
3509 VECTOR2I pos = elem.points.at( j );
3510
3511 if( schsym )
3512 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
3513
3514 switch( j - i )
3515 {
3516 case 0: bezier->SetStart( pos ); break;
3517 case 1: bezier->SetBezierC1( pos ); break;
3518 case 2: bezier->SetBezierC2( pos ); break;
3519 case 3: bezier->SetEnd( pos ); break;
3520 default: break; // Can't get here but silence warnings
3521 }
3522 }
3523
3526 }
3527 }
3528 }
3529}
3530
3531
3532void SCH_IO_ALTIUM::ParsePolyline( const std::map<wxString, wxString>& aProperties,
3533 std::vector<LIB_SYMBOL*>& aSymbol )
3534{
3535 ASCH_POLYLINE elem( aProperties );
3536
3537 if( elem.Points.size() < 2 )
3538 return;
3539
3540 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3541 {
3542 SCH_SCREEN* screen = getCurrentScreen();
3543 wxCHECK( screen, /* void */ );
3544
3545 for( size_t i = 1; i < elem.Points.size(); i++ )
3546 {
3547 SCH_LINE* line = new SCH_LINE;
3548
3549 line->SetStartPoint( elem.Points[i - 1] + m_sheetOffset );
3550 line->SetEndPoint( elem.Points[i] + m_sheetOffset );
3551
3553 GetColorFromInt( elem.Color ) ) );
3554
3555 line->SetFlags( IS_NEW );
3556
3557 screen->Append( line );
3558 }
3559 }
3560 else
3561 {
3562 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3563 SCH_SYMBOL* schsym = nullptr;
3564
3565 if( !symbol )
3566 {
3567 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3568
3569 if( libSymbolIt == m_libSymbols.end() )
3570 {
3571 // TODO: e.g. can depend on Template (RECORD=39
3572 m_errorMessages.emplace( wxString::Format( wxT( "Polyline's owner (%d) not found." ),
3573 elem.ownerindex ),
3575 return;
3576 }
3577
3578 symbol = libSymbolIt->second;
3579 schsym = m_symbols.at( libSymbolIt->first );
3580 }
3581
3583 symbol->AddDrawItem( line, false );
3584
3585 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3586
3587 if( symbol->GetBodyStyleCount() > 1 )
3588 {
3589 if( !aSymbol.empty() )
3590 {
3591 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3592 }
3593 else
3594 {
3595 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3596
3597 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3598 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3599 }
3600 }
3601
3602 for( VECTOR2I point : elem.Points )
3603 {
3604 if( schsym )
3605 point = GetRelativePosition( point + m_sheetOffset, schsym );
3606
3607 line->AddPoint( point );
3608 }
3609
3611 STROKE_PARAMS stroke = line->GetStroke();
3612 stroke.SetLineStyle( GetPlotDashType( elem.LineStyle ) );
3613
3614 line->SetStroke( stroke );
3615 }
3616}
3617
3618
3619void SCH_IO_ALTIUM::ParsePolygon( const std::map<wxString, wxString>& aProperties,
3620 std::vector<LIB_SYMBOL*>& aSymbol )
3621{
3622 ASCH_POLYGON elem( aProperties );
3623
3624 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3625 {
3626 SCH_SCREEN* screen = getCurrentScreen();
3627 wxCHECK( screen, /* void */ );
3628
3629 SCH_SHAPE* poly = new SCH_SHAPE( SHAPE_T::POLY );
3630
3631 for( VECTOR2I& point : elem.points )
3632 poly->AddPoint( point + m_sheetOffset );
3633 poly->AddPoint( elem.points.front() + m_sheetOffset );
3634
3635 SetSchShapeLine( elem, poly );
3636 SetSchShapeFillAndColor( elem, poly );
3637 poly->SetFlags( IS_NEW );
3638
3639 screen->Append( poly );
3640 }
3641 else
3642 {
3643 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3644 SCH_SYMBOL* schsym = nullptr;
3645
3646 if( !symbol )
3647 {
3648 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3649
3650 if( libSymbolIt == m_libSymbols.end() )
3651 {
3652 // TODO: e.g. can depend on Template (RECORD=39
3653 m_errorMessages.emplace( wxString::Format( wxT( "Polygon's owner (%d) not found." ),
3654 elem.ownerindex ),
3656 return;
3657 }
3658
3659 symbol = libSymbolIt->second;
3660 schsym = m_symbols.at( libSymbolIt->first );
3661 }
3662
3664
3665 symbol->AddDrawItem( line, false );
3666 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3667
3668 if( symbol->GetBodyStyleCount() > 1 )
3669 {
3670 if( !aSymbol.empty() )
3671 {
3672 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3673 }
3674 else
3675 {
3676 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3677
3678 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3679 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3680 }
3681 }
3682
3683 for( VECTOR2I point : elem.points )
3684 {
3685 if( schsym )
3686 point = GetRelativePosition( point + m_sheetOffset, schsym );
3687
3688 line->AddPoint( point );
3689 }
3690
3691 VECTOR2I point = elem.points.front();
3692
3693 if( schsym )
3694 point = GetRelativePosition( elem.points.front() + m_sheetOffset, schsym );
3695
3696 line->AddPoint( point );
3697
3700
3701 if( line->GetFillColor() == line->GetStroke().GetColor()
3702 && line->GetFillMode() != FILL_T::NO_FILL )
3703 {
3704 STROKE_PARAMS stroke = line->GetStroke();
3705 stroke.SetWidth( -1 );
3706 line->SetStroke( stroke );
3707 }
3708 }
3709}
3710
3711
3712void SCH_IO_ALTIUM::ParseRoundRectangle( const std::map<wxString, wxString>& aProperties,
3713 std::vector<LIB_SYMBOL*>& aSymbol )
3714{
3715 ASCH_ROUND_RECTANGLE elem( aProperties );
3716
3717 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3718 {
3719 SCH_SCREEN* screen = getCurrentScreen();
3720 wxCHECK( screen, /* void */ );
3721
3722 // TODO: misses rounded edges
3723 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
3724
3725 rect->SetPosition( elem.TopRight + m_sheetOffset );
3726 rect->SetEnd( elem.BottomLeft + m_sheetOffset );
3727 SetSchShapeLine( elem, rect );
3728 SetSchShapeFillAndColor( elem, rect );
3729 rect->SetFlags( IS_NEW );
3730
3731 screen->Append( rect );
3732 }
3733 else
3734 {
3735 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3736 SCH_SYMBOL* schsym = nullptr;
3737
3738 if( !symbol )
3739 {
3740 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3741
3742 if( libSymbolIt == m_libSymbols.end() )
3743 {
3744 // TODO: e.g. can depend on Template (RECORD=39
3745 m_errorMessages.emplace( wxString::Format( wxT( "Rounded rectangle's owner (%d) not found." ),
3746 elem.ownerindex ),
3748 return;
3749 }
3750
3751 symbol = libSymbolIt->second;
3752 schsym = m_symbols.at( libSymbolIt->first );
3753 }
3754
3755 SCH_SHAPE* rect = nullptr;
3756
3757 int width = std::abs( elem.TopRight.x - elem.BottomLeft.x );
3758 int height = std::abs( elem.TopRight.y - elem.BottomLeft.y );
3759
3760 // If it is a circle, make it a circle
3761 if( std::abs( elem.CornerRadius.x ) >= width / 2
3762 && std::abs( elem.CornerRadius.y ) >= height / 2 )
3763 {
3764 rect = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
3765
3766 VECTOR2I center = ( elem.TopRight + elem.BottomLeft ) / 2;
3767 int radius = std::min( width / 2, height / 2 );
3768
3769 if( schsym )
3771
3772 rect->SetPosition( center );
3773 rect->SetEnd( VECTOR2I( rect->GetPosition().x + radius, rect->GetPosition().y ) );
3774 }
3775 else
3776 {
3778
3779 if( !schsym )
3780 {
3781 rect->SetPosition( elem.TopRight );
3782 rect->SetEnd( elem.BottomLeft );
3783 }
3784 else
3785 {
3786 rect->SetPosition( GetRelativePosition( elem.TopRight + m_sheetOffset, schsym ) );
3787 rect->SetEnd( GetRelativePosition( elem.BottomLeft + m_sheetOffset, schsym ) );
3788 }
3789
3790 rect->Normalize();
3791 }
3792
3795
3796 symbol->AddDrawItem( rect, false );
3797 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
3798
3799 if( symbol->GetBodyStyleCount() > 1 )
3800 {
3801 if( !aSymbol.empty() )
3802 {
3803 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3804 }
3805 else
3806 {
3807 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3808
3809 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3810 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3811 }
3812 }
3813 }
3814}
3815
3816
3817void SCH_IO_ALTIUM::ParseArc( const std::map<wxString, wxString>& aProperties,
3818 std::vector<LIB_SYMBOL*>& aSymbol )
3819{
3820 ASCH_ARC elem( aProperties );
3821
3822 int arc_radius = elem.m_Radius;
3823 VECTOR2I center = elem.m_Center;
3824 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
3825 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
3826 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
3827 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
3828
3829 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3830 {
3831 SCH_SCREEN* currentScreen = getCurrentScreen();
3832 wxCHECK( currentScreen, /* void */ );
3833
3834 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3835 {
3837
3838 circle->SetPosition( elem.m_Center + m_sheetOffset );
3839 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
3840
3841 SetSchShapeLine( elem, circle );
3843
3844 currentScreen->Append( circle );
3845 }
3846 else
3847 {
3848 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
3849
3850 arc->SetCenter( elem.m_Center + m_sheetOffset );
3851 arc->SetStart( elem.m_Center + startOffset + m_sheetOffset );
3852 arc->SetEnd( elem.m_Center + endOffset + m_sheetOffset );
3853
3854 SetSchShapeLine( elem, arc );
3855 SetSchShapeFillAndColor( elem, arc );
3856
3857 currentScreen->Append( arc );
3858 }
3859 }
3860 else
3861 {
3862 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3863 SCH_SYMBOL* schsym = nullptr;
3864
3865 if( !symbol )
3866 {
3867 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3868
3869 if( libSymbolIt == m_libSymbols.end() )
3870 {
3871 // TODO: e.g. can depend on Template (RECORD=39
3872 m_errorMessages.emplace( wxString::Format( wxT( "Arc's owner (%d) not found." ), elem.ownerindex ),
3874 return;
3875 }
3876
3877 symbol = libSymbolIt->second;
3878 schsym = m_symbols.at( libSymbolIt->first );
3879 }
3880
3881 int bodyStyle = 0;
3882
3883 if( symbol->GetBodyStyleCount() > 1 )
3884 {
3885 if( !aSymbol.empty() )
3886 {
3887 bodyStyle = elem.ownerpartdisplaymode + 1;
3888 }
3889 else
3890 {
3891 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3892
3893 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3894 bodyStyle = elem.ownerpartdisplaymode + 1;
3895 }
3896 }
3897
3898 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3899 {
3901 symbol->AddDrawItem( circle, false );
3902
3903 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
3904
3905 if( bodyStyle > 0 )
3906 circle->SetBodyStyle( bodyStyle );
3907
3908 if( schsym )
3910
3911 circle->SetPosition( center );
3912
3913 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
3916 }
3917 else
3918 {
3920 symbol->AddDrawItem( arc, false );
3921 arc->SetUnit( std::max( 0, elem.ownerpartid ) );
3922
3923 if( bodyStyle > 0 )
3924 arc->SetBodyStyle( bodyStyle );
3925
3926 if( schsym )
3927 {
3929 startOffset = GetRelativePosition( elem.m_Center + startOffset + m_sheetOffset, schsym )
3930 - center;
3931 endOffset = GetRelativePosition( elem.m_Center + endOffset + m_sheetOffset, schsym )
3932 - center;
3933 }
3934
3935 arc->SetCenter( center );
3936 arc->SetStart( center + startOffset );
3937 arc->SetEnd( center + endOffset );
3938
3941 }
3942 }
3943}
3944
3945
3946void SCH_IO_ALTIUM::ParseEllipticalArc( const std::map<wxString, wxString>& aProperties,
3947 std::vector<LIB_SYMBOL*>& aSymbol )
3948{
3949 ASCH_ARC elem( aProperties );
3950
3951 if( elem.m_Radius == elem.m_SecondaryRadius && elem.m_StartAngle == 0
3952 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3953 {
3954 ParseCircle( aProperties, aSymbol );
3955 return;
3956 }
3957
3958 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3959 {
3960 SCH_SCREEN* currentScreen = getCurrentScreen();
3961 wxCHECK( currentScreen, /* void */ );
3962
3963 ELLIPSE<int> ellipse( elem.m_Center + m_sheetOffset, elem.m_Radius,
3966 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
3967 std::vector<BEZIER<int>> beziers;
3968
3969 TransformEllipseToBeziers( ellipse, beziers );
3970
3971 for( const BEZIER<int>& bezier : beziers )
3972 {
3973 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
3974 schbezier->SetStart( bezier.Start );
3975 schbezier->SetBezierC1( bezier.C1 );
3976 schbezier->SetBezierC2( bezier.C2 );
3977 schbezier->SetEnd( bezier.End );
3978 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
3980
3981 currentScreen->Append( schbezier );
3982 }
3983 }
3984 else
3985 {
3986 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3987 SCH_SYMBOL* schsym = nullptr;
3988
3989 if( !symbol )
3990 {
3991 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3992
3993 if( libSymbolIt == m_libSymbols.end() )
3994 {
3995 // TODO: e.g. can depend on Template (RECORD=39
3996 m_errorMessages.emplace( wxString::Format( wxT( "Elliptical Arc's owner (%d) not found." ),
3997 elem.ownerindex ),
3999 return;
4000 }
4001
4002 symbol = libSymbolIt->second;
4003 schsym = m_symbols.at( libSymbolIt->first );
4004 }
4005
4006 int bodyStyle = 0;
4007
4008 if( symbol->GetBodyStyleCount() > 1 )
4009 {
4010 if( !aSymbol.empty() )
4011 {
4012 bodyStyle = elem.ownerpartdisplaymode + 1;
4013 }
4014 else
4015 {
4016 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
4017
4018 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
4019 bodyStyle = elem.ownerpartdisplaymode + 1;
4020 }
4021 }
4022
4023 ELLIPSE<int> ellipse( elem.m_Center, elem.m_Radius,
4026 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
4027 std::vector<BEZIER<int>> beziers;
4028
4029 TransformEllipseToBeziers( ellipse, beziers );
4030
4031 for( const BEZIER<int>& bezier : beziers )
4032 {
4033 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
4034 symbol->AddDrawItem( schbezier, false );
4035
4036 schbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
4037
4038 if( bodyStyle > 0 )
4039 schbezier->SetBodyStyle( bodyStyle );
4040
4041 if( schsym )
4042 {
4043 schbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
4044 schbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
4045 schbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
4046 schbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
4047 }
4048 else
4049 {
4050 schbezier->SetStart( bezier.Start );
4051 schbezier->SetBezierC1( bezier.C1 );
4052 schbezier->SetBezierC2( bezier.C2 );
4053 schbezier->SetEnd( bezier.End );
4054 }
4055
4058 }
4059 }
4060}
4061
4062
4063void SCH_IO_ALTIUM::ParsePieChart( const std::map<wxString, wxString>& aProperties,
4064 std::vector<LIB_SYMBOL*>& aSymbol )
4065{
4066 ParseArc( aProperties, aSymbol );
4067
4068 ASCH_PIECHART elem( aProperties );
4069
4070 int arc_radius = elem.m_Radius;
4071 VECTOR2I center = elem.m_Center;
4072 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
4073 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
4074 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
4075 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
4076
4077 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
4078 {
4079 SCH_SCREEN* screen = getCurrentScreen();
4080 wxCHECK( screen, /* void */ );
4081
4082 // close polygon
4084 line->SetEndPoint( center + startOffset + m_sheetOffset );
4086
4087 line->SetFlags( IS_NEW );
4088 screen->Append( line );
4089
4091 line->SetEndPoint( center + endOffset + m_sheetOffset );
4093
4094 line->SetFlags( IS_NEW );
4095 screen->Append( line );
4096 }
4097 else
4098 {
4099 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
4100 SCH_SYMBOL* schsym = nullptr;
4101
4102 if( !symbol )
4103 {
4104 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4105
4106 if( libSymbolIt == m_libSymbols.end() )
4107 {
4108 // TODO: e.g. can depend on Template (RECORD=39
4109 m_errorMessages.emplace( wxString::Format( wxT( "Piechart's owner (%d) not found." ),
4110 elem.ownerindex ),
4112 return;
4113 }
4114
4115 symbol = libSymbolIt->second;
4116 schsym = m_symbols.at( libSymbolIt->first );
4117 }
4118
4120 symbol->AddDrawItem( line, false );
4121
4122 line->SetUnit( std::max( 0, elem.ownerpartid ) );
4123
4124 if( symbol->GetBodyStyleCount() > 1 )
4125 {
4126 if( !aSymbol.empty() )
4127 {
4128 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4129 }
4130 else
4131 {
4132 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
4133
4134 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
4135 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4136 }
4137 }
4138
4139 if( !schsym )
4140 {
4141 line->AddPoint( center + startOffset );
4142 line->AddPoint( center );
4143 line->AddPoint( center + endOffset );
4144 }
4145 else
4146 {
4147 line->AddPoint( GetRelativePosition( center + startOffset + m_sheetOffset, schsym ) );
4148 line->AddPoint( GetRelativePosition( center + m_sheetOffset, schsym ) );
4149 line->AddPoint( GetRelativePosition( center + endOffset + m_sheetOffset, schsym ) );
4150 }
4151
4153 }
4154}
4155
4156
4157void SCH_IO_ALTIUM::ParseEllipse( const std::map<wxString, wxString>& aProperties,
4158 std::vector<LIB_SYMBOL*>& aSymbol )
4159{
4160 ASCH_ELLIPSE elem( aProperties );
4161
4162 if( elem.Radius == elem.SecondaryRadius )
4163 {
4164 ParseCircle( aProperties, aSymbol );
4165 return;
4166 }
4167
4168 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
4169 {
4170 SCH_SCREEN* screen = getCurrentScreen();
4171 wxCHECK( screen, /* void */ );
4172
4173 COLOR4D fillColor = GetColorFromInt( elem.AreaColor );
4174
4175 if( elem.IsTransparent )
4176 fillColor = fillColor.WithAlpha( 0.5 );
4177
4179
4180 ELLIPSE<int> ellipse( elem.Center + m_sheetOffset, elem.Radius,
4181 KiROUND( elem.SecondaryRadius ), ANGLE_0 );
4182
4183 std::vector<BEZIER<int>> beziers;
4184 std::vector<VECTOR2I> polyPoints;
4185
4186 TransformEllipseToBeziers( ellipse, beziers );
4187
4188 for( const BEZIER<int>& bezier : beziers )
4189 {
4190 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
4191 schbezier->SetStart( bezier.Start );
4192 schbezier->SetBezierC1( bezier.C1 );
4193 schbezier->SetBezierC2( bezier.C2 );
4194 schbezier->SetEnd( bezier.End );
4195 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
4196 schbezier->SetFillColor( fillColor );
4197 schbezier->SetFillMode( fillMode );
4198
4200 screen->Append( schbezier );
4201
4202 polyPoints.push_back( bezier.Start );
4203 }
4204
4205 if( fillMode != FILL_T::NO_FILL )
4206 {
4207 SCH_SHAPE* schpoly = new SCH_SHAPE( SHAPE_T::POLY );
4208 schpoly->SetFillColor( fillColor );
4209 schpoly->SetFillMode( fillMode );
4210 schpoly->SetWidth( -1 );
4211
4212 for( const VECTOR2I& point : polyPoints )
4213 schpoly->AddPoint( point );
4214
4215 schpoly->AddPoint( polyPoints[0] );
4216
4217 screen->Append( schpoly );
4218 }
4219 }
4220 else
4221 {
4222 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
4223 SCH_SYMBOL* schsym = nullptr;
4224
4225 if( !symbol )
4226 {
4227 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4228
4229 if( libSymbolIt == m_libSymbols.end() )
4230 {
4231 // TODO: e.g. can depend on Template (RECORD=39
4232 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
4234 return;
4235 }
4236
4237 symbol = libSymbolIt->second;
4238 schsym = m_symbols.at( libSymbolIt->first );
4239 }
4240
4241 int bodyStyle = 0;
4242
4243 if( symbol->GetBodyStyleCount() > 1 )
4244 {
4245 if( !aSymbol.empty() )
4246 {
4247 bodyStyle = elem.ownerpartdisplaymode + 1;
4248 }
4249 else
4250 {
4251 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
4252
4253 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
4254 bodyStyle = elem.ownerpartdisplaymode + 1;
4255 }
4256 }
4257
4258 ELLIPSE<int> ellipse( elem.Center, elem.Radius, KiROUND( elem.SecondaryRadius ),
4259 ANGLE_0 );
4260
4261 std::vector<BEZIER<int>> beziers;
4262 std::vector<VECTOR2I> polyPoints;
4263
4264 TransformEllipseToBeziers( ellipse, beziers );
4265
4266 for( const BEZIER<int>& bezier : beziers )
4267 {
4268 SCH_SHAPE* libbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
4269 symbol->AddDrawItem( libbezier, false );
4270 libbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
4271
4272 if( bodyStyle > 0 )
4273 libbezier->SetBodyStyle( bodyStyle );
4274
4275 if( !schsym )
4276 {
4277 libbezier->SetStart( bezier.Start );
4278 libbezier->SetBezierC1( bezier.C1 );
4279 libbezier->SetBezierC2( bezier.C2 );
4280 libbezier->SetEnd( bezier.End );
4281 }
4282 else
4283 {
4284 libbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
4285 libbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
4286 libbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
4287 libbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
4288 }
4289
4290 SetLibShapeLine( elem, libbezier, ALTIUM_SCH_RECORD::ELLIPSE );
4293
4294 polyPoints.push_back( libbezier->GetStart() );
4295 }
4296
4297 // A series of beziers won't fill the center, so if this is meant to be fully filled,
4298 // Add a polygon to fill the center
4299 if( elem.IsSolid )
4300 {
4301 SCH_SHAPE* libline = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
4302 symbol->AddDrawItem( libline, false );
4303 libline->SetUnit( std::max( 0, elem.ownerpartid ) );
4304
4305 if( bodyStyle > 0 )
4306 libline->SetBodyStyle( bodyStyle );
4307
4308 for( const VECTOR2I& point : polyPoints )
4309 libline->AddPoint( point );
4310
4311 libline->AddPoint( polyPoints[0] );
4312
4313 libline->SetWidth( -1 );
4315 }
4316 }
4317}
4318
4319
4320void SCH_IO_ALTIUM::ParseCircle( const std::map<wxString, wxString>& aProperties,
4321 std::vector<LIB_SYMBOL*>& aSymbol )
4322{
4323 ASCH_ELLIPSE elem( aProperties );
4324
4325 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
4326 {
4327 SCH_SCREEN* screen = getCurrentScreen();
4328 wxCHECK( screen, /* void */ );
4329
4331
4332 circle->SetPosition( elem.Center + m_sheetOffset );
4333 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
4334 circle->SetStroke( STROKE_PARAMS( 1, LINE_STYLE::SOLID ) );
4335
4336 circle->SetFillColor( GetColorFromInt( elem.AreaColor ) );
4337
4338 if( elem.IsSolid )
4339 circle->SetFillMode( FILL_T::FILLED_WITH_COLOR );
4340 else
4341 circle->SetFilled( false );
4342
4343 screen->Append( circle );
4344 }
4345 else
4346 {
4347 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
4348 SCH_SYMBOL* schsym = nullptr;
4349
4350 if( !symbol )
4351 {
4352 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4353
4354 if( libSymbolIt == m_libSymbols.end() )
4355 {
4356 // TODO: e.g. can depend on Template (RECORD=39
4357 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
4359 return;
4360 }
4361
4362 symbol = libSymbolIt->second;
4363 schsym = m_symbols.at( libSymbolIt->first );
4364 }
4365
4366 VECTOR2I center = elem.Center;
4368 symbol->AddDrawItem( circle, false );
4369
4370 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
4371
4372 if( symbol->GetBodyStyleCount() > 1 )
4373 {
4374 if( !aSymbol.empty() )
4375 {
4376 circle->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4377 }
4378 else
4379 {
4380 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
4381
4382 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
4383 circle->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4384 }
4385 }
4386
4387 if( schsym )
4389
4390 circle->SetPosition( center );
4391 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
4392
4395 }
4396}
4397
4398
4399void SCH_IO_ALTIUM::ParseLine( const std::map<wxString, wxString>& aProperties,
4400 std::vector<LIB_SYMBOL*>& aSymbol )
4401{
4402 ASCH_LINE elem( aProperties );
4403
4404 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
4405 {
4406 SCH_SCREEN* screen = getCurrentScreen();
4407 wxCHECK( screen, /* void */ );
4408
4409 // close polygon
4411 line->SetEndPoint( elem.point2 + m_sheetOffset );
4413 GetColorFromInt( elem.Color ) ) );
4414
4415 line->SetFlags( IS_NEW );
4416 screen->Append( line );
4417 }
4418 else
4419 {
4420 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
4421 SCH_SYMBOL* schsym = nullptr;
4422
4423 if( !symbol )
4424 {
4425 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4426
4427 if( libSymbolIt == m_libSymbols.end() )
4428 {
4429 // TODO: e.g. can depend on Template (RECORD=39
4430 m_errorMessages.emplace( wxString::Format( wxT( "Line's owner (%d) not found." ), elem.ownerindex ),
4432 return;
4433 }
4434
4435 symbol = libSymbolIt->second;
4436 schsym = m_symbols.at( libSymbolIt->first );
4437 }
4438
4440 symbol->AddDrawItem( line, false );
4441
4442 line->SetUnit( std::max( 0, elem.ownerpartid ) );
4443
4444 if( symbol->GetBodyStyleCount() > 1 )
4445 {
4446 if( !aSymbol.empty() )
4447 {
4448 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4449 }
4450 else
4451 {
4452 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
4453
4454 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
4455 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4456 }
4457 }
4458
4459 if( !schsym )
4460 {
4461 line->AddPoint( elem.point1 );
4462 line->AddPoint( elem.point2 );
4463 }
4464 else
4465 {
4466 line->AddPoint( GetRelativePosition( elem.point1 + m_sheetOffset, schsym ) );
4467 line->AddPoint( GetRelativePosition( elem.point2 + m_sheetOffset, schsym ) );
4468 }
4469
4471 line->SetLineStyle( GetPlotDashType( elem.LineStyle ) );
4472 }
4473}
4474
4475
4476void SCH_IO_ALTIUM::ParseSignalHarness( const std::map<wxString, wxString>& aProperties )
4477{
4478 ASCH_SIGNAL_HARNESS elem( aProperties );
4479
4480 if( ShouldPutItemOnSheet( elem.ownerindex ) )
4481 {
4482 SCH_SCREEN* screen = getCurrentScreen();
4483 wxCHECK( screen, /* void */ );
4484
4485 for( size_t ii = 0; ii < elem.points.size() - 1; ii++ )
4486 {
4488 line->SetEndPoint( elem.points[ii + 1] + m_sheetOffset );
4490
4491 line->SetFlags( IS_NEW );
4492 screen->Append( line );
4493 }
4494 }
4495 else
4496 {
4497 // No clue if this situation can ever exist
4498 m_errorMessages.emplace( wxT( "Signal harness, belonging to the part is not currently supported." ),
4500 }
4501}
4502
4503
4504void SCH_IO_ALTIUM::ParseHarnessConnector( int aIndex, const std::map<wxString,
4505 wxString>& aProperties )
4506{
4507 ASCH_HARNESS_CONNECTOR elem( aProperties );
4508
4509 if( ShouldPutItemOnSheet( elem.ownerindex ) )
4510 {
4512 auto [it, _] = m_altiumHarnesses.insert( { m_harnessEntryParent, HARNESS()} );
4513
4514 HARNESS& harness = it->second;
4515 HARNESS::HARNESS_PORT& port = harness.m_entry;
4516 harness.m_location = elem.m_location + m_sheetOffset;
4517 harness.m_size = elem.m_size;
4518
4519 VECTOR2I pos = elem.m_location + m_sheetOffset;
4520 VECTOR2I size = elem.m_size;
4521
4522 switch( elem.m_harnessConnectorSide )
4523 {
4524 default:
4526 port.m_location = { pos.x, pos.y + elem.m_primaryConnectionPosition };
4527 break;
4529 port.m_location = { pos.x + size.x, pos.y + elem.m_primaryConnectionPosition };
4530 break;
4532 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y };
4533 break;
4535 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y + size.y };
4536 break;
4537 }
4538 }
4539 else
4540 {
4541 // I have no clue if this situation can ever exist
4542 m_errorMessages.emplace( wxT( "Harness connector, belonging to the part is not currently supported." ),
4544 }
4545}
4546
4547
4548void SCH_IO_ALTIUM::ParseHarnessEntry( const std::map<wxString, wxString>& aProperties )
4549{
4550 ASCH_HARNESS_ENTRY elem( aProperties );
4551
4552 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
4553
4554 if( harnessIt == m_altiumHarnesses.end() )
4555 {
4556 m_errorMessages.emplace( wxString::Format( wxT( "Harness entry's parent (%d) not found." ),
4559 return;
4560 }
4561
4562 HARNESS& harness = harnessIt->second;
4564 port.m_name = elem.Name;
4565 port.m_harnessConnectorSide = elem.Side;
4567
4568 VECTOR2I pos = harness.m_location;
4569 VECTOR2I size = harness.m_size;
4570 int quadrant = 1;
4571
4572 switch( elem.Side )
4573 {
4574 default:
4576 port.m_location = { pos.x, pos.y + elem.DistanceFromTop };
4577 break;
4579 quadrant = 4;
4580 port.m_location = { pos.x + size.x, pos.y + elem.DistanceFromTop };
4581 break;
4583 port.m_location = { pos.x + elem.DistanceFromTop, pos.y };
4584 break;
4586 quadrant = 2;
4587 port.m_location = { pos.x + elem.DistanceFromTop, pos.y + size.y };
4588 break;
4589 }
4590
4591
4592 SCH_SCREEN* screen = getCurrentScreen();
4593 wxCHECK( screen, /* void */ );
4594
4595 SCH_BUS_WIRE_ENTRY* entry = new SCH_BUS_WIRE_ENTRY( port.m_location, quadrant );
4596 port.m_entryLocation = entry->GetPosition() + entry->GetSize();
4597 entry->SetFlags( IS_NEW );
4598 screen->Append( entry );
4599 harness.m_ports.emplace_back( port );
4600}
4601
4602
4603void SCH_IO_ALTIUM::ParseHarnessType( const std::map<wxString, wxString>& aProperties )
4604{
4605 ASCH_HARNESS_TYPE elem( aProperties );
4606
4607 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
4608
4609 if( harnessIt == m_altiumHarnesses.end() )
4610 {
4611 m_errorMessages.emplace( wxString::Format( wxT( "Harness type's parent (%d) not found." ),
4614 return;
4615 }
4616
4617 HARNESS& harness = harnessIt->second;
4618 harness.m_name = elem.Text;
4619}
4620
4621
4622void SCH_IO_ALTIUM::ParseRectangle( const std::map<wxString, wxString>& aProperties,
4623 std::vector<LIB_SYMBOL*>& aSymbol )
4624{
4625 ASCH_RECTANGLE elem( aProperties );
4626
4627 VECTOR2I sheetTopRight = elem.TopRight + m_sheetOffset;
4628 VECTOR2I sheetBottomLeft = elem.BottomLeft + m_sheetOffset;
4629
4630 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
4631 {
4632 SCH_SCREEN* screen = getCurrentScreen();
4633 wxCHECK( screen, /* void */ );
4634
4635 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
4636
4637 rect->SetPosition( sheetTopRight );
4638 rect->SetEnd( sheetBottomLeft );
4639 SetSchShapeLine( elem, rect );
4640 SetSchShapeFillAndColor( elem, rect );
4641 rect->SetFlags( IS_NEW );
4642
4643 screen->Append( rect );
4644 }
4645 else
4646 {
4647 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
4648 SCH_SYMBOL* schsym = nullptr;
4649
4650 if( !symbol )
4651 {
4652 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4653
4654 if( libSymbolIt == m_libSymbols.end() )
4655 {
4656 // TODO: e.g. can depend on Template (RECORD=39
4657 m_errorMessages.emplace( wxString::Format( wxT( "Rectangle's owner (%d) not found." ),
4658 elem.ownerindex ),
4660 return;
4661 }
4662
4663 symbol = libSymbolIt->second;
4664 schsym = m_symbols.at( libSymbolIt->first );
4665 }
4666
4668 symbol->AddDrawItem( rect, false );
4669
4670 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
4671
4672 if( symbol->GetBodyStyleCount() > 1 )
4673 {
4674 if( !aSymbol.empty() )
4675 {
4676 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4677 }
4678 else
4679 {
4680 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
4681
4682 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
4683 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4684 }
4685 }
4686
4687 if( !schsym )
4688 {
4689 rect->SetPosition( sheetTopRight );
4690 rect->SetEnd( sheetBottomLeft );
4691 }
4692 else
4693 {
4694 rect->SetPosition( GetRelativePosition( sheetTopRight, schsym ) );
4695 rect->SetEnd( GetRelativePosition( sheetBottomLeft, schsym ) );
4696 }
4697
4700 }
4701}
4702
4703
4704void SCH_IO_ALTIUM::ParseSheetSymbol( int aIndex, const std::map<wxString, wxString>& aProperties )
4705{
4706 ASCH_SHEET_SYMBOL elem( aProperties );
4707
4708 SCH_SHEET* sheet = new SCH_SHEET( getCurrentSheet(), elem.location + m_sheetOffset, elem.size );
4709
4710 sheet->SetBorderColor( GetColorFromInt( elem.color ) );
4711
4712 if( elem.isSolid )
4714
4715 sheet->SetFlags( IS_NEW );
4716
4717 SCH_SCREEN* currentScreen = getCurrentScreen();
4718 wxCHECK( currentScreen, /* void */ );
4719 currentScreen->Append( sheet );
4720
4721 SCH_SHEET_PATH sheetpath = m_sheetPath;
4722 sheetpath.push_back( sheet );
4723
4724 // We'll update later if we find a pageNumber record for it.
4725 sheetpath.SetPageNumber( "#" );
4726
4727 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
4728 wxCHECK( rootScreen, /* void */ );
4729
4730 SCH_SHEET_INSTANCE sheetInstance;
4731
4732 sheetInstance.m_Path = sheetpath.Path();
4733 sheetInstance.m_PageNumber = wxT( "#" );
4734
4735 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
4736 m_sheets.insert( { aIndex, sheet } );
4737}
4738
4739
4740void SCH_IO_ALTIUM::ParseSheetEntry( const std::map<wxString, wxString>& aProperties )
4741{
4742 ASCH_SHEET_ENTRY elem( aProperties );
4743
4744 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4745
4746 if( sheetIt == m_sheets.end() )
4747 {
4748 m_errorMessages.emplace( wxString::Format( wxT( "Sheet entry's owner (%d) not found." ), elem.ownerindex ),
4750 return;
4751 }
4752
4753 SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheetIt->second );
4754 sheetIt->second->AddPin( sheetPin );
4755
4756 wxString pinName = elem.name;
4757
4758 if( !elem.harnessType.IsEmpty() )
4759 pinName += wxT( "{" ) + elem.harnessType + wxT( "}" );
4760
4761 sheetPin->SetText( pinName );
4763 //sheetPin->SetSpinStyle( getSpinStyle( term.OrientAngle, false ) );
4764 //sheetPin->SetPosition( getKiCadPoint( term.Position ) );
4765
4766 VECTOR2I pos = sheetIt->second->GetPosition();
4767 VECTOR2I size = sheetIt->second->GetSize();
4768
4769 switch( elem.side )
4770 {
4771 default:
4773 sheetPin->SetPosition( { pos.x, pos.y + elem.distanceFromTop } );
4774 sheetPin->SetSpinStyle( SPIN_STYLE::LEFT );
4775 sheetPin->SetSide( SHEET_SIDE::LEFT );
4776 break;
4777
4779 sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.distanceFromTop } );
4780 sheetPin->SetSpinStyle( SPIN_STYLE::RIGHT );
4781 sheetPin->SetSide( SHEET_SIDE::RIGHT );
4782 break;
4783
4785 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y } );
4786 sheetPin->SetSpinStyle( SPIN_STYLE::UP );
4787 sheetPin->SetSide( SHEET_SIDE::TOP );
4788 break;
4789
4791 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y + size.y } );
4792 sheetPin->SetSpinStyle( SPIN_STYLE::BOTTOM );
4793 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
4794 break;
4795 }
4796
4797 switch( elem.iotype )
4798 {
4799 default:
4802 break;
4803
4806 break;
4807
4810 break;
4811
4814 break;
4815 }
4816}
4817
4818
4820 REPORTER* aReporter )
4821{
4823 {
4825 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4826 line1->AddPoint( { 0, 0 } );
4827 line1->AddPoint( { 0, schIUScale.MilsToIU( 50 ) } );
4828 aKsymbol->AddDrawItem( line1, false );
4829
4830 if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE )
4831 {
4833 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
4834 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 75 ) } );
4835 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 25 ), 0 ) );
4836 aKsymbol->AddDrawItem( circle, false );
4837 }
4838 else
4839 {
4841 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4842 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4843 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
4844 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4845 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4846 aKsymbol->AddDrawItem( line2, false );
4847 }
4848
4849 return { 0, schIUScale.MilsToIU( 150 ) };
4850 }
4851 else if( aStyle == ASCH_POWER_PORT_STYLE::WAVE )
4852 {
4854 line->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4855 line->AddPoint( { 0, 0 } );
4856 line->AddPoint( { 0, schIUScale.MilsToIU( 72 ) } );
4857 aKsymbol->AddDrawItem( line, false );
4858
4860 bezier->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
4861 bezier->SetStart( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 50 ) } );
4862 bezier->SetBezierC1( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 87 ) } );
4863 bezier->SetBezierC2( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 63 ) } );
4864 bezier->SetEnd( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 100 ) } );
4865 aKsymbol->AddDrawItem( bezier, false );
4866
4867 return { 0, schIUScale.MilsToIU( 150 ) };
4868 }
4869 else if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND
4871 || aStyle == ASCH_POWER_PORT_STYLE::EARTH
4873 {
4875 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4876 line1->AddPoint( { 0, 0 } );
4877 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
4878 aKsymbol->AddDrawItem( line1, false );
4879
4881 {
4883 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4884 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4885 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4886 aKsymbol->AddDrawItem( line2, false );
4887
4889 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4890 line3->AddPoint( { schIUScale.MilsToIU( -70 ), schIUScale.MilsToIU( 130 ) } );
4891 line3->AddPoint( { schIUScale.MilsToIU( 70 ), schIUScale.MilsToIU( 130 ) } );
4892 aKsymbol->AddDrawItem( line3, false );
4893
4895 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4896 line4->AddPoint( { schIUScale.MilsToIU( -40 ), schIUScale.MilsToIU( 160 ) } );
4897 line4->AddPoint( { schIUScale.MilsToIU( 40 ), schIUScale.MilsToIU( 160 ) } );
4898 aKsymbol->AddDrawItem( line4, false );
4899
4901 line5->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4902 line5->AddPoint( { schIUScale.MilsToIU( -10 ), schIUScale.MilsToIU( 190 ) } );
4903 line5->AddPoint( { schIUScale.MilsToIU( 10 ), schIUScale.MilsToIU( 190 ) } );
4904 aKsymbol->AddDrawItem( line5, false );
4905 }
4906 else if( aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND )
4907 {
4909 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4910 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4911 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4912 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 200 ) } );
4913 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4914 aKsymbol->AddDrawItem( line2, false );
4915 }
4916 else if( aStyle == ASCH_POWER_PORT_STYLE::EARTH )
4917 {
4919 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4920 line2->AddPoint( { schIUScale.MilsToIU( -150 ), schIUScale.MilsToIU( 200 ) } );
4921 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4922 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4923 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 200 ) } );
4924 aKsymbol->AddDrawItem( line2, false );
4925
4927 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4928 line3->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4929 line3->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 200 ) } );
4930 aKsymbol->AddDrawItem( line3, false );
4931 }
4932 else // ASCH_POWER_PORT_STYLE::GOST_ARROW
4933 {
4935 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4936 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4937 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4938 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
4939 aKsymbol->AddDrawItem( line2, false );
4940
4941 return { 0, schIUScale.MilsToIU( 150 ) }; // special case
4942 }
4943
4944 return { 0, schIUScale.MilsToIU( 250 ) };
4945 }
4948 {
4950 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4951 line1->AddPoint( { 0, 0 } );
4952 line1->AddPoint( { 0, schIUScale.MilsToIU( 160 ) } );
4953 aKsymbol->AddDrawItem( line1, false );
4954
4956 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4957 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 160 ) } );
4958 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 160 ) } );
4959 aKsymbol->AddDrawItem( line2, false );
4960
4962 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4963 line3->AddPoint( { schIUScale.MilsToIU( -60 ), schIUScale.MilsToIU( 200 ) } );
4964 line3->AddPoint( { schIUScale.MilsToIU( 60 ), schIUScale.MilsToIU( 200 ) } );
4965 aKsymbol->AddDrawItem( line3, false );
4966
4968 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4969 line4->AddPoint( { schIUScale.MilsToIU( -20 ), schIUScale.MilsToIU( 240 ) } );
4970 line4->AddPoint( { schIUScale.MilsToIU( 20 ), schIUScale.MilsToIU( 240 ) } );
4971 aKsymbol->AddDrawItem( line4, false );
4972
4974 return { 0, schIUScale.MilsToIU( -300 ) };
4975
4977 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4978 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 160 ) } );
4979 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 120 ), 0 ) );
4980 aKsymbol->AddDrawItem( circle, false );
4981
4982 return { 0, schIUScale.MilsToIU( 350 ) };
4983 }
4984 else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_BAR )
4985 {
4987 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4988 line1->AddPoint( { 0, 0 } );
4989 line1->AddPoint( { 0, schIUScale.MilsToIU( 200 ) } );
4990 aKsymbol->AddDrawItem( line1, false );
4991
4993 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4994 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 200 ) } );
4995 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 200 ) } );
4996 aKsymbol->AddDrawItem( line2, false );
4997
4998 return { 0, schIUScale.MilsToIU( 250 ) };
4999 }
5000 else
5001 {
5002 if( aStyle != ASCH_POWER_PORT_STYLE::BAR )
5003 {
5004 aReporter->Report( _( "Power Port with unknown style imported as 'Bar' type." ),
5006 }
5007
5009 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
5010 line1->AddPoint( { 0, 0 } );
5011 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
5012 aKsymbol->AddDrawItem( line1, false );
5013
5015 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
5016 line2->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 100 ) } );
5017 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 100 ) } );
5018 aKsymbol->AddDrawItem( line2, false );
5019
5020 return { 0, schIUScale.MilsToIU( 150 ) };
5021 }
5022}
5023
5024
5025void SCH_IO_ALTIUM::ParsePowerPort( const std::map<wxString, wxString>& aProperties )
5026{
5027 ASCH_POWER_PORT elem( aProperties );
5028
5029 wxString symName( elem.text );
5030 std::string styleName( magic_enum::enum_name<ASCH_POWER_PORT_STYLE>( elem.style ) );
5031
5032 if( !styleName.empty() )
5033 symName << '_' << styleName;
5034
5035 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symName );
5036 LIB_SYMBOL* libSymbol = nullptr;
5037
5038 const auto& powerSymbolIt = m_powerSymbols.find( symName );
5039
5040 if( powerSymbolIt != m_powerSymbols.end() )
5041 {
5042 libSymbol = powerSymbolIt->second; // cache hit
5043 }
5044 else
5045 {
5046 libSymbol = new LIB_SYMBOL( wxEmptyString );
5047 libSymbol->SetGlobalPower();
5048 libSymbol->SetName( symName );
5049 libSymbol->GetReferenceField().SetText( "#PWR" );
5050 libSymbol->GetReferenceField().SetVisible( false );
5051 libSymbol->GetValueField().SetText( elem.text );
5052 libSymbol->GetValueField().SetVisible( true );
5053 libSymbol->SetDescription( wxString::Format( _( "Power symbol creates a global label with name '%s'" ),
5054 elem.text ) );
5055 libSymbol->SetKeyWords( "power-flag" );
5056 libSymbol->SetLibId( libId );
5057
5058 // generate graphic
5059 SCH_PIN* pin = new SCH_PIN( libSymbol );
5060 libSymbol->AddDrawItem( pin, false );
5061
5062 pin->SetName( elem.text );
5063 pin->SetPosition( { 0, 0 } );
5064 pin->SetLength( 0 );
5066 pin->SetVisible( false );
5067
5068 VECTOR2I valueFieldPos = HelperGeneratePowerPortGraphics( libSymbol, elem.style, m_reporter );
5069
5070 libSymbol->GetValueField().SetPosition( valueFieldPos );
5071
5072 // this has to be done after parsing the LIB_SYMBOL!
5073 m_powerSymbols.insert( { symName, libSymbol } );
5074 }
5075
5076 SCH_SCREEN* screen = getCurrentScreen();
5077 wxCHECK( screen, /* void */ );
5078
5079 SCH_SYMBOL* symbol = new SCH_SYMBOL();
5080 symbol->SetRef( &m_sheetPath, "#PWR?" );
5081 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
5082 symbol->SetValueFieldText( elem.text );
5083 symbol->SetLibId( libId );
5084 symbol->SetLibSymbol( new LIB_SYMBOL( *libSymbol ) );
5085
5086 SCH_FIELD* valueField = symbol->GetField( FIELD_T::VALUE );
5087 valueField->SetVisible( elem.showNetName );
5088 valueField->SetPosition( libSymbol->GetValueField().GetPosition() );
5089
5090 symbol->SetPosition( elem.location + m_sheetOffset );
5091
5092 switch( elem.orientation )
5093 {
5096 valueField->SetTextAngle( ANGLE_VERTICAL );
5098 break;
5099
5102 valueField->SetTextAngle( ANGLE_HORIZONTAL );
5104 break;
5105
5108 valueField->SetTextAngle( ANGLE_VERTICAL );
5110 break;
5111
5114 valueField->SetTextAngle( ANGLE_HORIZONTAL );
5116 break;
5117
5118 default:
5119 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
5120 break;
5121 }
5122
5123 screen->Append( symbol );
5124}
5125
5126
5128{
5129 ParsePortHelper( aElem );
5130}
5131
5132
5134{
5135 if( !aElem.HarnessType.IsEmpty() )
5136 {
5137 // Parse harness ports after "Additional" compound section is parsed
5138 m_altiumHarnessPortsCurrentSheet.emplace_back( aElem );
5139 return;
5140 }
5141
5142 ParsePortHelper( aElem );
5143}
5144
5145
5147{
5148 VECTOR2I start = aElem.Location + m_sheetOffset;
5149 VECTOR2I end = start;
5150
5151 switch( aElem.Style )
5152 {
5153 default:
5158 end.x += aElem.Width;
5159 break;
5160
5165 end.y -= aElem.Width;
5166 break;
5167 }
5168
5169 // Check which connection points exists in the schematic
5170 SCH_SCREEN* screen = getCurrentScreen();
5171 wxCHECK( screen, /* void */ );
5172
5173 bool startIsWireTerminal = screen->IsTerminalPoint( start, LAYER_WIRE );
5174 bool startIsBusTerminal = screen->IsTerminalPoint( start, LAYER_BUS );
5175
5176 bool endIsWireTerminal = screen->IsTerminalPoint( end, LAYER_WIRE );
5177 bool endIsBusTerminal = screen->IsTerminalPoint( end, LAYER_BUS );
5178
5179 // check if any of the points is a terminal point
5180 // TODO: there seems a problem to detect approximated connections towards component pins?
5181 bool connectionFound = startIsWireTerminal
5182 || startIsBusTerminal
5183 || endIsWireTerminal
5184 || endIsBusTerminal;
5185
5186 if( !connectionFound )
5187 {
5188 for( auto& [ _, harness ] : m_altiumHarnesses )
5189 {
5190 if( harness.m_name.CmpNoCase( aElem.HarnessType ) != 0 )
5191 continue;
5192
5193 BOX2I bbox( harness.m_location, harness.m_size );
5194 bbox.Inflate( 10 );
5195
5196 if( bbox.Contains( start ) )
5197 {
5198 startIsBusTerminal = true;
5199 connectionFound = true;
5200 break;
5201 }
5202
5203 if( bbox.Contains( end ) )
5204 {
5205 endIsBusTerminal = true;
5206 connectionFound = true;
5207 break;
5208 }
5209 }
5210
5211 if( !connectionFound )
5212 {
5213 m_errorMessages.emplace( wxString::Format( _( "Port %s has no connections." ), aElem.Name ),
5215 }
5216 }
5217
5218 // Select label position. In case both match, we will add a line later.
5219 VECTOR2I position = ( startIsWireTerminal || startIsBusTerminal ) ? start : end;
5220 SCH_LABEL_BASE* label;
5221
5222 wxString labelName = aElem.Name;
5223
5224 if( !aElem.HarnessType.IsEmpty() )
5225 labelName += wxT( "{" ) + aElem.HarnessType + wxT( "}" );
5226
5227 // TODO: detect correct label type depending on sheet settings, etc.
5228#if 1 // Set to 1 to use SCH_HIERLABEL label, 0 to use SCH_GLOBALLABEL
5229 {
5230 label = new SCH_HIERLABEL( position, labelName );
5231 }
5232#else
5233 label = new SCH_GLOBALLABEL( position, labelName );
5234
5235 // Default "Sheet References" field should be hidden, at least for now
5236 label->GetField( INTERSHEET_REFS )->SetVisible( false );
5237#endif
5238
5239 switch( aElem.IOtype )
5240 {
5241 default:
5246 }
5247
5248 switch( aElem.Style )
5249 {
5250 default:
5255 if( ( startIsWireTerminal || startIsBusTerminal ) )
5257 else
5259
5260 break;
5261
5266 if( ( startIsWireTerminal || startIsBusTerminal ) )
5267 label->SetSpinStyle( SPIN_STYLE::UP );
5268 else
5270
5271 break;
5272 }
5273
5274 label->AutoplaceFields( screen, AUTOPLACE_AUTO );
5275 label->SetFlags( IS_NEW );
5276
5277 screen->Append( label );
5278
5279 // This is a hack, for the case both connection points are valid: add a small wire
5280 if( ( startIsWireTerminal && endIsWireTerminal ) )
5281 {
5282 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
5283 wire->SetEndPoint( end );
5284 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
5285 wire->SetFlags( IS_NEW );
5286 screen->Append( wire );
5287 }
5288 else if( startIsBusTerminal && endIsBusTerminal )
5289 {
5290 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
5291 wire->SetEndPoint( end );
5292 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
5293 wire->SetFlags( IS_NEW );
5294 screen->Append( wire );
5295 }
5296}
5297
5298
5299void SCH_IO_ALTIUM::ParseNoERC( const std::map<wxString, wxString>& aProperties )
5300{
5301 ASCH_NO_ERC elem( aProperties );
5302
5303 SCH_SCREEN* screen = getCurrentScreen();
5304 wxCHECK( screen, /* void */ );
5305
5306 // Only the generic cross means unconnected, a specific No ERC just waives selected violations
5307 if( elem.isActive && elem.suppressAll )
5308 {
5309 SCH_NO_CONNECT* noConnect = new SCH_NO_CONNECT( elem.location + m_sheetOffset );
5310
5311 noConnect->SetFlags( IS_NEW );
5312 screen->Append( noConnect );
5313 }
5314}
5315
5316
5317void SCH_IO_ALTIUM::ParseNetLabel( const std::map<wxString, wxString>& aProperties )
5318{
5319 ASCH_NET_LABEL elem( aProperties );
5320
5321 SCH_LABEL* label = new SCH_LABEL( elem.location + m_sheetOffset, elem.text );
5322
5323 SCH_SCREEN* screen = getCurrentScreen();
5324 wxCHECK( screen, /* void */ );
5325
5326 SetTextPositioning( label, elem.justification, elem.orientation );
5327
5328 label->SetFlags( IS_NEW );
5329 screen->Append( label );
5330}
5331
5332
5333void SCH_IO_ALTIUM::ParseBus( const std::map<wxString, wxString>& aProperties )
5334{
5335 ASCH_BUS elem( aProperties );
5336
5337 SCH_SCREEN* screen = getCurrentScreen();
5338 wxCHECK( screen, /* void */ );
5339
5340 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
5341 {
5342 SCH_LINE* bus = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_BUS );
5343 bus->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
5344 bus->SetLineWidth( elem.lineWidth );
5345
5346 bus->SetFlags( IS_NEW );
5347 screen->Append( bus );
5348 }
5349}
5350
5351
5352void SCH_IO_ALTIUM::ParseWire( const std::map<wxString, wxString>& aProperties )
5353{
5354 ASCH_WIRE elem( aProperties );
5355
5356 SCH_SCREEN* screen = getCurrentScreen();
5357 wxCHECK( screen, /* void */ );
5358
5359 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
5360 {
5361 SCH_LINE* wire = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_WIRE );
5362 wire->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
5363 // wire->SetLineWidth( elem.lineWidth );
5364
5365 wire->SetFlags( IS_NEW );
5366 screen->Append( wire );
5367 }
5368}
5369
5370
5371void SCH_IO_ALTIUM::ParseJunction( const std::map<wxString, wxString>& aProperties )
5372{
5373 SCH_SCREEN* screen = getCurrentScreen();
5374 wxCHECK( screen, /* void */ );
5375
5376 ASCH_JUNCTION elem( aProperties );
5377
5378 SCH_JUNCTION* junction = new SCH_JUNCTION( elem.location + m_sheetOffset );
5379
5380 junction->SetFlags( IS_NEW );
5381 screen->Append( junction );
5382}
5383
5384
5385void SCH_IO_ALTIUM::ParseImage( const std::map<wxString, wxString>& aProperties )
5386{
5387 ASCH_IMAGE elem( aProperties );
5388
5389 const auto& component = m_altiumComponents.find( elem.ownerindex );
5390
5391 //Hide the image if it is owned by a component but the part id do not match
5392 if( component != m_altiumComponents.end()
5393 && component->second.currentpartid != elem.ownerpartid )
5394 return;
5395
5396 VECTOR2I center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
5397 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
5398 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
5399
5400 SCH_SCREEN* screen = getCurrentScreen();
5401 wxCHECK( screen, /* void */ );
5402
5403 if( elem.embedimage )
5404 {
5405 const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
5406
5407 if( !storageFile )
5408 {
5409 m_errorMessages.emplace( wxString::Format( _( "Embedded file '%s' not found in "
5410 "storage." ),
5411 elem.filename ),
5413 return;
5414 }
5415
5416 wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
5417
5418 // As wxZlibInputStream is not seekable, we need to write a temporary file
5419 wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
5420 wxZlibInputStream zlibInputStream( fileStream );
5421 wxFFileOutputStream outputStream( storagePath );
5422 outputStream.Write( zlibInputStream );
5423
5424 // wxFFileOutputStream::IsOk() reports the closed stream as not-ok, so capture the write
5425 // result before closing.
5426 const bool writeOk = outputStream.IsOk();
5427 const bool closeOk = outputStream.Close();
5428
5429 if( storagePath.IsEmpty() || !writeOk || !closeOk )
5430 {
5431 m_errorMessages.emplace(
5432 wxString::Format( _( "Could not write a temporary file while extracting the "
5433 "embedded image '%s'." ),
5434 elem.filename ),
5436
5437 if( !storagePath.IsEmpty() )
5438 wxRemoveFile( storagePath );
5439
5440 return;
5441 }
5442
5443 bool readOk;
5444
5445 {
5446 // wxImage emits its own log spam on a failed read; we report our own errors instead.
5447 wxLogNull noLog;
5448 readOk = refImage.ReadImageFile( storagePath );
5449 }
5450
5451 if( !readOk )
5452 {
5453 m_errorMessages.emplace(
5454 wxString::Format( _( "Failed to read embedded image '%s'. "
5455 "The image data may be corrupt or in an "
5456 "unsupported format." ),
5457 elem.filename ),
5459 wxRemoveFile( storagePath );
5460 return;
5461 }
5462
5463 // Remove temporary file
5464 wxRemoveFile( storagePath );
5465 }
5466 else
5467 {
5468 if( !wxFileExists( elem.filename ) )
5469 {
5470 m_errorMessages.emplace(
5471 wxString::Format( _( "Could not find image file '%s'. The file "
5472 "path may be specific to a different operating "
5473 "system." ),
5474 elem.filename ),
5476 return;
5477 }
5478
5479 bool readOk;
5480
5481 {
5482 wxLogNull noLog;
5483 readOk = refImage.ReadImageFile( elem.filename );
5484 }
5485
5486 if( !readOk )
5487 {
5488 m_errorMessages.emplace(
5489 wxString::Format( _( "Failed to read image file '%s'. The image "
5490 "may be corrupt or in an unsupported "
5491 "format." ),
5492 elem.filename ),
5494 return;
5495 }
5496 }
5497
5498 // we only support one scale, thus we need to select one in case it does not keep aspect ratio
5499 const VECTOR2I currentImageSize = refImage.GetSize();
5500 const VECTOR2I expectedImageSize = elem.location - elem.corner;
5501 const double scaleX = std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
5502 const double scaleY = std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
5503 refImage.SetImageScale( std::min( scaleX, scaleY ) );
5504
5505 bitmap->SetFlags( IS_NEW );
5506 screen->Append( bitmap.release() );
5507}
5508
5509
5510void SCH_IO_ALTIUM::ParseSheet( const std::map<wxString, wxString>& aProperties )
5511{
5512 m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
5513
5514 SCH_SCREEN* screen = getCurrentScreen();
5515 wxCHECK( screen, /* void */ );
5516
5517 PAGE_INFO pageInfo;
5518
5519 bool isPortrait = m_altiumSheet->sheetOrientation == ASCH_SHEET_WORKSPACEORIENTATION::PORTRAIT;
5520
5521 if( m_altiumSheet->useCustomSheet )
5522 {
5523 PAGE_INFO::SetCustomWidthMils( schIUScale.IUToMils( m_altiumSheet->customSize.x ) );
5524 PAGE_INFO::SetCustomHeightMils( schIUScale.IUToMils( m_altiumSheet->customSize.y ) );
5525 pageInfo.SetType( PAGE_SIZE_TYPE::User, isPortrait );
5526 }
5527 else
5528 {
5529 switch( m_altiumSheet->sheetSize )
5530 {
5531 default:
5532 case ASCH_SHEET_SIZE::A4: pageInfo.SetType( "A4", isPortrait ); break;
5533 case ASCH_SHEET_SIZE::A3: pageInfo.SetType( "A3", isPortrait ); break;
5534 case ASCH_SHEET_SIZE::A2: pageInfo.SetType( "A2", isPortrait ); break;
5535 case ASCH_SHEET_SIZE::A1: pageInfo.SetType( "A1", isPortrait ); break;
5536 case ASCH_SHEET_SIZE::A0: pageInfo.SetType( "A0", isPortrait ); break;
5537 case ASCH_SHEET_SIZE::A: pageInfo.SetType( "A", isPortrait ); break;
5538 case ASCH_SHEET_SIZE::B: pageInfo.SetType( "B", isPortrait ); break;
5539 case ASCH_SHEET_SIZE::C: pageInfo.SetType( "C", isPortrait ); break;
5540 case ASCH_SHEET_SIZE::D: pageInfo.SetType( "D", isPortrait ); break;
5541 case ASCH_SHEET_SIZE::E: pageInfo.SetType( "E", isPortrait ); break;
5542 case ASCH_SHEET_SIZE::LETTER: pageInfo.SetType( "USLetter", isPortrait ); break;
5543 case ASCH_SHEET_SIZE::LEGAL: pageInfo.SetType( "USLegal", isPortrait ); break;
5544 case ASCH_SHEET_SIZE::TABLOID: pageInfo.SetType( "A3", isPortrait ); break;
5545 case ASCH_SHEET_SIZE::ORCAD_A: pageInfo.SetType( "A", isPortrait ); break;
5546 case ASCH_SHEET_SIZE::ORCAD_B: pageInfo.SetType( "B", isPortrait ); break;
5547 case ASCH_SHEET_SIZE::ORCAD_C: pageInfo.SetType( "C", isPortrait ); break;
5548 case ASCH_SHEET_SIZE::ORCAD_D: pageInfo.SetType( "D", isPortrait ); break;
5549 case ASCH_SHEET_SIZE::ORCAD_E: pageInfo.SetType( "E", isPortrait ); break;
5550 }
5551 }
5552
5553 screen->SetPageSettings( pageInfo );
5554
5555 // Altium anchors its snap grid at the sheet origin, which the Y flip maps to the page bottom
5556 // ISO heights are not whole mils, so truncate to a grid step rather than flip past the page
5557 const int gridPitch = m_schematic->Settings().m_ConnectionGridSize;
5558 const int pageHeight = pageInfo.GetHeightIU( schIUScale.IU_PER_MILS );
5559
5560 m_sheetOffset = { 0, ( pageHeight / gridPitch ) * gridPitch };
5561}
5562
5563
5564void SCH_IO_ALTIUM::ParseSheetName( const std::map<wxString, wxString>& aProperties )
5565{
5566 ASCH_SHEET_NAME elem( aProperties );
5567 SCH_SCREEN* currentScreen = getCurrentScreen();
5568
5569 wxCHECK( currentScreen, /* void */ );
5570
5571 const auto& sheetIt = m_sheets.find( elem.ownerindex );
5572
5573 if( sheetIt == m_sheets.end() )
5574 {
5575 m_errorMessages.emplace( wxString::Format( wxT( "Sheetname's owner (%d) not found." ),
5576 elem.ownerindex ),
5578 return;
5579 }
5580
5581 wxString baseName = elem.text;
5582 baseName.Replace( wxT( "/" ), wxT( "_" ) );
5583
5584 wxString sheetName = baseName;
5585 std::set<wxString> sheetNames;
5586
5587 for( EDA_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
5588 {
5589 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
5590 sheetNames.insert( sheet->GetName() );
5591 }
5592
5593 for( int ii = 1; ; ++ii )
5594 {
5595 if( sheetNames.find( sheetName ) == sheetNames.end() )
5596 break;
5597
5598 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
5599 }
5600
5601 SCH_FIELD* sheetNameField = sheetIt->second->GetField( FIELD_T::SHEET_NAME );
5602
5603 sheetNameField->SetPosition( elem.location + m_sheetOffset );
5604 sheetNameField->SetText( sheetName );
5605 sheetNameField->SetVisible( !elem.isHidden );
5607}
5608
5609
5610void SCH_IO_ALTIUM::ParseFileName( const std::map<wxString, wxString>& aProperties )
5611{
5612 ASCH_FILE_NAME elem( aProperties );
5613
5614 const auto& sheetIt = m_sheets.find( elem.ownerindex );
5615
5616 if( sheetIt == m_sheets.end() )
5617 {
5618 m_errorMessages.emplace( wxString::Format( wxT( "Filename's owner (%d) not found." ),
5619 elem.ownerindex ),
5621 return;
5622 }
5623
5624 SCH_FIELD* filenameField = sheetIt->second->GetField( FIELD_T::SHEET_FILENAME );
5625
5626 filenameField->SetPosition( elem.location + m_sheetOffset );
5627
5628 // Keep the filename of the Altium file until after the file is actually loaded.
5629 filenameField->SetText( elem.text );
5630 filenameField->SetVisible( !elem.isHidden );
5632}
5633
5634
5635void SCH_IO_ALTIUM::ParseDesignator( const std::map<wxString, wxString>& aProperties )
5636{
5637 ASCH_DESIGNATOR elem( aProperties );
5638
5639 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
5640
5641 if( libSymbolIt == m_libSymbols.end() )
5642 {
5643 // TODO: e.g. can depend on Template (RECORD=39
5644 m_errorMessages.emplace( wxString::Format( wxT( "Designator's owner (%d) not found." ),
5645 elem.ownerindex ),
5647 return;
5648 }
5649
5650 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
5651 SCH_SHEET_PATH sheetpath;
5652
5653 SCH_SCREEN* screen = getCurrentScreen();
5654 wxCHECK( screen, /* void */ );
5655
5656 // Graphics symbols have no reference. '#GRAPHIC' allows them to not have footprint associated.
5657 // Note: not all unnamed imported symbols are necessarily graphics.
5658 bool emptyRef = elem.text.IsEmpty();
5659 symbol->SetRef( &m_sheetPath, emptyRef ? wxString( wxS( "#GRAPHIC" ) ) : elem.text );
5660
5661 // I am not sure value and ref should be invisible just because emptyRef is true
5662 // I have examples with this criteria fully incorrect.
5663 bool visible = !emptyRef;
5664
5665 symbol->GetField( FIELD_T::VALUE )->SetVisible( visible );
5666
5667 SCH_FIELD* field = symbol->GetField( FIELD_T::REFERENCE );
5668 field->SetVisible( visible );
5669 field->SetPosition( elem.location + m_sheetOffset );
5670 SetTextPositioning( field, elem.justification, elem.orientation );
5671
5672 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
5673
5674 if( altiumSymIt != m_altiumComponents.end() )
5675 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
5676}
5677
5678
5679void SCH_IO_ALTIUM::ParseLibDesignator( const std::map<wxString, wxString>& aProperties,
5680 std::vector<LIB_SYMBOL*>& aSymbol,
5681 std::vector<int>& aFontSizes )
5682{
5683 ASCH_DESIGNATOR elem( aProperties );
5684
5685 if( elem.ownerpartdisplaymode != 0 )
5686 return;
5687
5688 for( LIB_SYMBOL* symbol : aSymbol )
5689 {
5690 bool emptyRef = elem.text.IsEmpty();
5691 SCH_FIELD& refField = symbol->GetReferenceField();
5692
5693 if( emptyRef )
5694 refField.SetText( wxT( "X" ) );
5695 else
5696 refField.SetText( elem.text.BeforeLast( '?' ) ); // remove the '?' at the end for KiCad-style
5697
5698 refField.SetPosition( elem.location );
5699 SetTextPositioning( &refField, elem.justification, elem.orientation );
5700
5701 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
5702 {
5703 int size = aFontSizes[elem.fontId - 1];
5704 refField.SetTextSize( { size, size } );
5705 }
5706 }
5707}
5708
5709
5710void SCH_IO_ALTIUM::ParseBusEntry( const std::map<wxString, wxString>& aProperties )
5711{
5712 ASCH_BUS_ENTRY elem( aProperties );
5713
5714 SCH_SCREEN* screen = getCurrentScreen();
5715 wxCHECK( screen, /* void */ );
5716
5717 SCH_BUS_WIRE_ENTRY* busWireEntry = new SCH_BUS_WIRE_ENTRY( elem.location + m_sheetOffset );
5718
5719 VECTOR2I vector = elem.corner - elem.location;
5720 busWireEntry->SetSize( { vector.x, vector.y } );
5721
5722 busWireEntry->SetFlags( IS_NEW );
5723 screen->Append( busWireEntry );
5724}
5725
5726
5727void SCH_IO_ALTIUM::ParseParameter( const std::map<wxString, wxString>& aProperties )
5728{
5729 ASCH_PARAMETER elem( aProperties );
5730
5731 // TODO: fill in replacements from variant, sheet and project
5732 static const std::map<wxString, wxString> variableMap = {
5733 { "COMMENT", "VALUE" },
5734 { "VALUE", "ALTIUM_VALUE" },
5735 };
5736
5737 if( elem.ownerindex <= 0 )
5738 {
5739 // This is some sheet parameter
5740 wxString paramName = elem.name.Upper();
5741
5742 // an unset parameter still names a variable, which resolves to nothing
5743 if( elem.text == "*" )
5744 {
5745 if( paramName != "SHEETNUMBER" && paramName != "TITLE" && paramName != "REVISION" && paramName != "DATE"
5746 && paramName != "COMPANYNAME" )
5747 {
5748 m_unsetSheetParams.insert( paramName );
5749 }
5750
5751 return;
5752 }
5753
5754 if( paramName == "SHEETNUMBER" )
5755 {
5756 m_sheetPath.SetPageNumber( elem.text );
5757 }
5758 else if( paramName == "TITLE" )
5759 {
5760 m_currentTitleBlock->SetTitle( elem.text );
5761 }
5762 else if( paramName == "REVISION" )
5763 {
5764 m_currentTitleBlock->SetRevision( elem.text );
5765 }
5766 else if( paramName == "DATE" )
5767 {
5768 m_currentTitleBlock->SetDate( elem.text );
5769 }
5770 else if( paramName == "COMPANYNAME" )
5771 {
5772 m_currentTitleBlock->SetCompany( elem.text );
5773 }
5774 else
5775 {
5776 m_schematic->Project().GetTextVars()[ paramName ] = elem.text;
5777 }
5778 }
5779 else
5780 {
5781 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
5782
5783 if( libSymbolIt == m_libSymbols.end() )
5784 {
5785 // TODO: e.g. can depend on Template (RECORD=39
5786 return;
5787 }
5788
5789 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
5790 SCH_FIELD* field = nullptr;
5791 wxString upperName = elem.name.Upper();
5792
5793 if( upperName == "COMMENT" )
5794 {
5795 field = symbol->GetField( FIELD_T::VALUE );
5796 }
5797 else
5798 {
5799 wxString fieldName = elem.name.Upper();
5800
5801 if( fieldName.IsEmpty() )
5802 {
5803 int disambiguate = 1;
5804
5805 while( 1 )
5806 {
5807 fieldName = wxString::Format( "ALTIUM_UNNAMED_%d", disambiguate++ );
5808
5809 if( !symbol->GetField( fieldName ) )
5810 break;
5811 }
5812 }
5813 else if( fieldName == "VALUE" )
5814 {
5815 fieldName = "ALTIUM_VALUE";
5816 }
5817
5818 field = symbol->AddField( SCH_FIELD( symbol, FIELD_T::USER, fieldName ) );
5819 }
5820
5821 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
5822 field->SetText( kicadText );
5823 field->SetPosition( elem.location + m_sheetOffset );
5824 field->SetVisible( !elem.isHidden );
5825 field->SetNameShown( elem.isShowName );
5826 SetTextPositioning( field, elem.justification, elem.orientation );
5827
5828 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
5829
5830 if( altiumSymIt != m_altiumComponents.end() )
5831 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
5832 }
5833}
5834
5835
5836void SCH_IO_ALTIUM::ParseLibParameter( const std::map<wxString, wxString>& aProperties,
5837 std::vector<LIB_SYMBOL*>& aSymbol,
5838 std::vector<int>& aFontSizes )
5839{
5840 ASCH_PARAMETER elem( aProperties );
5841
5842 if( elem.ownerpartdisplaymode != 0 )
5843 return;
5844
5845 // Part ID 1 is the current library part.
5846 // Part ID ALTIUM_COMPONENT_NONE(-1) means all parts
5847 // If a parameter is assigned to a specific element such as a pin,
5848 // we will need to handle it here.
5849 // TODO: Handle HIDDENNETNAME property (others?)
5850 if( elem.ownerpartid != 1 && elem.ownerpartid != ALTIUM_COMPONENT_NONE )
5851 return;
5852
5853 // If ownerindex is populated, this is parameter belongs to a subelement (e.g. pin).
5854 // Ignore for now.
5855 // TODO: Update this when KiCad supports parameters for any object
5856 if( elem.ownerindex != ALTIUM_COMPONENT_NONE )
5857 return;
5858
5859 // TODO: fill in replacements from variant, sheet and project
5860 // N.B. We do not keep the Altium "VALUE" variable here because
5861 // we don't have a way to assign variables to specific symbols
5862 std::map<wxString, wxString> variableMap = {
5863 { "COMMENT", "VALUE" },
5864 };
5865
5866 for( LIB_SYMBOL* libSymbol : aSymbol )
5867 {
5868 SCH_FIELD* field = nullptr;
5869 wxString upperName = elem.name.Upper();
5870
5871 if( upperName == "COMMENT" )
5872 {
5873 field = &libSymbol->GetValueField();
5874 }
5875 else
5876 {
5877 wxString fieldNameStem = elem.name;
5878 wxString fieldName = fieldNameStem;
5879 int disambiguate = 1;
5880
5881 if( fieldName.IsEmpty() )
5882 {
5883 fieldNameStem = "ALTIUM_UNNAMED";
5884 fieldName = "ALTIUM_UNNAMED_1";
5885 disambiguate = 2;
5886 }
5887 else if( upperName == "VALUE" )
5888 {
5889 fieldNameStem = "ALTIUM_VALUE";
5890 fieldName = "ALTIUM_VALUE";
5891 }
5892
5893 // Avoid adding duplicate fields
5894 while( libSymbol->GetField( fieldName ) )
5895 fieldName = wxString::Format( "%s_%d", fieldNameStem, disambiguate++ );
5896
5897 SCH_FIELD* new_field = new SCH_FIELD( libSymbol, FIELD_T::USER, fieldName );
5898 libSymbol->AddField( new_field );
5899 field = new_field;
5900 }
5901
5902 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
5903 field->SetText( kicadText );
5904
5905 field->SetTextPos( elem.location );
5906 SetTextPositioning( field, elem.justification, elem.orientation );
5907 field->SetVisible( !elem.isHidden );
5908
5909 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
5910 {
5911 int size = aFontSizes[elem.fontId - 1];
5912 field->SetTextSize( { size, size } );
5913 }
5914 else
5915 {
5916 int size = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
5917 field->SetTextSize( { size, size } );
5918 }
5919
5920 }
5921}
5922
5923
5925 const std::map<wxString, wxString>& aProperties )
5926{
5927 ASCH_IMPLEMENTATION_LIST elem( aProperties );
5928
5929 m_altiumImplementationList.emplace( aIndex, elem.ownerindex );
5930}
5931
5932
5933void SCH_IO_ALTIUM::ParseImplementation( const std::map<wxString, wxString>& aProperties,
5934 std::vector<LIB_SYMBOL*>& aSymbol )
5935{
5936 ASCH_IMPLEMENTATION elem( aProperties );
5937
5938 if( elem.type != wxS( "PCBLIB" ) )
5939 return;
5940
5941 // For schematic files, we need to check if the model is current.
5942 if( aSymbol.size() == 0 && !elem.isCurrent )
5943 return;
5944
5945 // For IntLibs we want to use the same lib name for footprints. Otherwise the model data
5946 // file names the source PcbLib as a Windows path; take its base name so the footprint id
5947 // matches the nickname project import registers in the footprint library table.
5948 wxString libName = m_isIntLib ? m_libName : wxFileName( elem.libname, wxPATH_WIN ).GetName();
5949
5950 wxArrayString fpFilters;
5951 fpFilters.Add( wxString::Format( wxS( "*%s*" ), elem.name ) );
5952
5953 // Parse the footprint fields for the library symbol
5954 if( !aSymbol.empty() )
5955 {
5956 for( LIB_SYMBOL* symbol : aSymbol )
5957 {
5958 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
5959
5960 symbol->SetFPFilters( fpFilters );
5961 symbol->GetField( FIELD_T::FOOTPRINT )->SetText( fpLibId.Format() );
5962 }
5963
5964 return;
5965 }
5966
5967 const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex );
5968
5969 if( implementationOwnerIt == m_altiumImplementationList.end() )
5970 {
5971 m_errorMessages.emplace( wxString::Format( wxT( "Implementation's owner (%d) not found." ),
5972 elem.ownerindex ),
5974 return;
5975 }
5976
5977 const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second );
5978
5979 if( libSymbolIt == m_libSymbols.end() )
5980 {
5981 m_errorMessages.emplace( wxString::Format( wxT( "Footprint's owner (%d) not found." ),
5982 implementationOwnerIt->second ),
5984 return;
5985 }
5986
5987 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
5988
5989 libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it
5990
5991 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
5992
5993 symbol->SetFootprintFieldText( fpLibId.Format() );
5994}
5995
5996
5997
5998std::vector<LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibComponent( const std::map<wxString,
5999 wxString>& aProperties )
6000{
6001 ASCH_SYMBOL elem( aProperties );
6002
6003 LIB_SYMBOL* symbol = new LIB_SYMBOL( wxEmptyString );
6004 symbol->SetName( elem.libreference );
6005
6006 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symbol->GetName() );
6007 symbol->SetDescription( elem.componentdescription );
6008 symbol->SetLibId( libId );
6009
6010 // Altium PARTCOUNT is one more than the actual unit count. The property may be missing
6011 // (defaults to 0) or otherwise nonsensical, so clamp to a minimum of 1 unit.
6012 symbol->SetUnitCount( std::max( 1, elem.partcount - 1 ), true );
6013
6014 if( elem.displaymodecount > 1 )
6015 {
6016 std::vector<wxString> bodyStyleNames;
6017
6018 for( int i = 0; i < elem.displaymodecount; i++ )
6019 bodyStyleNames.push_back( wxString::Format( "Display %d", i + 1 ) );
6020
6021 symbol->SetBodyStyleNames( bodyStyleNames );
6022 }
6023
6024 return { symbol };
6025}
6026
6027
6030{
6032 std::vector<int> fontSizes;
6033 struct SYMBOL_PIN_FRAC
6034 {
6035 int x_frac;
6036 int y_frac;
6037 int len_frac;
6038 };
6039
6040 ParseLibHeader( aAltiumLibFile, fontSizes );
6041
6042 std::map<wxString, ALTIUM_SYMBOL_DATA> syms = aAltiumLibFile.GetLibSymbols( nullptr );
6043
6044 for( auto& [name, entry] : syms )
6045 {
6046 if( m_reporter )
6047 m_reporter->Report( wxString::Format( _( "Converting symbol '%s'" ), name ), RPT_SEVERITY_ACTION );
6048
6049 std::map<int, SYMBOL_PIN_FRAC> pinFracs;
6050
6051 if( entry.m_pinsFrac )
6052 {
6053 auto parse_binary_pin_frac =
6054 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
6055 {
6056 std::map<wxString, wxString> result;
6057 ALTIUM_COMPRESSED_READER cmpreader( binaryData );
6058
6059 std::pair<int, std::string*> pinFracData = cmpreader.ReadCompressedString();
6060
6061 ALTIUM_BINARY_READER binreader( *pinFracData.second );
6062 SYMBOL_PIN_FRAC pinFrac;
6063
6064 pinFrac.x_frac = binreader.ReadInt32();
6065 pinFrac.y_frac = binreader.ReadInt32();
6066 pinFrac.len_frac = binreader.ReadInt32();
6067 pinFracs.insert( { pinFracData.first, pinFrac } );
6068
6069 return result;
6070 };
6071
6072 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_pinsFrac );
6073
6074 while( reader.GetRemainingBytes() > 0 )
6075 reader.ReadProperties( parse_binary_pin_frac );
6076 }
6077
6078 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_symbol );
6079 std::vector<LIB_SYMBOL*> symbols;
6080 int pin_index = 0;
6081
6082 if( reader.GetRemainingBytes() <= 0 )
6083 THROW_IO_ERROR( "LibSymbol does not contain any data" );
6084
6085 {
6086 std::map<wxString, wxString> properties = reader.ReadProperties();
6087 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
6088 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
6089
6090 if( record != ALTIUM_SCH_RECORD::COMPONENT )
6091 THROW_IO_ERROR( "LibSymbol does not start with COMPONENT record" );
6092
6093 symbols = ParseLibComponent( properties );
6094 }
6095
6096 auto handleBinaryPinLambda =
6097 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
6098 {
6099 std::map<wxString, wxString> result;
6100
6101 ALTIUM_BINARY_READER binreader( binaryData );
6102
6103 int32_t recordId = binreader.ReadInt32();
6104
6105 if( recordId != static_cast<int32_t>( ALTIUM_SCH_RECORD::PIN ) )
6106 THROW_IO_ERROR( "Binary record missing PIN record" );
6107
6108 result["RECORD"] = wxString::Format( "%d", recordId );
6109 binreader.ReadByte(); // unknown
6110 result["OWNERPARTID"] = wxString::Format( "%d", binreader.ReadInt16() );
6111 result["OWNERPARTDISPLAYMODE"] = wxString::Format( "%d", binreader.ReadByte() );
6112 result["SYMBOL_INNEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
6113 result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
6114 result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() );
6115 result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() );
6116 result["TEXT"] = binreader.ReadShortPascalString();
6117 binreader.ReadByte(); // unknown
6118 result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() );
6119 result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() );
6120 result["PINLENGTH"] = wxString::Format( "%d", binreader.ReadInt16() );
6121 result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() );
6122 result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() );
6123 result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() );
6124 result["NAME"] = binreader.ReadShortPascalString();
6125 result["DESIGNATOR"] = binreader.ReadShortPascalString();
6126 result["SWAPIDGROUP"] = binreader.ReadShortPascalString();
6127
6128 if( auto it = pinFracs.find( pin_index ); it != pinFracs.end() )
6129 {
6130 result["LOCATION.X_FRAC"] = wxString::Format( "%d", it->second.x_frac );
6131 result["LOCATION.Y_FRAC"] = wxString::Format( "%d", it->second.y_frac );
6132 result["PINLENGTH_FRAC"] = wxString::Format( "%d", it->second.len_frac );
6133 }
6134
6135 std::string partSeq = binreader.ReadShortPascalString(); // This is 'part|&|seq'
6136 std::vector<std::string> partSeqSplit = split( partSeq, "|" );
6137
6138 if( partSeqSplit.size() == 3 )
6139 {
6140 result["PART"] = partSeqSplit[0];
6141 result["SEQ"] = partSeqSplit[2];
6142 }
6143
6144 return result;
6145 };
6146
6147 while( reader.GetRemainingBytes() > 0 )
6148 {
6149 std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryPinLambda );
6150
6151 if( properties.empty() )
6152 continue;
6153
6154 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
6155 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
6156
6157 switch( record )
6158 {
6160 ParsePin( properties, symbols );
6161 pin_index++;
6162 break;
6163
6164 case ALTIUM_SCH_RECORD::LABEL: ParseLabel( properties, symbols, fontSizes ); break;
6165 case ALTIUM_SCH_RECORD::BEZIER: ParseBezier( properties, symbols ); break;
6166 case ALTIUM_SCH_RECORD::POLYLINE: ParsePolyline( properties, symbols ); break;
6167 case ALTIUM_SCH_RECORD::POLYGON: ParsePolygon( properties, symbols ); break;
6168 case ALTIUM_SCH_RECORD::ELLIPSE: ParseEllipse( properties, symbols ); break;
6169 case ALTIUM_SCH_RECORD::PIECHART: ParsePieChart( properties, symbols ); break;
6170 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: ParseRoundRectangle( properties, symbols ); break;
6171 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: ParseEllipticalArc( properties, symbols ); break;
6172 case ALTIUM_SCH_RECORD::ARC: ParseArc( properties, symbols ); break;
6173 case ALTIUM_SCH_RECORD::LINE: ParseLine( properties, symbols ); break;
6174 case ALTIUM_SCH_RECORD::RECTANGLE: ParseRectangle( properties, symbols ); break;
6175 case ALTIUM_SCH_RECORD::DESIGNATOR: ParseLibDesignator( properties, symbols, fontSizes ); break;
6176 case ALTIUM_SCH_RECORD::PARAMETER: ParseLibParameter( properties, symbols, fontSizes ); break;
6177 case ALTIUM_SCH_RECORD::TEXT_FRAME: ParseTextFrame( properties, symbols, fontSizes ); break;
6178 case ALTIUM_SCH_RECORD::IMPLEMENTATION: ParseImplementation( properties, symbols ); break;
6179
6181 break;
6182
6185 break;
6186
6188 // TODO: add support for these. They are just drawn symbols, so we can probably hardcode
6189 break;
6190
6192 // TODO: Handle images once libedit supports them
6193 break;
6194
6196 // Nothing for now. TODO: Figure out how implementation lists are generated in libs
6197 break;
6198
6199 default:
6200 m_errorMessages.emplace( wxString::Format( _( "Unknown or unexpected record id %d found in %s." ),
6201 recordId,
6202 symbols[0]->GetName() ),
6204 break;
6205 }
6206 }
6207
6208 if( reader.HasParsingError() )
6209 THROW_IO_ERROR( wxT( "stream was not parsed correctly!" ) );
6210
6211 if( reader.GetRemainingBytes() != 0 )
6212 THROW_IO_ERROR( wxT( "stream is not fully parsed" ) );
6213
6214 LIB_SYMBOL* symbol = symbols[0];
6215 symbol->FixupDrawItems();
6216 fixupSymbolPinNameNumbers( symbol );
6217
6218 SCH_FIELD& valField = symbol->GetValueField();
6219
6220 if( valField.GetText().IsEmpty() )
6221 valField.SetText( name );
6222
6223 symbol->SetName( name );
6224 ret[name] = symbol;
6225 }
6226
6227 if( m_reporter )
6228 {
6229 for( const auto& [msg, severity] : m_errorMessages )
6230 m_reporter->Report( msg, severity );
6231 }
6232
6233 m_errorMessages.clear();
6234
6235 return ret;
6236}
6237
6238
6239long long SCH_IO_ALTIUM::getLibraryTimestamp( const wxString& aLibraryPath ) const
6240{
6241 wxFileName fn( aLibraryPath );
6242
6243 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
6244 return fn.GetModificationTime().GetValue().GetValue();
6245 else
6246 return 0;
6247}
6248
6249
6250void SCH_IO_ALTIUM::ensureLoadedLibrary( const wxString& aLibraryPath,
6251 const std::map<std::string, UTF8>* aProperties )
6252{
6253 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
6254 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
6255
6256 if( m_libCache.count( aLibraryPath ) )
6257 {
6258 wxCHECK( m_timestamps.count( aLibraryPath ), /*void*/ );
6259
6260 if( m_timestamps.at( aLibraryPath ) == getLibraryTimestamp( aLibraryPath ) )
6261 return;
6262 }
6263
6264 std::vector<std::unique_ptr<ALTIUM_COMPOUND_FILE>> compoundFiles;
6265
6266 wxFileName fileName( aLibraryPath );
6267 m_libName = fileName.GetName();
6268
6269 try
6270 {
6271 if( aLibraryPath.Lower().EndsWith( wxS( ".schlib" ) ) )
6272 {
6273 m_isIntLib = false;
6274
6275 compoundFiles.push_back( std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
6276 }
6277 else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) )
6278 {
6279 m_isIntLib = true;
6280
6281 std::unique_ptr<ALTIUM_COMPOUND_FILE> intCom = std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath );
6282
6283 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> schLibFiles = intCom->EnumDir( L"SchLib" );
6284
6285 for( const auto& [schLibName, cfe] : schLibFiles )
6286 {
6287 std::unique_ptr<ALTIUM_COMPOUND_FILE> decodedStream = std::make_unique<ALTIUM_COMPOUND_FILE>();
6288
6289 if( intCom->DecodeIntLibStream( *cfe, decodedStream.get() ) )
6290 compoundFiles.emplace_back( std::move( decodedStream ) );
6291 }
6292 }
6293
6294 CASE_INSENSITIVE_MAP<LIB_SYMBOL*>& cacheMapRef = m_libCache[aLibraryPath];
6295
6296 for( const std::unique_ptr<ALTIUM_COMPOUND_FILE>& altiumSchFilePtr : compoundFiles )
6297 {
6298 CASE_INSENSITIVE_MAP<LIB_SYMBOL*> parsed = ParseLibFile( *altiumSchFilePtr );
6299 cacheMapRef.insert( parsed.begin(), parsed.end() );
6300 }
6301
6302 m_timestamps[aLibraryPath] = getLibraryTimestamp( aLibraryPath );
6303 }
6304 catch( const CFB::CFBException& exception )
6305 {
6306 THROW_IO_ERROR( exception.what() );
6307 }
6308 catch( const std::exception& exc )
6309 {
6310 THROW_IO_ERRORF( _( "Error parsing Altium library: %s" ), exc.what() );
6311 }
6312}
6313
6314
6316 std::vector<int>& aFontSizes )
6317{
6318 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "FileHeader" } );
6319
6320 if( file == nullptr )
6321 THROW_IO_ERROR( wxT( "FileHeader not found" ) );
6322
6323 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
6324
6325 if( reader.GetRemainingBytes() <= 0 )
6326 THROW_IO_ERROR( wxT( "FileHeader does not contain any data" ) );
6327
6328 std::map<wxString, wxString> properties = reader.ReadProperties();
6329
6330 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
6331
6332 if( libtype.CmpNoCase( "Protel for Windows - Schematic Library Editor Binary File Version 5.0" ) )
6333 THROW_IO_ERROR( _( "Expected Altium Schematic Library file version 5.0" ) );
6334
6335 for( auto& [key, value] : properties )
6336 {
6337 wxString upperKey = key.Upper();
6338 wxString remaining;
6339
6340 if( upperKey.StartsWith( "SIZE", &remaining ) )
6341 {
6342 if( !remaining.empty() )
6343 {
6344 int ind = wxAtoi( remaining );
6345
6346 if( ind < 1 )
6347 continue;
6348
6349 if( static_cast<int>( aFontSizes.size() ) < ind )
6350 aFontSizes.resize( ind );
6351
6352 // Altium stores in pt. 1 pt = 1/72 inch. 1 mil = 1/1000 inch.
6353 int scaled = schIUScale.MilsToIU( wxAtoi( value ) * 72.0 / 10.0 );
6354 aFontSizes[ind - 1] = scaled;
6355 }
6356 }
6357 }
6358}
6359
6360
6361void SCH_IO_ALTIUM::doEnumerateSymbolLib( const wxString& aLibraryPath,
6362 const std::map<std::string, UTF8>* aProperties,
6363 std::function<void(const wxString&, LIB_SYMBOL*)> aInserter )
6364{
6365 ensureLoadedLibrary( aLibraryPath, aProperties );
6366
6367 bool powerSymbolsOnly = ( aProperties &&
6368 aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly ) );
6369
6370 auto it = m_libCache.find( aLibraryPath );
6371
6372 if( it != m_libCache.end() )
6373 {
6374 for( auto& [libnameStr, libSymbol] : it->second )
6375 {
6376 if( powerSymbolsOnly && !libSymbol->IsPower() )
6377 continue;
6378
6379 aInserter( libnameStr, libSymbol );
6380 }
6381 }
6382}
6383
6384
6385void SCH_IO_ALTIUM::EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
6386 const std::map<std::string, UTF8>* aProperties )
6387{
6388 doEnumerateSymbolLib( aLibraryPath, aProperties,
6389 [&]( const wxString& aStr, LIB_SYMBOL* )
6390 {
6391 aSymbolNameList.Add( aStr );
6392 } );
6393}
6394
6395
6396void SCH_IO_ALTIUM::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
6397 const wxString& aLibraryPath,
6398 const std::map<std::string, UTF8>* aProperties )
6399{
6400 doEnumerateSymbolLib( aLibraryPath, aProperties,
6401 [&]( const wxString&, LIB_SYMBOL* aSymbol )
6402 {
6403 aSymbolList.emplace_back( aSymbol );
6404 } );
6405}
6406
6407
6408LIB_SYMBOL* SCH_IO_ALTIUM::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
6409 const std::map<std::string, UTF8>* aProperties )
6410{
6411 ensureLoadedLibrary( aLibraryPath, aProperties );
6412
6413 auto it = m_libCache.find( aLibraryPath );
6414
6415 if( it != m_libCache.end() )
6416 {
6417 auto it2 = it->second.find( aAliasName );
6418
6419 if( it2 != it->second.end() )
6420 return it2->second;
6421 }
6422
6423 return nullptr;
6424}
int blue
int red
int green
int index
const char * name
ALTIUM_SCH_RECORD
ASCH_RECORD_ORIENTATION
const int ALTIUM_COMPONENT_NONE
ASCH_LABEL_JUSTIFICATION
ASCH_POLYLINE_LINESTYLE
ASCH_POWER_PORT_STYLE
wxString AltiumPinDesignatorToKiCad(const wxString &aDesignator)
Convert an Altium pin designator string to the equivalent KiCad pin number.
wxString AltiumSchSpecialStringsToKiCadVariables(const wxString &aString, const std::map< wxString, wxString > &aOverrides, std::set< wxString > *aUnmapped)
Convert the special strings in Altium schematic text to KiCad variables.
wxString AltiumEscapeTextVars(const wxString &aText)
Escape the KiCad variables and expressions in literal Altium text, which Altium shows as typed.
wxString AltiumPinNamesToKiCad(wxString &aString)
LIB_ID AltiumToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
std::vector< ALTIUM_PROJECT_VARIANT > ParseAltiumProjectVariants(const wxString &aPrjPcbPath)
Parse all [ProjectVariantN] sections from an Altium .PrjPcb project file.
std::map< wxString, wxString > ParseAltiumProjectParameters(const wxString &aPrjPcbPath)
Parse all [ParameterN] sections from an Altium .PrjPcb project file.
bool AltiumPowerPortNamesTakePriority(const wxString &aPrjPcbPath)
Read the project's Power Port Names Take Priority netlist option.
void ApplyAltiumProjectParametersToProject(PROJECT *aProject, const std::map< wxString, wxString > &aParameters)
Register Altium project parameters as KiCad project text variables so that imported board text refere...
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
constexpr double ARC_LOW_DEF_MM
Definition base_units.h:134
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.
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
std::map< wxString, ValueType, DETAIL::CASE_INSENSITIVE_COMPARER > CASE_INSENSITIVE_MAP
std::map< wxString, wxString > ReadProperties()
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.
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
Generic cubic Bezier representation.
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:256
constexpr coord_type GetBottom() const
Definition box2.h:219
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
Calculate the connectivity of a schematic and generate netlists.
const NET_MAP & GetNetMap() const
void Recalculate(const SCH_SHEET_LIST &aSheetList, bool aUnconditional=false, std::function< void(SCH_ITEM *)> *aChangedItemHandler=nullptr, PROGRESS_REPORTER *aProgressReporter=nullptr)
Update the connection graph for the given list of sheets.
A subgraph is a set of items that are electrically connected on a single sheet.
double Sin() const
Definition eda_angle.h:177
bool IsHorizontal() const
Definition eda_angle.h:141
double Cos() const
Definition eda_angle.h:196
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
void SetModified()
Definition eda_item.cpp:260
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:337
virtual void SetCenter(const VECTOR2I &aCenter)
FILL_T GetFillMode() const
Definition eda_shape.h:150
void SetLineStyle(const LINE_STYLE aStyle)
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:177
virtual void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:375
virtual void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:372
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:162
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:283
COLOR4D GetFillColor() const
Definition eda_shape.h:161
virtual void SetWidth(int aWidth)
void SetFillMode(FILL_T aFill)
virtual void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:287
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
void SetAttributes(const EDA_TEXT &aSrc, bool aSetPosition=true)
Set the text attributes from another instance.
Definition eda_text.cpp:394
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
void FlipHJustify()
Definition eda_text.h:247
virtual EDA_ANGLE GetTextAngle() const
Definition eda_text.h:178
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:310
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:242
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:268
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:253
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
Plain ellipse / elliptical-arc data.
Definition ellipse.h:32
RAII class to set and restore the fontconfig reporter.
Definition reporter.h:385
const wxString & GetName() const
Return a brief hard coded name for this IO interface.
Definition io_base.h:79
REPORTER * m_reporter
Reporter to log errors/warnings to, may be nullptr.
Definition io_base.h:242
PROGRESS_REPORTER * m_progressReporter
Progress reporter to track the progress of the operation, may be nullptr.
Definition io_base.h:245
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Definition io_base.cpp:71
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:309
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:596
wxString AsString() const
Definition kiid.cpp:423
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
UTF8 Format() const
Definition lib_id.cpp:132
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:205
Define a library symbol object.
Definition lib_symbol.h:114
void SetGlobalPower()
void FixupDrawItems()
This function finds the filled draw items that are covering up smaller draw items and replaces their ...
void SetBodyStyleNames(const std::vector< wxString > &aBodyStyleNames)
Definition lib_symbol.h:913
wxString GetName() const override
Definition lib_symbol.h:183
void SetUnitCount(int aCount, bool aDuplicateDrawItems)
Set the units per symbol count.
void SetDescription(const wxString &aDescription)
Set the Description field text value.
void SetKeyWords(const wxString &aKeyWords)
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
int GetBodyStyleCount() const override
The body styles are a property of the drawings, which a derived symbol inherits from its root symbol ...
int GetUnitCount() const override
void SetLibId(const LIB_ID &aLibId)
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
virtual void SetName(const wxString &aName)
SCH_FIELD & GetReferenceField()
Definition lib_symbol.h:451
static LOAD_INFO_REPORTER & GetInstance()
Definition reporter.cpp:351
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
int GetHeightIU(double aIUScale) const
Get the page height in IU.
Definition page_info.h:163
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
static void SetCustomWidthMils(double aWidthInMils)
Set the width of Custom page in mils for any custom page constructed or made via SetType() after maki...
static void SetCustomHeightMils(double aHeightInMils)
Set the height of Custom page in mils for any custom page constructed or made via SetType() after mak...
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
VECTOR2I GetSize() const
void SetImageScale(double aScale)
Set the image "zoom" value.
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
Holds all the data relating to one schematic.
Definition schematic.h:149
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:297
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
Replace the top level sheets, rebuilding the hierarchy and connectivity around them.
SCH_SHEET & Root() const
Definition schematic.h:208
VECTOR2I GetSize() const
void SetSize(const VECTOR2I &aSize)
VECTOR2I GetPosition() const override
Class for a wire to bus entry.
static bool IsBusLabel(const wxString &aLabel)
Test if aLabel has a bus notation.
VECTOR2I GetPosition() const override
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
Definition sch_field.h:230
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
void ParseFileHeader(const ALTIUM_COMPOUND_FILE &aAltiumSchFile)
std::vector< wxFileName > m_projectDocuments
void ParseSignalHarness(const std::map< wxString, wxString > &aProperties)
std::map< int, ASCH_TEMPLATE > m_altiumTemplates
std::map< int, ASCH_SYMBOL > m_altiumComponents
void ParsePort(const ASCH_PORT &aElem)
void ParseNote(const std::map< wxString, wxString > &aProperties)
std::map< std::vector< wxString >, SCH_SCREEN * > m_loadedScreens
void ParseAltiumSch(const wxString &aFileName)
std::vector< ASCH_PORT > m_altiumPortsCurrentSheet
void ParseSheetName(const std::map< wxString, wxString > &aProperties)
void ParseBusEntry(const std::map< wxString, wxString > &aProperties)
wxFileName ResolveSheetFileName(const wxString &aParentPath, const wxString &aSheetFileName) const
SCH_SHEET * getCurrentSheet()
void ParseStorage(const ALTIUM_COMPOUND_FILE &aAltiumSchFile)
std::map< wxString, LIB_SYMBOL * > m_powerSymbols
void ParseBus(const std::map< wxString, wxString > &aProperties)
void ParseFileName(const std::map< wxString, wxString > &aProperties)
static bool isASCIIFile(const wxString &aFileName)
wxString m_libName
LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aAliasName, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
std::map< int, SCH_SHEET * > m_sheets
void ParseLibHeader(const ALTIUM_COMPOUND_FILE &aAltiumSchFile, std::vector< int > &aFontSizes)
void ParseHarnessPort(const ASCH_PORT &aElem)
void ParseRoundRectangle(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void pruneCyclicSheets(const std::vector< SCH_SHEET * > &aCyclicSheets, SCH_SCREEN *aScreen)
Remove sheet symbols referencing a parse-stack file (an unrepresentable cycle), converting their pins...
int GetModifyHash() const override
Return the modification hash from the library cache.
void ParseLibDesignator(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym, std::vector< int > &aFontSize=nullint)
std::map< wxString, CASE_INSENSITIVE_MAP< LIB_SYMBOL * > > m_libCache
std::map< int, int > m_altiumImplementationList
void attachEmptySheetScreen(SCH_SHEET *aSheet, const wxString &aMissingFile)
Give a sheet whose source file is missing an empty screen with a file of its own; aMissingFile is emp...
std::map< wxString, SCH_SCREEN * > m_missingScreens
void ParseTextFrame(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym, std::vector< int > &aFontSize=nullint)
void ParsePolygon(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void fixupSymbolPinNameNumbers(SYMBOL *aSymbol)
VECTOR2I m_sheetOffset
void EnsureSheetSymbolNames()
void ParseRecord(int index, std::map< wxString, wxString > &properties, const wxString &aSectionName)
std::vector< ASCH_PORT > m_altiumHarnessPortsCurrentSheet
void ParseDesignator(const std::map< wxString, wxString > &aProperties)
int m_harnessOwnerIndexOffset
void ParsePortHelper(const ASCH_PORT &aElem)
std::set< wxString > m_subsheetFiles
static bool checkFileHeader(const wxString &aFileName)
long long getLibraryTimestamp(const wxString &aLibraryPath) const
void ParseSheetEntry(const std::map< wxString, wxString > &aProperties)
SCH_SHEET * m_rootSheet
static bool isBinaryFile(const wxString &aFileName)
void ParseHarnessType(const std::map< wxString, wxString > &aProperties)
wxString m_rootFilepath
void ParseJunction(const std::map< wxString, wxString > &aProperties)
SCHEMATIC * m_schematic
wxString m_rootSheetFile
void ParseLibParameter(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym, std::vector< int > &aFontSize=nullint)
std::set< wxString > m_parsedFiles
void ParseASCIISchematic(const wxString &aFileName)
void ParsePolyline(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::unique_ptr< ASCH_SHEET > m_altiumSheet
void ParseComponent(int aIndex, const std::map< wxString, wxString > &aProperties)
std::unique_ptr< TITLE_BLOCK > m_currentTitleBlock
SCH_SHEET * LoadSchematicProject(SCHEMATIC *aSchematic, const std::map< std::string, UTF8 > *aProperties)
void ParseImage(const std::map< wxString, wxString > &aProperties)
void ParseArc(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseNetLabel(const std::map< wxString, wxString > &aProperties)
void ParseNoERC(const std::map< wxString, wxString > &aProperties)
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
std::unordered_map< wxString, SEVERITY > m_errorMessages
std::map< wxString, int > m_parsingFiles
void ParseImplementationList(int aIndex, const std::map< wxString, wxString > &aProperties)
std::vector< LIB_SYMBOL * > ParseLibComponent(const std::map< wxString, wxString > &aProperties)
void ParseSheet(const std::map< wxString, wxString > &aProperties)
void ParseParameter(const std::map< wxString, wxString > &aProperties)
bool ShouldPutItemOnSheet(int aOwnerindex)
void NormalizeRepeatedSheetInstances()
void AddLibTextBox(const ASCH_TEXT_FRAME *aElem, std::vector< LIB_SYMBOL * > &aSymbol=nullsym, std::vector< int > &aFontSize=nullint)
bool CanReadSchematicFile(const wxString &aFileName) const override
Checks if this SCH_IO can read the specified schematic file.
void ParsePieChart(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseEllipticalArc(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::map< int, HARNESS > m_altiumHarnesses
void ParseEllipse(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::set< wxString > m_sheetSpecialStrings
void ParseWire(const std::map< wxString, wxString > &aProperties)
void ParseHarnessEntry(const std::map< wxString, wxString > &aProperties)
void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
void ParseImplementation(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseTemplate(int aIndex, const std::map< wxString, wxString > &aProperties)
void ParseAdditional(const ALTIUM_COMPOUND_FILE &aAltiumSchFile)
friend struct PARSE_FILE_GUARD
std::set< wxString > m_sheetFiles
std::set< wxString > m_unsetSheetParams
void AddTextBox(const ASCH_TEXT_FRAME *aElem)
void ParsePowerPort(const std::map< wxString, wxString > &aProperties)
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
std::map< const SCH_SYMBOL *, wxString > m_altiumSymbolToUid
const ASCH_STORAGE_FILE * GetFileFromStorage(const wxString &aFilename) const
void ParseRectangle(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void doEnumerateSymbolLib(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties, std::function< void(const wxString &, LIB_SYMBOL *)> aInserter)
wxString getLibName()
void NameNetsAfterNetLabels()
Name power nets after their net labels, as Altium does unless Power Port Names Take Priority is set.
std::vector< ASCH_STORAGE_FILE > m_altiumStorage
SCH_SHEET_PATH m_sheetPath
void PostProcessBusLabels()
wxString claimSheetFile(const wxString &aStem)
Claim a file in the project folder for a sheet screen that neither the root nor another sheet holds,...
void ParseLabel(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym, std::vector< int > &aFontSize=nullint)
CASE_INSENSITIVE_MAP< LIB_SYMBOL * > ParseLibFile(const ALTIUM_COMPOUND_FILE &aAltiumSchFile)
void ParseBezier(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseLine(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseCircle(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::map< int, SCH_SYMBOL * > m_symbols
void ParseSheetSymbol(int aIndex, const std::map< wxString, wxString > &aProperties)
std::map< wxString, long long > m_timestamps
wxFileName getLibFileName()
SCH_SCREEN * getCurrentScreen()
void ParsePin(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::map< int, LIB_SYMBOL * > m_libSymbols
void ParseHarnessConnector(int aIndex, const std::map< wxString, wxString > &aProperties)
void ensureLoadedLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties)
virtual bool CanReadSchematicFile(const wxString &aFileName) const
Checks if this SCH_IO can read the specified schematic file.
Definition sch_io.cpp:47
SCH_IO(const wxString &aName)
Definition sch_io.h:406
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
virtual void SetBodyStyle(int aBodyStyle)
Definition sch_item.h:252
virtual void SetUnit(int aUnit)
Definition sch_item.h:242
void MirrorHorizontally(int aCenter) override
Mirror item horizontally about aCenter.
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:179
const BOX2I GetBoundingBox() const override
Return the bounding box of the label including its fields.
void MirrorVertically(int aCenter) override
Mirror item vertically about aCenter.
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:137
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition sch_line.cpp:908
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Definition sch_line.cpp:809
bool IsWire() const
Return true if the line is a wire.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition sch_line.cpp:316
void SetLineWidth(const int aSize)
Definition sch_line.cpp:438
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
SEG GetSeg() const
Get the geometric aspect of the wire as a SEG.
Definition sch_line.h:155
bool IsBus() const
Return true if the line is a bus.
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:199
int GetLineWidth() const
Definition sch_line.h:195
KIGFX::COLOR4D GetLineColor() const
Return COLOR4D::UNSPECIFIED if a custom color hasn't been set for this line.
Definition sch_line.cpp:396
void SetLineColor(const KIGFX::COLOR4D &aColor)
Definition sch_line.cpp:372
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:146
bool IsGlobalPower() const
Return whether this pin forms a global power connection: i.e., is part of a power symbol and of type ...
Definition sch_pin.cpp:481
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:826
SCH_SCREEN * GetNext()
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
SCH_SCREEN * GetFirst()
void ClearEditFlags()
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:804
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:171
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition.
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:145
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:122
const wxString & GetFileName() const
Definition sch_screen.h:157
bool IsTerminalPoint(const VECTOR2I &aPosition, int aLayer) const
Test if aPosition is a connection point on aLayer.
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
void Update(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Refresh a changed item in the tree and invalidate connectivity.
void DeleteItem(SCH_ITEM *aItem)
Remove aItem from the linked list and deletes the object.
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:89
void SetFilled(bool aFilled) override
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
void Normalize()
void AddPoint(const VECTOR2I &aPosition)
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:59
VECTOR2I GetPosition() const override
Definition sch_shape.h:88
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
wxString GetNextPageNumber() const
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
SCH_SCREEN * LastScreen()
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
void pop_back()
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
void SetPosition(const VECTOR2I &aPosition) override
void SetSide(SHEET_SIDE aEdge)
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
void SetBorderColor(KIGFX::COLOR4D aColor)
Definition sch_sheet.h:160
void SetFileName(const wxString &aFilename)
Definition sch_sheet.h:398
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition sch_sheet.h:392
wxString GetName() const
Definition sch_sheet.h:148
void SetBackgroundColor(KIGFX::COLOR4D aColor)
Definition sch_sheet.h:163
void SetName(const wxString &aName)
Definition sch_sheet.h:149
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:151
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Variant information for a schematic symbol.
void InitializeAttributes(const SCH_SYMBOL &aSymbol)
Schematic symbol object.
Definition sch_symbol.h:75
void SetLibId(const LIB_ID &aName)
void SetPosition(const VECTOR2I &aPosition) override
Definition sch_symbol.h:909
void SetBodyStyle(int aBodyStyle) override
bool AddSheetPathReferenceEntryIfMissing(const KIID_PATH &aSheetPath)
Add an instance to the alternate references list (m_instances), if this entry does not already exist.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
void SetOrientation(int aOrientation)
Compute the new transform matrix based on aOrientation for the symbol which is applied to the current...
void SetFootprintFieldText(const wxString &aFootprint)
VECTOR2I GetPosition() const override
Definition sch_symbol.h:908
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
void AddVariant(const SCH_SHEET_PATH &aInstance, const SCH_SYMBOL_VARIANT &aVariant)
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
void SetLibSymbol(LIB_SYMBOL *aLibSymbol)
Set this schematic symbol library symbol reference to aLibSymbol.
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
VECTOR2I GetPosition() const override
Definition sch_text.h:143
virtual void Rotate90(bool aClockwise)
Definition sch_text.cpp:255
void SetPosition(const VECTOR2I &aPosition) override
Definition sch_text.h:144
Simple container to manage line stroke parameters.
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
void SetWidth(int aWidth)
void SetColor(const KIGFX::COLOR4D &aColor)
KIGFX::COLOR4D GetColor() const
static const char * PropPowerSymsOnly
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition symbol.h:60
virtual void SetShowPinNumbers(bool aShow)
Set or clear the pin number visibility flag.
Definition symbol.h:174
const TRANSFORM & GetTransform() const
Definition symbol.h:247
virtual void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
Definition symbol.h:168
for transforming drawing coordinates for a wxDC device context.
Definition transform.h:42
TRANSFORM InverseTransform() const
Calculate the Inverse mirror/rotation transform.
Definition transform.cpp:55
VECTOR2I TransformCoordinate(const VECTOR2I &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition transform.cpp:40
wxString wx_str() const
Definition utf8.cpp:41
bool m_ExcludedFromBOM
std::map< wxString, wxString > m_Fields
bool m_ExcludedFromPosFiles
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:312
static REPORTER & GetInstance()
Definition reporter.cpp:324
@ PURERED
Definition color4d.h:67
@ PUREBLUE
Definition color4d.h:64
@ BLACK
Definition color4d.h:40
#define DEFAULT_PINNUM_SIZE
The default pin number size when creating pins(can be changed in preference menu)
#define DEFAULT_PINNAME_SIZE
The default pin name size when creating pins(can be changed in preference menu)
#define DEFAULT_TEXT_SIZE
The default text size in mils. (can be changed in preference menu)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
@ DEGREES_T
Definition eda_angle.h:30
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
FILL_T
Definition eda_fill.h:29
@ FILLED_WITH_COLOR
Definition eda_fill.h:33
@ NO_FILL
Definition eda_fill.h:30
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_fill.h:32
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
#define IS_NEW
New item, just created.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define THROW_IO_ERRORF(msg,...)
#define THROW_IO_CANCELLED()
static const std::string KiCadSchematicFileExtension
static const std::string KiCadSymbolLibFileExtension
KIID niluuid(0)
@ LAYER_DEVICE
Definition layer_ids.h:494
@ LAYER_WIRE
Definition layer_ids.h:480
@ LAYER_NOTES
Definition layer_ids.h:495
@ LAYER_BUS
Definition layer_ids.h:481
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition eda_units.h:172
bool fileStartsWithPrefix(const wxString &aFilePath, const wxString &aPrefix, bool aIgnoreWhitespace)
Check if a file starts with a defined string.
Definition io_utils.cpp:33
const std::vector< uint8_t > COMPOUND_FILE_HEADER
Definition io_utils.cpp:30
bool fileHasBinaryHeader(const wxString &aFilePath, const std::vector< uint8_t > &aHeader, size_t aOffset)
Check if a file starts with a defined binary header.
Definition io_utils.cpp:60
const VECTOR2I & GetOtherEnd(const SEG &aSeg, const VECTOR2I &aPoint)
Get the end point of the segment that is not the given point.
bool signbit(T v)
Integral version of std::signbit that works all compilers.
Definition kicad_algo.h:172
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ PT_OUTPUT
usual output
Definition pin_type.h:34
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:36
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:35
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:45
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:44
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:41
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_DEBUG
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
static COLOR4D GetColorFromInt(int color)
static ITEM_NET_CODES itemNetCodes(const CONNECTION_GRAPH &aGraph, const std::map< const SCH_ITEM *, const SCH_ITEM * > &aOriginals)
static void SetLibShapeFillAndColor(const ASCH_FILL_INTERFACE &elem, SCH_SHAPE *shape, ALTIUM_SCH_RECORD aType, int aStrokeColor)
VECTOR2I HelperGeneratePowerPortGraphics(LIB_SYMBOL *aKsymbol, ASCH_POWER_PORT_STYLE aStyle, REPORTER *aReporter)
static void SetSchShapeLine(const ASCH_BORDER_INTERFACE &elem, SCH_SHAPE *shape)
static const VECTOR2I GetRelativePosition(const VECTOR2I &aPosition, const SCH_SYMBOL *aSymbol)
static void SetLibShapeLine(const ASCH_BORDER_INTERFACE &elem, SCH_SHAPE *shape, ALTIUM_SCH_RECORD aType)
static wxString AltiumParseKey(const wxFileName &aFileName)
void AdjustTextForSymbolOrientation(SCH_TEXT *aText, const ASCH_SYMBOL &aSymbol)
static LINE_STYLE GetPlotDashType(const ASCH_POLYLINE_LINESTYLE linestyle)
static void SetSchShapeFillAndColor(const ASCH_FILL_INTERFACE &elem, SCH_SHAPE *shape)
void AdjustFieldForSymbolOrientation(SCH_FIELD *aField, const ASCH_SYMBOL &aSymbol)
wxString AltiumDeriveSheetName(const wxString &aFilename, const std::set< wxString > &aExistingNames)
static bool sameNets(const ITEM_NET_CODES &aBefore, const ITEM_NET_CODES &aAfter)
wxString AltiumWrapBusLabel(const wxString &aText)
std::map< std::pair< KIID_PATH, const SCH_ITEM * >, int > ITEM_NET_CODES
void SetTextPositioning(EDA_TEXT *text, ASCH_LABEL_JUSTIFICATION justification, ASCH_RECORD_ORIENTATION orientation)
@ AUTOPLACE_AUTO
Definition sch_item.h:75
@ L_BIDI
Definition sch_label.h:100
@ L_UNSPECIFIED
Definition sch_label.h:102
@ L_OUTPUT
Definition sch_label.h:99
@ L_INPUT
Definition sch_label.h:98
Utility functions for working with shapes.
wxString UnescapeString(const wxString &aSource)
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_QUOTED_STR
@ CTX_NETNAME
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
LINE_STYLE
Dashed line types.
A project-level assembly variant parsed from an Altium .PrjPcb file.
A single component variation within an Altium project variant.
wxString uniqueId
wxString designator
double m_StartAngle
VECTOR2I m_Center
std::vector< VECTOR2I > points
VECTOR2I corner
VECTOR2I location
std::vector< VECTOR2I > points
ASCH_LABEL_JUSTIFICATION justification
ASCH_RECORD_ORIENTATION orientation
ASCH_RECORD_ORIENTATION orientation
ASCH_SHEET_ENTRY_SIDE m_harnessConnectorSide
int DistanceFromTop
ASCH_SHEET_ENTRY_SIDE Side
wxString Name
ASCH_RECORD_ORIENTATION orientation
ASCH_LABEL_JUSTIFICATION justification
ASCH_POLYLINE_LINESTYLE LineStyle
ASCH_LABEL_JUSTIFICATION justification
ASCH_RECORD_ORIENTATION orientation
ASCH_RECORD_ORIENTATION orientation
ASCH_LABEL_JUSTIFICATION justification
VECTOR2I location
ASCH_PIN_SYMBOL::PTYPE symbolOuterEdge
VECTOR2I kicadLocation
wxString designator
ASCH_PIN_ELECTRICAL electrical
ASCH_PIN_SYMBOL::PTYPE symbolInnerEdge
ASCH_RECORD_ORIENTATION orientation
std::vector< VECTOR2I > points
ASCH_POLYLINE_LINESTYLE LineStyle
std::vector< VECTOR2I > Points
VECTOR2I Location
ASCH_PORT_IOTYPE IOtype
wxString HarnessType
ASCH_PORT_STYLE Style
ASCH_POWER_PORT_STYLE style
ASCH_RECORD_ORIENTATION orientation
wxString name
int distanceFromTop
ASCH_SHEET_ENTRY_SIDE side
ASCH_PORT_IOTYPE iotype
wxString harnessType
ASCH_RECORD_ORIENTATION orientation
std::vector< VECTOR2I > points
std::vector< char > data
wxString componentdescription
wxString libreference
wxString sourcelibraryname
ASCH_TEXT_FRAME_ALIGNMENT Alignment
std::vector< VECTOR2I > points
ASCH_SHEET_ENTRY_SIDE m_harnessConnectorSide
VECTOR2I m_location
std::vector< HARNESS_PORT > m_ports
HARNESS_PORT m_entry
wxString m_name
VECTOR2I m_size
SCH_IO_ALTIUM * m_owner
PARSE_FILE_GUARD(SCH_IO_ALTIUM *aOwner, std::vector< wxString > aKeys)
PARSE_FILE_GUARD & operator=(const PARSE_FILE_GUARD &)=delete
std::vector< wxString > m_keys
PARSE_FILE_GUARD(SCH_IO_ALTIUM *aOwner, const wxString &aKey)
PARSE_FILE_GUARD(const PARSE_FILE_GUARD &)=delete
A simple container for sheet instance information.
@ SYM_ORIENT_270
Definition symbol.h:39
@ SYM_MIRROR_Y
Definition symbol.h:41
@ SYM_ORIENT_180
Definition symbol.h:38
@ SYM_ORIENT_90
Definition symbol.h:37
@ SYM_ORIENT_0
Definition symbol.h:36
@ USER
The field ID hasn't been set yet; field is invalid.
@ INTERSHEET_REFS
Global label cross-reference page numbers.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS top(path, &reporter)
KIBIS_PIN * pin
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
@ SCH_LINE_T
Definition typeinfo.h:159
@ LIB_SYMBOL_T
Definition typeinfo.h:144
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_LABEL_T
Definition typeinfo.h:163
@ SCH_SHEET_T
Definition typeinfo.h:171
@ SCH_HIER_LABEL_T
Definition typeinfo.h:165
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:164
@ SCH_PIN_T
Definition typeinfo.h:149
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
Definition of file extensions used in Kicad.