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