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