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