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
633 // Check if this pad has a real drill hole
634 // JLCEDA may use ["ROUND",0,0] to indicate SMD pads
635 bool hasHole = false;
636
637 if( !padHole.is_null() && !padHole.empty() )
638 {
639 wxString holeShape = padHole.at( 0 );
640
641 if( holeShape == wxS( "ROUND" ) || holeShape == wxS( "SLOT" ) )
642 {
643 VECTOR2D drill;
644 drill.x = padHole.at( 1 );
645 drill.y = padHole.at( 2 );
646
647 // Only treat as PTH if hole size is non-zero
648 if( drill.x > 0 || drill.y > 0 )
649 {
650 hasHole = true;
651
652 double drill_dir = 0;
653
654 if( line.at( 14 ).is_number() )
655 drill_dir = line.at( 14 );
656
657 double deg = EDA_ANGLE( drill_dir, DEGREES_T ).Normalize90().AsDegrees();
658
659 if( std::abs( deg ) >= 45 )
660 std::swap( drill.x, drill.y ); // KiCad doesn't support arbitrary hole direction
661
662 if( holeShape == wxS( "SLOT" ) )
663 {
664 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
665 }
666
667 pad->SetDrillSize( ScaleSize( drill ) );
668 pad->SetLayerSet( PAD::PTHMask() );
669 pad->SetAttribute( PAD_ATTRIB::PTH );
670 }
671 }
672 }
673
674 // If no valid hole, this is an SMD pad
675 if( !hasHole )
676 {
677 if( klayer == F_Cu )
678 {
679 pad->SetLayerSet( PAD::SMDMask() );
680 }
681 else if( klayer == B_Cu )
682 {
683 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
684 }
685
686 pad->SetAttribute( PAD_ATTRIB::SMD );
687 }
688
689 wxString padSh = padShape.at( 0 );
690 if( padSh == wxS( "RECT" ) )
691 {
692 VECTOR2D size;
693 size.x = padShape.at( 1 );
694 size.y = padShape.at( 2 );
695 double cr_p = padShape.size() > 3 ? padShape.at( 3 ).get<double>() : 0;
696
697 pad->SetSize( PADSTACK::ALL_LAYERS, ScaleSize( size ) );
698
699 if( cr_p == 0 )
700 {
702 }
703 else
704 {
706 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, cr_p / 100 );
707 }
708 }
709 else if( padSh == wxS( "ELLIPSE" ) )
710 {
711 VECTOR2D size;
712 size.x = padShape.at( 1 );
713 size.y = padShape.at( 2 );
714
715 pad->SetSize( PADSTACK::ALL_LAYERS, ScaleSize( size ) );
717 }
718 else if( padSh == wxS( "OVAL" ) )
719 {
720 VECTOR2D size;
721 size.x = padShape.at( 1 );
722 size.y = padShape.at( 2 );
723
724 pad->SetSize( PADSTACK::ALL_LAYERS, ScaleSize( size ) );
726 }
727 else if( padSh == wxS( "POLY" ) || padSh == wxS( "POLYGON" ) )
728 {
730 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
731 pad->SetSize( PADSTACK::ALL_LAYERS, { 1, 1 } );
732
733 nlohmann::json polyData = padShape.at( 1 );
734
735 std::vector<std::unique_ptr<PCB_SHAPE>> results =
736 ParsePoly( aFootprint, polyData, true, false );
737
738 for( auto& shape : results )
739 {
740 shape->SetLayer( klayer );
741 shape->SetWidth( 0 );
742
743 shape->Move( -pad->GetPosition() );
744
745 pad->AddPrimitive( PADSTACK::ALL_LAYERS, shape.release() );
746 }
747 }
748
749 pad->SetThermalSpokeAngle( ANGLE_90 );
750
751 return pad;
752}
753
754
756 const wxString& aFpUuid,
757 const std::vector<nlohmann::json>& aLines )
758{
759 std::unique_ptr<FOOTPRINT> footprintPtr = std::make_unique<FOOTPRINT>( m_board );
760 FOOTPRINT* footprint = footprintPtr.get();
761
762 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
763 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
764
765 for( PCB_FIELD* field : footprint->GetFields() )
766 {
767 field->SetTextSize( defaultTextSize );
768 field->SetTextThickness( defaultTextThickness );
769 }
770
771 for( const nlohmann::json& line : aLines )
772 {
773 if( line.size() == 0 )
774 continue;
775
776 wxString type = line.at( 0 );
777
778 if( type == wxS( "POLY" ) || type == wxS( "PAD" ) || type == wxS( "FILL" )
779 || type == wxS( "ATTR" ) )
780 {
781 wxString uuid = line.at( 1 );
782
783 // if( line.at( 2 ).is_number() )
784 // int unk = line.at( 2 ).get<int>();
785 // else if( line.at( 2 ).is_string() )
786 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
787
788 wxString netname = line.at( 3 );
789 int layer = line.at( 4 ).get<int>();
790 PCB_LAYER_ID klayer = LayerToKi( layer );
791
792 if( type == wxS( "POLY" ) )
793 {
794 double thickness = ( line.at( 5 ) );
795 nlohmann::json polyData = line.at( 6 );
796
797 std::vector<std::unique_ptr<PCB_SHAPE>> results =
798 ParsePoly( footprint, polyData, false, false );
799
800 for( auto& shape : results )
801 {
802 shape->SetLayer( klayer );
803 shape->SetWidth( ScaleSize( thickness ) );
804
805 footprint->Add( shape.release(), ADD_MODE::APPEND );
806 }
807 }
808 else if( type == wxS( "PAD" ) )
809 {
810 std::unique_ptr<PAD> pad = createPAD( footprint, line );
811
812 footprint->Add( pad.release(), ADD_MODE::APPEND );
813 }
814 else if( type == wxS( "FILL" ) )
815 {
816 int fillLayer = line.at( 4 ).get<int>();
817 PCB_LAYER_ID fillKlayer = LayerToKi( fillLayer );
818
819 nlohmann::json polyDataList = line.at( 7 );
820
821 if( !polyDataList.is_null() && !polyDataList.empty() )
822 {
823 if( !polyDataList.at( 0 ).is_array() )
824 polyDataList = nlohmann::json::array( { polyDataList } );
825
826 std::vector<SHAPE_LINE_CHAIN> contours;
827 for( nlohmann::json& polyData : polyDataList )
828 {
829 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
830 contour.SetClosed( true );
831
832 contours.push_back( contour );
833 }
834
835 SHAPE_POLY_SET polySet;
836
837 for( SHAPE_LINE_CHAIN& contour : contours )
838 polySet.AddOutline( contour );
839
840 polySet.RebuildHolesFromContours();
841
842 std::unique_ptr<PCB_GROUP> group;
843
844 if( polySet.OutlineCount() > 1 )
845 group = std::make_unique<PCB_GROUP>( footprint );
846
847 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
848 {
849 std::unique_ptr<PCB_SHAPE> shape =
850 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::POLY );
851
852 shape->SetFilled( true );
853 shape->SetPolyShape( poly );
854 shape->SetLayer( fillKlayer );
855 shape->SetWidth( 0 );
856
857 if( group )
858 group->AddItem( shape.get() );
859
860 footprint->Add( shape.release(), ADD_MODE::APPEND );
861 }
862
863 if( group )
864 footprint->Add( group.release(), ADD_MODE::APPEND );
865 }
866 }
867 else if( type == wxS( "ATTR" ) )
868 {
869 EASYEDAPRO::PCB_ATTR attr = line;
870
871 if( attr.key == wxS( "Designator" ) )
872 footprint->GetField( FIELD_T::REFERENCE )->SetText( attr.value );
873 }
874 }
875 else if( type == wxS( "REGION" ) )
876 {
877 wxString uuid = line.at( 1 );
878
879 // if( line.at( 2 ).is_number() )
880 // int unk = line.at( 2 ).get<int>();
881 // else if( line.at( 2 ).is_string() )
882 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
883
884 int layer = line.at( 3 ).get<int>();
885 PCB_LAYER_ID klayer = LayerToKi( layer );
886
887 std::set<int> flags = line.at( 5 );
888 nlohmann::json polyDataList = line.at( 6 );
889
890 for( nlohmann::json& polyData : polyDataList )
891 {
892 SHAPE_POLY_SET polySet;
893
894 std::vector<std::unique_ptr<PCB_SHAPE>> results =
895 ParsePoly( nullptr, polyData, true, false );
896
897 for( auto& shape : results )
898 {
899 shape->SetFilled( true );
900 shape->TransformShapeToPolygon( polySet, klayer, 0, ARC_HIGH_DEF, ERROR_INSIDE,
901 true );
902 }
903
904 polySet.Simplify();
905
906 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( footprint );
907
908 zone->SetIsRuleArea( true );
909 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
910 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
911 || !!flags.count( 8 ) );
912 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
913 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
914 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
915
916 zone->SetLayer( klayer );
917 zone->Outline()->Append( polySet );
918
919 footprint->Add( zone.release(), ADD_MODE::APPEND );
920 }
921 }
922 }
923
924 if( aProject.is_object() && aProject.contains( "devices" ) )
925 {
926 std::map<wxString, EASYEDAPRO::PRJ_DEVICE> devicesMap = aProject.at( "devices" );
927 std::map<wxString, wxString> compAttrs;
928
929 for( auto& [devUuid, devData] : devicesMap )
930 {
931 if( auto fp = get_opt( devData.attributes, "Footprint" ) )
932 {
933 if( *fp == aFpUuid )
934 {
935 compAttrs = devData.attributes;
936 break;
937 }
938 }
939 }
940
941 wxString modelUuid, modelTitle, modelTransform;
942
943 modelUuid = get_def( compAttrs, "3D Model", "" );
944 modelTitle = get_def( compAttrs, "3D Model Title", modelUuid );
945 modelTransform = get_def( compAttrs, "3D Model Transform", "" );
946
947 FillFootprintModelInfo( footprint, modelUuid, modelTitle, modelTransform );
948 }
949
950 // Heal board outlines
951 std::vector<PCB_SHAPE*> edgeShapes;
952
953 for( BOARD_ITEM* item : footprint->GraphicalItems() )
954 {
955 if( item->IsOnLayer( Edge_Cuts ) && item->Type() == PCB_SHAPE_T )
956 edgeShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
957 }
958
960
961 // EasyEDA footprints don't have courtyard, so build a box ourselves
962 if( !footprint->IsOnLayer( F_CrtYd ) )
963 {
964 BOX2I bbox = footprint->GetLayerBoundingBox( { F_Cu, F_Fab, F_Paste, F_Mask, Edge_Cuts } );
965 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) ); // Default courtyard clearance
966
967 std::unique_ptr<PCB_SHAPE> shape =
968 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::RECTANGLE );
969
970 shape->SetWidth( pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ) );
971 shape->SetLayer( F_CrtYd );
972 shape->SetStart( bbox.GetOrigin() );
973 shape->SetEnd( bbox.GetEnd() );
974
975 footprint->Add( shape.release(), ADD_MODE::APPEND );
976 }
977
978 bool hasFabRef = false;
979
980 for( BOARD_ITEM* item : footprint->GraphicalItems() )
981 {
982 if( item->Type() == PCB_TEXT_T && item->IsOnLayer( F_Fab ) )
983 {
984 if( static_cast<PCB_TEXT*>( item )->GetText() == wxT( "${REFERENCE}" ) )
985 {
986 hasFabRef = true;
987 break;
988 }
989 }
990 }
991
992 if( !hasFabRef )
993 {
994 // Add reference text field on F_Fab
995 int c_refTextSize = pcbIUScale.mmToIU( 0.5 ); // KLC min Fab text size
996 int c_refTextThickness = pcbIUScale.mmToIU( 0.1 ); // Decent text thickness
997 std::unique_ptr<PCB_TEXT> refText = std::make_unique<PCB_TEXT>( footprint );
998
999 refText->SetLayer( F_Fab );
1000 refText->SetTextSize( VECTOR2I( c_refTextSize, c_refTextSize ) );
1001 refText->SetTextThickness( c_refTextThickness );
1002 refText->SetText( wxT( "${REFERENCE}" ) );
1003
1004 footprint->Add( refText.release(), ADD_MODE::APPEND );
1005 }
1006
1007 return footprintPtr.release();
1008}
1009
1010
1012 BOARD* aBoard, const nlohmann::json& aProject,
1013 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
1014 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
1015 const std::multimap<wxString, EASYEDAPRO::POURED>& aPouredMap,
1016 const std::vector<nlohmann::json>& aLines, const wxString& aFpLibName )
1017{
1018 std::map<wxString, std::vector<nlohmann::json>> componentLines;
1019 std::map<wxString, std::vector<nlohmann::json>> ruleLines;
1020
1021 std::multimap<wxString, EASYEDAPRO::POURED> boardPouredMap = aPouredMap;
1022 std::map<wxString, ZONE*> poursToFill;
1023
1025
1026 for( const nlohmann::json& line : aLines )
1027 {
1028 if( line.size() == 0 )
1029 continue;
1030
1031 wxString type = line.at( 0 );
1032
1033 if( type == wxS( "LAYER" ) )
1034 {
1035 int layer = line.at( 1 );
1036 PCB_LAYER_ID klayer = LayerToKi( layer );
1037
1038 wxString layerType = line.at( 2 );
1039 wxString layerName = line.at( 3 );
1040 int layerFlag = line.at( 4 );
1041
1042 if( layerFlag != 0 )
1043 {
1044 LSET blayers = aBoard->GetEnabledLayers();
1045 blayers.set( klayer );
1046 aBoard->SetEnabledLayers( blayers );
1047 aBoard->SetLayerName( klayer, layerName );
1048 }
1049 }
1050 else if( type == wxS( "NET" ) )
1051 {
1052 wxString netname = line.at( 1 );
1053
1054 aBoard->Add( new NETINFO_ITEM( aBoard, netname, aBoard->GetNetCount() + 1 ),
1056 }
1057 else if( type == wxS( "RULE" ) )
1058 {
1059 wxString ruleType = line.at( 1 );
1060 wxString ruleName = line.at( 2 );
1061 int isDefault = line.at( 3 );
1062 nlohmann::json ruleData = line.at( 4 );
1063
1064 if( ruleType == wxS( "3" ) && isDefault ) // Track width
1065 {
1066 wxString units = ruleData.at( 0 );
1067
1068 if( ruleData.at( 1 ).is_number() )
1069 {
1070 // ["RULE","3","trackWidth",1,["mil",5,10,100]]
1071 double minVal = ruleData.at( 1 );
1072 // double optVal = ruleData.at( 2 );
1073 // double maxVal = ruleData.at( 3 );
1074
1075 bds.m_TrackMinWidth = ScaleSize( minVal );
1076 }
1077 else
1078 {
1079 // ["RULE","3","trackWidth",1,["mil",{"1":[10,10,150]}]]
1080 nlohmann::json table = ruleData.at( 1 );
1081
1082 for( auto& [key, arr] : table.items() )
1083 {
1084 double minVal = arr.at( 0 );
1085 // double optVal = arr.at( 1 );
1086 // double maxVal = arr.at( 2 );
1087
1088 bds.m_TrackMinWidth = ScaleSize( minVal );
1089 }
1090 }
1091 }
1092 else if( ruleType == wxS( "1" ) && isDefault )
1093 {
1094 wxString units = ruleData.at( 0 );
1095 nlohmann::json table = ruleData.at( 1 );
1096 int minVal = INT_MAX;
1097
1098 if( table.is_array() && !table.empty() && table.at( 0 ).is_array() )
1099 {
1100 // ["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]]]]
1101 for( const auto& arr : table )
1102 {
1103 for( int val : arr )
1104 {
1105 if( val != 0 && val < minVal )
1106 minVal = val;
1107 }
1108 }
1109 }
1110 else if( table.is_object() )
1111 {
1112 // ["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],
1113 // [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]]}]]
1114 for( auto& [key, arr] : table.items() )
1115 {
1116 for( const auto& subarr : arr )
1117 {
1118 for( int val : subarr )
1119 {
1120 if( val != 0 && val < minVal )
1121 minVal = val;
1122 }
1123 }
1124 }
1125 }
1126
1127 bds.m_MinClearance = ScaleSize( minVal );
1128 }
1129
1130 ruleLines[ruleType].push_back( line );
1131 }
1132 else if( type == wxS( "POURED" ) )
1133 {
1134 if( !line.at( 2 ).is_string() )
1135 continue; // Unknown type of POURED
1136
1137 EASYEDAPRO::POURED poured = line;
1138 boardPouredMap.emplace( poured.parentId, poured );
1139 }
1140 else if( type == wxS( "VIA" ) || type == wxS( "LINE" ) || type == wxS( "ARC" )
1141 || type == wxS( "POLY" ) || type == wxS( "FILL" ) || type == wxS( "POUR" ) )
1142 {
1143 wxString uuid = line.at( 1 );
1144
1145 // if( line.at( 2 ).is_number() )
1146 // int unk = line.at( 2 ).get<int>();
1147 // else if( line.at( 2 ).is_string() )
1148 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1149
1150 wxString netname = line.at( 3 );
1151
1152 if( type == wxS( "VIA" ) )
1153 {
1155 center.x = line.at( 5 );
1156 center.y = line.at( 6 );
1157
1158 double drill = line.at( 7 );
1159 double dia = line.at( 8 );
1160
1161 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( aBoard );
1162
1163 via->SetPosition( ScalePos( center ) );
1164 via->SetDrill( ScaleSize( drill ) );
1165 via->SetWidth( PADSTACK::ALL_LAYERS, ScaleSize( dia ) );
1166
1167 via->SetNet( getNet( aBoard, netname ) );
1168
1169 aBoard->Add( via.release(), ADD_MODE::APPEND );
1170 }
1171 else if( type == wxS( "LINE" ) )
1172 {
1173 int layer = line.at( 4 ).get<int>();
1174 PCB_LAYER_ID klayer = LayerToKi( layer );
1175
1176 VECTOR2D start;
1177 start.x = line.at( 5 );
1178 start.y = line.at( 6 );
1179
1180 VECTOR2D end;
1181 end.x = line.at( 7 );
1182 end.y = line.at( 8 );
1183
1184 double width = line.at( 9 );
1185
1186 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( aBoard );
1187
1188 track->SetLayer( klayer );
1189 track->SetStart( ScalePos( start ) );
1190 track->SetEnd( ScalePos( end ) );
1191 track->SetWidth( ScaleSize( width ) );
1192
1193 track->SetNet( getNet( aBoard, netname ) );
1194
1195 aBoard->Add( track.release(), ADD_MODE::APPEND );
1196 }
1197 else if( type == wxS( "ARC" ) )
1198 {
1199 int layer = line.at( 4 ).get<int>();
1200 PCB_LAYER_ID klayer = LayerToKi( layer );
1201
1202 VECTOR2D start;
1203 start.x = line.at( 5 );
1204 start.y = line.at( 6 );
1205
1206 VECTOR2D end;
1207 end.x = line.at( 7 );
1208 end.y = line.at( 8 );
1209
1210 double angle = line.at( 9 );
1211 double width = line.at( 10 );
1212
1213 VECTOR2D delta = end - start;
1214 VECTOR2D mid = ( start + delta / 2 );
1215
1216 double ha = angle / 2;
1217 double hd = delta.EuclideanNorm() / 2;
1218 double cdist = hd / tan( DEG2RAD( ha ) );
1219 VECTOR2D center = mid + delta.Perpendicular().Resize( cdist );
1220
1221 SHAPE_ARC sarc;
1223 ScalePos( center ), angle >= 0, width );
1224
1225 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( aBoard, &sarc );
1226 arc->SetWidth( ScaleSize( width ) );
1227
1228 arc->SetLayer( klayer );
1229 arc->SetNet( getNet( aBoard, netname ) );
1230
1231 aBoard->Add( arc.release(), ADD_MODE::APPEND );
1232 }
1233 else if( type == wxS( "FILL" ) )
1234 {
1235 int layer = line.at( 4 ).get<int>();
1236 PCB_LAYER_ID klayer = LayerToKi( layer );
1237
1238 nlohmann::json polyDataList = line.at( 7 );
1239
1240 if( !polyDataList.at( 0 ).is_array() )
1241 polyDataList = nlohmann::json::array( { polyDataList } );
1242
1243 std::vector<SHAPE_LINE_CHAIN> contours;
1244 for( nlohmann::json& polyData : polyDataList )
1245 {
1246 SHAPE_LINE_CHAIN contour = ParseContour( polyData, true );
1247 contour.SetClosed( true );
1248
1249 contours.push_back( contour );
1250 }
1251
1252 SHAPE_POLY_SET zoneFillPoly;
1253
1254 for( SHAPE_LINE_CHAIN& contour : contours )
1255 zoneFillPoly.AddOutline( contour );
1256
1257 zoneFillPoly.RebuildHolesFromContours();
1258 zoneFillPoly.Fracture();
1259
1260 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1261
1262 zone->SetNet( getNet( aBoard, netname ) );
1263 zone->SetLayer( klayer );
1264 zone->Outline()->Append( SHAPE_RECT( zoneFillPoly.BBox() ).Outline() );
1265 zone->SetFilledPolysList( klayer, zoneFillPoly );
1266 zone->SetAssignedPriority( 500 );
1267 zone->SetIsFilled( true );
1268 zone->SetNeedRefill( false );
1269
1270 zone->SetLocalClearance( bds.m_MinClearance );
1271 zone->SetMinThickness( bds.m_TrackMinWidth );
1272
1273 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1274 }
1275 else if( type == wxS( "POLY" ) )
1276 {
1277 int layer = line.at( 4 );
1278 PCB_LAYER_ID klayer = LayerToKi( layer );
1279
1280 double thickness = line.at( 5 );
1281 nlohmann::json polyData = line.at( 6 );
1282
1283 std::vector<std::unique_ptr<PCB_SHAPE>> results =
1284 ParsePoly( aBoard, polyData, false, false );
1285
1286 for( auto& shape : results )
1287 {
1288 shape->SetLayer( klayer );
1289 shape->SetWidth( ScaleSize( thickness ) );
1290
1291 aBoard->Add( shape.release(), ADD_MODE::APPEND );
1292 }
1293 }
1294 else if( type == wxS( "POUR" ) )
1295 {
1296 int layer = line.at( 4 ).get<int>();
1297 PCB_LAYER_ID klayer = LayerToKi( layer );
1298
1299 wxString pourname = line.at( 6 );
1300 int fillOrder = line.at( 7 ).get<int>();
1301 nlohmann::json polyDataList = line.at( 8 );
1302
1303 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1304
1305 zone->SetNet( getNet( aBoard, netname ) );
1306 zone->SetLayer( klayer );
1307 zone->SetAssignedPriority( 500 - fillOrder );
1308 zone->SetLocalClearance( bds.m_MinClearance );
1309 zone->SetMinThickness( bds.m_TrackMinWidth );
1310
1311 for( nlohmann::json& polyData : polyDataList )
1312 {
1313 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1314 contour.SetClosed( true );
1315
1316 zone->Outline()->Append( contour );
1317 }
1318
1319 wxASSERT( zone->Outline()->OutlineCount() == 1 );
1320
1321 poursToFill.emplace( uuid, zone.get() );
1322
1323 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1324 }
1325 }
1326 else if( type == wxS( "TEARDROP" ) )
1327 {
1328 wxString uuid = line.at( 1 );
1329 wxString netname = line.at( 2 );
1330 int layer = line.at( 3 ).get<int>();
1331 PCB_LAYER_ID klayer = LayerToKi( layer );
1332
1333 nlohmann::json polyData = line.at( 4 );
1334
1335 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1336 contour.SetClosed( true );
1337
1338 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1339
1340 zone->SetNet( getNet( aBoard, netname ) );
1341 zone->SetLayer( klayer );
1342 zone->Outline()->Append( contour );
1343 zone->SetFilledPolysList( klayer, contour );
1344 zone->SetNeedRefill( false );
1345 zone->SetIsFilled( true );
1346
1347 zone->SetAssignedPriority( 600 );
1348 zone->SetLocalClearance( 0 );
1349 zone->SetMinThickness( 0 );
1350 zone->SetTeardropAreaType( TEARDROP_TYPE::TD_UNSPECIFIED );
1351
1352 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1353 }
1354 else if( type == wxS( "REGION" ) )
1355 {
1356 wxString uuid = line.at( 1 );
1357
1358 // if( line.at( 2 ).is_number() )
1359 // int unk = line.at( 2 ).get<int>();
1360 // else if( line.at( 2 ).is_string() )
1361 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1362
1363 int layer = line.at( 3 ).get<int>();
1364 PCB_LAYER_ID klayer = LayerToKi( layer );
1365
1366 std::set<int> flags = line.at( 5 );
1367 nlohmann::json polyDataList = line.at( 6 );
1368
1369 for( nlohmann::json& polyData : polyDataList )
1370 {
1371 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1372 contour.SetClosed( true );
1373
1374 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1375
1376 zone->SetIsRuleArea( true );
1377 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
1378 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
1379 || !!flags.count( 8 ) );
1380 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
1381 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
1382 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
1383
1384 zone->SetLayer( klayer );
1385 zone->Outline()->Append( contour );
1386
1387 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1388 }
1389 }
1390 else if( type == wxS( "PAD" ) )
1391 {
1392 wxString netname = line.at( 3 );
1393
1394 std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( aBoard );
1395 std::unique_ptr<PAD> pad = createPAD( footprint.get(), line );
1396
1397 pad->SetNet( getNet( aBoard, netname ) );
1398
1399 VECTOR2I pos = pad->GetPosition();
1400 EDA_ANGLE orient = pad->GetOrientation();
1401
1402 pad->SetPosition( VECTOR2I() );
1403 pad->SetOrientation( ANGLE_0 );
1404
1405 footprint->Add( pad.release(), ADD_MODE::APPEND );
1406 footprint->SetPosition( pos );
1407 footprint->SetOrientation( orient );
1408
1409 wxString fpName = wxS( "Pad_" ) + line.at( 1 ).get<wxString>();
1410 LIB_ID fpID = EASYEDAPRO::ToKiCadLibID( wxEmptyString, fpName );
1411
1412 footprint->SetFPID( fpID );
1413 footprint->Reference().SetVisible( true );
1414 footprint->Value().SetVisible( true );
1415 footprint->AutoPositionFields();
1416
1417 aBoard->Add( footprint.release(), ADD_MODE::APPEND );
1418 }
1419 else if( type == wxS( "IMAGE" ) )
1420 {
1421 wxString uuid = line.at( 1 );
1422
1423 // if( line.at( 2 ).is_number() )
1424 // int unk = line.at( 2 ).get<int>();
1425 // else if( line.at( 2 ).is_string() )
1426 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1427
1428 int layer = line.at( 3 ).get<int>();
1429 PCB_LAYER_ID klayer = LayerToKi( layer );
1430
1431 VECTOR2D start( line.at( 4 ), line.at( 5 ) );
1432 VECTOR2D size( line.at( 6 ), line.at( 7 ) );
1433
1434 double angle = line.at( 8 ); // from top left corner
1435 int mirror = line.at( 9 );
1436 nlohmann::json polyDataList = line.at( 10 );
1437
1438 BOX2I bbox;
1439 std::vector<SHAPE_LINE_CHAIN> contours;
1440 for( nlohmann::json& polyData : polyDataList )
1441 {
1442 SHAPE_LINE_CHAIN contour = ParseContour( polyData, false );
1443 contour.SetClosed( true );
1444
1445 contours.push_back( contour );
1446
1447 bbox.Merge( contour.BBox() );
1448 }
1449
1450 VECTOR2D scale( ScaleSize( size.x ) / bbox.GetSize().x,
1451 ScaleSize( size.y ) / bbox.GetSize().y );
1452
1453 SHAPE_POLY_SET polySet;
1454
1455 for( SHAPE_LINE_CHAIN& contour : contours )
1456 {
1457 for( int i = 0; i < contour.PointCount(); i++ )
1458 {
1459 VECTOR2I pt = contour.CPoint( i );
1460 contour.SetPoint( i, VECTOR2I( pt.x * scale.x, pt.y * scale.y ) );
1461 }
1462
1463 polySet.AddOutline( contour );
1464 }
1465
1466 polySet.RebuildHolesFromContours();
1467
1468 std::unique_ptr<PCB_GROUP> group;
1469
1470 if( polySet.OutlineCount() > 1 )
1471 group = std::make_unique<PCB_GROUP>( aBoard );
1472
1473 BOX2I polyBBox = polySet.BBox();
1474
1475 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
1476 {
1477 std::unique_ptr<PCB_SHAPE> shape =
1478 std::make_unique<PCB_SHAPE>( aBoard, SHAPE_T::POLY );
1479
1480 shape->SetFilled( true );
1481 shape->SetPolyShape( poly );
1482 shape->SetLayer( klayer );
1483 shape->SetWidth( 0 );
1484
1485 shape->Move( ScalePos( start ) - polyBBox.GetOrigin() );
1486 shape->Rotate( ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
1487
1488 if( IsBackLayer( klayer ) ^ !!mirror )
1489 {
1490 FLIP_DIRECTION flipDirection = IsBackLayer( klayer )
1493 shape->Mirror( ScalePos( start ), flipDirection );
1494 }
1495
1496 if( group )
1497 group->AddItem( shape.get() );
1498
1499 aBoard->Add( shape.release(), ADD_MODE::APPEND );
1500 }
1501
1502 if( group )
1503 aBoard->Add( group.release(), ADD_MODE::APPEND );
1504 }
1505 else if( type == wxS( "OBJ" ) )
1506 {
1507 VECTOR2D start, size;
1508 wxString mimeType, base64Data;
1509 double angle = 0;
1510 int flipped = 0;
1511
1512 if( !line.at( 3 ).is_number() )
1513 continue;
1514
1515 int layer = line.at( 3 ).get<int>();
1516 PCB_LAYER_ID klayer = LayerToKi( layer );
1517
1518 start = VECTOR2D( line.at( 5 ), line.at( 6 ) );
1519 size = VECTOR2D( line.at( 7 ), line.at( 8 ) );
1520 angle = line.at( 9 );
1521 flipped = line.at( 10 );
1522
1523 wxString imageUrl = line.at( 11 );
1524
1525 if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
1526 {
1527 wxString objectId = imageUrl.AfterLast( ':' );
1528
1529 if( auto blob = get_opt( aBlobMap, objectId ) )
1530 {
1531 wxString blobUrl = blob->url;
1532
1533 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1534 {
1535 wxArrayString paramsArr =
1536 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1537
1538 base64Data = blobUrl.AfterFirst( ',' );
1539
1540 if( paramsArr.size() > 0 )
1541 mimeType = paramsArr[0];
1542 }
1543 }
1544 }
1545
1546 VECTOR2D kstart = ScalePos( start );
1547 VECTOR2D ksize = ScaleSize( size );
1548
1549 if( mimeType.empty() || base64Data.empty() )
1550 continue;
1551
1552 wxMemoryBuffer buf = wxBase64Decode( base64Data );
1553
1554 if( mimeType == wxS( "image/svg+xml" ) )
1555 {
1556 // Not yet supported by EasyEDA
1557 }
1558 else
1559 {
1560 VECTOR2D kcenter = kstart + ksize / 2;
1561
1562 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap =
1563 std::make_unique<PCB_REFERENCE_IMAGE>( aBoard, kcenter, klayer );
1564 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1565
1566 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1567 & ~wxImage::Load_Verbose );
1568
1569 if( refImage.ReadImageFile( buf ) )
1570 {
1571 double scaleFactor = ScaleSize( size.x ) / refImage.GetSize().x;
1572 refImage.SetImageScale( scaleFactor );
1573
1574 // TODO: support non-90-deg angles
1575 bitmap->Rotate( kstart, EDA_ANGLE( angle, DEGREES_T ) );
1576
1577 if( flipped )
1578 {
1579 int x = bitmap->GetPosition().x;
1580 MIRROR( x, KiROUND( kstart.x ) );
1581 bitmap->SetX( x );
1582
1584 }
1585
1586 aBoard->Add( bitmap.release(), ADD_MODE::APPEND );
1587 }
1588 }
1589 }
1590 else if( type == wxS( "STRING" ) )
1591 {
1592 wxString uuid = line.at( 1 );
1593
1594 // if( line.at( 2 ).is_number() )
1595 // int unk = line.at( 2 ).get<int>();
1596 // else if( line.at( 2 ).is_string() )
1597 // int unk = wxAtoi( line.at( 2 ).get<wxString>() );
1598
1599 int layer = line.at( 3 ).get<int>();
1600 PCB_LAYER_ID klayer = LayerToKi( layer );
1601
1602 VECTOR2D location( line.at( 4 ), line.at( 5 ) );
1603 wxString string = line.at( 6 );
1604 wxString font = line.at( 7 );
1605
1606 double height = line.at( 8 );
1607 double strokew = line.at( 9 );
1608
1609 int align = line.at( 12 );
1610 double angle = line.at( 13 );
1611 int inverted = line.at( 14 );
1612 int mirror = line.at( 16 );
1613
1614 PCB_TEXT* text = new PCB_TEXT( aBoard );
1615
1616 text->SetText( string );
1617 text->SetLayer( klayer );
1618 text->SetPosition( ScalePos( location ) );
1619 text->SetIsKnockout( inverted );
1620 text->SetTextThickness( ScaleSize( strokew ) );
1621 text->SetTextSize( VECTOR2D( ScaleSize( height * 0.6 ), ScaleSize( height * 0.7 ) ) );
1622
1623 if( font != wxS( "default" ) )
1624 {
1625 text->SetFont( KIFONT::FONT::GetFont( font ) );
1626 //text->SetupRenderCache( text->GetShownText(), text->GetFont(), EDA_ANGLE( angle, DEGREES_T ) );
1627
1628 //text->AddRenderCacheGlyph();
1629 // TODO: import geometry cache
1630 }
1631
1632 AlignText( text, align );
1633
1634 if( IsBackLayer( klayer ) ^ !!mirror )
1635 {
1636 text->SetMirrored( true );
1637 text->SetTextAngleDegrees( -angle );
1638 }
1639 else
1640 {
1641 text->SetTextAngleDegrees( angle );
1642 }
1643
1644 aBoard->Add( text, ADD_MODE::APPEND );
1645 }
1646 else if( type == wxS( "COMPONENT" ) )
1647 {
1648 wxString compId = line.at( 1 );
1649 componentLines[compId].push_back( line );
1650 }
1651 else if( type == wxS( "ATTR" ) )
1652 {
1653 wxString compId = line.at( 3 );
1654 componentLines[compId].push_back( line );
1655 }
1656 else if( type == wxS( "PAD_NET" ) )
1657 {
1658 wxString compId = line.at( 1 );
1659 componentLines[compId].push_back( line );
1660 }
1661 }
1662
1663 for( auto const& [compId, lines] : componentLines )
1664 {
1665 wxString deviceId;
1666 wxString fpIdOverride;
1667 wxString fpDesignator;
1668 std::map<wxString, wxString> localCompAttribs;
1669
1670 for( auto& line : lines )
1671 {
1672 if( line.size() == 0 )
1673 continue;
1674
1675 wxString type = line.at( 0 );
1676
1677 if( type == wxS( "COMPONENT" ) )
1678 {
1679 localCompAttribs = line.at( 7 );
1680 }
1681 else if( type == wxS( "ATTR" ) )
1682 {
1683 EASYEDAPRO::PCB_ATTR attr = line;
1684
1685 if( attr.key == wxS( "Device" ) )
1686 deviceId = attr.value;
1687
1688 else if( attr.key == wxS( "Footprint" ) )
1689 fpIdOverride = attr.value;
1690
1691 else if( attr.key == wxS( "Designator" ) )
1692 fpDesignator = attr.value;
1693 }
1694 }
1695
1696 if( deviceId.empty() )
1697 continue;
1698
1699 nlohmann::json compAttrs = aProject.at( "devices" ).at( deviceId ).at( "attributes" );
1700
1701 wxString fpId;
1702
1703 if( !fpIdOverride.IsEmpty() )
1704 fpId = fpIdOverride;
1705 else
1706 fpId = compAttrs.at( "Footprint" ).get<wxString>();
1707
1708 auto it = aFootprintMap.find( fpId );
1709 if( it == aFootprintMap.end() )
1710 {
1711 wxLogTrace( traceEasyEdaIo, "Footprint of '%s' with uuid '%s' not found.", fpDesignator, fpId );
1712 continue;
1713 }
1714
1715 std::unique_ptr<FOOTPRINT>& footprintOrig = it->second;
1716 std::unique_ptr<FOOTPRINT> footprint( static_cast<FOOTPRINT*>( footprintOrig->Clone() ) );
1717
1718 wxString modelUuid, modelTitle, modelTransform;
1719
1720 if( auto val = get_opt( localCompAttribs, "3D Model" ) )
1721 modelUuid = *val;
1722 else
1723 modelUuid = compAttrs.value<wxString>( "3D Model", "" );
1724
1725 if( auto val = get_opt( localCompAttribs, "3D Model Title" ) )
1726 modelTitle = val->Trim();
1727 else
1728 modelTitle = compAttrs.value<wxString>( "3D Model Title", modelUuid ).Trim();
1729
1730 if( auto val = get_opt( localCompAttribs, "3D Model Transform" ) )
1731 modelTransform = *val;
1732 else
1733 modelTransform = compAttrs.value<wxString>( "3D Model Transform", "" );
1734
1735 FillFootprintModelInfo( footprint.get(), modelUuid, modelTitle, modelTransform );
1736
1737 footprint->SetParent( aBoard );
1738
1739 for( auto& line : lines )
1740 {
1741 if( line.size() == 0 )
1742 continue;
1743
1744 wxString type = line.at( 0 );
1745
1746 if( type == wxS( "COMPONENT" ) )
1747 {
1748 int layer = line.at( 3 );
1749 PCB_LAYER_ID klayer = LayerToKi( layer );
1750
1751 VECTOR2D center( line.at( 4 ), line.at( 5 ) );
1752
1753 double orient = line.at( 6 );
1754 //std::map<wxString, wxString> props = line.at( 7 );
1755
1756 if( klayer == B_Cu )
1757 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1758
1759 footprint->SetOrientationDegrees( orient );
1760 footprint->SetPosition( ScalePos( center ) );
1761 }
1762 else if( type == wxS( "ATTR" ) )
1763 {
1764 EASYEDAPRO::PCB_ATTR attr = line;
1765
1766 PCB_LAYER_ID klayer = LayerToKi( attr.layer );
1767
1768 PCB_FIELD* field = nullptr;
1769
1770 if( attr.key == wxS( "Designator" ) )
1771 {
1772 if( attr.key == wxS( "Designator" ) )
1773 {
1774 field = footprint->GetField( FIELD_T::REFERENCE );
1775 }
1776 else
1777 {
1778 field = new PCB_FIELD( footprint.get(), FIELD_T::USER, attr.key );
1779 footprint->Add( field, ADD_MODE::APPEND );
1780 }
1781
1782 if( attr.fontName != wxS( "default" ) )
1783 field->SetFont( KIFONT::FONT::GetFont( attr.fontName ) );
1784
1785 if( attr.valVisible && attr.keyVisible )
1786 {
1787 field->SetText( attr.key + ':' + attr.value );
1788 }
1789 else if( attr.keyVisible )
1790 {
1791 field->SetText( attr.key );
1792 }
1793 else
1794 {
1795 field->SetText( attr.value );
1796 }
1797
1798 field->SetVisible( attr.keyVisible || attr.valVisible );
1799 field->SetLayer( klayer );
1800 field->SetPosition( ScalePos( attr.position ) );
1801 field->SetTextAngleDegrees( footprint->IsFlipped() ? -attr.rotation
1802 : attr.rotation );
1803 field->SetIsKnockout( attr.inverted );
1804 field->SetTextThickness( ScaleSize( attr.strokeWidth ) );
1805 field->SetTextSize( VECTOR2D( ScaleSize( attr.height * 0.55 ),
1806 ScaleSize( attr.height * 0.6 ) ) );
1807
1808 AlignText( field, attr.textOrigin );
1809 }
1810 }
1811 else if( type == wxS( "PAD_NET" ) )
1812 {
1813 wxString padNumber = line.at( 2 );
1814 wxString padNet = line.at( 3 );
1815
1816 PAD* pad = footprint->FindPadByNumber( padNumber );
1817 if( pad )
1818 {
1819 pad->SetNet( getNet( aBoard, padNet ) );
1820 }
1821 else
1822 {
1823 // Not a pad
1824 }
1825 }
1826 }
1827
1828 aBoard->Add( footprint.release(), ADD_MODE::APPEND );
1829 }
1830
1831 // Set zone fills
1833 {
1834 for( auto& [uuid, zone] : poursToFill )
1835 {
1836 SHAPE_POLY_SET fillPolySet;
1837 SHAPE_POLY_SET thermalSpokes;
1838
1839 auto range = boardPouredMap.equal_range( uuid );
1840 for( auto& it = range.first; it != range.second; ++it )
1841 {
1842 const EASYEDAPRO::POURED& poured = it->second;
1843
1844 SHAPE_POLY_SET thisPoly;
1845
1846 for( int dataId = 0; dataId < (int) poured.polyData.size(); dataId++ )
1847 {
1848 const nlohmann::json& fillData = poured.polyData[dataId];
1849 const double ptScale = 10;
1850
1851 SHAPE_LINE_CHAIN contour = ParseContour( fillData, false, ARC_HIGH_DEF / ptScale );
1852
1853 // Scale the fill
1854 for( int i = 0; i < contour.PointCount(); i++ )
1855 contour.SetPoint( i, contour.GetPoint( i ) * ptScale );
1856
1857 if( poured.isPoly )
1858 {
1859 contour.SetClosed( true );
1860
1861 // The contour can be self-intersecting
1862 SHAPE_POLY_SET simple( contour );
1863 simple.Simplify();
1864
1865 if( dataId == 0 )
1866 {
1867 thisPoly.Append( simple );
1868 }
1869 else
1870 {
1871 thisPoly.BooleanSubtract( simple );
1872 }
1873 }
1874 else
1875 {
1876 const int thermalWidth = pcbIUScale.mmToIU( 0.2 ); // Generic
1877
1878 for( int segId = 0; segId < contour.SegmentCount(); segId++ )
1879 {
1880 const SEG& seg = contour.CSegment( segId );
1881
1882 TransformOvalToPolygon( thermalSpokes, seg.A, seg.B, thermalWidth,
1884 }
1885 }
1886 }
1887
1888 fillPolySet.Append( thisPoly );
1889 }
1890
1891 if( !fillPolySet.IsEmpty() )
1892 {
1893 fillPolySet.Simplify();
1894
1895 const int strokeWidth = pcbIUScale.MilsToIU( 8 ); // Seems to be 8 mils
1896
1897 fillPolySet.Inflate( strokeWidth / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
1898 ARC_HIGH_DEF, false );
1899
1900 fillPolySet.BooleanAdd( thermalSpokes );
1901
1902 fillPolySet.Fracture();
1903
1904 zone->SetFilledPolysList( zone->GetFirstLayer(), fillPolySet );
1905 zone->SetNeedRefill( false );
1906 zone->SetIsFilled( true );
1907 }
1908 }
1909 }
1910
1911 // Heal board outlines
1912 std::vector<PCB_SHAPE*> shapes;
1913
1914 for( BOARD_ITEM* item : aBoard->Drawings() )
1915 {
1916 if( !item->IsOnLayer( Edge_Cuts ) )
1917 continue;
1918
1919 if( item->Type() == PCB_SHAPE_T )
1920 shapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1921 }
1922
1924
1925 // Center the board
1926 BOX2I outlineBbox = aBoard->ComputeBoundingBox( true, true );
1927 PAGE_INFO pageInfo = aBoard->GetPageSettings();
1928
1929 VECTOR2D pageCenter( pcbIUScale.MilsToIU( pageInfo.GetWidthMils() / 2 ),
1930 pcbIUScale.MilsToIU( pageInfo.GetHeightMils() / 2 ) );
1931
1932 VECTOR2D offset = pageCenter - outlineBbox.GetCenter();
1933
1934 int alignGrid = pcbIUScale.mmToIU( 10 );
1935 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1936 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1937
1938 aBoard->Move( offset );
1939 bds.SetAuxOrigin( offset );
1940}
@ 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:83
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:383
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:343
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1355
const PAGE_INFO & GetPageSettings() const
Definition board.h:901
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2777
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition board.cpp:820
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition board.cpp:686
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1158
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1043
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2510
unsigned GetNetCount() const
Definition board.h:1127
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1063
const DRAWINGS & Drawings() const
Definition board.h:423
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:395
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 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)
FOOTPRINT * ParseFootprint(const nlohmann::json &aProject, const wxString &aFpUuid, const std::vector< nlohmann::json > &aLines)
static double Convert(wxString aValue)
void SetTextThickness(int aWidth) override
The TextThickness is that set by the user.
Definition pcb_text.cpp:496
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true) override
Definition pcb_text.cpp:468
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)...
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:809
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
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: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