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-2023 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 wxString( 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( VECTOR2I( { 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->SetTextPos( getKiCadPoint( txtPoint ) );
1559 leaderDim->SetOverrideText( 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 applyTextCode( txt, aCadstarText.TextCodeID );
2549
2550 switch( aCadstarText.Alignment )
2551 {
2552 case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
2556 break;
2557
2561 break;
2562
2566 break;
2567
2571 break;
2572
2576 break;
2577
2581 break;
2582
2583 case ALIGNMENT::TOPLEFT:
2586 break;
2587
2591 break;
2592
2596 break;
2597
2598 default:
2599 wxFAIL_MSG( wxT( "Unknown Alignment - needs review!" ) );
2600 }
2601
2602 if( aMirrorInvert )
2603 {
2604 txt->Flip( aTransformCentre, true );
2605 }
2606
2607 //scale it after flipping:
2608 if( aScalingFactor != 1.0 )
2609 {
2610 VECTOR2I unscaledTextSize = txt->GetTextSize();
2611 int unscaledThickness = txt->GetTextThickness();
2612
2613 VECTOR2I scaledTextSize;
2614 scaledTextSize.x = KiROUND( (double) unscaledTextSize.x * aScalingFactor );
2615 scaledTextSize.y = KiROUND( (double) unscaledTextSize.y * aScalingFactor );
2616
2617 txt->SetTextSize( scaledTextSize );
2618 txt->SetTextThickness( KiROUND( (double) unscaledThickness * aScalingFactor ) );
2619 }
2620
2621 txt->Move( aMoveVector );
2622
2623 if( aCadstarText.Alignment == ALIGNMENT::NO_ALIGNMENT )
2625
2626 LAYER_ID layersToDrawOn = aCadstarLayerOverride;
2627
2628 if( layersToDrawOn.IsEmpty() )
2629 layersToDrawOn = aCadstarText.LayerID;
2630
2631 if( isLayerSet( layersToDrawOn ) )
2632 {
2633 //Make a copy on each layer
2634
2635 LSEQ layers = getKiCadLayerSet( layersToDrawOn ).Seq();
2636 PCB_TEXT* newtxt;
2637
2638 for( PCB_LAYER_ID layer : layers )
2639 {
2640 txt->SetLayer( layer );
2641 newtxt = static_cast<PCB_TEXT*>( txt->Duplicate() );
2642 m_board->Add( newtxt, ADD_MODE::APPEND );
2643
2644 if( !aCadstarGroupID.IsEmpty() )
2645 addToGroup( aCadstarGroupID, newtxt );
2646 }
2647
2648 m_board->Remove( txt );
2649 delete txt;
2650 }
2651 else
2652 {
2653 txt->SetLayer( getKiCadLayer( layersToDrawOn ) );
2654
2655 if( !aCadstarGroupID.IsEmpty() )
2656 addToGroup( aCadstarGroupID, txt );
2657 }
2658 //TODO Handle different font types when KiCad can support it.
2659}
2660
2661
2663 const PCB_LAYER_ID& aKiCadLayer,
2664 const int& aLineThickness,
2665 const wxString& aShapeName,
2666 BOARD_ITEM_CONTAINER* aContainer,
2667 const GROUP_ID& aCadstarGroupID,
2668 const VECTOR2I& aMoveVector,
2669 const double& aRotationAngle,
2670 const double& aScalingFactor,
2671 const VECTOR2I& aTransformCentre,
2672 const bool& aMirrorInvert )
2673{
2674 auto drawAsOutline = [&]()
2675 {
2676 drawCadstarVerticesAsShapes( aCadstarShape.Vertices, aKiCadLayer, aLineThickness,
2677 aContainer, aCadstarGroupID, aMoveVector, aRotationAngle,
2678 aScalingFactor, aTransformCentre, aMirrorInvert );
2679 drawCadstarCutoutsAsShapes( aCadstarShape.Cutouts, aKiCadLayer, aLineThickness, aContainer,
2680 aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2681 aTransformCentre, aMirrorInvert );
2682 };
2683
2684 switch( aCadstarShape.Type )
2685 {
2689 drawAsOutline();
2690 break;
2691
2694 wxLogWarning( wxString::Format(
2695 _( "The shape for '%s' is Hatch filled in CADSTAR, which has no KiCad equivalent. "
2696 "Using solid fill instead." ),
2697 aShapeName ) );
2698
2699 case SHAPE_TYPE::SOLID:
2700 {
2701 // Special case solid shapes that are effectively a single line
2702 if( aCadstarShape.Vertices.size() < 3
2703 || ( aCadstarShape.Vertices.size() == 3
2704 && aCadstarShape.Vertices.at( 0 ).End == aCadstarShape.Vertices.at( 2 ).End ) )
2705 {
2706 drawAsOutline();
2707 break;
2708 }
2709
2710 PCB_SHAPE* shape;
2711
2712 if( isFootprint( aContainer ) )
2713 shape = new FP_SHAPE( (FOOTPRINT*) aContainer, SHAPE_T::POLY );
2714 else
2715 shape = new PCB_SHAPE( aContainer, SHAPE_T::POLY );
2716
2717 shape->SetFilled( true );
2718
2719 SHAPE_POLY_SET shapePolys = getPolySetFromCadstarShape( aCadstarShape, -1, aContainer,
2720 aMoveVector, aRotationAngle,
2721 aScalingFactor, aTransformCentre,
2722 aMirrorInvert );
2723
2724 shapePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
2725
2726 shape->SetPolyShape( shapePolys );
2727 shape->SetStroke( STROKE_PARAMS( aLineThickness, PLOT_DASH_TYPE::SOLID ) );
2728 shape->SetLayer( aKiCadLayer );
2729 aContainer->Add( shape, ADD_MODE::APPEND );
2730
2731 if( !aCadstarGroupID.IsEmpty() )
2732 addToGroup( aCadstarGroupID, shape );
2733 }
2734 break;
2735 }
2736}
2737
2738
2739void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarCutoutsAsShapes( const std::vector<CUTOUT>& aCutouts,
2740 const PCB_LAYER_ID& aKiCadLayer,
2741 const int& aLineThickness,
2742 BOARD_ITEM_CONTAINER* aContainer,
2743 const GROUP_ID& aCadstarGroupID,
2744 const VECTOR2I& aMoveVector,
2745 const double& aRotationAngle,
2746 const double& aScalingFactor,
2747 const VECTOR2I& aTransformCentre,
2748 const bool& aMirrorInvert )
2749{
2750 for( CUTOUT cutout : aCutouts )
2751 {
2752 drawCadstarVerticesAsShapes( cutout.Vertices, aKiCadLayer, aLineThickness, aContainer,
2753 aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2754 aTransformCentre, aMirrorInvert );
2755 }
2756}
2757
2758
2759void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarVerticesAsShapes( const std::vector<VERTEX>& aCadstarVertices,
2760 const PCB_LAYER_ID& aKiCadLayer,
2761 const int& aLineThickness,
2762 BOARD_ITEM_CONTAINER* aContainer,
2763 const GROUP_ID& aCadstarGroupID,
2764 const VECTOR2I& aMoveVector,
2765 const double& aRotationAngle,
2766 const double& aScalingFactor,
2767 const VECTOR2I& aTransformCentre,
2768 const bool& aMirrorInvert )
2769{
2770 std::vector<PCB_SHAPE*> shapes = getShapesFromVertices( aCadstarVertices, aContainer,
2771 aCadstarGroupID, aMoveVector,
2772 aRotationAngle, aScalingFactor,
2773 aTransformCentre, aMirrorInvert );
2774
2775 for( PCB_SHAPE* shape : shapes )
2776 {
2777 shape->SetStroke( STROKE_PARAMS( aLineThickness, PLOT_DASH_TYPE::SOLID ) );
2778 shape->SetLayer( aKiCadLayer );
2779 shape->SetParent( aContainer );
2780 aContainer->Add( shape, ADD_MODE::APPEND );
2781 }
2782}
2783
2784
2786 const std::vector<VERTEX>& aCadstarVertices,
2787 BOARD_ITEM_CONTAINER* aContainer,
2788 const GROUP_ID& aCadstarGroupID,
2789 const VECTOR2I& aMoveVector,
2790 const double& aRotationAngle,
2791 const double& aScalingFactor,
2792 const VECTOR2I& aTransformCentre,
2793 const bool& aMirrorInvert )
2794{
2795 std::vector<PCB_SHAPE*> drawSegments;
2796
2797 if( aCadstarVertices.size() < 2 )
2798 //need at least two points to draw a segment! (unlikely but possible to have only one)
2799 return drawSegments;
2800
2801 const VERTEX* prev = &aCadstarVertices.at( 0 ); // first one should always be a point vertex
2802 const VERTEX* cur;
2803
2804 for( size_t i = 1; i < aCadstarVertices.size(); i++ )
2805 {
2806 cur = &aCadstarVertices.at( i );
2807 drawSegments.push_back( getShapeFromVertex( prev->End, *cur, aContainer, aCadstarGroupID,
2808 aMoveVector, aRotationAngle, aScalingFactor,
2809 aTransformCentre, aMirrorInvert ) );
2810 prev = cur;
2811 }
2812
2813 return drawSegments;
2814}
2815
2816
2818 const VERTEX& aCadstarVertex,
2819 BOARD_ITEM_CONTAINER* aContainer,
2820 const GROUP_ID& aCadstarGroupID,
2821 const VECTOR2I& aMoveVector,
2822 const double& aRotationAngle,
2823 const double& aScalingFactor,
2824 const VECTOR2I& aTransformCentre,
2825 const bool& aMirrorInvert )
2826{
2827 PCB_SHAPE* shape = nullptr;
2828 bool cw = false;
2829
2830 VECTOR2I startPoint = getKiCadPoint( aCadstarStartPoint );
2831 VECTOR2I endPoint = getKiCadPoint( aCadstarVertex.End );
2832 VECTOR2I centerPoint;
2833
2834 if( aCadstarVertex.Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE
2835 || aCadstarVertex.Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
2836 {
2837 centerPoint = ( startPoint + endPoint ) / 2;
2838 }
2839 else
2840 {
2841 centerPoint = getKiCadPoint( aCadstarVertex.Center );
2842 }
2843
2844 switch( aCadstarVertex.Type )
2845 {
2846
2847 case VERTEX_TYPE::POINT:
2848
2849 if( isFootprint( aContainer ) )
2850 shape = new FP_SHAPE( static_cast<FOOTPRINT*>( aContainer ), SHAPE_T::SEGMENT );
2851 else
2852 shape = new PCB_SHAPE( aContainer, SHAPE_T::SEGMENT );
2853
2854 shape->SetStart( startPoint );
2855 shape->SetEnd( endPoint );
2856 break;
2857
2860 cw = true;
2862
2865 {
2866 if( isFootprint( aContainer ) )
2867 shape = new FP_SHAPE((FOOTPRINT*) aContainer, SHAPE_T::ARC );
2868 else
2869 shape = new PCB_SHAPE( aContainer, SHAPE_T::ARC );
2870
2871 shape->SetCenter( centerPoint );
2872 shape->SetStart( startPoint );
2873
2874 EDA_ANGLE arcStartAngle( startPoint - centerPoint );
2875 EDA_ANGLE arcEndAngle( endPoint - centerPoint );
2876 EDA_ANGLE arcAngle = ( arcEndAngle - arcStartAngle ).Normalize();
2877 //TODO: detect if we are supposed to draw a circle instead (i.e. two SEMICIRCLEs
2878 // with opposite start/end points and same centre point)
2879
2880 if( !cw )
2881 arcAngle.NormalizeNegative(); // anticlockwise arc
2882
2883 shape->SetArcAngleAndEnd( arcAngle, true );
2884
2885 break;
2886 }
2887 }
2888
2889 //Apply transforms
2890 if( aMirrorInvert )
2891 shape->Flip( aTransformCentre, true );
2892
2893 if( aScalingFactor != 1.0 )
2894 {
2895 shape->Move( -1*aTransformCentre );
2896 shape->Scale( aScalingFactor );
2897 shape->Move( aTransformCentre );
2898 }
2899
2900 if( aRotationAngle != 0.0 )
2901 shape->Rotate( aTransformCentre, EDA_ANGLE( aRotationAngle, TENTHS_OF_A_DEGREE_T ) );
2902
2903 if( aMoveVector != VECTOR2I{ 0, 0 } )
2904 shape->Move( aMoveVector );
2905
2906 if( isFootprint( aContainer ) && shape != nullptr )
2907 static_cast<FP_SHAPE*>( shape )->SetLocalCoord();
2908
2909 if( !aCadstarGroupID.IsEmpty() )
2910 addToGroup( aCadstarGroupID, shape );
2911
2912 return shape;
2913}
2914
2915
2917 const int& aLineThickness,
2918 BOARD_ITEM_CONTAINER* aParentContainer )
2919{
2920 ZONE* zone = new ZONE( aParentContainer, isFootprint( aParentContainer ) );
2921
2922 if( aCadstarShape.Type == SHAPE_TYPE::HATCHED )
2923 {
2926 }
2927 else
2928 {
2930 }
2931
2932 SHAPE_POLY_SET polygon = getPolySetFromCadstarShape( aCadstarShape, aLineThickness );
2933
2934 zone->AddPolygon( polygon.COutline( 0 ) );
2935
2936 for( int i = 0; i < polygon.HoleCount( 0 ); i++ )
2937 zone->AddPolygon( polygon.CHole( 0, i ) );
2938
2939 return zone;
2940}
2941
2942
2944 const int& aLineThickness,
2945 BOARD_ITEM_CONTAINER* aContainer,
2946 const VECTOR2I& aMoveVector,
2947 const double& aRotationAngle,
2948 const double& aScalingFactor,
2949 const VECTOR2I& aTransformCentre,
2950 const bool& aMirrorInvert )
2951{
2952 GROUP_ID noGroup = wxEmptyString;
2953
2954 std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( aCadstarShape.Vertices,
2955 aContainer, noGroup, aMoveVector,
2956 aRotationAngle, aScalingFactor,
2957 aTransformCentre, aMirrorInvert );
2958
2959 SHAPE_POLY_SET polySet( getLineChainFromShapes( outlineShapes ) );
2960
2961 //cleanup
2962 for( PCB_SHAPE* shape : outlineShapes )
2963 delete shape;
2964
2965 for( CUTOUT cutout : aCadstarShape.Cutouts )
2966 {
2967 std::vector<PCB_SHAPE*> cutoutShapes = getShapesFromVertices( cutout.Vertices, aContainer,
2968 noGroup, aMoveVector,
2969 aRotationAngle, aScalingFactor,
2970 aTransformCentre, aMirrorInvert );
2971
2972 polySet.AddHole( getLineChainFromShapes( cutoutShapes ) );
2973
2974 //cleanup
2975 for( PCB_SHAPE* shape : cutoutShapes )
2976 delete shape;
2977 }
2978
2979 polySet.ClearArcs();
2980
2981 if( aLineThickness > 0 )
2982 {
2983 polySet.Inflate( aLineThickness / 2, 32,
2984 SHAPE_POLY_SET::CORNER_STRATEGY::ROUND_ALL_CORNERS );
2985 }
2986
2987#ifdef DEBUG
2988 for( int i = 0; i < polySet.OutlineCount(); ++i )
2989 {
2990 wxASSERT( polySet.Outline( i ).PointCount() > 2 );
2991
2992 for( int j = 0; j < polySet.HoleCount( i ); ++j )
2993 {
2994 wxASSERT( polySet.Hole( i, j ).PointCount() > 2 );
2995 }
2996 }
2997#endif
2998
2999 return polySet;
3000}
3001
3002
3004{
3005 SHAPE_LINE_CHAIN lineChain;
3006
3007 for( PCB_SHAPE* shape : aShapes )
3008 {
3009 switch( shape->GetShape() )
3010 {
3011 case SHAPE_T::ARC:
3012 {
3013 if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3014 {
3015 FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3016 SHAPE_ARC arc( fp_shape->GetCenter0(), fp_shape->GetStart0(), fp_shape->GetArcAngle() );
3017
3018 if( shape->EndsSwapped() )
3019 arc.Reverse();
3020
3021 lineChain.Append( arc );
3022 }
3023 else
3024 {
3025 SHAPE_ARC arc( shape->GetCenter(), shape->GetStart(), shape->GetArcAngle() );
3026
3027 if( shape->EndsSwapped() )
3028 arc.Reverse();
3029
3030 lineChain.Append( arc );
3031 }
3032 }
3033 break;
3034 case SHAPE_T::SEGMENT:
3035 if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3036 {
3037 FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3038 lineChain.Append( fp_shape->GetStart0().x, fp_shape->GetStart0().y );
3039 lineChain.Append( fp_shape->GetEnd0().x, fp_shape->GetEnd0().y );
3040 }
3041 else
3042 {
3043 lineChain.Append( shape->GetStartX(), shape->GetStartY() );
3044 lineChain.Append( shape->GetEndX(), shape->GetEndY() );
3045 }
3046 break;
3047
3048 default:
3049 wxFAIL_MSG( wxT( "Drawsegment type is unexpected. Ignored." ) );
3050 }
3051 }
3052
3053 // Shouldn't have less than 3 points to make a closed shape!
3054 wxASSERT( lineChain.PointCount() > 2 );
3055
3056 // Check if it is closed
3057 if( lineChain.GetPoint( 0 ) != lineChain.GetPoint( lineChain.PointCount() - 1 ) )
3058 {
3059 lineChain.Append( lineChain.GetPoint( 0 ) );
3060 }
3061
3062 lineChain.SetClosed( true );
3063
3064 return lineChain;
3065}
3066
3067
3069 const std::vector<PCB_SHAPE*> aShapes,
3070 BOARD_ITEM_CONTAINER* aParentContainer,
3071 NETINFO_ITEM* aNet, PCB_LAYER_ID aLayerOverride,
3072 int aWidthOverride )
3073{
3074 std::vector<PCB_TRACK*> tracks;
3075 PCB_TRACK* prevTrack = nullptr;
3076 PCB_TRACK* track = nullptr;
3077
3078 auto addTrack =
3079 [&]( PCB_TRACK* aTrack )
3080 {
3081 // Ignore zero length tracks in the same way as the CADSTAR postprocessor does
3082 // when generating gerbers. Note that CADSTAR reports these as "Route offset
3083 // errors" when running a DRC within CADSTAR, so we shouldn't be getting this in
3084 // general, however it is used to remove any synthetic points added to
3085 // aDrawSegments by the caller of this function.
3086 if( aTrack->GetLength() != 0 )
3087 {
3088 tracks.push_back( aTrack );
3089 aParentContainer->Add( aTrack, ADD_MODE::APPEND );
3090 }
3091 else
3092 {
3093 delete aTrack;
3094 }
3095 };
3096
3097 for( PCB_SHAPE* shape : aShapes )
3098 {
3099 switch( shape->GetShape() )
3100 {
3101 case SHAPE_T::ARC:
3102 if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3103 {
3104 FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3105 SHAPE_ARC arc( fp_shape->GetStart0(), fp_shape->GetArcMid0(), fp_shape->GetEnd0(), 0 );
3106
3107 if( fp_shape->EndsSwapped() )
3108 arc.Reverse();
3109
3110 track = new PCB_ARC( aParentContainer, &arc );
3111 }
3112 else
3113 {
3114 SHAPE_ARC arc( shape->GetStart(), shape->GetArcMid(), shape->GetEnd(), 0 );
3115
3116 if( shape->EndsSwapped() )
3117 arc.Reverse();
3118
3119 track = new PCB_ARC( aParentContainer, &arc );
3120 }
3121 break;
3122 case SHAPE_T::SEGMENT:
3123 if( shape->GetClass() == wxT( "MGRAPHIC" ) )
3124 {
3125 FP_SHAPE* fp_shape = (FP_SHAPE*) shape;
3126 track = new PCB_TRACK( aParentContainer );
3127 track->SetStart( fp_shape->GetStart0() );
3128 track->SetEnd( fp_shape->GetEnd0() );
3129 }
3130 else
3131 {
3132 track = new PCB_TRACK( aParentContainer );
3133 track->SetStart( shape->GetStart() );
3134 track->SetEnd( shape->GetEnd() );
3135 }
3136 break;
3137
3138 default:
3139 wxFAIL_MSG( wxT( "Drawsegment type is unexpected. Ignored." ) );
3140 continue;
3141 }
3142
3143 if( aWidthOverride == -1 )
3144 track->SetWidth( shape->GetWidth() );
3145 else
3146 track->SetWidth( aWidthOverride );
3147
3148 if( aLayerOverride == PCB_LAYER_ID::UNDEFINED_LAYER )
3149 track->SetLayer( shape->GetLayer() );
3150 else
3151 track->SetLayer( aLayerOverride );
3152
3153 if( aNet != nullptr )
3154 track->SetNet( aNet );
3155 else
3156 track->SetNetCode( -1 );
3157
3158 track->SetLocked( shape->IsLocked() );
3159
3160 // Apply route offsetting, mimmicking the behaviour of the CADSTAR post processor
3161 if( prevTrack != nullptr )
3162 {
3163 int offsetAmount = ( track->GetWidth() / 2 ) - ( prevTrack->GetWidth() / 2 );
3164
3165 if( offsetAmount > 0 )
3166 {
3167 // modify the start of the current track
3168 VECTOR2I newStart = track->GetStart();
3169 applyRouteOffset( &newStart, track->GetEnd(), offsetAmount );
3170 track->SetStart( newStart );
3171 }
3172 else if( offsetAmount < 0 )
3173 {
3174 // amend the end of the previous track
3175 VECTOR2I newEnd = prevTrack->GetEnd();
3176 applyRouteOffset( &newEnd, prevTrack->GetStart(), -offsetAmount );
3177 prevTrack->SetEnd( newEnd );
3178 } // don't do anything if offsetAmount == 0
3179
3180 // Add a synthetic track of the thinnest width between the tracks
3181 // to ensure KiCad features works as expected on the imported design
3182 // (KiCad expects tracks are contiguous segments)
3183 if( track->GetStart() != prevTrack->GetEnd() )
3184 {
3185 int minWidth = std::min( track->GetWidth(), prevTrack->GetWidth() );
3186 PCB_TRACK* synthTrack = new PCB_TRACK( aParentContainer );
3187 synthTrack->SetStart( prevTrack->GetEnd() );
3188 synthTrack->SetEnd( track->GetStart() );
3189 synthTrack->SetWidth( minWidth );
3190 synthTrack->SetLocked( track->IsLocked() );
3191 synthTrack->SetNet( track->GetNet() );
3192 synthTrack->SetLayer( track->GetLayer() );
3193 addTrack( synthTrack );
3194 }
3195 }
3196
3197 if( prevTrack )
3198 addTrack( prevTrack );
3199
3200 prevTrack = track;
3201 }
3202
3203 if( track )
3204 addTrack( track );
3205
3206 return tracks;
3207}
3208
3209
3211 const ATTRIBUTE_ID& aCadstarAttributeID,
3212 FOOTPRINT* aFootprint,
3213 const wxString& aAttributeValue )
3214{
3215 FP_TEXT* txt;
3216
3217 if( aCadstarAttributeID == COMPONENT_NAME_ATTRID )
3218 {
3219 txt = &aFootprint->Reference(); //text should be set outside this function
3220 }
3221 else if( aCadstarAttributeID == PART_NAME_ATTRID )
3222 {
3223 if( aFootprint->Value().GetText().IsEmpty() )
3224 {
3225 // Use PART_NAME_ATTRID as the value is value field is blank
3226 aFootprint->SetValue( aAttributeValue );
3227 txt = &aFootprint->Value();
3228 }
3229 else
3230 {
3231 txt = new FP_TEXT( aFootprint );
3232 aFootprint->Add( txt );
3233 txt->SetText( aAttributeValue );
3234 }
3235 txt->SetVisible( false ); //make invisible to avoid clutter.
3236 }
3237 else if( aCadstarAttributeID != COMPONENT_NAME_2_ATTRID
3238 && getAttributeName( aCadstarAttributeID ) == wxT( "Value" ) )
3239 {
3240 if( !aFootprint->Value().GetText().IsEmpty() )
3241 {
3242 //copy the object
3243 aFootprint->Add( aFootprint->Value().Duplicate() );
3244 }
3245
3246 aFootprint->SetValue( aAttributeValue );
3247 txt = &aFootprint->Value();
3248 txt->SetVisible( false ); //make invisible to avoid clutter.
3249 }
3250 else
3251 {
3252 txt = new FP_TEXT( aFootprint );
3253 aFootprint->Add( txt );
3254 txt->SetText( aAttributeValue );
3255 txt->SetVisible( false ); //make all user attributes invisible to avoid clutter.
3256 //TODO: Future improvement - allow user to decide what to do with attributes
3257 }
3258
3259 VECTOR2I rotatedTextPos = getKiCadPoint( aCadstarAttrLoc.Position ) - aFootprint->GetPosition();
3260 RotatePoint( rotatedTextPos, -aFootprint->GetOrientation() );
3261
3262 txt->SetTextPos( getKiCadPoint( aCadstarAttrLoc.Position ) );
3263 txt->SetPos0( rotatedTextPos );
3264 txt->SetLayer( getKiCadLayer( aCadstarAttrLoc.LayerID ) );
3265 txt->SetMirrored( aCadstarAttrLoc.Mirror );
3266 txt->SetTextAngle( getAngle( aCadstarAttrLoc.OrientAngle ) - aFootprint->GetOrientation() );
3267
3268 if( aCadstarAttrLoc.Mirror ) // If mirroring, invert angle to match CADSTAR
3269 txt->SetTextAngle( -txt->GetTextAngle() );
3270
3271 applyTextCode( txt, aCadstarAttrLoc.TextCodeID );
3272
3273 txt->SetKeepUpright( false ); //Keeping it upright seems to result in incorrect orientation
3274
3275 switch( aCadstarAttrLoc.Alignment )
3276 {
3277 case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
3283 break;
3284
3288 break;
3289
3293 break;
3294
3298 break;
3299
3303 break;
3304
3308 break;
3309
3310 case ALIGNMENT::TOPLEFT:
3313 break;
3314
3318 break;
3319
3323 break;
3324
3325 default:
3326 wxFAIL_MSG( wxT( "Unknown Alignment - needs review!" ) );
3327 }
3328
3329 //TODO Handle different font types when KiCad can support it.
3330}
3331
3332
3334 const VECTOR2I& aRefPoint,
3335 const long& aOffsetAmount )
3336{
3337 VECTOR2I v( *aPointToOffset - aRefPoint );
3338 int newLength = v.EuclideanNorm() - aOffsetAmount;
3339
3340 if( newLength > 0 )
3341 {
3342 VECTOR2I offsetted = v.Resize( newLength ) + VECTOR2I( aRefPoint );
3343 aPointToOffset->x = offsetted.x;
3344 aPointToOffset->y = offsetted.y;
3345 }
3346 else
3347 {
3348 *aPointToOffset = aRefPoint; // zero length track. Needs to be removed to mimmick
3349 // cadstar behaviour
3350 }
3351}
3352
3353
3355 const TEXTCODE_ID& aCadstarTextCodeID )
3356{
3357 TEXTCODE tc = getTextCode( aCadstarTextCodeID );
3358
3359 aKiCadText->SetTextThickness( getKiCadLength( tc.LineWidth ) );
3360
3361 VECTOR2I textSize;
3362 textSize.x = getKiCadLength( tc.Width );
3363
3364 // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
3365 // to work well for most fonts.
3366 if( textSize.x == 0 )
3367 textSize.x = getKiCadLength( tc.Height );
3368
3369 textSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
3370
3371 if( textSize.x == 0 || textSize.y == 0 )
3372 {
3373 // Make zero sized text not visible
3374
3375 aKiCadText->SetTextSize(
3378
3379 aKiCadText->SetVisible( false );
3380 }
3381 else
3382 {
3383 aKiCadText->SetTextSize( textSize );
3384 }
3385}
3386
3387
3389{
3390 wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID )
3393
3394 return getKiCadLength( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Width );
3395}
3396
3397
3399 const COPPERCODE_ID& aCadstaCopperCodeID )
3400{
3401 wxCHECK( Assignments.Codedefs.CopperCodes.find( aCadstaCopperCodeID )
3403 COPPERCODE() );
3404
3405 return Assignments.Codedefs.CopperCodes.at( aCadstaCopperCodeID );
3406}
3407
3408
3410 const TEXTCODE_ID& aCadstarTextCodeID )
3411{
3412 wxCHECK( Assignments.Codedefs.TextCodes.find( aCadstarTextCodeID )
3414 TEXTCODE() );
3415
3416 return Assignments.Codedefs.TextCodes.at( aCadstarTextCodeID );
3417}
3418
3419
3421 const PADCODE_ID& aCadstarPadCodeID )
3422{
3423 wxCHECK( Assignments.Codedefs.PadCodes.find( aCadstarPadCodeID )
3424 != Assignments.Codedefs.PadCodes.end(),
3425 PADCODE() );
3426
3427 return Assignments.Codedefs.PadCodes.at( aCadstarPadCodeID );
3428}
3429
3430
3432 const VIACODE_ID& aCadstarViaCodeID )
3433{
3434 wxCHECK( Assignments.Codedefs.ViaCodes.find( aCadstarViaCodeID )
3435 != Assignments.Codedefs.ViaCodes.end(),
3436 VIACODE() );
3437
3438 return Assignments.Codedefs.ViaCodes.at( aCadstarViaCodeID );
3439}
3440
3441
3443 const LAYERPAIR_ID& aCadstarLayerPairID )
3444{
3445 wxCHECK( Assignments.Codedefs.LayerPairs.find( aCadstarLayerPairID )
3447 LAYERPAIR() );
3448
3449 return Assignments.Codedefs.LayerPairs.at( aCadstarLayerPairID );
3450}
3451
3452
3454{
3455 wxCHECK( Assignments.Codedefs.AttributeNames.find( aCadstarAttributeID )
3457 wxEmptyString );
3458
3459 return Assignments.Codedefs.AttributeNames.at( aCadstarAttributeID ).Name;
3460}
3461
3462
3464 const std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE>& aCadstarAttributeMap )
3465{
3466 wxCHECK( aCadstarAttributeMap.find( aCadstarAttributeID ) != aCadstarAttributeMap.end(),
3467 wxEmptyString );
3468
3469 return aCadstarAttributeMap.at( aCadstarAttributeID ).Value;
3470}
3471
3472
3475{
3476 if( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) != Assignments.Layerdefs.Layers.end() )
3477 {
3478 return Assignments.Layerdefs.Layers.at( aCadstarLayerID ).Type;
3479 }
3480
3481 return LAYER_TYPE::UNDEFINED;
3482}
3483
3484
3486 const PART_ID& aCadstarPartID )
3487{
3488 wxCHECK( Parts.PartDefinitions.find( aCadstarPartID ) != Parts.PartDefinitions.end(), PART() );
3489
3490 return Parts.PartDefinitions.at( aCadstarPartID );
3491}
3492
3493
3495 const ROUTECODE_ID& aCadstarRouteCodeID )
3496{
3497 wxCHECK( Assignments.Codedefs.RouteCodes.find( aCadstarRouteCodeID )
3499 ROUTECODE() );
3500
3501 return Assignments.Codedefs.RouteCodes.at( aCadstarRouteCodeID );
3502}
3503
3504
3506 const HATCHCODE_ID& aCadstarHatchcodeID )
3507{
3508 wxCHECK( Assignments.Codedefs.HatchCodes.find( aCadstarHatchcodeID )
3510 HATCHCODE() );
3511
3512 return Assignments.Codedefs.HatchCodes.at( aCadstarHatchcodeID );
3513}
3514
3515
3517{
3518 checkAndLogHatchCode( aCadstarHatchcodeID );
3519 HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3520
3521 if( hcode.Hatches.size() < 1 )
3523 else
3524 return getAngle( hcode.Hatches.at( 0 ).OrientAngle );
3525}
3526
3527
3529 const HATCHCODE_ID& aCadstarHatchcodeID )
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 ).LineWidth );
3538}
3539
3540
3542{
3543 checkAndLogHatchCode( aCadstarHatchcodeID );
3544 HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3545
3546 if( hcode.Hatches.size() < 1 )
3548 else
3549 return getKiCadLength( hcode.Hatches.at( 0 ).Step );
3550}
3551
3552
3554{
3555 wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), nullptr );
3556
3557 return m_groupMap.at( aCadstarGroupID );
3558}
3559
3560
3562{
3563 if( m_hatchcodesTested.find( aCadstarHatchcodeID ) != m_hatchcodesTested.end() )
3564 {
3565 return; //already checked
3566 }
3567 else
3568 {
3569 HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3570
3571 if( hcode.Hatches.size() != 2 )
3572 {
3573 wxLogWarning( wxString::Format(
3574 _( "The CADSTAR Hatching code '%s' has %d hatches defined. "
3575 "KiCad only supports 2 hatches (crosshatching) 90 degrees apart. "
3576 "The imported hatching is crosshatched." ),
3577 hcode.Name, (int) hcode.Hatches.size() ) );
3578 }
3579 else
3580 {
3581 if( hcode.Hatches.at( 0 ).LineWidth != hcode.Hatches.at( 1 ).LineWidth )
3582 {
3583 wxLogWarning( wxString::Format(
3584 _( "The CADSTAR Hatching code '%s' has different line widths for each "
3585 "hatch. KiCad only supports one width for the hatching. The imported "
3586 "hatching uses the width defined in the first hatch definition, i.e. "
3587 "%.2f mm." ),
3588 hcode.Name,
3589 (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).LineWidth ) )
3590 / 1E6 ) );
3591 }
3592
3593 if( hcode.Hatches.at( 0 ).Step != hcode.Hatches.at( 1 ).Step )
3594 {
3595 wxLogWarning( wxString::Format(
3596 _( "The CADSTAR Hatching code '%s' has different step sizes for each "
3597 "hatch. KiCad only supports one step size for the hatching. The imported "
3598 "hatching uses the step size defined in the first hatching definition, "
3599 "i.e. %.2f mm." ),
3600 hcode.Name,
3601 (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).Step ) )
3602 / 1E6 ) );
3603 }
3604
3605 if( abs( hcode.Hatches.at( 0 ).OrientAngle - hcode.Hatches.at( 1 ).OrientAngle )
3606 != 90000 )
3607 {
3608 wxLogWarning( wxString::Format(
3609 _( "The hatches in CADSTAR Hatching code '%s' have an angle "
3610 "difference of %.1f degrees. KiCad only supports hatching 90 "
3611 "degrees apart. The imported hatching has two hatches 90 "
3612 "degrees apart, oriented %.1f degrees from horizontal." ),
3613 hcode.Name,
3614 getAngle( abs( hcode.Hatches.at( 0 ).OrientAngle
3615 - hcode.Hatches.at( 1 ).OrientAngle ) ).AsDegrees(),
3616 getAngle( hcode.Hatches.at( 0 ).OrientAngle ).AsDegrees() ) );
3617 }
3618 }
3619
3620 m_hatchcodesTested.insert( aCadstarHatchcodeID );
3621 }
3622}
3623
3624
3626 PCB_DIMENSION_BASE* aKiCadDim )
3627{
3628 UNITS dimensionUnits = aCadstarDim.LinearUnits;
3629 LINECODE linecode = Assignments.Codedefs.LineCodes.at( aCadstarDim.Line.LineCodeID );
3630
3631 aKiCadDim->SetLayer( getKiCadLayer( aCadstarDim.LayerID ) );
3632 aKiCadDim->SetPrecision( static_cast<DIM_PRECISION>( aCadstarDim.Precision ) );
3633 aKiCadDim->SetStart( getKiCadPoint( aCadstarDim.ExtensionLineParams.Start ) );
3634 aKiCadDim->SetEnd( getKiCadPoint( aCadstarDim.ExtensionLineParams.End ) );
3635 aKiCadDim->SetExtensionOffset( getKiCadLength( aCadstarDim.ExtensionLineParams.Offset ) );
3636 aKiCadDim->SetLineThickness( getKiCadLength( linecode.Width ) );
3637
3638 applyTextCode( aKiCadDim, aCadstarDim.Text.TextCodeID );
3639
3640 // Find prefix and suffix:
3641 wxString prefix = wxEmptyString;
3642 wxString suffix = wxEmptyString;
3643 size_t startpos = aCadstarDim.Text.Text.Find( wxT( "<@DISTANCE" ) );
3644
3645 if( startpos != wxNOT_FOUND )
3646 {
3647 prefix = ParseTextFields( aCadstarDim.Text.Text.SubString( 0, startpos - 1 ), &m_context );
3648 wxString remainingStr = aCadstarDim.Text.Text.Mid( startpos );
3649 size_t endpos = remainingStr.Find( "@>" );
3650 suffix = ParseTextFields( remainingStr.Mid( endpos + 2 ), &m_context );
3651 }
3652
3653 if( suffix.StartsWith( wxT( "mm" ) ) )
3654 {
3656 suffix = suffix.Mid( 2 );
3657 }
3658 else
3659 {
3661 }
3662
3663 aKiCadDim->SetPrefix( prefix );
3664 aKiCadDim->SetSuffix( suffix );
3665
3666 if( aCadstarDim.LinearUnits == UNITS::DESIGN )
3667 {
3668 // For now we will hardcode the units as per the original CADSTAR design.
3669 // TODO: update this when KiCad supports design units
3671 dimensionUnits = Assignments.Technology.Units;
3672 }
3673
3674 switch( dimensionUnits )
3675 {
3676 case UNITS::METER:
3677 case UNITS::CENTIMETER:
3678 case UNITS::MICROMETRE:
3679 wxLogWarning( wxString::Format( _( "Dimension ID %s uses a type of unit that "
3680 "is not supported in KiCad. Millimeters were "
3681 "applied instead." ),
3682 aCadstarDim.ID ) );
3684 case UNITS::MM:
3686 break;
3687
3688 case UNITS::INCH:
3690 break;
3691
3692 case UNITS::THOU:
3693 aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::MILS );
3694 break;
3695
3696 case UNITS::DESIGN:
3697 wxFAIL_MSG( wxT( "We should have handled design units before coming here!" ) );
3698 break;
3699 }
3700}
3701
3703{
3704 std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
3705
3706 auto inflateValue =
3707 [&]( ZONE* aZoneA, ZONE* aZoneB )
3708 {
3709 int extra = getKiCadLength( Assignments.Codedefs.SpacingCodes.at( wxT( "C_C" ) ).Spacing )
3711
3712 int retval = std::max( aZoneA->GetLocalClearance(), aZoneB->GetLocalClearance() );
3713
3714 retval += extra;
3715
3716 return retval;
3717 };
3718
3719 // Find the error in fill area when guessing that aHigherZone gets filled before aLowerZone
3720 auto errorArea =
3721 [&]( ZONE* aLowerZone, ZONE* aHigherZone ) -> double
3722 {
3723 SHAPE_POLY_SET intersectShape( *aHigherZone->Outline() );
3724 intersectShape.Inflate( inflateValue( aLowerZone, aHigherZone ) , 32 );
3725
3726 SHAPE_POLY_SET lowerZoneFill( *aLowerZone->GetFilledPolysList( aLayer ) );
3727 SHAPE_POLY_SET lowerZoneOutline( *aLowerZone->Outline() );
3728
3729 lowerZoneOutline.BooleanSubtract( intersectShape, SHAPE_POLY_SET::PM_FAST );
3730
3731 lowerZoneFill.BooleanSubtract( lowerZoneOutline, SHAPE_POLY_SET::PM_FAST );
3732
3733 double leftOverArea = lowerZoneFill.Area();
3734
3735 return leftOverArea;
3736 };
3737
3738 auto intersectionAreaOfZoneOutlines =
3739 [&]( ZONE* aZoneA, ZONE* aZoneB ) -> double
3740 {
3741 SHAPE_POLY_SET outLineA( *aZoneA->Outline() );
3742 outLineA.Inflate( inflateValue( aZoneA, aZoneB ), 32 );
3743
3744 SHAPE_POLY_SET outLineB( *aZoneA->Outline() );
3745 outLineB.Inflate( inflateValue( aZoneA, aZoneB ), 32 );
3746
3747 outLineA.BooleanIntersection( outLineB, SHAPE_POLY_SET::PM_FAST );
3748
3749 return outLineA.Area();
3750 };
3751
3752 // Lambda to determine if the zone with template ID 'a' is lower priority than 'b'
3753 auto isLowerPriority =
3754 [&]( const TEMPLATE_ID& a, const TEMPLATE_ID& b ) -> bool
3755 {
3756 return winningOverlaps[b].count( a ) > 0;
3757 };
3758
3759 for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = m_zonesMap.begin();
3760 it1 != m_zonesMap.end(); ++it1 )
3761 {
3762 TEMPLATE& thisTemplate = Layout.Templates.at( it1->first );
3763 ZONE* thisZone = it1->second;
3764
3765 if( !thisZone->GetLayerSet().Contains( aLayer ) )
3766 continue;
3767
3768 for( std::map<TEMPLATE_ID, ZONE*>::iterator it2 = it1;
3769 it2 != m_zonesMap.end(); ++it2 )
3770 {
3771 TEMPLATE& otherTemplate = Layout.Templates.at( it2->first );
3772 ZONE* otherZone = it2->second;
3773
3774 if( thisTemplate.ID == otherTemplate.ID )
3775 continue;
3776
3777 if( !otherZone->GetLayerSet().Contains( aLayer ) )
3778 {
3779 checkPoint();
3780 continue;
3781 }
3782
3783 if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) == 0 )
3784 {
3785 checkPoint();
3786 continue; // The zones do not interact in any way
3787 }
3788
3789 SHAPE_POLY_SET thisZonePolyFill = *thisZone->GetFilledPolysList( aLayer );
3790 SHAPE_POLY_SET otherZonePolyFill = *otherZone->GetFilledPolysList( aLayer );
3791
3792 if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
3793 {
3794 // Test if this zone were lower priority than other zone, what is the error?
3795 double areaThis = errorArea( thisZone, otherZone );
3796 // Vice-versa
3797 double areaOther = errorArea( otherZone, thisZone );
3798
3799 if( areaThis > areaOther )
3800 {
3801 // thisTemplate is filled before otherTemplate
3802 winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3803 }
3804 else
3805 {
3806 // thisTemplate is filled AFTER otherTemplate
3807 winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3808 }
3809 }
3810 else if( thisZonePolyFill.Area() > 0.0 )
3811 {
3812 // The other template is not filled, this one wins
3813 winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3814 }
3815 else if( otherZonePolyFill.Area() > 0.0 )
3816 {
3817 // This template is not filled, the other one wins
3818 winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3819 }
3820 else
3821 {
3822 // Neither of the templates is poured - use zone outlines instead (bigger outlines
3823 // get a lower priority)
3824 if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) != 0 )
3825 {
3826 if( thisZone->Outline()->Area() > otherZone->Outline()->Area() )
3827 winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3828 else
3829 winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3830 }
3831 }
3832
3833 checkPoint();
3834 }
3835 }
3836
3837 // Build a set of unique TEMPLATE_IDs of all the zones that intersect with another one
3838 std::set<TEMPLATE_ID> intersectingIDs;
3839
3840 for( const std::pair<TEMPLATE_ID, std::set<TEMPLATE_ID>>& idPair : winningOverlaps )
3841 {
3842 intersectingIDs.insert( idPair.first );
3843 intersectingIDs.insert( idPair.second.begin(), idPair.second.end() );
3844 }
3845
3846 // Now store them in a vector
3847 std::vector<TEMPLATE_ID> sortedIDs;
3848
3849 for( const TEMPLATE_ID& id : intersectingIDs )
3850 {
3851 sortedIDs.push_back( id );
3852 }
3853
3854 // sort by priority
3855 std::sort( sortedIDs.begin(), sortedIDs.end(), isLowerPriority );
3856
3857 TEMPLATE_ID prevID = wxEmptyString;
3858
3859 for( const TEMPLATE_ID& id : sortedIDs )
3860 {
3861 if( prevID.IsEmpty() )
3862 {
3863 prevID = id;
3864 continue;
3865 }
3866
3867 wxASSERT( !isLowerPriority( id, prevID ) );
3868
3869 int newPriority = m_zonesMap.at( prevID )->GetAssignedPriority();
3870
3871 // Only increase priority of the current zone
3872 if( isLowerPriority( prevID, id ) )
3873 newPriority++;
3874
3875 m_zonesMap.at( id )->SetAssignedPriority( newPriority );
3876 prevID = id;
3877 }
3878
3879 // Verify
3880 for( const std::pair<TEMPLATE_ID, std::set<TEMPLATE_ID>>& idPair : winningOverlaps )
3881 {
3882 const TEMPLATE_ID& winningID = idPair.first;
3883
3884 for( const TEMPLATE_ID& losingID : idPair.second )
3885 {
3886 if( m_zonesMap.at( losingID )->GetAssignedPriority()
3887 > m_zonesMap.at( winningID )->GetAssignedPriority() )
3888 {
3889 return false;
3890 }
3891 }
3892 }
3893
3894 return true;
3895}
3896
3897
3899 const COMPONENT_ID& aCadstarComponentID )
3900{
3901 if( m_componentMap.find( aCadstarComponentID ) == m_componentMap.end() )
3902 return nullptr;
3903 else
3904 return m_componentMap.at( aCadstarComponentID );
3905}
3906
3907
3909{
3910 VECTOR2I retval;
3911
3912 retval.x = ( aCadstarPoint.x - m_designCenter.x ) * KiCadUnitMultiplier;
3913 retval.y = -( aCadstarPoint.y - m_designCenter.y ) * KiCadUnitMultiplier;
3914
3915 return retval;
3916}
3917
3918
3920{
3921 if( aCadstarNetID.IsEmpty() )
3922 {
3923 return nullptr;
3924 }
3925 else if( m_netMap.find( aCadstarNetID ) != m_netMap.end() )
3926 {
3927 return m_netMap.at( aCadstarNetID );
3928 }
3929 else
3930 {
3931 wxCHECK( Layout.Nets.find( aCadstarNetID ) != Layout.Nets.end(), nullptr );
3932
3933 NET_PCB csNet = Layout.Nets.at( aCadstarNetID );
3934 wxString newName = csNet.Name;
3935
3936 if( csNet.Name.IsEmpty() )
3937 {
3938 if( csNet.Pins.size() > 0 )
3939 {
3940 // Create default KiCad net naming:
3941
3942 NET_PCB::PIN firstPin = ( *csNet.Pins.begin() ).second;
3943 //we should have already loaded the component with loadComponents() :
3944 FOOTPRINT* m = getFootprintFromCadstarID( firstPin.ComponentID );
3945 newName = wxT( "Net-(" );
3946 newName << m->Reference().GetText();
3947 newName << wxT( "-Pad" ) << wxString::Format( wxT( "%ld" ), firstPin.PadID );
3948 newName << wxT( ")" );
3949 }
3950 else
3951 {
3952 wxFAIL_MSG( wxT( "A net with no pins associated?" ) );
3953 newName = wxT( "csNet-" );
3954 newName << wxString::Format( wxT( "%i" ), csNet.SignalNum );
3955 }
3956 }
3957
3958 if( !m_doneNetClassWarning && !csNet.NetClassID.IsEmpty()
3959 && csNet.NetClassID != wxT( "NONE" ) )
3960 {
3961 wxLogMessage( _( "The CADSTAR design contains nets with a 'Net Class' assigned. KiCad "
3962 "does not have an equivalent to CADSTAR's Net Class so these elements "
3963 "were not imported. Note: KiCad's version of 'Net Class' is closer to "
3964 "CADSTAR's 'Net Route Code' (which has been imported for all nets)." ) );
3965 m_doneNetClassWarning = true;
3966 }
3967
3968 if( !m_doneSpacingClassWarning && !csNet.SpacingClassID.IsEmpty()
3969 && csNet.SpacingClassID != wxT( "NONE" ) )
3970 {
3971 wxLogWarning( _( "The CADSTAR design contains nets with a 'Spacing Class' assigned. "
3972 "KiCad does not have an equivalent to CADSTAR's Spacing Class so "
3973 "these elements were not imported. Please review the design rules as "
3974 "copper pours will affected by this." ) );
3976 }
3977
3978 std::shared_ptr<NET_SETTINGS>& netSettings = m_board->GetDesignSettings().m_NetSettings;
3979 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, newName, ++m_numNets );
3980 std::shared_ptr<NETCLASS> netclass;
3981
3982 std::tuple<ROUTECODE_ID, NETCLASS_ID, SPACING_CLASS_ID> key = { csNet.RouteCodeID,
3983 csNet.NetClassID,
3984 csNet.SpacingClassID };
3985
3986 if( m_netClassMap.find( key ) != m_netClassMap.end() )
3987 {
3988 netclass = m_netClassMap.at( key );
3989 }
3990 else
3991 {
3992 wxString netClassName;
3993
3994 ROUTECODE rc = getRouteCode( csNet.RouteCodeID );
3995 netClassName += wxT( "Route code: " ) + rc.Name;
3996
3997 if( !csNet.NetClassID.IsEmpty() )
3998 {
4000 netClassName += wxT( " | Net class: " ) + nc.Name;
4001 }
4002
4003 if( !csNet.SpacingClassID.IsEmpty() )
4004 {
4006 netClassName += wxT( " | Spacing class: " ) + sp.Name;
4007 }
4008
4009 netclass.reset( new NETCLASS( *netSettings->m_DefaultNetClass ) );
4010 netclass->SetName( netClassName );
4011 netSettings->m_NetClasses[ netClassName ] = netclass;
4012 netclass->SetTrackWidth( getKiCadLength( rc.OptimalWidth ) );
4013 m_netClassMap.insert( { key, netclass } );
4014 }
4015
4016 m_board->GetDesignSettings().m_NetSettings->m_NetClassPatternAssignments.push_back(
4017 {
4018 std::make_unique<EDA_COMBINED_MATCHER>( newName, CTX_NETCLASS ),
4019 netclass->GetName()
4020 } );
4021
4022 netInfo->SetNetClass( netclass );
4023 m_board->Add( netInfo, ADD_MODE::APPEND );
4024 m_netMap.insert( { aCadstarNetID, netInfo } );
4025 return netInfo;
4026 }
4027
4028 return nullptr;
4029}
4030
4031
4033 bool aDetectMaxLayer )
4034{
4035 if( aDetectMaxLayer && aLayerNum == m_numCopperLayers )
4036 return PCB_LAYER_ID::B_Cu;
4037
4038 switch( aLayerNum )
4039 {
4040 case 1: return PCB_LAYER_ID::F_Cu;
4041 case 2: return PCB_LAYER_ID::In1_Cu;
4042 case 3: return PCB_LAYER_ID::In2_Cu;
4043 case 4: return PCB_LAYER_ID::In3_Cu;
4044 case 5: return PCB_LAYER_ID::In4_Cu;
4045 case 6: return PCB_LAYER_ID::In5_Cu;
4046 case 7: return PCB_LAYER_ID::In6_Cu;
4047 case 8: return PCB_LAYER_ID::In7_Cu;
4048 case 9: return PCB_LAYER_ID::In8_Cu;
4049 case 10: return PCB_LAYER_ID::In9_Cu;
4050 case 11: return PCB_LAYER_ID::In10_Cu;
4051 case 12: return PCB_LAYER_ID::In11_Cu;
4052 case 13: return PCB_LAYER_ID::In12_Cu;
4053 case 14: return PCB_LAYER_ID::In13_Cu;
4054 case 15: return PCB_LAYER_ID::In14_Cu;
4055 case 16: return PCB_LAYER_ID::In15_Cu;
4056 case 17: return PCB_LAYER_ID::In16_Cu;
4057 case 18: return PCB_LAYER_ID::In17_Cu;
4058 case 19: return PCB_LAYER_ID::In18_Cu;
4059 case 20: return PCB_LAYER_ID::In19_Cu;
4060 case 21: return PCB_LAYER_ID::In20_Cu;
4061 case 22: return PCB_LAYER_ID::In21_Cu;
4062 case 23: return PCB_LAYER_ID::In22_Cu;
4063 case 24: return PCB_LAYER_ID::In23_Cu;
4064 case 25: return PCB_LAYER_ID::In24_Cu;
4065 case 26: return PCB_LAYER_ID::In25_Cu;
4066 case 27: return PCB_LAYER_ID::In26_Cu;
4067 case 28: return PCB_LAYER_ID::In27_Cu;
4068 case 29: return PCB_LAYER_ID::In28_Cu;
4069 case 30: return PCB_LAYER_ID::In29_Cu;
4070 case 31: return PCB_LAYER_ID::In30_Cu;
4071 case 32: return PCB_LAYER_ID::B_Cu;
4072 }
4073
4075}
4076
4077
4079{
4080 wxCHECK( Assignments.Layerdefs.Layers.find( aCadstarLayerID )
4081 != Assignments.Layerdefs.Layers.end(),
4082 false );
4083
4084 LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID );
4085
4086 switch( layer.Type )
4087 {
4088 case LAYER_TYPE::ALLDOC:
4091 return true;
4092
4093 default:
4094 return false;
4095 }
4096
4097 return false;
4098}
4099
4100
4102{
4103 if( getLayerType( aCadstarLayerID ) == LAYER_TYPE::NOLAYER )
4104 {
4105 //The "no layer" is common for CADSTAR documentation symbols
4106 //map it to undefined layer for later processing
4108 }
4109
4110 wxCHECK( m_layermap.find( aCadstarLayerID ) != m_layermap.end(),
4112
4113 return m_layermap.at( aCadstarLayerID );
4114}
4115
4116
4118{
4119 LAYER_TYPE layerType = getLayerType( aCadstarLayerID );
4120
4121 switch( layerType )
4122 {
4123 case LAYER_TYPE::ALLDOC:
4126
4129
4131 return LSET::AllLayersMask()
4133 ^ ( LSET( PCB_LAYER_ID::Rescue ) );
4134
4135 default:
4136 return LSET( getKiCadLayer( aCadstarLayerID ) );
4137 }
4138}
4139
4140
4142 const GROUP_ID& aCadstarGroupID, BOARD_ITEM* aKiCadItem )
4143{
4144 wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), );
4145
4146 PCB_GROUP* parentGroup = m_groupMap.at( aCadstarGroupID );
4147 parentGroup->AddItem( aKiCadItem );
4148}
4149
4150
4152 const wxString& aName )
4153{
4154 wxString groupName = aName;
4155 int num = 0;
4156
4157 while( m_groupMap.find( groupName ) != m_groupMap.end() )
4158 {
4159 groupName = aName + wxT( "_" ) + wxString::Format( wxT( "%i" ), ++num );
4160 }
4161
4162 PCB_GROUP* docSymGroup = new PCB_GROUP( m_board );
4163 m_board->Add( docSymGroup );
4164 docSymGroup->SetName( groupName );
4165 GROUP_ID groupID( groupName );
4166 m_groupMap.insert( { groupID, docSymGroup } );
4167
4168 return groupID;
4169}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:121
constexpr double PCB_IU_PER_MM
Definition: base_units.h:71
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition: board.h:147
@ LT_POWER
Definition: board.h:150
@ LT_JUMPER
Definition: board.h:152
@ LT_SIGNAL
Definition: board.h:149
@ 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:70
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:192
virtual void SetLocked(bool aLocked)
Definition: board_item.h:266
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:226
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:269
LSET GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition: board.cpp:587
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:772
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:607
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:490
void SetVisibleLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings changes the bit-mask of vis...
Definition: board.cpp:619
void SetCopperLayerCount(int aCount)
Definition: board.cpp:569
bool SetLayerType(PCB_LAYER_ID aLayer, LAYER_T aLayerType)
Change the type of the layer given by aLayer.
Definition: board.cpp:520
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
Definition: board.cpp:474
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:704
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:881
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)
void applyTextCode(EDA_TEXT *aKiCadText, const TEXTCODE_ID &aCadstarTextCodeID)
Apply cadstar textcode parameters to a KiCad text object.
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 NormalizeNegative()
Definition: eda_angle.h:266
double AsDegrees() const
Definition: eda_angle.h:149
EDA_ANGLE Normalize180()
Definition: eda_angle.h:288
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:585
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:470
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:596
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:72
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:373
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:226
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:250
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:219
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:187
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:258
void SetTextSize(const VECTOR2I &aNewSize)
Definition: eda_text.cpp:349
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:165
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:195
int GetTextThickness() const
Definition: eda_text.h:112
VECTOR2I GetTextSize() const
Definition: eda_text.h:196
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:242
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:1688
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:1820
void SetDescription(const wxString &aDoc)
Definition: footprint.h:219
BOARD_ITEM * Duplicate() const override
Create a copy of this BOARD_ITEM.
Definition: footprint.cpp:1874
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:568
void Flip(const VECTOR2I &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:1600
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
virtual void SetLocalCoord()
Set relative coordinates from draw coordinates.
Definition: fp_shape.cpp:52
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 ther