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
597std::pair<std::string, int> ORCAD_CONVERTER::libForInstance( const ORCAD_PLACED_INSTANCE& aInst )
598{
599 auto [sym, vi] = pickVariant( aInst );
600
601 std::string srcOrPkg = !aInst.sourcePackage.empty() ? aInst.sourcePackage : aInst.pkgName;
602 PKG_KEY key{ srcOrPkg, aInst.pkgName, vi };
603
604 auto found = m_pkgToLib.find( key );
605
606 if( found != m_pkgToLib.end() )
607 return found->second;
608
609 std::string base = !aInst.sourcePackage.empty()
610 ? aInst.sourcePackage
611 : aInst.pkgName.substr( 0, aInst.pkgName.find( '.' ) );
612
613 std::string letter = unitLetter( aInst );
614
615 if( !sym )
616 {
617 warn( wxString::Format( _( "No cached symbol for '%s' (%s); placeholder emitted." ),
618 FromOrcadString( aInst.pkgName ),
619 FromOrcadString( aInst.reference ) ) );
620
621 // Register empty definition so unit references stable storage.
622 ORCAD_SYMBOL_DEF& slot = m_design.symbols[aInst.pkgName];
623
624 if( slot.name.empty() )
625 {
626 slot.typeId = 0;
627 slot.name = aInst.pkgName;
628 }
629
630 sym = &slot;
631 }
632
633 const ORCAD_PACKAGE* pkg = nullptr;
634 auto pkgIt = m_design.packages.find( base );
635
636 if( pkgIt != m_design.packages.end() )
637 pkg = &pkgIt->second;
638
639 // Pin numbers from package device whose unitRef equals BARE unit letter; ':View'
640 // suffix only selects DeMorgan graphics, views share Normal pin map.
641 std::vector<std::string> pinNumbers;
642 std::string bare = letter.substr( 0, letter.find( ':' ) );
643
644 if( pkg )
645 {
646 bool foundDevice = false;
647
648 for( const ORCAD_DEVICE& d : pkg->devices )
649 {
650 if( d.unitRef == bare )
651 {
652 pinNumbers = d.pinNumbers;
653 foundDevice = true;
654 break;
655 }
656 }
657
658 if( !foundDevice && !pkg->devices.empty() && bare.empty() )
659 pinNumbers = pkg->devices.front().pinNumbers;
660 }
661
662 std::string libname = SymbolId( base );
663
664 if( vi )
665 libname += "_v" + std::to_string( vi + 1 );
666
667 LIB_ENTRY& ls = m_libSymbols[libname];
668
669 if( ls.name.empty() )
670 {
671 ls.name = libname;
672 ls.refPrefix = ( pkg && !pkg->refDes.empty() ) ? pkg->refDes : "U";
673 ls.footprint = pkg ? pkg->pcbFootprint : "";
674 }
675
676 int unitNo = 0;
677
678 for( size_t i = 0; i < ls.units.size(); ++i )
679 {
680 if( ls.units[i].letter == letter )
681 {
682 unitNo = (int) i + 1;
683 break;
684 }
685 }
686
687 if( unitNo == 0 )
688 {
689 UNIT_INFO unit;
690 unit.letter = letter;
691 unit.symbol = sym;
692 unit.pinNumbers = pinNumbers;
693 ls.units.push_back( std::move( unit ) );
694
695 std::stable_sort( ls.units.begin(), ls.units.end(),
696 []( const UNIT_INFO& a, const UNIT_INFO& b )
697 {
698 return a.letter < b.letter;
699 } );
700
701 for( size_t i = 0; i < ls.units.size(); ++i )
702 {
703 if( ls.units[i].letter == letter )
704 {
705 unitNo = (int) i + 1;
706 break;
707 }
708 }
709
710 // Inserting a unit renumbers later-sorting letters, so every map entry for
711 // this lib symbol is stale; rebuild them.
712 for( auto mapIt = m_pkgToLib.begin(); mapIt != m_pkgToLib.end(); )
713 {
714 if( mapIt->second.first == libname )
715 mapIt = m_pkgToLib.erase( mapIt );
716 else
717 ++mapIt;
718 }
719
720 for( size_t i = 0; i < ls.units.size(); ++i )
721 {
722 std::string pkgName = !aInst.sourcePackage.empty()
723 ? aInst.sourcePackage + ls.units[i].letter + ".Normal"
724 : aInst.pkgName;
725
726 m_pkgToLib[PKG_KEY{ srcOrPkg, pkgName, vi }] = { libname, (int) i + 1 };
727 }
728 }
729
730 m_pkgToLib[key] = { libname, unitNo };
731 return { libname, unitNo };
732}
733
734
735std::string ORCAD_CONVERTER::powerLibFor( const std::string& aSymbolName,
736 const std::string& aNetName )
737{
738 // Keyed by net name, not symbol name; users rename power ports and one symbol
739 // shape can serve several nets.
740 std::string libname = "PWR_" + SymbolId( aNetName );
741
742 if( m_libSymbols.count( libname ) )
743 return libname;
744
745 const ORCAD_SYMBOL_DEF* sym = nullptr;
746 auto it = m_design.symbols.find( aSymbolName );
747
748 if( it != m_design.symbols.end() )
749 {
750 sym = &it->second;
751 }
752 else
753 {
754 // Uncached power symbol; register empty definition (no graphics).
755 ORCAD_SYMBOL_DEF& slot = m_design.symbols[aSymbolName];
756
757 if( slot.name.empty() )
758 {
760 slot.name = aSymbolName;
761 }
762
763 sym = &slot;
764 }
765
766 LIB_ENTRY& ls = m_libSymbols[libname];
767 ls.name = libname;
768 ls.isPower = true;
769 ls.powerNet = aNetName;
770 ls.refPrefix = "#PWR";
771
772 UNIT_INFO unit;
773 unit.symbol = sym;
774 unit.pinNumbers = { "1" };
775 ls.units.push_back( std::move( unit ) );
776
777 return libname;
778}
779
780
781LIB_SYMBOL* ORCAD_CONVERTER::kicadSymbolFor( const std::string& aLibName )
782{
783 auto it = m_libSymbols.find( aLibName );
784
785 if( it == m_libSymbols.end() )
786 return nullptr;
787
788 LIB_ENTRY& entry = it->second;
789
790 if( entry.kicadSymbol )
791 return entry.kicadSymbol.get();
792
793 // OrCAD cache names can contain LIB_ID-illegal chars (e.g. backslashes from
794 // library path prefixes).
795 wxString name = LIB_ID::FixIllegalChars( FromOrcadString( entry.name ), false ).wx_str();
796
797 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( name );
798 symbol->SetLibId( LIB_ID( wxString::FromUTF8( LIB_NICK ), name ) );
799 symbol->SetPinNameOffset( schMm( 0.254 ) );
800
801 // Pin number/name visibility from LibraryPart flags; bit0=numbers visible,
802 // bit2=names hidden.
803 for( const UNIT_INFO& unit : entry.units )
804 {
805 if( unit.symbol && unit.symbol->generalFlags >= 0 )
806 {
807 int flags = unit.symbol->generalFlags;
808 symbol->SetShowPinNumbers( ( flags & 0x01 ) != 0 );
809 symbol->SetShowPinNames( ( flags & 0x04 ) == 0 );
810 break;
811 }
812 }
813
814 if( entry.isPower )
815 symbol->SetGlobalPower();
816
817 if( (int) entry.units.size() > 1 )
818 symbol->SetUnitCount( (int) entry.units.size(), false );
819
820 const ORCAD_SYMBOL_DEF* u0 = entry.units.empty() ? nullptr : entry.units.front().symbol;
821 ORCAD_BBOX bb = ( u0 && u0->bbox ) ? *u0->bbox : ORCAD_BBOX();
822
823 // Default field spot above body; in-memory symbol space Y-down, so above = more
824 // negative Y.
825 int topY = bb.y1 * ORCAD_IU_PER_DBU - schMm( 2.54 );
826
827 SCH_FIELD& refField = symbol->GetReferenceField();
828 SCH_FIELD& valField = symbol->GetValueField();
829
830 if( entry.isPower )
831 {
832 refField.SetText( wxS( "#PWR" ) );
833 refField.SetVisible( false );
834 refField.SetTextPos( VECTOR2I( 0, topY ) );
835
836 valField.SetText( FromOrcadString( entry.powerNet ) );
837 valField.SetVisible( true );
838 valField.SetTextPos( VECTOR2I( 0, topY ) );
839 }
840 else
841 {
842 refField.SetText( FromOrcadString( entry.refPrefix ) );
843 refField.SetVisible( true );
844 refField.SetTextPos( VECTOR2I( 0, topY ) );
845
846 valField.SetText( name );
847 valField.SetVisible( true );
848 valField.SetTextPos( VECTOR2I( 0, topY - schMm( 2.54 ) ) );
849 }
850
851 // OrCAD footprint names are not valid KiCad footprint library IDs; the source
852 // name is preserved as a user field on each placed instance instead. Leaving
853 // the Footprint field empty lets imported symbols link to imported board
854 // footprints via "Update PCB from Schematic -> re-link by reference".
855 symbol->GetFootprintField().SetVisible( false );
856 symbol->GetDatasheetField().SetVisible( false );
857
858 for( size_t ui = 0; ui < entry.units.size(); ++ui )
859 {
860 const UNIT_INFO& unit = entry.units[ui];
861 int unitNo = (int) ui + 1;
862
863 if( !unit.symbol )
864 continue;
865
866 for( const ORCAD_PRIMITIVE& prim : unit.symbol->primitives )
867 addSymbolPrimitive( symbol.get(), prim, unitNo, unit.symbol->color );
868
869 for( size_t pi = 0; pi < unit.symbol->pins.size(); ++pi )
870 {
871 const ORCAD_SYMBOL_PIN& sourcePin = unit.symbol->pins[pi];
872 int position =
873 unit.symbol->pins[pi].position >= 0 ? unit.symbol->pins[pi].position : static_cast<int>( pi );
874 wxString number = position < static_cast<int>( unit.pinNumbers.size() )
875 ? FromOrcadString( unit.pinNumbers[position] )
876 : wxString::Format( wxS( "%d" ), (int) pi + 1 );
877
878 if( !sourcePin.name.empty()
879 && std::count( unit.pinNumbers.begin(), unit.pinNumbers.end(), sourcePin.name ) == 1 )
880 number = FromOrcadString( sourcePin.name );
881
882 addSymbolPin( symbol.get(), sourcePin, number, unitNo, entry.isPower,
883 entry.isPower ? entry.powerNet : std::string() );
884 }
885 }
886
887 entry.kicadSymbol = std::move( symbol );
888 return entry.kicadSymbol.get();
889}
890
891
892void ORCAD_CONVERTER::addSymbolPrimitive( LIB_SYMBOL* aSymbol, const ORCAD_PRIMITIVE& aPrim, int aUnit, int aColor,
893 int aOffsetX, int aOffsetY )
894{
895 // Cache defs and in-memory symbol space both Y-down; .kicad_sch writer flips to
896 // file Y-up itself.
897 auto toX = [aOffsetX]( int aV )
898 {
899 return ( aV + aOffsetX ) * ORCAD_IU_PER_DBU;
900 };
901 auto toY = [aOffsetY]( int aV )
902 {
903 return ( aV + aOffsetY ) * ORCAD_IU_PER_DBU;
904 };
905
906 switch( aPrim.kind )
907 {
909 for( const ORCAD_PRIMITIVE& child : aPrim.children )
910 addSymbolPrimitive( aSymbol, child, aUnit, aColor, aOffsetX + aPrim.x1, aOffsetY + aPrim.y1 );
911
912 break;
913
915 {
917 shape->SetPosition( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ) );
918 shape->SetEnd( VECTOR2I( toX( aPrim.x2 ), toY( aPrim.y2 ) ) );
919 shape->SetStroke( strokeFor( aPrim, aColor ) );
920 shape->SetFillMode( OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
921 shape->SetUnit( aUnit );
922 aSymbol->AddDrawItem( shape, false );
923 break;
924 }
925
927 {
929 shape->AddPoint( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ) );
930 shape->AddPoint( VECTOR2I( toX( aPrim.x2 ), toY( aPrim.y2 ) ) );
931 shape->SetStroke( strokeFor( aPrim, aColor ) );
933 shape->SetUnit( aUnit );
934 aSymbol->AddDrawItem( shape, false );
935 break;
936 }
937
940 {
941 if( aPrim.points.size() < 2 )
942 return;
943
945
946 for( const ORCAD_POINT& pt : aPrim.points )
947 shape->AddPoint( VECTOR2I( toX( pt.x ), toY( pt.y ) ) );
948
949 bool isPolygon = aPrim.kind == ORCAD_PRIM_KIND::POLYGON;
950
951 if( isPolygon && aPrim.points.front() != aPrim.points.back() )
952 shape->AddPoint( VECTOR2I( toX( aPrim.points.front().x ),
953 toY( aPrim.points.front().y ) ) );
954
955 shape->SetStroke( strokeFor( aPrim, aColor ) );
956 shape->SetFillMode( isPolygon ? OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) : FILL_T::NO_FILL );
957 shape->SetUnit( aUnit );
958 aSymbol->AddDrawItem( shape, false );
959 break;
960 }
961
963 {
964 if( aPrim.points.size() < 4 || ( aPrim.points.size() - 1 ) % 3 != 0 )
965 {
966 if( aPrim.points.size() >= 2 )
967 {
969
970 for( const ORCAD_POINT& pt : aPrim.points )
971 shape->AddPoint( VECTOR2I( toX( pt.x ), toY( pt.y ) ) );
972
973 shape->SetStroke( strokeFor( aPrim, aColor ) );
975 shape->SetUnit( aUnit );
976 aSymbol->AddDrawItem( shape, false );
977 }
978
979 return;
980 }
981
982 for( size_t i = 0; i + 3 < aPrim.points.size(); i += 3 )
983 {
985 shape->SetPosition( VECTOR2I( toX( aPrim.points[i].x ), toY( aPrim.points[i].y ) ) );
986 shape->SetBezierC1( VECTOR2I( toX( aPrim.points[i + 1].x ), toY( aPrim.points[i + 1].y ) ) );
987 shape->SetBezierC2( VECTOR2I( toX( aPrim.points[i + 2].x ), toY( aPrim.points[i + 2].y ) ) );
988 shape->SetEnd( VECTOR2I( toX( aPrim.points[i + 3].x ), toY( aPrim.points[i + 3].y ) ) );
989 shape->SetStroke( strokeFor( aPrim, aColor ) );
991 shape->SetUnit( aUnit );
992 aSymbol->AddDrawItem( shape, false );
993 }
994
995 break;
996 }
997
999 {
1000 double cx = ( aPrim.x1 + aPrim.x2 ) / 2.0;
1001 double cy = ( aPrim.y1 + aPrim.y2 ) / 2.0;
1002 double rx = std::abs( aPrim.x2 - aPrim.x1 ) / 2.0;
1003 double ry = std::abs( aPrim.y2 - aPrim.y1 ) / 2.0;
1004
1005 if( std::abs( rx - ry ) < 0.5 )
1006 {
1008 VECTOR2I center( dbuIu( cx ), dbuIu( cy ) );
1009 shape->SetPosition( center );
1010 shape->SetEnd( center + VECTOR2I( dbuIu( rx ), 0 ) );
1011 shape->SetStroke( strokeFor( aPrim, aColor ) );
1012 shape->SetFillMode( OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
1013 shape->SetUnit( aUnit );
1014 aSymbol->AddDrawItem( shape, false );
1015 }
1016 else
1017 {
1018 // Unequal radii; approximate ellipse with closed polyline.
1020
1021 for( int k = 0; k <= 32; ++k )
1022 {
1023 double a = 2.0 * M_PI * k / 32.0;
1024 shape->AddPoint( VECTOR2I( dbuIu( cx + rx * std::cos( a ) ),
1025 dbuIu( cy + ry * std::sin( a ) ) ) );
1026 }
1027
1028 shape->SetStroke( strokeFor( aPrim, aColor ) );
1029 shape->SetFillMode( OrcadFillType( aPrim.fillStyle, aPrim.hatchStyle ) );
1030 shape->SetUnit( aUnit );
1031 aSymbol->AddDrawItem( shape, false );
1032 }
1033
1034 break;
1035 }
1036
1037 case ORCAD_PRIM_KIND::ARC: addSymbolArc( aSymbol, aPrim, aUnit, aColor, aOffsetX, aOffsetY ); break;
1038
1040 {
1041 SCH_TEXT* text = new SCH_TEXT( VECTOR2I( toX( aPrim.x1 ), toY( aPrim.y1 ) ),
1042 FromOrcadString( aPrim.text ), LAYER_DEVICE );
1043 int size = textSizeIU( aPrim.fontIdx );
1044 text->SetTextSize( VECTOR2I( size, size ) );
1045 applyFont( text, aPrim.fontIdx );
1046 text->SetTextColor( OrcadColor( aColor ) );
1047
1048 // OrCAD anchors comment text at top-left of bounding box.
1049 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1050 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
1051 text->SetUnit( aUnit );
1052 aSymbol->AddDrawItem( text, false );
1053 break;
1054 }
1055
1057 // Embedded images only in page graphics, not symbol bodies.
1058 break;
1059 }
1060}
1061
1062
1063void ORCAD_CONVERTER::addSymbolArc( LIB_SYMBOL* aSymbol, const ORCAD_PRIMITIVE& aPrim, int aUnit, int aColor,
1064 int aOffsetX, int aOffsetY )
1065{
1066 if( !aPrim.start || !aPrim.end )
1067 return;
1068
1069 double cx = ( aPrim.x1 + aPrim.x2 ) / 2.0;
1070 double cy = ( aPrim.y1 + aPrim.y2 ) / 2.0;
1071 double rx = std::abs( aPrim.x2 - aPrim.x1 ) / 2.0;
1072 double ry = std::abs( aPrim.y2 - aPrim.y1 ) / 2.0;
1073
1074 if( rx == 0.0 )
1075 rx = 0.01;
1076
1077 if( ry == 0.0 )
1078 ry = 0.01;
1079
1080 double a0 = std::atan2( ( aPrim.start->y - cy ) / ry, ( aPrim.start->x - cx ) / rx );
1081 double a1 = std::atan2( ( aPrim.end->y - cy ) / ry, ( aPrim.end->x - cx ) / rx );
1082
1083 // OrCAD draws arcs CCW in screen (Y-down) coords, meaning decreasing parameter
1084 // angle here.
1085 if( a1 >= a0 )
1086 a1 -= 2.0 * M_PI;
1087
1088 int steps = std::max( 4, (int) ( std::abs( a1 - a0 ) / ( M_PI / 16.0 ) ) );
1089
1090 // Polyline approximation; robust for elliptical arcs KiCad arcs cannot represent.
1092
1093 for( int k = 0; k <= steps; ++k )
1094 {
1095 double a = a0 + ( a1 - a0 ) * k / steps;
1096 shape->AddPoint(
1097 VECTOR2I( dbuIu( cx + rx * std::cos( a ) + aOffsetX ), dbuIu( cy + ry * std::sin( a ) + aOffsetY ) ) );
1098 }
1099
1100 shape->SetStroke( strokeFor( aPrim, aColor ) );
1101 shape->SetFillMode( FILL_T::NO_FILL );
1102 shape->SetUnit( aUnit );
1103 aSymbol->AddDrawItem( shape, false );
1104}
1105
1106
1108 const wxString& aNumber, int aUnit, bool aPower,
1109 const std::string& aNameOverride )
1110{
1111 int dx = aPin.startX - aPin.hotptX;
1112 int dy = aPin.startY - aPin.hotptY;
1113
1114 SCH_PIN* pin = new SCH_PIN( aSymbol );
1115
1116 // Hot point = connection point; start point = body end.
1117 pin->SetPosition( VECTOR2I( aPin.hotptX * ORCAD_IU_PER_DBU,
1118 aPin.hotptY * ORCAD_IU_PER_DBU ) );
1119 pin->SetLength( KiROUND( std::hypot( (double) dx, (double) dy ) ) * ORCAD_IU_PER_DBU );
1120
1122
1123 if( dx > 0 )
1124 orientation = PIN_ORIENTATION::PIN_RIGHT;
1125 else if( dx < 0 )
1126 orientation = PIN_ORIENTATION::PIN_LEFT;
1127 else if( dy > 0 )
1128 orientation = PIN_ORIENTATION::PIN_DOWN; // start below hot point (Y-down source)
1129 else if( dy < 0 )
1130 orientation = PIN_ORIENTATION::PIN_UP;
1131
1132 pin->SetOrientation( orientation );
1133
1134 // shapeBits bit1 = clock, bit2 = inverted dot.
1135 bool isClock = ( aPin.shapeBits & 0x2 ) != 0;
1136 bool isDot = ( aPin.shapeBits & 0x4 ) != 0;
1137
1138 if( isClock && isDot )
1140 else if( isClock )
1141 pin->SetShape( GRAPHIC_PINSHAPE::CLOCK );
1142 else if( isDot )
1143 pin->SetShape( GRAPHIC_PINSHAPE::INVERTED );
1144 else
1145 pin->SetShape( GRAPHIC_PINSHAPE::LINE );
1146
1147 if( aPower )
1148 {
1150 pin->SetVisible( false );
1151 }
1152 else
1153 {
1154 pin->SetType( pinTypeFor( aPin.portType ) );
1155 }
1156
1157 // "$PIN"-prefixed names are OrCAD auto-generated placeholders.
1158 wxString name;
1159
1160 if( !aNameOverride.empty() )
1161 name = FromOrcadString( aNameOverride );
1162 else if( !aPin.name.empty() && aPin.name.compare( 0, 4, "$PIN" ) != 0 )
1163 name = FromOrcadString( aPin.name );
1164
1165 // Avoid rendering same text twice next to pin.
1166 if( name == aNumber )
1167 name = wxEmptyString;
1168
1169 pin->SetName( name );
1170 pin->SetNumber( aNumber );
1171 pin->SetNameTextSize( schMm( 1.27 ) );
1172 pin->SetNumberTextSize( schMm( 1.27 ) );
1173 pin->SetUnit( aUnit );
1174 aSymbol->AddDrawItem( pin, false );
1175}
1176
1177
1179{
1180 switch( aOrient & 7 )
1181 {
1182 default:
1183 case 0: return SYM_ORIENT_0;
1184 case 1: return SYM_ORIENT_90;
1185 case 2: return SYM_ORIENT_180;
1186 case 3: return SYM_ORIENT_270;
1187 case 4: return SYM_ORIENT_0 + SYM_MIRROR_Y;
1188 case 5: return SYM_ORIENT_90 + SYM_MIRROR_X;
1189 case 6: return SYM_ORIENT_0 + SYM_MIRROR_X;
1190 case 7: return SYM_ORIENT_270 + SYM_MIRROR_X;
1191 }
1192}
1193
1194
1196 SCH_SCREEN* aScreen, const SCH_SHEET_PATH& aSheetPath )
1197{
1198 auto [libname, unit] = libForInstance( aInst );
1199
1200 LIB_ENTRY& entry = m_libSymbols.at( libname );
1201 const UNIT_INFO& uinfo = entry.units[unit - 1];
1202 const ORCAD_SYMBOL_DEF& def = *uinfo.symbol;
1203
1204 ORCAD_BBOX bb = def.bbox.value_or( ORCAD_BBOX() );
1205 int w = bb.x2 - bb.x1;
1206 int h = bb.y2 - bb.y1;
1207 int ori = OrcadOrientOf( aInst.rotation, aInst.mirror );
1208 VECTOR2I offset = OrcadOrientOffset( ori, w, h );
1209 VECTOR2I pos = OrcadDbuToIu( aInst.x + offset.x, aInst.y + offset.y );
1210
1211 // Transformed def hot points must land exactly on T0x10 absolute pin positions;
1212 // mismatch means wrong variant/orientation data.
1213 if( !aInst.pins.empty() && aInst.pins.size() == def.pins.size() )
1214 {
1215 bool match = true;
1216
1217 for( size_t i = 0; i < def.pins.size(); ++i )
1218 {
1219 VECTOR2I calc = OrcadTransformPoint( ori, w, h, aInst.x, aInst.y, def.pins[i].hotptX,
1220 def.pins[i].hotptY );
1221
1222 if( calc.x != aInst.pins[i].x || calc.y != aInst.pins[i].y )
1223 {
1224 match = false;
1225 break;
1226 }
1227 }
1228
1229 if( !match )
1230 {
1231 if( def.synthesized )
1232 {
1233 note( wxString::Format( _( "Page %s: %s uses a synthesized placeholder at a "
1234 "different orientation; pin layout approximate." ),
1235 FromOrcadString( aPage.name ),
1236 FromOrcadString( aInst.reference ) ) );
1237 }
1238 else
1239 {
1240 warn( wxString::Format( _( "Page %s: %s pin positions mismatch (orientation %d); "
1241 "geometry may be off." ),
1242 FromOrcadString( aPage.name ),
1243 FromOrcadString( aInst.reference ), ori ) );
1244 }
1245 }
1246 }
1247
1248 LIB_SYMBOL* libSymbol = kicadSymbolFor( libname );
1249
1250 if( !libSymbol )
1251 return;
1252
1253 LIB_ID libId( wxString::FromUTF8( LIB_NICK ),
1254 LIB_ID::FixIllegalChars( FromOrcadString( libname ), false ).wx_str() );
1255 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &aSheetPath, unit, 0, pos );
1256
1257 symbol->SetOrientation( toKicadOrientation( ori ) );
1258
1259 std::string value = aInst.value;
1260
1261 if( value.empty() )
1262 {
1263 auto vIt = aInst.props.find( "Value" );
1264
1265 if( vIt != aInst.props.end() )
1266 value = vIt->second;
1267 }
1268
1269 if( value.empty() )
1270 value = entry.name;
1271
1272 std::string footprint;
1273 auto fIt = aInst.props.find( "PCB Footprint" );
1274
1275 if( fIt != aInst.props.end() )
1276 footprint = fIt->second;
1277
1278 if( footprint.empty() )
1279 footprint = entry.footprint;
1280
1281 placeSymbolFields( symbol, aInst, def, ori, value, footprint );
1282
1283 wxString reference = resolveReference( aInst );
1284
1285 symbol->SetRef( &aSheetPath, reference );
1286 symbol->SetUnitSelection( &aSheetPath, unit );
1287
1288 // Parts without any pins (fiducial markers, logos, labels, mounting hardware) have no
1289 // electrical presence: keep them out of the board update so they are not proposed as
1290 // missing footprints. They stay in the BOM -- sockets and screws are real line items.
1291 if( def.pins.empty() && aInst.pins.empty() )
1292 symbol->SetExcludedFromBoard( true );
1293
1294 aScreen->Append( symbol );
1295 placeDefinitionImages( def, aInst.x, aInst.y, ori, aScreen );
1296
1297 // Placed pin w/ zero net word is unconnected; OrCAD draws no-connect marker,
1298 // reproduce it.
1299 for( const ORCAD_PIN_INST& pin : aInst.pins )
1300 {
1301 if( pin.wordA == 0 )
1302 aScreen->Append( new SCH_NO_CONNECT( OrcadDbuToIu( pin.x, pin.y ) ) );
1303 }
1304}
1305
1306
1308{
1309 wxString reference = FromOrcadString( aInst.reference );
1310
1311 // Hierarchy stream carries authoritative per-occurrence designator; overrides
1312 // stale placed "C?" template since OrCAD re-annotates without rewriting record.
1313 if( m_currentOccRefs )
1314 {
1315 auto it = m_currentOccRefs->find( aInst.dbId );
1316
1317 if( it != m_currentOccRefs->end() )
1318 {
1319 wxString occurrence = FromOrcadString( it->second );
1320
1321 if( !occurrence.IsEmpty() && !occurrence.EndsWith( wxS( "?" ) ) )
1322 reference = occurrence;
1323 }
1324 }
1325
1326 if( reference.IsEmpty() )
1327 reference = wxS( "?" );
1328
1329 return reference;
1330}
1331
1332
1334 const std::string& aNet, SCH_SCREEN* aScreen,
1335 const SCH_SHEET_PATH& aSheetPath )
1336{
1337 std::string libname = powerLibFor( aInst.name, aNet );
1338
1339 LIB_ENTRY& entry = m_libSymbols.at( libname );
1340 const ORCAD_SYMBOL_DEF* def = entry.units.front().symbol;
1341
1342 ORCAD_BBOX bb = ( def && def->bbox ) ? *def->bbox : ORCAD_BBOX{ 0, 0, 20, 10 };
1343 int w = bb.x2 - bb.x1;
1344 int h = bb.y2 - bb.y1;
1345 int ori = OrcadOrientOf( aInst.rotation, aInst.mirror );
1346 VECTOR2I offset = OrcadOrientOffset( ori, w, h );
1347
1348 bool hasBbox = aInst.bbox.x1 || aInst.bbox.y1 || aInst.bbox.x2 || aInst.bbox.y2;
1349 int bx = hasBbox ? std::min( aInst.bbox.x1, aInst.bbox.x2 ) : aInst.x;
1350 int by = hasBbox ? std::min( aInst.bbox.y1, aInst.bbox.y2 ) : aInst.y;
1351
1352 VECTOR2I pos = OrcadDbuToIu( bx + offset.x, by + offset.y );
1353
1354 m_powerCount++;
1355 wxString reference = wxString::Format( wxS( "#PWR%04d" ), m_powerCount );
1356
1357 LIB_SYMBOL* libSymbol = kicadSymbolFor( libname );
1358
1359 if( !libSymbol )
1360 return;
1361
1362 LIB_ID libId( wxString::FromUTF8( LIB_NICK ),
1363 LIB_ID::FixIllegalChars( FromOrcadString( libname ), false ).wx_str() );
1364 SCH_SYMBOL* symbol = new SCH_SYMBOL( *libSymbol, libId, &aSheetPath, 1, 0, pos );
1365
1366 symbol->SetOrientation( toKicadOrientation( ori ) );
1367
1368 SCH_FIELD* refField = symbol->GetField( FIELD_T::REFERENCE );
1369 refField->SetPosition( pos + VECTOR2I( 0, schMm( 2.54 ) ) );
1370 refField->SetTextAngle( ANGLE_HORIZONTAL );
1371 refField->SetVisible( false );
1372
1373 // Net name against body; below ground-style symbol (pin in top quarter of box),
1374 // above supply one.
1375 int bymin = std::min( aInst.bbox.y1, aInst.bbox.y2 );
1376 int bymax = std::max( aInst.bbox.y1, aInst.bbox.y2 );
1377
1378 VECTOR2I pinPos = graphicPinPos( aInst );
1379
1380 symbol->SetValueFieldText( FromOrcadString( aNet ) );
1381
1382 SCH_FIELD* valField = symbol->GetField( FIELD_T::VALUE );
1383
1384 // Net name snug against symbol body; OrCAD placed bbox padded w/ text, so anchor
1385 // near text edge for edge-to-edge gap.
1386 BOX2I body = symbol->GetBodyBoundingBox();
1387
1388 if( pinPos.y <= bymin + ( bymax - bymin ) / 4.0 )
1389 {
1390 valField->SetPosition( VECTOR2I( body.Centre().x, body.GetBottom() + schMm( 0.3 ) ) );
1392 }
1393 else
1394 {
1395 valField->SetPosition( VECTOR2I( body.Centre().x, body.GetTop() - schMm( 0.3 ) ) );
1397 }
1398
1399 // Net name reads horizontally; compensate for KiCad re-rotating field w/ flipped
1400 // (90/270) power symbol.
1401 valField->SetTextAngle( symbol->GetTransform().y1 ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
1403 valField->SetVisible( true );
1404
1405 SCH_FIELD* fpField = symbol->GetField( FIELD_T::FOOTPRINT );
1406 fpField->SetPosition( pos );
1407 fpField->SetVisible( false );
1408
1409 SCH_FIELD* dsField = symbol->GetField( FIELD_T::DATASHEET );
1410 dsField->SetPosition( pos );
1411 dsField->SetVisible( false );
1412
1413 symbol->SetRef( &aSheetPath, reference );
1414 symbol->SetUnitSelection( &aSheetPath, 1 );
1415
1416 aScreen->Append( symbol );
1417}
1418
1419
1421 const ORCAD_SYMBOL_DEF& aDef, int aOrient,
1422 const std::string& aValue,
1423 const std::string& aFootprint )
1424{
1425 // Placed bbox includes OrCAD displayed text; true body box = orientation
1426 // transform of cache def bbox.
1427 ORCAD_BBOX bb = aDef.bbox.value_or( ORCAD_BBOX() );
1428 int w = bb.x2 - bb.x1;
1429 int h = bb.y2 - bb.y1;
1430
1431 VECTOR2I c1 = OrcadTransformPoint( aOrient, w, h, aInst.x, aInst.y, bb.x1, bb.y1 );
1432 VECTOR2I c2 = OrcadTransformPoint( aOrient, w, h, aInst.x, aInst.y, bb.x2, bb.y2 );
1433
1434 int bxmin = std::min( c1.x, c2.x );
1435 int bxmax = std::max( c1.x, c2.x );
1436 int bymin = std::min( c1.y, c2.y );
1437 int bymax = std::max( c1.y, c2.y );
1438
1439 int cxm = ( bxmin + bxmax ) * ORCAD_IU_PER_DBU / 2;
1440 int cym = ( bymin + bymax ) * ORCAD_IU_PER_DBU / 2;
1441
1442 // Fields always read horizontally; KiCad rotates field text w/ symbol, so
1443 // 90/270 placement gets compensating field angle.
1444 int angle = ORCAD_ORIENT_TABLE[aOrient & 7].angle;
1445 bool vertical = angle == 90 || angle == 270;
1446 EDA_ANGLE fieldAngle = vertical ? ANGLE_VERTICAL : ANGLE_HORIZONTAL;
1447
1448 // Visibility follows source display mode; bit 0x100 is field visible flag.
1449 bool showRef = false;
1450 bool showVal = false;
1451
1452 for( const ORCAD_DISPLAY_PROP& dp : aInst.displayProps )
1453 {
1454 bool visible = ( dp.dispMode & 0x100 ) != 0;
1455
1456 if( dp.name == "Part Reference" )
1457 showRef = visible;
1458 else if( dp.name == "Value" )
1459 showVal = visible;
1460 }
1461
1462 aSymbol->SetValueFieldText( FromOrcadString( aValue ) );
1463
1464 SCH_FIELD* refField = aSymbol->GetField( FIELD_T::REFERENCE );
1465 SCH_FIELD* valField = aSymbol->GetField( FIELD_T::VALUE );
1466
1467 // Vertical two-pin passives stack Reference/Value right of body, centered;
1468 // everything else gets Reference above, Value below.
1469 bool side = !vertical && aDef.pins.size() == 2 && aInst.pins.size() == 2
1470 && std::abs( aInst.pins[0].y - aInst.pins[1].y )
1471 >= std::abs( aInst.pins[0].x - aInst.pins[1].x );
1472
1473 if( side )
1474 {
1475 int sx = bxmax * ORCAD_IU_PER_DBU + schMm( 1.0 );
1476 int ry = showVal ? cym - schMm( 0.9 ) : cym;
1477
1478 refField->SetPosition( VECTOR2I( sx, ry ) );
1479 refField->SetTextAngle( ANGLE_HORIZONTAL );
1481 refField->SetVisible( showRef );
1482
1483 valField->SetPosition( VECTOR2I( sx, cym + schMm( 0.9 ) ) );
1484 valField->SetTextAngle( ANGLE_HORIZONTAL );
1486 valField->SetVisible( showVal );
1487 }
1488 else
1489 {
1490 refField->SetPosition( VECTOR2I( cxm, bymin * ORCAD_IU_PER_DBU - schMm( 1.4 ) ) );
1491 refField->SetTextAngle( fieldAngle );
1493 refField->SetVisible( showRef );
1494
1495 valField->SetPosition( VECTOR2I( cxm, bymax * ORCAD_IU_PER_DBU + schMm( 1.4 ) ) );
1496 valField->SetTextAngle( fieldAngle );
1498 valField->SetVisible( showVal );
1499 }
1500
1501 // Keep the source (OrCAD) footprint name as metadata in a user field, but leave
1502 // the KiCad Footprint field empty: OrCAD names are not valid KiCad footprint
1503 // library IDs, and a non-matching Footprint field blocks linking the symbol to
1504 // an imported board footprint via "Update PCB -> re-link by reference".
1505 aSymbol->SetFootprintFieldText( wxEmptyString );
1506
1507 wxString fpName = FromOrcadString( aFootprint );
1508
1509 if( !fpName.IsEmpty() )
1510 {
1511 SCH_FIELD fpMeta( aSymbol, FIELD_T::USER, wxS( "OrCAD Footprint" ) );
1512 fpMeta.SetText( fpName );
1513 fpMeta.SetPosition( aSymbol->GetPosition() );
1514 fpMeta.SetVisible( false );
1515 aSymbol->AddField( fpMeta );
1516 }
1517
1518 SCH_FIELD* fpField = aSymbol->GetField( FIELD_T::FOOTPRINT );
1519 fpField->SetPosition( aSymbol->GetPosition() );
1520 fpField->SetVisible( false );
1521
1522 SCH_FIELD* dsField = aSymbol->GetField( FIELD_T::DATASHEET );
1523 dsField->SetPosition( aSymbol->GetPosition() );
1524 dsField->SetVisible( false );
1525
1526 // Display-prop positions are canvas-space offsets from instance anchor; field
1527 // text does not rotate w/ symbol, so not run through body orientation transform.
1528 std::map<std::string, std::pair<VECTOR2I, const ORCAD_DISPLAY_PROP*>> shown;
1529
1530 for( const ORCAD_DISPLAY_PROP& dp : aInst.displayProps )
1531 shown[dp.name] = { OrcadDbuToIu( aInst.x + dp.x, aInst.y + dp.y ), &dp };
1532
1533 // KiCad re-rotates field text when parent transform flips X/Y (GetDrawRotation),
1534 // so store angle that renders property's own text angle after flip.
1535 bool symbolFlips = aSymbol->GetTransform().y1 != 0;
1536
1537 auto storedFieldAngle = [&]( const ORCAD_DISPLAY_PROP* aDp ) -> EDA_ANGLE
1538 {
1539 bool textVertical = ( aDp->rotation & 1 ) != 0;
1540 return ( textVertical != symbolFlips ) ? ANGLE_VERTICAL : ANGLE_HORIZONTAL;
1541 };
1542
1543 // OrCAD carries explicit reference/value field positions; honor them so fields
1544 // land where source placed them, not computed fallback.
1545 auto applyDisplayPos = [&]( SCH_FIELD* aField, const char* aName )
1546 {
1547 auto it = shown.find( aName );
1548
1549 if( it == shown.end() )
1550 return;
1551
1552 const ORCAD_DISPLAY_PROP* dp = it->second.second;
1553 int size = textSizeIU( dp->fontIdx );
1554
1555 // OrCAD stores text top-left corner; text grows right and down, so reference
1556 // stacks above value clear of body.
1557 aField->SetPosition( it->second.first );
1558 aField->SetTextSize( VECTOR2I( size, size ) );
1559 applyFont( aField, dp->fontIdx );
1560 aField->SetTextColor( OrcadColor( dp->color ) );
1561 aField->SetTextAngle( storedFieldAngle( dp ) );
1564 };
1565
1566 applyDisplayPos( refField, "Part Reference" );
1567 applyDisplayPos( valField, "Value" );
1568
1569 for( const auto& [propName, propValue] : aInst.props )
1570 {
1571 if( propName == "Value" || propName == "PCB Footprint" || isBookkeepingProp( propName ) )
1572 continue;
1573
1574 SCH_FIELD field( aSymbol, FIELD_T::USER, FromOrcadString( propName ) );
1575 field.SetText( FromOrcadString( propValue ) );
1576
1577 auto sIt = shown.find( propName );
1578
1579 if( sIt != shown.end() )
1580 {
1581 const ORCAD_DISPLAY_PROP* dp = sIt->second.second;
1582 int size = textSizeIU( dp->fontIdx );
1583
1584 field.SetPosition( sIt->second.first );
1585 field.SetTextAngle( storedFieldAngle( dp ) );
1586 field.SetTextSize( VECTOR2I( size, size ) );
1587 applyFont( &field, dp->fontIdx );
1588 field.SetTextColor( OrcadColor( dp->color ) );
1591 field.SetVisible( ( dp->dispMode & 0x100 ) != 0 );
1592 }
1593 else
1594 {
1595 field.SetPosition( aSymbol->GetPosition() );
1596 field.SetTextSize( VECTOR2I( schMm( 1.27 ), schMm( 1.27 ) ) );
1597 field.SetVisible( false );
1598 }
1599
1600 aSymbol->AddField( field );
1601 }
1602}
1603
1604
1606{
1607 // Count references per font height; dominant height maps to KiCad default 1.27mm,
1608 // rest scale relative to it.
1609 std::map<int, int> counts;
1610 std::vector<int> order; // first-seen order breaks count ties
1611
1612 auto tally = [&]( int aFontIdx )
1613 {
1614 int height = fontHeightDbu( aFontIdx );
1615
1616 if( !height )
1617 return;
1618
1619 auto [it, isNew] = counts.try_emplace( height, 0 );
1620
1621 if( isNew )
1622 order.push_back( height );
1623
1624 it->second++;
1625 };
1626
1627 for( const ORCAD_RAW_PAGE& page : m_design.pages )
1628 {
1629 for( const ORCAD_WIRE& wire : page.wires )
1630 {
1631 for( const ORCAD_ALIAS& alias : wire.aliases )
1632 tally( alias.fontIdx );
1633 }
1634
1635 for( const ORCAD_PLACED_INSTANCE& inst : page.instances )
1636 {
1637 for( const ORCAD_DISPLAY_PROP& dp : inst.displayProps )
1638 tally( dp.fontIdx );
1639 }
1640 }
1641
1642 int best = 0;
1643 int bestCount = 0;
1644
1645 for( int height : order )
1646 {
1647 if( counts[height] > bestCount )
1648 {
1649 best = height;
1650 bestCount = counts[height];
1651 }
1652 }
1653
1654 m_fontBaselineDbu = best;
1655}
1656
1657
1658int ORCAD_CONVERTER::fontHeightDbu( int aFontIdx ) const
1659{
1660 if( aFontIdx > 0 && aFontIdx <= (int) m_design.library.fonts.size() )
1661 return std::abs( m_design.library.fonts[aFontIdx - 1].height );
1662
1663 return 0;
1664}
1665
1666
1667int ORCAD_CONVERTER::textSizeIU( int aFontIdx ) const
1668{
1669 int height = fontHeightDbu( aFontIdx );
1670
1671 if( !height )
1672 return schMm( 1.27 );
1673
1674 double mm;
1675
1676 if( m_fontBaselineDbu )
1677 {
1678 mm = std::round( 1.27 * height / m_fontBaselineDbu * 100.0 ) / 100.0;
1679 mm = std::max( 0.7, std::min( 5.0, mm ) );
1680 }
1681 else
1682 {
1683 // LOGFONTA heights = device units at 96 dpi; 1 pt = 1/72 inch.
1684 mm = std::round( height * 25.4 * 0.75 / 96.0 * 100.0 ) / 100.0;
1685 mm = std::max( 0.7, mm );
1686 }
1687
1688 return schMm( mm );
1689}
1690
1691
1692void ORCAD_CONVERTER::applyFont( EDA_TEXT* aText, int aFontIdx ) const
1693{
1694 if( !aText || aFontIdx <= 0 || aFontIdx > static_cast<int>( m_design.library.fonts.size() ) )
1695 return;
1696
1697 const ORCAD_FONT& font = m_design.library.fonts[aFontIdx - 1];
1698 aText->SetBold( font.bold );
1699 aText->SetItalic( font.italic );
1700
1701 if( !font.face.empty() )
1702 {
1703 aText->SetFont( KIFONT::FONT::GetFont( FromOrcadString( font.face ), font.bold, font.italic ) );
1704 }
1705}
1706
1707
1708std::string ORCAD_CONVERTER::SymbolId( const std::string& aName )
1709{
1710 std::string out;
1711 bool inRun = false;
1712
1713 for( unsigned char c : aName )
1714 {
1715 // 0xA0 = CP-1252 non-breaking space.
1716 bool separator = c == ':' || c == '"' || c == '/' || c == ' ' || c == '\t' || c == '\n'
1717 || c == '\r' || c == '\f' || c == '\v' || c == 0xA0;
1718
1719 if( separator )
1720 {
1721 if( !inRun )
1722 out += '_';
1723
1724 inRun = true;
1725 }
1726 else
1727 {
1728 out += (char) c;
1729 inRun = false;
1730 }
1731 }
1732
1733 size_t first = out.find_first_not_of( '_' );
1734
1735 if( first == std::string::npos )
1736 return "SYM";
1737
1738 size_t last = out.find_last_not_of( '_' );
1739 out = out.substr( first, last - first + 1 );
1740
1741 return out.empty() ? "SYM" : out;
1742}
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 ...
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).
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
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 numbers visible, bit2 = pin names 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.
@ 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