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 
28 #include <board_stackup_manager/stackup_predefined_prms.h> // KEY_COPPER, KEY_CORE, KEY_PREPREG
29 #include <board.h>
30 #include <dimension.h>
31 #include <pcb_shape.h>
32 #include <fp_shape.h>
33 #include <footprint.h>
34 #include <pcb_text.h>
35 #include <project.h>
36 #include <track.h>
37 #include <zone.h>
39 #include <trigo.h>
40 
41 #include <limits> // std::numeric_limits
42 
43 
45 {
46  m_board = aBoard;
47  m_project = aProject;
48 
49  Parse();
50 
52 
53  //Note: can't use getKiCadPoint() due wxPoint being int - need long long to make the check
54  long long designSizeXkicad = (long long) designLimit.x * KiCadUnitMultiplier;
55  long long designSizeYkicad = (long long) designLimit.y * KiCadUnitMultiplier;
56 
57  // Max size limited by the positive dimension of wxPoint (which is an int)
58  long long maxDesignSizekicad = std::numeric_limits<int>::max();
59 
60  if( designSizeXkicad > maxDesignSizekicad || designSizeYkicad > maxDesignSizekicad )
61  {
63  _( "The design is too large and cannot be imported into KiCad. \n"
64  "Please reduce the maximum design size in CADSTAR by navigating to: \n"
65  "Design Tab -> Properties -> Design Options -> Maximum Design Size. \n"
66  "Current Design size: %.2f, %.2f millimeters. \n"
67  "Maximum permitted design size: %.2f, %.2f millimeters.\n" ),
68  (double) designSizeXkicad / PCB_IU_PER_MM,
69  (double) designSizeYkicad / PCB_IU_PER_MM,
70  (double) maxDesignSizekicad / PCB_IU_PER_MM,
71  (double) maxDesignSizekicad / PCB_IU_PER_MM ) );
72  }
73 
76  / 2;
77 
79  {
80  wxLogWarning(
81  _( "The selected file indicates that nets might be out of synchronisation "
82  "with the schematic. It is recommended that you carry out an 'Align Nets' "
83  "procedure in CADSTAR and re-import, to avoid inconsistencies between the "
84  "PCB and the schematic. " ) );
85  }
86 
91  loadGroups();
92  loadBoards();
93  loadFigures();
94  loadTexts();
96  loadAreas();
99  loadTemplates();
100  loadCoppers();
102  loadNets();
104 
105  if( Layout.Trunks.size() > 0 )
106  {
107  wxLogWarning(
108  _( "The CADSTAR design contains Trunk routing elements, which have no KiCad "
109  "equivalent. These elements were not loaded." ) );
110  }
111 
112  if( Layout.VariantHierarchy.Variants.size() > 0 )
113  {
114  wxLogWarning( wxString::Format(
115  _( "The CADSTAR design contains variants which has no KiCad equivalent. Only "
116  "the variant '%s' was loaded." ),
117  Layout.VariantHierarchy.Variants.begin()->second.Name ) );
118  }
119 
120  if( Layout.ReuseBlocks.size() > 0 )
121  {
122  wxLogWarning(
123  _( "The CADSTAR design contains re-use blocks which has no KiCad equivalent. The "
124  "re-use block information has been discarded during the import." ) );
125  }
126 
127  wxLogMessage(
128  _( "The CADSTAR design has been imported successfully.\n"
129  "Please review the import errors and warnings (if any)." ) );
130 }
131 
133 {
134  std::vector<FOOTPRINT*> retval;
135 
136  for( std::pair<SYMDEF_ID, FOOTPRINT*> fpPair : m_libraryMap )
137  {
138  retval.push_back( static_cast<FOOTPRINT*>( fpPair.second->Clone() ) );
139  }
140 
141  return retval;
142 }
143 
144 
146  const wxString& aCadstarLayerName,
147  const PCB_LAYER_ID& aKiCadLayer )
148 {
149  if( m_logLayerWarnings )
150  {
151  wxLogWarning( wxString::Format(
152  _( "The CADSTAR layer '%s' has no KiCad equivalent. All elements on this "
153  "layer have been mapped to KiCad layer '%s' instead." ),
154  aCadstarLayerName, LSET::Name( aKiCadLayer ) ) );
155  }
156 }
157 
158 
159 void CADSTAR_PCB_ARCHIVE_LOADER::logBoardStackupMessage( const wxString& aCadstarLayerName,
160  const PCB_LAYER_ID& aKiCadLayer )
161 {
162  if( m_logLayerWarnings )
163  {
164  wxLogMessage( wxString::Format(
165  _( "The CADSTAR layer '%s' has been assumed to be a technical layer. All "
166  "elements on this layer have been mapped to KiCad layer '%s'." ),
167  aCadstarLayerName, LSET::Name( aKiCadLayer ) ) );
168  }
169 }
170 
171 
173  BOARD_STACKUP_ITEM* aKiCadItem,
174  int aDielectricSublayer )
175 {
176  if( !aCadstarLayer.MaterialId.IsEmpty() )
177  {
178  MATERIAL material = Assignments.Layerdefs.Materials.at( aCadstarLayer.MaterialId );
179 
180  aKiCadItem->SetMaterial( material.Name, aDielectricSublayer );
181  aKiCadItem->SetEpsilonR( material.Permittivity.GetDouble(), aDielectricSublayer );
182  aKiCadItem->SetLossTangent( material.LossTangent.GetDouble(), aDielectricSublayer );
183  //TODO add Resistivity when KiCad supports it
184  }
185 
186  aKiCadItem->SetLayerName( aCadstarLayer.Name );
187  aKiCadItem->SetThickness( getKiCadLength( aCadstarLayer.Thickness ), aDielectricSublayer );
188 }
189 
190 
192 {
193  // Structure describing an electrical layer with optional dielectric layers below it
194  // (construction layers in CADSTAR)
195  struct LAYER_BLOCK
196  {
197  LAYER_ID ElecLayerID = wxEmptyString; // Normally not empty, but could be empty if the
198  // first layer in the stackup is a construction
199  // layer
200  std::vector<LAYER_ID> ConstructionLayers; // Normally empty for the last electrical layer
201  // but it is possible to build a board in CADSTAR
202  // with no construction layers or with the bottom
203  // layer being a construction layer
204 
205  bool IsInitialised() { return !ElecLayerID.IsEmpty() || ConstructionLayers.size() > 0; };
206  };
207 
208  std::vector<LAYER_BLOCK> cadstarBoardStackup;
209  LAYER_BLOCK currentBlock;
210 
211  // Find the electrical and construction (dielectric) layers in the stackup
212  for( LAYER_ID cadstarLayerID : Assignments.Layerdefs.LayerStack )
213  {
214  LAYER cadstarLayer = Assignments.Layerdefs.Layers.at( cadstarLayerID );
215 
216  if( cadstarLayer.Type == LAYER_TYPE::JUMPERLAYER ||
217  cadstarLayer.Type == LAYER_TYPE::POWER ||
218  cadstarLayer.Type == LAYER_TYPE::ELEC )
219  {
220  if( currentBlock.IsInitialised() )
221  {
222  cadstarBoardStackup.push_back( currentBlock );
223  currentBlock = LAYER_BLOCK(); // reset the block
224  }
225 
226  currentBlock.ElecLayerID = cadstarLayerID;
227  }
228  else if( cadstarLayer.Type == LAYER_TYPE::CONSTRUCTION )
229  {
230  currentBlock.ConstructionLayers.push_back( cadstarLayerID );
231  }
232  }
233 
234  if( currentBlock.IsInitialised() )
235  cadstarBoardStackup.push_back( currentBlock );
236 
237  int totalCopperLayers = cadstarBoardStackup.size();
238 
239  // Special case: last layer in the stackup is a construction layer, we need to use B.Cu as a
240  // dummy layer
241  if( cadstarBoardStackup.back().ConstructionLayers.size() > 0 )
242  {
243  cadstarBoardStackup.push_back( LAYER_BLOCK() ); //Add dummy layer at the end
244  ++totalCopperLayers;
245  }
246 
247  // Make sure it is an even number of layers (KiCad doesn't yet support unbalanced stack-ups)
248  if( ( totalCopperLayers % 2 ) != 0 )
249  {
250  LAYER_BLOCK bottomLayer = cadstarBoardStackup.back();
251  cadstarBoardStackup.pop_back();
252 
253  LAYER_BLOCK secondToLastLayer = cadstarBoardStackup.back();
254  cadstarBoardStackup.pop_back();
255 
256  LAYER_BLOCK dummyLayer;
257  LAYER_ID lastConstruction = secondToLastLayer.ConstructionLayers.back();
258 
259  if( secondToLastLayer.ConstructionLayers.size() > 1 )
260  {
261  // At least two construction layers, lets remove it here and use it in the dummy layer
262  secondToLastLayer.ConstructionLayers.pop_back();
263  }
264  else
265  {
266  // There is only one construction layer, lets halve its thickness so it is split evenly
267  // between this layer and the dummy layer
268  Assignments.Layerdefs.Layers.at( lastConstruction ).Thickness /= 2;
269  }
270 
271  dummyLayer.ConstructionLayers.push_back( lastConstruction );
272  cadstarBoardStackup.push_back( secondToLastLayer );
273  cadstarBoardStackup.push_back( dummyLayer );
274  cadstarBoardStackup.push_back( bottomLayer );
275  ++totalCopperLayers;
276  }
277 
278  wxASSERT( totalCopperLayers == cadstarBoardStackup.size() );
279  wxASSERT( cadstarBoardStackup.back().ConstructionLayers.size() == 0 );
280 
281 
282  // Create a new stackup from default stackup list
284  stackup.RemoveAll();
287  m_board->SetCopperLayerCount( totalCopperLayers );
288  stackup.BuildDefaultStackupList( &m_board->GetDesignSettings(), totalCopperLayers );
289 
290  size_t stackIndex = 0;
291 
292  for( BOARD_STACKUP_ITEM* item : stackup.GetList() )
293  {
294  if( item->GetType() == BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_COPPER )
295  {
296  LAYER_ID layerID = cadstarBoardStackup.at( stackIndex ).ElecLayerID;
297 
298  if( layerID.IsEmpty() )
299  {
300  // Loading a dummy layer. Make zero thickness so it doesn't affect overall stackup
301  item->SetThickness( 0 );
302  }
303  else
304  {
305  LAYER copperLayer = Assignments.Layerdefs.Layers.at( layerID );
306  initStackupItem( copperLayer, item, 0 );
307  LAYER_T copperType = LAYER_T::LT_SIGNAL;
308 
309  switch( copperLayer.Type )
310  {
312  copperType = LAYER_T::LT_JUMPER;
313  break;
314 
315  case LAYER_TYPE::ELEC:
316  copperType = LAYER_T::LT_SIGNAL;
317  break;
318 
319  case LAYER_TYPE::POWER:
320  copperType = LAYER_T::LT_POWER;
321  m_powerPlaneLayers.push_back( copperLayer.ID ); //need to add a Copper zone
322  break;
323 
324  default:
325  wxFAIL_MSG( "Unexpected Layer type. Was expecting an electrical type" );
326  break;
327  }
328 
329  m_board->SetLayerType( item->GetBrdLayerId(), copperType );
330  m_board->SetLayerName( item->GetBrdLayerId(), item->GetLayerName() );
331  m_layermap.insert( { copperLayer.ID, item->GetBrdLayerId() } );
332  }
333  }
334  else if( item->GetType() == BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_DIELECTRIC )
335  {
336  LAYER_BLOCK layerBlock = cadstarBoardStackup.at( stackIndex );
337  LAYER_BLOCK layerBlockBelow = cadstarBoardStackup.at( stackIndex + 1 );
338 
339  // We should have made sure all layer blocks have at least one construction layer
340  wxASSERT( layerBlock.ConstructionLayers.size() > 0 );
341 
342  int dielectricId = stackIndex + 1;
343  // item->SetBrdLayerId();
344  item->SetDielectricLayerId( dielectricId );
345 
346  //Prepreg or core?
347  //Look at CADSTAR layer embedding (see LAYER->Embedding) to check whether the electrical
348  //layer embeds above and below to decide if current layer is prepreg or core
349  if( layerBlock.ElecLayerID.IsEmpty() )
350  {
351  //Dummy electrical layer, assume prepreg
352  item->SetTypeName( KEY_PREPREG );
353  }
354  else
355  {
356  LAYER copperLayer = Assignments.Layerdefs.Layers.at( layerBlock.ElecLayerID );
357 
358  if( layerBlockBelow.ElecLayerID.IsEmpty() )
359  {
360  // Dummy layer below, just use current layer to decide
361 
362  if( copperLayer.Embedding == EMBEDDING::ABOVE )
363  item->SetTypeName( KEY_CORE );
364  else
365  item->SetTypeName( KEY_PREPREG );
366  }
367  else
368  {
369  LAYER copperLayerBelow =
370  Assignments.Layerdefs.Layers.at( layerBlockBelow.ElecLayerID );
371 
372  if( copperLayer.Embedding == EMBEDDING::ABOVE )
373  {
374  // Need to check layer below is embedding downwards
375  if( copperLayerBelow.Embedding == EMBEDDING::BELOW )
376  item->SetTypeName( KEY_CORE );
377  else
378  item->SetTypeName( KEY_PREPREG );
379  }
380  else
381  {
382  item->SetTypeName( KEY_PREPREG );
383  }
384  }
385  }
386 
387  int dielectricSublayer = 0;
388 
389  for( LAYER_ID constructionLaID : layerBlock.ConstructionLayers )
390  {
391  LAYER dielectricLayer = Assignments.Layerdefs.Layers.at( constructionLaID );
392 
393  if( dielectricSublayer )
394  item->AddDielectricPrms( dielectricSublayer );
395 
396  initStackupItem( dielectricLayer, item, dielectricSublayer );
397  m_board->SetLayerName( item->GetBrdLayerId(), item->GetLayerName() );
398  m_layermap.insert( { dielectricLayer.ID, item->GetBrdLayerId() } );
399  ++dielectricSublayer;
400  }
401 
402  ++stackIndex;
403  }
404  }
405 
406  int numElecAndPowerLayers = 0;
407 
408  for( LAYER_ID cadstarLayerID : Assignments.Layerdefs.LayerStack )
409  {
410  LAYER curLayer = Assignments.Layerdefs.Layers.at( cadstarLayerID );
412  wxString layerName = curLayer.Name.Lower();
413 
414  enum class LOG_LEVEL
415  {
416  NONE,
417  MSG,
418  WARN
419  };
420 
421  auto selectLayerID =
422  [&]( PCB_LAYER_ID aFront, PCB_LAYER_ID aBack, LOG_LEVEL aLogType )
423  {
424  if( numElecAndPowerLayers > 0 )
425  kicadLayerID = aBack;
426  else
427  kicadLayerID = aFront;
428 
429  switch( aLogType )
430  {
431  case LOG_LEVEL::NONE:
432  break;
433 
434  case LOG_LEVEL::MSG:
435  logBoardStackupMessage( curLayer.Name, kicadLayerID );
436  break;
437 
438  case LOG_LEVEL::WARN:
439  logBoardStackupMessage( curLayer.Name, kicadLayerID );
440  break;
441  }
442  };
443 
444  switch( curLayer.Type )
445  {
446  case LAYER_TYPE::ALLDOC:
447  case LAYER_TYPE::ALLELEC:
450  case LAYER_TYPE::NOLAYER:
451  //Shouldn't be here if CPA file is correctly parsed and not corrupt
453  _( "Unexpected layer '%s' in layer stack." ), curLayer.Name ) );
454  break;
455 
457  case LAYER_TYPE::ELEC:
458  case LAYER_TYPE::POWER:
459  ++numElecAndPowerLayers;
462  //Already dealt with these when loading board stackup
463  break;
464 
465  case LAYER_TYPE::DOC:
466  selectLayerID( PCB_LAYER_ID::Dwgs_User, PCB_LAYER_ID::Cmts_User, LOG_LEVEL::WARN );
467  break;
468 
469  case LAYER_TYPE::NONELEC:
470  switch( curLayer.SubType )
471  {
474  break;
475 
478  break;
479 
481  // Generic Non-electrical layer (older CADSTAR versions).
482  // Attempt to detect technical layers by string matching.
483  if( layerName.Contains( "glue" ) || layerName.Contains( "adhesive" ) )
484  {
485  selectLayerID( PCB_LAYER_ID::F_Adhes, PCB_LAYER_ID::B_Adhes, LOG_LEVEL::MSG );
486  }
487  else if( layerName.Contains( "silk" ) || layerName.Contains( "legend" ) )
488  {
489  selectLayerID( PCB_LAYER_ID::F_SilkS, PCB_LAYER_ID::B_SilkS, LOG_LEVEL::MSG );
490  }
491  else if( layerName.Contains( "assembly" ) || layerName.Contains( "fabrication" ) )
492  {
493  selectLayerID( PCB_LAYER_ID::F_Fab, PCB_LAYER_ID::B_Fab, LOG_LEVEL::MSG );
494  }
495  else if( layerName.Contains( "resist" ) || layerName.Contains( "mask" ) )
496  {
497  selectLayerID( PCB_LAYER_ID::F_Mask, PCB_LAYER_ID::B_Mask, LOG_LEVEL::MSG );
498  }
499  else if( layerName.Contains( "paste" ) )
500  {
501  selectLayerID( PCB_LAYER_ID::F_Paste, PCB_LAYER_ID::B_Paste, LOG_LEVEL::MSG );
502  }
503  else
504  {
505  // Does not appear to be a technical layer - Map to Eco layers for now.
507  LOG_LEVEL::WARN );
508  }
509  break;
510 
512  selectLayerID( PCB_LAYER_ID::F_Paste, PCB_LAYER_ID::B_Paste, LOG_LEVEL::MSG );
513  break;
514 
516  selectLayerID( PCB_LAYER_ID::F_SilkS, PCB_LAYER_ID::B_SilkS, LOG_LEVEL::MSG );
517  break;
518 
520  selectLayerID( PCB_LAYER_ID::F_Mask, PCB_LAYER_ID::B_Mask, LOG_LEVEL::MSG );
521  break;
522 
525  //Unsure what these layer types are used for. Map to Eco layers for now.
526  selectLayerID( PCB_LAYER_ID::Eco1_User, PCB_LAYER_ID::Eco2_User, LOG_LEVEL::WARN );
527  break;
528 
529  default:
530  wxFAIL_MSG( "Unknown CADSTAR Layer Sub-type" );
531  break;
532  }
533  break;
534 
535  default:
536  wxFAIL_MSG( "Unknown CADSTAR Layer Type" );
537  break;
538  }
539 
540  m_layermap.insert( { curLayer.ID, kicadLayerID } );
541  }
542 }
543 
544 
546 {
547  LSET enabledLayers = m_board->GetEnabledLayers();
548  LSET validRemappingLayers = enabledLayers | LSET::AllBoardTechMask() |
550 
551  std::vector<INPUT_LAYER_DESC> inputLayers;
552  std::map<wxString, LAYER_ID> cadstarLayerNameMap;
553 
554  for( std::pair<LAYER_ID, PCB_LAYER_ID> layerPair : m_layermap )
555  {
556  LAYER* curLayer = &Assignments.Layerdefs.Layers.at( layerPair.first );
557 
558  //Only remap layers that we aren't sure about
559  if( curLayer->Type == LAYER_TYPE::DOC
560  || ( curLayer->Type == LAYER_TYPE::NONELEC
561  && curLayer->SubType == LAYER_SUBTYPE::LAYERSUBTYPE_NONE )
562  || ( curLayer->Type == LAYER_TYPE::NONELEC
563  && curLayer->SubType == LAYER_SUBTYPE::LAYERSUBTYPE_ROUT )
564  || ( curLayer->Type == LAYER_TYPE::NONELEC
566  {
567  INPUT_LAYER_DESC iLdesc;
568  iLdesc.Name = curLayer->Name;
569  iLdesc.PermittedLayers = validRemappingLayers;
570  iLdesc.AutoMapLayer = layerPair.second;
571 
572  inputLayers.push_back( iLdesc );
573  cadstarLayerNameMap.insert( { curLayer->Name, curLayer->ID } );
574  }
575  }
576 
577  if( inputLayers.size() == 0 )
578  return;
579 
580  // Callback:
581  std::map<wxString, PCB_LAYER_ID> reMappedLayers = m_layerMappingHandler( inputLayers );
582 
583  for( std::pair<wxString, PCB_LAYER_ID> layerPair : reMappedLayers )
584  {
585  if( layerPair.second == PCB_LAYER_ID::UNDEFINED_LAYER )
586  {
587  wxFAIL_MSG( "Unexpected Layer ID" );
588  continue;
589  }
590 
591  LAYER_ID cadstarLayerID = cadstarLayerNameMap.at( layerPair.first );
592  m_layermap.at( cadstarLayerID ) = layerPair.second;
593  enabledLayers |= LSET( layerPair.second );
594  }
595 
596  m_board->SetEnabledLayers( enabledLayers );
597  m_board->SetVisibleLayers( enabledLayers );
598 }
599 
600 
602 {
604  std::map<SPACINGCODE_ID, SPACINGCODE>& spacingCodes = Assignments.Codedefs.SpacingCodes;
605 
606  auto applyRule =
607  [&]( wxString aID, int* aVal )
608  {
609  if( spacingCodes.find( aID ) == spacingCodes.end() )
610  wxLogWarning( _( "Design rule %s was not found. This was ignored." ) );
611  else
612  *aVal = getKiCadLength( spacingCodes.at( aID ).Spacing );
613  };
614 
615  //Note: for details on the different spacing codes see SPACINGCODE::ID
616 
617  applyRule( "T_T", &ds.m_MinClearance );
618  applyRule( "C_B", &ds.m_CopperEdgeClearance );
619  applyRule( "H_H", &ds.m_HoleToHoleMin );
620 
622  ds.m_ViasMinSize = ds.m_TrackMinWidth; // Not specified, assumed same as track width
623  ds.m_ViasMinAnnulus = ds.m_TrackMinWidth / 2; // Not specified, assumed half track width
624  ds.m_MinThroughDrill = 0; // CADSTAR does not specify a minimum hole size
625  ds.m_HoleClearance = ds.m_CopperEdgeClearance; // Not specified, assumed same as edge
626 
627  auto applyNetClassRule = [&]( wxString aID, NETCLASS* aNetClassPtr,
628  void ( NETCLASS::*aFunc )( int ) ) {
629  int value = -1;
630  applyRule( aID, &value );
631 
632  if( value != -1 )
633  ( aNetClassPtr->*aFunc )( value );
634  };
635 
636  applyNetClassRule( "T_T", ds.GetDefault(), &::NETCLASS::SetClearance );
637 
639 
640  wxLogWarning( _( "KiCad design rules are different from CADSTAR ones. Only the compatible "
641  "design rules were imported. It is recommended that you review the design "
642  "rules that have been applied." ) );
643 }
644 
645 
647 {
648  for( std::pair<SYMDEF_ID, SYMDEF_PCB> symPair : Library.ComponentDefinitions )
649  {
650  SYMDEF_ID key = symPair.first;
651  SYMDEF_PCB component = symPair.second;
652  wxString fpName = component.ReferenceName + ( ( component.Alternate.size() > 0 ) ?
653  ( wxT( " (" ) + component.Alternate + wxT( ")" ) ) :
654  wxT( "" ) );
655  FOOTPRINT* footprint = new FOOTPRINT( m_board );
656  footprint->SetPosition( getKiCadPoint( component.Origin ) );
657 
658  LIB_ID libID;
659  libID.Parse( fpName, true );
660 
661  footprint->SetFPID( libID );
662  loadLibraryFigures( component, footprint );
663  loadLibraryCoppers( component, footprint );
664  loadLibraryAreas( component, footprint );
665  loadLibraryPads( component, footprint );
666 
667  m_libraryMap.insert( std::make_pair( key, footprint ) );
668  }
669 }
670 
671 
673  FOOTPRINT* aFootprint )
674 {
675  for( std::pair<FIGURE_ID, FIGURE> figPair : aComponent.Figures )
676  {
677  FIGURE& fig = figPair.second;
678 
681  wxString::Format( "Component %s:%s -> Figure %s",
682  aComponent.ReferenceName,
683  aComponent.Alternate,
684  fig.ID ),
685  aFootprint );
686  }
687 }
688 
689 
691 {
692  for( COMPONENT_COPPER compCopper : aComponent.ComponentCoppers )
693  {
694  int lineThickness = getKiCadLength( getCopperCode( compCopper.CopperCodeID ).CopperWidth );
695 
696  drawCadstarShape( compCopper.Shape, getKiCadLayer( compCopper.LayerID ), lineThickness,
697  wxString::Format( "Component %s:%s -> Copper element",
698  aComponent.ReferenceName,
699  aComponent.Alternate ),
700  aFootprint );
701  }
702 }
703 
704 
706  FOOTPRINT* aFootprint )
707 {
708  for( std::pair<COMP_AREA_ID, COMPONENT_AREA> areaPair : aComponent.ComponentAreas )
709  {
710  COMPONENT_AREA& area = areaPair.second;
711 
712  if( area.NoVias || area.NoTracks )
713  {
715  aFootprint );
716 
717  aFootprint->Add( zone, ADD_MODE::APPEND );
718 
719  if( isLayerSet( area.LayerID ) )
720  zone->SetLayerSet( getKiCadLayerSet( area.LayerID ) );
721  else
722  zone->SetLayer( getKiCadLayer( area.LayerID ) );
723 
724  zone->SetIsRuleArea( true ); //import all CADSTAR areas as Keepout zones
725  zone->SetDoNotAllowPads( false ); //no CADSTAR equivalent
726  zone->SetZoneName( area.ID );
727 
728  //There is no distinction between tracks and copper pours in CADSTAR Keepout zones
729  zone->SetDoNotAllowTracks( area.NoTracks );
730  zone->SetDoNotAllowCopperPour( area.NoTracks );
731 
732  zone->SetDoNotAllowVias( area.NoVias );
733  }
734  else
735  {
736  wxString libName = aComponent.ReferenceName;
737 
738  if( !aComponent.Alternate.IsEmpty() )
739  libName << wxT( " (" ) << aComponent.Alternate << wxT( ")" );
740 
741  wxLogError(
742  wxString::Format( _( "The CADSTAR area '%s' in library component '%s' does not "
743  "have a KiCad equivalent. The area is neither a via nor "
744  "route keepout area. The area was not imported." ),
745  area.ID, libName ) );
746  }
747  }
748 }
749 
750 
752  FOOTPRINT* aFootprint )
753 {
754  for( std::pair<PAD_ID, COMPONENT_PAD> padPair : aComponent.ComponentPads )
755  {
756  PAD* pad = getKiCadPad( padPair.second, aFootprint );
757  aFootprint->Add( pad, ADD_MODE::INSERT ); // insert so that we get correct behaviour
758  // when finding pads by PAD_ID - see loadNets()
759  }
760 }
761 
762 
764 {
765  PADCODE csPadcode = getPadCode( aCadstarPad.PadCodeID );
766 
767  PAD* pad = new PAD( aParent );
768 
769  switch( aCadstarPad.Side )
770  {
771  case PAD_SIDE::MAXIMUM: //Bottom side
773  pad->SetLayerSet( LSET( 3, B_Cu, B_Paste, B_Mask ) );
774  break;
775 
776  case PAD_SIDE::MINIMUM: //TOP side
778  pad->SetLayerSet( LSET( 3, F_Cu, F_Paste, F_Mask ) );
779  break;
780 
782  if( csPadcode.Plated )
784  else
786 
787  pad->SetLayerSet( pad->PTHMask() ); // for now we will assume no paste layers
788  //TODO: We need to read the csPad->Reassigns vector to make sure no paste
789  break;
790 
791  default:
792  wxFAIL_MSG( "Unknown Pad type" );
793  }
794 
795  pad->SetName( aCadstarPad.Identifier.IsEmpty() ?
796  wxString::Format( wxT( "%ld" ), aCadstarPad.ID ) :
797  aCadstarPad.Identifier );
798 
799  if( csPadcode.Shape.Size == 0 )
800  {
801  if( csPadcode.DrillDiameter == UNDEFINED_VALUE
802  && aCadstarPad.Side == PAD_SIDE::THROUGH_HOLE )
803  {
804  // Through-hole, zero sized pad?. Lets load this just on the F_Mask for now to
805  // prevent DRC errors.
806  // TODO: This could be a custom padstack, update when KiCad supports padstacks
808  pad->SetLayerSet( LSET( 1, F_Mask ) );
809  }
810 
811  // zero sized pads seems to break KiCad so lets make it very small instead
812  csPadcode.Shape.Size = 1;
813  }
814 
815  wxPoint padOffset = { 0, 0 }; // offset of the pad origin (before rotating)
816  wxPoint drillOffset = { 0, 0 }; // offset of the drill origin w.r.t. the pad (before rotating)
817 
818  switch( csPadcode.Shape.ShapeType )
819  {
821  //todo fix: use custom shape instead (Donught shape, i.e. a circle with a hole)
823  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
824  getKiCadLength( csPadcode.Shape.Size ) } );
825  break;
826 
829  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
830  + (long long) csPadcode.Shape.LeftLength
831  + (long long) csPadcode.Shape.RightLength ),
832  getKiCadLength( csPadcode.Shape.Size ) } );
835  pad->SetRoundRectRadiusRatio( 0.5 );
836  pad->SetChamferRectRatio( 0.0 );
837 
838  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
839  ( (long long) csPadcode.Shape.RightLength / 2 ) );
840  break;
841 
844  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
845  getKiCadLength( csPadcode.Shape.Size ) } );
846  break;
847 
849  {
850  // Cadstar diamond shape is a square rotated 45 degrees
851  // We convert it in KiCad to a square with chamfered edges
852  int sizeOfSquare = (double) getKiCadLength( csPadcode.Shape.Size ) * sqrt(2.0);
854  pad->SetChamferRectRatio( 0.5 );
855  pad->SetSize( { sizeOfSquare, sizeOfSquare } );
856 
857  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
858  ( (long long) csPadcode.Shape.RightLength / 2 ) );
859  }
860  break;
861 
864  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
865  + (long long) csPadcode.Shape.LeftLength
866  + (long long) csPadcode.Shape.RightLength ),
867  getKiCadLength( csPadcode.Shape.Size ) } );
868 
869  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
870  ( (long long) csPadcode.Shape.RightLength / 2 ) );
871  break;
872 
876  pad->SetChamferRectRatio( 0.25 );
877  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
878  getKiCadLength( csPadcode.Shape.Size ) } );
879  break;
880 
883  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
884  + (long long) csPadcode.Shape.LeftLength
885  + (long long) csPadcode.Shape.RightLength ),
886  getKiCadLength( csPadcode.Shape.Size ) } );
887 
888  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
889  ( (long long) csPadcode.Shape.RightLength / 2 ) );
890  break;
891 
895  pad->SetSize( { getKiCadLength( (long long) csPadcode.Shape.Size
896  + (long long) csPadcode.Shape.LeftLength
897  + (long long) csPadcode.Shape.RightLength ),
898  getKiCadLength( csPadcode.Shape.Size ) } );
899 
900  padOffset.x = getKiCadLength( ( (long long) csPadcode.Shape.LeftLength / 2 ) -
901  ( (long long) csPadcode.Shape.RightLength / 2 ) );
902  break;
903 
904 
907  pad->SetSize( { getKiCadLength( csPadcode.Shape.Size ),
908  getKiCadLength( csPadcode.Shape.Size ) } );
909  break;
910 
911  default:
912  wxFAIL_MSG( "Unknown Pad Shape" );
913  }
914 
915  if( csPadcode.ReliefClearance != UNDEFINED_VALUE )
916  pad->SetThermalGap( getKiCadLength( csPadcode.ReliefClearance ) );
917 
918  if( csPadcode.ReliefWidth != UNDEFINED_VALUE )
919  pad->SetThermalSpokeWidth( getKiCadLength( csPadcode.ReliefWidth ) );
920 
921 
922  if( csPadcode.DrillDiameter != UNDEFINED_VALUE )
923  {
924  if( csPadcode.SlotLength != UNDEFINED_VALUE )
925  {
927  pad->SetDrillSize( { getKiCadLength( (long long) csPadcode.SlotLength +
928  (long long) csPadcode.DrillDiameter ),
929  getKiCadLength( csPadcode.DrillDiameter ) } );
930  }
931  else
932  {
934  pad->SetDrillSize( { getKiCadLength( csPadcode.DrillDiameter ),
935  getKiCadLength( csPadcode.DrillDiameter ) } );
936  }
937 
938  drillOffset.x = -getKiCadLength( csPadcode.DrillXoffset );
939  drillOffset.y = getKiCadLength( csPadcode.DrillYoffset );
940  }
941  else
942  {
943  pad->SetDrillSize( { 0, 0 } );
944  }
945 
946 
947  if( csPadcode.SlotOrientation != 0 )
948  {
949  LSET lset = pad->GetLayerSet();
950  lset &= LSET::AllCuMask();
951 
952  if( lset.size() > 0 )
953  {
954  SHAPE_POLY_SET padOutline;
955  PCB_LAYER_ID layer = lset.Seq().at( 0 );
956  int maxError = m_board->GetDesignSettings().m_MaxError;
957 
958  pad->SetPosition( { 0, 0 } );
959  pad->SetPos0( { 0, 0 } );
960  pad->TransformShapeWithClearanceToPolygon( padOutline, layer, 0, maxError,
962 
963  PCB_SHAPE* padShape = new PCB_SHAPE;
964  padShape->SetShape( S_POLYGON );
965  padShape->SetFilled( true );
966  padShape->SetPolyShape( padOutline );
967  padShape->SetWidth( 0 );
968  padShape->Move( padOffset - drillOffset );
969  padShape->Rotate( wxPoint( 0, 0 ), 1800.0 - getAngleTenthDegree( csPadcode.SlotOrientation ) );
970 
971 
972  SHAPE_POLY_SET editedPadOutline = padShape->GetPolyShape();
973 
974  if( editedPadOutline.Contains( { 0, 0 } ) )
975  {
977  pad->SetSize( wxSize( { 4, 4 } ) );
979  pad->AddPrimitive( padShape );
980  padOffset = { 0, 0 };
981  }
982  else
983  {
984  // The CADSTAR pad has the hole shape outside the pad shape
985  // Lets just put the hole in the center of the pad instead
986  csPadcode.SlotOrientation = 0;
987  drillOffset = { 0, 0 };
988 
989  if( m_padcodesTested.find( csPadcode.ID ) == m_padcodesTested.end() )
990  {
991  wxLogError( wxString::Format(
992  _( "The CADSTAR pad definition '%s' has the hole shape outside the "
993  "pad shape. The hole has been moved to the center of the pad." ),
994  csPadcode.Name ) );
995 
996  m_padcodesTested.insert( csPadcode.ID );
997  }
998  }
999 
1000  }
1001  else
1002  {
1003  wxFAIL_MSG( "No copper layers defined in the pad?" );
1004  csPadcode.SlotOrientation = 0;
1005  pad->SetOffset( drillOffset );
1006  }
1007  }
1008  else
1009  {
1010  pad->SetOffset( drillOffset );
1011  }
1012 
1013  double padOrientation = getAngleTenthDegree( aCadstarPad.OrientAngle )
1014  + getAngleTenthDegree( csPadcode.Shape.OrientAngle );
1015 
1016  RotatePoint( &padOffset, padOrientation );
1017  RotatePoint( &drillOffset, padOrientation );
1018  pad->SetPos0( getKiCadPoint( aCadstarPad.Position ) - aParent->GetPosition() - padOffset
1019  - drillOffset );
1020  pad->SetOrientation( padOrientation + getAngleTenthDegree( csPadcode.SlotOrientation ) );
1021 
1022  //TODO handle csPadcode.Reassigns when KiCad supports full padstacks
1023 
1024  return pad;
1025 }
1026 
1027 
1029 {
1030  for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1031  {
1032  GROUP& csGroup = groupPair.second;
1033 
1034  PCB_GROUP* kiGroup = new PCB_GROUP( m_board );
1035 
1036  m_board->Add( kiGroup );
1037  kiGroup->SetName( csGroup.Name );
1038  kiGroup->SetLocked( csGroup.Fixed );
1039 
1040  m_groupMap.insert( { csGroup.ID, kiGroup } );
1041  }
1042 
1043  //now add any groups to their parent group
1044  for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1045  {
1046  GROUP& csGroup = groupPair.second;
1047 
1048  if( !csGroup.GroupID.IsEmpty() )
1049  {
1050  if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1051  {
1053  _( "The file appears to be corrupt. Unable to find group ID %s "
1054  "in the group definitions." ),
1055  csGroup.ID ) );
1056  }
1057  else if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1058  {
1060  _( "The file appears to be corrupt. Unable to find sub group %s "
1061  "in the group map (parent group ID=%s, Name=%s)." ),
1062  csGroup.GroupID, csGroup.ID, csGroup.Name ) );
1063  }
1064  else
1065  {
1066  PCB_GROUP* kiCadGroup = m_groupMap.at( csGroup.ID );
1067  PCB_GROUP* parentGroup = m_groupMap.at( csGroup.GroupID );
1068  parentGroup->AddItem( kiCadGroup );
1069  }
1070  }
1071  }
1072 }
1073 
1074 
1076 {
1077  for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
1078  {
1079  CADSTAR_BOARD& board = boardPair.second;
1080  GROUP_ID boardGroup = createUniqueGroupID( wxT( "Board" ) );
1082  getLineThickness( board.LineCodeID ), wxString::Format( "BOARD %s", board.ID ),
1083  m_board, boardGroup );
1084 
1085  if( !board.GroupID.IsEmpty() )
1086  {
1087  addToGroup( board.GroupID, getKiCadGroup( boardGroup ) );
1088  }
1089 
1090  //TODO process board attributes when KiCad supports them
1091  }
1092 }
1093 
1094 
1096 {
1097  for( std::pair<FIGURE_ID, FIGURE> figPair : Layout.Figures )
1098  {
1099  FIGURE& fig = figPair.second;
1101  getLineThickness( fig.LineCodeID ), wxString::Format( "FIGURE %s", fig.ID ), m_board,
1102  fig.GroupID );
1103 
1104  //TODO process "swaprule" (doesn't seem to apply to Layout Figures?)
1105  //TODO process re-use block when KiCad Supports it
1106  //TODO process attributes when KiCad Supports attributes in figures
1107  }
1108 }
1109 
1110 
1112 {
1113  for( std::pair<TEXT_ID, TEXT> txtPair : Layout.Texts )
1114  {
1115  TEXT& csTxt = txtPair.second;
1116  drawCadstarText( csTxt, m_board );
1117  }
1118 }
1119 
1120 
1122 {
1123  for( std::pair<DIMENSION_ID, DIMENSION> dimPair : Layout.Dimensions )
1124  {
1125  DIMENSION& csDim = dimPair.second;
1126 
1127  switch( csDim.Type )
1128  {
1129  case DIMENSION::TYPE::LINEARDIM:
1130  switch( csDim.Subtype )
1131  {
1132  case DIMENSION::SUBTYPE::ANGLED:
1133  wxLogWarning( wxString::Format( _( "Dimension ID %s is an angled dimension, which "
1134  "has no KiCad equivalent. An aligned dimension "
1135  "was loaded instead." ),
1136  csDim.ID ) );
1138  case DIMENSION::SUBTYPE::DIRECT:
1139  case DIMENSION::SUBTYPE::ORTHOGONAL:
1140  {
1141  if( csDim.Line.Style == DIMENSION::LINE::STYLE::EXTERNAL )
1142  {
1143  wxLogWarning( wxString::Format(
1144  _( "Dimension ID %s has 'External' style in CADSTAR. External "
1145  "dimension styles are not yet supported in KiCad. The dimension "
1146  "object was imported with an internal dimension style instead." ),
1147  csDim.ID ) );
1148  }
1149 
1150  ALIGNED_DIMENSION* dimension = nullptr;
1151 
1152  if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1153  {
1154  dimension = new ORTHOGONAL_DIMENSION( m_board );
1155  ORTHOGONAL_DIMENSION* orDim = static_cast<ORTHOGONAL_DIMENSION*>( dimension );
1156 
1157  if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1159  else
1161  }
1162  else
1163  {
1164  dimension = new ALIGNED_DIMENSION( m_board );
1165  }
1166 
1167  m_board->Add( dimension, ADD_MODE::APPEND );
1168  applyDimensionSettings( csDim, dimension );
1169 
1170  dimension->SetExtensionHeight(
1172 
1173  // Calculate height:
1174  wxPoint crossbarStart = getKiCadPoint( csDim.Line.Start );
1175  wxPoint crossbarEnd = getKiCadPoint( csDim.Line.End );
1176  VECTOR2I crossbarVector = crossbarEnd - crossbarStart;
1177  VECTOR2I heightVector = crossbarStart - dimension->GetStart();
1178  double height = 0.0;
1179 
1180  if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1181  {
1182  if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1183  height = heightVector.y;
1184  else
1185  height = heightVector.x;
1186  }
1187  else
1188  {
1189  double angle = crossbarVector.Angle() + ( M_PI / 2 );
1190  height = heightVector.x * cos( angle ) + heightVector.y * sin( angle );
1191  }
1192 
1193  dimension->SetHeight( height );
1194  }
1195  break;
1196 
1197  default:
1198  // Radius and diameter dimensions are LEADERDIM (even if not actually leader)
1199  // Angular dimensions are always ANGLEDIM
1200  wxLogError( wxString::Format(
1201  _( "Unexpected Dimension type (ID %s). This was not imported" ),
1202  csDim.ID ) );
1203  continue;
1204  }
1205  break;
1206 
1207  case DIMENSION::TYPE::LEADERDIM:
1208  //TODO: update import when KiCad supports radius and diameter dimensions
1209 
1210  if( csDim.Line.Style == DIMENSION::LINE::STYLE::INTERNAL )
1211  {
1212  // "internal" is a simple double sided arrow from start to end (no extension lines)
1213  ALIGNED_DIMENSION* dimension = new ALIGNED_DIMENSION( m_board );
1214  m_board->Add( dimension, ADD_MODE::APPEND );
1215  applyDimensionSettings( csDim, dimension );
1216 
1217  // Lets set again start/end:
1218  dimension->SetStart( getKiCadPoint( csDim.Line.Start ) );
1219  dimension->SetEnd( getKiCadPoint( csDim.Line.End ) );
1220 
1221  // Do not use any extension lines:
1222  dimension->SetExtensionOffset( 0 );
1223  dimension->SetExtensionHeight( 0 );
1224  dimension->SetHeight( 0 );
1225  }
1226  else
1227  {
1228  // "external" is a "leader" style dimension
1229  LEADER* leaderDim = new LEADER( m_board );
1230  m_board->Add( leaderDim, ADD_MODE::APPEND );
1231 
1232  applyDimensionSettings( csDim, leaderDim );
1233  leaderDim->SetStart( getKiCadPoint( csDim.Line.End ) );
1234 
1235  /*
1236  * In CADSTAR, the resulting shape orientation of the leader dimension depends on
1237  * on the positions of the #Start (S) and #End (E) points as shown below. In the
1238  * diagrams below, the leader angle (angRad) is represented by HEV
1239  *
1240  * Orientation 1: (orientX = -1, | Orientation 2: (orientX = 1,
1241  * orientY = 1) | orientY = 1)
1242  * |
1243  * --------V | V----------
1244  * \ | /
1245  * \ | /
1246  * H _E/ | \E_ H
1247  * |
1248  * S | S
1249  * |
1250  *
1251  * Orientation 3: (orientX = -1, | Orientation 4: (orientX = 1,
1252  * orientY = -1) | orientY = -1)
1253  * |
1254  * S | S
1255  * _ | _
1256  * H E\ | /E H
1257  * / | \
1258  * / | \
1259  * ----------V | V-----------
1260  * |
1261  *
1262  * Corner cases:
1263  *
1264  * It is not possible to generate a leader object with start and end point being
1265  * identical. Assume Orientation 2 if start and end points are identical.
1266  *
1267  * If start and end points are aligned vertically (i.e. S.x == E.x):
1268  * - If E.y > S.y - Orientation 2
1269  * - If E.y < S.y - Orientation 4
1270  *
1271  * If start and end points are aligned horitontally (i.e. S.y == E.y):
1272  * - If E.x > S.x - Orientation 2
1273  * - If E.x < S.x - Orientation 1
1274  */
1275  double angRad = DEG2RAD( getAngleDegrees( csDim.Line.LeaderAngle ) );
1276 
1277  double orientX = 1;
1278  double orientY = 1;
1279 
1280  if( csDim.Line.End.x >= csDim.Line.Start.x )
1281  {
1282  if( csDim.Line.End.y >= csDim.Line.Start.y )
1283  {
1284  //Orientation 2
1285  orientX = 1;
1286  orientY = 1;
1287  }
1288  else
1289  {
1290  //Orientation 4
1291  orientX = 1;
1292  orientY = -1;
1293  }
1294  }
1295  else
1296  {
1297  if( csDim.Line.End.y >= csDim.Line.Start.y )
1298  {
1299  //Orientation 1
1300  orientX = -1;
1301  orientY = 1;
1302  }
1303  else
1304  {
1305  //Orientation 3
1306  orientX = -1;
1307  orientY = -1;
1308  }
1309  }
1310 
1311  wxPoint endOffset( csDim.Line.LeaderLineLength * cos( angRad ) * orientX,
1312  csDim.Line.LeaderLineLength * sin( angRad ) * orientY );
1313 
1314  wxPoint endPoint = csDim.Line.End + endOffset;
1315  wxPoint txtPoint( endPoint.x + ( csDim.Line.LeaderLineExtensionLength * orientX ),
1316  endPoint.y );
1317 
1318  leaderDim->SetEnd( getKiCadPoint( endPoint ) );
1319  leaderDim->Text().SetTextPos( getKiCadPoint( txtPoint ) );
1320  leaderDim->SetText( ParseTextFields( csDim.Text.Text, &m_context ) );
1321  leaderDim->SetPrefix( wxEmptyString );
1322  leaderDim->SetSuffix( wxEmptyString );
1324 
1325  if( orientX == 1 )
1327  else
1328  leaderDim->Text().SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1329 
1330  leaderDim->SetExtensionOffset( 0 );
1331  }
1332  break;
1333 
1334  case DIMENSION::TYPE::ANGLEDIM:
1335  //TODO: update import when KiCad supports angular dimensions
1336  wxLogError( wxString::Format(
1337  _( "Dimension ID %s is an angular dimension which has no KiCad equivalent. "
1338  "The object was not imported." ),
1339  csDim.ID ) );
1340  break;
1341  }
1342  }
1343 }
1344 
1345 
1347 {
1348  for( std::pair<AREA_ID, AREA> areaPair : Layout.Areas )
1349  {
1350  AREA& area = areaPair.second;
1351 
1352  if( area.NoVias || area.NoTracks || area.Keepout || area.Routing )
1353  {
1355  m_board );
1356 
1357  m_board->Add( zone, ADD_MODE::APPEND );
1358 
1359  if( isLayerSet( area.LayerID ) )
1360  zone->SetLayerSet( getKiCadLayerSet( area.LayerID ) );
1361  else
1362  zone->SetLayer( getKiCadLayer( area.LayerID ) );
1363 
1364  zone->SetIsRuleArea( true ); //import all CADSTAR areas as Keepout zones
1365  zone->SetDoNotAllowPads( false ); //no CADSTAR equivalent
1366  zone->SetZoneName( area.Name );
1367 
1368  zone->SetDoNotAllowFootprints( area.Keepout );
1369 
1370  zone->SetDoNotAllowTracks( area.NoTracks );
1371  zone->SetDoNotAllowCopperPour( area.NoTracks );
1372 
1373  zone->SetDoNotAllowVias( area.NoVias );
1374 
1375  if( area.Placement )
1376  wxLogWarning( wxString::Format(
1377  _( "The CADSTAR area '%s' is marked as a placement area in CADSTAR. "
1378  "Placement areas are not supported in KiCad. Only the supported "
1379  "elements for the area were imported." ),
1380  area.Name ) );
1381  }
1382  else
1383  {
1384  wxLogError(
1385  wxString::Format( _( "The CADSTAR area '%s' does not have a KiCad equivalent. "
1386  "Pure Placement areas are not supported." ),
1387  area.Name ) );
1388  }
1389 
1390  //todo Process area.AreaHeight when KiCad supports 3D design rules
1391  //TODO process attributes
1392  //TODO process addition to a group
1393  //TODO process "swaprule"
1394  //TODO process re-use block
1395  }
1396 }
1397 
1398 
1400 {
1401  for( std::pair<COMPONENT_ID, COMPONENT> compPair : Layout.Components )
1402  {
1403  COMPONENT& comp = compPair.second;
1404 
1405  if( !comp.VariantID.empty() && comp.VariantParentComponentID != comp.ID )
1406  continue; // Only load master Variant
1407 
1408  auto fpIter = m_libraryMap.find( comp.SymdefID );
1409 
1410  if( fpIter == m_libraryMap.end() )
1411  {
1412  THROW_IO_ERROR( wxString::Format( _( "Unable to find component '%s' in the library"
1413  "(Symdef ID: '%s')" ),
1414  comp.Name, comp.SymdefID ) );
1415  }
1416 
1417  FOOTPRINT* libFootprint = fpIter->second;
1418 
1419  // Use Duplicate() to ensure unique KIID for all objects
1420  FOOTPRINT* footprint = static_cast<FOOTPRINT*>( libFootprint->Duplicate() );
1421 
1422  m_board->Add( footprint, ADD_MODE::APPEND );
1423 
1424  // First lets fix the pad names on the footprint.
1425  // CADSTAR defines the pad name in the PART definition and the SYMDEF (i.e. the PCB
1426  // footprint definition) uses a numerical sequence. COMP is the only object that has
1427  // visibility of both the SYMDEF and PART.
1428  if( Parts.PartDefinitions.find( comp.PartID ) != Parts.PartDefinitions.end() )
1429  {
1430  PART part = Parts.PartDefinitions.at( comp.PartID );
1431 
1432  // Only do this when the number of pins in the part definition equals the number of
1433  // pads in the footprint.
1434  if( part.Definition.Pins.size() == footprint->Pads().size() )
1435  {
1436  for( std::pair<PART_DEFINITION_PIN_ID, PART::DEFINITION::PIN> pinPair :
1437  part.Definition.Pins )
1438  {
1439  PART::DEFINITION::PIN pin = pinPair.second;
1440  wxString pinName = pin.Name;
1441 
1442  if( pinName.empty() )
1443  pinName = pin.Identifier;
1444 
1445  if( pinName.empty() )
1446  pinName = wxString::Format( wxT( "%ld" ), pin.ID );
1447 
1448  footprint->Pads().at( pin.ID - (long long) 1 )->SetName( pinName );
1449  }
1450  }
1451  }
1452 
1453  //Override pads with pad exceptions
1454  if( comp.PadExceptions.size() > 0 )
1455  {
1456  SYMDEF_PCB fpLibEntry = Library.ComponentDefinitions.at( comp.SymdefID );
1457 
1458  for( std::pair<PAD_ID, PADEXCEPTION> padPair : comp.PadExceptions )
1459  {
1460  PADEXCEPTION& padEx = padPair.second;
1461  COMPONENT_PAD csPad = fpLibEntry.ComponentPads.at( padPair.first );
1462 
1463  if( !padEx.PadCode.IsEmpty() )
1464  csPad.PadCodeID = padEx.PadCode;
1465 
1466  if( padEx.OverrideExits )
1467  csPad.Exits = padEx.Exits;
1468 
1469  if( padEx.OverrideOrientation )
1470  csPad.OrientAngle = padEx.OrientAngle;
1471 
1472  if( padEx.OverrideSide )
1473  csPad.Side = padEx.Side;
1474 
1475  // Find the pad in the footprint definition
1476  PAD* kiPad = footprint->Pads().at( padEx.ID - (long long) 1 );
1477  wxString padName = kiPad->GetName();
1478 
1479  if( kiPad )
1480  delete kiPad;
1481 
1482  kiPad = getKiCadPad( csPad, footprint );
1483  kiPad->SetName( padName );
1484  footprint->Pads().at( padEx.ID - (long long) 1 ) = kiPad;
1485  }
1486  }
1487 
1488  //set to empty string to avoid duplication when loading attributes:
1489  footprint->SetValue( wxEmptyString );
1490 
1491  footprint->SetPosition( getKiCadPoint( comp.Origin ) );
1492  footprint->SetOrientation( getAngleTenthDegree( comp.OrientAngle ) );
1493  footprint->SetReference( comp.Name );
1494 
1495  if( comp.Mirror )
1496  {
1497  double mirroredAngle = - getAngleTenthDegree( comp.OrientAngle );
1498  NORMALIZE_ANGLE_180( mirroredAngle );
1499  footprint->SetOrientation( mirroredAngle );
1500  footprint->Flip( getKiCadPoint( comp.Origin ), true );
1501  }
1502 
1503  loadComponentAttributes( comp, footprint );
1504 
1505  if( !comp.PartID.IsEmpty() && comp.PartID != wxT( "NO_PART" ) )
1506  footprint->SetDescription( getPart( comp.PartID ).Definition.Name );
1507 
1508  m_componentMap.insert( { comp.ID, footprint } );
1509  }
1510 }
1511 
1512 
1514 {
1515  //No KiCad equivalent. Loaded as graphic and text elements instead
1516 
1517  for( std::pair<DOCUMENTATION_SYMBOL_ID, DOCUMENTATION_SYMBOL> docPair :
1519  {
1520  DOCUMENTATION_SYMBOL& docSymInstance = docPair.second;
1521 
1522 
1523  auto docSymIter = Library.ComponentDefinitions.find( docSymInstance.SymdefID );
1524 
1525  if( docSymIter == Library.ComponentDefinitions.end() )
1526  {
1527  THROW_IO_ERROR( wxString::Format( _( "Unable to find documentation symbol in the "
1528  "library (Symdef ID: '%s')" ),
1529  docSymInstance.SymdefID ) );
1530  }
1531 
1532  SYMDEF_PCB& docSymDefinition = ( *docSymIter ).second;
1533  wxPoint moveVector =
1534  getKiCadPoint( docSymInstance.Origin ) - getKiCadPoint( docSymDefinition.Origin );
1535  double rotationAngle = getAngleTenthDegree( docSymInstance.OrientAngle );
1536  double scalingFactor = (double) docSymInstance.ScaleRatioNumerator
1537  / (double) docSymInstance.ScaleRatioDenominator;
1538  wxPoint centreOfTransform = getKiCadPoint( docSymDefinition.Origin );
1539  bool mirrorInvert = docSymInstance.Mirror;
1540 
1541  //create a group to store the items in
1542  wxString groupName = docSymDefinition.ReferenceName;
1543 
1544  if( !docSymDefinition.Alternate.IsEmpty() )
1545  groupName += wxT( " (" ) + docSymDefinition.Alternate + wxT( ")" );
1546 
1547  GROUP_ID groupID = createUniqueGroupID( groupName );
1548 
1549  LSEQ layers = getKiCadLayerSet( docSymInstance.LayerID ).Seq();
1550 
1551  for( PCB_LAYER_ID layer : layers )
1552  {
1553  for( std::pair<FIGURE_ID, FIGURE> figPair : docSymDefinition.Figures )
1554  {
1555  FIGURE fig = figPair.second;
1556  drawCadstarShape( fig.Shape, layer, getLineThickness( fig.LineCodeID ),
1557  wxString::Format( "DOCUMENTATION SYMBOL %s, FIGURE %s",
1558  docSymDefinition.ReferenceName, fig.ID ),
1559  m_board, groupID, moveVector, rotationAngle, scalingFactor,
1560  centreOfTransform, mirrorInvert );
1561  }
1562  }
1563 
1564  for( std::pair<TEXT_ID, TEXT> textPair : docSymDefinition.Texts )
1565  {
1566  TEXT txt = textPair.second;
1567  drawCadstarText( txt, m_board, groupID, docSymInstance.LayerID, moveVector,
1568  rotationAngle, scalingFactor, centreOfTransform, mirrorInvert );
1569  }
1570  }
1571 }
1572 
1573 
1575 {
1576  for( std::pair<TEMPLATE_ID, TEMPLATE> tempPair : Layout.Templates )
1577  {
1578  TEMPLATE& csTemplate = tempPair.second;
1579 
1580  int zonelinethickness = 0; // The line thickness in CADSTAR is only for display purposes but
1581  // does not affect the end copper result.
1582  ZONE* zone = getZoneFromCadstarShape( csTemplate.Shape, zonelinethickness, m_board );
1583 
1584  m_board->Add( zone, ADD_MODE::APPEND );
1585 
1586  zone->SetZoneName( csTemplate.Name );
1587  zone->SetLayer( getKiCadLayer( csTemplate.LayerID ) );
1588  zone->SetPriority( 1 ); // initially 1, we will increase in calculateZonePriorities
1589 
1590  if( !( csTemplate.NetID.IsEmpty() || csTemplate.NetID == wxT( "NONE" ) ) )
1591  zone->SetNet( getKiCadNet( csTemplate.NetID ) );
1592 
1593  if( csTemplate.Pouring.AllowInNoRouting )
1594  {
1595  wxLogWarning( wxString::Format(
1596  _( "The CADSTAR template '%s' has the setting 'Allow in No Routing Areas' "
1597  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1598  csTemplate.Name ) );
1599  }
1600 
1601  if( csTemplate.Pouring.BoxIsolatedPins )
1602  {
1603  wxLogWarning( wxString::Format(
1604  _( "The CADSTAR template '%s' has the setting 'Box Isolated Pins' "
1605  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1606  csTemplate.Name ) );
1607  }
1608 
1609  if( csTemplate.Pouring.AutomaticRepour )
1610  {
1611  wxLogWarning( wxString::Format(
1612  _( "The CADSTAR template '%s' has the setting 'Automatic Repour' "
1613  "enabled. This setting has no KiCad equivalent, so it has been ignored." ),
1614  csTemplate.Name ) );
1615  }
1616 
1617  // Sliver width has different behaviour to KiCad Zone's minimum thickness
1618  // In Cadstar 'Sliver width' has to be greater than the Copper thickness, whereas in
1619  // Kicad it is the opposite.
1620  if( csTemplate.Pouring.SliverWidth != 0 )
1621  {
1622  wxLogWarning( wxString::Format(
1623  _( "The CADSTAR template '%s' has a non-zero value defined for the "
1624  "'Sliver Width' setting. There is no KiCad equivalent for "
1625  "this, so this setting was ignored." ),
1626  csTemplate.Name ) );
1627  }
1628 
1629 
1630  if( csTemplate.Pouring.MinIsolatedCopper != csTemplate.Pouring.MinDisjointCopper )
1631  {
1632  wxLogWarning( wxString::Format(
1633  _( "The CADSTAR template '%s' has different settings for 'Retain Poured Copper "
1634  "- Disjoint' and 'Retain Poured Copper - Isolated'. KiCad does not "
1635  "distinguish between these two settings. The setting for disjoint copper "
1636  "has been applied as the minimum island area of the KiCad Zone." ),
1637  csTemplate.Name ) );
1638  }
1639 
1640  long long minIslandArea = -1;
1641 
1642  if( csTemplate.Pouring.MinDisjointCopper != UNDEFINED_VALUE )
1643  {
1644  minIslandArea = (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper )
1645  * (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper );
1646 
1648  }
1649  else
1650  {
1652  }
1653 
1654  zone->SetMinIslandArea( minIslandArea );
1655 
1656  // In cadstar zone clearance is in addition to the design rule "copper to copper"
1657  int clearance = getKiCadLength( csTemplate.Pouring.AdditionalIsolation );
1658 
1659  if( Assignments.Codedefs.SpacingCodes.find( wxT( "C_C" ) )
1660  != Assignments.Codedefs.SpacingCodes.end() )
1661  {
1662  int copperToCopper = Assignments.Codedefs.SpacingCodes.at( wxT( "C_C" ) ).Spacing;
1663  clearance += getKiCadLength( copperToCopper );
1664  }
1665  else
1666  {
1667  clearance += m_board->GetDesignSettings().m_MinClearance;
1668  }
1669 
1670  zone->SetLocalClearance( clearance );
1671 
1672  COPPERCODE pouringCopperCode = getCopperCode( csTemplate.Pouring.CopperCodeID );
1673  int minThickness = getKiCadLength( pouringCopperCode.CopperWidth );
1674  zone->SetMinThickness( minThickness );
1675 
1676  if( csTemplate.Pouring.FillType == TEMPLATE::POURING::COPPER_FILL_TYPE::HATCHED )
1677  {
1679  zone->SetHatchGap( getKiCadHatchCodeGap( csTemplate.Pouring.HatchCodeID ) );
1682  }
1683  else
1684  {
1686  }
1687 
1688  if( csTemplate.Pouring.ThermalReliefOnPads != csTemplate.Pouring.ThermalReliefOnVias
1689  || csTemplate.Pouring.ThermalReliefPadsAngle
1690  != csTemplate.Pouring.ThermalReliefViasAngle )
1691  {
1692  wxLogWarning( wxString::Format(
1693  _( "The CADSTAR template '%s' has different settings for thermal relief "
1694  "in pads and vias. KiCad only supports one single setting for both. The "
1695  "setting for pads has been applied." ),
1696  csTemplate.Name ) );
1697  }
1698 
1699  COPPERCODE reliefCopperCode = getCopperCode( csTemplate.Pouring.ReliefCopperCodeID );
1700  int spokeWidth = getKiCadLength( reliefCopperCode.CopperWidth );
1701  int reliefWidth = getKiCadLength( csTemplate.Pouring.ClearanceWidth );
1702 
1703  // Cadstar supports having a spoke width thinner than the minimum thickness of the zone, but
1704  // this is not permitted in KiCad. We load it as solid fill instead.
1705  if( csTemplate.Pouring.ThermalReliefOnPads && reliefWidth > 0 && spokeWidth > minThickness )
1706  {
1707  zone->SetThermalReliefGap( reliefWidth );
1708  zone->SetThermalReliefSpokeWidth( spokeWidth );
1710  }
1711  else
1712  {
1713  if( csTemplate.Pouring.ThermalReliefOnPads && spokeWidth > minThickness )
1714  {
1715  wxLogWarning( wxString::Format(
1716  _( "The CADSTAR template '%s' has thermal reliefs in the original design "
1717  "but there is no KiCad equivalent to the original CADSTAR settings. "
1718  "Solid fill has been applied instead. When the template is re-filled "
1719  "the thermal reliefs will be removed." ),
1720  csTemplate.Name ) );
1721  }
1722 
1724  }
1725 
1726  m_zonesMap.insert( { csTemplate.ID, zone } );
1727  }
1728 
1729  //Now create power plane layers:
1730  for( LAYER_ID layer : m_powerPlaneLayers )
1731  {
1732  wxASSERT(
1733  Assignments.Layerdefs.Layers.find( layer ) != Assignments.Layerdefs.Layers.end() );
1734 
1735  //The net name will equal the layer name
1736  wxString powerPlaneLayerName = Assignments.Layerdefs.Layers.at( layer ).Name;
1737  NET_ID netid = wxEmptyString;
1738 
1739  for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
1740  {
1741  NET_PCB net = netPair.second;
1742 
1743  if( net.Name == powerPlaneLayerName )
1744  {
1745  netid = net.ID;
1746  break;
1747  }
1748  }
1749 
1750  if( netid.IsEmpty() )
1751  {
1752  wxLogError( wxString::Format(
1753  _( "The CADSTAR layer '%s' is defined as a power plane layer. However no "
1754  "net with such name exists. The layer has been loaded but no copper zone "
1755  "was created." ),
1756  powerPlaneLayerName ) );
1757  }
1758  else
1759  {
1760  for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
1761  {
1762  //create a zone in each board shape
1764  CADSTAR_BOARD& board = boardPair.second;
1765  int defaultLineThicknesss = bds.GetLineThickness( PCB_LAYER_ID::Edge_Cuts );
1766  ZONE* zone = getZoneFromCadstarShape( board.Shape, defaultLineThicknesss, m_board );
1767 
1768  m_board->Add( zone, ADD_MODE::APPEND );
1769 
1770  zone->SetZoneName( powerPlaneLayerName );
1771  zone->SetLayer( getKiCadLayer( layer ) );
1774  zone->SetMinIslandArea( -1 );
1775  zone->SetPriority( 0 ); // Priority always 0 (lowest priority) for implied power planes.
1776  zone->SetNet( getKiCadNet( netid ) );
1777  }
1778  }
1779  }
1780 }
1781 
1782 
1784 {
1785  for( std::pair<COPPER_ID, COPPER> copPair : Layout.Coppers )
1786  {
1787  COPPER& csCopper = copPair.second;
1788 
1789  if( !csCopper.PouredTemplateID.IsEmpty() )
1790  {
1791  ZONE* pouredZone = m_zonesMap.at( csCopper.PouredTemplateID );
1792  SHAPE_POLY_SET rawPolys;
1793 
1794  int copperWidth = getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth );
1795 
1796  if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE )
1797  {
1798  // This is usually for themal reliefs. They are lines of copper with a thickness.
1799  // We convert them to an oval in most cases, but handle also the possibility of
1800  // encountering arcs in here.
1801 
1802  std::vector<PCB_SHAPE*> outlineSegments =
1804 
1805  for( auto& seg : outlineSegments )
1806  {
1807  SHAPE_POLY_SET segment;
1808 
1809  if( seg->GetShape() == PCB_SHAPE_TYPE_T::S_ARC )
1810  {
1811  TransformArcToPolygon( segment, seg->GetStart(), seg->GetArcMid(),
1812  seg->GetEnd(), copperWidth, ARC_HIGH_DEF,
1814  }
1815  else
1816  {
1817  TransformOvalToPolygon( segment, seg->GetStart(), seg->GetEnd(),
1818  copperWidth, ARC_HIGH_DEF, ERROR_LOC::ERROR_INSIDE );
1819  }
1820 
1821  rawPolys.BooleanAdd( segment, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
1822  }
1823 
1824  }
1825  else
1826  {
1827  rawPolys = getPolySetFromCadstarShape( csCopper.Shape, -1 );
1828  rawPolys.Inflate( copperWidth / 2, 32 );
1829  }
1830 
1831 
1832  if( pouredZone->HasFilledPolysForLayer( getKiCadLayer( csCopper.LayerID ) ) )
1833  {
1834  rawPolys.BooleanAdd( pouredZone->RawPolysList( getKiCadLayer( csCopper.LayerID )),
1836  }
1837 
1838  SHAPE_POLY_SET finalPolys = rawPolys;
1840 
1841  pouredZone->SetFillVersion( 6 );
1842  pouredZone->SetRawPolysList( getKiCadLayer( csCopper.LayerID ), rawPolys );
1843  pouredZone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), finalPolys );
1844  pouredZone->SetIsFilled( true );
1845  pouredZone->SetNeedRefill( false );
1846  continue;
1847  }
1848 
1849  // For now we are going to load coppers to a KiCad zone however this isn't perfect
1850  //TODO: Load onto a graphical polygon with a net (when KiCad has this feature)
1851 
1852  if( !m_doneCopperWarning )
1853  {
1854  wxLogWarning(
1855  _( "The CADSTAR design contains COPPER elements, which have no direct KiCad "
1856  "equivalent. These have been imported as a KiCad Zone if solid or hatch "
1857  "filled, or as a KiCad Track if the shape was an unfilled outline (open or "
1858  "closed)." ) );
1859  m_doneCopperWarning = true;
1860  }
1861 
1862 
1863  if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE
1864  || csCopper.Shape.Type == SHAPE_TYPE::OUTLINE )
1865  {
1866  std::vector<PCB_SHAPE*> outlineSegments =
1868 
1869  std::vector<TRACK*> outlineTracks = makeTracksFromDrawsegments( outlineSegments, m_board,
1870  getKiCadNet( csCopper.NetRef.NetID ), getKiCadLayer( csCopper.LayerID ),
1872 
1873  //cleanup
1874  for( PCB_SHAPE* seg : outlineSegments )
1875  delete seg;
1876 
1877  for( CUTOUT cutout : csCopper.Shape.Cutouts )
1878  {
1879  std::vector<PCB_SHAPE*> cutoutSeg =
1881 
1882  std::vector<TRACK*> cutoutTracks = makeTracksFromDrawsegments( cutoutSeg, m_board,
1883  getKiCadNet( csCopper.NetRef.NetID ), getKiCadLayer( csCopper.LayerID ),
1885 
1886  //cleanup
1887  for( PCB_SHAPE* seg : cutoutSeg )
1888  delete seg;
1889  }
1890  }
1891  else
1892  {
1893  ZONE* zone = getZoneFromCadstarShape( csCopper.Shape,
1895  m_board );
1896 
1897  m_board->Add( zone, ADD_MODE::APPEND );
1898 
1899  zone->SetZoneName( csCopper.ID );
1900  zone->SetLayer( getKiCadLayer( csCopper.LayerID ) );
1902 
1903  if( csCopper.Shape.Type == SHAPE_TYPE::HATCHED )
1904  {
1906  zone->SetHatchGap( getKiCadHatchCodeGap( csCopper.Shape.HatchCodeID ) );
1909  }
1910  else
1911  {
1913  }
1914 
1917  zone->SetNet( getKiCadNet( csCopper.NetRef.NetID ) );
1918  zone->SetPriority( m_zonesMap.size() + 1 ); // Highest priority (always fill first)
1919  zone->SetRawPolysList( getKiCadLayer( csCopper.LayerID ), *zone->Outline() );
1920 
1921  SHAPE_POLY_SET fillePolys( *zone->Outline() );
1922  fillePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
1923 
1924  zone->SetFillVersion( 6 );
1925  zone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fillePolys );
1926  }
1927  }
1928 }
1929 
1930 
1932 {
1933  for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
1934  {
1935  NET_PCB net = netPair.second;
1936  wxString netnameForErrorReporting = net.Name;
1937 
1938  if( netnameForErrorReporting.IsEmpty() )
1939  netnameForErrorReporting = wxString::Format( "$%ld", net.SignalNum );
1940 
1941  for( NET_PCB::CONNECTION_PCB connection : net.Connections )
1942  {
1943  if( !connection.Unrouted )
1944  loadNetTracks( net.ID, connection.Route );
1945 
1946  //TODO: all other elements
1947  }
1948 
1949  for( std::pair<NETELEMENT_ID, NET_PCB::VIA> viaPair : net.Vias )
1950  {
1951  NET_PCB::VIA via = viaPair.second;
1952  loadNetVia( net.ID, via );
1953  }
1954 
1955  for( std::pair<NETELEMENT_ID, NET_PCB::PIN> pinPair : net.Pins )
1956  {
1957  NET_PCB::PIN pin = pinPair.second;
1958  FOOTPRINT* footprint = getFootprintFromCadstarID( pin.ComponentID );
1959 
1960  if( footprint == nullptr )
1961  {
1962  wxLogWarning( wxString::Format(
1963  _( "The net '%s' references component ID '%s' which does not exist. "
1964  "This has been ignored." ),
1965  netnameForErrorReporting, pin.ComponentID ) );
1966  }
1967  else if( ( pin.PadID - (long) 1 ) > footprint->Pads().size() )
1968  {
1969  wxLogWarning( wxString::Format( _( "The net '%s' references non-existent pad index"
1970  " '%d' in component '%s'. This has been ignored." ),
1971  netnameForErrorReporting,
1972  pin.PadID,
1973  footprint->GetReference() ) );
1974  }
1975  else
1976  {
1977  // The below works because we have added the pads in the correct order to the
1978  // footprint and the PAD_ID in Cadstar is a sequential, numerical ID
1979  footprint->Pads().at( pin.PadID - (long) 1 )->SetNet( getKiCadNet( net.ID ) );
1980  }
1981  }
1982  }
1983 }
1984 
1985 
1987 {
1988  auto findAndReplaceTextField =
1989  [&]( TEXT_FIELD_NAME aField, wxString aValue )
1990  {
1991  if( m_context.TextFieldToValuesMap.find( aField ) !=
1993  {
1994  if( m_context.TextFieldToValuesMap.at( aField ) != aValue )
1995  {
1996  m_context.TextFieldToValuesMap.at( aField ) = aValue;
1997  m_context.InconsistentTextFields.insert( aField );
1998  return false;
1999  }
2000  }
2001  else
2002  {
2003  m_context.TextFieldToValuesMap.insert( { aField, aValue } );
2004  }
2005 
2006  return true;
2007  };
2008 
2009  if( m_project )
2010  {
2011  std::map<wxString, wxString>& txtVars = m_project->GetTextVars();
2012 
2013  // Most of the design text fields can be derived from other elements
2014  if( Layout.VariantHierarchy.Variants.size() > 0 )
2015  {
2016  VARIANT loadedVar = Layout.VariantHierarchy.Variants.begin()->second;
2017 
2018  findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_NAME, loadedVar.Name );
2019  findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_DESCRIPTION, loadedVar.Description );
2020  }
2021 
2022  findAndReplaceTextField( TEXT_FIELD_NAME::DESIGN_TITLE, Header.JobTitle );
2023 
2024  for( std::pair<TEXT_FIELD_NAME, wxString> txtvalue : m_context.TextFieldToValuesMap )
2025  {
2026  wxString varName = CADSTAR_TO_KICAD_FIELDS.at( txtvalue.first );
2027  wxString varValue = txtvalue.second;
2028 
2029  txtVars.insert( { varName, varValue } );
2030  }
2031 
2032  for( std::pair<wxString, wxString> txtvalue : m_context.FilenamesToTextMap )
2033  {
2034  wxString varName = txtvalue.first;
2035  wxString varValue = txtvalue.second;
2036 
2037  txtVars.insert( { varName, varValue } );
2038  }
2039  }
2040  else
2041  {
2042  wxLogError( _( "Text Variables could not be set as there is no project loaded." ) );
2043  }
2044 }
2045 
2046 
2048  FOOTPRINT* aFootprint )
2049 {
2050  for( std::pair<ATTRIBUTE_ID, ATTRIBUTE_VALUE> attrPair : aComponent.AttributeValues )
2051  {
2052  ATTRIBUTE_VALUE& attrval = attrPair.second;
2053 
2054  if( attrval.HasLocation ) //only import attributes with location. Ignore the rest
2055  {
2056  addAttribute( attrval.AttributeLocation, attrval.AttributeID, aFootprint,
2057  attrval.Value );
2058  }
2059  }
2060 
2061  for( std::pair<ATTRIBUTE_ID, TEXT_LOCATION> textlocPair : aComponent.TextLocations )
2062  {
2063  TEXT_LOCATION& textloc = textlocPair.second;
2064  wxString attrval;
2065 
2066  if( textloc.AttributeID == COMPONENT_NAME_ATTRID )
2067  {
2068  attrval = wxEmptyString; // Designator is loaded separately
2069  }
2070  else if( textloc.AttributeID == COMPONENT_NAME_2_ATTRID )
2071  {
2072  attrval = wxT( "${REFERENCE}" );
2073  }
2074  else if( textloc.AttributeID == PART_NAME_ATTRID )
2075  {
2076  attrval = getPart( aComponent.PartID ).Name;
2077  }
2078  else
2079  attrval = getAttributeValue( textloc.AttributeID, aComponent.AttributeValues );
2080 
2081  addAttribute( textloc, textloc.AttributeID, aFootprint, attrval );
2082  }
2083 }
2084 
2085 
2087  const NET_ID& aCadstarNetID, const NET_PCB::ROUTE& aCadstarRoute )
2088 {
2089  std::vector<PCB_SHAPE*> shapes;
2090 
2091  POINT prevEnd = aCadstarRoute.StartPoint;
2092 
2093  for( const NET_PCB::ROUTE_VERTEX& v : aCadstarRoute.RouteVertices )
2094  {
2095  PCB_SHAPE* shape = getDrawSegmentFromVertex( prevEnd, v.Vertex );
2096  shape->SetLayer( getKiCadLayer( aCadstarRoute.LayerID ) );
2097  shape->SetWidth( getKiCadLength( v.RouteWidth ) );
2098  shapes.push_back( shape );
2099  prevEnd = v.Vertex.End;
2100  }
2101 
2102  //Todo add real netcode to the tracks
2103  std::vector<TRACK*> tracks =
2104  makeTracksFromDrawsegments( shapes, m_board, getKiCadNet( aCadstarNetID ) );
2105 
2106  //cleanup
2107  for( PCB_SHAPE* shape : shapes )
2108  delete shape;
2109 }
2110 
2111 
2113  const NET_ID& aCadstarNetID, const NET_PCB::VIA& aCadstarVia )
2114 {
2115  VIA* via = new VIA( m_board );
2116  m_board->Add( via, ADD_MODE::APPEND );
2117 
2118  VIACODE csViaCode = getViaCode( aCadstarVia.ViaCodeID );
2119  LAYERPAIR csLayerPair = getLayerPair( aCadstarVia.LayerPairID );
2120 
2121  via->SetPosition( getKiCadPoint( aCadstarVia.Location ) );
2122  via->SetDrill( getKiCadLength( csViaCode.DrillDiameter ) );
2123  via->SetLocked( aCadstarVia.Fixed );
2124 
2125  if( csViaCode.Shape.ShapeType != PAD_SHAPE_TYPE::CIRCLE )
2126  wxLogError( wxString::Format(
2127  _( "The CADSTAR via code '%s' has different shape from a circle defined. "
2128  "KiCad only supports circular vias so this via type has been changed to "
2129  "be a via with circular shape of %.2f mm diameter." ),
2130  csViaCode.Name,
2131  (double) ( (double) getKiCadLength( csViaCode.Shape.Size ) / 1E6 ) ) );
2132 
2133  via->SetWidth( getKiCadLength( csViaCode.Shape.Size ) );
2134 
2135  bool start_layer_outside =
2136  csLayerPair.PhysicalLayerStart == 1
2138  bool end_layer_outside =
2139  csLayerPair.PhysicalLayerEnd == 1
2141 
2142  if( start_layer_outside && end_layer_outside )
2143  {
2144  via->SetViaType( VIATYPE::THROUGH );
2145  }
2146  else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
2147  {
2149  }
2150  else
2151  {
2152  via->SetViaType( VIATYPE::MICROVIA );
2153  }
2154 
2156  getKiCadCopperLayerID( csLayerPair.PhysicalLayerEnd ) );
2157  via->SetNet( getKiCadNet( aCadstarNetID ) );
2159 }
2160 
2161 
2163  BOARD_ITEM_CONTAINER* aContainer, const GROUP_ID& aCadstarGroupID,
2164  const LAYER_ID& aCadstarLayerOverride, const wxPoint& aMoveVector,
2165  const double& aRotationAngle, const double& aScalingFactor, const wxPoint& aTransformCentre,
2166  const bool& aMirrorInvert )
2167 {
2168  PCB_TEXT* txt = new PCB_TEXT( aContainer );
2169  aContainer->Add( txt );
2170  txt->SetText( aCadstarText.Text );
2171 
2172  wxPoint rotatedTextPos = getKiCadPoint( aCadstarText.Position );
2173  RotatePoint( &rotatedTextPos, aTransformCentre, aRotationAngle );
2174  rotatedTextPos.x =
2175  KiROUND( (double) ( rotatedTextPos.x - aTransformCentre.x ) * aScalingFactor );
2176  rotatedTextPos.y =
2177  KiROUND( (double) ( rotatedTextPos.y - aTransformCentre.y ) * aScalingFactor );
2178  rotatedTextPos += aTransformCentre;
2179  txt->SetTextPos( rotatedTextPos );
2180  txt->SetPosition( rotatedTextPos );
2181 
2182  txt->SetTextAngle( getAngleTenthDegree( aCadstarText.OrientAngle ) + aRotationAngle );
2183 
2184  if( aCadstarText.Mirror != aMirrorInvert ) // If mirroring, invert angle to match CADSTAR
2185  txt->SetTextAngle( -txt->GetTextAngle() );
2186 
2187  txt->SetMirrored( aCadstarText.Mirror );
2188 
2189  TEXTCODE tc = getTextCode( aCadstarText.TextCodeID );
2190 
2192 
2193  wxSize unscaledTextSize;
2194  unscaledTextSize.x = getKiCadLength( tc.Width );
2195 
2196  // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
2197  // to work well for most fonts.
2198  if( unscaledTextSize.x == 0 )
2199  unscaledTextSize.x = getKiCadLength( tc.Height );
2200 
2201  unscaledTextSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
2202  txt->SetTextSize( unscaledTextSize );
2203 
2204  switch( aCadstarText.Alignment )
2205  {
2206  case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
2207  case ALIGNMENT::BOTTOMLEFT:
2210  break;
2211 
2215  break;
2216 
2220  break;
2221 
2222  case ALIGNMENT::CENTERLEFT:
2225  break;
2226 
2230  break;
2231 
2235  break;
2236 
2237  case ALIGNMENT::TOPLEFT:
2240  break;
2241 
2242  case ALIGNMENT::TOPCENTER:
2245  break;
2246 
2247  case ALIGNMENT::TOPRIGHT:
2250  break;
2251 
2252  default:
2253  wxFAIL_MSG( "Unknown Aligment - needs review!" );
2254  }
2255 
2256  if( aMirrorInvert )
2257  {
2258  txt->Flip( aTransformCentre, true );
2259  }
2260 
2261  //scale it after flipping:
2262  if( aScalingFactor != 1.0 )
2263  {
2264  wxSize scaledTextSize;
2265  scaledTextSize.x = KiROUND( (double) unscaledTextSize.x * aScalingFactor );
2266  scaledTextSize.y = KiROUND( (double) unscaledTextSize.y * aScalingFactor );
2267  txt->SetTextSize( scaledTextSize );
2268  txt->SetTextThickness(
2269  KiROUND( (double) getKiCadLength( tc.LineWidth ) * aScalingFactor ) );
2270  }
2271 
2272  txt->Move( aMoveVector );
2273 
2274  if( aCadstarText.Alignment == ALIGNMENT::NO_ALIGNMENT )
2276 
2277  LAYER_ID layersToDrawOn = aCadstarLayerOverride;
2278 
2279  if( layersToDrawOn.IsEmpty() )
2280  layersToDrawOn = aCadstarText.LayerID;
2281 
2282  if( isLayerSet( layersToDrawOn ) )
2283  {
2284  //Make a copy on each layer
2285 
2286  LSEQ layers = getKiCadLayerSet( layersToDrawOn ).Seq();
2287  PCB_TEXT* newtxt;
2288 
2289  for( PCB_LAYER_ID layer : layers )
2290  {
2291  txt->SetLayer( layer );
2292  newtxt = static_cast<PCB_TEXT*>( txt->Duplicate() );
2293  m_board->Add( newtxt, ADD_MODE::APPEND );
2294 
2295  if( !aCadstarGroupID.IsEmpty() )
2296  addToGroup( aCadstarGroupID, newtxt );
2297  }
2298 
2299  m_board->Remove( txt );
2300  delete txt;
2301  }
2302  else
2303  {
2304  txt->SetLayer( getKiCadLayer( layersToDrawOn ) );
2305 
2306  if( !aCadstarGroupID.IsEmpty() )
2307  addToGroup( aCadstarGroupID, txt );
2308  }
2309  //TODO Handle different font types when KiCad can support it.
2310 }
2311 
2312 
2314  const PCB_LAYER_ID& aKiCadLayer,
2315  const int& aLineThickness,
2316  const wxString& aShapeName,
2317  BOARD_ITEM_CONTAINER* aContainer,
2318  const GROUP_ID& aCadstarGroupID,
2319  const wxPoint& aMoveVector,
2320  const double& aRotationAngle,
2321  const double& aScalingFactor,
2322  const wxPoint& aTransformCentre,
2323  const bool& aMirrorInvert )
2324 {
2325  switch( aCadstarShape.Type )
2326  {
2327  case SHAPE_TYPE::OPENSHAPE:
2328  case SHAPE_TYPE::OUTLINE:
2330  drawCadstarVerticesAsSegments( aCadstarShape.Vertices, aKiCadLayer, aLineThickness,
2331  aContainer, aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2332  aTransformCentre, aMirrorInvert );
2333  drawCadstarCutoutsAsSegments( aCadstarShape.Cutouts, aKiCadLayer, aLineThickness,
2334  aContainer, aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2335  aTransformCentre, aMirrorInvert );
2336  break;
2337 
2338  case SHAPE_TYPE::HATCHED:
2340  wxLogWarning( wxString::Format(
2341  _( "The shape for '%s' is Hatch filled in CADSTAR, which has no KiCad equivalent. "
2342  "Using solid fill instead." ),
2343  aShapeName ) );
2344 
2345  case SHAPE_TYPE::SOLID:
2346  {
2347  PCB_SHAPE* shape;
2348 
2349  if( isFootprint( aContainer ) )
2350  {
2351  shape = new FP_SHAPE( (FOOTPRINT*) aContainer, S_POLYGON );
2352  }
2353  else
2354  {
2355  shape = new PCB_SHAPE( aContainer );
2356  shape->SetShape( S_POLYGON );
2357  }
2358 
2359  shape->SetFilled( true );
2360 
2362  aCadstarShape, -1, aContainer, aMoveVector, aRotationAngle, aScalingFactor,
2363  aTransformCentre, aMirrorInvert );
2364 
2365  shapePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
2366 
2367  shape->SetPolyShape( shapePolys );
2368  shape->SetWidth( aLineThickness );
2369  shape->SetLayer( aKiCadLayer );
2370  aContainer->Add( shape, ADD_MODE::APPEND );
2371 
2372  if( !aCadstarGroupID.IsEmpty() )
2373  addToGroup( aCadstarGroupID, shape );
2374  }
2375  break;
2376  }
2377 }
2378 
2379 
2380 void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarCutoutsAsSegments( const std::vector<CUTOUT>& aCutouts,
2381  const PCB_LAYER_ID& aKiCadLayer,
2382  const int& aLineThickness,
2383  BOARD_ITEM_CONTAINER* aContainer,
2384  const GROUP_ID& aCadstarGroupID,
2385  const wxPoint& aMoveVector,
2386  const double& aRotationAngle,
2387  const double& aScalingFactor,
2388  const wxPoint& aTransformCentre,
2389  const bool& aMirrorInvert )
2390 {
2391  for( CUTOUT cutout : aCutouts )
2392  {
2393  drawCadstarVerticesAsSegments( cutout.Vertices, aKiCadLayer, aLineThickness, aContainer,
2394  aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2395  aTransformCentre, aMirrorInvert );
2396  }
2397 }
2398 
2399 
2401  const std::vector<VERTEX>& aCadstarVertices, const PCB_LAYER_ID& aKiCadLayer,
2402  const int& aLineThickness, BOARD_ITEM_CONTAINER* aContainer,
2403  const GROUP_ID& aCadstarGroupID, const wxPoint& aMoveVector, const double& aRotationAngle,
2404  const double& aScalingFactor, const wxPoint& aTransformCentre, const bool& aMirrorInvert )
2405 {
2406  std::vector<PCB_SHAPE*> drawSegments =
2407  getDrawSegmentsFromVertices( aCadstarVertices, aContainer, aCadstarGroupID, aMoveVector,
2408  aRotationAngle, aScalingFactor, aTransformCentre, aMirrorInvert );
2409 
2410  for( PCB_SHAPE* ds : drawSegments )
2411  {
2412  ds->SetWidth( aLineThickness );
2413  ds->SetLayer( aKiCadLayer );
2414  ds->SetParent( aContainer );
2415  aContainer->Add( ds, ADD_MODE::APPEND );
2416  }
2417 }
2418 
2419 
2421  const std::vector<VERTEX>& aCadstarVertices, BOARD_ITEM_CONTAINER* aContainer,
2422  const GROUP_ID& aCadstarGroupID, const wxPoint& aMoveVector, const double& aRotationAngle,
2423  const double& aScalingFactor, const wxPoint& aTransformCentre, const bool& aMirrorInvert )
2424 {
2425  std::vector<PCB_SHAPE*> drawSegments;
2426 
2427  if( aCadstarVertices.size() < 2 )
2428  //need at least two points to draw a segment! (unlikely but possible to have only one)
2429  return drawSegments;
2430 
2431  const VERTEX* prev = &aCadstarVertices.at( 0 ); // first one should always be a point vertex
2432  const VERTEX* cur;
2433 
2434  for( size_t i = 1; i < aCadstarVertices.size(); i++ )
2435  {
2436  cur = &aCadstarVertices.at( i );
2437  drawSegments.push_back(
2438  getDrawSegmentFromVertex( prev->End, *cur, aContainer, aCadstarGroupID, aMoveVector,
2439  aRotationAngle, aScalingFactor, aTransformCentre, aMirrorInvert ) );
2440  prev = cur;
2441  }
2442 
2443  return drawSegments;
2444 }
2445 
2446 
2448  const VERTEX& aCadstarVertex,
2449  BOARD_ITEM_CONTAINER* aContainer,
2450  const GROUP_ID& aCadstarGroupID,
2451  const wxPoint& aMoveVector,
2452  const double& aRotationAngle,
2453  const double& aScalingFactor,
2454  const wxPoint& aTransformCentre,
2455  const bool& aMirrorInvert )
2456 {
2457  PCB_SHAPE* ds = nullptr;
2458  bool cw = false;
2459  double arcStartAngle, arcEndAngle, arcAngle;
2460 
2461  wxPoint startPoint = getKiCadPoint( aCadstarStartPoint );
2462  wxPoint endPoint = getKiCadPoint( aCadstarVertex.End );
2463  wxPoint centerPoint;
2464 
2465  if( aCadstarVertex.Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE
2466  || aCadstarVertex.Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
2467  centerPoint = ( startPoint + endPoint ) / 2;
2468  else
2469  centerPoint = getKiCadPoint( aCadstarVertex.Center );
2470 
2471  switch( aCadstarVertex.Type )
2472  {
2473 
2474  case VERTEX_TYPE::POINT:
2475 
2476  if( isFootprint( aContainer ) )
2477  {
2478  ds = new FP_SHAPE( static_cast<FOOTPRINT*>( aContainer ), S_SEGMENT );
2479  }
2480  else
2481  {
2482  ds = new PCB_SHAPE( aContainer );
2483  ds->SetShape( S_SEGMENT );
2484  }
2485 
2486  ds->SetStart( startPoint );
2487  ds->SetEnd( endPoint );
2488  break;
2489 
2492  cw = true;
2494 
2497 
2498  if( isFootprint( aContainer ) )
2499  {
2500  ds = new FP_SHAPE((FOOTPRINT*) aContainer, S_ARC );
2501  }
2502  else
2503  {
2504  ds = new PCB_SHAPE( aContainer );
2505  ds->SetShape( S_ARC );
2506  }
2507 
2508  ds->SetArcStart( startPoint );
2509  ds->SetCenter( centerPoint );
2510 
2511  arcStartAngle = getPolarAngle( startPoint - centerPoint );
2512  arcEndAngle = getPolarAngle( endPoint - centerPoint );
2513  arcAngle = arcEndAngle - arcStartAngle;
2514  //TODO: detect if we are supposed to draw a circle instead (i.e. two SEMICIRCLEs
2515  // with opposite start/end points and same centre point)
2516 
2517  if( cw )
2518  ds->SetAngle( NormalizeAnglePos( arcAngle ) );
2519  else
2520  ds->SetAngle( NormalizeAngleNeg( arcAngle ) );
2521 
2522  break;
2523  }
2524 
2525  //Apply transforms
2526  if( aMirrorInvert )
2527  ds->Flip( aTransformCentre, true );
2528 
2529  if( aScalingFactor != 1.0 )
2530  {
2531  ds->Move( -aTransformCentre );
2532  ds->Scale( aScalingFactor );
2533  ds->Move( aTransformCentre );
2534  }
2535 
2536  if( aRotationAngle != 0.0 )
2537  ds->Rotate( aTransformCentre, aRotationAngle );
2538 
2539  if( aMoveVector != wxPoint{ 0, 0 } )
2540  ds->Move( aMoveVector );
2541 
2542  if( isFootprint( aContainer ) && ds != nullptr )
2543  static_cast<FP_SHAPE*>( ds )->SetLocalCoord();
2544 
2545  if( !aCadstarGroupID.IsEmpty() )
2546  addToGroup( aCadstarGroupID, ds );
2547 
2548  return ds;
2549 }
2550 
2551 
2553  const int& aLineThickness,
2554  BOARD_ITEM_CONTAINER* aParentContainer )
2555 {
2556  ZONE* zone = new ZONE( aParentContainer, isFootprint( aParentContainer ) );
2557 
2558  if( aCadstarShape.Type == SHAPE_TYPE::HATCHED )
2559  {
2562  }
2563  else
2564  {
2566  }
2567 
2568  SHAPE_POLY_SET polygon = getPolySetFromCadstarShape( aCadstarShape, aLineThickness );
2569 
2570  zone->AddPolygon( polygon.COutline( 0 ) );
2571 
2572  for( int i = 0; i < polygon.HoleCount( 0 ); i++ )
2573  zone->AddPolygon( polygon.CHole( 0, i ) );
2574 
2575  return zone;
2576 }
2577 
2578 
2580  const int& aLineThickness,
2581  BOARD_ITEM_CONTAINER* aContainer,
2582  const wxPoint& aMoveVector,
2583  const double& aRotationAngle,
2584  const double& aScalingFactor,
2585  const wxPoint& aTransformCentre,
2586  const bool& aMirrorInvert )
2587 {
2588  GROUP_ID noGroup = wxEmptyString;
2589 
2590  std::vector<PCB_SHAPE*> outlineSegments = getDrawSegmentsFromVertices( aCadstarShape.Vertices,
2591  aContainer, noGroup,
2592  aMoveVector,
2593  aRotationAngle,
2594  aScalingFactor,
2595  aTransformCentre,
2596  aMirrorInvert );
2597 
2598  SHAPE_POLY_SET polySet( getLineChainFromDrawsegments( outlineSegments ) );
2599 
2600  //cleanup
2601  for( PCB_SHAPE* seg : outlineSegments )
2602  delete seg;
2603 
2604  for( CUTOUT cutout : aCadstarShape.Cutouts )
2605  {
2606  std::vector<PCB_SHAPE*> cutoutSeg = getDrawSegmentsFromVertices( cutout.Vertices,
2607  aContainer, noGroup,
2608  aMoveVector,
2609  aRotationAngle,
2610  aScalingFactor,
2611  aTransformCentre,
2612  aMirrorInvert );
2613 
2614  polySet.AddHole( getLineChainFromDrawsegments( cutoutSeg ) );
2615 
2616  //cleanup
2617  for( PCB_SHAPE* seg : cutoutSeg )
2618  delete seg;
2619  }
2620 
2621  if( aLineThickness > 0 )
2622  polySet.Inflate( aLineThickness / 2, 32, SHAPE_POLY_SET::CORNER_STRATEGY::ROUND_ALL_CORNERS );
2623 
2624 #ifdef DEBUG
2625  for( int i = 0; i < polySet.OutlineCount(); ++i )
2626  {
2627  wxASSERT( polySet.Outline( i ).PointCount() > 2 );
2628 
2629  for( int j = 0; j < polySet.HoleCount( i ); ++j )
2630  {
2631  wxASSERT( polySet.Hole( i, j ).PointCount() > 2 );
2632  }
2633  }
2634 #endif
2635 
2636  return polySet;
2637 }
2638 
2639 
2641 {
2642  SHAPE_LINE_CHAIN lineChain;
2643 
2644  for( PCB_SHAPE* ds : aDrawsegments )
2645  {
2646  switch( ds->GetShape() )
2647  {
2648  case S_ARC:
2649  {
2650  if( ds->GetClass() == wxT( "MGRAPHIC" ) )
2651  {
2652  FP_SHAPE* em = (FP_SHAPE*) ds;
2653  SHAPE_ARC arc( em->GetStart0(), em->GetEnd0(), (double) em->GetAngle() / 10.0 );
2654  lineChain.Append( arc );
2655  }
2656  else
2657  {
2658  SHAPE_ARC arc( ds->GetCenter(), ds->GetArcStart(), (double) ds->GetAngle() / 10.0 );
2659  lineChain.Append( arc );
2660  }
2661  }
2662  break;
2663  case S_SEGMENT:
2664  if( ds->GetClass() == wxT( "MGRAPHIC" ) )
2665  {
2666  FP_SHAPE* em = (FP_SHAPE*) ds;
2667  lineChain.Append( em->GetStart0().x, em->GetStart0().y );
2668  lineChain.Append( em->GetEnd0().x, em->GetEnd0().y );
2669  }
2670  else
2671  {
2672  lineChain.Append( ds->GetStartX(), ds->GetStartY() );
2673  lineChain.Append( ds->GetEndX(), ds->GetEndY() );
2674  }
2675  break;
2676 
2677  default:
2678  wxFAIL_MSG( "Drawsegment type is unexpected. Ignored." );
2679  }
2680  }
2681 
2682  // Shouldn't have less than 3 points to make a closed shape!
2683  wxASSERT( lineChain.PointCount() > 2 );
2684 
2685  // Check if it is closed
2686  if( lineChain.GetPoint( 0 ) != lineChain.GetPoint( lineChain.PointCount() - 1 ) )
2687  {
2688  lineChain.Append( lineChain.GetPoint( 0 ) );
2689  }
2690 
2691  lineChain.SetClosed( true );
2692 
2693  return lineChain;
2694 }
2695 
2696 
2698  const std::vector<PCB_SHAPE*> aDrawsegments,
2699  BOARD_ITEM_CONTAINER* aParentContainer,
2700  NETINFO_ITEM* aNet, PCB_LAYER_ID aLayerOverride,
2701  int aWidthOverride )
2702 {
2703  std::vector<TRACK*> tracks;
2704 
2705  for( PCB_SHAPE* ds : aDrawsegments )
2706  {
2707  TRACK* track;
2708 
2709  switch( ds->GetShape() )
2710  {
2711  case S_ARC:
2712  if( ds->GetClass() == wxT( "MGRAPHIC" ) )
2713  {
2714  FP_SHAPE* em = (FP_SHAPE*) ds;
2715  SHAPE_ARC arc( em->GetStart0(), em->GetEnd0(), (double) em->GetAngle() / 10.0 );
2716  track = new ARC( aParentContainer, &arc );
2717  }
2718  else
2719  {
2720  SHAPE_ARC arc( ds->GetCenter(), ds->GetArcStart(), (double) ds->GetAngle() / 10.0 );
2721  track = new ARC( aParentContainer, &arc );
2722  }
2723  break;
2724  case S_SEGMENT:
2725  if( ds->GetClass() == wxT( "MGRAPHIC" ) )
2726  {
2727  FP_SHAPE* em = (FP_SHAPE*) ds;
2728  track = new TRACK( aParentContainer );
2729  track->SetStart( em->GetStart0() );
2730  track->SetEnd( em->GetEnd0() );
2731  }
2732  else
2733  {
2734  track = new TRACK( aParentContainer );
2735  track->SetStart( ds->GetStart() );
2736  track->SetEnd( ds->GetEnd() );
2737  }
2738  break;
2739 
2740  default:
2741  wxFAIL_MSG( "Drawsegment type is unexpected. Ignored." );
2742  continue;
2743  }
2744 
2745  if( aWidthOverride == -1 )
2746  track->SetWidth( ds->GetWidth() );
2747  else
2748  track->SetWidth( aWidthOverride );
2749 
2750  if( aLayerOverride == PCB_LAYER_ID::UNDEFINED_LAYER )
2751  track->SetLayer( ds->GetLayer() );
2752  else
2753  track->SetLayer( aLayerOverride );
2754 
2755  if( aNet != nullptr )
2756  track->SetNet( aNet );
2757 
2758  tracks.push_back( track );
2759  aParentContainer->Add( track, ADD_MODE::APPEND );
2760  }
2761 
2762  return tracks;
2763 }
2764 
2765 
2767  const ATTRIBUTE_ID& aCadstarAttributeID,
2768  FOOTPRINT* aFootprint,
2769  const wxString& aAttributeValue )
2770 {
2771  FP_TEXT* txt;
2772 
2773  if( aCadstarAttributeID == COMPONENT_NAME_ATTRID )
2774  {
2775  txt = &aFootprint->Reference(); //text should be set outside this function
2776  }
2777  else if( aCadstarAttributeID == PART_NAME_ATTRID )
2778  {
2779  if( aFootprint->Value().GetText().IsEmpty() )
2780  {
2781  // Use PART_NAME_ATTRID as the value is value field is blank
2782  aFootprint->SetValue( aAttributeValue );
2783  txt = &aFootprint->Value();
2784  }
2785  else
2786  {
2787  txt = new FP_TEXT( aFootprint );
2788  aFootprint->Add( txt );
2789  txt->SetText( aAttributeValue );
2790  }
2791  txt->SetVisible( false ); //make invisible to avoid clutter.
2792  }
2793  else if( aCadstarAttributeID != COMPONENT_NAME_2_ATTRID
2794  && getAttributeName( aCadstarAttributeID ) == wxT( "Value" ) )
2795  {
2796  if( !aFootprint->Value().GetText().IsEmpty() )
2797  {
2798  //copy the object
2799  aFootprint->Add( aFootprint->Value().Duplicate() );
2800  }
2801 
2802  aFootprint->SetValue( aAttributeValue );
2803  txt = &aFootprint->Value();
2804  txt->SetVisible( false ); //make invisible to avoid clutter.
2805  }
2806  else
2807  {
2808  txt = new FP_TEXT( aFootprint );
2809  aFootprint->Add( txt );
2810  txt->SetText( aAttributeValue );
2811  txt->SetVisible( false ); //make all user attributes invisible to avoid clutter.
2812  //TODO: Future improvement - allow user to decide what to do with attributes
2813  }
2814 
2815  wxPoint rotatedTextPos = getKiCadPoint( aCadstarAttrLoc.Position ) - aFootprint->GetPosition();
2816  RotatePoint( &rotatedTextPos, -aFootprint->GetOrientation() );
2817 
2818  txt->SetTextPos( getKiCadPoint( aCadstarAttrLoc.Position ) );
2819  txt->SetPos0( rotatedTextPos );
2820  txt->SetLayer( getKiCadLayer( aCadstarAttrLoc.LayerID ) );
2821  txt->SetMirrored( aCadstarAttrLoc.Mirror );
2822  txt->SetTextAngle(
2823  getAngleTenthDegree( aCadstarAttrLoc.OrientAngle ) - aFootprint->GetOrientation() );
2824 
2825  if( aCadstarAttrLoc.Mirror ) // If mirroring, invert angle to match CADSTAR
2826  txt->SetTextAngle( -txt->GetTextAngle() );
2827 
2828  TEXTCODE tc = getTextCode( aCadstarAttrLoc.TextCodeID );
2829 
2831 
2832  wxSize txtSize;
2833  txtSize.x = getKiCadLength( tc.Width );
2834 
2835  // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
2836  // to work well for most fonts.
2837  if( txtSize.x == 0 )
2838  txtSize.x = getKiCadLength( tc.Height );
2839 
2840  txtSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
2841  txt->SetTextSize( txtSize );
2842  txt->SetKeepUpright( false ); //Keeping it upright seems to result in incorrect orientation
2843 
2844  switch( aCadstarAttrLoc.Alignment )
2845  {
2846  case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
2849  case ALIGNMENT::BOTTOMLEFT:
2852  break;
2853 
2857  break;
2858 
2862  break;
2863 
2864  case ALIGNMENT::CENTERLEFT:
2867  break;
2868 
2872  break;
2873 
2877  break;
2878 
2879  case ALIGNMENT::TOPLEFT:
2882  break;
2883 
2884  case ALIGNMENT::TOPCENTER:
2887  break;
2888 
2889  case ALIGNMENT::TOPRIGHT:
2892  break;
2893 
2894  default:
2895  wxFAIL_MSG( "Unknown Aligment - needs review!" );
2896  }
2897 
2898  //TODO Handle different font types when KiCad can support it.
2899 }
2900 
2901 
2903 {
2904  wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID )
2905  != Assignments.Codedefs.LineCodes.end(),
2907 
2908  return getKiCadLength( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Width );
2909 }
2910 
2911 
2913  const COPPERCODE_ID& aCadstaCopperCodeID )
2914 {
2915  wxCHECK( Assignments.Codedefs.CopperCodes.find( aCadstaCopperCodeID )
2916  != Assignments.Codedefs.CopperCodes.end(),
2917  COPPERCODE() );
2918 
2919  return Assignments.Codedefs.CopperCodes.at( aCadstaCopperCodeID );
2920 }
2921 
2922 
2924  const TEXTCODE_ID& aCadstarTextCodeID )
2925 {
2926  wxCHECK( Assignments.Codedefs.TextCodes.find( aCadstarTextCodeID )
2927  != Assignments.Codedefs.TextCodes.end(),
2928  TEXTCODE() );
2929 
2930  return Assignments.Codedefs.TextCodes.at( aCadstarTextCodeID );
2931 }
2932 
2933 
2935  const PADCODE_ID& aCadstarPadCodeID )
2936 {
2937  wxCHECK( Assignments.Codedefs.PadCodes.find( aCadstarPadCodeID )
2938  != Assignments.Codedefs.PadCodes.end(),
2939  PADCODE() );
2940 
2941  return Assignments.Codedefs.PadCodes.at( aCadstarPadCodeID );
2942 }
2943 
2944 
2946  const VIACODE_ID& aCadstarViaCodeID )
2947 {
2948  wxCHECK( Assignments.Codedefs.ViaCodes.find( aCadstarViaCodeID )
2949  != Assignments.Codedefs.ViaCodes.end(),
2950  VIACODE() );
2951 
2952  return Assignments.Codedefs.ViaCodes.at( aCadstarViaCodeID );
2953 }
2954 
2955 
2957  const LAYERPAIR_ID& aCadstarLayerPairID )
2958 {
2959  wxCHECK( Assignments.Codedefs.LayerPairs.find( aCadstarLayerPairID )
2960  != Assignments.Codedefs.LayerPairs.end(),
2961  LAYERPAIR() );
2962 
2963  return Assignments.Codedefs.LayerPairs.at( aCadstarLayerPairID );
2964 }
2965 
2966 
2967 wxString CADSTAR_PCB_ARCHIVE_LOADER::getAttributeName( const ATTRIBUTE_ID& aCadstarAttributeID )
2968 {
2969  wxCHECK( Assignments.Codedefs.AttributeNames.find( aCadstarAttributeID )
2971  wxEmptyString );
2972 
2973  return Assignments.Codedefs.AttributeNames.at( aCadstarAttributeID ).Name;
2974 }
2975 
2976 
2978  const std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE>& aCadstarAttributeMap )
2979 {
2980  wxCHECK( aCadstarAttributeMap.find( aCadstarAttributeID ) != aCadstarAttributeMap.end(),
2981  wxEmptyString );
2982 
2983  return aCadstarAttributeMap.at( aCadstarAttributeID ).Value;
2984 }
2985 
2986 
2988  const PART_ID& aCadstarPartID )
2989 {
2990  wxCHECK( Parts.PartDefinitions.find( aCadstarPartID ) != Parts.PartDefinitions.end(), PART() );
2991 
2992  return Parts.PartDefinitions.at( aCadstarPartID );
2993 }
2994 
2995 
2997  const ROUTECODE_ID& aCadstarRouteCodeID )
2998 {
2999  wxCHECK( Assignments.Codedefs.RouteCodes.find( aCadstarRouteCodeID )
3000  != Assignments.Codedefs.RouteCodes.end(),
3001  ROUTECODE() );
3002 
3003  return Assignments.Codedefs.RouteCodes.at( aCadstarRouteCodeID );
3004 }
3005 
3006 
3008  const HATCHCODE_ID& aCadstarHatchcodeID )
3009 {
3010  wxCHECK( Assignments.Codedefs.HatchCodes.find( aCadstarHatchcodeID )
3011  != Assignments.Codedefs.HatchCodes.end(),
3012  HATCHCODE() );
3013 
3014  return Assignments.Codedefs.HatchCodes.at( aCadstarHatchcodeID );
3015 }
3016 
3017 
3019  const HATCHCODE_ID& aCadstarHatchcodeID )
3020 {
3021  checkAndLogHatchCode( aCadstarHatchcodeID );
3022  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3023 
3024  if( hcode.Hatches.size() < 1 )
3026  else
3027  return getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle );
3028 }
3029 
3030 
3032  const HATCHCODE_ID& aCadstarHatchcodeID )
3033 {
3034  checkAndLogHatchCode( aCadstarHatchcodeID );
3035  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3036 
3037  if( hcode.Hatches.size() < 1 )
3039  else
3040  return getKiCadLength( hcode.Hatches.at( 0 ).LineWidth );
3041 }
3042 
3043 
3045 {
3046  checkAndLogHatchCode( aCadstarHatchcodeID );
3047  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3048 
3049  if( hcode.Hatches.size() < 1 )
3051  else
3052  return getKiCadLength( hcode.Hatches.at( 0 ).Step );
3053 }
3054 
3055 
3057 {
3058  wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), nullptr );
3059 
3060  return m_groupMap.at( aCadstarGroupID );
3061 }
3062 
3063 
3065 {
3066  if( m_hatchcodesTested.find( aCadstarHatchcodeID ) != m_hatchcodesTested.end() )
3067  {
3068  return; //already checked
3069  }
3070  else
3071  {
3072  HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3073 
3074  if( hcode.Hatches.size() != 2 )
3075  {
3076  wxLogWarning( wxString::Format(
3077  _( "The CADSTAR Hatching code '%s' has %d hatches defined. "
3078  "KiCad only supports 2 hatches (crosshatching) 90 degrees apart. "
3079  "The imported hatching is crosshatched." ),
3080  hcode.Name, (int) hcode.Hatches.size() ) );
3081  }
3082  else
3083  {
3084  if( hcode.Hatches.at( 0 ).LineWidth != hcode.Hatches.at( 1 ).LineWidth )
3085  {
3086  wxLogWarning( wxString::Format(
3087  _( "The CADSTAR Hatching code '%s' has different line widths for each "
3088  "hatch. KiCad only supports one width for the haching. The imported "
3089  "hatching uses the width defined in the first hatch definition, i.e. "
3090  "%.2f mm." ),
3091  hcode.Name,
3092  (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).LineWidth ) )
3093  / 1E6 ) );
3094  }
3095 
3096  if( hcode.Hatches.at( 0 ).Step != hcode.Hatches.at( 1 ).Step )
3097  {
3098  wxLogWarning( wxString::Format(
3099  _( "The CADSTAR Hatching code '%s' has different step sizes for each "
3100  "hatch. KiCad only supports one step size for the haching. The imported "
3101  "hatching uses the step size defined in the first hatching definition, "
3102  "i.e. %.2f mm." ),
3103  hcode.Name,
3104  (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).Step ) )
3105  / 1E6 ) );
3106  }
3107 
3108  if( abs( hcode.Hatches.at( 0 ).OrientAngle - hcode.Hatches.at( 1 ).OrientAngle )
3109  != 90000 )
3110  {
3111  wxLogWarning( wxString::Format(
3112  _( "The hatches in CADSTAR Hatching code '%s' have an angle "
3113  "difference of %.1f degrees. KiCad only supports hatching 90 "
3114  "degrees apart. The imported hatching has two hatches 90 "
3115  "degrees apart, oriented %.1f degrees from horizontal." ),
3116  hcode.Name,
3117  getAngleDegrees( abs( hcode.Hatches.at( 0 ).OrientAngle
3118  - hcode.Hatches.at( 1 ).OrientAngle ) ),
3119  getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle ) ) );
3120  }
3121  }
3122 
3123  m_hatchcodesTested.insert( aCadstarHatchcodeID );
3124  }
3125 }
3126 
3127 
3129  DIMENSION_BASE* aKiCadDim )
3130 {
3131  UNITS dimensionUnits = aCadstarDim.LinearUnits;
3132  TEXTCODE txtCode = getTextCode( aCadstarDim.Text.TextCodeID );
3133  int correctedHeight = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( txtCode.Height ) );
3134  wxSize txtSize( getKiCadLength( txtCode.Width ), correctedHeight );
3135  LINECODE linecode = Assignments.Codedefs.LineCodes.at( aCadstarDim.Line.LineCodeID );
3136 
3137  aKiCadDim->SetLayer( getKiCadLayer( aCadstarDim.LayerID ) );
3138  aKiCadDim->SetPrecision( aCadstarDim.Precision );
3139  aKiCadDim->SetStart( getKiCadPoint( aCadstarDim.ExtensionLineParams.Start ) );
3140  aKiCadDim->SetEnd( getKiCadPoint( aCadstarDim.ExtensionLineParams.End ) );
3141  aKiCadDim->SetExtensionOffset( getKiCadLength( aCadstarDim.ExtensionLineParams.Offset ) );
3142  aKiCadDim->SetLineThickness( getKiCadLength( linecode.Width ) );
3143  aKiCadDim->Text().SetTextThickness( getKiCadLength( txtCode.LineWidth ) );
3144  aKiCadDim->Text().SetTextSize( txtSize );
3145 
3146  // Find prefix and suffix:
3147  wxString prefix = wxEmptyString;
3148  wxString suffix = wxEmptyString;
3149  size_t startpos = aCadstarDim.Text.Text.Find( wxT( "<@DISTANCE" ) );
3150 
3151  if( startpos != wxNOT_FOUND )
3152  {
3153  prefix = ParseTextFields( aCadstarDim.Text.Text.SubString( 0, startpos - 1 ), &m_context );
3154  wxString remainingStr = aCadstarDim.Text.Text.Mid( startpos );
3155  size_t endpos = remainingStr.Find( "@>" );
3156  suffix = ParseTextFields( remainingStr.Mid( endpos + 2 ), &m_context );
3157  }
3158 
3159  if( suffix.StartsWith( "mm" ) )
3160  {
3162  suffix = suffix.Mid( 2 );
3163  }
3164  else
3165  {
3167  }
3168 
3169  aKiCadDim->SetPrefix( prefix );
3170  aKiCadDim->SetSuffix( suffix );
3171 
3172  if( aCadstarDim.LinearUnits == UNITS::DESIGN )
3173  {
3174  // For now we will hardcode the units as per the original CADSTAR design.
3175  // TODO: update this when KiCad supports design units
3177  dimensionUnits = Assignments.Technology.Units;
3178  }
3179 
3180  switch( dimensionUnits )
3181  {
3182  case UNITS::METER:
3183  case UNITS::CENTIMETER:
3184  case UNITS::MICROMETRE:
3185  wxLogWarning( wxString::Format( _( "Dimension ID %s uses a type of unit that "
3186  "is not supported in KiCad. Milimetres were "
3187  "applied instead." ),
3188  aCadstarDim.ID ) );
3190  case UNITS::MM:
3192  break;
3193 
3194  case UNITS::INCH:
3195  aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::INCHES );
3196  break;
3197 
3198  case UNITS::THOU:
3199  aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::MILS );
3200  break;
3201 
3202  case UNITS::DESIGN:
3203  wxFAIL_MSG( "We should have handled design units before coming here!" );
3204  break;
3205  }
3206 }
3207 
3208 
3210 {
3211  std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
3212  std::set<std::pair<TEMPLATE_ID, TEMPLATE_ID>> scheduleInferPriorityFromOutline;
3213 
3214  // Calculate the intesection between aPolygon and the outline of aZone
3215  auto intersectionArea = [&]( SHAPE_POLY_SET aPolygon, ZONE* aZone ) -> double
3216  {
3217  SHAPE_POLY_SET intersectShape( *aZone->Outline() );
3218 
3219  intersectShape.BooleanIntersection( aPolygon,
3221  return intersectShape.Area();
3222  };
3223 
3224  // Lambda to determine if the zone with template ID 'a' is lower priority than 'b'
3225  auto isLowerPriority = [&]( const TEMPLATE_ID& a, const TEMPLATE_ID& b ) -> bool
3226  {
3227  return winningOverlaps[b].count( a ) > 0;
3228  };
3229 
3230  for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = m_zonesMap.begin();
3231  it1 != m_zonesMap.end(); ++it1 )
3232  {
3233  TEMPLATE thisTemplate = Layout.Templates.at( it1->first );
3234  PCB_LAYER_ID thisLayer = getKiCadLayer( thisTemplate.LayerID );
3235  ZONE* thisZone = it1->second;
3236 
3237  for( std::map<TEMPLATE_ID, ZONE*>::iterator it2 = it1;
3238  it2 != m_zonesMap.end(); ++it2 )
3239  {
3240  TEMPLATE otherTemplate = Layout.Templates.at( it2->first );
3241  PCB_LAYER_ID otherLayer = getKiCadLayer( otherTemplate.LayerID );
3242  ZONE* otherZone = it2->second;
3243 
3244  if( thisTemplate.ID == otherTemplate.ID )
3245  continue;
3246 
3247  if( thisLayer != otherLayer )
3248  continue;
3249 
3250  SHAPE_POLY_SET thisZonePolyFill = thisZone->GetFilledPolysList( thisLayer );
3251  SHAPE_POLY_SET otherZonePolyFill = otherZone->GetFilledPolysList( otherLayer );
3252 
3253  if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
3254  {
3255  // Intersect the filled polygons of thisZone with the *outline* of otherZone
3256  double areaThis = intersectionArea( thisZonePolyFill, otherZone );
3257  // Viceversa
3258  double areaOther = intersectionArea( otherZonePolyFill, thisZone );
3259 
3260  // Best effort: Compare Areas
3261  // If thisZone's fill polygons overlap otherZone's outline *and* the opposite
3262  // is true: otherZone's fill polygons overlap thisZone's outline then compare the
3263  // intersection areas to decide which of the two zones should have higher priority
3264  // There are some edge cases where this might not work, but it is in the minority.
3265  if( areaThis > areaOther )
3266  {
3267  winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3268  }
3269  else if( areaOther > 0.0 )
3270  {
3271  winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3272  }
3273  else
3274  {
3275  scheduleInferPriorityFromOutline.insert(
3276  { thisTemplate.ID, otherTemplate.ID } );
3277  }
3278  }
3279  else
3280  {
3281  // One of the templates is not poured in the original CADSTAR design.
3282  // Lets infer the priority based of the outlines instead
3283  scheduleInferPriorityFromOutline.insert( { thisTemplate.ID, otherTemplate.ID } );
3284  }
3285  }
3286  }
3287 
3288  // Build a set of unique TEMPLATE_IDs of all the zones that intersect with another one
3289  std::set<TEMPLATE_ID> intersectingIDs;
3290 
3291  for( const std::pair<TEMPLATE_ID, std::set<TEMPLATE_ID>>& idPair : winningOverlaps )
3292  {
3293  intersectingIDs.insert( idPair.first );
3294  intersectingIDs.insert( idPair.second.begin(), idPair.second.end() );
3295  }
3296 
3297  // Now store them in a vector
3298  std::vector<TEMPLATE_ID> sortedIDs;
3299 
3300  for( const TEMPLATE_ID& id : intersectingIDs )
3301  {
3302  sortedIDs.push_back( id );
3303  }
3304 
3305  // sort by priority
3306  std::sort( sortedIDs.begin(), sortedIDs.end(), isLowerPriority );
3307 
3308  TEMPLATE_ID prevID = wxEmptyString;
3309 
3310  for( const TEMPLATE_ID& id : sortedIDs )
3311  {
3312  if( prevID.IsEmpty() )
3313  {
3314  prevID = id;
3315  continue;
3316  }
3317 
3318  wxASSERT( !isLowerPriority( id, prevID ) );
3319 
3320  int newPriority = m_zonesMap.at( prevID )->GetPriority();
3321 
3322  // Only increase priority of the current zone
3323  if( isLowerPriority( prevID, id ) )
3324  newPriority++;
3325 
3326  m_zonesMap.at( id )->SetPriority( newPriority );
3327  prevID = id;
3328  }
3329 
3330 }
3331 
3332 
3334 {
3335  if( m_componentMap.find( aCadstarComponentID ) == m_componentMap.end() )
3336  return nullptr;
3337  else
3338  return m_componentMap.at( aCadstarComponentID );
3339 }
3340 
3341 
3342 wxPoint CADSTAR_PCB_ARCHIVE_LOADER::getKiCadPoint( wxPoint aCadstarPoint )
3343 {
3344  wxPoint retval;
3345 
3346  retval.x = ( aCadstarPoint.x - m_designCenter.x ) * KiCadUnitMultiplier;
3347  retval.y = -( aCadstarPoint.y - m_designCenter.y ) * KiCadUnitMultiplier;
3348 
3349  return retval;
3350 }
3351 
3352 
3354 {
3355 
3356  return NormalizeAnglePos( ArcTangente( aPoint.y, aPoint.x ) );
3357 }
3358 
3359 
3361 {
3362  if( aCadstarNetID.IsEmpty() )
3363  return nullptr;
3364  else if( m_netMap.find( aCadstarNetID ) != m_netMap.end() )
3365  {
3366  return m_netMap.at( aCadstarNetID );
3367  }
3368  else
3369  {
3370  wxCHECK( Layout.Nets.find( aCadstarNetID ) != Layout.Nets.end(), nullptr );
3371 
3372  NET_PCB csNet = Layout.Nets.at( aCadstarNetID );
3373  wxString newName = csNet.Name;
3374 
3375  if( csNet.Name.IsEmpty() )
3376  {
3377  if( csNet.Pins.size() > 0 )
3378  {
3379  // Create default KiCad net naming:
3380 
3381  NET_PCB::PIN firstPin = ( *csNet.Pins.begin() ).second;
3382  //we should have already loaded the component with loadComponents() :
3383  FOOTPRINT* m = getFootprintFromCadstarID( firstPin.ComponentID );
3384  newName = wxT( "Net-(" );
3385  newName << m->Reference().GetText();
3386  newName << "-Pad" << wxString::Format( "%ld", firstPin.PadID ) << ")";
3387  }
3388  else
3389  {
3390  wxFAIL_MSG( "A net with no pins associated?" );
3391  newName = wxT( "csNet-" );
3392  newName << wxString::Format( "%i", csNet.SignalNum );
3393  }
3394  }
3395 
3396  if( !m_doneNetClassWarning && !csNet.NetClassID.IsEmpty()
3397  && csNet.NetClassID != wxT( "NONE" ) )
3398  {
3399  wxLogMessage(
3400  _( "The CADSTAR design contains nets with a 'Net Class' assigned. KiCad does "
3401  "not have an equivalent to CADSTAR's Net Class so these elements were not "
3402  "imported. Note: KiCad's version of 'Net Class' is closer to CADSTAR's "
3403  "'Net Route Code' (which has been imported for all nets)." ) );
3404  m_doneNetClassWarning = true;
3405  }
3406 
3407  if( !m_doneSpacingClassWarning && !csNet.SpacingClassID.IsEmpty()
3408  && csNet.SpacingClassID != wxT( "NONE" ) )
3409  {
3410  wxLogWarning( _( "The CADSTAR design contains nets with a 'Spacing Class' assigned. "
3411  "KiCad does not have an equivalent to CADSTAR's Spacing Class so "
3412  "these elements were not imported. Please review the design rules as "
3413  "copper pours will affected by this." ) );
3415  }
3416 
3417  NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, newName, ++m_numNets );
3418  m_board->Add( netInfo, ADD_MODE::APPEND );
3419 
3420  if( m_netClassMap.find( csNet.RouteCodeID ) != m_netClassMap.end() )
3421  {
3422  NETCLASSPTR netclass = m_netClassMap.at( csNet.RouteCodeID );
3423  netInfo->SetNetClass( netclass );
3424  }
3425  else
3426  {
3427  ROUTECODE rc = getRouteCode( csNet.RouteCodeID );
3428  NETCLASSPTR netclass( new NETCLASS( rc.Name ) );
3429  netclass->SetTrackWidth( getKiCadLength( rc.OptimalWidth ) );
3430  netInfo->SetNetClass( netclass );
3431  m_netClassMap.insert( { csNet.RouteCodeID, netclass } );
3432  }
3433 
3434  m_netMap.insert( { aCadstarNetID, netInfo } );
3435  return netInfo;
3436  }
3437 
3438  return nullptr;
3439 }
3440 
3441 
3442 PCB_LAYER_ID CADSTAR_PCB_ARCHIVE_LOADER::getKiCadCopperLayerID( unsigned int aLayerNum, bool aDetectMaxLayer )
3443 {
3444  if( aDetectMaxLayer && aLayerNum == Assignments.Technology.MaxPhysicalLayer )
3445  return PCB_LAYER_ID::B_Cu;
3446 
3447  switch( aLayerNum )
3448  {
3449  case 1: return PCB_LAYER_ID::F_Cu;
3450  case 2: return PCB_LAYER_ID::In1_Cu;
3451  case 3: return PCB_LAYER_ID::In2_Cu;
3452  case 4: return PCB_LAYER_ID::In3_Cu;
3453  case 5: return PCB_LAYER_ID::In4_Cu;
3454  case 6: return PCB_LAYER_ID::In5_Cu;
3455  case 7: return PCB_LAYER_ID::In6_Cu;
3456  case 8: return PCB_LAYER_ID::In7_Cu;
3457  case 9: return PCB_LAYER_ID::In8_Cu;
3458  case 10: return PCB_LAYER_ID::In9_Cu;
3459  case 11: return PCB_LAYER_ID::In10_Cu;
3460  case 12: return PCB_LAYER_ID::In11_Cu;
3461  case 13: return PCB_LAYER_ID::In12_Cu;
3462  case 14: return PCB_LAYER_ID::In13_Cu;
3463  case 15: return PCB_LAYER_ID::In14_Cu;
3464  case 16: return PCB_LAYER_ID::In15_Cu;
3465  case 17: return PCB_LAYER_ID::In16_Cu;
3466  case 18: return PCB_LAYER_ID::In17_Cu;
3467  case 19: return PCB_LAYER_ID::In18_Cu;
3468  case 20: return PCB_LAYER_ID::In19_Cu;
3469  case 21: return PCB_LAYER_ID::In20_Cu;
3470  case 22: return PCB_LAYER_ID::In21_Cu;
3471  case 23: return PCB_LAYER_ID::In22_Cu;
3472  case 24: return PCB_LAYER_ID::In23_Cu;
3473  case 25: return PCB_LAYER_ID::In24_Cu;
3474  case 26: return PCB_LAYER_ID::In25_Cu;
3475  case 27: return PCB_LAYER_ID::In26_Cu;
3476  case 28: return PCB_LAYER_ID::In27_Cu;
3477  case 29: return PCB_LAYER_ID::In28_Cu;
3478  case 30: return PCB_LAYER_ID::In29_Cu;
3479  case 31: return PCB_LAYER_ID::In30_Cu;
3480  case 32: return PCB_LAYER_ID::B_Cu;
3481  }
3482 
3484 }
3485 
3486 bool CADSTAR_PCB_ARCHIVE_LOADER::isLayerSet( const LAYER_ID& aCadstarLayerID )
3487 {
3488  wxCHECK( Assignments.Layerdefs.Layers.find( aCadstarLayerID )
3489  != Assignments.Layerdefs.Layers.end(),
3490  false );
3491 
3492  LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID );
3493 
3494  switch( layer.Type )
3495  {
3496  case LAYER_TYPE::ALLDOC:
3497  case LAYER_TYPE::ALLELEC:
3498  case LAYER_TYPE::ALLLAYER:
3499  return true;
3500 
3501  default:
3502  return false;
3503  }
3504 
3505  return false;
3506 }
3507 
3508 
3510 {
3511  if( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) != Assignments.Layerdefs.Layers.end() )
3512  {
3513  if( Assignments.Layerdefs.Layers.at( aCadstarLayerID ).Type == LAYER_TYPE::NOLAYER )
3514  //The "no layer" is common for CADSTAR documentation symbols
3515  //map it to undefined layer for later processing
3517  }
3518 
3519  wxCHECK( m_layermap.find( aCadstarLayerID ) != m_layermap.end(), PCB_LAYER_ID::UNDEFINED_LAYER );
3520 
3521  return m_layermap.at( aCadstarLayerID );
3522 }
3523 
3524 
3526 {
3527  LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID );
3528 
3529  switch( layer.Type )
3530  {
3531  case LAYER_TYPE::ALLDOC:
3534 
3535  case LAYER_TYPE::ALLELEC:
3536  return LSET::AllCuMask();
3537 
3538  case LAYER_TYPE::ALLLAYER:
3539  return LSET::AllLayersMask();
3540 
3541  default:
3542  return LSET( getKiCadLayer( aCadstarLayerID ) );
3543  }
3544 }
3545 
3546 
3548  const GROUP_ID& aCadstarGroupID, BOARD_ITEM* aKiCadItem )
3549 {
3550  wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), );
3551 
3552  PCB_GROUP* parentGroup = m_groupMap.at( aCadstarGroupID );
3553  parentGroup->AddItem( aKiCadItem );
3554 }
3555 
3556 
3558  const wxString& aName )
3559 {
3560  wxString groupName = aName;
3561  int num = 0;
3562 
3563  while( m_groupMap.find( groupName ) != m_groupMap.end() )
3564  {
3565  groupName = aName + wxT( "_" ) + wxString::Format( "%i", ++num );
3566  }
3567 
3568  PCB_GROUP* docSymGroup = new PCB_GROUP( m_board );
3569  m_board->Add( docSymGroup );
3570  docSymGroup->SetName( groupName );
3571  GROUP_ID groupID( groupName );
3572  m_groupMap.insert( { groupID, docSymGroup } );
3573 
3574  return groupID;
3575 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:195
void SetReference(const wxString &aReference)
Definition: footprint.h:432
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:770
void SetOffset(const wxPoint &aOffset)
Definition: pad.h:248
Definition: track.h:343
virtual std::map< wxString, wxString > & GetTextVars() const
std::map< ROUTECODE_ID, ROUTECODE > RouteCodes
void SetHatchThickness(int aThickness)
Definition: zone.h:257
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:645
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:132
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 SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:765
std::pair< POINT, POINT > DesignArea
virtual void SetStart(const wxPoint &aPoint)
Definition: dimension.h:122
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:268
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:342
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.
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.
this class manage the layers needed to make a physical board they are solder mask,...
void SetPosition(const wxPoint &aPoint) override
Definition: track.h:412
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:151
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:194
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:82
long ScaleRatioNumerator
Documentation symbols can be arbitrarily scaled when added to a design.
void SetEnd(const wxPoint &aEnd)
Definition: track.h:112
void Scale(double aScale)
Definition: pcb_shape.cpp:147
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
Definition: zone.h:621
TEXT_FIELD_NAME
These are speccial fields in text objects enclosed between the tokens '<@' and '>' such as <@[FIELD_N...
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:67
void SetThermalSpokeWidth(int aWidth)
Set the width of the thermal spokes connecting the pad to a zone.
Definition: pad.h:482
std::map< COMP_AREA_ID, COMPONENT_AREA > ComponentAreas
void SetTextAngle(double aAngle) override
Definition: pcb_text.cpp:102
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
std::map< LINECODE_ID, LINECODE > LineCodes
void loadNetTracks(const NET_ID &aCadstarNetID, const NET_PCB::ROUTE &aCadstarRoute)
void SetFilled(bool aFlag)
Definition: pcb_shape.h:94
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:70
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:243
SHAPE_POLY_SET * Outline()
Definition: zone.h:323
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: dimension.cpp:212
#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 NoTracks
From CADSTAR Help: "Check this button to specify that any area created by the Rectangle,...
Filled closed shape (hatch fill).
void SetAnchorPadShape(PAD_SHAPE_T aShape)
Set the shape of the anchor pad for custom shaped pads.
Definition: pad.h:208
Smd pad, appears on the solder paste layer (default)
Definition: pad_shapes.h:81
virtual const wxPoint & GetStart() const
The dimension's origin is the first feature point for the dimension.
Definition: dimension.h:121
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:253
PCB_GROUP * getKiCadGroup(const GROUP_ID &aCadstarGroupID)
LAYERPAIR getLayerPair(const LAYERPAIR_ID &aCadstarLayerPairID)
polygon (not yet used for tracks, but could be in microwave apps)
Definition: board_item.h:54
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:132
void SetUnitsFormat(const DIM_UNITS_FORMAT aFormat)
Definition: dimension.h:167
std::map< FIGURE_ID, FIGURE > Figures
void SetPrefix(const wxString &aPrefix)
Definition: dimension.cpp:132
double GetOrientation() const
Definition: footprint.h:186
double GetTextAngle() const
Definition: eda_text.h:181
void SetCopperLayerCount(int aCount)
Definition: board.cpp:441
long ScaleRatioDenominator
Documentation symbols can be arbitrarily scaled when added to a design.
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
usual segment : line with rounded ends
Definition: board_item.h:50
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.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.h:591
Definition: eserie.h:39
Arcs (with rounded ends)
Definition: board_item.h:52
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...
const SHAPE_POLY_SET & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Function GetFilledPolysList returns a reference to the list of filled polygons.
Definition: zone.h:655
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 Returns a bit-mask of all t...
Definition: board.cpp:447
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:257
void SetSize(const wxSize &aSize)
Definition: pad.h:231
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:390
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].
void Inflate(int aAmount, int aCircleSegmentsCount, CORNER_STRATEGY aCornerStrategy=ROUND_ALL_CORNERS)
Perform outline inflation/deflation.
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition: pad_shapes.h:85
void loadNetVia(const NET_ID &aCadstarNetID, const NET_PCB::VIA &aCadstarVia)
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:197
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)
Function SetPriority.
Definition: zone.h:118
std::map< LAYERPAIR_ID, LAYERPAIR > LayerPairs
Default vias to use between pairs of layers.
Describes an imported layer and how it could be mapped to KiCad Layers.
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition: zone.h:185
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:164
UNITS Units
Units to display for linear dimensions.
void SetWidth(int aWidth)
Definition: track.h:109
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:459
void SetDescription(const wxString &aDoc)
Definition: footprint.h:194
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:762
FP_TEXT & Reference()
Definition: footprint.h:460
std::set< TEXT_FIELD_NAME > InconsistentTextFields
Text fields need to be updated in CADSTAR and it is possible that they are not consistent accross tex...
void SetExtensionHeight(int aHeight)
Definition: dimension.h:380
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...
void applyDimensionSettings(const DIMENSION &aCadstarDim, DIMENSION_BASE *aKiCadDim)
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.
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:112
std::map< GROUP_ID, PCB_GROUP * > m_groupMap
Map between Cadstar and KiCad groups.
void SetText(const wxString &aNewText)
Sets the override text - has no effect if m_overrideValue == false.
Definition: dimension.cpp:199
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:563
void SetLayerName(const wxString &aName)
void SetClosed(bool aClosed)
Function SetClosed()
long UnitDisplPrecision
Number of decimal points to display for linear dimensions.
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.
void SetHeight(int aHeight)
Sets the distance from the feature points to the crossbar line.
Definition: dimension.h:371
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:121
PART getPart(const PART_ID &aCadstarPartID)
Normal via.
Definition: router_tool.cpp:67
void SetHatchGap(int aStep)
Definition: zone.h:260
void SetPos0(const wxPoint &aPos)
Definition: pad.h:225
void SetShape(PCB_SHAPE_TYPE_T aShape)
Definition: pcb_shape.h:129
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.
A leader is a dimension-like object pointing to a specific point.
Definition: dimension.h:477
void SetThermalGap(int aGap)
Definition: pad.h:490
std::map< PAD_ID, COMPONENT_PAD > ComponentPads
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:766
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:473
SHAPE_LINE_CHAIN & Outline(int aIndex)
bool Placement
From CADSTAR Help: "Auto Placement can place components within this area.
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].
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:1549
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.
void SetDrillSize(const wxSize &aSize)
Definition: pad.h:241
The highest PHYSICAL_LAYER_ID currently defined (i.e.
PCB_TEXT & Text()
Definition: dimension.h:209
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 Flip(const wxPoint &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1366
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:358
bool ThermalReliefOnPads
false when subnode "NOPINRELIEF" is present
std::map< SYMDEF_ID, SYMDEF_PCB > ComponentDefinitions
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
Definition: dimension.h:414
const wxString & GetName() const
Definition: pad.h:133
std::vector< COMPONENT_COPPER > ComponentCoppers
static LSET PTHMask()
layer set for a through hole pad
Definition: pcbnew/pad.cpp:147
virtual void SetEnd(const wxPoint &aPoint)
Definition: dimension.h:125
const wxString & GetReference() const
Definition: footprint.h:423
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:764
BOARD_ITEM * Duplicate() const override
Create a copy of a of this BOARD_ITEM.
Definition: footprint.cpp:1593
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 SetLayerPair(PCB_LAYER_ID aTopLayer, PCB_LAYER_ID aBottomLayer)
Function SetLayerPair For a via m_layer contains the top layer, the other layer is in m_bottomLayer.
Definition: track.cpp:414
void SetMaterial(const wxString &aName, int aDielectricSubLayer=0)
ZONE handles a list of polygons defining a copper zone.
Definition: zone.h:57
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pad.h:360
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 SetExtensionOffset(int aOffset)
Definition: dimension.h:184
void SetMinThickness(int aMinThickness)
Definition: zone.h:248
void SetCenter(const wxPoint &aCenterPoint)
For arcs and circles:
Definition: pcb_shape.h:229
void SetIsFilled(bool isFilled)
Definition: zone.h:238
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.
void SetValue(const wxString &aValue)
Definition: footprint.h:453
wxString Identifier
This is an identifier that is displayed to the user.
All physical layers currently defined.
bool AutomaticRepour
true when subnode "REGENERATE" is present
double Angle() const
Compute the angle of the vector.
Definition: vector2d.h:307
#define KEY_PREPREG
void SetShape(PAD_SHAPE_T aShape)
Set the new shape of this pad.
Definition: pad.h:160
void SetSuffix(const wxString &aSuffix)
Definition: dimension.cpp:138
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union between a and b, store the result in it self For aFastMode meaning,...
wxString getAttributeName(const ATTRIBUTE_ID &aCadstarAttributeID)
this class manage one layer needed to make a physical board it can be a solder mask,...
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
void SetRoundRectRadiusRatio(double aRadiusScale)
Has meaning only for rounded rectangle pads.
Definition: pcbnew/pad.cpp:252
POINT Origin
Origin of the component (this is used as the reference point when placing the component in the design...
GROUP_ID GroupID
If not empty, this CADSTAR_BOARD is part of a group.
std::map< REUSEBLOCK_ID, REUSEBLOCK > ReuseBlocks
void SetOrientation(DIR aOrientation)
Sets the orientation of the dimension line (so, perpendicular to the feature lines)
Definition: dimension.h:447
static const long UNDEFINED_VALUE
Corresponds to CADSTAR "origin".
virtual const VECTOR2I GetPoint(int aIndex) const override
void Fracture(POLYGON_MODE aFastMode)
Convert a single outline slitted ("fractured") polygon into a set ouf outlines with holes.
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Return the area of this poly set.
LSEQ is a sequence (and therefore also a set) of PCB_LAYER_IDs.
< Nodename = "VARIANT" or "VMASTER" (master variant
void SetEpsilonR(double aEpsilon, int aDielectricSubLayer=0)
void SetDrillShape(PAD_DRILL_SHAPE_T aShape)
Definition: pad.h:345
void SetPos0(const wxPoint &aPos)
Definition: fp_text.h:165
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Function SetFilledPolysList sets the list of filled polygons.
Definition: zone.h:670
void SetThickness(int aThickness, int aDielectricSubLayer=0)
void SetRoundRectCornerRadius(double aRadius)
Has meaning only for rounded rectangle pads.
Definition: pcbnew/pad.cpp:243
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
std::map< SPACINGCODE_ID, SPACINGCODE > SpacingCodes
Spacing Design Rules.
void SetChamferPositions(int aPositions)
Has meaning only for chamfered rectangular pads.
Definition: pad.h:533
PADCODE getPadCode(const PADCODE_ID &aCadstarPadCodeID)
No updates are required.
Definition: view_item.h:51
std::map< PAD_ID, PADEXCEPTION > PadExceptions
Override pad definitions for this instance.
TEXTCODE getTextCode(const TEXTCODE_ID &aCadstarTextCodeID)
Use thermal relief for pads.
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:191
void SetKeepUpright(bool aKeepUpright)
Definition: fp_text.h:115
bool Keepout
From CADSTAR Help: "Auto Placement cannot place components within this area.
int getLineThickness(const LINECODE_ID &aCadstarLineCodeID)
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, PCB_LAYER_ID aLayer, int aClearanceValue, int aMaxError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
void Move(const wxPoint &aMoveVector) override
Move this object.
Definition: pcb_text.h:82
Handle the data for a net.
Definition: netinfo.h:64
std::vector< CUTOUT > Cutouts
Not Applicable to OPENSHAPE Type.
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:122
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:245
std::map< TEXTCODE_ID, TEXTCODE > TextCodes
void SetLayerSet(LSET aLayers) override
Definition: pad.h:359
TEMPLATE_ID PouredTemplateID
If not empty, it means this COPPER is part of a poured template.
void SetMinIslandArea(long long int aArea)
Definition: zone.h:774
PCB_SHAPE * getDrawSegmentFromVertex(const POINT &aCadstarStartPoint, const VERTEX &aCadstarVertex, BOARD_ITEM_CONTAINER *aContainer=nullptr, 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)
Returns a pointer to a PCB_SHAPE object.
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
void RemoveAll()
Delete all items in list and clear the list.
Inbuilt layer type (cannot be assigned to user layers)
std::map< DIMENSION_ID, DIMENSION > Dimensions
bool NoTracks
From CADSTAR Help: "Area cannot be used to place routes during automatic routing.
double DEG2RAD(double deg)
Definition: trigo.h:231
void SetStart(const wxPoint &aStart)
Definition: pcb_shape.h:148
std::map< NET_ID, NETINFO_ITEM * > m_netMap
Map between Cadstar and KiCad Nets.
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
wxString Name
This is undefined (wxEmptyString) if the net is unnamed.
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:208
bool m_logLayerWarnings
Used in loadBoardStackup()
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:763
wxString Name
Imported layer name as displayed in original application.
#define _(s)
Definition: 3d_actions.cpp:33
GROUP_ID createUniqueGroupID(const wxString &aName)
Adds a new PCB_GROUP* to m_groupMap.
SHAPE_LINE_CHAIN.
void SetPrecision(int aPrecision)
Definition: dimension.h:170
bool m_doneCopperWarning
Used by loadCoppers() to avoid multiple duplicate warnings.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
T NormalizeAnglePos(T Angle)
Normalize angle to be in the 0.0 .
Definition: trigo.h:279
PCB_LAYER_ID getKiCadLayer(const LAYER_ID &aCadstarLayerID)
std::map< HATCHCODE_ID, HATCHCODE > HatchCodes
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void SetPosition(const wxPoint &aPos) override
Definition: pad.h:171
void addToGroup(const GROUP_ID &aCadstarGroupID, BOARD_ITEM *aKiCadItem)
long SliverWidth
Minimum width of copper that may be created.
void drawCadstarCutoutsAsSegments(const std::vector< CUTOUT > &aCutouts, 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 cutouts on m_board object.
double GetAngle() const
Definition: pcb_shape.h:127
void loadLibraryFigures(const SYMDEF_PCB &aComponent, FOOTPRINT *aFootprint)
Plated through hole pad.
Definition: pad_shapes.h:80
void SetDrill(int aDrill)
Function SetDrill sets the drill value for vias.
Definition: track.h:474
NETCLASS * GetDefault() const
const wxPoint & GetEnd0() const
Definition: fp_shape.h:115
void AddPrimitive(PCB_SHAPE *aPrimitive)
Add item to the custom shape primitives list.
void SetWidth(int aWidth)
Definition: pcb_shape.h:117