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