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