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 if( totalUnits == 0 )
351 totalUnits = 1;
352
353 symInfo.partUnits = partUnits;
354 ksymbol->SetUnitCount( totalUnits, false );
355
356 int currentUnit = 1;
357
358 for( const nlohmann::json& line : aLines )
359 {
360 wxString type = line.at( 0 );
361
362 if( type == wxS( "PART" ) )
363 {
364 currentUnit = partUnits.at( line.at( 1 ) );
365 }
366 else if( type == wxS( "RECT" ) )
367 {
368 VECTOR2D start( line.at( 2 ), line.at( 3 ) );
369 VECTOR2D end( line.at( 4 ), line.at( 5 ) );
370 wxString styleStr = line.at( 9 );
371
372 auto rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
373
374 rect->SetStart( ScalePosSym( start ) );
375 rect->SetEnd( ScalePosSym( end ) );
376
377 rect->SetUnit( currentUnit );
378 ApplyLineStyle( lineStyles, rect, styleStr );
379
380 ksymbol->AddDrawItem( rect.release() );
381 }
382 else if( type == wxS( "CIRCLE" ) )
383 {
384 VECTOR2D center( line.at( 2 ), line.at( 3 ) );
385 double radius = line.at( 4 );
386 wxString styleStr = line.at( 5 );
387
388 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
389
390 circle->SetCenter( ScalePosSym( center ) );
391 circle->SetEnd( circle->GetCenter() + VECTOR2I( ScaleSize( radius ), 0 ) );
392
393 circle->SetUnit( currentUnit );
394 ApplyLineStyle( lineStyles, circle, styleStr );
395
396 ksymbol->AddDrawItem( circle.release() );
397 }
398 else if( type == wxS( "ARC" ) )
399 {
400 VECTOR2D start( line.at( 2 ), line.at( 3 ) );
401 VECTOR2D mid( line.at( 4 ), line.at( 5 ) );
402 VECTOR2D end( line.at( 6 ), line.at( 7 ) );
403 wxString styleStr = line.at( 8 );
404
405 VECTOR2D kstart = ScalePosSym( start );
406 VECTOR2D kmid = ScalePosSym( mid );
407 VECTOR2D kend = ScalePosSym( end );
408
409 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
410
411 shape->SetArcOrLine( kstart, kmid, kend );
412
413 shape->SetUnit( currentUnit );
414 ApplyLineStyle( lineStyles, shape, styleStr );
415
416 ksymbol->AddDrawItem( shape.release() );
417 }
418 else if( type == wxS( "BEZIER" ) )
419 {
420 std::vector<double> points = line.at( 2 );
421 wxString styleStr = line.at( 3 );
422
423 std::unique_ptr<SCH_SHAPE> shape =
424 std::make_unique<SCH_SHAPE>( SHAPE_T::BEZIER, LAYER_DEVICE );
425
426 for( size_t i = 1; i < points.size(); i += 2 )
427 {
428 VECTOR2I pt = ScalePosSym( VECTOR2D( points[i - 1], points[i] ) );
429
430 switch( i )
431 {
432 case 1: shape->SetStart( pt ); break;
433 case 3: shape->SetBezierC1( pt ); break;
434 case 5: shape->SetBezierC2( pt ); break;
435 case 7: shape->SetEnd( pt ); break;
436 }
437 }
438
439 shape->SetUnit( currentUnit );
440 ApplyLineStyle( lineStyles, shape, styleStr );
441
442 ksymbol->AddDrawItem( shape.release() );
443 }
444 else if( type == wxS( "POLY" ) )
445 {
446 std::vector<double> points = line.at( 2 );
447 wxString styleStr = line.at( 4 );
448
449 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
450
451 for( size_t i = 1; i < points.size(); i += 2 )
452 shape->AddPoint( ScalePosSym( VECTOR2D( points[i - 1], points[i] ) ) );
453
454 shape->SetUnit( currentUnit );
455 ApplyLineStyle( lineStyles, shape, styleStr );
456
457 ksymbol->AddDrawItem( shape.release() );
458 }
459 else if( type == wxS( "TEXT" ) )
460 {
461 VECTOR2D pos( line.at( 2 ), line.at( 3 ) );
462 double angle = line.at( 4 ).is_number() ? line.at( 4 ).get<double>() : 0.0;
463 wxString textStr = line.at( 5 );
464 wxString fontStyleStr = line.at( 6 );
465
466 auto text = std::make_unique<SCH_TEXT>( ScalePosSym( pos ), UnescapeHTML( textStr ),
467 LAYER_DEVICE );
468
469 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
470 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
471 text->SetTextAngleDegrees( angle );
472
473 text->SetUnit( currentUnit );
474 ApplyFontStyle( fontStyles, text, fontStyleStr );
475
476 ksymbol->AddDrawItem( text.release() );
477 }
478 else if( type == wxS( "OBJ" ) )
479 {
480 VECTOR2D start, size;
481 wxString mimeType, data;
482 //double angle = 0;
483 int upsideDown = 0;
484
485 if( line.at( 3 ).is_number() )
486 {
487 start = VECTOR2D( line.at( 3 ), line.at( 4 ) );
488 size = VECTOR2D( line.at( 5 ), line.at( 6 ) );
489 //angle = line.at( 7 );
490 upsideDown = line.at( 8 );
491
492 wxString imageUrl = line.at( 9 );
493
494 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
495 {
496 wxArrayString paramsArr =
497 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
498
499 data = imageUrl.AfterFirst( ',' );
500
501 if( paramsArr.size() > 0 )
502 {
503 mimeType = paramsArr[0];
504 }
505 }
506 }
507 else if( line.at( 3 ).is_string() )
508 {
509 mimeType = line.at( 3 ).get<wxString>().BeforeFirst( ';' );
510
511 start = VECTOR2D( line.at( 4 ), line.at( 5 ) );
512 size = VECTOR2D( line.at( 6 ), line.at( 7 ) );
513 //angle = line.at( 8 );
514 data = line.at( 9 ).get<wxString>();
515 }
516
517 if( mimeType.empty() || data.empty() )
518 continue;
519
520 wxMemoryBuffer buf = wxBase64Decode( data );
521
522 if( mimeType == wxS( "image/svg+xml" ) )
523 {
524 VECTOR2D offset = ScalePosSym( start );
525
526 SVG_IMPORT_PLUGIN svgImportPlugin;
527 GRAPHICS_IMPORTER_LIB_SYMBOL libsymImporter( ksymbol, 0 );
528
529 svgImportPlugin.SetImporter( &libsymImporter );
530 svgImportPlugin.LoadFromMemory( buf );
531
532 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
533 svgImportPlugin.GetImageHeight() );
534
535 VECTOR2D pixelScale( schIUScale.IUTomm( ScaleSize( size.x ) ) / imSize.x,
536 schIUScale.IUTomm( ScaleSize( size.y ) ) / imSize.y );
537
538 if( upsideDown )
539 pixelScale.y *= -1;
540
541 libsymImporter.SetScale( pixelScale );
542
543 VECTOR2D offsetMM( schIUScale.IUTomm( offset.x ), schIUScale.IUTomm( offset.y ) );
544
545 libsymImporter.SetImportOffsetMM( offsetMM );
546
547 svgImportPlugin.Import();
548
549 // TODO: rotation
550 for( std::unique_ptr<EDA_ITEM>& item : libsymImporter.GetItems() )
551 ksymbol->AddDrawItem( static_cast<SCH_ITEM*>( item.release() ) );
552 }
553 else
554 {
555 wxMemoryInputStream memis( buf.GetData(), buf.GetDataLen() );
556
557 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
558 & ~wxImage::Load_Verbose );
559 wxImage img;
560 if( img.LoadFile( memis, mimeType ) )
561 {
562 int dimMul = img.GetWidth() * img.GetHeight();
563 double maxPixels = 30000;
564
565 if( dimMul > maxPixels )
566 {
567 double scale = sqrt( maxPixels / dimMul );
568 img.Rescale( img.GetWidth() * scale, img.GetHeight() * scale );
569 }
570
571 VECTOR2D pixelScale( ScaleSize( size.x ) / img.GetWidth(),
572 ScaleSize( size.y ) / img.GetHeight() );
573
574 // TODO: rotation
575 ConvertImageToLibShapes( ksymbol, 0, img, pixelScale, ScalePosSym( start ) );
576 }
577 }
578 }
579 else if( type == wxS( "HEAD" ) )
580 {
581 symInfo.head = line;
582 }
583 else if( type == wxS( "PIN" ) )
584 {
585 wxString pinId = line.at( 1 );
586 unitParentedLines[currentUnit][pinId].push_back( line );
587 }
588 else if( type == wxS( "ATTR" ) )
589 {
590 wxString parentId = line.at( 2 );
591
592 if( parentId.empty() )
593 {
594 EASYEDAPRO::SCH_ATTR attr = line;
595 unitAttributes[currentUnit].emplace( attr.key, attr );
596 }
597 else
598 {
599 unitParentedLines[currentUnit][parentId].push_back( line );
600 }
601 }
602 }
603
606 {
607 ksymbol->SetGlobalPower();
608 ksymbol->GetReferenceField().SetText( wxS( "#PWR" ) );
609 ksymbol->GetReferenceField().SetVisible( false );
610 ksymbol->SetKeyWords( wxS( "power-flag" ) );
611 ksymbol->SetShowPinNames( false );
612 ksymbol->SetShowPinNumbers( false );
613
614 if( auto globalNetAttr = get_opt( unitAttributes[1], wxS( "Global Net Name" ) ) )
615 {
616 ApplyAttrToField( fontStyles, &ksymbol->GetValueField(), *globalNetAttr, true, true );
617
618 wxString globalNetname = globalNetAttr->value;
619
620 if( !globalNetname.empty() )
621 {
622 ksymbol->SetDescription( wxString::Format(
623 _( "Power symbol creates a global label with name '%s'" ),
624 globalNetname ) );
625 }
626 }
627 }
628 else
629 {
630 auto designatorAttr = get_opt( unitAttributes[1], wxS( "Designator" ) );
631
632 if( designatorAttr && !designatorAttr->value.empty() )
633 {
634 wxString symbolPrefix = designatorAttr->value;
635
636 if( symbolPrefix.EndsWith( wxS( "?" ) ) )
637 symbolPrefix.RemoveLast();
638
639 ksymbol->GetReferenceField().SetText( symbolPrefix );
640 }
641
643 aDeviceAttributes, true,
644 [&]( const wxString& attrName, const wxString& value )
645 {
646 SCH_FIELD* fd = ksymbol->FindFieldCaseInsensitive( attrName );
647
648 if( !fd )
649 {
650 fd = new SCH_FIELD( ksymbol, FIELD_T::USER, attrName );
651 ksymbol->AddField( fd );
652 }
653
654 fd->SetText( value );
655 fd->SetVisible( false );
656 } );
657 }
658
659 for( auto& [unitId, parentedLines] : unitParentedLines )
660 {
661 for( auto& [pinId, lines] : parentedLines )
662 {
663 std::optional<EASYEDAPRO::SYM_PIN> epin;
664 std::map<wxString, EASYEDAPRO::SCH_ATTR> pinAttributes;
665
666 for( const nlohmann::json& line : lines )
667 {
668 wxString type = line.at( 0 );
669
670 if( type == wxS( "ATTR" ) )
671 {
672 EASYEDAPRO::SCH_ATTR attr = line;
673 pinAttributes.emplace( attr.key, attr );
674 }
675 else if( type == wxS( "PIN" ) )
676 {
677 epin = line;
678 }
679 }
680
681 if( !epin )
682 continue;
683
684 EASYEDAPRO::PIN_INFO pinInfo;
685 pinInfo.pin = *epin;
686
687 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( ksymbol );
688
689 pin->SetUnit( unitId );
690
691 pin->SetLength( ScaleSize( epin->length ) );
692 pin->SetPosition( ScalePosSym( epin->position ) );
693
695
696 if( epin->rotation == 0 )
698 if( epin->rotation == 90 )
700 if( epin->rotation == 180 )
702 if( epin->rotation == 270 )
704
705 pin->SetOrientation( orient );
706
708 {
709 pin->SetName( ksymbol->GetName() );
710 //pin->SetVisible( false );
711 }
712 else
713 {
714 auto pinNameAttr = get_opt( pinAttributes, "Pin Name" ); // JLCEDA V3
715
716 if( !pinNameAttr )
717 pinNameAttr = get_opt( pinAttributes, "NAME" ); // EasyEDA V2
718
719 if( pinNameAttr )
720 {
721 pin->SetName( pinNameAttr->value );
722 pinInfo.name = pinNameAttr->value;
723
724 if( !pinNameAttr->valVisible )
725 pin->SetNameTextSize( schIUScale.MilsToIU( 1 ) );
726 }
727 }
728
729 auto pinNumAttr = get_opt( pinAttributes, "Pin Number" ); // JLCEDA V3
730
731 if( !pinNumAttr )
732 pinNumAttr = get_opt( pinAttributes, "NUMBER" ); // EasyEDA V2
733
734 if( pinNumAttr )
735 {
736 pin->SetNumber( pinNumAttr->value );
737 pinInfo.number = pinNumAttr->value;
738
739 if( !pinNumAttr->valVisible )
740 pin->SetNumberTextSize( schIUScale.MilsToIU( 1 ) );
741 }
742
744 {
746 }
747 else if( auto pinTypeAttr = get_opt( pinAttributes, "Pin Type" ) )
748 {
749 if( pinTypeAttr->value == wxS( "IN" ) )
751 if( pinTypeAttr->value == wxS( "OUT" ) )
753 if( pinTypeAttr->value == wxS( "BI" ) )
755 }
756
757 if( get_opt( pinAttributes, "NO_CONNECT" ) )
758 pin->SetType( ELECTRICAL_PINTYPE::PT_NC );
759
760 if( pin->GetNumberTextSize() * int( pin->GetNumber().size() ) > pin->GetLength() )
761 pin->SetNumberTextSize( pin->GetLength() / pin->GetNumber().size() );
762
763 symInfo.pins.push_back( pinInfo );
764 ksymbol->AddDrawItem( pin.release() );
765 }
766 }
767
768 symInfo.symbolAttr = get_opt( unitAttributes[1], "Symbol" ); // TODO: per-unit
769
770 /*BOX2I bbox = ksymbol->GetBodyBoundingBox( 0, 0, true, true );
771 bbox.Inflate( schIUScale.MilsToIU( 10 ) );*/
772
773 /*ksymbol->GetReferenceField().SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
774 ksymbol->GetReferenceField().SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
775 ksymbol->GetReferenceField().SetPosition( VECTOR2I( bbox.GetCenter().x, -bbox.GetTop() ) );
776
777 ksymbol->GetValueField().SetVertJustify( GR_TEXT_V_ALIGN_TOP );
778 ksymbol->GetValueField().SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
779 ksymbol->GetValueField().SetPosition( VECTOR2I( bbox.GetCenter().x, -bbox.GetBottom() ) );*/
780
781 symInfo.libSymbol = std::move( ksymbolPtr );
782
783 return symInfo;
784}
785
786
788 const nlohmann::json& aProject,
789 std::map<wxString, EASYEDAPRO::SYM_INFO>& aSymbolMap,
790 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
791 const std::vector<nlohmann::json>& aLines,
792 const wxString& aLibName )
793{
794 std::vector<std::unique_ptr<SCH_ITEM>> createdItems;
795
796 std::map<wxString, std::vector<nlohmann::json>> parentedLines;
797 std::map<wxString, std::vector<nlohmann::json>> ruleLines;
798
799 std::map<wxString, nlohmann::json> lineStyles;
800 std::map<wxString, nlohmann::json> fontStyles;
801
802 for( const nlohmann::json& line : aLines )
803 {
804 wxString type = line.at( 0 );
805
806 if( type == wxS( "LINESTYLE" ) )
807 lineStyles[line.at( 1 )] = line;
808 else if( type == wxS( "FONTSTYLE" ) )
809 fontStyles[line.at( 1 )] = line;
810 }
811
812 for( const nlohmann::json& line : aLines )
813 {
814 wxString type = line.at( 0 );
815
816 if( type == wxS( "RECT" ) )
817 {
818 VECTOR2D start( line.at( 2 ), line.at( 3 ) );
819 VECTOR2D end( line.at( 4 ), line.at( 5 ) );
820 wxString styleStr = line.at( 9 );
821
822 std::unique_ptr<SCH_SHAPE> rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
823
824 rect->SetStart( ScalePos( start ) );
825 rect->SetEnd( ScalePos( end ) );
826
827 ApplyLineStyle( lineStyles, rect, styleStr );
828
829 createdItems.push_back( std::move( rect ) );
830 }
831 else if( type == wxS( "CIRCLE" ) )
832 {
833 VECTOR2D center( line.at( 2 ), line.at( 3 ) );
834 double radius = line.at( 4 );
835 wxString styleStr = line.at( 5 );
836
837 std::unique_ptr<SCH_SHAPE> circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE );
838
839 circle->SetCenter( ScalePos( center ) );
840 circle->SetEnd( circle->GetCenter() + VECTOR2I( ScaleSize( radius ), 0 ) );
841
842 ApplyLineStyle( lineStyles, circle, styleStr );
843
844 createdItems.push_back( std::move( circle ) );
845 }
846 else if( type == wxS( "POLY" ) )
847 {
848 std::vector<double> points = line.at( 2 );
849 wxString styleStr = line.at( 4 );
850
852
853 for( size_t i = 1; i < points.size(); i += 2 )
854 chain.Append( ScalePos( VECTOR2D( points[i - 1], points[i] ) ) );
855
856 for( int segId = 0; segId < chain.SegmentCount(); segId++ )
857 {
858 const SEG& seg = chain.CSegment( segId );
859
860 std::unique_ptr<SCH_LINE> schLine =
861 std::make_unique<SCH_LINE>( seg.A, LAYER_NOTES );
862 schLine->SetEndPoint( seg.B );
863
864 ApplyLineStyle( lineStyles, schLine, styleStr );
865
866 createdItems.push_back( std::move( schLine ) );
867 }
868 }
869 else if( type == wxS( "TEXT" ) )
870 {
871 VECTOR2D pos( line.at( 2 ), line.at( 3 ) );
872 double angle = line.at( 4 ).is_number() ? line.at( 4 ).get<double>() : 0.0;
873 wxString textStr = line.at( 5 );
874 wxString fontStyleStr = line.at( 6 );
875
876 std::unique_ptr<SCH_TEXT> text =
877 std::make_unique<SCH_TEXT>( ScalePos( pos ), UnescapeHTML( textStr ) );
878
879 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
880 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
881
882 text->SetTextAngleDegrees( angle );
883
884 ApplyFontStyle( fontStyles, text, fontStyleStr );
885
886 createdItems.push_back( std::move( text ) );
887 }
888 else if( type == wxS( "OBJ" ) )
889 {
890 VECTOR2D start, size;
891 wxString mimeType, base64Data;
892 double angle = 0;
893 int flipped = 0;
894
895 if( line.at( 3 ).is_number() )
896 {
897 start = VECTOR2D( line.at( 3 ), line.at( 4 ) );
898 size = VECTOR2D( line.at( 5 ), line.at( 6 ) );
899 angle = line.at( 7 );
900 flipped = line.at( 8 );
901
902 wxString imageUrl = line.at( 9 );
903
904 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
905 {
906 wxArrayString paramsArr =
907 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
908
909 base64Data = imageUrl.AfterFirst( ',' );
910
911 if( paramsArr.size() > 0 )
912 mimeType = paramsArr[0];
913 }
914 else if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
915 {
916 wxString objectId = imageUrl.AfterLast( ':' );
917
918 if( auto blob = get_opt( aBlobMap, objectId ) )
919 {
920 wxString blobUrl = blob->url;
921
922 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
923 {
924 wxArrayString paramsArr = wxSplit(
925 blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
926
927 base64Data = blobUrl.AfterFirst( ',' );
928
929 if( paramsArr.size() > 0 )
930 mimeType = paramsArr[0];
931 }
932 }
933 }
934 }
935 else if( line.at( 3 ).is_string() )
936 {
937 mimeType = line.at( 3 ).get<wxString>().BeforeFirst( ';' );
938
939 start = VECTOR2D( line.at( 4 ), line.at( 5 ) );
940 size = VECTOR2D( line.at( 6 ), line.at( 7 ) );
941 angle = line.at( 8 );
942 base64Data = line.at( 9 ).get<wxString>();
943 }
944
945 VECTOR2D kstart = ScalePos( start );
946 VECTOR2D ksize = ScaleSize( size );
947
948 if( mimeType.empty() || base64Data.empty() )
949 continue;
950
951 wxMemoryBuffer buf = wxBase64Decode( base64Data );
952
953 if( mimeType == wxS( "image/svg+xml" ) )
954 {
955 SVG_IMPORT_PLUGIN svgImportPlugin;
956 GRAPHICS_IMPORTER_SCH schImporter;
957
958 svgImportPlugin.SetImporter( &schImporter );
959 svgImportPlugin.LoadFromMemory( buf );
960
961 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
962 svgImportPlugin.GetImageHeight() );
963
964 VECTOR2D pixelScale( schIUScale.IUTomm( ScaleSize( size.x ) ) / imSize.x,
965 schIUScale.IUTomm( ScaleSize( size.y ) ) / imSize.y );
966
967 schImporter.SetScale( pixelScale );
968
969 VECTOR2D offsetMM( schIUScale.IUTomm( kstart.x ), schIUScale.IUTomm( kstart.y ) );
970
971 schImporter.SetImportOffsetMM( offsetMM );
972
973 svgImportPlugin.Import();
974
975 for( std::unique_ptr<EDA_ITEM>& item : schImporter.GetItems() )
976 {
977 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item.release() );
978
979 for( double i = angle; i > 0; i -= 90 )
980 {
981 if( schItem->Type() == SCH_LINE_T )
982 {
983 // Lines need special handling for some reason
984 schItem->SetFlags( STARTPOINT );
985 schItem->Rotate( kstart, false );
986 schItem->ClearFlags( STARTPOINT );
987
988 schItem->SetFlags( ENDPOINT );
989 schItem->Rotate( kstart, false );
990 schItem->ClearFlags( ENDPOINT );
991 }
992 else
993 {
994 schItem->Rotate( kstart, false );
995 }
996 }
997
998 if( flipped )
999 {
1000 // Lines need special handling for some reason
1001 if( schItem->Type() == SCH_LINE_T )
1002 schItem->SetFlags( STARTPOINT | ENDPOINT );
1003
1004 schItem->MirrorHorizontally( kstart.x );
1005
1006 if( schItem->Type() == SCH_LINE_T )
1007 schItem->ClearFlags( STARTPOINT | ENDPOINT );
1008 }
1009
1010 createdItems.emplace_back( schItem );
1011 }
1012 }
1013 else
1014 {
1015 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
1016 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1017
1018 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1019 & ~wxImage::Load_Verbose );
1020
1021 if( refImage.ReadImageFile( buf ) )
1022 {
1023 VECTOR2D kcenter = kstart + ksize / 2;
1024
1025 double scaleFactor = ScaleSize( size.x ) / refImage.GetSize().x;
1026 refImage.SetImageScale( scaleFactor );
1027 bitmap->SetPosition( kcenter );
1028
1029 for( double i = angle; i > 0; i -= 90 )
1030 bitmap->Rotate( kstart, false );
1031
1032 if( flipped )
1033 bitmap->MirrorHorizontally( kstart.x );
1034
1035 createdItems.push_back( std::move( bitmap ) );
1036 }
1037 }
1038 }
1039 if( type == wxS( "WIRE" ) )
1040 {
1041 wxString wireId = line.at( 1 );
1042 parentedLines[wireId].push_back( line );
1043 }
1044 else if( type == wxS( "COMPONENT" ) )
1045 {
1046 wxString compId = line.at( 1 );
1047 parentedLines[compId].push_back( line );
1048 }
1049 else if( type == wxS( "ATTR" ) )
1050 {
1051 wxString compId = line.at( 2 );
1052 parentedLines[compId].push_back( line );
1053 }
1054 }
1055
1056 for( auto& [parentId, lines] : parentedLines )
1057 {
1058 std::optional<EASYEDAPRO::SCH_COMPONENT> component;
1059 std::optional<EASYEDAPRO::SCH_WIRE> wire;
1060 std::map<wxString, EASYEDAPRO::SCH_ATTR> attributes;
1061
1062 for( const nlohmann::json& line : lines )
1063 {
1064 if( line.at( 0 ) == "COMPONENT" )
1065 {
1066 component = line;
1067 }
1068 else if( line.at( 0 ) == "WIRE" )
1069 {
1070 wire = line;
1071 }
1072 else if( line.at( 0 ) == "ATTR" )
1073 {
1074 EASYEDAPRO::SCH_ATTR attr = line;
1075 attributes.emplace( attr.key, attr );
1076 }
1077 }
1078
1079 if( component )
1080 {
1081 auto deviceAttr = get_opt( attributes, "Device" );
1082 auto symbolAttr = get_opt( attributes, "Symbol" );
1083
1084 if( !deviceAttr )
1085 continue;
1086
1087 const std::map<wxString, wxString> prjCompAttrs = EASYEDAPRO::AnyMapToStringMap(
1088 aProject.at( "devices" ).at( deviceAttr->value ).at( "attributes" ) );
1089
1090 // Merge attributes, giving priority to schematic attributes over project attributes
1091 std::map<wxString, wxString> mergedAttrValues;
1092
1093 for( const auto& [key, value] : prjCompAttrs )
1094 mergedAttrValues[key] = value;
1095
1096 for( const auto& [key, attr] : attributes )
1097 mergedAttrValues[key] = attr.value;
1098
1099 wxString symbolId;
1100
1101 if( symbolAttr && !symbolAttr->value.IsEmpty() )
1102 symbolId = symbolAttr->value;
1103 else
1104 symbolId = prjCompAttrs.at( "Symbol" );
1105
1106 auto it = aSymbolMap.find( symbolId );
1107 if( it == aSymbolMap.end() )
1108 {
1109 wxLogTrace( traceEasyEdaIo, "Symbol of '%s' with uuid '%s' not found.", component->name, symbolId );
1110 continue;
1111 }
1112
1113 EASYEDAPRO::SYM_INFO& esymInfo = it->second;
1114 LIB_SYMBOL newLibSymbol = *esymInfo.libSymbol.get();
1115
1116 wxString unitName = component->name;
1117
1118 LIB_ID libId = EASYEDAPRO::ToKiCadLibID( aLibName,
1119 newLibSymbol.GetLibId().GetLibItemName() );
1120
1121 auto schSym = std::make_unique<SCH_SYMBOL>( newLibSymbol, libId,
1122 &aSchematic->CurrentSheet(),
1123 esymInfo.partUnits[unitName] );
1124
1125 schSym->SetFootprintFieldText( newLibSymbol.GetFootprint() );
1126
1127 for( double i = component->rotation; i > 0; i -= 90 )
1128 schSym->Rotate( VECTOR2I(), true );
1129
1130 if( component->mirror )
1131 schSym->MirrorHorizontally( 0 );
1132
1133 schSym->SetPosition( ScalePos( component->position ) );
1134
1136 {
1137 SCH_FIELD* valueField = schSym->GetField( FIELD_T::VALUE );
1138
1139 auto globalNetNameAttr = get_opt( attributes, "Global Net Name" );
1140 wxString globalNetNameFromProject = get_def( prjCompAttrs, "Global Net Name", wxEmptyString );
1141 wxString globalNetName;
1142
1143 // 1. Pick from schematic attr
1144 // 2. Pick from project.json
1145 // 3. Pick from symbol
1146 if( globalNetNameAttr && !globalNetNameAttr->value.IsEmpty() )
1147 {
1148 globalNetName = globalNetNameAttr->value;
1149
1150 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::VALUE ), *globalNetNameAttr, false, true,
1151 mergedAttrValues, schSym.get() );
1152 }
1153 else if( !globalNetNameFromProject.IsEmpty() )
1154 {
1155 globalNetName = globalNetNameFromProject;
1156
1157 valueField->SetText( EASYEDAPRO::ResolveDeviceFieldVariables( globalNetName, mergedAttrValues ) );
1158 }
1159 else
1160 {
1161 valueField->SetText( newLibSymbol.GetValueField().GetText() );
1162 }
1163
1164 for( SCH_PIN* pin : schSym->GetAllLibPins() )
1165 pin->SetName( globalNetName );
1166
1167 schSym->SetRef( &aSchematic->CurrentSheet(), wxS( "#PWR?" ) );
1168 schSym->GetField( FIELD_T::REFERENCE )->SetVisible( false );
1169 }
1170 else if( esymInfo.head.symbolType == EASYEDAPRO::SYMBOL_TYPE::NETPORT )
1171 {
1172 auto nameAttr = get_opt( attributes, "Name" );
1173
1174 wxString netName;
1175
1176 if( nameAttr && !nameAttr->value.IsEmpty() )
1177 netName = nameAttr->value;
1178 else
1179 netName = prjCompAttrs.at( "Name" );
1180
1181 std::unique_ptr<SCH_GLOBALLABEL> label = std::make_unique<SCH_GLOBALLABEL>(
1182 ScalePos( component->position ), netName );
1183
1184 std::vector<SCH_PIN*> pins = schSym->GetPins( &aSchematic->CurrentSheet() );
1185
1186 if( pins.size() > 0 )
1187 {
1188 switch( pins[0]->GetType() )
1189 {
1191 label->SetShape( LABEL_FLAG_SHAPE::L_INPUT );
1192 break;
1194 label->SetShape( LABEL_FLAG_SHAPE::L_OUTPUT );
1195 break;
1197 label->SetShape( LABEL_FLAG_SHAPE::L_BIDI );
1198 break;
1199 default:
1200 break;
1201 }
1202 }
1203
1204 BOX2I bbox = schSym->GetBodyAndPinsBoundingBox();
1205 bbox.Offset( -schSym->GetPosition() );
1206 VECTOR2I bboxCenter = bbox.GetCenter();
1207
1209
1210 if( std::abs( bboxCenter.x ) >= std::abs( bboxCenter.y ) )
1211 {
1212 if( bboxCenter.x >= 0 )
1213 spin = SPIN_STYLE::RIGHT;
1214 else
1215 spin = SPIN_STYLE::LEFT;
1216 }
1217 else
1218 {
1219 if( bboxCenter.y >= 0 )
1220 spin = SPIN_STYLE::BOTTOM;
1221 else
1222 spin = SPIN_STYLE::UP;
1223 }
1224
1225 label->SetSpinStyle( spin );
1226
1227 if( nameAttr )
1228 {
1229 nlohmann::json style = fontStyles[nameAttr->fontStyle];
1230
1231 if( !style.is_null() && style.at( 5 ).is_number() )
1232 {
1233 double size = style.at( 5 ).get<double>() * 0.62;
1234 label->SetTextSize( VECTOR2I( ScaleSize( size ), ScaleSize( size ) ) );
1235 }
1236 }
1237
1238 createdItems.push_back( std::move( label ) );
1239
1240 continue;
1241 }
1242 else
1243 {
1245 mergedAttrValues, true,
1246 [&]( const wxString& attrKey, const wxString& value )
1247 {
1248 SCH_FIELD* text = schSym->FindFieldCaseInsensitive( attrKey );
1249
1250 if( !text )
1251 text = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, attrKey ) );
1252
1253 text->SetText( value );
1254 text->SetVisible( false );
1255 } );
1256
1257 auto nameAttr = get_opt( attributes, "Name" );
1258 auto valueAttr = get_opt( attributes, "Value" );
1259
1260 if( valueAttr && valueAttr->value.empty() )
1261 valueAttr->value = get_def( prjCompAttrs, "Value", wxString() );
1262
1263 if( nameAttr && nameAttr->value.empty() )
1264 nameAttr->value = get_def( prjCompAttrs, "Name", wxString() );
1265
1266 std::optional<EASYEDAPRO::SCH_ATTR> targetValueAttr;
1267
1268 if( valueAttr && !valueAttr->value.empty() && valueAttr->valVisible )
1269 targetValueAttr = valueAttr;
1270 else if( nameAttr && !nameAttr->value.empty() && nameAttr->valVisible )
1271 targetValueAttr = nameAttr;
1272 else if( valueAttr && !valueAttr->value.empty() )
1273 targetValueAttr = valueAttr;
1274 else if( nameAttr && !nameAttr->value.empty() )
1275 targetValueAttr = nameAttr;
1276
1277 if( targetValueAttr )
1278 {
1279 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::VALUE ),
1280 *targetValueAttr, false, true, mergedAttrValues, schSym.get() );
1281 }
1282
1283 if( auto descrAttr = get_opt( attributes, "Description" ) )
1284 {
1285 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::DESCRIPTION ),
1286 *descrAttr, false, true, mergedAttrValues, schSym.get() );
1287 }
1288
1289 if( auto designatorAttr = get_opt( attributes, "Designator" ) )
1290 {
1291 ApplyAttrToField( fontStyles, schSym->GetField( FIELD_T::REFERENCE ),
1292 *designatorAttr, false, true, mergedAttrValues, schSym.get() );
1293
1294 schSym->SetRef( &aSchematic->CurrentSheet(), designatorAttr->value );
1295 }
1296
1297 for( auto& [attrKey, attr] : attributes )
1298 {
1299 if( attrKey == wxS( "Name" ) || attrKey == wxS( "Value" )
1300 || attrKey == wxS( "Global Net Name" ) || attrKey == wxS( "Designator" )
1301 || attrKey == wxS( "Description" ) || attrKey == wxS( "Device" )
1302 || attrKey == wxS( "Footprint" ) || attrKey == wxS( "Symbol" )
1303 || attrKey == wxS( "Unique ID" ) )
1304 {
1305 continue;
1306 }
1307
1308 if( attr.value.IsEmpty() )
1309 continue;
1310
1311 SCH_FIELD* text = schSym->FindFieldCaseInsensitive( attrKey );
1312
1313 if( !text )
1314 text = schSym->AddField( SCH_FIELD( schSym.get(), FIELD_T::USER, attrKey ) );
1315
1316 text->SetPosition( schSym->GetPosition() );
1317
1318 ApplyAttrToField( fontStyles, text, attr, false, true, mergedAttrValues,
1319 schSym.get() );
1320 }
1321 }
1322
1323 for( const EASYEDAPRO::PIN_INFO& pinInfo : esymInfo.pins )
1324 {
1325 wxString pinKey = parentId + pinInfo.pin.id;
1326 auto pinLines = get_opt( parentedLines, pinKey );
1327
1328 if( !pinLines )
1329 continue;
1330
1331 for( const nlohmann::json& pinLine : *pinLines )
1332 {
1333 if( pinLine.at( 0 ) != "ATTR" )
1334 continue;
1335
1336 EASYEDAPRO::SCH_ATTR attr = pinLine;
1337
1338 if( attr.key != wxS( "NO_CONNECT" ) )
1339 continue;
1340
1341 for( SCH_PIN* schPin : schSym->GetPinsByNumber( pinInfo.number ) )
1342 {
1343 VECTOR2I pos = schSym->GetPinPhysicalPosition( schPin->GetLibPin() );
1344
1345 std::unique_ptr<SCH_NO_CONNECT> noConn =
1346 std::make_unique<SCH_NO_CONNECT>( pos );
1347
1348 createdItems.push_back( std::move( noConn ) );
1349 }
1350 }
1351 }
1352
1353 createdItems.push_back( std::move( schSym ) );
1354 }
1355 else // Not component
1356 {
1357 std::vector<SHAPE_LINE_CHAIN> wireLines;
1358
1359 if( wire )
1360 {
1361 for( const std::vector<double>& ptArr : wire->geometry )
1362 {
1364
1365 for( size_t i = 1; i < ptArr.size(); i += 2 )
1366 chain.Append( ScalePos( VECTOR2D( ptArr[i - 1], ptArr[i] ) ) );
1367
1368 if( chain.PointCount() < 2 )
1369 continue;
1370
1371 wireLines.push_back( chain );
1372
1373 for( int segId = 0; segId < chain.SegmentCount(); segId++ )
1374 {
1375 const SEG& seg = chain.CSegment( segId );
1376
1377 std::unique_ptr<SCH_LINE> schLine =
1378 std::make_unique<SCH_LINE>( seg.A, LAYER_WIRE );
1379 schLine->SetEndPoint( seg.B );
1380
1381 createdItems.push_back( std::move( schLine ) );
1382 }
1383 }
1384 }
1385
1386 auto netAttr = get_opt( attributes, "NET" );
1387
1388 if( netAttr )
1389 {
1390 if( !netAttr->valVisible || netAttr->value.IsEmpty() )
1391 continue;
1392
1393 VECTOR2I kpos = ScalePos( *netAttr->position );
1394 VECTOR2I nearestPos = kpos;
1395 SEG::ecoord min_dist_sq = VECTOR2I::ECOORD_MAX;
1396
1397 for( const SHAPE_LINE_CHAIN& chain : wireLines )
1398 {
1399 VECTOR2I nearestPt = chain.NearestPoint( kpos, false );
1400 SEG::ecoord dist_sq = ( nearestPt - kpos ).SquaredEuclideanNorm();
1401
1402 if( dist_sq < min_dist_sq )
1403 {
1404 min_dist_sq = dist_sq;
1405 nearestPos = nearestPt;
1406 }
1407 }
1408
1409 std::unique_ptr<SCH_LABEL> label = std::make_unique<SCH_LABEL>();
1410
1411 label->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1412 label->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1413
1414 for( double i = netAttr->rotation; i > 0; i -= 90 )
1415 label->Rotate90( true );
1416
1417 label->SetPosition( nearestPos );
1418 label->SetText( netAttr->value );
1419
1420 ApplyFontStyle( fontStyles, label, netAttr->fontStyle );
1421
1422 createdItems.push_back( std::move( label ) );
1423 }
1424 }
1425 }
1426
1427 // Adjust page to content
1428 BOX2I sheetBBox;
1429
1430 for( std::unique_ptr<SCH_ITEM>& ptr : createdItems )
1431 {
1432 if( ptr->Type() == SCH_SYMBOL_T )
1433 sheetBBox.Merge( static_cast<SCH_SYMBOL*>( ptr.get() )->GetBodyAndPinsBoundingBox() );
1434 else
1435 sheetBBox.Merge( ptr->GetBoundingBox() );
1436 }
1437
1438 SCH_SCREEN* screen = aRootSheet->GetScreen();
1439 PAGE_INFO pageInfo = screen->GetPageSettings();
1440
1441 int alignGrid = schIUScale.MilsToIU( 50 );
1442
1443 VECTOR2D offset( -sheetBBox.GetLeft(), -sheetBBox.GetTop() );
1444 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1445 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1446
1447 pageInfo.SetWidthMils( schIUScale.IUToMils( sheetBBox.GetWidth() ) );
1448 pageInfo.SetHeightMils( schIUScale.IUToMils( sheetBBox.GetHeight() ) );
1449
1450 screen->SetPageSettings( pageInfo );
1451
1452 for( std::unique_ptr<SCH_ITEM>& ptr : createdItems )
1453 {
1454 ptr->Move( offset );
1455 screen->Append( ptr.release() );
1456 }
1457}
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:256
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
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:160
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
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:144
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:190
void SetGlobalPower()
SCH_FIELD * FindFieldCaseInsensitive(const wxString &aFieldName)
wxString GetName() const override
Definition lib_symbol.h:183
void SetUnitCount(int aCount, bool aDuplicateDrawItems)
Set the units per symbol count.
void SetDescription(const wxString &aDescription)
Set the Description field text value.
void SetKeyWords(const wxString &aKeyWords)
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
wxString GetFootprint() const override
For items with footprint fields.
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()
Definition lib_symbol.h:451
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:149
SCH_SHEET_PATH & CurrentSheet() const
Definition schematic.h:312
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:139
void SetText(const wxString &aText) override
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
virtual void MirrorHorizontally(int aCenter)
Mirror item horizontally about aCenter.
Definition sch_item.h:430
virtual void Rotate(const VECTOR2I &aCenter, bool aRotateCCW)
Rotate the item around aCenter 90 degrees in the clockwise direction.
Definition sch_item.h:446
const PAGE_INFO & GetPageSettings() const
Definition sch_screen.h:144
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition sch_screen.h:145
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
SCH_SCREEN * GetScreen() const
Definition sch_sheet.h:151
Schematic symbol object.
Definition sch_symbol.h:75
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...
Spin style for labels of all kinds on schematics.
Definition sch_label.h:38
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:174
virtual void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
Definition symbol.h:168
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
#define _(s)
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
#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:57
void ConvertImageToLibShapes(LIB_SYMBOL *aSymbol, int unit, wxImage img, VECTOR2D pixelScale, VECTOR2D offset)
#define THROW_IO_ERRORF(msg,...)
const wxChar *const traceEasyEdaIo
@ LAYER_DEVICE
Definition layer_ids.h:494
@ LAYER_WIRE
Definition layer_ids.h:480
@ LAYER_NOTES
Definition layer_ids.h:495
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:437
@ 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:39
@ SYM_MIRROR_Y
Definition symbol.h:41
@ SYM_ORIENT_180
Definition symbol.h:38
@ SYM_MIRROR_X
Definition symbol.h:40
@ SYM_ORIENT_90
Definition symbol.h:37
@ SYM_ORIENT_0
Definition symbol.h:36
@ 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:159
@ SCH_SYMBOL_T
Definition typeinfo.h:168
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707