KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_converter_sheet.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
28
31
32#include <algorithm>
33#include <array>
34#include <cctype>
35#include <cmath>
36#include <functional>
37#include <limits>
38#include <map>
39#include <memory>
40#include <set>
41#include <string>
42#include <utility>
43#include <vector>
44
45#include <wx/buffer.h>
46#include <wx/filename.h>
47#include <wx/log.h>
48#include <wx/translation.h>
49
50#include <base_units.h>
51#include <ki_exception.h>
52#include <layer_ids.h>
53#include <lib_symbol.h>
54#include <math/util.h>
55#include <page_info.h>
56#include <progress_reporter.h>
57#include <project.h>
58#include <reference_image.h>
59#include <reporter.h>
60#include <schematic.h>
61#include <sch_bitmap.h>
62#include <sch_bus_entry.h>
63#include <sch_junction.h>
64#include <sch_label.h>
65#include <sch_line.h>
66#include <sch_no_connect.h>
67#include <sch_screen.h>
68#include <sch_shape.h>
69#include <sch_sheet.h>
70#include <sch_sheet_path.h>
71#include <sch_sheet_pin.h>
72#include <sch_text.h>
73#include <string_utils.h>
74#include <stroke_params.h>
75#include <template_fieldnames.h>
76#include <title_block.h>
77
79
80
81namespace
82{
83
85constexpr double DBU_TO_MM = 0.254;
86
88constexpr int COMMENT_COUNT = 9;
89
90
91
92std::string trimmed( const std::string& aText )
93{
94 size_t begin = aText.find_first_not_of( " \t\r\n\f\v" );
95
96 if( begin == std::string::npos )
97 return std::string();
98
99 size_t end = aText.find_last_not_of( " \t\r\n\f\v" );
100
101 return aText.substr( begin, end - begin + 1 );
102}
103
104
105void pollProgress( PROGRESS_REPORTER* aReporter, const std::string& aPageName )
106{
107 if( !aReporter )
108 return;
109
110 aReporter->Report( wxString::Format( _( "Converting page '%s'..." ),
111 FromOrcadString( aPageName ) ) );
112
113 if( !aReporter->KeepRefreshing() )
114 THROW_IO_ERROR( _( "Open canceled by user." ) );
115}
116
117
118SCH_SHAPE* makeSheetPoly( const std::vector<VECTOR2I>& aPoints, const ORCAD_PRIMITIVE& aPrimitive,
119 const KIGFX::COLOR4D& aColor, bool aCanFill = false )
120{
122
123 for( const VECTOR2I& pt : aPoints )
124 poly->AddPoint( pt );
125
127 OrcadLineStyle( aPrimitive.lineStyle ), aColor ) );
128
129 // Page graphics have no symbol body color, so solid fill uses the foreground color.
130 if( aCanFill && aPrimitive.fillStyle == 0 )
132 else
133 poly->SetFillMode( aCanFill ? OrcadFillType( aPrimitive.fillStyle, aPrimitive.hatchStyle )
134 : FILL_T::NO_FILL );
135
136 return poly;
137}
138
139
140VECTOR2I dbuPointToIu( double aX, double aY )
141{
142 return VECTOR2I( KiROUND( aX * ORCAD_IU_PER_DBU ), KiROUND( aY * ORCAD_IU_PER_DBU ) );
143}
144
145} // namespace
146
147
148KIGFX::COLOR4D OrcadColor( int aColorIndex )
149{
150 static constexpr uint8_t palette[][3] = {
151 { 0, 0, 0 }, { 255, 255, 128 }, { 128, 255, 128 }, { 0, 255, 128 }, { 128, 255, 255 }, { 0, 128, 255 },
152 { 255, 128, 192 }, { 255, 128, 255 }, { 255, 0, 0 }, { 255, 255, 0 }, { 128, 255, 0 }, { 0, 255, 64 },
153 { 0, 255, 255 }, { 0, 128, 192 }, { 128, 128, 192 }, { 255, 0, 255 }, { 128, 64, 64 }, { 255, 128, 64 },
154 { 0, 255, 0 }, { 0, 128, 128 }, { 0, 64, 128 }, { 128, 128, 255 }, { 128, 0, 64 }, { 255, 0, 128 },
155 { 128, 0, 0 }, { 255, 128, 0 }, { 0, 128, 0 }, { 0, 128, 64 }, { 0, 0, 255 }, { 0, 0, 160 },
156 { 128, 0, 128 }, { 128, 0, 255 }, { 64, 0, 0 }, { 128, 64, 0 }, { 0, 64, 0 }, { 0, 64, 64 },
157 { 0, 0, 128 }, { 0, 0, 64 }, { 64, 0, 64 }, { 64, 0, 128 }, { 0, 0, 0 }, { 128, 128, 0 },
158 { 128, 128, 64 }, { 128, 128, 128 }, { 64, 128, 128 }, { 192, 192, 192 }, { 64, 0, 64 }, { 255, 255, 255 }
159 };
160
161 if( aColorIndex < 1 || aColorIndex >= static_cast<int>( std::size( palette ) ) )
163
164 return KIGFX::COLOR4D( palette[aColorIndex][0] / 255.0, palette[aColorIndex][1] / 255.0,
165 palette[aColorIndex][2] / 255.0, 1.0 );
166}
167
168
170 PROGRESS_REPORTER* aProgressReporter ) :
171 m_design( aDesign ),
172 m_schematic( aSchematic ),
173 m_reporter( aReporter ),
174 m_progressReporter( aProgressReporter ),
175 m_rootSheet( nullptr ),
176 m_powerCount( 0 ),
178{
179}
180
181
183
184
185void ORCAD_CONVERTER::warn( const wxString& aMsg )
186{
187 if( m_reporter )
188 m_reporter->Report( aMsg, RPT_SEVERITY_WARNING );
189}
190
191
192void ORCAD_CONVERTER::note( const wxString& aMsg )
193{
194 if( m_reporter )
195 m_reporter->Report( aMsg, RPT_SEVERITY_INFO );
196}
197
198
200int OrcadPageOrder( wxString& aName )
201{
202 size_t digitStart = 0;
203 wxString upper = aName.Upper();
204
205 for( const wxString& prefix : { wxS( "PAGE" ), wxS( "SCH" ), wxS( "PAG" ) } )
206 {
207 if( upper.StartsWith( prefix )
208 && ( aName.length() == prefix.length() || wxIsdigit( aName[prefix.length()] )
209 || wxIsspace( aName[prefix.length()] ) || aName[prefix.length()] == '_' ) )
210 {
211 digitStart = prefix.length();
212
213 while( digitStart < aName.length()
214 && ( wxIsspace( aName[digitStart] ) || aName[digitStart] == '_' ) )
215 {
216 digitStart++;
217 }
218
219 break;
220 }
221 }
222
223 size_t digitEnd = digitStart;
224
225 while( digitEnd < aName.length() && wxIsdigit( aName[digitEnd] ) )
226 digitEnd++;
227
228 long order = 0;
229
230 if( digitEnd == digitStart
231 || !aName.Mid( digitStart, digitEnd - digitStart ).ToLong( &order ) )
232 {
233 return -1;
234 }
235
236 size_t separator = digitEnd;
237
238 while( separator < aName.length() && wxIsspace( aName[separator] ) )
239 separator++;
240
241 if( separator == aName.length()
242 || ( aName[separator] != '-' && aName[separator] != '.' && aName[separator] != ':' ) )
243 return -1;
244
245 if( aName[separator] == '-' )
246 {
247 wxString rest = aName.Mid( separator + 1 );
248 rest.Trim( false );
249 aName = rest;
250 }
251
252 return static_cast<int>( order );
253}
254
255
257{
258 m_rootSheet = aRootSheet;
259
261
262 SCH_SCREEN* rootScreen = aRootSheet->GetScreen();
263
264 SCH_SHEET_PATH rootPath;
265 rootPath.push_back( aRootSheet );
266 rootPath.SetPageNumber( wxS( "1" ) );
267
268 std::function<bool( const ORCAD_RAW_PAGE&, const ORCAD_OCC_SCOPE& )> canBuildHierarchy =
269 [&]( const ORCAD_RAW_PAGE& aPage, const ORCAD_OCC_SCOPE& aScope )
270 {
271 if( aPage.blocks.size() != aScope.blocks.size() )
272 return false;
273
274 std::set<uint32_t> matchedBlocks;
275
276 for( const ORCAD_OCC_BLOCK& occurrence : aScope.blocks )
277 {
278 auto drawn = std::find_if( aPage.blocks.begin(), aPage.blocks.end(),
279 [&]( const ORCAD_DRAWN_INSTANCE& aBlock )
280 {
281 return aBlock.dbId == occurrence.targetDbId;
282 } );
283
284 std::string key = occurrence.childFolder;
285 std::transform( key.begin(), key.end(), key.begin(),
286 []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
287
288 auto pages = m_design.childFolderPages.find( key );
289
290 if( !matchedBlocks.insert( occurrence.targetDbId ).second
291 || drawn == aPage.blocks.end() || pages == m_design.childFolderPages.end()
292 || pages->second.size() != 1 || !canBuildHierarchy( pages->second.front(), occurrence.scope ) )
293 {
294 return false;
295 }
296 }
297
298 return true;
299 };
300
301 bool nativeHierarchy = m_design.pages.size() == 1 && !m_design.occurrenceRoot.blocks.empty()
302 && canBuildHierarchy( m_design.pages.front(), m_design.occurrenceRoot );
303
304 if( nativeHierarchy )
305 {
306 ORCAD_RAW_PAGE& rootPage = m_design.pages.front();
307
308 pollProgress( m_progressReporter, rootPage.name );
309 m_currentOccRefs = &m_design.occurrenceRoot.partRefs;
310 applyPageSettings( rootPage, rootScreen );
311 convertPage( rootPage, rootScreen, rootPath );
312
313 int pageIndex = 1;
314
315 std::function<void( ORCAD_RAW_PAGE&, const ORCAD_OCC_SCOPE&, SCH_SHEET*,
316 const SCH_SHEET_PATH& )> placeChildren =
317 [&]( ORCAD_RAW_PAGE& aParentPage, const ORCAD_OCC_SCOPE& aScope,
318 SCH_SHEET* aParentSheet, const SCH_SHEET_PATH& aParentPath )
319 {
320 std::vector<const ORCAD_OCC_BLOCK*> occurrences;
321
322 for( const ORCAD_OCC_BLOCK& occurrence : aScope.blocks )
323 occurrences.push_back( &occurrence );
324
325 std::stable_sort( occurrences.begin(), occurrences.end(),
326 []( const ORCAD_OCC_BLOCK* a, const ORCAD_OCC_BLOCK* b )
327 {
328 wxString aName = FromOrcadString( a->childFolder );
329 wxString bName = FromOrcadString( b->childFolder );
330 int aOrder = OrcadPageOrder( aName );
331 int bOrder = OrcadPageOrder( bName );
332 int aKey = aOrder >= 0 ? aOrder : std::numeric_limits<int>::max();
333 int bKey = bOrder >= 0 ? bOrder : std::numeric_limits<int>::max();
334 return aKey < bKey;
335 } );
336
337 for( const ORCAD_OCC_BLOCK* occurrencePtr : occurrences )
338 {
339 const ORCAD_OCC_BLOCK& occurrence = *occurrencePtr;
340 auto drawn = std::find_if( aParentPage.blocks.begin(), aParentPage.blocks.end(),
341 [&]( const ORCAD_DRAWN_INSTANCE& aBlock )
342 {
343 return aBlock.dbId == occurrence.targetDbId;
344 } );
345
346 std::string key = occurrence.childFolder;
347 std::transform( key.begin(), key.end(), key.begin(),
348 []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
349
350 ORCAD_RAW_PAGE& childPage = m_design.childFolderPages.at( key ).front();
351 SCH_SCREEN* childScreen = new SCH_SCREEN( m_schematic );
352 SCH_SHEET* childSheet = new SCH_SHEET( aParentSheet,
353 OrcadDbuToIu( drawn->x1, drawn->y1 ),
354 OrcadDbuToIu( drawn->w, drawn->h ) );
355 wxString sheetName = FromOrcadString( drawn->reference );
356
357 if( sheetName.IsEmpty() )
358 sheetName = FromOrcadString( childPage.name );
359
360 wxString base = sheetName;
361
362 for( int suffix = 2; !m_usedSheetNames.insert( sheetName.Lower() ).second; ++suffix )
363 sheetName = wxString::Format( wxS( "%s (%d)" ), base, suffix );
364
365 wxString fileName = MakePageFileName( ++pageIndex, childPage.name );
366 childSheet->GetField( FIELD_T::SHEET_NAME )->SetText( sheetName );
367 childSheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fileName );
368 childSheet->SetScreen( childScreen );
369 childScreen->SetFileName( m_schematic->Project().GetProjectPath() + fileName );
370
371 for( const ORCAD_BLOCK_PIN& sourcePin : drawn->pins )
372 {
373 VECTOR2I position = OrcadDbuToIu( sourcePin.x, sourcePin.y );
374 SCH_SHEET_PIN* pin = new SCH_SHEET_PIN( childSheet, position,
375 FromOrcadString( sourcePin.name ) );
376 std::array<std::pair<int, SHEET_SIDE>, 4> sides = {
377 std::pair{ std::abs( sourcePin.x - drawn->x1 ), SHEET_SIDE::LEFT },
378 std::pair{ std::abs( sourcePin.x - drawn->x1 - drawn->w ), SHEET_SIDE::RIGHT },
379 std::pair{ std::abs( sourcePin.y - drawn->y1 ), SHEET_SIDE::TOP },
380 std::pair{ std::abs( sourcePin.y - drawn->y1 - drawn->h ), SHEET_SIDE::BOTTOM }
381 };
382
383 pin->SetSide( std::min_element( sides.begin(), sides.end(),
384 []( const auto& a, const auto& b )
385 {
386 return a.first < b.first;
387 } )->second );
388
389 switch( sourcePin.portType )
390 {
391 case ORCAD_PORT_TYPE::INPUT: pin->SetShape( LABEL_FLAG_SHAPE::L_INPUT ); break;
395 default: pin->SetShape( LABEL_FLAG_SHAPE::L_UNSPECIFIED ); break;
396 }
397
398 childSheet->AddPin( pin );
399 }
400
401 aParentSheet->GetScreen()->Append( childSheet );
402
403 SCH_SHEET_PATH childPath = aParentPath;
404 childPath.push_back( childSheet );
405 childPath.SetPageNumber( wxString::Format( wxS( "%d" ), pageIndex ) );
406
407 pollProgress( m_progressReporter, childPage.name );
408 m_currentOccRefs = &occurrence.scope.partRefs;
409 applyPageSettings( childPage, childScreen );
410 convertPage( childPage, childScreen, childPath );
411 placeChildren( childPage, occurrence.scope, childSheet, childPath );
412 }
413 };
414
415 placeChildren( rootPage, m_design.occurrenceRoot, aRootSheet, rootPath );
416 m_currentOccRefs = nullptr;
417 return aRootSheet;
418 }
419
420 // Page list = root pages + each block occurrence's child pages, tagged w/ scope refs.
421 // Child schematic reused N times yields N jobs, each w/ own designators.
422 struct PAGE_JOB
423 {
424 ORCAD_RAW_PAGE* page;
425 const std::map<uint32_t, std::string>* refs;
426 };
427
428 std::vector<PAGE_JOB> jobs;
429
430 for( ORCAD_RAW_PAGE& page : m_design.pages )
431 jobs.push_back( { &page, &m_design.occurrenceRoot.partRefs } );
432
433 std::function<void( const ORCAD_OCC_SCOPE& )> expand =
434 [&]( const ORCAD_OCC_SCOPE& aScope )
435 {
436 for( const ORCAD_OCC_BLOCK& block : aScope.blocks )
437 {
438 std::string key = block.childFolder;
439 std::transform( key.begin(), key.end(), key.begin(),
440 []( unsigned char c ) { return static_cast<char>( std::tolower( c ) ); } );
441
442 auto it = m_design.childFolderPages.find( key );
443
444 if( it != m_design.childFolderPages.end() )
445 {
446 for( ORCAD_RAW_PAGE& childPage : it->second )
447 jobs.push_back( { &childPage, &block.scope.partRefs } );
448 }
449
450 expand( block.scope );
451 }
452 };
453
454 expand( m_design.occurrenceRoot );
455
456 if( jobs.size() == 1 )
457 {
458 PAGE_JOB& job = jobs[0];
459
460 pollProgress( m_progressReporter, job.page->name );
461 m_currentOccRefs = job.refs;
462 applyPageSettings( *job.page, rootScreen );
463 convertPage( *job.page, rootScreen, rootPath );
464 m_currentOccRefs = nullptr;
465 }
466 else
467 {
468 // Each page = flat top-level sheet (no stitching root); order root pages by
469 // leading "N - " prefix, also stripped for title.
470 struct SHEET_JOB
471 {
472 PAGE_JOB job;
473 wxString name;
474 int order;
475 };
476
477 std::vector<SHEET_JOB> sheetJobs;
478
479 for( size_t i = 0; i < jobs.size(); ++i )
480 {
481 wxString name = FromOrcadString( jobs[i].page->name );
482 int order = i < m_design.pages.size() ? OrcadPageOrder( name ) : -1;
483
484 if( name.IsEmpty() )
485 name = wxString::Format( wxS( "PAGE%zu" ), i + 1 );
486
487 sheetJobs.push_back( { jobs[i], name, order } );
488 }
489
490 std::stable_sort( sheetJobs.begin(), sheetJobs.end(),
491 []( const SHEET_JOB& a, const SHEET_JOB& b )
492 {
493 int ka = a.order >= 0 ? a.order : std::numeric_limits<int>::max();
494 int kb = b.order >= 0 ? b.order : std::numeric_limits<int>::max();
495 return ka < kb;
496 } );
497
498 std::vector<SCH_SHEET*> topSheets;
499
500 for( size_t i = 0; i < sheetJobs.size(); ++i )
501 {
502 SHEET_JOB& sj = sheetJobs[i];
503
504 pollProgress( m_progressReporter, sj.job.page->name );
505
506 SCH_SHEET* sheet;
507 SCH_SCREEN* screen;
508
509 if( i == 0 )
510 {
511 // Reuse sheet the loader created for first page
512 sheet = aRootSheet;
513 screen = rootScreen;
514 }
515 else
516 {
517 screen = new SCH_SCREEN( m_schematic );
518 sheet = new SCH_SHEET( m_schematic );
519 sheet->SetScreen( screen );
520 const_cast<KIID&>( sheet->m_Uuid ) = screen->GetUuid();
521 }
522
523 wxString base = sj.name;
524
525 for( int suffix = 2; !m_usedSheetNames.insert( sj.name.Lower() ).second; ++suffix )
526 sj.name = wxString::Format( wxS( "%s (%d)" ), base, suffix );
527
528 wxString fileName = MakePageFileName( static_cast<int>( i + 1 ), sj.job.page->name );
529
530 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( sj.name );
531 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( fileName );
532 screen->SetFileName( m_schematic->Project().GetProjectPath() + fileName );
533
534 SCH_SHEET_PATH pagePath;
535 pagePath.push_back( sheet );
536 pagePath.SetPageNumber( wxString::Format( wxS( "%zu" ), i + 1 ) );
537
538 m_currentOccRefs = sj.job.refs;
539 applyPageSettings( *sj.job.page, screen );
540 convertPage( *sj.job.page, screen, pagePath );
541 m_currentOccRefs = nullptr;
542
543 topSheets.push_back( sheet );
544 }
545
546 m_schematic->SetTopLevelSheets( topSheets );
547 }
548
549 return aRootSheet;
550}
551
552
554 const SCH_SHEET_PATH& aSheetPath )
555{
556 applyTitleBlock( aPage, aScreen );
557 buildNetLookup( aPage );
558
559 placeJunctions( aPage, aScreen );
560 placeNoConnects( aPage, aScreen );
561 placeBusEntries( aPage, aScreen );
562 placeWires( aPage, aScreen );
563 placeOffpageConnectors( aPage, aScreen );
564 placePorts( aPage, aScreen, aSheetPath.size() > 1 );
565 placeGraphics( aPage, aScreen );
566
567 for( const ORCAD_PLACED_INSTANCE& instance : aPage.instances )
568 placeInstance( aPage, instance, aScreen, aSheetPath );
569
570 for( const ORCAD_GRAPHIC_INST& global : aPage.globals )
571 placePowerSymbol( aPage, global, powerNet( aPage, global ), aScreen, aSheetPath );
572}
573
574
575wxString ORCAD_CONVERTER::MakePageFileName( int aPageIndex, const std::string& aPageName )
576{
577 wxString fileName = wxString::Format( wxS( "P%02d_" ), aPageIndex )
578 + SanitizeFileName( aPageName ) + wxS( ".kicad_sch" );
579
580 ReplaceIllegalFileNameChars( fileName, '_' );
581
582 return fileName;
583}
584
585
586wxString ORCAD_CONVERTER::SanitizeFileName( const std::string& aName )
587{
588 const wxString illegal( wxS( "<>:\"/\\|?*" ) );
589 wxString in = FromOrcadString( aName );
590 wxString out;
591
592 for( wxUniChar c : in )
593 {
594 if( c.GetValue() < 0x20 || illegal.Find( c ) != wxNOT_FOUND )
595 out += '_';
596 else
597 out += c;
598 }
599
600 while( !out.IsEmpty() && ( out.Last() == ' ' || out.Last() == '.' ) )
601 out.RemoveLast();
602
603 while( !out.IsEmpty() && ( out.GetChar( 0 ) == ' ' || out.GetChar( 0 ) == '.' ) )
604 out.Remove( 0, 1 );
605
606 if( out.IsEmpty() )
607 out = wxS( "unnamed" );
608
609 return out;
610}
611
612
614{
615 BOX2I extent = pageExtentDbu( aPage );
616
617 // Nominal paper from stored page size; mils, or micrometres when metric.
618 double k = aPage.isMetric ? 0.001 : 0.0254;
619 double nominalWmm = aPage.width * k;
620 double nominalHmm = aPage.height * k;
621
622 // The clearance shift below pushes a full-page drawing past its own paper, so a
623 // named size only survives while the content stays at source coordinates
624 PAGE_INFO named;
625
626 if( !aPage.pageSize.empty()
627 && named.SetType( FromOrcadString( aPage.pageSize ), nominalHmm > nominalWmm )
628 && named.GetType() != PAGE_SIZE_TYPE::User
629 && extent.GetLeft() >= 0 && extent.GetTop() >= 0
630 && extent.GetRight() * DBU_TO_MM <= nominalWmm
631 && extent.GetBottom() * DBU_TO_MM <= nominalHmm )
632 {
633 aScreen->SetPageSettings( named );
634 return;
635 }
636
637 // Shift content clear of frame, round up to 10-DBU grid to keep points on grid.
638 int dx = std::max( 0, MARGIN_L_DBU - extent.GetLeft() );
639 int dy = std::max( 0, MARGIN_T_DBU - extent.GetTop() );
640
641 dx = ( dx + 9 ) / 10 * 10;
642 dy = ( dy + 9 ) / 10 * 10;
643
644 int maxX = extent.GetRight();
645 int maxY = extent.GetBottom();
646
647 if( dx || dy )
648 {
649 offsetPage( aPage, dx, dy );
650 maxX += dx;
651 maxY += dy;
652 }
653
654 // Needed paper = shifted content extent plus right/bottom margins.
655 double neededWmm = ( maxX + MARGIN_R_DBU ) * DBU_TO_MM;
656 double neededHmm = ( maxY + MARGIN_B_DBU ) * DBU_TO_MM;
657
658 int paperWmm = static_cast<int>( std::ceil( std::max( nominalWmm, neededWmm ) ) );
659 int paperHmm = static_cast<int>( std::ceil( std::max( nominalHmm, neededHmm ) ) );
660
661 PAGE_INFO pageInfo;
662 PAGE_INFO::SetCustomWidthMils( paperWmm * 1000.0 / 25.4 );
663 PAGE_INFO::SetCustomHeightMils( paperHmm * 1000.0 / 25.4 );
664 pageInfo.SetType( PAGE_SIZE_TYPE::User );
665
666 aScreen->SetPageSettings( pageInfo );
667}
668
669
671{
672 bool any = false;
673 int minX = 0;
674 int minY = 0;
675 int maxX = 0;
676 int maxY = 0;
677
678 auto add = [&]( int aX, int aY )
679 {
680 if( !any )
681 {
682 minX = maxX = aX;
683 minY = maxY = aY;
684 any = true;
685 }
686 else
687 {
688 minX = std::min( minX, aX );
689 minY = std::min( minY, aY );
690 maxX = std::max( maxX, aX );
691 maxY = std::max( maxY, aY );
692 }
693 };
694
695 for( const ORCAD_WIRE& wire : aPage.wires )
696 {
697 add( wire.x1, wire.y1 );
698 add( wire.x2, wire.y2 );
699 }
700
701 for( const ORCAD_PLACED_INSTANCE& instance : aPage.instances )
702 {
703 add( instance.bbox.x1, instance.bbox.y1 );
704 add( instance.bbox.x2, instance.bbox.y2 );
705
706 for( const ORCAD_PIN_INST& pin : instance.pins )
707 add( pin.x, pin.y );
708 }
709
710 for( const std::vector<ORCAD_GRAPHIC_INST>* list :
711 { &aPage.globals, &aPage.offpage, &aPage.ports, &aPage.ercObjects } )
712 {
713 for( const ORCAD_GRAPHIC_INST& inst : *list )
714 {
715 add( inst.x, inst.y );
716 add( inst.bbox.x1, inst.bbox.y1 );
717 add( inst.bbox.x2, inst.bbox.y2 );
718 }
719 }
720
721 // Free graphics outer bbox = anchor-relative junk; only nested primitives carry real coords.
722 for( const ORCAD_GRAPHIC_INST& gfx : aPage.graphics )
723 {
724 if( !gfx.nested )
725 continue;
726
727 for( const ORCAD_PRIMITIVE& prim : gfx.nested->primitives )
728 {
729 if( !prim.points.empty() )
730 {
731 for( const ORCAD_POINT& pt : prim.points )
732 add( pt.x, pt.y );
733 }
734 else
735 {
736 add( prim.x1, prim.y1 );
737 add( prim.x2, prim.y2 );
738 }
739 }
740 }
741
742 for( const ORCAD_BUS_ENTRY& entry : aPage.busEntries )
743 {
744 add( entry.x1, entry.y1 );
745 add( entry.x2, entry.y2 );
746 }
747
748 for( const ORCAD_DRAWN_INSTANCE& block : aPage.blocks )
749 {
750 add( block.x1, block.y1 );
751 add( block.x1 + block.w, block.y1 + block.h );
752
753 for( const ORCAD_BLOCK_PIN& pin : block.pins )
754 add( pin.x, pin.y );
755 }
756
757 if( !any )
758 return BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( 3800, 2700 ) );
759
760 return BOX2I( VECTOR2I( minX, minY ), VECTOR2I( maxX - minX, maxY - minY ) );
761}
762
763
764void ORCAD_CONVERTER::offsetPage( ORCAD_RAW_PAGE& aPage, int aDx, int aDy )
765{
766 for( ORCAD_WIRE& wire : aPage.wires )
767 {
768 wire.x1 += aDx;
769 wire.y1 += aDy;
770 wire.x2 += aDx;
771 wire.y2 += aDy;
772
773 for( ORCAD_ALIAS& alias : wire.aliases )
774 {
775 alias.x += aDx;
776 alias.y += aDy;
777 }
778 }
779
780 for( ORCAD_PLACED_INSTANCE& instance : aPage.instances )
781 {
782 instance.x += aDx;
783 instance.y += aDy;
784 instance.bbox.x1 += aDx;
785 instance.bbox.y1 += aDy;
786 instance.bbox.x2 += aDx;
787 instance.bbox.y2 += aDy;
788
789 for( ORCAD_PIN_INST& pin : instance.pins )
790 {
791 pin.x += aDx;
792 pin.y += aDy;
793 }
794 }
795
796 auto shiftGraphic = [&]( ORCAD_GRAPHIC_INST& aInst )
797 {
798 aInst.x += aDx;
799 aInst.y += aDy;
800 aInst.bbox.x1 += aDx;
801 aInst.bbox.y1 += aDy;
802 aInst.bbox.x2 += aDx;
803 aInst.bbox.y2 += aDy;
804
805 if( !aInst.nested )
806 return;
807
808 for( ORCAD_PRIMITIVE& prim : aInst.nested->primitives )
809 {
810 prim.x1 += aDx;
811 prim.y1 += aDy;
812 prim.x2 += aDx;
813 prim.y2 += aDy;
814
815 for( ORCAD_POINT& pt : prim.points )
816 {
817 pt.x += aDx;
818 pt.y += aDy;
819 }
820
821 if( prim.start )
822 {
823 prim.start->x += aDx;
824 prim.start->y += aDy;
825 }
826
827 if( prim.end )
828 {
829 prim.end->x += aDx;
830 prim.end->y += aDy;
831 }
832 }
833 };
834
835 for( std::vector<ORCAD_GRAPHIC_INST>* list : { &aPage.globals, &aPage.offpage, &aPage.ports,
836 &aPage.ercObjects, &aPage.graphics } )
837 {
838 for( ORCAD_GRAPHIC_INST& inst : *list )
839 shiftGraphic( inst );
840 }
841
842 for( ORCAD_BUS_ENTRY& entry : aPage.busEntries )
843 {
844 entry.x1 += aDx;
845 entry.y1 += aDy;
846 entry.x2 += aDx;
847 entry.y2 += aDy;
848 }
849
850 for( ORCAD_DRAWN_INSTANCE& block : aPage.blocks )
851 {
852 block.x1 += aDx;
853 block.y1 += aDy;
854
855 for( ORCAD_BLOCK_PIN& pin : block.pins )
856 {
857 pin.x += aDx;
858 pin.y += aDy;
859 }
860 }
861}
862
863
865{
866 for( const ORCAD_GRAPHIC_INST& tbInst : aPage.titleBlocks )
867 {
868 auto symbolIt = m_design.symbols.find( tbInst.name );
869
870 if( symbolIt != m_design.symbols.end() )
871 {
872 placeDefinitionVectors( symbolIt->second, tbInst.bbox.x1, tbInst.bbox.y1,
873 OrcadOrientOf( tbInst.rotation, tbInst.mirror ), aScreen );
874 placeDefinitionImages( symbolIt->second, tbInst.bbox.x1, tbInst.bbox.y1,
875 OrcadOrientOf( tbInst.rotation, tbInst.mirror ), aScreen );
876 }
877
878 if( tbInst.props.empty() )
879 continue;
880
881 auto get = [&]( const char* aKey ) -> wxString
882 {
883 auto it = tbInst.props.find( aKey );
884
885 return it != tbInst.props.end() ? FromOrcadString( it->second ) : wxString();
886 };
887
888 TITLE_BLOCK titleBlock;
889
890 titleBlock.SetTitle( get( "Title" ) );
891
892 wxString date = get( "Page Modify Date" );
893
894 if( date.IsEmpty() )
895 date = get( "Doc Date" );
896
897 titleBlock.SetDate( date );
898 titleBlock.SetRevision( get( "RevCode" ) );
899 titleBlock.SetCompany( get( "OrgName" ) );
900
901 int slot = 0;
902
903 for( const char* stock : { "Doc", "OrgAddr1", "OrgAddr2" } )
904 {
905 if( wxString text = get( stock ); !text.IsEmpty() )
906 titleBlock.SetComment( slot++, text );
907 }
908
909 // Custom title blocks name their own fields and KiCad has no slot for them, so
910 // spill into the free comments. Page number and count KiCad resolves itself
911 static const std::set<std::string> mapped = {
912 "Title", "RevCode", "OrgName", "Doc", "OrgAddr1",
913 "OrgAddr2", "Doc Date", "Page Count", "Page Number", "Page Modify Date"
914 };
915
916 for( const auto& [propName, propValue] : tbInst.props )
917 {
918 if( slot >= COMMENT_COUNT || propValue.empty() || mapped.count( propName ) )
919 continue;
920
921 titleBlock.SetComment( slot++, wxString::Format( wxS( "%s: %s" ),
922 FromOrcadString( propName ),
923 FromOrcadString( propValue ) ) );
924 }
925
926 aScreen->SetTitleBlock( titleBlock );
927 return;
928 }
929}
930
931
932void ORCAD_CONVERTER::placeDefinitionVectors( const ORCAD_SYMBOL_DEF& aDefinition, int aBaseX, int aBaseY, int aOrient,
933 SCH_SCREEN* aScreen )
934{
935 ORCAD_BBOX bbox = aDefinition.bbox.value_or( ORCAD_BBOX() );
936 int width = bbox.x2 - bbox.x1;
937 int height = bbox.y2 - bbox.y1;
938
939 auto transform = [&]( int aX, int aY )
940 {
941 return OrcadTransformPoint( aOrient, width, height, aBaseX, aBaseY, aX, aY );
942 };
943
944 ORCAD_GRAPHIC_INST graphic;
945 graphic.rotation = aOrient & 3;
946 graphic.color = aDefinition.color;
947 graphic.nested = std::make_unique<ORCAD_SYMBOL_DEF>();
948
949 std::function<void( const std::vector<ORCAD_PRIMITIVE>&, int, int )> appendVectors =
950 [&]( const std::vector<ORCAD_PRIMITIVE>& aPrimitives, int aOffsetX, int aOffsetY )
951 {
952 for( const ORCAD_PRIMITIVE& source : aPrimitives )
953 {
954 if( source.kind == ORCAD_PRIM_KIND::GROUP )
955 {
956 appendVectors( source.children, aOffsetX + source.x1, aOffsetY + source.y1 );
957 continue;
958 }
959
960 if( source.kind == ORCAD_PRIM_KIND::IMAGE )
961 continue;
962
963 ORCAD_PRIMITIVE primitive = source;
964
965 auto transformBox = [&]
966 {
967 std::array<VECTOR2I, 4> corners = { transform( aOffsetX + source.x1, aOffsetY + source.y1 ),
968 transform( aOffsetX + source.x2, aOffsetY + source.y1 ),
969 transform( aOffsetX + source.x2, aOffsetY + source.y2 ),
970 transform( aOffsetX + source.x1, aOffsetY + source.y2 ) };
971
972 primitive.x1 = primitive.x2 = corners[0].x;
973 primitive.y1 = primitive.y2 = corners[0].y;
974
975 for( const VECTOR2I& corner : corners )
976 {
977 primitive.x1 = std::min( primitive.x1, corner.x );
978 primitive.y1 = std::min( primitive.y1, corner.y );
979 primitive.x2 = std::max( primitive.x2, corner.x );
980 primitive.y2 = std::max( primitive.y2, corner.y );
981 }
982 };
983
984 if( source.kind == ORCAD_PRIM_KIND::LINE )
985 {
986 VECTOR2I p1 = transform( aOffsetX + source.x1, aOffsetY + source.y1 );
987 VECTOR2I p2 = transform( aOffsetX + source.x2, aOffsetY + source.y2 );
988 primitive.x1 = p1.x;
989 primitive.y1 = p1.y;
990 primitive.x2 = p2.x;
991 primitive.y2 = p2.y;
992 }
993 else
994 {
995 transformBox();
996 }
997
998 for( ORCAD_POINT& point : primitive.points )
999 {
1000 VECTOR2I transformed = transform( aOffsetX + point.x, aOffsetY + point.y );
1001 point.x = transformed.x;
1002 point.y = transformed.y;
1003 }
1004
1005 if( primitive.start )
1006 {
1007 VECTOR2I transformed = transform( aOffsetX + primitive.start->x, aOffsetY + primitive.start->y );
1008 primitive.start = ORCAD_POINT{ transformed.x, transformed.y };
1009 }
1010
1011 if( primitive.end )
1012 {
1013 VECTOR2I transformed = transform( aOffsetX + primitive.end->x, aOffsetY + primitive.end->y );
1014 primitive.end = ORCAD_POINT{ transformed.x, transformed.y };
1015 }
1016
1017 graphic.nested->primitives.push_back( std::move( primitive ) );
1018 }
1019 };
1020
1021 appendVectors( aDefinition.primitives, 0, 0 );
1022
1023 ORCAD_RAW_PAGE page;
1024 page.graphics.push_back( std::move( graphic ) );
1025 placeGraphics( page, aScreen );
1026}
1027
1028
1029void ORCAD_CONVERTER::placeDefinitionImages( const ORCAD_SYMBOL_DEF& aDefinition, int aBaseX, int aBaseY, int aOrient,
1030 SCH_SCREEN* aScreen )
1031{
1032 ORCAD_BBOX bbox = aDefinition.bbox.value_or( ORCAD_BBOX() );
1033 int width = bbox.x2 - bbox.x1;
1034 int height = bbox.y2 - bbox.y1;
1035
1036 std::function<void( const std::vector<ORCAD_PRIMITIVE>&, int, int )> placeImages =
1037 [&]( const std::vector<ORCAD_PRIMITIVE>& aPrimitives, int aOffsetX, int aOffsetY )
1038 {
1039 for( const ORCAD_PRIMITIVE& primitive : aPrimitives )
1040 {
1041 if( primitive.kind == ORCAD_PRIM_KIND::GROUP )
1042 {
1043 placeImages( primitive.children, aOffsetX + primitive.x1, aOffsetY + primitive.y1 );
1044 continue;
1045 }
1046
1047 if( primitive.kind != ORCAD_PRIM_KIND::IMAGE )
1048 continue;
1049
1050 VECTOR2I center = OrcadTransformPoint( aOrient, width, height, aBaseX, aBaseY,
1051 aOffsetX + ( primitive.x1 + primitive.x2 ) / 2,
1052 aOffsetY + ( primitive.y1 + primitive.y2 ) / 2 );
1053 int imageWidth = std::abs( primitive.x2 - primitive.x1 );
1054 int imageHeight = std::abs( primitive.y2 - primitive.y1 );
1055 ORCAD_PRIMITIVE image = primitive;
1056 image.x1 = center.x - imageWidth / 2;
1057 image.y1 = center.y - imageHeight / 2;
1058 image.x2 = image.x1 + imageWidth;
1059 image.y2 = image.y1 + imageHeight;
1060 placeBitmap( image, aScreen, aOrient );
1061 }
1062 };
1063
1064 placeImages( aDefinition.primitives, 0, 0 );
1065}
1066
1067
1069{
1070 m_wireEndpoints.clear();
1071
1072 for( const ORCAD_WIRE& wire : aPage.wires )
1073 {
1074 m_wireEndpoints[{ wire.x1, wire.y1 }].push_back( &wire );
1075 m_wireEndpoints[{ wire.x2, wire.y2 }].push_back( &wire );
1076 }
1077}
1078
1079
1080std::string ORCAD_CONVERTER::netAt( const ORCAD_RAW_PAGE& aPage, int aX, int aY ) const
1081{
1082 auto endIt = m_wireEndpoints.find( { aX, aY } );
1083
1084 const std::vector<const ORCAD_WIRE*>* endWires =
1085 endIt != m_wireEndpoints.end() ? &endIt->second : nullptr;
1086
1087 if( endWires )
1088 {
1089 for( const ORCAD_WIRE* wire : *endWires )
1090 {
1091 auto netIt = aPage.netmap.find( wire->id );
1092
1093 if( netIt != aPage.netmap.end() )
1094 return netIt->second;
1095 }
1096 }
1097
1098 // Also try wires passing through point.
1099 for( const ORCAD_WIRE& wire : aPage.wires )
1100 {
1101 if( onSegment( aX, aY, wire ) )
1102 {
1103 auto netIt = aPage.netmap.find( wire.id );
1104
1105 if( netIt != aPage.netmap.end() )
1106 return netIt->second;
1107 }
1108 }
1109
1110 if( endWires )
1111 {
1112 for( const ORCAD_WIRE* wire : *endWires )
1113 {
1114 for( const ORCAD_ALIAS& alias : wire->aliases )
1115 return alias.name;
1116 }
1117 }
1118
1119 return std::string();
1120}
1121
1122
1124 const ORCAD_GRAPHIC_INST& aInst ) const
1125{
1126 // Capture's Name property is the effective net name, including renamed stock power symbols.
1127 auto nameIt = aInst.props.find( "Name" );
1128
1129 if( nameIt != aInst.props.end() && !trimmed( nameIt->second ).empty() )
1130 return trimmed( nameIt->second );
1131
1132 if( !trimmed( aInst.logicalName ).empty() )
1133 return trimmed( aInst.logicalName );
1134
1135 VECTOR2I pin = graphicPinPos( aInst );
1136 std::string net = netAt( aPage, pin.x, pin.y );
1137
1138 if( net.empty() )
1139 {
1140 // No wire touches the pin (direct pin-to-pin placement): the record's resolved
1141 // logical name is the net; the symbol shape name (e.g. a "VCC_BAR" graphic
1142 // shared by differently-named rails) is only a last resort.
1143 net = aInst.logicalName.empty() ? aInst.name : aInst.logicalName;
1144 }
1145
1146 return trimmed( net );
1147}
1148
1149
1151{
1152 auto symIt = m_design.symbols.find( aInst.name );
1153
1154 if( symIt == m_design.symbols.end() || symIt->second.pins.empty() )
1155 return VECTOR2I( aInst.x, aInst.y );
1156
1157 const ORCAD_SYMBOL_DEF& sym = symIt->second;
1158
1159 int baseX = std::min( aInst.bbox.x1, aInst.bbox.x2 );
1160 int baseY = std::min( aInst.bbox.y1, aInst.bbox.y2 );
1161
1162 ORCAD_BBOX symBox = sym.bbox.value_or( ORCAD_BBOX() );
1163
1164 int width = symBox.x2 - symBox.x1;
1165 int height = symBox.y2 - symBox.y1;
1166 int orient = OrcadOrientOf( aInst.rotation, aInst.mirror );
1167
1168 const ORCAD_SYMBOL_PIN& pin = sym.pins[0];
1169
1170 return OrcadTransformPoint( orient, width, height, baseX, baseY, pin.hotptX, pin.hotptY );
1171}
1172
1173
1174std::vector<ORCAD_CONVERTER::OFFPAGE_NET>
1176{
1177 std::vector<OFFPAGE_NET> out;
1178
1179 for( size_t i = 0; i < aPage.offpage.size(); ++i )
1180 {
1181 const ORCAD_GRAPHIC_INST& conn = aPage.offpage[i];
1182 VECTOR2I pin = graphicPinPos( conn );
1183
1184 OFFPAGE_NET entry;
1185 entry.index = static_cast<int>( i );
1186
1187 // Connector name = cross-page net identity; prefer over local wire net which
1188 // OrCAD may name differently (CAM0_IO1 on net GPIO19). Global label still binds wire.
1189 entry.net = trimmed( conn.logicalName );
1190
1191 if( entry.net.empty() )
1192 entry.net = trimmed( netAt( aPage, pin.x, pin.y ) );
1193
1194 entry.x = pin.x;
1195 entry.y = pin.y;
1196
1197 out.push_back( entry );
1198 }
1199
1200 return out;
1201}
1202
1203
1204std::vector<VECTOR2I> ORCAD_CONVERTER::computeJunctions( const ORCAD_RAW_PAGE& aPage ) const
1205{
1206 std::set<std::pair<int, int>> pinPts;
1207
1208 for( const ORCAD_PLACED_INSTANCE& instance : aPage.instances )
1209 {
1210 for( const ORCAD_PIN_INST& pin : instance.pins )
1211 pinPts.insert( { pin.x, pin.y } );
1212 }
1213
1214 for( const ORCAD_DRAWN_INSTANCE& block : aPage.blocks )
1215 {
1216 for( const ORCAD_BLOCK_PIN& pin : block.pins )
1217 pinPts.insert( { pin.x, pin.y } );
1218 }
1219
1220 std::map<std::pair<int, int>, int> ends;
1221
1222 for( const ORCAD_WIRE& wire : aPage.wires )
1223 {
1224 ends[{ wire.x1, wire.y1 }]++;
1225 ends[{ wire.x2, wire.y2 }]++;
1226 }
1227
1228 std::set<std::pair<int, int>> candidates = pinPts;
1229
1230 for( const std::pair<const std::pair<int, int>, int>& end : ends )
1231 candidates.insert( end.first );
1232
1233 std::vector<VECTOR2I> out;
1234
1235 for( const std::pair<int, int>& pt : candidates )
1236 {
1237 auto endIt = ends.find( pt );
1238 int endCount = endIt != ends.end() ? endIt->second : 0;
1239 int through = 0;
1240
1241 for( const ORCAD_WIRE& wire : aPage.wires )
1242 {
1243 if( onSegment( pt.first, pt.second, wire ) )
1244 through++;
1245 }
1246
1247 int pinCount = pinPts.count( pt ) ? 1 : 0;
1248 int score = endCount + 2 * through + pinCount;
1249
1250 // Junction needed when 3+ contributions meet and at least one terminates there.
1251 if( score >= 3 && endCount + pinCount >= 1 && endCount + through >= 2 )
1252 out.emplace_back( pt.first, pt.second );
1253 }
1254
1255 return out;
1256}
1257
1258
1260{
1261 for( const VECTOR2I& pt : computeJunctions( aPage ) )
1262 aScreen->Append( new SCH_JUNCTION( OrcadDbuToIu( pt.x, pt.y ) ) );
1263}
1264
1265
1267{
1268 for( const ORCAD_WIRE& wire : aPage.wires )
1269 {
1270 SCH_LINE* line = new SCH_LINE( OrcadDbuToIu( wire.x1, wire.y1 ),
1271 wire.isBus ? LAYER_BUS : LAYER_WIRE );
1272 line->SetEndPoint( OrcadDbuToIu( wire.x2, wire.y2 ) );
1273
1274 line->SetLineWidth( OrcadLineWidthIu( wire.lineWidth ) );
1275 line->SetLineStyle( OrcadLineStyle( wire.lineStyle ) );
1276
1277 line->SetLineColor( OrcadColor( wire.color ) );
1278
1279 aScreen->Append( line );
1280
1281 for( const ORCAD_ALIAS& alias : wire.aliases )
1282 {
1283 wxString text = FromOrcadString( trimmed( alias.name ) );
1284
1285 if( text.IsEmpty() )
1286 continue;
1287
1288 // Alias stores free display position; snap anchor onto wire so label binds
1289 // electrically.
1290 VECTOR2I anchor = snapToWire( alias.x, alias.y, wire );
1291
1292 SCH_LABEL* label = new SCH_LABEL( OrcadDbuToIu( anchor.x, anchor.y ), text );
1293
1294 // Quadrants 2/3 fold to 0/90 so text reads upright.
1295 int quadrant = alias.rotation & 3;
1296
1297 label->SetSpinStyle( ( quadrant == 1 || quadrant == 3 ) ? SPIN_STYLE::UP
1299
1300 int size = textSizeIU( alias.fontIdx );
1301 label->SetTextSize( VECTOR2I( size, size ) );
1302 applyFont( label, alias.fontIdx );
1303 label->SetTextColor( OrcadColor( alias.color ) );
1304
1305 aScreen->Append( label );
1306 }
1307 }
1308}
1309
1310
1312{
1313 for( const ORCAD_BUS_ENTRY& busEntry : aPage.busEntries )
1314 {
1315 SCH_BUS_WIRE_ENTRY* entry = new SCH_BUS_WIRE_ENTRY( OrcadDbuToIu( busEntry.x1,
1316 busEntry.y1 ) );
1317 entry->SetSize( VECTOR2I( ( busEntry.x2 - busEntry.x1 ) * ORCAD_IU_PER_DBU,
1318 ( busEntry.y2 - busEntry.y1 ) * ORCAD_IU_PER_DBU ) );
1319 entry->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::DEFAULT, OrcadColor( busEntry.color ) ) );
1320 aScreen->Append( entry );
1321 }
1322}
1323
1324
1326{
1327 for( const ORCAD_GRAPHIC_INST& erc : aPage.ercObjects )
1328 aScreen->Append( new SCH_NO_CONNECT( OrcadDbuToIu( erc.x, erc.y ) ) );
1329}
1330
1331
1333{
1334 for( const OFFPAGE_NET& offpage : offpageNets( aPage ) )
1335 {
1336 if( offpage.net.empty() )
1337 {
1338 note( wxString::Format( _( "Page '%s': the off-page connector at (%d, %d) is "
1339 "unconnected in the source design; skipped." ),
1340 FromOrcadString( aPage.name ), offpage.x, offpage.y ) );
1341 continue;
1342 }
1343
1344 SCH_GLOBALLABEL* label = new SCH_GLOBALLABEL( OrcadDbuToIu( offpage.x, offpage.y ),
1345 FromOrcadString( offpage.net ) );
1347 label->SetTextColor( OrcadColor( aPage.offpage[offpage.index].color ) );
1348
1349 // Point label away from attached wire; vertical wire gives up/down, horizontal left/right.
1351 auto endIt = m_wireEndpoints.find( { offpage.x, offpage.y } );
1352
1353 if( endIt != m_wireEndpoints.end() && !endIt->second.empty() )
1354 {
1355 const ORCAD_WIRE* wire = endIt->second.front();
1356 int64_t dx = (int64_t) wire->x1 + wire->x2 - 2LL * offpage.x;
1357 int64_t dy = (int64_t) wire->y1 + wire->y2 - 2LL * offpage.y;
1358
1359 if( std::abs( dx ) >= std::abs( dy ) )
1360 spin = dx > 0 ? SPIN_STYLE::LEFT : SPIN_STYLE::RIGHT;
1361 else
1362 spin = dy > 0 ? SPIN_STYLE::UP : SPIN_STYLE::BOTTOM;
1363 }
1364
1365 label->SetSpinStyle( spin );
1366
1367 if( SCH_FIELD* refs = label->GetField( FIELD_T::INTERSHEET_REFS ) )
1368 refs->SetVisible( false );
1369
1370 aScreen->Append( label );
1371 }
1372}
1373
1374
1376 bool aHierarchical )
1377{
1378 for( const ORCAD_GRAPHIC_INST& port : aPage.ports )
1379 {
1380 std::string net = trimmed( port.logicalName.empty() ? port.name : port.logicalName );
1381
1382 if( net.empty() )
1383 continue;
1384
1385 VECTOR2I pos = graphicPinPos( port );
1386
1387 // Symbol name encodes arrow direction, e.g. "PORTLEFT-L".
1388 wxString symbolName = FromOrcadString( port.name ).Upper();
1390
1391 if( symbolName.Contains( wxS( "LEFT" ) ) )
1393 else if( symbolName.Contains( wxS( "RIGHT" ) ) )
1395
1396 SCH_LABEL_BASE* label;
1397
1398 if( aHierarchical )
1399 label = new SCH_HIERLABEL( OrcadDbuToIu( pos.x, pos.y ), FromOrcadString( net ) );
1400 else
1401 label = new SCH_GLOBALLABEL( OrcadDbuToIu( pos.x, pos.y ), FromOrcadString( net ) );
1402
1403 label->SetShape( shape );
1405 label->SetTextColor( OrcadColor( port.color ) );
1406
1407 if( SCH_GLOBALLABEL* global = dynamic_cast<SCH_GLOBALLABEL*>( label ) )
1408 {
1409 if( SCH_FIELD* refs = global->GetField( FIELD_T::INTERSHEET_REFS ) )
1410 refs->SetVisible( false );
1411 }
1412
1413 aScreen->Append( label );
1414 }
1415}
1416
1417
1419{
1420 for( const ORCAD_GRAPHIC_INST& gfx : aPage.graphics )
1421 {
1422 if( !gfx.nested )
1423 continue;
1424
1425 KIGFX::COLOR4D graphicColor = OrcadColor( gfx.color );
1426 std::function<void( const ORCAD_PRIMITIVE&, int, int )> placePrimitive =
1427 [&]( const ORCAD_PRIMITIVE& aSource, int aOffsetX, int aOffsetY )
1428 {
1429 if( aSource.kind == ORCAD_PRIM_KIND::GROUP )
1430 {
1431 for( const ORCAD_PRIMITIVE& child : aSource.children )
1432 placePrimitive( child, aOffsetX + aSource.x1, aOffsetY + aSource.y1 );
1433
1434 return;
1435 }
1436
1437 ORCAD_PRIMITIVE prim = aSource;
1438 prim.x1 += aOffsetX;
1439 prim.y1 += aOffsetY;
1440 prim.x2 += aOffsetX;
1441 prim.y2 += aOffsetY;
1442
1443 for( ORCAD_POINT& point : prim.points )
1444 {
1445 point.x += aOffsetX;
1446 point.y += aOffsetY;
1447 }
1448
1449 if( prim.start )
1450 {
1451 prim.start->x += aOffsetX;
1452 prim.start->y += aOffsetY;
1453 }
1454
1455 if( prim.end )
1456 {
1457 prim.end->x += aOffsetX;
1458 prim.end->y += aOffsetY;
1459 }
1460
1461 switch( prim.kind )
1462 {
1463 case ORCAD_PRIM_KIND::GROUP: break;
1464
1465 case ORCAD_PRIM_KIND::IMAGE: placeBitmap( prim, aScreen ); break;
1466
1468 {
1469 wxString content = FromOrcadString( prim.text );
1470
1471 if( content.IsEmpty() )
1472 break;
1473
1474 SCH_TEXT* text = new SCH_TEXT( OrcadDbuToIu( prim.x1, prim.y1 ), content );
1475
1476 // Comment text anchors at top-left of bbox.
1477 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1478 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
1479 text->SetMultilineAllowed( true );
1480
1481 int size = textSizeIU( prim.fontIdx );
1482 text->SetTextSize( VECTOR2I( size, size ) );
1483 applyFont( text, prim.fontIdx );
1484 text->SetTextColor( graphicColor );
1485
1486 // Quadrants 2/3 fold to horizontal so text reads upright.
1487 if( ( gfx.rotation & 3 ) == 1 )
1488 text->SetTextAngle( ANGLE_VERTICAL );
1489
1490 aScreen->Append( text );
1491 break;
1492 }
1493
1495 aScreen->Append( makeSheetPoly( { OrcadDbuToIu( prim.x1, prim.y1 ), OrcadDbuToIu( prim.x2, prim.y2 ) },
1496 prim, graphicColor ) );
1497 break;
1498
1500 aScreen->Append( makeSheetPoly( { OrcadDbuToIu( prim.x1, prim.y1 ), OrcadDbuToIu( prim.x2, prim.y1 ),
1501 OrcadDbuToIu( prim.x2, prim.y2 ), OrcadDbuToIu( prim.x1, prim.y2 ),
1502 OrcadDbuToIu( prim.x1, prim.y1 ) },
1503 prim, graphicColor, true ) );
1504 break;
1505
1508 {
1509 if( prim.points.size() < 2 )
1510 break;
1511
1512 std::vector<VECTOR2I> pts;
1513
1514 for( const ORCAD_POINT& pt : prim.points )
1515 pts.push_back( OrcadDbuToIu( pt.x, pt.y ) );
1516
1517 if( prim.kind == ORCAD_PRIM_KIND::POLYGON )
1518 pts.push_back( pts.front() );
1519
1520 aScreen->Append( makeSheetPoly( pts, prim, graphicColor, prim.kind == ORCAD_PRIM_KIND::POLYGON ) );
1521 break;
1522 }
1523
1525 {
1526 if( prim.points.size() < 4 || ( prim.points.size() - 1 ) % 3 != 0 )
1527 {
1528 if( prim.points.size() >= 2 )
1529 {
1530 std::vector<VECTOR2I> pts;
1531
1532 for( const ORCAD_POINT& pt : prim.points )
1533 pts.push_back( OrcadDbuToIu( pt.x, pt.y ) );
1534
1535 aScreen->Append( makeSheetPoly( pts, prim, graphicColor ) );
1536 }
1537
1538 break;
1539 }
1540
1541 for( size_t i = 0; i + 3 < prim.points.size(); i += 3 )
1542 {
1544 shape->SetPosition( OrcadDbuToIu( prim.points[i].x, prim.points[i].y ) );
1545 shape->SetBezierC1( OrcadDbuToIu( prim.points[i + 1].x, prim.points[i + 1].y ) );
1546 shape->SetBezierC2( OrcadDbuToIu( prim.points[i + 2].x, prim.points[i + 2].y ) );
1547 shape->SetEnd( OrcadDbuToIu( prim.points[i + 3].x, prim.points[i + 3].y ) );
1548
1550 OrcadLineStyle( prim.lineStyle ), graphicColor ) );
1551 shape->SetFillMode( FILL_T::NO_FILL );
1552 aScreen->Append( shape );
1553 }
1554
1555 break;
1556 }
1557
1560 {
1561 double cx = ( prim.x1 + prim.x2 ) / 2.0;
1562 double cy = ( prim.y1 + prim.y2 ) / 2.0;
1563 double rx = std::abs( prim.x2 - prim.x1 ) / 2.0;
1564 double ry = std::abs( prim.y2 - prim.y1 ) / 2.0;
1565
1566 if( rx == 0.0 )
1567 rx = 0.01;
1568
1569 if( ry == 0.0 )
1570 ry = 0.01;
1571
1572 double a0 = 0.0;
1573 double a1 = 2.0 * M_PI;
1574
1575 if( prim.kind == ORCAD_PRIM_KIND::ARC && prim.start && prim.end )
1576 {
1577 a0 = std::atan2( ( prim.start->y - cy ) / ry, ( prim.start->x - cx ) / rx );
1578 a1 = std::atan2( ( prim.end->y - cy ) / ry, ( prim.end->x - cx ) / rx );
1579
1580 // Arcs run CCW in screen (Y-down) coords.
1581 if( a1 >= a0 )
1582 a1 -= 2.0 * M_PI;
1583 }
1584
1585 int steps = std::max( 8, static_cast<int>( std::abs( a1 - a0 )
1586 / ( M_PI / 16.0 ) ) );
1587
1588 std::vector<VECTOR2I> pts;
1589
1590 for( int k = 0; k <= steps; ++k )
1591 {
1592 double a = a0 + ( a1 - a0 ) * k / steps;
1593 pts.push_back( dbuPointToIu( cx + rx * std::cos( a ),
1594 cy + ry * std::sin( a ) ) );
1595 }
1596
1597 aScreen->Append( makeSheetPoly( pts, prim, graphicColor, prim.kind == ORCAD_PRIM_KIND::ELLIPSE ) );
1598 break;
1599 }
1600 }
1601 };
1602
1603 for( const ORCAD_PRIMITIVE& sourcePrimitive : gfx.nested->primitives )
1604 placePrimitive( sourcePrimitive, 0, 0 );
1605 }
1606}
1607
1608
1609void ORCAD_CONVERTER::placeBitmap( const ORCAD_PRIMITIVE& aPrim, SCH_SCREEN* aScreen, int aOrient )
1610{
1611 int widthDbu = std::abs( aPrim.x2 - aPrim.x1 );
1612 int heightDbu = std::abs( aPrim.y2 - aPrim.y1 );
1613
1614 if( widthDbu < 2 || heightDbu < 2 || aPrim.data.empty() )
1615 return;
1616
1617 wxMemoryBuffer bmpData;
1618
1619 VECTOR2I center = dbuPointToIu( ( aPrim.x1 + aPrim.x2 ) / 2.0, ( aPrim.y1 + aPrim.y2 ) / 2.0 );
1620
1621 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>( center );
1622 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1623
1624 bool readOk = false;
1625
1626 if( MakeBmpFromDib( aPrim.data, bmpData ) )
1627 {
1628 wxLogNull noLog;
1629 readOk = refImage.ReadImageFile( bmpData );
1630 }
1631 else
1632 {
1634
1635 if( preview.type == ORCAD_OLE_PREVIEW_TYPE::BMP )
1636 {
1637 bmpData.AppendData( preview.data.data(), preview.data.size() );
1638 wxLogNull noLog;
1639 readOk = refImage.ReadImageFile( bmpData );
1640 }
1641 else if( preview.type == ORCAD_OLE_PREVIEW_TYPE::DIB && MakeBmpFromDib( preview.data, bmpData ) )
1642 {
1643 wxLogNull noLog;
1644 readOk = refImage.ReadImageFile( bmpData );
1645 }
1646 else if( preview.type == ORCAD_OLE_PREVIEW_TYPE::WMF )
1647 {
1648 wxImage image;
1649 int maxWidth = std::clamp( widthDbu * 2, 1, 4096 );
1650 int maxHeight = std::clamp( heightDbu * 2, 1, 4096 );
1651
1652 if( OrcadRenderWmf( preview.data, maxWidth, maxHeight, image ) )
1653 readOk = refImage.SetImage( image );
1654 }
1655 }
1656
1657 if( !readOk )
1658 {
1659 warn( _( "An embedded picture could not be decoded and was skipped." ) );
1660 return;
1661 }
1662
1663 VECTOR2I nativeSize = refImage.GetSize();
1664
1665 if( nativeSize.x > 0 && nativeSize.y > 0 )
1666 {
1667 double scaleX = static_cast<double>( widthDbu * ORCAD_IU_PER_DBU ) / nativeSize.x;
1668 double scaleY = static_cast<double>( heightDbu * ORCAD_IU_PER_DBU ) / nativeSize.y;
1669
1670 refImage.SetImageScale( std::min( scaleX, scaleY ) );
1671 }
1672
1673 const ORCAD_ORIENT_ENTRY& orientation = ORCAD_ORIENT_TABLE[aOrient & 7];
1674
1675 if( orientation.mirror == 'x' )
1676 bitmap->MirrorVertically( center.y );
1677 else if( orientation.mirror == 'y' )
1678 bitmap->MirrorHorizontally( center.x );
1679
1680 for( int angle = 0; angle < orientation.angle; angle += 90 )
1681 bitmap->Rotate( center, true );
1682
1683 aScreen->Append( bitmap.release() );
1684}
1685
1686
1687bool ORCAD_CONVERTER::MakeBmpFromDib( const std::vector<uint8_t>& aDib, wxMemoryBuffer& aOut )
1688{
1689 // DIB = BITMAPINFOHEADER + optional palette + pixels; prepend 14-byte BITMAPFILEHEADER
1690 // with palette-aware pixel offset for loadable .BMP.
1691 if( aDib.size() < 40 )
1692 return false;
1693
1694 auto readU16 = [&aDib]( size_t aOffset ) -> uint32_t
1695 {
1696 return static_cast<uint32_t>( aDib[aOffset] )
1697 | ( static_cast<uint32_t>( aDib[aOffset + 1] ) << 8 );
1698 };
1699
1700 auto readU32 = [&aDib]( size_t aOffset ) -> uint32_t
1701 {
1702 return static_cast<uint32_t>( aDib[aOffset] )
1703 | ( static_cast<uint32_t>( aDib[aOffset + 1] ) << 8 )
1704 | ( static_cast<uint32_t>( aDib[aOffset + 2] ) << 16 )
1705 | ( static_cast<uint32_t>( aDib[aOffset + 3] ) << 24 );
1706 };
1707
1708 uint32_t biSize = readU32( 0 );
1709
1710 // Header size outside BITMAPINFOHEADER..BITMAPV5HEADER = not DIB (rejects OLE embeds).
1711 if( biSize < 40 || biSize > 200 )
1712 return false;
1713
1714 uint32_t bitCount = readU16( 14 );
1715 uint32_t compression = readU32( 16 );
1716 uint32_t clrUsed = readU32( 32 );
1717 uint32_t paletteEntries = clrUsed ? clrUsed : ( bitCount <= 8 ? ( 1u << bitCount ) : 0 );
1718 uint32_t pixelOffset = 14 + biSize + paletteEntries * 4;
1719
1720 if( compression == 3 ) // BI_BITFIELDS, three DWORD channel masks follow header
1721 pixelOffset += 12;
1722
1723 uint32_t fileSize = 14 + static_cast<uint32_t>( aDib.size() );
1724
1725 uint8_t header[14];
1726 header[0] = 'B';
1727 header[1] = 'M';
1728 header[2] = static_cast<uint8_t>( fileSize );
1729 header[3] = static_cast<uint8_t>( fileSize >> 8 );
1730 header[4] = static_cast<uint8_t>( fileSize >> 16 );
1731 header[5] = static_cast<uint8_t>( fileSize >> 24 );
1732 header[6] = 0;
1733 header[7] = 0;
1734 header[8] = 0;
1735 header[9] = 0;
1736 header[10] = static_cast<uint8_t>( pixelOffset );
1737 header[11] = static_cast<uint8_t>( pixelOffset >> 8 );
1738 header[12] = static_cast<uint8_t>( pixelOffset >> 16 );
1739 header[13] = static_cast<uint8_t>( pixelOffset >> 24 );
1740
1741 aOut.AppendData( header, sizeof( header ) );
1742 aOut.AppendData( aDib.data(), aDib.size() );
1743
1744 return true;
1745}
1746
1747
1748VECTOR2I ORCAD_CONVERTER::snapToWire( int aX, int aY, const ORCAD_WIRE& aWire )
1749{
1750 if( aWire.x1 == aWire.x2 ) // vertical
1751 {
1752 return VECTOR2I( aWire.x1, std::clamp( aY, std::min( aWire.y1, aWire.y2 ),
1753 std::max( aWire.y1, aWire.y2 ) ) );
1754 }
1755
1756 if( aWire.y1 == aWire.y2 ) // horizontal
1757 {
1758 return VECTOR2I( std::clamp( aX, std::min( aWire.x1, aWire.x2 ),
1759 std::max( aWire.x1, aWire.x2 ) ),
1760 aWire.y1 );
1761 }
1762
1763 // diagonal, project onto segment
1764 double dx = aWire.x2 - aWire.x1;
1765 double dy = aWire.y2 - aWire.y1;
1766 double t = ( ( aX - aWire.x1 ) * dx + ( aY - aWire.y1 ) * dy ) / ( dx * dx + dy * dy );
1767
1768 t = std::clamp( t, 0.0, 1.0 );
1769
1770 return VECTOR2I( KiROUND( aWire.x1 + t * dx ), KiROUND( aWire.y1 + t * dy ) );
1771}
1772
1773
1774bool ORCAD_CONVERTER::onSegment( int aX, int aY, const ORCAD_WIRE& aWire )
1775{
1776 if( ( aX == aWire.x1 && aY == aWire.y1 ) || ( aX == aWire.x2 && aY == aWire.y2 ) )
1777 return false;
1778
1779 if( aWire.x1 == aWire.x2 && aWire.x1 == aX )
1780 return std::min( aWire.y1, aWire.y2 ) < aY && aY < std::max( aWire.y1, aWire.y2 );
1781
1782 if( aWire.y1 == aWire.y2 && aWire.y1 == aY )
1783 return std::min( aWire.x1, aWire.x2 ) < aX && aX < std::max( aWire.x1, aWire.x2 );
1784
1785 return false;
1786}
const char * name
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
constexpr coord_type GetLeft() const
Definition box2.h:224
constexpr coord_type GetRight() const
Definition box2.h:213
constexpr coord_type GetTop() const
Definition box2.h:225
constexpr coord_type GetBottom() const
Definition box2.h:218
const KIID m_Uuid
Definition eda_item.h:531
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)
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:290
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:532
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
Definition kiid.h:46
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 placeJunctions(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
Append computed junctions as SCH_JUNCTION items.
std::vector< VECTOR2I > computeJunctions(const ORCAD_RAW_PAGE &aPage) const
Junction points (DBU) from wire geometry + shared net ids.
void prepareSymbols()
Pre-pass over all pages: synthesize placeholder definitions for symbols absent from the cache (one no...
std::vector< OFFPAGE_NET > offpageNets(const ORCAD_RAW_PAGE &aPage) const
Resolve every off-page connector on the page to (index, net, pin position).
std::string netAt(const ORCAD_RAW_PAGE &aPage, int aX, int aY) const
Net name at a point: first a wire with an endpoint here whose id is in the page net table; then any w...
static constexpr int MARGIN_B_DBU
PROGRESS_REPORTER * m_progressReporter
SCH_SHEET * Convert(SCH_SHEET *aRootSheet)
Populate the schematic.
void buildNetLookup(const ORCAD_RAW_PAGE &aPage)
Rebuild m_wireEndpoints for a page (both endpoints of every wire).
void convertPage(ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen, const SCH_SHEET_PATH &aSheetPath)
Convert one page onto a screen: applyTitleBlock(), buildNetLookup(), then junctions,...
static constexpr int MARGIN_R_DBU
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 ...
void applyPageSettings(ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
Fit the page and set the screen's PAGE_INFO.
ORCAD_CONVERTER(ORCAD_DESIGN &aDesign, SCHEMATIC *aSchematic, REPORTER *aReporter, PROGRESS_REPORTER *aProgressReporter=nullptr)
static bool onSegment(int aX, int aY, const ORCAD_WIRE &aWire)
True when the point lies strictly INSIDE (not at an endpoint of) an H/V wire.
void placeBitmap(const ORCAD_PRIMITIVE &aPrim, SCH_SCREEN *aScreen, int aOrient=0)
One image primitive -> SCH_BITMAP centered on the primitive's page extent, scaled from its native pix...
~ORCAD_CONVERTER()
Out-of-line: LIB_ENTRY holds LIB_SYMBOL by unique_ptr. [orcad_converter_sheet.cpp].
SCH_SHEET * m_rootSheet
static wxString MakePageFileName(int aPageIndex, const std::string &aPageName)
"P%02d_<pagename>.kicad_sch" (1-based index), sanitized: control characters and the characters <>:"/|...
static BOX2I pageExtentDbu(const ORCAD_RAW_PAGE &aPage)
Bounding box (DBU) of everything drawable on the page: wire ends, instance placed bboxes + T0x10 pin ...
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
std::set< wxString > m_usedSheetNames
Lower-cased sheet names already emitted, to keep sibling sheet names unique.
int textSizeIU(int aFontIdx) const
Text size in IU for a font reference: 1.27 mm when the height is unknown; with a baseline,...
void placeWires(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
Wires/buses as SCH_LINE on LAYER_WIRE / LAYER_BUS, plus a local SCH_LABEL per wire alias: anchor snap...
void applyFont(EDA_TEXT *aText, int aFontIdx) const
int m_powerCount
"#PWR%04d" counter
void applyTitleBlock(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
Fill the screen TITLE_BLOCK from the first page title block carrying properties: Title,...
void placeDefinitionImages(const ORCAD_SYMBOL_DEF &aDefinition, int aBaseX, int aBaseY, int aOrient, SCH_SCREEN *aScreen)
static void offsetPage(ORCAD_RAW_PAGE &aPage, int aDx, int aDy)
Shift every drawable coordinate on the page by (aDx, aDy) DBU: wires and their aliases,...
void placeOffpageConnectors(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
SCH_GLOBALLABEL per resolved off-page connector, at the connection point; an unconnected connector (e...
static constexpr int MARGIN_T_DBU
SCHEMATIC * m_schematic
void placeGraphics(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
Free page graphics from the nested primitives of each Graphic*Inst (the outer record itself is not dr...
void warn(const wxString &aMsg)
Conversion problem: REPORTER at RPT_SEVERITY_WARNING.
void placeDefinitionVectors(const ORCAD_SYMBOL_DEF &aDefinition, int aBaseX, int aBaseY, int aOrient, SCH_SCREEN *aScreen)
REPORTER * m_reporter
void placeNoConnects(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
SCH_NO_CONNECT per ERC object, at the object's anchor.
void placePorts(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen, bool aHierarchical)
SCH_HIERLABEL per port on a hierarchical child page, otherwise SCH_GLOBALLABEL.
static VECTOR2I snapToWire(int aX, int aY, const ORCAD_WIRE &aWire)
Closest point (DBU) on a wire segment to (aX, aY); exact for H/V wires.
static constexpr int MARGIN_L_DBU
Page content margins, DBU.
std::string powerNet(const ORCAD_RAW_PAGE &aPage, const ORCAD_GRAPHIC_INST &aInst) const
Net stamped by a power-symbol instance: the net of the wire its pin touches (authoritative — ports ca...
std::map< std::pair< int, int >, std::vector< const ORCAD_WIRE * > > m_wireEndpoints
Per-page wire lookup: endpoint -> wires ending there. Rebuilt by buildNetLookup().
void placeBusEntries(const ORCAD_RAW_PAGE &aPage, SCH_SCREEN *aScreen)
SCH_BUS_WIRE_ENTRY per bus entry (position x1,y1; size x2-x1, y2-y1).
static wxString SanitizeFileName(const std::string &aName)
Filesystem-safe name: control characters and <>:"/|?
static bool MakeBmpFromDib(const std::vector< uint8_t > &aDib, wxMemoryBuffer &aOut)
Synthesize a .BMP in front of a raw DIB (BITMAPINFOHEADER + optional palette + pixels): pixel data of...
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
bool SetType(PAGE_SIZE_TYPE aPageSize, bool aIsPortrait=false)
Set the name of the page type and also the sizes and margins commonly associated with that type name.
static void SetCustomWidthMils(double aWidthInMils)
Set the width of Custom page in mils for any custom page constructed or made via SetType() after maki...
static void SetCustomHeightMils(double aHeightInMils)
Set the height of Custom page in mils for any custom page constructed or made via SetType() after mak...
const PAGE_SIZE_TYPE & GetType() const
Definition page_info.h:98
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void Report(const wxString &aMessage)=0
Display aMessage in the progress bar dialog.
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
bool SetImage(const wxImage &aImage)
Set the image from an existing wxImage.
VECTOR2I GetSize() const
void SetImageScale(double aScale)
Set the image "zoom" value.
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:72
Holds all the data relating to one schematic.
Definition schematic.h:90
void SetSize(const VECTOR2I &aSize)
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Class for a wire to bus entry.
void SetText(const wxString &aText) override
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this label.
void SetSpinStyle(SPIN_STYLE aSpinStyle) override
void SetShape(LABEL_FLAG_SHAPE aShape)
Definition sch_label.h:179
virtual void SetSpinStyle(SPIN_STYLE aSpinStyle)
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
void SetLineColor(const COLOR4D &aColor)
Definition sch_line.cpp:319
void SetLineWidth(const int aSize)
Definition sch_line.cpp:385
void SetLineStyle(const LINE_STYLE aStyle)
Definition sch_line.cpp:356
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:145
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:164
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:138
const KIID & GetUuid() const
Definition sch_screen.h:529
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
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...
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
size_t size() const
Forwarded method from std::vector.
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:44
void AddPin(SCH_SHEET_PIN *aSheetPin)
Add aSheetPin to the sheet.
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:139
void SetScreen(SCH_SCREEN *aScreen)
Set the SCH_SCREEN associated with this sheet to aScreen.
Simple container to manage line stroke parameters.
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:37
void SetRevision(const wxString &aRevision)
Definition title_block.h:77
void SetComment(int aIdx, const wxString &aComment)
Definition title_block.h:97
void SetTitle(const wxString &aTitle)
Definition title_block.h:54
void SetCompany(const wxString &aCompany)
Definition title_block.h:87
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:67
A type-safe container of any type.
Definition ki_any.h:92
#define _(s)
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
@ NO_FILL
Definition eda_shape.h:60
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:61
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
@ LAYER_WIRE
Definition layer_ids.h:458
@ LAYER_NOTES
Definition layer_ids.h:473
@ LAYER_BUS
Definition layer_ids.h:459
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 (...
int OrcadLineWidthIu(int aWidth)
int OrcadPageOrder(wxString &aName)
Return a numeric page prefix (or -1); strip only the "N - title" convention.
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)
int OrcadPageOrder(wxString &aName)
Return a numeric page prefix (or -1); strip only the "N - title" convention.
KIGFX::COLOR4D OrcadColor(int aColorIndex)
ORCAD_OLE_PREVIEW OrcadExtractOlePreview(const std::vector< uint8_t > &aPayload)
Definition orcad_ole.cpp:78
bool OrcadRenderWmf(const std::vector< uint8_t > &aWmf, int aMaxWidth, int aMaxHeight, wxImage &aImage)
wxString FromOrcadString(const std::string &aText)
Decode raw stream bytes (Windows-1252) into a wxString for UI/UX use.
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_INFO
LABEL_FLAG_SHAPE
Definition sch_label.h:97
@ L_BIDI
Definition sch_label.h:100
@ L_TRISTATE
Definition sch_label.h:101
@ L_UNSPECIFIED
Definition sch_label.h:102
@ L_OUTPUT
Definition sch_label.h:99
@ L_INPUT
Definition sch_label.h:98
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
Net alias attached to a wire (becomes a local label).
std::string name
int rotation
0..3 quarter turns
Axis-aligned box in OrCAD DBU; corner order as stored (not normalized).
One interface pin of a hierarchical block, at its absolute page position.
ORCAD_PORT_TYPE portType
std::string name
Bus entry (structure type 29).
int y1
int color
int x2
int y2
int x1
One resolved off-page connector: index into page.offpage, net, pin position.
Everything parsed from one .DSN, as handed to ORCAD_CONVERTER.
Structure type 12: a hierarchical block instance placed on a page.
int x1
block rectangle top-left, page DBU
std::vector< ORCAD_BLOCK_PIN > pins
Shared body of Port / Global / OffPageConnector / TitleBlock / ERCObject and the Graphic*Inst shapes ...
std::unique_ptr< ORCAD_SYMBOL_DEF > nested
SthInPages0 body, else nullptr.
std::map< std::string, std::string > props
int rotation
0..3 quarter turns
std::string name
cache symbol name
std::string logicalName
ports: resolved net/port name
A hierarchical block occurrence: one placement of a child schematic folder on a parent page.
uint32_t targetDbId
type-12 drawn-instance dbId on the parent page
ORCAD_OCC_SCOPE scope
the child's occurrences under this path
std::string childFolder
child schematic folder name
One node of the design occurrence tree parsed from the root Hierarchy stream.
std::map< uint32_t, std::string > partRefs
type-13 dbId -> occurrence refdes
std::vector< uint8_t > data
Definition orcad_ole.h:31
ORCAD_OLE_PREVIEW_TYPE type
Definition orcad_ole.h:30
One entry of the 8-orientation placement table.
char mirror
0 = none, 'x' or 'y' = KiCad mirror axis
int angle
KiCad placement angle in degrees (0/90/180/270, CCW)
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).
ORCAD_BBOX bbox
placed box, page DBU
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
std::vector< uint8_t > data
kind == IMAGE: raw embedded payload
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::vector< ORCAD_GRAPHIC_INST > ports
std::string name
std::vector< ORCAD_GRAPHIC_INST > globals
placed power symbols
std::map< uint32_t, std::string > netmap
net db id -> net name
std::vector< ORCAD_WIRE > wires
uint32_t width
mils, or um when isMetric
std::vector< ORCAD_DRAWN_INSTANCE > blocks
hierarchical blocks (detection only)
std::vector< ORCAD_PLACED_INSTANCE > instances
std::vector< ORCAD_GRAPHIC_INST > graphics
free comment text/shapes/images
std::vector< ORCAD_GRAPHIC_INST > offpage
off-page connectors
std::vector< ORCAD_GRAPHIC_INST > titleBlocks
std::vector< ORCAD_BUS_ENTRY > busEntries
std::string pageSize
page-size name string, e.g. "B"
std::vector< ORCAD_GRAPHIC_INST > ercObjects
no-connect markers
A symbol definition from the design Cache (LibraryPart / GlobalSymbol / PortSymbol / OffPageSymbol / ...
std::vector< ORCAD_SYMBOL_PIN > pins
std::vector< ORCAD_PRIMITIVE > primitives
std::optional< ORCAD_BBOX > bbox
symbol-space body box
One pin of a symbol definition (structure types 26/27).
One wire or bus segment.
uint32_t id
bool isBus
true for structure type 21
std::vector< ORCAD_ALIAS > aliases
@ INTERSHEET_REFS
Global label cross-reference page numbers.
KIBIS_PIN * pin
VECTOR2I center
VECTOR2I end
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683