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 <[email protected]>
5  * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include "altium_pcb.h"
26 #include "altium_parser_pcb.h"
29 
30 #include <board.h>
31 #include <board_design_settings.h>
32 #include <pcb_dimension.h>
33 #include <pad.h>
34 #include <pcb_shape.h>
35 #include <pcb_text.h>
36 #include <pcb_track.h>
37 #include <string_utils.h>
38 
39 #include <fp_shape.h>
40 #include <fp_text.h>
41 #include <zone.h>
42 
44 
45 #include <compoundfilereader.h>
47 #include <project.h>
48 #include <trigo.h>
49 #include <utf.h>
50 #include <wx/docview.h>
51 #include <wx/log.h>
52 #include <wx/mstream.h>
53 #include <wx/wfstream.h>
54 #include <wx/zstream.h>
55 #include <progress_reporter.h>
56 
57 
58 void ParseAltiumPcb( BOARD* aBoard, const wxString& aFileName, PROGRESS_REPORTER* aProgressReporter,
59  const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
60 {
61  // Open file
62  FILE* fp = wxFopen( aFileName, "rb" );
63 
64  if( fp == nullptr )
65  {
66  wxLogError( _( "Cannot open file '%s'." ), aFileName );
67  return;
68  }
69 
70  fseek( fp, 0, SEEK_END );
71  long len = ftell( fp );
72 
73  if( len < 0 )
74  {
75  fclose( fp );
76  THROW_IO_ERROR( _( "Error reading file: cannot determine length." ) );
77  }
78 
79  std::unique_ptr<unsigned char[]> buffer( new unsigned char[len] );
80  fseek( fp, 0, SEEK_SET );
81 
82  size_t bytesRead = fread( buffer.get(), sizeof( unsigned char ), len, fp );
83  fclose( fp );
84 
85  if( static_cast<size_t>( len ) != bytesRead )
86  {
87  THROW_IO_ERROR( _( "Error reading file." ) );
88  }
89 
90  try
91  {
92  CFB::CompoundFileReader reader( buffer.get(), bytesRead );
93 
94  // Parse File
95  ALTIUM_PCB pcb( aBoard, aProgressReporter );
96  pcb.Parse( reader, aFileMapping );
97  }
98  catch( CFB::CFBException& exception )
99  {
100  THROW_IO_ERROR( exception.what() );
101  }
102 }
103 
104 
106 {
107  return aLayer >= ALTIUM_LAYER::TOP_LAYER && aLayer <= ALTIUM_LAYER::BOTTOM_LAYER;
108 }
109 
110 
112 {
114 }
115 
116 
118 {
119  if( aComponent == ALTIUM_COMPONENT_NONE )
120  {
121  PCB_SHAPE* shape = new PCB_SHAPE( m_board );
122  m_board->Add( shape, ADD_MODE::APPEND );
123  return shape;
124  }
125  else
126  {
127  if( m_components.size() <= aComponent )
128  {
129  THROW_IO_ERROR( wxString::Format( "Component creator tries to access component id %d "
130  "of %d existing components",
131  aComponent,
132  m_components.size() ) );
133  }
134 
135  FOOTPRINT* footprint = m_components.at( aComponent );
136  PCB_SHAPE* fpShape = new FP_SHAPE( footprint );
137 
138  footprint->Add( fpShape, ADD_MODE::APPEND );
139  return fpShape;
140  }
141 }
142 
143 
144 void ALTIUM_PCB::HelperShapeSetLocalCoord( PCB_SHAPE* aShape, uint16_t aComponent )
145 {
146  if( aComponent != ALTIUM_COMPONENT_NONE )
147  {
148  FP_SHAPE* fpShape = dynamic_cast<FP_SHAPE*>( aShape );
149 
150  if( fpShape )
151  {
152  fpShape->SetLocalCoord();
153 
154  // TODO: SetLocalCoord() does not update the polygon shape!
155  // This workaround converts the poly shape into the local coordinates
156  SHAPE_POLY_SET& polyShape = fpShape->GetPolyShape();
157  if( !polyShape.IsEmpty() )
158  {
159  FOOTPRINT* fp = m_components.at( aComponent );
160 
161  polyShape.Move( -fp->GetPosition() );
162  polyShape.Rotate( -fp->GetOrientationRadians() );
163  }
164  }
165  }
166 }
167 
168 
170  const std::vector<ALTIUM_VERTICE>& aVertices )
171 {
172  for( const ALTIUM_VERTICE& vertex : aVertices )
173  {
174  if( vertex.isRound )
175  {
176  double angle = NormalizeAngleDegreesPos( vertex.endangle - vertex.startangle );
177 
178  double startradiant = DEG2RAD( vertex.startangle );
179  double endradiant = DEG2RAD( vertex.endangle );
180  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * vertex.radius ),
181  -KiROUND( std::sin( startradiant ) * vertex.radius ) );
182 
183  wxPoint arcEndOffset = wxPoint( KiROUND( std::cos( endradiant ) * vertex.radius ),
184  -KiROUND( std::sin( endradiant ) * vertex.radius ) );
185 
186  wxPoint arcStart = vertex.center + arcStartOffset;
187  wxPoint arcEnd = vertex.center + arcEndOffset;
188 
189  if( GetLineLength( arcStart, vertex.position )
190  < GetLineLength( arcEnd, vertex.position ) )
191  {
192  aLine.Append( SHAPE_ARC( vertex.center, arcStart, -angle ) );
193  }
194  else
195  {
196  aLine.Append( SHAPE_ARC( vertex.center, arcEnd, angle ) );
197  }
198  }
199  else
200  {
201  aLine.Append( vertex.position );
202  }
203  }
204 
205  aLine.SetClosed( true );
206 }
207 
208 
210 {
211  auto override = m_layermap.find( aAltiumLayer );
212  if( override != m_layermap.end() )
213  {
214  return override->second;
215  }
216 
217  switch( aAltiumLayer )
218  {
220 
221  case ALTIUM_LAYER::TOP_LAYER: return F_Cu;
222  case ALTIUM_LAYER::MID_LAYER_1: return In1_Cu;
223  case ALTIUM_LAYER::MID_LAYER_2: return In2_Cu;
224  case ALTIUM_LAYER::MID_LAYER_3: return In3_Cu;
225  case ALTIUM_LAYER::MID_LAYER_4: return In4_Cu;
226  case ALTIUM_LAYER::MID_LAYER_5: return In5_Cu;
227  case ALTIUM_LAYER::MID_LAYER_6: return In6_Cu;
228  case ALTIUM_LAYER::MID_LAYER_7: return In7_Cu;
229  case ALTIUM_LAYER::MID_LAYER_8: return In8_Cu;
230  case ALTIUM_LAYER::MID_LAYER_9: return In9_Cu;
231  case ALTIUM_LAYER::MID_LAYER_10: return In10_Cu;
232  case ALTIUM_LAYER::MID_LAYER_11: return In11_Cu;
233  case ALTIUM_LAYER::MID_LAYER_12: return In12_Cu;
234  case ALTIUM_LAYER::MID_LAYER_13: return In13_Cu;
235  case ALTIUM_LAYER::MID_LAYER_14: return In14_Cu;
236  case ALTIUM_LAYER::MID_LAYER_15: return In15_Cu;
237  case ALTIUM_LAYER::MID_LAYER_16: return In16_Cu;
238  case ALTIUM_LAYER::MID_LAYER_17: return In17_Cu;
239  case ALTIUM_LAYER::MID_LAYER_18: return In18_Cu;
240  case ALTIUM_LAYER::MID_LAYER_19: return In19_Cu;
241  case ALTIUM_LAYER::MID_LAYER_20: return In20_Cu;
242  case ALTIUM_LAYER::MID_LAYER_21: return In21_Cu;
243  case ALTIUM_LAYER::MID_LAYER_22: return In22_Cu;
244  case ALTIUM_LAYER::MID_LAYER_23: return In23_Cu;
245  case ALTIUM_LAYER::MID_LAYER_24: return In24_Cu;
246  case ALTIUM_LAYER::MID_LAYER_25: return In25_Cu;
247  case ALTIUM_LAYER::MID_LAYER_26: return In26_Cu;
248  case ALTIUM_LAYER::MID_LAYER_27: return In27_Cu;
249  case ALTIUM_LAYER::MID_LAYER_28: return In28_Cu;
250  case ALTIUM_LAYER::MID_LAYER_29: return In29_Cu;
251  case ALTIUM_LAYER::MID_LAYER_30: return In30_Cu;
252  case ALTIUM_LAYER::BOTTOM_LAYER: return B_Cu;
253 
254  case ALTIUM_LAYER::TOP_OVERLAY: return F_SilkS;
256  case ALTIUM_LAYER::TOP_PASTE: return F_Paste;
257  case ALTIUM_LAYER::BOTTOM_PASTE: return B_Paste;
258  case ALTIUM_LAYER::TOP_SOLDER: return F_Mask;
259  case ALTIUM_LAYER::BOTTOM_SOLDER: return B_Mask;
260 
277 
280 
281  case ALTIUM_LAYER::MECHANICAL_1: return User_1; //Edge_Cuts;
282  case ALTIUM_LAYER::MECHANICAL_2: return User_2;
283  case ALTIUM_LAYER::MECHANICAL_3: return User_3;
284  case ALTIUM_LAYER::MECHANICAL_4: return User_4;
285  case ALTIUM_LAYER::MECHANICAL_5: return User_5;
286  case ALTIUM_LAYER::MECHANICAL_6: return User_6;
287  case ALTIUM_LAYER::MECHANICAL_7: return User_7;
288  case ALTIUM_LAYER::MECHANICAL_8: return User_8;
289  case ALTIUM_LAYER::MECHANICAL_9: return User_9;
291  case ALTIUM_LAYER::MECHANICAL_11: return Eco2_User; //Eco1 is used for unknown elements
292  case ALTIUM_LAYER::MECHANICAL_12: return F_Fab;
293  case ALTIUM_LAYER::MECHANICAL_13: return B_Fab; // Don't use courtyard layers for other purposes
297 
308 
309  default: return UNDEFINED_LAYER;
310  }
311 }
312 
313 
314 ALTIUM_PCB::ALTIUM_PCB( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
315 {
316  m_board = aBoard;
317  m_progressReporter = aProgressReporter;
318  m_doneCount = 0;
320  m_totalCount = 0;
321  m_num_nets = 0;
323 }
324 
326 {
327 }
328 
330 {
331  const unsigned PROGRESS_DELTA = 250;
332 
333  if( m_progressReporter )
334  {
335  if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
336  {
338  / std::max( 1U, m_totalCount ) );
339 
341  THROW_IO_ERROR( ( "Open cancelled by user." ) );
342 
344  }
345  }
346 }
347 
348 void ALTIUM_PCB::Parse( const CFB::CompoundFileReader& aReader,
349  const std::map<ALTIUM_PCB_DIR, std::string>& aFileMapping )
350 {
351  // this vector simply declares in which order which functions to call.
352  const std::vector<std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>> parserOrder = {
354  [this]( auto aReader, auto fileHeader )
355  {
356  this->ParseFileHeader( aReader, fileHeader );
357  } },
358  { true, ALTIUM_PCB_DIR::BOARD6,
359  [this]( auto aReader, auto fileHeader )
360  {
361  this->ParseBoard6Data( aReader, fileHeader );
362  } },
364  [this]( auto aReader, auto fileHeader )
365  {
366  this->ParseComponents6Data( aReader, fileHeader );
367  } },
368  { true, ALTIUM_PCB_DIR::MODELS,
369  [this, aFileMapping]( auto aReader, auto fileHeader )
370  {
371  wxString dir( aFileMapping.at( ALTIUM_PCB_DIR::MODELS ) );
372  this->ParseModelsData( aReader, fileHeader, dir );
373  } },
375  [this]( auto aReader, auto fileHeader )
376  {
377  this->ParseComponentsBodies6Data( aReader, fileHeader );
378  } },
379  { true, ALTIUM_PCB_DIR::NETS6,
380  [this]( auto aReader, auto fileHeader )
381  {
382  this->ParseNets6Data( aReader, fileHeader );
383  } },
384  { true, ALTIUM_PCB_DIR::CLASSES6,
385  [this]( auto aReader, auto fileHeader )
386  {
387  this->ParseClasses6Data( aReader, fileHeader );
388  } },
389  { true, ALTIUM_PCB_DIR::RULES6,
390  [this]( auto aReader, auto fileHeader )
391  {
392  this->ParseRules6Data( aReader, fileHeader );
393  } },
395  [this]( auto aReader, auto fileHeader )
396  {
397  this->ParseDimensions6Data( aReader, fileHeader );
398  } },
400  [this]( auto aReader, auto fileHeader )
401  {
402  this->ParsePolygons6Data( aReader, fileHeader );
403  } },
404  { true, ALTIUM_PCB_DIR::ARCS6,
405  [this]( auto aReader, auto fileHeader )
406  {
407  this->ParseArcs6Data( aReader, fileHeader );
408  } },
409  { true, ALTIUM_PCB_DIR::PADS6,
410  [this]( auto aReader, auto fileHeader )
411  {
412  this->ParsePads6Data( aReader, fileHeader );
413  } },
414  { true, ALTIUM_PCB_DIR::VIAS6,
415  [this]( auto aReader, auto fileHeader )
416  {
417  this->ParseVias6Data( aReader, fileHeader );
418  } },
419  { true, ALTIUM_PCB_DIR::TRACKS6,
420  [this]( auto aReader, auto fileHeader )
421  {
422  this->ParseTracks6Data( aReader, fileHeader );
423  } },
425  [this]( auto aReader, auto fileHeader )
426  {
427  this->ParseWideStrings6Data( aReader, fileHeader );
428  } },
429  { true, ALTIUM_PCB_DIR::TEXTS6,
430  [this]( auto aReader, auto fileHeader )
431  {
432  this->ParseTexts6Data( aReader, fileHeader );
433  } },
434  { true, ALTIUM_PCB_DIR::FILLS6,
435  [this]( auto aReader, auto fileHeader )
436  {
437  this->ParseFills6Data( aReader, fileHeader );
438  } },
440  [this]( auto aReader, auto fileHeader )
441  {
442  this->ParseBoardRegionsData( aReader, fileHeader );
443  } },
445  [this]( auto aReader, auto fileHeader )
446  {
447  this->ParseShapeBasedRegions6Data( aReader, fileHeader );
448  } },
449  { true, ALTIUM_PCB_DIR::REGIONS6,
450  [this]( auto aReader, auto fileHeader )
451  {
452  this->ParseRegions6Data( aReader, fileHeader );
453  } }
454  };
455 
456  if( m_progressReporter != nullptr )
457  {
458  // Count number of records we will read for the progress reporter
459  for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
460  {
461  bool isRequired;
464  std::tie( isRequired, directory, fp ) = cur;
465 
467  {
468  continue;
469  }
470 
471  const auto& mappedDirectory = aFileMapping.find( directory );
472  if( mappedDirectory == aFileMapping.end() )
473  {
474  continue;
475  }
476 
477  std::string mappedFile = mappedDirectory->second + "Header";
478 
479  const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, mappedFile.c_str() );
480  if( file == nullptr )
481  {
482  continue;
483  }
484 
485  ALTIUM_PARSER reader( aReader, file );
486  uint32_t numOfRecords = reader.Read<uint32_t>();
487 
488  if( reader.HasParsingError() )
489  {
490  wxLogError( _( "'%s' was not parsed correctly." ), mappedFile );
491  continue;
492  }
493 
494  m_totalCount += numOfRecords;
495 
496  if( reader.GetRemainingBytes() != 0 )
497  {
498  wxLogError( _( "'%s' was not fully parsed." ), mappedFile );
499  continue;
500  }
501  }
502  }
503 
504  // Parse data in specified order
505  for( const std::tuple<bool, ALTIUM_PCB_DIR, PARSE_FUNCTION_POINTER_fp>& cur : parserOrder )
506  {
507  bool isRequired;
510  std::tie( isRequired, directory, fp ) = cur;
511 
512  const auto& mappedDirectory = aFileMapping.find( directory );
513 
514  if( mappedDirectory == aFileMapping.end() )
515  {
516  wxASSERT_MSG( !isRequired, wxString::Format( "Altium Directory of kind %d was expected, "
517  "but no mapping is present in the code",
518  directory ) );
519  continue;
520  }
521 
522  std::string mappedFile = mappedDirectory->second;
523 
525  mappedFile += "Data";
526 
527  const CFB::COMPOUND_FILE_ENTRY* file = FindStream( aReader, mappedFile.c_str() );
528 
529  if( file != nullptr )
530  fp( aReader, file );
531  else if( isRequired )
532  wxLogError( _( "File not found: '%s'." ), mappedFile );
533  }
534 
535  // fixup zone priorities since Altium stores them in the opposite order
536  for( ZONE* zone : m_polygons )
537  {
538  if( !zone )
539  continue;
540 
541  // Altium "fills" - not poured in Altium
542  if( zone->GetPriority() == 1000 )
543  {
544  // Unlikely, but you never know
545  if( m_highest_pour_index >= 1000 )
546  zone->SetPriority( m_highest_pour_index + 1 );
547 
548  continue;
549  }
550 
551  int priority = m_highest_pour_index - zone->GetPriority();
552 
553  zone->SetPriority( priority >= 0 ? priority : 0 );
554  }
555 
556  // change priority of outer zone to zero
557  for( std::pair<const ALTIUM_LAYER, ZONE*>& zone : m_outer_plane )
558  zone.second->SetPriority( 0 );
559 
560  // Altium doesn't appear to store either the dimension value nor the dimensioned object in
561  // the dimension record. (Yes, there is a REFERENCE0OBJECTID, but it doesn't point to the
562  // dimensioned object.) We attempt to plug this gap by finding a colocated arc or circle
563  // and using its radius. If there are more than one such arcs/circles, well, :shrug:.
565  {
566  int radius = 0;
567 
568  for( BOARD_ITEM* item : m_board->Drawings() )
569  {
570  if( item->Type() != PCB_SHAPE_T )
571  continue;
572 
573  PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
574 
575  if( shape->GetShape() != SHAPE_T::ARC && shape->GetShape() != SHAPE_T::CIRCLE )
576  continue;
577 
578  if( shape->GetPosition() == dim->GetPosition() )
579  {
580  radius = shape->GetRadius();
581  break;
582  }
583  }
584 
585  if( radius == 0 )
586  {
587  for( PCB_TRACK* track : m_board->Tracks() )
588  {
589  if( track->Type() != PCB_ARC_T )
590  continue;
591 
592  PCB_ARC* arc = static_cast<PCB_ARC*>( track );
593 
594  if( arc->GetCenter() == dim->GetPosition() )
595  {
596  radius = arc->GetRadius();
597  break;
598  }
599  }
600  }
601 
602  // Force a measured value, calculate the value text, and then stick it into the override
603  // text (since leaders don't have calculated text).
604  dim->SetMeasuredValue( radius );
605  dim->SetText( dim->GetPrefix() + dim->GetValueText() + dim->GetSuffix() );
606  dim->SetPrefix( wxEmptyString );
607  dim->SetSuffix( wxEmptyString );
608 
609  // Move the leader line start to the radius point
610  VECTOR2I radialLine = dim->GetEnd() - dim->GetStart();
611  radialLine = radialLine.Resize( radius );
612  dim->SetStart( dim->GetStart() + (wxPoint) radialLine );
613  }
614 
615  // center board
617 
618  int w = m_board->GetPageSettings().GetWidthIU();
619  int h = m_board->GetPageSettings().GetHeightIU();
620 
621  int desired_x = ( w - bbbox.GetWidth() ) / 2;
622  int desired_y = ( h - bbbox.GetHeight() ) / 2;
623 
624  wxPoint movementVector( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() );
625  m_board->Move( movementVector );
626 
628  bds.SetAuxOrigin( bds.GetAuxOrigin() + movementVector );
629  bds.SetGridOrigin( bds.GetGridOrigin() + movementVector );
630 
631  m_board->SetModified();
632 }
633 
634 int ALTIUM_PCB::GetNetCode( uint16_t aId ) const
635 {
636  if( aId == ALTIUM_NET_UNCONNECTED )
637  {
639  }
640  else if( m_num_nets < aId )
641  {
643  "Netcode with id %d does not exist. Only %d nets are known", aId, m_num_nets ) );
644  }
645  else
646  {
647  return aId + 1;
648  }
649 }
650 
651 const ARULE6* ALTIUM_PCB::GetRule( ALTIUM_RULE_KIND aKind, const wxString& aName ) const
652 {
653  const auto rules = m_rules.find( aKind );
654  if( rules == m_rules.end() )
655  {
656  return nullptr;
657  }
658  for( const ARULE6& rule : rules->second )
659  {
660  if( rule.name == aName )
661  {
662  return &rule;
663  }
664  }
665  return nullptr;
666 }
667 
669 {
670  const auto rules = m_rules.find( aKind );
671  if( rules == m_rules.end() )
672  {
673  return nullptr;
674  }
675  for( const ARULE6& rule : rules->second )
676  {
677  if( rule.scope1expr == "All" && rule.scope2expr == "All" )
678  {
679  return &rule;
680  }
681  }
682  return nullptr;
683 }
684 
685 void ALTIUM_PCB::ParseFileHeader( const CFB::CompoundFileReader& aReader,
686  const CFB::COMPOUND_FILE_ENTRY* aEntry )
687 {
688  ALTIUM_PARSER reader( aReader, aEntry );
689 
690  reader.ReadAndSetSubrecordLength();
691  wxString header = reader.ReadWxString();
692 
693  //std::cout << "HEADER: " << header << std::endl; // tells me: PCB 5.0 Binary File
694 
695  //reader.SkipSubrecord();
696 
697  // TODO: does not seem to work all the time at the moment
698  //if( reader.GetRemainingBytes() != 0 )
699  //{
700  // THROW_IO_ERROR( "FileHeader stream is not fully parsed" );
701  //}
702 }
703 
704 void ALTIUM_PCB::ParseBoard6Data( const CFB::CompoundFileReader& aReader,
705  const CFB::COMPOUND_FILE_ENTRY* aEntry )
706 {
707  if( m_progressReporter )
708  m_progressReporter->Report( "Loading board data..." );
709 
710  ALTIUM_PARSER reader( aReader, aEntry );
711 
712  checkpoint();
713  ABOARD6 elem( reader );
714 
715  if( reader.GetRemainingBytes() != 0 )
716  {
717  THROW_IO_ERROR( "Board6 stream is not fully parsed" );
718  }
719 
722 
723  // read layercount from stackup, because LAYERSETSCOUNT is not always correct?!
724  size_t layercount = 0;
725  for( size_t i = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
726  i < elem.stackup.size() && i != 0; i = elem.stackup[i - 1].nextId, layercount++ )
727  ;
728  size_t kicadLayercount = ( layercount % 2 == 0 ) ? layercount : layercount + 1;
729  m_board->SetCopperLayerCount( kicadLayercount );
730 
731  BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
732  BOARD_STACKUP& stackup = designSettings.GetStackupDescriptor();
733 
734  // create board stackup
735  stackup.RemoveAll(); // Just to be sure
736  stackup.BuildDefaultStackupList( &designSettings, layercount );
737 
738  auto it = stackup.GetList().begin();
739  // find first copper layer
740  for( ; it != stackup.GetList().end() && ( *it )->GetType() != BS_ITEM_TYPE_COPPER; ++it )
741  ;
742 
743  auto curLayer = static_cast<int>( F_Cu );
744  for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_LAYER );
745  altiumLayerId < elem.stackup.size() && altiumLayerId != 0;
746  altiumLayerId = elem.stackup[altiumLayerId - 1].nextId )
747  {
748  // array starts with 0, but stackup with 1
749  ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
750 
751  // handle unused layer in case of odd layercount
752  if( layer.nextId == 0 && layercount != kicadLayercount )
753  {
754  m_board->SetLayerName( ( *it )->GetBrdLayerId(), "[unused]" );
755 
756  if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
757  {
758  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
759  }
760  ( *it )->SetThickness( 0 );
761 
762  ++it;
763  if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
764  {
765  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
766  }
767  ( *it )->SetThickness( 0, 0 );
768  ( *it )->SetThicknessLocked( true, 0 );
769  ++it;
770  }
771 
772  m_layermap.insert( { static_cast<ALTIUM_LAYER>( altiumLayerId ),
773  static_cast<PCB_LAYER_ID>( curLayer++ ) } );
774 
775  if( ( *it )->GetType() != BS_ITEM_TYPE_COPPER )
776  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
777 
778  ( *it )->SetThickness( layer.copperthick );
779 
780  ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
781  PCB_LAYER_ID klayer = ( *it )->GetBrdLayerId();
782 
783  m_board->SetLayerName( klayer, layer.name );
784 
785  if( layer.copperthick == 0 )
786  {
787  m_board->SetLayerType( klayer, LAYER_T::LT_JUMPER ); // used for things like wirebonding
788  }
789  else if( IsAltiumLayerAPlane( alayer ) )
790  {
792  }
793 
794  if( klayer == B_Cu )
795  {
796  if( layer.nextId != 0 )
797  THROW_IO_ERROR( "Board6 stream, unexpected id while parsing last stackup layer" );
798 
799  // overwrite entry from internal -> bottom
800  m_layermap[alayer] = B_Cu;
801  break;
802  }
803 
804  ++it;
805 
806  if( ( *it )->GetType() != BS_ITEM_TYPE_DIELECTRIC )
807  THROW_IO_ERROR( "Board6 stream, unexpected item while parsing stackup" );
808 
809  ( *it )->SetThickness( layer.dielectricthick, 0 );
810  ( *it )->SetMaterial( layer.dielectricmaterial.empty() ?
811  NotSpecifiedPrm() :
812  wxString( layer.dielectricmaterial ) );
813  ( *it )->SetEpsilonR( layer.dielectricconst, 0 );
814 
815  ++it;
816  }
817 
818  // Set name of all non-cu layers
819  for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::TOP_OVERLAY );
820  altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::BOTTOM_SOLDER ); altiumLayerId++ )
821  {
822  // array starts with 0, but stackup with 1
823  ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
824 
825  ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
826  PCB_LAYER_ID klayer = GetKicadLayer( alayer );
827 
828  m_board->SetLayerName( klayer, layer.name );
829  }
830 
831  for( size_t altiumLayerId = static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_1 );
832  altiumLayerId <= static_cast<size_t>( ALTIUM_LAYER::MECHANICAL_16 ); altiumLayerId++ )
833  {
834  // array starts with 0, but stackup with 1
835  ABOARD6_LAYER_STACKUP& layer = elem.stackup.at( altiumLayerId - 1 );
836 
837  ALTIUM_LAYER alayer = static_cast<ALTIUM_LAYER>( altiumLayerId );
838  PCB_LAYER_ID klayer = GetKicadLayer( alayer );
839 
840  m_board->SetLayerName( klayer, layer.name );
841  }
842 
844 }
845 
846 void ALTIUM_PCB::HelperCreateBoardOutline( const std::vector<ALTIUM_VERTICE>& aVertices )
847 {
848  SHAPE_LINE_CHAIN lineChain;
849  HelperShapeLineChainFromAltiumVertices( lineChain, aVertices );
850 
851  for( int i = 0; i <= lineChain.PointCount() && i != -1; i = lineChain.NextShape( i ) )
852  {
853  if( lineChain.IsArcStart( i ) )
854  {
855  const SHAPE_ARC& currentArc = lineChain.Arc( lineChain.ArcIndex( i ) );
856  int nextShape = lineChain.NextShape( i );
857  bool isLastShape = nextShape < 0;
858 
859  PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::ARC );
860  m_board->Add( shape, ADD_MODE::APPEND );
861 
863  shape->SetLayer( Edge_Cuts );
864  shape->SetArcGeometry( (wxPoint) currentArc.GetP0(), (wxPoint) currentArc.GetArcMid(),
865  (wxPoint) currentArc.GetP1() );
866  }
867  else
868  {
869  const SEG& seg = lineChain.Segment( i );
870 
871  PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
872  m_board->Add( shape, ADD_MODE::APPEND );
873 
875  shape->SetLayer( Edge_Cuts );
876  shape->SetStart( (wxPoint) seg.A );
877  shape->SetEnd( (wxPoint) seg.B );
878  }
879  }
880 }
881 
882 void ALTIUM_PCB::ParseClasses6Data( const CFB::CompoundFileReader& aReader,
883  const CFB::COMPOUND_FILE_ENTRY* aEntry )
884 {
885  if( m_progressReporter )
886  m_progressReporter->Report( "Loading netclasses..." );
887 
888  ALTIUM_PARSER reader( aReader, aEntry );
889 
890  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
891  {
892  checkpoint();
893  ACLASS6 elem( reader );
894  if( elem.kind == ALTIUM_CLASS_KIND::NET_CLASS )
895  {
896  NETCLASSPTR nc = std::make_shared<NETCLASS>( elem.name );
897 
898  for( const auto& name : elem.names )
899  {
900  // TODO: it seems it can happen that we have names not attached to any net.
901  nc->Add( name );
902  }
903 
904  if( !m_board->GetDesignSettings().GetNetClasses().Add( nc ) )
905  {
906  // Name conflict, this is likely a bad board file.
907  // unique_ptr will delete nc on this code path
908  THROW_IO_ERROR( wxString::Format( _( "Duplicate netclass name '%s'." ), elem.name ) );
909  }
910  }
911  }
912 
913  if( reader.GetRemainingBytes() != 0 )
914  {
915  THROW_IO_ERROR( "Classes6 stream is not fully parsed" );
916  }
917 
919 }
920 
921 void ALTIUM_PCB::ParseComponents6Data( const CFB::CompoundFileReader& aReader,
922  const CFB::COMPOUND_FILE_ENTRY* aEntry )
923 {
924  if( m_progressReporter )
925  m_progressReporter->Report( "Loading components..." );
926 
927  ALTIUM_PARSER reader( aReader, aEntry );
928 
929  uint16_t componentId = 0;
930  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
931  {
932  checkpoint();
933  ACOMPONENT6 elem( reader );
934 
935  FOOTPRINT* footprint = new FOOTPRINT( m_board );
936  m_board->Add( footprint, ADD_MODE::APPEND );
937  m_components.emplace_back( footprint );
938 
940 
941  footprint->SetFPID( fpID );
942 
943  footprint->SetPosition( elem.position );
944  footprint->SetOrientationDegrees( elem.rotation );
945 
946  // KiCad netlisting requires parts to have non-digit + digit annotation.
947  // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
948  wxString reference = elem.sourcedesignator;
949  if( reference.find_first_not_of( "0123456789" ) == wxString::npos )
950  reference.Prepend( "UNK" );
951  footprint->SetReference( reference );
952 
953  footprint->SetLocked( elem.locked );
954  footprint->Reference().SetVisible( elem.nameon );
955  footprint->Value().SetVisible( elem.commenton );
956  footprint->SetLayer( elem.layer == ALTIUM_LAYER::TOP_LAYER ? F_Cu : B_Cu );
957 
958  componentId++;
959  }
960 
961  if( reader.GetRemainingBytes() != 0 )
962  {
963  THROW_IO_ERROR( "Components6 stream is not fully parsed" );
964  }
965 }
966 
967 
968 void ALTIUM_PCB::ParseComponentsBodies6Data( const CFB::CompoundFileReader& aReader,
969  const CFB::COMPOUND_FILE_ENTRY* aEntry )
970 {
971  if( m_progressReporter )
972  m_progressReporter->Report( "Loading component 3D models..." );
973 
974  ALTIUM_PARSER reader( aReader, aEntry );
975 
976  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
977  {
978  checkpoint();
979  ACOMPONENTBODY6 elem( reader ); // TODO: implement
980 
981  if( elem.component == ALTIUM_COMPONENT_NONE )
982  continue; // TODO: we do not support components for the board yet
983 
984  if( m_components.size() <= elem.component )
985  {
987  "ComponentsBodies6 stream tries to access component id %d of %d existing components",
988  elem.component, m_components.size() ) );
989  }
990 
991  if( !elem.modelIsEmbedded )
992  continue;
993 
994  auto modelTuple = m_models.find( elem.modelId );
995 
996  if( modelTuple == m_models.end() )
997  {
999  "ComponentsBodies6 stream tries to access model id %s which does not exist",
1000  elem.modelId ) );
1001  }
1002 
1003  FOOTPRINT* footprint = m_components.at( elem.component );
1004  const wxPoint& fpPosition = footprint->GetPosition();
1005 
1006  FP_3DMODEL modelSettings;
1007 
1008  modelSettings.m_Filename = modelTuple->second;
1009 
1010  modelSettings.m_Offset.x = Iu2Millimeter((int) elem.modelPosition.x - fpPosition.x );
1011  modelSettings.m_Offset.y = -Iu2Millimeter((int) elem.modelPosition.y - fpPosition.y );
1012  modelSettings.m_Offset.z = Iu2Millimeter( (int) elem.modelPosition.z );
1013 
1014  double orientation = footprint->GetOrientation();
1015 
1016  if( footprint->IsFlipped() )
1017  {
1018  modelSettings.m_Offset.y = -modelSettings.m_Offset.y;
1019  orientation = -orientation;
1020  }
1021 
1022  RotatePoint( &modelSettings.m_Offset.x, &modelSettings.m_Offset.y, orientation );
1023 
1024  modelSettings.m_Rotation.x = NormalizeAngleDegrees( -elem.modelRotation.x, -180, 180 );
1025  modelSettings.m_Rotation.y = NormalizeAngleDegrees( -elem.modelRotation.y, -180, 180 );
1026  modelSettings.m_Rotation.z = NormalizeAngleDegrees( -elem.modelRotation.z
1027  + elem.rotation
1028  + orientation / 10, -180, 180 );
1029  modelSettings.m_Opacity = elem.bodyOpacity;
1030 
1031  footprint->Models().push_back( modelSettings );
1032  }
1033 
1034  if( reader.GetRemainingBytes() != 0 )
1035  THROW_IO_ERROR( "ComponentsBodies6 stream is not fully parsed" );
1036 }
1037 
1038 
1040 {
1041  if( aElem.referencePoint.size() != 2 )
1042  THROW_IO_ERROR( "Incorrect number of reference points for linear dimension object" );
1043 
1044  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1045 
1046  if( klayer == UNDEFINED_LAYER )
1047  {
1048  wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1049  "It has been moved to KiCad layer Eco1_User." ),
1050  aElem.layer );
1051  klayer = Eco1_User;
1052  }
1053 
1054  wxPoint referencePoint0 = aElem.referencePoint.at( 0 );
1055  wxPoint referencePoint1 = aElem.referencePoint.at( 1 );
1056 
1057  PCB_DIM_ALIGNED* dimension = new PCB_DIM_ALIGNED( m_board );
1058  m_board->Add( dimension, ADD_MODE::APPEND );
1059 
1060  dimension->SetPrecision( aElem.textprecision );
1061  dimension->SetLayer( klayer );
1062  dimension->SetStart( referencePoint0 );
1063 
1064  if( referencePoint0 != aElem.xy1 )
1065  {
1075  wxPoint direction = aElem.xy1 - referencePoint0;
1076  wxPoint directionNormalVector = wxPoint( -direction.y, direction.x );
1077  SEG segm1( referencePoint0, referencePoint0 + directionNormalVector );
1078  SEG segm2( referencePoint1, referencePoint1 + direction );
1079  wxPoint intersection( segm1.Intersect( segm2, true, true ).get() );
1080  dimension->SetEnd( intersection );
1081 
1082  int height = static_cast<int>( EuclideanNorm( direction ) );
1083 
1084  if( direction.x <= 0 && direction.y <= 0 ) // TODO: I suspect this is not always correct
1085  height = -height;
1086 
1087  dimension->SetHeight( height );
1088  }
1089  else
1090  {
1091  dimension->SetEnd( referencePoint1 );
1092  }
1093 
1094  dimension->SetLineThickness( aElem.linewidth );
1095 
1096  dimension->SetPrefix( aElem.textprefix );
1097 
1098  // Suffix normally holds the units
1099  dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1101 
1102  dimension->Text().SetTextThickness( aElem.textlinewidth );
1103  dimension->Text().SetTextSize( wxSize( aElem.textheight, aElem.textheight ) );
1104  dimension->Text().SetBold( aElem.textbold );
1105  dimension->Text().SetItalic( aElem.textitalic );
1106 
1107  switch( aElem.textunit )
1108  {
1109  case ALTIUM_UNIT::INCHES:
1110  dimension->SetUnits( EDA_UNITS::INCHES );
1111  break;
1112  case ALTIUM_UNIT::MILS:
1113  dimension->SetUnits( EDA_UNITS::MILS );
1114  break;
1117  dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1118  break;
1119  default:
1120  break;
1121  }
1122 }
1123 
1124 
1126 {
1127  if( aElem.referencePoint.size() < 2 )
1128  THROW_IO_ERROR( "Not enough reference points for radial dimension object" );
1129 
1130  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1131 
1132  if( klayer == UNDEFINED_LAYER )
1133  {
1134  wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1135  "It has been moved to KiCad layer Eco1_User." ),
1136  aElem.layer );
1137  klayer = Eco1_User;
1138  }
1139 
1140  wxPoint referencePoint0 = aElem.referencePoint.at( 0 );
1141  wxPoint referencePoint1 = aElem.referencePoint.at( 1 );
1142 
1143  //
1144  // We don't have radial dimensions yet so fake it with a leader:
1145 
1146  PCB_DIM_LEADER* dimension = new PCB_DIM_LEADER( m_board );
1147  m_board->Add( dimension, ADD_MODE::APPEND );
1148  m_radialDimensions.push_back( dimension );
1149 
1150  dimension->SetPrecision( aElem.textprecision );
1151  dimension->SetLayer( klayer );
1152  dimension->SetStart( referencePoint0 );
1153  dimension->SetEnd( aElem.xy1 );
1154  dimension->SetLineThickness( aElem.linewidth );
1155 
1156  dimension->SetPrefix( aElem.textprefix );
1157 
1158  // Suffix normally holds the units
1159  dimension->SetUnitsFormat( aElem.textsuffix.IsEmpty() ? DIM_UNITS_FORMAT::NO_SUFFIX
1161 
1162  switch( aElem.textunit )
1163  {
1164  case ALTIUM_UNIT::INCHES:
1165  dimension->SetUnits( EDA_UNITS::INCHES );
1166  break;
1167  case ALTIUM_UNIT::MILS:
1168  dimension->SetUnits( EDA_UNITS::MILS );
1169  break;
1172  dimension->SetUnits( EDA_UNITS::MILLIMETRES );
1173  break;
1174  default:
1175  break;
1176  }
1177 
1178  if( aElem.textPoint.empty() )
1179  {
1180  wxLogError( "No text position present for leader dimension object" );
1181  return;
1182  }
1183 
1184  dimension->Text().SetPosition( aElem.textPoint.at( 0 ) );
1185  dimension->Text().SetTextThickness( aElem.textlinewidth );
1186  dimension->Text().SetTextSize( wxSize( aElem.textheight, aElem.textheight ) );
1187  dimension->Text().SetBold( aElem.textbold );
1188  dimension->Text().SetItalic( aElem.textitalic );
1191 
1192  int yAdjust = dimension->Text().GetCenter().y - dimension->Text().GetPosition().y;
1193  dimension->Text().Move( wxPoint( 0, yAdjust + aElem.textgap ) );
1195 }
1196 
1197 
1199 {
1200  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1201 
1202  if( klayer == UNDEFINED_LAYER )
1203  {
1204  wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1205  "It has been moved to KiCad layer Eco1_User." ),
1206  aElem.layer );
1207  klayer = Eco1_User;
1208  }
1209 
1210  if( !aElem.referencePoint.empty() )
1211  {
1212  wxPoint referencePoint0 = aElem.referencePoint.at( 0 );
1213 
1214  // line
1215  wxPoint last = referencePoint0;
1216  for( size_t i = 1; i < aElem.referencePoint.size(); i++ )
1217  {
1218  PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1219  m_board->Add( shape, ADD_MODE::APPEND );
1220  shape->SetLayer( klayer );
1221  shape->SetWidth( aElem.linewidth );
1222  shape->SetStart( last );
1223  shape->SetEnd( aElem.referencePoint.at( i ) );
1224  last = aElem.referencePoint.at( i );
1225  }
1226 
1227  // arrow
1228  if( aElem.referencePoint.size() >= 2 )
1229  {
1230  wxPoint dirVec = aElem.referencePoint.at( 1 ) - referencePoint0;
1231  if( dirVec.x != 0 || dirVec.y != 0 )
1232  {
1233  double scaling = EuclideanNorm( dirVec ) / aElem.arrowsize;
1234  wxPoint arrVec =
1235  wxPoint( KiROUND( dirVec.x / scaling ), KiROUND( dirVec.y / scaling ) );
1236  RotatePoint( &arrVec, 200. );
1237 
1238  PCB_SHAPE* shape1 = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1239  m_board->Add( shape1, ADD_MODE::APPEND );
1240  shape1->SetLayer( klayer );
1241  shape1->SetWidth( aElem.linewidth );
1242  shape1->SetStart( referencePoint0 );
1243  shape1->SetEnd( referencePoint0 + arrVec );
1244 
1245  RotatePoint( &arrVec, -400. );
1246 
1247  PCB_SHAPE* shape2 = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1248  m_board->Add( shape2, ADD_MODE::APPEND );
1249  shape2->SetLayer( klayer );
1250  shape2->SetWidth( aElem.linewidth );
1251  shape2->SetStart( referencePoint0 );
1252  shape2->SetEnd( referencePoint0 + arrVec );
1253  }
1254  }
1255  }
1256 
1257  if( aElem.textPoint.empty() )
1258  {
1259  wxLogError( "No text position present for leader dimension object" );
1260  return;
1261  }
1262 
1263  PCB_TEXT* text = new PCB_TEXT( m_board );
1265  text->SetText( aElem.textformat );
1266  text->SetPosition( aElem.textPoint.at( 0 ) );
1267  text->SetLayer( klayer );
1268  text->SetTextSize( wxSize( aElem.textheight, aElem.textheight ) ); // TODO: parse text width
1269  text->SetTextThickness( aElem.textlinewidth );
1272 }
1273 
1274 
1276 {
1277  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1278 
1279  if( klayer == UNDEFINED_LAYER )
1280  {
1281  wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1282  "It has been moved to KiCad layer Eco1_User." ),
1283  aElem.layer );
1284  klayer = Eco1_User;
1285  }
1286 
1287  for( size_t i = 0; i < aElem.referencePoint.size(); i++ )
1288  {
1289  PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::SEGMENT );
1290  m_board->Add( shape, ADD_MODE::APPEND );
1291  shape->SetLayer( klayer );
1292  shape->SetWidth( aElem.linewidth );
1293  shape->SetStart( aElem.referencePoint.at( i ) );
1294  // shape->SetEnd( /* TODO: seems to be based on TEXTY */ );
1295  }
1296 }
1297 
1298 
1300 {
1301  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
1302 
1303  if( klayer == UNDEFINED_LAYER )
1304  {
1305  wxLogWarning( _( "Dimension found on an Altium layer (%d) with no KiCad equivalent. "
1306  "It has been moved to KiCad layer Eco1_User." ),
1307  aElem.layer );
1308  klayer = Eco1_User;
1309  }
1310 
1311  wxPoint vec = wxPoint( 0, aElem.height / 2 );
1312  RotatePoint( &vec, aElem.angle * 10. );
1313 
1314  PCB_DIM_CENTER* dimension = new PCB_DIM_CENTER( m_board );
1315  m_board->Add( dimension, ADD_MODE::APPEND );
1316  dimension->SetLayer( klayer );
1317  dimension->SetLineThickness( aElem.linewidth );
1318  dimension->SetStart( aElem.xy1 );
1319  dimension->SetEnd( aElem.xy1 + vec );
1320 }
1321 
1322 
1323 void ALTIUM_PCB::ParseDimensions6Data( const CFB::CompoundFileReader& aReader,
1324  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1325 {
1326  if( m_progressReporter )
1327  m_progressReporter->Report( "Loading dimension drawings..." );
1328 
1329  ALTIUM_PARSER reader( aReader, aEntry );
1330 
1331  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1332  {
1333  checkpoint();
1334  ADIMENSION6 elem( reader );
1335 
1336  switch( elem.kind )
1337  {
1340  break;
1343  break;
1346  break;
1348  wxLogError( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1349  // HelperParseDimensions6Datum( elem );
1350  break;
1353  break;
1354  default:
1355  wxLogError( _( "Ignored dimension of kind %d (not yet supported)." ), elem.kind );
1356  break;
1357  }
1358  }
1359 
1360  if( reader.GetRemainingBytes() != 0 )
1361  THROW_IO_ERROR( "Dimensions6 stream is not fully parsed" );
1362 }
1363 
1364 
1365 void ALTIUM_PCB::ParseModelsData( const CFB::CompoundFileReader& aReader,
1366  const CFB::COMPOUND_FILE_ENTRY* aEntry, const wxString& aRootDir )
1367 {
1368  if( m_progressReporter )
1369  m_progressReporter->Report( "Loading 3D models..." );
1370 
1371  ALTIUM_PARSER reader( aReader, aEntry );
1372 
1373  if( reader.GetRemainingBytes() == 0 )
1374  return;
1375 
1376  wxString projectPath = wxPathOnly( m_board->GetFileName() );
1377  // TODO: set KIPRJMOD always after import (not only when loading project)?
1378  wxSetEnv( PROJECT_VAR_NAME, projectPath );
1379 
1380  // TODO: make this path configurable?
1381  const wxString altiumModelDir = "ALTIUM_EMBEDDED_MODELS";
1382 
1383  wxFileName altiumModelsPath = wxFileName::DirName( projectPath );
1384  wxString kicadModelPrefix = "${KIPRJMOD}/" + altiumModelDir + "/";
1385 
1386  if( !altiumModelsPath.AppendDir( altiumModelDir ) )
1387  {
1388  THROW_IO_ERROR( "Cannot construct directory path for step models" );
1389  }
1390 
1391  // Create dir if it does not exist
1392  if( !altiumModelsPath.DirExists() )
1393  {
1394  if( !altiumModelsPath.Mkdir() )
1395  {
1396  wxLogError( _( "Failed to create folder '%s'." ) + wxS( " " )
1397  + _( "No 3D-models will be imported." ),
1398  altiumModelsPath.GetFullPath() );
1399  return;
1400  }
1401  }
1402 
1403  int idx = 0;
1404  wxString invalidChars = wxFileName::GetForbiddenChars();
1405 
1406  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1407  {
1408  checkpoint();
1409  AMODEL elem( reader );
1410 
1411  wxString stepPath = wxString::Format( aRootDir + "%d", idx );
1412  bool validName = !elem.name.IsEmpty() && elem.name.IsAscii() &&
1413  wxString::npos == elem.name.find_first_of( invalidChars );
1414  wxString storageName = !validName ? wxString::Format( "model_%d", idx ) : elem.name;
1415  wxFileName storagePath( altiumModelsPath.GetPath(), storageName );
1416 
1417  idx++;
1418 
1419  const CFB::COMPOUND_FILE_ENTRY* stepEntry = FindStream( aReader, stepPath.c_str() );
1420 
1421  if( stepEntry == nullptr )
1422  {
1423  wxLogError( _( "File not found: '%s'. 3D-model not imported." ), stepPath );
1424  continue;
1425  }
1426 
1427  size_t stepSize = static_cast<size_t>( stepEntry->size );
1428  std::unique_ptr<char[]> stepContent( new char[stepSize] );
1429 
1430  // read file into buffer
1431  aReader.ReadFile( stepEntry, 0, stepContent.get(), stepSize );
1432 
1433  if( !storagePath.IsDirWritable() )
1434  {
1435  wxLogError( _( "Insufficient permissions to save file '%s'." ),
1436  storagePath.GetFullPath() );
1437  continue;
1438  }
1439 
1440  wxMemoryInputStream stepStream( stepContent.get(), stepSize );
1441  wxZlibInputStream zlibInputStream( stepStream );
1442 
1443  wxFFileOutputStream outputStream( storagePath.GetFullPath() );
1444  outputStream.Write( zlibInputStream );
1445  outputStream.Close();
1446 
1447  m_models.insert( { elem.id, kicadModelPrefix + storageName } );
1448  }
1449 
1450  if( reader.GetRemainingBytes() != 0 )
1451  THROW_IO_ERROR( "Models stream is not fully parsed" );
1452 }
1453 
1454 
1455 void ALTIUM_PCB::ParseNets6Data( const CFB::CompoundFileReader& aReader,
1456  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1457 {
1458  if( m_progressReporter )
1459  m_progressReporter->Report( _( "Loading nets..." ) );
1460 
1461  ALTIUM_PARSER reader( aReader, aEntry );
1462 
1463  wxASSERT( m_num_nets == 0 );
1464  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1465  {
1466  checkpoint();
1467  ANET6 elem( reader );
1468 
1470  }
1471 
1472  if( reader.GetRemainingBytes() != 0 )
1473  THROW_IO_ERROR( "Nets6 stream is not fully parsed" );
1474 }
1475 
1476 void ALTIUM_PCB::ParsePolygons6Data( const CFB::CompoundFileReader& aReader,
1477  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1478 {
1479  if( m_progressReporter )
1480  m_progressReporter->Report( _( "Loading polygons..." ) );
1481 
1482  ALTIUM_PARSER reader( aReader, aEntry );
1483 
1484  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1485  {
1486  checkpoint();
1487  APOLYGON6 elem( reader );
1488 
1489  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1490 
1491  if( klayer == UNDEFINED_LAYER )
1492  {
1493  wxLogWarning( _( "Polygon found on an Altium layer (%d) with no KiCad equivalent. "
1494  "It has been moved to KiCad layer Eco1_User." ),
1495  elem.layer );
1496  klayer = Eco1_User;
1497  }
1498 
1499  SHAPE_LINE_CHAIN linechain;
1501 
1502  if( linechain.PointCount() < 2 )
1503  {
1504  // We have found multiple Altium files with polygon records containing nothing but two
1505  // coincident vertices. These polygons do not appear when opening the file in Altium.
1506  // https://gitlab.com/kicad/code/kicad/-/issues/8183
1507  //
1508  // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At least 2 "
1509  // "points are required." ),
1510  // linechain.PointCount(),
1511  // elem.vertices.size() );
1512 
1513  m_polygons.emplace_back( nullptr );
1514  continue;
1515  }
1516 
1517  ZONE* zone = new ZONE( m_board );
1518  m_board->Add( zone, ADD_MODE::APPEND );
1519  m_polygons.emplace_back( zone );
1520 
1521  zone->SetFillVersion( 6 );
1522  zone->SetNetCode( GetNetCode( elem.net ) );
1523  zone->SetLayer( klayer );
1524  zone->SetPosition( elem.vertices.at( 0 ).position );
1525  zone->SetLocked( elem.locked );
1526  zone->SetPriority( elem.pourindex > 0 ? elem.pourindex : 0 );
1527  zone->Outline()->AddOutline( linechain );
1528 
1529  if( elem.pourindex > m_highest_pour_index )
1531 
1532  // TODO: more flexible rule parsing
1533  const ARULE6* clearanceRule = GetRuleDefault( ALTIUM_RULE_KIND::PLANE_CLEARANCE );
1534 
1535  if( clearanceRule != nullptr )
1536  zone->SetLocalClearance( clearanceRule->planeclearanceClearance );
1537 
1538  const ARULE6* polygonConnectRule = GetRuleDefault( ALTIUM_RULE_KIND::POLYGON_CONNECT );
1539 
1540  if( polygonConnectRule != nullptr )
1541  {
1542  switch( polygonConnectRule->polygonconnectStyle )
1543  {
1546  break;
1547 
1550  break;
1551 
1552  default:
1555  break;
1556  }
1557 
1558  // TODO: correct variables?
1560  polygonConnectRule->polygonconnectReliefconductorwidth );
1561  zone->SetThermalReliefGap( polygonConnectRule->polygonconnectAirgapwidth );
1562 
1563  if( polygonConnectRule->polygonconnectReliefconductorwidth < zone->GetMinThickness() )
1564  zone->SetMinThickness( polygonConnectRule->polygonconnectReliefconductorwidth );
1565  }
1566 
1567  if( IsAltiumLayerAPlane( elem.layer ) )
1568  {
1569  // outer zone will be set to priority 0 later.
1570  zone->SetPriority( 1 );
1571 
1572  // check if this is the outer zone by simply comparing the BBOX
1573  const auto& outer_plane = m_outer_plane.find( elem.layer );
1574  if( outer_plane == m_outer_plane.end()
1575  || zone->GetBoundingBox().Contains( outer_plane->second->GetBoundingBox() ) )
1576  {
1577  m_outer_plane[elem.layer] = zone;
1578  }
1579  }
1580 
1583  {
1585  zone->SetHatchThickness( elem.trackwidth );
1586 
1588  {
1589  // use a small hack to get us only an outline (hopefully)
1590  const EDA_RECT& bbox = zone->GetBoundingBox();
1591  zone->SetHatchGap( std::max( bbox.GetHeight(), bbox.GetWidth() ) );
1592  }
1593  else
1594  {
1595  zone->SetHatchGap( elem.gridsize - elem.trackwidth );
1596  }
1597 
1599  }
1600 
1602  ZONE::GetDefaultHatchPitch(), true );
1603  }
1604 
1605  if( reader.GetRemainingBytes() != 0 )
1606  {
1607  THROW_IO_ERROR( "Polygons6 stream is not fully parsed" );
1608  }
1609 }
1610 
1611 void ALTIUM_PCB::ParseRules6Data( const CFB::CompoundFileReader& aReader,
1612  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1613 {
1614  if( m_progressReporter )
1615  m_progressReporter->Report( _( "Loading rules..." ) );
1616 
1617  ALTIUM_PARSER reader( aReader, aEntry );
1618 
1619  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1620  {
1621  checkpoint();
1622  ARULE6 elem( reader );
1623 
1624  m_rules[elem.kind].emplace_back( elem );
1625  }
1626 
1627  // sort rules by priority
1628  for( auto&& val : m_rules )
1629  {
1630  std::sort( val.second.begin(), val.second.end(),
1631  []( const auto& lhs, const auto& rhs )
1632  {
1633  return lhs.priority < rhs.priority;
1634  } );
1635  }
1636 
1637  if( reader.GetRemainingBytes() != 0 )
1638  {
1639  THROW_IO_ERROR( "Rules6 stream is not fully parsed" );
1640  }
1641 }
1642 
1643 void ALTIUM_PCB::ParseBoardRegionsData( const CFB::CompoundFileReader& aReader,
1644  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1645 {
1646  if( m_progressReporter )
1647  m_progressReporter->Report( _( "Loading board regions..." ) );
1648 
1649  ALTIUM_PARSER reader( aReader, aEntry );
1650 
1651  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1652  {
1653  checkpoint();
1654  AREGION6 elem( reader, false );
1655 
1656  // TODO: implement?
1657  }
1658 
1659  if( reader.GetRemainingBytes() != 0 )
1660  {
1661  THROW_IO_ERROR( "BoardRegions stream is not fully parsed" );
1662  }
1663 }
1664 
1665 void ALTIUM_PCB::ParseShapeBasedRegions6Data( const CFB::CompoundFileReader& aReader,
1666  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1667 {
1668  if( m_progressReporter )
1669  m_progressReporter->Report( _( "Loading zones..." ) );
1670 
1671  ALTIUM_PARSER reader( aReader, aEntry );
1672 
1673  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1674  {
1675  checkpoint();
1676  AREGION6 elem( reader, true );
1677 
1679  {
1681  }
1682  else if( elem.kind == ALTIUM_REGION_KIND::POLYGON_CUTOUT || elem.is_keepout )
1683  {
1684  SHAPE_LINE_CHAIN linechain;
1686 
1687  if( linechain.PointCount() < 2 )
1688  {
1689  // We have found multiple Altium files with polygon records containing nothing but
1690  // two coincident vertices. These polygons do not appear when opening the file in
1691  // Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
1692  //
1693  // wxLogError( _( "ShapeBasedRegion has only %d point extracted from %ld vertices. "
1694  // "At least 2 points are required." ),
1695  // linechain.PointCount(),
1696  // elem.outline.size() );
1697  continue;
1698  }
1699 
1700  ZONE* zone = new ZONE( m_board );
1701  m_board->Add( zone, ADD_MODE::APPEND );
1702 
1703  zone->SetFillVersion( 6 );
1704  zone->SetIsRuleArea( true );
1705  zone->SetDoNotAllowTracks( false );
1706  zone->SetDoNotAllowVias( false );
1707  zone->SetDoNotAllowPads( false );
1708  zone->SetDoNotAllowFootprints( false );
1709  zone->SetDoNotAllowCopperPour( true );
1710 
1711  zone->SetPosition( elem.outline.at( 0 ).position );
1712  zone->Outline()->AddOutline( linechain );
1713 
1714  if( elem.layer == ALTIUM_LAYER::MULTI_LAYER )
1715  {
1716  zone->SetLayer( F_Cu );
1717  zone->SetLayerSet( LSET::AllCuMask() );
1718  }
1719  else
1720  {
1721  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1722 
1723  if( klayer == UNDEFINED_LAYER )
1724  {
1725  wxLogWarning( _( "Zone found on an Altium layer (%d) with no KiCad equivalent. "
1726  "It has been moved to KiCad layer Eco1_User." ),
1727  elem.layer );
1728  klayer = Eco1_User;
1729  }
1730  zone->SetLayer( klayer );
1731  }
1732 
1734  ZONE::GetDefaultHatchPitch(), true );
1735  }
1736  else if( elem.kind == ALTIUM_REGION_KIND::COPPER )
1737  {
1738  if( elem.subpolyindex == ALTIUM_POLYGON_NONE )
1739  {
1740  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1741 
1742  if( klayer == UNDEFINED_LAYER )
1743  {
1744  wxLogWarning( _( "Polygon found on an Altium layer (%d) with no KiCad equivalent. "
1745  "It has been moved to KiCad layer Eco1_User." ),
1746  elem.layer );
1747  klayer = Eco1_User;
1748  }
1749 
1750  SHAPE_LINE_CHAIN linechain;
1752 
1753  if( linechain.PointCount() < 2 )
1754  {
1755  // We have found multiple Altium files with polygon records containing nothing
1756  // but two coincident vertices. These polygons do not appear when opening the
1757  // file in Altium. https://gitlab.com/kicad/code/kicad/-/issues/8183
1758  //
1759  // wxLogError( _( "Polygon has only %d point extracted from %ld vertices. At "
1760  // "least 2 points are required." ),
1761  // linechain.PointCount(),
1762  // elem.outline.size() );
1763 
1764  continue;
1765  }
1766 
1767  PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::POLY );
1768  m_board->Add( shape, ADD_MODE::APPEND );
1769  shape->SetFilled( true );
1770  shape->SetLayer( klayer );
1771  shape->SetWidth( 0 );
1772 
1773  shape->SetPolyShape( linechain );
1774  }
1775  }
1776  else
1777  {
1778  wxLogError( _( "Ignored polygon shape of kind %d (not yet supported)." ), elem.kind );
1779  }
1780  }
1781 
1782  if( reader.GetRemainingBytes() != 0 )
1783  {
1784  THROW_IO_ERROR( "ShapeBasedRegions6 stream is not fully parsed" );
1785  }
1786 }
1787 
1788 void ALTIUM_PCB::ParseRegions6Data( const CFB::CompoundFileReader& aReader,
1789  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1790 {
1791  if( m_progressReporter )
1792  m_progressReporter->Report( _( "Loading zone fills..." ) );
1793 
1794  ALTIUM_PARSER reader( aReader, aEntry );
1795 
1796  for( ZONE* zone : m_polygons )
1797  {
1798  if( zone )
1799  zone->UnFill(); // just to be sure
1800  }
1801 
1802  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1803  {
1804  checkpoint();
1805  AREGION6 elem( reader, false );
1806 
1807  if( elem.subpolyindex != ALTIUM_POLYGON_NONE )
1808  {
1809  if( m_polygons.size() <= elem.subpolyindex )
1810  {
1811  THROW_IO_ERROR( wxString::Format( "Region stream tries to access polygon id %d "
1812  "of %d existing polygons.",
1813  elem.subpolyindex,
1814  m_polygons.size() ) );
1815  }
1816 
1817  ZONE *zone = m_polygons.at( elem.subpolyindex );
1818 
1819  if( zone == nullptr )
1820  {
1821  continue; // we know the zone id, but because we do not know the layer we did not add it!
1822  }
1823 
1824  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1825  if( klayer == UNDEFINED_LAYER )
1826  {
1827  continue; // Just skip it for now. Users can fill it themselves.
1828  }
1829 
1830  SHAPE_LINE_CHAIN linechain;
1831  for( const ALTIUM_VERTICE& vertice : elem.outline )
1832  {
1833  linechain.Append( vertice.position );
1834  }
1835  linechain.Append( elem.outline.at( 0 ).position );
1836  linechain.SetClosed( true );
1837 
1838  SHAPE_POLY_SET rawPolys;
1839  rawPolys.AddOutline( linechain );
1840 
1841  for( const std::vector<ALTIUM_VERTICE>& hole : elem.holes )
1842  {
1843  SHAPE_LINE_CHAIN hole_linechain;
1844  for( const ALTIUM_VERTICE& vertice : hole )
1845  {
1846  hole_linechain.Append( vertice.position );
1847  }
1848  hole_linechain.Append( hole.at( 0 ).position );
1849  hole_linechain.SetClosed( true );
1850  rawPolys.AddHole( hole_linechain );
1851  }
1852 
1853  if( zone->GetFilledPolysUseThickness() )
1854  rawPolys.Deflate( zone->GetMinThickness() / 2, 32 );
1855 
1856  if( zone->HasFilledPolysForLayer( klayer ) )
1857  rawPolys.BooleanAdd( zone->RawPolysList( klayer ),
1859 
1860  SHAPE_POLY_SET finalPolys = rawPolys;
1862 
1863  zone->SetRawPolysList( klayer, rawPolys );
1864  zone->SetFilledPolysList( klayer, finalPolys );
1865  zone->SetIsFilled( true );
1866  zone->SetNeedRefill( false );
1867  }
1868  }
1869 
1870  if( reader.GetRemainingBytes() != 0 )
1871  {
1872  THROW_IO_ERROR( "Regions6 stream is not fully parsed" );
1873  }
1874 }
1875 
1876 
1877 void ALTIUM_PCB::ParseArcs6Data( const CFB::CompoundFileReader& aReader,
1878  const CFB::COMPOUND_FILE_ENTRY* aEntry )
1879 {
1880  if( m_progressReporter )
1881  m_progressReporter->Report( _( "Loading arcs..." ) );
1882 
1883  ALTIUM_PARSER reader( aReader, aEntry );
1884 
1885  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
1886  {
1887  checkpoint();
1888  AARC6 elem( reader );
1889 
1890  if( elem.is_polygonoutline || elem.subpolyindex != ALTIUM_POLYGON_NONE )
1891  continue;
1892 
1893  // element in plane is in fact substracted from the plane. Should be already done by Altium?
1894  //if( IsAltiumLayerAPlane( elem.layer ) )
1895  // continue;
1896 
1897  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1898 
1899  if( elem.is_keepout || IsAltiumLayerAPlane( elem.layer ) )
1900  {
1901  PCB_SHAPE shape( nullptr ); // just a helper to get the graphic
1902  shape.SetWidth( elem.width );
1903 
1904  if( elem.startangle == 0. && elem.endangle == 360. )
1905  { // TODO: other variants to define circle?
1906  shape.SetShape( SHAPE_T::CIRCLE );
1907  shape.SetStart( elem.center );
1908  shape.SetEnd( elem.center - wxPoint( 0, elem.radius ) );
1909  }
1910  else
1911  {
1912  shape.SetShape( SHAPE_T::ARC );
1913 
1914  double includedAngle = elem.endangle - elem.startangle;
1915  double startradiant = DEG2RAD( elem.startangle );
1916  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
1917  -KiROUND( std::sin( startradiant ) * elem.radius ) );
1918 
1919  shape.SetCenter( elem.center );
1920  shape.SetStart( elem.center + arcStartOffset );
1921  shape.SetArcAngleAndEnd( -NormalizeAngleDegreesPos( includedAngle ) * 10.0, true );
1922  }
1923 
1924  ZONE* zone = new ZONE( m_board );
1925  m_board->Add( zone, ADD_MODE::APPEND );
1926 
1927  zone->SetFillVersion( 6 );
1928  zone->SetIsRuleArea( true );
1929  zone->SetDoNotAllowTracks( false );
1930  zone->SetDoNotAllowVias( false );
1931  zone->SetDoNotAllowPads( false );
1932  zone->SetDoNotAllowFootprints( false );
1933  zone->SetDoNotAllowCopperPour( true );
1934 
1935  if( elem.layer == ALTIUM_LAYER::MULTI_LAYER )
1936  {
1937  zone->SetLayer( F_Cu );
1938  zone->SetLayerSet( LSET::AllCuMask() );
1939  }
1940  else
1941  {
1942  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
1943 
1944  if( klayer == UNDEFINED_LAYER )
1945  {
1946  wxLogWarning( _( "Arc keepout found on an Altium layer (%d) with no KiCad "
1947  "equivalent. It has been moved to KiCad layer Eco1_User." ),
1948  elem.layer );
1949  klayer = Eco1_User;
1950  }
1951  zone->SetLayer( klayer );
1952  }
1953 
1954  shape.TransformShapeWithClearanceToPolygon( *zone->Outline(), klayer, 0, ARC_HIGH_DEF,
1955  ERROR_INSIDE );
1956  zone->Outline()->Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); // the outline is not a single polygon!
1957 
1959  ZONE::GetDefaultHatchPitch(), true );
1960  continue;
1961  }
1962 
1963  if( klayer == UNDEFINED_LAYER )
1964  {
1965  wxLogWarning( _( "Arc found on an Altium layer (%d) with no KiCad equivalent. "
1966  "It has been moved to KiCad layer Eco1_User." ),
1967  elem.layer );
1968  klayer = Eco1_User;
1969  }
1970 
1971  if( klayer >= F_Cu && klayer <= B_Cu )
1972  {
1973  double angle = -NormalizeAngleDegreesPos( elem.endangle - elem.startangle );
1974  double startradiant = DEG2RAD( elem.startangle );
1975  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
1976  -KiROUND( std::sin( startradiant ) * elem.radius ) );
1977 
1978  arcStartOffset += elem.center;
1979 
1980  // If it's a circle then add two 180-degree arcs
1981  if( elem.startangle == 0. && elem.endangle == 360. )
1982  angle = 180.;
1983 
1984  SHAPE_ARC shapeArc( elem.center, arcStartOffset, angle, elem.width );
1985  PCB_ARC* arc = new PCB_ARC( m_board, &shapeArc );
1986  m_board->Add( arc, ADD_MODE::APPEND );
1987 
1988  arc->SetWidth( elem.width );
1989  arc->SetLayer( klayer );
1990  arc->SetNetCode( GetNetCode( elem.net ) );
1991 
1992  // Add second 180-degree arc for a circle
1993  if( elem.startangle == 0. && elem.endangle == 360. )
1994  {
1995  shapeArc = SHAPE_ARC( elem.center, arcStartOffset, -angle, elem.width );
1996  arc = new PCB_ARC( m_board, &shapeArc );
1997  m_board->Add( arc, ADD_MODE::APPEND );
1998 
1999  arc->SetWidth( elem.width );
2000  arc->SetLayer( klayer );
2001  arc->SetNetCode( GetNetCode( elem.net ) );
2002  }
2003  }
2004  else
2005  {
2006  PCB_SHAPE* shape = HelperCreateAndAddShape( elem.component );
2007  shape->SetWidth( elem.width );
2008  shape->SetLayer( klayer );
2009 
2010  if( elem.startangle == 0. && elem.endangle == 360. )
2011  { // TODO: other variants to define circle?
2012  shape->SetShape( SHAPE_T::CIRCLE );
2013  shape->SetStart( elem.center );
2014  shape->SetEnd( elem.center - wxPoint( 0, elem.radius ) );
2015  }
2016  else
2017  {
2018  shape->SetShape( SHAPE_T::ARC );
2019 
2020  double includedAngle = elem.endangle - elem.startangle;
2021  double startradiant = DEG2RAD( elem.startangle );
2022  wxPoint arcStartOffset = wxPoint( KiROUND( std::cos( startradiant ) * elem.radius ),
2023  -KiROUND( std::sin( startradiant ) * elem.radius ) );
2024 
2025  shape->SetCenter( elem.center );
2026  shape->SetStart( elem.center + arcStartOffset );
2027  shape->SetArcAngleAndEnd( -NormalizeAngleDegreesPos( includedAngle ) * 10.0, true );
2028  }
2029 
2030  HelperShapeSetLocalCoord( shape, elem.component );
2031  }
2032  }
2033 
2034  if( reader.GetRemainingBytes() != 0 )
2035  {
2036  THROW_IO_ERROR( "Arcs6 stream is not fully parsed" );
2037  }
2038 }
2039 
2040 
2041 void ALTIUM_PCB::ParsePads6Data( const CFB::CompoundFileReader& aReader,
2042  const CFB::COMPOUND_FILE_ENTRY* aEntry )
2043 {
2044  if( m_progressReporter )
2045  m_progressReporter->Report( _( "Loading pads..." ) );
2046 
2047  ALTIUM_PARSER reader( aReader, aEntry );
2048 
2049  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2050  {
2051  checkpoint();
2052  APAD6 elem( reader );
2053 
2054  // It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
2055  if( !IsAltiumLayerCopper( elem.layer ) && !IsAltiumLayerAPlane( elem.layer )
2056  && elem.layer != ALTIUM_LAYER::MULTI_LAYER )
2057  {
2058  HelperParsePad6NonCopper( elem );
2059  continue;
2060  }
2061 
2062  // Create Pad
2063  FOOTPRINT* footprint = nullptr;
2064 
2065  if( elem.component == ALTIUM_COMPONENT_NONE )
2066  {
2067  footprint = new FOOTPRINT( m_board ); // We cannot add a pad directly into the PCB
2068  m_board->Add( footprint, ADD_MODE::APPEND );
2069  footprint->SetPosition( elem.position );
2070  }
2071  else
2072  {
2073  if( m_components.size() <= elem.component )
2074  {
2075  THROW_IO_ERROR( wxString::Format( "Pads6 stream tries to access component id %d "
2076  "of %d existing components",
2077  elem.component,
2078  m_components.size() ) );
2079  }
2080  footprint = m_components.at( elem.component );
2081  }
2082 
2083  PAD* pad = new PAD( footprint );
2084  footprint->Add( pad, ADD_MODE::APPEND );
2085 
2086  pad->SetNumber( elem.name );
2087  pad->SetNetCode( GetNetCode( elem.net ) );
2088  pad->SetLocked( elem.is_locked );
2089 
2090  pad->SetPosition( elem.position );
2091  pad->SetOrientationDegrees( elem.direction );
2092  pad->SetLocalCoord();
2093 
2094  pad->SetSize( elem.topsize );
2095 
2096  if( elem.holesize == 0 )
2097  {
2098  pad->SetAttribute( PAD_ATTRIB::SMD );
2099  }
2100  else
2101  {
2102  if( elem.layer != ALTIUM_LAYER::MULTI_LAYER )
2103  {
2104  // TODO: I assume other values are possible as well?
2105  wxLogError( _( "Footprint %s pad %s is not marked as multilayer, but is a TH pad." ),
2106  footprint->GetReference(),
2107  elem.name );
2108  }
2109  pad->SetAttribute( elem.plated ? PAD_ATTRIB::PTH :
2110  PAD_ATTRIB::NPTH );
2111  if( !elem.sizeAndShape || elem.sizeAndShape->holeshape == ALTIUM_PAD_HOLE_SHAPE::ROUND )
2112  {
2114  pad->SetDrillSize( wxSize( elem.holesize, elem.holesize ) );
2115  }
2116  else
2117  {
2118  switch( elem.sizeAndShape->holeshape )
2119  {
2121  wxFAIL_MSG( "Round holes are handled before the switch" );
2122  break;
2123 
2125  wxLogWarning( _( "Footprint %s pad %s has a square hole (not yet supported)." ),
2126  footprint->GetReference(),
2127  elem.name );
2129  pad->SetDrillSize( wxSize( elem.holesize, elem.holesize ) ); // Workaround
2130  // TODO: elem.sizeAndShape->slotsize was 0 in testfile. Either use holesize in this case or rect holes have a different id
2131  break;
2132 
2134  {
2136  double normalizedSlotrotation =
2137  NormalizeAngleDegreesPos( elem.sizeAndShape->slotrotation );
2138 
2139  if( normalizedSlotrotation == 0. || normalizedSlotrotation == 180. )
2140  {
2141  pad->SetDrillSize( wxSize( elem.sizeAndShape->slotsize, elem.holesize ) );
2142  }
2143  else
2144  {
2145  if( normalizedSlotrotation != 90. && normalizedSlotrotation != 270. )
2146  {
2147  wxLogWarning( _( "Footprint %s pad %s has a hole-rotation of %f "
2148  "degrees. KiCad only supports 90 degree rotations." ),
2149  footprint->GetReference(),
2150  elem.name,
2151  normalizedSlotrotation );
2152  }
2153 
2154  pad->SetDrillSize( wxSize( elem.holesize, elem.sizeAndShape->slotsize ) );
2155  }
2156  }
2157  break;
2158 
2159  default:
2161  wxLogError( _( "Footprint %s pad %s uses a hole of unknown kind %d." ),
2162  footprint->GetReference(),
2163  elem.name,
2164  elem.sizeAndShape->holeshape );
2166  pad->SetDrillSize( wxSize( elem.holesize, elem.holesize ) ); // Workaround
2167  break;
2168  }
2169  }
2170 
2171  if( elem.sizeAndShape )
2172  {
2173  pad->SetOffset( elem.sizeAndShape->holeoffset[0] );
2174  }
2175  }
2176 
2177  if( elem.padmode != ALTIUM_PAD_MODE::SIMPLE )
2178  {
2179  wxLogError( _( "Footprint %s pad %s uses a complex pad stack (not yet supported.)" ),
2180  footprint->GetReference(),
2181  elem.name );
2182  }
2183 
2184  switch( elem.topshape )
2185  {
2187  pad->SetShape( PAD_SHAPE::RECT );
2188  break;
2190  if( elem.sizeAndShape
2191  && elem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
2192  {
2193  pad->SetShape( PAD_SHAPE::ROUNDRECT ); // 100 = round, 0 = rectangular
2194  double ratio = elem.sizeAndShape->cornerradius[0] / 200.;
2195  pad->SetRoundRectRadiusRatio( ratio );
2196  }
2197  else if( elem.topsize.x == elem.topsize.y )
2198  {
2199  pad->SetShape( PAD_SHAPE::CIRCLE );
2200  }
2201  else
2202  {
2203  pad->SetShape( PAD_SHAPE::OVAL );
2204  }
2205  break;
2207  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
2208  pad->SetChamferPositions( RECT_CHAMFER_ALL );
2209  pad->SetChamferRectRatio( 0.25 );
2210  break;
2212  default:
2213  wxLogError( _( "Footprint %s pad %s uses an unknown pad-shape." ),
2214  footprint->GetReference(),
2215  elem.name );
2216  break;
2217  }
2218 
2219  switch( elem.layer )
2220  {
2222  pad->SetLayer( F_Cu );
2223  pad->SetLayerSet( PAD::SMDMask() );
2224  break;
2226  pad->SetLayer( B_Cu );
2227  pad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
2228  break;
2230  pad->SetLayerSet( elem.plated ? PAD::PTHMask() : PAD::UnplatedHoleMask() );
2231  break;
2232  default:
2233  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2234  pad->SetLayer( klayer );
2235  pad->SetLayerSet( LSET( 1, klayer ) );
2236  break;
2237  }
2238 
2240  {
2241  pad->SetLocalSolderPasteMargin( elem.pastemaskexpansionmanual );
2242  }
2243 
2245  {
2246  pad->SetLocalSolderMaskMargin( elem.soldermaskexpansionmanual );
2247  }
2248 
2249  if( elem.is_tent_top )
2250  {
2251  pad->SetLayerSet( pad->GetLayerSet().reset( F_Mask ) );
2252  }
2253  if( elem.is_tent_bottom )
2254  {
2255  pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
2256  }
2257  }
2258 
2259  if( reader.GetRemainingBytes() != 0 )
2260  {
2261  THROW_IO_ERROR( "Pads6 stream is not fully parsed" );
2262  }
2263 }
2264 
2265 
2267 {
2268  PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
2269 
2270  if( klayer == UNDEFINED_LAYER )
2271  {
2272  wxLogWarning( _( "Non-copper pad %s found on an Altium layer (%d) with no KiCad equivalent. "
2273  "It has been moved to KiCad layer Eco1_User." ),
2274  aElem.name,
2275  aElem.layer );
2276  klayer = Eco1_User;
2277  }
2278 
2279  if( aElem.net != ALTIUM_NET_UNCONNECTED )
2280  {
2281  wxLogError( _( "Non-copper pad %s is connected to a net, which is not supported." ),
2282  aElem.name );
2283  }
2284 
2285  if( aElem.holesize != 0 )
2286  {
2287  wxLogError( _( "Non-copper pad %s has a hole, which is not supported." ), aElem.name );
2288  }
2289 
2290  if( aElem.padmode != ALTIUM_PAD_MODE::SIMPLE )
2291  {
2292  wxLogWarning( _( "Non-copper pad %s has a complex pad stack (not yet supported)." ),
2293  aElem.name );
2294  }
2295 
2296  switch( aElem.topshape )
2297  {
2299  {
2300  // filled rect
2301  PCB_SHAPE* shape = HelperCreateAndAddShape( aElem.component );
2302  shape->SetShape( SHAPE_T::POLY );
2303  shape->SetFilled( true );
2304  shape->SetLayer( klayer );
2305  shape->SetWidth( 0 );
2306 
2307  shape->SetPolyPoints( { aElem.position + wxPoint( aElem.topsize.x / 2, aElem.topsize.y / 2 ),
2308  aElem.position + wxPoint( aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
2309  aElem.position + wxPoint( -aElem.topsize.x / 2, -aElem.topsize.y / 2 ),
2310  aElem.position + wxPoint( -aElem.topsize.x / 2, aElem.topsize.y / 2 ) } );
2311 
2312  if( aElem.direction != 0 )
2313  shape->Rotate( aElem.position, aElem.direction * 10 );
2314 
2315  HelperShapeSetLocalCoord( shape, aElem.component );
2316  }
2317  break;
2318 
2320  if( aElem.sizeAndShape
2321  && aElem.sizeAndShape->alt_shape[0] == ALTIUM_PAD_SHAPE_ALT::ROUNDRECT )
2322  {
2323  // filled roundrect
2324  int cornerradius = aElem.sizeAndShape->cornerradius[0];
2325  int offset = ( std::min( aElem.topsize.x, aElem.topsize.y ) * cornerradius ) / 200;
2326 
2327  PCB_SHAPE* shape = HelperCreateAndAddShape( aElem.component );
2328  shape->SetLayer( klayer );
2329  shape->SetWidth( offset * 2 );
2330 
2331  if( cornerradius < 100 )
2332  {
2333  int offsetX = aElem.topsize.x / 2 - offset;
2334  int offsetY = aElem.topsize.y / 2 - offset;
2335 
2336  wxPoint p11 = aElem.position + wxPoint( offsetX, offsetY );
2337  wxPoint p12 = aElem.position + wxPoint( offsetX, -offsetY );
2338  wxPoint p22 = aElem.position + wxPoint( -offsetX, -offsetY );
2339  wxPoint p21 = aElem.position + wxPoint( -offsetX, offsetY );
2340 
2341  shape->SetShape( SHAPE_T::POLY );
2342  shape->SetFilled( true );
2343  shape->SetPolyPoints( { p11, p12, p22, p21 } );
2344  }
2345  else if( aElem.topsize.x == aElem.topsize.y )
2346  {
2347  // circle
2348  shape->SetShape( SHAPE_T::CIRCLE );
2349  shape->SetFilled( true );
2350  shape->SetStart( aElem.position );
2351  shape->SetEnd( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
2352  shape->SetWidth( aElem.topsize.x / 2 );
2353  }
2354  else if( aElem.topsize.x < aElem.topsize.y )
2355  {
2356  // short vertical line
2357  shape->SetShape( SHAPE_T::SEGMENT );
2358  wxPoint pointOffset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
2359  shape->SetStart( aElem.position + pointOffset );
2360  shape->SetEnd( aElem.position - pointOffset );
2361  }
2362  else
2363  {
2364  // short horizontal line
2365  shape->SetShape( SHAPE_T::SEGMENT );
2366  wxPoint pointOffset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
2367  shape->SetStart( aElem.position + pointOffset );
2368  shape->SetEnd( aElem.position - pointOffset );
2369  }
2370 
2371  if( aElem.direction != 0 )
2372  shape->Rotate( aElem.position, aElem.direction * 10 );
2373 
2374  HelperShapeSetLocalCoord( shape, aElem.component );
2375  }
2376  else if( aElem.topsize.x == aElem.topsize.y )
2377  {
2378  // filled circle
2379  PCB_SHAPE* shape = HelperCreateAndAddShape( aElem.component );
2380  shape->SetShape( SHAPE_T::CIRCLE );
2381  shape->SetFilled( true );
2382  shape->SetLayer( klayer );
2383  shape->SetStart( aElem.position );
2384  shape->SetEnd( aElem.position - wxPoint( 0, aElem.topsize.x / 4 ) );
2385  shape->SetWidth( aElem.topsize.x / 2 );
2386  HelperShapeSetLocalCoord( shape, aElem.component );
2387  }
2388  else
2389  {
2390  // short line
2391  PCB_SHAPE* shape = HelperCreateAndAddShape( aElem.component );
2392  shape->SetShape( SHAPE_T::SEGMENT );
2393  shape->SetLayer( klayer );
2394  shape->SetWidth( std::min( aElem.topsize.x, aElem.topsize.y ) );
2395 
2396  if( aElem.topsize.x < aElem.topsize.y )
2397  {
2398  wxPoint offset( 0, ( aElem.topsize.y - aElem.topsize.x ) / 2 );
2399  shape->SetStart( aElem.position + offset );
2400  shape->SetEnd( aElem.position - offset );
2401  }
2402  else
2403  {
2404  wxPoint offset( ( aElem.topsize.x - aElem.topsize.y ) / 2, 0 );
2405  shape->SetStart( aElem.position + offset );
2406  shape->SetEnd( aElem.position - offset );
2407  }
2408 
2409  if( aElem.direction != 0 )
2410  shape->Rotate( aElem.position, aElem.direction * 10. );
2411 
2412  HelperShapeSetLocalCoord( shape, aElem.component );
2413  }
2414  break;
2415 
2417  {
2418  // filled octagon
2419  PCB_SHAPE* shape = HelperCreateAndAddShape( aElem.component );
2420  shape->SetShape( SHAPE_T::POLY );
2421  shape->SetFilled( true );
2422  shape->SetLayer( klayer );
2423  shape->SetWidth( 0 );
2424 
2425  wxPoint p11 = aElem.position + wxPoint( aElem.topsize.x / 2, aElem.topsize.y / 2 );
2426  wxPoint p12 = aElem.position + wxPoint( aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2427  wxPoint p22 = aElem.position + wxPoint( -aElem.topsize.x / 2, -aElem.topsize.y / 2 );
2428  wxPoint p21 = aElem.position + wxPoint( -aElem.topsize.x / 2, aElem.topsize.y / 2 );
2429 
2430  int chamfer = std::min( aElem.topsize.x, aElem.topsize.y ) / 4;
2431  wxPoint chamferX( chamfer, 0 );
2432  wxPoint chamferY( 0, chamfer );
2433 
2434  shape->SetPolyPoints( { p11 - chamferX, p11 - chamferY, p12 + chamferY, p12 - chamferX,
2435  p22 + chamferX, p22 + chamferY, p21 - chamferY, p21 + chamferX } );
2436 
2437  if( aElem.direction != 0. )
2438  shape->Rotate( aElem.position, aElem.direction * 10 );
2439 
2440  HelperShapeSetLocalCoord( shape, aElem.component );
2441  }
2442  break;
2443 
2445  default:
2446  wxLogError( _( "Non-copper pad %s uses an unknown pad-shape." ), aElem.name );
2447  break;
2448  }
2449 }
2450 
2451 void ALTIUM_PCB::ParseVias6Data( const CFB::CompoundFileReader& aReader,
2452  const CFB::COMPOUND_FILE_ENTRY* aEntry )
2453 {
2454  if( m_progressReporter )
2455  m_progressReporter->Report( _( "Loading vias..." ) );
2456 
2457  ALTIUM_PARSER reader( aReader, aEntry );
2458 
2459  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2460  {
2461  checkpoint();
2462  AVIA6 elem( reader );
2463 
2464  PCB_VIA* via = new PCB_VIA( m_board );
2466 
2467  via->SetPosition( elem.position );
2468  via->SetWidth( elem.diameter );
2469  via->SetDrill( elem.holesize );
2470  via->SetNetCode( GetNetCode( elem.net ) );
2471  via->SetLocked( elem.is_locked );
2472 
2473  bool start_layer_outside = elem.layer_start == ALTIUM_LAYER::TOP_LAYER
2475  bool end_layer_outside = elem.layer_end == ALTIUM_LAYER::TOP_LAYER
2477 
2478  if( start_layer_outside && end_layer_outside )
2479  {
2480  via->SetViaType( VIATYPE::THROUGH );
2481  }
2482  else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
2483  {
2484  via->SetViaType( VIATYPE::BLIND_BURIED );
2485  }
2486  else
2487  {
2488  via->SetViaType( VIATYPE::MICROVIA ); // TODO: always a microvia?
2489  }
2490 
2491  PCB_LAYER_ID start_klayer = GetKicadLayer( elem.layer_start );
2492  PCB_LAYER_ID end_klayer = GetKicadLayer( elem.layer_end );
2493 
2494  if( !IsCopperLayer( start_klayer ) || !IsCopperLayer( end_klayer ) )
2495  {
2496  wxLogError( _( "Via from layer %d to %d uses a non-copper layer, which is not "
2497  "supported." ),
2498  elem.layer_start,
2499  elem.layer_end );
2500  continue; // just assume through-hole instead.
2501  }
2502 
2503  // we need VIATYPE set!
2504  via->SetLayerPair( start_klayer, end_klayer );
2505  }
2506 
2507  if( reader.GetRemainingBytes() != 0 )
2508  {
2509  THROW_IO_ERROR( "Vias6 stream is not fully parsed" );
2510  }
2511 }
2512 
2513 void ALTIUM_PCB::ParseTracks6Data( const CFB::CompoundFileReader& aReader,
2514  const CFB::COMPOUND_FILE_ENTRY* aEntry )
2515 {
2516  if( m_progressReporter )
2517  m_progressReporter->Report( _( "Loading tracks..." ) );
2518 
2519  ALTIUM_PARSER reader( aReader, aEntry );
2520 
2521  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2522  {
2523  checkpoint();
2524  ATRACK6 elem( reader );
2525 
2526  if( elem.is_polygonoutline || elem.subpolyindex != ALTIUM_POLYGON_NONE )
2527  continue;
2528 
2529  // element in plane is in fact substracted from the plane. Already done by Altium?
2530  //if( IsAltiumLayerAPlane( elem.layer ) )
2531  // continue;
2532 
2533  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2534 
2535  if( elem.is_keepout || IsAltiumLayerAPlane( elem.layer ) )
2536  {
2537  PCB_SHAPE shape( nullptr, SHAPE_T::SEGMENT );
2538  shape.SetStart( elem.start );
2539  shape.SetEnd( elem.end );
2540  shape.SetWidth( elem.width );
2541 
2542  ZONE* zone = new ZONE( m_board );
2543  m_board->Add( zone, ADD_MODE::APPEND );
2544 
2545  zone->SetFillVersion( 6 );
2546  zone->SetIsRuleArea( true );
2547  zone->SetDoNotAllowTracks( false );
2548  zone->SetDoNotAllowVias( false );
2549  zone->SetDoNotAllowPads( false );
2550  zone->SetDoNotAllowFootprints( false );
2551  zone->SetDoNotAllowCopperPour( true );
2552 
2553  if( elem.layer == ALTIUM_LAYER::MULTI_LAYER )
2554  {
2555  zone->SetLayer( F_Cu );
2556  zone->SetLayerSet( LSET::AllCuMask() );
2557  }
2558  else
2559  {
2560  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2561 
2562  if( klayer == UNDEFINED_LAYER )
2563  {
2564  wxLogWarning( _( "Track keepout found on an Altium layer (%d) with no KiCad "
2565  "equivalent. It has been moved to KiCad layer Eco1_User." ),
2566  elem.layer );
2567  klayer = Eco1_User;
2568  }
2569  zone->SetLayer( klayer );
2570  }
2571 
2572  shape.TransformShapeWithClearanceToPolygon( *zone->Outline(), klayer, 0, ARC_HIGH_DEF,
2573  ERROR_INSIDE );
2574 
2576  ZONE::GetDefaultHatchPitch(), true );
2577  continue;
2578  }
2579 
2580  if( klayer == UNDEFINED_LAYER )
2581  {
2582  wxLogWarning( _( "Track found on an Altium layer (%d) with no KiCad equivalent. "
2583  "It has been moved to KiCad layer Eco1_User." ),
2584  elem.layer );
2585  klayer = Eco1_User;
2586  }
2587 
2588  if( klayer >= F_Cu && klayer <= B_Cu )
2589  {
2590  PCB_TRACK* track = new PCB_TRACK( m_board );
2591  m_board->Add( track, ADD_MODE::APPEND );
2592 
2593  track->SetStart( elem.start );
2594  track->SetEnd( elem.end );
2595  track->SetWidth( elem.width );
2596  track->SetLayer( klayer );
2597  track->SetNetCode( GetNetCode( elem.net ) );
2598  }
2599  else
2600  {
2601  PCB_SHAPE* shape = HelperCreateAndAddShape( elem.component );
2602  shape->SetShape( SHAPE_T::SEGMENT );
2603  shape->SetStart( elem.start );
2604  shape->SetEnd( elem.end );
2605  shape->SetWidth( elem.width );
2606  shape->SetLayer( klayer );
2607  HelperShapeSetLocalCoord( shape, elem.component );
2608  }
2609 
2610  reader.SkipSubrecord();
2611  }
2612 
2613  if( reader.GetRemainingBytes() != 0 )
2614  {
2615  THROW_IO_ERROR( "Tracks6 stream is not fully parsed" );
2616  }
2617 }
2618 
2619 void ALTIUM_PCB::ParseWideStrings6Data( const CFB::CompoundFileReader& aReader,
2620  const CFB::COMPOUND_FILE_ENTRY* aEntry )
2621 {
2622  if( m_progressReporter )
2623  m_progressReporter->Report( _( "Loading unicode strings..." ) );
2624 
2625  ALTIUM_PARSER reader( aReader, aEntry );
2626 
2628 
2629  if( reader.GetRemainingBytes() != 0 )
2630  THROW_IO_ERROR( "WideStrings6 stream is not fully parsed" );
2631 }
2632 
2633 void ALTIUM_PCB::ParseTexts6Data( const CFB::CompoundFileReader& aReader,
2634  const CFB::COMPOUND_FILE_ENTRY* aEntry )
2635 {
2636  if( m_progressReporter )
2637  m_progressReporter->Report( _( "Loading text..." ) );
2638 
2639  ALTIUM_PARSER reader( aReader, aEntry );
2640 
2641  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2642  {
2643  checkpoint();
2644  ATEXT6 elem( reader, m_unicodeStrings );
2645 
2646  if( elem.fonttype == ALTIUM_TEXT_TYPE::BARCODE )
2647  {
2648  wxLogError( _( "Ignored barcode on Altium layer %d (not yet supported)." ),
2649  elem.layer );
2650  continue;
2651  }
2652 
2653  // TODO: better approach to select if item belongs to a FOOTPRINT
2654  EDA_TEXT* tx = nullptr;
2655  BOARD_ITEM* itm = nullptr;
2656 
2657  if( elem.component == ALTIUM_COMPONENT_NONE )
2658  {
2659  PCB_TEXT* pcbText = new PCB_TEXT( m_board );
2660  tx = pcbText;
2661  itm = pcbText;
2662  m_board->Add( pcbText, ADD_MODE::APPEND );
2663  }
2664  else
2665  {
2666  if( m_components.size() <= elem.component )
2667  {
2668  THROW_IO_ERROR( wxString::Format( "Texts6 stream tries to access component id %d "
2669  "of %d existing components",
2670  elem.component,
2671  m_components.size() ) );
2672  }
2673 
2674  FOOTPRINT* footprint = m_components.at( elem.component );
2675  FP_TEXT* fpText;
2676 
2677  if( elem.isDesignator )
2678  {
2679  fpText = &footprint->Reference();
2680  }
2681  else if( elem.isComment )
2682  {
2683  fpText = &footprint->Value();
2684  }
2685  else
2686  {
2687  fpText = new FP_TEXT( footprint );
2688  footprint->Add( fpText, ADD_MODE::APPEND );
2689  }
2690 
2691  fpText->SetKeepUpright( false );
2692 
2693  tx = fpText;
2694  itm = fpText;
2695  }
2696 
2697  wxString trimmedText = elem.text.Trim();
2698  if( !elem.isDesignator && trimmedText.CmpNoCase( ".Designator" ) == 0 )
2699  {
2700  tx->SetText( "${REFERENCE}" );
2701  }
2702  else if( !elem.isComment && trimmedText.CmpNoCase( ".Comment" ) == 0 )
2703  {
2704  tx->SetText( "${VALUE}" );
2705  }
2706  else if( trimmedText.CmpNoCase( ".Layer_Name" ) == 0 )
2707  {
2708  tx->SetText( "${LAYER}" );
2709  }
2710  else
2711  {
2712  tx->SetText( elem.text );
2713  }
2714 
2715  itm->SetPosition( elem.position );
2716  tx->SetTextAngle( elem.rotation * 10. );
2717 
2718  if( elem.component != ALTIUM_COMPONENT_NONE )
2719  {
2720  FP_TEXT* fpText = dynamic_cast<FP_TEXT*>( tx );
2721 
2722  if( fpText )
2723  {
2724  FOOTPRINT* parentFootprint = static_cast<FOOTPRINT*>( fpText->GetParent() );
2725  double orientation = parentFootprint->GetOrientation();
2726 
2727  fpText->SetTextAngle( fpText->GetTextAngle() - orientation );
2728  fpText->SetLocalCoord();
2729  }
2730  }
2731 
2732  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2733 
2734  if( klayer == UNDEFINED_LAYER )
2735  {
2736  wxLogWarning( _( "Text found on an Altium layer (%d) with no KiCad equivalent. "
2737  "It has been moved to KiCad layer Eco1_User." ),
2738  elem.layer );
2739  klayer = Eco1_User;
2740  }
2741 
2742  itm->SetLayer( klayer );
2743 
2744  if( elem.fonttype == ALTIUM_TEXT_TYPE::TRUETYPE )
2745  {
2746  // TODO: why is this required? Somehow, truetype size is calculated differently
2747  tx->SetTextSize( wxSize( elem.height / 2, elem.height / 2 ) );
2748  }
2749  else
2750  {
2751  tx->SetTextSize( wxSize( elem.height, elem.height ) ); // TODO: parse text width
2752  }
2753 
2754  tx->SetTextThickness( elem.strokewidth );
2755  tx->SetBold( elem.isBold );
2756  tx->SetItalic( elem.isItalic );
2757  tx->SetMirrored( elem.isMirrored );
2758 
2759  if( elem.isDesignator || elem.isComment ) // That's just a bold assumption
2760  {
2763  }
2764  else
2765  {
2766  switch( elem.textposition )
2767  {
2772  break;
2777  break;
2782  break;
2783  default:
2784  wxLogError( "Unexpected horizontal Text Position. This should never happen." );
2785  break;
2786  }
2787 
2788  switch( elem.textposition )
2789  {
2794  break;
2799  break;
2804  break;
2805  default:
2806  wxLogError( "Unexpected vertical text position. This should never happen." );
2807  break;
2808  }
2809  }
2810  }
2811 
2812  if( reader.GetRemainingBytes() != 0 )
2813  {
2814  THROW_IO_ERROR( "Texts6 stream is not fully parsed" );
2815  }
2816 }
2817 
2818 void ALTIUM_PCB::ParseFills6Data( const CFB::CompoundFileReader& aReader,
2819  const CFB::COMPOUND_FILE_ENTRY* aEntry )
2820 {
2821  if( m_progressReporter )
2822  m_progressReporter->Report( _( "Loading rectangles..." ) );
2823 
2824  ALTIUM_PARSER reader( aReader, aEntry );
2825 
2826  while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
2827  {
2828  checkpoint();
2829  AFILL6 elem( reader );
2830 
2831  wxPoint p11( elem.pos1.x, elem.pos1.y );
2832  wxPoint p12( elem.pos1.x, elem.pos2.y );
2833  wxPoint p22( elem.pos2.x, elem.pos2.y );
2834  wxPoint p21( elem.pos2.x, elem.pos1.y );
2835 
2836  wxPoint center( ( elem.pos1.x + elem.pos2.x ) / 2, ( elem.pos1.y + elem.pos2.y ) / 2 );
2837 
2838  PCB_LAYER_ID klayer = GetKicadLayer( elem.layer );
2839 
2840  if( klayer == UNDEFINED_LAYER )
2841  {
2842  wxLogWarning( _( "Fill found on an Altium layer (%d) with no KiCad equivalent. "
2843  "It has been moved to KiCad layer Eco1_User." ),
2844  elem.layer );
2845  klayer = Eco1_User;
2846  }
2847 
2848  if( elem.is_keepout || elem.net != ALTIUM_NET_UNCONNECTED )
2849  {
2850  ZONE* zone = new ZONE( m_board );
2851  m_board->Add( zone, ADD_MODE::APPEND );
2852 
2853  zone->SetFillVersion( 6 );
2854  zone->SetNetCode( GetNetCode( elem.net ) );
2855  zone->SetLayer( klayer );
2856  zone->SetPosition( elem.pos1 );
2857  zone->SetPriority( 1000 );
2858 
2859  const int outlineIdx = -1; // this is the id of the copper zone main outline
2860  zone->AppendCorner( p11, outlineIdx );
2861  zone->AppendCorner( p12, outlineIdx );
2862  zone->AppendCorner( p22, outlineIdx );
2863  zone->AppendCorner( p21, outlineIdx );
2864 
2865  // should be correct?
2866  zone->SetLocalClearance( 0 );
2868 
2869  if( elem.is_keepout )
2870  {
2871  zone->SetIsRuleArea( true );
2872  zone->SetDoNotAllowTracks( false );
2873  zone->SetDoNotAllowVias( false );
2874  zone->SetDoNotAllowPads( false );
2875  zone->SetDoNotAllowFootprints( false );
2876  zone->SetDoNotAllowCopperPour( true );
2877  }
2878 
2879  if( elem.rotation != 0. )
2880  zone->Rotate( center, elem.rotation * 10 );
2881 
2883  ZONE::GetDefaultHatchPitch(), true );
2884  }
2885  else
2886  {
2887  PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::POLY );
2888  m_board->Add( shape, ADD_MODE::APPEND );
2889  shape->SetFilled( true );
2890  shape->SetLayer( klayer );
2891  shape->SetWidth( 0 );
2892 
2893  shape->SetPolyPoints( { p11, p12, p22, p21 } );
2894 
2895  if( elem.rotation != 0. )
2896  shape->Rotate( center, elem.rotation * 10 );
2897  }
2898  }
2899 
2900  if( reader.GetRemainingBytes() != 0 )
2901  {
2902  THROW_IO_ERROR( "Fills6 stream is not fully parsed" );
2903  }
2904 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:209
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
void SetReference(const wxString &aReference)
Definition: footprint.h:473
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:759
uint32_t textlinewidth
uint32_t linewidth
FP_3DMODEL::VECTOR3D modelPosition
int32_t soldermaskexpansionmanual
ALTIUM_RULE_KIND kind
double rotation
uint32_t holesize
const SHAPE_ARC & Arc(size_t aArc) const
void ParseAltiumPcb(BOARD *aBoard, const wxString &aFileName, PROGRESS_REPORTER *aProgressReporter, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
Helper method which opens a Altium Board File and parses it.
Definition: altium_pcb.cpp:58
void SetUnits(EDA_UNITS aUnits)
void SetModified()
Definition: eda_item.cpp:65
uint32_t width
virtual wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:280
void Parse(const CFB::CompoundFileReader &aReader, const std::map< ALTIUM_PCB_DIR, std::string > &aFileMapping)
Definition: altium_pcb.cpp:348
int planeclearanceClearance
void SetHatchThickness(int aThickness)
Definition: zone.h:254
void ParseVias6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
wxString name
bool isDesignator
const PAGE_INFO & GetPageSettings() const
Definition: board.h:536
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint.
Definition: lset.cpp:576
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:627
std::vector< ZONE * > m_polygons
Definition: altium_pcb.h:195
double GetOrientationRadians() const
Definition: footprint.h:193
wxPoint pos1
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
double GetLineLength(const wxPoint &aPointA, const wxPoint &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:222
ALTIUM_LAYER layer
const ARULE6 * GetRule(ALTIUM_RULE_KIND aKind, const wxString &aName) const
Definition: altium_pcb.cpp:651
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:183
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:745
wxPoint start
wxSize topsize
void SetFilled(bool aFlag)
Definition: eda_shape.h:83
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:126
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:273
void ParseComponents6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:921
virtual void SetPosition(const wxPoint &aPos) override
Definition: pcb_text.h:81
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.
void SetArcGeometry(const wxPoint &aStart, const wxPoint &aMid, const wxPoint &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:489
unsigned m_totalCount
for progress reporting
Definition: altium_pcb.h:208
Manage layers needed to make a physical board.
ALTIUM_DIMENSION_KIND kind
void SetUnitsFormat(const DIM_UNITS_FORMAT aFormat)
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:163
virtual void SetPosition(const wxPoint &aPos)
Definition: eda_item.h:252
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aHatchStyle, int aHatchPitch, bool aRebuildHatch)
Set all hatch parameters for the zone.
Definition: zone.cpp:910
ALTIUM_REGION_KIND kind
Definition: board.h:73
int GetX() const
Definition: eda_rect.h:98
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
wxPoint position
const EDA_RECT GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:738
void ParsePolygons6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
PCB_TEXT & Text()
A progress reporter interface for use in multi-threaded environments.
void ParseWideStrings6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:215
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:154
void SetTextAngle(double aAngle) override
Definition: fp_text.cpp:73
double startangle
size_t GetRemainingBytes() const
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:250
uint16_t component
SHAPE_POLY_SET * Outline()
Definition: zone.h:320
void SetItalic(bool isItalic)
Definition: eda_text.h:200
bool IsEmpty() const
#define PROJECT_VAR_NAME
A variable name whose value holds the current project directory.
Definition: project.h:38
Smd pad, appears on the solder paste layer (default)
int32_t polygonconnectAirgapwidth
wxPoint center
int GetWidth() const
Definition: eda_rect.h:109
ALTIUM_LAYER layer
double GetOrientation() const
Definition: footprint.h:191
double GetTextAngle() const
Definition: eda_text.h:195
void SetCopperLayerCount(int aCount)
Definition: board.cpp:461
bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
std::vector< std::vector< ALTIUM_VERTICE > > holes
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
wxString sourcedesignator
const uint16_t ALTIUM_POLYGON_NONE
ALTIUM_LAYER layer
void SetArcAngleAndEnd(double aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:525
ALTIUM_LAYER layer_end
void ParseComponentsBodies6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:968
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:190
Abstract dimension API.
Definition: pcb_dimension.h:95
ALTIUM_PAD_RULE soldermaskexpansionmode
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:258
virtual void SetLocked(bool aLocked)
Modify the 'lock' status for of the item.
Definition: board_item.h:223
uint16_t net
std::map< uint32_t, wxString > ReadWideStringTable()
Definition: altium_parser.h:87
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:410
wxString sourcefootprintlibrary
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
const wxPoint & GetGridOrigin()
wxString name
virtual void Rotate(const wxPoint &aRotCentre, double aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:109
unsigned m_lastProgressCount
Definition: altium_pcb.h:207
PCB_LAYER_ID GetKicadLayer(ALTIUM_LAYER aAltiumLayer) const
Definition: altium_pcb.cpp:209
wxString text
int PointCount() const
Return the number of points (vertices) in this line chain.
void SetPriority(unsigned aPriority)
Definition: zone.h:117
const wxString & GetFileName() const
Definition: board.h:229
VECTOR3D m_Offset
3D model offset (mm)
Definition: footprint.h:96
void ParseArcs6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
wxString scope2expr
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
bool is_polygonoutline
uint32_t height
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: zone.h:180
std::map< ALTIUM_LAYER, ZONE * > m_outer_plane
Definition: altium_pcb.h:203
bool Contains(const wxPoint &aPoint) const
Definition: eda_rect.cpp:57
void checkpoint()
Definition: altium_pcb.cpp:329
const ARULE6 * GetRuleDefault(ALTIUM_RULE_KIND aKind) const
Definition: altium_pcb.cpp:668
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
ALTIUM_CONNECT_STYLE polygonconnectStyle
Plated through hole pad.
int GetNetCode(uint16_t aId) const
Definition: altium_pcb.cpp:634
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:500
std::vector< wxString > names
void SetLineThickness(int aWidth)
For better understanding of the points that make a dimension:
int32_t trackwidth
void HelperParseDimensions6Radial(const ADIMENSION6 &aElem)
std::vector< wxPoint > textPoint
void SetIsRuleArea(bool aEnable)
Definition: zone.h:742
wxString textformat
FP_TEXT & Reference()
Definition: footprint.h:501
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:140
void SetStart(const wxPoint &aStart)
Definition: eda_shape.h:101
std::map< wxString, wxString > m_models
Definition: altium_pcb.h:197
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
ALTIUM_PCB(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter)
Definition: altium_pcb.cpp:314
void Rotate(const wxPoint &aCentre, double aAngle) override
Move the outlines.
Definition: zone.cpp:721
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
uint16_t subpolyindex
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
const CFB::COMPOUND_FILE_ENTRY * FindStream(const CFB::CompoundFileReader &aReader, const char *aStreamName)
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:206
bool IsArcStart(size_t aIndex) const
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
Definition: board.cpp:608
void ParsePads6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void HelperParseDimensions6Leader(const ADIMENSION6 &aElem)
std::vector< FOOTPRINT * > m_components
Definition: altium_pcb.h:194
PCB_SHAPE * HelperCreateAndAddShape(uint16_t aComponent)
Definition: altium_pcb.cpp:117
void ParseRegions6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
like PAD_PTH, but not plated
size_t ReadAndSetSubrecordLength()
BOARD_STACKUP & GetStackupDescriptor()
double NormalizeAngleDegreesPos(double Angle)
Normalize angle to be in the 0.0 .. 360.0 range: angle is in degrees.
Definition: trigo.h:297
BOARD * m_board
Definition: altium_pcb.h:193
ALTIUM_PAD_RULE pastemaskexpansionmode
wxPoint position
uint16_t subpolyindex
double direction
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:505
std::map< ALTIUM_LAYER, PCB_LAYER_ID > m_layermap
Definition: altium_pcb.h:200
pads are covered by copper
void ParseDimensions6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
ALTIUM_RULE_KIND
void SetAuxOrigin(const wxPoint &aOrigin)
int GetMinThickness() const
Definition: zone.h:244
wxString textsuffix
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:124
const EDA_RECT GetBoundingBox() const override
Definition: zone.cpp:326
void SetHatchGap(int aStep)
Definition: zone.h:257
double endangle
void Move(const VECTOR2I &aVector) override
wxString id
virtual wxPoint GetPosition() const override
Definition: pcb_text.h:76
void ParseTexts6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
const wxPoint position
static int GetDefaultHatchPitch()
Definition: zone.cpp:1102
wxString NotSpecifiedPrm()
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:746
Represent a set of closed polygons.
virtual void SetEnd(const wxPoint &aPoint)
ALTIUM_CLASS_KIND kind
double GetRadius() const
Definition: pcb_track.cpp:931
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:223
const VECTOR2I & GetP0() const
Definition: shape_arc.h:111
ALTIUM_LAYER layer
const uint16_t ALTIUM_NET_UNCONNECTED
unsigned m_doneCount
Definition: altium_pcb.h:206
void SetPrefix(const wxString &aPrefix)
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:378
Mark the center of a circle or arc with a cross shape.
void ParseClasses6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:882
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:169
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:113
void ParseFileHeader(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Definition: altium_pcb.cpp:685
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:158
void SetStart(const wxPoint &aStart)
Definition: pcb_track.h:107
void ParseBoardRegionsData(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
const wxString & GetReference() const
Definition: footprint.h:464
#define _(s)
uint16_t component
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:744
double m_Opacity
Definition: footprint.h:97
void SetWidth(int aWidth)
Definition: eda_shape.h:88
NETCLASSES & GetNetClasses() const
virtual wxPoint GetCenter() const
This defaults to the center of the bounding box if not overridden.
Definition: board_item.h:81
uint32_t holesize
void Simplify(POLYGON_MODE aFastMode)
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
wxPoint sheetpos
wxString textprefix
void SetMinThickness(int aMinThickness)
Definition: zone.h:245
void SetIsFilled(bool isFilled)
Definition: zone.h:235
void SetPrecision(int aPrecision)
void SetCenter(const wxPoint &aCenter)
Definition: eda_shape.cpp:419
uint16_t net
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:110
const std::vector< BOARD_STACKUP_ITEM * > & GetList() const
void ParseModelsData(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry, const wxString &aRootDir)
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:207
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:797
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:650
size_t m_num_nets
Definition: altium_pcb.h:199
int m_highest_pour_index
Altium stores pour order across all layers.
Definition: altium_pcb.h:211
bool is_keepout
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:98
bool is_locked
double rotation
Definition: seg.h:40
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:268
uint16_t subpolyindex
wxPoint GetPosition() const override
Definition: pcb_shape.h:77
bool IsFlipped() const
Definition: footprint.h:278
Use thermal relief for pads.
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:196
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
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:404
void SetWidth(int aWidth)
Definition: pcb_track.h:101
void ParseTracks6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
LIB_ID AltiumToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
void Move(const wxPoint &aMoveVector) override
Move this object.
Definition: pcb_text.h:86
Handle the data for a net.
Definition: netinfo.h:66
void SetPolyPoints(const std::vector< wxPoint > &aPoints)
Definition: eda_shape.cpp:1063
SEG Segment(int aIndex)
Return a copy of the aIndex-th segment in the line chain.
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:242
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:850
void HelperParseDimensions6Linear(const ADIMENSION6 &aElem)
void SetPosition(const wxPoint &aPos) override
Definition: zone.h:112
void RemoveAll()
Delete all items in list and clear the list.
uint32_t width
ALTIUM_TEXT_POSITION textposition
VECTOR3D m_Rotation
3D model rotation (degrees)
Definition: footprint.h:95
ALTIUM_LAYER layer
uint16_t net
ALTIUM_LAYER layer_start
const char * name
Definition: DXF_plotter.cpp:56
const wxPoint & GetAuxOrigin()
double DEG2RAD(double deg)
Definition: trigo.h:229
void HelperParsePad6NonCopper(const APAD6 &aElem)
void ParseNets6Data(const CFB::CompoundFileReader &aReader, const CFB::COMPOUND_FILE_ENTRY *aEntry)
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:191
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:222
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:743
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
uint32_t radius
wxPoint position
Definition: layer_ids.h:71
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
std::vector< ALTIUM_VERTICE > outline
wxString ReadWxString()
Definition: altium_parser.h:69
int32_t pastemaskexpansionmanual
VECTOR2I A
Definition: seg.h:48
wxString name
Handle the component boundary box.
Definition: eda_rect.h:42
virtual void SetStart(const wxPoint &aPoint)
void SetLocked(bool isLocked) override
Set the #MODULE_is_LOCKED bit in the m_ModuleStatus.
Definition: footprint.h:304
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:91
uint16_t net
void SetLocalCoord()
Definition: fp_text.cpp:209
void HelperCreateBoardOutline(const std::vector< ALTIUM_VERTICE > &aVertices)
Definition: altium_pcb.cpp:846
void SetHatchOrientation(double aStep)
Definition: zone.h:260
int GetY() const
Definition: eda_rect.h:99
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:73
void SetGridOrigin(const wxPoint &aOrigin)
Pads are not covered.
wxPoint GetPosition() const override
Definition: footprint.h:187
double NormalizeAngleDegrees(double Angle, double aMin, double aMax)
Normalize angle to be aMin < angle <= aMax angle is in degrees.
Definition: trigo.h:327
SHAPE_POLY_SET & RawPolysList(PCB_LAYER_ID aLayer)
Definition: zone.h:718
bool IsAltiumLayerCopper(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:105
void HelperShapeSetLocalCoord(PCB_SHAPE *aShape, uint16_t aComponent)
Definition: altium_pcb.cpp:144
void SetFillVersion(int aVersion)
Definition: zone.h:693
ALTIUM_LAYER
void SetThermalReliefGap(int aThermalReliefGap)
Definition: zone.h:183
std::map< ALTIUM_RULE_KIND, std::vector< ARULE6 > > m_rules
Definition: altium_pcb.h:201
ALTIUM_PAD_MODE padmode
ALTIUM_LAYER layer
std::unique_ptr< APAD6_SIZE_AND_SHAPE > sizeAndShape
A leader is a dimension-like object pointing to a specific point.
void SetLocalCoord()
Set relative coordinates from draw coordinates.
Definition: fp_shape.cpp:52
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
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:242
std::vector< ALTIUM_VERTICE > board_vertices
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:180
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:179
const int ALTIUM_COMPONENT_NONE
ALTIUM_UNIT textunit
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:238
ALTIUM_LAYER layer
std::vector< PCB_DIMENSION_BASE * > m_radialDimensions
Definition: altium_pcb.h:196
void SetLocalClearance(int aClearance)
Definition: zone.h:158
virtual void SetTextAngle(double aAngle)
Definition: eda_text.h:188
SHAPE_T GetShape() const
Definition: eda_shape.h:92
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Removes an item from the container.
Definition: footprint.cpp:513
void SkipSubrecord()
void SetRawPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition: zone.h:658
int NextShape(int aPointIndex, bool aForwards=true) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
int GetRadius() const
Definition: eda_shape.cpp:466
std::vector< wxPoint > referencePoint
Definition: pad.h:57
void SetPosition(const wxPoint &aPos) override
Definition: footprint.cpp:1561
std::vector< ABOARD6_LAYER_STACKUP > stackup
uint32_t strokewidth
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the shape to a closed polygon.
Definition: pcb_shape.cpp:236
void BuildDefaultStackupList(const BOARD_DESIGN_SETTINGS *aSettings, int aActiveCopperLayersCount=0)
Create 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:704
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:135
bool is_tent_top
bool IsAltiumLayerAPlane(ALTIUM_LAYER aLayer)
Definition: altium_pcb.cpp:111
int32_t gridsize
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:165
void SetBold(bool aBold)
Definition: eda_text.h:203
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:372
uint16_t component
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
bool HasParsingError()
DRAWINGS & Drawings()
Definition: board.h:237
ALTIUM_TEXT_TYPE fonttype
int32_t textprecision
wxPoint pos2
uint32_t diameter
const VECTOR2I & GetP1() const
Definition: shape_arc.h:112
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
Definition: altium_pcb.h:205
bool is_locked
TRACKS & Tracks()
Definition: board.h:231
wxString name
std::map< uint32_t, wxString > m_unicodeStrings
Definition: altium_pcb.h:198
std::function< void(const CFB::CompoundFileReader &, const CFB::COMPOUND_FILE_ENTRY *)> PARSE_FUNCTION_POINTER_fp
Definition: altium_pcb.h:112
uint16_t net
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:747
bool GetFilledPolysUseThickness() const
Definition: zone.h:689
Container for design settings for a BOARD object.
wxString scope1expr
int32_t polygonconnectReliefconductorwidth
int32_t pourindex
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition: zone.h:194
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
VECTOR2I B
Definition: seg.h:49