KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_easyedapro_parser.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2023 Alex Shvartzkop <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
27
28#include <memory>
29
30#include <json_common.h>
32#include <core/map_helpers.h>
33#include <string_utils.h>
34
35#include <wx/wfstream.h>
36#include <wx/stdstream.h>
37#include <wx/base64.h>
38#include <wx/log.h>
39#include <glm/glm.hpp>
40
41#include <progress_reporter.h>
42#include <footprint.h>
43#include <board.h>
45#include <bezier_curves.h>
46#include <pcb_group.h>
47#include <pcb_track.h>
48#include <pcb_shape.h>
49#include <pcb_text.h>
50#include <font/font.h>
51#include <geometry/shape_arc.h>
54#include <geometry/shape_rect.h>
56#include <zone.h>
57#include <pad.h>
59#include <project.h>
60#include <fix_board_shape.h>
61#include <pcb_reference_image.h>
62#include <core/mirror.h>
63
64
65static const wxString QUERY_MODEL_UUID_KEY = wxS( "JLC_3DModel_Q" );
66static const wxString MODEL_SIZE_KEY = wxS( "JLC_3D_Size" );
67
68static const int SHAPE_JOIN_DISTANCE = pcbIUScale.mmToIU( 1.5 );
69
70
71double PCB_IO_EASYEDAPRO_PARSER::Convert( wxString aValue )
72{
73 double value = 0;
74
75 if( !aValue.ToCDouble( &value ) )
76 THROW_IO_ERROR( wxString::Format( _( "Failed to parse value: '%s'" ), aValue ) );
77
78 return value;
79}
80
81
83{
84 m_board = aBoard;
85}
86
87
91
92
94{
95 switch( aLayer )
96 {
97 case 1: return F_Cu;
98 case 2: return B_Cu;
99 case 3: return F_SilkS;
100 case 4: return B_SilkS;
101 case 5: return F_Mask;
102 case 6: return B_Mask;
103 case 7: return F_Paste;
104 case 8: return B_Paste;
105 case 9: return F_Fab;
106 case 10: return B_Fab;
107 case 11: return Edge_Cuts;
108 case 12: return Edge_Cuts; // Multi
109 case 13: return Dwgs_User;
110 case 14: return Eco2_User;
111
112 case 15: return In1_Cu;
113 case 16: return In2_Cu;
114 case 17: return In3_Cu;
115 case 18: return In4_Cu;
116 case 19: return In5_Cu;
117 case 20: return In6_Cu;
118 case 21: return In7_Cu;
119 case 22: return In8_Cu;
120 case 23: return In9_Cu;
121 case 24: return In10_Cu;
122 case 25: return In11_Cu;
123 case 26: return In12_Cu;
124 case 27: return In13_Cu;
125 case 28: return In14_Cu;
126 case 29: return In15_Cu;
127 case 30: return In16_Cu;
128 case 31: return In17_Cu;
129 case 32: return In18_Cu;
130 case 33: return In19_Cu;
131 case 34: return In20_Cu;
132 case 35: return In21_Cu;
133 case 36: return In22_Cu;
134 case 37: return In23_Cu;
135 case 38: return In24_Cu;
136 case 39: return In25_Cu;
137 case 40: return In26_Cu;
138 case 41: return In27_Cu;
139 case 42: return In28_Cu;
140 case 43: return In29_Cu;
141 case 44: return In30_Cu;
142
143 case 48: return F_Fab; // Component shape layer
144 case 49: return F_Fab; // Component marking
145
146 case 53: return User_4; // 3D shell outline
147 case 54: return User_5; // 3D shell top
148 case 55: return User_6; // 3D shell bot
149 case 56: return User_7; // Drill drawing
150
151 default: break;
152 }
153
154 return User_1;
155}
156
157
158static void AlignText( EDA_TEXT* text, int align )
159{
160 switch( align )
161 {
162 case 1:
163 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
164 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
165 break;
166 case 2:
167 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
168 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
169 break;
170 case 3:
171 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
172 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
173 break;
174
175 case 4:
176 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
177 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
178 break;
179 case 5:
180 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
181 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
182 break;
183 case 6:
184 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
185 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
186 break;
187
188 case 7:
189 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
190 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
191 break;
192 case 8:
193 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
194 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
195 break;
196 case 9:
197 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
198 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
199 break;
200 }
201}
202
203
205 const wxString& modelUuid,
206 const wxString& modelTitle,
207 const wxString& modelTransform ) const
208{
209 // TODO: make this path configurable?
210 const wxString easyedaModelDir = wxS( "EASYEDA_MODELS" );
211 const wxString kicadModelPrefix = wxS( "${KIPRJMOD}/" ) + easyedaModelDir + wxS( "/" );
212
213 VECTOR3D kmodelOffset;
214 VECTOR3D kmodelRotation;
215
216 if( !modelUuid.IsEmpty() && !footprint->GetField( QUERY_MODEL_UUID_KEY ) )
217 {
218 PCB_FIELD* field = new PCB_FIELD( footprint, FIELD_T::USER, QUERY_MODEL_UUID_KEY );
219 field->SetLayer( Cmts_User );
220 field->SetVisible( false );
221 field->SetText( modelUuid );
222 footprint->Add( field );
223 }
224
225 if( !modelTransform.IsEmpty() && !footprint->GetField( MODEL_SIZE_KEY ) )
226 {
227 wxArrayString arr = wxSplit( modelTransform, ',', '\0' );
228
229 double fitXmm = pcbIUScale.IUTomm( ScaleSize( Convert( arr[0] ) ) );
230 double fitYmm = pcbIUScale.IUTomm( ScaleSize( Convert( arr[1] ) ) );
231
232 if( fitXmm > 0.0 && fitYmm > 0.0 )
233 {
234 PCB_FIELD* field = new PCB_FIELD( footprint, FIELD_T::USER, MODEL_SIZE_KEY );
235 field->SetLayer( Cmts_User );
236 field->SetVisible( false );
237 field->SetText( wxString::FromCDouble( fitXmm ) + wxS( " " )
238 + wxString::FromCDouble( fitYmm ) );
239 footprint->Add( field );
240 }
241
242 kmodelRotation.z = -Convert( arr[3] );
243 kmodelRotation.x = -Convert( arr[4] );
244 kmodelRotation.y = -Convert( arr[5] );
245
246 kmodelOffset.x = pcbIUScale.IUTomm( ScaleSize( Convert( arr[6] ) ) );
247 kmodelOffset.y = pcbIUScale.IUTomm( ScaleSize( Convert( arr[7] ) ) );
248 kmodelOffset.z = pcbIUScale.IUTomm( ScaleSize( Convert( arr[8] ) ) );
249 }
250
251 if( !modelTitle.IsEmpty() && footprint->Models().empty() )
252 {
254 model.m_Filename = kicadModelPrefix
256 + wxS( ".step" );
257 model.m_Offset = kmodelOffset;
258 model.m_Rotation = kmodelRotation;
259 footprint->Models().push_back( model );
260 }
261}
262
263
264std::vector<std::unique_ptr<PCB_SHAPE>>
265PCB_IO_EASYEDAPRO_PARSER::ParsePoly( BOARD_ITEM_CONTAINER* aContainer, nlohmann::json polyData,
266 bool aClosed, bool aInFill ) const
267{
268 std::vector<std::unique_ptr<PCB_SHAPE>> results;
269
270 VECTOR2D prevPt;
271 for( int i = 0; i < (int) polyData.size(); i++ )
272 {
273 nlohmann::json val = polyData.at( i );
274
275 if( val.is_string() )
276 {
277 wxString str = val;
278 if( str == wxS( "CIRCLE" ) )
279 {
281 center.x = ( polyData.at( ++i ) );
282 center.y = ( polyData.at( ++i ) );
283 double r = ( polyData.at( ++i ) );
284
285 std::unique_ptr<PCB_SHAPE> shape =
286 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::CIRCLE );
287
288 shape->SetCenter( ScalePos( center ) );
289 shape->SetEnd( ScalePos( center + VECTOR2D( r, 0 ) ) );
290 shape->SetFilled( aClosed );
291
292 results.emplace_back( std::move( shape ) );
293 }
294 else if( str == wxS( "R" ) )
295 {
296 VECTOR2D start, size;
297 start.x = ( polyData.at( ++i ) );
298 start.y = ( polyData.at( ++i ) );
299 size.x = ( polyData.at( ++i ) );
300 size.y = -( polyData.at( ++i ).get<double>() );
301 double angle = polyData.at( ++i );
302 double cr = ( i + 1 ) < (int) polyData.size() ? polyData.at( ++i ).get<double>() : 0;
303
304 if( cr == 0 )
305 {
306 std::unique_ptr<PCB_SHAPE> shape =
307 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::RECTANGLE );
308
309 shape->SetStart( ScalePos( start ) );
310 shape->SetEnd( ScalePos( start + size ) );
311 shape->SetFilled( aClosed );
312 shape->Rotate( ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
313
314 results.emplace_back( std::move( shape ) );
315 }
316 else
317 {
318 VECTOR2D end = start + size;
319
320 auto addSegment = [&]( VECTOR2D aStart, VECTOR2D aEnd )
321 {
322 std::unique_ptr<PCB_SHAPE> shape =
323 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::SEGMENT );
324
325 shape->SetStart( ScalePos( aStart ) );
326 shape->SetEnd( ScalePos( aEnd ) );
327 shape->SetFilled( aClosed );
328 shape->Rotate( ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
329
330 results.emplace_back( std::move( shape ) );
331 };
332
333 auto addArc = [&]( VECTOR2D aStart, VECTOR2D aEnd, VECTOR2D center )
334 {
335 std::unique_ptr<PCB_SHAPE> shape =
336 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::ARC );
337
338 shape->SetStart( ScalePos( aStart ) );
339 shape->SetEnd( ScalePos( aEnd ) );
340 shape->SetCenter( ScalePos( center ) );
341 shape->SetFilled( aClosed );
342 shape->Rotate( ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
343
344 results.emplace_back( std::move( shape ) );
345 };
346
347 addSegment( { start.x + cr, start.y }, { end.x - cr, start.y } );
348 addSegment( { end.x, start.y - cr }, { end.x, end.y + cr } );
349 addSegment( { start.x + cr, end.y }, { end.x - cr, end.y } );
350 addSegment( { start.x, start.y - cr }, { start.x, end.y + cr } );
351
352 addArc( { end.x - cr, start.y }, { end.x, start.y - cr },
353 { end.x - cr, start.y - cr } );
354
355 addArc( { end.x, end.y + cr }, { end.x - cr, end.y },
356 { end.x - cr, end.y + cr } );
357
358 addArc( { start.x + cr, end.y }, { start.x, end.y + cr },
359 { start.x + cr, end.y + cr } );
360
361 addArc( { start.x, start.y - cr }, { start.x + cr, start.y },
362 { start.x + cr, start.y - cr } );
363 }
364 }
365 else if( str == wxS( "ARC" ) || str == wxS( "CARC" ) )
366 {
368 double angle = polyData.at( ++i ).get<double>() / ( aInFill ? 10 : 1 );
369 end.x = ( polyData.at( ++i ) );
370 end.y = ( polyData.at( ++i ) );
371
372 std::unique_ptr<PCB_SHAPE> shape =
373 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::ARC );
374
375 if( angle < 0 )
376 {
377 shape->SetStart( ScalePos( prevPt ) );
378 shape->SetEnd( ScalePos( end ) );
379 }
380 else
381 {
382 shape->SetStart( ScalePos( end ) );
383 shape->SetEnd( ScalePos( prevPt ) );
384 }
385
386 VECTOR2D delta = end - prevPt;
387 VECTOR2D mid = ( prevPt + delta / 2 );
388
389 double ha = angle / 2;
390 double hd = delta.EuclideanNorm() / 2;
391 double cdist = hd / tan( DEG2RAD( ha ) );
392 VECTOR2D center = mid + delta.Perpendicular().Resize( cdist );
393 shape->SetCenter( ScalePos( center ) );
394
395 shape->SetFilled( aClosed );
396
397 results.emplace_back( std::move( shape ) );
398
399 prevPt = end;
400 }
401 else if( str == wxS( "L" ) )
402 {
404 chain.Append( ScalePos( prevPt ) );
405
406 while( i < (int) polyData.size() - 2 && polyData.at( i + 1 ).is_number() )
407 {
408 VECTOR2D pt;
409 pt.x = ( polyData.at( ++i ) );
410 pt.y = ( polyData.at( ++i ) );
411
412 chain.Append( ScalePos( pt ) );
413
414 prevPt = pt;
415 }
416
417 if( aClosed )
418 {
419 std::unique_ptr<PCB_SHAPE> shape =
420 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::POLY );
421
422 wxASSERT( chain.PointCount() > 2 );
423
424 if( chain.PointCount() > 2 )
425 {
426 chain.SetClosed( true );
427 shape->SetFilled( true );
428 shape->SetPolyShape( chain );
429
430 results.emplace_back( std::move( shape ) );
431 }
432 }
433 else
434 {
435 for( int s = 0; s < chain.SegmentCount(); s++ )
436 {
437 SEG seg = chain.Segment( s );
438
439 std::unique_ptr<PCB_SHAPE> shape =
440 std::make_unique<PCB_SHAPE>( aContainer, SHAPE_T::SEGMENT );
441
442 shape->SetStart( seg.A );
443 shape->SetEnd( seg.B );
444
445 results.emplace_back( std::move( shape ) );
446 }
447 }
448 }
449 }
450 else if( val.is_number() )
451 {
452 prevPt.x = ( polyData.at( i ) );
453 prevPt.y = ( polyData.at( ++i ) );
454 }
455 }
456
457 return results;
458}
459
460
462PCB_IO_EASYEDAPRO_PARSER::ParseContour( nlohmann::json polyData, bool aInFill,
463 int aMaxError ) const
464{
466 VECTOR2D prevPt;
467
468 for( int i = 0; i < (int) polyData.size(); i++ )
469 {
470 nlohmann::json val = polyData.at( i );
471
472 if( val.is_string() )
473 {
474 wxString str = val;
475 if( str == wxS( "CIRCLE" ) )
476 {
478 center.x = ( polyData.at( ++i ) );
479 center.y = ( polyData.at( ++i ) );
480 double r = ( polyData.at( ++i ) );
481
483 ERROR_INSIDE );
484 }
485 else if( str == wxS( "R" ) )
486 {
487 VECTOR2D start, size;
488 start.x = ( polyData.at( ++i ) );
489 start.y = ( polyData.at( ++i ) );
490 size.x = ( polyData.at( ++i ) );
491 size.y = ( polyData.at( ++i ).get<double>() );
492 double angle = polyData.at( ++i );
493 double cr = ( i + 1 ) < (int) polyData.size() ? polyData.at( ++i ).get<double>() : 0;
494
495 SHAPE_POLY_SET poly;
496
497 VECTOR2D kstart = ScalePos( start );
498 VECTOR2D ksize = ScaleSize( size );
499 VECTOR2D kcenter = kstart + ksize / 2;
500 RotatePoint( kcenter, kstart, EDA_ANGLE( angle, DEGREES_T ) );
501
502 TransformRoundChamferedRectToPolygon( poly, kcenter, ksize, EDA_ANGLE( angle, DEGREES_T ),
503 ScaleSize( cr ), 0, 0, 0, aMaxError, ERROR_INSIDE );
504
505 result.Append( poly.Outline( 0 ) );
506 }
507 else if( str == wxS( "ARC" ) || str == wxS( "CARC" ) )
508 {
510 double angle = polyData.at( ++i ).get<double>();
511
512 if( aInFill ) // In .epcb fills, the angle is 10x for some reason
513 angle /= 10;
514
515 end.x = ( polyData.at( ++i ) );
516 end.y = ( polyData.at( ++i ) );
517
518 VECTOR2D arcStart, arcEnd;
519 arcStart = prevPt;
520 arcEnd = end;
521
522 VECTOR2D delta = end - prevPt;
523 VECTOR2D mid = ( prevPt + delta / 2 );
524
525 double ha = angle / 2;
526 double hd = delta.EuclideanNorm() / 2;
527 double cdist = hd / tan( DEG2RAD( ha ) );
528 VECTOR2D center = mid + delta.Perpendicular().Resize( cdist );
529
530 SHAPE_ARC sarc;
531 sarc.ConstructFromStartEndCenter( ScalePos( arcStart ), ScalePos( arcEnd ),
532 ScalePos( center ), angle >= 0, 0 );
533
534 result.Append( sarc, aMaxError );
535
536 prevPt = end;
537 }
538 else if( str == wxS( "C" ) )
539 {
540 VECTOR2D pt1;
541 pt1.x = ( polyData.at( ++i ) );
542 pt1.y = ( polyData.at( ++i ) );
543
544 VECTOR2D pt2;
545 pt2.x = ( polyData.at( ++i ) );
546 pt2.y = ( polyData.at( ++i ) );
547
548 VECTOR2D pt3;
549 pt3.x = ( polyData.at( ++i ) );
550 pt3.y = ( polyData.at( ++i ) );
551
552 std::vector<VECTOR2I> ctrlPoints = { ScalePos( prevPt ), ScalePos( pt1 ),
553 ScalePos( pt2 ), ScalePos( pt3 ) };
554 BEZIER_POLY converter( ctrlPoints );
555
556 std::vector<VECTOR2I> bezierPoints;
557 converter.GetPoly( bezierPoints, aMaxError );
558
559 result.Append( bezierPoints );
560
561 prevPt = pt3;
562 }
563 else if( str == wxS( "L" ) )
564 {
565 result.Append( ScalePos( prevPt ) );
566
567 while( i < (int) polyData.size() - 2 && polyData.at( i + 1 ).is_number() )
568 {
569 VECTOR2D pt;
570 pt.x = ( polyData.at( ++i ) );
571 pt.y = ( polyData.at( ++i ) );
572
573 result.Append( ScalePos( pt ) );
574
575 prevPt = pt;
576 }
577 }
578 }
579 else if( val.is_number() )
580 {
581 prevPt.x = ( polyData.at( i ) );
582 prevPt.y = ( polyData.at( ++i ) );
583 }
584 }
585
586 return result;
587}
588
589
590std::unique_ptr<PAD> PCB_IO_EASYEDAPRO_PARSER::createPAD( FOOTPRINT* aFootprint,
591 const nlohmann::json& line )
592{
593 wxString uuid = line.at( 1 );
594
595 // if( line.at( 2 ).is_number() )
596 // int unk = line.at( 2 ).get<int>();
597 // else if( line.at( 2 ).is_string() )
598 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
599
600 wxString netname = line.at( 3 );
601 int layer = line.at( 4 ).get<int>();
602 PCB_LAYER_ID klayer = LayerToKi( layer );
603
604 wxString padNumber = line.at( 5 );
605
607 center.x = line.at( 6 );
608 center.y = line.at( 7 );
609
610 double orientation = line.at( 8 );
611
612 nlohmann::json padHole = line.at( 9 );
613 nlohmann::json padShape = line.at( 10 );
614
615 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
616
617 pad->SetNumber( padNumber );
618 pad->SetPosition( ScalePos( center ) );
619 pad->SetOrientationDegrees( orientation );
620
621 // Check if this pad has a real drill hole
622 // JLCEDA may use ["ROUND",0,0] to indicate SMD pads
623 bool hasHole = false;
624
625 if( !padHole.is_null() && !padHole.empty() )
626 {
627 wxString holeShape = padHole.at( 0 );
628
629 if( holeShape == wxS( "ROUND" ) || holeShape == wxS( "SLOT" ) )
630 {
631 VECTOR2D drill;
632 drill.x = padHole.at( 1 );
633 drill.y = padHole.at( 2 );
634
635 // Only treat as PTH if hole size is non-zero
636 if( drill.x > 0 || drill.y > 0 )
637 {
638 hasHole = true;
639
640 double drill_dir = 0;
641
642 if( line.at( 14 ).is_number() )
643 drill_dir = line.at( 14 );
644
645 double deg = EDA_ANGLE( drill_dir, DEGREES_T ).Normalize90().AsDegrees();
646
647 if( std::abs( deg ) >= 45 )
648 std::swap( drill.x, drill.y ); // KiCad doesn't support arbitrary hole direction
649
650 if( holeShape == wxS( "SLOT" ) )
651 {
652 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
653 }
654
655 pad->SetDrillSize( ScaleSize( drill ) );
656 pad->SetLayerSet( PAD::PTHMask() );
657 pad->SetAttribute( PAD_ATTRIB::PTH );
658 }
659 }
660 }
661
662 // If no valid hole, this is an SMD pad
663 if( !hasHole )
664 {
665 if( klayer == F_Cu )
666 {
667 pad->SetLayerSet( PAD::SMDMask() );
668 }
669 else if( klayer == B_Cu )
670 {
671 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
672 }
673
674 pad->SetAttribute( PAD_ATTRIB::SMD );
675 }
676
677 wxString padSh = padShape.at( 0 );
678 if( padSh == wxS( "RECT" ) )
679 {
680 VECTOR2D size;
681 size.x = padShape.at( 1 );
682 size.y = padShape.at( 2 );
683 double cr_p = padShape.size() > 3 ? padShape.at( 3 ).get<double>() : 0;
684
685 pad->SetSize( PADSTACK::ALL_LAYERS, ScaleSize( size ) );
686
687 if( cr_p == 0 )
688 {
690 }
691 else
692 {
694 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, cr_p / 100 );
695 }
696 }
697 else if( padSh == wxS( "ELLIPSE" ) )
698 {
699 VECTOR2D size;
700 size.x = padShape.at( 1 );
701 size.y = padShape.at( 2 );
702
703 pad->SetSize( PADSTACK::ALL_LAYERS, ScaleSize( size ) );
705 }
706 else if( padSh == wxS( "OVAL" ) )
707 {
708 VECTOR2D size;
709 size.x = padShape.at( 1 );
710 size.y = padShape.at( 2 );
711
712 pad->SetSize( PADSTACK::ALL_LAYERS, ScaleSize( size ) );
714 }
715 else if( padSh == wxS( "POLY" ) || padSh == wxS( "POLYGON" ) )
716 {
718 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
719 pad->SetSize( PADSTACK::ALL_LAYERS, { 1, 1 } );
720
721 nlohmann::json polyData = padShape.at( 1 );
722
723 std::vector<std::unique_ptr<PCB_SHAPE>> results =
724 ParsePoly( aFootprint, polyData, true, false );
725
726 for( auto& shape : results )
727 {
728 shape->SetLayer( klayer );
729 shape->SetWidth( 0 );
730
731 shape->Move( -pad->GetPosition() );
732
733 pad->AddPrimitive( PADSTACK::ALL_LAYERS, shape.release() );
734 }
735 }
736
737 pad->SetThermalSpokeAngle( ANGLE_90 );
738
739 return pad;
740}
741
742
744 const wxString& aFpUuid,
745 const std::vector<nlohmann::json>& aLines )
746{
747 std::unique_ptr<FOOTPRINT> footprintPtr = std::make_unique<FOOTPRINT>( m_board );
748 FOOTPRINT* footprint = footprintPtr.get();
749
750 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
751 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
752
753 for( PCB_FIELD* field : footprint->GetFields() )
754 {
755 field->SetTextSize( defaultTextSize );
756 field->SetTextThickness( defaultTextThickness );
757 }
758
759 for( const nlohmann::json& line : aLines )
760 {
761 if( line.size() == 0 )
762 continue;
763
764 wxString type = line.at( 0 );
765
766 if( type == wxS( "POLY" ) || type == wxS( "PAD" ) || type == wxS( "FILL" )
767 || type == wxS( "ATTR" ) )
768 {
769 wxString uuid = line.at( 1 );
770
771 // if( line.at( 2 ).is_number() )
772 // int unk = line.at( 2 ).get<int>();
773 // else if( line.at( 2 ).is_string() )
774 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
775
776 wxString netname = line.at( 3 );
777 int layer = line.at( 4 ).get<int>();
778 PCB_LAYER_ID klayer = LayerToKi( layer );
779
780 if( type == wxS( "POLY" ) )
781 {
782 double thickness = ( line.at( 5 ) );
783 nlohmann::json polyData = line.at( 6 );
784
785 std::vector<std::unique_ptr<PCB_SHAPE>> results =
786 ParsePoly( footprint, polyData, false, false );
787
788 for( auto& shape : results )
789 {
790 shape->SetLayer( klayer );
791 shape->SetWidth( ScaleSize( thickness ) );
792
793 footprint->Add( shape.release(), ADD_MODE::APPEND );
794 }
795 }
796 else if( type == wxS( "PAD" ) )
797 {
798 std::unique_ptr<PAD> pad = createPAD( footprint, line );
799
800 footprint->Add( pad.release(), ADD_MODE::APPEND );
801 }
802 else if( type == wxS( "FILL" ) )
803 {
804 int fillLayer = line.at( 4 ).get<int>();
805 PCB_LAYER_ID fillKlayer = LayerToKi( fillLayer );
806
807 nlohmann::json polyDataList = line.at( 7 );
808
809 if( !polyDataList.is_null() && !polyDataList.empty() )
810 {
811 if( !polyDataList.at( 0 ).is_array() )
812 polyDataList = nlohmann::json::array( { polyDataList } );
813
814 std::vector<SHAPE_LINE_CHAIN> contours;
815 for( nlohmann::json& polyData : polyDataList )
816 {
817 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
818 contour.SetClosed( true );
819
820 contours.push_back( contour );
821 }
822
823 SHAPE_POLY_SET polySet;
824
825 for( SHAPE_LINE_CHAIN& contour : contours )
826 polySet.AddOutline( contour );
827
828 polySet.RebuildHolesFromContours();
829
830 std::unique_ptr<PCB_GROUP> group;
831
832 if( polySet.OutlineCount() > 1 )
833 group = std::make_unique<PCB_GROUP>( footprint );
834
835 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
836 {
837 std::unique_ptr<PCB_SHAPE> shape =
838 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::POLY );
839
840 shape->SetFilled( true );
841 shape->SetPolyShape( poly );
842 shape->SetLayer( fillKlayer );
843 shape->SetWidth( 0 );
844
845 if( group )
846 group->AddItem( shape.get() );
847
848 footprint->Add( shape.release(), ADD_MODE::APPEND );
849 }
850
851 if( group )
852 footprint->Add( group.release(), ADD_MODE::APPEND );
853 }
854 }
855 else if( type == wxS( "ATTR" ) )
856 {
857 EASYEDAPRO::PCB_ATTR attr = line;
858
859 if( attr.key == wxS( "Designator" ) )
860 footprint->GetField( FIELD_T::REFERENCE )->SetText( attr.value );
861 }
862 }
863 else if( type == wxS( "REGION" ) )
864 {
865 wxString uuid = line.at( 1 );
866
867 // if( line.at( 2 ).is_number() )
868 // int unk = line.at( 2 ).get<int>();
869 // else if( line.at( 2 ).is_string() )
870 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
871
872 int layer = line.at( 3 ).get<int>();
873 PCB_LAYER_ID klayer = LayerToKi( layer );
874
875 std::set<int> flags = line.at( 5 );
876 nlohmann::json polyDataList = line.at( 6 );
877
878 for( nlohmann::json& polyData : polyDataList )
879 {
880 SHAPE_POLY_SET polySet;
881
882 std::vector<std::unique_ptr<PCB_SHAPE>> results =
883 ParsePoly( nullptr, polyData, true, false );
884
885 for( auto& shape : results )
886 {
887 shape->SetFilled( true );
888 shape->TransformShapeToPolygon( polySet, klayer, 0, ARC_HIGH_DEF, ERROR_INSIDE,
889 true );
890 }
891
892 polySet.Simplify();
893
894 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( footprint );
895
896 zone->SetIsRuleArea( true );
897 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
898 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
899 || !!flags.count( 8 ) );
900 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
901 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
902 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
903
904 zone->SetLayer( klayer );
905 zone->Outline()->Append( polySet );
906
907 footprint->Add( zone.release(), ADD_MODE::APPEND );
908 }
909 }
910 }
911
912 if( aProject.is_object() && aProject.contains( "devices" ) )
913 {
914 std::map<wxString, EASYEDAPRO::PRJ_DEVICE> devicesMap = aProject.at( "devices" );
915 std::map<wxString, wxString> compAttrs;
916
917 for( auto& [devUuid, devData] : devicesMap )
918 {
919 if( auto fp = get_opt( devData.attributes, "Footprint" ) )
920 {
921 if( *fp == aFpUuid )
922 {
923 compAttrs = devData.attributes;
924 break;
925 }
926 }
927 }
928
929 wxString modelUuid, modelTitle, modelTransform;
930
931 modelUuid = get_def( compAttrs, "3D Model", "" );
932 modelTitle = get_def( compAttrs, "3D Model Title", modelUuid );
933 modelTransform = get_def( compAttrs, "3D Model Transform", "" );
934
935 fillFootprintModelInfo( footprint, modelUuid, modelTitle, modelTransform );
936 }
937
938 // Heal board outlines
939 std::vector<PCB_SHAPE*> edgeShapes;
940
941 for( BOARD_ITEM* item : footprint->GraphicalItems() )
942 {
943 if( item->IsOnLayer( Edge_Cuts ) && item->Type() == PCB_SHAPE_T )
944 edgeShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
945 }
946
948
949 // EasyEDA footprints don't have courtyard, so build a box ourselves
950 if( !footprint->IsOnLayer( F_CrtYd ) )
951 {
952 BOX2I bbox = footprint->GetLayerBoundingBox( { F_Cu, F_Fab, F_Paste, F_Mask, Edge_Cuts } );
953 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Default courtyard clearance
954
955 std::unique_ptr<PCB_SHAPE> shape =
956 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::RECTANGLE );
957
958 shape->SetWidth( pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ) );
959 shape->SetLayer( F_CrtYd );
960 shape->SetStart( bbox.GetOrigin() );
961 shape->SetEnd( bbox.GetEnd() );
962
963 footprint->Add( shape.release(), ADD_MODE::APPEND );
964 }
965
966 bool hasFabRef = false;
967
968 for( BOARD_ITEM* item : footprint->GraphicalItems() )
969 {
970 if( item->Type() == PCB_TEXT_T && item->IsOnLayer( F_Fab ) )
971 {
972 if( static_cast<PCB_TEXT*>( item )->GetText() == wxT( "${REFERENCE}" ) )
973 {
974 hasFabRef = true;
975 break;
976 }
977 }
978 }
979
980 if( !hasFabRef )
981 {
982 // Add reference text field on F_Fab
983 int c_refTextSize = pcbIUScale.mmToIU( 0.5 ); // KLC min Fab text size
984 int c_refTextThickness = pcbIUScale.mmToIU( 0.1 ); // Decent text thickness
985 std::unique_ptr<PCB_TEXT> refText = std::make_unique<PCB_TEXT>( footprint );
986
987 refText->SetLayer( F_Fab );
988 refText->SetTextSize( VECTOR2I( c_refTextSize, c_refTextSize ) );
989 refText->SetTextThickness( c_refTextThickness );
990 refText->SetText( wxT( "${REFERENCE}" ) );
991
992 footprint->Add( refText.release(), ADD_MODE::APPEND );
993 }
994
995 return footprintPtr.release();
996}
997
998
1000 BOARD* aBoard, const nlohmann::json& aProject,
1001 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
1002 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
1003 const std::multimap<wxString, EASYEDAPRO::POURED>& aPouredMap,
1004 const std::vector<nlohmann::json>& aLines, const wxString& aFpLibName )
1005{
1006 std::map<wxString, std::vector<nlohmann::json>> componentLines;
1007 std::map<wxString, std::vector<nlohmann::json>> ruleLines;
1008
1009 std::multimap<wxString, EASYEDAPRO::POURED> boardPouredMap = aPouredMap;
1010 std::map<wxString, ZONE*> poursToFill;
1011
1013
1014 for( const nlohmann::json& line : aLines )
1015 {
1016 if( line.size() == 0 )
1017 continue;
1018
1019 wxString type = line.at( 0 );
1020
1021 if( type == wxS( "LAYER" ) )
1022 {
1023 int layer = line.at( 1 );
1024 PCB_LAYER_ID klayer = LayerToKi( layer );
1025
1026 wxString layerType = line.at( 2 );
1027 wxString layerName = line.at( 3 );
1028 int layerFlag = line.at( 4 );
1029
1030 if( layerFlag != 0 )
1031 {
1032 LSET blayers = aBoard->GetEnabledLayers();
1033 blayers.set( klayer );
1034 aBoard->SetEnabledLayers( blayers );
1035 aBoard->SetLayerName( klayer, layerName );
1036 }
1037 }
1038 else if( type == wxS( "NET" ) )
1039 {
1040 wxString netname = line.at( 1 );
1041
1042 aBoard->Add( new NETINFO_ITEM( aBoard, netname, aBoard->GetNetCount() + 1 ),
1044 }
1045 else if( type == wxS( "RULE" ) )
1046 {
1047 wxString ruleType = line.at( 1 );
1048 wxString ruleName = line.at( 2 );
1049 int isDefault = line.at( 3 );
1050 nlohmann::json ruleData = line.at( 4 );
1051
1052 if( ruleType == wxS( "3" ) && isDefault ) // Track width
1053 {
1054 wxString units = ruleData.at( 0 );
1055 double minVal = ruleData.at( 1 );
1056
1057 bds.m_TrackMinWidth = ScaleSize( minVal );
1058 }
1059 else if( ruleType == wxS( "1" ) && isDefault )
1060 {
1061 wxString units = ruleData.at( 0 );
1062 nlohmann::json table = ruleData.at( 1 );
1063
1064 int minVal = INT_MAX;
1065 for( const auto& arr : table )
1066 {
1067 for( int val : arr )
1068 {
1069 if( val < minVal )
1070 minVal = val;
1071 }
1072 }
1073
1074 bds.m_MinClearance = ScaleSize( minVal );
1075 }
1076
1077 ruleLines[ruleType].push_back( line );
1078 }
1079 else if( type == wxS( "POURED" ) )
1080 {
1081 if( !line.at( 2 ).is_string() )
1082 continue; // Unknown type of POURED
1083
1084 EASYEDAPRO::POURED poured = line;
1085 boardPouredMap.emplace( poured.parentId, poured );
1086 }
1087 else if( type == wxS( "VIA" ) || type == wxS( "LINE" ) || type == wxS( "ARC" )
1088 || type == wxS( "POLY" ) || type == wxS( "FILL" ) || type == wxS( "POUR" ) )
1089 {
1090 wxString uuid = line.at( 1 );
1091
1092 // if( line.at( 2 ).is_number() )
1093 // int unk = line.at( 2 ).get<int>();
1094 // else if( line.at( 2 ).is_string() )
1095 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1096
1097 wxString netname = line.at( 3 );
1098
1099 if( type == wxS( "VIA" ) )
1100 {
1102 center.x = line.at( 5 );
1103 center.y = line.at( 6 );
1104
1105 double drill = line.at( 7 );
1106 double dia = line.at( 8 );
1107
1108 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( aBoard );
1109
1110 via->SetPosition( ScalePos( center ) );
1111 via->SetDrill( ScaleSize( drill ) );
1112 via->SetWidth( PADSTACK::ALL_LAYERS, ScaleSize( dia ) );
1113
1114 via->SetNet( aBoard->FindNet( netname ) );
1115
1116 aBoard->Add( via.release(), ADD_MODE::APPEND );
1117 }
1118 else if( type == wxS( "LINE" ) )
1119 {
1120 int layer = line.at( 4 ).get<int>();
1121 PCB_LAYER_ID klayer = LayerToKi( layer );
1122
1123 VECTOR2D start;
1124 start.x = line.at( 5 );
1125 start.y = line.at( 6 );
1126
1127 VECTOR2D end;
1128 end.x = line.at( 7 );
1129 end.y = line.at( 8 );
1130
1131 double width = line.at( 9 );
1132
1133 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( aBoard );
1134
1135 track->SetLayer( klayer );
1136 track->SetStart( ScalePos( start ) );
1137 track->SetEnd( ScalePos( end ) );
1138 track->SetWidth( ScaleSize( width ) );
1139
1140 track->SetNet( aBoard->FindNet( netname ) );
1141
1142 aBoard->Add( track.release(), ADD_MODE::APPEND );
1143 }
1144 else if( type == wxS( "ARC" ) )
1145 {
1146 int layer = line.at( 4 ).get<int>();
1147 PCB_LAYER_ID klayer = LayerToKi( layer );
1148
1149 VECTOR2D start;
1150 start.x = line.at( 5 );
1151 start.y = line.at( 6 );
1152
1153 VECTOR2D end;
1154 end.x = line.at( 7 );
1155 end.y = line.at( 8 );
1156
1157 double angle = line.at( 9 );
1158 double width = line.at( 10 );
1159
1160 VECTOR2D delta = end - start;
1161 VECTOR2D mid = ( start + delta / 2 );
1162
1163 double ha = angle / 2;
1164 double hd = delta.EuclideanNorm() / 2;
1165 double cdist = hd / tan( DEG2RAD( ha ) );
1166 VECTOR2D center = mid + delta.Perpendicular().Resize( cdist );
1167
1168 SHAPE_ARC sarc;
1170 ScalePos( center ), angle >= 0, width );
1171
1172 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( aBoard, &sarc );
1173 arc->SetWidth( ScaleSize( width ) );
1174
1175 arc->SetLayer( klayer );
1176 arc->SetNet( aBoard->FindNet( netname ) );
1177
1178 aBoard->Add( arc.release(), ADD_MODE::APPEND );
1179 }
1180 else if( type == wxS( "FILL" ) )
1181 {
1182 int layer = line.at( 4 ).get<int>();
1183 PCB_LAYER_ID klayer = LayerToKi( layer );
1184
1185 nlohmann::json polyDataList = line.at( 7 );
1186
1187 if( !polyDataList.at( 0 ).is_array() )
1188 polyDataList = nlohmann::json::array( { polyDataList } );
1189
1190 std::vector<SHAPE_LINE_CHAIN> contours;
1191 for( nlohmann::json& polyData : polyDataList )
1192 {
1193 SHAPE_LINE_CHAIN contour = ParseContour( polyData, true );
1194 contour.SetClosed( true );
1195
1196 contours.push_back( contour );
1197 }
1198
1199 SHAPE_POLY_SET zoneFillPoly;
1200
1201 for( SHAPE_LINE_CHAIN& contour : contours )
1202 zoneFillPoly.AddOutline( contour );
1203
1204 zoneFillPoly.RebuildHolesFromContours();
1205 zoneFillPoly.Fracture();
1206
1207 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1208
1209 zone->SetNet( aBoard->FindNet( netname ) );
1210 zone->SetLayer( klayer );
1211 zone->Outline()->Append( SHAPE_RECT( zoneFillPoly.BBox() ).Outline() );
1212 zone->SetFilledPolysList( klayer, zoneFillPoly );
1213 zone->SetAssignedPriority( 500 );
1214 zone->SetIsFilled( true );
1215 zone->SetNeedRefill( false );
1216
1217 zone->SetLocalClearance( bds.m_MinClearance );
1218 zone->SetMinThickness( bds.m_TrackMinWidth );
1219
1220 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1221 }
1222 else if( type == wxS( "POLY" ) )
1223 {
1224 int layer = line.at( 4 );
1225 PCB_LAYER_ID klayer = LayerToKi( layer );
1226
1227 double thickness = line.at( 5 );
1228 nlohmann::json polyData = line.at( 6 );
1229
1230 std::vector<std::unique_ptr<PCB_SHAPE>> results =
1231 ParsePoly( aBoard, polyData, false, false );
1232
1233 for( auto& shape : results )
1234 {
1235 shape->SetLayer( klayer );
1236 shape->SetWidth( ScaleSize( thickness ) );
1237
1238 aBoard->Add( shape.release(), ADD_MODE::APPEND );
1239 }
1240 }
1241 else if( type == wxS( "POUR" ) )
1242 {
1243 int layer = line.at( 4 ).get<int>();
1244 PCB_LAYER_ID klayer = LayerToKi( layer );
1245
1246 wxString pourname = line.at( 6 );
1247 int fillOrder = line.at( 7 ).get<int>();
1248 nlohmann::json polyDataList = line.at( 8 );
1249
1250 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1251
1252 zone->SetNet( aBoard->FindNet( netname ) );
1253 zone->SetLayer( klayer );
1254 zone->SetAssignedPriority( 500 - fillOrder );
1255 zone->SetLocalClearance( bds.m_MinClearance );
1256 zone->SetMinThickness( bds.m_TrackMinWidth );
1257
1258 for( nlohmann::json& polyData : polyDataList )
1259 {
1260 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1261 contour.SetClosed( true );
1262
1263 zone->Outline()->Append( contour );
1264 }
1265
1266 wxASSERT( zone->Outline()->OutlineCount() == 1 );
1267
1268 poursToFill.emplace( uuid, zone.get() );
1269
1270 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1271 }
1272 }
1273 else if( type == wxS( "TEARDROP" ) )
1274 {
1275 wxString uuid = line.at( 1 );
1276 wxString netname = line.at( 2 );
1277 int layer = line.at( 3 ).get<int>();
1278 PCB_LAYER_ID klayer = LayerToKi( layer );
1279
1280 nlohmann::json polyData = line.at( 4 );
1281
1282 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1283 contour.SetClosed( true );
1284
1285 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1286
1287 zone->SetNet( aBoard->FindNet( netname ) );
1288 zone->SetLayer( klayer );
1289 zone->Outline()->Append( contour );
1290 zone->SetFilledPolysList( klayer, contour );
1291 zone->SetNeedRefill( false );
1292 zone->SetIsFilled( true );
1293
1294 zone->SetAssignedPriority( 600 );
1295 zone->SetLocalClearance( 0 );
1296 zone->SetMinThickness( 0 );
1297 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_UNSPECIFIED );
1298
1299 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1300 }
1301 else if( type == wxS( "REGION" ) )
1302 {
1303 wxString uuid = line.at( 1 );
1304
1305 // if( line.at( 2 ).is_number() )
1306 // int unk = line.at( 2 ).get<int>();
1307 // else if( line.at( 2 ).is_string() )
1308 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1309
1310 int layer = line.at( 3 ).get<int>();
1311 PCB_LAYER_ID klayer = LayerToKi( layer );
1312
1313 std::set<int> flags = line.at( 5 );
1314 nlohmann::json polyDataList = line.at( 6 );
1315
1316 for( nlohmann::json& polyData : polyDataList )
1317 {
1318 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1319 contour.SetClosed( true );
1320
1321 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1322
1323 zone->SetIsRuleArea( true );
1324 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
1325 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
1326 || !!flags.count( 8 ) );
1327 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
1328 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
1329 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
1330
1331 zone->SetLayer( klayer );
1332 zone->Outline()->Append( contour );
1333
1334 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1335 }
1336 }
1337 else if( type == wxS( "PAD" ) )
1338 {
1339 wxString netname = line.at( 3 );
1340
1341 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( aBoard );
1342 std::unique_ptr<PAD> pad = createPAD( footprint.get(), line );
1343
1344 pad->SetNet( aBoard->FindNet( netname ) );
1345
1346 VECTOR2I pos = pad->GetPosition();
1347 EDA_ANGLE orient = pad->GetOrientation();
1348
1349 pad->SetPosition( VECTOR2I() );
1350 pad->SetOrientation( ANGLE_0 );
1351
1352 footprint->Add( pad.release(), ADD_MODE::APPEND );
1353 footprint->SetPosition( pos );
1354 footprint->SetOrientation( orient );
1355
1356 wxString fpName = wxS( "Pad_" ) + line.at( 1 ).get<wxString>();
1357 LIB_ID fpID = EASYEDAPRO::ToKiCadLibID( wxEmptyString, fpName );
1358
1359 footprint->SetFPID( fpID );
1360 footprint->Reference().SetVisible( true );
1361 footprint->Value().SetVisible( true );
1362 footprint->AutoPositionFields();
1363
1364 aBoard->Add( footprint.release(), ADD_MODE::APPEND );
1365 }
1366 else if( type == wxS( "IMAGE" ) )
1367 {
1368 wxString uuid = line.at( 1 );
1369
1370 // if( line.at( 2 ).is_number() )
1371 // int unk = line.at( 2 ).get<int>();
1372 // else if( line.at( 2 ).is_string() )
1373 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1374
1375 int layer = line.at( 3 ).get<int>();
1376 PCB_LAYER_ID klayer = LayerToKi( layer );
1377
1378 VECTOR2D start( line.at( 4 ), line.at( 5 ) );
1379 VECTOR2D size( line.at( 6 ), line.at( 7 ) );
1380
1381 double angle = line.at( 8 ); // from top left corner
1382 int mirror = line.at( 9 );
1383 nlohmann::json polyDataList = line.at( 10 );
1384
1385 BOX2I bbox;
1386 std::vector<SHAPE_LINE_CHAIN> contours;
1387 for( nlohmann::json& polyData : polyDataList )
1388 {
1389 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1390 contour.SetClosed( true );
1391
1392 contours.push_back( contour );
1393
1394 bbox.Merge( contour.BBox() );
1395 }
1396
1397 VECTOR2D scale( ScaleSize( size.x ) / bbox.GetSize().x,
1398 ScaleSize( size.y ) / bbox.GetSize().y );
1399
1400 SHAPE_POLY_SET polySet;
1401
1402 for( SHAPE_LINE_CHAIN& contour : contours )
1403 {
1404 for( int i = 0; i < contour.PointCount(); i++ )
1405 {
1406 VECTOR2I pt = contour.CPoint( i );
1407 contour.SetPoint( i, VECTOR2I( pt.x * scale.x, pt.y * scale.y ) );
1408 }
1409
1410 polySet.AddOutline( contour );
1411 }
1412
1413 polySet.RebuildHolesFromContours();
1414
1415 std::unique_ptr<PCB_GROUP> group;
1416
1417 if( polySet.OutlineCount() > 1 )
1418 group = std::make_unique<PCB_GROUP>( aBoard );
1419
1420 BOX2I polyBBox = polySet.BBox();
1421
1422 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
1423 {
1424 std::unique_ptr<PCB_SHAPE> shape =
1425 std::make_unique<PCB_SHAPE>( aBoard, SHAPE_T::POLY );
1426
1427 shape->SetFilled( true );
1428 shape->SetPolyShape( poly );
1429 shape->SetLayer( klayer );
1430 shape->SetWidth( 0 );
1431
1432 shape->Move( ScalePos( start ) - polyBBox.GetOrigin() );
1433 shape->Rotate( ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
1434
1435 if( IsBackLayer( klayer ) ^ !!mirror )
1436 {
1437 FLIP_DIRECTION flipDirection = IsBackLayer( klayer )
1440 shape->Mirror( ScalePos( start ), flipDirection );
1441 }
1442
1443 if( group )
1444 group->AddItem( shape.get() );
1445
1446 aBoard->Add( shape.release(), ADD_MODE::APPEND );
1447 }
1448
1449 if( group )
1450 aBoard->Add( group.release(), ADD_MODE::APPEND );
1451 }
1452 else if( type == wxS( "OBJ" ) )
1453 {
1454 VECTOR2D start, size;
1455 wxString mimeType, base64Data;
1456 double angle = 0;
1457 int flipped = 0;
1458
1459 if( !line.at( 3 ).is_number() )
1460 continue;
1461
1462 int layer = line.at( 3 ).get<int>();
1463 PCB_LAYER_ID klayer = LayerToKi( layer );
1464
1465 start = VECTOR2D( line.at( 5 ), line.at( 6 ) );
1466 size = VECTOR2D( line.at( 7 ), line.at( 8 ) );
1467 angle = line.at( 9 );
1468 flipped = line.at( 10 );
1469
1470 wxString imageUrl = line.at( 11 );
1471
1472 if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
1473 {
1474 wxString objectId = imageUrl.AfterLast( ':' );
1475
1476 if( auto blob = get_opt( aBlobMap, objectId ) )
1477 {
1478 wxString blobUrl = blob->url;
1479
1480 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1481 {
1482 wxArrayString paramsArr =
1483 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1484
1485 base64Data = blobUrl.AfterFirst( ',' );
1486
1487 if( paramsArr.size() > 0 )
1488 mimeType = paramsArr[0];
1489 }
1490 }
1491 }
1492
1493 VECTOR2D kstart = ScalePos( start );
1494 VECTOR2D ksize = ScaleSize( size );
1495
1496 if( mimeType.empty() || base64Data.empty() )
1497 continue;
1498
1499 wxMemoryBuffer buf = wxBase64Decode( base64Data );
1500
1501 if( mimeType == wxS( "image/svg+xml" ) )
1502 {
1503 // Not yet supported by EasyEDA
1504 }
1505 else
1506 {
1507 VECTOR2D kcenter = kstart + ksize / 2;
1508
1509 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap =
1510 std::make_unique<PCB_REFERENCE_IMAGE>( aBoard, kcenter, klayer );
1511 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1512
1513 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1514 & ~wxImage::Load_Verbose );
1515
1516 if( refImage.ReadImageFile( buf ) )
1517 {
1518 double scaleFactor = ScaleSize( size.x ) / refImage.GetSize().x;
1519 refImage.SetImageScale( scaleFactor );
1520
1521 // TODO: support non-90-deg angles
1522 bitmap->Rotate( kstart, EDA_ANGLE( angle, DEGREES_T ) );
1523
1524 if( flipped )
1525 {
1526 int x = bitmap->GetPosition().x;
1527 MIRROR( x, KiROUND( kstart.x ) );
1528 bitmap->SetX( x );
1529
1531 }
1532
1533 aBoard->Add( bitmap.release(), ADD_MODE::APPEND );
1534 }
1535 }
1536 }
1537 else if( type == wxS( "STRING" ) )
1538 {
1539 wxString uuid = line.at( 1 );
1540
1541 // if( line.at( 2 ).is_number() )
1542 // int unk = line.at( 2 ).get<int>();
1543 // else if( line.at( 2 ).is_string() )
1544 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1545
1546 int layer = line.at( 3 ).get<int>();
1547 PCB_LAYER_ID klayer = LayerToKi( layer );
1548
1549 VECTOR2D location( line.at( 4 ), line.at( 5 ) );
1550 wxString string = line.at( 6 );
1551 wxString font = line.at( 7 );
1552
1553 double height = line.at( 8 );
1554 double strokew = line.at( 9 );
1555
1556 int align = line.at( 12 );
1557 double angle = line.at( 13 );
1558 int inverted = line.at( 14 );
1559 int mirror = line.at( 16 );
1560
1561 PCB_TEXT* text = new PCB_TEXT( aBoard );
1562
1563 text->SetText( string );
1564 text->SetLayer( klayer );
1565 text->SetPosition( ScalePos( location ) );
1566 text->SetIsKnockout( inverted );
1567 text->SetTextThickness( ScaleSize( strokew ) );
1568 text->SetTextSize( VECTOR2D( ScaleSize( height * 0.6 ), ScaleSize( height * 0.7 ) ) );
1569
1570 if( font != wxS( "default" ) )
1571 {
1572 text->SetFont( KIFONT::FONT::GetFont( font ) );
1573 //text->SetupRenderCache( text->GetShownText(), text->GetFont(), EDA_ANGLE( angle, DEGREES_T ) );
1574
1575 //text->AddRenderCacheGlyph();
1576 // TODO: import geometry cache
1577 }
1578
1579 AlignText( text, align );
1580
1581 if( IsBackLayer( klayer ) ^ !!mirror )
1582 {
1583 text->SetMirrored( true );
1584 text->SetTextAngleDegrees( -angle );
1585 }
1586 else
1587 {
1588 text->SetTextAngleDegrees( angle );
1589 }
1590
1591 aBoard->Add( text, ADD_MODE::APPEND );
1592 }
1593 else if( type == wxS( "COMPONENT" ) )
1594 {
1595 wxString compId = line.at( 1 );
1596 componentLines[compId].push_back( line );
1597 }
1598 else if( type == wxS( "ATTR" ) )
1599 {
1600 wxString compId = line.at( 3 );
1601 componentLines[compId].push_back( line );
1602 }
1603 else if( type == wxS( "PAD_NET" ) )
1604 {
1605 wxString compId = line.at( 1 );
1606 componentLines[compId].push_back( line );
1607 }
1608 }
1609
1610 for( auto const& [compId, lines] : componentLines )
1611 {
1612 wxString deviceId;
1613 wxString fpIdOverride;
1614 wxString fpDesignator;
1615 std::map<wxString, wxString> localCompAttribs;
1616
1617 for( auto& line : lines )
1618 {
1619 if( line.size() == 0 )
1620 continue;
1621
1622 wxString type = line.at( 0 );
1623
1624 if( type == wxS( "COMPONENT" ) )
1625 {
1626 localCompAttribs = line.at( 7 );
1627 }
1628 else if( type == wxS( "ATTR" ) )
1629 {
1630 EASYEDAPRO::PCB_ATTR attr = line;
1631
1632 if( attr.key == wxS( "Device" ) )
1633 deviceId = attr.value;
1634
1635 else if( attr.key == wxS( "Footprint" ) )
1636 fpIdOverride = attr.value;
1637
1638 else if( attr.key == wxS( "Designator" ) )
1639 fpDesignator = attr.value;
1640 }
1641 }
1642
1643 if( deviceId.empty() )
1644 continue;
1645
1646 nlohmann::json compAttrs = aProject.at( "devices" ).at( deviceId ).at( "attributes" );
1647
1648 wxString fpId;
1649
1650 if( !fpIdOverride.IsEmpty() )
1651 fpId = fpIdOverride;
1652 else
1653 fpId = compAttrs.at( "Footprint" ).get<wxString>();
1654
1655 auto it = aFootprintMap.find( fpId );
1656 if( it == aFootprintMap.end() )
1657 {
1658 wxLogError( "Footprint of '%s' with uuid '%s' not found.", fpDesignator, fpId );
1659 continue;
1660 }
1661
1662 std::unique_ptr<FOOTPRINT>& footprintOrig = it->second;
1663 std::unique_ptr<FOOTPRINT> footprint( static_cast<FOOTPRINT*>( footprintOrig->Clone() ) );
1664
1665 wxString modelUuid, modelTitle, modelTransform;
1666
1667 if( auto val = get_opt( localCompAttribs, "3D Model" ) )
1668 modelUuid = *val;
1669 else
1670 modelUuid = compAttrs.value<wxString>( "3D Model", "" );
1671
1672 if( auto val = get_opt( localCompAttribs, "3D Model Title" ) )
1673 modelTitle = val->Trim();
1674 else
1675 modelTitle = compAttrs.value<wxString>( "3D Model Title", modelUuid ).Trim();
1676
1677 if( auto val = get_opt( localCompAttribs, "3D Model Transform" ) )
1678 modelTransform = *val;
1679 else
1680 modelTransform = compAttrs.value<wxString>( "3D Model Transform", "" );
1681
1682 fillFootprintModelInfo( footprint.get(), modelUuid, modelTitle, modelTransform );
1683
1684 footprint->SetParent( aBoard );
1685
1686 for( auto& line : lines )
1687 {
1688 if( line.size() == 0 )
1689 continue;
1690
1691 wxString type = line.at( 0 );
1692
1693 if( type == wxS( "COMPONENT" ) )
1694 {
1695 int layer = line.at( 3 );
1696 PCB_LAYER_ID klayer = LayerToKi( layer );
1697
1698 VECTOR2D center( line.at( 4 ), line.at( 5 ) );
1699
1700 double orient = line.at( 6 );
1701 //std::map<wxString, wxString> props = line.at( 7 );
1702
1703 if( klayer == B_Cu )
1704 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1705
1706 footprint->SetOrientationDegrees( orient );
1707 footprint->SetPosition( ScalePos( center ) );
1708 }
1709 else if( type == wxS( "ATTR" ) )
1710 {
1711 EASYEDAPRO::PCB_ATTR attr = line;
1712
1713 PCB_LAYER_ID klayer = LayerToKi( attr.layer );
1714
1715 PCB_FIELD* field = nullptr;
1716
1717 if( attr.key == wxS( "Designator" ) )
1718 {
1719 if( attr.key == wxS( "Designator" ) )
1720 {
1721 field = footprint->GetField( FIELD_T::REFERENCE );
1722 }
1723 else
1724 {
1725 field = new PCB_FIELD( footprint.get(), FIELD_T::USER, attr.key );
1726 footprint->Add( field, ADD_MODE::APPEND );
1727 }
1728
1729 if( attr.fontName != wxS( "default" ) )
1730 field->SetFont( KIFONT::FONT::GetFont( attr.fontName ) );
1731
1732 if( attr.valVisible && attr.keyVisible )
1733 {
1734 field->SetText( attr.key + ':' + attr.value );
1735 }
1736 else if( attr.keyVisible )
1737 {
1738 field->SetText( attr.key );
1739 }
1740 else
1741 {
1742 field->SetVisible( false );
1743 field->SetText( attr.value );
1744 }
1745
1746 field->SetLayer( klayer );
1747 field->SetPosition( ScalePos( attr.position ) );
1748 field->SetTextAngleDegrees( footprint->IsFlipped() ? -attr.rotation
1749 : attr.rotation );
1750 field->SetIsKnockout( attr.inverted );
1751 field->SetTextThickness( ScaleSize( attr.strokeWidth ) );
1752 field->SetTextSize( VECTOR2D( ScaleSize( attr.height * 0.55 ),
1753 ScaleSize( attr.height * 0.6 ) ) );
1754
1755 AlignText( field, attr.textOrigin );
1756 }
1757 }
1758 else if( type == wxS( "PAD_NET" ) )
1759 {
1760 wxString padNumber = line.at( 2 );
1761 wxString padNet = line.at( 3 );
1762
1763 PAD* pad = footprint->FindPadByNumber( padNumber );
1764 if( pad )
1765 {
1766 pad->SetNet( aBoard->FindNet( padNet ) );
1767 }
1768 else
1769 {
1770 // Not a pad
1771 }
1772 }
1773 }
1774
1775 aBoard->Add( footprint.release(), ADD_MODE::APPEND );
1776 }
1777
1778 // Set zone fills
1780 {
1781 for( auto& [uuid, zone] : poursToFill )
1782 {
1783 SHAPE_POLY_SET fillPolySet;
1784 SHAPE_POLY_SET thermalSpokes;
1785
1786 auto range = boardPouredMap.equal_range( uuid );
1787 for( auto& it = range.first; it != range.second; ++it )
1788 {
1789 const EASYEDAPRO::POURED& poured = it->second;
1790
1791 SHAPE_POLY_SET thisPoly;
1792
1793 for( int dataId = 0; dataId < (int) poured.polyData.size(); dataId++ )
1794 {
1795 const nlohmann::json& fillData = poured.polyData[dataId];
1796 const double ptScale = 10;
1797
1798 SHAPE_LINE_CHAIN contour = ParseContour( fillData, false, ARC_HIGH_DEF / ptScale );
1799
1800 // Scale the fill
1801 for( int i = 0; i < contour.PointCount(); i++ )
1802 contour.SetPoint( i, contour.GetPoint( i ) * ptScale );
1803
1804 if( poured.isPoly )
1805 {
1806 contour.SetClosed( true );
1807
1808 // The contour can be self-intersecting
1809 SHAPE_POLY_SET simple( contour );
1810 simple.Simplify();
1811
1812 if( dataId == 0 )
1813 {
1814 thisPoly.Append( simple );
1815 }
1816 else
1817 {
1818 thisPoly.BooleanSubtract( simple );
1819 }
1820 }
1821 else
1822 {
1823 const int thermalWidth = pcbIUScale.mmToIU( 0.2 ); // Generic
1824
1825 for( int segId = 0; segId < contour.SegmentCount(); segId++ )
1826 {
1827 const SEG& seg = contour.CSegment( segId );
1828
1829 TransformOvalToPolygon( thermalSpokes, seg.A, seg.B, thermalWidth,
1831 }
1832 }
1833 }
1834
1835 fillPolySet.Append( thisPoly );
1836 }
1837
1838 if( !fillPolySet.IsEmpty() )
1839 {
1840 fillPolySet.Simplify();
1841
1842 const int strokeWidth = pcbIUScale.MilsToIU( 8 ); // Seems to be 8 mils
1843
1844 fillPolySet.Inflate( strokeWidth / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
1845 ARC_HIGH_DEF, false );
1846
1847 fillPolySet.BooleanAdd( thermalSpokes );
1848
1849 fillPolySet.Fracture();
1850
1851 zone->SetFilledPolysList( zone->GetFirstLayer(), fillPolySet );
1852 zone->SetNeedRefill( false );
1853 zone->SetIsFilled( true );
1854 }
1855 }
1856 }
1857
1858 // Heal board outlines
1859 std::vector<PCB_SHAPE*> shapes;
1860
1861 for( BOARD_ITEM* item : aBoard->Drawings() )
1862 {
1863 if( !item->IsOnLayer( Edge_Cuts ) )
1864 continue;
1865
1866 if( item->Type() == PCB_SHAPE_T )
1867 shapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1868 }
1869
1871
1872 // Center the board
1873 BOX2I outlineBbox = aBoard->ComputeBoundingBox( true, true );
1874 PAGE_INFO pageInfo = aBoard->GetPageSettings();
1875
1876 VECTOR2D pageCenter( pcbIUScale.MilsToIU( pageInfo.GetWidthMils() / 2 ),
1877 pcbIUScale.MilsToIU( pageInfo.GetHeightMils() / 2 ) );
1878
1879 VECTOR2D offset = pageCenter - outlineBbox.GetCenter();
1880
1881 int alignGrid = pcbIUScale.mmToIU( 10 );
1882 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1883 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1884
1885 aBoard->Move( offset );
1886 bds.SetAuxOrigin( offset );
1887}
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:129
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
#define DEFAULT_COURTYARD_WIDTH
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
BASE_SET & set(size_t pos)
Definition base_set.h:116
Bezier curves to polygon converter.
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
void Mirror(FLIP_DIRECTION aFlipDirection)
Mirror image vertically (i.e.
Container for design settings for a BOARD object.
void SetAuxOrigin(const VECTOR2I &aOrigin)
Abstract interface for BOARD_ITEMs capable of storing other items inside.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:325
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:285
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1223
const PAGE_INFO & GetPageSettings() const
Definition board.h:799
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2375
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition board.cpp:747
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition board.cpp:616
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1083
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:968
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2117
unsigned GetNetCount() const
Definition board.h:1027
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:988
const DRAWINGS & Drawings() const
Definition board.h:365
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:558
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
constexpr const Vec GetCenter() const
Definition box2.h:230
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr const SizeVec & GetSize() const
Definition box2.h:206
EDA_ANGLE Normalize90()
Definition eda_angle.h:257
double AsDegrees() const
Definition eda_angle.h:116
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:80
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:546
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:400
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:298
void SetTextAngleDegrees(double aOrientation)
Definition eda_text.h:150
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:284
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:513
const BOX2I GetLayerBoundingBox(const LSET &aLayers) const
Return the bounding box of the footprint on a given set of layers.
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:323
DRAWINGS & GraphicalItems()
Definition footprint.h:309
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:147
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:49
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Handle the data for a net.
Definition netinfo.h:54
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:369
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:376
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:79
double GetHeightMils() const
Definition page_info.h:147
double GetWidthMils() const
Definition page_info.h:142
static VECTOR2< T > ScalePos(VECTOR2< T > aValue)
PCB_IO_EASYEDAPRO_PARSER(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter)
std::vector< std::unique_ptr< PCB_SHAPE > > ParsePoly(BOARD_ITEM_CONTAINER *aContainer, nlohmann::json polyData, bool aClosed, bool aInFill) const
SHAPE_LINE_CHAIN ParseContour(nlohmann::json polyData, bool aInFill, int aMaxError=SHAPE_ARC::DefaultAccuracyForPCB()) const
void ParseBoard(BOARD *aBoard, const nlohmann::json &aProject, std::map< wxString, std::unique_ptr< FOOTPRINT > > &aFootprintMap, const std::map< wxString, EASYEDAPRO::BLOB > &aBlobMap, const std::multimap< wxString, EASYEDAPRO::POURED > &aPouredMap, const std::vector< nlohmann::json > &aLines, const wxString &aFpLibName)
std::unique_ptr< PAD > createPAD(FOOTPRINT *aFootprint, const nlohmann::json &line)
PCB_LAYER_ID LayerToKi(int aLayer)
FOOTPRINT * ParseFootprint(const nlohmann::json &aProject, const wxString &aFpUuid, const std::vector< nlohmann::json > &aLines)
static double Convert(wxString aValue)
void fillFootprintModelInfo(FOOTPRINT *footprint, const wxString &modelUuid, const wxString &modelTitle, const wxString &modelTransform) const
virtual void SetPosition(const VECTOR2I &aPos) override
Definition pcb_text.h:84
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...
BITMAP_BASE & MutableImage() const
Only use this if you really need to modify the underlying image.
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
VECTOR2I GetSize() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
SHAPE_ARC & ConstructFromStartEndCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aClockwise=false, double aWidth=0)
Constructs this arc from the given start, end and center.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
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.
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.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void RebuildHolesFromContours()
Extract all contours from this polygon set, then recreate polygons with holes.
int OutlineCount() const
Return the number of outlines in the set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const std::vector< POLYGON > & CPolygons() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
const SHAPE_LINE_CHAIN Outline() const
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
@ ROUND_ALL_CORNERS
All angles are rounded.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ DEGREES_T
Definition eda_angle.h:31
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, int aChainingEpsilon)
Connects shapes to each other, making continious contours (adjacent shapes will have a common vertex)...
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:803
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ In22_Cu
Definition layer_ids.h:87
@ In11_Cu
Definition layer_ids.h:76
@ In29_Cu
Definition layer_ids.h:94
@ In30_Cu
Definition layer_ids.h:95
@ F_CrtYd
Definition layer_ids.h:116
@ In17_Cu
Definition layer_ids.h:82
@ Edge_Cuts
Definition layer_ids.h:112
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ In9_Cu
Definition layer_ids.h:74
@ Cmts_User
Definition layer_ids.h:108
@ User_6
Definition layer_ids.h:129
@ User_7
Definition layer_ids.h:130
@ In19_Cu
Definition layer_ids.h:84
@ In7_Cu
Definition layer_ids.h:72
@ In28_Cu
Definition layer_ids.h:93
@ In26_Cu
Definition layer_ids.h:91
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ User_5
Definition layer_ids.h:128
@ F_Mask
Definition layer_ids.h:97
@ In21_Cu
Definition layer_ids.h:86
@ In23_Cu
Definition layer_ids.h:88
@ B_Paste
Definition layer_ids.h:105
@ In15_Cu
Definition layer_ids.h:80
@ In2_Cu
Definition layer_ids.h:67
@ F_Fab
Definition layer_ids.h:119
@ In10_Cu
Definition layer_ids.h:75
@ F_SilkS
Definition layer_ids.h:100
@ In4_Cu
Definition layer_ids.h:69
@ Eco2_User
Definition layer_ids.h:110
@ In16_Cu
Definition layer_ids.h:81
@ In24_Cu
Definition layer_ids.h:89
@ In1_Cu
Definition layer_ids.h:66
@ User_1
Definition layer_ids.h:124
@ B_SilkS
Definition layer_ids.h:101
@ In13_Cu
Definition layer_ids.h:78
@ User_4
Definition layer_ids.h:127
@ In8_Cu
Definition layer_ids.h:73
@ In14_Cu
Definition layer_ids.h:79
@ In12_Cu
Definition layer_ids.h:77
@ In27_Cu
Definition layer_ids.h:92
@ In6_Cu
Definition layer_ids.h:71
@ In5_Cu
Definition layer_ids.h:70
@ In3_Cu
Definition layer_ids.h:68
@ In20_Cu
Definition layer_ids.h:85
@ F_Cu
Definition layer_ids.h:64
@ In18_Cu
Definition layer_ids.h:83
@ In25_Cu
Definition layer_ids.h:90
@ B_Fab
Definition layer_ids.h:118
wxString get_def(const std::map< wxString, wxString > &aMap, const char *aKey, const char *aDefval="")
Definition map_helpers.h:64
std::optional< V > get_opt(const std::map< wxString, V > &aMap, const wxString &aKey)
Definition map_helpers.h:34
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:45
FLIP_DIRECTION
Definition mirror.h:27
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:28
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
LIB_ID ToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
static const bool IMPORT_POURED
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ ROUNDRECT
Definition padstack.h:57
@ RECTANGLE
Definition padstack.h:54
Class to handle a set of BOARD_ITEMs.
static const wxString MODEL_SIZE_KEY
static const int SHAPE_JOIN_DISTANCE
static void AlignText(EDA_TEXT *text, int align)
static const wxString QUERY_MODEL_UUID_KEY
static bool addSegment(VRML_LAYER &model, IDF_SEGMENT *seg, int icont, int iseg)
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:...
@ CTX_FILENAME
nlohmann::json polyData
@ USER
The field ID hasn't been set yet; field is invalid.
@ REFERENCE
Field Reference of part, i.e. "IC21".
KIBIS_MODEL * model
VECTOR2I center
const SHAPE_LINE_CHAIN chain
VECTOR2I end
VECTOR2I location
wxString result
Test unit parsing edge cases and error handling.
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
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
double DEG2RAD(double deg)
Definition trigo.h:166
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694
VECTOR3< double > VECTOR3D
Definition vector3.h:230