KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_converter_symbols.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * Based on the dsn2kicad reference implementation and on OrCAD file format
7 * documentation from the OpenOrCadParser project (MIT licensed).
8 *
9 * This program is free software: you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23// Cache and KiCad library coordinates both use Y down. The file writer applies the axis flip.
24
25#include <algorithm>
26#include <array>
27#include <cctype>
28#include <cmath>
29#include <cstdlib>
30#include <limits>
31#include <map>
32#include <memory>
33#include <optional>
34#include <string>
35#include <tuple>
36#include <utility>
37#include <vector>
38
39#include <wx/string.h>
40#include <wx/translation.h>
41
42#include <base_units.h>
43#include <eda_text.h>
44#include <embedded_files.h>
45#include <font/font.h>
47#include <layer_ids.h>
48#include <lib_id.h>
49#include <lib_symbol.h>
50#include <math/util.h>
51#include <pin_type.h>
52#include <sch_io/ole_image.h>
53
54#include <sch_field.h>
55#include <sch_no_connect.h>
56#include <sch_pin.h>
57#include <sch_screen.h>
58#include <sch_shape.h>
59#include <sch_sheet_path.h>
60#include <sch_symbol.h>
61#include <sch_text.h>
62#include <schematic.h>
63#include <stroke_params.h>
64#include <string_utils.h>
65#include <symbol.h>
66#include <template_fieldnames.h>
67
70
71
72static std::string normalizedPath( std::string aPath )
73{
74 std::transform( aPath.begin(), aPath.end(), aPath.begin(),
75 []( unsigned char c )
76 {
77 return c == '\\' ? '/' : static_cast<char>( std::tolower( c ) );
78 } );
79 return aPath;
80}
81
82
83static LIB_ID libIdFor( const std::string& aLibName )
84{
85 return LIB_ID( wxString::FromUTF8( ORCAD_CONVERTER::LIB_NICK ),
86 LIB_ID::FixIllegalChars( FromOrcadString( aLibName ), false ).wx_str() );
87}
88
89
90static bool hasDualRowDescription( const std::map<std::string, std::string>& aProps )
91{
92 return std::any_of( aProps.begin(), aProps.end(),
93 []( const auto& aProperty )
94 {
95 return OrcadLower( aProperty.second ).find( "dual row" ) != std::string::npos;
96 } );
97}
98
99
100static bool isInstalledPropertyName( const std::string& aName )
101{
102 std::string normalized;
103
104 for( unsigned char character : aName )
105 {
106 if( std::isalnum( character ) )
107 normalized.push_back( static_cast<char>( std::tolower( character ) ) );
108 }
109
110 return normalized == "installedcomponent" || normalized == "installedcomponents"
111 || normalized == "installedproperty";
112}
113
114
116{
117 return ( aProp.dispMode >> 8 ) & 0xFF;
118}
119
120
122{
123 // Capture renders simulation results through mutually exclusive UI layers.
124 // Their display mode describes text within that layer, not persistent visibility.
125 if( OrcadIEquals( aProp.name, "BiasValue Power" ) || OrcadIEquals( aProp.name, "BiasValue Current" )
126 || OrcadIEquals( aProp.name, "BiasValue Voltage" ) )
127 {
128 return false;
129 }
130
131 int type = OrcadDisplayType( aProp );
132 return type >= 1 && type <= 4;
133}
134
135
137{
138 int type = OrcadDisplayType( aProp );
139 return type == 2 || type == 3;
140}
141
142
144{
145 int type = OrcadDisplayType( aProp );
146 return type == 1 || type == 2 || type == 4;
147}
148
149
151{
152 if( aProp.fontIdx > 0 )
153 return aProp.fontIdx;
154
155 if( aProp.name == "Part Reference" || aProp.name == "Reference" )
156 return 2;
157
158 if( aProp.name == "Value" )
159 return 9;
160
161 return 10;
162}
163
164
165wxString OrcadPinNameMarkup( const wxString& aName )
166{
167 wxString result;
168 wxString overbar;
169 bool marked = false;
170
171 auto flushOverbar = [&]()
172 {
173 if( !overbar.IsEmpty() )
174 {
175 result += wxS( "~{" ) + overbar + wxS( "}" );
176 overbar.clear();
177 }
178 };
179
180 for( wxUniChar character : aName )
181 {
182 if( character == '\\' )
183 {
184 marked = true;
185 continue;
186 }
187
188 if( marked && character != ' ' && character != '\t' && character != '\r' && character != '\n' )
189 {
190 overbar += character;
191 }
192 else
193 {
194 flushOverbar();
195 result += character;
196 }
197
198 marked = false;
199 }
200
201 flushOverbar();
202 return result;
203}
204
205
207{
208 switch( aStyle )
209 {
210 case 0: return LINE_STYLE::SOLID;
211 case 1: return LINE_STYLE::DASH;
212 case 2: return LINE_STYLE::DOT;
213 case 3: return LINE_STYLE::DASHDOT;
214 case 4: return LINE_STYLE::DASHDOTDOT;
215 case 5: return LINE_STYLE::DEFAULT;
216 default: return LINE_STYLE::DEFAULT;
217 }
218}
219
220
221std::pair<double, double> OrcadDashRatios( int aFormatVersionMajor )
222{
223 if( aFormatVersionMajor < 3 )
224 return { 67.0, 21.0 };
225
226 return { 3.0, 1.0 };
227}
228
229
230int OrcadLineWidthIu( int aWidth )
231{
232 switch( aWidth )
233 {
234 case 0: return schIUScale.MilsToIU( 10 );
235 case 1: return schIUScale.MilsToIU( 30 );
236 case 2: return schIUScale.MilsToIU( 50 );
237 case 3: return schIUScale.MilsToIU( 10 );
238 default: return 0;
239 }
240}
241
242
244{
245 return aWidth == 0 ? schIUScale.MilsToIU( 5 ) : OrcadLineWidthIu( aWidth );
246}
247
248
249FILL_T OrcadFillType( int aFillStyle, int aHatchStyle )
250{
251 if( aFillStyle == 0 )
253
254 if( aFillStyle == 2 )
255 {
256 switch( aHatchStyle )
257 {
258 case 3: return FILL_T::REVERSE_HATCH;
259 case 4:
260 case 5: return FILL_T::CROSS_HATCH;
261 default: return FILL_T::HATCH;
262 }
263 }
264
265 return FILL_T::NO_FILL;
266}
267
268
269int OrcadHatchPitchIu( uint32_t aModifyTimestamp )
270{
271 constexpr uint32_t currentHatchEpoch = 1577836800;
272 return schIUScale.MilsToIU( aModifyTimestamp >= currentHatchEpoch ? 80 : 25 );
273}
274
275
276int OrcadHatchLineWidthIu( uint32_t aModifyTimestamp )
277{
278 constexpr uint32_t currentHatchEpoch = 1577836800;
279 return schIUScale.MilsToIU( aModifyTimestamp >= currentHatchEpoch ? 10 : 3 );
280}
281
282
283std::vector<SEG> OrcadHatchLines( const EDA_SHAPE& aShape, int aHatchStyle, int aPitch )
284{
285 EDA_SHAPE shape( aShape );
287
288 SHAPE_POLY_SET polygon;
289 shape.TransformShapeToPolygon( polygon, 0, schIUScale.MilsToIU( 1 ), ERROR_INSIDE, true );
290
291 std::vector<SEG> lines;
292
293 auto append = [&]( const std::vector<double>& aSlopes, bool aVertical )
294 {
295 SHAPE_POLY_SET hatchArea = polygon.CloneDropTriangulation();
296
297 if( aVertical )
298 hatchArea.Rotate( ANGLE_90 );
299
300 std::vector<SEG> generated = hatchArea.GenerateHatchLines( aSlopes, aPitch, -1 );
301
302 if( aVertical )
303 {
304 for( SEG& line : generated )
305 {
306 RotatePoint( line.A, -ANGLE_90 );
307 RotatePoint( line.B, -ANGLE_90 );
308 }
309 }
310
311 lines.insert( lines.end(), generated.begin(), generated.end() );
312 };
313
314 switch( aHatchStyle )
315 {
316 case 0: append( { 0.0 }, false ); break;
317 case 1: append( { 0.0 }, true ); break;
318 case 2: append( { 1.0 }, false ); break;
319 case 3: append( { -1.0 }, false ); break;
320 case 4:
321 append( { 0.0 }, false );
322 append( { 0.0 }, true );
323 break;
324 case 5: append( { -1.0, 1.0 }, false ); break;
325 default: append( { -1.0 }, false ); break;
326 }
327
328 return lines;
329}
330
331
332int OrcadTextBaselineOffset( int aTextSize )
333{
334 return KiROUND( aTextSize * 8.0 / 21.0 );
335}
336
337
338namespace
339{
340
341int schMm( double aMm )
342{
343 return schIUScale.mmToIU( aMm );
344}
345
346
347int dbuIu( double aDbu )
348{
349 return KiROUND( aDbu * ORCAD_IU_PER_DBU );
350}
351
352
353STROKE_PARAMS strokeFor( const ORCAD_PRIMITIVE& aPrimitive, int aColor )
354{
355 return STROKE_PARAMS( OrcadLineWidthIu( aPrimitive.lineWidth ), OrcadLineStyle( aPrimitive.lineStyle ),
356 OrcadColor( aColor ) );
357}
358
359
360bool laterRectangleOccludesTextUnderscores( const std::vector<ORCAD_PRIMITIVE>& aPrimitives,
361 size_t aTextIndex )
362{
363 const ORCAD_PRIMITIVE& text = aPrimitives[aTextIndex];
364
365 if( text.kind != ORCAD_PRIM_KIND::TEXT || text.text.find( '_' ) == std::string::npos )
366 return false;
367
368 int textLeft = std::min( text.x1, text.x2 );
369 int textRight = std::max( text.x1, text.x2 );
370 int textTop = std::min( text.y1, text.y2 );
371 int textBottom = std::max( text.y1, text.y2 );
372
373 for( size_t i = aTextIndex + 1; i < aPrimitives.size(); ++i )
374 {
375 const ORCAD_PRIMITIVE& rectangle = aPrimitives[i];
376
377 if( rectangle.kind != ORCAD_PRIM_KIND::RECTANGLE )
378 continue;
379
380 int rectangleLeft = std::min( rectangle.x1, rectangle.x2 );
381 int rectangleRight = std::max( rectangle.x1, rectangle.x2 );
382 int rectangleTop = std::min( rectangle.y1, rectangle.y2 );
383 int rectangleBottom = std::max( rectangle.y1, rectangle.y2 );
384 int halfStroke = KiROUND( static_cast<double>( OrcadLineWidthIu( rectangle.lineWidth ) )
385 / ORCAD_IU_PER_DBU / 2.0 );
386
387 if( textLeft >= rectangleLeft && textRight <= rectangleRight && textTop >= rectangleTop
388 && textBottom <= rectangleBottom && rectangleBottom - textBottom <= halfStroke )
389 {
390 return true;
391 }
392 }
393
394 return false;
395}
396
397
398ELECTRICAL_PINTYPE pinTypeFor( ORCAD_PORT_TYPE aType )
399{
400 switch( aType )
401 {
410 }
411
413}
414
415
416// OrCAD bookkeeping props kept out of user fields; useful ones map to dedicated
417// KiCad fields, rest describe library linkage import re-establishes itself.
418bool isBookkeepingProp( const std::string& aName )
419{
420 static const char* const skipped[] = {
421 "Part Reference", "Reference", "Name", "Graphic", "Implementation",
422 "Implementation Type", "Source Library", "Source Package", "Source Part", "Power Pins Visible",
423 };
424
425 for( const char* name : skipped )
426 {
427 if( OrcadIEquals( aName, name ) )
428 return true;
429 }
430
431 return false;
432}
433
434
436wxString propertyFieldName( const std::string& aProperty )
437{
438 if( OrcadIEquals( aProperty, "Part Reference" ) || OrcadIEquals( aProperty, "Reference" )
439 || isBookkeepingProp( aProperty ) )
440 {
441 return wxEmptyString;
442 }
443
444 if( OrcadIEquals( aProperty, "Value" ) )
446
447 if( OrcadIEquals( aProperty, "Description" ) )
449
450 if( OrcadIEquals( aProperty, "Datasheet" ) )
452
453 if( OrcadIEquals( aProperty, "PCB Footprint" ) )
454 return wxS( "OrCAD Footprint" );
455
456 if( OrcadIEquals( aProperty, "Footprint" ) )
457 return wxS( "OrCAD Footprint Property" );
458
459 return FromOrcadString( aProperty );
460}
461
462
463size_t symbolPinIndex( const ORCAD_SYMBOL_DEF& aSymbol, const ORCAD_PIN_INST& aPin, size_t aFallback )
464{
465 if( aPin.pinIndex == 0 )
466 return aFallback;
467
468 int position = std::abs( static_cast<int>( aPin.pinIndex ) ) - 1;
469
470 for( size_t i = 0; i < aSymbol.pins.size(); ++i )
471 {
472 if( aSymbol.pins[i].position == position )
473 return i;
474 }
475
476 return static_cast<size_t>( position );
477}
478
479
480// Sequential matching pairs pins by order; otherwise by their recorded symbol pin index
481std::optional<int64_t> pinDisplacement( const ORCAD_SYMBOL_DEF& aSymbol, const ORCAD_PLACED_INSTANCE& aInstance,
482 bool aSequential )
483{
484 if( aSequential ? aInstance.pins.size() != aSymbol.pins.size() : aInstance.pins.size() > aSymbol.pins.size() )
485 return std::nullopt;
486
487 ORCAD_BBOX box = aSymbol.bbox.value_or( ORCAD_BBOX() );
488 int orient = OrcadOrientOf( aInstance.rotation, aInstance.mirror );
489 int64_t displacement = 0;
490
491 for( size_t i = 0; i < aInstance.pins.size(); ++i )
492 {
493 size_t symbolPin = aSequential ? i : symbolPinIndex( aSymbol, aInstance.pins[i], i );
494
495 if( symbolPin >= aSymbol.pins.size() )
496 return std::nullopt;
497
498 VECTOR2I point = OrcadTransformPoint( orient, box.x2 - box.x1, box.y2 - box.y1, aInstance.x, aInstance.y,
499 aSymbol.pins[symbolPin].hotptX, aSymbol.pins[symbolPin].hotptY );
500 displacement += std::abs( static_cast<int64_t>( point.x ) - aInstance.pins[i].x );
501 displacement += std::abs( static_cast<int64_t>( point.y ) - aInstance.pins[i].y );
502 }
503
504 return displacement;
505}
506
507
508bool symbolPinsMatch( const ORCAD_SYMBOL_DEF& aSymbol, const ORCAD_PLACED_INSTANCE& aInstance,
509 bool aSequential = false )
510{
511 return pinDisplacement( aSymbol, aInstance, aSequential ) == 0;
512}
513
514
515bool symbolGeometryMatches( const ORCAD_SYMBOL_DEF& aLeft, const ORCAD_SYMBOL_DEF& aRight )
516{
517 if( aLeft.bbox.has_value() != aRight.bbox.has_value() || aLeft.pins.size() != aRight.pins.size() )
518 return false;
519
520 if( aLeft.bbox
521 && ( aLeft.bbox->x1 != aRight.bbox->x1 || aLeft.bbox->y1 != aRight.bbox->y1 || aLeft.bbox->x2 != aRight.bbox->x2
522 || aLeft.bbox->y2 != aRight.bbox->y2 ) )
523 {
524 return false;
525 }
526
527 for( size_t i = 0; i < aLeft.pins.size(); ++i )
528 {
529 const ORCAD_SYMBOL_PIN& left = aLeft.pins[i];
530 const ORCAD_SYMBOL_PIN& right = aRight.pins[i];
531
532 if( left.startX != right.startX || left.startY != right.startY || left.hotptX != right.hotptX
533 || left.hotptY != right.hotptY )
534 {
535 return false;
536 }
537 }
538
539 return true;
540}
541
542
543ORCAD_SYMBOL_DEF symbolVariantForPlacedPins( const ORCAD_SYMBOL_DEF& aSymbol, const ORCAD_PLACED_INSTANCE& aInstance )
544{
545 ORCAD_SYMBOL_DEF fitted = aSymbol;
546 fitted.variants.clear();
547 fitted.synthesized = true;
548
549 if( aInstance.pins.size() > fitted.pins.size() )
550 return fitted;
551
552 ORCAD_BBOX box = fitted.bbox.value_or( ORCAD_BBOX() );
553 int orient = OrcadOrientOf( aInstance.rotation, aInstance.mirror );
554 VECTOR2I offset = OrcadOrientOffset( orient, box.x2 - box.x1, box.y2 - box.y1 );
555 std::map<std::pair<int, int>, size_t> targetCounts;
556 std::map<std::pair<int, int>, std::set<size_t>> targetPins;
557
558 auto targetOf = [&]( const ORCAD_PIN_INST& aPin )
559 {
560 ORCAD_POINT local =
561 OrcadInverseOrient( orient, aPin.x - aInstance.x - offset.x, aPin.y - aInstance.y - offset.y );
562 return std::pair{ local.x, local.y };
563 };
564
565 for( size_t i = 0; i < aInstance.pins.size(); ++i )
566 {
567 size_t symbolPin = symbolPinIndex( fitted, aInstance.pins[i], i );
568
569 if( symbolPin >= fitted.pins.size() )
570 continue;
571
572 auto target = targetOf( aInstance.pins[i] );
573 ++targetCounts[target];
574 targetPins[target].insert( symbolPin );
575 }
576
577 std::set<std::pair<int, int>> handledStackedTargets;
578
579 for( size_t i = 0; i < aInstance.pins.size(); ++i )
580 {
581 size_t symbolPin = symbolPinIndex( fitted, aInstance.pins[i], i );
582
583 if( symbolPin >= fitted.pins.size() )
584 continue;
585
586 auto target = targetOf( aInstance.pins[i] );
587 int hotptX = target.first;
588 int hotptY = target.second;
589 const std::set<size_t>& stackedPins = targetPins.at( target );
590 bool stackedTarget = targetCounts.at( target ) > 1;
591
592 auto occupant = std::find_if( fitted.pins.begin(), fitted.pins.end(),
593 [&]( const ORCAD_SYMBOL_PIN& aPin )
594 {
595 return aPin.hotptX == hotptX && aPin.hotptY == hotptY;
596 } );
597
598 if( stackedTarget && !handledStackedTargets.count( target ) )
599 {
600 occupant = std::find_if( fitted.pins.begin(), fitted.pins.end(),
601 [&]( const ORCAD_SYMBOL_PIN& aPin )
602 {
603 size_t index = static_cast<size_t>( &aPin - fitted.pins.data() );
604 return aPin.hotptX == hotptX && aPin.hotptY == hotptY
605 && !stackedPins.count( index );
606 } );
607 handledStackedTargets.insert( target );
608 }
609 else if( stackedTarget )
610 {
611 occupant = fitted.pins.end();
612 }
613
614 if( occupant != fitted.pins.end() && occupant != fitted.pins.begin() + symbolPin )
615 {
616 std::swap( occupant->hotptX, fitted.pins[symbolPin].hotptX );
617 std::swap( occupant->hotptY, fitted.pins[symbolPin].hotptY );
618 std::swap( occupant->startX, fitted.pins[symbolPin].startX );
619 std::swap( occupant->startY, fitted.pins[symbolPin].startY );
620 continue;
621 }
622
623 int deltaX = hotptX - fitted.pins[symbolPin].hotptX;
624 int deltaY = hotptY - fitted.pins[symbolPin].hotptY;
625
626 fitted.pins[symbolPin].hotptX = hotptX;
627 fitted.pins[symbolPin].hotptY = hotptY;
628 fitted.pins[symbolPin].startX += deltaX;
629 fitted.pins[symbolPin].startY += deltaY;
630 }
631
632 return fitted;
633}
634
635
637std::pair<std::string, std::string> splitCacheName( const std::string& aName, const std::string& aBase )
638{
639 std::string tail = aName.compare( 0, aBase.size(), aBase ) == 0 ? aName.substr( aBase.size() ) : aName;
640 size_t dot = tail.find( '.' );
641
642 return { tail.substr( 0, dot ), dot == std::string::npos ? std::string() : tail.substr( dot + 1 ) };
643}
644
645
647std::string packageUnitLetter( const ORCAD_PACKAGE& aPackage, size_t aIndex )
648{
649 if( aIndex < aPackage.devices.size() && !aPackage.devices[aIndex].unitRef.empty() )
650 return aPackage.devices[aIndex].unitRef;
651
652 if( aPackage.devices.size() < 2 )
653 return {};
654
655 std::string index = std::to_string( aIndex );
656 return "#" + std::string( 10 - std::min<size_t>( 10, index.size() ), '0' ) + index;
657}
658
659
660std::vector<bool> nonblankPinNumbers( const std::vector<std::string>& aNumbers )
661{
662 std::vector<bool> visible;
663 visible.reserve( aNumbers.size() );
664
665 for( const std::string& number : aNumbers )
666 visible.push_back( !number.empty() );
667
668 return visible;
669}
670
671
672bool unitLetterLess( const std::string& aLeft, const std::string& aRight )
673{
674 return StrNumCmp( wxString::FromUTF8( aLeft ), wxString::FromUTF8( aRight ) ) < 0;
675}
676
677} // namespace
678
679
681{
682 // Page symbols absent from design cache get synthesized placeholder so T0x10
683 // connection points stay electrically intact.
684 std::vector<std::string> missingOrder;
685 std::map<std::string, std::vector<const ORCAD_PLACED_INSTANCE*>> missing;
686
687 // Scan root and child-folder pages; child-folder parts live in childFolderPages,
688 // not root page list.
689 std::vector<const ORCAD_RAW_PAGE*> allPages;
690
692 [&]( const ORCAD_RAW_PAGE& aPage )
693 {
694 allPages.push_back( &aPage );
695 },
696 true );
697
698 for( const ORCAD_RAW_PAGE* page : allPages )
699 {
700 for( const ORCAD_PLACED_INSTANCE& inst : page->instances )
701 {
702 if( inst.pkgName.empty() || m_design.symbols.count( inst.pkgName ) )
703 continue;
704
705 auto [it, isNew] = missing.try_emplace( inst.pkgName );
706
707 if( isNew )
708 missingOrder.push_back( inst.pkgName );
709
710 it->second.push_back( &inst );
711 }
712 }
713
714 for( const std::string& name : missingOrder )
715 {
716 const std::vector<const ORCAD_PLACED_INSTANCE*>& insts = missing[name];
717
718 note( wxString::Format( _( "Symbol '%s' is absent from the design cache; synthesized "
719 "placeholder from %d instance(s)." ),
720 FromOrcadString( name ), (int) insts.size() ) );
721
722 m_design.symbols[name] = synthesizeSymbol( name, insts );
723 }
724
725 for( const ORCAD_RAW_PAGE* page : allPages )
726 {
727 for( const ORCAD_PLACED_INSTANCE& inst : page->instances )
728 {
729 auto symbol = m_design.symbols.find( inst.pkgName );
730
731 if( symbol == m_design.symbols.end() || inst.pins.empty() )
732 continue;
733
734 std::vector<const ORCAD_SYMBOL_DEF*> candidates = { &symbol->second };
735
736 for( const ORCAD_SYMBOL_DEF& variant : symbol->second.variants )
737 {
738 if( !variant.synthesized )
739 candidates.push_back( &variant );
740 }
741
742 for( const ORCAD_SYMBOL_DEF& variant : symbol->second.variants )
743 {
744 if( variant.synthesized )
745 candidates.push_back( &variant );
746 }
747
748 const ORCAD_SYMBOL_DEF* matching = nullptr;
749
750 for( const ORCAD_SYMBOL_DEF* candidate : candidates )
751 {
752 if( symbolPinsMatch( *candidate, inst ) )
753 {
754 matching = candidate;
755 break;
756 }
757 }
758
759 if( !matching )
760 {
761 for( const ORCAD_SYMBOL_DEF* candidate : candidates )
762 {
763 if( !symbolPinsMatch( *candidate, inst, true ) )
764 continue;
765
766 ORCAD_SYMBOL_DEF fitted = *candidate;
767 fitted.variants.clear();
768 fitted.synthesized = true;
769
770 for( size_t i = 0; i < inst.pins.size(); ++i )
771 {
772 if( inst.pins[i].pinIndex )
773 fitted.pins[i].position = std::abs( static_cast<int>( inst.pins[i].pinIndex ) ) - 1;
774 }
775
776 if( symbolPinsMatch( fitted, inst ) )
777 {
778 symbol->second.variants.insert( symbol->second.variants.begin(), std::move( fitted ) );
779 matching = &symbol->second.variants.front();
780 }
781
782 break;
783 }
784 }
785
786 if( matching )
787 {
788 auto sourceProperty = inst.props.find( "Source Library" );
789 std::string sourceLibrary = sourceProperty != inst.props.end() ? sourceProperty->second
790 : inst.sourceLibrary;
791
792 bool sourceMatchedGraphics =
793 !sourceLibrary.empty() && !matching->sourceLib.empty()
794 && normalizedPath( sourceLibrary ) == normalizedPath( matching->sourceLib );
795
796 if( matching != &symbol->second && !sourceMatchedGraphics
797 && std::none_of( symbol->second.variants.begin(), symbol->second.variants.end(),
798 [&]( const ORCAD_SYMBOL_DEF& aVariant )
799 {
800 return aVariant.synthesized && symbolGeometryMatches( aVariant, *matching );
801 } ) )
802 {
803 ORCAD_SYMBOL_DEF fitted = *matching;
804 fitted.primitives = symbol->second.primitives;
805 fitted.color = symbol->second.color;
806 fitted.props = symbol->second.props;
807 fitted.generalFlags = symbol->second.generalFlags;
808 fitted.variants.clear();
809 fitted.synthesized = true;
810
811 if( symbolPinsMatch( fitted, inst ) )
812 symbol->second.variants.insert( symbol->second.variants.begin(), std::move( fitted ) );
813 }
814
815 continue;
816 }
817
818 const ORCAD_SYMBOL_DEF* best = nullptr;
819 int64_t bestDisplacement = std::numeric_limits<int64_t>::max();
820
821 for( const ORCAD_SYMBOL_DEF* candidate : candidates )
822 {
823 std::optional<int64_t> displacement = pinDisplacement( *candidate, inst, false );
824
825 if( displacement && *displacement < bestDisplacement )
826 {
827 best = candidate;
828 bestDisplacement = *displacement;
829 }
830 }
831
832 if( best )
833 {
834 ORCAD_SYMBOL_DEF fitted = symbolVariantForPlacedPins( *best, inst );
835
836 if( symbolPinsMatch( fitted, inst ) )
837 symbol->second.variants.push_back( std::move( fitted ) );
838 }
839 }
840 }
841
842 for( const ORCAD_RAW_PAGE* page : allPages )
843 {
844 for( const ORCAD_PLACED_INSTANCE& inst : page->instances )
845 libForInstance( inst );
846 }
847
849
850 for( const auto& [name, entry] : m_libSymbols )
851 m_preparedLibUnits.emplace( name, entry.units );
852
854}
855
856
858{
859 LIB_ENTRY entry;
860 entry.name = SymbolId( aPackage.name );
861 entry.refPrefix = aPackage.refDes.empty() ? "U" : aPackage.refDes;
862 entry.footprint = aPackage.pcbFootprint;
863
864 // Heterogeneous units are cached as "<package><unit>.<view>", homogeneous ones share "<package>.<view>"
865 auto view = [&]( const std::string& aUnit, const char* aView ) -> const ORCAD_SYMBOL_DEF*
866 {
867 for( const std::string& name : { aPackage.name + aUnit + "." + aView, aPackage.name + "." + aView } )
868 {
869 auto it = m_design.symbols.find( name );
870
871 if( it != m_design.symbols.end() && it->second.typeId == ORCAD_ST_LIBRARY_PART )
872 return &it->second;
873 }
874
875 return nullptr;
876 };
877
878 for( size_t i = 0; i < aPackage.devices.size(); ++i )
879 {
880 const ORCAD_DEVICE& device = aPackage.devices[i];
881 const ORCAD_SYMBOL_DEF* normal = view( device.unitRef, "Normal" );
882 const ORCAD_SYMBOL_DEF* convert = view( device.unitRef, "Convert" );
883 std::string letter = packageUnitLetter( aPackage, i );
884
885 if( !normal )
886 std::swap( normal, convert );
887
888 if( !normal || std::any_of( entry.units.begin(), entry.units.end(),
889 [&]( const UNIT_INFO& aUnit )
890 {
891 return aUnit.letter == letter;
892 } ) )
893 {
894 continue;
895 }
896
897 UNIT_INFO unit;
898 unit.letter = letter;
899 unit.symbol = normal;
900 unit.convert = convert;
901 unit.pinNumbers = device.pinNumbers;
902 unit.pinNumberVisible = nonblankPinNumbers( device.pinNumbers );
903 unit.pinIgnore = device.pinIgnore;
904 entry.units.push_back( std::move( unit ) );
905 }
906
907 std::stable_sort( entry.units.begin(), entry.units.end(),
908 []( const UNIT_INFO& a, const UNIT_INFO& b )
909 {
910 return unitLetterLess( a.letter, b.letter );
911 } );
912
913 return entry;
914}
915
916
917std::vector<LIB_SYMBOL*> ORCAD_CONVERTER::BuildSymbolLibrary()
918{
919 std::set<const ORCAD_SYMBOL_DEF*> packaged;
920
921 for( const auto& [name, package] : m_design.packages )
922 {
923 LIB_ENTRY entry = buildPackageEntry( package );
924
925 if( entry.units.empty() || m_libSymbols.count( entry.name ) )
926 continue;
927
928 for( const UNIT_INFO& unit : entry.units )
929 packaged.insert( { unit.symbol, unit.convert } );
930
931 m_libSymbols.emplace( entry.name, std::move( entry ) );
932 }
933
934 // Power symbols and parts without a package become single-unit items
935 for( const auto& [cacheName, def] : m_design.symbols )
936 {
937 if( packaged.count( &def )
938 || ( def.typeId != ORCAD_ST_LIBRARY_PART && def.typeId != ORCAD_ST_GLOBAL_SYMBOL ) )
939 {
940 continue;
941 }
942
943 size_t dot = cacheName.rfind( '.' );
944 std::string view = dot == std::string::npos ? std::string() : cacheName.substr( dot + 1 );
945 std::string base = view == "Normal" || view == "Convert" ? cacheName.substr( 0, dot ) : cacheName;
946 auto normal = m_design.symbols.find( base + ".Normal" );
947 auto convert = m_design.symbols.find( base + ".Convert" );
948
949 if( view == "Convert" && normal != m_design.symbols.end() && !packaged.count( &normal->second ) )
950 continue;
951
952 std::string libname = SymbolId( base );
953
954 if( m_libSymbols.count( libname ) )
955 {
956 warn( wxString::Format( _( "The symbol '%s' duplicates the name of another library item and was "
957 "left out of the library." ),
958 FromOrcadString( cacheName ) ) );
959 continue;
960 }
961
962 std::string bare = base.substr( 0, base.find( '.' ) );
963 const ORCAD_PACKAGE* pkg = nullptr;
964
965 for( const std::string& key : { base, bare } )
966 {
967 auto pkgIt = m_design.packages.find( key );
968
969 if( pkgIt != m_design.packages.end() )
970 {
971 pkg = &pkgIt->second;
972 break;
973 }
974 }
975
976 LIB_ENTRY& ls = m_libSymbols[libname];
977 ls.name = libname;
978 ls.isPower = def.typeId == ORCAD_ST_GLOBAL_SYMBOL;
979 ls.refPrefix = ( pkg && !pkg->refDes.empty() ) ? pkg->refDes : ( ls.isPower ? "#PWR" : "U" );
980 ls.footprint = pkg ? pkg->pcbFootprint : "";
981
982 if( ls.isPower )
983 ls.powerNet = base; // power value = net name (symbol base)
984
985 UNIT_INFO unit;
986 unit.letter = "A";
987 unit.symbol = &def;
988
989 if( view == "Normal" && convert != m_design.symbols.end() && convert->second.typeId == def.typeId )
990 unit.convert = &convert->second;
991
992 if( pkg && !pkg->devices.empty() )
993 {
994 unit.pinNumbers = pkg->devices.front().pinNumbers;
995 unit.pinNumberVisible = nonblankPinNumbers( unit.pinNumbers );
996 unit.pinIgnore = pkg->devices.front().pinIgnore;
997 }
998
999 ls.units.push_back( std::move( unit ) );
1000 }
1001
1003
1004 std::vector<LIB_SYMBOL*> out;
1005
1006 for( auto& [libname, entry] : m_libSymbols )
1007 {
1008 // Single malformed cache symbol must not sink whole library, but dropping one without
1009 // saying so leaves a library that looks complete and is not.
1010 try
1011 {
1012 if( LIB_SYMBOL* symbol = kicadSymbolFor( libname ) )
1013 out.push_back( static_cast<LIB_SYMBOL*>( symbol->Duplicate() ) );
1014 }
1015 catch( const std::exception& e )
1016 {
1017 warn( wxString::Format( _( "The symbol '%s' could not be converted and was left out of the "
1018 "library (%s)." ),
1019 FromOrcadString( libname ), wxString::FromUTF8( e.what() ) ) );
1020 }
1021 }
1022
1023 return out;
1024}
1025
1026
1028ORCAD_CONVERTER::synthesizeSymbol( const std::string& aPkgName,
1029 const std::vector<const ORCAD_PLACED_INSTANCE*>& aInstances ) const
1030{
1031 // Prefer reference-orientation instance: t-term in T = I + t(w,h) + M*p cancels
1032 // there, making reconstructed connection points exact.
1033 const ORCAD_PLACED_INSTANCE* ref = aInstances.front();
1034
1035 for( const ORCAD_PLACED_INSTANCE* inst : aInstances )
1036 {
1037 if( OrcadOrientOf( inst->rotation, inst->mirror ) == 0 )
1038 {
1039 ref = inst;
1040 break;
1041 }
1042 }
1043
1044 int ori = OrcadOrientOf( ref->rotation, ref->mirror );
1045
1046 auto inv = [&]( int aX, int aY )
1047 {
1048 return OrcadInverseOrient( ori, aX, aY );
1049 };
1050
1051 // Instance-local pin positions from T0x10 records.
1052 std::vector<ORCAD_POINT> r;
1053
1054 for( const ORCAD_PIN_INST& t : ref->pins )
1055 r.push_back( inv( t.x - ref->x, t.y - ref->y ) );
1056
1057 std::optional<int> inferredWidth;
1058 std::optional<int> inferredHeight;
1059
1060 if( r.size() == 1 && ori == 0 )
1061 {
1062 std::map<int, int> widthCounts;
1063 std::map<int, int> heightCounts;
1064
1065 for( const ORCAD_PLACED_INSTANCE* inst : aInstances )
1066 {
1067 if( inst->pins.size() != 1 )
1068 continue;
1069
1070 int instOrient = OrcadOrientOf( inst->rotation, inst->mirror );
1071 const ORCAD_ORIENT_ENTRY& transform = ORCAD_ORIENT_TABLE[instOrient];
1072 int localX = transform.a * r.front().x + transform.b * r.front().y;
1073 int localY = transform.c * r.front().x + transform.d * r.front().y;
1074 int offsetX = inst->pins.front().x - inst->x - localX;
1075 int offsetY = inst->pins.front().y - inst->y - localY;
1076
1077 auto tally = [&]( int aSelector, int aOffset )
1078 {
1079 if( aOffset <= 0 )
1080 return;
1081
1082 if( aSelector == 1 )
1083 ++widthCounts[aOffset];
1084 else if( aSelector == 2 )
1085 ++heightCounts[aOffset];
1086 };
1087
1088 tally( transform.txSel, offsetX );
1089 tally( transform.tySel, offsetY );
1090 }
1091
1092 auto mostFrequent = []( const std::map<int, int>& aCounts ) -> std::optional<int>
1093 {
1094 auto best = std::max_element( aCounts.begin(), aCounts.end(),
1095 []( const auto& aLeft, const auto& aRight )
1096 {
1097 return aLeft.second < aRight.second;
1098 } );
1099 return best == aCounts.end() ? std::nullopt : std::optional<int>( best->first );
1100 };
1101
1102 inferredWidth = mostFrequent( widthCounts );
1103 inferredHeight = mostFrequent( heightCounts );
1104 }
1105
1106 std::vector<char> sides; // 'L', 'R', 'T', 'B' or 0 per pin
1107 int bx1 = 0;
1108 int by1 = 0;
1109 int bx2 = 0;
1110 int by2 = 0;
1111
1112 if( !r.empty() )
1113 {
1114 int x0 = r.front().x;
1115 int x1 = r.front().x;
1116 int y0 = r.front().y;
1117 int y1 = r.front().y;
1118
1119 for( const ORCAD_POINT& p : r )
1120 {
1121 x0 = std::min( x0, p.x );
1122 x1 = std::max( x1, p.x );
1123 y0 = std::min( y0, p.y );
1124 y1 = std::max( y1, p.y );
1125 }
1126
1127 bool horizFirst = ( x1 - x0 ) >= ( y1 - y0 );
1128
1129 for( const ORCAD_POINT& p : r )
1130 {
1131 std::vector<char> cand;
1132
1133 if( p.x == x0 )
1134 cand.push_back( 'L' );
1135
1136 if( p.x == x1 )
1137 cand.push_back( 'R' );
1138
1139 if( p.y == y0 )
1140 cand.push_back( 'T' );
1141
1142 if( p.y == y1 )
1143 cand.push_back( 'B' );
1144
1145 if( cand.empty() )
1146 {
1147 sides.push_back( 0 );
1148 continue;
1149 }
1150
1151 // Prefer L/R sides when pin field wider than tall, T/B otherwise; ties
1152 // keep L,R,T,B order.
1153 char pick = cand.front();
1154
1155 for( char c : cand )
1156 {
1157 bool isTB = c == 'T' || c == 'B';
1158
1159 if( isTB != horizFirst )
1160 {
1161 pick = c;
1162 break;
1163 }
1164 }
1165
1166 sides.push_back( pick );
1167 }
1168
1169 if( r.size() == 1 && inferredWidth && inferredHeight )
1170 {
1171 bx1 = 0;
1172 by1 = 0;
1173 bx2 = *inferredWidth;
1174 by2 = *inferredHeight;
1175 }
1176 else
1177 {
1178 auto hasSide = [&]( char aSide )
1179 {
1180 return std::find( sides.begin(), sides.end(), aSide ) != sides.end();
1181 };
1182
1183 bx1 = hasSide( 'L' ) ? x0 + PIN_LEN_DBU : x0;
1184 bx2 = hasSide( 'R' ) ? x1 - PIN_LEN_DBU : x1;
1185 by1 = hasSide( 'T' ) ? y0 + PIN_LEN_DBU : y0;
1186 by2 = hasSide( 'B' ) ? y1 - PIN_LEN_DBU : y1;
1187
1188 if( bx2 <= bx1 )
1189 {
1190 int m = (int) std::floor( ( bx1 + bx2 ) / 2.0 );
1191 bx1 = m - PIN_LEN_DBU;
1192 bx2 = m + PIN_LEN_DBU;
1193 }
1194
1195 if( by2 <= by1 )
1196 {
1197 int m = (int) std::floor( ( by1 + by2 ) / 2.0 );
1198 by1 = m - PIN_LEN_DBU;
1199 by2 = m + PIN_LEN_DBU;
1200 }
1201 }
1202 }
1203 else
1204 {
1205 // No pin records; derive body from inverse-transformed placed box.
1206 ORCAD_POINT c1 = inv( ref->bbox.x1 - ref->x, ref->bbox.y1 - ref->y );
1207 ORCAD_POINT c2 = inv( ref->bbox.x2 - ref->x, ref->bbox.y2 - ref->y );
1208
1209 bx1 = std::min( c1.x, c2.x );
1210 bx2 = std::max( c1.x, c2.x );
1211 by1 = std::min( c1.y, c2.y );
1212 by2 = std::max( c1.y, c2.y );
1213 }
1214
1215 // Remove orientation re-anchoring offset so definition anchors like real cache
1216 // symbol.
1217 int w = bx2 - bx1;
1218 int h = by2 - by1;
1219 VECTOR2I t = OrcadOrientOffset( ori, w, h );
1220 ORCAD_POINT s = inv( t.x, t.y );
1221
1222 ORCAD_SYMBOL_DEF sym;
1224 sym.name = aPkgName;
1225 sym.synthesized = true;
1226 sym.bbox = ORCAD_BBOX{ bx1 - s.x, by1 - s.y, bx2 - s.x, by2 - s.y };
1227
1228 ORCAD_PRIMITIVE rect;
1230 rect.x1 = bx1 - s.x;
1231 rect.y1 = by1 - s.y;
1232 rect.x2 = bx2 - s.x;
1233 rect.y2 = by2 - s.y;
1234 sym.primitives.push_back( rect );
1235
1236 bool dualRowConnector = hasDualRowDescription( ref->props );
1237 auto package = m_design.packages.find( ref->sourcePackage );
1238
1239 if( package != m_design.packages.end() )
1240 dualRowConnector = dualRowConnector || hasDualRowDescription( package->second.props );
1241
1242 for( size_t i = 0; i < r.size(); ++i )
1243 {
1244 int px = r[i].x - s.x;
1245 int py = r[i].y - s.y;
1246 int ddx = 0;
1247 int ddy = 0;
1248
1249 switch( sides[i] )
1250 {
1251 case 'L': ddx = PIN_LEN_DBU; break;
1252 case 'R': ddx = -PIN_LEN_DBU; break;
1253 case 'T': ddy = PIN_LEN_DBU; break;
1254 case 'B': ddy = -PIN_LEN_DBU; break;
1255 default: break;
1256 }
1257
1259
1260 if( dualRowConnector )
1261 {
1262 int pinIndex = std::abs( static_cast<int>( ref->pins[i].pinIndex ) );
1263 pin.position = pinIndex > 0 ? pinIndex - 1 : static_cast<int>( i );
1264 pin.name = std::to_string( pinIndex > 0 ? pinIndex : i + 1 );
1265 }
1266
1267 pin.startX = px + ddx;
1268 pin.startY = py + ddy;
1269 pin.hotptX = px;
1270 pin.hotptY = py;
1271 pin.portType = ORCAD_PORT_TYPE::PASSIVE;
1272 pin.shapeBits = 0;
1273 sym.pins.push_back( pin );
1274 }
1275
1276 return sym;
1277}
1278
1279
1281{
1282 // Cache names look like "<base><unit>.<view>", e.g. "74LS00A.Normal" (unit A of
1283 // 74LS00) or "C.Normal" (single-unit capacitor).
1284 const std::string& v = aInst.pkgName;
1285 auto sourceProperty = aInst.props.find( "Source Package" );
1286 std::string base = sourceProperty != aInst.props.end() && !sourceProperty->second.empty() ? sourceProperty->second
1287 : !aInst.sourcePackage.empty() ? aInst.sourcePackage
1288 : v.substr( 0, v.find( '.' ) );
1289
1290 auto [unit, view] = splitCacheName( v, base );
1291
1292 if( !view.empty() )
1293 {
1294 std::string lower = OrcadLower( view );
1295
1296 // Non-Normal views are DeMorgan alternates with own body graphics, so view
1297 // name stays part of unit discriminator.
1298 if( lower != "normal" )
1299 unit += ":" + view;
1300 }
1301
1302 return unit;
1303}
1304
1305
1306std::pair<const ORCAD_SYMBOL_DEF*, int> ORCAD_CONVERTER::pickVariant( const ORCAD_PLACED_INSTANCE& aInst ) const
1307{
1308 auto it = m_design.symbols.find( aInst.pkgName );
1309
1310 if( it == m_design.symbols.end() )
1311 return { nullptr, 0 };
1312
1313 const ORCAD_SYMBOL_DEF* prime = &it->second;
1314
1315 std::vector<const ORCAD_SYMBOL_DEF*> variants;
1316 variants.push_back( prime );
1317
1318 for( const ORCAD_SYMBOL_DEF& v : prime->variants )
1319 variants.push_back( &v );
1320
1321 if( aInst.pins.empty() )
1322 return { prime, 0 };
1323
1324 auto sourceProperty = aInst.props.find( "Source Library" );
1325 std::string sourceLibrary = sourceProperty != aInst.props.end() ? sourceProperty->second : aInst.sourceLibrary;
1326
1327 if( !sourceLibrary.empty() )
1328 {
1329 std::string sourceKey = normalizedPath( sourceLibrary );
1330
1331 for( size_t vi = 0; vi < variants.size(); ++vi )
1332 {
1333 if( normalizedPath( variants[vi]->sourceLib ) == sourceKey && symbolPinsMatch( *variants[vi], aInst ) )
1334 return { variants[vi], static_cast<int>( vi ) };
1335 }
1336 }
1337
1338 for( size_t vi = 0; vi < variants.size(); ++vi )
1339 {
1340 const ORCAD_SYMBOL_DEF* sym = variants[vi];
1341
1342 if( symbolPinsMatch( *sym, aInst ) )
1343 return { sym, (int) vi };
1344 }
1345
1346 return { prime, 0 };
1347}
1348
1349
1351{
1352 auto packageProperty = aInst.props.find( "Source Package" );
1353 std::string base = packageProperty != aInst.props.end() && !packageProperty->second.empty()
1354 ? packageProperty->second
1355 : !aInst.sourcePackage.empty() ? aInst.sourcePackage
1356 : aInst.pkgName.substr( 0, aInst.pkgName.find( '.' ) );
1357
1358 auto it = m_design.packages.find( base );
1359
1360 if( it == m_design.packages.end() )
1361 return nullptr;
1362
1363 auto matchesInstance = [&]( const ORCAD_PACKAGE& aPackage )
1364 {
1365 for( const ORCAD_DEVICE& device : aPackage.devices )
1366 {
1367 size_t activePins = 0;
1368
1369 for( size_t i = 0; i < device.pinNumbers.size(); ++i )
1370 {
1371 if( i >= device.pinIgnore.size() || !device.pinIgnore[i] )
1372 ++activePins;
1373 }
1374
1375 if( activePins == aInst.pins.size() )
1376 return true;
1377 }
1378
1379 return false;
1380 };
1381
1382 std::vector<const ORCAD_PACKAGE*> candidates = { &it->second };
1383
1384 for( const ORCAD_PACKAGE& variant : it->second.variants )
1385 candidates.push_back( &variant );
1386
1387 const ORCAD_SYMBOL_DEF* selectedSymbol = pickVariant( aInst ).first;
1388
1389 auto sourceProperty = aInst.props.find( "Source Library" );
1390 std::string sourceLibrary = sourceProperty != aInst.props.end() ? sourceProperty->second : std::string();
1391
1392 if( sourceLibrary.empty() )
1393 sourceLibrary = aInst.sourceLibrary;
1394
1395 if( sourceLibrary.empty() && selectedSymbol )
1396 sourceLibrary = selectedSymbol->sourceLib;
1397
1398 auto candidateFootprint = []( const ORCAD_PACKAGE& aPackage )
1399 {
1400 std::string footprint = aPackage.pcbFootprint;
1401 auto property = aPackage.props.find( "PCB Footprint" );
1402
1403 if( footprint.empty() && property != aPackage.props.end() )
1404 footprint = property->second;
1405
1406 return footprint;
1407 };
1408
1409 auto hasNumberedPins = []( const ORCAD_PACKAGE& aPackage )
1410 {
1411 return std::any_of( aPackage.devices.begin(), aPackage.devices.end(),
1412 []( const ORCAD_DEVICE& aDevice )
1413 {
1414 return std::any_of( aDevice.pinNumbers.begin(), aDevice.pinNumbers.end(),
1415 []( const std::string& aNumber )
1416 {
1417 return !aNumber.empty();
1418 } );
1419 } );
1420 };
1421
1422 if( !sourceLibrary.empty() )
1423 {
1424 std::string sourceKey = normalizedPath( sourceLibrary );
1425 bool designLibrary = sourceKey.ends_with( ".dsn" );
1426 bool numberedAlternative =
1427 std::any_of( candidates.begin(), candidates.end(),
1428 [&]( const ORCAD_PACKAGE* aCandidate )
1429 {
1430 return matchesInstance( *aCandidate ) && hasNumberedPins( *aCandidate );
1431 } );
1432
1433 for( const ORCAD_PACKAGE* candidate : candidates )
1434 {
1435 if( !matchesInstance( *candidate ) || normalizedPath( candidate->sourceLib ) != sourceKey )
1436 continue;
1437
1438 if( !designLibrary && numberedAlternative && !hasNumberedPins( *candidate ) )
1439 continue;
1440
1441 return candidate;
1442 }
1443 }
1444
1445 auto footprintProperty = aInst.props.find( "PCB Footprint" );
1446 std::string footprintKey =
1447 footprintProperty != aInst.props.end() ? normalizedPath( footprintProperty->second ) : std::string();
1448
1449 if( !footprintKey.empty() )
1450 {
1451 std::vector<const ORCAD_PACKAGE*> footprintMatches;
1452
1453 for( const ORCAD_PACKAGE* candidate : candidates )
1454 {
1455 if( matchesInstance( *candidate ) && normalizedPath( candidateFootprint( *candidate ) ) == footprintKey )
1456 footprintMatches.push_back( candidate );
1457 }
1458
1459 if( footprintMatches.size() == 1 )
1460 return footprintMatches.front();
1461 }
1462
1463 auto logicalPinMatches = [&]( const ORCAD_PACKAGE& aPackage )
1464 {
1465 if( !selectedSymbol || aInst.unitIndex >= aPackage.devices.size() )
1466 return 0;
1467
1468 const std::vector<std::string>& numbers = aPackage.devices[aInst.unitIndex].pinNumbers;
1469 int matches = 0;
1470
1471 for( const ORCAD_SYMBOL_PIN& pin : selectedSymbol->pins )
1472 {
1473 if( pin.position >= 0 && static_cast<size_t>( pin.position ) < numbers.size() && !pin.name.empty()
1474 && pin.name == numbers[pin.position] )
1475 {
1476 ++matches;
1477 }
1478 }
1479
1480 return matches;
1481 };
1482
1483 const ORCAD_PACKAGE* bestNumbered = nullptr;
1484 int bestLogicalMatches = -1;
1485
1486 for( const ORCAD_PACKAGE* candidate : candidates )
1487 {
1488 if( !matchesInstance( *candidate ) || !hasNumberedPins( *candidate ) )
1489 continue;
1490
1491 int logicalMatches = logicalPinMatches( *candidate );
1492
1493 if( logicalMatches > bestLogicalMatches )
1494 {
1495 bestNumbered = candidate;
1496 bestLogicalMatches = logicalMatches;
1497 }
1498 }
1499
1500 if( bestNumbered )
1501 return bestNumbered;
1502
1503 for( const ORCAD_PACKAGE* candidate : candidates )
1504 {
1505 if( matchesInstance( *candidate ) )
1506 return candidate;
1507 }
1508
1509 return &it->second;
1510}
1511
1512
1513std::map<std::string, std::string> ORCAD_CONVERTER::effectiveProps( const ORCAD_PLACED_INSTANCE& aInst,
1514 const ORCAD_SYMBOL_DEF& aDef ) const
1515{
1516 std::map<std::string, std::string> props = aDef.props;
1517
1518 auto overlay = [&]( const std::map<std::string, std::string>& aProperties, bool aAllowEmpty = false )
1519 {
1520 for( const auto& [name, value] : aProperties )
1521 {
1522 auto existing = std::find_if( props.begin(), props.end(),
1523 [&]( const auto& aProperty )
1524 {
1525 return OrcadIEquals( aProperty.first, name );
1526 } );
1527
1528 if( existing != props.end() )
1529 {
1530 if( aAllowEmpty || !value.empty() || existing->second.empty() )
1531 existing->second = value;
1532 }
1533 else
1534 props[name] = value;
1535 }
1536 };
1537
1538 if( const ORCAD_PACKAGE* pkg = packageFor( aInst ) )
1539 overlay( pkg->props );
1540
1541 overlay( aInst.props, true );
1542
1543 if( m_scope.occ )
1544 {
1545 auto occurrence = m_scope.occ->partProps.find( aInst.dbId );
1546
1547 if( occurrence != m_scope.occ->partProps.end() )
1548 overlay( occurrence->second, true );
1549 }
1550
1551 return props;
1552}
1553
1554
1556 const std::string& aNetName ) const
1557{
1558 const ORCAD_SYMBOL_DEF* definition = pickVariant( aInstance ).first;
1559
1560 if( !definition || aPinIndex >= aInstance.pins.size() )
1561 return false;
1562
1563 size_t index = symbolPinIndex( *definition, aInstance.pins[aPinIndex], aPinIndex );
1564
1565 if( index >= definition->pins.size() )
1566 return false;
1567
1568 const ORCAD_SYMBOL_PIN& pin = definition->pins[index];
1569
1570 return pin.portType == ORCAD_PORT_TYPE::POWER_IN && ( pin.shapeBits & 0x80 ) != 0
1571 && pin.hotptX == pin.startX && pin.hotptY == pin.startY && !pin.name.empty()
1572 && pin.name.compare( 0, 4, "$PIN" ) != 0 && pin.name == aNetName;
1573}
1574
1575
1576std::pair<std::string, int> ORCAD_CONVERTER::libForInstance( const ORCAD_PLACED_INSTANCE& aInst,
1577 const PKG_KEY** aSourceUnit )
1578{
1579 auto [sym, vi] = pickVariant( aInst );
1580
1581 auto sourceProperty = aInst.props.find( "Source Library" );
1582 std::string sourceLibrary = sourceProperty != aInst.props.end() ? sourceProperty->second : std::string();
1583
1584 if( sourceLibrary.empty() )
1585 sourceLibrary = aInst.sourceLibrary;
1586
1587 if( sourceLibrary.empty() && sym )
1588 sourceLibrary = sym->sourceLib;
1589
1590 auto packageProperty = aInst.props.find( "Source Package" );
1591 std::string sourcePackage = packageProperty != aInst.props.end() && !packageProperty->second.empty()
1592 ? packageProperty->second
1593 : !aInst.sourcePackage.empty() ? aInst.sourcePackage
1594 : aInst.pkgName.substr( 0, aInst.pkgName.find( '.' ) );
1595
1596 std::string srcOrPkg = sourceLibrary + "\n" + sourcePackage;
1597
1598 auto footprintProperty = aInst.props.find( "PCB Footprint" );
1599
1600 if( footprintProperty != aInst.props.end() )
1601 srcOrPkg += "\n" + footprintProperty->second;
1602 std::string base = sourcePackage;
1603
1604 std::string letter = unitLetter( aInst );
1605
1606 if( !sym )
1607 {
1608 warn( wxString::Format( _( "No cached symbol for '%s' (%s); placeholder emitted." ),
1609 FromOrcadString( aInst.pkgName ), FromOrcadString( aInst.reference ) ) );
1610
1611 // Register empty definition so unit references stable storage.
1612 ORCAD_SYMBOL_DEF& slot = m_design.symbols[aInst.pkgName];
1613
1614 if( slot.name.empty() )
1615 {
1616 slot.typeId = 0;
1617 slot.name = aInst.pkgName;
1618 }
1619
1620 sym = &slot;
1621 }
1622
1623 const ORCAD_PACKAGE* pkg = packageFor( aInst );
1624
1625 size_t unitIndex = aInst.unitIndex;
1626
1627 if( m_scope.occ )
1628 {
1629 auto unitRefIt = m_scope.occ->partUnitRefs.find( aInst.dbId );
1630
1631 if( unitRefIt != m_scope.occ->partUnitRefs.end() )
1632 letter = unitRefIt->second;
1633 }
1634
1635 if( letter.empty() && pkg )
1636 letter = packageUnitLetter( *pkg, unitIndex );
1637
1638 // The placed instance selects the package device. Occurrence hierarchy can
1639 // reassign its displayed unit letter without changing the physical pin map.
1640 std::vector<std::string> pinNumbers;
1641 std::vector<bool> pinIgnore;
1642
1643 if( pkg )
1644 {
1645 bool foundDevice = false;
1646
1647 if( unitIndex < pkg->devices.size() )
1648 {
1649 pinNumbers = pkg->devices[unitIndex].pinNumbers;
1650 pinIgnore = pkg->devices[unitIndex].pinIgnore;
1651 foundDevice = true;
1652 }
1653
1654 std::string bare = letter.substr( 0, letter.find( ':' ) );
1655
1656 if( !foundDevice )
1657 {
1658 for( const ORCAD_DEVICE& d : pkg->devices )
1659 {
1660 if( d.unitRef == bare )
1661 {
1662 pinNumbers = d.pinNumbers;
1663 pinIgnore = d.pinIgnore;
1664 foundDevice = true;
1665 break;
1666 }
1667 }
1668 }
1669
1670 if( !foundDevice && !pkg->devices.empty() && bare.empty() )
1671 {
1672 pinNumbers = pkg->devices.front().pinNumbers;
1673 pinIgnore = pkg->devices.front().pinIgnore;
1674 }
1675 }
1676
1677 std::string normalizedSourceLibrary = normalizedPath( sourceLibrary );
1678 std::string normalizedPackageLibrary = pkg ? normalizedPath( pkg->sourceLib ) : std::string();
1679
1680 bool logicalNumbers = std::all_of( sym->pins.begin(), sym->pins.end(),
1681 []( const ORCAD_SYMBOL_PIN& aPin )
1682 {
1683 return !aPin.name.empty()
1684 && std::all_of( aPin.name.begin(), aPin.name.end(),
1685 []( unsigned char c )
1686 {
1687 return std::isdigit( c );
1688 } );
1689 } );
1690 bool packagePinCountMatches = pkg
1691 && std::any_of( pkg->devices.begin(), pkg->devices.end(),
1692 [&]( const ORCAD_DEVICE& aDevice )
1693 {
1694 size_t activePins = 0;
1695
1696 for( size_t i = 0; i < aDevice.pinNumbers.size(); ++i )
1697 {
1698 if( i >= aDevice.pinIgnore.size() || !aDevice.pinIgnore[i] )
1699 {
1700 ++activePins;
1701 }
1702 }
1703
1704 return activePins == aInst.pins.size();
1705 } );
1706 bool stalePackageMap = logicalNumbers && pkg && pkg->devices.size() == 1
1707 && !normalizedSourceLibrary.ends_with( ".dsn" )
1708 && normalizedPackageLibrary != normalizedSourceLibrary;
1709
1710 if( logicalNumbers && pkg && !packagePinCountMatches && normalizedPackageLibrary != normalizedSourceLibrary )
1711 {
1712 pinNumbers.resize( sym->pins.size() );
1713 pinIgnore.assign( sym->pins.size(), false );
1714
1715 for( size_t pi = 0; pi < sym->pins.size(); ++pi )
1716 {
1717 int position = sym->pins[pi].position >= 0 ? sym->pins[pi].position : static_cast<int>( pi );
1718
1719 if( position >= 0 && static_cast<size_t>( position ) < pinNumbers.size() )
1720 pinNumbers[position] = sym->pins[pi].name;
1721 }
1722 }
1723
1724 bool packageUsesLogicalPinNames = vi > 0 && sym->pins.size() == 2 && pinNumbers.size() == 2;
1725
1726 if( packageUsesLogicalPinNames )
1727 {
1728 for( size_t pi = 0; pi < sym->pins.size(); ++pi )
1729 {
1730 int position = sym->pins[pi].position >= 0 ? sym->pins[pi].position : static_cast<int>( pi );
1731
1732 if( position < 0 || static_cast<size_t>( position ) >= pinNumbers.size()
1733 || !OrcadIEquals( pinNumbers[position], sym->pins[pi].name ) )
1734 {
1735 packageUsesLogicalPinNames = false;
1736 break;
1737 }
1738 }
1739 }
1740
1741 if( packageUsesLogicalPinNames && normalizedSourceLibrary.ends_with( ".dsn" ) )
1742 {
1743 const std::map<std::string, std::string> diodeNumbers = {
1744 { "a", "2" }, { "anode", "2" }, { "k", "1" }, { "cathode", "1" }
1745 };
1746 bool diodePins = true;
1747
1748 for( const ORCAD_SYMBOL_PIN& pin : sym->pins )
1749 {
1750 std::string name = OrcadLower( pin.name );
1751 diodePins = diodePins && diodeNumbers.count( name );
1752 }
1753
1754 if( diodePins )
1755 {
1756 for( size_t pi = 0; pi < sym->pins.size(); ++pi )
1757 {
1758 int position = sym->pins[pi].position >= 0 ? sym->pins[pi].position : static_cast<int>( pi );
1759 std::string name = OrcadLower( sym->pins[pi].name );
1760 pinNumbers[position] = diodeNumbers.at( name );
1761 }
1762 }
1763 }
1764
1765 bool blankPackageMap = !pinNumbers.empty()
1766 && std::all_of( pinNumbers.begin(), pinNumbers.end(),
1767 []( const std::string& aNumber )
1768 {
1769 return aNumber.empty();
1770 } );
1771 std::vector<bool> pinNumberVisible = nonblankPinNumbers( pinNumbers );
1772
1773 bool dualRowConnector =
1774 sym->synthesized && pinNumbers.size() == sym->pins.size() && pinNumbers.size() % 2 == 0
1775 && ( hasDualRowDescription( aInst.props ) || ( pkg && hasDualRowDescription( pkg->props ) ) );
1776
1777 if( blankPackageMap && dualRowConnector )
1778 {
1779 size_t half = pinNumbers.size() / 2;
1780 std::set<int> positions;
1781
1782 for( size_t pi = 0; pi < sym->pins.size(); ++pi )
1783 {
1784 int position = sym->pins[pi].position >= 0 ? sym->pins[pi].position : static_cast<int>( pi );
1785
1786 if( position >= 0 && static_cast<size_t>( position ) < pinNumbers.size() )
1787 positions.insert( position );
1788 }
1789
1790 if( positions.size() == pinNumbers.size() )
1791 {
1792 for( size_t position = 0; position < pinNumbers.size(); ++position )
1793 {
1794 size_t number = position < half ? 2 * position + 1 : 2 * ( position - half + 1 );
1795 pinNumbers[position] = std::to_string( number );
1796 pinNumberVisible[position] = true;
1797 }
1798 }
1799 }
1800
1801 if( !pinNumbers.empty() )
1802 {
1803 for( size_t pi = 0; pi < sym->pins.size(); ++pi )
1804 {
1805 int position = sym->pins[pi].position >= 0 ? sym->pins[pi].position : static_cast<int>( pi );
1806
1807 if( position >= 0 && static_cast<size_t>( position ) < pinNumbers.size() && pinNumbers[position].empty()
1808 && ( static_cast<size_t>( position ) >= pinIgnore.size() || !pinIgnore[position] ) )
1809 {
1810 pinNumbers[position] = sym->pins[pi].name;
1811 }
1812 }
1813 }
1814
1815 if( !normalizedSourceLibrary.ends_with( ".dsn" ) && sym->pins.size() == 2 && logicalNumbers && pkg
1816 && pkg->devices.size() == 1 )
1817 {
1818 for( size_t pi = 0; pi < sym->pins.size(); ++pi )
1819 {
1820 int position = sym->pins[pi].position >= 0 ? sym->pins[pi].position : static_cast<int>( pi );
1821
1822 if( position >= 0 && static_cast<size_t>( position ) < pinNumbers.size() && stalePackageMap
1823 && ( static_cast<size_t>( position ) >= pinIgnore.size() || !pinIgnore[position] ) )
1824 {
1825 pinNumbers[position] = sym->pins[pi].name;
1826 }
1827 }
1828 }
1829
1830 std::vector<int> pinOffsets( sym->pins.size() );
1831 std::vector<bool> explicitPinNets( sym->pins.size() );
1832 std::vector<int> placedOffsets = placedStackedPinOffsets( aInst );
1833
1834 for( size_t i = 0; i < aInst.pins.size(); ++i )
1835 {
1836 size_t symbolPin = symbolPinIndex( *sym, aInst.pins[i], i );
1837
1838 if( symbolPin < pinOffsets.size() )
1839 {
1840 pinOffsets[symbolPin] = placedOffsets[i];
1841
1842 if( !aInst.pins[i].IsNoConnect() && ( aInst.pins[i].wordA || aInst.pins[i].wordB )
1843 && !m_currentImplicitPowerPins.count( &aInst.pins[i] ) )
1844 explicitPinNets[symbolPin] = true;
1845 }
1846 }
1847
1848 PKG_KEY key{ srcOrPkg, aInst.pkgName, vi, letter };
1849
1850 if( aSourceUnit )
1851 {
1852 *aSourceUnit = nullptr;
1853 auto prepared = m_preparedPkgToLib.find( key );
1854
1855 if( prepared != m_preparedPkgToLib.end() )
1856 {
1857 *aSourceUnit = &prepared->first;
1858 }
1859 else
1860 {
1861 // An occurrence can rename its unit letter without changing the source device.
1862 for( const auto& [sourceKey, selection] : m_preparedPkgToLib )
1863 {
1864 if( std::get<0>( sourceKey ) != srcOrPkg || std::get<1>( sourceKey ) != aInst.pkgName
1865 || std::get<2>( sourceKey ) != vi )
1866 continue;
1867
1868 const UNIT_INFO& source = m_preparedLibUnits.at( selection.first )[selection.second - 1];
1869
1870 if( source.symbol == sym && source.pinNumbers == pinNumbers )
1871 {
1872 if( *aSourceUnit )
1873 {
1874 *aSourceUnit = nullptr;
1875 break;
1876 }
1877
1878 *aSourceUnit = &sourceKey;
1879 }
1880 }
1881 }
1882 }
1883
1884 auto found = m_pkgToLib.find( key );
1885
1886 if( found != m_pkgToLib.end() )
1887 {
1888 auto libIt = m_libSymbols.find( found->second.first );
1889 int cachedUnit = found->second.second;
1890
1891 if( libIt != m_libSymbols.end() && cachedUnit > 0
1892 && static_cast<size_t>( cachedUnit ) <= libIt->second.units.size() )
1893 {
1894 const UNIT_INFO& cached = libIt->second.units[cachedUnit - 1];
1895
1896 if( cached.pinNumbers == pinNumbers && cached.pinNumberVisible == pinNumberVisible
1897 && cached.pinIgnore == pinIgnore && cached.pinOffsets == pinOffsets
1898 && cached.explicitPinNets == explicitPinNets )
1899 return found->second;
1900 }
1901 }
1902
1903 std::string libnameBase = SymbolId( base );
1904
1905 if( vi )
1906 libnameBase += "_v" + std::to_string( vi + 1 );
1907
1908 std::string libname =
1909 uniqueLibName( libnameBase,
1910 [&]( const LIB_ENTRY& aLib )
1911 {
1912 auto unit = std::find_if( aLib.units.begin(), aLib.units.end(),
1913 [&]( const UNIT_INFO& aUnit )
1914 {
1915 return aUnit.letter == letter;
1916 } );
1917
1918 return unit == aLib.units.end()
1919 || ( unit->pinNumbers == pinNumbers && unit->pinNumberVisible == pinNumberVisible
1920 && unit->pinIgnore == pinIgnore && unit->pinOffsets == pinOffsets
1921 && unit->explicitPinNets == explicitPinNets );
1922 } );
1923
1924 LIB_ENTRY& ls = m_libSymbols[libname];
1925
1926 if( ls.name.empty() )
1927 {
1928 ls.name = libname;
1929 ls.refPrefix = ( pkg && !pkg->refDes.empty() ) ? pkg->refDes : "U";
1930 ls.footprint = pkg ? pkg->pcbFootprint : "";
1931 }
1932
1933 int unitNo = 0;
1934
1935 for( size_t i = 0; i < ls.units.size(); ++i )
1936 {
1937 if( ls.units[i].letter == letter )
1938 {
1939 unitNo = (int) i + 1;
1940 break;
1941 }
1942 }
1943
1944 if( unitNo == 0 )
1945 {
1946 UNIT_INFO unit;
1947 unit.letter = letter;
1948 unit.symbol = sym;
1949 unit.pinNumbers = pinNumbers;
1950 unit.pinNumberVisible = pinNumberVisible;
1951 unit.pinIgnore = pinIgnore;
1952 unit.pinOffsets = pinOffsets;
1953 unit.explicitPinNets = explicitPinNets;
1954 ls.units.push_back( std::move( unit ) );
1955 ls.kicadSymbol.reset();
1956
1957 std::stable_sort( ls.units.begin(), ls.units.end(),
1958 []( const UNIT_INFO& a, const UNIT_INFO& b )
1959 {
1960 return unitLetterLess( a.letter, b.letter );
1961 } );
1962
1963 for( size_t i = 0; i < ls.units.size(); ++i )
1964 {
1965 if( ls.units[i].letter == letter )
1966 {
1967 unitNo = (int) i + 1;
1968 break;
1969 }
1970 }
1971
1972 // Inserting a unit renumbers later-sorting letters, so every map entry for
1973 // this lib symbol is stale; rebuild them.
1974 for( auto mapIt = m_pkgToLib.begin(); mapIt != m_pkgToLib.end(); )
1975 {
1976 if( mapIt->second.first == libname )
1977 mapIt = m_pkgToLib.erase( mapIt );
1978 else
1979 ++mapIt;
1980 }
1981
1982 for( size_t i = 0; i < ls.units.size(); ++i )
1983 {
1984 std::string pkgName = sourcePackage + ls.units[i].letter + ".Normal";
1985
1986 m_pkgToLib[PKG_KEY{ srcOrPkg, pkgName, vi, ls.units[i].letter }] = { libname, (int) i + 1 };
1987 }
1988 }
1989
1990 m_pkgToLib[key] = { libname, unitNo };
1991 return { libname, unitNo };
1992}
1993
1994
1995std::string ORCAD_CONVERTER::powerLibFor( const std::string& aSymbolName, const std::string& aNetName )
1996{
1997 std::string libname = "PWR_" + SymbolId( aNetName ) + "_" + SymbolId( aSymbolName );
1998
1999 if( m_libSymbols.count( libname ) )
2000 return libname;
2001
2002 const ORCAD_SYMBOL_DEF* sym = nullptr;
2003 auto it = m_design.symbols.find( aSymbolName );
2004
2005 if( it != m_design.symbols.end() )
2006 {
2007 sym = &it->second;
2008 }
2009 else
2010 {
2011 // Uncached power symbol; register empty definition (no graphics).
2012 ORCAD_SYMBOL_DEF& slot = m_design.symbols[aSymbolName];
2013
2014 if( slot.name.empty() )
2015 {
2017 slot.name = aSymbolName;
2018 }
2019
2020 sym = &slot;
2021 }
2022
2023 LIB_ENTRY& ls = m_libSymbols[libname];
2024 ls.name = libname;
2025 ls.isPower = true;
2026 ls.powerNet = aNetName;
2027 ls.refPrefix = "#PWR";
2028
2029 UNIT_INFO unit;
2030 unit.symbol = sym;
2031 unit.pinNumbers = { "1" };
2032 ls.units.push_back( std::move( unit ) );
2033
2034 return libname;
2035}
2036
2037
2038LIB_SYMBOL* ORCAD_CONVERTER::kicadSymbolFor( const std::string& aLibName )
2039{
2040 auto it = m_libSymbols.find( aLibName );
2041
2042 if( it == m_libSymbols.end() )
2043 return nullptr;
2044
2045 LIB_ENTRY& entry = it->second;
2046
2047 if( entry.kicadSymbol )
2048 return entry.kicadSymbol.get();
2049
2050 // OrCAD cache names can contain LIB_ID-illegal chars (e.g. backslashes from
2051 // library path prefixes).
2052 wxString name = LIB_ID::FixIllegalChars( FromOrcadString( entry.name ), false ).wx_str();
2053
2054 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( name );
2055 symbol->SetLibId( libIdFor( entry.name ) );
2056 symbol->SetPinNameOffset( schMm( 0.254 ) );
2057
2058 // Per-pin text requires the symbol-wide visibility switch. Pin text sizes retain individual visibility.
2059 bool defaultShowPinNames = symbol->GetShowPinNames();
2060 bool defaultShowPinNumbers = symbol->GetShowPinNumbers();
2061
2062 for( const UNIT_INFO& unit : entry.units )
2063 {
2064 if( unit.symbol && unit.symbol->generalFlags >= 0 )
2065 {
2066 int flags = unit.symbol->generalFlags;
2067 defaultShowPinNumbers = ( flags & 0x04 ) == 0;
2068 defaultShowPinNames = ( flags & 0x01 ) != 0;
2069 break;
2070 }
2071 }
2072
2073 bool showPinNames = defaultShowPinNames;
2074 bool showPinNumbers = defaultShowPinNumbers;
2075
2076 for( const UNIT_INFO& unit : entry.units )
2077 {
2078 if( !unit.symbol )
2079 continue;
2080
2081 for( size_t pinIndex = 0; pinIndex < unit.symbol->pins.size(); ++pinIndex )
2082 {
2083 const ORCAD_SYMBOL_PIN& sourcePin = unit.symbol->pins[pinIndex];
2084 int position = sourcePin.position >= 0 ? sourcePin.position : static_cast<int>( pinIndex );
2085
2086 for( const ORCAD_DISPLAY_PROP& display : sourcePin.displayProps )
2087 {
2088 if( !OrcadDisplayPropVisible( display ) )
2089 continue;
2090
2091 showPinNames |= OrcadIEquals( display.name, "Name" ) || OrcadIEquals( display.name, "Pin Name" );
2092
2093 if( position >= static_cast<int>( unit.pinNumberVisible.size() )
2094 || unit.pinNumberVisible[position] )
2095 {
2096 showPinNumbers |=
2097 OrcadIEquals( display.name, "Number" ) || OrcadIEquals( display.name, "Pin Number" );
2098 }
2099 }
2100 }
2101 }
2102
2103 symbol->SetShowPinNames( showPinNames );
2104 symbol->SetShowPinNumbers( showPinNumbers );
2105
2106 if( entry.isPower )
2107 symbol->SetGlobalPower();
2108
2109 if( std::any_of( entry.units.begin(), entry.units.end(),
2110 []( const UNIT_INFO& aUnit )
2111 {
2112 return aUnit.convert != nullptr;
2113 } ) )
2114 {
2115 symbol->SetHasDeMorganBodyStyles( true );
2116 }
2117
2118 if( (int) entry.units.size() > 1 )
2119 {
2120 symbol->SetUnitCount( (int) entry.units.size(), false );
2121
2122 for( size_t unit = 0; unit < entry.units.size(); ++unit )
2123 symbol->GetUnitDisplayNames()[static_cast<int>( unit ) + 1] = wxEmptyString;
2124 }
2125
2126 const ORCAD_SYMBOL_DEF* u0 = entry.units.empty() ? nullptr : entry.units.front().symbol;
2127 ORCAD_BBOX bb = ( u0 && u0->bbox ) ? *u0->bbox : ORCAD_BBOX();
2128
2129 // Default field spot above body; in-memory symbol space Y-down, so above = more
2130 // negative Y.
2131 int topY = bb.y1 * ORCAD_IU_PER_DBU - schMm( 2.54 );
2132
2133 SCH_FIELD& refField = symbol->GetReferenceField();
2134 SCH_FIELD& valField = symbol->GetValueField();
2135
2136 if( entry.isPower )
2137 {
2138 refField.SetText( wxS( "#PWR" ) );
2139 refField.SetVisible( false );
2140 refField.SetTextPos( VECTOR2I( 0, topY ) );
2141
2142 valField.SetText( FromOrcadString( entry.powerNet ) );
2143 valField.SetVisible( true );
2144 valField.SetTextPos( VECTOR2I( 0, topY ) );
2145 }
2146 else
2147 {
2148 refField.SetText( FromOrcadString( entry.refPrefix ) );
2149 refField.SetVisible( true );
2150 refField.SetTextPos( VECTOR2I( 0, topY ) );
2151
2152 valField.SetText( name );
2153 valField.SetVisible( true );
2154 valField.SetTextPos( VECTOR2I( 0, topY - schMm( 2.54 ) ) );
2155 }
2156
2157 // An empty Footprint field permits relinking imported board footprints by reference.
2158 symbol->GetFootprintField().SetVisible( false );
2159 symbol->GetDatasheetField().SetVisible( false );
2160
2161 // Items stay common to both body styles unless their unit has a Convert view
2162 auto stampBodyStyle =
2163 [&]( int aUnit, int aBodyStyle )
2164 {
2165 for( SCH_ITEM& item : symbol->GetDrawItems() )
2166 {
2167 if( item.Type() != SCH_FIELD_T && item.GetUnit() == aUnit && item.GetBodyStyle() == ALL_BODY_STYLES )
2168 item.SetBodyStyle( aBodyStyle );
2169 }
2170 };
2171
2172 for( size_t ui = 0; ui < entry.units.size(); ++ui )
2173 {
2174 const UNIT_INFO& unit = entry.units[ui];
2175 int unitNo = (int) ui + 1;
2176
2177 if( !unit.symbol )
2178 continue;
2179
2180 const int bodyStyles = unit.convert ? 2 : 1;
2181
2182 for( int bodyStyle = 1; bodyStyle <= bodyStyles; ++bodyStyle )
2183 {
2184 const ORCAD_SYMBOL_DEF& view = bodyStyle == 1 ? *unit.symbol : *unit.convert;
2185 const int stamp = unit.convert ? bodyStyle : 0;
2186
2187 for( size_t primitiveIndex = 0; primitiveIndex < view.primitives.size(); ++primitiveIndex )
2188 {
2189 const ORCAD_PRIMITIVE& primitive = view.primitives[primitiveIndex];
2190
2191 if( laterRectangleOccludesTextUnderscores( view.primitives, primitiveIndex ) )
2192 {
2193 ORCAD_PRIMITIVE visible = primitive;
2194 std::replace( visible.text.begin(), visible.text.end(), '_', ' ' );
2195 addSymbolPrimitive( symbol.get(), visible, unitNo, view.color );
2196 }
2197 else
2198 {
2199 addSymbolPrimitive( symbol.get(), primitive, unitNo, view.color );
2200 }
2201 }
2202
2203 if( stamp )
2204 stampBodyStyle( unitNo, stamp );
2205
2206 BOX2I bodyBox = symbol->GetBodyBoundingBox( unitNo, stamp, false, false );
2207
2208 for( size_t pi = 0; pi < view.pins.size(); ++pi )
2209 {
2210 ORCAD_SYMBOL_PIN sourcePin = view.pins[pi];
2211 int position = view.pins[pi].position >= 0 ? view.pins[pi].position
2212 : static_cast<int>( pi );
2213
2214 if( position < static_cast<int>( unit.pinIgnore.size() ) && unit.pinIgnore[position] )
2215 continue;
2216
2217 wxString number = position < static_cast<int>( unit.pinNumbers.size() )
2218 ? FromOrcadString( unit.pinNumbers[position] )
2219 : wxString::Format( wxS( "%d" ), (int) pi + 1 );
2220
2221 int pinOffset = pi < unit.pinOffsets.size() ? unit.pinOffsets[pi] : 0;
2222 sourcePin.hotptX += pinOffset;
2223 sourcePin.startX += pinOffset;
2224
2225 PIN_EMIT emit;
2226 emit.number = number;
2227 emit.unit = unitNo;
2228 emit.power = entry.isPower;
2229 emit.nameOverride = entry.isPower ? entry.powerNet : std::string();
2230 emit.nameVisible = defaultShowPinNames;
2231 emit.showPinNumbers = defaultShowPinNumbers;
2232 emit.numberVisible = position >= static_cast<int>( unit.pinNumberVisible.size() )
2233 || unit.pinNumberVisible[position];
2234 emit.hidden = pinOffset != 0;
2235 emit.explicitNet = pi < unit.explicitPinNets.size() && unit.explicitPinNets[pi];
2236 addSymbolPin( symbol.get(), sourcePin, bodyBox, std::move( emit ) );
2237 }
2238
2239 if( stamp )
2240 stampBodyStyle( unitNo, stamp );
2241 }
2242 }
2243
2244 entry.kicadSymbol = std::move( symbol );
2245 return entry.kicadSymbol.get();
2246}
2247
2248
2249static void addOrcadSymbolHatch( LIB_SYMBOL* aSymbol, const SCH_SHAPE& aOutline,
2250 const ORCAD_PRIMITIVE& aPrimitive, int aUnit, int aColor,
2251 uint32_t aModifyTimestamp )
2252{
2253 if( aPrimitive.fillStyle != 2 )
2254 return;
2255
2256 STROKE_PARAMS hatchStroke( OrcadHatchLineWidthIu( aModifyTimestamp ), LINE_STYLE::SOLID,
2257 OrcadColor( aColor ) );
2258
2259 for( const SEG& line : OrcadHatchLines( aOutline, aPrimitive.hatchStyle,
2260 OrcadHatchPitchIu( aModifyTimestamp ) ) )
2261 {
2263 hatch->AddPoint( line.A );
2264 hatch->AddPoint( line.B );
2265 hatch->SetStroke( hatchStroke );
2266 hatch->SetFillMode( FILL_T::NO_FILL );
2267 hatch->SetUnit( aUnit );
2268 aSymbol->AddDrawItem( hatch, false );
2269 }
2270}
2271
2272
2273void ORCAD_CONVERTER::addSymbolPrimitive( LIB_SYMBOL* aSymbol, const ORCAD_PRIMITIVE& aPrim, int aUnit, int aColor,
2274 int aOffsetX, int aOffsetY )
2275{
2276 // Cache defs and in-memory symbol space both Y-down; .kicad_sch writer flips to
2277 // file Y-up itself.
2278 auto toX = [aOffsetX]( int aV )
2279 {
2280 return ( aV + aOffsetX ) * ORCAD_IU_PER_DBU;
2281 };
2282 auto toY = [aOffsetY]( int aV )
2283 {
2284 return ( aV + aOffsetY ) * ORCAD_IU_PER_DBU;
2285 };
2286
2287 switch( aPrim.kind )
2288 {
2290 for( const ORCAD_PRIMITIVE& child : aPrim.children )
2291 addSymbolPrimitive( aSymbol, child, aUnit, aColor, aOffsetX + aPrim.x1, aOffsetY + aPrim.y1 );
2292
2293 break;
2294
2296 {
2298 shape->SetPosition( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ) );
2299 shape->SetEnd( VECTOR2I( toX( aPrim.x2 ), toY( aPrim.y2 ) ) );
2300 shape->SetStroke( strokeFor( aPrim, aColor ) );
2301 shape->SetFillMode( aPrim.fillStyle == 2 ? FILL_T::NO_FILL
2302 : OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
2303 shape->SetUnit( aUnit );
2304 aSymbol->AddDrawItem( shape, false );
2305 addOrcadSymbolHatch( aSymbol, *shape, aPrim, aUnit, aColor, m_design.library.modifyTimestamp );
2306 break;
2307 }
2308
2310 {
2312 shape->AddPoint( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ) );
2313 shape->AddPoint( VECTOR2I( toX( aPrim.x2 ), toY( aPrim.y2 ) ) );
2314 shape->SetStroke( strokeFor( aPrim, aColor ) );
2315 shape->SetFillMode( FILL_T::NO_FILL );
2316 shape->SetUnit( aUnit );
2317 aSymbol->AddDrawItem( shape, false );
2318 break;
2319 }
2320
2323 {
2324 if( aPrim.points.size() < 2 )
2325 return;
2326
2328
2329 for( const ORCAD_POINT& pt : aPrim.points )
2330 shape->AddPoint( VECTOR2I( toX( pt.x ), toY( pt.y ) ) );
2331
2332 bool isPolygon = aPrim.kind == ORCAD_PRIM_KIND::POLYGON;
2333
2334 if( isPolygon && aPrim.points.front() != aPrim.points.back() )
2335 shape->AddPoint( VECTOR2I( toX( aPrim.points.front().x ), toY( aPrim.points.front().y ) ) );
2336
2337 shape->SetStroke( strokeFor( aPrim, aColor ) );
2338 shape->SetFillMode( isPolygon && aPrim.fillStyle != 2
2339 ? OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle )
2340 : FILL_T::NO_FILL );
2341 shape->SetUnit( aUnit );
2342 aSymbol->AddDrawItem( shape, false );
2343
2344 if( isPolygon )
2345 addOrcadSymbolHatch( aSymbol, *shape, aPrim, aUnit, aColor, m_design.library.modifyTimestamp );
2346
2347 break;
2348 }
2349
2351 {
2352 if( aPrim.points.size() < 4 || ( aPrim.points.size() - 1 ) % 3 != 0 )
2353 {
2354 if( aPrim.points.size() >= 2 )
2355 {
2357
2358 for( const ORCAD_POINT& pt : aPrim.points )
2359 shape->AddPoint( VECTOR2I( toX( pt.x ), toY( pt.y ) ) );
2360
2361 shape->SetStroke( strokeFor( aPrim, aColor ) );
2362 shape->SetFillMode( FILL_T::NO_FILL );
2363 shape->SetUnit( aUnit );
2364 aSymbol->AddDrawItem( shape, false );
2365 }
2366
2367 return;
2368 }
2369
2370 for( size_t i = 0; i + 3 < aPrim.points.size(); i += 3 )
2371 {
2373 shape->SetPosition( VECTOR2I( toX( aPrim.points[i].x ), toY( aPrim.points[i].y ) ) );
2374 shape->SetBezierC1( VECTOR2I( toX( aPrim.points[i + 1].x ), toY( aPrim.points[i + 1].y ) ) );
2375 shape->SetBezierC2( VECTOR2I( toX( aPrim.points[i + 2].x ), toY( aPrim.points[i + 2].y ) ) );
2376 shape->SetEnd( VECTOR2I( toX( aPrim.points[i + 3].x ), toY( aPrim.points[i + 3].y ) ) );
2377 shape->SetStroke( strokeFor( aPrim, aColor ) );
2378 shape->SetFillMode( FILL_T::NO_FILL );
2379 shape->SetUnit( aUnit );
2380 aSymbol->AddDrawItem( shape, false );
2381 }
2382
2383 break;
2384 }
2385
2387 {
2388 double cx = ( aPrim.x1 + aPrim.x2 ) / 2.0;
2389 double cy = ( aPrim.y1 + aPrim.y2 ) / 2.0;
2390 double rx = std::abs( aPrim.x2 - aPrim.x1 ) / 2.0;
2391 double ry = std::abs( aPrim.y2 - aPrim.y1 ) / 2.0;
2392
2393 if( std::abs( rx - ry ) < 0.5 )
2394 {
2396 VECTOR2I center( dbuIu( cx ), dbuIu( cy ) );
2397 shape->SetPosition( center );
2398 shape->SetEnd( center + VECTOR2I( dbuIu( rx ), 0 ) );
2399 shape->SetStroke( strokeFor( aPrim, aColor ) );
2400 shape->SetFillMode( aPrim.fillStyle == 2 ? FILL_T::NO_FILL
2401 : OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
2402 shape->SetUnit( aUnit );
2403 aSymbol->AddDrawItem( shape, false );
2404 addOrcadSymbolHatch( aSymbol, *shape, aPrim, aUnit, aColor, m_design.library.modifyTimestamp );
2405 }
2406 else
2407 {
2408 // Unequal radii; approximate ellipse with closed polyline.
2410
2411 for( int k = 0; k <= 32; ++k )
2412 {
2413 double a = 2.0 * M_PI * k / 32.0;
2414 shape->AddPoint( VECTOR2I( dbuIu( cx + rx * std::cos( a ) ), dbuIu( cy + ry * std::sin( a ) ) ) );
2415 }
2416
2417 shape->SetStroke( strokeFor( aPrim, aColor ) );
2418 shape->SetFillMode( aPrim.fillStyle == 2 ? FILL_T::NO_FILL
2419 : OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
2420 shape->SetUnit( aUnit );
2421 aSymbol->AddDrawItem( shape, false );
2422 addOrcadSymbolHatch( aSymbol, *shape, aPrim, aUnit, aColor, m_design.library.modifyTimestamp );
2423 }
2424
2425 break;
2426 }
2427
2428 case ORCAD_PRIM_KIND::ARC: addSymbolArc( aSymbol, aPrim, aUnit, aColor, aOffsetX, aOffsetY ); break;
2429
2431 {
2432 bool multiline = aPrim.text.find( '\n' ) != std::string::npos;
2433 bool hasBox = aPrim.x1 != aPrim.x2 || aPrim.y1 != aPrim.y2;
2434 int textX = hasBox && !multiline ? ( aPrim.x1 + aPrim.x2 ) / 2 : aPrim.x1;
2435 int textY = hasBox ? ( aPrim.y1 + aPrim.y2 ) / 2 : aPrim.y1;
2436 int boxedTextYOffset = 0;
2437 SCH_TEXT* text =
2438 new SCH_TEXT( VECTOR2I( toX( textX ), toY( textY ) ), FromOrcadString( aPrim.text ), LAYER_DEVICE );
2439 text->SetTextSize( textSize( aPrim.fontIdx, false ) );
2440 applyFont( text, aPrim.fontIdx, false );
2441 applyMultilineSpacing( text, aPrim.fontIdx, false );
2442 text->SetTextColor( OrcadColor( aColor ) );
2443
2444 if( aPrim.fontIdx > 0 && aPrim.fontIdx <= static_cast<int>( m_design.library.fonts.size() ) )
2445 {
2446 int quarterTurns = KiROUND( m_design.library.fonts[aPrim.fontIdx - 1].escapement / 900.0 );
2447
2448 if( std::abs( quarterTurns ) % 2 == 1 )
2449 text->SetTextAngle( ANGLE_VERTICAL );
2450 }
2451
2452 text->SetHorizJustify( hasBox && !multiline ? GR_TEXT_H_ALIGN_CENTER : GR_TEXT_H_ALIGN_LEFT );
2453 text->SetVertJustify( hasBox ? GR_TEXT_V_ALIGN_CENTER : GR_TEXT_V_ALIGN_TOP );
2454
2455 if( hasBox && aPrim.fontIdx > 0 && aPrim.fontIdx <= static_cast<int>( m_design.library.fonts.size() ) )
2456 {
2457 const ORCAD_FONT& font = m_design.library.fonts[aPrim.fontIdx - 1];
2458
2459 if( ( font.pitchAndFamily & 0x3 ) == 1 && font.width != 0 )
2460 {
2461 BOX2I inkBox = text->GetEffectiveTextShape( false )->BBox();
2462 int boxWidth = std::abs( aPrim.x2 - aPrim.x1 ) * ORCAD_IU_PER_DBU;
2463 int boxHeight = std::abs( aPrim.y2 - aPrim.y1 ) * ORCAD_IU_PER_DBU;
2464 int inkWidth = std::max( ORCAD_IU_PER_DBU,
2465 boxWidth - std::abs( font.width ) * ORCAD_IU_PER_DBU );
2466 int inkHeight = KiROUND( boxHeight * 0.7 );
2467 VECTOR2I size = text->GetTextSize();
2468
2469 if( inkBox.GetWidth() > 0 && inkBox.GetHeight() > 0 )
2470 {
2471 size.x = KiROUND( static_cast<double>( size.x ) * inkWidth / inkBox.GetWidth() );
2472 size.y = KiROUND( static_cast<double>( size.y ) * inkHeight / inkBox.GetHeight() );
2473 text->SetTextSize( size );
2474 boxedTextYOffset = ( boxHeight - inkHeight ) / 4;
2475 }
2476 }
2477 }
2478
2479 if( hasBox )
2480 text->SetPosition( VECTOR2I( toX( textX ), toY( textY ) + boxedTextYOffset ) );
2481
2482 text->SetUnit( aUnit );
2483 aSymbol->AddDrawItem( text, false );
2484 break;
2485 }
2486
2488 // Embedded images only in page graphics, not symbol bodies.
2489 break;
2490 }
2491}
2492
2493
2494void ORCAD_CONVERTER::addSymbolArc( LIB_SYMBOL* aSymbol, const ORCAD_PRIMITIVE& aPrim, int aUnit, int aColor,
2495 int aOffsetX, int aOffsetY )
2496{
2497 if( !aPrim.start || !aPrim.end )
2498 return;
2499
2500 if( aPrim.start == aPrim.end )
2501 return;
2502
2503 double cx = ( aPrim.x1 + aPrim.x2 ) / 2.0;
2504 double cy = ( aPrim.y1 + aPrim.y2 ) / 2.0;
2505 double rx = std::abs( aPrim.x2 - aPrim.x1 ) / 2.0;
2506 double ry = std::abs( aPrim.y2 - aPrim.y1 ) / 2.0;
2507
2508 if( rx == 0.0 )
2509 rx = 0.01;
2510
2511 if( ry == 0.0 )
2512 ry = 0.01;
2513
2514 double a0 = std::atan2( ( aPrim.start->y - cy ) / ry, ( aPrim.start->x - cx ) / rx );
2515 double a1 = std::atan2( ( aPrim.end->y - cy ) / ry, ( aPrim.end->x - cx ) / rx );
2516
2517 // OrCAD draws arcs CCW in screen (Y-down) coords, meaning decreasing parameter
2518 // angle here.
2519 if( a1 >= a0 )
2520 a1 -= 2.0 * M_PI;
2521
2522 int steps = std::max( 4, (int) ( std::abs( a1 - a0 ) / ( M_PI / 16.0 ) ) );
2523
2524 // Polyline approximation; robust for elliptical arcs KiCad arcs cannot represent.
2526
2527 for( int k = 0; k <= steps; ++k )
2528 {
2529 double a = a0 + ( a1 - a0 ) * k / steps;
2530 shape->AddPoint(
2531 VECTOR2I( dbuIu( cx + rx * std::cos( a ) + aOffsetX ), dbuIu( cy + ry * std::sin( a ) + aOffsetY ) ) );
2532 }
2533
2534 shape->SetStroke( strokeFor( aPrim, aColor ) );
2535 shape->SetFillMode( FILL_T::NO_FILL );
2536 shape->SetUnit( aUnit );
2537 aSymbol->AddDrawItem( shape, false );
2538}
2539
2540
2541std::string ORCAD_CONVERTER::uniqueLibName( const std::string& aBase,
2542 const std::function<bool( const LIB_ENTRY& )>& aReusable ) const
2543{
2544 std::string libname = aBase;
2545
2546 for( size_t discriminator = 2;; ++discriminator )
2547 {
2548 auto lib = m_libSymbols.find( libname );
2549
2550 if( lib == m_libSymbols.end() || aReusable( lib->second ) )
2551 return libname;
2552
2553 libname = aBase + "_pins" + std::to_string( discriminator );
2554 }
2555}
2556
2557
2558SCH_SYMBOL* ORCAD_CONVERTER::instantiateSymbol( const LIB_SYMBOL& aLibSymbol, const std::string& aLibName, int aUnit,
2559 int aOrient, const VECTOR2I& aPos,
2560 const SCH_SHEET_PATH& aSheetPath ) const
2561{
2562 SCH_SYMBOL* symbol = new SCH_SYMBOL( aLibSymbol, libIdFor( aLibName ), &aSheetPath, aUnit, 0, aPos );
2563 symbol->SetOrientation( toKicadOrientation( aOrient ) );
2564 return symbol;
2565}
2566
2567
2568void ORCAD_CONVERTER::addSymbolPin( LIB_SYMBOL* aSymbol, const ORCAD_SYMBOL_PIN& aPin, const BOX2I& aBodyBox,
2569 PIN_EMIT aEmit )
2570{
2571 int dx = aPin.startX - aPin.hotptX;
2572 int dy = aPin.startY - aPin.hotptY;
2573 int pinLength = KiROUND( std::hypot( (double) dx, (double) dy ) ) * ORCAD_IU_PER_DBU;
2574 bool hiddenPowerStyle = pinLength == 0 && ( aPin.shapeBits & 0x80 ) != 0;
2575
2576 if( dx == 0 && dy == 0 )
2577 {
2578 VECTOR2I hotPoint( aPin.hotptX * ORCAD_IU_PER_DBU, aPin.hotptY * ORCAD_IU_PER_DBU );
2579 std::array<int, 4> distances = {
2580 std::abs( hotPoint.x - aBodyBox.GetLeft() ), std::abs( hotPoint.x - aBodyBox.GetRight() ),
2581 std::abs( hotPoint.y - aBodyBox.GetTop() ), std::abs( hotPoint.y - aBodyBox.GetBottom() )
2582 };
2583
2584 switch( std::min_element( distances.begin(), distances.end() ) - distances.begin() )
2585 {
2586 case 0: dx = 1; break;
2587 case 1: dx = -1; break;
2588 case 2: dy = 1; break;
2589 case 3: dy = -1; break;
2590 }
2591 }
2592
2593 SCH_PIN* pin = new SCH_PIN( aSymbol );
2594
2595 // Hot point = connection point; start point = body end.
2596 pin->SetPosition( VECTOR2I( aPin.hotptX * ORCAD_IU_PER_DBU, aPin.hotptY * ORCAD_IU_PER_DBU ) );
2597 pin->SetLength( pinLength );
2598
2600
2601 if( dx > 0 )
2602 orientation = PIN_ORIENTATION::PIN_RIGHT;
2603 else if( dx < 0 )
2604 orientation = PIN_ORIENTATION::PIN_LEFT;
2605 else if( dy > 0 )
2606 orientation = PIN_ORIENTATION::PIN_DOWN; // start below hot point (Y-down source)
2607 else if( dy < 0 )
2608 orientation = PIN_ORIENTATION::PIN_UP;
2609
2610 pin->SetOrientation( orientation );
2611
2612 // shapeBits bit1 = clock, bit2 = inverted dot.
2613 bool isClock = ( aPin.shapeBits & 0x2 ) != 0;
2614 bool isDot = ( aPin.shapeBits & 0x4 ) != 0;
2615
2616 if( isClock && isDot )
2618 else if( isClock )
2619 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
2620 else if( isDot )
2621 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
2622 else
2623 pin->SetShape( GRAPHIC_PINSHAPE::LINE );
2624
2625 if( aEmit.power )
2626 {
2628 pin->SetVisible( false );
2629 }
2630 else
2631 {
2632 pin->SetType( pinTypeFor( aPin.portType ) );
2633 }
2634
2635 if( hiddenPowerStyle )
2636 pin->SetVisible( false );
2637
2638 if( aEmit.hidden )
2639 {
2640 pin->SetVisible( false );
2641 pin->SetLength( 0 );
2642 }
2643
2644 if( aEmit.explicitNet && !pin->IsVisible() && pin->GetType() == ELECTRICAL_PINTYPE::PT_POWER_IN )
2646
2647 // "$PIN"-prefixed names are OrCAD auto-generated placeholders.
2648 wxString name;
2649
2650 if( !aEmit.nameOverride.empty() )
2652 else if( !aPin.name.empty() && aPin.name.compare( 0, 4, "$PIN" ) != 0 )
2653 name = FromOrcadString( aPin.name );
2654 else if( aEmit.nameVisible && !aEmit.showPinNumbers )
2655 name = aEmit.number;
2656
2657 auto pinTextSize = [&]( int aFontIdx )
2658 {
2659 int resolved = resolveFontIndex( aFontIdx );
2660
2661 if( resolved > 0 && resolved <= static_cast<int>( m_design.library.fonts.size() ) )
2662 return textSizeIU( aFontIdx );
2663
2664 return schMm( 1.78 );
2665 };
2666
2667 auto findDisplay = [&]( const char* aName, const char* aAlternate ) -> const ORCAD_DISPLAY_PROP*
2668 {
2669 auto it = std::find_if( aPin.displayProps.begin(), aPin.displayProps.end(),
2670 [&]( const ORCAD_DISPLAY_PROP& aProp )
2671 {
2672 return OrcadIEquals( aProp.name, aName ) || OrcadIEquals( aProp.name, aAlternate );
2673 } );
2674 return it != aPin.displayProps.end() ? &*it : nullptr;
2675 };
2676
2677 const ORCAD_DISPLAY_PROP* nameDisplay = findDisplay( "Name", "Pin Name" );
2678 const ORCAD_DISPLAY_PROP* numberDisplay = findDisplay( "Number", "Pin Number" );
2679
2680 if( nameDisplay )
2681 aEmit.nameVisible = OrcadDisplayPropVisible( *nameDisplay );
2682
2683 aEmit.numberVisible =
2684 aEmit.numberVisible && ( numberDisplay ? OrcadDisplayPropVisible( *numberDisplay ) : aEmit.showPinNumbers );
2685
2686 int nameFont = nameDisplay && nameDisplay->fontIdx > 0 ? nameDisplay->fontIdx : m_design.library.pinNameFont;
2687 int numberFont = numberDisplay && numberDisplay->fontIdx > 0 ? numberDisplay->fontIdx
2688 : m_design.library.pinNumberFont;
2689
2690 pin->SetName( OrcadPinNameMarkup( name ) );
2691 pin->SetNumber( aEmit.number );
2692 pin->SetNameTextSize( aEmit.nameVisible && !name.IsEmpty() ? pinTextSize( nameFont ) : 0 );
2693 pin->SetNumberTextSize( aEmit.numberVisible && !aEmit.number.IsEmpty() ? pinTextSize( numberFont ) : 0 );
2694 pin->SetUnit( aEmit.unit );
2695
2696 if( aEmit.hidden )
2697 {
2698 pin->SetNameTextSize( 0 );
2699 pin->SetNumberTextSize( 0 );
2700 }
2701
2702 aSymbol->AddDrawItem( pin, false );
2703
2704 if( !aEmit.power && !aEmit.hidden && pinLength > 0 && aPin.portType == ORCAD_PORT_TYPE::INPUT_TYPE )
2705 {
2706 VECTOR2I direction( ( dx > 0 ) - ( dx < 0 ), ( dy > 0 ) - ( dy < 0 ) );
2707 VECTOR2I normal( -direction.y, direction.x );
2709 VECTOR2I base = apex - direction * dbuIu( 4 );
2710 VECTOR2I halfWidth = normal * dbuIu( 3 );
2712 wedge->AddPoint( apex );
2713 wedge->AddPoint( base + halfWidth );
2714 wedge->AddPoint( base - halfWidth );
2715 wedge->AddPoint( apex );
2718 wedge->SetUnit( aEmit.unit );
2719 aSymbol->AddDrawItem( wedge, false );
2720 }
2721}
2722
2723
2725{
2726 switch( aOrient & 7 )
2727 {
2728 default:
2729 case 0: return SYM_ORIENT_0;
2730 case 1: return SYM_ORIENT_90;
2731 case 2: return SYM_ORIENT_180;
2732 case 3: return SYM_ORIENT_270;
2733 case 4: return SYM_ORIENT_0 + SYM_MIRROR_Y;
2734 case 5: return SYM_ORIENT_90 + SYM_MIRROR_X;
2735 case 6: return SYM_ORIENT_0 + SYM_MIRROR_X;
2736 case 7: return SYM_ORIENT_270 + SYM_MIRROR_X;
2737 }
2738}
2739
2740
2742{
2743 using PART_KEY = std::pair<const void*, wxString>;
2744 std::map<PART_KEY, size_t> indices;
2745 std::vector<std::vector<const PLACED_PACKAGE_UNIT*>> parts;
2746
2747 for( const PLACED_PACKAGE_UNIT& unit : m_placedPackageUnits )
2748 {
2749 auto [entry, inserted] = indices.emplace( PART_KEY{ unit.scope, unit.reference }, parts.size() );
2750
2751 if( inserted )
2752 parts.emplace_back();
2753
2754 parts[entry->second].push_back( &unit );
2755 }
2756
2757 for( const auto& part : parts )
2758 {
2759 if( !m_nativePowerFamilies.count( std::get<0>( *part.front()->sourceUnit ) ) )
2760 continue;
2761
2762 const PLACED_PACKAGE_UNIT& first = *part.front();
2763 const std::string& templateName = m_preparedPkgToLib.at( *first.sourceUnit ).first;
2764 std::vector<UNIT_INFO> units = m_preparedLibUnits.at( templateName );
2765 std::set<size_t> placedUnits;
2766 std::vector<size_t> unitIndices;
2767 bool compatible = !first.reference.empty() && !first.reference.EndsWith( wxS( "?" ) );
2768
2769 for( const PLACED_PACKAGE_UNIT* placed : part )
2770 {
2771 const auto& source = m_preparedPkgToLib.at( *placed->sourceUnit );
2772 size_t index = static_cast<size_t>( source.second - 1 );
2773
2774 if( source.first != templateName || std::get<0>( *placed->sourceUnit ) != std::get<0>( *first.sourceUnit )
2775 || index >= units.size() || !placedUnits.insert( index ).second )
2776 {
2777 compatible = false;
2778 break;
2779 }
2780
2781 units[index] = placed->unit;
2782 unitIndices.push_back( index );
2783 }
2784
2785 std::set<std::string> letters;
2786
2787 for( const UNIT_INFO& unit : units )
2788 compatible &= letters.insert( unit.letter ).second;
2789
2790 if( !compatible )
2791 {
2792 warn( wxString::Format( _( "Native power units for '%s' have ambiguous package identity; "
2793 "their symbol definitions were kept separate." ), first.reference ) );
2794 continue;
2795 }
2796
2797 std::string libname = uniqueLibName( templateName,
2798 [&]( const LIB_ENTRY& aLib )
2799 {
2800 return aLib.units == units;
2801 } );
2802
2803 auto [entry, inserted] = m_libSymbols.try_emplace( libname );
2804
2805 if( inserted )
2806 {
2807 const LIB_ENTRY& source = m_libSymbols.at( templateName );
2808 entry->second.name = libname;
2809 entry->second.units = std::move( units );
2810 entry->second.refPrefix = source.refPrefix;
2811 entry->second.footprint = source.footprint;
2812 }
2813
2814 LIB_SYMBOL* definition = kicadSymbolFor( libname );
2815
2816 if( !definition )
2817 continue;
2818
2819 LIB_SYMBOL complete( *definition );
2820
2821 // Placement adjusts source text and graphics for the instance transform. Retain each
2822 // placed unit's adjusted drawings when assembling the complete package definition.
2823 for( size_t i = 0; i < part.size(); ++i )
2824 {
2825 SCH_SYMBOL* symbol = part[i]->symbol;
2826 int targetUnit = static_cast<int>( unitIndices[i] + 1 );
2827 std::vector<SCH_ITEM*> replaced;
2828
2829 for( SCH_ITEM& item : complete.GetDrawItems() )
2830 {
2831 if( item.GetUnit() == targetUnit && item.Type() != SCH_FIELD_T )
2832 replaced.push_back( &item );
2833 }
2834
2835 for( SCH_ITEM* item : replaced )
2836 complete.RemoveDrawItem( item );
2837
2838 for( const SCH_ITEM& item : symbol->GetLibSymbolRef()->GetDrawItems() )
2839 {
2840 if( item.GetUnit() == symbol->GetUnit() && item.Type() != SCH_FIELD_T )
2841 {
2842 SCH_ITEM* copy = static_cast<SCH_ITEM*>( item.Clone() );
2843 copy->SetUnit( targetUnit );
2844 complete.AddDrawItem( copy, false );
2845 }
2846 }
2847 }
2848
2849 complete.GetDrawItems().sort();
2850 LIB_ID id = libIdFor( libname );
2851
2852 // All units of one physical part must carry the same complete library definition on save.
2853 for( size_t i = 0; i < part.size(); ++i )
2854 {
2855 SCH_SYMBOL* symbol = part[i]->symbol;
2856 SCH_SCREEN* screen = static_cast<SCH_SCREEN*>( symbol->GetParent() );
2857 int unit = static_cast<int>( unitIndices[i] + 1 );
2858 screen->Remove( symbol );
2859 symbol->SetSchSymbolLibraryName( wxEmptyString );
2860 symbol->SetUnit( unit );
2861 symbol->SetUnitSelection( unit );
2862 symbol->SetLibId( id );
2863 symbol->SetLibSymbol( new LIB_SYMBOL( complete ) );
2864 screen->Append( symbol );
2865 }
2866 }
2867
2868 m_placedPackageUnits.clear();
2869}
2870
2871
2873 const SCH_SHEET_PATH& aSheetPath )
2874{
2875 const PKG_KEY* sourceUnit = nullptr;
2876 auto [libname, unit] = libForInstance( aInst, &sourceUnit );
2877
2878 LIB_ENTRY& entry = m_libSymbols.at( libname );
2879 const UNIT_INFO& uinfo = entry.units[unit - 1];
2880 const ORCAD_SYMBOL_DEF& def = *uinfo.symbol;
2881
2882 ORCAD_BBOX bb = def.bbox.value_or( ORCAD_BBOX() );
2883 int w = bb.x2 - bb.x1;
2884 int h = bb.y2 - bb.y1;
2885 int ori = OrcadOrientOf( aInst.rotation, aInst.mirror );
2886 VECTOR2I offset = OrcadOrientOffset( ori, w, h );
2887 VECTOR2I pos = OrcadDbuToIu( aInst.x + offset.x, aInst.y + offset.y );
2888
2889 // Transformed def hot points must land exactly on T0x10 absolute pin positions;
2890 // mismatch means wrong variant/orientation data.
2891 if( !aInst.pins.empty() && !symbolPinsMatch( def, aInst ) )
2892 {
2893 if( def.synthesized )
2894 {
2895 note( wxString::Format( _( "Page %s: %s uses a synthesized placeholder at a "
2896 "different orientation; pin layout approximate." ),
2897 FromOrcadString( aPage.name ), FromOrcadString( aInst.reference ) ) );
2898 }
2899 else
2900 {
2901 warn( wxString::Format( _( "Page %s: %s pin positions mismatch (orientation %d); "
2902 "geometry may be off." ),
2903 FromOrcadString( aPage.name ), FromOrcadString( aInst.reference ), ori ) );
2904 }
2905 }
2906
2907 LIB_SYMBOL* libSymbol = kicadSymbolFor( libname );
2908
2909 if( !libSymbol )
2910 return;
2911
2912 SCH_SYMBOL* symbol = instantiateSymbol( *libSymbol, libname, unit, ori, pos, aSheetPath );
2913
2914 TRANSFORM inverseTransform = symbol->GetTransform().InverseTransform();
2915 std::vector<std::pair<SCH_TEXT*, SCH_SHAPE*>> degreeMarks;
2916
2917 auto circleTouchesStroke =
2918 [&]( const SCH_SHAPE& aCircle )
2919 {
2920 int64_t radiusSquared = static_cast<int64_t>( aCircle.GetRadius() ) * aCircle.GetRadius();
2921
2922 for( const SCH_ITEM& item : symbol->GetLibSymbolRef()->GetDrawItems() )
2923 {
2924 if( item.Type() != SCH_SHAPE_T )
2925 continue;
2926
2927 const SCH_SHAPE& shape = static_cast<const SCH_SHAPE&>( item );
2928
2929 if( &shape == &aCircle || shape.GetShape() != SHAPE_T::POLY || shape.GetPolyShape().IsEmpty() )
2930 continue;
2931
2932 for( const VECTOR2I& point : shape.GetPolyShape().Outline( 0 ).CPoints() )
2933 {
2934 if( ( point - aCircle.GetPosition() ).SquaredEuclideanNorm() <= radiusSquared )
2935 return true;
2936 }
2937 }
2938
2939 return false;
2940 };
2941
2942 for( SCH_ITEM& item : symbol->GetLibSymbolRef()->GetDrawItems() )
2943 {
2944 if( item.Type() != SCH_TEXT_T )
2945 continue;
2946
2947 SCH_TEXT* text = static_cast<SCH_TEXT*>( &item );
2948
2949 if( text->GetLayer() == LAYER_PINNAM || text->GetLayer() == LAYER_PINNUM )
2950 continue;
2951
2952 for( SCH_ITEM& candidate : symbol->GetLibSymbolRef()->GetDrawItems() )
2953 {
2954 if( candidate.Type() != SCH_SHAPE_T )
2955 continue;
2956
2957 SCH_SHAPE* shape = static_cast<SCH_SHAPE*>( &candidate );
2958
2959 if( shape->GetShape() != SHAPE_T::CIRCLE || shape->GetRadius() > 2 * ORCAD_IU_PER_DBU
2960 || circleTouchesStroke( *shape ) )
2961 {
2962 continue;
2963 }
2964
2965 VECTOR2I delta = shape->GetPosition() - text->GetPosition();
2966
2967 if( std::abs( delta.x ) <= text->GetTextSize().x && std::abs( delta.y ) <= text->GetTextSize().y )
2968 degreeMarks.emplace_back( text, shape );
2969 }
2970 }
2971
2972 for( SCH_ITEM& item : symbol->GetLibSymbolRef()->GetDrawItems() )
2973 {
2974 if( item.Type() != SCH_TEXT_T )
2975 continue;
2976
2977 SCH_TEXT& text = static_cast<SCH_TEXT&>( item );
2978
2979 if( text.GetLayer() == LAYER_PINNAM || text.GetLayer() == LAYER_PINNUM )
2980 continue;
2981
2982 bool degreeCue = std::any_of( degreeMarks.begin(), degreeMarks.end(),
2983 [&]( const auto& aPair )
2984 {
2985 return aPair.first == &text;
2986 } );
2987 bool centeredBox = text.GetVertJustify() == GR_TEXT_V_ALIGN_CENTER && !degreeCue;
2988
2989 if( centeredBox )
2990 {
2991 VECTOR2I target = text.GetPosition();
2992 BOX2I glyphBox = text.GetEffectiveTextShape( false )->BBox();
2993 VECTOR2I correction = target - glyphBox.Centre();
2994
2995 if( text.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
2996 correction.x = 0;
2997
2998 text.SetPosition( text.GetPosition() + correction );
2999 continue;
3000 }
3001
3002 int baseline = OrcadTextBaselineOffset( text.GetTextSize().y );
3003 bool sourceVertical = text.GetTextAngle().IsVertical();
3004 VECTOR2I sourceCanvas( text.GetPosition().x, -text.GetPosition().y );
3005 VECTOR2I canvasOffset( offset.x * ORCAD_IU_PER_DBU, offset.y * ORCAD_IU_PER_DBU );
3006 VECTOR2I canvasPosition = sourceCanvas - canvasOffset + VECTOR2I( 0, baseline );
3007 VECTOR2I target = inverseTransform.TransformCoordinate( canvasPosition );
3008 text.SetPosition( target );
3009 bool symbolQuarterTurn = symbol->GetTransform().y1 != 0;
3010 text.SetTextAngle( sourceVertical != symbolQuarterTurn ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
3011 }
3012
3013 for( const auto& [text, degree] : degreeMarks )
3014 {
3015 BOX2I textBox = symbol->GetTransform().TransformCoordinate( text->GetBoundingBox() );
3016 int radius = degree->GetRadius();
3017 VECTOR2I degreePage( textBox.GetRight() + radius + ORCAD_IU_PER_DBU, textBox.Centre().y );
3018 VECTOR2I degreeLocal = inverseTransform.TransformCoordinate( degreePage );
3019 degree->SetPosition( degreeLocal );
3020 degree->SetEnd( degreeLocal + VECTOR2I( radius, 0 ) );
3021 }
3022
3023 std::vector<SCH_ITEM*> pinTexts;
3024
3025 for( SCH_ITEM& item : symbol->GetLibSymbolRef()->GetDrawItems() )
3026 {
3027 if( item.Type() == SCH_TEXT_T
3028 && ( item.GetLayer() == LAYER_PINNAM || item.GetLayer() == LAYER_PINNUM ) )
3029 {
3030 pinTexts.push_back( &item );
3031 }
3032 }
3033
3034 for( SCH_ITEM* item : pinTexts )
3035 item->SetLayer( LAYER_DEVICE );
3036
3037 symbol->GetLibSymbolRef()->GetDrawItems().sort();
3038
3039 std::map<std::string, std::string> props = effectiveProps( aInst, def );
3040
3041 auto occurrenceProperty = [&]( const char* aName ) -> const std::string*
3042 {
3043 if( !m_scope.occ )
3044 return nullptr;
3045
3046 auto occurrence = m_scope.occ->partProps.find( aInst.dbId );
3047
3048 if( occurrence == m_scope.occ->partProps.end() )
3049 return nullptr;
3050
3051 auto property = std::find_if( occurrence->second.begin(), occurrence->second.end(),
3052 [&]( const auto& aProperty )
3053 {
3054 return OrcadIEquals( aProperty.first, aName );
3055 } );
3056 return property != occurrence->second.end() ? &property->second : nullptr;
3057 };
3058
3059 const std::string* occurrenceValue = occurrenceProperty( "Value" );
3060 std::string value = occurrenceValue ? *occurrenceValue : aInst.value;
3061
3062 if( !occurrenceValue && value.empty() )
3063 {
3064 auto vIt = props.find( "Value" );
3065
3066 if( vIt != props.end() )
3067 value = vIt->second;
3068 }
3069
3070 if( !occurrenceValue && value.empty() )
3071 value = entry.name;
3072
3073 auto installed = std::find_if( props.begin(), props.end(),
3074 []( const auto& aProperty )
3075 {
3076 return isInstalledPropertyName( aProperty.first );
3077 } );
3078 bool notInstalled = installed != props.end() && OrcadIEquals( installed->second, "NI" );
3079
3080 const std::string* occurrenceFootprint = occurrenceProperty( "PCB Footprint" );
3081 std::string footprint = occurrenceFootprint ? *occurrenceFootprint : std::string();
3082 auto fIt = props.find( "PCB Footprint" );
3083
3084 if( !occurrenceFootprint && fIt != props.end() )
3085 footprint = fIt->second;
3086
3087 if( !occurrenceFootprint && footprint.empty() )
3088 footprint = entry.footprint;
3089
3090 wxString reference = resolveReference( aInst );
3091
3092 placeSymbolFields( symbol, aInst, def, ori, value, footprint );
3093
3094 if( !notInstalled )
3095 {
3096 for( const SCH_FIELD& field : symbol->GetFields() )
3097 {
3098 if( isInstalledPropertyName( field.GetName().ToStdString() )
3099 && field.GetText().CmpNoCase( wxS( "NI" ) ) == 0 )
3100 {
3101 notInstalled = true;
3102 break;
3103 }
3104 }
3105 }
3106
3107 if( notInstalled )
3108 {
3109 symbol->SetDNP( true );
3110
3111 SCH_FIELD* valueField = symbol->GetField( FIELD_T::VALUE );
3112
3113 if( valueField && valueField->IsVisible() )
3114 valueField->SetText( wxS( "NI" ) );
3115 }
3116
3117 symbol->SetRef( &aSheetPath, reference );
3118 symbol->SetUnitSelection( &aSheetPath, unit );
3119
3120 if( sourceUnit && m_preparedLibUnits.at( m_preparedPkgToLib.at( *sourceUnit ).first ).size() > 1 )
3121 {
3122 bool nativePower = std::any_of( aInst.pins.begin(), aInst.pins.end(),
3123 [&]( const ORCAD_PIN_INST& aPin )
3124 {
3125 return m_currentImplicitPowerPins.count( &aPin );
3126 } );
3127 const void* scope = m_scope.occ ? static_cast<const void*>( &m_scope.occ->partRefs ) : aScreen;
3128 m_placedPackageUnits.push_back( { symbol, scope, reference, sourceUnit, uinfo } );
3129
3130 if( nativePower )
3131 m_nativePowerFamilies.insert( std::get<0>( *sourceUnit ) );
3132 }
3133
3134 // Parts without pins remain in the BOM but must not request footprints during board updates.
3135 if( def.pins.empty() && aInst.pins.empty() )
3136 symbol->SetExcludedFromBoard( true );
3137
3138 applyCisVariants( symbol, aInst, aSheetPath, value );
3139
3140 m_sourceInstances[symbol] = &aInst;
3141 auto& sourcePins = m_sourcePinIdentities[symbol];
3142
3143 for( size_t index = 0; index < aInst.pins.size(); ++index )
3144 {
3145 SOURCE_PIN_IDENTITY identity;
3146 size_t pi = symbolPinIndex( def, aInst.pins[index], index );
3147 VECTOR2I rawPosition = placedPinElectricalPosition( aInst, index );
3148 identity.position = OrcadDbuToIu( rawPosition.x, rawPosition.y );
3149
3150 if( pi < def.pins.size() )
3151 {
3152 size_t position = def.pins[pi].position >= 0 ? def.pins[pi].position : pi;
3153 identity.ignored = position < uinfo.pinIgnore.size() && uinfo.pinIgnore[position];
3154 identity.number = position < uinfo.pinNumbers.size() ? FromOrcadString( uinfo.pinNumbers[position] )
3155 : wxString::Format( wxS( "%zu" ), pi + 1 );
3156 std::vector<SCH_PIN*> candidates;
3157
3158 for( SCH_PIN* pin : symbol->GetPins( &aSheetPath ) )
3159 {
3160 if( pin->GetNumber() == identity.number && pin->GetPosition() == identity.position )
3161 candidates.push_back( pin );
3162 }
3163
3164 if( candidates.size() == 1 && candidates.front()->GetLibPin() )
3165 identity.libraryPin = candidates.front()->GetLibPin()->m_Uuid;
3166 }
3167
3168 sourcePins.push_back( std::move( identity ) );
3169 }
3170
3171 appendPageItem( aScreen, symbol );
3172 placeDefinitionImages( def, aInst.x, aInst.y, ori, aScreen );
3173
3174 for( const ORCAD_PIN_INST& pin : aInst.pins )
3175 {
3176 if( pin.IsNoConnect() && !pin.wordA && !pin.wordB )
3177 appendPageItem( aScreen, new SCH_NO_CONNECT( OrcadDbuToIu( pin.x, pin.y ) ) );
3178 }
3179}
3180
3181
3183 const SCH_SHEET_PATH& aSheetPath, const std::string& aValue )
3184{
3185 if( m_design.cisVariants.empty() )
3186 return;
3187
3188 const ORCAD_OCC_SCOPE* scope = m_scope.occ;
3189
3190 if( !scope )
3191 return;
3192
3193 auto occurrence = scope->partOccurrenceIds.find( aInst.dbId );
3194
3195 if( occurrence == scope->partOccurrenceIds.end() )
3196 return;
3197
3198 for( const ORCAD_CIS_VARIANT& cis : m_design.cisVariants )
3199 {
3200 SCH_SYMBOL_VARIANT variant( FromOrcadString( cis.name ) );
3201 variant.InitializeAttributes( *aSymbol );
3202
3203 if( auto installed = cis.installed.find( occurrence->second ); installed != cis.installed.end() )
3204 variant.m_DNP = !installed->second;
3205
3206 // A variant that installs a part the core design leaves out shows its value, not the "NI" marker
3207 if( aSymbol->GetDNP() && !variant.m_DNP && aSymbol->GetField( FIELD_T::VALUE )->GetText() == wxS( "NI" ) )
3208 variant.m_Fields[aSymbol->GetField( FIELD_T::VALUE )->GetName()] = FromOrcadString( aValue );
3209
3210 if( auto props = cis.props.find( occurrence->second ); props != cis.props.end() )
3211 {
3212 for( const auto& [property, value] : props->second )
3213 {
3214 wxString fieldName = propertyFieldName( property );
3215
3216 // CIS database rows fill unused columns with this placeholder
3217 if( fieldName.IsEmpty() || value == "UNDEFINED" )
3218 continue;
3219
3220 wxString text = value == "<" + property + ">" ? wxString() : FromOrcadString( value );
3221 SCH_FIELD* field = aSymbol->FindFieldCaseInsensitive( fieldName );
3222
3223 // KiCad variants override existing fields, so a property only the variant sets gets an empty base
3224 if( !field )
3225 {
3226 SCH_FIELD added( aSymbol, FIELD_T::USER, fieldName );
3227 added.SetPosition( aSymbol->GetPosition() );
3228 added.SetTextSize( VECTOR2I( schMm( 1.27 ), schMm( 1.27 ) ) );
3229 added.SetVisible( false );
3230 field = aSymbol->AddField( added );
3231 }
3232
3233 if( field->GetText() != text )
3234 variant.m_Fields[field->GetName()] = text;
3235 }
3236 }
3237
3238 if( variant.HasDifferentials( *aSymbol ) )
3239 aSymbol->AddVariant( aSheetPath, variant );
3240 }
3241}
3242
3243
3245{
3246 wxString reference = FromOrcadString( aInst.reference );
3247
3248 // Hierarchy stream carries the authoritative per-occurrence designator; the
3249 // placed record remains the reusable page template's reference.
3250 if( m_scope.occ )
3251 {
3252 auto it = m_scope.occ->partRefs.find( aInst.dbId );
3253
3254 if( it != m_scope.occ->partRefs.end() )
3255 {
3256 wxString occurrence = FromOrcadString( it->second );
3257
3258 if( !occurrence.IsEmpty() && !occurrence.EndsWith( wxS( "?" ) ) )
3259 reference = occurrence;
3260 }
3261 }
3262
3263 if( reference.IsEmpty() )
3264 reference = wxS( "?" );
3265
3266 return reference;
3267}
3268
3269
3270void ORCAD_CONVERTER::placePowerSymbol( ORCAD_RAW_PAGE& aPage, const ORCAD_GRAPHIC_INST& aInst, const std::string& aNet,
3271 SCH_SCREEN* aScreen, const SCH_SHEET_PATH& aSheetPath )
3272{
3273 std::string net = canonicalGlobalNetName( aNet );
3274
3275 // Capture power names ignore case; occurrence-specific names must still remain distinct.
3276 if( !OrcadIEquals( net, aNet ) )
3277 net = aNet;
3278
3279 std::string libname = powerLibFor( aInst.name, net );
3280
3281 LIB_ENTRY& entry = m_libSymbols.at( libname );
3282 const ORCAD_SYMBOL_DEF* def = entry.units.front().symbol;
3283
3284 ORCAD_BBOX bb = ( def && def->bbox ) ? *def->bbox : ORCAD_BBOX{ 0, 0, 20, 10 };
3285 int w = bb.x2 - bb.x1;
3286 int h = bb.y2 - bb.y1;
3287 int ori = OrcadOrientOf( aInst.rotation, aInst.mirror );
3288 VECTOR2I offset = OrcadOrientOffset( ori, w, h );
3289
3290 bool hasBbox = aInst.bbox.x1 || aInst.bbox.y1 || aInst.bbox.x2 || aInst.bbox.y2;
3291 int bx = hasBbox ? std::min( aInst.bbox.x1, aInst.bbox.x2 ) : aInst.x;
3292 int by = hasBbox ? std::min( aInst.bbox.y1, aInst.bbox.y2 ) : aInst.y;
3293
3294 VECTOR2I pos = OrcadDbuToIu( bx + offset.x, by + offset.y );
3295
3296 m_powerCount++;
3297 wxString reference = wxString::Format( wxS( "#PWR%04d" ), m_powerCount );
3298
3299 LIB_SYMBOL* libSymbol = kicadSymbolFor( libname );
3300
3301 if( !libSymbol )
3302 return;
3303
3304 SCH_SYMBOL* symbol = instantiateSymbol( *libSymbol, libname, 1, ori, pos, aSheetPath );
3305
3306 std::vector<SCH_PIN*> pins = symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES );
3307 VECTOR2I sourcePinDbu = powerPinPos( aPage, aInst );
3308
3309 if( !pins.empty() )
3310 {
3311 VECTOR2I sourcePin = OrcadDbuToIu( sourcePinDbu.x, sourcePinDbu.y );
3312 symbol->SetPosition( symbol->GetPosition() + sourcePin - pins.front()->GetPosition() );
3313 pos = symbol->GetPosition();
3314 }
3315
3316 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
3317 refField->SetPosition( pos + VECTOR2I( 0, schMm( 2.54 ) ) );
3318 refField->SetTextAngle( ANGLE_HORIZONTAL );
3319 refField->SetVisible( false );
3320
3321 auto nameProperty = aInst.props.find( "Name" );
3322 std::string displayName = nameProperty != aInst.props.end() ? nameProperty->second : std::string();
3323 std::string logicalName = aInst.logicalName;
3324 symbol->SetValueFieldText( FromOrcadString( net ) );
3325
3326 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
3327 valField->SetPosition( pos );
3328 valField->SetVisible( false );
3329
3330 auto displayedName =
3331 std::find_if( aInst.displayProps.begin(), aInst.displayProps.end(),
3332 []( const ORCAD_DISPLAY_PROP& aProp )
3333 {
3334 return OrcadIEquals( aProp.name, "Name" ) || OrcadIEquals( aProp.name, "NODENAME" );
3335 } );
3336
3337 if( displayedName != aInst.displayProps.end() )
3338 {
3339 SCH_FIELD displayField( symbol, FIELD_T::USER, FromOrcadString( displayedName->name ) );
3340 displayField.SetText( FromOrcadString( !displayName.empty() ? displayName
3341 : !logicalName.empty() ? logicalName
3342 : aNet ) );
3343 applyDisplayProp( displayField, *displayedName, OrcadDbuToIu( bx + displayedName->x, by + displayedName->y ),
3344 symbol->GetTransform().y1 != 0 );
3345 symbol->AddField( displayField );
3346 }
3347
3348 SCH_FIELD* fpField = symbol->GetField( FIELD_T::FOOTPRINT );
3349 fpField->SetPosition( pos );
3350 fpField->SetVisible( false );
3351
3352 SCH_FIELD* dsField = symbol->GetField( FIELD_T::DATASHEET );
3353 dsField->SetPosition( pos );
3354 dsField->SetVisible( false );
3355
3356 symbol->SetRef( &aSheetPath, reference );
3357 symbol->SetUnitSelection( &aSheetPath, 1 );
3358
3359 appendPageItem( aScreen, symbol );
3360}
3361
3362
3364 const ORCAD_SYMBOL_DEF& aDef, int aOrient, const std::string& aValue,
3365 const std::string& aFootprint )
3366{
3367 // Placed bbox includes OrCAD displayed text; true body box = orientation
3368 // transform of cache def bbox.
3369 ORCAD_BBOX bb = aDef.bbox.value_or( ORCAD_BBOX() );
3370 int w = bb.x2 - bb.x1;
3371 int h = bb.y2 - bb.y1;
3372
3373 VECTOR2I c1 = OrcadTransformPoint( aOrient, w, h, aInst.x, aInst.y, bb.x1, bb.y1 );
3374 VECTOR2I c2 = OrcadTransformPoint( aOrient, w, h, aInst.x, aInst.y, bb.x2, bb.y2 );
3375
3376 int bxmin = std::min( c1.x, c2.x );
3377 int bxmax = std::max( c1.x, c2.x );
3378 int bymin = std::min( c1.y, c2.y );
3379 int bymax = std::max( c1.y, c2.y );
3380
3381 int cxm = ( bxmin + bxmax ) * ORCAD_IU_PER_DBU / 2;
3382 int cym = ( bymin + bymax ) * ORCAD_IU_PER_DBU / 2;
3383
3384 // Fields always read horizontally; KiCad rotates field text w/ symbol, so
3385 // 90/270 placement gets compensating field angle.
3386 int angle = ORCAD_ORIENT_TABLE[aOrient & 7].angle;
3387 bool vertical = angle == 90 || angle == 270;
3388 EDA_ANGLE fieldAngle = vertical ? ANGLE_VERTICAL : ANGLE_HORIZONTAL;
3389
3390 // Visibility follows source display mode; bit 0x100 is field visible flag.
3391 bool showRef = false;
3392 bool showVal = false;
3393 bool showRefName = false;
3394 bool showValName = false;
3395
3396 auto findReferenceDisplay = [&]( const char* aName, bool aVisibleOnly ) -> const ORCAD_DISPLAY_PROP*
3397 {
3398 auto it = std::find_if( aInst.displayProps.begin(), aInst.displayProps.end(),
3399 [&]( const ORCAD_DISPLAY_PROP& aDisplay )
3400 {
3401 return OrcadIEquals( aDisplay.name, aName )
3402 && ( !aVisibleOnly || OrcadDisplayPropVisible( aDisplay ) );
3403 } );
3404
3405 return it != aInst.displayProps.end() ? &*it : nullptr;
3406 };
3407
3408 const ORCAD_DISPLAY_PROP* referenceDisplay = findReferenceDisplay( "Part Reference", true );
3409
3410 if( !referenceDisplay )
3411 referenceDisplay = findReferenceDisplay( "Reference", true );
3412
3413 if( !referenceDisplay )
3414 referenceDisplay = findReferenceDisplay( "Part Reference", false );
3415
3416 if( !referenceDisplay )
3417 referenceDisplay = findReferenceDisplay( "Reference", false );
3418
3419 if( referenceDisplay )
3420 {
3421 showRef = OrcadDisplayPropVisible( *referenceDisplay );
3422 showRefName = OrcadDisplayPropShowsName( *referenceDisplay );
3423 }
3424
3425 for( const ORCAD_DISPLAY_PROP& dp : aInst.displayProps )
3426 {
3427 bool visible = OrcadDisplayPropVisible( dp );
3428
3429 if( OrcadIEquals( dp.name, "Value" ) )
3430 {
3431 showVal = visible;
3432 showValName = OrcadDisplayPropShowsName( dp );
3433 }
3434 }
3435
3436 aSymbol->SetValueFieldText( FromOrcadString( aValue ) );
3437
3438 SCH_FIELD* refField = aSymbol->GetField( FIELD_T::REFERENCE );
3439 SCH_FIELD* valField = aSymbol->GetField( FIELD_T::VALUE );
3440
3441 // Vertical two-pin passives stack Reference/Value right of body, centered;
3442 // everything else gets Reference above, Value below.
3443 bool side = !vertical && aDef.pins.size() == 2 && aInst.pins.size() == 2
3444 && std::abs( aInst.pins[0].y - aInst.pins[1].y ) >= std::abs( aInst.pins[0].x - aInst.pins[1].x );
3445
3446 if( side )
3447 {
3448 int sx = bxmax * ORCAD_IU_PER_DBU + schMm( 1.0 );
3449 int ry = showVal ? cym - schMm( 0.9 ) : cym;
3450
3451 refField->SetPosition( VECTOR2I( sx, ry ) );
3452 refField->SetTextAngle( ANGLE_HORIZONTAL );
3454 refField->SetVisible( showRef );
3455 refField->SetNameShown( showRefName );
3456
3457 valField->SetPosition( VECTOR2I( sx, cym + schMm( 0.9 ) ) );
3458 valField->SetTextAngle( ANGLE_HORIZONTAL );
3460 valField->SetVisible( showVal );
3461 valField->SetNameShown( showValName );
3462 }
3463 else
3464 {
3465 refField->SetPosition( VECTOR2I( cxm, bymin * ORCAD_IU_PER_DBU - schMm( 1.4 ) ) );
3466 refField->SetTextAngle( fieldAngle );
3468 refField->SetVisible( showRef );
3469 refField->SetNameShown( showRefName );
3470
3471 valField->SetPosition( VECTOR2I( cxm, bymax * ORCAD_IU_PER_DBU + schMm( 1.4 ) ) );
3472 valField->SetTextAngle( fieldAngle );
3474 valField->SetVisible( showVal );
3475 valField->SetNameShown( showValName );
3476 }
3477
3478 // Display-prop positions are canvas-space offsets from instance anchor; field
3479 // text does not rotate w/ symbol, so not run through body orientation transform.
3480 std::map<std::string, std::pair<VECTOR2I, const ORCAD_DISPLAY_PROP*>> shown;
3481
3482 for( const ORCAD_DISPLAY_PROP& dp : aInst.displayProps )
3483 {
3484 auto value = std::make_pair( OrcadDbuToIu( aInst.x + dp.x, aInst.y + dp.y ), &dp );
3485
3486 if( &dp == referenceDisplay )
3487 shown["Part Reference"] = value;
3488 else if( OrcadIEquals( dp.name, "Part Reference" ) || OrcadIEquals( dp.name, "Reference" ) )
3489 continue;
3490 else
3491 shown[dp.name] = value;
3492 }
3493
3494 auto findShown = [&]( const std::string& aName )
3495 {
3496 auto exact = shown.find( aName );
3497
3498 if( exact != shown.end() )
3499 return exact;
3500
3501 return std::find_if( shown.begin(), shown.end(),
3502 [&]( const auto& aItem )
3503 {
3504 return OrcadIEquals( aItem.first, aName );
3505 } );
3506 };
3507
3508 // KiCad re-rotates field text when parent transform flips X/Y (GetDrawRotation),
3509 // so store angle that renders property's own text angle after flip.
3510 bool symbolFlips = aSymbol->GetTransform().y1 != 0;
3511
3512 auto drawDisplayedField = [&]( SCH_FIELD* aField, const ORCAD_DISPLAY_PROP& aDisplay )
3513 {
3514 if( !aField->IsVisible() || !OrcadDisplayPropShowsName( aDisplay ) )
3515 return;
3516
3517 wxString content = FromOrcadString( aDisplay.name );
3518
3519 if( OrcadDisplayPropShowsValue( aDisplay ) )
3520 content += wxS( " = " ) + aField->GetText();
3521
3522 SCH_TEXT* text = new SCH_TEXT( aField->GetPosition(), content, LAYER_NOTES );
3523 int fontId = displayFontId( aDisplay );
3524 bool templateFont = displayUsesTemplateFont( aDisplay );
3525 text->SetTextSize( aField->GetTextSize() );
3526 applyFont( text, fontId, templateFont );
3527 text->SetTextColor( OrcadColor( aDisplay.color ) );
3528 text->SetTextAngle( ( aDisplay.rotation & 1 ) ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
3529 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
3530 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
3532 aField->SetVisible( false );
3533 aField->SetNameShown( false );
3534 };
3535
3536 // Keep the source footprint as metadata. An empty Footprint field permits relinking by reference.
3537 aSymbol->SetFootprintFieldText( wxEmptyString );
3538
3539 wxString fpName = FromOrcadString( aFootprint );
3540
3541 if( !fpName.IsEmpty() )
3542 {
3543 SCH_FIELD fpMeta( aSymbol, FIELD_T::USER, wxS( "OrCAD Footprint" ) );
3544 fpMeta.SetText( fpName );
3545
3546 auto shownFootprint = findShown( "PCB Footprint" );
3547
3548 if( shownFootprint != shown.end() )
3549 {
3550 const ORCAD_DISPLAY_PROP& dp = *shownFootprint->second.second;
3551
3552 applyDisplayProp( fpMeta, dp, shownFootprint->second.first, symbolFlips );
3553 drawDisplayedField( &fpMeta, dp );
3554 }
3555 else
3556 {
3557 fpMeta.SetPosition( aSymbol->GetPosition() );
3558 fpMeta.SetVisible( false );
3559 }
3560
3561 aSymbol->AddField( fpMeta );
3562 }
3563
3564 SCH_FIELD* fpField = aSymbol->GetField( FIELD_T::FOOTPRINT );
3565 fpField->SetPosition( aSymbol->GetPosition() );
3566 fpField->SetVisible( false );
3567
3568 SCH_FIELD* dsField = aSymbol->GetField( FIELD_T::DATASHEET );
3569 dsField->SetPosition( aSymbol->GetPosition() );
3570 dsField->SetVisible( false );
3571
3572 // OrCAD carries explicit reference/value field positions; honor them so fields
3573 // land where source placed them, not computed fallback.
3574 auto applyDisplayPos = [&]( SCH_FIELD* aField, const char* aName )
3575 {
3576 auto it = findShown( aName );
3577
3578 if( it == shown.end() )
3579 return;
3580
3581 applyDisplayProp( *aField, *it->second.second, it->second.first, symbolFlips, false );
3582 };
3583
3584 applyDisplayPos( refField, "Part Reference" );
3585 applyDisplayPos( valField, "Value" );
3586
3587 if( auto shownValue = findShown( "Value" ); shownValue != shown.end() )
3588 drawDisplayedField( valField, *shownValue->second.second );
3589
3590 std::map<std::string, std::string> properties = effectiveProps( aInst, aDef );
3591
3592 auto partReference = std::find_if( properties.begin(), properties.end(),
3593 []( const auto& aProperty )
3594 {
3595 return OrcadIEquals( aProperty.first, "Part Reference" );
3596 } );
3597
3598 const wxString resolvedReference = resolveReference( aInst );
3599 const bool occurrenceOverridesReference = resolvedReference != FromOrcadString( aInst.reference );
3600 wxString displayedText;
3601
3602 if( partReference != properties.end() && !occurrenceOverridesReference )
3603 {
3604 displayedText = FromOrcadString( partReference->second );
3605 }
3606 else
3607 {
3608 std::string unitDesignator;
3609
3610 if( m_scope.occ )
3611 {
3612 auto unitRef = m_scope.occ->partUnitRefs.find( aInst.dbId );
3613
3614 if( unitRef != m_scope.occ->partUnitRefs.end() )
3615 unitDesignator = unitRef->second;
3616 }
3617
3618 const ORCAD_PACKAGE* package = packageFor( aInst );
3619
3620 if( unitDesignator.empty() && package && aInst.unitIndex < package->devices.size() )
3621 unitDesignator = package->devices[aInst.unitIndex].unitRef;
3622
3623 if( unitDesignator.empty() && !package )
3624 unitDesignator = unitLetter( aInst );
3625
3626 if( unitDesignator.empty() && package && package->devices.size() > 1 )
3627 unitDesignator = unitLetter( aInst );
3628
3629 unitDesignator = unitDesignator.substr( 0, unitDesignator.find( ':' ) );
3630
3631 if( !unitDesignator.empty() && unitDesignator.front() != '#' )
3632 displayedText = resolvedReference + FromOrcadString( unitDesignator );
3633 }
3634
3635 if( showRef )
3636 {
3637 if( !displayedText.IsEmpty() && displayedText != resolvedReference )
3638 {
3639 refField->SetVisible( false );
3640
3641 SCH_FIELD displayedReference( aSymbol, FIELD_T::USER, wxS( "Part Reference" ) );
3642 displayedReference.SetText( displayedText );
3643 displayedReference.SetVisible( true );
3644 displayedReference.SetNameShown( showRefName );
3645 applyDisplayPos( &displayedReference, "Part Reference" );
3646 aSymbol->AddField( displayedReference );
3647 }
3648 }
3649
3650 if( findShown( "Implementation" ) != shown.end() && !aInst.value.empty() )
3651 {
3652 auto implementation = std::find_if( properties.begin(), properties.end(),
3653 []( const auto& aProperty )
3654 {
3655 return OrcadIEquals( aProperty.first, "Implementation" );
3656 } );
3657
3658 if( implementation == properties.end() )
3659 properties["Implementation"] = aInst.value;
3660 else if( implementation->second.empty() )
3661 implementation->second = aInst.value;
3662 }
3663
3664 static const std::pair<const char*, FIELD_T> standardFields[] = { { "Description", FIELD_T::DESCRIPTION },
3665 { "Datasheet", FIELD_T::DATASHEET } };
3666
3667 for( const auto& [propName, propValue] : properties )
3668 {
3669 if( OrcadIEquals( propName, "Value" ) || OrcadIEquals( propName, "PCB Footprint" )
3670 || OrcadIEquals( propName, "Part Reference" ) || OrcadIEquals( propName, "Reference" ) )
3671 continue;
3672
3673 if( isBookkeepingProp( propName ) && findShown( propName ) == shown.end() )
3674 continue;
3675
3676 bool unset = propValue.empty() || propValue == "<" + propName + ">";
3677 auto standardField = std::find_if( std::begin( standardFields ), std::end( standardFields ),
3678 [&]( const auto& aStandard )
3679 {
3680 return OrcadIEquals( propName, aStandard.first );
3681 } );
3682
3683 if( unset )
3684 {
3685 if( standardField != std::end( standardFields ) )
3686 aSymbol->GetField( standardField->second )->SetText( wxString() );
3687 else
3688 {
3689 std::vector<SCH_FIELD*> fields;
3690 aSymbol->GetFields( fields, false );
3691
3692 for( SCH_FIELD* field : fields )
3693 {
3694 if( field->GetId() >= FIELD_T::USER && OrcadIEquals( field->GetName().ToStdString(), propName ) )
3695 {
3696 aSymbol->RemoveField( field );
3697 break;
3698 }
3699 }
3700 }
3701
3702 continue;
3703 }
3704
3705 if( standardField != std::end( standardFields ) )
3706 {
3707 SCH_FIELD* standard = aSymbol->GetField( standardField->second );
3708 standard->SetText( FromOrcadString( propValue ) );
3709
3710 if( auto shownStandard = findShown( standardField->first ); shownStandard != shown.end() )
3711 {
3712 const ORCAD_DISPLAY_PROP& dp = *shownStandard->second.second;
3713 applyDisplayProp( *standard, dp, shownStandard->second.first, symbolFlips );
3714 drawDisplayedField( standard, dp );
3715 }
3716
3717 continue;
3718 }
3719
3720 wxString fieldName = OrcadIEquals( propName, "Footprint" ) ? wxString( "OrCAD Footprint Property" )
3721 : FromOrcadString( propName );
3722 SCH_FIELD field( aSymbol, FIELD_T::USER, fieldName );
3723 field.SetText( FromOrcadString( propValue ) );
3724
3725 auto sIt = findShown( propName );
3726
3727 if( sIt != shown.end() )
3728 {
3729 const ORCAD_DISPLAY_PROP& dp = *sIt->second.second;
3730
3731 applyDisplayProp( field, dp, sIt->second.first, symbolFlips );
3732 drawDisplayedField( &field, dp );
3733 }
3734 else
3735 {
3736 field.SetPosition( aSymbol->GetPosition() );
3737 field.SetTextSize( VECTOR2I( schMm( 1.27 ), schMm( 1.27 ) ) );
3738 field.SetVisible( false );
3739 }
3740
3741 aSymbol->AddField( field );
3742 }
3743}
3744
3745
3747{
3748 std::map<int, int> counts;
3749 std::vector<int> order;
3750
3751 auto tally = [&]( int aFontIdx, bool aTemplateFont = true )
3752 {
3753 int height = fontHeightDbu( aFontIdx, aTemplateFont );
3754
3755 if( !height )
3756 return;
3757
3758 auto [it, isNew] = counts.try_emplace( height, 0 );
3759
3760 if( isNew )
3761 order.push_back( height );
3762
3763 it->second++;
3764 };
3765
3766 for( const ORCAD_RAW_PAGE& page : m_design.pages )
3767 {
3768 for( const ORCAD_WIRE& wire : page.wires )
3769 {
3770 for( const ORCAD_ALIAS& alias : wire.aliases )
3771 tally( wireAliasFontId( alias ) );
3772 }
3773
3774 for( const ORCAD_PLACED_INSTANCE& inst : page.instances )
3775 {
3776 for( const ORCAD_DISPLAY_PROP& dp : inst.displayProps )
3777 tally( displayFontId( dp ), displayUsesTemplateFont( dp ) );
3778 }
3779 }
3780
3781 int best = 0;
3782 int bestCount = 0;
3783
3784 for( int height : order )
3785 {
3786 if( counts[height] > bestCount )
3787 {
3788 best = height;
3789 bestCount = counts[height];
3790 }
3791 }
3792
3793 m_fontBaselineDbu = best;
3794}
3795
3796
3798{
3799 return m_design.library.templateFonts.empty() ? resolveFontIndex( aProp.fontIdx ) : OrcadDisplayFontId( aProp );
3800}
3801
3802
3804{
3805 return aProp.fontIdx <= 0 && !m_design.library.templateFonts.empty();
3806}
3807
3808
3810{
3811 int fontId = displayFontId( aDisplay );
3812 bool templateFont = displayUsesTemplateFont( aDisplay );
3813 int baseline = textBaselineOffset( textSizeIU( fontId, templateFont ), fontId, templateFont );
3814
3815 return aAnchorIu + ( ( aDisplay.rotation & 1 ) ? VECTOR2I( baseline, 0 ) : VECTOR2I( 0, baseline ) );
3816}
3817
3818
3820 const VECTOR2I& aAnchorIu, bool aSymbolFlips, bool aApplyVisibility ) const
3821{
3822 int fontId = displayFontId( aDisplay );
3823 bool templateFont = displayUsesTemplateFont( aDisplay );
3824 bool vertical = ( aDisplay.rotation & 1 ) != 0;
3825
3826 aField.SetPosition( displayPropPosition( aDisplay, aAnchorIu ) );
3827 aField.SetTextAngle( vertical != aSymbolFlips ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
3828 aField.SetTextSize( textSize( fontId, templateFont ) );
3829 applyFont( &aField, fontId, templateFont );
3830 aField.SetTextColor( OrcadColor( aDisplay.color ) );
3831
3832 // Effective justification reads the rendered box, so it must follow every geometry setter
3835
3836 if( aApplyVisibility )
3837 {
3838 aField.SetVisible( OrcadDisplayPropVisible( aDisplay ) );
3839 aField.SetNameShown( OrcadDisplayPropShowsName( aDisplay ) );
3840 }
3841}
3842
3843
3845{
3846 if( !m_design.library.templateFonts.empty() )
3847 {
3848 if( aAlias.fontIdx <= 0 || aAlias.fontIdx >= static_cast<int>( m_design.library.templateFonts.size() ) )
3849 return 5;
3850 }
3851
3852 return aAlias.fontIdx;
3853}
3854
3855
3856int ORCAD_CONVERTER::textBaselineOffset( int aTextSize, int aFontIdx, bool aTemplateFont ) const
3857{
3858 int resolved = aTemplateFont ? resolveFontIndex( aFontIdx ) : aFontIdx;
3859
3860 if( resolved > 0 && resolved <= static_cast<int>( m_design.library.fonts.size() ) )
3861 {
3862 std::string face = OrcadLower( m_design.library.fonts[resolved - 1].face );
3863
3864 if( face == "arial narrow" )
3865 return KiROUND( aTextSize * ( m_design.library.fonts[resolved - 1].bold ? 0.62 : 0.47 ) );
3866
3867 if( face == "verdana" )
3868 return KiROUND( aTextSize * 0.675 );
3869 }
3870
3871 return OrcadTextBaselineOffset( aTextSize );
3872}
3873
3874
3875int ORCAD_CONVERTER::resolveFontIndex( int aFontIdx ) const
3876{
3877 const std::vector<int>& templateFonts = m_design.library.templateFonts;
3878
3879 if( !templateFonts.empty() && aFontIdx >= 0 && aFontIdx < static_cast<int>( templateFonts.size() ) )
3880 {
3881 int mapped = templateFonts[aFontIdx];
3882
3883 if( mapped > 0 )
3884 return mapped;
3885
3886 if( templateFonts.front() > 0 )
3887 return templateFonts.front();
3888 }
3889
3890 if( aFontIdx == 0 && !m_design.library.fonts.empty() )
3891 return 1;
3892
3893 return aFontIdx;
3894}
3895
3896
3897int ORCAD_CONVERTER::fontHeightDbu( int aFontIdx, bool aTemplateFont ) const
3898{
3899 if( aTemplateFont )
3900 aFontIdx = resolveFontIndex( aFontIdx );
3901
3902 if( aFontIdx > 0 && aFontIdx <= (int) m_design.library.fonts.size() )
3903 return std::abs( m_design.library.fonts[aFontIdx - 1].height );
3904
3905 return 0;
3906}
3907
3908
3909int ORCAD_CONVERTER::textSizeIU( int aFontIdx, bool aTemplateFont ) const
3910{
3911 int height = fontHeightDbu( aFontIdx, aTemplateFont );
3912
3913 if( !height )
3914 return schMm( 1.27 );
3915
3916 double compensation = 1.4;
3917 int resolved = aTemplateFont ? resolveFontIndex( aFontIdx ) : aFontIdx;
3918
3919 if( resolved > 0 && resolved <= static_cast<int>( m_design.library.fonts.size() ) )
3920 {
3921 std::string face = OrcadLower( m_design.library.fonts[resolved - 1].face );
3922
3923 if( face == "arial narrow" )
3924 compensation = m_design.library.fonts[resolved - 1].bold ? 1.6 : 1.46;
3925 else if( face == "verdana" )
3926 compensation = 1.46;
3927 }
3928
3929 double mm = std::round( height * 25.4 / ( 96.0 * compensation ) * 100.0 ) / 100.0;
3930
3931 return schMm( mm );
3932}
3933
3934
3935VECTOR2I ORCAD_CONVERTER::textSize( int aFontIdx, bool aTemplateFont ) const
3936{
3937 int height = textSizeIU( aFontIdx, aTemplateFont );
3938
3939 if( aTemplateFont )
3940 aFontIdx = resolveFontIndex( aFontIdx );
3941
3942 if( aFontIdx <= 0 || aFontIdx > static_cast<int>( m_design.library.fonts.size() ) )
3943 return VECTOR2I( height, height );
3944
3945 const ORCAD_FONT& font = m_design.library.fonts[aFontIdx - 1];
3946
3947 constexpr uint8_t c_FIXED_PITCH = 1;
3948
3949 if( font.width == 0 )
3950 return VECTOR2I( height, height );
3951
3952 if( ( font.pitchAndFamily & 0x3 ) != c_FIXED_PITCH )
3953 {
3954 std::string face = OrcadLower( font.face );
3955 // lfWidth is an average character width; KiCad's X size is an em scale.
3956 double averageEmRatio = 0.5;
3957
3958 if( face == "arial narrow" )
3959 {
3960 // Capture's GDI metrics use different average advances for regular and bold faces.
3961 averageEmRatio = font.bold ? 803.0 / 2048.0 : 0.358;
3962 }
3963
3964 double aspect = std::abs( font.width ) / ( std::abs( font.height ) * averageEmRatio );
3965 double mm = std::round( schIUScale.IUTomm( height ) * aspect * 100.0 ) / 100.0;
3966 return VECTOR2I( schMm( mm ), height );
3967 }
3968
3969 double mm = std::round( std::abs( font.width ) * 25.4 * 0.9 / 96.0 * 100.0 ) / 100.0;
3970
3971 return VECTOR2I( schMm( mm ), height );
3972}
3973
3974
3975void ORCAD_CONVERTER::applyFont( EDA_TEXT* aText, int aFontIdx, bool aTemplateFont ) const
3976{
3977 if( aTemplateFont )
3978 aFontIdx = resolveFontIndex( aFontIdx );
3979
3980 if( !aText || aFontIdx <= 0 || aFontIdx > static_cast<int>( m_design.library.fonts.size() ) )
3981 return;
3982
3983 const ORCAD_FONT& font = m_design.library.fonts[aFontIdx - 1];
3984 aText->SetBold( font.bold );
3985 aText->SetItalic( font.italic );
3986
3987 if( !font.face.empty() )
3988 {
3989 wxString face = FromOrcadString( font.face );
3990 const std::vector<wxString>* embeddedFonts = nullptr;
3991
3992 if( face.CmpNoCase( wxS( "Elephant" ) ) == 0 )
3993 {
3994 face = wxS( "KiCad OrCAD Elephant" );
3995 wxFileName fontFile( OleLibWmfFontDirectory()
3996 + wxS( "/KiCadOrCADElephant-Black.ttf" ) );
3997 EMBEDDED_FILES* files = m_schematic->GetEmbeddedFiles();
3998
3999 if( files->AddFile( fontFile, false ) )
4000 {
4001 files->SetAreFontsEmbedded( true );
4002 embeddedFonts = files->UpdateFontFiles();
4003 }
4004 }
4005
4006 aText->SetFont( KIFONT::FONT::GetFont( face, font.bold, font.italic, embeddedFonts ) );
4007 }
4008}
4009
4010
4011void ORCAD_CONVERTER::applyMultilineSpacing( SCH_TEXT* aText, int aFontIdx, bool aTemplateFont ) const
4012{
4013 if( !aText || aText->GetText().Find( '\n' ) == wxNOT_FOUND )
4014 return;
4015
4016 int sourceHeight = fontHeightDbu( aFontIdx, aTemplateFont );
4017
4018 if( sourceHeight <= 0 )
4019 return;
4020
4021 KIFONT::FONT* font = aText->GetDrawFont( nullptr );
4022 int sourceInterline = schMm( sourceHeight * 25.4 / 96.0 );
4023 int kicadInterline = KiROUND( font->GetInterline( aText->GetTextSize().y, aText->GetFontMetrics() ) );
4024
4025 if( kicadInterline > 0 )
4026 aText->SetLineSpacing( static_cast<double>( sourceInterline ) / kicadInterline );
4027}
4028
4029
4030std::string ORCAD_CONVERTER::SymbolId( const std::string& aName )
4031{
4032 std::string out;
4033 bool inRun = false;
4034
4035 for( unsigned char c : aName )
4036 {
4037 // 0xA0 = CP-1252 non-breaking space.
4038 bool separator = c == ':' || c == '"' || c == '/' || c == ' ' || c == '\t' || c == '\n' || c == '\r'
4039 || c == '\f' || c == '\v' || c == 0xA0;
4040
4041 if( separator )
4042 {
4043 if( !inRun )
4044 out += '_';
4045
4046 inRun = true;
4047 }
4048 else
4049 {
4050 out += (char) c;
4051 inRun = false;
4052 }
4053 }
4054
4055 size_t first = out.find_first_not_of( '_' );
4056
4057 if( first == std::string::npos )
4058 return "SYM";
4059
4060 size_t last = out.find_last_not_of( '_' );
4061 out = out.substr( first, last - first + 1 );
4062
4063 return out.empty() ? "SYM" : out;
4064}
int index
const char * name
@ ERROR_INSIDE
static std::optional< VECTOR2D > normalized(const VECTOR2D &aVec)
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
EDA_ITEM * GetParent() const
Definition eda_item.h:112
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:329
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false, bool includeFill=false) const
Convert the shape to a closed polygon.
SHAPE_POLY_SET & GetPolyShape()
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:175
virtual void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:367
virtual void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:364
void SetFillMode(FILL_T aFill)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual VECTOR2I GetTextSize() const
Definition eda_text.h:301
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:309
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual bool IsVisible() const
Definition eda_text.h:226
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
void SetLineSpacing(double aLineSpacing)
Definition eda_text.cpp:504
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:310
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:268
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:471
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
const std::vector< wxString > * UpdateFontFiles()
Helper function to get a list of fonts for fontconfig to add to the library.
EMBEDDED_FILE * AddFile(const wxFileName &aName, bool aOverwrite)
Load a file from disk and adds it to the collection.
void SetAreFontsEmbedded(bool aEmbedFonts)
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
virtual double GetInterline(double aGlyphHeight, const METRICS &aFontMetrics) const =0
Compute the distance (interline) between 2 lines of text (for multiline texts).
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:205
Define a library symbol object.
Definition lib_symbol.h:114
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:843
void RemoveDrawItem(SCH_ITEM *aItem)
Remove draw aItem from list.
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
void placePowerSymbol(ORCAD_RAW_PAGE &aPage, const ORCAD_GRAPHIC_INST &aInst, const std::string &aNet, SCH_SCREEN *aScreen, const SCH_SHEET_PATH &aSheetPath)
std::string uniqueLibName(const std::string &aBase, const std::function< bool(const LIB_ENTRY &)> &aReusable) const
aBase, or aBase_pinsN for the first N >= 2 whose existing entry is absent or passes aReusable.
bool displayUsesTemplateFont(const ORCAD_DISPLAY_PROP &aProp) const
int displayFontId(const ORCAD_DISPLAY_PROP &aProp) const
LIB_ENTRY buildPackageEntry(const ORCAD_PACKAGE &aPackage) const
One unit per package device; a Convert view becomes the unit's DeMorgan body.
std::map< SCH_SYMBOL *, std::vector< SOURCE_PIN_IDENTITY > > m_sourcePinIdentities
void forEachDesignPage(const std::function< void(const ORCAD_RAW_PAGE &)> &aVisit, bool aUnreferenced=false) const
Visit root pages, then child-folder pages, then unreferenced folder pages if asked.
static constexpr int PIN_LEN_DBU
– constants (calibrated; do not change) -------------------------------------—
std::pair< const ORCAD_SYMBOL_DEF *, int > pickVariant(const ORCAD_PLACED_INSTANCE &aInst) const
Match cached variants to placed pin positions; use the first entry if none matches.
std::map< SCH_SYMBOL *, const ORCAD_PLACED_INSTANCE * > m_sourceInstances
const ORCAD_PACKAGE * packageFor(const ORCAD_PLACED_INSTANCE &aInst) const
The cache package backing a placement, or nullptr when the design has none.
std::map< std::string, std::vector< UNIT_INFO > > m_preparedLibUnits
VECTOR2I displayPropPosition(const ORCAD_DISPLAY_PROP &aDisplay, const VECTOR2I &aAnchorIu) const
Capture's display-property origin sits on the font baseline; return the KiCad text origin.
int resolveFontIndex(int aFontIdx) const
Resolve a Design Template font ID to its 1-based LOGFONT index.
void applyCisVariants(SCH_SYMBOL *aSymbol, const ORCAD_PLACED_INSTANCE &aInst, const SCH_SHEET_PATH &aSheetPath, const std::string &aValue)
Record each CIS variant's installed state and property overrides as KiCad symbol variants.
LIB_SYMBOL * kicadSymbolFor(const std::string &aLibName)
std::map< PKG_KEY, std::pair< std::string, int > > m_pkgToLib
void appendPageItem(SCH_SCREEN *aScreen, SCH_ITEM *aItem)
void note(const wxString &aMsg)
Fact about the source design (not a conversion problem): RPT_SEVERITY_INFO.
void placeInstance(ORCAD_RAW_PAGE &aPage, const ORCAD_PLACED_INSTANCE &aInst, SCH_SCREEN *aScreen, const SCH_SHEET_PATH &aSheetPath)
ORCAD_DESIGN & m_design
int textBaselineOffset(int aTextSize, int aFontIdx, bool aTemplateFont=true) const
int m_fontBaselineDbu
dominant text height; 0 = none
void placeSymbolFields(SCH_SYMBOL *aSymbol, const ORCAD_PLACED_INSTANCE &aInst, const ORCAD_SYMBOL_DEF &aDef, int aOrient, const std::string &aValue, const std::string &aFootprint)
void addSymbolArc(LIB_SYMBOL *aSymbol, const ORCAD_PRIMITIVE &aPrim, int aUnit, int aColor, int aOffsetX=0, int aOffsetY=0)
Polyline approximation of an arc primitive.
SCH_SCREEN * m_pageItemScreen
std::map< std::string, LIB_ENTRY > m_libSymbols
keyed by emitted lib name
static int toKicadOrientation(int aOrient)
int m_powerCount
"#PWR%04d" counter
VECTOR2I powerPinPos(const ORCAD_RAW_PAGE &aPage, const ORCAD_GRAPHIC_INST &aInst) const
Power-symbol connection point, corrected to a nearby endpoint of its named wire.
SCH_SYMBOL * instantiateSymbol(const LIB_SYMBOL &aLibSymbol, const std::string &aLibName, int aUnit, int aOrient, const VECTOR2I &aPos, const SCH_SHEET_PATH &aSheetPath) const
New placement of a library symbol, oriented by an OrCAD orientation code.
void placeDefinitionImages(const ORCAD_SYMBOL_DEF &aDefinition, int aBaseX, int aBaseY, int aOrient, SCH_SCREEN *aScreen)
wxString resolveReference(const ORCAD_PLACED_INSTANCE &aInst) const
Use the occurrence reference when the instance has an unannotated template.
void applyFont(EDA_TEXT *aText, int aFontIdx, bool aTemplateFont=true) const
std::pair< std::string, int > libForInstance(const ORCAD_PLACED_INSTANCE &aInst, const PKG_KEY **aSourceUnit=nullptr)
Returns the registered library name and unit number.
VECTOR2I textSize(int aFontIdx, bool aTemplateFont=true) const
Preserve an explicit LOGFONT width while retaining natural font aspect when it is zero.
std::string unitLetter(const ORCAD_PLACED_INSTANCE &aInst) const
The unit key includes a non-Normal view so DeMorgan graphics remain distinct.
static std::string SymbolId(const std::string &aName)
LIB_ID-safe symbol name: ':', '"', '/' and whitespace runs -> '_'; "SYM" if empty.
bool hasImplicitPowerPinName(const ORCAD_PLACED_INSTANCE &aInstance, size_t aPinIndex, const std::string &aNetName) const
std::map< PKG_KEY, std::pair< std::string, int > > m_preparedPkgToLib
void addSymbolPrimitive(LIB_SYMBOL *aSymbol, const ORCAD_PRIMITIVE &aPrim, int aUnit, int aColor, int aOffsetX=0, int aOffsetY=0)
std::vector< PLACED_PACKAGE_UNIT > m_placedPackageUnits
ORCAD_SYMBOL_DEF synthesizeSymbol(const std::string &aPkgName, const std::vector< const ORCAD_PLACED_INSTANCE * > &aInstances) const
Use placed pin positions to keep uncached symbols connected.
std::tuple< std::string, std::string, int, std::string > PKG_KEY
(sourcePackage-or-pkgName, pkgName, variant index, unit discriminator) -> (lib name,...
SCHEMATIC * m_schematic
std::string powerLibFor(const std::string &aSymbolName, const std::string &aNetName)
Key power symbols by net name because users can rename their ports.
int textSizeIU(int aFontIdx, bool aTemplateFont=true) const
void warn(const wxString &aMsg)
– reporting [orcad_converter_sheet.cpp] ---------------------------------------—
std::string canonicalGlobalNetName(const std::string &aName) const
Return the design-wide spelling selected by prepareGlobalNetNames().
void applyMultilineSpacing(SCH_TEXT *aText, int aFontIdx, bool aTemplateFont=true) const
std::vector< LIB_SYMBOL * > BuildSymbolLibrary()
The caller owns the returned library symbols.
static constexpr const char * LIB_NICK
Symbol library nickname used in every emitted LIB_ID.
int wireAliasFontId(const ORCAD_ALIAS &aAlias) const
void addSymbolPin(LIB_SYMBOL *aSymbol, const ORCAD_SYMBOL_PIN &aPin, const BOX2I &aBodyBox, PIN_EMIT aEmit)
VECTOR2I placedPinElectricalPosition(const ORCAD_PLACED_INSTANCE &aInstance, size_t aPinIndex) const
std::set< std::string > m_nativePowerFamilies
void applyDisplayProp(SCH_FIELD &aField, const ORCAD_DISPLAY_PROP &aDisplay, const VECTOR2I &aAnchorIu, bool aSymbolFlips, bool aApplyVisibility=true) const
Style a field from a display property.
int fontHeightDbu(int aFontIdx, bool aTemplateFont=true) const
|lfHeight| of a Design Template font ID; ID 0 selects the template default.
std::map< std::string, std::string > effectiveProps(const ORCAD_PLACED_INSTANCE &aInst, const ORCAD_SYMBOL_DEF &aDef) const
Empty package and placement values must not clear a library property.
VECTOR2I GetPosition() const override
void SetEffectiveHorizJustify(GR_TEXT_H_ALIGN_T)
void SetEffectiveVertJustify(GR_TEXT_V_ALIGN_T)
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
wxString GetName(bool aUseDefaultName=true) const
Return the field name (not translated).
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
Definition sch_field.h:230
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
int GetUnit() const
Definition sch_item.h:243
virtual void SetUnit(int aUnit)
Definition sch_item.h:242
const KIFONT::METRICS & GetFontMetrics() const
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
bool Remove(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Remove aItem from the schematic associated with this screen.
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:87
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
void AddPoint(const VECTOR2I &aPosition)
VECTOR2I GetPosition() const override
Definition sch_shape.h:86
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Variant information for a schematic symbol.
void InitializeAttributes(const SCH_SYMBOL &aSymbol)
bool HasDifferentials(const SCH_SYMBOL &aSymbol) const
Return true if the variant carries any differential against the base symbol values,...
Schematic symbol object.
Definition sch_symbol.h:73
void SetLibId(const LIB_ID &aName)
virtual void SetDNP(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
void SetPosition(const VECTOR2I &aPosition) override
Definition sch_symbol.h:896
void RemoveField(const wxString &aFieldName)
Remove a user field from the symbol.
std::vector< const SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet) const
Retrieve a list of the SCH_PINs for the given sheet path.
SCH_FIELD * FindFieldCaseInsensitive(const wxString &aFieldName)
Search for a SCH_FIELD with aFieldName.
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
void SetOrientation(int aOrientation)
Compute the new transform matrix based on aOrientation for the symbol which is applied to the current...
void SetFootprintFieldText(const wxString &aFootprint)
VECTOR2I GetPosition() const override
Definition sch_symbol.h:895
void SetSchSymbolLibraryName(const wxString &aName)
The name of the symbol in the schematic library symbol list.
Definition sch_symbol.h:178
void SetValueFieldText(const wxString &aValue, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString)
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit, int aBodyStyle) const override
void AddVariant(const SCH_SHEET_PATH &aInstance, const SCH_SYMBOL_VARIANT &aVariant)
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
void SetUnitSelection(const SCH_SHEET_PATH *aSheet, int aUnitSelection)
Set the selected unit of this symbol on one sheet.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:182
void SetLibSymbol(LIB_SYMBOL *aLibSymbol)
Set this schematic symbol library symbol reference to aLibSymbol.
virtual bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
void SetExcludedFromBoard(bool aEnable, const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
KIFONT::FONT * GetDrawFont(const RENDER_SETTINGS *aSettings) const override
Definition sch_text.cpp:330
Definition seg.h:38
const std::vector< VECTOR2I > & CPoints() const
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
const std::vector< SEG > GenerateHatchLines(const std::vector< double > &aSlopes, int aSpacing, int aLineLength) const
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_POLY_SET CloneDropTriangulation() const
Simple container to manage line stroke parameters.
const TRANSFORM & GetTransform() const
Definition symbol.h:247
for transforming drawing coordinates for a wxDC device context.
Definition transform.h:42
TRANSFORM InverseTransform() const
Calculate the Inverse mirror/rotation transform.
Definition transform.cpp:55
VECTOR2I TransformCoordinate(const VECTOR2I &aPoint) const
Calculate a new coordinate according to the mirror/rotation transform.
Definition transform.cpp:40
wxString wx_str() const
Definition utf8.cpp:41
std::map< wxString, wxString > m_Fields
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
FILL_T
Definition eda_fill.h:29
@ NO_FILL
Definition eda_fill.h:30
@ REVERSE_HATCH
Definition eda_fill.h:35
@ HATCH
Definition eda_fill.h:34
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
@ CROSS_HATCH
Definition eda_fill.h:36
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
std::string source
@ LAYER_PINNUM
Definition layer_ids.h:480
@ LAYER_DEVICE
Definition layer_ids.h:488
@ LAYER_PINNAM
Definition layer_ids.h:481
@ LAYER_NOTES
Definition layer_ids.h:489
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
wxString OleLibWmfFontDirectory()
Locate the bundled libwmf fonts directory, falling back to a search above the executable so running f...
VECTOR2I OrcadDbuToIu(int aX, int aY)
bool OrcadDisplayPropShowsName(const ORCAD_DISPLAY_PROP &aProp)
FILL_T OrcadFillType(int aFillStyle, int aHatchStyle)
ORCAD_POINT OrcadInverseOrient(int aOrient, int aX, int aY)
Undo the orientation matrix of OrcadTransformPoint.
int OrcadTextBaselineOffset(int aTextSize)
int OrcadHatchPitchIu(uint32_t aModifyTimestamp)
bool OrcadDisplayPropShowsValue(const ORCAD_DISPLAY_PROP &aProp)
KIGFX::COLOR4D OrcadColor(int aColorIndex)
int OrcadDisplayFontId(const ORCAD_DISPLAY_PROP &aProp)
wxString OrcadPinNameMarkup(const wxString &aName)
constexpr int ORCAD_IU_PER_DBU
Schematic internal units per OrCAD DBU: 10 mil * 254 IU/mil.
bool OrcadDisplayPropVisible(const ORCAD_DISPLAY_PROP &aProp)
LINE_STYLE OrcadLineStyle(int aStyle)
int OrcadDisplayType(const ORCAD_DISPLAY_PROP &aProp)
bool OrcadDisplayPropShowsName(const ORCAD_DISPLAY_PROP &aProp)
FILL_T OrcadFillType(int aFillStyle, int aHatchStyle)
static bool isInstalledPropertyName(const std::string &aName)
static bool hasDualRowDescription(const std::map< std::string, std::string > &aProps)
int OrcadLineWidthIu(int aWidth)
std::pair< double, double > OrcadDashRatios(int aFormatVersionMajor)
int OrcadTextBaselineOffset(int aTextSize)
std::vector< SEG > OrcadHatchLines(const EDA_SHAPE &aShape, int aHatchStyle, int aPitch)
int OrcadHatchPitchIu(uint32_t aModifyTimestamp)
bool OrcadDisplayPropShowsValue(const ORCAD_DISPLAY_PROP &aProp)
int OrcadHatchLineWidthIu(uint32_t aModifyTimestamp)
static void addOrcadSymbolHatch(LIB_SYMBOL *aSymbol, const SCH_SHAPE &aOutline, const ORCAD_PRIMITIVE &aPrimitive, int aUnit, int aColor, uint32_t aModifyTimestamp)
int OrcadDisplayFontId(const ORCAD_DISPLAY_PROP &aProp)
wxString OrcadPinNameMarkup(const wxString &aName)
static std::string normalizedPath(std::string aPath)
bool OrcadDisplayPropVisible(const ORCAD_DISPLAY_PROP &aProp)
LINE_STYLE OrcadLineStyle(int aStyle)
static LIB_ID libIdFor(const std::string &aLibName)
int OrcadPageGraphicLineWidthIu(int aWidth)
constexpr ORCAD_ORIENT_ENTRY ORCAD_ORIENT_TABLE[8]
Indexed by orientation bits: angle, mirror, offset selectors, then matrix coefficients.
VECTOR2I OrcadTransformPoint(int aOrient, int aWidth, int aHeight, int aBaseX, int aBaseY, int aPx, int aPy)
Parts use the instance anchor as the base.
int OrcadOrientOf(int aRotation, bool aMirror)
Compose the 3-bit orientation code from the rotation bits and mirror bit.
VECTOR2I OrcadOrientOffset(int aOrient, int aWidth, int aHeight)
Bbox re-anchoring offset for the given orientation and body size (DBU).
ORCAD_PORT_TYPE
Map unknown electrical type codes to PASSIVE.
@ ORCAD_ST_LIBRARY_PART
@ ORCAD_ST_GLOBAL_SYMBOL
power symbol definition
std::string OrcadLower(std::string_view aText)
Fold ASCII letters only.
wxString FromOrcadString(const std::string &aText)
Use an 8-bit fallback if Windows-1252 decoding fails.
bool OrcadIEquals(std::string_view aLeft, std::string_view aRight)
Case-insensitive comparison with the folding of OrcadLower.
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition pin_type.h:32
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ PT_OUTPUT
usual output
Definition pin_type.h:34
@ PT_TRISTATE
tri state bus pin
Definition pin_type.h:36
@ PT_BIDI
input or output (like port for a microprocessor)
Definition pin_type.h:35
@ PT_OPENEMITTER
pin type open emitter
Definition pin_type.h:45
@ PT_OPENCOLLECTOR
pin type open collector
Definition pin_type.h:44
@ PT_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:101
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
std::shared_ptr< PNS_LOG_VIEWER_OVERLAY > overlay
constexpr double correction
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
int StrNumCmp(const wxString &aString1, const wxString &aString2, bool aIgnoreCase)
Compare two strings with alphanumerical content.
LINE_STYLE
Dashed line types.
The owning wire defines the connection.
Axis-aligned box in OrCAD DBU; corner order as stored (not normalized).
A CIS BOM variant, keyed by part occurrence id.
std::map< uint32_t, bool > installed
std::map< uint32_t, std::map< std::string, std::string > > props
One emitted KiCad lib symbol (possibly multi-unit).
std::string name
lib item name (no nickname)
std::unique_ptr< LIB_SYMBOL > kicadSymbol
built lazily by kicadSymbolFor()
std::string footprint
bool isPower
std::vector< UNIT_INFO > units
sorted by letter; unit numbers are 1-based indices
std::string refPrefix
std::string powerNet
power symbols are keyed by NET name
Per-pin choices kicadSymbolFor() makes before emitting a pin.
– shared bookkeeping types ------------------------------------------------—
std::vector< int > pinOffsets
hidden duplicate-pin electrical offsets, DBU
std::vector< bool > pinIgnore
package pins suppressed for this unit
const ORCAD_SYMBOL_DEF * convert
DeMorgan view sharing this unit's pin map.
const ORCAD_SYMBOL_DEF * symbol
std::vector< bool > explicitPinNets
placed pins whose source net overrides implicit power naming
std::vector< std::string > pinNumbers
device pin-number map for this unit
std::string letter
unit discriminator, e.g. "A", "-16", "B:Convert"
std::vector< bool > pinNumberVisible
nonblank source package pin numbers
Device unit names omit the view suffix.
std::vector< std::string > pinNumbers
std::vector< bool > pinIgnore
std::string unitRef
Display positions use symbol coordinates; rotFont combines the font index and quarter turns.
int fontIdx
1-based into ORCAD_LIBRARY_INFO::fonts; 0 = default
int rotation
0..3 quarter turns
std::string name
resolved property name (empty when index invalid)
Font indices are one-based; zero selects the default.
uint8_t pitchAndFamily
bool bold
lfWeight >= 600
int width
raw lfWidth; zero lets the font choose its natural aspect ratio
std::string face
int height
raw lfHeight (typically negative)
Free graphics use nested primitive coordinates.
std::map< std::string, std::string > props
int rotation
0..3 quarter turns
std::string name
cache symbol name
std::vector< ORCAD_DISPLAY_PROP > displayProps
std::string logicalName
ports: resolved net/port name
Each scope holds the references and child blocks for one instantiation path.
std::map< uint32_t, uint32_t > partOccurrenceIds
dbId -> part occurrence id
OrCAD rotates about the bounding box; KiCad rotates about the anchor.
int8_t tySel
Y offset selector.
int8_t c
int8_t a
int8_t d
int8_t txSel
X offset selector.
int8_t b
std::string refDes
std::map< std::string, std::string > props
Part-level properties shared by every placement (Description, Tolerance, ...).
std::vector< ORCAD_PACKAGE > variants
later same-name cache entries in stream order
std::string sourceLib
std::vector< ORCAD_DEVICE > devices
std::string pcbFootprint
std::string name
Pin positions are absolute page connection points.
The placed box includes displayed text.
std::string sourcePackage
package base name
std::vector< ORCAD_DISPLAY_PROP > displayProps
ORCAD_BBOX bbox
placed box, page DBU
int rotation
0..3 quarter turns
uint16_t unitIndex
zero-based package device index
std::map< std::string, std::string > props
short-prefix property pairs
bool mirror
orientation bit 2
std::string sourceLibrary
source library path from the placed-instance header
std::string value
resolved Part Value
std::vector< ORCAD_PIN_INST > pins
successfully parsed T0x10 records
Integer point in OrCAD DBU.
Primitive byte lengths can include or exclude the eight-byte size envelope.
int fillStyle
0 solid, 1 none, 2 hatch pattern
std::string text
kind == TEXT
std::vector< ORCAD_PRIMITIVE > children
kind == GROUP, translated by (x1, y1)
std::vector< ORCAD_POINT > points
polygon/polyline/bezier vertices
ORCAD_PRIM_KIND kind
int lineStyle
0 solid, 1 dash, 2 dot, 3 dash-dot, 4 dash-dot-dot, 5 default
std::optional< ORCAD_POINT > start
arc start point
std::optional< ORCAD_POINT > end
arc end point
int lineWidth
Capture width enum: 0 thin, 1 medium, 2 wide, 3 default.
One parsed 'Views/<folder>/Pages/<page>' stream, raw structure lists in stream order.
std::string name
std::vector< ORCAD_WIRE > wires
std::vector< ORCAD_PLACED_INSTANCE > instances
The bounding box occupies the final eight bytes before the next prefix stop.
std::string name
cache name, e.g. "C.Normal"
std::vector< ORCAD_SYMBOL_PIN > pins
std::vector< ORCAD_PRIMITIVE > primitives
bool synthesized
placeholder built from T0x10 data
std::map< std::string, std::string > props
int typeId
ORCAD_ST value.
std::string sourceLib
std::optional< ORCAD_BBOX > bbox
symbol-space body box
std::vector< ORCAD_SYMBOL_DEF > variants
Variant zero is this entry.
int generalFlags
LibraryPart GeneralProperties flags (-1 = absent); bit0 = pin names visible, bit1 = pin text rotates ...
Pin coordinates use symbol space with Y down.
int position
slot in the parent symbol pin vector
std::vector< ORCAD_DISPLAY_PROP > displayProps
ORCAD_PORT_TYPE portType
The wire ID refers to the page net table.
std::vector< ORCAD_ALIAS > aliases
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
@ SYM_ORIENT_270
Definition symbol.h:39
@ SYM_MIRROR_Y
Definition symbol.h:41
@ SYM_ORIENT_180
Definition symbol.h:38
@ SYM_MIRROR_X
Definition symbol.h:40
@ SYM_ORIENT_90
Definition symbol.h:37
@ SYM_ORIENT_0
Definition symbol.h:36
wxString GetDefaultFieldName(FIELD_T aFieldId, TRANSLATION aTranslation)
Return a default symbol field name for a mandatory field type.
@ USER
The field ID hasn't been set yet; field is invalid.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
@ UNTRANSLATED
KIBIS_PIN * pin
static std::vector< int > candidates(const SEGMENT_INDEX &aIndex, const SEG &aQuery, int aPadding)
VECTOR2I center
int radius
wxString result
Test unit parsing edge cases and error handling.
int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:227
@ SCH_FIELD_T
Definition typeinfo.h:146
@ SCH_SHAPE_T
Definition typeinfo.h:145
@ SCH_TEXT_T
Definition typeinfo.h:147
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708