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