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