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{
841 for( COMPONENT_COPPER compCopper : aComponent.ComponentCoppers )
842 {
843 int lineThickness = getKiCadLength( getCopperCode( compCopper.CopperCodeID ).CopperWidth );
844 LSET layers = getKiCadLayerSet( compCopper.LayerID );
845 LSET copperLayers = LSET::AllCuMask() & layers;
846 LSET remainingLayers = layers;
847
848 if( compCopper.AssociatedPadIDs.size() > 0
849 && copperLayers.count() > 0
850 && compCopper.Shape.Type == SHAPE_TYPE::SOLID )
851 {
852 // The copper is associated with pads and in an electrical layer which means it can
853 // have a net associated with it. Load as a pad instead.
854 // Note: we can only handle SOLID copper shapes. If the copper shape is an outline or
855 // hatched or outline, then we give up and load as a graphical shape instead.
856
857 // Find the first non-PCB-only pad. If there are none, use the first one
858 COMPONENT_PAD anchorPad;
859 bool found = false;
860
861 for( PAD_ID padID : compCopper.AssociatedPadIDs )
862 {
863 anchorPad = aComponent.ComponentPads.at( padID );
864
865 if( !anchorPad.PCBonlyPad )
866 {
867 found = true;
868 break;
869 }
870 }
871
872 if( !found )
873 anchorPad = aComponent.ComponentPads.at( compCopper.AssociatedPadIDs.front() );
874
875 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
876 pad->SetAttribute( PAD_ATTRIB::SMD );
877 pad->SetLayerSet( copperLayers );
878 pad->SetNumber( anchorPad.Identifier.IsEmpty() ? wxString::Format( wxT( "%ld" ), anchorPad.ID )
879 : anchorPad.Identifier );
880
881 // Custom pad shape with an anchor at the position of one of the associated
882 // pads and same size as the pad. Shape circle as it fits inside a rectangle
883 // but not the other way round
884 PADCODE anchorpadcode = getPadCode( anchorPad.PadCodeID );
885 int anchorSize = getKiCadLength( anchorpadcode.Shape.Size );
886 VECTOR2I anchorPos = getKiCadPoint( anchorPad.Position );
887
888 if( anchorSize <= 0 )
889 anchorSize = 1;
890
891 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
893 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
894 pad->SetSize( PADSTACK::ALL_LAYERS, { anchorSize, anchorSize } );
895 pad->SetPosition( anchorPos );
896
897 SHAPE_POLY_SET shapePolys = getPolySetFromCadstarShape( compCopper.Shape, lineThickness, aFootprint );
898 shapePolys.Move( -anchorPos );
899 pad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, shapePolys, 0, true );
900
901 // Now renumber all the associated pads
902 COMPONENT_PAD associatedPad;
903
904 for( PAD_ID padID : compCopper.AssociatedPadIDs )
905 {
906 PAD* assocPad = getPadReference( aFootprint, padID );
907 assocPad->SetNumber( pad->GetNumber() );
908 }
909
910 aFootprint->Add( pad.release(), ADD_MODE::APPEND ); // Append so that we get the correct behaviour
911 // when finding pads by PAD_ID. See loadNets()
912
913 m_librarycopperpads[aComponent.ID][anchorPad.ID].push_back( aFootprint->Pads().size() );
914
915 remainingLayers ^= copperLayers; // don't process copper layers again!
916 }
917
918 if( remainingLayers.any() )
919 {
920 for( const PCB_LAYER_ID& layer : remainingLayers.Seq() )
921 {
922 drawCadstarShape( compCopper.Shape, layer, lineThickness, LINE_STYLE::SOLID,
923 wxString::Format( wxT( "Component %s:%s -> Copper element" ),
924 aComponent.ReferenceName, aComponent.Alternate ),
925 aFootprint );
926 }
927 }
928 }
929}
930
931
933{
934 for( std::pair<COMP_AREA_ID, COMPONENT_AREA> areaPair : aComponent.ComponentAreas )
935 {
936 COMPONENT_AREA& area = areaPair.second;
937
938 if( area.NoVias || area.NoTracks )
939 {
940 int lineThickness = 0; // CADSTAR areas only use the line width for display purpose
941 ZONE* zone = getZoneFromCadstarShape( area.Shape, lineThickness, aFootprint );
942
943 aFootprint->Add( zone, ADD_MODE::APPEND );
944
945 if( isLayerSet( area.LayerID ) )
946 zone->SetLayerSet( getKiCadLayerSet( area.LayerID ) );
947 else
948 zone->SetLayer( getKiCadLayer( area.LayerID ) );
949
950 zone->SetIsRuleArea( true ); //import all CADSTAR areas as Keepout zones
951 zone->SetDoNotAllowPads( false ); //no CADSTAR equivalent
952 zone->SetZoneName( area.ID );
953
954 //There is no distinction between tracks and copper pours in CADSTAR Keepout zones
955 zone->SetDoNotAllowTracks( area.NoTracks );
956 zone->SetDoNotAllowZoneFills( area.NoTracks );
957
958 zone->SetDoNotAllowVias( area.NoVias );
959 }
960 else
961 {
962 wxString libName = aComponent.ReferenceName;
963
964 if( !aComponent.Alternate.IsEmpty() )
965 libName << wxT( " (" ) << aComponent.Alternate << wxT( ")" );
966
967 reportError( wxString::Format( _( "The CADSTAR area '%s' in library component '%s' does not "
968 "have a KiCad equivalent. The area is neither a via nor "
969 "route keepout area. The area was not imported." ),
970 area.ID,
971 libName ) );
972 }
973 }
974}
975
976
978 FOOTPRINT* aFootprint )
979{
980 for( std::pair<PAD_ID, COMPONENT_PAD> padPair : aComponent.ComponentPads )
981 {
982 if( PAD* pad = getKiCadPad( padPair.second, aFootprint ) )
983 {
984 aFootprint->Add( pad, ADD_MODE::APPEND ); // Append so that we get correct behaviour
985 // when finding pads by PAD_ID - see loadNets()
986 }
987 }
988}
989
990
992 const CADSTAR_PAD_SHAPE& aShape )
993{
994 // CADSTAR grows some shapes by a left and a right length, moving the centre off the pad origin
995 VECTOR2I offset = { 0, 0 };
996
997 auto elongatedSize =
998 [&]() -> VECTOR2I
999 {
1000 return { getKiCadLength( (long long) aShape.Size + (long long) aShape.LeftLength
1001 + (long long) aShape.RightLength ),
1002 getKiCadLength( aShape.Size ) };
1003 };
1004
1005 auto elongationOffset =
1006 [&]()
1007 {
1008 offset.x = getKiCadLength( ( (long long) aShape.LeftLength / 2 )
1009 - ( (long long) aShape.RightLength / 2 ) );
1010 };
1011
1012 switch( aShape.ShapeType )
1013 {
1015 //todo fix: use custom shape instead (Donught shape, i.e. a circle with a hole)
1016 aPad->SetShape( aPadLayer, PAD_SHAPE::CIRCLE );
1017 aPad->SetSize( aPadLayer, { getKiCadLength( aShape.Size ),
1018 getKiCadLength( aShape.Size ) } );
1019 break;
1020
1022 aPad->SetShape( aPadLayer, PAD_SHAPE::CHAMFERED_RECT );
1023 aPad->SetSize( aPadLayer, elongatedSize() );
1024 aPad->SetChamferPositions( aPadLayer,
1027 aPad->SetRoundRectRadiusRatio( aPadLayer, 0.5 );
1028 aPad->SetChamferRectRatio( aPadLayer, 0.0 );
1029
1030 elongationOffset();
1031 break;
1032
1034 aPad->SetShape( aPadLayer, PAD_SHAPE::CIRCLE );
1035 aPad->SetSize( aPadLayer, { getKiCadLength( aShape.Size ),
1036 getKiCadLength( aShape.Size ) } );
1037 break;
1038
1040 {
1041 // Cadstar diamond shape is a square rotated 45 degrees
1042 // We convert it in KiCad to a square with chamfered edges
1043 int sizeOfSquare = (double) getKiCadLength( aShape.Size ) * sqrt(2.0);
1044 aPad->SetShape( aPadLayer, PAD_SHAPE::RECTANGLE );
1045 aPad->SetChamferRectRatio( aPadLayer, 0.5 );
1046 aPad->SetSize( aPadLayer, { sizeOfSquare, sizeOfSquare } );
1047
1048 elongationOffset();
1049 break;
1050 }
1051
1053 aPad->SetShape( aPadLayer, PAD_SHAPE::OVAL );
1054 aPad->SetSize( aPadLayer, elongatedSize() );
1055
1056 elongationOffset();
1057 break;
1058
1060 aPad->SetShape( aPadLayer, PAD_SHAPE::CHAMFERED_RECT );
1062 aPad->SetChamferRectRatio( aPadLayer, 0.25 );
1063 aPad->SetSize( aPadLayer, { getKiCadLength( aShape.Size ),
1064 getKiCadLength( aShape.Size ) } );
1065 break;
1066
1068 aPad->SetShape( aPadLayer, PAD_SHAPE::RECTANGLE );
1069 aPad->SetSize( aPadLayer, elongatedSize() );
1070
1071 elongationOffset();
1072 break;
1073
1075 aPad->SetShape( aPadLayer, PAD_SHAPE::ROUNDRECT );
1076 aPad->SetRoundRectCornerRadius( aPadLayer, getKiCadLength( aShape.InternalFeature ) );
1077 aPad->SetSize( aPadLayer, elongatedSize() );
1078
1079 elongationOffset();
1080 break;
1081
1083 aPad->SetShape( aPadLayer, PAD_SHAPE::RECTANGLE );
1084 aPad->SetSize( aPadLayer, { getKiCadLength( aShape.Size ),
1085 getKiCadLength( aShape.Size ) } );
1086 break;
1087
1088 default:
1089 wxFAIL_MSG( wxT( "Unknown Pad Shape" ) );
1090 }
1091
1092 return offset;
1093}
1094
1095
1097{
1098 PADCODE csPadcode = getPadCode( aCadstarPad.PadCodeID );
1099 wxString errorMSG;
1100
1101 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aParent );
1102 LSET padLayerSet;
1103
1104 switch( aCadstarPad.Side )
1105 {
1106 case PAD_SIDE::MAXIMUM: //Bottom side
1107 padLayerSet |= LSET( { B_Cu, B_Paste, B_Mask } );
1108 break;
1109
1110 case PAD_SIDE::MINIMUM: //TOP side
1111 padLayerSet |= LSET( { F_Cu, F_Paste, F_Mask } );
1112 break;
1113
1115 padLayerSet = LSET::AllCuMask( m_numCopperLayers ) | LSET( { F_Mask, B_Mask, F_Paste, B_Paste } );
1116 break;
1117
1118 default:
1119 wxFAIL_MSG( wxT( "Unknown Pad type" ) );
1120 }
1121
1122 pad->SetAttribute( PAD_ATTRIB::SMD ); // assume SMD pad for now
1123 pad->SetLocalSolderMaskMargin( 0 );
1124 pad->SetLocalSolderPasteMargin( 0 );
1125 pad->SetLocalSolderPasteMarginRatio( 0.0 );
1126
1127 std::map<PCB_LAYER_ID, CADSTAR_PAD_SHAPE> copperReassigns;
1128
1129 for( auto& [layer, shape] : csPadcode.Reassigns )
1130 {
1131 PCB_LAYER_ID kiLayer = getKiCadLayer( layer );
1132
1133 if( shape.Size == 0 )
1134 {
1135 if( kiLayer > UNDEFINED_LAYER )
1136 padLayerSet.reset( kiLayer );
1137 }
1138 else
1139 {
1140 int newMargin = getKiCadLength( shape.Size - csPadcode.Shape.Size ) / 2;
1141
1142 if( kiLayer == F_Mask || kiLayer == B_Mask )
1143 {
1144 std::optional<int> localMargin = pad->GetLocalSolderMaskMargin();
1145
1146 if( !localMargin.has_value() )
1147 pad->SetLocalSolderMaskMargin( newMargin );
1148 else if( std::abs( localMargin.value() ) < std::abs( newMargin ) )
1149 pad->SetLocalSolderMaskMargin( newMargin );
1150 }
1151 else if( kiLayer == F_Paste || kiLayer == B_Paste )
1152 {
1153 std::optional<int> localMargin = pad->GetLocalSolderPasteMargin();
1154
1155 if( !localMargin.has_value() )
1156 pad->SetLocalSolderPasteMargin( newMargin );
1157 else if( std::abs( localMargin.value() ) < std::abs( newMargin ) )
1158 pad->SetLocalSolderPasteMargin( newMargin );
1159 }
1160 else if( IsCopperLayer( kiLayer ) )
1161 {
1162 copperReassigns[kiLayer] = shape;
1163 }
1164 }
1165 }
1166
1167 pad->SetLayerSet( padLayerSet );
1168
1169 if( aCadstarPad.PCBonlyPad )
1170 {
1171 // PCB Only pads in CADSTAR do not have a representation in the schematic - they are
1172 // purely mechanical pads that have no net associated with them. Make the pad name
1173 // empty to avoid warnings when importing from the schematic
1174 pad->SetNumber( wxT( "" ) );
1175 }
1176 else
1177 {
1178 pad->SetNumber( aCadstarPad.Identifier.IsEmpty() ? wxString::Format( wxT( "%ld" ), aCadstarPad.ID )
1179 : aCadstarPad.Identifier );
1180 }
1181
1182 if( csPadcode.Shape.Size == 0 )
1183 {
1184 if( csPadcode.DrillDiameter == UNDEFINED_VALUE && aCadstarPad.Side == PAD_SIDE::THROUGH_HOLE )
1185 {
1186 // Through-hole, zero sized pad? Lets load this just on the F_Mask for now to
1187 // prevent DRC errors.
1188 // TODO: This could be a custom padstack
1189 pad->SetAttribute( PAD_ATTRIB::SMD );
1190 pad->SetLayerSet( LSET( { F_Mask } ) );
1191 }
1192
1193 // zero sized pads seems to break KiCad so lets make it very small instead
1194 csPadcode.Shape.Size = 1;
1195 }
1196
1197 VECTOR2I padOffset = applyPadShape( pad.get(), PADSTACK::ALL_LAYERS, csPadcode.Shape );
1198 VECTOR2I drillOffset = { 0, 0 }; // offset of the drill origin w.r.t. the pad (before rotating)
1199
1200 // A layer reassign needs a full-custom padstack. The shape centre can move per layer, so
1201 // the delta from the base shape becomes that layer's offset
1202 if( !copperReassigns.empty() )
1203 {
1204 pad->Padstack().SetMode( PADSTACK::MODE::CUSTOM );
1205
1206 for( const auto& [kiLayer, shape] : copperReassigns )
1207 {
1208 VECTOR2I layerOffset = applyPadShape( pad.get(), kiLayer, shape );
1209 pad->SetOffset( kiLayer, padOffset - layerOffset );
1210 }
1211 }
1212
1213 if( csPadcode.ReliefClearance != UNDEFINED_VALUE )
1214 pad->SetThermalGap( getKiCadLength( csPadcode.ReliefClearance ) );
1215
1216 if( csPadcode.ReliefWidth != UNDEFINED_VALUE )
1217 pad->SetLocalThermalSpokeWidthOverride( getKiCadLength( csPadcode.ReliefWidth ) );
1218
1219 if( csPadcode.DrillDiameter != UNDEFINED_VALUE )
1220 {
1221 if( csPadcode.SlotLength != UNDEFINED_VALUE )
1222 {
1223 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
1224 pad->SetDrillSize( { getKiCadLength( (long long) csPadcode.SlotLength +
1225 (long long) csPadcode.DrillDiameter ),
1226 getKiCadLength( csPadcode.DrillDiameter ) } );
1227 }
1228 else
1229 {
1230 pad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
1231 pad->SetDrillSize( { getKiCadLength( csPadcode.DrillDiameter ),
1232 getKiCadLength( csPadcode.DrillDiameter ) } );
1233 }
1234
1235 drillOffset.x = -getKiCadLength( csPadcode.DrillXoffset );
1236 drillOffset.y = getKiCadLength( csPadcode.DrillYoffset );
1237
1238 if( csPadcode.Plated )
1239 pad->SetAttribute( PAD_ATTRIB::PTH );
1240 else
1241 pad->SetAttribute( PAD_ATTRIB::NPTH );
1242 }
1243 else
1244 {
1245 pad->SetDrillSize( { 0, 0 } );
1246 }
1247
1248 if( csPadcode.SlotOrientation != 0 )
1249 {
1250 LSET lset = pad->GetLayerSet();
1251 lset &= LSET::AllCuMask();
1252
1253 if( lset.size() > 0 )
1254 {
1255 int maxError = m_board->GetDesignSettings().m_MaxError;
1256 EDA_ANGLE slotAngle = ANGLE_180 - getAngle( csPadcode.SlotOrientation );
1257 bool holeInsidePad = true;
1258
1259 pad->SetPosition( { 0, 0 } );
1260
1261 // Rotating the slot bakes the copper into a custom primitive, so each padstack
1262 // layer needs its own
1263 std::map<PCB_LAYER_ID, std::unique_ptr<PCB_SHAPE>> slotShapes;
1264
1265 for( PCB_LAYER_ID padLayer : pad->Padstack().UniqueLayers() )
1266 {
1267 SHAPE_POLY_SET padOutline;
1268 pad->TransformShapeToPolygon( padOutline, padLayer, 0, maxError, ERROR_INSIDE );
1269
1270 auto padShape = std::make_unique<PCB_SHAPE>();
1271 padShape->SetShape( SHAPE_T::POLY );
1272 padShape->SetFilled( true );
1273 padShape->SetPolyShape( padOutline );
1274 padShape->SetStroke( STROKE_PARAMS( 0 ) );
1275 padShape->Move( padOffset - drillOffset );
1276 padShape->Rotate( VECTOR2I( 0, 0 ), slotAngle );
1277
1278 if( !padShape->GetPolyShape().Contains( { 0, 0 } ) )
1279 holeInsidePad = false;
1280
1281 slotShapes[padLayer] = std::move( padShape );
1282 }
1283
1284 if( holeInsidePad )
1285 {
1286 for( auto& [padLayer, padShape] : slotShapes )
1287 {
1288 pad->SetAnchorPadShape( padLayer, PAD_SHAPE::RECTANGLE );
1289 pad->SetSize( padLayer, VECTOR2I( { 4, 4 } ) );
1290 pad->SetShape( padLayer, PAD_SHAPE::CUSTOM );
1291
1292 // The outline already includes the layer offset; leaving it set shifts twice
1293 pad->SetOffset( padLayer, { 0, 0 } );
1294 pad->AddPrimitive( padLayer, padShape.release() );
1295 }
1296
1297 padOffset = { 0, 0 };
1298 }
1299 else
1300 {
1301 // The CADSTAR pad has the hole shape outside the pad shape
1302 // Lets just put the hole in the center of the pad instead
1303 csPadcode.SlotOrientation = 0;
1304 drillOffset = { 0, 0 };
1305
1306 errorMSG += wxT( "\n - " )
1307 + wxString::Format( _( "The CADSTAR pad definition '%s' has the hole shape outside the "
1308 "pad shape. The hole has been moved to the center of the pad." ),
1309 csPadcode.Name );
1310 }
1311 }
1312 else
1313 {
1314 wxFAIL_MSG( wxT( "No copper layers defined in the pad?" ) );
1315 csPadcode.SlotOrientation = 0;
1316 pad->SetOffset( PADSTACK::ALL_LAYERS, drillOffset );
1317 }
1318 }
1319 else
1320 {
1321 // The reassigned layers already carry the offset that centres their own copper
1322 for( PCB_LAYER_ID padLayer : pad->Padstack().UniqueLayers() )
1323 pad->SetOffset( padLayer, pad->GetOffset( padLayer ) + drillOffset );
1324 }
1325
1326 EDA_ANGLE padOrientation = getAngle( aCadstarPad.OrientAngle )
1327 + getAngle( csPadcode.Shape.OrientAngle );
1328
1329 RotatePoint( padOffset, padOrientation );
1330 RotatePoint( drillOffset, padOrientation );
1331 pad->SetPosition( getKiCadPoint( aCadstarPad.Position ) - padOffset - drillOffset );
1332 pad->SetOrientation( padOrientation + getAngle( csPadcode.SlotOrientation ) );
1333
1334 //log warnings:
1335 if( m_padcodesTested.find( csPadcode.ID ) == m_padcodesTested.end() && !errorMSG.IsEmpty() )
1336 {
1337 reportError( wxString::Format( _( "The CADSTAR pad definition '%s' has import errors: %s" ),
1338 csPadcode.Name,
1339 errorMSG ) );
1340
1341 m_padcodesTested.insert( csPadcode.ID );
1342 }
1343
1344 return pad.release();
1345}
1346
1347
1349{
1350 size_t index = aCadstarPadID - (long) 1;
1351
1352 if( !( index < aFootprint->Pads().size() ) )
1353 {
1354 THROW_IO_ERRORF( _( "Unable to find pad index '%ld' in footprint '%s'." ),
1355 (long) aCadstarPadID, aFootprint->GetReference() );
1356 }
1357
1358 return aFootprint->Pads().at( index );
1359}
1360
1361
1363{
1364 for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1365 {
1366 GROUP& csGroup = groupPair.second;
1367
1368 PCB_GROUP* kiGroup = new PCB_GROUP( m_board );
1369
1370 m_board->Add( kiGroup );
1371 kiGroup->SetName( csGroup.Name );
1372 kiGroup->SetLocked( csGroup.Fixed );
1373
1374 m_groupMap.insert( { csGroup.ID, kiGroup } );
1375 }
1376
1377 //now add any groups to their parent group
1378 for( std::pair<GROUP_ID, GROUP> groupPair : Layout.Groups )
1379 {
1380 GROUP& csGroup = groupPair.second;
1381
1382 if( !csGroup.GroupID.IsEmpty() )
1383 {
1384 if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1385 {
1386 THROW_IO_ERRORF( _( "Unable to find group ID %s in the group definitions." ), csGroup.ID );
1387 }
1388 else if( m_groupMap.find( csGroup.ID ) == m_groupMap.end() )
1389 {
1390 THROW_IO_ERRORF( _( "Unable to find sub group %s in the group map (parent group ID=%s, Name=%s)." ),
1391 csGroup.GroupID,
1392 csGroup.ID,
1393 csGroup.Name );
1394 }
1395 else
1396 {
1397 PCB_GROUP* kiCadGroup = m_groupMap.at( csGroup.ID );
1398 PCB_GROUP* parentGroup = m_groupMap.at( csGroup.GroupID );
1399 parentGroup->AddItem( kiCadGroup );
1400 }
1401 }
1402 }
1403}
1404
1405
1407{
1408 for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
1409 {
1410 CADSTAR_BOARD& board = boardPair.second;
1411 GROUP_ID boardGroup = createUniqueGroupID( wxT( "Board" ) );
1412
1414 getLineStyle( board.LineCodeID ), wxString::Format( wxT( "BOARD %s" ), board.ID ),
1415 m_board, boardGroup );
1416
1417 if( !board.GroupID.IsEmpty() )
1418 addToGroup( board.GroupID, getKiCadGroup( boardGroup ) );
1419
1420 //TODO process board attributes when KiCad supports them
1421 }
1422}
1423
1424
1426{
1427 for( std::pair<FIGURE_ID, FIGURE> figPair : Layout.Figures )
1428 {
1429 FIGURE& fig = figPair.second;
1430
1431 for( const PCB_LAYER_ID& layer : getKiCadLayerSet( fig.LayerID ).Seq() )
1432 {
1434 wxString::Format( wxT( "FIGURE %s" ), fig.ID ), m_board, fig.GroupID );
1435 }
1436
1437 //TODO process "swaprule" (doesn't seem to apply to Layout Figures?)
1438 //TODO process re-use block when KiCad Supports it
1439 //TODO process attributes when KiCad Supports attributes in figures
1440 }
1441}
1442
1443
1445{
1446 for( std::pair<TEXT_ID, TEXT> txtPair : Layout.Texts )
1447 {
1448 TEXT& csTxt = txtPair.second;
1449 drawCadstarText( csTxt, m_board );
1450 }
1451}
1452
1453
1455{
1456 for( std::pair<DIMENSION_ID, DIMENSION> dimPair : Layout.Dimensions )
1457 {
1458 DIMENSION& csDim = dimPair.second;
1459
1460 switch( csDim.Type )
1461 {
1462 case DIMENSION::TYPE::LINEARDIM:
1463 switch( csDim.Subtype )
1464 {
1465 case DIMENSION::SUBTYPE::ANGLED:
1466 reportWarning( wxString::Format( _( "Dimension ID %s is an angled dimension, which has no KiCad "
1467 "equivalent. An aligned dimension was loaded instead." ),
1468 csDim.ID ) );
1470 case DIMENSION::SUBTYPE::DIRECT:
1471 case DIMENSION::SUBTYPE::ORTHOGONAL:
1472 {
1473 if( csDim.Line.Style == DIMENSION::LINE::STYLE::EXTERNAL )
1474 {
1475 reportWarning( wxString::Format( _( "Dimension ID %s has 'External' style in CADSTAR. External "
1476 "dimension styles are not yet supported in KiCad. The "
1477 "dimension object was imported with an internal dimension "
1478 "style instead." ),
1479 csDim.ID ) );
1480 }
1481
1482 PCB_DIM_ALIGNED* dimension = nullptr;
1483
1484 if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1485 {
1486 dimension = new PCB_DIM_ORTHOGONAL( m_board );
1487 PCB_DIM_ORTHOGONAL* orDim = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1488
1489 if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1491 else
1493 }
1494 else
1495 {
1496 dimension = new PCB_DIM_ALIGNED( m_board, PCB_DIM_ALIGNED_T );
1497 }
1498
1499 m_board->Add( dimension, ADD_MODE::APPEND );
1500 applyDimensionSettings( csDim, dimension );
1501
1503
1504 // Calculate height:
1505 VECTOR2I crossbarStart = getKiCadPoint( csDim.Line.Start );
1506 VECTOR2I crossbarEnd = getKiCadPoint( csDim.Line.End );
1507 VECTOR2I crossbarVector = crossbarEnd - crossbarStart;
1508 VECTOR2I heightVector = crossbarStart - dimension->GetStart();
1509 double height = 0.0;
1510
1511 if( csDim.Subtype == DIMENSION::SUBTYPE::ORTHOGONAL )
1512 {
1513 if( csDim.ExtensionLineParams.Start.x == csDim.Line.Start.x )
1514 height = heightVector.y;
1515 else
1516 height = heightVector.x;
1517 }
1518 else
1519 {
1520 EDA_ANGLE angle( crossbarVector );
1521 angle += ANGLE_90;
1522 height = heightVector.x * angle.Cos() + heightVector.y * angle.Sin();
1523 }
1524
1525 dimension->SetHeight( height );
1526 }
1527 break;
1528
1529 default:
1530 // Radius and diameter dimensions are LEADERDIM (even if not actually leader)
1531 // Angular dimensions are always ANGLEDIM
1532 reportError( wxString::Format( _( "Unexpected Dimension type (ID %s). This was not imported." ),
1533 csDim.ID ) );
1534 continue;
1535 }
1536 break;
1537
1538 case DIMENSION::TYPE::LEADERDIM:
1539 //TODO: update import when KiCad supports radius and diameter dimensions
1540
1541 if( csDim.Line.Style == DIMENSION::LINE::STYLE::INTERNAL )
1542 {
1543 // "internal" is a simple double sided arrow from start to end (no extension lines)
1545 m_board->Add( dimension, ADD_MODE::APPEND );
1546 applyDimensionSettings( csDim, dimension );
1547
1548 // Lets set again start/end:
1549 dimension->SetStart( getKiCadPoint( csDim.Line.Start ) );
1550 dimension->SetEnd( getKiCadPoint( csDim.Line.End ) );
1551
1552 // Do not use any extension lines:
1553 dimension->SetExtensionOffset( 0 );
1554 dimension->SetExtensionHeight( 0 );
1555 dimension->SetHeight( 0 );
1556 }
1557 else
1558 {
1559 // "external" is a "leader" style dimension
1560 PCB_DIM_LEADER* leaderDim = new PCB_DIM_LEADER( m_board );
1561 m_board->Add( leaderDim, ADD_MODE::APPEND );
1562
1563 applyDimensionSettings( csDim, leaderDim );
1564 leaderDim->SetStart( getKiCadPoint( csDim.Line.End ) );
1565
1566 /*
1567 * In CADSTAR, the resulting shape orientation of the leader dimension depends on
1568 * on the positions of the #Start (S) and #End (E) points as shown below. In the
1569 * diagrams below, the leader angle (angRad) is represented by HEV
1570 *
1571 * Orientation 1: (orientX = -1, | Orientation 2: (orientX = 1,
1572 * orientY = 1) | orientY = 1)
1573 * |
1574 * --------V | V----------
1575 * \ | /
1576 * \ | /
1577 * H _E/ | \E_ H
1578 * |
1579 * S | S
1580 * |
1581 *
1582 * Orientation 3: (orientX = -1, | Orientation 4: (orientX = 1,
1583 * orientY = -1) | orientY = -1)
1584 * |
1585 * S | S
1586 * _ | _
1587 * H E\ | /E H
1588 * / | \
1589 * / | \
1590 * ----------V | V-----------
1591 * |
1592 *
1593 * Corner cases:
1594 *
1595 * It is not possible to generate a leader object with start and end point being
1596 * identical. Assume Orientation 2 if start and end points are identical.
1597 *
1598 * If start and end points are aligned vertically (i.e. S.x == E.x):
1599 * - If E.y > S.y - Orientation 2
1600 * - If E.y < S.y - Orientation 4
1601 *
1602 * If start and end points are aligned horitontally (i.e. S.y == E.y):
1603 * - If E.x > S.x - Orientation 2
1604 * - If E.x < S.x - Orientation 1
1605 */
1606 double angRad = DEG2RAD( getAngleDegrees( csDim.Line.LeaderAngle ) );
1607
1608 double orientX = 1;
1609 double orientY = 1;
1610
1611 if( csDim.Line.End.x >= csDim.Line.Start.x )
1612 {
1613 if( csDim.Line.End.y >= csDim.Line.Start.y )
1614 {
1615 //Orientation 2
1616 orientX = 1;
1617 orientY = 1;
1618 }
1619 else
1620 {
1621 //Orientation 4
1622 orientX = 1;
1623 orientY = -1;
1624 }
1625 }
1626 else
1627 {
1628 if( csDim.Line.End.y >= csDim.Line.Start.y )
1629 {
1630 //Orientation 1
1631 orientX = -1;
1632 orientY = 1;
1633 }
1634 else
1635 {
1636 //Orientation 3
1637 orientX = -1;
1638 orientY = -1;
1639 }
1640 }
1641
1642 VECTOR2I endOffset( csDim.Line.LeaderLineLength * cos( angRad ) * orientX,
1643 csDim.Line.LeaderLineLength * sin( angRad ) * orientY );
1644
1645 VECTOR2I endPoint = VECTOR2I( csDim.Line.End ) + endOffset;
1646 VECTOR2I txtPoint( endPoint.x + ( csDim.Line.LeaderLineExtensionLength * orientX ),
1647 endPoint.y );
1648
1649 leaderDim->SetEnd( getKiCadPoint( endPoint ) );
1650 leaderDim->SetTextPos( getKiCadPoint( txtPoint ) );
1651 leaderDim->SetOverrideText( ParseTextFields( csDim.Text.Text, &m_context ) );
1652 leaderDim->SetPrefix( wxEmptyString );
1653 leaderDim->SetSuffix( wxEmptyString );
1655
1656 if( orientX == 1 )
1658 else
1660
1661 leaderDim->SetExtensionOffset( 0 );
1662 }
1663 break;
1664
1665 case DIMENSION::TYPE::ANGLEDIM:
1666 //TODO: update import when KiCad supports angular dimensions
1667 reportError( wxString::Format( _( "Dimension %s is an angular dimension which has no KiCad equivalent. "
1668 "The object was not imported." ),
1669 csDim.ID ) );
1670 break;
1671 }
1672 }
1673}
1674
1675
1677{
1678 for( std::pair<AREA_ID, AREA> areaPair : Layout.Areas )
1679 {
1680 AREA& area = areaPair.second;
1681
1682 if( area.NoVias || area.NoTracks || area.Keepout || area.Routing )
1683 {
1684 int lineThickness = 0; // CADSTAR areas only use the line width for display purpose
1685 ZONE* zone = getZoneFromCadstarShape( area.Shape, lineThickness, m_board );
1686
1687 m_board->Add( zone, ADD_MODE::APPEND );
1688
1689 if( isLayerSet( area.LayerID ) )
1690 zone->SetLayerSet( getKiCadLayerSet( area.LayerID ) );
1691 else
1692 zone->SetLayer( getKiCadLayer( area.LayerID ) );
1693
1694 zone->SetIsRuleArea( true ); //import all CADSTAR areas as Keepout zones
1695 zone->SetDoNotAllowPads( false ); //no CADSTAR equivalent
1696 zone->SetZoneName( area.Name );
1697
1698 zone->SetDoNotAllowFootprints( area.Keepout );
1699
1700 zone->SetDoNotAllowTracks( area.NoTracks );
1701 zone->SetDoNotAllowZoneFills( area.NoTracks );
1702
1703 zone->SetDoNotAllowVias( area.NoVias );
1704
1705 if( area.Placement )
1706 {
1707 reportWarning( wxString::Format( _( "The CADSTAR area '%s' is marked as a placement area in "
1708 "CADSTAR. Placement areas are not supported in KiCad. Only "
1709 "the supported elements for the area were imported." ),
1710 area.Name ) );
1711 }
1712 }
1713 else
1714 {
1715 reportError( wxString::Format( _( "The CADSTAR area '%s' does not have a KiCad equivalent. Pure "
1716 "Placement areas are not supported." ),
1717 area.Name ) );
1718 }
1719
1720 //todo Process area.AreaHeight when KiCad supports 3D design rules
1721 //TODO process attributes
1722 //TODO process addition to a group
1723 //TODO process "swaprule"
1724 //TODO process re-use block
1725 }
1726}
1727
1728
1730{
1731 for( std::pair<COMPONENT_ID, COMPONENT> compPair : Layout.Components )
1732 {
1733 COMPONENT& comp = compPair.second;
1734
1735 if( !comp.VariantID.empty() && comp.VariantParentComponentID != comp.ID )
1736 continue; // Only load master Variant
1737
1738 auto fpIter = m_libraryMap.find( comp.SymdefID );
1739
1740 if( fpIter == m_libraryMap.end() )
1741 {
1742 THROW_IO_ERRORF( _( "Unable to find component '%s' in the library (Symdef ID: '%s')" ),
1743 comp.Name,
1744 comp.SymdefID );
1745 }
1746
1747 FOOTPRINT* libFootprint = fpIter->second;
1748
1749 // Use Duplicate() to ensure unique KIID for all objects
1750 FOOTPRINT* footprint = static_cast<FOOTPRINT*>( libFootprint->Duplicate( IGNORE_PARENT_GROUP ) );
1751
1752 m_board->Add( footprint, ADD_MODE::APPEND );
1753
1754 // First lets fix the pad names on the footprint.
1755 // CADSTAR defines the pad name in the PART definition and the SYMDEF (i.e. the PCB
1756 // footprint definition) uses a numerical sequence. COMP is the only object that has
1757 // visibility of both the SYMDEF and PART.
1758 if( Parts.PartDefinitions.find( comp.PartID ) != Parts.PartDefinitions.end() )
1759 {
1760 PART part = Parts.PartDefinitions.at( comp.PartID );
1761
1762 // Only do this when the number of pins in the part definition equals the number of
1763 // pads in the footprint.
1764 if( part.Definition.Pins.size() == footprint->Pads().size() )
1765 {
1766 for( std::pair<PART_DEFINITION_PIN_ID, PART::DEFINITION::PIN> pinPair :
1767 part.Definition.Pins )
1768 {
1769 PART::DEFINITION::PIN pin = pinPair.second;
1770 wxString pinName = pin.Name;
1771
1772 if( pinName.empty() )
1773 pinName = pin.Identifier;
1774
1775 if( pinName.empty() )
1776 pinName = wxString::Format( wxT( "%ld" ), pin.ID );
1777
1778 getPadReference( footprint, pin.ID )->SetNumber( pinName );
1779 }
1780 }
1781 }
1782
1783 //Override pads with pad exceptions
1784 if( comp.PadExceptions.size() > 0 )
1785 {
1786 SYMDEF_PCB fpLibEntry = Library.ComponentDefinitions.at( comp.SymdefID );
1787
1788 for( std::pair<PAD_ID, PADEXCEPTION> padPair : comp.PadExceptions )
1789 {
1790 PADEXCEPTION& padEx = padPair.second;
1791 COMPONENT_PAD csPad = fpLibEntry.ComponentPads.at( padPair.first );
1792
1793 // Reset the pad to be around 0,0
1794 csPad.Position -= fpLibEntry.Origin;
1795 csPad.Position += m_designCenter;
1796
1797 if( !padEx.PadCode.IsEmpty() )
1798 csPad.PadCodeID = padEx.PadCode;
1799
1800 if( padEx.OverrideExits )
1801 csPad.Exits = padEx.Exits;
1802
1803 if( padEx.OverrideOrientation )
1804 csPad.OrientAngle = padEx.OrientAngle;
1805
1806 if( padEx.OverrideSide )
1807 csPad.Side = padEx.Side;
1808
1809 // Find the pad in the footprint definition
1810 PAD* kiPad = getPadReference( footprint, padEx.ID );
1811
1812 wxString padNumber = kiPad->GetNumber();
1813
1814 delete kiPad;
1815
1816 if( ( kiPad = getKiCadPad( csPad, footprint ) ) )
1817 {
1818 kiPad->SetNumber( padNumber );
1819
1820 // Change the pointer in the footprint to the newly created pad
1821 getPadReference( footprint, padEx.ID ) = kiPad;
1822 }
1823 }
1824 }
1825
1826 //set to empty string to avoid duplication when loading attributes:
1827 footprint->SetValue( wxEmptyString );
1828
1829 footprint->SetPosition( getKiCadPoint( comp.Origin ) );
1830 footprint->SetOrientation( getAngle( comp.OrientAngle ) );
1831 footprint->SetReference( comp.Name );
1832
1833 if( comp.Mirror )
1834 {
1835 EDA_ANGLE mirroredAngle = - getAngle( comp.OrientAngle );
1836 mirroredAngle.Normalize180();
1837 footprint->SetOrientation( mirroredAngle );
1838 footprint->Flip( getKiCadPoint( comp.Origin ), FLIP_DIRECTION::LEFT_RIGHT );
1839 }
1840
1841 loadComponentAttributes( comp, footprint );
1842
1843 if( !comp.PartID.IsEmpty() && comp.PartID != wxT( "NO_PART" ) )
1844 footprint->SetLibDescription( getPart( comp.PartID ).Definition.Name );
1845
1846 m_componentMap.insert( { comp.ID, footprint } );
1847 }
1848}
1849
1850
1852{
1853 //No KiCad equivalent. Loaded as graphic and text elements instead
1854
1855 for( std::pair<DOCUMENTATION_SYMBOL_ID, DOCUMENTATION_SYMBOL> docPair : Layout.DocumentationSymbols )
1856 {
1857 DOCUMENTATION_SYMBOL& docSymInstance = docPair.second;
1858
1859
1860 auto docSymIter = Library.ComponentDefinitions.find( docSymInstance.SymdefID );
1861
1862 if( docSymIter == Library.ComponentDefinitions.end() )
1863 {
1864 THROW_IO_ERRORF( _( "Unable to find documentation symbol in the library (Symdef ID: '%s')" ),
1865 docSymInstance.SymdefID );
1866 }
1867
1868 SYMDEF_PCB& docSymDefinition = ( *docSymIter ).second;
1869 VECTOR2I moveVector = getKiCadPoint( docSymInstance.Origin ) - getKiCadPoint( docSymDefinition.Origin );
1870 double rotationAngle = getAngleTenthDegree( docSymInstance.OrientAngle );
1871 double scalingFactor = (double) docSymInstance.ScaleRatioNumerator
1872 / (double) docSymInstance.ScaleRatioDenominator;
1873 VECTOR2I centreOfTransform = getKiCadPoint( docSymDefinition.Origin );
1874 bool mirrorInvert = docSymInstance.Mirror;
1875
1876 //create a group to store the items in
1877 wxString groupName = docSymDefinition.ReferenceName;
1878
1879 if( !docSymDefinition.Alternate.IsEmpty() )
1880 groupName += wxT( " (" ) + docSymDefinition.Alternate + wxT( ")" );
1881
1882 GROUP_ID groupID = createUniqueGroupID( groupName );
1883
1884 LSEQ layers = getKiCadLayerSet( docSymInstance.LayerID ).Seq();
1885
1886 for( PCB_LAYER_ID layer : layers )
1887 {
1888 for( std::pair<FIGURE_ID, FIGURE> figPair : docSymDefinition.Figures )
1889 {
1890 FIGURE fig = figPair.second;
1892 wxString::Format( wxT( "DOCUMENTATION SYMBOL %s, FIGURE %s" ),
1893 docSymDefinition.ReferenceName,
1894 fig.ID ),
1895 m_board, groupID, moveVector, rotationAngle, scalingFactor,
1896 centreOfTransform, mirrorInvert );
1897 }
1898 }
1899
1900 for( std::pair<TEXT_ID, TEXT> textPair : docSymDefinition.Texts )
1901 {
1902 TEXT txt = textPair.second;
1903 drawCadstarText( txt, m_board, groupID, docSymInstance.LayerID, moveVector, rotationAngle,
1904 scalingFactor, centreOfTransform, mirrorInvert );
1905 }
1906 }
1907}
1908
1909
1911{
1912 for( std::pair<TEMPLATE_ID, TEMPLATE> tempPair : Layout.Templates )
1913 {
1914 TEMPLATE& csTemplate = tempPair.second;
1915
1916 int zonelinethickness = 0; // The line thickness in CADSTAR is only for display purposes but
1917 // does not affect the end copper result.
1918 ZONE* zone = getZoneFromCadstarShape( csTemplate.Shape, zonelinethickness, m_board );
1919
1920 m_board->Add( zone, ADD_MODE::APPEND );
1921
1922 zone->SetZoneName( csTemplate.Name );
1923 zone->SetLayer( getKiCadLayer( csTemplate.LayerID ) );
1924 zone->SetAssignedPriority( 1 ); // initially 1, we will increase in calculateZonePriorities
1925
1926 if( !( csTemplate.NetID.IsEmpty() || csTemplate.NetID == wxT( "NONE" ) ) )
1927 zone->SetNet( getKiCadNet( csTemplate.NetID ) );
1928
1929 if( csTemplate.Pouring.AllowInNoRouting )
1930 {
1931 reportWarning( wxString::Format( _( "The CADSTAR template '%s' has the setting 'Allow in No Routing "
1932 "Areas' enabled. This setting has no KiCad equivalent, so it has "
1933 "been ignored." ),
1934 csTemplate.Name ) );
1935 }
1936
1937 if( csTemplate.Pouring.BoxIsolatedPins )
1938 {
1939 reportWarning( wxString::Format( _( "The CADSTAR template '%s' has the setting 'Box Isolated Pins' "
1940 "enabled. This setting has no KiCad equivalent, so it has been "
1941 "ignored." ),
1942 csTemplate.Name ) );
1943 }
1944
1945 if( csTemplate.Pouring.AutomaticRepour )
1946 {
1947 reportWarning( wxString::Format( _( "The CADSTAR template '%s' has the setting 'Automatic Repour' "
1948 "enabled. This setting has no KiCad equivalent, so it has been "
1949 "ignored." ),
1950 csTemplate.Name ) );
1951 }
1952
1953 // Sliver width has different behaviour to KiCad Zone's minimum thickness
1954 // In Cadstar 'Sliver width' has to be greater than the Copper thickness, whereas in
1955 // Kicad it is the opposite.
1956 if( csTemplate.Pouring.SliverWidth != 0 )
1957 {
1958 reportWarning( wxString::Format( _( "The CADSTAR template '%s' has a non-zero value defined for the "
1959 "'Sliver Width' setting. There is no KiCad equivalent for "
1960 "this, so this setting was ignored." ),
1961 csTemplate.Name ) );
1962 }
1963
1964
1965 if( csTemplate.Pouring.MinIsolatedCopper != csTemplate.Pouring.MinDisjointCopper )
1966 {
1967 reportWarning( wxString::Format( _( "The CADSTAR template '%s' has different settings for "
1968 "'Retain Poured Copper - Disjoint' and "
1969 "'Retain Poured Copper - Isolated'. KiCad does not distinguish "
1970 "between these two settings. The setting for disjoint copper "
1971 "has been applied as the minimum island area of the KiCad Zone." ),
1972 csTemplate.Name ) );
1973 }
1974
1975 long long minIslandArea = -1;
1976
1977 if( csTemplate.Pouring.MinDisjointCopper != UNDEFINED_VALUE )
1978 {
1979 minIslandArea = (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper )
1980 * (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper );
1981
1983 }
1984 else
1985 {
1987 }
1988
1989 zone->SetMinIslandArea( minIslandArea );
1990
1991 // In cadstar zone clearance is in addition to the global clearance.
1992 // TODO: need to create custom rules for individual items: zone to pad, zone to track, etc.
1994 clearance += m_board->GetDesignSettings().m_MinClearance;
1995
1997
1998 COPPERCODE pouringCopperCode = getCopperCode( csTemplate.Pouring.CopperCodeID );
1999 int minThickness = getKiCadLength( pouringCopperCode.CopperWidth );
2000 zone->SetMinThickness( minThickness );
2001
2002 if( csTemplate.Pouring.FillType == TEMPLATE::POURING::COPPER_FILL_TYPE::HATCHED )
2003 {
2005 zone->SetHatchGap( getKiCadHatchCodeGap( csTemplate.Pouring.HatchCodeID ) );
2008 }
2009 else
2010 {
2012 }
2013
2014 if( csTemplate.Pouring.ThermalReliefOnPads != csTemplate.Pouring.ThermalReliefOnVias
2015 || csTemplate.Pouring.ThermalReliefPadsAngle
2016 != csTemplate.Pouring.ThermalReliefViasAngle )
2017 {
2018 reportWarning( wxString::Format( _( "The CADSTAR template '%s' has different settings for thermal relief "
2019 "in pads and vias. KiCad only supports one single setting for both. "
2020 "The setting for pads has been applied." ),
2021 csTemplate.Name ) );
2022 }
2023
2024 COPPERCODE reliefCopperCode = getCopperCode( csTemplate.Pouring.ReliefCopperCodeID );
2025 int spokeWidth = getKiCadLength( reliefCopperCode.CopperWidth );
2026 int reliefWidth = getKiCadLength( csTemplate.Pouring.ClearanceWidth );
2027
2028 // Cadstar supports having a spoke width thinner than the minimum thickness of the zone, but
2029 // this is not permitted in KiCad. We load it as solid fill instead.
2030 if( csTemplate.Pouring.ThermalReliefOnPads && reliefWidth > 0 )
2031 {
2032 if( spokeWidth < minThickness )
2033 {
2034 reportWarning( wxString::Format( _( "The CADSTAR template '%s' has thermal reliefs in the original "
2035 "design but the spoke width (%.2f mm) is thinner " //format:allow
2036 "than the minimum thickness of the zone (%.2f mm). " //format:allow
2037 "KiCad requires the minimum thickness of the zone to be preserved. "
2038 "Therefore the minimum thickness has been applied as the new "
2039 "spoke width and will be applied next time the zones are filled." ),
2040 csTemplate.Name, (double) getKiCadLength( spokeWidth ) / 1E6,
2041 (double) getKiCadLength( minThickness ) / 1E6 ) );
2042
2043 spokeWidth = minThickness;
2044 }
2045
2046 zone->SetThermalReliefGap( reliefWidth );
2047 zone->SetThermalReliefSpokeWidth( spokeWidth );
2049 }
2050 else
2051 {
2053 }
2054
2055 m_zonesMap.insert( { csTemplate.ID, zone } );
2056 }
2057
2058 //Now create power plane layers:
2059 for( const LAYER_ID& layer : m_powerPlaneLayers )
2060 {
2061 wxASSERT( Assignments.Layerdefs.Layers.find( layer ) != Assignments.Layerdefs.Layers.end() );
2062
2063 //The net name will equal the layer name
2064 wxString powerPlaneLayerName = Assignments.Layerdefs.Layers.at( layer ).Name;
2065 NET_ID netid = wxEmptyString;
2066
2067 for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
2068 {
2069 NET_PCB net = netPair.second;
2070
2071 if( net.Name == powerPlaneLayerName )
2072 {
2073 netid = net.ID;
2074 break;
2075 }
2076 }
2077
2078 if( netid.IsEmpty() )
2079 {
2080 reportError( wxString::Format( _( "The CADSTAR layer '%s' is defined as a power plane layer. "
2081 "However no net with such name exists. The layer has been loaded "
2082 "but no copper zone was created." ),
2083 powerPlaneLayerName ) );
2084 }
2085 else
2086 {
2087 for( std::pair<BOARD_ID, CADSTAR_BOARD> boardPair : Layout.Boards )
2088 {
2089 //create a zone in each board shape
2090 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
2091 CADSTAR_BOARD& board = boardPair.second;
2092 int defaultLineThicknesss = bds.GetLineThickness( PCB_LAYER_ID::Edge_Cuts );
2093 ZONE* zone = getZoneFromCadstarShape( board.Shape, defaultLineThicknesss, m_board );
2094
2095 m_board->Add( zone, ADD_MODE::APPEND );
2096
2097 zone->SetZoneName( powerPlaneLayerName );
2098 zone->SetLayer( getKiCadLayer( layer ) );
2101 zone->SetMinIslandArea( -1 );
2102 zone->SetAssignedPriority( 0 ); // Priority always 0 (lowest priority) for implied power planes.
2103 zone->SetNet( getKiCadNet( netid ) );
2104 }
2105 }
2106 }
2107}
2108
2109
2111{
2112 for( std::pair<COPPER_ID, COPPER> copPair : Layout.Coppers )
2113 {
2114 COPPER& csCopper = copPair.second;
2115 int copperWidth = getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth );
2116
2117
2118 checkPoint();
2119
2120 if( !csCopper.PouredTemplateID.IsEmpty() )
2121 {
2122 ZONE* pouredZone = m_zonesMap.at( csCopper.PouredTemplateID );
2123 SHAPE_POLY_SET fill;
2124
2125 if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE )
2126 {
2127 // This is usually for themal reliefs. They are lines of copper with a thickness.
2128 // We convert them to an oval in most cases, but handle also the possibility of
2129 // encountering arcs in here.
2130
2131 std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( csCopper.Shape.Vertices );
2132
2133 for( PCB_SHAPE* shape : outlineShapes )
2134 {
2135 SHAPE_POLY_SET poly;
2136
2137 if( shape->GetShape() == SHAPE_T::ARC )
2138 {
2139 TransformArcToPolygon( poly, shape->GetStart(), shape->GetArcMid(), shape->GetEnd(),
2140 copperWidth, ARC_HIGH_DEF, ERROR_LOC::ERROR_INSIDE );
2141 }
2142 else
2143 {
2144 TransformOvalToPolygon( poly, shape->GetStart(), shape->GetEnd(), copperWidth,
2146 }
2147
2148 poly.ClearArcs();
2149 fill.BooleanAdd( poly );
2150 }
2151
2152 }
2153 else
2154 {
2155 fill = getPolySetFromCadstarShape( csCopper.Shape, -1 );
2156 fill.ClearArcs();
2158 }
2159
2160 if( pouredZone->HasFilledPolysForLayer( getKiCadLayer( csCopper.LayerID ) ) )
2161 fill.BooleanAdd( *pouredZone->GetFill( getKiCadLayer( csCopper.LayerID ) ) );
2162
2163 fill.Fracture();
2164
2165 pouredZone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fill );
2166 pouredZone->SetIsFilled( true );
2167 pouredZone->SetNeedRefill( false );
2168 continue;
2169 }
2170
2171 // For now we are going to load coppers to a KiCad zone however this isn't perfect
2172 //TODO: Load onto a graphical polygon with a net
2173
2174 if( !m_doneCopperWarning )
2175 {
2176 reportWarning( _( "The CADSTAR design contains COPPER elements, which have no direct KiCad "
2177 "equivalent. These have been imported as a KiCad Zone if solid or hatch "
2178 "filled, or as a KiCad Track if the shape was an unfilled outline (open or "
2179 "closed)." ) );
2180 m_doneCopperWarning = true;
2181 }
2182
2183
2184 if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE
2185 || csCopper.Shape.Type == SHAPE_TYPE::OUTLINE )
2186 {
2187 std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( csCopper.Shape.Vertices );
2188
2189 std::vector<PCB_TRACK*> outlineTracks = makeTracksFromShapes( outlineShapes, m_board,
2190 getKiCadNet( csCopper.NetRef.NetID ),
2191 getKiCadLayer( csCopper.LayerID ), copperWidth );
2192
2193 //cleanup
2194 for( PCB_SHAPE* shape : outlineShapes )
2195 delete shape;
2196
2197 for( const CUTOUT& cutout : csCopper.Shape.Cutouts )
2198 {
2199 std::vector<PCB_SHAPE*> cutoutShapes = getShapesFromVertices( cutout.Vertices );
2200
2201 std::vector<PCB_TRACK*> cutoutTracks = makeTracksFromShapes( cutoutShapes, m_board,
2202 getKiCadNet( csCopper.NetRef.NetID ),
2203 getKiCadLayer( csCopper.LayerID ), copperWidth );
2204
2205 //cleanup
2206 for( PCB_SHAPE* shape : cutoutShapes )
2207 delete shape;
2208 }
2209 }
2210 else
2211 {
2212 ZONE* zone = getZoneFromCadstarShape( csCopper.Shape, copperWidth, m_board );
2213
2214 m_board->Add( zone, ADD_MODE::APPEND );
2215
2216 zone->SetZoneName( csCopper.ID );
2217 zone->SetLayer( getKiCadLayer( csCopper.LayerID ) );
2219
2220 if( csCopper.Shape.Type == SHAPE_TYPE::HATCHED )
2221 {
2226 }
2227 else
2228 {
2230 }
2231
2234 zone->SetNet( getKiCadNet( csCopper.NetRef.NetID ) );
2235 zone->SetAssignedPriority( m_zonesMap.size() + 1 ); // Highest priority (always fill first)
2236
2237 SHAPE_POLY_SET fill( *zone->Outline() );
2238 fill.Fracture();
2239
2240 zone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fill );
2241 }
2242 }
2243}
2244
2245
2247{
2248 for( std::pair<NET_ID, NET_PCB> netPair : Layout.Nets )
2249 {
2250 NET_PCB net = netPair.second;
2251 wxString netnameForErrorReporting = net.Name;
2252
2253 std::map<NETELEMENT_ID, long> netelementSizes;
2254
2255 if( netnameForErrorReporting.IsEmpty() )
2256 netnameForErrorReporting = wxString::Format( wxT( "$%ld" ), net.SignalNum );
2257
2258 for( std::pair<NETELEMENT_ID, NET_PCB::VIA> viaPair : net.Vias )
2259 {
2260 NET_PCB::VIA via = viaPair.second;
2261
2262 // viasize is used for calculating route offset (as done in CADSTAR post processor)
2263 int viaSize = loadNetVia( net.ID, via );
2264 netelementSizes.insert( { viaPair.first, viaSize } );
2265 }
2266
2267 for( std::pair<NETELEMENT_ID, NET_PCB::PIN> pinPair : net.Pins )
2268 {
2269 NET_PCB::PIN pin = pinPair.second;
2270 FOOTPRINT* footprint = getFootprintFromCadstarID( pin.ComponentID );
2271
2272 if( footprint == nullptr )
2273 {
2274 reportWarning( wxString::Format( _( "The net '%s' references component ID '%s' which does not exist. "
2275 "This has been ignored." ),
2276 netnameForErrorReporting, pin.ComponentID ) );
2277 }
2278 else if( pin.PadID <= 0 || static_cast<size_t>( pin.PadID ) > footprint->Pads().size() )
2279 {
2280 // Pad IDs are one-based, so the valid range is [1, Pads().size()]. Anything
2281 // else would throw out of getPadReference() and abort the whole import.
2282 reportWarning( wxString::Format( _( "The net '%s' references non-existent pad index '%ld' in "
2283 "component '%s'. This has been ignored." ),
2284 netnameForErrorReporting,
2285 pin.PadID,
2286 footprint->GetReference() ) );
2287 }
2288 else
2289 {
2290 // The below works because we have added the pads in the correct order to the
2291 // footprint and the PAD_ID in Cadstar is a sequential, numerical ID
2292 PAD* pad = getPadReference( footprint, pin.PadID );
2293 pad->SetNet( getKiCadNet( net.ID ) );
2294
2295 // also set the net to any copper pads (i.e. copper elements that we have imported
2296 // as pads instead:
2297 SYMDEF_ID symdefid = Layout.Components.at( pin.ComponentID ).SymdefID;
2298
2299 if( m_librarycopperpads.find( symdefid ) != m_librarycopperpads.end() )
2300 {
2301 ASSOCIATED_COPPER_PADS assocPads = m_librarycopperpads.at( symdefid );
2302
2303 if( assocPads.find( pin.PadID ) != assocPads.end() )
2304 {
2305 for( PAD_ID copperPadID : assocPads.at( pin.PadID ) )
2306 {
2307 PAD* copperpad = getPadReference( footprint, copperPadID );
2308 copperpad->SetNet( getKiCadNet( net.ID ) );
2309 }
2310 }
2311 }
2312
2313 // padsize is used for calculating route offset (as done in CADSTAR post processor)
2314 int padsize = std::min( pad->GetSizeX(), pad->GetSizeY() );
2315 netelementSizes.insert( { pinPair.first, padsize } );
2316 }
2317 }
2318
2319 // For junction points we need to find out the biggest size of the other routes connecting
2320 // at the junction in order to correctly apply the same "route offset" operation that the
2321 // CADSTAR post processor applies when generating Manufacturing output. The only exception
2322 // is if there is just a single route at the junction point, we use that route width
2323 // Optimal width of a route code, falling back to the default netclass track width when the
2324 // route code is missing or specifies no usable width. Unlike getRouteCode() this tolerates
2325 // an absent route code silently, as expected for CADSTAR Revision 7 routes that omit
2326 // ROUTEWIDTH, and never yields a non-positive track width.
2327 auto getRouteCodeWidth =
2328 [&]( const ROUTECODE_ID& aRouteCodeID ) -> long
2329 {
2330 auto rcIt = Assignments.Codedefs.RouteCodes.find( aRouteCodeID );
2331
2332 if( rcIt != Assignments.Codedefs.RouteCodes.end() && rcIt->second.OptimalWidth > 0 )
2333 return rcIt->second.OptimalWidth;
2334
2335 long defaultWidth =
2336 m_board->GetDesignSettings().m_NetSettings->GetDefaultNetclass()->GetTrackWidth();
2337
2338 return defaultWidth / KiCadUnitMultiplier;
2339 };
2340
2341 // Resolve a vertex width, falling back to the connection's route code when the
2342 // ROUTEWIDTH node was omitted (CADSTAR Revision 7 format).
2343 auto getVertexWidth =
2344 [&]( const NET_PCB::ROUTE_VERTEX& aVertex, const ROUTECODE_ID& aRouteCodeID ) -> long
2345 {
2346 if( aVertex.RouteWidthIsExplicit )
2347 return aVertex.RouteWidth;
2348
2349 return getRouteCodeWidth( aRouteCodeID );
2350 };
2351
2352 auto getJunctionSize =
2353 [&]( const NETELEMENT_ID& aJptNetElemId, const NET_PCB::CONNECTION_PCB& aConnectionToIgnore ) -> int
2354 {
2355 int jptsize = std::numeric_limits<int>::max();
2356
2357 for( NET_PCB::CONNECTION_PCB connection : net.Connections )
2358 {
2359 if( connection.Route.RouteVertices.size() == 0 )
2360 continue;
2361
2362 if( connection.StartNode == aConnectionToIgnore.StartNode
2363 && connection.EndNode == aConnectionToIgnore.EndNode )
2364 {
2365 continue;
2366 }
2367
2368 if( connection.StartNode == aJptNetElemId )
2369 {
2370 int s = getKiCadLength( getVertexWidth( connection.Route.RouteVertices.front(),
2371 connection.RouteCodeID ) );
2372 jptsize = std::max( jptsize, s );
2373 }
2374 else if( connection.EndNode == aJptNetElemId )
2375 {
2376 int s = getKiCadLength( getVertexWidth( connection.Route.RouteVertices.back(),
2377 connection.RouteCodeID ) );
2378 jptsize = std::max( jptsize, s );
2379 }
2380 }
2381
2382 if( jptsize == std::numeric_limits<int>::max()
2383 && !aConnectionToIgnore.Route.RouteVertices.empty() )
2384 {
2385 // aConnectionToIgnore is actually the only one that has a route, so lets use that
2386 // to determine junction size
2387 NET_PCB::ROUTE_VERTEX vertex = aConnectionToIgnore.Route.RouteVertices.front();
2388
2389 if( aConnectionToIgnore.EndNode == aJptNetElemId )
2390 vertex = aConnectionToIgnore.Route.RouteVertices.back();
2391
2392 jptsize = getKiCadLength( getVertexWidth( vertex, aConnectionToIgnore.RouteCodeID ) );
2393 }
2394
2395 return jptsize;
2396 };
2397
2398 for( const NET_PCB::CONNECTION_PCB& connection : net.Connections )
2399 {
2400 int startSize = std::numeric_limits<int>::max();
2401 int endSize = std::numeric_limits<int>::max();
2402
2403 if( netelementSizes.find( connection.StartNode ) != netelementSizes.end() )
2404 startSize = netelementSizes.at( connection.StartNode );
2405 else if( net.Junctions.find( connection.StartNode ) != net.Junctions.end() )
2406 startSize = getJunctionSize( connection.StartNode, connection );
2407
2408 if( netelementSizes.find( connection.EndNode ) != netelementSizes.end() )
2409 endSize = netelementSizes.at( connection.EndNode );
2410 else if( net.Junctions.find( connection.EndNode ) != net.Junctions.end() )
2411 endSize = getJunctionSize( connection.EndNode, connection );
2412
2413 startSize /= KiCadUnitMultiplier;
2414 endSize /= KiCadUnitMultiplier;
2415
2416 if( !connection.Unrouted )
2417 {
2418 loadNetTracks( net.ID, connection.Route, getRouteCodeWidth( connection.RouteCodeID ),
2419 startSize, endSize );
2420 }
2421 }
2422 }
2423}
2424
2425
2427{
2428 auto findAndReplaceTextField =
2429 [&]( TEXT_FIELD_NAME aField, const wxString& aValue )
2430 {
2431 if( m_context.TextFieldToValuesMap.find( aField ) != m_context.TextFieldToValuesMap.end() )
2432 {
2433 if( m_context.TextFieldToValuesMap.at( aField ) != aValue )
2434 {
2435 m_context.TextFieldToValuesMap.at( aField ) = aValue;
2436 m_context.InconsistentTextFields.insert( aField );
2437 return false;
2438 }
2439 }
2440 else
2441 {
2442 m_context.TextFieldToValuesMap.insert( { aField, aValue } );
2443 }
2444
2445 return true;
2446 };
2447
2448 if( m_project )
2449 {
2450 std::map<wxString, wxString>& txtVars = m_project->GetTextVars();
2451
2452 // Most of the design text fields can be derived from other elements
2453 if( Layout.VariantHierarchy.Variants.size() > 0 )
2454 {
2455 VARIANT loadedVar = Layout.VariantHierarchy.Variants.begin()->second;
2456
2457 findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_NAME, loadedVar.Name );
2458 findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_DESCRIPTION, loadedVar.Description );
2459 }
2460
2461 findAndReplaceTextField( TEXT_FIELD_NAME::DESIGN_TITLE, Header.JobTitle );
2462
2463 for( std::pair<TEXT_FIELD_NAME, wxString> txtvalue : m_context.TextFieldToValuesMap )
2464 {
2465 wxString varName = CADSTAR_TO_KICAD_FIELDS.at( txtvalue.first );
2466 wxString varValue = txtvalue.second;
2467
2468 txtVars.insert( { varName, varValue } );
2469 }
2470
2471 for( std::pair<wxString, wxString> txtvalue : m_context.FilenamesToTextMap )
2472 {
2473 wxString varName = txtvalue.first;
2474 wxString varValue = txtvalue.second;
2475
2476 txtVars.insert( { varName, varValue } );
2477 }
2478 }
2479 else
2480 {
2481 reportError( _( "Text Variables could not be set as there is no project loaded." ) );
2482 }
2483}
2484
2485
2487 FOOTPRINT* aFootprint )
2488{
2489 for( std::pair<ATTRIBUTE_ID, ATTRIBUTE_VALUE> attrPair : aComponent.AttributeValues )
2490 {
2491 ATTRIBUTE_VALUE& attrval = attrPair.second;
2492
2493 if( attrval.HasLocation ) //only import attributes with location. Ignore the rest
2494 addAttribute( attrval.AttributeLocation, attrval.AttributeID, aFootprint, attrval.Value );
2495 }
2496
2497 for( std::pair<ATTRIBUTE_ID, TEXT_LOCATION> textlocPair : aComponent.TextLocations )
2498 {
2499 TEXT_LOCATION& textloc = textlocPair.second;
2500 wxString attrval;
2501
2502 if( textloc.AttributeID == COMPONENT_NAME_ATTRID )
2503 attrval = wxEmptyString; // Designator is loaded separately
2504 else if( textloc.AttributeID == COMPONENT_NAME_2_ATTRID )
2505 attrval = wxT( "${REFERENCE}" );
2506 else if( textloc.AttributeID == PART_NAME_ATTRID )
2507 attrval = getPart( aComponent.PartID ).Name;
2508 else
2509 attrval = getAttributeValue( textloc.AttributeID, aComponent.AttributeValues );
2510
2511 addAttribute( textloc, textloc.AttributeID, aFootprint, attrval );
2512 }
2513}
2514
2515
2517 const NET_PCB::ROUTE& aCadstarRoute,
2518 long aDefaultRouteWidth,
2519 long aStartWidth, long aEndWidth )
2520{
2521 if( aCadstarRoute.RouteVertices.size() == 0 )
2522 return;
2523
2524 std::vector<PCB_SHAPE*> shapes;
2525 std::vector<NET_PCB::ROUTE_VERTEX> routeVertices = aCadstarRoute.RouteVertices;
2526
2527 // Fill in default route width for vertices that don't have explicit widths (CADSTAR Revision 7)
2528 for( NET_PCB::ROUTE_VERTEX& v : routeVertices )
2529 {
2530 if( !v.RouteWidthIsExplicit )
2531 v.RouteWidth = aDefaultRouteWidth;
2532 }
2533
2534 // Add thin route at front so that route offsetting works as expected
2535 if( aStartWidth < routeVertices.front().RouteWidth )
2536 {
2537 NET_PCB::ROUTE_VERTEX newFrontVertex = aCadstarRoute.RouteVertices.front();
2538 newFrontVertex.RouteWidth = aStartWidth;
2539 newFrontVertex.Vertex.End = aCadstarRoute.StartPoint;
2540 routeVertices.insert( routeVertices.begin(), newFrontVertex );
2541 }
2542
2543 // Add thin route at the back if required
2544 if( aEndWidth < routeVertices.back().RouteWidth )
2545 {
2546 NET_PCB::ROUTE_VERTEX newBackVertex = aCadstarRoute.RouteVertices.back();
2547 newBackVertex.RouteWidth = aEndWidth;
2548 routeVertices.push_back( newBackVertex );
2549 }
2550
2551 POINT prevEnd = aCadstarRoute.StartPoint;
2552
2553 for( const NET_PCB::ROUTE_VERTEX& v : routeVertices )
2554 {
2555 PCB_SHAPE* shape = getShapeFromVertex( prevEnd, v.Vertex );
2556 shape->SetLayer( getKiCadLayer( aCadstarRoute.LayerID ) );
2557 shape->SetStroke( STROKE_PARAMS( getKiCadLength( v.RouteWidth ), LINE_STYLE::SOLID ) );
2558 shape->SetLocked( v.Fixed );
2559 shapes.push_back( shape );
2560 prevEnd = v.Vertex.End;
2561
2562 if( !m_doneTearDropWarning && ( v.TeardropAtEnd || v.TeardropAtStart ) )
2563 {
2564 // TODO: load teardrops
2565 reportError( _( "The CADSTAR design contains teardrops. This importer does not yet support them, "
2566 "so the teardrops in the design have been ignored." ) );
2567
2568 m_doneTearDropWarning = true;
2569 }
2570 }
2571
2572 NETINFO_ITEM* net = getKiCadNet( aCadstarNetID );
2573 std::vector<PCB_TRACK*> tracks = makeTracksFromShapes( shapes, m_board, net );
2574
2575 //cleanup
2576 for( PCB_SHAPE* shape : shapes )
2577 delete shape;
2578}
2579
2580
2581int CADSTAR_PCB_ARCHIVE_LOADER::loadNetVia( const NET_ID& aCadstarNetID, const NET_PCB::VIA& aCadstarVia )
2582{
2583 PCB_VIA* via = new PCB_VIA( m_board );
2584 m_board->Add( via, ADD_MODE::APPEND );
2585
2586 VIACODE csViaCode = getViaCode( aCadstarVia.ViaCodeID );
2587 LAYERPAIR csLayerPair = getLayerPair( aCadstarVia.LayerPairID );
2588
2589 via->SetPadstackMode( PADSTACK::MODE::NORMAL );
2590 via->SetPosition( getKiCadPoint( aCadstarVia.Location ) );
2591 via->SetDrill( getKiCadLength( csViaCode.DrillDiameter ) );
2592 via->SetLocked( aCadstarVia.Fixed );
2593
2594 if( csViaCode.Shape.ShapeType != PAD_SHAPE_TYPE::CIRCLE )
2595 {
2596 reportError( wxString::Format( _( "The CADSTAR via code '%s' has different shape from a circle defined. "
2597 "KiCad only supports circular vias so this via type has been changed to "
2598 "be a via with circular shape of %.2f mm diameter." ), //format:allow
2599 csViaCode.Name,
2600 (double) getKiCadLength( csViaCode.Shape.Size ) / 1E6 ) );
2601 }
2602
2603 via->SetWidth( PADSTACK::ALL_LAYERS, getKiCadLength( csViaCode.Shape.Size ) );
2604
2605 // A via code can reassign copper per layer, which KiCad holds as a full-custom padstack
2606 for( const auto& [layer, shape] : csViaCode.Reassigns )
2607 {
2608 PCB_LAYER_ID kiLayer = getKiCadLayer( layer );
2609
2610 if( !IsCopperLayer( kiLayer ) || shape.Size <= 0 )
2611 continue;
2612
2613 via->SetPadstackMode( PADSTACK::MODE::CUSTOM );
2614 via->SetWidth( kiLayer, getKiCadLength( shape.Size ) );
2615 }
2616
2617 bool start_layer_outside = csLayerPair.PhysicalLayerStart == 1
2618 || csLayerPair.PhysicalLayerStart == Assignments.Technology.MaxPhysicalLayer;
2619 bool end_layer_outside = csLayerPair.PhysicalLayerEnd == 1
2620 || csLayerPair.PhysicalLayerEnd == Assignments.Technology.MaxPhysicalLayer;
2621
2622 if( start_layer_outside && end_layer_outside )
2623 via->SetViaType( VIATYPE::THROUGH );
2624 else if( ( !start_layer_outside ) && ( !end_layer_outside ) )
2625 via->SetViaType( VIATYPE::BURIED );
2626 else
2627 via->SetViaType( VIATYPE::BLIND );
2628
2629 via->SetLayerPair( getKiCadCopperLayerID( csLayerPair.PhysicalLayerStart ),
2630 getKiCadCopperLayerID( csLayerPair.PhysicalLayerEnd ) );
2631 via->SetNet( getKiCadNet( aCadstarNetID ) );
2632
2633 return via->GetWidth( PADSTACK::ALL_LAYERS );
2634}
2635
2636
2638 BOARD_ITEM_CONTAINER* aContainer,
2639 const GROUP_ID& aCadstarGroupID,
2640 const LAYER_ID& aCadstarLayerOverride,
2641 const VECTOR2I& aMoveVector,
2642 double aRotationAngle, double aScalingFactor,
2643 const VECTOR2I& aTransformCentre,
2644 bool aMirrorInvert )
2645{
2646 PCB_TEXT* txt = new PCB_TEXT( aContainer );
2647 aContainer->Add( txt );
2648 txt->SetText( aCadstarText.Text );
2649
2650 EDA_ANGLE rotationAngle( aRotationAngle, TENTHS_OF_A_DEGREE_T );
2651 VECTOR2I rotatedTextPos = getKiCadPoint( aCadstarText.Position );
2652 RotatePoint( rotatedTextPos, aTransformCentre, rotationAngle );
2653 rotatedTextPos.x = KiROUND( ( rotatedTextPos.x - aTransformCentre.x ) * aScalingFactor );
2654 rotatedTextPos.y = KiROUND( ( rotatedTextPos.y - aTransformCentre.y ) * aScalingFactor );
2655 rotatedTextPos += aTransformCentre;
2656 txt->SetTextPos( rotatedTextPos );
2657 txt->SetPosition( rotatedTextPos );
2658
2659 txt->SetTextAngle( getAngle( aCadstarText.OrientAngle ) + rotationAngle );
2660
2661 txt->SetMirrored( aCadstarText.Mirror );
2662
2663 applyTextCode( txt, aCadstarText.TextCodeID );
2664
2665 switch( aCadstarText.Alignment )
2666 {
2667 case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
2671 break;
2672
2676 break;
2677
2681 break;
2682
2686 break;
2687
2691 break;
2692
2696 break;
2697
2698 case ALIGNMENT::TOPLEFT:
2701 break;
2702
2706 break;
2707
2711 break;
2712
2713 default:
2714 wxFAIL_MSG( wxT( "Unknown Alignment - needs review!" ) );
2715 }
2716
2717 if( aMirrorInvert )
2718 txt->Flip( aTransformCentre, FLIP_DIRECTION::LEFT_RIGHT );
2719
2720 //scale it after flipping:
2721 if( aScalingFactor != 1.0 )
2722 {
2723 VECTOR2I unscaledTextSize = txt->GetTextSize();
2724 int unscaledThickness = txt->GetTextThickness();
2725
2726 VECTOR2I scaledTextSize;
2727 scaledTextSize.x = KiROUND( (double) unscaledTextSize.x * aScalingFactor );
2728 scaledTextSize.y = KiROUND( (double) unscaledTextSize.y * aScalingFactor );
2729
2730 txt->SetTextSize( scaledTextSize );
2731 txt->SetTextThickness( KiROUND( (double) unscaledThickness * aScalingFactor ) );
2732 }
2733
2734 txt->Move( aMoveVector );
2735
2736 if( aCadstarText.Alignment == ALIGNMENT::NO_ALIGNMENT )
2738
2739 LAYER_ID layersToDrawOn = aCadstarLayerOverride;
2740
2741 if( layersToDrawOn.IsEmpty() )
2742 layersToDrawOn = aCadstarText.LayerID;
2743
2744 if( isLayerSet( layersToDrawOn ) )
2745 {
2746 //Make a copy on each layer
2747 for( PCB_LAYER_ID layer : getKiCadLayerSet( layersToDrawOn ).Seq() )
2748 {
2749 txt->SetLayer( layer );
2750 PCB_TEXT* newtxt = static_cast<PCB_TEXT*>( txt->Duplicate( IGNORE_PARENT_GROUP ) );
2751 m_board->Add( newtxt, ADD_MODE::APPEND );
2752
2753 if( !aCadstarGroupID.IsEmpty() )
2754 addToGroup( aCadstarGroupID, newtxt );
2755 }
2756
2757 m_board->Remove( txt );
2758 delete txt;
2759 }
2760 else
2761 {
2762 txt->SetLayer( getKiCadLayer( layersToDrawOn ) );
2763
2764 if( !aCadstarGroupID.IsEmpty() )
2765 addToGroup( aCadstarGroupID, txt );
2766 }
2767}
2768
2769
2770void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarShape( const SHAPE& aCadstarShape, const PCB_LAYER_ID& aKiCadLayer,
2771 int aLineThickness, LINE_STYLE aLineStyle,
2772 const wxString& aShapeName, BOARD_ITEM_CONTAINER* aContainer,
2773 const GROUP_ID& aCadstarGroupID, const VECTOR2I& aMoveVector,
2774 double aRotationAngle, double aScalingFactor,
2775 const VECTOR2I& aTransformCentre, bool aMirrorInvert )
2776{
2777 auto drawAsOutline =
2778 [&]()
2779 {
2780 drawCadstarVerticesAsShapes( aCadstarShape.Vertices, aKiCadLayer, aLineThickness, aLineStyle,
2781 aContainer, aCadstarGroupID, aMoveVector, aRotationAngle,
2782 aScalingFactor, aTransformCentre, aMirrorInvert );
2783 drawCadstarCutoutsAsShapes( aCadstarShape.Cutouts, aKiCadLayer, aLineThickness, aContainer,
2784 aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor,
2785 aTransformCentre, aMirrorInvert );
2786 };
2787
2788 if( aCadstarShape.Type == SHAPE_TYPE::OPENSHAPE || aCadstarShape.Type == SHAPE_TYPE::OUTLINE )
2789 {
2790 drawAsOutline();
2791 return;
2792 }
2793
2794 // Special case solid shapes that are effectively a single line
2795 if( aCadstarShape.Vertices.size() < 3 )
2796 {
2797 drawAsOutline();
2798 return;
2799 }
2800
2801 PCB_SHAPE* shape = new PCB_SHAPE( aContainer, SHAPE_T::POLY );
2802
2803 if( aCadstarShape.Type == SHAPE_TYPE::SOLID )
2805 else if( getHatchCodeAngle( aCadstarShape.HatchCodeID ) > ANGLE_90 )
2807 else
2808 shape->SetFillMode( FILL_T::HATCH );
2809
2810 SHAPE_POLY_SET shapePolys = getPolySetFromCadstarShape( aCadstarShape, -1, aContainer, aMoveVector,
2811 aRotationAngle, aScalingFactor, aTransformCentre,
2812 aMirrorInvert );
2813
2814 shapePolys.Fracture();
2815
2816 shape->SetPolyShape( shapePolys );
2817 shape->SetStroke( STROKE_PARAMS( aLineThickness, aLineStyle ) );
2818 shape->SetLayer( aKiCadLayer );
2819 aContainer->Add( shape, ADD_MODE::APPEND );
2820
2821 if( !aCadstarGroupID.IsEmpty() )
2822 addToGroup( aCadstarGroupID, shape );
2823}
2824
2825
2826void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarCutoutsAsShapes( const std::vector<CUTOUT>& aCutouts,
2827 const PCB_LAYER_ID& aKiCadLayer,
2828 int aLineThickness,
2829 BOARD_ITEM_CONTAINER* aContainer,
2830 const GROUP_ID& aCadstarGroupID,
2831 const VECTOR2I& aMoveVector,
2832 double aRotationAngle,
2833 double aScalingFactor,
2834 const VECTOR2I& aTransformCentre,
2835 bool aMirrorInvert )
2836{
2837 for( const CUTOUT& cutout : aCutouts )
2838 {
2839 drawCadstarVerticesAsShapes( cutout.Vertices, aKiCadLayer, aLineThickness, LINE_STYLE::SOLID, aContainer,
2840 aCadstarGroupID, aMoveVector, aRotationAngle, aScalingFactor, aTransformCentre,
2841 aMirrorInvert );
2842 }
2843}
2844
2845
2846void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarVerticesAsShapes( const std::vector<VERTEX>& aCadstarVertices,
2847 const PCB_LAYER_ID& aKiCadLayer,
2848 int aLineThickness, LINE_STYLE aLineStyle,
2849 BOARD_ITEM_CONTAINER* aContainer,
2850 const GROUP_ID& aCadstarGroupID,
2851 const VECTOR2I& aMoveVector, double aRotationAngle,
2852 double aScalingFactor, const VECTOR2I& aTransformCentre,
2853 bool aMirrorInvert )
2854{
2855 std::vector<PCB_SHAPE*> shapes = getShapesFromVertices( aCadstarVertices, aContainer, aCadstarGroupID,
2856 aMoveVector, aRotationAngle, aScalingFactor,
2857 aTransformCentre, aMirrorInvert );
2858
2859 for( PCB_SHAPE* shape : shapes )
2860 {
2861 shape->SetStroke( STROKE_PARAMS( aLineThickness, aLineStyle ) );
2862 shape->SetLayer( aKiCadLayer );
2863 shape->SetParent( aContainer );
2864 aContainer->Add( shape, ADD_MODE::APPEND );
2865 }
2866}
2867
2868
2869std::vector<PCB_SHAPE*>
2870CADSTAR_PCB_ARCHIVE_LOADER::getShapesFromVertices( const std::vector<VERTEX>& aCadstarVertices,
2871 BOARD_ITEM_CONTAINER* aContainer,
2872 const GROUP_ID& aCadstarGroupID,
2873 const VECTOR2I& aMoveVector,
2874 double aRotationAngle, double aScalingFactor,
2875 const VECTOR2I& aTransformCentre,
2876 bool aMirrorInvert )
2877{
2878 std::vector<PCB_SHAPE*> shapes;
2879
2880 if( aCadstarVertices.size() < 2 )
2881 //need at least two points to draw a segment! (unlikely but possible to have only one)
2882 return shapes;
2883
2884 const VERTEX* prev = &aCadstarVertices.at( 0 ); // first one should always be a point vertex
2885 const VERTEX* cur;
2886
2887 for( size_t i = 1; i < aCadstarVertices.size(); i++ )
2888 {
2889 cur = &aCadstarVertices.at( i );
2890 shapes.push_back( getShapeFromVertex( prev->End, *cur, aContainer, aCadstarGroupID, aMoveVector,
2891 aRotationAngle, aScalingFactor, aTransformCentre, aMirrorInvert ) );
2892 prev = cur;
2893 }
2894
2895 return shapes;
2896}
2897
2898
2900 const VERTEX& aCadstarVertex,
2901 BOARD_ITEM_CONTAINER* aContainer,
2902 const GROUP_ID& aCadstarGroupID,
2903 const VECTOR2I& aMoveVector,
2904 double aRotationAngle,
2905 double aScalingFactor,
2906 const VECTOR2I& aTransformCentre,
2907 bool aMirrorInvert )
2908{
2909 PCB_SHAPE* shape = nullptr;
2910 bool cw = false;
2911
2912 VECTOR2I startPoint = getKiCadPoint( aCadstarStartPoint );
2913 VECTOR2I endPoint = getKiCadPoint( aCadstarVertex.End );
2914 VECTOR2I centerPoint;
2915
2916 if( aCadstarVertex.Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE
2917 || aCadstarVertex.Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
2918 {
2919 centerPoint = ( startPoint + endPoint ) / 2;
2920 }
2921 else
2922 {
2923 centerPoint = getKiCadPoint( aCadstarVertex.Center );
2924 }
2925
2926 switch( aCadstarVertex.Type )
2927 {
2928
2930 shape = new PCB_SHAPE( aContainer, SHAPE_T::SEGMENT );
2931
2932 shape->SetStart( startPoint );
2933 shape->SetEnd( endPoint );
2934 break;
2935
2938 cw = true;
2940
2943 {
2944 shape = new PCB_SHAPE( aContainer, SHAPE_T::ARC );
2945
2946 shape->SetCenter( centerPoint );
2947 shape->SetStart( startPoint );
2948
2949 EDA_ANGLE arcStartAngle( startPoint - centerPoint );
2950 EDA_ANGLE arcEndAngle( endPoint - centerPoint );
2951 EDA_ANGLE arcAngle = ( arcEndAngle - arcStartAngle ).Normalize();
2952 //TODO: detect if we are supposed to draw a circle instead (i.e. two SEMICIRCLEs with opposite
2953 // start/end points and same centre point)
2954
2955 if( !cw )
2956 arcAngle.NormalizeNegative(); // anticlockwise arc
2957
2958 shape->SetArcAngleAndEnd( arcAngle, true );
2959
2960 break;
2961 }
2962 }
2963
2964 //Apply transforms
2965 if( aMirrorInvert )
2966 shape->Flip( aTransformCentre, FLIP_DIRECTION::LEFT_RIGHT );
2967
2968 if( aScalingFactor != 1.0 )
2969 {
2970 shape->Move( -1*aTransformCentre );
2971 shape->Scale( aScalingFactor );
2972 shape->Move( aTransformCentre );
2973 }
2974
2975 if( aRotationAngle != 0.0 )
2976 shape->Rotate( aTransformCentre, EDA_ANGLE( aRotationAngle, TENTHS_OF_A_DEGREE_T ) );
2977
2978 if( aMoveVector != VECTOR2I{ 0, 0 } )
2979 shape->Move( aMoveVector );
2980
2981 if( !aCadstarGroupID.IsEmpty() )
2982 addToGroup( aCadstarGroupID, shape );
2983
2984 return shape;
2985}
2986
2987
2988ZONE* CADSTAR_PCB_ARCHIVE_LOADER::getZoneFromCadstarShape( const SHAPE& aCadstarShape, const int& aLineThickness,
2989 BOARD_ITEM_CONTAINER* aParentContainer )
2990{
2991 ZONE* zone = new ZONE( aParentContainer );
2992
2993 if( aCadstarShape.Type == SHAPE_TYPE::HATCHED )
2994 {
2997 }
2998 else
2999 {
3001 }
3002
3003 SHAPE_POLY_SET polygon = getPolySetFromCadstarShape( aCadstarShape, aLineThickness );
3004
3005 zone->AddPolygon( polygon.COutline( 0 ) );
3006
3007 for( int i = 0; i < polygon.HoleCount( 0 ); i++ )
3008 zone->AddPolygon( polygon.CHole( 0, i ) );
3009
3010 return zone;
3011}
3012
3013
3015 int aLineThickness,
3016 BOARD_ITEM_CONTAINER* aContainer,
3017 const VECTOR2I& aMoveVector,
3018 double aRotationAngle,
3019 double aScalingFactor,
3020 const VECTOR2I& aTransformCentre,
3021 bool aMirrorInvert )
3022{
3023 GROUP_ID noGroup = wxEmptyString;
3024
3025 std::vector<PCB_SHAPE*> outlineShapes = getShapesFromVertices( aCadstarShape.Vertices, aContainer, noGroup,
3026 aMoveVector, aRotationAngle, aScalingFactor,
3027 aTransformCentre, aMirrorInvert );
3028
3029 SHAPE_POLY_SET polySet( getLineChainFromShapes( outlineShapes ) );
3030
3031 //cleanup
3032 for( PCB_SHAPE* shape : outlineShapes )
3033 delete shape;
3034
3035 for( const CUTOUT& cutout : aCadstarShape.Cutouts )
3036 {
3037 std::vector<PCB_SHAPE*> cutoutShapes = getShapesFromVertices( cutout.Vertices, aContainer, noGroup,
3038 aMoveVector, aRotationAngle, aScalingFactor,
3039 aTransformCentre, aMirrorInvert );
3040
3041 polySet.AddHole( getLineChainFromShapes( cutoutShapes ) );
3042
3043 //cleanup
3044 for( PCB_SHAPE* shape : cutoutShapes )
3045 delete shape;
3046 }
3047
3048 polySet.ClearArcs();
3049
3050 if( aLineThickness > 0 )
3051 polySet.Inflate( aLineThickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_HIGH_DEF );
3052
3053#ifdef DEBUG
3054 for( int i = 0; i < polySet.OutlineCount(); ++i )
3055 {
3056 wxASSERT( polySet.Outline( i ).PointCount() > 2 );
3057
3058 for( int j = 0; j < polySet.HoleCount( i ); ++j )
3059 wxASSERT( polySet.Hole( i, j ).PointCount() > 2 );
3060 }
3061#endif
3062
3063 return polySet;
3064}
3065
3066
3068{
3069 SHAPE_LINE_CHAIN lineChain;
3070
3071 for( PCB_SHAPE* shape : aShapes )
3072 {
3073 switch( shape->GetShape() )
3074 {
3075 case SHAPE_T::ARC:
3076 {
3077 SHAPE_ARC arc( shape->GetCenter(), shape->GetStart(), shape->GetArcAngle() );
3078
3079 if( shape->EndsSwapped() )
3080 arc.Reverse();
3081
3082 lineChain.Append( arc );
3083 break;
3084 }
3085
3086 case SHAPE_T::SEGMENT:
3087 lineChain.Append( shape->GetStartX(), shape->GetStartY() );
3088 lineChain.Append( shape->GetEndX(), shape->GetEndY() );
3089 break;
3090
3091 default:
3092 wxFAIL_MSG( wxT( "Drawsegment type is unexpected. Ignored." ) );
3093 }
3094 }
3095
3096 // Shouldn't have less than 3 points to make a closed shape!
3097 wxASSERT( lineChain.PointCount() > 2 );
3098
3099 // Check if it is closed
3100 if( lineChain.GetPoint( 0 ) != lineChain.GetPoint( lineChain.PointCount() - 1 ) )
3101 lineChain.Append( lineChain.GetPoint( 0 ) );
3102
3103 lineChain.SetClosed( true );
3104
3105 return lineChain;
3106}
3107
3108
3109std::vector<PCB_TRACK*> CADSTAR_PCB_ARCHIVE_LOADER::makeTracksFromShapes( const std::vector<PCB_SHAPE*>& aShapes,
3110 BOARD_ITEM_CONTAINER* aParentContainer,
3111 NETINFO_ITEM* aNet,
3112 PCB_LAYER_ID aLayerOverride,
3113 int aWidthOverride )
3114{
3115 std::vector<PCB_TRACK*> tracks;
3116 PCB_TRACK* prevTrack = nullptr;
3117 PCB_TRACK* track = nullptr;
3118
3119 auto addTrack =
3120 [&]( PCB_TRACK* aTrack )
3121 {
3122 // Ignore zero length tracks in the same way as the CADSTAR postprocessor does
3123 // when generating gerbers. Note that CADSTAR reports these as "Route offset
3124 // errors" when running a DRC within CADSTAR, so we shouldn't be getting this in
3125 // general, however it is used to remove any synthetic points added to
3126 // aDrawSegments by the caller of this function.
3127 if( aTrack->GetLength() != 0 )
3128 {
3129 tracks.push_back( aTrack );
3130 aParentContainer->Add( aTrack, ADD_MODE::APPEND );
3131 }
3132 else
3133 {
3134 delete aTrack;
3135 }
3136 };
3137
3138 for( PCB_SHAPE* shape : aShapes )
3139 {
3140 switch( shape->GetShape() )
3141 {
3142 case SHAPE_T::ARC:
3143 {
3144 SHAPE_ARC arc( shape->GetStart(), shape->GetArcMid(), shape->GetEnd(), 0 );
3145
3146 if( shape->EndsSwapped() )
3147 arc.Reverse();
3148
3149 track = new PCB_ARC( aParentContainer, &arc );
3150 break;
3151 }
3152
3153 case SHAPE_T::SEGMENT:
3154 track = new PCB_TRACK( aParentContainer );
3155 track->SetStart( shape->GetStart() );
3156 track->SetEnd( shape->GetEnd() );
3157 break;
3158
3159 default:
3160 wxFAIL_MSG( wxT( "Drawsegment type is unexpected. Ignored." ) );
3161 continue;
3162 }
3163
3164 if( aWidthOverride == -1 )
3165 track->SetWidth( shape->GetWidth() );
3166 else
3167 track->SetWidth( aWidthOverride );
3168
3169 if( aLayerOverride == PCB_LAYER_ID::UNDEFINED_LAYER )
3170 track->SetLayer( shape->GetLayer() );
3171 else
3172 track->SetLayer( aLayerOverride );
3173
3174 if( aNet != nullptr )
3175 track->SetNet( aNet );
3176 else
3177 track->SetNetCode( -1 );
3178
3179 track->SetLocked( shape->IsLocked() );
3180
3181 // Apply route offsetting, mimmicking the behaviour of the CADSTAR post processor
3182 if( prevTrack != nullptr )
3183 {
3184 int offsetAmount = ( track->GetWidth() / 2 ) - ( prevTrack->GetWidth() / 2 );
3185
3186 if( offsetAmount > 0 )
3187 {
3188 // modify the start of the current track
3189 VECTOR2I newStart = track->GetStart();
3190 applyRouteOffset( &newStart, track->GetEnd(), offsetAmount );
3191 track->SetStart( newStart );
3192 }
3193 else if( offsetAmount < 0 )
3194 {
3195 // amend the end of the previous track
3196 VECTOR2I newEnd = prevTrack->GetEnd();
3197 applyRouteOffset( &newEnd, prevTrack->GetStart(), -offsetAmount );
3198 prevTrack->SetEnd( newEnd );
3199 } // don't do anything if offsetAmount == 0
3200
3201 // Add a synthetic track of the thinnest width between the tracks
3202 // to ensure KiCad features works as expected on the imported design
3203 // (KiCad expects tracks are contiguous segments)
3204 if( track->GetStart() != prevTrack->GetEnd() )
3205 {
3206 int minWidth = std::min( track->GetWidth(), prevTrack->GetWidth() );
3207 PCB_TRACK* synthTrack = new PCB_TRACK( aParentContainer );
3208 synthTrack->SetStart( prevTrack->GetEnd() );
3209 synthTrack->SetEnd( track->GetStart() );
3210 synthTrack->SetWidth( minWidth );
3211 synthTrack->SetLocked( track->IsLocked() );
3212 synthTrack->SetNet( track->GetNet() );
3213 synthTrack->SetLayer( track->GetLayer() );
3214 addTrack( synthTrack );
3215 }
3216 }
3217
3218 if( prevTrack )
3219 addTrack( prevTrack );
3220
3221 prevTrack = track;
3222 }
3223
3224 if( track )
3225 addTrack( track );
3226
3227 return tracks;
3228}
3229
3230
3232 const ATTRIBUTE_ID& aCadstarAttributeID,
3233 FOOTPRINT* aFootprint, const wxString& aAttributeValue )
3234{
3235 PCB_FIELD* field;
3236
3237 if( aCadstarAttributeID == COMPONENT_NAME_ATTRID )
3238 {
3239 field = &aFootprint->Reference(); //text should be set outside this function
3240 }
3241 else if( aCadstarAttributeID == PART_NAME_ATTRID )
3242 {
3243 if( aFootprint->Value().GetText().IsEmpty() )
3244 {
3245 // Use PART_NAME_ATTRID as the value is value field is blank
3246 aFootprint->SetValue( aAttributeValue );
3247 field = &aFootprint->Value();
3248 }
3249 else
3250 {
3251 field = new PCB_FIELD( aFootprint, FIELD_T::USER, aCadstarAttributeID );
3252 aFootprint->Add( field );
3253 field->SetText( aAttributeValue );
3254 }
3255 field->SetVisible( false ); //make invisible to avoid clutter.
3256 }
3257 else if( aCadstarAttributeID != COMPONENT_NAME_2_ATTRID
3258 && getAttributeName( aCadstarAttributeID ) == wxT( "Value" ) )
3259 {
3260 if( !aFootprint->Value().GetText().IsEmpty() )
3261 {
3262 //copy the object
3263 aFootprint->Add( aFootprint->Value().Duplicate( IGNORE_PARENT_GROUP ) );
3264 }
3265
3266 aFootprint->SetValue( aAttributeValue );
3267 field = &aFootprint->Value();
3268 field->SetVisible( false ); //make invisible to avoid clutter.
3269 }
3270 else
3271 {
3272 field = new PCB_FIELD( aFootprint, FIELD_T::USER, aCadstarAttributeID );
3273 aFootprint->Add( field );
3274 field->SetText( aAttributeValue );
3275 field->SetVisible( false ); //make all user attributes invisible to avoid clutter.
3276 }
3277
3278 field->SetPosition( getKiCadPoint( aCadstarAttrLoc.Position ) );
3279 field->SetLayer( getKiCadLayer( aCadstarAttrLoc.LayerID ) );
3280 field->SetMirrored( aCadstarAttrLoc.Mirror );
3281 field->SetTextAngle( getAngle( aCadstarAttrLoc.OrientAngle ) );
3282
3283 if( aCadstarAttrLoc.Mirror ) // If mirroring, invert angle to match CADSTAR
3284 field->SetTextAngle( -field->GetTextAngle() );
3285
3286 applyTextCode( field, aCadstarAttrLoc.TextCodeID );
3287
3288 field->SetKeepUpright( false ); //Keeping it upright seems to result in incorrect orientation
3289
3290 switch( aCadstarAttrLoc.Alignment )
3291 {
3292 case ALIGNMENT::NO_ALIGNMENT: // Default for Single line text is Bottom Left
3298 break;
3299
3303 break;
3304
3308 break;
3309
3313 break;
3314
3318 break;
3319
3323 break;
3324
3325 case ALIGNMENT::TOPLEFT:
3328 break;
3329
3333 break;
3334
3338 break;
3339
3340 default:
3341 wxFAIL_MSG( wxT( "Unknown Alignment - needs review!" ) );
3342 }
3343}
3344
3345
3347 const VECTOR2I& aRefPoint,
3348 const long& aOffsetAmount )
3349{
3350 VECTOR2I v( *aPointToOffset - aRefPoint );
3351 int newLength = v.EuclideanNorm() - aOffsetAmount;
3352
3353 if( newLength > 0 )
3354 {
3355 VECTOR2I offsetted = v.Resize( newLength ) + VECTOR2I( aRefPoint );
3356 aPointToOffset->x = offsetted.x;
3357 aPointToOffset->y = offsetted.y;
3358 }
3359 else
3360 {
3361 *aPointToOffset = aRefPoint; // zero length track. Needs to be removed to mimmick
3362 // cadstar behaviour
3363 }
3364}
3365
3366
3367void CADSTAR_PCB_ARCHIVE_LOADER:: applyTextCode( EDA_TEXT* aKiCadText, const TEXTCODE_ID& aCadstarTextCodeID )
3368{
3369 TEXTCODE tc = getTextCode( aCadstarTextCodeID );
3370
3371 aKiCadText->SetTextThickness( getKiCadLength( tc.LineWidth ) );
3372
3373 if( tc.Font.Modifier1 == FONT_BOLD )
3374 aKiCadText->SetBold( true );
3375
3376 if( tc.Font.Italic )
3377 aKiCadText->SetItalic( true );
3378
3379 VECTOR2I textSize;
3380 textSize.x = getKiCadLength( tc.Width );
3381
3382 // The width is zero for all non-cadstar fonts. Using a width equal to the height seems
3383 // to work well for most fonts.
3384 if( textSize.x == 0 )
3385 textSize.x = getKiCadLength( tc.Height );
3386
3387 textSize.y = KiROUND( TXT_HEIGHT_RATIO * (double) getKiCadLength( tc.Height ) );
3388
3389 if( textSize.x == 0 || textSize.y == 0 )
3390 {
3391 // Make zero sized text not visible
3392
3395 }
3396 else
3397 {
3398 aKiCadText->SetTextSize( textSize );
3399 }
3400
3401 KIFONT::FONT* font;
3402
3403 if( tc.Font.Name == CADSTAR_FONT_NAME )
3404 {
3405 // Kicad currently only supports a single stroke font, so even if we had a facsimile of the CADSTAR stroke
3406 // font we wouldn't be able to use it.
3407 // So, substitute the Kicad stroke font. (We could default to a similarly-named outline font, but the
3408 // performance penalty on some designs would be large. Better to let the user do that if they want.)
3409
3410 fontconfig::FONTCONFIG::GetReporter().Report( wxString::Format( _( "Font '%s' not found; substituting '%s'." ),
3412
3414 }
3415 else
3416 {
3418 }
3419
3420 if( font )
3421 aKiCadText->SetFont( font );
3422
3423 // The line width is the intended rendered stroke; store the base so Bold doesn't double
3424 // it. Must run after SetFont() so the stroke-vs-outline check below sees the real font.
3425 aKiCadText->MigrateLegacyBoldStrokeWidth();
3426}
3427
3428
3430{
3431 wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID ) != Assignments.Codedefs.LineCodes.end(),
3432 m_board->GetDesignSettings().GetLineThickness( PCB_LAYER_ID::Edge_Cuts ) );
3433
3434 return getKiCadLength( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Width );
3435}
3436
3437
3439{
3440 wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID ) != Assignments.Codedefs.LineCodes.end(),
3442
3443 switch( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Style )
3444 {
3445 case LINESTYLE::DASH: return LINE_STYLE::DASH;
3448 case LINESTYLE::DOT: return LINE_STYLE::DOT;
3450 default: return LINE_STYLE::DEFAULT;
3451 }
3452}
3453
3454
3456 const COPPERCODE_ID& aCadstaCopperCodeID )
3457{
3458 wxCHECK( Assignments.Codedefs.CopperCodes.find( aCadstaCopperCodeID ) != Assignments.Codedefs.CopperCodes.end(),
3459 COPPERCODE() );
3460
3461 return Assignments.Codedefs.CopperCodes.at( aCadstaCopperCodeID );
3462}
3463
3464
3466{
3467 wxCHECK( Assignments.Codedefs.TextCodes.find( aCadstarTextCodeID ) != Assignments.Codedefs.TextCodes.end(),
3468 TEXTCODE() );
3469
3470 return Assignments.Codedefs.TextCodes.at( aCadstarTextCodeID );
3471}
3472
3473
3475{
3476 wxCHECK( Assignments.Codedefs.PadCodes.find( aCadstarPadCodeID ) != Assignments.Codedefs.PadCodes.end(),
3477 PADCODE() );
3478
3479 return Assignments.Codedefs.PadCodes.at( aCadstarPadCodeID );
3480}
3481
3482
3484{
3485 wxCHECK( Assignments.Codedefs.ViaCodes.find( aCadstarViaCodeID ) != Assignments.Codedefs.ViaCodes.end(),
3486 VIACODE() );
3487
3488 return Assignments.Codedefs.ViaCodes.at( aCadstarViaCodeID );
3489}
3490
3491
3493{
3494 wxCHECK( Assignments.Codedefs.LayerPairs.find( aCadstarLayerPairID ) != Assignments.Codedefs.LayerPairs.end(),
3495 LAYERPAIR() );
3496
3497 return Assignments.Codedefs.LayerPairs.at( aCadstarLayerPairID );
3498}
3499
3500
3502{
3503 wxCHECK( Assignments.Codedefs.AttributeNames.find( aCadstarAttributeID )
3504 != Assignments.Codedefs.AttributeNames.end(),
3505 wxEmptyString );
3506
3507 return Assignments.Codedefs.AttributeNames.at( aCadstarAttributeID ).Name;
3508}
3509
3510
3512 const std::map<ATTRIBUTE_ID, ATTRIBUTE_VALUE>& aCadstarAttrMap )
3513{
3514 wxCHECK( aCadstarAttrMap.find( aCadstarAttributeID ) != aCadstarAttrMap.end(), wxEmptyString );
3515
3516 return aCadstarAttrMap.at( aCadstarAttributeID ).Value;
3517}
3518
3519
3522{
3523 if( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) != Assignments.Layerdefs.Layers.end() )
3524 return Assignments.Layerdefs.Layers.at( aCadstarLayerID ).Type;
3525
3526 return LAYER_TYPE::UNDEFINED;
3527}
3528
3529
3531{
3532 wxCHECK( Parts.PartDefinitions.find( aCadstarPartID ) != Parts.PartDefinitions.end(), PART() );
3533
3534 return Parts.PartDefinitions.at( aCadstarPartID );
3535}
3536
3537
3539 const ROUTECODE_ID& aCadstarRouteCodeID )
3540{
3541 wxCHECK( Assignments.Codedefs.RouteCodes.find( aCadstarRouteCodeID ) != Assignments.Codedefs.RouteCodes.end(),
3542 ROUTECODE() );
3543
3544 return Assignments.Codedefs.RouteCodes.at( aCadstarRouteCodeID );
3545}
3546
3547
3549 const HATCHCODE_ID& aCadstarHatchcodeID )
3550{
3551 wxCHECK( Assignments.Codedefs.HatchCodes.find( aCadstarHatchcodeID ) != Assignments.Codedefs.HatchCodes.end(),
3552 HATCHCODE() );
3553
3554 return Assignments.Codedefs.HatchCodes.at( aCadstarHatchcodeID );
3555}
3556
3557
3559{
3560 checkAndLogHatchCode( aCadstarHatchcodeID );
3561 HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3562
3563 if( hcode.Hatches.size() < 1 )
3564 return m_board->GetDesignSettings().GetDefaultZoneSettings().m_HatchOrientation;
3565 else
3566 return getAngle( hcode.Hatches.at( 0 ).OrientAngle );
3567}
3568
3569
3571{
3572 checkAndLogHatchCode( aCadstarHatchcodeID );
3573 HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3574
3575 if( hcode.Hatches.size() < 1 )
3576 return m_board->GetDesignSettings().GetDefaultZoneSettings().m_HatchThickness;
3577 else
3578 return getKiCadLength( hcode.Hatches.at( 0 ).LineWidth );
3579}
3580
3581
3583{
3584 checkAndLogHatchCode( aCadstarHatchcodeID );
3585 HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3586
3587 if( hcode.Hatches.size() < 1 )
3588 return m_board->GetDesignSettings().GetDefaultZoneSettings().m_HatchGap;
3589 else
3590 return getKiCadLength( hcode.Hatches.at( 0 ).Step );
3591}
3592
3593
3595{
3596 wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), nullptr );
3597
3598 return m_groupMap.at( aCadstarGroupID );
3599}
3600
3601
3603{
3604 if( m_hatchcodesTested.find( aCadstarHatchcodeID ) != m_hatchcodesTested.end() )
3605 {
3606 return; //already checked
3607 }
3608 else
3609 {
3610 HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID );
3611
3612 if( hcode.Hatches.size() != 2 )
3613 {
3614 reportWarning( wxString::Format( _( "The CADSTAR Hatching code '%s' has %d hatches defined. "
3615 "KiCad only supports 2 hatches (crosshatching) 90 degrees apart. "
3616 "The imported hatching is crosshatched." ),
3617 hcode.Name,
3618 (int) hcode.Hatches.size() ) );
3619 }
3620 else
3621 {
3622 if( hcode.Hatches.at( 0 ).LineWidth != hcode.Hatches.at( 1 ).LineWidth )
3623 {
3624 reportWarning( wxString::Format( _( "The CADSTAR Hatching code '%s' has different line widths for "
3625 "each hatch. KiCad only supports one width for the hatching. The "
3626 "imported hatching uses the width defined in the first hatch "
3627 "definition, i.e. %.2f mm." ), //format:allow
3628 hcode.Name,
3629 (double) getKiCadLength( hcode.Hatches.at( 0 ).LineWidth ) / 1E6 ) );
3630 }
3631
3632 if( hcode.Hatches.at( 0 ).Step != hcode.Hatches.at( 1 ).Step )
3633 {
3634 reportWarning( wxString::Format( _( "The CADSTAR Hatching code '%s' has different step sizes for "
3635 "each hatch. KiCad only supports one step size for the hatching. "
3636 "The imported hatching uses the step size defined in the first "
3637 "hatching definition, i.e. %.2f mm." ), //format:allow
3638 hcode.Name,
3639 (double) getKiCadLength( hcode.Hatches.at( 0 ).Step ) / 1E6 ) );
3640 }
3641
3642 if( abs( hcode.Hatches.at( 0 ).OrientAngle - hcode.Hatches.at( 1 ).OrientAngle ) != 90000 )
3643 {
3644 reportWarning( wxString::Format( _( "The hatches in CADSTAR Hatching code '%s' have an angle "
3645 "difference of %.1f degrees. KiCad only supports hatching 90 " //format:allow
3646 "degrees apart. The imported hatching has two hatches 90 "
3647 "degrees apart, oriented %.1f degrees from horizontal." ), //format:allow
3648 hcode.Name,
3649 getAngle( abs( hcode.Hatches.at( 0 ).OrientAngle
3650 - hcode.Hatches.at( 1 ).OrientAngle ) ).AsDegrees(),
3651 getAngle( hcode.Hatches.at( 0 ).OrientAngle ).AsDegrees() ) );
3652 }
3653 }
3654
3655 m_hatchcodesTested.insert( aCadstarHatchcodeID );
3656 }
3657}
3658
3659
3661{
3662 UNITS dimensionUnits = aCadstarDim.LinearUnits;
3663 LINECODE linecode = Assignments.Codedefs.LineCodes.at( aCadstarDim.Line.LineCodeID );
3664
3665 aKiCadDim->SetLayer( getKiCadLayer( aCadstarDim.LayerID ) );
3666 aKiCadDim->SetPrecision( static_cast<DIM_PRECISION>( aCadstarDim.Precision ) );
3667 aKiCadDim->SetStart( getKiCadPoint( aCadstarDim.ExtensionLineParams.Start ) );
3668 aKiCadDim->SetEnd( getKiCadPoint( aCadstarDim.ExtensionLineParams.End ) );
3669 aKiCadDim->SetExtensionOffset( getKiCadLength( aCadstarDim.ExtensionLineParams.Offset ) );
3670 aKiCadDim->SetLineThickness( getKiCadLength( linecode.Width ) );
3671
3672 applyTextCode( aKiCadDim, aCadstarDim.Text.TextCodeID );
3673
3674 // Find prefix and suffix:
3675 wxString prefix = wxEmptyString;
3676 wxString suffix = wxEmptyString;
3677 int startpos = aCadstarDim.Text.Text.Find( wxT( "<@DISTANCE" ) );
3678
3679 if( startpos != wxNOT_FOUND )
3680 {
3681 prefix = ParseTextFields( aCadstarDim.Text.Text.SubString( 0, startpos - 1 ), &m_context );
3682 wxString remainingStr = aCadstarDim.Text.Text.Mid( startpos );
3683 int endpos = remainingStr.Find( "@>" );
3684 suffix = ParseTextFields( remainingStr.Mid( endpos + 2 ), &m_context );
3685 }
3686
3687 if( suffix.StartsWith( wxT( "mm" ) ) )
3688 {
3690 suffix = suffix.Mid( 2 );
3691 }
3692 else
3693 {
3695 }
3696
3697 aKiCadDim->SetPrefix( prefix );
3698 aKiCadDim->SetSuffix( suffix );
3699
3700 if( aCadstarDim.LinearUnits == UNITS::DESIGN )
3701 {
3702 // For now we will hardcode the units as per the original CADSTAR design.
3703 // TODO: update this when KiCad supports design units
3704 aKiCadDim->SetPrecision( static_cast<DIM_PRECISION>( Assignments.Technology.UnitDisplPrecision ) );
3705 dimensionUnits = Assignments.Technology.Units;
3706 }
3707
3708 switch( dimensionUnits )
3709 {
3710 case UNITS::METER:
3711 case UNITS::CENTIMETER:
3712 case UNITS::MICROMETRE:
3713 reportWarning( wxString::Format( _( "Dimension ID %s uses a type of unit that is not supported in KiCad. "
3714 "Millimeters were applied instead." ),
3715 aCadstarDim.ID ) );
3717 case UNITS::MM:
3718 aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::MM );
3719 break;
3720
3721 case UNITS::INCH:
3722 aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::INCH );
3723 break;
3724
3725 case UNITS::THOU:
3726 aKiCadDim->SetUnitsMode( DIM_UNITS_MODE::MILS );
3727 break;
3728
3729 case UNITS::DESIGN:
3730 wxFAIL_MSG( wxT( "We should have handled design units before coming here!" ) );
3731 break;
3732 }
3733}
3734
3735
3737{
3738 std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
3739
3740 auto inflateValue =
3741 [&]( ZONE* aZoneA, ZONE* aZoneB )
3742 {
3743 int extra = getKiCadLength( Assignments.Codedefs.SpacingCodes.at( wxT( "C_C" ) ).Spacing )
3744 - m_board->GetDesignSettings().m_MinClearance;
3745
3746 int retval = std::max( aZoneA->GetLocalClearance().value(), aZoneB->GetLocalClearance().value() );
3747
3748 retval += extra;
3749
3750 return retval;
3751 };
3752
3753 // Find the error in fill area when guessing that aHigherZone gets filled before aLowerZone
3754 auto errorArea =
3755 [&]( ZONE* aLowerZone, ZONE* aHigherZone ) -> double
3756 {
3757 SHAPE_POLY_SET intersectShape( *aHigherZone->Outline() );
3758 intersectShape.Inflate( inflateValue( aLowerZone, aHigherZone ),
3760
3761 SHAPE_POLY_SET lowerZoneFill( *aLowerZone->GetFilledPolysList( aLayer ) );
3762 SHAPE_POLY_SET lowerZoneOutline( *aLowerZone->Outline() );
3763
3764 lowerZoneOutline.BooleanSubtract( intersectShape );
3765
3766 lowerZoneFill.BooleanSubtract( lowerZoneOutline );
3767
3768 double leftOverArea = lowerZoneFill.Area();
3769
3770 return leftOverArea;
3771 };
3772
3773 auto intersectionAreaOfZoneOutlines =
3774 [&]( ZONE* aZoneA, ZONE* aZoneB ) -> double
3775 {
3776 SHAPE_POLY_SET outLineA( *aZoneA->Outline() );
3777 outLineA.Inflate( inflateValue( aZoneA, aZoneB ), CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_HIGH_DEF );
3778
3779 SHAPE_POLY_SET outLineB( *aZoneA->Outline() );
3780 outLineB.Inflate( inflateValue( aZoneA, aZoneB ), CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_HIGH_DEF );
3781
3782 outLineA.BooleanIntersection( outLineB );
3783
3784 return outLineA.Area();
3785 };
3786
3787 // Lambda to determine if the zone with template ID 'a' is lower priority than 'b'
3788 auto isLowerPriority =
3789 [&]( const TEMPLATE_ID& a, const TEMPLATE_ID& b ) -> bool
3790 {
3791 return winningOverlaps[b].count( a ) > 0;
3792 };
3793
3794 for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = m_zonesMap.begin(); it1 != m_zonesMap.end(); ++it1 )
3795 {
3796 TEMPLATE& thisTemplate = Layout.Templates.at( it1->first );
3797 ZONE* thisZone = it1->second;
3798
3799 if( !thisZone->GetLayerSet().Contains( aLayer ) )
3800 continue;
3801
3802 for( std::map<TEMPLATE_ID, ZONE*>::iterator it2 = it1;
3803 it2 != m_zonesMap.end(); ++it2 )
3804 {
3805 TEMPLATE& otherTemplate = Layout.Templates.at( it2->first );
3806 ZONE* otherZone = it2->second;
3807
3808 if( thisTemplate.ID == otherTemplate.ID )
3809 continue;
3810
3811 if( !otherZone->GetLayerSet().Contains( aLayer ) )
3812 {
3813 checkPoint();
3814 continue;
3815 }
3816
3817 if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) == 0 )
3818 {
3819 checkPoint();
3820 continue; // The zones do not interact in any way
3821 }
3822
3823 SHAPE_POLY_SET thisZonePolyFill = *thisZone->GetFilledPolysList( aLayer );
3824 SHAPE_POLY_SET otherZonePolyFill = *otherZone->GetFilledPolysList( aLayer );
3825
3826 if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
3827 {
3828 // Test if this zone were lower priority than other zone, what is the error?
3829 double areaThis = errorArea( thisZone, otherZone );
3830 // Vice-versa
3831 double areaOther = errorArea( otherZone, thisZone );
3832
3833 if( areaThis > areaOther )
3834 {
3835 // thisTemplate is filled before otherTemplate
3836 winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3837 }
3838 else
3839 {
3840 // thisTemplate is filled AFTER otherTemplate
3841 winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3842 }
3843 }
3844 else if( thisZonePolyFill.Area() > 0.0 )
3845 {
3846 // The other template is not filled, this one wins
3847 winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3848 }
3849 else if( otherZonePolyFill.Area() > 0.0 )
3850 {
3851 // This template is not filled, the other one wins
3852 winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3853 }
3854 else
3855 {
3856 // Neither of the templates is poured - use zone outlines instead (bigger outlines
3857 // get a lower priority)
3858 if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) != 0 )
3859 {
3860 if( thisZone->Outline()->Area() > otherZone->Outline()->Area() )
3861 winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
3862 else
3863 winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
3864 }
3865 }
3866
3867 checkPoint();
3868 }
3869 }
3870
3871 // Build a set of unique TEMPLATE_IDs of all the zones that intersect with another one
3872 std::set<TEMPLATE_ID> intersectingIDs;
3873
3874 for( const auto& idPair : winningOverlaps )
3875 {
3876 intersectingIDs.insert( idPair.first );
3877 intersectingIDs.insert( idPair.second.begin(), idPair.second.end() );
3878 }
3879
3880 // Now store them in a vector
3881 std::vector<TEMPLATE_ID> sortedIDs;
3882
3883 for( const TEMPLATE_ID& id : intersectingIDs )
3884 {
3885 sortedIDs.push_back( id );
3886 }
3887
3888 // sort by priority
3889 std::sort( sortedIDs.begin(), sortedIDs.end(), isLowerPriority );
3890
3891 TEMPLATE_ID prevID = wxEmptyString;
3892
3893 for( const TEMPLATE_ID& id : sortedIDs )
3894 {
3895 if( prevID.IsEmpty() )
3896 {
3897 prevID = id;
3898 continue;
3899 }
3900
3901 wxASSERT( !isLowerPriority( id, prevID ) );
3902
3903 int newPriority = m_zonesMap.at( prevID )->GetAssignedPriority();
3904
3905 // Only increase priority of the current zone
3906 if( isLowerPriority( prevID, id ) )
3907 newPriority++;
3908
3909 m_zonesMap.at( id )->SetAssignedPriority( newPriority );
3910 prevID = id;
3911 }
3912
3913 // Verify
3914 for( const auto& idPair : winningOverlaps )
3915 {
3916 const TEMPLATE_ID& winningID = idPair.first;
3917
3918 for( const TEMPLATE_ID& losingID : idPair.second )
3919 {
3920 if( m_zonesMap.at( losingID )->GetAssignedPriority() > m_zonesMap.at( winningID )->GetAssignedPriority() )
3921 return false;
3922 }
3923 }
3924
3925 return true;
3926}
3927
3928
3930{
3931 if( m_componentMap.find( aCadstarComponentID ) == m_componentMap.end() )
3932 return nullptr;
3933 else
3934 return m_componentMap.at( aCadstarComponentID );
3935}
3936
3937
3939{
3940 VECTOR2I retval;
3941
3942 retval.x = ( aCadstarPoint.x - m_designCenter.x ) * KiCadUnitMultiplier;
3943 retval.y = -( aCadstarPoint.y - m_designCenter.y ) * KiCadUnitMultiplier;
3944
3945 return retval;
3946}
3947
3948
3950{
3951 if( aCadstarNetID.IsEmpty() )
3952 {
3953 return nullptr;
3954 }
3955 else if( m_netMap.find( aCadstarNetID ) != m_netMap.end() )
3956 {
3957 return m_netMap.at( aCadstarNetID );
3958 }
3959 else
3960 {
3961 wxCHECK( Layout.Nets.find( aCadstarNetID ) != Layout.Nets.end(), nullptr );
3962
3963 NET_PCB csNet = Layout.Nets.at( aCadstarNetID );
3964 wxString newName = csNet.Name;
3965
3966 if( csNet.Name.IsEmpty() )
3967 {
3968 if( csNet.Pins.size() > 0 )
3969 {
3970 // Create default KiCad net naming:
3971
3972 NET_PCB::PIN firstPin = ( *csNet.Pins.begin() ).second;
3973 //we should have already loaded the component with loadComponents() :
3974 FOOTPRINT* m = getFootprintFromCadstarID( firstPin.ComponentID );
3975 newName = wxT( "Net-(" );
3976 newName << m->Reference().GetText();
3977 newName << wxT( "-Pad" ) << wxString::Format( wxT( "%ld" ), firstPin.PadID );
3978 newName << wxT( ")" );
3979 }
3980 else
3981 {
3982 wxFAIL_MSG( wxT( "A net with no pins associated?" ) );
3983 newName = wxT( "csNet-" );
3984 newName << wxString::Format( wxT( "%i" ), csNet.SignalNum );
3985 }
3986 }
3987
3988 if( !m_doneNetClassWarning && !csNet.NetClassID.IsEmpty() && csNet.NetClassID != wxT( "NONE" ) )
3989 {
3990 reportInfo( _( "The CADSTAR design contains nets with a 'Net Class' assigned. KiCad "
3991 "does not have an equivalent to CADSTAR's Net Class so these elements "
3992 "were not imported. Note: KiCad's version of 'Net Class' is closer to "
3993 "CADSTAR's 'Net Route Code' (which has been imported for all nets)." ) );
3994 m_doneNetClassWarning = true;
3995 }
3996
3997 if( !m_doneSpacingClassWarning && !csNet.SpacingClassID.IsEmpty() && csNet.SpacingClassID != wxT( "NONE" ) )
3998 {
3999 reportWarning( _( "The CADSTAR design contains nets with a 'Spacing Class' assigned. "
4000 "KiCad does not have an equivalent to CADSTAR's Spacing Class so "
4001 "these elements were not imported. Please review the design rules as "
4002 "copper pours may be affected by this." ) );
4004 }
4005
4006 std::shared_ptr<NET_SETTINGS>& netSettings = m_board->GetDesignSettings().m_NetSettings;
4007 NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, newName, ++m_numNets );
4008 std::shared_ptr<NETCLASS> netclass;
4009
4010 std::tuple<ROUTECODE_ID, NETCLASS_ID, SPACING_CLASS_ID> key = { csNet.RouteCodeID,
4011 csNet.NetClassID,
4012 csNet.SpacingClassID };
4013
4014 if( m_netClassMap.find( key ) != m_netClassMap.end() )
4015 {
4016 netclass = m_netClassMap.at( key );
4017 }
4018 else if( auto rcIt = Assignments.Codedefs.RouteCodes.find( csNet.RouteCodeID );
4019 !csNet.RouteCodeID.IsEmpty() && rcIt != Assignments.Codedefs.RouteCodes.end()
4020 && rcIt->second.OptimalWidth > 0 )
4021 {
4022 wxString netClassName;
4023
4024 ROUTECODE rc = getRouteCode( csNet.RouteCodeID );
4025 netClassName += wxT( "Route code: " ) + rc.Name;
4026
4027 if( !csNet.NetClassID.IsEmpty() )
4028 {
4029 CADSTAR_NETCLASS nc = Assignments.Codedefs.NetClasses.at( csNet.NetClassID );
4030 netClassName += wxT( " | Net class: " ) + nc.Name;
4031 }
4032
4033 if( !csNet.SpacingClassID.IsEmpty() )
4034 {
4035 SPCCLASSNAME sp = Assignments.Codedefs.SpacingClassNames.at( csNet.SpacingClassID );
4036 netClassName += wxT( " | Spacing class: " ) + sp.Name;
4037 }
4038
4039 netclass.reset( new NETCLASS( netClassName ) );
4040 netSettings->SetNetclass( netClassName, netclass );
4041 netclass->SetTrackWidth( getKiCadLength( rc.OptimalWidth ) );
4042 m_netClassMap.insert( { key, netclass } );
4043 }
4044 else
4045 {
4046 // No route code specified, route code not found, or it has no usable width; use the
4047 // default netclass
4048 netclass = netSettings->GetDefaultNetclass();
4049 }
4050
4051 m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment( newName, netclass->GetName() );
4052
4053 netInfo->SetNetClass( netclass );
4054 m_board->Add( netInfo, ADD_MODE::APPEND );
4055 m_netMap.insert( { aCadstarNetID, netInfo } );
4056 return netInfo;
4057 }
4058}
4059
4060
4061PCB_LAYER_ID CADSTAR_PCB_ARCHIVE_LOADER::getKiCadCopperLayerID( unsigned int aLayerNum, bool aDetectMaxLayer )
4062{
4063 if( aDetectMaxLayer && aLayerNum == (unsigned int) m_numCopperLayers )
4064 return PCB_LAYER_ID::B_Cu;
4065
4066 switch( aLayerNum )
4067 {
4068 case 1: return PCB_LAYER_ID::F_Cu;
4069 case 2: return PCB_LAYER_ID::In1_Cu;
4070 case 3: return PCB_LAYER_ID::In2_Cu;
4071 case 4: return PCB_LAYER_ID::In3_Cu;
4072 case 5: return PCB_LAYER_ID::In4_Cu;
4073 case 6: return PCB_LAYER_ID::In5_Cu;
4074 case 7: return PCB_LAYER_ID::In6_Cu;
4075 case 8: return PCB_LAYER_ID::In7_Cu;
4076 case 9: return PCB_LAYER_ID::In8_Cu;
4077 case 10: return PCB_LAYER_ID::In9_Cu;
4078 case 11: return PCB_LAYER_ID::In10_Cu;
4079 case 12: return PCB_LAYER_ID::In11_Cu;
4080 case 13: return PCB_LAYER_ID::In12_Cu;
4081 case 14: return PCB_LAYER_ID::In13_Cu;
4082 case 15: return PCB_LAYER_ID::In14_Cu;
4083 case 16: return PCB_LAYER_ID::In15_Cu;
4084 case 17: return PCB_LAYER_ID::In16_Cu;
4085 case 18: return PCB_LAYER_ID::In17_Cu;
4086 case 19: return PCB_LAYER_ID::In18_Cu;
4087 case 20: return PCB_LAYER_ID::In19_Cu;
4088 case 21: return PCB_LAYER_ID::In20_Cu;
4089 case 22: return PCB_LAYER_ID::In21_Cu;
4090 case 23: return PCB_LAYER_ID::In22_Cu;
4091 case 24: return PCB_LAYER_ID::In23_Cu;
4092 case 25: return PCB_LAYER_ID::In24_Cu;
4093 case 26: return PCB_LAYER_ID::In25_Cu;
4094 case 27: return PCB_LAYER_ID::In26_Cu;
4095 case 28: return PCB_LAYER_ID::In27_Cu;
4096 case 29: return PCB_LAYER_ID::In28_Cu;
4097 case 30: return PCB_LAYER_ID::In29_Cu;
4098 case 31: return PCB_LAYER_ID::In30_Cu;
4099 case 32: return PCB_LAYER_ID::B_Cu;
4100 }
4101
4103}
4104
4105
4107{
4108 wxCHECK( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) != Assignments.Layerdefs.Layers.end(), false );
4109
4110 LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID );
4111
4112 switch( layer.Type )
4113 {
4114 case LAYER_TYPE::ALLDOC:
4117 return true;
4118
4119 default:
4120 return false;
4121 }
4122}
4123
4124
4126{
4127 if( getLayerType( aCadstarLayerID ) == LAYER_TYPE::NOLAYER )
4128 {
4129 //The "no layer" is common for CADSTAR documentation symbols
4130 //map it to undefined layer for later processing
4132 }
4133
4134 wxCHECK( m_layermap.find( aCadstarLayerID ) != m_layermap.end(), PCB_LAYER_ID::UNDEFINED_LAYER );
4135
4136 return m_layermap.at( aCadstarLayerID );
4137}
4138
4139
4141{
4142 LAYER_TYPE layerType = getLayerType( aCadstarLayerID );
4143
4144 switch( layerType )
4145 {
4146 case LAYER_TYPE::ALLDOC:
4147 return LSET( { PCB_LAYER_ID::Dwgs_User,
4152
4155
4164
4165 default:
4166 return LSET( { getKiCadLayer( aCadstarLayerID ) } );
4167 }
4168}
4169
4170
4171void CADSTAR_PCB_ARCHIVE_LOADER::addToGroup( const GROUP_ID& aCadstarGroupID, BOARD_ITEM* aKiCadItem )
4172{
4173 wxCHECK( m_groupMap.find( aCadstarGroupID ) != m_groupMap.end(), );
4174
4175 PCB_GROUP* parentGroup = m_groupMap.at( aCadstarGroupID );
4176 parentGroup->AddItem( aKiCadItem );
4177}
4178
4179
4181{
4182 wxString groupName = aName;
4183 int num = 0;
4184
4185 while( m_groupMap.find( groupName ) != m_groupMap.end() )
4186 groupName = aName + wxT( "_" ) + wxString::Format( wxT( "%i" ), ++num );
4187
4188 PCB_GROUP* docSymGroup = new PCB_GROUP( m_board );
4189 m_board->Add( docSymGroup );
4190 docSymGroup->SetName( groupName );
4191 GROUP_ID groupID( groupName );
4192 m_groupMap.insert( { groupID, docSymGroup } );
4193
4194 return groupID;
4195}
int index
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:144
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:128
LAYER_T
The allowed types of layers, same as Specctra DSN spec.
Definition board.h:242
@ LT_POWER
Definition board.h:245
@ LT_JUMPER
Definition board.h:247
@ LT_SIGNAL
Definition board.h:244
@ 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:982
#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 NETINFO_ITEM 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:418
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.
void drawCadstarVerticesAsShapes(const std::vector< VERTEX > &aCadstarVertices, const PCB_LAYER_ID &aKiCadLayer, int aLineThickness, LINE_STYLE aLineStyle, 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.
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)
void drawCadstarShape(const SHAPE &aCadstarShape, const PCB_LAYER_ID &aKiCadLayer, int aLineThickness, LINE_STYLE aLineStyle, 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)
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)
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.
LINE_STYLE getLineStyle(const LINECODE_ID &aCadstarLineCodeID)
int getKiCadHatchCodeGap(const HATCHCODE_ID &aCadstarHatchcodeID)
ROUTECODE getRouteCode(const ROUTECODE_ID &aCadstarRouteCodeID)
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:247
double Sin() const
Definition eda_angle.h:177
EDA_ANGLE Normalize180()
Definition eda_angle.h:273
double Cos() const
Definition eda_angle.h:196
void AddItem(EDA_ITEM *aItem)
Add item to group.
Definition eda_group.cpp:58
void SetName(const wxString &aName)
Definition eda_group.h:76
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:512
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:556
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:354
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
void MigrateLegacyBoldStrokeWidth()
Migrate a pre-v11 bold stroke text so its stored thickness holds the base (non-bold) width.
Definition eda_text.cpp:332
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:250
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:310
void SetKeepUpright(bool aKeepUpright)
Definition eda_text.cpp:386
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:471
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
void SetPosition(const VECTOR2I &aPos) override
void SetFPID(const LIB_ID &aFPID)
Definition footprint.h:475
void SetOrientation(const EDA_ANGLE &aNewAngle)
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:947
std::deque< PAD * > & Pads()
Definition footprint.h:405
void SetReference(const wxString &aReference)
Definition footprint.h:915
void SetValue(const wxString &aValue)
Definition footprint.h:938
PCB_FIELD & Reference()
Definition footprint.h:948
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Adds an item to 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:492
const wxString & GetReference() const
Definition footprint.h:909
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:1232
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition pad.cpp:265
void SetRoundRectCornerRadius(PCB_LAYER_ID aLayer, double aRadius)
Definition pad.cpp:1188
void SetChamferPositions(PCB_LAYER_ID aLayer, int aPositions)
Has meaning only for chamfered rectangular pads.
Definition pad.h:847
void SetRoundRectRadiusRatio(PCB_LAYER_ID aLayer, double aRadiusScale)
Has meaning only for rounded rectangle pads.
Definition pad.cpp:1194
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:631
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:572
VECTOR2I GetTextSize() const override
Definition pcb_text.cpp:470
void SetEnd(const VECTOR2I &aEnd)
Definition pcb_track.h:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
virtual void SetWidth(int aWidth)
Definition pcb_track.h:91
virtual int GetWidth() const
Definition pcb_track.h:92
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:296
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:281
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
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:851
std::optional< int > GetLocalClearance() const override
Definition zone.cpp:1062
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:1447
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:699
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:640
SHAPE_POLY_SET * Outline()
Definition zone.h:421
void SetHatchStyle(ZONE_BORDER_DISPLAY_STYLE aStyle)
Definition zone.h:688
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:706
void SetIsRuleArea(bool aEnable)
Definition zone.h:833
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:850
void SetFilledPolysList(PCB_LAYER_ID aLayer, const SHAPE_POLY_SET &aPolysList)
Set the list of filled polygons.
Definition zone.h:746
void SetIsFilled(bool isFilled)
Definition zone.h:307
void SetFillMode(ZONE_FILL_MODE aFillMode)
Definition zone.cpp:646
bool HasFilledPolysForLayer(PCB_LAYER_ID aLayer) const
Definition zone.h:690
void SetLayerSet(const LSET &aLayerSet) override
Definition zone.cpp:665
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:849
void SetNet(NETINFO_ITEM *aNetInfo) override
Override that drops aNetInfo when this zone is in copper-thieving fill mode.
Definition zone.cpp:631
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:852
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:848
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:855
void SetMinIslandArea(long long int aArea)
Definition zone.h:858
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:450
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:29
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
@ 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:172
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ 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
LINE_STYLE
Dashed line types.
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...
SHAPE Shape
Uses the component's coordinate frame if within a component definition, otherwise uses the design's c...
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.
static void addTrack(BOARD &aBoard, int aNetCode, PCB_LAYER_ID aLayer, const VECTOR2I &aStart, const VECTOR2I &aEnd)
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:227
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:708
@ THERMAL
Use thermal relief for pads.
Definition zones.h:46
@ FULL
pads are covered by copper
Definition zones.h:47