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