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->SetArcGeometry( V3ScalePosSym( start ),
398 V3ScalePosSym( mid ),
399 V3ScalePosSym( end ) );
400 shape->SetUnit( rowUnit );
401 ApplyV3LineStyle( shape, row.inner );
402
403 ksymbol->AddDrawItem( shape.release() );
404 }
405 else if( row.type == wxS( "BEZIER" ) )
406 {
407 std::vector<double> points = row.inner.value( "controls", nlohmann::json::array() );
408
409 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
410
411 for( size_t i = 1; i < points.size(); i += 2 )
412 {
413 VECTOR2I pt = V3ScalePosSym( VECTOR2D( points[i - 1], points[i] ) );
414
415 switch( i )
416 {
417 case 1: shape->SetStart( pt ); break;
418 case 3: shape->SetBezierC1( pt ); break;
419 case 5: shape->SetBezierC2( pt ); break;
420 case 7: shape->SetEnd( pt ); break;
421 }
422 }
423
424 shape->SetUnit( rowUnit );
425 ApplyV3LineStyle( shape, row.inner );
426
427 ksymbol->AddDrawItem( shape.release() );
428 }
429 else if( row.type == wxS( "POLY" ) )
430 {
431 nlohmann::json flatPts = FlattenPoints(
432 row.inner.value( "points", nlohmann::json::array() ) );
433 std::vector<double> points = flatPts;
434
435 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
436
437 for( size_t i = 1; i < points.size(); i += 2 )
438 shape->AddPoint( V3ScalePosSym( VECTOR2D( points[i - 1], points[i] ) ) );
439
440 shape->SetUnit( rowUnit );
441 ApplyV3LineStyle( shape, row.inner );
442
443 ksymbol->AddDrawItem( shape.release() );
444 }
445 else if( row.type == wxS( "TEXT" ) )
446 {
447 VECTOR2D pos( V3GetDouble( row.inner, "x" ), V3GetDouble( row.inner, "y" ) );
448 double angle = V3GetDouble( row.inner, "rotation" );
449 wxString textStr = V3GetString( row.inner, "value" );
450
451 auto text = std::make_unique<SCH_TEXT>(
452 V3ScalePosSym( pos ), UnescapeHTML( textStr ),
453 LAYER_DEVICE );
454
455 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
456 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
457 text->SetTextAngleDegrees( angle );
458 text->SetUnit( rowUnit );
460
461 ksymbol->AddDrawItem( text.release() );
462 }
463 else if( row.type == wxS( "OBJ" ) )
464 {
465 VECTOR2D start( V3GetDouble( row.inner, "startX" ),
466 V3GetDouble( row.inner, "startY" ) );
467 VECTOR2D size( V3GetDouble( row.inner, "width" ),
468 V3GetDouble( row.inner, "height" ) );
469 int upsideDown = V3GetBool( row.inner, "isMirror" ) ? 1 : 0;
470
471 wxString imageUrl = V3GetString( row.inner, "content" );
472 wxString mimeType, data;
473
474 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
475 {
476 wxArrayString paramsArr =
477 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
478
479 data = imageUrl.AfterFirst( ',' );
480
481 if( paramsArr.size() > 0 )
482 mimeType = paramsArr[0];
483 }
484 else if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
485 {
486 wxString objectId = imageUrl.AfterLast( ':' );
487
488 if( auto blob = get_opt( aBlobMap, objectId ) )
489 {
490 wxString blobUrl = blob->url;
491
492 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
493 {
494 wxArrayString paramsArr =
495 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
496
497 data = blobUrl.AfterFirst( ',' );
498
499 if( paramsArr.size() > 0 )
500 mimeType = paramsArr[0];
501 }
502 }
503 }
504
505 if( mimeType.empty() || data.empty() )
506 continue;
507
508 wxMemoryBuffer buf = wxBase64Decode( data );
509
510 if( mimeType == wxS( "image/svg+xml" ) )
511 {
512 VECTOR2D offset = V3ScalePosSym( start );
513
514 SVG_IMPORT_PLUGIN svgImportPlugin;
515 GRAPHICS_IMPORTER_LIB_SYMBOL libsymImporter( ksymbol, 0 );
516
517 svgImportPlugin.SetImporter( &libsymImporter );
518 svgImportPlugin.LoadFromMemory( buf );
519
520 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
521 svgImportPlugin.GetImageHeight() );
522
523 VECTOR2D pixelScale(
525 / imSize.x,
527 / imSize.y );
528
529 if( upsideDown )
530 pixelScale.y *= -1;
531
532 libsymImporter.SetScale( pixelScale );
533
534 VECTOR2D offsetMM( schIUScale.IUTomm( offset.x ),
535 schIUScale.IUTomm( offset.y ) );
536
537 libsymImporter.SetImportOffsetMM( offsetMM );
538 svgImportPlugin.Import();
539
540 for( std::unique_ptr<EDA_ITEM>& item : libsymImporter.GetItems() )
541 ksymbol->AddDrawItem( static_cast<SCH_ITEM*>( item.release() ) );
542 }
543 else
544 {
545 wxMemoryInputStream memis( buf.GetData(), buf.GetDataLen() );
546
547 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
548 & ~wxImage::Load_Verbose );
549 wxImage img;
550
551 if( img.LoadFile( memis, mimeType ) )
552 {
553 int dimMul = img.GetWidth() * img.GetHeight();
554 double maxPixels = 30000;
555
556 if( dimMul > maxPixels )
557 {
558 double scale = sqrt( maxPixels / dimMul );
559 img.Rescale( img.GetWidth() * scale, img.GetHeight() * scale );
560 }
561
562 VECTOR2D pixelScale(
563 SCH_EASYEDAPRO_PARSER::ScaleSize( size.x ) / img.GetWidth(),
564 SCH_EASYEDAPRO_PARSER::ScaleSize( size.y ) / img.GetHeight() );
565
566 ConvertImageToLibShapes( ksymbol, 0, img, pixelScale,
567 V3ScalePosSym( start ) );
568 }
569 }
570 }
571 else if( row.type == wxS( "PIN" ) )
572 {
573 unitParentedRows[rowUnit][row.id].push_back( &row );
574 }
575 else if( row.type == wxS( "ATTR" ) )
576 {
577 wxString parentId = V3GetString( row.inner, "parentId" );
578
579 if( parentId.empty() )
580 {
582 attr.key = V3GetString( row.inner, "key" );
583 attr.value = V3JsonToString( row.inner.value( "value", nlohmann::json() ) );
584 attr.keyVisible = V3GetBool( row.inner, "keyVisible" );
585 attr.valVisible = V3GetBool( row.inner, "valueVisible" );
586
587 if( !V3IsNullOrMissing( row.inner, "x" ) )
588 {
589 attr.position = VECTOR2D( V3GetDouble( row.inner, "x" ),
590 V3GetDouble( row.inner, "y" ) );
591 }
592
593 attr.rotation = V3GetDouble( row.inner, "rotation" );
594 attr.fontStyle = wxEmptyString; // v3 has inline styles
595
596 unitAttributes[rowUnit].emplace( attr.key, attr );
597 unitAttributeRows[rowUnit].emplace( attr.key, &row );
598 }
599 else
600 {
601 unitParentedRows[rowUnit][parentId].push_back( &row );
602 }
603 }
604 }
605
606 // Apply symbol type attributes
609 {
610 ksymbol->SetGlobalPower();
611 ksymbol->GetReferenceField().SetText( wxS( "#PWR" ) );
612 ksymbol->GetReferenceField().SetVisible( false );
613 ksymbol->SetKeyWords( wxS( "power-flag" ) );
614 ksymbol->SetShowPinNames( false );
615 ksymbol->SetShowPinNumbers( false );
616
617 if( auto globalNetAttr = get_opt( unitAttributes[1], wxS( "Global Net Name" ) ) )
618 {
619 wxString powerName = globalNetAttr->value;
620
621 if( powerName.IsEmpty() )
622 {
623 if( auto nameAttr = get_opt( unitAttributes[1], wxS( "Name" ) ) )
624 powerName = nameAttr->value;
625 }
626
627 EDA_TEXT* valueText = static_cast<EDA_TEXT*>( &ksymbol->GetValueField() );
628 valueText->SetText( powerName );
629 valueText->SetVisible( globalNetAttr->valVisible );
630
631 if( auto globalNetAttrRow = get_opt( unitAttributeRows[1], wxS( "Global Net Name" ) ) )
632 {
633 const nlohmann::json& attrInner = ( *globalNetAttrRow )->inner;
634
635 wxString color = V3GetString( attrInner, "color", wxEmptyString );
636 wxString fontFamily = V3GetString( attrInner, "fontFamily", wxS( "default" ) );
637 wxString align = V3GetString( attrInner, "align", wxS( "LEFT_BOTTOM" ) );
638
639 if( !color.empty() && color.StartsWith( wxS( "#" ) ) )
640 {
641 COLOR4D c( color );
642 valueText->SetTextColor( c );
643 }
644
645 if( fontFamily != wxS( "Arial" ) && !fontFamily.IsSameAs( wxS( "default" ), false ) )
646 valueText->SetFont( KIFONT::FONT::GetFont( fontFamily ) );
647
648 ApplyV3TextSizeIfDefined( valueText, attrInner );
649
650 int vAlign = AlignToFontV( align );
651 int hAlign = AlignToFontH( align );
652
653 if( vAlign == 0 )
655 else if( vAlign == 1 )
657 else
659
660 if( hAlign == 0 )
662 else if( hAlign == 1 )
664 else
666 }
667
668 if( globalNetAttr->position )
669 ksymbol->GetValueField().SetPosition( V3ScalePosSym( *globalNetAttr->position ) );
670
671 wxString globalNetname = globalNetAttr->value;
672
673 if( globalNetname.IsEmpty() )
674 globalNetname = powerName;
675
676 if( !globalNetname.empty() )
677 {
678 ksymbol->SetDescription( wxString::Format(
679 _( "Power symbol creates a global label with name '%s'" ),
680 globalNetname ) );
681 }
682 }
683 }
684 else
685 {
686 auto designatorAttr = get_opt( unitAttributes[1], wxS( "Designator" ) );
687
688 if( designatorAttr && !designatorAttr->value.empty() )
689 {
690 wxString symbolPrefix = designatorAttr->value;
691
692 if( symbolPrefix.EndsWith( wxS( "?" ) ) )
693 symbolPrefix.RemoveLast();
694
695 ksymbol->GetReferenceField().SetText( symbolPrefix );
696 }
697
699 aDeviceAttributes, true,
700 [&]( const wxString& attrName, const wxString& value )
701 {
702 SCH_FIELD* fd = ksymbol->FindFieldCaseInsensitive( attrName );
703
704 if( !fd )
705 {
706 fd = new SCH_FIELD( ksymbol, FIELD_T::USER, attrName );
707 ksymbol->AddField( fd );
708 }
709
710 fd->SetText( value );
711 fd->SetVisible( false );
712 } );
713 }
714
715 // Process pins
716 for( auto& [unitId, parentedRows] : unitParentedRows )
717 {
718 for( auto& [pinId, rows] : parentedRows )
719 {
720 const V3_ROW* pinRow = nullptr;
721 std::map<wxString, EASYEDAPRO::SCH_ATTR> pinAttributes;
722
723 for( const V3_ROW* r : rows )
724 {
725 if( r->type == wxS( "ATTR" ) )
726 {
728 attr.key = V3GetString( r->inner, "key" );
729 attr.value = V3JsonToString( r->inner.value( "value", nlohmann::json() ) );
730 attr.keyVisible = V3GetBool( r->inner, "keyVisible" );
731 attr.valVisible = V3GetBool( r->inner, "valueVisible" );
732 pinAttributes.emplace( attr.key, attr );
733
734 }
735 else if( r->type == wxS( "PIN" ) )
736 {
737 pinRow = r;
738 }
739 }
740
741 if( !pinRow )
742 continue;
743
744 EASYEDAPRO::PIN_INFO pinInfo;
745 pinInfo.pin.position = VECTOR2D( V3GetDouble( pinRow->inner, "x" ),
746 V3GetDouble( pinRow->inner, "y" ) );
747 pinInfo.pin.length = V3GetDouble( pinRow->inner, "length" );
748 pinInfo.pin.rotation = V3GetDouble( pinRow->inner, "rotation" );
749
750 wxString pinShape = V3GetString( pinRow->inner, "pinShape" );
751 pinInfo.pin.inverted = ( pinShape == wxS( "INVERTED" )
752 || pinShape == wxS( "INVERTED_CLOCK" ) );
753
754 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( ksymbol );
755
756 pin->SetUnit( unitId );
757 pin->SetLength( SCH_EASYEDAPRO_PARSER::ScaleSize( pinInfo.pin.length ) );
758 pin->SetPosition( V3ScalePosSym( pinInfo.pin.position ) );
759
761
762 if( pinInfo.pin.rotation == 0 )
764 if( pinInfo.pin.rotation == 90 )
766 if( pinInfo.pin.rotation == 180 )
768 if( pinInfo.pin.rotation == 270 )
770
771 pin->SetOrientation( orient );
772
774 {
775 pin->SetName( ksymbol->GetName() );
776 }
777 else
778 {
779 auto pinNameAttr = get_opt( pinAttributes, wxS( "Pin Name" ) );
780
781 if( !pinNameAttr )
782 pinNameAttr = get_opt( pinAttributes, wxS( "NAME" ) );
783
784 if( pinNameAttr )
785 {
786 pin->SetName( pinNameAttr->value );
787 pinInfo.name = pinNameAttr->value;
788
789 if( !pinNameAttr->valVisible )
790 pin->SetNameTextSize( schIUScale.MilsToIU( 1 ) );
791 }
792 }
793
794 auto pinNumAttr = get_opt( pinAttributes, wxS( "Pin Number" ) );
795
796 if( !pinNumAttr )
797 pinNumAttr = get_opt( pinAttributes, wxS( "NUMBER" ) );
798
799 if( pinNumAttr )
800 {
801 pin->SetNumber( pinNumAttr->value );
802 pinInfo.number = pinNumAttr->value;
803
804 if( !pinNumAttr->valVisible )
805 pin->SetNumberTextSize( schIUScale.MilsToIU( 1 ) );
806 }
807
809 {
811 }
812 else if( auto pinTypeAttr = get_opt( pinAttributes, wxS( "Pin Type" ) ) )
813 {
814 if( pinTypeAttr->value == wxS( "IN" ) )
816 if( pinTypeAttr->value == wxS( "OUT" ) )
818 if( pinTypeAttr->value == wxS( "BI" ) )
820 }
821
822 if( get_opt( pinAttributes, wxS( "NO_CONNECT" ) ) )
823 pin->SetType( ELECTRICAL_PINTYPE::PT_NC );
824
825 if( pin->GetNumberTextSize() * int( pin->GetNumber().size() ) > pin->GetLength() )
826 pin->SetNumberTextSize( pin->GetLength() / pin->GetNumber().size() );
827
828 symInfo.pins.push_back( pinInfo );
829 ksymbol->AddDrawItem( pin.release() );
830 }
831 }
832
833 symInfo.symbolAttr = get_opt( unitAttributes[1], wxS( "Symbol" ) );
834 symInfo.libSymbol = std::move( ksymbolPtr );
835
836 return symInfo;
837}
838
839
841{
843 attr.key = V3GetString( aRow.inner, "key" );
844 attr.value = V3JsonToString( aRow.inner.value( "value", nlohmann::json() ) );
845 attr.keyVisible = V3GetBool( aRow.inner, "keyVisible" );
846 attr.valVisible = V3GetBool( aRow.inner, "valueVisible" );
847
848 if( !V3IsNullOrMissing( aRow.inner, "x" ) )
849 {
850 attr.position = VECTOR2D( V3GetDouble( aRow.inner, "x" ),
851 V3GetDouble( aRow.inner, "y" ) );
852 }
853
854 attr.rotation = V3GetDouble( aRow.inner, "rotation" );
855 attr.fontStyle = wxEmptyString;
856
857 return attr;
858}
859
860
861static void ApplyV3AttrToField( SCH_FIELD* aField, const EASYEDAPRO::SCH_ATTR& aAttr,
862 bool aIsSym, bool aToSym,
863 const std::map<wxString, wxString>& aDeviceAttributes,
864 SCH_SYMBOL* aParent, const nlohmann::json& aInner )
865{
866 EDA_TEXT* text = static_cast<EDA_TEXT*>( aField );
867
868 text->SetText( ResolveDeviceFieldVariables( aAttr.value, aDeviceAttributes ) );
869 text->SetVisible( aAttr.keyVisible || aAttr.valVisible );
870
871 aField->SetNameShown( aAttr.keyVisible );
872
873 if( aAttr.position )
874 {
875 aField->SetPosition( !aIsSym ? V3ScalePos( *aAttr.position )
876 : V3ScalePosSym( *aAttr.position ) );
877 }
878
879 // Apply inline font style from v3 JSON
880 wxString color = V3GetString( aInner, "color", wxEmptyString );
881 wxString fontFamily = V3GetString( aInner, "fontFamily", wxS( "default" ) );
882 wxString align = V3GetString( aInner, "align", wxS( "LEFT_BOTTOM" ) );
883
884 if( !color.empty() && color.StartsWith( wxS( "#" ) ) )
885 {
886 COLOR4D c( color );
887 text->SetTextColor( c );
888 }
889
890 if( fontFamily != wxS( "Arial" ) && !fontFamily.IsSameAs( wxS( "default" ), false ) )
891 text->SetFont( KIFONT::FONT::GetFont( fontFamily ) );
892
894
895 int vAlign = AlignToFontV( align );
896 int hAlign = AlignToFontH( align );
897
898 if( vAlign == 0 )
899 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
900 else if( vAlign == 1 )
901 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
902 else
903 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
904
905 if( hAlign == 0 )
906 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
907 else if( hAlign == 1 )
908 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
909 else
910 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
911
912 auto parent = aParent;
913 if( aToSym && parent && parent->Type() == SCH_SYMBOL_T )
914 {
915 int orient = static_cast<SCH_SYMBOL*>( parent )->GetOrientation();
916
917 if( orient == SYM_ORIENT_180 )
918 {
919 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
920 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
921 }
922 else if( orient == SYM_MIRROR_X + SYM_ORIENT_0 )
923 {
924 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
925 }
926 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_0 )
927 {
928 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
929 }
930 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_180 )
931 {
932 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( text->GetHorizJustify() ) );
933 }
934 else if( orient == SYM_ORIENT_90 )
935 {
936 text->SetTextAngle( ANGLE_VERTICAL );
937 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
938 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
939 }
940 if( orient == SYM_ORIENT_270 )
941 {
942 text->SetTextAngle( ANGLE_VERTICAL );
943 }
944 else if( orient == SYM_MIRROR_X + SYM_ORIENT_90 )
945 {
946 text->SetTextAngle( ANGLE_VERTICAL );
947 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
948 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
949 }
950 else if( orient == SYM_MIRROR_X + SYM_ORIENT_270 )
951 {
952 text->SetTextAngle( ANGLE_VERTICAL );
953 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
954 }
955 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_90 )
956 {
957 text->SetTextAngle( ANGLE_VERTICAL );
958 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
959 }
960 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_270 )
961 {
962 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( text->GetHorizJustify() ) );
963 }
964
965 if( aAttr.rotation == 90 )
966 {
967 if( text->GetTextAngle() == ANGLE_HORIZONTAL )
968 text->SetTextAngle( ANGLE_VERTICAL );
969 else
970 text->SetTextAngle( ANGLE_HORIZONTAL );
971
972 if( orient == SYM_ORIENT_90 )
973 {
974 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
975 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
976 }
977 if( orient == SYM_ORIENT_270 )
978 {
979 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
980 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
981 }
982 else if( orient == SYM_MIRROR_X + SYM_ORIENT_90 )
983 {
984 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
985 }
986 }
987 }
988}
989
990
992 SCHEMATIC* aSchematic, SCH_SHEET* aRootSheet, const nlohmann::json& aProject,
993 std::map<wxString, EASYEDAPRO::SYM_INFO>& aSymbolMap,
994 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
995 const EASYEDAPRO::V3_DOC_RAW& aDoc, const wxString& aLibName )
996{
997 std::vector<std::unique_ptr<SCH_ITEM>> createdItems;
998
999 // v3: Collect WIRE and LINE rows by lineGroup for wire geometry
1000 std::map<wxString, std::vector<const EASYEDAPRO::V3_ROW*>> wireGeometry;
1001 std::map<wxString, const EASYEDAPRO::V3_ROW*> wireRows;
1002
1003 // Collect COMPONENT and ATTR rows by parent ID
1004 std::map<wxString, std::vector<const EASYEDAPRO::V3_ROW*>> parentedRows;
1005
1006 // First pass: collect wires, components, and attributes
1007 for( const EASYEDAPRO::V3_ROW& row : aDoc.rows )
1008 {
1009 if( row.type == wxS( "WIRE" ) )
1010 {
1011 wireRows[row.id] = &row;
1012 }
1013 else if( row.type == wxS( "LINE" ) && !EASYEDAPRO::V3IsNullOrMissing( row.inner, "lineGroup" ) )
1014 {
1015 wxString lineGroup = EASYEDAPRO::V3GetString( row.inner, "lineGroup" );
1016 wireGeometry[lineGroup].push_back( &row );
1017 }
1018 else if( row.type == wxS( "COMPONENT" ) )
1019 {
1020 parentedRows[row.id].push_back( &row );
1021 }
1022 else if( row.type == wxS( "ATTR" ) && !EASYEDAPRO::V3IsNullOrMissing( row.inner, "parentId" ) )
1023 {
1024 wxString parentId = EASYEDAPRO::V3GetString( row.inner, "parentId" );
1025 parentedRows[parentId].push_back( &row );
1026 }
1027 }
1028
1029 // Second pass: create shapes and other non-parented items
1030 for( const EASYEDAPRO::V3_ROW& row : aDoc.rows )
1031 {
1032 if( row.type == wxS( "RECT" ) )
1033 {
1034 double x1 = EASYEDAPRO::V3GetDouble( row.inner, "dotX1" );
1035 double y1 = EASYEDAPRO::V3GetDouble( row.inner, "dotY1" );
1036 double x2 = EASYEDAPRO::V3GetDouble( row.inner, "dotX2" );
1037 double y2 = EASYEDAPRO::V3GetDouble( row.inner, "dotY2" );
1038
1039 std::unique_ptr<SCH_SHAPE> rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
1040 rect->SetStart( V3ScalePos( VECTOR2D( x1, y1 ) ) );
1041 rect->SetEnd( V3ScalePos( VECTOR2D( x2, y2 ) ) );
1042
1043 SCH_SHAPE* rectPtr = rect.get();
1044 ApplyV3LineStyle( rectPtr, row.inner );
1045 createdItems.push_back( std::move( rect ) );
1046 }
1047 else if( row.type == wxS( "CIRCLE" ) )
1048 {
1049 double centerX = EASYEDAPRO::V3GetDouble( row.inner, "centerX" );
1050 double centerY = EASYEDAPRO::V3GetDouble( row.inner, "centerY" );
1051 double radius = EASYEDAPRO::V3GetDouble( row.inner, "radius" );
1052
1053 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
1054 circle->SetCenter( V3ScalePos( VECTOR2D( centerX, centerY ) ) );
1055 circle->SetEnd( circle->GetCenter()
1057
1058 SCH_SHAPE* circlePtr = circle.get();
1059 ApplyV3LineStyle( circlePtr, row.inner );
1060 createdItems.push_back( std::move( circle ) );
1061 }
1062 else if( row.type == wxS( "ARC" ) )
1063 {
1064 VECTOR2D start( EASYEDAPRO::V3GetDouble( row.inner, "startX" ),
1065 EASYEDAPRO::V3GetDouble( row.inner, "startY" ) );
1066 VECTOR2D mid( EASYEDAPRO::V3GetDouble( row.inner, "referX" ),
1067 EASYEDAPRO::V3GetDouble( row.inner, "referY" ) );
1068 VECTOR2D end( EASYEDAPRO::V3GetDouble( row.inner, "endX" ),
1069 EASYEDAPRO::V3GetDouble( row.inner, "endY" ) );
1070
1071 std::unique_ptr<SCH_SHAPE> arc = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC );
1072 arc->SetArcGeometry( V3ScalePos( start ),
1073 V3ScalePos( mid ),
1074 V3ScalePos( end ) );
1075
1076 SCH_SHAPE* arcPtr = arc.get();
1077 ApplyV3LineStyle( arcPtr, row.inner );
1078 createdItems.push_back( std::move( arc ) );
1079 }
1080 else if( row.type == wxS( "POLY" ) )
1081 {
1082 if( EASYEDAPRO::V3IsNullOrMissing( row.inner, "points" ) )
1083 continue;
1084
1085 nlohmann::json flatPts = FlattenPoints(
1086 row.inner.value( "points", nlohmann::json::array() ) );
1087 std::vector<double> pointsData = flatPts;
1088
1089 if( pointsData.size() < 4 )
1090 continue;
1091
1092 std::unique_ptr<SCH_SHAPE> poly = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY );
1093
1094 for( size_t i = 1; i < pointsData.size(); i += 2 )
1095 poly->AddPoint( V3ScalePos( VECTOR2D( pointsData[i - 1], pointsData[i] ) ) );
1096
1097 SCH_SHAPE* polyPtr = poly.get();
1098 ApplyV3LineStyle( polyPtr, row.inner );
1099 createdItems.push_back( std::move( poly ) );
1100 }
1101 else if( row.type == wxS( "TEXT" ) )
1102 {
1103 double x = EASYEDAPRO::V3GetDouble( row.inner, "x" );
1104 double y = EASYEDAPRO::V3GetDouble( row.inner, "y" );
1105 double angle = EASYEDAPRO::V3GetDouble( row.inner, "rotation", 0.0 );
1106 wxString text = EASYEDAPRO::V3GetString( row.inner, "value" );
1107
1108 std::unique_ptr<SCH_TEXT> schText = std::make_unique<SCH_TEXT>(
1109 V3ScalePos( VECTOR2D( x, y ) ),
1110 UnescapeHTML( text ) );
1111
1112 schText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1113 schText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
1114 schText->SetTextAngleDegrees( angle );
1115
1116 SCH_TEXT* textPtr = schText.get();
1117 ApplyV3FontStyle( textPtr, row.inner, "align" );
1118 createdItems.push_back( std::move( schText ) );
1119 }
1120 else if( row.type == wxS( "OBJ" ) )
1121 {
1122 if( EASYEDAPRO::V3IsNullOrMissing( row.inner, "content" ) )
1123 continue;
1124
1125 wxString content = EASYEDAPRO::V3GetString( row.inner, "content" );
1126
1127 double x = EASYEDAPRO::V3GetDouble( row.inner, "startX",
1128 EASYEDAPRO::V3GetDouble( row.inner, "x" ) );
1129 double y = EASYEDAPRO::V3GetDouble( row.inner, "startY",
1130 EASYEDAPRO::V3GetDouble( row.inner, "y" ) );
1131 double width = EASYEDAPRO::V3GetDouble( row.inner, "width" );
1132 double height = EASYEDAPRO::V3GetDouble( row.inner, "height" );
1133 double angle = EASYEDAPRO::V3GetDouble( row.inner, "rotation", 0.0 );
1134 bool flipped = EASYEDAPRO::V3GetBool( row.inner, "isMirror", false );
1135
1136 VECTOR2D start( x, y );
1137 VECTOR2D size( width, height );
1138 VECTOR2D kstart = V3ScalePos( start );
1140
1141 wxString mimeType;
1142 wxString base64Data;
1143
1144 if( content.BeforeFirst( ':' ) == wxS( "data" ) )
1145 {
1146 wxArrayString paramsArr =
1147 wxSplit( content.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1148
1149 if( paramsArr.size() > 0 )
1150 mimeType = paramsArr[0];
1151
1152 base64Data = content.AfterFirst( ',' );
1153 }
1154 else if( content.BeforeFirst( ':' ) == wxS( "blob" ) )
1155 {
1156 wxString objectId = content.AfterLast( ':' );
1157
1158 if( auto blob = get_opt( aBlobMap, objectId ) )
1159 {
1160 wxString blobUrl = blob->url;
1161
1162 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1163 {
1164 wxArrayString paramsArr =
1165 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1166
1167 if( paramsArr.size() > 0 )
1168 mimeType = paramsArr[0];
1169
1170 base64Data = blobUrl.AfterFirst( ',' );
1171 }
1172 }
1173 }
1174
1175 if( mimeType.empty() || base64Data.empty() )
1176 continue;
1177
1178 wxMemoryBuffer buf = wxBase64Decode( base64Data );
1179
1180 if( mimeType == wxS( "image/svg+xml" ) )
1181 {
1182 SVG_IMPORT_PLUGIN svgImportPlugin;
1183 GRAPHICS_IMPORTER_SCH schImporter;
1184
1185 svgImportPlugin.SetImporter( &schImporter );
1186 svgImportPlugin.LoadFromMemory( buf );
1187
1188 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
1189 svgImportPlugin.GetImageHeight() );
1190
1191 VECTOR2D pixelScale( schIUScale.IUTomm( SCH_EASYEDAPRO_PARSER::ScaleSize( size.x ) )
1192 / imSize.x,
1194 / imSize.y );
1195
1196 schImporter.SetScale( pixelScale );
1197
1198 VECTOR2D offsetMM( schIUScale.IUTomm( kstart.x ), schIUScale.IUTomm( kstart.y ) );
1199
1200 schImporter.SetImportOffsetMM( offsetMM );
1201
1202 svgImportPlugin.Import();
1203
1204 for( std::unique_ptr<EDA_ITEM>& item : schImporter.GetItems() )
1205 {
1206 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item.release() );
1207
1208 for( double i = angle; i > 0; i -= 90 )
1209 {
1210 if( schItem->Type() == SCH_LINE_T )
1211 {
1212 schItem->SetFlags( STARTPOINT );
1213 schItem->Rotate( kstart, false );
1214 schItem->ClearFlags( STARTPOINT );
1215
1216 schItem->SetFlags( ENDPOINT );
1217 schItem->Rotate( kstart, false );
1218 schItem->ClearFlags( ENDPOINT );
1219 }
1220 else
1221 {
1222 schItem->Rotate( kstart, false );
1223 }
1224 }
1225
1226 if( flipped )
1227 {
1228 if( schItem->Type() == SCH_LINE_T )
1229 schItem->SetFlags( STARTPOINT | ENDPOINT );
1230
1231 schItem->MirrorHorizontally( kstart.x );
1232
1233 if( schItem->Type() == SCH_LINE_T )
1234 schItem->ClearFlags( STARTPOINT | ENDPOINT );
1235 }
1236
1237 createdItems.emplace_back( schItem );
1238 }
1239 }
1240 else
1241 {
1242 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
1243 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1244
1245 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1246 & ~wxImage::Load_Verbose );
1247
1248 if( refImage.ReadImageFile( buf ) )
1249 {
1250 VECTOR2D kcenter = kstart + ksize / 2;
1251
1252 double scaleFactor = SCH_EASYEDAPRO_PARSER::ScaleSize( size.x )
1253 / refImage.GetSize().x;
1254 refImage.SetImageScale( scaleFactor );
1255 bitmap->SetPosition( kcenter );
1256
1257 for( double i = angle; i > 0; i -= 90 )
1258 bitmap->Rotate( kstart, false );
1259
1260 if( flipped )
1261 bitmap->MirrorHorizontally( kstart.x );
1262
1263 createdItems.push_back( std::move( bitmap ) );
1264 }
1265 }
1266 }
1267 }
1268
1269 // Third pass: process wires
1270 for( const auto& [wireId, wireRow] : wireRows )
1271 {
1272 auto geomIt = wireGeometry.find( wireId );
1273 if( geomIt == wireGeometry.end() )
1274 continue;
1275
1276 const std::vector<const EASYEDAPRO::V3_ROW*>& lineRows = geomIt->second;
1277
1278 bool firstPointSet = false;
1279 VECTOR2D firstPoint;
1280
1281 for( const EASYEDAPRO::V3_ROW* lineRow : lineRows )
1282 {
1283 if( EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "startX" )
1284 || EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "startY" )
1285 || EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "endX" )
1286 || EASYEDAPRO::V3IsNullOrMissing( lineRow->inner, "endY" ) )
1287 {
1288 continue;
1289 }
1290
1291 VECTOR2D start( EASYEDAPRO::V3GetDouble( lineRow->inner, "startX" ),
1292 EASYEDAPRO::V3GetDouble( lineRow->inner, "startY" ) );
1293 VECTOR2D end( EASYEDAPRO::V3GetDouble( lineRow->inner, "endX" ),
1294 EASYEDAPRO::V3GetDouble( lineRow->inner, "endY" ) );
1295
1296 if( !firstPointSet )
1297 {
1298 firstPoint = start;
1299 firstPointSet = true;
1300 }
1301
1302 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>(
1303 V3ScalePos( start ), LAYER_WIRE );
1304
1305 line->SetEndPoint( V3ScalePos( end ) );
1306 createdItems.push_back( std::move( line ) );
1307 }
1308
1309 wxString netName;
1310 bool netVisible = false;
1311 VECTOR2D netPos;
1312 bool netPosSet = false;
1313 wxString netAlign = wxS( "LEFT_BOTTOM" );
1314 int netRotation = 0;
1315
1316 if( auto attrRows = get_opt( parentedRows, wireId ) )
1317 {
1318 for( const EASYEDAPRO::V3_ROW* row : *attrRows )
1319 {
1320 if( row->type != wxS( "ATTR" ) )
1321 continue;
1322
1323 if( EASYEDAPRO::V3GetString( row->inner, "key" ) != wxS( "NET" ) )
1324 continue;
1325
1326 netName = EASYEDAPRO::V3JsonToString( row->inner.value( "value", nlohmann::json() ) );
1327 netVisible = EASYEDAPRO::V3GetBool( row->inner, "valueVisible" );
1328 netAlign = EASYEDAPRO::V3GetString( row->inner, "align", wxS( "LEFT_BOTTOM" ) );
1329 netRotation = EASYEDAPRO::V3GetInt( row->inner, "rotation", 0 );
1330
1331 if( !EASYEDAPRO::V3IsNullOrMissing( row->inner, "x" )
1332 && !EASYEDAPRO::V3IsNullOrMissing( row->inner, "y" ) )
1333 {
1334 netPos = VECTOR2D( EASYEDAPRO::V3GetDouble( row->inner, "x" ),
1335 EASYEDAPRO::V3GetDouble( row->inner, "y" ) );
1336 netPosSet = true;
1337 }
1338
1339 break;
1340 }
1341 }
1342
1343 if( netVisible && !netName.IsEmpty() && firstPointSet )
1344 {
1345 VECTOR2D labelPos = netPosSet ? netPos : firstPoint;
1346
1347 std::unique_ptr<SCH_LABEL> label = std::make_unique<SCH_LABEL>(
1348 V3ScalePos( labelPos ), netName );
1349
1350 SPIN_STYLE spinStyle = SPIN_STYLE::RIGHT;
1351
1352 if( netAlign.StartsWith( wxS( "RIGHT" ) ) )
1353 spinStyle = SPIN_STYLE::LEFT;
1354 else if( netAlign.StartsWith( wxS( "LEFT" ) ) )
1355 spinStyle = SPIN_STYLE::RIGHT;
1356
1357 int rot = netRotation % 360;
1358
1359 if( rot < 0 )
1360 rot += 360;
1361
1362 // EasyEDA uses CCW rotation, same as KiCad.
1363 if( rot % 90 == 0 )
1364 {
1365 for( int i = 0; i < rot; i += 90 )
1366 spinStyle = spinStyle.RotateCCW();
1367 }
1368
1369 label->SetSpinStyle( spinStyle );
1370
1371 createdItems.push_back( std::move( label ) );
1372 }
1373 }
1374
1375 // Fourth pass: process components with their attributes
1376 for( const auto& [parentId, rows] : parentedRows )
1377 {
1378 const EASYEDAPRO::V3_ROW* componentRow = nullptr;
1379 std::map<wxString, std::pair<EASYEDAPRO::SCH_ATTR, const EASYEDAPRO::V3_ROW*>> attributes;
1380
1381 for( const EASYEDAPRO::V3_ROW* row : rows )
1382 {
1383 if( row->type == wxS( "COMPONENT" ) )
1384 {
1385 componentRow = row;
1386 }
1387 else if( row->type == wxS( "ATTR" ) )
1388 {
1389 EASYEDAPRO::SCH_ATTR attr = V3RowToSchAttr( *row );
1390 attributes.emplace( attr.key, std::make_pair( attr, row ) );
1391 }
1392 }
1393
1394 if( !componentRow )
1395 continue;
1396
1397 auto deviceAttrIt = attributes.find( "Device" );
1398 auto symbolAttrIt = attributes.find( "Symbol" );
1399
1400 std::map<wxString, wxString> compAttrs;
1401
1402 if( deviceAttrIt != attributes.end() && !deviceAttrIt->second.first.value.IsEmpty() )
1403 {
1404 V3_DEVICE_DATA deviceData = GetV3DeviceData( aProject, deviceAttrIt->second.first.value );
1405
1406 if( deviceData.found )
1407 compAttrs = std::move( deviceData.attributes );
1408 }
1409
1410 wxString symbolId;
1411
1412 if( symbolAttrIt != attributes.end() && !symbolAttrIt->second.first.value.IsEmpty() )
1413 symbolId = symbolAttrIt->second.first.value;
1414 else
1415 symbolId = get_def( compAttrs, wxS( "Symbol" ), wxEmptyString );
1416
1417 if( symbolId.IsEmpty() )
1418 {
1419 wxLogTrace( traceEasyEdaIo, wxT( "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 wxLogTrace( traceEasyEdaIo, "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 wxLogTrace( traceEasyEdaIo,
1445 wxT( "EasyEDA Pro v3 component '%s': partId '%s' not found in "
1446 "symbol '%s', using unit 1." ),
1447 parentId, unitName, symbolId );
1448 }
1449 }
1450
1451 // Prefer the Device-backed project-lib name when the component links a Device.
1452 wxString libItemName = newLibSymbol.GetLibId().GetLibItemName();
1453
1454 if( deviceAttrIt != attributes.end() && !deviceAttrIt->second.first.value.IsEmpty() )
1455 {
1456 wxString deviceLibName = LookupV3DeviceLibName( aProject, deviceAttrIt->second.first.value );
1457
1458 if( !deviceLibName.empty() )
1459 libItemName = deviceLibName;
1460 }
1461
1462 LIB_ID libId = EASYEDAPRO::ToKiCadLibID( aLibName, libItemName );
1463
1464 auto schSym = std::make_unique<SCH_SYMBOL>( newLibSymbol, libId,
1465 &aSchematic->CurrentSheet(),
1466 unitId );
1467
1468 schSym->SetFootprintFieldText( newLibSymbol.GetFootprint() );
1469
1470 auto footprintAttrIt = attributes.find( wxS( "Footprint" ) );
1471
1472 wxString resolvedFootprint;
1473
1474 if( footprintAttrIt != attributes.end() && !footprintAttrIt->second.first.value.IsEmpty() )
1475 resolvedFootprint = ResolveV3FootprintText( footprintAttrIt->second.first.value,
1476 aProject, aLibName );
1477 else
1478 resolvedFootprint = ResolveV3FootprintText( get_def( compAttrs, wxS( "Footprint" ), wxEmptyString ),
1479 aProject, aLibName );
1480
1481 if( !resolvedFootprint.IsEmpty() )
1482 schSym->SetFootprintFieldText( resolvedFootprint );
1483
1484 double x = EASYEDAPRO::V3GetDouble( componentRow->inner, "x" );
1485 double y = EASYEDAPRO::V3GetDouble( componentRow->inner, "y" );
1486 double rotation = EASYEDAPRO::V3GetDouble( componentRow->inner, "rotation", 0.0 );
1487 bool mirror = EASYEDAPRO::V3GetBool( componentRow->inner, "isMirror", false );
1488
1489 for( double i = rotation; i > 0; i -= 90 )
1490 schSym->Rotate( VECTOR2I(), true );
1491
1492 if( mirror )
1493 schSym->MirrorHorizontally( 0 );
1494
1495 schSym->SetPosition( V3ScalePos( VECTOR2D( x, y ) ) );
1496
1498 {
1499 wxString netName;
1500
1501 auto nameAttrIt = attributes.find( wxS( "Name" ) );
1502
1503 if( nameAttrIt != attributes.end() && !nameAttrIt->second.first.value.IsEmpty() )
1504 netName = nameAttrIt->second.first.value;
1505 else
1506 netName = get_def( compAttrs, wxS( "Name" ), wxEmptyString );
1507
1508 if( netName.IsEmpty() )
1509 netName = newLibSymbol.GetValueField().GetText();
1510
1511 netName = ResolveDeviceFieldVariables( netName, compAttrs );
1512
1513 std::vector<SCH_PIN*> pins = schSym->GetPins( &aSchematic->CurrentSheet() );
1514 SCH_PIN* pin = pins.empty() ? nullptr : pins.front();
1515
1516 VECTOR2I labelPos = schSym->GetPosition();
1517 SPIN_STYLE spinStyle = SPIN_STYLE::RIGHT;
1519
1520 if( pin )
1521 {
1522 labelPos = pin->GetPosition();
1523
1524 switch( pin->PinDrawOrient( schSym->GetTransform() ) )
1525 {
1526 case PIN_ORIENTATION::PIN_LEFT: spinStyle = SPIN_STYLE::LEFT; break;
1527 case PIN_ORIENTATION::PIN_RIGHT: spinStyle = SPIN_STYLE::RIGHT; break;
1528 case PIN_ORIENTATION::PIN_UP: spinStyle = SPIN_STYLE::UP; break;
1529 case PIN_ORIENTATION::PIN_DOWN: spinStyle = SPIN_STYLE::BOTTOM; break;
1530 default: break;
1531 }
1532
1533 switch( pin->GetType() )
1534 {
1536 labelShape = LABEL_FLAG_SHAPE::L_INPUT;
1537 break;
1538
1540 labelShape = LABEL_FLAG_SHAPE::L_OUTPUT;
1541 break;
1542
1544 labelShape = LABEL_FLAG_SHAPE::L_BIDI;
1545 break;
1546
1548 labelShape = LABEL_FLAG_SHAPE::L_TRISTATE;
1549 break;
1550
1551 default:
1553 break;
1554 }
1555 }
1556
1557 std::unique_ptr<SCH_GLOBALLABEL> label =
1558 std::make_unique<SCH_GLOBALLABEL>( labelPos, netName );
1559 label->SetSpinStyle( spinStyle );
1560 label->SetShape( labelShape );
1561
1562 bool labelVisible = !netName.IsEmpty();
1563
1564 if( nameAttrIt != attributes.end() && nameAttrIt->second.second )
1565 {
1566 const EASYEDAPRO::V3_ROW* attrRow = nameAttrIt->second.second;
1567
1568 labelVisible =
1569 EASYEDAPRO::V3GetBool( attrRow->inner, "valueVisible", true )
1570 && !netName.IsEmpty();
1571
1572 wxString color = V3GetString( attrRow->inner, "color", wxEmptyString );
1573 wxString fontFamily =
1574 V3GetString( attrRow->inner, "fontFamily", wxS( "default" ) );
1575
1576 if( !color.empty() && color.StartsWith( wxS( "#" ) ) )
1577 label->SetTextColor( COLOR4D( color ) );
1578
1579 if( fontFamily != wxS( "Arial" )
1580 && !fontFamily.IsSameAs( wxS( "default" ), false ) )
1581 {
1582 label->SetFont( KIFONT::FONT::GetFont( fontFamily ) );
1583 }
1584
1585 ApplyV3TextSizeIfDefined( label, attrRow->inner );
1586 }
1587
1588 label->SetVisible( labelVisible );
1589
1590 createdItems.push_back( std::move( label ) );
1591 continue;
1592 }
1593
1595 {
1596 SCH_FIELD* valueField = schSym->GetField( FIELD_T::VALUE );
1597
1598 auto globalNetNameAttrIt = attributes.find( "Global Net Name" );
1599 auto nameAttrIt = attributes.find( "Name" );
1600 wxString globalNetNameFromProject =
1601 get_def( compAttrs, "Global Net Name", wxEmptyString );
1602 wxString nameFromProject = get_def( compAttrs, "Name", wxEmptyString );
1603
1604 const std::pair<EASYEDAPRO::SCH_ATTR, const EASYEDAPRO::V3_ROW*>* valueSource = nullptr;
1605
1606 if( globalNetNameAttrIt != attributes.end() )
1607 valueSource = &globalNetNameAttrIt->second;
1608 else if( nameAttrIt != attributes.end() )
1609 valueSource = &nameAttrIt->second;
1610
1611 wxString globalNetName;
1612
1613 // 1. Pick from schematic attr
1614 // 2. Pick Name from schematic attr (used by +5V)
1615 // 3. Pick from project.json
1616 // 4. Pick Name from project.json
1617 // 5. Pick from symbol
1618 if( globalNetNameAttrIt != attributes.end()
1619 && !globalNetNameAttrIt->second.first.value.IsEmpty() )
1620 {
1621 globalNetName = globalNetNameAttrIt->second.first.value;
1622 }
1623 else if( nameAttrIt != attributes.end()
1624 && !nameAttrIt->second.first.value.IsEmpty() )
1625 {
1626 globalNetName = nameAttrIt->second.first.value;
1627 }
1628 else if( !globalNetNameFromProject.IsEmpty() )
1629 {
1630 globalNetName = globalNetNameFromProject;
1631 }
1632 else if( !nameFromProject.IsEmpty() )
1633 {
1634 globalNetName = nameFromProject;
1635 }
1636 else
1637 {
1638 globalNetName = newLibSymbol.GetValueField().GetText();
1639 }
1640
1641 globalNetName = ResolveDeviceFieldVariables( globalNetName, compAttrs );
1642
1643 if( valueSource && valueSource->second )
1644 {
1645 EASYEDAPRO::SCH_ATTR attr = valueSource->first;
1646 const EASYEDAPRO::V3_ROW* attrRow = valueSource->second;
1647 bool valueVisibleMissing = EASYEDAPRO::V3IsNullOrMissing( attrRow->inner,
1648 "valueVisible" );
1649 bool valueFromNameAttr = ( nameAttrIt != attributes.end()
1650 && valueSource == &nameAttrIt->second );
1651
1652 int attrRotation = EASYEDAPRO::V3GetInt( attrRow->inner, "rotation", 0 ) % 360;
1653
1654 if( attrRotation < 0 )
1655 attrRotation += 360;
1656
1657 bool hasExplicitStyleOrPosition =
1658 !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "x" )
1659 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "y" )
1660 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "align" )
1661 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "fontSize" )
1662 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "fontFamily" )
1663 || !EASYEDAPRO::V3IsNullOrMissing( attrRow->inner, "color" )
1664 || attrRotation != 0;
1665
1666 attr.value = globalNetName;
1667
1668 if( !globalNetName.IsEmpty() )
1669 attr.valVisible = true;
1670 else if( valueVisibleMissing )
1671 attr.valVisible = false;
1672
1673 if( valueFromNameAttr && !hasExplicitStyleOrPosition )
1674 {
1675 valueField->SetText( ResolveDeviceFieldVariables( globalNetName, compAttrs ) );
1676
1677 if( !globalNetName.IsEmpty() )
1678 valueField->SetVisible( true );
1679 else if( valueVisibleMissing )
1680 valueField->SetVisible( false );
1681 }
1682 else
1683 {
1684 ApplyV3AttrToField( schSym->GetField( FIELD_T::VALUE ), attr, false, true,
1685 compAttrs, schSym.get(), attrRow->inner );
1686
1687 if( !globalNetName.IsEmpty() )
1688 valueField->SetVisible( true );
1689 }
1690 }
1691 else
1692 {
1693 valueField->SetText( ResolveDeviceFieldVariables( globalNetName, compAttrs ) );
1694 valueField->SetVisible( !globalNetName.IsEmpty() );
1695 }
1696
1697 for( SCH_PIN* pin : schSym->GetAllLibPins() )
1698 pin->SetName( globalNetName );
1699
1700 schSym->SetRef( &aSchematic->CurrentSheet(), wxS( "#PWR?" ) );
1701 schSym->GetField( FIELD_T::REFERENCE )->SetVisible( false );
1702 }
1703 else
1704 {
1705 // Regular component
1706 auto designatorAttrIt = attributes.find( "Designator" );
1707 SCH_FIELD* refField = schSym->GetField( FIELD_T::REFERENCE );
1708 bool hasReferenceValue = false;
1709
1710 if( designatorAttrIt != attributes.end() )
1711 {
1712 const EASYEDAPRO::SCH_ATTR& attr = designatorAttrIt->second.first;
1713 const EASYEDAPRO::V3_ROW* attrRow = designatorAttrIt->second.second;
1714
1715 ApplyV3AttrToField( refField, attr, false, true, compAttrs,
1716 schSym.get(), attrRow->inner );
1717
1718 if( !attr.value.IsEmpty() )
1719 {
1720 schSym->SetRef( &aSchematic->CurrentSheet(), attr.value );
1721 hasReferenceValue = true;
1722 }
1723 }
1724
1725 if( !hasReferenceValue )
1726 refField->SetVisible( false );
1727
1728 // Device BOM / identity fields live on the Device, not as schematic ATTR rows.
1729 // Apply them per-instance so shared symbol geometry stays device-agnostic.
1731 compAttrs, true,
1732 [&]( const wxString& attrKey, const wxString& value )
1733 {
1734 SCH_FIELD* field = schSym->FindFieldCaseInsensitive( attrKey );
1735
1736 if( !field )
1737 field = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, attrKey ) );
1738
1739 field->SetText( value );
1740 field->SetVisible( false );
1741 } );
1742
1743 wxString description = get_def( compAttrs, wxS( "Description" ), wxEmptyString );
1744
1745 if( !description.empty() )
1746 {
1747 schSym->GetField( FIELD_T::DESCRIPTION )
1748 ->SetText( NormalizeEasyEDAText( ResolveDeviceFieldVariables( description, compAttrs ) ) );
1749 }
1750
1751 auto valueAttrIt = attributes.find( "Value" );
1752 auto nameAttrIt = attributes.find( "Name" );
1753
1754 wxString fallbackValue = get_def( compAttrs, wxS( "Value" ), wxEmptyString );
1755
1756 if( fallbackValue.IsEmpty() )
1757 fallbackValue = get_def( compAttrs, wxS( "Name" ), wxEmptyString );
1758
1759 const std::pair<EASYEDAPRO::SCH_ATTR, const EASYEDAPRO::V3_ROW*>* valueSource = nullptr;
1760
1761 if( valueAttrIt != attributes.end() && valueAttrIt->second.first.valVisible )
1762 valueSource = &valueAttrIt->second;
1763 else if( nameAttrIt != attributes.end() && nameAttrIt->second.first.valVisible )
1764 valueSource = &nameAttrIt->second;
1765 else if( valueAttrIt != attributes.end() )
1766 valueSource = &valueAttrIt->second;
1767 else if( nameAttrIt != attributes.end() )
1768 valueSource = &nameAttrIt->second;
1769
1770 wxString valueText;
1771
1772 if( valueAttrIt != attributes.end() && !valueAttrIt->second.first.value.IsEmpty() )
1773 valueText = valueAttrIt->second.first.value;
1774 else if( nameAttrIt != attributes.end() && !nameAttrIt->second.first.value.IsEmpty() )
1775 valueText = nameAttrIt->second.first.value;
1776 else
1777 valueText = fallbackValue;
1778
1779 SCH_FIELD* valueField = schSym->GetField( FIELD_T::VALUE );
1780
1781 if( !valueText.IsEmpty() )
1782 {
1783 if( valueSource && valueSource->second )
1784 {
1785 EASYEDAPRO::SCH_ATTR attr = valueSource->first;
1786 attr.value = valueText;
1787
1788 ApplyV3AttrToField( valueField, attr, false, true, compAttrs,
1789 schSym.get(), valueSource->second->inner );
1790 }
1791 else
1792 {
1793 valueField->SetText( ResolveDeviceFieldVariables( valueText, compAttrs ) );
1794 }
1795 }
1796 else
1797 {
1798 valueField->SetVisible( false );
1799 }
1800
1801 // Apply other visible attributes as fields
1802 for( const auto& [key, attrPair] : attributes )
1803 {
1804 if( key == wxS( "Designator" ) || key == wxS( "Device" ) || key == wxS( "Symbol" )
1805 || key == wxS( "Name" ) || key == wxS( "Value" ) )
1806 {
1807 continue;
1808 }
1809
1810 const EASYEDAPRO::SCH_ATTR& attr = attrPair.first;
1811 const EASYEDAPRO::V3_ROW* attrRow = attrPair.second;
1812
1813 if( !attr.keyVisible && !attr.valVisible )
1814 continue;
1815
1816 SCH_FIELD* field = nullptr;
1817
1818 if( key == wxS( "Footprint" ) )
1819 {
1820 field = schSym->GetField( FIELD_T::FOOTPRINT );
1821 }
1822 else
1823 {
1824 field = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, key ) );
1825 }
1826
1827 if( field )
1828 {
1829 if( key == wxS( "Footprint" ) )
1830 {
1831 EASYEDAPRO::SCH_ATTR footprintAttr = attr;
1832 footprintAttr.value = ResolveV3FootprintText( attr.value, aProject,
1833 aLibName );
1834
1835 ApplyV3AttrToField( field, footprintAttr, false, true,
1836 compAttrs, schSym.get(), attrRow->inner );
1837 }
1838 else
1839 {
1840 ApplyV3AttrToField( field, attr, false, true, compAttrs,
1841 schSym.get(), attrRow->inner );
1842 }
1843 }
1844 }
1845 }
1846
1847 createdItems.push_back( std::move( schSym ) );
1848 }
1849
1850 BOX2I sheetBBox;
1851 bool hasBBox = false;
1852
1853 for( std::unique_ptr<SCH_ITEM>& item : createdItems )
1854 {
1855 BOX2I itemBBox;
1856
1857 if( item->Type() == SCH_SYMBOL_T )
1858 itemBBox = static_cast<SCH_SYMBOL*>( item.get() )->GetBodyAndPinsBoundingBox();
1859 else
1860 itemBBox = item->GetBoundingBox();
1861
1862 if( !hasBBox )
1863 {
1864 sheetBBox = itemBBox;
1865 hasBBox = true;
1866 }
1867 else
1868 {
1869 sheetBBox.Merge( itemBBox );
1870 }
1871 }
1872
1873 SCH_SCREEN* screen = aRootSheet->GetScreen();
1874
1875 if( hasBBox )
1876 {
1877 const int margin = schIUScale.MilsToIU( 200 );
1878
1879 VECTOR2I offset( -sheetBBox.GetLeft() + margin,
1880 -sheetBBox.GetTop() + margin );
1881
1882 int alignGrid = schIUScale.MilsToIU( 50 );
1883
1884 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1885 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1886
1887 PAGE_INFO pageInfo = screen->GetPageSettings();
1888 pageInfo.SetWidthMils( schIUScale.IUToMils( sheetBBox.GetWidth() + 2 * margin ) );
1889 pageInfo.SetHeightMils( schIUScale.IUToMils( sheetBBox.GetHeight() + 2 * margin ) );
1890
1891 screen->SetPageSettings( pageInfo );
1892
1893 for( std::unique_ptr<SCH_ITEM>& item : createdItems )
1894 {
1895 item->Move( offset );
1896 screen->Append( item.release() );
1897 }
1898 }
1899 else
1900 {
1901 for( std::unique_ptr<SCH_ITEM>& item : createdItems )
1902 screen->Append( item.release() );
1903 }
1904}
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)
const wxChar *const traceEasyEdaIo
@ 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
wxLogTrace helper definitions.
@ 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