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