KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sprint_layout_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 * Binary format knowledge derived from:
24 * https://github.com/sergey-raevskiy/xlay (lay6.h)
25 * https://github.com/OpenBoardView/OpenBoardView (LAYFile.cpp)
26 */
27
29
30#include <board.h>
33#include <footprint.h>
34#include <netinfo.h>
35#include <pad.h>
36#include <pcb_shape.h>
37#include <pcb_text.h>
38#include <zone.h>
40#include <math/util.h>
41#include <font/fontconfig.h>
42
43#include <wx/filename.h>
44#include <wx/wfstream.h>
45#include <wx/log.h>
46#include <wx/strconv.h>
47#include <wx/fontenc.h>
48
49#include <algorithm>
50#include <cmath>
51#include <cstring>
52#include <limits>
53
54// All multi-byte reads below decode little-endian explicitly,
55// so this parser works correctly on any host byte order.
56
57static constexpr uint32_t MAX_OBJECTS = 1000000;
58static constexpr uint32_t MAX_GROUPS = 100000;
59static constexpr uint32_t MAX_CHILDREN = 10000;
60static constexpr uint32_t MAX_POINTS = 1000000;
61
62
64
65
67
68
69// ============================================================================
70// Binary reading helpers
71// ============================================================================
72
74{
75 if( m_pos + 1 > m_end )
76 THROW_IO_ERROR( _( "Unexpected end of Sprint Layout file" ) );
77
78 return *m_pos++;
79}
80
81
83{
84 if( m_pos + 2 > m_end )
85 THROW_IO_ERROR( _( "Unexpected end of Sprint Layout file" ) );
86
87 uint16_t v = static_cast<uint16_t>( m_pos[0] )
88 | ( static_cast<uint16_t>( m_pos[1] ) << 8 );
89 m_pos += 2;
90 return v;
91}
92
93
95{
96 if( m_pos + 4 > m_end )
97 THROW_IO_ERROR( _( "Unexpected end of Sprint Layout file" ) );
98
99 uint32_t v = static_cast<uint32_t>( m_pos[0] )
100 | ( static_cast<uint32_t>( m_pos[1] ) << 8 )
101 | ( static_cast<uint32_t>( m_pos[2] ) << 16 )
102 | ( static_cast<uint32_t>( m_pos[3] ) << 24 );
103 m_pos += 4;
104 return v;
105}
106
107
109{
110 return static_cast<int32_t>( readUint32() );
111}
112
113
115{
116 uint32_t bits = readUint32();
117 float v;
118 std::memcpy( &v, &bits, sizeof( v ) );
119 return v;
120}
121
122
124{
125 if( m_pos + 8 > m_end )
126 THROW_IO_ERROR( _( "Unexpected end of Sprint Layout file" ) );
127
128 uint64_t bits = static_cast<uint64_t>( m_pos[0] )
129 | ( static_cast<uint64_t>( m_pos[1] ) << 8 )
130 | ( static_cast<uint64_t>( m_pos[2] ) << 16 )
131 | ( static_cast<uint64_t>( m_pos[3] ) << 24 )
132 | ( static_cast<uint64_t>( m_pos[4] ) << 32 )
133 | ( static_cast<uint64_t>( m_pos[5] ) << 40 )
134 | ( static_cast<uint64_t>( m_pos[6] ) << 48 )
135 | ( static_cast<uint64_t>( m_pos[7] ) << 56 );
136 m_pos += 8;
137
138 double v;
139 std::memcpy( &v, &bits, sizeof( v ) );
140 return v;
141}
142
143
144std::string SPRINT_LAYOUT_PARSER::readFixedString( size_t aMaxLen )
145{
146 size_t rawLen = readUint8();
147 size_t len = std::min( rawLen, aMaxLen );
148
149 if( m_pos + aMaxLen > m_end )
150 THROW_IO_ERROR( _( "Unexpected end of Sprint Layout file" ) );
151
152 std::string s( reinterpret_cast<const char*>( m_pos ), len );
153 m_pos += aMaxLen;
154 return s;
155}
156
157
159{
160 uint32_t len = readUint32();
161
162 if( len > 100000 )
163 THROW_IO_ERROR( _( "Invalid string length in Sprint Layout file" ) );
164
165 if( m_pos + len > m_end )
166 THROW_IO_ERROR( _( "Unexpected end of Sprint Layout file" ) );
167
168 std::string s( reinterpret_cast<const char*>( m_pos ), len );
169 m_pos += len;
170 return s;
171}
172
173
174void SPRINT_LAYOUT_PARSER::skip( size_t aBytes )
175{
176 if( m_pos + aBytes > m_end )
177 THROW_IO_ERROR( _( "Unexpected end of Sprint Layout file" ) );
178
179 m_pos += aBytes;
180}
181
182
183// ============================================================================
184// Parsing
185// ============================================================================
186
187bool SPRINT_LAYOUT_PARSER::Parse( const wxString& aFileName )
188{
189 wxFFileInputStream stream( aFileName );
190
191 if( !stream.IsOk() )
192 THROW_IO_ERROR( wxString::Format( _( "Cannot open file '%s'" ), aFileName ) );
193
194 size_t fileSize = stream.GetLength();
195
196 if( fileSize < 8 )
197 THROW_IO_ERROR( wxString::Format( _( "File '%s' is too small to be a Sprint Layout file" ), aFileName ) );
198
199 m_buffer.resize( fileSize );
200 stream.Read( m_buffer.data(), fileSize );
201
202 if( stream.LastRead() != fileSize )
203 THROW_IO_ERROR( wxString::Format( _( "Failed to read file '%s'" ), aFileName ) );
204
205 m_start = m_buffer.data();
206 m_pos = m_start;
207 m_end = m_start + fileSize;
208
209 // File header: version + magic bytes (0x33, 0xAA, 0xFF)
210 m_fileData.version = readUint8();
211 uint8_t magic1 = readUint8();
212 uint8_t magic2 = readUint8();
213 uint8_t magic3 = readUint8();
214
215 if( m_fileData.version > 6 || magic1 != 0x33 || magic2 != 0xAA || magic3 != 0xFF )
216 THROW_IO_ERROR( _( "Invalid Sprint Layout file header" ) );
217
218 uint32_t numBoards = readUint32();
219
220 if( numBoards == 0 || numBoards > 100 )
221 THROW_IO_ERROR( _( "Invalid board count in Sprint Layout file" ) );
222
223 m_fileData.boards.resize( numBoards );
224
225 for( uint32_t b = 0; b < numBoards; b++ )
226 {
227 parseBoardHeader( m_fileData.boards[b] );
228
229 uint32_t numConnections = 0;
230
231 for( auto& obj : m_fileData.boards[b].objects )
232 {
233 if( obj.type == SPRINT_LAYOUT::OBJ_THT_PAD || obj.type == SPRINT_LAYOUT::OBJ_SMD_PAD )
234 numConnections++;
235 }
236
237 // Read connection records (one per pad object)
238 for( uint32_t c = 0; c < numConnections; c++ )
239 {
240 uint32_t connCount = readUint32();
241
242 // Skip the connection data for now
243 skip( connCount * sizeof( uint32_t ) );
244 }
245 }
246
247 parseTrailer();
248
249 return true;
250}
251
252
254{
255 // Board name (Pascal string, 30 bytes max)
256 aBoard.name = readFixedString( 30 );
257
258 // Unknown padding
259 skip( 4 );
260
261 aBoard.size_x = readUint32();
262 aBoard.size_y = readUint32();
263
264 // Ground plane enabled flag per layer (C1, S1, C2, S2, I1, I2, O)
265 for( int i = 0; i < 7; i++ )
266 aBoard.ground_plane[i] = readUint8();
267
268 // Grid and viewport (not needed for import)
269 readDouble(); // active_grid_val
270 readDouble(); // zoom
271 readUint32(); // viewport_offset_x
272 readUint32(); // viewport_offset_y
273
274 // Active layer + padding
275 skip( 4 );
276
277 // Layer visibility + scanned copy flags
278 skip( 7 ); // layer_visible[7]
279 skip( 1 ); // show_scanned_copy_top
280 skip( 1 ); // show_scanned_copy_bottom
281
282 // Scanned copy paths
283 readFixedString( 200 );
284 readFixedString( 200 );
285
286 // DPI and shift values for scanned copies
287 skip( 4 * 6 ); // dpi_top, dpi_bottom, shiftx/y_top, shiftx/y_bottom
288
289 // Unknown fields
290 skip( 4 * 2 );
291
292 aBoard.center_x = readInt32();
293 aBoard.center_y = readInt32();
294
295 aBoard.is_multilayer = readUint8();
296
297 uint32_t numObjects = readUint32();
298
299 if( numObjects > MAX_OBJECTS )
300 THROW_IO_ERROR( _( "Too many objects in Sprint Layout board" ) );
301
302 aBoard.objects.resize( numObjects );
303
304 for( uint32_t i = 0; i < numObjects; i++ )
305 parseObject( aBoard.objects[i] );
306}
307
308
310{
311 aObj.type = readUint8();
312
313 // Type 0 entries are deleted objects with zeroed type bytes. The original
314 // application has no special case for type 0 either -- its signed group-count
315 // loop and broad exception handler mask the parse failures. The 117-byte fixed
316 // size (1 type + 116 data) was validated against ku14194revb.lay6 (10 type-0
317 // objects, 0 bytes remaining).
318 if( aObj.type == 0 )
319 {
320 skip( 116 );
321 return;
322 }
323
327 {
328 THROW_IO_ERROR( wxString::Format( _( "Unknown object type %d in Sprint Layout file" ),
329 aObj.type ) );
330 }
331
332 aObj.x = readFloat();
333 aObj.y = readFloat();
334 aObj.outer = readFloat();
335 aObj.inner = readFloat();
336 aObj.line_width = readUint32();
337 skip( 1 ); // padding
338 aObj.layer = readUint8();
339 aObj.tht_shape = readUint8();
340 skip( 4 ); // padding
341 aObj.component_id = readUint16();
342 skip( 1 ); // selected
343 aObj.start_angle = readInt32(); // also th_style[4]
344 skip( 5 ); // unknown
345 aObj.filled = readUint8();
346 aObj.clearance = readInt32();
347 skip( 5 ); // unknown
348 aObj.thermal_width = readUint8();
349 aObj.mirror = readUint8();
350 aObj.keepout = readUint8();
351 aObj.rotation = readInt32(); // thzise
352 aObj.plated = readUint8();
353 aObj.soldermask = readUint8();
354 skip( 18 ); // unknown padding
355
356 // Variable-length data after header
357 if( !aIsTextChild )
358 {
359 aObj.text = readVarString();
360 aObj.net_name = readVarString();
361
362 uint32_t groupCount = readUint32();
363
364 if( groupCount > MAX_GROUPS )
365 THROW_IO_ERROR( _( "Too many groups in Sprint Layout object" ) );
366
367 aObj.groups.resize( groupCount );
368
369 for( uint32_t i = 0; i < groupCount; i++ )
370 aObj.groups[i] = readUint32();
371 }
372
373 switch( aObj.type )
374 {
376 // Circles have no points list
377 return;
378
380 {
381 uint32_t childCount = readUint32();
382
383 if( childCount > MAX_CHILDREN )
384 THROW_IO_ERROR( _( "Too many text children in Sprint Layout object" ) );
385
386 aObj.text_children.resize( childCount );
387
388 for( uint32_t i = 0; i < childCount; i++ )
389 parseObject( aObj.text_children[i], true );
390
391 // Component data follows for text objects that define a component
392 if( aObj.tht_shape == 1 )
393 {
394 aObj.component.valid = true;
395 aObj.component.off_x = readFloat();
396 aObj.component.off_y = readFloat();
401 aObj.component.use = readUint8();
402 }
403
404 return;
405 }
406
407 default:
408 break;
409 }
410
411 // Points list for pads, lines, and polygons
412 uint32_t pointCount = readUint32();
413
414 if( pointCount > MAX_POINTS )
415 THROW_IO_ERROR( _( "Too many points in Sprint Layout object" ) );
416
417 aObj.points.resize( pointCount );
418
419 for( uint32_t i = 0; i < pointCount; i++ )
420 {
421 aObj.points[i].x = readFloat();
422 aObj.points[i].y = readFloat();
423 }
424}
425
426
428{
429 readUint32(); // active_board_tab
430 m_fileData.project_name = readFixedString( 100 );
431 m_fileData.project_author = readFixedString( 100 );
432 m_fileData.project_company = readFixedString( 100 );
433 m_fileData.project_comment = readVarString();
434}
435
436
437// ============================================================================
438// Board construction
439// ============================================================================
440
441PCB_LAYER_ID SPRINT_LAYOUT_PARSER::mapLayer( uint8_t aSprintLayer ) const
442{
443 switch( aSprintLayer )
444 {
445 case SPRINT_LAYOUT::LAYER_C1: return F_Cu;
446 case SPRINT_LAYOUT::LAYER_S1: return F_SilkS;
447 case SPRINT_LAYOUT::LAYER_C2: return B_Cu;
448 case SPRINT_LAYOUT::LAYER_S2: return B_SilkS;
449 case SPRINT_LAYOUT::LAYER_I1: return In1_Cu;
450 case SPRINT_LAYOUT::LAYER_I2: return In2_Cu;
452 default: return F_Cu;
453 }
454}
455
456
458{
459 // Sprint Layout 6 uses 1/10000 mm
460 // Older versions seem to use 1/100 mm
461 // KiCad uses nanometers (1 nm = 1e-6 mm)
462 double nm;
463
464 if( m_fileData.version >= 6 )
465 nm = static_cast<double>( aValue ) * 100.0; // 100 nm
466 else
467 nm = static_cast<double>( aValue ) * 10000.0; // 10 um
468
469 if( nm > std::numeric_limits<int>::max() || nm < std::numeric_limits<int>::min() )
470 THROW_IO_ERROR( _( "Coordinate value out of range in Sprint Layout file" ) );
471
472 return KiROUND( nm );
473}
474
475
477{
478 // Sprint Layout uses Y-up (mathematical), KiCad uses Y-down (screen)
479 return VECTOR2I( sprintToKicadCoord( aX ), sprintToKicadCoord( -aY ) );
480}
481
482
483wxString SPRINT_LAYOUT_PARSER::convertString( const std::string& aStr ) const
484{
485 static wxCSConv convCP1251( wxFONTENCODING_CP1251 );
486
487 if( aStr.empty() )
488 return wxEmptyString;
489
490 wxString ret = wxString::FromUTF8( aStr );
491
492 if( ret.empty() && convCP1251.IsOk() )
493 ret = wxString( aStr.c_str(), convCP1251 );
494
495 return ret;
496}
497
498
499BOARD* SPRINT_LAYOUT_PARSER::CreateBoard( std::map<wxString, std::unique_ptr<FOOTPRINT>>& aFootprintMap,
500 size_t aBoardIndex )
501{
502 if( aBoardIndex >= m_fileData.boards.size() )
503 return nullptr;
504
505 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
506
507 // Set up copper layers based on whether inner layers are used
508 const SPRINT_LAYOUT::BOARD_DATA& boardData = m_fileData.boards[aBoardIndex];
509 bool hasInnerLayers = false;
510
511 for( const auto& obj : boardData.objects )
512 {
513 if( obj.layer == SPRINT_LAYOUT::LAYER_I1 || obj.layer == SPRINT_LAYOUT::LAYER_I2 )
514 {
515 hasInnerLayers = true;
516 break;
517 }
518 }
519
520 if( hasInnerLayers || boardData.is_multilayer )
521 board->SetCopperLayerCount( 4 );
522 else
523 board->SetCopperLayerCount( 2 );
524
525 // Maps component_id to FOOTPRINT for grouping component-owned objects
526 std::map<uint16_t, FOOTPRINT*> componentMap;
527 std::vector<std::vector<VECTOR2I>> outlineSegments;
528
529 auto getOrCreateComponentFootprint = [&]( const SPRINT_LAYOUT::OBJECT& aObj ) -> FOOTPRINT*
530 {
531 if( aObj.component_id == 0 )
532 return nullptr;
533
534 auto it = componentMap.find( aObj.component_id );
535
536 if( it != componentMap.end() )
537 return it->second;
538
539 FOOTPRINT* fp = new FOOTPRINT( board.get() );
540
541 if( aObj.type == SPRINT_LAYOUT::OBJ_TEXT && !aObj.text.empty() )
542 {
543 fp->SetReference( convertString( aObj.text ) );
544 }
545 else
546 {
547 fp->SetReference( wxString::Format( wxS( "U%d" ), aObj.component_id ) );
548
549 for( PCB_FIELD* fd : fp->GetFields() )
550 fd->SetVisible( false );
551 }
552
553 if( aObj.type == SPRINT_LAYOUT::OBJ_TEXT && aObj.component.valid )
554 {
555 if( !aObj.component.comment.empty() )
556 fp->GetField( FIELD_T::DESCRIPTION )->SetText( convertString( aObj.component.comment ) );
557
558 if( !aObj.component.package.empty() )
559 fp->SetLibDescription( convertString( aObj.component.package ) );
560
561 fp->SetOrientationDegrees( aObj.component.rotation );
562 }
563
564 PCB_LAYER_ID layer = mapLayer( aObj.layer );
565 fp->SetLayer( ( layer == B_Cu || layer == B_SilkS ) ? B_Cu : F_Cu );
566
567 componentMap[aObj.component_id] = fp;
568 board->Add( fp );
569 return fp;
570 };
571
572 // First pass: create footprints from component text records where available
573 for( const auto& obj : boardData.objects )
574 {
575 if( obj.type == SPRINT_LAYOUT::OBJ_TEXT && obj.component_id > 0 && obj.component.valid )
576 getOrCreateComponentFootprint( obj );
577 }
578
579 // Second pass: process all objects in board/footprint context
580 for( const auto& obj : boardData.objects )
581 {
582 BOARD_ITEM_CONTAINER* container = board.get();
583
584 if( FOOTPRINT* fp = getOrCreateComponentFootprint( obj ) )
585 container = fp;
586
587 switch( obj.type )
588 {
591 processPad( container, obj );
592 break;
593
595 processLine( container, obj, outlineSegments );
596 break;
597
599 processPoly( container, obj, outlineSegments );
600 break;
601
603 processCircle( container, obj, outlineSegments );
604 break;
605
607 processText( container, obj );
608 break;
609
610 default:
611 break;
612 }
613 }
614
615 // Re-anchor footprints after all elements are added.
616 for( FOOTPRINT* fp : board->Footprints() )
617 {
618 BOX2I fpBbox = fp->GetBoundingHull().BBox();
619
620 VECTOR2I anchor = fpBbox.GetCenter();
621 fp->SetPosition( anchor );
622
623 VECTOR2I anchorShift( -anchor.x, -anchor.y );
624 RotatePoint( anchorShift, fp->GetOrientation() );
625 fp->MoveAnchorPosition( anchorShift );
626 }
627
628 for( const auto& [ componentId, fp ] : componentMap )
629 {
630 wxString fpKey = wxString::Format( wxS( "SprintLayout_%s" ), fp->GetReference() );
631 FOOTPRINT* fpCopy = static_cast<FOOTPRINT*>( fp->Clone() );
632 fpCopy->SetParent( nullptr );
633 aFootprintMap[fpKey] = std::unique_ptr<FOOTPRINT>( fpCopy );
634 }
635
636 buildOutline( board.get(), outlineSegments, boardData );
637
638 // Create ground plane zones for layers where ground plane is enabled.
639 // Sprint Layout stores a per-layer flag in the board header.
640 // Indices: 0=C1(F.Cu), 2=C2(B.Cu), 4=I1(In1.Cu), 5=I2(In2.Cu)
641 static const struct
642 {
643 int index;
644 PCB_LAYER_ID layer;
645 } groundPlaneMap[] = {
646 { 0, F_Cu },
647 { 2, B_Cu },
648 { 4, In1_Cu },
649 { 5, In2_Cu },
650 };
651
652 for( const auto& gp : groundPlaneMap )
653 {
654 if( boardData.ground_plane[gp.index] == 0 )
655 continue;
656
657 int w = sprintToKicadCoord( static_cast<float>( boardData.size_x ) );
658 int h = sprintToKicadCoord( static_cast<float>( boardData.size_y ) );
659
660 if( w <= 0 || h <= 0 )
661 continue;
662
663 ZONE* zone = new ZONE( board.get() );
664 zone->SetLayer( gp.layer );
665 zone->SetIsRuleArea( false );
666 zone->SetZoneName( wxString::Format( wxS( "GND_PLANE_%s" ),
667 board->GetLayerName( gp.layer ) ) );
668 zone->SetLocalClearance( std::optional<int>( pcbIUScale.mmToIU( 0.3 ) ) );
669 zone->SetThermalReliefGap( pcbIUScale.mmToIU( 0.5 ) );
670 zone->SetThermalReliefSpokeWidth( pcbIUScale.mmToIU( 0.5 ) );
671 zone->SetAssignedPriority( 0 );
672
673 SHAPE_POLY_SET outline;
674 outline.NewOutline();
675 outline.Append( 0, 0 );
676 outline.Append( w, 0 );
677 outline.Append( w, h );
678 outline.Append( 0, h );
679
680 zone->AddPolygon( outline.COutline( 0 ) );
681 board->Add( zone );
682 }
683
684 // Center the board content on the page
685 BOX2I bbox = board->ComputeBoundingBox( true );
686
687 if( bbox.GetWidth() > 0 && bbox.GetHeight() > 0 )
688 {
689 VECTOR2I pageSize = board->GetPageSettings().GetSizeIU( pcbIUScale.IU_PER_MILS );
690 VECTOR2I centerOffset = VECTOR2I( pageSize.x / 2, pageSize.y / 2 ) - bbox.GetCenter();
691
692 for( FOOTPRINT* fp : board->Footprints() )
693 fp->Move( centerOffset );
694
695 for( ZONE* zone : board->Zones() )
696 zone->Move( centerOffset );
697
698 for( BOARD_ITEM* item : board->Drawings() )
699 item->Move( centerOffset );
700 }
701
702 return board.release();
703}
704
705
707{
708 BOARD* board = aContainer ? aContainer->GetBoard() : nullptr;
709 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aContainer );
710
711 if( !fp )
712 {
713 // Standalone pad without a component gets its own footprint
714 fp = new FOOTPRINT( board );
715 fp->SetReference( wxString::Format( wxS( "PAD%d" ),
716 static_cast<int>( board->Footprints().size() ) ) );
717 fp->Reference().SetVisible( false );
718 fp->SetLayer( F_Cu );
719 aContainer->Add( fp );
720 }
721
722 PAD* pad = new PAD( fp );
723
724 // SMD pad x,y may be a component-relative offset rather than an absolute
725 // position (depends on the Sprint Layout version that created the file).
726 // The points array always stores absolute coordinates, so derive the pad
727 // center from the points when available.
728 VECTOR2I pos;
729
730 if( aObj.type == SPRINT_LAYOUT::OBJ_SMD_PAD && !aObj.points.empty() )
731 {
732 double cx = 0, cy = 0;
733
734 for( const auto& pt : aObj.points )
735 {
736 cx += pt.x;
737 cy += pt.y;
738 }
739
740 cx /= static_cast<double>( aObj.points.size() );
741 cy /= static_cast<double>( aObj.points.size() );
742 pos = sprintToKicadPos( static_cast<float>( cx ), static_cast<float>( cy ) );
743 }
744 else
745 {
746 pos = sprintToKicadPos( aObj.x, aObj.y );
747 }
748
749 pad->SetPosition( pos );
750
751 if( aObj.type == SPRINT_LAYOUT::OBJ_THT_PAD )
752 {
753 PCB_LAYER_ID padLayer = mapLayer( aObj.layer );
754
755 if( aObj.plated == 0 )
756 {
757 pad->SetAttribute( PAD_ATTRIB::NPTH );
758
759 if( padLayer == B_Cu || padLayer == B_SilkS )
760 {
761 pad->SetLayerSet( LSET( { B_Cu, B_Mask } ) );
762 fp->SetLayer( B_Cu );
763 }
764 else
765 {
766 pad->SetLayerSet( LSET( { F_Cu, F_Mask } ) );
767 }
768 }
769 else
770 {
771 pad->SetAttribute( PAD_ATTRIB::PTH );
772 pad->SetLayerSet( PAD::PTHMask() );
773 }
774
775 int outerDia = sprintToKicadCoord( aObj.outer * 2.0f );
776 int drillDia = sprintToKicadCoord( aObj.inner * 2.0f );
777
778 VECTOR2I padSize( outerDia, outerDia );
779 VECTOR2I drillSize( drillDia, drillDia );
780
781 switch( aObj.tht_shape )
782 {
786 padSize.x *= 2;
787 break;
788
792 padSize.y *= 2;
793 break;
794
795 default: break;
796 }
797
798 pad->SetSize( PADSTACK::ALL_LAYERS, padSize );
799 pad->SetDrillSize( drillSize );
800
801 switch( aObj.tht_shape )
802 {
805 break;
806
810 break;
811
816 pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, 0.25 );
817 pad->SetChamferPositions( PADSTACK::ALL_LAYERS,
819 break;
820
825 break;
826
827 default:
829 break;
830 }
831
832 // The start_angle field is a union with th_style[4] for pad objects.
833 // Each byte is the thermal style for one copper layer (C1, C2, I1, I2).
834 // 0=direct/full, 1=thermal relief, 2=no connection
835 uint8_t thStyle = aObj.start_angle & 0xFF;
836
837 if( thStyle == 0 )
838 pad->SetLocalZoneConnection( ZONE_CONNECTION::FULL );
839 else if( thStyle == 2 )
840 pad->SetLocalZoneConnection( ZONE_CONNECTION::NONE );
841 else
842 pad->SetLocalZoneConnection( ZONE_CONNECTION::THERMAL );
843 }
844 else
845 {
846 pad->SetAttribute( PAD_ATTRIB::SMD );
847
848 PCB_LAYER_ID padLayer = mapLayer( aObj.layer );
849
850 if( padLayer == B_Cu || padLayer == B_SilkS )
851 {
852 pad->SetLayerSet( LSET( { B_Cu, B_Paste, B_Mask } ) );
853 fp->SetLayer( B_Cu );
854 }
855 else
856 {
857 pad->SetLayerSet( PAD::SMDMask() );
858 }
859
860 int width = sprintToKicadCoord( aObj.outer * 2.0f );
861 int height = sprintToKicadCoord( aObj.inner * 2.0f );
862
863 if( height <= 0 )
864 height = width;
865
866 pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( width, height ) );
867
870 else
872 }
873
874 // Solder mask: soldermask==0 means no mask opening (pad is tented/covered)
875 if( aObj.soldermask == 0 )
876 {
877 pad->Padstack().FrontOuterLayers().has_solder_mask = false;
878 pad->Padstack().BackOuterLayers().has_solder_mask = false;
879 }
880
881 // Per-element ground plane clearance
882 if( aObj.clearance > 0 )
883 {
884 int clearance = sprintToKicadCoord( static_cast<float>( aObj.clearance ) );
885 pad->SetLocalClearance( std::optional<int>( clearance ) );
886 }
887
888 // Thermal spoke width
889 if( aObj.thermal_width > 0 )
890 {
891 int spokeWidth = sprintToKicadCoord( static_cast<float>( aObj.thermal_width ) );
892 pad->SetLocalThermalSpokeWidthOverride( std::optional<int>( spokeWidth ) );
893 }
894
895 // Set net name
896 if( !aObj.net_name.empty() )
897 {
898 wxString netName = convertString( aObj.net_name );
899 NETINFO_ITEM* net = board->FindNet( netName );
900
901 if( !net )
902 {
903 net = new NETINFO_ITEM( board, netName );
904 board->Add( net );
905 }
906
907 pad->SetNet( net );
908 }
909
910 pad->SetNumber( wxString::Format( wxS( "%d" ),
911 static_cast<int>( fp->Pads().size() + 1 ) ) );
912
913 fp->Add( pad );
914}
915
916
918 std::vector<std::vector<VECTOR2I>>& aOutlineSegments )
919{
920 if( aObj.points.size() < 2 )
921 return;
922
923 PCB_LAYER_ID layer = mapLayer( aObj.layer );
924
925 if( layer == Edge_Cuts )
926 {
927 std::vector<VECTOR2I> segment;
928
929 for( const auto& pt : aObj.points )
930 segment.push_back( sprintToKicadPos( pt.x, pt.y ) );
931
932 aOutlineSegments.push_back( std::move( segment ) );
933 return;
934 }
935
936 int width = sprintToKicadCoord( static_cast<float>( aObj.line_width ) );
937
938 if( width <= 0 )
939 width = pcbIUScale.mmToIU( 0.25 );
940
941 for( size_t i = 0; i + 1 < aObj.points.size(); i++ )
942 {
943 PCB_SHAPE* shape = new PCB_SHAPE( aContainer );
944 shape->SetShape( SHAPE_T::SEGMENT );
945 shape->SetLayer( layer );
946 shape->SetWidth( width );
947 shape->SetStart( sprintToKicadPos( aObj.points[i].x, aObj.points[i].y ) );
948 shape->SetEnd( sprintToKicadPos( aObj.points[i + 1].x, aObj.points[i + 1].y ) );
949 aContainer->Add( shape );
950 }
951}
952
953
955 std::vector<std::vector<VECTOR2I>>& aOutlineSegments )
956{
957 if( aObj.points.size() < 2 )
958 return;
959
960 BOARD* board = aContainer ? aContainer->GetBoard() : nullptr;
961 PCB_LAYER_ID layer = mapLayer( aObj.layer );
962
963 if( layer == Edge_Cuts )
964 {
965 std::vector<VECTOR2I> segment;
966
967 for( const auto& pt : aObj.points )
968 segment.push_back( sprintToKicadPos( pt.x, pt.y ) );
969
970 aOutlineSegments.push_back( std::move( segment ) );
971 return;
972 }
973
974 bool isFilled = ( aObj.filled != 0 );
975 bool isCutout = ( aObj.keepout != 0 );
976
977 if( isCutout && LSET::AllCuMask().Contains( layer ) && aObj.points.size() >= 3 )
978 {
979 // Cutout area for ground plane exclusion -> rule area (keepout zone)
980 ZONE* zone = new ZONE( aContainer );
981 zone->SetLayer( layer );
982 zone->SetIsRuleArea( true );
983 zone->SetDoNotAllowZoneFills( true );
984 zone->SetDoNotAllowTracks( false );
985 zone->SetDoNotAllowVias( false );
986 zone->SetDoNotAllowPads( false );
987 zone->SetDoNotAllowFootprints( false );
988
989 SHAPE_POLY_SET outline;
990 outline.NewOutline();
991
992 for( const auto& pt : aObj.points )
993 {
994 VECTOR2I pos = sprintToKicadPos( pt.x, pt.y );
995 outline.Append( pos.x, pos.y );
996 }
997
998 zone->AddPolygon( outline.COutline( 0 ) );
999 aContainer->Add( zone );
1000 }
1001 else if( isFilled && LSET::AllCuMask().Contains( layer ) && aObj.points.size() >= 3 )
1002 {
1003 // Filled polygon on copper -> ZONE
1004 ZONE* zone = new ZONE( aContainer );
1005 zone->SetLayer( layer );
1006 zone->SetIsRuleArea( false );
1007 zone->SetDoNotAllowZoneFills( false );
1008
1009 SHAPE_POLY_SET outline;
1010 outline.NewOutline();
1011
1012 for( const auto& pt : aObj.points )
1013 {
1014 VECTOR2I pos = sprintToKicadPos( pt.x, pt.y );
1015 outline.Append( pos.x, pos.y );
1016 }
1017
1018 zone->AddPolygon( outline.COutline( 0 ) );
1019
1020 if( !aObj.net_name.empty() )
1021 {
1022 wxString netName = convertString( aObj.net_name );
1023 NETINFO_ITEM* net = board->FindNet( netName );
1024
1025 if( !net )
1026 {
1027 net = new NETINFO_ITEM( board, netName );
1028 board->Add( net );
1029 }
1030
1031 zone->SetNet( net );
1032 }
1033
1034 aContainer->Add( zone );
1035 }
1036 else if( isFilled && aObj.points.size() >= 3 )
1037 {
1038 // Filled polygon on non-copper layer -> filled PCB_SHAPE
1039 PCB_SHAPE* shape = new PCB_SHAPE( aContainer );
1040 shape->SetShape( SHAPE_T::POLY );
1041 shape->SetFilled( true );
1042 shape->SetLayer( layer );
1043 shape->SetWidth( 0 );
1044
1045 SHAPE_POLY_SET polySet;
1046 polySet.NewOutline();
1047
1048 for( const auto& pt : aObj.points )
1049 {
1050 VECTOR2I pos = sprintToKicadPos( pt.x, pt.y );
1051 polySet.Append( pos.x, pos.y );
1052 }
1053
1054 shape->SetPolyShape( polySet );
1055 aContainer->Add( shape );
1056 }
1057 else
1058 {
1059 // Unfilled polygon -> PCB_SHAPE polyline segments
1060 int width = sprintToKicadCoord( static_cast<float>( aObj.line_width ) );
1061
1062 if( width <= 0 )
1063 width = pcbIUScale.mmToIU( 0.25 );
1064
1065 for( size_t i = 0; i + 1 < aObj.points.size(); i++ )
1066 {
1067 PCB_SHAPE* shape = new PCB_SHAPE( aContainer );
1068 shape->SetShape( SHAPE_T::SEGMENT );
1069 shape->SetLayer( layer );
1070 shape->SetWidth( width );
1071 shape->SetStart( sprintToKicadPos( aObj.points[i].x, aObj.points[i].y ) );
1072 shape->SetEnd( sprintToKicadPos( aObj.points[i + 1].x, aObj.points[i + 1].y ) );
1073 aContainer->Add( shape );
1074 }
1075 }
1076}
1077
1078
1080 std::vector<std::vector<VECTOR2I>>& aOutlineSegments )
1081{
1082 PCB_LAYER_ID layer = mapLayer( aObj.layer );
1083 VECTOR2I center = sprintToKicadPos( aObj.x, aObj.y );
1084 float radius = ( aObj.outer + aObj.inner ) / 2.0f;
1085 int kiRadius = sprintToKicadCoord( radius );
1086 int width = sprintToKicadCoord( aObj.outer - aObj.inner );
1087
1088 if( width <= 0 )
1089 width = pcbIUScale.mmToIU( 0.25 );
1090
1091 int32_t startAngle = aObj.start_angle;
1092
1093 // line_width is uint32_t but stores end_angle (signed) for circle objects.
1094 // Two's complement reinterpretation is correct here.
1095 int32_t endAngle = static_cast<int32_t>( aObj.line_width );
1096
1097 bool isFullCircle = ( startAngle == 0 && endAngle == 0 )
1098 || ( endAngle - startAngle >= 360000 )
1099 || ( startAngle == endAngle );
1100
1101 if( layer == Edge_Cuts )
1102 {
1103 // Approximate arcs as line segments for outline reconstruction
1104 std::vector<VECTOR2I> segment;
1105
1106 if( isFullCircle )
1107 {
1108 for( int i = 0; i <= 24; i++ )
1109 {
1110 double angle = ( static_cast<double>( i ) / 24.0 ) * 2.0 * M_PI;
1111 int px = center.x + static_cast<int>( std::cos( angle ) * kiRadius );
1112 int py = center.y - static_cast<int>( std::sin( angle ) * kiRadius );
1113 segment.emplace_back( px, py );
1114 }
1115 }
1116 else
1117 {
1118 int32_t sa = startAngle;
1119 int32_t ea = endAngle;
1120
1121 if( ea <= sa )
1122 ea += 360000;
1123
1124 for( int32_t a = sa; a <= ea; a += 15000 )
1125 {
1126 double rad = ( static_cast<double>( a ) / 1000.0 ) * M_PI / 180.0;
1127 int px = center.x + static_cast<int>( std::cos( rad ) * kiRadius );
1128 int py = center.y - static_cast<int>( std::sin( rad ) * kiRadius );
1129 segment.emplace_back( px, py );
1130 }
1131
1132 double endRad = ( static_cast<double>( ea ) / 1000.0 ) * M_PI / 180.0;
1133 int epx = center.x + static_cast<int>( std::cos( endRad ) * kiRadius );
1134 int epy = center.y - static_cast<int>( std::sin( endRad ) * kiRadius );
1135 segment.emplace_back( epx, epy );
1136 }
1137
1138 aOutlineSegments.push_back( std::move( segment ) );
1139 return;
1140 }
1141
1142 PCB_SHAPE* shape = new PCB_SHAPE( aContainer );
1143 shape->SetLayer( layer );
1144 shape->SetWidth( width );
1145
1146 if( isFullCircle )
1147 {
1148 shape->SetShape( SHAPE_T::CIRCLE );
1149 shape->SetCenter( center );
1150 shape->SetEnd( VECTOR2I( center.x + kiRadius, center.y ) );
1151 }
1152 else
1153 {
1154 shape->SetShape( SHAPE_T::ARC );
1155 shape->SetCenter( center );
1156
1157 // Y-flip reverses angular direction, so negate start angle
1158 double startRad = ( static_cast<double>( startAngle ) / 1000.0 ) * M_PI / 180.0;
1159 int sx = center.x + static_cast<int>( std::cos( startRad ) * kiRadius );
1160 int sy = center.y - static_cast<int>( std::sin( startRad ) * kiRadius );
1161 shape->SetStart( VECTOR2I( sx, sy ) );
1162
1163 int32_t ea = endAngle;
1164
1165 if( ea <= startAngle )
1166 ea += 360000;
1167
1168 // Negate arc angle for Y-flip (reverses sweep direction)
1169 double arcAngle = -static_cast<double>( ea - startAngle ) / 1000.0;
1170 shape->SetArcAngleAndEnd( EDA_ANGLE( arcAngle, DEGREES_T ), true );
1171 }
1172
1173 aContainer->Add( shape );
1174}
1175
1176
1178{
1179 FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( aContainer );
1180
1181 // Skip component reference/value text only when it is not attached to a footprint.
1182 if( aObj.component_id > 0 && !fp )
1183 return;
1184
1185 PCB_LAYER_ID layer = mapLayer( aObj.layer );
1186
1187 if( layer == Edge_Cuts )
1188 return;
1189
1190 PCB_TEXT* text = nullptr;
1191 bool add = false;
1192
1193 if( fp && aObj.tht_shape > 0 && aObj.tht_shape <= 2 )
1194 {
1195 if( aObj.tht_shape == 1 )
1196 text = &fp->Reference();
1197 else if( aObj.tht_shape == 2 )
1198 text = &fp->Value();
1199 }
1200 else
1201 {
1202 if( aObj.text.empty() )
1203 return;
1204
1205 text = new PCB_TEXT( aContainer );
1206 add = true;
1207 }
1208
1209 text->SetLayer( layer );
1210 text->SetText( convertString( aObj.text ) );
1211 text->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
1212 text->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
1213 text->SetKeepUpright( false );
1214 text->SetVisible( aObj.filled != 0 );
1215
1216 VECTOR2I pos = sprintToKicadPos( aObj.x, aObj.y );
1217 text->SetTextPos( pos );
1218
1219 int height = sprintToKicadCoord( aObj.outer ) * 0.8;
1220
1221 if( height <= 0 )
1222 height = pcbIUScale.mmToIU( 1.0 );
1223
1224 double widthScale = 0.5 + 0.5 * aObj.line_width;
1225 text->SetTextSize( VECTOR2I( height * widthScale, height ) );
1226
1227 double thicknessScale = 0.06 + 0.05 * aObj.inner;
1228 int thickness = height * thicknessScale;
1229
1230 if( thickness <= 0 )
1231 thickness = std::max( 1, height / 8 );
1232
1233 text->SetTextThickness( thickness );
1234 text->SetTextAngle( EDA_ANGLE( -aObj.rotation, DEGREES_T ) );
1235
1236 if( aObj.mirror != 0 || aObj.thermal_width != 0 )
1237 text->SetMirrored( true );
1238
1239 if( add )
1240 aContainer->Add( text );
1241}
1242
1243
1244void SPRINT_LAYOUT_PARSER::buildOutline( BOARD* aBoard, std::vector<std::vector<VECTOR2I>>& aOutlineSegments,
1245 const SPRINT_LAYOUT::BOARD_DATA& aBoardData )
1246{
1247 // Try to join outline segments into closed polygons
1248 // Similar to OpenBoardView's outline_order_segments algorithm
1249 static const int PROXIMITY_DELTA = 100; // 100 nm tolerance
1250
1251 auto closeEnough = []( const VECTOR2I& a, const VECTOR2I& b, int delta ) -> bool
1252 {
1253 return std::abs( a.x - b.x ) < delta && std::abs( a.y - b.y ) < delta;
1254 };
1255
1256 // Try to join segments end-to-end. After each successful join, restart
1257 // the inner scan because seg.back() has changed.
1258 for( size_t iterations = 0; iterations < aOutlineSegments.size(); iterations++ )
1259 {
1260 bool joined = false;
1261
1262 for( auto& seg : aOutlineSegments )
1263 {
1264 if( seg.size() < 2 )
1265 continue;
1266
1267 for( auto& other : aOutlineSegments )
1268 {
1269 if( &seg == &other || other.empty() )
1270 continue;
1271
1272 bool frontMatch = closeEnough( seg.back(), other.front(), PROXIMITY_DELTA );
1273 bool backMatch = !frontMatch
1274 && closeEnough( seg.back(), other.back(), PROXIMITY_DELTA );
1275
1276 if( backMatch )
1277 {
1278 std::reverse( other.begin(), other.end() );
1279 frontMatch = true;
1280 }
1281
1282 if( !frontMatch )
1283 continue;
1284
1285 if( seg.back() == other.front() )
1286 seg.insert( seg.end(), other.begin() + 1, other.end() );
1287 else
1288 seg.insert( seg.end(), other.begin(), other.end() );
1289
1290 other.clear();
1291 joined = true;
1292 break;
1293 }
1294 }
1295
1296 if( !joined )
1297 break;
1298 }
1299
1300 bool hasOutline = false;
1301
1302 for( const auto& seg : aOutlineSegments )
1303 {
1304 if( seg.size() < 2 )
1305 continue;
1306
1307 hasOutline = true;
1308
1309 for( size_t i = 0; i + 1 < seg.size(); i++ )
1310 {
1311 PCB_SHAPE* shape = new PCB_SHAPE( aBoard );
1312 shape->SetShape( SHAPE_T::SEGMENT );
1313 shape->SetLayer( Edge_Cuts );
1314 shape->SetWidth( pcbIUScale.mmToIU( 0.1 ) );
1315 shape->SetStart( seg[i] );
1316 shape->SetEnd( seg[i + 1] );
1317 aBoard->Add( shape );
1318 }
1319 }
1320
1321 // Fallback: create rectangular outline from board dimensions
1322 if( !hasOutline )
1323 {
1324 int w = sprintToKicadCoord( static_cast<float>( aBoardData.size_x ) );
1325 int h = sprintToKicadCoord( static_cast<float>( aBoardData.size_y ) );
1326
1327 if( w > 0 && h > 0 )
1328 {
1329 VECTOR2I corners[4] = {
1330 { 0, 0 },
1331 { w, 0 },
1332 { w, h },
1333 { 0, h }
1334 };
1335
1336 for( int i = 0; i < 4; i++ )
1337 {
1338 PCB_SHAPE* shape = new PCB_SHAPE( aBoard );
1339 shape->SetShape( SHAPE_T::SEGMENT );
1340 shape->SetLayer( Edge_Cuts );
1341 shape->SetWidth( pcbIUScale.mmToIU( 0.1 ) );
1342 shape->SetStart( corners[i] );
1343 shape->SetEnd( corners[( i + 1 ) % 4] );
1344 aBoard->Add( shape );
1345 }
1346 }
1347 }
1348}
int index
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
Abstract interface for BOARD_ITEMs capable of storing other items inside.
virtual void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false)=0
Adds an item to the container.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:316
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:323
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition board.cpp:1247
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
Definition board.cpp:2597
const FOOTPRINTS & Footprints() const
Definition board.h:364
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr const Vec GetCenter() const
Definition box2.h:230
constexpr size_type GetHeight() const
Definition box2.h:215
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
void SetCenter(const VECTOR2I &aCenter)
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition eda_shape.h:360
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:152
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:194
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:184
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:236
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
void SetWidth(int aWidth)
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:385
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:269
PCB_FIELD & Value()
read/write accessors:
Definition footprint.h:865
void SetOrientationDegrees(double aOrientation)
Definition footprint.h:420
PCB_FIELD * GetField(FIELD_T aFieldType)
Return a mandatory field in this footprint.
std::deque< PAD * > & Pads()
Definition footprint.h:377
void SetReference(const wxString &aReference)
Definition footprint.h:835
PCB_FIELD & Reference()
Definition footprint.h:866
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
void GetFields(std::vector< PCB_FIELD * > &aVector, bool aVisibleOnly) const
Populate a std::vector with PCB_TEXTs.
void SetLibDescription(const wxString &aDesc)
Definition footprint.h:447
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:599
Handle the data for a net.
Definition netinfo.h:50
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:55
static LSET PTHMask()
layer set for a through hole pad
Definition pad.cpp:368
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition pad.cpp:375
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Represent a set of closed polygons.
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)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
wxString convertString(const std::string &aStr) const
SPRINT_LAYOUT::FILE_DATA m_fileData
void processText(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj)
PCB_LAYER_ID mapLayer(uint8_t aSprintLayer) const
std::string readFixedString(size_t aMaxLen)
bool Parse(const wxString &aFileName)
void buildOutline(BOARD *aBoard, std::vector< std::vector< VECTOR2I > > &aOutlineSegments, const SPRINT_LAYOUT::BOARD_DATA &aBoardData)
void processCircle(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj, std::vector< std::vector< VECTOR2I > > &aOutlineSegments)
void parseObject(SPRINT_LAYOUT::OBJECT &aObject, bool aIsTextChild=false)
BOARD * CreateBoard(std::map< wxString, std::unique_ptr< FOOTPRINT > > &aFootprintMap, size_t aBoardIndex=0)
void parseBoardHeader(SPRINT_LAYOUT::BOARD_DATA &aBoard)
void processLine(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj, std::vector< std::vector< VECTOR2I > > &aOutlineSegments)
void processPoly(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj, std::vector< std::vector< VECTOR2I > > &aOutlineSegments)
VECTOR2I sprintToKicadPos(float aX, float aY) const
int sprintToKicadCoord(float aValue) const
std::vector< uint8_t > m_buffer
void processPad(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj)
Handle a list of polygons defining a copper zone.
Definition zone.h:74
void SetDoNotAllowPads(bool aEnable)
Definition zone.h:735
void SetLocalClearance(std::optional< int > aClearance)
Definition zone.h:187
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
Definition zone.cpp:1172
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
Definition zone.h:238
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition zone.cpp:550
void SetIsRuleArea(bool aEnable)
Definition zone.h:717
void SetDoNotAllowTracks(bool aEnable)
Definition zone.h:734
void SetDoNotAllowVias(bool aEnable)
Definition zone.h:733
void SetThermalReliefGap(int aThermalReliefGap)
Definition zone.h:227
void SetDoNotAllowFootprints(bool aEnable)
Definition zone.h:736
void SetDoNotAllowZoneFills(bool aEnable)
Definition zone.h:732
void SetAssignedPriority(unsigned aPriority)
Definition zone.h:121
void SetZoneName(const wxString &aName)
Definition zone.h:165
#define _(s)
@ DEGREES_T
Definition eda_angle.h:31
@ SEGMENT
Definition eda_shape.h:48
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ Edge_Cuts
Definition layer_ids.h:112
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ In2_Cu
Definition layer_ids.h:67
@ F_SilkS
Definition layer_ids.h:100
@ In1_Cu
Definition layer_ids.h:66
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ SMD
Smd pad, appears on the solder paste layer (default)
Definition padstack.h:99
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ CHAMFERED_RECT
Definition padstack.h:60
@ RECTANGLE
Definition padstack.h:54
static constexpr uint32_t MAX_CHILDREN
static constexpr uint32_t MAX_GROUPS
static constexpr uint32_t MAX_OBJECTS
static constexpr uint32_t MAX_POINTS
std::vector< OBJECT > objects
std::vector< POINT > points
std::vector< OBJECT > text_children
std::vector< uint32_t > groups
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
VECTOR2I center
int radius
int clearance
int delta
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
#define M_PI
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
@ THERMAL
Use thermal relief for pads.
Definition zones.h:50
@ NONE
Pads are not covered.
Definition zones.h:49
@ FULL
pads are covered by copper
Definition zones.h:51