KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_easyeda_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 (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include "sch_easyeda_parser.h"
26
27#include <sch_io/sch_io_mgr.h>
28#include <schematic.h>
29#include <sch_sheet.h>
30#include <sch_line.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 <sch_bitmap.h>
37#include <string_utils.h>
38#include <bezier_curves.h>
39#include <wx/base64.h>
40#include <wx/mstream.h>
41#include <gfx_import_utils.h>
45
46
48 PROGRESS_REPORTER* aProgressReporter )
49{
50 m_schematic = aSchematic;
51}
52
53
55{
56}
57
58
59static std::vector<std::vector<wxString>> RegexMatchAll( wxRegEx& aRegex, const wxString& aString )
60{
61 std::vector<std::vector<wxString>> allMatches;
62
63 size_t start = 0;
64 size_t len = 0;
65 size_t prevstart = 0;
66
67 wxString str = aString;
68
69 while( aRegex.Matches( str ) )
70 {
71 std::vector<wxString> matches;
72 aRegex.GetMatch( &start, &len );
73
74 for( size_t i = 0; i < aRegex.GetMatchCount(); i++ )
75 matches.emplace_back( aRegex.GetMatch( str, i ) );
76
77 allMatches.emplace_back( matches );
78
79 prevstart = start + len;
80 str = str.Mid( prevstart );
81 }
82
83 return allMatches;
84}
85
86
94static std::vector<std::pair<wxString, std::vector<double>>>
95ParseImageTransform( const wxString& transformData )
96{
97 wxRegEx transformRegex( "(rotate|translate|scale)\\(([\\w\\s,\\.\\-]*)\\)", wxRE_ICASE );
98 std::vector<std::vector<wxString>> allMatches = RegexMatchAll( transformRegex, transformData );
99
100 std::vector<std::pair<wxString, std::vector<double>>> transformCmds;
101
102 for( int cmdId = allMatches.size() - 1; cmdId >= 0; cmdId-- )
103 {
104 std::vector<wxString>& groups = allMatches[cmdId];
105
106 if( groups.size() != 3 )
107 continue;
108
109 const wxString& cmdName = groups[1].Strip( wxString::both ).Lower();
110 const wxString& cmdArgsStr = groups[2];
111
112 wxArrayString cmdParts = wxSplit( cmdArgsStr, ',', '\0' );
113 std::vector<double> cmdArgs;
114
115 for( const wxString& cmdPart : cmdParts )
116 {
117 double arg = 0;
118 wxASSERT( cmdPart.Strip( wxString::both ).ToCDouble( &arg ) );
119
120 cmdArgs.push_back( arg );
121 }
122
123 transformCmds.emplace_back( cmdName, cmdArgs );
124 }
125
126 return transformCmds;
127}
128
129
130static LIB_ID EasyEdaToKiCadLibID( const wxString& aLibName, const wxString& aLibReference )
131{
132 wxString libReference = EscapeString( aLibReference, CTX_LIBID );
133
134 wxString key = !aLibName.empty() ? ( aLibName + ':' + libReference ) : libReference;
135
136 LIB_ID libId;
137 libId.Parse( key, true );
138
139 return libId;
140}
141
142
143static LINE_STYLE ConvertStrokeStyle( const wxString& aStyle )
144{
145 if( aStyle == wxS( "0" ) )
146 return LINE_STYLE::SOLID;
147 else if( aStyle == wxS( "1" ) )
148 return LINE_STYLE::DASH;
149 else if( aStyle == wxS( "2" ) )
150 return LINE_STYLE::DOT;
151
152 return LINE_STYLE::DEFAULT;
153}
154
155
156static ELECTRICAL_PINTYPE ConvertElecType( const wxString& aType )
157{
158 if( aType == wxS( "0" ) )
160 else if( aType == wxS( "1" ) )
162 else if( aType == wxS( "2" ) )
164 else if( aType == wxS( "3" ) )
166 else if( aType == wxS( "4" ) )
168
170}
171
172
174 REPORTER* aReporter )
175{
177 {
179 aKsymbol->AddDrawItem( line1 );
181 line1->AddPoint( { 0, 0 } );
182 line1->AddPoint( { 0, schIUScale.MilsToIU( -50 ) } );
183
185 {
187 aKsymbol->AddDrawItem( circle );
189 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( -75 ) } );
190 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 25 ), 0 ) );
191 }
192 else
193 {
195 aKsymbol->AddDrawItem( line2 );
197 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( -50 ) } );
198 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( -50 ) } );
199 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( -100 ) } );
200 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( -50 ) } );
201 }
202
203 return { 0, schIUScale.MilsToIU( 150 ) };
204 }
205 else if( aStyle == EASYEDA::POWER_FLAG_STYLE::WAVE )
206 {
208 aKsymbol->AddDrawItem( line );
210 line->AddPoint( { 0, 0 } );
211 line->AddPoint( { 0, schIUScale.MilsToIU( -72 ) } );
212
214 aKsymbol->AddDrawItem( bezier );
216 bezier->AddPoint( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( -50 ) } );
217 bezier->AddPoint( { schIUScale.MilsToIU( 30 ), schIUScale.MilsToIU( -87 ) } );
218 bezier->AddPoint( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( -63 ) } );
219 bezier->AddPoint( { schIUScale.MilsToIU( -30 ), schIUScale.MilsToIU( -100 ) } );
220
221 return { 0, schIUScale.MilsToIU( 150 ) };
222 }
227 {
229 aKsymbol->AddDrawItem( line1 );
231 line1->AddPoint( { 0, 0 } );
232 line1->AddPoint( { 0, schIUScale.MilsToIU( -100 ) } );
233
235 {
237 aKsymbol->AddDrawItem( line2 );
239 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( -100 ) } );
240 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( -100 ) } );
241
243 aKsymbol->AddDrawItem( line3 );
245 line3->AddPoint( { schIUScale.MilsToIU( -70 ), schIUScale.MilsToIU( -120 ) } );
246 line3->AddPoint( { schIUScale.MilsToIU( 70 ), schIUScale.MilsToIU( -120 ) } );
247
249 aKsymbol->AddDrawItem( line4 );
251 line4->AddPoint( { schIUScale.MilsToIU( -40 ), schIUScale.MilsToIU( -140 ) } );
252 line4->AddPoint( { schIUScale.MilsToIU( 40 ), schIUScale.MilsToIU( -140 ) } );
253
255 aKsymbol->AddDrawItem( line5 );
257 line5->AddPoint( { schIUScale.MilsToIU( -10 ), schIUScale.MilsToIU( -160 ) } );
258 line5->AddPoint( { schIUScale.MilsToIU( 10 ), schIUScale.MilsToIU( -160 ) } );
259 }
261 {
263 aKsymbol->AddDrawItem( line2 );
265 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( -100 ) } );
266 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( -100 ) } );
267 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( -160 ) } );
268 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( -100 ) } );
269 }
270 else if( aStyle == EASYEDA::POWER_FLAG_STYLE::EARTH )
271 {
273 aKsymbol->AddDrawItem( line2 );
275 line2->AddPoint( { schIUScale.MilsToIU( -150 ), schIUScale.MilsToIU( -200 ) } );
276 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( -100 ) } );
277 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( -100 ) } );
278 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( -200 ) } );
279
281 aKsymbol->AddDrawItem( line3 );
283 line3->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( -100 ) } );
284 line3->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( -200 ) } );
285 }
286 else // EASYEDA::POWER_FLAG_STYLE::GOST_ARROW
287 {
289 aKsymbol->AddDrawItem( line2 );
291 line2->AddPoint( { schIUScale.MilsToIU( -25 ), schIUScale.MilsToIU( -50 ) } );
292 line2->AddPoint( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( -100 ) } );
293 line2->AddPoint( { schIUScale.MilsToIU( 25 ), schIUScale.MilsToIU( -50 ) } );
294
295 return { 0, schIUScale.MilsToIU( 150 ) }; // special case
296 }
297
298 return { 0, schIUScale.MilsToIU( 250 ) };
299 }
302 {
304 aKsymbol->AddDrawItem( line1 );
306 line1->AddPoint( { 0, 0 } );
307 line1->AddPoint( { 0, schIUScale.MilsToIU( -160 ) } );
308
310 aKsymbol->AddDrawItem( line2 );
312 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( -160 ) } );
313 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( -160 ) } );
314
316 aKsymbol->AddDrawItem( line3 );
318 line3->AddPoint( { schIUScale.MilsToIU( -60 ), schIUScale.MilsToIU( -200 ) } );
319 line3->AddPoint( { schIUScale.MilsToIU( 60 ), schIUScale.MilsToIU( -200 ) } );
320
322 aKsymbol->AddDrawItem( line4 );
324 line4->AddPoint( { schIUScale.MilsToIU( -20 ), schIUScale.MilsToIU( -240 ) } );
325 line4->AddPoint( { schIUScale.MilsToIU( 20 ), schIUScale.MilsToIU( -240 ) } );
326
328 return { 0, schIUScale.MilsToIU( 300 ) };
329
331 aKsymbol->AddDrawItem( circle );
333 circle->SetPosition( { schIUScale.MilsToIU( 0 ), schIUScale.MilsToIU( -160 ) } );
334 circle->SetEnd( circle->GetPosition() + VECTOR2I( schIUScale.MilsToIU( 120 ), 0 ) );
335
336 return { 0, schIUScale.MilsToIU( 350 ) };
337 }
338 else if( aStyle == EASYEDA::POWER_FLAG_STYLE::GOST_BAR )
339 {
341 aKsymbol->AddDrawItem( line1 );
343 line1->AddPoint( { 0, 0 } );
344 line1->AddPoint( { 0, schIUScale.MilsToIU( -200 ) } );
345
347 aKsymbol->AddDrawItem( line2 );
349 line2->AddPoint( { schIUScale.MilsToIU( -100 ), schIUScale.MilsToIU( -200 ) } );
350 line2->AddPoint( { schIUScale.MilsToIU( 100 ), schIUScale.MilsToIU( -200 ) } );
351
352 return { 0, schIUScale.MilsToIU( 250 ) };
353 }
354 else
355 {
356 if( aStyle != EASYEDA::POWER_FLAG_STYLE::BAR )
357 {
358 aReporter->Report( _( "Power Port with unknown style imported as 'Bar' type." ),
360 }
361
363 aKsymbol->AddDrawItem( line1 );
365 line1->AddPoint( { 0, 0 } );
366 line1->AddPoint( { 0, schIUScale.MilsToIU( -100 ) } );
367
369 aKsymbol->AddDrawItem( line2 );
371 line2->AddPoint( { schIUScale.MilsToIU( -50 ), schIUScale.MilsToIU( -100 ) } );
372 line2->AddPoint( { schIUScale.MilsToIU( 50 ), schIUScale.MilsToIU( -100 ) } );
373
374 return { 0, schIUScale.MilsToIU( 150 ) };
375 }
376}
377
378
380 std::map<wxString, wxString> paramMap,
381 wxArrayString aShapes )
382{
383 for( wxString shapeStr : aShapes )
384 {
385 wxArrayString arr = wxSplit( shapeStr, '~', '\0' );
386
387 wxString elType = arr[0];
388 if( elType == wxS( "PL" ) || elType == wxS( "PG" ) )
389 {
390 wxArrayString ptArr = wxSplit( arr[1], ' ', '\0' );
391 wxString strokeColor = arr[2];
392 double lineWidth = Convert( arr[3] );
393 LINE_STYLE strokeStyle = ConvertStrokeStyle( arr[4] );
394 wxString fillColor = arr[5].Lower();
395 //bool locked = arr[7] != wxS( "0" );
396
397 SHAPE_LINE_CHAIN chain;
398
399 for( size_t i = 1; i < ptArr.size(); i += 2 )
400 {
401 chain.Append(
402 RelPosSym( VECTOR2I( Convert( ptArr[i - 1] ), Convert( ptArr[i] ) ) ) );
403 }
404
405 auto line = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
406
407 if( elType == wxS( "PG" ) )
408 chain.SetClosed( true );
409
410 if( chain.PointCount() < 2 )
411 continue;
412
413 for( int i = 0; i < chain.PointCount(); i++ )
414 line->AddPoint( chain.CPoint( i ) );
415
416 if( chain.IsClosed() )
417 line->AddPoint( chain.CPoint( 0 ) );
418
419 line->SetUnit( 0 );
420 line->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) );
421
422 if( fillColor != wxS( "none" ) )
423 {
424 line->SetFilled( true );
425
426 if( fillColor == strokeColor )
427 line->SetFillMode( FILL_T::FILLED_SHAPE );
428 else
429 line->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
430 }
431
432 aSymbol->AddDrawItem( line.release() );
433 }
434 else if( elType == wxS( "PT" ) ) // Freedraw
435 {
436 wxString pointsData = arr[1];
437 wxString strokeColor = arr[2];
438 double lineWidth = Convert( arr[3] );
439 LINE_STYLE strokeStyle = ConvertStrokeStyle( arr[4] );
440 wxString fillColor = arr[5].Lower();
441 //bool locked = arr[7] != wxS( "0" );
442
443 std::vector<SHAPE_LINE_CHAIN> lineChains =
444 ParseLineChains( pointsData, schIUScale.MilsToIU( 10 ), false );
445
446 for( SHAPE_LINE_CHAIN outline : lineChains )
447 {
448 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
449
450 outline.Mirror( false, true );
451
452 if( outline.IsClosed() )
453 outline.Append( outline.CPoint( 0 ), true );
454
455 for( const VECTOR2I& pt : outline.CPoints() )
456 shape->AddPoint( pt );
457
458 shape->SetUnit( 0 );
459 shape->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) );
460
461 if( fillColor != wxS( "none" ) )
462 {
463 shape->SetFilled( true );
464
465 if( fillColor == strokeColor )
466 shape->SetFillMode( FILL_T::FILLED_SHAPE );
467 else
468 shape->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
469 }
470
471 aSymbol->AddDrawItem( shape.release() );
472 }
473 }
474 else if( elType == wxS( "Pimage" ) )
475 {
476 //double angle = Convert( arr[4] ); // TODO
477 VECTOR2D start( Convert( arr[6] ), Convert( arr[7] ) );
478 VECTOR2D size( Convert( arr[8] ), Convert( arr[9] ) );
479 wxString imageUrl = arr[10];
480
481 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
482 {
483 wxArrayString paramsArr =
484 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
485
486 wxString data = imageUrl.AfterFirst( ',' );
487
488 if( paramsArr.size() > 0 )
489 {
490 wxString mimeType = paramsArr[0];
491 wxMemoryBuffer buf = wxBase64Decode( data );
492
493 if( mimeType == wxS( "image/svg+xml" ) )
494 {
495 VECTOR2D offset = RelPosSym( start );
496
497 SVG_IMPORT_PLUGIN svgImportPlugin;
498 GRAPHICS_IMPORTER_LIB_SYMBOL libsymImporter( aSymbol, 0 );
499
500 svgImportPlugin.SetImporter( &libsymImporter );
501 svgImportPlugin.LoadFromMemory( buf );
502
503 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
504 svgImportPlugin.GetImageHeight() );
505
506 VECTOR2D pixelScale( schIUScale.IUTomm( ScaleSize( size.x ) ) / imSize.x,
507 schIUScale.IUTomm( -ScaleSize( size.y ) ) / imSize.y );
508
509 libsymImporter.SetScale( pixelScale );
510
511 VECTOR2D offsetMM( schIUScale.IUTomm( offset.x ),
512 schIUScale.IUTomm( offset.y ) );
513
514 libsymImporter.SetImportOffsetMM( offsetMM );
515
516 svgImportPlugin.Import();
517
518 for( std::unique_ptr<EDA_ITEM>& item : libsymImporter.GetItems() )
519 aSymbol->AddDrawItem( static_cast<SCH_ITEM*>( item.release() ) );
520 }
521 else
522 {
523 wxMemoryInputStream memis( buf.GetData(), buf.GetDataLen() );
524
525 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
526 & ~wxImage::Load_Verbose );
527 wxImage img;
528 if( img.LoadFile( memis, mimeType ) )
529 {
530 int dimMul = img.GetWidth() * img.GetHeight();
531 double maxPixels = 30000;
532
533 if( dimMul > maxPixels )
534 {
535 double scale = sqrt( maxPixels / dimMul );
536 img.Rescale( img.GetWidth() * scale, img.GetHeight() * scale );
537 }
538
539 VECTOR2D pixelScale( ScaleSize( size.x ) / img.GetWidth(),
540 -ScaleSize( size.y ) / img.GetHeight() );
541
542 ConvertImageToLibShapes( aSymbol, 0, img, pixelScale,
543 RelPosSym( start ) );
544 }
545 }
546 }
547 }
548 }
549 else if( elType == wxS( "A" ) )
550 {
551 wxString data = arr[1];
552 wxString strokeColor = arr[3];
553 double lineWidth = Convert( arr[4] );
554 LINE_STYLE strokeStyle = ConvertStrokeStyle( arr[5] );
555 wxString fillColor = arr[6].Lower();
556 //bool locked = arr[8] != wxS( "0" );
557
558 VECTOR2D start, end;
559 VECTOR2D rad( 10, 10 );
560 bool isFar = false;
561 bool cw = false;
562
563 size_t pos = 0;
564 auto readNumber = [&]( wxString& aOut )
565 {
566 wxUniChar ch = data[pos];
567
568 while( ch == ' ' || ch == ',' )
569 ch = data[++pos];
570
571 while( isdigit( ch ) || ch == '.' || ch == '-' )
572 {
573 aOut += ch;
574 pos++;
575
576 if( pos == data.size() )
577 break;
578
579 ch = data[pos];
580 }
581 };
582
583 do
584 {
585 wxUniChar sym = data[pos++];
586
587 if( sym == 'M' )
588 {
589 wxString xStr, yStr;
590 readNumber( xStr );
591 readNumber( yStr );
592
593 start = VECTOR2D( Convert( xStr ), Convert( yStr ) );
594 }
595 else if( sym == 'A' )
596 {
597 wxString radX, radY, unknown, farFlag, cwFlag, endX, endY;
598 readNumber( radX );
599 readNumber( radY );
600 readNumber( unknown );
601 readNumber( farFlag );
602 readNumber( cwFlag );
603 readNumber( endX );
604 readNumber( endY );
605
606 isFar = farFlag == wxS( "1" );
607 cw = cwFlag == wxS( "1" );
608 rad = VECTOR2D( Convert( radX ), Convert( radY ) );
609 end = VECTOR2D( Convert( endX ), Convert( endY ) );
610 }
611 } while( pos < data.size() );
612
613 VECTOR2D delta = end - start;
614
615 double avgRad = ( rad.x + rad.y ) / 2;
616 double d = delta.EuclideanNorm();
617 double h = sqrt( std::max( 0.0, avgRad * avgRad - d * d / 4 ) );
618
619 //( !far && cw ) => h
620 //( far && cw ) => -h
621 //( !far && !cw ) => -h
622 //( far && !cw ) => h
623 VECTOR2D arcCenter =
624 start + delta / 2 + delta.Perpendicular().Resize( ( isFar ^ cw ) ? h : -h );
625
626 if( cw )
627 std::swap( start, end );
628
629 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::ARC, LAYER_DEVICE );
630
631 shape->SetStart( RelPosSym( start ) );
632 shape->SetEnd( RelPosSym( end ) );
633 shape->SetCenter( RelPosSym( arcCenter ) );
634
635 shape->SetUnit( 0 );
636 shape->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) );
637
638 if( fillColor != wxS( "none" ) )
639 {
640 shape->SetFilled( true );
641
642 if( fillColor == strokeColor )
643 shape->SetFillMode( FILL_T::FILLED_SHAPE );
644 else
645 shape->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
646 }
647
648 aSymbol->AddDrawItem( shape.release() );
649 }
650 else if( elType == wxS( "R" ) )
651 {
652 VECTOR2D start( Convert( arr[1] ), Convert( arr[2] ) );
653
654 VECTOR2D cr;
655 cr.x = !arr[3].empty() ? Convert( arr[3] ) : 0,
656 cr.y = !arr[4].empty() ? Convert( arr[4] ) : 0; // TODO: corner radius
657
658 VECTOR2D size( Convert( arr[5] ), Convert( arr[6] ) );
659 wxString strokeColor = arr[7];
660 double lineWidth = Convert( arr[8] );
661 LINE_STYLE strokeStyle = ConvertStrokeStyle( arr[9] );
662 wxString fillColor = arr[10].Lower();
663 //bool locked = arr[12] != wxS( "0" );
664
665 //if( cr.x == 0 )
666 {
667 auto rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE, LAYER_DEVICE );
668
669 rect->SetStart( RelPosSym( start ) );
670 rect->SetEnd( RelPosSym( start + size ) );
671
672 rect->SetUnit( 0 );
673 rect->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) );
674
675 if( fillColor != wxS( "none" ) )
676 {
677 rect->SetFilled( true );
678
679 if( fillColor == strokeColor )
680 rect->SetFillMode( FILL_T::FILLED_SHAPE );
681 else
682 rect->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
683 }
684
685 aSymbol->AddDrawItem( rect.release() );
686 }
687 // TODO: rounded rectangles
688 }
689 else if( elType == wxS( "E" ) )
690 {
691 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
692
693 VECTOR2D center( Convert( arr[1] ), Convert( arr[2] ) );
694 VECTOR2D radius( Convert( arr[3] ), Convert( arr[4] ) ); // TODO: corner radius
695 wxString strokeColor = arr[5];
696 double lineWidth = Convert( arr[6] );
697 LINE_STYLE strokeStyle = ConvertStrokeStyle( arr[7] );
698 wxString fillColor = arr[8].Lower();
699 //bool locked = arr[10] != wxS( "0" );
700
701 circle->SetCenter( RelPosSym( center ) );
702 circle->SetEnd( RelPosSym( center + VECTOR2I( radius.x, 0 ) ) );
703
704 circle->SetUnit( 0 );
705 circle->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) );
706
707 if( fillColor != wxS( "none" ) )
708 {
709 circle->SetFilled( true );
710
711 if( fillColor == strokeColor )
712 circle->SetFillMode( FILL_T::FILLED_SHAPE );
713 else
714 circle->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
715 }
716
717 aSymbol->AddDrawItem( circle.release() );
718 }
719 else if( elType == wxS( "P" ) )
720 {
721 wxString sepShapeStr = shapeStr;
722 sepShapeStr.Replace( wxS( "^^" ), wxS( "\n" ) );
723
724 wxArrayString segments = wxSplit( sepShapeStr, '\n', '\0' );
725 wxArrayString mainParts = wxSplit( segments[0], '~', '\0' );
726 wxArrayString pinDotParts = wxSplit( segments[1], '~', '\0' );
727 wxArrayString pinPathColorParts = wxSplit( segments[2], '~', '\0' );
728 wxArrayString pinNameParts = wxSplit( segments[3], '~', '\0' );
729 wxArrayString pinNumParts = wxSplit( segments[4], '~', '\0' );
730
731 //bool show = mainParts[1].Lower() != wxS( "none" );
732 ELECTRICAL_PINTYPE elecType = ConvertElecType( mainParts[2] );
733 wxString pinNumber = mainParts[3];
734 VECTOR2D pinPos( Convert( mainParts[4] ), Convert( mainParts[5] ) );
735 //int pinRot = Convert( mainParts[6] );
736
737 VECTOR2D pinDotPos( Convert( pinDotParts[0] ), Convert( pinDotParts[1] ) );
738
739 bool nameVisible = pinNameParts[0] != wxS( "0" );
740 wxString pinName = pinNameParts[4];
741
742 bool numVisible = pinNumParts[0] != wxS( "0" );
743 wxString ptSize = pinNumParts[4];
744
745 VECTOR2D startPoint;
746 bool vertical = false;
747 double pinLen = 0;
748
749 // M360,290h10 or M 420 300 h -5
750 wxString lineData = pinPathColorParts[0];
751 wxRegEx regex( wxS( "^M\\s*([-\\d.]+)[,\\s]([-\\d.]+)\\s*([h|v])\\s*([-\\d.]+)\\s*$" ) );
752
753 if( regex.Matches( lineData ) )
754 {
755 startPoint.x = Convert( regex.GetMatch( lineData, 1 ) );
756 startPoint.y = Convert( regex.GetMatch( lineData, 2 ) );
757
758 vertical = regex.GetMatch( lineData, 3 ).Contains( wxS( "v" ) );
759 pinLen = Convert( regex.GetMatch( lineData, 4 ) );
760 }
761
762 int pinRotation = 0;
763
764 if( !vertical )
765 {
766 if( startPoint.x == pinPos.x && pinLen < 0 )
767 pinRotation = 180;
768 else if( startPoint.x == pinPos.x && pinLen > 0 )
769 pinRotation = 0;
770 else if( startPoint.x != pinPos.x && pinLen < 0 )
771 pinRotation = 0;
772 else if( startPoint.x != pinPos.x && pinLen > 0 )
773 pinRotation = 180;
774 }
775 else
776 {
777 if( startPoint.y == pinPos.y && pinLen < 0 )
778 pinRotation = 90;
779 else if( startPoint.y == pinPos.y && pinLen > 0 )
780 pinRotation = 270;
781 else if( startPoint.y != pinPos.y && pinLen < 0 )
782 pinRotation = 270;
783 else if( startPoint.y != pinPos.y && pinLen > 0 )
784 pinRotation = 90;
785 }
786
787 PIN_ORIENTATION orient = PIN_ORIENTATION::PIN_RIGHT;
788
789 if( pinRotation == 0 )
790 orient = PIN_ORIENTATION::PIN_RIGHT;
791 else if( pinRotation == 90 )
792 orient = PIN_ORIENTATION::PIN_UP;
793 else if( pinRotation == 180 )
794 orient = PIN_ORIENTATION::PIN_LEFT;
795 else if( pinRotation == 270 )
796 orient = PIN_ORIENTATION::PIN_DOWN;
797
798 int pinUnit = 0;
799 int kPinLen = ScaleSize( std::abs( pinLen ) );
800
801 if( segments.size() > 5 )
802 {
803 wxArrayString dotParts = wxSplit( segments[5], '~', '\0' );
804 wxArrayString clockParts = wxSplit( segments[6], '~', '\0' );
805
806 if( dotParts.size() == 3 && clockParts.size() == 2 )
807 {
808 if( dotParts[0] == wxS( "1" ) )
809 {
810 VECTOR2D dotPos( Convert( dotParts[1] ), Convert( dotParts[2] ) );
811
812 auto circle = std::make_unique<SCH_SHAPE>( SHAPE_T::CIRCLE, LAYER_DEVICE );
813
814 circle->SetCenter( RelPosSym( dotPos ) );
815 circle->SetEnd( RelPosSym(
816 pinPos + ( dotPos - pinPos ).Resize( std::abs( pinLen ) ) ) );
817
818 circle->SetUnit( 0 );
819
820 aSymbol->AddDrawItem( circle.release() );
821 }
822
823 if( clockParts[0] == wxS( "1" ) )
824 {
825 std::vector<SHAPE_LINE_CHAIN> lineChains =
826 ParseLineChains( clockParts[1], schIUScale.MilsToIU( 10 ), false );
827
828 for( SHAPE_LINE_CHAIN outline : lineChains )
829 {
830 auto shape = std::make_unique<SCH_SHAPE>( SHAPE_T::POLY, LAYER_DEVICE );
831
832 outline.Mirror( false, true );
833
834 if( outline.IsClosed() )
835 outline.Append( outline.CPoint( 0 ), true );
836
837 for( const VECTOR2I& pt : outline.CPoints() )
838 shape->AddPoint( pt );
839
840 shape->SetUnit( 0 );
841
842 aSymbol->AddDrawItem( shape.release() );
843 }
844 }
845 }
846 }
847
848 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( aSymbol );
849
850 pin->SetName( pinName );
851 pin->SetNumber( pinNumber );
852 pin->SetOrientation( orient );
853 pin->SetType( elecType );
854 pin->SetLength( kPinLen );
855 pin->SetPosition( RelPosSym( pinPos ) );
856 pin->SetUnit( pinUnit );
857
858 if( pin->GetNumberTextSize() * int( pinNumber.size() ) > kPinLen )
859 pin->SetNumberTextSize( kPinLen / pinNumber.size() );
860
861 if( !nameVisible )
862 pin->SetNameTextSize( schIUScale.MilsToIU( 1 ) );
863
864 if( !numVisible )
865 pin->SetNumberTextSize( schIUScale.MilsToIU( 1 ) );
866
867 aSymbol->AddDrawItem( pin.release() );
868 }
869 else if( elType == wxS( "T" ) )
870 {
871 wxString textType = arr[1];
872 VECTOR2D pos( Convert( arr[2] ), Convert( arr[3] ) );
873 int angle = Convert( arr[4] );
874 wxString color = arr[5];
875 wxString fontname = arr[6];
876 wxString fontSize = arr[7];
877 wxString baselineAlign = arr[10];
878 wxString textStr = arr[12];
879 bool visible = arr[13] != wxS( "0" );
880
881 textStr.Replace( wxS( "\\n" ), wxS( "\n" ) );
882 textStr = UnescapeHTML( textStr );
883
884 wxString halignStr = arr[14]; // Empty, start, middle, end, inherit
885
886 bool added = false;
887 EDA_TEXT* textItem = nullptr;
888
889 if( textType == wxS( "P" ) )
890 {
891 textItem = &aSymbol->GetReferenceField();
892 textItem->SetTextPos( RelPosSym( pos ) );
893 textItem->SetText( textStr );
894 }
895 else if( textType == wxS( "N" ) )
896 {
897 textItem = &aSymbol->GetValueField();
898 textItem->SetTextPos( RelPosSym( pos ) );
899 textItem->SetText( textStr );
900 }
901 else
902 {
903 textItem = new SCH_TEXT( RelPosSym( pos ), textStr, LAYER_DEVICE );
904 added = true;
905 }
906
907 textItem->SetTextAngleDegrees( ( 360 - angle ) % 360 );
909
910 if( halignStr == wxS( "middle" ) )
912 else if( halignStr == wxS( "end" ) )
914 else
916
917 textItem->SetFont( KIFONT::FONT::GetFont( fontname ) );
918 textItem->SetVisible( visible );
919
920 double ptSize = 7;
921
922 if( !fontSize.IsEmpty() )
923 {
924 if( fontSize.EndsWith( wxS( "pt" ) ) )
925 ptSize = Convert( fontSize.BeforeFirst( 'p' ) );
926 else if( fontSize.IsNumber() )
927 ptSize = Convert( fontSize );
928 }
929
930 double ktextSize = ScaleSize( ptSize );
931
932 if( textStr.Contains( wxS( "\n" ) ) )
933 ktextSize *= 0.8;
934 else
935 ktextSize *= 0.95;
936
937 textItem->SetTextSize( VECTOR2I( ktextSize, ktextSize ) );
938
939 TransformTextToBaseline( textItem, baselineAlign, true );
940
941 if( added )
942 aSymbol->AddDrawItem( dynamic_cast<SCH_ITEM*>( textItem ) );
943 }
944 }
945}
946
947
949 std::map<wxString, wxString> aParams,
950 wxArrayString aShapes )
951{
952 std::unique_ptr<LIB_SYMBOL> ksymbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
953
954 m_relOrigin = aOrigin;
955
956 wxString symbolName = wxS( "Unknown" );
957
958 if( aParams.find( wxS( "name" ) ) != aParams.end() )
959 symbolName = aParams.at( wxS( "name" ) );
960 else if( aParams.find( wxS( "spiceSymbolName" ) ) != aParams.end() )
961 symbolName = aParams.at( wxS( "spiceSymbolName" ) );
962
963 wxString symbolPrefix;
964
965 if( aParams.find( wxS( "pre" ) ) != aParams.end() )
966 symbolPrefix = aParams.at( wxS( "pre" ) );
967 else if( aParams.find( wxS( "spicePre" ) ) != aParams.end() )
968 symbolPrefix = aParams.at( wxS( "spicePre" ) );
969
970 if( !symbolPrefix.EndsWith( wxS( "?" ) ) )
971 symbolPrefix += wxS( "?" );
972
973 LIB_ID libId = EasyEdaToKiCadLibID( wxEmptyString, symbolName );
974
975 ksymbol->SetLibId( libId );
976 ksymbol->SetName( symbolName );
977
978 ksymbol->GetReferenceField().SetText( symbolPrefix );
979 ksymbol->GetValueField().SetText( symbolName );
980
981 ParseSymbolShapes( ksymbol.get(), aParams, aShapes );
982
983 return ksymbol.release();
984}
985
986std::pair<LIB_SYMBOL*, bool> SCH_EASYEDA_PARSER::MakePowerSymbol( const wxString& aFlagTypename,
987 const wxString& aNetname )
988{
989 std::unique_ptr<LIB_SYMBOL> ksymbol = std::make_unique<LIB_SYMBOL>( wxEmptyString );
990
992
993 LIB_ID libId = EasyEdaToKiCadLibID( wxEmptyString, aNetname );
994
995 ksymbol->SetPower();
996 ksymbol->SetLibId( libId );
997 ksymbol->SetName( aNetname );
998 ksymbol->GetReferenceField().SetText( wxS( "#PWR" ) );
999 ksymbol->GetReferenceField().SetVisible( false );
1000 ksymbol->GetValueField().SetText( aNetname );
1001 ksymbol->GetValueField().SetVisible( true );
1002 ksymbol->SetDescription( wxString::Format( _( "Power symbol creates a global "
1003 "label with name '%s'" ),
1004 aNetname ) );
1005 ksymbol->SetKeyWords( wxS( "power-flag" ) );
1006 ksymbol->SetShowPinNames( false );
1007 ksymbol->SetShowPinNumbers( false );
1008
1009 std::unique_ptr<SCH_PIN> pin = std::make_unique<SCH_PIN>( ksymbol.get() );
1010
1011 pin->SetName( aNetname );
1012 pin->SetNumber( wxS( "1" ) );
1013 pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN );
1014 pin->SetType( ELECTRICAL_PINTYPE::PT_POWER_IN );
1015 pin->SetLength( 0 );
1016
1017 ksymbol->AddDrawItem( pin.release() );
1018
1020
1021 bool flip = false;
1022
1023 if( aFlagTypename == wxS( "part_netLabel_gnD" ) )
1024 {
1026 }
1027 else if( aFlagTypename == wxS( "part_netLabel_GNd" ) )
1028 {
1030 }
1031 else if( aFlagTypename == wxS( "part_netLabel_gNd" ) )
1032 {
1034 }
1035 else if( aFlagTypename == wxS( "part_netLabel_GnD" ) )
1036 {
1038 }
1039 else if( aFlagTypename == wxS( "part_netLabel_VCC" ) )
1040 {
1042 flip = true;
1043 }
1044 else if( aFlagTypename == wxS( "part_netLabel_+5V" ) )
1045 {
1047 flip = true;
1048 }
1049 else if( aFlagTypename == wxS( "part_netLabel_VEE" ) )
1050 {
1052 }
1053 else if( aFlagTypename == wxS( "part_netLabel_-5V" ) )
1054 {
1056 }
1057 else if( aFlagTypename == wxS( "part_netLabel_Bar" ) )
1058 {
1060 flip = true;
1061 }
1062
1063 VECTOR2I valueFieldPos = HelperGeneratePowerPortGraphics( ksymbol.get(), flagStyle, nullptr );
1064 ksymbol->GetValueField().SetPosition( valueFieldPos );
1065
1066 return std::make_pair( ksymbol.release(), flip );
1067}
1068
1070 const wxString& aFileName, wxArrayString aShapes )
1071{
1072 std::map<wxString, std::unique_ptr<LIB_SYMBOL>> loadedSymbols;
1073 std::map<wxString, int> namesCounter;
1074 std::vector<std::unique_ptr<SCH_ITEM>> createdItems;
1075
1076 for( wxString shap : aShapes )
1077 {
1078 shap.Replace( wxS( "#@$" ), wxS( "\n" ) );
1079 wxArrayString parts = wxSplit( shap, '\n', '\0' );
1080
1081 if( parts.size() < 1 )
1082 continue;
1083
1084 wxArrayString arr = wxSplit( parts[0], '~', '\0' );
1085
1086 if( arr.size() < 1 )
1087 continue;
1088
1089 wxString rootType = arr[0];
1090
1091 if( rootType == wxS( "LIB" ) )
1092 {
1093 if( arr.size() < 4 )
1094 continue;
1095
1096 VECTOR2D origin( Convert( arr[1] ), Convert( arr[2] ) );
1097
1098 wxString symbolName = wxString::Format( wxS( "Unknown_%s_%s" ), arr[1], arr[2] );
1099
1100 wxArrayString paramParts = wxSplit( arr[3], '`', '\0' );
1101
1102 std::map<wxString, wxString> paramMap;
1103
1104 for( size_t i = 1; i < paramParts.size(); i += 2 )
1105 {
1106 wxString key = paramParts[i - 1];
1107 wxString value = paramParts[i];
1108
1109 if( key == wxS( "spiceSymbolName" ) && !value.IsEmpty() )
1110 symbolName = value;
1111
1112 paramMap[key] = value;
1113 }
1114
1115 int& serial = namesCounter[symbolName];
1116
1117 if( serial > 0 )
1118 symbolName << wxS( "_" ) << serial;
1119
1120 serial++;
1121
1122 paramMap[wxS( "spiceSymbolName" )] = symbolName;
1123
1124 parts.RemoveAt( 0 );
1125
1126 VECTOR2D pcbOrigin = m_relOrigin;
1127
1128 LIB_SYMBOL* sym = ParseSymbol( origin, paramMap, parts );
1129 loadedSymbols.emplace( symbolName, sym );
1130
1131 wxString referenceStr = sym->GetReferenceField().GetText();
1132
1133 m_relOrigin = pcbOrigin;
1134 LIB_ID libId = EasyEdaToKiCadLibID( wxEmptyString, symbolName );
1135
1136 std::unique_ptr<SCH_SYMBOL> schSym =
1137 std::make_unique<SCH_SYMBOL>( *sym, libId, &aSchematic->CurrentSheet(), 0 );
1138
1139 schSym->SetPosition( RelPos( origin ) );
1140 schSym->SetRef( &aSchematic->CurrentSheet(), referenceStr );
1141
1142 createdItems.push_back( std::move( schSym ) );
1143 }
1144 else if( rootType == wxS( "F" ) )
1145 {
1146 wxString sepShapeStr = parts[0];
1147 sepShapeStr.Replace( wxS( "^^" ), wxS( "\n" ) );
1148
1149 wxArrayString segments = wxSplit( sepShapeStr, '\n', '\0' );
1150 wxArrayString mainParts = wxSplit( segments[0], '~', '\0' );
1151 wxArrayString uselessParts = wxSplit( segments[1], '~', '\0' );
1152 wxArrayString valueParts = wxSplit( segments[2], '~', '\0' );
1153
1154 wxString flagTypename = arr[1];
1155 VECTOR2D pos( Convert( arr[2] ), Convert( arr[3] ) );
1156 double angle = Convert( arr[4] );
1157
1158 wxString netnameValue = valueParts[0];
1159 VECTOR2D valuePos( Convert( valueParts[2] ), Convert( valueParts[3] ) );
1160 double textAngle = Convert( valueParts[4] );
1161 wxString halignStr = valueParts[5];
1162 wxString valueFontname = valueParts[7];
1163 wxString valueFontsize = valueParts[8];
1164
1165 if( flagTypename == wxS( "part_netLabel_netPort" ) )
1166 {
1167 std::unique_ptr<SCH_GLOBALLABEL> label =
1168 std::make_unique<SCH_GLOBALLABEL>( RelPos( pos ), netnameValue );
1169
1171
1172 for( double i = angle; i > 0; i -= 90 )
1173 spin = spin.RotateCCW();
1174
1175 // If the shape was mirrored, we can't rely on angle value to determine direction.
1176 if( segments.size() > 3 )
1177 {
1178 wxArrayString shapeParts = wxSplit( segments[3], '~', '\0' );
1179 if( shapeParts[0] == wxS( "PL" ) )
1180 {
1181 wxArrayString ptArr = wxSplit( shapeParts[1], ' ', '\0' );
1182
1183 SHAPE_LINE_CHAIN chain;
1184
1185 for( size_t i = 1; i < ptArr.size(); i += 2 )
1186 {
1187 chain.Append( RelPos(
1188 VECTOR2I( Convert( ptArr[i - 1] ), Convert( ptArr[i] ) ) ) );
1189 }
1190
1191 chain.Move( -RelPos( pos ) );
1192
1193 VECTOR2I shapeCenter = chain.Centre();
1194
1195 if( std::abs( shapeCenter.x ) >= std::abs( shapeCenter.y ) )
1196 {
1197 if( shapeCenter.x >= 0 )
1198 spin = SPIN_STYLE::RIGHT;
1199 else
1200 spin = SPIN_STYLE::LEFT;
1201 }
1202 else
1203 {
1204 if( shapeCenter.y >= 0 )
1205 spin = SPIN_STYLE::BOTTOM;
1206 else
1207 spin = SPIN_STYLE::UP;
1208 }
1209 }
1210 }
1211
1212 label->SetSpinStyle( spin );
1213 label->SetShape( LABEL_FLAG_SHAPE::L_INPUT );
1214
1215 createdItems.push_back( std::move( label ) );
1216 }
1217 else
1218 {
1219 auto pair = MakePowerSymbol( flagTypename, netnameValue );
1220 LIB_SYMBOL* pwrLibSym = pair.first;
1221 bool flip = pair.second;
1222
1223 LIB_ID libId = EasyEdaToKiCadLibID( wxEmptyString, netnameValue );
1224
1225 std::unique_ptr<SCH_SYMBOL> schSym = std::make_unique<SCH_SYMBOL>(
1226 *pwrLibSym, libId, &aSchematic->CurrentSheet(), 0 );
1227
1228 if( flip )
1229 schSym->SetOrientation( SYM_MIRROR_X );
1230
1231 if( angle == 0 )
1232 {
1233 }
1234 else if( angle == 90 )
1235 {
1236 schSym->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
1237 }
1238 if( angle == 180 )
1239 {
1240 schSym->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
1241 schSym->SetOrientation( SYM_ROTATE_COUNTERCLOCKWISE );
1242 }
1243 if( angle == 270 )
1244 {
1245 schSym->SetOrientation( SYM_ROTATE_CLOCKWISE );
1246 }
1247
1248 schSym->SetPosition( RelPos( pos ) );
1249
1250 SCH_FIELD* valField = schSym->GetField( VALUE_FIELD );
1251
1252 valField->SetPosition( RelPos( valuePos ) );
1253 valField->SetTextAngleDegrees( textAngle - angle );
1254
1255 if( !flip )
1257 else
1259
1260 if( halignStr == wxS( "middle" ) )
1262 else if( halignStr == wxS( "end" ) )
1264 else
1266
1267 if( flip && ( angle == 90 || angle == 270 ) )
1268 {
1269 if( valField->GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
1271 else if( valField->GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
1273
1274 if( valField->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
1276 else if( valField->GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
1278
1279 if( flagTypename == wxS( "part_netLabel_Bar" ) ) // "Circle"
1281 }
1282
1283 if( angle == 0 && flagTypename == wxS( "part_netLabel_Bar" ) ) // "Circle"
1285
1286 valField->SetFont( KIFONT::FONT::GetFont( valueFontname ) );
1287
1288 double ptSize = 7;
1289
1290 if( valueFontsize.EndsWith( wxS( "pt" ) ) )
1291 ptSize = Convert( valueFontsize.BeforeFirst( 'p' ) );
1292
1293 double ktextSize = ScaleSize( ptSize );
1294
1295 if( netnameValue.Contains( wxS( "\n" ) ) )
1296 ktextSize *= 0.8;
1297 else
1298 ktextSize *= 0.95;
1299
1300 valField->SetTextSize( VECTOR2I( ktextSize, ktextSize ) );
1301
1302 //TransformTextToBaseline( valField, wxS( "" ), true );
1303
1304 createdItems.push_back( std::move( schSym ) );
1305 }
1306 }
1307 else if( rootType == wxS( "W" ) )
1308 {
1309 wxArrayString ptArr = wxSplit( arr[1], ' ', '\0' );
1310 wxString strokeColor = arr[2];
1311 //double lineWidth = Convert( arr[3] );
1312 //LINE_STYLE strokeStyle = ConvertStrokeStyle( arr[4] );
1313 wxString fillColor = arr[5].Lower();
1314 //bool locked = arr[7] != wxS( "0" );
1315
1316 SHAPE_LINE_CHAIN chain;
1317
1318 for( size_t i = 1; i < ptArr.size(); i += 2 )
1319 chain.Append( VECTOR2I( RelPosX( ptArr[i - 1] ), RelPosY( ptArr[i] ) ) );
1320
1321 for( int segId = 0; segId < chain.SegmentCount(); segId++ )
1322 {
1323 const SEG& seg = chain.CSegment( segId );
1324
1325 std::unique_ptr<SCH_LINE> line = std::make_unique<SCH_LINE>( seg.A, LAYER_WIRE );
1326 line->SetEndPoint( seg.B );
1327
1328 createdItems.push_back( std::move( line ) );
1329 }
1330 }
1331 else if( rootType == wxS( "N" ) )
1332 {
1333 VECTOR2D pos( Convert( arr[1] ), Convert( arr[2] ) );
1334 double angle = Convert( arr[3] );
1335 wxString netname = arr[5];
1336 wxString halignStr = arr[7];
1337 VECTOR2D text_pos( Convert( arr[8] ),
1338 Convert( arr[9] ) ); // TODO: detect top/bottom align
1339
1340 wxString fontname = arr[10];
1341 wxString fontSize = arr[11];
1342
1343 std::unique_ptr<SCH_LABEL> label =
1344 std::make_unique<SCH_LABEL>( RelPos( pos ), netname );
1345
1346 if( halignStr == wxS( "middle" ) )
1347 label->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
1348 else if( halignStr == wxS( "end" ) )
1349 label->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
1350 else
1351 label->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1352
1353 for( int left = angle; left > 0; left -= 90 )
1354 label->Rotate90( false );
1355
1356 createdItems.push_back( std::move( label ) );
1357 }
1358 else if( rootType == wxS( "O" ) )
1359 {
1360 VECTOR2D pos( Convert( arr[1] ), Convert( arr[2] ) );
1361
1362 std::unique_ptr<SCH_NO_CONNECT> noConn =
1363 std::make_unique<SCH_NO_CONNECT>( RelPos( pos ) );
1364
1365 createdItems.push_back( std::move( noConn ) );
1366 }
1367 else if( rootType == wxS( "J" ) )
1368 {
1369 VECTOR2D pos( Convert( arr[1] ), Convert( arr[2] ) );
1370 //double dia = Convert( arr[3] );
1371
1372 std::unique_ptr<SCH_JUNCTION> junction =
1373 std::make_unique<SCH_JUNCTION>( RelPos( pos ) /*, ScaleSizeUnit( dia )*/ );
1374
1375 createdItems.push_back( std::move( junction ) );
1376 }
1377 else if( rootType == wxS( "T" ) )
1378 {
1379 VECTOR2D pos( Convert( arr[2] ), Convert( arr[3] ) );
1380 int angle = Convert( arr[4] );
1381 wxString color = arr[5];
1382 wxString fontname = arr[6];
1383 wxString fontSize = arr[7];
1384 wxString baselineAlign = arr[10];
1385 wxString textStr = arr[12];
1386
1387 textStr.Replace( wxS( "\\n" ), wxS( "\n" ) );
1388 textStr = UnescapeHTML( textStr );
1389
1390 wxString halignStr = arr[14]; // Empty, start, middle, end, inherit
1391
1392 std::unique_ptr<SCH_TEXT> textItem =
1393 std::make_unique<SCH_TEXT>( RelPos( pos ), textStr );
1394
1395 textItem->SetTextAngleDegrees( ( 360 - angle ) % 360 );
1396 textItem->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
1397
1398 if( halignStr == wxS( "middle" ) )
1399 textItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
1400 else if( halignStr == wxS( "end" ) )
1401 textItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
1402 else
1403 textItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1404
1405 textItem->SetFont( KIFONT::FONT::GetFont( fontname ) );
1406
1407 double ptSize = 7;
1408
1409 if( fontSize.EndsWith( wxS( "pt" ) ) )
1410 ptSize = Convert( fontSize.BeforeFirst( 'p' ) );
1411
1412 double ktextSize = ScaleSize( ptSize );
1413
1414 if( textStr.Contains( wxS( "\n" ) ) )
1415 ktextSize *= 0.8;
1416 else
1417 ktextSize *= 0.95;
1418
1419 textItem->SetTextSize( VECTOR2I( ktextSize, ktextSize ) );
1420
1421 TransformTextToBaseline( textItem.get(), baselineAlign, false );
1422
1423 createdItems.push_back( std::move( textItem ) );
1424 }
1425 else if( rootType == wxS( "R" ) )
1426 {
1427 VECTOR2D start( Convert( arr[1] ), Convert( arr[2] ) );
1428
1429 VECTOR2D cr;
1430 cr.x = !arr[3].empty() ? Convert( arr[3] ) : 0,
1431 cr.y = !arr[4].empty() ? Convert( arr[4] ) : 0; // TODO: corner radius
1432
1433 VECTOR2D size( Convert( arr[5] ), Convert( arr[6] ) );
1434 wxString strokeColor = arr[7];
1435 double lineWidth = Convert( arr[8] );
1436 LINE_STYLE strokeStyle = ConvertStrokeStyle( arr[9] );
1437 wxString fillColor = arr[10].Lower();
1438 //bool locked = arr[12] != wxS( "0" );
1439
1440 //if( cr.x == 0 )
1441 {
1442 std::unique_ptr<SCH_SHAPE> rect = std::make_unique<SCH_SHAPE>( SHAPE_T::RECTANGLE );
1443
1444 rect->SetStart( RelPos( start ) );
1445 rect->SetEnd( RelPos( start + size ) );
1446
1447 rect->SetStroke( STROKE_PARAMS( ScaleSize( lineWidth ), strokeStyle ) );
1448
1449 if( fillColor != wxS( "none" ) )
1450 {
1451 rect->SetFilled( true );
1452
1453 if( fillColor == strokeColor )
1454 rect->SetFillMode( FILL_T::FILLED_SHAPE );
1455 else
1456 rect->SetFillMode( FILL_T::FILLED_WITH_BG_BODYCOLOR );
1457 }
1458
1459 createdItems.push_back( std::move( rect ) );
1460 }
1461 /*else
1462 {
1463 }*/
1464 }
1465 else if( rootType == wxS( "I" ) )
1466 {
1467 VECTOR2D start( Convert( arr[1] ), Convert( arr[2] ) );
1468 VECTOR2D size( Convert( arr[3] ), Convert( arr[4] ) );
1469 //double angle = Convert( arr[5] ); // CSS-like transformations are used instead.
1470 wxString imageUrl = arr[6];
1471 wxString transformData = arr[9];
1472
1473 VECTOR2D kstart = RelPos( start );
1474 VECTOR2D ksize = ScalePos( size );
1475
1476 std::vector<std::pair<wxString, std::vector<double>>> transformCmds =
1477 ParseImageTransform( transformData );
1478
1479 auto applyTransform = [&]( SCH_ITEM* aSchItem )
1480 {
1481 for( const auto& cmd : transformCmds )
1482 {
1483 if( cmd.first == wxS( "rotate" ) )
1484 {
1485 if( cmd.second.size() != 3 )
1486 continue;
1487
1488 double cmdAngle = 360 - cmd.second[0];
1489 VECTOR2D cmdAround( cmd.second[1], cmd.second[2] );
1490
1491 for( double i = cmdAngle; i > 0; i -= 90 )
1492 {
1493 if( aSchItem->Type() == SCH_LINE_T )
1494 {
1495 // Lines need special handling for some reason
1496 aSchItem->SetFlags( STARTPOINT );
1497 aSchItem->Rotate( RelPos( cmdAround ), false );
1498 aSchItem->ClearFlags( STARTPOINT );
1499
1500 aSchItem->SetFlags( ENDPOINT );
1501 aSchItem->Rotate( RelPos( cmdAround ), false );
1502 aSchItem->ClearFlags( ENDPOINT );
1503 }
1504 else
1505 {
1506 aSchItem->Rotate( RelPos( cmdAround ), false );
1507 }
1508 }
1509 }
1510 else if( cmd.first == wxS( "translate" ) )
1511 {
1512 if( cmd.second.size() != 2 )
1513 continue;
1514
1515 VECTOR2D cmdOffset( cmd.second[0], cmd.second[1] );
1516 aSchItem->Move( ScalePos( cmdOffset ) );
1517 }
1518 else if( cmd.first == wxS( "scale" ) )
1519 {
1520 if( cmd.second.size() != 2 )
1521 continue;
1522
1523 double cmdScaleX = cmd.second[0];
1524 double cmdScaleY = cmd.second[1];
1525
1526 // Lines need special handling for some reason
1527 if( aSchItem->Type() == SCH_LINE_T )
1528 aSchItem->SetFlags( STARTPOINT | ENDPOINT );
1529
1530 if( cmdScaleX < 0 && cmdScaleY > 0 )
1531 {
1532 aSchItem->MirrorHorizontally( 0 );
1533 }
1534 else if( cmdScaleX > 0 && cmdScaleY < 0 )
1535 {
1536 aSchItem->MirrorVertically( 0 );
1537 }
1538 else if( cmdScaleX < 0 && cmdScaleY < 0 )
1539 {
1540 aSchItem->MirrorHorizontally( 0 );
1541 aSchItem->MirrorVertically( 0 );
1542 }
1543
1544 if( aSchItem->Type() == SCH_LINE_T )
1545 aSchItem->ClearFlags( STARTPOINT | ENDPOINT );
1546 }
1547 }
1548 };
1549
1550 if( imageUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1551 {
1552 wxArrayString paramsArr =
1553 wxSplit( imageUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1554
1555 wxString data = imageUrl.AfterFirst( ',' );
1556
1557 if( paramsArr.size() > 0 )
1558 {
1559 wxString mimeType = paramsArr[0];
1560 wxMemoryBuffer buf = wxBase64Decode( data );
1561
1562 if( mimeType == wxS( "image/svg+xml" ) )
1563 {
1564 VECTOR2D offset = RelPos( start );
1565
1566 SVG_IMPORT_PLUGIN svgImportPlugin;
1567 GRAPHICS_IMPORTER_SCH schImporter;
1568
1569 svgImportPlugin.SetImporter( &schImporter );
1570 svgImportPlugin.LoadFromMemory( buf );
1571
1572 VECTOR2D imSize( svgImportPlugin.GetImageWidth(),
1573 svgImportPlugin.GetImageHeight() );
1574
1575 VECTOR2D pixelScale( schIUScale.IUTomm( ScaleSize( size.x ) ) / imSize.x,
1576 schIUScale.IUTomm( ScaleSize( size.y ) ) / imSize.y );
1577
1578 schImporter.SetScale( pixelScale );
1579
1580 VECTOR2D offsetMM( schIUScale.IUTomm( offset.x ),
1581 schIUScale.IUTomm( offset.y ) );
1582
1583 schImporter.SetImportOffsetMM( offsetMM );
1584
1585 svgImportPlugin.Import();
1586
1587 for( std::unique_ptr<EDA_ITEM>& item : schImporter.GetItems() )
1588 {
1589 SCH_ITEM* schItem = static_cast<SCH_ITEM*>( item.release() );
1590
1591 applyTransform( schItem );
1592
1593 createdItems.emplace_back( schItem );
1594 }
1595 }
1596 else
1597 {
1598 std::unique_ptr<SCH_BITMAP> bitmap = std::make_unique<SCH_BITMAP>();
1599
1600 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1601 & ~wxImage::Load_Verbose );
1602
1603 if( bitmap->ReadImageFile( buf ) )
1604 {
1605 VECTOR2D kcenter = kstart + ksize / 2;
1606
1607 double scaleFactor = ScaleSize( size.x ) / bitmap->GetSize().x;
1608 bitmap->SetImageScale( scaleFactor );
1609 bitmap->SetPosition( kcenter );
1610
1611 applyTransform( bitmap.get() );
1612
1613 createdItems.push_back( std::move( bitmap ) );
1614 }
1615 }
1616 }
1617 }
1618 }
1619 }
1620
1621 BOX2I sheetBBox;
1622
1623 for( std::unique_ptr<SCH_ITEM>& ptr : createdItems )
1624 if( ptr->Type() == SCH_SYMBOL_T )
1625 sheetBBox.Merge( static_cast<SCH_SYMBOL*>( ptr.get() )->GetBodyBoundingBox() );
1626
1627 SCH_SCREEN* screen = aRootSheet->GetScreen();
1628 PAGE_INFO pageInfo = screen->GetPageSettings();
1629
1630 int alignGrid = schIUScale.MilsToIU( 50 );
1631
1632 VECTOR2D offset( -sheetBBox.GetLeft(), -sheetBBox.GetTop() );
1633 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1634 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1635
1636 pageInfo.SetWidthMils( schIUScale.IUToMils( sheetBBox.GetWidth() ) );
1637 pageInfo.SetHeightMils( schIUScale.IUToMils( sheetBBox.GetHeight() ) );
1638
1639 screen->SetPageSettings( pageInfo );
1640
1641 for( std::unique_ptr<SCH_ITEM>& ptr : createdItems )
1642 {
1643 ptr->Move( offset );
1644 screen->Append( ptr.release() );
1645 }
1646}
int color
Definition: DXF_plotter.cpp:58
constexpr EDA_IU_SCALE schIUScale
Definition: base_units.h:110
size_type GetHeight() const
Definition: box2.h:205
coord_type GetTop() const
Definition: box2.h:219
size_type GetWidth() const
Definition: box2.h:204
coord_type GetLeft() const
Definition: box2.h:218
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:623
std::vector< SHAPE_LINE_CHAIN > ParseLineChains(const wxString &aData, int aArcMinSegLen, bool aForceClosed)
static double Convert(const wxString &aValue)
void TransformTextToBaseline(EDA_TEXT *textItem, const wxString &baselineAlign, bool invertY)
double RelPosX(double aValue)
double RelPosY(double aValue)
VECTOR2< T > RelPos(const VECTOR2< T > &aVec)
VECTOR2< T > ScalePos(const VECTOR2< T > &aValue)
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:154
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:83
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:374
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:98
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:419
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:276
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:164
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:245
void SetTextAngleDegrees(double aOrientation)
Definition: eda_text.h:137
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition: eda_text.h:167
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:183
void SetFont(KIFONT::FONT *aFont)
Definition: eda_text.cpp:358
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:268
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)
Definition: font.cpp:146
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:51
Define a library symbol object.
Definition: lib_symbol.h:77
SCH_FIELD & GetValueField() const
Return reference to the value field.
SCH_FIELD & GetReferenceField() const
Return reference to the reference designator field.
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
Definition: lib_symbol.cpp:969
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition: page_info.h:59
void SetHeightMils(double aHeightInMils)
Definition: page_info.cpp:261
void SetWidthMils(double aWidthInMils)
Definition: page_info.cpp:247
A progress reporter interface for use in multi-threaded environments.
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
Holds all the data relating to one schematic.
Definition: schematic.h:75
SCH_SHEET_PATH & CurrentSheet() const override
Definition: schematic.h:136
void ParseSymbolShapes(LIB_SYMBOL *aContainer, std::map< wxString, wxString > paramMap, wxArrayString aShapes)
std::pair< LIB_SYMBOL *, bool > MakePowerSymbol(const wxString &aFlagTypename, const wxString &aNetname)
double ScaleSize(double aValue) override
void ParseSchematic(SCHEMATIC *aSchematic, SCH_SHEET *aRootSheet, const wxString &aFileName, wxArrayString aShapes)
VECTOR2< T > RelPosSym(const VECTOR2< T > &aVec)
LIB_SYMBOL * ParseSymbol(const VECTOR2D &aOrigin, std::map< wxString, wxString > aParams, wxArrayString aShapes)
SCH_EASYEDA_PARSER(SCHEMATIC *aSchematic, PROGRESS_REPORTER *aProgressReporter)
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
void SetPosition(const VECTOR2I &aPosition) override
Definition: sch_field.cpp:1387
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:174
const PAGE_INFO & GetPageSettings() const
Definition: sch_screen.h:131
void Append(SCH_ITEM *aItem, bool aUpdateLibSymbol=true)
Definition: sch_screen.cpp:150
void SetPageSettings(const PAGE_INFO &aPageSettings)
Definition: sch_screen.h:132
void SetPosition(const VECTOR2I &aPos) override
Definition: sch_shape.h:71
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: sch_shape.cpp:63
void AddPoint(const VECTOR2I &aPosition)
Definition: sch_shape.cpp:642
VECTOR2I GetPosition() const override
Definition: sch_shape.h:70
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition: sch_sheet.h:57
SCH_SCREEN * GetScreen() const
Definition: sch_sheet.h:110
Schematic symbol object.
Definition: sch_symbol.h:108
BOX2I GetBodyBoundingBox() const
Return a bounding box for the symbol body but not the pins or fields.
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
bool IsClosed() const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
virtual VECTOR2I Centre() const
Compute a center-of-mass of the shape.
Definition: shape.h:232
SPIN_STYLE RotateCCW()
Definition: sch_label.cpp:157
Simple container to manage line stroke parameters.
Definition: stroke_params.h:81
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.
#define _(s)
#define ENDPOINT
ends. (Used to support dragging.)
#define STARTPOINT
When a line is selected, these flags indicate which.
void ConvertImageToLibShapes(LIB_SYMBOL *aSymbol, int unit, wxImage img, VECTOR2D pixelScale, VECTOR2D offset)
@ LAYER_DEVICE
Definition: layer_ids.h:370
@ LAYER_WIRE
Definition: layer_ids.h:356
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:424
ELECTRICAL_PINTYPE
The symbol library pin object electrical types used in ERC tests.
Definition: pin_type.h:36
@ PT_INPUT
usual pin input: must be connected
@ PT_OUTPUT
usual output
@ PT_BIDI
input or output (like port for a microprocessor)
@ PT_UNSPECIFIED
unknown electrical properties: creates always a warning when connected
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin
PIN_ORIENTATION
The symbol library pin object orientations.
Definition: pin_type.h:77
@ RPT_SEVERITY_WARNING
static std::vector< std::vector< wxString > > RegexMatchAll(wxRegEx &aRegex, const wxString &aString)
static ELECTRICAL_PINTYPE ConvertElecType(const wxString &aType)
VECTOR2I HelperGeneratePowerPortGraphics(LIB_SYMBOL *aKsymbol, EASYEDA::POWER_FLAG_STYLE aStyle, REPORTER *aReporter)
static LIB_ID EasyEdaToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
static std::vector< std::pair< wxString, std::vector< double > > > ParseImageTransform(const wxString &transformData)
EasyEDA image transformations are in the form of:
static LINE_STYLE ConvertStrokeStyle(const wxString &aStyle)
VECTOR2I HelperGeneratePowerPortGraphics(LIB_SYMBOL *aKsymbol, ASCH_POWER_PORT_STYLE aStyle, REPORTER *aReporter)
@ SYM_ROTATE_CLOCKWISE
Definition: sch_symbol.h:82
@ SYM_ROTATE_COUNTERCLOCKWISE
Definition: sch_symbol.h:83
@ SYM_MIRROR_X
Definition: sch_symbol.h:88
const int scale
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
@ CTX_LIBID
Definition: string_utils.h:54
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:48
constexpr double IUTomm(int iu) const
Definition: base_units.h:86
constexpr int IUToMils(int iu) const
Definition: base_units.h:99
constexpr int MilsToIU(int mils) const
Definition: base_units.h:93
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
constexpr int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
@ SCH_LINE_T
Definition: typeinfo.h:163
@ SCH_SYMBOL_T
Definition: typeinfo.h:172
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:118
VECTOR2< double > VECTOR2D
Definition: vector2d.h:587
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588