KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_easyedapro_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 (C) 2023 Alex Shvartzkop <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
23
24#include <core/map_helpers.h>
25
26#include <sch_io/sch_io_mgr.h>
27#include <schematic.h>
28#include <sch_sheet.h>
29#include <sch_line.h>
30#include <sch_bitmap.h>
31#include <sch_no_connect.h>
32#include <sch_label.h>
33#include <sch_junction.h>
34#include <sch_edit_frame.h>
35#include <sch_shape.h>
36#include <string_utils.h>
37#include <bezier_curves.h>
38#include <wx/base64.h>
39#include <wx/log.h>
40#include <wx/url.h>
41#include <wx/mstream.h>
42#include <gfx_import_utils.h>
46
47
48// clang-format off
49static const std::vector<wxString> c_attributesWhitelist = { "Value",
50 "Datasheet",
51 "Manufacturer Part",
52 "Manufacturer",
53 "BOM_Manufacturer Part",
54 "BOM_Manufacturer",
55 "Supplier Part",
56 "Supplier",
57 "BOM_Supplier Part",
58 "BOM_Supplier",
59 "LCSC Part Name" };
60// clang-format on
61
62
64 PROGRESS_REPORTER* aProgressReporter )
65{
66 m_schematic = aSchematic;
67}
68
69
73
74
75double SCH_EASYEDAPRO_PARSER::Convert( wxString aValue )
76{
77 double value = 0;
78
79 if( !aValue.ToCDouble( &value ) )
80 THROW_IO_ERROR( wxString::Format( _( "Failed to parse value: '%s'" ), aValue ) );
81
82 return value;
83}
84
85
86double SCH_EASYEDAPRO_PARSER::SizeToKi( wxString aValue )
87{
88 return ScaleSize( Convert( aValue ) );
89}
90
91
92static LINE_STYLE ConvertStrokeStyle( int aStyle )
93{
94 if( aStyle == 0 )
95 return LINE_STYLE::SOLID;
96 else if( aStyle == 1 )
97 return LINE_STYLE::DASH;
98 else if( aStyle == 2 )
99 return LINE_STYLE::DOT;
100 else if( aStyle == 3 )
101 return LINE_STYLE::DASHDOT;
102
103 return LINE_STYLE::DEFAULT;
104}
105
106
107template <typename T>
108void SCH_EASYEDAPRO_PARSER::ApplyFontStyle( const std::map<wxString, nlohmann::json>& fontStyles,
109 T& text, const wxString& styleStr )
110{
111 auto it = fontStyles.find( styleStr );
112
113 if( it == fontStyles.end() )
114 return;
115
116 nlohmann::json style = it->second;
117
118 if( !style.is_array() )
119 return;
120
121 if( style.size() < 12 )
122 return;
123
124 if( style.at( 3 ).is_string() )
125 {
126 COLOR4D color( style.at( 3 ).get<wxString>() );
127 text->SetTextColor( color );
128 }
129
130 if( style.at( 4 ).is_string() )
131 {
132 wxString fontname = ( style.at( 4 ) );
133
134 // JLCEDA Pro V3 export to format version V1 specifies Arial explicitly instead of null for default font
135 if( fontname != wxS( "Arial" ) && !fontname.IsSameAs( wxS( "default" ), false ) )
136 text->SetFont( KIFONT::FONT::GetFont( fontname ) );
137 }
138
139 if( style.at( 5 ).is_number() )
140 {
141 double size = style.at( 5 ).get<double>() * 0.62;
142 text->SetTextSize( VECTOR2I( ScaleSize( size ), ScaleSize( size ) ) );
143 }
144
145 if( style.at( 10 ).is_number() )
146 {
147 int valign = style.at( 10 );
148
149 if( !text->GetText().Contains( wxS( "\n" ) ) )
150 {
151 if( valign == 0 )
152 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
153 else if( valign == 1 )
154 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
155 else if( valign == 2 )
156 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
157 }
158 else
159 {
160 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
161 // TODO: align by first line
162 }
163 }
164 else
165 {
166 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
167 }
168
169 if( style.at( 11 ).is_number() )
170 {
171 int halign = style.at( 11 );
172
173 if( halign == 0 )
174 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
175 else if( halign == 1 )
176 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
177 else if( halign == 2 )
178 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
179 }
180 else
181 {
182 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
183 }
184}
185
186
187template <typename T>
188void SCH_EASYEDAPRO_PARSER::ApplyLineStyle( const std::map<wxString, nlohmann::json>& lineStyles,
189 T& shape, const wxString& styleStr )
190{
191 auto it = lineStyles.find( styleStr );
192
193 if( it == lineStyles.end() )
194 return;
195
196 nlohmann::json style = it->second;
197
198 if( !style.is_array() )
199 return;
200
201 if( style.size() < 6 )
202 return;
203
204 STROKE_PARAMS stroke = shape->GetStroke();
205
206 if( style.at( 2 ).is_string() )
207 {
208 wxString colorStr = style.at( 2 ).get<wxString>();
209
210 if( !colorStr.empty() && colorStr.starts_with( wxS( "#" ) ) )
211 {
212 COLOR4D color( colorStr );
213 stroke.SetColor( color );
214 }
215 }
216
217 if( style.at( 3 ).is_number() )
218 {
219 int dashStyle = style.at( 3 );
220 stroke.SetLineStyle( ConvertStrokeStyle( dashStyle ) );
221 }
222
223 if( style.at( 5 ).is_number() )
224 {
225 double thickness = style.at( 5 );
226 stroke.SetWidth( ScaleSize( thickness ) );
227 }
228
229 shape->SetStroke( stroke );
230}
231
232
234 const wxString aInput, const std::map<wxString, wxString>& aDeviceAttributes )
235{
236 wxString inputText = aInput;
237 wxString resolvedText;
238 int variableCount = 0;
239
240 // Resolve variables
241 // ={Variable1}text{Variable2}
242 do
243 {
244 if( !inputText.StartsWith( wxS( "={" ) ) )
245 return inputText;
246
247 resolvedText.Clear();
248 variableCount = 0;
249
250 for( size_t i = 1; i < inputText.size(); )
251 {
252 wxUniChar c = inputText[i++];
253
254 if( c == '{' )
255 {
256 wxString varName;
257 bool endFound = false;
258
259 while( i < inputText.size() )
260 {
261 c = inputText[i++];
262
263 if( c == '}' )
264 {
265 endFound = true;
266 break;
267 }
268
269 varName << c;
270 }
271
272 if( !endFound )
273 return inputText;
274
275 wxString varValue =
276 get_def( aDeviceAttributes, varName, wxString::Format( "{%s!}", varName ) );
277
278 resolvedText << varValue;
279 variableCount++;
280 }
281 else
282 {
283 resolvedText << c;
284 }
285 }
286 inputText = resolvedText;
287
288 } while( variableCount > 0 );
289
290 return resolvedText;
291}
292
293
294template <typename T>
295void SCH_EASYEDAPRO_PARSER::ApplyAttrToField( const std::map<wxString, nlohmann::json>& fontStyles,
296 T* field, const EASYEDAPRO::SCH_ATTR& aAttr,
297 bool aIsSym, bool aToSym,
298 const std::map<wxString, wxString>& aDeviceAttributes,
299 SCH_SYMBOL* aParent )
300{
301 EDA_TEXT* text = static_cast<EDA_TEXT*>( field );
302
303 text->SetText( ResolveFieldVariables( aAttr.value, aDeviceAttributes ) );
304 text->SetVisible( aAttr.keyVisible || aAttr.valVisible );
305
306 field->SetNameShown( aAttr.keyVisible );
307
308 if( aAttr.position )
309 {
310 field->SetPosition( !aIsSym ? ScalePos( *aAttr.position )
311 : ScalePosSym( *aAttr.position ) );
312 }
313
314 ApplyFontStyle( fontStyles, text, aAttr.fontStyle );
315
316 auto parent = aParent;
317 if( parent && parent->Type() == SCH_SYMBOL_T )
318 {
319 int orient = static_cast<SCH_SYMBOL*>( parent )->GetOrientation();
320
321 if( orient == SYM_ORIENT_180 )
322 {
323 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
324 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
325 }
326 else if( orient == SYM_MIRROR_X + SYM_ORIENT_0 )
327 {
328 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
329 }
330 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_0 )
331 {
332 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
333 }
334 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_180 )
335 {
336 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( text->GetHorizJustify() ) );
337 }
338 else if( orient == SYM_ORIENT_90 )
339 {
340 text->SetTextAngle( ANGLE_VERTICAL );
341 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
342 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
343 }
344 if( orient == SYM_ORIENT_270 )
345 {
346 text->SetTextAngle( ANGLE_VERTICAL );
347 }
348 else if( orient == SYM_MIRROR_X + SYM_ORIENT_90 )
349 {
350 text->SetTextAngle( ANGLE_VERTICAL );
351 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
352 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
353 }
354 else if( orient == SYM_MIRROR_X + SYM_ORIENT_270 )
355 {
356 text->SetTextAngle( ANGLE_VERTICAL );
357 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
358 }
359 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_90 )
360 {
361 text->SetTextAngle( ANGLE_VERTICAL );
362 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
363 }
364 else if( orient == SYM_MIRROR_Y + SYM_ORIENT_270 )
365 {
366 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( text->GetHorizJustify() ) );
367 }
368
369 if( aAttr.rotation == 90 )
370 {
371 if( text->GetTextAngle() == ANGLE_HORIZONTAL )
372 text->SetTextAngle( ANGLE_VERTICAL );
373 else
374 text->SetTextAngle( ANGLE_HORIZONTAL );
375
376 if( orient == SYM_ORIENT_90 )
377 {
378 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
379 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
380 }
381 if( orient == SYM_ORIENT_270 )
382 {
383 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
384 text->SetHorizJustify( static_cast<GR_TEXT_H_ALIGN_T>( -text->GetHorizJustify() ) );
385 }
386 else if( orient == SYM_MIRROR_X + SYM_ORIENT_90 )
387 {
388 text->SetVertJustify( static_cast<GR_TEXT_V_ALIGN_T>( -text->GetVertJustify() ) );
389 }
390 }
391 }
392}
393
394
396SCH_EASYEDAPRO_PARSER::ParseSymbol( const std::vector<nlohmann::json>& aLines,
397 const std::map<wxString, wxString>& aDeviceAttributes )
398{
399 EASYEDAPRO::SYM_INFO symInfo;
400
401 std::unique_ptr<LIB_SYMBOL> ksymbolPtr = std::make_unique<LIB_SYMBOL>( wxEmptyString );
402 LIB_SYMBOL* ksymbol = ksymbolPtr.get();
403
404 std::map<wxString, nlohmann::json> lineStyles;
405 std::map<wxString, nlohmann::json> fontStyles;
406 std::map<wxString, int> partUnits;
407
408 std::map<int, std::map<wxString, EASYEDAPRO::SCH_ATTR>> unitAttributes;
409 std::map<int, std::map<wxString, std::vector<nlohmann::json>>> unitParentedLines;
410
411 int totalUnits = 0;
412
413 for( const nlohmann::json& line : aLines )
414 {
415 wxString type = line.at( 0 );
416
417 if( type == wxS( "LINESTYLE" ) )
418 lineStyles[line.at( 1 )] = line;
419 else if( type == wxS( "FONTSTYLE" ) )
420 fontStyles[line.at( 1 )] = line;
421 else if( type == wxS( "PART" ) )
422 partUnits[line.at( 1 )] = ++totalUnits;
423 }
424
425 symInfo.partUnits = partUnits;
426 ksymbol->SetUnitCount( totalUnits, false );
427
428 int currentUnit = 1;
429
430 for( const nlohmann::json& line : aLines )
431 {
432 wxString type = line.at( 0 );
433
434 if( type == wxS( "PART" ) )
435 {
436 currentUnit = partUnits.at( line.at( 1 ) );
437 }
438 else if( type == wxS( "RECT" ) )
439 {
440 VECTOR2D start( line.at( 2 ), line.at( 3 ) );
441 VECTOR2D end( line.at( 4 ), line.at( 5 ) );
442 wxString styleStr = line.at( 9 );
443
444 auto rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
445
446 rect->SetStart( ScalePosSym( start ) );
447 rect->SetEnd( ScalePosSym( end ) );
448
449 rect->SetUnit( currentUnit );
450 ApplyLineStyle( lineStyles, rect, styleStr );
451
452 ksymbol->AddDrawItem( rect.release() );
453 }
454 else if( type == wxS( "CIRCLE" ) )
455 {
456 VECTOR2D center( line.at( 2 ), line.at( 3 ) );
457 double radius = line.at( 4 );
458 wxString styleStr = line.at( 5 );
459
460 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
461
462 circle->SetCenter( ScalePosSym( center ) );
463 circle->SetEnd( circle->GetCenter() + VECTOR2I( ScaleSize( radius ), 0 ) );
464
465 circle->SetUnit( currentUnit );
466 ApplyLineStyle( lineStyles, circle, styleStr );
467
468 ksymbol->AddDrawItem( circle.release() );
469 }
470 else if( type == wxS( "ARC" ) )
471 {
472 VECTOR2D start( line.at( 2 ), line.at( 3 ) );
473 VECTOR2D mid( line.at( 4 ), line.at( 5 ) );
474 VECTOR2D end( line.at( 6 ), line.at( 7 ) );
475 wxString styleStr = line.at( 8 );
476
477 VECTOR2D kstart = ScalePosSym( start );
478 VECTOR2D kmid = ScalePosSym( mid );
479 VECTOR2D kend = ScalePosSym( end );
480
481 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
482
483 shape->SetArcGeometry( kstart, kmid, kend );
484
485 shape->SetUnit( currentUnit );
486 ApplyLineStyle( lineStyles, shape, styleStr );
487
488 ksymbol->AddDrawItem( shape.release() );
489 }
490 else if( type == wxS( "BEZIER" ) )
491 {
492 std::vector<double> points = line.at( 2 );
493 wxString styleStr = line.at( 3 );
494
495 std::unique_ptr<SCH_SHAPE> shape =
496 std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
497
498 for( size_t i = 1; i < points.size(); i += 2 )
499 {
500 VECTOR2I pt = ScalePosSym( VECTOR2D( points[i - 1], points[i] ) );
501
502 switch( i )
503 {
504 case 1: shape->SetStart( pt ); break;
505 case 3: shape->SetBezierC1( pt ); break;
506 case 5: shape->SetBezierC2( pt ); break;
507 case 7: shape->SetEnd( pt ); break;
508 }
509 }
510
511 shape->SetUnit( currentUnit );
512 ApplyLineStyle( lineStyles, shape, styleStr );
513
514 ksymbol->AddDrawItem( shape.release() );
515 }
516 else if( type == wxS( "POLY" ) )
517 {
518 std::vector<double> points = line.at( 2 );
519 wxString styleStr = line.at( 4 );
520
521 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
522
523 for( size_t i = 1; i < points.size(); i += 2 )
524 shape->AddPoint( ScalePosSym( VECTOR2D( points[i - 1], points[i] ) ) );
525
526 shape->SetUnit( currentUnit );
527 ApplyLineStyle( lineStyles, shape, styleStr );
528
529 ksymbol->AddDrawItem( shape.release() );
530 }
531 else if( type == wxS( "TEXT" ) )
532 {
533 VECTOR2D pos( line.at( 2 ), line.at( 3 ) );
534 double angle = line.at( 4 ).is_number() ? line.at( 4 ).get<double>() : 0.0;
535 wxString textStr = line.at( 5 );
536 wxString fontStyleStr = line.at( 6 );
537
538 auto text = std::make_unique<SCH_TEXT>( ScalePosSym( pos ), UnescapeHTML( textStr ),
539 LAYER_DEVICE );
540
541 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
542 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
543 text->SetTextAngleDegrees( angle );
544
545 text->SetUnit( currentUnit );
546 ApplyFontStyle( fontStyles, text, fontStyleStr );
547
548 ksymbol->AddDrawItem( text.release() );
549 }
550 else if( type == wxS( "OBJ" ) )
551 {
552 VECTOR2D start, size;
553 wxString mimeType, data;
554 //double angle = 0;
555 int upsideDown = 0;
556
557 if( line.at( 3 ).is_number() )
558 {
559 start = VECTOR2D( line.at( 3 ), line.at( 4 ) );
560 size = VECTOR2D( line.at( 5 ), line.at( 6 ) );
561 //angle = line.at( 7 );
562 upsideDown = line.at( 8 );
563
564 wxString imageUrl = line.at( 9 );
565
566 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
567 {
568 wxArrayString paramsArr =
569 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
570
571 data = imageUrl.AfterFirst( ',' );
572
573 if( paramsArr.size() > 0 )
574 {
575 mimeType = paramsArr[0];
576 }
577 }
578 }
579 else if( line.at( 3 ).is_string() )
580 {
581 mimeType = line.at( 3 ).get<wxString>().BeforeFirst( ';' );
582
583 start = VECTOR2D( line.at( 4 ), line.at( 5 ) );
584 size = VECTOR2D( line.at( 6 ), line.at( 7 ) );
585 //angle = line.at( 8 );
586 data = line.at( 9 ).get<wxString>();
587 }
588
589 if( mimeType.empty() || data.empty() )
590 continue;
591
592 wxMemoryBuffer buf = wxBase64Decode( data );
593
594 if( mimeType == wxS( "image/svg+xml" ) )
595 {
596 VECTOR2D offset = ScalePosSym( start );
597
598 SVG_IMPORT_PLUGIN svgImportPlugin;
599 GRAPHICS_IMPORTER_LIB_SYMBOL libsymImporter( ksymbol, 0 );
600
601 svgImportPlugin.SetImporter( &libsymImporter );
602 svgImportPlugin.LoadFromMemory( buf );
603
604 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
605 svgImportPlugin.GetImageHeight() );
606
607 VECTOR2D pixelScale( schIUScale.IUTomm( ScaleSize( size.x ) ) / imSize.x,
608 schIUScale.IUTomm( ScaleSize( size.y ) ) / imSize.y );
609
610 if( upsideDown )
611 pixelScale.y *= -1;
612
613 libsymImporter.SetScale( pixelScale );
614
615 VECTOR2D offsetMM( schIUScale.IUTomm( offset.x ), schIUScale.IUTomm( offset.y ) );
616
617 libsymImporter.SetImportOffsetMM( offsetMM );
618
619 svgImportPlugin.Import();
620
621 // TODO: rotation
622 for( std::unique_ptr<EDA_ITEM>& item : libsymImporter.GetItems() )
623 ksymbol->AddDrawItem( static_cast<SCH_ITEM*>( item.release() ) );
624 }
625 else
626 {
627 wxMemoryInputStream memis( buf.GetData(), buf.GetDataLen() );
628
629 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
630 & ~wxImage::Load_Verbose );
631 wxImage img;
632 if( img.LoadFile( memis, mimeType ) )
633 {
634 int dimMul = img.GetWidth() * img.GetHeight();
635 double maxPixels = 30000;
636
637 if( dimMul > maxPixels )
638 {
639 double scale = sqrt( maxPixels / dimMul );
640 img.Rescale( img.GetWidth() * scale, img.GetHeight() * scale );
641 }
642
643 VECTOR2D pixelScale( ScaleSize( size.x ) / img.GetWidth(),
644 ScaleSize( size.y ) / img.GetHeight() );
645
646 // TODO: rotation
647 ConvertImageToLibShapes( ksymbol, 0, img, pixelScale, ScalePosSym( start ) );
648 }
649 }
650 }
651 else if( type == wxS( "HEAD" ) )
652 {
653 symInfo.head = line;
654 }
655 else if( type == wxS( "PIN" ) )
656 {
657 wxString pinId = line.at( 1 );
658 unitParentedLines[currentUnit][pinId].push_back( line );
659 }
660 else if( type == wxS( "ATTR" ) )
661 {
662 wxString parentId = line.at( 2 );
663
664 if( parentId.empty() )
665 {
666 EASYEDAPRO::SCH_ATTR attr = line;
667 unitAttributes[currentUnit].emplace( attr.key, attr );
668 }
669 else
670 {
671 unitParentedLines[currentUnit][parentId].push_back( line );
672 }
673 }
674 }
675
678 {
679 ksymbol->SetGlobalPower();
680 ksymbol->GetReferenceField().SetText( wxS( "#PWR" ) );
681 ksymbol->GetReferenceField().SetVisible( false );
682 ksymbol->SetKeyWords( wxS( "power-flag" ) );
683 ksymbol->SetShowPinNames( false );
684 ksymbol->SetShowPinNumbers( false );
685
686 if( auto globalNetAttr = get_opt( unitAttributes[1], wxS( "Global Net Name" ) ) )
687 {
688 ApplyAttrToField( fontStyles, &ksymbol->GetValueField(), *globalNetAttr, true, true );
689
690 wxString globalNetname = globalNetAttr->value;
691
692 if( !globalNetname.empty() )
693 {
694 ksymbol->SetDescription( wxString::Format(
695 _( "Power symbol creates a global label with name '%s'" ),
696 globalNetname ) );
697 }
698 }
699 }
700 else
701 {
702 auto designatorAttr = get_opt( unitAttributes[1], wxS( "Designator" ) );
703
704 if( designatorAttr && !designatorAttr->value.empty() )
705 {
706 wxString symbolPrefix = designatorAttr->value;
707
708 if( symbolPrefix.EndsWith( wxS( "?" ) ) )
709 symbolPrefix.RemoveLast();
710
711 ksymbol->GetReferenceField().SetText( symbolPrefix );
712 }
713
714 for( const wxString& attrName : c_attributesWhitelist )
715 {
716 if( auto valOpt = get_opt( aDeviceAttributes, attrName ) )
717 {
718 if( valOpt->empty() )
719 continue;
720
721 SCH_FIELD* fd = ksymbol->FindFieldCaseInsensitive( attrName );
722
723 if( !fd )
724 {
725 fd = new SCH_FIELD( ksymbol, FIELD_T::USER, attrName );
726 ksymbol->AddField( fd );
727 }
728
729 wxString value = *valOpt;
730
731 value.Replace( wxS( "\u2103" ), wxS( "\u00B0C" ), true ); // ℃ -> °C
732
733 fd->SetText( value );
734 fd->SetVisible( false );
735 }
736 }
737 }
738
739 for( auto& [unitId, parentedLines] : unitParentedLines )
740 {
741 for( auto& [pinId, lines] : parentedLines )
742 {
743 std::optional<EASYEDAPRO::SYM_PIN> epin;
744 std::map<wxString, EASYEDAPRO::SCH_ATTR> pinAttributes;
745
746 for( const nlohmann::json& line : lines )
747 {
748 wxString type = line.at( 0 );
749
750 if( type == wxS( "ATTR" ) )
751 {
752 EASYEDAPRO::SCH_ATTR attr = line;
753 pinAttributes.emplace( attr.key, attr );
754 }
755 else if( type == wxS( "PIN" ) )
756 {
757 epin = line;
758 }
759 }
760
761 if( !epin )
762 continue;
763
764 EASYEDAPRO::PIN_INFO pinInfo;
765 pinInfo.pin = *epin;
766
767 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( ksymbol );
768
769 pin->SetUnit( unitId );
770
771 pin->SetLength( ScaleSize( epin->length ) );
772 pin->SetPosition( ScalePosSym( epin->position ) );
773
775
776 if( epin->rotation == 0 )
778 if( epin->rotation == 90 )
780 if( epin->rotation == 180 )
782 if( epin->rotation == 270 )
784
785 pin->SetOrientation( orient );
786
788 {
789 pin->SetName( ksymbol->GetName() );
790 //pin->SetVisible( false );
791 }
792 else
793 {
794 auto pinNameAttr = get_opt( pinAttributes, "Pin Name" ); // JLCEDA V3
795
796 if( !pinNameAttr )
797 pinNameAttr = get_opt( pinAttributes, "NAME" ); // EasyEDA V2
798
799 if( pinNameAttr )
800 {
801 pin->SetName( pinNameAttr->value );
802 pinInfo.name = pinNameAttr->value;
803
804 if( !pinNameAttr->valVisible )
805 pin->SetNameTextSize( schIUScale.MilsToIU( 1 ) );
806 }
807 }
808
809 auto pinNumAttr = get_opt( pinAttributes, "Pin Number" ); // JLCEDA V3
810
811 if( !pinNumAttr )
812 pinNumAttr = get_opt( pinAttributes, "NUMBER" ); // EasyEDA V2
813
814 if( pinNumAttr )
815 {
816 pin->SetNumber( pinNumAttr->value );
817 pinInfo.number = pinNumAttr->value;
818
819 if( !pinNumAttr->valVisible )
820 pin->SetNumberTextSize( schIUScale.MilsToIU( 1 ) );
821 }
822
824 {
826 }
827 else if( auto pinTypeAttr = get_opt( pinAttributes, "Pin Type" ) )
828 {
829 if( pinTypeAttr->value == wxS( "IN" ) )
831 if( pinTypeAttr->value == wxS( "OUT" ) )
833 if( pinTypeAttr->value == wxS( "BI" ) )
835 }
836
837 if( get_opt( pinAttributes, "NO_CONNECT" ) )
838 pin->SetType( ELECTRICAL_PINTYPE::PT_NC );
839
840 if( pin->GetNumberTextSize() * int( pin->GetNumber().size() ) > pin->GetLength() )
841 pin->SetNumberTextSize( pin->GetLength() / pin->GetNumber().size() );
842
843 symInfo.pins.push_back( pinInfo );
844 ksymbol->AddDrawItem( pin.release() );
845 }
846 }
847
848 symInfo.symbolAttr = get_opt( unitAttributes[1], "Symbol" ); // TODO: per-unit
849
850 /*BOX2I bbox = ksymbol->GetBodyBoundingBox( 0, 0, true, true );
851 bbox.Inflate( schIUScale.MilsToIU( 10 ) );*/
852
853 /*ksymbol->GetReferenceField().SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
854 ksymbol->GetReferenceField().SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
855 ksymbol->GetReferenceField().SetPosition( VECTOR2I( bbox.GetCenter().x, -bbox.GetTop() ) );
856
857 ksymbol->GetValueField().SetVertJustify( GR_TEXT_V_ALIGN_TOP );
858 ksymbol->GetValueField().SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
859 ksymbol->GetValueField().SetPosition( VECTOR2I( bbox.GetCenter().x, -bbox.GetBottom() ) );*/
860
861 symInfo.libSymbol = std::move( ksymbolPtr );
862
863 return symInfo;
864}
865
866
868 const nlohmann::json& aProject,
869 std::map<wxString, EASYEDAPRO::SYM_INFO>& aSymbolMap,
870 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
871 const std::vector<nlohmann::json>& aLines,
872 const wxString& aLibName )
873{
874 std::vector<std::unique_ptr<SCH_ITEM>> createdItems;
875
876 std::map<wxString, std::vector<nlohmann::json>> parentedLines;
877 std::map<wxString, std::vector<nlohmann::json>> ruleLines;
878
879 std::map<wxString, nlohmann::json> lineStyles;
880 std::map<wxString, nlohmann::json> fontStyles;
881
882 for( const nlohmann::json& line : aLines )
883 {
884 wxString type = line.at( 0 );
885
886 if( type == wxS( "LINESTYLE" ) )
887 lineStyles[line.at( 1 )] = line;
888 else if( type == wxS( "FONTSTYLE" ) )
889 fontStyles[line.at( 1 )] = line;
890 }
891
892 for( const nlohmann::json& line : aLines )
893 {
894 wxString type = line.at( 0 );
895
896 if( type == wxS( "RECT" ) )
897 {
898 VECTOR2D start( line.at( 2 ), line.at( 3 ) );
899 VECTOR2D end( line.at( 4 ), line.at( 5 ) );
900 wxString styleStr = line.at( 9 );
901
902 std::unique_ptr<SCH_SHAPE> rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
903
904 rect->SetStart( ScalePos( start ) );
905 rect->SetEnd( ScalePos( end ) );
906
907 ApplyLineStyle( lineStyles, rect, styleStr );
908
909 createdItems.push_back( std::move( rect ) );
910 }
911 else if( type == wxS( "CIRCLE" ) )
912 {
913 VECTOR2D center( line.at( 2 ), line.at( 3 ) );
914 double radius = line.at( 4 );
915 wxString styleStr = line.at( 5 );
916
917 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
918
919 circle->SetCenter( ScalePos( center ) );
920 circle->SetEnd( circle->GetCenter() + VECTOR2I( ScaleSize( radius ), 0 ) );
921
922 ApplyLineStyle( lineStyles, circle, styleStr );
923
924 createdItems.push_back( std::move( circle ) );
925 }
926 else if( type == wxS( "POLY" ) )
927 {
928 std::vector<double> points = line.at( 2 );
929 wxString styleStr = line.at( 4 );
930
932
933 for( size_t i = 1; i < points.size(); i += 2 )
934 chain.Append( ScalePos( VECTOR2D( points[i - 1], points[i] ) ) );
935
936 for( int segId = 0; segId < chain.SegmentCount(); segId++ )
937 {
938 const SEG& seg = chain.CSegment( segId );
939
940 std::unique_ptr<SCH_LINE> schLine =
941 std::make_unique<SCH_LINE>( seg.A, LAYER_NOTES );
942 schLine->SetEndPoint( seg.B );
943
944 ApplyLineStyle( lineStyles, schLine, styleStr );
945
946 createdItems.push_back( std::move( schLine ) );
947 }
948 }
949 else if( type == wxS( "TEXT" ) )
950 {
951 VECTOR2D pos( line.at( 2 ), line.at( 3 ) );
952 double angle = line.at( 4 ).is_number() ? line.at( 4 ).get<double>() : 0.0;
953 wxString textStr = line.at( 5 );
954 wxString fontStyleStr = line.at( 6 );
955
956 std::unique_ptr<SCH_TEXT> text =
957 std::make_unique<SCH_TEXT>( ScalePos( pos ), UnescapeHTML( textStr ) );
958
959 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
960 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
961
962 text->SetTextAngleDegrees( angle );
963
964 ApplyFontStyle( fontStyles, text, fontStyleStr );
965
966 createdItems.push_back( std::move( text ) );
967 }
968 else if( type == wxS( "OBJ" ) )
969 {
970 VECTOR2D start, size;
971 wxString mimeType, base64Data;
972 double angle = 0;
973 int flipped = 0;
974
975 if( line.at( 3 ).is_number() )
976 {
977 start = VECTOR2D( line.at( 3 ), line.at( 4 ) );
978 size = VECTOR2D( line.at( 5 ), line.at( 6 ) );
979 angle = line.at( 7 );
980 flipped = line.at( 8 );
981
982 wxString imageUrl = line.at( 9 );
983
984 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
985 {
986 wxArrayString paramsArr =
987 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
988
989 base64Data = imageUrl.AfterFirst( ',' );
990
991 if( paramsArr.size() > 0 )
992 mimeType = paramsArr[0];
993 }
994 else if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
995 {
996 wxString objectId = imageUrl.AfterLast( ':' );
997
998 if( auto blob = get_opt( aBlobMap, objectId ) )
999 {
1000 wxString blobUrl = blob->url;
1001
1002 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1003 {
1004 wxArrayString paramsArr = wxSplit(
1005 blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1006
1007 base64Data = blobUrl.AfterFirst( ',' );
1008
1009 if( paramsArr.size() > 0 )
1010 mimeType = paramsArr[0];
1011 }
1012 }
1013 }
1014 }
1015 else if( line.at( 3 ).is_string() )
1016 {
1017 mimeType = line.at( 3 ).get<wxString>().BeforeFirst( ';' );
1018
1019 start = VECTOR2D( line.at( 4 ), line.at( 5 ) );
1020 size = VECTOR2D( line.at( 6 ), line.at( 7 ) );
1021 angle = line.at( 8 );
1022 base64Data = line.at( 9 ).get<wxString>();
1023 }
1024
1025 VECTOR2D kstart = ScalePos( start );
1026 VECTOR2D ksize = ScaleSize( size );
1027
1028 if( mimeType.empty() || base64Data.empty() )
1029 continue;
1030
1031 wxMemoryBuffer buf = wxBase64Decode( base64Data );
1032
1033 if( mimeType == wxS( "image/svg+xml" ) )
1034 {
1035 SVG_IMPORT_PLUGIN svgImportPlugin;
1036 GRAPHICS_IMPORTER_SCH schImporter;
1037
1038 svgImportPlugin.SetImporter( &schImporter );
1039 svgImportPlugin.LoadFromMemory( buf );
1040
1041 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
1042 svgImportPlugin.GetImageHeight() );
1043
1044 VECTOR2D pixelScale( schIUScale.IUTomm( ScaleSize( size.x ) ) / imSize.x,
1045 schIUScale.IUTomm( ScaleSize( size.y ) ) / imSize.y );
1046
1047 schImporter.SetScale( pixelScale );
1048
1049 VECTOR2D offsetMM( schIUScale.IUTomm( kstart.x ), schIUScale.IUTomm( kstart.y ) );
1050
1051 schImporter.SetImportOffsetMM( offsetMM );
1052
1053 svgImportPlugin.Import();
1054
1055 for( std::unique_ptr<EDA_ITEM>& item : schImporter.GetItems() )
1056 {
1057 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item.release() );
1058
1059 for( double i = angle; i > 0; i -= 90 )
1060 {
1061 if( schItem->Type() == SCH_LINE_T )
1062 {
1063 // Lines need special handling for some reason
1064 schItem->SetFlags( STARTPOINT );
1065 schItem->Rotate( kstart, false );
1066 schItem->ClearFlags( STARTPOINT );
1067
1068 schItem->SetFlags( ENDPOINT );
1069 schItem->Rotate( kstart, false );
1070 schItem->ClearFlags( ENDPOINT );
1071 }
1072 else
1073 {
1074 schItem->Rotate( kstart, false );
1075 }
1076 }
1077
1078 if( flipped )
1079 {
1080 // Lines need special handling for some reason
1081 if( schItem->Type() == SCH_LINE_T )
1082 schItem->SetFlags( STARTPOINT | ENDPOINT );
1083
1084 schItem->MirrorHorizontally( kstart.x );
1085
1086 if( schItem->Type() == SCH_LINE_T )
1087 schItem->ClearFlags( STARTPOINT | ENDPOINT );
1088 }
1089
1090 createdItems.emplace_back( schItem );
1091 }
1092 }
1093 else
1094 {
1095 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
1096 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1097
1098 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1099 & ~wxImage::Load_Verbose );
1100
1101 if( refImage.ReadImageFile( buf ) )
1102 {
1103 VECTOR2D kcenter = kstart + ksize / 2;
1104
1105 double scaleFactor = ScaleSize( size.x ) / refImage.GetSize().x;
1106 refImage.SetImageScale( scaleFactor );
1107 bitmap->SetPosition( kcenter );
1108
1109 for( double i = angle; i > 0; i -= 90 )
1110 bitmap->Rotate( kstart, false );
1111
1112 if( flipped )
1113 bitmap->MirrorHorizontally( kstart.x );
1114
1115 createdItems.push_back( std::move( bitmap ) );
1116 }
1117 }
1118 }
1119 if( type == wxS( "WIRE" ) )
1120 {
1121 wxString wireId = line.at( 1 );
1122 parentedLines[wireId].push_back( line );
1123 }
1124 else if( type == wxS( "COMPONENT" ) )
1125 {
1126 wxString compId = line.at( 1 );
1127 parentedLines[compId].push_back( line );
1128 }
1129 else if( type == wxS( "ATTR" ) )
1130 {
1131 wxString compId = line.at( 2 );
1132 parentedLines[compId].push_back( line );
1133 }
1134 }
1135
1136 for( auto& [parentId, lines] : parentedLines )
1137 {
1138 std::optional<EASYEDAPRO::SCH_COMPONENT> component;
1139 std::optional<EASYEDAPRO::SCH_WIRE> wire;
1140 std::map<wxString, EASYEDAPRO::SCH_ATTR> attributes;
1141
1142 for( const nlohmann::json& line : lines )
1143 {
1144 if( line.at( 0 ) == "COMPONENT" )
1145 {
1146 component = line;
1147 }
1148 else if( line.at( 0 ) == "WIRE" )
1149 {
1150 wire = line;
1151 }
1152 else if( line.at( 0 ) == "ATTR" )
1153 {
1154 EASYEDAPRO::SCH_ATTR attr = line;
1155 attributes.emplace( attr.key, attr );
1156 }
1157 }
1158
1159 if( component )
1160 {
1161 auto deviceAttr = get_opt( attributes, "Device" );
1162 auto symbolAttr = get_opt( attributes, "Symbol" );
1163
1164 if( !deviceAttr )
1165 continue;
1166
1167 const std::map<wxString, wxString> prjCompAttrs = EASYEDAPRO::AnyMapToStringMap(
1168 aProject.at( "devices" ).at( deviceAttr->value ).at( "attributes" ) );
1169
1170 // Merge attributes, giving priority to schematic attributes over project attributes
1171 std::map<wxString, wxString> mergedAttrValues;
1172
1173 for( const auto& [key, value] : prjCompAttrs )
1174 mergedAttrValues[key] = value;
1175
1176 for( const auto& [key, attr] : attributes )
1177 mergedAttrValues[key] = attr.value;
1178
1179 wxString symbolId;
1180
1181 if( symbolAttr && !symbolAttr->value.IsEmpty() )
1182 symbolId = symbolAttr->value;
1183 else
1184 symbolId = prjCompAttrs.at( "Symbol" );
1185
1186 auto it = aSymbolMap.find( symbolId );
1187 if( it == aSymbolMap.end() )
1188 {
1189 wxLogError( "Symbol of '%s' with uuid '%s' not found.", component->name, symbolId );
1190 continue;
1191 }
1192
1193 EASYEDAPRO::SYM_INFO& esymInfo = it->second;
1194 LIB_SYMBOL newLibSymbol = *esymInfo.libSymbol.get();
1195
1196 wxString unitName = component->name;
1197
1198 LIB_ID libId = EASYEDAPRO::ToKiCadLibID( aLibName,
1199 newLibSymbol.GetLibId().GetLibItemName() );
1200
1201 auto schSym = std::make_unique<SCH_SYMBOL>( newLibSymbol, libId,
1202 &aSchematic->CurrentSheet(),
1203 esymInfo.partUnits[unitName] );
1204
1205 schSym->SetFootprintFieldText( newLibSymbol.GetFootprint() );
1206
1207 for( double i = component->rotation; i > 0; i -= 90 )
1208 schSym->Rotate( VECTOR2I(), true );
1209
1210 if( component->mirror )
1211 schSym->MirrorHorizontally( 0 );
1212
1213 schSym->SetPosition( ScalePos( component->position ) );
1214
1216 {
1217 SCH_FIELD* valueField = schSym->GetField( FIELD_T::VALUE );
1218
1219 auto globalNetNameAttr = get_opt( attributes, "Global Net Name" );
1220 wxString globalNetNameFromProject = get_def( prjCompAttrs, "Global Net Name", wxEmptyString );
1221 wxString globalNetName;
1222
1223 // 1. Pick from schematic attr
1224 // 2. Pick from project.json
1225 // 3. Pick from symbol
1226 if( globalNetNameAttr && !globalNetNameAttr->value.IsEmpty() )
1227 {
1228 globalNetName = globalNetNameAttr->value;
1229
1230 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::VALUE ), *globalNetNameAttr, false, true,
1231 mergedAttrValues, schSym.get() );
1232 }
1233 else if( !globalNetNameFromProject.IsEmpty() )
1234 {
1235 globalNetName = globalNetNameFromProject;
1236
1237 valueField->SetText( ResolveFieldVariables( globalNetName, mergedAttrValues ) );
1238 }
1239 else
1240 {
1241 valueField->SetText( newLibSymbol.GetValueField().GetText() );
1242 }
1243
1244 for( SCH_PIN* pin : schSym->GetAllLibPins() )
1245 pin->SetName( globalNetName );
1246
1247 schSym->SetRef( &aSchematic->CurrentSheet(), wxS( "#PWR?" ) );
1248 schSym->GetField( FIELD_T::REFERENCE )->SetVisible( false );
1249 }
1250 else if( esymInfo.head.symbolType == EASYEDAPRO::SYMBOL_TYPE::NETPORT )
1251 {
1252 auto nameAttr = get_opt( attributes, "Name" );
1253
1254 wxString netName;
1255
1256 if( nameAttr && !nameAttr->value.IsEmpty() )
1257 netName = nameAttr->value;
1258 else
1259 netName = prjCompAttrs.at( "Name" );
1260
1261 std::unique_ptr<SCH_GLOBALLABEL> label = std::make_unique<SCH_GLOBALLABEL>(
1262 ScalePos( component->position ), netName );
1263
1264 std::vector<SCH_PIN*> pins = schSym->GetPins( &aSchematic->CurrentSheet() );
1265
1266 if( pins.size() > 0 )
1267 {
1268 switch( pins[0]->GetType() )
1269 {
1271 label->SetShape( LABEL_FLAG_SHAPE::L_INPUT );
1272 break;
1274 label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT );
1275 break;
1277 label->SetShape( LABEL_FLAG_SHAPE::L_BIDI );
1278 break;
1279 default:
1280 break;
1281 }
1282 }
1283
1284 BOX2I bbox = schSym->GetBodyAndPinsBoundingBox();
1285 bbox.Offset( -schSym->GetPosition() );
1286 VECTOR2I bboxCenter = bbox.GetCenter();
1287
1289
1290 if( std::abs( bboxCenter.x ) >= std::abs( bboxCenter.y ) )
1291 {
1292 if( bboxCenter.x >= 0 )
1293 spin = SPIN_STYLE::RIGHT;
1294 else
1295 spin = SPIN_STYLE::LEFT;
1296 }
1297 else
1298 {
1299 if( bboxCenter.y >= 0 )
1300 spin = SPIN_STYLE::BOTTOM;
1301 else
1302 spin = SPIN_STYLE::UP;
1303 }
1304
1305 label->SetSpinStyle( spin );
1306
1307 if( nameAttr )
1308 {
1309 nlohmann::json style = fontStyles[nameAttr->fontStyle];
1310
1311 if( !style.is_null() && style.at( 5 ).is_number() )
1312 {
1313 double size = style.at( 5 ).get<double>() * 0.62;
1314 label->SetTextSize( VECTOR2I( ScaleSize( size ), ScaleSize( size ) ) );
1315 }
1316 }
1317
1318 createdItems.push_back( std::move( label ) );
1319
1320 continue;
1321 }
1322 else
1323 {
1324 for( const wxString& attrKey : c_attributesWhitelist )
1325 {
1326 if( auto valOpt = get_opt( mergedAttrValues, attrKey ) )
1327 {
1328 if( valOpt->empty() )
1329 continue;
1330
1331 SCH_FIELD* text = schSym->FindFieldCaseInsensitive( attrKey );
1332
1333 if( !text )
1334 text = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, attrKey ) );
1335
1336 wxString value = *valOpt;
1337
1338 value.Replace( wxS( "\u2103" ), wxS( "\u00B0C" ), true ); // ℃ -> °C
1339
1340 text->SetText( value );
1341 text->SetVisible( false );
1342 }
1343 }
1344
1345 auto nameAttr = get_opt( attributes, "Name" );
1346 auto valueAttr = get_opt( attributes, "Value" );
1347
1348 if( valueAttr && valueAttr->value.empty() )
1349 valueAttr->value = get_def( prjCompAttrs, "Value", wxString() );
1350
1351 if( nameAttr && nameAttr->value.empty() )
1352 nameAttr->value = get_def( prjCompAttrs, "Name", wxString() );
1353
1354 std::optional<EASYEDAPRO::SCH_ATTR> targetValueAttr;
1355
1356 if( valueAttr && !valueAttr->value.empty() && valueAttr->valVisible )
1357 targetValueAttr = valueAttr;
1358 else if( nameAttr && !nameAttr->value.empty() && nameAttr->valVisible )
1359 targetValueAttr = nameAttr;
1360 else if( valueAttr && !valueAttr->value.empty() )
1361 targetValueAttr = valueAttr;
1362 else if( nameAttr && !nameAttr->value.empty() )
1363 targetValueAttr = nameAttr;
1364
1365 if( targetValueAttr )
1366 {
1367 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::VALUE ),
1368 *targetValueAttr, false, true, mergedAttrValues, schSym.get() );
1369 }
1370
1371 if( auto descrAttr = get_opt( attributes, "Description" ) )
1372 {
1373 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::DESCRIPTION ),
1374 *descrAttr, false, true, mergedAttrValues, schSym.get() );
1375 }
1376
1377 if( auto designatorAttr = get_opt( attributes, "Designator" ) )
1378 {
1379 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::REFERENCE ),
1380 *designatorAttr, false, true, mergedAttrValues, schSym.get() );
1381
1382 schSym->SetRef( &aSchematic->CurrentSheet(), designatorAttr->value );
1383 }
1384
1385 for( auto& [attrKey, attr] : attributes )
1386 {
1387 if( attrKey == wxS( "Name" ) || attrKey == wxS( "Value" )
1388 || attrKey == wxS( "Global Net Name" ) || attrKey == wxS( "Designator" )
1389 || attrKey == wxS( "Description" ) || attrKey == wxS( "Device" )
1390 || attrKey == wxS( "Footprint" ) || attrKey == wxS( "Symbol" )
1391 || attrKey == wxS( "Unique ID" ) )
1392 {
1393 continue;
1394 }
1395
1396 if( attr.value.IsEmpty() )
1397 continue;
1398
1399 SCH_FIELD* text = schSym->FindFieldCaseInsensitive( attrKey );
1400
1401 if( !text )
1402 text = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, attrKey ) );
1403
1404 text->SetPosition( schSym->GetPosition() );
1405
1406 ApplyAttrToField( fontStyles, text, attr, false, true, mergedAttrValues,
1407 schSym.get() );
1408 }
1409 }
1410
1411 for( const EASYEDAPRO::PIN_INFO& pinInfo : esymInfo.pins )
1412 {
1413 wxString pinKey = parentId + pinInfo.pin.id;
1414 auto pinLines = get_opt( parentedLines, pinKey );
1415
1416 if( !pinLines )
1417 continue;
1418
1419 for( const nlohmann::json& pinLine : *pinLines )
1420 {
1421 if( pinLine.at( 0 ) != "ATTR" )
1422 continue;
1423
1424 EASYEDAPRO::SCH_ATTR attr = pinLine;
1425
1426 if( attr.key != wxS( "NO_CONNECT" ) )
1427 continue;
1428
1429 for( SCH_PIN* schPin : schSym->GetPinsByNumber( pinInfo.number ) )
1430 {
1431 VECTOR2I pos = schSym->GetPinPhysicalPosition( schPin->GetLibPin() );
1432
1433 std::unique_ptr<SCH_NO_CONNECT> noConn =
1434 std::make_unique<SCH_NO_CONNECT>( pos );
1435
1436 createdItems.push_back( std::move( noConn ) );
1437 }
1438 }
1439 }
1440
1441 createdItems.push_back( std::move( schSym ) );
1442 }
1443 else // Not component
1444 {
1445 std::vector<SHAPE_LINE_CHAIN> wireLines;
1446
1447 if( wire )
1448 {
1449 for( const std::vector<double>& ptArr : wire->geometry )
1450 {
1452
1453 for( size_t i = 1; i < ptArr.size(); i += 2 )
1454 chain.Append( ScalePos( VECTOR2D( ptArr[i - 1], ptArr[i] ) ) );
1455
1456 if( chain.PointCount() < 2 )
1457 continue;
1458
1459 wireLines.push_back( chain );
1460
1461 for( int segId = 0; segId < chain.SegmentCount(); segId++ )
1462 {
1463 const SEG& seg = chain.CSegment( segId );
1464
1465 std::unique_ptr<SCH_LINE> schLine =
1466 std::make_unique<SCH_LINE>( seg.A, LAYER_WIRE );
1467 schLine->SetEndPoint( seg.B );
1468
1469 createdItems.push_back( std::move( schLine ) );
1470 }
1471 }
1472 }
1473
1474 auto netAttr = get_opt( attributes, "NET" );
1475
1476 if( netAttr )
1477 {
1478 if( !netAttr->valVisible || netAttr->value.IsEmpty() )
1479 continue;
1480
1481 VECTOR2I kpos = ScalePos( *netAttr->position );
1482 VECTOR2I nearestPos = kpos;
1483 SEG::ecoord min_dist_sq = VECTOR2I::ECOORD_MAX;
1484
1485 for( const SHAPE_LINE_CHAIN& chain : wireLines )
1486 {
1487 VECTOR2I nearestPt = chain.NearestPoint( kpos, false );
1488 SEG::ecoord dist_sq = ( nearestPt - kpos ).SquaredEuclideanNorm();
1489
1490 if( dist_sq < min_dist_sq )
1491 {
1492 min_dist_sq = dist_sq;
1493 nearestPos = nearestPt;
1494 }
1495 }
1496
1497 std::unique_ptr<SCH_LABEL> label = std::make_unique<SCH_LABEL>();
1498
1499 label->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1500 label->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1501
1502 for( double i = netAttr->rotation; i > 0; i -= 90 )
1503 label->Rotate90( true );
1504
1505 label->SetPosition( nearestPos );
1506 label->SetText( netAttr->value );
1507
1508 ApplyFontStyle( fontStyles, label, netAttr->fontStyle );
1509
1510 createdItems.push_back( std::move( label ) );
1511 }
1512 }
1513 }
1514
1515 // Adjust page to content
1516 BOX2I sheetBBox;
1517
1518 for( std::unique_ptr<SCH_ITEM>& ptr : createdItems )
1519 {
1520 if( ptr->Type() == SCH_SYMBOL_T )
1521 sheetBBox.Merge( static_cast<SCH_SYMBOL*>( ptr.get() )->GetBodyAndPinsBoundingBox() );
1522 else
1523 sheetBBox.Merge( ptr->GetBoundingBox() );
1524 }
1525
1526 SCH_SCREEN* screen = aRootSheet->GetScreen();
1527 PAGE_INFO pageInfo = screen->GetPageSettings();
1528
1529 int alignGrid = schIUScale.MilsToIU( 50 );
1530
1531 VECTOR2D offset( -sheetBBox.GetLeft(), -sheetBBox.GetTop() );
1532 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1533 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1534
1535 pageInfo.SetWidthMils( schIUScale.IUToMils( sheetBBox.GetWidth() ) );
1536 pageInfo.SetHeightMils( schIUScale.IUToMils( sheetBBox.GetHeight() ) );
1537
1538 screen->SetPageSettings( pageInfo );
1539
1540 for( std::unique_ptr<SCH_ITEM>& ptr : createdItems )
1541 {
1542 ptr->Move( offset );
1543 screen->Append( ptr.release() );
1544 }
1545}
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 const Vec GetCenter() const
Definition box2.h:226
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
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:255
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
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:381
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:79
const LIB_ID & GetLibId() const override
Definition lib_symbol.h:148
void SetGlobalPower()
wxString GetFootprint() override
For items with footprint fields.
SCH_FIELD * FindFieldCaseInsensitive(const wxString &aFieldName)
wxString GetName() const override
Definition lib_symbol.h:141
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:329
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:333
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 double Convert(wxString aValue)
static VECTOR2< T > ScalePos(VECTOR2< T > aValue)
double SizeToKi(wxString units)
static T ScaleSize(T aValue)
void ApplyFontStyle(const std::map< wxString, nlohmann::json > &fontStyles, T &text, const wxString &styleStr)
static VECTOR2< T > ScalePosSym(VECTOR2< T > aValue)
SCH_EASYEDAPRO_PARSER(SCHEMATIC *aSchematic, PROGRESS_REPORTER *aProgressReporter)
EASYEDAPRO::SYM_INFO ParseSymbol(const std::vector< nlohmann::json > &aLines, const std::map< wxString, wxString > &aDeviceAttributes)
void ApplyAttrToField(const std::map< wxString, nlohmann::json > &fontStyles, T *text, const EASYEDAPRO::SCH_ATTR &aAttr, bool aIsSym, bool aToSym, const std::map< wxString, wxString > &aDeviceAttributes={}, SCH_SYMBOL *aParent=nullptr)
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 std::vector< nlohmann::json > &aLines, const wxString &aLibName)
wxString ResolveFieldVariables(const wxString aInput, const std::map< wxString, wxString > &aDeviceAttributes)
void ApplyLineStyle(const std::map< wxString, nlohmann::json > &lineStyles, T &shape, const wxString &styleStr)
bool Replace(const EDA_SEARCH_DATA &aSearchData, void *aAuxData=nullptr) override
Perform a text replace using the find and replace criteria in aSearchData on items that support text ...
virtual const wxString & GetText() const override
Return the string associated with the text object.
Definition sch_field.h:128
void SetText(const wxString &aText) override
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.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
#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)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
@ LAYER_DEVICE
Definition layer_ids.h:464
@ LAYER_WIRE
Definition layer_ids.h:450
@ LAYER_NOTES
Definition layer_ids.h:465
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)
std::map< wxString, wxString > AnyMapToStringMap(const std::map< wxString, nlohmann::json > &aInput)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ 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_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 const std::vector< wxString > c_attributesWhitelist
static LINE_STYLE ConvertStrokeStyle(const wxString &aStyle)
static LINE_STYLE ConvertStrokeStyle(int aStyle)
@ L_BIDI
Definition sch_label.h:100
@ 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
@ 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".
@ REFERENCE
Field Reference of part, i.e. "IC21".
@ VALUE
Field Value of part, i.e. "3.3K".
KIBIS_PIN * pin
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
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