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