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