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