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