KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pads_sch_binary_builder.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 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your option)
9 * any later version.
10 */
11
13
14#include <bitmap_base.h>
15#include <lib_id.h>
16#include <lib_symbol.h>
17#include <connection_graph.h>
18#include <embedded_files.h>
19#include <font/font.h>
20#include <page_info.h>
21#include <pin_type.h>
22#include <sch_bus_entry.h>
23#include <sch_bitmap.h>
24#include <sch_field.h>
25#include <sch_junction.h>
26#include <sch_label.h>
27#include <sch_line.h>
28#include <sch_pin.h>
29#include <sch_screen.h>
30#include <sch_shape.h>
31#include <sch_sheet.h>
32#include <sch_sheet_path.h>
33#include <sch_symbol.h>
34#include <sch_text.h>
35#include <schematic.h>
36#include <schematic_settings.h>
37#include <sch_io/ole_image.h>
39#include <stroke_params.h>
40#include <title_block.h>
41
42#include <ki_exception.h>
44
45#include <algorithm>
46#include <array>
47#include <cmath>
48#include <functional>
49#include <limits>
50#include <locale>
51#include <map>
52#include <memory>
53#include <set>
54#include <sstream>
55#include <tuple>
56#include <utility>
57#include <wx/buffer.h>
58#include <wx/image.h>
59#include <wx/log.h>
60
62{
63namespace
64{
65
66 int toIU( int64_t aHalfMils )
67 {
68 return schIUScale.MilsToIU( static_cast<double>( aHalfMils ) / 2.0 );
69 }
70
71
72 VECTOR2I localPoint( const SOURCE_POINT& aPoint )
73 {
74 return { toIU( aPoint.x ), -toIU( aPoint.y ) };
75 }
76
77
78 // A saved schematic keys its library on the LIB_ID, so two placements of one part type that
79 // build different symbols have to be told apart. Keyed by part-type name, valued by the
80 // distinct symbols seen under it and the unique name each was given.
81 using LIBRARY_SYMBOL_VARIANTS = std::map<wxString, std::vector<std::pair<std::unique_ptr<LIB_SYMBOL>, wxString>>>;
82
83
84 VECTOR2I pagePoint( const SOURCE_POINT& aPoint, int aPageHeight )
85 {
86 return { toIU( aPoint.x ), aPageHeight - toIU( aPoint.y ) };
87 }
88
89
90 std::unique_ptr<SCH_BITMAP> makeEmbeddedImage( const MODEL_EMBEDDED_IMAGE& aSource, int aPageHeight,
91 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
92 {
93 if( aSource.size.x <= 0 || aSource.size.y <= 0 )
94 {
95 aDiagnostics.emplace_back( RPT_SEVERITY_WARNING, aSource.source,
96 wxS( "embedded OLE image has a degenerate page box and was skipped" ) );
97 return nullptr;
98 }
99
100 auto bitmap = std::make_unique<SCH_BITMAP>( pagePoint( aSource.position, aPageHeight ) );
101 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
102 bool decoded = false;
103 wxMemoryBuffer buffer;
104
105 switch( aSource.type )
106 {
108 buffer.AppendData( aSource.data.data(), aSource.data.size() );
109 {
110 wxLogNull noLog;
111 decoded = refImage.ReadImageFile( buffer );
112 }
113 break;
114
116 if( OleMakeBmpFromDib( aSource.data, buffer ) )
117 {
118 wxLogNull noLog;
119 decoded = refImage.ReadImageFile( buffer );
120 }
121 break;
122
124 {
125 wxImage image;
126 int width = std::clamp<int64_t>( std::abs( static_cast<int64_t>( aSource.extent[2] ) - aSource.extent[0] ),
127 1, 4096 );
128 int height = std::clamp<int64_t>( std::abs( static_cast<int64_t>( aSource.extent[3] ) - aSource.extent[1] ),
129 1, 4096 );
130
131 if( OleRenderWmf( aSource.data, width, height, image ) )
132 {
133 // Clamp before rounding; llround on an out-of-range double is undefined
134 double scaledHeight = static_cast<double>( width ) * aSource.size.y / aSource.size.x;
135 int fittedHeight =
136 static_cast<int>( std::llround( std::clamp( scaledHeight, 1.0, 4096.0 ) ) );
137 image.Rescale( width, fittedHeight, wxIMAGE_QUALITY_HIGH );
138 decoded = refImage.SetImage( image );
139 }
140
141 break;
142 }
143
145 }
146
147 if( !decoded )
148 {
149 aDiagnostics.emplace_back( RPT_SEVERITY_WARNING, aSource.source,
150 wxS( "embedded OLE image could not be rasterized and was skipped" ) );
151 return nullptr;
152 }
153
154 const int targetWidth = toIU( aSource.size.x );
155 const int targetHeight = toIU( aSource.size.y );
156
157 if( targetWidth <= 0 || targetHeight <= 0 )
158 THROW_IO_ERROR( FormatParserError( aSource.source, wxS( "embedded image has invalid page size" ) ) );
159
160 const wxImage* decodedImage = refImage.GetImage().GetImageData();
161
162 if( decodedImage && decodedImage->IsOk() )
163 {
164 const int fittedHeight =
165 std::max( 1, static_cast<int>( std::llround( static_cast<double>( decodedImage->GetWidth() )
166 * targetHeight / targetWidth ) ) );
167
168 if( fittedHeight != decodedImage->GetHeight() )
169 {
170 wxImage fitted = decodedImage->Copy();
171 fitted.Rescale( fitted.GetWidth(), fittedHeight, wxIMAGE_QUALITY_HIGH );
172 refImage.SetImage( fitted );
173 }
174 }
175
176 refImage.SetWidth( targetWidth );
177
178 const VECTOR2I center = bitmap->GetPosition();
179
180 if( aSource.mirrorHorizontal )
181 bitmap->MirrorHorizontally( center.x );
182
183 if( aSource.mirrorVertical )
184 bitmap->MirrorVertically( center.y );
185
186 return bitmap;
187 }
188
189
190 LINE_STYLE lineStyle( MODEL_LINE_STYLE aStyle )
191 {
192 switch( aStyle )
193 {
199 }
200
201 return LINE_STYLE::SOLID;
202 }
203
204
205 FILL_T fillStyle( MODEL_FILL_STYLE aFill )
206 {
207 switch( aFill )
208 {
212 }
213
214 return FILL_T::NO_FILL;
215 }
216
217
218 ELECTRICAL_PINTYPE pinType( uint32_t aType )
219 {
220 switch( aType )
221 {
222 case 0: return ELECTRICAL_PINTYPE::PT_PASSIVE;
223 case 1: return ELECTRICAL_PINTYPE::PT_INPUT;
224 case 2: return ELECTRICAL_PINTYPE::PT_OUTPUT;
225 case 3: return ELECTRICAL_PINTYPE::PT_BIDI;
226 case 4: return ELECTRICAL_PINTYPE::PT_TRISTATE;
229 case 7: return ELECTRICAL_PINTYPE::PT_POWER_IN;
231 }
232 }
233
234
235 GRAPHIC_PINSHAPE pinShape( uint32_t aStyle )
236 {
237 switch( aStyle )
238 {
239 case 1: return GRAPHIC_PINSHAPE::INVERTED;
240 case 2: return GRAPHIC_PINSHAPE::CLOCK;
242 default: return GRAPHIC_PINSHAPE::LINE;
243 }
244 }
245
246
247 PIN_ORIENTATION pinOrientation( const MODEL_PIN_DEFINITION& aPin )
248 {
249 const int angle = NormalizeAngle( aPin.angle );
250 const bool verticalDecal = aPin.decalName.text.Contains( wxS( "VRT" ) );
251
252 if( verticalDecal )
254
255 switch( angle )
256 {
257 case 900: return aPin.side >= 2 ? PIN_ORIENTATION::PIN_DOWN : PIN_ORIENTATION::PIN_UP;
258 case 1800: return ( aPin.side & 1 ) != 0 ? PIN_ORIENTATION::PIN_RIGHT : PIN_ORIENTATION::PIN_LEFT;
259 case 2700: return aPin.side >= 2 ? PIN_ORIENTATION::PIN_UP : PIN_ORIENTATION::PIN_DOWN;
260 default: return ( aPin.side & 1 ) != 0 ? PIN_ORIENTATION::PIN_LEFT : PIN_ORIENTATION::PIN_RIGHT;
261 }
262 }
263
264
265 GR_TEXT_H_ALIGN_T horizontalJustification( MODEL_JUSTIFICATION aJustification )
266 {
267 switch( aJustification )
268 {
271 default: return GR_TEXT_H_ALIGN_LEFT;
272 }
273 }
274
275
276 GR_TEXT_V_ALIGN_T verticalJustification( MODEL_JUSTIFICATION aJustification )
277 {
278 switch( aJustification )
279 {
282 default: return GR_TEXT_V_ALIGN_CENTER;
283 }
284 }
285
286
287 void applyTextPresentation( EDA_TEXT* aText, const MODEL_TEXT_PRESENTATION& aPresentation, bool aVisible,
288 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
289 {
290 if( aPresentation.height > 0 )
291 aText->SetTextSize( { toIU( aPresentation.height ), toIU( aPresentation.height ) } );
292
293 aText->SetHorizJustify( horizontalJustification( aPresentation.horizontalJustification ) );
294 aText->SetVertJustify( verticalJustification( aPresentation.verticalJustification ) );
295 aText->SetBold( aPresentation.bold );
296 aText->SetItalic( aPresentation.italic );
297 aText->SetVisible( aVisible );
298
299 if( aPresentation.width > 0 )
300 aText->SetTextThickness( toIU( aPresentation.width ) );
301
302 if( !aPresentation.font.text.IsEmpty() && aPresentation.font.text != wxS( "Default Font" ) )
303 aText->SetFont(
304 KIFONT::FONT::GetFont( aPresentation.font.text, aPresentation.bold, aPresentation.italic ) );
305
306 // PADS gives the rendered stroke; run it back through the bold factor once the face is
307 // resolved, since only a stroke font applies that factor
309
310 if( aPresentation.underline )
311 {
312 aDiagnostics.push_back( MakePropertyDiagnostic( RPT_SEVERITY_WARNING, aPresentation.source,
313 wxS( "underline" ), PROPERTY_DISPOSITION::UNSUPPORTED,
314 wxS( "PADS underline presentation is unsupported" ) ) );
315 }
316 }
317
318
319 std::unique_ptr<SCH_SHAPE> makeShape( const MODEL_GRAPHIC& aGraphic, bool aPageCoordinates, int aPageHeight )
320 {
321 if( aGraphic.kind == MODEL_GRAPHIC_KIND::TEXT || aGraphic.points.size() < 2 )
322 THROW_IO_ERROR( FormatParserError( aGraphic.source, wxS( "graphic has inconsistent geometry" ) ) );
323
324 auto convert = [&]( const SOURCE_POINT& aPoint )
325 {
326 return aPageCoordinates ? pagePoint( aPoint, aPageHeight ) : localPoint( aPoint );
327 };
328
329 SHAPE_T shapeType = SHAPE_T::POLY;
330
331 switch( aGraphic.kind )
332 {
333 case MODEL_GRAPHIC_KIND::RECTANGLE: shapeType = SHAPE_T::RECTANGLE; break;
334 case MODEL_GRAPHIC_KIND::CIRCLE: shapeType = SHAPE_T::CIRCLE; break;
335 case MODEL_GRAPHIC_KIND::ARC: shapeType = SHAPE_T::ARC; break;
336 default: shapeType = SHAPE_T::POLY; break;
337 }
338
339 auto shape = std::make_unique<SCH_SHAPE>( shapeType, aPageCoordinates ? LAYER_NOTES : LAYER_DEVICE );
340
341 if( aGraphic.kind == MODEL_GRAPHIC_KIND::RECTANGLE && aGraphic.points.size() >= 2 )
342 {
343 shape->SetStart( convert( aGraphic.points[0] ) );
344 shape->SetEnd( convert( aGraphic.points[1] ) );
345 }
346 else if( aGraphic.kind == MODEL_GRAPHIC_KIND::CIRCLE && aGraphic.points.size() >= 2 )
347 {
349 center.x = ( aGraphic.points[0].x + aGraphic.points[1].x ) / 2;
350 center.y = ( aGraphic.points[0].y + aGraphic.points[1].y ) / 2;
351 shape->SetStart( convert( center ) );
352 shape->SetEnd( convert( aGraphic.points[0] ) );
353 }
354 else if( aGraphic.kind == MODEL_GRAPHIC_KIND::ARC && aGraphic.points.size() >= 2 )
355 {
356 const SOURCE_POINT& start = aGraphic.points.front();
357 const SOURCE_POINT& end = aGraphic.points.back();
358 double startAngle = std::atan2( static_cast<double>( start.y - aGraphic.arcCenter.y ),
359 static_cast<double>( start.x - aGraphic.arcCenter.x ) );
360 double sweep = std::abs( aGraphic.arcSweepAngle ) * M_PI / 1800.0;
361
362 if( aGraphic.arcClockwise )
363 sweep = -sweep;
364
365 double radius = std::hypot( static_cast<double>( start.x - aGraphic.arcCenter.x ),
366 static_cast<double>( start.y - aGraphic.arcCenter.y ) );
367 SOURCE_POINT mid;
368 mid.x = aGraphic.arcCenter.x + std::llround( radius * std::cos( startAngle + sweep / 2.0 ) );
369 mid.y = aGraphic.arcCenter.y + std::llround( radius * std::sin( startAngle + sweep / 2.0 ) );
370 shape->SetArcGeometry( convert( start ), convert( mid ), convert( end ) );
371 }
372 else
373 {
374 for( const SOURCE_POINT& point : aGraphic.points )
375 shape->AddPoint( convert( point ) );
376 }
377
378 shape->SetStroke( STROKE_PARAMS( toIU( aGraphic.strokeWidth ), lineStyle( aGraphic.lineStyle ) ) );
379 shape->SetFillMode( fillStyle( aGraphic.fill ) );
380 return shape;
381 }
382
383
384 std::unique_ptr<SCH_TEXT> makeGraphicText( const MODEL_GRAPHIC& aGraphic,
385 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
386 {
387 if( aGraphic.points.empty() )
388 THROW_IO_ERROR( FormatParserError( aGraphic.source, wxS( "symbol text has no position" ) ) );
389
390 auto text =
391 std::make_unique<SCH_TEXT>( localPoint( aGraphic.points.front() ), aGraphic.text.text, LAYER_DEVICE );
392 text->SetTextAngle( EDA_ANGLE( aGraphic.angle, TENTHS_OF_A_DEGREE_T ) );
393 applyTextPresentation( text.get(), aGraphic.presentation, aGraphic.presentation.visible, aDiagnostics );
394 return text;
395 }
396
397
398 std::unique_ptr<SCH_TEXT> makePageText( const SOURCE_STRING& aText, const SOURCE_POINT& aPosition, int aAngle,
399 const MODEL_TEXT_PRESENTATION& aPresentation, int aPageHeight,
400 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
401 {
402 auto text = std::make_unique<SCH_TEXT>( pagePoint( aPosition, aPageHeight ), aText.text, LAYER_NOTES );
403 text->SetTextAngle( EDA_ANGLE( aAngle, TENTHS_OF_A_DEGREE_T ) );
404
405 // The s-expression writer emits no visibility for a plain text and the reader forces it
406 // visible, so importing hidden would disagree with the first save
407 if( !aPresentation.visible )
408 {
409 aDiagnostics.push_back( MakePropertyDiagnostic( RPT_SEVERITY_WARNING, aPresentation.source,
410 wxS( "display_flags" ), PROPERTY_DISPOSITION::UNSUPPORTED,
411 wxS( "hidden PADS text is unsupported" ) ) );
412 }
413
414 applyTextPresentation( text.get(), aPresentation, true, aDiagnostics );
415 return text;
416 }
417
418
419 size_t appendPageGraphic( SCH_SCREEN* aScreen, const MODEL_GRAPHIC& aGraphic, int aPageHeight,
420 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
421 {
422 if( aGraphic.kind == MODEL_GRAPHIC_KIND::TEXT )
423 {
424 if( aGraphic.points.empty() )
425 THROW_IO_ERROR( FormatParserError( aGraphic.source, wxS( "page text has no position" ) ) );
426
427 std::unique_ptr<SCH_TEXT> text = makePageText( aGraphic.text, aGraphic.points.front(), aGraphic.angle,
428 aGraphic.presentation, aPageHeight, aDiagnostics );
429 aScreen->Append( text.get() );
430 text.release();
431 return 1;
432 }
433
434 if( ( aGraphic.kind == MODEL_GRAPHIC_KIND::LINE || aGraphic.kind == MODEL_GRAPHIC_KIND::POLYLINE )
435 && aGraphic.fill == MODEL_FILL_STYLE::NONE )
436 {
437 if( aGraphic.points.size() < 2 )
439 FormatParserError( aGraphic.source, wxS( "page polyline has inconsistent geometry" ) ) );
440
441 for( size_t point = 1; point < aGraphic.points.size(); ++point )
442 {
443 auto line =
444 std::make_unique<SCH_LINE>( pagePoint( aGraphic.points[point - 1], aPageHeight ), LAYER_NOTES );
445 line->SetEndPoint( pagePoint( aGraphic.points[point], aPageHeight ) );
446 line->SetStroke( STROKE_PARAMS( toIU( aGraphic.strokeWidth ), lineStyle( aGraphic.lineStyle ) ) );
447 aScreen->Append( line.get() );
448 line.release();
449 }
450
451 return aGraphic.points.size() - 1;
452 }
453
454 std::unique_ptr<SCH_SHAPE> shape = makeShape( aGraphic, true, aPageHeight );
455
456 if( aGraphic.fill == MODEL_FILL_STYLE::FILLED )
457 shape->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
458
459 aScreen->Append( shape.get() );
460 shape.release();
461 return 1;
462 }
463
464
465 std::unique_ptr<LIB_SYMBOL> makePadsPowerLibrary( const MODEL_LABEL& aLabel )
466 {
467 wxString name;
468
469 if( aLabel.kind == MODEL_LABEL_KIND::GROUND )
470 name = aLabel.symbolVariant == 1 ? wxS( "PADS_GNDA" )
471 : aLabel.symbolVariant == 2 ? wxS( "PADS_GNDCH" )
472 : wxS( "PADS_GND" );
473 else
474 name = wxString::Format( wxS( "PADS_POWER_%u" ), aLabel.symbolVariant );
475
476 auto symbol = std::make_unique<LIB_SYMBOL>( name );
477 symbol->SetGlobalPower();
478 symbol->SetShowPinNumbers( false );
479 symbol->SetShowPinNames( false );
480
481 auto mil = []( int aMils )
482 {
483 return schIUScale.MilsToIU( aMils );
484 };
485 auto addLine = [&]( std::initializer_list<VECTOR2I> aPoints )
486 {
487 auto line = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
488
489 for( const VECTOR2I& point : aPoints )
490 line->AddPoint( point );
491
492 line->SetStroke( STROKE_PARAMS( mil( 10 ), LINE_STYLE::SOLID ) );
493 symbol->AddDrawItem( line.release() );
494 };
495 auto addTriangle = [&]( int aStemLength )
496 {
497 addLine( { { 0, 0 }, { 0, mil( aStemLength ) } } );
498 auto triangle = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
499 triangle->AddPoint( { 0, mil( aStemLength ) } );
500 triangle->AddPoint( { -mil( 50 ), mil( 100 ) } );
501 triangle->AddPoint( { mil( 50 ), mil( 100 ) } );
502 triangle->AddPoint( { 0, mil( aStemLength ) } );
503 triangle->SetStroke( STROKE_PARAMS( mil( 10 ), LINE_STYLE::SOLID ) );
504 triangle->SetFillMode( FILL_T::FILLED_SHAPE );
505 symbol->AddDrawItem( triangle.release() );
506 };
507
508 if( aLabel.kind == MODEL_LABEL_KIND::GROUND )
509 {
510 switch( aLabel.symbolVariant )
511 {
512 case 1:
513 addLine( { { 0, 0 },
514 { 0, -mil( 50 ) },
515 { -mil( 100 ), -mil( 50 ) },
516 { 0, -mil( 200 ) },
517 { mil( 100 ), -mil( 50 ) },
518 { 0, -mil( 50 ) } } );
519 break;
520
521 case 2:
522 addLine( { { 0, 0 }, { 0, -mil( 100 ) } } );
523 addLine( { { -mil( 100 ), -mil( 100 ) }, { mil( 100 ), -mil( 100 ) } } );
524 addLine( { { -mil( 100 ), -mil( 100 ) }, { -mil( 150 ), -mil( 200 ) } } );
525 addLine( { { 0, -mil( 100 ) }, { -mil( 50 ), -mil( 200 ) } } );
526 addLine( { { mil( 100 ), -mil( 100 ) }, { mil( 50 ), -mil( 200 ) } } );
527 break;
528
529 default:
530 addLine( { { 0, 0 }, { 0, -mil( 100 ) } } );
531 addLine( { { -mil( 100 ), -mil( 100 ) }, { mil( 100 ), -mil( 100 ) } } );
532 addLine( { { -mil( 60 ), -mil( 150 ) }, { mil( 60 ), -mil( 150 ) } } );
533 addLine( { { -mil( 20 ), -mil( 200 ) }, { mil( 20 ), -mil( 200 ) } } );
534 break;
535 }
536 }
537 else if( aLabel.symbolVariant == 1 || aLabel.symbolVariant == 3 )
538 {
539 addTriangle( 250 );
540 }
541 else if( aLabel.symbolVariant == 4 )
542 {
543 addTriangle( 200 );
544 }
545 else
546 {
547 addLine( { { 0, 0 }, { 0, mil( 100 ) } } );
548 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
549 circle->SetCenter( { 0, mil( 150 ) } );
550 circle->SetEnd( { mil( 50 ), mil( 150 ) } );
551 circle->SetStroke( STROKE_PARAMS( mil( 10 ), LINE_STYLE::SOLID ) );
552 circle->SetFillMode( FILL_T::NO_FILL );
553 symbol->AddDrawItem( circle.release() );
554 }
555
556 auto pin = std::make_unique<SCH_PIN>( symbol.get() );
557 pin->SetNumber( wxS( "1" ) );
558 pin->SetName( name );
560 pin->SetVisible( false );
561 pin->SetLength( 0 );
562 pin->SetPosition( { 0, 0 } );
565 symbol->AddDrawItem( pin.release() );
566 symbol->GetReferenceField().SetText( wxS( "#PWR" ) );
567 symbol->GetReferenceField().SetVisible( false );
568 return symbol;
569 }
570
571
572 std::unique_ptr<SCH_SYMBOL> makePowerSymbol( const MODEL_LABEL& aLabel, const SCH_SHEET_PATH& aPath,
573 int aPageHeight, int aOrdinal,
574 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
575 {
576 int orientation = SYM_ORIENT_180;
577
578 if( aLabel.kind == MODEL_LABEL_KIND::POWER && ( aLabel.symbolVariant == 2 || aLabel.symbolVariant == 3 ) )
579 {
580 orientation = SYM_ORIENT_0;
581 }
582
583 std::unique_ptr<LIB_SYMBOL> library = makePadsPowerLibrary( aLabel );
584
585 if( !library )
586 THROW_IO_ERROR( FormatParserError( aLabel.source, wxS( "could not construct power symbol" ) ) );
587
588 auto symbol = std::make_unique<SCH_SYMBOL>();
589 LIB_ID libId;
590 libId.SetLibNickname( wxS( "pads_import" ) );
591 libId.SetLibItemName( library->GetName() );
592 symbol->SetLibId( libId );
593 symbol->SetLibSymbol( library.release() );
594 symbol->SetPosition( pagePoint( aLabel.position, aPageHeight ) );
595 symbol->SetOrientation( orientation );
596 const wxString reference = wxString::Format( wxS( "#PWR%04d" ), aOrdinal );
597 symbol->SetRef( &aPath, reference );
598 symbol->AddHierarchicalReference( aPath.Path(), reference, 1 );
599 symbol->SetValueFieldText( aLabel.text.text, &aPath );
600
601 if( SCH_FIELD* field = symbol->GetField( FIELD_T::REFERENCE ) )
602 field->SetVisible( false );
603
604 if( SCH_FIELD* value = symbol->GetField( FIELD_T::VALUE ) )
605 {
606 value->SetText( aLabel.text.text );
607 SOURCE_POINT textPosition = aLabel.position;
608 textPosition.x += aLabel.textOffset.x;
609 textPosition.y += aLabel.textOffset.y;
610 value->SetPosition( pagePoint( textPosition, aPageHeight ) );
611 value->SetTextAngle( EDA_ANGLE( aLabel.angle, TENTHS_OF_A_DEGREE_T ) );
612 applyTextPresentation( value, aLabel.presentation, aLabel.presentation.visible, aDiagnostics );
613 }
614
615 return symbol;
616 }
617
618
619 std::unique_ptr<SCH_PIN> makePin( const MODEL_PIN_DEFINITION& aPin, LIB_SYMBOL* aParent )
620 {
621 auto pin = std::make_unique<SCH_PIN>( aParent );
622 pin->SetNumber( aPin.number.text );
623 pin->SetName( aPin.name.text );
624 pin->SetPosition( localPoint( aPin.position ) );
625 pin->SetOrientation( pinOrientation( aPin ) );
626 pin->SetLength( toIU( aPin.length ) );
627 pin->SetType( pinType( aPin.electricalType ) );
628 pin->SetShape( pinShape( aPin.graphicStyle ) );
629 pin->SetVisible( aPin.presentation.visible );
630
631 // toIU already halves, the same as applyTextPresentation; a second /2 here made pin text
632 // half the size of every other imported string
633 pin->SetNameTextSize( toIU( aPin.namePresentation.height ) );
634 pin->SetNumberTextSize( toIU( aPin.numberPresentation.height ) );
635
636 return pin;
637 }
638
639
640 struct MODEL_INDEX
641 {
642 using POINT_KEY = std::tuple<uint32_t, int64_t, int64_t>;
643
644 explicit MODEL_INDEX( const PADS_SCH_MODEL& aModel )
645 {
646 for( const MODEL_PLACEMENT& placement : aModel.placements )
647 placementsBySheet[placement.sheet.id].push_back( &placement );
648
649 for( const MODEL_NET& net : aModel.nets )
650 {
651 netsById.emplace( net.id, &net );
652 netsBySheet[net.sheet.id].push_back( &net );
653
654 for( const MODEL_CONNECTION& connection : net.connections )
655 {
656 if( connection.vertices.size() < 2 )
657 continue;
658
659 endpointAdjacency[{ net.id.Value(), connection.vertices.front().x, connection.vertices.front().y }]
660 .push_back( connection.vertices[1] );
661 endpointAdjacency[{ net.id.Value(), connection.vertices.back().x, connection.vertices.back().y }]
662 .push_back( connection.vertices[connection.vertices.size() - 2] );
663 }
664 }
665
666 for( const MODEL_BUS& bus : aModel.buses )
667 busesBySheet[bus.sheet.id].push_back( &bus );
668
669 for( const MODEL_LABEL& label : aModel.labels )
670 labelsBySheet[label.sheet.id].push_back( &label );
671
672 for( const MODEL_JUNCTION& junction : aModel.junctions )
673 junctionsBySheet[junction.sheet.id].push_back( &junction );
674
675 for( const MODEL_TEXT& text : aModel.texts )
676 textsBySheet[text.sheet.id].push_back( &text );
677
678 for( const MODEL_PAGE_GRAPHIC& graphic : aModel.graphics )
679 graphicsBySheet[graphic.sheet.id].push_back( &graphic );
680
681 for( const MODEL_EMBEDDED_IMAGE& image : aModel.images )
682 imagesBySheet[image.sheet.id].push_back( &image );
683
684 for( const MODEL_SYMBOL_DEFINITION& definition : aModel.definitions )
685 {
686 definitionsById.emplace( definition.id, &definition );
687 std::map<PIN_ID, const MODEL_PIN_DEFINITION*>& definitionPins = pinsByDefinition[definition.id];
688
689 for( const MODEL_PIN_DEFINITION& pin : definition.pins )
690 definitionPins.emplace( pin.id, &pin );
691 }
692
693 for( const MODEL_PART_TYPE& partType : aModel.partTypes )
694 partTypesById.emplace( partType.id, &partType );
695 }
696
697 template <typename T>
698 static const std::vector<const T*>& ForSheet( const std::map<SHEET_ID, std::vector<const T*>>& aMap,
699 SHEET_ID aSheet )
700 {
701 static const std::vector<const T*> empty;
702 auto found = aMap.find( aSheet );
703 return found == aMap.end() ? empty : found->second;
704 }
705
706 std::map<SHEET_ID, std::vector<const MODEL_PLACEMENT*>> placementsBySheet;
707 std::map<SHEET_ID, std::vector<const MODEL_NET*>> netsBySheet;
708 std::map<SHEET_ID, std::vector<const MODEL_BUS*>> busesBySheet;
709 std::map<SHEET_ID, std::vector<const MODEL_LABEL*>> labelsBySheet;
710 std::map<SHEET_ID, std::vector<const MODEL_JUNCTION*>> junctionsBySheet;
711 std::map<SHEET_ID, std::vector<const MODEL_TEXT*>> textsBySheet;
712 std::map<SHEET_ID, std::vector<const MODEL_PAGE_GRAPHIC*>> graphicsBySheet;
713 std::map<SHEET_ID, std::vector<const MODEL_EMBEDDED_IMAGE*>> imagesBySheet;
714 std::map<NET_ID, const MODEL_NET*> netsById;
715 std::map<POINT_KEY, std::vector<SOURCE_POINT>> endpointAdjacency;
716
717 std::map<DEFINITION_ID, const MODEL_SYMBOL_DEFINITION*> definitionsById;
718 std::map<PART_TYPE_ID, const MODEL_PART_TYPE*> partTypesById;
719 std::map<DEFINITION_ID, std::map<PIN_ID, const MODEL_PIN_DEFINITION*>> pinsByDefinition;
720 };
721
722
723 const MODEL_SYMBOL_DEFINITION& definitionById( const MODEL_INDEX& aIndex, DEFINITION_ID aId )
724 {
725 auto definition = aIndex.definitionsById.find( aId );
726
727 if( definition == aIndex.definitionsById.end() )
728 THROW_IO_ERROR( wxS( "resolved definition is missing during schematic staging" ) );
729
730 return *definition->second;
731 }
732
733
734 const MODEL_PART_TYPE& partById( const MODEL_INDEX& aIndex, PART_TYPE_ID aId )
735 {
736 auto part = aIndex.partTypesById.find( aId );
737
738 if( part == aIndex.partTypesById.end() )
739 THROW_IO_ERROR( wxS( "resolved part type is missing during schematic staging" ) );
740
741 return *part->second;
742 }
743
744
745 const MODEL_PIN_DEFINITION& pinById( const MODEL_INDEX& aIndex, const MODEL_SYMBOL_DEFINITION& aDefinition,
746 PIN_ID aId )
747 {
748 auto definitionPins = aIndex.pinsByDefinition.find( aDefinition.id );
749
750 if( definitionPins == aIndex.pinsByDefinition.end() )
751 THROW_IO_ERROR( wxS( "resolved pin is missing during schematic staging" ) );
752
753 auto pin = definitionPins->second.find( aId );
754
755 if( pin == definitionPins->second.end() )
756 THROW_IO_ERROR( wxS( "resolved pin is missing during schematic staging" ) );
757
758 return *pin->second;
759 }
760
761
762 void addDefinitionUnit( const MODEL_INDEX& aIndex, LIB_SYMBOL* aLibrary,
763 const MODEL_SYMBOL_DEFINITION& aDefinition, const MODEL_GATE* aGate,
764 int aUnit, std::vector<PARSER_DIAGNOSTIC>& aDiagnostics,
765 const std::vector<PLACED_PIN_REFERENCE>* aPlacementPins = nullptr,
766 const MODEL_CONNECTOR_PIN* aConnectorPin = nullptr )
767 {
768 for( const MODEL_GRAPHIC& graphic : aDefinition.graphics )
769 {
770 if( graphic.kind == MODEL_GRAPHIC_KIND::TEXT )
771 {
772 std::unique_ptr<SCH_TEXT> text = makeGraphicText( graphic, aDiagnostics );
773 text->SetUnit( aUnit );
774 aLibrary->AddDrawItem( text.release() );
775 }
776 else
777 {
778 std::unique_ptr<SCH_SHAPE> shape = makeShape( graphic, false, 0 );
779 shape->SetUnit( aUnit );
780 aLibrary->AddDrawItem( shape.release() );
781 }
782 }
783
784 auto addPin = [&]( const PIN_REFERENCE& aPinReference, size_t aPinOrdinal )
785 {
786 std::unique_ptr<SCH_PIN> pin = makePin( pinById( aIndex, aDefinition, aPinReference.id ), aLibrary );
787
788 if( aGate && aPinOrdinal < aGate->logicalPins.size() )
789 {
790 const MODEL_GATE_PIN& logicalPin = aGate->logicalPins[aPinOrdinal];
791 pin->SetNumber( logicalPin.number.text );
792 pin->SetName( logicalPin.name.text );
793 pin->SetType( pinType( logicalPin.electricalType ) );
794 }
795
796 if( aConnectorPin )
797 {
798 pin->SetNumber( aConnectorPin->number.text );
799 pin->SetName( aConnectorPin->name.text );
800 pin->SetType( pinType( aConnectorPin->electricalType ) );
801 }
802
803 pin->SetUnit( aUnit );
804 aLibrary->AddDrawItem( pin.release() );
805 };
806
807 if( aPlacementPins && !aPlacementPins->empty() )
808 {
809 for( size_t pinOrdinal = 0; pinOrdinal < aPlacementPins->size(); ++pinOrdinal )
810 addPin( ( *aPlacementPins )[pinOrdinal], pinOrdinal );
811 }
812 else if( aGate && !aGate->pins.empty() )
813 {
814 for( size_t pinOrdinal = 0; pinOrdinal < aGate->pins.size(); ++pinOrdinal )
815 addPin( aGate->pins[pinOrdinal], pinOrdinal );
816 }
817 else
818 {
819 for( const MODEL_PIN_DEFINITION& sourcePin : aDefinition.pins )
820 {
821 std::unique_ptr<SCH_PIN> pin = makePin( sourcePin, aLibrary );
822
823 if( aConnectorPin )
824 {
825 pin->SetNumber( aConnectorPin->number.text );
826 pin->SetName( aConnectorPin->name.text );
827 pin->SetType( pinType( aConnectorPin->electricalType ) );
828 }
829
830 pin->SetUnit( aUnit );
831 aLibrary->AddDrawItem( pin.release() );
832 }
833 }
834 }
835
836
837 std::unique_ptr<LIB_SYMBOL> makeLibrarySymbol( const MODEL_INDEX& aIndex, const MODEL_PLACEMENT& aPlacement,
838 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics, wxString& aReference,
839 int& aUnit )
840 {
841 const MODEL_PART_TYPE& part = partById( aIndex, aPlacement.partType.id );
842 wxString libraryName = part.name.text;
843 auto library = std::make_unique<LIB_SYMBOL>( libraryName );
844 const MODEL_GATE* connectorGate = nullptr;
845
846 for( const MODEL_GATE& gate : part.gates )
847 {
848 if( !gate.connectorPins.empty() )
849 {
850 connectorGate = &gate;
851 break;
852 }
853 }
854
855 if( connectorGate )
856 {
857 const MODEL_SYMBOL_DEFINITION& definition = definitionById( aIndex, aPlacement.definition.id );
858 library->SetUnitCount( static_cast<int>( connectorGate->connectorPins.size() ), false );
859 library->LockUnits( true );
860 aReference = aPlacement.reference.text.BeforeLast( '-' );
861
862 if( aReference.IsEmpty() )
863 aReference = aPlacement.reference.text;
864
865 aUnit = static_cast<int>( aPlacement.unit );
866
867 for( size_t index = 0; index < connectorGate->connectorPins.size(); ++index )
868 {
869 const MODEL_CONNECTOR_PIN& connectorPin = connectorGate->connectorPins[index];
870 addDefinitionUnit( aIndex, library.get(), definition, connectorGate, static_cast<int>( index + 1 ),
871 aDiagnostics, &aPlacement.pins, &connectorPin );
872
873 if( aPlacement.reference.text.EndsWith( wxS( "-" ) + connectorPin.number.text ) )
874 aUnit = static_cast<int>( index + 1 );
875 }
876 }
877 else if( part.gates.size() > 1 )
878 {
879 library->SetUnitCount( static_cast<int>( part.gates.size() ), false );
880 library->LockUnits( true );
881 aReference = aPlacement.reference.text.BeforeLast( '-' );
882
883 if( aReference.IsEmpty() )
884 aReference = aPlacement.reference.text;
885
886 for( const MODEL_GATE& gate : part.gates )
887 {
888 const DEFINITION_ID definitionId =
889 gate.unit == aPlacement.unit ? aPlacement.definition.id : gate.definition.id;
890 const MODEL_SYMBOL_DEFINITION& definition = definitionById( aIndex, definitionId );
891 const std::vector<PLACED_PIN_REFERENCE>* placementPins =
892 gate.unit == aPlacement.unit ? &aPlacement.pins : nullptr;
893 addDefinitionUnit( aIndex, library.get(), definition, &gate, static_cast<int>( gate.unit ),
894 aDiagnostics, placementPins );
895 }
896 }
897 else
898 {
899 const MODEL_SYMBOL_DEFINITION& definition = definitionById( aIndex, aPlacement.definition.id );
900 const MODEL_GATE* gate = part.gates.empty() ? nullptr : &part.gates.front();
901 addDefinitionUnit( aIndex, library.get(), definition, gate, 0, aDiagnostics, &aPlacement.pins );
902 }
903
904 for( const MODEL_SIGNAL_PIN& signalPin : part.signalPins )
905 {
906 auto pin = std::make_unique<SCH_PIN>( library.get() );
907 pin->SetNumber( signalPin.number.text );
908 pin->SetName( signalPin.name.text );
910 pin->SetVisible( false );
911 pin->SetLength( 0 );
912 library->AddDrawItem( pin.release() );
913 }
914
915 library->SetShowPinNames( aPlacement.pinNamesVisible );
916 library->SetShowPinNumbers( aPlacement.pinNumbersVisible );
917 return library;
918 }
919
920
921 void applyField( SCH_SYMBOL* aSymbol, const MODEL_FIELD& aSource, std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
922 {
923 SCH_FIELD* field = nullptr;
924
925 if( aSource.name.text.CmpNoCase( wxS( "REF-DES" ) ) == 0 )
926 field = aSymbol->GetField( FIELD_T::REFERENCE );
927 else if( aSource.name.text.CmpNoCase( wxS( "PART-TYPE" ) ) == 0
928 || aSource.name.text.CmpNoCase( wxS( "VALUE" ) ) == 0 )
929 field = aSymbol->GetField( FIELD_T::VALUE );
930 else
931 {
932 field = aSymbol->GetField( aSource.name.text );
933
934 if( !field )
935 {
936 SCH_FIELD newField( aSymbol, FIELD_T::USER, aSource.name.text );
937 aSymbol->AddField( newField );
938 field = aSymbol->GetField( aSource.name.text );
939 }
940 }
941
942 if( !field )
943 THROW_IO_ERROR( FormatParserError( aSource.source, wxS( "could not stage symbol field" ) ) );
944
945 field->SetText( aSource.value.text );
946 field->SetPosition( aSymbol->GetPosition() + localPoint( aSource.position ) );
947 field->SetTextAngle( EDA_ANGLE( aSource.angle, TENTHS_OF_A_DEGREE_T ) );
948 applyTextPresentation( field, aSource.presentation, aSource.visible && aSource.presentation.visible,
949 aDiagnostics );
950 }
951
952
957 wxString resolveLibrarySymbolName( LIBRARY_SYMBOL_VARIANTS& aVariantsByName, LIB_SYMBOL* aSymbol )
958 {
960 auto& variants = aVariantsByName[aSymbol->GetName()];
961
962 for( const auto& [candidate, resolvedName] : variants )
963 {
964 if( candidate->Compare( *aSymbol, compareFlags ) == 0 )
965 return resolvedName;
966 }
967
968 wxString resolved = aSymbol->GetName();
969
970 if( !variants.empty() )
971 resolved = wxString::Format( wxS( "%s_%zu" ), resolved, variants.size() );
972
973 variants.emplace_back( std::make_unique<LIB_SYMBOL>( *aSymbol ), resolved );
974 return resolved;
975 }
976
977
978 std::unique_ptr<SCH_SYMBOL> makeSymbol( LIBRARY_SYMBOL_VARIANTS& aVariantsByName, const MODEL_INDEX& aIndex,
979 const MODEL_PLACEMENT& aPlacement, const SCH_SHEET_PATH& aPath,
980 int aPageHeight, std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
981 {
982 wxString reference = aPlacement.reference.text;
983 int unit = static_cast<int>( aPlacement.unit );
984 std::unique_ptr<LIB_SYMBOL> library = makeLibrarySymbol( aIndex, aPlacement, aDiagnostics, reference, unit );
985 const wxString libraryName = resolveLibrarySymbolName( aVariantsByName, library.get() );
986
987 library->SetName( libraryName );
988
989 auto symbol = std::make_unique<SCH_SYMBOL>();
990 LIB_ID libId;
991 libId.SetLibNickname( wxS( "pads_import" ) );
992 libId.SetLibItemName( libraryName );
993 symbol->SetLibId( libId );
994 symbol->SetExcludedFromBoard( library->GetPins().empty() );
995 symbol->SetLibSymbol( library.release() );
996 symbol->SetPosition( pagePoint( aPlacement.position, aPageHeight ) );
997
998 const int angle = NormalizeAngle( aPlacement.angle );
999 int orientation = SYM_ORIENT_0;
1000
1001 switch( angle )
1002 {
1003 case 900: orientation = SYM_ORIENT_90; break;
1004 case 1800: orientation = SYM_ORIENT_180; break;
1005 case 2700: orientation = SYM_ORIENT_270; break;
1006 default: break;
1007 }
1008
1009 if( aPlacement.mirrorFlags & 1 )
1010 orientation |= SYM_MIRROR_Y;
1011
1012 if( aPlacement.mirrorFlags & 2 )
1013 orientation |= SYM_MIRROR_X;
1014
1015 symbol->SetOrientation( orientation );
1016
1017 symbol->SetUnit( unit );
1018 symbol->SetRef( &aPath, reference );
1019 symbol->AddHierarchicalReference( aPath.Path(), reference, unit );
1020
1021 for( const MODEL_FIELD& field : aPlacement.fields )
1022 applyField( symbol.get(), field, aDiagnostics );
1023
1024 return symbol;
1025 }
1026
1027
1028 PAGE_INFO pageInfo( const MODEL_SHEET& aSheet )
1029 {
1030 PAGE_INFO page;
1031 page.SetWidthMils( static_cast<int>( aSheet.pageSize.x / 2 ) );
1032 page.SetHeightMils( static_cast<int>( aSheet.pageSize.y / 2 ) );
1033 return page;
1034 }
1035
1036
1037 void applyTitleBlock( SCH_SCREEN* aScreen, const MODEL_SHEET& aSheet )
1038 {
1039 TITLE_BLOCK title;
1040 title.SetTitle( aSheet.title.text );
1041
1042 static const std::map<wxString, int> commentFields = {
1043 { wxS( "designed" ), 0 }, { wxS( "des date" ), 1 }, { wxS( "drawn by" ), 2 },
1044 { wxS( "checked by" ), 3 }, { wxS( "checked date" ), 4 }, { wxS( "approved" ), 5 },
1045 { wxS( "app date" ), 6 }, { wxS( "drawing number" ), 7 }, { wxS( "scale" ), 8 }
1046 };
1047 std::array<bool, 9> reservedComments{};
1048 std::vector<const MODEL_FIELD*> fallbackComments;
1049 wxString companyName;
1050 wxString code;
1051
1052 for( const MODEL_FIELD& field : aSheet.titleBlockFields )
1053 {
1054 const wxString name = field.name.text.Lower();
1055
1056 if( name == wxS( "title" ) )
1057 title.SetTitle( field.value.text );
1058 else if( name == wxS( "revision" ) )
1059 title.SetRevision( field.value.text );
1060 else if( name == wxS( "date" ) || name == wxS( "drawn date" ) )
1061 title.SetDate( field.value.text );
1062 else if( name == wxS( "company name" ) )
1063 companyName = field.value.text;
1064 else if( name == wxS( "code" ) )
1065 code = field.value.text;
1066 else if( auto comment = commentFields.find( name ); comment != commentFields.end() )
1067 {
1068 reservedComments[comment->second] = true;
1069 title.SetComment( comment->second, field.value.text );
1070 }
1071 else
1072 {
1073 fallbackComments.push_back( &field );
1074 }
1075 }
1076
1077 title.SetCompany( companyName.IsEmpty() ? code : companyName );
1078
1079 auto fallback = fallbackComments.begin();
1080
1081 for( size_t comment = 0; comment < reservedComments.size() && fallback != fallbackComments.end(); ++comment )
1082 {
1083 if( !reservedComments[comment] )
1084 title.SetComment( comment, ( *fallback++ )->value.text );
1085 }
1086
1087 aScreen->SetTitleBlock( title );
1088 }
1089
1090
1091 std::string quoteWorksheetText( const wxString& aText )
1092 {
1093 std::string result;
1094
1095 for( char character : std::string( aText.utf8_str() ) )
1096 {
1097 if( character == '\\' || character == '"' )
1098 result.push_back( '\\' );
1099
1100 if( character == '\n' )
1101 result += "\\n";
1102 else if( character != '\r' )
1103 result.push_back( character );
1104 }
1105
1106 return result;
1107 }
1108
1109
1110 std::string worksheetVariable( const wxString& aText )
1111 {
1112 static const std::map<wxString, std::string> variables = {
1113 { wxS( "Title" ), "${TITLE}" }, { wxS( "Revision" ), "${REVISION}" },
1114 { wxS( "Drawn Date" ), "${ISSUE_DATE}" }, { wxS( "Code" ), "${COMPANY}" },
1115 { wxS( "Designed" ), "${COMMENT1}" }, { wxS( "Des Date" ), "${COMMENT2}" },
1116 { wxS( "Drawn By" ), "${COMMENT3}" }, { wxS( "Checked By" ), "${COMMENT4}" },
1117 { wxS( "Checked Date" ), "${COMMENT5}" }, { wxS( "Approved" ), "${COMMENT6}" },
1118 { wxS( "App Date" ), "${COMMENT7}" }, { wxS( "Drawing Number" ), "${COMMENT8}" },
1119 { wxS( "Scale" ), "${COMMENT9}" }, { wxS( "Sheet Number" ), "${#}" },
1120 { wxS( "Number of Sheets" ), "${##}" }, { wxS( "Sheet Name" ), "${SHEETNAME}" },
1121 { wxS( "Sheet Size" ), "${PAPER}" }
1122 };
1123 auto found = variables.find( aText );
1124 return found == variables.end() ? quoteWorksheetText( aText ) : found->second;
1125 }
1126
1127
1128 double worksheetX( const SOURCE_POINT& aPoint, const SOURCE_POINT& aPageSize )
1129 {
1130 return static_cast<double>( aPageSize.x - aPoint.x ) * 0.0127;
1131 }
1132
1133
1134 double worksheetY( const SOURCE_POINT& aPoint )
1135 {
1136 return static_cast<double>( aPoint.y ) * 0.0127;
1137 }
1138
1139
1140 void appendWorksheetLine( std::ostringstream& aOutput, const SOURCE_POINT& aStart, const SOURCE_POINT& aEnd,
1141 const SOURCE_POINT& aPageSize, int64_t aWidth )
1142 {
1143 aOutput << " (line (name \"\") (start " << worksheetX( aStart, aPageSize ) << ' ' << worksheetY( aStart )
1144 << ") (end " << worksheetX( aEnd, aPageSize ) << ' ' << worksheetY( aEnd ) << ')';
1145
1146 if( aWidth > 0 )
1147 aOutput << " (linewidth " << static_cast<double>( aWidth ) * 0.0127 << ')';
1148
1149 aOutput << ")\n";
1150 }
1151
1152
1153 std::string serializeWorksheet( const MODEL_WORKSHEET& aWorksheet, const MODEL_SHEET& aSheet )
1154 {
1155 std::ostringstream output;
1156 output.imbue( std::locale::classic() );
1157 output.precision( 8 );
1158 output << "(kicad_wks (version 20220228) (generator pads_import)\n"
1159 " (setup (textsize 1.27 1.27) (linewidth 0) (textlinewidth 0)"
1160 " (left_margin 0) (right_margin 0) (top_margin 0) (bottom_margin 0))\n";
1161
1162 for( const MODEL_GRAPHIC& graphic : aWorksheet.graphics )
1163 {
1164 if( graphic.kind == MODEL_GRAPHIC_KIND::TEXT )
1165 {
1166 if( graphic.points.empty() || !graphic.presentation.visible )
1167 continue;
1168
1169 output << " (tbtext \"" << worksheetVariable( graphic.text.text ) << "\" (name \"\") (pos "
1170 << worksheetX( graphic.points.front(), aSheet.pageSize ) << ' '
1171 << worksheetY( graphic.points.front() );
1172
1173 if( graphic.angle != 0 )
1174 output << ") (rotate " << static_cast<double>( graphic.angle ) / 10.0;
1175
1176 output << ") (font (size " << static_cast<double>( graphic.presentation.height ) * 0.0127 << ' '
1177 << static_cast<double>( graphic.presentation.height ) * 0.0127 << ')';
1178
1179 if( graphic.presentation.width > 0 )
1180 output << " (linewidth " << static_cast<double>( graphic.presentation.width ) * 0.0127 << ')';
1181
1182 if( graphic.presentation.bold )
1183 output << " bold";
1184
1185 if( graphic.presentation.italic )
1186 output << " italic";
1187
1188 output << ')';
1189
1192 {
1193 output << " (justify";
1194
1196 output << " center";
1198 output << " right";
1199
1201 output << " top";
1203 output << " bottom";
1204
1205 output << ')';
1206 }
1207
1208 output << ")\n";
1209 continue;
1210 }
1211
1212 if( graphic.kind == MODEL_GRAPHIC_KIND::CIRCLE && graphic.points.size() >= 2 )
1213 {
1214 const SOURCE_POINT& first = graphic.points[0];
1215 const SOURCE_POINT& second = graphic.points[1];
1216 const double centerX = ( static_cast<double>( first.x ) + second.x ) / 2.0;
1217 const double centerY = ( static_cast<double>( first.y ) + second.y ) / 2.0;
1218 const double radius = std::hypot( static_cast<double>( second.x - first.x ),
1219 static_cast<double>( second.y - first.y ) )
1220 / 2.0;
1221 constexpr int segments = 64;
1222
1223 for( int segment = 0; segment < segments; ++segment )
1224 {
1225 const double firstAngle = 2.0 * M_PI * segment / segments;
1226 const double secondAngle = 2.0 * M_PI * ( segment + 1 ) / segments;
1227 SOURCE_POINT start{ KiROUND( centerX + radius * std::cos( firstAngle ) ),
1228 KiROUND( centerY + radius * std::sin( firstAngle ) ), graphic.source };
1229 SOURCE_POINT end{ KiROUND( centerX + radius * std::cos( secondAngle ) ),
1230 KiROUND( centerY + radius * std::sin( secondAngle ) ), graphic.source };
1231 appendWorksheetLine( output, start, end, aSheet.pageSize, graphic.strokeWidth );
1232 }
1233
1234 continue;
1235 }
1236
1237 if( graphic.kind == MODEL_GRAPHIC_KIND::ARC )
1238 {
1239 const double startAngle =
1240 std::atan2( static_cast<double>( graphic.arcBoundsStart.y - graphic.arcCenter.y ),
1241 static_cast<double>( graphic.arcBoundsStart.x - graphic.arcCenter.x ) );
1242 const double sweep = static_cast<double>( graphic.arcSweepAngle ) * M_PI / 1800.0
1243 * ( graphic.arcClockwise ? -1.0 : 1.0 );
1244 const int segments = std::max( 1, static_cast<int>( std::ceil( std::abs( sweep ) * 16.0 / M_PI ) ) );
1245 const double radius =
1246 std::hypot( static_cast<double>( graphic.arcBoundsStart.x - graphic.arcCenter.x ),
1247 static_cast<double>( graphic.arcBoundsStart.y - graphic.arcCenter.y ) );
1248
1249 for( int segment = 0; segment < segments; ++segment )
1250 {
1251 const double angle1 = startAngle + sweep * segment / segments;
1252 const double angle2 = startAngle + sweep * ( segment + 1 ) / segments;
1253 SOURCE_POINT start{ KiROUND( graphic.arcCenter.x + radius * std::cos( angle1 ) ),
1254 KiROUND( graphic.arcCenter.y + radius * std::sin( angle1 ) ), graphic.source };
1255 SOURCE_POINT end{ KiROUND( graphic.arcCenter.x + radius * std::cos( angle2 ) ),
1256 KiROUND( graphic.arcCenter.y + radius * std::sin( angle2 ) ), graphic.source };
1257 appendWorksheetLine( output, start, end, aSheet.pageSize, graphic.strokeWidth );
1258 }
1259
1260 continue;
1261 }
1262
1263 if( graphic.kind == MODEL_GRAPHIC_KIND::RECTANGLE && graphic.points.size() >= 2 )
1264 {
1265 const SOURCE_POINT topLeft{ graphic.points[0].x, graphic.points[0].y, graphic.source };
1266 const SOURCE_POINT topRight{ graphic.points[1].x, graphic.points[0].y, graphic.source };
1267 const SOURCE_POINT bottomRight{ graphic.points[1].x, graphic.points[1].y, graphic.source };
1268 const SOURCE_POINT bottomLeft{ graphic.points[0].x, graphic.points[1].y, graphic.source };
1269 appendWorksheetLine( output, topLeft, topRight, aSheet.pageSize, graphic.strokeWidth );
1270 appendWorksheetLine( output, topRight, bottomRight, aSheet.pageSize, graphic.strokeWidth );
1271 appendWorksheetLine( output, bottomRight, bottomLeft, aSheet.pageSize, graphic.strokeWidth );
1272 appendWorksheetLine( output, bottomLeft, topLeft, aSheet.pageSize, graphic.strokeWidth );
1273 continue;
1274 }
1275
1276 for( size_t point = 1; point < graphic.points.size(); ++point )
1277 {
1278 appendWorksheetLine( output, graphic.points[point - 1], graphic.points[point], aSheet.pageSize,
1279 graphic.strokeWidth );
1280 }
1281 }
1282
1283 output << ")\n";
1284 return output.str();
1285 }
1286
1287
1288 wxString sanitizedFilename( const wxString& aName, size_t aOrdinal, std::set<wxString>& aUsed )
1289 {
1290 wxString stem = aName;
1291
1292 for( wxUniChar character : wxS( "<>:\"/\\|?*" ) )
1293 stem.Replace( wxString( character ), wxS( "_" ) );
1294
1295 stem.Trim( true ).Trim( false );
1296
1297 if( stem.IsEmpty() )
1298 stem = wxString::Format( wxS( "pads_sheet_%zu" ), aOrdinal + 1 );
1299
1300 wxString candidate = stem + wxS( "." ) + FILEEXT::KiCadSchematicFileExtension;
1301 size_t suffix = 2;
1302
1303 while( aUsed.contains( candidate.Lower() ) )
1304 candidate = wxString::Format( wxS( "%s_%zu.%s" ), stem, suffix++, FILEEXT::KiCadSchematicFileExtension );
1305
1306 aUsed.insert( candidate.Lower() );
1307 return candidate;
1308 }
1309
1310
1311 struct STAGED_SCHEMATIC
1312 {
1313 SCH_SHEET* destinationRoot = nullptr;
1314 std::unique_ptr<SCH_SCREEN> replacementScreen;
1315 std::unique_ptr<SCH_SCREEN> appendCache;
1316 EE_RTREE appendIndex;
1317 std::vector<std::unique_ptr<SCH_ITEM>> appendItems;
1318 std::unique_ptr<SCH_SCREEN> topLevelCache;
1319 EE_RTREE topLevelIndex;
1320 std::vector<std::unique_ptr<SCH_SHEET>> topLevelOwners;
1321 std::vector<SCH_SHEET*> topLevelSheets;
1322 SCH_SHEET_LIST hierarchy;
1323 std::optional<SCH_SHEET_PATH> replacementCurrentSheet;
1324 std::unique_ptr<CONNECTION_GRAPH> connectionGraph;
1325 std::unique_ptr<EMBEDDED_FILES> replacementEmbeddedFiles;
1326 wxString replacementDrawingSheet;
1328
1329 // Runs across every sheet because a per-sheet restart collides on sheet two
1330 int nextPowerOrdinal = 1;
1331
1332 LIBRARY_SYMBOL_VARIANTS librarySymbolVariants;
1333
1334 static void ValidateScreen( const SCH_SCREEN* aScreen )
1335 {
1336 if( !aScreen )
1337 THROW_IO_ERROR( wxS( "staged sheet is missing its screen" ) );
1338
1339 for( SCH_ITEM* item : aScreen->Items().OfType( SCH_SYMBOL_T ) )
1340 {
1341 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item );
1342
1343 if( !symbol->GetLibSymbolRef() || symbol->GetLibId().GetLibItemName().empty() )
1344 THROW_IO_ERROR( wxS( "staged symbol is missing its library link" ) );
1345 }
1346 }
1347
1348 static void ValidateChildSheet( const SCH_SHEET* aSheet, std::set<wxString>& aFilenames )
1349 {
1350 if( !aSheet || !aSheet->GetScreen() )
1351 THROW_IO_ERROR( wxS( "staged child sheet is missing its screen" ) );
1352
1353 ValidateScreen( aSheet->GetScreen() );
1354
1355 wxString filename = aSheet->GetField( FIELD_T::SHEET_FILENAME )->GetText();
1356
1357 if( filename.IsEmpty() || filename.Contains( wxS( "/" ) ) || filename.Contains( wxS( "\\" ) )
1358 || filename.Contains( wxS( ":" ) ) || filename.Contains( wxS( "*" ) ) )
1359 {
1360 THROW_IO_ERROR( wxS( "staged child sheet has an invalid filename" ) );
1361 }
1362
1363 if( !aFilenames.insert( filename.Lower() ).second )
1364 THROW_IO_ERROR( wxS( "staged child sheet filenames are not unique" ) );
1365 }
1366
1367 void Validate( bool aAppending ) const
1368 {
1369 if( replacementEmbeddedFiles
1370 && ( aAppending || replacementDrawingSheet.IsEmpty()
1371 || !replacementEmbeddedFiles->HasFile( wxS( "pads_import.kicad_wks" ) ) ) )
1372 {
1373 THROW_IO_ERROR( wxS( "embedded worksheet staging is incomplete" ) );
1374 }
1375
1376 if( !replacementEmbeddedFiles && !replacementDrawingSheet.IsEmpty() )
1377 THROW_IO_ERROR( wxS( "worksheet path has no staged embedded file" ) );
1378
1379 if( topLevelCache )
1380 {
1381 if( replacementScreen || appendCache || topLevelOwners.empty()
1382 || topLevelOwners.size() != topLevelSheets.size() || topLevelSheets.size() != result.counts.sheets )
1383 {
1384 THROW_IO_ERROR( wxS( "top-level sheet staging has invalid ownership" ) );
1385 }
1386
1387 for( const std::unique_ptr<SCH_SHEET>& sheet : topLevelOwners )
1388 {
1389 if( !sheet || !sheet->GetScreen() || !sheet->GetScreen()->Items().OfType( SCH_SHEET_T ).empty() )
1390 THROW_IO_ERROR( wxS( "staged top-level sheet has invalid content" ) );
1391
1392 ValidateScreen( sheet->GetScreen() );
1393 }
1394
1395 if( hierarchy.size() != topLevelSheets.size() || !replacementCurrentSheet || !connectionGraph )
1396 THROW_IO_ERROR( wxS( "top-level sheet staging has incomplete hierarchy state" ) );
1397
1398 return;
1399 }
1400
1401 if( aAppending && ( replacementScreen || !appendCache ) )
1402 THROW_IO_ERROR( wxS( "append staging has invalid screen ownership" ) );
1403
1404 if( !aAppending && ( !destinationRoot || !replacementScreen || !replacementCurrentSheet ) )
1405 THROW_IO_ERROR( wxS( "replacement staging has no destination root or screen" ) );
1406
1407 std::set<wxString> filenames;
1408
1409 if( replacementScreen )
1410 {
1411 ValidateScreen( replacementScreen.get() );
1412
1413 size_t childCount = 0;
1414
1415 for( SCH_ITEM* item : replacementScreen->Items().OfType( SCH_SHEET_T ) )
1416 {
1417 ValidateChildSheet( static_cast<SCH_SHEET*>( item ), filenames );
1418 ++childCount;
1419 }
1420
1421 if( result.counts.sheets > 1 && childCount != result.counts.sheets )
1422 THROW_IO_ERROR( wxS( "staged replacement hierarchy does not own every source sheet" ) );
1423
1424 if( result.counts.sheets == 1 && childCount != 0 )
1425 THROW_IO_ERROR( wxS( "single-sheet staging unexpectedly contains child sheets" ) );
1426 }
1427
1428 size_t appendedSheetCount = 0;
1429
1430 for( const std::unique_ptr<SCH_ITEM>& item : appendItems )
1431 {
1432 if( !item )
1433 THROW_IO_ERROR( wxS( "staged schematic contains an empty object" ) );
1434
1435 if( item->Type() == SCH_SHEET_T )
1436 {
1437 ValidateChildSheet( static_cast<const SCH_SHEET*>( item.get() ), filenames );
1438 ++appendedSheetCount;
1439 }
1440 else if( item->Type() == SCH_SYMBOL_T )
1441 {
1442 const SCH_SYMBOL* symbol = static_cast<const SCH_SYMBOL*>( item.get() );
1443
1444 if( !symbol->GetLibSymbolRef() || symbol->GetLibId().GetLibItemName().empty() )
1445 THROW_IO_ERROR( wxS( "staged symbol is missing its library link" ) );
1446 }
1447 }
1448
1449 if( aAppending && result.counts.sheets > 1 && appendedSheetCount != result.counts.sheets )
1450 THROW_IO_ERROR( wxS( "staged append hierarchy does not own every source sheet" ) );
1451
1452 if( hierarchy.empty() || !connectionGraph )
1453 THROW_IO_ERROR( wxS( "staged schematic has incomplete hierarchy state" ) );
1454 }
1455
1458 SCHEMATIC_CONTENT PackContent( SCH_SHEET* aAppendToMe )
1459 {
1460 SCHEMATIC_CONTENT content;
1461 content.hierarchy = std::move( hierarchy );
1462 content.currentSheet = std::move( replacementCurrentSheet );
1463 content.connectionGraph = std::move( connectionGraph );
1464
1465 if( replacementEmbeddedFiles )
1466 {
1467 content.embeddedFiles.emplace( std::move( *replacementEmbeddedFiles ) );
1468 content.drawingSheetFileName = std::move( replacementDrawingSheet );
1469 }
1470
1471 if( replacementScreen )
1472 {
1473 content.targetSheet = destinationRoot;
1474 content.screen = std::move( replacementScreen );
1475 }
1476 else if( topLevelCache )
1477 {
1478 // The virtual root owns the top-level sheets, so it is the target here.
1479 content.screenItems = std::move( topLevelIndex );
1480 content.screenLibSymbols = std::move( topLevelCache );
1481 content.topLevelSheets = std::move( topLevelSheets );
1482
1483 for( std::unique_ptr<SCH_SHEET>& sheet : topLevelOwners )
1484 content.itemOwners.emplace_back( std::move( sheet ) );
1485 }
1486 else
1487 {
1488 content.targetSheet = aAppendToMe;
1489 content.preserveNetChains = true;
1490 content.screenItems = std::move( appendIndex );
1491 content.screenLibSymbols = std::move( appendCache );
1492 content.itemOwners = std::move( appendItems );
1493 }
1494
1495 return content;
1496 }
1497
1498 void Commit( SCHEMATIC* aSchematic, SCH_SHEET* aAppendToMe, const std::function<void()>& aBeforeCommit )
1499 {
1500 SCHEMATIC_CONTENT content = PackContent( aAppendToMe );
1501
1502 if( aBeforeCommit )
1503 aBeforeCommit();
1504
1505 aSchematic->AdoptContent( std::move( content ) );
1506 }
1507 };
1508
1509
1510 void collectDispositionDiagnostics( const std::vector<SOURCE_PROPERTY>& aProperties,
1511 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
1512 {
1513 for( const SOURCE_PROPERTY& property : aProperties )
1514 {
1515 if( property.disposition == PROPERTY_DISPOSITION::EXACT
1516 || property.disposition == PROPERTY_DISPOSITION::PRESERVED )
1517 continue;
1518
1519 aDiagnostics.push_back( MakePropertyDiagnostic(
1520 RPT_SEVERITY_WARNING, property,
1521 wxString::Format( wxS( "PADS property '%s' retained with %s disposition" ), property.name.text,
1522 property.disposition == PROPERTY_DISPOSITION::APPROXIMATE
1523 ? wxS( "approximate" )
1524 : wxS( "unsupported" ) ) ) );
1525 }
1526 }
1527
1528
1529 void collectPresentationDiagnostics( const MODEL_TEXT_PRESENTATION& aPresentation,
1530 std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
1531 {
1532 collectDispositionDiagnostics( aPresentation.properties, aDiagnostics );
1533 }
1534
1535
1536 void validatePropertyDispositions( const PADS_SCH_MODEL& aModel, std::vector<PARSER_DIAGNOSTIC>& aDiagnostics )
1537 {
1538 collectDispositionDiagnostics( aModel.settings.properties, aDiagnostics );
1539
1540 for( const MODEL_SHEET& sheet : aModel.sheets )
1541 {
1542 collectDispositionDiagnostics( sheet.properties, aDiagnostics );
1543
1544 for( const MODEL_GRAPHIC& graphic : sheet.border )
1545 {
1546 collectDispositionDiagnostics( graphic.properties, aDiagnostics );
1547 collectPresentationDiagnostics( graphic.presentation, aDiagnostics );
1548 }
1549
1550 for( const MODEL_FIELD& field : sheet.titleBlockFields )
1551 {
1552 collectDispositionDiagnostics( field.properties, aDiagnostics );
1553 collectPresentationDiagnostics( field.presentation, aDiagnostics );
1554 }
1555 }
1556
1557 for( const MODEL_SYMBOL_DEFINITION& definition : aModel.definitions )
1558 {
1559 collectDispositionDiagnostics( definition.properties, aDiagnostics );
1560
1561 for( const MODEL_GRAPHIC& graphic : definition.graphics )
1562 {
1563 collectDispositionDiagnostics( graphic.properties, aDiagnostics );
1564 collectPresentationDiagnostics( graphic.presentation, aDiagnostics );
1565 }
1566
1567 for( const MODEL_PIN_DEFINITION& pin : definition.pins )
1568 {
1569 collectDispositionDiagnostics( pin.properties, aDiagnostics );
1570 collectPresentationDiagnostics( pin.presentation, aDiagnostics );
1571 collectPresentationDiagnostics( pin.namePresentation, aDiagnostics );
1572 collectPresentationDiagnostics( pin.numberPresentation, aDiagnostics );
1573 }
1574
1575 for( const MODEL_FIELD& field : definition.fields )
1576 {
1577 collectDispositionDiagnostics( field.properties, aDiagnostics );
1578 collectPresentationDiagnostics( field.presentation, aDiagnostics );
1579 }
1580 }
1581
1582 for( const MODEL_PART_TYPE& part : aModel.partTypes )
1583 {
1584 collectDispositionDiagnostics( part.properties, aDiagnostics );
1585
1586 for( const MODEL_GATE& gate : part.gates )
1587 collectDispositionDiagnostics( gate.properties, aDiagnostics );
1588
1589 for( const MODEL_FIELD& field : part.fields )
1590 {
1591 collectDispositionDiagnostics( field.properties, aDiagnostics );
1592 collectPresentationDiagnostics( field.presentation, aDiagnostics );
1593 }
1594 }
1595
1596 for( const MODEL_PLACEMENT& placement : aModel.placements )
1597 {
1598 collectDispositionDiagnostics( placement.properties, aDiagnostics );
1599
1600 for( const MODEL_FIELD& field : placement.fields )
1601 {
1602 collectDispositionDiagnostics( field.properties, aDiagnostics );
1603 collectPresentationDiagnostics( field.presentation, aDiagnostics );
1604 }
1605 }
1606
1607 for( const MODEL_NET& net : aModel.nets )
1608 {
1609 collectDispositionDiagnostics( net.properties, aDiagnostics );
1610
1611 for( const MODEL_CONNECTION& connection : net.connections )
1612 {
1613 collectDispositionDiagnostics( connection.properties, aDiagnostics );
1614
1615 for( const MODEL_CONNECTION_ENDPOINT& endpoint : connection.endpoints )
1616 collectDispositionDiagnostics( endpoint.properties, aDiagnostics );
1617 }
1618 }
1619
1620 for( const MODEL_BUS& bus : aModel.buses )
1621 {
1622 collectDispositionDiagnostics( bus.properties, aDiagnostics );
1623
1624 for( const MODEL_BUS_ENTRY& entry : bus.entries )
1625 collectDispositionDiagnostics( entry.properties, aDiagnostics );
1626 }
1627
1628 for( const MODEL_LABEL& label : aModel.labels )
1629 {
1630 collectDispositionDiagnostics( label.properties, aDiagnostics );
1631 collectPresentationDiagnostics( label.presentation, aDiagnostics );
1632 }
1633
1634 for( const MODEL_JUNCTION& junction : aModel.junctions )
1635 collectDispositionDiagnostics( junction.properties, aDiagnostics );
1636
1637 for( const MODEL_TEXT& text : aModel.texts )
1638 {
1639 collectDispositionDiagnostics( text.properties, aDiagnostics );
1640 collectPresentationDiagnostics( text.presentation, aDiagnostics );
1641 }
1642
1643 for( const MODEL_PAGE_GRAPHIC& graphic : aModel.graphics )
1644 {
1645 collectDispositionDiagnostics( graphic.graphic.properties, aDiagnostics );
1646 collectPresentationDiagnostics( graphic.graphic.presentation, aDiagnostics );
1647 }
1648
1649 for( const MODEL_WORKSHEET& worksheet : aModel.worksheets )
1650 {
1651 for( const MODEL_GRAPHIC& graphic : worksheet.graphics )
1652 {
1653 collectDispositionDiagnostics( graphic.properties, aDiagnostics );
1654 collectPresentationDiagnostics( graphic.presentation, aDiagnostics );
1655 }
1656 }
1657
1658 for( const MODEL_EMBEDDED_IMAGE& image : aModel.images )
1659 collectDispositionDiagnostics( image.properties, aDiagnostics );
1660
1661 for( const PRESERVED_CONTROLLER_PAYLOAD& payload : aModel.preservedControllerPayloads )
1662 {
1663 aDiagnostics.push_back( MakePropertyDiagnostic(
1664 RPT_SEVERITY_WARNING, payload.source, wxS( "controller_payload" ), payload.disposition,
1665 wxS( "PADS controller payload retained without schematic construction" ) ) );
1666 }
1667
1668 std::set<DIAGNOSTIC_PROPERTY_KEY> parserOwnedProperties;
1669
1670 for( const PARSER_DIAGNOSTIC& diagnostic : aModel.diagnostics )
1671 {
1672 if( std::optional key = DiagnosticPropertyKey( diagnostic ) )
1673 parserOwnedProperties.insert( std::move( *key ) );
1674 }
1675
1676 std::erase_if( aDiagnostics,
1677 [&]( const PARSER_DIAGNOSTIC& aBuilderDiagnostic )
1678 {
1679 std::optional key = DiagnosticPropertyKey( aBuilderDiagnostic );
1680 return key && parserOwnedProperties.contains( *key );
1681 } );
1682 }
1683
1684
1685 void stageSheetContent( STAGED_SCHEMATIC& aStaged, const MODEL_INDEX& aIndex,
1686 const MODEL_SHEET& aSourceSheet, SCH_SCREEN* aScreen, const SCH_SHEET_PATH& aPath )
1687 {
1688 PAGE_INFO page = pageInfo( aSourceSheet );
1689 aScreen->SetPageSettings( page );
1690 applyTitleBlock( aScreen, aSourceSheet );
1691 const int pageHeight = page.GetHeightIU( schIUScale.IU_PER_MILS );
1692
1693 for( const MODEL_PLACEMENT* placement : MODEL_INDEX::ForSheet( aIndex.placementsBySheet, aSourceSheet.id ) )
1694 {
1695 std::unique_ptr<SCH_SYMBOL> symbol =
1696 makeSymbol( aStaged.librarySymbolVariants, aIndex, *placement, aPath, pageHeight,
1697 aStaged.result.diagnostics );
1698 aScreen->Append( symbol.get() );
1699 symbol.release();
1700 ++aStaged.result.counts.symbols;
1701 }
1702
1703 for( const MODEL_GRAPHIC& graphic : aSourceSheet.border )
1704 {
1705 if( graphic.kind == MODEL_GRAPHIC_KIND::TEXT )
1706 {
1707 appendPageGraphic( aScreen, graphic, pageHeight, aStaged.result.diagnostics );
1708 ++aStaged.result.counts.texts;
1709 continue;
1710 }
1711
1712 aStaged.result.counts.graphics +=
1713 appendPageGraphic( aScreen, graphic, pageHeight, aStaged.result.diagnostics );
1714 }
1715
1716 using SEGMENT_KEY = std::tuple<uint32_t, uint32_t, int64_t, int64_t, int64_t, int64_t>;
1717 std::set<SEGMENT_KEY> busEntrySegments;
1718
1719 auto segmentKey = []( SHEET_ID aSheet, NET_ID aNet, const SOURCE_POINT& aStart, const SOURCE_POINT& aEnd )
1720 {
1721 if( std::tie( aStart.x, aStart.y ) <= std::tie( aEnd.x, aEnd.y ) )
1722 return SEGMENT_KEY( aSheet.Value(), aNet.Value(), aStart.x, aStart.y, aEnd.x, aEnd.y );
1723
1724 return SEGMENT_KEY( aSheet.Value(), aNet.Value(), aEnd.x, aEnd.y, aStart.x, aStart.y );
1725 };
1726
1727 for( const MODEL_BUS* bus : MODEL_INDEX::ForSheet( aIndex.busesBySheet, aSourceSheet.id ) )
1728 {
1729 if( bus->vertices.size() < 2 )
1730 THROW_IO_ERROR( FormatParserError( bus->source, wxS( "bus has inconsistent geometry" ) ) );
1731
1732 for( size_t vertex = 1; vertex < bus->vertices.size(); ++vertex )
1733 {
1734 auto line = std::make_unique<SCH_LINE>( pagePoint( bus->vertices[vertex - 1], pageHeight ), LAYER_BUS );
1735 line->SetEndPoint( pagePoint( bus->vertices[vertex], pageHeight ) );
1736 line->SetStroke( STROKE_PARAMS( toIU( aSourceSheet.defaultBusWidth ), LINE_STYLE::SOLID ) );
1737 aScreen->Append( line.get() );
1738 line.release();
1739 ++aStaged.result.counts.buses;
1740 }
1741
1742 wxString busLabel = bus->name.text;
1743
1744 if( !bus->declaredMembers.empty() )
1745 {
1746 busLabel += wxS( "{" );
1747
1748 for( size_t member = 0; member < bus->declaredMembers.size(); ++member )
1749 {
1750 if( member )
1751 busLabel += wxS( " " );
1752
1753 busLabel += bus->declaredMembers[member].text;
1754 }
1755
1756 busLabel += wxS( "}" );
1757 }
1758
1759 auto label = std::make_unique<SCH_LABEL>( pagePoint( bus->vertices.front(), pageHeight ), busLabel );
1760 aScreen->Append( label.get() );
1761 label.release();
1762 ++aStaged.result.counts.labels;
1763
1764 for( const MODEL_BUS_ENTRY& entry : bus->entries )
1765 {
1766 auto ownerNet = aIndex.netsById.find( entry.memberNet.id );
1767
1768 if( ownerNet == aIndex.netsById.end() )
1770 wxS( "resolved bus-entry net is missing during staging" ) ) );
1771
1772 auto adjacency = aIndex.endpointAdjacency.find(
1773 { entry.memberNet.id.Value(), entry.position.x, entry.position.y } );
1774
1775 if( adjacency == aIndex.endpointAdjacency.end() )
1776 THROW_IO_ERROR( FormatParserError( entry.source, wxS( "bus-entry geometry is unresolved" ) ) );
1777
1778 if( adjacency->second.size() != 1 )
1779 THROW_IO_ERROR( FormatParserError( entry.source, wxS( "bus-entry geometry is ambiguous" ) ) );
1780
1781 const VECTOR2I start = pagePoint( entry.position, pageHeight );
1782 const VECTOR2I end = pagePoint( adjacency->second.front(), pageHeight );
1783 const VECTOR2I delta = end - start;
1784 const int span = std::max( std::abs( delta.x ), std::abs( delta.y ) );
1785 const int entrySpan = std::min( span, schIUScale.MilsToIU( DEFAULT_SCH_ENTRY_SIZE ) );
1786 // A stub longer than a third of an inch overflows this product in 32 bits
1787 auto clamped = [&]( int aDelta )
1788 {
1789 return static_cast<int>( static_cast<int64_t>( aDelta ) * entrySpan / span );
1790 };
1791
1792 const VECTOR2I entryEnd = span == 0 ? end : start + VECTOR2I( clamped( delta.x ), clamped( delta.y ) );
1793 auto entryItem = std::make_unique<SCH_BUS_WIRE_ENTRY>( start );
1794 entryItem->SetSize( entryEnd - start );
1795 aScreen->Append( entryItem.get() );
1796 entryItem.release();
1797
1798 if( entryEnd != end )
1799 {
1800 auto wire = std::make_unique<SCH_LINE>( entryEnd, LAYER_WIRE );
1801 wire->SetEndPoint( end );
1802 wire->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
1803 aScreen->Append( wire.get() );
1804 wire.release();
1805 ++aStaged.result.counts.wires;
1806 }
1807
1808 busEntrySegments.insert(
1809 segmentKey( aSourceSheet.id, entry.memberNet.id, entry.position, adjacency->second.front() ) );
1810 ++aStaged.result.counts.busEntries;
1811 }
1812 }
1813
1814 for( const MODEL_NET* net : MODEL_INDEX::ForSheet( aIndex.netsBySheet, aSourceSheet.id ) )
1815 {
1816 for( const MODEL_CONNECTION& connection : net->connections )
1817 {
1818 if( connection.vertices.size() < 2 )
1820 FormatParserError( connection.source, wxS( "connection has inconsistent geometry" ) ) );
1821
1822 for( size_t vertex = 1; vertex < connection.vertices.size(); ++vertex )
1823 {
1824 if( busEntrySegments.contains( segmentKey( aSourceSheet.id, net->id,
1825 connection.vertices[vertex - 1],
1826 connection.vertices[vertex] ) ) )
1827 {
1828 continue;
1829 }
1830
1831 auto line = std::make_unique<SCH_LINE>( pagePoint( connection.vertices[vertex - 1], pageHeight ),
1832 LAYER_WIRE );
1833 line->SetEndPoint( pagePoint( connection.vertices[vertex], pageHeight ) );
1834 line->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
1835 aScreen->Append( line.get() );
1836 line.release();
1837 ++aStaged.result.counts.wires;
1838 }
1839
1840 SOURCE_POINT labelPoint = connection.vertices.front();
1841 auto pinEndpoint = std::ranges::find( connection.endpoints, MODEL_ENDPOINT_KIND::PIN,
1843
1844 if( pinEndpoint != connection.endpoints.end() )
1845 labelPoint = pinEndpoint->point;
1846 else if( connection.vertices.size() >= 2
1847 && busEntrySegments.contains( segmentKey( aSourceSheet.id, net->id, connection.vertices[0],
1848 connection.vertices[1] ) ) )
1849 labelPoint = connection.vertices[1];
1850
1851 const bool hasSourceLabel = std::ranges::any_of(
1852 MODEL_INDEX::ForSheet( aIndex.labelsBySheet, aSourceSheet.id ),
1853 [&]( const MODEL_LABEL* aLabel )
1854 {
1855 return ( aLabel->kind == MODEL_LABEL_KIND::GLOBAL || aLabel->kind == MODEL_LABEL_KIND::POWER
1856 || aLabel->kind == MODEL_LABEL_KIND::GROUND )
1857 && aLabel->text.text == net->name.text && aLabel->position.x == labelPoint.x
1858 && aLabel->position.y == labelPoint.y;
1859 } );
1860
1861 if( !hasSourceLabel )
1862 {
1863 auto label =
1864 std::make_unique<SCH_GLOBALLABEL>( pagePoint( labelPoint, pageHeight ), net->name.text );
1865 label->SetTextSize( VECTOR2I( 1, 1 ) );
1866 aScreen->Append( label.get() );
1867 label.release();
1868 }
1869 }
1870 }
1871
1872 for( const MODEL_JUNCTION* junction : MODEL_INDEX::ForSheet( aIndex.junctionsBySheet, aSourceSheet.id ) )
1873 {
1874 auto item = std::make_unique<SCH_JUNCTION>( pagePoint( junction->position, pageHeight ) );
1875 aScreen->Append( item.get() );
1876 item.release();
1877 ++aStaged.result.counts.junctions;
1878 }
1879
1880 for( const MODEL_LABEL* labelPointer : MODEL_INDEX::ForSheet( aIndex.labelsBySheet, aSourceSheet.id ) )
1881 {
1882 const MODEL_LABEL& label = *labelPointer;
1883
1884 std::unique_ptr<SCH_ITEM> item;
1885
1886 switch( label.kind )
1887 {
1890 item = std::make_unique<SCH_LABEL>( pagePoint( label.position, pageHeight ), label.text.text );
1891 break;
1892
1894 item = std::make_unique<SCH_GLOBALLABEL>( pagePoint( label.position, pageHeight ), label.text.text );
1895 break;
1896
1898 item = std::make_unique<SCH_HIERLABEL>( pagePoint( label.position, pageHeight ), label.text.text );
1899 break;
1900
1903 item = makePowerSymbol( label, aPath, pageHeight, aStaged.nextPowerOrdinal++,
1904 aStaged.result.diagnostics );
1905 ++aStaged.result.counts.symbols;
1906 break;
1907
1909 {
1910 auto property =
1911 std::ranges::find_if( label.properties,
1912 []( const SOURCE_PROPERTY& aProperty )
1913 {
1914 return aProperty.name.text == wxS( "unsupported_offpage_decal" );
1915 } );
1916
1917 if( property == label.properties.end() )
1918 {
1919 aStaged.result.diagnostics.push_back( MakePropertyDiagnostic(
1920 RPT_SEVERITY_WARNING, label.source, wxS( "unsupported_offpage_decal" ),
1922 wxS( "PADS unsupported label has no KiCad schematic representation" ) ) );
1923 }
1924
1925 continue;
1926 }
1927 }
1928
1929 if( auto* text = dynamic_cast<EDA_TEXT*>( item.get() ) )
1930 {
1931 text->SetTextAngle( EDA_ANGLE( label.angle, TENTHS_OF_A_DEGREE_T ) );
1932 applyTextPresentation( text, label.presentation, label.presentation.visible,
1933 aStaged.result.diagnostics );
1934 }
1935
1936 aScreen->Append( item.get() );
1937 item.release();
1938 ++aStaged.result.counts.labels;
1939 }
1940
1941 for( const MODEL_TEXT* sourceTextPointer : MODEL_INDEX::ForSheet( aIndex.textsBySheet, aSourceSheet.id ) )
1942 {
1943 const MODEL_TEXT& sourceText = *sourceTextPointer;
1944
1945 std::unique_ptr<SCH_TEXT> text =
1946 makePageText( sourceText.text, sourceText.position, sourceText.angle, sourceText.presentation,
1947 pageHeight, aStaged.result.diagnostics );
1948 aScreen->Append( text.get() );
1949 text.release();
1950 ++aStaged.result.counts.texts;
1951 }
1952
1953 for( const MODEL_PAGE_GRAPHIC* pageGraphicPointer :
1954 MODEL_INDEX::ForSheet( aIndex.graphicsBySheet, aSourceSheet.id ) )
1955 {
1956 const MODEL_PAGE_GRAPHIC& pageGraphic = *pageGraphicPointer;
1957
1958 if( pageGraphic.graphic.kind == MODEL_GRAPHIC_KIND::TEXT )
1959 {
1960 appendPageGraphic( aScreen, pageGraphic.graphic, pageHeight, aStaged.result.diagnostics );
1961 ++aStaged.result.counts.texts;
1962 }
1963 else
1964 {
1965 aStaged.result.counts.graphics +=
1966 appendPageGraphic( aScreen, pageGraphic.graphic, pageHeight, aStaged.result.diagnostics );
1967 }
1968 }
1969
1970 for( const MODEL_EMBEDDED_IMAGE* sourceImage : MODEL_INDEX::ForSheet( aIndex.imagesBySheet, aSourceSheet.id ) )
1971 {
1972 std::unique_ptr<SCH_BITMAP> bitmap =
1973 makeEmbeddedImage( *sourceImage, pageHeight, aStaged.result.diagnostics );
1974
1975 if( !bitmap )
1976 continue;
1977
1978 aScreen->Append( bitmap.get() );
1979 bitmap.release();
1980 ++aStaged.result.counts.images;
1981 }
1982 }
1983
1984} // namespace
1985
1986
1988 SCH_SHEET* aAppendToMe, const wxString& aSourcePath )
1989{
1990 if( !aSchematic )
1991 THROW_IO_ERROR( wxS( "cannot build a PADS schematic without a destination" ) );
1992
1993 if( aSourcePath.IsEmpty() )
1994 THROW_IO_ERROR( wxS( "cannot build a PADS schematic without a source filename" ) );
1995
1996 if( aModel.sheets.empty() )
1997 THROW_IO_ERROR( FormatParserError( aModel.source, wxS( "schematic model has no sheets" ) ) );
1998
1999 if( aAppendToMe && !aAppendToMe->GetScreen() )
2000 THROW_IO_ERROR( wxS( "cannot append a PADS schematic to a sheet without a screen" ) );
2001
2002 aModel.ValidateOrThrow();
2003 std::vector<const MODEL_SHEET*> sourceSheets;
2004 sourceSheets.reserve( aModel.sheets.size() );
2005
2006 for( const MODEL_SHEET& sheet : aModel.sheets )
2007 sourceSheets.push_back( &sheet );
2008
2009 std::ranges::sort( sourceSheets, {}, &MODEL_SHEET::index );
2010
2011 MODEL_INDEX modelIndex( aModel );
2012 STAGED_SCHEMATIC staged;
2013 staged.result.counts.sheets = aModel.sheets.size();
2014 staged.connectionGraph = std::make_unique<CONNECTION_GRAPH>( aSchematic );
2015
2016 if( aAppendToMe )
2017 staged.nextPowerOrdinal = PADS_SCH::PADS_SCH_SYMBOL_BUILDER::NextFreePowerOrdinal( aAppendToMe );
2018
2019 validatePropertyDispositions( aModel, staged.result.diagnostics );
2020
2021 if( !aAppendToMe && !aModel.worksheets.empty() )
2022 {
2023 auto worksheet = std::ranges::find_if( aModel.worksheets,
2024 []( const MODEL_WORKSHEET& aWorksheet )
2025 {
2026 return aWorksheet.name.text == wxS( "DRW5982" );
2027 } );
2028
2029 if( worksheet == aModel.worksheets.end() )
2030 worksheet = aModel.worksheets.begin();
2031
2032 auto sourceSheet = std::ranges::find( aModel.sheets, worksheet->sheet.id, &MODEL_SHEET::id );
2033
2034 if( sourceSheet == aModel.sheets.end() )
2035 THROW_IO_ERROR( FormatParserError( worksheet->source, wxS( "worksheet references an unknown sheet" ) ) );
2036
2037 const std::string serialized = serializeWorksheet( *worksheet, *sourceSheet );
2038 auto embeddedWorksheet = std::make_shared<EMBEDDED_FILES::EMBEDDED_FILE>();
2039 embeddedWorksheet->name = wxS( "pads_import.kicad_wks" );
2041 embeddedWorksheet->decompressedData.assign( serialized.begin(), serialized.end() );
2042
2044 THROW_IO_ERROR( FormatParserError( worksheet->source, wxS( "could not embed the PADS worksheet" ) ) );
2045
2046 embeddedWorksheet->is_valid = true;
2047 staged.replacementEmbeddedFiles = std::make_unique<EMBEDDED_FILES>();
2048 staged.replacementEmbeddedFiles->SetFileAddedCallback( aSchematic->GetEmbeddedFiles()->GetFileAddedCallback() );
2049 staged.replacementEmbeddedFiles->AddFile( embeddedWorksheet );
2050 staged.replacementDrawingSheet = embeddedWorksheet->GetLink();
2051 }
2052
2053 const bool multiSheet = aModel.sheets.size() > 1;
2054 std::set<wxString> usedFilenames;
2055 size_t firstChildPage = 2;
2056
2057 if( aAppendToMe )
2058 {
2059 for( const SCH_SHEET_PATH& path : aSchematic->BuildSheetListSortedByPageNumbers() )
2060 {
2061 unsigned long page = 0;
2062
2063 if( path.GetPageNumber().ToULong( &page ) )
2064 {
2065 if( page >= std::numeric_limits<size_t>::max() )
2066 THROW_IO_ERROR( wxS( "existing schematic page number leaves no append range" ) );
2067
2068 firstChildPage = std::max( firstChildPage, static_cast<size_t>( page ) + 1 );
2069 }
2070 }
2071
2072 if( multiSheet && sourceSheets.size() > std::numeric_limits<size_t>::max() - firstChildPage )
2073 THROW_IO_ERROR( wxS( "imported hierarchy page range overflows" ) );
2074
2075 staged.appendCache = std::make_unique<SCH_SCREEN>( aSchematic );
2076
2077 for( const auto& [name, symbol] : aAppendToMe->GetScreen()->GetLibSymbols() )
2078 {
2079 if( !symbol )
2080 THROW_IO_ERROR( wxString::Format( wxS( "destination library cache entry '%s' is null" ), name ) );
2081
2082 auto clone = std::make_unique<LIB_SYMBOL>( *symbol );
2083 staged.appendCache->AddLibSymbol( name, std::move( clone ) );
2084 }
2085
2086 for( SCH_ITEM* item : aAppendToMe->GetScreen()->Items().OfType( SCH_SHEET_T ) )
2087 {
2088 const SCH_SHEET* child = static_cast<const SCH_SHEET*>( item );
2089 usedFilenames.insert( child->GetField( FIELD_T::SHEET_FILENAME )->GetText().Lower() );
2090 }
2091 }
2092
2093 if( !aAppendToMe )
2094 {
2095 if( !multiSheet )
2096 {
2097 staged.destinationRoot = aSchematic->GetTopLevelSheet();
2098
2099 if( !staged.destinationRoot || !staged.destinationRoot->GetScreen() )
2100 THROW_IO_ERROR( wxS( "cannot replace a schematic without a top-level sheet and screen" ) );
2101
2102 staged.replacementScreen = std::make_unique<SCH_SCREEN>( aSchematic );
2103 staged.replacementScreen->SetFileName( aSourcePath );
2104 SCH_SHEET_PATH rootPath;
2105 rootPath.push_back( staged.destinationRoot );
2106 staged.hierarchy.push_back( rootPath );
2107 staged.replacementCurrentSheet.emplace( rootPath );
2108 stageSheetContent( staged, modelIndex, *sourceSheets.front(), staged.replacementScreen.get(), rootPath );
2109 }
2110 else
2111 {
2112 staged.topLevelCache = std::make_unique<SCH_SCREEN>( aSchematic );
2113
2114 for( size_t index = 0; index < sourceSheets.size(); ++index )
2115 {
2116 const MODEL_SHEET& sourceSheet = *sourceSheets[index];
2117 auto sheet = std::make_unique<SCH_SHEET>( aSchematic );
2118 auto screen = new SCH_SCREEN( aSchematic );
2119 sheet->SetScreen( screen );
2120 sheet->SyncUuidToScreen();
2121 sheet->SetParent( &aSchematic->Root() );
2122 sheet->GetField( FIELD_T::SHEET_NAME )->SetText( sourceSheet.name.text );
2123 wxString filename = sanitizedFilename( sourceSheet.name.text, index, usedFilenames );
2124 sheet->GetField( FIELD_T::SHEET_FILENAME )->SetText( filename );
2125 screen->SetFileName( filename );
2126 screen->SetPageNumber( wxString::Format( wxS( "%zu" ), index + 1 ) );
2128 path.push_back( sheet.get() );
2129 path.SetPageNumber( wxString::Format( wxS( "%zu" ), index + 1 ) );
2130 stageSheetContent( staged, modelIndex, sourceSheet, screen, path );
2131 staged.hierarchy.push_back( path );
2132
2133 if( index == 0 )
2134 staged.replacementCurrentSheet.emplace( path );
2135
2136 staged.topLevelIndex.insert( sheet.get() );
2137 staged.topLevelSheets.push_back( sheet.get() );
2138 staged.topLevelOwners.push_back( std::move( sheet ) );
2139 }
2140 }
2141 }
2142 else if( !multiSheet )
2143 {
2144 const MODEL_SHEET& sourceSheet = *sourceSheets.front();
2145 SCH_SCREEN* temporaryScreen = staged.appendCache.get();
2147 path.push_back( aAppendToMe );
2148 stageSheetContent( staged, modelIndex, sourceSheet, temporaryScreen, path );
2149
2150 std::vector<SCH_ITEM*> temporaryItems;
2151
2152 for( SCH_ITEM* item : temporaryScreen->Items() )
2153 temporaryItems.push_back( item );
2154
2155 for( SCH_ITEM* item : temporaryItems )
2156 {
2157 std::unique_ptr<SCH_ITEM> itemOwner( item );
2158
2159 // remove() falls back to a full-tree search, so a failure means the screen no longer
2160 // holds the item and itemOwner is the only thing left that can free it
2161 if( !temporaryScreen->Items().remove( item ) )
2162 THROW_IO_ERROR( wxS( "staged append item is missing from its spatial index" ) );
2163
2164 item->SetParent( aAppendToMe->GetScreen() );
2165 staged.appendItems.emplace_back( std::move( itemOwner ) );
2166 }
2167 }
2168 else
2169 {
2170 SCH_SHEET_PATH rootPath;
2171 rootPath.push_back( aAppendToMe );
2172
2173 for( size_t index = 0; index < sourceSheets.size(); ++index )
2174 {
2175 const MODEL_SHEET& sourceSheet = *sourceSheets[index];
2176 VECTOR2I position( schIUScale.MilsToIU( 500 + static_cast<int>( index % 4 ) * 2500 ),
2177 schIUScale.MilsToIU( 500 + static_cast<int>( index / 4 ) * 2000 ) );
2178 auto child = std::make_unique<SCH_SHEET>(
2179 aAppendToMe, position, VECTOR2I( schIUScale.MilsToIU( 2000 ), schIUScale.MilsToIU( 1500 ) ) );
2180 auto childScreen = new SCH_SCREEN( aSchematic );
2181 child->SetScreen( childScreen );
2182 child->GetField( FIELD_T::SHEET_NAME )->SetText( sourceSheet.name.text );
2183 wxString filename = sanitizedFilename( sourceSheet.name.text, index, usedFilenames );
2184 child->GetField( FIELD_T::SHEET_FILENAME )->SetText( filename );
2185 childScreen->SetFileName( filename );
2186 childScreen->SetPageNumber( wxString::Format( wxS( "%zu" ), firstChildPage + index ) );
2187 SCH_SHEET_PATH childPath( rootPath );
2188 childPath.push_back( child.get() );
2189 childPath.SetPageNumber( wxString::Format( wxS( "%zu" ), firstChildPage + index ) );
2190 stageSheetContent( staged, modelIndex, sourceSheet, childScreen, childPath );
2191 child->SetParent( aAppendToMe->GetScreen() );
2192 staged.appendItems.emplace_back( std::move( child ) );
2193 }
2194 }
2195
2196 if( aAppendToMe )
2197 {
2198 SCH_SHEET_LIST currentHierarchy = aSchematic->Hierarchy();
2199 staged.hierarchy.assign( currentHierarchy.begin(), currentHierarchy.end() );
2200
2201 for( SCH_ITEM* item : aAppendToMe->GetScreen()->Items() )
2202 staged.appendIndex.insert( item );
2203
2204 for( const std::unique_ptr<SCH_ITEM>& item : staged.appendItems )
2205 {
2206 staged.appendIndex.insert( item.get() );
2207
2208 if( item->Type() == SCH_SHEET_T )
2209 {
2211 path.push_back( aAppendToMe );
2212 path.push_back( static_cast<SCH_SHEET*>( item.get() ) );
2213 staged.hierarchy.push_back( path );
2214 }
2215 }
2216 }
2217
2218 staged.Validate( aAppendToMe != nullptr );
2219 BUILD_RESULT result = staged.result;
2220 staged.Commit( aSchematic, aAppendToMe, m_beforeCommit );
2221 return result;
2222}
2223
2224} // namespace PADS_SCH_BINARY
int index
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
wxImage * GetImageData()
Definition bitmap_base.h:64
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:495
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:373
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:342
void MigrateLegacyBoldStrokeWidth()
Migrate a pre-v11 bold stroke text so its stored thickness holds the base (non-bold) width.
Definition eda_text.cpp:327
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:245
void SetBold(bool aBold)
Set the text to be bold - this will also update the font if needed.
Definition eda_text.cpp:305
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:263
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:285
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:458
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:365
Implement an R-tree for fast spatial and type indexing of schematic items.
Definition sch_rtree.h:37
bool remove(SCH_ITEM *aItem)
Remove an item from the tree.
Definition sch_rtree.h:99
void insert(SCH_ITEM *aItem)
Insert an item into the tree.
Definition sch_rtree.h:70
EE_TYPE OfType(KICAD_T aType) const
Definition sch_rtree.h:248
FILE_ADDED_CALLBACK GetFileAddedCallback() const
static RETURN_CODE CompressAndEncode(EMBEDDED_FILE &aFile)
Take data from the #decompressedData buffer and compresses it using ZSTD into the #compressedEncodedD...
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
int SetLibItemName(const UTF8 &aLibItemName)
Override the library item name portion of the LIB_ID to aLibItemName.
Definition lib_id.cpp:124
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
Define a library symbol object.
Definition lib_symbol.h:119
wxString GetName() const override
Definition lib_symbol.h:181
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
static int NextFreePowerOrdinal(SCH_SHEET *aSheet)
Return the first 1-based ordinal that no "#PWRnnnn" reference under aSheet uses.
constexpr ValueType Value() const
BUILD_RESULT Build(const PADS_SCH_MODEL &aModel, SCHEMATIC *aSchematic, SCH_SHEET *aAppendToMe, const wxString &aSourcePath)
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition page_info.h:164
void SetHeightMils(double aHeightInMils)
void SetWidthMils(double aWidthInMils)
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.
void SetWidth(int aWidth)
const BITMAP_BASE & GetImage() const
Get the underlying image.
Holds all the data relating to one schematic.
Definition schematic.h:148
SCH_SHEET_LIST BuildSheetListSortedByPageNumbers() const
void AdoptContent(SCHEMATIC_CONTENT &&aContent) noexcept
Take a staged schematic over from an importer in one indivisible step.
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
SCH_SHEET * GetTopLevelSheet(int aIndex=0) const
EMBEDDED_FILES * GetEmbeddedFiles() override
SCH_SHEET & Root() const
Definition schematic.h:199
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:138
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:165
void SetTitleBlock(const TITLE_BLOCK &aTitleBlock)
Definition sch_screen.h:167
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void AddLibSymbol(LIB_SYMBOL *aLibSymbol)
Add aLibSymbol to the library symbol map.
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:141
const std::map< wxString, LIB_SYMBOL * > & GetLibSymbols() const
Fetch a list of unique LIB_SYMBOL object pointers required to properly render each SCH_SYMBOL in this...
Definition sch_screen.h:503
EE_RTREE & Items()
Get the full RTree, usually for iterating.
Definition sch_screen.h:118
void SetFileName(const wxString &aFileName)
Set the file name for this screen to aFileName.
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
KIID_PATH Path() const
Get the sheet path as an KIID_PATH.
void SetPageNumber(const wxString &aPageNumber)
Set the sheet instance user definable page number.
void push_back(SCH_SHEET *aSheet)
Forwarded method from std::vector.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this sheet.
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:145
Schematic symbol object.
Definition sch_symbol.h:75
VECTOR2I GetPosition() const override
Definition sch_symbol.h:934
const LIB_ID & GetLibId() const override
Definition sch_symbol.h:164
SCH_FIELD * AddField(const SCH_FIELD &aField)
Add a field to the symbol.
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:183
SCH_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this symbol.
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:38
void SetRevision(const wxString &aRevision)
Definition title_block.h:78
void SetComment(int aIdx, const wxString &aComment)
Definition title_block.h:98
void SetTitle(const wxString &aTitle)
Definition title_block.h:55
void SetCompany(const wxString &aCompany)
Definition title_block.h:88
void SetDate(const wxString &aDate)
Set the date field, and defaults to the current time and date.
Definition title_block.h:68
bool empty() const
Definition utf8.h:105
#define DEFAULT_SCH_ENTRY_SIZE
The default text size in mils. (can be changed in preference menu)
static bool empty(const wxTextEntryBase *aCtrl)
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
FILL_T
Definition eda_fill.h:29
@ NO_FILL
Definition eda_fill.h:30
@ HATCH
Definition eda_fill.h:34
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_fill.h:32
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
SHAPE_T
Definition eda_shape.h:54
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
static const std::string KiCadSchematicFileExtension
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
@ LAYER_DEVICE
Definition layer_ids.h:488
@ LAYER_WIRE
Definition layer_ids.h:474
@ LAYER_NOTES
Definition layer_ids.h:489
@ LAYER_BUS
Definition layer_ids.h:475
constexpr int NormalizeAngle(int aAngle)
CONTROLLER_ID< NET_ID_TAG > NET_ID
CONTROLLER_ID< DEFINITION_ID_TAG > DEFINITION_ID
CONTROLLER_ID< SHEET_ID_TAG > SHEET_ID
CONTROLLER_ID< PIN_ID_TAG > PIN_ID
CONTROLLER_ID< PART_TYPE_ID_TAG > PART_TYPE_ID
wxString FormatParserError(const SOURCE_PROVENANCE &aSource, const wxString &aMessage)
std::optional< DIAGNOSTIC_PROPERTY_KEY > DiagnosticPropertyKey(const PARSER_DIAGNOSTIC &aDiagnostic)
PARSER_DIAGNOSTIC MakePropertyDiagnostic(SEVERITY aSeverity, const SOURCE_PROPERTY &aProperty, const wxString &aMessage)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
bool OleRenderWmf(const std::vector< uint8_t > &aWmf, int aMaxWidth, int aMaxHeight, wxImage &aImage, double aTargetAspect)
bool OleMakeBmpFromDib(const std::vector< uint8_t > &aDib, wxMemoryBuffer &aOut)
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_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
Definition pin_type.h:41
@ 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
GRAPHIC_PINSHAPE
Definition pin_type.h:80
@ RPT_SEVERITY_WARNING
Definition of the SCH_SHEET_PATH and SCH_SHEET_LIST classes for Eeschema.
LINE_STYLE
Dashed line types.
std::vector< PARSER_DIAGNOSTIC > diagnostics
std::vector< SOURCE_PROPERTY > properties
SOURCE_PROVENANCE source
NET_REFERENCE memberNet
std::vector< SOURCE_PROPERTY > properties
SOURCE_POINT position
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_BUS_ENTRY > entries
std::vector< MODEL_CONNECTION_ENDPOINT > endpoints
std::vector< SOURCE_PROPERTY > properties
std::vector< SOURCE_POINT > vertices
MODEL_TEXT_PRESENTATION presentation
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_CONNECTOR_PIN > connectorPins
std::vector< SOURCE_PROPERTY > properties
std::vector< PIN_REFERENCE > pins
std::vector< MODEL_GATE_PIN > logicalPins
std::vector< SOURCE_PROPERTY > properties
std::vector< SOURCE_POINT > points
std::vector< SOURCE_PROPERTY > properties
MODEL_TEXT_PRESENTATION presentation
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_CONNECTION > connections
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_SIGNAL_PIN > signalPins
std::vector< SOURCE_PROPERTY > properties
std::vector< PLACED_PIN_REFERENCE > pins
std::vector< SOURCE_PROPERTY > properties
std::vector< SOURCE_PROPERTY > properties
std::vector< MODEL_FIELD > titleBlockFields
std::vector< MODEL_GRAPHIC > border
std::vector< MODEL_PIN_DEFINITION > pins
MODEL_TEXT_PRESENTATION presentation
std::vector< MODEL_GRAPHIC > graphics
std::vector< MODEL_SYMBOL_DEFINITION > definitions
std::vector< MODEL_JUNCTION > junctions
std::vector< MODEL_PAGE_GRAPHIC > graphics
std::vector< PRESERVED_CONTROLLER_PAYLOAD > preservedControllerPayloads
std::vector< MODEL_WORKSHEET > worksheets
std::vector< MODEL_LABEL > labels
std::vector< MODEL_EMBEDDED_IMAGE > images
std::vector< MODEL_PART_TYPE > partTypes
std::vector< MODEL_SHEET > sheets
std::vector< MODEL_PLACEMENT > placements
std::vector< PARSER_DIAGNOSTIC > diagnostics
A schematic staged off to the side by an importer, ready to be swapped into a live SCHEMATIC in one s...
Definition schematic.h:108
std::optional< EMBEDDED_FILES > embeddedFiles
Definition schematic.h:136
std::unique_ptr< SCH_SCREEN > screen
Screen that replaces the target sheet's screen outright.
Definition schematic.h:114
std::optional< SCH_SHEET_PATH > currentSheet
Definition schematic.h:130
std::vector< std::unique_ptr< SCH_ITEM > > itemOwners
Items in screenItems that nothing else owns yet.
Definition schematic.h:124
SCH_SHEET_LIST hierarchy
Definition schematic.h:129
std::unique_ptr< CONNECTION_GRAPH > connectionGraph
Definition schematic.h:131
bool preserveNetChains
Keep the schematic's net chains instead of the staged graph's, for append.
Definition schematic.h:134
std::vector< SCH_SHEET * > topLevelSheets
Replacement top level sheets. Empty leaves the schematic's own in place.
Definition schematic.h:127
SCH_SHEET * targetSheet
Sheet whose screen receives the content. Null names the schematic's virtual root.
Definition schematic.h:110
std::unique_ptr< SCH_SCREEN > screenLibSymbols
Screen holding only the library cache that the target screen adopts.
Definition schematic.h:121
EE_RTREE screenItems
Spatial index that replaces the target screen's own.
Definition schematic.h:118
wxString drawingSheetFileName
Definition schematic.h:137
@ 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.
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
std::string path
KIBIS_PIN * pin
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
int delta
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
@ SCH_SYMBOL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:171
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
Definition of file extensions used in Kicad.