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