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