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