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