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