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