KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_easyedapro_v3_parser.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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
27
28#include <core/map_helpers.h>
29
30#include <schematic.h>
31#include <sch_sheet.h>
32#include <sch_line.h>
33#include <sch_bitmap.h>
34#include <sch_no_connect.h>
35#include <sch_label.h>
36#include <sch_junction.h>
37#include <sch_screen.h>
38#include <sch_symbol.h>
39#include <sch_shape.h>
40#include <string_utils.h>
41#include <bezier_curves.h>
42#include <wx/base64.h>
43#include <wx/log.h>
44#include <wx/mstream.h>
45#include <gfx_import_utils.h>
49#include <trace_helpers.h>
50
51using namespace EASYEDAPRO;
52
53
54static LINE_STYLE ConvertStrokeStyle( const nlohmann::json& aStyle )
55{
56 int dash = 0;
57
58 if( aStyle.is_number_integer() )
59 dash = aStyle.get<int>();
60 else if( aStyle.is_string() )
61 {
62 wxString value = aStyle.get<wxString>();
63
64 if( value == wxS( "DASH" ) )
65 dash = 1;
66 else if( value == wxS( "DOT" ) )
67 dash = 2;
68 else if( value == wxS( "DASH_DOT" ) || value == wxS( "DASHDOT" ) )
69 dash = 3;
70 }
71
72 if( dash == 1 )
73 return LINE_STYLE::DASH;
74 else if( dash == 2 )
75 return LINE_STYLE::DOT;
76 else if( dash == 3 )
78
79 return LINE_STYLE::SOLID;
80}
81
82
83int EASYEDAPRO::AlignToFontV( const wxString& aAlign )
84{
85 if( aAlign.Contains( wxS( "TOP" ) ) )
86 return 0;
87
88 // Vertical centering is always spelled MIDDLE; CENTER is the horizontal token, so
89 // matching it here would mis-align e.g. CENTER_BOTTOM.
90 if( aAlign.Contains( wxS( "MIDDLE" ) ) )
91 return 1;
92
93 return 2; // BOTTOM
94}
95
96
97int EASYEDAPRO::AlignToFontH( const wxString& aAlign )
98{
99 if( aAlign.StartsWith( wxS( "LEFT" ) ) )
100 return 0;
101
102 if( aAlign.StartsWith( wxS( "CENTER" ) ) )
103 return 1;
104
105 return 2; // RIGHT
106}
107
108
109template <typename T>
111{
114}
115
116
117template <typename T>
123
124
125static constexpr double c_v3TextSizeScale = 0.62;
126
127
128template <typename T>
129static void ApplyV3TextSizeIfDefined( T& aText, const nlohmann::json& aInner )
130{
131 if( EASYEDAPRO::V3IsNullOrMissing( aInner, "fontSize" ) )
132 return;
133
134 double fontSize = EASYEDAPRO::V3GetDouble( aInner, "fontSize", 0.0 );
135
136 if( fontSize <= 0.0 )
137 return;
138
139 double scaledSize = fontSize * c_v3TextSizeScale;
140
141 aText->SetTextSize( VECTOR2I( SCH_EASYEDAPRO_PARSER::ScaleSize( scaledSize ),
142 SCH_EASYEDAPRO_PARSER::ScaleSize( scaledSize ) ) );
143}
144
145
146static wxString ResolveV3FootprintText( const wxString& aFootprintValue, const nlohmann::json& aProject,
147 const wxString& aLibName )
148{
149 if( aFootprintValue.empty() )
150 return wxEmptyString;
151
152 if( aFootprintValue.Contains( wxS( ":" ) ) )
153 return aFootprintValue;
154
155 std::string footprintUuid = std::string( aFootprintValue.ToUTF8() );
156
157 if( aProject.contains( "footprints" ) && aProject.at( "footprints" ).is_object()
158 && aProject.at( "footprints" ).contains( footprintUuid ) )
159 {
160 const nlohmann::json& footprint = aProject.at( "footprints" ).at( footprintUuid );
161 wxString footprintTitle;
162
163 if( footprint.is_object() )
164 footprintTitle = EASYEDAPRO::V3GetString( footprint, "title" );
165 else
166 footprintTitle = EASYEDAPRO::V3JsonToString( footprint );
167
168 if( !footprintTitle.empty() )
169 return aLibName + wxS( ":" ) + footprintTitle;
170 }
171
172 return aFootprintValue;
173}
174
175
177{
178 m_schematic = aSchematic;
179}
180
181
182template <typename T>
183void SCH_EASYEDAPRO_V3_PARSER::ApplyV3FontStyle( T& text, const nlohmann::json& aInner, const char* aAlignField )
184{
185 wxString color = V3GetString( aInner, "color", wxEmptyString );
186 wxString fontFamily = V3GetString( aInner, "fontFamily", wxS( "default" ) );
187 wxString align = V3GetString( aInner, aAlignField, wxS( "LEFT_BOTTOM" ) );
188
189 if( !color.empty() && color.StartsWith( wxS( "#" ) ) )
190 {
191 COLOR4D c( color );
192 text->SetTextColor( c );
193 }
194
195 if( fontFamily != wxS( "Arial" ) && !fontFamily.IsSameAs( wxS( "default" ), false ) )
196 text->SetFont( KIFONT::FONT::GetFont( fontFamily ) );
197
199
200 int vAlign = AlignToFontV( align );
201 int hAlign = AlignToFontH( align );
202
203 if( !text->GetText().Contains( wxS( "\n" ) ) )
204 {
205 if( vAlign == 0 )
206 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
207 else if( vAlign == 1 )
208 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
209 else
210 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
211 }
212 else
213 {
214 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
215 }
216
217 if( hAlign == 0 )
218 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
219 else if( hAlign == 1 )
220 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
221 else
222 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
223}
224
225
226template <typename T>
227void SCH_EASYEDAPRO_V3_PARSER::ApplyV3LineStyle( T& shape, const nlohmann::json& aInner )
228{
229 STROKE_PARAMS stroke = shape->GetStroke();
230
231 wxString colorStr = V3GetString( aInner, "strokeColor", wxEmptyString );
232
233 if( !colorStr.empty() && colorStr.StartsWith( wxS( "#" ) ) )
234 {
235 COLOR4D color( colorStr );
236 stroke.SetColor( color );
237 }
238
239 stroke.SetLineStyle( ConvertStrokeStyle( aInner.value( "strokeStyle", nlohmann::json() ) ) );
240
241 double width = V3GetDouble( aInner, "strokeWidth", 0.0 );
242
243 if( width != 0 )
245
246 shape->SetStroke( stroke );
247}
248
249
250static nlohmann::json FlattenPoints( const nlohmann::json& aPoints )
251{
252 nlohmann::json result = nlohmann::json::array();
253
254 if( !aPoints.is_array() )
255 return result;
256
257 for( const nlohmann::json& point : aPoints )
258 {
259 if( point.is_number() )
260 {
261 result.push_back( point );
262 continue;
263 }
264
265 if( point.is_array() && point.size() >= 2 )
266 {
267 result.push_back( point[0] );
268 result.push_back( point[1] );
269 continue;
270 }
271
272 if( point.is_object() )
273 {
274 if( point.contains( "x" ) && point.contains( "y" ) )
275 {
276 result.push_back( point.at( "x" ) );
277 result.push_back( point.at( "y" ) );
278 }
279 }
280 }
281
282 return result;
283}
284
285
288 const std::map<wxString, wxString>& aDeviceAttributes,
289 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap )
290{
291 EASYEDAPRO::SYM_INFO symInfo;
292
293 std::unique_ptr<LIB_SYMBOL> ksymbolPtr = std::make_unique<LIB_SYMBOL>( wxEmptyString );
294 LIB_SYMBOL* ksymbol = ksymbolPtr.get();
295
296 std::map<wxString, int> partUnits;
297
298 std::map<int, std::map<wxString, EASYEDAPRO::SCH_ATTR>> unitAttributes;
299 std::map<int, std::map<wxString, const V3_ROW*>> unitAttributeRows;
300 std::map<int, std::map<wxString, std::vector<const V3_ROW*>>> unitParentedRows;
301
302 int totalUnits = 0;
303
304 // First pass: collect PART units and HEAD
305 for( const V3_ROW& row : aDoc.rows )
306 {
307 if( row.type == wxS( "PART" ) )
308 {
309 int unitId = ++totalUnits;
310
311 partUnits[row.id] = unitId;
312
313 wxString partTitle = V3GetString( row.inner, "title" );
314
315 if( !partTitle.empty() )
316 partUnits[partTitle] = unitId;
317 }
318 else if( row.type == wxS( "META" ) )
319 {
320 if( row.inner.contains( "docType" ) )
321 symInfo.head.symbolType = static_cast<EASYEDAPRO::SYMBOL_TYPE>(
322 V3GetInt( row.inner, "docType",
323 static_cast<int>( EASYEDAPRO::SYMBOL_TYPE::NORMAL ) ) );
324 }
325 }
326
327 symInfo.partUnits = partUnits;
328 ksymbol->SetUnitCount( totalUnits, false );
329
330 int currentUnit = 1;
331
332 auto resolveRowUnit = [&]( const V3_ROW& aRow )
333 {
334 wxString partId = V3GetString( aRow.inner, "partId" );
335
336 if( !partId.empty() )
337 {
338 if( auto unitOpt = get_opt( partUnits, partId ) )
339 return *unitOpt;
340 }
341
342 return currentUnit;
343 };
344
345 // Second pass: create shapes and collect pins/attrs
346 for( const V3_ROW& row : aDoc.rows )
347 {
348 int rowUnit = resolveRowUnit( row );
349
350 if( row.type == wxS( "PART" ) )
351 {
352 currentUnit = partUnits.at( row.id );
353 }
354 else if( row.type == wxS( "RECT" ) )
355 {
356 VECTOR2D start( V3GetDouble( row.inner, "dotX1" ),
357 V3GetDouble( row.inner, "dotY1" ) );
358 VECTOR2D end( V3GetDouble( row.inner, "dotX2" ),
359 V3GetDouble( row.inner, "dotY2" ) );
360
361 auto rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
362
363 rect->SetStart( V3ScalePosSym( start ) );
364 rect->SetEnd( V3ScalePosSym( end ) );
365 rect->SetUnit( rowUnit );
366 ApplyV3LineStyle( rect, row.inner );
367
368 ksymbol->AddDrawItem( rect.release() );
369 }
370 else if( row.type == wxS( "CIRCLE" ) )
371 {
372 VECTOR2D center( V3GetDouble( row.inner, "centerX" ),
373 V3GetDouble( row.inner, "centerY" ) );
374 double radius = V3GetDouble( row.inner, "radius" );
375
376 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
377
378 circle->SetCenter( V3ScalePosSym( center ) );
379 circle->SetEnd( circle->GetCenter()
381 circle->SetUnit( rowUnit );
383
384 ksymbol->AddDrawItem( circle.release() );
385 }
386 else if( row.type == wxS( "ARC" ) )
387 {
388 VECTOR2D start( V3GetDouble( row.inner, "startX" ),
389 V3GetDouble( row.inner, "startY" ) );
390 VECTOR2D mid( V3GetDouble( row.inner, "referX" ),
391 V3GetDouble( row.inner, "referY" ) );
392 VECTOR2D end( V3GetDouble( row.inner, "endX" ),
393 V3GetDouble( row.inner, "endY" ) );
394
395 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
396
397 shape->SetArcOrLine( V3ScalePosSym( start ), V3ScalePosSym( mid ), V3ScalePosSym( end ) );
398 shape->SetUnit( rowUnit );
399 ApplyV3LineStyle( shape, row.inner );
400
401 ksymbol->AddDrawItem( shape.release() );
402 }
403 else if( row.type == wxS( "BEZIER" ) )
404 {
405 std::vector<double> points = row.inner.value( "controls", nlohmann::json::array() );
406
407 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
408
409 for( size_t i = 1; i < points.size(); i += 2 )
410 {
411 VECTOR2I pt = V3ScalePosSym( VECTOR2D( points[i - 1], points[i] ) );
412
413 switch( i )
414 {
415 case 1: shape->SetStart( pt ); break;
416 case 3: shape->SetBezierC1( pt ); break;
417 case 5: shape->SetBezierC2( pt ); break;
418 case 7: shape->SetEnd( pt ); break;
419 }
420 }
421
422 shape->SetUnit( rowUnit );
423 ApplyV3LineStyle( shape, row.inner );
424
425 ksymbol->AddDrawItem( shape.release() );
426 }
427 else if( row.type == wxS( "POLY" ) )
428 {
429 nlohmann::json flatPts = FlattenPoints(
430 row.inner.value( "points", nlohmann::json::array() ) );
431 std::vector<double> points = flatPts;
432
433 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
434
435 for( size_t i = 1; i < points.size(); i += 2 )
436 shape->AddPoint( V3ScalePosSym( VECTOR2D( points[i - 1], points[i] ) ) );
437
438 shape->SetUnit( rowUnit );
439 ApplyV3LineStyle( shape, row.inner );
440
441 ksymbol->AddDrawItem( shape.release() );
442 }
443 else if( row.type == wxS( "TEXT" ) )
444 {
445 VECTOR2D pos( V3GetDouble( row.inner, "x" ), V3GetDouble( row.inner, "y" ) );
446 double angle = V3GetDouble( row.inner, "rotation" );
447 wxString textStr = V3GetString( row.inner, "value" );
448
449 auto text = std::make_unique<SCH_TEXT>(
450 V3ScalePosSym( pos ), UnescapeHTML( textStr ),
451 LAYER_DEVICE );
452
453 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
454 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
455 text->SetTextAngleDegrees( angle );
456 text->SetUnit( rowUnit );
458
459 ksymbol->AddDrawItem( text.release() );
460 }
461 else if( row.type == wxS( "OBJ" ) )
462 {
463 VECTOR2D start( V3GetDouble( row.inner, "startX" ),
464 V3GetDouble( row.inner, "startY" ) );
465 VECTOR2D size( V3GetDouble( row.inner, "width" ),
466 V3GetDouble( row.inner, "height" ) );
467 int upsideDown = V3GetBool( row.inner, "isMirror" ) ? 1 : 0;
468
469 wxString imageUrl = V3GetString( row.inner, "content" );
470 wxString mimeType, data;
471
472 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
473 {
474 wxArrayString paramsArr =
475 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
476
477 data = imageUrl.AfterFirst( ',' );
478
479 if( paramsArr.size() > 0 )
480 mimeType = paramsArr[0];
481 }
482 else if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
483 {
484 wxString objectId = imageUrl.AfterLast( ':' );
485
486 if( auto blob = get_opt( aBlobMap, objectId ) )
487 {
488 wxString blobUrl = blob->url;
489
490 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
491 {
492 wxArrayString paramsArr =
493 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
494
495 data = blobUrl.AfterFirst( ',' );
496
497 if( paramsArr.size() > 0 )
498 mimeType = paramsArr[0];
499 }
500 }
501 }
502
503 if( mimeType.empty() || data.empty() )
504 continue;
505
506 wxMemoryBuffer buf = wxBase64Decode( data );
507
508 if( mimeType == wxS( "image/svg+xml" ) )
509 {
510 VECTOR2D offset = V3ScalePosSym( start );
511
512 SVG_IMPORT_PLUGIN svgImportPlugin;
513 GRAPHICS_IMPORTER_LIB_SYMBOL libsymImporter( ksymbol, 0 );
514
515 svgImportPlugin.SetImporter( &libsymImporter );
516 svgImportPlugin.LoadFromMemory( buf );
517
518 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
519 svgImportPlugin.GetImageHeight() );
520
521 VECTOR2D pixelScale(
523 / imSize.x,
525 / imSize.y );
526
527 if( upsideDown )
528 pixelScale.y *= -1;
529
530 libsymImporter.SetScale( pixelScale );
531
532 VECTOR2D offsetMM( schIUScale.IUTomm( offset.x ),
533 schIUScale.IUTomm( offset.y ) );
534
535 libsymImporter.SetImportOffsetMM( offsetMM );
536 svgImportPlugin.Import();
537
538 for( std::unique_ptr<EDA_ITEM>& item : libsymImporter.GetItems() )
539 ksymbol->AddDrawItem( static_cast<SCH_ITEM*>( item.release() ) );
540 }
541 else
542 {
543 wxMemoryInputStream memis( buf.GetData(), buf.GetDataLen() );
544
545 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
546 & ~wxImage::Load_Verbose );
547 wxImage img;
548
549 if( img.LoadFile( memis, mimeType ) )
550 {
551 int dimMul = img.GetWidth() * img.GetHeight();
552 double maxPixels = 30000;
553
554 if( dimMul > maxPixels )
555 {
556 double scale = sqrt( maxPixels / dimMul );
557 img.Rescale( img.GetWidth() * scale, img.GetHeight() * scale );
558 }
559
560 VECTOR2D pixelScale(
561 SCH_EASYEDAPRO_PARSER::ScaleSize( size.x ) / img.GetWidth(),
562 SCH_EASYEDAPRO_PARSER::ScaleSize( size.y ) / img.GetHeight() );
563
564 ConvertImageToLibShapes( ksymbol, 0, img, pixelScale,
565 V3ScalePosSym( start ) );
566 }
567 }
568 }
569 else if( row.type == wxS( "PIN" ) )
570 {
571 unitParentedRows[rowUnit][row.id].push_back( &row );
572 }
573 else if( row.type == wxS( "ATTR" ) )
574 {
575 wxString parentId = V3GetString( row.inner, "parentId" );
576
577 if( parentId.empty() )
578 {
580 attr.key = V3GetString( row.inner, "key" );
581 attr.value = V3JsonToString( row.inner.value( "value", nlohmann::json() ) );
582 attr.keyVisible = V3GetBool( row.inner, "keyVisible" );
583 attr.valVisible = V3GetBool( row.inner, "valueVisible" );
584
585 if( !V3IsNullOrMissing( row.inner, "x" ) )
586 {
587 attr.position = VECTOR2D( V3GetDouble( row.inner, "x" ),
588 V3GetDouble( row.inner, "y" ) );
589 }
590
591 attr.rotation = V3GetDouble( row.inner, "rotation" );
592 attr.fontStyle = wxEmptyString; // v3 has inline styles
593
594 unitAttributes[rowUnit].emplace( attr.key, attr );
595 unitAttributeRows[rowUnit].emplace( attr.key, &row );
596 }
597 else
598 {
599 unitParentedRows[rowUnit][parentId].push_back( &row );
600 }
601 }
602 }
603
604 // Apply symbol type attributes
607 {
608 ksymbol->SetGlobalPower();
609 ksymbol->GetReferenceField().SetText( wxS( "#PWR" ) );
610 ksymbol->GetReferenceField().SetVisible( false );
611 ksymbol->SetKeyWords( wxS( "power-flag" ) );
612 ksymbol->SetShowPinNames( false );
613 ksymbol->SetShowPinNumbers( false );
614
615 if( auto globalNetAttr = get_opt( unitAttributes[1], wxS( "Global Net Name" ) ) )
616 {
617 wxString powerName = globalNetAttr->value;
618
619 if( powerName.IsEmpty() )
620 {
621 if( auto nameAttr = get_opt( unitAttributes[1], wxS( "Name" ) ) )
622 powerName = nameAttr->value;
623 }
624
625 EDA_TEXT* valueText = static_cast<EDA_TEXT*>( &ksymbol->GetValueField() );
626 valueText->SetText( powerName );
627 valueText->SetVisible( globalNetAttr->valVisible );
628
629 if( auto globalNetAttrRow = get_opt( unitAttributeRows[1], wxS( "Global Net Name" ) ) )
630 {
631 const nlohmann::json& attrInner = ( *globalNetAttrRow )->inner;
632
633 wxString color = V3GetString( attrInner, "color", wxEmptyString );
634 wxString fontFamily = V3GetString( attrInner, "fontFamily", wxS( "default" ) );
635 wxString align = V3GetString( attrInner, "align", wxS( "LEFT_BOTTOM" ) );
636
637 if( !color.empty() && color.StartsWith( wxS( "#" ) ) )
638 {
639 COLOR4D c( color );
640 valueText->SetTextColor( c );
641 }
642
643 if( fontFamily != wxS( "Arial" ) && !fontFamily.IsSameAs( wxS( "default" ), false ) )
644 valueText->SetFont( KIFONT::FONT::GetFont( fontFamily ) );
645
646 ApplyV3TextSizeIfDefined( valueText, attrInner );
647
648 int vAlign = AlignToFontV( align );
649 int hAlign = AlignToFontH( align );
650
651 if( vAlign == 0 )
653 else if( vAlign == 1 )
655 else
657
658 if( hAlign == 0 )
660 else if( hAlign == 1 )
662 else
664 }
665
666 if( globalNetAttr->position )
667 ksymbol->GetValueField().SetPosition( V3ScalePosSym( *globalNetAttr->position ) );
668
669 wxString globalNetname = globalNetAttr->value;
670
671 if( globalNetname.IsEmpty() )
672 globalNetname = powerName;
673
674 if( !globalNetname.empty() )
675 {
676 ksymbol->SetDescription( wxString::Format(
677 _( "Power symbol creates a global label with name '%s'" ),
678 globalNetname ) );
679 }
680 }
681 }
682 else
683 {
684 auto designatorAttr = get_opt( unitAttributes[1], wxS( "Designator" ) );
685
686 if( designatorAttr && !designatorAttr->value.empty() )
687 {
688 wxString symbolPrefix = designatorAttr->value;
689
690 if( symbolPrefix.EndsWith( wxS( "?" ) ) )
691 symbolPrefix.RemoveLast();
692
693 ksymbol->GetReferenceField().SetText( symbolPrefix );
694 }
695
697 aDeviceAttributes, true,
698 [&]( const wxString& attrName, const wxString& value )
699 {
700 SCH_FIELD* fd = ksymbol->FindFieldCaseInsensitive( attrName );
701
702 if( !fd )
703 {
704 fd = new SCH_FIELD( ksymbol, FIELD_T::USER, attrName );
705 ksymbol->AddField( fd );
706 }
707
708 fd->SetText( value );
709 fd->SetVisible( false );
710 } );
711 }
712
713 // Process pins
714 for( auto& [unitId, parentedRows] : unitParentedRows )
715 {
716 for( auto& [pinId, rows] : parentedRows )
717 {
718 const V3_ROW* pinRow = nullptr;
719 std::map<wxString, EASYEDAPRO::SCH_ATTR> pinAttributes;
720
721 for( const V3_ROW* r : rows )
722 {
723 if( r->type == wxS( "ATTR" ) )
724 {
726 attr.key = V3GetString( r->inner, "key" );
727 attr.value = V3JsonToString( r->inner.value( "value", nlohmann::json() ) );
728 attr.keyVisible = V3GetBool( r->inner, "keyVisible" );
729 attr.valVisible = V3GetBool( r->inner, "valueVisible" );
730 pinAttributes.emplace( attr.key, attr );
731
732 }
733 else if( r->type == wxS( "PIN" ) )
734 {
735 pinRow = r;
736 }
737 }
738
739 if( !pinRow )
740 continue;
741
742 EASYEDAPRO::PIN_INFO pinInfo;
743 pinInfo.pin.position = VECTOR2D( V3GetDouble( pinRow->inner, "x" ),
744 V3GetDouble( pinRow->inner, "y" ) );
745 pinInfo.pin.length = V3GetDouble( pinRow->inner, "length" );
746 pinInfo.pin.rotation = V3GetDouble( pinRow->inner, "rotation" );
747
748 wxString pinShape = V3GetString( pinRow->inner, "pinShape" );
749 pinInfo.pin.inverted = ( pinShape == wxS( "INVERTED" )
750 || pinShape == wxS( "INVERTED_CLOCK" ) );
751
752 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( ksymbol );
753
754 pin->SetUnit( unitId );
755 pin->SetLength( SCH_EASYEDAPRO_PARSER::ScaleSize( pinInfo.pin.length ) );
756 pin->SetPosition( V3ScalePosSym( pinInfo.pin.position ) );
757
759
760 if( pinInfo.pin.rotation == 0 )
762 if( pinInfo.pin.rotation == 90 )
764 if( pinInfo.pin.rotation == 180 )
766 if( pinInfo.pin.rotation == 270 )
768
769 pin->SetOrientation( orient );
770
772 {
773 pin->SetName( ksymbol->GetName() );
774 }
775 else
776 {
777 auto pinNameAttr = get_opt( pinAttributes, wxS( "Pin Name" ) );
778
779 if( !pinNameAttr )
780 pinNameAttr = get_opt( pinAttributes, wxS( "NAME" ) );
781
782 if( pinNameAttr )
783 {
784 pin->SetName( pinNameAttr->value );
785 pinInfo.name = pinNameAttr->value;
786
787 if( !pinNameAttr->valVisible )
788 pin->SetNameTextSize( schIUScale.MilsToIU( 1 ) );
789 }
790 }
791
792 auto pinNumAttr = get_opt( pinAttributes, wxS( "Pin Number" ) );
793
794 if( !pinNumAttr )
795 pinNumAttr = get_opt( pinAttributes, wxS( "NUMBER" ) );
796
797 if( pinNumAttr )
798 {
799 pin->SetNumber( pinNumAttr->value );
800 pinInfo.number = pinNumAttr->value;
801
802 if( !pinNumAttr->valVisible )
803 pin->SetNumberTextSize( schIUScale.MilsToIU( 1 ) );
804 }
805
807 {
809 }
810 else if( auto pinTypeAttr = get_opt( pinAttributes, wxS( "Pin Type" ) ) )
811 {
812 if( pinTypeAttr->value == wxS( "IN" ) )
814 if( pinTypeAttr->value == wxS( "OUT" ) )
816 if( pinTypeAttr->value == wxS( "BI" ) )
818 }
819
820 if( get_opt( pinAttributes, wxS( "NO_CONNECT" ) ) )
821 pin->SetType( ELECTRICAL_PINTYPE::PT_NC );
822
823 if( pin->GetNumberTextSize() * int( pin->GetNumber().size() ) > pin->GetLength() )
824 pin->SetNumberTextSize( pin->GetLength() / pin->GetNumber().size() );
825
826 symInfo.pins.push_back( pinInfo );
827 ksymbol->AddDrawItem( pin.release() );
828 }
829 }
830
831 symInfo.symbolAttr = get_opt( unitAttributes[1], wxS( "Symbol" ) );
832 symInfo.libSymbol = std::move( ksymbolPtr );
833
834 return symInfo;
835}
836
837
839{
841 attr.key = V3GetString( aRow.inner, "key" );
842 attr.value = V3JsonToString( aRow.inner.value( "value", nlohmann::json() ) );
843 attr.keyVisible = V3GetBool( aRow.inner, "keyVisible" );
844 attr.valVisible = V3GetBool( aRow.inner, "valueVisible" );
845
846 if( !V3IsNullOrMissing( aRow.inner, "x" ) )
847 {
848 attr.position = VECTOR2D( V3GetDouble( aRow.inner, "x" ),
849 V3GetDouble( aRow.inner, "y" ) );
850 }
851
852 attr.rotation = V3GetDouble( aRow.inner, "rotation" );
853 attr.fontStyle = wxEmptyString;
854
855 return attr;
856}
857
858
859static void ApplyV3AttrToField( SCH_FIELD* aField, const EASYEDAPRO::SCH_ATTR& aAttr,
860 bool aIsSym, bool aToSym,
861 const std::map<wxString, wxString>& aDeviceAttributes,
862 SCH_SYMBOL* aParent, const nlohmann::json& aInner )
863{
864 EDA_TEXT* text = static_cast<EDA_TEXT*>( aField );
865
866 text->SetText( ResolveDeviceFieldVariables( aAttr.value, aDeviceAttributes ) );
867 text->SetVisible( aAttr.keyVisible || aAttr.valVisible );
868
869 aField->SetNameShown( aAttr.keyVisible );
870
871 if( aAttr.position )
872 {
873 aField->SetPosition( !aIsSym ? V3ScalePos( *aAttr.position )
874 : V3ScalePosSym( *aAttr.position ) );
875 }
876
877 // Apply inline font style from v3 JSON
878 wxString color = V3GetString( aInner, "color", wxEmptyString );
879 wxString fontFamily = V3GetString( aInner, "fontFamily", wxS( "default" ) );
880 wxString align = V3GetString( aInner, "align", wxS( "LEFT_BOTTOM" ) );
881
882 if( !color.empty() && color.StartsWith( wxS( "#" ) ) )
883 {
884 COLOR4D c( color );
885 text->SetTextColor( c );
886 }
887
888 if( fontFamily != wxS( "Arial" ) && !fontFamily.IsSameAs( wxS( "default" ), false ) )
889 text->SetFont( KIFONT::FONT::GetFont( fontFamily ) );
890
892
893 int vAlign = AlignToFontV( align );
894 int hAlign = AlignToFontH( align );
895
896 if( vAlign == 0 )
897 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
898 else if( vAlign == 1 )
899 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
900 else
901 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
902
903 if( hAlign == 0 )
904 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
905 else if( hAlign == 1 )
906 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
907 else
908 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
909
910 auto parent = aParent;
911 if( aToSym && parent && parent->Type() == SCH_SYMBOL_T )
912 {
913 int orient = static_cast<SCH_SYMBOL*>( parent )->GetOrientation();
914
915 if( orient == SYM_ORIENT_180 )
916 {
917 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
918 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
919 }
920 else if( orient == SYM_MIRROR_X + SYM_ORIENT_0 )
921 {
922 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
923 }
924 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_0 )
925 {
926 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
927 }
928 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_180 )
929 {
930 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( text->GetHorizJustify() ) );
931 }
932 else if( orient == SYM_ORIENT_90 )
933 {
934 text->SetTextAngle( ANGLE_VERTICAL );
935 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
936 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
937 }
938 if( orient == SYM_ORIENT_270 )
939 {
940 text->SetTextAngle( ANGLE_VERTICAL );
941 }
942 else if( orient == SYM_MIRROR_X + SYM_ORIENT_90 )
943 {
944 text->SetTextAngle( ANGLE_VERTICAL );
945 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
946 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
947 }
948 else if( orient == SYM_MIRROR_X + SYM_ORIENT_270 )
949 {
950 text->SetTextAngle( ANGLE_VERTICAL );
951 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
952 }
953 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_90 )
954 {
955 text->SetTextAngle( ANGLE_VERTICAL );
956 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
957 }
958 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_270 )
959 {
960 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( text->GetHorizJustify() ) );
961 }
962
963 if( aAttr.rotation == 90 )
964 {
965 if( text->GetTextAngle() == ANGLE_HORIZONTAL )
966 text->SetTextAngle( ANGLE_VERTICAL );
967 else
968 text->SetTextAngle( ANGLE_HORIZONTAL );
969
970 if( orient == SYM_ORIENT_90 )
971 {
972 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
973 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
974 }
975 if( orient == SYM_ORIENT_270 )
976 {
977 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
978 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
979 }
980 else if( orient == SYM_MIRROR_X + SYM_ORIENT_90 )
981 {
982 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
983 }
984 }
985 }
986}
987
988
990 SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet, const nlohmann::json& aProject,
991 std::map<wxString, EASYEDAPRO::SYM_INFO>& aSymbolMap,
992 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
993 const EASYEDAPRO::V3_DOC_RAW& aDoc, const wxString& aLibName )
994{
995 std::vector<std::unique_ptr<SCH_ITEM>> createdItems;
996
997 // v3: Collect WIRE and LINE rows by lineGroup for wire geometry
998 std::map<wxString, std::vector<const EASYEDAPRO::V3_ROW*>> wireGeometry;
999 std::map<wxString, const EASYEDAPRO::V3_ROW*> wireRows;
1000
1001 // Collect COMPONENT and ATTR rows by parent ID
1002 std::map<wxString, std::vector<const EASYEDAPRO::V3_ROW*>> parentedRows;
1003
1004 // First pass: collect wires, components, and attributes
1005 for( const EASYEDAPRO::V3_ROW& row : aDoc.rows )
1006 {
1007 if( row.type == wxS( "WIRE" ) )
1008 {
1009 wireRows[row.id] = &row;
1010 }
1011 else if( row.type == wxS( "LINE" ) && !EASYEDAPRO::V3IsNullOrMissing( row.inner, "lineGroup" ) )
1012 {
1013 wxString lineGroup = EASYEDAPRO::V3GetString( row.inner, "lineGroup" );
1014 wireGeometry[lineGroup].push_back( &row );
1015 }
1016 else if( row.type == wxS( "COMPONENT" ) )
1017 {
1018 parentedRows[row.id].push_back( &row );
1019 }
1020 else if( row.type == wxS( "ATTR" ) && !EASYEDAPRO::V3IsNullOrMissing( row.inner, "parentId" ) )
1021 {
1022 wxString parentId = EASYEDAPRO::V3GetString( row.inner, "parentId" );
1023 parentedRows[parentId].push_back( &row );
1024 }
1025 }
1026
1027 // Second pass: create shapes and other non-parented items
1028 for( const EASYEDAPRO::V3_ROW& row : aDoc.rows )
1029 {
1030 if( row.type == wxS( "RECT" ) )
1031 {
1032 double x1 = EASYEDAPRO::V3GetDouble( row.inner, "dotX1" );
1033 double y1 = EASYEDAPRO::V3GetDouble( row.inner, "dotY1" );
1034 double x2 = EASYEDAPRO::V3GetDouble( row.inner, "dotX2" );
1035 double y2 = EASYEDAPRO::V3GetDouble( row.inner, "dotY2" );
1036
1037 std::unique_ptr<SCH_SHAPE> rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
1038 rect->SetStart( V3ScalePos( VECTOR2D( x1, y1 ) ) );
1039 rect->SetEnd( V3ScalePos( VECTOR2D( x2, y2 ) ) );
1040
1041 SCH_SHAPE* rectPtr = rect.get();
1042 ApplyV3LineStyle( rectPtr, row.inner );
1043 createdItems.push_back( std::move( rect ) );
1044 }
1045 else if( row.type == wxS( "CIRCLE" ) )
1046 {
1047 double centerX = EASYEDAPRO::V3GetDouble( row.inner, "centerX" );
1048 double centerY = EASYEDAPRO::V3GetDouble( row.inner, "centerY" );
1049 double radius = EASYEDAPRO::V3GetDouble( row.inner, "radius" );
1050
1051 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
1052 circle->SetCenter( V3ScalePos( VECTOR2D( centerX, centerY ) ) );
1053 circle->SetEnd( circle->GetCenter()
1055
1056 SCH_SHAPE* circlePtr = circle.get();
1057 ApplyV3LineStyle( circlePtr, row.inner );
1058 createdItems.push_back( std::move( circle ) );
1059 }
1060 else if( row.type == wxS( "ARC" ) )
1061 {
1062 VECTOR2D start( EASYEDAPRO::V3GetDouble( row.inner, "startX" ),
1063 EASYEDAPRO::V3GetDouble( row.inner, "startY" ) );
1064 VECTOR2D mid( EASYEDAPRO::V3GetDouble( row.inner, "referX" ),
1065 EASYEDAPRO::V3GetDouble( row.inner, "referY" ) );
1066 VECTOR2D end( EASYEDAPRO::V3GetDouble( row.inner, "endX" ),
1067 EASYEDAPRO::V3GetDouble( row.inner, "endY" ) );
1068
1069 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
1070 arc->SetArcOrLine( V3ScalePos( start ), V3ScalePos( mid ), V3ScalePos( end ) );
1071
1072 SCH_SHAPE* arcPtr = arc.get();
1073 ApplyV3LineStyle( arcPtr, row.inner );
1074 createdItems.push_back( std::move( arc ) );
1075 }
1076 else if( row.type == wxS( "POLY" ) )
1077 {
1078 if( EASYEDAPRO::V3IsNullOrMissing( row.inner, "points" ) )
1079 continue;
1080
1081 nlohmann::json flatPts = FlattenPoints(
1082 row.inner.value( "points", nlohmann::json::array() ) );
1083 std::vector<double> pointsData = flatPts;
1084
1085 if( pointsData.size() < 4 )
1086 continue;
1087
1088 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY );
1089
1090 for( size_t i = 1; i < pointsData.size(); i += 2 )
1091 poly->AddPoint( V3ScalePos( VECTOR2D( pointsData[i - 1], pointsData[i] ) ) );
1092
1093 SCH_SHAPE* polyPtr = poly.get();
1094 ApplyV3LineStyle( polyPtr, row.inner );
1095 createdItems.push_back( std::move( poly ) );
1096 }
1097 else if( row.type == wxS( "TEXT" ) )
1098 {
1099 double x = EASYEDAPRO::V3GetDouble( row.inner, "x" );
1100 double y = EASYEDAPRO::V3GetDouble( row.inner, "y" );
1101 double angle = EASYEDAPRO::V3GetDouble( row.inner, "rotation", 0.0 );
1102 wxString text = EASYEDAPRO::V3GetString( row.inner, "value" );
1103
1104 std::unique_ptr<SCH_TEXT> schText = std::make_unique<SCH_TEXT>(
1105 V3ScalePos( VECTOR2D( x, y ) ),
1106 UnescapeHTML( text ) );
1107
1108 schText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1109 schText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
1110 schText->SetTextAngleDegrees( angle );
1111
1112 SCH_TEXT* textPtr = schText.get();
1113 ApplyV3FontStyle( textPtr, row.inner, "align" );
1114 createdItems.push_back( std::move( schText ) );
1115 }
1116 else if( row.type == wxS( "OBJ" ) )
1117 {
1118 if( EASYEDAPRO::V3IsNullOrMissing( row.inner, "content" ) )
1119 continue;
1120
1121 wxString content = EASYEDAPRO::V3GetString( row.inner, "content" );
1122
1123 double x = EASYEDAPRO::V3GetDouble( row.inner, "startX",
1124 EASYEDAPRO::V3GetDouble( row.inner, "x" ) );
1125 double y = EASYEDAPRO::V3GetDouble( row.inner, "startY",
1126 EASYEDAPRO::V3GetDouble( row.inner, "y" ) );
1127 double width = EASYEDAPRO::V3GetDouble( row.inner, "width" );
1128 double height = EASYEDAPRO::V3GetDouble( row.inner, "height" );
1129 double angle = EASYEDAPRO::V3GetDouble( row.inner, "rotation", 0.0 );
1130 bool flipped = EASYEDAPRO::V3GetBool( row.inner, "isMirror", false );
1131
1132 VECTOR2D start( x, y );
1133 VECTOR2D size( width, height );
1134 VECTOR2D kstart = V3ScalePos( start );
1136
1137 wxString mimeType;
1138 wxString base64Data;
1139
1140 if( content.BeforeFirst( ':' ) == wxS( "data" ) )
1141 {
1142 wxArrayString paramsArr =
1143 wxSplit( content.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1144
1145 if( paramsArr.size() > 0 )
1146 mimeType = paramsArr[0];
1147
1148 base64Data = content.AfterFirst( ',' );
1149 }
1150 else if( content.BeforeFirst( ':' ) == wxS( "blob" ) )
1151 {
1152 wxString objectId = content.AfterLast( ':' );
1153
1154 if( auto blob = get_opt( aBlobMap, objectId ) )
1155 {
1156 wxString blobUrl = blob->url;
1157
1158 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1159 {
1160 wxArrayString paramsArr =
1161 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1162
1163 if( paramsArr.size() > 0 )
1164 mimeType = paramsArr[0];
1165
1166 base64Data = blobUrl.AfterFirst( ',' );
1167 }
1168 }
1169 }
1170
1171 if( mimeType.empty() || base64Data.empty() )
1172 continue;
1173
1174 wxMemoryBuffer buf = wxBase64Decode( base64Data );
1175
1176 if( mimeType == wxS( "image/svg+xml" ) )
1177 {
1178 SVG_IMPORT_PLUGIN svgImportPlugin;
1179 GRAPHICS_IMPORTER_SCH schImporter;
1180
1181 svgImportPlugin.SetImporter( &schImporter );
1182 svgImportPlugin.LoadFromMemory( buf );
1183
1184 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
1185 svgImportPlugin.GetImageHeight() );
1186
1187 VECTOR2D pixelScale( schIUScale.IUTomm( SCH_EASYEDAPRO_PARSER::ScaleSize( size.x ) )
1188 / imSize.x,
1190 / imSize.y );
1191
1192 schImporter.SetScale( pixelScale );
1193
1194 VECTOR2D offsetMM( schIUScale.IUTomm( kstart.x ), schIUScale.IUTomm( kstart.y ) );
1195
1196 schImporter.SetImportOffsetMM( offsetMM );
1197
1198 svgImportPlugin.Import();
1199
1200 for( std::unique_ptr<EDA_ITEM>& item : schImporter.GetItems() )
1201 {
1202 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item.release() );
1203
1204 for( double i = angle; i > 0; i -= 90 )
1205 {
1206 if( schItem->Type() == SCH_LINE_T )
1207 {
1208 schItem->SetFlags( STARTPOINT );
1209 schItem->Rotate( kstart, false );
1210 schItem->ClearFlags( STARTPOINT );
1211
1212 schItem->SetFlags( ENDPOINT );
1213 schItem->Rotate( kstart, false );
1214 schItem->ClearFlags( ENDPOINT );
1215 }
1216 else
1217 {
1218 schItem->Rotate( kstart, false );
1219 }
1220 }
1221
1222 if( flipped )
1223 {
1224 if( schItem->Type() == SCH_LINE_T )
1225 schItem->SetFlags( STARTPOINT | ENDPOINT );
1226
1227 schItem->MirrorHorizontally( kstart.x );
1228
1229 if( schItem->Type() == SCH_LINE_T )
1230 schItem->ClearFlags( STARTPOINT | ENDPOINT );
1231 }
1232
1233 createdItems.emplace_back( schItem );
1234 }
1235 }
1236 else
1237 {
1238 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
1239 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1240
1241 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1242 & ~wxImage::Load_Verbose );
1243
1244 if( refImage.ReadImageFile( buf ) )
1245 {
1246 VECTOR2D kcenter = kstart + ksize / 2;
1247
1248 double scaleFactor = SCH_EASYEDAPRO_PARSER::ScaleSize( size.x )
1249 / refImage.GetSize().x;
1250 refImage.SetImageScale( scaleFactor );
1251 bitmap->SetPosition( kcenter );
1252
1253 for( double i = angle; i > 0; i -= 90 )
1254 bitmap->Rotate( kstart, false );
1255
1256 if( flipped )
1257 bitmap->MirrorHorizontally( kstart.x );
1258
1259 createdItems.push_back( std::move( bitmap ) );
1260 }
1261 }
1262 }
1263 }
1264
1265 // Third pass: process wires
1266 for( const auto& [wireId, wireRow] : wireRows )
1267 {
1268 auto geomIt = wireGeometry.find( wireId );
1269 if( geomIt == wireGeometry.end() )
1270 continue;
1271
1272 const std::vector<const EASYEDAPRO::V3_ROW*>& lineRows = geomIt->second;
1273
1274 bool firstPointSet = false;
1275 VECTOR2D firstPoint;
1276
1277 for( const EASYEDAPRO::V3_ROW* lineRow : lineRows )
1278 {
1279 if( EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "startX" )
1280 || EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "startY" )
1281 || EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "endX" )
1282 || EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "endY" ) )
1283 {
1284 continue;
1285 }
1286
1287 VECTOR2D start( EASYEDAPRO::V3GetDouble( lineRow->inner, "startX" ),
1288 EASYEDAPRO::V3GetDouble( lineRow->inner, "startY" ) );
1289 VECTOR2D end( EASYEDAPRO::V3GetDouble( lineRow->inner, "endX" ),
1290 EASYEDAPRO::V3GetDouble( lineRow->inner, "endY" ) );
1291
1292 if( !firstPointSet )
1293 {
1294 firstPoint = start;
1295 firstPointSet = true;
1296 }
1297
1298 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>(
1299 V3ScalePos( start ), LAYER_WIRE );
1300
1301 line->SetEndPoint( V3ScalePos( end ) );
1302 createdItems.push_back( std::move( line ) );
1303 }
1304
1305 wxString netName;
1306 bool netVisible = false;
1307 VECTOR2D netPos;
1308 bool netPosSet = false;
1309 wxString netAlign = wxS( "LEFT_BOTTOM" );
1310 int netRotation = 0;
1311
1312 if( auto attrRows = get_opt( parentedRows, wireId ) )
1313 {
1314 for( const EASYEDAPRO::V3_ROW* row : *attrRows )
1315 {
1316 if( row->type != wxS( "ATTR" ) )
1317 continue;
1318
1319 if( EASYEDAPRO::V3GetString( row->inner, "key" ) != wxS( "NET" ) )
1320 continue;
1321
1322 netName = EASYEDAPRO::V3JsonToString( row->inner.value( "value", nlohmann::json() ) );
1323 netVisible = EASYEDAPRO::V3GetBool( row->inner, "valueVisible" );
1324 netAlign = EASYEDAPRO::V3GetString( row->inner, "align", wxS( "LEFT_BOTTOM" ) );
1325 netRotation = EASYEDAPRO::V3GetInt( row->inner, "rotation", 0 );
1326
1327 if( !EASYEDAPRO::V3IsNullOrMissing( row->inner, "x" )
1328 && !EASYEDAPRO::V3IsNullOrMissing( row->inner, "y" ) )
1329 {
1330 netPos = VECTOR2D( EASYEDAPRO::V3GetDouble( row->inner, "x" ),
1331 EASYEDAPRO::V3GetDouble( row->inner, "y" ) );
1332 netPosSet = true;
1333 }
1334
1335 break;
1336 }
1337 }
1338
1339 if( netVisible && !netName.IsEmpty() && firstPointSet )
1340 {
1341 VECTOR2D labelPos = netPosSet ? netPos : firstPoint;
1342
1343 std::unique_ptr<SCH_LABEL> label = std::make_unique<SCH_LABEL>(
1344 V3ScalePos( labelPos ), netName );
1345
1346 SPIN_STYLE spinStyle = SPIN_STYLE::RIGHT;
1347
1348 if( netAlign.StartsWith( wxS( "RIGHT" ) ) )
1349 spinStyle = SPIN_STYLE::LEFT;
1350 else if( netAlign.StartsWith( wxS( "LEFT" ) ) )
1351 spinStyle = SPIN_STYLE::RIGHT;
1352
1353 int rot = netRotation % 360;
1354
1355 if( rot < 0 )
1356 rot += 360;
1357
1358 // EasyEDA uses CCW rotation, same as KiCad.
1359 if( rot % 90 == 0 )
1360 {
1361 for( int i = 0; i < rot; i += 90 )
1362 spinStyle = spinStyle.RotateCCW();
1363 }
1364
1365 label->SetSpinStyle( spinStyle );
1366
1367 createdItems.push_back( std::move( label ) );
1368 }
1369 }
1370
1371 // Fourth pass: process components with their attributes
1372 for( const auto& [parentId, rows] : parentedRows )
1373 {
1374 const EASYEDAPRO::V3_ROW* componentRow = nullptr;
1375 std::map<wxString, std::pair<EASYEDAPRO::SCH_ATTR, const EASYEDAPRO::V3_ROW*>> attributes;
1376
1377 for( const EASYEDAPRO::V3_ROW* row : rows )
1378 {
1379 if( row->type == wxS( "COMPONENT" ) )
1380 {
1381 componentRow = row;
1382 }
1383 else if( row->type == wxS( "ATTR" ) )
1384 {
1385 EASYEDAPRO::SCH_ATTR attr = V3RowToSchAttr( *row );
1386 attributes.emplace( attr.key, std::make_pair( attr, row ) );
1387 }
1388 }
1389
1390 if( !componentRow )
1391 continue;
1392
1393 auto deviceAttrIt = attributes.find( "Device" );
1394 auto symbolAttrIt = attributes.find( "Symbol" );
1395
1396 std::map<wxString, wxString> compAttrs;
1397
1398 if( deviceAttrIt != attributes.end() && !deviceAttrIt->second.first.value.IsEmpty() )
1399 {
1400 V3_DEVICE_DATA deviceData = GetV3DeviceData( aProject, deviceAttrIt->second.first.value );
1401
1402 if( deviceData.found )
1403 compAttrs = std::move( deviceData.attributes );
1404 }
1405
1406 wxString symbolId;
1407
1408 if( symbolAttrIt != attributes.end() && !symbolAttrIt->second.first.value.IsEmpty() )
1409 symbolId = symbolAttrIt->second.first.value;
1410 else
1411 symbolId = get_def( compAttrs, wxS( "Symbol" ), wxEmptyString );
1412
1413 if( symbolId.IsEmpty() )
1414 {
1415 wxLogTrace( traceEasyEdaIo, wxT( "EasyEDA Pro v3 component '%s': missing Symbol attribute, skipping." ),
1416 parentId );
1417 continue;
1418 }
1419
1420 auto it = aSymbolMap.find( symbolId );
1421 if( it == aSymbolMap.end() )
1422 {
1423 wxString compName = EASYEDAPRO::V3GetString( componentRow->inner, "name", wxS( "?" ) );
1424 wxLogTrace( traceEasyEdaIo, "Symbol of '%s' with uuid '%s' not found.", compName, symbolId );
1425 continue;
1426 }
1427
1428 EASYEDAPRO::SYM_INFO& esymInfo = it->second;
1429 LIB_SYMBOL newLibSymbol = *esymInfo.libSymbol.get();
1430
1431 wxString unitName = EASYEDAPRO::V3GetString( componentRow->inner, "partId" );
1432
1433 int unitId = 1;
1434 if( !unitName.IsEmpty() )
1435 {
1436 if( auto unitOpt = get_opt( esymInfo.partUnits, unitName ) )
1437 unitId = *unitOpt;
1438 else
1439 {
1440 wxLogTrace( traceEasyEdaIo,
1441 wxT( "EasyEDA Pro v3 component '%s': partId '%s' not found in "
1442 "symbol '%s', using unit 1." ),
1443 parentId, unitName, symbolId );
1444 }
1445 }
1446
1447 // Prefer the Device-backed project-lib name when the component links a Device.
1448 wxString libItemName = newLibSymbol.GetLibId().GetLibItemName();
1449
1450 if( deviceAttrIt != attributes.end() && !deviceAttrIt->second.first.value.IsEmpty() )
1451 {
1452 wxString deviceLibName = LookupV3DeviceLibName( aProject, deviceAttrIt->second.first.value );
1453
1454 if( !deviceLibName.empty() )
1455 libItemName = deviceLibName;
1456 }
1457
1458 LIB_ID libId = EASYEDAPRO::ToKiCadLibID( aLibName, libItemName );
1459
1460 auto schSym = std::make_unique<SCH_SYMBOL>( newLibSymbol, libId,
1461 &aSchematic->CurrentSheet(),
1462 unitId );
1463
1464 schSym->SetFootprintFieldText( newLibSymbol.GetFootprint() );
1465
1466 auto footprintAttrIt = attributes.find( wxS( "Footprint" ) );
1467
1468 wxString resolvedFootprint;
1469
1470 if( footprintAttrIt != attributes.end() && !footprintAttrIt->second.first.value.IsEmpty() )
1471 resolvedFootprint = ResolveV3FootprintText( footprintAttrIt->second.first.value,
1472 aProject, aLibName );
1473 else
1474 resolvedFootprint = ResolveV3FootprintText( get_def( compAttrs, wxS( "Footprint" ), wxEmptyString ),
1475 aProject, aLibName );
1476
1477 if( !resolvedFootprint.IsEmpty() )
1478 schSym->SetFootprintFieldText( resolvedFootprint );
1479
1480 double x = EASYEDAPRO::V3GetDouble( componentRow->inner, "x" );
1481 double y = EASYEDAPRO::V3GetDouble( componentRow->inner, "y" );
1482 double rotation = EASYEDAPRO::V3GetDouble( componentRow->inner, "rotation", 0.0 );
1483 bool mirror = EASYEDAPRO::V3GetBool( componentRow->inner, "isMirror", false );
1484
1485 for( double i = rotation; i > 0; i -= 90 )
1486 schSym->Rotate( VECTOR2I(), true );
1487
1488 if( mirror )
1489 schSym->MirrorHorizontally( 0 );
1490
1491 schSym->SetPosition( V3ScalePos( VECTOR2D( x, y ) ) );
1492
1494 {
1495 wxString netName;
1496
1497 auto nameAttrIt = attributes.find( wxS( "Name" ) );
1498
1499 if( nameAttrIt != attributes.end() && !nameAttrIt->second.first.value.IsEmpty() )
1500 netName = nameAttrIt->second.first.value;
1501 else
1502 netName = get_def( compAttrs, wxS( "Name" ), wxEmptyString );
1503
1504 if( netName.IsEmpty() )
1505 netName = newLibSymbol.GetValueField().GetText();
1506
1507 netName = ResolveDeviceFieldVariables( netName, compAttrs );
1508
1509 std::vector<SCH_PIN*> pins = schSym->GetPins( &aSchematic->CurrentSheet() );
1510 SCH_PIN* pin = pins.empty() ? nullptr : pins.front();
1511
1512 VECTOR2I labelPos = schSym->GetPosition();
1513 SPIN_STYLE spinStyle = SPIN_STYLE::RIGHT;
1515
1516 if( pin )
1517 {
1518 labelPos = pin->GetPosition();
1519
1520 switch( pin->PinDrawOrient( schSym->GetTransform() ) )
1521 {
1522 case PIN_ORIENTATION::PIN_LEFT: spinStyle = SPIN_STYLE::LEFT; break;
1523 case PIN_ORIENTATION::PIN_RIGHT: spinStyle = SPIN_STYLE::RIGHT; break;
1524 case PIN_ORIENTATION::PIN_UP: spinStyle = SPIN_STYLE::UP; break;
1525 case PIN_ORIENTATION::PIN_DOWN: spinStyle = SPIN_STYLE::BOTTOM; break;
1526 default: break;
1527 }
1528
1529 switch( pin->GetType() )
1530 {
1532 labelShape = LABEL_FLAG_SHAPE::L_INPUT;
1533 break;
1534
1536 labelShape = LABEL_FLAG_SHAPE::L_OUTPUT;
1537 break;
1538
1540 labelShape = LABEL_FLAG_SHAPE::L_BIDI;
1541 break;
1542
1544 labelShape = LABEL_FLAG_SHAPE::L_TRISTATE;
1545 break;
1546
1547 default:
1549 break;
1550 }
1551 }
1552
1553 std::unique_ptr<SCH_GLOBALLABEL> label =
1554 std::make_unique<SCH_GLOBALLABEL>( labelPos, netName );
1555 label->SetSpinStyle( spinStyle );
1556 label->SetShape( labelShape );
1557
1558 bool labelVisible = !netName.IsEmpty();
1559
1560 if( nameAttrIt != attributes.end() && nameAttrIt->second.second )
1561 {
1562 const EASYEDAPRO::V3_ROW* attrRow = nameAttrIt->second.second;
1563
1564 labelVisible =
1565 EASYEDAPRO::V3GetBool( attrRow->inner, "valueVisible", true )
1566 && !netName.IsEmpty();
1567
1568 wxString color = V3GetString( attrRow->inner, "color", wxEmptyString );
1569 wxString fontFamily =
1570 V3GetString( attrRow->inner, "fontFamily", wxS( "default" ) );
1571
1572 if( !color.empty() && color.StartsWith( wxS( "#" ) ) )
1573 label->SetTextColor( COLOR4D( color ) );
1574
1575 if( fontFamily != wxS( "Arial" )
1576 && !fontFamily.IsSameAs( wxS( "default" ), false ) )
1577 {
1578 label->SetFont( KIFONT::FONT::GetFont( fontFamily ) );
1579 }
1580
1581 ApplyV3TextSizeIfDefined( label, attrRow->inner );
1582 }
1583
1584 label->SetVisible( labelVisible );
1585
1586 createdItems.push_back( std::move( label ) );
1587 continue;
1588 }
1589
1591 {
1592 SCH_FIELD* valueField = schSym->GetField( FIELD_T::VALUE );
1593
1594 auto globalNetNameAttrIt = attributes.find( "Global Net Name" );
1595 auto nameAttrIt = attributes.find( "Name" );
1596 wxString globalNetNameFromProject =
1597 get_def( compAttrs, "Global Net Name", wxEmptyString );
1598 wxString nameFromProject = get_def( compAttrs, "Name", wxEmptyString );
1599
1600 const std::pair<EASYEDAPRO::SCH_ATTR, const EASYEDAPRO::V3_ROW*>* valueSource = nullptr;
1601
1602 if( globalNetNameAttrIt != attributes.end() )
1603 valueSource = &globalNetNameAttrIt->second;
1604 else if( nameAttrIt != attributes.end() )
1605 valueSource = &nameAttrIt->second;
1606
1607 wxString globalNetName;
1608
1609 // 1. Pick from schematic attr
1610 // 2. Pick Name from schematic attr (used by +5V)
1611 // 3. Pick from project.json
1612 // 4. Pick Name from project.json
1613 // 5. Pick from symbol
1614 if( globalNetNameAttrIt != attributes.end()
1615 && !globalNetNameAttrIt->second.first.value.IsEmpty() )
1616 {
1617 globalNetName = globalNetNameAttrIt->second.first.value;
1618 }
1619 else if( nameAttrIt != attributes.end()
1620 && !nameAttrIt->second.first.value.IsEmpty() )
1621 {
1622 globalNetName = nameAttrIt->second.first.value;
1623 }
1624 else if( !globalNetNameFromProject.IsEmpty() )
1625 {
1626 globalNetName = globalNetNameFromProject;
1627 }
1628 else if( !nameFromProject.IsEmpty() )
1629 {
1630 globalNetName = nameFromProject;
1631 }
1632 else
1633 {
1634 globalNetName = newLibSymbol.GetValueField().GetText();
1635 }
1636
1637 globalNetName = ResolveDeviceFieldVariables( globalNetName, compAttrs );
1638
1639 if( valueSource && valueSource->second )
1640 {
1641 EASYEDAPRO::SCH_ATTR attr = valueSource->first;
1642 const EASYEDAPRO::V3_ROW* attrRow = valueSource->second;
1643 bool valueVisibleMissing = EASYEDAPRO::V3IsNullOrMissing( attrRow->inner,
1644 "valueVisible" );
1645 bool valueFromNameAttr = ( nameAttrIt != attributes.end()
1646 && valueSource == &nameAttrIt->second );
1647
1648 int attrRotation = EASYEDAPRO::V3GetInt( attrRow->inner, "rotation", 0 ) % 360;
1649
1650 if( attrRotation < 0 )
1651 attrRotation += 360;
1652
1653 bool hasExplicitStyleOrPosition =
1654 !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "x" )
1655 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "y" )
1656 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "align" )
1657 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "fontSize" )
1658 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "fontFamily" )
1659 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "color" )
1660 || attrRotation != 0;
1661
1662 attr.value = globalNetName;
1663
1664 if( !globalNetName.IsEmpty() )
1665 attr.valVisible = true;
1666 else if( valueVisibleMissing )
1667 attr.valVisible = false;
1668
1669 if( valueFromNameAttr && !hasExplicitStyleOrPosition )
1670 {
1671 valueField->SetText( ResolveDeviceFieldVariables( globalNetName, compAttrs ) );
1672
1673 if( !globalNetName.IsEmpty() )
1674 valueField->SetVisible( true );
1675 else if( valueVisibleMissing )
1676 valueField->SetVisible( false );
1677 }
1678 else
1679 {
1680 ApplyV3AttrToField( schSym->GetField( FIELD_T::VALUE ), attr, false, true,
1681 compAttrs, schSym.get(), attrRow->inner );
1682
1683 if( !globalNetName.IsEmpty() )
1684 valueField->SetVisible( true );
1685 }
1686 }
1687 else
1688 {
1689 valueField->SetText( ResolveDeviceFieldVariables( globalNetName, compAttrs ) );
1690 valueField->SetVisible( !globalNetName.IsEmpty() );
1691 }
1692
1693 for( SCH_PIN* pin : schSym->GetAllLibPins() )
1694 pin->SetName( globalNetName );
1695
1696 schSym->SetRef( &aSchematic->CurrentSheet(), wxS( "#PWR?" ) );
1697 schSym->GetField( FIELD_T::REFERENCE )->SetVisible( false );
1698 }
1699 else
1700 {
1701 // Regular component
1702 auto designatorAttrIt = attributes.find( "Designator" );
1703 SCH_FIELD* refField = schSym->GetField( FIELD_T::REFERENCE );
1704 bool hasReferenceValue = false;
1705
1706 if( designatorAttrIt != attributes.end() )
1707 {
1708 const EASYEDAPRO::SCH_ATTR& attr = designatorAttrIt->second.first;
1709 const EASYEDAPRO::V3_ROW* attrRow = designatorAttrIt->second.second;
1710
1711 ApplyV3AttrToField( refField, attr, false, true, compAttrs,
1712 schSym.get(), attrRow->inner );
1713
1714 if( !attr.value.IsEmpty() )
1715 {
1716 schSym->SetRef( &aSchematic->CurrentSheet(), attr.value );
1717 hasReferenceValue = true;
1718 }
1719 }
1720
1721 if( !hasReferenceValue )
1722 refField->SetVisible( false );
1723
1724 // Device BOM / identity fields live on the Device, not as schematic ATTR rows.
1725 // Apply them per-instance so shared symbol geometry stays device-agnostic.
1727 compAttrs, true,
1728 [&]( const wxString& attrKey, const wxString& value )
1729 {
1730 SCH_FIELD* field = schSym->FindFieldCaseInsensitive( attrKey );
1731
1732 if( !field )
1733 field = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, attrKey ) );
1734
1735 field->SetText( value );
1736 field->SetVisible( false );
1737 } );
1738
1739 wxString description = get_def( compAttrs, wxS( "Description" ), wxEmptyString );
1740
1741 if( !description.empty() )
1742 {
1743 schSym->GetField( FIELD_T::DESCRIPTION )
1744 ->SetText( NormalizeEasyEDAText( ResolveDeviceFieldVariables( description, compAttrs ) ) );
1745 }
1746
1747 auto valueAttrIt = attributes.find( "Value" );
1748 auto nameAttrIt = attributes.find( "Name" );
1749
1750 wxString fallbackValue = get_def( compAttrs, wxS( "Value" ), wxEmptyString );
1751
1752 if( fallbackValue.IsEmpty() )
1753 fallbackValue = get_def( compAttrs, wxS( "Name" ), wxEmptyString );
1754
1755 const std::pair<EASYEDAPRO::SCH_ATTR, const EASYEDAPRO::V3_ROW*>* valueSource = nullptr;
1756
1757 if( valueAttrIt != attributes.end() && valueAttrIt->second.first.valVisible )
1758 valueSource = &valueAttrIt->second;
1759 else if( nameAttrIt != attributes.end() && nameAttrIt->second.first.valVisible )
1760 valueSource = &nameAttrIt->second;
1761 else if( valueAttrIt != attributes.end() )
1762 valueSource = &valueAttrIt->second;
1763 else if( nameAttrIt != attributes.end() )
1764 valueSource = &nameAttrIt->second;
1765
1766 wxString valueText;
1767
1768 if( valueAttrIt != attributes.end() && !valueAttrIt->second.first.value.IsEmpty() )
1769 valueText = valueAttrIt->second.first.value;
1770 else if( nameAttrIt != attributes.end() && !nameAttrIt->second.first.value.IsEmpty() )
1771 valueText = nameAttrIt->second.first.value;
1772 else
1773 valueText = fallbackValue;
1774
1775 SCH_FIELD* valueField = schSym->GetField( FIELD_T::VALUE );
1776
1777 if( !valueText.IsEmpty() )
1778 {
1779 if( valueSource && valueSource->second )
1780 {
1781 EASYEDAPRO::SCH_ATTR attr = valueSource->first;
1782 attr.value = valueText;
1783
1784 ApplyV3AttrToField( valueField, attr, false, true, compAttrs,
1785 schSym.get(), valueSource->second->inner );
1786 }
1787 else
1788 {
1789 valueField->SetText( ResolveDeviceFieldVariables( valueText, compAttrs ) );
1790 }
1791 }
1792 else
1793 {
1794 valueField->SetVisible( false );
1795 }
1796
1797 // Apply other visible attributes as fields
1798 for( const auto& [key, attrPair] : attributes )
1799 {
1800 if( key == wxS( "Designator" ) || key == wxS( "Device" ) || key == wxS( "Symbol" )
1801 || key == wxS( "Name" ) || key == wxS( "Value" ) )
1802 {
1803 continue;
1804 }
1805
1806 const EASYEDAPRO::SCH_ATTR& attr = attrPair.first;
1807 const EASYEDAPRO::V3_ROW* attrRow = attrPair.second;
1808
1809 if( !attr.keyVisible && !attr.valVisible )
1810 continue;
1811
1812 SCH_FIELD* field = nullptr;
1813
1814 if( key == wxS( "Footprint" ) )
1815 {
1816 field = schSym->GetField( FIELD_T::FOOTPRINT );
1817 }
1818 else
1819 {
1820 field = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, key ) );
1821 }
1822
1823 if( field )
1824 {
1825 if( key == wxS( "Footprint" ) )
1826 {
1827 EASYEDAPRO::SCH_ATTR footprintAttr = attr;
1828 footprintAttr.value = ResolveV3FootprintText( attr.value, aProject,
1829 aLibName );
1830
1831 ApplyV3AttrToField( field, footprintAttr, false, true,
1832 compAttrs, schSym.get(), attrRow->inner );
1833 }
1834 else
1835 {
1836 ApplyV3AttrToField( field, attr, false, true, compAttrs,
1837 schSym.get(), attrRow->inner );
1838 }
1839 }
1840 }
1841 }
1842
1843 createdItems.push_back( std::move( schSym ) );
1844 }
1845
1846 BOX2I sheetBBox;
1847 bool hasBBox = false;
1848
1849 for( std::unique_ptr<SCH_ITEM>& item : createdItems )
1850 {
1851 BOX2I itemBBox;
1852
1853 if( item->Type() == SCH_SYMBOL_T )
1854 itemBBox = static_cast<SCH_SYMBOL*>( item.get() )->GetBodyAndPinsBoundingBox();
1855 else
1856 itemBBox = item->GetBoundingBox();
1857
1858 if( !hasBBox )
1859 {
1860 sheetBBox = itemBBox;
1861 hasBBox = true;
1862 }
1863 else
1864 {
1865 sheetBBox.Merge( itemBBox );
1866 }
1867 }
1868
1869 SCH_SCREEN* screen = aRootSheet->GetScreen();
1870
1871 if( hasBBox )
1872 {
1873 const int margin = schIUScale.MilsToIU( 200 );
1874
1875 VECTOR2I offset( -sheetBBox.GetLeft() + margin,
1876 -sheetBBox.GetTop() + margin );
1877
1878 int alignGrid = schIUScale.MilsToIU( 50 );
1879
1880 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1881 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1882
1883 PAGE_INFO pageInfo = screen->GetPageSettings();
1884 pageInfo.SetWidthMils( schIUScale.IUToMils( sheetBBox.GetWidth() + 2 * margin ) );
1885 pageInfo.SetHeightMils( schIUScale.IUToMils( sheetBBox.GetHeight() + 2 * margin ) );
1886
1887 screen->SetPageSettings( pageInfo );
1888
1889 for( std::unique_ptr<SCH_ITEM>& item : createdItems )
1890 {
1891 item->Move( offset );
1892 screen->Append( item.release() );
1893 }
1894 }
1895 else
1896 {
1897 for( std::unique_ptr<SCH_ITEM>& item : createdItems )
1898 screen->Append( item.release() );
1899 }
1900}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr coord_type GetTop() const
Definition box2.h:226
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:160
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
void SetTextColor(const COLOR4D &aColor)
Definition eda_text.h:309
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:471
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
void SetImportOffsetMM(const VECTOR2D &aOffset)
Set the offset in millimeters to add to coordinates when importing graphic items.
void SetScale(const VECTOR2D &aScale)
Set the scale factor affecting the imported shapes.
std::list< std::unique_ptr< EDA_ITEM > > & GetItems()
Return the list of objects representing the imported shapes.
virtual void SetImporter(GRAPHICS_IMPORTER *aImporter)
Set the receiver of the imported shapes.
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:144
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
Define a library symbol object.
Definition lib_symbol.h:114
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:190
void SetGlobalPower()
SCH_FIELD * FindFieldCaseInsensitive(const wxString &aFieldName)
wxString GetName() const override
Definition lib_symbol.h:183
void SetUnitCount(int aCount, bool aDuplicateDrawItems)
Set the units per symbol count.
void SetDescription(const wxString &aDescription)
Set the Description field text value.
void SetKeyWords(const wxString &aKeyWords)
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
wxString GetFootprint() const override
For items with footprint fields.
void AddField(SCH_FIELD *aField)
Add a field.
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
SCH_FIELD & GetReferenceField()
Definition lib_symbol.h:451
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
void SetHeightMils(double aHeightInMils)
void SetWidthMils(double aWidthInMils)
A progress reporter interface for use in multi-threaded environments.
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.
VECTOR2I GetSize() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Holds all the data relating to one schematic.
Definition schematic.h:149
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:312
static T ScaleSize(T aValue)
void ParseSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const nlohmann::json &aProject, std::map< wxString, EASYEDAPRO::SYM_INFO > &aSymbolMap, const std::map< wxString, EASYEDAPRO::BLOB > &aBlobMap, const EASYEDAPRO::V3_DOC_RAW &aDoc, const wxString &aLibName)
void ApplyV3LineStyle(T &shape, const nlohmann::json &aInner)
void ApplyV3FontStyle(T &text, const nlohmann::json &aInner, const char *aAlignField="align")
SCH_EASYEDAPRO_V3_PARSER(SCHEMATIC *aSchematic, PROGRESS_REPORTER *aProgressReporter)
EASYEDAPRO::SYM_INFO ParseSymbol(const EASYEDAPRO::V3_DOC_RAW &aDoc, const std::map< wxString, wxString > &aDeviceAttributes, const std::map< wxString, EASYEDAPRO::BLOB > &aBlobMap={})
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:139
void SetPosition(const VECTOR2I &aPosition) override
void SetText(const wxString &aText) override
void SetNameShown(bool aShown=true)
Definition sch_field.h:230
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
virtual void MirrorHorizontally(int aCenter)
Mirror item horizontally about aCenter.
Definition sch_item.h:430
virtual void Rotate(const VECTOR2I &aCenter, bool aRotateCCW)
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_item.h:446
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:144
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:145
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:151
Schematic symbol object.
Definition sch_symbol.h:75
BOX2I GetBodyAndPinsBoundingBox() const override
Return a bounding box for the symbol body and pins but not the fields.
Spin style for labels of all kinds on schematics.
Definition sch_label.h:38
SPIN_STYLE RotateCCW()
Simple container to manage line stroke parameters.
void SetLineStyle(LINE_STYLE aLineStyle)
void SetWidth(int aWidth)
void SetColor(const KIGFX::COLOR4D &aColor)
bool Import() override
Actually imports the file.
virtual double GetImageWidth() const override
Return image width from original imported file.
bool LoadFromMemory(const wxMemoryBuffer &aMemBuffer) override
Set memory buffer with content for import.
virtual double GetImageHeight() const override
Return image height from original imported file.
virtual void SetShowPinNumbers(bool aShow)
Set or clear the pin number visibility flag.
Definition symbol.h:174
virtual void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
Definition symbol.h:168
Define a general 2D-vector/point.
Definition vector2d.h:67
#define _(s)
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
#define ENDPOINT
ends. (Used to support dragging.)
#define STARTPOINT
When a line is selected, these flags indicate which.
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
void ConvertImageToLibShapes(LIB_SYMBOL *aSymbol, int unit, wxImage img, VECTOR2D pixelScale, VECTOR2D offset)
const wxChar *const traceEasyEdaIo
@ LAYER_DEVICE
Definition layer_ids.h:494
@ LAYER_WIRE
Definition layer_ids.h:480
wxString get_def(const std::map< wxString, wxString > &aMap, const char *aKey, const char *aDefval="")
Definition map_helpers.h:60
std::optional< V > get_opt(const std::map< wxString, V > &aMap, const wxString &aKey)
Definition map_helpers.h:30
LIB_ID ToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
bool V3GetBool(const nlohmann::json &aObj, const char *aKey, bool aDefault)
void ForEachImportedDeviceField(const std::map< wxString, wxString > &aDeviceAttributes, bool aIncludeValue, const std::function< void(const wxString &aKey, const wxString &aValue)> &aCallback)
Invoke aCallback for each non-empty whitelisted Device field (resolved + normalized).
wxString ResolveDeviceFieldVariables(const wxString &aInput, const std::map< wxString, wxString > &aDeviceAttributes)
Resolve EasyEDA ={Var} / ={A}text{B} field expressions against device attributes.
wxString NormalizeEasyEDAText(wxString aText)
Replace EasyEDA temperature glyph (℃) with °C.
V3_DEVICE_DATA GetV3DeviceData(const nlohmann::json &aProject, const wxString &aDeviceUuid)
wxString LookupV3DeviceLibName(const nlohmann::json &aProject, const wxString &aDeviceUuid)
int AlignToFontV(const wxString &aAlign)
Map an EasyEDA "HORIZ_VERT" alignment token to a KiCad justify code (0 top/left, 1 center,...
bool V3IsNullOrMissing(const nlohmann::json &aObj, const char *aKey)
double V3GetDouble(const nlohmann::json &aObj, const char *aKey, double aDefault)
wxString V3JsonToString(const nlohmann::json &aValue, const wxString &aDefault)
int V3GetInt(const nlohmann::json &aObj, const char *aKey, int aDefault)
int AlignToFontH(const wxString &aAlign)
wxString V3GetString(const nlohmann::json &aObj, const char *aKey, const wxString &aDefault)
@ PT_INPUT
usual pin input: must be connected
Definition pin_type.h:33
@ PT_NC
not connected (must be left open)
Definition pin_type.h:46
@ 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_POWER_IN
power input (GND, VCC for ICs). Must be connected to a power output.
Definition pin_type.h:42
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
static LINE_STYLE ConvertStrokeStyle(const wxString &aStyle)
static VECTOR2< T > V3ScalePos(VECTOR2< T > aValue)
static nlohmann::json FlattenPoints(const nlohmann::json &aPoints)
static LINE_STYLE ConvertStrokeStyle(const nlohmann::json &aStyle)
static wxString ResolveV3FootprintText(const wxString &aFootprintValue, const nlohmann::json &aProject, const wxString &aLibName)
static VECTOR2< T > V3ScalePosSym(VECTOR2< T > aValue)
static EASYEDAPRO::SCH_ATTR V3RowToSchAttr(const V3_ROW &aRow)
static void ApplyV3AttrToField(SCH_FIELD *aField, const EASYEDAPRO::SCH_ATTR &aAttr, bool aIsSym, bool aToSym, const std::map< wxString, wxString > &aDeviceAttributes, SCH_SYMBOL *aParent, const nlohmann::json &aInner)
static constexpr double c_v3TextSizeScale
static void ApplyV3TextSizeIfDefined(T &aText, const nlohmann::json &aInner)
LABEL_FLAG_SHAPE
Label and flag shapes used with text objects.
Definition sch_label.h:97
@ L_BIDI
Definition sch_label.h:100
@ L_TRISTATE
Definition sch_label.h:101
@ L_UNSPECIFIED
Definition sch_label.h:102
@ L_OUTPUT
Definition sch_label.h:99
@ L_INPUT
Definition sch_label.h:98
const int scale
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
LINE_STYLE
Dashed line types.
EASYEDAPRO::SYM_PIN pin
std::optional< VECTOR2D > position
std::unique_ptr< LIB_SYMBOL > libSymbol
std::vector< PIN_INFO > pins
std::optional< EASYEDAPRO::SCH_ATTR > symbolAttr
std::map< wxString, int > partUnits
EASYEDAPRO::SYM_HEAD head
std::map< wxString, wxString > attributes
Raw parsed document from an EasyEDA Pro v3 .epru stream.
std::vector< V3_ROW > rows
One parsed row from an EasyEDA Pro v3 .epru document stream.
@ SYM_ORIENT_270
Definition symbol.h:39
@ SYM_MIRROR_Y
Definition symbol.h:41
@ SYM_ORIENT_180
Definition symbol.h:38
@ SYM_MIRROR_X
Definition symbol.h:40
@ SYM_ORIENT_90
Definition symbol.h:37
@ SYM_ORIENT_0
Definition symbol.h:36
@ USER
The field ID hasn't been set yet; field is invalid.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ FOOTPRINT
Field Name Module PCB, i.e. "16DIP300".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
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.
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
wxLogTrace helper definitions.
@ SCH_LINE_T
Definition typeinfo.h:159
@ SCH_SYMBOL_T
Definition typeinfo.h:168
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707