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