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