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