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