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.h"
26 #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  wxString::Format( "Netcode with id %d does not exist. Only %d nets are known",
504  aId, m_num_nets ) );
505  }
506  else
507  {
508  return aId + 1;
509  }
510 }
511 
512 const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
513 {
514  const auto rules = m_rules.find( aKind );
515  if( rules == m_rules.end() )
516  {
517  return nullptr;
518  }
519  for( const ARULE6& rule : rules->second )
520  {
521  if( rule.name == aName )
522  {
523  return &rule;
524  }
525  }
526  return nullptr;
527 }
528 
530 {
531  const auto rules = m_rules.find( aKind );
532  if( rules == m_rules.end() )
533  {
534  return nullptr;
535  }
536  for( const ARULE6& rule : rules->second )
537  {
538  if( rule.scope1expr == "All" && rule.scope2expr == "All" )
539  {
540  return &rule;
541  }
542  }
543  return nullptr;
544 }
545 
547  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
548 {
549  ALTIUM_PARSER reader( aReader, aEntry );
550 
551  reader.ReadAndSetSubrecordLength();
552  wxString header = reader.ReadWxString();
553 
554  //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
555 
556  //reader.SkipSubrecord();
557 
558  // TODO: does not seem to work all the time at the moment
559  //if( reader.GetRemainingBytes() != 0 )
560  //{
561  // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
562  //}
563 }
564 
566  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
567 {
568  ALTIUM_PARSER reader( aReader, aEntry );
569 
570  ABOARD6 elem( reader );
571 
572  if( reader.GetRemainingBytes() != 0 )
573  {
574  THROW_IO_ERROR( "Board6 stream is not fully parsed" );
575  }
576 
579 
580  // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
581  size_t layercount = 0;
582  for( size_t i = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
583  i < elem.stackup.size() && i != 0; i = elem.stackup[i - 1].nextId, layercount++ )
584  ;
585  size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
586  m_board->SetCopperLayerCount( kicadLayercount );
587 
588  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
589  BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
590 
591  // create board stackup
592  stackup.RemoveAll(); // Just to be sure
593  stackup.BuildDefaultStackupList( &designSettings, layercount );
594 
595  auto it = stackup.GetList().begin();
596  // find first copper layer
597  for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
598  ;
599 
600  auto curLayer = static_cast<int>( F_Cu );
601  for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
602  altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
603  altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
604  {
605  // array starts with 0, but stackup with 1
606  ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
607 
608  // handle unused layer in case of odd layercount
609  if( layer.nextId == 0 && layercount != kicadLayercount )
610  {
611  m_board->SetLayerName( ( *it )->GetBrdLayerId(), "[unused]" );
612 
613  if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
614  {
615  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
616  }
617  ( *it )->SetThickness( 0 );
618 
619  ++it;
620  if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
621  {
622  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
623  }
624  ( *it )->SetThickness( 0, 0 );
625  ( *it )->SetThicknessLocked( true, 0 );
626  ++it;
627  }
628 
629  m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ),
630  static_cast<PCB_LAYER_ID>( curLayer++ ) } );
631 
632  if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
633  {
634  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
635  }
636  ( *it )->SetThickness( layer.copperthick );
637 
638  ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
639  PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
640 
641  m_board->SetLayerName( klayer, layer.name );
642 
643  if( layer.copperthick == 0 )
644  {
645  m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
646  }
647  else if( IsAltiumLayerAPlane( alayer ) )
648  {
650  }
651 
652  if( klayer == B_Cu )
653  {
654  if( layer.nextId != 0 )
655  {
657  "Board6 stream, unexpected id while parsing last stackup layer" );
658  }
659  // overwrite entry from internal -> bottom
660  m_layermap[alayer] = B_Cu;
661  break;
662  }
663 
664  ++it;
665  if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
666  {
667  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
668  }
669  ( *it )->SetThickness( layer.dielectricthick, 0 );
670  ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
671  NotSpecifiedPrm() :
672  wxString( layer.dielectricmaterial ) );
673  ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
674 
675  ++it;
676  }
677 
679 }
680 
681 void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
682 {
683  if( !aVertices.empty() )
684  {
685  const ALTIUM_VERTICE* last = &aVertices.at( 0 );
686  for( size_t i = 0; i < aVertices.size(); i++ )
687  {
688  const ALTIUM_VERTICE* cur = &aVertices.at( ( i + 1 ) % aVertices.size() );
689 
690  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
691  m_board->Add( ds, ADD_MODE::APPEND );
692 
694  ds->SetLayer( Edge_Cuts );
695 
696  if( !last->isRound && !cur->isRound )
697  {
699  ds->SetStart( last->position );
700  ds->SetEnd( cur->position );
701  }
702  else if( cur->isRound )
703  {
704  ds->SetShape( STROKE_T::S_ARC );
705  ds->SetAngle( -NormalizeAngleDegreesPos( cur->endangle - cur->startangle ) * 10. );
706 
707  double startradiant = DEG2RAD( cur->startangle );
708  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * cur->radius ),
709  -KiROUND( std::sin( startradiant ) * cur->radius ) );
710  wxPoint arcStart = cur->center + arcStartOffset;
711  ds->SetCenter( cur->center );
712  ds->SetArcStart( arcStart );
713 
714  if( !last->isRound )
715  {
716  double endradiant = DEG2RAD( cur->endangle );
717  wxPoint arcEndOffset = wxPoint( KiROUND( std::cos( endradiant ) * cur->radius ),
718  -KiROUND( std::sin( endradiant ) * cur->radius ) );
719  wxPoint arcEnd = cur->center + arcEndOffset;
720 
721  DRAWSEGMENT* ds2 = new DRAWSEGMENT( m_board );
723  m_board->Add( ds2, ADD_MODE::APPEND );
725  ds2->SetLayer( Edge_Cuts );
726  ds2->SetStart( last->position );
727 
728  // TODO: this is more of a hack than the real solution
729  double lineLengthStart = GetLineLength( last->position, arcStart );
730  double lineLengthEnd = GetLineLength( last->position, arcEnd );
731  if( lineLengthStart > lineLengthEnd )
732  {
733  ds2->SetEnd( cur->center + arcEndOffset );
734  }
735  else
736  {
737  ds2->SetEnd( cur->center + arcStartOffset );
738  }
739  }
740  }
741  last = cur;
742  }
743  }
744 }
745 
747  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
748 {
749  ALTIUM_PARSER reader( aReader, aEntry );
750 
751  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
752 
753  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
754  {
755  ACLASS6 elem( reader );
756 
757  if( elem.kind == ALTIUM_CLASS_KIND::NET_CLASS )
758  {
759  const NETCLASSPTR& netclass = std::make_shared<NETCLASS>( elem.name );
760  designSettings.GetNetClasses().Add( netclass );
761 
762  for( const auto& name : elem.names )
763  {
764  netclass->Add(
765  name ); // TODO: it seems it can happen that we have names not attached to any net.
766  }
767  }
768  }
769 
770  if( reader.GetRemainingBytes() != 0 )
771  {
772  THROW_IO_ERROR( "Classes6 stream is not fully parsed" );
773  }
774 }
775 
777  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
778 {
779  ALTIUM_PARSER reader( aReader, aEntry );
780 
781  uint16_t componentId = 0;
782  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
783  {
784  ACOMPONENT6 elem( reader );
785 
786  MODULE* module = new MODULE( m_board );
787  m_board->Add( module, ADD_MODE::APPEND );
788  m_components.emplace_back( module );
789 
790  wxString pack_ref = elem.sourcelibreference;
791  wxString lib_ref = elem.sourcefootprintlibrary; // TODO: remove ".PcbLib" part
792  ReplaceIllegalFileNameChars( lib_ref, '_' );
793  ReplaceIllegalFileNameChars( pack_ref, '_' );
794 
795  wxString key = !lib_ref.empty() ? lib_ref + ":" + pack_ref : pack_ref;
796 
797  LIB_ID fpID;
798  fpID.Parse( key, LIB_ID::ID_PCB, true );
799  module->SetFPID( fpID );
800 
801  module->SetPosition( elem.position );
802  module->SetOrientationDegrees( elem.rotation );
803 
804  // KiCad netlisting requires parts to have non-digit + digit annotation.
805  // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
806  wxString reference = elem.sourcedesignator;
807  if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
808  reference.Prepend( "UNK" );
809  module->SetReference( reference );
810 
811  module->SetLocked( elem.locked );
812  module->Reference().SetVisible( elem.nameon );
813  module->Value().SetVisible( elem.commenton );
814  module->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
815 
816  componentId++;
817  }
818 
819  if( reader.GetRemainingBytes() != 0 )
820  {
821  THROW_IO_ERROR( "Components6 stream is not fully parsed" );
822  }
823 }
824 
825 
827  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
828 {
829  ALTIUM_PARSER reader( aReader, aEntry );
830 
831  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
832  {
833  ACOMPONENTBODY6 elem( reader ); // TODO: implement
834 
835  if( elem.component == ALTIUM_COMPONENT_NONE )
836  {
837  continue; // TODO: we do not support components for the board yet
838  }
839 
840  if( m_components.size() <= elem.component )
841  {
843  "ComponentsBodies6 stream tries to access component id %d of %d existing components",
844  elem.component, m_components.size() ) );
845  }
846 
847  if( !elem.modelIsEmbedded )
848  {
849  continue;
850  }
851 
852  auto modelTuple = m_models.find( elem.modelId );
853  if( modelTuple == m_models.end() )
854  {
856  "ComponentsBodies6 stream tries to access model id %s which does not exist",
857  elem.modelId ) );
858  }
859 
860  MODULE* module = m_components.at( elem.component );
861  const wxPoint& modulePosition = module->GetPosition();
862 
863  MODULE_3D_SETTINGS modelSettings;
864 
865  modelSettings.m_Filename = modelTuple->second;
866 
867  modelSettings.m_Offset.x = Iu2Millimeter( (int) elem.modelPosition.x - modulePosition.x );
868  modelSettings.m_Offset.y = -Iu2Millimeter( (int) elem.modelPosition.y - modulePosition.y );
869  modelSettings.m_Offset.z = Iu2Millimeter( (int) elem.modelPosition.z );
870 
871  double orientation = module->GetOrientation();
872 
873  if( module->IsFlipped() )
874  {
875  modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
876  orientation = -orientation;
877  }
878 
879  RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
880 
881  modelSettings.m_Rotation.x = NormalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
882  modelSettings.m_Rotation.y = NormalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
883  modelSettings.m_Rotation.z = NormalizeAngleDegrees(
884  -elem.modelRotation.z + elem.rotation + orientation / 10, -180, 180 );
885 
886  modelSettings.m_Opacity = elem.bodyOpacity;
887 
888  module->Models().push_back( modelSettings );
889  }
890 
891  if( reader.GetRemainingBytes() != 0 )
892  {
893  THROW_IO_ERROR( "ComponentsBodies6 stream is not fully parsed" );
894  }
895 }
896 
897 
899 {
900  if( aElem.referencePoint.size() != 2 )
901  {
902  THROW_IO_ERROR( "Incorrect number of reference points for linear dimension object" );
903  }
904 
905  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
906  if( klayer == UNDEFINED_LAYER )
907  {
908  wxLogWarning( wxString::Format(
909  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
910  aElem.layer ) );
911  klayer = Eco1_User;
912  }
913 
914  wxPoint referencePoint0 = aElem.referencePoint.at( 0 );
915  wxPoint referencePoint1 = aElem.referencePoint.at( 1 );
916 
917  DIMENSION* dimension = new DIMENSION( m_board );
918  m_board->Add( dimension, ADD_MODE::APPEND );
919 
920  dimension->SetLayer( klayer );
921  dimension->SetOrigin( referencePoint0, aElem.textprecission );
922  if( referencePoint0 != aElem.xy1 )
923  {
933  wxPoint direction = aElem.xy1 - referencePoint0;
934  wxPoint directionNormalVector = wxPoint( -direction.y, direction.x );
935  SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
936  SEG segm2( referencePoint1, referencePoint1 + direction );
937  wxPoint intersection( segm1.Intersect( segm2, true, true ).get() );
938  dimension->SetEnd( intersection, aElem.textprecission );
939 
940  int height = static_cast<int>( EuclideanNorm( direction ) );
941  if( direction.x <= 0 && direction.y <= 0 ) // TODO: I suspect this is not always correct
942  {
943  height = -height;
944  }
945  dimension->SetHeight( height, aElem.textprecission );
946  }
947  else
948  {
949  dimension->SetEnd( referencePoint1, aElem.textprecission );
950  }
951 
952  dimension->SetWidth( aElem.linewidth );
953 
954  dimension->Text().SetTextThickness( aElem.textlinewidth );
955  dimension->Text().SetTextSize( wxSize( aElem.textheight, aElem.textheight ) );
956  dimension->Text().SetBold( aElem.textbold );
957  dimension->Text().SetItalic( aElem.textitalic );
958 
959  switch( aElem.textunit )
960  {
961  case ALTIUM_UNIT::INCHES:
962  dimension->SetUnits( EDA_UNITS::INCHES, false );
963  break;
964  case ALTIUM_UNIT::MILS:
965  dimension->SetUnits( EDA_UNITS::INCHES, true );
966  break;
969  dimension->SetUnits( EDA_UNITS::MILLIMETRES, false );
970  break;
971  default:
972  break;
973  }
974 
975  dimension->AdjustDimensionDetails( aElem.textprecission );
976 }
977 
979 {
980  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
981  if( klayer == UNDEFINED_LAYER )
982  {
983  wxLogWarning( wxString::Format(
984  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
985  aElem.layer ) );
986  klayer = Eco1_User;
987  }
988 
989  if( !aElem.referencePoint.empty() )
990  {
991  wxPoint referencePoint0 = aElem.referencePoint.at( 0 );
992 
993  // line
994  wxPoint last = referencePoint0;
995  for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
996  {
997  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
998  m_board->Add( ds, ADD_MODE::APPEND );
1000  ds->SetLayer( klayer );
1001  ds->SetWidth( aElem.linewidth );
1002  ds->SetStart( last );
1003  ds->SetEnd( aElem.referencePoint.at( i ) );
1004  last = aElem.referencePoint.at( i );
1005  }
1006 
1007  // arrow
1008  if( aElem.referencePoint.size() >= 2 )
1009  {
1010  wxPoint dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1011  if( dirVec.x != 0 || dirVec.y != 0 )
1012  {
1013  double scaling = EuclideanNorm( dirVec ) / aElem.arrowsize;
1014  wxPoint arrVec =
1015  wxPoint( KiROUND( dirVec.x / scaling ), KiROUND( dirVec.y / scaling ) );
1016  RotatePoint( &arrVec, 200. );
1017 
1018  DRAWSEGMENT* ds1 = new DRAWSEGMENT( m_board );
1019  m_board->Add( ds1, ADD_MODE::APPEND );
1020  ds1->SetShape( STROKE_T::S_SEGMENT );
1021  ds1->SetLayer( klayer );
1022  ds1->SetWidth( aElem.linewidth );
1023  ds1->SetStart( referencePoint0 );
1024  ds1->SetEnd( referencePoint0 + arrVec );
1025 
1026  RotatePoint( &arrVec, -400. );
1027 
1028  DRAWSEGMENT* ds2 = new DRAWSEGMENT( m_board );
1029  m_board->Add( ds2, ADD_MODE::APPEND );
1030  ds2->SetShape( STROKE_T::S_SEGMENT );
1031  ds2->SetLayer( klayer );
1032  ds2->SetWidth( aElem.linewidth );
1033  ds2->SetStart( referencePoint0 );
1034  ds2->SetEnd( referencePoint0 + arrVec );
1035  }
1036  }
1037  }
1038 
1039  if( aElem.textPoint.empty() )
1040  {
1041  wxLogError( "No text position present for leader dimension object" );
1042  return;
1043  }
1044 
1045  TEXTE_PCB* text = new TEXTE_PCB( m_board );
1046  m_board->Add( text, ADD_MODE::APPEND );
1047  text->SetText( aElem.textformat );
1048  text->SetPosition( aElem.textPoint.at( 0 ) );
1049  text->SetLayer( klayer );
1050  text->SetTextSize( wxSize( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1051  text->SetTextThickness( aElem.textlinewidth );
1054 }
1055 
1057 {
1058  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1059  if( klayer == UNDEFINED_LAYER )
1060  {
1061  wxLogWarning( wxString::Format(
1062  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1063  aElem.layer ) );
1064  klayer = Eco1_User;
1065  }
1066 
1067  for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1068  {
1069  DRAWSEGMENT* ds1 = new DRAWSEGMENT( m_board );
1070  m_board->Add( ds1, ADD_MODE::APPEND );
1071  ds1->SetShape( STROKE_T::S_SEGMENT );
1072  ds1->SetLayer( klayer );
1073  ds1->SetWidth( aElem.linewidth );
1074  ds1->SetStart( aElem.referencePoint.at( i ) );
1075  // ds1->SetEnd( /* TODO: seems to be based on TEXTY */ );
1076  }
1077 }
1078 
1080 {
1081  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1082  if( klayer == UNDEFINED_LAYER )
1083  {
1084  wxLogWarning( wxString::Format(
1085  _( "Dimension on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
1086  aElem.layer ) );
1087  klayer = Eco1_User;
1088  }
1089 
1090  DRAWSEGMENT* ds1 = new DRAWSEGMENT( m_board );
1091  m_board->Add( ds1, ADD_MODE::APPEND );
1092  ds1->SetShape( STROKE_T::S_SEGMENT );
1093  ds1->SetLayer( klayer );
1094  ds1->SetWidth( aElem.linewidth );
1095 
1096  wxPoint vec1 = wxPoint( 0, aElem.height / 2 );
1097  RotatePoint( &vec1, aElem.angle * 10. );
1098  ds1->SetStart( aElem.xy1 + vec1 );
1099  ds1->SetEnd( aElem.xy1 - vec1 );
1100 
1101  DRAWSEGMENT* ds2 = new DRAWSEGMENT( m_board );
1102  m_board->Add( ds2, ADD_MODE::APPEND );
1103  ds2->SetShape( STROKE_T::S_SEGMENT );
1104  ds2->SetLayer( klayer );
1105  ds2->SetWidth( aElem.linewidth );
1106 
1107  wxPoint vec2 = wxPoint( aElem.height / 2, 0 );
1108  RotatePoint( &vec2, aElem.angle * 10. );
1109  ds2->SetStart( aElem.xy1 + vec2 );
1110  ds2->SetEnd( aElem.xy1 - vec2 );
1111 }
1112 
1113 
1115  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1116 {
1117  ALTIUM_PARSER reader( aReader, aEntry );
1118 
1119  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1120  {
1121  ADIMENSION6 elem( reader );
1122 
1123  switch( elem.kind )
1124  {
1127  break;
1130  break;
1132  wxLogWarning( wxString::Format( "Ignore dimension object of kind %d", elem.kind ) );
1133  // HelperParseDimensions6Datum( elem );
1134  break;
1137  break;
1138  default:
1139  wxLogWarning( wxString::Format( "Ignore dimension object of kind %d", elem.kind ) );
1140  break;
1141  }
1142  }
1143 
1144  if( reader.GetRemainingBytes() != 0 )
1145  {
1146  THROW_IO_ERROR( "Dimensions6 stream is not fully parsed" );
1147  }
1148 }
1149 
1150 
1151 void ALTIUM_PCB::ParseModelsData( const CFB::CompoundFileReader& aReader,
1152  const CFB::COMPOUND_FILE_ENTRY* aEntry, const wxString aRootDir )
1153 {
1154  ALTIUM_PARSER reader( aReader, aEntry );
1155 
1156  if( reader.GetRemainingBytes() == 0 )
1157  {
1158  return; // fast path: no 3d-models present which need to be imported -> no directory needs to be created
1159  }
1160 
1161  wxString projectPath = wxPathOnly( m_board->GetFileName() );
1162  // TODO: set KIPRJMOD always after import (not only when loading project)?
1163  wxSetEnv( PROJECT_VAR_NAME, projectPath );
1164 
1165  // TODO: make this path configurable?
1166  const wxString altiumModelDir = "ALTIUM_EMBEDDED_MODELS";
1167 
1168  wxFileName altiumModelsPath = wxFileName::DirName( projectPath );
1169  wxString kicadModelPrefix = "${KIPRJMOD}/" + altiumModelDir + "/";
1170  if( !altiumModelsPath.AppendDir( altiumModelDir ) )
1171  {
1172  THROW_IO_ERROR( "Cannot construct directory path for step models" );
1173  }
1174 
1175  // Create dir if it does not exist
1176  if( !altiumModelsPath.DirExists() )
1177  {
1178  if( !altiumModelsPath.Mkdir() )
1179  {
1180  wxLogError( wxString::Format(
1181  _( "Cannot create directory \"%s\" -> no 3D-models will be imported." ),
1182  GetChars( altiumModelsPath.GetFullPath() ) ) );
1183  return;
1184  }
1185  }
1186 
1187  int idx = 0;
1188  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1189  {
1190  AMODEL elem( reader );
1191 
1192  wxString stepPath = aRootDir + std::to_string( idx++ );
1193 
1194  const CFB::COMPOUND_FILE_ENTRY* stepEntry = FindStream( aReader, stepPath.c_str() );
1195 
1196  size_t stepSize = static_cast<size_t>( stepEntry->size );
1197  std::unique_ptr<char[]> stepContent( new char[stepSize] );
1198 
1199  // read file into buffer
1200  aReader.ReadFile( stepEntry, 0, stepContent.get(), stepSize );
1201 
1202  wxFileName storagePath( altiumModelsPath.GetPath(), elem.name );
1203  if( !storagePath.IsDirWritable() )
1204  {
1205  wxLogError(
1206  wxString::Format( _( "You do not have write permissions to save file \"%s\"." ),
1207  GetChars( storagePath.GetFullPath() ) ) );
1208  continue;
1209  }
1210 
1211  wxMemoryInputStream stepStream( stepContent.get(), stepSize );
1212  wxZlibInputStream zlibInputStream( stepStream );
1213 
1214  wxFileOutputStream outputStream( storagePath.GetFullPath() );
1215  outputStream.Write( zlibInputStream );
1216  outputStream.Close();
1217 
1218  m_models.insert( { elem.id, kicadModelPrefix + elem.name } );
1219  }
1220 
1221  if( reader.GetRemainingBytes() != 0 )
1222  {
1223  THROW_IO_ERROR( "Models stream is not fully parsed" );
1224  }
1225 }
1226 
1227 
1229  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1230 {
1231  ALTIUM_PARSER reader( aReader, aEntry );
1232 
1233  wxASSERT( m_num_nets == 0 );
1234  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1235  {
1236  ANET6 elem( reader );
1237 
1239  }
1240 
1241  if( reader.GetRemainingBytes() != 0 )
1242  {
1243  THROW_IO_ERROR( "Nets6 stream is not fully parsed" );
1244  }
1245 }
1246 
1248  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
1249 {
1250  ALTIUM_PARSER reader( aReader, aEntry );
1251 
1252  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1253  {
1254  APOLYGON6 elem( reader );
1255 
1256  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1257  if( klayer == UNDEFINED_LAYER )
1258  {
1259  wxLogWarning( wxString::Format(
1260  _( "Polygon on Altium layer %d has no KiCad equivalent. Ignore it instead" ),
1261  elem.layer ) );
1262  m_polygons.emplace_back( nullptr );
1263  continue;
1264  }
1265 
1266  ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
1267  m_board->Add( zone, ADD_MODE::APPEND );
1268  m_polygons.emplace_back( zone );
1269 
1270  zone->SetNetCode( GetNetCode( elem.net ) );
1271  zone->SetLayer( klayer );
1272  zone->SetPosition( elem.vertices.at( 0 ).position );
1273  zone->SetLocked( elem.locked );
1274  zone->SetPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
1275 
1276  if( elem.pourindex > m_highest_pour_index )
1278 
1279  for( auto& vertice : elem.vertices )
1280  {
1281  zone->AppendCorner( vertice.position, -1 ); // TODO: arcs
1282  }
1283 
1284  // TODO: more flexible rule parsing
1285  const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
1286 
1287  if( clearanceRule != nullptr )
1288  {
1289  zone->SetZoneClearance( clearanceRule->planeclearanceClearance );
1290  }
1291 
1292  const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
1293 
1294  if( polygonConnectRule != nullptr )
1295  {
1296  switch( polygonConnectRule->polygonconnectStyle )
1297  {
1300  break;
1301 
1304  break;
1305 
1306  default:
1309  break;
1310  }
1311 
1312  // TODO: correct variables?
1314  polygonConnectRule->polygonconnectReliefconductorwidth );
1315  zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
1316 
1317  if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
1318  zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
1319  }
1320 
1321  if( IsAltiumLayerAPlane( elem.layer ) )
1322  {
1323  // outer zone will be set to priority 0 later.
1324  zone->SetPriority( 1 );
1325 
1326  // check if this is the outer zone by simply comparing the BBOX
1327  const auto& cur_outer_plane = m_outer_plane.find( elem.layer );
1328  if( cur_outer_plane == m_outer_plane.end()
1329  || zone->GetBoundingBox().Contains(
1330  cur_outer_plane->second->GetBoundingBox() ) )
1331  {
1332  m_outer_plane[elem.layer] = zone;
1333  }
1334  }
1335 
1338  {
1340  zone->SetHatchFillTypeThickness( elem.trackwidth );
1341 
1343  {
1344  // use a small hack to get us only an outline (hopefully)
1345  const EDA_RECT& bbox = zone->GetBoundingBox();
1346  zone->SetHatchFillTypeGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
1347  }
1348  else
1349  {
1350  zone->SetHatchFillTypeGap( elem.gridsize - elem.trackwidth );
1351  }
1352 
1355  }
1356 
1357  zone->SetHatch(
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->SetIsKeepout( 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 
1460  zone->SetHatch(
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->SetIsKeepout( 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(), 0, ARC_HIGH_DEF, false );
1624  zone->Outline()->Simplify(
1625  SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); // the outline is not a single polygon!
1626 
1627  zone->SetHatch(
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",
1793  elem.name, 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->SetIsKeepout( 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(), 0, ARC_HIGH_DEF, false );
2179 
2180  zone->SetHatch(
2182  continue;
2183  }
2184 
2185  if( klayer >= F_Cu && klayer <= B_Cu )
2186  {
2187  TRACK* track = new TRACK( m_board );
2188  m_board->Add( track, ADD_MODE::APPEND );
2189 
2190  track->SetStart( elem.start );
2191  track->SetEnd( elem.end );
2192  track->SetWidth( elem.width );
2193  track->SetLayer( klayer );
2194  track->SetNetCode( GetNetCode( elem.net ) );
2195  }
2196  else
2197  {
2200  ds->SetStart( elem.start );
2201  ds->SetEnd( elem.end );
2202  ds->SetWidth( elem.width );
2203  ds->SetLayer( klayer );
2205  }
2206 
2207  reader.SkipSubrecord();
2208  }
2209 
2210  if( reader.GetRemainingBytes() != 0 )
2211  {
2212  THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
2213  }
2214 }
2215 
2217  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
2218 {
2219  ALTIUM_PARSER reader( aReader, aEntry );
2220 
2221  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2222  {
2223  ATEXT6 elem( reader );
2224 
2225  if( elem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
2226  {
2227  wxLogWarning( wxString::Format(
2228  _( "Ignore Barcode on Altium layer %d because it is not supported right now." ),
2229  elem.layer ) );
2230  continue;
2231  }
2232 
2233  // TODO: better approach to select if item belongs to a MODULE
2234  EDA_TEXT* tx = nullptr;
2235  BOARD_ITEM* itm = nullptr;
2236  if( elem.component == ALTIUM_COMPONENT_NONE )
2237  {
2238  TEXTE_PCB* txp = new TEXTE_PCB( m_board );
2239  tx = txp;
2240  itm = txp;
2241  m_board->Add( txp, ADD_MODE::APPEND );
2242  }
2243  else
2244  {
2245  if( m_components.size() <= elem.component )
2246  {
2248  "Texts6 stream tries to access component id %d of %d existing components",
2249  elem.component, m_components.size() ) );
2250  }
2251  MODULE* module = m_components.at( elem.component );
2252  TEXTE_MODULE* txm;
2253  if( elem.isDesignator )
2254  {
2255  txm = &module->Reference();
2256  }
2257  else if( elem.isComment )
2258  {
2259  txm = &module->Value();
2260  }
2261  else
2262  {
2263  txm = new TEXTE_MODULE( module );
2264  module->Add( txm, ADD_MODE::APPEND );
2265  }
2266 
2267  txm->SetKeepUpright( false );
2268 
2269  tx = txm;
2270  itm = txm;
2271  }
2272 
2273  wxString trimmedText = elem.text.Trim();
2274  if( !elem.isDesignator && trimmedText.CmpNoCase( ".Designator" ) == 0 )
2275  {
2276  tx->SetText( "${REFERENCE}" );
2277  }
2278  else if( !elem.isComment && trimmedText.CmpNoCase( ".Comment" ) == 0 )
2279  {
2280  tx->SetText( "${VALUE}" );
2281  }
2282  else if( trimmedText.CmpNoCase( ".Layer_Name" ) == 0 )
2283  {
2284  tx->SetText( "${LAYER}" );
2285  }
2286  else
2287  {
2288  tx->SetText( elem.text );
2289  }
2290 
2291  itm->SetPosition( elem.position );
2292  tx->SetTextAngle( elem.rotation * 10. );
2293 
2294  if( elem.component != ALTIUM_COMPONENT_NONE )
2295  {
2296  TEXTE_MODULE* txm = dynamic_cast<TEXTE_MODULE*>( tx );
2297 
2298  if( txm )
2299  {
2300  double orientation =
2301  static_cast<const MODULE*>( txm->GetParent() )->GetOrientation();
2302  txm->SetTextAngle( txm->GetTextAngle() - orientation );
2303  txm->SetLocalCoord();
2304  }
2305  }
2306 
2307  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2308  if( klayer == UNDEFINED_LAYER )
2309  {
2310  wxLogWarning( wxString::Format(
2311  _( "Text on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
2312  elem.layer ) );
2313  klayer = Eco1_User;
2314  }
2315  itm->SetLayer( klayer );
2316 
2317  if( elem.fonttype == ALTIUM_TEXT_TYPE::TRUETYPE )
2318  {
2319  // TODO: why is this required? Somehow, truetype size is calculated differently
2320  tx->SetTextSize( wxSize( elem.height / 2, elem.height / 2 ) );
2321  }
2322  else
2323  {
2324  tx->SetTextSize( wxSize( elem.height, elem.height ) ); // TODO: parse text width
2325  }
2326  tx->SetTextThickness( elem.strokewidth );
2327  tx->SetBold( elem.isBold );
2328  tx->SetItalic( elem.isItalic );
2329  tx->SetMirrored( elem.isMirrored );
2330  if( elem.isDesignator || elem.isComment ) // That's just a bold assumption
2331  {
2334  }
2335  else
2336  {
2337  switch( elem.textposition )
2338  {
2343  break;
2348  break;
2353  break;
2354  default:
2355  wxLogError( "Unexpected horizontal Text Position. This should never happen." );
2356  break;
2357  }
2358 
2359  switch( elem.textposition )
2360  {
2365  break;
2370  break;
2375  break;
2376  default:
2377  wxLogError( "Unexpected vertical text position. This should never happen." );
2378  break;
2379  }
2380  }
2381  }
2382 
2383  if( reader.GetRemainingBytes() != 0 )
2384  {
2385  THROW_IO_ERROR( "Texts6 stream is not fully parsed" );
2386  }
2387 }
2388 
2390  const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
2391 {
2392  ALTIUM_PARSER reader( aReader, aEntry );
2393 
2394  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2395  {
2396  AFILL6 elem( reader );
2397 
2398  wxPoint p11( elem.pos1.x, elem.pos1.y );
2399  wxPoint p12( elem.pos1.x, elem.pos2.y );
2400  wxPoint p22( elem.pos2.x, elem.pos2.y );
2401  wxPoint p21( elem.pos2.x, elem.pos1.y );
2402 
2403  wxPoint center( ( elem.pos1.x + elem.pos2.x ) / 2, ( elem.pos1.y + elem.pos2.y ) / 2 );
2404 
2405  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2406  if( klayer == UNDEFINED_LAYER )
2407  {
2408  wxLogWarning( wxString::Format(
2409  _( "Fill on Altium layer %d has no KiCad equivalent. Put it on Eco1_User instead" ),
2410  elem.layer ) );
2411  klayer = Eco1_User;
2412  }
2413 
2414  if( elem.is_keepout || elem.net != ALTIUM_NET_UNCONNECTED )
2415  {
2416  ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
2417  m_board->Add( zone, ADD_MODE::APPEND );
2418 
2419  zone->SetNetCode( GetNetCode( elem.net ) );
2420  zone->SetLayer( klayer );
2421  zone->SetPosition( elem.pos1 );
2422  zone->SetPriority( 1000 );
2423 
2424  const int outlineIdx = -1; // this is the id of the copper zone main outline
2425  zone->AppendCorner( p11, outlineIdx );
2426  zone->AppendCorner( p12, outlineIdx );
2427  zone->AppendCorner( p22, outlineIdx );
2428  zone->AppendCorner( p21, outlineIdx );
2429 
2430  // should be correct?
2431  zone->SetZoneClearance( 0 );
2433 
2434  if( elem.is_keepout )
2435  {
2436  zone->SetIsKeepout( true );
2437  zone->SetDoNotAllowTracks( false );
2438  zone->SetDoNotAllowVias( false );
2439  zone->SetDoNotAllowPads( false );
2440  zone->SetDoNotAllowFootprints( false );
2441  zone->SetDoNotAllowCopperPour( true );
2442  }
2443 
2444  if( elem.rotation != 0. )
2445  {
2446  zone->Rotate( center, elem.rotation * 10 );
2447  }
2448 
2449  zone->SetHatch(
2451  }
2452  else
2453  {
2454  DRAWSEGMENT* ds = new DRAWSEGMENT( m_board );
2455  m_board->Add( ds, ADD_MODE::APPEND );
2456 
2458  ds->SetLayer( klayer );
2459  ds->SetWidth( 0 );
2460 
2461  ds->SetPolyPoints( { p11, p12, p22, p21 } );
2462 
2463  if( elem.rotation != 0. )
2464  {
2465  ds->Rotate( center, elem.rotation * 10 );
2466  }
2467  }
2468  }
2469 
2470  if( reader.GetRemainingBytes() != 0 )
2471  {
2472  THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
2473  }
2474 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:187
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:128
void HelperParseDimensions6Datum(const ADIMENSION6 &aElem)
void SetDoNotAllowTracks(bool aEnable)
Definition: class_zone.h:721
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:712
uint32_t textlinewidth
uint32_t linewidth
int32_t soldermaskexpansionmanual
ALTIUM_RULE_KIND kind
double rotation
uint32_t holesize
void SetModified()
Definition: base_struct.cpp:85
ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:61
void SetHatch(ZONE_HATCH_STYLE aHatchStyle, int aHatchPitch, bool aRebuildHatch)
Function SetHatch sets all hatch parameters for the zone.
Definition: class_zone.cpp:951
uint32_t width
double GetOrientation() const
Definition: class_module.h:211
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:474
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint BACK and FRONT copper layers, mask,...
Definition: lset.cpp:531
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:99
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:206
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:512
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: class_zone.h:196
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:776
virtual void SetPosition(const wxPoint &aPos)=0
ALTIUM_LAYER layer
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
bool IsFlipped() const
function IsFlipped
Definition: class_module.h:288
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:412
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:120
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:114
ALTIUM_REGION_KIND kind
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox (virtual)
Definition: class_zone.cpp:335
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: class_pad.cpp:106
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, 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...
wxPoint position
void ParsePolygons6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
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:723
void SetItalic(bool isItalic)
Definition: eda_text.h:178
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:490
#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:184
int GetWidth() const
Definition: eda_rect.h:119
ALTIUM_LAYER layer
SHAPE_POLY_SET * Outline()
Definition: class_zone.h:271
DRAWSEGMENT * HelperCreateAndAddDrawsegment(uint16_t aComponent)
Definition: altium_pcb.cpp:106
double GetTextAngle() const
Definition: eda_text.h:173
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:602
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
Definition: string.cpp:714
void SetHeight(int aHeight, int aPrecision)
Function SetHeight Sets the length of feature lines.
wxString sourcedesignator
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:156
const uint16_t ALTIUM_POLYGON_NONE
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:551
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:826
void SetOrientationDegrees(double aOrientation)
Definition: class_module.h:210
ALTIUM_PAD_RULE soldermaskexpansionmode
void SetDrillSize(const wxSize &aSize)
Definition: class_pad.h:226
ALTIUM_LAYER layer
MODULE_3D_SETTINGS::VECTOR3D modelRotation
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:237
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:178
const double startangle
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
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:186
wxString text
void SetCenter(const wxPoint &aCenterPoint)
For arcs and circles:
const wxString & GetFileName() const
Definition: class_board.h:255
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:396
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:529
VECTOR3D m_Offset
3D model offset (mm)
Definition: class_module.h:94
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
ALTIUM_CONNECT_STYLE polygonconnectStyle
void SetDoNotAllowVias(bool aEnable)
Definition: class_zone.h:720
void SetWidth(int aWidth)
Definition: class_track.h:111
const wxString GetReference() const
Function GetReference.
Definition: class_module.h:433
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:95
wxString textformat
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:112
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 ...
void SetLayerSet(LSET aLayerSet)
Definition: class_zone.cpp:264
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:978
void SetLocked(bool aLocked) override
Function SetLocked modifies 'lock' status for of the item.
Definition: class_track.h:143
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:265
BOARD * m_board
Definition: altium_pcb.h:185
ALTIUM_PAD_RULE pastemaskexpansionmode
void SetHatchFillTypeGap(int aStep)
Definition: class_zone.h:214
wxPoint position
uint16_t subpolyindex
double direction
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
void SetOrigin(const wxPoint &aOrigin, int aPrecision)
Function SetOrigin Sets a new origin of the crossbar line.
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:116
double endangle
wxString id
void SetEnd(const wxPoint &aEnd, int aPrecision)
Function SetEnd Sets a new end of the crossbar line.
MODULES & Modules()
Definition: class_board.h:266
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:78
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:128
wxString NotSpecifiedPrm()
SHAPE_POLY_SET.
TEXTE_MODULE & Value()
read/write accessors:
Definition: class_module.h:473
ALTIUM_CLASS_KIND kind
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:202
Arcs (with rounded ends)
ALTIUM_LAYER layer
const uint16_t ALTIUM_NET_UNCONNECTED
LSET GetLayerSet() const override
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
Definition: class_pad.h:325
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:746
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 &centre, double angle) override
Function Rotate Move the outlines.
Definition: class_zone.cpp:770
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Function SetLayer sets the layer this item is on.
Definition: class_zone.cpp:256
void ParseFileHeader(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:546
void SetSize(const wxSize &aSize)
Definition: class_pad.h:220
void ParseBoardRegionsData(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void SetOrientationDegrees(double aOrientation)
Set orientation in degrees.
Definition: class_pad.h:309
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:443
#define THROW_IO_ERROR(msg)
wxPoint sheetpos
void SetPosition(const wxPoint &aPos) override
void SetZoneClearance(int aZoneClearance)
Definition: class_zone.h:192
const int32_t radius
std::vector< MODULE * > m_components
Definition: altium_pcb.h:186
void SetDoNotAllowPads(bool aEnable)
Definition: class_zone.h:722
uint16_t net
void SetTextAngle(double aAngle) override
void SetAttribute(PAD_ATTR_T aAttribute)
Definition: class_pad.cpp:411
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)
void SetThermalReliefCopperBridge(int aThermalReliefCopperBridge)
Definition: class_zone.h:153
bool is_keepout
const wxPoint center
wxString m_Filename
The 3D shape filename in 3D library.
Definition: class_module.h:96
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:617
void AdjustDimensionDetails(int aPrecision)
Function AdjustDimensionDetails Calculate coordinates of segments used to draw the dimension.
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:319
bool is_tent_bottom
uint16_t subpolyindex
void SetDoNotAllowCopperPour(bool aEnable)
Definition: class_zone.h:719
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
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:324
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:201
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
Definition: altium_pcb.cpp:898
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: class_zone.h:147
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:307
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:205
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: class_module.h:93
double DEG2RAD(double deg)
Definition: trigo.h:214
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:180
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:201
void SetLocalSolderMaskMargin(int aMargin)
Definition: class_pad.h:341
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
void SetHatchFillTypeOrientation(double aStep)
Definition: class_zone.h:217
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:447
uint16_t net
void HelperCreateBoardOutline(const std::vector< ALTIUM_VERTICE > &aVertices)
Definition: altium_pcb.cpp:681
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:201
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:292
void SetShape(PAD_SHAPE_T aShape)
Set the new shape of this pad.
Definition: class_pad.h:145
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:94
void SetEnd(const wxPoint &aEnd)
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
void SetHatchFillTypeThickness(int aThickness)
Definition: class_zone.h:211
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:89
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:894
ALTIUM_LAYER layer
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
const double endangle
void SetStart(const wxPoint &aStart)
Definition: class_track.h:117
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:186
uint16_t component
void SetLocalSolderPasteMargin(int aMargin)
Definition: class_pad.h:347
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:157
ALTIUM_UNIT textunit
wxPoint GetPosition() const override
Definition: class_module.h:206
ALTIUM_LAYER layer
virtual void SetTextAngle(double aAngle)
Definition: eda_text.h:166
void SkipSubrecord()
void SetIsKeepout(bool aEnable)
Definition: class_zone.h:718
std::vector< wxPoint > referencePoint
void SetWidth(int aWidth)
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
void ParseBoard6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:565
BOARD_ITEM_CONTAINER * GetParent() const
const uint16_t ALTIUM_COMPONENT_NONE
bool is_tent_top
void SetOffset(const wxPoint &aOffset)
Definition: class_pad.h:229
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:100
int32_t gridsize
void SetMinThickness(int aMinThickness)
Definition: class_zone.h:202
void SetViaType(VIATYPE aViaType)
Definition: class_track.h:374
void SetBold(bool aBold)
Definition: eda_text.h:181
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:461
uint16_t component
ALTIUM_TEXT_TYPE fonttype
void SetFPID(const LIB_ID &aFPID)
Definition: class_module.h:216
wxPoint pos2
uint32_t diameter
wxPoint m_AuxOrigin
origin for plot exports
void SetThermalReliefGap(int aThermalReliefGap)
Definition: class_zone.h:150
bool is_locked
DIMENSION.
wxString name
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)