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