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