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
96 // Process hole
97 bool hasHole = false;
98
99 if( holeObj.is_object() )
100 {
101 wxString holeType = V3GetString( holeObj, "holeType", wxS( "ROUND" ) );
102
103 if( holeType == wxS( "RECT" ) )
104 holeType = wxS( "SLOT" );
105
106 if( holeType != wxS( "ROUND" ) && holeType != wxS( "SLOT" ) )
107 holeType = wxS( "ROUND" );
108
109 VECTOR2D drill;
110 drill.x = V3GetDouble( holeObj, "width" );
111 drill.y = V3GetDouble( holeObj, "height" );
112
113 if( drill.x > 0 || drill.y > 0 )
114 {
115 hasHole = true;
116
117 double drill_dir = V3GetDouble( aRow.inner, "relativeAngle" );
118
119 double deg = EDA_ANGLE( drill_dir, DEGREES_T ).Normalize90().AsDegrees();
120
121 if( std::abs( deg ) >= 45 )
122 std::swap( drill.x, drill.y );
123
124 if( holeType == wxS( "SLOT" ) )
125 pad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
126
127 pad->SetDrillSize( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( drill ) );
128 pad->SetLayerSet( PAD::PTHMask() );
129 pad->SetAttribute( PAD_ATTRIB::PTH );
130 }
131 }
132
133 if( !hasHole )
134 {
135 if( klayer == F_Cu )
136 pad->SetLayerSet( PAD::SMDMask() );
137 else if( klayer == B_Cu )
138 pad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
139
140 pad->SetAttribute( PAD_ATTRIB::SMD );
141 }
142
143 // Process pad shape
144 if( padDef.is_object() )
145 {
146 wxString padType = V3GetString( padDef, "padType", wxS( "RECT" ) );
147
148 if( padType == wxS( "RECT" ) )
149 {
150 VECTOR2D size;
151 size.x = V3GetDouble( padDef, "width", 1.0 );
152 size.y = V3GetDouble( padDef, "height", 1.0 );
153 double radius = V3GetDouble( padDef, "radius" );
154 double radiusRatio = std::clamp( radius, 0.0, 100.0 ) / 100 / 2;
155
156 pad->SetSize( PADSTACK::ALL_LAYERS,
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
173 pad->SetSize( PADSTACK::ALL_LAYERS,
175 pad->SetShape( PADSTACK::ALL_LAYERS,
176 size.x == size.y ? PAD_SHAPE::CIRCLE : PAD_SHAPE::OVAL );
177 }
178 else if( padType == wxS( "OVAL" ) )
179 {
180 VECTOR2D size;
181 size.x = V3GetDouble( padDef, "width", 1.0 );
182 size.y = V3GetDouble( padDef, "height", 1.0 );
183
184 pad->SetSize( PADSTACK::ALL_LAYERS,
187 }
188 else if( padType == wxS( "POLY" ) || padType == wxS( "POLYGON" ) )
189 {
191 pad->SetAnchorPadShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
192 pad->SetSize( PADSTACK::ALL_LAYERS, { 1, 1 } );
193
194 nlohmann::json polyData = padDef.value( "path", nlohmann::json() );
195
196 std::vector<std::unique_ptr<PCB_SHAPE>> results =
197 m_v2Parser.ParsePoly( aFootprint, polyData, true, false );
198
199 for( auto& shape : results )
200 {
201 shape->SetLayer( klayer );
202 shape->SetWidth( 0 );
203 shape->Move( -pad->GetPosition() );
204 pad->AddPrimitive( PADSTACK::ALL_LAYERS, shape.release() );
205 }
206 }
207 }
208 else
209 {
210 pad->SetSize( PADSTACK::ALL_LAYERS,
213 }
214
215 pad->SetThermalSpokeAngle( ANGLE_90 );
216
217 return pad;
218}
219
220
222 const EASYEDAPRO::V3_ROW& aRow )
223{
224 int layer = V3GetInt( aRow.inner, "layerId", 3 );
225 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
226
228 location.x = V3GetDouble( aRow.inner, "x" );
229 location.y = V3GetDouble( aRow.inner, "y" );
230
231 wxString string = V3GetString( aRow.inner, "text" );
232 wxString font = V3GetString( aRow.inner, "fontFamily", wxS( "default" ) );
233
234 double height = V3GetDouble( aRow.inner, "fontSize", 45.0 );
235 double strokew = V3GetDouble( aRow.inner, "strokeWidth", 6.0 );
236
237 wxString originStr = V3GetString( aRow.inner, "origin", wxS( "LEFT_BOTTOM" ) );
238 int align = V3AlignToOriginCode( originStr );
239 double angle = V3GetDouble( aRow.inner, "angle" );
240 int inverted = V3GetBool( aRow.inner, "reverse" ) ? 1 : 0;
241 int mirror = V3GetBool( aRow.inner, "mirror" ) ? 1 : 0;
242
243 PCB_TEXT* text = new PCB_TEXT( aContainer );
244
245 text->SetText( string );
246 text->SetLayer( klayer );
248 text->SetIsKnockout( inverted );
249 text->SetTextThickness( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( strokew ) );
250 text->SetTextSize( VECTOR2D( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( height * 0.6 ),
251 PCB_IO_EASYEDAPRO_PARSER::ScaleSize( height * 0.7 ) ) );
252
253 if( font != wxS( "default" ) )
254 text->SetFont( KIFONT::FONT::GetFont( font ) );
255
256 V3AlignText( text, align );
257
258 if( IsBackLayer( klayer ) ^ !!mirror )
259 {
260 text->SetMirrored( true );
261 text->SetTextAngleDegrees( -angle );
262 }
263 else
264 {
265 text->SetTextAngleDegrees( angle );
266 }
267
268 return text;
269}
270
271
272FOOTPRINT* PCB_IO_EASYEDAPRO_V3_PARSER::ParseFootprint( const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
273 const V3_DOC_RAW& aDoc )
274{
275 std::unique_ptr<FOOTPRINT> footprintPtr = std::make_unique<FOOTPRINT>( m_board );
276 FOOTPRINT* footprint = footprintPtr.get();
277
278 const VECTOR2I defaultTextSize( pcbIUScale.mmToIU( 1.0 ), pcbIUScale.mmToIU( 1.0 ) );
279 const int defaultTextThickness( pcbIUScale.mmToIU( 0.15 ) );
280
281 for( PCB_FIELD* field : footprint->GetFields() )
282 {
283 field->SetTextSize( defaultTextSize );
284 field->SetTextThickness( defaultTextThickness );
285 }
286
287 for( const V3_ROW& row : aDoc.rows )
288 {
289 if( row.type == wxS( "POLY" ) )
290 {
291 int layer = V3GetInt( row.inner, "layerId", 1 );
292 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
293 double thickness = V3GetDouble( row.inner, "width" );
294
295 nlohmann::json polyData = row.inner.value( "path", nlohmann::json::array() );
296
297 std::vector<std::unique_ptr<PCB_SHAPE>> results =
298 m_v2Parser.ParsePoly( footprint, polyData, false, false );
299
300 for( auto& shape : results )
301 {
302 shape->SetLayer( klayer );
303 shape->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( thickness ) );
304 footprint->Add( shape.release(), ADD_MODE::APPEND );
305 }
306 }
307 else if( row.type == wxS( "PAD" ) )
308 {
309 std::unique_ptr<PAD> pad = createV3PAD( footprint, row );
310 footprint->Add( pad.release(), ADD_MODE::APPEND );
311 }
312 else if( row.type == wxS( "FILL" ) )
313 {
314 int layer = V3GetInt( row.inner, "layerId", 1 );
315 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
316
317 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
318
319 if( !polyDataList.is_null() && !polyDataList.empty() )
320 {
321 if( !polyDataList.at( 0 ).is_array() )
322 polyDataList = nlohmann::json::array( { polyDataList } );
323
324 std::vector<SHAPE_LINE_CHAIN> contours;
325
326 for( nlohmann::json& polyData : polyDataList )
327 {
328 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
329 contour.SetClosed( true );
330 contours.push_back( contour );
331 }
332
333 SHAPE_POLY_SET polySet;
334
335 for( SHAPE_LINE_CHAIN& contour : contours )
336 polySet.AddOutline( contour );
337
338 polySet.RebuildHolesFromContours();
339
340 std::unique_ptr<PCB_GROUP> group;
341
342 if( polySet.OutlineCount() > 1 )
343 group = std::make_unique<PCB_GROUP>( footprint );
344
345 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
346 {
347 std::unique_ptr<PCB_SHAPE> shape =
348 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::POLY );
349
350 shape->SetFilled( true );
351 shape->SetPolyShape( poly );
352 shape->SetLayer( klayer );
353 shape->SetWidth( 0 );
354
355 if( group )
356 group->AddItem( shape.get() );
357
358 footprint->Add( shape.release(), ADD_MODE::APPEND );
359 }
360
361 if( group )
362 footprint->Add( group.release(), ADD_MODE::APPEND );
363 }
364 }
365 else if( row.type == wxS( "ATTR" ) )
366 {
367 wxString key = V3GetString( row.inner, "key" );
368 wxString value = V3JsonToString( row.inner.value( "value", nlohmann::json() ) );
369
370 if( key == wxS( "Designator" ) )
371 footprint->GetField( FIELD_T::REFERENCE )->SetText( value );
372 }
373 else if( row.type == wxS( "REGION" ) )
374 {
375 int layer = V3GetInt( row.inner, "layerId", 1 );
376 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
377
378 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
379 nlohmann::json prohibitTypes = row.inner.value( "prohibitType", nlohmann::json::array() );
380
381 std::set<int> flags;
382
383 if( prohibitTypes.is_array() )
384 {
385 for( const auto& pt : prohibitTypes )
386 {
387 wxString ptStr;
388
389 if( pt.is_string() )
390 ptStr = pt.get<std::string>();
391
392 if( ptStr == wxS( "COMPONENT" ) )
393 flags.insert( 2 );
394 else if( ptStr == wxS( "TRACK" ) )
395 flags.insert( 5 );
396 else if( ptStr == wxS( "COPPER" ) )
397 flags.insert( 6 );
398 else if( ptStr == wxS( "VIA" ) )
399 flags.insert( 7 );
400 else if( ptStr == wxS( "FILL" ) || ptStr == wxS( "PLANE" ) )
401 flags.insert( 8 );
402 }
403 }
404
405 for( nlohmann::json& polyData : polyDataList )
406 {
407 SHAPE_POLY_SET polySet;
408
409 std::vector<std::unique_ptr<PCB_SHAPE>> results =
410 m_v2Parser.ParsePoly( nullptr, polyData, true, false );
411
412 for( auto& shape : results )
413 {
414 shape->SetFilled( true );
415 shape->TransformShapeToPolygon( polySet, klayer, 0, ARC_HIGH_DEF,
416 ERROR_INSIDE, true );
417 }
418
419 polySet.Simplify();
420
421 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( footprint );
422
423 zone->SetIsRuleArea( true );
424 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
425 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
426 || !!flags.count( 8 ) );
427 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
428 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
429 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
430
431 zone->SetLayer( klayer );
432 zone->Outline()->Append( polySet );
433
434 footprint->Add( zone.release(), ADD_MODE::APPEND );
435 }
436 }
437 else if( row.type == wxS( "IMAGE" ) )
438 {
439 int layer = V3GetInt( row.inner, "layerId", 1 );
440 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
441
442 VECTOR2D start;
443 start.x = V3GetDouble( row.inner, "startX" );
444 start.y = V3GetDouble( row.inner, "startY" );
445
446 VECTOR2D size;
447 size.x = V3GetDouble( row.inner, "width" );
448 size.y = V3GetDouble( row.inner, "height" );
449
450 double angle = V3GetDouble( row.inner, "angle" );
451 bool mirror = V3GetBool( row.inner, "mirror" );
452 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
453
454 BOX2I bbox;
455 std::vector<SHAPE_LINE_CHAIN> contours;
456 for( nlohmann::json& polyData : polyDataList )
457 {
458 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
459 contour.SetClosed( true );
460
461 contours.push_back( contour );
462
463 bbox.Merge( contour.BBox() );
464 }
465
466 if( bbox.GetSize().x == 0 || bbox.GetSize().y == 0 )
467 continue;
468
471
472 SHAPE_POLY_SET polySet;
473
474 for( SHAPE_LINE_CHAIN& contour : contours )
475 {
476 for( int i = 0; i < contour.PointCount(); i++ )
477 {
478 VECTOR2I pt = contour.CPoint( i );
479 contour.SetPoint( i, VECTOR2I( pt.x * scale.x, pt.y * scale.y ) );
480 }
481
482 polySet.AddOutline( contour );
483 }
484
485 polySet.RebuildHolesFromContours();
486
487 std::unique_ptr<PCB_GROUP> group;
488
489 if( polySet.OutlineCount() > 1 )
490 group = std::make_unique<PCB_GROUP>( footprint );
491
492 BOX2I polyBBox = polySet.BBox();
493
494 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
495 {
496 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::POLY );
497
498 shape->SetFilled( true );
499 shape->SetPolyShape( poly );
500 shape->SetLayer( klayer );
501 shape->SetWidth( 0 );
502
503 shape->Move( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ) - polyBBox.GetOrigin() );
504 shape->Rotate( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
505
506 if( IsBackLayer( klayer ) ^ mirror )
507 {
509 shape->Mirror( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), flipDirection );
510 }
511
512 if( group )
513 group->AddItem( shape.get() );
514
515 footprint->Add( shape.release(), ADD_MODE::APPEND );
516 }
517
518 if( group )
519 footprint->Add( group.release(), ADD_MODE::APPEND );
520 }
521 else if( row.type == wxS( "OBJ" ) )
522 {
523 int layer = V3GetInt( row.inner, "layerId", 1 );
524 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
525
526 VECTOR2D start;
527 start.x = V3GetDouble( row.inner, "startX" );
528 start.y = V3GetDouble( row.inner, "startY" );
529
530 VECTOR2D size;
531 size.x = V3GetDouble( row.inner, "width" );
532 size.y = V3GetDouble( row.inner, "height" );
533
534 double angle = V3GetDouble( row.inner, "angle" );
535 bool mirror = V3GetBool( row.inner, "mirror" );
536 wxString imageUrl = V3GetString( row.inner, "path" );
537
538 if( imageUrl.empty() )
539 continue;
540
541 wxString mimeType, base64Data;
542
543 if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
544 {
545 wxString objectId = imageUrl.AfterLast( ':' );
546
547 if( auto blob = get_opt( aBlobMap, objectId ) )
548 {
549 wxString blobUrl = blob->url;
550
551 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
552 {
553 wxArrayString paramsArr =
554 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
555
556 base64Data = blobUrl.AfterFirst( ',' );
557
558 if( paramsArr.size() > 0 )
559 mimeType = paramsArr[0];
560 }
561 }
562 }
563
566
567 if( mimeType.empty() || base64Data.empty() )
568 continue;
569
570 wxMemoryBuffer buf = wxBase64Decode( base64Data );
571
572 if( mimeType == wxS( "image/svg+xml" ) )
573 {
574 // Not yet supported by EasyEDA
575 }
576 else
577 {
578 VECTOR2D kcenter = kstart + ksize / 2;
579
580 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap =
581 std::make_unique<PCB_REFERENCE_IMAGE>( footprint, kcenter, klayer );
582 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
583
584 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
585 & ~wxImage::Load_Verbose );
586
587 if( refImage.ReadImageFile( buf ) )
588 {
589 double scaleFactor = PCB_IO_EASYEDAPRO_PARSER::ScaleSize( size.x ) / refImage.GetSize().x;
590 refImage.SetImageScale( scaleFactor );
591
592 // TODO: support non-90-deg angles
593 bitmap->Rotate( kstart, EDA_ANGLE( angle, DEGREES_T ) );
594
595 if( mirror )
596 {
597 int x = bitmap->GetPosition().x;
598 MIRROR( x, KiROUND( kstart.x ) );
599 bitmap->SetX( x );
600
602 }
603
604 footprint->Add( bitmap.release(), ADD_MODE::APPEND );
605 }
606 }
607 }
608 else if( row.type == wxS( "STRING" ) )
609 {
610 footprint->Add( createV3Text( footprint, row ), ADD_MODE::APPEND );
611 }
612 }
613
614 // 3D models are applied by the caller from the component's Device ATTR (board import)
615 // or a matching library device (FootprintLoad). Do not pick an arbitrary device here:
616 // footprints are often shared by devices with different models.
617
618 // Heal board outlines
619 std::vector<PCB_SHAPE*> edgeShapes;
620
621 for( BOARD_ITEM* item : footprint->GraphicalItems() )
622 {
623 if( item->IsOnLayer( Edge_Cuts ) && item->Type() == PCB_SHAPE_T )
624 edgeShapes.push_back( static_cast<PCB_SHAPE*>( item ) );
625 }
626
628
629 // Build courtyard if missing
630 if( !footprint->IsOnLayer( F_CrtYd ) )
631 {
632 BOX2I bbox = footprint->GetLayerBoundingBox( { F_Cu, F_Fab, F_Paste, F_Mask, Edge_Cuts } );
633 bbox.Inflate( pcbIUScale.mmToIU( 0.25 ) );
634
635 std::unique_ptr<PCB_SHAPE> shape =
636 std::make_unique<PCB_SHAPE>( footprint, SHAPE_T::RECTANGLE );
637
638 shape->SetWidth( pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ) );
639 shape->SetLayer( F_CrtYd );
640 shape->SetStart( bbox.GetOrigin() );
641 shape->SetEnd( bbox.GetEnd() );
642
643 footprint->Add( shape.release(), ADD_MODE::APPEND );
644 }
645
646 // Add F_Fab reference text if missing
647 bool hasFabRef = false;
648
649 for( BOARD_ITEM* item : footprint->GraphicalItems() )
650 {
651 if( item->Type() == PCB_TEXT_T && item->IsOnLayer( F_Fab ) )
652 {
653 if( static_cast<PCB_TEXT*>( item )->GetText() == wxT( "${REFERENCE}" ) )
654 {
655 hasFabRef = true;
656 break;
657 }
658 }
659 }
660
661 if( !hasFabRef )
662 {
663 int c_refTextSize = pcbIUScale.mmToIU( 0.5 );
664 int c_refTextThickness = pcbIUScale.mmToIU( 0.1 );
665 std::unique_ptr<PCB_TEXT> refText = std::make_unique<PCB_TEXT>( footprint );
666
667 refText->SetLayer( F_Fab );
668 refText->SetTextSize( VECTOR2I( c_refTextSize, c_refTextSize ) );
669 refText->SetTextThickness( c_refTextThickness );
670 refText->SetText( wxT( "${REFERENCE}" ) );
671
672 footprint->Add( refText.release(), ADD_MODE::APPEND );
673 }
674
675 return footprintPtr.release();
676}
677
678
679static void V3AlignText( EDA_TEXT* text, int align )
680{
681 switch( align )
682 {
683 case 1:
684 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
685 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
686 break;
687 case 2:
688 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
689 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
690 break;
691 case 3:
692 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
693 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
694 break;
695 case 4:
696 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
697 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
698 break;
699 case 5:
700 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
701 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
702 break;
703 case 6:
704 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
705 text->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
706 break;
707 case 7:
708 text->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
709 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
710 break;
711 case 8:
712 text->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
713 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
714 break;
715 case 9:
716 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
717 text->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
718 break;
719 }
720}
721
722
723static int V3AlignToOriginCode( const wxString& aAlign )
724{
725 if( aAlign == wxS( "LEFT_TOP" ) )
726 return 1;
727 if( aAlign == wxS( "LEFT_CENTER" ) || aAlign == wxS( "LEFT_MIDDLE" ) )
728 return 2;
729 if( aAlign == wxS( "LEFT_BOTTOM" ) )
730 return 3;
731 if( aAlign == wxS( "CENTER_TOP" ) )
732 return 4;
733 if( aAlign == wxS( "CENTER_MIDDLE" ) || aAlign == wxS( "CENTER_CENTER" ) )
734 return 5;
735 if( aAlign == wxS( "CENTER_BOTTOM" ) )
736 return 6;
737 if( aAlign == wxS( "RIGHT_TOP" ) )
738 return 7;
739 if( aAlign == wxS( "RIGHT_CENTER" ) || aAlign == wxS( "RIGHT_MIDDLE" ) )
740 return 8;
741 if( aAlign == wxS( "RIGHT_BOTTOM" ) )
742 return 9;
743
744 return 3; // default: LEFT_BOTTOM
745}
746
747
749 BOARD* aBoard, const nlohmann::json& aProject,
750 std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
751 const std::map<wxString, EASYEDAPRO::BLOB>& aBlobMap,
752 const std::multimap<wxString, EASYEDAPRO::POURED>& aPouredMap,
753 const V3_DOC_RAW& aDoc, const wxString& aFpLibName )
754{
755 // Structures to collect component-related rows for second-pass placement
756 struct COMP_DATA
757 {
758 int layer = 1;
759 VECTOR2D pos;
760 double angle = 0;
761 std::map<wxString, wxString> attrs;
762 };
763
764 struct ATTR_DATA
765 {
766 wxString parentId;
767 int layer = 3;
768 double x = 0;
769 double y = 0;
770 bool hasPos = false;
771 wxString key;
772 wxString value;
773 bool keyVisible = false;
774 bool valVisible = false;
775 wxString fontName;
776 double fontSize = 45.0;
777 double strokeWidth = 6.0;
778 wxString origin;
779 double angle = 0;
780 int inverted = 0;
781 int mirror = 0;
782 };
783
784 struct PAD_NET_DATA
785 {
786 wxString compId;
787 wxString padNumber;
788 wxString padNet;
789 };
790
791 std::map<wxString, COMP_DATA> componentMap;
792 std::multimap<wxString, ATTR_DATA> attrMap;
793 std::multimap<wxString, PAD_NET_DATA> padNetMap;
794
795 std::multimap<wxString, EASYEDAPRO::POURED> boardPouredMap = aPouredMap;
796 std::map<wxString, ZONE*> poursToFill;
797
799
800 for( const V3_ROW& row : aDoc.rows )
801 {
802 if( row.type == wxS( "LAYER" ) )
803 {
804 int layer = V3GetInt( row.inner, "layerId", 1 );
805 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
806
807 bool use = V3GetBool( row.inner, "use", true );
808 wxString layerName = V3GetString( row.inner, "layerName" );
809
810 if( use )
811 {
812 LSET blayers = aBoard->GetEnabledLayers();
813 blayers.set( klayer );
814 aBoard->SetEnabledLayers( blayers );
815
816 if( !layerName.IsEmpty() )
817 aBoard->SetLayerName( klayer, layerName );
818 }
819 }
820 else if( row.type == wxS( "NET" ) )
821 {
822 nlohmann::json idArr = V3ParseIdArray( row.id );
823 wxString netname;
824
825 if( idArr.is_array() && idArr.size() > 1 )
826 netname = V3JsonToString( idArr[1] );
827
828 if( !netname.IsEmpty() )
829 {
830 aBoard->Add( new NETINFO_ITEM( aBoard, netname,
831 aBoard->GetNetCount() + 1 ),
833 }
834 }
835 else if( row.type == wxS( "RULE" ) )
836 {
837 nlohmann::json idArr = V3ParseIdArray( row.id );
838
839 if( !idArr.is_array() || idArr.size() < 3 )
840 continue;
841
842 wxString ruleType = V3JsonToString( idArr[1] );
843 bool isDefault = V3GetString( row.inner, "ruleState" ) == wxS( "DEFAULT" );
844
845 nlohmann::json context = row.inner.value( "ruleContext", nlohmann::json() );
846
847 if( ruleType == wxS( "TRACK" ) && isDefault )
848 {
849 nlohmann::json track = context.value( "track", nlohmann::json() );
850 nlohmann::json content = track.value( "content", nlohmann::json::array() );
851
852 if( content.is_array() && !content.empty() )
853 {
854 double minVal = V3GetDouble( content[0], "stroMin" );
856 }
857 }
858 else if( ruleType == wxS( "SAFE" ) && isDefault )
859 {
860 nlohmann::json safeSpacing = context.value( "safeSpacing",
861 nlohmann::json::array() );
862
863 if( safeSpacing.is_array() && !safeSpacing.empty() )
864 {
865 nlohmann::json content = safeSpacing[0].value( "content",
866 nlohmann::json::array() );
867
868 int minVal = INT_MAX;
869
870 for( const auto& arr : content )
871 {
872 if( arr.is_array() )
873 {
874 for( const auto& val : arr )
875 {
876 int v = val.is_number() ? val.get<int>() : 0;
877 if( v < minVal )
878 minVal = v;
879 }
880 }
881 }
882
883 if( minVal != INT_MAX )
885 }
886 }
887 }
888 else if( row.type == wxS( "VIA" ) )
889 {
891 center.x = V3GetDouble( row.inner, "centerX" );
892 center.y = V3GetDouble( row.inner, "centerY" );
893
894 double drill = V3GetDouble( row.inner, "holeDiameter" );
895 double dia = V3GetDouble( row.inner, "viaDiameter" );
896 wxString netname = V3GetString( row.inner, "netName" );
897
898 std::unique_ptr<PCB_VIA> via = std::make_unique<PCB_VIA>( aBoard );
899
901 via->SetDrill( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( drill ) );
902 via->SetWidth( PADSTACK::ALL_LAYERS,
904 via->SetNet( aBoard->FindNet( netname ) );
905
906 aBoard->Add( via.release(), ADD_MODE::APPEND );
907 }
908 else if( row.type == wxS( "LINE" ) )
909 {
910 int layer = V3GetInt( row.inner, "layerId", 1 );
911 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
912
913 VECTOR2D start;
914 start.x = V3GetDouble( row.inner, "startX" );
915 start.y = V3GetDouble( row.inner, "startY" );
916
918 end.x = V3GetDouble( row.inner, "endX" );
919 end.y = V3GetDouble( row.inner, "endY" );
920
921 double width = V3GetDouble( row.inner, "width" );
922 wxString netname = V3GetString( row.inner, "netName" );
923
924 std::unique_ptr<PCB_TRACK> track = std::make_unique<PCB_TRACK>( aBoard );
925
926 track->SetLayer( klayer );
927 track->SetStart( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ) );
928 track->SetEnd( PCB_IO_EASYEDAPRO_PARSER::ScalePos( end ) );
929 track->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( width ) );
930 track->SetNet( aBoard->FindNet( netname ) );
931
932 aBoard->Add( track.release(), ADD_MODE::APPEND );
933 }
934 else if( row.type == wxS( "ARC" ) )
935 {
936 int layer = V3GetInt( row.inner, "layerId", 1 );
937 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
938
939 VECTOR2D start;
940 start.x = V3GetDouble( row.inner, "startX" );
941 start.y = V3GetDouble( row.inner, "startY" );
942
944 end.x = V3GetDouble( row.inner, "endX" );
945 end.y = V3GetDouble( row.inner, "endY" );
946
947 double angle = V3GetDouble( row.inner, "angle" );
948 double width = V3GetDouble( row.inner, "width" );
949 wxString netname = V3GetString( row.inner, "netName" );
950
951 VECTOR2D delta = end - start;
952 VECTOR2D mid = ( start + delta / 2 );
953
954 double ha = angle / 2;
955 double hd = delta.EuclideanNorm() / 2;
956 double cdist = hd / tan( DEG2RAD( ha ) );
957 VECTOR2D center = mid + delta.Perpendicular().Resize( cdist );
958
959 SHAPE_ARC sarc;
963 PCB_IO_EASYEDAPRO_PARSER::ScalePos( center ), angle >= 0, width );
964
965 std::unique_ptr<PCB_ARC> arc = std::make_unique<PCB_ARC>( aBoard, &sarc );
966 arc->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( width ) );
967 arc->SetLayer( klayer );
968 arc->SetNet( aBoard->FindNet( netname ) );
969
970 aBoard->Add( arc.release(), ADD_MODE::APPEND );
971 }
972 else if( row.type == wxS( "POLY" ) )
973 {
974 int layer = V3GetInt( row.inner, "layerId", 1 );
975 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
976
977 double thickness = V3GetDouble( row.inner, "width" );
978 nlohmann::json polyData = row.inner.value( "path", nlohmann::json::array() );
979
980 std::vector<std::unique_ptr<PCB_SHAPE>> results =
981 m_v2Parser.ParsePoly( aBoard, polyData, false, false );
982
983 for( auto& shape : results )
984 {
985 shape->SetLayer( klayer );
986 shape->SetWidth( PCB_IO_EASYEDAPRO_PARSER::ScaleSize( thickness ) );
987
988 aBoard->Add( shape.release(), ADD_MODE::APPEND );
989 }
990 }
991 else if( row.type == wxS( "FILL" ) )
992 {
993 int layer = V3GetInt( row.inner, "layerId", 1 );
994 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
995
996 wxString netname = V3GetString( row.inner, "netName" );
997
998 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
999
1000 if( polyDataList.is_null() || polyDataList.empty() )
1001 continue;
1002
1003 if( !polyDataList.at( 0 ).is_array() )
1004 polyDataList = nlohmann::json::array( { polyDataList } );
1005
1006 std::vector<SHAPE_LINE_CHAIN> contours;
1007
1008 for( nlohmann::json& polyData : polyDataList )
1009 {
1010 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, true );
1011 contour.SetClosed( true );
1012 contours.push_back( contour );
1013 }
1014
1015 SHAPE_POLY_SET zoneFillPoly;
1016
1017 for( SHAPE_LINE_CHAIN& contour : contours )
1018 zoneFillPoly.AddOutline( contour );
1019
1020 zoneFillPoly.RebuildHolesFromContours();
1021 zoneFillPoly.Fracture();
1022
1023 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1024
1025 zone->SetNet( aBoard->FindNet( netname ) );
1026 zone->SetLayer( klayer );
1027 zone->Outline()->Append( SHAPE_RECT( zoneFillPoly.BBox() ).Outline() );
1028 zone->SetFilledPolysList( klayer, zoneFillPoly );
1029 zone->SetAssignedPriority( 500 );
1030 zone->SetIsFilled( true );
1031 zone->SetNeedRefill( false );
1032
1033 zone->SetLocalClearance( bds.m_MinClearance );
1034 zone->SetMinThickness( bds.m_TrackMinWidth );
1035
1036 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1037 }
1038 else if( row.type == wxS( "POUR" ) )
1039 {
1040 int layer = V3GetInt( row.inner, "layerId", 1 );
1041 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1042
1043 wxString netname = V3GetString( row.inner, "netName" );
1044 int fillOrder = V3GetInt( row.inner, "order" );
1045
1046 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
1047
1048 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1049
1050 zone->SetNet( aBoard->FindNet( netname ) );
1051 zone->SetLayer( klayer );
1052 zone->SetAssignedPriority( 500 - fillOrder );
1053 zone->SetLocalClearance( bds.m_MinClearance );
1054 zone->SetMinThickness( bds.m_TrackMinWidth );
1055
1056 for( nlohmann::json& polyData : polyDataList )
1057 {
1058 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
1059 contour.SetClosed( true );
1060 zone->Outline()->Append( contour );
1061 }
1062
1063 wxASSERT( zone->Outline()->OutlineCount() == 1 );
1064
1065 poursToFill.emplace( row.id, zone.get() );
1066
1067 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1068 }
1069 else if( row.type == wxS( "POURED" ) )
1070 {
1071 nlohmann::json idArr = V3ParseIdArray( row.id );
1072 wxString parentId;
1073
1074 if( idArr.is_array() && idArr.size() > 1 )
1075 parentId = V3JsonToString( idArr[1] );
1076
1077 nlohmann::json fills = row.inner.value( "pourFill", nlohmann::json::array() );
1078
1079 if( !fills.is_array() )
1080 continue;
1081
1082 for( const nlohmann::json& fill : fills )
1083 {
1084 EASYEDAPRO::POURED poured;
1085 poured.pouredId = V3JsonToString( fill.value( "id",
1086 nlohmann::json( row.id ) ) );
1087 poured.parentId = parentId;
1088 poured.unki = V3GetInt( fill, "strokeWidth" );
1089 poured.isPoly = V3GetBool( fill, "fill" );
1090 poured.polyData = fill.value( "path", nlohmann::json::array() );
1091
1092 boardPouredMap.emplace( poured.parentId, poured );
1093 }
1094 }
1095 else if( row.type == wxS( "REGION" ) )
1096 {
1097 int layer = V3GetInt( row.inner, "layerId", 1 );
1098 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1099
1100 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
1101 nlohmann::json prohibitTypes = row.inner.value( "prohibitType",
1102 nlohmann::json::array() );
1103
1104 std::set<int> flags;
1105
1106 if( prohibitTypes.is_array() )
1107 {
1108 for( const auto& pt : prohibitTypes )
1109 {
1110 wxString ptStr;
1111
1112 if( pt.is_string() )
1113 ptStr = pt.get<std::string>();
1114
1115 if( ptStr == wxS( "COMPONENT" ) )
1116 flags.insert( 2 );
1117 else if( ptStr == wxS( "TRACK" ) )
1118 flags.insert( 5 );
1119 else if( ptStr == wxS( "COPPER" ) )
1120 flags.insert( 6 );
1121 else if( ptStr == wxS( "VIA" ) )
1122 flags.insert( 7 );
1123 else if( ptStr == wxS( "FILL" ) || ptStr == wxS( "PLANE" ) )
1124 flags.insert( 8 );
1125 }
1126 }
1127
1128 for( nlohmann::json& polyData : polyDataList )
1129 {
1130 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
1131 contour.SetClosed( true );
1132
1133 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( aBoard );
1134
1135 zone->SetIsRuleArea( true );
1136 zone->SetDoNotAllowFootprints( !!flags.count( 2 ) );
1137 zone->SetDoNotAllowZoneFills( !!flags.count( 7 ) || !!flags.count( 6 )
1138 || !!flags.count( 8 ) );
1139 zone->SetDoNotAllowPads( !!flags.count( 7 ) );
1140 zone->SetDoNotAllowTracks( !!flags.count( 7 ) || !!flags.count( 5 ) );
1141 zone->SetDoNotAllowVias( !!flags.count( 7 ) );
1142
1143 zone->SetLayer( klayer );
1144 zone->Outline()->Append( contour );
1145
1146 aBoard->Add( zone.release(), ADD_MODE::APPEND );
1147 }
1148 }
1149 else if( row.type == wxS( "STRING" ) )
1150 {
1151 aBoard->Add( createV3Text( aBoard, row ), ADD_MODE::APPEND );
1152 }
1153 else if( row.type == wxS( "COMPONENT" ) )
1154 {
1155 COMP_DATA comp;
1156 comp.layer = V3GetInt( row.inner, "layerId", 1 );
1157 comp.pos.x = V3GetDouble( row.inner, "x" );
1158 comp.pos.y = V3GetDouble( row.inner, "y" );
1159 comp.angle = V3GetDouble( row.inner, "angle" );
1160
1161 nlohmann::json attrsObj = row.inner.value( "attrs", nlohmann::json::object() );
1162
1163 if( attrsObj.is_object() )
1164 {
1165 for( auto& [k, v] : attrsObj.items() )
1166 comp.attrs[wxString( k )] = V3JsonToString( v );
1167 }
1168
1169 componentMap[row.id] = comp;
1170 }
1171 else if( row.type == wxS( "ATTR" ) )
1172 {
1173 ATTR_DATA attr;
1174 attr.parentId = V3GetString( row.inner, "parentId" );
1175 attr.layer = V3GetInt( row.inner, "layerId", 3 );
1176 attr.hasPos = !V3IsNullOrMissing( row.inner, "x" );
1177
1178 if( attr.hasPos )
1179 {
1180 attr.x = V3GetDouble( row.inner, "x" );
1181 attr.y = V3GetDouble( row.inner, "y" );
1182 }
1183
1184 attr.key = V3GetString( row.inner, "key" );
1185 attr.value = V3JsonToString( row.inner.value( "value", nlohmann::json() ) );
1186 attr.keyVisible = V3GetBool( row.inner, "keyVisible" );
1187 attr.valVisible = V3GetBool( row.inner, "valueVisible" );
1188 attr.fontName = V3GetString( row.inner, "fontFamily", wxS( "default" ) );
1189 attr.fontSize = V3GetDouble( row.inner, "fontSize", 45.0 );
1190 attr.strokeWidth = V3GetDouble( row.inner, "strokeWidth", 6.0 );
1191 attr.origin = V3GetString( row.inner, "origin", wxS( "LEFT_BOTTOM" ) );
1192 attr.angle = V3GetDouble( row.inner, "angle" );
1193 attr.inverted = V3GetBool( row.inner, "reverse" ) ? 1 : 0;
1194 attr.mirror = V3GetBool( row.inner, "mirror" ) ? 1 : 0;
1195
1196 attrMap.emplace( attr.parentId, attr );
1197 }
1198 else if( row.type == wxS( "PAD_NET" ) )
1199 {
1200 nlohmann::json idArr = V3ParseIdArray( row.id );
1201
1202 PAD_NET_DATA pn;
1203 pn.compId = idArr.is_array() && idArr.size() > 1
1204 ? V3JsonToString( idArr[1] )
1205 : wxString();
1206 pn.padNumber = idArr.is_array() && idArr.size() > 2
1207 ? V3JsonToString( idArr[2] )
1208 : wxString();
1209 pn.padNet = V3GetString( row.inner, "padNet" );
1210
1211 if( !pn.compId.IsEmpty() )
1212 padNetMap.emplace( pn.compId, pn );
1213 }
1214 else if( row.type == wxS( "PAD" ) )
1215 {
1216 wxString netname = V3GetString( row.inner, "netName" );
1217
1218 std::unique_ptr<FOOTPRINT> footprint =
1219 std::make_unique<FOOTPRINT>( aBoard );
1220 std::unique_ptr<PAD> pad = createV3PAD( footprint.get(), row );
1221
1222 pad->SetNet( aBoard->FindNet( netname ) );
1223
1224 VECTOR2I pos = pad->GetPosition();
1225 EDA_ANGLE orient = pad->GetOrientation();
1226
1227 pad->SetPosition( VECTOR2I() );
1228 pad->SetOrientation( ANGLE_0 );
1229
1230 footprint->Add( pad.release(), ADD_MODE::APPEND );
1231 footprint->SetPosition( pos );
1232 footprint->SetOrientation( orient );
1233
1234 wxString fpName = wxS( "Pad_" ) + row.id;
1235 LIB_ID fpID = EASYEDAPRO::ToKiCadLibID( wxEmptyString, fpName );
1236
1237 footprint->SetFPID( fpID );
1238 footprint->Reference().SetVisible( true );
1239 footprint->Value().SetVisible( true );
1240 footprint->AutoPositionFields();
1241
1242 aBoard->Add( footprint.release(), ADD_MODE::APPEND );
1243 }
1244 else if( row.type == wxS( "IMAGE" ) )
1245 {
1246 int layer = V3GetInt( row.inner, "layerId", 1 );
1247 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1248
1249 VECTOR2D start;
1250 start.x = V3GetDouble( row.inner, "startX" );
1251 start.y = V3GetDouble( row.inner, "startY" );
1252
1253 VECTOR2D size;
1254 size.x = V3GetDouble( row.inner, "width" );
1255 size.y = V3GetDouble( row.inner, "height" );
1256
1257 double angle = V3GetDouble( row.inner, "angle" );
1258 bool mirror = V3GetBool( row.inner, "mirror" );
1259 nlohmann::json polyDataList = row.inner.value( "path", nlohmann::json::array() );
1260
1261 BOX2I bbox;
1262 std::vector<SHAPE_LINE_CHAIN> contours;
1263 for( nlohmann::json& polyData : polyDataList )
1264 {
1265 SHAPE_LINE_CHAIN contour = m_v2Parser.ParseContour( polyData, false );
1266 contour.SetClosed( true );
1267
1268 contours.push_back( contour );
1269
1270 bbox.Merge( contour.BBox() );
1271 }
1272
1273 if( bbox.GetSize().x == 0 || bbox.GetSize().y == 0 )
1274 continue;
1275
1278
1279 SHAPE_POLY_SET polySet;
1280
1281 for( SHAPE_LINE_CHAIN& contour : contours )
1282 {
1283 for( int i = 0; i < contour.PointCount(); i++ )
1284 {
1285 VECTOR2I pt = contour.CPoint( i );
1286 contour.SetPoint( i, VECTOR2I( pt.x * scale.x, pt.y * scale.y ) );
1287 }
1288
1289 polySet.AddOutline( contour );
1290 }
1291
1292 polySet.RebuildHolesFromContours();
1293
1294 std::unique_ptr<PCB_GROUP> group;
1295
1296 if( polySet.OutlineCount() > 1 )
1297 group = std::make_unique<PCB_GROUP>( aBoard );
1298
1299 BOX2I polyBBox = polySet.BBox();
1300
1301 for( const SHAPE_POLY_SET::POLYGON& poly : polySet.CPolygons() )
1302 {
1303 std::unique_ptr<PCB_SHAPE> shape = std::make_unique<PCB_SHAPE>( aBoard, SHAPE_T::POLY );
1304
1305 shape->SetFilled( true );
1306 shape->SetPolyShape( poly );
1307 shape->SetLayer( klayer );
1308 shape->SetWidth( 0 );
1309
1310 shape->Move( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ) - polyBBox.GetOrigin() );
1311 shape->Rotate( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
1312
1313 if( IsBackLayer( klayer ) ^ mirror )
1314 {
1316 shape->Mirror( PCB_IO_EASYEDAPRO_PARSER::ScalePos( start ), flipDirection );
1317 }
1318
1319 if( group )
1320 group->AddItem( shape.get() );
1321
1322 aBoard->Add( shape.release(), ADD_MODE::APPEND );
1323 }
1324
1325 if( group )
1326 aBoard->Add( group.release(), ADD_MODE::APPEND );
1327 }
1328 else if( row.type == wxS( "OBJ" ) )
1329 {
1330 int layer = V3GetInt( row.inner, "layerId", 1 );
1331 PCB_LAYER_ID klayer = m_v2Parser.LayerToKi( layer );
1332
1333 VECTOR2D start;
1334 start.x = V3GetDouble( row.inner, "startX" );
1335 start.y = V3GetDouble( row.inner, "startY" );
1336
1337 VECTOR2D size;
1338 size.x = V3GetDouble( row.inner, "width" );
1339 size.y = V3GetDouble( row.inner, "height" );
1340
1341 double angle = V3GetDouble( row.inner, "angle" );
1342 bool mirror = V3GetBool( row.inner, "mirror" );
1343 wxString imageUrl = V3GetString( row.inner, "path" );
1344
1345 if( imageUrl.empty() )
1346 continue;
1347
1348 wxString mimeType, base64Data;
1349
1350 if( imageUrl.BeforeFirst( ':' ) == wxS( "blob" ) )
1351 {
1352 wxString objectId = imageUrl.AfterLast( ':' );
1353
1354 if( auto blob = get_opt( aBlobMap, objectId ) )
1355 {
1356 wxString blobUrl = blob->url;
1357
1358 if( blobUrl.BeforeFirst( ':' ) == wxS( "data" ) )
1359 {
1360 wxArrayString paramsArr =
1361 wxSplit( blobUrl.AfterFirst( ':' ).BeforeFirst( ',' ), ';', '\0' );
1362
1363 base64Data = blobUrl.AfterFirst( ',' );
1364
1365 if( paramsArr.size() > 0 )
1366 mimeType = paramsArr[0];
1367 }
1368 }
1369 }
1370
1373
1374 if( mimeType.empty() || base64Data.empty() )
1375 continue;
1376
1377 wxMemoryBuffer buf = wxBase64Decode( base64Data );
1378
1379 if( mimeType == wxS( "image/svg+xml" ) )
1380 {
1381 // Not yet supported by EasyEDA
1382 }
1383 else
1384 {
1385 VECTOR2D kcenter = kstart + ksize / 2;
1386
1387 std::unique_ptr<PCB_REFERENCE_IMAGE> bitmap =
1388 std::make_unique<PCB_REFERENCE_IMAGE>( aBoard, kcenter, klayer );
1389 REFERENCE_IMAGE& refImage = bitmap->GetReferenceImage();
1390
1391 wxImage::SetDefaultLoadFlags( wxImage::GetDefaultLoadFlags()
1392 & ~wxImage::Load_Verbose );
1393
1394 if( refImage.ReadImageFile( buf ) )
1395 {
1396 double scaleFactor = PCB_IO_EASYEDAPRO_PARSER::ScaleSize( size.x ) / refImage.GetSize().x;
1397 refImage.SetImageScale( scaleFactor );
1398
1399 // TODO: support non-90-deg angles
1400 bitmap->Rotate( kstart, EDA_ANGLE( angle, DEGREES_T ) );
1401
1402 if( mirror )
1403 {
1404 int x = bitmap->GetPosition().x;
1405 MIRROR( x, KiROUND( kstart.x ) );
1406 bitmap->SetX( x );
1407
1409 }
1410
1411 aBoard->Add( bitmap.release(), ADD_MODE::APPEND );
1412 }
1413 }
1414 }
1415 } // end first pass
1416
1417 // Second pass: place components
1418 for( auto const& [compId, comp] : componentMap )
1419 {
1420 wxString deviceId;
1421 wxString fpIdOverride;
1422 wxString fpDesignator;
1423
1424 // Collect attrs for this component
1425 auto attrRange = attrMap.equal_range( compId );
1426
1427 for( auto it = attrRange.first; it != attrRange.second; ++it )
1428 {
1429 const ATTR_DATA& attr = it->second;
1430
1431 if( attr.key == wxS( "Device" ) )
1432 deviceId = attr.value;
1433 else if( attr.key == wxS( "Footprint" ) )
1434 fpIdOverride = attr.value;
1435 else if( attr.key == wxS( "Designator" ) )
1436 fpDesignator = attr.value;
1437 }
1438
1439 // BOM / identity / 3D fields come from the component's Device ATTR.
1440 EASYEDAPRO::V3_DEVICE_DATA deviceData = GetV3DeviceData( aProject, deviceId );
1441 const std::map<wxString, wxString>& deviceAttrs = deviceData.attributes;
1442
1443 wxString fpId = fpIdOverride;
1444
1445 if( fpId.empty() )
1446 fpId = get_def( deviceAttrs, wxS( "Footprint" ), wxEmptyString );
1447
1448 if( fpId.empty() )
1449 {
1450 wxLogTrace( traceEasyEdaIo, wxT( "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 wxLogTrace( traceEasyEdaIo, "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:383
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:343
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:2777
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:2510
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.
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)...
const wxChar *const traceEasyEdaIo
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
wxLogTrace helper definitions.
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