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