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