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 const ENTRY_LIST* entries = nullptr;
689
690 auto symUidIt = m_altiumSymbolToUid.find( symbol );
691
692 if( symUidIt != m_altiumSymbolToUid.end() )
693 {
694 auto varIt = variantsByUid.find( symUidIt->second );
695
696 if( varIt != variantsByUid.end() )
697 entries = &varIt->second;
698 }
699
700 if( !entries )
701 {
702 wxString ref = symbol->GetRef( &path );
703 auto varIt = variantsByDesignator.find( ref );
704
705 if( varIt != variantsByDesignator.end() )
706 entries = &varIt->second;
707 }
708
709 if( !entries )
710 continue;
711
712 for( const auto& [variantName, entry] : *entries )
713 {
714 SCH_SYMBOL_VARIANT variant( variantName );
715 variant.InitializeAttributes( *symbol );
716
717 if( entry->kind == 1 )
718 {
719 variant.m_DNP = true;
720 variant.m_ExcludedFromBOM = true;
721 variant.m_ExcludedFromPosFiles = true;
722 }
723 else if( entry->kind == 0 )
724 {
725 for( const auto& [key, value] : entry->alternateFields )
726 {
727 if( key.CmpNoCase( wxS( "LibReference" ) ) == 0 )
728 variant.m_Fields[wxS( "Value" )] = value;
729 else if( key.CmpNoCase( wxS( "Description" ) ) == 0 )
730 variant.m_Fields[wxS( "Description" )] = value;
731 else if( key.CmpNoCase( wxS( "Footprint" ) ) == 0 )
732 variant.m_Fields[wxS( "Footprint" )] = value;
733 }
734 }
735
736 symbol->AddVariant( path, variant );
737 }
738 }
739 }
740 }
741 }
742
743 // Set up the default netclass wire & bus width based on imported wires & buses.
744 //
745
746 int minWireWidth = std::numeric_limits<int>::max();
747 int minBusWidth = std::numeric_limits<int>::max();
748
749 for( SCH_SCREEN* screen = allSheets.GetFirst(); screen != nullptr; screen = allSheets.GetNext() )
750 {
751 std::vector<SCH_MARKER*> markers;
752
753 for( SCH_ITEM* item : screen->Items().OfType( SCH_LINE_T ) )
754 {
755 SCH_LINE* line = static_cast<SCH_LINE*>( item );
756
757 if( line->IsWire() && line->GetLineWidth() > 0 )
758 minWireWidth = std::min( minWireWidth, line->GetLineWidth() );
759
760 if( line->IsBus() && line->GetLineWidth() > 0 )
761 minBusWidth = std::min( minBusWidth, line->GetLineWidth() );
762 }
763 }
764
765 std::shared_ptr<NET_SETTINGS>& netSettings = m_schematic->Project().GetProjectFile().NetSettings();
766
767 if( minWireWidth < std::numeric_limits<int>::max() )
768 netSettings->GetDefaultNetclass()->SetWireWidth( minWireWidth );
769
770 if( minBusWidth < std::numeric_limits<int>::max() )
771 netSettings->GetDefaultNetclass()->SetBusWidth( minBusWidth );
772
773 return m_rootSheet;
774}
775
776
778{
779 return m_sheetPath.LastScreen();
780}
781
782
787
788
790{
791 SCH_SCREEN* screen = getCurrentScreen();
792 wxCHECK( screen, /* void */ );
793
794 std::vector<SCH_LINE*> busLines;
795 std::map<VECTOR2I, std::vector<SCH_LINE*>> busLineMap;
796
797 for( SCH_ITEM* elem : screen->Items().OfType( SCH_LINE_T) )
798 {
799 SCH_LINE* line = static_cast<SCH_LINE*>( elem );
800
801 if( line->IsBus() )
802 {
803 busLines.push_back( line );
804 busLineMap[ line->GetStartPoint() ].push_back( line );
805 busLineMap[ line->GetEndPoint() ].push_back( line );
806 }
807 }
808
809 std::function<SCH_LABEL*(VECTOR2I, std::set<SCH_LINE*>&)> walkBusLine =
810 [&]( const VECTOR2I& aStart, std::set<SCH_LINE*>& aVisited ) -> SCH_LABEL*
811 {
812 auto it = busLineMap.find( aStart );
813
814 if( it == busLineMap.end() )
815 return nullptr;
816
817 for( SCH_LINE* line : it->second )
818 {
819 // Skip lines we've already checked to avoid cycles
820 if( aVisited.count( line ) )
821 continue;
822
823 aVisited.insert( line );
824
825 for( SCH_ITEM* elem : screen->Items().Overlapping( SCH_LABEL_T, line->GetBoundingBox() ) )
826 {
827 SCH_LABEL* label = static_cast<SCH_LABEL*>( elem );
828
829 if( line->HitTest( label->GetPosition() ) )
830 return label;
831 }
832
833 SCH_LABEL* result = walkBusLine( KIGEOM::GetOtherEnd( line->GetSeg(), aStart ), aVisited );
834
835 if( result )
836 return result;
837 }
838
839 return nullptr;
840 };
841
842 for( auto& [_, harness] : m_altiumHarnesses )
843 {
844 std::shared_ptr<BUS_ALIAS> alias = std::make_shared<BUS_ALIAS>();
845 alias->SetName( harness.m_name );
846
847 for( HARNESS::HARNESS_PORT& port : harness.m_ports )
848 alias->AddMember( port.m_name );
849
850 screen->AddBusAlias( alias );
851
852 VECTOR2I pos;
853 BOX2I box( harness.m_location, harness.m_size );
854 SCH_LINE* busLine = nullptr;
855
856 for( SCH_ITEM* elem : screen->Items().Overlapping( SCH_LINE_T, box ) )
857 {
858 SCH_LINE* line = static_cast<SCH_LINE*>( elem );
859
860 if( !line->IsBus() )
861 continue;
862
863 busLine = line;
864
865 for( const VECTOR2I& pt : line->GetConnectionPoints() )
866 {
867 if( box.Contains( pt ) )
868 {
869 pos = pt;
870 break;
871 }
872 }
873 }
874
875 if( !busLine )
876 {
877 for( SCH_ITEM* elem : screen->Items().Overlapping( SCH_HIER_LABEL_T, box ) )
878 {
879 SCH_HIERLABEL* label = static_cast<SCH_HIERLABEL*>( elem );
880
881 pos = label->GetPosition();
882 VECTOR2I center = box.GetCenter();
883 int delta_x = center.x - pos.x;
884 int delta_y = center.y - pos.y;
885
886 if( std::abs( delta_x ) > std::abs( delta_y ) )
887 {
888 busLine = new SCH_LINE( pos, SCH_LAYER_ID::LAYER_BUS );
889 busLine->SetEndPoint( VECTOR2I( center.x, pos.y ) );
890 busLine->SetFlags( IS_NEW );
891 screen->Append( busLine );
892 }
893 else
894 {
895 busLine = new SCH_LINE( pos, SCH_LAYER_ID::LAYER_BUS );
896 busLine->SetEndPoint( VECTOR2I( pos.x, center.y ) );
897 busLine->SetFlags( IS_NEW );
898 screen->Append( busLine );
899 }
900
901 break;
902 }
903 }
904
905 if( !busLine )
906 continue;
907
908 std::set<SCH_LINE*> visited;
909 SCH_LABEL* label = walkBusLine( pos, visited );
910
911 // Altium supports two different naming conventions for harnesses. If there is a specific
912 // harness name, then the nets inside the harness will be named harnessname.netname.
913 // However, if there is no harness name, the nets will be named just netname.
914
915 // KiCad bus labels need some special handling to be recognized as bus labels
916 if( label && !label->GetText().StartsWith( wxT( "{" ) ) )
917 label->SetText( label->GetText() + wxT( "{" ) + harness.m_name + wxT( "}" ) );
918
919 if( !label )
920 {
921 label = new SCH_LABEL( busLine->GetStartPoint(), wxT( "{" ) + harness.m_name + wxT( "}" ) );
923
924 if( busLine->GetEndPoint().x < busLine->GetStartPoint().x )
926 else
928
929 screen->Append( label );
930 }
931
932 // Draw the bus line from the individual ports to the harness
933
934 bool isVertical = true;
935
936 if( harness.m_ports.size() > 1 )
937 {
938 VECTOR2I first = harness.m_ports.front().m_location;
939 VECTOR2I last = harness.m_ports.back().m_location;
940
941 if( first.y == last.y )
942 isVertical = false;
943 }
944
945 if( isVertical )
946 {
947 VECTOR2I bottom = harness.m_ports.front().m_entryLocation;
948 VECTOR2I top = harness.m_ports.front().m_entryLocation;
949 int delta_space = EDA_UNIT_UTILS::Mils2IU( schIUScale, 100 );
950
951 for( HARNESS::HARNESS_PORT& port : harness.m_ports )
952 {
953 if( port.m_entryLocation.y > bottom.y )
954 bottom = port.m_entryLocation;
955
956 if( port.m_entryLocation.y < top.y )
957 top = port.m_entryLocation;
958 }
959
960 VECTOR2I last_pt;
961 SCH_LINE* line = new SCH_LINE( bottom, SCH_LAYER_ID::LAYER_BUS );
962 line->SetStartPoint( bottom );
963 line->SetEndPoint( top );
964 line->SetLineWidth( busLine->GetLineWidth() );
965 line->SetLineColor( busLine->GetLineColor() );
966 screen->Append( line );
967
968 last_pt = ( busLine->GetStartPoint() - line->GetEndPoint() ).SquaredEuclideanNorm() <
969 ( busLine->GetStartPoint() - line->GetStartPoint() ).SquaredEuclideanNorm()
970 ? line->GetEndPoint()
971 : line->GetStartPoint();
972
973 // If the busline is not on the save y coordinate as the bus/wire connectors, add a short
974 // hop to bring the bus down to the level of the connectors
975 if( last_pt.y != busLine->GetStartPoint().y )
976 {
977 line = new SCH_LINE( last_pt, SCH_LAYER_ID::LAYER_BUS );
978 line->SetStartPoint( last_pt );
979
980 if( alg::signbit( busLine->GetStartPoint().x - last_pt.x ) )
981 line->SetEndPoint( last_pt + VECTOR2I( -delta_space, 0 ) );
982 else
983 line->SetEndPoint( last_pt + VECTOR2I( delta_space, 0 ) );
984
985 line->SetLineWidth( busLine->GetLineWidth() );
986 line->SetLineColor( busLine->GetLineColor() );
987 screen->Append( line );
988 last_pt = line->GetEndPoint();
989
990 line = new SCH_LINE( last_pt, SCH_LAYER_ID::LAYER_BUS );
991 line->SetStartPoint( last_pt );
992 line->SetEndPoint( last_pt + VECTOR2I( 0, busLine->GetStartPoint().y - last_pt.y ) );
993 line->SetLineWidth( busLine->GetLineWidth() );
994 line->SetLineColor( busLine->GetLineColor() );
995 screen->Append( line );
996 last_pt = line->GetEndPoint();
997 }
998
999 line = new SCH_LINE( last_pt, SCH_LAYER_ID::LAYER_BUS );
1000 line->SetStartPoint( last_pt );
1001 line->SetEndPoint( busLine->GetStartPoint() );
1002 line->SetLineWidth( busLine->GetLineWidth() );
1003 line->SetLineColor( busLine->GetLineColor() );
1004 screen->Append( line );
1005 }
1006 }
1007}
1008
1009
1010void SCH_IO_ALTIUM::ParseAltiumSch( const wxString& aFileName )
1011{
1012 // Load path may be different from the project path.
1013 wxFileName parentFileName( aFileName );
1014
1015 if( m_rootFilepath.IsEmpty() )
1016 m_rootFilepath = parentFileName.GetPath();
1017
1018 if( m_progressReporter )
1019 {
1020 wxFileName relative = parentFileName;
1021 relative.MakeRelativeTo( m_rootFilepath );
1022
1023 m_progressReporter->Report( wxString::Format( _( "Importing %s" ), relative.GetFullPath() ) );
1024
1025 if( !m_progressReporter->KeepRefreshing() )
1026 THROW_IO_ERROR( _( "File import canceled by user." ) );
1027 }
1028
1029 if( isBinaryFile( aFileName ) )
1030 {
1031 ALTIUM_COMPOUND_FILE altiumSchFile( aFileName );
1032
1033 try
1034 {
1035 ParseStorage( altiumSchFile ); // we need this before parsing the FileHeader
1036 ParseFileHeader( altiumSchFile );
1037
1038 // Parse "Additional" because sheet is set up during "FileHeader" parsing.
1039 ParseAdditional( altiumSchFile );
1040 }
1041 catch( const CFB::CFBException& exception )
1042 {
1043 THROW_IO_ERROR( exception.what() );
1044 }
1045 catch( const std::exception& exc )
1046 {
1047 THROW_IO_ERROR( wxString::Format( _( "Error parsing Altium schematic: %s" ), exc.what() ) );
1048 }
1049 }
1050 else // ASCII
1051 {
1052 ParseASCIISchematic( aFileName );
1053 }
1054
1055 SCH_SCREEN* currentScreen = getCurrentScreen();
1056 wxCHECK( currentScreen, /* void */ );
1057
1058 // Descend the sheet hierarchy.
1059 for( SCH_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
1060 {
1061 SCH_SCREEN* loadedScreen = nullptr;
1062 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
1063
1064 wxCHECK2( sheet, continue );
1065
1066 // The assumption is that all of the Altium schematic files will be in the same
1067 // path as the parent sheet path.
1068 wxFileName loadAltiumFileName( parentFileName.GetPath(), sheet->GetFileName() );
1069
1070 if( !loadAltiumFileName.IsFileReadable() )
1071 {
1072 // Altium sheet symbols sometimes store filenames without the .SchDoc extension.
1073 // Try appending it before falling through to the directory search.
1074 if( !loadAltiumFileName.HasExt() )
1075 {
1076 wxFileName withExt( loadAltiumFileName );
1077 withExt.SetExt( wxT( "SchDoc" ) );
1078
1079 if( withExt.IsFileReadable() )
1080 loadAltiumFileName = withExt;
1081 }
1082 }
1083
1084 if( !loadAltiumFileName.IsFileReadable() )
1085 {
1086 // Try case-insensitive search, matching by base name so that extensionless
1087 // filenames from Altium sheet symbols can resolve to .SchDoc files on disk.
1088 wxFileName sheetFn( sheet->GetFileName() );
1089 bool extensionless = !sheetFn.HasExt();
1090
1091 wxArrayString files;
1092 wxDir::GetAllFiles( parentFileName.GetPath(), &files, wxEmptyString,
1093 wxDIR_FILES | wxDIR_HIDDEN );
1094
1095 for( const wxString& candidate : files )
1096 {
1097 wxFileName candidateFname( candidate );
1098
1099 if( candidateFname.GetFullName().IsSameAs( sheet->GetFileName(), false )
1100 || ( extensionless
1101 && !sheetFn.GetName().empty()
1102 && candidateFname.GetName().IsSameAs( sheetFn.GetName(), false )
1103 && candidateFname.GetExt().IsSameAs( wxT( "SchDoc" ), false ) ) )
1104 {
1105 loadAltiumFileName = candidateFname;
1106 break;
1107 }
1108 }
1109 }
1110
1111 if( loadAltiumFileName.GetFullName().IsEmpty() || !loadAltiumFileName.IsFileReadable() )
1112 {
1113 m_errorMessages.emplace( wxString::Format( _( "The file name for sheet %s is undefined, "
1114 "this is probably an Altium signal harness "
1115 "that got converted to a sheet." ),
1116 sheet->GetName() ),
1118 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
1119 continue;
1120 }
1121
1122 m_rootSheet->SearchHierarchy( loadAltiumFileName.GetFullPath(), &loadedScreen );
1123
1124 if( loadedScreen )
1125 {
1126 sheet->SetScreen( loadedScreen );
1127
1128 wxFileName projectFileName = loadAltiumFileName;
1129 projectFileName.SetPath( m_schematic->Project().GetProjectPath() );
1130 projectFileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
1131 sheet->SetFileName( projectFileName.GetFullName() );
1132
1133 // Set up symbol instance data for this new sheet path. When the same schematic
1134 // is reused in multiple hierarchical instances, each instance needs its own
1135 // symbol references with the sheet name as suffix to match Altium's behavior.
1136 m_sheetPath.push_back( sheet );
1137
1138 for( SCH_ITEM* symItem : loadedScreen->Items().OfType( SCH_SYMBOL_T ) )
1139 {
1140 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( symItem );
1141
1142 // Get the base reference from existing instance data
1143 wxString baseRef;
1144
1145 if( !symbol->GetInstances().empty() )
1146 baseRef = symbol->GetInstances().front().m_Reference;
1147 else
1148 baseRef = symbol->GetField( FIELD_T::REFERENCE )->GetText();
1149
1150 // Skip power symbols and graphics
1151 if( baseRef.StartsWith( wxT( "#" ) ) )
1152 {
1154 continue;
1155 }
1156
1157 // Create new reference with sheet name suffix (e.g., P1 -> P1_Connector2)
1158 wxString newRef = baseRef + wxT( "_" ) + sheet->GetName();
1159 symbol->SetRef( &m_sheetPath, newRef );
1160 }
1161
1162 m_sheetPath.pop_back();
1163 // Do not need to load the sub-sheets - this has already been done.
1164 }
1165 else
1166 {
1167 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
1168 SCH_SCREEN* screen = sheet->GetScreen();
1169
1170 if( sheet->GetName().Trim().empty() )
1171 {
1172 wxString baseName = loadAltiumFileName.GetName();
1173 baseName.Replace( wxT( "/" ), wxT( "_" ) );
1174
1175 wxString sheetName = baseName;
1176 std::set<wxString> sheetNames;
1177
1178 for( EDA_ITEM* otherItem : currentScreen->Items().OfType( SCH_SHEET_T ) )
1179 {
1180 SCH_SHEET* otherSheet = static_cast<SCH_SHEET*>( otherItem );
1181 sheetNames.insert( otherSheet->GetName() );
1182 }
1183
1184 for( int ii = 1; ; ++ii )
1185 {
1186 if( sheetNames.find( sheetName ) == sheetNames.end() )
1187 break;
1188
1189 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
1190 }
1191
1192 sheet->SetName( sheetName );
1193 }
1194
1195 wxCHECK2( screen, continue );
1196
1197 m_sheetPath.push_back( sheet );
1198 m_sheets.clear();
1199 ParseAltiumSch( loadAltiumFileName.GetFullPath() );
1200
1201 // Map the loaded Altium file to the project file.
1202 wxFileName projectFileName = loadAltiumFileName;
1203 projectFileName.SetPath( m_schematic->Project().GetProjectPath() );
1204 projectFileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
1205 sheet->SetFileName( projectFileName.GetFullName() );
1206 screen->SetFileName( projectFileName.GetFullPath() );
1207
1208 m_sheetPath.pop_back();
1209 }
1210 }
1211}
1212
1213
1215{
1216 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "Storage" } );
1217
1218 if( file == nullptr )
1219 return;
1220
1221 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
1222
1223 std::map<wxString, wxString> properties = reader.ReadProperties();
1224 ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
1225 int weight = ALTIUM_PROPS_UTILS::ReadInt( properties, "WEIGHT", 0 );
1226
1227 if( weight < 0 )
1228 THROW_IO_ERROR( "Storage weight is negative!" );
1229
1230 for( int i = 0; i < weight; i++ )
1231 m_altiumStorage.emplace_back( reader );
1232
1233 if( reader.HasParsingError() )
1234 THROW_IO_ERROR( "stream was not parsed correctly!" );
1235
1236 // TODO pointhi: is it possible to have multiple headers in one Storage file? Otherwise
1237 // throw IO Error.
1238 if( reader.GetRemainingBytes() != 0 )
1239 {
1240 m_errorMessages.emplace( wxString::Format( _( "Storage file not fully parsed (%d bytes remaining)." ),
1241 reader.GetRemainingBytes() ),
1243 }
1244}
1245
1246
1248{
1249 wxString streamName = wxS( "Additional" );
1250
1251 const CFB::COMPOUND_FILE_ENTRY* file =
1252 aAltiumSchFile.FindStream( { streamName.ToStdString() } );
1253
1254 if( file == nullptr )
1255 return;
1256
1257 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
1258
1259 if( reader.GetRemainingBytes() <= 0 )
1260 {
1261 THROW_IO_ERROR( "Additional section does not contain any data" );
1262 }
1263 else
1264 {
1265 std::map<wxString, wxString> properties = reader.ReadProperties();
1266
1267 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
1268 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
1269
1270 if( record != ALTIUM_SCH_RECORD::HEADER )
1271 THROW_IO_ERROR( "Header expected" );
1272 }
1273
1274 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
1275 {
1276 std::map<wxString, wxString> properties = reader.ReadProperties();
1277
1278 ParseRecord( index, properties, streamName );
1279 }
1280
1281 // Handle harness Ports
1282 for( const ASCH_PORT& port : m_altiumHarnessPortsCurrentSheet )
1283 ParseHarnessPort( port );
1284
1285 CreateAliases();
1286
1287 if( reader.HasParsingError() )
1288 THROW_IO_ERROR( "stream was not parsed correctly!" );
1289
1290 if( reader.GetRemainingBytes() != 0 )
1291 THROW_IO_ERROR( "stream is not fully parsed" );
1292
1293 m_altiumHarnesses.clear();
1295}
1296
1297
1299{
1300 wxString streamName = wxS( "FileHeader" );
1301
1302 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { streamName.ToStdString() } );
1303
1304 if( file == nullptr )
1305 THROW_IO_ERROR( "FileHeader not found" );
1306
1307 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
1308
1309 if( reader.GetRemainingBytes() <= 0 )
1310 {
1311 THROW_IO_ERROR( "FileHeader does not contain any data" );
1312 }
1313 else
1314 {
1315 std::map<wxString, wxString> properties = reader.ReadProperties();
1316
1317 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
1318
1319 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Binary File Version 5.0" ) )
1320 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
1321 }
1322
1323 // Prepare some local variables
1324 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
1325 wxCHECK( !m_currentTitleBlock, /* void */ );
1326
1327 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
1328
1329 // index is required to resolve OWNERINDEX
1330 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
1331 {
1332 std::map<wxString, wxString> properties = reader.ReadProperties();
1333
1334 ParseRecord( index, properties, streamName );
1335 }
1336
1337 if( reader.HasParsingError() )
1338 THROW_IO_ERROR( "stream was not parsed correctly!" );
1339
1340 if( reader.GetRemainingBytes() != 0 )
1341 THROW_IO_ERROR( "stream is not fully parsed" );
1342
1343 // assign LIB_SYMBOL -> COMPONENT
1344 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
1345 {
1346 auto libSymbolIt = m_libSymbols.find( symbol.first );
1347
1348 if( libSymbolIt == m_libSymbols.end() )
1349 THROW_IO_ERROR( "every symbol should have a symbol attached" );
1350
1351 fixupSymbolPinNameNumbers( symbol.second );
1352 fixupSymbolPinNameNumbers( libSymbolIt->second );
1353
1354 symbol.second->SetLibSymbol( libSymbolIt->second );
1355 }
1356
1357 SCH_SCREEN* screen = getCurrentScreen();
1358 wxCHECK( screen, /* void */ );
1359
1360 // Handle title blocks
1362 m_currentTitleBlock.reset();
1363
1364 // Handle Ports
1365 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
1366 ParsePort( port );
1367
1369 m_altiumComponents.clear();
1370 m_altiumTemplates.clear();
1372
1373 m_symbols.clear();
1374 m_libSymbols.clear();
1375
1376 // Otherwise we cannot save the imported sheet?
1377 SCH_SHEET* sheet = getCurrentSheet();
1378
1379 wxCHECK( sheet, /* void */ );
1380
1381 sheet->SetModified();
1382}
1383
1384
1385void SCH_IO_ALTIUM::ParseASCIISchematic( const wxString& aFileName )
1386{
1387 // Read storage content first
1388 {
1389 ALTIUM_ASCII_PARSER storageReader( aFileName );
1390
1391 while( storageReader.CanRead() )
1392 {
1393 std::map<wxString, wxString> properties = storageReader.ReadProperties();
1394
1395 // Binary data
1396 if( properties.find( wxS( "BINARY" ) ) != properties.end() )
1397 m_altiumStorage.emplace_back( properties );
1398 }
1399 }
1400
1401 // Read other data
1402 ALTIUM_ASCII_PARSER reader( aFileName );
1403
1404 if( !reader.CanRead() )
1405 {
1406 THROW_IO_ERROR( "FileHeader does not contain any data" );
1407 }
1408 else
1409 {
1410 std::map<wxString, wxString> properties = reader.ReadProperties();
1411
1412 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
1413
1414 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Ascii File Version 5.0" ) )
1415 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
1416 }
1417
1418 // Prepare some local variables
1419 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
1420 wxCHECK( !m_currentTitleBlock, /* void */ );
1421
1422 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
1423
1424 // index is required to resolve OWNERINDEX
1425 int index = 0;
1426
1427 while( reader.CanRead() )
1428 {
1429 std::map<wxString, wxString> properties = reader.ReadProperties();
1430
1431 // Reset index at headers
1432 if( properties.find( wxS( "HEADER" ) ) != properties.end() )
1433 {
1434 index = 0;
1435 continue;
1436 }
1437
1438 if( properties.find( wxS( "RECORD" ) ) != properties.end() )
1439 ParseRecord( index, properties, aFileName );
1440
1441 index++;
1442 }
1443
1444 if( reader.HasParsingError() )
1445 THROW_IO_ERROR( "stream was not parsed correctly!" );
1446
1447 if( reader.CanRead() )
1448 THROW_IO_ERROR( "stream is not fully parsed" );
1449
1450 // assign LIB_SYMBOL -> COMPONENT
1451 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
1452 {
1453 auto libSymbolIt = m_libSymbols.find( symbol.first );
1454
1455 if( libSymbolIt == m_libSymbols.end() )
1456 THROW_IO_ERROR( "every symbol should have a symbol attached" );
1457
1458 fixupSymbolPinNameNumbers( symbol.second );
1459 fixupSymbolPinNameNumbers( libSymbolIt->second );
1460
1461 symbol.second->SetLibSymbol( libSymbolIt->second );
1462 }
1463
1464 SCH_SCREEN* screen = getCurrentScreen();
1465 wxCHECK( screen, /* void */ );
1466
1467 // Handle title blocks
1469 m_currentTitleBlock.reset();
1470
1471 // Handle harness Ports
1472 for( const ASCH_PORT& port : m_altiumHarnessPortsCurrentSheet )
1473 ParseHarnessPort( port );
1474
1475 // Handle Ports
1476 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
1477 ParsePort( port );
1478
1479 // Add the aliases used for harnesses
1480 CreateAliases();
1481
1482 m_altiumHarnesses.clear();
1484 m_altiumComponents.clear();
1485 m_altiumTemplates.clear();
1487
1488 m_symbols.clear();
1489 m_libSymbols.clear();
1490
1491 // Otherwise we cannot save the imported sheet?
1492 SCH_SHEET* sheet = getCurrentSheet();
1493
1494 wxCHECK( sheet, /* void */ );
1495
1496 sheet->SetModified();
1497}
1498
1499
1500void SCH_IO_ALTIUM::ParseRecord( int index, std::map<wxString, wxString>& properties,
1501 const wxString& aSectionName )
1502{
1503 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", -1 );
1504 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
1505
1506 // see: https://github.com/vadmium/python-altium/blob/master/format.md
1507 switch( record )
1508 {
1509 // FileHeader section
1510
1512 THROW_IO_ERROR( "Header already parsed" );
1513
1515 ParseComponent( index, properties );
1516 break;
1517
1519 ParsePin( properties );
1520 break;
1521
1523 m_errorMessages.emplace( _( "Record 'IEEE_SYMBOL' not handled." ), RPT_SEVERITY_INFO );
1524 break;
1525
1527 ParseLabel( properties );
1528 break;
1529
1531 ParseBezier( properties );
1532 break;
1533
1535 ParsePolyline( properties );
1536 break;
1537
1539 ParsePolygon( properties );
1540 break;
1541
1543 ParseEllipse( properties );
1544 break;
1545
1547 ParsePieChart( properties );
1548 break;
1549
1551 ParseRoundRectangle( properties );
1552 break;
1553
1556 ParseArc( properties );
1557 break;
1558
1560 ParseLine( properties );
1561 break;
1562
1564 ParseRectangle( properties );
1565 break;
1566
1568 ParseSheetSymbol( index, properties );
1569 break;
1570
1572 ParseSheetEntry( properties );
1573 break;
1574
1576 ParsePowerPort( properties );
1577 break;
1578
1580 // Ports are parsed after the sheet was parsed
1581 // This is required because we need all electrical connection points before placing.
1582 m_altiumPortsCurrentSheet.emplace_back( properties );
1583 break;
1584
1586 ParseNoERC( properties );
1587 break;
1588
1590 ParseNetLabel( properties );
1591 break;
1592
1594 ParseBus( properties );
1595 break;
1596
1598 ParseWire( properties );
1599 break;
1600
1602 ParseTextFrame( properties );
1603 break;
1604
1606 ParseJunction( properties );
1607 break;
1608
1610 ParseImage( properties );
1611 break;
1612
1614 ParseSheet( properties );
1615 break;
1616
1618 ParseSheetName( properties );
1619 break;
1620
1622 ParseFileName( properties );
1623 break;
1624
1626 ParseDesignator( properties );
1627 break;
1628
1630 ParseBusEntry( properties );
1631 break;
1632
1634 ParseTemplate( index, properties );
1635 break;
1636
1638 ParseParameter( properties );
1639 break;
1640
1642 m_errorMessages.emplace( _( "Parameter Set not currently supported." ), RPT_SEVERITY_ERROR );
1643 break;
1644
1646 ParseImplementationList( index, properties );
1647 break;
1648
1650 ParseImplementation( properties );
1651 break;
1652
1654 break;
1655
1657 break;
1658
1660 break;
1661
1663 ParseNote( properties );
1664 break;
1665
1667 m_errorMessages.emplace( _( "Compile mask not currently supported." ), RPT_SEVERITY_ERROR );
1668 break;
1669
1671 break;
1672
1673 // Additional section
1674
1676 ParseHarnessConnector( index, properties );
1677 break;
1678
1680 ParseHarnessEntry( properties );
1681 break;
1682
1684 ParseHarnessType( properties );
1685 break;
1686
1688 ParseSignalHarness( properties );
1689 break;
1690
1692 m_errorMessages.emplace( _( "Blanket not currently supported." ), RPT_SEVERITY_ERROR );
1693 break;
1694
1695 default:
1696 m_errorMessages.emplace(
1697 wxString::Format( _( "Unknown or unexpected record id %d found in %s." ), recordId,
1698 aSectionName ),
1700 break;
1701 }
1702
1704}
1705
1706
1707
1708const ASCH_STORAGE_FILE* SCH_IO_ALTIUM::GetFileFromStorage( const wxString& aFilename ) const
1709{
1710 const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
1711
1712 for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
1713 {
1714 if( file.filename.IsSameAs( aFilename ) )
1715 return &file;
1716
1717 if( file.filename.EndsWith( aFilename ) )
1718 nonExactMatch = &file;
1719 }
1720
1721 return nonExactMatch;
1722}
1723
1724
1725void SCH_IO_ALTIUM::ParseComponent( int aIndex, const std::map<wxString, wxString>& aProperties )
1726{
1727 SCH_SHEET* currentSheet = m_sheetPath.Last();
1728 wxCHECK( currentSheet, /* void */ );
1729
1730 wxString sheetName = currentSheet->GetName();
1731
1732 if( sheetName.IsEmpty() )
1733 sheetName = wxT( "root" );
1734
1735 ASCH_SYMBOL altiumSymbol( aProperties );
1736
1737 if( m_altiumComponents.count( aIndex ) )
1738 {
1739 const ASCH_SYMBOL& currentSymbol = m_altiumComponents.at( aIndex );
1740
1741 m_errorMessages.emplace( wxString::Format( _( "Symbol '%s' in sheet '%s' at index %d "
1742 "replaced with symbol \"%s\"." ),
1743 currentSymbol.libreference,
1744 sheetName,
1745 aIndex,
1746 altiumSymbol.libreference ),
1748 }
1749
1750 auto pair = m_altiumComponents.insert( { aIndex, altiumSymbol } );
1751 const ASCH_SYMBOL& elem = pair.first->second;
1752
1753 // TODO: this is a hack until we correctly apply all transformations to every element
1754 wxString name = wxString::Format( "%s_%d%s_%s_%s",
1755 sheetName,
1756 elem.orientation,
1757 elem.isMirrored ? "_mirrored" : "",
1758 elem.libreference,
1759 elem.sourcelibraryname );
1760
1762
1763 LIB_SYMBOL* ksymbol = new LIB_SYMBOL( wxEmptyString );
1764 ksymbol->SetName( name );
1765 ksymbol->SetDescription( elem.componentdescription );
1766 ksymbol->SetLibId( libId );
1767
1768 // Altium PARTCOUNT is one more than the actual unit count. The property may be missing
1769 // (defaults to 0) or otherwise nonsensical, so clamp to a minimum of 1 unit.
1770 ksymbol->SetUnitCount( std::max( 1, elem.partcount - 1 ), true );
1771
1772 if( elem.displaymodecount > 1 )
1773 {
1774 std::vector<wxString> bodyStyleNames;
1775
1776 for( int i = 0; i < elem.displaymodecount; i++ )
1777 bodyStyleNames.push_back( wxString::Format( "Display %d", i + 1 ) );
1778
1779 ksymbol->SetBodyStyleNames( bodyStyleNames );
1780 }
1781
1782 m_libSymbols.insert( { aIndex, ksymbol } );
1783
1784 // each component has its own symbol for now
1785 SCH_SYMBOL* symbol = new SCH_SYMBOL();
1786
1787 symbol->SetPosition( elem.location + m_sheetOffset );
1788
1789 for( SCH_FIELD& field : symbol->GetFields() )
1790 field.SetVisible( false );
1791
1792 int orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
1793
1794 switch( elem.orientation )
1795 {
1796 case 0: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_90; break;
1797 case 1: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_180; break;
1798 case 2: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_270; break;
1799 case 3: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0; break;
1800 default: break;
1801 }
1802
1803 if( elem.isMirrored )
1805
1806 symbol->SetOrientation( orientation );
1807
1808 // If Altium has defined a library from which we have the part,
1809 // use this as the designated source library.
1810 if( !elem.sourcelibraryname.IsEmpty() )
1811 {
1812 wxFileName fn( elem.sourcelibraryname );
1813 libId.SetLibNickname( fn.GetName() );
1814 }
1815
1816 symbol->SetLibId( libId );
1817
1818 if( ksymbol->GetUnitCount() > 1 )
1819 symbol->SetUnit( std::max( 1, elem.currentpartid ) );
1820 else
1821 symbol->SetUnit( 1 );
1822
1823 if( elem.displaymodecount > 1 )
1824 symbol->SetBodyStyle( elem.displaymode + 1 );
1825
1827
1828 SCH_SCREEN* screen = getCurrentScreen();
1829 wxCHECK( screen, /* void */ );
1830
1831 screen->Append( symbol );
1832
1833 m_symbols.insert( { aIndex, symbol } );
1834
1835 if( !elem.uniqueid.empty() )
1836 m_altiumSymbolToUid[symbol] = elem.uniqueid;
1837}
1838
1839
1840void SCH_IO_ALTIUM::ParseTemplate( int aIndex, const std::map<wxString, wxString>& aProperties )
1841{
1842 SCH_SHEET* currentSheet = m_sheetPath.Last();
1843 wxCHECK( currentSheet, /* void */ );
1844
1845 wxString sheetName = currentSheet->GetName();
1846
1847 if( sheetName.IsEmpty() )
1848 sheetName = wxT( "root" );
1849
1850 ASCH_TEMPLATE altiumTemplate( aProperties );
1851
1852 // Extract base name from path
1853 wxString baseName = altiumTemplate.filename.AfterLast( '\\' ).BeforeLast( '.' );
1854
1855 if( baseName.IsEmpty() )
1856 baseName = wxS( "Template" );
1857
1858 m_altiumTemplates.insert( { aIndex, altiumTemplate } );
1859 // No need to create a symbol - graphics is put on the sheet
1860}
1861
1862
1863void SCH_IO_ALTIUM::ParsePin( const std::map<wxString, wxString>& aProperties,
1864 std::vector<LIB_SYMBOL*>& aSymbol )
1865{
1866 ASCH_PIN elem( aProperties );
1867
1868 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
1869 SCH_SYMBOL* schSymbol = nullptr;
1870
1871 if( !symbol )
1872 {
1873 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1874
1875 if( libSymbolIt == m_libSymbols.end() )
1876 {
1877 // TODO: e.g. can depend on Template (RECORD=39
1878 m_errorMessages.emplace( wxString::Format( wxT( "Pin's owner (%d) not found." ),
1879 elem.ownerindex ),
1881 return;
1882 }
1883
1884 schSymbol = m_symbols.at( libSymbolIt->first );
1885 symbol = libSymbolIt->second;
1886 }
1887
1888 SCH_PIN* pin = new SCH_PIN( symbol );
1889
1890 // Make sure that these are visible when initializing the symbol
1891 // This may be overriden by the file data but not by the pin defaults
1892 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
1893 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
1894
1895 symbol->AddDrawItem( pin, false );
1896
1897 pin->SetUnit( std::max( 0, elem.ownerpartid ) );
1898
1899 if( symbol->GetBodyStyleCount() > 1 )
1900 {
1901 if( !aSymbol.empty() )
1902 {
1903 pin->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
1904 }
1905 else
1906 {
1907 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
1908
1909 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
1910 pin->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
1911 }
1912 }
1913
1914 pin->SetName( AltiumPinNamesToKiCad( elem.name ) );
1915 pin->SetNumber( AltiumPinDesignatorToKiCad( elem.designator ) );
1916 pin->SetLength( elem.pinlength );
1917
1918 if( elem.hidden )
1919 pin->SetVisible( false );
1920
1921 if( !elem.showDesignator )
1922 pin->SetNumberTextSize( 0 );
1923
1924 if( !elem.showPinName )
1925 pin->SetNameTextSize( 0 );
1926
1927 // Altium gives the pin body end location (elem.location) and the pre-computed
1928 // electrical connection point (elem.kicadLocation) which accounts for pin length
1929 // with combined integer+fractional arithmetic to avoid rounding errors.
1930 VECTOR2I bodyEnd = elem.location;
1931 VECTOR2I pinLocation = elem.kicadLocation;
1932
1933 switch( elem.orientation )
1934 {
1936 pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT );
1937 break;
1938
1940 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
1941 break;
1942
1944 pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT );
1945 break;
1946
1948 pin->SetOrientation( PIN_ORIENTATION::PIN_UP );
1949 break;
1950
1951 default:
1952 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
1953 break;
1954 }
1955
1956 if( schSymbol )
1957 {
1958 // Both points are in absolute schematic coordinates. Transform them to library-local
1959 // space, then derive the pin orientation from the resulting direction vector.
1960 pinLocation = GetRelativePosition( pinLocation + m_sheetOffset, schSymbol );
1961 bodyEnd = GetRelativePosition( bodyEnd + m_sheetOffset, schSymbol );
1962
1963 VECTOR2I dir = bodyEnd - pinLocation;
1964
1965 if( std::abs( dir.x ) >= std::abs( dir.y ) )
1966 pin->SetOrientation( dir.x > 0 ? PIN_ORIENTATION::PIN_RIGHT : PIN_ORIENTATION::PIN_LEFT );
1967 else
1968 pin->SetOrientation( dir.y > 0 ? PIN_ORIENTATION::PIN_DOWN : PIN_ORIENTATION::PIN_UP );
1969 }
1970
1971 pin->SetPosition( pinLocation );
1972
1973 switch( elem.electrical )
1974 {
1977 break;
1978
1980 pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
1981 break;
1982
1985 break;
1986
1989 break;
1990
1993 break;
1994
1997 break;
1998
2001 break;
2002
2005 break;
2006
2008 default:
2010 m_errorMessages.emplace( _( "Pin has unexpected electrical type." ), RPT_SEVERITY_WARNING );
2011 break;
2012 }
2013
2015 m_errorMessages.emplace( _( "Pin has unexpected outer edge type." ), RPT_SEVERITY_WARNING );
2016
2018 m_errorMessages.emplace( _( "Pin has unexpected inner edge type." ), RPT_SEVERITY_WARNING );
2019
2021 {
2022 switch( elem.symbolInnerEdge )
2023 {
2026 break;
2027
2028 default:
2029 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
2030 break;
2031 }
2032 }
2034 {
2035 switch( elem.symbolInnerEdge )
2036 {
2038 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
2039 break;
2040
2041 default:
2042 pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
2043 break;
2044 }
2045 }
2047 {
2048 pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
2049 }
2050 else
2051 {
2052 switch( elem.symbolInnerEdge )
2053 {
2055 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
2056 break;
2057
2058 default:
2059 pin->SetShape( GRAPHIC_PINSHAPE::LINE ); // nothing to do
2060 break;
2061 }
2062 }
2063}
2064
2065
2067 ASCH_RECORD_ORIENTATION orientation )
2068{
2069 int vjustify, hjustify;
2071
2072 switch( justification )
2073 {
2074 default:
2079 vjustify = GR_TEXT_V_ALIGN_BOTTOM;
2080 break;
2081
2085 vjustify = GR_TEXT_V_ALIGN_CENTER;
2086 break;
2087
2091 vjustify = GR_TEXT_V_ALIGN_TOP;
2092 break;
2093 }
2094
2095 switch( justification )
2096 {
2097 default:
2102 hjustify = GR_TEXT_H_ALIGN_LEFT;
2103 break;
2104
2108 hjustify = GR_TEXT_H_ALIGN_CENTER;
2109 break;
2110
2114 hjustify = GR_TEXT_H_ALIGN_RIGHT;
2115 break;
2116 }
2117
2118 switch( orientation )
2119 {
2121 angle = ANGLE_HORIZONTAL;
2122 break;
2123
2125 hjustify *= -1;
2126 angle = ANGLE_HORIZONTAL;
2127 break;
2128
2130 angle = ANGLE_VERTICAL;
2131 break;
2132
2134 hjustify *= -1;
2135 angle = ANGLE_VERTICAL;
2136 break;
2137 }
2138
2139 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( vjustify ) );
2140 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( hjustify ) );
2141 text->SetTextAngle( angle );
2142}
2143
2144
2145// Altium text orientation and justification are in absolute (page) coordinates. KiCad stores
2146// field text properties relative to the parent symbol and applies the symbol's transform at
2147// render time. This function adjusts the field's stored text angle and justification to
2148// compensate for the symbol's orientation so that the final rendered appearance matches the
2149// original Altium layout.
2150//
2151// The compensation follows the same logic as SCH_FIELD::Rotate() but applied in the
2152// inverse direction to undo the symbol's rotation effect on text properties.
2154{
2155 bool isHorizontal = aField->GetTextAngle().IsHorizontal();
2156
2157 // Altium orientation 0 (RIGHTWARDS) maps to KiCad SYM_ORIENT_90 (CCW). To compensate,
2158 // apply CW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CW rotation
2159 // of horizontal text flips justification; CW rotation of vertical text does not.
2160 if( aSymbol.orientation == 0 )
2161 {
2162 if( isHorizontal )
2163 {
2164 aField->SetHorizJustify(
2165 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2166 }
2167
2168 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2169 }
2170 // Altium orientation 1 (UPWARDS) maps to KiCad SYM_ORIENT_180 (two CCW rotations). The
2171 // transform [-1,0,0,-1] negates both X and Y, flipping horizontal justification. Apply
2172 // one correction for the full 180 degrees regardless of text angle.
2173 else if( aSymbol.orientation == 1 )
2174 {
2175 aField->SetHorizJustify(
2176 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2177 }
2178 // Altium orientation 2 (LEFTWARDS) maps to KiCad SYM_ORIENT_270 (CW). To compensate,
2179 // apply CCW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CCW rotation
2180 // of vertical text flips justification; CCW rotation of horizontal text does not.
2181 else if( aSymbol.orientation == 2 )
2182 {
2183 if( !isHorizontal )
2184 {
2185 aField->SetHorizJustify(
2186 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2187 }
2188
2189 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2190 }
2191 // Altium orientation 3 (DOWNWARDS) maps to KiCad SYM_ORIENT_0 (identity). No rotation
2192 // compensation needed.
2193
2194 // Mirror-Y in KiCad negates the X component of the bounding box, which effectively
2195 // flips horizontal justification. Compensate so the rendered text matches Altium.
2196 if( aSymbol.isMirrored )
2197 {
2198 aField->SetHorizJustify(
2199 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2200 }
2201}
2202
2203
2204// Altium text in symbols uses absolute orientation, but KiCad applies the symbol's transform
2205// to library body items via OrientAndMirrorSymbolItems at render time. This function pre-
2206// compensates the stored text angle and justification so that after the render-time transform,
2207// the final appearance matches the original Altium layout. Position is not adjusted here
2208// since GetRelativePosition already handles the positional component.
2210{
2211 int nRenderRotations = ( aSymbol.orientation + 1 ) % 4;
2212
2213 // Undo mirror first (reverse of render-time application order).
2214 // MirrorHorizontally on LAYER_DEVICE text flips H-justify when horizontal
2215 // and V-justify when vertical.
2216 if( aSymbol.isMirrored )
2217 {
2218 if( aText->GetTextAngle().IsHorizontal() )
2219 aText->FlipHJustify();
2220 else
2221 aText->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -aText->GetVertJustify() ) );
2222 }
2223
2224 // The render pipeline applies Rotate90(false) N times; undo with N inverse rotations.
2225 for( int i = 0; i < nRenderRotations; i++ )
2226 aText->Rotate90( true );
2227}
2228
2229
2231{
2232 // No component assigned -> Put on sheet
2233 if( aOwnerindex == ALTIUM_COMPONENT_NONE )
2234 return true;
2235
2236 // For a template -> Put on sheet so we can resolve variables
2237 if( m_altiumTemplates.find( aOwnerindex ) != m_altiumTemplates.end() )
2238 return true;
2239
2240 return false;
2241}
2242
2243
2244void SCH_IO_ALTIUM::ParseLabel( const std::map<wxString, wxString>& aProperties,
2245 std::vector<LIB_SYMBOL*>& aSymbol, std::vector<int>& aFontSizes )
2246{
2247 ASCH_LABEL elem( aProperties );
2248
2249 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2250 {
2251 static const std::map<wxString, wxString> variableMap = {
2252 { "APPLICATION_BUILDNUMBER", "KICAD_VERSION" },
2253 { "SHEETNUMBER", "#" },
2254 { "SHEETTOTAL", "##" },
2255 { "TITLE", "TITLE" }, // including 1:1 maps makes it easier
2256 { "REVISION", "REVISION" }, // to see that the list is complete
2257 { "DATE", "ISSUE_DATE" },
2258 { "CURRENTDATE", "CURRENT_DATE" },
2259 { "COMPANYNAME", "COMPANY" },
2260 { "DOCUMENTNAME", "FILENAME" },
2261 { "DOCUMENTFULLPATHANDNAME", "FILEPATH" },
2262 { "PROJECTNAME", "PROJECTNAME" },
2263 };
2264
2265 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
2266 SCH_TEXT* textItem = new SCH_TEXT( elem.location + m_sheetOffset, kicadText );
2267
2268 SetTextPositioning( textItem, elem.justification, elem.orientation );
2269
2270 size_t fontId = static_cast<int>( elem.fontId );
2271
2272 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2273 {
2274 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2275 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
2276
2277 // Must come after SetTextSize()
2278 textItem->SetBold( font.Bold );
2279 textItem->SetItalic( font.Italic );
2280 }
2281
2282 textItem->SetFlags( IS_NEW );
2283
2284 SCH_SCREEN* screen = getCurrentScreen();
2285 wxCHECK( screen, /* void */ );
2286
2287 screen->Append( textItem );
2288 }
2289 else
2290 {
2291 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2292 SCH_SYMBOL* schsym = nullptr;
2293
2294 if( !symbol )
2295 {
2296 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2297
2298 if( libSymbolIt == m_libSymbols.end() )
2299 {
2300 // TODO: e.g. can depend on Template (RECORD=39
2301 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), elem.ownerindex ),
2303 return;
2304 }
2305
2306 symbol = libSymbolIt->second;
2307 schsym = m_symbols.at( libSymbolIt->first );
2308 }
2309
2310 VECTOR2I pos = elem.location;
2311 SCH_TEXT* textItem = new SCH_TEXT( { 0, 0 }, elem.text, LAYER_DEVICE );
2312 symbol->AddDrawItem( textItem, false );
2313
2314 if( symbol->GetBodyStyleCount() > 1 )
2315 {
2316 if( !aSymbol.empty() )
2317 {
2318 textItem->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2319 }
2320 else
2321 {
2322 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2323
2324 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2325 textItem->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2326 }
2327 }
2328
2330 if( schsym )
2331 pos = GetRelativePosition( elem.location + m_sheetOffset, schsym );
2332
2333 textItem->SetPosition( pos );
2334 textItem->SetUnit( std::max( 0, elem.ownerpartid ) );
2335 SetTextPositioning( textItem, elem.justification, elem.orientation );
2336
2337 if( schsym )
2338 {
2339 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
2340
2341 if( altiumSymIt != m_altiumComponents.end() )
2342 AdjustTextForSymbolOrientation( textItem, altiumSymIt->second );
2343 }
2344
2345 size_t fontId = elem.fontId;
2346
2347 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2348 {
2349 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2350 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
2351
2352 // Must come after SetTextSize()
2353 textItem->SetBold( font.Bold );
2354 textItem->SetItalic( font.Italic );
2355 }
2356 else if( fontId > 0 && fontId <= aFontSizes.size() )
2357 {
2358 int size = aFontSizes[fontId - 1];
2359 textItem->SetTextSize( { size, size } );
2360 }
2361 }
2362}
2363
2364
2365void SCH_IO_ALTIUM::ParseTextFrame( const std::map<wxString, wxString>& aProperties,
2366 std::vector<LIB_SYMBOL*>& aSymbol,
2367 std::vector<int>& aFontSizes )
2368{
2369 ASCH_TEXT_FRAME elem( aProperties );
2370
2371 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2372 AddTextBox( &elem );
2373 else
2374 AddLibTextBox( &elem, aSymbol, aFontSizes );
2375}
2376
2377
2378void SCH_IO_ALTIUM::ParseNote( const std::map<wxString, wxString>& aProperties )
2379{
2380 ASCH_NOTE elem( aProperties );
2381 AddTextBox( static_cast<ASCH_TEXT_FRAME*>( &elem ) );
2382
2383 // TODO: need some sort of property system for storing author....
2384}
2385
2386
2388{
2389 SCH_TEXTBOX* textBox = new SCH_TEXTBOX();
2390
2391 VECTOR2I sheetTopRight = aElem->TopRight + m_sheetOffset;
2392 VECTOR2I sheetBottomLeft = aElem->BottomLeft +m_sheetOffset;
2393
2394 textBox->SetStart( sheetTopRight );
2395 textBox->SetEnd( sheetBottomLeft );
2396
2397 textBox->SetText( aElem->Text );
2398
2399 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
2400
2401 if( aElem->isSolid)
2403 else
2404 textBox->SetFilled( false );
2405
2406 if( aElem->ShowBorder )
2408 else
2410
2411 switch( aElem->Alignment )
2412 {
2413 default:
2416 break;
2419 break;
2422 break;
2423 }
2424
2425 size_t fontId = static_cast<int>( aElem->FontID );
2426
2427 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2428 {
2429 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2430 textBox->SetTextSize( { font.Size / 2, font.Size / 2 } );
2431
2432 // Must come after SetTextSize()
2433 textBox->SetBold( font.Bold );
2434 textBox->SetItalic( font.Italic );
2435 //textBox->SetFont( //how to set font, we have a font name here: ( font.fontname );
2436 }
2437
2438 textBox->SetFlags( IS_NEW );
2439
2440 SCH_SCREEN* screen = getCurrentScreen();
2441 wxCHECK( screen, /* void */ );
2442
2443 screen->Append( textBox );
2444}
2445
2446
2447void SCH_IO_ALTIUM::AddLibTextBox( const ASCH_TEXT_FRAME *aElem, std::vector<LIB_SYMBOL*>& aSymbol,
2448 std::vector<int>& aFontSizes )
2449{
2450 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2451 SCH_SYMBOL* schsym = nullptr;
2452
2453 if( !symbol )
2454 {
2455 const auto& libSymbolIt = m_libSymbols.find( aElem->ownerindex );
2456
2457 if( libSymbolIt == m_libSymbols.end() )
2458 {
2459 // TODO: e.g. can depend on Template (RECORD=39
2460 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), aElem->ownerindex ),
2462 return;
2463 }
2464
2465 symbol = libSymbolIt->second;
2466 schsym = m_symbols.at( libSymbolIt->first );
2467 }
2468
2469 SCH_TEXTBOX* textBox = new SCH_TEXTBOX( LAYER_DEVICE );
2470
2471 textBox->SetUnit( std::max( 0, aElem->ownerpartid ) );
2472 symbol->AddDrawItem( textBox, false );
2473
2474 if( symbol->GetBodyStyleCount() > 1 )
2475 {
2476 if( !aSymbol.empty() )
2477 {
2478 textBox->SetBodyStyle( aElem->ownerpartdisplaymode + 1 );
2479 }
2480 else
2481 {
2482 const auto& compIt = m_altiumComponents.find( aElem->ownerindex );
2483
2484 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2485 textBox->SetBodyStyle( aElem->ownerpartdisplaymode + 1 );
2486 }
2487 }
2488
2490 if( !schsym )
2491 {
2492 textBox->SetStart( aElem->TopRight );
2493 textBox->SetEnd( aElem->BottomLeft );
2494 }
2495 else
2496 {
2497 textBox->SetStart( GetRelativePosition( aElem->TopRight + m_sheetOffset, schsym ) );
2498 textBox->SetEnd( GetRelativePosition( aElem->BottomLeft + m_sheetOffset, schsym ) );
2499 }
2500
2501 textBox->SetText( aElem->Text );
2502
2503 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
2504
2505 if( aElem->isSolid)
2507 else
2508 textBox->SetFilled( false );
2509
2510 if( aElem->ShowBorder )
2512 else
2513 textBox->SetStroke( STROKE_PARAMS( -1 ) );
2514
2515 switch( aElem->Alignment )
2516 {
2517 default:
2520 break;
2523 break;
2526 break;
2527 }
2528
2529 if( aElem->FontID > 0 && aElem->FontID <= static_cast<int>( aFontSizes.size() ) )
2530 {
2531 int size = aFontSizes[aElem->FontID - 1];
2532 textBox->SetTextSize( { size, size } );
2533 }
2534}
2535
2536
2537void SCH_IO_ALTIUM::ParseBezier( const std::map<wxString, wxString>& aProperties,
2538 std::vector<LIB_SYMBOL*>& aSymbol )
2539{
2540 ASCH_BEZIER elem( aProperties );
2541
2542 if( elem.points.size() < 2 )
2543 {
2544 m_errorMessages.emplace( wxString::Format( _( "Bezier has %d control points. At least 2 are expected." ),
2545 static_cast<int>( elem.points.size() ) ),
2547 return;
2548 }
2549
2550 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2551 {
2552 SCH_SCREEN* currentScreen = getCurrentScreen();
2553 wxCHECK( currentScreen, /* void */ );
2554
2555 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
2556 {
2557 if( i + 2 == elem.points.size() )
2558 {
2559 // special case: single line
2560 SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
2562
2563 line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
2565
2566 line->SetFlags( IS_NEW );
2567
2568 currentScreen->Append( line );
2569 }
2570 else
2571 {
2572 // simulate Bezier using line segments
2573 std::vector<VECTOR2I> bezierPoints;
2574 std::vector<VECTOR2I> polyPoints;
2575
2576 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
2577 bezierPoints.push_back( elem.points.at( j ) );
2578
2579 BEZIER_POLY converter( bezierPoints );
2580 converter.GetPoly( polyPoints );
2581
2582 for( size_t k = 0; k + 1 < polyPoints.size(); k++ )
2583 {
2584 SCH_LINE* line = new SCH_LINE( polyPoints.at( k ) + m_sheetOffset,
2586
2587 line->SetEndPoint( polyPoints.at( k + 1 ) + m_sheetOffset );
2589
2590 line->SetFlags( IS_NEW );
2591 currentScreen->Append( line );
2592 }
2593 }
2594 }
2595 }
2596 else
2597 {
2598 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2599 SCH_SYMBOL* schsym = nullptr;
2600
2601 if( !symbol )
2602 {
2603 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2604
2605 if( libSymbolIt == m_libSymbols.end() )
2606 {
2607 // TODO: e.g. can depend on Template (RECORD=39
2608 m_errorMessages.emplace( wxString::Format( wxT( "Bezier's owner (%d) not found." ),
2609 elem.ownerindex ),
2611 return;
2612 }
2613
2614 symbol = libSymbolIt->second;
2615 schsym = m_symbols.at( libSymbolIt->first );
2616 }
2617
2618 int bodyStyle = 0;
2619
2620 if( symbol->GetBodyStyleCount() > 1 )
2621 {
2622 if( !aSymbol.empty() )
2623 {
2624 bodyStyle = elem.ownerpartdisplaymode + 1;
2625 }
2626 else
2627 {
2628 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2629
2630 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2631 bodyStyle = elem.ownerpartdisplaymode + 1;
2632 }
2633 }
2634
2635 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
2636 {
2637 if( i + 2 == elem.points.size() )
2638 {
2639 // special case: single line
2641 symbol->AddDrawItem( line, false );
2642
2643 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2644
2645 if( bodyStyle > 0 )
2646 line->SetBodyStyle( bodyStyle );
2647
2648 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
2649 {
2650 VECTOR2I pos = elem.points.at( j );
2651
2652 if( schsym )
2653 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2654
2655 line->AddPoint( pos );
2656 }
2657
2659 }
2660 else if( i + 3 == elem.points.size() )
2661 {
2662 // TODO: special case of a single line with an extra point?
2663 // I haven't a clue what this is all about, but the sample document we have in
2664 // https://gitlab.com/kicad/code/kicad/-/issues/8974 responds best by treating it
2665 // as another single line special case.
2667 symbol->AddDrawItem( line, false );
2668
2669 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2670
2671 if( bodyStyle > 0 )
2672 line->SetBodyStyle( bodyStyle );
2673
2674 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
2675 {
2676 VECTOR2I pos = elem.points.at( j );
2677
2678 if( schsym )
2679 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2680
2681 line->AddPoint( pos );
2682 }
2683
2685 }
2686 else
2687 {
2688 // Bezier always has exactly 4 control points
2690 symbol->AddDrawItem( bezier, false );
2691
2692 bezier->SetUnit( std::max( 0, elem.ownerpartid ) );
2693
2694 if( bodyStyle > 0 )
2695 bezier->SetBodyStyle( bodyStyle );
2696
2697 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
2698 {
2699 VECTOR2I pos = elem.points.at( j );
2700
2701 if( schsym )
2702 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2703
2704 switch( j - i )
2705 {
2706 case 0: bezier->SetStart( pos ); break;
2707 case 1: bezier->SetBezierC1( pos ); break;
2708 case 2: bezier->SetBezierC2( pos ); break;
2709 case 3: bezier->SetEnd( pos ); break;
2710 default: break; // Can't get here but silence warnings
2711 }
2712 }
2713
2716 }
2717 }
2718 }
2719}
2720
2721
2722void SCH_IO_ALTIUM::ParsePolyline( const std::map<wxString, wxString>& aProperties,
2723 std::vector<LIB_SYMBOL*>& aSymbol )
2724{
2725 ASCH_POLYLINE elem( aProperties );
2726
2727 if( elem.Points.size() < 2 )
2728 return;
2729
2730 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2731 {
2732 SCH_SCREEN* screen = getCurrentScreen();
2733 wxCHECK( screen, /* void */ );
2734
2735 for( size_t i = 1; i < elem.Points.size(); i++ )
2736 {
2737 SCH_LINE* line = new SCH_LINE;
2738
2739 line->SetStartPoint( elem.Points[i - 1] + m_sheetOffset );
2740 line->SetEndPoint( elem.Points[i] + m_sheetOffset );
2741
2743 GetColorFromInt( elem.Color ) ) );
2744
2745 line->SetFlags( IS_NEW );
2746
2747 screen->Append( line );
2748 }
2749 }
2750 else
2751 {
2752 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2753 SCH_SYMBOL* schsym = nullptr;
2754
2755 if( !symbol )
2756 {
2757 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2758
2759 if( libSymbolIt == m_libSymbols.end() )
2760 {
2761 // TODO: e.g. can depend on Template (RECORD=39
2762 m_errorMessages.emplace( wxString::Format( wxT( "Polyline's owner (%d) not found." ),
2763 elem.ownerindex ),
2765 return;
2766 }
2767
2768 symbol = libSymbolIt->second;
2769 schsym = m_symbols.at( libSymbolIt->first );
2770 }
2771
2773 symbol->AddDrawItem( line, false );
2774
2775 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2776
2777 if( symbol->GetBodyStyleCount() > 1 )
2778 {
2779 if( !aSymbol.empty() )
2780 {
2781 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2782 }
2783 else
2784 {
2785 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2786
2787 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2788 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2789 }
2790 }
2791
2792 for( VECTOR2I point : elem.Points )
2793 {
2794 if( schsym )
2795 point = GetRelativePosition( point + m_sheetOffset, schsym );
2796
2797 line->AddPoint( point );
2798 }
2799
2801 STROKE_PARAMS stroke = line->GetStroke();
2802 stroke.SetLineStyle( GetPlotDashType( elem.LineStyle ) );
2803
2804 line->SetStroke( stroke );
2805 }
2806}
2807
2808
2809void SCH_IO_ALTIUM::ParsePolygon( const std::map<wxString, wxString>& aProperties,
2810 std::vector<LIB_SYMBOL*>& aSymbol )
2811{
2812 ASCH_POLYGON elem( aProperties );
2813
2814 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2815 {
2816 SCH_SCREEN* screen = getCurrentScreen();
2817 wxCHECK( screen, /* void */ );
2818
2819 SCH_SHAPE* poly = new SCH_SHAPE( SHAPE_T::POLY );
2820
2821 for( VECTOR2I& point : elem.points )
2822 poly->AddPoint( point + m_sheetOffset );
2823 poly->AddPoint( elem.points.front() + m_sheetOffset );
2824
2825 SetSchShapeLine( elem, poly );
2826 SetSchShapeFillAndColor( elem, poly );
2827 poly->SetFlags( IS_NEW );
2828
2829 screen->Append( poly );
2830 }
2831 else
2832 {
2833 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2834 SCH_SYMBOL* schsym = nullptr;
2835
2836 if( !symbol )
2837 {
2838 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2839
2840 if( libSymbolIt == m_libSymbols.end() )
2841 {
2842 // TODO: e.g. can depend on Template (RECORD=39
2843 m_errorMessages.emplace( wxString::Format( wxT( "Polygon's owner (%d) not found." ),
2844 elem.ownerindex ),
2846 return;
2847 }
2848
2849 symbol = libSymbolIt->second;
2850 schsym = m_symbols.at( libSymbolIt->first );
2851 }
2852
2854
2855 symbol->AddDrawItem( line, false );
2856 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2857
2858 if( symbol->GetBodyStyleCount() > 1 )
2859 {
2860 if( !aSymbol.empty() )
2861 {
2862 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2863 }
2864 else
2865 {
2866 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2867
2868 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2869 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2870 }
2871 }
2872
2873 for( VECTOR2I point : elem.points )
2874 {
2875 if( schsym )
2876 point = GetRelativePosition( point + m_sheetOffset, schsym );
2877
2878 line->AddPoint( point );
2879 }
2880
2881 VECTOR2I point = elem.points.front();
2882
2883 if( schsym )
2884 point = GetRelativePosition( elem.points.front() + m_sheetOffset, schsym );
2885
2886 line->AddPoint( point );
2887
2890
2891 if( line->GetFillColor() == line->GetStroke().GetColor()
2892 && line->GetFillMode() != FILL_T::NO_FILL )
2893 {
2894 STROKE_PARAMS stroke = line->GetStroke();
2895 stroke.SetWidth( -1 );
2896 line->SetStroke( stroke );
2897 }
2898 }
2899}
2900
2901
2902void SCH_IO_ALTIUM::ParseRoundRectangle( const std::map<wxString, wxString>& aProperties,
2903 std::vector<LIB_SYMBOL*>& aSymbol )
2904{
2905 ASCH_ROUND_RECTANGLE elem( aProperties );
2906
2907 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2908 {
2909 SCH_SCREEN* screen = getCurrentScreen();
2910 wxCHECK( screen, /* void */ );
2911
2912 // TODO: misses rounded edges
2913 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
2914
2915 rect->SetPosition( elem.TopRight + m_sheetOffset );
2916 rect->SetEnd( elem.BottomLeft + m_sheetOffset );
2917 SetSchShapeLine( elem, rect );
2918 SetSchShapeFillAndColor( elem, rect );
2919 rect->SetFlags( IS_NEW );
2920
2921 screen->Append( rect );
2922 }
2923 else
2924 {
2925 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2926 SCH_SYMBOL* schsym = nullptr;
2927
2928 if( !symbol )
2929 {
2930 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2931
2932 if( libSymbolIt == m_libSymbols.end() )
2933 {
2934 // TODO: e.g. can depend on Template (RECORD=39
2935 m_errorMessages.emplace( wxString::Format( wxT( "Rounded rectangle's owner (%d) not found." ),
2936 elem.ownerindex ),
2938 return;
2939 }
2940
2941 symbol = libSymbolIt->second;
2942 schsym = m_symbols.at( libSymbolIt->first );
2943 }
2944
2945 SCH_SHAPE* rect = nullptr;
2946
2947 int width = std::abs( elem.TopRight.x - elem.BottomLeft.x );
2948 int height = std::abs( elem.TopRight.y - elem.BottomLeft.y );
2949
2950 // If it is a circle, make it a circle
2951 if( std::abs( elem.CornerRadius.x ) >= width / 2
2952 && std::abs( elem.CornerRadius.y ) >= height / 2 )
2953 {
2954 rect = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
2955
2956 VECTOR2I center = ( elem.TopRight + elem.BottomLeft ) / 2;
2957 int radius = std::min( width / 2, height / 2 );
2958
2959 if( schsym )
2961
2962 rect->SetPosition( center );
2963 rect->SetEnd( VECTOR2I( rect->GetPosition().x + radius, rect->GetPosition().y ) );
2964 }
2965 else
2966 {
2968
2969 if( !schsym )
2970 {
2971 rect->SetPosition( elem.TopRight );
2972 rect->SetEnd( elem.BottomLeft );
2973 }
2974 else
2975 {
2976 rect->SetPosition( GetRelativePosition( elem.TopRight + m_sheetOffset, schsym ) );
2977 rect->SetEnd( GetRelativePosition( elem.BottomLeft + m_sheetOffset, schsym ) );
2978 }
2979
2980 rect->Normalize();
2981 }
2982
2985
2986 symbol->AddDrawItem( rect, false );
2987 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
2988
2989 if( symbol->GetBodyStyleCount() > 1 )
2990 {
2991 if( !aSymbol.empty() )
2992 {
2993 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2994 }
2995 else
2996 {
2997 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2998
2999 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3000 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3001 }
3002 }
3003 }
3004}
3005
3006
3007void SCH_IO_ALTIUM::ParseArc( const std::map<wxString, wxString>& aProperties,
3008 std::vector<LIB_SYMBOL*>& aSymbol )
3009{
3010 ASCH_ARC elem( aProperties );
3011
3012 int arc_radius = elem.m_Radius;
3013 VECTOR2I center = elem.m_Center;
3014 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
3015 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
3016 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
3017 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
3018
3019 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3020 {
3021 SCH_SCREEN* currentScreen = getCurrentScreen();
3022 wxCHECK( currentScreen, /* void */ );
3023
3024 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3025 {
3027
3028 circle->SetPosition( elem.m_Center + m_sheetOffset );
3029 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
3030
3031 SetSchShapeLine( elem, circle );
3033
3034 currentScreen->Append( circle );
3035 }
3036 else
3037 {
3038 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
3039
3040 arc->SetCenter( elem.m_Center + m_sheetOffset );
3041 arc->SetStart( elem.m_Center + startOffset + m_sheetOffset );
3042 arc->SetEnd( elem.m_Center + endOffset + m_sheetOffset );
3043
3044 SetSchShapeLine( elem, arc );
3045 SetSchShapeFillAndColor( elem, arc );
3046
3047 currentScreen->Append( arc );
3048 }
3049 }
3050 else
3051 {
3052 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3053 SCH_SYMBOL* schsym = nullptr;
3054
3055 if( !symbol )
3056 {
3057 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3058
3059 if( libSymbolIt == m_libSymbols.end() )
3060 {
3061 // TODO: e.g. can depend on Template (RECORD=39
3062 m_errorMessages.emplace( wxString::Format( wxT( "Arc's owner (%d) not found." ), elem.ownerindex ),
3064 return;
3065 }
3066
3067 symbol = libSymbolIt->second;
3068 schsym = m_symbols.at( libSymbolIt->first );
3069 }
3070
3071 int bodyStyle = 0;
3072
3073 if( symbol->GetBodyStyleCount() > 1 )
3074 {
3075 if( !aSymbol.empty() )
3076 {
3077 bodyStyle = elem.ownerpartdisplaymode + 1;
3078 }
3079 else
3080 {
3081 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3082
3083 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3084 bodyStyle = elem.ownerpartdisplaymode + 1;
3085 }
3086 }
3087
3088 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3089 {
3091 symbol->AddDrawItem( circle, false );
3092
3093 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
3094
3095 if( bodyStyle > 0 )
3096 circle->SetBodyStyle( bodyStyle );
3097
3098 if( schsym )
3100
3101 circle->SetPosition( center );
3102
3103 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
3106 }
3107 else
3108 {
3110 symbol->AddDrawItem( arc, false );
3111 arc->SetUnit( std::max( 0, elem.ownerpartid ) );
3112
3113 if( bodyStyle > 0 )
3114 arc->SetBodyStyle( bodyStyle );
3115
3116 if( schsym )
3117 {
3119 startOffset = GetRelativePosition( elem.m_Center + startOffset + m_sheetOffset, schsym )
3120 - center;
3121 endOffset = GetRelativePosition( elem.m_Center + endOffset + m_sheetOffset, schsym )
3122 - center;
3123 }
3124
3125 arc->SetCenter( center );
3126 arc->SetStart( center + startOffset );
3127 arc->SetEnd( center + endOffset );
3128
3131 }
3132 }
3133}
3134
3135
3136void SCH_IO_ALTIUM::ParseEllipticalArc( const std::map<wxString, wxString>& aProperties,
3137 std::vector<LIB_SYMBOL*>& aSymbol )
3138{
3139 ASCH_ARC elem( aProperties );
3140
3141 if( elem.m_Radius == elem.m_SecondaryRadius && elem.m_StartAngle == 0
3142 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3143 {
3144 ParseCircle( aProperties, aSymbol );
3145 return;
3146 }
3147
3148 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3149 {
3150 SCH_SCREEN* currentScreen = getCurrentScreen();
3151 wxCHECK( currentScreen, /* void */ );
3152
3153 ELLIPSE<int> ellipse( elem.m_Center + m_sheetOffset, elem.m_Radius,
3156 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
3157 std::vector<BEZIER<int>> beziers;
3158
3159 TransformEllipseToBeziers( ellipse, beziers );
3160
3161 for( const BEZIER<int>& bezier : beziers )
3162 {
3163 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
3164 schbezier->SetStart( bezier.Start );
3165 schbezier->SetBezierC1( bezier.C1 );
3166 schbezier->SetBezierC2( bezier.C2 );
3167 schbezier->SetEnd( bezier.End );
3168 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
3170
3171 currentScreen->Append( schbezier );
3172 }
3173 }
3174 else
3175 {
3176 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3177 SCH_SYMBOL* schsym = nullptr;
3178
3179 if( !symbol )
3180 {
3181 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3182
3183 if( libSymbolIt == m_libSymbols.end() )
3184 {
3185 // TODO: e.g. can depend on Template (RECORD=39
3186 m_errorMessages.emplace( wxString::Format( wxT( "Elliptical Arc's owner (%d) not found." ),
3187 elem.ownerindex ),
3189 return;
3190 }
3191
3192 symbol = libSymbolIt->second;
3193 schsym = m_symbols.at( libSymbolIt->first );
3194 }
3195
3196 int bodyStyle = 0;
3197
3198 if( symbol->GetBodyStyleCount() > 1 )
3199 {
3200 if( !aSymbol.empty() )
3201 {
3202 bodyStyle = elem.ownerpartdisplaymode + 1;
3203 }
3204 else
3205 {
3206 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3207
3208 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3209 bodyStyle = elem.ownerpartdisplaymode + 1;
3210 }
3211 }
3212
3213 ELLIPSE<int> ellipse( elem.m_Center, elem.m_Radius,
3216 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
3217 std::vector<BEZIER<int>> beziers;
3218
3219 TransformEllipseToBeziers( ellipse, beziers );
3220
3221 for( const BEZIER<int>& bezier : beziers )
3222 {
3223 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
3224 symbol->AddDrawItem( schbezier, false );
3225
3226 schbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
3227
3228 if( bodyStyle > 0 )
3229 schbezier->SetBodyStyle( bodyStyle );
3230
3231 if( schsym )
3232 {
3233 schbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
3234 schbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
3235 schbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
3236 schbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
3237 }
3238 else
3239 {
3240 schbezier->SetStart( bezier.Start );
3241 schbezier->SetBezierC1( bezier.C1 );
3242 schbezier->SetBezierC2( bezier.C2 );
3243 schbezier->SetEnd( bezier.End );
3244 }
3245
3248 }
3249 }
3250}
3251
3252
3253void SCH_IO_ALTIUM::ParsePieChart( const std::map<wxString, wxString>& aProperties,
3254 std::vector<LIB_SYMBOL*>& aSymbol )
3255{
3256 ParseArc( aProperties, aSymbol );
3257
3258 ASCH_PIECHART elem( aProperties );
3259
3260 int arc_radius = elem.m_Radius;
3261 VECTOR2I center = elem.m_Center;
3262 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
3263 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
3264 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
3265 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
3266
3267 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3268 {
3269 SCH_SCREEN* screen = getCurrentScreen();
3270 wxCHECK( screen, /* void */ );
3271
3272 // close polygon
3274 line->SetEndPoint( center + startOffset + m_sheetOffset );
3276
3277 line->SetFlags( IS_NEW );
3278 screen->Append( line );
3279
3281 line->SetEndPoint( center + endOffset + m_sheetOffset );
3283
3284 line->SetFlags( IS_NEW );
3285 screen->Append( line );
3286 }
3287 else
3288 {
3289 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3290 SCH_SYMBOL* schsym = nullptr;
3291
3292 if( !symbol )
3293 {
3294 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3295
3296 if( libSymbolIt == m_libSymbols.end() )
3297 {
3298 // TODO: e.g. can depend on Template (RECORD=39
3299 m_errorMessages.emplace( wxString::Format( wxT( "Piechart's owner (%d) not found." ),
3300 elem.ownerindex ),
3302 return;
3303 }
3304
3305 symbol = libSymbolIt->second;
3306 schsym = m_symbols.at( libSymbolIt->first );
3307 }
3308
3310 symbol->AddDrawItem( line, false );
3311
3312 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3313
3314 if( symbol->GetBodyStyleCount() > 1 )
3315 {
3316 if( !aSymbol.empty() )
3317 {
3318 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3319 }
3320 else
3321 {
3322 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3323
3324 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3325 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3326 }
3327 }
3328
3329 if( !schsym )
3330 {
3331 line->AddPoint( center + startOffset );
3332 line->AddPoint( center );
3333 line->AddPoint( center + endOffset );
3334 }
3335 else
3336 {
3337 line->AddPoint( GetRelativePosition( center + startOffset + m_sheetOffset, schsym ) );
3338 line->AddPoint( GetRelativePosition( center + m_sheetOffset, schsym ) );
3339 line->AddPoint( GetRelativePosition( center + endOffset + m_sheetOffset, schsym ) );
3340 }
3341
3343 }
3344}
3345
3346
3347void SCH_IO_ALTIUM::ParseEllipse( const std::map<wxString, wxString>& aProperties,
3348 std::vector<LIB_SYMBOL*>& aSymbol )
3349{
3350 ASCH_ELLIPSE elem( aProperties );
3351
3352 if( elem.Radius == elem.SecondaryRadius )
3353 {
3354 ParseCircle( aProperties, aSymbol );
3355 return;
3356 }
3357
3358 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3359 {
3360 SCH_SCREEN* screen = getCurrentScreen();
3361 wxCHECK( screen, /* void */ );
3362
3363 COLOR4D fillColor = GetColorFromInt( elem.AreaColor );
3364
3365 if( elem.IsTransparent )
3366 fillColor = fillColor.WithAlpha( 0.5 );
3367
3369
3370 ELLIPSE<int> ellipse( elem.Center + m_sheetOffset, elem.Radius,
3371 KiROUND( elem.SecondaryRadius ), ANGLE_0 );
3372
3373 std::vector<BEZIER<int>> beziers;
3374 std::vector<VECTOR2I> polyPoints;
3375
3376 TransformEllipseToBeziers( ellipse, beziers );
3377
3378 for( const BEZIER<int>& bezier : beziers )
3379 {
3380 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
3381 schbezier->SetStart( bezier.Start );
3382 schbezier->SetBezierC1( bezier.C1 );
3383 schbezier->SetBezierC2( bezier.C2 );
3384 schbezier->SetEnd( bezier.End );
3385 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
3386 schbezier->SetFillColor( fillColor );
3387 schbezier->SetFillMode( fillMode );
3388
3390 screen->Append( schbezier );
3391
3392 polyPoints.push_back( bezier.Start );
3393 }
3394
3395 if( fillMode != FILL_T::NO_FILL )
3396 {
3397 SCH_SHAPE* schpoly = new SCH_SHAPE( SHAPE_T::POLY );
3398 schpoly->SetFillColor( fillColor );
3399 schpoly->SetFillMode( fillMode );
3400 schpoly->SetWidth( -1 );
3401
3402 for( const VECTOR2I& point : polyPoints )
3403 schpoly->AddPoint( point );
3404
3405 schpoly->AddPoint( polyPoints[0] );
3406
3407 screen->Append( schpoly );
3408 }
3409 }
3410 else
3411 {
3412 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3413 SCH_SYMBOL* schsym = nullptr;
3414
3415 if( !symbol )
3416 {
3417 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3418
3419 if( libSymbolIt == m_libSymbols.end() )
3420 {
3421 // TODO: e.g. can depend on Template (RECORD=39
3422 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
3424 return;
3425 }
3426
3427 symbol = libSymbolIt->second;
3428 schsym = m_symbols.at( libSymbolIt->first );
3429 }
3430
3431 int bodyStyle = 0;
3432
3433 if( symbol->GetBodyStyleCount() > 1 )
3434 {
3435 if( !aSymbol.empty() )
3436 {
3437 bodyStyle = elem.ownerpartdisplaymode + 1;
3438 }
3439 else
3440 {
3441 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3442
3443 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3444 bodyStyle = elem.ownerpartdisplaymode + 1;
3445 }
3446 }
3447
3448 ELLIPSE<int> ellipse( elem.Center, elem.Radius, KiROUND( elem.SecondaryRadius ),
3449 ANGLE_0 );
3450
3451 std::vector<BEZIER<int>> beziers;
3452 std::vector<VECTOR2I> polyPoints;
3453
3454 TransformEllipseToBeziers( ellipse, beziers );
3455
3456 for( const BEZIER<int>& bezier : beziers )
3457 {
3458 SCH_SHAPE* libbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
3459 symbol->AddDrawItem( libbezier, false );
3460 libbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
3461
3462 if( bodyStyle > 0 )
3463 libbezier->SetBodyStyle( bodyStyle );
3464
3465 if( !schsym )
3466 {
3467 libbezier->SetStart( bezier.Start );
3468 libbezier->SetBezierC1( bezier.C1 );
3469 libbezier->SetBezierC2( bezier.C2 );
3470 libbezier->SetEnd( bezier.End );
3471 }
3472 else
3473 {
3474 libbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
3475 libbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
3476 libbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
3477 libbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
3478 }
3479
3480 SetLibShapeLine( elem, libbezier, ALTIUM_SCH_RECORD::ELLIPSE );
3483
3484 polyPoints.push_back( libbezier->GetStart() );
3485 }
3486
3487 // A series of beziers won't fill the center, so if this is meant to be fully filled,
3488 // Add a polygon to fill the center
3489 if( elem.IsSolid )
3490 {
3491 SCH_SHAPE* libline = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
3492 symbol->AddDrawItem( libline, false );
3493 libline->SetUnit( std::max( 0, elem.ownerpartid ) );
3494
3495 if( bodyStyle > 0 )
3496 libline->SetBodyStyle( bodyStyle );
3497
3498 for( const VECTOR2I& point : polyPoints )
3499 libline->AddPoint( point );
3500
3501 libline->AddPoint( polyPoints[0] );
3502
3503 libline->SetWidth( -1 );
3505 }
3506 }
3507}
3508
3509
3510void SCH_IO_ALTIUM::ParseCircle( const std::map<wxString, wxString>& aProperties,
3511 std::vector<LIB_SYMBOL*>& aSymbol )
3512{
3513 ASCH_ELLIPSE elem( aProperties );
3514
3515 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3516 {
3517 SCH_SCREEN* screen = getCurrentScreen();
3518 wxCHECK( screen, /* void */ );
3519
3521
3522 circle->SetPosition( elem.Center + m_sheetOffset );
3523 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
3524 circle->SetStroke( STROKE_PARAMS( 1, LINE_STYLE::SOLID ) );
3525
3526 circle->SetFillColor( GetColorFromInt( elem.AreaColor ) );
3527
3528 if( elem.IsSolid )
3529 circle->SetFillMode( FILL_T::FILLED_WITH_COLOR );
3530 else
3531 circle->SetFilled( false );
3532
3533 screen->Append( circle );
3534 }
3535 else
3536 {
3537 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3538 SCH_SYMBOL* schsym = nullptr;
3539
3540 if( !symbol )
3541 {
3542 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3543
3544 if( libSymbolIt == m_libSymbols.end() )
3545 {
3546 // TODO: e.g. can depend on Template (RECORD=39
3547 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
3549 return;
3550 }
3551
3552 symbol = libSymbolIt->second;
3553 schsym = m_symbols.at( libSymbolIt->first );
3554 }
3555
3556 VECTOR2I center = elem.Center;
3558 symbol->AddDrawItem( circle, false );
3559
3560 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
3561
3562 if( symbol->GetBodyStyleCount() > 1 )
3563 {
3564 if( !aSymbol.empty() )
3565 {
3566 circle->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3567 }
3568 else
3569 {
3570 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3571
3572 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3573 circle->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3574 }
3575 }
3576
3577 if( schsym )
3579
3580 circle->SetPosition( center );
3581 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
3582
3585 }
3586}
3587
3588
3589void SCH_IO_ALTIUM::ParseLine( const std::map<wxString, wxString>& aProperties,
3590 std::vector<LIB_SYMBOL*>& aSymbol )
3591{
3592 ASCH_LINE elem( aProperties );
3593
3594 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3595 {
3596 SCH_SCREEN* screen = getCurrentScreen();
3597 wxCHECK( screen, /* void */ );
3598
3599 // close polygon
3601 line->SetEndPoint( elem.point2 + m_sheetOffset );
3603 GetColorFromInt( elem.Color ) ) );
3604
3605 line->SetFlags( IS_NEW );
3606 screen->Append( line );
3607 }
3608 else
3609 {
3610 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3611 SCH_SYMBOL* schsym = nullptr;
3612
3613 if( !symbol )
3614 {
3615 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3616
3617 if( libSymbolIt == m_libSymbols.end() )
3618 {
3619 // TODO: e.g. can depend on Template (RECORD=39
3620 m_errorMessages.emplace( wxString::Format( wxT( "Line's owner (%d) not found." ), elem.ownerindex ),
3622 return;
3623 }
3624
3625 symbol = libSymbolIt->second;
3626 schsym = m_symbols.at( libSymbolIt->first );
3627 }
3628
3630 symbol->AddDrawItem( line, false );
3631
3632 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3633
3634 if( symbol->GetBodyStyleCount() > 1 )
3635 {
3636 if( !aSymbol.empty() )
3637 {
3638 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3639 }
3640 else
3641 {
3642 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3643
3644 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3645 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3646 }
3647 }
3648
3649 if( !schsym )
3650 {
3651 line->AddPoint( elem.point1 );
3652 line->AddPoint( elem.point2 );
3653 }
3654 else
3655 {
3656 line->AddPoint( GetRelativePosition( elem.point1 + m_sheetOffset, schsym ) );
3657 line->AddPoint( GetRelativePosition( elem.point2 + m_sheetOffset, schsym ) );
3658 }
3659
3661 line->SetLineStyle( GetPlotDashType( elem.LineStyle ) );
3662 }
3663}
3664
3665
3666void SCH_IO_ALTIUM::ParseSignalHarness( const std::map<wxString, wxString>& aProperties )
3667{
3668 ASCH_SIGNAL_HARNESS elem( aProperties );
3669
3670 if( ShouldPutItemOnSheet( elem.ownerindex ) )
3671 {
3672 SCH_SCREEN* screen = getCurrentScreen();
3673 wxCHECK( screen, /* void */ );
3674
3675 for( size_t ii = 0; ii < elem.points.size() - 1; ii++ )
3676 {
3678 line->SetEndPoint( elem.points[ii + 1] + m_sheetOffset );
3680
3681 line->SetFlags( IS_NEW );
3682 screen->Append( line );
3683 }
3684 }
3685 else
3686 {
3687 // No clue if this situation can ever exist
3688 m_errorMessages.emplace( wxT( "Signal harness, belonging to the part is not currently supported." ),
3690 }
3691}
3692
3693
3694void SCH_IO_ALTIUM::ParseHarnessConnector( int aIndex, const std::map<wxString,
3695 wxString>& aProperties )
3696{
3697 ASCH_HARNESS_CONNECTOR elem( aProperties );
3698
3699 if( ShouldPutItemOnSheet( elem.ownerindex ) )
3700 {
3702 auto [it, _] = m_altiumHarnesses.insert( { m_harnessEntryParent, HARNESS()} );
3703
3704 HARNESS& harness = it->second;
3705 HARNESS::HARNESS_PORT& port = harness.m_entry;
3706 harness.m_location = elem.m_location + m_sheetOffset;
3707 harness.m_size = elem.m_size;
3708
3709 VECTOR2I pos = elem.m_location + m_sheetOffset;
3710 VECTOR2I size = elem.m_size;
3711
3712 switch( elem.m_harnessConnectorSide )
3713 {
3714 default:
3716 port.m_location = { pos.x, pos.y + elem.m_primaryConnectionPosition };
3717 break;
3719 port.m_location = { pos.x + size.x, pos.y + elem.m_primaryConnectionPosition };
3720 break;
3722 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y };
3723 break;
3725 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y + size.y };
3726 break;
3727 }
3728 }
3729 else
3730 {
3731 // I have no clue if this situation can ever exist
3732 m_errorMessages.emplace( wxT( "Harness connector, belonging to the part is not currently supported." ),
3734 }
3735}
3736
3737
3738void SCH_IO_ALTIUM::ParseHarnessEntry( const std::map<wxString, wxString>& aProperties )
3739{
3740 ASCH_HARNESS_ENTRY elem( aProperties );
3741
3742 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
3743
3744 if( harnessIt == m_altiumHarnesses.end() )
3745 {
3746 m_errorMessages.emplace( wxString::Format( wxT( "Harness entry's parent (%d) not found." ),
3749 return;
3750 }
3751
3752 HARNESS& harness = harnessIt->second;
3754 port.m_name = elem.Name;
3755 port.m_harnessConnectorSide = elem.Side;
3757
3758 VECTOR2I pos = harness.m_location;
3759 VECTOR2I size = harness.m_size;
3760 int quadrant = 1;
3761
3762 switch( elem.Side )
3763 {
3764 default:
3766 port.m_location = { pos.x, pos.y + elem.DistanceFromTop };
3767 break;
3769 quadrant = 4;
3770 port.m_location = { pos.x + size.x, pos.y + elem.DistanceFromTop };
3771 break;
3773 port.m_location = { pos.x + elem.DistanceFromTop, pos.y };
3774 break;
3776 quadrant = 2;
3777 port.m_location = { pos.x + elem.DistanceFromTop, pos.y + size.y };
3778 break;
3779 }
3780
3781
3782 SCH_SCREEN* screen = getCurrentScreen();
3783 wxCHECK( screen, /* void */ );
3784
3785 SCH_BUS_WIRE_ENTRY* entry = new SCH_BUS_WIRE_ENTRY( port.m_location, quadrant );
3786 port.m_entryLocation = entry->GetPosition() + entry->GetSize();
3787 entry->SetFlags( IS_NEW );
3788 screen->Append( entry );
3789 harness.m_ports.emplace_back( port );
3790}
3791
3792
3793void SCH_IO_ALTIUM::ParseHarnessType( const std::map<wxString, wxString>& aProperties )
3794{
3795 ASCH_HARNESS_TYPE elem( aProperties );
3796
3797 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
3798
3799 if( harnessIt == m_altiumHarnesses.end() )
3800 {
3801 m_errorMessages.emplace( wxString::Format( wxT( "Harness type's parent (%d) not found." ),
3804 return;
3805 }
3806
3807 HARNESS& harness = harnessIt->second;
3808 harness.m_name = elem.Text;
3809}
3810
3811
3812void SCH_IO_ALTIUM::ParseRectangle( const std::map<wxString, wxString>& aProperties,
3813 std::vector<LIB_SYMBOL*>& aSymbol )
3814{
3815 ASCH_RECTANGLE elem( aProperties );
3816
3817 VECTOR2I sheetTopRight = elem.TopRight + m_sheetOffset;
3818 VECTOR2I sheetBottomLeft = elem.BottomLeft + m_sheetOffset;
3819
3820 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3821 {
3822 SCH_SCREEN* screen = getCurrentScreen();
3823 wxCHECK( screen, /* void */ );
3824
3825 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
3826
3827 rect->SetPosition( sheetTopRight );
3828 rect->SetEnd( sheetBottomLeft );
3829 SetSchShapeLine( elem, rect );
3830 SetSchShapeFillAndColor( elem, rect );
3831 rect->SetFlags( IS_NEW );
3832
3833 screen->Append( rect );
3834 }
3835 else
3836 {
3837 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3838 SCH_SYMBOL* schsym = nullptr;
3839
3840 if( !symbol )
3841 {
3842 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3843
3844 if( libSymbolIt == m_libSymbols.end() )
3845 {
3846 // TODO: e.g. can depend on Template (RECORD=39
3847 m_errorMessages.emplace( wxString::Format( wxT( "Rectangle's owner (%d) not found." ),
3848 elem.ownerindex ),
3850 return;
3851 }
3852
3853 symbol = libSymbolIt->second;
3854 schsym = m_symbols.at( libSymbolIt->first );
3855 }
3856
3858 symbol->AddDrawItem( rect, false );
3859
3860 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
3861
3862 if( symbol->GetBodyStyleCount() > 1 )
3863 {
3864 if( !aSymbol.empty() )
3865 {
3866 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3867 }
3868 else
3869 {
3870 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3871
3872 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3873 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3874 }
3875 }
3876
3877 if( !schsym )
3878 {
3879 rect->SetPosition( sheetTopRight );
3880 rect->SetEnd( sheetBottomLeft );
3881 }
3882 else
3883 {
3884 rect->SetPosition( GetRelativePosition( sheetTopRight, schsym ) );
3885 rect->SetEnd( GetRelativePosition( sheetBottomLeft, schsym ) );
3886 }
3887
3890 }
3891}
3892
3893
3894void SCH_IO_ALTIUM::ParseSheetSymbol( int aIndex, const std::map<wxString, wxString>& aProperties )
3895{
3896 ASCH_SHEET_SYMBOL elem( aProperties );
3897
3898 SCH_SHEET* sheet = new SCH_SHEET( getCurrentSheet(), elem.location + m_sheetOffset, elem.size );
3899
3900 sheet->SetBorderColor( GetColorFromInt( elem.color ) );
3901
3902 if( elem.isSolid )
3904
3905 sheet->SetFlags( IS_NEW );
3906
3907 SCH_SCREEN* currentScreen = getCurrentScreen();
3908 wxCHECK( currentScreen, /* void */ );
3909 currentScreen->Append( sheet );
3910
3911 SCH_SHEET_PATH sheetpath = m_sheetPath;
3912 sheetpath.push_back( sheet );
3913
3914 // We'll update later if we find a pageNumber record for it.
3915 sheetpath.SetPageNumber( "#" );
3916
3917 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
3918 wxCHECK( rootScreen, /* void */ );
3919
3920 SCH_SHEET_INSTANCE sheetInstance;
3921
3922 sheetInstance.m_Path = sheetpath.Path();
3923 sheetInstance.m_PageNumber = wxT( "#" );
3924
3925 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
3926 m_sheets.insert( { aIndex, sheet } );
3927}
3928
3929
3930void SCH_IO_ALTIUM::ParseSheetEntry( const std::map<wxString, wxString>& aProperties )
3931{
3932 ASCH_SHEET_ENTRY elem( aProperties );
3933
3934 const auto& sheetIt = m_sheets.find( elem.ownerindex );
3935
3936 if( sheetIt == m_sheets.end() )
3937 {
3938 m_errorMessages.emplace( wxString::Format( wxT( "Sheet entry's owner (%d) not found." ), elem.ownerindex ),
3940 return;
3941 }
3942
3943 SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheetIt->second );
3944 sheetIt->second->AddPin( sheetPin );
3945
3946 wxString pinName = elem.name;
3947
3948 if( !elem.harnessType.IsEmpty() )
3949 pinName += wxT( "{" ) + elem.harnessType + wxT( "}" );
3950
3951 sheetPin->SetText( pinName );
3953 //sheetPin->SetSpinStyle( getSpinStyle( term.OrientAngle, false ) );
3954 //sheetPin->SetPosition( getKiCadPoint( term.Position ) );
3955
3956 VECTOR2I pos = sheetIt->second->GetPosition();
3957 VECTOR2I size = sheetIt->second->GetSize();
3958
3959 switch( elem.side )
3960 {
3961 default:
3963 sheetPin->SetPosition( { pos.x, pos.y + elem.distanceFromTop } );
3964 sheetPin->SetSpinStyle( SPIN_STYLE::LEFT );
3965 sheetPin->SetSide( SHEET_SIDE::LEFT );
3966 break;
3967
3969 sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.distanceFromTop } );
3970 sheetPin->SetSpinStyle( SPIN_STYLE::RIGHT );
3971 sheetPin->SetSide( SHEET_SIDE::RIGHT );
3972 break;
3973
3975 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y } );
3976 sheetPin->SetSpinStyle( SPIN_STYLE::UP );
3977 sheetPin->SetSide( SHEET_SIDE::TOP );
3978 break;
3979
3981 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y + size.y } );
3982 sheetPin->SetSpinStyle( SPIN_STYLE::BOTTOM );
3983 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
3984 break;
3985 }
3986
3987 switch( elem.iotype )
3988 {
3989 default:
3992 break;
3993
3996 break;
3997
4000 break;
4001
4004 break;
4005 }
4006}
4007
4008
4010 REPORTER* aReporter )
4011{
4013 {
4015 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4016 line1->AddPoint( { 0, 0 } );
4017 line1->AddPoint( { 0, schIUScale.MilsToIU( 50 ) } );
4018 aKsymbol->AddDrawItem( line1, false );
4019
4020 if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE )
4021 {
4023 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
4024 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 75 ) } );
4025 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 25 ), 0 ) );
4026 aKsymbol->AddDrawItem( circle, false );
4027 }
4028 else
4029 {
4031 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4032 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4033 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
4034 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4035 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4036 aKsymbol->AddDrawItem( line2, false );
4037 }
4038
4039 return { 0, schIUScale.MilsToIU( 150 ) };
4040 }
4041 else if( aStyle == ASCH_POWER_PORT_STYLE::WAVE )
4042 {
4044 line->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4045 line->AddPoint( { 0, 0 } );
4046 line->AddPoint( { 0, schIUScale.MilsToIU( 72 ) } );
4047 aKsymbol->AddDrawItem( line, false );
4048
4050 bezier->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
4051 bezier->SetStart( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 50 ) } );
4052 bezier->SetBezierC1( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 87 ) } );
4053 bezier->SetBezierC2( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 63 ) } );
4054 bezier->SetEnd( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 100 ) } );
4055 aKsymbol->AddDrawItem( bezier, false );
4056
4057 return { 0, schIUScale.MilsToIU( 150 ) };
4058 }
4059 else if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND
4061 || aStyle == ASCH_POWER_PORT_STYLE::EARTH
4063 {
4065 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4066 line1->AddPoint( { 0, 0 } );
4067 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
4068 aKsymbol->AddDrawItem( line1, false );
4069
4071 {
4073 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4074 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4075 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4076 aKsymbol->AddDrawItem( line2, false );
4077
4079 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4080 line3->AddPoint( { schIUScale.MilsToIU( -70 ), schIUScale.MilsToIU( 130 ) } );
4081 line3->AddPoint( { schIUScale.MilsToIU( 70 ), schIUScale.MilsToIU( 130 ) } );
4082 aKsymbol->AddDrawItem( line3, false );
4083
4085 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4086 line4->AddPoint( { schIUScale.MilsToIU( -40 ), schIUScale.MilsToIU( 160 ) } );
4087 line4->AddPoint( { schIUScale.MilsToIU( 40 ), schIUScale.MilsToIU( 160 ) } );
4088 aKsymbol->AddDrawItem( line4, false );
4089
4091 line5->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4092 line5->AddPoint( { schIUScale.MilsToIU( -10 ), schIUScale.MilsToIU( 190 ) } );
4093 line5->AddPoint( { schIUScale.MilsToIU( 10 ), schIUScale.MilsToIU( 190 ) } );
4094 aKsymbol->AddDrawItem( line5, false );
4095 }
4096 else if( aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND )
4097 {
4099 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4100 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4101 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4102 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 200 ) } );
4103 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4104 aKsymbol->AddDrawItem( line2, false );
4105 }
4106 else if( aStyle == ASCH_POWER_PORT_STYLE::EARTH )
4107 {
4109 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4110 line2->AddPoint( { schIUScale.MilsToIU( -150 ), schIUScale.MilsToIU( 200 ) } );
4111 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4112 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4113 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 200 ) } );
4114 aKsymbol->AddDrawItem( line2, false );
4115
4117 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4118 line3->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4119 line3->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 200 ) } );
4120 aKsymbol->AddDrawItem( line3, false );
4121 }
4122 else // ASCH_POWER_PORT_STYLE::GOST_ARROW
4123 {
4125 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4126 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4127 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4128 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
4129 aKsymbol->AddDrawItem( line2, false );
4130
4131 return { 0, schIUScale.MilsToIU( 150 ) }; // special case
4132 }
4133
4134 return { 0, schIUScale.MilsToIU( 250 ) };
4135 }
4138 {
4140 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4141 line1->AddPoint( { 0, 0 } );
4142 line1->AddPoint( { 0, schIUScale.MilsToIU( 160 ) } );
4143 aKsymbol->AddDrawItem( line1, false );
4144
4146 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4147 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 160 ) } );
4148 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 160 ) } );
4149 aKsymbol->AddDrawItem( line2, false );
4150
4152 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4153 line3->AddPoint( { schIUScale.MilsToIU( -60 ), schIUScale.MilsToIU( 200 ) } );
4154 line3->AddPoint( { schIUScale.MilsToIU( 60 ), schIUScale.MilsToIU( 200 ) } );
4155 aKsymbol->AddDrawItem( line3, false );
4156
4158 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4159 line4->AddPoint( { schIUScale.MilsToIU( -20 ), schIUScale.MilsToIU( 240 ) } );
4160 line4->AddPoint( { schIUScale.MilsToIU( 20 ), schIUScale.MilsToIU( 240 ) } );
4161 aKsymbol->AddDrawItem( line4, false );
4162
4164 return { 0, schIUScale.MilsToIU( -300 ) };
4165
4167 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4168 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 160 ) } );
4169 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 120 ), 0 ) );
4170 aKsymbol->AddDrawItem( circle, false );
4171
4172 return { 0, schIUScale.MilsToIU( 350 ) };
4173 }
4174 else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_BAR )
4175 {
4177 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4178 line1->AddPoint( { 0, 0 } );
4179 line1->AddPoint( { 0, schIUScale.MilsToIU( 200 ) } );
4180 aKsymbol->AddDrawItem( line1, false );
4181
4183 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4184 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 200 ) } );
4185 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 200 ) } );
4186 aKsymbol->AddDrawItem( line2, false );
4187
4188 return { 0, schIUScale.MilsToIU( 250 ) };
4189 }
4190 else
4191 {
4192 if( aStyle != ASCH_POWER_PORT_STYLE::BAR )
4193 {
4194 aReporter->Report( _( "Power Port with unknown style imported as 'Bar' type." ),
4196 }
4197
4199 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4200 line1->AddPoint( { 0, 0 } );
4201 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
4202 aKsymbol->AddDrawItem( line1, false );
4203
4205 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4206 line2->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 100 ) } );
4207 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 100 ) } );
4208 aKsymbol->AddDrawItem( line2, false );
4209
4210 return { 0, schIUScale.MilsToIU( 150 ) };
4211 }
4212}
4213
4214
4215void SCH_IO_ALTIUM::ParsePowerPort( const std::map<wxString, wxString>& aProperties )
4216{
4217 ASCH_POWER_PORT elem( aProperties );
4218
4219 wxString symName( elem.text );
4220 std::string styleName( magic_enum::enum_name<ASCH_POWER_PORT_STYLE>( elem.style ) );
4221
4222 if( !styleName.empty() )
4223 symName << '_' << styleName;
4224
4225 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symName );
4226 LIB_SYMBOL* libSymbol = nullptr;
4227
4228 const auto& powerSymbolIt = m_powerSymbols.find( symName );
4229
4230 if( powerSymbolIt != m_powerSymbols.end() )
4231 {
4232 libSymbol = powerSymbolIt->second; // cache hit
4233 }
4234 else
4235 {
4236 libSymbol = new LIB_SYMBOL( wxEmptyString );
4237 libSymbol->SetGlobalPower();
4238 libSymbol->SetName( symName );
4239 libSymbol->GetReferenceField().SetText( "#PWR" );
4240 libSymbol->GetReferenceField().SetVisible( false );
4241 libSymbol->GetValueField().SetText( elem.text );
4242 libSymbol->GetValueField().SetVisible( true );
4243 libSymbol->SetDescription( wxString::Format( _( "Power symbol creates a global label with name '%s'" ),
4244 elem.text ) );
4245 libSymbol->SetKeyWords( "power-flag" );
4246 libSymbol->SetLibId( libId );
4247
4248 // generate graphic
4249 SCH_PIN* pin = new SCH_PIN( libSymbol );
4250 libSymbol->AddDrawItem( pin, false );
4251
4252 pin->SetName( elem.text );
4253 pin->SetPosition( { 0, 0 } );
4254 pin->SetLength( 0 );
4256 pin->SetVisible( false );
4257
4258 VECTOR2I valueFieldPos = HelperGeneratePowerPortGraphics( libSymbol, elem.style, m_reporter );
4259
4260 libSymbol->GetValueField().SetPosition( valueFieldPos );
4261
4262 // this has to be done after parsing the LIB_SYMBOL!
4263 m_powerSymbols.insert( { symName, libSymbol } );
4264 }
4265
4266 SCH_SCREEN* screen = getCurrentScreen();
4267 wxCHECK( screen, /* void */ );
4268
4269 SCH_SYMBOL* symbol = new SCH_SYMBOL();
4270 symbol->SetRef( &m_sheetPath, "#PWR?" );
4271 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
4272 symbol->SetValueFieldText( elem.text );
4273 symbol->SetLibId( libId );
4274 symbol->SetLibSymbol( new LIB_SYMBOL( *libSymbol ) );
4275
4276 SCH_FIELD* valueField = symbol->GetField( FIELD_T::VALUE );
4277 valueField->SetVisible( elem.showNetName );
4278 valueField->SetPosition( libSymbol->GetValueField().GetPosition() );
4279
4280 symbol->SetPosition( elem.location + m_sheetOffset );
4281
4282 switch( elem.orientation )
4283 {
4286 valueField->SetTextAngle( ANGLE_VERTICAL );
4288 break;
4289
4292 valueField->SetTextAngle( ANGLE_HORIZONTAL );
4294 break;
4295
4298 valueField->SetTextAngle( ANGLE_VERTICAL );
4300 break;
4301
4304 valueField->SetTextAngle( ANGLE_HORIZONTAL );
4306 break;
4307
4308 default:
4309 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
4310 break;
4311 }
4312
4313 screen->Append( symbol );
4314}
4315
4316
4318{
4319 ParsePortHelper( aElem );
4320}
4321
4322
4324{
4325 if( !aElem.HarnessType.IsEmpty() )
4326 {
4327 // Parse harness ports after "Additional" compound section is parsed
4328 m_altiumHarnessPortsCurrentSheet.emplace_back( aElem );
4329 return;
4330 }
4331
4332 ParsePortHelper( aElem );
4333}
4334
4335
4337{
4338 VECTOR2I start = aElem.Location + m_sheetOffset;
4339 VECTOR2I end = start;
4340
4341 switch( aElem.Style )
4342 {
4343 default:
4348 end.x += aElem.Width;
4349 break;
4350
4355 end.y -= aElem.Width;
4356 break;
4357 }
4358
4359 // Check which connection points exists in the schematic
4360 SCH_SCREEN* screen = getCurrentScreen();
4361 wxCHECK( screen, /* void */ );
4362
4363 bool startIsWireTerminal = screen->IsTerminalPoint( start, LAYER_WIRE );
4364 bool startIsBusTerminal = screen->IsTerminalPoint( start, LAYER_BUS );
4365
4366 bool endIsWireTerminal = screen->IsTerminalPoint( end, LAYER_WIRE );
4367 bool endIsBusTerminal = screen->IsTerminalPoint( end, LAYER_BUS );
4368
4369 // check if any of the points is a terminal point
4370 // TODO: there seems a problem to detect approximated connections towards component pins?
4371 bool connectionFound = startIsWireTerminal
4372 || startIsBusTerminal
4373 || endIsWireTerminal
4374 || endIsBusTerminal;
4375
4376 if( !connectionFound )
4377 {
4378 for( auto& [ _, harness ] : m_altiumHarnesses )
4379 {
4380 if( harness.m_name.CmpNoCase( aElem.HarnessType ) != 0 )
4381 continue;
4382
4383 BOX2I bbox( harness.m_location, harness.m_size );
4384 bbox.Inflate( 10 );
4385
4386 if( bbox.Contains( start ) )
4387 {
4388 startIsBusTerminal = true;
4389 connectionFound = true;
4390 break;
4391 }
4392
4393 if( bbox.Contains( end ) )
4394 {
4395 endIsBusTerminal = true;
4396 connectionFound = true;
4397 break;
4398 }
4399 }
4400
4401 if( !connectionFound )
4402 {
4403 m_errorMessages.emplace( wxString::Format( _( "Port %s has no connections." ), aElem.Name ),
4405 }
4406 }
4407
4408 // Select label position. In case both match, we will add a line later.
4409 VECTOR2I position = ( startIsWireTerminal || startIsBusTerminal ) ? start : end;
4410 SCH_LABEL_BASE* label;
4411
4412 wxString labelName = aElem.Name;
4413
4414 if( !aElem.HarnessType.IsEmpty() )
4415 labelName += wxT( "{" ) + aElem.HarnessType + wxT( "}" );
4416
4417 // TODO: detect correct label type depending on sheet settings, etc.
4418#if 1 // Set to 1 to use SCH_HIERLABEL label, 0 to use SCH_GLOBALLABEL
4419 {
4420 label = new SCH_HIERLABEL( position, labelName );
4421 }
4422#else
4423 label = new SCH_GLOBALLABEL( position, labelName );
4424
4425 // Default "Sheet References" field should be hidden, at least for now
4426 label->GetField( INTERSHEET_REFS )->SetVisible( false );
4427#endif
4428
4429 switch( aElem.IOtype )
4430 {
4431 default:
4436 }
4437
4438 switch( aElem.Style )
4439 {
4440 default:
4445 if( ( startIsWireTerminal || startIsBusTerminal ) )
4447 else
4449
4450 break;
4451
4456 if( ( startIsWireTerminal || startIsBusTerminal ) )
4457 label->SetSpinStyle( SPIN_STYLE::UP );
4458 else
4460
4461 break;
4462 }
4463
4464 label->AutoplaceFields( screen, AUTOPLACE_AUTO );
4465 label->SetFlags( IS_NEW );
4466
4467 screen->Append( label );
4468
4469 // This is a hack, for the case both connection points are valid: add a small wire
4470 if( ( startIsWireTerminal && endIsWireTerminal ) )
4471 {
4472 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
4473 wire->SetEndPoint( end );
4474 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
4475 wire->SetFlags( IS_NEW );
4476 screen->Append( wire );
4477 }
4478 else if( startIsBusTerminal && endIsBusTerminal )
4479 {
4480 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
4481 wire->SetEndPoint( end );
4482 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
4483 wire->SetFlags( IS_NEW );
4484 screen->Append( wire );
4485 }
4486}
4487
4488
4489void SCH_IO_ALTIUM::ParseNoERC( const std::map<wxString, wxString>& aProperties )
4490{
4491 ASCH_NO_ERC elem( aProperties );
4492
4493 SCH_SCREEN* screen = getCurrentScreen();
4494 wxCHECK( screen, /* void */ );
4495
4496 if( elem.isActive )
4497 {
4498 SCH_NO_CONNECT* noConnect = new SCH_NO_CONNECT( elem.location + m_sheetOffset );
4499
4500 noConnect->SetFlags( IS_NEW );
4501 screen->Append( noConnect );
4502 }
4503}
4504
4505
4506void SCH_IO_ALTIUM::ParseNetLabel( const std::map<wxString, wxString>& aProperties )
4507{
4508 ASCH_NET_LABEL elem( aProperties );
4509
4510 SCH_LABEL* label = new SCH_LABEL( elem.location + m_sheetOffset, elem.text );
4511
4512 SCH_SCREEN* screen = getCurrentScreen();
4513 wxCHECK( screen, /* void */ );
4514
4515 SetTextPositioning( label, elem.justification, elem.orientation );
4516
4517 label->SetFlags( IS_NEW );
4518 screen->Append( label );
4519}
4520
4521
4522void SCH_IO_ALTIUM::ParseBus( const std::map<wxString, wxString>& aProperties )
4523{
4524 ASCH_BUS elem( aProperties );
4525
4526 SCH_SCREEN* screen = getCurrentScreen();
4527 wxCHECK( screen, /* void */ );
4528
4529 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
4530 {
4531 SCH_LINE* bus = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_BUS );
4532 bus->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
4533 bus->SetLineWidth( elem.lineWidth );
4534
4535 bus->SetFlags( IS_NEW );
4536 screen->Append( bus );
4537 }
4538}
4539
4540
4541void SCH_IO_ALTIUM::ParseWire( const std::map<wxString, wxString>& aProperties )
4542{
4543 ASCH_WIRE elem( aProperties );
4544
4545 SCH_SCREEN* screen = getCurrentScreen();
4546 wxCHECK( screen, /* void */ );
4547
4548 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
4549 {
4550 SCH_LINE* wire = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_WIRE );
4551 wire->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
4552 // wire->SetLineWidth( elem.lineWidth );
4553
4554 wire->SetFlags( IS_NEW );
4555 screen->Append( wire );
4556 }
4557}
4558
4559
4560void SCH_IO_ALTIUM::ParseJunction( const std::map<wxString, wxString>& aProperties )
4561{
4562 SCH_SCREEN* screen = getCurrentScreen();
4563 wxCHECK( screen, /* void */ );
4564
4565 ASCH_JUNCTION elem( aProperties );
4566
4567 SCH_JUNCTION* junction = new SCH_JUNCTION( elem.location + m_sheetOffset );
4568
4569 junction->SetFlags( IS_NEW );
4570 screen->Append( junction );
4571}
4572
4573
4574void SCH_IO_ALTIUM::ParseImage( const std::map<wxString, wxString>& aProperties )
4575{
4576 ASCH_IMAGE elem( aProperties );
4577
4578 const auto& component = m_altiumComponents.find( elem.ownerindex );
4579
4580 //Hide the image if it is owned by a component but the part id do not match
4581 if( component != m_altiumComponents.end()
4582 && component->second.currentpartid != elem.ownerpartid )
4583 return;
4584
4585 VECTOR2I center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
4586 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
4587 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
4588
4589 SCH_SCREEN* screen = getCurrentScreen();
4590 wxCHECK( screen, /* void */ );
4591
4592 if( elem.embedimage )
4593 {
4594 const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
4595
4596 if( !storageFile )
4597 {
4598 m_errorMessages.emplace( wxString::Format( _( "Embedded file %s not found in storage." ),
4599 elem.filename ),
4601 return;
4602 }
4603
4604 wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
4605
4606 // As wxZlibInputStream is not seekable, we need to write a temporary file
4607 wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
4608 wxZlibInputStream zlibInputStream( fileStream );
4609 wxFFileOutputStream outputStream( storagePath );
4610 outputStream.Write( zlibInputStream );
4611 outputStream.Close();
4612
4613 if( !refImage.ReadImageFile( storagePath ) )
4614 {
4615 m_errorMessages.emplace( wxString::Format( _( "Error reading image %s." ), storagePath ),
4617 return;
4618 }
4619
4620 // Remove temporary file
4621 wxRemoveFile( storagePath );
4622 }
4623 else
4624 {
4625 if( !wxFileExists( elem.filename ) )
4626 {
4627 m_errorMessages.emplace( wxString::Format( _( "File not found %s." ), elem.filename ),
4629 return;
4630 }
4631
4632 if( !refImage.ReadImageFile( elem.filename ) )
4633 {
4634 m_errorMessages.emplace( wxString::Format( _( "Error reading image %s." ), elem.filename ),
4636 return;
4637 }
4638 }
4639
4640 // we only support one scale, thus we need to select one in case it does not keep aspect ratio
4641 const VECTOR2I currentImageSize = refImage.GetSize();
4642 const VECTOR2I expectedImageSize = elem.location - elem.corner;
4643 const double scaleX = std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
4644 const double scaleY = std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
4645 refImage.SetImageScale( std::min( scaleX, scaleY ) );
4646
4647 bitmap->SetFlags( IS_NEW );
4648 screen->Append( bitmap.release() );
4649}
4650
4651
4652void SCH_IO_ALTIUM::ParseSheet( const std::map<wxString, wxString>& aProperties )
4653{
4654 m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
4655
4656 SCH_SCREEN* screen = getCurrentScreen();
4657 wxCHECK( screen, /* void */ );
4658
4659 PAGE_INFO pageInfo;
4660
4661 bool isPortrait = m_altiumSheet->sheetOrientation == ASCH_SHEET_WORKSPACEORIENTATION::PORTRAIT;
4662
4663 if( m_altiumSheet->useCustomSheet )
4664 {
4665 PAGE_INFO::SetCustomWidthMils( schIUScale.IUToMils( m_altiumSheet->customSize.x ) );
4666 PAGE_INFO::SetCustomHeightMils( schIUScale.IUToMils( m_altiumSheet->customSize.y ) );
4667 pageInfo.SetType( PAGE_SIZE_TYPE::User, isPortrait );
4668 }
4669 else
4670 {
4671 switch( m_altiumSheet->sheetSize )
4672 {
4673 default:
4674 case ASCH_SHEET_SIZE::A4: pageInfo.SetType( "A4", isPortrait ); break;
4675 case ASCH_SHEET_SIZE::A3: pageInfo.SetType( "A3", isPortrait ); break;
4676 case ASCH_SHEET_SIZE::A2: pageInfo.SetType( "A2", isPortrait ); break;
4677 case ASCH_SHEET_SIZE::A1: pageInfo.SetType( "A1", isPortrait ); break;
4678 case ASCH_SHEET_SIZE::A0: pageInfo.SetType( "A0", isPortrait ); break;
4679 case ASCH_SHEET_SIZE::A: pageInfo.SetType( "A", isPortrait ); break;
4680 case ASCH_SHEET_SIZE::B: pageInfo.SetType( "B", isPortrait ); break;
4681 case ASCH_SHEET_SIZE::C: pageInfo.SetType( "C", isPortrait ); break;
4682 case ASCH_SHEET_SIZE::D: pageInfo.SetType( "D", isPortrait ); break;
4683 case ASCH_SHEET_SIZE::E: pageInfo.SetType( "E", isPortrait ); break;
4684 case ASCH_SHEET_SIZE::LETTER: pageInfo.SetType( "USLetter", isPortrait ); break;
4685 case ASCH_SHEET_SIZE::LEGAL: pageInfo.SetType( "USLegal", isPortrait ); break;
4686 case ASCH_SHEET_SIZE::TABLOID: pageInfo.SetType( "A3", isPortrait ); break;
4687 case ASCH_SHEET_SIZE::ORCAD_A: pageInfo.SetType( "A", isPortrait ); break;
4688 case ASCH_SHEET_SIZE::ORCAD_B: pageInfo.SetType( "B", isPortrait ); break;
4689 case ASCH_SHEET_SIZE::ORCAD_C: pageInfo.SetType( "C", isPortrait ); break;
4690 case ASCH_SHEET_SIZE::ORCAD_D: pageInfo.SetType( "D", isPortrait ); break;
4691 case ASCH_SHEET_SIZE::ORCAD_E: pageInfo.SetType( "E", isPortrait ); break;
4692 }
4693 }
4694
4695 screen->SetPageSettings( pageInfo );
4696
4697 m_sheetOffset = { 0, pageInfo.GetHeightIU( schIUScale.IU_PER_MILS ) };
4698}
4699
4700
4701void SCH_IO_ALTIUM::ParseSheetName( const std::map<wxString, wxString>& aProperties )
4702{
4703 ASCH_SHEET_NAME elem( aProperties );
4704 SCH_SCREEN* currentScreen = getCurrentScreen();
4705
4706 wxCHECK( currentScreen, /* void */ );
4707
4708 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4709
4710 if( sheetIt == m_sheets.end() )
4711 {
4712 m_errorMessages.emplace( wxString::Format( wxT( "Sheetname's owner (%d) not found." ),
4713 elem.ownerindex ),
4715 return;
4716 }
4717
4718 wxString baseName = elem.text;
4719 baseName.Replace( wxT( "/" ), wxT( "_" ) );
4720
4721 wxString sheetName = baseName;
4722 std::set<wxString> sheetNames;
4723
4724 for( EDA_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
4725 {
4726 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
4727 sheetNames.insert( sheet->GetName() );
4728 }
4729
4730 for( int ii = 1; ; ++ii )
4731 {
4732 if( sheetNames.find( sheetName ) == sheetNames.end() )
4733 break;
4734
4735 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
4736 }
4737
4738 SCH_FIELD* sheetNameField = sheetIt->second->GetField( FIELD_T::SHEET_NAME );
4739
4740 sheetNameField->SetPosition( elem.location + m_sheetOffset );
4741 sheetNameField->SetText( sheetName );
4742 sheetNameField->SetVisible( !elem.isHidden );
4744}
4745
4746
4747void SCH_IO_ALTIUM::ParseFileName( const std::map<wxString, wxString>& aProperties )
4748{
4749 ASCH_FILE_NAME elem( aProperties );
4750
4751 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4752
4753 if( sheetIt == m_sheets.end() )
4754 {
4755 m_errorMessages.emplace( wxString::Format( wxT( "Filename's owner (%d) not found." ),
4756 elem.ownerindex ),
4758 return;
4759 }
4760
4761 SCH_FIELD* filenameField = sheetIt->second->GetField( FIELD_T::SHEET_FILENAME );
4762
4763 filenameField->SetPosition( elem.location + m_sheetOffset );
4764
4765 // Keep the filename of the Altium file until after the file is actually loaded.
4766 filenameField->SetText( elem.text );
4767 filenameField->SetVisible( !elem.isHidden );
4769}
4770
4771
4772void SCH_IO_ALTIUM::ParseDesignator( const std::map<wxString, wxString>& aProperties )
4773{
4774 ASCH_DESIGNATOR elem( aProperties );
4775
4776 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4777
4778 if( libSymbolIt == m_libSymbols.end() )
4779 {
4780 // TODO: e.g. can depend on Template (RECORD=39
4781 m_errorMessages.emplace( wxString::Format( wxT( "Designator's owner (%d) not found." ),
4782 elem.ownerindex ),
4784 return;
4785 }
4786
4787 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4788 SCH_SHEET_PATH sheetpath;
4789
4790 SCH_SCREEN* screen = getCurrentScreen();
4791 wxCHECK( screen, /* void */ );
4792
4793 // Graphics symbols have no reference. '#GRAPHIC' allows them to not have footprint associated.
4794 // Note: not all unnamed imported symbols are necessarily graphics.
4795 bool emptyRef = elem.text.IsEmpty();
4796 symbol->SetRef( &m_sheetPath, emptyRef ? wxString( wxS( "#GRAPHIC" ) ) : elem.text );
4797
4798 // I am not sure value and ref should be invisible just because emptyRef is true
4799 // I have examples with this criteria fully incorrect.
4800 bool visible = !emptyRef;
4801
4802 symbol->GetField( FIELD_T::VALUE )->SetVisible( visible );
4803
4804 SCH_FIELD* field = symbol->GetField( FIELD_T::REFERENCE );
4805 field->SetVisible( visible );
4806 field->SetPosition( elem.location + m_sheetOffset );
4807 SetTextPositioning( field, elem.justification, elem.orientation );
4808
4809 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
4810
4811 if( altiumSymIt != m_altiumComponents.end() )
4812 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
4813}
4814
4815
4816void SCH_IO_ALTIUM::ParseLibDesignator( const std::map<wxString, wxString>& aProperties,
4817 std::vector<LIB_SYMBOL*>& aSymbol,
4818 std::vector<int>& aFontSizes )
4819{
4820 ASCH_DESIGNATOR elem( aProperties );
4821
4822 if( elem.ownerpartdisplaymode != 0 )
4823 return;
4824
4825 for( LIB_SYMBOL* symbol : aSymbol )
4826 {
4827 bool emptyRef = elem.text.IsEmpty();
4828 SCH_FIELD& refField = symbol->GetReferenceField();
4829
4830 if( emptyRef )
4831 refField.SetText( wxT( "X" ) );
4832 else
4833 refField.SetText( elem.text.BeforeLast( '?' ) ); // remove the '?' at the end for KiCad-style
4834
4835 refField.SetPosition( elem.location );
4836 SetTextPositioning( &refField, elem.justification, elem.orientation );
4837
4838 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
4839 {
4840 int size = aFontSizes[elem.fontId - 1];
4841 refField.SetTextSize( { size, size } );
4842 }
4843 }
4844}
4845
4846
4847void SCH_IO_ALTIUM::ParseBusEntry( const std::map<wxString, wxString>& aProperties )
4848{
4849 ASCH_BUS_ENTRY elem( aProperties );
4850
4851 SCH_SCREEN* screen = getCurrentScreen();
4852 wxCHECK( screen, /* void */ );
4853
4854 SCH_BUS_WIRE_ENTRY* busWireEntry = new SCH_BUS_WIRE_ENTRY( elem.location + m_sheetOffset );
4855
4856 VECTOR2I vector = elem.corner - elem.location;
4857 busWireEntry->SetSize( { vector.x, vector.y } );
4858
4859 busWireEntry->SetFlags( IS_NEW );
4860 screen->Append( busWireEntry );
4861}
4862
4863
4864void SCH_IO_ALTIUM::ParseParameter( const std::map<wxString, wxString>& aProperties )
4865{
4866 ASCH_PARAMETER elem( aProperties );
4867
4868 // TODO: fill in replacements from variant, sheet and project
4869 static const std::map<wxString, wxString> variableMap = {
4870 { "COMMENT", "VALUE" },
4871 { "VALUE", "ALTIUM_VALUE" },
4872 };
4873
4874 if( elem.ownerindex <= 0 )
4875 {
4876 // This is some sheet parameter
4877 if( elem.text == "*" )
4878 return; // indicates parameter not set?
4879
4880 wxString paramName = elem.name.Upper();
4881
4882 if( paramName == "SHEETNUMBER" )
4883 {
4884 m_sheetPath.SetPageNumber( elem.text );
4885 }
4886 else if( paramName == "TITLE" )
4887 {
4888 m_currentTitleBlock->SetTitle( elem.text );
4889 }
4890 else if( paramName == "REVISION" )
4891 {
4892 m_currentTitleBlock->SetRevision( elem.text );
4893 }
4894 else if( paramName == "DATE" )
4895 {
4896 m_currentTitleBlock->SetDate( elem.text );
4897 }
4898 else if( paramName == "COMPANYNAME" )
4899 {
4900 m_currentTitleBlock->SetCompany( elem.text );
4901 }
4902 else
4903 {
4904 m_schematic->Project().GetTextVars()[ paramName ] = elem.text;
4905 }
4906 }
4907 else
4908 {
4909 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4910
4911 if( libSymbolIt == m_libSymbols.end() )
4912 {
4913 // TODO: e.g. can depend on Template (RECORD=39
4914 return;
4915 }
4916
4917 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4918 SCH_FIELD* field = nullptr;
4919 wxString upperName = elem.name.Upper();
4920
4921 if( upperName == "COMMENT" )
4922 {
4923 field = symbol->GetField( FIELD_T::VALUE );
4924 }
4925 else
4926 {
4927 wxString fieldName = elem.name.Upper();
4928
4929 if( fieldName.IsEmpty() )
4930 {
4931 int disambiguate = 1;
4932
4933 while( 1 )
4934 {
4935 fieldName = wxString::Format( "ALTIUM_UNNAMED_%d", disambiguate++ );
4936
4937 if( !symbol->GetField( fieldName ) )
4938 break;
4939 }
4940 }
4941 else if( fieldName == "VALUE" )
4942 {
4943 fieldName = "ALTIUM_VALUE";
4944 }
4945
4946 field = symbol->AddField( SCH_FIELD( symbol, FIELD_T::USER, fieldName ) );
4947 }
4948
4949 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
4950 field->SetText( kicadText );
4951 field->SetPosition( elem.location + m_sheetOffset );
4952 field->SetVisible( !elem.isHidden );
4953 field->SetNameShown( elem.isShowName );
4954 SetTextPositioning( field, elem.justification, elem.orientation );
4955
4956 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
4957
4958 if( altiumSymIt != m_altiumComponents.end() )
4959 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
4960 }
4961}
4962
4963
4964void SCH_IO_ALTIUM::ParseLibParameter( const std::map<wxString, wxString>& aProperties,
4965 std::vector<LIB_SYMBOL*>& aSymbol,
4966 std::vector<int>& aFontSizes )
4967{
4968 ASCH_PARAMETER elem( aProperties );
4969
4970 if( elem.ownerpartdisplaymode != 0 )
4971 return;
4972
4973 // Part ID 1 is the current library part.
4974 // Part ID ALTIUM_COMPONENT_NONE(-1) means all parts
4975 // If a parameter is assigned to a specific element such as a pin,
4976 // we will need to handle it here.
4977 // TODO: Handle HIDDENNETNAME property (others?)
4978 if( elem.ownerpartid != 1 && elem.ownerpartid != ALTIUM_COMPONENT_NONE )
4979 return;
4980
4981 // If ownerindex is populated, this is parameter belongs to a subelement (e.g. pin).
4982 // Ignore for now.
4983 // TODO: Update this when KiCad supports parameters for any object
4984 if( elem.ownerindex != ALTIUM_COMPONENT_NONE )
4985 return;
4986
4987 // TODO: fill in replacements from variant, sheet and project
4988 // N.B. We do not keep the Altium "VALUE" variable here because
4989 // we don't have a way to assign variables to specific symbols
4990 std::map<wxString, wxString> variableMap = {
4991 { "COMMENT", "VALUE" },
4992 };
4993
4994 for( LIB_SYMBOL* libSymbol : aSymbol )
4995 {
4996 SCH_FIELD* field = nullptr;
4997 wxString upperName = elem.name.Upper();
4998
4999 if( upperName == "COMMENT" )
5000 {
5001 field = &libSymbol->GetValueField();
5002 }
5003 else
5004 {
5005 wxString fieldNameStem = elem.name;
5006 wxString fieldName = fieldNameStem;
5007 int disambiguate = 1;
5008
5009 if( fieldName.IsEmpty() )
5010 {
5011 fieldNameStem = "ALTIUM_UNNAMED";
5012 fieldName = "ALTIUM_UNNAMED_1";
5013 disambiguate = 2;
5014 }
5015 else if( upperName == "VALUE" )
5016 {
5017 fieldNameStem = "ALTIUM_VALUE";
5018 fieldName = "ALTIUM_VALUE";
5019 }
5020
5021 // Avoid adding duplicate fields
5022 while( libSymbol->GetField( fieldName ) )
5023 fieldName = wxString::Format( "%s_%d", fieldNameStem, disambiguate++ );
5024
5025 SCH_FIELD* new_field = new SCH_FIELD( libSymbol, FIELD_T::USER, fieldName );
5026 libSymbol->AddField( new_field );
5027 field = new_field;
5028 }
5029
5030 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
5031 field->SetText( kicadText );
5032
5033 field->SetTextPos( elem.location );
5034 SetTextPositioning( field, elem.justification, elem.orientation );
5035 field->SetVisible( !elem.isHidden );
5036
5037 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
5038 {
5039 int size = aFontSizes[elem.fontId - 1];
5040 field->SetTextSize( { size, size } );
5041 }
5042 else
5043 {
5044 int size = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
5045 field->SetTextSize( { size, size } );
5046 }
5047
5048 }
5049}
5050
5051
5053 const std::map<wxString, wxString>& aProperties )
5054{
5055 ASCH_IMPLEMENTATION_LIST elem( aProperties );
5056
5057 m_altiumImplementationList.emplace( aIndex, elem.ownerindex );
5058}
5059
5060
5061void SCH_IO_ALTIUM::ParseImplementation( const std::map<wxString, wxString>& aProperties,
5062 std::vector<LIB_SYMBOL*>& aSymbol )
5063{
5064 ASCH_IMPLEMENTATION elem( aProperties );
5065
5066 if( elem.type != wxS( "PCBLIB" ) )
5067 return;
5068
5069 // For schematic files, we need to check if the model is current.
5070 if( aSymbol.size() == 0 && !elem.isCurrent )
5071 return;
5072
5073 // For IntLibs we want to use the same lib name for footprints
5074 wxString libName = m_isIntLib ? m_libName : elem.libname;
5075
5076 wxArrayString fpFilters;
5077 fpFilters.Add( wxString::Format( wxS( "*%s*" ), elem.name ) );
5078
5079 // Parse the footprint fields for the library symbol
5080 if( !aSymbol.empty() )
5081 {
5082 for( LIB_SYMBOL* symbol : aSymbol )
5083 {
5084 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
5085
5086 symbol->SetFPFilters( fpFilters );
5087 symbol->GetField( FIELD_T::FOOTPRINT )->SetText( fpLibId.Format() );
5088 }
5089
5090 return;
5091 }
5092
5093 const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex );
5094
5095 if( implementationOwnerIt == m_altiumImplementationList.end() )
5096 {
5097 m_errorMessages.emplace( wxString::Format( wxT( "Implementation's owner (%d) not found." ),
5098 elem.ownerindex ),
5100 return;
5101 }
5102
5103 const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second );
5104
5105 if( libSymbolIt == m_libSymbols.end() )
5106 {
5107 m_errorMessages.emplace( wxString::Format( wxT( "Footprint's owner (%d) not found." ),
5108 implementationOwnerIt->second ),
5110 return;
5111 }
5112
5113 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
5114
5115 libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it
5116
5117 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
5118
5119 symbol->SetFootprintFieldText( fpLibId.Format() );
5120}
5121
5122
5123
5124std::vector<LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibComponent( const std::map<wxString,
5125 wxString>& aProperties )
5126{
5127 ASCH_SYMBOL elem( aProperties );
5128
5129 LIB_SYMBOL* symbol = new LIB_SYMBOL( wxEmptyString );
5130 symbol->SetName( elem.libreference );
5131
5132 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symbol->GetName() );
5133 symbol->SetDescription( elem.componentdescription );
5134 symbol->SetLibId( libId );
5135
5136 // Altium PARTCOUNT is one more than the actual unit count. The property may be missing
5137 // (defaults to 0) or otherwise nonsensical, so clamp to a minimum of 1 unit.
5138 symbol->SetUnitCount( std::max( 1, elem.partcount - 1 ), true );
5139
5140 if( elem.displaymodecount > 1 )
5141 {
5142 std::vector<wxString> bodyStyleNames;
5143
5144 for( int i = 0; i < elem.displaymodecount; i++ )
5145 bodyStyleNames.push_back( wxString::Format( "Display %d", i + 1 ) );
5146
5147 symbol->SetBodyStyleNames( bodyStyleNames );
5148 }
5149
5150 return { symbol };
5151}
5152
5153
5156{
5158 std::vector<int> fontSizes;
5159 struct SYMBOL_PIN_FRAC
5160 {
5161 int x_frac;
5162 int y_frac;
5163 int len_frac;
5164 };
5165
5166 ParseLibHeader( aAltiumLibFile, fontSizes );
5167
5168 std::map<wxString, ALTIUM_SYMBOL_DATA> syms = aAltiumLibFile.GetLibSymbols( nullptr );
5169
5170 for( auto& [name, entry] : syms )
5171 {
5172 std::map<int, SYMBOL_PIN_FRAC> pinFracs;
5173
5174 if( entry.m_pinsFrac )
5175 {
5176 auto parse_binary_pin_frac =
5177 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
5178 {
5179 std::map<wxString, wxString> result;
5180 ALTIUM_COMPRESSED_READER cmpreader( binaryData );
5181
5182 std::pair<int, std::string*> pinFracData = cmpreader.ReadCompressedString();
5183
5184 ALTIUM_BINARY_READER binreader( *pinFracData.second );
5185 SYMBOL_PIN_FRAC pinFrac;
5186
5187 pinFrac.x_frac = binreader.ReadInt32();
5188 pinFrac.y_frac = binreader.ReadInt32();
5189 pinFrac.len_frac = binreader.ReadInt32();
5190 pinFracs.insert( { pinFracData.first, pinFrac } );
5191
5192 return result;
5193 };
5194
5195 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_pinsFrac );
5196
5197 while( reader.GetRemainingBytes() > 0 )
5198 reader.ReadProperties( parse_binary_pin_frac );
5199 }
5200
5201 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_symbol );
5202 std::vector<LIB_SYMBOL*> symbols;
5203 int pin_index = 0;
5204
5205 if( reader.GetRemainingBytes() <= 0 )
5206 THROW_IO_ERROR( "LibSymbol does not contain any data" );
5207
5208 {
5209 std::map<wxString, wxString> properties = reader.ReadProperties();
5210 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
5211 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
5212
5213 if( record != ALTIUM_SCH_RECORD::COMPONENT )
5214 THROW_IO_ERROR( "LibSymbol does not start with COMPONENT record" );
5215
5216 symbols = ParseLibComponent( properties );
5217 }
5218
5219 auto handleBinaryPinLambda =
5220 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
5221 {
5222 std::map<wxString, wxString> result;
5223
5224 ALTIUM_BINARY_READER binreader( binaryData );
5225
5226 int32_t recordId = binreader.ReadInt32();
5227
5228 if( recordId != static_cast<int32_t>( ALTIUM_SCH_RECORD::PIN ) )
5229 THROW_IO_ERROR( "Binary record missing PIN record" );
5230
5231 result["RECORD"] = wxString::Format( "%d", recordId );
5232 binreader.ReadByte(); // unknown
5233 result["OWNERPARTID"] = wxString::Format( "%d", binreader.ReadInt16() );
5234 result["OWNERPARTDISPLAYMODE"] = wxString::Format( "%d", binreader.ReadByte() );
5235 result["SYMBOL_INNEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
5236 result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
5237 result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() );
5238 result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() );
5239 result["TEXT"] = binreader.ReadShortPascalString();
5240 binreader.ReadByte(); // unknown
5241 result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() );
5242 result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() );
5243 result["PINLENGTH"] = wxString::Format( "%d", binreader.ReadInt16() );
5244 result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() );
5245 result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() );
5246 result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() );
5247 result["NAME"] = binreader.ReadShortPascalString();
5248 result["DESIGNATOR"] = binreader.ReadShortPascalString();
5249 result["SWAPIDGROUP"] = binreader.ReadShortPascalString();
5250
5251 if( auto it = pinFracs.find( pin_index ); it != pinFracs.end() )
5252 {
5253 result["LOCATION.X_FRAC"] = wxString::Format( "%d", it->second.x_frac );
5254 result["LOCATION.Y_FRAC"] = wxString::Format( "%d", it->second.y_frac );
5255 result["PINLENGTH_FRAC"] = wxString::Format( "%d", it->second.len_frac );
5256 }
5257
5258 std::string partSeq = binreader.ReadShortPascalString(); // This is 'part|&|seq'
5259 std::vector<std::string> partSeqSplit = split( partSeq, "|" );
5260
5261 if( partSeqSplit.size() == 3 )
5262 {
5263 result["PART"] = partSeqSplit[0];
5264 result["SEQ"] = partSeqSplit[2];
5265 }
5266
5267 return result;
5268 };
5269
5270 while( reader.GetRemainingBytes() > 0 )
5271 {
5272 std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryPinLambda );
5273
5274 if( properties.empty() )
5275 continue;
5276
5277 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
5278 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
5279
5280 switch( record )
5281 {
5283 ParsePin( properties, symbols );
5284 pin_index++;
5285 break;
5286
5287 case ALTIUM_SCH_RECORD::LABEL: ParseLabel( properties, symbols, fontSizes ); break;
5288 case ALTIUM_SCH_RECORD::BEZIER: ParseBezier( properties, symbols ); break;
5289 case ALTIUM_SCH_RECORD::POLYLINE: ParsePolyline( properties, symbols ); break;
5290 case ALTIUM_SCH_RECORD::POLYGON: ParsePolygon( properties, symbols ); break;
5291 case ALTIUM_SCH_RECORD::ELLIPSE: ParseEllipse( properties, symbols ); break;
5292 case ALTIUM_SCH_RECORD::PIECHART: ParsePieChart( properties, symbols ); break;
5293 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: ParseRoundRectangle( properties, symbols ); break;
5294 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: ParseEllipticalArc( properties, symbols ); break;
5295 case ALTIUM_SCH_RECORD::ARC: ParseArc( properties, symbols ); break;
5296 case ALTIUM_SCH_RECORD::LINE: ParseLine( properties, symbols ); break;
5297 case ALTIUM_SCH_RECORD::RECTANGLE: ParseRectangle( properties, symbols ); break;
5298 case ALTIUM_SCH_RECORD::DESIGNATOR: ParseLibDesignator( properties, symbols, fontSizes ); break;
5299 case ALTIUM_SCH_RECORD::PARAMETER: ParseLibParameter( properties, symbols, fontSizes ); break;
5300 case ALTIUM_SCH_RECORD::TEXT_FRAME: ParseTextFrame( properties, symbols, fontSizes ); break;
5301 case ALTIUM_SCH_RECORD::IMPLEMENTATION: ParseImplementation( properties, symbols ); break;
5302
5304 break;
5305
5308 break;
5309
5311 // TODO: add support for these. They are just drawn symbols, so we can probably hardcode
5312 break;
5313
5315 // TODO: Handle images once libedit supports them
5316 break;
5317
5319 // Nothing for now. TODO: Figure out how implementation lists are generated in libs
5320 break;
5321
5322 default:
5323 m_errorMessages.emplace( wxString::Format( _( "Unknown or unexpected record id %d found in %s." ),
5324 recordId,
5325 symbols[0]->GetName() ),
5327 break;
5328 }
5329 }
5330
5331 if( reader.HasParsingError() )
5332 THROW_IO_ERROR( "stream was not parsed correctly!" );
5333
5334 if( reader.GetRemainingBytes() != 0 )
5335 THROW_IO_ERROR( "stream is not fully parsed" );
5336
5337 LIB_SYMBOL* symbol = symbols[0];
5338 symbol->FixupDrawItems();
5339 fixupSymbolPinNameNumbers( symbol );
5340
5341 SCH_FIELD& valField = symbol->GetValueField();
5342
5343 if( valField.GetText().IsEmpty() )
5344 valField.SetText( name );
5345
5346 symbol->SetName( name );
5347 ret[name] = symbol;
5348 }
5349
5350 return ret;
5351}
5352
5353
5354long long SCH_IO_ALTIUM::getLibraryTimestamp( const wxString& aLibraryPath ) const
5355{
5356 wxFileName fn( aLibraryPath );
5357
5358 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
5359 return fn.GetModificationTime().GetValue().GetValue();
5360 else
5361 return 0;
5362}
5363
5364
5365void SCH_IO_ALTIUM::ensureLoadedLibrary( const wxString& aLibraryPath,
5366 const std::map<std::string, UTF8>* aProperties )
5367{
5368 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
5369 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
5370
5371 if( m_libCache.count( aLibraryPath ) )
5372 {
5373 wxCHECK( m_timestamps.count( aLibraryPath ), /*void*/ );
5374
5375 if( m_timestamps.at( aLibraryPath ) == getLibraryTimestamp( aLibraryPath ) )
5376 return;
5377 }
5378
5379 std::vector<std::unique_ptr<ALTIUM_COMPOUND_FILE>> compoundFiles;
5380
5381 wxFileName fileName( aLibraryPath );
5382 m_libName = fileName.GetName();
5383
5384 try
5385 {
5386 if( aLibraryPath.Lower().EndsWith( wxS( ".schlib" ) ) )
5387 {
5388 m_isIntLib = false;
5389
5390 compoundFiles.push_back( std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
5391 }
5392 else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) )
5393 {
5394 m_isIntLib = true;
5395
5396 std::unique_ptr<ALTIUM_COMPOUND_FILE> intCom = std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath );
5397
5398 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> schLibFiles = intCom->EnumDir( L"SchLib" );
5399
5400 for( const auto& [schLibName, cfe] : schLibFiles )
5401 {
5402 std::unique_ptr<ALTIUM_COMPOUND_FILE> decodedStream = std::make_unique<ALTIUM_COMPOUND_FILE>();
5403
5404 if( intCom->DecodeIntLibStream( *cfe, decodedStream.get() ) )
5405 compoundFiles.emplace_back( std::move( decodedStream ) );
5406 }
5407 }
5408
5409 CASE_INSENSITIVE_MAP<LIB_SYMBOL*>& cacheMapRef = m_libCache[aLibraryPath];
5410
5411 for( const std::unique_ptr<ALTIUM_COMPOUND_FILE>& altiumSchFilePtr : compoundFiles )
5412 {
5413 CASE_INSENSITIVE_MAP<LIB_SYMBOL*> parsed = ParseLibFile( *altiumSchFilePtr );
5414 cacheMapRef.insert( parsed.begin(), parsed.end() );
5415 }
5416
5417 m_timestamps[aLibraryPath] = getLibraryTimestamp( aLibraryPath );
5418 }
5419 catch( const CFB::CFBException& exception )
5420 {
5421 THROW_IO_ERROR( exception.what() );
5422 }
5423 catch( const std::exception& exc )
5424 {
5425 THROW_IO_ERROR( wxString::Format( _( "Error parsing Altium library: %s" ), exc.what() ) );
5426 }
5427}
5428
5429
5431 std::vector<int>& aFontSizes )
5432{
5433 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "FileHeader" } );
5434
5435 if( file == nullptr )
5436 THROW_IO_ERROR( "FileHeader not found" );
5437
5438 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
5439
5440 if( reader.GetRemainingBytes() <= 0 )
5441 THROW_IO_ERROR( "FileHeader does not contain any data" );
5442
5443 std::map<wxString, wxString> properties = reader.ReadProperties();
5444
5445 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
5446
5447 if( libtype.CmpNoCase( "Protel for Windows - Schematic Library Editor Binary File Version 5.0" ) )
5448 THROW_IO_ERROR( _( "Expected Altium Schematic Library file version 5.0" ) );
5449
5450 for( auto& [key, value] : properties )
5451 {
5452 wxString upperKey = key.Upper();
5453 wxString remaining;
5454
5455 if( upperKey.StartsWith( "SIZE", &remaining ) )
5456 {
5457 if( !remaining.empty() )
5458 {
5459 int ind = wxAtoi( remaining );
5460
5461 if( static_cast<int>( aFontSizes.size() ) < ind )
5462 aFontSizes.resize( ind );
5463
5464 // Altium stores in pt. 1 pt = 1/72 inch. 1 mil = 1/1000 inch.
5465 int scaled = schIUScale.MilsToIU( wxAtoi( value ) * 72.0 / 10.0 );
5466 aFontSizes[ind - 1] = scaled;
5467 }
5468 }
5469 }
5470}
5471
5472
5473void SCH_IO_ALTIUM::doEnumerateSymbolLib( const wxString& aLibraryPath,
5474 const std::map<std::string, UTF8>* aProperties,
5475 std::function<void(const wxString&, LIB_SYMBOL*)> aInserter )
5476{
5477 ensureLoadedLibrary( aLibraryPath, aProperties );
5478
5479 bool powerSymbolsOnly = ( aProperties &&
5480 aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly ) );
5481
5482 auto it = m_libCache.find( aLibraryPath );
5483
5484 if( it != m_libCache.end() )
5485 {
5486 for( auto& [libnameStr, libSymbol] : it->second )
5487 {
5488 if( powerSymbolsOnly && !libSymbol->IsPower() )
5489 continue;
5490
5491 aInserter( libnameStr, libSymbol );
5492 }
5493 }
5494}
5495
5496
5497void SCH_IO_ALTIUM::EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
5498 const std::map<std::string, UTF8>* aProperties )
5499{
5500 doEnumerateSymbolLib( aLibraryPath, aProperties,
5501 [&]( const wxString& aStr, LIB_SYMBOL* )
5502 {
5503 aSymbolNameList.Add( aStr );
5504 } );
5505}
5506
5507
5508void SCH_IO_ALTIUM::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
5509 const wxString& aLibraryPath,
5510 const std::map<std::string, UTF8>* aProperties )
5511{
5512 doEnumerateSymbolLib( aLibraryPath, aProperties,
5513 [&]( const wxString&, LIB_SYMBOL* aSymbol )
5514 {
5515 aSymbolList.emplace_back( aSymbol );
5516 } );
5517}
5518
5519
5520LIB_SYMBOL* SCH_IO_ALTIUM::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
5521 const std::map<std::string, UTF8>* aProperties )
5522{
5523 ensureLoadedLibrary( aLibraryPath, aProperties );
5524
5525 auto it = m_libCache.find( aLibraryPath );
5526
5527 if( it != m_libCache.end() )
5528 {
5529 auto it2 = it->second.find( aAliasName );
5530
5531 if( it2 != it->second.end() )
5532 return it2->second;
5533 }
5534
5535 return nullptr;
5536}
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:149
void SetModified()
Definition eda_item.cpp:110
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:274
void SetCenter(const VECTOR2I &aCenter)
FILL_T GetFillMode() const
Definition eda_shape.h:158
void SetLineStyle(const LINE_STYLE aStyle)
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:170
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:194
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
COLOR4D GetFillColor() const
Definition eda_shape.h:169
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:236
void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:271
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:532
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:576
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
This class was created to handle importing ellipses from other file formats that support them nativel...
Definition ellipse.h:34
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:788
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:332
int GetBodyStyleCount() const override
Definition lib_symbol.h:776
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:336
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:126
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
Definition sch_field.h:217
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:168
virtual void SetBodyStyle(int aBodyStyle)
Definition sch_item.h:247
virtual void SetUnit(int aUnit)
Definition sch_item.h:238
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:857
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Definition sch_line.cpp:757
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:274
void SetLineColor(const COLOR4D &aColor)
Definition sch_line.cpp:321
void SetLineWidth(const int aSize)
Definition sch_line.cpp:387
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:345
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:76
void SetLibId(const LIB_ID &aName)
const std::vector< SCH_SYMBOL_INSTANCE > & GetInstances() const
Definition sch_symbol.h:135
void SetPosition(const VECTOR2I &aPosition) override
Definition sch_symbol.h:872
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:871
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:49
FILL_T
Definition eda_shape.h:59
@ FILLED_WITH_COLOR
Definition eda_shape.h:63
@ NO_FILL
Definition eda_shape.h:60
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:62
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
static const std::string KiCadSchematicFileExtension
static const std::string KiCadSymbolLibFileExtension
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
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.