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