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 <roberto.fer.bau@gmail.com>
5  * Copyright (C) 2020-2021 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 
21 /**
22  * @file cadstar_sch_archive_loader.cpp
23  * @brief Loads a csa file into a KiCad SCHEMATIC object
24  */
25 
26 #include <sch_plugins/cadstar/cadstar_sch_archive_loader.h>
27 
28 #include <bus_alias.h>
29 #include <core/mirror.h>
30 #include <eda_text.h>
31 #include <lib_shape.h>
32 #include <lib_text.h>
33 #include <macros.h>
34 #include <progress_reporter.h>
35 #include <string_utils.h>
36 #include <sch_bus_entry.h>
37 #include <sch_edit_frame.h> //SYMBOL_ORIENTATION_T
38 #include <sch_io_mgr.h>
39 #include <sch_junction.h>
40 #include <sch_line.h>
41 #include <sch_screen.h>
42 #include <sch_sheet.h>
43 #include <sch_sheet_path.h>
44 #include <sch_sheet_pin.h>
45 #include <sch_text.h>
46 #include <schematic.h>
47 #include <trigo.h>
48 #include <wildcards_and_files_ext.h>
49 
50 
51 const wxString PartNameFieldName = "Part Name";
52 
53 
Load(SCHEMATIC * aSchematic,SCH_SHEET * aRootSheet,SCH_PLUGIN::SCH_PLUGIN_RELEASER * aSchPlugin,const wxFileName & aLibraryFileName)54 void CADSTAR_SCH_ARCHIVE_LOADER::Load( SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet,
55         SCH_PLUGIN::SCH_PLUGIN_RELEASER* aSchPlugin, const wxFileName& aLibraryFileName )
56 {
57     if( m_progressReporter )
58         m_progressReporter->SetNumPhases( 3 ); // (0) Read file, (1) Parse file, (2) Load file
59 
60     Parse();
61 
62     LONGPOINT designLimit = Assignments.Settings.DesignLimit;
63 
64     //Note: can't use getKiCadPoint() due wxPoint being int - need long long to make the check
65     long long designSizeXkicad = (long long) designLimit.x / KiCadUnitDivider;
66     long long designSizeYkicad = (long long) designLimit.y / KiCadUnitDivider;
67 
68     // Max size limited by the positive dimension of wxPoint (which is an int)
69     constexpr long long maxDesignSizekicad = std::numeric_limits<int>::max();
70 
71     if( designSizeXkicad > maxDesignSizekicad || designSizeYkicad > maxDesignSizekicad )
72     {
73         THROW_IO_ERROR( wxString::Format(
74                 _( "The design is too large and cannot be imported into KiCad. \n"
75                    "Please reduce the maximum design size in CADSTAR by navigating to: \n"
76                    "Design Tab -> Properties -> Design Options -> Maximum Design Size. \n"
77                    "Current Design size: %.2f, %.2f millimeters. \n"
78                    "Maximum permitted design size: %.2f, %.2f millimeters.\n" ),
79                 (double) designSizeXkicad / SCH_IU_PER_MM,
80                 (double) designSizeYkicad / SCH_IU_PER_MM,
81                 (double) maxDesignSizekicad / SCH_IU_PER_MM,
82                 (double) maxDesignSizekicad / SCH_IU_PER_MM ) );
83     }
84 
85     // Assume the center at 0,0 since we are going to be translating the design afterwards anyway
86     m_designCenter = { 0, 0 };
87 
88     m_schematic       = aSchematic;
89     m_rootSheet       = aRootSheet;
90     m_plugin          = aSchPlugin;
91     m_libraryFileName = aLibraryFileName;
92 
93     if( m_progressReporter )
94     {
95         m_progressReporter->BeginPhase( 2 );
96         long numSteps = 11; // one step for each of below functions + one at the end of import
97 
98         // Step 4 is by far the longest - add granularity in reporting
99         numSteps += Parts.PartDefinitions.size();
100 
101         m_progressReporter->SetMaxProgress( numSteps );
102     }
103 
104     loadTextVariables(); // Load text variables right at the start to ensure bounding box
105                          // calculations work correctly for text items
106     checkPoint(); // Step 1
107     loadSheets();
108     checkPoint(); // Step 2
109     loadHierarchicalSheetPins();
110     checkPoint(); // Step 3
111     loadPartsLibrary();
112     checkPoint(); // Step 4, Subdivided into extra steps
113     loadSchematicSymbolInstances();
114     checkPoint(); // Step 5
115     loadBusses();
116     checkPoint(); // Step 6
117     loadNets();
118     checkPoint(); // Step 7
119     loadFigures();
120     checkPoint(); // Step 8
121     loadTexts();
122     checkPoint(); // Step 9
123     loadDocumentationSymbols();
124     checkPoint(); // Step 10
125 
126     if( Schematic.VariantHierarchy.Variants.size() > 0 )
127     {
128         m_reporter->Report( wxString::Format( _( "The CADSTAR design contains variants which has "
129                                                  "no KiCad equivalent. Only the master variant "
130                                                  "('%s') was loaded." ),
131                                               Schematic.VariantHierarchy.Variants.at( "V0" ).Name ),
132                             RPT_SEVERITY_WARNING );
133     }
134 
135     if( Schematic.Groups.size() > 0 )
136     {
137         m_reporter->Report( _( "The CADSTAR design contains grouped items which has no KiCad "
138                                "equivalent. Any grouped items have been ungrouped." ),
139                             RPT_SEVERITY_WARNING );
140     }
141 
142     if( Schematic.ReuseBlocks.size() > 0 )
143     {
144         m_reporter->Report( _( "The CADSTAR design contains re-use blocks which has no KiCad "
145                                "equivalent. The re-use block information has been discarded during "
146                                "the import." ),
147                             RPT_SEVERITY_WARNING  );
148     }
149 
150 
151     // For all sheets, center all elements and re calculate the page size:
152     for( std::pair<LAYER_ID, SCH_SHEET*> sheetPair : m_sheetMap )
153     {
154         SCH_SHEET* sheet = sheetPair.second;
155 
156         // Calculate the new sheet size.
157         EDA_RECT sheetBoundingBox;
158 
159         for( auto item : sheet->GetScreen()->Items() )
160         {
161             EDA_RECT bbox;
162 
163             // Only use the visible fields of the symbols to calculate their bounding box
164             // (hidden fields could be very long and artificially enlarge the sheet bounding box)
165             if( item->Type() == SCH_SYMBOL_T )
166             {
167                 SCH_SYMBOL* comp = static_cast<SCH_SYMBOL*>( item );
168                 bbox = comp->GetBodyAndPinsBoundingBox();
169 
170                 for( const SCH_FIELD& field : comp->GetFields() )
171                 {
172                     if( field.IsVisible() )
173                         bbox.Merge( field.GetBoundingBox() );
174                 }
175             }
176             else if( item->Type() == SCH_TEXT_T )
177             {
178                 SCH_TEXT* txtItem = static_cast<SCH_TEXT*>( item );
179                 wxString  txt = txtItem->GetText();
180 
181                 if( txt.Contains( "${" ) )
182                     continue; // We can't calculate bounding box of text items with variables
183                 else
184                     bbox = txtItem->GetBoundingBox();
185             }
186             else
187             {
188                 bbox = item->GetBoundingBox();
189             }
190 
191             sheetBoundingBox.Merge( bbox );
192         }
193 
194         // Find the working grid of the original CADSTAR design
195         int grid = Assignments.Grids.WorkingGrid.Param1;
196 
197         if( Assignments.Grids.WorkingGrid.Type == GRID_TYPE::FRACTIONALGRID )
198             grid = grid / Assignments.Grids.WorkingGrid.Param2;
199         else if( Assignments.Grids.WorkingGrid.Param2 > grid )
200             grid = Assignments.Grids.WorkingGrid.Param2;
201 
202         grid = getKiCadLength( grid );
203 
204         auto roundToNearestGrid =
205             [&]( int aNumber ) -> int
206             {
207                 int error = aNumber % grid;
208                 int absError = sign( error ) * error;
209 
210                 if( absError > ( grid / 2 ) )
211                  return aNumber + ( sign( error ) * grid ) - error;
212                 else
213                   return aNumber - error;
214             };
215 
216         // When exporting to pdf, CADSTAR applies a margin of 3% of the longest dimension (height
217         // or width) to all 4 sides (top, bottom, left right). For the import, we are also rounding
218         // the margin to the nearest grid, ensuring all items remain on the grid.
219         wxSize targetSheetSize = sheetBoundingBox.GetSize();
220         int    longestSide = std::max( targetSheetSize.x, targetSheetSize.y );
221         int    margin = ( (double) longestSide * 0.03);
222         margin = roundToNearestGrid( margin );
223         targetSheetSize.IncBy( margin * 2, margin * 2 );
224 
225         // Update page size always
226         PAGE_INFO pageInfo   = sheet->GetScreen()->GetPageSettings();
227         pageInfo.SetWidthMils( Iu2Mils( targetSheetSize.x ) );
228         pageInfo.SetHeightMils( Iu2Mils( targetSheetSize.y ) );
229 
230         // Set the new sheet size.
231         sheet->GetScreen()->SetPageSettings( pageInfo );
232 
233         wxSize  pageSizeIU = sheet->GetScreen()->GetPageSettings().GetSizeIU();
234         wxPoint sheetcentre( pageSizeIU.x / 2, pageSizeIU.y / 2 );
235         wxPoint itemsCentre = sheetBoundingBox.Centre();
236 
237         // round the translation to nearest point on the grid
238         wxPoint translation = sheetcentre - itemsCentre;
239         translation.x = roundToNearestGrid( translation.x );
240         translation.y = roundToNearestGrid( translation.y );
241 
242         // Translate the items.
243         std::vector<SCH_ITEM*> allItems;
244 
245         std::copy( sheet->GetScreen()->Items().begin(), sheet->GetScreen()->Items().end(),
246                 std::back_inserter( allItems ) );
247 
248         for( SCH_ITEM* item : allItems )
249         {
250             item->Move( translation );
251             item->ClearFlags();
252             sheet->GetScreen()->Update( item );
253         }
254     }
255 
256     checkPoint();
257 
258     m_reporter->Report( _( "The CADSTAR design has been imported successfully.\n"
259                            "Please review the import errors and warnings (if any)." ) );
260 }
261 
262 
loadSheets()263 void CADSTAR_SCH_ARCHIVE_LOADER::loadSheets()
264 {
265     const std::vector<LAYER_ID>& orphanSheets = findOrphanSheets();
266     SCH_SHEET_PATH               rootPath;
267     rootPath.push_back( m_rootSheet );
268     m_rootSheet->AddInstance( rootPath );
269     m_rootSheet->SetPageNumber( rootPath, wxT( "1" ) );
270 
271     if( orphanSheets.size() > 1 )
272     {
273         int x = 1;
274         int y = 1;
275 
276         for( LAYER_ID sheetID : orphanSheets )
277         {
278             wxPoint pos( x * Mils2iu( 1000 ), y * Mils2iu( 1000 ) );
279             wxSize  siz( Mils2iu( 1000 ), Mils2iu( 1000 ) );
280 
281             loadSheetAndChildSheets( sheetID, pos, siz, rootPath );
282 
283             x += 2;
284 
285             if( x > 10 ) // start next row
286             {
287                 x = 1;
288                 y += 2;
289             }
290         }
291     }
292     else if( orphanSheets.size() > 0 )
293     {
294         LAYER_ID rootSheetID = orphanSheets.at( 0 );
295 
296         wxFileName loadedFilePath = wxFileName( Filename );
297 
298         std::string filename = wxString::Format(
299                 "%s_%02d", loadedFilePath.GetName(), getSheetNumber( rootSheetID ) )
300                                        .ToStdString();
301         ReplaceIllegalFileNameChars( &filename );
302         filename += wxT( "." ) + KiCadSchematicFileExtension;
303 
304         wxFileName fn( m_schematic->Prj().GetProjectPath() + filename );
305         m_rootSheet->GetScreen()->SetFileName( fn.GetFullPath() );
306 
307         m_sheetMap.insert( { rootSheetID, m_rootSheet } );
308         loadChildSheets( rootSheetID, rootPath );
309     }
310     else
311     {
312         THROW_IO_ERROR( _( "The CADSTAR schematic might be corrupt: there is no root sheet." ) );
313     }
314 }
315 
316 
loadHierarchicalSheetPins()317 void CADSTAR_SCH_ARCHIVE_LOADER::loadHierarchicalSheetPins()
318 {
319     for( std::pair<BLOCK_ID, BLOCK> blockPair : Schematic.Blocks )
320     {
321         BLOCK&   block   = blockPair.second;
322         LAYER_ID sheetID = "";
323 
324         if( block.Type == BLOCK::TYPE::PARENT )
325             sheetID = block.LayerID;
326         else if( block.Type == BLOCK::TYPE::CHILD )
327             sheetID = block.AssocLayerID;
328         else
329             continue;
330 
331         if( m_sheetMap.find( sheetID ) != m_sheetMap.end() )
332         {
333             SCH_SHEET* sheet = m_sheetMap.at( sheetID );
334 
335             for( std::pair<TERMINAL_ID, TERMINAL> termPair : block.Terminals )
336             {
337                 TERMINAL term = termPair.second;
338                 wxString name = "YOU SHOULDN'T SEE THIS TEXT. THIS IS A BUG.";
339 
340                 SCH_HIERLABEL* sheetPin = nullptr;
341 
342                 if( block.Type == BLOCK::TYPE::PARENT )
343                     sheetPin = new SCH_HIERLABEL();
344                 else if( block.Type == BLOCK::TYPE::CHILD )
345                     sheetPin = new SCH_SHEET_PIN( sheet );
346 
347                 sheetPin->SetText( name );
348                 sheetPin->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );
349                 sheetPin->SetLabelSpinStyle( getSpinStyle( term.OrientAngle, false ) );
350                 sheetPin->SetPosition( getKiCadPoint( term.Position ) );
351 
352                 if( sheetPin->Type() == SCH_SHEET_PIN_T )
353                     sheet->AddPin( (SCH_SHEET_PIN*) sheetPin );
354                 else
355                     sheet->GetScreen()->Append( sheetPin );
356 
357                 BLOCK_PIN_ID blockPinID = std::make_pair( block.ID, term.ID );
358                 m_sheetPinMap.insert( { blockPinID, sheetPin } );
359             }
360         }
361     }
362 }
363 
364 
loadPartsLibrary()365 void CADSTAR_SCH_ARCHIVE_LOADER::loadPartsLibrary()
366 {
367     for( std::pair<PART_ID, PART> partPair : Parts.PartDefinitions )
368     {
369         PART_ID partID  = partPair.first;
370         PART    part = partPair.second;
371 
372         if( part.Definition.GateSymbols.size() == 0 )
373             continue;
374 
375         wxString    escapedPartName = EscapeString( part.Name, CTX_LIBID );
376         LIB_SYMBOL* kiPart = new LIB_SYMBOL( escapedPartName );
377 
378         kiPart->SetUnitCount( part.Definition.GateSymbols.size() );
379         bool ok = true;
380 
381         for( std::pair<GATE_ID, PART::DEFINITION::GATE> gatePair : part.Definition.GateSymbols )
382         {
383             GATE_ID                gateID   = gatePair.first;
384             PART::DEFINITION::GATE gate     = gatePair.second;
385             SYMDEF_ID              symbolID = getSymDefFromName( gate.Name, gate.Alternate );
386             m_partSymbolsMap.insert( { { partID, gateID }, symbolID } );
387 
388             if( symbolID.IsEmpty() )
389             {
390                 m_reporter->Report( wxString::Format( _( "Part definition '%s' references symbol "
391                                                          "'%s' (alternate '%s') which could not be "
392                                                          "found in the symbol library. The part has "
393                                                          "not been loaded into the KiCad library." ),
394                                                       part.Name,
395                                                       gate.Name,
396                                                       gate.Alternate ),
397                                     RPT_SEVERITY_WARNING);
398 
399                 ok = false;
400                 break;
401             }
402 
403             loadSymDefIntoLibrary( symbolID, &part, gateID, kiPart );
404         }
405 
406         if( ok )
407         {
408             ( *m_plugin )->SaveSymbol( m_libraryFileName.GetFullPath(), kiPart );
409 
410             LIB_SYMBOL* loadedPart =
411                     ( *m_plugin )->LoadSymbol( m_libraryFileName.GetFullPath(), kiPart->GetName() );
412 
413             m_partMap.insert( { partID, loadedPart } );
414         }
415         else
416         {
417             // Don't save in the library, but still keep it cached as some of the units might have
418             // been loaded correctly (saving us time later on)
419             m_partMap.insert( { partID, kiPart } );
420         }
421 
422         checkPoint();
423     }
424 }
425 
426 
loadSchematicSymbolInstances()427 void CADSTAR_SCH_ARCHIVE_LOADER::loadSchematicSymbolInstances()
428 {
429     for( std::pair<SYMBOL_ID, SYMBOL> symPair : Schematic.Symbols )
430     {
431         SYMBOL sym = symPair.second;
432 
433         if( !sym.VariantID.empty() && sym.VariantParentSymbolID != sym.ID )
434             continue; // Only load master Variant
435 
436         if( sym.IsComponent )
437         {
438             if( m_partMap.find( sym.PartRef.RefID ) == m_partMap.end() )
439             {
440                 m_reporter->Report( wxString::Format( _( "Symbol '%s' references part '%s' which "
441                                                          "could not be found in the library. The "
442                                                          "symbol was not loaded" ),
443                                                       sym.ComponentRef.Designator,
444                                                       sym.PartRef.RefID ),
445                                     RPT_SEVERITY_ERROR);
446 
447                 continue;
448             }
449 
450             if( sym.GateID.IsEmpty() )
451                 sym.GateID = wxT( "A" ); // Assume Gate "A" if unspecified
452 
453             PART_GATE_ID partSymbolID = { sym.PartRef.RefID, sym.GateID };
454             LIB_SYMBOL*  kiPart = m_partMap.at( sym.PartRef.RefID );
455             bool         copy = false;
456 
457             // The symbol definition in the part either does not exist for this gate number
458             // or is different to the symbol instance. We need to load a new symbol
459             if( m_partSymbolsMap.find( partSymbolID ) == m_partSymbolsMap.end()
460                 || m_partSymbolsMap.at( partSymbolID ) != sym.SymdefID )
461             {
462                 kiPart = new LIB_SYMBOL( *kiPart ); // Make a copy
463                 copy = true;
464                 const PART& part = Parts.PartDefinitions.at( sym.PartRef.RefID );
465                 loadSymDefIntoLibrary( sym.SymdefID, &part, sym.GateID, kiPart );
466             }
467 
468             LIB_SYMBOL* scaledPart = getScaledLibPart( kiPart, sym.ScaleRatioNumerator,
469                                                        sym.ScaleRatioDenominator );
470 
471             double      symOrientDeciDeg = 0.0;
472             SCH_SYMBOL* symbol = loadSchematicSymbol( sym, *scaledPart, symOrientDeciDeg );
473 
474             delete scaledPart;
475 
476             if( copy )
477                 delete kiPart;
478 
479             SCH_FIELD* refField = symbol->GetField( REFERENCE_FIELD );
480 
481             sym.ComponentRef.Designator.Replace( wxT( "\n" ), wxT( "\\n" ) );
482             sym.ComponentRef.Designator.Replace( wxT( "\r" ), wxT( "\\r" ) );
483             sym.ComponentRef.Designator.Replace( wxT( "\t" ), wxT( "\\t" ) );
484             sym.ComponentRef.Designator.Replace( wxT( " " ), wxT( "_" ) );
485 
486             refField->SetText( sym.ComponentRef.Designator );
487             loadSymbolFieldAttribute( sym.ComponentRef.AttrLoc, symOrientDeciDeg,
488                                       sym.Mirror, refField );
489 
490             if( sym.HasPartRef )
491             {
492                 SCH_FIELD* partField = symbol->FindField( PartNameFieldName );
493 
494                 if( !partField )
495                 {
496                     int fieldID = symbol->GetFieldCount();
497                     partField = symbol->AddField( SCH_FIELD( wxPoint(), fieldID, symbol,
498                                                              PartNameFieldName ) );
499                 }
500 
501                 wxASSERT( partField->GetName() == PartNameFieldName );
502 
503                 wxString partname = getPart( sym.PartRef.RefID ).Name;
504                 partname.Replace( wxT( "\n" ), wxT( "\\n" ) );
505                 partname.Replace( wxT( "\r" ), wxT( "\\r" ) );
506                 partname.Replace( wxT( "\t" ), wxT( "\\t" ) );
507                 partField->SetText( partname );
508 
509                 loadSymbolFieldAttribute( sym.PartRef.AttrLoc, symOrientDeciDeg,
510                                           sym.Mirror, partField );
511 
512                 partField->SetVisible( SymbolPartNameColor.IsVisible );
513             }
514 
515             for( auto attr : sym.AttributeValues )
516             {
517                 ATTRIBUTE_VALUE attrVal = attr.second;
518 
519                 if( attrVal.HasLocation )
520                 {
521                     wxString attrName = getAttributeName( attrVal.AttributeID );
522                     SCH_FIELD* attrField = symbol->FindField( attrName );
523 
524                     if( !attrField )
525                     {
526                         int fieldID = symbol->GetFieldCount();
527                         attrField = symbol->AddField( SCH_FIELD( wxPoint(), fieldID,
528                                                                  symbol, attrName ) );
529                     }
530 
531                     wxASSERT( attrField->GetName() == attrName );
532 
533                     attrVal.Value.Replace( wxT( "\n" ), wxT( "\\n" ) );
534                     attrVal.Value.Replace( wxT( "\r" ), wxT( "\\r" ) );
535                     attrVal.Value.Replace( wxT( "\t" ), wxT( "\\t" ) );
536                     attrField->SetText( attrVal.Value );
537 
538                     loadSymbolFieldAttribute( attrVal.AttributeLocation, symOrientDeciDeg,
539                                               sym.Mirror, attrField );
540                     attrField->SetVisible( isAttributeVisible( attrVal.AttributeID ) );
541                 }
542             }
543         }
544         else if( sym.IsSymbolVariant )
545         {
546             if( Library.SymbolDefinitions.find( sym.SymdefID ) == Library.SymbolDefinitions.end() )
547             {
548                 THROW_IO_ERROR( wxString::Format(
549                         _( "Symbol ID '%s' references library symbol '%s' which could not be "
550                            "found in the library. Did you export all items of the design?" ),
551                         sym.ID, sym.PartRef.RefID ) );
552             }
553 
554             SYMDEF_SCM libSymDef = Library.SymbolDefinitions.at( sym.SymdefID );
555 
556             if( libSymDef.Terminals.size() != 1 )
557             {
558                 THROW_IO_ERROR( wxString::Format(
559                         _( "Symbol ID '%s' is a signal reference or global signal but it has too "
560                            "many pins. The expected number of pins is 1 but %d were found." ),
561                         sym.ID, libSymDef.Terminals.size() ) );
562             }
563 
564             if( sym.SymbolVariant.Type == SYMBOLVARIANT::TYPE::GLOBALSIGNAL )
565             {
566                 SYMDEF_ID symID  = sym.SymdefID;
567                 LIB_SYMBOL* kiPart = nullptr;
568 
569                 // In CADSTAR "GlobalSignal" is a special type of symbol which defines
570                 // a Power Symbol. The "Alternate" name defines the default net name of
571                 // the power symbol but this can be overridden in the design itself.
572                 wxString libraryNetName = Library.SymbolDefinitions.at( symID ).Alternate;
573 
574                 // Name of the net that the symbol instance in CADSTAR refers to:
575                 wxString symbolInstanceNetName = sym.SymbolVariant.Reference;
576                 symbolInstanceNetName = EscapeString( symbolInstanceNetName, CTX_LIBID );
577 
578                 // Name of the symbol we will use for saving the part in KiCad
579                 // Note: In CADSTAR all power symbols will start have the reference name be
580                 // "GLOBALSIGNAL" followed by the default net name, so it makes sense to save
581                 // the symbol in KiCad as the default net name as well.
582                 wxString libPartName = libraryNetName;
583 
584                 // In CADSTAR power symbol instances can refer to a different net to that defined
585                 // in the library. This causes problems in KiCad v6 as it breaks connectivity when
586                 // the user decides to update all symbols from library. We handle this by creating
587                 // individual versions of the power symbol for each net name.
588                 if( libPartName != symbolInstanceNetName )
589                 {
590                     libPartName += wxT( " (" ) + symbolInstanceNetName + wxT( ")" );
591                 }
592 
593                 if( m_powerSymLibMap.find( libPartName ) == m_powerSymLibMap.end() )
594                 {
595                     SYMDEF_SCM symbolDef = Library.SymbolDefinitions.at( symID );
596 
597                     kiPart = new LIB_SYMBOL( libPartName );
598                     kiPart->SetPower();
599                     loadSymDefIntoLibrary( symID, nullptr, "A", kiPart );
600 
601                     kiPart->GetValueField().SetText( symbolInstanceNetName );
602 
603                     if( symbolDef.TextLocations.find( SIGNALNAME_ORIGIN_ATTRID )
604                             != symbolDef.TextLocations.end() )
605                     {
606                         TEXT_LOCATION txtLoc =
607                                 symbolDef.TextLocations.at( SIGNALNAME_ORIGIN_ATTRID );
608 
609                         wxPoint valPos = getKiCadLibraryPoint( txtLoc.Position, symbolDef.Origin );
610 
611                         kiPart->GetValueField().SetPosition( valPos );
612                         kiPart->GetValueField().SetVisible( true );
613                     }
614                     else
615                     {
616                         kiPart->GetValueField().SetVisible( false );
617                     }
618 
619                     kiPart->GetReferenceField().SetText( "#PWR" );
620                     kiPart->GetReferenceField().SetVisible( false );
621                     ( *m_plugin )->SaveSymbol( m_libraryFileName.GetFullPath(), kiPart );
622                     m_powerSymLibMap.insert( { libPartName, kiPart } );
623                 }
624                 else
625                 {
626                     kiPart = m_powerSymLibMap.at( libPartName );
627                     wxASSERT( kiPart->GetValueField().GetText() == symbolInstanceNetName );
628                 }
629 
630                 LIB_SYMBOL* scaledPart = getScaledLibPart( kiPart, sym.ScaleRatioNumerator,
631                                                            sym.ScaleRatioDenominator );
632 
633                 double returnedOrient = 0.0;
634                 SCH_SYMBOL* symbol = loadSchematicSymbol( sym, *scaledPart, returnedOrient );
635                 m_powerSymMap.insert( { sym.ID, symbol } );
636 
637                 delete scaledPart;
638             }
639             else if( sym.SymbolVariant.Type == SYMBOLVARIANT::TYPE::SIGNALREF )
640             {
641                 // There should only be one pin and we'll use that to set the position
642                 TERMINAL& symbolTerminal = libSymDef.Terminals.begin()->second;
643                 wxPoint   terminalPosOffset = symbolTerminal.Position - libSymDef.Origin;
644                 double    rotateDeciDegree = getAngleTenthDegree( sym.OrientAngle );
645 
646                 if( sym.Mirror )
647                     rotateDeciDegree += 1800.0;
648 
649                 RotatePoint( &terminalPosOffset, -rotateDeciDegree );
650 
651                 SCH_GLOBALLABEL* netLabel = new SCH_GLOBALLABEL;
652                 netLabel->SetPosition( getKiCadPoint( sym.Origin + terminalPosOffset ) );
653                 netLabel->SetText( "***UNKNOWN NET****" ); // This should be later updated when we load the netlist
654                 netLabel->SetTextSize( wxSize( Mils2iu( 50 ), Mils2iu( 50 ) ) );
655 
656                 SYMDEF_SCM symbolDef = Library.SymbolDefinitions.at( sym.SymdefID );
657 
658                 if( symbolDef.TextLocations.count( LINK_ORIGIN_ATTRID ) )
659                 {
660                     TEXT_LOCATION linkOrigin = symbolDef.TextLocations.at( LINK_ORIGIN_ATTRID );
661                     applyTextSettings( netLabel, linkOrigin.TextCodeID, linkOrigin.Alignment,
662                                        linkOrigin.Justification );
663                 }
664 
665                 netLabel->SetLabelSpinStyle( getSpinStyle( sym.OrientAngle, sym.Mirror ) );
666 
667                 if( libSymDef.Alternate.Lower().Contains( "in" ) )
668                     netLabel->SetShape( PINSHEETLABEL_SHAPE::PS_INPUT );
669                 else if( libSymDef.Alternate.Lower().Contains( "bi" ) )
670                     netLabel->SetShape( PINSHEETLABEL_SHAPE::PS_BIDI );
671                 else if( libSymDef.Alternate.Lower().Contains( "out" ) )
672                     netLabel->SetShape( PINSHEETLABEL_SHAPE::PS_OUTPUT );
673                 else
674                     netLabel->SetShape( PINSHEETLABEL_SHAPE::PS_UNSPECIFIED );
675 
676                 SCH_SCREEN* screen = m_sheetMap.at( sym.LayerID )->GetScreen();
677 
678                 // autoplace intersheet refs
679                 netLabel->AutoplaceFields( screen, false );
680 
681                 screen->Append( netLabel );
682                 m_globalLabelsMap.insert( { sym.ID, netLabel } );
683             }
684             else
685             {
686                 wxASSERT_MSG( false, "Unknown Symbol Variant." );
687             }
688         }
689         else
690         {
691             m_reporter->Report( wxString::Format( _( "Symbol ID '%s' is of an unknown type. It is "
692                                                      "neither a symbol or a net power / symbol. "
693                                                      "The symbol was not loaded." ),
694                                                   sym.ID ),
695                                 RPT_SEVERITY_ERROR );
696         }
697 
698         if( sym.ScaleRatioDenominator != 1 || sym.ScaleRatioNumerator != 1 )
699         {
700             wxString symbolName = sym.ComponentRef.Designator;
701 
702             if( symbolName.empty() )
703                 symbolName = wxString::Format( "ID: %s", sym.ID );
704             else
705                 symbolName += sym.GateID;
706 
707             m_reporter->Report( wxString::Format( _( "Symbol '%s' is scaled in the original "
708                                                      "CADSTAR schematic but this is not supported "
709                                                      "in KiCad. When the symbol is reloaded from "
710                                                      "the library, it will revert to the original "
711                                                      "1:1 scale." ),
712                                                   symbolName,
713                                                   sym.PartRef.RefID ),
714                                 RPT_SEVERITY_ERROR );
715         }
716     }
717 }
718 
719 
loadBusses()720 void CADSTAR_SCH_ARCHIVE_LOADER::loadBusses()
721 {
722     for( std::pair<BUS_ID, BUS> busPair : Schematic.Buses )
723     {
724         BUS    bus     = busPair.second;
725         bool   firstPt = true;
726         VERTEX last;
727 
728         if( bus.LayerID != wxT( "NO_SHEET" ) )
729         {
730             SCH_SCREEN*                screen     = m_sheetMap.at( bus.LayerID )->GetScreen();
731             std::shared_ptr<BUS_ALIAS> kiBusAlias = std::make_shared<BUS_ALIAS>();
732 
733             kiBusAlias->SetName( bus.Name );
734             kiBusAlias->SetParent( screen );
735             screen->AddBusAlias( kiBusAlias );
736             m_busesMap.insert( { bus.ID, kiBusAlias } );
737 
738             SCH_LABEL* label = new SCH_LABEL();
739 
740             wxString busname = HandleTextOverbar( bus.Name );
741 
742             label->SetText( wxT( "{" ) + busname + wxT( "}" ) );
743             label->SetVisible( true );
744             screen->Append( label );
745 
746             SHAPE_LINE_CHAIN busLineChain; // to compute nearest segment to bus label
747 
748             for( const VERTEX& cur : bus.Shape.Vertices )
749             {
750                 busLineChain.Append( getKiCadPoint( cur.End ) );
751 
752                 if( firstPt )
753                 {
754                     last    = cur;
755                     firstPt = false;
756 
757                     if( !bus.HasBusLabel )
758                     {
759                         // Add a bus label on the starting point if the original CADSTAR design
760                         // does not have an explicit label
761                         label->SetPosition( getKiCadPoint( last.End ) );
762                     }
763 
764                     continue;
765                 }
766 
767 
768                 SCH_LINE* kiBus = new SCH_LINE();
769 
770                 kiBus->SetStartPoint( getKiCadPoint( last.End ) );
771                 kiBus->SetEndPoint( getKiCadPoint( cur.End ) );
772                 kiBus->SetLayer( LAYER_BUS );
773                 kiBus->SetLineWidth( getLineThickness( bus.LineCodeID ) );
774                 screen->Append( kiBus );
775 
776                 last = cur;
777             }
778 
779             if( bus.HasBusLabel )
780             {
781                 //lets find the closest point in the busline to the label
782                 VECTOR2I busLabelLoc = getKiCadPoint( bus.BusLabel.Position );
783                 wxPoint  nearestPt   = (wxPoint) busLineChain.NearestPoint( busLabelLoc );
784 
785                 label->SetPosition( nearestPt );
786 
787                 applyTextSettings( label,
788                                    bus.BusLabel.TextCodeID,
789                                    bus.BusLabel.Alignment,
790                                    bus.BusLabel.Justification );
791 
792                 // Re-set bus name as it might have been "double-escaped" after applyTextSettings
793                 label->SetText( wxT( "{" ) + busname + wxT( "}" ) );
794 
795                 // Note orientation of the bus label will be determined in loadNets
796                 // (the position of the wire will determine how best to place the bus label)
797             }
798         }
799 
800     }
801 }
802 
803 
loadNets()804 void CADSTAR_SCH_ARCHIVE_LOADER::loadNets()
805 {
806     for( std::pair<NET_ID, NET_SCH> netPair : Schematic.Nets )
807     {
808         NET_SCH                             net     = netPair.second;
809         wxString                            netName = net.Name;
810         std::map<NETELEMENT_ID, SCH_LABEL*> netlabels;
811 
812         if( netName.IsEmpty() )
813             netName = wxString::Format( "$%ld", net.SignalNum );
814 
815         netName = HandleTextOverbar( netName );
816 
817         for( std::pair<NETELEMENT_ID, NET_SCH::SYM_TERM> terminalPair : net.Terminals )
818         {
819             NET_SCH::SYM_TERM netTerm = terminalPair.second;
820 
821             if( m_powerSymMap.find( netTerm.SymbolID ) != m_powerSymMap.end() )
822             {
823                 SCH_FIELD* val = m_powerSymMap.at( netTerm.SymbolID )->GetField( VALUE_FIELD );
824                 val->SetText( netName );
825                 val->SetBold( false );
826                 val->SetVisible( false );
827 
828                 if( netTerm.HasNetLabel )
829                 {
830                     val->SetVisible( true );
831                     val->SetPosition( getKiCadPoint( netTerm.NetLabel.Position ) );
832 
833                     applyTextSettings( val,
834                                        netTerm.NetLabel.TextCodeID,
835                                        netTerm.NetLabel.Alignment,
836                                        netTerm.NetLabel.Justification,
837                                        netTerm.NetLabel.OrientAngle,
838                                        netTerm.NetLabel.Mirror  );
839                 }
840             }
841             else if( m_globalLabelsMap.find( netTerm.SymbolID ) != m_globalLabelsMap.end() )
842             {
843                 m_globalLabelsMap.at( netTerm.SymbolID )->SetText( netName );
844 
845                 LAYER_ID sheet = Schematic.Symbols.at( netTerm.SymbolID ).LayerID;
846 
847                 if( m_sheetMap.count( sheet ) )
848                 {
849                     SCH_SCREEN* screen = m_sheetMap.at( sheet )->GetScreen();
850 
851                     // autoplace intersheet refs again since we've changed the name
852                     m_globalLabelsMap.at( netTerm.SymbolID )->AutoplaceFields( screen, false );
853                 }
854             }
855             else if( !net.Name.IsEmpty() && Schematic.Symbols.count( netTerm.SymbolID )
856                      && netTerm.HasNetLabel )
857             {
858                 // This is a named net that connects to a schematic symbol pin - we need to put a label
859                 SCH_LABEL* label = new SCH_LABEL();
860                 label->SetText( netName );
861 
862                 POINT pinLocation = getLocationOfNetElement( net, netTerm.ID );
863                 label->SetPosition( getKiCadPoint( pinLocation ) );
864                 label->SetVisible( true );
865 
866                 applyTextSettings( label, netTerm.NetLabel.TextCodeID, netTerm.NetLabel.Alignment,
867                                    netTerm.NetLabel.Justification );
868 
869                 netlabels.insert( { netTerm.ID, label } );
870 
871                 LAYER_ID sheet = Schematic.Symbols.at( netTerm.SymbolID ).LayerID;
872                 m_sheetMap.at( sheet )->GetScreen()->Append( label );
873             }
874         }
875 
876         auto getHierarchicalLabel =
877             [&]( NETELEMENT_ID aNode ) -> SCH_HIERLABEL*
878             {
879                 if( aNode.Contains( "BLKT" ) )
880                 {
881                     NET_SCH::BLOCK_TERM blockTerm = net.BlockTerminals.at( aNode );
882                     BLOCK_PIN_ID blockPinID = std::make_pair( blockTerm.BlockID,
883                                                               blockTerm.TerminalID );
884 
885                     if( m_sheetPinMap.find( blockPinID )
886                             != m_sheetPinMap.end() )
887                     {
888                         return m_sheetPinMap.at( blockPinID );
889                     }
890                 }
891 
892                 return nullptr;
893             };
894 
895         //Add net name to all hierarchical pins (block terminals in CADSTAR)
896         for( std::pair<NETELEMENT_ID, NET_SCH::BLOCK_TERM> blockPair : net.BlockTerminals )
897         {
898             SCH_HIERLABEL* label = getHierarchicalLabel( blockPair.first );
899 
900             if( label )
901                 label->SetText( netName );
902         }
903 
904         // Load all bus entries and add net label if required
905         for( std::pair<NETELEMENT_ID, NET_SCH::BUS_TERM> busPair : net.BusTerminals )
906         {
907             NET_SCH::BUS_TERM busTerm = busPair.second;
908             BUS               bus     = Schematic.Buses.at( busTerm.BusID );
909 
910             if( !m_busesMap.at( bus.ID )->Contains( netName ) )
911                 m_busesMap.at( bus.ID )->AddMember( netName );
912 
913             SCH_BUS_WIRE_ENTRY* busEntry =
914                     new SCH_BUS_WIRE_ENTRY( getKiCadPoint( busTerm.FirstPoint ), false );
915 
916             wxPoint size =
917                     getKiCadPoint( busTerm.SecondPoint ) - getKiCadPoint( busTerm.FirstPoint );
918             busEntry->SetSize( wxSize( size.x, size.y ) );
919 
920             m_sheetMap.at( bus.LayerID )->GetScreen()->Append( busEntry );
921 
922             // Always add a label at bus terminals to ensure connectivity.
923             // If the original design does not have a label, just make it very small
924             // to keep connectivity but make the design look visually similar to
925             // the original.
926             SCH_LABEL* label = new SCH_LABEL();
927             label->SetText( netName );
928             label->SetPosition( getKiCadPoint( busTerm.SecondPoint ) );
929             label->SetVisible( true );
930 
931             if( busTerm.HasNetLabel )
932             {
933                 applyTextSettings( label,
934                                    busTerm.NetLabel.TextCodeID,
935                                    busTerm.NetLabel.Alignment,
936                                    busTerm.NetLabel.Justification );
937             }
938             else
939             {
940                 label->SetTextSize( wxSize( SMALL_LABEL_SIZE, SMALL_LABEL_SIZE ) );
941             }
942 
943             netlabels.insert( { busTerm.ID, label } );
944             m_sheetMap.at( bus.LayerID )->GetScreen()->Append( label );
945         }
946 
947         for( std::pair<NETELEMENT_ID, NET_SCH::DANGLER> danglerPair : net.Danglers )
948         {
949             NET_SCH::DANGLER dangler = danglerPair.second;
950 
951             SCH_LABEL* label = new SCH_LABEL();
952             label->SetPosition( getKiCadPoint( dangler.Position ) );
953             label->SetVisible( true );
954 
955             if( dangler.HasNetLabel )
956             {
957                 applyTextSettings( label,
958                                    dangler.NetLabel.TextCodeID,
959                                    dangler.NetLabel.Alignment,
960                                    dangler.NetLabel.Justification );
961             }
962 
963             label->SetText( netName ); // set text after applying settings to avoid double-escaping
964             netlabels.insert( { dangler.ID, label } );
965 
966             m_sheetMap.at( dangler.LayerID )->GetScreen()->Append( label );
967         }
968 
969         for( NET_SCH::CONNECTION_SCH conn : net.Connections )
970         {
971             if( conn.LayerID == wxT( "NO_SHEET" ) )
972                 continue; // No point loading virtual connections. KiCad handles that internally
973 
974             POINT start = getLocationOfNetElement( net, conn.StartNode );
975             POINT end = getLocationOfNetElement( net, conn.EndNode );
976 
977             if( start.x == UNDEFINED_VALUE || end.x == UNDEFINED_VALUE )
978                 continue;
979 
980             // Connections in CADSTAR are always implied between symbols even if the route
981             // doesn't start and end exactly at the connection points
982             if( conn.Path.size() < 1 || conn.Path.front() != start )
983                 conn.Path.insert( conn.Path.begin(), start );
984 
985             if( conn.Path.size() < 2 || conn.Path.back() != end )
986                 conn.Path.push_back( end );
987 
988             bool      firstPt  = true;
989             bool      secondPt = false;
990             wxPoint   last;
991             SCH_LINE* wire = nullptr;
992 
993             SHAPE_LINE_CHAIN wireChain; // Create a temp. line chain representing the connection
994 
995             for( POINT pt : conn.Path )
996             {
997                 wireChain.Append( getKiCadPoint( pt ) );
998             }
999 
1000             // AUTO-FIX SHEET PINS
1001             //--------------------
1002             // KiCad constrains the sheet pin on the edge of the sheet object whereas in
1003             // CADSTAR it can be anywhere. Let's find the intersection of the wires with the sheet
1004             // and place the hierarchical
1005             std::vector<NETELEMENT_ID> nodes;
1006             nodes.push_back( conn.StartNode );
1007             nodes.push_back( conn.EndNode );
1008 
1009             for( NETELEMENT_ID node : nodes )
1010             {
1011                 SCH_HIERLABEL* sheetPin = getHierarchicalLabel( node );
1012 
1013                 if( sheetPin )
1014                 {
1015                     if( sheetPin->Type() == SCH_SHEET_PIN_T
1016                             && SCH_SHEET::ClassOf( sheetPin->GetParent() ) )
1017                     {
1018                         SCH_SHEET* parentSheet   = static_cast<SCH_SHEET*>( sheetPin->GetParent() );
1019                         wxSize     sheetSize     = parentSheet->GetSize();
1020                         wxPoint    sheetPosition = parentSheet->GetPosition();
1021 
1022                         int leftSide  = sheetPosition.x;
1023                         int rightSide = sheetPosition.x + sheetSize.x;
1024                         int topSide   = sheetPosition.y;
1025                         int botSide   = sheetPosition.y + sheetSize.y;
1026 
1027                         SHAPE_LINE_CHAIN sheetEdge;
1028 
1029                         sheetEdge.Append( leftSide, topSide );
1030                         sheetEdge.Append( rightSide, topSide );
1031                         sheetEdge.Append( rightSide, botSide );
1032                         sheetEdge.Append( leftSide, botSide );
1033                         sheetEdge.Append( leftSide, topSide );
1034 
1035                         SHAPE_LINE_CHAIN::INTERSECTIONS wireToSheetIntersects;
1036 
1037                         if( !wireChain.Intersect( sheetEdge, wireToSheetIntersects ) )
1038                         {
1039                             // The block terminal is outside the block shape in the original
1040                             // CADSTAR design. Since KiCad's Sheet Pin will already be constrained
1041                             // on the edge, we will simply join to it with a straight line.
1042                             if( node == conn.StartNode )
1043                                 wireChain = wireChain.Reverse();
1044 
1045                             wireChain.Append( sheetPin->GetPosition() );
1046 
1047                             if( node == conn.StartNode )
1048                                 wireChain = wireChain.Reverse();
1049                         }
1050                         else
1051                         {
1052                             // The block terminal is either inside or on the shape edge. Lets use
1053                             // the first intersection point.
1054                             VECTOR2I intsctPt   = wireToSheetIntersects.at( 0 ).p;
1055                             int      intsctIndx = wireChain.FindSegment( intsctPt );
1056                             wxASSERT_MSG( intsctIndx != -1, "Can't find intersecting segment" );
1057 
1058                             if( node == conn.StartNode )
1059                                 wireChain.Replace( 0, intsctIndx, intsctPt );
1060                             else
1061                                 wireChain.Replace( intsctIndx + 1, /*end index*/ -1, intsctPt );
1062 
1063                             sheetPin->SetPosition( (wxPoint) intsctPt );
1064                         }
1065                     }
1066                 }
1067             }
1068 
1069             auto fixNetLabelsAndSheetPins =
1070                 [&]( double aWireAngleDeciDeg, NETELEMENT_ID& aNetEleID )
1071                 {
1072                     LABEL_SPIN_STYLE spin = getSpinStyleDeciDeg( aWireAngleDeciDeg );
1073 
1074                     if( netlabels.find( aNetEleID ) != netlabels.end() )
1075                         netlabels.at( aNetEleID )->SetLabelSpinStyle( spin.MirrorY() );
1076 
1077                     SCH_HIERLABEL* sheetPin = getHierarchicalLabel( aNetEleID );
1078 
1079                     if( sheetPin )
1080                         sheetPin->SetLabelSpinStyle( spin.MirrorX() );
1081                 };
1082 
1083             // Now we can load the wires and fix the label orientations
1084             for( const VECTOR2I& pt : wireChain.CPoints() )
1085             {
1086                 if( firstPt )
1087                 {
1088                     last     = (wxPoint) pt;
1089                     firstPt  = false;
1090                     secondPt = true;
1091                     continue;
1092                 }
1093 
1094                 if( secondPt )
1095                 {
1096                     secondPt = false;
1097 
1098 
1099                     wxPoint kiLast           = last;
1100                     wxPoint kiCurrent        = (wxPoint) pt;
1101                     double  wireangleDeciDeg = getPolarAngle( kiLast - kiCurrent );
1102                     fixNetLabelsAndSheetPins( wireangleDeciDeg, conn.StartNode );
1103                 }
1104 
1105                 wire = new SCH_LINE();
1106 
1107                 wire->SetStartPoint( last );
1108                 wire->SetEndPoint( (wxPoint) pt );
1109                 wire->SetLayer( LAYER_WIRE );
1110 
1111                 if( !conn.ConnectionLineCode.IsEmpty() )
1112                     wire->SetLineWidth( getLineThickness( conn.ConnectionLineCode ) );
1113 
1114                 last = (wxPoint) pt;
1115 
1116                 m_sheetMap.at( conn.LayerID )->GetScreen()->Append( wire );
1117             }
1118 
1119             //Fix labels on the end wire
1120             if( wire )
1121             {
1122                 wxPoint kiLast           = wire->GetEndPoint();
1123                 wxPoint kiCurrent        = wire->GetStartPoint();
1124                 double  wireangleDeciDeg = getPolarAngle( kiLast - kiCurrent );
1125                 fixNetLabelsAndSheetPins( wireangleDeciDeg, conn.EndNode );
1126             }
1127         }
1128 
1129         for( std::pair<NETELEMENT_ID, NET_SCH::JUNCTION_SCH> juncPair : net.Junctions )
1130         {
1131             NET_SCH::JUNCTION_SCH junc = juncPair.second;
1132 
1133             SCH_JUNCTION* kiJunc = new SCH_JUNCTION();
1134 
1135             kiJunc->SetPosition( getKiCadPoint( junc.Location ) );
1136             m_sheetMap.at( junc.LayerID )->GetScreen()->Append( kiJunc );
1137 
1138             if( junc.HasNetLabel )
1139             {
1140                 // In CADSTAR the label can be placed anywhere, but in KiCad it has to be placed
1141                 // in the same location as the junction for it to be connected to it.
1142                 SCH_LABEL* label = new SCH_LABEL();
1143                 label->SetText( netName );
1144                 label->SetPosition( getKiCadPoint( junc.Location ) );
1145                 label->SetVisible( true );
1146 
1147                 double labelAngleDeciDeg = getAngleTenthDegree( junc.NetLabel.OrientAngle );
1148                 LABEL_SPIN_STYLE spin = getSpinStyleDeciDeg( labelAngleDeciDeg );
1149                 label->SetLabelSpinStyle( spin );
1150 
1151                 m_sheetMap.at( junc.LayerID )->GetScreen()->Append( label );
1152             }
1153         }
1154     }
1155 }
1156 
1157 
loadFigures()1158 void CADSTAR_SCH_ARCHIVE_LOADER::loadFigures()
1159 {
1160     for( std::pair<FIGURE_ID, FIGURE> figPair : Schematic.Figures )
1161     {
1162         FIGURE fig = figPair.second;
1163 
1164         loadFigure( fig, fig.LayerID, LAYER_NOTES );
1165     }
1166 }
1167 
1168 
loadTexts()1169 void CADSTAR_SCH_ARCHIVE_LOADER::loadTexts()
1170 {
1171     for( std::pair<TEXT_ID, TEXT> textPair : Schematic.Texts )
1172     {
1173         TEXT txt = textPair.second;
1174 
1175         SCH_TEXT* kiTxt = getKiCadSchText( txt );
1176         loadItemOntoKiCadSheet( txt.LayerID, kiTxt );
1177     }
1178 }
1179 
1180 
loadDocumentationSymbols()1181 void CADSTAR_SCH_ARCHIVE_LOADER::loadDocumentationSymbols()
1182 {
1183     for( std::pair<DOCUMENTATION_SYMBOL_ID, DOCUMENTATION_SYMBOL> docSymPair :
1184             Schematic.DocumentationSymbols )
1185     {
1186         DOCUMENTATION_SYMBOL docSym = docSymPair.second;
1187 
1188         if( Library.SymbolDefinitions.find( docSym.SymdefID ) == Library.SymbolDefinitions.end() )
1189         {
1190             m_reporter->Report( wxString::Format( _( "Documentation Symbol '%s' refers to symbol "
1191                                                      "definition ID '%s' which does not exist in "
1192                                                      "the library. The symbol was not loaded." ),
1193                                                   docSym.ID,
1194                                                   docSym.SymdefID ),
1195                                 RPT_SEVERITY_ERROR );
1196             continue;
1197         }
1198 
1199         SYMDEF_SCM docSymDef  = Library.SymbolDefinitions.at( docSym.SymdefID );
1200         wxPoint    moveVector = getKiCadPoint( docSym.Origin ) - getKiCadPoint( docSymDef.Origin );
1201         double     rotationAngle = getAngleTenthDegree( docSym.OrientAngle );
1202         double     scalingFactor =
1203                 (double) docSym.ScaleRatioNumerator / (double) docSym.ScaleRatioDenominator;
1204         wxPoint centreOfTransform = getKiCadPoint( docSymDef.Origin );
1205         bool    mirrorInvert      = docSym.Mirror;
1206 
1207         for( std::pair<FIGURE_ID, FIGURE> figPair : docSymDef.Figures )
1208         {
1209             FIGURE fig = figPair.second;
1210 
1211             loadFigure( fig, docSym.LayerID, LAYER_NOTES, moveVector, rotationAngle, scalingFactor,
1212                         centreOfTransform, mirrorInvert );
1213         }
1214 
1215         for( std::pair<TEXT_ID, TEXT> textPair : docSymDef.Texts )
1216         {
1217             TEXT txt = textPair.second;
1218 
1219             txt.Mirror = ( txt.Mirror ) ? !mirrorInvert : mirrorInvert;
1220             txt.OrientAngle = docSym.OrientAngle - txt.OrientAngle;
1221 
1222             SCH_TEXT* kiTxt = getKiCadSchText( txt );
1223 
1224             wxPoint newPosition = applyTransform( kiTxt->GetPosition(), moveVector, rotationAngle,
1225                                                   scalingFactor, centreOfTransform, mirrorInvert );
1226 
1227             int     newTxtWidth     = KiROUND( kiTxt->GetTextWidth() * scalingFactor );
1228             int     newTxtHeight    = KiROUND( kiTxt->GetTextHeight() * scalingFactor );
1229             int     newTxtThickness = KiROUND( kiTxt->GetTextThickness() * scalingFactor );
1230 
1231             kiTxt->SetPosition( newPosition );
1232             kiTxt->SetTextWidth( newTxtWidth );
1233             kiTxt->SetTextHeight( newTxtHeight );
1234             kiTxt->SetTextThickness( newTxtThickness );
1235 
1236             loadItemOntoKiCadSheet( docSym.LayerID, kiTxt );
1237         }
1238     }
1239 }
1240 
1241 
loadTextVariables()1242 void CADSTAR_SCH_ARCHIVE_LOADER::loadTextVariables()
1243 {
1244     auto findAndReplaceTextField = [&]( TEXT_FIELD_NAME aField, wxString aValue ) {
1245         if( m_context.TextFieldToValuesMap.find( aField ) != m_context.TextFieldToValuesMap.end() )
1246         {
1247             if( m_context.TextFieldToValuesMap.at( aField ) != aValue )
1248             {
1249                 m_context.TextFieldToValuesMap.at( aField ) = aValue;
1250                 m_context.InconsistentTextFields.insert( aField );
1251                 return false;
1252             }
1253         }
1254         else
1255         {
1256             m_context.TextFieldToValuesMap.insert( { aField, aValue } );
1257         }
1258 
1259         return true;
1260     };
1261 
1262     PROJECT* pj = &m_schematic->Prj();
1263 
1264     if( pj )
1265     {
1266         std::map<wxString, wxString>& txtVars = pj->GetTextVars();
1267 
1268         // Most of the design text fields can be derived from other elements
1269         if( Schematic.VariantHierarchy.Variants.size() > 0 )
1270         {
1271             VARIANT loadedVar = Schematic.VariantHierarchy.Variants.begin()->second;
1272 
1273             findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_NAME, loadedVar.Name );
1274             findAndReplaceTextField( TEXT_FIELD_NAME::VARIANT_DESCRIPTION, loadedVar.Description );
1275         }
1276 
1277         findAndReplaceTextField( TEXT_FIELD_NAME::DESIGN_TITLE, Header.JobTitle );
1278 
1279         for( std::pair<TEXT_FIELD_NAME, wxString> txtvalue : m_context.TextFieldToValuesMap )
1280         {
1281             wxString varName  = CADSTAR_TO_KICAD_FIELDS.at( txtvalue.first );
1282             wxString varValue = txtvalue.second;
1283 
1284             txtVars.insert( { varName, varValue } );
1285         }
1286 
1287         for( std::pair<wxString, wxString> txtvalue : m_context.FilenamesToTextMap )
1288         {
1289             wxString varName  = txtvalue.first;
1290             wxString varValue = txtvalue.second;
1291 
1292             txtVars.insert( { varName, varValue } );
1293         }
1294     }
1295     else
1296     {
1297         m_reporter->Report( _( "Text Variables could not be set as there is no project attached." ),
1298                             RPT_SEVERITY_ERROR );
1299     }
1300 }
1301 
1302 
loadSymDefIntoLibrary(const SYMDEF_ID & aSymdefID,const PART * aCadstarPart,const GATE_ID & aGateID,LIB_SYMBOL * aSymbol)1303 void CADSTAR_SCH_ARCHIVE_LOADER::loadSymDefIntoLibrary( const SYMDEF_ID& aSymdefID,
1304         const PART* aCadstarPart, const GATE_ID& aGateID, LIB_SYMBOL* aSymbol )
1305 {
1306     wxCHECK( Library.SymbolDefinitions.find( aSymdefID ) != Library.SymbolDefinitions.end(), );
1307 
1308     SYMDEF_SCM symbol = Library.SymbolDefinitions.at( aSymdefID );
1309     int        gateNumber = getKiCadUnitNumberFromGate( aGateID );
1310 
1311     for( std::pair<FIGURE_ID, FIGURE> figPair : symbol.Figures )
1312     {
1313         FIGURE fig = figPair.second;
1314         int    lineThickness = getLineThickness( fig.LineCodeID );
1315 
1316         loadLibrarySymbolShapeVertices( fig.Shape.Vertices, symbol.Origin, aSymbol, gateNumber,
1317                                             lineThickness );
1318 
1319         for( CUTOUT c : fig.Shape.Cutouts )
1320         {
1321             loadLibrarySymbolShapeVertices( c.Vertices, symbol.Origin, aSymbol, gateNumber,
1322                                             lineThickness );
1323         }
1324     }
1325 
1326     TERMINAL_TO_PINNUM_MAP pinNumMap;
1327 
1328     for( std::pair<TERMINAL_ID, TERMINAL> termPair : symbol.Terminals )
1329     {
1330         TERMINAL term    = termPair.second;
1331         wxString pinNum  = wxString::Format( "%ld", term.ID );
1332         wxString pinName = wxEmptyString;
1333         LIB_PIN* pin = new LIB_PIN( aSymbol );
1334 
1335         if( aCadstarPart )
1336         {
1337             PART::DEFINITION::PIN csPin = getPartDefinitionPin( *aCadstarPart, aGateID, term.ID );
1338 
1339             pinName = HandleTextOverbar( csPin.Label );
1340             pinNum = HandleTextOverbar( csPin.Name );
1341 
1342             if( pinNum.IsEmpty() )
1343             {
1344                 if( !csPin.Identifier.IsEmpty() )
1345                     pinNum = csPin.Identifier;
1346                 else if( csPin.ID == UNDEFINED_VALUE )
1347                     pinNum = wxString::Format( "%ld", term.ID );
1348                 else
1349                     pinNum = wxString::Format( "%ld", csPin.ID );
1350             }
1351 
1352             pin->SetType( getKiCadPinType( csPin.Type ) );
1353 
1354             pinNumMap.insert( { term.ID, pinNum } );
1355         }
1356         else
1357         {
1358             // If no part is defined, we don't know the pin type. Assume passive pin
1359             pin->SetType( ELECTRICAL_PINTYPE::PT_PASSIVE );
1360         }
1361 
1362         pin->SetPosition( getKiCadLibraryPoint( term.Position, symbol.Origin ) );
1363         pin->SetLength( 0 ); //CADSTAR Pins are just a point (have no length)
1364         pin->SetShape( GRAPHIC_PINSHAPE::LINE );
1365         pin->SetUnit( gateNumber );
1366         pin->SetNumber( pinNum );
1367         pin->SetName( pinName );
1368 
1369         int pinNumberHeight = getTextHeightFromTextCode( wxT( "TC0" ) ); // TC0 is the default CADSTAR text size for name/number
1370         int pinNameHeight = getTextHeightFromTextCode( wxT( "TC0" ) );
1371 
1372         if( symbol.PinNumberLocations.count( term.ID ) )
1373         {
1374             PIN_NUM_LABEL_LOC pinNumLocation = symbol.PinNumberLocations.at( term.ID );
1375             pinNumberHeight = getTextHeightFromTextCode( pinNumLocation.TextCodeID );
1376         }
1377 
1378         if( symbol.PinLabelLocations.count( term.ID ) )
1379         {
1380             PIN_NUM_LABEL_LOC pinNameLocation = symbol.PinLabelLocations.at( term.ID );
1381             pinNameHeight = getTextHeightFromTextCode( pinNameLocation.TextCodeID );
1382         }
1383 
1384         pin->SetNumberTextSize( pinNumberHeight );
1385         pin->SetNameTextSize( pinNameHeight );
1386 
1387         if( aSymbol->IsPower() )
1388         {
1389             pin->SetVisible( false );
1390             pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
1391             pin->SetName( aSymbol->GetName() );
1392         }
1393 
1394         aSymbol->AddDrawItem( pin );
1395     }
1396 
1397     fixUpLibraryPins( aSymbol, gateNumber );
1398 
1399     if(aCadstarPart)
1400         m_pinNumsMap.insert( { aCadstarPart->ID + aGateID, pinNumMap } );
1401 
1402     for( std::pair<TEXT_ID, TEXT> textPair : symbol.Texts )
1403     {
1404         TEXT csText = textPair.second;
1405 
1406         LIB_TEXT* libtext = new LIB_TEXT( aSymbol );
1407         libtext->SetText( csText.Text );
1408         libtext->SetUnit( gateNumber );
1409         libtext->SetPosition( getKiCadLibraryPoint( csText.Position, symbol.Origin ) );
1410         libtext->SetMultilineAllowed( true ); // temporarily so that we calculate bbox correctly
1411 
1412         applyTextSettings( libtext,
1413                            csText.TextCodeID,
1414                            csText.Alignment,
1415                            csText.Justification,
1416                            csText.OrientAngle,
1417                            csText.Mirror );
1418 
1419         // Split out multi line text items into individual text elements
1420         if( csText.Text.Contains( "\n" ) )
1421         {
1422             wxArrayString strings;
1423             wxStringSplit( csText.Text, strings, '\n' );
1424             wxPoint firstLinePos;
1425 
1426             for( size_t ii = 0; ii < strings.size(); ++ii )
1427             {
1428                 EDA_RECT bbox = libtext->GetTextBox( ii, true );
1429                 wxPoint  linePos = { bbox.GetLeft(), -bbox.GetBottom() };
1430 
1431                 RotatePoint( &linePos, libtext->GetTextPos(), -libtext->GetTextAngle() );
1432 
1433                 LIB_TEXT* line = static_cast<LIB_TEXT*>( libtext->Clone() );
1434                 line->SetText( strings[ii] );
1435                 line->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1436                 line->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
1437                 line->SetTextPos( linePos );
1438 
1439                 // Multiline text not allowed in LIB_TEXT
1440                 line->SetMultilineAllowed( false );
1441                 aSymbol->AddDrawItem( line );
1442             }
1443 
1444             delete libtext;
1445         }
1446         else
1447         {
1448             // Multiline text not allowed in LIB_TEXT
1449             libtext->SetMultilineAllowed( false );
1450             aSymbol->AddDrawItem( libtext );
1451         }
1452     }
1453 
1454     if( symbol.TextLocations.find( SYMBOL_NAME_ATTRID ) != symbol.TextLocations.end() )
1455     {
1456         TEXT_LOCATION textLoc = symbol.TextLocations.at( SYMBOL_NAME_ATTRID );
1457         LIB_FIELD*    field = &aSymbol->GetReferenceField();
1458         applyToLibraryFieldAttribute( textLoc, symbol.Origin, field );
1459         field->SetUnit( gateNumber );
1460     }
1461 
1462     // Hide the value field for now (it might get unhidden if an attribute exists in the cadstar
1463     // design with the text "Value"
1464     aSymbol->GetValueField().SetVisible( false );
1465 
1466     if( symbol.TextLocations.find( PART_NAME_ATTRID ) != symbol.TextLocations.end() )
1467     {
1468         TEXT_LOCATION textLoc = symbol.TextLocations.at( PART_NAME_ATTRID );
1469         LIB_FIELD*    field = aSymbol->FindField( PartNameFieldName );
1470 
1471         if( !field )
1472         {
1473             int fieldID = aSymbol->GetFieldCount();
1474             field = new LIB_FIELD( aSymbol, fieldID );
1475             field->SetName( PartNameFieldName );
1476             aSymbol->AddField( field );
1477         }
1478 
1479         wxASSERT( field->GetName() == PartNameFieldName );
1480         applyToLibraryFieldAttribute( textLoc, symbol.Origin, field );
1481 
1482         if( aCadstarPart )
1483         {
1484             wxString partName = aCadstarPart->Name;
1485             partName.Replace( wxT( "\n" ), wxT( "\\n" ) );
1486             partName.Replace( wxT( "\r" ), wxT( "\\r" ) );
1487             partName.Replace( wxT( "\t" ), wxT( "\\t" ) );
1488             field->SetText( partName );
1489         }
1490 
1491         field->SetUnit( gateNumber );
1492         field->SetVisible( SymbolPartNameColor.IsVisible );
1493     }
1494 
1495     if( aCadstarPart )
1496     {
1497         wxString footprintRefName = wxEmptyString;
1498         wxString footprintAlternateName = wxEmptyString;
1499 
1500         auto loadLibraryField =
1501             [&]( ATTRIBUTE_VALUE& aAttributeVal )
1502             {
1503                 wxString attrName = getAttributeName( aAttributeVal.AttributeID );
1504 
1505                 // Remove invalid field characters
1506                 aAttributeVal.Value.Replace( wxT( "\n" ), wxT( "\\n" ) );
1507                 aAttributeVal.Value.Replace( wxT( "\r" ), wxT( "\\r" ) );
1508                 aAttributeVal.Value.Replace( wxT( "\t" ), wxT( "\\t" ) );
1509 
1510                 //TODO: Handle "links": In cadstar a field can be a "link" if its name starts
1511                 // with the characters "Link ". Need to figure out how to convert them to
1512                 // equivalent in KiCad.
1513 
1514                 if( attrName == wxT( "(PartDefinitionNameStem)" ) )
1515                 {
1516                     //Space not allowed in Reference field
1517                     aAttributeVal.Value.Replace( wxT( " " ), "_" );
1518                     aSymbol->GetReferenceField().SetText( aAttributeVal.Value );
1519                     return;
1520                 }
1521                 else if( attrName == wxT( "(PartDescription)" ) )
1522                 {
1523                     aSymbol->SetDescription( aAttributeVal.Value );
1524                     return;
1525                 }
1526                 else if( attrName == wxT( "(PartDefinitionReferenceName)" ) )
1527                 {
1528                     footprintRefName = aAttributeVal.Value;
1529                     return;
1530                 }
1531                 else if( attrName == wxT( "(PartDefinitionAlternateName)" ) )
1532                 {
1533                     footprintAlternateName = aAttributeVal.Value;
1534                     return;
1535                 }
1536 
1537                 LIB_FIELD* attrField = aSymbol->FindField( attrName );
1538 
1539                 if( !attrField )
1540                 {
1541                     int fieldID = aSymbol->GetFieldCount();
1542                     attrField = new LIB_FIELD( aSymbol, fieldID );
1543                     attrField->SetName( attrName );
1544                     aSymbol->AddField( attrField );
1545                 }
1546 
1547                 wxASSERT( attrField->GetName() == attrName );
1548                 attrField->SetText( aAttributeVal.Value );
1549                 attrField->SetUnit( gateNumber );
1550 
1551                 ATTRIBUTE_ID& attrid = aAttributeVal.AttributeID;
1552                 attrField->SetVisible( isAttributeVisible( attrid ) );
1553 
1554                 if( aAttributeVal.HasLocation )
1555                 {
1556                     // #1 Check if the part itself defined a location for the field
1557                     applyToLibraryFieldAttribute( aAttributeVal.AttributeLocation, symbol.Origin,
1558                                                   attrField );
1559                 }
1560                 else if( symbol.TextLocations.find( aAttributeVal.AttributeID )
1561                          != symbol.TextLocations.end() )
1562                 {
1563                     // #2 Look in the symbol definition: Text locations
1564                     TEXT_LOCATION symTxtLoc = symbol.TextLocations.at( aAttributeVal.AttributeID );
1565                     applyToLibraryFieldAttribute( symTxtLoc, symbol.Origin, attrField );
1566                 }
1567                 else if( symbol.AttributeValues.find( attrid ) != symbol.AttributeValues.end()
1568                          && symbol.AttributeValues.at( attrid ).HasLocation )
1569                 {
1570                     // #3 Look in the symbol definition: Attribute values
1571                     ATTRIBUTE_VALUE symAttrVal = symbol.AttributeValues.at( attrid );
1572                     applyToLibraryFieldAttribute( symAttrVal.AttributeLocation, symbol.Origin,
1573                                                   attrField );
1574                 }
1575                 else
1576                 {
1577                     attrField->SetVisible( false );
1578                     applyTextSettings( attrField, wxT( "TC1" ), ALIGNMENT::NO_ALIGNMENT,
1579                                        JUSTIFICATION::LEFT );
1580                 }
1581             };
1582 
1583         // Load all attributes in the Part Definition
1584         for( std::pair<ATTRIBUTE_ID,
1585              ATTRIBUTE_VALUE> attr : aCadstarPart->Definition.AttributeValues )
1586         {
1587             ATTRIBUTE_VALUE attrVal = attr.second;
1588             loadLibraryField( attrVal );
1589         }
1590 
1591         // Load all attributes in the Part itself.
1592         for( std::pair<ATTRIBUTE_ID, ATTRIBUTE_VALUE> attr : aCadstarPart->AttributeValues )
1593         {
1594             ATTRIBUTE_VALUE attrVal = attr.second;
1595             loadLibraryField( attrVal );
1596         }
1597 
1598         wxString      fpNameInLibrary = generateLibName( footprintRefName, footprintAlternateName );
1599         wxArrayString fpFilters;
1600         fpFilters.Add( fpNameInLibrary );
1601 
1602         aSymbol->SetFPFilters( fpFilters );
1603 
1604         // Assume that the PCB footprint library name will be the same as the schematic filename
1605         wxFileName schFilename( Filename );
1606         wxString   libName = schFilename.GetName();
1607 
1608         aSymbol->GetFootprintField().SetText( libName + wxT( ":" ) + fpNameInLibrary );
1609     }
1610 
1611     if( aCadstarPart && aCadstarPart->Definition.HidePinNames )
1612     {
1613         aSymbol->SetShowPinNames( false );
1614         aSymbol->SetShowPinNumbers( false );
1615     }
1616 }
1617 
1618 
loadLibrarySymbolShapeVertices(const std::vector<VERTEX> & aCadstarVertices,wxPoint aSymbolOrigin,LIB_SYMBOL * aSymbol,int aGateNumber,int aLineThickness)1619 void CADSTAR_SCH_ARCHIVE_LOADER::loadLibrarySymbolShapeVertices( const std::vector<VERTEX>& aCadstarVertices,
1620                                                                  wxPoint aSymbolOrigin,
1621                                                                  LIB_SYMBOL* aSymbol,
1622                                                                  int aGateNumber,
1623                                                                  int aLineThickness )
1624 {
1625     const VERTEX* prev = &aCadstarVertices.at( 0 );
1626     const VERTEX* cur;
1627 
1628     wxASSERT_MSG( prev->Type == VERTEX_TYPE::POINT, "First vertex should always be a point." );
1629 
1630     for( size_t i = 1; i < aCadstarVertices.size(); i++ )
1631     {
1632         cur = &aCadstarVertices.at( i );
1633 
1634         LIB_SHAPE* shape      = nullptr;
1635         bool       cw         = false;
1636         wxPoint    startPoint = getKiCadLibraryPoint( prev->End, aSymbolOrigin );
1637         wxPoint    endPoint   = getKiCadLibraryPoint( cur->End, aSymbolOrigin );
1638         wxPoint    centerPoint;
1639 
1640         if( cur->Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE
1641                 || cur->Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
1642         {
1643             centerPoint = ( startPoint + endPoint ) / 2;
1644         }
1645         else
1646         {
1647             centerPoint = getKiCadLibraryPoint( cur->Center, aSymbolOrigin );
1648         }
1649 
1650 
1651         switch( cur->Type )
1652         {
1653         case VERTEX_TYPE::POINT:
1654             shape = new LIB_SHAPE( aSymbol, SHAPE_T::POLY );
1655             shape->AddPoint( startPoint );
1656             shape->AddPoint( endPoint );
1657             break;
1658 
1659         case VERTEX_TYPE::CLOCKWISE_SEMICIRCLE:
1660         case VERTEX_TYPE::CLOCKWISE_ARC:
1661             cw = true;
1662             KI_FALLTHROUGH;
1663 
1664         case VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE:
1665         case VERTEX_TYPE::ANTICLOCKWISE_ARC:
1666             shape = new LIB_SHAPE( aSymbol, SHAPE_T::ARC );
1667 
1668             shape->SetPosition( centerPoint );
1669 
1670             if( cw )
1671             {
1672                 shape->SetStart( endPoint );
1673                 shape->SetEnd( startPoint );
1674             }
1675             else
1676             {
1677                 shape->SetStart( startPoint );
1678                 shape->SetEnd( endPoint );
1679             }
1680 
1681             break;
1682         }
1683 
1684         shape->SetUnit( aGateNumber );
1685         shape->SetWidth( aLineThickness );
1686         aSymbol->AddDrawItem( shape );
1687 
1688         prev = cur;
1689     }
1690 }
1691 
1692 
applyToLibraryFieldAttribute(const ATTRIBUTE_LOCATION & aCadstarAttrLoc,wxPoint aSymbolOrigin,LIB_FIELD * aKiCadField)1693 void CADSTAR_SCH_ARCHIVE_LOADER::applyToLibraryFieldAttribute(
1694         const ATTRIBUTE_LOCATION& aCadstarAttrLoc, wxPoint aSymbolOrigin, LIB_FIELD* aKiCadField )
1695 {
1696     aKiCadField->SetTextPos( getKiCadLibraryPoint( aCadstarAttrLoc.Position, aSymbolOrigin ) );
1697 
1698     applyTextSettings( aKiCadField,
1699                        aCadstarAttrLoc.TextCodeID,
1700                        aCadstarAttrLoc.Alignment,
1701                        aCadstarAttrLoc.Justification,
1702                        aCadstarAttrLoc.OrientAngle,
1703                        aCadstarAttrLoc.Mirror );
1704 }
1705 
1706 
loadSchematicSymbol(const SYMBOL & aCadstarSymbol,const LIB_SYMBOL & aKiCadPart,double & aComponentOrientationDeciDeg)1707 SCH_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::loadSchematicSymbol( const SYMBOL& aCadstarSymbol,
1708                                                              const LIB_SYMBOL& aKiCadPart,
1709                                                              double& aComponentOrientationDeciDeg )
1710 {
1711     LIB_ID  libId( m_libraryFileName.GetName(), aKiCadPart.GetName() );
1712     int     unit = getKiCadUnitNumberFromGate( aCadstarSymbol.GateID );
1713 
1714     SCH_SHEET_PATH sheetpath;
1715     SCH_SHEET* kiSheet = m_sheetMap.at( aCadstarSymbol.LayerID );
1716     m_rootSheet->LocatePathOfScreen( kiSheet->GetScreen(), &sheetpath );
1717 
1718     SCH_SYMBOL* symbol = new SCH_SYMBOL( aKiCadPart, libId, &sheetpath, unit );
1719 
1720     if( aCadstarSymbol.IsComponent )
1721     {
1722         symbol->SetRef( &sheetpath, aCadstarSymbol.ComponentRef.Designator );
1723     }
1724 
1725     symbol->SetPosition( getKiCadPoint( aCadstarSymbol.Origin ) );
1726 
1727     double compAngleDeciDeg = getAngleTenthDegree( aCadstarSymbol.OrientAngle );
1728     int compOrientation  = 0;
1729 
1730     if( aCadstarSymbol.Mirror )
1731     {
1732         compAngleDeciDeg = -compAngleDeciDeg;
1733         compOrientation += SYMBOL_ORIENTATION_T::SYM_MIRROR_Y;
1734     }
1735 
1736     compOrientation += getComponentOrientation( compAngleDeciDeg, aComponentOrientationDeciDeg );
1737 
1738     if( NormalizeAngle180( compAngleDeciDeg ) != NormalizeAngle180( aComponentOrientationDeciDeg ) )
1739     {
1740         m_reporter->Report( wxString::Format( _( "Symbol '%s' is rotated by an angle of %.1f "
1741                                                  "degrees in the original CADSTAR design but "
1742                                                  "KiCad only supports rotation angles multiples "
1743                                                  "of 90 degrees. The connecting wires will need "
1744                                                  "manual fixing." ),
1745                                               aCadstarSymbol.ComponentRef.Designator,
1746                                               compAngleDeciDeg / 10.0 ),
1747                             RPT_SEVERITY_ERROR);
1748     }
1749 
1750     symbol->SetOrientation( compOrientation );
1751 
1752     if( m_sheetMap.find( aCadstarSymbol.LayerID ) == m_sheetMap.end() )
1753     {
1754         m_reporter->Report( wxString::Format( _( "Symbol '%s' references sheet ID '%s' which does "
1755                                                  "not exist in the design. The symbol was not "
1756                                                  "loaded." ),
1757                                               aCadstarSymbol.ComponentRef.Designator,
1758                                               aCadstarSymbol.LayerID ),
1759                             RPT_SEVERITY_ERROR );
1760 
1761         delete symbol;
1762         return nullptr;
1763     }
1764 
1765     wxString gate = ( aCadstarSymbol.GateID.IsEmpty() ) ? wxT( "A" ) : aCadstarSymbol.GateID;
1766     wxString partGateIndex = aCadstarSymbol.PartRef.RefID + gate;
1767 
1768     //Handle pin swaps
1769     if( m_pinNumsMap.find( partGateIndex ) != m_pinNumsMap.end() )
1770     {
1771         TERMINAL_TO_PINNUM_MAP termNumMap = m_pinNumsMap.at( partGateIndex );
1772 
1773         std::map<wxString, LIB_PIN*> pinNumToLibPinMap;
1774 
1775         for( auto& term : termNumMap )
1776         {
1777             wxString pinNum = term.second;
1778             pinNumToLibPinMap.insert( { pinNum,
1779                     symbol->GetLibSymbolRef()->GetPin( term.second ) } );
1780         }
1781 
1782         auto replacePinNumber = [&]( wxString aOldPinNum, wxString aNewPinNum )
1783                                 {
1784                                     if( aOldPinNum == aNewPinNum )
1785                                         return;
1786 
1787                                     LIB_PIN* libpin = pinNumToLibPinMap.at( aOldPinNum );
1788                                     libpin->SetNumber( HandleTextOverbar( aNewPinNum ) );
1789                                 };
1790 
1791         //Older versions of Cadstar used pin numbers
1792         for( auto& pinPair : aCadstarSymbol.PinNumbers )
1793         {
1794             SYMBOL::PIN_NUM pin = pinPair.second;
1795 
1796             replacePinNumber( termNumMap.at( pin.TerminalID ),
1797                               wxString::Format( "%ld", pin.PinNum ) );
1798         }
1799 
1800         //Newer versions of Cadstar use pin names
1801         for( auto& pinPair : aCadstarSymbol.PinNames )
1802         {
1803             SYMPINNAME_LABEL pin = pinPair.second;
1804             replacePinNumber( termNumMap.at( pin.TerminalID ), pin.NameOrLabel );
1805         }
1806 
1807         symbol->UpdatePins();
1808     }
1809 
1810     kiSheet->GetScreen()->Append( symbol );
1811 
1812     return symbol;
1813 }
1814 
1815 
loadSymbolFieldAttribute(const ATTRIBUTE_LOCATION & aCadstarAttrLoc,const double & aComponentOrientationDeciDeg,bool aIsMirrored,SCH_FIELD * aKiCadField)1816 void CADSTAR_SCH_ARCHIVE_LOADER::loadSymbolFieldAttribute(
1817         const ATTRIBUTE_LOCATION& aCadstarAttrLoc, const double& aComponentOrientationDeciDeg,
1818         bool aIsMirrored, SCH_FIELD* aKiCadField )
1819 {
1820     aKiCadField->SetPosition( getKiCadPoint( aCadstarAttrLoc.Position ) );
1821     aKiCadField->SetVisible( true );
1822 
1823     ALIGNMENT alignment = aCadstarAttrLoc.Alignment;
1824 
1825     double textAngle = getAngleTenthDegree( aCadstarAttrLoc.OrientAngle );
1826     long long cadstarAngle = getCadstarAngle( textAngle - aComponentOrientationDeciDeg );
1827 
1828     if( aIsMirrored )
1829     {
1830         // We need to change the aligment when the symbol is mirrored based on the text orientation
1831         // To ensure the anchor point is the same in KiCad.
1832 
1833         int textIsVertical = KiROUND( textAngle / 900.0 ) % 2;
1834 
1835         if( textIsVertical )
1836             alignment = rotate180( alignment );
1837 
1838         alignment = mirrorX( alignment );
1839     }
1840 
1841     applyTextSettings( aKiCadField,
1842                        aCadstarAttrLoc.TextCodeID,
1843                        alignment,
1844                        aCadstarAttrLoc.Justification,
1845                        cadstarAngle,
1846                        aCadstarAttrLoc.Mirror );
1847 }
1848 
1849 
getComponentOrientation(double aOrientAngleDeciDeg,double & aReturnedOrientationDeciDeg)1850 int CADSTAR_SCH_ARCHIVE_LOADER::getComponentOrientation(
1851         double aOrientAngleDeciDeg, double& aReturnedOrientationDeciDeg )
1852 {
1853     int compOrientation = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
1854 
1855     int oDeg = (int) NormalizeAngle180( aOrientAngleDeciDeg );
1856 
1857     if( oDeg >= -450 && oDeg <= 450 )
1858     {
1859         compOrientation             = SYMBOL_ORIENTATION_T::SYM_ORIENT_0;
1860         aReturnedOrientationDeciDeg = 0.0;
1861     }
1862     else if( oDeg >= 450 && oDeg <= 1350 )
1863     {
1864         compOrientation             = SYMBOL_ORIENTATION_T::SYM_ORIENT_90;
1865         aReturnedOrientationDeciDeg = 900.0;
1866     }
1867     else if( oDeg >= 1350 || oDeg <= -1350 )
1868     {
1869         compOrientation             = SYMBOL_ORIENTATION_T::SYM_ORIENT_180;
1870         aReturnedOrientationDeciDeg = 1800.0;
1871     }
1872     else
1873     {
1874         compOrientation             = SYMBOL_ORIENTATION_T::SYM_ORIENT_270;
1875         aReturnedOrientationDeciDeg = 2700.0;
1876     }
1877 
1878     return compOrientation;
1879 }
1880 
1881 
getLocationOfNetElement(const NET_SCH & aNet,const NETELEMENT_ID & aNetElementID)1882 CADSTAR_SCH_ARCHIVE_LOADER::POINT CADSTAR_SCH_ARCHIVE_LOADER::getLocationOfNetElement(
1883         const NET_SCH& aNet, const NETELEMENT_ID& aNetElementID )
1884 {
1885     // clang-format off
1886     auto logUnknownNetElementError =
1887         [&]()
1888         {
1889             m_reporter->Report( wxString::Format( _( "Net %s references unknown net element %s. "
1890                                                      "The net was not properly loaded and may "
1891                                                      "require manual fixing." ),
1892                                                   getNetName( aNet ),
1893                                                   aNetElementID ),
1894                                 RPT_SEVERITY_ERROR );
1895 
1896             return POINT();
1897         };
1898     // clang-format on
1899 
1900     if( aNetElementID.Contains( "J" ) ) // Junction
1901     {
1902         if( aNet.Junctions.find( aNetElementID ) == aNet.Junctions.end() )
1903             return logUnknownNetElementError();
1904 
1905         return aNet.Junctions.at( aNetElementID ).Location;
1906     }
1907     else if( aNetElementID.Contains( "P" ) ) // Terminal/Pin of a symbol
1908     {
1909         if( aNet.Terminals.find( aNetElementID ) == aNet.Terminals.end() )
1910             return logUnknownNetElementError();
1911 
1912         SYMBOL_ID   symid  = aNet.Terminals.at( aNetElementID ).SymbolID;
1913         TERMINAL_ID termid = aNet.Terminals.at( aNetElementID ).TerminalID;
1914 
1915         if( Schematic.Symbols.find( symid ) == Schematic.Symbols.end() )
1916             return logUnknownNetElementError();
1917 
1918         SYMBOL    sym          = Schematic.Symbols.at( symid );
1919         SYMDEF_ID symdefid     = sym.SymdefID;
1920         wxPoint   symbolOrigin = sym.Origin;
1921 
1922         if( Library.SymbolDefinitions.find( symdefid ) == Library.SymbolDefinitions.end() )
1923             return logUnknownNetElementError();
1924 
1925         wxPoint libpinPosition =
1926                 Library.SymbolDefinitions.at( symdefid ).Terminals.at( termid ).Position;
1927         wxPoint libOrigin   = Library.SymbolDefinitions.at( symdefid ).Origin;
1928 
1929         wxPoint pinOffset = libpinPosition - libOrigin;
1930         pinOffset.x = ( pinOffset.x * sym.ScaleRatioNumerator ) / sym.ScaleRatioDenominator;
1931         pinOffset.y = ( pinOffset.y * sym.ScaleRatioNumerator ) / sym.ScaleRatioDenominator;
1932 
1933         wxPoint pinPosition = symbolOrigin + pinOffset;
1934 
1935         double compAngleDeciDeg = getAngleTenthDegree( sym.OrientAngle );
1936 
1937         if( sym.Mirror )
1938             pinPosition.x = ( 2 * symbolOrigin.x ) - pinPosition.x;
1939 
1940         double adjustedOrientationDecideg;
1941         getComponentOrientation( compAngleDeciDeg, adjustedOrientationDecideg );
1942 
1943         RotatePoint( &pinPosition, symbolOrigin, -adjustedOrientationDecideg );
1944 
1945         POINT retval;
1946         retval.x = pinPosition.x;
1947         retval.y = pinPosition.y;
1948 
1949         return retval;
1950     }
1951     else if( aNetElementID.Contains( "BT" ) ) // Bus Terminal
1952     {
1953         if( aNet.BusTerminals.find( aNetElementID ) == aNet.BusTerminals.end() )
1954             return logUnknownNetElementError();
1955 
1956         return aNet.BusTerminals.at( aNetElementID ).SecondPoint;
1957     }
1958     else if( aNetElementID.Contains( "BLKT" ) ) // Block Terminal (sheet hierarchy connection)
1959     {
1960         if( aNet.BlockTerminals.find( aNetElementID ) == aNet.BlockTerminals.end() )
1961             return logUnknownNetElementError();
1962 
1963         BLOCK_ID    blockid = aNet.BlockTerminals.at( aNetElementID ).BlockID;
1964         TERMINAL_ID termid  = aNet.BlockTerminals.at( aNetElementID ).TerminalID;
1965 
1966         if( Schematic.Blocks.find( blockid ) == Schematic.Blocks.end() )
1967             return logUnknownNetElementError();
1968 
1969         return Schematic.Blocks.at( blockid ).Terminals.at( termid ).Position;
1970     }
1971     else if( aNetElementID.Contains( "D" ) ) // Dangler
1972     {
1973         if( aNet.Danglers.find( aNetElementID ) == aNet.Danglers.end() )
1974             return logUnknownNetElementError();
1975 
1976         return aNet.Danglers.at( aNetElementID ).Position;
1977     }
1978     else
1979     {
1980         return logUnknownNetElementError();
1981     }
1982 
1983     return POINT();
1984 }
1985 
1986 
getNetName(const NET_SCH & aNet)1987 wxString CADSTAR_SCH_ARCHIVE_LOADER::getNetName( const NET_SCH& aNet )
1988 {
1989     wxString netname = aNet.Name;
1990 
1991     if( netname.IsEmpty() )
1992         netname = wxString::Format( "$%ld", aNet.SignalNum );
1993 
1994     return netname;
1995 }
1996 
1997 
loadGraphicStaightSegment(const wxPoint & aStartPoint,const wxPoint & aEndPoint,const LINECODE_ID & aCadstarLineCodeID,const LAYER_ID & aCadstarSheetID,const SCH_LAYER_ID & aKiCadSchLayerID,const wxPoint & aMoveVector,const double & aRotationAngleDeciDeg,const double & aScalingFactor,const wxPoint & aTransformCentre,const bool & aMirrorInvert)1998 void CADSTAR_SCH_ARCHIVE_LOADER::loadGraphicStaightSegment( const wxPoint& aStartPoint,
1999         const wxPoint& aEndPoint, const LINECODE_ID& aCadstarLineCodeID,
2000         const LAYER_ID& aCadstarSheetID, const SCH_LAYER_ID& aKiCadSchLayerID,
2001         const wxPoint& aMoveVector, const double& aRotationAngleDeciDeg,
2002         const double& aScalingFactor, const wxPoint& aTransformCentre, const bool& aMirrorInvert )
2003 {
2004     SCH_LINE* segment = new SCH_LINE();
2005 
2006     segment->SetLayer( aKiCadSchLayerID );
2007     segment->SetLineWidth( KiROUND( getLineThickness( aCadstarLineCodeID ) * aScalingFactor ) );
2008     segment->SetLineStyle( getLineStyle( aCadstarLineCodeID ) );
2009 
2010     //Apply transforms
2011     wxPoint startPoint = applyTransform( aStartPoint, aMoveVector, aRotationAngleDeciDeg,
2012                                          aScalingFactor, aTransformCentre, aMirrorInvert );
2013     wxPoint endPoint = applyTransform( aEndPoint, aMoveVector, aRotationAngleDeciDeg,
2014                                        aScalingFactor, aTransformCentre, aMirrorInvert );
2015 
2016     segment->SetStartPoint( startPoint );
2017     segment->SetEndPoint( endPoint );
2018 
2019     loadItemOntoKiCadSheet( aCadstarSheetID, segment );
2020 }
2021 
2022 
loadShapeVertices(const std::vector<VERTEX> & aCadstarVertices,LINECODE_ID aCadstarLineCodeID,LAYER_ID aCadstarSheetID,SCH_LAYER_ID aKiCadSchLayerID,const wxPoint & aMoveVector,const double & aRotationAngleDeciDeg,const double & aScalingFactor,const wxPoint & aTransformCentre,const bool & aMirrorInvert)2023 void CADSTAR_SCH_ARCHIVE_LOADER::loadShapeVertices( const std::vector<VERTEX>& aCadstarVertices,
2024         LINECODE_ID aCadstarLineCodeID, LAYER_ID aCadstarSheetID, SCH_LAYER_ID aKiCadSchLayerID,
2025         const wxPoint& aMoveVector, const double& aRotationAngleDeciDeg,
2026         const double& aScalingFactor, const wxPoint& aTransformCentre, const bool& aMirrorInvert )
2027 {
2028     const VERTEX* prev = &aCadstarVertices.at( 0 );
2029     const VERTEX* cur;
2030 
2031     wxASSERT_MSG(
2032             prev->Type == VERTEX_TYPE::POINT, "First vertex should always be a point vertex" );
2033 
2034     for( size_t ii = 1; ii < aCadstarVertices.size(); ii++ )
2035     {
2036         cur = &aCadstarVertices.at( ii );
2037 
2038         wxPoint   startPoint  = getKiCadPoint( prev->End );
2039         wxPoint   endPoint    = getKiCadPoint( cur->End );
2040         wxPoint   centerPoint = getKiCadPoint( cur->Center );
2041         bool      cw          = false;
2042 
2043         if( cur->Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE
2044                 || cur->Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE )
2045         {
2046             centerPoint = ( startPoint + endPoint ) / 2;
2047         }
2048 
2049         switch( cur->Type )
2050         {
2051         case VERTEX_TYPE::CLOCKWISE_SEMICIRCLE:
2052         case VERTEX_TYPE::CLOCKWISE_ARC:
2053             cw = true;
2054             KI_FALLTHROUGH;
2055         case VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE:
2056         case VERTEX_TYPE::ANTICLOCKWISE_ARC:
2057         {
2058             double arcStartAngle = getPolarAngle( startPoint - centerPoint );
2059             double arcEndAngle   = getPolarAngle( endPoint - centerPoint );
2060             double arcAngleDeciDeg = arcEndAngle - arcStartAngle;
2061 
2062             if( cw )
2063                 arcAngleDeciDeg = NormalizeAnglePos( arcAngleDeciDeg );
2064             else
2065                 arcAngleDeciDeg = NormalizeAngleNeg( arcAngleDeciDeg );
2066 
2067             SHAPE_ARC tempArc( centerPoint, startPoint, arcAngleDeciDeg / 10.0 );
2068             SHAPE_LINE_CHAIN arcSegments = tempArc.ConvertToPolyline( Millimeter2iu( 0.1 ) );
2069 
2070             // Load the arc as a series of piece-wise segments
2071 
2072             for( int jj = 0; jj < arcSegments.SegmentCount(); jj++ )
2073             {
2074                 wxPoint segStart = (wxPoint) arcSegments.Segment( jj ).A;
2075                 wxPoint segEnd   = (wxPoint) arcSegments.Segment( jj ).B;
2076 
2077                 loadGraphicStaightSegment( segStart, segEnd, aCadstarLineCodeID, aCadstarSheetID,
2078                                            aKiCadSchLayerID, aMoveVector, aRotationAngleDeciDeg,
2079                                            aScalingFactor, aTransformCentre, aMirrorInvert );
2080             }
2081         }
2082             break;
2083 
2084         case VERTEX_TYPE::POINT:
2085             loadGraphicStaightSegment( startPoint, endPoint, aCadstarLineCodeID, aCadstarSheetID,
2086                                        aKiCadSchLayerID, aMoveVector, aRotationAngleDeciDeg,
2087                                        aScalingFactor, aTransformCentre, aMirrorInvert );
2088             break;
2089 
2090         default:
2091             wxFAIL_MSG( "Unknown CADSTAR Vertex type" );
2092         }
2093 
2094 
2095         prev = cur;
2096     }
2097 }
2098 
2099 
loadFigure(const FIGURE & aCadstarFigure,const LAYER_ID & aCadstarSheetIDOverride,SCH_LAYER_ID aKiCadSchLayerID,const wxPoint & aMoveVector,const double & aRotationAngleDeciDeg,const double & aScalingFactor,const wxPoint & aTransformCentre,const bool & aMirrorInvert)2100 void CADSTAR_SCH_ARCHIVE_LOADER::loadFigure( const FIGURE& aCadstarFigure,
2101         const LAYER_ID& aCadstarSheetIDOverride, SCH_LAYER_ID aKiCadSchLayerID,
2102         const wxPoint& aMoveVector, const double& aRotationAngleDeciDeg,
2103         const double& aScalingFactor, const wxPoint& aTransformCentre, const bool& aMirrorInvert )
2104 {
2105     loadShapeVertices( aCadstarFigure.Shape.Vertices, aCadstarFigure.LineCodeID,
2106                        aCadstarSheetIDOverride, aKiCadSchLayerID, aMoveVector,
2107                        aRotationAngleDeciDeg, aScalingFactor, aTransformCentre, aMirrorInvert );
2108 
2109     for( CUTOUT cutout : aCadstarFigure.Shape.Cutouts )
2110     {
2111         loadShapeVertices( cutout.Vertices, aCadstarFigure.LineCodeID, aCadstarSheetIDOverride,
2112                            aKiCadSchLayerID, aMoveVector, aRotationAngleDeciDeg, aScalingFactor,
2113                            aTransformCentre, aMirrorInvert );
2114     }
2115 }
2116 
2117 
loadSheetAndChildSheets(LAYER_ID aCadstarSheetID,const wxPoint & aPosition,wxSize aSheetSize,const SCH_SHEET_PATH & aParentSheet)2118 void CADSTAR_SCH_ARCHIVE_LOADER::loadSheetAndChildSheets(
2119         LAYER_ID aCadstarSheetID, const wxPoint& aPosition, wxSize aSheetSize,
2120         const SCH_SHEET_PATH& aParentSheet )
2121 {
2122     wxCHECK_MSG( m_sheetMap.find( aCadstarSheetID ) == m_sheetMap.end(), ,
2123                  "Sheet already loaded!" );
2124 
2125     SCH_SHEET*  sheet  = new SCH_SHEET( aParentSheet.Last(), aPosition );
2126     SCH_SCREEN* screen = new SCH_SCREEN( m_schematic );
2127     SCH_SHEET_PATH instance( aParentSheet );
2128 
2129     sheet->SetSize( aSheetSize );
2130     sheet->SetScreen( screen );
2131 
2132     wxString name = Sheets.SheetNames.at( aCadstarSheetID );
2133 
2134     SCH_FIELD& sheetNameField = sheet->GetFields()[SHEETNAME];
2135     SCH_FIELD& filenameField  = sheet->GetFields()[SHEETFILENAME];
2136 
2137     sheetNameField.SetText( name );
2138 
2139     int sheetNum = getSheetNumber( aCadstarSheetID );
2140     wxString  loadedFilename = wxFileName( Filename ).GetName();
2141     std::string filename = wxString::Format( "%s_%02d", loadedFilename, sheetNum ).ToStdString();
2142 
2143     ReplaceIllegalFileNameChars( &filename );
2144     filename += wxT( "." ) + KiCadSchematicFileExtension;
2145 
2146     filenameField.SetText( filename );
2147 
2148     wxFileName fn( m_schematic->Prj().GetProjectPath() + filename );
2149     sheet->GetScreen()->SetFileName( fn.GetFullPath() );
2150     aParentSheet.Last()->GetScreen()->Append( sheet );
2151     instance.push_back( sheet );
2152     sheet->AddInstance( instance );
2153 
2154     wxString pageNumStr = wxString::Format( "%d", getSheetNumber( aCadstarSheetID ) );
2155     sheet->SetPageNumber( instance, pageNumStr );
2156 
2157     sheet->AutoplaceFields( /* aScreen */ nullptr, /* aManual */ false );
2158 
2159     m_sheetMap.insert( { aCadstarSheetID, sheet } );
2160 
2161     loadChildSheets( aCadstarSheetID, instance );
2162 }
2163 
2164 
loadChildSheets(LAYER_ID aCadstarSheetID,const SCH_SHEET_PATH & aSheet)2165 void CADSTAR_SCH_ARCHIVE_LOADER::loadChildSheets( LAYER_ID aCadstarSheetID,
2166                                                   const SCH_SHEET_PATH& aSheet )
2167 {
2168     wxCHECK_MSG( m_sheetMap.find( aCadstarSheetID ) != m_sheetMap.end(), ,
2169             "FIXME! Parent sheet should be loaded before attempting to load subsheets" );
2170 
2171     for( std::pair<BLOCK_ID, BLOCK> blockPair : Schematic.Blocks )
2172     {
2173         BLOCK& block = blockPair.second;
2174 
2175         if( block.LayerID == aCadstarSheetID && block.Type == BLOCK::TYPE::CHILD )
2176         {
2177             if( block.AssocLayerID == wxT( "NO_LINK" ) )
2178             {
2179                 if( block.Figures.size() > 0 )
2180                 {
2181                     m_reporter->Report( wxString::Format( _( "The block ID %s (Block name: '%s') "
2182                                                              "is drawn on sheet '%s' but is not "
2183                                                              "linked to another sheet in the "
2184                                                              "design. KiCad requires all sheet "
2185                                                              "symbols to be associated to a sheet, "
2186                                                              "so the block was not loaded." ),
2187                                                           block.ID, block.Name,
2188                                                           Sheets.SheetNames.at( aCadstarSheetID ) ),
2189                                         RPT_SEVERITY_ERROR );
2190                 }
2191 
2192                 continue;
2193             }
2194 
2195             // In KiCad you can only draw rectangular shapes whereas in Cadstar arbitrary shapes
2196             // are allowed. We will calculate the extents of the Cadstar shape and draw a rectangle
2197 
2198             std::pair<wxPoint, wxSize> blockExtents;
2199 
2200             if( block.Figures.size() > 0 )
2201             {
2202                 blockExtents = getFigureExtentsKiCad( block.Figures.begin()->second );
2203             }
2204             else
2205             {
2206                 THROW_IO_ERROR( wxString::Format( _( "The CADSTAR schematic might be corrupt: "
2207                                                      "Block %s references a child sheet but has no "
2208                                                      "Figure defined." ),
2209                                                   block.ID ) );
2210             }
2211 
2212             loadSheetAndChildSheets( block.AssocLayerID, blockExtents.first, blockExtents.second,
2213                                      aSheet );
2214 
2215             // Hide all KiCad sheet properties (sheet name/filename is not applicable in CADSTAR)
2216             SCH_SHEET* loadedSheet = m_sheetMap.at( block.AssocLayerID );
2217             SCH_FIELDS fields = loadedSheet->GetFields();
2218 
2219             for( SCH_FIELD& field : fields )
2220             {
2221                 field.SetVisible( false );
2222             }
2223 
2224             if( block.HasBlockLabel )
2225             {
2226                 //@todo use below code when KiCad supports multi-line fields
2227                 /*
2228                 // Add the block label as a separate field
2229                 SCH_FIELD blockNameField( getKiCadPoint( block.BlockLabel.Position ), 2,
2230                         loadedSheet, wxString( "Block name" ) );
2231                 blockNameField.SetText( block.Name );
2232                 blockNameField.SetVisible( true );
2233 
2234                 applyTextSettings( &blockNameField,
2235                                     block.BlockLabel.TextCodeID,
2236                                     block.BlockLabel.Alignment,
2237                                     block.BlockLabel.Justification,
2238                                     block.BlockLabel.OrientAngle,
2239                                     block.BlockLabel.Mirror );
2240 
2241                 fields.push_back( blockNameField );*/
2242 
2243                 // For now as as a text item (supports multi-line properly)
2244                 SCH_TEXT* kiTxt = new SCH_TEXT();
2245 
2246                 kiTxt->SetParent( m_schematic );
2247                 kiTxt->SetPosition( getKiCadPoint( block.BlockLabel.Position ) );
2248                 kiTxt->SetText( block.Name );
2249 
2250                 applyTextSettings( kiTxt,
2251                                     block.BlockLabel.TextCodeID,
2252                                     block.BlockLabel.Alignment,
2253                                     block.BlockLabel.Justification,
2254                                     block.BlockLabel.OrientAngle,
2255                                     block.BlockLabel.Mirror );
2256 
2257                 loadItemOntoKiCadSheet( aCadstarSheetID, kiTxt );
2258             }
2259 
2260             loadedSheet->SetFields( fields );
2261         }
2262     }
2263 }
2264 
2265 
findOrphanSheets()2266 std::vector<CADSTAR_SCH_ARCHIVE_LOADER::LAYER_ID> CADSTAR_SCH_ARCHIVE_LOADER::findOrphanSheets()
2267 {
2268     std::vector<LAYER_ID> childSheets, orphanSheets;
2269 
2270     //Find all sheets that are child of another
2271     for( std::pair<BLOCK_ID, BLOCK> blockPair : Schematic.Blocks )
2272     {
2273         BLOCK&    block        = blockPair.second;
2274         LAYER_ID& assocSheetID = block.AssocLayerID;
2275 
2276         if( block.Type == BLOCK::TYPE::CHILD )
2277             childSheets.push_back( assocSheetID );
2278     }
2279 
2280     //Add sheets that do not have a parent
2281     for( LAYER_ID sheetID : Sheets.SheetOrder )
2282     {
2283         if( std::find( childSheets.begin(), childSheets.end(), sheetID ) == childSheets.end() )
2284             orphanSheets.push_back( sheetID );
2285     }
2286 
2287     return orphanSheets;
2288 }
2289 
2290 
getSheetNumber(LAYER_ID aCadstarSheetID)2291 int CADSTAR_SCH_ARCHIVE_LOADER::getSheetNumber( LAYER_ID aCadstarSheetID )
2292 {
2293     int i = 1;
2294 
2295     for( LAYER_ID sheetID : Sheets.SheetOrder )
2296     {
2297         if( sheetID == aCadstarSheetID )
2298             return i;
2299 
2300         ++i;
2301     }
2302 
2303     return -1;
2304 }
2305 
2306 
loadItemOntoKiCadSheet(LAYER_ID aCadstarSheetID,SCH_ITEM * aItem)2307 void CADSTAR_SCH_ARCHIVE_LOADER::loadItemOntoKiCadSheet( LAYER_ID aCadstarSheetID, SCH_ITEM* aItem )
2308 {
2309     wxCHECK_MSG( aItem, /*void*/, "aItem is null" );
2310 
2311     if( aCadstarSheetID == "ALL_SHEETS" )
2312     {
2313         SCH_ITEM* duplicateItem;
2314 
2315         for( std::pair<LAYER_ID, SHEET_NAME> sheetPair : Sheets.SheetNames )
2316         {
2317             LAYER_ID sheetID = sheetPair.first;
2318             duplicateItem    = aItem->Duplicate();
2319             m_sheetMap.at( sheetID )->GetScreen()->Append( aItem->Duplicate() );
2320         }
2321 
2322         //Get rid of the extra copy:
2323         delete aItem;
2324         aItem = duplicateItem;
2325     }
2326     else if( aCadstarSheetID == "NO_SHEET" )
2327     {
2328         wxASSERT_MSG(
2329                 false, "Trying to add an item to NO_SHEET? This might be a documentation symbol." );
2330     }
2331     else
2332     {
2333         if( m_sheetMap.find( aCadstarSheetID ) != m_sheetMap.end() )
2334         {
2335             m_sheetMap.at( aCadstarSheetID )->GetScreen()->Append( aItem );
2336         }
2337         else
2338         {
2339             delete aItem;
2340             wxASSERT_MSG( false, "Unknown Sheet ID." );
2341         }
2342     }
2343 }
2344 
2345 
getSymDefFromName(const wxString & aSymdefName,const wxString & aSymDefAlternate)2346 CADSTAR_SCH_ARCHIVE_LOADER::SYMDEF_ID CADSTAR_SCH_ARCHIVE_LOADER::getSymDefFromName(
2347         const wxString& aSymdefName, const wxString& aSymDefAlternate )
2348 {
2349     // Do a case-insensitive comparison
2350     for( std::pair<SYMDEF_ID, SYMDEF_SCM> symPair : Library.SymbolDefinitions )
2351     {
2352         SYMDEF_ID  id     = symPair.first;
2353         SYMDEF_SCM symdef = symPair.second;
2354 
2355         if( symdef.ReferenceName.Lower() == aSymdefName.Lower()
2356             && symdef.Alternate.Lower() == aSymDefAlternate.Lower() )
2357         {
2358             return id;
2359         }
2360     }
2361 
2362     return SYMDEF_ID();
2363 }
2364 
2365 
isAttributeVisible(const ATTRIBUTE_ID & aCadstarAttributeID)2366 bool CADSTAR_SCH_ARCHIVE_LOADER::isAttributeVisible( const ATTRIBUTE_ID& aCadstarAttributeID )
2367 {
2368     // Use CADSTAR visibility settings to determine if an attribute is visible
2369     if( AttrColors.AttributeColors.find( aCadstarAttributeID ) != AttrColors.AttributeColors.end() )
2370     {
2371         return AttrColors.AttributeColors.at( aCadstarAttributeID ).IsVisible;
2372     }
2373 
2374     return false; // If there is no visibility setting, assume not displayed
2375 }
2376 
2377 
getLineThickness(const LINECODE_ID & aCadstarLineCodeID)2378 int CADSTAR_SCH_ARCHIVE_LOADER::getLineThickness( const LINECODE_ID& aCadstarLineCodeID )
2379 {
2380     wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID )
2381                      != Assignments.Codedefs.LineCodes.end(),
2382              Mils2iu( DEFAULT_WIRE_WIDTH_MILS ) );
2383 
2384     return getKiCadLength( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Width );
2385 }
2386 
2387 
getLineStyle(const LINECODE_ID & aCadstarLineCodeID)2388 PLOT_DASH_TYPE CADSTAR_SCH_ARCHIVE_LOADER::getLineStyle( const LINECODE_ID& aCadstarLineCodeID )
2389 {
2390     wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID )
2391                      != Assignments.Codedefs.LineCodes.end(),
2392              PLOT_DASH_TYPE::SOLID );
2393 
2394     // clang-format off
2395     switch( Assignments.Codedefs.LineCodes.at( aCadstarLineCodeID ).Style )
2396     {
2397     case LINESTYLE::DASH:       return PLOT_DASH_TYPE::DASH;
2398     case LINESTYLE::DASHDOT:    return PLOT_DASH_TYPE::DASHDOT;
2399     case LINESTYLE::DASHDOTDOT: return PLOT_DASH_TYPE::DASHDOT; //TODO: update in future
2400     case LINESTYLE::DOT:        return PLOT_DASH_TYPE::DOT;
2401     case LINESTYLE::SOLID:      return PLOT_DASH_TYPE::SOLID;
2402     default:                    return PLOT_DASH_TYPE::DEFAULT;
2403     }
2404     // clang-format on
2405 
2406     return PLOT_DASH_TYPE();
2407 }
2408 
2409 
getTextCode(const TEXTCODE_ID & aCadstarTextCodeID)2410 CADSTAR_SCH_ARCHIVE_LOADER::TEXTCODE CADSTAR_SCH_ARCHIVE_LOADER::getTextCode(
2411         const TEXTCODE_ID& aCadstarTextCodeID )
2412 {
2413     wxCHECK( Assignments.Codedefs.TextCodes.find( aCadstarTextCodeID )
2414                      != Assignments.Codedefs.TextCodes.end(),
2415              TEXTCODE() );
2416 
2417     return Assignments.Codedefs.TextCodes.at( aCadstarTextCodeID );
2418 }
2419 
2420 
getTextHeightFromTextCode(const TEXTCODE_ID & aCadstarTextCodeID)2421 int CADSTAR_SCH_ARCHIVE_LOADER::getTextHeightFromTextCode( const TEXTCODE_ID& aCadstarTextCodeID )
2422 {
2423     TEXTCODE txtCode = getTextCode( aCadstarTextCodeID );
2424 
2425     return KiROUND( (double) getKiCadLength( txtCode.Height ) * TXT_HEIGHT_RATIO );
2426 }
2427 
2428 
getAttributeName(const ATTRIBUTE_ID & aCadstarAttributeID)2429 wxString CADSTAR_SCH_ARCHIVE_LOADER::getAttributeName( const ATTRIBUTE_ID& aCadstarAttributeID )
2430 {
2431     wxCHECK( Assignments.Codedefs.AttributeNames.find( aCadstarAttributeID )
2432                      != Assignments.Codedefs.AttributeNames.end(),
2433              wxEmptyString );
2434 
2435     return Assignments.Codedefs.AttributeNames.at( aCadstarAttributeID ).Name;
2436 }
2437 
2438 
getPart(const PART_ID & aCadstarPartID)2439 CADSTAR_SCH_ARCHIVE_LOADER::PART CADSTAR_SCH_ARCHIVE_LOADER::getPart(
2440         const PART_ID& aCadstarPartID )
2441 {
2442     wxCHECK( Parts.PartDefinitions.find( aCadstarPartID ) != Parts.PartDefinitions.end(), PART() );
2443 
2444     return Parts.PartDefinitions.at( aCadstarPartID );
2445 }
2446 
2447 
getRouteCode(const ROUTECODE_ID & aCadstarRouteCodeID)2448 CADSTAR_SCH_ARCHIVE_LOADER::ROUTECODE CADSTAR_SCH_ARCHIVE_LOADER::getRouteCode(
2449         const ROUTECODE_ID& aCadstarRouteCodeID )
2450 {
2451     wxCHECK( Assignments.Codedefs.RouteCodes.find( aCadstarRouteCodeID )
2452                      != Assignments.Codedefs.RouteCodes.end(),
2453              ROUTECODE() );
2454 
2455     return Assignments.Codedefs.RouteCodes.at( aCadstarRouteCodeID );
2456 }
2457 
2458 
getPartDefinitionPin(const PART & aCadstarPart,const GATE_ID & aGateID,const TERMINAL_ID & aTerminalID)2459 CADSTAR_SCH_ARCHIVE_LOADER::PART::DEFINITION::PIN CADSTAR_SCH_ARCHIVE_LOADER::getPartDefinitionPin(
2460         const PART& aCadstarPart, const GATE_ID& aGateID, const TERMINAL_ID& aTerminalID )
2461 {
2462     for( std::pair<PART_DEFINITION_PIN_ID, PART::DEFINITION::PIN> pinPair :
2463             aCadstarPart.Definition.Pins )
2464     {
2465         PART::DEFINITION::PIN partPin = pinPair.second;
2466 
2467         if( partPin.TerminalGate == aGateID && partPin.TerminalPin == aTerminalID )
2468             return partPin;
2469     }
2470 
2471     return PART::DEFINITION::PIN();
2472 }
2473 
2474 
getKiCadPinType(const PART::PIN_TYPE & aPinType)2475 ELECTRICAL_PINTYPE CADSTAR_SCH_ARCHIVE_LOADER::getKiCadPinType( const PART::PIN_TYPE& aPinType )
2476 {
2477     switch( aPinType )
2478     {
2479     case PART::PIN_TYPE::UNCOMMITTED:        return ELECTRICAL_PINTYPE::PT_PASSIVE;
2480     case PART::PIN_TYPE::INPUT:              return ELECTRICAL_PINTYPE::PT_INPUT;
2481     case PART::PIN_TYPE::OUTPUT_OR:          return ELECTRICAL_PINTYPE::PT_OPENCOLLECTOR;
2482     case PART::PIN_TYPE::OUTPUT_NOT_OR:      return ELECTRICAL_PINTYPE::PT_OUTPUT;
2483     case PART::PIN_TYPE::OUTPUT_NOT_NORM_OR: return ELECTRICAL_PINTYPE::PT_OUTPUT;
2484     case PART::PIN_TYPE::POWER:              return ELECTRICAL_PINTYPE::PT_POWER_IN;
2485     case PART::PIN_TYPE::GROUND:             return ELECTRICAL_PINTYPE::PT_POWER_IN;
2486     case PART::PIN_TYPE::TRISTATE_BIDIR:     return ELECTRICAL_PINTYPE::PT_BIDI;
2487     case PART::PIN_TYPE::TRISTATE_INPUT:     return ELECTRICAL_PINTYPE::PT_INPUT;
2488     case PART::PIN_TYPE::TRISTATE_DRIVER:    return ELECTRICAL_PINTYPE::PT_OUTPUT;
2489     }
2490 
2491     return ELECTRICAL_PINTYPE::PT_UNSPECIFIED;
2492 }
2493 
getKiCadUnitNumberFromGate(const GATE_ID & aCadstarGateID)2494 int CADSTAR_SCH_ARCHIVE_LOADER::getKiCadUnitNumberFromGate( const GATE_ID& aCadstarGateID )
2495 {
2496     if( aCadstarGateID.IsEmpty() )
2497         return 1;
2498 
2499     return (int) aCadstarGateID.Upper().GetChar( 0 ) - (int) wxUniChar( 'A' ) + 1;
2500 }
2501 
2502 
getSpinStyle(const long long & aCadstarOrientation,bool aMirror)2503 LABEL_SPIN_STYLE CADSTAR_SCH_ARCHIVE_LOADER::getSpinStyle( const long long& aCadstarOrientation,
2504                                                            bool aMirror )
2505 {
2506     double           orientationDeciDegree = getAngleTenthDegree( aCadstarOrientation );
2507     LABEL_SPIN_STYLE spinStyle             = getSpinStyleDeciDeg( orientationDeciDegree );
2508 
2509     if( aMirror )
2510     {
2511         spinStyle = spinStyle.RotateCCW();
2512         spinStyle = spinStyle.RotateCCW();
2513     }
2514 
2515     return spinStyle;
2516 }
2517 
2518 
getSpinStyleDeciDeg(const double & aOrientationDeciDeg)2519 LABEL_SPIN_STYLE CADSTAR_SCH_ARCHIVE_LOADER::getSpinStyleDeciDeg(
2520         const double& aOrientationDeciDeg )
2521 {
2522     LABEL_SPIN_STYLE spinStyle = LABEL_SPIN_STYLE::LEFT;
2523 
2524     int oDeg = (int) NormalizeAngle180( aOrientationDeciDeg );
2525 
2526     if( oDeg >= -450 && oDeg <= 450 )
2527         spinStyle = LABEL_SPIN_STYLE::RIGHT; // 0deg
2528     else if( oDeg >= 450 && oDeg <= 1350 )
2529         spinStyle = LABEL_SPIN_STYLE::UP; // 90deg
2530     else if( oDeg >= 1350 || oDeg <= -1350 )
2531         spinStyle = LABEL_SPIN_STYLE::LEFT; // 180deg
2532     else
2533         spinStyle = LABEL_SPIN_STYLE::BOTTOM; // 270deg
2534 
2535     return spinStyle;
2536 }
2537 
2538 
2539 CADSTAR_SCH_ARCHIVE_LOADER::ALIGNMENT
mirrorX(const ALIGNMENT & aCadstarAlignment)2540 CADSTAR_SCH_ARCHIVE_LOADER::mirrorX( const ALIGNMENT& aCadstarAlignment )
2541 {
2542     switch( aCadstarAlignment )
2543     {
2544     // Change left to right:
2545     case ALIGNMENT::NO_ALIGNMENT:
2546     case ALIGNMENT::BOTTOMLEFT:    return ALIGNMENT::BOTTOMRIGHT;
2547     case ALIGNMENT::CENTERLEFT:    return ALIGNMENT::CENTERRIGHT;
2548     case ALIGNMENT::TOPLEFT:       return ALIGNMENT::TOPRIGHT;
2549 
2550     //Change right to left:
2551     case ALIGNMENT::BOTTOMRIGHT:   return ALIGNMENT::BOTTOMLEFT;
2552     case ALIGNMENT::CENTERRIGHT:   return ALIGNMENT::CENTERLEFT;
2553     case ALIGNMENT::TOPRIGHT:      return ALIGNMENT::TOPLEFT;
2554 
2555     // Center alignment does not mirror:
2556     case ALIGNMENT::BOTTOMCENTER:
2557     case ALIGNMENT::CENTERCENTER:
2558     case ALIGNMENT::TOPCENTER:     return aCadstarAlignment;
2559 
2560     // Shouldn't be here
2561     default: wxFAIL_MSG( "Unknown Cadstar Alignment" ); return aCadstarAlignment;
2562     }
2563 }
2564 
2565 
2566 CADSTAR_SCH_ARCHIVE_LOADER::ALIGNMENT
rotate180(const ALIGNMENT & aCadstarAlignment)2567 CADSTAR_SCH_ARCHIVE_LOADER::rotate180( const ALIGNMENT& aCadstarAlignment )
2568 {
2569     switch( aCadstarAlignment )
2570     {
2571     case ALIGNMENT::NO_ALIGNMENT:
2572     case ALIGNMENT::BOTTOMLEFT:    return ALIGNMENT::TOPRIGHT;
2573     case ALIGNMENT::BOTTOMCENTER:  return ALIGNMENT::TOPCENTER;
2574     case ALIGNMENT::BOTTOMRIGHT:   return ALIGNMENT::TOPLEFT;
2575     case ALIGNMENT::TOPLEFT:       return ALIGNMENT::BOTTOMRIGHT;
2576     case ALIGNMENT::TOPCENTER:     return ALIGNMENT::BOTTOMCENTER;
2577     case ALIGNMENT::TOPRIGHT:      return ALIGNMENT::BOTTOMLEFT;
2578     case ALIGNMENT::CENTERLEFT:    return ALIGNMENT::CENTERRIGHT;
2579     case ALIGNMENT::CENTERCENTER:  return ALIGNMENT::CENTERCENTER;
2580     case ALIGNMENT::CENTERRIGHT:   return ALIGNMENT::CENTERLEFT;
2581 
2582     // Shouldn't be here
2583     default: wxFAIL_MSG( "Unknown Cadstar Alignment" ); return aCadstarAlignment;
2584     }
2585 }
2586 
2587 
applyTextSettings(EDA_TEXT * aKiCadTextItem,const TEXTCODE_ID & aCadstarTextCodeID,const ALIGNMENT & aCadstarAlignment,const JUSTIFICATION & aCadstarJustification,const long long aCadstarOrientAngle,bool aMirrored)2588 void CADSTAR_SCH_ARCHIVE_LOADER::applyTextSettings( EDA_TEXT*            aKiCadTextItem,
2589                                                     const TEXTCODE_ID&   aCadstarTextCodeID,
2590                                                     const ALIGNMENT&     aCadstarAlignment,
2591                                                     const JUSTIFICATION& aCadstarJustification,
2592                                                     const long long      aCadstarOrientAngle,
2593                                                     bool                 aMirrored )
2594 {
2595     // Justification ignored for now as not supported in Eeschema, but leaving this code in
2596     // place for future upgrades.
2597     // TODO update this when Eeschema supports justification independent of anchor position.
2598 
2599     TEXTCODE textCode = getTextCode( aCadstarTextCodeID );
2600     int      textHeight = KiROUND( (double) getKiCadLength( textCode.Height ) * TXT_HEIGHT_RATIO );
2601     int      textWidth = getKiCadLength( textCode.Width );
2602 
2603     // Ensure we have no Cadstar overbar characters
2604     wxString escapedText = HandleTextOverbar( aKiCadTextItem->GetText() );
2605     aKiCadTextItem->SetText( escapedText );
2606 
2607     // The width is zero for all non-cadstar fonts. Using a width equal to 2/3 the height seems
2608     // to work well for most fonts.
2609     if( textWidth == 0 )
2610         textWidth = getKiCadLength( 2 * textCode.Height / 3 );
2611 
2612     aKiCadTextItem->SetTextWidth( textWidth );
2613     aKiCadTextItem->SetTextHeight( textHeight );
2614     aKiCadTextItem->SetTextThickness( getKiCadLength( textCode.LineWidth ) );
2615     aKiCadTextItem->SetTextAngle( getAngleTenthDegree( aCadstarOrientAngle ) );
2616     aKiCadTextItem->SetBold( textCode.Font.Modifier1 == FONT_BOLD );
2617     aKiCadTextItem->SetItalic( textCode.Font.Italic );
2618 
2619     ALIGNMENT textAlignment = aCadstarAlignment;
2620 
2621     // KiCad mirrors the justification and alignment when the symbol is mirrored but CADSTAR
2622     // specifies it post-mirroring. In contrast, if the text item itself is mirrored (not
2623     // supported in KiCad), CADSTAR specifies the alignment and justification pre-mirroring
2624     if( aMirrored )
2625         textAlignment = mirrorX( aCadstarAlignment );
2626 
2627     auto setAlignment = [&]( EDA_TEXT* aText, ALIGNMENT aAlignment )
2628                         {
2629                             switch( aAlignment )
2630                             {
2631                             case ALIGNMENT::NO_ALIGNMENT: // Bottom left of the first line
2632                                 //No exact KiCad equivalent, so lets move the position of the text
2633                                 FixTextPositionNoAlignment( aText );
2634                                 KI_FALLTHROUGH;
2635                             case ALIGNMENT::BOTTOMLEFT:
2636                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2637                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
2638                                 break;
2639 
2640                             case ALIGNMENT::BOTTOMCENTER:
2641                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2642                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
2643                                 break;
2644 
2645                             case ALIGNMENT::BOTTOMRIGHT:
2646                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
2647                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
2648                                 break;
2649 
2650                             case ALIGNMENT::CENTERLEFT:
2651                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
2652                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
2653                                 break;
2654 
2655                             case ALIGNMENT::CENTERCENTER:
2656                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
2657                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
2658                                 break;
2659 
2660                             case ALIGNMENT::CENTERRIGHT:
2661                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
2662                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
2663                                 break;
2664 
2665                             case ALIGNMENT::TOPLEFT:
2666                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2667                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
2668                                 break;
2669 
2670                             case ALIGNMENT::TOPCENTER:
2671                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2672                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER );
2673                                 break;
2674 
2675                             case ALIGNMENT::TOPRIGHT:
2676                                 aText->SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
2677                                 aText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
2678                                 break;
2679                             }
2680                         };
2681 
2682     LABEL_SPIN_STYLE spin = getSpinStyle( aCadstarOrientAngle, aMirrored );
2683     EDA_ITEM* textEdaItem = dynamic_cast<EDA_ITEM*>( aKiCadTextItem );
2684     wxCHECK( textEdaItem, /* void */ ); // ensure this is a EDA_ITEM
2685 
2686     switch( textEdaItem->Type() )
2687     {
2688     // Some KiCad schematic text items only permit a limited amount of angles
2689     // and text justifications
2690     case LIB_TEXT_T:
2691     case SCH_FIELD_T:
2692     case LIB_FIELD_T:
2693     {
2694         // Spin style not used. All text justifications are permitted. However, only orientations
2695         // of 0 deg or 90 deg are supported
2696         double angleDeciDeg = NormalizeAnglePos( aKiCadTextItem->GetTextAngle() );
2697         int    quadrant = KiROUND( angleDeciDeg / 900.0 );
2698         quadrant %= 4;
2699 
2700         switch( quadrant )
2701         {
2702         case 0:
2703             angleDeciDeg = 0;
2704             break;
2705         case 1:
2706             angleDeciDeg = 900;
2707             break;
2708         case 2:
2709             angleDeciDeg = 0;
2710             textAlignment = rotate180( textAlignment );
2711             break;
2712         case 3:
2713             angleDeciDeg = 900;
2714             textAlignment = rotate180( textAlignment );
2715             break;
2716         default: wxFAIL_MSG( "Unknown Quadrant" );
2717         }
2718 
2719         aKiCadTextItem->SetTextAngle( angleDeciDeg );
2720         setAlignment( aKiCadTextItem, textAlignment );
2721     }
2722         return;
2723 
2724     case SCH_TEXT_T:
2725     {
2726         // Note spin style in a SCH_TEXT results in a vertical alignment GR_TEXT_VJUSTIFY_BOTTOM
2727         // so need to adjust the location of the text element based on Cadstar's original text
2728         // alignment (anchor position).
2729         setAlignment( aKiCadTextItem, textAlignment );
2730         EDA_RECT bb = textEdaItem->GetBoundingBox();
2731         int      off = static_cast<SCH_TEXT*>( aKiCadTextItem )->GetTextOffset();
2732         wxPoint  pos;
2733 
2734         // Change the anchor point of the text item to make it match the same bounding box
2735         // And correct the error introduced by the text offsetting in KiCad
2736         switch( spin )
2737         {
2738         case LABEL_SPIN_STYLE::BOTTOM: pos = { bb.GetRight() - off, bb.GetTop()          }; break;
2739         case LABEL_SPIN_STYLE::UP:     pos = { bb.GetRight() - off, bb.GetBottom()       }; break;
2740         case LABEL_SPIN_STYLE::LEFT:   pos = { bb.GetRight()      , bb.GetBottom() + off }; break;
2741         case LABEL_SPIN_STYLE::RIGHT:  pos = { bb.GetLeft()       , bb.GetBottom() + off }; break;
2742         }
2743 
2744         aKiCadTextItem->SetTextPos( pos );
2745     }
2746         KI_FALLTHROUGH;
2747 
2748     // We don't want to change position of net labels as that would break connectivity
2749     case SCH_LABEL_T:
2750     case SCH_GLOBAL_LABEL_T:
2751     case SCH_HIER_LABEL_T:
2752     case SCH_SHEET_PIN_T:
2753         static_cast<SCH_TEXT*>( aKiCadTextItem )->SetLabelSpinStyle( spin );
2754         return;
2755 
2756     default:
2757         wxFAIL_MSG( "Unexpected item type" );
2758         return;
2759     }
2760 }
2761 
2762 
getKiCadSchText(const TEXT & aCadstarTextElement)2763 SCH_TEXT* CADSTAR_SCH_ARCHIVE_LOADER::getKiCadSchText( const TEXT& aCadstarTextElement )
2764 {
2765     SCH_TEXT* kiTxt = new SCH_TEXT();
2766 
2767     kiTxt->SetParent( m_schematic ); // set to the schematic for now to avoid asserts
2768     kiTxt->SetPosition( getKiCadPoint( aCadstarTextElement.Position ) );
2769     kiTxt->SetText( aCadstarTextElement.Text );
2770 
2771     applyTextSettings( kiTxt,
2772                        aCadstarTextElement.TextCodeID,
2773                        aCadstarTextElement.Alignment,
2774                        aCadstarTextElement.Justification,
2775                        aCadstarTextElement.OrientAngle,
2776                        aCadstarTextElement.Mirror );
2777 
2778     return kiTxt;
2779 }
2780 
2781 
getScaledLibPart(const LIB_SYMBOL * aSymbol,long long aScalingFactorNumerator,long long aScalingFactorDenominator)2782 LIB_SYMBOL* CADSTAR_SCH_ARCHIVE_LOADER::getScaledLibPart( const LIB_SYMBOL* aSymbol,
2783                                                           long long aScalingFactorNumerator,
2784                                                           long long aScalingFactorDenominator )
2785 {
2786     LIB_SYMBOL* retval = new LIB_SYMBOL( *aSymbol );
2787 
2788     if( aScalingFactorNumerator == aScalingFactorDenominator )
2789         return retval; // 1:1 scale, nothing to do
2790 
2791     auto scaleLen =
2792         [&]( int aLength ) -> int
2793         {
2794             return( aLength * aScalingFactorNumerator ) / aScalingFactorDenominator;
2795         };
2796 
2797     auto scalePt =
2798         [&]( wxPoint aCoord ) -> wxPoint
2799         {
2800             return wxPoint( scaleLen( aCoord.x ), scaleLen( aCoord.y ) );
2801         };
2802 
2803     auto scaleSize =
2804         [&]( wxSize aSize ) -> wxSize
2805         {
2806             return wxSize( scaleLen( aSize.x ), scaleLen( aSize.y ) );
2807         };
2808 
2809     LIB_ITEMS_CONTAINER& items = retval->GetDrawItems();
2810 
2811     for( LIB_ITEM& item : items )
2812     {
2813         switch( item.Type() )
2814         {
2815         case KICAD_T::LIB_SHAPE_T:
2816         {
2817             LIB_SHAPE& shape = static_cast<LIB_SHAPE&>( item );
2818 
2819             if( shape.GetShape() == SHAPE_T::ARC )
2820             {
2821                 shape.SetPosition( scalePt( shape.GetPosition() ) );
2822                 shape.SetStart( scalePt( shape.GetStart() ) );
2823                 shape.SetEnd( scalePt( shape.GetEnd() ) );
2824             }
2825             else if( shape.GetShape() == SHAPE_T::POLY )
2826             {
2827                 SHAPE_LINE_CHAIN& poly = shape.GetPolyShape().Outline( 0 );
2828 
2829                 for( size_t ii = 0; ii < poly.GetPointCount(); ++ii )
2830                     poly.SetPoint( ii, scalePt( (wxPoint) poly.CPoint( ii ) ) );
2831             }
2832         }
2833             break;
2834 
2835         case KICAD_T::LIB_PIN_T:
2836         {
2837             LIB_PIN& pin = static_cast<LIB_PIN&>( item );
2838 
2839             pin.SetPosition( scalePt( pin.GetPosition() ) );
2840             pin.SetLength( scaleLen( pin.GetLength() ) );
2841         }
2842             break;
2843 
2844         case KICAD_T::LIB_TEXT_T:
2845         {
2846             LIB_TEXT& txt = static_cast<LIB_TEXT&>( item );
2847 
2848             txt.SetPosition( scalePt( txt.GetPosition() ) );
2849             txt.SetTextSize( scaleSize( txt.GetTextSize() ) );
2850         }
2851             break;
2852 
2853         default:
2854             break;
2855         }
2856 
2857     }
2858 
2859     return retval;
2860 }
2861 
2862 
fixUpLibraryPins(LIB_SYMBOL * aSymbolToFix,int aGateNumber)2863 void CADSTAR_SCH_ARCHIVE_LOADER::fixUpLibraryPins( LIB_SYMBOL* aSymbolToFix, int aGateNumber )
2864 {
2865     auto compLambda = []( const VECTOR2I& aA, const VECTOR2I& aB )
2866     {
2867         return LexicographicalCompare( aA, aB ) < 0;
2868     };
2869 
2870     // Store a list of vertical or horizontal segments in the symbol
2871     // Note: Need the custom comparison function to ensure the map is sorted correctly
2872     std::map<VECTOR2I, SHAPE_LINE_CHAIN, decltype( compLambda )> uniqueSegments( compLambda );
2873 
2874     LIB_ITEMS_CONTAINER::ITERATOR shapeIt = aSymbolToFix->GetDrawItems().begin( LIB_SHAPE_T );
2875 
2876     for( ; shapeIt != aSymbolToFix->GetDrawItems().end( LIB_SHAPE_T ); ++shapeIt )
2877     {
2878         LIB_SHAPE& shape = static_cast<LIB_SHAPE&>( *shapeIt );
2879 
2880         if( aGateNumber > 0 && shape.GetUnit() != aGateNumber )
2881             continue;
2882 
2883         if( shape.GetShape() != SHAPE_T::POLY )
2884             continue;
2885 
2886         SHAPE_LINE_CHAIN poly = shape.GetPolyShape().Outline( 0 );
2887 
2888         if( poly.GetPointCount() == 2 )
2889         {
2890             VECTOR2I pt0 = poly.CPoint( 0 );
2891             VECTOR2I pt1 = poly.CPoint( 1 );
2892 
2893             if( pt0 != pt1 && uniqueSegments.count( pt0 ) == 0 && uniqueSegments.count( pt1 ) == 0 )
2894             {
2895                 // we are only interested in vertical or horizontal segments
2896                 if( pt0.x == pt1.x || pt0.y == pt1.y )
2897                 {
2898                     uniqueSegments.insert( { pt0, poly } );
2899                     uniqueSegments.insert( { pt1, poly } );
2900                 }
2901             }
2902         }
2903     }
2904 
2905     LIB_PINS pins;
2906     aSymbolToFix->GetPins( pins, aGateNumber );
2907 
2908     for( auto& pin : pins )
2909     {
2910         auto setPinOrientation =
2911             [&]( double aAngleRad )
2912             {
2913                 int oDeg = (int) NormalizeAngle180( RAD2DEG( aAngleRad ) );
2914 
2915                 if( oDeg >= -45 && oDeg <= 45 )
2916                     pin->SetOrientation( 'R' ); // 0 degrees
2917                 else if( oDeg >= 45 && oDeg <= 135 )
2918                     pin->SetOrientation( 'U' ); // 90 degrees
2919                 else if( oDeg >= 135 || oDeg <= -135 )
2920                     pin->SetOrientation( 'L' ); // 180 degrees
2921                 else
2922                     pin->SetOrientation( 'D' ); // -90 degrees
2923             };
2924 
2925         if( uniqueSegments.count( pin->GetPosition() ) )
2926         {
2927             SHAPE_LINE_CHAIN& poly = uniqueSegments.at( pin->GetPosition() );
2928 
2929             VECTOR2I otherPt = poly.CPoint( 0 );
2930 
2931             if( otherPt == pin->GetPosition() )
2932                 otherPt = poly.CPoint( 1 );
2933 
2934             VECTOR2I vec( otherPt - pin->GetPosition() );
2935 
2936             pin->SetLength( vec.EuclideanNorm() );
2937             setPinOrientation( vec.Angle() );
2938         }
2939     }
2940 }
2941 
2942 
getFigureExtentsKiCad(const FIGURE & aCadstarFigure)2943 std::pair<wxPoint, wxSize> CADSTAR_SCH_ARCHIVE_LOADER::getFigureExtentsKiCad(
2944         const FIGURE& aCadstarFigure )
2945 {
2946     wxPoint upperLeft( Assignments.Settings.DesignLimit.x, 0 );
2947     wxPoint lowerRight( 0, Assignments.Settings.DesignLimit.y );
2948 
2949     for( const VERTEX& v : aCadstarFigure.Shape.Vertices )
2950     {
2951         if( upperLeft.x > v.End.x )
2952             upperLeft.x = v.End.x;
2953 
2954         if( upperLeft.y < v.End.y )
2955             upperLeft.y = v.End.y;
2956 
2957         if( lowerRight.x < v.End.x )
2958             lowerRight.x = v.End.x;
2959 
2960         if( lowerRight.y > v.End.y )
2961             lowerRight.y = v.End.y;
2962     }
2963 
2964     for( CUTOUT cutout : aCadstarFigure.Shape.Cutouts )
2965     {
2966         for( const VERTEX& v : aCadstarFigure.Shape.Vertices )
2967         {
2968             if( upperLeft.x > v.End.x )
2969                 upperLeft.x = v.End.x;
2970 
2971             if( upperLeft.y < v.End.y )
2972                 upperLeft.y = v.End.y;
2973 
2974             if( lowerRight.x < v.End.x )
2975                 lowerRight.x = v.End.x;
2976 
2977             if( lowerRight.y > v.End.y )
2978                 lowerRight.y = v.End.y;
2979         }
2980     }
2981 
2982     wxPoint upperLeftKiCad  = getKiCadPoint( upperLeft );
2983     wxPoint lowerRightKiCad = getKiCadPoint( lowerRight );
2984 
2985     wxPoint size = lowerRightKiCad - upperLeftKiCad;
2986 
2987     return { upperLeftKiCad, wxSize( abs( size.x ), abs( size.y ) ) };
2988 }
2989 
2990 
getKiCadPoint(const wxPoint & aCadstarPoint)2991 wxPoint CADSTAR_SCH_ARCHIVE_LOADER::getKiCadPoint( const wxPoint& aCadstarPoint )
2992 {
2993     wxPoint retval;
2994 
2995     retval.x = getKiCadLength( aCadstarPoint.x - m_designCenter.x );
2996     retval.y = -getKiCadLength( aCadstarPoint.y - m_designCenter.y );
2997 
2998     return retval;
2999 }
3000 
3001 
getKiCadLibraryPoint(const wxPoint & aCadstarPoint,const wxPoint & aCadstarCentre)3002 wxPoint CADSTAR_SCH_ARCHIVE_LOADER::getKiCadLibraryPoint( const wxPoint& aCadstarPoint,
3003                                                           const wxPoint& aCadstarCentre )
3004 {
3005     wxPoint retval;
3006 
3007     retval.x = getKiCadLength( aCadstarPoint.x - aCadstarCentre.x );
3008     retval.y = getKiCadLength( aCadstarPoint.y - aCadstarCentre.y );
3009 
3010     return retval;
3011 }
3012 
3013 
applyTransform(const wxPoint & aPoint,const wxPoint & aMoveVector,const double & aRotationAngleDeciDeg,const double & aScalingFactor,const wxPoint & aTransformCentre,const bool & aMirrorInvert)3014 wxPoint CADSTAR_SCH_ARCHIVE_LOADER::applyTransform( const wxPoint& aPoint,
3015         const wxPoint& aMoveVector, const double& aRotationAngleDeciDeg,
3016         const double& aScalingFactor, const wxPoint& aTransformCentre, const bool& aMirrorInvert )
3017 {
3018     wxPoint retVal = aPoint;
3019 
3020     if( aScalingFactor != 1.0 )
3021     {
3022         //scale point
3023         retVal -= aTransformCentre;
3024         retVal.x = KiROUND( retVal.x * aScalingFactor );
3025         retVal.y = KiROUND( retVal.y * aScalingFactor );
3026         retVal += aTransformCentre;
3027     }
3028 
3029     if( aMirrorInvert )
3030     {
3031         MIRROR( retVal.x, aTransformCentre.x );
3032     }
3033 
3034     if( aRotationAngleDeciDeg != 0.0 )
3035     {
3036         RotatePoint( &retVal, aTransformCentre, aRotationAngleDeciDeg );
3037     }
3038 
3039     if( aMoveVector != wxPoint{ 0, 0 } )
3040     {
3041         retVal += aMoveVector;
3042     }
3043 
3044     return retVal;
3045 }
3046 
3047 
getPolarAngle(const wxPoint & aPoint)3048 double CADSTAR_SCH_ARCHIVE_LOADER::getPolarAngle( const wxPoint& aPoint )
3049 {
3050     return NormalizeAnglePos( ArcTangente( aPoint.y, aPoint.x ) );
3051 }
3052 
3053 
getPolarRadius(const wxPoint & aPoint)3054 double CADSTAR_SCH_ARCHIVE_LOADER::getPolarRadius( const wxPoint& aPoint )
3055 {
3056     return sqrt(
3057             ( (double) aPoint.x * (double) aPoint.x ) + ( (double) aPoint.y * (double) aPoint.y ) );
3058 }
3059