KiCad PCB EDA Suite
cadstar_pcb_archive_loader.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) 2020-2021 Roberto Fernandez Bautista <roberto.fer.bau@gmail.com>
5  * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation, either version 3 of the License, or (at your
10  * option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
27 
29 #include <board_stackup_manager/stackup_predefined_prms.h> // KEY_COPPER, KEY_CORE, KEY_PREPREG
30 #include <board.h>
31 #include <board_design_settings.h>
32 #include <pcb_dimension.h>
33 #include <pcb_shape.h>
34 #include <fp_shape.h>
35 #include <footprint.h>
36 #include <pad.h>
37 #include <pcb_group.h>
38 #include <pcb_text.h>
39 #include <project.h>
40 #include <pcb_track.h>
41 #include <progress_reporter.h>
42 #include <zone.h>
44 #include <trigo.h>
45 #include <macros.h>
46 #include <wx/log.h>
47 
48 #include <limits> // std::numeric_limits
49 
50 
52 {
53  m_board = aBoard;
54  m_project = aProject;
55 
56  if( m_progressReporter )
57  m_progressReporter->SetNumPhases( 3 ); // (0) Read file, (1) Parse file, (2) Load file
58 
59  Parse();
60 
62 
63  //Note: can't use getKiCadPoint() due wxPoint being int - need long long to make the check
64  long long designSizeXkicad = (long long) designLimit.x * KiCadUnitMultiplier;
65  long long designSizeYkicad = (long long) designLimit.y * KiCadUnitMultiplier;
66 
67  // Max size limited by the positive dimension of wxPoint (which is an int)
68  long long maxDesignSizekicad = std::numeric_limits<int>::max();
69 
70  if( designSizeXkicad > maxDesignSizekicad || designSizeYkicad > maxDesignSizekicad )
71  {
73  _( "The design is too large and cannot be imported into KiCad. \n"
74  "Please reduce the maximum design size in CADSTAR by navigating to: \n"
75  "Design Tab -> Properties -> Design Options -> Maximum Design Size. \n"
76  "Current Design size: %.2f, %.2f millimeters. \n"
77  "Maximum permitted design size: %.2f, %.2f millimeters.\n" ),
78  (double) designSizeXkicad / PCB_IU_PER_MM,
79  (double) designSizeYkicad / PCB_IU_PER_MM,
80  (double) maxDesignSizekicad / PCB_IU_PER_MM,
81  (double) maxDesignSizekicad / PCB_IU_PER_MM ) );
82  }
83 
86  / 2;
87 
89  {
90  wxLogWarning(
91  _( "The selected file indicates that nets might be out of synchronisation "
92  "with the schematic. It is recommended that you carry out an 'Align Nets' "
93  "procedure in CADSTAR and re-import, to avoid inconsistencies between the "
94  "PCB and the schematic. " ) );
95  }
96 
97  if( m_progressReporter )
98  {
100 
101  // Significantly most amount of time spent loading coppers compared to all the other steps
102  // (39 seconds vs max of 100ms in other steps). This is due to requirement of boolean
103  // operations to join them together into a single polygon.
104  long numSteps = Layout.Coppers.size();
105 
106  m_progressReporter->SetMaxProgress( numSteps );
107  }
108 
111  loadDesignRules();
113  loadGroups();
114  loadBoards();
115  loadFigures();
116  loadTexts();
117  loadDimensions();
118  loadAreas();
119  loadComponents();
121  loadTemplates();
122  loadCoppers(); // Progress reporting is here as significantly most amount of time spent
124  loadNets();
126 
127  if( Layout.Trunks.size() > 0 )
128  {
129  wxLogWarning(
130  _( "The CADSTAR design contains Trunk routing elements, which have no KiCad "
131  "equivalent. These elements were not loaded." ) );
132  }
133 
134  if( Layout.VariantHierarchy.Variants.size() > 0 )
135  {
136  wxLogWarning( wxString::Format(
137  _( "The CADSTAR design contains variants which has no KiCad equivalent. Only "
138  "the variant '%s' was loaded." ),
139  Layout.VariantHierarchy.Variants.begin()->second.Name ) );
140  }
141 
142  if( Layout.ReuseBlocks.size() > 0 )
143  {
144  wxLogWarning(
145  _( "The CADSTAR design contains re-use blocks which has no KiCad equivalent. The "
146  "re-use block information has been discarded during the import." ) );
147  }
148 
149  wxLogMessage(
150  _( "The CADSTAR design has been imported successfully.\n"
151  "Please review the import errors and warnings (if any)." ) );
152 }
153 
155 {
156  std::vector<FOOTPRINT*> retval;
157 
158  for( std::pair<SYMDEF_ID, FOOTPRINT*> fpPair : m_libraryMap )
159  {
160  retval.push_back( static_cast<FOOTPRINT*>( fpPair.second->Clone() ) );
161  }
162 
163  return retval;
164 }
165 
166 
168  const wxString& aCadstarLayerName,
169  const PCB_LAYER_ID& aKiCadLayer )
170 {
171  if( m_logLayerWarnings )
172  {
173  wxLogWarning( wxString::Format(
174  _( "The CADSTAR layer '%s' has no KiCad equivalent. All elements on this "
175  "layer have been mapped to KiCad layer '%s' instead." ),
176  aCadstarLayerName, LSET::Name( aKiCadLayer ) ) );
177  }
178 }
179 
180 
181 void CADSTAR_PCB_ARCHIVE_LOADER::logBoardStackupMessage( const wxString& aCadstarLayerName,
182  const PCB_LAYER_ID& aKiCadLayer )
183 {
184  if( m_logLayerWarnings )
185  {
186  wxLogMessage( wxString::Format(
187  _( "The CADSTAR layer '%s' has been assumed to be a technical layer. All "
188  "elements on this layer have been mapped to KiCad layer '%s'." ),
189  aCadstarLayerName, LSET::Name( aKiCadLayer ) ) );
190  }
191 }
192 
193 
195  BOARD_STACKUP_ITEM* aKiCadItem,
196  int aDielectricSublayer )
197 {
198  if( !aCadstarLayer.MaterialId.IsEmpty() )
199  {
200  MATERIAL material = Assignments.Layerdefs.Materials.at( aCadstarLayer.MaterialId );
201 
202  aKiCadItem->SetMaterial( material.Name, aDielectricSublayer );
203  aKiCadItem->SetEpsilonR( material.Permittivity.GetDouble(), aDielectricSublayer );
204  aKiCadItem->SetLossTangent( material.LossTangent.GetDouble(), aDielectricSublayer );
205  //TODO add Resistivity when KiCad supports it
206  }
207 
208  aKiCadItem->SetLayerName( aCadstarLayer.Name );
209  aKiCadItem->SetThickness( getKiCadLength( aCadstarLayer.Thickness ), aDielectricSublayer );
210 }
211 
212 
214 {
215  // Structure describing an electrical layer with optional dielectric layers below it
216  // (construction layers in CADSTAR)
217  struct LAYER_BLOCK
218  {
219  LAYER_ID ElecLayerID = wxEmptyString; // Normally not empty, but could be empty if the
220  // first layer in the stackup is a construction
221  // layer
222  std::vector<LAYER_ID> ConstructionLayers; // Normally empty for the last electrical layer
223  // but it is possible to build a board in CADSTAR
224  // with no construction layers or with the bottom
225  // layer being a construction layer
226 
227  bool IsInitialised() { return !ElecLayerID.IsEmpty() || ConstructionLayers.size() > 0; };
228  };
229 
230  std::vector<LAYER_BLOCK> cadstarBoardStackup;
231  LAYER_BLOCK currentBlock;
232 
233  // Find the electrical and construction (dielectric) layers in the stackup
234  for( LAYER_ID cadstarLayerID : Assignments.Layerdefs.LayerStack )
235  {
236  LAYER cadstarLayer = Assignments.Layerdefs.Layers.at( cadstarLayerID );
237 
238  if( cadstarLayer.Type == LAYER_TYPE::JUMPERLAYER ||
239  cadstarLayer.Type == LAYER_TYPE::POWER ||
240  cadstarLayer.Type == LAYER_TYPE::ELEC )
241  {
242  if( currentBlock.IsInitialised() )
243  {
244  cadstarBoardStackup.push_back( currentBlock );
245  currentBlock = LAYER_BLOCK(); // reset the block
246  }
247 
248  currentBlock.ElecLayerID = cadstarLayerID;
249  }
250  else if( cadstarLayer.Type == LAYER_TYPE::CONSTRUCTION )
251  {
252  currentBlock.ConstructionLayers.push_back( cadstarLayerID );
253  }
254  }
255 
256  if( currentBlock.IsInitialised() )
257  cadstarBoardStackup.push_back( currentBlock );
258 
259  int totalCopperLayers = cadstarBoardStackup.size();
260 
261  // Special case: last layer in the stackup is a construction layer, we need to use B.Cu as a
262  // dummy layer
263  if( cadstarBoardStackup.back().ConstructionLayers.size() > 0 )
264  {
265  cadstarBoardStackup.push_back( LAYER_BLOCK() ); //Add dummy layer at the end
266  ++totalCopperLayers;
267  }
268 
269  // Make sure it is an even number of layers (KiCad doesn't yet support unbalanced stack-ups)
270  if( ( totalCopperLayers % 2 ) != 0 )
271  {
272  LAYER_BLOCK bottomLayer = cadstarBoardStackup.back();
273  cadstarBoardStackup.pop_back();
274 
275  LAYER_BLOCK secondToLastLayer = cadstarBoardStackup.back();
276  cadstarBoardStackup.pop_back();
277 
278  LAYER_BLOCK dummyLayer;
279  LAYER_ID lastConstruction = secondToLastLayer.ConstructionLayers.back();
280 
281  if( secondToLastLayer.ConstructionLayers.size() > 1 )
282  {
283  // At least two construction layers, lets remove it here and use it in the dummy layer
284  secondToLastLayer.ConstructionLayers.pop_back();
285  }
286  else
287  {
288  // There is only one construction layer, lets halve its thickness so it is split evenly
289  // between this layer and the dummy layer
290  Assignments.Layerdefs.Layers.at( lastConstruction ).Thickness /= 2;
291  }
292 
293  dummyLayer.ConstructionLayers.push_back( lastConstruction );
294  cadstarBoardStackup.push_back( secondToLastLayer );
295  cadstarBoardStackup.push_back( dummyLayer );
296  cadstarBoardStackup.push_back( bottomLayer );
297  ++totalCopperLayers;
298  }
299 
300  wxASSERT( totalCopperLayers == cadstarBoardStackup.size() );
301  wxASSERT( cadstarBoardStackup.back().ConstructionLayers.size() == 0 );
302 
303  // Create a new stackup from default stackup list
305  stackup.RemoveAll();
308  m_board->SetCopperLayerCount( totalCopperLayers );
309  stackup.BuildDefaultStackupList( &m_board->GetDesignSettings(), totalCopperLayers );
310 
311  size_t stackIndex = 0;
312 
313  for( BOARD_STACKUP_ITEM* item : stackup.GetList() )
314  {
315  if( item->GetType() == BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_COPPER )
316  {
317  LAYER_ID layerID = cadstarBoardStackup.at( stackIndex ).ElecLayerID;
318 
319  if( layerID.IsEmpty() )
320  {
321  // Loading a dummy layer. Make zero thickness so it doesn't affect overall stackup
322  item->SetThickness( 0 );
323  }
324  else
325  {
326  LAYER copperLayer = Assignments.Layerdefs.Layers.at( layerID );
327  initStackupItem( copperLayer, item, 0 );
328  LAYER_T copperType = LAYER_T::LT_SIGNAL;
329 
330  switch( copperLayer.Type )
331  {
333  copperType = LAYER_T::LT_JUMPER;
334  break;
335 
336  case LAYER_TYPE::ELEC:
337  copperType = LAYER_T::LT_SIGNAL;
338  break;
339 
340  case LAYER_TYPE::POWER:
341  copperType = LAYER_T::LT_POWER;
342  m_powerPlaneLayers.push_back( copperLayer.ID ); //need to add a Copper zone
343  break;
344 
345  default:
346  wxFAIL_MSG( "Unexpected Layer type. Was expecting an electrical type" );
347  break;
348  }
349 
350  m_board->SetLayerType( item->GetBrdLayerId(), copperType );
351  m_board->SetLayerName( item->GetBrdLayerId(), item->GetLayerName() );
352  m_layermap.insert( { copperLayer.ID, item->GetBrdLayerId() } );
353  }
354  }
355  else if( item->GetType() == BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_DIELECTRIC )
356  {
357  LAYER_BLOCK layerBlock = cadstarBoardStackup.at( stackIndex );
358  LAYER_BLOCK layerBlockBelow = cadstarBoardStackup.at( stackIndex + 1 );
359 
360  // We should have made sure all layer blocks have at least one construction layer
361  wxASSERT( layerBlock.ConstructionLayers.size() > 0 );
362 
363  int dielectricId = stackIndex + 1;
364  // item->SetBrdLayerId();
365  item->SetDielectricLayerId( dielectricId );
366 
367  //Prepreg or core?
368  //Look at CADSTAR layer embedding (see LAYER->Embedding) to check whether the electrical
369  //layer embeds above and below to decide if current layer is prepreg or core
370  if( layerBlock.ElecLayerID.IsEmpty() )
371  {
372  //Dummy electrical layer, assume prepreg
373  item->SetTypeName( KEY_PREPREG );
374  }
375  else
376  {
377  LAYER copperLayer = Assignments.Layerdefs.Layers.at( layerBlock.ElecLayerID );
378 
379  if( layerBlockBelow.ElecLayerID.IsEmpty() )
380  {
381  // Dummy layer below, just use current layer to decide
382 
383  if( copperLayer.Embedding == EMBEDDING::ABOVE )
384  item->SetTypeName( KEY_CORE );
385  else
386  item->SetTypeName( KEY_PREPREG );
387  }
388  else
389  {
390  LAYER copperLayerBelow =
391  Assignments.Layerdefs.Layers.at( layerBlockBelow.ElecLayerID );
392 
393  if( copperLayer.Embedding == EMBEDDING::ABOVE )
394  {
395  // Need to check layer below is embedding downwards
396  if( copperLayerBelow.Embedding == EMBEDDING::BELOW )
397  item->SetTypeName( KEY_CORE );
398  else
399  item->SetTypeName( KEY_PREPREG );
400  }
401  else
402  {
403  item->SetTypeName( KEY_PREPREG );
404  }
405  }
406  }
407 
408  int dielectricSublayer = 0;
409 
410  for( LAYER_ID constructionLaID : layerBlock.ConstructionLayers )
411  {
412  LAYER dielectricLayer = Assignments.Layerdefs.Layers.at( constructionLaID );
413 
414  if( dielectricSublayer )
415  item->AddDielectricPrms( dielectricSublayer );
416 
417  initStackupItem( dielectricLayer, item, dielectricSublayer );
418  m_board->SetLayerName( item->GetBrdLayerId(), item->GetLayerName() );
419  m_layermap.insert( { dielectricLayer.ID, item->GetBrdLayerId() } );
420  ++dielectricSublayer;
421  }
422 
423  ++stackIndex;
424  }
425  }
426 
427  int numElecAndPowerLayers = 0;
428 
429  for( LAYER_ID cadstarLayerID : Assignments.Layerdefs.LayerStack )
430  {
431  LAYER curLayer = Assignments.Layerdefs.Layers.at( cadstarLayerID );
433  wxString layerName = curLayer.Name.Lower();
434 
435  enum class LOG_LEVEL
436  {
437  NONE,
438  MSG,
439  WARN
440  };
441 
442  auto selectLayerID =
443  [&]( PCB_LAYER_ID aFront, PCB_LAYER_ID aBack, LOG_LEVEL aLogType )
444  {
445  if( numElecAndPowerLayers > 0 )
446  kicadLayerID = aBack;
447  else
448  kicadLayerID = aFront;
449 
450  switch( aLogType )
451  {
452  case LOG_LEVEL::NONE:
453  break;
454 
455  case LOG_LEVEL::MSG:
456  logBoardStackupMessage( curLayer.Name, kicadLayerID );
457  break;
458 
459  case LOG_LEVEL::WARN:
460  logBoardStackupMessage( curLayer.Name, kicadLayerID );
461  break;
462  }
463  };
464 
465  switch( curLayer.Type )
466  {
467  case LAYER_TYPE::ALLDOC:
468  case LAYER_TYPE::ALLELEC:
471  case LAYER_TYPE::NOLAYER:
472  //Shouldn't be here if CPA file is correctly parsed and not corrupt
473  THROW_IO_ERROR( wxString::Format( _( "Unexpected layer '%s' in layer stack." ),
474  curLayer.Name ) );
475  break;
476 
478  case LAYER_TYPE::ELEC:
479  case LAYER_TYPE::POWER:
480  ++numElecAndPowerLayers;
483  //Already dealt with these when loading board stackup
484  break;
485 
486  case LAYER_TYPE::DOC:
487  selectLayerID( PCB_LAYER_ID::Dwgs_User, PCB_LAYER_ID::Cmts_User, LOG_LEVEL::WARN );
488  break;
489 
490  case LAYER_TYPE::NONELEC:
491  switch( curLayer.SubType )
492  {
495  break;
496 
499  break;
500 
502  // Generic Non-electrical layer (older CADSTAR versions).
503  // Attempt to detect technical layers by string matching.
504  if( layerName.Contains( "glue" ) || layerName.Contains( "adhesive" ) )
505  {
506  selectLayerID( PCB_LAYER_ID::F_Adhes, PCB_LAYER_ID::B_Adhes, LOG_LEVEL::MSG );
507  }
508  else if( layerName.Contains( "silk" ) || layerName.Contains( "legend" ) )
509  {
510  selectLayerID( PCB_LAYER_ID::F_SilkS, PCB_LAYER_ID::B_SilkS, LOG_LEVEL::MSG );
511  }
512  else if( layerName.Contains( "assembly" ) || layerName.Contains( "fabrication" ) )
513  {
514  selectLayerID( PCB_LAYER_ID::F_Fab, PCB_LAYER_ID::B_Fab, LOG_LEVEL::MSG );
515  }
516  else if( layerName.Contains( "resist" ) || layerName.Contains( "mask" ) )
517  {
518  selectLayerID( PCB_LAYER_ID::F_Mask, PCB_LAYER_ID::B_Mask, LOG_LEVEL::MSG );
519  }
520  else if( layerName.Contains( "paste" ) )
521  {
522  selectLayerID( PCB_LAYER_ID::F_Paste, PCB_LAYER_ID::B_Paste, LOG_LEVEL::MSG );
523  }
524  else
525  {
526  // Does not appear to be a technical layer - Map to Eco layers for now.
528  LOG_LEVEL::WARN );
529  }
530  break;
531 
533  selectLayerID( PCB_LAYER_ID::F_Paste, PCB_LAYER_ID::B_Paste, LOG_LEVEL::MSG );
534  break;
535 
537  selectLayerID( PCB_LAYER_ID::F_SilkS, PCB_LAYER_ID::B_SilkS, LOG_LEVEL::MSG );
538  break;
539 
541  selectLayerID( PCB_LAYER_ID::F_Mask, PCB_LAYER_ID::B_Mask, LOG_LEVEL::MSG );
542  break;
543 
546  //Unsure what these layer types are used for. Map to Eco layers for now.
547  selectLayerID( PCB_LAYER_ID::Eco1_User, PCB_LAYER_ID::Eco2_User, LOG_LEVEL::WARN );
548  break;
549 
550  default:
551  wxFAIL_MSG( "Unknown CADSTAR Layer Sub-type" );
552  break;
553  }
554  break;
555 
556  default:
557  wxFAIL_MSG( "Unknown CADSTAR Layer Type" );
558  break;
559  }
560 
561  m_layermap.insert( { curLayer.ID, kicadLayerID } );
562  }
563 }
564 
565 
567 {
568  LSET enabledLayers = m_board->GetEnabledLayers();
569  LSET validRemappingLayers = enabledLayers | LSET::AllBoardTechMask() |
571 
572  std::vector<INPUT_LAYER_DESC> inputLayers;
573  std::map<wxString, LAYER_ID> cadstarLayerNameMap;
574 
575  for( std::pair<LAYER_ID, PCB_LAYER_ID> layerPair : m_layermap )
576  {
577  LAYER* curLayer = &Assignments.Layerdefs.Layers.at( layerPair.first );
578 
579  //Only remap layers that we aren't sure about
580  if( curLayer->Type == LAYER_TYPE::DOC
581  || ( curLayer->Type == LAYER_TYPE::NONELEC
582  && curLayer->SubType == LAYER_SUBTYPE::LAYERSUBTYPE_NONE )
583  || ( curLayer->Type == LAYER_TYPE::NONELEC
584  && curLayer->SubType == LAYER_SUBTYPE::LAYERSUBTYPE_ROUT )
585  || ( curLayer->Type == LAYER_TYPE::NONELEC
587  {
588  INPUT_LAYER_DESC iLdesc;
589  iLdesc.Name = curLayer->Name;
590  iLdesc.PermittedLayers = validRemappingLayers;
591  iLdesc.AutoMapLayer = layerPair.second;
592 
593  inputLayers.push_back( iLdesc );
594  cadstarLayerNameMap.insert( { curLayer->Name, curLayer->ID } );
595  }
596  }
597 
598  if( inputLayers.size() == 0 )
599  return;
600 
601  // Callback:
602  std::map<wxString, PCB_LAYER_ID> reMappedLayers = m_layerMappingHandler( inputLayers );
603 
604  for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers )
605  {
606  if( layerPair.second == PCB_LAYER_ID::UNDEFINED_LAYER )
607  {
608  wxFAIL_MSG( "Unexpected Layer ID" );
609  continue;
610  }
611 
612  LAYER_ID cadstarLayerID = cadstarLayerNameMap.at( layerPair.first );
613  m_layermap.at( cadstarLayerID ) = layerPair.second;
614  enabledLayers |= LSET( layerPair.second );
615  }
616 
617  m_board->SetEnabledLayers( enabledLayers );
618  m_board->SetVisibleLayers( enabledLayers );
619 }
620 
621 
623 {
625  std::map<SPACINGCODE_ID, SPACINGCODE>& spacingCodes = Assignments.Codedefs.SpacingCodes;
626 
627  auto applyRule =
628  [&]( wxString aID, int* aVal )
629  {
630  if( spacingCodes.find( aID ) == spacingCodes.end() )
631  wxLogWarning( _( "Design rule %s was not found. This was ignored." ) );
632  else
633  *aVal = getKiCadLength( spacingCodes.at( aID ).Spacing );
634  };
635 
636  //Note: for details on the different spacing codes see SPACINGCODE::ID
637 
638  applyRule( "T_T", &ds.m_MinClearance );
639  applyRule( "C_B", &ds.m_CopperEdgeClearance );
640  applyRule( "H_H", &ds.m_HoleToHoleMin );
641 
643  ds.m_ViasMinSize = ds.m_TrackMinWidth; // Not specified, assumed same as track width
644  ds.m_ViasMinAnnularWidth = ds.m_TrackMinWidth / 2; // Not specified, assumed half track width
645  ds.m_MinThroughDrill = PCB_IU_PER_MM * 0.0508; // CADSTAR does not specify a minimum hole size
646  // so set to minimum permitted in KiCad (2 mils)
647  ds.m_HoleClearance = ds.m_CopperEdgeClearance; // Not specified, assumed same as edge
648 
649  auto applyNetClassRule = [&]( wxString aID, NETCLASS* aNetClassPtr,
650  void ( NETCLASS::*aFunc )( int ) ) {
651  int value = -1;
652  applyRule( aID, &value );
653 
654  if( value != -1 )
655  ( aNetClassPtr->*aFunc )( value );
656  };
657 
658  applyNetClassRule( "T_T", ds.GetDefault(), &::NETCLASS::SetClearance );
659 
661 
662  wxLogWarning( _( "KiCad design rules are different from CADSTAR ones. Only the compatible "
663  "design rules were imported. It is recommended that you review the design "
664  "rules that have been applied." ) );
665 }
666 
667 
669 {
670  for( std::pair<SYMDEF_ID, SYMDEF_PCB> symPair : Library.ComponentDefinitions )
671  {
672  SYMDEF_ID key = symPair.first;
673  SYMDEF_PCB component = symPair.second;
674  wxString fpName = component.ReferenceName + ( ( component.Alternate.size() > 0 ) ?
675  ( wxT( " (" ) + component.Alternate + wxT( ")" ) ) :
676  wxT( "" ) );
677  FOOTPRINT* footprint = new FOOTPRINT( m_board );
678  footprint->SetPosition( getKiCadPoint( component.Origin ) );
679 
680  LIB_ID libID;
681  libID.Parse( fpName, true );
682 
683  footprint->SetFPID( libID );
684  loadLibraryFigures( component, footprint );
685  loadLibraryAreas( component, footprint );
686  loadLibraryPads( component, footprint );
687  loadLibraryCoppers( component, footprint ); // Load coppers after pads to ensure correct
688  // ordering of pads in footprint->Pads()
689 
690  m_libraryMap.insert( std::make_pair( key, footprint ) );
691  }
692 }
693 
694 
696  FOOTPRINT* aFootprint )
697 {
698  for( std::pair<FIGURE_ID, FIGURE> figPair : aComponent.Figures )
699  {
700  FIGURE& fig = figPair.second;
701 
704  wxString::Format( "Component %s:%s -> Figure %s",
705  aComponent.ReferenceName,
706  aComponent.Alternate,
707  fig.ID ),
708  aFootprint );
709  }
710 }
711 
712 
714  FOOTPRINT* aFootprint )
715 {
716  int totalCopperPads = 0;
717 
718  for( COMPONENT_COPPER compCopper : aComponent.ComponentCoppers )
719  {
720  int lineThickness = getKiCadLength( getCopperCode( compCopper.CopperCodeID ).CopperWidth );
721  PCB_LAYER_ID copperLayer = getKiCadLayer( compCopper.LayerID );
722 
723  if( compCopper.AssociatedPadIDs.size() > 0 && LSET::AllCuMask().Contains( copperLayer )
724  && compCopper.Shape.Type == SHAPE_TYPE::SOLID )
725  {
726  // The copper is associated with pads and in an electrical layer which means it can
727  // have a net associated with it. Load as a pad instead.
728  // Note: we can only handle SOLID copper shapes. If the copper shape is an outline or
729  // hatched or outline, then we give up and load as a graphical shape instead.
730 
731  // Find the first non-PCB-only pad. If there are none, use the first one
732  COMPONENT_PAD anchorPad;
733  bool found = false;
734 
735  for( PAD_ID padID : compCopper.AssociatedPadIDs )
736  {
737  anchorPad = aComponent.ComponentPads.at( padID );
738 
739  if( !anchorPad.PCBonlyPad )
740  {
741  found = true;
742  break;
743  }
744  }
745 
746  if( !found )
747  anchorPad = aComponent.ComponentPads.at( compCopper.AssociatedPadIDs.front() );
748 
749  PAD* pad = new PAD( aFootprint );
750  pad->SetAttribute( PAD_ATTRIB::SMD );
751  pad->SetLayerSet( LSET( 1, copperLayer ) );
752  pad->SetNumber( anchorPad.Identifier.IsEmpty()
753  ? wxString::Format( wxT( "%ld" ), anchorPad.ID )
754  : anchorPad.Identifier );
755 
756  // Custom pad shape with an anchor at the position of one of the associated
757  // pads and same size as the pad. Shape circle as it fits inside a rectangle
758  // but not the other way round
759  PADCODE anchorpadcode = getPadCode( anchorPad.PadCodeID );
760  int anchorSize = getKiCadLength( anchorpadcode.Shape.Size );
761  wxPoint anchorPos = getKiCadPoint( anchorPad.Position );
762 
763  pad->SetShape( PAD_SHAPE::CUSTOM );
764  pad->SetAnchorPadShape( PAD_SHAPE::CIRCLE );
765  pad->SetSize( { anchorSize, anchorSize } );
766  pad->SetPosition( anchorPos );
767  pad->SetLocalCoord();
768  pad->SetLocked( true ); // Cadstar pads are always locked with respect to the footprint
769 
770  SHAPE_POLY_SET shapePolys = getPolySetFromCadstarShape( compCopper.Shape,
771  lineThickness,
772  aFootprint );
773  shapePolys.Move( aFootprint->GetPosition() - anchorPos );
774  pad->AddPrimitivePoly( shapePolys, 0, true );
775 
776  aFootprint->Add( pad, ADD_MODE::APPEND ); // Append so that we get the correct behaviour
777  // when finding pads by PAD_ID. See loadNets()
778 
779  m_librarycopperpads[aComponent.ID][anchorPad.ID].push_back( aFootprint->Pads().size() );
780  totalCopperPads++;
781 
782  // Now renumber all the associated pads if they are PCB Only
783  int numRenames = 0;
784  COMPONENT_PAD associatedPad;
785 
786  for( PAD_ID padID : compCopper.AssociatedPadIDs )
787  {
788  associatedPad = aComponent.ComponentPads.at( padID );
789 
790  if( associatedPad.PCBonlyPad )
791  {
792  PAD* assocPad = getPadReference( aFootprint, padID );
793  assocPad->SetNumber( pad->GetNumber() );
794  ++numRenames;
795  }
796  }
797 
798  if( numRenames < compCopper.AssociatedPadIDs.size() - 1 )
799  {
800  // This is an older design of thermal pad. The schematic will
801  // have multiple pins for the same pad, so lets use the
802  // "allow thermal pads" hack
803  aFootprint->SetKeywords( wxT( "allow thermal pads" ) );
804  }
805  }
806  else
807  {
808  drawCadstarShape( compCopper.Shape, copperLayer, lineThickness,
809  wxString::Format( "Component %s:%s -> Copper element",
810  aComponent.ReferenceName, aComponent.Alternate ),
811  aFootprint );
812  }
813  }
814 }
815 
816 
818  FOOTPRINT* aFootprint )
819 {
820  for( std::pair<COMP_AREA_ID, COMPONENT_AREA> areaPair : aComponent.ComponentAreas )
821  {
822  COMPONENT_AREA& area = areaPair.second;
823 
824  if( area.NoVias || area.NoTracks )
825  {
827  aFootprint );
828 
829  aFootprint->Add( zone, ADD_MODE::APPEND );
830 
831  if( isLayerSet( area.LayerID ) )
832  zone->SetLayerSet( getKiCadLayerSet( area.LayerID ) );
833  else
834  zone->SetLayer( getKiCadLayer( area.LayerID ) );
835 
836  zone->SetIsRuleArea( true ); //import all CADSTAR areas as Keepout zones
837  zone->SetDoNotAllowPads( false ); //no CADSTAR equivalent
838  zone->SetZoneName( area.ID );
839 
840  //There is no distinction between tracks and copper pours in CADSTAR Keepout zones
841  zone->SetDoNotAllowTracks( area.NoTracks );
842  zone->SetDoNotAllowCopperPour( area.NoTracks );
843 
844  zone->SetDoNotAllowVias( area.NoVias );
845  }
846  else
847  {
848  wxString libName = aComponent.ReferenceName;
849 
850  if( !aComponent.Alternate.IsEmpty() )
851  libName << wxT( " (" ) << aComponent.Alternate << wxT( ")" );
852 
853  wxLogError(
854  wxString::Format( _( "The CADSTAR area '%s' in library component '%s' does not "
855  "have a KiCad equivalent. The area is neither a via nor "
856  "route keepout area. The area was not imported." ),
857  area.ID, libName ) );
858  }
859  }
860 }
861 
862 
864  FOOTPRINT* aFootprint )
865 {
866  for( std::pair<PAD_ID, COMPONENT_PAD> padPair : aComponent.ComponentPads )
867  {
868  PAD* pad = getKiCadPad( padPair.second, aFootprint );
869  aFootprint->Add( pad, ADD_MODE::APPEND ); // Append so that we get correct behaviour
870  // when finding pads by PAD_ID - see loadNets()
871  }
872 }
873 
874 
876 {
877  PADCODE csPadcode = getPadCode( aCadstarPad.PadCodeID );
878  wxString errorMSG;
879 
880  PAD* pad = new PAD( aParent );
881  LSET padLayerSet;
882 
883  switch( aCadstarPad.Side )
884  {
885  case PAD_SIDE::MAXIMUM: //Bottom side
886  pad->SetAttribute( PAD_ATTRIB::SMD );
887  padLayerSet |= LSET( 3, B_Cu, B_Paste, B_Mask );
888  break;
889 
890  case PAD_SIDE::MINIMUM: //TOP side
891  pad->SetAttribute( PAD_ATTRIB::SMD );
892  padLayerSet |= LSET( 3, F_Cu, F_Paste, F_Mask );
893  break;
894 
896  if( csPadcode.Plated )
897  pad->SetAttribute( PAD_ATTRIB::PTH );
898  else
899  pad->SetAttribute( PAD_ATTRIB::NPTH );
900 
901  padLayerSet = LSET::AllCuMask() | LSET( 4, F_Mask, B_Mask, F_Paste, B_Paste );
902  break;
903 
904  default:
905  wxFAIL_MSG( "Unknown Pad type" );
906  }
907 
908  pad->SetLocalSolderMaskMargin( 0 );
909  pad->SetLocalSolderPasteMargin( 0 );
910  pad->SetLocalSolderPasteMarginRatio( 0.0 );
911  bool complexPadErrorLogged = false;
912 
913  for( auto& reassign : csPadcode.Reassigns )
914  {
915  PCB_LAYER_ID kiLayer = getKiCadLayer( reassign.first );
916  CADSTAR_PAD_SHAPE shape = reassign.second;
917 
918  if( shape.Size == 0 )
919  {
920  padLayerSet.reset( kiLayer );
921  }
922  else
923  {
924  int newMargin = getKiCadLength( shape.Size - csPadcode.Shape.Size ) / 2;
925 
926  if( kiLayer == F_Mask || kiLayer == B_Mask )
927  {
928  if( std::abs( pad->GetLocalSolderMaskMargin() ) < std::abs( newMargin ) )
929  pad->SetLocalSolderMaskMargin( newMargin );
930  }
931  else if( kiLayer == F_Paste || kiLayer == B_Paste )
932  {
933  if( std::abs( pad->GetLocalSolderPasteMargin() ) < std::abs( newMargin ) )
934  pad->SetLocalSolderPasteMargin( newMargin );
935  }
936  else
937  {
938  //TODO fix properly when KiCad supports full padstacks
939 
940  if( !complexPadErrorLogged )
941  {
942  complexPadErrorLogged = true;
943  errorMSG +=
944  wxT( "\n - " )
946  _( "The CADSTAR pad definition '%s' is a complex pad stack, "
947  "which is not supported in KiCad. Please review the "
948  "imported pads as they may require manual correction." ),
949  csPadcode.Name );
950  }
951  }
952  }
953  }
954 
955  pad->SetLayerSet( padLayerSet );
956 
957  pad->SetNumber( aCadstarPad.Identifier.IsEmpty() ?
958  wxString::Format( wxT( "%ld" ), aCadstarPad.ID ) :
959  aCadstarPad.Identifier );
960 
961  if( csPadcode.Shape.Size == 0 )
962  {
963  if( csPadcode.DrillDiameter == UNDEFINED_VALUE
964  && aCadstarPad.Side == PAD_SIDE::THROUGH_HOLE )
965  {
966  // Through-hole, zero sized pad?. Lets load this just on the F_Mask for now to
967  // prevent DRC errors.
968  // TODO: This could be a custom padstack, update when KiCad supports padstacks
969  pad->SetAttribute( PAD_ATTRIB::SMD );
970  pad->SetLayerSet( LSET( 1, F_Mask ) );
971  }
972 
973  // zero sized pads seems to break KiCad so lets make it very small instead
974  csPadcode.Shape.Size = 1;
975  }
976 
977  wxPoint padOffset = { 0, 0 }; // offset of the pad origin (before rotating)
978  wxPoint drillOffset = { 0, 0 }; // offset of the drill origin w.r.t. the pad (before rotating)
979 
980  switch( csPadcode.Shape.ShapeType )
981  {
983  //todo fix: use custom shape instead (Donught shape, i.e. a circle with a hole)
984  pad->SetShape( PAD_SHAPE::CIRCLE );
985  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
986  getKiCadLength( csPadcode.Shape.Size ) } );
987  break;
988 
990  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
991  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
992  + (long long) csPadcode.Shape.LeftLength
993  + (long long) csPadcode.Shape.RightLength ),
994  getKiCadLength( csPadcode.Shape.Size ) } );
997  pad->SetRoundRectRadiusRatio( 0.5 );
998  pad->SetChamferRectRatio( 0.0 );
999 
1000  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
1001  ( (long long) csPadcode.Shape.RightLength / 2 ) );
1002  break;
1003 
1005  pad->SetShape( PAD_SHAPE::CIRCLE );
1006  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
1007  getKiCadLength( csPadcode.Shape.Size ) } );
1008  break;
1009 
1011  {
1012  // Cadstar diamond shape is a square rotated 45 degrees
1013  // We convert it in KiCad to a square with chamfered edges
1014  int sizeOfSquare = (double) getKiCadLength( csPadcode.Shape.Size ) * sqrt(2.0);
1015  pad->SetShape( PAD_SHAPE::RECT );
1016  pad->SetChamferRectRatio( 0.5 );
1017  pad->SetSize( { sizeOfSquare, sizeOfSquare } );
1018 
1019  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
1020  ( (long long) csPadcode.Shape.RightLength / 2 ) );
1021  }
1022  break;
1023 
1025  pad->SetShape( PAD_SHAPE::OVAL );
1026  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
1027  + (long long) csPadcode.Shape.LeftLength
1028  + (long long) csPadcode.Shape.RightLength ),
1029  getKiCadLength( csPadcode.Shape.Size ) } );
1030 
1031  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
1032  ( (long long) csPadcode.Shape.RightLength / 2 ) );
1033  break;
1034 
1036  pad->SetShape( PAD_SHAPE::CHAMFERED_RECT );
1037  pad->SetChamferPositions( RECT_CHAMFER_POSITIONS::RECT_CHAMFER_ALL );
1038  pad->SetChamferRectRatio( 0.25 );
1039  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
1040  getKiCadLength( csPadcode.Shape.Size ) } );
1041  break;
1042 
1044  pad->SetShape( PAD_SHAPE::RECT );
1045  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
1046  + (long long) csPadcode.Shape.LeftLength
1047  + (long long) csPadcode.Shape.RightLength ),
1048  getKiCadLength( csPadcode.Shape.Size ) } );
1049 
1050  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
1051  ( (long long) csPadcode.Shape.RightLength / 2 ) );
1052  break;
1053 
1055  pad->SetShape( PAD_SHAPE::RECT );
1056  pad->SetRoundRectCornerRadius( getKiCadLength( csPadcode.Shape.InternalFeature ) );
1057  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
1058  + (long long) csPadcode.Shape.LeftLength
1059  + (long long) csPadcode.Shape.RightLength ),
1060  getKiCadLength( csPadcode.Shape.Size ) } );
1061 
1062  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
1063  ( (long long) csPadcode.Shape.RightLength / 2 ) );
1064  break;
1065 
1066 
1068  pad->SetShape( PAD_SHAPE::RECT );
1069  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
1070  getKiCadLength( csPadcode.Shape.Size ) } );
1071  break;
1072 
1073  default:
1074  wxFAIL_MSG( "Unknown Pad Shape" );
1075  }
1076 
1077  if( csPadcode.ReliefClearance != UNDEFINED_VALUE )
1078  pad->SetThermalGap( getKiCadLength( csPadcode.ReliefClearance ) );
1079 
1080  if( csPadcode.ReliefWidth != UNDEFINED_VALUE )
1081  pad->SetThermalSpokeWidth( getKiCadLength( csPadcode.ReliefWidth ) );
1082 
1083  if( csPadcode.DrillDiameter != UNDEFINED_VALUE )
1084  {
1085  if( csPadcode.SlotLength != UNDEFINED_VALUE )
1086  {
1088  pad->SetDrillSize( { getKiCadLength( (long long) csPadcode.SlotLength +
1089  (long long) csPadcode.DrillDiameter ),
1090  getKiCadLength( csPadcode.DrillDiameter ) } );
1091  }
1092  else
1093  {
1095  pad->SetDrillSize( { getKiCadLength( csPadcode.DrillDiameter ),
1096  getKiCadLength( csPadcode.DrillDiameter ) } );
1097  }
1098 
1099  drillOffset.x = -getKiCadLength( csPadcode.DrillXoffset );
1100  drillOffset.y = getKiCadLength( csPadcode.DrillYoffset );
1101  }
1102  else
1103  {
1104  pad->SetDrillSize( { 0, 0 } );
1105  }
1106 
1107  if( csPadcode.SlotOrientation != 0 )
1108  {
1109  LSET lset = pad->GetLayerSet();
1110  lset &= LSET::AllCuMask();
1111 
1112  if( lset.size() > 0 )
1113  {
1114  SHAPE_POLY_SET padOutline;
1115  PCB_LAYER_ID layer = lset.Seq().at( 0 );
1116  int maxError = m_board->GetDesignSettings().m_MaxError;
1117 
1118  pad->SetPosition( { 0, 0 } );
1119  pad->SetPos0( { 0, 0 } );
1120  pad->TransformShapeWithClearanceToPolygon( padOutline, layer, 0, maxError,
1122 
1123  PCB_SHAPE* padShape = new PCB_SHAPE;
1124  padShape->SetShape( SHAPE_T::POLY );
1125  padShape->SetFilled( true );
1126  padShape->SetPolyShape( padOutline );
1127  padShape->SetWidth( 0 );
1128  padShape->Move( padOffset - drillOffset );
1129  padShape->Rotate( wxPoint( 0, 0 ),
1130  1800.0 - getAngleTenthDegree( csPadcode.SlotOrientation ) );
1131 
1132  SHAPE_POLY_SET editedPadOutline = padShape->GetPolyShape();
1133 
1134  if( editedPadOutline.Contains( { 0, 0 } ) )
1135  {
1136  pad->SetAnchorPadShape( PAD_SHAPE::RECT );
1137  pad->SetSize( wxSize( { 4, 4 } ) );
1138  pad->SetShape( PAD_SHAPE::CUSTOM );
1139  pad->AddPrimitive( padShape );
1140  padOffset = { 0, 0 };
1141  }
1142  else
1143  {
1144  // The CADSTAR pad has the hole shape outside the pad shape
1145  // Lets just put the hole in the center of the pad instead
1146  csPadcode.SlotOrientation = 0;
1147  drillOffset = { 0, 0 };
1148 
1149  errorMSG +=
1150  wxT( "\n - " )
1151  + wxString::Format(
1152  _( "The CADSTAR pad definition '%s' has the hole shape outside the "
1153  "pad shape. The hole has been moved to the center of the pad." ),
1154  csPadcode.Name );
1155  }
1156  }
1157  else
1158  {
1159  wxFAIL_MSG( "No copper layers defined in the pad?" );
1160  csPadcode.SlotOrientation = 0;
1161  pad->SetOffset( drillOffset );
1162  }
1163  }
1164  else
1165  {
1166  pad->SetOffset( drillOffset );
1167  }
1168 
1169  double padOrientation = getAngleTenthDegree( aCadstarPad.OrientAngle )
1170  + getAngleTenthDegree( csPadcode.Shape.OrientAngle );
1171 
1172  RotatePoint( &padOffset, padOrientation );
1173  RotatePoint( &drillOffset, padOrientation );
1174  pad->SetPos0( getKiCadPoint( aCadstarPad.Position ) - aParent->GetPosition() - padOffset
1175  - drillOffset );
1176  pad->SetOrientation( padOrientation + getAngleTenthDegree( csPadcode.SlotOrientation ) );
1177 
1178  //TODO handle csPadcode.Reassigns when KiCad supports full padstacks
1179 
1180  pad->SetLocked( true ); // Cadstar pads are always locked with respect to the footprint
1181 
1182  //log warnings:
1183  if( m_padcodesTested.find( csPadcode.ID ) == m_padcodesTested.end() && !errorMSG.IsEmpty() )
1184  {
1185  wxLogError( _( "The CADSTAR pad definition '%s' has import errors: %s" ),
1186  csPadcode.Name,
1187  errorMSG );
1188 
1189  m_padcodesTested.insert( csPadcode.ID );
1190  }
1191 
1192  return pad;
1193 }
1194 
1195 
1197  const PAD_ID aCadstarPadID )
1198 {
1199  return aFootprint->Pads().at( aCadstarPadID - (long long) 1 );
1200 }
1201 
1202 
1204 {
1205  for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1206  {
1207  GROUP& csGroup = groupPair.second;
1208 
1209  PCB_GROUP* kiGroup = new PCB_GROUP( m_board );
1210 
1211  m_board->Add( kiGroup );
1212  kiGroup->SetName( csGroup.Name );
1213  kiGroup->SetLocked( csGroup.Fixed );
1214 
1215  m_groupMap.insert( { csGroup.ID, kiGroup } );
1216  }
1217 
1218  //now add any groups to their parent group
1219  for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1220  {
1221  GROUP& csGroup = groupPair.second;
1222 
1223  if( !csGroup.GroupID.IsEmpty() )
1224  {
1225  if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1226  {
1227  THROW_IO_ERROR( wxString::Format( _( "Unable to find group ID %s in the group "
1228  "definitions." ),
1229  csGroup.ID ) );
1230  }
1231  else if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1232  {
1233  THROW_IO_ERROR( wxString::Format( _( "Unable to find sub group %s in the group "
1234  "map (parent group ID=%s, Name=%s)." ),
1235  csGroup.GroupID,
1236  csGroup.ID,
1237  csGroup.Name ) );
1238  }
1239  else
1240  {
1241  PCB_GROUP* kiCadGroup = m_groupMap.at( csGroup.ID );
1242  PCB_GROUP* parentGroup = m_groupMap.at( csGroup.GroupID );
1243  parentGroup->AddItem( kiCadGroup );
1244  }
1245  }
1246  }
1247 }
1248 
1249 
1251 {
1252  for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
1253  {
1254  CADSTAR_BOARD& board = boardPair.second;
1255  GROUP_ID boardGroup = createUniqueGroupID( wxT( "Board" ) );
1257  getLineThickness( board.LineCodeID ), wxString::Format( "BOARD %s", board.ID ),
1258  m_board, boardGroup );
1259 
1260  if( !board.GroupID.IsEmpty() )
1261  {
1262  addToGroup( board.GroupID, getKiCadGroup( boardGroup ) );
1263  }
1264 
1265  //TODO process board attributes when KiCad supports them
1266  }
1267 }
1268 
1269 
1271 {
1272  for( std::pair<FIGURE_ID, FIGURE> figPair : Layout.Figures )
1273  {
1274  FIGURE& fig = figPair.second;
1276  getLineThickness( fig.LineCodeID ), wxString::Format( "FIGURE %s", fig.ID ),
1277  m_board, fig.GroupID );
1278 
1279  //TODO process "swaprule" (doesn't seem to apply to Layout Figures?)
1280  //TODO process re-use block when KiCad Supports it
1281  //TODO process attributes when KiCad Supports attributes in figures
1282  }
1283 }
1284 
1285 
1287 {
1288  for( std::pair<TEXT_ID, TEXT> txtPair : Layout.Texts )
1289  {
1290  TEXT& csTxt = txtPair.second;
1291  drawCadstarText( csTxt, m_board );
1292  }
1293 }
1294 
1295 
1297 {
1298  for( std::pair<DIMENSION_ID, DIMENSION> dimPair : Layout.Dimensions )
1299  {
1300  DIMENSION& csDim = dimPair.second;
1301 
1302  switch( csDim.Type )
1303  {
1304  case DIMENSION::TYPE::LINEARDIM:
1305  switch( csDim.Subtype )
1306  {
1307  case DIMENSION::SUBTYPE::ANGLED:
1308  wxLogWarning( wxString::Format( _( "Dimension ID %s is an angled dimension, which "
1309  "has no KiCad equivalent. An aligned dimension "
1310  "was loaded instead." ),
1311  csDim.ID ) );
1313  case DIMENSION::SUBTYPE::DIRECT:
1314  case DIMENSION::SUBTYPE::ORTHOGONAL:
1315  {
1316  if( csDim.Line.Style == DIMENSION::LINE::STYLE::EXTERNAL )
1317  {
1318  wxLogWarning( wxString::Format(
1319  _( "Dimension ID %s has 'External' style in CADSTAR. External "
1320  "dimension styles are not yet supported in KiCad. The dimension "
1321  "object was imported with an internal dimension style instead." ),
1322  csDim.ID ) );
1323  }
1324 
1325  PCB_DIM_ALIGNED* dimension = nullptr;
1326 
1327  if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1328  {
1329  dimension = new PCB_DIM_ORTHOGONAL( m_board );
1330  PCB_DIM_ORTHOGONAL* orDim = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1331 
1332  if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1334  else
1336  }
1337  else
1338  {
1339  dimension = new PCB_DIM_ALIGNED( m_board );
1340  }
1341 
1342  m_board->Add( dimension, ADD_MODE::APPEND );
1343  applyDimensionSettings( csDim, dimension );
1344 
1345  dimension->SetExtensionHeight(
1347 
1348  // Calculate height:
1349  wxPoint crossbarStart = getKiCadPoint( csDim.Line.Start );
1350  wxPoint crossbarEnd = getKiCadPoint( csDim.Line.End );
1351  VECTOR2I crossbarVector = crossbarEnd - crossbarStart;
1352  VECTOR2I heightVector = crossbarStart - dimension->GetStart();
1353  double height = 0.0;
1354 
1355  if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1356  {
1357  if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1358  height = heightVector.y;
1359  else
1360  height = heightVector.x;
1361  }
1362  else
1363  {
1364  double angle = crossbarVector.Angle() + ( M_PI / 2 );
1365  height = heightVector.x * cos( angle ) + heightVector.y * sin( angle );
1366  }
1367 
1368  dimension->SetHeight( height );
1369  }
1370  break;
1371 
1372  default:
1373  // Radius and diameter dimensions are LEADERDIM (even if not actually leader)
1374  // Angular dimensions are always ANGLEDIM
1375  wxLogError( _( "Unexpected Dimension type (ID %s). This was not imported." ),
1376  csDim.ID );
1377  continue;
1378  }
1379  break;
1380 
1381  case DIMENSION::TYPE::LEADERDIM:
1382  //TODO: update import when KiCad supports radius and diameter dimensions
1383 
1384  if( csDim.Line.Style == DIMENSION::LINE::STYLE::INTERNAL )
1385  {
1386  // "internal" is a simple double sided arrow from start to end (no extension lines)
1387  PCB_DIM_ALIGNED* dimension = new PCB_DIM_ALIGNED( m_board );
1388  m_board->Add( dimension, ADD_MODE::APPEND );
1389  applyDimensionSettings( csDim, dimension );
1390 
1391  // Lets set again start/end:
1392  dimension->SetStart( getKiCadPoint( csDim.Line.Start ) );
1393  dimension->SetEnd( getKiCadPoint( csDim.Line.End ) );
1394 
1395  // Do not use any extension lines:
1396  dimension->SetExtensionOffset( 0 );
1397  dimension->SetExtensionHeight( 0 );
1398  dimension->SetHeight( 0 );
1399  }
1400  else
1401  {
1402  // "external" is a "leader" style dimension
1403  PCB_DIM_LEADER* leaderDim = new PCB_DIM_LEADER( m_board );
1404  m_board->Add( leaderDim, ADD_MODE::APPEND );
1405 
1406  applyDimensionSettings( csDim, leaderDim );
1407  leaderDim->SetStart( getKiCadPoint( csDim.Line.End ) );
1408 
1409  /*
1410  * In CADSTAR, the resulting shape orientation of the leader dimension depends on
1411  * on the positions of the #Start (S) and #End (E) points as shown below. In the
1412  * diagrams below, the leader angle (angRad) is represented by HEV
1413  *
1414  * Orientation 1: (orientX = -1, | Orientation 2: (orientX = 1,
1415  * orientY = 1) | orientY = 1)
1416  * |
1417  * --------V | V----------
1418  * \ | /
1419  * \ | /
1420  * H _E/ | \E_ H
1421  * |
1422  * S | S
1423  * |
1424  *
1425  * Orientation 3: (orientX = -1, | Orientation 4: (orientX = 1,
1426  * orientY = -1) | orientY = -1)
1427  * |
1428  * S | S
1429  * _ | _
1430  * H E\ | /E H
1431  * / | \
1432  * / | \
1433  * ----------V | V-----------
1434  * |
1435  *
1436  * Corner cases:
1437  *
1438  * It is not possible to generate a leader object with start and end point being
1439  * identical. Assume Orientation 2 if start and end points are identical.
1440  *
1441  * If start and end points are aligned vertically (i.e. S.x == E.x):
1442  * - If E.y > S.y - Orientation 2
1443  * - If E.y < S.y - Orientation 4
1444  *
1445  * If start and end points are aligned horitontally (i.e. S.y == E.y):
1446  * - If E.x > S.x - Orientation 2
1447  * - If E.x < S.x - Orientation 1
1448  */
1449  double angRad = DEG2RAD( getAngleDegrees( csDim.Line.LeaderAngle ) );
1450 
1451  double orientX = 1;
1452  double orientY = 1;
1453 
1454  if( csDim.Line.End.x >= csDim.Line.Start.x )
1455  {
1456  if( csDim.Line.End.y >= csDim.Line.Start.y )
1457  {
1458  //Orientation 2
1459  orientX = 1;
1460  orientY = 1;
1461  }
1462  else
1463  {
1464  //Orientation 4
1465  orientX = 1;
1466  orientY = -1;
1467  }
1468  }
1469  else
1470  {
1471  if( csDim.Line.End.y >= csDim.Line.Start.y )
1472  {
1473  //Orientation 1
1474  orientX = -1;
1475  orientY = 1;
1476  }
1477  else
1478  {
1479  //Orientation 3
1480  orientX = -1;
1481  orientY = -1;
1482  }
1483  }
1484 
1485  wxPoint endOffset( csDim.Line.LeaderLineLength * cos( angRad ) * orientX,
1486  csDim.Line.LeaderLineLength * sin( angRad ) * orientY );
1487 
1488  wxPoint endPoint = csDim.Line.End + endOffset;
1489  wxPoint txtPoint( endPoint.x + ( csDim.Line.LeaderLineExtensionLength * orientX ),
1490  endPoint.y );
1491 
1492  leaderDim->SetEnd( getKiCadPoint( endPoint ) );
1493  leaderDim->Text().SetTextPos( getKiCadPoint( txtPoint ) );
1494  leaderDim->SetText( ParseTextFields( csDim.Text.Text, &m_context ) );
1495  leaderDim->SetPrefix( wxEmptyString );
1496  leaderDim->SetSuffix( wxEmptyString );
1498 
1499  if( orientX == 1 )
1501  else
1502  leaderDim->Text().SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1503 
1504  leaderDim->SetExtensionOffset( 0 );
1505  }
1506  break;
1507 
1508  case DIMENSION::TYPE::ANGLEDIM:
1509  //TODO: update import when KiCad supports angular dimensions
1510  wxLogError( _( "Dimension %s is an angular dimension which has no KiCad equivalent. "
1511  "The object was not imported." ),
1512  csDim.ID );
1513  break;
1514  }
1515  }
1516 }
1517 
1518 
1520 {
1521  for( std::pair<AREA_ID, AREA> areaPair : Layout.Areas )
1522  {
1523  AREA& area = areaPair.second;
1524 
1525  if( area.NoVias || area.NoTracks || area.Keepout || area.Routing )
1526  {
1528  m_board );
1529 
1530  m_board->Add( zone, ADD_MODE::APPEND );
1531 
1532  if( isLayerSet( area.LayerID ) )
1533  zone->SetLayerSet( getKiCadLayerSet( area.LayerID ) );
1534  else
1535  zone->SetLayer( getKiCadLayer( area.LayerID ) );
1536 
1537  zone->SetIsRuleArea( true ); //import all CADSTAR areas as Keepout zones
1538  zone->SetDoNotAllowPads( false ); //no CADSTAR equivalent
1539  zone->SetZoneName( area.Name );
1540 
1541  zone->SetDoNotAllowFootprints( area.Keepout );
1542 
1543  zone->SetDoNotAllowTracks( area.NoTracks );
1544  zone->SetDoNotAllowCopperPour( area.NoTracks );
1545 
1546  zone->SetDoNotAllowVias( area.NoVias );
1547 
1548  if( area.Placement )
1549  {
1550  wxLogWarning( wxString::Format( _( "The CADSTAR area '%s' is marked as a placement "
1551  "area in CADSTAR. Placement areas are not "
1552  "supported in KiCad. Only the supported elements "
1553  "for the area were imported." ),
1554  area.Name ) );
1555  }
1556  }
1557  else
1558  {
1559  wxLogError( wxString::Format( _( "The CADSTAR area '%s' does not have a KiCad "
1560  "equivalent. Pure Placement areas are not supported." ),
1561  area.Name ) );
1562  }
1563 
1564  //todo Process area.AreaHeight when KiCad supports 3D design rules
1565  //TODO process attributes
1566  //TODO process addition to a group
1567  //TODO process "swaprule"
1568  //TODO process re-use block
1569  }
1570 }
1571 
1572 
1574 {
1575  for( std::pair<COMPONENT_ID, COMPONENT> compPair : Layout.Components )
1576  {
1577  COMPONENT& comp = compPair.second;
1578 
1579  if( !comp.VariantID.empty() && comp.VariantParentComponentID != comp.ID )
1580  continue; // Only load master Variant
1581 
1582  auto fpIter = m_libraryMap.find( comp.SymdefID );
1583 
1584  if( fpIter == m_libraryMap.end() )
1585  {
1586  THROW_IO_ERROR( wxString::Format( _( "Unable to find component '%s' in the library"
1587  "(Symdef ID: '%s')" ),
1588  comp.Name,
1589  comp.SymdefID ) );
1590  }
1591 
1592  FOOTPRINT* libFootprint = fpIter->second;
1593 
1594  // Use Duplicate() to ensure unique KIID for all objects
1595  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( libFootprint->Duplicate() );
1596 
1597  m_board->Add( footprint, ADD_MODE::APPEND );
1598 
1599  // First lets fix the pad names on the footprint.
1600  // CADSTAR defines the pad name in the PART definition and the SYMDEF (i.e. the PCB
1601  // footprint definition) uses a numerical sequence. COMP is the only object that has
1602  // visibility of both the SYMDEF and PART.
1603  if( Parts.PartDefinitions.find( comp.PartID ) != Parts.PartDefinitions.end() )
1604  {
1605  PART part = Parts.PartDefinitions.at( comp.PartID );
1606 
1607  // Only do this when the number of pins in the part definition equals the number of
1608  // pads in the footprint.
1609  if( part.Definition.Pins.size() == footprint->Pads().size() )
1610  {
1611  for( std::pair<PART_DEFINITION_PIN_ID, PART::DEFINITION::PIN> pinPair :
1612  part.Definition.Pins )
1613  {
1614  PART::DEFINITION::PIN pin = pinPair.second;
1615  wxString pinName = pin.Name;
1616 
1617  if( pinName.empty() )
1618  pinName = pin.Identifier;
1619 
1620  if( pinName.empty() )
1621  pinName = wxString::Format( wxT( "%ld" ), pin.ID );
1622 
1623  getPadReference( footprint, pin.ID )->SetNumber( pinName );
1624  }
1625  }
1626  }
1627 
1628  //Override pads with pad exceptions
1629  if( comp.PadExceptions.size() > 0 )
1630  {
1631  SYMDEF_PCB fpLibEntry = Library.ComponentDefinitions.at( comp.SymdefID );
1632 
1633  for( std::pair<PAD_ID, PADEXCEPTION> padPair : comp.PadExceptions )
1634  {
1635  PADEXCEPTION& padEx = padPair.second;
1636  COMPONENT_PAD csPad = fpLibEntry.ComponentPads.at( padPair.first );
1637 
1638  if( !padEx.PadCode.IsEmpty() )
1639  csPad.PadCodeID = padEx.PadCode;
1640 
1641  if( padEx.OverrideExits )
1642  csPad.Exits = padEx.Exits;
1643 
1644  if( padEx.OverrideOrientation )
1645  csPad.OrientAngle = padEx.OrientAngle;
1646 
1647  if( padEx.OverrideSide )
1648  csPad.Side = padEx.Side;
1649 
1650  // Find the pad in the footprint definition
1651  PAD* kiPad = getPadReference( footprint, padEx.ID );
1652  wxString padNumber = kiPad->GetNumber();
1653 
1654  if( kiPad )
1655  delete kiPad;
1656 
1657  kiPad = getKiCadPad( csPad, footprint );
1658  kiPad->SetNumber( padNumber );
1659 
1660  // Change the pointer in the footprint to the newly created pad
1661  getPadReference( footprint, padEx.ID ) = kiPad;
1662  }
1663  }
1664 
1665  //set to empty string to avoid duplication when loading attributes:
1666  footprint->SetValue( wxEmptyString );
1667 
1668  footprint->SetPosition( getKiCadPoint( comp.Origin ) );
1669  footprint->SetOrientation( getAngleTenthDegree( comp.OrientAngle ) );
1670  footprint->SetReference( comp.Name );
1671 
1672  if( comp.Mirror )
1673  {
1674  double mirroredAngle = - getAngleTenthDegree( comp.OrientAngle );
1675  NORMALIZE_ANGLE_180( mirroredAngle );
1676  footprint->SetOrientation( mirroredAngle );
1677  footprint->Flip( getKiCadPoint( comp.Origin ), true );
1678  }
1679 
1680  loadComponentAttributes( comp, footprint );
1681 
1682  if( !comp.PartID.IsEmpty() && comp.PartID != wxT( "NO_PART" ) )
1683  footprint->SetDescription( getPart( comp.PartID ).Definition.Name );
1684 
1685  m_componentMap.insert( { comp.ID, footprint } );
1686  }
1687 }
1688 
1689 
1691 {
1692  //No KiCad equivalent. Loaded as graphic and text elements instead
1693 
1694  for( std::pair<DOCUMENTATION_SYMBOL_ID, DOCUMENTATION_SYMBOL> docPair :
1696  {
1697  DOCUMENTATION_SYMBOL& docSymInstance = docPair.second;
1698 
1699 
1700  auto docSymIter = Library.ComponentDefinitions.find( docSymInstance.SymdefID );
1701 
1702  if( docSymIter == Library.ComponentDefinitions.end() )
1703  {
1704  THROW_IO_ERROR( wxString::Format( _( "Unable to find documentation symbol in the "
1705  "library (Symdef ID: '%s')" ),
1706  docSymInstance.SymdefID ) );
1707  }
1708 
1709  SYMDEF_PCB& docSymDefinition = ( *docSymIter ).second;
1710  wxPoint moveVector =
1711  getKiCadPoint( docSymInstance.Origin ) - getKiCadPoint( docSymDefinition.Origin );
1712  double rotationAngle = getAngleTenthDegree( docSymInstance.OrientAngle );
1713  double scalingFactor = (double) docSymInstance.ScaleRatioNumerator
1714  / (double) docSymInstance.ScaleRatioDenominator;
1715  wxPoint centreOfTransform = getKiCadPoint( docSymDefinition.Origin );
1716  bool mirrorInvert = docSymInstance.Mirror;
1717 
1718  //create a group to store the items in
1719  wxString groupName = docSymDefinition.ReferenceName;
1720 
1721  if( !docSymDefinition.Alternate.IsEmpty() )
1722  groupName += wxT( " (" ) + docSymDefinition.Alternate + wxT( ")" );
1723 
1724  GROUP_ID groupID = createUniqueGroupID( groupName );
1725 
1726  LSEQ layers = getKiCadLayerSet( docSymInstance.LayerID ).Seq();
1727 
1728  for( PCB_LAYER_ID layer : layers )
1729  {
1730  for( std::pair<FIGURE_ID, FIGURE> figPair : docSymDefinition.Figures )
1731  {
1732  FIGURE fig = figPair.second;
1733  drawCadstarShape( fig.Shape, layer, getLineThickness( fig.LineCodeID ),
1734  wxString::Format( "DOCUMENTATION SYMBOL %s, FIGURE %s",
1735  docSymDefinition.ReferenceName, fig.ID ),
1736  m_board, groupID, moveVector, rotationAngle, scalingFactor,
1737  centreOfTransform, mirrorInvert );
1738  }
1739  }
1740 
1741  for( std::pair<TEXT_ID, TEXT> textPair : docSymDefinition.Texts )
1742  {
1743  TEXT txt = textPair.second;
1744  drawCadstarText( txt, m_board, groupID, docSymInstance.LayerID, moveVector,
1745  rotationAngle, scalingFactor, centreOfTransform, mirrorInvert );
1746  }
1747  }
1748 }
1749 
1750 
1752 {
1753  for( std::pair<TEMPLATE_ID, TEMPLATE> tempPair : Layout.Templates )
1754  {
1755  TEMPLATE& csTemplate = tempPair.second;
1756 
1757  int zonelinethickness = 0; // The line thickness in CADSTAR is only for display purposes but
1758  // does not affect the end copper result.
1759  ZONE* zone = getZoneFromCadstarShape( csTemplate.Shape, zonelinethickness, m_board );
1760 
1761  m_board->Add( zone, ADD_MODE::APPEND );
1762 
1763  zone->SetZoneName( csTemplate.Name );
1764  zone->SetLayer( getKiCadLayer( csTemplate.LayerID ) );
1765  zone->SetPriority( 1 ); // initially 1, we will increase in calculateZonePriorities
1766 
1767  if( !( csTemplate.NetID.IsEmpty() || csTemplate.NetID == wxT( "NONE" ) ) )
1768  zone->SetNet( getKiCadNet( csTemplate.NetID ) );
1769 
1770  if( csTemplate.Pouring.AllowInNoRouting )
1771  {
1772  wxLogWarning( wxString::Format(
1773  _( "The CADSTAR template '%s' has the setting 'Allow in No Routing Areas' "
1774  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1775  csTemplate.Name ) );
1776  }
1777 
1778  if( csTemplate.Pouring.BoxIsolatedPins )
1779  {
1780  wxLogWarning( wxString::Format(
1781  _( "The CADSTAR template '%s' has the setting 'Box Isolated Pins' "
1782  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1783  csTemplate.Name ) );
1784  }
1785 
1786  if( csTemplate.Pouring.AutomaticRepour )
1787  {
1788  wxLogWarning( wxString::Format(
1789  _( "The CADSTAR template '%s' has the setting 'Automatic Repour' "
1790  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1791  csTemplate.Name ) );
1792  }
1793 
1794  // Sliver width has different behaviour to KiCad Zone's minimum thickness
1795  // In Cadstar 'Sliver width' has to be greater than the Copper thickness, whereas in
1796  // Kicad it is the opposite.
1797  if( csTemplate.Pouring.SliverWidth != 0 )
1798  {
1799  wxLogWarning( wxString::Format(
1800  _( "The CADSTAR template '%s' has a non-zero value defined for the "
1801  "'Sliver Width' setting. There is no KiCad equivalent for "
1802  "this, so this setting was ignored." ),
1803  csTemplate.Name ) );
1804  }
1805 
1806 
1807  if( csTemplate.Pouring.MinIsolatedCopper != csTemplate.Pouring.MinDisjointCopper )
1808  {
1809  wxLogWarning( wxString::Format(
1810  _( "The CADSTAR template '%s' has different settings for 'Retain Poured Copper "
1811  "- Disjoint' and 'Retain Poured Copper - Isolated'. KiCad does not "
1812  "distinguish between these two settings. The setting for disjoint copper "
1813  "has been applied as the minimum island area of the KiCad Zone." ),
1814  csTemplate.Name ) );
1815  }
1816 
1817  long long minIslandArea = -1;
1818 
1819  if( csTemplate.Pouring.MinDisjointCopper != UNDEFINED_VALUE )
1820  {
1821  minIslandArea = (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper )
1822  * (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper );
1823 
1825  }
1826  else
1827  {
1829  }
1830 
1831  zone->SetMinIslandArea( minIslandArea );
1832 
1833  // In cadstar zone clearance is in addition to the design rule "copper to copper"
1834  int clearance = getKiCadLength( csTemplate.Pouring.AdditionalIsolation );
1835 
1836  if( Assignments.Codedefs.SpacingCodes.find( wxT( "C_C" ) )
1837  != Assignments.Codedefs.SpacingCodes.end() )
1838  {
1839  int copperToCopper = Assignments.Codedefs.SpacingCodes.at( wxT( "C_C" ) ).Spacing;
1840  clearance += getKiCadLength( copperToCopper );
1841  }
1842  else
1843  {
1844  clearance += m_board->GetDesignSettings().m_MinClearance;
1845  }
1846 
1847  zone->SetLocalClearance( clearance );
1848 
1849  COPPERCODE pouringCopperCode = getCopperCode( csTemplate.Pouring.CopperCodeID );
1850  int minThickness = getKiCadLength( pouringCopperCode.CopperWidth );
1851  zone->SetMinThickness( minThickness );
1852 
1853  if( csTemplate.Pouring.FillType == TEMPLATE::POURING::COPPER_FILL_TYPE::HATCHED )
1854  {
1856  zone->SetHatchGap( getKiCadHatchCodeGap( csTemplate.Pouring.HatchCodeID ) );
1859  }
1860  else
1861  {
1863  }
1864 
1865  if( csTemplate.Pouring.ThermalReliefOnPads != csTemplate.Pouring.ThermalReliefOnVias
1866  || csTemplate.Pouring.ThermalReliefPadsAngle
1867  != csTemplate.Pouring.ThermalReliefViasAngle )
1868  {
1869  wxLogWarning( wxString::Format(
1870  _( "The CADSTAR template '%s' has different settings for thermal relief "
1871  "in pads and vias. KiCad only supports one single setting for both. The "
1872  "setting for pads has been applied." ),
1873  csTemplate.Name ) );
1874  }
1875 
1876  COPPERCODE reliefCopperCode = getCopperCode( csTemplate.Pouring.ReliefCopperCodeID );
1877  int spokeWidth = getKiCadLength( reliefCopperCode.CopperWidth );
1878  int reliefWidth = getKiCadLength( csTemplate.Pouring.ClearanceWidth );
1879 
1880  // Cadstar supports having a spoke width thinner than the minimum thickness of the zone, but
1881  // this is not permitted in KiCad. We load it as solid fill instead.
1882  if( csTemplate.Pouring.ThermalReliefOnPads && reliefWidth > 0 && spokeWidth > minThickness )
1883  {
1884  zone->SetThermalReliefGap( reliefWidth );
1885  zone->SetThermalReliefSpokeWidth( spokeWidth );
1887  }
1888  else
1889  {
1890  if( csTemplate.Pouring.ThermalReliefOnPads && spokeWidth > minThickness )
1891  {
1892  wxLogWarning( wxString::Format(
1893  _( "The CADSTAR template '%s' has thermal reliefs in the original design "
1894  "but there is no KiCad equivalent to the original CADSTAR settings. "
1895  "Solid fill has been applied instead. When the template is re-filled "
1896  "the thermal reliefs will be removed." ),
1897  csTemplate.Name ) );
1898  }
1899 
1901  }
1902 
1903  m_zonesMap.insert( { csTemplate.ID, zone } );
1904  }
1905 
1906  //Now create power plane layers:
1907  for( LAYER_ID layer : m_powerPlaneLayers )
1908  {
1909  wxASSERT(
1910  Assignments.Layerdefs.Layers.find( layer ) != Assignments.Layerdefs.Layers.end() );
1911 
1912  //The net name will equal the layer name
1913  wxString powerPlaneLayerName = Assignments.Layerdefs.Layers.at( layer ).Name;
1914  NET_ID netid = wxEmptyString;
1915 
1916  for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
1917  {
1918  NET_PCB net = netPair.second;
1919 
1920  if( net.Name == powerPlaneLayerName )
1921  {
1922  netid = net.ID;
1923  break;
1924  }
1925  }
1926 
1927  if( netid.IsEmpty() )
1928  {
1929  wxLogError( _( "The CADSTAR layer '%s' is defined as a power plane layer. However no "
1930  "net with such name exists. The layer has been loaded but no copper "
1931  "zone was created." ),
1932  powerPlaneLayerName );
1933  }
1934  else
1935  {
1936  for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
1937  {
1938  //create a zone in each board shape
1940  CADSTAR_BOARD& board = boardPair.second;
1941  int defaultLineThicknesss = bds.GetLineThickness( PCB_LAYER_ID::Edge_Cuts );
1942  ZONE* zone = getZoneFromCadstarShape( board.Shape, defaultLineThicknesss, m_board );
1943 
1944  m_board->Add( zone, ADD_MODE::APPEND );
1945 
1946  zone->SetZoneName( powerPlaneLayerName );
1947  zone->SetLayer( getKiCadLayer( layer ) );
1950  zone->SetMinIslandArea( -1 );
1951  zone->SetPriority( 0 ); // Priority always 0 (lowest priority) for implied power planes.
1952  zone->SetNet( getKiCadNet( netid ) );
1953  }
1954  }
1955  }
1956 }
1957 
1958 
1960 {
1961  for( std::pair<COPPER_ID, COPPER> copPair : Layout.Coppers )
1962  {
1963  COPPER& csCopper = copPair.second;
1964 
1965  checkPoint();
1966 
1967  if( !csCopper.PouredTemplateID.IsEmpty() )
1968  {
1969  ZONE* pouredZone = m_zonesMap.at( csCopper.PouredTemplateID );
1970  SHAPE_POLY_SET rawPolys;
1971 
1972  int copperWidth = getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth );
1973 
1974  if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE )
1975  {
1976  // This is usually for themal reliefs. They are lines of copper with a thickness.
1977  // We convert them to an oval in most cases, but handle also the possibility of
1978  // encountering arcs in here.
1979 
1980  std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( csCopper.Shape.Vertices );
1981 
1982  for( PCB_SHAPE* shape : outlineShapes )
1983  {
1984  SHAPE_POLY_SET poly;
1985 
1986  if( shape->GetShape() == SHAPE_T::ARC )
1987  {
1988  TransformArcToPolygon( poly, shape->GetArcStart(), shape->GetArcMid(),
1989  shape->GetArcEnd(), copperWidth, ARC_HIGH_DEF,
1991  }
1992  else
1993  {
1994  TransformOvalToPolygon( poly, shape->GetStart(), shape->GetEnd(),
1995  copperWidth, ARC_HIGH_DEF,
1997  }
1998 
2000  }
2001 
2002  }
2003  else
2004  {
2005  rawPolys = getPolySetFromCadstarShape( csCopper.Shape, -1 );
2006  rawPolys.Inflate( copperWidth / 2, 32 );
2007  }
2008 
2009  if( pouredZone->HasFilledPolysForLayer( getKiCadLayer( csCopper.LayerID ) ) )
2010  {
2011  rawPolys.BooleanAdd( pouredZone->RawPolysList( getKiCadLayer( csCopper.LayerID )),
2013  }
2014 
2015  SHAPE_POLY_SET finalPolys = rawPolys;
2017 
2018  pouredZone->SetFillVersion( 6 );
2019  pouredZone->SetRawPolysList( getKiCadLayer( csCopper.LayerID ), rawPolys );
2020  pouredZone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), finalPolys );
2021  pouredZone->SetIsFilled( true );
2022  pouredZone->SetNeedRefill( false );
2023  continue;
2024  }
2025 
2026  // For now we are going to load coppers to a KiCad zone however this isn't perfect
2027  //TODO: Load onto a graphical polygon with a net (when KiCad has this feature)
2028 
2029  if( !m_doneCopperWarning )
2030  {
2031  wxLogWarning(
2032  _( "The CADSTAR design contains COPPER elements, which have no direct KiCad "
2033  "equivalent. These have been imported as a KiCad Zone if solid or hatch "
2034  "filled, or as a KiCad Track if the shape was an unfilled outline (open or "
2035  "closed)." ) );
2036  m_doneCopperWarning = true;
2037  }
2038 
2039 
2040  if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE
2041  || csCopper.Shape.Type == SHAPE_TYPE::OUTLINE )
2042  {
2043  std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( csCopper.Shape.Vertices );
2044 
2045  std::vector<PCB_TRACK*> outlineTracks = makeTracksFromShapes( outlineShapes, m_board,
2046  getKiCadNet( csCopper.NetRef.NetID ),
2047  getKiCadLayer( csCopper.LayerID ),
2049 
2050  //cleanup
2051  for( PCB_SHAPE* shape : outlineShapes )
2052  delete shape;
2053 
2054  for( CUTOUT cutout : csCopper.Shape.Cutouts )
2055  {
2056  std::vector<PCB_SHAPE*> cutoutShapes = getShapesFromVertices( cutout.Vertices );
2057 
2058  std::vector<PCB_TRACK*> cutoutTracks = makeTracksFromShapes( cutoutShapes, m_board,
2059  getKiCadNet( csCopper.NetRef.NetID ),
2060  getKiCadLayer( csCopper.LayerID ),
2062 
2063  //cleanup
2064  for( PCB_SHAPE* shape : cutoutShapes )
2065  delete shape;
2066  }
2067  }
2068  else
2069  {
2070  ZONE* zone = getZoneFromCadstarShape( csCopper.Shape,
2072  m_board );
2073 
2074  m_board->Add( zone, ADD_MODE::APPEND );
2075 
2076  zone->SetZoneName( csCopper.ID );
2077  zone->SetLayer( getKiCadLayer( csCopper.LayerID ) );
2079 
2080  if( csCopper.Shape.Type == SHAPE_TYPE::HATCHED )
2081  {
2083  zone->SetHatchGap( getKiCadHatchCodeGap( csCopper.Shape.HatchCodeID ) );
2086  }
2087  else
2088  {
2090  }
2091 
2094  zone->SetNet( getKiCadNet( csCopper.NetRef.NetID ) );
2095  zone->SetPriority( m_zonesMap.size() + 1 ); // Highest priority (always fill first)
2096  zone->SetRawPolysList( getKiCadLayer( csCopper.LayerID ), *zone->Outline() );
2097 
2098  SHAPE_POLY_SET fillePolys( *zone->Outline() );
2099  fillePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
2100 
2101  zone->SetFillVersion( 6 );
2102  zone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fillePolys );
2103  }
2104  }
2105 }
2106 
2107 
2109 {
2110  for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
2111  {
2112  NET_PCB net = netPair.second;
2113  wxString netnameForErrorReporting = net.Name;
2114 
2115  std::map<NETELEMENT_ID, long> netelementSizes;
2116 
2117  if( netnameForErrorReporting.IsEmpty() )
2118  netnameForErrorReporting = wxString::Format( "$%ld", net.SignalNum );
2119 
2120  for( std::pair<NETELEMENT_ID, NET_PCB::VIA> viaPair : net.Vias )
2121  {
2122  NET_PCB::VIA via = viaPair.second;
2123 
2124  // viasize is used for calculating route offset (as done in CADSTAR post processor)
2125  int viaSize = loadNetVia( net.ID, via );
2126  netelementSizes.insert( { viaPair.first, viaSize } );
2127  }
2128 
2129  for( std::pair<NETELEMENT_ID, NET_PCB::PIN> pinPair : net.Pins )
2130  {
2131  NET_PCB::PIN pin = pinPair.second;
2132  FOOTPRINT* footprint = getFootprintFromCadstarID( pin.ComponentID );
2133 
2134  if( footprint == nullptr )
2135  {
2136  wxLogWarning( wxString::Format(
2137  _( "The net '%s' references component ID '%s' which does not exist. "
2138  "This has been ignored." ),
2139  netnameForErrorReporting, pin.ComponentID ) );
2140  }
2141  else if( ( pin.PadID - (long) 1 ) > footprint->Pads().size() )
2142  {
2143  wxLogWarning( wxString::Format( _( "The net '%s' references non-existent pad index"
2144  " '%d' in component '%s'. This has been "
2145  "ignored." ),
2146  netnameForErrorReporting,
2147  pin.PadID,
2148  footprint->GetReference() ) );
2149  }
2150  else
2151  {
2152  // The below works because we have added the pads in the correct order to the
2153  // footprint and the PAD_ID in Cadstar is a sequential, numerical ID
2154  PAD* pad = getPadReference( footprint, pin.PadID );
2155  pad->SetNet( getKiCadNet( net.ID ) );
2156 
2157  // also set the net to any copper pads (i.e. copper elements that we have imported
2158  // as pads instead:
2159  SYMDEF_ID symdefid = Layout.Components.at( pin.ComponentID ).SymdefID;
2160 
2161  if( m_librarycopperpads.find( symdefid ) != m_librarycopperpads.end() )
2162  {
2163  ASSOCIATED_COPPER_PADS assocPads = m_librarycopperpads.at( symdefid );
2164 
2165  if( assocPads.find( pin.PadID ) != assocPads.end() )
2166  {
2167  for( PAD_ID copperPadID : assocPads.at( pin.PadID ) )
2168  {
2169  PAD* copperpad = getPadReference( footprint, copperPadID );
2170  copperpad->SetNet( getKiCadNet( net.ID ) );
2171  }
2172  }
2173  }
2174 
2175  // padsize is used for calculating route offset (as done in CADSTAR post processor)
2176  int padsize = std::min( pad->GetSizeX(), pad->GetSizeY() );
2177  netelementSizes.insert( { pinPair.first, padsize } );
2178  }
2179  }
2180 
2181  // For junction points we need to find out the biggest size of the other routes connecting
2182  // at the junction in order to correctly apply the same "route offset" operation that the
2183  // CADSTAR post processor applies when generating Manufacturing output
2184  auto getJunctionSize =
2185  [&]( NETELEMENT_ID aJptNetElemId, const NET_PCB::CONNECTION_PCB& aConnectionToIgnore ) -> int
2186  {
2187  int jptsize = 0;
2188 
2189  for( NET_PCB::CONNECTION_PCB connection : net.Connections )
2190  {
2191  if( connection.Route.RouteVertices.size() == 0 )
2192  continue;
2193 
2194  if( connection.StartNode == aConnectionToIgnore.StartNode
2195  && connection.EndNode == aConnectionToIgnore.EndNode )
2196  continue;
2197 
2198  if( connection.StartNode == aJptNetElemId )
2199  {
2200  int s = getKiCadLength( connection.Route.RouteVertices.front().RouteWidth );
2201  jptsize = std::max( jptsize, s );
2202  }
2203  else if( connection.EndNode == aJptNetElemId )
2204  {
2205  int s = getKiCadLength( connection.Route.RouteVertices.back().RouteWidth );
2206  jptsize = std::max( jptsize, s );
2207  }
2208  }
2209 
2210  return jptsize;
2211  };
2212 
2213  for( NET_PCB::CONNECTION_PCB connection : net.Connections )
2214  {
2215  int startSize = std::numeric_limits<int>::max();
2216  int endSize = std::numeric_limits<int>::max();
2217 
2218  if( netelementSizes.find( connection.StartNode ) != netelementSizes.end() )
2219  startSize = netelementSizes.at( connection.StartNode );
2220  else if( net.Junctions.find( connection.StartNode ) != net.Junctions.end() )
2221  startSize = getJunctionSize( connection.StartNode, connection );
2222 
2223  if( netelementSizes.find( connection.EndNode ) != netelementSizes.end() )
2224  endSize = netelementSizes.at( connection.EndNode );
2225  else if( net.Junctions.find( connection.EndNode ) != net.Junctions.end() )
2226  endSize = getJunctionSize( connection.EndNode, connection );
2227 
2228  startSize /= KiCadUnitMultiplier;
2229  endSize /= KiCadUnitMultiplier;
2230 
2231  if( !connection.Unrouted )
2232  loadNetTracks( net.ID, connection.Route, startSize, endSize );
2233  }
2234  }
2235 }
2236 
2237 
2239 {
2240  auto findAndReplaceTextField =
2241  [&]( TEXT_FIELD_NAME aField, wxString aValue )
2242  {
2243  if( m_context.TextFieldToValuesMap.find( aField ) !=
2245  {
2246  if( m_context.TextFieldToValuesMap.at( aField ) != aValue )
2247  {
2248  m_context.TextFieldToValuesMap.at( aField ) = aValue;
2249  m_context.InconsistentTextFields.insert( aField );
2250  return false;
2251  }
2252  }
2253  else
2254  {
2255  m_context.TextFieldToValuesMap.insert( { aField, aValue } );
2256  }
2257 
2258  return true;
2259  };
2260 
2261  if( m_project )
2262  {
2263  std::map<wxString, wxString>& txtVars = m_project->GetTextVars();
2264 
2265  // Most of the design text fields can be derived from other elements
2266  if( Layout.VariantHierarchy.Variants.size() > 0 )
2267  {
2268  VARIANT loadedVar = Layout.VariantHierarchy.Variants.begin()->second;
2269 
2270  findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_NAME, loadedVar.Name );
2271  findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_DESCRIPTION, loadedVar.Description );
2272  }
2273 
2274  findAndReplaceTextField( TEXT_FIELD_NAME::DESIGN_TITLE, Header.JobTitle );
2275 
2276  for( std::pair<TEXT_FIELD_NAME, wxString> txtvalue : m_context.TextFieldToValuesMap )
2277  {
2278  wxString varName = CADSTAR_TO_KICAD_FIELDS.at( txtvalue.first );
2279  wxString varValue = txtvalue.second;
2280 
2281  txtVars.insert( { varName, varValue } );
2282  }
2283 
2284  for( std::pair<wxString, wxString> txtvalue : m_context.FilenamesToTextMap )
2285  {
2286  wxString varName = txtvalue.first;
2287  wxString varValue = txtvalue.second;
2288 
2289  txtVars.insert( { varName, varValue } );
2290  }
2291  }
2292  else
2293  {
2294  wxLogError( _( "Text Variables could not be set as there is no project loaded." ) );
2295  }
2296 }
2297 
2298 
2300  FOOTPRINT* aFootprint )
2301 {
2302  for( std::pair<ATTRIBUTE_ID, ATTRIBUTE_VALUE> attrPair : aComponent.AttributeValues )
2303  {
2304  ATTRIBUTE_VALUE& attrval = attrPair.second;
2305 
2306  if( attrval.HasLocation ) //only import attributes with location. Ignore the rest
2307  {
2308  addAttribute( attrval.AttributeLocation, attrval.AttributeID, aFootprint,
2309  attrval.Value );
2310  }
2311  }
2312 
2313  for( std::pair<ATTRIBUTE_ID, TEXT_LOCATION> textlocPair : aComponent.TextLocations )
2314  {
2315  TEXT_LOCATION& textloc = textlocPair.second;
2316  wxString attrval;
2317 
2318  if( textloc.AttributeID == COMPONENT_NAME_ATTRID )
2319  {
2320  attrval = wxEmptyString; // Designator is loaded separately
2321  }
2322  else if( textloc.AttributeID == COMPONENT_NAME_2_ATTRID )
2323  {
2324  attrval = wxT( "${REFERENCE}" );
2325  }
2326  else if( textloc.AttributeID == PART_NAME_ATTRID )
2327  {
2328  attrval = getPart( aComponent.PartID ).Name;
2329  }
2330  else
2331  attrval = getAttributeValue( textloc.AttributeID, aComponent.AttributeValues );
2332 
2333  addAttribute( textloc, textloc.AttributeID, aFootprint, attrval );
2334  }
2335 }
2336 
2337 
2339  const NET_PCB::ROUTE& aCadstarRoute,
2340  long aStartWidth, long aEndWidth )
2341 {
2342  if( aCadstarRoute.RouteVertices.size() == 0 )
2343  return;
2344 
2345  std::vector<PCB_SHAPE*> shapes;
2346  std::vector<NET_PCB::ROUTE_VERTEX> routeVertices = aCadstarRoute.RouteVertices;
2347 
2348  // Add thin route at front so that route offsetting works as expected
2349  if( aStartWidth < routeVertices.front().RouteWidth )
2350  {
2351  NET_PCB::ROUTE_VERTEX newFrontVertex = aCadstarRoute.RouteVertices.front();
2352  newFrontVertex.RouteWidth = aStartWidth;
2353  newFrontVertex.Vertex.End = aCadstarRoute.StartPoint;
2354  routeVertices.insert( routeVertices.begin(), newFrontVertex );
2355  }
2356 
2357  // Add thin route at the back if required
2358  if( aEndWidth < routeVertices.back().RouteWidth )
2359  {
2360  NET_PCB::ROUTE_VERTEX newBackVertex = aCadstarRoute.RouteVertices.back();
2361  newBackVertex.RouteWidth = aEndWidth;
2362  routeVertices.push_back( newBackVertex );
2363  }
2364 
2365  POINT prevEnd = aCadstarRoute.StartPoint;
2366 
2367  for( const NET_PCB::ROUTE_VERTEX& v : routeVertices )
2368  {
2369  PCB_SHAPE* shape = getShapeFromVertex( prevEnd, v.Vertex );
2370  shape->SetLayer( getKiCadLayer( aCadstarRoute.LayerID ) );
2371  shape->SetWidth( getKiCadLength( v.RouteWidth ) );
2372  shape->SetLocked( v.Fixed );
2373  shapes.push_back( shape );
2374  prevEnd = v.Vertex.End;
2375  }
2376 
2377  NETINFO_ITEM* net = getKiCadNet( aCadstarNetID );
2378  std::vector<PCB_TRACK*> tracks = makeTracksFromShapes( shapes, m_board, net );
2379 
2380  //cleanup
2381  for( PCB_SHAPE* shape : shapes )
2382  delete shape;
2383 }
2384 
2385 
2387  const NET_ID& aCadstarNetID, const NET_PCB::VIA& aCadstarVia )
2388 {
2389  PCB_VIA* via = new PCB_VIA( m_board );
2391 
2392  VIACODE csViaCode = getViaCode( aCadstarVia.ViaCodeID );
2393  LAYERPAIR csLayerPair = getLayerPair( aCadstarVia.LayerPairID );
2394 
2395  via->SetPosition( getKiCadPoint( aCadstarVia.Location ) );
2396  via->SetDrill( getKiCadLength( csViaCode.DrillDiameter ) );
2397  via->SetLocked( aCadstarVia.Fixed );
2398 
2399  if( csViaCode.Shape.ShapeType != PAD_SHAPE_TYPE::CIRCLE )
2400  {
2401  wxLogError( _( "The CADSTAR via code '%s' has different shape from a circle defined. "
2402  "KiCad only supports circular vias so this via type has been changed to "
2403  "be a via with circular shape of %.2f mm diameter." ),
2404  csViaCode.Name,
2405  (double) ( (double) getKiCadLength( csViaCode.Shape.Size ) / 1E6 ) );
2406  }
2407 
2408  via->SetWidth( getKiCadLength( csViaCode.Shape.Size ) );
2409 
2410  bool start_layer_outside =
2411  csLayerPair.PhysicalLayerStart == 1
2413  bool end_layer_outside =
2414  csLayerPair.PhysicalLayerEnd == 1
2416 
2417  if( start_layer_outside && end_layer_outside )
2418  {
2419  via->SetViaType( VIATYPE::THROUGH );
2420  }
2421  else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
2422  {
2423  via->SetViaType( VIATYPE::BLIND_BURIED );
2424  }
2425  else
2426  {
2427  via->SetViaType( VIATYPE::MICROVIA );
2428  }
2429 
2430  via->SetLayerPair( getKiCadCopperLayerID( csLayerPair.PhysicalLayerStart ),
2431  getKiCadCopperLayerID( csLayerPair.PhysicalLayerEnd ) );
2432  via->SetNet( getKiCadNet( aCadstarNetID ) );
2434 
2435  return via->GetWidth();
2436 }
2437 
2438 
2440  BOARD_ITEM_CONTAINER* aContainer, const GROUP_ID& aCadstarGroupID,
2441  const LAYER_ID& aCadstarLayerOverride, const wxPoint& aMoveVector,
2442  const double& aRotationAngle, const double& aScalingFactor, const wxPoint& aTransformCentre,
2443  const bool& aMirrorInvert )
2444 {
2445  PCB_TEXT* txt = new PCB_TEXT( aContainer );
2446  aContainer->Add( txt );
2447  txt->SetText( aCadstarText.Text );
2448 
2449  wxPoint rotatedTextPos = getKiCadPoint( aCadstarText.Position );
2450  RotatePoint( &rotatedTextPos, aTransformCentre, aRotationAngle );
2451  rotatedTextPos.x =
2452  KiROUND( (double) ( rotatedTextPos.x - aTransformCentre.x ) * aScalingFactor );
2453  rotatedTextPos.y =
2454  KiROUND( (double) ( rotatedTextPos.y - aTransformCentre.y ) * aScalingFactor );
2455  rotatedTextPos += aTransformCentre;
2456  txt->SetTextPos( rotatedTextPos );
2457  txt->SetPosition( rotatedTextPos );
2458 
2459  txt->SetTextAngle( getAngleTenthDegree( aCadstarText.OrientAngle ) + aRotationAngle );
2460 
2461  if( aCadstarText.Mirror != aMirrorInvert ) // If mirroring, invert angle to match CADSTAR
2462  txt->SetTextAngle( -txt->GetTextAngle() );
2463 
2464  txt->SetMirrored( aCadstarText.Mirror );
2465 
2466  TEXTCODE tc = getTextCode( aCadstarText.TextCodeID );
2467 
2469 
2470  wxSize unscaledTextSize;
2471  unscaledTextSize.x = getKiCadLength( tc.Width );
2472 
2473  // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
2474  // to work well for most fonts.
2475  if( unscaledTextSize.x == 0 )
2476  unscaledTextSize.x = getKiCadLength( tc.Height );
2477 
2478  unscaledTextSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
2479  txt->SetTextSize( unscaledTextSize );
2480 
2481  switch( aCadstarText.Alignment )
2482  {
2483  case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
2484  case ALIGNMENT::BOTTOMLEFT:
2487  break;
2488 
2492  break;
2493 
2497  break;
2498 
2499  case ALIGNMENT::CENTERLEFT:
2502  break;
2503 
2507  break;
2508 
2512  break;
2513 
2514  case ALIGNMENT::TOPLEFT:
2517  break;
2518 
2519  case ALIGNMENT::TOPCENTER:
2522  break;
2523 
2524  case ALIGNMENT::TOPRIGHT:
2527  break;
2528 
2529  default:
2530  wxFAIL_MSG( "Unknown Alignment - needs review!" );
2531  }
2532 
2533  if( aMirrorInvert )
2534  {
2535  txt->Flip( aTransformCentre, true );
2536  }
2537 
2538  //scale it after flipping:
2539  if( aScalingFactor != 1.0 )
2540  {
2541  wxSize scaledTextSize;
2542  scaledTextSize.x = KiROUND( (double) unscaledTextSize.x * aScalingFactor );
2543  scaledTextSize.y = KiROUND( (double) unscaledTextSize.y * aScalingFactor );
2544  txt->SetTextSize( scaledTextSize );
2545  txt->SetTextThickness(
2546  KiROUND( (double) getKiCadLength( tc.LineWidth ) * aScalingFactor ) );
2547  }
2548 
2549  txt->Move( aMoveVector );
2550 
2551  if( aCadstarText.Alignment == ALIGNMENT::NO_ALIGNMENT )
2553 
2554  LAYER_ID layersToDrawOn = aCadstarLayerOverride;
2555 
2556  if( layersToDrawOn.IsEmpty() )
2557  layersToDrawOn = aCadstarText.LayerID;
2558 
2559  if( isLayerSet( layersToDrawOn ) )
2560  {
2561  //Make a copy on each layer
2562 
2563  LSEQ layers = getKiCadLayerSet( layersToDrawOn ).Seq();
2564  PCB_TEXT* newtxt;
2565 
2566  for( PCB_LAYER_ID layer : layers )
2567  {
2568  txt->SetLayer( layer );
2569  newtxt = static_cast<PCB_TEXT*>( txt->Duplicate() );
2570  m_board->Add( newtxt, ADD_MODE::APPEND );
2571 
2572  if( !aCadstarGroupID.IsEmpty() )
2573  addToGroup( aCadstarGroupID, newtxt );
2574  }
2575 
2576  m_board->Remove( txt );
2577  delete txt;
2578  }
2579  else
2580  {
2581  txt->SetLayer( getKiCadLayer( layersToDrawOn ) );
2582 
2583  if( !aCadstarGroupID.IsEmpty() )
2584  addToGroup( aCadstarGroupID, txt );
2585  }
2586  //TODO Handle different font types when KiCad can support it.
2587 }
2588 
2589 
2591  const PCB_LAYER_ID& aKiCadLayer,
2592  const int& aLineThickness,
2593  const wxString& aShapeName,
2594  BOARD_ITEM_CONTAINER* aContainer,
2595  const GROUP_ID& aCadstarGroupID,
2596  const wxPoint& aMoveVector,
2597  const double& aRotationAngle,
2598  const double& aScalingFactor,
2599  const wxPoint& aTransformCentre,
2600  const bool& aMirrorInvert )
2601 {
2602  switch( aCadstarShape.Type )
2603  {
2604  case SHAPE_TYPE::OPENSHAPE:
2605  case SHAPE_TYPE::OUTLINE:
2607  drawCadstarVerticesAsShapes( aCadstarShape.Vertices, aKiCadLayer, aLineThickness,
2608  aContainer, aCadstarGroupID, aMoveVector, aRotationAngle,
2609  aScalingFactor, aTransformCentre, aMirrorInvert );
2610  drawCadstarCutoutsAsShapes( aCadstarShape.Cutouts, aKiCadLayer, aLineThickness,
2611  aContainer, aCadstarGroupID, aMoveVector, aRotationAngle,
2612  aScalingFactor, aTransformCentre, aMirrorInvert );
2613  break;
2614 
2615  case SHAPE_TYPE::HATCHED:
2617  wxLogWarning( wxString::Format(
2618  _( "The shape for '%s' is Hatch filled in CADSTAR, which has no KiCad equivalent. "
2619  "Using solid fill instead." ),
2620  aShapeName ) );
2621 
2622  case SHAPE_TYPE::SOLID:
2623  {
2624  PCB_SHAPE* shape;
2625 
2626  if( isFootprint( aContainer ) )
2627  {
2628  shape = new FP_SHAPE( (FOOTPRINT*) aContainer, SHAPE_T::POLY );
2629  }
2630  else
2631  {
2632  shape = new PCB_SHAPE( aContainer );
2633  shape->SetShape( SHAPE_T::POLY );
2634  }
2635 
2636  shape->SetFilled( true );
2637 
2639  aCadstarShape, -1, aContainer, aMoveVector, aRotationAngle, aScalingFactor,
2640  aTransformCentre, aMirrorInvert );
2641 
2642  shapePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
2643 
2644  shape->SetPolyShape( shapePolys );
2645  shape->SetWidth( aLineThickness );
2646  shape->SetLayer( aKiCadLayer );
2647  aContainer->Add( shape, ADD_MODE::APPEND );
2648 
2649  if( !aCadstarGroupID.IsEmpty() )
2650  addToGroup( aCadstarGroupID, shape );
2651  }
2652  break;
2653  }
2654 }
2655 
2656 
2657 void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarCutoutsAsShapes( const std::vector<CUTOUT>& aCutouts,
2658  const PCB_LAYER_ID& aKiCadLayer,
2659  const int& aLineThickness,
2660  BOARD_ITEM_CONTAINER* aContainer,
2661  const GROUP_ID& aCadstarGroupID,
2662  const wxPoint& aMoveVector,
2663  const double& aRotationAngle,
2664  const double& aScalingFactor,
2665  const wxPoint& aTransformCentre,
2666  const bool& aMirrorInvert )
2667 {
2668  for( CUTOUT cutout : aCutouts )
2669  {
2670  drawCadstarVerticesAsShapes( cutout.Vertices, aKiCadLayer, aLineThickness, aContainer,
2671  aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2672  aTransformCentre, aMirrorInvert );
2673  }
2674 }
2675 
2676 
2677 void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarVerticesAsShapes( const std::vector<VERTEX>& aCadstarVertices,
2678  const PCB_LAYER_ID& aKiCadLayer,
2679  const int& aLineThickness,
2680  BOARD_ITEM_CONTAINER* aContainer,
2681  const GROUP_ID& aCadstarGroupID,
2682  const wxPoint& aMoveVector,
2683  const double& aRotationAngle,
2684  const double& aScalingFactor,
2685  const wxPoint& aTransformCentre,
2686  const bool& aMirrorInvert )
2687 {
2688  std::vector<PCB_SHAPE*> shapes = getShapesFromVertices( aCadstarVertices, aContainer,
2689  aCadstarGroupID, aMoveVector,
2690  aRotationAngle, aScalingFactor,
2691  aTransformCentre, aMirrorInvert );
2692 
2693  for( PCB_SHAPE* shape : shapes )
2694  {
2695  shape->SetWidth( aLineThickness );
2696  shape->SetLayer( aKiCadLayer );
2697  shape->SetParent( aContainer );
2698  aContainer->Add( shape, ADD_MODE::APPEND );
2699  }
2700 }
2701 
2702 
2704  const std::vector<VERTEX>& aCadstarVertices,
2705  BOARD_ITEM_CONTAINER* aContainer,
2706  const GROUP_ID& aCadstarGroupID,
2707  const wxPoint& aMoveVector,
2708  const double& aRotationAngle,
2709  const double& aScalingFactor,
2710  const wxPoint& aTransformCentre,
2711  const bool& aMirrorInvert )
2712 {
2713  std::vector<PCB_SHAPE*> drawSegments;
2714 
2715  if( aCadstarVertices.size() < 2 )
2716  //need at least two points to draw a segment! (unlikely but possible to have only one)
2717  return drawSegments;
2718 
2719  const VERTEX* prev = &aCadstarVertices.at( 0 ); // first one should always be a point vertex
2720  const VERTEX* cur;
2721 
2722  for( size_t i = 1; i < aCadstarVertices.size(); i++ )
2723  {
2724  cur = &aCadstarVertices.at( i );
2725  drawSegments.push_back( getShapeFromVertex( prev->End, *cur, aContainer, aCadstarGroupID,
2726  aMoveVector, aRotationAngle, aScalingFactor,
2727  aTransformCentre, aMirrorInvert ) );
2728  prev = cur;
2729  }
2730 
2731  return drawSegments;
2732 }
2733 
2734 
2736  const VERTEX& aCadstarVertex,
2737  BOARD_ITEM_CONTAINER* aContainer,
2738  const GROUP_ID& aCadstarGroupID,
2739  const wxPoint& aMoveVector,
2740  const double& aRotationAngle,
2741  const double& aScalingFactor,
2742  const wxPoint& aTransformCentre,
2743  const bool& aMirrorInvert )
2744 {
2745  PCB_SHAPE* shape = nullptr;
2746  bool cw = false;
2747  double arcStartAngle, arcEndAngle, arcAngle;
2748 
2749  wxPoint startPoint = getKiCadPoint( aCadstarStartPoint );
2750  wxPoint endPoint = getKiCadPoint( aCadstarVertex.End );
2751  wxPoint centerPoint;
2752 
2753  if( aCadstarVertex.Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE
2754  || aCadstarVertex.Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
2755  {
2756  centerPoint = ( startPoint + endPoint ) / 2;
2757  }
2758  else
2759  {
2760  centerPoint = getKiCadPoint( aCadstarVertex.Center );
2761  }
2762 
2763  switch( aCadstarVertex.Type )
2764  {
2765 
2766  case VERTEX_TYPE::POINT:
2767 
2768  if( isFootprint( aContainer ) )
2769  {
2770  shape = new FP_SHAPE( static_cast<FOOTPRINT*>( aContainer ), SHAPE_T::SEGMENT );
2771  }
2772  else
2773  {
2774  shape = new PCB_SHAPE( aContainer );
2775  shape->SetShape( SHAPE_T::SEGMENT );
2776  }
2777 
2778  shape->SetStart( startPoint );
2779  shape->SetEnd( endPoint );
2780  break;
2781 
2784  cw = true;
2786 
2789 
2790  if( isFootprint( aContainer ) )
2791  {
2792  shape = new FP_SHAPE((FOOTPRINT*) aContainer, SHAPE_T::ARC );
2793  }
2794  else
2795  {
2796  shape = new PCB_SHAPE( aContainer );
2797  shape->SetShape( SHAPE_T::ARC );
2798  }
2799 
2800  shape->SetArcStart( startPoint );
2801  shape->SetCenter( centerPoint );
2802 
2803  arcStartAngle = getPolarAngle( startPoint - centerPoint );
2804  arcEndAngle = getPolarAngle( endPoint - centerPoint );
2805  arcAngle = arcEndAngle - arcStartAngle;
2806  //TODO: detect if we are supposed to draw a circle instead (i.e. two SEMICIRCLEs
2807  // with opposite start/end points and same centre point)
2808 
2809  if( cw )
2810  shape->SetAngle( NormalizeAnglePos( arcAngle ) );
2811  else
2812  shape->SetAngle( NormalizeAngleNeg( arcAngle ) );
2813 
2814  break;
2815  }
2816 
2817  //Apply transforms
2818  if( aMirrorInvert )
2819  shape->Flip( aTransformCentre, true );
2820 
2821  if( aScalingFactor != 1.0 )
2822  {
2823  shape->Move( -aTransformCentre );
2824  shape->Scale( aScalingFactor );
2825  shape->Move( aTransformCentre );
2826  }
2827 
2828  if( aRotationAngle != 0.0 )
2829  shape->Rotate( aTransformCentre, aRotationAngle );
2830 
2831  if( aMoveVector != wxPoint{ 0, 0 } )
2832  shape->Move( aMoveVector );
2833 
2834  if( isFootprint( aContainer ) && shape != nullptr )
2835  static_cast<FP_SHAPE*>( shape )->SetLocalCoord();
2836 
2837  if( !aCadstarGroupID.IsEmpty() )
2838  addToGroup( aCadstarGroupID, shape );
2839 
2840  return shape;
2841 }
2842 
2843 
2845  const int& aLineThickness,
2846  BOARD_ITEM_CONTAINER* aParentContainer )
2847 {
2848  ZONE* zone = new ZONE( aParentContainer, isFootprint( aParentContainer ) );
2849 
2850  if( aCadstarShape.Type == SHAPE_TYPE::HATCHED )
2851  {
2854  }
2855  else
2856  {
2858  }
2859 
2860  SHAPE_POLY_SET polygon = getPolySetFromCadstarShape( aCadstarShape, aLineThickness );
2861 
2862  zone->AddPolygon( polygon.COutline( 0 ) );
2863 
2864  for( int i = 0; i < polygon.HoleCount( 0 ); i++ )
2865  zone->AddPolygon( polygon.CHole( 0, i ) );
2866 
2867  return zone;
2868 }
2869 
2870 
2872  const int& aLineThickness,
2873  BOARD_ITEM_CONTAINER* aContainer,
2874  const wxPoint& aMoveVector,
2875  const double& aRotationAngle,
2876  const double& aScalingFactor,
2877  const wxPoint& aTransformCentre,
2878  const bool& aMirrorInvert )
2879 {
2880  GROUP_ID noGroup = wxEmptyString;
2881 
2882  std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( aCadstarShape.Vertices,
2883  aContainer, noGroup, aMoveVector,
2884  aRotationAngle, aScalingFactor,
2885  aTransformCentre, aMirrorInvert );
2886 
2887  SHAPE_POLY_SET polySet( getLineChainFromShapes( outlineShapes ) );
2888 
2889  //cleanup
2890  for( PCB_SHAPE* shape : outlineShapes )
2891  delete shape;
2892 
2893  for( CUTOUT cutout : aCadstarShape.Cutouts )
2894  {
2895  std::vector<PCB_SHAPE*> cutoutShapes = getShapesFromVertices( cutout.Vertices, aContainer,
2896  noGroup, aMoveVector,
2897  aRotationAngle, aScalingFactor,
2898  aTransformCentre, aMirrorInvert );
2899 
2900  polySet.AddHole( getLineChainFromShapes( cutoutShapes ) );
2901 
2902  //cleanup
2903  for( PCB_SHAPE* shape : cutoutShapes )
2904  delete shape;
2905  }
2906 
2907  if( aLineThickness > 0 )
2908  polySet.Inflate( aLineThickness / 2, 32,
2909  SHAPE_POLY_SET::CORNER_STRATEGY::ROUND_ALL_CORNERS );
2910 
2911 #ifdef DEBUG
2912  for( int i = 0; i < polySet.OutlineCount(); ++i )
2913  {
2914  wxASSERT( polySet.Outline( i ).PointCount() > 2 );
2915 
2916  for( int j = 0; j < polySet.HoleCount( i ); ++j )
2917  {
2918  wxASSERT( polySet.Hole( i, j ).PointCount() > 2 );
2919  }
2920  }
2921 #endif
2922 
2923  return polySet;
2924 }
2925 
2926 
2928 {
2929  SHAPE_LINE_CHAIN lineChain;
2930 
2931  for( PCB_SHAPE* shape : aShapes )
2932  {
2933  switch( shape->GetShape() )
2934  {
2935  case SHAPE_T::ARC:
2936  {
2937  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
2938  {
2939  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
2940  SHAPE_ARC arc( fp_shape->GetStart0(), fp_shape->GetEnd0(), fp_shape->GetAngle() / 10.0 );
2941  lineChain.Append( arc );
2942  }
2943  else
2944  {
2945  SHAPE_ARC arc( shape->GetCenter(), shape->GetArcStart(), shape->GetAngle() / 10.0 );
2946  lineChain.Append( arc );
2947  }
2948  }
2949  break;
2950  case SHAPE_T::SEGMENT:
2951  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
2952  {
2953  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
2954  lineChain.Append( fp_shape->GetStart0().x, fp_shape->GetStart0().y );
2955  lineChain.Append( fp_shape->GetEnd0().x, fp_shape->GetEnd0().y );
2956  }
2957  else
2958  {
2959  lineChain.Append( shape->GetStartX(), shape->GetStartY() );
2960  lineChain.Append( shape->GetEndX(), shape->GetEndY() );
2961  }
2962  break;
2963 
2964  default:
2965  wxFAIL_MSG( "Drawsegment type is unexpected. Ignored." );
2966  }
2967  }
2968 
2969  // Shouldn't have less than 3 points to make a closed shape!
2970  wxASSERT( lineChain.PointCount() > 2 );
2971 
2972  // Check if it is closed
2973  if( lineChain.GetPoint( 0 ) != lineChain.GetPoint( lineChain.PointCount() - 1 ) )
2974  {
2975  lineChain.Append( lineChain.GetPoint( 0 ) );
2976  }
2977 
2978  lineChain.SetClosed( true );
2979 
2980  return lineChain;
2981 }
2982 
2983 
2985  const std::vector<PCB_SHAPE*> aShapes,
2986  BOARD_ITEM_CONTAINER* aParentContainer,
2987  NETINFO_ITEM* aNet, PCB_LAYER_ID aLayerOverride,
2988  int aWidthOverride )
2989 {
2990  std::vector<PCB_TRACK*> tracks;
2991  PCB_TRACK* prevTrack = nullptr;
2992  PCB_TRACK* track = nullptr;
2993 
2994  auto addTrack =
2995  [&]( PCB_TRACK* aTrack )
2996  {
2997  // Ignore zero length tracks in the same way as the CADSTAR postprocessor does
2998  // when generating gerbers. Note that CADSTAR reports these as "Route offset
2999  // errors" when running a DRC within CADSTAR, so we shouldn't be getting this in
3000  // general, however it is used to remove any synthetic points added to
3001  // aDrawSegments by the caller of this function.
3002  if( aTrack->GetLength() != 0 )
3003  {
3004  tracks.push_back( aTrack );
3005  aParentContainer->Add( aTrack, ADD_MODE::APPEND );
3006  }
3007  else
3008  {
3009  delete aTrack;
3010  }
3011  };
3012 
3013  for( PCB_SHAPE* shape : aShapes )
3014  {
3015  switch( shape->GetShape() )
3016  {
3017  case SHAPE_T::ARC:
3018  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3019  {
3020  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3021  SHAPE_ARC arc( fp_shape->GetStart0(), fp_shape->GetEnd0(),
3022  fp_shape->GetAngle() / 10.0 );
3023  track = new PCB_ARC( aParentContainer, &arc );
3024  }
3025  else
3026  {
3027  SHAPE_ARC arc( shape->GetCenter(), shape->GetArcStart(), shape->GetAngle() / 10.0 );
3028  track = new PCB_ARC( aParentContainer, &arc );
3029  }
3030  break;
3031  case SHAPE_T::SEGMENT:
3032  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3033  {
3034  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3035  track = new PCB_TRACK( aParentContainer );
3036  track->SetStart( fp_shape->GetStart0() );
3037  track->SetEnd( fp_shape->GetEnd0() );
3038  }
3039  else
3040  {
3041  track = new PCB_TRACK( aParentContainer );
3042  track->SetStart( shape->GetStart() );
3043  track->SetEnd( shape->GetEnd() );
3044  }
3045  break;
3046 
3047  default:
3048  wxFAIL_MSG( "Drawsegment type is unexpected. Ignored." );
3049  continue;
3050  }
3051 
3052  if( aWidthOverride == -1 )
3053  track->SetWidth( shape->GetWidth() );
3054  else
3055  track->SetWidth( aWidthOverride );
3056 
3057  if( aLayerOverride == PCB_LAYER_ID::UNDEFINED_LAYER )
3058  track->SetLayer( shape->GetLayer() );
3059  else
3060  track->SetLayer( aLayerOverride );
3061 
3062  if( aNet != nullptr )
3063  track->SetNet( aNet );
3064 
3065  track->SetLocked( shape->IsLocked() );
3066 
3067  // Apply route offsetting, mimmicking the behaviour of the CADSTAR post processor
3068  if( prevTrack != nullptr )
3069  {
3070  track->SetStart( prevTrack->GetEnd() ); // remove discontinuities if possible
3071 
3072  int offsetAmount = ( track->GetWidth() / 2 ) - ( prevTrack->GetWidth() / 2 );
3073 
3074  if( offsetAmount > 0 )
3075  {
3076  // modify the start of the current track
3077  wxPoint newStart = track->GetStart();
3078  applyRouteOffset( &newStart, track->GetEnd(), offsetAmount );
3079  track->SetStart( newStart );
3080  }
3081  else if( offsetAmount < 0 )
3082  {
3083  // amend the end of the previous track
3084  wxPoint newEnd = prevTrack->GetEnd();
3085  applyRouteOffset( &newEnd, prevTrack->GetStart(), -offsetAmount );
3086  prevTrack->SetEnd( newEnd );
3087  } // don't do anything if offsetAmount == 0
3088 
3089  // Add a synthetic track of the thinnest width between the tracks
3090  // to ensure KiCad features works as expected on the imported design
3091  // (KiCad expects tracks are contiguous segments)
3092  if( track->GetStart() != prevTrack->GetEnd() )
3093  {
3094  int minWidth = std::min( track->GetWidth(), prevTrack->GetWidth() );
3095  PCB_TRACK* synthTrack = new PCB_TRACK( aParentContainer );
3096  synthTrack->SetStart( prevTrack->GetEnd() );
3097  synthTrack->SetEnd( track->GetStart() );
3098  synthTrack->SetWidth( minWidth );
3099  synthTrack->SetLocked( track->IsLocked() );
3100  synthTrack->SetNet( track->GetNet() );
3101  synthTrack->SetLayer( track->GetLayer() );
3102  addTrack( synthTrack );
3103  }
3104  }
3105 
3106  if( prevTrack )
3107  addTrack( prevTrack );
3108 
3109  prevTrack = track;
3110  }
3111 
3112  if( track )
3113  addTrack( track );
3114 
3115  return tracks;
3116 }
3117 
3118 
3120  const ATTRIBUTE_ID& aCadstarAttributeID,
3121  FOOTPRINT* aFootprint,
3122  const wxString& aAttributeValue )
3123 {
3124  FP_TEXT* txt;
3125 
3126  if( aCadstarAttributeID == COMPONENT_NAME_ATTRID )
3127  {
3128  txt = &aFootprint->Reference(); //text should be set outside this function
3129  }
3130  else if( aCadstarAttributeID == PART_NAME_ATTRID )
3131  {
3132  if( aFootprint->Value().GetText().IsEmpty() )
3133  {
3134  // Use PART_NAME_ATTRID as the value is value field is blank
3135  aFootprint->SetValue( aAttributeValue );
3136  txt = &aFootprint->Value();
3137  }
3138  else
3139  {
3140  txt = new FP_TEXT( aFootprint );
3141  aFootprint->Add( txt );
3142  txt->SetText( aAttributeValue );
3143  }
3144  txt->SetVisible( false ); //make invisible to avoid clutter.
3145  }
3146  else if( aCadstarAttributeID != COMPONENT_NAME_2_ATTRID
3147  && getAttributeName( aCadstarAttributeID ) == wxT( "Value" ) )
3148  {
3149  if( !aFootprint->Value().GetText().IsEmpty() )
3150  {
3151  //copy the object
3152  aFootprint->Add( aFootprint->Value().Duplicate() );
3153  }
3154 
3155  aFootprint->SetValue( aAttributeValue );
3156  txt = &aFootprint->Value();
3157  txt->SetVisible( false ); //make invisible to avoid clutter.
3158  }
3159  else
3160  {
3161  txt = new FP_TEXT( aFootprint );
3162  aFootprint->Add( txt );
3163  txt->SetText( aAttributeValue );
3164  txt->SetVisible( false ); //make all user attributes invisible to avoid clutter.
3165  //TODO: Future improvement - allow user to decide what to do with attributes
3166  }
3167 
3168  wxPoint rotatedTextPos = getKiCadPoint( aCadstarAttrLoc.Position ) - aFootprint->GetPosition();
3169  RotatePoint( &rotatedTextPos, -aFootprint->GetOrientation() );
3170 
3171  txt->SetTextPos( getKiCadPoint( aCadstarAttrLoc.Position ) );
3172  txt->SetPos0( rotatedTextPos );
3173  txt->SetLayer( getKiCadLayer( aCadstarAttrLoc.LayerID ) );
3174  txt->SetMirrored( aCadstarAttrLoc.Mirror );
3175  txt->SetTextAngle(
3176  getAngleTenthDegree( aCadstarAttrLoc.OrientAngle ) - aFootprint->GetOrientation() );
3177 
3178  if( aCadstarAttrLoc.Mirror ) // If mirroring, invert angle to match CADSTAR
3179  txt->SetTextAngle( -txt->GetTextAngle() );
3180 
3181  TEXTCODE tc = getTextCode( aCadstarAttrLoc.TextCodeID );
3182 
3184 
3185  wxSize txtSize;
3186  txtSize.x = getKiCadLength( tc.Width );
3187 
3188  // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
3189  // to work well for most fonts.
3190  if( txtSize.x == 0 )
3191  txtSize.x = getKiCadLength( tc.Height );
3192 
3193  txtSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
3194  txt->SetTextSize( txtSize );
3195  txt->SetKeepUpright( false ); //Keeping it upright seems to result in incorrect orientation
3196 
3197  switch( aCadstarAttrLoc.Alignment )
3198  {
3199  case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
3202  case ALIGNMENT::BOTTOMLEFT:
3205  break;
3206 
3210  break;
3211 
3215  break;
3216 
3217  case ALIGNMENT::CENTERLEFT:
3220  break;
3221 
3225  break;
3226 
3230  break;
3231 
3232  case ALIGNMENT::TOPLEFT:
3235  break;
3236 
3237  case ALIGNMENT::TOPCENTER:
3240  break;
3241 
3242  case ALIGNMENT::TOPRIGHT:
3245  break;
3246 
3247  default:
3248  wxFAIL_MSG( "Unknown Alignment - needs review!" );
3249  }
3250 
3251  //TODO Handle different font types when KiCad can support it.
3252 }
3253 
3254 
3255 void CADSTAR_PCB_ARCHIVE_LOADER::applyRouteOffset( wxPoint* aPointToOffset,
3256  const wxPoint& aRefPoint,
3257  const long& aOffsetAmount )
3258 {
3259  VECTOR2I v( *aPointToOffset - aRefPoint );
3260  int newLength = v.EuclideanNorm() - aOffsetAmount;
3261 
3262  if( newLength > 0 )
3263  {
3264  VECTOR2I offsetted = v.Resize( newLength ) + VECTOR2I( aRefPoint );
3265  aPointToOffset->x = offsetted.x;
3266  aPointToOffset->y = offsetted.y;
3267  }
3268  else
3269  {
3270  *aPointToOffset = aRefPoint; // zero length track. Needs to be removed to mimmick
3271  // cadstar behaviour
3272  }
3273 }
3274 
3275 
3277 {
3278  wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID )
3279  != Assignments.Codedefs.LineCodes.end(),
3281 
3282  return getKiCadLength( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Width );
3283 }
3284 
3285 
3287  const COPPERCODE_ID& aCadstaCopperCodeID )
3288 {
3289  wxCHECK( Assignments.Codedefs.CopperCodes.find( aCadstaCopperCodeID )
3290  != Assignments.Codedefs.CopperCodes.end(),
3291  COPPERCODE() );
3292 
3293  return Assignments.Codedefs.CopperCodes.at( aCadstaCopperCodeID );
3294 }
3295 
3296 
3298  const TEXTCODE_ID& aCadstarTextCodeID )
3299 {
3300  wxCHECK( Assignments.Codedefs.TextCodes.find( aCadstarTextCodeID )
3301  != Assignments.Codedefs.TextCodes.end(),
3302  TEXTCODE() );
3303 
3304  return Assignments.Codedefs.TextCodes.at( aCadstarTextCodeID );
3305 }
3306 
3307 
3309  const PADCODE_ID& aCadstarPadCodeID )
3310 {
3311  wxCHECK( Assignments.Codedefs.PadCodes.find( aCadstarPadCodeID )
3312  != Assignments.Codedefs.PadCodes.end(),
3313  PADCODE() );
3314 
3315  return Assignments.Codedefs.PadCodes.at( aCadstarPadCodeID );
3316 }
3317 
3318 
3320  const VIACODE_ID& aCadstarViaCodeID )
3321 {
3322  wxCHECK( Assignments.Codedefs.ViaCodes.find( aCadstarViaCodeID )
3323  != Assignments.Codedefs.ViaCodes.end(),
3324  VIACODE() );
3325 
3326  return Assignments.Codedefs.ViaCodes.at( aCadstarViaCodeID );
3327 }
3328 
3329 
3331  const LAYERPAIR_ID& aCadstarLayerPairID )
3332 {
3333  wxCHECK( Assignments.Codedefs.LayerPairs.find( aCadstarLayerPairID )
3334  != Assignments.Codedefs.LayerPairs.end(),
3335  LAYERPAIR() );
3336 
3337  return Assignments.Codedefs.LayerPairs.at( aCadstarLayerPairID );
3338 }
3339 
3340 
3341 wxString CADSTAR_PCB_ARCHIVE_LOADER::getAttributeName( const ATTRIBUTE_ID& aCadstarAttributeID )
3342 {
3343  wxCHECK( Assignments.Codedefs.AttributeNames.find( aCadstarAttributeID )
3345  wxEmptyString );
3346 
3347  return Assignments.Codedefs.AttributeNames.at( aCadstarAttributeID ).Name;
3348 }
3349 
3350 
3352  const std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE>& aCadstarAttributeMap )
3353 {
3354  wxCHECK( aCadstarAttributeMap.find( aCadstarAttributeID ) != aCadstarAttributeMap.end(),
3355  wxEmptyString );
3356 
3357  return aCadstarAttributeMap.at( aCadstarAttributeID ).Value;
3358 }
3359 
3360 
3362  const PART_ID& aCadstarPartID )
3363 {
3364  wxCHECK( Parts.PartDefinitions.find( aCadstarPartID ) != Parts.PartDefinitions.end(), PART() );
3365 
3366  return Parts.PartDefinitions.at( aCadstarPartID );
3367 }
3368 
3369 
3371  const ROUTECODE_ID& aCadstarRouteCodeID )
3372 {
3373  wxCHECK( Assignments.Codedefs.RouteCodes.find( aCadstarRouteCodeID )
3374  != Assignments.Codedefs.RouteCodes.end(),
3375  ROUTECODE() );
3376 
3377  return Assignments.Codedefs.RouteCodes.at( aCadstarRouteCodeID );
3378 }
3379 
3380 
3382  const HATCHCODE_ID& aCadstarHatchcodeID )
3383 {
3384  wxCHECK( Assignments.Codedefs.HatchCodes.find( aCadstarHatchcodeID )
3385  != Assignments.Codedefs.HatchCodes.end(),
3386  HATCHCODE() );
3387 
3388  return Assignments.Codedefs.HatchCodes.at( aCadstarHatchcodeID );
3389 }
3390 
3391 
3393  const HATCHCODE_ID& aCadstarHatchcodeID )
3394 {
3395  checkAndLogHatchCode( aCadstarHatchcodeID );
3396  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3397 
3398  if( hcode.Hatches.size() < 1 )
3400  else
3401  return getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle );
3402 }
3403 
3404 
3406  const HATCHCODE_ID& aCadstarHatchcodeID )
3407 {
3408  checkAndLogHatchCode( aCadstarHatchcodeID );
3409  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3410 
3411  if( hcode.Hatches.size() < 1 )
3413  else
3414  return getKiCadLength( hcode.Hatches.at( 0 ).LineWidth );
3415 }
3416 
3417 
3419 {
3420  checkAndLogHatchCode( aCadstarHatchcodeID );
3421  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3422 
3423  if( hcode.Hatches.size() < 1 )
3425  else
3426  return getKiCadLength( hcode.Hatches.at( 0 ).Step );
3427 }
3428 
3429 
3431 {
3432  wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), nullptr );
3433 
3434  return m_groupMap.at( aCadstarGroupID );
3435 }
3436 
3437 
3439 {
3440  if( m_hatchcodesTested.find( aCadstarHatchcodeID ) != m_hatchcodesTested.end() )
3441  {
3442  return; //already checked
3443  }
3444  else
3445  {
3446  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3447 
3448  if( hcode.Hatches.size() != 2 )
3449  {
3450  wxLogWarning( wxString::Format(
3451  _( "The CADSTAR Hatching code '%s' has %d hatches defined. "
3452  "KiCad only supports 2 hatches (crosshatching) 90 degrees apart. "
3453  "The imported hatching is crosshatched." ),
3454  hcode.Name, (int) hcode.Hatches.size() ) );
3455  }
3456  else
3457  {
3458  if( hcode.Hatches.at( 0 ).LineWidth != hcode.Hatches.at( 1 ).LineWidth )
3459  {
3460  wxLogWarning( wxString::Format(
3461  _( "The CADSTAR Hatching code '%s' has different line widths for each "
3462  "hatch. KiCad only supports one width for the haching. The imported "
3463  "hatching uses the width defined in the first hatch definition, i.e. "
3464  "%.2f mm." ),
3465  hcode.Name,
3466  (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).LineWidth ) )
3467  / 1E6 ) );
3468  }
3469 
3470  if( hcode.Hatches.at( 0 ).Step != hcode.Hatches.at( 1 ).Step )
3471  {
3472  wxLogWarning( wxString::Format(
3473  _( "The CADSTAR Hatching code '%s' has different step sizes for each "
3474  "hatch. KiCad only supports one step size for the haching. The imported "
3475  "hatching uses the step size defined in the first hatching definition, "
3476  "i.e. %.2f mm." ),
3477  hcode.Name,
3478  (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).Step ) )
3479  / 1E6 ) );
3480  }
3481 
3482  if( abs( hcode.Hatches.at( 0 ).OrientAngle - hcode.Hatches.at( 1 ).OrientAngle )
3483  != 90000 )
3484  {
3485  wxLogWarning( wxString::Format(
3486  _( "The hatches in CADSTAR Hatching code '%s' have an angle "
3487  "difference of %.1f degrees. KiCad only supports hatching 90 "
3488  "degrees apart. The imported hatching has two hatches 90 "
3489  "degrees apart, oriented %.1f degrees from horizontal." ),
3490  hcode.Name,
3491  getAngleDegrees( abs( hcode.Hatches.at( 0 ).OrientAngle
3492  - hcode.Hatches.at( 1 ).OrientAngle ) ),
3493  getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle ) ) );
3494  }
3495  }
3496 
3497  m_hatchcodesTested.insert( aCadstarHatchcodeID );
3498  }
3499 }
3500 
3501 
3503  PCB_DIMENSION_BASE* aKiCadDim )
3504 {
3505  UNITS dimensionUnits = aCadstarDim.LinearUnits;
3506  TEXTCODE txtCode = getTextCode( aCadstarDim.Text.TextCodeID );
3507  int correctedHeight = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( txtCode.Height ) );
3508  wxSize txtSize( getKiCadLength( txtCode.Width ), correctedHeight );
3509  LINECODE linecode = Assignments.Codedefs.LineCodes.at( aCadstarDim.Line.LineCodeID );
3510 
3511  aKiCadDim->SetLayer( getKiCadLayer( aCadstarDim.LayerID ) );
3512  aKiCadDim->SetPrecision( aCadstarDim.Precision );
3513  aKiCadDim->SetStart( getKiCadPoint( aCadstarDim.ExtensionLineParams.Start ) );
3514  aKiCadDim->SetEnd( getKiCadPoint( aCadstarDim.ExtensionLineParams.End ) );
3515  aKiCadDim->SetExtensionOffset( getKiCadLength( aCadstarDim.ExtensionLineParams.Offset ) );
3516  aKiCadDim->SetLineThickness( getKiCadLength( linecode.Width ) );
3517  aKiCadDim->Text().SetTextThickness( getKiCadLength( txtCode.LineWidth ) );
3518  aKiCadDim->Text().SetTextSize( txtSize );
3519 
3520  // Find prefix and suffix:
3521  wxString prefix = wxEmptyString;
3522  wxString suffix = wxEmptyString;
3523  size_t startpos = aCadstarDim.Text.Text.Find( wxT( "<@DISTANCE" ) );
3524 
3525  if( startpos != wxNOT_FOUND )
3526  {
3527  prefix = ParseTextFields( aCadstarDim.Text.Text.SubString( 0, startpos - 1 ), &m_context );
3528  wxString remainingStr = aCadstarDim.Text.Text.Mid( startpos );
3529  size_t endpos = remainingStr.Find( "@>" );
3530  suffix = ParseTextFields( remainingStr.Mid( endpos + 2 ), &m_context );
3531  }
3532 
3533  if( suffix.StartsWith( "mm" ) )
3534  {
3536  suffix = suffix.Mid( 2 );
3537  }
3538  else
3539  {
3541  }
3542 
3543  aKiCadDim->SetPrefix( prefix );
3544  aKiCadDim->SetSuffix( suffix );
3545 
3546  if( aCadstarDim.LinearUnits == UNITS::DESIGN )
3547  {
3548  // For now we will hardcode the units as per the original CADSTAR design.
3549  // TODO: update this when KiCad supports design units
3551  dimensionUnits = Assignments.Technology.Units;
3552  }
3553 
3554  switch( dimensionUnits )
3555  {
3556  case UNITS::METER:
3557  case UNITS::CENTIMETER:
3558  case UNITS::MICROMETRE:
3559  wxLogWarning( wxString::Format( _( "Dimension ID %s uses a type of unit that "
3560  "is not supported in KiCad. Millimetres were "
3561  "applied instead." ),
3562  aCadstarDim.ID ) );
3564  case UNITS::MM:
3566  break;
3567 
3568  case UNITS::INCH:
3569  aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::INCHES );
3570  break;
3571 
3572  case UNITS::THOU:
3573  aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::MILS );
3574  break;
3575 
3576  case UNITS::DESIGN:
3577  wxFAIL_MSG( "We should have handled design units before coming here!" );
3578  break;
3579  }
3580 }
3581 
3582 
3584 {
3585  std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
3586  std::set<std::pair<TEMPLATE_ID, TEMPLATE_ID>> scheduleInferPriorityFromOutline;
3587 
3588  // Calculate the intersection between aPolygon and the outline of aZone
3589  auto intersectionArea = [&]( const SHAPE_POLY_SET& aPolygon, ZONE* aZone ) -> double
3590  {
3591  SHAPE_POLY_SET intersectShape( *aZone->Outline() );
3592 
3593  intersectShape.BooleanIntersection( aPolygon,
3595  return intersectShape.Area();
3596  };
3597 
3598  // Lambda to determine if the zone with template ID 'a' is lower priority than 'b'
3599  auto isLowerPriority = [&]( const TEMPLATE_ID& a, const TEMPLATE_ID& b ) -> bool
3600  {
3601  return winningOverlaps[b].count( a ) > 0;
3602  };
3603 
3604  for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = m_zonesMap.begin();
3605  it1 != m_zonesMap.end(); ++it1 )
3606  {
3607  TEMPLATE thisTemplate = Layout.Templates.at( it1->first );
3608  PCB_LAYER_ID thisLayer = getKiCadLayer( thisTemplate.LayerID );
3609  ZONE* thisZone = it1->second;
3610 
3611  for( std::map<TEMPLATE_ID, ZONE*>::iterator it2 = it1;
3612  it2 != m_zonesMap.end(); ++it2 )
3613  {
3614  TEMPLATE otherTemplate = Layout.Templates.at( it2->first );
3615  PCB_LAYER_ID otherLayer = getKiCadLayer( otherTemplate.LayerID );
3616  ZONE* otherZone = it2->second;
3617 
3618  if( thisTemplate.ID == otherTemplate.ID )
3619  continue;
3620 
3621  if( thisLayer != otherLayer )
3622  continue;
3623 
3624  SHAPE_POLY_SET thisZonePolyFill = thisZone->GetFilledPolysList( thisLayer );
3625  SHAPE_POLY_SET otherZonePolyFill = otherZone->GetFilledPolysList( otherLayer );
3626 
3627  if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
3628  {
3629  // Intersect the filled polygons of thisZone with the *outline* of otherZone
3630  double areaThis = intersectionArea( thisZonePolyFill, otherZone );
3631  // Viceversa
3632  double areaOther = intersectionArea( otherZonePolyFill, thisZone );
3633 
3634  // Best effort: Compare Areas
3635  // If thisZone's fill polygons overlap otherZone's outline *and* the opposite
3636  // is true: otherZone's fill polygons overlap thisZone's outline then compare the
3637  // intersection areas to decide which of the two zones should have higher priority
3638  // There are some edge cases where this might not work, but it is in the minority.
3639  if( areaThis > areaOther )
3640  {
3641  winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3642  }
3643  else if( areaOther > 0.0 )
3644  {
3645  winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3646  }
3647  else
3648  {
3649  scheduleInferPriorityFromOutline.insert(
3650  { thisTemplate.ID, otherTemplate.ID } );
3651  }
3652  }
3653  else
3654  {
3655  // One of the templates is not poured in the original CADSTAR design.
3656  // Lets infer the priority based of the outlines instead
3657  scheduleInferPriorityFromOutline.insert( { thisTemplate.ID, otherTemplate.ID } );
3658  }
3659  }
3660  }
3661 
3662  // Build a set of unique TEMPLATE_IDs of all the zones that intersect with another one
3663  std::set<TEMPLATE_ID> intersectingIDs;
3664 
3665  for( const std::pair<TEMPLATE_ID, std::set<TEMPLATE_ID>>& idPair : winningOverlaps )
3666  {
3667  intersectingIDs.insert( idPair.first );
3668  intersectingIDs.insert( idPair.second.begin(), idPair.second.end() );
3669  }
3670 
3671  // Now store them in a vector
3672  std::vector<TEMPLATE_ID> sortedIDs;
3673 
3674  for( const TEMPLATE_ID& id : intersectingIDs )
3675  {
3676  sortedIDs.push_back( id );
3677  }
3678 
3679  // sort by priority
3680  std::sort( sortedIDs.begin(), sortedIDs.end(), isLowerPriority );
3681 
3682  TEMPLATE_ID prevID = wxEmptyString;
3683 
3684  for( const TEMPLATE_ID& id : sortedIDs )
3685  {
3686  if( prevID.IsEmpty() )
3687  {
3688  prevID = id;
3689  continue;
3690  }
3691 
3692  wxASSERT( !isLowerPriority( id, prevID ) );
3693 
3694  int newPriority = m_zonesMap.at( prevID )->GetPriority();
3695 
3696  // Only increase priority of the current zone
3697  if( isLowerPriority( prevID, id ) )
3698  newPriority++;
3699 
3700  m_zonesMap.at( id )->SetPriority( newPriority );
3701  prevID = id;
3702  }
3703 
3704 }
3705 
3706 
3708  const COMPONENT_ID& aCadstarComponentID )
3709 {
3710  if( m_componentMap.find( aCadstarComponentID ) == m_componentMap.end() )
3711  return nullptr;
3712  else
3713  return m_componentMap.at( aCadstarComponentID );
3714 }
3715 
3716 
3717 wxPoint CADSTAR_PCB_ARCHIVE_LOADER::getKiCadPoint( const wxPoint& aCadstarPoint )
3718 {
3719  wxPoint retval;
3720 
3721  retval.x = ( aCadstarPoint.x - m_designCenter.x ) * KiCadUnitMultiplier;
3722  retval.y = -( aCadstarPoint.y - m_designCenter.y ) * KiCadUnitMultiplier;
3723 
3724  return retval;
3725 }
3726 
3727 
3728 double CADSTAR_PCB_ARCHIVE_LOADER::getPolarAngle( const wxPoint& aPoint )
3729 {
3730  return NormalizeAnglePos( ArcTangente( aPoint.y, aPoint.x ) );
3731 }
3732 
3733 
3735 {
3736  if( aCadstarNetID.IsEmpty() )
3737  return nullptr;
3738  else if( m_netMap.find( aCadstarNetID ) != m_netMap.end() )
3739  {
3740  return m_netMap.at( aCadstarNetID );
3741  }
3742  else
3743  {
3744  wxCHECK( Layout.Nets.find( aCadstarNetID ) != Layout.Nets.end(), nullptr );
3745 
3746  NET_PCB csNet = Layout.Nets.at( aCadstarNetID );
3747  wxString newName = csNet.Name;
3748 
3749  if( csNet.Name.IsEmpty() )
3750  {
3751  if( csNet.Pins.size() > 0 )
3752  {
3753  // Create default KiCad net naming:
3754 
3755  NET_PCB::PIN firstPin = ( *csNet.Pins.begin() ).second;
3756  //we should have already loaded the component with loadComponents() :
3757  FOOTPRINT* m = getFootprintFromCadstarID( firstPin.ComponentID );
3758  newName = wxT( "Net-(" );
3759  newName << m->Reference().GetText();
3760  newName << "-Pad" << wxString::Format( "%ld", firstPin.PadID ) << ")";
3761  }
3762  else
3763  {
3764  wxFAIL_MSG( "A net with no pins associated?" );
3765  newName = wxT( "csNet-" );
3766  newName << wxString::Format( "%i", csNet.SignalNum );
3767  }
3768  }
3769 
3770  if( !m_doneNetClassWarning && !csNet.NetClassID.IsEmpty()
3771  && csNet.NetClassID != wxT( "NONE" ) )
3772  {
3773  wxLogMessage(
3774  _( "The CADSTAR design contains nets with a 'Net Class' assigned. KiCad does "
3775  "not have an equivalent to CADSTAR's Net Class so these elements were not "
3776  "imported. Note: KiCad's version of 'Net Class' is closer to CADSTAR's "
3777  "'Net Route Code' (which has been imported for all nets)." ) );
3778  m_doneNetClassWarning = true;
3779  }
3780 
3781  if( !m_doneSpacingClassWarning && !csNet.SpacingClassID.IsEmpty()
3782  && csNet.SpacingClassID != wxT( "NONE" ) )
3783  {
3784  wxLogWarning( _( "The CADSTAR design contains nets with a 'Spacing Class' assigned. "
3785  "KiCad does not have an equivalent to CADSTAR's Spacing Class so "
3786  "these elements were not imported. Please review the design rules as "
3787  "copper pours will affected by this." ) );
3789  }
3790 
3791  NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, newName, ++m_numNets );
3792  m_board->Add( netInfo, ADD_MODE::APPEND );
3793 
3794  if( m_netClassMap.find( csNet.RouteCodeID ) != m_netClassMap.end() )
3795  {
3796  NETCLASSPTR netclass = m_netClassMap.at( csNet.RouteCodeID );
3797  netInfo->SetNetClass( netclass );
3798  }
3799  else
3800  {
3801  ROUTECODE rc = getRouteCode( csNet.RouteCodeID );
3802  NETCLASSPTR netclass( new NETCLASS( rc.Name ) );
3803  netclass->SetTrackWidth( getKiCadLength( rc.OptimalWidth ) );
3804  netInfo->SetNetClass( netclass );
3805  m_netClassMap.insert( { csNet.RouteCodeID, netclass } );
3806  }
3807 
3808  m_netMap.insert( { aCadstarNetID, netInfo } );
3809  return netInfo;
3810  }
3811 
3812  return nullptr;
3813 }
3814 
3815 
3817  bool aDetectMaxLayer )
3818 {
3819  if( aDetectMaxLayer && aLayerNum == Assignments.Technology.MaxPhysicalLayer )
3820  return PCB_LAYER_ID::B_Cu;
3821 
3822  switch( aLayerNum )
3823  {
3824  case 1: return PCB_LAYER_ID::F_Cu;
3825  case 2: return PCB_LAYER_ID::In1_Cu;
3826  case 3: return PCB_LAYER_ID::In2_Cu;
3827  case 4: return PCB_LAYER_ID::In3_Cu;
3828  case 5: return PCB_LAYER_ID::In4_Cu;
3829  case 6: return PCB_LAYER_ID::In5_Cu;
3830  case 7: return PCB_LAYER_ID::In6_Cu;
3831  case 8: return PCB_LAYER_ID::In7_Cu;
3832  case 9: return PCB_LAYER_ID::In8_Cu;
3833  case 10: return PCB_LAYER_ID::In9_Cu;
3834  case 11: return PCB_LAYER_ID::In10_Cu;
3835  case 12: return PCB_LAYER_ID::In11_Cu;
3836  case 13: return PCB_LAYER_ID::In12_Cu;
3837  case 14: return PCB_LAYER_ID::In13_Cu;
3838  case 15: return PCB_LAYER_ID::In14_Cu;
3839  case 16: return PCB_LAYER_ID::In15_Cu;
3840  case 17: return PCB_LAYER_ID::In16_Cu;
3841  case 18: return PCB_LAYER_ID::In17_Cu;
3842  case 19: return PCB_LAYER_ID::In18_Cu;
3843  case 20: return PCB_LAYER_ID::In19_Cu;
3844  case 21: return PCB_LAYER_ID::In20_Cu;
3845  case 22: return PCB_LAYER_ID::In21_Cu;
3846  case 23: return PCB_LAYER_ID::In22_Cu;
3847  case 24: return PCB_LAYER_ID::In23_Cu;
3848  case 25: return PCB_LAYER_ID::In24_Cu;
3849  case 26: return PCB_LAYER_ID::In25_Cu;
3850  case 27: return PCB_LAYER_ID::In26_Cu;
3851  case 28: return PCB_LAYER_ID::In27_Cu;
3852  case 29: return PCB_LAYER_ID::In28_Cu;
3853  case 30: return PCB_LAYER_ID::In29_Cu;
3854  case 31: return PCB_LAYER_ID::In30_Cu;
3855  case 32: return PCB_LAYER_ID::B_Cu;
3856  }
3857 
3859 }
3860 
3861 
3862 bool CADSTAR_PCB_ARCHIVE_LOADER::isLayerSet( const LAYER_ID& aCadstarLayerID )
3863 {
3864  wxCHECK( Assignments.Layerdefs.Layers.find( aCadstarLayerID )
3865  != Assignments.Layerdefs.Layers.end(),
3866  false );
3867 
3868  LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID );
3869 
3870  switch( layer.Type )
3871  {
3872  case LAYER_TYPE::ALLDOC:
3873  case LAYER_TYPE::ALLELEC:
3874  case LAYER_TYPE::ALLLAYER:
3875  return true;
3876 
3877  default:
3878  return false;
3879  }
3880 
3881  return false;
3882 }
3883 
3884 
3886 {
3887  if( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) != Assignments.Layerdefs.Layers.end() )
3888  {
3889  if( Assignments.Layerdefs.Layers.at( aCadstarLayerID ).Type == LAYER_TYPE::NOLAYER )
3890  //The "no layer" is common for CADSTAR documentation symbols
3891  //map it to undefined layer for later processing
3893  }
3894 
3895  wxCHECK( m_layermap.find( aCadstarLayerID ) != m_layermap.end(),
3897 
3898  return m_layermap.at( aCadstarLayerID );
3899 }
3900 
3901 
3903 {
3904  LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID );
3905 
3906  switch( layer.Type )
3907  {
3908  case LAYER_TYPE::ALLDOC:
3911 
3912  case LAYER_TYPE::ALLELEC:
3913  return LSET::AllCuMask();
3914 
3915  case LAYER_TYPE::ALLLAYER:
3916  return LSET::AllLayersMask();
3917 
3918  default:
3919  return LSET( getKiCadLayer( aCadstarLayerID ) );
3920  }
3921 }
3922 
3923 
3925  const GROUP_ID& aCadstarGroupID, BOARD_ITEM* aKiCadItem )
3926 {
3927  wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), );
3928 
3929  PCB_GROUP* parentGroup = m_groupMap.at( aCadstarGroupID );
3930  parentGroup->AddItem( aKiCadItem );
3931 }
3932 
3933 
3935  const wxString& aName )
3936 {
3937  wxString groupName = aName;
3938  int num = 0;
3939 
3940  while( m_groupMap.find( groupName ) != m_groupMap.end() )
3941  {
3942  groupName = aName + wxT( "_" ) + wxString::Format( "%i", ++num );
3943  }
3944 
3945  PCB_GROUP* docSymGroup = new PCB_GROUP( m_board );
3946  m_board->Add( docSymGroup );
3947  docSymGroup->SetName( groupName );
3948  GROUP_ID groupID( groupName );
3949  m_groupMap.insert( { groupID, docSymGroup } );
3950 
3951  return groupID;
3952 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:188
void SetReference(const wxString &aReference)
Definition: footprint.h:439
static LSET UserDefinedLayers()
Return a mask with all of the allowable user defined layers.
Definition: lset.cpp:856
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
COPPERCODE getCopperCode(const COPPERCODE_ID &aCadstaCopperCodeID)
int getKiCadLength(long long aCadstarLength)
LAYER_ID LayerID
Move all objects in the Symdef to this layer.
bool AddItem(BOARD_ITEM *aItem)
Add item to group.
Definition: pcb_group.cpp:38
std::vector< PCB_TRACK * > makeTracksFromShapes(const std::vector< PCB_SHAPE * > aShapes, BOARD_ITEM_CONTAINER *aParentContainer, NETINFO_ITEM *aNet=nullptr, PCB_LAYER_ID aLayerOverride=UNDEFINED_LAYER, int aWidthOverride=-1)
Returns a vector of pointers to TRACK/ARC objects.
void addAttribute(const ATTRIBUTE_LOCATION &aCadstarAttrLoc, const ATTRIBUTE_ID &aCadstarAttributeID, FOOTPRINT *aFootprint, const wxString &aAttributeValue)
Adds a CADSTAR Attribute to a KiCad footprint.
int getKiCadHatchCodeGap(const HATCHCODE_ID &aCadstarHatchcodeID)
virtual void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT)=0
Adds an item to the container.
void SetIslandRemovalMode(ISLAND_REMOVAL_MODE aRemove)
Definition: zone.h:750
void applyDimensionSettings(const DIMENSION &aCadstarDim, PCB_DIMENSION_BASE *aKiCadDim)
virtual std::map< wxString, wxString > & GetTextVars() const
Definition: project.cpp:78
Arcs (with rounded ends)
virtual void SetMaxProgress(int aMaxProgress)=0
Fix the value that gives the 100 percent progress bar length (inside the current virtual zone).
std::map< ROUTECODE_ID, ROUTECODE > RouteCodes
void SetHatchThickness(int aThickness)
Definition: zone.h:254
T NormalizeAngleNeg(T Angle)
Normalize angle to be in the 0.0 .. -360.0 range: angle is in 1/10 degrees.
Definition: trigo.h:268
POURING Pouring
Copper pour settings (e.g. relief / hatching /etc.)
PADCODE_ID PadCode
If not empty, override padcode.
virtual void BeginPhase(int aPhase)=0
Initialize the aPhase virtual zone of the dialog progress bar.
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition: zone.h:627
Inbuilt layer type (cannot be assigned to user layers)
int OutlineCount() const
Return the number of vertices in a given outline/hole.
Inbuilt layer type (cannot be assigned to user layers)
Inbuilt layer type (cannot be assigned to user layers)
long LeaderAngle
Only for TYPE=LEADERLINE subnode "LEADERANG".
void SetZoneName(const wxString &aName)
Definition: zone.h:130
bool ThermalReliefOnVias
false when subnode "NOVIARELIEF" is present
static const double TXT_HEIGHT_RATIO
CADSTAR fonts are drawn on a 24x24 integer matrix, where the each axis goes from 0 to 24.
NETINFO_ITEM * getKiCadNet(const NET_ID &aCadstarNetID)
Searches m_netMap and returns the NETINFO_ITEM pointer if exists.
Container for project specific data.
Definition: project.h:62
std::map< PART_DEFINITION_PIN_ID, PIN > Pins
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
bool PCBonlyPad
From CADSTAR Help: "The PCB Only Pad property can be used to stop ECO Update, Back Annotation,...
void SetShape(SHAPE_T aShape)
Definition: pcb_shape.h:109
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:745
std::pair< POINT, POINT > DesignArea
polygon (not yet used for tracks, but could be in microwave apps)
COPPERCODE_ID CopperCodeID
From CADSTAR Help: "Copper Code is for selecting the width of the line used to draw the outline and f...
SHAPE_POLY_SET & GetPolyShape()
Definition: pcb_shape.h:240
double getAngleDegrees(const long long &aCadstarAngle)
wxString ReferenceName
This is the name which identifies the symbol in the library Multiple components may exist with the sa...
bool m_LegacyNetclassesLoaded
True if netclasses were loaded from the file.
Definition: board.h:272
ZONE * getZoneFromCadstarShape(const SHAPE &aCadstarShape, const int &aLineThickness, BOARD_ITEM_CONTAINER *aParentContainer)
virtual void SetPosition(const wxPoint &aPos) override
Definition: pcb_text.h:81
long SignalNum
This is undefined if the net has been given a name.
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
std::map< COMPONENT_ID, FOOTPRINT * > m_componentMap
Map between Cadstar and KiCad components on the board.
void BooleanAdd(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset difference For aFastMode meaning, see function booleanOp.
Manage layers needed to make a physical board.
std::map< ATTRIBUTE_ID, ATTRNAME > AttributeNames
bool NoVias
From CADSTAR Help: "Check this button to specify that any area created by the Rectangle,...
void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: pcb_text.cpp:172
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:192
double getAngleTenthDegree(const long long &aCadstarAngle)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
long ScaleRatioNumerator
Documentation symbols can be arbitrarily scaled when added to a design.
void Scale(double aScale)
Definition: pcb_shape.cpp:191
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
Definition: zone.h:614
TEXT_FIELD_NAME
These are special fields in text objects enclosed between the tokens '<@' and '>' such as <@[FIELD_NA...
void drawCadstarShape(const SHAPE &aCadstarShape, const PCB_LAYER_ID &aKiCadLayer, const int &aLineThickness, const wxString &aShapeName, BOARD_ITEM_CONTAINER *aContainer, const GROUP_ID &aCadstarGroupID=wxEmptyString, const wxPoint &aMoveVector={ 0, 0 }, const double &aRotationAngle=0.0, const double &aScalingFactor=1.0, const wxPoint &aTransformCentre={ 0, 0 }, const bool &aMirrorInvert=false)
Definition: board.h:72
std::map< COMP_AREA_ID, COMPONENT_AREA > ComponentAreas
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
wxPoint getKiCadPoint(const wxPoint &aCadstarPoint)
Scales, offsets and inverts y axis to make the point usable directly in KiCad.
void SetTextAngle(double aAngle) override
Definition: pcb_text.cpp:104
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
PCB_TEXT & Text()
std::map< LINECODE_ID, LINECODE > LineCodes
void SetFilled(bool aFlag)
Definition: pcb_shape.h:73
wxString getAttributeValue(const ATTRIBUTE_ID &aCadstarAttributeID, const std::map< ATTRIBUTE_ID, ATTRIBUTE_VALUE > &aCadstarAttributeMap)
LAYER_MAPPING_HANDLER m_layerMappingHandler
Callback to get layer mapping.
EXTENSION_LINE ExtensionLineParams
Not applicable to TYPE=LEADERDIM.
void SetTextAngle(double aAngle) override
Definition: fp_text.cpp:73
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:246
SHAPE_POLY_SET * Outline()
Definition: zone.h:320
#define COMPONENT_NAME_2_ATTRID
Component Name 2 Attribute ID - typically used for indicating the placement of designators in placeme...
bool BoxIsolatedPins
true when subnode "BOXPINS" is present
wxPoint m_designCenter
Used for calculating the required offset to apply to the Cadstar design so that it fits in KiCad canv...
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: layer_ids.h:573
bool NoTracks
From CADSTAR Help: "Check this button to specify that any area created by the Rectangle,...
Filled closed shape (hatch fill).
Smd pad, appears on the solder paste layer (default)
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:246
PCB_GROUP * getKiCadGroup(const GROUP_ID &aCadstarGroupID)
LAYERPAIR getLayerPair(const LAYERPAIR_ID &aCadstarLayerPairID)
usual segment : line with rounded ends
std::map< FIGURE_ID, FIGURE > Figures
void SetText(const wxString &aNewText)
Set the override text - has no effect if m_overrideValue == false.
double GetOrientation() const
Definition: footprint.h:190
double GetTextAngle() const
Definition: eda_text.h:174
void SetCopperLayerCount(int aCount)
Definition: board.cpp:460
long ScaleRatioDenominator
Documentation symbols can be arbitrarily scaled when added to a design.
long PAD_ID
Pad identifier (pin) in the PCB.
void remapUnsureLayers()
Callback m_layerMappingHandler for layers we aren't sure of.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
const wxPoint & GetStart0() const
Definition: fp_shape.h:112
double Area()
Count the number of arc shapes present.
std::map< COPPERCODE_ID, COPPERCODE > CopperCodes
std::map< MATERIAL_ID, MATERIAL > Materials
#define COMPONENT_NAME_ATTRID
Component Name Attribute ID - typically used for placement of designators on silk screen.
bool m_doneSpacingClassWarning
Used by getKiCadNet() to avoid multiple duplicate warnings.
std::map< SYMDEF_ID, FOOTPRINT * > m_libraryMap
Map between Cadstar and KiCad components in the library.
wxString Name
Designator e.g. "C1", "R1", etc.
Definition: eserie.h:39
ROUTECODE getRouteCode(const ROUTECODE_ID &aCadstarRouteCodeID)
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
From CADSTAR Help: "Area is for creating areas within which, and nowhere else, certain operations are...
Abstract dimension API.
Definition: pcb_dimension.h:95
const SHAPE_POLY_SET & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:635
bool HasLocation
Flag to know if this ATTRIBUTE_VALUE has a location i.e.
long MinIsolatedCopper
The value is the length of one side of a notional square.
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:466
ALIGNMENT Alignment
In CADSTAR The default alignment for a TEXT object (when "(No Alignment()" is selected) Bottom Left o...
void checkAndLogHatchCode(const HATCHCODE_ID &aCadstarHatchcodeID)
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:237
std::set< HATCHCODE_ID > m_hatchcodesTested
Used by checkAndLogHatchCode() to avoid multiple duplicate warnings.
virtual void SetLocked(bool aLocked)
Modify the 'lock' status for of the item.
Definition: board_item.h:252
int GetWidth() const
Definition: pcb_track.h:102
void loadComponentAttributes(const COMPONENT &aComponent, FOOTPRINT *aFootprint)
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, int aAccuracy=0, bool aUseBBoxCaches=false) const
Return true if a given subpolygon contains the point aP.
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:409
void NORMALIZE_ANGLE_180(T &Angle)
Definition: trigo.h:398
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
LAYER_ID LayerID
ID on which to draw this [param1].
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:51
bool Routing
From CADSTAR Help: "Area can be used to place routes during Automatic Routing.
std::map< wxString, wxString > FilenamesToTextMap
CADSTAR doesn't have user defined text fields but does allow loading text from a file.
void logBoardStackupWarning(const wxString &aCadstarLayerName, const PCB_LAYER_ID &aKiCadLayer)
virtual void Rotate(const wxPoint &aRotCentre, double aAngle) override
Rotate this object.
Definition: pcb_shape.cpp:241
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
void loadLibraryPads(const SYMDEF_PCB &aComponent, FOOTPRINT *aFootprint)
virtual bool IsLocked() const
Definition: board_item.cpp:78
int PointCount() const
Return the number of points (vertices) in this line chain.
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
void SetPriority(unsigned aPriority)
Definition: zone.h:117
std::map< LAYERPAIR_ID, LAYERPAIR > LayerPairs
Default vias to use between pairs of layers.
void SetExtensionOffset(int aOffset)
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:589
Describes an imported layer and how it could be mapped to KiCad Layers.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: zone.h:180
long Thickness
Note: Units of length are defined in file header.
POINT Origin
Origin of the component (this is used as the reference point when placing the component in the design...
long MaxPhysicalLayer
Should equal number of copper layers.
std::map< TEMPLATE_ID, TEMPLATE > Templates
wxString Name
This name can be different to the PART name.
ALIGNMENT Alignment
In CADSTAR The default alignment for a TEXT object (when "(No Alignment()" is selected) Bottom Left o...
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
PADS & Pads()
Definition: footprint.h:168
Plated through hole pad.
This file contains miscellaneous commonly used macros and functions.
UNITS Units
Units to display for linear dimensions.
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:466
void SetLineThickness(int aWidth)
void SetDescription(const wxString &aDoc)
Definition: footprint.h:198
For better understanding of the points that make a dimension:
wxString HatchCodeID
Only Applicable for HATCHED Type.
std::vector< FOOTPRINT * > GetLoadedLibraryFootpints() const
Return a copy of the loaded library footprints (caller owns the objects)
void SetIsRuleArea(bool aEnable)
Definition: zone.h:742
FP_TEXT & Reference()
Definition: footprint.h:467
std::set< TEXT_FIELD_NAME > InconsistentTextFields
Text fields need to be updated in CADSTAR and it is possible that they are not consistent across text...
From CADSTAR Help: "Area is for creating areas within which, and nowhere else, certain operations are...
long ReliefWidth
if undefined inherits from design
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
A shape of copper in the component footprint.
std::map< NET_ID, NET_PCB > Nets
Contains tracks and vias.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
GROUP_ID GroupID
If not empty, this FIGURE is part of a group.
void drawCadstarText(const TEXT &aCadstarText, BOARD_ITEM_CONTAINER *aContainer, const GROUP_ID &aCadstarGroupID=wxEmptyString, const LAYER_ID &aCadstarLayerOverride=wxEmptyString, const wxPoint &aMoveVector={ 0, 0 }, const double &aRotationAngle=0.0, const double &aScalingFactor=1.0, const wxPoint &aTransformCentre={ 0, 0 }, const bool &aMirrorInvert=false)
virtual void Move(const wxPoint &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:156
virtual void SetVisible(bool aVisible)
Definition: eda_text.h:185
std::map< GROUP_ID, PCB_GROUP * > m_groupMap
Map between Cadstar and KiCad groups.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT) override
Adds an item to the container.
Definition: board.cpp:607
void SetLayerName(const wxString &aName)
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
virtual const wxPoint & GetStart() const
The dimension's origin is the first feature point for the dimension.
long UnitDisplPrecision
Number of decimal points to display for linear dimensions.
like PAD_PTH, but not plated
std::map< LAYER_ID, CADSTAR_PAD_SHAPE > Reassigns
BOARD_STACKUP & GetStackupDescriptor()
void loadLibraryAreas(const SYMDEF_PCB &aComponent, FOOTPRINT *aFootprint)
PAD * getKiCadPad(const COMPONENT_PAD &aCadstarPad, FOOTPRINT *aParent)
bool m_doneNetClassWarning
Used by getKiCadNet() to avoid multiple duplicate warnings.
double getPolarAngle(const wxPoint &aPoint)
Inherits from design units (assumed Assignments->Technology->Units)
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:502
pads are covered by copper
Represents a point in x,y coordinates.
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:114
PART getPart(const PART_ID &aCadstarPartID)
Normal via.
Definition: router_tool.cpp:70
void SetHatchGap(int aStep)
Definition: zone.h:257
void Move(const VECTOR2I &aVector) override
void drawCadstarVerticesAsShapes(const std::vector< VERTEX > &aCadstarVertices, const PCB_LAYER_ID &aKiCadLayer, const int &aLineThickness, BOARD_ITEM_CONTAINER *aContainer, const GROUP_ID &aCadstarGroupID=wxEmptyString, const wxPoint &aMoveVector={ 0, 0 }, const double &aRotationAngle=0.0, const double &aScalingFactor=1.0, const wxPoint &aTransformCentre={ 0, 0 }, const bool &aMirrorInvert=false)
Uses PCB_SHAPE to draw the vertices on m_board object.
long ThermalReliefPadsAngle
Orientation for the thermal reliefs.
void initStackupItem(const LAYER &aCadstarLayer, BOARD_STACKUP_ITEM *aKiCadItem, int aDielectricSublayer)
bool AllowInNoRouting
true when subnode "IGNORETRN" is present
static const wxChar * Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition: lset.cpp:82
HATCHCODE_ID HatchCodeID
Only for FillType = HATCHED.
std::map< PAD_ID, COMPONENT_PAD > ComponentPads
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:746
void loadLibraryCoppers(const SYMDEF_PCB &aComponent, FOOTPRINT *aFootprint)
void SetClearance(int aClearance)
Definition: netclass.h:125
Represent a set of closed polygons.
std::map< PART_ID, PART > PartDefinitions
DIMENSION_ID ID
Some ID (doesn't seem to be used) subnode="DIMREF".
constexpr double PCB_IU_PER_MM
void SetVisibleLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:498
SHAPE_LINE_CHAIN & Outline(int aIndex)
bool Placement
From CADSTAR Help: "Auto Placement can place components within this area.
virtual void SetEnd(const wxPoint &aPoint)
bool isFootprint(BOARD_ITEM_CONTAINER *aContainer)
GROUP_ID GroupID
If not empty, this GROUP is part of another GROUP.
long Precision
Number of decimal points to display in the measurement [param3].
long LeaderLineLength
Only for TYPE=LEADERLINE Length of the angled part of the leader line [param5].
int loadNetVia(const NET_ID &aCadstarNetID, const NET_PCB::VIA &aCadstarVia)
Load via and return via size.
long Width
Defaults to 0 if using system fonts or, if using CADSTAR font, default to equal height (1:1 aspect ra...
Inbuilt layer type (cannot be assigned to user layers)
Loads a cpa file into a KiCad BOARD object.
void SetOrientation(double aNewAngle)
Definition: footprint.cpp:1618
std::set< PADCODE_ID > m_padcodesTested
Used by getKiCadPad() to avoid multiple duplicate warnings.
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:202
PCB_LAYER_ID getKiCadCopperLayerID(unsigned int aLayerNum, bool aDetectMaxLayer=true)
LSET PermittedLayers
KiCad layers that the imported layer can be mapped onto.
The highest PHYSICAL_LAYER_ID currently defined (i.e.
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
void SetPrefix(const wxString &aPrefix)
void Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1428
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:377
bool ThermalReliefOnPads
false when subnode "NOPINRELIEF" is present
std::map< SYMDEF_ID, SYMDEF_PCB > ComponentDefinitions
void SetExtensionHeight(int aHeight)
std::vector< COMPONENT_COPPER > ComponentCoppers
const wxString & GetNumber() const
Definition: pad.h:129
void SetStart(const wxPoint &aStart)
Definition: pcb_track.h:107
const wxString & GetReference() const
Definition: footprint.h:430
#define _(s)
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:744
void applyRouteOffset(wxPoint *aPointToOffset, const wxPoint &aRefPoint, const long &aOffsetAmount)
CADSTAR's Post Processor does an action called "Route Offset" which is applied when a route is wider ...
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:1662
SYMDEF_ID SymdefID
Normally documentation symbols only have TEXT, FIGURE and TEXT_LOCATION objects which are all drawn o...
static LSET AllLayersMask()
Definition: lset.cpp:787
void SetSuffix(const wxString &aSuffix)
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
void loadNetTracks(const NET_ID &aCadstarNetID, const NET_PCB::ROUTE &aCadstarRoute, long aStartWidth=std::numeric_limits< long >::max(), long aEndWidth=std::numeric_limits< long >::max())
int getKiCadHatchCodeThickness(const HATCHCODE_ID &aCadstarHatchcodeID)
void Load(BOARD *aBoard, PROJECT *aProject)
Loads a CADSTAR PCB Archive file into the KiCad BOARD object given.
#define PART_NAME_ATTRID
void SetMinThickness(int aMinThickness)
Definition: zone.h:245
void SetCenter(const wxPoint &aCenterPoint)
Definition: pcb_shape.h:198
void SetIsFilled(bool isFilled)
Definition: zone.h:235