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
1708{
1709 const auto& component = m_altiumComponents.find( aElem.ownerindex );
1710 const auto& templ = m_altiumTemplates.find( aElem.ownerindex );
1711
1712 if( component != m_altiumComponents.end() )
1713 return component->second.displaymode == aElem.ownerpartdisplaymode;
1714
1715 if( templ != m_altiumTemplates.end() )
1716 return true;
1717
1718 return false;
1719}
1720
1721
1722const ASCH_STORAGE_FILE* SCH_IO_ALTIUM::GetFileFromStorage( const wxString& aFilename ) const
1723{
1724 const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
1725
1726 for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
1727 {
1728 if( file.filename.IsSameAs( aFilename ) )
1729 return &file;
1730
1731 if( file.filename.EndsWith( aFilename ) )
1732 nonExactMatch = &file;
1733 }
1734
1735 return nonExactMatch;
1736}
1737
1738
1739void SCH_IO_ALTIUM::ParseComponent( int aIndex, const std::map<wxString, wxString>& aProperties )
1740{
1741 SCH_SHEET* currentSheet = m_sheetPath.Last();
1742 wxCHECK( currentSheet, /* void */ );
1743
1744 wxString sheetName = currentSheet->GetName();
1745
1746 if( sheetName.IsEmpty() )
1747 sheetName = wxT( "root" );
1748
1749 ASCH_SYMBOL altiumSymbol( aProperties );
1750
1751 if( m_altiumComponents.count( aIndex ) )
1752 {
1753 const ASCH_SYMBOL& currentSymbol = m_altiumComponents.at( aIndex );
1754
1755 m_errorMessages.emplace( wxString::Format( _( "Symbol '%s' in sheet '%s' at index %d "
1756 "replaced with symbol \"%s\"." ),
1757 currentSymbol.libreference,
1758 sheetName,
1759 aIndex,
1760 altiumSymbol.libreference ),
1762 }
1763
1764 auto pair = m_altiumComponents.insert( { aIndex, altiumSymbol } );
1765 const ASCH_SYMBOL& elem = pair.first->second;
1766
1767 // TODO: this is a hack until we correctly apply all transformations to every element
1768 wxString name = wxString::Format( "%s_%d%s_%s_%s",
1769 sheetName,
1770 elem.orientation,
1771 elem.isMirrored ? "_mirrored" : "",
1772 elem.libreference,
1773 elem.sourcelibraryname );
1774
1775 if( elem.displaymodecount > 1 )
1776 name << '_' << elem.displaymode;
1777
1779
1780 LIB_SYMBOL* ksymbol = new LIB_SYMBOL( wxEmptyString );
1781 ksymbol->SetName( name );
1782 ksymbol->SetDescription( elem.componentdescription );
1783 ksymbol->SetLibId( libId );
1784 ksymbol->SetUnitCount( elem.partcount - 1, true );
1785 m_libSymbols.insert( { aIndex, ksymbol } );
1786
1787 // each component has its own symbol for now
1788 SCH_SYMBOL* symbol = new SCH_SYMBOL();
1789
1790 symbol->SetPosition( elem.location + m_sheetOffset );
1791
1792 for( SCH_FIELD& field : symbol->GetFields() )
1793 field.SetVisible( false );
1794
1795 int orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
1796
1797 switch( elem.orientation )
1798 {
1799 case 0: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_90; break;
1800 case 1: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_180; break;
1801 case 2: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_270; break;
1802 case 3: orientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0; break;
1803 default: break;
1804 }
1805
1806 if( elem.isMirrored )
1808
1809 symbol->SetOrientation( orientation );
1810
1811 // If Altium has defined a library from which we have the part,
1812 // use this as the designated source library.
1813 if( !elem.sourcelibraryname.IsEmpty() )
1814 {
1815 wxFileName fn( elem.sourcelibraryname );
1816 libId.SetLibNickname( fn.GetName() );
1817 }
1818
1819 symbol->SetLibId( libId );
1820
1821 if( ksymbol->GetUnitCount() > 1 )
1822 symbol->SetUnit( std::max( 1, elem.currentpartid ) );
1823 else
1824 symbol->SetUnit( 1 );
1825
1827
1828 SCH_SCREEN* screen = getCurrentScreen();
1829 wxCHECK( screen, /* void */ );
1830
1831 screen->Append( symbol );
1832
1833 m_symbols.insert( { aIndex, symbol } );
1834
1835 if( !elem.uniqueid.empty() )
1836 m_altiumSymbolToUid[symbol] = elem.uniqueid;
1837}
1838
1839
1840void SCH_IO_ALTIUM::ParseTemplate( int aIndex, const std::map<wxString, wxString>& aProperties )
1841{
1842 SCH_SHEET* currentSheet = m_sheetPath.Last();
1843 wxCHECK( currentSheet, /* void */ );
1844
1845 wxString sheetName = currentSheet->GetName();
1846
1847 if( sheetName.IsEmpty() )
1848 sheetName = wxT( "root" );
1849
1850 ASCH_TEMPLATE altiumTemplate( aProperties );
1851
1852 // Extract base name from path
1853 wxString baseName = altiumTemplate.filename.AfterLast( '\\' ).BeforeLast( '.' );
1854
1855 if( baseName.IsEmpty() )
1856 baseName = wxS( "Template" );
1857
1858 m_altiumTemplates.insert( { aIndex, altiumTemplate } );
1859 // No need to create a symbol - graphics is put on the sheet
1860}
1861
1862
1863void SCH_IO_ALTIUM::ParsePin( const std::map<wxString, wxString>& aProperties,
1864 std::vector<LIB_SYMBOL*>& aSymbol )
1865{
1866 ASCH_PIN elem( aProperties );
1867
1868 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
1869 : aSymbol[elem.ownerpartdisplaymode];
1870 SCH_SYMBOL* schSymbol = nullptr;
1871
1872 if( !symbol )
1873 {
1874 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
1875
1876 if( libSymbolIt == m_libSymbols.end() )
1877 {
1878 // TODO: e.g. can depend on Template (RECORD=39
1879 m_errorMessages.emplace( wxString::Format( wxT( "Pin's owner (%d) not found." ),
1880 elem.ownerindex ),
1882 return;
1883 }
1884
1885 if( !IsComponentPartVisible( elem ) )
1886 return;
1887
1888 schSymbol = m_symbols.at( libSymbolIt->first );
1889 symbol = libSymbolIt->second;
1890 }
1891
1892 SCH_PIN* pin = new SCH_PIN( symbol );
1893
1894 // Make sure that these are visible when initializing the symbol
1895 // This may be overriden by the file data but not by the pin defaults
1896 pin->SetNameTextSize( schIUScale.MilsToIU( DEFAULT_PINNAME_SIZE ) );
1897 pin->SetNumberTextSize( schIUScale.MilsToIU( DEFAULT_PINNUM_SIZE ) );
1898
1899 symbol->AddDrawItem( pin, false );
1900
1901 pin->SetUnit( std::max( 0, elem.ownerpartid ) );
1902
1903 pin->SetName( AltiumPinNamesToKiCad( elem.name ) );
1904 pin->SetNumber( elem.designator );
1905 pin->SetLength( elem.pinlength );
1906
1907 if( elem.hidden )
1908 pin->SetVisible( false );
1909
1910 if( !elem.showDesignator )
1911 pin->SetNumberTextSize( 0 );
1912
1913 if( !elem.showPinName )
1914 pin->SetNameTextSize( 0 );
1915
1916 // Altium gives the pin body end location (elem.location) and the pre-computed
1917 // electrical connection point (elem.kicadLocation) which accounts for pin length
1918 // with combined integer+fractional arithmetic to avoid rounding errors.
1919 VECTOR2I bodyEnd = elem.location;
1920 VECTOR2I pinLocation = elem.kicadLocation;
1921
1922 switch( elem.orientation )
1923 {
1925 pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT );
1926 break;
1927
1929 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
1930 break;
1931
1933 pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT );
1934 break;
1935
1937 pin->SetOrientation( PIN_ORIENTATION::PIN_UP );
1938 break;
1939
1940 default:
1941 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
1942 break;
1943 }
1944
1945 if( schSymbol )
1946 {
1947 // Both points are in absolute schematic coordinates. Transform them to library-local
1948 // space, then derive the pin orientation from the resulting direction vector.
1949 pinLocation = GetRelativePosition( pinLocation + m_sheetOffset, schSymbol );
1950 bodyEnd = GetRelativePosition( bodyEnd + m_sheetOffset, schSymbol );
1951
1952 VECTOR2I dir = bodyEnd - pinLocation;
1953
1954 if( std::abs( dir.x ) >= std::abs( dir.y ) )
1955 pin->SetOrientation( dir.x > 0 ? PIN_ORIENTATION::PIN_RIGHT : PIN_ORIENTATION::PIN_LEFT );
1956 else
1957 pin->SetOrientation( dir.y > 0 ? PIN_ORIENTATION::PIN_DOWN : PIN_ORIENTATION::PIN_UP );
1958 }
1959
1960 pin->SetPosition( pinLocation );
1961
1962 switch( elem.electrical )
1963 {
1966 break;
1967
1969 pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
1970 break;
1971
1974 break;
1975
1978 break;
1979
1982 break;
1983
1986 break;
1987
1990 break;
1991
1994 break;
1995
1997 default:
1999 m_errorMessages.emplace( _( "Pin has unexpected electrical type." ), RPT_SEVERITY_WARNING );
2000 break;
2001 }
2002
2004 m_errorMessages.emplace( _( "Pin has unexpected outer edge type." ), RPT_SEVERITY_WARNING );
2005
2007 m_errorMessages.emplace( _( "Pin has unexpected inner edge type." ), RPT_SEVERITY_WARNING );
2008
2010 {
2011 switch( elem.symbolInnerEdge )
2012 {
2015 break;
2016
2017 default:
2018 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
2019 break;
2020 }
2021 }
2023 {
2024 switch( elem.symbolInnerEdge )
2025 {
2027 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
2028 break;
2029
2030 default:
2031 pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
2032 break;
2033 }
2034 }
2036 {
2037 pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
2038 }
2039 else
2040 {
2041 switch( elem.symbolInnerEdge )
2042 {
2044 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
2045 break;
2046
2047 default:
2048 pin->SetShape( GRAPHIC_PINSHAPE::LINE ); // nothing to do
2049 break;
2050 }
2051 }
2052}
2053
2054
2056 ASCH_RECORD_ORIENTATION orientation )
2057{
2058 int vjustify, hjustify;
2060
2061 switch( justification )
2062 {
2063 default:
2068 vjustify = GR_TEXT_V_ALIGN_BOTTOM;
2069 break;
2070
2074 vjustify = GR_TEXT_V_ALIGN_CENTER;
2075 break;
2076
2080 vjustify = GR_TEXT_V_ALIGN_TOP;
2081 break;
2082 }
2083
2084 switch( justification )
2085 {
2086 default:
2091 hjustify = GR_TEXT_H_ALIGN_LEFT;
2092 break;
2093
2097 hjustify = GR_TEXT_H_ALIGN_CENTER;
2098 break;
2099
2103 hjustify = GR_TEXT_H_ALIGN_RIGHT;
2104 break;
2105 }
2106
2107 switch( orientation )
2108 {
2110 angle = ANGLE_HORIZONTAL;
2111 break;
2112
2114 hjustify *= -1;
2115 angle = ANGLE_HORIZONTAL;
2116 break;
2117
2119 angle = ANGLE_VERTICAL;
2120 break;
2121
2123 hjustify *= -1;
2124 angle = ANGLE_VERTICAL;
2125 break;
2126 }
2127
2128 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( vjustify ) );
2129 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( hjustify ) );
2130 text->SetTextAngle( angle );
2131}
2132
2133
2134// Altium text orientation and justification are in absolute (page) coordinates. KiCad stores
2135// field text properties relative to the parent symbol and applies the symbol's transform at
2136// render time. This function adjusts the field's stored text angle and justification to
2137// compensate for the symbol's orientation so that the final rendered appearance matches the
2138// original Altium layout.
2139//
2140// The compensation follows the same logic as SCH_FIELD::Rotate() but applied in the
2141// inverse direction to undo the symbol's rotation effect on text properties.
2143{
2144 bool isHorizontal = aField->GetTextAngle().IsHorizontal();
2145
2146 // Altium orientation 0 (RIGHTWARDS) maps to KiCad SYM_ORIENT_90 (CCW). To compensate,
2147 // apply CW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CW rotation
2148 // of horizontal text flips justification; CW rotation of vertical text does not.
2149 if( aSymbol.orientation == 0 )
2150 {
2151 if( isHorizontal )
2152 {
2153 aField->SetHorizJustify(
2154 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2155 }
2156
2157 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2158 }
2159 // Altium orientation 1 (UPWARDS) maps to KiCad SYM_ORIENT_180 (two CCW rotations). The
2160 // transform [-1,0,0,-1] negates both X and Y, flipping horizontal justification. Apply
2161 // one correction for the full 180 degrees regardless of text angle.
2162 else if( aSymbol.orientation == 1 )
2163 {
2164 aField->SetHorizJustify(
2165 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2166 }
2167 // Altium orientation 2 (LEFTWARDS) maps to KiCad SYM_ORIENT_270 (CW). To compensate,
2168 // apply CCW 90-degree rotation to text properties. Per SCH_FIELD::Rotate(), CCW rotation
2169 // of vertical text flips justification; CCW rotation of horizontal text does not.
2170 else if( aSymbol.orientation == 2 )
2171 {
2172 if( !isHorizontal )
2173 {
2174 aField->SetHorizJustify(
2175 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2176 }
2177
2178 aField->SetTextAngle( isHorizontal ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
2179 }
2180 // Altium orientation 3 (DOWNWARDS) maps to KiCad SYM_ORIENT_0 (identity). No rotation
2181 // compensation needed.
2182
2183 // Mirror-Y in KiCad negates the X component of the bounding box, which effectively
2184 // flips horizontal justification. Compensate so the rendered text matches Altium.
2185 if( aSymbol.isMirrored )
2186 {
2187 aField->SetHorizJustify(
2188 static_cast<GR_TEXT_H_ALIGN_T>( -aField->GetHorizJustify() ) );
2189 }
2190}
2191
2192
2193// Altium text in symbols uses absolute orientation, but KiCad applies the symbol's transform
2194// to library body items via OrientAndMirrorSymbolItems at render time. This function pre-
2195// compensates the stored text angle and justification so that after the render-time transform,
2196// the final appearance matches the original Altium layout. Position is not adjusted here
2197// since GetRelativePosition already handles the positional component.
2199{
2200 int nRenderRotations = ( aSymbol.orientation + 1 ) % 4;
2201
2202 // Undo mirror first (reverse of render-time application order).
2203 // MirrorHorizontally on LAYER_DEVICE text flips H-justify when horizontal
2204 // and V-justify when vertical.
2205 if( aSymbol.isMirrored )
2206 {
2207 if( aText->GetTextAngle().IsHorizontal() )
2208 aText->FlipHJustify();
2209 else
2210 aText->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -aText->GetVertJustify() ) );
2211 }
2212
2213 // The render pipeline applies Rotate90(false) N times; undo with N inverse rotations.
2214 for( int i = 0; i < nRenderRotations; i++ )
2215 aText->Rotate90( true );
2216}
2217
2218
2220{
2221 // No component assigned -> Put on sheet
2222 if( aOwnerindex == ALTIUM_COMPONENT_NONE )
2223 return true;
2224
2225 // For a template -> Put on sheet so we can resolve variables
2226 if( m_altiumTemplates.find( aOwnerindex ) != m_altiumTemplates.end() )
2227 return true;
2228
2229 return false;
2230}
2231
2232
2233void SCH_IO_ALTIUM::ParseLabel( const std::map<wxString, wxString>& aProperties,
2234 std::vector<LIB_SYMBOL*>& aSymbol, std::vector<int>& aFontSizes )
2235{
2236 ASCH_LABEL elem( aProperties );
2237
2238 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2239 {
2240 static const std::map<wxString, wxString> variableMap = {
2241 { "APPLICATION_BUILDNUMBER", "KICAD_VERSION" },
2242 { "SHEETNUMBER", "#" },
2243 { "SHEETTOTAL", "##" },
2244 { "TITLE", "TITLE" }, // including 1:1 maps makes it easier
2245 { "REVISION", "REVISION" }, // to see that the list is complete
2246 { "DATE", "ISSUE_DATE" },
2247 { "CURRENTDATE", "CURRENT_DATE" },
2248 { "COMPANYNAME", "COMPANY" },
2249 { "DOCUMENTNAME", "FILENAME" },
2250 { "DOCUMENTFULLPATHANDNAME", "FILEPATH" },
2251 { "PROJECTNAME", "PROJECTNAME" },
2252 };
2253
2254 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
2255 SCH_TEXT* textItem = new SCH_TEXT( elem.location + m_sheetOffset, kicadText );
2256
2257 SetTextPositioning( textItem, elem.justification, elem.orientation );
2258
2259 size_t fontId = static_cast<int>( elem.fontId );
2260
2261 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2262 {
2263 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2264 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
2265
2266 // Must come after SetTextSize()
2267 textItem->SetBold( font.Bold );
2268 textItem->SetItalic( font.Italic );
2269 }
2270
2271 textItem->SetFlags( IS_NEW );
2272
2273 SCH_SCREEN* screen = getCurrentScreen();
2274 wxCHECK( screen, /* void */ );
2275
2276 screen->Append( textItem );
2277 }
2278 else
2279 {
2280 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
2281 : aSymbol[elem.ownerpartdisplaymode];
2282 SCH_SYMBOL* schsym = nullptr;
2283
2284 if( !symbol )
2285 {
2286 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2287
2288 if( libSymbolIt == m_libSymbols.end() )
2289 {
2290 // TODO: e.g. can depend on Template (RECORD=39
2291 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), elem.ownerindex ),
2293 return;
2294 }
2295
2296 symbol = libSymbolIt->second;
2297 schsym = m_symbols.at( libSymbolIt->first );
2298 }
2299
2300 VECTOR2I pos = elem.location;
2301 SCH_TEXT* textItem = new SCH_TEXT( { 0, 0 }, elem.text, LAYER_DEVICE );
2302 symbol->AddDrawItem( textItem, false );
2303
2305 if( schsym )
2306 pos = GetRelativePosition( elem.location + m_sheetOffset, schsym );
2307
2308 textItem->SetPosition( pos );
2309 textItem->SetUnit( std::max( 0, elem.ownerpartid ) );
2310 SetTextPositioning( textItem, elem.justification, elem.orientation );
2311
2312 if( schsym )
2313 {
2314 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
2315
2316 if( altiumSymIt != m_altiumComponents.end() )
2317 AdjustTextForSymbolOrientation( textItem, altiumSymIt->second );
2318 }
2319
2320 size_t fontId = elem.fontId;
2321
2322 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2323 {
2324 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2325 textItem->SetTextSize( { font.Size / 2, font.Size / 2 } );
2326
2327 // Must come after SetTextSize()
2328 textItem->SetBold( font.Bold );
2329 textItem->SetItalic( font.Italic );
2330 }
2331 else if( fontId > 0 && fontId <= aFontSizes.size() )
2332 {
2333 int size = aFontSizes[fontId - 1];
2334 textItem->SetTextSize( { size, size } );
2335 }
2336 }
2337}
2338
2339
2340void SCH_IO_ALTIUM::ParseTextFrame( const std::map<wxString, wxString>& aProperties,
2341 std::vector<LIB_SYMBOL*>& aSymbol,
2342 std::vector<int>& aFontSizes )
2343{
2344 ASCH_TEXT_FRAME elem( aProperties );
2345
2346 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2347 AddTextBox( &elem );
2348 else
2349 AddLibTextBox( &elem, aSymbol, aFontSizes );
2350}
2351
2352
2353void SCH_IO_ALTIUM::ParseNote( const std::map<wxString, wxString>& aProperties )
2354{
2355 ASCH_NOTE elem( aProperties );
2356 AddTextBox( static_cast<ASCH_TEXT_FRAME*>( &elem ) );
2357
2358 // TODO: need some sort of property system for storing author....
2359}
2360
2361
2363{
2364 SCH_TEXTBOX* textBox = new SCH_TEXTBOX();
2365
2366 VECTOR2I sheetTopRight = aElem->TopRight + m_sheetOffset;
2367 VECTOR2I sheetBottomLeft = aElem->BottomLeft +m_sheetOffset;
2368
2369 textBox->SetStart( sheetTopRight );
2370 textBox->SetEnd( sheetBottomLeft );
2371
2372 textBox->SetText( aElem->Text );
2373
2374 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
2375
2376 if( aElem->isSolid)
2378 else
2379 textBox->SetFilled( false );
2380
2381 if( aElem->ShowBorder )
2383 else
2385
2386 switch( aElem->Alignment )
2387 {
2388 default:
2391 break;
2394 break;
2397 break;
2398 }
2399
2400 size_t fontId = static_cast<int>( aElem->FontID );
2401
2402 if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
2403 {
2404 const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
2405 textBox->SetTextSize( { font.Size / 2, font.Size / 2 } );
2406
2407 // Must come after SetTextSize()
2408 textBox->SetBold( font.Bold );
2409 textBox->SetItalic( font.Italic );
2410 //textBox->SetFont( //how to set font, we have a font name here: ( font.fontname );
2411 }
2412
2413 textBox->SetFlags( IS_NEW );
2414
2415 SCH_SCREEN* screen = getCurrentScreen();
2416 wxCHECK( screen, /* void */ );
2417
2418 screen->Append( textBox );
2419}
2420
2421
2422void SCH_IO_ALTIUM::AddLibTextBox( const ASCH_TEXT_FRAME *aElem, std::vector<LIB_SYMBOL*>& aSymbol,
2423 std::vector<int>& aFontSizes )
2424{
2425 LIB_SYMBOL* symbol = static_cast<int>( aSymbol.size() ) <= aElem->ownerpartdisplaymode
2426 ? nullptr
2427 : aSymbol[aElem->ownerpartdisplaymode];
2428 SCH_SYMBOL* schsym = nullptr;
2429
2430 if( !symbol )
2431 {
2432 const auto& libSymbolIt = m_libSymbols.find( aElem->ownerindex );
2433
2434 if( libSymbolIt == m_libSymbols.end() )
2435 {
2436 // TODO: e.g. can depend on Template (RECORD=39
2437 m_errorMessages.emplace( wxString::Format( wxT( "Label's owner (%d) not found." ), aElem->ownerindex ),
2439 return;
2440 }
2441
2442 symbol = libSymbolIt->second;
2443 schsym = m_symbols.at( libSymbolIt->first );
2444 }
2445
2446 SCH_TEXTBOX* textBox = new SCH_TEXTBOX( LAYER_DEVICE );
2447
2448 textBox->SetUnit( std::max( 0, aElem->ownerpartid ) );
2449 symbol->AddDrawItem( textBox, false );
2450
2452 if( !schsym )
2453 {
2454 textBox->SetStart( aElem->TopRight );
2455 textBox->SetEnd( aElem->BottomLeft );
2456 }
2457 else
2458 {
2459 textBox->SetStart( GetRelativePosition( aElem->TopRight + m_sheetOffset, schsym ) );
2460 textBox->SetEnd( GetRelativePosition( aElem->BottomLeft + m_sheetOffset, schsym ) );
2461 }
2462
2463 textBox->SetText( aElem->Text );
2464
2465 textBox->SetFillColor( GetColorFromInt( aElem->AreaColor ) );
2466
2467 if( aElem->isSolid)
2469 else
2470 textBox->SetFilled( false );
2471
2472 if( aElem->ShowBorder )
2474 else
2475 textBox->SetStroke( STROKE_PARAMS( -1 ) );
2476
2477 switch( aElem->Alignment )
2478 {
2479 default:
2482 break;
2485 break;
2488 break;
2489 }
2490
2491 if( aElem->FontID > 0 && aElem->FontID <= static_cast<int>( aFontSizes.size() ) )
2492 {
2493 int size = aFontSizes[aElem->FontID - 1];
2494 textBox->SetTextSize( { size, size } );
2495 }
2496}
2497
2498
2499void SCH_IO_ALTIUM::ParseBezier( const std::map<wxString, wxString>& aProperties,
2500 std::vector<LIB_SYMBOL*>& aSymbol )
2501{
2502 ASCH_BEZIER elem( aProperties );
2503
2504 if( elem.points.size() < 2 )
2505 {
2506 m_errorMessages.emplace( wxString::Format( _( "Bezier has %d control points. At least 2 are expected." ),
2507 static_cast<int>( elem.points.size() ) ),
2509 return;
2510 }
2511
2512 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2513 {
2514 SCH_SCREEN* currentScreen = getCurrentScreen();
2515 wxCHECK( currentScreen, /* void */ );
2516
2517 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
2518 {
2519 if( i + 2 == elem.points.size() )
2520 {
2521 // special case: single line
2522 SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
2524
2525 line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
2527
2528 line->SetFlags( IS_NEW );
2529
2530 currentScreen->Append( line );
2531 }
2532 else
2533 {
2534 // simulate Bezier using line segments
2535 std::vector<VECTOR2I> bezierPoints;
2536 std::vector<VECTOR2I> polyPoints;
2537
2538 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
2539 bezierPoints.push_back( elem.points.at( j ) );
2540
2541 BEZIER_POLY converter( bezierPoints );
2542 converter.GetPoly( polyPoints );
2543
2544 for( size_t k = 0; k + 1 < polyPoints.size(); k++ )
2545 {
2546 SCH_LINE* line = new SCH_LINE( polyPoints.at( k ) + m_sheetOffset,
2548
2549 line->SetEndPoint( polyPoints.at( k + 1 ) + m_sheetOffset );
2551
2552 line->SetFlags( IS_NEW );
2553 currentScreen->Append( line );
2554 }
2555 }
2556 }
2557 }
2558 else
2559 {
2560 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
2561 : aSymbol[elem.ownerpartdisplaymode];
2562 SCH_SYMBOL* schsym = nullptr;
2563
2564 if( !symbol )
2565 {
2566 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2567
2568 if( libSymbolIt == m_libSymbols.end() )
2569 {
2570 // TODO: e.g. can depend on Template (RECORD=39
2571 m_errorMessages.emplace( wxString::Format( wxT( "Bezier's owner (%d) not found." ),
2572 elem.ownerindex ),
2574 return;
2575 }
2576
2577 symbol = libSymbolIt->second;
2578 schsym = m_symbols.at( libSymbolIt->first );
2579 }
2580
2581 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2582 return;
2583
2584 for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
2585 {
2586 if( i + 2 == elem.points.size() )
2587 {
2588 // special case: single line
2590 symbol->AddDrawItem( line, false );
2591
2592 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2593
2594 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
2595 {
2596 VECTOR2I pos = elem.points.at( j );
2597
2598 if( schsym )
2599 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2600
2601 line->AddPoint( pos );
2602 }
2603
2605 }
2606 else if( i + 3 == elem.points.size() )
2607 {
2608 // TODO: special case of a single line with an extra point?
2609 // I haven't a clue what this is all about, but the sample document we have in
2610 // https://gitlab.com/kicad/code/kicad/-/issues/8974 responds best by treating it
2611 // as another single line special case.
2613 symbol->AddDrawItem( line, false );
2614
2615 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2616
2617 for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
2618 {
2619 VECTOR2I pos = elem.points.at( j );
2620
2621 if( schsym )
2622 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2623
2624 line->AddPoint( pos );
2625 }
2626
2628 }
2629 else
2630 {
2631 // Bezier always has exactly 4 control points
2633 symbol->AddDrawItem( bezier, false );
2634
2635 bezier->SetUnit( std::max( 0, elem.ownerpartid ) );
2636
2637 for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
2638 {
2639 VECTOR2I pos = elem.points.at( j );
2640
2641 if( schsym )
2642 pos = GetRelativePosition( pos + m_sheetOffset, schsym );
2643
2644 switch( j - i )
2645 {
2646 case 0: bezier->SetStart( pos ); break;
2647 case 1: bezier->SetBezierC1( pos ); break;
2648 case 2: bezier->SetBezierC2( pos ); break;
2649 case 3: bezier->SetEnd( pos ); break;
2650 default: break; // Can't get here but silence warnings
2651 }
2652 }
2653
2656 }
2657 }
2658 }
2659}
2660
2661
2662void SCH_IO_ALTIUM::ParsePolyline( const std::map<wxString, wxString>& aProperties,
2663 std::vector<LIB_SYMBOL*>& aSymbol )
2664{
2665 ASCH_POLYLINE elem( aProperties );
2666
2667 if( elem.Points.size() < 2 )
2668 return;
2669
2670 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2671 {
2672 SCH_SCREEN* screen = getCurrentScreen();
2673 wxCHECK( screen, /* void */ );
2674
2675 for( size_t i = 1; i < elem.Points.size(); i++ )
2676 {
2677 SCH_LINE* line = new SCH_LINE;
2678
2679 line->SetStartPoint( elem.Points[i - 1] + m_sheetOffset );
2680 line->SetEndPoint( elem.Points[i] + m_sheetOffset );
2681
2683 GetColorFromInt( elem.Color ) ) );
2684
2685 line->SetFlags( IS_NEW );
2686
2687 screen->Append( line );
2688 }
2689 }
2690 else
2691 {
2692 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
2693 : aSymbol[elem.ownerpartdisplaymode];
2694 SCH_SYMBOL* schsym = nullptr;
2695
2696 if( !symbol )
2697 {
2698 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2699
2700 if( libSymbolIt == m_libSymbols.end() )
2701 {
2702 // TODO: e.g. can depend on Template (RECORD=39
2703 m_errorMessages.emplace( wxString::Format( wxT( "Polyline's owner (%d) not found." ),
2704 elem.ownerindex ),
2706 return;
2707 }
2708
2709 symbol = libSymbolIt->second;
2710 schsym = m_symbols.at( libSymbolIt->first );
2711 }
2712
2713 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2714 return;
2715
2717 symbol->AddDrawItem( line, false );
2718
2719 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2720
2721 for( VECTOR2I point : elem.Points )
2722 {
2723 if( schsym )
2724 point = GetRelativePosition( point + m_sheetOffset, schsym );
2725
2726 line->AddPoint( point );
2727 }
2728
2730 STROKE_PARAMS stroke = line->GetStroke();
2731 stroke.SetLineStyle( GetPlotDashType( elem.LineStyle ) );
2732
2733 line->SetStroke( stroke );
2734 }
2735}
2736
2737
2738void SCH_IO_ALTIUM::ParsePolygon( const std::map<wxString, wxString>& aProperties,
2739 std::vector<LIB_SYMBOL*>& aSymbol )
2740{
2741 ASCH_POLYGON elem( aProperties );
2742
2743 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2744 {
2745 SCH_SCREEN* screen = getCurrentScreen();
2746 wxCHECK( screen, /* void */ );
2747
2748 SCH_SHAPE* poly = new SCH_SHAPE( SHAPE_T::POLY );
2749
2750 for( VECTOR2I& point : elem.points )
2751 poly->AddPoint( point + m_sheetOffset );
2752 poly->AddPoint( elem.points.front() + m_sheetOffset );
2753
2754 SetSchShapeLine( elem, poly );
2755 SetSchShapeFillAndColor( elem, poly );
2756 poly->SetFlags( IS_NEW );
2757
2758 screen->Append( poly );
2759 }
2760 else
2761 {
2762 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
2763 : aSymbol[elem.ownerpartdisplaymode];
2764 SCH_SYMBOL* schsym = nullptr;
2765
2766 if( !symbol )
2767 {
2768 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2769
2770 if( libSymbolIt == m_libSymbols.end() )
2771 {
2772 // TODO: e.g. can depend on Template (RECORD=39
2773 m_errorMessages.emplace( wxString::Format( wxT( "Polygon's owner (%d) not found." ),
2774 elem.ownerindex ),
2776 return;
2777 }
2778
2779 symbol = libSymbolIt->second;
2780 schsym = m_symbols.at( libSymbolIt->first );
2781 }
2782
2783 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2784 return;
2785
2787
2788 symbol->AddDrawItem( line, false );
2789 line->SetUnit( std::max( 0, elem.ownerpartid ) );
2790
2791 for( VECTOR2I point : elem.points )
2792 {
2793 if( schsym )
2794 point = GetRelativePosition( point + m_sheetOffset, schsym );
2795
2796 line->AddPoint( point );
2797 }
2798
2799 VECTOR2I point = elem.points.front();
2800
2801 if( schsym )
2802 point = GetRelativePosition( elem.points.front() + m_sheetOffset, schsym );
2803
2804 line->AddPoint( point );
2805
2808
2809 if( line->GetFillColor() == line->GetStroke().GetColor()
2810 && line->GetFillMode() != FILL_T::NO_FILL )
2811 {
2812 STROKE_PARAMS stroke = line->GetStroke();
2813 stroke.SetWidth( -1 );
2814 line->SetStroke( stroke );
2815 }
2816 }
2817}
2818
2819
2820void SCH_IO_ALTIUM::ParseRoundRectangle( const std::map<wxString, wxString>& aProperties,
2821 std::vector<LIB_SYMBOL*>& aSymbol )
2822{
2823 ASCH_ROUND_RECTANGLE elem( aProperties );
2824
2825 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2826 {
2827 SCH_SCREEN* screen = getCurrentScreen();
2828 wxCHECK( screen, /* void */ );
2829
2830 // TODO: misses rounded edges
2831 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
2832
2833 rect->SetPosition( elem.TopRight + m_sheetOffset );
2834 rect->SetEnd( elem.BottomLeft + m_sheetOffset );
2835 SetSchShapeLine( elem, rect );
2836 SetSchShapeFillAndColor( elem, rect );
2837 rect->SetFlags( IS_NEW );
2838
2839 screen->Append( rect );
2840 }
2841 else
2842 {
2843 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
2844 : aSymbol[elem.ownerpartdisplaymode];
2845 SCH_SYMBOL* schsym = nullptr;
2846
2847 if( !symbol )
2848 {
2849 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2850
2851 if( libSymbolIt == m_libSymbols.end() )
2852 {
2853 // TODO: e.g. can depend on Template (RECORD=39
2854 m_errorMessages.emplace( wxString::Format( wxT( "Rounded rectangle's owner (%d) not found." ),
2855 elem.ownerindex ),
2857 return;
2858 }
2859
2860 symbol = libSymbolIt->second;
2861 schsym = m_symbols.at( libSymbolIt->first );
2862 }
2863
2864 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2865 return;
2866
2867 SCH_SHAPE* rect = nullptr;
2868
2869 int width = std::abs( elem.TopRight.x - elem.BottomLeft.x );
2870 int height = std::abs( elem.TopRight.y - elem.BottomLeft.y );
2871
2872 // If it is a circle, make it a circle
2873 if( std::abs( elem.CornerRadius.x ) >= width / 2
2874 && std::abs( elem.CornerRadius.y ) >= height / 2 )
2875 {
2876 rect = new SCH_SHAPE( SHAPE_T::CIRCLE, LAYER_DEVICE );
2877
2878 VECTOR2I center = ( elem.TopRight + elem.BottomLeft ) / 2;
2879 int radius = std::min( width / 2, height / 2 );
2880
2881 if( schsym )
2883
2884 rect->SetPosition( center );
2885 rect->SetEnd( VECTOR2I( rect->GetPosition().x + radius, rect->GetPosition().y ) );
2886 }
2887 else
2888 {
2890
2891 if( !schsym )
2892 {
2893 rect->SetPosition( elem.TopRight );
2894 rect->SetEnd( elem.BottomLeft );
2895 }
2896 else
2897 {
2898 rect->SetPosition( GetRelativePosition( elem.TopRight + m_sheetOffset, schsym ) );
2899 rect->SetEnd( GetRelativePosition( elem.BottomLeft + m_sheetOffset, schsym ) );
2900 }
2901
2902 rect->Normalize();
2903 }
2904
2907
2908 symbol->AddDrawItem( rect, false );
2909 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
2910 }
2911}
2912
2913
2914void SCH_IO_ALTIUM::ParseArc( const std::map<wxString, wxString>& aProperties,
2915 std::vector<LIB_SYMBOL*>& aSymbol )
2916{
2917 ASCH_ARC elem( aProperties );
2918
2919 int arc_radius = elem.m_Radius;
2920 VECTOR2I center = elem.m_Center;
2921 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
2922 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
2923 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
2924 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
2925
2926 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
2927 {
2928 SCH_SCREEN* currentScreen = getCurrentScreen();
2929 wxCHECK( currentScreen, /* void */ );
2930
2931 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
2932 {
2934
2935 circle->SetPosition( elem.m_Center + m_sheetOffset );
2936 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
2937
2938 SetSchShapeLine( elem, circle );
2940
2941 currentScreen->Append( circle );
2942 }
2943 else
2944 {
2945 SCH_SHAPE* arc = new SCH_SHAPE( SHAPE_T::ARC );
2946
2947 arc->SetCenter( elem.m_Center + m_sheetOffset );
2948 arc->SetStart( elem.m_Center + startOffset + m_sheetOffset );
2949 arc->SetEnd( elem.m_Center + endOffset + m_sheetOffset );
2950
2951 SetSchShapeLine( elem, arc );
2952 SetSchShapeFillAndColor( elem, arc );
2953
2954 currentScreen->Append( arc );
2955 }
2956 }
2957 else
2958 {
2959 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
2960 : aSymbol[elem.ownerpartdisplaymode];
2961 SCH_SYMBOL* schsym = nullptr;
2962
2963 if( !symbol )
2964 {
2965 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
2966
2967 if( libSymbolIt == m_libSymbols.end() )
2968 {
2969 // TODO: e.g. can depend on Template (RECORD=39
2970 m_errorMessages.emplace( wxString::Format( wxT( "Arc's owner (%d) not found." ), elem.ownerindex ),
2972 return;
2973 }
2974
2975 symbol = libSymbolIt->second;
2976 schsym = m_symbols.at( libSymbolIt->first );
2977 }
2978
2979 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
2980 return;
2981
2982 if( elem.m_StartAngle == 0 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
2983 {
2985 symbol->AddDrawItem( circle, false );
2986
2987 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
2988
2989 if( schsym )
2991
2992 circle->SetPosition( center );
2993
2994 circle->SetEnd( circle->GetPosition() + VECTOR2I( arc_radius, 0 ) );
2997 }
2998 else
2999 {
3001 symbol->AddDrawItem( arc, false );
3002 arc->SetUnit( std::max( 0, elem.ownerpartid ) );
3003
3004 if( schsym )
3005 {
3007 startOffset = GetRelativePosition( elem.m_Center + startOffset + m_sheetOffset, schsym )
3008 - center;
3009 endOffset = GetRelativePosition( elem.m_Center + endOffset + m_sheetOffset, schsym )
3010 - center;
3011 }
3012
3013 arc->SetCenter( center );
3014 arc->SetStart( center + startOffset );
3015 arc->SetEnd( center + endOffset );
3016
3019 }
3020 }
3021}
3022
3023
3024void SCH_IO_ALTIUM::ParseEllipticalArc( const std::map<wxString, wxString>& aProperties,
3025 std::vector<LIB_SYMBOL*>& aSymbol )
3026{
3027 ASCH_ARC elem( aProperties );
3028
3029 if( elem.m_Radius == elem.m_SecondaryRadius && elem.m_StartAngle == 0
3030 && ( elem.m_EndAngle == 0 || elem.m_EndAngle == 360 ) )
3031 {
3032 ParseCircle( aProperties, aSymbol );
3033 return;
3034 }
3035
3036 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3037 {
3038 SCH_SCREEN* currentScreen = getCurrentScreen();
3039 wxCHECK( currentScreen, /* void */ );
3040
3041 ELLIPSE<int> ellipse( elem.m_Center + m_sheetOffset, elem.m_Radius,
3044 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
3045 std::vector<BEZIER<int>> beziers;
3046
3047 TransformEllipseToBeziers( ellipse, beziers );
3048
3049 for( const BEZIER<int>& bezier : beziers )
3050 {
3051 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
3052 schbezier->SetStart( bezier.Start );
3053 schbezier->SetBezierC1( bezier.C1 );
3054 schbezier->SetBezierC2( bezier.C2 );
3055 schbezier->SetEnd( bezier.End );
3056 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
3058
3059 currentScreen->Append( schbezier );
3060 }
3061 }
3062 else
3063 {
3064 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
3065 : aSymbol[elem.ownerpartdisplaymode];
3066 SCH_SYMBOL* schsym = nullptr;
3067
3068 if( !symbol )
3069 {
3070 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3071
3072 if( libSymbolIt == m_libSymbols.end() )
3073 {
3074 // TODO: e.g. can depend on Template (RECORD=39
3075 m_errorMessages.emplace( wxString::Format( wxT( "Elliptical Arc's owner (%d) not found." ),
3076 elem.ownerindex ),
3078 return;
3079 }
3080
3081 symbol = libSymbolIt->second;
3082 schsym = m_symbols.at( libSymbolIt->first );
3083 }
3084
3085 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
3086 return;
3087
3088 ELLIPSE<int> ellipse( elem.m_Center, elem.m_Radius,
3091 EDA_ANGLE( elem.m_EndAngle, DEGREES_T ) );
3092 std::vector<BEZIER<int>> beziers;
3093
3094 TransformEllipseToBeziers( ellipse, beziers );
3095
3096 for( const BEZIER<int>& bezier : beziers )
3097 {
3098 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
3099 symbol->AddDrawItem( schbezier, false );
3100
3101 schbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
3102
3103 if( schsym )
3104 {
3105 schbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
3106 schbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
3107 schbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
3108 schbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
3109 }
3110 else
3111 {
3112 schbezier->SetStart( bezier.Start );
3113 schbezier->SetBezierC1( bezier.C1 );
3114 schbezier->SetBezierC2( bezier.C2 );
3115 schbezier->SetEnd( bezier.End );
3116 }
3117
3120 }
3121 }
3122}
3123
3124
3125void SCH_IO_ALTIUM::ParsePieChart( const std::map<wxString, wxString>& aProperties,
3126 std::vector<LIB_SYMBOL*>& aSymbol )
3127{
3128 ParseArc( aProperties, aSymbol );
3129
3130 ASCH_PIECHART elem( aProperties );
3131
3132 int arc_radius = elem.m_Radius;
3133 VECTOR2I center = elem.m_Center;
3134 EDA_ANGLE startAngle( elem.m_EndAngle, DEGREES_T );
3135 EDA_ANGLE endAngle( elem.m_StartAngle, DEGREES_T );
3136 VECTOR2I startOffset = KiROUND( arc_radius * startAngle.Cos(), -( arc_radius * startAngle.Sin() ) );
3137 VECTOR2I endOffset = KiROUND( arc_radius * endAngle.Cos(), -( arc_radius * endAngle.Sin() ) );
3138
3139 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3140 {
3141 SCH_SCREEN* screen = getCurrentScreen();
3142 wxCHECK( screen, /* void */ );
3143
3144 // close polygon
3146 line->SetEndPoint( center + startOffset + m_sheetOffset );
3148
3149 line->SetFlags( IS_NEW );
3150 screen->Append( line );
3151
3153 line->SetEndPoint( center + endOffset + m_sheetOffset );
3155
3156 line->SetFlags( IS_NEW );
3157 screen->Append( line );
3158 }
3159 else
3160 {
3161 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
3162 : aSymbol[elem.ownerpartdisplaymode];
3163 SCH_SYMBOL* schsym = nullptr;
3164
3165 if( !symbol )
3166 {
3167 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3168
3169 if( libSymbolIt == m_libSymbols.end() )
3170 {
3171 // TODO: e.g. can depend on Template (RECORD=39
3172 m_errorMessages.emplace( wxString::Format( wxT( "Piechart's owner (%d) not found." ),
3173 elem.ownerindex ),
3175 return;
3176 }
3177
3178 symbol = libSymbolIt->second;
3179 schsym = m_symbols.at( libSymbolIt->first );
3180 }
3181
3182 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
3183 return;
3184
3186 symbol->AddDrawItem( line, false );
3187
3188 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3189
3190 if( !schsym )
3191 {
3192 line->AddPoint( center + startOffset );
3193 line->AddPoint( center );
3194 line->AddPoint( center + endOffset );
3195 }
3196 else
3197 {
3198 line->AddPoint( GetRelativePosition( center + startOffset + m_sheetOffset, schsym ) );
3199 line->AddPoint( GetRelativePosition( center + m_sheetOffset, schsym ) );
3200 line->AddPoint( GetRelativePosition( center + endOffset + m_sheetOffset, schsym ) );
3201 }
3202
3204 }
3205}
3206
3207
3208void SCH_IO_ALTIUM::ParseEllipse( const std::map<wxString, wxString>& aProperties,
3209 std::vector<LIB_SYMBOL*>& aSymbol )
3210{
3211 ASCH_ELLIPSE elem( aProperties );
3212
3213 if( elem.Radius == elem.SecondaryRadius )
3214 {
3215 ParseCircle( aProperties, aSymbol );
3216 return;
3217 }
3218
3219 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3220 {
3221 SCH_SCREEN* screen = getCurrentScreen();
3222 wxCHECK( screen, /* void */ );
3223
3224 COLOR4D fillColor = GetColorFromInt( elem.AreaColor );
3225
3226 if( elem.IsTransparent )
3227 fillColor = fillColor.WithAlpha( 0.5 );
3228
3230
3231 ELLIPSE<int> ellipse( elem.Center + m_sheetOffset, elem.Radius,
3232 KiROUND( elem.SecondaryRadius ), ANGLE_0 );
3233
3234 std::vector<BEZIER<int>> beziers;
3235 std::vector<VECTOR2I> polyPoints;
3236
3237 TransformEllipseToBeziers( ellipse, beziers );
3238
3239 for( const BEZIER<int>& bezier : beziers )
3240 {
3241 SCH_SHAPE* schbezier = new SCH_SHAPE( SHAPE_T::BEZIER );
3242 schbezier->SetStart( bezier.Start );
3243 schbezier->SetBezierC1( bezier.C1 );
3244 schbezier->SetBezierC2( bezier.C2 );
3245 schbezier->SetEnd( bezier.End );
3246 schbezier->SetStroke( STROKE_PARAMS( elem.LineWidth, LINE_STYLE::SOLID ) );
3247 schbezier->SetFillColor( fillColor );
3248 schbezier->SetFillMode( fillMode );
3249
3251 screen->Append( schbezier );
3252
3253 polyPoints.push_back( bezier.Start );
3254 }
3255
3256 if( fillMode != FILL_T::NO_FILL )
3257 {
3258 SCH_SHAPE* schpoly = new SCH_SHAPE( SHAPE_T::POLY );
3259 schpoly->SetFillColor( fillColor );
3260 schpoly->SetFillMode( fillMode );
3261 schpoly->SetWidth( -1 );
3262
3263 for( const VECTOR2I& point : polyPoints )
3264 schpoly->AddPoint( point );
3265
3266 schpoly->AddPoint( polyPoints[0] );
3267
3268 screen->Append( schpoly );
3269 }
3270 }
3271 else
3272 {
3273 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
3274 : aSymbol[elem.ownerpartdisplaymode];
3275 SCH_SYMBOL* schsym = nullptr;
3276
3277 if( !symbol )
3278 {
3279 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3280
3281 if( libSymbolIt == m_libSymbols.end() )
3282 {
3283 // TODO: e.g. can depend on Template (RECORD=39
3284 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
3286 return;
3287 }
3288
3289 symbol = libSymbolIt->second;
3290 schsym = m_symbols.at( libSymbolIt->first );
3291 }
3292
3293 ELLIPSE<int> ellipse( elem.Center, elem.Radius, KiROUND( elem.SecondaryRadius ),
3294 ANGLE_0 );
3295
3296 std::vector<BEZIER<int>> beziers;
3297 std::vector<VECTOR2I> polyPoints;
3298
3299 TransformEllipseToBeziers( ellipse, beziers );
3300
3301 for( const BEZIER<int>& bezier : beziers )
3302 {
3303 SCH_SHAPE* libbezier = new SCH_SHAPE( SHAPE_T::BEZIER, LAYER_DEVICE );
3304 symbol->AddDrawItem( libbezier, false );
3305 libbezier->SetUnit( std::max( 0, elem.ownerpartid ) );
3306
3307 if( !schsym )
3308 {
3309 libbezier->SetStart( bezier.Start );
3310 libbezier->SetBezierC1( bezier.C1 );
3311 libbezier->SetBezierC2( bezier.C2 );
3312 libbezier->SetEnd( bezier.End );
3313 }
3314 else
3315 {
3316 libbezier->SetStart( GetRelativePosition( bezier.Start + m_sheetOffset, schsym ) );
3317 libbezier->SetBezierC1( GetRelativePosition( bezier.C1 + m_sheetOffset, schsym ) );
3318 libbezier->SetBezierC2( GetRelativePosition( bezier.C2 + m_sheetOffset, schsym ) );
3319 libbezier->SetEnd( GetRelativePosition( bezier.End + m_sheetOffset, schsym ) );
3320 }
3321
3322 SetLibShapeLine( elem, libbezier, ALTIUM_SCH_RECORD::ELLIPSE );
3325
3326 polyPoints.push_back( libbezier->GetStart() );
3327 }
3328
3329 // A series of beziers won't fill the center, so if this is meant to be fully filled,
3330 // Add a polygon to fill the center
3331 if( elem.IsSolid )
3332 {
3333 SCH_SHAPE* libline = new SCH_SHAPE( SHAPE_T::POLY, LAYER_DEVICE );
3334 symbol->AddDrawItem( libline, false );
3335 libline->SetUnit( std::max( 0, elem.ownerpartid ) );
3336
3337 for( const VECTOR2I& point : polyPoints )
3338 libline->AddPoint( point );
3339
3340 libline->AddPoint( polyPoints[0] );
3341
3342 libline->SetWidth( -1 );
3344 }
3345 }
3346}
3347
3348
3349void SCH_IO_ALTIUM::ParseCircle( const std::map<wxString, wxString>& aProperties,
3350 std::vector<LIB_SYMBOL*>& aSymbol )
3351{
3352 ASCH_ELLIPSE elem( aProperties );
3353
3354 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3355 {
3356 SCH_SCREEN* screen = getCurrentScreen();
3357 wxCHECK( screen, /* void */ );
3358
3360
3361 circle->SetPosition( elem.Center + m_sheetOffset );
3362 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
3363 circle->SetStroke( STROKE_PARAMS( 1, LINE_STYLE::SOLID ) );
3364
3365 circle->SetFillColor( GetColorFromInt( elem.AreaColor ) );
3366
3367 if( elem.IsSolid )
3368 circle->SetFillMode( FILL_T::FILLED_WITH_COLOR );
3369 else
3370 circle->SetFilled( false );
3371
3372 screen->Append( circle );
3373 }
3374 else
3375 {
3376 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
3377 : aSymbol[elem.ownerpartdisplaymode];
3378 SCH_SYMBOL* schsym = nullptr;
3379
3380 if( !symbol )
3381 {
3382 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3383
3384 if( libSymbolIt == m_libSymbols.end() )
3385 {
3386 // TODO: e.g. can depend on Template (RECORD=39
3387 m_errorMessages.emplace( wxString::Format( wxT( "Ellipse's owner (%d) not found." ), elem.ownerindex ),
3389 return;
3390 }
3391
3392 symbol = libSymbolIt->second;
3393 schsym = m_symbols.at( libSymbolIt->first );
3394 }
3395
3396 VECTOR2I center = elem.Center;
3398 symbol->AddDrawItem( circle, false );
3399
3400 circle->SetUnit( std::max( 0, elem.ownerpartid ) );
3401
3402 if( schsym )
3404
3405 circle->SetPosition( center );
3406 circle->SetEnd( circle->GetPosition() + VECTOR2I( elem.Radius, 0 ) );
3407
3410 }
3411}
3412
3413
3414void SCH_IO_ALTIUM::ParseLine( const std::map<wxString, wxString>& aProperties,
3415 std::vector<LIB_SYMBOL*>& aSymbol )
3416{
3417 ASCH_LINE elem( aProperties );
3418
3419 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3420 {
3421 SCH_SCREEN* screen = getCurrentScreen();
3422 wxCHECK( screen, /* void */ );
3423
3424 // close polygon
3426 line->SetEndPoint( elem.point2 + m_sheetOffset );
3428 GetColorFromInt( elem.Color ) ) );
3429
3430 line->SetFlags( IS_NEW );
3431 screen->Append( line );
3432 }
3433 else
3434 {
3435 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
3436 : aSymbol[elem.ownerpartdisplaymode];
3437 SCH_SYMBOL* schsym = nullptr;
3438
3439 if( !symbol )
3440 {
3441 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3442
3443 if( libSymbolIt == m_libSymbols.end() )
3444 {
3445 // TODO: e.g. can depend on Template (RECORD=39
3446 m_errorMessages.emplace( wxString::Format( wxT( "Line's owner (%d) not found." ), elem.ownerindex ),
3448 return;
3449 }
3450
3451 symbol = libSymbolIt->second;
3452 schsym = m_symbols.at( libSymbolIt->first );
3453 }
3454
3455 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
3456 return;
3457
3459 symbol->AddDrawItem( line, false );
3460
3461 line->SetUnit( std::max( 0, elem.ownerpartid ) );
3462
3463 if( !schsym )
3464 {
3465 line->AddPoint( elem.point1 );
3466 line->AddPoint( elem.point2 );
3467 }
3468 else
3469 {
3470 line->AddPoint( GetRelativePosition( elem.point1 + m_sheetOffset, schsym ) );
3471 line->AddPoint( GetRelativePosition( elem.point2 + m_sheetOffset, schsym ) );
3472 }
3473
3475 line->SetLineStyle( GetPlotDashType( elem.LineStyle ) );
3476 }
3477}
3478
3479
3480void SCH_IO_ALTIUM::ParseSignalHarness( const std::map<wxString, wxString>& aProperties )
3481{
3482 ASCH_SIGNAL_HARNESS elem( aProperties );
3483
3484 if( ShouldPutItemOnSheet( elem.ownerindex ) )
3485 {
3486 SCH_SCREEN* screen = getCurrentScreen();
3487 wxCHECK( screen, /* void */ );
3488
3489 for( size_t ii = 0; ii < elem.points.size() - 1; ii++ )
3490 {
3492 line->SetEndPoint( elem.points[ii + 1] + m_sheetOffset );
3494
3495 line->SetFlags( IS_NEW );
3496 screen->Append( line );
3497 }
3498 }
3499 else
3500 {
3501 // No clue if this situation can ever exist
3502 m_errorMessages.emplace( wxT( "Signal harness, belonging to the part is not currently supported." ),
3504 }
3505}
3506
3507
3508void SCH_IO_ALTIUM::ParseHarnessConnector( int aIndex, const std::map<wxString,
3509 wxString>& aProperties )
3510{
3511 ASCH_HARNESS_CONNECTOR elem( aProperties );
3512
3513 if( ShouldPutItemOnSheet( elem.ownerindex ) )
3514 {
3516 auto [it, _] = m_altiumHarnesses.insert( { m_harnessEntryParent, HARNESS()} );
3517
3518 HARNESS& harness = it->second;
3519 HARNESS::HARNESS_PORT& port = harness.m_entry;
3520 harness.m_location = elem.m_location + m_sheetOffset;
3521 harness.m_size = elem.m_size;
3522
3523 VECTOR2I pos = elem.m_location + m_sheetOffset;
3524 VECTOR2I size = elem.m_size;
3525
3526 switch( elem.m_harnessConnectorSide )
3527 {
3528 default:
3530 port.m_location = { pos.x, pos.y + elem.m_primaryConnectionPosition };
3531 break;
3533 port.m_location = { pos.x + size.x, pos.y + elem.m_primaryConnectionPosition };
3534 break;
3536 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y };
3537 break;
3539 port.m_location = { pos.x + elem.m_primaryConnectionPosition, pos.y + size.y };
3540 break;
3541 }
3542 }
3543 else
3544 {
3545 // I have no clue if this situation can ever exist
3546 m_errorMessages.emplace( wxT( "Harness connector, belonging to the part is not currently supported." ),
3548 }
3549}
3550
3551
3552void SCH_IO_ALTIUM::ParseHarnessEntry( const std::map<wxString, wxString>& aProperties )
3553{
3554 ASCH_HARNESS_ENTRY elem( aProperties );
3555
3556 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
3557
3558 if( harnessIt == m_altiumHarnesses.end() )
3559 {
3560 m_errorMessages.emplace( wxString::Format( wxT( "Harness entry's parent (%d) not found." ),
3563 return;
3564 }
3565
3566 HARNESS& harness = harnessIt->second;
3568 port.m_name = elem.Name;
3569 port.m_harnessConnectorSide = elem.Side;
3571
3572 VECTOR2I pos = harness.m_location;
3573 VECTOR2I size = harness.m_size;
3574 int quadrant = 1;
3575
3576 switch( elem.Side )
3577 {
3578 default:
3580 port.m_location = { pos.x, pos.y + elem.DistanceFromTop };
3581 break;
3583 quadrant = 4;
3584 port.m_location = { pos.x + size.x, pos.y + elem.DistanceFromTop };
3585 break;
3587 port.m_location = { pos.x + elem.DistanceFromTop, pos.y };
3588 break;
3590 quadrant = 2;
3591 port.m_location = { pos.x + elem.DistanceFromTop, pos.y + size.y };
3592 break;
3593 }
3594
3595
3596 SCH_SCREEN* screen = getCurrentScreen();
3597 wxCHECK( screen, /* void */ );
3598
3599 SCH_BUS_WIRE_ENTRY* entry = new SCH_BUS_WIRE_ENTRY( port.m_location, quadrant );
3600 port.m_entryLocation = entry->GetPosition() + entry->GetSize();
3601 entry->SetFlags( IS_NEW );
3602 screen->Append( entry );
3603 harness.m_ports.emplace_back( port );
3604}
3605
3606
3607void SCH_IO_ALTIUM::ParseHarnessType( const std::map<wxString, wxString>& aProperties )
3608{
3609 ASCH_HARNESS_TYPE elem( aProperties );
3610
3611 auto harnessIt = m_altiumHarnesses.find( m_harnessEntryParent );
3612
3613 if( harnessIt == m_altiumHarnesses.end() )
3614 {
3615 m_errorMessages.emplace( wxString::Format( wxT( "Harness type's parent (%d) not found." ),
3618 return;
3619 }
3620
3621 HARNESS& harness = harnessIt->second;
3622 harness.m_name = elem.Text;
3623}
3624
3625
3626void SCH_IO_ALTIUM::ParseRectangle( const std::map<wxString, wxString>& aProperties,
3627 std::vector<LIB_SYMBOL*>& aSymbol )
3628{
3629 ASCH_RECTANGLE elem( aProperties );
3630
3631 VECTOR2I sheetTopRight = elem.TopRight + m_sheetOffset;
3632 VECTOR2I sheetBottomLeft = elem.BottomLeft + m_sheetOffset;
3633
3634 if( aSymbol.empty() && ShouldPutItemOnSheet( elem.ownerindex ) )
3635 {
3636 SCH_SCREEN* screen = getCurrentScreen();
3637 wxCHECK( screen, /* void */ );
3638
3639 SCH_SHAPE* rect = new SCH_SHAPE( SHAPE_T::RECTANGLE );
3640
3641 rect->SetPosition( sheetTopRight );
3642 rect->SetEnd( sheetBottomLeft );
3643 SetSchShapeLine( elem, rect );
3644 SetSchShapeFillAndColor( elem, rect );
3645 rect->SetFlags( IS_NEW );
3646
3647 screen->Append( rect );
3648 }
3649 else
3650 {
3651 LIB_SYMBOL* symbol = (int) aSymbol.size() <= elem.ownerpartdisplaymode ? nullptr
3652 : aSymbol[elem.ownerpartdisplaymode];
3653 SCH_SYMBOL* schsym = nullptr;
3654
3655 if( !symbol )
3656 {
3657 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
3658
3659 if( libSymbolIt == m_libSymbols.end() )
3660 {
3661 // TODO: e.g. can depend on Template (RECORD=39
3662 m_errorMessages.emplace( wxString::Format( wxT( "Rectangle's owner (%d) not found." ),
3663 elem.ownerindex ),
3665 return;
3666 }
3667
3668 symbol = libSymbolIt->second;
3669 schsym = m_symbols.at( libSymbolIt->first );
3670 }
3671
3672 if( aSymbol.empty() && !IsComponentPartVisible( elem ) )
3673 return;
3674
3676 symbol->AddDrawItem( rect, false );
3677
3678 rect->SetUnit( std::max( 0, elem.ownerpartid ) );
3679
3680 if( !schsym )
3681 {
3682 rect->SetPosition( sheetTopRight );
3683 rect->SetEnd( sheetBottomLeft );
3684 }
3685 else
3686 {
3687 rect->SetPosition( GetRelativePosition( sheetTopRight, schsym ) );
3688 rect->SetEnd( GetRelativePosition( sheetBottomLeft, schsym ) );
3689 }
3690
3693 }
3694}
3695
3696
3697void SCH_IO_ALTIUM::ParseSheetSymbol( int aIndex, const std::map<wxString, wxString>& aProperties )
3698{
3699 ASCH_SHEET_SYMBOL elem( aProperties );
3700
3701 SCH_SHEET* sheet = new SCH_SHEET( getCurrentSheet(), elem.location + m_sheetOffset, elem.size );
3702
3703 sheet->SetBorderColor( GetColorFromInt( elem.color ) );
3704
3705 if( elem.isSolid )
3707
3708 sheet->SetFlags( IS_NEW );
3709
3710 SCH_SCREEN* currentScreen = getCurrentScreen();
3711 wxCHECK( currentScreen, /* void */ );
3712 currentScreen->Append( sheet );
3713
3714 SCH_SHEET_PATH sheetpath = m_sheetPath;
3715 sheetpath.push_back( sheet );
3716
3717 // We'll update later if we find a pageNumber record for it.
3718 sheetpath.SetPageNumber( "#" );
3719
3720 SCH_SCREEN* rootScreen = m_rootSheet->GetScreen();
3721 wxCHECK( rootScreen, /* void */ );
3722
3723 SCH_SHEET_INSTANCE sheetInstance;
3724
3725 sheetInstance.m_Path = sheetpath.Path();
3726 sheetInstance.m_PageNumber = wxT( "#" );
3727
3728 rootScreen->m_sheetInstances.emplace_back( sheetInstance );
3729 m_sheets.insert( { aIndex, sheet } );
3730}
3731
3732
3733void SCH_IO_ALTIUM::ParseSheetEntry( const std::map<wxString, wxString>& aProperties )
3734{
3735 ASCH_SHEET_ENTRY elem( aProperties );
3736
3737 const auto& sheetIt = m_sheets.find( elem.ownerindex );
3738
3739 if( sheetIt == m_sheets.end() )
3740 {
3741 m_errorMessages.emplace( wxString::Format( wxT( "Sheet entry's owner (%d) not found." ), elem.ownerindex ),
3743 return;
3744 }
3745
3746 SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheetIt->second );
3747 sheetIt->second->AddPin( sheetPin );
3748
3749 wxString pinName = elem.name;
3750
3751 if( !elem.harnessType.IsEmpty() )
3752 pinName += wxT( "{" ) + elem.harnessType + wxT( "}" );
3753
3754 sheetPin->SetText( pinName );
3756 //sheetPin->SetSpinStyle( getSpinStyle( term.OrientAngle, false ) );
3757 //sheetPin->SetPosition( getKiCadPoint( term.Position ) );
3758
3759 VECTOR2I pos = sheetIt->second->GetPosition();
3760 VECTOR2I size = sheetIt->second->GetSize();
3761
3762 switch( elem.side )
3763 {
3764 default:
3766 sheetPin->SetPosition( { pos.x, pos.y + elem.distanceFromTop } );
3767 sheetPin->SetSpinStyle( SPIN_STYLE::LEFT );
3768 sheetPin->SetSide( SHEET_SIDE::LEFT );
3769 break;
3770
3772 sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.distanceFromTop } );
3773 sheetPin->SetSpinStyle( SPIN_STYLE::RIGHT );
3774 sheetPin->SetSide( SHEET_SIDE::RIGHT );
3775 break;
3776
3778 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y } );
3779 sheetPin->SetSpinStyle( SPIN_STYLE::UP );
3780 sheetPin->SetSide( SHEET_SIDE::TOP );
3781 break;
3782
3784 sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y + size.y } );
3785 sheetPin->SetSpinStyle( SPIN_STYLE::BOTTOM );
3786 sheetPin->SetSide( SHEET_SIDE::BOTTOM );
3787 break;
3788 }
3789
3790 switch( elem.iotype )
3791 {
3792 default:
3795 break;
3796
3799 break;
3800
3803 break;
3804
3807 break;
3808 }
3809}
3810
3811
3813 REPORTER* aReporter )
3814{
3816 {
3818 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3819 line1->AddPoint( { 0, 0 } );
3820 line1->AddPoint( { 0, schIUScale.MilsToIU( 50 ) } );
3821 aKsymbol->AddDrawItem( line1, false );
3822
3823 if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE )
3824 {
3826 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
3827 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 75 ) } );
3828 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 25 ), 0 ) );
3829 aKsymbol->AddDrawItem( circle, false );
3830 }
3831 else
3832 {
3834 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3835 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
3836 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
3837 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
3838 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
3839 aKsymbol->AddDrawItem( line2, false );
3840 }
3841
3842 return { 0, schIUScale.MilsToIU( 150 ) };
3843 }
3844 else if( aStyle == ASCH_POWER_PORT_STYLE::WAVE )
3845 {
3847 line->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3848 line->AddPoint( { 0, 0 } );
3849 line->AddPoint( { 0, schIUScale.MilsToIU( 72 ) } );
3850 aKsymbol->AddDrawItem( line, false );
3851
3853 bezier->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 5 ), LINE_STYLE::SOLID ) );
3854 bezier->SetStart( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 50 ) } );
3855 bezier->SetBezierC1( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( 87 ) } );
3856 bezier->SetBezierC2( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 63 ) } );
3857 bezier->SetEnd( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( 100 ) } );
3858 aKsymbol->AddDrawItem( bezier, false );
3859
3860 return { 0, schIUScale.MilsToIU( 150 ) };
3861 }
3862 else if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND
3864 || aStyle == ASCH_POWER_PORT_STYLE::EARTH
3866 {
3868 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3869 line1->AddPoint( { 0, 0 } );
3870 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
3871 aKsymbol->AddDrawItem( line1, false );
3872
3874 {
3876 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3877 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3878 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
3879 aKsymbol->AddDrawItem( line2, false );
3880
3882 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3883 line3->AddPoint( { schIUScale.MilsToIU( -70 ), schIUScale.MilsToIU( 130 ) } );
3884 line3->AddPoint( { schIUScale.MilsToIU( 70 ), schIUScale.MilsToIU( 130 ) } );
3885 aKsymbol->AddDrawItem( line3, false );
3886
3888 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3889 line4->AddPoint( { schIUScale.MilsToIU( -40 ), schIUScale.MilsToIU( 160 ) } );
3890 line4->AddPoint( { schIUScale.MilsToIU( 40 ), schIUScale.MilsToIU( 160 ) } );
3891 aKsymbol->AddDrawItem( line4, false );
3892
3894 line5->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3895 line5->AddPoint( { schIUScale.MilsToIU( -10 ), schIUScale.MilsToIU( 190 ) } );
3896 line5->AddPoint( { schIUScale.MilsToIU( 10 ), schIUScale.MilsToIU( 190 ) } );
3897 aKsymbol->AddDrawItem( line5, false );
3898 }
3899 else if( aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND )
3900 {
3902 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3903 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3904 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
3905 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 200 ) } );
3906 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3907 aKsymbol->AddDrawItem( line2, false );
3908 }
3909 else if( aStyle == ASCH_POWER_PORT_STYLE::EARTH )
3910 {
3912 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3913 line2->AddPoint( { schIUScale.MilsToIU( -150 ), schIUScale.MilsToIU( 200 ) } );
3914 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 100 ) } );
3915 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 100 ) } );
3916 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 200 ) } );
3917 aKsymbol->AddDrawItem( line2, false );
3918
3920 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3921 line3->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
3922 line3->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 200 ) } );
3923 aKsymbol->AddDrawItem( line3, false );
3924 }
3925 else // ASCH_POWER_PORT_STYLE::GOST_ARROW
3926 {
3928 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3929 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( 50 ) } );
3930 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 100 ) } );
3931 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( 50 ) } );
3932 aKsymbol->AddDrawItem( line2, false );
3933
3934 return { 0, schIUScale.MilsToIU( 150 ) }; // special case
3935 }
3936
3937 return { 0, schIUScale.MilsToIU( 250 ) };
3938 }
3941 {
3943 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3944 line1->AddPoint( { 0, 0 } );
3945 line1->AddPoint( { 0, schIUScale.MilsToIU( 160 ) } );
3946 aKsymbol->AddDrawItem( line1, false );
3947
3949 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3950 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 160 ) } );
3951 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 160 ) } );
3952 aKsymbol->AddDrawItem( line2, false );
3953
3955 line3->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3956 line3->AddPoint( { schIUScale.MilsToIU( -60 ), schIUScale.MilsToIU( 200 ) } );
3957 line3->AddPoint( { schIUScale.MilsToIU( 60 ), schIUScale.MilsToIU( 200 ) } );
3958 aKsymbol->AddDrawItem( line3, false );
3959
3961 line4->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3962 line4->AddPoint( { schIUScale.MilsToIU( -20 ), schIUScale.MilsToIU( 240 ) } );
3963 line4->AddPoint( { schIUScale.MilsToIU( 20 ), schIUScale.MilsToIU( 240 ) } );
3964 aKsymbol->AddDrawItem( line4, false );
3965
3967 return { 0, schIUScale.MilsToIU( -300 ) };
3968
3970 circle->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3971 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( 160 ) } );
3972 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 120 ), 0 ) );
3973 aKsymbol->AddDrawItem( circle, false );
3974
3975 return { 0, schIUScale.MilsToIU( 350 ) };
3976 }
3977 else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_BAR )
3978 {
3980 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3981 line1->AddPoint( { 0, 0 } );
3982 line1->AddPoint( { 0, schIUScale.MilsToIU( 200 ) } );
3983 aKsymbol->AddDrawItem( line1, false );
3984
3986 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
3987 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( 200 ) } );
3988 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( 200 ) } );
3989 aKsymbol->AddDrawItem( line2, false );
3990
3991 return { 0, schIUScale.MilsToIU( 250 ) };
3992 }
3993 else
3994 {
3995 if( aStyle != ASCH_POWER_PORT_STYLE::BAR )
3996 {
3997 aReporter->Report( _( "Power Port with unknown style imported as 'Bar' type." ),
3999 }
4000
4002 line1->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4003 line1->AddPoint( { 0, 0 } );
4004 line1->AddPoint( { 0, schIUScale.MilsToIU( 100 ) } );
4005 aKsymbol->AddDrawItem( line1, false );
4006
4008 line2->SetStroke( STROKE_PARAMS( schIUScale.MilsToIU( 10 ), LINE_STYLE::SOLID ) );
4009 line2->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( 100 ) } );
4010 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( 100 ) } );
4011 aKsymbol->AddDrawItem( line2, false );
4012
4013 return { 0, schIUScale.MilsToIU( 150 ) };
4014 }
4015}
4016
4017
4018void SCH_IO_ALTIUM::ParsePowerPort( const std::map<wxString, wxString>& aProperties )
4019{
4020 ASCH_POWER_PORT elem( aProperties );
4021
4022 wxString symName( elem.text );
4023 std::string styleName( magic_enum::enum_name<ASCH_POWER_PORT_STYLE>( elem.style ) );
4024
4025 if( !styleName.empty() )
4026 symName << '_' << styleName;
4027
4028 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symName );
4029 LIB_SYMBOL* libSymbol = nullptr;
4030
4031 const auto& powerSymbolIt = m_powerSymbols.find( symName );
4032
4033 if( powerSymbolIt != m_powerSymbols.end() )
4034 {
4035 libSymbol = powerSymbolIt->second; // cache hit
4036 }
4037 else
4038 {
4039 libSymbol = new LIB_SYMBOL( wxEmptyString );
4040 libSymbol->SetGlobalPower();
4041 libSymbol->SetName( symName );
4042 libSymbol->GetReferenceField().SetText( "#PWR" );
4043 libSymbol->GetReferenceField().SetVisible( false );
4044 libSymbol->GetValueField().SetText( elem.text );
4045 libSymbol->GetValueField().SetVisible( true );
4046 libSymbol->SetDescription( wxString::Format( _( "Power symbol creates a global label with name '%s'" ),
4047 elem.text ) );
4048 libSymbol->SetKeyWords( "power-flag" );
4049 libSymbol->SetLibId( libId );
4050
4051 // generate graphic
4052 SCH_PIN* pin = new SCH_PIN( libSymbol );
4053 libSymbol->AddDrawItem( pin, false );
4054
4055 pin->SetName( elem.text );
4056 pin->SetPosition( { 0, 0 } );
4057 pin->SetLength( 0 );
4059 pin->SetVisible( false );
4060
4061 VECTOR2I valueFieldPos = HelperGeneratePowerPortGraphics( libSymbol, elem.style, m_reporter );
4062
4063 libSymbol->GetValueField().SetPosition( valueFieldPos );
4064
4065 // this has to be done after parsing the LIB_SYMBOL!
4066 m_powerSymbols.insert( { symName, libSymbol } );
4067 }
4068
4069 SCH_SCREEN* screen = getCurrentScreen();
4070 wxCHECK( screen, /* void */ );
4071
4072 SCH_SYMBOL* symbol = new SCH_SYMBOL();
4073 symbol->SetRef( &m_sheetPath, "#PWR?" );
4074 symbol->GetField( FIELD_T::REFERENCE )->SetVisible( false );
4075 symbol->SetValueFieldText( elem.text );
4076 symbol->SetLibId( libId );
4077 symbol->SetLibSymbol( new LIB_SYMBOL( *libSymbol ) );
4078
4079 SCH_FIELD* valueField = symbol->GetField( FIELD_T::VALUE );
4080 valueField->SetVisible( elem.showNetName );
4081 valueField->SetPosition( libSymbol->GetValueField().GetPosition() );
4082
4083 symbol->SetPosition( elem.location + m_sheetOffset );
4084
4085 switch( elem.orientation )
4086 {
4089 valueField->SetTextAngle( ANGLE_VERTICAL );
4091 break;
4092
4095 valueField->SetTextAngle( ANGLE_HORIZONTAL );
4097 break;
4098
4101 valueField->SetTextAngle( ANGLE_VERTICAL );
4103 break;
4104
4107 valueField->SetTextAngle( ANGLE_HORIZONTAL );
4109 break;
4110
4111 default:
4112 m_errorMessages.emplace( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
4113 break;
4114 }
4115
4116 screen->Append( symbol );
4117}
4118
4119
4121{
4122 ParsePortHelper( aElem );
4123}
4124
4125
4127{
4128 if( !aElem.HarnessType.IsEmpty() )
4129 {
4130 // Parse harness ports after "Additional" compound section is parsed
4131 m_altiumHarnessPortsCurrentSheet.emplace_back( aElem );
4132 return;
4133 }
4134
4135 ParsePortHelper( aElem );
4136}
4137
4138
4140{
4141 VECTOR2I start = aElem.Location + m_sheetOffset;
4142 VECTOR2I end = start;
4143
4144 switch( aElem.Style )
4145 {
4146 default:
4151 end.x += aElem.Width;
4152 break;
4153
4158 end.y -= aElem.Width;
4159 break;
4160 }
4161
4162 // Check which connection points exists in the schematic
4163 SCH_SCREEN* screen = getCurrentScreen();
4164 wxCHECK( screen, /* void */ );
4165
4166 bool startIsWireTerminal = screen->IsTerminalPoint( start, LAYER_WIRE );
4167 bool startIsBusTerminal = screen->IsTerminalPoint( start, LAYER_BUS );
4168
4169 bool endIsWireTerminal = screen->IsTerminalPoint( end, LAYER_WIRE );
4170 bool endIsBusTerminal = screen->IsTerminalPoint( end, LAYER_BUS );
4171
4172 // check if any of the points is a terminal point
4173 // TODO: there seems a problem to detect approximated connections towards component pins?
4174 bool connectionFound = startIsWireTerminal
4175 || startIsBusTerminal
4176 || endIsWireTerminal
4177 || endIsBusTerminal;
4178
4179 if( !connectionFound )
4180 {
4181 for( auto& [ _, harness ] : m_altiumHarnesses )
4182 {
4183 if( harness.m_name.CmpNoCase( aElem.HarnessType ) != 0 )
4184 continue;
4185
4186 BOX2I bbox( harness.m_location, harness.m_size );
4187 bbox.Inflate( 10 );
4188
4189 if( bbox.Contains( start ) )
4190 {
4191 startIsBusTerminal = true;
4192 connectionFound = true;
4193 break;
4194 }
4195
4196 if( bbox.Contains( end ) )
4197 {
4198 endIsBusTerminal = true;
4199 connectionFound = true;
4200 break;
4201 }
4202 }
4203
4204 if( !connectionFound )
4205 {
4206 m_errorMessages.emplace( wxString::Format( _( "Port %s has no connections." ), aElem.Name ),
4208 }
4209 }
4210
4211 // Select label position. In case both match, we will add a line later.
4212 VECTOR2I position = ( startIsWireTerminal || startIsBusTerminal ) ? start : end;
4213 SCH_LABEL_BASE* label;
4214
4215 wxString labelName = aElem.Name;
4216
4217 if( !aElem.HarnessType.IsEmpty() )
4218 labelName += wxT( "{" ) + aElem.HarnessType + wxT( "}" );
4219
4220 // TODO: detect correct label type depending on sheet settings, etc.
4221#if 1 // Set to 1 to use SCH_HIERLABEL label, 0 to use SCH_GLOBALLABEL
4222 {
4223 label = new SCH_HIERLABEL( position, labelName );
4224 }
4225#else
4226 label = new SCH_GLOBALLABEL( position, labelName );
4227
4228 // Default "Sheet References" field should be hidden, at least for now
4229 label->GetField( INTERSHEET_REFS )->SetVisible( false );
4230#endif
4231
4232 switch( aElem.IOtype )
4233 {
4234 default:
4239 }
4240
4241 switch( aElem.Style )
4242 {
4243 default:
4248 if( ( startIsWireTerminal || startIsBusTerminal ) )
4250 else
4252
4253 break;
4254
4259 if( ( startIsWireTerminal || startIsBusTerminal ) )
4260 label->SetSpinStyle( SPIN_STYLE::UP );
4261 else
4263
4264 break;
4265 }
4266
4267 label->AutoplaceFields( screen, AUTOPLACE_AUTO );
4268 label->SetFlags( IS_NEW );
4269
4270 screen->Append( label );
4271
4272 // This is a hack, for the case both connection points are valid: add a small wire
4273 if( ( startIsWireTerminal && endIsWireTerminal ) )
4274 {
4275 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
4276 wire->SetEndPoint( end );
4277 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
4278 wire->SetFlags( IS_NEW );
4279 screen->Append( wire );
4280 }
4281 else if( startIsBusTerminal && endIsBusTerminal )
4282 {
4283 SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
4284 wire->SetEndPoint( end );
4285 wire->SetLineWidth( schIUScale.MilsToIU( 2 ) );
4286 wire->SetFlags( IS_NEW );
4287 screen->Append( wire );
4288 }
4289}
4290
4291
4292void SCH_IO_ALTIUM::ParseNoERC( const std::map<wxString, wxString>& aProperties )
4293{
4294 ASCH_NO_ERC elem( aProperties );
4295
4296 SCH_SCREEN* screen = getCurrentScreen();
4297 wxCHECK( screen, /* void */ );
4298
4299 if( elem.isActive )
4300 {
4301 SCH_NO_CONNECT* noConnect = new SCH_NO_CONNECT( elem.location + m_sheetOffset );
4302
4303 noConnect->SetFlags( IS_NEW );
4304 screen->Append( noConnect );
4305 }
4306}
4307
4308
4309void SCH_IO_ALTIUM::ParseNetLabel( const std::map<wxString, wxString>& aProperties )
4310{
4311 ASCH_NET_LABEL elem( aProperties );
4312
4313 SCH_LABEL* label = new SCH_LABEL( elem.location + m_sheetOffset, elem.text );
4314
4315 SCH_SCREEN* screen = getCurrentScreen();
4316 wxCHECK( screen, /* void */ );
4317
4318 SetTextPositioning( label, elem.justification, elem.orientation );
4319
4320 label->SetFlags( IS_NEW );
4321 screen->Append( label );
4322}
4323
4324
4325void SCH_IO_ALTIUM::ParseBus( const std::map<wxString, wxString>& aProperties )
4326{
4327 ASCH_BUS elem( aProperties );
4328
4329 SCH_SCREEN* screen = getCurrentScreen();
4330 wxCHECK( screen, /* void */ );
4331
4332 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
4333 {
4334 SCH_LINE* bus = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_BUS );
4335 bus->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
4336 bus->SetLineWidth( elem.lineWidth );
4337
4338 bus->SetFlags( IS_NEW );
4339 screen->Append( bus );
4340 }
4341}
4342
4343
4344void SCH_IO_ALTIUM::ParseWire( const std::map<wxString, wxString>& aProperties )
4345{
4346 ASCH_WIRE elem( aProperties );
4347
4348 SCH_SCREEN* screen = getCurrentScreen();
4349 wxCHECK( screen, /* void */ );
4350
4351 for( size_t i = 0; i + 1 < elem.points.size(); i++ )
4352 {
4353 SCH_LINE* wire = new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_WIRE );
4354 wire->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
4355 // wire->SetLineWidth( elem.lineWidth );
4356
4357 wire->SetFlags( IS_NEW );
4358 screen->Append( wire );
4359 }
4360}
4361
4362
4363void SCH_IO_ALTIUM::ParseJunction( const std::map<wxString, wxString>& aProperties )
4364{
4365 SCH_SCREEN* screen = getCurrentScreen();
4366 wxCHECK( screen, /* void */ );
4367
4368 ASCH_JUNCTION elem( aProperties );
4369
4370 SCH_JUNCTION* junction = new SCH_JUNCTION( elem.location + m_sheetOffset );
4371
4372 junction->SetFlags( IS_NEW );
4373 screen->Append( junction );
4374}
4375
4376
4377void SCH_IO_ALTIUM::ParseImage( const std::map<wxString, wxString>& aProperties )
4378{
4379 ASCH_IMAGE elem( aProperties );
4380
4381 const auto& component = m_altiumComponents.find( elem.ownerindex );
4382
4383 //Hide the image if it is owned by a component but the part id do not match
4384 if( component != m_altiumComponents.end()
4385 && component->second.currentpartid != elem.ownerpartid )
4386 return;
4387
4388 VECTOR2I center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
4389 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
4390 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
4391
4392 SCH_SCREEN* screen = getCurrentScreen();
4393 wxCHECK( screen, /* void */ );
4394
4395 if( elem.embedimage )
4396 {
4397 const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
4398
4399 if( !storageFile )
4400 {
4401 m_errorMessages.emplace( wxString::Format( _( "Embedded file %s not found in storage." ),
4402 elem.filename ),
4404 return;
4405 }
4406
4407 wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
4408
4409 // As wxZlibInputStream is not seekable, we need to write a temporary file
4410 wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
4411 wxZlibInputStream zlibInputStream( fileStream );
4412 wxFFileOutputStream outputStream( storagePath );
4413 outputStream.Write( zlibInputStream );
4414 outputStream.Close();
4415
4416 if( !refImage.ReadImageFile( storagePath ) )
4417 {
4418 m_errorMessages.emplace( wxString::Format( _( "Error reading image %s." ), storagePath ),
4420 return;
4421 }
4422
4423 // Remove temporary file
4424 wxRemoveFile( storagePath );
4425 }
4426 else
4427 {
4428 if( !wxFileExists( elem.filename ) )
4429 {
4430 m_errorMessages.emplace( wxString::Format( _( "File not found %s." ), elem.filename ),
4432 return;
4433 }
4434
4435 if( !refImage.ReadImageFile( elem.filename ) )
4436 {
4437 m_errorMessages.emplace( wxString::Format( _( "Error reading image %s." ), elem.filename ),
4439 return;
4440 }
4441 }
4442
4443 // we only support one scale, thus we need to select one in case it does not keep aspect ratio
4444 const VECTOR2I currentImageSize = refImage.GetSize();
4445 const VECTOR2I expectedImageSize = elem.location - elem.corner;
4446 const double scaleX = std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
4447 const double scaleY = std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
4448 refImage.SetImageScale( std::min( scaleX, scaleY ) );
4449
4450 bitmap->SetFlags( IS_NEW );
4451 screen->Append( bitmap.release() );
4452}
4453
4454
4455void SCH_IO_ALTIUM::ParseSheet( const std::map<wxString, wxString>& aProperties )
4456{
4457 m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
4458
4459 SCH_SCREEN* screen = getCurrentScreen();
4460 wxCHECK( screen, /* void */ );
4461
4462 PAGE_INFO pageInfo;
4463
4464 bool isPortrait = m_altiumSheet->sheetOrientation == ASCH_SHEET_WORKSPACEORIENTATION::PORTRAIT;
4465
4466 if( m_altiumSheet->useCustomSheet )
4467 {
4468 PAGE_INFO::SetCustomWidthMils( schIUScale.IUToMils( m_altiumSheet->customSize.x ) );
4469 PAGE_INFO::SetCustomHeightMils( schIUScale.IUToMils( m_altiumSheet->customSize.y ) );
4470 pageInfo.SetType( PAGE_SIZE_TYPE::User, isPortrait );
4471 }
4472 else
4473 {
4474 switch( m_altiumSheet->sheetSize )
4475 {
4476 default:
4477 case ASCH_SHEET_SIZE::A4: pageInfo.SetType( "A4", isPortrait ); break;
4478 case ASCH_SHEET_SIZE::A3: pageInfo.SetType( "A3", isPortrait ); break;
4479 case ASCH_SHEET_SIZE::A2: pageInfo.SetType( "A2", isPortrait ); break;
4480 case ASCH_SHEET_SIZE::A1: pageInfo.SetType( "A1", isPortrait ); break;
4481 case ASCH_SHEET_SIZE::A0: pageInfo.SetType( "A0", isPortrait ); break;
4482 case ASCH_SHEET_SIZE::A: pageInfo.SetType( "A", isPortrait ); break;
4483 case ASCH_SHEET_SIZE::B: pageInfo.SetType( "B", isPortrait ); break;
4484 case ASCH_SHEET_SIZE::C: pageInfo.SetType( "C", isPortrait ); break;
4485 case ASCH_SHEET_SIZE::D: pageInfo.SetType( "D", isPortrait ); break;
4486 case ASCH_SHEET_SIZE::E: pageInfo.SetType( "E", isPortrait ); break;
4487 case ASCH_SHEET_SIZE::LETTER: pageInfo.SetType( "USLetter", isPortrait ); break;
4488 case ASCH_SHEET_SIZE::LEGAL: pageInfo.SetType( "USLegal", isPortrait ); break;
4489 case ASCH_SHEET_SIZE::TABLOID: pageInfo.SetType( "A3", isPortrait ); break;
4490 case ASCH_SHEET_SIZE::ORCAD_A: pageInfo.SetType( "A", isPortrait ); break;
4491 case ASCH_SHEET_SIZE::ORCAD_B: pageInfo.SetType( "B", isPortrait ); break;
4492 case ASCH_SHEET_SIZE::ORCAD_C: pageInfo.SetType( "C", isPortrait ); break;
4493 case ASCH_SHEET_SIZE::ORCAD_D: pageInfo.SetType( "D", isPortrait ); break;
4494 case ASCH_SHEET_SIZE::ORCAD_E: pageInfo.SetType( "E", isPortrait ); break;
4495 }
4496 }
4497
4498 screen->SetPageSettings( pageInfo );
4499
4500 m_sheetOffset = { 0, pageInfo.GetHeightIU( schIUScale.IU_PER_MILS ) };
4501}
4502
4503
4504void SCH_IO_ALTIUM::ParseSheetName( const std::map<wxString, wxString>& aProperties )
4505{
4506 ASCH_SHEET_NAME elem( aProperties );
4507 SCH_SCREEN* currentScreen = getCurrentScreen();
4508
4509 wxCHECK( currentScreen, /* void */ );
4510
4511 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4512
4513 if( sheetIt == m_sheets.end() )
4514 {
4515 m_errorMessages.emplace( wxString::Format( wxT( "Sheetname's owner (%d) not found." ),
4516 elem.ownerindex ),
4518 return;
4519 }
4520
4521 wxString baseName = elem.text;
4522 baseName.Replace( wxT( "/" ), wxT( "_" ) );
4523
4524 wxString sheetName = baseName;
4525 std::set<wxString> sheetNames;
4526
4527 for( EDA_ITEM* item : currentScreen->Items().OfType( SCH_SHEET_T ) )
4528 {
4529 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
4530 sheetNames.insert( sheet->GetName() );
4531 }
4532
4533 for( int ii = 1; ; ++ii )
4534 {
4535 if( sheetNames.find( sheetName ) == sheetNames.end() )
4536 break;
4537
4538 sheetName = baseName + wxString::Format( wxT( "_%d" ), ii );
4539 }
4540
4541 SCH_FIELD* sheetNameField = sheetIt->second->GetField( FIELD_T::SHEET_NAME );
4542
4543 sheetNameField->SetPosition( elem.location + m_sheetOffset );
4544 sheetNameField->SetText( sheetName );
4545 sheetNameField->SetVisible( !elem.isHidden );
4547}
4548
4549
4550void SCH_IO_ALTIUM::ParseFileName( const std::map<wxString, wxString>& aProperties )
4551{
4552 ASCH_FILE_NAME elem( aProperties );
4553
4554 const auto& sheetIt = m_sheets.find( elem.ownerindex );
4555
4556 if( sheetIt == m_sheets.end() )
4557 {
4558 m_errorMessages.emplace( wxString::Format( wxT( "Filename's owner (%d) not found." ),
4559 elem.ownerindex ),
4561 return;
4562 }
4563
4564 SCH_FIELD* filenameField = sheetIt->second->GetField( FIELD_T::SHEET_FILENAME );
4565
4566 filenameField->SetPosition( elem.location + m_sheetOffset );
4567
4568 // Keep the filename of the Altium file until after the file is actually loaded.
4569 filenameField->SetText( elem.text );
4570 filenameField->SetVisible( !elem.isHidden );
4572}
4573
4574
4575void SCH_IO_ALTIUM::ParseDesignator( const std::map<wxString, wxString>& aProperties )
4576{
4577 ASCH_DESIGNATOR elem( aProperties );
4578
4579 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4580
4581 if( libSymbolIt == m_libSymbols.end() )
4582 {
4583 // TODO: e.g. can depend on Template (RECORD=39
4584 m_errorMessages.emplace( wxString::Format( wxT( "Designator's owner (%d) not found." ),
4585 elem.ownerindex ),
4587 return;
4588 }
4589
4590 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4591 SCH_SHEET_PATH sheetpath;
4592
4593 SCH_SCREEN* screen = getCurrentScreen();
4594 wxCHECK( screen, /* void */ );
4595
4596 // Graphics symbols have no reference. '#GRAPHIC' allows them to not have footprint associated.
4597 // Note: not all unnamed imported symbols are necessarily graphics.
4598 bool emptyRef = elem.text.IsEmpty();
4599 symbol->SetRef( &m_sheetPath, emptyRef ? wxString( wxS( "#GRAPHIC" ) ) : elem.text );
4600
4601 // I am not sure value and ref should be invisible just because emptyRef is true
4602 // I have examples with this criteria fully incorrect.
4603 bool visible = !emptyRef;
4604
4605 symbol->GetField( FIELD_T::VALUE )->SetVisible( visible );
4606
4607 SCH_FIELD* field = symbol->GetField( FIELD_T::REFERENCE );
4608 field->SetVisible( visible );
4609 field->SetPosition( elem.location + m_sheetOffset );
4610 SetTextPositioning( field, elem.justification, elem.orientation );
4611
4612 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
4613
4614 if( altiumSymIt != m_altiumComponents.end() )
4615 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
4616}
4617
4618
4619void SCH_IO_ALTIUM::ParseLibDesignator( const std::map<wxString, wxString>& aProperties,
4620 std::vector<LIB_SYMBOL*>& aSymbol,
4621 std::vector<int>& aFontSizes )
4622{
4623 ASCH_DESIGNATOR elem( aProperties );
4624
4625 // Designators are shared by everyone
4626 for( LIB_SYMBOL* symbol : aSymbol )
4627 {
4628 bool emptyRef = elem.text.IsEmpty();
4629 SCH_FIELD& refField = symbol->GetReferenceField();
4630
4631 if( emptyRef )
4632 refField.SetText( wxT( "X" ) );
4633 else
4634 refField.SetText( elem.text.BeforeLast( '?' ) ); // remove the '?' at the end for KiCad-style
4635
4636 refField.SetPosition( elem.location );
4637 SetTextPositioning( &refField, elem.justification, elem.orientation );
4638
4639 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
4640 {
4641 int size = aFontSizes[elem.fontId - 1];
4642 refField.SetTextSize( { size, size } );
4643 }
4644 }
4645}
4646
4647
4648void SCH_IO_ALTIUM::ParseBusEntry( const std::map<wxString, wxString>& aProperties )
4649{
4650 ASCH_BUS_ENTRY elem( aProperties );
4651
4652 SCH_SCREEN* screen = getCurrentScreen();
4653 wxCHECK( screen, /* void */ );
4654
4655 SCH_BUS_WIRE_ENTRY* busWireEntry = new SCH_BUS_WIRE_ENTRY( elem.location + m_sheetOffset );
4656
4657 VECTOR2I vector = elem.corner - elem.location;
4658 busWireEntry->SetSize( { vector.x, vector.y } );
4659
4660 busWireEntry->SetFlags( IS_NEW );
4661 screen->Append( busWireEntry );
4662}
4663
4664
4665void SCH_IO_ALTIUM::ParseParameter( const std::map<wxString, wxString>& aProperties )
4666{
4667 ASCH_PARAMETER elem( aProperties );
4668
4669 // TODO: fill in replacements from variant, sheet and project
4670 static const std::map<wxString, wxString> variableMap = {
4671 { "COMMENT", "VALUE" },
4672 { "VALUE", "ALTIUM_VALUE" },
4673 };
4674
4675 if( elem.ownerindex <= 0 )
4676 {
4677 // This is some sheet parameter
4678 if( elem.text == "*" )
4679 return; // indicates parameter not set?
4680
4681 wxString paramName = elem.name.Upper();
4682
4683 if( paramName == "SHEETNUMBER" )
4684 {
4685 m_sheetPath.SetPageNumber( elem.text );
4686 }
4687 else if( paramName == "TITLE" )
4688 {
4689 m_currentTitleBlock->SetTitle( elem.text );
4690 }
4691 else if( paramName == "REVISION" )
4692 {
4693 m_currentTitleBlock->SetRevision( elem.text );
4694 }
4695 else if( paramName == "DATE" )
4696 {
4697 m_currentTitleBlock->SetDate( elem.text );
4698 }
4699 else if( paramName == "COMPANYNAME" )
4700 {
4701 m_currentTitleBlock->SetCompany( elem.text );
4702 }
4703 else
4704 {
4705 m_schematic->Project().GetTextVars()[ paramName ] = elem.text;
4706 }
4707 }
4708 else
4709 {
4710 const auto& libSymbolIt = m_libSymbols.find( elem.ownerindex );
4711
4712 if( libSymbolIt == m_libSymbols.end() )
4713 {
4714 // TODO: e.g. can depend on Template (RECORD=39
4715 return;
4716 }
4717
4718 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4719 SCH_FIELD* field = nullptr;
4720 wxString upperName = elem.name.Upper();
4721
4722 if( upperName == "COMMENT" )
4723 {
4724 field = symbol->GetField( FIELD_T::VALUE );
4725 }
4726 else
4727 {
4728 wxString fieldName = elem.name.Upper();
4729
4730 if( fieldName.IsEmpty() )
4731 {
4732 int disambiguate = 1;
4733
4734 while( 1 )
4735 {
4736 fieldName = wxString::Format( "ALTIUM_UNNAMED_%d", disambiguate++ );
4737
4738 if( !symbol->GetField( fieldName ) )
4739 break;
4740 }
4741 }
4742 else if( fieldName == "VALUE" )
4743 {
4744 fieldName = "ALTIUM_VALUE";
4745 }
4746
4747 field = symbol->AddField( SCH_FIELD( symbol, FIELD_T::USER, fieldName ) );
4748 }
4749
4750 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
4751 field->SetText( kicadText );
4752 field->SetPosition( elem.location + m_sheetOffset );
4753 field->SetVisible( !elem.isHidden );
4754 field->SetNameShown( elem.isShowName );
4755 SetTextPositioning( field, elem.justification, elem.orientation );
4756
4757 const auto& altiumSymIt = m_altiumComponents.find( elem.ownerindex );
4758
4759 if( altiumSymIt != m_altiumComponents.end() )
4760 AdjustFieldForSymbolOrientation( field, altiumSymIt->second );
4761 }
4762}
4763
4764
4765void SCH_IO_ALTIUM::ParseLibParameter( const std::map<wxString, wxString>& aProperties,
4766 std::vector<LIB_SYMBOL*>& aSymbol,
4767 std::vector<int>& aFontSizes )
4768{
4769 ASCH_PARAMETER elem( aProperties );
4770
4771 // Part ID 1 is the current library part.
4772 // Part ID ALTIUM_COMPONENT_NONE(-1) means all parts
4773 // If a parameter is assigned to a specific element such as a pin,
4774 // we will need to handle it here.
4775 // TODO: Handle HIDDENNETNAME property (others?)
4776 if( elem.ownerpartid != 1 && elem.ownerpartid != ALTIUM_COMPONENT_NONE )
4777 return;
4778
4779 // If ownerindex is populated, this is parameter belongs to a subelement (e.g. pin).
4780 // Ignore for now.
4781 // TODO: Update this when KiCad supports parameters for any object
4782 if( elem.ownerindex != ALTIUM_COMPONENT_NONE )
4783 return;
4784
4785 // TODO: fill in replacements from variant, sheet and project
4786 // N.B. We do not keep the Altium "VALUE" variable here because
4787 // we don't have a way to assign variables to specific symbols
4788 std::map<wxString, wxString> variableMap = {
4789 { "COMMENT", "VALUE" },
4790 };
4791
4792 for( LIB_SYMBOL* libSymbol : aSymbol )
4793 {
4794 SCH_FIELD* field = nullptr;
4795 wxString upperName = elem.name.Upper();
4796
4797 if( upperName == "COMMENT" )
4798 {
4799 field = &libSymbol->GetValueField();
4800 }
4801 else
4802 {
4803 wxString fieldNameStem = elem.name;
4804 wxString fieldName = fieldNameStem;
4805 int disambiguate = 1;
4806
4807 if( fieldName.IsEmpty() )
4808 {
4809 fieldNameStem = "ALTIUM_UNNAMED";
4810 fieldName = "ALTIUM_UNNAMED_1";
4811 disambiguate = 2;
4812 }
4813 else if( upperName == "VALUE" )
4814 {
4815 fieldNameStem = "ALTIUM_VALUE";
4816 fieldName = "ALTIUM_VALUE";
4817 }
4818
4819 // Avoid adding duplicate fields
4820 while( libSymbol->GetField( fieldName ) )
4821 fieldName = wxString::Format( "%s_%d", fieldNameStem, disambiguate++ );
4822
4823 SCH_FIELD* new_field = new SCH_FIELD( libSymbol, FIELD_T::USER, fieldName );
4824 libSymbol->AddField( new_field );
4825 field = new_field;
4826 }
4827
4828 wxString kicadText = AltiumSchSpecialStringsToKiCadVariables( elem.text, variableMap );
4829 field->SetText( kicadText );
4830
4831 field->SetTextPos( elem.location );
4832 SetTextPositioning( field, elem.justification, elem.orientation );
4833 field->SetVisible( !elem.isHidden );
4834
4835 if( elem.fontId > 0 && elem.fontId <= static_cast<int>( aFontSizes.size() ) )
4836 {
4837 int size = aFontSizes[elem.fontId - 1];
4838 field->SetTextSize( { size, size } );
4839 }
4840 else
4841 {
4842 int size = schIUScale.MilsToIU( DEFAULT_TEXT_SIZE );
4843 field->SetTextSize( { size, size } );
4844 }
4845
4846 }
4847}
4848
4849
4851 const std::map<wxString, wxString>& aProperties )
4852{
4853 ASCH_IMPLEMENTATION_LIST elem( aProperties );
4854
4855 m_altiumImplementationList.emplace( aIndex, elem.ownerindex );
4856}
4857
4858
4859void SCH_IO_ALTIUM::ParseImplementation( const std::map<wxString, wxString>& aProperties,
4860 std::vector<LIB_SYMBOL*>& aSymbol )
4861{
4862 ASCH_IMPLEMENTATION elem( aProperties );
4863
4864 if( elem.type != wxS( "PCBLIB" ) )
4865 return;
4866
4867 // For schematic files, we need to check if the model is current.
4868 if( aSymbol.size() == 0 && !elem.isCurrent )
4869 return;
4870
4871 // For IntLibs we want to use the same lib name for footprints
4872 wxString libName = m_isIntLib ? m_libName : elem.libname;
4873
4874 wxArrayString fpFilters;
4875 fpFilters.Add( wxString::Format( wxS( "*%s*" ), elem.name ) );
4876
4877 // Parse the footprint fields for the library symbol
4878 if( !aSymbol.empty() )
4879 {
4880 for( LIB_SYMBOL* symbol : aSymbol )
4881 {
4882 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
4883
4884 symbol->SetFPFilters( fpFilters );
4885 symbol->GetField( FIELD_T::FOOTPRINT )->SetText( fpLibId.Format() );
4886 }
4887
4888 return;
4889 }
4890
4891 const auto& implementationOwnerIt = m_altiumImplementationList.find( elem.ownerindex );
4892
4893 if( implementationOwnerIt == m_altiumImplementationList.end() )
4894 {
4895 m_errorMessages.emplace( wxString::Format( wxT( "Implementation's owner (%d) not found." ),
4896 elem.ownerindex ),
4898 return;
4899 }
4900
4901 const auto& libSymbolIt = m_libSymbols.find( implementationOwnerIt->second );
4902
4903 if( libSymbolIt == m_libSymbols.end() )
4904 {
4905 m_errorMessages.emplace( wxString::Format( wxT( "Footprint's owner (%d) not found." ),
4906 implementationOwnerIt->second ),
4908 return;
4909 }
4910
4911 LIB_ID fpLibId = AltiumToKiCadLibID( libName, elem.name );
4912
4913 libSymbolIt->second->SetFPFilters( fpFilters ); // TODO: not ideal as we overwrite it
4914
4915 SCH_SYMBOL* symbol = m_symbols.at( libSymbolIt->first );
4916
4917 symbol->SetFootprintFieldText( fpLibId.Format() );
4918}
4919
4920
4921
4922std::vector<LIB_SYMBOL*> SCH_IO_ALTIUM::ParseLibComponent( const std::map<wxString,
4923 wxString>& aProperties )
4924{
4925 ASCH_SYMBOL elem( aProperties );
4926
4927 std::vector<LIB_SYMBOL*> symbols;
4928
4929 symbols.reserve( elem.displaymodecount );
4930
4931 for( int i = 0; i < elem.displaymodecount; i++ )
4932 {
4933 LIB_SYMBOL* symbol = new LIB_SYMBOL( wxEmptyString );
4934
4935 if( elem.displaymodecount > 1 )
4936 symbol->SetName( wxString::Format( "%s (Altium Display %d)", elem.libreference, i + 1 ) );
4937 else
4938 symbol->SetName( elem.libreference );
4939
4940 LIB_ID libId = AltiumToKiCadLibID( getLibName(), symbol->GetName() );
4941 symbol->SetDescription( elem.componentdescription );
4942 symbol->SetLibId( libId );
4943 symbol->SetUnitCount( elem.partcount - 1, true );
4944 symbols.push_back( symbol );
4945 }
4946
4947 return symbols;
4948}
4949
4950
4953{
4955 std::vector<int> fontSizes;
4956 struct SYMBOL_PIN_FRAC
4957 {
4958 int x_frac;
4959 int y_frac;
4960 int len_frac;
4961 };
4962
4963 ParseLibHeader( aAltiumLibFile, fontSizes );
4964
4965 std::map<wxString, ALTIUM_SYMBOL_DATA> syms = aAltiumLibFile.GetLibSymbols( nullptr );
4966
4967 for( auto& [name, entry] : syms )
4968 {
4969 std::map<int, SYMBOL_PIN_FRAC> pinFracs;
4970
4971 if( entry.m_pinsFrac )
4972 {
4973 auto parse_binary_pin_frac =
4974 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
4975 {
4976 std::map<wxString, wxString> result;
4977 ALTIUM_COMPRESSED_READER cmpreader( binaryData );
4978
4979 std::pair<int, std::string*> pinFracData = cmpreader.ReadCompressedString();
4980
4981 ALTIUM_BINARY_READER binreader( *pinFracData.second );
4982 SYMBOL_PIN_FRAC pinFrac;
4983
4984 pinFrac.x_frac = binreader.ReadInt32();
4985 pinFrac.y_frac = binreader.ReadInt32();
4986 pinFrac.len_frac = binreader.ReadInt32();
4987 pinFracs.insert( { pinFracData.first, pinFrac } );
4988
4989 return result;
4990 };
4991
4992 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_pinsFrac );
4993
4994 while( reader.GetRemainingBytes() > 0 )
4995 reader.ReadProperties( parse_binary_pin_frac );
4996 }
4997
4998 ALTIUM_BINARY_PARSER reader( aAltiumLibFile, entry.m_symbol );
4999 std::vector<LIB_SYMBOL*> symbols;
5000 int pin_index = 0;
5001
5002 if( reader.GetRemainingBytes() <= 0 )
5003 THROW_IO_ERROR( "LibSymbol does not contain any data" );
5004
5005 {
5006 std::map<wxString, wxString> properties = reader.ReadProperties();
5007 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
5008 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
5009
5010 if( record != ALTIUM_SCH_RECORD::COMPONENT )
5011 THROW_IO_ERROR( "LibSymbol does not start with COMPONENT record" );
5012
5013 symbols = ParseLibComponent( properties );
5014 }
5015
5016 auto handleBinaryPinLambda =
5017 [&]( const std::string& binaryData ) -> std::map<wxString, wxString>
5018 {
5019 std::map<wxString, wxString> result;
5020
5021 ALTIUM_BINARY_READER binreader( binaryData );
5022
5023 int32_t recordId = binreader.ReadInt32();
5024
5025 if( recordId != static_cast<int32_t>( ALTIUM_SCH_RECORD::PIN ) )
5026 THROW_IO_ERROR( "Binary record missing PIN record" );
5027
5028 result["RECORD"] = wxString::Format( "%d", recordId );
5029 binreader.ReadByte(); // unknown
5030 result["OWNERPARTID"] = wxString::Format( "%d", binreader.ReadInt16() );
5031 result["OWNERPARTDISPLAYMODE"] = wxString::Format( "%d", binreader.ReadByte() );
5032 result["SYMBOL_INNEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
5033 result["SYMBOL_OUTEREDGE"] = wxString::Format( "%d", binreader.ReadByte() );
5034 result["SYMBOL_INNER"] = wxString::Format( "%d", binreader.ReadByte() );
5035 result["SYMBOL_OUTER"] = wxString::Format( "%d", binreader.ReadByte() );
5036 result["TEXT"] = binreader.ReadShortPascalString();
5037 binreader.ReadByte(); // unknown
5038 result["ELECTRICAL"] = wxString::Format( "%d", binreader.ReadByte() );
5039 result["PINCONGLOMERATE"] = wxString::Format( "%d", binreader.ReadByte() );
5040 result["PINLENGTH"] = wxString::Format( "%d", binreader.ReadInt16() );
5041 result["LOCATION.X"] = wxString::Format( "%d", binreader.ReadInt16() );
5042 result["LOCATION.Y"] = wxString::Format( "%d", binreader.ReadInt16() );
5043 result["COLOR"] = wxString::Format( "%d", binreader.ReadInt32() );
5044 result["NAME"] = binreader.ReadShortPascalString();
5045 result["DESIGNATOR"] = binreader.ReadShortPascalString();
5046 result["SWAPIDGROUP"] = binreader.ReadShortPascalString();
5047
5048 if( auto it = pinFracs.find( pin_index ); it != pinFracs.end() )
5049 {
5050 result["LOCATION.X_FRAC"] = wxString::Format( "%d", it->second.x_frac );
5051 result["LOCATION.Y_FRAC"] = wxString::Format( "%d", it->second.y_frac );
5052 result["PINLENGTH_FRAC"] = wxString::Format( "%d", it->second.len_frac );
5053 }
5054
5055 std::string partSeq = binreader.ReadShortPascalString(); // This is 'part|&|seq'
5056 std::vector<std::string> partSeqSplit = split( partSeq, "|" );
5057
5058 if( partSeqSplit.size() == 3 )
5059 {
5060 result["PART"] = partSeqSplit[0];
5061 result["SEQ"] = partSeqSplit[2];
5062 }
5063
5064 return result;
5065 };
5066
5067 while( reader.GetRemainingBytes() > 0 )
5068 {
5069 std::map<wxString, wxString> properties = reader.ReadProperties( handleBinaryPinLambda );
5070
5071 if( properties.empty() )
5072 continue;
5073
5074 int recordId = ALTIUM_PROPS_UTILS::ReadInt( properties, "RECORD", 0 );
5075 ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
5076
5077 switch( record )
5078 {
5080 ParsePin( properties, symbols );
5081 pin_index++;
5082 break;
5083
5084 case ALTIUM_SCH_RECORD::LABEL: ParseLabel( properties, symbols, fontSizes ); break;
5085 case ALTIUM_SCH_RECORD::BEZIER: ParseBezier( properties, symbols ); break;
5086 case ALTIUM_SCH_RECORD::POLYLINE: ParsePolyline( properties, symbols ); break;
5087 case ALTIUM_SCH_RECORD::POLYGON: ParsePolygon( properties, symbols ); break;
5088 case ALTIUM_SCH_RECORD::ELLIPSE: ParseEllipse( properties, symbols ); break;
5089 case ALTIUM_SCH_RECORD::PIECHART: ParsePieChart( properties, symbols ); break;
5090 case ALTIUM_SCH_RECORD::ROUND_RECTANGLE: ParseRoundRectangle( properties, symbols ); break;
5091 case ALTIUM_SCH_RECORD::ELLIPTICAL_ARC: ParseEllipticalArc( properties, symbols ); break;
5092 case ALTIUM_SCH_RECORD::ARC: ParseArc( properties, symbols ); break;
5093 case ALTIUM_SCH_RECORD::LINE: ParseLine( properties, symbols ); break;
5094 case ALTIUM_SCH_RECORD::RECTANGLE: ParseRectangle( properties, symbols ); break;
5095 case ALTIUM_SCH_RECORD::DESIGNATOR: ParseLibDesignator( properties, symbols, fontSizes ); break;
5096 case ALTIUM_SCH_RECORD::PARAMETER: ParseLibParameter( properties, symbols, fontSizes ); break;
5097 case ALTIUM_SCH_RECORD::TEXT_FRAME: ParseTextFrame( properties, symbols, fontSizes ); break;
5098 case ALTIUM_SCH_RECORD::IMPLEMENTATION: ParseImplementation( properties, symbols ); break;
5099
5101 break;
5102
5105 break;
5106
5108 // TODO: add support for these. They are just drawn symbols, so we can probably hardcode
5109 break;
5110
5112 // TODO: Handle images once libedit supports them
5113 break;
5114
5116 // Nothing for now. TODO: Figure out how implementation lists are generated in libs
5117 break;
5118
5119 default:
5120 m_errorMessages.emplace( wxString::Format( _( "Unknown or unexpected record id %d found in %s." ),
5121 recordId,
5122 symbols[0]->GetName() ),
5124 break;
5125 }
5126 }
5127
5128 if( reader.HasParsingError() )
5129 THROW_IO_ERROR( "stream was not parsed correctly!" );
5130
5131 if( reader.GetRemainingBytes() != 0 )
5132 THROW_IO_ERROR( "stream is not fully parsed" );
5133
5134 for( size_t ii = 0; ii < symbols.size(); ii++ )
5135 {
5136 LIB_SYMBOL* symbol = symbols[ii];
5137 symbol->FixupDrawItems();
5138 fixupSymbolPinNameNumbers( symbol );
5139
5140 SCH_FIELD& valField = symbol->GetValueField();
5141
5142 if( valField.GetText().IsEmpty() )
5143 valField.SetText( name );
5144
5145 // Set the symbol name to match the cache key. The directory name (used as cache
5146 // key) may differ from the Altium library reference when the original name
5147 // contains characters invalid for directory names (like '/').
5148 wxString cacheName;
5149
5150 if( symbols.size() == 1 )
5151 cacheName = name;
5152 else
5153 cacheName = wxString::Format( "%s (Altium Display %zd)", name, ii + 1 );
5154
5155 symbol->SetName( cacheName );
5156 ret[cacheName] = symbol;
5157 }
5158 }
5159
5160 return ret;
5161}
5162
5163
5164long long SCH_IO_ALTIUM::getLibraryTimestamp( const wxString& aLibraryPath ) const
5165{
5166 wxFileName fn( aLibraryPath );
5167
5168 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
5169 return fn.GetModificationTime().GetValue().GetValue();
5170 else
5171 return 0;
5172}
5173
5174
5175void SCH_IO_ALTIUM::ensureLoadedLibrary( const wxString& aLibraryPath,
5176 const std::map<std::string, UTF8>* aProperties )
5177{
5178 // Suppress font substitution warnings (RAII - automatically restored on scope exit)
5179 FONTCONFIG_REPORTER_SCOPE fontconfigScope( nullptr );
5180
5181 if( m_libCache.count( aLibraryPath ) )
5182 {
5183 wxCHECK( m_timestamps.count( aLibraryPath ), /*void*/ );
5184
5185 if( m_timestamps.at( aLibraryPath ) == getLibraryTimestamp( aLibraryPath ) )
5186 return;
5187 }
5188
5189 std::vector<std::unique_ptr<ALTIUM_COMPOUND_FILE>> compoundFiles;
5190
5191 wxFileName fileName( aLibraryPath );
5192 m_libName = fileName.GetName();
5193
5194 try
5195 {
5196 if( aLibraryPath.Lower().EndsWith( wxS( ".schlib" ) ) )
5197 {
5198 m_isIntLib = false;
5199
5200 compoundFiles.push_back( std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath ) );
5201 }
5202 else if( aLibraryPath.Lower().EndsWith( wxS( ".intlib" ) ) )
5203 {
5204 m_isIntLib = true;
5205
5206 std::unique_ptr<ALTIUM_COMPOUND_FILE> intCom = std::make_unique<ALTIUM_COMPOUND_FILE>( aLibraryPath );
5207
5208 std::map<wxString, const CFB::COMPOUND_FILE_ENTRY*> schLibFiles = intCom->EnumDir( L"SchLib" );
5209
5210 for( const auto& [schLibName, cfe] : schLibFiles )
5211 {
5212 std::unique_ptr<ALTIUM_COMPOUND_FILE> decodedStream = std::make_unique<ALTIUM_COMPOUND_FILE>();
5213
5214 if( intCom->DecodeIntLibStream( *cfe, decodedStream.get() ) )
5215 compoundFiles.emplace_back( std::move( decodedStream ) );
5216 }
5217 }
5218
5219 CASE_INSENSITIVE_MAP<LIB_SYMBOL*>& cacheMapRef = m_libCache[aLibraryPath];
5220
5221 for( const std::unique_ptr<ALTIUM_COMPOUND_FILE>& altiumSchFilePtr : compoundFiles )
5222 {
5223 CASE_INSENSITIVE_MAP<LIB_SYMBOL*> parsed = ParseLibFile( *altiumSchFilePtr );
5224 cacheMapRef.insert( parsed.begin(), parsed.end() );
5225 }
5226
5227 m_timestamps[aLibraryPath] = getLibraryTimestamp( aLibraryPath );
5228 }
5229 catch( const CFB::CFBException& exception )
5230 {
5231 THROW_IO_ERROR( exception.what() );
5232 }
5233 catch( const std::exception& exc )
5234 {
5235 THROW_IO_ERROR( wxString::Format( _( "Error parsing Altium library: %s" ), exc.what() ) );
5236 }
5237}
5238
5239
5241 std::vector<int>& aFontSizes )
5242{
5243 const CFB::COMPOUND_FILE_ENTRY* file = aAltiumSchFile.FindStream( { "FileHeader" } );
5244
5245 if( file == nullptr )
5246 THROW_IO_ERROR( "FileHeader not found" );
5247
5248 ALTIUM_BINARY_PARSER reader( aAltiumSchFile, file );
5249
5250 if( reader.GetRemainingBytes() <= 0 )
5251 THROW_IO_ERROR( "FileHeader does not contain any data" );
5252
5253 std::map<wxString, wxString> properties = reader.ReadProperties();
5254
5255 wxString libtype = ALTIUM_PROPS_UTILS::ReadString( properties, "HEADER", "" );
5256
5257 if( libtype.CmpNoCase( "Protel for Windows - Schematic Library Editor Binary File Version 5.0" ) )
5258 THROW_IO_ERROR( _( "Expected Altium Schematic Library file version 5.0" ) );
5259
5260 for( auto& [key, value] : properties )
5261 {
5262 wxString upperKey = key.Upper();
5263 wxString remaining;
5264
5265 if( upperKey.StartsWith( "SIZE", &remaining ) )
5266 {
5267 if( !remaining.empty() )
5268 {
5269 int ind = wxAtoi( remaining );
5270
5271 if( static_cast<int>( aFontSizes.size() ) < ind )
5272 aFontSizes.resize( ind );
5273
5274 // Altium stores in pt. 1 pt = 1/72 inch. 1 mil = 1/1000 inch.
5275 int scaled = schIUScale.MilsToIU( wxAtoi( value ) * 72.0 / 10.0 );
5276 aFontSizes[ind - 1] = scaled;
5277 }
5278 }
5279 }
5280}
5281
5282
5283void SCH_IO_ALTIUM::doEnumerateSymbolLib( const wxString& aLibraryPath,
5284 const std::map<std::string, UTF8>* aProperties,
5285 std::function<void(const wxString&, LIB_SYMBOL*)> aInserter )
5286{
5287 ensureLoadedLibrary( aLibraryPath, aProperties );
5288
5289 bool powerSymbolsOnly = ( aProperties &&
5290 aProperties->contains( SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly ) );
5291
5292 auto it = m_libCache.find( aLibraryPath );
5293
5294 if( it != m_libCache.end() )
5295 {
5296 for( auto& [libnameStr, libSymbol] : it->second )
5297 {
5298 if( powerSymbolsOnly && !libSymbol->IsPower() )
5299 continue;
5300
5301 aInserter( libnameStr, libSymbol );
5302 }
5303 }
5304}
5305
5306
5307void SCH_IO_ALTIUM::EnumerateSymbolLib( wxArrayString& aSymbolNameList, const wxString& aLibraryPath,
5308 const std::map<std::string, UTF8>* aProperties )
5309{
5310 doEnumerateSymbolLib( aLibraryPath, aProperties,
5311 [&]( const wxString& aStr, LIB_SYMBOL* )
5312 {
5313 aSymbolNameList.Add( aStr );
5314 } );
5315}
5316
5317
5318void SCH_IO_ALTIUM::EnumerateSymbolLib( std::vector<LIB_SYMBOL*>& aSymbolList,
5319 const wxString& aLibraryPath,
5320 const std::map<std::string, UTF8>* aProperties )
5321{
5322 doEnumerateSymbolLib( aLibraryPath, aProperties,
5323 [&]( const wxString&, LIB_SYMBOL* aSymbol )
5324 {
5325 aSymbolList.emplace_back( aSymbol );
5326 } );
5327}
5328
5329
5330LIB_SYMBOL* SCH_IO_ALTIUM::LoadSymbol( const wxString& aLibraryPath, const wxString& aAliasName,
5331 const std::map<std::string, UTF8>* aProperties )
5332{
5333 ensureLoadedLibrary( aLibraryPath, aProperties );
5334
5335 auto it = m_libCache.find( aLibraryPath );
5336
5337 if( it != m_libCache.end() )
5338 {
5339 auto it2 = it->second.find( aAliasName );
5340
5341 if( it2 != it->second.end() )
5342 return it2->second;
5343 }
5344
5345 return nullptr;
5346}
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:99
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:148
void SetModified()
Definition eda_item.cpp:110
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:111
void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:258
void SetCenter(const VECTOR2I &aCenter)
FILL_T GetFillMode() const
Definition eda_shape.h:142
void SetLineStyle(const LINE_STYLE aStyle)
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:169
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:154
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:178
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:174
COLOR4D GetFillColor() const
Definition eda_shape.h:153
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:220
void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:255
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:80
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:147
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:546
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:98
void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:590
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:431
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
void FlipHJustify()
Definition eda_text.h:208
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:349
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:203
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:284
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:313
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:321
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:423
EE_TYPE Overlapping(const BOX2I &aRect) const
Definition sch_rtree.h:246
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:241
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:49
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 ...
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 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:88
PROJECT & Project() const
Return a reference to the project this schematic is part of.
Definition schematic.h:103
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition schematic.h:172
void SetTopLevelSheets(const std::vector< SCH_SHEET * > &aSheets)
SCH_SHEET & Root() const
Definition schematic.h:132
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:116
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
Definition sch_field.h:207
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)
bool IsComponentPartVisible(const ASCH_OWNER_INTERFACE &aElem) const
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 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:868
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:867
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:46
FILL_T
Definition eda_shape.h:56
@ FILLED_WITH_COLOR
Definition eda_shape.h:60
@ NO_FILL
Definition eda_shape.h:57
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:59
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:58
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:466
@ LAYER_WIRE
Definition layer_ids.h:452
@ LAYER_NOTES
Definition layer_ids.h:467
@ LAYER_BUS
Definition layer_ids.h:453
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:34
const std::vector< uint8_t > COMPOUND_FILE_HEADER
Definition io_utils.cpp:31
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:59
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:167
@ LIB_SYMBOL_T
Definition typeinfo.h:152
@ SCH_SYMBOL_T
Definition typeinfo.h:176
@ SCH_LABEL_T
Definition typeinfo.h:171
@ SCH_SHEET_T
Definition typeinfo.h:179
@ SCH_HIER_LABEL_T
Definition typeinfo.h:173
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
Definition of file extensions used in Kicad.