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
29
30#include <algorithm>
31#include <cctype>
32#include <cmath>
33#include <cstdlib>
34#include <map>
35#include <memory>
36#include <string>
37#include <utility>
38#include <vector>
39
40#include <wx/string.h>
41#include <wx/translation.h>
42
43#include <base_units.h>
44#include <eda_text.h>
45#include <font/font.h>
46#include <layer_ids.h>
47#include <lib_id.h>
48#include <lib_symbol.h>
49#include <math/util.h>
50#include <pin_type.h>
51#include <sch_field.h>
52#include <sch_no_connect.h>
53#include <sch_pin.h>
54#include <sch_screen.h>
55#include <sch_shape.h>
56#include <sch_sheet_path.h>
57#include <sch_symbol.h>
58#include <sch_text.h>
59#include <stroke_params.h>
60#include <symbol.h>
61#include <template_fieldnames.h>
62
65
66
68{
69 switch( aStyle )
70 {
71 case 0: return LINE_STYLE::SOLID;
72 case 1: return LINE_STYLE::DASH;
73 case 2: return LINE_STYLE::DOT;
74 case 3: return LINE_STYLE::DASHDOT;
75 case 4: return LINE_STYLE::DASHDOTDOT;
76 case 5: return LINE_STYLE::DEFAULT;
77 default: return LINE_STYLE::DEFAULT;
78 }
79}
80
81
82int OrcadLineWidthIu( int aWidth )
83{
84 if( aWidth == 3 )
85 return 0;
86
87 return aWidth >= 0 && aWidth <= 2 ? schIUScale.MilsToIU( 5 * ( aWidth + 1 ) ) : 0;
88}
89
90
91FILL_T OrcadFillType( int aFillStyle, int aHatchStyle )
92{
93 if( aFillStyle == 0 )
95
96 if( aFillStyle == 2 )
97 {
98 switch( aHatchStyle )
99 {
100 case 4: return FILL_T::REVERSE_HATCH;
101 case 5: return FILL_T::CROSS_HATCH;
102 default: return FILL_T::HATCH;
103 }
104 }
105
106 return FILL_T::NO_FILL;
107}
108
109
110namespace
111{
112
113int schMm( double aMm )
114{
115 return schIUScale.mmToIU( aMm );
116}
117
118
119int dbuIu( double aDbu )
120{
121 return KiROUND( aDbu * ORCAD_IU_PER_DBU );
122}
123
124
125STROKE_PARAMS strokeFor( const ORCAD_PRIMITIVE& aPrimitive, int aColor )
126{
127 return STROKE_PARAMS( OrcadLineWidthIu( aPrimitive.lineWidth ), OrcadLineStyle( aPrimitive.lineStyle ),
128 OrcadColor( aColor ) );
129}
130
131
132ELECTRICAL_PINTYPE pinTypeFor( ORCAD_PORT_TYPE aType )
133{
134 switch( aType )
135 {
144 }
145
147}
148
149
150// OrCAD bookkeeping props kept out of user fields; useful ones map to dedicated
151// KiCad fields, rest describe library linkage import re-establishes itself.
152bool isBookkeepingProp( const std::string& aName )
153{
154 static const char* const skipped[] = {
155 "Part Reference",
156 "Reference",
157 "Name",
158 "Graphic",
159 "Implementation",
160 "Implementation Path",
161 "Implementation Type",
162 "Source Library",
163 "Source Package",
164 "Source Part",
165 };
166
167 for( const char* name : skipped )
168 {
169 if( aName == name )
170 return true;
171 }
172
173 return false;
174}
175
176} // namespace
177
178
180{
181 // Page symbols absent from design cache get synthesized placeholder so T0x10
182 // connection points stay electrically intact.
183 std::vector<std::string> missingOrder;
184 std::map<std::string, std::vector<const ORCAD_PLACED_INSTANCE*>> missing;
185
186 // Scan root and child-folder pages; child-folder parts live in childFolderPages,
187 // not root page list.
188 std::vector<const ORCAD_RAW_PAGE*> allPages;
189
190 for( const ORCAD_RAW_PAGE& page : m_design.pages )
191 allPages.push_back( &page );
192
193 for( const auto& [folder, pages] : m_design.childFolderPages )
194 {
195 for( const ORCAD_RAW_PAGE& page : pages )
196 allPages.push_back( &page );
197 }
198
199 for( const ORCAD_RAW_PAGE* page : allPages )
200 {
201 for( const ORCAD_PLACED_INSTANCE& inst : page->instances )
202 {
203 if( inst.pkgName.empty() || m_design.symbols.count( inst.pkgName ) )
204 continue;
205
206 auto [it, isNew] = missing.try_emplace( inst.pkgName );
207
208 if( isNew )
209 missingOrder.push_back( inst.pkgName );
210
211 it->second.push_back( &inst );
212 }
213 }
214
215 for( const std::string& name : missingOrder )
216 {
217 const std::vector<const ORCAD_PLACED_INSTANCE*>& insts = missing[name];
218
219 note( wxString::Format( _( "Symbol '%s' is absent from the design cache; synthesized "
220 "placeholder from %d instance(s)." ),
221 FromOrcadString( name ), (int) insts.size() ) );
222
223 m_design.symbols[name] = synthesizeSymbol( name, insts );
224 }
225
226 for( const ORCAD_RAW_PAGE* page : allPages )
227 {
228 for( const ORCAD_PLACED_INSTANCE& inst : page->instances )
229 libForInstance( inst );
230 }
231
233}
234
235
236std::vector<LIB_SYMBOL*> ORCAD_CONVERTER::BuildSymbolLibrary()
237{
238 // .OLB has same Library/Cache/Packages streams but no pages, so lib entries built
239 // directly from cache defs (grouped by base name, Normal view) w/ package data.
240 auto stripView = []( const std::string& aName ) -> std::string
241 {
242 size_t dot = aName.rfind( '.' );
243
244 if( dot != std::string::npos )
245 {
246 std::string view = aName.substr( dot + 1 );
247
248 if( view == "Normal" || view == "Convert" )
249 return aName.substr( 0, dot );
250 }
251
252 return aName;
253 };
254
255 for( auto& [cacheName, def] : m_design.symbols )
256 {
257 // Only drawable library parts and power symbols become library items
258 if( def.typeId != ORCAD_ST_LIBRARY_PART && def.typeId != ORCAD_ST_GLOBAL_SYMBOL )
259 continue;
260
261 bool convertView = cacheName.size() > 8
262 && cacheName.compare( cacheName.size() - 8, 8, ".Convert" ) == 0;
263 std::string base = stripView( cacheName );
264 std::string libname = SymbolId( base );
265
266 LIB_ENTRY& ls = m_libSymbols[libname];
267
268 // First (Normal) view wins; .Convert is DeMorgan alternate sharing pin map,
269 // not separate item.
270 if( !ls.name.empty() )
271 continue;
272
273 if( convertView && m_design.symbols.count( base + ".Normal" ) )
274 continue;
275
276 ls.name = libname;
277
278 std::string bare = base.substr( 0, base.find( '.' ) );
279 const ORCAD_PACKAGE* pkg = nullptr;
280
281 for( const std::string& key : { base, bare } )
282 {
283 auto pkgIt = m_design.packages.find( key );
284
285 if( pkgIt != m_design.packages.end() )
286 {
287 pkg = &pkgIt->second;
288 break;
289 }
290 }
291
292 ls.isPower = def.typeId == ORCAD_ST_GLOBAL_SYMBOL;
293 ls.refPrefix = ( pkg && !pkg->refDes.empty() ) ? pkg->refDes : ( ls.isPower ? "#PWR" : "U" );
294 ls.footprint = pkg ? pkg->pcbFootprint : "";
295
296 if( ls.isPower )
297 ls.powerNet = base; // power value = net name (symbol base)
298
299 std::vector<std::string> pinNumbers;
300
301 if( pkg && !pkg->devices.empty() )
302 pinNumbers = pkg->devices.front().pinNumbers;
303
304 ls.units.push_back( { "A", &def, pinNumbers } );
305 }
306
308
309 std::vector<LIB_SYMBOL*> out;
310
311 for( auto& [libname, entry] : m_libSymbols )
312 {
313 // Single malformed cache symbol must not sink whole library.
314 try
315 {
316 if( LIB_SYMBOL* symbol = kicadSymbolFor( libname ) )
317 out.push_back( static_cast<LIB_SYMBOL*>( symbol->Duplicate() ) );
318 }
319 catch( const std::exception& )
320 {
321 }
322 }
323
324 return out;
325}
326
327
329ORCAD_CONVERTER::synthesizeSymbol( const std::string& aPkgName,
330 const std::vector<const ORCAD_PLACED_INSTANCE*>& aInstances ) const
331{
332 // Prefer reference-orientation instance: t-term in T = I + t(w,h) + M*p cancels
333 // there, making reconstructed connection points exact.
334 const ORCAD_PLACED_INSTANCE* ref = aInstances.front();
335
336 for( const ORCAD_PLACED_INSTANCE* inst : aInstances )
337 {
338 if( OrcadOrientOf( inst->rotation, inst->mirror ) == 0 )
339 {
340 ref = inst;
341 break;
342 }
343 }
344
345 int ori = OrcadOrientOf( ref->rotation, ref->mirror );
347 int det = e.a * e.d - e.b * e.c; // always +/-1
348
349 auto inv = [&]( int aX, int aY ) -> ORCAD_POINT
350 {
351 return { ( e.d * aX - e.b * aY ) / det, ( -e.c * aX + e.a * aY ) / det };
352 };
353
354 // Instance-local pin positions from T0x10 records.
355 std::vector<ORCAD_POINT> r;
356
357 for( const ORCAD_PIN_INST& t : ref->pins )
358 r.push_back( inv( t.x - ref->x, t.y - ref->y ) );
359
360 std::vector<char> sides; // 'L', 'R', 'T', 'B' or 0 per pin
361 int bx1 = 0;
362 int by1 = 0;
363 int bx2 = 0;
364 int by2 = 0;
365
366 if( !r.empty() )
367 {
368 int x0 = r.front().x;
369 int x1 = r.front().x;
370 int y0 = r.front().y;
371 int y1 = r.front().y;
372
373 for( const ORCAD_POINT& p : r )
374 {
375 x0 = std::min( x0, p.x );
376 x1 = std::max( x1, p.x );
377 y0 = std::min( y0, p.y );
378 y1 = std::max( y1, p.y );
379 }
380
381 bool horizFirst = ( x1 - x0 ) >= ( y1 - y0 );
382
383 for( const ORCAD_POINT& p : r )
384 {
385 std::vector<char> cand;
386
387 if( p.x == x0 )
388 cand.push_back( 'L' );
389
390 if( p.x == x1 )
391 cand.push_back( 'R' );
392
393 if( p.y == y0 )
394 cand.push_back( 'T' );
395
396 if( p.y == y1 )
397 cand.push_back( 'B' );
398
399 if( cand.empty() )
400 {
401 sides.push_back( 0 );
402 continue;
403 }
404
405 // Prefer L/R sides when pin field wider than tall, T/B otherwise; ties
406 // keep L,R,T,B order.
407 char pick = cand.front();
408
409 for( char c : cand )
410 {
411 bool isTB = c == 'T' || c == 'B';
412
413 if( isTB != horizFirst )
414 {
415 pick = c;
416 break;
417 }
418 }
419
420 sides.push_back( pick );
421 }
422
423 auto hasSide = [&]( char aSide )
424 {
425 return std::find( sides.begin(), sides.end(), aSide ) != sides.end();
426 };
427
428 bx1 = hasSide( 'L' ) ? x0 + PIN_LEN_DBU : x0;
429 bx2 = hasSide( 'R' ) ? x1 - PIN_LEN_DBU : x1;
430 by1 = hasSide( 'T' ) ? y0 + PIN_LEN_DBU : y0;
431 by2 = hasSide( 'B' ) ? y1 - PIN_LEN_DBU : y1;
432
433 if( bx2 <= bx1 )
434 {
435 int m = (int) std::floor( ( bx1 + bx2 ) / 2.0 );
436 bx1 = m - PIN_LEN_DBU;
437 bx2 = m + PIN_LEN_DBU;
438 }
439
440 if( by2 <= by1 )
441 {
442 int m = (int) std::floor( ( by1 + by2 ) / 2.0 );
443 by1 = m - PIN_LEN_DBU;
444 by2 = m + PIN_LEN_DBU;
445 }
446 }
447 else
448 {
449 // No pin records; derive body from inverse-transformed placed box.
450 ORCAD_POINT c1 = inv( ref->bbox.x1 - ref->x, ref->bbox.y1 - ref->y );
451 ORCAD_POINT c2 = inv( ref->bbox.x2 - ref->x, ref->bbox.y2 - ref->y );
452
453 bx1 = std::min( c1.x, c2.x );
454 bx2 = std::max( c1.x, c2.x );
455 by1 = std::min( c1.y, c2.y );
456 by2 = std::max( c1.y, c2.y );
457 }
458
459 // Remove orientation re-anchoring offset so definition anchors like real cache
460 // symbol.
461 int w = bx2 - bx1;
462 int h = by2 - by1;
463 VECTOR2I t = OrcadOrientOffset( ori, w, h );
464 ORCAD_POINT s = inv( t.x, t.y );
465
468 sym.name = aPkgName;
469 sym.synthesized = true;
470 sym.bbox = ORCAD_BBOX{ bx1 - s.x, by1 - s.y, bx2 - s.x, by2 - s.y };
471
472 ORCAD_PRIMITIVE rect;
474 rect.x1 = bx1 - s.x;
475 rect.y1 = by1 - s.y;
476 rect.x2 = bx2 - s.x;
477 rect.y2 = by2 - s.y;
478 sym.primitives.push_back( rect );
479
480 for( size_t i = 0; i < r.size(); ++i )
481 {
482 int px = r[i].x - s.x;
483 int py = r[i].y - s.y;
484 int ddx = 0;
485 int ddy = 0;
486
487 switch( sides[i] )
488 {
489 case 'L': ddx = PIN_LEN_DBU; break;
490 case 'R': ddx = -PIN_LEN_DBU; break;
491 case 'T': ddy = PIN_LEN_DBU; break;
492 case 'B': ddy = -PIN_LEN_DBU; break;
493 default: break;
494 }
495
497 pin.startX = px + ddx;
498 pin.startY = py + ddy;
499 pin.hotptX = px;
500 pin.hotptY = py;
501 pin.portType = ORCAD_PORT_TYPE::PASSIVE;
502 pin.shapeBits = 0;
503 sym.pins.push_back( pin );
504 }
505
506 return sym;
507}
508
509
510std::string ORCAD_CONVERTER::unitLetter( const ORCAD_PLACED_INSTANCE& aInst ) const
511{
512 // Cache names look like "<base><unit>.<view>", e.g. "74LS00A.Normal" (unit A of
513 // 74LS00) or "C.Normal" (single-unit capacitor).
514 const std::string& base = aInst.sourcePackage;
515 const std::string& v = aInst.pkgName;
516
517 std::string tail = ( !base.empty() && v.compare( 0, base.size(), base ) == 0 )
518 ? v.substr( base.size() )
519 : v;
520
521 size_t dot = tail.find( '.' );
522 std::string unit = tail.substr( 0, dot );
523 std::string view = dot == std::string::npos ? std::string() : tail.substr( dot + 1 );
524
525 if( !view.empty() )
526 {
527 std::string lower = view;
528
529 std::transform( lower.begin(), lower.end(), lower.begin(),
530 []( unsigned char c )
531 {
532 return (char) std::tolower( c );
533 } );
534
535 // Non-Normal views are DeMorgan alternates with own body graphics, so view
536 // name stays part of unit discriminator.
537 if( lower != "normal" )
538 unit += ":" + view;
539 }
540
541 return unit;
542}
543
544
545std::pair<const ORCAD_SYMBOL_DEF*, int>
547{
548 auto it = m_design.symbols.find( aInst.pkgName );
549
550 if( it == m_design.symbols.end() )
551 return { nullptr, 0 };
552
553 const ORCAD_SYMBOL_DEF* prime = &it->second;
554
555 std::vector<const ORCAD_SYMBOL_DEF*> variants;
556 variants.push_back( prime );
557
558 for( const ORCAD_SYMBOL_DEF& v : prime->variants )
559 variants.push_back( &v );
560
561 if( aInst.pins.empty() || variants.size() == 1 )
562 return { prime, 0 };
563
564 int ori = OrcadOrientOf( aInst.rotation, aInst.mirror );
565
566 for( size_t vi = 0; vi < variants.size(); ++vi )
567 {
568 const ORCAD_SYMBOL_DEF* sym = variants[vi];
569
570 if( sym->pins.size() != aInst.pins.size() )
571 continue;
572
573 ORCAD_BBOX bb = sym->bbox.value_or( ORCAD_BBOX() );
574 bool match = true;
575
576 for( size_t i = 0; i < sym->pins.size(); ++i )
577 {
578 VECTOR2I calc = OrcadTransformPoint( ori, bb.x2 - bb.x1, bb.y2 - bb.y1, aInst.x,
579 aInst.y, sym->pins[i].hotptX,
580 sym->pins[i].hotptY );
581
582 if( calc.x != aInst.pins[i].x || calc.y != aInst.pins[i].y )
583 {
584 match = false;
585 break;
586 }
587 }
588
589 if( match )
590 return { sym, (int) vi };
591 }
592
593 return { prime, 0 };
594}
595
596
598{
599 std::string base = !aInst.sourcePackage.empty()
600 ? aInst.sourcePackage
601 : aInst.pkgName.substr( 0, aInst.pkgName.find( '.' ) );
602
603 auto it = m_design.packages.find( base );
604
605 return it != m_design.packages.end() ? &it->second : nullptr;
606}
607
608
609std::map<std::string, std::string>
611 const ORCAD_SYMBOL_DEF& aDef ) const
612{
613 std::map<std::string, std::string> props = aDef.props;
614
615 if( const ORCAD_PACKAGE* pkg = packageFor( aInst ) )
616 {
617 for( const auto& [name, value] : pkg->props )
618 props[name] = value;
619 }
620
621 for( const auto& [name, value] : aInst.props )
622 props[name] = value;
623
624 return props;
625}
626
627
628std::pair<std::string, int> ORCAD_CONVERTER::libForInstance( const ORCAD_PLACED_INSTANCE& aInst )
629{
630 auto [sym, vi] = pickVariant( aInst );
631
632 std::string srcOrPkg = !aInst.sourcePackage.empty() ? aInst.sourcePackage : aInst.pkgName;
633 PKG_KEY key{ srcOrPkg, aInst.pkgName, vi };
634
635 auto found = m_pkgToLib.find( key );
636
637 if( found != m_pkgToLib.end() )
638 return found->second;
639
640 std::string base = !aInst.sourcePackage.empty()
641 ? aInst.sourcePackage
642 : aInst.pkgName.substr( 0, aInst.pkgName.find( '.' ) );
643
644 std::string letter = unitLetter( aInst );
645
646 if( !sym )
647 {
648 warn( wxString::Format( _( "No cached symbol for '%s' (%s); placeholder emitted." ),
649 FromOrcadString( aInst.pkgName ),
650 FromOrcadString( aInst.reference ) ) );
651
652 // Register empty definition so unit references stable storage.
653 ORCAD_SYMBOL_DEF& slot = m_design.symbols[aInst.pkgName];
654
655 if( slot.name.empty() )
656 {
657 slot.typeId = 0;
658 slot.name = aInst.pkgName;
659 }
660
661 sym = &slot;
662 }
663
664 const ORCAD_PACKAGE* pkg = nullptr;
665 auto pkgIt = m_design.packages.find( base );
666
667 if( pkgIt != m_design.packages.end() )
668 pkg = &pkgIt->second;
669
670 // Pin numbers from package device whose unitRef equals BARE unit letter; ':View'
671 // suffix only selects DeMorgan graphics, views share Normal pin map.
672 std::vector<std::string> pinNumbers;
673 std::string bare = letter.substr( 0, letter.find( ':' ) );
674
675 if( pkg )
676 {
677 bool foundDevice = false;
678
679 for( const ORCAD_DEVICE& d : pkg->devices )
680 {
681 if( d.unitRef == bare )
682 {
683 pinNumbers = d.pinNumbers;
684 foundDevice = true;
685 break;
686 }
687 }
688
689 if( !foundDevice && !pkg->devices.empty() && bare.empty() )
690 pinNumbers = pkg->devices.front().pinNumbers;
691 }
692
693 std::string libname = SymbolId( base );
694
695 if( vi )
696 libname += "_v" + std::to_string( vi + 1 );
697
698 LIB_ENTRY& ls = m_libSymbols[libname];
699
700 if( ls.name.empty() )
701 {
702 ls.name = libname;
703 ls.refPrefix = ( pkg && !pkg->refDes.empty() ) ? pkg->refDes : "U";
704 ls.footprint = pkg ? pkg->pcbFootprint : "";
705 }
706
707 int unitNo = 0;
708
709 for( size_t i = 0; i < ls.units.size(); ++i )
710 {
711 if( ls.units[i].letter == letter )
712 {
713 unitNo = (int) i + 1;
714 break;
715 }
716 }
717
718 if( unitNo == 0 )
719 {
720 UNIT_INFO unit;
721 unit.letter = letter;
722 unit.symbol = sym;
723 unit.pinNumbers = pinNumbers;
724 ls.units.push_back( std::move( unit ) );
725
726 std::stable_sort( ls.units.begin(), ls.units.end(),
727 []( const UNIT_INFO& a, const UNIT_INFO& b )
728 {
729 return a.letter < b.letter;
730 } );
731
732 for( size_t i = 0; i < ls.units.size(); ++i )
733 {
734 if( ls.units[i].letter == letter )
735 {
736 unitNo = (int) i + 1;
737 break;
738 }
739 }
740
741 // Inserting a unit renumbers later-sorting letters, so every map entry for
742 // this lib symbol is stale; rebuild them.
743 for( auto mapIt = m_pkgToLib.begin(); mapIt != m_pkgToLib.end(); )
744 {
745 if( mapIt->second.first == libname )
746 mapIt = m_pkgToLib.erase( mapIt );
747 else
748 ++mapIt;
749 }
750
751 for( size_t i = 0; i < ls.units.size(); ++i )
752 {
753 std::string pkgName = !aInst.sourcePackage.empty()
754 ? aInst.sourcePackage + ls.units[i].letter + ".Normal"
755 : aInst.pkgName;
756
757 m_pkgToLib[PKG_KEY{ srcOrPkg, pkgName, vi }] = { libname, (int) i + 1 };
758 }
759 }
760
761 m_pkgToLib[key] = { libname, unitNo };
762 return { libname, unitNo };
763}
764
765
766std::string ORCAD_CONVERTER::powerLibFor( const std::string& aSymbolName,
767 const std::string& aNetName )
768{
769 // Keyed by net name, not symbol name; users rename power ports and one symbol
770 // shape can serve several nets.
771 std::string libname = "PWR_" + SymbolId( aNetName );
772
773 if( m_libSymbols.count( libname ) )
774 return libname;
775
776 const ORCAD_SYMBOL_DEF* sym = nullptr;
777 auto it = m_design.symbols.find( aSymbolName );
778
779 if( it != m_design.symbols.end() )
780 {
781 sym = &it->second;
782 }
783 else
784 {
785 // Uncached power symbol; register empty definition (no graphics).
786 ORCAD_SYMBOL_DEF& slot = m_design.symbols[aSymbolName];
787
788 if( slot.name.empty() )
789 {
791 slot.name = aSymbolName;
792 }
793
794 sym = &slot;
795 }
796
797 LIB_ENTRY& ls = m_libSymbols[libname];
798 ls.name = libname;
799 ls.isPower = true;
800 ls.powerNet = aNetName;
801 ls.refPrefix = "#PWR";
802
803 UNIT_INFO unit;
804 unit.symbol = sym;
805 unit.pinNumbers = { "1" };
806 ls.units.push_back( std::move( unit ) );
807
808 return libname;
809}
810
811
812LIB_SYMBOL* ORCAD_CONVERTER::kicadSymbolFor( const std::string& aLibName )
813{
814 auto it = m_libSymbols.find( aLibName );
815
816 if( it == m_libSymbols.end() )
817 return nullptr;
818
819 LIB_ENTRY& entry = it->second;
820
821 if( entry.kicadSymbol )
822 return entry.kicadSymbol.get();
823
824 // OrCAD cache names can contain LIB_ID-illegal chars (e.g. backslashes from
825 // library path prefixes).
826 wxString name = LIB_ID::FixIllegalChars( FromOrcadString( entry.name ), false ).wx_str();
827
828 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( name );
829 symbol->SetLibId( LIB_ID( wxString::FromUTF8( LIB_NICK ), name ) );
830 symbol->SetPinNameOffset( schMm( 0.254 ) );
831
832 // Pin number/name visibility from LibraryPart flags; bit0=names visible,
833 // bit2=numbers hidden
834 for( const UNIT_INFO& unit : entry.units )
835 {
836 if( unit.symbol && unit.symbol->generalFlags >= 0 )
837 {
838 int flags = unit.symbol->generalFlags;
839 symbol->SetShowPinNumbers( ( flags & 0x04 ) == 0 );
840 symbol->SetShowPinNames( ( flags & 0x01 ) != 0 );
841 break;
842 }
843 }
844
845 if( entry.isPower )
846 symbol->SetGlobalPower();
847
848 if( (int) entry.units.size() > 1 )
849 symbol->SetUnitCount( (int) entry.units.size(), false );
850
851 const ORCAD_SYMBOL_DEF* u0 = entry.units.empty() ? nullptr : entry.units.front().symbol;
852 ORCAD_BBOX bb = ( u0 && u0->bbox ) ? *u0->bbox : ORCAD_BBOX();
853
854 // Default field spot above body; in-memory symbol space Y-down, so above = more
855 // negative Y.
856 int topY = bb.y1 * ORCAD_IU_PER_DBU - schMm( 2.54 );
857
858 SCH_FIELD& refField = symbol->GetReferenceField();
859 SCH_FIELD& valField = symbol->GetValueField();
860
861 if( entry.isPower )
862 {
863 refField.SetText( wxS( "#PWR" ) );
864 refField.SetVisible( false );
865 refField.SetTextPos( VECTOR2I( 0, topY ) );
866
867 valField.SetText( FromOrcadString( entry.powerNet ) );
868 valField.SetVisible( true );
869 valField.SetTextPos( VECTOR2I( 0, topY ) );
870 }
871 else
872 {
873 refField.SetText( FromOrcadString( entry.refPrefix ) );
874 refField.SetVisible( true );
875 refField.SetTextPos( VECTOR2I( 0, topY ) );
876
877 valField.SetText( name );
878 valField.SetVisible( true );
879 valField.SetTextPos( VECTOR2I( 0, topY - schMm( 2.54 ) ) );
880 }
881
882 // OrCAD footprint names are not valid KiCad footprint library IDs; the source
883 // name is preserved as a user field on each placed instance instead. Leaving
884 // the Footprint field empty lets imported symbols link to imported board
885 // footprints via "Update PCB from Schematic -> re-link by reference".
886 symbol->GetFootprintField().SetVisible( false );
887 symbol->GetDatasheetField().SetVisible( false );
888
889 for( size_t ui = 0; ui < entry.units.size(); ++ui )
890 {
891 const UNIT_INFO& unit = entry.units[ui];
892 int unitNo = (int) ui + 1;
893
894 if( !unit.symbol )
895 continue;
896
897 for( const ORCAD_PRIMITIVE& prim : unit.symbol->primitives )
898 addSymbolPrimitive( symbol.get(), prim, unitNo, unit.symbol->color );
899
900 for( size_t pi = 0; pi < unit.symbol->pins.size(); ++pi )
901 {
902 const ORCAD_SYMBOL_PIN& sourcePin = unit.symbol->pins[pi];
903 int position =
904 unit.symbol->pins[pi].position >= 0 ? unit.symbol->pins[pi].position : static_cast<int>( pi );
905 wxString number = position < static_cast<int>( unit.pinNumbers.size() )
906 ? FromOrcadString( unit.pinNumbers[position] )
907 : wxString::Format( wxS( "%d" ), (int) pi + 1 );
908
909 if( !sourcePin.name.empty()
910 && std::count( unit.pinNumbers.begin(), unit.pinNumbers.end(), sourcePin.name ) == 1 )
911 number = FromOrcadString( sourcePin.name );
912
913 addSymbolPin( symbol.get(), sourcePin, number, unitNo, entry.isPower,
914 entry.isPower ? entry.powerNet : std::string() );
915 }
916 }
917
918 entry.kicadSymbol = std::move( symbol );
919 return entry.kicadSymbol.get();
920}
921
922
923void ORCAD_CONVERTER::addSymbolPrimitive( LIB_SYMBOL* aSymbol, const ORCAD_PRIMITIVE& aPrim, int aUnit, int aColor,
924 int aOffsetX, int aOffsetY )
925{
926 // Cache defs and in-memory symbol space both Y-down; .kicad_sch writer flips to
927 // file Y-up itself.
928 auto toX = [aOffsetX]( int aV )
929 {
930 return ( aV + aOffsetX ) * ORCAD_IU_PER_DBU;
931 };
932 auto toY = [aOffsetY]( int aV )
933 {
934 return ( aV + aOffsetY ) * ORCAD_IU_PER_DBU;
935 };
936
937 switch( aPrim.kind )
938 {
940 for( const ORCAD_PRIMITIVE& child : aPrim.children )
941 addSymbolPrimitive( aSymbol, child, aUnit, aColor, aOffsetX + aPrim.x1, aOffsetY + aPrim.y1 );
942
943 break;
944
946 {
948 shape->SetPosition( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ) );
949 shape->SetEnd( VECTOR2I( toX( aPrim.x2 ), toY( aPrim.y2 ) ) );
950 shape->SetStroke( strokeFor( aPrim, aColor ) );
951 shape->SetFillMode( OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
952 shape->SetUnit( aUnit );
953 aSymbol->AddDrawItem( shape, false );
954 break;
955 }
956
958 {
960 shape->AddPoint( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ) );
961 shape->AddPoint( VECTOR2I( toX( aPrim.x2 ), toY( aPrim.y2 ) ) );
962 shape->SetStroke( strokeFor( aPrim, aColor ) );
964 shape->SetUnit( aUnit );
965 aSymbol->AddDrawItem( shape, false );
966 break;
967 }
968
971 {
972 if( aPrim.points.size() < 2 )
973 return;
974
976
977 for( const ORCAD_POINT& pt : aPrim.points )
978 shape->AddPoint( VECTOR2I( toX( pt.x ), toY( pt.y ) ) );
979
980 bool isPolygon = aPrim.kind == ORCAD_PRIM_KIND::POLYGON;
981
982 if( isPolygon && aPrim.points.front() != aPrim.points.back() )
983 shape->AddPoint( VECTOR2I( toX( aPrim.points.front().x ),
984 toY( aPrim.points.front().y ) ) );
985
986 shape->SetStroke( strokeFor( aPrim, aColor ) );
987 shape->SetFillMode( isPolygon ? OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) : FILL_T::NO_FILL );
988 shape->SetUnit( aUnit );
989 aSymbol->AddDrawItem( shape, false );
990 break;
991 }
992
994 {
995 if( aPrim.points.size() < 4 || ( aPrim.points.size() - 1 ) % 3 != 0 )
996 {
997 if( aPrim.points.size() >= 2 )
998 {
1000
1001 for( const ORCAD_POINT& pt : aPrim.points )
1002 shape->AddPoint( VECTOR2I( toX( pt.x ), toY( pt.y ) ) );
1003
1004 shape->SetStroke( strokeFor( aPrim, aColor ) );
1005 shape->SetFillMode( FILL_T::NO_FILL );
1006 shape->SetUnit( aUnit );
1007 aSymbol->AddDrawItem( shape, false );
1008 }
1009
1010 return;
1011 }
1012
1013 for( size_t i = 0; i + 3 < aPrim.points.size(); i += 3 )
1014 {
1016 shape->SetPosition( VECTOR2I( toX( aPrim.points[i].x ), toY( aPrim.points[i].y ) ) );
1017 shape->SetBezierC1( VECTOR2I( toX( aPrim.points[i + 1].x ), toY( aPrim.points[i + 1].y ) ) );
1018 shape->SetBezierC2( VECTOR2I( toX( aPrim.points[i + 2].x ), toY( aPrim.points[i + 2].y ) ) );
1019 shape->SetEnd( VECTOR2I( toX( aPrim.points[i + 3].x ), toY( aPrim.points[i + 3].y ) ) );
1020 shape->SetStroke( strokeFor( aPrim, aColor ) );
1021 shape->SetFillMode( FILL_T::NO_FILL );
1022 shape->SetUnit( aUnit );
1023 aSymbol->AddDrawItem( shape, false );
1024 }
1025
1026 break;
1027 }
1028
1030 {
1031 double cx = ( aPrim.x1 + aPrim.x2 ) / 2.0;
1032 double cy = ( aPrim.y1 + aPrim.y2 ) / 2.0;
1033 double rx = std::abs( aPrim.x2 - aPrim.x1 ) / 2.0;
1034 double ry = std::abs( aPrim.y2 - aPrim.y1 ) / 2.0;
1035
1036 if( std::abs( rx - ry ) < 0.5 )
1037 {
1039 VECTOR2I center( dbuIu( cx ), dbuIu( cy ) );
1040 shape->SetPosition( center );
1041 shape->SetEnd( center + VECTOR2I( dbuIu( rx ), 0 ) );
1042 shape->SetStroke( strokeFor( aPrim, aColor ) );
1043 shape->SetFillMode( OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
1044 shape->SetUnit( aUnit );
1045 aSymbol->AddDrawItem( shape, false );
1046 }
1047 else
1048 {
1049 // Unequal radii; approximate ellipse with closed polyline.
1051
1052 for( int k = 0; k <= 32; ++k )
1053 {
1054 double a = 2.0 * M_PI * k / 32.0;
1055 shape->AddPoint( VECTOR2I( dbuIu( cx + rx * std::cos( a ) ),
1056 dbuIu( cy + ry * std::sin( a ) ) ) );
1057 }
1058
1059 shape->SetStroke( strokeFor( aPrim, aColor ) );
1060 shape->SetFillMode( OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
1061 shape->SetUnit( aUnit );
1062 aSymbol->AddDrawItem( shape, false );
1063 }
1064
1065 break;
1066 }
1067
1068 case ORCAD_PRIM_KIND::ARC: addSymbolArc( aSymbol, aPrim, aUnit, aColor, aOffsetX, aOffsetY ); break;
1069
1071 {
1072 SCH_TEXT* text = new SCH_TEXT( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ),
1073 FromOrcadString( aPrim.text ), LAYER_DEVICE );
1074 int size = textSizeIU( aPrim.fontIdx );
1075 text->SetTextSize( VECTOR2I( size, size ) );
1076 applyFont( text, aPrim.fontIdx );
1077 text->SetTextColor( OrcadColor( aColor ) );
1078
1079 // OrCAD anchors comment text at top-left of bounding box.
1080 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1081 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
1082 text->SetUnit( aUnit );
1083 aSymbol->AddDrawItem( text, false );
1084 break;
1085 }
1086
1088 // Embedded images only in page graphics, not symbol bodies.
1089 break;
1090 }
1091}
1092
1093
1094void ORCAD_CONVERTER::addSymbolArc( LIB_SYMBOL* aSymbol, const ORCAD_PRIMITIVE& aPrim, int aUnit, int aColor,
1095 int aOffsetX, int aOffsetY )
1096{
1097 if( !aPrim.start || !aPrim.end )
1098 return;
1099
1100 double cx = ( aPrim.x1 + aPrim.x2 ) / 2.0;
1101 double cy = ( aPrim.y1 + aPrim.y2 ) / 2.0;
1102 double rx = std::abs( aPrim.x2 - aPrim.x1 ) / 2.0;
1103 double ry = std::abs( aPrim.y2 - aPrim.y1 ) / 2.0;
1104
1105 if( rx == 0.0 )
1106 rx = 0.01;
1107
1108 if( ry == 0.0 )
1109 ry = 0.01;
1110
1111 double a0 = std::atan2( ( aPrim.start->y - cy ) / ry, ( aPrim.start->x - cx ) / rx );
1112 double a1 = std::atan2( ( aPrim.end->y - cy ) / ry, ( aPrim.end->x - cx ) / rx );
1113
1114 // OrCAD draws arcs CCW in screen (Y-down) coords, meaning decreasing parameter
1115 // angle here.
1116 if( a1 >= a0 )
1117 a1 -= 2.0 * M_PI;
1118
1119 int steps = std::max( 4, (int) ( std::abs( a1 - a0 ) / ( M_PI / 16.0 ) ) );
1120
1121 // Polyline approximation; robust for elliptical arcs KiCad arcs cannot represent.
1123
1124 for( int k = 0; k <= steps; ++k )
1125 {
1126 double a = a0 + ( a1 - a0 ) * k / steps;
1127 shape->AddPoint(
1128 VECTOR2I( dbuIu( cx + rx * std::cos( a ) + aOffsetX ), dbuIu( cy + ry * std::sin( a ) + aOffsetY ) ) );
1129 }
1130
1131 shape->SetStroke( strokeFor( aPrim, aColor ) );
1132 shape->SetFillMode( FILL_T::NO_FILL );
1133 shape->SetUnit( aUnit );
1134 aSymbol->AddDrawItem( shape, false );
1135}
1136
1137
1139 const wxString& aNumber, int aUnit, bool aPower,
1140 const std::string& aNameOverride )
1141{
1142 int dx = aPin.startX - aPin.hotptX;
1143 int dy = aPin.startY - aPin.hotptY;
1144
1145 SCH_PIN* pin = new SCH_PIN( aSymbol );
1146
1147 // Hot point = connection point; start point = body end.
1148 pin->SetPosition( VECTOR2I( aPin.hotptX * ORCAD_IU_PER_DBU,
1149 aPin.hotptY * ORCAD_IU_PER_DBU ) );
1150 pin->SetLength( KiROUND( std::hypot( (double) dx, (double) dy ) ) * ORCAD_IU_PER_DBU );
1151
1153
1154 if( dx > 0 )
1155 orientation = PIN_ORIENTATION::PIN_RIGHT;
1156 else if( dx < 0 )
1157 orientation = PIN_ORIENTATION::PIN_LEFT;
1158 else if( dy > 0 )
1159 orientation = PIN_ORIENTATION::PIN_DOWN; // start below hot point (Y-down source)
1160 else if( dy < 0 )
1161 orientation = PIN_ORIENTATION::PIN_UP;
1162
1163 pin->SetOrientation( orientation );
1164
1165 // shapeBits bit1 = clock, bit2 = inverted dot.
1166 bool isClock = ( aPin.shapeBits & 0x2 ) != 0;
1167 bool isDot = ( aPin.shapeBits & 0x4 ) != 0;
1168
1169 if( isClock && isDot )
1171 else if( isClock )
1172 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
1173 else if( isDot )
1174 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
1175 else
1176 pin->SetShape( GRAPHIC_PINSHAPE::LINE );
1177
1178 if( aPower )
1179 {
1181 pin->SetVisible( false );
1182 }
1183 else
1184 {
1185 pin->SetType( pinTypeFor( aPin.portType ) );
1186 }
1187
1188 // "$PIN"-prefixed names are OrCAD auto-generated placeholders.
1189 wxString name;
1190
1191 if( !aNameOverride.empty() )
1192 name = FromOrcadString( aNameOverride );
1193 else if( !aPin.name.empty() && aPin.name.compare( 0, 4, "$PIN" ) != 0 )
1194 name = FromOrcadString( aPin.name );
1195
1196 // Avoid rendering same text twice next to pin.
1197 if( name == aNumber )
1198 name = wxEmptyString;
1199
1200 pin->SetName( name );
1201 pin->SetNumber( aNumber );
1202 pin->SetNameTextSize( schMm( 1.27 ) );
1203 pin->SetNumberTextSize( schMm( 1.27 ) );
1204 pin->SetUnit( aUnit );
1205 aSymbol->AddDrawItem( pin, false );
1206}
1207
1208
1210{
1211 switch( aOrient & 7 )
1212 {
1213 default:
1214 case 0: return SYM_ORIENT_0;
1215 case 1: return SYM_ORIENT_90;
1216 case 2: return SYM_ORIENT_180;
1217 case 3: return SYM_ORIENT_270;
1218 case 4: return SYM_ORIENT_0 + SYM_MIRROR_Y;
1219 case 5: return SYM_ORIENT_90 + SYM_MIRROR_X;
1220 case 6: return SYM_ORIENT_0 + SYM_MIRROR_X;
1221 case 7: return SYM_ORIENT_270 + SYM_MIRROR_X;
1222 }
1223}
1224
1225
1227 SCH_SCREEN* aScreen, const SCH_SHEET_PATH& aSheetPath )
1228{
1229 auto [libname, unit] = libForInstance( aInst );
1230
1231 LIB_ENTRY& entry = m_libSymbols.at( libname );
1232 const UNIT_INFO& uinfo = entry.units[unit - 1];
1233 const ORCAD_SYMBOL_DEF& def = *uinfo.symbol;
1234
1235 ORCAD_BBOX bb = def.bbox.value_or( ORCAD_BBOX() );
1236 int w = bb.x2 - bb.x1;
1237 int h = bb.y2 - bb.y1;
1238 int ori = OrcadOrientOf( aInst.rotation, aInst.mirror );
1239 VECTOR2I offset = OrcadOrientOffset( ori, w, h );
1240 VECTOR2I pos = OrcadDbuToIu( aInst.x + offset.x, aInst.y + offset.y );
1241
1242 // Transformed def hot points must land exactly on T0x10 absolute pin positions;
1243 // mismatch means wrong variant/orientation data.
1244 if( !aInst.pins.empty() && aInst.pins.size() == def.pins.size() )
1245 {
1246 bool match = true;
1247
1248 for( size_t i = 0; i < def.pins.size(); ++i )
1249 {
1250 VECTOR2I calc = OrcadTransformPoint( ori, w, h, aInst.x, aInst.y, def.pins[i].hotptX,
1251 def.pins[i].hotptY );
1252
1253 if( calc.x != aInst.pins[i].x || calc.y != aInst.pins[i].y )
1254 {
1255 match = false;
1256 break;
1257 }
1258 }
1259
1260 if( !match )
1261 {
1262 if( def.synthesized )
1263 {
1264 note( wxString::Format( _( "Page %s: %s uses a synthesized placeholder at a "
1265 "different orientation; pin layout approximate." ),
1266 FromOrcadString( aPage.name ),
1267 FromOrcadString( aInst.reference ) ) );
1268 }
1269 else
1270 {
1271 warn( wxString::Format( _( "Page %s: %s pin positions mismatch (orientation %d); "
1272 "geometry may be off." ),
1273 FromOrcadString( aPage.name ),
1274 FromOrcadString( aInst.reference ), ori ) );
1275 }
1276 }
1277 }
1278
1279 LIB_SYMBOL* libSymbol = kicadSymbolFor( libname );
1280
1281 if( !libSymbol )
1282 return;
1283
1284 LIB_ID libId( wxString::FromUTF8( LIB_NICK ),
1285 LIB_ID::FixIllegalChars( FromOrcadString( libname ), false ).wx_str() );
1286 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &aSheetPath, unit, 0, pos );
1287
1288 symbol->SetOrientation( toKicadOrientation( ori ) );
1289
1290 std::map<std::string, std::string> props = effectiveProps( aInst, def );
1291
1292 std::string value = aInst.value;
1293
1294 if( value.empty() )
1295 {
1296 auto vIt = props.find( "Value" );
1297
1298 if( vIt != props.end() )
1299 value = vIt->second;
1300 }
1301
1302 if( value.empty() )
1303 value = entry.name;
1304
1305 std::string footprint;
1306 auto fIt = props.find( "PCB Footprint" );
1307
1308 if( fIt != props.end() )
1309 footprint = fIt->second;
1310
1311 if( footprint.empty() )
1312 footprint = entry.footprint;
1313
1314 placeSymbolFields( symbol, aInst, def, ori, value, footprint );
1315
1316 wxString reference = resolveReference( aInst );
1317
1318 symbol->SetRef( &aSheetPath, reference );
1319 symbol->SetUnitSelection( &aSheetPath, unit );
1320
1321 // Parts without any pins (fiducial markers, logos, labels, mounting hardware) have no
1322 // electrical presence: keep them out of the board update so they are not proposed as
1323 // missing footprints. They stay in the BOM -- sockets and screws are real line items.
1324 if( def.pins.empty() && aInst.pins.empty() )
1325 symbol->SetExcludedFromBoard( true );
1326
1327 aScreen->Append( symbol );
1328 placeDefinitionImages( def, aInst.x, aInst.y, ori, aScreen );
1329
1330 // Placed pin w/ zero net word is unconnected; OrCAD draws no-connect marker,
1331 // reproduce it.
1332 for( const ORCAD_PIN_INST& pin : aInst.pins )
1333 {
1334 if( pin.wordA == 0 )
1335 aScreen->Append( new SCH_NO_CONNECT( OrcadDbuToIu( pin.x, pin.y ) ) );
1336 }
1337}
1338
1339
1341{
1342 wxString reference = FromOrcadString( aInst.reference );
1343
1344 // Hierarchy stream carries authoritative per-occurrence designator; overrides
1345 // stale placed "C?" template since OrCAD re-annotates without rewriting record.
1346 if( m_currentOccRefs )
1347 {
1348 auto it = m_currentOccRefs->find( aInst.dbId );
1349
1350 if( it != m_currentOccRefs->end() )
1351 {
1352 wxString occurrence = FromOrcadString( it->second );
1353
1354 if( !occurrence.IsEmpty() && !occurrence.EndsWith( wxS( "?" ) ) )
1355 reference = occurrence;
1356 }
1357 }
1358
1359 if( reference.IsEmpty() )
1360 reference = wxS( "?" );
1361
1362 return reference;
1363}
1364
1365
1367 const std::string& aNet, SCH_SCREEN* aScreen,
1368 const SCH_SHEET_PATH& aSheetPath )
1369{
1370 std::string libname = powerLibFor( aInst.name, aNet );
1371
1372 LIB_ENTRY& entry = m_libSymbols.at( libname );
1373 const ORCAD_SYMBOL_DEF* def = entry.units.front().symbol;
1374
1375 ORCAD_BBOX bb = ( def && def->bbox ) ? *def->bbox : ORCAD_BBOX{ 0, 0, 20, 10 };
1376 int w = bb.x2 - bb.x1;
1377 int h = bb.y2 - bb.y1;
1378 int ori = OrcadOrientOf( aInst.rotation, aInst.mirror );
1379 VECTOR2I offset = OrcadOrientOffset( ori, w, h );
1380
1381 bool hasBbox = aInst.bbox.x1 || aInst.bbox.y1 || aInst.bbox.x2 || aInst.bbox.y2;
1382 int bx = hasBbox ? std::min( aInst.bbox.x1, aInst.bbox.x2 ) : aInst.x;
1383 int by = hasBbox ? std::min( aInst.bbox.y1, aInst.bbox.y2 ) : aInst.y;
1384
1385 VECTOR2I pos = OrcadDbuToIu( bx + offset.x, by + offset.y );
1386
1387 m_powerCount++;
1388 wxString reference = wxString::Format( wxS( "#PWR%04d" ), m_powerCount );
1389
1390 LIB_SYMBOL* libSymbol = kicadSymbolFor( libname );
1391
1392 if( !libSymbol )
1393 return;
1394
1395 LIB_ID libId( wxString::FromUTF8( LIB_NICK ),
1396 LIB_ID::FixIllegalChars( FromOrcadString( libname ), false ).wx_str() );
1397 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &aSheetPath, 1, 0, pos );
1398
1399 symbol->SetOrientation( toKicadOrientation( ori ) );
1400
1401 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
1402 refField->SetPosition( pos + VECTOR2I( 0, schMm( 2.54 ) ) );
1403 refField->SetTextAngle( ANGLE_HORIZONTAL );
1404 refField->SetVisible( false );
1405
1406 // Net name against body; below ground-style symbol (pin in top quarter of box),
1407 // above supply one.
1408 int bymin = std::min( aInst.bbox.y1, aInst.bbox.y2 );
1409 int bymax = std::max( aInst.bbox.y1, aInst.bbox.y2 );
1410
1411 VECTOR2I pinPos = graphicPinPos( aInst );
1412
1413 symbol->SetValueFieldText( FromOrcadString( aNet ) );
1414
1415 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
1416
1417 // Net name snug against symbol body; OrCAD placed bbox padded w/ text, so anchor
1418 // near text edge for edge-to-edge gap.
1419 BOX2I body = symbol->GetBodyBoundingBox();
1420
1421 if( pinPos.y <= bymin + ( bymax - bymin ) / 4.0 )
1422 {
1423 valField->SetPosition( VECTOR2I( body.Centre().x, body.GetBottom() + schMm( 0.3 ) ) );
1425 }
1426 else
1427 {
1428 valField->SetPosition( VECTOR2I( body.Centre().x, body.GetTop() - schMm( 0.3 ) ) );
1430 }
1431
1432 // Net name reads horizontally; compensate for KiCad re-rotating field w/ flipped
1433 // (90/270) power symbol.
1434 valField->SetTextAngle( symbol->GetTransform().y1 ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
1436 valField->SetVisible( true );
1437
1438 SCH_FIELD* fpField = symbol->GetField( FIELD_T::FOOTPRINT );
1439 fpField->SetPosition( pos );
1440 fpField->SetVisible( false );
1441
1442 SCH_FIELD* dsField = symbol->GetField( FIELD_T::DATASHEET );
1443 dsField->SetPosition( pos );
1444 dsField->SetVisible( false );
1445
1446 symbol->SetRef( &aSheetPath, reference );
1447 symbol->SetUnitSelection( &aSheetPath, 1 );
1448
1449 aScreen->Append( symbol );
1450}
1451
1452
1454 const ORCAD_SYMBOL_DEF& aDef, int aOrient,
1455 const std::string& aValue,
1456 const std::string& aFootprint )
1457{
1458 // Placed bbox includes OrCAD displayed text; true body box = orientation
1459 // transform of cache def bbox.
1460 ORCAD_BBOX bb = aDef.bbox.value_or( ORCAD_BBOX() );
1461 int w = bb.x2 - bb.x1;
1462 int h = bb.y2 - bb.y1;
1463
1464 VECTOR2I c1 = OrcadTransformPoint( aOrient, w, h, aInst.x, aInst.y, bb.x1, bb.y1 );
1465 VECTOR2I c2 = OrcadTransformPoint( aOrient, w, h, aInst.x, aInst.y, bb.x2, bb.y2 );
1466
1467 int bxmin = std::min( c1.x, c2.x );
1468 int bxmax = std::max( c1.x, c2.x );
1469 int bymin = std::min( c1.y, c2.y );
1470 int bymax = std::max( c1.y, c2.y );
1471
1472 int cxm = ( bxmin + bxmax ) * ORCAD_IU_PER_DBU / 2;
1473 int cym = ( bymin + bymax ) * ORCAD_IU_PER_DBU / 2;
1474
1475 // Fields always read horizontally; KiCad rotates field text w/ symbol, so
1476 // 90/270 placement gets compensating field angle.
1477 int angle = ORCAD_ORIENT_TABLE[aOrient & 7].angle;
1478 bool vertical = angle == 90 || angle == 270;
1479 EDA_ANGLE fieldAngle = vertical ? ANGLE_VERTICAL : ANGLE_HORIZONTAL;
1480
1481 // Visibility follows source display mode; bit 0x100 is field visible flag.
1482 bool showRef = false;
1483 bool showVal = false;
1484
1485 for( const ORCAD_DISPLAY_PROP& dp : aInst.displayProps )
1486 {
1487 bool visible = ( dp.dispMode & 0x100 ) != 0;
1488
1489 if( dp.name == "Part Reference" )
1490 showRef = visible;
1491 else if( dp.name == "Value" )
1492 showVal = visible;
1493 }
1494
1495 aSymbol->SetValueFieldText( FromOrcadString( aValue ) );
1496
1497 SCH_FIELD* refField = aSymbol->GetField( FIELD_T::REFERENCE );
1498 SCH_FIELD* valField = aSymbol->GetField( FIELD_T::VALUE );
1499
1500 // Vertical two-pin passives stack Reference/Value right of body, centered;
1501 // everything else gets Reference above, Value below.
1502 bool side = !vertical && aDef.pins.size() == 2 && aInst.pins.size() == 2
1503 && std::abs( aInst.pins[0].y - aInst.pins[1].y )
1504 >= std::abs( aInst.pins[0].x - aInst.pins[1].x );
1505
1506 if( side )
1507 {
1508 int sx = bxmax * ORCAD_IU_PER_DBU + schMm( 1.0 );
1509 int ry = showVal ? cym - schMm( 0.9 ) : cym;
1510
1511 refField->SetPosition( VECTOR2I( sx, ry ) );
1512 refField->SetTextAngle( ANGLE_HORIZONTAL );
1514 refField->SetVisible( showRef );
1515
1516 valField->SetPosition( VECTOR2I( sx, cym + schMm( 0.9 ) ) );
1517 valField->SetTextAngle( ANGLE_HORIZONTAL );
1519 valField->SetVisible( showVal );
1520 }
1521 else
1522 {
1523 refField->SetPosition( VECTOR2I( cxm, bymin * ORCAD_IU_PER_DBU - schMm( 1.4 ) ) );
1524 refField->SetTextAngle( fieldAngle );
1526 refField->SetVisible( showRef );
1527
1528 valField->SetPosition( VECTOR2I( cxm, bymax * ORCAD_IU_PER_DBU + schMm( 1.4 ) ) );
1529 valField->SetTextAngle( fieldAngle );
1531 valField->SetVisible( showVal );
1532 }
1533
1534 // Keep the source (OrCAD) footprint name as metadata in a user field, but leave
1535 // the KiCad Footprint field empty: OrCAD names are not valid KiCad footprint
1536 // library IDs, and a non-matching Footprint field blocks linking the symbol to
1537 // an imported board footprint via "Update PCB -> re-link by reference".
1538 aSymbol->SetFootprintFieldText( wxEmptyString );
1539
1540 wxString fpName = FromOrcadString( aFootprint );
1541
1542 if( !fpName.IsEmpty() )
1543 {
1544 SCH_FIELD fpMeta( aSymbol, FIELD_T::USER, wxS( "OrCAD Footprint" ) );
1545 fpMeta.SetText( fpName );
1546 fpMeta.SetPosition( aSymbol->GetPosition() );
1547 fpMeta.SetVisible( false );
1548 aSymbol->AddField( fpMeta );
1549 }
1550
1551 SCH_FIELD* fpField = aSymbol->GetField( FIELD_T::FOOTPRINT );
1552 fpField->SetPosition( aSymbol->GetPosition() );
1553 fpField->SetVisible( false );
1554
1555 SCH_FIELD* dsField = aSymbol->GetField( FIELD_T::DATASHEET );
1556 dsField->SetPosition( aSymbol->GetPosition() );
1557 dsField->SetVisible( false );
1558
1559 // Display-prop positions are canvas-space offsets from instance anchor; field
1560 // text does not rotate w/ symbol, so not run through body orientation transform.
1561 std::map<std::string, std::pair<VECTOR2I, const ORCAD_DISPLAY_PROP*>> shown;
1562
1563 for( const ORCAD_DISPLAY_PROP& dp : aInst.displayProps )
1564 shown[dp.name] = { OrcadDbuToIu( aInst.x + dp.x, aInst.y + dp.y ), &dp };
1565
1566 // KiCad re-rotates field text when parent transform flips X/Y (GetDrawRotation),
1567 // so store angle that renders property's own text angle after flip.
1568 bool symbolFlips = aSymbol->GetTransform().y1 != 0;
1569
1570 auto storedFieldAngle = [&]( const ORCAD_DISPLAY_PROP* aDp ) -> EDA_ANGLE
1571 {
1572 bool textVertical = ( aDp->rotation & 1 ) != 0;
1573 return ( textVertical != symbolFlips ) ? ANGLE_VERTICAL : ANGLE_HORIZONTAL;
1574 };
1575
1576 // OrCAD carries explicit reference/value field positions; honor them so fields
1577 // land where source placed them, not computed fallback.
1578 auto applyDisplayPos = [&]( SCH_FIELD* aField, const char* aName )
1579 {
1580 auto it = shown.find( aName );
1581
1582 if( it == shown.end() )
1583 return;
1584
1585 const ORCAD_DISPLAY_PROP* dp = it->second.second;
1586 int size = textSizeIU( dp->fontIdx );
1587
1588 // OrCAD stores text top-left corner; text grows right and down, so reference
1589 // stacks above value clear of body.
1590 aField->SetPosition( it->second.first );
1591 aField->SetTextSize( VECTOR2I( size, size ) );
1592 applyFont( aField, dp->fontIdx );
1593 aField->SetTextColor( OrcadColor( dp->color ) );
1594 aField->SetTextAngle( storedFieldAngle( dp ) );
1597 };
1598
1599 applyDisplayPos( refField, "Part Reference" );
1600 applyDisplayPos( valField, "Value" );
1601
1602 for( const auto& [propName, propValue] : effectiveProps( aInst, aDef ) )
1603 {
1604 if( propName == "Value" || propName == "PCB Footprint" || isBookkeepingProp( propName ) )
1605 continue;
1606
1607 // An unset part property reads back empty or as the literal "<PropertyName>"
1608 // placeholder, neither of which Capture draws
1609 if( propValue.empty() || propValue == "<" + propName + ">" )
1610 continue;
1611
1612 // OrCAD carries Description as an ordinary part property and never draws it
1613 if( propName == "Description" )
1614 {
1615 aSymbol->GetField( FIELD_T::DESCRIPTION )->SetText( FromOrcadString( propValue ) );
1616 continue;
1617 }
1618
1619 SCH_FIELD field( aSymbol, FIELD_T::USER, FromOrcadString( propName ) );
1620 field.SetText( FromOrcadString( propValue ) );
1621
1622 auto sIt = shown.find( propName );
1623
1624 if( sIt != shown.end() )
1625 {
1626 const ORCAD_DISPLAY_PROP* dp = sIt->second.second;
1627 int size = textSizeIU( dp->fontIdx );
1628
1629 field.SetPosition( sIt->second.first );
1630 field.SetTextAngle( storedFieldAngle( dp ) );
1631 field.SetTextSize( VECTOR2I( size, size ) );
1632 applyFont( &field, dp->fontIdx );
1633 field.SetTextColor( OrcadColor( dp->color ) );
1636 field.SetVisible( ( dp->dispMode & 0x100 ) != 0 );
1637 }
1638 else
1639 {
1640 field.SetPosition( aSymbol->GetPosition() );
1641 field.SetTextSize( VECTOR2I( schMm( 1.27 ), schMm( 1.27 ) ) );
1642 field.SetVisible( false );
1643 }
1644
1645 aSymbol->AddField( field );
1646 }
1647}
1648
1649
1651{
1652 // Count references per font height; dominant height maps to KiCad default 1.27mm,
1653 // rest scale relative to it.
1654 std::map<int, int> counts;
1655 std::vector<int> order; // first-seen order breaks count ties
1656
1657 auto tally = [&]( int aFontIdx )
1658 {
1659 int height = fontHeightDbu( aFontIdx );
1660
1661 if( !height )
1662 return;
1663
1664 auto [it, isNew] = counts.try_emplace( height, 0 );
1665
1666 if( isNew )
1667 order.push_back( height );
1668
1669 it->second++;
1670 };
1671
1672 for( const ORCAD_RAW_PAGE& page : m_design.pages )
1673 {
1674 for( const ORCAD_WIRE& wire : page.wires )
1675 {
1676 for( const ORCAD_ALIAS& alias : wire.aliases )
1677 tally( alias.fontIdx );
1678 }
1679
1680 for( const ORCAD_PLACED_INSTANCE& inst : page.instances )
1681 {
1682 for( const ORCAD_DISPLAY_PROP& dp : inst.displayProps )
1683 tally( dp.fontIdx );
1684 }
1685 }
1686
1687 int best = 0;
1688 int bestCount = 0;
1689
1690 for( int height : order )
1691 {
1692 if( counts[height] > bestCount )
1693 {
1694 best = height;
1695 bestCount = counts[height];
1696 }
1697 }
1698
1699 m_fontBaselineDbu = best;
1700}
1701
1702
1703int ORCAD_CONVERTER::fontHeightDbu( int aFontIdx ) const
1704{
1705 if( aFontIdx > 0 && aFontIdx <= (int) m_design.library.fonts.size() )
1706 return std::abs( m_design.library.fonts[aFontIdx - 1].height );
1707
1708 return 0;
1709}
1710
1711
1712int ORCAD_CONVERTER::textSizeIU( int aFontIdx ) const
1713{
1714 int height = fontHeightDbu( aFontIdx );
1715
1716 if( !height )
1717 return schMm( 1.27 );
1718
1719 double mm;
1720
1721 if( m_fontBaselineDbu )
1722 {
1723 mm = std::round( 1.27 * height / m_fontBaselineDbu * 100.0 ) / 100.0;
1724 mm = std::max( 0.7, std::min( 5.0, mm ) );
1725 }
1726 else
1727 {
1728 // LOGFONTA heights = device units at 96 dpi; 1 pt = 1/72 inch.
1729 mm = std::round( height * 25.4 * 0.75 / 96.0 * 100.0 ) / 100.0;
1730 mm = std::max( 0.7, mm );
1731 }
1732
1733 return schMm( mm );
1734}
1735
1736
1737void ORCAD_CONVERTER::applyFont( EDA_TEXT* aText, int aFontIdx ) const
1738{
1739 if( !aText || aFontIdx <= 0 || aFontIdx > static_cast<int>( m_design.library.fonts.size() ) )
1740 return;
1741
1742 const ORCAD_FONT& font = m_design.library.fonts[aFontIdx - 1];
1743 aText->SetBold( font.bold );
1744 aText->SetItalic( font.italic );
1745
1746 if( !font.face.empty() )
1747 {
1748 aText->SetFont( KIFONT::FONT::GetFont( FromOrcadString( font.face ), font.bold, font.italic ) );
1749 }
1750}
1751
1752
1753std::string ORCAD_CONVERTER::SymbolId( const std::string& aName )
1754{
1755 std::string out;
1756 bool inRun = false;
1757
1758 for( unsigned char c : aName )
1759 {
1760 // 0xA0 = CP-1252 non-breaking space.
1761 bool separator = c == ':' || c == '"' || c == '/' || c == ' ' || c == '\t' || c == '\n'
1762 || c == '\r' || c == '\f' || c == '\v' || c == 0xA0;
1763
1764 if( separator )
1765 {
1766 if( !inRun )
1767 out += '_';
1768
1769 inRun = true;
1770 }
1771 else
1772 {
1773 out += (char) c;
1774 inRun = false;
1775 }
1776 }
1777
1778 size_t first = out.find_first_not_of( '_' );
1779
1780 if( first == std::string::npos )
1781 return "SYM";
1782
1783 size_t last = out.find_last_not_of( '_' );
1784 out = out.substr( first, last - first + 1 );
1785
1786 return out.empty() ? "SYM" : out;
1787}
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
constexpr Vec Centre() const
Definition box2.h:93
constexpr coord_type GetTop() const
Definition box2.h:225
constexpr coord_type GetBottom() const
Definition box2.h:218
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:244
virtual void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:282
virtual void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:279
void SetFillMode(FILL_T aFill)
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:89
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:290
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:576
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:412
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:330
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:294
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:302
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:495
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:404
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
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
Definition lib_id.cpp:205
Define a library symbol object.
Definition lib_symbol.h:114
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)
Place one power symbol: transform base = placed bbox min corner (fall back to the anchor when the bbo...
void prepareSymbols()
Pre-pass over all pages: synthesize placeholder definitions for symbols absent from the cache (one no...
void computeFontBaseline()
Find the design's dominant text height (most frequent font height over all alias and display-prop fon...
std::pair< std::string, int > libForInstance(const ORCAD_PLACED_INSTANCE &aInst)
Resolve/register the emitted lib symbol and unit number for a placed part.
static constexpr int PIN_LEN_DBU
Standard OrCAD pin length used for synthesized placeholder pins, DBU.
std::pair< const ORCAD_SYMBOL_DEF *, int > pickVariant(const ORCAD_PLACED_INSTANCE &aInst) const
The cache may hold several stale library versions of one symbol name; pick the variant whose transfor...
const std::map< uint32_t, std::string > * m_currentOccRefs
Occurrence reference designators of the scope currently being converted; set per page so a child sche...
VECTOR2I graphicPinPos(const ORCAD_GRAPHIC_INST &aInst) const
Absolute connection point (DBU) of a GraphicInst-derived instance (power symbol / off-page connector ...
const ORCAD_PACKAGE * packageFor(const ORCAD_PLACED_INSTANCE &aInst) const
The cache package backing a placement, or nullptr when the design has none.
LIB_SYMBOL * kicadSymbolFor(const std::string &aLibName)
Return the fully built KiCad LIB_SYMBOL for a registered LIB_ENTRY, building and caching it on first ...
void addSymbolPin(LIB_SYMBOL *aSymbol, const ORCAD_SYMBOL_PIN &aPin, const wxString &aNumber, int aUnit, bool aPower, const std::string &aNameOverride)
Add one pin: position = hot point, length = |start - hot|, orientation from the start-relative-to-hot...
std::map< PKG_KEY, std::pair< std::string, int > > m_pkgToLib
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)
Place one part instance on a screen: SCH_SYMBOL from the built LIB_SYMBOL (flattened-copy constructor...
ORCAD_DESIGN & m_design
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)
KiCad-style field placement for a placed part, always reading horizontally (fields rotate with the sy...
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. Helper of addSymbolPrimitive().
int textSizeIU(int aFontIdx) const
Text size in IU for a font reference: 1.27 mm when the height is unknown; with a baseline,...
std::map< std::string, LIB_ENTRY > m_libSymbols
keyed by emitted lib name
void applyFont(EDA_TEXT *aText, int aFontIdx) const
static int toKicadOrientation(int aOrient)
Map an orientation code to the composed SYMBOL_ORIENTATION_T value for SCH_SYMBOL::SetOrientation(): ...
int m_powerCount
"#PWR%04d" counter
void placeDefinitionImages(const ORCAD_SYMBOL_DEF &aDefinition, int aBaseX, int aBaseY, int aOrient, SCH_SCREEN *aScreen)
wxString resolveReference(const ORCAD_PLACED_INSTANCE &aInst) const
Resolve a placed instance's reference designator, falling back to the Hierarchy-stream occurrence des...
std::string unitLetter(const ORCAD_PLACED_INSTANCE &aInst) const
Unit discriminator of an instance: the part of pkgName between the package base name and the '....
static std::string SymbolId(const std::string &aName)
LIB_ID-safe symbol name: ':', '"', '/' and whitespace runs -> '_'; "SYM" if empty.
void addSymbolPrimitive(LIB_SYMBOL *aSymbol, const ORCAD_PRIMITIVE &aPrim, int aUnit, int aColor, int aOffsetX=0, int aOffsetY=0)
Add one body primitive to a LIB_SYMBOL unit on LAYER_DEVICE.
ORCAD_SYMBOL_DEF synthesizeSymbol(const std::string &aPkgName, const std::vector< const ORCAD_PLACED_INSTANCE * > &aInstances) const
Best-effort placeholder for a cache-less symbol, built from the instances' T0x10 absolute pin records...
int fontHeightDbu(int aFontIdx) const
|lfHeight| of a 1-based font index; 0 for invalid/default indices.
std::string powerLibFor(const std::string &aSymbolName, const std::string &aNetName)
Resolve/register the power lib symbol for a power-symbol instance.
void warn(const wxString &aMsg)
Conversion problem: REPORTER at RPT_SEVERITY_WARNING.
std::vector< LIB_SYMBOL * > BuildSymbolLibrary()
Build KiCad library symbols from the design cache and packages alone (no schematic pages),...
static constexpr const char * LIB_NICK
Symbol library nickname used in every emitted LIB_ID.
std::tuple< std::string, std::string, int > PKG_KEY
(sourcePackage-or-pkgName, pkgName, variant index) -> (lib name, unit number).
std::map< std::string, std::string > effectiveProps(const ORCAD_PLACED_INSTANCE &aInst, const ORCAD_SYMBOL_DEF &aDef) const
Properties in effect for a placement.
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
virtual void SetUnit(int aUnit)
Definition sch_item.h:232
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void SetPosition(const VECTOR2I &aPos) override
Definition sch_shape.h:85
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_shape.cpp:98
void AddPoint(const VECTOR2I &aPosition)
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:69
void SetRef(const SCH_SHEET_PATH *aSheet, const wxString &aReference)
Set the reference for the given sheet path for this symbol.
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:914
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.
BOX2I GetBodyBoundingBox() const override
Return a bounding box for the symbol body but not the pins or fields.
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.
Simple container to manage line stroke parameters.
const TRANSFORM & GetTransform() const
Definition symbol.h:243
wxString wx_str() const
Definition utf8.cpp:41
#define _(s)
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
FILL_T
Definition eda_shape.h:59
@ NO_FILL
Definition eda_shape.h:60
@ REVERSE_HATCH
Definition eda_shape.h:65
@ HATCH
Definition eda_shape.h:64
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:62
@ CROSS_HATCH
Definition eda_shape.h:66
@ LAYER_DEVICE
Definition layer_ids.h:472
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
ORCAD_CONVERTER turns a parsed ORCAD_DESIGN into KiCad schematic objects.
constexpr ORCAD_ORIENT_ENTRY ORCAD_ORIENT_TABLE[8]
Orientation table, indexed by the 3-bit orientation code.
VECTOR2I OrcadDbuToIu(int aX, int aY)
FILL_T OrcadFillType(int aFillStyle, int aHatchStyle)
VECTOR2I OrcadTransformPoint(int aOrient, int aWidth, int aHeight, int aBaseX, int aBaseY, int aPx, int aPy)
Absolute canvas position (DBU) of symbol-space point (aPx, aPy) for an instance with transform base (...
KIGFX::COLOR4D OrcadColor(int aColorIndex)
int OrcadOrientOf(int aRotation, bool aMirror)
Compose the 3-bit orientation code from the rotation bits and mirror bit.
constexpr int ORCAD_IU_PER_DBU
Schematic internal units per OrCAD DBU: 10 mil * 254 IU/mil.
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).
FILL_T OrcadFillType(int aFillStyle, int aHatchStyle)
int OrcadLineWidthIu(int aWidth)
LINE_STYLE OrcadLineStyle(int aStyle)
ORCAD_PORT_TYPE
Pin electrical type codes (u32 portType field of a symbol pin).
@ ORCAD_ST_LIBRARY_PART
@ ORCAD_ST_GLOBAL_SYMBOL
power symbol definition
wxString FromOrcadString(const std::string &aText)
Decode raw stream bytes (Windows-1252) into a wxString for UI/UX use.
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
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
LINE_STYLE
Dashed line types.
Net alias attached to a wire (becomes a local label).
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
One unit of an emitted multi-unit symbol.
const ORCAD_SYMBOL_DEF * symbol
std::vector< std::string > pinNumbers
device pin-number map for this unit
std::string letter
unit discriminator, e.g. "A", "-16", "B:Convert"
One device of a package: the pin-number map of a unit (structure type 32).
std::vector< std::string > pinNumbers
std::string unitRef
One displayed property of an instance (Part Reference, Value, user fields).
int fontIdx
1-based into ORCAD_LIBRARY_INFO::fonts; 0 = default
std::string name
resolved property name (empty when index invalid)
One text font from the Library stream (a Win32 LOGFONTA record, 60 bytes: lfHeight i32 at +0,...
bool bold
lfWeight >= 600
std::string face
Shared body of Port / Global / OffPageConnector / TitleBlock / ERCObject and the Graphic*Inst shapes ...
int rotation
0..3 quarter turns
std::string name
cache symbol name
One entry of the 8-orientation placement table.
int8_t c
int8_t a
int8_t d
int8_t b
A package: refdes prefix, footprint and per-unit devices (structure type 31).
std::string refDes
std::vector< ORCAD_DEVICE > devices
std::string pcbFootprint
T0x10: one pin of a placed instance, carrying the pin's absolute page position (the connection point ...
A placed part instance on a page (structure type 13).
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
std::map< std::string, std::string > props
short-prefix property pairs
bool mirror
orientation bit 2
std::string value
resolved Part Value
std::vector< ORCAD_PIN_INST > pins
successfully parsed T0x10 records
Integer point in OrCAD DBU.
One graphic primitive of a symbol body or nested page graphic.
int fillStyle
0 solid, 1 none, 2 hatch pattern
std::string text
kind == TEXT
std::vector< ORCAD_PRIMITIVE > children
kind == GROUP, translated by (x1, y1)
std::vector< ORCAD_POINT > points
polygon/polyline/bezier vertices
ORCAD_PRIM_KIND kind
int lineStyle
0 solid, 1 dash, 2 dot, 3 dash-dot, 4 dash-dot-dot, 5 default
std::optional< ORCAD_POINT > start
arc start point
std::optional< ORCAD_POINT > end
arc end point
int lineWidth
Capture width enum: 0 thin, 1 medium, 2 wide, 3 default.
One parsed 'Views/<folder>/Pages/<page>' stream, raw structure lists in stream order.
std::string name
std::vector< ORCAD_WIRE > wires
std::vector< ORCAD_PLACED_INSTANCE > instances
A symbol definition from the design Cache (LibraryPart / GlobalSymbol / PortSymbol / OffPageSymbol / ...
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::optional< ORCAD_BBOX > bbox
symbol-space body box
std::vector< ORCAD_SYMBOL_DEF > variants
Stale same-name cache entries (older library versions), in cache order, EXCLUDING this entry itself.
int generalFlags
LibraryPart GeneralProperties flags (-1 = absent); bit0 = pin names visible, bit2 = pin numbers hidde...
One pin of a symbol definition (structure types 26/27).
ORCAD_PORT_TYPE portType
One wire or bus segment.
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
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683