KiCad PCB EDA Suite
altium_pcb.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) 2019-2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include "altium_pcb.h"
25 #include "altium_parser_pcb.h"
27 
28 #include <class_board.h>
29 #include <class_dimension.h>
30 #include <class_drawsegment.h>
31 #include <class_pcb_text.h>
32 #include <class_track.h>
33 
34 #include <class_edge_mod.h>
35 #include <class_text_mod.h>
36 
38 
39 #include <compoundfilereader.h>
41 #include <project.h>
42 #include <trigo.h>
43 #include <utf.h>
44 #include <wx/docview.h>
45 #include <wx/mstream.h>
46 #include <wx/wfstream.h>
47 #include <wx/zstream.h>
48 
49 
50 void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName,
51  const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
52 {
53  // Open file
54  FILE* fp = wxFopen( aFileName, "rb" );
55  if( fp == nullptr )
56  {
57  wxLogError( wxString::Format( _( "Cannot open file '%s'" ), aFileName ) );
58  return;
59  }
60 
61  fseek( fp, 0, SEEK_END );
62  long len = ftell( fp );
63  if( len < 0 )
64  {
65  fclose( fp );
66  THROW_IO_ERROR( "Reading error, cannot determine length of file" );
67  }
68 
69  std::unique_ptr<unsigned char[]> buffer( new unsigned char[len] );
70  fseek( fp, 0, SEEK_SET );
71 
72  size_t bytesRead = fread( buffer.get(), sizeof( unsigned char ), len, fp );
73  fclose( fp );
74  if( static_cast<size_t>( len ) != bytesRead )
75  {
76  THROW_IO_ERROR( "Reading error" );
77  }
78 
79  try
80  {
81  CFB::CompoundFileReader reader( buffer.get(), bytesRead );
82 
83  // Parse File
84  ALTIUM_PCB pcb( aBoard );
85  pcb.Parse( reader, aFileMapping );
86  }
87  catch( CFB::CFBException& exception )
88  {
89  THROW_IO_ERROR( exception.what() );
90  }
91 }
92 
93 
95 {
96  return aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER;
97 }
98 
99 
101 {
103 }
104 
105 
107 {
108  if( aComponent == ALTIUM_COMPONENT_NONE )
109  {
110  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
111  m_board->Add( ds, ADD_MODE::APPEND );
112  return ds;
113  }
114  else
115  {
116  if( m_components.size() <= aComponent )
117  {
119  "Component creator tries to access component id %d of %d existing components",
120  aComponent, m_components.size() ) );
121  }
122  MODULE* module = m_components.at( aComponent );
123  DRAWSEGMENT* ds = new EDGE_MODULE( module );
124  module->Add( ds, ADD_MODE::APPEND );
125  return ds;
126  }
127 }
128 
129 
131 {
132  if( aComponent != ALTIUM_COMPONENT_NONE )
133  {
134  auto em = dynamic_cast<EDGE_MODULE*>( aDs );
135 
136  if( em )
137  em->SetLocalCoord();
138  }
139 }
140 
141 
143 {
144  auto override = m_layermap.find( aAltiumLayer );
145  if( override != m_layermap.end() )
146  {
147  return override->second;
148  }
149 
150  switch( aAltiumLayer )
151  {
153  return UNDEFINED_LAYER;
154 
156  return F_Cu;
158  return In1_Cu; // TODO: stackup same as in KiCad?
160  return In2_Cu;
162  return In3_Cu;
164  return In4_Cu;
166  return In5_Cu;
168  return In6_Cu;
170  return In7_Cu;
172  return In8_Cu;
174  return In9_Cu;
176  return In10_Cu;
178  return In11_Cu;
180  return In12_Cu;
182  return In13_Cu;
184  return In14_Cu;
186  return In15_Cu;
188  return In16_Cu;
190  return In17_Cu;
192  return In18_Cu;
194  return In19_Cu;
196  return In20_Cu;
198  return In21_Cu;
200  return In22_Cu;
202  return In23_Cu;
204  return In24_Cu;
206  return In25_Cu;
208  return In26_Cu;
210  return In27_Cu;
212  return In28_Cu;
214  return In29_Cu;
216  return In30_Cu;
218  return B_Cu;
219 
221  return F_SilkS;
223  return B_SilkS;
225  return F_Paste;
227  return B_Paste;
229  return F_Mask;
231  return B_Mask;
232 
234  return UNDEFINED_LAYER;
236  return UNDEFINED_LAYER;
238  return UNDEFINED_LAYER;
240  return UNDEFINED_LAYER;
242  return UNDEFINED_LAYER;
244  return UNDEFINED_LAYER;
246  return UNDEFINED_LAYER;
248  return UNDEFINED_LAYER;
250  return UNDEFINED_LAYER;
252  return UNDEFINED_LAYER;
254  return UNDEFINED_LAYER;
256  return UNDEFINED_LAYER;
258  return UNDEFINED_LAYER;
260  return UNDEFINED_LAYER;
262  return UNDEFINED_LAYER;
264  return UNDEFINED_LAYER;
265 
267  return Dwgs_User;
269  return Margin;
270 
272  return Dwgs_User; //Edge_Cuts;
274  return Dwgs_User;
276  return Dwgs_User;
278  return Dwgs_User;
280  return Dwgs_User;
282  return Dwgs_User;
284  return Dwgs_User;
286  return Dwgs_User;
288  return Dwgs_User;
290  return Dwgs_User;
292  return Dwgs_User;
294  return Dwgs_User;
296  return F_Fab;
298  return B_Fab;
300  return F_CrtYd;
302  return B_CrtYd;
303 
305  return Dwgs_User;
307  return UNDEFINED_LAYER;
309  return UNDEFINED_LAYER;
311  return UNDEFINED_LAYER;
313  return UNDEFINED_LAYER;
315  return UNDEFINED_LAYER;
317  return UNDEFINED_LAYER;
319  return UNDEFINED_LAYER;
321  return UNDEFINED_LAYER;
323  return UNDEFINED_LAYER;
324 
325  default:
326  return UNDEFINED_LAYER;
327  }
328 }
329 
330 
332 {
333  m_board = aBoard;
334  m_num_nets = 0;
336 }
337 
339 {
340 }
341 
342 void ALTIUM_PCB::Parse( const CFB::CompoundFileReader& aReader,
343  const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
344 {
345  // this vector simply declares in which order which functions to call.
346  const std::vector<std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>> parserOrder = {
348  [this]( auto aReader, auto fileHeader ) {
349  this->ParseFileHeader( aReader, fileHeader );
350  } },
351  { true, ALTIUM_PCB_DIR::BOARD6,
352  [this]( auto aReader, auto fileHeader ) {
353  this->ParseBoard6Data( aReader, fileHeader );
354  } },
356  [this]( auto aReader, auto fileHeader ) {
357  this->ParseComponents6Data( aReader, fileHeader );
358  } },
359  { true, ALTIUM_PCB_DIR::MODELS,
360  [this, aFileMapping]( auto aReader, auto fileHeader ) {
361  wxString dir( aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) );
362  dir.RemoveLast( 4 ); // Remove "Data" from the path
363  this->ParseModelsData( aReader, fileHeader, dir );
364  } },
366  [this]( auto aReader, auto fileHeader ) {
367  this->ParseComponentsBodies6Data( aReader, fileHeader );
368  } },
369  { true, ALTIUM_PCB_DIR::NETS6,
370  [this]( auto aReader, auto fileHeader ) {
371  this->ParseNets6Data( aReader, fileHeader );
372  } },
373  { true, ALTIUM_PCB_DIR::CLASSES6,
374  [this]( auto aReader, auto fileHeader ) {
375  this->ParseClasses6Data( aReader, fileHeader );
376  } },
377  { true, ALTIUM_PCB_DIR::RULES6,
378  [this]( auto aReader, auto fileHeader ) {
379  this->ParseRules6Data( aReader, fileHeader );
380  } },
382  [this]( auto aReader, auto fileHeader ) {
383  this->ParseDimensions6Data( aReader, fileHeader );
384  } },
386  [this]( auto aReader, auto fileHeader ) {
387  this->ParsePolygons6Data( aReader, fileHeader );
388  } },
389  { true, ALTIUM_PCB_DIR::ARCS6,
390  [this]( auto aReader, auto fileHeader ) {
391  this->ParseArcs6Data( aReader, fileHeader );
392  } },
393  { true, ALTIUM_PCB_DIR::PADS6,
394  [this]( auto aReader, auto fileHeader ) {
395  this->ParsePads6Data( aReader, fileHeader );
396  } },
397  { true, ALTIUM_PCB_DIR::VIAS6,
398  [this]( auto aReader, auto fileHeader ) {
399  this->ParseVias6Data( aReader, fileHeader );
400  } },
401  { true, ALTIUM_PCB_DIR::TRACKS6,
402  [this]( auto aReader, auto fileHeader ) {
403  this->ParseTracks6Data( aReader, fileHeader );
404  } },
405  { true, ALTIUM_PCB_DIR::TEXTS6,
406  [this]( auto aReader, auto fileHeader ) {
407  this->ParseTexts6Data( aReader, fileHeader );
408  } },
409  { true, ALTIUM_PCB_DIR::FILLS6,
410  [this]( auto aReader, auto fileHeader ) {
411  this->ParseFills6Data( aReader, fileHeader );
412  } },
414  [this]( auto aReader, auto fileHeader ) {
415  this->ParseBoardRegionsData( aReader, fileHeader );
416  } },
418  [this]( auto aReader, auto fileHeader ) {
419  this->ParseShapeBasedRegions6Data( aReader, fileHeader );
420  } },
421  { true, ALTIUM_PCB_DIR::REGIONS6,
422  [this]( auto aReader, auto fileHeader ) {
423  this->ParseRegions6Data( aReader, fileHeader );
424  } }
425  };
426 
427  // Parse data in specified order
428  for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
429  {
430  bool isRequired;
431  ALTIUM_PCB_DIR directory;
433  std::tie( isRequired, directory, fp ) = cur;
434 
435  const auto& mappedDirectory = aFileMapping.find( directory );
436  if( mappedDirectory == aFileMapping.end() )
437  {
438  wxASSERT_MSG( !isRequired,
440  "Altium Directory of kind %d was expected, but no mapping is present in the code",
441  directory ) );
442  continue;
443  }
444 
445  const CFB::COMPOUND_FILE_ENTRY* file =
446  FindStream( aReader, mappedDirectory->second.c_str() );
447  if( file != nullptr )
448  {
449  fp( aReader, file );
450  }
451  else if( isRequired )
452  {
453  wxLogError( wxString::Format( _( "File not found: '%s'" ), mappedDirectory->second ) );
454  }
455  }
456 
457  // fixup zone priorities since Altium stores them in the opposite order
458  for( auto& zone : m_polygons )
459  {
460  if( !zone )
461  continue;
462 
463  // Altium "fills" - not poured in Altium
464  if( zone->GetPriority() == 1000 )
465  {
466  // Unlikely, but you never know
467  if( m_highest_pour_index >= 1000 )
468  zone->SetPriority( m_highest_pour_index + 1 );
469 
470  continue;
471  }
472 
473  int priority = m_highest_pour_index - zone->GetPriority();
474 
475  zone->SetPriority( priority >= 0 ? priority : 0 );
476  }
477 
478  // change priority of outer zone to zero
479  for( auto& zone : m_outer_plane )
480  {
481  zone.second->SetPriority( 0 );
482  }
483 
484  // Finish Board by recalculating module boundingboxes
485  for( auto& module : m_board->Modules() )
486  {
487  module->CalculateBoundingBox();
488  }
489 
490  // Otherwise we cannot save the imported board
491  m_board->SetModified();
492 }
493 
494 int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
495 {
496  if( aId == ALTIUM_NET_UNCONNECTED )
497  {
499  }
500  else if( m_num_nets < aId )
501  {
503  "Netcode with id %d does not exist. Only %d nets are known", aId, m_num_nets ) );
504  }
505  else
506  {
507  return aId + 1;
508  }
509 }
510 
511 const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
512 {
513  const auto rules = m_rules.find( aKind );
514  if( rules == m_rules.end() )
515  {
516  return nullptr;
517  }
518  for( const ARULE6& rule : rules->second )
519  {
520  if( rule.name == aName )
521  {
522  return &rule;
523  }
524  }
525  return nullptr;
526 }
527 
529 {
530  const auto rules = m_rules.find( aKind );
531  if( rules == m_rules.end() )
532  {
533  return nullptr;
534  }
535  for( const ARULE6& rule : rules->second )
536  {
537  if( rule.scope1expr == "All" && rule.scope2expr == "All" )
538  {
539  return &rule;
540  }
541  }
542  return nullptr;
543 }
544 
546  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
547 {
548  ALTIUM_PARSER reader( aReader, aEntry );
549 
550  reader.ReadAndSetSubrecordLength();
551  wxString header = reader.ReadWxString();
552 
553  //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
554 
555  //reader.SkipSubrecord();
556 
557  // TODO: does not seem to work all the time at the moment
558  //if( reader.GetRemainingBytes() != 0 )
559  //{
560  // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
561  //}
562 }
563 
565  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
566 {
567  ALTIUM_PARSER reader( aReader, aEntry );
568 
569  ABOARD6 elem( reader );
570 
571  if( reader.GetRemainingBytes() != 0 )
572  {
573  THROW_IO_ERROR( "Board6 stream is not fully parsed" );
574  }
575 
578 
579  // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
580  size_t layercount = 0;
581  for( size_t i = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
582  i < elem.stackup.size() && i != 0; i = elem.stackup[i - 1].nextId, layercount++ )
583  ;
584  size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
585  m_board->SetCopperLayerCount( kicadLayercount );
586 
587  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
588  BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
589 
590  // create board stackup
591  stackup.RemoveAll(); // Just to be sure
592  stackup.BuildDefaultStackupList( &designSettings, layercount );
593 
594  auto it = stackup.GetList().begin();
595  // find first copper layer
596  for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
597  ;
598 
599  auto curLayer = static_cast<int>( F_Cu );
600  for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
601  altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
602  altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
603  {
604  // array starts with 0, but stackup with 1
605  ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
606 
607  // handle unused layer in case of odd layercount
608  if( layer.nextId == 0 && layercount != kicadLayercount )
609  {
610  m_board->SetLayerName( ( *it )->GetBrdLayerId(), "[unused]" );
611 
612  if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
613  {
614  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
615  }
616  ( *it )->SetThickness( 0 );
617 
618  ++it;
619  if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
620  {
621  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
622  }
623  ( *it )->SetThickness( 0, 0 );
624  ( *it )->SetThicknessLocked( true, 0 );
625  ++it;
626  }
627 
628  m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ),
629  static_cast<PCB_LAYER_ID>( curLayer++ ) } );
630 
631  if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
632  {
633  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
634  }
635  ( *it )->SetThickness( layer.copperthick );
636 
637  ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
638  PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
639 
640  m_board->SetLayerName( klayer, layer.name );
641 
642  if( layer.copperthick == 0 )
643  {
644  m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
645  }
646  else if( IsAltiumLayerAPlane( alayer ) )
647  {
649  }
650 
651  if( klayer == B_Cu )
652  {
653  if( layer.nextId != 0 )
654  {
655  THROW_IO_ERROR( "Board6 stream, unexpected id while parsing last stackup layer" );
656  }
657  // overwrite entry from internal -> bottom
658  m_layermap[alayer] = B_Cu;
659  break;
660  }
661 
662  ++it;
663  if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
664  {
665  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
666  }
667  ( *it )->SetThickness( layer.dielectricthick, 0 );
668  ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
669  NotSpecifiedPrm() :
670  wxString( layer.dielectricmaterial ) );
671  ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
672 
673  ++it;
674  }
675 
677 }
678 
679 void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
680 {
681  if( !aVertices.empty() )
682  {
683  const ALTIUM_VERTICE* last = &aVertices.at( 0 );
684  for( size_t i = 0; i < aVertices.size(); i++ )
685  {
686  const ALTIUM_VERTICE* cur = &aVertices.at( ( i + 1 ) % aVertices.size() );
687 
688  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
689  m_board->Add( ds, ADD_MODE::APPEND );
690 
692  ds->SetLayer( Edge_Cuts );
693 
694  if( !last->isRound && !cur->isRound )
695  {
697  ds->SetStart( last->position );
698  ds->SetEnd( cur->position );
699  }
700  else if( cur->isRound )
701  {
702  ds->SetShape( STROKE_T::S_ARC );
703  ds->SetAngle( -NormalizeAngleDegreesPos( cur->endangle - cur->startangle ) * 10. );
704 
705  double startradiant = DEG2RAD( cur->startangle );
706  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * cur->radius ),
707  -KiROUND( std::sin( startradiant ) * cur->radius ) );
708  wxPoint arcStart = cur->center + arcStartOffset;
709  ds->SetCenter( cur->center );
710  ds->SetArcStart( arcStart );
711 
712  if( !last->isRound )
713  {
714  double endradiant = DEG2RAD( cur->endangle );
715  wxPoint arcEndOffset = wxPoint( KiROUND( std::cos( endradiant ) * cur->radius ),
716  -KiROUND( std::sin( endradiant ) * cur->radius ) );
717  wxPoint arcEnd = cur->center + arcEndOffset;
718 
719  DRAWSEGMENT* ds2 = new DRAWSEGMENT( m_board );
721  m_board->Add( ds2, ADD_MODE::APPEND );
723  ds2->SetLayer( Edge_Cuts );
724  ds2->SetStart( last->position );
725 
726  // TODO: this is more of a hack than the real solution
727  double lineLengthStart = GetLineLength( last->position, arcStart );
728  double lineLengthEnd = GetLineLength( last->position, arcEnd );
729  if( lineLengthStart > lineLengthEnd )
730  {
731  ds2->SetEnd( cur->center + arcEndOffset );
732  }
733  else
734  {
735  ds2->SetEnd( cur->center + arcStartOffset );
736  }
737  }
738  }
739  last = cur;
740  }
741  }
742 }
743 
745  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
746 {
747  ALTIUM_PARSER reader( aReader, aEntry );
748 
749  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
750 
751  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
752  {
753  ACLASS6 elem( reader );
754 
755  if( elem.kind == ALTIUM_CLASS_KIND::NET_CLASS )
756  {
757  const NETCLASSPTR& netclass = std::make_shared<NETCLASS>( elem.name );
758  designSettings.GetNetClasses().Add( netclass );
759 
760  for( const auto& name : elem.names )
761  {
762  netclass->Add(
763  name ); // TODO: it seems it can happen that we have names not attached to any net.
764  }
765  }
766  }
767 
768  if( reader.GetRemainingBytes() != 0 )
769  {
770  THROW_IO_ERROR( "Classes6 stream is not fully parsed" );
771  }
772 }
773 
775  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
776 {
777  ALTIUM_PARSER reader( aReader, aEntry );
778 
779  uint16_t componentId = 0;
780  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
781  {
782  ACOMPONENT6 elem( reader );
783 
784  MODULE* module = new MODULE( m_board );
785  m_board->Add( module, ADD_MODE::APPEND );
786  m_components.emplace_back( module );
787 
788  wxString pack_ref = elem.sourcelibreference;
789  wxString lib_ref = elem.sourcefootprintlibrary; // TODO: remove ".PcbLib" part
790  ReplaceIllegalFileNameChars( lib_ref, '_' );
791  ReplaceIllegalFileNameChars( pack_ref, '_' );
792 
793  wxString key = !lib_ref.empty() ? lib_ref + ":" + pack_ref : pack_ref;
794 
795  LIB_ID fpID;
796  fpID.Parse( key, LIB_ID::ID_PCB, true );
797  module->SetFPID( fpID );
798 
799  module->SetPosition( elem.position );
800  module->SetOrientationDegrees( elem.rotation );
801 
802  // KiCad netlisting requires parts to have non-digit + digit annotation.
803  // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
804  wxString reference = elem.sourcedesignator;
805  if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
806  reference.Prepend( "UNK" );
807  module->SetReference( reference );
808 
809  module->SetLocked( elem.locked );
810  module->Reference().SetVisible( elem.nameon );
811  module->Value().SetVisible( elem.commenton );
812  module->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
813 
814  componentId++;
815  }
816 
817  if( reader.GetRemainingBytes() != 0 )
818  {
819  THROW_IO_ERROR( "Components6 stream is not fully parsed" );
820  }
821 }
822 
823 
825  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
826 {
827  ALTIUM_PARSER reader( aReader, aEntry );
828 
829  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
830  {
831  ACOMPONENTBODY6 elem( reader ); // TODO: implement
832 
833  if( elem.component == ALTIUM_COMPONENT_NONE )
834  {
835  continue; // TODO: we do not support components for the board yet
836  }
837 
838  if( m_components.size() <= elem.component )
839  {
841  "ComponentsBodies6 stream tries to access component id %d of %d existing components",
842  elem.component, m_components.size() ) );
843  }
844 
845  if( !elem.modelIsEmbedded )
846  {
847  continue;
848  }
849 
850  auto modelTuple = m_models.find( elem.modelId );
851  if( modelTuple == m_models.end() )
852  {
854  "ComponentsBodies6 stream tries to access model id %s which does not exist",
855  elem.modelId ) );
856  }
857 
858  MODULE* module = m_components.at( elem.component );
859  const wxPoint& modulePosition = module->GetPosition();
860 
861  MODULE_3D_SETTINGS modelSettings;
862 
863  modelSettings.m_Filename = modelTuple->second;
864 
865  modelSettings.m_Offset.x = Iu2Millimeter( (int) elem.modelPosition.x - modulePosition.x );
866  modelSettings.m_Offset.y = -Iu2Millimeter( (int) elem.modelPosition.y - modulePosition.y );
867  modelSettings.m_Offset.z = Iu2Millimeter( (int) elem.modelPosition.z );
868 
869  double orientation = module->GetOrientation();
870 
871  if( module->IsFlipped() )
872  {
873  modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
874  orientation = -orientation;
875  }
876 
877  RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
878 
879  modelSettings.m_Rotation.x = NormalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
880  modelSettings.m_Rotation.y = NormalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
881  modelSettings.m_Rotation.z = NormalizeAngleDegrees(
882  -elem.modelRotation.z + elem.rotation + orientation / 10, -180, 180 );
883 
884  modelSettings.m_Opacity = elem.bodyOpacity;
885 
886  module->Models().push_back( modelSettings );
887  }
888 
889  if( reader.GetRemainingBytes() != 0 )
890  {
891  THROW_IO_ERROR( "ComponentsBodies6 stream is not fully parsed" );
892  }
893 }
894 
895 
897 {
898  if( aElem.referencePoint.size() != 2 )
899  {
900  THROW_IO_ERROR( "Incorrect number of reference points for linear dimension object" );
901  }
902 
903  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
904 
905  if( klayer == UNDEFINED_LAYER )
906  {
907  wxLogWarning( wxString::Format(
908  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
909  aElem.layer ) );
910  klayer = Eco1_User;
911  }
912 
913  wxPoint referencePoint0 = aElem.referencePoint.at( 0 );
914  wxPoint referencePoint1 = aElem.referencePoint.at( 1 );
915 
916  ALIGNED_DIMENSION* dimension = new ALIGNED_DIMENSION( m_board );
917  m_board->Add( dimension, ADD_MODE::APPEND );
918 
919  dimension->SetPrecision( aElem.textprecission );
920  dimension->SetLayer( klayer );
921  dimension->SetStart( referencePoint0 );
922 
923  if( referencePoint0 != aElem.xy1 )
924  {
934  wxPoint direction = aElem.xy1 - referencePoint0;
935  wxPoint directionNormalVector = wxPoint( -direction.y, direction.x );
936  SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
937  SEG segm2( referencePoint1, referencePoint1 + direction );
938  wxPoint intersection( segm1.Intersect( segm2, true, true ).get() );
939  dimension->SetEnd( intersection );
940 
941  int height = static_cast<int>( EuclideanNorm( direction ) );
942 
943  if( direction.x <= 0 && direction.y <= 0 ) // TODO: I suspect this is not always correct
944  {
945  height = -height;
946  }
947 
948  dimension->SetHeight( height );
949  }
950  else
951  {
952  dimension->SetEnd( referencePoint1 );
953  }
954 
955  dimension->SetLineThickness( aElem.linewidth );
956 
957  dimension->Text().SetTextThickness( aElem.textlinewidth );
958  dimension->Text().SetTextSize( wxSize( aElem.textheight, aElem.textheight ) );
959  dimension->Text().SetBold( aElem.textbold );
960  dimension->Text().SetItalic( aElem.textitalic );
961 
962  switch( aElem.textunit )
963  {
964  case ALTIUM_UNIT::INCHES:
965  dimension->SetUnits( EDA_UNITS::INCHES, false );
966  break;
967  case ALTIUM_UNIT::MILS:
968  dimension->SetUnits( EDA_UNITS::INCHES, true );
969  break;
972  dimension->SetUnits( EDA_UNITS::MILLIMETRES, false );
973  break;
974  default:
975  break;
976  }
977 }
978 
980 {
981  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
982  if( klayer == UNDEFINED_LAYER )
983  {
984  wxLogWarning( wxString::Format(
985  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
986  aElem.layer ) );
987  klayer = Eco1_User;
988  }
989 
990  if( !aElem.referencePoint.empty() )
991  {
992  wxPoint referencePoint0 = aElem.referencePoint.at( 0 );
993 
994  // line
995  wxPoint last = referencePoint0;
996  for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
997  {
998  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
999  m_board->Add( ds, ADD_MODE::APPEND );
1001  ds->SetLayer( klayer );
1002  ds->SetWidth( aElem.linewidth );
1003  ds->SetStart( last );
1004  ds->SetEnd( aElem.referencePoint.at( i ) );
1005  last = aElem.referencePoint.at( i );
1006  }
1007 
1008  // arrow
1009  if( aElem.referencePoint.size() >= 2 )
1010  {
1011  wxPoint dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1012  if( dirVec.x != 0 || dirVec.y != 0 )
1013  {
1014  double scaling = EuclideanNorm( dirVec ) / aElem.arrowsize;
1015  wxPoint arrVec =
1016  wxPoint( KiROUND( dirVec.x / scaling ), KiROUND( dirVec.y / scaling ) );
1017  RotatePoint( &arrVec, 200. );
1018 
1019  DRAWSEGMENT* ds1 = new DRAWSEGMENT( m_board );
1020  m_board->Add( ds1, ADD_MODE::APPEND );
1021  ds1->SetShape( STROKE_T::S_SEGMENT );
1022  ds1->SetLayer( klayer );
1023  ds1->SetWidth( aElem.linewidth );
1024  ds1->SetStart( referencePoint0 );
1025  ds1->SetEnd( referencePoint0 + arrVec );
1026 
1027  RotatePoint( &arrVec, -400. );
1028 
1029  DRAWSEGMENT* ds2 = new DRAWSEGMENT( m_board );
1030  m_board->Add( ds2, ADD_MODE::APPEND );
1031  ds2->SetShape( STROKE_T::S_SEGMENT );
1032  ds2->SetLayer( klayer );
1033  ds2->SetWidth( aElem.linewidth );
1034  ds2->SetStart( referencePoint0 );
1035  ds2->SetEnd( referencePoint0 + arrVec );
1036  }
1037  }
1038  }
1039 
1040  if( aElem.textPoint.empty() )
1041  {
1042  wxLogError( "No text position present for leader dimension object" );
1043  return;
1044  }
1045 
1046  TEXTE_PCB* text = new TEXTE_PCB( m_board );
1047  m_board->Add( text, ADD_MODE::APPEND );
1048  text->SetText( aElem.textformat );
1049  text->SetPosition( aElem.textPoint.at( 0 ) );
1050  text->SetLayer( klayer );
1051  text->SetTextSize( wxSize( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1052  text->SetTextThickness( aElem.textlinewidth );
1055 }
1056 
1058 {
1059  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1060  if( klayer == UNDEFINED_LAYER )
1061  {
1062  wxLogWarning( wxString::Format(
1063  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1064  aElem.layer ) );
1065  klayer = Eco1_User;
1066  }
1067 
1068  for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1069  {
1070  DRAWSEGMENT* ds1 = new DRAWSEGMENT( m_board );
1071  m_board->Add( ds1, ADD_MODE::APPEND );
1072  ds1->SetShape( STROKE_T::S_SEGMENT );
1073  ds1->SetLayer( klayer );
1074  ds1->SetWidth( aElem.linewidth );
1075  ds1->SetStart( aElem.referencePoint.at( i ) );
1076  // ds1->SetEnd( /* TODO: seems to be based on TEXTY */ );
1077  }
1078 }
1079 
1081 {
1082  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1083  if( klayer == UNDEFINED_LAYER )
1084  {
1085  wxLogWarning( wxString::Format(
1086  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1087  aElem.layer ) );
1088  klayer = Eco1_User;
1089  }
1090 
1091  DRAWSEGMENT* ds1 = new DRAWSEGMENT( m_board );
1092  m_board->Add( ds1, ADD_MODE::APPEND );
1093  ds1->SetShape( STROKE_T::S_SEGMENT );
1094  ds1->SetLayer( klayer );
1095  ds1->SetWidth( aElem.linewidth );
1096 
1097  wxPoint vec1 = wxPoint( 0, aElem.height / 2 );
1098  RotatePoint( &vec1, aElem.angle * 10. );
1099  ds1->SetStart( aElem.xy1 + vec1 );
1100  ds1->SetEnd( aElem.xy1 - vec1 );
1101 
1102  DRAWSEGMENT* ds2 = new DRAWSEGMENT( m_board );
1103  m_board->Add( ds2, ADD_MODE::APPEND );
1104  ds2->SetShape( STROKE_T::S_SEGMENT );
1105  ds2->SetLayer( klayer );
1106  ds2->SetWidth( aElem.linewidth );
1107 
1108  wxPoint vec2 = wxPoint( aElem.height / 2, 0 );
1109  RotatePoint( &vec2, aElem.angle * 10. );
1110  ds2->SetStart( aElem.xy1 + vec2 );
1111  ds2->SetEnd( aElem.xy1 - vec2 );
1112 }
1113 
1114 
1116  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1117 {
1118  ALTIUM_PARSER reader( aReader, aEntry );
1119 
1120  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1121  {
1122  ADIMENSION6 elem( reader );
1123 
1124  switch( elem.kind )
1125  {
1128  break;
1131  break;
1133  wxLogWarning( wxString::Format( "Ignore dimension object of kind %d", elem.kind ) );
1134  // HelperParseDimensions6Datum( elem );
1135  break;
1138  break;
1139  default:
1140  wxLogWarning( wxString::Format( "Ignore dimension object of kind %d", elem.kind ) );
1141  break;
1142  }
1143  }
1144 
1145  if( reader.GetRemainingBytes() != 0 )
1146  {
1147  THROW_IO_ERROR( "Dimensions6 stream is not fully parsed" );
1148  }
1149 }
1150 
1151 
1152 void ALTIUM_PCB::ParseModelsData( const CFB::CompoundFileReader& aReader,
1153  const CFB::COMPOUND_FILE_ENTRY* aEntry, const wxString aRootDir )
1154 {
1155  ALTIUM_PARSER reader( aReader, aEntry );
1156 
1157  if( reader.GetRemainingBytes() == 0 )
1158  {
1159  return; // fast path: no 3d-models present which need to be imported -> no directory needs to be created
1160  }
1161 
1162  wxString projectPath = wxPathOnly( m_board->GetFileName() );
1163  // TODO: set KIPRJMOD always after import (not only when loading project)?
1164  wxSetEnv( PROJECT_VAR_NAME, projectPath );
1165 
1166  // TODO: make this path configurable?
1167  const wxString altiumModelDir = "ALTIUM_EMBEDDED_MODELS";
1168 
1169  wxFileName altiumModelsPath = wxFileName::DirName( projectPath );
1170  wxString kicadModelPrefix = "${KIPRJMOD}/" + altiumModelDir + "/";
1171  if( !altiumModelsPath.AppendDir( altiumModelDir ) )
1172  {
1173  THROW_IO_ERROR( "Cannot construct directory path for step models" );
1174  }
1175 
1176  // Create dir if it does not exist
1177  if( !altiumModelsPath.DirExists() )
1178  {
1179  if( !altiumModelsPath.Mkdir() )
1180  {
1181  wxLogError( wxString::Format(
1182  _( "Cannot create directory \"%s\" -> no 3D-models will be imported." ),
1183  GetChars( altiumModelsPath.GetFullPath() ) ) );
1184  return;
1185  }
1186  }
1187 
1188  int idx = 0;
1189  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1190  {
1191  AMODEL elem( reader );
1192 
1193  wxString stepPath = aRootDir + std::to_string( idx++ );
1194 
1195  const CFB::COMPOUND_FILE_ENTRY* stepEntry = FindStream( aReader, stepPath.c_str() );
1196 
1197  size_t stepSize = static_cast<size_t>( stepEntry->size );
1198  std::unique_ptr<char[]> stepContent( new char[stepSize] );
1199 
1200  // read file into buffer
1201  aReader.ReadFile( stepEntry, 0, stepContent.get(), stepSize );
1202 
1203  wxFileName storagePath( altiumModelsPath.GetPath(), elem.name );
1204  if( !storagePath.IsDirWritable() )
1205  {
1206  wxLogError(
1207  wxString::Format( _( "You do not have write permissions to save file \"%s\"." ),
1208  GetChars( storagePath.GetFullPath() ) ) );
1209  continue;
1210  }
1211 
1212  wxMemoryInputStream stepStream( stepContent.get(), stepSize );
1213  wxZlibInputStream zlibInputStream( stepStream );
1214 
1215  wxFileOutputStream outputStream( storagePath.GetFullPath() );
1216  outputStream.Write( zlibInputStream );
1217  outputStream.Close();
1218 
1219  m_models.insert( { elem.id, kicadModelPrefix + elem.name } );
1220  }
1221 
1222  if( reader.GetRemainingBytes() != 0 )
1223  {
1224  THROW_IO_ERROR( "Models stream is not fully parsed" );
1225  }
1226 }
1227 
1228 
1230  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1231 {
1232  ALTIUM_PARSER reader( aReader, aEntry );
1233 
1234  wxASSERT( m_num_nets == 0 );
1235  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1236  {
1237  ANET6 elem( reader );
1238 
1240  }
1241 
1242  if( reader.GetRemainingBytes() != 0 )
1243  {
1244  THROW_IO_ERROR( "Nets6 stream is not fully parsed" );
1245  }
1246 }
1247 
1249  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1250 {
1251  ALTIUM_PARSER reader( aReader, aEntry );
1252 
1253  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1254  {
1255  APOLYGON6 elem( reader );
1256 
1257  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1258  if( klayer == UNDEFINED_LAYER )
1259  {
1260  wxLogWarning( wxString::Format(
1261  _( "Polygon on Altium layer %d has no KiCad equivalent. Ignore it instead" ),
1262  elem.layer ) );
1263  m_polygons.emplace_back( nullptr );
1264  continue;
1265  }
1266 
1267  ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
1268  m_board->Add( zone, ADD_MODE::APPEND );
1269  m_polygons.emplace_back( zone );
1270 
1271  zone->SetNetCode( GetNetCode( elem.net ) );
1272  zone->SetLayer( klayer );
1273  zone->SetPosition( elem.vertices.at( 0 ).position );
1274  zone->SetLocked( elem.locked );
1275  zone->SetPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
1276 
1277  if( elem.pourindex > m_highest_pour_index )
1279 
1280  for( auto& vertice : elem.vertices )
1281  {
1282  zone->AppendCorner( vertice.position, -1 ); // TODO: arcs
1283  }
1284 
1285  // TODO: more flexible rule parsing
1286  const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
1287 
1288  if( clearanceRule != nullptr )
1289  {
1290  zone->SetLocalClearance( clearanceRule->planeclearanceClearance );
1291  }
1292 
1293  const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
1294 
1295  if( polygonConnectRule != nullptr )
1296  {
1297  switch( polygonConnectRule->polygonconnectStyle )
1298  {
1301  break;
1302 
1305  break;
1306 
1307  default:
1310  break;
1311  }
1312 
1313  // TODO: correct variables?
1315  polygonConnectRule->polygonconnectReliefconductorwidth );
1316  zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
1317 
1318  if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
1319  zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
1320  }
1321 
1322  if( IsAltiumLayerAPlane( elem.layer ) )
1323  {
1324  // outer zone will be set to priority 0 later.
1325  zone->SetPriority( 1 );
1326 
1327  // check if this is the outer zone by simply comparing the BBOX
1328  const auto& cur_outer_plane = m_outer_plane.find( elem.layer );
1329  if( cur_outer_plane == m_outer_plane.end()
1330  || zone->GetBoundingBox().Contains(
1331  cur_outer_plane->second->GetBoundingBox() ) )
1332  {
1333  m_outer_plane[elem.layer] = zone;
1334  }
1335  }
1336 
1339  {
1341  zone->SetHatchThickness( elem.trackwidth );
1342 
1344  {
1345  // use a small hack to get us only an outline (hopefully)
1346  const EDA_RECT& bbox = zone->GetBoundingBox();
1347  zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
1348  }
1349  else
1350  {
1351  zone->SetHatchGap( elem.gridsize - elem.trackwidth );
1352  }
1353 
1355  }
1356 
1359  }
1360 
1361  if( reader.GetRemainingBytes() != 0 )
1362  {
1363  THROW_IO_ERROR( "Polygons6 stream is not fully parsed" );
1364  }
1365 }
1366 
1368  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1369 {
1370  ALTIUM_PARSER reader( aReader, aEntry );
1371 
1372  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1373  {
1374  ARULE6 elem( reader );
1375 
1376  m_rules[elem.kind].emplace_back( elem );
1377  }
1378 
1379  // sort rules by priority
1380  for( auto&& val : m_rules )
1381  {
1382  std::sort( val.second.begin(), val.second.end(),
1383  []( const auto& lhs, const auto& rhs ) { return lhs.priority < rhs.priority; } );
1384  }
1385 
1386  if( reader.GetRemainingBytes() != 0 )
1387  {
1388  THROW_IO_ERROR( "Rules6 stream is not fully parsed" );
1389  }
1390 }
1391 
1393  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1394 {
1395  ALTIUM_PARSER reader( aReader, aEntry );
1396 
1397  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1398  {
1399  AREGION6 elem( reader, false );
1400 
1401  // TODO: implement?
1402  }
1403 
1404  if( reader.GetRemainingBytes() != 0 )
1405  {
1406  THROW_IO_ERROR( "BoardRegions stream is not fully parsed" );
1407  }
1408 }
1409 
1411  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1412 {
1413  ALTIUM_PARSER reader( aReader, aEntry );
1414 
1415  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1416  {
1417  AREGION6 elem( reader, true );
1418 
1420  {
1422  }
1423  else if( elem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || elem.is_keepout )
1424  {
1425  ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
1426  m_board->Add( zone, ADD_MODE::APPEND );
1427 
1428  zone->SetIsRuleArea( true );
1429  zone->SetDoNotAllowTracks( false );
1430  zone->SetDoNotAllowVias( false );
1431  zone->SetDoNotAllowPads( false );
1432  zone->SetDoNotAllowFootprints( false );
1433  zone->SetDoNotAllowCopperPour( true );
1434 
1435  if( elem.layer == ALTIUM_LAYER::MULTI_LAYER )
1436  {
1437  zone->SetLayer( F_Cu );
1438  zone->SetLayerSet( LSET::AllCuMask() );
1439  }
1440  else
1441  {
1442  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1443  if( klayer == UNDEFINED_LAYER )
1444  {
1445  wxLogWarning( wxString::Format(
1446  _( "Zone on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1447  elem.layer ) );
1448  klayer = Eco1_User;
1449  }
1450  zone->SetLayer( klayer );
1451  }
1452 
1453  zone->SetPosition( elem.vertices.at( 0 ).position );
1454 
1455  for( auto& vertice : elem.vertices )
1456  {
1457  zone->AppendCorner( vertice.position, -1 );
1458  }
1459 
1462  }
1463  else if( elem.kind == ALTIUM_REGION_KIND::COPPER )
1464  {
1465  if( elem.subpolyindex == ALTIUM_POLYGON_NONE )
1466  {
1467  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1468  if( klayer == UNDEFINED_LAYER )
1469  {
1470  wxLogWarning( wxString::Format(
1471  _( "Polygon on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1472  elem.layer ) );
1473  klayer = Eco1_User;
1474  }
1475 
1476  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
1477  m_board->Add( ds, ADD_MODE::APPEND );
1479  ds->SetLayer( klayer );
1480  ds->SetWidth( 0 );
1481 
1482  std::vector<wxPoint> pts;
1483  for( auto& vertice : elem.vertices )
1484  {
1485  pts.push_back( vertice.position );
1486  }
1487  ds->SetPolyPoints( pts );
1488  }
1489  }
1490  else
1491  {
1492  wxLogError( wxString::Format(
1493  "Ignore polygon shape of kind %d on layer %s, because not implemented yet",
1494  elem.kind, LSET::Name( GetKicadLayer( elem.layer ) ) ) );
1495  }
1496  }
1497 
1498  if( reader.GetRemainingBytes() != 0 )
1499  {
1500  THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
1501  }
1502 }
1503 
1505  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1506 {
1507  ALTIUM_PARSER reader( aReader, aEntry );
1508 
1509  for( ZONE_CONTAINER* zone : m_polygons )
1510  {
1511  if( zone != nullptr )
1512  {
1513  zone->UnFill(); // just to be sure
1514  }
1515  }
1516 
1517  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1518  {
1519  AREGION6 elem( reader, false );
1520 
1521 #if 0 // TODO: it seems this code has multiple issues right now, and we can manually fill anyways
1522  if( elem.subpolyindex != ALTIUM_POLYGON_NONE )
1523  {
1524  if( m_polygons.size() <= elem.subpolyindex )
1525  {
1527  "Region stream tries to access polygon id %d of %d existing polygons",
1528  elem.subpolyindex, m_polygons.size() ) );
1529  }
1530 
1531  ZONE_CONTAINER *zone = m_polygons.at(elem.subpolyindex);
1532 
1533  if( zone == nullptr )
1534  {
1535  continue; // we know the zone id, but because we do not know the layer we did not add it!
1536  }
1537 
1538  SHAPE_LINE_CHAIN linechain;
1539  for( auto& vertice : elem.vertices )
1540  {
1541  linechain.Append( vertice.position );
1542  }
1543  linechain.Append( elem.vertices.at( 0 ).position );
1544  linechain.SetClosed( true );
1545 
1546  SHAPE_POLY_SET polyset;
1547  polyset.AddOutline( linechain );
1548  polyset.BooleanAdd( zone->GetFilledPolysList(), SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
1549 
1550  zone->SetFilledPolysList( polyset );
1551  zone->SetIsFilled( true );
1552  }
1553 #endif
1554  }
1555 
1556  if( reader.GetRemainingBytes() != 0 )
1557  {
1558  THROW_IO_ERROR( "Regions6 stream is not fully parsed" );
1559  }
1560 }
1561 
1563  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1564 {
1565  ALTIUM_PARSER reader( aReader, aEntry );
1566 
1567  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1568  {
1569  AARC6 elem( reader );
1570 
1571  if( elem.is_polygonoutline || elem.subpolyindex != ALTIUM_POLYGON_NONE )
1572  {
1573  continue;
1574  }
1575 
1576  // element in plane is in fact substracted from the plane. Should be already done by Altium?
1577  /*if( IsAltiumLayerAPlane( elem.layer ) )
1578  {
1579  continue;
1580  }*/
1581 
1582  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1583  if( klayer == UNDEFINED_LAYER )
1584  {
1585  wxLogWarning( wxString::Format(
1586  _( "Arc on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1587  elem.layer ) );
1588  klayer = Eco1_User;
1589  }
1590 
1591  if( elem.is_keepout || IsAltiumLayerAPlane( elem.layer ) )
1592  {
1593  DRAWSEGMENT ds( nullptr ); // just a helper to get the graphic
1594  ds.SetWidth( elem.width );
1595  ds.SetCenter( elem.center );
1596  if( elem.startangle == 0. && elem.endangle == 360. )
1597  { // TODO: other variants to define circle?
1599  ds.SetArcStart( elem.center - wxPoint( 0, elem.radius ) );
1600  }
1601  else
1602  {
1603  ds.SetShape( STROKE_T::S_ARC );
1604  ds.SetAngle( -NormalizeAngleDegreesPos( elem.endangle - elem.startangle ) * 10. );
1605 
1606  double startradiant = DEG2RAD( elem.startangle );
1607  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
1608  -KiROUND( std::sin( startradiant ) * elem.radius ) );
1609  ds.SetArcStart( elem.center + arcStartOffset );
1610  }
1611 
1612  ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
1613  m_board->Add( zone, ADD_MODE::APPEND );
1614 
1615  zone->SetLayer( klayer );
1616  zone->SetIsRuleArea( true );
1617  zone->SetDoNotAllowTracks( false );
1618  zone->SetDoNotAllowVias( false );
1619  zone->SetDoNotAllowPads( false );
1620  zone->SetDoNotAllowFootprints( false );
1621  zone->SetDoNotAllowCopperPour( true );
1622 
1623  ds.TransformShapeWithClearanceToPolygon( *zone->Outline(), klayer, 0, ARC_HIGH_DEF,
1624  false );
1625  zone->Outline()->Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); // the outline is not a single polygon!
1626 
1629  continue;
1630  }
1631 
1632  if( klayer >= F_Cu && klayer <= B_Cu )
1633  {
1634  double angle = -NormalizeAngleDegreesPos( elem.endangle - elem.startangle );
1635  double startradiant = DEG2RAD( elem.startangle );
1636  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
1637  -KiROUND( std::sin( startradiant ) * elem.radius ) );
1638 
1639  SHAPE_ARC shapeArc( elem.center, elem.center + arcStartOffset, angle, elem.width );
1640  ARC* arc = new ARC( m_board, &shapeArc );
1641  m_board->Add( arc, ADD_MODE::APPEND );
1642 
1643  arc->SetWidth( elem.width );
1644  arc->SetLayer( klayer );
1645  arc->SetNetCode( GetNetCode( elem.net ) );
1646  }
1647  else
1648  {
1650  ds->SetCenter( elem.center );
1651  ds->SetWidth( elem.width );
1652  ds->SetLayer( klayer );
1653 
1654  if( elem.startangle == 0. && elem.endangle == 360. )
1655  { // TODO: other variants to define circle?
1657  ds->SetArcStart( elem.center - wxPoint( 0, elem.radius ) );
1658  }
1659  else
1660  {
1661  ds->SetShape( STROKE_T::S_ARC );
1662  ds->SetAngle( -NormalizeAngleDegreesPos( elem.endangle - elem.startangle ) * 10. );
1663 
1664  double startradiant = DEG2RAD( elem.startangle );
1665  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
1666  -KiROUND( std::sin( startradiant ) * elem.radius ) );
1667  ds->SetArcStart( elem.center + arcStartOffset );
1668  }
1669 
1671  }
1672  }
1673 
1674  if( reader.GetRemainingBytes() != 0 )
1675  {
1676  THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
1677  }
1678 }
1679 
1681  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1682 {
1683  ALTIUM_PARSER reader( aReader, aEntry );
1684 
1685  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1686  {
1687  APAD6 elem( reader );
1688 
1689  // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
1690  if( !IsAltiumLayerCopper( elem.layer ) && !IsAltiumLayerAPlane( elem.layer )
1691  && elem.layer != ALTIUM_LAYER::MULTI_LAYER )
1692  {
1693  HelperParsePad6NonCopper( elem );
1694  continue;
1695  }
1696 
1697  // Create Pad
1698  MODULE* module = nullptr;
1699  if( elem.component == ALTIUM_COMPONENT_NONE )
1700  {
1701  module = new MODULE( m_board ); // We cannot add a pad directly into the PCB
1702  m_board->Add( module, ADD_MODE::APPEND );
1703  module->SetPosition( elem.position );
1704  }
1705  else
1706  {
1707  if( m_components.size() <= elem.component )
1708  {
1710  "Pads6 stream tries to access component id %d of %d existing components",
1711  elem.component, m_components.size() ) );
1712  }
1713  module = m_components.at( elem.component );
1714  }
1715 
1716  D_PAD* pad = new D_PAD( module );
1717  module->Add( pad, ADD_MODE::APPEND );
1718 
1719  pad->SetName( elem.name );
1720  pad->SetNetCode( GetNetCode( elem.net ) );
1721  pad->SetLocked( elem.is_locked );
1722 
1723  pad->SetPosition( elem.position );
1724  pad->SetOrientationDegrees( elem.direction );
1725  pad->SetLocalCoord();
1726 
1727  pad->SetSize( elem.topsize );
1728 
1729  if( elem.holesize == 0 )
1730  {
1732  }
1733  else
1734  {
1735  if( elem.layer != ALTIUM_LAYER::MULTI_LAYER )
1736  {
1737  // TODO: I assume other values are possible as well?
1738  wxLogError( wxString::Format(
1739  "Pad '%s' of Footprint %s is not marked as multilayer, but it is an THT pad",
1740  elem.name, module->GetReference() ) );
1741  }
1744  if( !elem.sizeAndShape || elem.sizeAndShape->holeshape == ALTIUM_PAD_HOLE_SHAPE::ROUND )
1745  {
1747  pad->SetDrillSize( wxSize( elem.holesize, elem.holesize ) );
1748  }
1749  else
1750  {
1751  switch( elem.sizeAndShape->holeshape )
1752  {
1754  wxFAIL_MSG( "Round holes are handled before the switch" );
1755  break;
1756 
1758  wxLogWarning( wxString::Format(
1759  _( "Pad '%s' of Footprint %s has a square hole. KiCad does not support this yet" ),
1760  elem.name, module->GetReference() ) );
1762  pad->SetDrillSize( wxSize( elem.holesize, elem.holesize ) ); // Workaround
1763  // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in this case or rect holes have a different id
1764  break;
1765 
1767  {
1769  double normalizedSlotrotation =
1770  NormalizeAngleDegreesPos( elem.sizeAndShape->slotrotation );
1771  if( normalizedSlotrotation == 0. || normalizedSlotrotation == 180. )
1772  {
1773  pad->SetDrillSize( wxSize( elem.sizeAndShape->slotsize, elem.holesize ) );
1774  }
1775  else
1776  {
1777  if( normalizedSlotrotation != 90. && normalizedSlotrotation != 270. )
1778  {
1779  wxLogWarning( wxString::Format(
1780  _( "Pad '%s' of Footprint %s has a hole-rotation of %f degree. KiCad only supports 90 degree angles" ),
1781  elem.name, module->GetReference(), normalizedSlotrotation ) );
1782  }
1783 
1784  pad->SetDrillSize( wxSize( elem.holesize, elem.sizeAndShape->slotsize ) );
1785  }
1786  }
1787  break;
1788 
1789  default:
1791  wxLogError( wxString::Format(
1792  "Pad '%s' of Footprint %s uses a hole of unknown kind %d", elem.name,
1793  module->GetReference(), elem.sizeAndShape->holeshape ) );
1795  pad->SetDrillSize( wxSize( elem.holesize, elem.holesize ) ); // Workaround
1796  break;
1797  }
1798  }
1799 
1800  if( elem.sizeAndShape )
1801  {
1802  pad->SetOffset( elem.sizeAndShape->holeoffset[0] );
1803  }
1804  }
1805 
1806  if( elem.padmode != ALTIUM_PAD_MODE::SIMPLE )
1807  {
1808  wxLogWarning( wxString::Format(
1809  _( "Pad '%s' of Footprint %s uses a complex pad stack (kind %d), which is not supported yet" ),
1810  elem.name, module->GetReference(), elem.padmode ) );
1811  }
1812 
1813  switch( elem.topshape )
1814  {
1817  break;
1819  if( elem.sizeAndShape
1820  && elem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
1821  {
1822  pad->SetShape( PAD_SHAPE_T::PAD_SHAPE_ROUNDRECT ); // 100 = round, 0 = rectangular
1823  double ratio = elem.sizeAndShape->cornerradius[0] / 200.;
1824  pad->SetRoundRectRadiusRatio( ratio );
1825  }
1826  else if( elem.topsize.x == elem.topsize.y )
1827  {
1829  }
1830  else
1831  {
1833  }
1834  break;
1838  pad->SetChamferRectRatio( 0.25 );
1839  break;
1841  default:
1842  wxLogError( wxString::Format( "Pad '%s' of Footprint %s uses a unknown pad-shape",
1843  elem.name, module->GetReference() ) );
1844  break;
1845  }
1846 
1847  switch( elem.layer )
1848  {
1850  pad->SetLayer( F_Cu );
1851  pad->SetLayerSet( D_PAD::SMDMask() );
1852  break;
1854  pad->SetLayer( B_Cu );
1856  break;
1859  break;
1860  default:
1861  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1862  pad->SetLayer( klayer );
1863  pad->SetLayerSet( LSET( 1, klayer ) );
1864  break;
1865  }
1866 
1868  {
1870  }
1871 
1873  {
1875  }
1876 
1877  if( elem.is_tent_top )
1878  {
1879  pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
1880  }
1881  if( elem.is_tent_bottom )
1882  {
1883  pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
1884  }
1885  }
1886 
1887  if( reader.GetRemainingBytes() != 0 )
1888  {
1889  THROW_IO_ERROR( "Pads6 stream is not fully parsed" );
1890  }
1891 }
1892 
1894 {
1895  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1896  if( klayer == UNDEFINED_LAYER )
1897  {
1898  wxLogWarning( wxString::Format(
1899  _( "Non-Copper Pad on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1900  aElem.layer ) );
1901  klayer = Eco1_User;
1902  }
1903 
1904  if( aElem.net != ALTIUM_NET_UNCONNECTED )
1905  {
1906  wxLogError( wxString::Format(
1907  "Non-Copper Pad '%s' is connected to a net. This is not supported", aElem.name ) );
1908  }
1909 
1910  if( aElem.holesize != 0 )
1911  {
1912  wxLogError( wxString::Format(
1913  "Non-Copper Pad '%s' has a hole. This should not happen", aElem.name ) );
1914  }
1915 
1916  if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
1917  {
1918  wxLogWarning( wxString::Format(
1919  _( "Non-Copper Pad '%s' uses a complex pad stack (kind %d). This should not happen" ),
1920  aElem.name, aElem.padmode ) );
1921  }
1922 
1923  switch( aElem.topshape )
1924  {
1926  {
1927  // filled rect
1930  ds->SetLayer( klayer );
1931  ds->SetWidth( 0 );
1932 
1933  ds->SetPolyPoints( { aElem.position + wxPoint( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
1934  aElem.position + wxPoint( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
1935  aElem.position + wxPoint( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
1936  aElem.position + wxPoint( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
1937 
1938  if( aElem.direction != 0 )
1939  {
1940  ds->Rotate( aElem.position, aElem.direction * 10 );
1941  }
1942 
1944  }
1945  break;
1947  if( aElem.sizeAndShape
1948  && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
1949  {
1950  // filled roundrect
1951  int cornerradius = aElem.sizeAndShape->cornerradius[0];
1952  int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
1953 
1955  ds->SetLayer( klayer );
1956  ds->SetWidth( offset * 2 );
1957 
1958  if( cornerradius < 100 )
1959  {
1960  int offsetX = aElem.topsize.x / 2 - offset;
1961  int offsetY = aElem.topsize.y / 2 - offset;
1962 
1963  wxPoint p11 = aElem.position + wxPoint( offsetX, offsetY );
1964  wxPoint p12 = aElem.position + wxPoint( offsetX, -offsetY );
1965  wxPoint p22 = aElem.position + wxPoint( -offsetX, -offsetY );
1966  wxPoint p21 = aElem.position + wxPoint( -offsetX, offsetY );
1967 
1969  ds->SetPolyPoints( { p11, p12, p22, p21 } );
1970  }
1971  else if( aElem.topsize.x == aElem.topsize.y )
1972  {
1973  // circle
1975  ds->SetCenter( aElem.position );
1976  ds->SetWidth( aElem.topsize.x / 2 );
1977  ds->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
1978  }
1979  else if( aElem.topsize.x < aElem.topsize.y )
1980  {
1981  // short vertical line
1983  wxPoint pointOffset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
1984  ds->SetStart( aElem.position + pointOffset );
1985  ds->SetEnd( aElem.position - pointOffset );
1986  }
1987  else
1988  {
1989  // short horizontal line
1991  wxPoint pointOffset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
1992  ds->SetStart( aElem.position + pointOffset );
1993  ds->SetEnd( aElem.position - pointOffset );
1994  }
1995 
1996  if( aElem.direction != 0 )
1997  {
1998  ds->Rotate( aElem.position, aElem.direction * 10 );
1999  }
2000 
2002  }
2003  else if( aElem.topsize.x == aElem.topsize.y )
2004  {
2005  // filled circle
2008  ds->SetLayer( klayer );
2009  ds->SetCenter( aElem.position );
2010  ds->SetWidth( aElem.topsize.x / 2 );
2011  ds->SetArcStart( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
2013  }
2014  else
2015  {
2016  // short line
2019  ds->SetLayer( klayer );
2020  ds->SetWidth( std::min( aElem.topsize.x, aElem.topsize.y ) );
2021  if( aElem.topsize.x < aElem.topsize.y )
2022  {
2023  wxPoint offset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
2024  ds->SetStart( aElem.position + offset );
2025  ds->SetEnd( aElem.position - offset );
2026  }
2027  else
2028  {
2029  wxPoint offset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
2030  ds->SetStart( aElem.position + offset );
2031  ds->SetEnd( aElem.position - offset );
2032  }
2033  if( aElem.direction != 0 )
2034  {
2035  ds->Rotate( aElem.position, aElem.direction * 10. );
2036  }
2038  }
2039  break;
2041  {
2042  // filled octagon
2045  ds->SetLayer( klayer );
2046  ds->SetWidth( 0 );
2047 
2048  wxPoint p11 = aElem.position + wxPoint( aElem.topsize.x / 2, aElem.topsize.y / 2 );
2049  wxPoint p12 = aElem.position + wxPoint( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2050  wxPoint p22 = aElem.position + wxPoint( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2051  wxPoint p21 = aElem.position + wxPoint( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
2052 
2053  int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
2054  wxPoint chamferX( chamfer, 0 );
2055  wxPoint chamferY( 0, chamfer );
2056 
2057  ds->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
2058  p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
2059 
2060  if( aElem.direction != 0. )
2061  {
2062  ds->Rotate( aElem.position, aElem.direction * 10 );
2063  }
2064 
2066  }
2067  break;
2069  default:
2070  wxLogError(
2071  wxString::Format( "Non-Copper Pad '%s' uses a unknown pad-shape", aElem.name ) );
2072  break;
2073  }
2074 }
2075 
2077  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
2078 {
2079  ALTIUM_PARSER reader( aReader, aEntry );
2080 
2081  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2082  {
2083  AVIA6 elem( reader );
2084 
2085  VIA* via = new VIA( m_board );
2086  m_board->Add( via, ADD_MODE::APPEND );
2087 
2088  via->SetPosition( elem.position );
2089  via->SetWidth( elem.diameter );
2090  via->SetDrill( elem.holesize );
2091  via->SetNetCode( GetNetCode( elem.net ) );
2092  via->SetLocked( elem.is_locked );
2093 
2094  bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
2096  bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
2098  if( start_layer_outside && end_layer_outside )
2099  {
2100  via->SetViaType( VIATYPE::THROUGH );
2101  }
2102  else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
2103  {
2105  }
2106  else
2107  {
2108  via->SetViaType( VIATYPE::MICROVIA ); // TODO: always a microvia?
2109  }
2110 
2111  PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
2112  PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
2113  if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
2114  {
2115  wxLogError( wxString::Format(
2116  "Via from layer %d <-> %d uses non-copper layer. This should not happen.",
2117  elem.layer_start, elem.layer_end ) );
2118  continue; // just assume through-hole instead.
2119  }
2120 
2121  // we need VIATYPE set!
2122  via->SetLayerPair( start_klayer, end_klayer );
2123  }
2124 
2125  if( reader.GetRemainingBytes() != 0 )
2126  {
2127  THROW_IO_ERROR( "Vias6 stream is not fully parsed" );
2128  }
2129 }
2130 
2132  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
2133 {
2134  ALTIUM_PARSER reader( aReader, aEntry );
2135 
2136  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2137  {
2138  ATRACK6 elem( reader );
2139 
2140  if( elem.is_polygonoutline || elem.subpolyindex != ALTIUM_POLYGON_NONE )
2141  {
2142  continue;
2143  }
2144 
2145  // element in plane is in fact substracted from the plane. Already done by Altium?
2146  /*if( IsAltiumLayerAPlane( elem.layer ) )
2147  {
2148  continue;
2149  }*/
2150 
2151  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2152  if( klayer == UNDEFINED_LAYER )
2153  {
2154  wxLogWarning( wxString::Format(
2155  _( "Track on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
2156  elem.layer ) );
2157  klayer = Eco1_User;
2158  }
2159 
2160  if( elem.is_keepout || IsAltiumLayerAPlane( elem.layer ) )
2161  {
2162  DRAWSEGMENT ds( nullptr ); // just a helper to get the graphic
2164  ds.SetStart( elem.start );
2165  ds.SetEnd( elem.end );
2166  ds.SetWidth( elem.width );
2167 
2168  ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
2169  m_board->Add( zone, ADD_MODE::APPEND );
2170  zone->SetLayer( klayer );
2171  zone->SetIsRuleArea( true );
2172  zone->SetDoNotAllowTracks( false );
2173  zone->SetDoNotAllowVias( false );
2174  zone->SetDoNotAllowPads( false );
2175  zone->SetDoNotAllowFootprints( false );
2176  zone->SetDoNotAllowCopperPour( true );
2177 
2178  ds.TransformShapeWithClearanceToPolygon( *zone->Outline(), klayer, 0, ARC_HIGH_DEF,
2179  false );
2180 
2183  continue;
2184  }
2185 
2186  if( klayer >= F_Cu && klayer <= B_Cu )
2187  {
2188  TRACK* track = new TRACK( m_board );
2189  m_board->Add( track, ADD_MODE::APPEND );
2190 
2191  track->SetStart( elem.start );
2192  track->SetEnd( elem.end );
2193  track->SetWidth( elem.width );
2194  track->SetLayer( klayer );
2195  track->SetNetCode( GetNetCode( elem.net ) );
2196  }
2197  else
2198  {
2201  ds->SetStart( elem.start );
2202  ds->SetEnd( elem.end );
2203  ds->SetWidth( elem.width );
2204  ds->SetLayer( klayer );
2206  }
2207 
2208  reader.SkipSubrecord();
2209  }
2210 
2211  if( reader.GetRemainingBytes() != 0 )
2212  {
2213  THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
2214  }
2215 }
2216 
2218  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
2219 {
2220  ALTIUM_PARSER reader( aReader, aEntry );
2221 
2222  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2223  {
2224  ATEXT6 elem( reader );
2225 
2226  if( elem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
2227  {
2228  wxLogWarning( wxString::Format(
2229  _( "Ignore Barcode on Altium layer %d because it is not supported right now." ),
2230  elem.layer ) );
2231  continue;
2232  }
2233 
2234  // TODO: better approach to select if item belongs to a MODULE
2235  EDA_TEXT* tx = nullptr;
2236  BOARD_ITEM* itm = nullptr;
2237  if( elem.component == ALTIUM_COMPONENT_NONE )
2238  {
2239  TEXTE_PCB* txp = new TEXTE_PCB( m_board );
2240  tx = txp;
2241  itm = txp;
2242  m_board->Add( txp, ADD_MODE::APPEND );
2243  }
2244  else
2245  {
2246  if( m_components.size() <= elem.component )
2247  {
2249  "Texts6 stream tries to access component id %d of %d existing components",
2250  elem.component, m_components.size() ) );
2251  }
2252  MODULE* module = m_components.at( elem.component );
2253  TEXTE_MODULE* txm;
2254  if( elem.isDesignator )
2255  {
2256  txm = &module->Reference();
2257  }
2258  else if( elem.isComment )
2259  {
2260  txm = &module->Value();
2261  }
2262  else
2263  {
2264  txm = new TEXTE_MODULE( module );
2265  module->Add( txm, ADD_MODE::APPEND );
2266  }
2267 
2268  txm->SetKeepUpright( false );
2269 
2270  tx = txm;
2271  itm = txm;
2272  }
2273 
2274  wxString trimmedText = elem.text.Trim();
2275  if( !elem.isDesignator && trimmedText.CmpNoCase( ".Designator" ) == 0 )
2276  {
2277  tx->SetText( "${REFERENCE}" );
2278  }
2279  else if( !elem.isComment && trimmedText.CmpNoCase( ".Comment" ) == 0 )
2280  {
2281  tx->SetText( "${VALUE}" );
2282  }
2283  else if( trimmedText.CmpNoCase( ".Layer_Name" ) == 0 )
2284  {
2285  tx->SetText( "${LAYER}" );
2286  }
2287  else
2288  {
2289  tx->SetText( elem.text );
2290  }
2291 
2292  itm->SetPosition( elem.position );
2293  tx->SetTextAngle( elem.rotation * 10. );
2294 
2295  if( elem.component != ALTIUM_COMPONENT_NONE )
2296  {
2297  TEXTE_MODULE* txm = dynamic_cast<TEXTE_MODULE*>( tx );
2298 
2299  if( txm )
2300  {
2301  double orientation =
2302  static_cast<const MODULE*>( txm->GetParent() )->GetOrientation();
2303  txm->SetTextAngle( txm->GetTextAngle() - orientation );
2304  txm->SetLocalCoord();
2305  }
2306  }
2307 
2308  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2309  if( klayer == UNDEFINED_LAYER )
2310  {
2311  wxLogWarning( wxString::Format(
2312  _( "Text on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
2313  elem.layer ) );
2314  klayer = Eco1_User;
2315  }
2316  itm->SetLayer( klayer );
2317 
2318  if( elem.fonttype == ALTIUM_TEXT_TYPE::TRUETYPE )
2319  {
2320  // TODO: why is this required? Somehow, truetype size is calculated differently
2321  tx->SetTextSize( wxSize( elem.height / 2, elem.height / 2 ) );
2322  }
2323  else
2324  {
2325  tx->SetTextSize( wxSize( elem.height, elem.height ) ); // TODO: parse text width
2326  }
2327  tx->SetTextThickness( elem.strokewidth );
2328  tx->SetBold( elem.isBold );
2329  tx->SetItalic( elem.isItalic );
2330  tx->SetMirrored( elem.isMirrored );
2331  if( elem.isDesignator || elem.isComment ) // That's just a bold assumption
2332  {
2335  }
2336  else
2337  {
2338  switch( elem.textposition )
2339  {
2344  break;
2349  break;
2354  break;
2355  default:
2356  wxLogError( "Unexpected horizontal Text Position. This should never happen." );
2357  break;
2358  }
2359 
2360  switch( elem.textposition )
2361  {
2366  break;
2371  break;
2376  break;
2377  default:
2378  wxLogError( "Unexpected vertical text position. This should never happen." );
2379  break;
2380  }
2381  }
2382  }
2383 
2384  if( reader.GetRemainingBytes() != 0 )
2385  {
2386  THROW_IO_ERROR( "Texts6 stream is not fully parsed" );
2387  }
2388 }
2389 
2391  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
2392 {
2393  ALTIUM_PARSER reader( aReader, aEntry );
2394 
2395  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2396  {
2397  AFILL6 elem( reader );
2398 
2399  wxPoint p11( elem.pos1.x, elem.pos1.y );
2400  wxPoint p12( elem.pos1.x, elem.pos2.y );
2401  wxPoint p22( elem.pos2.x, elem.pos2.y );
2402  wxPoint p21( elem.pos2.x, elem.pos1.y );
2403 
2404  wxPoint center( ( elem.pos1.x + elem.pos2.x ) / 2, ( elem.pos1.y + elem.pos2.y ) / 2 );
2405 
2406  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2407  if( klayer == UNDEFINED_LAYER )
2408  {
2409  wxLogWarning( wxString::Format(
2410  _( "Fill on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
2411  elem.layer ) );
2412  klayer = Eco1_User;
2413  }
2414 
2415  if( elem.is_keepout || elem.net != ALTIUM_NET_UNCONNECTED )
2416  {
2417  ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
2418  m_board->Add( zone, ADD_MODE::APPEND );
2419 
2420  zone->SetNetCode( GetNetCode( elem.net ) );
2421  zone->SetLayer( klayer );
2422  zone->SetPosition( elem.pos1 );
2423  zone->SetPriority( 1000 );
2424 
2425  const int outlineIdx = -1; // this is the id of the copper zone main outline
2426  zone->AppendCorner( p11, outlineIdx );
2427  zone->AppendCorner( p12, outlineIdx );
2428  zone->AppendCorner( p22, outlineIdx );
2429  zone->AppendCorner( p21, outlineIdx );
2430 
2431  // should be correct?
2432  zone->SetLocalClearance( 0 );
2434 
2435  if( elem.is_keepout )
2436  {
2437  zone->SetIsRuleArea( true );
2438  zone->SetDoNotAllowTracks( false );
2439  zone->SetDoNotAllowVias( false );
2440  zone->SetDoNotAllowPads( false );
2441  zone->SetDoNotAllowFootprints( false );
2442  zone->SetDoNotAllowCopperPour( true );
2443  }
2444 
2445  if( elem.rotation != 0. )
2446  {
2447  zone->Rotate( center, elem.rotation * 10 );
2448  }
2449 
2452  }
2453  else
2454  {
2455  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
2456  m_board->Add( ds, ADD_MODE::APPEND );
2457 
2459  ds->SetLayer( klayer );
2460  ds->SetWidth( 0 );
2461 
2462  ds->SetPolyPoints( { p11, p12, p22, p21 } );
2463 
2464  if( elem.rotation != 0. )
2465  {
2466  ds->Rotate( center, elem.rotation * 10 );
2467  }
2468  }
2469  }
2470 
2471  if( reader.GetRemainingBytes() != 0 )
2472  {
2473  THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
2474  }
2475 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:188
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:133
void HelperParseDimensions6Datum(const ADIMENSION6 &aElem)
void SetDoNotAllowTracks(bool aEnable)
Definition: class_zone.h:735
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:719
uint32_t textlinewidth
uint32_t linewidth
int32_t soldermaskexpansionmanual
void SetHatchGap(int aStep)
Definition: class_zone.h:237
ALTIUM_RULE_KIND kind
double rotation
uint32_t holesize
void SetModified()
Definition: base_struct.cpp:87
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:61
uint32_t width
double GetOrientation() const
Definition: class_module.h:224
void Parse(const CFB::CompoundFileReader &aReader, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
Definition: altium_pcb.cpp:342
std::vector< ALTIUM_VERTICE > vertices
int planeclearanceClearance
void ParseVias6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
wxString name
bool isDesignator
void SetKeepUpright(bool aKeepUpright)
TEXTE_MODULE & Reference()
Definition: class_module.h:492
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint BACK and FRONT copper layers, mask,...
Definition: lset.cpp:536
void ParseShapeBasedRegions6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
ALTIUM_PAD_SHAPE topshape
void SetShape(STROKE_T aShape)
wxPoint pos1
static LSET StandardMask()
layer set for a through hole pad
Definition: class_pad.cpp:141
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:211
ALTIUM_LAYER layer
wxPoint m_GridOrigin
origin for grid offsets
std::vector< BOARD_STACKUP_ITEM * > & GetList()
const ARULE6 * GetRule(ALTIUM_RULE_KIND aKind, const wxString &aName) const
Definition: altium_pcb.cpp:511
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: class_zone.h:222
TEXTE_PCB class definition.
wxPoint start
wxSize topsize
like PAD_STANDARD, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:85
void ParseComponents6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:774
ALTIUM_LAYER layer
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
bool IsFlipped() const
function IsFlipped
Definition: class_module.h:302
void ParseRules6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset union For aFastMode meaning, see function booleanOp
this class manage the layers needed to make a physical board they are solder mask,...
void SetPosition(const wxPoint &aPoint) override
Definition: class_track.h:421
ALTIUM_DIMENSION_KIND kind
virtual void SetLayer(PCB_LAYER_ID aLayer)
Function SetLayer sets the layer this item is on.
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: class_pad.cpp:162
virtual void SetPosition(const wxPoint &aPos)
Definition: base_struct.h:338
BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class,...
int32_t textprecission
void SetEnd(const wxPoint &aEnd)
Definition: class_track.h:112
ALTIUM_REGION_KIND kind
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox (virtual)
Definition: class_zone.cpp:318
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: class_pad.cpp:148
wxPoint position
void ParsePolygons6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
virtual void SetStart(const wxPoint &aPoint)
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Function Intersect()
Definition: seg.cpp:93
double startangle
size_t GetRemainingBytes() const
uint16_t component
void SetDoNotAllowFootprints(bool aEnable)
Definition: class_zone.h:737
void SetItalic(bool isItalic)
Definition: eda_text.h:179
polygon (not yet used for tracks, but could be in microwave apps)
void SetChamferPositions(int aPositions)
has meaning only for chamfered rect pads set the position of the chamfers for orientation 0.
Definition: class_pad.h:502
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:38
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:81
int32_t polygonconnectAirgapwidth
wxPoint center
void SetVisible(bool aVisible)
Definition: eda_text.h:185
int GetWidth() const
Definition: eda_rect.h:119
ALTIUM_LAYER layer
SHAPE_POLY_SET * Outline()
Definition: class_zone.h:300
DRAWSEGMENT * HelperCreateAndAddDrawsegment(uint16_t aComponent)
Definition: altium_pcb.cpp:106
double GetTextAngle() const
Definition: eda_text.h:174
void SetCopperLayerCount(int aCount)
bool SetNetCode(int aNetCode, bool aNoAssert)
Sets net using a net code.
const SHAPE_POLY_SET & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Function GetFilledPolysList returns a reference to the list of filled polygons.
Definition: class_zone.h:627
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
Definition: string.cpp:735
wxString sourcedesignator
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:159
const uint16_t ALTIUM_POLYGON_NONE
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:537
ALTIUM_LAYER layer
std::vector< ZONE_CONTAINER * > m_polygons
Definition: altium_pcb.h:187
usual segment : line with rounded ends
ALTIUM_LAYER layer_end
void ParseComponentsBodies6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:824
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition: class_zone.h:175
void SetOrientationDegrees(double aOrientation)
Definition: class_module.h:223
ALTIUM_PAD_RULE soldermaskexpansionmode
void SetDrillSize(const wxSize &aSize)
Definition: class_pad.h:229
ALTIUM_LAYER layer
MODULE_3D_SETTINGS::VECTOR3D modelRotation
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:238
virtual void SetLocked(bool aLocked)
Function SetLocked modifies 'lock' status for of the item.
uint16_t net
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Function SetLayerType changes the type of the layer given by aLayer.
wxString sourcefootprintlibrary
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
void SetRoundRectRadiusRatio(double aRadiusScale)
has meaning only for rounded rect pads Set the ratio between the smaller X or Y size and the rounded ...
Definition: class_pad.cpp:239
const double startangle
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
void SetIsRuleArea(bool aEnable)
Definition: class_zone.h:732
void SetUnits(EDA_UNITS aUnits, bool aUseMils)
wxString name
MODULE_3D_SETTINGS::VECTOR3D modelPosition
wxString sourcelibreference
PCB_LAYER_ID GetKicadLayer(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:142
void SetIsFilled(bool isFilled)
Definition: class_zone.h:215
wxString text
void SetCenter(const wxPoint &aCenterPoint)
For arcs and circles:
const wxString & GetFileName() const
Definition: class_board.h:244
void ParseArcs6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
wxString scope2expr
bool is_polygonoutline
uint32_t height
bool Contains(const wxPoint &aPoint) const
Function Contains.
static int GetDefaultHatchPitch()
Function GetDefaultHatchPitchMils.
void SetLayer(PCB_LAYER_ID aLayer) override
Function SetLayer sets the layer this item is on.
void SetLocalCoord()
Set relative coordinates.
Definition: class_pad.cpp:485
A single base class (TRACK) represents both tracks and vias, with subclasses for curved tracks (ARC) ...
const ARULE6 * GetRuleDefault(ALTIUM_RULE_KIND aKind) const
Definition: altium_pcb.cpp:528
VECTOR3D m_Offset
3D model offset (mm)
Definition: class_module.h:95
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
ALTIUM_CONNECT_STYLE polygonconnectStyle
void SetDoNotAllowVias(bool aEnable)
Definition: class_zone.h:734
void SetWidth(int aWidth)
Definition: class_track.h:109
const wxString GetReference() const
Function GetReference.
Definition: class_module.h:451
int GetNetCode(uint16_t aId) const
Definition: altium_pcb.cpp:494
std::vector< wxString > names
int32_t trackwidth
std::vector< wxPoint > textPoint
void SetPriority(unsigned aPriority)
Function SetPriority.
Definition: class_zone.h:100
wxString textformat
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:113
std::map< wxString, wxString > m_models
Definition: altium_pcb.h:188
int GetLineThickness(PCB_LAYER_ID aLayer) const
Function GetLineThickness Returns the default graphic segment thickness from the layer class for the ...
uint16_t subpolyindex
const CFB::COMPOUND_FILE_ENTRY * FindStream(const CFB::CompoundFileReader &aReader, const char *aStreamName)
DIMENSION class definition.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
void ParsePads6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void SetClosed(bool aClosed)
Function SetClosed()
void HelperParseDimensions6Leader(const ADIMENSION6 &aElem)
Definition: altium_pcb.cpp:979
void SetLocked(bool aLocked) override
Function SetLocked modifies 'lock' status for of the item.
Definition: class_track.h:141
void ParseRegions6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
size_t ReadAndSetSubrecordLength()
BOARD_STACKUP & GetStackupDescriptor()
PCB_LAYER_ID
A quick note on layer IDs:
double NormalizeAngleDegreesPos(double Angle)
Normalize angle to be in the 0.0 .
Definition: trigo.h:284
BOARD * m_board
Definition: altium_pcb.h:185
ALTIUM_PAD_RULE pastemaskexpansionmode
wxPoint position
uint16_t subpolyindex
double direction
void SetHeight(int aHeight)
Sets the distance from the feature points to the crossbar line.
void SetHatchThickness(int aThickness)
Definition: class_zone.h:234
LSET is a set of PCB_LAYER_IDs.
std::map< ALTIUM_LAYER, PCB_LAYER_ID > m_layermap
Definition: altium_pcb.h:190
pads are covered by copper
Footprint text class description.
void ParseDimensions6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
ALTIUM_RULE_KIND
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:121
void SetLayerSet(LSET aLayerSet) override
Definition: class_zone.cpp:242
double endangle
wxString id
MODULES & Modules()
Definition: class_board.h:249
void ParseTexts6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
const wxPoint position
static const wxChar * Name(PCB_LAYER_ID aLayerId)
Function Name returns the fixed name association with aLayerId.
Definition: lset.cpp:81
void SetName(const wxString &aName)
Set the pad name (sometimes called pad number, although it can be an array reference like AA12).
Definition: class_pad.h:131
wxString NotSpecifiedPrm()
SHAPE_POLY_SET.
TEXTE_MODULE & Value()
read/write accessors:
Definition: class_module.h:491
ALTIUM_CLASS_KIND kind
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:203
Arcs (with rounded ends)
ALTIUM_LAYER layer
const uint16_t ALTIUM_NET_UNCONNECTED
LSET GetLayerSet() const override
Function GetLayerSet returns a std::bitset of all layers on which the item physically resides.
Definition: class_pad.h:332
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Function SetLayerName changes the name of the layer given by aLayer.
void ParseClasses6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:744
ALTIUM_PCB_DIR
Definition: altium_pcb.h:34
std::map< ALTIUM_LAYER, ZONE_CONTAINER * > m_outer_plane
Definition: altium_pcb.h:193
wxString name
void Rotate(const wxPoint &aCentre, double aAngle) override
Function Rotate Move the outlines.
Definition: class_zone.cpp:698
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Function SetLayer sets the layer this item is on.
Definition: class_zone.cpp:234
void ParseFileHeader(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:545
void SetSize(const wxSize &aSize)
Definition: class_pad.h:223
void ParseBoardRegionsData(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void SetOrientationDegrees(double aOrientation)
Set orientation in degrees.
Definition: class_pad.h:314
uint16_t component
NETCLASSES & GetNetClasses() const
uint32_t holesize
void SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
Function SetLayerPair For a via m_Layer contains the top layer, the other layer is in m_BottomLayer.
void Simplify(POLYGON_MODE aFastMode)
Simplifies the polyset (merges overlapping polys, eliminates degeneracy/self-intersections) For aFast...
void SetReference(const wxString &aReference)
Function SetReference.
Definition: class_module.h:461
wxPoint sheetpos
void SetPosition(const wxPoint &aPos) override
const int32_t radius
std::vector< MODULE * > m_components
Definition: altium_pcb.h:186
void SetDoNotAllowPads(bool aEnable)
Definition: class_zone.h:736
uint16_t net
void SetTextAngle(double aAngle) override
void SetPrecision(int aPrecision)
void SetAttribute(PAD_ATTR_T aAttribute)
Definition: class_pad.cpp:500
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aHatchStyle, int aHatchPitch, bool aRebuildHatch)
Function SetBorderDisplayStyle sets all hatch parameters for the zone.
Definition: class_zone.cpp:884
void HelperParseDimensions6Center(const ADIMENSION6 &aElem)
int GetHeight() const
Definition: eda_rect.h:120
size_t m_num_nets
Definition: altium_pcb.h:189
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
int m_highest_pour_index
Altium stores pour order across all layers.
Definition: altium_pcb.h:196
void SetStart(const wxPoint &aStart)
bool is_keepout
const wxPoint center
wxString m_Filename
The 3D shape filename in 3D library.
Definition: class_module.h:97
void SetLocalClearance(int aClearance)
Definition: class_zone.h:138
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
void ParseModelsData(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry, const wxString aRootDir)
bool is_locked
double rotation
Definition: seg.h:39
bool is_polygonoutline
void SetFilledPolysList(PCB_LAYER_ID aLayer, SHAPE_POLY_SET &aPolysList)
Function SetFilledPolysList sets the list of filled polygons.
Definition: class_zone.h:642
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index
void SetDrillShape(PAD_DRILL_SHAPE_T aShape)
Definition: class_pad.h:325
bool is_tent_bottom
uint16_t subpolyindex
void SetDoNotAllowCopperPour(bool aEnable)
Definition: class_zone.h:733
Use thermal relief for pads.
bool Add(const NETCLASSPTR &aNetclass)
Function Add takes aNetclass and puts it into this NETCLASSES container.
Definition: netclass.cpp:90
void ParseTracks6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
virtual void SetPosition(const wxPoint &aPos) override
NETINFO_ITEM handles the data for a net.
Definition: netinfo.h:65
void SetLocalCoord()
Set relative coordinates.
std::vector< ALTIUM_VERTICE > vertices
virtual void Rotate(const wxPoint &aRotCentre, double aAngle) override
Function Rotate Rotate this object.
std::list< MODULE_3D_SETTINGS > & Models()
Definition: class_module.h:214
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
Definition: altium_pcb.cpp:896
void SetHatchOrientation(double aStep)
Definition: class_zone.h:240
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: class_zone.h:162
void RemoveAll()
Delete all items in list and clear the list.
uint32_t width
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:153
ALTIUM_TEXT_POSITION textposition
ALTIUM_LAYER layer
uint16_t net
ALTIUM_LAYER layer_start
void SetLocked(bool isLocked) override
Function SetLocked sets the MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: class_module.h:321
Class to handle a graphic segment.
const char * name
Definition: DXF_plotter.cpp:60
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:201
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: class_module.h:94
double DEG2RAD(double deg)
Definition: trigo.h:219
void HelperParsePad6NonCopper(const APAD6 &aElem)
void ParseNets6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
BOARD holds information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:178
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:202
void SetLocalSolderMaskMargin(int aMargin)
Definition: class_pad.h:348
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
uint32_t radius
wxPoint position
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
TEXTE_PCB & Text()
wxString ReadWxString()
Definition: altium_parser.h:68
int32_t pastemaskexpansionmanual
wxString name
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
void SetDrill(int aDrill)
Function SetDrill sets the drill value for vias.
Definition: class_track.h:483
uint16_t net
void HelperCreateBoardOutline(const std::vector< ALTIUM_VERTICE > &aVertices)
Definition: altium_pcb.cpp:679
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:68
int GetMinThickness() const
Definition: class_zone.h:224
Pads are not covered.
double NormalizeAngleDegrees(double Angle, double aMin, double aMax)
Normalize angle to be aMin < angle <= aMax angle is in degrees.
Definition: trigo.h:311
void SetShape(PAD_SHAPE_T aShape)
Set the new shape of this pad.
Definition: class_pad.h:148
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:94
void SetEnd(const wxPoint &aEnd)
virtual void SetAngle(double aAngle)
Function SetAngle sets the angle for arcs, and normalizes it within the range 0 - 360 degrees.
bool IsCopperLayer(LAYER_NUM aLayerId)
Function IsCopperLayer tests whether a layer is a copper layer.
ALTIUM_LAYER
std::map< ALTIUM_RULE_KIND, std::vector< ARULE6 > > m_rules
Definition: altium_pcb.h:191
ALTIUM_PAD_MODE padmode
void SetPosition(const wxPoint &aPos) override
Definition: class_zone.h:94
bool AppendCorner(wxPoint aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
Definition: class_zone.cpp:827
ALTIUM_LAYER layer
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
const double endangle
void SetStart(const wxPoint &aStart)
Definition: class_track.h:115
void SetChamferRectRatio(double aChamferScale)
has meaning only for chamfered rect pads Set the ratio between the smaller X or Y size and chamfered ...
Definition: class_pad.cpp:247
uint16_t component
void SetLocalSolderPasteMargin(int aMargin)
Definition: class_pad.h:355
void ParseFills6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
std::vector< ALTIUM_VERTICE > board_vertices
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:158
const int ALTIUM_COMPONENT_NONE
ALTIUM_UNIT textunit
wxPoint GetPosition() const override
Definition: class_module.h:219
ALTIUM_LAYER layer
virtual void SetTextAngle(double aAngle)
Definition: eda_text.h:167
void SkipSubrecord()
std::vector< wxPoint > referencePoint
std::vector< ABOARD6_LAYER_STACKUP > stackup
void ParseAltiumPcb(BOARD *aBoard, const wxString &aFileName, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
Helper method which opens a Altium Board File and parses it.
Definition: altium_pcb.cpp:50
EDGE_MODULE class definition.
uint32_t strokewidth
int Parse(const UTF8 &aId, LIB_ID_TYPE aType, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
For better understanding of the points that make a dimension:
void ParseBoard6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:564
BOARD_ITEM_CONTAINER * GetParent() const
bool is_tent_top
void SetOffset(const wxPoint &aOffset)
Definition: class_pad.h:232
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:100
int32_t gridsize
void SetMinThickness(int aMinThickness)
Definition: class_zone.h:225
void SetViaType(VIATYPE aViaType)
Definition: class_track.h:382
void SetBold(bool aBold)
Definition: eda_text.h:182
uint32_t textheight
ALTIUM_POLYGON_HATCHSTYLE hatchstyle
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition: netinfo.h:478
uint16_t component
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
ALTIUM_TEXT_TYPE fonttype
void SetFPID(const LIB_ID &aFPID)
Definition: class_module.h:229
wxPoint pos2
void SetLayerSet(LSET aLayers) override
Definition: class_pad.h:331
uint32_t diameter
wxPoint m_AuxOrigin
origin for plot exports
void SetThermalReliefGap(int aThermalReliefGap)
Definition: class_zone.h:165
bool is_locked
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError=ARC_HIGH_DEF, bool ignoreLineWidth=false) const override
Function TransformShapeWithClearanceToPolygon Convert the draw segment to a closed polygon Used in fi...
wxString name
void SetLineThickness(int aWidth)
virtual void SetEnd(const wxPoint &aPoint)
void HelperDrawsegmentSetLocalCoord(DRAWSEGMENT *aDs, uint16_t aComponent)
Definition: altium_pcb.cpp:130
std::function< void(const CFB::CompoundFileReader &, const CFB::COMPOUND_FILE_ENTRY *)> PARSE_FUNCTION_POINTER_fp
Definition: altium_pcb.h:109
uint16_t net
void BuildDefaultStackupList(BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Creates a default stackup, according to the current BOARD_DESIGN_SETTINGS settings.
BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
wxString scope1expr
int32_t polygonconnectReliefconductorwidth
int32_t pourindex
ALTIUM_PCB(BOARD *aBoard)
Definition: altium_pcb.cpp:331
void SetWidth(int aWidth)