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_connection.h>
43#include <sch_symbol.h>
44#include <sch_junction.h>
45#include <sch_line.h>
46#include <sch_shape.h>
47#include <sch_no_connect.h>
48#include <sch_screen.h>
49#include <sch_label.h>
50#include <sch_sheet.h>
51#include <sch_sheet_pin.h>
52#include <sch_textbox.h>
53
54#include <bezier_curves.h>
55#include <compoundfilereader.h>
56#include <font/fontconfig.h>
57#include <reporter.h>
58#include <geometry/ellipse.h>
60#include <string_utils.h>
61#include <sch_edit_frame.h>
63#include <wx/log.h>
64#include <wx/dir.h>
65#include <wx/mstream.h>
66#include <wx/zstream.h>
67#include <wx/wfstream.h>
68#include <magic_enum.hpp>
69#include "sch_io_altium.h"
70
71
72// Harness port object itself does not contain color information about itself
73// It seems altium is drawing harness ports using these colors
74#define HARNESS_PORT_COLOR_DEFAULT_BACKGROUND COLOR4D( 0.92941176470588238, \
75 0.94901960784313721, \
76 0.98431372549019602, 1.0 )
77
78#define HARNESS_PORT_COLOR_DEFAULT_OUTLINE COLOR4D( 0.56078431372549020, \
79 0.61960784313725492, \
80 0.78823529411764703, 1.0 )
81
82
83// Pure label/name transforms shared with the QA suite; see AltiumWrapBusLabel and
84// AltiumDeriveSheetName below.
85wxString AltiumWrapBusLabel( const wxString& aText );
86wxString AltiumDeriveSheetName( const wxString& aFilename, const std::set<wxString>& aExistingNames );
87
88
89static const VECTOR2I GetRelativePosition( const VECTOR2I& aPosition, const SCH_SYMBOL* aSymbol )
90{
92 return t.TransformCoordinate( aPosition - aSymbol->GetPosition() );
93}
94
95
96static COLOR4D GetColorFromInt( int color )
97{
98 int red = color & 0x0000FF;
99 int green = ( color & 0x00FF00 ) >> 8;
100 int blue = ( color & 0xFF0000 ) >> 16;
101
102 return COLOR4D().FromCSSRGBA( red, green, blue, 1.0 );
103}
104
105
117
118
119static void SetSchShapeLine( const ASCH_BORDER_INTERFACE& elem, SCH_SHAPE* shape )
120{
122 GetColorFromInt( elem.Color ) ) );
123}
124
125static void SetSchShapeFillAndColor( const ASCH_FILL_INTERFACE& elem, SCH_SHAPE* shape )
126{
127
128 if( !elem.IsSolid )
129 {
131 }
132 else
133 {
135 shape->SetFillColor( GetColorFromInt( elem.AreaColor ) );
136 }
137
138 // Fixup small circles that had their widths set to 0
139 if( shape->GetShape() == SHAPE_T::CIRCLE && shape->GetStroke().GetWidth() == 0
140 && shape->GetRadius() <= schIUScale.MilsToIU( 10 ) )
141 {
143 }
144}
145
146
147static void SetLibShapeLine( const ASCH_BORDER_INTERFACE& elem, SCH_SHAPE* shape,
148 ALTIUM_SCH_RECORD aType )
149{
150 COLOR4D default_color;
151 COLOR4D alt_default_color = COLOR4D( PUREBLUE ); // PUREBLUE is used for many objects, so if
152 // it is used, we will assume that it should
153 // blend with the others
154 STROKE_PARAMS stroke;
155 stroke.SetColor( GetColorFromInt( elem.Color ) );
157
158 switch( aType )
159 {
160 case ALTIUM_SCH_RECORD::ARC: default_color = COLOR4D( PUREBLUE ); break;
161 case ALTIUM_SCH_RECORD::BEZIER: default_color = COLOR4D( PURERED ); break;
162 case ALTIUM_SCH_RECORD::ELLIPSE: default_color = COLOR4D( PUREBLUE ); break;
163 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: default_color = COLOR4D( PUREBLUE ); break;
164 case ALTIUM_SCH_RECORD::LINE: default_color = COLOR4D( PUREBLUE ); break;
165 case ALTIUM_SCH_RECORD::POLYGON: default_color = COLOR4D( PUREBLUE ); break;
166 case ALTIUM_SCH_RECORD::POLYLINE: default_color = COLOR4D( BLACK ); break;
167 case ALTIUM_SCH_RECORD::RECTANGLE: default_color = COLOR4D( 0.5, 0, 0, 1.0 ); break;
168 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: default_color = COLOR4D( PUREBLUE ); break;
169 default: default_color = COLOR4D( PUREBLUE ); break;
170 }
171
172 if( stroke.GetColor() == default_color || stroke.GetColor() == alt_default_color )
174
175 // In Altium libraries, you cannot change the width of the pins. So, to match pin width,
176 // if the line width of other elements is the default pin width (10 mil), we set the width
177 // to the KiCad default pin width ( represented by 0 )
178 if( elem.LineWidth == 2540 )
179 stroke.SetWidth( 0 );
180 else
181 stroke.SetWidth( elem.LineWidth );
182
183 shape->SetStroke( stroke );
184}
185
187 ALTIUM_SCH_RECORD aType, int aStrokeColor )
188{
189 COLOR4D bgcolor = GetColorFromInt( elem.AreaColor );
190 COLOR4D default_bgcolor;
191
192 switch (aType)
193 {
195 default_bgcolor = GetColorFromInt( 11599871 ); // Light Yellow
196 break;
197 default:
198 default_bgcolor = GetColorFromInt( 12632256 ); // Grey
199 break;
200 }
201
202 if( elem.IsTransparent )
203 bgcolor = bgcolor.WithAlpha( 0.5 );
204
205 if( !elem.IsSolid )
206 {
208 }
209 else if( elem.AreaColor == aStrokeColor )
210 {
211 bgcolor = shape->GetStroke().GetColor();
212
214 }
215 else if( bgcolor.WithAlpha( 1.0 ) == default_bgcolor )
216 {
218 }
219 else
220 {
222 }
223
224 shape->SetFillColor( bgcolor );
225
226 if( elem.AreaColor == aStrokeColor
227 && shape->GetStroke().GetWidth() == schIUScale.MilsToIU( 1 ) )
228 {
229 STROKE_PARAMS stroke = shape->GetStroke();
230 stroke.SetWidth( -1 );
231 shape->SetStroke( stroke );
232 }
233
234 // Fixup small circles that had their widths set to 0
235 if( shape->GetShape() == SHAPE_T::CIRCLE && shape->GetStroke().GetWidth() == 0
236 && shape->GetRadius() <= schIUScale.MilsToIU( 10 ) )
237 {
239 }
240}
241
242
244 SCH_IO( wxS( "Altium" ) )
245{
246 m_isIntLib = false;
247 m_rootSheet = nullptr;
248 m_schematic = nullptr;
251
253}
254
255
257{
258 for( auto& [libName, lib] : m_libCache )
259 {
260 for( auto& [name, symbol] : lib )
261 delete symbol;
262 }
263}
264
265
267{
268 return 0;
269}
270
271
272bool SCH_IO_ALTIUM::isBinaryFile( const wxString& aFileName )
273{
274 // Compound File Binary Format header
276}
277
278
279bool SCH_IO_ALTIUM::isASCIIFile( const wxString& aFileName )
280{
281 // ASCII file format
282 return IO_UTILS::fileStartsWithPrefix( aFileName, wxS( "|HEADER=" ), false );
283}
284
285
286bool SCH_IO_ALTIUM::checkFileHeader( const wxString& aFileName )
287{
288 return isBinaryFile( aFileName ) || isASCIIFile( aFileName );
289}
290
291
292bool SCH_IO_ALTIUM::CanReadSchematicFile( const wxString& aFileName ) const
293{
294 if( !SCH_IO::CanReadSchematicFile( aFileName ) )
295 return false;
296
297 return checkFileHeader( aFileName );
298}
299
300
301bool SCH_IO_ALTIUM::CanReadLibrary( const wxString& aFileName ) const
302{
303 if( !SCH_IO::CanReadLibrary( aFileName ) )
304 return false;
305
306 return checkFileHeader( aFileName );
307}
308
309
311{
312 std::vector<SCH_PIN*> pins;
313
314 if( aSymbol->Type() == SCH_SYMBOL_T )
315 pins = static_cast<SCH_SYMBOL*>( aSymbol )->GetPins( nullptr );
316 else if( aSymbol->Type() == LIB_SYMBOL_T )
317 pins = static_cast<LIB_SYMBOL*>( aSymbol )->GetGraphicalPins( 0, 0 );
318
319
320 bool names_visible = false;
321 bool numbers_visible = false;
322
323 for( SCH_PIN* pin : pins )
324 {
325 if( pin->GetNameTextSize() > 0 && !pin->GetName().empty() )
326 names_visible = true;
327
328 if( pin->GetNumberTextSize() > 0 && !pin->GetNumber().empty() )
329 numbers_visible = true;
330 }
331
332 if( !names_visible )
333 {
334 for( SCH_PIN* pin : pins )
335 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
336
337 aSymbol->SetShowPinNames( false );
338 }
339
340 if( !numbers_visible )
341 {
342 for( SCH_PIN* pin : pins )
343 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
344
345 aSymbol->SetShowPinNumbers( false );
346 }
347}
348
349
351{
352 if( m_libName.IsEmpty() )
353 {
354 // Try to come up with a meaningful name
355 m_libName = m_schematic->Project().GetProjectName();
356
357 if( m_libName.IsEmpty() )
358 {
359 wxFileName fn( m_rootSheet->GetFileName() );
360 m_libName = fn.GetName();
361 }
362
363 if( m_libName.IsEmpty() )
364 m_libName = "noname";
365
366 m_libName += "-altium-import";
368 }
369
370 return m_libName;
371}
372
373
375{
376 wxFileName fn( m_schematic->Project().GetProjectPath(), getLibName(),
378
379 return fn;
380}
381
382
383SCH_SHEET* SCH_IO_ALTIUM::LoadSchematicProject( SCHEMATIC* aSchematic, const std::map<std::string, UTF8>* aProperties )
384{
385 int x = 1;
386 int y = 1;
387 int page = 2; // Start at page 2 since page 1 is the root sheet.
388
389 std::map<wxString, SCH_SHEET*> sheets;
390 wxFileName project( aProperties->at( "project_file" ) );
391
392 for( auto& [ key, filestring] : *aProperties )
393 {
394 if( !key.starts_with( "sch" ) )
395 continue;
396
397 wxFileName fn( filestring );
398
399 // Check if this file was already loaded as a subsheet of another sheet.
400 // This can happen when the project file lists sheets in an order where a parent
401 // sheet is processed before its subsheets. We need to handle potential case
402 // differences in filenames (e.g., LVDS.SCHDOC vs LVDS.SchDoc).
403 SCH_SCREEN* existingScreen = nullptr;
404 m_rootSheet->SearchHierarchy( fn.GetFullPath(), &existingScreen );
405
406 // If not found, try case-insensitive search by checking all loaded screens.
407 // Compare base names only (without extension) since Altium uses .SchDoc/.SCHDOC
408 // while KiCad uses .kicad_sch
409 if( !existingScreen )
410 {
411 SCH_SCREENS allScreens( m_rootSheet );
412
413 for( SCH_SCREEN* screen = allScreens.GetFirst(); screen; screen = allScreens.GetNext() )
414 {
415 wxFileName screenFn( screen->GetFileName() );
416 wxFileName checkFn( fn.GetFullPath() );
417
418 if( screenFn.GetName().IsSameAs( checkFn.GetName(), false ) )
419 {
420 existingScreen = screen;
421 break;
422 }
423 }
424 }
425
426 if( existingScreen )
427 continue;
428
429 VECTOR2I pos = VECTOR2I( x * schIUScale.MilsToIU( 1000 ),
430 y * schIUScale.MilsToIU( 1000 ) );
431
432 wxFileName kicad_fn( fn );
433 std::unique_ptr<SCH_SHEET> sheet = std::make_unique<SCH_SHEET>( m_rootSheet, pos );
434 SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
435 sheet->SetScreen( screen );
436
437 // Convert to KiCad project-relative path with .kicad_sch extension
438 kicad_fn.SetExt( FILEEXT::KiCadSchematicFileExtension );
439 kicad_fn.SetPath( aSchematic->Project().GetProjectPath() );
440
441 // Sheet uses relative filename, screen uses full path
442 sheet->SetFileName( kicad_fn.GetFullName() );
443 screen->SetFileName( kicad_fn.GetFullPath() );
444
445 wxCHECK2( sheet && screen, continue );
446
447 wxString pageNo = wxString::Format( wxT( "%d" ), page++ );
448
449 m_sheetPath.push_back( sheet.get() );
450
451 // Parse from the original Altium file location
452 ParseAltiumSch( fn.GetFullPath() );
453
454 // Sheets created here won't have names set by ParseSheetName (which only applies
455 // to sheet symbols within a parent). Derive a name from the Altium filename.
456 if( sheet->GetName().Trim().empty() )
457 {
458 std::set<wxString> existingNames;
459
460 for( auto& [path, existing] : sheets )
461 existingNames.insert( existing->GetName() );
462
463 sheet->SetName( AltiumDeriveSheetName( fn.GetFullPath(), existingNames ) );
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
1023wxString AltiumWrapBusLabel( const wxString& aText )
1024{
1025 // Altium marks bus membership by geometry alone (a scalar label placed on a bus line);
1026 // KiCad needs the same label expressed as a single-member bus group to make the
1027 // connection. Already-formatted bus labels are left untouched.
1028 if( SCH_CONNECTION::IsBusLabel( aText ) )
1029 return aText;
1030
1031 // Spaces and commas separate members inside a bus group, so a multi-word net name would
1032 // otherwise fan out into several members. Quote such names so the bus-group reader in
1033 // NET_SETTINGS::ParseBusGroup keeps the whole name as a single member.
1034 if( aText.Contains( wxT( " " ) ) || aText.Contains( wxT( "," ) ) )
1035 return wxT( "{\"" ) + aText + wxT( "\"}" );
1036
1037 return wxT( "{" ) + aText + wxT( "}" );
1038}
1039
1040
1041wxString AltiumDeriveSheetName( const wxString& aFilename, const std::set<wxString>& aExistingNames )
1042{
1043 wxString baseName = wxFileName( aFilename ).GetName();
1044 baseName.Replace( wxT( "/" ), wxT( "_" ) );
1045
1046 if( baseName.Trim().empty() )
1047 baseName = wxT( "Sheet" );
1048
1049 wxString sheetName = baseName;
1050
1051 for( int ii = 1; aExistingNames.count( sheetName ); ++ii )
1052 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
1053
1054 return sheetName;
1055}
1056
1057
1059{
1060 SCH_SCREEN* screen = getCurrentScreen();
1061 wxCHECK( screen, /* void */ );
1062
1063 bool hasBusLines = false;
1064
1065 for( SCH_ITEM* item : screen->Items().OfType( SCH_LINE_T ) )
1066 {
1067 SCH_LINE* line = static_cast<SCH_LINE*>( item );
1068
1069 if( line->IsBus() )
1070 {
1071 hasBusLines = true;
1072 break;
1073 }
1074 }
1075
1076 if( !hasBusLines )
1077 return;
1078
1079 // Collect labels that need wrapping, then modify them after iteration to avoid
1080 // modifying the R-tree during traversal.
1081 std::vector<SCH_LABEL*> labelsToWrap;
1082
1083 for( SCH_ITEM* item : screen->Items().OfType( SCH_LABEL_T ) )
1084 {
1085 SCH_LABEL* label = static_cast<SCH_LABEL*>( item );
1086
1087 if( SCH_CONNECTION::IsBusLabel( label->GetText() ) )
1088 continue;
1089
1090 for( SCH_ITEM* busItem : screen->Items().Overlapping( SCH_LINE_T, label->GetBoundingBox() ) )
1091 {
1092 SCH_LINE* busLine = static_cast<SCH_LINE*>( busItem );
1093
1094 if( busLine->IsBus() && busLine->HitTest( label->GetPosition() ) )
1095 {
1096 labelsToWrap.push_back( label );
1097 break;
1098 }
1099 }
1100 }
1101
1102 for( SCH_LABEL* label : labelsToWrap )
1103 label->SetText( AltiumWrapBusLabel( label->GetText() ) );
1104}
1105
1106
1108{
1109 SCH_SCREEN* screen = getCurrentScreen();
1110 wxCHECK( screen, /* void */ );
1111
1112 std::set<wxString> existingNames;
1113
1114 for( SCH_ITEM* item : screen->Items().OfType( SCH_SHEET_T ) )
1115 {
1116 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
1117
1118 if( !sheet->GetName().Trim().empty() )
1119 existingNames.insert( sheet->GetName() );
1120 }
1121
1122 for( auto& [idx, sheet] : m_sheets )
1123 {
1124 if( !sheet->GetName().Trim().empty() )
1125 continue;
1126
1127 SCH_FIELD* filenameField = sheet->GetField( FIELD_T::SHEET_FILENAME );
1128 wxString filename = filenameField ? filenameField->GetText() : wxString();
1129 wxString sheetName = AltiumDeriveSheetName( filename, existingNames );
1130
1131 sheet->SetName( sheetName );
1132 existingNames.insert( sheetName );
1133 }
1134}
1135
1136
1137void SCH_IO_ALTIUM::ParseAltiumSch( const wxString& aFileName )
1138{
1139 // Load path may be different from the project path.
1140 wxFileName parentFileName( aFileName );
1141
1142 if( m_rootFilepath.IsEmpty() )
1143 m_rootFilepath = parentFileName.GetPath();
1144
1145 if( m_progressReporter )
1146 {
1147 wxFileName relative = parentFileName;
1148 relative.MakeRelativeTo( m_rootFilepath );
1149
1150 m_progressReporter->Report( wxString::Format( _( "Importing %s" ), relative.GetFullPath() ) );
1151
1152 if( !m_progressReporter->KeepRefreshing() )
1153 THROW_IO_ERROR( _( "File import canceled by user." ) );
1154 }
1155
1156 if( isBinaryFile( aFileName ) )
1157 {
1158 ALTIUM_COMPOUND_FILE altiumSchFile( aFileName );
1159
1160 try
1161 {
1162 ParseStorage( altiumSchFile ); // we need this before parsing the FileHeader
1163 ParseFileHeader( altiumSchFile );
1164
1165 // Parse "Additional" because sheet is set up during "FileHeader" parsing.
1166 ParseAdditional( altiumSchFile );
1167 }
1168 catch( const CFB::CFBException& exception )
1169 {
1170 THROW_IO_ERROR( exception.what() );
1171 }
1172 catch( const std::exception& exc )
1173 {
1174 THROW_IO_ERROR( wxString::Format( _( "Error parsing Altium schematic: %s" ), exc.what() ) );
1175 }
1176 }
1177 else // ASCII
1178 {
1179 ParseASCIISchematic( aFileName );
1180 }
1181
1182 SCH_SCREEN* currentScreen = getCurrentScreen();
1183 wxCHECK( currentScreen, /* void */ );
1184
1185 // Descend the sheet hierarchy.
1186 for( SCH_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
1187 {
1188 SCH_SCREEN* loadedScreen = nullptr;
1189 SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( item );
1190
1191 wxCHECK2( sheet, continue );
1192
1193 // The assumption is that all of the Altium schematic files will be in the same
1194 // path as the parent sheet path.
1195 wxFileName loadAltiumFileName( parentFileName.GetPath(), sheet->GetFileName() );
1196
1197 if( !loadAltiumFileName.IsFileReadable() )
1198 {
1199 // Altium sheet symbols sometimes store filenames without the .SchDoc extension.
1200 // Try appending it before falling through to the directory search.
1201 if( !loadAltiumFileName.HasExt() )
1202 {
1203 wxFileName withExt( loadAltiumFileName );
1204 withExt.SetExt( wxT( "SchDoc" ) );
1205
1206 if( withExt.IsFileReadable() )
1207 loadAltiumFileName = withExt;
1208 }
1209 }
1210
1211 if( !loadAltiumFileName.IsFileReadable() )
1212 {
1213 // Try case-insensitive search, matching by base name so that extensionless
1214 // filenames from Altium sheet symbols can resolve to .SchDoc files on disk.
1215 wxFileName sheetFn( sheet->GetFileName() );
1216 bool extensionless = !sheetFn.HasExt();
1217
1218 wxArrayString files;
1219 wxDir::GetAllFiles( parentFileName.GetPath(), &files, wxEmptyString,
1220 wxDIR_FILES | wxDIR_HIDDEN );
1221
1222 for( const wxString& candidate : files )
1223 {
1224 wxFileName candidateFname( candidate );
1225
1226 if( candidateFname.GetFullName().IsSameAs( sheet->GetFileName(), false )
1227 || ( extensionless
1228 && !sheetFn.GetName().empty()
1229 && candidateFname.GetName().IsSameAs( sheetFn.GetName(), false )
1230 && candidateFname.GetExt().IsSameAs( wxT( "SchDoc" ), false ) ) )
1231 {
1232 loadAltiumFileName = candidateFname;
1233 break;
1234 }
1235 }
1236 }
1237
1238 if( loadAltiumFileName.GetFullName().IsEmpty() || !loadAltiumFileName.IsFileReadable() )
1239 {
1240 m_errorMessages.emplace( wxString::Format( _( "The file name for sheet %s is undefined, "
1241 "this is probably an Altium signal harness "
1242 "that got converted to a sheet." ),
1243 sheet->GetName() ),
1245 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
1246 continue;
1247 }
1248
1249 m_rootSheet->SearchHierarchy( loadAltiumFileName.GetFullPath(), &loadedScreen );
1250
1251 if( loadedScreen )
1252 {
1253 sheet->SetScreen( loadedScreen );
1254
1255 wxFileName projectFileName = loadAltiumFileName;
1256 projectFileName.SetPath( m_schematic->Project().GetProjectPath() );
1257 projectFileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
1258 sheet->SetFileName( projectFileName.GetFullName() );
1259
1260 // Set up symbol instance data for this new sheet path. When the same schematic
1261 // is reused in multiple hierarchical instances, each instance needs its own
1262 // symbol references with the sheet name as suffix to match Altium's behavior.
1263 m_sheetPath.push_back( sheet );
1264
1265 for( SCH_ITEM* symItem : loadedScreen->Items().OfType( SCH_SYMBOL_T ) )
1266 {
1267 SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( symItem );
1268
1269 // Get the base reference from existing instance data
1270 wxString baseRef;
1271
1272 if( !symbol->GetInstances().empty() )
1273 baseRef = symbol->GetInstances().front().m_Reference;
1274 else
1275 baseRef = symbol->GetField( FIELD_T::REFERENCE )->GetText();
1276
1277 // Skip power symbols and graphics
1278 if( baseRef.StartsWith( wxT( "#" ) ) )
1279 {
1281 continue;
1282 }
1283
1284 // Create new reference with sheet name suffix (e.g., P1 -> P1_Connector2)
1285 wxString newRef = baseRef + wxT( "_" ) + sheet->GetName();
1286 symbol->SetRef( &m_sheetPath, newRef );
1287 }
1288
1289 m_sheetPath.pop_back();
1290 // Do not need to load the sub-sheets - this has already been done.
1291 }
1292 else
1293 {
1294 sheet->SetScreen( new SCH_SCREEN( m_schematic ) );
1295 SCH_SCREEN* screen = sheet->GetScreen();
1296
1297 if( sheet->GetName().Trim().empty() )
1298 {
1299 wxString baseName = loadAltiumFileName.GetName();
1300 baseName.Replace( wxT( "/" ), wxT( "_" ) );
1301
1302 wxString sheetName = baseName;
1303 std::set<wxString> sheetNames;
1304
1305 for( EDA_ITEM* otherItem : currentScreen->Items().OfType( SCH_SHEET_T ) )
1306 {
1307 SCH_SHEET* otherSheet = static_cast<SCH_SHEET*>( otherItem );
1308 sheetNames.insert( otherSheet->GetName() );
1309 }
1310
1311 for( int ii = 1; ; ++ii )
1312 {
1313 if( sheetNames.find( sheetName ) == sheetNames.end() )
1314 break;
1315
1316 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
1317 }
1318
1319 sheet->SetName( sheetName );
1320 }
1321
1322 wxCHECK2( screen, continue );
1323
1324 m_sheetPath.push_back( sheet );
1325 m_sheets.clear();
1326 ParseAltiumSch( loadAltiumFileName.GetFullPath() );
1327
1328 // Map the loaded Altium file to the project file.
1329 wxFileName projectFileName = loadAltiumFileName;
1330 projectFileName.SetPath( m_schematic->Project().GetProjectPath() );
1331 projectFileName.SetExt( FILEEXT::KiCadSchematicFileExtension );
1332 sheet->SetFileName( projectFileName.GetFullName() );
1333 screen->SetFileName( projectFileName.GetFullPath() );
1334
1335 m_sheetPath.pop_back();
1336 }
1337 }
1338}
1339
1340
1342{
1343 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "Storage" } );
1344
1345 if( file == nullptr )
1346 return;
1347
1348 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
1349
1350 std::map<wxString, wxString> properties = reader.ReadProperties();
1351 ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
1352 int weight = ALTIUM_PROPS_UTILS::ReadInt( properties, "WEIGHT", 0 );
1353
1354 if( weight < 0 )
1355 THROW_IO_ERROR( "Storage weight is negative!" );
1356
1357 for( int i = 0; i < weight; i++ )
1358 m_altiumStorage.emplace_back( reader );
1359
1360 if( reader.HasParsingError() )
1361 THROW_IO_ERROR( "stream was not parsed correctly!" );
1362
1363 // TODO pointhi: is it possible to have multiple headers in one Storage file? Otherwise
1364 // throw IO Error.
1365 if( reader.GetRemainingBytes() != 0 )
1366 {
1367 m_errorMessages.emplace( wxString::Format( _( "Storage file not fully parsed (%d bytes remaining)." ),
1368 reader.GetRemainingBytes() ),
1370 }
1371}
1372
1373
1375{
1376 wxString streamName = wxS( "Additional" );
1377
1378 const CFB::COMPOUND_FILE_ENTRY* file =
1379 aAltiumSchFile.FindStream( { streamName.ToStdString() } );
1380
1381 if( file == nullptr )
1382 return;
1383
1384 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
1385
1386 if( reader.GetRemainingBytes() <= 0 )
1387 {
1388 THROW_IO_ERROR( "Additional section does not contain any data" );
1389 }
1390 else
1391 {
1392 std::map<wxString, wxString> properties = reader.ReadProperties();
1393
1394 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
1395 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
1396
1397 if( record != ALTIUM_SCH_RECORD::HEADER )
1398 THROW_IO_ERROR( "Header expected" );
1399 }
1400
1401 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
1402 {
1403 std::map<wxString, wxString> properties = reader.ReadProperties();
1404
1405 ParseRecord( index, properties, streamName );
1406 }
1407
1408 // Handle harness Ports
1409 for( const ASCH_PORT& port : m_altiumHarnessPortsCurrentSheet )
1410 ParseHarnessPort( port );
1411
1412 CreateAliases();
1413
1414 // Wrap any remaining net labels sitting on bus lines in curly braces
1416
1417 if( reader.HasParsingError() )
1418 THROW_IO_ERROR( "stream was not parsed correctly!" );
1419
1420 if( reader.GetRemainingBytes() != 0 )
1421 THROW_IO_ERROR( "stream is not fully parsed" );
1422
1423 m_altiumHarnesses.clear();
1425}
1426
1427
1429{
1430 wxString streamName = wxS( "FileHeader" );
1431
1432 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { streamName.ToStdString() } );
1433
1434 if( file == nullptr )
1435 THROW_IO_ERROR( "FileHeader not found" );
1436
1437 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
1438
1439 if( reader.GetRemainingBytes() <= 0 )
1440 {
1441 THROW_IO_ERROR( "FileHeader does not contain any data" );
1442 }
1443 else
1444 {
1445 std::map<wxString, wxString> properties = reader.ReadProperties();
1446
1447 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
1448
1449 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Binary File Version 5.0" ) )
1450 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
1451 }
1452
1453 // Prepare some local variables
1454 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
1455 wxCHECK( !m_currentTitleBlock, /* void */ );
1456
1457 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
1458
1459 // index is required to resolve OWNERINDEX
1460 for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
1461 {
1462 std::map<wxString, wxString> properties = reader.ReadProperties();
1463
1464 ParseRecord( index, properties, streamName );
1465 }
1466
1467 if( reader.HasParsingError() )
1468 THROW_IO_ERROR( "stream was not parsed correctly!" );
1469
1470 if( reader.GetRemainingBytes() != 0 )
1471 THROW_IO_ERROR( "stream is not fully parsed" );
1472
1473 // assign LIB_SYMBOL -> COMPONENT
1474 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
1475 {
1476 auto libSymbolIt = m_libSymbols.find( symbol.first );
1477
1478 if( libSymbolIt == m_libSymbols.end() )
1479 THROW_IO_ERROR( "every symbol should have a symbol attached" );
1480
1481 fixupSymbolPinNameNumbers( symbol.second );
1482 fixupSymbolPinNameNumbers( libSymbolIt->second );
1483
1484 symbol.second->SetLibSymbol( libSymbolIt->second );
1485 }
1486
1487 SCH_SCREEN* screen = getCurrentScreen();
1488 wxCHECK( screen, /* void */ );
1489
1490 // Handle title blocks
1492 m_currentTitleBlock.reset();
1493
1494 // Handle Ports
1495 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
1496 ParsePort( port );
1497
1498 // Bus labels are wrapped in ParseAdditional() after CreateAliases() has had a chance to
1499 // append harness suffixes; doing it here would prematurely mark labels as bus groups.
1500
1501 // Assign default names to any sheet symbols that didn't get a SHEET_NAME record
1503
1505 m_altiumComponents.clear();
1506 m_altiumTemplates.clear();
1508
1509 m_symbols.clear();
1510 m_libSymbols.clear();
1511
1512 // Otherwise we cannot save the imported sheet?
1513 SCH_SHEET* sheet = getCurrentSheet();
1514
1515 wxCHECK( sheet, /* void */ );
1516
1517 sheet->SetModified();
1518}
1519
1520
1521void SCH_IO_ALTIUM::ParseASCIISchematic( const wxString& aFileName )
1522{
1523 // Read storage content first
1524 {
1525 ALTIUM_ASCII_PARSER storageReader( aFileName );
1526
1527 while( storageReader.CanRead() )
1528 {
1529 std::map<wxString, wxString> properties = storageReader.ReadProperties();
1530
1531 // Binary data
1532 if( properties.find( wxS( "BINARY" ) ) != properties.end() )
1533 m_altiumStorage.emplace_back( properties );
1534 }
1535 }
1536
1537 // Read other data
1538 ALTIUM_ASCII_PARSER reader( aFileName );
1539
1540 if( !reader.CanRead() )
1541 {
1542 THROW_IO_ERROR( "FileHeader does not contain any data" );
1543 }
1544 else
1545 {
1546 std::map<wxString, wxString> properties = reader.ReadProperties();
1547
1548 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
1549
1550 if( libtype.CmpNoCase( "Protel for Windows - Schematic Capture Ascii File Version 5.0" ) )
1551 THROW_IO_ERROR( _( "Expected Altium Schematic file version 5.0" ) );
1552 }
1553
1554 // Prepare some local variables
1555 wxCHECK( m_altiumPortsCurrentSheet.empty(), /* void */ );
1556 wxCHECK( !m_currentTitleBlock, /* void */ );
1557
1558 m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
1559
1560 // index is required to resolve OWNERINDEX
1561 int index = 0;
1562
1563 while( reader.CanRead() )
1564 {
1565 std::map<wxString, wxString> properties = reader.ReadProperties();
1566
1567 // Reset index at headers
1568 if( properties.find( wxS( "HEADER" ) ) != properties.end() )
1569 {
1570 index = 0;
1571 continue;
1572 }
1573
1574 if( properties.find( wxS( "RECORD" ) ) != properties.end() )
1575 ParseRecord( index, properties, aFileName );
1576
1577 index++;
1578 }
1579
1580 if( reader.HasParsingError() )
1581 THROW_IO_ERROR( "stream was not parsed correctly!" );
1582
1583 if( reader.CanRead() )
1584 THROW_IO_ERROR( "stream is not fully parsed" );
1585
1586 // assign LIB_SYMBOL -> COMPONENT
1587 for( std::pair<const int, SCH_SYMBOL*>& symbol : m_symbols )
1588 {
1589 auto libSymbolIt = m_libSymbols.find( symbol.first );
1590
1591 if( libSymbolIt == m_libSymbols.end() )
1592 THROW_IO_ERROR( "every symbol should have a symbol attached" );
1593
1594 fixupSymbolPinNameNumbers( symbol.second );
1595 fixupSymbolPinNameNumbers( libSymbolIt->second );
1596
1597 symbol.second->SetLibSymbol( libSymbolIt->second );
1598 }
1599
1600 SCH_SCREEN* screen = getCurrentScreen();
1601 wxCHECK( screen, /* void */ );
1602
1603 // Handle title blocks
1605 m_currentTitleBlock.reset();
1606
1607 // Handle harness Ports
1608 for( const ASCH_PORT& port : m_altiumHarnessPortsCurrentSheet )
1609 ParseHarnessPort( port );
1610
1611 // Handle Ports
1612 for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
1613 ParsePort( port );
1614
1615 // Add the aliases used for harnesses
1616 CreateAliases();
1617
1618 // Wrap net labels sitting on bus lines in curly braces so KiCad recognizes them
1620
1621 // Assign default names to any sheet symbols that didn't get a SHEET_NAME record
1623
1624 m_altiumHarnesses.clear();
1626 m_altiumComponents.clear();
1627 m_altiumTemplates.clear();
1629
1630 m_symbols.clear();
1631 m_libSymbols.clear();
1632
1633 // Otherwise we cannot save the imported sheet?
1634 SCH_SHEET* sheet = getCurrentSheet();
1635
1636 wxCHECK( sheet, /* void */ );
1637
1638 sheet->SetModified();
1639}
1640
1641
1642void SCH_IO_ALTIUM::ParseRecord( int index, std::map<wxString, wxString>& properties,
1643 const wxString& aSectionName )
1644{
1645 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", -1 );
1646 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
1647
1648 // see: https://github.com/vadmium/python-altium/blob/master/format.md
1649 switch( record )
1650 {
1651 // FileHeader section
1652
1654 THROW_IO_ERROR( "Header already parsed" );
1655
1657 ParseComponent( index, properties );
1658 break;
1659
1661 ParsePin( properties );
1662 break;
1663
1665 m_errorMessages.emplace( _( "Record 'IEEE_SYMBOL' not handled." ), RPT_SEVERITY_INFO );
1666 break;
1667
1669 ParseLabel( properties );
1670 break;
1671
1673 ParseBezier( properties );
1674 break;
1675
1677 ParsePolyline( properties );
1678 break;
1679
1681 ParsePolygon( properties );
1682 break;
1683
1685 ParseEllipse( properties );
1686 break;
1687
1689 ParsePieChart( properties );
1690 break;
1691
1693 ParseRoundRectangle( properties );
1694 break;
1695
1698 ParseArc( properties );
1699 break;
1700
1702 ParseLine( properties );
1703 break;
1704
1706 ParseRectangle( properties );
1707 break;
1708
1710 ParseSheetSymbol( index, properties );
1711 break;
1712
1714 ParseSheetEntry( properties );
1715 break;
1716
1718 ParsePowerPort( properties );
1719 break;
1720
1722 // Ports are parsed after the sheet was parsed
1723 // This is required because we need all electrical connection points before placing.
1724 m_altiumPortsCurrentSheet.emplace_back( properties );
1725 break;
1726
1728 ParseNoERC( properties );
1729 break;
1730
1732 ParseNetLabel( properties );
1733 break;
1734
1736 ParseBus( properties );
1737 break;
1738
1740 ParseWire( properties );
1741 break;
1742
1744 ParseTextFrame( properties );
1745 break;
1746
1748 ParseJunction( properties );
1749 break;
1750
1752 ParseImage( properties );
1753 break;
1754
1756 ParseSheet( properties );
1757 break;
1758
1760 ParseSheetName( properties );
1761 break;
1762
1764 ParseFileName( properties );
1765 break;
1766
1768 ParseDesignator( properties );
1769 break;
1770
1772 ParseBusEntry( properties );
1773 break;
1774
1776 ParseTemplate( index, properties );
1777 break;
1778
1780 ParseParameter( properties );
1781 break;
1782
1784 m_errorMessages.emplace( _( "Parameter Set not currently supported." ), RPT_SEVERITY_ERROR );
1785 break;
1786
1788 ParseImplementationList( index, properties );
1789 break;
1790
1792 ParseImplementation( properties );
1793 break;
1794
1796 break;
1797
1799 break;
1800
1802 break;
1803
1805 ParseNote( properties );
1806 break;
1807
1809 m_errorMessages.emplace( _( "Compile mask not currently supported." ), RPT_SEVERITY_ERROR );
1810 break;
1811
1813 break;
1814
1815 // Additional section
1816
1818 ParseHarnessConnector( index, properties );
1819 break;
1820
1822 ParseHarnessEntry( properties );
1823 break;
1824
1826 ParseHarnessType( properties );
1827 break;
1828
1830 ParseSignalHarness( properties );
1831 break;
1832
1834 m_errorMessages.emplace( _( "Blanket not currently supported." ), RPT_SEVERITY_ERROR );
1835 break;
1836
1837 default:
1838 m_errorMessages.emplace(
1839 wxString::Format( _( "Unknown or unexpected record id %d found in %s." ), recordId,
1840 aSectionName ),
1842 break;
1843 }
1844
1846}
1847
1848
1849
1850const ASCH_STORAGE_FILE* SCH_IO_ALTIUM::GetFileFromStorage( const wxString& aFilename ) const
1851{
1852 const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
1853
1854 for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
1855 {
1856 if( file.filename.IsSameAs( aFilename ) )
1857 return &file;
1858
1859 if( file.filename.EndsWith( aFilename ) )
1860 nonExactMatch = &file;
1861 }
1862
1863 return nonExactMatch;
1864}
1865
1866
1867void SCH_IO_ALTIUM::ParseComponent( int aIndex, const std::map<wxString, wxString>& aProperties )
1868{
1869 SCH_SHEET* currentSheet = m_sheetPath.Last();
1870 wxCHECK( currentSheet, /* void */ );
1871
1872 wxString sheetName = currentSheet->GetName();
1873
1874 if( sheetName.IsEmpty() )
1875 sheetName = wxT( "root" );
1876
1877 ASCH_SYMBOL altiumSymbol( aProperties );
1878
1879 if( m_altiumComponents.count( aIndex ) )
1880 {
1881 const ASCH_SYMBOL& currentSymbol = m_altiumComponents.at( aIndex );
1882
1883 m_errorMessages.emplace( wxString::Format( _( "Symbol '%s' in sheet '%s' at index %d "
1884 "replaced with symbol \"%s\"." ),
1885 currentSymbol.libreference,
1886 sheetName,
1887 aIndex,
1888 altiumSymbol.libreference ),
1890 }
1891
1892 auto pair = m_altiumComponents.insert( { aIndex, altiumSymbol } );
1893 const ASCH_SYMBOL& elem = pair.first->second;
1894
1895 wxString name;
1896 LIB_ID libId;
1897
1898 if( !elem.sourcelibraryname.IsEmpty() && !elem.libreference.IsEmpty() )
1899 {
1900 // The part comes from an Altium library that project import registers in the symbol
1901 // library table, so address it by its real library id and let the placement transform
1902 // ride on the SCH_SYMBOL rather than baking it into a unique name. Altium stores Windows
1903 // paths, so split the source library name accordingly.
1904 name = elem.libreference;
1905 libId = AltiumToKiCadLibID( wxFileName( elem.sourcelibraryname, wxPATH_WIN ).GetName(),
1906 name );
1907 }
1908 else
1909 {
1910 // TODO: this is a hack until we correctly apply all transformations to every element
1911 name = wxString::Format( "%s_%d%s_%s_%s",
1912 sheetName,
1913 elem.orientation,
1914 elem.isMirrored ? "_mirrored" : "",
1915 elem.libreference,
1916 elem.sourcelibraryname );
1917
1918 libId = AltiumToKiCadLibID( getLibName(), name );
1919 }
1920
1921 LIB_SYMBOL* ksymbol = new LIB_SYMBOL( wxEmptyString );
1922 ksymbol->SetName( name );
1923 ksymbol->SetDescription( elem.componentdescription );
1924 ksymbol->SetLibId( libId );
1925
1926 // Altium PARTCOUNT is one more than the actual unit count. The property may be missing
1927 // (defaults to 0) or otherwise nonsensical, so clamp to a minimum of 1 unit.
1928 ksymbol->SetUnitCount( std::max( 1, elem.partcount - 1 ), true );
1929
1930 if( elem.displaymodecount > 1 )
1931 {
1932 std::vector<wxString> bodyStyleNames;
1933
1934 for( int i = 0; i < elem.displaymodecount; i++ )
1935 bodyStyleNames.push_back( wxString::Format( "Display %d", i + 1 ) );
1936
1937 ksymbol->SetBodyStyleNames( bodyStyleNames );
1938 }
1939
1940 m_libSymbols.insert( { aIndex, ksymbol } );
1941
1942 // each component has its own symbol for now
1943 SCH_SYMBOL* symbol = new SCH_SYMBOL();
1944
1945 symbol->SetPosition( elem.location + m_sheetOffset );
1946
1947 for( SCH_FIELD& field : symbol->GetFields() )
1948 field.SetVisible( false );
1949
1950 int orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
1951
1952 switch( elem.orientation )
1953 {
1954 case 0: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_90; break;
1955 case 1: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_180; break;
1956 case 2: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_270; break;
1957 case 3: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0; break;
1958 default: break;
1959 }
1960
1961 if( elem.isMirrored )
1963
1964 symbol->SetOrientation( orientation );
1965
1966 symbol->SetLibId( libId );
1967
1968 if( ksymbol->GetUnitCount() > 1 )
1969 symbol->SetUnit( std::max( 1, elem.currentpartid ) );
1970 else
1971 symbol->SetUnit( 1 );
1972
1973 if( elem.displaymodecount > 1 )
1974 symbol->SetBodyStyle( elem.displaymode + 1 );
1975
1977
1978 SCH_SCREEN* screen = getCurrentScreen();
1979 wxCHECK( screen, /* void */ );
1980
1981 screen->Append( symbol );
1982
1983 m_symbols.insert( { aIndex, symbol } );
1984
1985 if( !elem.uniqueid.empty() )
1986 m_altiumSymbolToUid[symbol] = elem.uniqueid;
1987}
1988
1989
1990void SCH_IO_ALTIUM::ParseTemplate( int aIndex, const std::map<wxString, wxString>& aProperties )
1991{
1992 SCH_SHEET* currentSheet = m_sheetPath.Last();
1993 wxCHECK( currentSheet, /* void */ );
1994
1995 wxString sheetName = currentSheet->GetName();
1996
1997 if( sheetName.IsEmpty() )
1998 sheetName = wxT( "root" );
1999
2000 ASCH_TEMPLATE altiumTemplate( aProperties );
2001
2002 // Extract base name from path
2003 wxString baseName = altiumTemplate.filename.AfterLast( '\\' ).BeforeLast( '.' );
2004
2005 if( baseName.IsEmpty() )
2006 baseName = wxS( "Template" );
2007
2008 m_altiumTemplates.insert( { aIndex, altiumTemplate } );
2009 // No need to create a symbol - graphics is put on the sheet
2010}
2011
2012
2013void SCH_IO_ALTIUM::ParsePin( const std::map<wxString, wxString>& aProperties,
2014 std::vector<LIB_SYMBOL*>& aSymbol )
2015{
2016 ASCH_PIN elem( aProperties );
2017
2018 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2019 SCH_SYMBOL* schSymbol = nullptr;
2020
2021 if( !symbol )
2022 {
2023 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2024
2025 if( libSymbolIt == m_libSymbols.end() )
2026 {
2027 // TODO: e.g. can depend on Template (RECORD=39
2028 m_errorMessages.emplace( wxString::Format( wxT( "Pin's owner (%d) not found." ),
2029 elem.ownerindex ),
2031 return;
2032 }
2033
2034 schSymbol = m_symbols.at( libSymbolIt->first );
2035 symbol = libSymbolIt->second;
2036 }
2037
2038 SCH_PIN* pin = new SCH_PIN( symbol );
2039
2040 // Make sure that these are visible when initializing the symbol
2041 // This may be overriden by the file data but not by the pin defaults
2042 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
2043 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
2044
2045 symbol->AddDrawItem( pin, false );
2046
2047 pin->SetUnit( std::max( 0, elem.ownerpartid ) );
2048
2049 if( symbol->GetBodyStyleCount() > 1 )
2050 {
2051 if( !aSymbol.empty() )
2052 {
2053 pin->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2054 }
2055 else
2056 {
2057 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2058
2059 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2060 pin->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2061 }
2062 }
2063
2064 pin->SetName( AltiumPinNamesToKiCad( elem.name ) );
2065 pin->SetNumber( AltiumPinDesignatorToKiCad( elem.designator ) );
2066 pin->SetLength( elem.pinlength );
2067
2068 if( elem.hidden )
2069 pin->SetVisible( false );
2070
2071 if( !elem.showDesignator )
2072 pin->SetNumberTextSize( 0 );
2073
2074 if( !elem.showPinName )
2075 pin->SetNameTextSize( 0 );
2076
2077 // Altium gives the pin body end location (elem.location) and the pre-computed
2078 // electrical connection point (elem.kicadLocation) which accounts for pin length
2079 // with combined integer+fractional arithmetic to avoid rounding errors.
2080 VECTOR2I bodyEnd = elem.location;
2081 VECTOR2I pinLocation = elem.kicadLocation;
2082
2083 switch( elem.orientation )
2084 {
2086 pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT );
2087 break;
2088
2090 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
2091 break;
2092
2094 pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT );
2095 break;
2096
2098 pin->SetOrientation( PIN_ORIENTATION::PIN_UP );
2099 break;
2100
2101 default:
2102 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
2103 break;
2104 }
2105
2106 if( schSymbol )
2107 {
2108 // Both points are in absolute schematic coordinates. Transform them to library-local
2109 // space, then derive the pin orientation from the resulting direction vector.
2110 pinLocation = GetRelativePosition( pinLocation + m_sheetOffset, schSymbol );
2111 bodyEnd = GetRelativePosition( bodyEnd + m_sheetOffset, schSymbol );
2112
2113 VECTOR2I dir = bodyEnd - pinLocation;
2114
2115 if( std::abs( dir.x ) >= std::abs( dir.y ) )
2116 pin->SetOrientation( dir.x > 0 ? PIN_ORIENTATION::PIN_RIGHT : PIN_ORIENTATION::PIN_LEFT );
2117 else
2118 pin->SetOrientation( dir.y > 0 ? PIN_ORIENTATION::PIN_DOWN : PIN_ORIENTATION::PIN_UP );
2119 }
2120
2121 pin->SetPosition( pinLocation );
2122
2123 switch( elem.electrical )
2124 {
2127 break;
2128
2130 pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
2131 break;
2132
2135 break;
2136
2139 break;
2140
2143 break;
2144
2147 break;
2148
2151 break;
2152
2155 break;
2156
2158 default:
2160 m_errorMessages.emplace( _( "Pin has unexpected electrical type." ), RPT_SEVERITY_WARNING );
2161 break;
2162 }
2163
2165 m_errorMessages.emplace( _( "Pin has unexpected outer edge type." ), RPT_SEVERITY_WARNING );
2166
2168 m_errorMessages.emplace( _( "Pin has unexpected inner edge type." ), RPT_SEVERITY_WARNING );
2169
2171 {
2172 switch( elem.symbolInnerEdge )
2173 {
2176 break;
2177
2178 default:
2179 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
2180 break;
2181 }
2182 }
2184 {
2185 switch( elem.symbolInnerEdge )
2186 {
2188 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
2189 break;
2190
2191 default:
2192 pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
2193 break;
2194 }
2195 }
2197 {
2198 pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
2199 }
2200 else
2201 {
2202 switch( elem.symbolInnerEdge )
2203 {
2205 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
2206 break;
2207
2208 default:
2209 pin->SetShape( GRAPHIC_PINSHAPE::LINE ); // nothing to do
2210 break;
2211 }
2212 }
2213}
2214
2215
2217 ASCH_RECORD_ORIENTATION orientation )
2218{
2219 int vjustify, hjustify;
2221
2222 switch( justification )
2223 {
2224 default:
2229 vjustify = GR_TEXT_V_ALIGN_BOTTOM;
2230 break;
2231
2235 vjustify = GR_TEXT_V_ALIGN_CENTER;
2236 break;
2237
2241 vjustify = GR_TEXT_V_ALIGN_TOP;
2242 break;
2243 }
2244
2245 switch( justification )
2246 {
2247 default:
2252 hjustify = GR_TEXT_H_ALIGN_LEFT;
2253 break;
2254
2258 hjustify = GR_TEXT_H_ALIGN_CENTER;
2259 break;
2260
2264 hjustify = GR_TEXT_H_ALIGN_RIGHT;
2265 break;
2266 }
2267
2268 switch( orientation )
2269 {
2271 angle = ANGLE_HORIZONTAL;
2272 break;
2273
2275 hjustify *= -1;
2276 angle = ANGLE_HORIZONTAL;
2277 break;
2278
2280 angle = ANGLE_VERTICAL;
2281 break;
2282
2284 hjustify *= -1;
2285 angle = ANGLE_VERTICAL;
2286 break;
2287 }
2288
2289 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( vjustify ) );
2290 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( hjustify ) );
2291 text->SetTextAngle( angle );
2292}
2293
2294
2295// Altium text orientation and justification are in absolute (page) coordinates. KiCad stores
2296// field text properties relative to the parent symbol and applies the symbol's transform at
2297// render time. This function adjusts the field's stored text angle and justification to
2298// compensate for the symbol's orientation so that the final rendered appearance matches the
2299// original Altium layout.
2300//
2301// The compensation follows the same logic as SCH_FIELD::Rotate() but applied in the
2302// inverse direction to undo the symbol's rotation effect on text properties.
2304{
2305 bool isHorizontal = aField->GetTextAngle().IsHorizontal();
2306
2307 // Altium orientation 0 (RIGHTWARDS) maps to KiCad SYM_ORIENT_90 (CCW). To compensate,
2308 // apply CW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CW rotation
2309 // of horizontal text flips justification; CW rotation of vertical text does not.
2310 if( aSymbol.orientation == 0 )
2311 {
2312 if( isHorizontal )
2313 {
2314 aField->SetHorizJustify(
2315 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2316 }
2317
2318 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2319 }
2320 // Altium orientation 1 (UPWARDS) maps to KiCad SYM_ORIENT_180 (two CCW rotations). The
2321 // transform [-1,0,0,-1] negates both X and Y, flipping horizontal justification. Apply
2322 // one correction for the full 180 degrees regardless of text angle.
2323 else if( aSymbol.orientation == 1 )
2324 {
2325 aField->SetHorizJustify(
2326 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2327 }
2328 // Altium orientation 2 (LEFTWARDS) maps to KiCad SYM_ORIENT_270 (CW). To compensate,
2329 // apply CCW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CCW rotation
2330 // of vertical text flips justification; CCW rotation of horizontal text does not.
2331 else if( aSymbol.orientation == 2 )
2332 {
2333 if( !isHorizontal )
2334 {
2335 aField->SetHorizJustify(
2336 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2337 }
2338
2339 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2340 }
2341 // Altium orientation 3 (DOWNWARDS) maps to KiCad SYM_ORIENT_0 (identity). No rotation
2342 // compensation needed.
2343
2344 // Mirror-Y in KiCad negates the X component of the bounding box, which effectively
2345 // flips horizontal justification. Compensate so the rendered text matches Altium.
2346 if( aSymbol.isMirrored )
2347 {
2348 aField->SetHorizJustify(
2349 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2350 }
2351}
2352
2353
2354// Altium text in symbols uses absolute orientation, but KiCad applies the symbol's transform
2355// to library body items via OrientAndMirrorSymbolItems at render time. This function pre-
2356// compensates the stored text angle and justification so that after the render-time transform,
2357// the final appearance matches the original Altium layout. Position is not adjusted here
2358// since GetRelativePosition already handles the positional component.
2360{
2361 int nRenderRotations = ( aSymbol.orientation + 1 ) % 4;
2362
2363 // Undo mirror first (reverse of render-time application order).
2364 // MirrorHorizontally on LAYER_DEVICE text flips H-justify when horizontal
2365 // and V-justify when vertical.
2366 if( aSymbol.isMirrored )
2367 {
2368 if( aText->GetTextAngle().IsHorizontal() )
2369 aText->FlipHJustify();
2370 else
2371 aText->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -aText->GetVertJustify() ) );
2372 }
2373
2374 // The render pipeline applies Rotate90(false) N times; undo with N inverse rotations.
2375 for( int i = 0; i < nRenderRotations; i++ )
2376 aText->Rotate90( true );
2377}
2378
2379
2381{
2382 // No component assigned -> Put on sheet
2383 if( aOwnerindex == ALTIUM_COMPONENT_NONE )
2384 return true;
2385
2386 // For a template -> Put on sheet so we can resolve variables
2387 if( m_altiumTemplates.find( aOwnerindex ) != m_altiumTemplates.end() )
2388 return true;
2389
2390 return false;
2391}
2392
2393
2394void SCH_IO_ALTIUM::ParseLabel( const std::map<wxString, wxString>& aProperties,
2395 std::vector<LIB_SYMBOL*>& aSymbol, std::vector<int>& aFontSizes )
2396{
2397 ASCH_LABEL elem( aProperties );
2398
2399 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2400 {
2401 static const std::map<wxString, wxString> variableMap = {
2402 { "APPLICATION_BUILDNUMBER", "KICAD_VERSION" },
2403 { "SHEETNUMBER", "#" },
2404 { "SHEETTOTAL", "##" },
2405 { "TITLE", "TITLE" }, // including 1:1 maps makes it easier
2406 { "REVISION", "REVISION" }, // to see that the list is complete
2407 { "DATE", "ISSUE_DATE" },
2408 { "CURRENTDATE", "CURRENT_DATE" },
2409 { "COMPANYNAME", "COMPANY" },
2410 { "DOCUMENTNAME", "FILENAME" },
2411 { "DOCUMENTFULLPATHANDNAME", "FILEPATH" },
2412 { "PROJECTNAME", "PROJECTNAME" },
2413 };
2414
2415 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
2416 SCH_TEXT* textItem = new SCH_TEXT( elem.location + m_sheetOffset, kicadText );
2417
2418 SetTextPositioning( textItem, elem.justification, elem.orientation );
2419
2420 size_t fontId = static_cast<int>( elem.fontId );
2421
2422 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2423 {
2424 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2425 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
2426
2427 // Must come after SetTextSize()
2428 textItem->SetBold( font.Bold );
2429 textItem->SetItalic( font.Italic );
2430 }
2431
2432 textItem->SetFlags( IS_NEW );
2433
2434 SCH_SCREEN* screen = getCurrentScreen();
2435 wxCHECK( screen, /* void */ );
2436
2437 screen->Append( textItem );
2438 }
2439 else
2440 {
2441 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2442 SCH_SYMBOL* schsym = nullptr;
2443
2444 if( !symbol )
2445 {
2446 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2447
2448 if( libSymbolIt == m_libSymbols.end() )
2449 {
2450 // TODO: e.g. can depend on Template (RECORD=39
2451 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), elem.ownerindex ),
2453 return;
2454 }
2455
2456 symbol = libSymbolIt->second;
2457 schsym = m_symbols.at( libSymbolIt->first );
2458 }
2459
2460 VECTOR2I pos = elem.location;
2461 SCH_TEXT* textItem = new SCH_TEXT( { 0, 0 }, elem.text, LAYER_DEVICE );
2462 symbol->AddDrawItem( textItem, false );
2463
2464 if( symbol->GetBodyStyleCount() > 1 )
2465 {
2466 if( !aSymbol.empty() )
2467 {
2468 textItem->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2469 }
2470 else
2471 {
2472 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2473
2474 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2475 textItem->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2476 }
2477 }
2478
2480 if( schsym )
2481 pos = GetRelativePosition( elem.location + m_sheetOffset, schsym );
2482
2483 textItem->SetPosition( pos );
2484 textItem->SetUnit( std::max( 0, elem.ownerpartid ) );
2485 SetTextPositioning( textItem, elem.justification, elem.orientation );
2486
2487 if( schsym )
2488 {
2489 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
2490
2491 if( altiumSymIt != m_altiumComponents.end() )
2492 AdjustTextForSymbolOrientation( textItem, altiumSymIt->second );
2493 }
2494
2495 size_t fontId = elem.fontId;
2496
2497 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2498 {
2499 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2500 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
2501
2502 // Must come after SetTextSize()
2503 textItem->SetBold( font.Bold );
2504 textItem->SetItalic( font.Italic );
2505 }
2506 else if( fontId > 0 && fontId <= aFontSizes.size() )
2507 {
2508 int size = aFontSizes[fontId - 1];
2509 textItem->SetTextSize( { size, size } );
2510 }
2511 }
2512}
2513
2514
2515void SCH_IO_ALTIUM::ParseTextFrame( const std::map<wxString, wxString>& aProperties,
2516 std::vector<LIB_SYMBOL*>& aSymbol,
2517 std::vector<int>& aFontSizes )
2518{
2519 ASCH_TEXT_FRAME elem( aProperties );
2520
2521 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2522 AddTextBox( &elem );
2523 else
2524 AddLibTextBox( &elem, aSymbol, aFontSizes );
2525}
2526
2527
2528void SCH_IO_ALTIUM::ParseNote( const std::map<wxString, wxString>& aProperties )
2529{
2530 ASCH_NOTE elem( aProperties );
2531 AddTextBox( static_cast<ASCH_TEXT_FRAME*>( &elem ) );
2532
2533 // TODO: need some sort of property system for storing author....
2534}
2535
2536
2538{
2539 SCH_TEXTBOX* textBox = new SCH_TEXTBOX();
2540
2541 VECTOR2I sheetTopRight = aElem->TopRight + m_sheetOffset;
2542 VECTOR2I sheetBottomLeft = aElem->BottomLeft +m_sheetOffset;
2543
2544 textBox->SetStart( sheetTopRight );
2545 textBox->SetEnd( sheetBottomLeft );
2546
2547 textBox->SetText( aElem->Text );
2548
2549 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
2550
2551 if( aElem->isSolid)
2553 else
2554 textBox->SetFilled( false );
2555
2556 if( aElem->ShowBorder )
2558 else
2560
2561 switch( aElem->Alignment )
2562 {
2563 default:
2566 break;
2569 break;
2572 break;
2573 }
2574
2575 size_t fontId = static_cast<int>( aElem->FontID );
2576
2577 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2578 {
2579 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2580 textBox->SetTextSize( { font.Size / 2, font.Size / 2 } );
2581
2582 // Must come after SetTextSize()
2583 textBox->SetBold( font.Bold );
2584 textBox->SetItalic( font.Italic );
2585 //textBox->SetFont( //how to set font, we have a font name here: ( font.fontname );
2586 }
2587
2588 textBox->SetFlags( IS_NEW );
2589
2590 SCH_SCREEN* screen = getCurrentScreen();
2591 wxCHECK( screen, /* void */ );
2592
2593 screen->Append( textBox );
2594}
2595
2596
2597void SCH_IO_ALTIUM::AddLibTextBox( const ASCH_TEXT_FRAME *aElem, std::vector<LIB_SYMBOL*>& aSymbol,
2598 std::vector<int>& aFontSizes )
2599{
2600 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2601 SCH_SYMBOL* schsym = nullptr;
2602
2603 if( !symbol )
2604 {
2605 const auto& libSymbolIt = m_libSymbols.find( aElem->ownerindex );
2606
2607 if( libSymbolIt == m_libSymbols.end() )
2608 {
2609 // TODO: e.g. can depend on Template (RECORD=39
2610 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), aElem->ownerindex ),
2612 return;
2613 }
2614
2615 symbol = libSymbolIt->second;
2616 schsym = m_symbols.at( libSymbolIt->first );
2617 }
2618
2619 SCH_TEXTBOX* textBox = new SCH_TEXTBOX( LAYER_DEVICE );
2620
2621 textBox->SetUnit( std::max( 0, aElem->ownerpartid ) );
2622 symbol->AddDrawItem( textBox, false );
2623
2624 if( symbol->GetBodyStyleCount() > 1 )
2625 {
2626 if( !aSymbol.empty() )
2627 {
2628 textBox->SetBodyStyle( aElem->ownerpartdisplaymode + 1 );
2629 }
2630 else
2631 {
2632 const auto& compIt = m_altiumComponents.find( aElem->ownerindex );
2633
2634 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2635 textBox->SetBodyStyle( aElem->ownerpartdisplaymode + 1 );
2636 }
2637 }
2638
2640 if( !schsym )
2641 {
2642 textBox->SetStart( aElem->TopRight );
2643 textBox->SetEnd( aElem->BottomLeft );
2644 }
2645 else
2646 {
2647 textBox->SetStart( GetRelativePosition( aElem->TopRight + m_sheetOffset, schsym ) );
2648 textBox->SetEnd( GetRelativePosition( aElem->BottomLeft + m_sheetOffset, schsym ) );
2649 }
2650
2651 textBox->SetText( aElem->Text );
2652
2653 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
2654
2655 if( aElem->isSolid)
2657 else
2658 textBox->SetFilled( false );
2659
2660 if( aElem->ShowBorder )
2662 else
2663 textBox->SetStroke( STROKE_PARAMS( -1 ) );
2664
2665 switch( aElem->Alignment )
2666 {
2667 default:
2670 break;
2673 break;
2676 break;
2677 }
2678
2679 if( aElem->FontID > 0 && aElem->FontID <= static_cast<int>( aFontSizes.size() ) )
2680 {
2681 int size = aFontSizes[aElem->FontID - 1];
2682 textBox->SetTextSize( { size, size } );
2683 }
2684}
2685
2686
2687void SCH_IO_ALTIUM::ParseBezier( const std::map<wxString, wxString>& aProperties,
2688 std::vector<LIB_SYMBOL*>& aSymbol )
2689{
2690 ASCH_BEZIER elem( aProperties );
2691
2692 if( elem.points.size() < 2 )
2693 {
2694 m_errorMessages.emplace( wxString::Format( _( "Bezier has %d control points. At least 2 are expected." ),
2695 static_cast<int>( elem.points.size() ) ),
2697 return;
2698 }
2699
2700 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2701 {
2702 SCH_SCREEN* currentScreen = getCurrentScreen();
2703 wxCHECK( currentScreen, /* void */ );
2704
2705 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
2706 {
2707 if( i + 2 == elem.points.size() )
2708 {
2709 // special case: single line
2710 SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
2712
2713 line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
2715
2716 line->SetFlags( IS_NEW );
2717
2718 currentScreen->Append( line );
2719 }
2720 else
2721 {
2722 // simulate Bezier using line segments
2723 std::vector<VECTOR2I> bezierPoints;
2724 std::vector<VECTOR2I> polyPoints;
2725
2726 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
2727 bezierPoints.push_back( elem.points.at( j ) );
2728
2729 BEZIER_POLY converter( bezierPoints );
2730 converter.GetPoly( polyPoints );
2731
2732 for( size_t k = 0; k + 1 < polyPoints.size(); k++ )
2733 {
2734 SCH_LINE* line = new SCH_LINE( polyPoints.at( k ) + m_sheetOffset,
2736
2737 line->SetEndPoint( polyPoints.at( k + 1 ) + m_sheetOffset );
2739
2740 line->SetFlags( IS_NEW );
2741 currentScreen->Append( line );
2742 }
2743 }
2744 }
2745 }
2746 else
2747 {
2748 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2749 SCH_SYMBOL* schsym = nullptr;
2750
2751 if( !symbol )
2752 {
2753 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2754
2755 if( libSymbolIt == m_libSymbols.end() )
2756 {
2757 // TODO: e.g. can depend on Template (RECORD=39
2758 m_errorMessages.emplace( wxString::Format( wxT( "Bezier's owner (%d) not found." ),
2759 elem.ownerindex ),
2761 return;
2762 }
2763
2764 symbol = libSymbolIt->second;
2765 schsym = m_symbols.at( libSymbolIt->first );
2766 }
2767
2768 int bodyStyle = 0;
2769
2770 if( symbol->GetBodyStyleCount() > 1 )
2771 {
2772 if( !aSymbol.empty() )
2773 {
2774 bodyStyle = elem.ownerpartdisplaymode + 1;
2775 }
2776 else
2777 {
2778 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2779
2780 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2781 bodyStyle = elem.ownerpartdisplaymode + 1;
2782 }
2783 }
2784
2785 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
2786 {
2787 if( i + 2 == elem.points.size() )
2788 {
2789 // special case: single line
2791 symbol->AddDrawItem( line, false );
2792
2793 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2794
2795 if( bodyStyle > 0 )
2796 line->SetBodyStyle( bodyStyle );
2797
2798 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
2799 {
2800 VECTOR2I pos = elem.points.at( j );
2801
2802 if( schsym )
2803 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2804
2805 line->AddPoint( pos );
2806 }
2807
2809 }
2810 else if( i + 3 == elem.points.size() )
2811 {
2812 // TODO: special case of a single line with an extra point?
2813 // I haven't a clue what this is all about, but the sample document we have in
2814 // https://gitlab.com/kicad/code/kicad/-/issues/8974 responds best by treating it
2815 // as another single line special case.
2817 symbol->AddDrawItem( line, false );
2818
2819 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2820
2821 if( bodyStyle > 0 )
2822 line->SetBodyStyle( bodyStyle );
2823
2824 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
2825 {
2826 VECTOR2I pos = elem.points.at( j );
2827
2828 if( schsym )
2829 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2830
2831 line->AddPoint( pos );
2832 }
2833
2835 }
2836 else
2837 {
2838 // Bezier always has exactly 4 control points
2840 symbol->AddDrawItem( bezier, false );
2841
2842 bezier->SetUnit( std::max( 0, elem.ownerpartid ) );
2843
2844 if( bodyStyle > 0 )
2845 bezier->SetBodyStyle( bodyStyle );
2846
2847 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
2848 {
2849 VECTOR2I pos = elem.points.at( j );
2850
2851 if( schsym )
2852 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2853
2854 switch( j - i )
2855 {
2856 case 0: bezier->SetStart( pos ); break;
2857 case 1: bezier->SetBezierC1( pos ); break;
2858 case 2: bezier->SetBezierC2( pos ); break;
2859 case 3: bezier->SetEnd( pos ); break;
2860 default: break; // Can't get here but silence warnings
2861 }
2862 }
2863
2866 }
2867 }
2868 }
2869}
2870
2871
2872void SCH_IO_ALTIUM::ParsePolyline( const std::map<wxString, wxString>& aProperties,
2873 std::vector<LIB_SYMBOL*>& aSymbol )
2874{
2875 ASCH_POLYLINE elem( aProperties );
2876
2877 if( elem.Points.size() < 2 )
2878 return;
2879
2880 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2881 {
2882 SCH_SCREEN* screen = getCurrentScreen();
2883 wxCHECK( screen, /* void */ );
2884
2885 for( size_t i = 1; i < elem.Points.size(); i++ )
2886 {
2887 SCH_LINE* line = new SCH_LINE;
2888
2889 line->SetStartPoint( elem.Points[i - 1] + m_sheetOffset );
2890 line->SetEndPoint( elem.Points[i] + m_sheetOffset );
2891
2893 GetColorFromInt( elem.Color ) ) );
2894
2895 line->SetFlags( IS_NEW );
2896
2897 screen->Append( line );
2898 }
2899 }
2900 else
2901 {
2902 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2903 SCH_SYMBOL* schsym = nullptr;
2904
2905 if( !symbol )
2906 {
2907 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2908
2909 if( libSymbolIt == m_libSymbols.end() )
2910 {
2911 // TODO: e.g. can depend on Template (RECORD=39
2912 m_errorMessages.emplace( wxString::Format( wxT( "Polyline's owner (%d) not found." ),
2913 elem.ownerindex ),
2915 return;
2916 }
2917
2918 symbol = libSymbolIt->second;
2919 schsym = m_symbols.at( libSymbolIt->first );
2920 }
2921
2923 symbol->AddDrawItem( line, false );
2924
2925 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2926
2927 if( symbol->GetBodyStyleCount() > 1 )
2928 {
2929 if( !aSymbol.empty() )
2930 {
2931 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2932 }
2933 else
2934 {
2935 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
2936
2937 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
2938 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
2939 }
2940 }
2941
2942 for( VECTOR2I point : elem.Points )
2943 {
2944 if( schsym )
2945 point = GetRelativePosition( point + m_sheetOffset, schsym );
2946
2947 line->AddPoint( point );
2948 }
2949
2951 STROKE_PARAMS stroke = line->GetStroke();
2952 stroke.SetLineStyle( GetPlotDashType( elem.LineStyle ) );
2953
2954 line->SetStroke( stroke );
2955 }
2956}
2957
2958
2959void SCH_IO_ALTIUM::ParsePolygon( const std::map<wxString, wxString>& aProperties,
2960 std::vector<LIB_SYMBOL*>& aSymbol )
2961{
2962 ASCH_POLYGON elem( aProperties );
2963
2964 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2965 {
2966 SCH_SCREEN* screen = getCurrentScreen();
2967 wxCHECK( screen, /* void */ );
2968
2969 SCH_SHAPE* poly = new SCH_SHAPE( SHAPE_T::POLY );
2970
2971 for( VECTOR2I& point : elem.points )
2972 poly->AddPoint( point + m_sheetOffset );
2973 poly->AddPoint( elem.points.front() + m_sheetOffset );
2974
2975 SetSchShapeLine( elem, poly );
2976 SetSchShapeFillAndColor( elem, poly );
2977 poly->SetFlags( IS_NEW );
2978
2979 screen->Append( poly );
2980 }
2981 else
2982 {
2983 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
2984 SCH_SYMBOL* schsym = nullptr;
2985
2986 if( !symbol )
2987 {
2988 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2989
2990 if( libSymbolIt == m_libSymbols.end() )
2991 {
2992 // TODO: e.g. can depend on Template (RECORD=39
2993 m_errorMessages.emplace( wxString::Format( wxT( "Polygon's owner (%d) not found." ),
2994 elem.ownerindex ),
2996 return;
2997 }
2998
2999 symbol = libSymbolIt->second;
3000 schsym = m_symbols.at( libSymbolIt->first );
3001 }
3002
3004
3005 symbol->AddDrawItem( line, false );
3006 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3007
3008 if( symbol->GetBodyStyleCount() > 1 )
3009 {
3010 if( !aSymbol.empty() )
3011 {
3012 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3013 }
3014 else
3015 {
3016 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3017
3018 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3019 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3020 }
3021 }
3022
3023 for( VECTOR2I point : elem.points )
3024 {
3025 if( schsym )
3026 point = GetRelativePosition( point + m_sheetOffset, schsym );
3027
3028 line->AddPoint( point );
3029 }
3030
3031 VECTOR2I point = elem.points.front();
3032
3033 if( schsym )
3034 point = GetRelativePosition( elem.points.front() + m_sheetOffset, schsym );
3035
3036 line->AddPoint( point );
3037
3040
3041 if( line->GetFillColor() == line->GetStroke().GetColor()
3042 && line->GetFillMode() != FILL_T::NO_FILL )
3043 {
3044 STROKE_PARAMS stroke = line->GetStroke();
3045 stroke.SetWidth( -1 );
3046 line->SetStroke( stroke );
3047 }
3048 }
3049}
3050
3051
3052void SCH_IO_ALTIUM::ParseRoundRectangle( const std::map<wxString, wxString>& aProperties,
3053 std::vector<LIB_SYMBOL*>& aSymbol )
3054{
3055 ASCH_ROUND_RECTANGLE elem( aProperties );
3056
3057 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3058 {
3059 SCH_SCREEN* screen = getCurrentScreen();
3060 wxCHECK( screen, /* void */ );
3061
3062 // TODO: misses rounded edges
3063 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
3064
3065 rect->SetPosition( elem.TopRight + m_sheetOffset );
3066 rect->SetEnd( elem.BottomLeft + m_sheetOffset );
3067 SetSchShapeLine( elem, rect );
3068 SetSchShapeFillAndColor( elem, rect );
3069 rect->SetFlags( IS_NEW );
3070
3071 screen->Append( rect );
3072 }
3073 else
3074 {
3075 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3076 SCH_SYMBOL* schsym = nullptr;
3077
3078 if( !symbol )
3079 {
3080 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3081
3082 if( libSymbolIt == m_libSymbols.end() )
3083 {
3084 // TODO: e.g. can depend on Template (RECORD=39
3085 m_errorMessages.emplace( wxString::Format( wxT( "Rounded rectangle's owner (%d) not found." ),
3086 elem.ownerindex ),
3088 return;
3089 }
3090
3091 symbol = libSymbolIt->second;
3092 schsym = m_symbols.at( libSymbolIt->first );
3093 }
3094
3095 SCH_SHAPE* rect = nullptr;
3096
3097 int width = std::abs( elem.TopRight.x - elem.BottomLeft.x );
3098 int height = std::abs( elem.TopRight.y - elem.BottomLeft.y );
3099
3100 // If it is a circle, make it a circle
3101 if( std::abs( elem.CornerRadius.x ) >= width / 2
3102 && std::abs( elem.CornerRadius.y ) >= height / 2 )
3103 {
3104 rect = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
3105
3106 VECTOR2I center = ( elem.TopRight + elem.BottomLeft ) / 2;
3107 int radius = std::min( width / 2, height / 2 );
3108
3109 if( schsym )
3111
3112 rect->SetPosition( center );
3113 rect->SetEnd( VECTOR2I( rect->GetPosition().x + radius, rect->GetPosition().y ) );
3114 }
3115 else
3116 {
3118
3119 if( !schsym )
3120 {
3121 rect->SetPosition( elem.TopRight );
3122 rect->SetEnd( elem.BottomLeft );
3123 }
3124 else
3125 {
3126 rect->SetPosition( GetRelativePosition( elem.TopRight + m_sheetOffset, schsym ) );
3127 rect->SetEnd( GetRelativePosition( elem.BottomLeft + m_sheetOffset, schsym ) );
3128 }
3129
3130 rect->Normalize();
3131 }
3132
3135
3136 symbol->AddDrawItem( rect, false );
3137 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
3138
3139 if( symbol->GetBodyStyleCount() > 1 )
3140 {
3141 if( !aSymbol.empty() )
3142 {
3143 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3144 }
3145 else
3146 {
3147 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3148
3149 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3150 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3151 }
3152 }
3153 }
3154}
3155
3156
3157void SCH_IO_ALTIUM::ParseArc( const std::map<wxString, wxString>& aProperties,
3158 std::vector<LIB_SYMBOL*>& aSymbol )
3159{
3160 ASCH_ARC elem( aProperties );
3161
3162 int arc_radius = elem.m_Radius;
3163 VECTOR2I center = elem.m_Center;
3164 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
3165 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
3166 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
3167 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
3168
3169 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3170 {
3171 SCH_SCREEN* currentScreen = getCurrentScreen();
3172 wxCHECK( currentScreen, /* void */ );
3173
3174 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3175 {
3177
3178 circle->SetPosition( elem.m_Center + m_sheetOffset );
3179 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
3180
3181 SetSchShapeLine( elem, circle );
3183
3184 currentScreen->Append( circle );
3185 }
3186 else
3187 {
3188 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
3189
3190 arc->SetCenter( elem.m_Center + m_sheetOffset );
3191 arc->SetStart( elem.m_Center + startOffset + m_sheetOffset );
3192 arc->SetEnd( elem.m_Center + endOffset + m_sheetOffset );
3193
3194 SetSchShapeLine( elem, arc );
3195 SetSchShapeFillAndColor( elem, arc );
3196
3197 currentScreen->Append( arc );
3198 }
3199 }
3200 else
3201 {
3202 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3203 SCH_SYMBOL* schsym = nullptr;
3204
3205 if( !symbol )
3206 {
3207 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3208
3209 if( libSymbolIt == m_libSymbols.end() )
3210 {
3211 // TODO: e.g. can depend on Template (RECORD=39
3212 m_errorMessages.emplace( wxString::Format( wxT( "Arc's owner (%d) not found." ), elem.ownerindex ),
3214 return;
3215 }
3216
3217 symbol = libSymbolIt->second;
3218 schsym = m_symbols.at( libSymbolIt->first );
3219 }
3220
3221 int bodyStyle = 0;
3222
3223 if( symbol->GetBodyStyleCount() > 1 )
3224 {
3225 if( !aSymbol.empty() )
3226 {
3227 bodyStyle = elem.ownerpartdisplaymode + 1;
3228 }
3229 else
3230 {
3231 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3232
3233 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3234 bodyStyle = elem.ownerpartdisplaymode + 1;
3235 }
3236 }
3237
3238 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3239 {
3241 symbol->AddDrawItem( circle, false );
3242
3243 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
3244
3245 if( bodyStyle > 0 )
3246 circle->SetBodyStyle( bodyStyle );
3247
3248 if( schsym )
3250
3251 circle->SetPosition( center );
3252
3253 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
3256 }
3257 else
3258 {
3260 symbol->AddDrawItem( arc, false );
3261 arc->SetUnit( std::max( 0, elem.ownerpartid ) );
3262
3263 if( bodyStyle > 0 )
3264 arc->SetBodyStyle( bodyStyle );
3265
3266 if( schsym )
3267 {
3269 startOffset = GetRelativePosition( elem.m_Center + startOffset + m_sheetOffset, schsym )
3270 - center;
3271 endOffset = GetRelativePosition( elem.m_Center + endOffset + m_sheetOffset, schsym )
3272 - center;
3273 }
3274
3275 arc->SetCenter( center );
3276 arc->SetStart( center + startOffset );
3277 arc->SetEnd( center + endOffset );
3278
3281 }
3282 }
3283}
3284
3285
3286void SCH_IO_ALTIUM::ParseEllipticalArc( const std::map<wxString, wxString>& aProperties,
3287 std::vector<LIB_SYMBOL*>& aSymbol )
3288{
3289 ASCH_ARC elem( aProperties );
3290
3291 if( elem.m_Radius == elem.m_SecondaryRadius && elem.m_StartAngle == 0
3292 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3293 {
3294 ParseCircle( aProperties, aSymbol );
3295 return;
3296 }
3297
3298 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3299 {
3300 SCH_SCREEN* currentScreen = getCurrentScreen();
3301 wxCHECK( currentScreen, /* void */ );
3302
3303 ELLIPSE<int> ellipse( elem.m_Center + m_sheetOffset, elem.m_Radius,
3306 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
3307 std::vector<BEZIER<int>> beziers;
3308
3309 TransformEllipseToBeziers( ellipse, beziers );
3310
3311 for( const BEZIER<int>& bezier : beziers )
3312 {
3313 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
3314 schbezier->SetStart( bezier.Start );
3315 schbezier->SetBezierC1( bezier.C1 );
3316 schbezier->SetBezierC2( bezier.C2 );
3317 schbezier->SetEnd( bezier.End );
3318 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
3320
3321 currentScreen->Append( schbezier );
3322 }
3323 }
3324 else
3325 {
3326 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3327 SCH_SYMBOL* schsym = nullptr;
3328
3329 if( !symbol )
3330 {
3331 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3332
3333 if( libSymbolIt == m_libSymbols.end() )
3334 {
3335 // TODO: e.g. can depend on Template (RECORD=39
3336 m_errorMessages.emplace( wxString::Format( wxT( "Elliptical Arc's owner (%d) not found." ),
3337 elem.ownerindex ),
3339 return;
3340 }
3341
3342 symbol = libSymbolIt->second;
3343 schsym = m_symbols.at( libSymbolIt->first );
3344 }
3345
3346 int bodyStyle = 0;
3347
3348 if( symbol->GetBodyStyleCount() > 1 )
3349 {
3350 if( !aSymbol.empty() )
3351 {
3352 bodyStyle = elem.ownerpartdisplaymode + 1;
3353 }
3354 else
3355 {
3356 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3357
3358 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3359 bodyStyle = elem.ownerpartdisplaymode + 1;
3360 }
3361 }
3362
3363 ELLIPSE<int> ellipse( elem.m_Center, elem.m_Radius,
3366 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
3367 std::vector<BEZIER<int>> beziers;
3368
3369 TransformEllipseToBeziers( ellipse, beziers );
3370
3371 for( const BEZIER<int>& bezier : beziers )
3372 {
3373 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
3374 symbol->AddDrawItem( schbezier, false );
3375
3376 schbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
3377
3378 if( bodyStyle > 0 )
3379 schbezier->SetBodyStyle( bodyStyle );
3380
3381 if( schsym )
3382 {
3383 schbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
3384 schbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
3385 schbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
3386 schbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
3387 }
3388 else
3389 {
3390 schbezier->SetStart( bezier.Start );
3391 schbezier->SetBezierC1( bezier.C1 );
3392 schbezier->SetBezierC2( bezier.C2 );
3393 schbezier->SetEnd( bezier.End );
3394 }
3395
3398 }
3399 }
3400}
3401
3402
3403void SCH_IO_ALTIUM::ParsePieChart( const std::map<wxString, wxString>& aProperties,
3404 std::vector<LIB_SYMBOL*>& aSymbol )
3405{
3406 ParseArc( aProperties, aSymbol );
3407
3408 ASCH_PIECHART elem( aProperties );
3409
3410 int arc_radius = elem.m_Radius;
3411 VECTOR2I center = elem.m_Center;
3412 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
3413 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
3414 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
3415 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
3416
3417 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3418 {
3419 SCH_SCREEN* screen = getCurrentScreen();
3420 wxCHECK( screen, /* void */ );
3421
3422 // close polygon
3424 line->SetEndPoint( center + startOffset + m_sheetOffset );
3426
3427 line->SetFlags( IS_NEW );
3428 screen->Append( line );
3429
3431 line->SetEndPoint( center + endOffset + m_sheetOffset );
3433
3434 line->SetFlags( IS_NEW );
3435 screen->Append( line );
3436 }
3437 else
3438 {
3439 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3440 SCH_SYMBOL* schsym = nullptr;
3441
3442 if( !symbol )
3443 {
3444 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3445
3446 if( libSymbolIt == m_libSymbols.end() )
3447 {
3448 // TODO: e.g. can depend on Template (RECORD=39
3449 m_errorMessages.emplace( wxString::Format( wxT( "Piechart's owner (%d) not found." ),
3450 elem.ownerindex ),
3452 return;
3453 }
3454
3455 symbol = libSymbolIt->second;
3456 schsym = m_symbols.at( libSymbolIt->first );
3457 }
3458
3460 symbol->AddDrawItem( line, false );
3461
3462 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3463
3464 if( symbol->GetBodyStyleCount() > 1 )
3465 {
3466 if( !aSymbol.empty() )
3467 {
3468 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3469 }
3470 else
3471 {
3472 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3473
3474 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3475 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3476 }
3477 }
3478
3479 if( !schsym )
3480 {
3481 line->AddPoint( center + startOffset );
3482 line->AddPoint( center );
3483 line->AddPoint( center + endOffset );
3484 }
3485 else
3486 {
3487 line->AddPoint( GetRelativePosition( center + startOffset + m_sheetOffset, schsym ) );
3488 line->AddPoint( GetRelativePosition( center + m_sheetOffset, schsym ) );
3489 line->AddPoint( GetRelativePosition( center + endOffset + m_sheetOffset, schsym ) );
3490 }
3491
3493 }
3494}
3495
3496
3497void SCH_IO_ALTIUM::ParseEllipse( const std::map<wxString, wxString>& aProperties,
3498 std::vector<LIB_SYMBOL*>& aSymbol )
3499{
3500 ASCH_ELLIPSE elem( aProperties );
3501
3502 if( elem.Radius == elem.SecondaryRadius )
3503 {
3504 ParseCircle( aProperties, aSymbol );
3505 return;
3506 }
3507
3508 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3509 {
3510 SCH_SCREEN* screen = getCurrentScreen();
3511 wxCHECK( screen, /* void */ );
3512
3513 COLOR4D fillColor = GetColorFromInt( elem.AreaColor );
3514
3515 if( elem.IsTransparent )
3516 fillColor = fillColor.WithAlpha( 0.5 );
3517
3519
3520 ELLIPSE<int> ellipse( elem.Center + m_sheetOffset, elem.Radius,
3521 KiROUND( elem.SecondaryRadius ), ANGLE_0 );
3522
3523 std::vector<BEZIER<int>> beziers;
3524 std::vector<VECTOR2I> polyPoints;
3525
3526 TransformEllipseToBeziers( ellipse, beziers );
3527
3528 for( const BEZIER<int>& bezier : beziers )
3529 {
3530 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
3531 schbezier->SetStart( bezier.Start );
3532 schbezier->SetBezierC1( bezier.C1 );
3533 schbezier->SetBezierC2( bezier.C2 );
3534 schbezier->SetEnd( bezier.End );
3535 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
3536 schbezier->SetFillColor( fillColor );
3537 schbezier->SetFillMode( fillMode );
3538
3540 screen->Append( schbezier );
3541
3542 polyPoints.push_back( bezier.Start );
3543 }
3544
3545 if( fillMode != FILL_T::NO_FILL )
3546 {
3547 SCH_SHAPE* schpoly = new SCH_SHAPE( SHAPE_T::POLY );
3548 schpoly->SetFillColor( fillColor );
3549 schpoly->SetFillMode( fillMode );
3550 schpoly->SetWidth( -1 );
3551
3552 for( const VECTOR2I& point : polyPoints )
3553 schpoly->AddPoint( point );
3554
3555 schpoly->AddPoint( polyPoints[0] );
3556
3557 screen->Append( schpoly );
3558 }
3559 }
3560 else
3561 {
3562 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3563 SCH_SYMBOL* schsym = nullptr;
3564
3565 if( !symbol )
3566 {
3567 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3568
3569 if( libSymbolIt == m_libSymbols.end() )
3570 {
3571 // TODO: e.g. can depend on Template (RECORD=39
3572 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
3574 return;
3575 }
3576
3577 symbol = libSymbolIt->second;
3578 schsym = m_symbols.at( libSymbolIt->first );
3579 }
3580
3581 int bodyStyle = 0;
3582
3583 if( symbol->GetBodyStyleCount() > 1 )
3584 {
3585 if( !aSymbol.empty() )
3586 {
3587 bodyStyle = elem.ownerpartdisplaymode + 1;
3588 }
3589 else
3590 {
3591 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3592
3593 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3594 bodyStyle = elem.ownerpartdisplaymode + 1;
3595 }
3596 }
3597
3598 ELLIPSE<int> ellipse( elem.Center, elem.Radius, KiROUND( elem.SecondaryRadius ),
3599 ANGLE_0 );
3600
3601 std::vector<BEZIER<int>> beziers;
3602 std::vector<VECTOR2I> polyPoints;
3603
3604 TransformEllipseToBeziers( ellipse, beziers );
3605
3606 for( const BEZIER<int>& bezier : beziers )
3607 {
3608 SCH_SHAPE* libbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
3609 symbol->AddDrawItem( libbezier, false );
3610 libbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
3611
3612 if( bodyStyle > 0 )
3613 libbezier->SetBodyStyle( bodyStyle );
3614
3615 if( !schsym )
3616 {
3617 libbezier->SetStart( bezier.Start );
3618 libbezier->SetBezierC1( bezier.C1 );
3619 libbezier->SetBezierC2( bezier.C2 );
3620 libbezier->SetEnd( bezier.End );
3621 }
3622 else
3623 {
3624 libbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
3625 libbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
3626 libbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
3627 libbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
3628 }
3629
3630 SetLibShapeLine( elem, libbezier, ALTIUM_SCH_RECORD::ELLIPSE );
3633
3634 polyPoints.push_back( libbezier->GetStart() );
3635 }
3636
3637 // A series of beziers won't fill the center, so if this is meant to be fully filled,
3638 // Add a polygon to fill the center
3639 if( elem.IsSolid )
3640 {
3641 SCH_SHAPE* libline = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
3642 symbol->AddDrawItem( libline, false );
3643 libline->SetUnit( std::max( 0, elem.ownerpartid ) );
3644
3645 if( bodyStyle > 0 )
3646 libline->SetBodyStyle( bodyStyle );
3647
3648 for( const VECTOR2I& point : polyPoints )
3649 libline->AddPoint( point );
3650
3651 libline->AddPoint( polyPoints[0] );
3652
3653 libline->SetWidth( -1 );
3655 }
3656 }
3657}
3658
3659
3660void SCH_IO_ALTIUM::ParseCircle( const std::map<wxString, wxString>& aProperties,
3661 std::vector<LIB_SYMBOL*>& aSymbol )
3662{
3663 ASCH_ELLIPSE elem( aProperties );
3664
3665 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3666 {
3667 SCH_SCREEN* screen = getCurrentScreen();
3668 wxCHECK( screen, /* void */ );
3669
3671
3672 circle->SetPosition( elem.Center + m_sheetOffset );
3673 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
3674 circle->SetStroke( STROKE_PARAMS( 1, LINE_STYLE::SOLID ) );
3675
3676 circle->SetFillColor( GetColorFromInt( elem.AreaColor ) );
3677
3678 if( elem.IsSolid )
3679 circle->SetFillMode( FILL_T::FILLED_WITH_COLOR );
3680 else
3681 circle->SetFilled( false );
3682
3683 screen->Append( circle );
3684 }
3685 else
3686 {
3687 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3688 SCH_SYMBOL* schsym = nullptr;
3689
3690 if( !symbol )
3691 {
3692 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3693
3694 if( libSymbolIt == m_libSymbols.end() )
3695 {
3696 // TODO: e.g. can depend on Template (RECORD=39
3697 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
3699 return;
3700 }
3701
3702 symbol = libSymbolIt->second;
3703 schsym = m_symbols.at( libSymbolIt->first );
3704 }
3705
3706 VECTOR2I center = elem.Center;
3708 symbol->AddDrawItem( circle, false );
3709
3710 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
3711
3712 if( symbol->GetBodyStyleCount() > 1 )
3713 {
3714 if( !aSymbol.empty() )
3715 {
3716 circle->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3717 }
3718 else
3719 {
3720 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3721
3722 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3723 circle->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3724 }
3725 }
3726
3727 if( schsym )
3729
3730 circle->SetPosition( center );
3731 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
3732
3735 }
3736}
3737
3738
3739void SCH_IO_ALTIUM::ParseLine( const std::map<wxString, wxString>& aProperties,
3740 std::vector<LIB_SYMBOL*>& aSymbol )
3741{
3742 ASCH_LINE elem( aProperties );
3743
3744 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3745 {
3746 SCH_SCREEN* screen = getCurrentScreen();
3747 wxCHECK( screen, /* void */ );
3748
3749 // close polygon
3751 line->SetEndPoint( elem.point2 + m_sheetOffset );
3753 GetColorFromInt( elem.Color ) ) );
3754
3755 line->SetFlags( IS_NEW );
3756 screen->Append( line );
3757 }
3758 else
3759 {
3760 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3761 SCH_SYMBOL* schsym = nullptr;
3762
3763 if( !symbol )
3764 {
3765 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3766
3767 if( libSymbolIt == m_libSymbols.end() )
3768 {
3769 // TODO: e.g. can depend on Template (RECORD=39
3770 m_errorMessages.emplace( wxString::Format( wxT( "Line's owner (%d) not found." ), elem.ownerindex ),
3772 return;
3773 }
3774
3775 symbol = libSymbolIt->second;
3776 schsym = m_symbols.at( libSymbolIt->first );
3777 }
3778
3780 symbol->AddDrawItem( line, false );
3781
3782 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3783
3784 if( symbol->GetBodyStyleCount() > 1 )
3785 {
3786 if( !aSymbol.empty() )
3787 {
3788 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3789 }
3790 else
3791 {
3792 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
3793
3794 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
3795 line->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
3796 }
3797 }
3798
3799 if( !schsym )
3800 {
3801 line->AddPoint( elem.point1 );
3802 line->AddPoint( elem.point2 );
3803 }
3804 else
3805 {
3806 line->AddPoint( GetRelativePosition( elem.point1 + m_sheetOffset, schsym ) );
3807 line->AddPoint( GetRelativePosition( elem.point2 + m_sheetOffset, schsym ) );
3808 }
3809
3811 line->SetLineStyle( GetPlotDashType( elem.LineStyle ) );
3812 }
3813}
3814
3815
3816void SCH_IO_ALTIUM::ParseSignalHarness( const std::map<wxString, wxString>& aProperties )
3817{
3818 ASCH_SIGNAL_HARNESS elem( aProperties );
3819
3820 if( ShouldPutItemOnSheet( elem.ownerindex ) )
3821 {
3822 SCH_SCREEN* screen = getCurrentScreen();
3823 wxCHECK( screen, /* void */ );
3824
3825 for( size_t ii = 0; ii < elem.points.size() - 1; ii++ )
3826 {
3828 line->SetEndPoint( elem.points[ii + 1] + m_sheetOffset );
3830
3831 line->SetFlags( IS_NEW );
3832 screen->Append( line );
3833 }
3834 }
3835 else
3836 {
3837 // No clue if this situation can ever exist
3838 m_errorMessages.emplace( wxT( "Signal harness, belonging to the part is not currently supported." ),
3840 }
3841}
3842
3843
3844void SCH_IO_ALTIUM::ParseHarnessConnector( int aIndex, const std::map<wxString,
3845 wxString>& aProperties )
3846{
3847 ASCH_HARNESS_CONNECTOR elem( aProperties );
3848
3849 if( ShouldPutItemOnSheet( elem.ownerindex ) )
3850 {
3852 auto [it, _] = m_altiumHarnesses.insert( { m_harnessEntryParent, HARNESS()} );
3853
3854 HARNESS& harness = it->second;
3855 HARNESS::HARNESS_PORT& port = harness.m_entry;
3856 harness.m_location = elem.m_location + m_sheetOffset;
3857 harness.m_size = elem.m_size;
3858
3859 VECTOR2I pos = elem.m_location + m_sheetOffset;
3860 VECTOR2I size = elem.m_size;
3861
3862 switch( elem.m_harnessConnectorSide )
3863 {
3864 default:
3866 port.m_location = { pos.x, pos.y + elem.m_primaryConnectionPosition };
3867 break;
3869 port.m_location = { pos.x + size.x, pos.y + elem.m_primaryConnectionPosition };
3870 break;
3872 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y };
3873 break;
3875 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y + size.y };
3876 break;
3877 }
3878 }
3879 else
3880 {
3881 // I have no clue if this situation can ever exist
3882 m_errorMessages.emplace( wxT( "Harness connector, belonging to the part is not currently supported." ),
3884 }
3885}
3886
3887
3888void SCH_IO_ALTIUM::ParseHarnessEntry( const std::map<wxString, wxString>& aProperties )
3889{
3890 ASCH_HARNESS_ENTRY elem( aProperties );
3891
3892 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
3893
3894 if( harnessIt == m_altiumHarnesses.end() )
3895 {
3896 m_errorMessages.emplace( wxString::Format( wxT( "Harness entry's parent (%d) not found." ),
3899 return;
3900 }
3901
3902 HARNESS& harness = harnessIt->second;
3904 port.m_name = elem.Name;
3905 port.m_harnessConnectorSide = elem.Side;
3907
3908 VECTOR2I pos = harness.m_location;
3909 VECTOR2I size = harness.m_size;
3910 int quadrant = 1;
3911
3912 switch( elem.Side )
3913 {
3914 default:
3916 port.m_location = { pos.x, pos.y + elem.DistanceFromTop };
3917 break;
3919 quadrant = 4;
3920 port.m_location = { pos.x + size.x, pos.y + elem.DistanceFromTop };
3921 break;
3923 port.m_location = { pos.x + elem.DistanceFromTop, pos.y };
3924 break;
3926 quadrant = 2;
3927 port.m_location = { pos.x + elem.DistanceFromTop, pos.y + size.y };
3928 break;
3929 }
3930
3931
3932 SCH_SCREEN* screen = getCurrentScreen();
3933 wxCHECK( screen, /* void */ );
3934
3935 SCH_BUS_WIRE_ENTRY* entry = new SCH_BUS_WIRE_ENTRY( port.m_location, quadrant );
3936 port.m_entryLocation = entry->GetPosition() + entry->GetSize();
3937 entry->SetFlags( IS_NEW );
3938 screen->Append( entry );
3939 harness.m_ports.emplace_back( port );
3940}
3941
3942
3943void SCH_IO_ALTIUM::ParseHarnessType( const std::map<wxString, wxString>& aProperties )
3944{
3945 ASCH_HARNESS_TYPE elem( aProperties );
3946
3947 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
3948
3949 if( harnessIt == m_altiumHarnesses.end() )
3950 {
3951 m_errorMessages.emplace( wxString::Format( wxT( "Harness type's parent (%d) not found." ),
3954 return;
3955 }
3956
3957 HARNESS& harness = harnessIt->second;
3958 harness.m_name = elem.Text;
3959}
3960
3961
3962void SCH_IO_ALTIUM::ParseRectangle( const std::map<wxString, wxString>& aProperties,
3963 std::vector<LIB_SYMBOL*>& aSymbol )
3964{
3965 ASCH_RECTANGLE elem( aProperties );
3966
3967 VECTOR2I sheetTopRight = elem.TopRight + m_sheetOffset;
3968 VECTOR2I sheetBottomLeft = elem.BottomLeft + m_sheetOffset;
3969
3970 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3971 {
3972 SCH_SCREEN* screen = getCurrentScreen();
3973 wxCHECK( screen, /* void */ );
3974
3975 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
3976
3977 rect->SetPosition( sheetTopRight );
3978 rect->SetEnd( sheetBottomLeft );
3979 SetSchShapeLine( elem, rect );
3980 SetSchShapeFillAndColor( elem, rect );
3981 rect->SetFlags( IS_NEW );
3982
3983 screen->Append( rect );
3984 }
3985 else
3986 {
3987 LIB_SYMBOL* symbol = aSymbol.empty() ? nullptr : aSymbol[0];
3988 SCH_SYMBOL* schsym = nullptr;
3989
3990 if( !symbol )
3991 {
3992 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3993
3994 if( libSymbolIt == m_libSymbols.end() )
3995 {
3996 // TODO: e.g. can depend on Template (RECORD=39
3997 m_errorMessages.emplace( wxString::Format( wxT( "Rectangle's owner (%d) not found." ),
3998 elem.ownerindex ),
4000 return;
4001 }
4002
4003 symbol = libSymbolIt->second;
4004 schsym = m_symbols.at( libSymbolIt->first );
4005 }
4006
4008 symbol->AddDrawItem( rect, false );
4009
4010 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
4011
4012 if( symbol->GetBodyStyleCount() > 1 )
4013 {
4014 if( !aSymbol.empty() )
4015 {
4016 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4017 }
4018 else
4019 {
4020 const auto& compIt = m_altiumComponents.find( elem.ownerindex );
4021
4022 if( compIt != m_altiumComponents.end() && compIt->second.displaymodecount > 1 )
4023 rect->SetBodyStyle( elem.ownerpartdisplaymode + 1 );
4024 }
4025 }
4026
4027 if( !schsym )
4028 {
4029 rect->SetPosition( sheetTopRight );
4030 rect->SetEnd( sheetBottomLeft );
4031 }
4032 else
4033 {
4034 rect->SetPosition( GetRelativePosition( sheetTopRight, schsym ) );
4035 rect->SetEnd( GetRelativePosition( sheetBottomLeft, schsym ) );
4036 }
4037
4040 }
4041}
4042
4043
4044void SCH_IO_ALTIUM::ParseSheetSymbol( int aIndex, const std::map<wxString, wxString>& aProperties )
4045{
4046 ASCH_SHEET_SYMBOL elem( aProperties );
4047
4048 SCH_SHEET* sheet = new SCH_SHEET( getCurrentSheet(), elem.location + m_sheetOffset, elem.size );
4049
4050 sheet->SetBorderColor( GetColorFromInt( elem.color ) );
4051
4052 if( elem.isSolid )
4054
4055 sheet->SetFlags( IS_NEW );
4056
4057 SCH_SCREEN* currentScreen = getCurrentScreen();
4058 wxCHECK( currentScreen, /* void */ );
4059 currentScreen->Append( sheet );
4060
4061 SCH_SHEET_PATH sheetpath = m_sheetPath;
4062 sheetpath.push_back( sheet );
4063
4064 // We'll update later if we find a pageNumber record for it.
4065 sheetpath.SetPageNumber( "#" );
4066
4067 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
4068 wxCHECK( rootScreen, /* void */ );
4069
4070 SCH_SHEET_INSTANCE sheetInstance;
4071
4072 sheetInstance.m_Path = sheetpath.Path();
4073 sheetInstance.m_PageNumber = wxT( "#" );
4074
4075 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
4076 m_sheets.insert( { aIndex, sheet } );
4077}
4078
4079
4080void SCH_IO_ALTIUM::ParseSheetEntry( const std::map<wxString, wxString>& aProperties )
4081{
4082 ASCH_SHEET_ENTRY elem( aProperties );
4083
4084 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4085
4086 if( sheetIt == m_sheets.end() )
4087 {
4088 m_errorMessages.emplace( wxString::Format( wxT( "Sheet entry's owner (%d) not found." ), elem.ownerindex ),
4090 return;
4091 }
4092
4093 SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheetIt->second );
4094 sheetIt->second->AddPin( sheetPin );
4095
4096 wxString pinName = elem.name;
4097
4098 if( !elem.harnessType.IsEmpty() )
4099 pinName += wxT( "{" ) + elem.harnessType + wxT( "}" );
4100
4101 sheetPin->SetText( pinName );
4103 //sheetPin->SetSpinStyle( getSpinStyle( term.OrientAngle, false ) );
4104 //sheetPin->SetPosition( getKiCadPoint( term.Position ) );
4105
4106 VECTOR2I pos = sheetIt->second->GetPosition();
4107 VECTOR2I size = sheetIt->second->GetSize();
4108
4109 switch( elem.side )
4110 {
4111 default:
4113 sheetPin->SetPosition( { pos.x, pos.y + elem.distanceFromTop } );
4114 sheetPin->SetSpinStyle( SPIN_STYLE::LEFT );
4115 sheetPin->SetSide( SHEET_SIDE::LEFT );
4116 break;
4117
4119 sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.distanceFromTop } );
4120 sheetPin->SetSpinStyle( SPIN_STYLE::RIGHT );
4121 sheetPin->SetSide( SHEET_SIDE::RIGHT );
4122 break;
4123
4125 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y } );
4126 sheetPin->SetSpinStyle( SPIN_STYLE::UP );
4127 sheetPin->SetSide( SHEET_SIDE::TOP );
4128 break;
4129
4131 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y + size.y } );
4132 sheetPin->SetSpinStyle( SPIN_STYLE::BOTTOM );
4133 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
4134 break;
4135 }
4136
4137 switch( elem.iotype )
4138 {
4139 default:
4142 break;
4143
4146 break;
4147
4150 break;
4151
4154 break;
4155 }
4156}
4157
4158
4160 REPORTER* aReporter )
4161{
4163 {
4165 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4166 line1->AddPoint( { 0, 0 } );
4167 line1->AddPoint( { 0, schIUScale.MilsToIU( 50 ) } );
4168 aKsymbol->AddDrawItem( line1, false );
4169
4170 if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE )
4171 {
4173 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
4174 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 75 ) } );
4175 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 25 ), 0 ) );
4176 aKsymbol->AddDrawItem( circle, false );
4177 }
4178 else
4179 {
4181 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4182 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4183 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
4184 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4185 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4186 aKsymbol->AddDrawItem( line2, false );
4187 }
4188
4189 return { 0, schIUScale.MilsToIU( 150 ) };
4190 }
4191 else if( aStyle == ASCH_POWER_PORT_STYLE::WAVE )
4192 {
4194 line->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4195 line->AddPoint( { 0, 0 } );
4196 line->AddPoint( { 0, schIUScale.MilsToIU( 72 ) } );
4197 aKsymbol->AddDrawItem( line, false );
4198
4200 bezier->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
4201 bezier->SetStart( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 50 ) } );
4202 bezier->SetBezierC1( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 87 ) } );
4203 bezier->SetBezierC2( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 63 ) } );
4204 bezier->SetEnd( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 100 ) } );
4205 aKsymbol->AddDrawItem( bezier, false );
4206
4207 return { 0, schIUScale.MilsToIU( 150 ) };
4208 }
4209 else if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND
4211 || aStyle == ASCH_POWER_PORT_STYLE::EARTH
4213 {
4215 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4216 line1->AddPoint( { 0, 0 } );
4217 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
4218 aKsymbol->AddDrawItem( line1, false );
4219
4221 {
4223 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4224 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4225 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4226 aKsymbol->AddDrawItem( line2, false );
4227
4229 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4230 line3->AddPoint( { schIUScale.MilsToIU( -70 ), schIUScale.MilsToIU( 130 ) } );
4231 line3->AddPoint( { schIUScale.MilsToIU( 70 ), schIUScale.MilsToIU( 130 ) } );
4232 aKsymbol->AddDrawItem( line3, false );
4233
4235 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4236 line4->AddPoint( { schIUScale.MilsToIU( -40 ), schIUScale.MilsToIU( 160 ) } );
4237 line4->AddPoint( { schIUScale.MilsToIU( 40 ), schIUScale.MilsToIU( 160 ) } );
4238 aKsymbol->AddDrawItem( line4, false );
4239
4241 line5->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4242 line5->AddPoint( { schIUScale.MilsToIU( -10 ), schIUScale.MilsToIU( 190 ) } );
4243 line5->AddPoint( { schIUScale.MilsToIU( 10 ), schIUScale.MilsToIU( 190 ) } );
4244 aKsymbol->AddDrawItem( line5, false );
4245 }
4246 else if( aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND )
4247 {
4249 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4250 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4251 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4252 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 200 ) } );
4253 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4254 aKsymbol->AddDrawItem( line2, false );
4255 }
4256 else if( aStyle == ASCH_POWER_PORT_STYLE::EARTH )
4257 {
4259 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4260 line2->AddPoint( { schIUScale.MilsToIU( -150 ), schIUScale.MilsToIU( 200 ) } );
4261 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
4262 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
4263 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 200 ) } );
4264 aKsymbol->AddDrawItem( line2, false );
4265
4267 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4268 line3->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4269 line3->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 200 ) } );
4270 aKsymbol->AddDrawItem( line3, false );
4271 }
4272 else // ASCH_POWER_PORT_STYLE::GOST_ARROW
4273 {
4275 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4276 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
4277 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
4278 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
4279 aKsymbol->AddDrawItem( line2, false );
4280
4281 return { 0, schIUScale.MilsToIU( 150 ) }; // special case
4282 }
4283
4284 return { 0, schIUScale.MilsToIU( 250 ) };
4285 }
4288 {
4290 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4291 line1->AddPoint( { 0, 0 } );
4292 line1->AddPoint( { 0, schIUScale.MilsToIU( 160 ) } );
4293 aKsymbol->AddDrawItem( line1, false );
4294
4296 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4297 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 160 ) } );
4298 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 160 ) } );
4299 aKsymbol->AddDrawItem( line2, false );
4300
4302 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4303 line3->AddPoint( { schIUScale.MilsToIU( -60 ), schIUScale.MilsToIU( 200 ) } );
4304 line3->AddPoint( { schIUScale.MilsToIU( 60 ), schIUScale.MilsToIU( 200 ) } );
4305 aKsymbol->AddDrawItem( line3, false );
4306
4308 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4309 line4->AddPoint( { schIUScale.MilsToIU( -20 ), schIUScale.MilsToIU( 240 ) } );
4310 line4->AddPoint( { schIUScale.MilsToIU( 20 ), schIUScale.MilsToIU( 240 ) } );
4311 aKsymbol->AddDrawItem( line4, false );
4312
4314 return { 0, schIUScale.MilsToIU( -300 ) };
4315
4317 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4318 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 160 ) } );
4319 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 120 ), 0 ) );
4320 aKsymbol->AddDrawItem( circle, false );
4321
4322 return { 0, schIUScale.MilsToIU( 350 ) };
4323 }
4324 else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_BAR )
4325 {
4327 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4328 line1->AddPoint( { 0, 0 } );
4329 line1->AddPoint( { 0, schIUScale.MilsToIU( 200 ) } );
4330 aKsymbol->AddDrawItem( line1, false );
4331
4333 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4334 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 200 ) } );
4335 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 200 ) } );
4336 aKsymbol->AddDrawItem( line2, false );
4337
4338 return { 0, schIUScale.MilsToIU( 250 ) };
4339 }
4340 else
4341 {
4342 if( aStyle != ASCH_POWER_PORT_STYLE::BAR )
4343 {
4344 aReporter->Report( _( "Power Port with unknown style imported as 'Bar' type." ),
4346 }
4347
4349 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4350 line1->AddPoint( { 0, 0 } );
4351 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
4352 aKsymbol->AddDrawItem( line1, false );
4353
4355 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4356 line2->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 100 ) } );
4357 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 100 ) } );
4358 aKsymbol->AddDrawItem( line2, false );
4359
4360 return { 0, schIUScale.MilsToIU( 150 ) };
4361 }
4362}
4363
4364
4365void SCH_IO_ALTIUM::ParsePowerPort( const std::map<wxString, wxString>& aProperties )
4366{
4367 ASCH_POWER_PORT elem( aProperties );
4368
4369 wxString symName( elem.text );
4370 std::string styleName( magic_enum::enum_name<ASCH_POWER_PORT_STYLE>( elem.style ) );
4371
4372 if( !styleName.empty() )
4373 symName << '_' << styleName;
4374
4375 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symName );
4376 LIB_SYMBOL* libSymbol = nullptr;
4377
4378 const auto& powerSymbolIt = m_powerSymbols.find( symName );
4379
4380 if( powerSymbolIt != m_powerSymbols.end() )
4381 {
4382 libSymbol = powerSymbolIt->second; // cache hit
4383 }
4384 else
4385 {
4386 libSymbol = new LIB_SYMBOL( wxEmptyString );
4387 libSymbol->SetGlobalPower();
4388 libSymbol->SetName( symName );
4389 libSymbol->GetReferenceField().SetText( "#PWR" );
4390 libSymbol->GetReferenceField().SetVisible( false );
4391 libSymbol->GetValueField().SetText( elem.text );
4392 libSymbol->GetValueField().SetVisible( true );
4393 libSymbol->SetDescription( wxString::Format( _( "Power symbol creates a global label with name '%s'" ),
4394 elem.text ) );
4395 libSymbol->SetKeyWords( "power-flag" );
4396 libSymbol->SetLibId( libId );
4397
4398 // generate graphic
4399 SCH_PIN* pin = new SCH_PIN( libSymbol );
4400 libSymbol->AddDrawItem( pin, false );
4401
4402 pin->SetName( elem.text );
4403 pin->SetPosition( { 0, 0 } );
4404 pin->SetLength( 0 );
4406 pin->SetVisible( false );
4407
4408 VECTOR2I valueFieldPos = HelperGeneratePowerPortGraphics( libSymbol, elem.style, m_reporter );
4409
4410 libSymbol->GetValueField().SetPosition( valueFieldPos );
4411
4412 // this has to be done after parsing the LIB_SYMBOL!
4413 m_powerSymbols.insert( { symName, libSymbol } );
4414 }
4415
4416 SCH_SCREEN* screen = getCurrentScreen();
4417 wxCHECK( screen, /* void */ );
4418
4419 SCH_SYMBOL* symbol = new SCH_SYMBOL();
4420 symbol->SetRef( &m_sheetPath, "#PWR?" );
4421 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
4422 symbol->SetValueFieldText( elem.text );
4423 symbol->SetLibId( libId );
4424 symbol->SetLibSymbol( new LIB_SYMBOL( *libSymbol ) );
4425
4426 SCH_FIELD* valueField = symbol->GetField( FIELD_T::VALUE );
4427 valueField->SetVisible( elem.showNetName );
4428 valueField->SetPosition( libSymbol->GetValueField().GetPosition() );
4429
4430 symbol->SetPosition( elem.location + m_sheetOffset );
4431
4432 switch( elem.orientation )
4433 {
4436 valueField->SetTextAngle( ANGLE_VERTICAL );
4438 break;
4439
4442 valueField->SetTextAngle( ANGLE_HORIZONTAL );
4444 break;
4445
4448 valueField->SetTextAngle( ANGLE_VERTICAL );
4450 break;
4451
4454 valueField->SetTextAngle( ANGLE_HORIZONTAL );
4456 break;
4457
4458 default:
4459 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
4460 break;
4461 }
4462
4463 screen->Append( symbol );
4464}
4465
4466
4468{
4469 ParsePortHelper( aElem );
4470}
4471
4472
4474{
4475 if( !aElem.HarnessType.IsEmpty() )
4476 {
4477 // Parse harness ports after "Additional" compound section is parsed
4478 m_altiumHarnessPortsCurrentSheet.emplace_back( aElem );
4479 return;
4480 }
4481
4482 ParsePortHelper( aElem );
4483}
4484
4485
4487{
4488 VECTOR2I start = aElem.Location + m_sheetOffset;
4489 VECTOR2I end = start;
4490
4491 switch( aElem.Style )
4492 {
4493 default:
4498 end.x += aElem.Width;
4499 break;
4500
4505 end.y -= aElem.Width;
4506 break;
4507 }
4508
4509 // Check which connection points exists in the schematic
4510 SCH_SCREEN* screen = getCurrentScreen();
4511 wxCHECK( screen, /* void */ );
4512
4513 bool startIsWireTerminal = screen->IsTerminalPoint( start, LAYER_WIRE );
4514 bool startIsBusTerminal = screen->IsTerminalPoint( start, LAYER_BUS );
4515
4516 bool endIsWireTerminal = screen->IsTerminalPoint( end, LAYER_WIRE );
4517 bool endIsBusTerminal = screen->IsTerminalPoint( end, LAYER_BUS );
4518
4519 // check if any of the points is a terminal point
4520 // TODO: there seems a problem to detect approximated connections towards component pins?
4521 bool connectionFound = startIsWireTerminal
4522 || startIsBusTerminal
4523 || endIsWireTerminal
4524 || endIsBusTerminal;
4525
4526 if( !connectionFound )
4527 {
4528 for( auto& [ _, harness ] : m_altiumHarnesses )
4529 {
4530 if( harness.m_name.CmpNoCase( aElem.HarnessType ) != 0 )
4531 continue;
4532
4533 BOX2I bbox( harness.m_location, harness.m_size );
4534 bbox.Inflate( 10 );
4535
4536 if( bbox.Contains( start ) )
4537 {
4538 startIsBusTerminal = true;
4539 connectionFound = true;
4540 break;
4541 }
4542
4543 if( bbox.Contains( end ) )
4544 {
4545 endIsBusTerminal = true;
4546 connectionFound = true;
4547 break;
4548 }
4549 }
4550
4551 if( !connectionFound )
4552 {
4553 m_errorMessages.emplace( wxString::Format( _( "Port %s has no connections." ), aElem.Name ),
4555 }
4556 }
4557
4558 // Select label position. In case both match, we will add a line later.
4559 VECTOR2I position = ( startIsWireTerminal || startIsBusTerminal ) ? start : end;
4560 SCH_LABEL_BASE* label;
4561
4562 wxString labelName = aElem.Name;
4563
4564 if( !aElem.HarnessType.IsEmpty() )
4565 labelName += wxT( "{" ) + aElem.HarnessType + wxT( "}" );
4566
4567 // TODO: detect correct label type depending on sheet settings, etc.
4568#if 1 // Set to 1 to use SCH_HIERLABEL label, 0 to use SCH_GLOBALLABEL
4569 {
4570 label = new SCH_HIERLABEL( position, labelName );
4571 }
4572#else
4573 label = new SCH_GLOBALLABEL( position, labelName );
4574
4575 // Default "Sheet References" field should be hidden, at least for now
4576 label->GetField( INTERSHEET_REFS )->SetVisible( false );
4577#endif
4578
4579 switch( aElem.IOtype )
4580 {
4581 default:
4586 }
4587
4588 switch( aElem.Style )
4589 {
4590 default:
4595 if( ( startIsWireTerminal || startIsBusTerminal ) )
4597 else
4599
4600 break;
4601
4606 if( ( startIsWireTerminal || startIsBusTerminal ) )
4607 label->SetSpinStyle( SPIN_STYLE::UP );
4608 else
4610
4611 break;
4612 }
4613
4614 label->AutoplaceFields( screen, AUTOPLACE_AUTO );
4615 label->SetFlags( IS_NEW );
4616
4617 screen->Append( label );
4618
4619 // This is a hack, for the case both connection points are valid: add a small wire
4620 if( ( startIsWireTerminal && endIsWireTerminal ) )
4621 {
4622 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
4623 wire->SetEndPoint( end );
4624 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
4625 wire->SetFlags( IS_NEW );
4626 screen->Append( wire );
4627 }
4628 else if( startIsBusTerminal && endIsBusTerminal )
4629 {
4630 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
4631 wire->SetEndPoint( end );
4632 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
4633 wire->SetFlags( IS_NEW );
4634 screen->Append( wire );
4635 }
4636}
4637
4638
4639void SCH_IO_ALTIUM::ParseNoERC( const std::map<wxString, wxString>& aProperties )
4640{
4641 ASCH_NO_ERC elem( aProperties );
4642
4643 SCH_SCREEN* screen = getCurrentScreen();
4644 wxCHECK( screen, /* void */ );
4645
4646 if( elem.isActive )
4647 {
4648 SCH_NO_CONNECT* noConnect = new SCH_NO_CONNECT( elem.location + m_sheetOffset );
4649
4650 noConnect->SetFlags( IS_NEW );
4651 screen->Append( noConnect );
4652 }
4653}
4654
4655
4656void SCH_IO_ALTIUM::ParseNetLabel( const std::map<wxString, wxString>& aProperties )
4657{
4658 ASCH_NET_LABEL elem( aProperties );
4659
4660 SCH_LABEL* label = new SCH_LABEL( elem.location + m_sheetOffset, elem.text );
4661
4662 SCH_SCREEN* screen = getCurrentScreen();
4663 wxCHECK( screen, /* void */ );
4664
4665 SetTextPositioning( label, elem.justification, elem.orientation );
4666
4667 label->SetFlags( IS_NEW );
4668 screen->Append( label );
4669}
4670
4671
4672void SCH_IO_ALTIUM::ParseBus( const std::map<wxString, wxString>& aProperties )
4673{
4674 ASCH_BUS elem( aProperties );
4675
4676 SCH_SCREEN* screen = getCurrentScreen();
4677 wxCHECK( screen, /* void */ );
4678
4679 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
4680 {
4681 SCH_LINE* bus = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_BUS );
4682 bus->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
4683 bus->SetLineWidth( elem.lineWidth );
4684
4685 bus->SetFlags( IS_NEW );
4686 screen->Append( bus );
4687 }
4688}
4689
4690
4691void SCH_IO_ALTIUM::ParseWire( const std::map<wxString, wxString>& aProperties )
4692{
4693 ASCH_WIRE elem( aProperties );
4694
4695 SCH_SCREEN* screen = getCurrentScreen();
4696 wxCHECK( screen, /* void */ );
4697
4698 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
4699 {
4700 SCH_LINE* wire = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_WIRE );
4701 wire->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
4702 // wire->SetLineWidth( elem.lineWidth );
4703
4704 wire->SetFlags( IS_NEW );
4705 screen->Append( wire );
4706 }
4707}
4708
4709
4710void SCH_IO_ALTIUM::ParseJunction( const std::map<wxString, wxString>& aProperties )
4711{
4712 SCH_SCREEN* screen = getCurrentScreen();
4713 wxCHECK( screen, /* void */ );
4714
4715 ASCH_JUNCTION elem( aProperties );
4716
4717 SCH_JUNCTION* junction = new SCH_JUNCTION( elem.location + m_sheetOffset );
4718
4719 junction->SetFlags( IS_NEW );
4720 screen->Append( junction );
4721}
4722
4723
4724void SCH_IO_ALTIUM::ParseImage( const std::map<wxString, wxString>& aProperties )
4725{
4726 ASCH_IMAGE elem( aProperties );
4727
4728 const auto& component = m_altiumComponents.find( elem.ownerindex );
4729
4730 //Hide the image if it is owned by a component but the part id do not match
4731 if( component != m_altiumComponents.end()
4732 && component->second.currentpartid != elem.ownerpartid )
4733 return;
4734
4735 VECTOR2I center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
4736 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
4737 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
4738
4739 SCH_SCREEN* screen = getCurrentScreen();
4740 wxCHECK( screen, /* void */ );
4741
4742 if( elem.embedimage )
4743 {
4744 const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
4745
4746 if( !storageFile )
4747 {
4748 m_errorMessages.emplace( wxString::Format( _( "Embedded file '%s' not found in "
4749 "storage." ),
4750 elem.filename ),
4752 return;
4753 }
4754
4755 wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
4756
4757 // As wxZlibInputStream is not seekable, we need to write a temporary file
4758 wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
4759 wxZlibInputStream zlibInputStream( fileStream );
4760 wxFFileOutputStream outputStream( storagePath );
4761 outputStream.Write( zlibInputStream );
4762
4763 // wxFFileOutputStream::IsOk() reports the closed stream as not-ok, so capture the write
4764 // result before closing.
4765 const bool writeOk = outputStream.IsOk();
4766 const bool closeOk = outputStream.Close();
4767
4768 if( storagePath.IsEmpty() || !writeOk || !closeOk )
4769 {
4770 m_errorMessages.emplace(
4771 wxString::Format( _( "Could not write a temporary file while extracting the "
4772 "embedded image '%s'." ),
4773 elem.filename ),
4775
4776 if( !storagePath.IsEmpty() )
4777 wxRemoveFile( storagePath );
4778
4779 return;
4780 }
4781
4782 bool readOk;
4783
4784 {
4785 // wxImage emits its own log spam on a failed read; we report our own errors instead.
4786 wxLogNull noLog;
4787 readOk = refImage.ReadImageFile( storagePath );
4788 }
4789
4790 if( !readOk )
4791 {
4792 m_errorMessages.emplace(
4793 wxString::Format( _( "Failed to read embedded image '%s'. "
4794 "The image data may be corrupt or in an "
4795 "unsupported format." ),
4796 elem.filename ),
4798 wxRemoveFile( storagePath );
4799 return;
4800 }
4801
4802 // Remove temporary file
4803 wxRemoveFile( storagePath );
4804 }
4805 else
4806 {
4807 if( !wxFileExists( elem.filename ) )
4808 {
4809 m_errorMessages.emplace(
4810 wxString::Format( _( "Could not find image file '%s'. The file "
4811 "path may be specific to a different operating "
4812 "system." ),
4813 elem.filename ),
4815 return;
4816 }
4817
4818 bool readOk;
4819
4820 {
4821 wxLogNull noLog;
4822 readOk = refImage.ReadImageFile( elem.filename );
4823 }
4824
4825 if( !readOk )
4826 {
4827 m_errorMessages.emplace(
4828 wxString::Format( _( "Failed to read image file '%s'. The image "
4829 "may be corrupt or in an unsupported "
4830 "format." ),
4831 elem.filename ),
4833 return;
4834 }
4835 }
4836
4837 // we only support one scale, thus we need to select one in case it does not keep aspect ratio
4838 const VECTOR2I currentImageSize = refImage.GetSize();
4839 const VECTOR2I expectedImageSize = elem.location - elem.corner;
4840 const double scaleX = std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
4841 const double scaleY = std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
4842 refImage.SetImageScale( std::min( scaleX, scaleY ) );
4843
4844 bitmap->SetFlags( IS_NEW );
4845 screen->Append( bitmap.release() );
4846}
4847
4848
4849void SCH_IO_ALTIUM::ParseSheet( const std::map<wxString, wxString>& aProperties )
4850{
4851 m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
4852
4853 SCH_SCREEN* screen = getCurrentScreen();
4854 wxCHECK( screen, /* void */ );
4855
4856 PAGE_INFO pageInfo;
4857
4858 bool isPortrait = m_altiumSheet->sheetOrientation == ASCH_SHEET_WORKSPACEORIENTATION::PORTRAIT;
4859
4860 if( m_altiumSheet->useCustomSheet )
4861 {
4862 PAGE_INFO::SetCustomWidthMils( schIUScale.IUToMils( m_altiumSheet->customSize.x ) );
4863 PAGE_INFO::SetCustomHeightMils( schIUScale.IUToMils( m_altiumSheet->customSize.y ) );
4864 pageInfo.SetType( PAGE_SIZE_TYPE::User, isPortrait );
4865 }
4866 else
4867 {
4868 switch( m_altiumSheet->sheetSize )
4869 {
4870 default:
4871 case ASCH_SHEET_SIZE::A4: pageInfo.SetType( "A4", isPortrait ); break;
4872 case ASCH_SHEET_SIZE::A3: pageInfo.SetType( "A3", isPortrait ); break;
4873 case ASCH_SHEET_SIZE::A2: pageInfo.SetType( "A2", isPortrait ); break;
4874 case ASCH_SHEET_SIZE::A1: pageInfo.SetType( "A1", isPortrait ); break;
4875 case ASCH_SHEET_SIZE::A0: pageInfo.SetType( "A0", isPortrait ); break;
4876 case ASCH_SHEET_SIZE::A: pageInfo.SetType( "A", isPortrait ); break;
4877 case ASCH_SHEET_SIZE::B: pageInfo.SetType( "B", isPortrait ); break;
4878 case ASCH_SHEET_SIZE::C: pageInfo.SetType( "C", isPortrait ); break;
4879 case ASCH_SHEET_SIZE::D: pageInfo.SetType( "D", isPortrait ); break;
4880 case ASCH_SHEET_SIZE::E: pageInfo.SetType( "E", isPortrait ); break;
4881 case ASCH_SHEET_SIZE::LETTER: pageInfo.SetType( "USLetter", isPortrait ); break;
4882 case ASCH_SHEET_SIZE::LEGAL: pageInfo.SetType( "USLegal", isPortrait ); break;
4883 case ASCH_SHEET_SIZE::TABLOID: pageInfo.SetType( "A3", isPortrait ); break;
4884 case ASCH_SHEET_SIZE::ORCAD_A: pageInfo.SetType( "A", isPortrait ); break;
4885 case ASCH_SHEET_SIZE::ORCAD_B: pageInfo.SetType( "B", isPortrait ); break;
4886 case ASCH_SHEET_SIZE::ORCAD_C: pageInfo.SetType( "C", isPortrait ); break;
4887 case ASCH_SHEET_SIZE::ORCAD_D: pageInfo.SetType( "D", isPortrait ); break;
4888 case ASCH_SHEET_SIZE::ORCAD_E: pageInfo.SetType( "E", isPortrait ); break;
4889 }
4890 }
4891
4892 screen->SetPageSettings( pageInfo );
4893
4894 m_sheetOffset = { 0, pageInfo.GetHeightIU( schIUScale.IU_PER_MILS ) };
4895}
4896
4897
4898void SCH_IO_ALTIUM::ParseSheetName( const std::map<wxString, wxString>& aProperties )
4899{
4900 ASCH_SHEET_NAME elem( aProperties );
4901 SCH_SCREEN* currentScreen = getCurrentScreen();
4902
4903 wxCHECK( currentScreen, /* void */ );
4904
4905 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4906
4907 if( sheetIt == m_sheets.end() )
4908 {
4909 m_errorMessages.emplace( wxString::Format( wxT( "Sheetname's owner (%d) not found." ),
4910 elem.ownerindex ),
4912 return;
4913 }
4914
4915 wxString baseName = elem.text;
4916 baseName.Replace( wxT( "/" ), wxT( "_" ) );
4917
4918 wxString sheetName = baseName;
4919 std::set<wxString> sheetNames;
4920
4921 for( EDA_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
4922 {
4923 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
4924 sheetNames.insert( sheet->GetName() );
4925 }
4926
4927 for( int ii = 1; ; ++ii )
4928 {
4929 if( sheetNames.find( sheetName ) == sheetNames.end() )
4930 break;
4931
4932 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
4933 }
4934
4935 SCH_FIELD* sheetNameField = sheetIt->second->GetField( FIELD_T::SHEET_NAME );
4936
4937 sheetNameField->SetPosition( elem.location + m_sheetOffset );
4938 sheetNameField->SetText( sheetName );
4939 sheetNameField->SetVisible( !elem.isHidden );
4941}
4942
4943
4944void SCH_IO_ALTIUM::ParseFileName( const std::map<wxString, wxString>& aProperties )
4945{
4946 ASCH_FILE_NAME elem( aProperties );
4947
4948 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4949
4950 if( sheetIt == m_sheets.end() )
4951 {
4952 m_errorMessages.emplace( wxString::Format( wxT( "Filename's owner (%d) not found." ),
4953 elem.ownerindex ),
4955 return;
4956 }
4957
4958 SCH_FIELD* filenameField = sheetIt->second->GetField( FIELD_T::SHEET_FILENAME );
4959
4960 filenameField->SetPosition( elem.location + m_sheetOffset );
4961
4962 // Keep the filename of the Altium file until after the file is actually loaded.
4963 filenameField->SetText( elem.text );
4964 filenameField->SetVisible( !elem.isHidden );
4966}
4967
4968
4969void SCH_IO_ALTIUM::ParseDesignator( const std::map<wxString, wxString>& aProperties )
4970{
4971 ASCH_DESIGNATOR elem( aProperties );
4972
4973 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4974
4975 if( libSymbolIt == m_libSymbols.end() )
4976 {
4977 // TODO: e.g. can depend on Template (RECORD=39
4978 m_errorMessages.emplace( wxString::Format( wxT( "Designator's owner (%d) not found." ),
4979 elem.ownerindex ),
4981 return;
4982 }
4983
4984 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4985 SCH_SHEET_PATH sheetpath;
4986
4987 SCH_SCREEN* screen = getCurrentScreen();
4988 wxCHECK( screen, /* void */ );
4989
4990 // Graphics symbols have no reference. '#GRAPHIC' allows them to not have footprint associated.
4991 // Note: not all unnamed imported symbols are necessarily graphics.
4992 bool emptyRef = elem.text.IsEmpty();
4993 symbol->SetRef( &m_sheetPath, emptyRef ? wxString( wxS( "#GRAPHIC" ) ) : elem.text );
4994
4995 // I am not sure value and ref should be invisible just because emptyRef is true
4996 // I have examples with this criteria fully incorrect.
4997 bool visible = !emptyRef;
4998
4999 symbol->GetField( FIELD_T::VALUE )->SetVisible( visible );
5000
5001 SCH_FIELD* field = symbol->GetField( FIELD_T::REFERENCE );
5002 field->SetVisible( visible );
5003 field->SetPosition( elem.location + m_sheetOffset );
5004 SetTextPositioning( field, elem.justification, elem.orientation );
5005
5006 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
5007
5008 if( altiumSymIt != m_altiumComponents.end() )
5009 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
5010}
5011
5012
5013void SCH_IO_ALTIUM::ParseLibDesignator( const std::map<wxString, wxString>& aProperties,
5014 std::vector<LIB_SYMBOL*>& aSymbol,
5015 std::vector<int>& aFontSizes )
5016{
5017 ASCH_DESIGNATOR elem( aProperties );
5018
5019 if( elem.ownerpartdisplaymode != 0 )
5020 return;
5021
5022 for( LIB_SYMBOL* symbol : aSymbol )
5023 {
5024 bool emptyRef = elem.text.IsEmpty();
5025 SCH_FIELD& refField = symbol->GetReferenceField();
5026
5027 if( emptyRef )
5028 refField.SetText( wxT( "X" ) );
5029 else
5030 refField.SetText( elem.text.BeforeLast( '?' ) ); // remove the '?' at the end for KiCad-style
5031
5032 refField.SetPosition( elem.location );
5033 SetTextPositioning( &refField, elem.justification, elem.orientation );
5034
5035 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
5036 {
5037 int size = aFontSizes[elem.fontId - 1];
5038 refField.SetTextSize( { size, size } );
5039 }
5040 }
5041}
5042
5043
5044void SCH_IO_ALTIUM::ParseBusEntry( const std::map<wxString, wxString>& aProperties )
5045{
5046 ASCH_BUS_ENTRY elem( aProperties );
5047
5048 SCH_SCREEN* screen = getCurrentScreen();
5049 wxCHECK( screen, /* void */ );
5050
5051 SCH_BUS_WIRE_ENTRY* busWireEntry = new SCH_BUS_WIRE_ENTRY( elem.location + m_sheetOffset );
5052
5053 VECTOR2I vector = elem.corner - elem.location;
5054 busWireEntry->SetSize( { vector.x, vector.y } );
5055
5056 busWireEntry->SetFlags( IS_NEW );
5057 screen->Append( busWireEntry );
5058}
5059
5060
5061void SCH_IO_ALTIUM::ParseParameter( const std::map<wxString, wxString>& aProperties )
5062{
5063 ASCH_PARAMETER elem( aProperties );
5064
5065 // TODO: fill in replacements from variant, sheet and project
5066 static const std::map<wxString, wxString> variableMap = {
5067 { "COMMENT", "VALUE" },
5068 { "VALUE", "ALTIUM_VALUE" },
5069 };
5070
5071 if( elem.ownerindex <= 0 )
5072 {
5073 // This is some sheet parameter
5074 if( elem.text == "*" )
5075 return; // indicates parameter not set?
5076
5077 wxString paramName = elem.name.Upper();
5078
5079 if( paramName == "SHEETNUMBER" )
5080 {
5081 m_sheetPath.SetPageNumber( elem.text );
5082 }
5083 else if( paramName == "TITLE" )
5084 {
5085 m_currentTitleBlock->SetTitle( elem.text );
5086 }
5087 else if( paramName == "REVISION" )
5088 {
5089 m_currentTitleBlock->SetRevision( elem.text );
5090 }
5091 else if( paramName == "DATE" )
5092 {
5093 m_currentTitleBlock->SetDate( elem.text );
5094 }
5095 else if( paramName == "COMPANYNAME" )
5096 {
5097 m_currentTitleBlock->SetCompany( elem.text );
5098 }
5099 else
5100 {
5101 m_schematic->Project().GetTextVars()[ paramName ] = elem.text;
5102 }
5103 }
5104 else
5105 {
5106 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
5107
5108 if( libSymbolIt == m_libSymbols.end() )
5109 {
5110 // TODO: e.g. can depend on Template (RECORD=39
5111 return;
5112 }
5113
5114 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
5115 SCH_FIELD* field = nullptr;
5116 wxString upperName = elem.name.Upper();
5117
5118 if( upperName == "COMMENT" )
5119 {
5120 field = symbol->GetField( FIELD_T::VALUE );
5121 }
5122 else
5123 {
5124 wxString fieldName = elem.name.Upper();
5125
5126 if( fieldName.IsEmpty() )
5127 {
5128 int disambiguate = 1;
5129
5130 while( 1 )
5131 {
5132 fieldName = wxString::Format( "ALTIUM_UNNAMED_%d", disambiguate++ );
5133
5134 if( !symbol->GetField( fieldName ) )
5135 break;
5136 }
5137 }
5138 else if( fieldName == "VALUE" )
5139 {
5140 fieldName = "ALTIUM_VALUE";
5141 }
5142
5143 field = symbol->AddField( SCH_FIELD( symbol, FIELD_T::USER, fieldName ) );
5144 }
5145
5146 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
5147 field->SetText( kicadText );
5148 field->SetPosition( elem.location + m_sheetOffset );
5149 field->SetVisible( !elem.isHidden );
5150 field->SetNameShown( elem.isShowName );
5151 SetTextPositioning( field, elem.justification, elem.orientation );
5152
5153 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
5154
5155 if( altiumSymIt != m_altiumComponents.end() )
5156 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
5157 }
5158}
5159
5160
5161void SCH_IO_ALTIUM::ParseLibParameter( const std::map<wxString, wxString>& aProperties,
5162 std::vector<LIB_SYMBOL*>& aSymbol,
5163 std::vector<int>& aFontSizes )
5164{
5165 ASCH_PARAMETER elem( aProperties );
5166
5167 if( elem.ownerpartdisplaymode != 0 )
5168 return;
5169
5170 // Part ID 1 is the current library part.
5171 // Part ID ALTIUM_COMPONENT_NONE(-1) means all parts
5172 // If a parameter is assigned to a specific element such as a pin,
5173 // we will need to handle it here.
5174 // TODO: Handle HIDDENNETNAME property (others?)
5175 if( elem.ownerpartid != 1 && elem.ownerpartid != ALTIUM_COMPONENT_NONE )
5176 return;
5177
5178 // If ownerindex is populated, this is parameter belongs to a subelement (e.g. pin).
5179 // Ignore for now.
5180 // TODO: Update this when KiCad supports parameters for any object
5181 if( elem.ownerindex != ALTIUM_COMPONENT_NONE )
5182 return;
5183
5184 // TODO: fill in replacements from variant, sheet and project
5185 // N.B. We do not keep the Altium "VALUE" variable here because
5186 // we don't have a way to assign variables to specific symbols
5187 std::map<wxString, wxString> variableMap = {
5188 { "COMMENT", "VALUE" },
5189 };
5190
5191 for( LIB_SYMBOL* libSymbol : aSymbol )
5192 {
5193 SCH_FIELD* field = nullptr;
5194 wxString upperName = elem.name.Upper();
5195
5196 if( upperName == "COMMENT" )
5197 {
5198 field = &libSymbol->GetValueField();
5199 }
5200 else
5201 {
5202 wxString fieldNameStem = elem.name;
5203 wxString fieldName = fieldNameStem;
5204 int disambiguate = 1;
5205
5206 if( fieldName.IsEmpty() )
5207 {
5208 fieldNameStem = "ALTIUM_UNNAMED";
5209 fieldName = "ALTIUM_UNNAMED_1";
5210 disambiguate = 2;
5211 }
5212 else if( upperName == "VALUE" )
5213 {
5214 fieldNameStem = "ALTIUM_VALUE";
5215 fieldName = "ALTIUM_VALUE";
5216 }
5217
5218 // Avoid adding duplicate fields
5219 while( libSymbol->GetField( fieldName ) )
5220 fieldName = wxString::Format( "%s_%d", fieldNameStem, disambiguate++ );
5221
5222 SCH_FIELD* new_field = new SCH_FIELD( libSymbol, FIELD_T::USER, fieldName );
5223 libSymbol->AddField( new_field );
5224 field = new_field;
5225 }
5226
5227 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
5228 field->SetText( kicadText );
5229
5230 field->SetTextPos( elem.location );
5231 SetTextPositioning( field, elem.justification, elem.orientation );
5232 field->SetVisible( !elem.isHidden );
5233
5234 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
5235 {
5236 int size = aFontSizes[elem.fontId - 1];
5237 field->SetTextSize( { size, size } );
5238 }
5239 else
5240 {
5241 int size = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
5242 field->SetTextSize( { size, size } );
5243 }
5244
5245 }
5246}
5247
5248
5250 const std::map<wxString, wxString>& aProperties )
5251{
5252 ASCH_IMPLEMENTATION_LIST elem( aProperties );
5253
5254 m_altiumImplementationList.emplace( aIndex, elem.ownerindex );
5255}
5256
5257
5258void SCH_IO_ALTIUM::ParseImplementation( const std::map<wxString, wxString>& aProperties,
5259 std::vector<LIB_SYMBOL*>& aSymbol )
5260{
5261 ASCH_IMPLEMENTATION elem( aProperties );
5262
5263 if( elem.type != wxS( "PCBLIB" ) )
5264 return;
5265
5266 // For schematic files, we need to check if the model is current.
5267 if( aSymbol.size() == 0 && !elem.isCurrent )
5268 return;
5269
5270 // For IntLibs we want to use the same lib name for footprints. Otherwise the model data
5271 // file names the source PcbLib as a Windows path; take its base name so the footprint id
5272 // matches the nickname project import registers in the footprint library table.
5273 wxString libName = m_isIntLib ? m_libName : wxFileName( elem.libname, wxPATH_WIN ).GetName();
5274
5275 wxArrayString fpFilters;
5276 fpFilters.Add( wxString::Format( wxS( "*%s*" ), elem.name ) );
5277
5278 // Parse the footprint fields for the library symbol
5279 if( !aSymbol.empty() )
5280 {
5281 for( LIB_SYMBOL* symbol : aSymbol )
5282 {
5283 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
5284
5285 symbol->SetFPFilters( fpFilters );
5286 symbol->GetField( FIELD_T::FOOTPRINT )->SetText( fpLibId.Format() );
5287 }
5288
5289 return;
5290 }
5291
5292 const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex );
5293
5294 if( implementationOwnerIt == m_altiumImplementationList.end() )
5295 {
5296 m_errorMessages.emplace( wxString::Format( wxT( "Implementation's owner (%d) not found." ),
5297 elem.ownerindex ),
5299 return;
5300 }
5301
5302 const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second );
5303
5304 if( libSymbolIt == m_libSymbols.end() )
5305 {
5306 m_errorMessages.emplace( wxString::Format( wxT( "Footprint's owner (%d) not found." ),
5307 implementationOwnerIt->second ),
5309 return;
5310 }
5311
5312 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
5313
5314 libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it
5315
5316 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
5317
5318 symbol->SetFootprintFieldText( fpLibId.Format() );
5319}
5320
5321
5322
5323std::vector<LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibComponent( const std::map<wxString,
5324 wxString>& aProperties )
5325{
5326 ASCH_SYMBOL elem( aProperties );
5327
5328 LIB_SYMBOL* symbol = new LIB_SYMBOL( wxEmptyString );
5329 symbol->SetName( elem.libreference );
5330
5331 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symbol->GetName() );
5332 symbol->SetDescription( elem.componentdescription );
5333 symbol->SetLibId( libId );
5334
5335 // Altium PARTCOUNT is one more than the actual unit count. The property may be missing
5336 // (defaults to 0) or otherwise nonsensical, so clamp to a minimum of 1 unit.
5337 symbol->SetUnitCount( std::max( 1, elem.partcount - 1 ), true );
5338
5339 if( elem.displaymodecount > 1 )
5340 {
5341 std::vector<wxString> bodyStyleNames;
5342
5343 for( int i = 0; i < elem.displaymodecount; i++ )
5344 bodyStyleNames.push_back( wxString::Format( "Display %d", i + 1 ) );
5345
5346 symbol->SetBodyStyleNames( bodyStyleNames );
5347 }
5348
5349 return { symbol };
5350}
5351
5352
5355{
5357 std::vector<int> fontSizes;
5358 struct SYMBOL_PIN_FRAC
5359 {
5360 int x_frac;
5361 int y_frac;
5362 int len_frac;
5363 };
5364
5365 ParseLibHeader( aAltiumLibFile, fontSizes );
5366
5367 std::map<wxString, ALTIUM_SYMBOL_DATA> syms = aAltiumLibFile.GetLibSymbols( nullptr );
5368
5369 for( auto& [name, entry] : syms )
5370 {
5371 std::map<int, SYMBOL_PIN_FRAC> pinFracs;
5372
5373 if( entry.m_pinsFrac )
5374 {
5375 auto parse_binary_pin_frac =
5376 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
5377 {
5378 std::map<wxString, wxString> result;
5379 ALTIUM_COMPRESSED_READER cmpreader( binaryData );
5380
5381 std::pair<int, std::string*> pinFracData = cmpreader.ReadCompressedString();
5382
5383 ALTIUM_BINARY_READER binreader( *pinFracData.second );
5384 SYMBOL_PIN_FRAC pinFrac;
5385
5386 pinFrac.x_frac = binreader.ReadInt32();
5387 pinFrac.y_frac = binreader.ReadInt32();
5388 pinFrac.len_frac = binreader.ReadInt32();
5389 pinFracs.insert( { pinFracData.first, pinFrac } );
5390
5391 return result;
5392 };
5393
5394 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_pinsFrac );
5395
5396 while( reader.GetRemainingBytes() > 0 )
5397 reader.ReadProperties( parse_binary_pin_frac );
5398 }
5399
5400 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_symbol );
5401 std::vector<LIB_SYMBOL*> symbols;
5402 int pin_index = 0;
5403
5404 if( reader.GetRemainingBytes() <= 0 )
5405 THROW_IO_ERROR( "LibSymbol does not contain any data" );
5406
5407 {
5408 std::map<wxString, wxString> properties = reader.ReadProperties();
5409 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
5410 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
5411
5412 if( record != ALTIUM_SCH_RECORD::COMPONENT )
5413 THROW_IO_ERROR( "LibSymbol does not start with COMPONENT record" );
5414
5415 symbols = ParseLibComponent( properties );
5416 }
5417
5418 auto handleBinaryPinLambda =
5419 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
5420 {
5421 std::map<wxString, wxString> result;
5422
5423 ALTIUM_BINARY_READER binreader( binaryData );
5424
5425 int32_t recordId = binreader.ReadInt32();
5426
5427 if( recordId != static_cast<int32_t>( ALTIUM_SCH_RECORD::PIN ) )
5428 THROW_IO_ERROR( "Binary record missing PIN record" );
5429
5430 result["RECORD"] = wxString::Format( "%d", recordId );
5431 binreader.ReadByte(); // unknown
5432 result["OWNERPARTID"] = wxString::Format( "%d", binreader.ReadInt16() );
5433 result["OWNERPARTDISPLAYMODE"] = wxString::Format( "%d", binreader.ReadByte() );
5434 result["SYMBOL_INNEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
5435 result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
5436 result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() );
5437 result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() );
5438 result["TEXT"] = binreader.ReadShortPascalString();
5439 binreader.ReadByte(); // unknown
5440 result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() );
5441 result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() );
5442 result["PINLENGTH"] = wxString::Format( "%d", binreader.ReadInt16() );
5443 result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() );
5444 result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() );
5445 result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() );
5446 result["NAME"] = binreader.ReadShortPascalString();
5447 result["DESIGNATOR"] = binreader.ReadShortPascalString();
5448 result["SWAPIDGROUP"] = binreader.ReadShortPascalString();
5449
5450 if( auto it = pinFracs.find( pin_index ); it != pinFracs.end() )
5451 {
5452 result["LOCATION.X_FRAC"] = wxString::Format( "%d", it->second.x_frac );
5453 result["LOCATION.Y_FRAC"] = wxString::Format( "%d", it->second.y_frac );
5454 result["PINLENGTH_FRAC"] = wxString::Format( "%d", it->second.len_frac );
5455 }
5456
5457 std::string partSeq = binreader.ReadShortPascalString(); // This is 'part|&|seq'
5458 std::vector<std::string> partSeqSplit = split( partSeq, "|" );
5459
5460 if( partSeqSplit.size() == 3 )
5461 {
5462 result["PART"] = partSeqSplit[0];
5463 result["SEQ"] = partSeqSplit[2];
5464 }
5465
5466 return result;
5467 };
5468
5469 while( reader.GetRemainingBytes() > 0 )
5470 {
5471 std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryPinLambda );
5472
5473 if( properties.empty() )
5474 continue;
5475
5476 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
5477 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
5478
5479 switch( record )
5480 {
5482 ParsePin( properties, symbols );
5483 pin_index++;
5484 break;
5485
5486 case ALTIUM_SCH_RECORD::LABEL: ParseLabel( properties, symbols, fontSizes ); break;
5487 case ALTIUM_SCH_RECORD::BEZIER: ParseBezier( properties, symbols ); break;
5488 case ALTIUM_SCH_RECORD::POLYLINE: ParsePolyline( properties, symbols ); break;
5489 case ALTIUM_SCH_RECORD::POLYGON: ParsePolygon( properties, symbols ); break;
5490 case ALTIUM_SCH_RECORD::ELLIPSE: ParseEllipse( properties, symbols ); break;
5491 case ALTIUM_SCH_RECORD::PIECHART: ParsePieChart( properties, symbols ); break;
5492 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: ParseRoundRectangle( properties, symbols ); break;
5493 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: ParseEllipticalArc( properties, symbols ); break;
5494 case ALTIUM_SCH_RECORD::ARC: ParseArc( properties, symbols ); break;
5495 case ALTIUM_SCH_RECORD::LINE: ParseLine( properties, symbols ); break;
5496 case ALTIUM_SCH_RECORD::RECTANGLE: ParseRectangle( properties, symbols ); break;
5497 case ALTIUM_SCH_RECORD::DESIGNATOR: ParseLibDesignator( properties, symbols, fontSizes ); break;
5498 case ALTIUM_SCH_RECORD::PARAMETER: ParseLibParameter( properties, symbols, fontSizes ); break;
5499 case ALTIUM_SCH_RECORD::TEXT_FRAME: ParseTextFrame( properties, symbols, fontSizes ); break;
5500 case ALTIUM_SCH_RECORD::IMPLEMENTATION: ParseImplementation( properties, symbols ); break;
5501
5503 break;
5504
5507 break;
5508
5510 // TODO: add support for these. They are just drawn symbols, so we can probably hardcode
5511 break;
5512
5514 // TODO: Handle images once libedit supports them
5515 break;
5516
5518 // Nothing for now. TODO: Figure out how implementation lists are generated in libs
5519 break;
5520
5521 default:
5522 m_errorMessages.emplace( wxString::Format( _( "Unknown or unexpected record id %d found in %s." ),
5523 recordId,
5524 symbols[0]->GetName() ),
5526 break;
5527 }
5528 }
5529
5530 if( reader.HasParsingError() )
5531 THROW_IO_ERROR( "stream was not parsed correctly!" );
5532
5533 if( reader.GetRemainingBytes() != 0 )
5534 THROW_IO_ERROR( "stream is not fully parsed" );
5535
5536 LIB_SYMBOL* symbol = symbols[0];
5537 symbol->FixupDrawItems();
5538 fixupSymbolPinNameNumbers( symbol );
5539
5540 SCH_FIELD& valField = symbol->GetValueField();
5541
5542 if( valField.GetText().IsEmpty() )
5543 valField.SetText( name );
5544
5545 symbol->SetName( name );
5546 ret[name] = symbol;
5547 }
5548
5549 return ret;
5550}
5551
5552
5553long long SCH_IO_ALTIUM::getLibraryTimestamp( const wxString& aLibraryPath ) const
5554{
5555 wxFileName fn( aLibraryPath );
5556
5557 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
5558 return fn.GetModificationTime().GetValue().GetValue();
5559 else
5560 return 0;
5561}
5562
5563
5564void SCH_IO_ALTIUM::ensureLoadedLibrary( const wxString& aLibraryPath,
5565 const std::map<std::string, UTF8>* aProperties )
5566{
5567 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
5568 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
5569
5570 if( m_libCache.count( aLibraryPath ) )
5571 {
5572 wxCHECK( m_timestamps.count( aLibraryPath ), /*void*/ );
5573
5574 if( m_timestamps.at( aLibraryPath ) == getLibraryTimestamp( aLibraryPath ) )
5575 return;
5576 }
5577
5578 std::vector<std::unique_ptr<ALTIUM_COMPOUND_FILE>> compoundFiles;
5579
5580 wxFileName fileName( aLibraryPath );
5581 m_libName = fileName.GetName();
5582
5583 try
5584 {
5585 if( aLibraryPath.Lower().EndsWith( wxS( ".schlib" ) ) )
5586 {
5587 m_isIntLib = false;
5588
5589 compoundFiles.push_back( std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
5590 }
5591 else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) )
5592 {
5593 m_isIntLib = true;
5594
5595 std::unique_ptr<ALTIUM_COMPOUND_FILE> intCom = std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath );
5596
5597 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> schLibFiles = intCom->EnumDir( L"SchLib" );
5598
5599 for( const auto& [schLibName, cfe] : schLibFiles )
5600 {
5601 std::unique_ptr<ALTIUM_COMPOUND_FILE> decodedStream = std::make_unique<ALTIUM_COMPOUND_FILE>();
5602
5603 if( intCom->DecodeIntLibStream( *cfe, decodedStream.get() ) )
5604 compoundFiles.emplace_back( std::move( decodedStream ) );
5605 }
5606 }
5607
5608 CASE_INSENSITIVE_MAP<LIB_SYMBOL*>& cacheMapRef = m_libCache[aLibraryPath];
5609
5610 for( const std::unique_ptr<ALTIUM_COMPOUND_FILE>& altiumSchFilePtr : compoundFiles )
5611 {
5612 CASE_INSENSITIVE_MAP<LIB_SYMBOL*> parsed = ParseLibFile( *altiumSchFilePtr );
5613 cacheMapRef.insert( parsed.begin(), parsed.end() );
5614 }
5615
5616 m_timestamps[aLibraryPath] = getLibraryTimestamp( aLibraryPath );
5617 }
5618 catch( const CFB::CFBException& exception )
5619 {
5620 THROW_IO_ERROR( exception.what() );
5621 }
5622 catch( const std::exception& exc )
5623 {
5624 THROW_IO_ERROR( wxString::Format( _( "Error parsing Altium library: %s" ), exc.what() ) );
5625 }
5626}
5627
5628
5630 std::vector<int>& aFontSizes )
5631{
5632 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "FileHeader" } );
5633
5634 if( file == nullptr )
5635 THROW_IO_ERROR( "FileHeader not found" );
5636
5637 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
5638
5639 if( reader.GetRemainingBytes() <= 0 )
5640 THROW_IO_ERROR( "FileHeader does not contain any data" );
5641
5642 std::map<wxString, wxString> properties = reader.ReadProperties();
5643
5644 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
5645
5646 if( libtype.CmpNoCase( "Protel for Windows - Schematic Library Editor Binary File Version 5.0" ) )
5647 THROW_IO_ERROR( _( "Expected Altium Schematic Library file version 5.0" ) );
5648
5649 for( auto& [key, value] : properties )
5650 {
5651 wxString upperKey = key.Upper();
5652 wxString remaining;
5653
5654 if( upperKey.StartsWith( "SIZE", &remaining ) )
5655 {
5656 if( !remaining.empty() )
5657 {
5658 int ind = wxAtoi( remaining );
5659
5660 if( static_cast<int>( aFontSizes.size() ) < ind )
5661 aFontSizes.resize( ind );
5662
5663 // Altium stores in pt. 1 pt = 1/72 inch. 1 mil = 1/1000 inch.
5664 int scaled = schIUScale.MilsToIU( wxAtoi( value ) * 72.0 / 10.0 );
5665 aFontSizes[ind - 1] = scaled;
5666 }
5667 }
5668 }
5669}
5670
5671
5672void SCH_IO_ALTIUM::doEnumerateSymbolLib( const wxString& aLibraryPath,
5673 const std::map<std::string, UTF8>* aProperties,
5674 std::function<void(const wxString&, LIB_SYMBOL*)> aInserter )
5675{
5676 ensureLoadedLibrary( aLibraryPath, aProperties );
5677
5678 bool powerSymbolsOnly = ( aProperties &&
5679 aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly ) );
5680
5681 auto it = m_libCache.find( aLibraryPath );
5682
5683 if( it != m_libCache.end() )
5684 {
5685 for( auto& [libnameStr, libSymbol] : it->second )
5686 {
5687 if( powerSymbolsOnly && !libSymbol->IsPower() )
5688 continue;
5689
5690 aInserter( libnameStr, libSymbol );
5691 }
5692 }
5693}
5694
5695
5696void SCH_IO_ALTIUM::EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
5697 const std::map<std::string, UTF8>* aProperties )
5698{
5699 doEnumerateSymbolLib( aLibraryPath, aProperties,
5700 [&]( const wxString& aStr, LIB_SYMBOL* )
5701 {
5702 aSymbolNameList.Add( aStr );
5703 } );
5704}
5705
5706
5707void SCH_IO_ALTIUM::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
5708 const wxString& aLibraryPath,
5709 const std::map<std::string, UTF8>* aProperties )
5710{
5711 doEnumerateSymbolLib( aLibraryPath, aProperties,
5712 [&]( const wxString&, LIB_SYMBOL* aSymbol )
5713 {
5714 aSymbolList.emplace_back( aSymbol );
5715 } );
5716}
5717
5718
5719LIB_SYMBOL* SCH_IO_ALTIUM::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
5720 const std::map<std::string, UTF8>* aProperties )
5721{
5722 ensureLoadedLibrary( aLibraryPath, aProperties );
5723
5724 auto it = m_libCache.find( aLibraryPath );
5725
5726 if( it != m_libCache.end() )
5727 {
5728 auto it2 = it->second.find( aAliasName );
5729
5730 if( it2 != it->second.end() )
5731 return it2->second;
5732 }
5733
5734 return nullptr;
5735}
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
UTF8 Format() const
Definition lib_id.cpp:132
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:205
Define a library symbol object.
Definition lib_symbol.h:80
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:835
wxString GetName() const override
Definition lib_symbol.h:142
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:378
int GetBodyStyleCount() const override
Definition lib_symbol.h:823
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:382
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.
static bool IsBusLabel(const wxString &aLabel)
Test if aLabel has a bus notation.
VECTOR2I GetPosition() const override
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
Definition sch_field.h:219
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
void ParseFileHeader(const ALTIUM_COMPOUND_FILE &aAltiumSchFile)
void ParseSignalHarness(const std::map< wxString, wxString > &aProperties)
std::map< int, ASCH_TEMPLATE > m_altiumTemplates
std::map< int, ASCH_SYMBOL > m_altiumComponents
void ParsePort(const ASCH_PORT &aElem)
void ParseNote(const std::map< wxString, wxString > &aProperties)
void ParseAltiumSch(const wxString &aFileName)
std::vector< ASCH_PORT > m_altiumPortsCurrentSheet
void ParseSheetName(const std::map< wxString, wxString > &aProperties)
void ParseBusEntry(const std::map< wxString, wxString > &aProperties)
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 EnsureSheetSymbolNames()
void ParseRecord(int index, std::map< wxString, wxString > &properties, const wxString &aSectionName)
std::vector< ASCH_PORT > m_altiumHarnessPortsCurrentSheet
void ParseDesignator(const std::map< wxString, wxString > &aProperties)
int m_harnessOwnerIndexOffset
void ParsePortHelper(const ASCH_PORT &aElem)
static bool checkFileHeader(const wxString &aFileName)
long long getLibraryTimestamp(const wxString &aLibraryPath) const
void ParseSheetEntry(const std::map< wxString, wxString > &aProperties)
SCH_SHEET * m_rootSheet
static bool isBinaryFile(const wxString &aFileName)
void ParseHarnessType(const std::map< wxString, wxString > &aProperties)
wxString m_rootFilepath
void ParseJunction(const std::map< wxString, wxString > &aProperties)
SCHEMATIC * m_schematic
void ParseLibParameter(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym, std::vector< int > &aFontSize=nullint)
void ParseASCIISchematic(const wxString &aFileName)
void ParsePolyline(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::unique_ptr< ASCH_SHEET > m_altiumSheet
void ParseComponent(int aIndex, const std::map< wxString, wxString > &aProperties)
std::unique_ptr< TITLE_BLOCK > m_currentTitleBlock
SCH_SHEET * LoadSchematicProject(SCHEMATIC *aSchematic, const std::map< std::string, UTF8 > *aProperties)
void ParseImage(const std::map< wxString, wxString > &aProperties)
void ParseArc(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseNetLabel(const std::map< wxString, wxString > &aProperties)
void ParseNoERC(const std::map< wxString, wxString > &aProperties)
SCH_SHEET * LoadSchematicFile(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load information from some input file format that this SCH_IO implementation knows about,...
std::unordered_map< wxString, SEVERITY > m_errorMessages
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 PostProcessBusLabels()
void ParseLabel(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym, std::vector< int > &aFontSize=nullint)
CASE_INSENSITIVE_MAP< LIB_SYMBOL * > ParseLibFile(const ALTIUM_COMPOUND_FILE &aAltiumSchFile)
void ParseBezier(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseLine(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
void ParseCircle(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::map< int, SCH_SYMBOL * > m_symbols
void ParseSheetSymbol(int aIndex, const std::map< wxString, wxString > &aProperties)
std::map< wxString, long long > m_timestamps
wxFileName getLibFileName()
SCH_SCREEN * getCurrentScreen()
void ParsePin(const std::map< wxString, wxString > &aProperties, std::vector< LIB_SYMBOL * > &aSymbol=nullsym)
std::map< int, LIB_SYMBOL * > m_libSymbols
void ParseHarnessConnector(int aIndex, const std::map< wxString, wxString > &aProperties)
void ensureLoadedLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties)
virtual bool CanReadSchematicFile(const wxString &aFileName) const
Checks if this SCH_IO can read the specified schematic file.
Definition sch_io.cpp:45
SCH_IO(const wxString &aName)
Definition sch_io.h: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
const BOX2I GetBoundingBox() const override
Return the bounding box of the label including its fields.
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:136
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition sch_line.cpp:855
std::vector< VECTOR2I > GetConnectionPoints() const override
Add all the connection points for this item to aPoints.
Definition sch_line.cpp:755
bool IsWire() const
Return true if the line is a wire.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition sch_line.cpp:272
void SetLineColor(const COLOR4D &aColor)
Definition sch_line.cpp:319
void SetLineWidth(const int aSize)
Definition sch_line.cpp:385
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:135
SEG GetSeg() const
Get the geometric aspect of the wire as a SEG.
Definition sch_line.h:154
bool IsBus() const
Return true if the line is a bus.
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:198
int GetLineWidth() const
Definition sch_line.h:194
COLOR4D GetLineColor() const
Return COLOR4D::UNSPECIFIED if a custom color hasn't been set for this line.
Definition sch_line.cpp:343
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:145
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition sch_screen.h:746
SCH_SCREEN * GetNext()
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
SCH_SCREEN * GetFirst()
void ClearEditFlags()
std::vector< SCH_SHEET_INSTANCE > m_sheetInstances
Definition sch_screen.h:724
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:164
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void AddBusAlias(std::shared_ptr< BUS_ALIAS > aAlias)
Add a bus alias definition.
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:138
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:115
const KIID & GetUuid() const
Definition sch_screen.h:529
bool IsTerminalPoint(const VECTOR2I &aPosition, int aLayer) const
Test if aPosition is a connection point on aLayer.
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
void 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:914
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:913
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)
wxString AltiumDeriveSheetName(const wxString &aFilename, const std::set< wxString > &aExistingNames)
wxString AltiumWrapBusLabel(const wxString &aText)
void SetTextPositioning(EDA_TEXT *text, ASCH_LABEL_JUSTIFICATION justification, ASCH_RECORD_ORIENTATION orientation)
@ AUTOPLACE_AUTO
Definition sch_item.h:67
@ L_BIDI
Definition sch_label.h:100
@ L_UNSPECIFIED
Definition sch_label.h:102
@ L_OUTPUT
Definition sch_label.h:99
@ L_INPUT
Definition sch_label.h:98
Utility functions for working with shapes.
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
LINE_STYLE
Dashed line types.
A project-level assembly variant parsed from an Altium .PrjPcb file.
A single component variation within an Altium project variant.
wxString uniqueId
wxString designator
double m_StartAngle
VECTOR2I m_Center
std::vector< VECTOR2I > points
VECTOR2I corner
VECTOR2I location
std::vector< VECTOR2I > points
ASCH_LABEL_JUSTIFICATION justification
ASCH_RECORD_ORIENTATION orientation
ASCH_RECORD_ORIENTATION orientation
ASCH_SHEET_ENTRY_SIDE m_harnessConnectorSide
int DistanceFromTop
ASCH_SHEET_ENTRY_SIDE Side
wxString Name
ASCH_RECORD_ORIENTATION orientation
ASCH_LABEL_JUSTIFICATION justification
ASCH_POLYLINE_LINESTYLE LineStyle
ASCH_LABEL_JUSTIFICATION justification
ASCH_RECORD_ORIENTATION orientation
ASCH_RECORD_ORIENTATION orientation
ASCH_LABEL_JUSTIFICATION justification
VECTOR2I location
ASCH_PIN_SYMBOL::PTYPE symbolOuterEdge
VECTOR2I kicadLocation
wxString designator
ASCH_PIN_ELECTRICAL electrical
ASCH_PIN_SYMBOL::PTYPE symbolInnerEdge
ASCH_RECORD_ORIENTATION orientation
std::vector< VECTOR2I > points
ASCH_POLYLINE_LINESTYLE LineStyle
std::vector< VECTOR2I > Points
VECTOR2I Location
ASCH_PORT_IOTYPE IOtype
wxString HarnessType
ASCH_PORT_STYLE Style
ASCH_POWER_PORT_STYLE style
ASCH_RECORD_ORIENTATION orientation
wxString name
int distanceFromTop
ASCH_SHEET_ENTRY_SIDE side
ASCH_PORT_IOTYPE iotype
wxString harnessType
ASCH_RECORD_ORIENTATION orientation
std::vector< VECTOR2I > points
std::vector< char > data
wxString componentdescription
wxString libreference
wxString sourcelibraryname
ASCH_TEXT_FRAME_ALIGNMENT Alignment
std::vector< VECTOR2I > points
ASCH_SHEET_ENTRY_SIDE m_harnessConnectorSide
VECTOR2I m_location
std::vector< HARNESS_PORT > m_ports
HARNESS_PORT m_entry
wxString m_name
VECTOR2I m_size
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.