KiCad PCB EDA Suite
sch_altium_plugin.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 <thomas.pointhuber@gmx.at>
5  * Copyright (C) 2021 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"
31 
32 #include <schematic.h>
33 
34 #include <lib_arc.h>
35 #include <lib_bezier.h>
36 #include <lib_circle.h>
37 #include <lib_id.h>
38 #include <lib_item.h>
39 #include <lib_pin.h>
40 #include <lib_polyline.h>
41 #include <lib_rectangle.h>
42 #include <lib_text.h>
43 
44 #include <bus_alias.h>
45 #include <sch_bitmap.h>
46 #include <sch_bus_entry.h>
47 #include <sch_symbol.h>
48 #include <sch_junction.h>
49 #include <sch_line.h>
50 #include <sch_no_connect.h>
51 #include <sch_screen.h>
52 #include <sch_sheet.h>
53 #include <sch_sheet_pin.h>
54 #include <sch_text.h>
55 
56 #include <bezier_curves.h>
57 #include <compoundfilereader.h>
58 #include <kicad_string.h>
59 #include <sch_edit_frame.h>
61 #include <wx/mstream.h>
62 #include <wx/log.h>
63 #include <wx/zstream.h>
64 #include <wx/wfstream.h>
65 
66 
67 const wxPoint GetRelativePosition( const wxPoint& aPosition, const SCH_SYMBOL* aSymbol )
68 {
69  TRANSFORM t = aSymbol->GetTransform().InverseTransform();
70  return t.TransformCoordinate( aPosition - aSymbol->GetPosition() );
71 }
72 
73 
75 {
76  int red = color & 0x0000FF;
77  int green = ( color & 0x00FF00 ) >> 8;
78  int blue = ( color & 0xFF0000 ) >> 16;
79 
80  return COLOR4D().FromCSSRGBA( red, green, blue, 1.0 );
81 }
82 
84 {
85  m_rootSheet = nullptr;
86  m_currentSheet = nullptr;
87  m_schematic = nullptr;
88 
90 }
91 
92 
94 {
95 }
96 
97 
98 const wxString SCH_ALTIUM_PLUGIN::GetName() const
99 {
100  return "Altium";
101 }
102 
103 
105 {
106  return "SchDoc";
107 }
108 
109 
111 {
112  return "SchLib";
113 }
114 
115 
117 {
118  return 0;
119 }
120 
121 
122 bool SCH_ALTIUM_PLUGIN::CheckHeader( const wxString& aFileName )
123 {
124  // TODO
125 
126  return true;
127 }
128 
129 
131 {
132  if( m_libName.IsEmpty() )
133  {
134  // Try to come up with a meaningful name
136 
137  if( m_libName.IsEmpty() )
138  {
139  wxFileName fn( m_rootSheet->GetFileName() );
140  m_libName = fn.GetName();
141  }
142 
143  if( m_libName.IsEmpty() )
144  m_libName = "noname";
145 
146  m_libName += "-altium-import";
148  }
149 
150  return m_libName;
151 }
152 
153 
155 {
157 
158  return fn;
159 }
160 
161 
162 SCH_SHEET* SCH_ALTIUM_PLUGIN::Load( const wxString& aFileName, SCHEMATIC* aSchematic,
163  SCH_SHEET* aAppendToMe, const PROPERTIES* aProperties )
164 {
165  wxASSERT( !aFileName || aSchematic != NULL );
166 
167  wxFileName fileName( aFileName );
168  fileName.SetExt( KiCadSchematicFileExtension );
169  m_schematic = aSchematic;
170 
171  // Delete on exception, if I own m_rootSheet, according to aAppendToMe
172  std::unique_ptr<SCH_SHEET> deleter( aAppendToMe ? nullptr : m_rootSheet );
173 
174  if( aAppendToMe )
175  {
176  wxCHECK_MSG( aSchematic->IsValid(), nullptr, "Can't append to a schematic with no root!" );
177  m_rootSheet = &aSchematic->Root();
178  }
179  else
180  {
181  m_rootSheet = new SCH_SHEET( aSchematic );
182  m_rootSheet->SetFileName( fileName.GetFullPath() );
183  }
184 
185  if( !m_rootSheet->GetScreen() )
186  {
187  SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
188  screen->SetFileName( aFileName );
189  m_rootSheet->SetScreen( screen );
190  }
191 
192  SYMBOL_LIB_TABLE* libTable = m_schematic->Prj().SchSymbolLibTable();
193 
194  wxCHECK_MSG( libTable, NULL, "Could not load symbol lib table." );
195 
196  m_pi.set( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
197 
200  if( !libTable->HasLibrary( getLibName() ) )
201  {
202  // Create a new empty symbol library.
203  m_pi->CreateSymbolLib( getLibFileName().GetFullPath() );
204  wxString libTableUri = "${KIPRJMOD}/" + getLibFileName().GetFullName();
205 
206  // Add the new library to the project symbol library table.
207  libTable->InsertRow( new SYMBOL_LIB_TABLE_ROW( getLibName(), libTableUri,
208  wxString( "KiCad" ) ) );
209 
210  // Save project symbol library table.
211  wxFileName fn( m_schematic->Prj().GetProjectPath(),
213 
214  // So output formatter goes out of scope and closes the file before reloading.
215  {
216  FILE_OUTPUTFORMATTER formatter( fn.GetFullPath() );
217  libTable->Format( &formatter, 0 );
218  }
219 
220  // Reload the symbol library table.
222  m_schematic->Prj().SchSymbolLibTable();
223  }
224 
226  ParseAltiumSch( aFileName );
227 
228  m_pi->SaveLibrary( getLibFileName().GetFullPath() );
229 
230  SCH_SCREENS allSheets( m_rootSheet );
231  allSheets.UpdateSymbolLinks(); // Update all symbol library links for all sheets.
232 
233  return m_rootSheet;
234 }
235 
236 
237 /*wxString SCH_EAGLE_PLUGIN::fixSymbolName( const wxString& aName )
238 {
239  wxString ret = LIB_ID::FixIllegalChars( aName, LIB_ID::ID_SCH );
240 
241  return ret;
242 }*/
243 
244 void SCH_ALTIUM_PLUGIN::ParseAltiumSch( const wxString& aFileName )
245 {
246  // Open file
247  FILE* fp = wxFopen( aFileName, "rb" );
248 
249  if( fp == nullptr )
250  {
251  m_reporter->Report( wxString::Format( _( "Cannot open file '%s'." ), aFileName ),
253  return;
254  }
255 
256  fseek( fp, 0, SEEK_END );
257  long len = ftell( fp );
258 
259  if( len < 0 )
260  {
261  fclose( fp );
262  THROW_IO_ERROR( "Reading error, cannot determine length of file" );
263  }
264 
265  std::unique_ptr<unsigned char[]> buffer( new unsigned char[len] );
266  fseek( fp, 0, SEEK_SET );
267 
268  size_t bytesRead = fread( buffer.get(), sizeof( unsigned char ), len, fp );
269  fclose( fp );
270 
271  if( static_cast<size_t>( len ) != bytesRead )
272  THROW_IO_ERROR( "Reading error" );
273 
274  try
275  {
276  CFB::CompoundFileReader reader( buffer.get(), bytesRead );
277  ParseStorage( reader ); // we need this before parsing the FileHeader
278  ParseFileHeader( reader );
279  }
280  catch( CFB::CFBException& exception )
281  {
282  THROW_IO_ERROR( exception.what() );
283  }
284 }
285 
286 
287 void SCH_ALTIUM_PLUGIN::ParseStorage( const CFB::CompoundFileReader& aReader )
288 {
289  const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, "Storage" );
290 
291  if( file == nullptr )
292  return;
293 
294  ALTIUM_PARSER reader( aReader, file );
295 
296  std::map<wxString, wxString> properties = reader.ReadProperties();
297  wxString header = ALTIUM_PARSER::PropertiesReadString( properties, "HEADER", "" );
298  int weight = ALTIUM_PARSER::PropertiesReadInt( properties, "WEIGHT", 0 );
299 
300  if( weight < 0 )
301  THROW_IO_ERROR( "Storage weight is negative!" );
302 
303  for( int i = 0; i < weight; i++ )
304  {
305  m_altiumStorage.emplace_back( reader );
306  }
307 
308  if( reader.HasParsingError() )
309  THROW_IO_ERROR( "stream was not parsed correctly!" );
310 
311  // TODO pointhi: is it possible to have multiple headers in one Storage file? Otherwise
312  // throw IO Error.
313  if( reader.GetRemainingBytes() != 0 )
314  wxLogError(
315  wxString::Format( "Storage file was not fully parsed as %d bytes are remaining.",
316  reader.GetRemainingBytes() ) );
317 }
318 
319 
320 void SCH_ALTIUM_PLUGIN::ParseFileHeader( const CFB::CompoundFileReader& aReader )
321 {
322  const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, "FileHeader" );
323 
324  if( file == nullptr )
325  THROW_IO_ERROR( "FileHeader not found" );
326 
327  ALTIUM_PARSER reader( aReader, file );
328 
329  if( reader.GetRemainingBytes() <= 0 )
330  {
331  THROW_IO_ERROR( "FileHeader does not contain any data" );
332  }
333  else
334  {
335  std::map<wxString, wxString> properties = reader.ReadProperties();
336 
337  int recordId = ALTIUM_PARSER::PropertiesReadInt( properties, "RECORD", 0 );
338  ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
339 
340  if( record != ALTIUM_SCH_RECORD::HEADER )
341  THROW_IO_ERROR( "Header expected" );
342  }
343 
344  // Prepare some local variables
345  wxASSERT( m_altiumPortsCurrentSheet.empty() );
346  wxASSERT( !m_currentTitleBlock );
347 
348  m_currentTitleBlock = std::make_unique<TITLE_BLOCK>();
349 
350  // index is required required to resolve OWNERINDEX
351  for( int index = 0; reader.GetRemainingBytes() > 0; index++ )
352  {
353  std::map<wxString, wxString> properties = reader.ReadProperties();
354 
355  int recordId = ALTIUM_PARSER::PropertiesReadInt( properties, "RECORD", 0 );
356  ALTIUM_SCH_RECORD record = static_cast<ALTIUM_SCH_RECORD>( recordId );
357 
358  // see: https://github.com/vadmium/python-altium/blob/master/format.md
359  switch( record )
360  {
362  THROW_IO_ERROR( "Header already parsed" );
364  ParseComponent( index, properties );
365  break;
367  ParsePin( properties );
368  break;
370  break;
372  ParseLabel( properties );
373  break;
375  ParseBezier( properties );
376  break;
378  ParsePolyline( properties );
379  break;
381  ParsePolygon( properties );
382  break;
384  break;
386  break;
388  ParseRoundRectangle( properties );
389  break;
391  break;
393  ParseArc( properties );
394  break;
396  ParseLine( properties );
397  break;
399  ParseRectangle( properties );
400  break;
402  ParseSheetSymbol( index, properties );
403  break;
405  ParseSheetEntry( properties );
406  break;
408  ParsePowerPort( properties );
409  break;
411  // Ports are parsed after the sheet was parsed
412  // This is required because we need all electrical connection points before placing.
413  m_altiumPortsCurrentSheet.emplace_back( properties );
414  break;
416  ParseNoERC( properties );
417  break;
419  ParseNetLabel( properties );
420  break;
422  ParseBus( properties );
423  break;
425  ParseWire( properties );
426  break;
428  break;
430  ParseJunction( properties );
431  break;
432  case ALTIUM_SCH_RECORD::IMAGE: ParseImage( properties ); break;
434  ParseSheet( properties );
435  break;
437  ParseSheetName( properties );
438  break;
440  ParseFileName( properties );
441  break;
443  ParseDesignator( properties );
444  break;
446  ParseBusEntry( properties );
447  break;
449  break;
451  ParseParameter( properties );
452  break;
454  break;
456  break;
458  break;
460  break;
462  break;
464  break;
466  break;
468  break;
470  break;
472  break;
474  break;
476  break;
477  default:
478  m_reporter->Report( wxString::Format( _( "Unknown Record id: %d." ), recordId ),
480  break;
481  }
482  }
483 
484  if( reader.HasParsingError() )
485  THROW_IO_ERROR( "stream was not parsed correctly!" );
486 
487  if( reader.GetRemainingBytes() != 0 )
488  THROW_IO_ERROR( "stream is not fully parsed" );
489 
490  // assign LIB_SYMBOL -> COMPONENT
491  for( auto component : m_components )
492  {
493  auto ksymbol = m_symbols.find( component.first );
494 
495  if( ksymbol == m_symbols.end() )
496  THROW_IO_ERROR( "every component should have a symbol attached" );
497 
498  m_pi->SaveSymbol( getLibFileName().GetFullPath(), new LIB_SYMBOL( *( ksymbol->second ) ),
499  m_properties.get() );
500 
501  component.second->SetLibSymbol( ksymbol->second );
502  }
503 
504  // Handle title blocks
506  m_currentTitleBlock.reset();
507 
508  // Handle Ports
509  for( const ASCH_PORT& port : m_altiumPortsCurrentSheet )
510  ParsePort( port );
511 
513 
514  m_components.clear();
515  m_symbols.clear();
516 
517  // Otherwise we cannot save the imported sheet?
519 }
520 
521 
522 bool SCH_ALTIUM_PLUGIN::IsComponentPartVisible( int aOwnerindex, int aOwnerpartdisplaymode ) const
523 {
524  const auto& component = m_altiumComponents.find( aOwnerindex );
525 
526  if( component == m_altiumComponents.end() )
527  return false;
528 
529  return component->second.displaymode == aOwnerpartdisplaymode;
530 }
531 
532 
533 const ASCH_STORAGE_FILE* SCH_ALTIUM_PLUGIN::GetFileFromStorage( const wxString& aFilename ) const
534 {
535  const ASCH_STORAGE_FILE* nonExactMatch = nullptr;
536 
537  for( const ASCH_STORAGE_FILE& file : m_altiumStorage )
538  {
539  if( file.filename.IsSameAs( aFilename ) )
540  {
541  return &file;
542  }
543 
544  if( file.filename.EndsWith( aFilename ) )
545  {
546  nonExactMatch = &file;
547  }
548  }
549 
550  return nonExactMatch;
551 }
552 
553 
555  const std::map<wxString, wxString>& aProperties )
556 {
557  auto pair = m_altiumComponents.insert( { aIndex, ASCH_SYMBOL( aProperties ) } );
558  const ASCH_SYMBOL& elem = pair.first->second;
559 
560  // TODO: this is a hack until we correctly apply all transformations to every element
561  wxString name = wxString::Format( "%d%s_%s",
562  elem.orientation,
563  elem.isMirrored ? "_mirrored" : "",
564  elem.libreference );
566 
567  LIB_SYMBOL* ksymbol = new LIB_SYMBOL( wxEmptyString );
568  ksymbol->SetName( name );
569  ksymbol->SetDescription( elem.componentdescription );
570  ksymbol->SetLibId( libId );
571  m_symbols.insert( { aIndex, ksymbol } );
572 
573  // each component has its own symbol for now
574  SCH_SYMBOL* symbol = new SCH_SYMBOL();
575 
576  symbol->SetPosition( elem.location + m_sheetOffset );
577  //component->SetOrientation( elem.orientation ); // TODO: keep it simple for now, and only set position
578  symbol->SetLibId( libId );
579  //component->SetLibSymbol( ksymbol ); // this has to be done after parsing the LIB_SYMBOL!
580 
581  symbol->SetUnit( elem.currentpartid );
582 
583  m_currentSheet->GetScreen()->Append( symbol );
584 
585  m_components.insert( { aIndex, symbol } );
586 }
587 
588 
589 void SCH_ALTIUM_PLUGIN::ParsePin( const std::map<wxString, wxString>& aProperties )
590 {
591  ASCH_PIN elem( aProperties );
592 
593  const auto& symbol = m_symbols.find( elem.ownerindex );
594 
595  if( symbol == m_symbols.end() )
596  {
597  // TODO: e.g. can depend on Template (RECORD=39
598  m_reporter->Report( wxString::Format( _( "Pin has non-existent ownerindex %d." ),
599  elem.ownerindex ),
601  return;
602  }
603 
605  return;
606 
607  const auto& component = m_components.at( symbol->first );
608 
609  LIB_PIN* pin = new LIB_PIN( symbol->second );
610  symbol->second->AddDrawItem( pin );
611 
612  pin->SetUnit( elem.ownerpartid );
613 
614  pin->SetName( elem.name );
615  pin->SetNumber( elem.designator );
616  pin->SetLength( elem.pinlength );
617 
618  wxPoint pinLocation = elem.location; // the location given is not the connection point!
619 
620  switch( elem.orientation )
621  {
623  pin->SetOrientation( DrawPinOrient::PIN_LEFT );
624  pinLocation.x += elem.pinlength;
625  break;
627  pin->SetOrientation( DrawPinOrient::PIN_DOWN );
628  pinLocation.y -= elem.pinlength;
629  break;
631  pin->SetOrientation( DrawPinOrient::PIN_RIGHT );
632  pinLocation.x -= elem.pinlength;
633  break;
635  pin->SetOrientation( DrawPinOrient::PIN_UP );
636  pinLocation.y += elem.pinlength;
637  break;
638  default:
639  m_reporter->Report( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
640  break;
641  }
642 
643  // TODO: position can be sometimes off a little bit!
644  pin->SetPosition( GetRelativePosition( pinLocation + m_sheetOffset, component ) );
645  // TODO: the following fix is even worse for now?
646  // pin->SetPosition( GetRelativePosition( elem.kicadLocation, component ) );
647 
648  switch( elem.electrical )
649  {
651  pin->SetType( ELECTRICAL_PINTYPE::PT_INPUT );
652  break;
654  pin->SetType( ELECTRICAL_PINTYPE::PT_BIDI );
655  break;
658  break;
661  break;
664  break;
667  break;
670  break;
673  break;
675  default:
677  m_reporter->Report( _( "Pin has unexpected electrical type." ), RPT_SEVERITY_WARNING );
678  break;
679  }
680 
682  m_reporter->Report( _( "Pin has unexpected outer edge type." ), RPT_SEVERITY_WARNING );
683 
685  m_reporter->Report( _( "Pin has unexpected inner edge type." ), RPT_SEVERITY_WARNING );
686 
688  {
689  switch( elem.symbolInnerEdge )
690  {
693  break;
694  default:
695  pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
696  break;
697  }
698  }
700  {
701  switch( elem.symbolInnerEdge )
702  {
704  pin->SetShape( GRAPHIC_PINSHAPE::CLOCK_LOW );
705  break;
706  default:
707  pin->SetShape( GRAPHIC_PINSHAPE::INPUT_LOW );
708  break;
709  }
710  }
712  {
713  pin->SetShape( GRAPHIC_PINSHAPE::OUTPUT_LOW );
714  }
715  else
716  {
717  switch( elem.symbolInnerEdge )
718  {
720  pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
721  break;
722  default:
723  pin->SetShape( GRAPHIC_PINSHAPE::LINE ); // nothing to do
724  break;
725  }
726  }
727 }
728 
729 
731 {
732  switch( justification )
733  {
734  default:
740  break;
745  break;
750  break;
751  }
752 
753  switch( justification )
754  {
755  default:
761  break;
766  break;
771  break;
772  }
773 }
774 
775 
776 void SCH_ALTIUM_PLUGIN::ParseLabel( const std::map<wxString, wxString>& aProperties )
777 {
778  ASCH_LABEL elem( aProperties );
779 
780  // TODO: text variable support
781  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
782  {
783  SCH_TEXT* text = new SCH_TEXT( elem.location + m_sheetOffset, elem.text );
784 
786 
787  size_t fontId = static_cast<int>( elem.fontId );
788 
789  if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
790  {
791  const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
792  text->SetItalic( font.italic );
793  text->SetBold( font.bold );
794  text->SetTextSize( { font.size / 2, font.size / 2 } );
795  }
796 
797  text->SetFlags( IS_NEW );
799  }
800  else
801  {
802  const auto& symbol = m_symbols.find( elem.ownerindex );
803 
804  if( symbol == m_symbols.end() )
805  {
806  // TODO: e.g. can depend on Template (RECORD=39
807  m_reporter->Report( wxString::Format( _( "Label has non-existent ownerindex %d." ),
808  elem.ownerindex ),
810  return;
811  }
812 
813  const auto& component = m_components.at( symbol->first );
814 
815  LIB_TEXT* text = new LIB_TEXT( symbol->second );
816  symbol->second->AddDrawItem( text );
817 
818  text->SetUnit( elem.ownerpartid );
819 
820  text->SetPosition( GetRelativePosition( elem.location + m_sheetOffset, component ) );
821  text->SetText( elem.text );
823 
824  size_t fontId = static_cast<int>( elem.fontId );
825 
826  if( m_altiumSheet && fontId > 0 && fontId <= m_altiumSheet->fonts.size() )
827  {
828  const ASCH_SHEET_FONT& font = m_altiumSheet->fonts.at( fontId - 1 );
829  text->SetItalic( font.italic );
830  text->SetBold( font.bold );
831  text->SetTextSize( { font.size / 2, font.size / 2 } );
832  }
833  }
834 }
835 
836 
837 void SCH_ALTIUM_PLUGIN::ParseBezier( const std::map<wxString, wxString>& aProperties )
838 {
839  ASCH_BEZIER elem( aProperties );
840 
841  if( elem.points.size() < 2 )
842  {
843  m_reporter->Report( wxString::Format( _( "Bezier has %d control points. At least 2 are "
844  "expected." ),
845  static_cast<int>( elem.points.size() ) ),
847  return;
848  }
849 
850  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
851  {
852  for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
853  {
854  if( i + 2 == elem.points.size() )
855  {
856  // special case: single line
857  SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
859 
860  line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
861  line->SetLineWidth( elem.lineWidth );
863 
864  line->SetFlags( IS_NEW );
865  m_currentSheet->GetScreen()->Append( line );
866  }
867  else
868  {
869  // simulate bezier using line segments
870  std::vector<wxPoint> bezierPoints;
871  std::vector<wxPoint> polyPoints;
872  for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
873  {
874  bezierPoints.push_back( elem.points.at( j ) + m_sheetOffset );
875  }
876 
877  BEZIER_POLY converter( bezierPoints );
878  converter.GetPoly( polyPoints );
879 
880  for( size_t k = 0; k + 1 < polyPoints.size(); k++ )
881  {
882  SCH_LINE* line = new SCH_LINE( polyPoints.at( k ) + m_sheetOffset,
884 
885  line->SetEndPoint( polyPoints.at( k + 1 ) + m_sheetOffset );
886  line->SetLineWidth( elem.lineWidth );
887 
888  line->SetFlags( IS_NEW );
889  m_currentSheet->GetScreen()->Append( line );
890  }
891  }
892  }
893  }
894  else
895  {
896  const auto& symbol = m_symbols.find( elem.ownerindex );
897 
898  if( symbol == m_symbols.end() )
899  {
900  // TODO: e.g. can depend on Template (RECORD=39
901  m_reporter->Report( wxString::Format( _( "Bezier has non-existent ownerindex %d." ),
902  elem.ownerindex ),
904  return;
905  }
906 
908  return;
909 
910  const auto& component = m_components.at( symbol->first );
911 
912  for( size_t i = 0; i + 1 < elem.points.size(); i += 3 )
913  {
914  if( i + 2 == elem.points.size() )
915  {
916  // special case: single line
917  LIB_POLYLINE* line = new LIB_POLYLINE( symbol->second );
918  symbol->second->AddDrawItem( line );
919 
920  line->SetUnit( elem.ownerpartid );
921 
922  for( size_t j = i; j < elem.points.size() && j < i + 2; j++ )
923  {
924  line->AddPoint( GetRelativePosition( elem.points.at( j ) + m_sheetOffset,
925  component ) );
926  }
927 
928  line->SetWidth( elem.lineWidth );
929  }
930  else
931  {
932  // bezier always has maximum of 4 control points
933  LIB_BEZIER* bezier = new LIB_BEZIER( symbol->second );
934  symbol->second->AddDrawItem( bezier );
935 
936  bezier->SetUnit( elem.ownerpartid );
937 
938  for( size_t j = i; j < elem.points.size() && j < i + 4; j++ )
939  {
940  bezier->AddPoint( GetRelativePosition( elem.points.at( j ) + m_sheetOffset,
941  component ) );
942  }
943 
944  bezier->SetWidth( elem.lineWidth );
945  }
946  }
947  }
948 }
949 
950 
951 void SCH_ALTIUM_PLUGIN::ParsePolyline( const std::map<wxString, wxString>& aProperties )
952 {
953  ASCH_POLYLINE elem( aProperties );
954 
955  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
956  {
958  switch( elem.linestyle )
959  {
960  default:
965  }
966 
967  for( size_t i = 0; i + 1 < elem.points.size(); i++ )
968  {
969  SCH_LINE* line = new SCH_LINE( elem.points.at( i ) + m_sheetOffset,
971 
972  line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
973  line->SetLineWidth( elem.lineWidth );
974  line->SetLineStyle( dashType );
975 
976  line->SetFlags( IS_NEW );
977  m_currentSheet->GetScreen()->Append( line );
978  }
979  }
980  else
981  {
982  const auto& symbol = m_symbols.find( elem.ownerindex );
983  if( symbol == m_symbols.end() )
984  {
985  // TODO: e.g. can depend on Template (RECORD=39
986  m_reporter->Report( wxString::Format( _( "Polyline has non-existent ownerindex %d." ),
987  elem.ownerindex ),
989  return;
990  }
991 
993  return;
994 
995  const auto& component = m_components.at( symbol->first );
996 
997  LIB_POLYLINE* line = new LIB_POLYLINE( symbol->second );
998  symbol->second->AddDrawItem( line );
999 
1000  line->SetUnit( elem.ownerpartid );
1001 
1002  for( wxPoint& point : elem.points )
1003  {
1004  line->AddPoint( GetRelativePosition( point + m_sheetOffset, component ) );
1005  }
1006 
1007  line->SetWidth( elem.lineWidth );
1008  }
1009 }
1010 
1011 
1012 void SCH_ALTIUM_PLUGIN::ParsePolygon( const std::map<wxString, wxString>& aProperties )
1013 {
1014  ASCH_POLYGON elem( aProperties );
1015 
1016  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1017  {
1018  // TODO: we cannot fill this polygon, only draw it for now
1019  for( size_t i = 0; i + 1 < elem.points.size(); i++ )
1020  {
1021  SCH_LINE* line =
1022  new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_NOTES );
1023  line->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
1024  line->SetLineWidth( elem.lineWidth );
1026 
1027  line->SetFlags( IS_NEW );
1028  m_currentSheet->GetScreen()->Append( line );
1029  }
1030 
1031  // close polygon
1032  SCH_LINE* line =
1033  new SCH_LINE( elem.points.front() + m_sheetOffset, SCH_LAYER_ID::LAYER_NOTES );
1034  line->SetEndPoint( elem.points.back() + m_sheetOffset );
1035  line->SetLineWidth( elem.lineWidth );
1037 
1038  line->SetFlags( IS_NEW );
1039  m_currentSheet->GetScreen()->Append( line );
1040  }
1041  else
1042  {
1043  const auto& symbol = m_symbols.find( elem.ownerindex );
1044 
1045  if( symbol == m_symbols.end() )
1046  {
1047  // TODO: e.g. can depend on Template (RECORD=39
1048  m_reporter->Report( wxString::Format( _( "Polygon has non-existent ownerindex %d." ),
1049  elem.ownerindex ),
1051  return;
1052  }
1053 
1055  return;
1056 
1057  const auto& component = m_components.at( symbol->first );
1058 
1059  LIB_POLYLINE* line = new LIB_POLYLINE( symbol->second );
1060  symbol->second->AddDrawItem( line );
1061 
1062  line->SetUnit( elem.ownerpartid );
1063 
1064  for( wxPoint& point : elem.points )
1065  line->AddPoint( GetRelativePosition( point + m_sheetOffset, component ) );
1066 
1067  line->AddPoint( GetRelativePosition( elem.points.front() + m_sheetOffset, component ) );
1068 
1069  line->SetWidth( elem.lineWidth );
1070 
1071  if( !elem.isSolid )
1073  else if( elem.color == elem.areacolor )
1075  else
1077  }
1078 }
1079 
1080 
1081 void SCH_ALTIUM_PLUGIN::ParseRoundRectangle( const std::map<wxString, wxString>& aProperties )
1082 {
1083  ASCH_ROUND_RECTANGLE elem( aProperties );
1084 
1085  wxPoint sheetTopRight = elem.topRight + m_sheetOffset;
1086  wxPoint sheetBottomLeft = elem.bottomLeft + m_sheetOffset;
1087 
1088  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1089  {
1090  const wxPoint topLeft = { sheetBottomLeft.x, sheetTopRight.y };
1091  const wxPoint bottomRight = { sheetTopRight.x, sheetBottomLeft.y };
1092 
1093  // TODO: we cannot fill this rectangle, only draw it for now
1094  // TODO: misses rounded edges
1095  SCH_LINE* lineTop = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1096  lineTop->SetEndPoint( topLeft );
1097  lineTop->SetLineWidth( elem.lineWidth );
1098  lineTop->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1099  lineTop->SetFlags( IS_NEW );
1100  m_currentSheet->GetScreen()->Append( lineTop );
1101 
1102  SCH_LINE* lineBottom = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1103  lineBottom->SetEndPoint( bottomRight );
1104  lineBottom->SetLineWidth( elem.lineWidth );
1105  lineBottom->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1106  lineBottom->SetFlags( IS_NEW );
1107  m_currentSheet->GetScreen()->Append( lineBottom );
1108 
1109  SCH_LINE* lineRight = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1110  lineRight->SetEndPoint( bottomRight );
1111  lineRight->SetLineWidth( elem.lineWidth );
1112  lineRight->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1113  lineRight->SetFlags( IS_NEW );
1114  m_currentSheet->GetScreen()->Append( lineRight );
1115 
1116  SCH_LINE* lineLeft = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1117  lineLeft->SetEndPoint( topLeft );
1118  lineLeft->SetLineWidth( elem.lineWidth );
1119  lineLeft->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1120  lineLeft->SetFlags( IS_NEW );
1121  m_currentSheet->GetScreen()->Append( lineLeft );
1122  }
1123  else
1124  {
1125  const auto& symbol = m_symbols.find( elem.ownerindex );
1126 
1127  if( symbol == m_symbols.end() )
1128  {
1129  // TODO: e.g. can depend on Template (RECORD=39
1130  m_reporter->Report( wxString::Format( _( "Rounded rectangle has non-existent "
1131  "ownerindex %d." ),
1132  elem.ownerindex ),
1134  return;
1135  }
1136 
1138  return;
1139 
1140  const auto& component = m_components.at( symbol->first );
1141 
1142  // TODO: misses rounded edges
1143  LIB_RECTANGLE* rect = new LIB_RECTANGLE( symbol->second );
1144  symbol->second->AddDrawItem( rect );
1145 
1146  rect->SetUnit( elem.ownerpartid );
1147 
1148  rect->SetPosition( GetRelativePosition( elem.topRight + m_sheetOffset, component ) );
1149  rect->SetEnd( GetRelativePosition( elem.bottomLeft + m_sheetOffset, component ) );
1150  rect->SetWidth( elem.lineWidth );
1151 
1152  if( !elem.isSolid )
1154  else if( elem.color == elem.areacolor )
1156  else
1158  }
1159 }
1160 
1161 
1162 void SCH_ALTIUM_PLUGIN::ParseArc( const std::map<wxString, wxString>& aProperties )
1163 {
1164  ASCH_ARC elem( aProperties );
1165 
1166  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1167  {
1168  m_reporter->Report( _( "Arc drawing is not possible for now on schematic." ),
1170  }
1171  else
1172  {
1173  const auto& symbol = m_symbols.find( elem.ownerindex );
1174  if( symbol == m_symbols.end() )
1175  {
1176  // TODO: e.g. can depend on Template (RECORD=39
1177  m_reporter->Report( wxString::Format( _( "Arc has non-existent ownerindex %d." ),
1178  elem.ownerindex ),
1180  return;
1181  }
1182 
1184  return;
1185 
1186  const auto& component = m_components.at( symbol->first );
1187 
1188  if( elem.startAngle == 0 && ( elem.endAngle == 0 || elem.endAngle == 360 ) )
1189  {
1190  LIB_CIRCLE* circle = new LIB_CIRCLE( symbol->second );
1191  symbol->second->AddDrawItem( circle );
1192 
1193  circle->SetUnit( elem.ownerpartid );
1194 
1195  circle->SetPosition( GetRelativePosition( elem.center + m_sheetOffset, component ) );
1196  circle->SetRadius( elem.radius );
1197  circle->SetWidth( elem.lineWidth );
1198  }
1199  else
1200  {
1201  LIB_ARC* arc = new LIB_ARC( symbol->second );
1202  symbol->second->AddDrawItem( arc );
1203 
1204  arc->SetUnit( elem.ownerpartid );
1205 
1206  // TODO: correct?
1207  arc->SetPosition( GetRelativePosition( elem.center + m_sheetOffset, component ) );
1208  arc->SetRadius( elem.radius );
1209  arc->SetFirstRadiusAngle( elem.startAngle * 10. );
1210  arc->SetSecondRadiusAngle( elem.endAngle * 10. );
1211  }
1212  }
1213 }
1214 
1215 
1216 void SCH_ALTIUM_PLUGIN::ParseLine( const std::map<wxString, wxString>& aProperties )
1217 {
1218  ASCH_LINE elem( aProperties );
1219 
1220  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1221  {
1222  // close polygon
1224  line->SetEndPoint( elem.point2 + m_sheetOffset );
1225  line->SetLineWidth( elem.lineWidth );
1226  line->SetLineStyle( PLOT_DASH_TYPE::SOLID ); // TODO?
1227 
1228  line->SetFlags( IS_NEW );
1229  m_currentSheet->GetScreen()->Append( line );
1230  }
1231  else
1232  {
1233  const auto& symbol = m_symbols.find( elem.ownerindex );
1234 
1235  if( symbol == m_symbols.end() )
1236  {
1237  // TODO: e.g. can depend on Template (RECORD=39
1238  m_reporter->Report( wxString::Format( _( "Line has non-existent ownerindex %d." ),
1239  elem.ownerindex ),
1241  return;
1242  }
1243 
1245  return;
1246 
1247  const auto& component = m_components.at( symbol->first );
1248 
1249  LIB_POLYLINE* line = new LIB_POLYLINE( symbol->second );
1250  symbol->second->AddDrawItem( line );
1251 
1252  line->SetUnit( elem.ownerpartid );
1253 
1254  line->AddPoint( GetRelativePosition( elem.point1 + m_sheetOffset, component ) );
1255  line->AddPoint( GetRelativePosition( elem.point2 + m_sheetOffset, component ) );
1256 
1257  line->SetWidth( elem.lineWidth );
1258  }
1259 }
1260 
1261 
1262 void SCH_ALTIUM_PLUGIN::ParseRectangle( const std::map<wxString, wxString>& aProperties )
1263 {
1264  ASCH_RECTANGLE elem( aProperties );
1265 
1266  wxPoint sheetTopRight = elem.topRight + m_sheetOffset;
1267  wxPoint sheetBottomLeft = elem.bottomLeft + m_sheetOffset;
1268 
1269  if( elem.ownerpartid == ALTIUM_COMPONENT_NONE )
1270  {
1271  const wxPoint topLeft = { sheetBottomLeft.x, sheetTopRight.y };
1272  const wxPoint bottomRight = { sheetTopRight.x, sheetBottomLeft.y };
1273 
1274  // TODO: we cannot fill this rectangle, only draw it for now
1275  SCH_LINE* lineTop = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1276  lineTop->SetEndPoint( topLeft );
1277  lineTop->SetLineWidth( elem.lineWidth );
1278  lineTop->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1279  lineTop->SetFlags( IS_NEW );
1280  m_currentSheet->GetScreen()->Append( lineTop );
1281 
1282  SCH_LINE* lineBottom = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1283  lineBottom->SetEndPoint( bottomRight );
1284  lineBottom->SetLineWidth( elem.lineWidth );
1285  lineBottom->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1286  lineBottom->SetFlags( IS_NEW );
1287  m_currentSheet->GetScreen()->Append( lineBottom );
1288 
1289  SCH_LINE* lineRight = new SCH_LINE( sheetTopRight, SCH_LAYER_ID::LAYER_NOTES );
1290  lineRight->SetEndPoint( bottomRight );
1291  lineRight->SetLineWidth( elem.lineWidth );
1292  lineRight->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1293  lineRight->SetFlags( IS_NEW );
1294  m_currentSheet->GetScreen()->Append( lineRight );
1295 
1296  SCH_LINE* lineLeft = new SCH_LINE( sheetBottomLeft, SCH_LAYER_ID::LAYER_NOTES );
1297  lineLeft->SetEndPoint( topLeft );
1298  lineLeft->SetLineWidth( elem.lineWidth );
1299  lineLeft->SetLineStyle( PLOT_DASH_TYPE::SOLID );
1300  lineLeft->SetFlags( IS_NEW );
1301  m_currentSheet->GetScreen()->Append( lineLeft );
1302  }
1303  else
1304  {
1305  const auto& symbol = m_symbols.find( elem.ownerindex );
1306 
1307  if( symbol == m_symbols.end() )
1308  {
1309  // TODO: e.g. can depend on Template (RECORD=39
1310  m_reporter->Report( wxString::Format( _( "Rectangle has non-existent ownerindex %d." ),
1311  elem.ownerindex ),
1313  return;
1314  }
1315 
1317  return;
1318 
1319  const auto& component = m_components.at( symbol->first );
1320 
1321  LIB_RECTANGLE* rect = new LIB_RECTANGLE( symbol->second );
1322  symbol->second->AddDrawItem( rect );
1323 
1324  rect->SetUnit( elem.ownerpartid );
1325 
1326  rect->SetPosition( GetRelativePosition( sheetTopRight, component ) );
1327  rect->SetEnd( GetRelativePosition( sheetBottomLeft, component ) );
1328  rect->SetWidth( elem.lineWidth );
1329 
1330  if( !elem.isSolid )
1332  else if( elem.color == elem.areacolor )
1334  else
1336  }
1337 }
1338 
1339 
1341  int aIndex, const std::map<wxString, wxString>& aProperties )
1342 {
1343  ASCH_SHEET_SYMBOL elem( aProperties );
1344 
1345  SCH_SHEET* sheet = new SCH_SHEET( m_currentSheet, elem.location + m_sheetOffset );
1346  SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
1347 
1348  sheet->SetSize( elem.size );
1349 
1350  sheet->SetBorderColor( GetColorFromInt( elem.color ) );
1351  if( elem.isSolid )
1352  sheet->SetBackgroundColor( GetColorFromInt( elem.areacolor ) );
1353 
1354  sheet->SetScreen( screen );
1355 
1356  sheet->SetFlags( IS_NEW );
1357  m_currentSheet->GetScreen()->Append( sheet );
1358 
1359  m_sheets.insert( { aIndex, sheet } );
1360 }
1361 
1362 
1363 void SCH_ALTIUM_PLUGIN::ParseSheetEntry( const std::map<wxString, wxString>& aProperties )
1364 {
1365  ASCH_SHEET_ENTRY elem( aProperties );
1366 
1367  const auto& sheet = m_sheets.find( elem.ownerindex );
1368  if( sheet == m_sheets.end() )
1369  {
1370  wxLogError( wxString::Format( "Sheet Entry has non-existent ownerindex %d",
1371  elem.ownerindex ) );
1372  return;
1373  }
1374 
1375  SCH_SHEET_PIN* sheetPin = new SCH_SHEET_PIN( sheet->second );
1376  sheet->second->AddPin( sheetPin );
1377 
1378  sheetPin->SetText( elem.name );
1380  //sheetPin->SetLabelSpinStyle( getSpinStyle( term.OrientAngle, false ) );
1381  //sheetPin->SetPosition( getKiCadPoint( term.Position ) );
1382 
1383  wxPoint pos = sheet->second->GetPosition();
1384  wxSize size = sheet->second->GetSize();
1385 
1386  switch( elem.side )
1387  {
1388  default:
1390  sheetPin->SetPosition( { pos.x, pos.y + elem.distanceFromTop } );
1392  sheetPin->SetEdge( SHEET_SIDE::LEFT );
1393  break;
1395  sheetPin->SetPosition( { pos.x + size.x, pos.y + elem.distanceFromTop } );
1397  sheetPin->SetEdge( SHEET_SIDE::RIGHT );
1398  break;
1400  sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y } );
1402  sheetPin->SetEdge( SHEET_SIDE::TOP );
1403  break;
1405  sheetPin->SetPosition( { pos.x + elem.distanceFromTop, pos.y + size.y } );
1407  sheetPin->SetEdge( SHEET_SIDE::BOTTOM );
1408  break;
1409  }
1410 
1411  switch( elem.iotype )
1412  {
1413  default:
1416  break;
1419  break;
1422  break;
1425  break;
1426  }
1427 }
1428 
1429 
1431  REPORTER* aReporter )
1432 {
1433  if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE || aStyle == ASCH_POWER_PORT_STYLE::ARROW )
1434  {
1435  LIB_POLYLINE* line1 = new LIB_POLYLINE( aKsymbol );
1436  aKsymbol->AddDrawItem( line1 );
1437  line1->SetWidth( Mils2iu( 10 ) );
1438  line1->AddPoint( { 0, 0 } );
1439  line1->AddPoint( { 0, Mils2iu( -50 ) } );
1440 
1441  if( aStyle == ASCH_POWER_PORT_STYLE::CIRCLE )
1442  {
1443  LIB_CIRCLE* circle = new LIB_CIRCLE( aKsymbol );
1444  aKsymbol->AddDrawItem( circle );
1445  circle->SetWidth( Mils2iu( 5 ) );
1446  circle->SetRadius( Mils2iu( 25 ) );
1447  circle->SetPosition( { Mils2iu( 0 ), Mils2iu( -75 ) } );
1448  }
1449  else
1450  {
1451  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1452  aKsymbol->AddDrawItem( line2 );
1453  line2->SetWidth( Mils2iu( 10 ) );
1454  line2->AddPoint( { Mils2iu( -25 ), Mils2iu( -50 ) } );
1455  line2->AddPoint( { Mils2iu( 25 ), Mils2iu( -50 ) } );
1456  line2->AddPoint( { Mils2iu( 0 ), Mils2iu( -100 ) } );
1457  line2->AddPoint( { Mils2iu( -25 ), Mils2iu( -50 ) } );
1458  }
1459 
1460  return { 0, Mils2iu( 150 ) };
1461  }
1462  else if( aStyle == ASCH_POWER_PORT_STYLE::WAVE )
1463  {
1464  LIB_POLYLINE* line = new LIB_POLYLINE( aKsymbol );
1465  aKsymbol->AddDrawItem( line );
1466  line->SetWidth( Mils2iu( 10 ) );
1467  line->AddPoint( { 0, 0 } );
1468  line->AddPoint( { 0, Mils2iu( -72 ) } );
1469 
1470  LIB_BEZIER* bezier = new LIB_BEZIER( aKsymbol );
1471  aKsymbol->AddDrawItem( bezier );
1472  bezier->SetWidth( Mils2iu( 5 ) );
1473  bezier->AddPoint( { Mils2iu( 30 ), Mils2iu( -50 ) } );
1474  bezier->AddPoint( { Mils2iu( 30 ), Mils2iu( -87 ) } );
1475  bezier->AddPoint( { Mils2iu( -30 ), Mils2iu( -63 ) } );
1476  bezier->AddPoint( { Mils2iu( -30 ), Mils2iu( -100 ) } );
1477 
1478  return { 0, Mils2iu( 150 ) };
1479  }
1480  else if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND
1482  || aStyle == ASCH_POWER_PORT_STYLE::EARTH
1483  || aStyle == ASCH_POWER_PORT_STYLE::GOST_ARROW )
1484  {
1485  LIB_POLYLINE* line1 = new LIB_POLYLINE( aKsymbol );
1486  aKsymbol->AddDrawItem( line1 );
1487  line1->SetWidth( Mils2iu( 10 ) );
1488  line1->AddPoint( { 0, 0 } );
1489  line1->AddPoint( { 0, Mils2iu( -100 ) } );
1490 
1491  if( aStyle == ASCH_POWER_PORT_STYLE::POWER_GROUND )
1492  {
1493  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1494  aKsymbol->AddDrawItem( line2 );
1495  line2->SetWidth( Mils2iu( 10 ) );
1496  line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1497  line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -100 ) } );
1498 
1499  LIB_POLYLINE* line3 = new LIB_POLYLINE( aKsymbol );
1500  aKsymbol->AddDrawItem( line3 );
1501  line3->SetWidth( Mils2iu( 10 ) );
1502  line3->AddPoint( { Mils2iu( -70 ), Mils2iu( -130 ) } );
1503  line3->AddPoint( { Mils2iu( 70 ), Mils2iu( -130 ) } );
1504 
1505  LIB_POLYLINE* line4 = new LIB_POLYLINE( aKsymbol );
1506  aKsymbol->AddDrawItem( line4 );
1507  line4->SetWidth( Mils2iu( 10 ) );
1508  line4->AddPoint( { Mils2iu( -40 ), Mils2iu( -160 ) } );
1509  line4->AddPoint( { Mils2iu( 40 ), Mils2iu( -160 ) } );
1510 
1511  LIB_POLYLINE* line5 = new LIB_POLYLINE( aKsymbol );
1512  aKsymbol->AddDrawItem( line5 );
1513  line5->SetWidth( Mils2iu( 10 ) );
1514  line5->AddPoint( { Mils2iu( -10 ), Mils2iu( -190 ) } );
1515  line5->AddPoint( { Mils2iu( 10 ), Mils2iu( -190 ) } );
1516  }
1517  else if( aStyle == ASCH_POWER_PORT_STYLE::SIGNAL_GROUND )
1518  {
1519  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1520  aKsymbol->AddDrawItem( line2 );
1521  line2->SetWidth( Mils2iu( 10 ) );
1522  line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1523  line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -100 ) } );
1524  line2->AddPoint( { Mils2iu( 0 ), Mils2iu( -200 ) } );
1525  line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1526  }
1527  else if( aStyle == ASCH_POWER_PORT_STYLE::EARTH )
1528  {
1529  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1530  aKsymbol->AddDrawItem( line2 );
1531  line2->SetWidth( Mils2iu( 10 ) );
1532  line2->AddPoint( { Mils2iu( -150 ), Mils2iu( -200 ) } );
1533  line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -100 ) } );
1534  line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -100 ) } );
1535  line2->AddPoint( { Mils2iu( 50 ), Mils2iu( -200 ) } );
1536 
1537  LIB_POLYLINE* line3 = new LIB_POLYLINE( aKsymbol );
1538  aKsymbol->AddDrawItem( line3 );
1539  line3->SetWidth( Mils2iu( 10 ) );
1540  line3->AddPoint( { Mils2iu( 0 ), Mils2iu( -100 ) } );
1541  line3->AddPoint( { Mils2iu( -50 ), Mils2iu( -200 ) } );
1542  }
1543  else // ASCH_POWER_PORT_STYLE::GOST_ARROW
1544  {
1545  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1546  aKsymbol->AddDrawItem( line2 );
1547  line2->SetWidth( Mils2iu( 10 ) );
1548  line2->AddPoint( { Mils2iu( -25 ), Mils2iu( -50 ) } );
1549  line2->AddPoint( { Mils2iu( 0 ), Mils2iu( -100 ) } );
1550  line2->AddPoint( { Mils2iu( 25 ), Mils2iu( -50 ) } );
1551 
1552  return { 0, Mils2iu( 150 ) }; // special case
1553  }
1554 
1555  return { 0, Mils2iu( 250 ) };
1556  }
1557  else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_POWER_GROUND
1558  || aStyle == ASCH_POWER_PORT_STYLE::GOST_EARTH )
1559  {
1560  LIB_POLYLINE* line1 = new LIB_POLYLINE( aKsymbol );
1561  aKsymbol->AddDrawItem( line1 );
1562  line1->SetWidth( Mils2iu( 10 ) );
1563  line1->AddPoint( { 0, 0 } );
1564  line1->AddPoint( { 0, Mils2iu( -160 ) } );
1565 
1566  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1567  aKsymbol->AddDrawItem( line2 );
1568  line2->SetWidth( Mils2iu( 10 ) );
1569  line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -160 ) } );
1570  line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -160 ) } );
1571 
1572  LIB_POLYLINE* line3 = new LIB_POLYLINE( aKsymbol );
1573  aKsymbol->AddDrawItem( line3 );
1574  line3->SetWidth( Mils2iu( 10 ) );
1575  line3->AddPoint( { Mils2iu( -60 ), Mils2iu( -200 ) } );
1576  line3->AddPoint( { Mils2iu( 60 ), Mils2iu( -200 ) } );
1577 
1578  LIB_POLYLINE* line4 = new LIB_POLYLINE( aKsymbol );
1579  aKsymbol->AddDrawItem( line4 );
1580  line4->SetWidth( Mils2iu( 10 ) );
1581  line4->AddPoint( { Mils2iu( -20 ), Mils2iu( -240 ) } );
1582  line4->AddPoint( { Mils2iu( 20 ), Mils2iu( -240 ) } );
1583 
1585  return { 0, Mils2iu( 300 ) };
1586 
1587  LIB_CIRCLE* circle = new LIB_CIRCLE( aKsymbol );
1588  aKsymbol->AddDrawItem( circle );
1589  circle->SetWidth( Mils2iu( 10 ) );
1590  circle->SetRadius( Mils2iu( 120 ) );
1591  circle->SetPosition( { Mils2iu( 0 ), Mils2iu( -160 ) } );
1592 
1593  return { 0, Mils2iu( 350 ) };
1594  }
1595  else if( aStyle == ASCH_POWER_PORT_STYLE::GOST_BAR )
1596  {
1597  LIB_POLYLINE* line1 = new LIB_POLYLINE( aKsymbol );
1598  aKsymbol->AddDrawItem( line1 );
1599  line1->SetWidth( Mils2iu( 10 ) );
1600  line1->AddPoint( { 0, 0 } );
1601  line1->AddPoint( { 0, Mils2iu( -200 ) } );
1602 
1603  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1604  aKsymbol->AddDrawItem( line2 );
1605  line2->SetWidth( Mils2iu( 10 ) );
1606  line2->AddPoint( { Mils2iu( -100 ), Mils2iu( -200 ) } );
1607  line2->AddPoint( { Mils2iu( 100 ), Mils2iu( -200 ) } );
1608 
1609  return { 0, Mils2iu( 250 ) };
1610  }
1611  else
1612  {
1613  if( aStyle != ASCH_POWER_PORT_STYLE::BAR )
1614  {
1615  aReporter->Report( _( "Power Port has unknown style, use bar instead." ),
1617  }
1618 
1619  LIB_POLYLINE* line1 = new LIB_POLYLINE( aKsymbol );
1620  aKsymbol->AddDrawItem( line1 );
1621  line1->SetWidth( Mils2iu( 10 ) );
1622  line1->AddPoint( { 0, 0 } );
1623  line1->AddPoint( { 0, Mils2iu( -100 ) } );
1624 
1625  LIB_POLYLINE* line2 = new LIB_POLYLINE( aKsymbol );
1626  aKsymbol->AddDrawItem( line2 );
1627  line2->SetWidth( Mils2iu( 10 ) );
1628  line2->AddPoint( { Mils2iu( -50 ), Mils2iu( -100 ) } );
1629  line2->AddPoint( { Mils2iu( 50 ), Mils2iu( -100 ) } );
1630 
1631  return { 0, Mils2iu( 150 ) };
1632  }
1633 }
1634 
1635 
1636 void SCH_ALTIUM_PLUGIN::ParsePowerPort( const std::map<wxString, wxString>& aProperties )
1637 {
1638  ASCH_POWER_PORT elem( aProperties );
1639 
1640  LIB_ID libId = AltiumToKiCadLibID( getLibName(), elem.text );
1641 
1642  LIB_SYMBOL* ksymbol = nullptr;
1643 
1644  const auto& symbol = m_powerSymbols.find( elem.text );
1645  if( symbol != m_powerSymbols.end() )
1646  {
1647  ksymbol = symbol->second; // cache hit
1648  }
1649  else
1650  {
1651  ksymbol = new LIB_SYMBOL( wxEmptyString );
1652  ksymbol->SetPower();
1653  ksymbol->SetName( elem.text );
1654  ksymbol->GetReferenceField().SetText( "#PWR" );
1655  ksymbol->GetValueField().SetText( elem.text );
1656  ksymbol->GetValueField().SetVisible( true ); // TODO: why does this not work?
1657  ksymbol->SetDescription(
1658  wxString::Format( _( "Power symbol creates a global label with name '%s'" ),
1659  elem.text ) );
1660  ksymbol->SetKeyWords( "power-flag" );
1661  ksymbol->SetLibId( libId );
1662 
1663  // generate graphic
1664  LIB_PIN* pin = new LIB_PIN( ksymbol );
1665  ksymbol->AddDrawItem( pin );
1666 
1667  pin->SetName( elem.text );
1668  pin->SetPosition( { 0, 0 } );
1669  pin->SetLength( 0 );
1670 
1671  // marks the pin as a global label
1673  pin->SetVisible( false );
1674 
1675  wxPoint valueFieldPos = HelperGeneratePowerPortGraphics( ksymbol, elem.style, m_reporter );
1676 
1677  ksymbol->GetValueField().SetPosition( valueFieldPos );
1678 
1679  // this has to be done after parsing the LIB_SYMBOL!
1680  m_pi->SaveSymbol( getLibFileName().GetFullPath(), ksymbol, m_properties.get() );
1681  m_powerSymbols.insert( { elem.text, ksymbol } );
1682  }
1683 
1684  SCH_SHEET_PATH sheetpath;
1686 
1687  // each component has its own symbol for now
1688  SCH_SYMBOL* component = new SCH_SYMBOL();
1689  component->SetRef( &sheetpath, "#PWR?" );
1690  component->SetValue( elem.text );
1691  component->SetLibId( libId );
1692  component->SetLibSymbol( new LIB_SYMBOL( *ksymbol ) );
1693 
1694  SCH_FIELD* valueField = component->GetField( VALUE_FIELD );
1695 
1696  // TODO: Why do I need to set those a second time?
1697  valueField->SetVisible( true );
1698  valueField->SetPosition( ksymbol->GetValueField().GetPosition() );
1699 
1700  component->SetPosition( elem.location + m_sheetOffset );
1701 
1702  switch( elem.orientation )
1703  {
1706  valueField->SetTextAngle( -900. );
1708  break;
1711  valueField->SetTextAngle( -1800. );
1713  break;
1716  valueField->SetTextAngle( -2700. );
1718  break;
1721  valueField->SetTextAngle( 0. );
1723  break;
1724  default:
1725  m_reporter->Report( _( "Pin has unexpected orientation." ), RPT_SEVERITY_WARNING );
1726  break;
1727  }
1728 
1729  m_currentSheet->GetScreen()->Append( component );
1730 }
1731 
1732 
1734 {
1735  // Get both connection points where we could connect to
1736  wxPoint start = aElem.location + m_sheetOffset;
1737  wxPoint end = start;
1738 
1739  switch( aElem.style )
1740  {
1741  default:
1743  case ASCH_PORT_STYLE::LEFT:
1746  end.x += aElem.width;
1747  break;
1749  case ASCH_PORT_STYLE::TOP:
1752  end.y -= aElem.width;
1753  break;
1754  }
1755 
1756  // Check which connection points exists in the schematic
1757  SCH_SCREEN* screen = m_currentSheet->GetScreen();
1758 
1759  bool startIsWireTerminal = screen->IsTerminalPoint( start, LAYER_WIRE );
1760  bool startIsBusTerminal = screen->IsTerminalPoint( start, LAYER_BUS );
1761 
1762  bool endIsWireTerminal = screen->IsTerminalPoint( end, LAYER_WIRE );
1763  bool endIsBusTerminal = screen->IsTerminalPoint( end, LAYER_BUS );
1764 
1765  // check if any of the points is a terminal point
1766  // TODO: there seems a problem to detect approximated connections towards component pins?
1767  bool connectionFound = startIsWireTerminal
1768  || startIsBusTerminal
1769  || endIsWireTerminal
1770  || endIsBusTerminal;
1771 
1772  if( !connectionFound )
1773  {
1774  wxLogError( wxString::Format( "There is a Port for \"%s\", but no connections towards it?",
1775  aElem.name ) );
1776  }
1777 
1778  // Select label position. In case both match, we will add a line later.
1779  wxPoint position = ( startIsWireTerminal || startIsBusTerminal ) ? start : end;
1780 
1781  SCH_TEXT* const label = new SCH_GLOBALLABEL( position, aElem.name );
1782  // TODO: detect correct label type depending on sheet settings, etc.
1783  // label = new SCH_HIERLABEL( elem.location + m_sheetOffset, elem.name );
1784 
1785  switch( aElem.iotype )
1786  {
1787  default:
1790  break;
1793  break;
1796  break;
1799  break;
1800  }
1801 
1802  switch( aElem.style )
1803  {
1804  default:
1806  case ASCH_PORT_STYLE::LEFT:
1809  if( ( startIsWireTerminal || startIsBusTerminal ) )
1811  else
1813  break;
1815  case ASCH_PORT_STYLE::TOP:
1818  if( ( startIsWireTerminal || startIsBusTerminal ) )
1820  else
1822  break;
1823  }
1824 
1825  label->SetFlags( IS_NEW );
1826  m_currentSheet->GetScreen()->Append( label );
1827 
1828  // This is a hack, for the case both connection points are valid: add a small wire
1829  if( ( startIsWireTerminal && endIsWireTerminal ) || !connectionFound )
1830  {
1831  SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_WIRE );
1832  wire->SetEndPoint( end );
1833  wire->SetLineWidth( Mils2iu( 2 ) );
1834  wire->SetFlags( IS_NEW );
1835  m_currentSheet->GetScreen()->Append( wire );
1836  }
1837  else if( startIsBusTerminal && endIsBusTerminal )
1838  {
1839  SCH_LINE* wire = new SCH_LINE( start, SCH_LAYER_ID::LAYER_BUS );
1840  wire->SetEndPoint( end );
1841  wire->SetLineWidth( Mils2iu( 2 ) );
1842  wire->SetFlags( IS_NEW );
1843  m_currentSheet->GetScreen()->Append( wire );
1844  }
1845 }
1846 
1847 
1848 void SCH_ALTIUM_PLUGIN::ParseNoERC( const std::map<wxString, wxString>& aProperties )
1849 {
1850  ASCH_NO_ERC elem( aProperties );
1851 
1852  if( elem.isActive )
1853  {
1854  SCH_NO_CONNECT* noConnect = new SCH_NO_CONNECT( elem.location + m_sheetOffset );
1855 
1856  noConnect->SetFlags( IS_NEW );
1857  m_currentSheet->GetScreen()->Append( noConnect );
1858  }
1859 }
1860 
1861 
1862 void SCH_ALTIUM_PLUGIN::ParseNetLabel( const std::map<wxString, wxString>& aProperties )
1863 {
1864  ASCH_NET_LABEL elem( aProperties );
1865 
1866  SCH_LABEL* label = new SCH_LABEL( elem.location + m_sheetOffset, elem.text );
1867 
1868  switch( elem.orientation )
1869  {
1872  break;
1875  break;
1878  break;
1881  break;
1882  default:
1883  break;
1884  }
1885 
1886  label->SetFlags( IS_NEW );
1887  m_currentSheet->GetScreen()->Append( label );
1888 }
1889 
1890 
1891 void SCH_ALTIUM_PLUGIN::ParseBus( const std::map<wxString, wxString>& aProperties )
1892 {
1893  ASCH_BUS elem( aProperties );
1894 
1895  for( size_t i = 0; i + 1 < elem.points.size(); i++ )
1896  {
1897  SCH_LINE* bus =
1898  new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_BUS );
1899  bus->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
1900  bus->SetLineWidth( elem.lineWidth );
1901 
1902  bus->SetFlags( IS_NEW );
1903  m_currentSheet->GetScreen()->Append( bus );
1904  }
1905 }
1906 
1907 
1908 void SCH_ALTIUM_PLUGIN::ParseWire( const std::map<wxString, wxString>& aProperties )
1909 {
1910  ASCH_WIRE elem( aProperties );
1911 
1912  for( size_t i = 0; i + 1 < elem.points.size(); i++ )
1913  {
1914  SCH_LINE* wire =
1915  new SCH_LINE( elem.points.at( i ) + m_sheetOffset, SCH_LAYER_ID::LAYER_WIRE );
1916  wire->SetEndPoint( elem.points.at( i + 1 ) + m_sheetOffset );
1917  wire->SetLineWidth( elem.lineWidth );
1918 
1919  wire->SetFlags( IS_NEW );
1920  m_currentSheet->GetScreen()->Append( wire );
1921  }
1922 }
1923 
1924 
1925 void SCH_ALTIUM_PLUGIN::ParseJunction( const std::map<wxString, wxString>& aProperties )
1926 {
1927  ASCH_JUNCTION elem( aProperties );
1928 
1929  SCH_JUNCTION* junction = new SCH_JUNCTION( elem.location + m_sheetOffset );
1930 
1931  junction->SetFlags( IS_NEW );
1932  m_currentSheet->GetScreen()->Append( junction );
1933 }
1934 
1935 
1936 void SCH_ALTIUM_PLUGIN::ParseImage( const std::map<wxString, wxString>& aProperties )
1937 {
1938  ASCH_IMAGE elem( aProperties );
1939 
1940  wxPoint center = ( elem.location + elem.corner ) / 2 + m_sheetOffset;
1941  std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
1942 
1943  if( elem.embedimage )
1944  {
1945  const ASCH_STORAGE_FILE* storageFile = GetFileFromStorage( elem.filename );
1946 
1947  if( !storageFile )
1948  {
1949  wxLogError(
1950  wxString::Format( "Embedded file not found in storage: %s", elem.filename ) );
1951  return;
1952  }
1953 
1954  wxString storagePath = wxFileName::CreateTempFileName( "kicad_import_" );
1955 
1956  // As wxZlibInputStream is not seekable, we need to write a temporary file
1957  wxMemoryInputStream fileStream( storageFile->data.data(), storageFile->data.size() );
1958  wxZlibInputStream zlibInputStream( fileStream );
1959  wxFFileOutputStream outputStream( storagePath );
1960  outputStream.Write( zlibInputStream );
1961  outputStream.Close();
1962 
1963  if( !bitmap->ReadImageFile( storagePath ) )
1964  {
1965  wxLogError( wxString::Format( "Error while reading image: %s", storagePath ) );
1966  return;
1967  }
1968 
1969  // Remove temporary file
1970  wxRemoveFile( storagePath );
1971  }
1972  else
1973  {
1974  if( !wxFileExists( elem.filename ) )
1975  {
1976  wxLogError( wxString::Format( "File not found on disk: %s", elem.filename ) );
1977  return;
1978  }
1979 
1980  if( !bitmap->ReadImageFile( elem.filename ) )
1981  {
1982  wxLogError( wxString::Format( "Error while reading image: %s", elem.filename ) );
1983  return;
1984  }
1985  }
1986 
1987  // we only support one scale, thus we need to select one in case it does not keep aspect ratio
1988  wxSize currentImageSize = bitmap->GetSize();
1989  wxPoint expectedImageSize = elem.location - elem.corner;
1990  double scaleX = std::abs( static_cast<double>( expectedImageSize.x ) / currentImageSize.x );
1991  double scaleY = std::abs( static_cast<double>( expectedImageSize.y ) / currentImageSize.y );
1992  bitmap->SetImageScale( std::min( scaleX, scaleY ) );
1993 
1994  bitmap->SetFlags( IS_NEW );
1995  m_currentSheet->GetScreen()->Append( bitmap.release() );
1996 }
1997 
1998 
1999 void SCH_ALTIUM_PLUGIN::ParseSheet( const std::map<wxString, wxString>& aProperties )
2000 {
2001  m_altiumSheet = std::make_unique<ASCH_SHEET>( aProperties );
2002 
2003  PAGE_INFO pageInfo;
2004 
2005  bool isPortrait = m_altiumSheet->sheetOrientation == ASCH_SHEET_WORKSPACEORIENTATION::PORTRAIT;
2006  switch( m_altiumSheet->sheetSize )
2007  {
2008  default:
2009  case ASCH_SHEET_SIZE::A4:
2010  pageInfo.SetType( "A4", isPortrait );
2011  break;
2012  case ASCH_SHEET_SIZE::A3:
2013  pageInfo.SetType( "A3", isPortrait );
2014  break;
2015  case ASCH_SHEET_SIZE::A2:
2016  pageInfo.SetType( "A2", isPortrait );
2017  break;
2018  case ASCH_SHEET_SIZE::A1:
2019  pageInfo.SetType( "A1", isPortrait );
2020  break;
2021  case ASCH_SHEET_SIZE::A0:
2022  pageInfo.SetType( "A0", isPortrait );
2023  break;
2024  case ASCH_SHEET_SIZE::A:
2025  pageInfo.SetType( "A", isPortrait );
2026  break;
2027  case ASCH_SHEET_SIZE::B:
2028  pageInfo.SetType( "B", isPortrait );
2029  break;
2030  case ASCH_SHEET_SIZE::C:
2031  pageInfo.SetType( "C", isPortrait );
2032  break;
2033  case ASCH_SHEET_SIZE::D:
2034  pageInfo.SetType( "D", isPortrait );
2035  break;
2036  case ASCH_SHEET_SIZE::E:
2037  pageInfo.SetType( "E", isPortrait );
2038  break;
2040  pageInfo.SetType( "USLetter", isPortrait );
2041  break;
2043  pageInfo.SetType( "USLegal", isPortrait );
2044  break;
2046  pageInfo.SetType( "A3", isPortrait ); // TODO: use User
2047  break;
2049  pageInfo.SetType( "A", isPortrait );
2050  break;
2052  pageInfo.SetType( "B", isPortrait );
2053  break;
2055  pageInfo.SetType( "C", isPortrait );
2056  break;
2058  pageInfo.SetType( "D", isPortrait );
2059  break;
2061  pageInfo.SetType( "E", isPortrait );
2062  break;
2063  }
2064 
2065  m_currentSheet->GetScreen()->SetPageSettings( pageInfo );
2066 
2067  m_sheetOffset = { 0, pageInfo.GetHeightIU() };
2068 }
2069 
2070 
2072 {
2073  switch( aOrientation )
2074  {
2075  default:
2077  aField.SetTextAngle( 0 );
2078  break;
2080  aField.SetTextAngle( 900 );
2081  break;
2083  aField.SetTextAngle( 1800 );
2084  break;
2086  aField.SetTextAngle( 2700 );
2087  break;
2088  }
2089 }
2090 
2091 
2092 void SCH_ALTIUM_PLUGIN::ParseSheetName( const std::map<wxString, wxString>& aProperties )
2093 {
2094  ASCH_SHEET_NAME elem( aProperties );
2095 
2096  const auto& sheet = m_sheets.find( elem.ownerindex );
2097  if( sheet == m_sheets.end() )
2098  {
2099  wxLogError( wxString::Format( "Sheet Name has non-existent ownerindex %d",
2100  elem.ownerindex ) );
2101  return;
2102  }
2103 
2104  SCH_FIELD& sheetNameField = sheet->second->GetFields()[SHEETNAME];
2105 
2106  sheetNameField.SetPosition( elem.location + m_sheetOffset );
2107  sheetNameField.SetText( elem.text );
2108  sheetNameField.SetVisible( !elem.isHidden );
2109 
2112 
2113  SetFieldOrientation( sheetNameField, elem.orientation );
2114 }
2115 
2116 
2117 void SCH_ALTIUM_PLUGIN::ParseFileName( const std::map<wxString, wxString>& aProperties )
2118 {
2119  ASCH_FILE_NAME elem( aProperties );
2120 
2121  const auto& sheet = m_sheets.find( elem.ownerindex );
2122  if( sheet == m_sheets.end() )
2123  {
2124  wxLogError( wxString::Format( "File Name has non-existent ownerindex %d",
2125  elem.ownerindex ) );
2126  return;
2127  }
2128 
2129  SCH_FIELD& filenameField = sheet->second->GetFields()[SHEETFILENAME];
2130 
2131  filenameField.SetPosition( elem.location + m_sheetOffset );
2132 
2133  // If last symbols are ".sChDoC", change them to ".kicad_sch"
2134  if( ( elem.text.Right( GetFileExtension().length() + 1 ).Lower() ) == ( "." + GetFileExtension().Lower() ))
2135  {
2136  elem.text.RemoveLast( GetFileExtension().length() );
2138  }
2139 
2140  filenameField.SetText( elem.text );
2141 
2142  filenameField.SetVisible( !elem.isHidden );
2143 
2146 
2147  SetFieldOrientation( filenameField, elem.orientation );
2148 }
2149 
2150 
2151 void SCH_ALTIUM_PLUGIN::ParseDesignator( const std::map<wxString, wxString>& aProperties )
2152 {
2153  ASCH_DESIGNATOR elem( aProperties );
2154 
2155  const auto& symbol = m_symbols.find( elem.ownerindex );
2156  if( symbol == m_symbols.end() )
2157  {
2158  // TODO: e.g. can depend on Template (RECORD=39
2159  m_reporter->Report( wxString::Format( _( "Designator has non-existent ownerindex %d." ),
2160  elem.ownerindex ),
2162  return;
2163  }
2164 
2165  const auto& component = m_components.at( symbol->first );
2166 
2167  SCH_SHEET_PATH sheetpath;
2169 
2170  component->SetRef( &sheetpath, elem.text );
2171 
2172  SCH_FIELD* refField = component->GetField( REFERENCE_FIELD );
2173 
2174  refField->SetPosition( elem.location + m_sheetOffset );
2175  refField->SetVisible( true );
2176 
2179 
2180  SetFieldOrientation( *refField, elem.orientation );
2181 }
2182 
2183 
2184 void SCH_ALTIUM_PLUGIN::ParseBusEntry( const std::map<wxString, wxString>& aProperties )
2185 {
2186  ASCH_BUS_ENTRY elem( aProperties );
2187 
2188  SCH_BUS_WIRE_ENTRY* busWireEntry = new SCH_BUS_WIRE_ENTRY( elem.location + m_sheetOffset );
2189 
2190  wxPoint vector = elem.corner - elem.location;
2191  busWireEntry->SetSize( { vector.x, vector.y } );
2192 
2193  busWireEntry->SetFlags( IS_NEW );
2194  m_currentSheet->GetScreen()->Append( busWireEntry );
2195 }
2196 
2197 
2198 void SCH_ALTIUM_PLUGIN::ParseParameter( const std::map<wxString, wxString>& aProperties )
2199 {
2200  ASCH_PARAMETER elem( aProperties );
2201 
2202  // TODO: fill in replacements from variant, sheet and project
2203  altium_override_map_t stringReplacement = {
2204  { "Comment", "${VALUE}" },
2205  { "Value", "${Altium_Value}" },
2206  };
2207 
2208  if( elem.ownerindex <= 0 && elem.ownerpartid == ALTIUM_COMPONENT_NONE )
2209  {
2210  // This is some sheet parameter
2211  if( elem.text == "*" )
2212  return; // indicates parameter not set?
2213 
2214  SCH_SHEET_PATH sheetpath;
2216 
2217  if( elem.name == "SheetNumber" )
2218  m_rootSheet->SetPageNumber( sheetpath, elem.text );
2219  else if( elem.name == "Title" )
2220  m_currentTitleBlock->SetTitle( elem.text );
2221  else if( elem.name == "Revision" )
2222  m_currentTitleBlock->SetRevision( elem.text );
2223  else if( elem.name == "Date" )
2224  m_currentTitleBlock->SetDate( elem.text );
2225  else if( elem.name == "CompanyName" )
2226  m_currentTitleBlock->SetCompany( elem.text );
2227  // TODO: parse other parameters
2228  // TODO: handle parameters in labels
2229  }
2230  else
2231  {
2232  const auto& symbol = m_symbols.find( elem.ownerindex );
2233  if( symbol == m_symbols.end() )
2234  {
2235  // TODO: e.g. can depend on Template (RECORD=39
2236  return;
2237  }
2238 
2239  const auto& component = m_components.at( symbol->first );
2240 
2241  // TODO: location not correct?
2242  const wxPoint position = elem.location + m_sheetOffset;
2243 
2244  SCH_FIELD* field = nullptr;
2245  if( elem.name == "Comment" )
2246  {
2247  field = component->GetField( VALUE_FIELD );
2248  field->SetPosition( position );
2249  }
2250  else
2251  {
2252  int fieldIdx = component->GetFieldCount();
2253  wxString fieldName = elem.name.IsSameAs( "Value", false ) ? "Altium_Value" : elem.name;
2254  field = component->AddField( { position, fieldIdx, component, fieldName } );
2255  }
2256 
2257  wxString kicadText = AltiumSpecialStringsToKiCadVariables( elem.text, stringReplacement );
2258  field->SetText( kicadText );
2259 
2260  field->SetVisible( !elem.isHidden );
2262 
2263  switch( elem.orientation )
2264  {
2265  case ASCH_RECORD_ORIENTATION::RIGHTWARDS: field->SetTextAngle( 0 ); break;
2266  case ASCH_RECORD_ORIENTATION::UPWARDS: field->SetTextAngle( 90 ); break;
2267  case ASCH_RECORD_ORIENTATION::LEFTWARDS: field->SetTextAngle( 180 ); break;
2268  case ASCH_RECORD_ORIENTATION::DOWNWARDS: field->SetTextAngle( 270 ); break;
2269  default:
2270  break;
2271  }
2272  }
2273 }
Field Reference of part, i.e. "IC21".
void ParsePort(const ASCH_PORT &aElem)
wxPoint location
power input (GND, VCC for ICs). Must be connected to a power output.
static const wxString & GetSymbolLibTableFileName()
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
std::unique_ptr< PROPERTIES > m_properties
void SetModified()
Definition: eda_item.cpp:65
static REPORTER & GetInstance()
Definition: reporter.cpp:166
ASCH_RECORD_ORIENTATION orientation
void SetShape(PINSHEETLABEL_SHAPE aShape)
Definition: sch_text.h:163
static int PropertiesReadInt(const std::map< wxString, wxString > &aProperties, const wxString &aKey, int aDefault)
void ParseLabel(const std::map< wxString, wxString > &aProperties)
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib=false)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition: lib_id.cpp:347
Hold a record identifying a symbol library accessed by the appropriate symbol library SCH_PLUGIN obje...
bool HasLibrary(const wxString &aNickname, bool aCheckEnabled=false) const
Test for the existence of aNickname in the library table.
void ParseBezier(const std::map< wxString, wxString > &aProperties)
std::vector< char > data
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:495
void SetEdaTextJustification(EDA_TEXT *text, ASCH_LABEL_JUSTIFICATION justification)
int distanceFromTop
std::map< int, ASCH_SYMBOL > m_altiumComponents
const wxPoint GetRelativePosition(const wxPoint &aPosition, const SCH_SYMBOL *aSymbol)
std::vector< ASCH_STORAGE_FILE > m_altiumStorage
void SetPosition(const wxPoint &aPosition) override
Definition: sch_symbol.h:642
void ParsePowerPort(const std::map< wxString, wxString > &aProperties)
void SetOrientation(int aOrientation)
Compute the new transform matrix based on aOrientation for the symbol which is applied to the current...
void ParseJunction(const std::map< wxString, wxString > &aProperties)
SCH_FIELD * GetField(MANDATORY_FIELD_T aFieldType)
Return a mandatory field in this symbol.
Definition: sch_symbol.cpp:665
void SetUnit(int aUnit)
Change the unit number to aUnit.
Definition: sch_symbol.cpp:321
Holds all the data relating to one schematic.
Definition: schematic.h:59
void SetSize(const wxSize &aSize)
Definition: sch_bus_entry.h:64
#define IS_NEW
New item, just created.
wxPoint GetPosition() const override
Definition: lib_field.h:157
bool InsertRow(LIB_TABLE_ROW *aRow, bool doReplace=false)
Adds aRow if it does not already exist or if doReplace is true.
Define a symbol library graphical text item.
Definition: lib_text.h:39
bool IsTerminalPoint(const wxPoint &aPosition, int aLayer) const
Test if aPosition is a connection point on aLayer.
Definition: sch_screen.cpp:473
ASCH_PORT_IOTYPE iotype
void GetPoly(std::vector< wxPoint > &aOutput, int aMinSegLen=0)
Convert a Bezier curve to a polygon.
void SetLineWidth(const int aSize)
Definition: sch_line.cpp:271
void ParseParameter(const std::map< wxString, wxString > &aProperties)
size_t GetRemainingBytes() const
SCH_SHEET * Load(const wxString &aFileName, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe=nullptr, const PROPERTIES *aProperties=nullptr) override
Load information from some input file format that this SCH_PLUGIN implementation knows about,...
void ParseRoundRectangle(const std::map< wxString, wxString > &aProperties)
std::vector< wxPoint > points
int color
Definition: DXF_plotter.cpp:60
void SetFillMode(FILL_TYPE aFillMode)
Definition: lib_item.h:264
void SetValue(const SCH_SHEET_PATH *sheet, const wxString &aValue)
Definition: sch_symbol.cpp:584
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Definition: sch_sheet.cpp:157
pin for passive symbols: must be connected, and can be connected to any pin
bool IsValid() const
A simple test if the schematic is loaded, not a complete one.
Definition: schematic.h:108
TRANSFORM InverseTransform() const
Calculate the Inverse mirror/rotation transform.
Definition: transform.cpp:59
const wxString GetName() const override
Return a brief hard coded name for this SCH_PLUGIN.
void SetVisible(bool aVisible)
Definition: eda_text.h:192
ASCH_LABEL_JUSTIFICATION
void SetFirstRadiusAngle(int aAngle)
Definition: lib_arc.h:86
bool SetType(const wxString &aStandardPageDescriptionName, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
Definition: page_info.cpp:119
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:153
ASCH_LABEL_JUSTIFICATION justification
int ownerpartdisplaymode
wxString filename
std::map< wxString, LIB_SYMBOL * > m_powerSymbols
void ParseBusEntry(const std::map< wxString, wxString > &aProperties)
unknown electrical properties: creates always a warning when connected
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: sch_screen.h:125
void ParseSheet(const std::map< wxString, wxString > &aProperties)
ASCH_POWER_PORT_STYLE style
void ParsePolygon(const std::map< wxString, wxString > &aProperties)
void set(SCH_PLUGIN *aPlugin)
Definition: sch_io_mgr.h:501
ASCH_PORT_STYLE style
void SetLibId(const LIB_ID &aName)
Definition: sch_symbol.cpp:228
Definition: lib_pin.h:48
LIB_FIELD & GetValueField()
Return reference to the value field.
Definition: lib_symbol.cpp:979
wxPoint corner
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:64
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
A name/value tuple with unique names and optional values.
Definition: properties.h:33
LIB_ID AltiumToKiCadLibID(wxString aLibName, wxString aLibReference)
void SetWidth(int aWidth) override
Definition: lib_polyline.h:97
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:103
Define a library symbol object.
Definition: lib_symbol.h:96
TRANSFORM & GetTransform()
Definition: sch_symbol.h:231
void SetLibSymbol(LIB_SYMBOL *aLibSymbol)
Set this schematic symbol library symbol reference to aLibSymbol.
Definition: sch_symbol.cpp:247
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
virtual const wxString GetProjectPath() const
Return the full path of the project.
Definition: project.cpp:123
LIB_FIELD & GetReferenceField()
Return reference to the reference designator field.
Definition: lib_symbol.cpp:987
void SetPageNumber(const SCH_SHEET_PATH &aInstance, const wxString &aPageNumber)
Set the page number for the sheet instance aInstance.
Definition: sch_sheet.cpp:1136
wxPoint TransformCoordinate(const wxPoint &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition: transform.cpp:42
std::map< int, SCH_SHEET * > m_sheets
COLOR4D GetColorFromInt(int color)
void SetEndPoint(const wxPoint &aPosition)
Definition: sch_line.h:94
void ParseRectangle(const std::map< wxString, wxString > &aProperties)
void ParsePolyline(const std::map< wxString, wxString > &aProperties)
virtual void SetElem(ELEM_T aIndex, _ELEM *aElem)
Definition: project.cpp:259
ASCH_RECORD_ORIENTATION orientation
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:119
std::map< wxString, wxString, CASE_INSENSITIVE_COMPARATOR > altium_override_map_t
const CFB::COMPOUND_FILE_ENTRY * FindStream(const CFB::CompoundFileReader &aReader, const char *aStreamName)
void ParseImage(const std::map< wxString, wxString > &aProperties)
std::vector< ASCH_PORT > m_altiumPortsCurrentSheet
wxPoint location
ASCH_RECORD_ORIENTATION orientation
void SetLineStyle(const PLOT_DASH_TYPE aStyle)
Definition: sch_line.cpp:239
ASCH_RECORD_ORIENTATION orientation
wxString libreference
std::map< wxString, wxString > ReadProperties()
for transforming drawing coordinates for a wxDC device context.
Definition: transform.h:45
void SetFileName(wxString aFilename)
Definition: sch_sheet.h:321
int ownerindex
void SetBackgroundColor(KIGFX::COLOR4D aColor)
Definition: sch_sheet.h:115
Field Value of part, i.e. "3.3K".
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
void ParseLine(const std::map< wxString, wxString > &aProperties)
#define NULL
void SetWidth(int aWidth) override
Definition: lib_rectangle.h:79
void SetFieldOrientation(SCH_FIELD &aField, ASCH_RECORD_ORIENTATION aOrientation)
ASCH_RECORD_ORIENTATION
wxFileName getLibFileName()
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:53
virtual void SetName(const wxString &aName)
Definition: lib_symbol.cpp:314
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:209
std::unique_ptr< ASCH_SHEET > m_altiumSheet
void ParseSheetEntry(const std::map< wxString, wxString > &aProperties)
void SetEdge(SHEET_SIDE aEdge)
std::vector< wxPoint > points
void SetDescription(const wxString &aDescription)
Definition: lib_symbol.h:140
void SetRadius(int aRadius)
Definition: lib_circle.h:81
void SetBorderColor(KIGFX::COLOR4D aColor)
Definition: sch_sheet.h:112
Definition of file extensions used in Kicad.
void SetLabelSpinStyle(LABEL_SPIN_STYLE aSpinStyle) override
Set a spin or rotation angle, along with specific horizontal and vertical justification styles with e...
Definition: sch_text.cpp:1471
#define _(s)
int GetModifyHash() const override
Return the modification hash from the library cache.
void AddDrawItem(LIB_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
Definition: lib_symbol.cpp:655
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Definition: sch_sheet_pin.h:65
int ownerpartdisplaymode
ASCH_RECORD_ORIENTATION orientation
void ParseFileName(const std::map< wxString, wxString > &aProperties)
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
virtual void Format(OUTPUTFORMATTER *aOutput, int aIndentLevel) const override
Generate the table in s-expression format to aOutput with an indention level of aIndentLevel.
void SetLibId(const LIB_ID &aLibId)
Definition: lib_symbol.h:136
wxString GetFileName() const
Return the filename corresponding to this sheet.
Definition: sch_sheet.h:315
void AddPoint(const wxPoint &aPoint)
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition: sch_screen.h:144
void ParseNoERC(const std::map< wxString, wxString > &aProperties)
wxString name
const ASCH_STORAGE_FILE * GetFileFromStorage(const wxString &aFilename) const
std::vector< wxPoint > points
void SetSecondRadiusAngle(int aAngle)
Definition: lib_arc.h:89
SCH_SHEET * m_currentSheet
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:54
void SetSize(const wxSize &aSize)
Definition: sch_sheet.h:106
wxPoint HelperGeneratePowerPortGraphics(LIB_SYMBOL *aKsymbol, ASCH_POWER_PORT_STYLE aStyle, REPORTER *aReporter)
PLOT_DASH_TYPE
Dashed line types.
Definition: plotter.h:104
PROJECT & Prj() const override
Return a reference to the project this schematic is part of.
Definition: schematic.h:75
wxString designator
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
ASCH_PORT_IOTYPE iotype
const wxString GetLibraryFileExtension() const override
Return the library file extension for the SCH_PLUGIN object.
void SetPosition(const wxPoint &aPosition) override
void SetUnit(int aUnit)
Definition: lib_item.h:258
bool LocatePathOfScreen(SCH_SCREEN *aScreen, SCH_SHEET_PATH *aList)
Search the existing hierarchy for an instance of screen loaded from aFileName.
Definition: sch_sheet.cpp:666
wxString AltiumSpecialStringsToKiCadVariables(const wxString &aString, const altium_override_map_t &aOverride)
void ParseComponent(int aIndex, const std::map< wxString, wxString > &aProperties)
Bezier curves to polygon converter.
Definition: bezier_curves.h:36
SCH_SHEET & Root() const
Definition: schematic.h:92
void ParseFileHeader(const CFB::CompoundFileReader &aReader)
void SetEnd(const wxPoint &aEnd)
Definition: lib_rectangle.h:81
void ParseSheetSymbol(int aIndex, const std::map< wxString, wxString > &aProperties)
Schematic symbol object.
Definition: sch_symbol.h:78
const char * name
Definition: DXF_plotter.cpp:59
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
std::map< int, SCH_SYMBOL * > m_components
void Append(SCH_ITEM *aItem)
Definition: sch_screen.cpp:144
ASCH_SHEET_ENTRY_SIDE side
ASCH_RECORD_ORIENTATION orientation
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:208
double startAngle
ASCH_PIN_SYMBOL_OUTEREDGE symbolOuterEdge
usual pin input: must be connected
void ParseWire(const std::map< wxString, wxString > &aProperties)
void ParseSheetName(const std::map< wxString, wxString > &aProperties)
void SetPower()
Definition: lib_symbol.cpp:420
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
Definition: sch_symbol.cpp:471
const wxString GetFileExtension() const override
Return the file extension for the SCH_PLUGIN.
void SetRadius(int aRadius)
Definition: lib_arc.h:83
const std::string KiCadSchematicFileExtension
void ParseArc(const std::map< wxString, wxString > &aProperties)
wxPoint GetPosition() const override
Definition: sch_symbol.h:641
Used for text file output.
Definition: richio.h:453
ASCH_POLYLINE_LINESTYLE linestyle
void ParseBus(const std::map< wxString, wxString > &aProperties)
Class for a wire to bus entry.
void SetWidth(int aWidth) override
Definition: lib_circle.h:79
void SetPosition(const wxPoint &aPosition) override
Definition: lib_item.h:212
ASCH_RECORD_ORIENTATION orientation
wxPoint location
virtual const wxString GetProjectName() const
Return the short name of the project.
Definition: project.cpp:129
input or output (like port for a microprocessor)
const int ALTIUM_COMPONENT_NONE
std::vector< wxPoint > points
void ParseAltiumSch(const wxString &aFileName)
ASCH_POWER_PORT_STYLE
virtual void SetTextAngle(double aAngle)
Definition: eda_text.h:174
int ownerpartdisplaymode
void ParseDesignator(const std::map< wxString, wxString > &aProperties)
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
Definition: sch_screen.cpp:108
virtual void SetLabelSpinStyle(LABEL_SPIN_STYLE aSpinStyle)
Set a spin or rotation angle, along with specific horizontal and vertical justification styles with e...
Definition: sch_text.cpp:317
std::map< int, LIB_SYMBOL * > m_symbols
ASCH_PIN_ELECTRICAL electrical
bool CheckHeader(const wxString &aFileName) override
Return true if the first line in aFileName begins with the expected header.
void ParsePin(const std::map< wxString, wxString > &aProperties)
wxPoint center
void SetWidth(int aWidth) override
Definition: lib_bezier.h:83
void UpdateSymbolLinks(REPORTER *aReporter=nullptr)
Initialize the LIB_SYMBOL reference for each SCH_SYMBOL found in the full schematic.
SCH_PLUGIN::SCH_PLUGIN_RELEASER m_pi
static wxString PropertiesReadString(const std::map< wxString, wxString > &aProperties, const wxString &aKey, const wxString &aDefault)
void ParseStorage(const CFB::CompoundFileReader &aReader)
ASCH_PIN_SYMBOL_INNEREDGE symbolInnerEdge
void ParseNetLabel(const std::map< wxString, wxString > &aProperties)
void SetPosition(const wxPoint &aPosition) override
Definition: sch_field.cpp:693
ALTIUM_SCH_RECORD
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
bool HasParsingError()
void AddPoint(const wxPoint &aPoint)
Definition: lib_bezier.h:54
bool IsComponentPartVisible(int aOwnerindex, int aOwnerpartdisplaymode) const
wxString componentdescription
void SetKeyWords(const wxString &aKeyWords)
Definition: lib_symbol.h:153
std::unique_ptr< TITLE_BLOCK > m_currentTitleBlock
Container class that holds multiple SCH_SCREEN objects in a hierarchy.
Definition: sch_screen.h:551
Define a bezier curve graphic body item.
Definition: lib_bezier.h:34
std::vector< wxPoint > points
const std::string KiCadSymbolLibFileExtension
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
wxString name