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