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