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