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