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