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( wxT( "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( wxT( "White" ) );
449  }
450  else if( item->GetType() == BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_SOLDERMASK )
451  {
452  item->SetColor( wxT( "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( wxT( "glue" ) ) || layerName.Contains( wxT( "adhesive" ) ) )
548  {
549  selectLayerID( PCB_LAYER_ID::F_Adhes, PCB_LAYER_ID::B_Adhes, LOG_LEVEL::MSG );
550  }
551  else if( layerName.Contains( wxT( "silk" ) ) || layerName.Contains( wxT( "legend" ) ) )
552  {
553  selectLayerID( PCB_LAYER_ID::F_SilkS, PCB_LAYER_ID::B_SilkS, LOG_LEVEL::MSG );
554  }
555  else if( layerName.Contains( wxT( "assembly" ) ) || layerName.Contains( wxT( "fabrication" ) ) )
556  {
557  selectLayerID( PCB_LAYER_ID::F_Fab, PCB_LAYER_ID::B_Fab, LOG_LEVEL::MSG );
558  }
559  else if( layerName.Contains( wxT( "resist" ) ) || layerName.Contains( wxT( "mask" ) ) )
560  {
561  selectLayerID( PCB_LAYER_ID::F_Mask, PCB_LAYER_ID::B_Mask, LOG_LEVEL::MSG );
562  }
563  else if( layerName.Contains( wxT( "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( wxT( "Unknown CADSTAR Layer Sub-type" ) );
595  break;
596  }
597  break;
598 
599  default:
600  wxFAIL_MSG( wxT( "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( wxT( "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  wxString() );
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( wxT( "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( wxT( "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( wxT( "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( wxEmptyString );
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( wxT( "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( wxT( "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  size_t index = aCadstarPadID - (long) 1;
1255 
1256  if( !( index < aFootprint->Pads().size() ) )
1257  {
1258  THROW_IO_ERROR( wxString::Format( _( "Unable to find pad index '%d' in footprint '%s'." ),
1259  (long) aCadstarPadID,
1260  aFootprint->GetReference() ) );
1261  }
1262 
1263  return aFootprint->Pads().at( index );
1264 }
1265 
1266 
1268 {
1269  for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1270  {
1271  GROUP& csGroup = groupPair.second;
1272 
1273  PCB_GROUP* kiGroup = new PCB_GROUP( m_board );
1274 
1275  m_board->Add( kiGroup );
1276  kiGroup->SetName( csGroup.Name );
1277  kiGroup->SetLocked( csGroup.Fixed );
1278 
1279  m_groupMap.insert( { csGroup.ID, kiGroup } );
1280  }
1281 
1282  //now add any groups to their parent group
1283  for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1284  {
1285  GROUP& csGroup = groupPair.second;
1286 
1287  if( !csGroup.GroupID.IsEmpty() )
1288  {
1289  if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1290  {
1291  THROW_IO_ERROR( wxString::Format( _( "Unable to find group ID %s in the group "
1292  "definitions." ),
1293  csGroup.ID ) );
1294  }
1295  else if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1296  {
1297  THROW_IO_ERROR( wxString::Format( _( "Unable to find sub group %s in the group "
1298  "map (parent group ID=%s, Name=%s)." ),
1299  csGroup.GroupID,
1300  csGroup.ID,
1301  csGroup.Name ) );
1302  }
1303  else
1304  {
1305  PCB_GROUP* kiCadGroup = m_groupMap.at( csGroup.ID );
1306  PCB_GROUP* parentGroup = m_groupMap.at( csGroup.GroupID );
1307  parentGroup->AddItem( kiCadGroup );
1308  }
1309  }
1310  }
1311 }
1312 
1313 
1315 {
1316  for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
1317  {
1318  CADSTAR_BOARD& board = boardPair.second;
1319  GROUP_ID boardGroup = createUniqueGroupID( wxT( "Board" ) );
1321  getLineThickness( board.LineCodeID ),
1322  wxString::Format( wxT( "BOARD %s" ), board.ID ),
1323  m_board, boardGroup );
1324 
1325  if( !board.GroupID.IsEmpty() )
1326  {
1327  addToGroup( board.GroupID, getKiCadGroup( boardGroup ) );
1328  }
1329 
1330  //TODO process board attributes when KiCad supports them
1331  }
1332 }
1333 
1334 
1336 {
1337  for( std::pair<FIGURE_ID, FIGURE> figPair : Layout.Figures )
1338  {
1339  FIGURE& fig = figPair.second;
1342  wxString::Format( wxT( "FIGURE %s" ), fig.ID ),
1343  m_board, fig.GroupID );
1344 
1345  //TODO process "swaprule" (doesn't seem to apply to Layout Figures?)
1346  //TODO process re-use block when KiCad Supports it
1347  //TODO process attributes when KiCad Supports attributes in figures
1348  }
1349 }
1350 
1351 
1353 {
1354  for( std::pair<TEXT_ID, TEXT> txtPair : Layout.Texts )
1355  {
1356  TEXT& csTxt = txtPair.second;
1357  drawCadstarText( csTxt, m_board );
1358  }
1359 }
1360 
1361 
1363 {
1364  for( std::pair<DIMENSION_ID, DIMENSION> dimPair : Layout.Dimensions )
1365  {
1366  DIMENSION& csDim = dimPair.second;
1367 
1368  switch( csDim.Type )
1369  {
1370  case DIMENSION::TYPE::LINEARDIM:
1371  switch( csDim.Subtype )
1372  {
1373  case DIMENSION::SUBTYPE::ANGLED:
1374  wxLogWarning( wxString::Format( _( "Dimension ID %s is an angled dimension, which "
1375  "has no KiCad equivalent. An aligned dimension "
1376  "was loaded instead." ),
1377  csDim.ID ) );
1379  case DIMENSION::SUBTYPE::DIRECT:
1380  case DIMENSION::SUBTYPE::ORTHOGONAL:
1381  {
1382  if( csDim.Line.Style == DIMENSION::LINE::STYLE::EXTERNAL )
1383  {
1384  wxLogWarning( wxString::Format(
1385  _( "Dimension ID %s has 'External' style in CADSTAR. External "
1386  "dimension styles are not yet supported in KiCad. The dimension "
1387  "object was imported with an internal dimension style instead." ),
1388  csDim.ID ) );
1389  }
1390 
1391  PCB_DIM_ALIGNED* dimension = nullptr;
1392 
1393  if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1394  {
1395  dimension = new PCB_DIM_ORTHOGONAL( m_board );
1396  PCB_DIM_ORTHOGONAL* orDim = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1397 
1398  if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1400  else
1402  }
1403  else
1404  {
1405  dimension = new PCB_DIM_ALIGNED( m_board );
1406  }
1407 
1408  m_board->Add( dimension, ADD_MODE::APPEND );
1409  applyDimensionSettings( csDim, dimension );
1410 
1411  dimension->SetExtensionHeight(
1413 
1414  // Calculate height:
1415  wxPoint crossbarStart = getKiCadPoint( csDim.Line.Start );
1416  wxPoint crossbarEnd = getKiCadPoint( csDim.Line.End );
1417  VECTOR2I crossbarVector = crossbarEnd - crossbarStart;
1418  VECTOR2I heightVector = crossbarStart - dimension->GetStart();
1419  double height = 0.0;
1420 
1421  if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1422  {
1423  if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1424  height = heightVector.y;
1425  else
1426  height = heightVector.x;
1427  }
1428  else
1429  {
1430  double angle = crossbarVector.Angle() + ( M_PI / 2 );
1431  height = heightVector.x * cos( angle ) + heightVector.y * sin( angle );
1432  }
1433 
1434  dimension->SetHeight( height );
1435  }
1436  break;
1437 
1438  default:
1439  // Radius and diameter dimensions are LEADERDIM (even if not actually leader)
1440  // Angular dimensions are always ANGLEDIM
1441  wxLogError( _( "Unexpected Dimension type (ID %s). This was not imported." ),
1442  csDim.ID );
1443  continue;
1444  }
1445  break;
1446 
1447  case DIMENSION::TYPE::LEADERDIM:
1448  //TODO: update import when KiCad supports radius and diameter dimensions
1449 
1450  if( csDim.Line.Style == DIMENSION::LINE::STYLE::INTERNAL )
1451  {
1452  // "internal" is a simple double sided arrow from start to end (no extension lines)
1453  PCB_DIM_ALIGNED* dimension = new PCB_DIM_ALIGNED( m_board );
1454  m_board->Add( dimension, ADD_MODE::APPEND );
1455  applyDimensionSettings( csDim, dimension );
1456 
1457  // Lets set again start/end:
1458  dimension->SetStart( getKiCadPoint( csDim.Line.Start ) );
1459  dimension->SetEnd( getKiCadPoint( csDim.Line.End ) );
1460 
1461  // Do not use any extension lines:
1462  dimension->SetExtensionOffset( 0 );
1463  dimension->SetExtensionHeight( 0 );
1464  dimension->SetHeight( 0 );
1465  }
1466  else
1467  {
1468  // "external" is a "leader" style dimension
1469  PCB_DIM_LEADER* leaderDim = new PCB_DIM_LEADER( m_board );
1470  m_board->Add( leaderDim, ADD_MODE::APPEND );
1471 
1472  applyDimensionSettings( csDim, leaderDim );
1473  leaderDim->SetStart( getKiCadPoint( csDim.Line.End ) );
1474 
1475  /*
1476  * In CADSTAR, the resulting shape orientation of the leader dimension depends on
1477  * on the positions of the #Start (S) and #End (E) points as shown below. In the
1478  * diagrams below, the leader angle (angRad) is represented by HEV
1479  *
1480  * Orientation 1: (orientX = -1, | Orientation 2: (orientX = 1,
1481  * orientY = 1) | orientY = 1)
1482  * |
1483  * --------V | V----------
1484  * \ | /
1485  * \ | /
1486  * H _E/ | \E_ H
1487  * |
1488  * S | S
1489  * |
1490  *
1491  * Orientation 3: (orientX = -1, | Orientation 4: (orientX = 1,
1492  * orientY = -1) | orientY = -1)
1493  * |
1494  * S | S
1495  * _ | _
1496  * H E\ | /E H
1497  * / | \
1498  * / | \
1499  * ----------V | V-----------
1500  * |
1501  *
1502  * Corner cases:
1503  *
1504  * It is not possible to generate a leader object with start and end point being
1505  * identical. Assume Orientation 2 if start and end points are identical.
1506  *
1507  * If start and end points are aligned vertically (i.e. S.x == E.x):
1508  * - If E.y > S.y - Orientation 2
1509  * - If E.y < S.y - Orientation 4
1510  *
1511  * If start and end points are aligned horitontally (i.e. S.y == E.y):
1512  * - If E.x > S.x - Orientation 2
1513  * - If E.x < S.x - Orientation 1
1514  */
1515  double angRad = DEG2RAD( getAngleDegrees( csDim.Line.LeaderAngle ) );
1516 
1517  double orientX = 1;
1518  double orientY = 1;
1519 
1520  if( csDim.Line.End.x >= csDim.Line.Start.x )
1521  {
1522  if( csDim.Line.End.y >= csDim.Line.Start.y )
1523  {
1524  //Orientation 2
1525  orientX = 1;
1526  orientY = 1;
1527  }
1528  else
1529  {
1530  //Orientation 4
1531  orientX = 1;
1532  orientY = -1;
1533  }
1534  }
1535  else
1536  {
1537  if( csDim.Line.End.y >= csDim.Line.Start.y )
1538  {
1539  //Orientation 1
1540  orientX = -1;
1541  orientY = 1;
1542  }
1543  else
1544  {
1545  //Orientation 3
1546  orientX = -1;
1547  orientY = -1;
1548  }
1549  }
1550 
1551  wxPoint endOffset( csDim.Line.LeaderLineLength * cos( angRad ) * orientX,
1552  csDim.Line.LeaderLineLength * sin( angRad ) * orientY );
1553 
1554  wxPoint endPoint = csDim.Line.End + endOffset;
1555  wxPoint txtPoint( endPoint.x + ( csDim.Line.LeaderLineExtensionLength * orientX ),
1556  endPoint.y );
1557 
1558  leaderDim->SetEnd( getKiCadPoint( endPoint ) );
1559  leaderDim->Text().SetTextPos( getKiCadPoint( txtPoint ) );
1560  leaderDim->SetText( ParseTextFields( csDim.Text.Text, &m_context ) );
1561  leaderDim->SetPrefix( wxEmptyString );
1562  leaderDim->SetSuffix( wxEmptyString );
1564 
1565  if( orientX == 1 )
1567  else
1568  leaderDim->Text().SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1569 
1570  leaderDim->SetExtensionOffset( 0 );
1571  }
1572  break;
1573 
1574  case DIMENSION::TYPE::ANGLEDIM:
1575  //TODO: update import when KiCad supports angular dimensions
1576  wxLogError( _( "Dimension %s is an angular dimension which has no KiCad equivalent. "
1577  "The object was not imported." ),
1578  csDim.ID );
1579  break;
1580  }
1581  }
1582 }
1583 
1584 
1586 {
1587  for( std::pair<AREA_ID, AREA> areaPair : Layout.Areas )
1588  {
1589  AREA& area = areaPair.second;
1590 
1591  if( area.NoVias || area.NoTracks || area.Keepout || area.Routing )
1592  {
1593  int lineThickness = 0; // CADSTAR areas only use the line width for display purpose
1594  ZONE* zone = getZoneFromCadstarShape( area.Shape, lineThickness, m_board );
1595 
1596  m_board->Add( zone, ADD_MODE::APPEND );
1597 
1598  if( isLayerSet( area.LayerID ) )
1599  zone->SetLayerSet( getKiCadLayerSet( area.LayerID ) );
1600  else
1601  zone->SetLayer( getKiCadLayer( area.LayerID ) );
1602 
1603  zone->SetIsRuleArea( true ); //import all CADSTAR areas as Keepout zones
1604  zone->SetDoNotAllowPads( false ); //no CADSTAR equivalent
1605  zone->SetZoneName( area.Name );
1606 
1607  zone->SetDoNotAllowFootprints( area.Keepout );
1608 
1609  zone->SetDoNotAllowTracks( area.NoTracks );
1610  zone->SetDoNotAllowCopperPour( area.NoTracks );
1611 
1612  zone->SetDoNotAllowVias( area.NoVias );
1613 
1614  if( area.Placement )
1615  {
1616  wxLogWarning( wxString::Format( _( "The CADSTAR area '%s' is marked as a placement "
1617  "area in CADSTAR. Placement areas are not "
1618  "supported in KiCad. Only the supported elements "
1619  "for the area were imported." ),
1620  area.Name ) );
1621  }
1622  }
1623  else
1624  {
1625  wxLogError( wxString::Format( _( "The CADSTAR area '%s' does not have a KiCad "
1626  "equivalent. Pure Placement areas are not supported." ),
1627  area.Name ) );
1628  }
1629 
1630  //todo Process area.AreaHeight when KiCad supports 3D design rules
1631  //TODO process attributes
1632  //TODO process addition to a group
1633  //TODO process "swaprule"
1634  //TODO process re-use block
1635  }
1636 }
1637 
1638 
1640 {
1641  for( std::pair<COMPONENT_ID, COMPONENT> compPair : Layout.Components )
1642  {
1643  COMPONENT& comp = compPair.second;
1644 
1645  if( !comp.VariantID.empty() && comp.VariantParentComponentID != comp.ID )
1646  continue; // Only load master Variant
1647 
1648  auto fpIter = m_libraryMap.find( comp.SymdefID );
1649 
1650  if( fpIter == m_libraryMap.end() )
1651  {
1652  THROW_IO_ERROR( wxString::Format( _( "Unable to find component '%s' in the library"
1653  "(Symdef ID: '%s')" ),
1654  comp.Name,
1655  comp.SymdefID ) );
1656  }
1657 
1658  FOOTPRINT* libFootprint = fpIter->second;
1659 
1660  // Use Duplicate() to ensure unique KIID for all objects
1661  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( libFootprint->Duplicate() );
1662 
1663  m_board->Add( footprint, ADD_MODE::APPEND );
1664 
1665  // First lets fix the pad names on the footprint.
1666  // CADSTAR defines the pad name in the PART definition and the SYMDEF (i.e. the PCB
1667  // footprint definition) uses a numerical sequence. COMP is the only object that has
1668  // visibility of both the SYMDEF and PART.
1669  if( Parts.PartDefinitions.find( comp.PartID ) != Parts.PartDefinitions.end() )
1670  {
1671  PART part = Parts.PartDefinitions.at( comp.PartID );
1672 
1673  // Only do this when the number of pins in the part definition equals the number of
1674  // pads in the footprint.
1675  if( part.Definition.Pins.size() == footprint->Pads().size() )
1676  {
1677  for( std::pair<PART_DEFINITION_PIN_ID, PART::DEFINITION::PIN> pinPair :
1678  part.Definition.Pins )
1679  {
1680  PART::DEFINITION::PIN pin = pinPair.second;
1681  wxString pinName = pin.Name;
1682 
1683  if( pinName.empty() )
1684  pinName = pin.Identifier;
1685 
1686  if( pinName.empty() )
1687  pinName = wxString::Format( wxT( "%ld" ), pin.ID );
1688 
1689  getPadReference( footprint, pin.ID )->SetNumber( pinName );
1690  }
1691  }
1692  }
1693 
1694  //Override pads with pad exceptions
1695  if( comp.PadExceptions.size() > 0 )
1696  {
1697  SYMDEF_PCB fpLibEntry = Library.ComponentDefinitions.at( comp.SymdefID );
1698 
1699  for( std::pair<PAD_ID, PADEXCEPTION> padPair : comp.PadExceptions )
1700  {
1701  PADEXCEPTION& padEx = padPair.second;
1702  COMPONENT_PAD csPad = fpLibEntry.ComponentPads.at( padPair.first );
1703 
1704  if( !padEx.PadCode.IsEmpty() )
1705  csPad.PadCodeID = padEx.PadCode;
1706 
1707  if( padEx.OverrideExits )
1708  csPad.Exits = padEx.Exits;
1709 
1710  if( padEx.OverrideOrientation )
1711  csPad.OrientAngle = padEx.OrientAngle;
1712 
1713  if( padEx.OverrideSide )
1714  csPad.Side = padEx.Side;
1715 
1716  // Find the pad in the footprint definition
1717  PAD* kiPad = getPadReference( footprint, padEx.ID );
1718 
1719  wxString padNumber = kiPad->GetNumber();
1720 
1721  delete kiPad;
1722  kiPad = getKiCadPad( csPad, footprint );
1723  kiPad->SetNumber( padNumber );
1724 
1725  // Change the pointer in the footprint to the newly created pad
1726  getPadReference( footprint, padEx.ID ) = kiPad;
1727  }
1728  }
1729 
1730  //set to empty string to avoid duplication when loading attributes:
1731  footprint->SetValue( wxEmptyString );
1732 
1733  footprint->SetPosition( getKiCadPoint( comp.Origin ) );
1734  footprint->SetOrientation( getAngleTenthDegree( comp.OrientAngle ) );
1735  footprint->SetReference( comp.Name );
1736 
1737  if( comp.Mirror )
1738  {
1739  double mirroredAngle = - getAngleTenthDegree( comp.OrientAngle );
1740  NORMALIZE_ANGLE_180( mirroredAngle );
1741  footprint->SetOrientation( mirroredAngle );
1742  footprint->Flip( getKiCadPoint( comp.Origin ), true );
1743  }
1744 
1745  loadComponentAttributes( comp, footprint );
1746 
1747  if( !comp.PartID.IsEmpty() && comp.PartID != wxT( "NO_PART" ) )
1748  footprint->SetDescription( getPart( comp.PartID ).Definition.Name );
1749 
1750  m_componentMap.insert( { comp.ID, footprint } );
1751  }
1752 }
1753 
1754 
1756 {
1757  //No KiCad equivalent. Loaded as graphic and text elements instead
1758 
1759  for( std::pair<DOCUMENTATION_SYMBOL_ID, DOCUMENTATION_SYMBOL> docPair :
1761  {
1762  DOCUMENTATION_SYMBOL& docSymInstance = docPair.second;
1763 
1764 
1765  auto docSymIter = Library.ComponentDefinitions.find( docSymInstance.SymdefID );
1766 
1767  if( docSymIter == Library.ComponentDefinitions.end() )
1768  {
1769  THROW_IO_ERROR( wxString::Format( _( "Unable to find documentation symbol in the "
1770  "library (Symdef ID: '%s')" ),
1771  docSymInstance.SymdefID ) );
1772  }
1773 
1774  SYMDEF_PCB& docSymDefinition = ( *docSymIter ).second;
1775  wxPoint moveVector =
1776  getKiCadPoint( docSymInstance.Origin ) - getKiCadPoint( docSymDefinition.Origin );
1777  double rotationAngle = getAngleTenthDegree( docSymInstance.OrientAngle );
1778  double scalingFactor = (double) docSymInstance.ScaleRatioNumerator
1779  / (double) docSymInstance.ScaleRatioDenominator;
1780  wxPoint centreOfTransform = getKiCadPoint( docSymDefinition.Origin );
1781  bool mirrorInvert = docSymInstance.Mirror;
1782 
1783  //create a group to store the items in
1784  wxString groupName = docSymDefinition.ReferenceName;
1785 
1786  if( !docSymDefinition.Alternate.IsEmpty() )
1787  groupName += wxT( " (" ) + docSymDefinition.Alternate + wxT( ")" );
1788 
1789  GROUP_ID groupID = createUniqueGroupID( groupName );
1790 
1791  LSEQ layers = getKiCadLayerSet( docSymInstance.LayerID ).Seq();
1792 
1793  for( PCB_LAYER_ID layer : layers )
1794  {
1795  for( std::pair<FIGURE_ID, FIGURE> figPair : docSymDefinition.Figures )
1796  {
1797  FIGURE fig = figPair.second;
1798  drawCadstarShape( fig.Shape, layer, getLineThickness( fig.LineCodeID ),
1799  wxString::Format( wxT( "DOCUMENTATION SYMBOL %s, FIGURE %s" ),
1800  docSymDefinition.ReferenceName, fig.ID ),
1801  m_board, groupID, moveVector, rotationAngle, scalingFactor,
1802  centreOfTransform, mirrorInvert );
1803  }
1804  }
1805 
1806  for( std::pair<TEXT_ID, TEXT> textPair : docSymDefinition.Texts )
1807  {
1808  TEXT txt = textPair.second;
1809  drawCadstarText( txt, m_board, groupID, docSymInstance.LayerID, moveVector,
1810  rotationAngle, scalingFactor, centreOfTransform, mirrorInvert );
1811  }
1812  }
1813 }
1814 
1815 
1817 {
1818  for( std::pair<TEMPLATE_ID, TEMPLATE> tempPair : Layout.Templates )
1819  {
1820  TEMPLATE& csTemplate = tempPair.second;
1821 
1822  int zonelinethickness = 0; // The line thickness in CADSTAR is only for display purposes but
1823  // does not affect the end copper result.
1824  ZONE* zone = getZoneFromCadstarShape( csTemplate.Shape, zonelinethickness, m_board );
1825 
1826  m_board->Add( zone, ADD_MODE::APPEND );
1827 
1828  zone->SetZoneName( csTemplate.Name );
1829  zone->SetLayer( getKiCadLayer( csTemplate.LayerID ) );
1830  zone->SetPriority( 1 ); // initially 1, we will increase in calculateZonePriorities
1831 
1832  if( !( csTemplate.NetID.IsEmpty() || csTemplate.NetID == wxT( "NONE" ) ) )
1833  zone->SetNet( getKiCadNet( csTemplate.NetID ) );
1834 
1835  if( csTemplate.Pouring.AllowInNoRouting )
1836  {
1837  wxLogWarning( wxString::Format(
1838  _( "The CADSTAR template '%s' has the setting 'Allow in No Routing Areas' "
1839  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1840  csTemplate.Name ) );
1841  }
1842 
1843  if( csTemplate.Pouring.BoxIsolatedPins )
1844  {
1845  wxLogWarning( wxString::Format(
1846  _( "The CADSTAR template '%s' has the setting 'Box Isolated Pins' "
1847  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1848  csTemplate.Name ) );
1849  }
1850 
1851  if( csTemplate.Pouring.AutomaticRepour )
1852  {
1853  wxLogWarning( wxString::Format(
1854  _( "The CADSTAR template '%s' has the setting 'Automatic Repour' "
1855  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1856  csTemplate.Name ) );
1857  }
1858 
1859  // Sliver width has different behaviour to KiCad Zone's minimum thickness
1860  // In Cadstar 'Sliver width' has to be greater than the Copper thickness, whereas in
1861  // Kicad it is the opposite.
1862  if( csTemplate.Pouring.SliverWidth != 0 )
1863  {
1864  wxLogWarning( wxString::Format(
1865  _( "The CADSTAR template '%s' has a non-zero value defined for the "
1866  "'Sliver Width' setting. There is no KiCad equivalent for "
1867  "this, so this setting was ignored." ),
1868  csTemplate.Name ) );
1869  }
1870 
1871 
1872  if( csTemplate.Pouring.MinIsolatedCopper != csTemplate.Pouring.MinDisjointCopper )
1873  {
1874  wxLogWarning( wxString::Format(
1875  _( "The CADSTAR template '%s' has different settings for 'Retain Poured Copper "
1876  "- Disjoint' and 'Retain Poured Copper - Isolated'. KiCad does not "
1877  "distinguish between these two settings. The setting for disjoint copper "
1878  "has been applied as the minimum island area of the KiCad Zone." ),
1879  csTemplate.Name ) );
1880  }
1881 
1882  long long minIslandArea = -1;
1883 
1884  if( csTemplate.Pouring.MinDisjointCopper != UNDEFINED_VALUE )
1885  {
1886  minIslandArea = (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper )
1887  * (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper );
1888 
1890  }
1891  else
1892  {
1894  }
1895 
1896  zone->SetMinIslandArea( minIslandArea );
1897 
1898  // In cadstar zone clearance is in addition to the global clearance.
1899  // TODO: need to create custom rules for individual items: zone to pad, zone to track, etc.
1900  int clearance = getKiCadLength( csTemplate.Pouring.AdditionalIsolation );
1901  clearance += m_board->GetDesignSettings().m_MinClearance;
1902 
1903  zone->SetLocalClearance( clearance );
1904 
1905  COPPERCODE pouringCopperCode = getCopperCode( csTemplate.Pouring.CopperCodeID );
1906  int minThickness = getKiCadLength( pouringCopperCode.CopperWidth );
1907  zone->SetMinThickness( minThickness );
1908 
1909  if( csTemplate.Pouring.FillType == TEMPLATE::POURING::COPPER_FILL_TYPE::HATCHED )
1910  {
1912  zone->SetHatchGap( getKiCadHatchCodeGap( csTemplate.Pouring.HatchCodeID ) );
1915  }
1916  else
1917  {
1919  }
1920 
1921  if( csTemplate.Pouring.ThermalReliefOnPads != csTemplate.Pouring.ThermalReliefOnVias
1922  || csTemplate.Pouring.ThermalReliefPadsAngle
1923  != csTemplate.Pouring.ThermalReliefViasAngle )
1924  {
1925  wxLogWarning( wxString::Format(
1926  _( "The CADSTAR template '%s' has different settings for thermal relief "
1927  "in pads and vias. KiCad only supports one single setting for both. The "
1928  "setting for pads has been applied." ),
1929  csTemplate.Name ) );
1930  }
1931 
1932  COPPERCODE reliefCopperCode = getCopperCode( csTemplate.Pouring.ReliefCopperCodeID );
1933  int spokeWidth = getKiCadLength( reliefCopperCode.CopperWidth );
1934  int reliefWidth = getKiCadLength( csTemplate.Pouring.ClearanceWidth );
1935 
1936  // Cadstar supports having a spoke width thinner than the minimum thickness of the zone, but
1937  // this is not permitted in KiCad. We load it as solid fill instead.
1938  if( csTemplate.Pouring.ThermalReliefOnPads && reliefWidth > 0 )
1939  {
1940  if( spokeWidth < minThickness )
1941  {
1942  wxLogWarning( wxString::Format(
1943  _( "The CADSTAR template '%s' has thermal reliefs in the original design "
1944  "but the spoke width (%.2f mm) is thinner than the minimum thickness of "
1945  "the zone (%.2f mm). KiCad requires the minimum thickness of the zone "
1946  "to be preserved. Therefore the minimum thickness has been applied as "
1947  "the new spoke width and will be applied next time the zones are "
1948  "filled." ),
1949  csTemplate.Name, (double) getKiCadLength( spokeWidth ) / 1E6,
1950  (double) getKiCadLength( minThickness ) / 1E6 ) );
1951 
1952  spokeWidth = minThickness;
1953  }
1954 
1955  zone->SetThermalReliefGap( reliefWidth );
1956  zone->SetThermalReliefSpokeWidth( spokeWidth );
1958  }
1959  else
1960  {
1962  }
1963 
1964  m_zonesMap.insert( { csTemplate.ID, zone } );
1965  }
1966 
1967  //Now create power plane layers:
1968  for( LAYER_ID layer : m_powerPlaneLayers )
1969  {
1970  wxASSERT(
1971  Assignments.Layerdefs.Layers.find( layer ) != Assignments.Layerdefs.Layers.end() );
1972 
1973  //The net name will equal the layer name
1974  wxString powerPlaneLayerName = Assignments.Layerdefs.Layers.at( layer ).Name;
1975  NET_ID netid = wxEmptyString;
1976 
1977  for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
1978  {
1979  NET_PCB net = netPair.second;
1980 
1981  if( net.Name == powerPlaneLayerName )
1982  {
1983  netid = net.ID;
1984  break;
1985  }
1986  }
1987 
1988  if( netid.IsEmpty() )
1989  {
1990  wxLogError( _( "The CADSTAR layer '%s' is defined as a power plane layer. However no "
1991  "net with such name exists. The layer has been loaded but no copper "
1992  "zone was created." ),
1993  powerPlaneLayerName );
1994  }
1995  else
1996  {
1997  for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
1998  {
1999  //create a zone in each board shape
2001  CADSTAR_BOARD& board = boardPair.second;
2002  int defaultLineThicknesss = bds.GetLineThickness( PCB_LAYER_ID::Edge_Cuts );
2003  ZONE* zone = getZoneFromCadstarShape( board.Shape, defaultLineThicknesss, m_board );
2004 
2005  m_board->Add( zone, ADD_MODE::APPEND );
2006 
2007  zone->SetZoneName( powerPlaneLayerName );
2008  zone->SetLayer( getKiCadLayer( layer ) );
2011  zone->SetMinIslandArea( -1 );
2012  zone->SetPriority( 0 ); // Priority always 0 (lowest priority) for implied power planes.
2013  zone->SetNet( getKiCadNet( netid ) );
2014  }
2015  }
2016  }
2017 }
2018 
2019 
2021 {
2022  for( std::pair<COPPER_ID, COPPER> copPair : Layout.Coppers )
2023  {
2024  COPPER& csCopper = copPair.second;
2025 
2026  checkPoint();
2027 
2028  if( !csCopper.PouredTemplateID.IsEmpty() )
2029  {
2030  ZONE* pouredZone = m_zonesMap.at( csCopper.PouredTemplateID );
2031  SHAPE_POLY_SET rawPolys;
2032 
2033  int copperWidth = getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth );
2034 
2035  if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE )
2036  {
2037  // This is usually for themal reliefs. They are lines of copper with a thickness.
2038  // We convert them to an oval in most cases, but handle also the possibility of
2039  // encountering arcs in here.
2040 
2041  std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( csCopper.Shape.Vertices );
2042 
2043  for( PCB_SHAPE* shape : outlineShapes )
2044  {
2045  SHAPE_POLY_SET poly;
2046 
2047  if( shape->GetShape() == SHAPE_T::ARC )
2048  {
2049  TransformArcToPolygon( poly, shape->GetStart(), shape->GetArcMid(),
2050  shape->GetEnd(), copperWidth, ARC_HIGH_DEF,
2052  }
2053  else
2054  {
2055  TransformOvalToPolygon( poly, shape->GetStart(), shape->GetEnd(),
2056  copperWidth, ARC_HIGH_DEF,
2058  }
2059 
2060  poly.ClearArcs();
2062  }
2063 
2064  }
2065  else
2066  {
2067  rawPolys = getPolySetFromCadstarShape( csCopper.Shape, -1 );
2068  rawPolys.ClearArcs();
2069  rawPolys.Inflate( copperWidth / 2, 32 );
2070  }
2071 
2072  if( pouredZone->HasFilledPolysForLayer( getKiCadLayer( csCopper.LayerID ) ) )
2073  {
2074  rawPolys.BooleanAdd( pouredZone->RawPolysList( getKiCadLayer( csCopper.LayerID )),
2076  }
2077 
2078  SHAPE_POLY_SET finalPolys = rawPolys;
2080 
2081  pouredZone->SetFillVersion( 6 );
2082  pouredZone->SetRawPolysList( getKiCadLayer( csCopper.LayerID ), rawPolys );
2083  pouredZone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), finalPolys );
2084  pouredZone->SetIsFilled( true );
2085  pouredZone->SetNeedRefill( false );
2086  continue;
2087  }
2088 
2089  // For now we are going to load coppers to a KiCad zone however this isn't perfect
2090  //TODO: Load onto a graphical polygon with a net (when KiCad has this feature)
2091 
2092  if( !m_doneCopperWarning )
2093  {
2094  wxLogWarning(
2095  _( "The CADSTAR design contains COPPER elements, which have no direct KiCad "
2096  "equivalent. These have been imported as a KiCad Zone if solid or hatch "
2097  "filled, or as a KiCad Track if the shape was an unfilled outline (open or "
2098  "closed)." ) );
2099  m_doneCopperWarning = true;
2100  }
2101 
2102 
2103  if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE
2104  || csCopper.Shape.Type == SHAPE_TYPE::OUTLINE )
2105  {
2106  std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( csCopper.Shape.Vertices );
2107 
2108  std::vector<PCB_TRACK*> outlineTracks = makeTracksFromShapes( outlineShapes, m_board,
2109  getKiCadNet( csCopper.NetRef.NetID ),
2110  getKiCadLayer( csCopper.LayerID ),
2112 
2113  //cleanup
2114  for( PCB_SHAPE* shape : outlineShapes )
2115  delete shape;
2116 
2117  for( CUTOUT cutout : csCopper.Shape.Cutouts )
2118  {
2119  std::vector<PCB_SHAPE*> cutoutShapes = getShapesFromVertices( cutout.Vertices );
2120 
2121  std::vector<PCB_TRACK*> cutoutTracks = makeTracksFromShapes( cutoutShapes, m_board,
2122  getKiCadNet( csCopper.NetRef.NetID ),
2123  getKiCadLayer( csCopper.LayerID ),
2125 
2126  //cleanup
2127  for( PCB_SHAPE* shape : cutoutShapes )
2128  delete shape;
2129  }
2130  }
2131  else
2132  {
2133  ZONE* zone = getZoneFromCadstarShape( csCopper.Shape,
2135  m_board );
2136 
2137  m_board->Add( zone, ADD_MODE::APPEND );
2138 
2139  zone->SetZoneName( csCopper.ID );
2140  zone->SetLayer( getKiCadLayer( csCopper.LayerID ) );
2142 
2143  if( csCopper.Shape.Type == SHAPE_TYPE::HATCHED )
2144  {
2146  zone->SetHatchGap( getKiCadHatchCodeGap( csCopper.Shape.HatchCodeID ) );
2149  }
2150  else
2151  {
2153  }
2154 
2157  zone->SetNet( getKiCadNet( csCopper.NetRef.NetID ) );
2158  zone->SetPriority( m_zonesMap.size() + 1 ); // Highest priority (always fill first)
2159  zone->SetRawPolysList( getKiCadLayer( csCopper.LayerID ), *zone->Outline() );
2160 
2161  SHAPE_POLY_SET fillePolys( *zone->Outline() );
2162  fillePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
2163 
2164  zone->SetFillVersion( 6 );
2165  zone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fillePolys );
2166  }
2167  }
2168 }
2169 
2170 
2172 {
2173  for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
2174  {
2175  NET_PCB net = netPair.second;
2176  wxString netnameForErrorReporting = net.Name;
2177 
2178  std::map<NETELEMENT_ID, long> netelementSizes;
2179 
2180  if( netnameForErrorReporting.IsEmpty() )
2181  netnameForErrorReporting = wxString::Format( wxT( "$%ld" ), net.SignalNum );
2182 
2183  for( std::pair<NETELEMENT_ID, NET_PCB::VIA> viaPair : net.Vias )
2184  {
2185  NET_PCB::VIA via = viaPair.second;
2186 
2187  // viasize is used for calculating route offset (as done in CADSTAR post processor)
2188  int viaSize = loadNetVia( net.ID, via );
2189  netelementSizes.insert( { viaPair.first, viaSize } );
2190  }
2191 
2192  for( std::pair<NETELEMENT_ID, NET_PCB::PIN> pinPair : net.Pins )
2193  {
2194  NET_PCB::PIN pin = pinPair.second;
2195  FOOTPRINT* footprint = getFootprintFromCadstarID( pin.ComponentID );
2196 
2197  if( footprint == nullptr )
2198  {
2199  wxLogWarning( wxString::Format(
2200  _( "The net '%s' references component ID '%s' which does not exist. "
2201  "This has been ignored." ),
2202  netnameForErrorReporting, pin.ComponentID ) );
2203  }
2204  else if( ( pin.PadID - (long) 1 ) > footprint->Pads().size() )
2205  {
2206  wxLogWarning( wxString::Format( _( "The net '%s' references non-existent pad index"
2207  " '%d' in component '%s'. This has been "
2208  "ignored." ),
2209  netnameForErrorReporting,
2210  pin.PadID,
2211  footprint->GetReference() ) );
2212  }
2213  else
2214  {
2215  // The below works because we have added the pads in the correct order to the
2216  // footprint and the PAD_ID in Cadstar is a sequential, numerical ID
2217  PAD* pad = getPadReference( footprint, pin.PadID );
2218  pad->SetNet( getKiCadNet( net.ID ) );
2219 
2220  // also set the net to any copper pads (i.e. copper elements that we have imported
2221  // as pads instead:
2222  SYMDEF_ID symdefid = Layout.Components.at( pin.ComponentID ).SymdefID;
2223 
2224  if( m_librarycopperpads.find( symdefid ) != m_librarycopperpads.end() )
2225  {
2226  ASSOCIATED_COPPER_PADS assocPads = m_librarycopperpads.at( symdefid );
2227 
2228  if( assocPads.find( pin.PadID ) != assocPads.end() )
2229  {
2230  for( PAD_ID copperPadID : assocPads.at( pin.PadID ) )
2231  {
2232  PAD* copperpad = getPadReference( footprint, copperPadID );
2233  copperpad->SetNet( getKiCadNet( net.ID ) );
2234  }
2235  }
2236  }
2237 
2238  // padsize is used for calculating route offset (as done in CADSTAR post processor)
2239  int padsize = std::min( pad->GetSizeX(), pad->GetSizeY() );
2240  netelementSizes.insert( { pinPair.first, padsize } );
2241  }
2242  }
2243 
2244  // For junction points we need to find out the biggest size of the other routes connecting
2245  // at the junction in order to correctly apply the same "route offset" operation that the
2246  // CADSTAR post processor applies when generating Manufacturing output. The only exception
2247  // is if there is just a single route at the junction point, we use that route width
2248  auto getJunctionSize =
2249  [&]( NETELEMENT_ID aJptNetElemId, const NET_PCB::CONNECTION_PCB& aConnectionToIgnore ) -> int
2250  {
2251  int jptsize = 0;
2252 
2253  for( NET_PCB::CONNECTION_PCB connection : net.Connections )
2254  {
2255  if( connection.Route.RouteVertices.size() == 0 )
2256  continue;
2257 
2258  if( connection.StartNode == aConnectionToIgnore.StartNode
2259  && connection.EndNode == aConnectionToIgnore.EndNode )
2260  {
2261  continue;
2262  }
2263 
2264  if( connection.StartNode == aJptNetElemId )
2265  {
2266  int s = getKiCadLength( connection.Route.RouteVertices.front().RouteWidth );
2267  jptsize = std::max( jptsize, s );
2268  }
2269  else if( connection.EndNode == aJptNetElemId )
2270  {
2271  int s = getKiCadLength( connection.Route.RouteVertices.back().RouteWidth );
2272  jptsize = std::max( jptsize, s );
2273  }
2274  }
2275 
2276  if( jptsize == 0 )
2277  {
2278  // aConnectionToIgnore is actually the only one that has a route, so lets use that
2279  // to determine junction size
2280  NET_PCB::ROUTE_VERTEX vertex = aConnectionToIgnore.Route.RouteVertices.front();
2281 
2282  if( aConnectionToIgnore.EndNode == aJptNetElemId )
2283  vertex = aConnectionToIgnore.Route.RouteVertices.back();
2284 
2285  jptsize = getKiCadLength( vertex.RouteWidth );
2286  }
2287 
2288  return jptsize;
2289  };
2290 
2291  for( NET_PCB::CONNECTION_PCB connection : net.Connections )
2292  {
2293  int startSize = std::numeric_limits<int>::max();
2294  int endSize = std::numeric_limits<int>::max();
2295 
2296  if( netelementSizes.find( connection.StartNode ) != netelementSizes.end() )
2297  startSize = netelementSizes.at( connection.StartNode );
2298  else if( net.Junctions.find( connection.StartNode ) != net.Junctions.end() )
2299  startSize = getJunctionSize( connection.StartNode, connection );
2300 
2301  if( netelementSizes.find( connection.EndNode ) != netelementSizes.end() )
2302  endSize = netelementSizes.at( connection.EndNode );
2303  else if( net.Junctions.find( connection.EndNode ) != net.Junctions.end() )
2304  endSize = getJunctionSize( connection.EndNode, connection );
2305 
2306  startSize /= KiCadUnitMultiplier;
2307  endSize /= KiCadUnitMultiplier;
2308 
2309  if( !connection.Unrouted )
2310  loadNetTracks( net.ID, connection.Route, startSize, endSize );
2311  }
2312  }
2313 }
2314 
2315 
2317 {
2318  auto findAndReplaceTextField =
2319  [&]( TEXT_FIELD_NAME aField, wxString aValue )
2320  {
2321  if( m_context.TextFieldToValuesMap.find( aField ) !=
2323  {
2324  if( m_context.TextFieldToValuesMap.at( aField ) != aValue )
2325  {
2326  m_context.TextFieldToValuesMap.at( aField ) = aValue;
2327  m_context.InconsistentTextFields.insert( aField );
2328  return false;
2329  }
2330  }
2331  else
2332  {
2333  m_context.TextFieldToValuesMap.insert( { aField, aValue } );
2334  }
2335 
2336  return true;
2337  };
2338 
2339  if( m_project )
2340  {
2341  std::map<wxString, wxString>& txtVars = m_project->GetTextVars();
2342 
2343  // Most of the design text fields can be derived from other elements
2344  if( Layout.VariantHierarchy.Variants.size() > 0 )
2345  {
2346  VARIANT loadedVar = Layout.VariantHierarchy.Variants.begin()->second;
2347 
2348  findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_NAME, loadedVar.Name );
2349  findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_DESCRIPTION, loadedVar.Description );
2350  }
2351 
2352  findAndReplaceTextField( TEXT_FIELD_NAME::DESIGN_TITLE, Header.JobTitle );
2353 
2354  for( std::pair<TEXT_FIELD_NAME, wxString> txtvalue : m_context.TextFieldToValuesMap )
2355  {
2356  wxString varName = CADSTAR_TO_KICAD_FIELDS.at( txtvalue.first );
2357  wxString varValue = txtvalue.second;
2358 
2359  txtVars.insert( { varName, varValue } );
2360  }
2361 
2362  for( std::pair<wxString, wxString> txtvalue : m_context.FilenamesToTextMap )
2363  {
2364  wxString varName = txtvalue.first;
2365  wxString varValue = txtvalue.second;
2366 
2367  txtVars.insert( { varName, varValue } );
2368  }
2369  }
2370  else
2371  {
2372  wxLogError( _( "Text Variables could not be set as there is no project loaded." ) );
2373  }
2374 }
2375 
2376 
2378  FOOTPRINT* aFootprint )
2379 {
2380  for( std::pair<ATTRIBUTE_ID, ATTRIBUTE_VALUE> attrPair : aComponent.AttributeValues )
2381  {
2382  ATTRIBUTE_VALUE& attrval = attrPair.second;
2383 
2384  if( attrval.HasLocation ) //only import attributes with location. Ignore the rest
2385  {
2386  addAttribute( attrval.AttributeLocation, attrval.AttributeID, aFootprint,
2387  attrval.Value );
2388  }
2389  }
2390 
2391  for( std::pair<ATTRIBUTE_ID, TEXT_LOCATION> textlocPair : aComponent.TextLocations )
2392  {
2393  TEXT_LOCATION& textloc = textlocPair.second;
2394  wxString attrval;
2395 
2396  if( textloc.AttributeID == COMPONENT_NAME_ATTRID )
2397  {
2398  attrval = wxEmptyString; // Designator is loaded separately
2399  }
2400  else if( textloc.AttributeID == COMPONENT_NAME_2_ATTRID )
2401  {
2402  attrval = wxT( "${REFERENCE}" );
2403  }
2404  else if( textloc.AttributeID == PART_NAME_ATTRID )
2405  {
2406  attrval = getPart( aComponent.PartID ).Name;
2407  }
2408  else
2409  attrval = getAttributeValue( textloc.AttributeID, aComponent.AttributeValues );
2410 
2411  addAttribute( textloc, textloc.AttributeID, aFootprint, attrval );
2412  }
2413 }
2414 
2415 
2417  const NET_PCB::ROUTE& aCadstarRoute,
2418  long aStartWidth, long aEndWidth )
2419 {
2420  if( aCadstarRoute.RouteVertices.size() == 0 )
2421  return;
2422 
2423  std::vector<PCB_SHAPE*> shapes;
2424  std::vector<NET_PCB::ROUTE_VERTEX> routeVertices = aCadstarRoute.RouteVertices;
2425 
2426  // Add thin route at front so that route offsetting works as expected
2427  if( aStartWidth < routeVertices.front().RouteWidth )
2428  {
2429  NET_PCB::ROUTE_VERTEX newFrontVertex = aCadstarRoute.RouteVertices.front();
2430  newFrontVertex.RouteWidth = aStartWidth;
2431  newFrontVertex.Vertex.End = aCadstarRoute.StartPoint;
2432  routeVertices.insert( routeVertices.begin(), newFrontVertex );
2433  }
2434 
2435  // Add thin route at the back if required
2436  if( aEndWidth < routeVertices.back().RouteWidth )
2437  {
2438  NET_PCB::ROUTE_VERTEX newBackVertex = aCadstarRoute.RouteVertices.back();
2439  newBackVertex.RouteWidth = aEndWidth;
2440  routeVertices.push_back( newBackVertex );
2441  }
2442 
2443  POINT prevEnd = aCadstarRoute.StartPoint;
2444 
2445  for( const NET_PCB::ROUTE_VERTEX& v : routeVertices )
2446  {
2447  PCB_SHAPE* shape = getShapeFromVertex( prevEnd, v.Vertex );
2448  shape->SetLayer( getKiCadLayer( aCadstarRoute.LayerID ) );
2449  shape->SetWidth( getKiCadLength( v.RouteWidth ) );
2450  shape->SetLocked( v.Fixed );
2451  shapes.push_back( shape );
2452  prevEnd = v.Vertex.End;
2453  }
2454 
2455  NETINFO_ITEM* net = getKiCadNet( aCadstarNetID );
2456  std::vector<PCB_TRACK*> tracks = makeTracksFromShapes( shapes, m_board, net );
2457 
2458  //cleanup
2459  for( PCB_SHAPE* shape : shapes )
2460  delete shape;
2461 }
2462 
2463 
2465  const NET_ID& aCadstarNetID, const NET_PCB::VIA& aCadstarVia )
2466 {
2467  PCB_VIA* via = new PCB_VIA( m_board );
2469 
2470  VIACODE csViaCode = getViaCode( aCadstarVia.ViaCodeID );
2471  LAYERPAIR csLayerPair = getLayerPair( aCadstarVia.LayerPairID );
2472 
2473  via->SetPosition( getKiCadPoint( aCadstarVia.Location ) );
2474  via->SetDrill( getKiCadLength( csViaCode.DrillDiameter ) );
2475  via->SetLocked( aCadstarVia.Fixed );
2476 
2477  if( csViaCode.Shape.ShapeType != PAD_SHAPE_TYPE::CIRCLE )
2478  {
2479  wxLogError( _( "The CADSTAR via code '%s' has different shape from a circle defined. "
2480  "KiCad only supports circular vias so this via type has been changed to "
2481  "be a via with circular shape of %.2f mm diameter." ),
2482  csViaCode.Name,
2483  (double) ( (double) getKiCadLength( csViaCode.Shape.Size ) / 1E6 ) );
2484  }
2485 
2486  via->SetWidth( getKiCadLength( csViaCode.Shape.Size ) );
2487 
2488  bool start_layer_outside =
2489  csLayerPair.PhysicalLayerStart == 1
2491  bool end_layer_outside =
2492  csLayerPair.PhysicalLayerEnd == 1
2494 
2495  if( start_layer_outside && end_layer_outside )
2496  {
2497  via->SetViaType( VIATYPE::THROUGH );
2498  }
2499  else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
2500  {
2501  via->SetViaType( VIATYPE::BLIND_BURIED );
2502  }
2503  else
2504  {
2505  via->SetViaType( VIATYPE::MICROVIA );
2506  }
2507 
2508  via->SetLayerPair( getKiCadCopperLayerID( csLayerPair.PhysicalLayerStart ),
2509  getKiCadCopperLayerID( csLayerPair.PhysicalLayerEnd ) );
2510  via->SetNet( getKiCadNet( aCadstarNetID ) );
2512 
2513  return via->GetWidth();
2514 }
2515 
2516 
2518  BOARD_ITEM_CONTAINER* aContainer, const GROUP_ID& aCadstarGroupID,
2519  const LAYER_ID& aCadstarLayerOverride, const wxPoint& aMoveVector,
2520  const double& aRotationAngle, const double& aScalingFactor, const wxPoint& aTransformCentre,
2521  const bool& aMirrorInvert )
2522 {
2523  PCB_TEXT* txt = new PCB_TEXT( aContainer );
2524  aContainer->Add( txt );
2525  txt->SetText( aCadstarText.Text );
2526 
2527  wxPoint rotatedTextPos = getKiCadPoint( aCadstarText.Position );
2528  RotatePoint( &rotatedTextPos, aTransformCentre, aRotationAngle );
2529  rotatedTextPos.x =
2530  KiROUND( (double) ( rotatedTextPos.x - aTransformCentre.x ) * aScalingFactor );
2531  rotatedTextPos.y =
2532  KiROUND( (double) ( rotatedTextPos.y - aTransformCentre.y ) * aScalingFactor );
2533  rotatedTextPos += aTransformCentre;
2534  txt->SetTextPos( rotatedTextPos );
2535  txt->SetPosition( rotatedTextPos );
2536 
2537  txt->SetTextAngle( getAngleTenthDegree( aCadstarText.OrientAngle ) + aRotationAngle );
2538 
2539  txt->SetMirrored( aCadstarText.Mirror );
2540 
2541  TEXTCODE tc = getTextCode( aCadstarText.TextCodeID );
2542 
2544 
2545  wxSize unscaledTextSize;
2546  unscaledTextSize.x = getKiCadLength( tc.Width );
2547 
2548  // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
2549  // to work well for most fonts.
2550  if( unscaledTextSize.x == 0 )
2551  unscaledTextSize.x = getKiCadLength( tc.Height );
2552 
2553  unscaledTextSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
2554  txt->SetTextSize( unscaledTextSize );
2555 
2556  switch( aCadstarText.Alignment )
2557  {
2558  case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
2559  case ALIGNMENT::BOTTOMLEFT:
2562  break;
2563 
2567  break;
2568 
2572  break;
2573 
2574  case ALIGNMENT::CENTERLEFT:
2577  break;
2578 
2582  break;
2583 
2587  break;
2588 
2589  case ALIGNMENT::TOPLEFT:
2592  break;
2593 
2594  case ALIGNMENT::TOPCENTER:
2597  break;
2598 
2599  case ALIGNMENT::TOPRIGHT:
2602  break;
2603 
2604  default:
2605  wxFAIL_MSG( wxT( "Unknown Alignment - needs review!" ) );
2606  }
2607 
2608  if( aMirrorInvert )
2609  {
2610  txt->Flip( aTransformCentre, true );
2611  }
2612 
2613  //scale it after flipping:
2614  if( aScalingFactor != 1.0 )
2615  {
2616  wxSize scaledTextSize;
2617  scaledTextSize.x = KiROUND( (double) unscaledTextSize.x * aScalingFactor );
2618  scaledTextSize.y = KiROUND( (double) unscaledTextSize.y * aScalingFactor );
2619  txt->SetTextSize( scaledTextSize );
2620  txt->SetTextThickness(
2621  KiROUND( (double) getKiCadLength( tc.LineWidth ) * aScalingFactor ) );
2622  }
2623 
2624  txt->Move( aMoveVector );
2625 
2626  if( aCadstarText.Alignment == ALIGNMENT::NO_ALIGNMENT )
2628 
2629  LAYER_ID layersToDrawOn = aCadstarLayerOverride;
2630 
2631  if( layersToDrawOn.IsEmpty() )
2632  layersToDrawOn = aCadstarText.LayerID;
2633 
2634  if( isLayerSet( layersToDrawOn ) )
2635  {
2636  //Make a copy on each layer
2637 
2638  LSEQ layers = getKiCadLayerSet( layersToDrawOn ).Seq();
2639  PCB_TEXT* newtxt;
2640 
2641  for( PCB_LAYER_ID layer : layers )
2642  {
2643  txt->SetLayer( layer );
2644  newtxt = static_cast<PCB_TEXT*>( txt->Duplicate() );
2645  m_board->Add( newtxt, ADD_MODE::APPEND );
2646 
2647  if( !aCadstarGroupID.IsEmpty() )
2648  addToGroup( aCadstarGroupID, newtxt );
2649  }
2650 
2651  m_board->Remove( txt );
2652  delete txt;
2653  }
2654  else
2655  {
2656  txt->SetLayer( getKiCadLayer( layersToDrawOn ) );
2657 
2658  if( !aCadstarGroupID.IsEmpty() )
2659  addToGroup( aCadstarGroupID, txt );
2660  }
2661  //TODO Handle different font types when KiCad can support it.
2662 }
2663 
2664 
2666  const PCB_LAYER_ID& aKiCadLayer,
2667  const int& aLineThickness,
2668  const wxString& aShapeName,
2669  BOARD_ITEM_CONTAINER* aContainer,
2670  const GROUP_ID& aCadstarGroupID,
2671  const wxPoint& aMoveVector,
2672  const double& aRotationAngle,
2673  const double& aScalingFactor,
2674  const wxPoint& aTransformCentre,
2675  const bool& aMirrorInvert )
2676 {
2677  switch( aCadstarShape.Type )
2678  {
2679  case SHAPE_TYPE::OPENSHAPE:
2680  case SHAPE_TYPE::OUTLINE:
2682  drawCadstarVerticesAsShapes( aCadstarShape.Vertices, aKiCadLayer, aLineThickness,
2683  aContainer, aCadstarGroupID, aMoveVector, aRotationAngle,
2684  aScalingFactor, aTransformCentre, aMirrorInvert );
2685  drawCadstarCutoutsAsShapes( aCadstarShape.Cutouts, aKiCadLayer, aLineThickness,
2686  aContainer, aCadstarGroupID, aMoveVector, aRotationAngle,
2687  aScalingFactor, aTransformCentre, aMirrorInvert );
2688  break;
2689 
2690  case SHAPE_TYPE::HATCHED:
2692  wxLogWarning( wxString::Format(
2693  _( "The shape for '%s' is Hatch filled in CADSTAR, which has no KiCad equivalent. "
2694  "Using solid fill instead." ),
2695  aShapeName ) );
2696 
2697  case SHAPE_TYPE::SOLID:
2698  {
2699  PCB_SHAPE* shape;
2700 
2701  if( isFootprint( aContainer ) )
2702  shape = new FP_SHAPE( (FOOTPRINT*) aContainer, SHAPE_T::POLY );
2703  else
2704  shape = new PCB_SHAPE( aContainer, SHAPE_T::POLY );
2705 
2706  shape->SetFilled( true );
2707 
2709  aCadstarShape, -1, aContainer, aMoveVector, aRotationAngle, aScalingFactor,
2710  aTransformCentre, aMirrorInvert );
2711 
2712  shapePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
2713 
2714  shape->SetPolyShape( shapePolys );
2715  shape->SetWidth( aLineThickness );
2716  shape->SetLayer( aKiCadLayer );
2717  aContainer->Add( shape, ADD_MODE::APPEND );
2718 
2719  if( !aCadstarGroupID.IsEmpty() )
2720  addToGroup( aCadstarGroupID, shape );
2721  }
2722  break;
2723  }
2724 }
2725 
2726 
2727 void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarCutoutsAsShapes( const std::vector<CUTOUT>& aCutouts,
2728  const PCB_LAYER_ID& aKiCadLayer,
2729  const int& aLineThickness,
2730  BOARD_ITEM_CONTAINER* aContainer,
2731  const GROUP_ID& aCadstarGroupID,
2732  const wxPoint& aMoveVector,
2733  const double& aRotationAngle,
2734  const double& aScalingFactor,
2735  const wxPoint& aTransformCentre,
2736  const bool& aMirrorInvert )
2737 {
2738  for( CUTOUT cutout : aCutouts )
2739  {
2740  drawCadstarVerticesAsShapes( cutout.Vertices, aKiCadLayer, aLineThickness, aContainer,
2741  aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2742  aTransformCentre, aMirrorInvert );
2743  }
2744 }
2745 
2746 
2747 void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarVerticesAsShapes( const std::vector<VERTEX>& aCadstarVertices,
2748  const PCB_LAYER_ID& aKiCadLayer,
2749  const int& aLineThickness,
2750  BOARD_ITEM_CONTAINER* aContainer,
2751  const GROUP_ID& aCadstarGroupID,
2752  const wxPoint& aMoveVector,
2753  const double& aRotationAngle,
2754  const double& aScalingFactor,
2755  const wxPoint& aTransformCentre,
2756  const bool& aMirrorInvert )
2757 {
2758  std::vector<PCB_SHAPE*> shapes = getShapesFromVertices( aCadstarVertices, aContainer,
2759  aCadstarGroupID, aMoveVector,
2760  aRotationAngle, aScalingFactor,
2761  aTransformCentre, aMirrorInvert );
2762 
2763  for( PCB_SHAPE* shape : shapes )
2764  {
2765  shape->SetWidth( aLineThickness );
2766  shape->SetLayer( aKiCadLayer );
2767  shape->SetParent( aContainer );
2768  aContainer->Add( shape, ADD_MODE::APPEND );
2769  }
2770 }
2771 
2772 
2774  const std::vector<VERTEX>& aCadstarVertices,
2775  BOARD_ITEM_CONTAINER* aContainer,
2776  const GROUP_ID& aCadstarGroupID,
2777  const wxPoint& aMoveVector,
2778  const double& aRotationAngle,
2779  const double& aScalingFactor,
2780  const wxPoint& aTransformCentre,
2781  const bool& aMirrorInvert )
2782 {
2783  std::vector<PCB_SHAPE*> drawSegments;
2784 
2785  if( aCadstarVertices.size() < 2 )
2786  //need at least two points to draw a segment! (unlikely but possible to have only one)
2787  return drawSegments;
2788 
2789  const VERTEX* prev = &aCadstarVertices.at( 0 ); // first one should always be a point vertex
2790  const VERTEX* cur;
2791 
2792  for( size_t i = 1; i < aCadstarVertices.size(); i++ )
2793  {
2794  cur = &aCadstarVertices.at( i );
2795  drawSegments.push_back( getShapeFromVertex( prev->End, *cur, aContainer, aCadstarGroupID,
2796  aMoveVector, aRotationAngle, aScalingFactor,
2797  aTransformCentre, aMirrorInvert ) );
2798  prev = cur;
2799  }
2800 
2801  return drawSegments;
2802 }
2803 
2804 
2806  const VERTEX& aCadstarVertex,
2807  BOARD_ITEM_CONTAINER* aContainer,
2808  const GROUP_ID& aCadstarGroupID,
2809  const wxPoint& aMoveVector,
2810  const double& aRotationAngle,
2811  const double& aScalingFactor,
2812  const wxPoint& aTransformCentre,
2813  const bool& aMirrorInvert )
2814 {
2815  PCB_SHAPE* shape = nullptr;
2816  bool cw = false;
2817  double arcStartAngle, arcEndAngle, arcAngle;
2818 
2819  wxPoint startPoint = getKiCadPoint( aCadstarStartPoint );
2820  wxPoint endPoint = getKiCadPoint( aCadstarVertex.End );
2821  wxPoint centerPoint;
2822 
2823  if( aCadstarVertex.Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE
2824  || aCadstarVertex.Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
2825  {
2826  centerPoint = ( startPoint + endPoint ) / 2;
2827  }
2828  else
2829  {
2830  centerPoint = getKiCadPoint( aCadstarVertex.Center );
2831  }
2832 
2833  switch( aCadstarVertex.Type )
2834  {
2835 
2836  case VERTEX_TYPE::POINT:
2837 
2838  if( isFootprint( aContainer ) )
2839  shape = new FP_SHAPE( static_cast<FOOTPRINT*>( aContainer ), SHAPE_T::SEGMENT );
2840  else
2841  shape = new PCB_SHAPE( aContainer, SHAPE_T::SEGMENT );
2842 
2843  shape->SetStart( startPoint );
2844  shape->SetEnd( endPoint );
2845  break;
2846 
2849  cw = true;
2851 
2854 
2855  if( isFootprint( aContainer ) )
2856  shape = new FP_SHAPE((FOOTPRINT*) aContainer, SHAPE_T::ARC );
2857  else
2858  shape = new PCB_SHAPE( aContainer, SHAPE_T::ARC );
2859 
2860  shape->SetCenter( centerPoint );
2861  shape->SetStart( startPoint );
2862 
2863  arcStartAngle = getPolarAngle( startPoint - centerPoint );
2864  arcEndAngle = getPolarAngle( endPoint - centerPoint );
2865  arcAngle = arcEndAngle - arcStartAngle;
2866  //TODO: detect if we are supposed to draw a circle instead (i.e. two SEMICIRCLEs
2867  // with opposite start/end points and same centre point)
2868 
2869  if( cw )
2870  shape->SetArcAngleAndEnd( NormalizeAnglePos( arcAngle ) );
2871  else
2872  shape->SetArcAngleAndEnd( NormalizeAngleNeg( arcAngle ), true );
2873 
2874  break;
2875  }
2876 
2877  //Apply transforms
2878  if( aMirrorInvert )
2879  shape->Flip( aTransformCentre, true );
2880 
2881  if( aScalingFactor != 1.0 )
2882  {
2883  shape->Move( -aTransformCentre );
2884  shape->Scale( aScalingFactor );
2885  shape->Move( aTransformCentre );
2886  }
2887 
2888  if( aRotationAngle != 0.0 )
2889  shape->Rotate( aTransformCentre, aRotationAngle );
2890 
2891  if( aMoveVector != wxPoint{ 0, 0 } )
2892  shape->Move( aMoveVector );
2893 
2894  if( isFootprint( aContainer ) && shape != nullptr )
2895  static_cast<FP_SHAPE*>( shape )->SetLocalCoord();
2896 
2897  if( !aCadstarGroupID.IsEmpty() )
2898  addToGroup( aCadstarGroupID, shape );
2899 
2900  return shape;
2901 }
2902 
2903 
2905  const int& aLineThickness,
2906  BOARD_ITEM_CONTAINER* aParentContainer )
2907 {
2908  ZONE* zone = new ZONE( aParentContainer, isFootprint( aParentContainer ) );
2909 
2910  if( aCadstarShape.Type == SHAPE_TYPE::HATCHED )
2911  {
2914  }
2915  else
2916  {
2918  }
2919 
2920  SHAPE_POLY_SET polygon = getPolySetFromCadstarShape( aCadstarShape, aLineThickness );
2921 
2922  zone->AddPolygon( polygon.COutline( 0 ) );
2923 
2924  for( int i = 0; i < polygon.HoleCount( 0 ); i++ )
2925  zone->AddPolygon( polygon.CHole( 0, i ) );
2926 
2927  return zone;
2928 }
2929 
2930 
2932  const int& aLineThickness,
2933  BOARD_ITEM_CONTAINER* aContainer,
2934  const wxPoint& aMoveVector,
2935  const double& aRotationAngle,
2936  const double& aScalingFactor,
2937  const wxPoint& aTransformCentre,
2938  const bool& aMirrorInvert )
2939 {
2940  GROUP_ID noGroup = wxEmptyString;
2941 
2942  std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( aCadstarShape.Vertices,
2943  aContainer, noGroup, aMoveVector,
2944  aRotationAngle, aScalingFactor,
2945  aTransformCentre, aMirrorInvert );
2946 
2947  SHAPE_POLY_SET polySet( getLineChainFromShapes( outlineShapes ) );
2948 
2949  //cleanup
2950  for( PCB_SHAPE* shape : outlineShapes )
2951  delete shape;
2952 
2953  for( CUTOUT cutout : aCadstarShape.Cutouts )
2954  {
2955  std::vector<PCB_SHAPE*> cutoutShapes = getShapesFromVertices( cutout.Vertices, aContainer,
2956  noGroup, aMoveVector,
2957  aRotationAngle, aScalingFactor,
2958  aTransformCentre, aMirrorInvert );
2959 
2960  polySet.AddHole( getLineChainFromShapes( cutoutShapes ) );
2961 
2962  //cleanup
2963  for( PCB_SHAPE* shape : cutoutShapes )
2964  delete shape;
2965  }
2966 
2967  polySet.ClearArcs();
2968 
2969  if( aLineThickness > 0 )
2970  {
2971  polySet.Inflate( aLineThickness / 2, 32,
2972  SHAPE_POLY_SET::CORNER_STRATEGY::ROUND_ALL_CORNERS );
2973  }
2974 
2975 #ifdef DEBUG
2976  for( int i = 0; i < polySet.OutlineCount(); ++i )
2977  {
2978  wxASSERT( polySet.Outline( i ).PointCount() > 2 );
2979 
2980  for( int j = 0; j < polySet.HoleCount( i ); ++j )
2981  {
2982  wxASSERT( polySet.Hole( i, j ).PointCount() > 2 );
2983  }
2984  }
2985 #endif
2986 
2987  return polySet;
2988 }
2989 
2990 
2992 {
2993  SHAPE_LINE_CHAIN lineChain;
2994 
2995  for( PCB_SHAPE* shape : aShapes )
2996  {
2997  switch( shape->GetShape() )
2998  {
2999  case SHAPE_T::ARC:
3000  {
3001  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3002  {
3003  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3004  SHAPE_ARC arc( fp_shape->GetCenter0(), fp_shape->GetStart0(), fp_shape->GetArcAngle() / 10.0 );
3005 
3006  if( shape->EndsSwapped() )
3007  arc.Reverse();
3008 
3009  lineChain.Append( arc );
3010  }
3011  else
3012  {
3013  SHAPE_ARC arc( shape->GetCenter(), shape->GetStart(), shape->GetArcAngle() / 10.0 );
3014 
3015  if( shape->EndsSwapped() )
3016  arc.Reverse();
3017 
3018  lineChain.Append( arc );
3019  }
3020  }
3021  break;
3022  case SHAPE_T::SEGMENT:
3023  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3024  {
3025  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3026  lineChain.Append( fp_shape->GetStart0().x, fp_shape->GetStart0().y );
3027  lineChain.Append( fp_shape->GetEnd0().x, fp_shape->GetEnd0().y );
3028  }
3029  else
3030  {
3031  lineChain.Append( shape->GetStartX(), shape->GetStartY() );
3032  lineChain.Append( shape->GetEndX(), shape->GetEndY() );
3033  }
3034  break;
3035 
3036  default:
3037  wxFAIL_MSG( wxT( "Drawsegment type is unexpected. Ignored." ) );
3038  }
3039  }
3040 
3041  // Shouldn't have less than 3 points to make a closed shape!
3042  wxASSERT( lineChain.PointCount() > 2 );
3043 
3044  // Check if it is closed
3045  if( lineChain.GetPoint( 0 ) != lineChain.GetPoint( lineChain.PointCount() - 1 ) )
3046  {
3047  lineChain.Append( lineChain.GetPoint( 0 ) );
3048  }
3049 
3050  lineChain.SetClosed( true );
3051 
3052  return lineChain;
3053 }
3054 
3055 
3057  const std::vector<PCB_SHAPE*> aShapes,
3058  BOARD_ITEM_CONTAINER* aParentContainer,
3059  NETINFO_ITEM* aNet, PCB_LAYER_ID aLayerOverride,
3060  int aWidthOverride )
3061 {
3062  std::vector<PCB_TRACK*> tracks;
3063  PCB_TRACK* prevTrack = nullptr;
3064  PCB_TRACK* track = nullptr;
3065 
3066  auto addTrack =
3067  [&]( PCB_TRACK* aTrack )
3068  {
3069  // Ignore zero length tracks in the same way as the CADSTAR postprocessor does
3070  // when generating gerbers. Note that CADSTAR reports these as "Route offset
3071  // errors" when running a DRC within CADSTAR, so we shouldn't be getting this in
3072  // general, however it is used to remove any synthetic points added to
3073  // aDrawSegments by the caller of this function.
3074  if( aTrack->GetLength() != 0 )
3075  {
3076  tracks.push_back( aTrack );
3077  aParentContainer->Add( aTrack, ADD_MODE::APPEND );
3078  }
3079  else
3080  {
3081  delete aTrack;
3082  }
3083  };
3084 
3085  for( PCB_SHAPE* shape : aShapes )
3086  {
3087  switch( shape->GetShape() )
3088  {
3089  case SHAPE_T::ARC:
3090  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3091  {
3092  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3093  SHAPE_ARC arc( fp_shape->GetStart0(), fp_shape->GetArcMid0(), fp_shape->GetEnd0(), 0 );
3094 
3095  if( fp_shape->EndsSwapped() )
3096  arc.Reverse();
3097 
3098  track = new PCB_ARC( aParentContainer, &arc );
3099  }
3100  else
3101  {
3102  SHAPE_ARC arc( shape->GetStart(), shape->GetArcMid(), shape->GetEnd(), 0 );
3103 
3104  if( shape->EndsSwapped() )
3105  arc.Reverse();
3106 
3107  track = new PCB_ARC( aParentContainer, &arc );
3108  }
3109  break;
3110  case SHAPE_T::SEGMENT:
3111  if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3112  {
3113  FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3114  track = new PCB_TRACK( aParentContainer );
3115  track->SetStart( fp_shape->GetStart0() );
3116  track->SetEnd( fp_shape->GetEnd0() );
3117  }
3118  else
3119  {
3120  track = new PCB_TRACK( aParentContainer );
3121  track->SetStart( shape->GetStart() );
3122  track->SetEnd( shape->GetEnd() );
3123  }
3124  break;
3125 
3126  default:
3127  wxFAIL_MSG( wxT( "Drawsegment type is unexpected. Ignored." ) );
3128  continue;
3129  }
3130 
3131  if( aWidthOverride == -1 )
3132  track->SetWidth( shape->GetWidth() );
3133  else
3134  track->SetWidth( aWidthOverride );
3135 
3136  if( aLayerOverride == PCB_LAYER_ID::UNDEFINED_LAYER )
3137  track->SetLayer( shape->GetLayer() );
3138  else
3139  track->SetLayer( aLayerOverride );
3140 
3141  if( aNet != nullptr )
3142  track->SetNet( aNet );
3143  else
3144  track->SetNetCode( -1 );
3145 
3146  track->SetLocked( shape->IsLocked() );
3147 
3148  // Apply route offsetting, mimmicking the behaviour of the CADSTAR post processor
3149  if( prevTrack != nullptr )
3150  {
3151  int offsetAmount = ( track->GetWidth() / 2 ) - ( prevTrack->GetWidth() / 2 );
3152 
3153  if( offsetAmount > 0 )
3154  {
3155  // modify the start of the current track
3156  wxPoint newStart = track->GetStart();
3157  applyRouteOffset( &newStart, track->GetEnd(), offsetAmount );
3158  track->SetStart( newStart );
3159  }
3160  else if( offsetAmount < 0 )
3161  {
3162  // amend the end of the previous track
3163  wxPoint newEnd = prevTrack->GetEnd();
3164  applyRouteOffset( &newEnd, prevTrack->GetStart(), -offsetAmount );
3165  prevTrack->SetEnd( newEnd );
3166  } // don't do anything if offsetAmount == 0
3167 
3168  // Add a synthetic track of the thinnest width between the tracks
3169  // to ensure KiCad features works as expected on the imported design
3170  // (KiCad expects tracks are contiguous segments)
3171  if( track->GetStart() != prevTrack->GetEnd() )
3172  {
3173  int minWidth = std::min( track->GetWidth(), prevTrack->GetWidth() );
3174  PCB_TRACK* synthTrack = new PCB_TRACK( aParentContainer );
3175  synthTrack->SetStart( prevTrack->GetEnd() );
3176  synthTrack->SetEnd( track->GetStart() );
3177  synthTrack->SetWidth( minWidth );
3178  synthTrack->SetLocked( track->IsLocked() );
3179  synthTrack->SetNet( track->GetNet() );
3180  synthTrack->SetLayer( track->GetLayer() );
3181  addTrack( synthTrack );
3182  }
3183  }
3184 
3185  if( prevTrack )
3186  addTrack( prevTrack );
3187 
3188  prevTrack = track;
3189  }
3190 
3191  if( track )
3192  addTrack( track );
3193 
3194  return tracks;
3195 }
3196 
3197 
3199  const ATTRIBUTE_ID& aCadstarAttributeID,
3200  FOOTPRINT* aFootprint,
3201  const wxString& aAttributeValue )
3202 {
3203  FP_TEXT* txt;
3204 
3205  if( aCadstarAttributeID == COMPONENT_NAME_ATTRID )
3206  {
3207  txt = &aFootprint->Reference(); //text should be set outside this function
3208  }
3209  else if( aCadstarAttributeID == PART_NAME_ATTRID )
3210  {
3211  if( aFootprint->Value().GetText().IsEmpty() )
3212  {
3213  // Use PART_NAME_ATTRID as the value is value field is blank
3214  aFootprint->SetValue( aAttributeValue );
3215  txt = &aFootprint->Value();
3216  }
3217  else
3218  {
3219  txt = new FP_TEXT( aFootprint );
3220  aFootprint->Add( txt );
3221  txt->SetText( aAttributeValue );
3222  }
3223  txt->SetVisible( false ); //make invisible to avoid clutter.
3224  }
3225  else if( aCadstarAttributeID != COMPONENT_NAME_2_ATTRID
3226  && getAttributeName( aCadstarAttributeID ) == wxT( "Value" ) )
3227  {
3228  if( !aFootprint->Value().GetText().IsEmpty() )
3229  {
3230  //copy the object
3231  aFootprint->Add( aFootprint->Value().Duplicate() );
3232  }
3233 
3234  aFootprint->SetValue( aAttributeValue );
3235  txt = &aFootprint->Value();
3236  txt->SetVisible( false ); //make invisible to avoid clutter.
3237  }
3238  else
3239  {
3240  txt = new FP_TEXT( aFootprint );
3241  aFootprint->Add( txt );
3242  txt->SetText( aAttributeValue );
3243  txt->SetVisible( false ); //make all user attributes invisible to avoid clutter.
3244  //TODO: Future improvement - allow user to decide what to do with attributes
3245  }
3246 
3247  wxPoint rotatedTextPos = getKiCadPoint( aCadstarAttrLoc.Position ) - aFootprint->GetPosition();
3248  RotatePoint( &rotatedTextPos, -aFootprint->GetOrientation() );
3249 
3250  txt->SetTextPos( getKiCadPoint( aCadstarAttrLoc.Position ) );
3251  txt->SetPos0( rotatedTextPos );
3252  txt->SetLayer( getKiCadLayer( aCadstarAttrLoc.LayerID ) );
3253  txt->SetMirrored( aCadstarAttrLoc.Mirror );
3254  txt->SetTextAngle(
3255  getAngleTenthDegree( aCadstarAttrLoc.OrientAngle ) - aFootprint->GetOrientation() );
3256 
3257  if( aCadstarAttrLoc.Mirror ) // If mirroring, invert angle to match CADSTAR
3258  txt->SetTextAngle( -txt->GetTextAngle() );
3259 
3260  TEXTCODE tc = getTextCode( aCadstarAttrLoc.TextCodeID );
3261 
3263 
3264  wxSize txtSize;
3265  txtSize.x = getKiCadLength( tc.Width );
3266 
3267  // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
3268  // to work well for most fonts.
3269  if( txtSize.x == 0 )
3270  txtSize.x = getKiCadLength( tc.Height );
3271 
3272  txtSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
3273  txt->SetTextSize( txtSize );
3274  txt->SetKeepUpright( false ); //Keeping it upright seems to result in incorrect orientation
3275 
3276  switch( aCadstarAttrLoc.Alignment )
3277  {
3278  case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
3281  case ALIGNMENT::BOTTOMLEFT:
3284  break;
3285 
3289  break;
3290 
3294  break;
3295 
3296  case ALIGNMENT::CENTERLEFT:
3299  break;
3300 
3304  break;
3305 
3309  break;
3310 
3311  case ALIGNMENT::TOPLEFT:
3314  break;
3315 
3316  case ALIGNMENT::TOPCENTER:
3319  break;
3320 
3321  case ALIGNMENT::TOPRIGHT:
3324  break;
3325 
3326  default:
3327  wxFAIL_MSG( wxT( "Unknown Alignment - needs review!" ) );
3328  }
3329 
3330  //TODO Handle different font types when KiCad can support it.
3331 }
3332 
3333 
3334 void CADSTAR_PCB_ARCHIVE_LOADER::applyRouteOffset( wxPoint* aPointToOffset,
3335  const wxPoint& aRefPoint,
3336  const long& aOffsetAmount )
3337 {
3338  VECTOR2I v( *aPointToOffset - aRefPoint );
3339  int newLength = v.EuclideanNorm() - aOffsetAmount;
3340 
3341  if( newLength > 0 )
3342  {
3343  VECTOR2I offsetted = v.Resize( newLength ) + VECTOR2I( aRefPoint );
3344  aPointToOffset->x = offsetted.x;
3345  aPointToOffset->y = offsetted.y;
3346  }
3347  else
3348  {
3349  *aPointToOffset = aRefPoint; // zero length track. Needs to be removed to mimmick
3350  // cadstar behaviour
3351  }
3352 }
3353 
3354 
3356 {
3357  wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID )
3358  != Assignments.Codedefs.LineCodes.end(),
3360 
3361  return getKiCadLength( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Width );
3362 }
3363 
3364 
3366  const COPPERCODE_ID& aCadstaCopperCodeID )
3367 {
3368  wxCHECK( Assignments.Codedefs.CopperCodes.find( aCadstaCopperCodeID )
3369  != Assignments.Codedefs.CopperCodes.end(),
3370  COPPERCODE() );
3371 
3372  return Assignments.Codedefs.CopperCodes.at( aCadstaCopperCodeID );
3373 }
3374 
3375 
3377  const TEXTCODE_ID& aCadstarTextCodeID )
3378 {
3379  wxCHECK( Assignments.Codedefs.TextCodes.find( aCadstarTextCodeID )
3380  != Assignments.Codedefs.TextCodes.end(),
3381  TEXTCODE() );
3382 
3383  return Assignments.Codedefs.TextCodes.at( aCadstarTextCodeID );
3384 }
3385 
3386 
3388  const PADCODE_ID& aCadstarPadCodeID )
3389 {
3390  wxCHECK( Assignments.Codedefs.PadCodes.find( aCadstarPadCodeID )
3391  != Assignments.Codedefs.PadCodes.end(),
3392  PADCODE() );
3393 
3394  return Assignments.Codedefs.PadCodes.at( aCadstarPadCodeID );
3395 }
3396 
3397 
3399  const VIACODE_ID& aCadstarViaCodeID )
3400 {
3401  wxCHECK( Assignments.Codedefs.ViaCodes.find( aCadstarViaCodeID )
3402  != Assignments.Codedefs.ViaCodes.end(),
3403  VIACODE() );
3404 
3405  return Assignments.Codedefs.ViaCodes.at( aCadstarViaCodeID );
3406 }
3407 
3408 
3410  const LAYERPAIR_ID& aCadstarLayerPairID )
3411 {
3412  wxCHECK( Assignments.Codedefs.LayerPairs.find( aCadstarLayerPairID )
3413  != Assignments.Codedefs.LayerPairs.end(),
3414  LAYERPAIR() );
3415 
3416  return Assignments.Codedefs.LayerPairs.at( aCadstarLayerPairID );
3417 }
3418 
3419 
3420 wxString CADSTAR_PCB_ARCHIVE_LOADER::getAttributeName( const ATTRIBUTE_ID& aCadstarAttributeID )
3421 {
3422  wxCHECK( Assignments.Codedefs.AttributeNames.find( aCadstarAttributeID )
3424  wxEmptyString );
3425 
3426  return Assignments.Codedefs.AttributeNames.at( aCadstarAttributeID ).Name;
3427 }
3428 
3429 
3431  const std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE>& aCadstarAttributeMap )
3432 {
3433  wxCHECK( aCadstarAttributeMap.find( aCadstarAttributeID ) != aCadstarAttributeMap.end(),
3434  wxEmptyString );
3435 
3436  return aCadstarAttributeMap.at( aCadstarAttributeID ).Value;
3437 }
3438 
3439 
3442 {
3443  if( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) != Assignments.Layerdefs.Layers.end() )
3444  {
3445  return Assignments.Layerdefs.Layers.at( aCadstarLayerID ).Type;
3446  }
3447 
3448  return LAYER_TYPE::UNDEFINED;
3449 }
3450 
3451 
3453  const PART_ID& aCadstarPartID )
3454 {
3455  wxCHECK( Parts.PartDefinitions.find( aCadstarPartID ) != Parts.PartDefinitions.end(), PART() );
3456 
3457  return Parts.PartDefinitions.at( aCadstarPartID );
3458 }
3459 
3460 
3462  const ROUTECODE_ID& aCadstarRouteCodeID )
3463 {
3464  wxCHECK( Assignments.Codedefs.RouteCodes.find( aCadstarRouteCodeID )
3465  != Assignments.Codedefs.RouteCodes.end(),
3466  ROUTECODE() );
3467 
3468  return Assignments.Codedefs.RouteCodes.at( aCadstarRouteCodeID );
3469 }
3470 
3471 
3473  const HATCHCODE_ID& aCadstarHatchcodeID )
3474 {
3475  wxCHECK( Assignments.Codedefs.HatchCodes.find( aCadstarHatchcodeID )
3476  != Assignments.Codedefs.HatchCodes.end(),
3477  HATCHCODE() );
3478 
3479  return Assignments.Codedefs.HatchCodes.at( aCadstarHatchcodeID );
3480 }
3481 
3482 
3484  const HATCHCODE_ID& aCadstarHatchcodeID )
3485 {
3486  checkAndLogHatchCode( aCadstarHatchcodeID );
3487  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3488 
3489  if( hcode.Hatches.size() < 1 )
3491  else
3492  return getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle );
3493 }
3494 
3495 
3497  const HATCHCODE_ID& aCadstarHatchcodeID )
3498 {
3499  checkAndLogHatchCode( aCadstarHatchcodeID );
3500  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3501 
3502  if( hcode.Hatches.size() < 1 )
3504  else
3505  return getKiCadLength( hcode.Hatches.at( 0 ).LineWidth );
3506 }
3507 
3508 
3510 {
3511  checkAndLogHatchCode( aCadstarHatchcodeID );
3512  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3513 
3514  if( hcode.Hatches.size() < 1 )
3516  else
3517  return getKiCadLength( hcode.Hatches.at( 0 ).Step );
3518 }
3519 
3520 
3522 {
3523  wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), nullptr );
3524 
3525  return m_groupMap.at( aCadstarGroupID );
3526 }
3527 
3528 
3530 {
3531  if( m_hatchcodesTested.find( aCadstarHatchcodeID ) != m_hatchcodesTested.end() )
3532  {
3533  return; //already checked
3534  }
3535  else
3536  {
3537  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3538 
3539  if( hcode.Hatches.size() != 2 )
3540  {
3541  wxLogWarning( wxString::Format(
3542  _( "The CADSTAR Hatching code '%s' has %d hatches defined. "
3543  "KiCad only supports 2 hatches (crosshatching) 90 degrees apart. "
3544  "The imported hatching is crosshatched." ),
3545  hcode.Name, (int) hcode.Hatches.size() ) );
3546  }
3547  else
3548  {
3549  if( hcode.Hatches.at( 0 ).LineWidth != hcode.Hatches.at( 1 ).LineWidth )
3550  {
3551  wxLogWarning( wxString::Format(
3552  _( "The CADSTAR Hatching code '%s' has different line widths for each "
3553  "hatch. KiCad only supports one width for the hatching. The imported "
3554  "hatching uses the width defined in the first hatch definition, i.e. "
3555  "%.2f mm." ),
3556  hcode.Name,
3557  (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).LineWidth ) )
3558  / 1E6 ) );
3559  }
3560 
3561  if( hcode.Hatches.at( 0 ).Step != hcode.Hatches.at( 1 ).Step )
3562  {
3563  wxLogWarning( wxString::Format(
3564  _( "The CADSTAR Hatching code '%s' has different step sizes for each "
3565  "hatch. KiCad only supports one step size for the hatching. The imported "
3566  "hatching uses the step size defined in the first hatching definition, "
3567  "i.e. %.2f mm." ),
3568  hcode.Name,
3569  (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).Step ) )
3570  / 1E6 ) );
3571  }
3572 
3573  if( abs( hcode.Hatches.at( 0 ).OrientAngle - hcode.Hatches.at( 1 ).OrientAngle )
3574  != 90000 )
3575  {
3576  wxLogWarning( wxString::Format(
3577  _( "The hatches in CADSTAR Hatching code '%s' have an angle "
3578  "difference of %.1f degrees. KiCad only supports hatching 90 "
3579  "degrees apart. The imported hatching has two hatches 90 "
3580  "degrees apart, oriented %.1f degrees from horizontal." ),
3581  hcode.Name,
3582  getAngleDegrees( abs( hcode.Hatches.at( 0 ).OrientAngle
3583  - hcode.Hatches.at( 1 ).OrientAngle ) ),
3584  getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle ) ) );
3585  }
3586  }
3587 
3588  m_hatchcodesTested.insert( aCadstarHatchcodeID );
3589  }
3590 }
3591 
3592 
3594  PCB_DIMENSION_BASE* aKiCadDim )
3595 {
3596  UNITS dimensionUnits = aCadstarDim.LinearUnits;
3597  TEXTCODE txtCode = getTextCode( aCadstarDim.Text.TextCodeID );
3598  int correctedHeight = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( txtCode.Height ) );
3599  wxSize txtSize( getKiCadLength( txtCode.Width ), correctedHeight );
3600  LINECODE linecode = Assignments.Codedefs.LineCodes.at( aCadstarDim.Line.LineCodeID );
3601 
3602  aKiCadDim->SetLayer( getKiCadLayer( aCadstarDim.LayerID ) );
3603  aKiCadDim->SetPrecision( aCadstarDim.Precision );
3604  aKiCadDim->SetStart( getKiCadPoint( aCadstarDim.ExtensionLineParams.Start ) );
3605  aKiCadDim->SetEnd( getKiCadPoint( aCadstarDim.ExtensionLineParams.End ) );
3606  aKiCadDim->SetExtensionOffset( getKiCadLength( aCadstarDim.ExtensionLineParams.Offset ) );
3607  aKiCadDim->SetLineThickness( getKiCadLength( linecode.Width ) );
3608  aKiCadDim->Text().SetTextThickness( getKiCadLength( txtCode.LineWidth ) );
3609  aKiCadDim->Text().SetTextSize( txtSize );
3610 
3611  // Find prefix and suffix:
3612  wxString prefix = wxEmptyString;
3613  wxString suffix = wxEmptyString;
3614  size_t startpos = aCadstarDim.Text.Text.Find( wxT( "<@DISTANCE" ) );
3615 
3616  if( startpos != wxNOT_FOUND )
3617  {
3618  prefix = ParseTextFields( aCadstarDim.Text.Text.SubString( 0, startpos - 1 ), &m_context );
3619  wxString remainingStr = aCadstarDim.Text.Text.Mid( startpos );
3620  size_t endpos = remainingStr.Find( "@>" );
3621  suffix = ParseTextFields( remainingStr.Mid( endpos + 2 ), &m_context );
3622  }
3623 
3624  if( suffix.StartsWith( wxT( "mm" ) ) )
3625  {
3627  suffix = suffix.Mid( 2 );
3628  }
3629  else
3630  {
3632  }
3633 
3634  aKiCadDim->SetPrefix( prefix );
3635  aKiCadDim->SetSuffix( suffix );
3636 
3637  if( aCadstarDim.LinearUnits == UNITS::DESIGN )
3638  {
3639  // For now we will hardcode the units as per the original CADSTAR design.
3640  // TODO: update this when KiCad supports design units
3642  dimensionUnits = Assignments.Technology.Units;
3643  }
3644 
3645  switch( dimensionUnits )
3646  {
3647  case UNITS::METER:
3648  case UNITS::CENTIMETER:
3649  case UNITS::MICROMETRE:
3650  wxLogWarning( wxString::Format( _( "Dimension ID %s uses a type of unit that "
3651  "is not supported in KiCad. Millimeters were "
3652  "applied instead." ),
3653  aCadstarDim.ID ) );
3655  case UNITS::MM:
3657  break;
3658 
3659  case UNITS::INCH:
3660  aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::INCHES );
3661  break;
3662 
3663  case UNITS::THOU:
3664  aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::MILS );
3665  break;
3666 
3667  case UNITS::DESIGN:
3668  wxFAIL_MSG( wxT( "We should have handled design units before coming here!" ) );
3669  break;
3670  }
3671 }
3672 
3674 {
3675  std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
3676 
3677  auto inflateValue =
3678  [&]( ZONE* aZoneA, ZONE* aZoneB )
3679  {
3680  int extra = getKiCadLength( Assignments.Codedefs.SpacingCodes.at( wxT( "C_C" ) ).Spacing )
3682 
3683  int retval = std::max( aZoneA->GetLocalClearance(), aZoneB->GetLocalClearance() );
3684 
3685  retval += extra;
3686 
3687  return retval;
3688  };
3689 
3690  // Find the error in fill area when guessing that aHigherZone gets filled before aLowerZone
3691  auto errorArea =
3692  [&]( ZONE* aLowerZone, ZONE* aHigherZone ) -> double
3693  {
3694  SHAPE_POLY_SET intersectShape( *aHigherZone->Outline() );
3695  intersectShape.Inflate( inflateValue( aLowerZone, aHigherZone ) , 32 );
3696 
3697  SHAPE_POLY_SET lowerZoneFill( aLowerZone->GetFilledPolysList( aLayer ) );
3698 
3699  SHAPE_POLY_SET lowerZoneOutline( *aLowerZone->Outline() );
3700 
3701  lowerZoneOutline.BooleanSubtract( intersectShape,
3703 
3704  lowerZoneFill.BooleanSubtract( lowerZoneOutline,
3706 
3707  double leftOverArea = lowerZoneFill.Area();
3708 
3709  return leftOverArea;
3710  };
3711 
3712  auto intersectionAreaOfZoneOutlines =
3713  [&]( ZONE* aZoneA, ZONE* aZoneB ) -> double
3714  {
3715  SHAPE_POLY_SET outLineA( *aZoneA->Outline() );
3716  outLineA.Inflate( inflateValue( aZoneA, aZoneB ), 32 );
3717 
3718  SHAPE_POLY_SET outLineB( *aZoneA->Outline() );
3719  outLineB.Inflate( inflateValue( aZoneA, aZoneB ), 32 );
3720 
3721  outLineA.BooleanIntersection( outLineB, SHAPE_POLY_SET::PM_FAST );
3722 
3723  return outLineA.Area();
3724  };
3725 
3726  // Lambda to determine if the zone with template ID 'a' is lower priority than 'b'
3727  auto isLowerPriority =
3728  [&]( const TEMPLATE_ID& a, const TEMPLATE_ID& b ) -> bool
3729  {
3730  return winningOverlaps[b].count( a ) > 0;
3731  };
3732 
3733  for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = m_zonesMap.begin();
3734  it1 != m_zonesMap.end(); ++it1 )
3735  {
3736  TEMPLATE& thisTemplate = Layout.Templates.at( it1->first );
3737  ZONE* thisZone = it1->second;
3738 
3739  if( !thisZone->GetLayerSet().Contains( aLayer ) )
3740  continue;
3741 
3742  for( std::map<TEMPLATE_ID, ZONE*>::iterator it2 = it1;
3743  it2 != m_zonesMap.end(); ++it2 )
3744  {
3745  TEMPLATE& otherTemplate = Layout.Templates.at( it2->first );
3746  ZONE* otherZone = it2->second;
3747 
3748  if( thisTemplate.ID == otherTemplate.ID )
3749  continue;
3750 
3751  if( !otherZone->GetLayerSet().Contains( aLayer ) )
3752  {
3753  checkPoint();
3754  continue;
3755  }
3756 
3757  if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) == 0 )
3758  {
3759  checkPoint();
3760  continue; // The zones do not interact in any way
3761  }
3762 
3763  SHAPE_POLY_SET thisZonePolyFill = thisZone->GetFilledPolysList( aLayer );
3764  SHAPE_POLY_SET otherZonePolyFill = otherZone->GetFilledPolysList( aLayer );
3765 
3766  if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
3767  {
3768  // Test if this zone were lower priority than other zone, what is the error?
3769  double areaThis = errorArea( thisZone, otherZone );
3770  // Viceversa
3771  double areaOther = errorArea( otherZone, thisZone );
3772 
3773  if( areaThis > areaOther )
3774  {
3775  // thisTemplate is filled before otherTemplate
3776  winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3777  }
3778  else
3779  {
3780  // thisTemplate is filled AFTER otherTemplate
3781  winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3782  }
3783  }
3784  else if( thisZonePolyFill.Area() > 0.0 )
3785  {
3786  // The other template is not filled, this one wins
3787  winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3788  }
3789  else if( otherZonePolyFill.Area() > 0.0 )
3790  {
3791  // This template is not filled, the other one wins
3792  winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3793  }
3794  else
3795  {
3796  // Neither of the templates is poured - use zone outlines instead (bigger outlines
3797  // get a lower priority)
3798  if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) != 0 )
3799  {
3800  if( thisZone->Outline()->Area() > otherZone->Outline()->Area() )
3801  winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3802  else
3803  winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3804  }
3805  }
3806 
3807  checkPoint();
3808  }
3809  }
3810 
3811  // Build a set of unique TEMPLATE_IDs of all the zones that intersect with another one
3812  std::set<TEMPLATE_ID> intersectingIDs;
3813 
3814  for( const std::pair<TEMPLATE_ID, std::set<TEMPLATE_ID>>& idPair : winningOverlaps )
3815  {
3816  intersectingIDs.insert( idPair.first );
3817  intersectingIDs.insert( idPair.second.begin(), idPair.second.end() );
3818  }
3819 
3820  // Now store them in a vector
3821  std::vector<TEMPLATE_ID> sortedIDs;
3822 
3823  for( const TEMPLATE_ID& id : intersectingIDs )
3824  {
3825  sortedIDs.push_back( id );
3826  }
3827 
3828  // sort by priority
3829  std::sort( sortedIDs.begin(), sortedIDs.end(), isLowerPriority );
3830 
3831  TEMPLATE_ID prevID = wxEmptyString;
3832 
3833  for( const TEMPLATE_ID& id : sortedIDs )
3834  {
3835  if( prevID.IsEmpty() )
3836  {
3837  prevID = id;
3838  continue;
3839  }
3840 
3841  wxASSERT( !isLowerPriority( id, prevID ) );
3842 
3843  int newPriority = m_zonesMap.at( prevID )->GetPriority();
3844 
3845  // Only increase priority of the current zone
3846  if( isLowerPriority( prevID, id ) )
3847  newPriority++;
3848 
3849  m_zonesMap.at( id )->SetPriority( newPriority );
3850  prevID = id;
3851  }
3852 
3853  // Verify
3854  for( const std::pair<TEMPLATE_ID, std::set<TEMPLATE_ID>>& idPair : winningOverlaps )
3855  {
3856  const TEMPLATE_ID& winningID = idPair.first;
3857 
3858  for( const TEMPLATE_ID& losingID : idPair.second )
3859  {
3860  if( m_zonesMap.at( losingID )->GetPriority()
3861  > m_zonesMap.at( winningID )->GetPriority() )
3862  {
3863  return false;
3864  }
3865  }
3866  }
3867 
3868  return true;
3869 }
3870 
3871 
3873  const COMPONENT_ID& aCadstarComponentID )
3874 {
3875  if( m_componentMap.find( aCadstarComponentID ) == m_componentMap.end() )
3876  return nullptr;
3877  else
3878  return m_componentMap.at( aCadstarComponentID );
3879 }
3880 
3881 
3882 wxPoint CADSTAR_PCB_ARCHIVE_LOADER::getKiCadPoint( const wxPoint& aCadstarPoint )
3883 {
3884  wxPoint retval;
3885 
3886  retval.x = ( aCadstarPoint.x - m_designCenter.x ) * KiCadUnitMultiplier;
3887  retval.y = -( aCadstarPoint.y - m_designCenter.y ) * KiCadUnitMultiplier;
3888 
3889  return retval;
3890 }
3891 
3892 
3893 double CADSTAR_PCB_ARCHIVE_LOADER::getPolarAngle( const wxPoint& aPoint )
3894 {
3895  return NormalizeAnglePos( ArcTangente( aPoint.y, aPoint.x ) );
3896 }
3897 
3898 
3900 {
3901  if( aCadstarNetID.IsEmpty() )
3902  {
3903  return nullptr;
3904  }
3905  else if( m_netMap.find( aCadstarNetID ) != m_netMap.end() )
3906  {
3907  return m_netMap.at( aCadstarNetID );
3908  }
3909  else
3910  {
3911  wxCHECK( Layout.Nets.find( aCadstarNetID ) != Layout.Nets.end(), nullptr );
3912 
3913  NET_PCB csNet = Layout.Nets.at( aCadstarNetID );
3914  wxString newName = csNet.Name;
3915 
3916  if( csNet.Name.IsEmpty() )
3917  {
3918  if( csNet.Pins.size() > 0 )
3919  {
3920  // Create default KiCad net naming:
3921 
3922  NET_PCB::PIN firstPin = ( *csNet.Pins.begin() ).second;
3923  //we should have already loaded the component with loadComponents() :
3924  FOOTPRINT* m = getFootprintFromCadstarID( firstPin.ComponentID );
3925  newName = wxT( "Net-(" );
3926  newName << m->Reference().GetText();
3927  newName << wxT( "-Pad" ) << wxString::Format( wxT( "%ld" ), firstPin.PadID );
3928  newName << wxT( ")" );
3929  }
3930  else
3931  {
3932  wxFAIL_MSG( wxT( "A net with no pins associated?" ) );
3933  newName = wxT( "csNet-" );
3934  newName << wxString::Format( wxT( "%i" ), csNet.SignalNum );
3935  }
3936  }
3937 
3938  if( !m_doneNetClassWarning && !csNet.NetClassID.IsEmpty()
3939  && csNet.NetClassID != wxT( "NONE" ) )
3940  {
3941  wxLogMessage( _( "The CADSTAR design contains nets with a 'Net Class' assigned. KiCad "
3942  "does not have an equivalent to CADSTAR's Net Class so these elements "
3943  "were not imported. Note: KiCad's version of 'Net Class' is closer to "
3944  "CADSTAR's 'Net Route Code' (which has been imported for all nets)." ) );
3945  m_doneNetClassWarning = true;
3946  }
3947 
3948  if( !m_doneSpacingClassWarning && !csNet.SpacingClassID.IsEmpty()
3949  && csNet.SpacingClassID != wxT( "NONE" ) )
3950  {
3951  wxLogWarning( _( "The CADSTAR design contains nets with a 'Spacing Class' assigned. "
3952  "KiCad does not have an equivalent to CADSTAR's Spacing Class so "
3953  "these elements were not imported. Please review the design rules as "
3954  "copper pours will affected by this." ) );
3956  }
3957 
3958  NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, newName, ++m_numNets );
3959  NETCLASSPTR netclass;
3960 
3961  std::tuple<ROUTECODE_ID, NETCLASS_ID, SPACING_CLASS_ID> key = { csNet.RouteCodeID,
3962  csNet.NetClassID,
3963  csNet.SpacingClassID };
3964 
3965  if( m_netClassMap.find( key ) != m_netClassMap.end() )
3966  {
3967  netclass = m_netClassMap.at( key );
3968  }
3969  else
3970  {
3971  wxString netClassName;
3972 
3973 
3974  ROUTECODE rc = getRouteCode( csNet.RouteCodeID );
3975  netClassName += wxT( "Route code: " ) + rc.Name;
3976 
3977  if( !csNet.NetClassID.IsEmpty() )
3978  {
3979  CADSTAR_NETCLASS nc = Assignments.Codedefs.NetClasses.at( csNet.NetClassID );
3980  netClassName += wxT( " | Net class: " ) + nc.Name;
3981  }
3982 
3983  if( !csNet.SpacingClassID.IsEmpty() )
3984  {
3985  SPCCLASSNAME sp = Assignments.Codedefs.SpacingClassNames.at( csNet.SpacingClassID );
3986  netClassName += wxT( " | Spacing class: " ) + sp.Name;
3987  }
3988 
3989  netclass.reset( new NETCLASS( *m_board->GetDesignSettings().GetDefault() ) );
3990  netclass->SetName( netClassName );
3991  m_board->GetDesignSettings().GetNetClasses().Add( netclass );
3992  netclass->SetTrackWidth( getKiCadLength( rc.OptimalWidth ) );
3993  m_netClassMap.insert( { key, netclass } );
3994  }
3995 
3996  netclass->Add( newName );
3997  netInfo->SetNetClass( netclass );
3998  m_board->Add( netInfo, ADD_MODE::APPEND );
3999  m_netMap.insert( { aCadstarNetID, netInfo } );
4000  return netInfo;
4001  }
4002 
4003  return nullptr;
4004 }
4005 
4006 
4008  bool aDetectMaxLayer )
4009 {
4010  if( aDetectMaxLayer && aLayerNum == m_numCopperLayers )
4011  return PCB_LAYER_ID::B_Cu;
4012 
4013  switch( aLayerNum )
4014  {
4015  case 1: return PCB_LAYER_ID::F_Cu;
4016  case 2: return PCB_LAYER_ID::In1_Cu;
4017  case 3: return PCB_LAYER_ID::In2_Cu;
4018  case 4: return PCB_LAYER_ID::In3_Cu;
4019  case 5: return PCB_LAYER_ID::In4_Cu;
4020  case 6: return PCB_LAYER_ID::In5_Cu;
4021  case 7: return PCB_LAYER_ID::In6_Cu;
4022  case 8: return PCB_LAYER_ID::In7_Cu;
4023  case 9: return PCB_LAYER_ID::In8_Cu;
4024  case 10: return PCB_LAYER_ID::In9_Cu;
4025  case 11: return PCB_LAYER_ID::In10_Cu;
4026  case 12: return PCB_LAYER_ID::In11_Cu;
4027  case 13: return PCB_LAYER_ID::In12_Cu;
4028  case 14: return PCB_LAYER_ID::In13_Cu;
4029  case 15: return PCB_LAYER_ID::In14_Cu;
4030  case 16: return PCB_LAYER_ID::In15_Cu;
4031  case 17: return PCB_LAYER_ID::In16_Cu;
4032  case 18: return PCB_LAYER_ID::In17_Cu;
4033  case 19: return PCB_LAYER_ID::In18_Cu;
4034  case 20: return PCB_LAYER_ID::In19_Cu;
4035  case 21: return PCB_LAYER_ID::In20_Cu;
4036  case 22: return PCB_LAYER_ID::In21_Cu;
4037  case 23: return PCB_LAYER_ID::In22_Cu;
4038  case 24: return PCB_LAYER_ID::In23_Cu;
4039  case 25: return PCB_LAYER_ID::In24_Cu;
4040  case 26: return PCB_LAYER_ID::In25_Cu;
4041  case 27: return PCB_LAYER_ID::In26_Cu;
4042  case 28: return PCB_LAYER_ID::In27_Cu;
4043  case 29: return PCB_LAYER_ID::In28_Cu;
4044  case 30: return PCB_LAYER_ID::In29_Cu;
4045  case 31: return PCB_LAYER_ID::In30_Cu;
4046  case 32: return PCB_LAYER_ID::B_Cu;
4047  }
4048 
4050 }
4051 
4052 
4053 bool CADSTAR_PCB_ARCHIVE_LOADER::isLayerSet( const LAYER_ID& aCadstarLayerID )
4054 {
4055  wxCHECK( Assignments.Layerdefs.Layers.find( aCadstarLayerID )
4056  != Assignments.Layerdefs.Layers.end(),
4057  false );
4058 
4059  LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID );
4060 
4061  switch( layer.Type )
4062  {
4063  case LAYER_TYPE::ALLDOC:
4064  case LAYER_TYPE::ALLELEC:
4065  case LAYER_TYPE::ALLLAYER:
4066  return true;
4067 
4068  default:
4069  return false;
4070  }
4071 
4072  return false;
4073 }
4074 
4075 
4077 {
4078  if( getLayerType( aCadstarLayerID ) == LAYER_TYPE::NOLAYER )
4079  {
4080  //The "no layer" is common for CADSTAR documentation symbols
4081  //map it to undefined layer for later processing
4083  }
4084 
4085  wxCHECK( m_layermap.find( aCadstarLayerID ) != m_layermap.end(),
4087 
4088  return m_layermap.at( aCadstarLayerID );
4089 }
4090 
4091 
4093 {
4094  LAYER_TYPE layerType = getLayerType( aCadstarLayerID );
4095 
4096  switch( layerType )
4097  {
4098  case LAYER_TYPE::ALLDOC:
4101 
4102  case LAYER_TYPE::ALLELEC:
4104 
4105  case LAYER_TYPE::ALLLAYER:
4106  return LSET::AllLayersMask()
4108  ^ ( LSET( PCB_LAYER_ID::Rescue ) );
4109 
4110  default:
4111  return LSET( getKiCadLayer( aCadstarLayerID ) );
4112  }
4113 }
4114 
4115 
4117  const GROUP_ID& aCadstarGroupID, BOARD_ITEM* aKiCadItem )
4118 {
4119  wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), );
4120 
4121  PCB_GROUP* parentGroup = m_groupMap.at( aCadstarGroupID );
4122  parentGroup->AddItem( aKiCadItem );
4123 }
4124 
4125 
4127  const wxString& aName )
4128 {
4129  wxString groupName = aName;
4130  int num = 0;
4131 
4132  while( m_groupMap.find( groupName ) != m_groupMap.end() )
4133  {
4134  groupName = aName + wxT( "_" ) + wxString::Format( wxT( "%i" ), ++num );
4135  }
4136 
4137  PCB_GROUP* docSymGroup = new PCB_GROUP( m_board );
4138  m_board->Add( docSymGroup );
4139  docSymGroup->SetName( groupName );
4140  GROUP_ID groupID( groupName );
4141  m_groupMap.insert( { groupID, docSymGroup } );
4142 
4143  return groupID;
4144 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:209
void SetReference(const wxString &aReference)
Definition: footprint.h:475
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:752
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:629
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:747
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:92
double getAngleDegrees(const long long &aCadstarAngle)
void SetEnd(const wxPoint &aEnd)
Definition: eda_shape.h:135
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:164
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:616
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:235
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:587
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:175
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:289
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:536
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:547
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:637
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:218
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:502
void SetLineThickness(int aWidth)
void SetDescription(const wxString &aDoc)
Definition: footprint.h:202
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:744
wxPoint GetCenter0() const
Definition: fp_shape.cpp:145
FP_TEXT & Reference()
Definition: footprint.h:503
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:110
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:516
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:748
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:492
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:1681
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:1491
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:466
#define _(s)
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:746
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:1725
void SetWidth(int aWidth)
Definition: eda_shape.h:97
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
Definition: