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