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