KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_io_easyedapro_v3_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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
26
27#include <memory>
28
29#include <json_common.h>
31#include <core/map_helpers.h>
32#include <string_utils.h>
33
34#include <wx/log.h>
35#include <wx/base64.h>
36#include <wx/mstream.h>
37
38#include <footprint.h>
39#include <board.h>
40#include <pcb_group.h>
41#include <pcb_shape.h>
42#include <pcb_text.h>
43#include <pcb_track.h>
44#include <pcb_reference_image.h>
45#include <geometry/shape_arc.h>
46#include <geometry/shape_rect.h>
47#include <zone.h>
48#include <pad.h>
49#include <fix_board_shape.h>
51#include <font/font.h>
52#include <core/mirror.h>
54#include <trace_helpers.h>
55
56using namespace EASYEDAPRO;
57
58static const int SHAPE_JOIN_DISTANCE = pcbIUScale.mmToIU( 1.5 );
59
60
62 PROGRESS_REPORTER* aProgressReporter ) :
63 m_board( aBoard ),
64 m_v2Parser( aBoard, aProgressReporter )
65{
66}
67
68
69static void V3AlignText( EDA_TEXT* text, int align );
70static int V3AlignToOriginCode( const wxString& aAlign );
71
72
73std::unique_ptr<PAD> PCB_IO_EASYEDAPRO_V3_PARSER::createV3PAD( FOOTPRINT* aFootprint,
74 const V3_ROW& aRow )
75{
76 int layer = V3GetInt( aRow.inner, "layerId", 1 );
77 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
78
79 wxString padNumber = V3GetString( aRow.inner, "num" );
80
82 center.x = V3GetDouble( aRow.inner, "centerX" );
83 center.y = V3GetDouble( aRow.inner, "centerY" );
84
85 double orientation = V3GetDouble( aRow.inner, "padAngle" );
86
87 nlohmann::json holeObj = aRow.inner.value( "hole", nlohmann::json() );
88 nlohmann::json padDef = aRow.inner.value( "defaultPad", nlohmann::json() );
89
90 std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
91
92 pad->SetNumber( padNumber );
94 pad->SetOrientationDegrees( orientation );
95 pad->SetPadstackMode( PADSTACK::MODE::NORMAL );
96
97 // Process hole
98 bool hasHole = false;
99
100 if( holeObj.is_object() )
101 {
102 wxString holeType = V3GetString( holeObj, "holeType", wxS( "ROUND" ) );
103
104 if( holeType == wxS( "RECT" ) )
105 holeType = wxS( "SLOT" );
106
107 if( holeType != wxS( "ROUND" ) && holeType != wxS( "SLOT" ) )
108 holeType = wxS( "ROUND" );
109
110 VECTOR2D drill;
111 drill.x = V3GetDouble( holeObj, "width" );
112 drill.y = V3GetDouble( holeObj, "height" );
113
114 if( drill.x > 0 || drill.y > 0 )
115 {
116 hasHole = true;
117
118 double drill_dir = V3GetDouble( aRow.inner, "relativeAngle" );
119
120 double deg = EDA_ANGLE( drill_dir, DEGREES_T ).Normalize90().AsDegrees();
121
122 if( std::abs( deg ) >= 45 )
123 std::swap( drill.x, drill.y );
124
125 if( holeType == wxS( "SLOT" ) )
126 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
127
128 pad->SetDrillSize( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( drill ) );
129 pad->SetLayerSet( PAD::PTHMask() );
130 pad->SetAttribute( PAD_ATTRIB::PTH );
131 }
132 }
133
134 if( !hasHole )
135 {
136 if( klayer == F_Cu )
137 pad->SetLayerSet( PAD::SMDMask() );
138 else if( klayer == B_Cu )
139 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
140
141 pad->SetAttribute( PAD_ATTRIB::SMD );
142 }
143
144 // Process pad shape
145 if( padDef.is_object() )
146 {
147 wxString padType = V3GetString( padDef, "padType", wxS( "RECT" ) );
148
149 if( padType == wxS( "RECT" ) )
150 {
151 VECTOR2D size;
152 size.x = V3GetDouble( padDef, "width", 1.0 );
153 size.y = V3GetDouble( padDef, "height", 1.0 );
154 double radius = V3GetDouble( padDef, "radius" );
155 double radiusRatio = std::clamp( radius, 0.0, 100.0 ) / 100 / 2;
156
158
159 if( radiusRatio == 0 )
161 else
162 {
164 pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, radiusRatio );
165 }
166 }
167 else if( padType == wxS( "ELLIPSE" ) )
168 {
169 VECTOR2D size;
170 size.x = V3GetDouble( padDef, "width", 1.0 );
171 size.y = V3GetDouble( padDef, "height", 1.0 );
172
174 pad->SetShape( PADSTACK::ALL_LAYERS, size.x == size.y ? PAD_SHAPE::CIRCLE
175 : PAD_SHAPE::OVAL );
176 }
177 else if( padType == wxS( "OVAL" ) )
178 {
179 VECTOR2D size;
180 size.x = V3GetDouble( padDef, "width", 1.0 );
181 size.y = V3GetDouble( padDef, "height", 1.0 );
182
185 }
186 else if( padType == wxS( "POLY" ) || padType == wxS( "POLYGON" ) )
187 {
189 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
190 pad->SetSize( PADSTACK::ALL_LAYERS, { 1, 1 } );
191
192 nlohmann::json polyData = padDef.value( "path", nlohmann::json() );
193
194 std::vector<std::unique_ptr<PCB_SHAPE>> results =
195 m_v2Parser.ParsePoly( aFootprint, polyData, true, false );
196
197 for( auto& shape : results )
198 {
199 shape->SetLayer( klayer );
200 shape->SetWidth( 0 );
201 shape->Move( -pad->GetPosition() );
202 pad->AddPrimitive( PADSTACK::ALL_LAYERS, shape.release() );
203 }
204 }
205 }
206 else
207 {
210 }
211
212 pad->SetThermalSpokeAngle( ANGLE_90 );
213
214 return pad;
215}
216
217
219 const EASYEDAPRO::V3_ROW& aRow )
220{
221 int layer = V3GetInt( aRow.inner, "layerId", 3 );
222 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
223
225 location.x = V3GetDouble( aRow.inner, "x" );
226 location.y = V3GetDouble( aRow.inner, "y" );
227
228 wxString string = V3GetString( aRow.inner, "text" );
229 wxString font = V3GetString( aRow.inner, "fontFamily", wxS( "default" ) );
230
231 double height = V3GetDouble( aRow.inner, "fontSize", 45.0 );
232 double strokew = V3GetDouble( aRow.inner, "strokeWidth", 6.0 );
233
234 wxString originStr = V3GetString( aRow.inner, "origin", wxS( "LEFT_BOTTOM" ) );
235 int align = V3AlignToOriginCode( originStr );
236 double angle = V3GetDouble( aRow.inner, "angle" );
237 int inverted = V3GetBool( aRow.inner, "reverse" ) ? 1 : 0;
238 int mirror = V3GetBool( aRow.inner, "mirror" ) ? 1 : 0;
239
240 PCB_TEXT* text = new PCB_TEXT( aContainer );
241
242 text->SetText( string );
243 text->SetLayer( klayer );
245 text->SetIsKnockout( inverted );
246 text->SetTextThickness( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( strokew ) );
247 text->SetTextSize( VECTOR2D( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( height * 0.6 ),
248 PCB_IO_EASYEDAPRO_PARSER::ScaleSize( height * 0.7 ) ) );
249
250 if( font != wxS( "default" ) )
251 text->SetFont( KIFONT::FONT::GetFont( font ) );
252
253 V3AlignText( text, align );
254
255 if( IsBackLayer( klayer ) ^ !!mirror )
256 {
257 text->SetMirrored( true );
258 text->SetTextAngleDegrees( -angle );
259 }
260 else
261 {
262 text->SetTextAngleDegrees( angle );
263 }
264
265 return text;
266}
267
268
269std::unique_ptr<FOOTPRINT>
270PCB_IO_EASYEDAPRO_V3_PARSER::ParseFootprint( const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
271 const V3_DOC_RAW& aDoc )
272{
273 std::unique_ptr<FOOTPRINT> footprintPtr = std::make_unique<FOOTPRINT>( m_board );
274 FOOTPRINT* footprint = footprintPtr.get();
275
276 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
277 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
278
279 for( PCB_FIELD* field : footprint->GetFields() )
280 {
281 field->SetTextSize( defaultTextSize );
282 field->SetTextThickness( defaultTextThickness );
283 }
284
285 for( const V3_ROW& row : aDoc.rows )
286 {
287 if( row.type == wxS( "POLY" ) )
288 {
289 int layer = V3GetInt( row.inner, "layerId", 1 );
290 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
291 double thickness = V3GetDouble( row.inner, "width" );
292
293 nlohmann::json polyData = row.inner.value( "path", nlohmann::json::array() );
294
295 std::vector<std::unique_ptr<PCB_SHAPE>> results =
296 m_v2Parser.ParsePoly( footprint, polyData, false, false );
297
298 for( auto& shape : results )
299 {
300 shape->SetLayer( klayer );
301 shape->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( thickness ) );
302 footprint->Add( shape.release(), ADD_MODE::APPEND );
303 }
304 }
305 else if( row.type == wxS( "PAD" ) )
306 {
307 std::unique_ptr<PAD> pad = createV3PAD( footprint, row );
308 footprint->Add( pad.release(), ADD_MODE::APPEND );
309 }
310 else if( row.type == wxS( "FILL" ) )
311 {
312 int layer = V3GetInt( row.inner, "layerId", 1 );
313 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
314
315 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
316
317 if( !polyDataList.is_null() && !polyDataList.empty() )
318 {
319 if( !polyDataList.at( 0 ).is_array() )
320 polyDataList = nlohmann::json::array( { polyDataList } );
321
322 std::vector<SHAPE_LINE_CHAIN> contours;
323
324 for( nlohmann::json& polyData : polyDataList )
325 {
326 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
327 contour.SetClosed( true );
328 contours.push_back( contour );
329 }
330
331 SHAPE_POLY_SET polySet;
332
333 for( SHAPE_LINE_CHAIN& contour : contours )
334 polySet.AddOutline( contour );
335
336 polySet.RebuildHolesFromContours();
337
338 std::unique_ptr<PCB_GROUP> group;
339
340 if( polySet.OutlineCount() > 1 )
341 group = std::make_unique<PCB_GROUP>( footprint );
342
343 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
344 {
345 std::unique_ptr<PCB_SHAPE> shape =
346 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::POLY );
347
348 shape->SetFilled( true );
349 shape->SetPolyShape( poly );
350 shape->SetLayer( klayer );
351 shape->SetWidth( 0 );
352
353 if( group )
354 group->AddItem( shape.get() );
355
356 footprint->Add( shape.release(), ADD_MODE::APPEND );
357 }
358
359 if( group )
360 footprint->Add( group.release(), ADD_MODE::APPEND );
361 }
362 }
363 else if( row.type == wxS( "ATTR" ) )
364 {
365 wxString key = V3GetString( row.inner, "key" );
366 wxString value = V3JsonToString( row.inner.value( "value", nlohmann::json() ) );
367
368 if( key == wxS( "Designator" ) )
369 footprint->GetField( FIELD_T::REFERENCE )->SetText( value );
370 }
371 else if( row.type == wxS( "REGION" ) )
372 {
373 int layer = V3GetInt( row.inner, "layerId", 1 );
374 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
375
376 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
377 nlohmann::json prohibitTypes = row.inner.value( "prohibitType", nlohmann::json::array() );
378
379 std::set<int> flags;
380
381 if( prohibitTypes.is_array() )
382 {
383 for( const auto& pt : prohibitTypes )
384 {
385 wxString ptStr;
386
387 if( pt.is_string() )
388 ptStr = pt.get<std::string>();
389
390 if( ptStr == wxS( "COMPONENT" ) )
391 flags.insert( 2 );
392 else if( ptStr == wxS( "TRACK" ) )
393 flags.insert( 5 );
394 else if( ptStr == wxS( "COPPER" ) )
395 flags.insert( 6 );
396 else if( ptStr == wxS( "VIA" ) )
397 flags.insert( 7 );
398 else if( ptStr == wxS( "FILL" ) || ptStr == wxS( "PLANE" ) )
399 flags.insert( 8 );
400 }
401 }
402
403 for( nlohmann::json& polyData : polyDataList )
404 {
405 SHAPE_POLY_SET polySet;
406
407 std::vector<std::unique_ptr<PCB_SHAPE>> results =
408 m_v2Parser.ParsePoly( nullptr, polyData, true, false );
409
410 for( auto& shape : results )
411 {
412 shape->SetFilled( true );
413 shape->TransformShapeToPolygon( polySet, klayer, 0, ARC_HIGH_DEF,
414 ERROR_INSIDE, true );
415 }
416
417 polySet.Simplify();
418
419 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( footprint );
420
421 zone->SetIsRuleArea( true );
422 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
423 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
424 || !!flags.count( 8 ) );
425 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
426 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
427 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
428
429 zone->SetLayer( klayer );
430 zone->Outline()->Append( polySet );
431
432 footprint->Add( zone.release(), ADD_MODE::APPEND );
433 }
434 }
435 else if( row.type == wxS( "IMAGE" ) )
436 {
437 int layer = V3GetInt( row.inner, "layerId", 1 );
438 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
439
440 VECTOR2D start;
441 start.x = V3GetDouble( row.inner, "startX" );
442 start.y = V3GetDouble( row.inner, "startY" );
443
444 VECTOR2D size;
445 size.x = V3GetDouble( row.inner, "width" );
446 size.y = V3GetDouble( row.inner, "height" );
447
448 double angle = V3GetDouble( row.inner, "angle" );
449 bool mirror = V3GetBool( row.inner, "mirror" );
450 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
451
452 BOX2I bbox;
453 std::vector<SHAPE_LINE_CHAIN> contours;
454 for( nlohmann::json& polyData : polyDataList )
455 {
456 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
457 contour.SetClosed( true );
458
459 contours.push_back( contour );
460
461 bbox.Merge( contour.BBox() );
462 }
463
464 if( bbox.GetSize().x == 0 || bbox.GetSize().y == 0 )
465 continue;
466
469
470 SHAPE_POLY_SET polySet;
471
472 for( SHAPE_LINE_CHAIN& contour : contours )
473 {
474 for( int i = 0; i < contour.PointCount(); i++ )
475 {
476 VECTOR2I pt = contour.CPoint( i );
477 contour.SetPoint( i, VECTOR2I( pt.x * scale.x, pt.y * scale.y ) );
478 }
479
480 polySet.AddOutline( contour );
481 }
482
483 polySet.RebuildHolesFromContours();
484
485 std::unique_ptr<PCB_GROUP> group;
486
487 if( polySet.OutlineCount() > 1 )
488 group = std::make_unique<PCB_GROUP>( footprint );
489
490 BOX2I polyBBox = polySet.BBox();
491
492 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
493 {
494 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::POLY );
495
496 shape->SetFilled( true );
497 shape->SetPolyShape( poly );
498 shape->SetLayer( klayer );
499 shape->SetWidth( 0 );
500
501 shape->Move( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ) - polyBBox.GetOrigin() );
502 shape->Rotate( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
503
504 if( IsBackLayer( klayer ) ^ mirror )
505 {
507 shape->Mirror( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), flipDirection );
508 }
509
510 if( group )
511 group->AddItem( shape.get() );
512
513 footprint->Add( shape.release(), ADD_MODE::APPEND );
514 }
515
516 if( group )
517 footprint->Add( group.release(), ADD_MODE::APPEND );
518 }
519 else if( row.type == wxS( "OBJ" ) )
520 {
521 int layer = V3GetInt( row.inner, "layerId", 1 );
522 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
523
524 VECTOR2D start;
525 start.x = V3GetDouble( row.inner, "startX" );
526 start.y = V3GetDouble( row.inner, "startY" );
527
528 VECTOR2D size;
529 size.x = V3GetDouble( row.inner, "width" );
530 size.y = V3GetDouble( row.inner, "height" );
531
532 double angle = V3GetDouble( row.inner, "angle" );
533 bool mirror = V3GetBool( row.inner, "mirror" );
534 wxString imageUrl = V3GetString( row.inner, "path" );
535
536 if( imageUrl.empty() )
537 continue;
538
539 wxString mimeType, base64Data;
540
541 if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
542 {
543 wxString objectId = imageUrl.AfterLast( ':' );
544
545 if( auto blob = get_opt( aBlobMap, objectId ) )
546 {
547 wxString blobUrl = blob->url;
548
549 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
550 {
551 wxArrayString paramsArr =
552 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
553
554 base64Data = blobUrl.AfterFirst( ',' );
555
556 if( paramsArr.size() > 0 )
557 mimeType = paramsArr[0];
558 }
559 }
560 }
561
564
565 if( mimeType.empty() || base64Data.empty() )
566 continue;
567
568 wxMemoryBuffer buf = wxBase64Decode( base64Data );
569
570 if( mimeType == wxS( "image/svg+xml" ) )
571 {
572 // Not yet supported by EasyEDA
573 }
574 else
575 {
576 VECTOR2D kcenter = kstart + ksize / 2;
577
578 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap =
579 std::make_unique<PCB_REFERENCE_IMAGE>( footprint, kcenter, klayer );
580 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
581
582 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
583 & ~wxImage::Load_Verbose );
584
585 if( refImage.ReadImageFile( buf ) )
586 {
587 double scaleFactor = PCB_IO_EASYEDAPRO_PARSER::ScaleSize( size.x ) / refImage.GetSize().x;
588 refImage.SetImageScale( scaleFactor );
589
590 // TODO: support non-90-deg angles
591 bitmap->Rotate( kstart, EDA_ANGLE( angle, DEGREES_T ) );
592
593 if( mirror )
594 {
595 int x = bitmap->GetPosition().x;
596 MIRROR( x, KiROUND( kstart.x ) );
597 bitmap->SetX( x );
598
600 }
601
602 footprint->Add( bitmap.release(), ADD_MODE::APPEND );
603 }
604 }
605 }
606 else if( row.type == wxS( "STRING" ) )
607 {
608 footprint->Add( createV3Text( footprint, row ), ADD_MODE::APPEND );
609 }
610 }
611
612 // 3D models are applied by the caller from the component's Device ATTR (board import)
613 // or a matching library device (FootprintLoad). Do not pick an arbitrary device here:
614 // footprints are often shared by devices with different models.
615
616 // Heal board outlines
617 std::vector<PCB_SHAPE*> edgeShapes;
618
619 for( BOARD_ITEM* item : footprint->GraphicalItems() )
620 {
621 if( item->IsOnLayer( Edge_Cuts ) && item->Type() == PCB_SHAPE_T )
622 edgeShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
623 }
624
626
627 // Build courtyard if missing
628 if( !footprint->IsOnLayer( F_CrtYd ) )
629 {
630 BOX2I bbox = footprint->GetLayerBoundingBox( { F_Cu, F_Fab, F_Paste, F_Mask, Edge_Cuts } );
631 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) );
632
633 std::unique_ptr<PCB_SHAPE> shape =
634 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::RECTANGLE );
635
636 shape->SetWidth( pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ) );
637 shape->SetLayer( F_CrtYd );
638 shape->SetStart( bbox.GetOrigin() );
639 shape->SetEnd( bbox.GetEnd() );
640
641 footprint->Add( shape.release(), ADD_MODE::APPEND );
642 }
643
644 // Add F_Fab reference text if missing
645 bool hasFabRef = false;
646
647 for( BOARD_ITEM* item : footprint->GraphicalItems() )
648 {
649 if( item->Type() == PCB_TEXT_T && item->IsOnLayer( F_Fab ) )
650 {
651 if( static_cast<PCB_TEXT*>( item )->GetText() == wxT( "${REFERENCE}" ) )
652 {
653 hasFabRef = true;
654 break;
655 }
656 }
657 }
658
659 if( !hasFabRef )
660 {
661 int c_refTextSize = pcbIUScale.mmToIU( 0.5 );
662 int c_refTextThickness = pcbIUScale.mmToIU( 0.1 );
663 std::unique_ptr<PCB_TEXT> refText = std::make_unique<PCB_TEXT>( footprint );
664
665 refText->SetLayer( F_Fab );
666 refText->SetTextSize( VECTOR2I( c_refTextSize, c_refTextSize ) );
667 refText->SetTextThickness( c_refTextThickness );
668 refText->SetText( wxT( "${REFERENCE}" ) );
669
670 footprint->Add( refText.release(), ADD_MODE::APPEND );
671 }
672
673 return footprintPtr;
674}
675
676
677static void V3AlignText( EDA_TEXT* text, int align )
678{
679 switch( align )
680 {
681 case 1:
682 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
683 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
684 break;
685 case 2:
686 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
687 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
688 break;
689 case 3:
690 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
691 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
692 break;
693 case 4:
694 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
695 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
696 break;
697 case 5:
698 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
699 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
700 break;
701 case 6:
702 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
703 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
704 break;
705 case 7:
706 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
707 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
708 break;
709 case 8:
710 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
711 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
712 break;
713 case 9:
714 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
715 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
716 break;
717 }
718}
719
720
721static int V3AlignToOriginCode( const wxString& aAlign )
722{
723 if( aAlign == wxS( "LEFT_TOP" ) )
724 return 1;
725 if( aAlign == wxS( "LEFT_CENTER" ) || aAlign == wxS( "LEFT_MIDDLE" ) )
726 return 2;
727 if( aAlign == wxS( "LEFT_BOTTOM" ) )
728 return 3;
729 if( aAlign == wxS( "CENTER_TOP" ) )
730 return 4;
731 if( aAlign == wxS( "CENTER_MIDDLE" ) || aAlign == wxS( "CENTER_CENTER" ) )
732 return 5;
733 if( aAlign == wxS( "CENTER_BOTTOM" ) )
734 return 6;
735 if( aAlign == wxS( "RIGHT_TOP" ) )
736 return 7;
737 if( aAlign == wxS( "RIGHT_CENTER" ) || aAlign == wxS( "RIGHT_MIDDLE" ) )
738 return 8;
739 if( aAlign == wxS( "RIGHT_BOTTOM" ) )
740 return 9;
741
742 return 3; // default: LEFT_BOTTOM
743}
744
745
747 BOARD* aBoard, const nlohmann::json& aProject,
748 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
749 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
750 const std::multimap<wxString, EASYEDAPRO::POURED>& aPouredMap,
751 const V3_DOC_RAW& aDoc, const wxString& aFpLibName )
752{
753 // Structures to collect component-related rows for second-pass placement
754 struct COMP_DATA
755 {
756 int layer = 1;
757 VECTOR2D pos;
758 double angle = 0;
759 std::map<wxString, wxString> attrs;
760 };
761
762 struct ATTR_DATA
763 {
764 wxString parentId;
765 int layer = 3;
766 double x = 0;
767 double y = 0;
768 bool hasPos = false;
769 wxString key;
770 wxString value;
771 bool keyVisible = false;
772 bool valVisible = false;
773 wxString fontName;
774 double fontSize = 45.0;
775 double strokeWidth = 6.0;
776 wxString origin;
777 double angle = 0;
778 int inverted = 0;
779 int mirror = 0;
780 };
781
782 struct PAD_NET_DATA
783 {
784 wxString compId;
785 wxString padNumber;
786 wxString padNet;
787 };
788
789 std::map<wxString, COMP_DATA> componentMap;
790 std::multimap<wxString, ATTR_DATA> attrMap;
791 std::multimap<wxString, PAD_NET_DATA> padNetMap;
792
793 std::multimap<wxString, EASYEDAPRO::POURED> boardPouredMap = aPouredMap;
794 std::map<wxString, ZONE*> poursToFill;
795
797
798 for( const V3_ROW& row : aDoc.rows )
799 {
800 if( row.type == wxS( "LAYER" ) )
801 {
802 int layer = V3GetInt( row.inner, "layerId", 1 );
803 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
804
805 bool use = V3GetBool( row.inner, "use", true );
806 wxString layerName = V3GetString( row.inner, "layerName" );
807
808 if( use )
809 {
810 LSET blayers = aBoard->GetEnabledLayers();
811 blayers.set( klayer );
812 aBoard->SetEnabledLayers( blayers );
813
814 if( !layerName.IsEmpty() )
815 aBoard->SetLayerName( klayer, layerName );
816 }
817 }
818 else if( row.type == wxS( "NET" ) )
819 {
820 nlohmann::json idArr = V3ParseIdArray( row.id );
821 wxString netname;
822
823 if( idArr.is_array() && idArr.size() > 1 )
824 netname = V3JsonToString( idArr[1] );
825
826 if( !netname.IsEmpty() )
827 {
828 aBoard->Add( new NETINFO_ITEM( aBoard, netname,
829 aBoard->GetNetCount() + 1 ),
831 }
832 }
833 else if( row.type == wxS( "RULE" ) )
834 {
835 nlohmann::json idArr = V3ParseIdArray( row.id );
836
837 if( !idArr.is_array() || idArr.size() < 3 )
838 continue;
839
840 wxString ruleType = V3JsonToString( idArr[1] );
841 bool isDefault = V3GetString( row.inner, "ruleState" ) == wxS( "DEFAULT" );
842
843 nlohmann::json context = row.inner.value( "ruleContext", nlohmann::json() );
844
845 if( ruleType == wxS( "TRACK" ) && isDefault )
846 {
847 nlohmann::json track = context.value( "track", nlohmann::json() );
848 nlohmann::json content = track.value( "content", nlohmann::json::array() );
849
850 if( content.is_array() && !content.empty() )
851 {
852 double minVal = V3GetDouble( content[0], "stroMin" );
854 }
855 }
856 else if( ruleType == wxS( "SAFE" ) && isDefault )
857 {
858 nlohmann::json safeSpacing = context.value( "safeSpacing",
859 nlohmann::json::array() );
860
861 if( safeSpacing.is_array() && !safeSpacing.empty() )
862 {
863 nlohmann::json content = safeSpacing[0].value( "content",
864 nlohmann::json::array() );
865
866 int minVal = INT_MAX;
867
868 for( const auto& arr : content )
869 {
870 if( arr.is_array() )
871 {
872 for( const auto& val : arr )
873 {
874 int v = val.is_number() ? val.get<int>() : 0;
875 if( v < minVal )
876 minVal = v;
877 }
878 }
879 }
880
881 if( minVal != INT_MAX )
883 }
884 }
885 }
886 else if( row.type == wxS( "VIA" ) )
887 {
889 center.x = V3GetDouble( row.inner, "centerX" );
890 center.y = V3GetDouble( row.inner, "centerY" );
891
892 double drill = V3GetDouble( row.inner, "holeDiameter" );
893 double dia = V3GetDouble( row.inner, "viaDiameter" );
894 wxString netname = V3GetString( row.inner, "netName" );
895
896 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( aBoard );
897
898 via->SetPadstackMode( PADSTACK::MODE::NORMAL );
900 via->SetDrill( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( drill ) );
902 via->SetNet( aBoard->FindNet( netname ) );
903
904 aBoard->Add( via.release(), ADD_MODE::APPEND );
905 }
906 else if( row.type == wxS( "LINE" ) )
907 {
908 int layer = V3GetInt( row.inner, "layerId", 1 );
909 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
910
911 VECTOR2D start;
912 start.x = V3GetDouble( row.inner, "startX" );
913 start.y = V3GetDouble( row.inner, "startY" );
914
916 end.x = V3GetDouble( row.inner, "endX" );
917 end.y = V3GetDouble( row.inner, "endY" );
918
919 double width = V3GetDouble( row.inner, "width" );
920 wxString netname = V3GetString( row.inner, "netName" );
921
922 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( aBoard );
923
924 track->SetLayer( klayer );
925 track->SetStart( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ) );
926 track->SetEnd( PCB_IO_EASYEDAPRO_PARSER::ScalePos( end ) );
927 track->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( width ) );
928 track->SetNet( aBoard->FindNet( netname ) );
929
930 aBoard->Add( track.release(), ADD_MODE::APPEND );
931 }
932 else if( row.type == wxS( "ARC" ) )
933 {
934 int layer = V3GetInt( row.inner, "layerId", 1 );
935 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
936
937 VECTOR2D start;
938 start.x = V3GetDouble( row.inner, "startX" );
939 start.y = V3GetDouble( row.inner, "startY" );
940
942 end.x = V3GetDouble( row.inner, "endX" );
943 end.y = V3GetDouble( row.inner, "endY" );
944
945 double angle = V3GetDouble( row.inner, "angle" );
946 double width = V3GetDouble( row.inner, "width" );
947 wxString netname = V3GetString( row.inner, "netName" );
948
949 VECTOR2D delta = end - start;
950 VECTOR2D mid = ( start + delta / 2 );
951
952 double ha = angle / 2;
953 double hd = delta.EuclideanNorm() / 2;
954 double cdist = hd / tan( DEG2RAD( ha ) );
955 VECTOR2D center = mid + delta.Perpendicular().Resize( cdist );
956
957 SHAPE_ARC sarc;
961 PCB_IO_EASYEDAPRO_PARSER::ScalePos( center ), angle >= 0, width );
962
963 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( aBoard, &sarc );
964 arc->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( width ) );
965 arc->SetLayer( klayer );
966 arc->SetNet( aBoard->FindNet( netname ) );
967
968 aBoard->Add( arc.release(), ADD_MODE::APPEND );
969 }
970 else if( row.type == wxS( "POLY" ) )
971 {
972 int layer = V3GetInt( row.inner, "layerId", 1 );
973 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
974
975 double thickness = V3GetDouble( row.inner, "width" );
976 nlohmann::json polyData = row.inner.value( "path", nlohmann::json::array() );
977
978 std::vector<std::unique_ptr<PCB_SHAPE>> results =
979 m_v2Parser.ParsePoly( aBoard, polyData, false, false );
980
981 for( auto& shape : results )
982 {
983 shape->SetLayer( klayer );
984 shape->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( thickness ) );
985
986 aBoard->Add( shape.release(), ADD_MODE::APPEND );
987 }
988 }
989 else if( row.type == wxS( "FILL" ) )
990 {
991 int layer = V3GetInt( row.inner, "layerId", 1 );
992 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
993
994 wxString netname = V3GetString( row.inner, "netName" );
995
996 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
997
998 if( polyDataList.is_null() || polyDataList.empty() )
999 continue;
1000
1001 if( !polyDataList.at( 0 ).is_array() )
1002 polyDataList = nlohmann::json::array( { polyDataList } );
1003
1004 std::vector<SHAPE_LINE_CHAIN> contours;
1005
1006 for( nlohmann::json& polyData : polyDataList )
1007 {
1008 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, true );
1009 contour.SetClosed( true );
1010 contours.push_back( contour );
1011 }
1012
1013 SHAPE_POLY_SET zoneFillPoly;
1014
1015 for( SHAPE_LINE_CHAIN& contour : contours )
1016 zoneFillPoly.AddOutline( contour );
1017
1018 zoneFillPoly.RebuildHolesFromContours();
1019 zoneFillPoly.Fracture();
1020
1021 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1022
1023 zone->SetNet( aBoard->FindNet( netname ) );
1024 zone->SetLayer( klayer );
1025 zone->Outline()->Append( SHAPE_RECT( zoneFillPoly.BBox() ).Outline() );
1026 zone->SetFilledPolysList( klayer, zoneFillPoly );
1027 zone->SetAssignedPriority( 500 );
1028 zone->SetIsFilled( true );
1029 zone->SetNeedRefill( false );
1030
1031 zone->SetLocalClearance( bds.m_MinClearance );
1032 zone->SetMinThickness( bds.m_TrackMinWidth );
1033
1034 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1035 }
1036 else if( row.type == wxS( "POUR" ) )
1037 {
1038 int layer = V3GetInt( row.inner, "layerId", 1 );
1039 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1040
1041 wxString netname = V3GetString( row.inner, "netName" );
1042 int fillOrder = V3GetInt( row.inner, "order" );
1043
1044 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
1045
1046 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1047
1048 zone->SetNet( aBoard->FindNet( netname ) );
1049 zone->SetLayer( klayer );
1050 zone->SetAssignedPriority( 500 - fillOrder );
1051 zone->SetLocalClearance( bds.m_MinClearance );
1052 zone->SetMinThickness( bds.m_TrackMinWidth );
1053
1054 for( nlohmann::json& polyData : polyDataList )
1055 {
1056 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
1057 contour.SetClosed( true );
1058 zone->Outline()->Append( contour );
1059 }
1060
1061 wxASSERT( zone->Outline()->OutlineCount() == 1 );
1062
1063 poursToFill.emplace( row.id, zone.get() );
1064
1065 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1066 }
1067 else if( row.type == wxS( "POURED" ) )
1068 {
1069 nlohmann::json idArr = V3ParseIdArray( row.id );
1070 wxString parentId;
1071
1072 if( idArr.is_array() && idArr.size() > 1 )
1073 parentId = V3JsonToString( idArr[1] );
1074
1075 nlohmann::json fills = row.inner.value( "pourFill", nlohmann::json::array() );
1076
1077 if( !fills.is_array() )
1078 continue;
1079
1080 for( const nlohmann::json& fill : fills )
1081 {
1082 EASYEDAPRO::POURED poured;
1083 poured.pouredId = V3JsonToString( fill.value( "id",
1084 nlohmann::json( row.id ) ) );
1085 poured.parentId = parentId;
1086 poured.unki = V3GetInt( fill, "strokeWidth" );
1087 poured.isPoly = V3GetBool( fill, "fill" );
1088 poured.polyData = fill.value( "path", nlohmann::json::array() );
1089
1090 boardPouredMap.emplace( poured.parentId, poured );
1091 }
1092 }
1093 else if( row.type == wxS( "REGION" ) )
1094 {
1095 int layer = V3GetInt( row.inner, "layerId", 1 );
1096 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1097
1098 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
1099 nlohmann::json prohibitTypes = row.inner.value( "prohibitType",
1100 nlohmann::json::array() );
1101
1102 std::set<int> flags;
1103
1104 if( prohibitTypes.is_array() )
1105 {
1106 for( const auto& pt : prohibitTypes )
1107 {
1108 wxString ptStr;
1109
1110 if( pt.is_string() )
1111 ptStr = pt.get<std::string>();
1112
1113 if( ptStr == wxS( "COMPONENT" ) )
1114 flags.insert( 2 );
1115 else if( ptStr == wxS( "TRACK" ) )
1116 flags.insert( 5 );
1117 else if( ptStr == wxS( "COPPER" ) )
1118 flags.insert( 6 );
1119 else if( ptStr == wxS( "VIA" ) )
1120 flags.insert( 7 );
1121 else if( ptStr == wxS( "FILL" ) || ptStr == wxS( "PLANE" ) )
1122 flags.insert( 8 );
1123 }
1124 }
1125
1126 for( nlohmann::json& polyData : polyDataList )
1127 {
1128 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
1129 contour.SetClosed( true );
1130
1131 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1132
1133 zone->SetIsRuleArea( true );
1134 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
1135 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
1136 || !!flags.count( 8 ) );
1137 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
1138 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
1139 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
1140
1141 zone->SetLayer( klayer );
1142 zone->Outline()->Append( contour );
1143
1144 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1145 }
1146 }
1147 else if( row.type == wxS( "STRING" ) )
1148 {
1149 aBoard->Add( createV3Text( aBoard, row ), ADD_MODE::APPEND );
1150 }
1151 else if( row.type == wxS( "COMPONENT" ) )
1152 {
1153 COMP_DATA comp;
1154 comp.layer = V3GetInt( row.inner, "layerId", 1 );
1155 comp.pos.x = V3GetDouble( row.inner, "x" );
1156 comp.pos.y = V3GetDouble( row.inner, "y" );
1157 comp.angle = V3GetDouble( row.inner, "angle" );
1158
1159 nlohmann::json attrsObj = row.inner.value( "attrs", nlohmann::json::object() );
1160
1161 if( attrsObj.is_object() )
1162 {
1163 for( auto& [k, v] : attrsObj.items() )
1164 comp.attrs[wxString( k )] = V3JsonToString( v );
1165 }
1166
1167 componentMap[row.id] = comp;
1168 }
1169 else if( row.type == wxS( "ATTR" ) )
1170 {
1171 ATTR_DATA attr;
1172 attr.parentId = V3GetString( row.inner, "parentId" );
1173 attr.layer = V3GetInt( row.inner, "layerId", 3 );
1174 attr.hasPos = !V3IsNullOrMissing( row.inner, "x" );
1175
1176 if( attr.hasPos )
1177 {
1178 attr.x = V3GetDouble( row.inner, "x" );
1179 attr.y = V3GetDouble( row.inner, "y" );
1180 }
1181
1182 attr.key = V3GetString( row.inner, "key" );
1183 attr.value = V3JsonToString( row.inner.value( "value", nlohmann::json() ) );
1184 attr.keyVisible = V3GetBool( row.inner, "keyVisible" );
1185 attr.valVisible = V3GetBool( row.inner, "valueVisible" );
1186 attr.fontName = V3GetString( row.inner, "fontFamily", wxS( "default" ) );
1187 attr.fontSize = V3GetDouble( row.inner, "fontSize", 45.0 );
1188 attr.strokeWidth = V3GetDouble( row.inner, "strokeWidth", 6.0 );
1189 attr.origin = V3GetString( row.inner, "origin", wxS( "LEFT_BOTTOM" ) );
1190 attr.angle = V3GetDouble( row.inner, "angle" );
1191 attr.inverted = V3GetBool( row.inner, "reverse" ) ? 1 : 0;
1192 attr.mirror = V3GetBool( row.inner, "mirror" ) ? 1 : 0;
1193
1194 attrMap.emplace( attr.parentId, attr );
1195 }
1196 else if( row.type == wxS( "PAD_NET" ) )
1197 {
1198 nlohmann::json idArr = V3ParseIdArray( row.id );
1199
1200 PAD_NET_DATA pn;
1201 pn.compId = idArr.is_array() && idArr.size() > 1
1202 ? V3JsonToString( idArr[1] )
1203 : wxString();
1204 pn.padNumber = idArr.is_array() && idArr.size() > 2
1205 ? V3JsonToString( idArr[2] )
1206 : wxString();
1207 pn.padNet = V3GetString( row.inner, "padNet" );
1208
1209 if( !pn.compId.IsEmpty() )
1210 padNetMap.emplace( pn.compId, pn );
1211 }
1212 else if( row.type == wxS( "PAD" ) )
1213 {
1214 wxString netname = V3GetString( row.inner, "netName" );
1215
1216 std::unique_ptr<FOOTPRINT> footprint =
1217 std::make_unique<FOOTPRINT>( aBoard );
1218 std::unique_ptr<PAD> pad = createV3PAD( footprint.get(), row );
1219
1220 pad->SetNet( aBoard->FindNet( netname ) );
1221
1222 VECTOR2I pos = pad->GetPosition();
1223 EDA_ANGLE orient = pad->GetOrientation();
1224
1225 pad->SetPosition( VECTOR2I() );
1226 pad->SetOrientation( ANGLE_0 );
1227
1228 footprint->Add( pad.release(), ADD_MODE::APPEND );
1229 footprint->SetPosition( pos );
1230 footprint->SetOrientation( orient );
1231
1232 wxString fpName = wxS( "Pad_" ) + row.id;
1233 LIB_ID fpID = EASYEDAPRO::ToKiCadLibID( wxEmptyString, fpName );
1234
1235 footprint->SetFPID( fpID );
1236 footprint->Reference().SetVisible( true );
1237 footprint->Value().SetVisible( true );
1238 footprint->AutoPositionFields();
1239
1240 aBoard->Add( footprint.release(), ADD_MODE::APPEND );
1241 }
1242 else if( row.type == wxS( "IMAGE" ) )
1243 {
1244 int layer = V3GetInt( row.inner, "layerId", 1 );
1245 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1246
1247 VECTOR2D start;
1248 start.x = V3GetDouble( row.inner, "startX" );
1249 start.y = V3GetDouble( row.inner, "startY" );
1250
1251 VECTOR2D size;
1252 size.x = V3GetDouble( row.inner, "width" );
1253 size.y = V3GetDouble( row.inner, "height" );
1254
1255 double angle = V3GetDouble( row.inner, "angle" );
1256 bool mirror = V3GetBool( row.inner, "mirror" );
1257 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
1258
1259 BOX2I bbox;
1260 std::vector<SHAPE_LINE_CHAIN> contours;
1261 for( nlohmann::json& polyData : polyDataList )
1262 {
1263 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
1264 contour.SetClosed( true );
1265
1266 contours.push_back( contour );
1267
1268 bbox.Merge( contour.BBox() );
1269 }
1270
1271 if( bbox.GetSize().x == 0 || bbox.GetSize().y == 0 )
1272 continue;
1273
1276
1277 SHAPE_POLY_SET polySet;
1278
1279 for( SHAPE_LINE_CHAIN& contour : contours )
1280 {
1281 for( int i = 0; i < contour.PointCount(); i++ )
1282 {
1283 VECTOR2I pt = contour.CPoint( i );
1284 contour.SetPoint( i, VECTOR2I( pt.x * scale.x, pt.y * scale.y ) );
1285 }
1286
1287 polySet.AddOutline( contour );
1288 }
1289
1290 polySet.RebuildHolesFromContours();
1291
1292 std::unique_ptr<PCB_GROUP> group;
1293
1294 if( polySet.OutlineCount() > 1 )
1295 group = std::make_unique<PCB_GROUP>( aBoard );
1296
1297 BOX2I polyBBox = polySet.BBox();
1298
1299 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
1300 {
1301 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aBoard, SHAPE_T::POLY );
1302
1303 shape->SetFilled( true );
1304 shape->SetPolyShape( poly );
1305 shape->SetLayer( klayer );
1306 shape->SetWidth( 0 );
1307
1308 shape->Move( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ) - polyBBox.GetOrigin() );
1309 shape->Rotate( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
1310
1311 if( IsBackLayer( klayer ) ^ mirror )
1312 {
1314 shape->Mirror( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), flipDirection );
1315 }
1316
1317 if( group )
1318 group->AddItem( shape.get() );
1319
1320 aBoard->Add( shape.release(), ADD_MODE::APPEND );
1321 }
1322
1323 if( group )
1324 aBoard->Add( group.release(), ADD_MODE::APPEND );
1325 }
1326 else if( row.type == wxS( "OBJ" ) )
1327 {
1328 int layer = V3GetInt( row.inner, "layerId", 1 );
1329 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1330
1331 VECTOR2D start;
1332 start.x = V3GetDouble( row.inner, "startX" );
1333 start.y = V3GetDouble( row.inner, "startY" );
1334
1335 VECTOR2D size;
1336 size.x = V3GetDouble( row.inner, "width" );
1337 size.y = V3GetDouble( row.inner, "height" );
1338
1339 double angle = V3GetDouble( row.inner, "angle" );
1340 bool mirror = V3GetBool( row.inner, "mirror" );
1341 wxString imageUrl = V3GetString( row.inner, "path" );
1342
1343 if( imageUrl.empty() )
1344 continue;
1345
1346 wxString mimeType, base64Data;
1347
1348 if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
1349 {
1350 wxString objectId = imageUrl.AfterLast( ':' );
1351
1352 if( auto blob = get_opt( aBlobMap, objectId ) )
1353 {
1354 wxString blobUrl = blob->url;
1355
1356 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1357 {
1358 wxArrayString paramsArr =
1359 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1360
1361 base64Data = blobUrl.AfterFirst( ',' );
1362
1363 if( paramsArr.size() > 0 )
1364 mimeType = paramsArr[0];
1365 }
1366 }
1367 }
1368
1371
1372 if( mimeType.empty() || base64Data.empty() )
1373 continue;
1374
1375 wxMemoryBuffer buf = wxBase64Decode( base64Data );
1376
1377 if( mimeType == wxS( "image/svg+xml" ) )
1378 {
1379 // Not yet supported by EasyEDA
1380 }
1381 else
1382 {
1383 VECTOR2D kcenter = kstart + ksize / 2;
1384
1385 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap =
1386 std::make_unique<PCB_REFERENCE_IMAGE>( aBoard, kcenter, klayer );
1387 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1388
1389 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1390 & ~wxImage::Load_Verbose );
1391
1392 if( refImage.ReadImageFile( buf ) )
1393 {
1394 double scaleFactor = PCB_IO_EASYEDAPRO_PARSER::ScaleSize( size.x ) / refImage.GetSize().x;
1395 refImage.SetImageScale( scaleFactor );
1396
1397 // TODO: support non-90-deg angles
1398 bitmap->Rotate( kstart, EDA_ANGLE( angle, DEGREES_T ) );
1399
1400 if( mirror )
1401 {
1402 int x = bitmap->GetPosition().x;
1403 MIRROR( x, KiROUND( kstart.x ) );
1404 bitmap->SetX( x );
1405
1407 }
1408
1409 aBoard->Add( bitmap.release(), ADD_MODE::APPEND );
1410 }
1411 }
1412 }
1413 } // end first pass
1414
1415 // Second pass: place components
1416 for( auto const& [compId, comp] : componentMap )
1417 {
1418 wxString deviceId;
1419 wxString fpIdOverride;
1420 wxString fpDesignator;
1421
1422 // Collect attrs for this component
1423 auto attrRange = attrMap.equal_range( compId );
1424
1425 for( auto it = attrRange.first; it != attrRange.second; ++it )
1426 {
1427 const ATTR_DATA& attr = it->second;
1428
1429 if( attr.key == wxS( "Device" ) )
1430 deviceId = attr.value;
1431 else if( attr.key == wxS( "Footprint" ) )
1432 fpIdOverride = attr.value;
1433 else if( attr.key == wxS( "Designator" ) )
1434 fpDesignator = attr.value;
1435 }
1436
1437 // BOM / identity / 3D fields come from the component's Device ATTR.
1438 EASYEDAPRO::V3_DEVICE_DATA deviceData = GetV3DeviceData( aProject, deviceId );
1439 const std::map<wxString, wxString>& deviceAttrs = deviceData.attributes;
1440
1441 wxString fpId = fpIdOverride;
1442
1443 if( fpId.empty() )
1444 fpId = get_def( deviceAttrs, wxS( "Footprint" ), wxEmptyString );
1445
1446 if( fpId.empty() )
1447 {
1448 wxLogTrace( traceEasyEdaIo, wxT( "EasyEDA Pro v3 component '%s' (%s): no footprint mapping, skipping." ),
1449 compId, fpDesignator );
1450 continue;
1451 }
1452
1453 auto it = aFootprintMap.find( fpId );
1454
1455 if( it == aFootprintMap.end() )
1456 {
1457 wxLogTrace( traceEasyEdaIo, "Footprint of '%s' with uuid '%s' not found.", fpDesignator, fpId );
1458 continue;
1459 }
1460
1461 std::unique_ptr<FOOTPRINT>& footprintOrig = it->second;
1462 std::unique_ptr<FOOTPRINT> footprint(
1463 static_cast<FOOTPRINT*>( footprintOrig->Clone() ) );
1464
1465 footprint->SetParent( aBoard );
1466
1467 // Footprint LIB_ID uses package geometry name (e.g. C0402), matching schematic
1468 // Footprint fields. Device/BOM identity stays in fields, not the FPID.
1469 wxString libItemName = footprint->GetFPID().GetLibItemName();
1470
1471 if( libItemName.empty() )
1472 libItemName = fpId;
1473
1474 footprint->SetFPID( ToKiCadLibID( aFpLibName, libItemName ) );
1475
1476 wxString modelUuid = get_def( deviceAttrs, wxS( "3D Model" ), wxEmptyString );
1477 wxString modelTitle = get_def( deviceAttrs, wxS( "3D Model Title" ), modelUuid ).Trim();
1478 wxString modelTransform = get_def( deviceAttrs, wxS( "3D Model Transform" ), wxEmptyString );
1479
1480 m_v2Parser.FillFootprintModelInfo( footprint.get(), modelUuid, modelTitle, modelTransform );
1481
1482 wxString valueText = ResolveV3DeviceValueText( deviceAttrs );
1483
1484 if( !valueText.empty() )
1485 footprint->SetValue( valueText );
1486
1487 wxString description = deviceData.description;
1488
1489 if( description.empty() )
1490 description = get_def( deviceAttrs, wxS( "Description" ), wxEmptyString );
1491
1492 if( !description.empty() )
1493 {
1494 footprint->GetField( FIELD_T::DESCRIPTION )
1495 ->SetText( NormalizeEasyEDAText( ResolveDeviceFieldVariables( description, deviceAttrs ) ) );
1496 }
1497
1499 deviceAttrs, false,
1500 [&]( const wxString& attrKey, const wxString& value )
1501 {
1502 PCB_FIELD* field = nullptr;
1503
1504 if( attrKey == wxS( "Datasheet" ) )
1505 field = footprint->GetField( FIELD_T::DATASHEET );
1506 else
1507 field = footprint->GetField( attrKey );
1508
1509 if( !field )
1510 {
1511 field = new PCB_FIELD( footprint.get(), FIELD_T::USER, attrKey );
1512 footprint->Add( field, ADD_MODE::APPEND );
1513 }
1514
1515 field->SetText( value );
1516 field->SetVisible( false );
1517 } );
1518
1519 // Apply position, rotation, flip
1520 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( comp.layer );
1521
1522 if( klayer == B_Cu )
1523 footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
1524
1525 footprint->SetOrientationDegrees( comp.angle );
1526 footprint->SetPosition( PCB_IO_EASYEDAPRO_PARSER::ScalePos( comp.pos ) );
1527
1528 // Apply attributes (designator, etc.)
1529 for( auto attrIt = attrRange.first; attrIt != attrRange.second; ++attrIt )
1530 {
1531 const ATTR_DATA& attr = attrIt->second;
1532
1533 if( attr.key == wxS( "Designator" ) )
1534 {
1535 PCB_FIELD* field = footprint->GetField( FIELD_T::REFERENCE );
1536
1537 if( attr.fontName != wxS( "default" ) )
1538 field->SetFont( KIFONT::FONT::GetFont( attr.fontName ) );
1539
1540 if( attr.valVisible && attr.keyVisible )
1541 field->SetText( attr.key + ':' + attr.value );
1542 else if( attr.keyVisible )
1543 field->SetText( attr.key );
1544 else
1545 field->SetText( attr.value );
1546
1547 field->SetVisible( attr.keyVisible || attr.valVisible );
1548
1549 PCB_LAYER_ID attrLayer = m_v2Parser.LayerToKi( attr.layer );
1550 field->SetLayer( attrLayer );
1551
1552 if( attr.hasPos )
1553 {
1555 VECTOR2D( attr.x, attr.y ) ) );
1556 }
1557
1558 field->SetKeepUpright( false );
1559 field->SetTextAngleDegrees( footprint->IsFlipped() ? -attr.angle
1560 : attr.angle );
1561 field->SetIsKnockout( attr.inverted );
1562 field->SetTextThickness(
1563 PCB_IO_EASYEDAPRO_PARSER::ScaleSize( attr.strokeWidth ) );
1564 field->SetTextSize( VECTOR2D(
1565 PCB_IO_EASYEDAPRO_PARSER::ScaleSize( attr.fontSize * 0.55 ),
1566 PCB_IO_EASYEDAPRO_PARSER::ScaleSize( attr.fontSize * 0.6 ) ) );
1567
1568 int alignCode = V3AlignToOriginCode( attr.origin );
1569 V3AlignText( field, alignCode );
1570 }
1571 }
1572
1573 // Apply pad nets
1574 auto padNetRange = padNetMap.equal_range( compId );
1575
1576 for( auto pnIt = padNetRange.first; pnIt != padNetRange.second; ++pnIt )
1577 {
1578 const PAD_NET_DATA& pn = pnIt->second;
1579
1580 PAD* pad = footprint->FindPadByNumber( pn.padNumber );
1581
1582 if( pad )
1583 pad->SetNet( aBoard->FindNet( pn.padNet ) );
1584 }
1585
1586 aBoard->Add( footprint.release(), ADD_MODE::APPEND );
1587 }
1588
1589 // Set zone fills from POURED data
1591 {
1592 for( auto& [uuid, zone] : poursToFill )
1593 {
1594 SHAPE_POLY_SET fillPolySet;
1595 SHAPE_POLY_SET thermalSpokes;
1596
1597 auto range = boardPouredMap.equal_range( uuid );
1598
1599 for( auto& pIt = range.first; pIt != range.second; ++pIt )
1600 {
1601 const EASYEDAPRO::POURED& poured = pIt->second;
1602
1603 SHAPE_POLY_SET thisPoly;
1604
1605 for( int dataId = 0;
1606 dataId < static_cast<int>( poured.polyData.size() ); dataId++ )
1607 {
1608 const nlohmann::json& fillData = poured.polyData[dataId];
1609 const double ptScale = 10;
1610
1611 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour(
1612 fillData, false, ARC_HIGH_DEF / ptScale );
1613
1614 // Scale the fill
1615 for( int i = 0; i < contour.PointCount(); i++ )
1616 contour.SetPoint( i, contour.GetPoint( i ) * ptScale );
1617
1618 if( poured.isPoly )
1619 {
1620 contour.SetClosed( true );
1621
1622 // The contour can be self-intersecting
1623 SHAPE_POLY_SET simple( contour );
1624 simple.Simplify();
1625
1626 if( dataId == 0 )
1627 {
1628 thisPoly.Append( simple );
1629 }
1630 else
1631 {
1632 thisPoly.BooleanSubtract( simple );
1633 }
1634 }
1635 else
1636 {
1637 const int thermalWidth = pcbIUScale.mmToIU( 0.2 );
1638
1639 for( int segId = 0; segId < contour.SegmentCount(); segId++ )
1640 {
1641 const SEG& seg = contour.CSegment( segId );
1642
1643 TransformOvalToPolygon( thermalSpokes, seg.A, seg.B,
1644 thermalWidth, ARC_HIGH_DEF,
1645 ERROR_INSIDE );
1646 }
1647 }
1648 }
1649
1650 fillPolySet.Append( thisPoly );
1651 }
1652
1653 if( !fillPolySet.IsEmpty() )
1654 {
1655 fillPolySet.Simplify();
1656
1657 const int strokeWidth = pcbIUScale.MilsToIU( 8 );
1658
1659 fillPolySet.Inflate( strokeWidth / 2,
1661 ARC_HIGH_DEF, false );
1662
1663 fillPolySet.BooleanAdd( thermalSpokes );
1664 fillPolySet.Fracture();
1665
1666 zone->SetFilledPolysList( zone->GetFirstLayer(), fillPolySet );
1667 zone->SetNeedRefill( false );
1668 zone->SetIsFilled( true );
1669 }
1670 }
1671 }
1672
1673 // Heal board outlines
1674 std::vector<PCB_SHAPE*> shapes;
1675
1676 for( BOARD_ITEM* item : aBoard->Drawings() )
1677 {
1678 if( !item->IsOnLayer( Edge_Cuts ) )
1679 continue;
1680
1681 if( item->Type() == PCB_SHAPE_T )
1682 shapes.push_back( static_cast<PCB_SHAPE*>( item ) );
1683 }
1684
1686
1687 // Center the board
1688 BOX2I outlineBbox = aBoard->ComputeBoundingBox( true );
1689 PAGE_INFO pageInfo = aBoard->GetPageSettings();
1690
1691 VECTOR2D pageCenter( pcbIUScale.MilsToIU( pageInfo.GetWidthMils() / 2 ),
1692 pcbIUScale.MilsToIU( pageInfo.GetHeightMils() / 2 ) );
1693
1694 VECTOR2D offset = pageCenter - outlineBbox.GetCenter();
1695
1696 int alignGrid = pcbIUScale.mmToIU( 10 );
1697 offset.x = KiROUND( offset.x / alignGrid ) * alignGrid;
1698 offset.y = KiROUND( offset.y / alignGrid ) * alignGrid;
1699
1700 aBoard->Move( offset );
1701 bds.SetAuxOrigin( offset );
1702}
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:137
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
#define DEFAULT_COURTYARD_WIDTH
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
BASE_SET & set(size_t pos)
Definition base_set.h:126
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:414
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:374
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1497
const PAGE_INFO & GetPageSettings() const
Definition board.h:1010
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2980
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition board.cpp:954
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition board.cpp:820
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1183
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aPhysicalLayersOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition board.cpp:2721
unsigned GetNetCount() const
Definition board.h:1242
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1203
const DRAWINGS & Drawings() const
Definition board.h:465
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:653
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr const SizeVec & GetSize() const
Definition box2.h:203
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:94
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:342
void SetTextAngleDegrees(double aOrientation)
Definition eda_text.h:181
void SetKeepUpright(bool aKeepUpright)
Definition eda_text.cpp:381
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:231
void SetFont(KIFONT::FONT *aFont)
Definition eda_text.cpp:458
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.
DRAWINGS & GraphicalItems()
Definition footprint.h:407
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
Handle the data for a net.
Definition netinfo.h:50
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
Definition pad.h:61
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:606
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:613
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
double GetHeightMils() const
Definition page_info.h:143
double GetWidthMils() const
Definition page_info.h:138
static VECTOR2< T > ScalePos(VECTOR2< T > aValue)
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 EASYEDAPRO::V3_DOC_RAW &aDoc, const wxString &aFpLibName)
PCB_IO_EASYEDAPRO_V3_PARSER(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter)
std::unique_ptr< PAD > createV3PAD(FOOTPRINT *aFootprint, const EASYEDAPRO::V3_ROW &aRow)
std::unique_ptr< FOOTPRINT > ParseFootprint(const std::map< wxString, EASYEDAPRO::BLOB > &aBlobMap, const EASYEDAPRO::V3_DOC_RAW &aDoc)
PCB_TEXT * createV3Text(BOARD_ITEM_CONTAINER *aContainer, const EASYEDAPRO::V3_ROW &aRow)
void SetTextThickness(int aWidth) override
The TextThickness is that set by the user.
Definition pcb_text.cpp:512
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true) override
Definition pcb_text.cpp:484
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_text.h:102
A progress reporter interface for use in multi-threaded environments.
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
BITMAP_BASE & MutableImage() const
Only use this if you really need to modify the underlying image.
bool ReadImageFile(const wxString &aFullFilename)
Read and store an image file.
VECTOR2I GetSize() const
void SetImageScale(double aScale)
Set the image "zoom" value.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
SHAPE_ARC & ConstructFromStartEndCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aClockwise=false, double aWidth=0)
Constructs this arc from the given start, end and center.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
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 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.
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:422
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:424
@ DEGREES_T
Definition eda_angle.h:31
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, int aChainingEpsilon)
Connects shapes to each other, making continious contours (adjacent shapes will have a common vertex)...
const wxChar *const traceEasyEdaIo
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:829
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ F_Paste
Definition layer_ids.h:100
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ F_Fab
Definition layer_ids.h:115
@ F_Cu
Definition layer_ids.h:60
wxString get_def(const std::map< wxString, wxString > &aMap, const char *aKey, const char *aDefval="")
Definition map_helpers.h:60
std::optional< V > get_opt(const std::map< wxString, V > &aMap, const wxString &aKey)
Definition map_helpers.h:30
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:41
FLIP_DIRECTION
Definition mirror.h:23
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:24
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:25
LIB_ID ToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
bool V3GetBool(const nlohmann::json &aObj, const char *aKey, bool aDefault)
void ForEachImportedDeviceField(const std::map< wxString, wxString > &aDeviceAttributes, bool aIncludeValue, const std::function< void(const wxString &aKey, const wxString &aValue)> &aCallback)
Invoke aCallback for each non-empty whitelisted Device field (resolved + normalized).
wxString ResolveDeviceFieldVariables(const wxString &aInput, const std::map< wxString, wxString > &aDeviceAttributes)
Resolve EasyEDA ={Var} / ={A}text{B} field expressions against device attributes.
wxString NormalizeEasyEDAText(wxString aText)
Replace EasyEDA temperature glyph (℃) with °C.
V3_DEVICE_DATA GetV3DeviceData(const nlohmann::json &aProject, const wxString &aDeviceUuid)
static const bool IMPORT_POURED
nlohmann::json V3ParseIdArray(const wxString &aId)
bool V3IsNullOrMissing(const nlohmann::json &aObj, const char *aKey)
double V3GetDouble(const nlohmann::json &aObj, const char *aKey, double aDefault)
wxString V3JsonToString(const nlohmann::json &aValue, const wxString &aDefault)
wxString ResolveV3DeviceValueText(const std::map< wxString, wxString > &aDeviceAttributes)
Preferred Value text: Value attribute, else Name, with variables resolved.
int V3GetInt(const nlohmann::json &aObj, const char *aKey, int aDefault)
wxString V3GetString(const nlohmann::json &aObj, const char *aKey, const wxString &aDefault)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:98
@ PTH
Plated through hole pad.
Definition padstack.h:97
@ ROUNDRECT
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:53
Class to handle a set of BOARD_ITEMs.
static const int SHAPE_JOIN_DISTANCE
static int V3AlignToOriginCode(const wxString &aAlign)
static void V3AlignText(EDA_TEXT *text, int align)
const int scale
nlohmann::json polyData
std::map< wxString, wxString > attributes
Raw parsed document from an EasyEDA Pro v3 .epru stream.
std::vector< V3_ROW > rows
One parsed row from an EasyEDA Pro v3 .epru document stream.
@ USER
The field ID hasn't been set yet; field is invalid.
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
@ DATASHEET
name of datasheet
@ REFERENCE
Field Reference of part, i.e. "IC21".
KIBIS_COMPONENT * comp
VECTOR2I center
int radius
VECTOR2I end
VECTOR2I location
int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
wxLogTrace helper definitions.
double DEG2RAD(double deg)
Definition trigo.h:172
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682