KiCad PCB EDA Suite
Loading...
Searching...
No Matches
import_fabmaster.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 BeagleBoard Foundation
5 * Copyright (C) 2020-2023, 2024 KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Seth Hillbrand <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "import_fabmaster.h"
27
28#include <algorithm>
29#include <array>
30#include <iostream>
31#include <fstream>
32#include <map>
33#include <memory>
34#include <string>
35#include <sstream>
36#include <vector>
37#include <utility>
38
39#include <wx/log.h>
40
41#include <board.h>
43#include <board_item.h>
44#include <footprint.h>
45#include <pad.h>
46#include <padstack.h>
47#include <pcb_group.h>
48#include <pcb_shape.h>
49#include <pcb_text.h>
50#include <pcb_track.h>
51#include <zone.h>
52#include <common.h>
53#include <geometry/shape_arc.h>
55#include <string_utils.h>
56#include <progress_reporter.h>
57#include <math/util.h>
58
59#include <wx/filename.h>
60
61
67static const wxChar traceFabmaster[] = wxT( "KICAD_FABMASTER" );
68
69
71{
72 const unsigned PROGRESS_DELTA = 250;
73
75 {
76 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
77 {
79 / std::max( 1U, m_totalCount ) );
80
82 THROW_IO_ERROR( _( "Open cancelled by user." ) );
83
85 }
86 }
87}
88
89
90double FABMASTER::readDouble( const std::string& aStr ) const
91{
92 // This is bad, but at least don't return uninitialized data
93 wxCHECK_MSG( !aStr.empty(), 0.0, "Empty string passed to readDouble" );
94
95 std::istringstream istr( aStr );
96 istr.imbue( std::locale::classic() );
97
98 double doubleValue;
99 istr >> doubleValue;
100 return doubleValue;
101}
102
103
104int FABMASTER::readInt( const std::string& aStr ) const
105{
106 // This is bad, but at least don't return uninitialized data
107 wxCHECK_MSG( !aStr.empty(), 0, "Empty string passed to readInt" );
108
109 std::istringstream istr( aStr );
110 istr.imbue( std::locale::classic() );
111
112 int intValue;
113 istr >> intValue;
114 return intValue;
115}
116
117
118bool FABMASTER::Read( const std::string& aFile )
119{
120 std::ifstream ifs( aFile, std::ios::in | std::ios::binary );
121
122 if( !ifs.is_open() )
123 return false;
124
125 m_filename = aFile;
126
127 // Read/ignore all bytes in the file to find the size and then go back to the beginning
128 ifs.ignore( std::numeric_limits<std::streamsize>::max() );
129 std::streamsize length = ifs.gcount();
130 ifs.clear();
131 ifs.seekg( 0, std::ios_base::beg );
132
133 std::string buffer( std::istreambuf_iterator<char>{ ifs }, {} );
134
135 std::vector < std::string > row;
136
137 // Reserve an estimate of the number of rows to prevent continual re-allocation
138 // crashing (Looking at you MSVC)
139 row.reserve( length / 100 );
140 std::string cell;
141 cell.reserve( 100 );
142
143 bool quoted = false;
144
145 for( auto& ch : buffer )
146 {
147 switch( ch )
148 {
149 case '"':
150
151 if( cell.empty() || cell[0] == '"' )
152 quoted = !quoted;
153
154 cell += ch;
155 break;
156
157 case '!':
158 if( !quoted )
159 {
160 row.push_back( cell );
161 cell.clear();
162 }
163 else
164 cell += ch;
165
166 break;
167
168 case '\n':
169
171 if( !cell.empty() )
172 row.push_back( cell );
173
174 cell.clear();
175 rows.push_back( row );
176 row.clear();
177 quoted = false;
178 break;
179
180 case '\r':
181 break;
182
183 default:
184 cell += std::toupper( ch );
185 }
186 }
187
188 // Handle last line without linebreak
189 if( !cell.empty() || !row.empty() )
190 {
191 row.push_back( cell );
192 cell.clear();
193 rows.push_back( row );
194 row.clear();
195 }
196
197 return true;
198}
199
200
202{
203 single_row row;
204
205 try
206 {
207 row = rows.at( aOffset );
208 }
209 catch( std::out_of_range& )
210 {
211 return UNKNOWN_EXTRACT;
212 }
213
214 if( row.size() < 3 )
215 return UNKNOWN_EXTRACT;
216
217 if( row[0].back() != 'A' )
218 return UNKNOWN_EXTRACT;
219
220 std::string row1 = row[1];
221 std::string row2 = row[2];
222 std::string row3{};
223
225 // some do not
226 alg::delete_if( row1, []( char c ){ return c == '_'; } );
227 alg::delete_if( row2, []( char c ){ return c == '_'; } );
228
229 if( row.size() > 3 )
230 {
231 row3 = row[3];
232 alg::delete_if( row3, []( char c ){ return c == '_'; } );
233 }
234
235 if( row1 == "REFDES" && row2 == "COMPCLASS" )
236 return EXTRACT_REFDES;
237
238 if( row1 == "NETNAME" && row2 == "REFDES" )
239 return EXTRACT_NETS;
240
241 if( row1 == "CLASS" && row2 == "SUBCLASS" && row3.empty() )
243
244 if( row1 == "GRAPHICDATANAME" && row2 == "GRAPHICDATANUMBER" )
245 return EXTRACT_GRAPHICS;
246
247 if( row1 == "CLASS" && row2 == "SUBCLASS" && row3 == "GRAPHICDATANAME" )
248 return EXTRACT_TRACES;
249
250 if( row1 == "SYMNAME" && row2 == "PINNAME" )
252
253 if( row1 == "SYMNAME" && row2 == "SYMMIRROR" && row3 == "PINNAME" )
254 return EXTRACT_PINS;
255
256 if( row1 == "VIAX" && row2 == "VIAY" )
257 return EXTRACT_VIAS;
258
259 if( row1 == "SUBCLASS" && row2 == "PADSHAPENAME" )
260 return EXTRACT_PAD_SHAPES;
261
262 if( row1 == "PADNAME" )
263 return EXTRACT_PADSTACKS;
264
265 if( row1 == "LAYERSORT" )
266 return EXTRACT_FULL_LAYERS;
267
268 wxLogError( _( "Unknown FABMASTER section %s:%s at row %zu." ),
269 row1.c_str(),
270 row2.c_str(),
271 aOffset );
272 return UNKNOWN_EXTRACT;
273
274}
275
276
277double FABMASTER::processScaleFactor( size_t aRow )
278{
279 double retval = 0.0;
280
281 if( aRow >= rows.size() )
282 return -1.0;
283
284 if( rows[aRow].size() < 11 )
285 {
286 wxLogError( _( "Invalid row size in J row %zu. Expecting 11 elements but found %zu." ),
287 aRow,
288 rows[aRow].size() );
289 return -1.0;
290 }
291
292 for( int i = 7; i < 10 && retval < 1.0; ++i )
293 {
294 std::string units = rows[aRow][i];
295 std::transform(units.begin(), units.end(),units.begin(), ::toupper);
296
297 if( units == "MILS" )
298 retval = pcbIUScale.IU_PER_MILS;
299 else if( units == "MILLIMETERS" )
300 retval = pcbIUScale.IU_PER_MM;
301 else if( units == "MICRONS" )
302 retval = pcbIUScale.IU_PER_MM * 10.0;
303 else if( units == "INCHES" )
304 retval = pcbIUScale.IU_PER_MILS * 1000.0;
305 }
306
307 if( retval < 1.0 )
308 {
309 wxLogError( _( "Could not find units value, defaulting to mils." ) );
310 retval = pcbIUScale.IU_PER_MILS;
311 }
312
313 return retval;
314}
315
316
317int FABMASTER::getColFromName( size_t aRow, const std::string& aStr )
318{
319 if( aRow >= rows.size() )
320 return -1;
321
322 std::vector<std::string> header = rows[aRow];
323
324 for( size_t i = 0; i < header.size(); i++ )
325 {
328 alg::delete_if( header[i], []( const char c ) { return c == '_'; } );
329
330 if( header[i] == aStr )
331 return i;
332 }
333
334 THROW_IO_ERROR( wxString::Format( _( "Could not find column label %s." ), aStr.c_str() ) );
335 return -1;
336}
337
338
339PCB_LAYER_ID FABMASTER::getLayer( const std::string& aLayerName )
340{
341 const auto& kicad_layer = layers.find( aLayerName);
342
343 if( kicad_layer == layers.end() )
344 return UNDEFINED_LAYER;
345 else
346 return static_cast<PCB_LAYER_ID>( kicad_layer->second.layerid );
347}
348
349
351{
352 size_t rownum = aRow + 2;
353
354 if( rownum >= rows.size() )
355 return -1;
356
357 const single_row& header = rows[aRow];
358
359 int pad_name_col = getColFromName( aRow, "PADNAME" );
360 int pad_num_col = getColFromName( aRow, "RECNUMBER" );
361 int pad_lay_col = getColFromName( aRow, "LAYER" );
362 int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
363 int pad_via_col = getColFromName( aRow, "VIAFLAG" );
364 int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
365 int pad_width_col = getColFromName( aRow, "PADWIDTH" );
366 int pad_height_col = getColFromName( aRow, "PADHGHT" );
367 int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
368 int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
369 int pad_flash_col = getColFromName( aRow, "PADFLASH" );
370 int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
371
372 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
373 {
374 const single_row& row = rows[rownum];
375
376 if( row.size() != header.size() )
377 {
378 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
379 rownum,
380 header.size(),
381 row.size() );
382 continue;
383 }
384
385 auto& pad_name = row[pad_name_col];
386 auto& pad_num = row[pad_num_col];
387 auto& pad_layer = row[pad_lay_col];
388 auto& pad_is_fixed = row[pad_fix_col];
389 auto& pad_is_via = row[pad_via_col];
390 auto& pad_shape = row[pad_shape_col];
391 auto& pad_width = row[pad_width_col];
392 auto& pad_height = row[pad_height_col];
393 auto& pad_xoff = row[pad_xoff_col];
394 auto& pad_yoff = row[pad_yoff_col];
395 auto& pad_flash = row[pad_flash_col];
396 auto& pad_shapename = row[pad_shape_name_col];
397
398 // This layer setting seems to be unused
399 if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
400 continue;
401
402 // Skip the technical layers
403 if( pad_layer[0] == '~' )
404 break;
405
406 auto result = layers.emplace( pad_layer, FABMASTER_LAYER{} );
407 FABMASTER_LAYER& layer = result.first->second;
408
410 if( layer.id == 0 )
411 {
412 layer.name = pad_layer;
413 layer.id = readInt( pad_num );
414 layer.conductive = true;
415 }
416 }
417
418 return 0;
419}
420
421
428size_t FABMASTER::processPadStacks( size_t aRow )
429{
430 size_t rownum = aRow + 2;
431
432 if( rownum >= rows.size() )
433 return -1;
434
435 const single_row& header = rows[aRow];
436 double scale_factor = processScaleFactor( aRow + 1 );
437
438 if( scale_factor <= 0.0 )
439 return -1;
440
441 int pad_name_col = getColFromName( aRow, "PADNAME" );
442 int pad_num_col = getColFromName( aRow, "RECNUMBER" );
443 int pad_lay_col = getColFromName( aRow, "LAYER" );
444 int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
445 int pad_via_col = getColFromName( aRow, "VIAFLAG" );
446 int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
447 int pad_width_col = getColFromName( aRow, "PADWIDTH" );
448 int pad_height_col = getColFromName( aRow, "PADHGHT" );
449 int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
450 int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
451 int pad_flash_col = getColFromName( aRow, "PADFLASH" );
452 int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
453
454 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
455 {
456 const single_row& row = rows[rownum];
457 FM_PAD* pad;
458
459 if( row.size() != header.size() )
460 {
461 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
462 rownum,
463 header.size(),
464 row.size() );
465 continue;
466 }
467
468 auto& pad_name = row[pad_name_col];
469 auto& pad_num = row[pad_num_col];
470 auto& pad_layer = row[pad_lay_col];
471 auto& pad_is_fixed = row[pad_fix_col];
472 auto& pad_is_via = row[pad_via_col];
473 auto& pad_shape = row[pad_shape_col];
474 auto& pad_width = row[pad_width_col];
475 auto& pad_height = row[pad_height_col];
476 auto& pad_xoff = row[pad_xoff_col];
477 auto& pad_yoff = row[pad_yoff_col];
478 auto& pad_flash = row[pad_flash_col];
479 auto& pad_shapename = row[pad_shape_name_col];
480
481 // This layer setting seems to be unused
482 if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
483 continue;
484
485 int recnum = KiROUND( readDouble( pad_num ) );
486
487 auto new_pad = pads.find( pad_name );
488
489 if( new_pad != pads.end() )
490 pad = &new_pad->second;
491 else
492 {
493 pads[pad_name] = FM_PAD();
494 pad = &pads[pad_name];
495 pad->name = pad_name;
496 }
497
499 if( pad_layer == "~DRILL" )
500 {
501 int drill_hit;
502 int drill_x;
503 int drill_y;
504
505 try
506 {
507 drill_hit = KiROUND( std::fabs( readDouble( pad_shape ) * scale_factor ) );
508 drill_x = KiROUND( std::fabs( readDouble( pad_width ) * scale_factor ) );
509 drill_y = KiROUND( std::fabs( readDouble( pad_height ) * scale_factor ) );
510 }
511 catch( ... )
512 {
513 wxLogError( _( "Expecting drill size value but found %s!%s!%s in row %zu." ),
514 pad_shape.c_str(),
515 pad_width.c_str(),
516 pad_height.c_str(),
517 rownum );
518 continue;
519 }
520
521 if( drill_hit == 0 )
522 {
523 pad->drill = false;
524 continue;
525 }
526
527 pad->drill = true;
528
529 // This is to account for broken fabmaster outputs where circle drill hits don't
530 // actually get the drill hit value.
531 if( drill_x == drill_y )
532 {
533 pad->drill_size_x = drill_hit;
534 pad->drill_size_y = drill_hit;
535 }
536 else
537 {
538 pad->drill_size_x = drill_x;
539 pad->drill_size_y = drill_y;
540 }
541
542 if( !pad_shapename.empty() && pad_shapename[0] == 'P' )
543 pad->plated = true;
544
545 continue;
546 }
547
548 if( pad_shape.empty() )
549 continue;
550
551 double w;
552 double h;
553
554 try
555 {
556 w = readDouble( pad_width ) * scale_factor;
557 h = readDouble( pad_height ) * scale_factor;
558 }
559 catch( ... )
560 {
561 wxLogError( _( "Expecting pad size values but found %s : %s in row %zu." ),
562 pad_width.c_str(),
563 pad_height.c_str(),
564 rownum );
565 continue;
566 }
567
568 if( w <= 0.0 )
569 continue;
570
571 auto layer = layers.find( pad_layer );
572
573 if( layer != layers.end() )
574 {
575 if( layer->second.layerid == F_Cu )
576 pad->top = true;
577 else if( layer->second.layerid == B_Cu )
578 pad->bottom = true;
579 }
580
581 if( w > std::numeric_limits<int>::max() || h > std::numeric_limits<int>::max() )
582 {
583 wxLogError( _( "Invalid pad size in row %zu." ), rownum );
584 continue;
585 }
586
587 if( pad_layer == "~TSM" || pad_layer == "~BSM" )
588 {
589 if( w > 0.0 && h > 0.0 )
590 {
591 pad->mask_width = KiROUND( w );
592 pad->mask_height = KiROUND( h );
593 }
594 continue;
595 }
596
597 if( pad_layer == "~TSP" || pad_layer == "~BSP" )
598 {
599 if( w > 0.0 && h > 0.0 )
600 {
601 pad->paste_width = KiROUND( w );
602 pad->paste_height = KiROUND( h );
603 }
604 continue;
605 }
606
608 if( pad_layer[0] == '~' )
609 continue;
610
611 try
612 {
613 pad->x_offset = KiROUND( readDouble( pad_xoff ) * scale_factor );
614 pad->y_offset = -KiROUND( readDouble( pad_yoff ) * scale_factor );
615 }
616 catch( ... )
617 {
618 wxLogError( _( "Expecting pad offset values but found %s:%s in row %zu." ),
619 pad_xoff.c_str(),
620 pad_yoff.c_str(),
621 rownum );
622 continue;
623 }
624
625 if( w > 0.0 && h > 0.0 && recnum == 1 )
626 {
627 pad->width = KiROUND( w );
628 pad->height = KiROUND( h );
629 pad->via = ( std::toupper( pad_is_via[0] ) != 'V' );
630
631 if( pad_shape == "CIRCLE" )
632 {
633 pad->height = pad->width;
634 pad->shape = PAD_SHAPE::CIRCLE;
635 }
636 else if( pad_shape == "RECTANGLE" )
637 {
638 pad->shape = PAD_SHAPE::RECTANGLE;
639 }
640 else if( pad_shape == "ROUNDED_RECT" )
641 {
642 pad->shape = PAD_SHAPE::ROUNDRECT;
643 }
644 else if( pad_shape == "SQUARE" )
645 {
646 pad->shape = PAD_SHAPE::RECTANGLE;
647 pad->height = pad->width;
648 }
649 else if( pad_shape == "OBLONG" || pad_shape == "OBLONG_X" || pad_shape == "OBLONG_Y" )
650 pad->shape = PAD_SHAPE::OVAL;
651 else if( pad_shape == "OCTAGON" )
652 {
653 pad->shape = PAD_SHAPE::RECTANGLE;
654 pad->is_octogon = true;
655 }
656 else if( pad_shape == "SHAPE" )
657 {
658 pad->shape = PAD_SHAPE::CUSTOM;
659 pad->custom_name = pad_shapename;
660 }
661 else
662 {
663 wxLogError( _( "Unknown pad shape name '%s' on layer '%s' in row %zu." ),
664 pad_shape.c_str(),
665 pad_layer.c_str(),
666 rownum );
667 continue;
668 }
669 }
670 }
671
672 return rownum - aRow;
673}
674
675
677{
678 size_t rownum = aRow + 2;
679
680 if( rownum >= rows.size() )
681 return -1;
682
683 auto& header = rows[aRow];
684 double scale_factor = processScaleFactor( aRow + 1 );
685
686 if( scale_factor <= 0.0 )
687 return -1;
688
689 int layer_class_col = getColFromName( aRow, "CLASS" );
690 int layer_subclass_col = getColFromName( aRow, "SUBCLASS" );
691
692 if( layer_class_col < 0 || layer_subclass_col < 0 )
693 return -1;
694
695 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
696 {
697 const single_row& row = rows[rownum];
698
699 if( row.size() != header.size() )
700 {
701 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
702 rownum,
703 header.size(),
704 row.size() );
705 continue;
706 }
707
708 auto result = layers.emplace( row[layer_subclass_col], FABMASTER_LAYER{} );
709 FABMASTER_LAYER& layer = result.first->second;
710
711 layer.name = row[layer_subclass_col];
712 layer.positive = true;
713 layer.conductive = false;
714
715 if( row[layer_class_col] == "ANTI ETCH" )
716 {
717 layer.positive = false;
718 layer.conductive = true;
719 }
720 else if( row[layer_class_col] == "ETCH" )
721 {
722 layer.conductive = true;
723 }
724 }
725
726 return rownum - aRow;
727}
728
729
731{
732 bool has_l1 = false;
733 int max_layer = 0;
734 std::string max_layer_name;
735
736 std::vector<std::pair<std::string, int>> extra_layers
737 {
738 { "ASSEMBLY_TOP", F_Fab },
739 { "ASSEMBLY_BOTTOM", B_Fab },
740 { "PLACE_BOUND_TOP", F_CrtYd },
741 { "PLACE_BOUND_BOTTOM", B_CrtYd },
742 };
743
744 std::vector<FABMASTER_LAYER*> layer_order;
745
746 int next_user_layer = User_1;
747
748 for( auto& el : layers )
749 {
750 FABMASTER_LAYER& layer = el.second;
752
753 if( layer.conductive )
754 {
755 layer_order.push_back( &layer );
756 }
757 else if( ( layer.name.find( "SILK" ) != std::string::npos
758 && layer.name.find( "AUTOSILK" )
759 == std::string::npos ) // Skip the autosilk layer
760 || layer.name.find( "DISPLAY" ) != std::string::npos )
761 {
762 if( layer.name.find( "B" ) != std::string::npos )
763 layer.layerid = B_SilkS;
764 else
765 layer.layerid = F_SilkS;
766 }
767 else if( layer.name.find( "MASK" ) != std::string::npos ||
768 layer.name.find( "MSK" ) != std::string::npos )
769 {
770 if( layer.name.find( "B" ) != std::string::npos )
771 layer.layerid = B_Mask;
772 else
773 layer.layerid = F_Mask;
774 }
775 else if( layer.name.find( "PAST" ) != std::string::npos )
776 {
777 if( layer.name.find( "B" ) != std::string::npos )
778 layer.layerid = B_Paste;
779 else
780 layer.layerid = F_Paste;
781 }
782 else if( layer.name.find( "NCLEGEND" ) != std::string::npos )
783 {
784 layer.layerid = Dwgs_User;
785 }
786 else
787 {
788 // Try to gather as many other layers into user layers as possible
789
790 // Skip ones that seem like a waste of good layers
791 if( layer.name.find( "AUTOSILK" ) == std::string::npos )
792 {
793 if( next_user_layer <= User_9 )
794 {
795 // Assign the mapping
796 layer.layerid = next_user_layer;
797 next_user_layer += 2;
798 }
799 else
800 {
801 // Out of additional layers
802 // For now, drop it, but maybr we could gather onto some other layer.
803 // Or implement a proper layer remapper.
804 layer.disable = true;
805 wxLogWarning( _( "No user layer to put layer %s" ), layer.name );
806 }
807 }
808 }
809 }
810
811 std::sort( layer_order.begin(), layer_order.end(), FABMASTER_LAYER::BY_ID() );
812
813 for( size_t layeri = 0; layeri < layer_order.size(); ++layeri )
814 {
815 FABMASTER_LAYER* layer = layer_order[layeri];
816 if( layeri == 0 )
817 layer->layerid = F_Cu;
818 else if( layeri == layer_order.size() - 1 )
819 layer->layerid = B_Cu;
820 else
821 layer->layerid = layeri * 2 + 2;
822 }
823
824 for( auto& new_pair : extra_layers )
825 {
826 FABMASTER_LAYER new_layer;
827
828 new_layer.name = new_pair.first;
829 new_layer.layerid = new_pair.second;
830 new_layer.conductive = false;
831
832 auto result = layers.emplace( new_pair.first, new_layer );
833
834 if( !result.second )
835 {
836 result.first->second.layerid = new_pair.second;
837 result.first->second.disable = false;
838 }
839 }
840
841 for( const auto& [layer_name, fabmaster_layer] : layers )
842 {
843 wxLogTrace( traceFabmaster, wxT( "Layer %s -> KiCad layer %d" ), layer_name,
844 fabmaster_layer.layerid );
845 }
846
847 return true;
848}
849
850
856size_t FABMASTER::processLayers( size_t aRow )
857{
858 size_t rownum = aRow + 2;
859
860 if( rownum >= rows.size() )
861 return -1;
862
863 auto& header = rows[aRow];
864 double scale_factor = processScaleFactor( aRow + 1 );
865
866 if( scale_factor <= 0.0 )
867 return -1;
868
869 int layer_sort_col = getColFromName( aRow, "LAYERSORT" );
870 int layer_subclass_col = getColFromName( aRow, "LAYERSUBCLASS" );
871 int layer_art_col = getColFromName( aRow, "LAYERARTWORK" );
872 int layer_use_col = getColFromName( aRow, "LAYERUSE" );
873 int layer_cond_col = getColFromName( aRow, "LAYERCONDUCTOR" );
874 int layer_er_col = getColFromName( aRow, "LAYERDIELECTRICCONSTANT" );
875 int layer_rho_col = getColFromName( aRow, "LAYERELECTRICALCONDUCTIVITY" );
876 int layer_mat_col = getColFromName( aRow, "LAYERMATERIAL" );
877
878 if( layer_sort_col < 0 || layer_subclass_col < 0 || layer_art_col < 0 || layer_use_col < 0
879 || layer_cond_col < 0 || layer_er_col < 0 || layer_rho_col < 0 || layer_mat_col < 0 )
880 return -1;
881
882 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
883 {
884 const single_row& row = rows[rownum];
885
886 if( row.size() != header.size() )
887 {
888 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
889 rownum,
890 header.size(),
891 row.size() );
892 continue;
893 }
894
895 auto& layer_sort = row[layer_sort_col];
896 auto& layer_subclass = row[layer_subclass_col];
897 auto& layer_art = row[layer_art_col];
898 auto& layer_use = row[layer_use_col];
899 auto& layer_cond = row[layer_cond_col];
900 auto& layer_er = row[layer_er_col];
901 auto& layer_rho = row[layer_rho_col];
902 auto& layer_mat = row[layer_mat_col];
903
904 if( layer_mat == "AIR" )
905 continue;
906
907 FABMASTER_LAYER layer;
908
909 if( layer_subclass.empty() )
910 {
911 if( layer_cond != "NO" )
912 layer.name = "In.Cu" + layer_sort;
913 else
914 layer.name = "Dielectric" + layer_sort;
915 }
916
917 layer.positive = ( layer_art != "NEGATIVE" );
918
919 layers.emplace( layer.name, layer );
920 }
921
922 return rownum - aRow;
923}
924
925
931size_t FABMASTER::processCustomPads( size_t aRow )
932{
933 size_t rownum = aRow + 2;
934
935 if( rownum >= rows.size() )
936 return -1;
937
938 auto& header = rows[aRow];
939 double scale_factor = processScaleFactor( aRow + 1 );
940
941 if( scale_factor <= 0.0 )
942 return -1;
943
944 int pad_subclass_col = getColFromName( aRow, "SUBCLASS" );
945 int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
946 int pad_grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
947 int pad_grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
948 int pad_record_tag_col = getColFromName( aRow, "RECORDTAG" );
949 int pad_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
950 int pad_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
951 int pad_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
952 int pad_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
953 int pad_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
954 int pad_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
955 int pad_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
956 int pad_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
957 int pad_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
958 int pad_stack_name_col = getColFromName( aRow, "PADSTACKNAME" );
959 int pad_refdes_col = getColFromName( aRow, "REFDES" );
960 int pad_pin_num_col = getColFromName( aRow, "PINNUMBER" );
961
962 if( pad_subclass_col < 0 || pad_shape_name_col < 0 || pad_grdata1_col < 0 || pad_grdata2_col < 0
963 || pad_grdata3_col < 0 || pad_grdata4_col < 0 || pad_grdata5_col < 0
964 || pad_grdata6_col < 0 || pad_grdata7_col < 0 || pad_grdata8_col < 0
965 || pad_grdata9_col < 0 || pad_stack_name_col < 0 || pad_refdes_col < 0
966 || pad_pin_num_col < 0 )
967 return -1;
968
969 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
970 {
971 const single_row& row = rows[rownum];
972
973 if( row.size() != header.size() )
974 {
975 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
976 rownum,
977 header.size(),
978 row.size() );
979
980 continue;
981 }
982
983 auto& pad_layer = row[pad_subclass_col];
984 auto pad_shape_name = row[pad_shape_name_col];
985 auto& pad_record_tag = row[pad_record_tag_col];
986
987 GRAPHIC_DATA gr_data;
988 gr_data.graphic_dataname = row[pad_grdata_name_col];
989 gr_data.graphic_datanum = row[pad_grdata_num_col];
990 gr_data.graphic_data1 = row[pad_grdata1_col];
991 gr_data.graphic_data2 = row[pad_grdata2_col];
992 gr_data.graphic_data3 = row[pad_grdata3_col];
993 gr_data.graphic_data4 = row[pad_grdata4_col];
994 gr_data.graphic_data5 = row[pad_grdata5_col];
995 gr_data.graphic_data6 = row[pad_grdata6_col];
996 gr_data.graphic_data7 = row[pad_grdata7_col];
997 gr_data.graphic_data8 = row[pad_grdata8_col];
998 gr_data.graphic_data9 = row[pad_grdata9_col];
999
1000 auto& pad_stack_name = row[pad_stack_name_col];
1001 auto& pad_refdes = row[pad_refdes_col];
1002 auto& pad_pin_num = row[pad_pin_num_col];
1003
1004 // N.B. We get the FIGSHAPE records as "FIG_SHAPE name". We only want "name"
1005 // and we don't process other pad shape records
1006 std::string prefix( "FIG_SHAPE " );
1007
1008 if( pad_shape_name.length() <= prefix.length()
1009 || !std::equal( prefix.begin(), prefix.end(), pad_shape_name.begin() ) )
1010 {
1011 continue;
1012 }
1013
1014 // Custom pads are a series of records with the same record ID but incrementing
1015 // Sequence numbers.
1016 int id = -1;
1017 int seq = -1;
1018
1019 if( std::sscanf( pad_record_tag.c_str(), "%d %d", &id, &seq ) != 2 )
1020 {
1021 wxLogError( _( "Invalid format for id string '%s' in custom pad row %zu." ),
1022 pad_record_tag.c_str(),
1023 rownum );
1024 continue;
1025 }
1026
1027 auto name = pad_shape_name.substr( prefix.length() );
1028 name += "_" + pad_refdes + "_" + pad_pin_num;
1029 auto ret = pad_shapes.emplace( name, FABMASTER_PAD_SHAPE{} );
1030
1031 auto& custom_pad = ret.first->second;
1032
1033 // If we were able to insert the pad name, then we need to initialize the
1034 // record
1035 if( ret.second )
1036 {
1037 custom_pad.name = name;
1038 custom_pad.padstack = pad_stack_name;
1039 custom_pad.pinnum = pad_pin_num;
1040 custom_pad.refdes = pad_refdes;
1041 }
1042
1043 // At this point we extract the individual graphical elements for processing the complex
1044 // pad. The coordinates are in board origin format, so we'll need to fix the offset later
1045 // when we assign them to the modules.
1046
1047 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1048
1049 if( gr_item )
1050 {
1051 gr_item->layer = pad_layer;
1052 gr_item->refdes = pad_refdes;
1053 gr_item->seq = seq;
1054 gr_item->subseq = 0;
1055
1056 // emplace may fail here, in which case, it returns the correct position to use for
1057 // the existing map
1058 auto pad_it = custom_pad.elements.emplace( id, graphic_element{} );
1059 auto retval = pad_it.first->second.insert( std::move(gr_item ) );
1060
1061 if( !retval.second )
1062 {
1063 wxLogError( _( "Could not insert graphical item %d into padstack '%s'." ),
1064 seq,
1065 pad_stack_name.c_str() );
1066 }
1067 }
1068 else
1069 {
1070 wxLogError( _( "Unrecognized pad shape primitive '%s' in row %zu." ),
1071 gr_data.graphic_dataname,
1072 rownum );
1073 }
1074 }
1075
1076 return rownum - aRow;
1077}
1078
1079
1081 double aScale )
1082{
1083 GRAPHIC_LINE* new_line = new GRAPHIC_LINE ;
1084
1085 new_line->shape = GR_SHAPE_LINE;
1086 new_line->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1087 new_line->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1088 new_line->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1089 new_line->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1090 new_line->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1091
1092 return new_line;
1093}
1094
1095
1097{
1098 GRAPHIC_ARC* new_arc = new GRAPHIC_ARC ;
1099
1100 new_arc->shape = GR_SHAPE_ARC;
1101 new_arc->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1102 new_arc->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1103 new_arc->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1104 new_arc->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1105 new_arc->center_x = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1106 new_arc->center_y = -KiROUND( readDouble( aData.graphic_data6 ) * aScale );
1107 new_arc->radius = KiROUND( readDouble( aData.graphic_data7 ) * aScale );
1108 new_arc->width = KiROUND( readDouble( aData.graphic_data8 ) * aScale );
1109
1110 new_arc->clockwise = ( aData.graphic_data9 != "COUNTERCLOCKWISE" );
1111
1112 EDA_ANGLE startangle( VECTOR2I( new_arc->start_x, new_arc->start_y )
1113 - VECTOR2I( new_arc->center_x, new_arc->center_y ) );
1114 EDA_ANGLE endangle( VECTOR2I( new_arc->end_x, new_arc->end_y )
1115 - VECTOR2I( new_arc->center_x, new_arc->center_y ) );
1116 EDA_ANGLE angle;
1117
1118 startangle.Normalize();
1119 endangle.Normalize();
1120
1121 VECTOR2I center( new_arc->center_x, new_arc->center_y );
1122 VECTOR2I start( new_arc->start_x, new_arc->start_y );
1123 VECTOR2I mid( new_arc->start_x, new_arc->start_y );
1124 VECTOR2I end( new_arc->end_x, new_arc->end_y );
1125
1126 angle = endangle - startangle;
1127
1128 if( new_arc->clockwise && angle < ANGLE_0 )
1129 angle += ANGLE_360;
1130 if( !new_arc->clockwise && angle > ANGLE_0 )
1131 angle -= ANGLE_360;
1132
1133 if( start == end )
1134 angle = -ANGLE_360;
1135
1136 RotatePoint( mid, center, -angle / 2.0 );
1137
1138 if( start == end )
1139 new_arc->shape = GR_SHAPE_CIRCLE;
1140
1141 new_arc->result = SHAPE_ARC( start, mid, end, 0 );
1142
1143 return new_arc;
1144}
1145
1146
1148{
1149 /*
1150 * Example:
1151 * S!DRAWING FORMAT!ASSY!CIRCLE!2!251744 1!-2488.00!1100.00!240.00!240.00!0!!!!!!
1152 *
1153 * Although this is a circle, we treat it as an 360 degree arc.
1154 * This is because files can contain circles in both forms and the arc form
1155 * is more convenient for directly adding to SHAPE_POLY_SET when needed.
1156 *
1157 * It will be identified as a circle based on the 'shape' field, and turned
1158 * back into a circle when needed (or used as an arc if it is part of a polygon).
1159 */
1160
1161 std::unique_ptr<GRAPHIC_ARC> new_circle = std::make_unique<GRAPHIC_ARC>();
1162
1163 new_circle->shape = GR_SHAPE_CIRCLE;
1164
1165 const VECTOR2I center{
1166 KiROUND( readDouble( aData.graphic_data1 ) * aScale ),
1167 -KiROUND( readDouble( aData.graphic_data2 ) * aScale ),
1168 };
1169 const VECTOR2I size{ KiROUND( readDouble( aData.graphic_data3 ) * aScale ),
1170 KiROUND( readDouble( aData.graphic_data4 ) * aScale ) };
1171
1172 if( size.x != size.y )
1173 {
1174 wxLogError( _( "Circle with unequal x and y radii (x=%d, y=%d)" ), size.x, size.y );
1175 return nullptr;
1176 }
1177
1178 new_circle->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1179
1180 new_circle->radius = size.x / 2;
1181
1182 // Fake up a 360 degree arc
1183 const VECTOR2I start = center - VECTOR2I{ new_circle->radius, 0 };
1184 const VECTOR2I mid = center + VECTOR2I{ new_circle->radius, 0 };
1185
1186 new_circle->start_x = start.x;
1187 new_circle->start_y = start.y;
1188
1189 new_circle->end_x = start.x;
1190 new_circle->end_y = start.y;
1191
1192 new_circle->center_x = center.x;
1193 new_circle->center_y = center.y;
1194
1195 new_circle->clockwise = true;
1196
1197 new_circle->result = SHAPE_ARC{ start, mid, start, 0 };
1198
1199 return new_circle.release();
1200}
1201
1202
1204 double aScale )
1205{
1206 /*
1207 * Examples:
1208 * S!ROUTE KEEPOUT!BOTTOM!RECTANGLE!259!10076 1!-90.00!-1000.00!-60.00!-990.00!1!!!!!!
1209 */
1210
1211 GRAPHIC_RECTANGLE* new_rect = new GRAPHIC_RECTANGLE;
1212
1213 new_rect->shape = GR_SHAPE_RECTANGLE;
1214 new_rect->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1215 new_rect->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1216 new_rect->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1217 new_rect->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1218 new_rect->fill = aData.graphic_data5 == "1";
1219 new_rect->width = 0;
1220
1221 return new_rect;
1222}
1223
1224
1226 double aScale )
1227{
1228 /*
1229 * Examples:
1230 * S!MANUFACTURING!NCLEGEND-1-10!FIG_RECTANGLE!6!8318 1!4891.50!1201.00!35.43!26.57!0!!!!!!
1231 */
1232
1233 auto new_rect = std::make_unique<GRAPHIC_RECTANGLE>();
1234
1235 const int center_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1236 const int center_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1237
1238 const int size_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1239 const int size_y = KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1240
1241 new_rect->shape = GR_SHAPE_RECTANGLE;
1242 new_rect->start_x = center_x - size_x / 2;
1243 new_rect->start_y = center_y + size_y / 2;
1244 new_rect->end_x = center_x + size_x / 2;
1245 new_rect->end_y = center_y - size_y / 2;
1246 new_rect->fill = aData.graphic_data5 == "1";
1247 new_rect->width = 0;
1248
1249 return new_rect.release();
1250}
1251
1252
1254 double aScale )
1255{
1256 /*
1257 * Example:
1258 * S!DRAWING FORMAT!ASSY!SQUARE!5!250496 1!4813.08!2700.00!320.00!320.00!0!!!!!!
1259 */
1260
1261 // This appears to be identical to a FIG_RECTANGLE
1262 return processFigRectangle( aData, aScale );
1263}
1264
1265
1267 double aScale )
1268{
1269 /*
1270 * Examples:
1271 * S!DRAWING FORMAT!ASSY!OBLONG_X!11!250497 1!4449.08!2546.40!240.00!64.00!0!!!!!!
1272 * S!DRAWING FORMAT!ASSY!OBLONG_Y!12!251256 1!15548.68!1900.00!280.00!720.00!0!!!!!!
1273 */
1274 auto new_oblong = std::make_unique<GRAPHIC_OBLONG>();
1275
1276 new_oblong->shape = GR_SHAPE_OBLONG;
1277 new_oblong->oblong_x = aData.graphic_dataname == "OBLONG_X";
1278 new_oblong->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1279 new_oblong->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1280 new_oblong->size_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1281 new_oblong->size_y = KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1282
1283 // Unclear if this is fill or width
1284 new_oblong->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1285
1286 return new_oblong.release();
1287}
1288
1289
1291 double aScale )
1292{
1293 /*
1294 * Examples:
1295 * S!MANUFACTURING!NCLEGEND-1-6!TRIANGLE_1!18!252565 1!-965.00!5406.00!125.00!125.00!0!!!!!!
1296 * S!MANUFACTURING!NCLEGEND-1-6!DIAMOND!7!252566 1!-965.00!5656.00!63.00!63.00!0!!!!!!
1297 * S!MANUFACTURING!NCLEGEND-1-6!OCTAGON!3!252567 1!-965.00!5906.00!40.00!40.00!0!!!!!!
1298 * S!MANUFACTURING!NCLEGEND-1-6!HEXAGON_Y!16!252568 1!-965.00!6156.00!35.00!35.00!0!!!!!!
1299 * S!MANUFACTURING!NCLEGEND-1-6!HEXAGON_X!15!252569 1!-965.00!6406.00!12.00!12.00!0!!!!!!
1300 */
1301
1302 const VECTOR2D c{
1303 readDouble( aData.graphic_data1 ) * aScale,
1304 -readDouble( aData.graphic_data2 ) * aScale,
1305 };
1306
1307 const VECTOR2D s{
1308 readDouble( aData.graphic_data3 ) * aScale,
1309 readDouble( aData.graphic_data4 ) * aScale,
1310 };
1311
1312 if( s.x != s.y )
1313 {
1314 wxLogError( _( "Expected x and y to be the same, got x = %f and y = %f " ), s.x, s.y );
1315 }
1316
1317 auto new_poly = std::make_unique<GRAPHIC_POLYGON>();
1318 new_poly->shape = GR_SHAPE_POLYGON;
1319 new_poly->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1320
1321 int radius = s.x / 2;
1322 bool across_corners = true;
1323 EDA_ANGLE pt0_angle = ANGLE_90; // /Pointing up
1324 int n_pts = 0;
1325
1326 if( aData.graphic_dataname == "TRIANGLE_1" )
1327 {
1328 // Upright equilateral triangle (pointing upwards, horizontal base)
1329 // The size appears to be (?) the size of the circumscribing circle,
1330 // rather than the width of the base.
1331 n_pts = 3;
1332 }
1333 else if( aData.graphic_dataname == "DIAMOND" )
1334 {
1335 // Square diamond (can it be non-square?)
1336 // Size is point-to-point width/height
1337 n_pts = 4;
1338 }
1339 else if( aData.graphic_dataname == "HEXAGON_X" )
1340 {
1341 // Hexagon with horizontal top/bottom
1342 // Size is the overall width (across corners)
1343 n_pts = 6;
1344 pt0_angle = ANGLE_0;
1345 }
1346 else if( aData.graphic_dataname == "HEXAGON_Y" )
1347 {
1348 // Hexagon with vertical left/right sides
1349 // Size is the height (i.e. across corners)
1350 n_pts = 6;
1351 }
1352 else if( aData.graphic_dataname == "OCTAGON" )
1353 {
1354 // Octagon with horizontal/vertical sides
1355 // Size is the overall width (across flats)
1356 across_corners = false;
1357 pt0_angle = FULL_CIRCLE / 16;
1358 n_pts = 8;
1359 }
1360 else
1361 {
1362 wxCHECK_MSG( false, nullptr,
1363 wxString::Format( "Unhandled polygon type: %s", aData.graphic_dataname ) );
1364 }
1365
1366 new_poly->m_pts =
1367 KIGEOM::MakeRegularPolygonPoints( c, n_pts, radius, across_corners, pt0_angle );
1368 return new_poly.release();
1369}
1370
1371
1373 double aScale )
1374{
1375 /*
1376 * Examples:
1377 * S!MANUFACTURING!NCLEGEND-1-6!CROSS!4!252571 1!-965.00!6906.00!6.00!6.00!0!!!!!!
1378 */
1379 auto new_cross = std::make_unique<GRAPHIC_CROSS>();
1380
1381 new_cross->shape = GR_SHAPE_CROSS;
1382 new_cross->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1383 new_cross->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1384 new_cross->size_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1385 new_cross->size_y = KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1386 new_cross->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1387
1388 return new_cross.release();
1389}
1390
1391
1393 double aScale )
1394{
1395 GRAPHIC_TEXT* new_text = new GRAPHIC_TEXT;
1396
1397 new_text->shape = GR_SHAPE_TEXT;
1398 new_text->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1399 new_text->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1400 new_text->rotation = KiROUND( readDouble( aData.graphic_data3 ) );
1401 new_text->mirror = ( aData.graphic_data4 == "YES" );
1402
1403 if( aData.graphic_data5 == "RIGHT" )
1404 new_text->orient = GR_TEXT_H_ALIGN_RIGHT;
1405 else if( aData.graphic_data5 == "CENTER" )
1406 new_text->orient = GR_TEXT_H_ALIGN_CENTER;
1407 else
1408 new_text->orient = GR_TEXT_H_ALIGN_LEFT;
1409
1410 std::vector<std::string> toks = split( aData.graphic_data6, " \t" );
1411
1412 if( toks.size() < 8 )
1413 {
1414 // We log the error here but continue in the case of too few tokens
1415 wxLogError( _( "Invalid token count. Expected 8 but found %zu." ), toks.size() );
1416 new_text->height = 0;
1417 new_text->width = 0;
1418 new_text->ital = false;
1419 new_text->thickness = 0;
1420 }
1421 else
1422 {
1423 // 0 = size
1424 // 1 = font
1425 new_text->height = KiROUND( readDouble( toks[2] ) * aScale );
1426 new_text->width = KiROUND( readDouble( toks[3] ) * aScale );
1427 new_text->ital = readDouble( toks[4] ) != 0.0;
1428 // 5 = character spacing
1429 // 6 = line spacing
1430 new_text->thickness = KiROUND( readDouble( toks[7] ) * aScale );
1431 }
1432
1433 new_text->text = aData.graphic_data7;
1434 return new_text;
1435}
1436
1437
1439{
1440 GRAPHIC_ITEM* retval = nullptr;
1441
1442 if( aData.graphic_dataname == "LINE" )
1443 retval = processLine( aData, aScale );
1444 else if( aData.graphic_dataname == "ARC" )
1445 retval = processArc( aData, aScale );
1446 else if( aData.graphic_dataname == "CIRCLE" )
1447 retval = processCircle( aData, aScale );
1448 else if( aData.graphic_dataname == "RECTANGLE" )
1449 retval = processRectangle( aData, aScale );
1450 else if( aData.graphic_dataname == "FIG_RECTANGLE" )
1451 retval = processFigRectangle( aData, aScale );
1452 else if( aData.graphic_dataname == "SQUARE" )
1453 retval = processSquare( aData, aScale );
1454 else if( aData.graphic_dataname == "OBLONG_X" || aData.graphic_dataname == "OBLONG_Y" )
1455 retval = processOblong( aData, aScale );
1456 else if( aData.graphic_dataname == "TRIANGLE_1" || aData.graphic_dataname == "DIAMOND"
1457 || aData.graphic_dataname == "HEXAGON_X" || aData.graphic_dataname == "HEXAGON_Y"
1458 || aData.graphic_dataname == "OCTAGON" )
1459 retval = processPolygon( aData, aScale );
1460 else if( aData.graphic_dataname == "CROSS" )
1461 retval = processCross( aData, aScale );
1462 else if( aData.graphic_dataname == "TEXT" )
1463 retval = processText( aData, aScale );
1464
1465 if( retval && !aData.graphic_data10.empty() )
1466 {
1467 if( aData.graphic_data10 == "CONNECT" )
1468 retval->type = GR_TYPE_CONNECT;
1469 else if( aData.graphic_data10 == "NOTCONNECT" )
1470 retval->type = GR_TYPE_NOTCONNECT;
1471 else if( aData.graphic_data10 == "SHAPE" )
1472 retval->type = GR_TYPE_NOTCONNECT;
1473 else if( aData.graphic_data10 == "VOID" )
1474 retval->type = GR_TYPE_NOTCONNECT;
1475 else if( aData.graphic_data10 == "POLYGON" )
1476 retval->type = GR_TYPE_NOTCONNECT;
1477 else
1478 retval->type = GR_TYPE_NONE;
1479 }
1480
1481 return retval;
1482}
1483
1484
1490size_t FABMASTER::processGeometry( size_t aRow )
1491{
1492 size_t rownum = aRow + 2;
1493
1494 if( rownum >= rows.size() )
1495 return -1;
1496
1497 const single_row& header = rows[aRow];
1498 double scale_factor = processScaleFactor( aRow + 1 );
1499
1500 if( scale_factor <= 0.0 )
1501 return -1;
1502
1503 int geo_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1504 int geo_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1505 int geo_tag_col = getColFromName( aRow, "RECORDTAG" );
1506 int geo_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1507 int geo_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1508 int geo_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1509 int geo_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1510 int geo_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1511 int geo_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1512 int geo_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1513 int geo_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1514 int geo_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1515 int geo_subclass_col = getColFromName( aRow, "SUBCLASS" );
1516 int geo_sym_name_col = getColFromName( aRow, "SYMNAME" );
1517 int geo_refdes_col = getColFromName( aRow, "REFDES" );
1518
1519 if( geo_name_col < 0 || geo_num_col < 0 || geo_grdata1_col < 0 || geo_grdata2_col < 0
1520 || geo_grdata3_col < 0 || geo_grdata4_col < 0 || geo_grdata5_col < 0
1521 || geo_grdata6_col < 0 || geo_grdata7_col < 0 || geo_grdata8_col < 0
1522 || geo_grdata9_col < 0 || geo_subclass_col < 0 || geo_sym_name_col < 0
1523 || geo_refdes_col < 0 )
1524 return -1;
1525
1526 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1527 {
1528 const single_row& row = rows[rownum];
1529
1530 if( row.size() != header.size() )
1531 {
1532 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1533 rownum,
1534 header.size(),
1535 row.size() );
1536 continue;
1537 }
1538
1539 auto& geo_tag = row[geo_tag_col];
1540
1541 GRAPHIC_DATA gr_data;
1542 gr_data.graphic_dataname = row[geo_name_col];
1543 gr_data.graphic_datanum = row[geo_num_col];
1544 gr_data.graphic_data1 = row[geo_grdata1_col];
1545 gr_data.graphic_data2 = row[geo_grdata2_col];
1546 gr_data.graphic_data3 = row[geo_grdata3_col];
1547 gr_data.graphic_data4 = row[geo_grdata4_col];
1548 gr_data.graphic_data5 = row[geo_grdata5_col];
1549 gr_data.graphic_data6 = row[geo_grdata6_col];
1550 gr_data.graphic_data7 = row[geo_grdata7_col];
1551 gr_data.graphic_data8 = row[geo_grdata8_col];
1552 gr_data.graphic_data9 = row[geo_grdata9_col];
1553
1554 auto& geo_refdes = row[geo_refdes_col];
1555
1556 // Grouped graphics are a series of records with the same record ID but incrementing
1557 // Sequence numbers.
1558 int id = -1;
1559 int seq = -1;
1560 int subseq = 0;
1561
1562 if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1563 {
1564 wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
1565 geo_tag.c_str(),
1566 rownum );
1567 continue;
1568 }
1569
1570 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1571
1572 if( !gr_item )
1573 {
1574 wxLogDebug( wxT( "Unhandled graphic item '%s' in row %zu." ),
1575 gr_data.graphic_dataname.c_str(),
1576 geo_tag.c_str(),
1577 rownum );
1578 continue;
1579 }
1580
1581 gr_item->layer = row[geo_subclass_col];
1582 gr_item->seq = seq;
1583 gr_item->subseq = subseq;
1584
1585 if( geo_refdes.empty() )
1586 {
1587 if( board_graphics.empty() || board_graphics.back().id != id )
1588 {
1589 GEOM_GRAPHIC new_gr;
1590 new_gr.subclass = row[geo_subclass_col];
1591 new_gr.refdes = row[geo_refdes_col];
1592 new_gr.name = row[geo_sym_name_col];
1593 new_gr.id = id;
1594 new_gr.elements = std::make_unique<graphic_element>();
1595 board_graphics.push_back( std::move( new_gr ) );
1596 }
1597
1598 GEOM_GRAPHIC& graphic = board_graphics.back();
1599 graphic.elements->emplace( std::move( gr_item ) );
1600 }
1601 else
1602 {
1603 auto sym_gr_it = comp_graphics.emplace( geo_refdes,
1604 std::map<int, GEOM_GRAPHIC>{} );
1605 auto map_it = sym_gr_it.first->second.emplace( id, GEOM_GRAPHIC{} );
1606 auto& gr = map_it.first;
1607
1608 if( map_it.second )
1609 {
1610 gr->second.subclass = row[geo_subclass_col];
1611 gr->second.refdes = row[geo_refdes_col];
1612 gr->second.name = row[geo_sym_name_col];
1613 gr->second.id = id;
1614 gr->second.elements = std::make_unique<graphic_element>();
1615 }
1616
1617 auto result = gr->second.elements->emplace( std::move( gr_item ) );
1618 }
1619 }
1620
1621 return rownum - aRow;
1622}
1623
1624
1628size_t FABMASTER::processVias( size_t aRow )
1629{
1630 size_t rownum = aRow + 2;
1631
1632 if( rownum >= rows.size() )
1633 return -1;
1634
1635 const single_row& header = rows[aRow];
1636 double scale_factor = processScaleFactor( aRow + 1 );
1637
1638 if( scale_factor <= 0.0 )
1639 return -1;
1640
1641 int viax_col = getColFromName( aRow, "VIAX" );
1642 int viay_col = getColFromName( aRow, "VIAY" );
1643 int padstack_name_col = getColFromName( aRow, "PADSTACKNAME" );
1644 int net_name_col = getColFromName( aRow, "NETNAME" );
1645 int test_point_col = getColFromName( aRow, "TESTPOINT" );
1646
1647 if( viax_col < 0 || viay_col < 0 || padstack_name_col < 0 || net_name_col < 0
1648 || test_point_col < 0 )
1649 return -1;
1650
1651 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1652 {
1653 const single_row& row = rows[rownum];
1654
1655 if( row.size() != header.size() )
1656 {
1657 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1658 rownum,
1659 header.size(),
1660 row.size() );
1661 continue;
1662 }
1663
1664 vias.emplace_back( std::make_unique<FM_VIA>() );
1665 auto& via = vias.back();
1666
1667 via->x = KiROUND( readDouble( row[viax_col] ) * scale_factor );
1668 via->y = -KiROUND( readDouble( row[viay_col] ) * scale_factor );
1669 via->padstack = row[padstack_name_col];
1670 via->net = row[net_name_col];
1671 via->test_point = ( row[test_point_col] == "YES" );
1672 }
1673
1674 return rownum - aRow;
1675}
1676
1677
1683size_t FABMASTER::processTraces( size_t aRow )
1684{
1685 size_t rownum = aRow + 2;
1686
1687 if( rownum >= rows.size() )
1688 return -1;
1689
1690 const single_row& header = rows[aRow];
1691 double scale_factor = processScaleFactor( aRow + 1 );
1692
1693 if( scale_factor <= 0.0 )
1694 return -1;
1695
1696 int class_col = getColFromName( aRow, "CLASS" );
1697 int layer_col = getColFromName( aRow, "SUBCLASS" );
1698 int grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1699 int grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1700 int tag_col = getColFromName( aRow, "RECORDTAG" );
1701 int grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1702 int grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1703 int grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1704 int grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1705 int grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1706 int grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1707 int grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1708 int grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1709 int grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1710 int netname_col = getColFromName( aRow, "NETNAME" );
1711
1712 if( class_col < 0 || layer_col < 0 || grdata_name_col < 0 || grdata_num_col < 0
1713 || tag_col < 0 || grdata1_col < 0 || grdata2_col < 0 || grdata3_col < 0
1714 || grdata4_col < 0 || grdata5_col < 0 || grdata6_col < 0 || grdata7_col < 0
1715 || grdata8_col < 0 || grdata9_col < 0 || netname_col < 0 )
1716 return -1;
1717
1718 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1719 {
1720 const single_row& row = rows[rownum];
1721
1722 if( row.size() != header.size() )
1723 {
1724 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1725 rownum,
1726 header.size(),
1727 row.size() );
1728 continue;
1729 }
1730
1731 GRAPHIC_DATA gr_data;
1732 gr_data.graphic_dataname = row[grdata_name_col];
1733 gr_data.graphic_datanum = row[grdata_num_col];
1734 gr_data.graphic_data1 = row[grdata1_col];
1735 gr_data.graphic_data2 = row[grdata2_col];
1736 gr_data.graphic_data3 = row[grdata3_col];
1737 gr_data.graphic_data4 = row[grdata4_col];
1738 gr_data.graphic_data5 = row[grdata5_col];
1739 gr_data.graphic_data6 = row[grdata6_col];
1740 gr_data.graphic_data7 = row[grdata7_col];
1741 gr_data.graphic_data8 = row[grdata8_col];
1742 gr_data.graphic_data9 = row[grdata9_col];
1743
1744 const std::string& geo_tag = row[tag_col];
1745 // Grouped graphics are a series of records with the same record ID but incrementing
1746 // Sequence numbers.
1747 int id = -1;
1748 int seq = -1;
1749 int subseq = 0;
1750
1751 if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1752 {
1753 wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
1754 geo_tag.c_str(),
1755 rownum );
1756 continue;
1757 }
1758
1759 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1760
1761 if( !gr_item )
1762 {
1763 wxLogTrace( traceFabmaster, _( "Unhandled graphic item '%s' in row %zu." ),
1764 gr_data.graphic_dataname.c_str(),
1765 rownum );
1766 continue;
1767 }
1768
1769 auto new_trace = std::make_unique<TRACE>();
1770 new_trace->id = id;
1771 new_trace->layer = row[layer_col];
1772 new_trace->netname = row[netname_col];
1773 new_trace->lclass = row[class_col];
1774
1775 gr_item->layer = row[layer_col];
1776 gr_item->seq = seq;
1777 gr_item->subseq = subseq;
1778
1779 // Collect the reference designator positions for the footprints later
1780 if( new_trace->lclass == "REF DES" )
1781 {
1782 auto result = refdes.emplace( std::move( new_trace ) );
1783 auto& ref = *result.first;
1784 ref->segment.emplace( std::move( gr_item ) );
1785 }
1786 else if( new_trace->lclass == "DEVICE TYPE" || new_trace->lclass == "COMPONENT VALUE"
1787 || new_trace->lclass == "TOLERANCE" )
1788 {
1789 // TODO: This seems like a value field, but it's not immediately clear how to map it
1790 // to the right footprint.
1791 // So these spam the board with huge amount of overlapping text.
1792
1793 // Examples:
1794 // S!DEVICE TYPE!SILKSCREEN_BOTTOM!TEXT!260!255815 1!2725.00!1675.00!270.000!YES!LEFT!45 0 60.00 48.00 0.000 0.00 0.00 0.00!CAP_0.1UF_X5R_6.3V_20% 0201 _40!!!!
1795 // S!DEVICE TYPE!ASSEMBLY_BOTTOM!TEXT!260!255816 1!2725.00!1675.00!270.000!YES!LEFT!45 0 60.00 48.00 0.000 0.00 0.00 0.00!CAP_0.1UF_X5R_6.3V_20% 0201 _40!!!!
1796 // S!COMPONENT VALUE!SILKSCREEN_BOTTOM!TEXT!260!18949 1!361.665!1478.087!270.000!YES!LEFT!31 0 30.000 20.000 0.000 6.000 31.000 6.000!0.01uF!!!!
1797
1798 // For now, just don't do anything with them.
1799 }
1800 else if( gr_item->width == 0 )
1801 {
1802 auto result = zones.emplace( std::move( new_trace ) );
1803 auto& zone = *result.first;
1804 auto gr_result = zone->segment.emplace( std::move( gr_item ) );
1805
1806 if( !gr_result.second )
1807 {
1808 wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
1809 id,
1810 seq,
1811 rownum );
1812 }
1813 }
1814 else
1815 {
1816 auto result = traces.emplace( std::move( new_trace ) );
1817 auto& trace = *result.first;
1818 auto gr_result = trace->segment.emplace( std::move( gr_item ) );
1819
1820 if( !gr_result.second )
1821 {
1822 wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
1823 id,
1824 seq,
1825 rownum );
1826 }
1827 }
1828 }
1829
1830 return rownum - aRow;
1831}
1832
1833
1834FABMASTER::SYMTYPE FABMASTER::parseSymType( const std::string& aSymType )
1835{
1836 if( aSymType == "PACKAGE" )
1837 return SYMTYPE_PACKAGE;
1838 else if( aSymType == "DRAFTING")
1839 return SYMTYPE_DRAFTING;
1840 else if( aSymType == "MECHANICAL" )
1841 return SYMTYPE_MECH;
1842 else if( aSymType == "FORMAT" )
1843 return SYMTYPE_FORMAT;
1844
1845 return SYMTYPE_NONE;
1846}
1847
1848
1850{
1851 if( aCmpClass == "IO" )
1852 return COMPCLASS_IO;
1853 else if( aCmpClass == "IC" )
1854 return COMPCLASS_IC;
1855 else if( aCmpClass == "DISCRETE" )
1856 return COMPCLASS_DISCRETE;
1857
1858 return COMPCLASS_NONE;
1859}
1860
1861
1866size_t FABMASTER::processFootprints( size_t aRow )
1867{
1868 size_t rownum = aRow + 2;
1869
1870 if( rownum >= rows.size() )
1871 return -1;
1872
1873 const single_row& header = rows[aRow];
1874 double scale_factor = processScaleFactor( aRow + 1 );
1875
1876 if( scale_factor <= 0.0 )
1877 return -1;
1878
1879 int refdes_col = getColFromName( aRow, "REFDES" );
1880 int compclass_col = getColFromName( aRow, "COMPCLASS" );
1881 int comppartnum_col = getColFromName( aRow, "COMPPARTNUMBER" );
1882 int compheight_col = getColFromName( aRow, "COMPHEIGHT" );
1883 int compdevlabelcol = getColFromName( aRow, "COMPDEVICELABEL" );
1884 int compinscode_col = getColFromName( aRow, "COMPINSERTIONCODE" );
1885 int symtype_col = getColFromName( aRow, "SYMTYPE" );
1886 int symname_col = getColFromName( aRow, "SYMNAME" );
1887 int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1888 int symrotate_col = getColFromName( aRow, "SYMROTATE" );
1889 int symx_col = getColFromName( aRow, "SYMX" );
1890 int symy_col = getColFromName( aRow, "SYMY" );
1891 int compvalue_col = getColFromName( aRow, "COMPVALUE" );
1892 int comptol_col = getColFromName( aRow, "COMPTOL" );
1893 int compvolt_col = getColFromName( aRow, "COMPVOLTAGE" );
1894
1895 if( refdes_col < 0 || compclass_col < 0 || comppartnum_col < 0 || compheight_col < 0
1896 || compdevlabelcol < 0 || compinscode_col < 0 || symtype_col < 0 || symname_col < 0
1897 || symmirror_col < 0 || symrotate_col < 0 || symx_col < 0 || symy_col < 0
1898 || compvalue_col < 0 || comptol_col < 0 || compvolt_col < 0 )
1899 return -1;
1900
1901 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1902 {
1903 const single_row& row = rows[rownum];
1904
1905 if( row.size() != header.size() )
1906 {
1907 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1908 rownum,
1909 header.size(),
1910 row.size() );
1911 continue;
1912 }
1913
1914 const wxString& refdes = row[refdes_col];
1915
1916 if( row[symx_col].empty() || row[symy_col].empty() || row[symrotate_col].empty() )
1917 {
1918 wxLogError( _( "Missing X, Y, or rotation data in row %zu for refdes %s. "
1919 "This may be an unplaced component." ),
1920 rownum, refdes );
1921 continue;
1922 }
1923
1924 auto cmp = std::make_unique<COMPONENT>();
1925
1926 cmp->refdes = refdes;
1927 cmp->cclass = parseCompClass( row[compclass_col] );
1928 cmp->pn = row[comppartnum_col];
1929 cmp->height = row[compheight_col];
1930 cmp->dev_label = row[compdevlabelcol];
1931 cmp->insert_code = row[compinscode_col];
1932 cmp->type = parseSymType( row[symtype_col] );
1933 cmp->name = row[symname_col];
1934 cmp->mirror = ( row[symmirror_col] == "YES" );
1935 cmp->rotate = readDouble( row[symrotate_col] );
1936 cmp->x = KiROUND( readDouble( row[symx_col] ) * scale_factor );
1937 cmp->y = -KiROUND( readDouble( row[symy_col] ) * scale_factor );
1938 cmp->value = row[compvalue_col];
1939 cmp->tol = row[comptol_col];
1940 cmp->voltage = row[compvolt_col];
1941
1942 auto vec = components.find( cmp->refdes );
1943
1944 if( vec == components.end() )
1945 {
1946 auto retval = components.insert( std::make_pair( cmp->refdes, std::vector<std::unique_ptr<COMPONENT>>{} ) );
1947
1948 vec = retval.first;
1949 }
1950
1951 vec->second.push_back( std::move( cmp ) );
1952 }
1953
1954 return rownum - aRow;
1955}
1956
1957
1962size_t FABMASTER::processPins( size_t aRow )
1963{
1964 size_t rownum = aRow + 2;
1965
1966 if( rownum >= rows.size() )
1967 return -1;
1968
1969 const single_row& header = rows[aRow];
1970 double scale_factor = processScaleFactor( aRow + 1 );
1971
1972 if( scale_factor <= 0.0 )
1973 return -1;
1974
1975 int symname_col = getColFromName( aRow, "SYMNAME" );
1976 int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1977 int pinname_col = getColFromName( aRow, "PINNAME" );
1978 int pinnum_col = getColFromName( aRow, "PINNUMBER" );
1979 int pinx_col = getColFromName( aRow, "PINX" );
1980 int piny_col = getColFromName( aRow, "PINY" );
1981 int padstack_col = getColFromName( aRow, "PADSTACKNAME" );
1982 int refdes_col = getColFromName( aRow, "REFDES" );
1983 int pinrot_col = getColFromName( aRow, "PINROTATION" );
1984 int testpoint_col = getColFromName( aRow, "TESTPOINT" );
1985
1986 if( symname_col < 0 ||symmirror_col < 0 || pinname_col < 0 || pinnum_col < 0 || pinx_col < 0
1987 || piny_col < 0 || padstack_col < 0 || refdes_col < 0 || pinrot_col < 0
1988 || testpoint_col < 0 )
1989 return -1;
1990
1991 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1992 {
1993 const single_row& row = rows[rownum];
1994
1995 if( row.size() != header.size() )
1996 {
1997 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1998 rownum,
1999 header.size(),
2000 row.size() );
2001 continue;
2002 }
2003
2004 auto pin = std::make_unique<PIN>();
2005
2006 pin->name = row[symname_col];
2007 pin->mirror = ( row[symmirror_col] == "YES" );
2008 pin->pin_name = row[pinname_col];
2009 pin->pin_number = row[pinnum_col];
2010 pin->pin_x = KiROUND( readDouble( row[pinx_col] ) * scale_factor );
2011 pin->pin_y = -KiROUND( readDouble( row[piny_col] ) * scale_factor );
2012 pin->padstack = row[padstack_col];
2013 pin->refdes = row[refdes_col];
2014 pin->rotation = readDouble( row[pinrot_col] );
2015
2016 auto map_it = pins.find( pin->refdes );
2017
2018 if( map_it == pins.end() )
2019 {
2020 auto retval = pins.insert( std::make_pair( pin->refdes, std::set<std::unique_ptr<PIN>,
2021 PIN::BY_NUM>{} ) );
2022 map_it = retval.first;
2023 }
2024
2025 map_it->second.insert( std::move( pin ) );
2026 }
2027
2028 return rownum - aRow;
2029}
2030
2031
2035size_t FABMASTER::processNets( size_t aRow )
2036{
2037 size_t rownum = aRow + 2;
2038
2039 if( rownum >= rows.size() )
2040 return -1;
2041
2042 const single_row& header = rows[aRow];
2043 double scale_factor = processScaleFactor( aRow + 1 );
2044
2045 if( scale_factor <= 0.0 )
2046 return -1;
2047
2048 int netname_col = getColFromName( aRow, "NETNAME" );
2049 int refdes_col = getColFromName( aRow, "REFDES" );
2050 int pinnum_col = getColFromName( aRow, "PINNUMBER" );
2051 int pinname_col = getColFromName( aRow, "PINNAME" );
2052 int pingnd_col = getColFromName( aRow, "PINGROUND" );
2053 int pinpwr_col = getColFromName( aRow, "PINPOWER" );
2054
2055 if( netname_col < 0 || refdes_col < 0 || pinnum_col < 0 || pinname_col < 0 || pingnd_col < 0
2056 || pinpwr_col < 0 )
2057 return -1;
2058
2059 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
2060 {
2061 const single_row& row = rows[rownum];
2062
2063 if( row.size() != header.size() )
2064 {
2065 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
2066 rownum,
2067 header.size(),
2068 row.size() );
2069 continue;
2070 }
2071
2072 NETNAME new_net;
2073 new_net.name = row[netname_col];
2074 new_net.refdes = row[refdes_col];
2075 new_net.pin_num = row[pinnum_col];
2076 new_net.pin_name = row[pinname_col];
2077 new_net.pin_gnd = ( row[pingnd_col] == "YES" );
2078 new_net.pin_pwr = ( row[pinpwr_col] == "YES" );
2079
2080 pin_nets.emplace( std::make_pair( new_net.refdes, new_net.pin_num ), new_net );
2081 netnames.insert( row[netname_col] );
2082 }
2083
2084 return rownum - aRow;
2085}
2086
2087
2089{
2090
2091 for( size_t i = 0; i < rows.size(); )
2092 {
2093 auto type = detectType( i );
2094
2095 switch( type )
2096 {
2097 case EXTRACT_PADSTACKS:
2098 {
2102 assignLayers();
2103 int retval = processPadStacks( i );
2104
2105 i += std::max( retval, 1 );
2106 break;
2107 }
2108
2110 {
2111 int retval = processLayers( i );
2112
2113 i += std::max( retval, 1 );
2114 break;
2115 }
2116
2118 {
2119 int retval = processSimpleLayers( i );
2120
2121 i += std::max( retval, 1 );
2122 break;
2123 }
2124
2125 case EXTRACT_VIAS:
2126 {
2127 int retval = processVias( i );
2128
2129 i += std::max( retval, 1 );
2130 break;
2131 }
2132
2133 case EXTRACT_TRACES:
2134 {
2135 int retval = processTraces( i );
2136
2137 i += std::max( retval, 1 );
2138 break;
2139 }
2140
2141 case EXTRACT_REFDES:
2142 {
2143 int retval = processFootprints( i );
2144
2145 i += std::max( retval, 1 );
2146 break;
2147 }
2148
2149 case EXTRACT_NETS:
2150 {
2151 int retval = processNets( i );
2152
2153 i += std::max( retval, 1 );
2154 break;
2155 }
2156
2157 case EXTRACT_GRAPHICS:
2158 {
2159 int retval = processGeometry( i );
2160
2161 i += std::max( retval, 1 );
2162 break;
2163 }
2164
2165 case EXTRACT_PINS:
2166 {
2167 int retval = processPins( i );
2168
2169 i += std::max( retval, 1 );
2170 break;
2171 }
2172
2173 case EXTRACT_PAD_SHAPES:
2174 {
2175 int retval = processCustomPads( i );
2176
2177 i += std::max( retval, 1 );
2178 break;
2179 }
2180
2181 default:
2182 ++i;
2183 break;
2184 }
2185
2186 }
2187
2188 return true;
2189}
2190
2191
2193{
2194 for( auto& zone : zones )
2195 {
2196 checkpoint();
2197
2198 if( IsCopperLayer( getLayer( zone->layer ) ) || zone->layer == "ALL" )
2199 {
2200 loadZone( aBoard, zone );
2201 }
2202 else
2203 {
2204 if( zone->layer == "OUTLINE" || zone->layer == "DESIGN_OUTLINE" )
2205 {
2206 loadOutline( aBoard, zone );
2207 }
2208 else
2209 {
2210 loadPolygon( aBoard, zone );
2211 }
2212 }
2213 }
2214
2225 std::set<ZONE*> zones_to_delete;
2226
2227 for( auto zone : aBoard->Zones() )
2228 {
2230 if( zone->GetNetCode() > 0 )
2231 {
2232 zones_to_delete.insert( zone );
2233 }
2234 }
2235
2236 for( auto zone1 : aBoard->Zones() )
2237 {
2239 if( zone1->GetNetCode() > 0 )
2240 continue;
2241
2242 SHAPE_LINE_CHAIN& outline1 = zone1->Outline()->Outline( 0 );
2243 std::vector<size_t> overlaps( aBoard->GetNetInfo().GetNetCount() + 1, 0 );
2244 std::vector<std::vector<ZONE*>> possible_deletions( overlaps.size() );
2245
2246 for( auto zone2 : aBoard->Zones() )
2247 {
2248 if( zone2->GetNetCode() <= 0 )
2249 continue;
2250
2251 SHAPE_LINE_CHAIN& outline2 = zone2->Outline()->Outline( 0 );
2252
2253 if( zone1->GetLayer() != zone2->GetLayer() )
2254 continue;
2255
2256 if( !outline1.BBox().Intersects( outline2.BBox() ) )
2257 continue;
2258
2259 for( auto& pt1 : outline1.CPoints() )
2260 {
2262 if( outline2.PointOnEdge( pt1, 1 ) )
2263 overlaps[ zone2->GetNetCode() ]++;
2264 }
2265
2266 for( auto& pt2 : outline2.CPoints() )
2267 {
2270 if( outline1.PointOnEdge( pt2, 1 ) )
2271 overlaps[ zone2->GetNetCode() ]++;
2272 }
2273 }
2274
2275 size_t max_net = 0;
2276 size_t max_net_id = 0;
2277
2278 for( size_t el = 1; el < overlaps.size(); ++el )
2279 {
2280 if( overlaps[el] > max_net )
2281 {
2282 max_net = overlaps[el];
2283 max_net_id = el;
2284 }
2285 }
2286
2287 if( max_net > 0 )
2288 zone1->SetNetCode( max_net_id );
2289 }
2290
2291 for( auto zone : zones_to_delete )
2292 {
2293 aBoard->Remove( zone );
2294 delete zone;
2295 }
2296
2297 return true;
2298}
2299
2300
2302 PCB_TEXT& aText, const BOARD& aBoard, const OPT_VECTOR2I& aMirrorPoint )
2303{
2304 aText.SetHorizJustify( aGText.orient );
2305
2306 aText.SetKeepUpright( false );
2307
2308 EDA_ANGLE angle = EDA_ANGLE( aGText.rotation );
2309 angle.Normalize180();
2310
2311 if( aMirrorPoint.has_value() )
2312 {
2313 aText.SetLayer( aBoard.FlipLayer( aLayer ) );
2314 aText.SetTextPos( VECTOR2I(
2315 aGText.start_x, 2 * aMirrorPoint->y - ( aGText.start_y - aGText.height / 2 ) ) );
2316 aText.SetMirrored( !aGText.mirror );
2317
2318 aText.SetTextAngle( -angle + ANGLE_180 );
2319 }
2320 else
2321 {
2322 aText.SetLayer( aLayer );
2323 aText.SetTextPos( VECTOR2I( aGText.start_x, aGText.start_y - aGText.height / 2 ) );
2324 aText.SetMirrored( aGText.mirror );
2325
2326 aText.SetTextAngle( angle );
2327 }
2328
2329 if( std::abs( angle ) >= ANGLE_90 )
2330 {
2332 }
2333
2334 aText.SetText( aGText.text );
2335 aText.SetItalic( aGText.ital );
2336 aText.SetTextThickness( aGText.thickness );
2337 aText.SetTextHeight( aGText.height );
2338 aText.SetTextWidth( aGText.width );
2339}
2340
2341
2343{
2344 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2345 const auto& ds = aBoard->GetDesignSettings();
2346
2347 for( auto& mod : components )
2348 {
2349 checkpoint();
2350
2351 bool has_multiple = mod.second.size() > 1;
2352
2353 for( int i = 0; i < mod.second.size(); ++i )
2354 {
2355 auto& src = mod.second[i];
2356
2357 FOOTPRINT* fp = new FOOTPRINT( aBoard );
2358
2359 wxString mod_ref = src->name;
2360 wxString lib_ref = m_filename.GetName();
2361
2362 if( has_multiple )
2363 mod_ref.Append( wxString::Format( wxT( "_%d" ), i ) );
2364
2365 ReplaceIllegalFileNameChars( lib_ref, '_' );
2366 ReplaceIllegalFileNameChars( mod_ref, '_' );
2367
2368 wxString key = !lib_ref.empty() ? lib_ref + wxT( ":" ) + mod_ref : mod_ref;
2369
2370 LIB_ID fpID;
2371 fpID.Parse( key, true );
2372 fp->SetFPID( fpID );
2373
2374 fp->SetPosition( VECTOR2I( src->x, src->y ) );
2375 fp->SetOrientationDegrees( -src->rotate );
2376
2377 // KiCad netlisting requires parts to have non-digit + digit annotation.
2378 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source
2379 // designator
2380 wxString reference = src->refdes;
2381
2382 if( !std::isalpha( src->refdes[0] ) )
2383 reference.Prepend( "UNK" );
2384
2385 fp->SetReference( reference );
2386
2387 fp->SetValue( src->value );
2388 fp->Value().SetLayer( F_Fab );
2389 fp->Value().SetVisible( false );
2390
2391 // Set refdes invisible until we find the text for it
2392 // (otherwise we'll plonk a default-sized ref-des on the silkscreen layer
2393 // which wasn't there in the imported file)
2394 fp->Reference().SetVisible( false );
2395
2396 for( auto& ref : refdes )
2397 {
2398 const GRAPHIC_TEXT& lsrc =
2399 static_cast<const GRAPHIC_TEXT&>( **ref->segment.begin() );
2400
2401 if( lsrc.text == src->refdes )
2402 {
2403 PCB_TEXT* txt = nullptr;
2404 PCB_LAYER_ID layer = getLayer( ref->layer );
2405
2406 if( !IsPcbLayer( layer ) )
2407 {
2408 wxLogTrace( traceFabmaster, wxS( "The layer %s is not mapped?" ),
2409 ref->layer.c_str() );
2410 continue;
2411 }
2412
2413 if( layer == F_SilkS || layer == B_SilkS )
2414 {
2415 txt = &( fp->Reference() );
2416 txt->SetVisible( true );
2417 }
2418 else
2419 {
2420 txt = new PCB_TEXT( fp );
2421 }
2422
2423 OPT_VECTOR2I flip_point = std::nullopt;
2424 if( src->mirror )
2425 flip_point = VECTOR2I( src->x, src->y );
2426
2427 const EDA_ANGLE fp_angle = EDA_ANGLE( lsrc.rotation ).Normalized();
2428 txt->SetTextAngle( fp_angle );
2429
2430 setupText( lsrc, layer, *txt, *aBoard, flip_point );
2431
2432 if( txt != &fp->Reference() )
2433 fp->Add( txt, ADD_MODE::APPEND );
2434 }
2435 }
2436
2439 fp->SetLayer( F_Cu );
2440
2441 auto gr_it = comp_graphics.find( src->refdes );
2442
2443 if( gr_it == comp_graphics.end() )
2444 {
2445 continue;
2446 //TODO: Error
2447 }
2448
2449 for( auto& gr_ref : gr_it->second )
2450 {
2451 auto& graphic = gr_ref.second;
2452
2453 for( auto& seg : *graphic.elements )
2454 {
2455 PCB_LAYER_ID layer = Dwgs_User;
2456
2457 if( IsPcbLayer( getLayer( seg->layer ) ) )
2458 layer = getLayer( seg->layer );
2459
2460 STROKE_PARAMS defaultStroke( ds.GetLineThickness( layer ) );
2461
2462 switch( seg->shape )
2463 {
2464
2465 case GR_SHAPE_LINE:
2466 {
2467 const GRAPHIC_LINE* lsrc = static_cast<const GRAPHIC_LINE*>( seg.get() );
2468
2469 PCB_SHAPE* line = new PCB_SHAPE( fp, SHAPE_T::SEGMENT );
2470
2471 if( src->mirror )
2472 {
2473 line->SetLayer( aBoard->FlipLayer( layer ) );
2474 line->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2475 line->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2476 }
2477 else
2478 {
2479 line->SetLayer( layer );
2480 line->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
2481 line->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2482 }
2483
2484 line->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
2485
2486 if( lsrc->width == 0 )
2487 line->SetStroke( defaultStroke );
2488
2489 fp->Add( line, ADD_MODE::APPEND );
2490 break;
2491 }
2492 case GR_SHAPE_CIRCLE:
2493 {
2494 const GRAPHIC_ARC& lsrc = static_cast<const GRAPHIC_ARC&>( *seg );
2495
2496 PCB_SHAPE* circle = new PCB_SHAPE( fp, SHAPE_T::CIRCLE );
2497
2498 circle->SetLayer( layer );
2499 circle->SetCenter( VECTOR2I( lsrc.center_x, lsrc.center_y ) );
2500 circle->SetEnd( VECTOR2I( lsrc.end_x, lsrc.end_y ) );
2501 circle->SetWidth( lsrc.width );
2502
2503 if( IsBackLayer( layer ) )
2504 {
2505 // Circles seem to have a flip around the FP origin that lines don't have
2506 const VECTOR2I fp_orig = fp->GetPosition();
2507 circle->Mirror( fp_orig, FLIP_DIRECTION::TOP_BOTTOM );
2508 }
2509
2510 if( lsrc.width == 0 )
2511 {
2512 // It seems that 0-width circles on DISPLAY_T/B layers are filled
2513 // (but not, say, SILKSCREEN_T/B).
2514 // There is an oblique reference to something like this here:
2515 // https://github.com/plusea/EAGLE/blob/master/ulp/fabmaster.ulp
2516 if( lsrc.layer == "DISPLAY_TOP" || lsrc.layer == "DISPLAY_BOTTOM" )
2517 circle->SetFilled( true );
2518 else
2519 circle->SetWidth( ds.GetLineThickness( circle->GetLayer() ) );
2520 }
2521
2522 if( src->mirror )
2523 circle->Flip( circle->GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
2524
2525 fp->Add( circle, ADD_MODE::APPEND );
2526 break;
2527 }
2528 case GR_SHAPE_ARC:
2529 {
2530 const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
2531
2532 std::unique_ptr<PCB_SHAPE> arc =
2533 std::make_unique<PCB_SHAPE>( fp, SHAPE_T::ARC );
2534
2535 SHAPE_ARC sarc = lsrc->result;
2536
2537 if( IsBackLayer( layer ) )
2538 {
2539 // Arcs seem to have a vertical flip around the FP origin that lines don't have
2540 // and are also flipped around their center (this is a best guess at the transformation)
2541 const VECTOR2I fp_orig = fp->GetPosition();
2542 sarc.Mirror( fp_orig, FLIP_DIRECTION::TOP_BOTTOM );
2543 sarc.Mirror( sarc.GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
2544 }
2545
2546 arc->SetLayer( layer );
2547 arc->SetArcGeometry( sarc.GetP0(), sarc.GetArcMid(), sarc.GetP1() );
2548 arc->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
2549
2550 if( lsrc->width == 0 )
2551 arc->SetStroke( defaultStroke );
2552
2553 if( src->mirror )
2554 arc->Flip( arc->GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
2555
2556 fp->Add( arc.release(), ADD_MODE::APPEND );
2557 break;
2558 }
2559 case GR_SHAPE_RECTANGLE:
2560 {
2561 const GRAPHIC_RECTANGLE *lsrc =
2562 static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2563
2564 PCB_SHAPE* rect = new PCB_SHAPE( fp, SHAPE_T::RECTANGLE );
2565
2566 if( src->mirror )
2567 {
2568 rect->SetLayer( aBoard->FlipLayer( layer ) );
2569 rect->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2570 rect->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2571 }
2572 else
2573 {
2574 rect->SetLayer( layer );
2575 rect->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
2576 rect->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2577 }
2578
2579 rect->SetStroke( defaultStroke );
2580
2581 fp->Add( rect, ADD_MODE::APPEND );
2582 break;
2583 }
2584 case GR_SHAPE_TEXT:
2585 {
2586 const GRAPHIC_TEXT& lsrc = static_cast<const GRAPHIC_TEXT&>( *seg );
2587
2588 std::unique_ptr<PCB_TEXT> txt = std::make_unique<PCB_TEXT>( fp );
2589
2590 OPT_VECTOR2I flip_point;
2591 if( src->mirror )
2592 flip_point = VECTOR2I( src->x, src->y );
2593
2594 setupText( lsrc, layer, *txt, *aBoard, flip_point );
2595
2596 // FABMASTER doesn't have visibility flags but layers that are not silk
2597 // should be hidden by default to prevent clutter.
2598 if( txt->GetLayer() != F_SilkS && txt->GetLayer() != B_SilkS )
2599 txt->SetVisible( false );
2600
2601 fp->Add( txt.release(), ADD_MODE::APPEND );
2602 break;
2603 }
2604 default:
2605 continue;
2606 }
2607 }
2608 }
2609
2610 auto pin_it = pins.find( src->refdes );
2611
2612 if( pin_it != pins.end() )
2613 {
2614 for( auto& pin : pin_it->second )
2615 {
2616 auto pin_net_it = pin_nets.find( std::make_pair( pin->refdes,
2617 pin->pin_number ) );
2618 auto padstack = pads.find( pin->padstack );
2619 std::string netname = "";
2620
2621 if( pin_net_it != pin_nets.end() )
2622 netname = pin_net_it->second.name;
2623
2624 auto net_it = netinfo.find( netname );
2625
2626 std::unique_ptr<PAD> newpad = std::make_unique<PAD>( fp );
2627
2628 if( net_it != netinfo.end() )
2629 newpad->SetNet( net_it->second );
2630 else
2631 newpad->SetNetCode( 0 );
2632
2633 newpad->SetX( pin->pin_x );
2634
2635 if( src->mirror )
2636 newpad->SetY( 2 * src->y - pin->pin_y );
2637 else
2638 newpad->SetY( pin->pin_y );
2639
2640 newpad->SetNumber( pin->pin_number );
2641
2642 if( padstack == pads.end() )
2643 {
2644 wxLogError( _( "Unable to locate padstack %s in file %s\n" ),
2645 pin->padstack.c_str(), aBoard->GetFileName().wc_str() );
2646 continue;
2647 }
2648 else
2649 {
2650 auto& pad = padstack->second;
2651
2652 newpad->SetShape( PADSTACK::ALL_LAYERS, pad.shape );
2653
2654 if( pad.shape == PAD_SHAPE::CUSTOM )
2655 {
2656 // Choose the smaller dimension to ensure the base pad
2657 // is fully hidden by the custom pad
2658 int pad_size = std::min( pad.width, pad.height );
2659
2660 newpad->SetSize( PADSTACK::ALL_LAYERS,
2661 VECTOR2I( pad_size / 2, pad_size / 2 ) );
2662
2663 std::string custom_name = pad.custom_name + "_" + pin->refdes + "_" +
2664 pin->pin_number;
2665 auto custom_it = pad_shapes.find( custom_name );
2666
2667 if( custom_it != pad_shapes.end() )
2668 {
2669
2670 SHAPE_POLY_SET poly_outline;
2671 int last_subseq = 0;
2672 int hole_idx = -1;
2673
2674 poly_outline.NewOutline();
2675
2676 // Custom pad shapes have a group of elements
2677 // that are a list of graphical polygons
2678 for( const auto& el : (*custom_it).second.elements )
2679 {
2680 // For now, we are only processing the custom pad for the
2681 // top layer
2682 // TODO: Use full padstacks when implementing in KiCad
2683 PCB_LAYER_ID primary_layer = src->mirror ? B_Cu : F_Cu;
2684
2685 if( getLayer( ( *( el.second.begin() ) )->layer ) != primary_layer )
2686 continue;
2687
2688 for( const auto& seg : el.second )
2689 {
2690 if( seg->subseq > 0 || seg->subseq != last_subseq )
2691 {
2692 poly_outline.Polygon(0).back().SetClosed( true );
2693 hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
2694 }
2695
2696 if( seg->shape == GR_SHAPE_LINE )
2697 {
2698 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2699
2700 if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
2701 poly_outline.Append( src->start_x, src->start_y,
2702 0, hole_idx );
2703
2704 poly_outline.Append( src->end_x, src->end_y, 0,
2705 hole_idx );
2706 }
2707 else if( seg->shape == GR_SHAPE_ARC )
2708 {
2709 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2710 SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
2711
2712 chain.Append( src->result );
2713 }
2714 }
2715 }
2716
2717 if( poly_outline.OutlineCount() < 1
2718 || poly_outline.Outline( 0 ).PointCount() < 3 )
2719 {
2720 wxLogError( _( "Invalid custom pad '%s'. Replacing with "
2721 "circular pad." ),
2722 custom_name.c_str() );
2723 newpad->SetShape( F_Cu, PAD_SHAPE::CIRCLE );
2724 }
2725 else
2726 {
2728
2729 poly_outline.Move( -newpad->GetPosition() );
2730
2731 if( src->mirror )
2732 {
2733 poly_outline.Mirror( VECTOR2I( 0, ( pin->pin_y - src->y ) ),
2734 FLIP_DIRECTION::TOP_BOTTOM );
2735 poly_outline.Rotate( EDA_ANGLE( src->rotate - pin->rotation,
2736 DEGREES_T ) );
2737 }
2738 else
2739 {
2740 poly_outline.Rotate( EDA_ANGLE( -src->rotate + pin->rotation,
2741 DEGREES_T ) );
2742 }
2743
2744 newpad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, poly_outline, 0, true );
2745 }
2746
2747 SHAPE_POLY_SET mergedPolygon;
2748 newpad->MergePrimitivesAsPolygon( PADSTACK::ALL_LAYERS, &mergedPolygon );
2749
2750 if( mergedPolygon.OutlineCount() > 1 )
2751 {
2752 wxLogError( _( "Invalid custom pad '%s'. Replacing with "
2753 "circular pad." ),
2754 custom_name.c_str() );
2755 newpad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
2756 }
2757 }
2758 else
2759 {
2760 wxLogError( _( "Could not find custom pad '%s'." ),
2761 custom_name.c_str() );
2762 }
2763 }
2764 else
2765 {
2766 newpad->SetSize( PADSTACK::ALL_LAYERS,
2767 VECTOR2I( pad.width, pad.height ) );
2768 }
2769
2770 if( pad.drill )
2771 {
2772 if( pad.plated )
2773 {
2774 newpad->SetAttribute( PAD_ATTRIB::PTH );
2775 newpad->SetLayerSet( PAD::PTHMask() );
2776 }
2777 else
2778 {
2779 newpad->SetAttribute( PAD_ATTRIB::NPTH );
2780 newpad->SetLayerSet( PAD::UnplatedHoleMask() );
2781 }
2782
2783 if( pad.drill_size_x == pad.drill_size_y )
2784 newpad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
2785 else
2786 newpad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
2787
2788 newpad->SetDrillSize( VECTOR2I( pad.drill_size_x, pad.drill_size_y ) );
2789 }
2790 else
2791 {
2792 newpad->SetAttribute( PAD_ATTRIB::SMD );
2793
2794 if( pad.top )
2795 newpad->SetLayerSet( PAD::SMDMask() );
2796 else if( pad.bottom )
2797 newpad->SetLayerSet( PAD::SMDMask().Flip() );
2798 }
2799 }
2800
2801 if( src->mirror )
2802 newpad->SetOrientation( EDA_ANGLE( -src->rotate + pin->rotation,
2803 DEGREES_T ) );
2804 else
2805 newpad->SetOrientation( EDA_ANGLE( src->rotate - pin->rotation,
2806 DEGREES_T ) );
2807
2808 if( newpad->GetSizeX() > 0 || newpad->GetSizeY() > 0 )
2809 {
2810 fp->Add( newpad.release(), ADD_MODE::APPEND );
2811 }
2812 else
2813 {
2814 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
2815 aBoard->GetFileName().wc_str() );
2816 }
2817 }
2818 }
2819
2820 if( src->mirror )
2821 {
2822 fp->SetOrientationDegrees( 180.0 - src->rotate );
2823 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
2824 }
2825
2826 aBoard->Add( fp, ADD_MODE::APPEND );
2827 }
2828 }
2829
2830 return true;
2831}
2832
2833
2835{
2836 LSET layer_set;
2837
2839 layer_set |= LSET::AllTechMask() | LSET::UserMask();
2840
2841 for( auto& layer : layers )
2842 {
2843 checkpoint();
2844
2845 if( layer.second.layerid >= PCBNEW_LAYER_ID_START )
2846 layer_set.set( layer.second.layerid );
2847 }
2848
2849 aBoard->SetEnabledLayers( layer_set );
2850
2851 for( auto& layer : layers )
2852 {
2853 if( layer.second.conductive )
2854 {
2855 aBoard->SetLayerName( static_cast<PCB_LAYER_ID>( layer.second.layerid ),
2856 layer.second.name );
2857 }
2858 }
2859
2860 return true;
2861}
2862
2863
2865{
2866 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2867 const auto& ds = aBoard->GetDesignSettings();
2868
2869 for( auto& via : vias )
2870 {
2871 checkpoint();
2872
2873 auto net_it = netinfo.find( via->net );
2874 auto padstack = pads.find( via->padstack );
2875
2876 PCB_VIA* new_via = new PCB_VIA( aBoard );
2877
2878 new_via->SetPosition( VECTOR2I( via->x, via->y ) );
2879
2880 if( net_it != netinfo.end() )
2881 new_via->SetNet( net_it->second );
2882
2883 if( padstack == pads.end() )
2884 {
2885 new_via->SetDrillDefault();
2886
2887 if( !ds.m_ViasDimensionsList.empty() )
2888 {
2889 new_via->SetWidth( PADSTACK::ALL_LAYERS, ds.m_ViasDimensionsList[0].m_Diameter );
2890 new_via->SetDrill( ds.m_ViasDimensionsList[0].m_Drill );
2891 }
2892 else
2893 {
2894 new_via->SetDrillDefault();
2895 new_via->SetWidth( PADSTACK::ALL_LAYERS, ds.m_ViasMinSize );
2896 }
2897 }
2898 else
2899 {
2900 new_via->SetDrill( padstack->second.drill_size_x );
2901 new_via->SetWidth( PADSTACK::ALL_LAYERS, padstack->second.width );
2902 }
2903
2904 aBoard->Add( new_via, ADD_MODE::APPEND );
2905 }
2906
2907 return true;
2908}
2909
2910
2912{
2913 for( auto& net : netnames )
2914 {
2915 checkpoint();
2916
2917 NETINFO_ITEM *newnet = new NETINFO_ITEM( aBoard, net );
2918 aBoard->Add( newnet, ADD_MODE::APPEND );
2919 }
2920
2921 return true;
2922}
2923
2924
2925bool FABMASTER::loadEtch( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
2926{
2927 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2928 auto net_it = netinfo.find( aLine->netname );
2929
2930 int last_subseq = 0;
2931 ZONE* new_zone = nullptr;
2932
2933 for( const auto& seg : aLine->segment )
2934 {
2935 PCB_LAYER_ID layer = getLayer( seg->layer );
2936
2937 if( IsCopperLayer( layer ) )
2938 {
2939 switch( seg->shape )
2940 {
2941 case GR_SHAPE_LINE:
2942 {
2943 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2944
2945 PCB_TRACK* trk = new PCB_TRACK( aBoard );
2946
2947 trk->SetLayer( layer );
2948 trk->SetStart( VECTOR2I( src->start_x, src->start_y ) );
2949 trk->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2950 trk->SetWidth( src->width );
2951
2952 if( net_it != netinfo.end() )
2953 trk->SetNet( net_it->second );
2954
2955 aBoard->Add( trk, ADD_MODE::APPEND );
2956 break;
2957 }
2958 case GR_SHAPE_ARC:
2959 {
2960 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2961
2962 PCB_ARC* trk = new PCB_ARC( aBoard, &src->result );
2963 trk->SetLayer( layer );
2964 trk->SetWidth( src->width );
2965
2966 if( net_it != netinfo.end() )
2967 trk->SetNet( net_it->second );
2968
2969 aBoard->Add( trk, ADD_MODE::APPEND );
2970 break;
2971 }
2972 default:
2973 {
2974 // Defer to the generic graphics factory
2975 for( std::unique_ptr<BOARD_ITEM>& new_item :
2976 createBoardItems( *aBoard, layer, *seg ) )
2977 {
2978 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
2979 }
2980 break;
2981 }
2982 }
2983 }
2984 else
2985 {
2986 wxLogError( _( "Expecting etch data to be on copper layer. Row found on layer '%s'" ),
2987 seg->layer.c_str() );
2988 }
2989 }
2990
2991 return true;
2992}
2993
2994
2996{
2997 SHAPE_POLY_SET poly_outline;
2998 int last_subseq = 0;
2999 int hole_idx = -1;
3000
3001 poly_outline.NewOutline();
3002
3003 for( const auto& seg : aElement )
3004 {
3005 if( seg->subseq > 0 || seg->subseq != last_subseq )
3006 hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
3007
3008 if( seg->shape == GR_SHAPE_LINE )
3009 {
3010 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
3011
3012 if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
3013 poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
3014
3015 poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
3016 }
3017 else if( seg->shape == GR_SHAPE_ARC || seg->shape == GR_SHAPE_CIRCLE )
3018 {
3019 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
3020 SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
3021
3022 chain.Append( src->result );
3023 }
3024 }
3025
3026 return poly_outline;
3027}
3028
3029
3030/*
3031 * The format doesn't seem to distinguish between open and closed polygons.
3032 * So the best we can really do is to try to detect an open polyline by looking
3033 * for a closed subsequence 0.
3034 *
3035 * For example three lines like this will be open:
3036 *
3037 * +----
3038 * |
3039 * +----
3040 *
3041 * But four lines will be closed:
3042 *
3043 * +----+
3044 * | |
3045 * +----+
3046 *
3047 * This means that "closed" zones (which can have fill patterns in Allegro)
3048 * and "a bunch of lines, which happen to be closed) are not distinguishable,
3049 * but that just seems to be information thrown away on export to FABMASTER.
3050 */
3052{
3053 if( aLine.segment.size() == 0 )
3054 return true;
3055
3056 // First and last item in the first subsequence
3057 const GRAPHIC_ITEM* first = nullptr;
3058 const GRAPHIC_ITEM* last = nullptr;
3059 int first_subseq = -1;
3060 bool have_multiple_subseqs = false;
3061
3062 for( const std::unique_ptr<GRAPHIC_ITEM>& gr_item : aLine.segment )
3063 {
3064 if( first == nullptr )
3065 {
3066 first = gr_item.get();
3067 first_subseq = gr_item->subseq;
3068 }
3069 else if( gr_item->subseq == first_subseq )
3070 {
3071 last = gr_item.get();
3072 }
3073 else
3074 {
3075 have_multiple_subseqs = true;
3076 break;
3077 }
3078 }
3079
3080 // Should have at least one item
3081 wxCHECK( first, true );
3082
3083 // First subsequence was only one item
3084 if( !last )
3085 {
3086 // It can still be a closed polygon if the outer border is a circle
3087 // and there are inner shapes.
3088 if( first->shape == GR_SHAPE_CIRCLE && have_multiple_subseqs )
3089 return false;
3090
3091 return true;
3092 }
3093
3094 const VECTOR2I start{ first->start_x, first->start_y };
3095
3096 // It's not always possible to find an end
3097 OPT_VECTOR2I end;
3098
3099 switch( last->shape )
3100 {
3101 case GR_SHAPE_LINE:
3102 {
3103 const GRAPHIC_LINE& line = static_cast<const GRAPHIC_LINE&>( *last );
3104 end = VECTOR2I{ line.end_x, line.end_y };
3105 break;
3106 }
3107 case GR_SHAPE_ARC:
3108 {
3109 const GRAPHIC_ARC& arc = static_cast<const GRAPHIC_ARC&>( *last );
3110 end = VECTOR2I{ arc.end_x, arc.end_y };
3111 break;
3112 }
3113 default:
3114 // These shapes don't have "ends" that make sense for a polyline
3115 break;
3116 }
3117
3118 // This looks like a closed polygon
3119 if( end.has_value() && start == end )
3120 return false;
3121
3122 // Open polyline
3123 return true;
3124}
3125
3126
3127std::vector<std::unique_ptr<BOARD_ITEM>>
3129{
3130 std::vector<std::unique_ptr<BOARD_ITEM>> new_items;
3131
3132 const BOARD_DESIGN_SETTINGS& boardSettings = aBoard.GetDesignSettings();
3133 const STROKE_PARAMS defaultStroke( boardSettings.GetLineThickness( aLayer ) );
3134
3135 const auto setShapeParameters = [&]( PCB_SHAPE& aShape )
3136 {
3137 aShape.SetStroke( STROKE_PARAMS( aGraphic.width, LINE_STYLE::SOLID ) );
3138
3139 if( aShape.GetWidth() == 0 )
3140 aShape.SetStroke( defaultStroke );
3141 };
3142
3143 switch( aGraphic.shape )
3144 {
3145 case GR_SHAPE_TEXT:
3146 {
3147 const GRAPHIC_TEXT& src = static_cast<const GRAPHIC_TEXT&>( aGraphic );
3148
3149 auto new_text = std::make_unique<PCB_TEXT>( &aBoard );
3150
3151 if( IsBackLayer( aLayer ) )
3152 {
3153 new_text->SetMirrored( true );
3154 }
3155
3156 setupText( src, aLayer, *new_text, aBoard, std::nullopt );
3157
3158 new_items.emplace_back( std::move( new_text ) );
3159 break;
3160 }
3161 case GR_SHAPE_CROSS:
3162 {
3163 const GRAPHIC_CROSS& src = static_cast<const GRAPHIC_CROSS&>( aGraphic );
3164
3165 const VECTOR2I c{ src.start_x, src.start_y };
3166 const VECTOR2I s{ src.size_x, src.size_y };
3167
3168 const std::vector<SEG> segs = KIGEOM::MakeCrossSegments( c, s, ANGLE_0 );
3169
3170 for( const SEG& seg : segs )
3171 {
3172 auto line = std::make_unique<PCB_SHAPE>( &aBoard );
3173 line->SetShape( SHAPE_T::SEGMENT );
3174 line->SetStart( seg.A );
3175 line->SetEnd( seg.B );
3176
3177 setShapeParameters( *line );
3178 new_items.emplace_back( std::move( line ) );
3179 }
3180 break;
3181 }
3182 default:
3183 {
3184 // Simple single shape
3185 auto new_shape = std::make_unique<PCB_SHAPE>( &aBoard );
3186
3187 setShapeParameters( *new_shape );
3188
3189 switch( aGraphic.shape )
3190 {
3191 case GR_SHAPE_LINE:
3192 {
3193 const GRAPHIC_LINE& src = static_cast<const GRAPHIC_LINE&>( aGraphic );
3194
3195 new_shape->SetShape( SHAPE_T::SEGMENT );
3196 new_shape->SetStart( VECTOR2I( src.start_x, src.start_y ) );
3197 new_shape->SetEnd( VECTOR2I( src.end_x, src.end_y ) );
3198
3199 break;
3200 }
3201 case GR_SHAPE_ARC:
3202 {
3203 const GRAPHIC_ARC& src = static_cast<const GRAPHIC_ARC&>( aGraphic );
3204
3205 new_shape->SetShape( SHAPE_T::ARC );
3206 new_shape->SetArcGeometry( src.result.GetP0(), src.result.GetArcMid(),
3207 src.result.GetP1() );
3208 break;
3209 }
3210 case GR_SHAPE_CIRCLE:
3211 {
3212 const GRAPHIC_ARC& src = static_cast<const GRAPHIC_ARC&>( aGraphic );
3213
3214 new_shape->SetShape( SHAPE_T::CIRCLE );
3215 new_shape->SetCenter( VECTOR2I( src.center_x, src.center_y ) );
3216 new_shape->SetRadius( src.radius );
3217 break;
3218 }
3219 case GR_SHAPE_RECTANGLE:
3220 {
3221 const GRAPHIC_RECTANGLE& src = static_cast<const GRAPHIC_RECTANGLE&>( aGraphic );
3222
3223 new_shape->SetShape( SHAPE_T::RECTANGLE );
3224 new_shape->SetStart( VECTOR2I( src.start_x, src.start_y ) );
3225 new_shape->SetEnd( VECTOR2I( src.end_x, src.end_y ) );
3226
3227 new_shape->SetFilled( src.fill );
3228 break;
3229 }
3230 case GR_SHAPE_POLYGON:
3231 {
3232 const GRAPHIC_POLYGON& src = static_cast<const GRAPHIC_POLYGON&>( aGraphic );
3233 new_shape->SetShape( SHAPE_T::POLY );
3234 new_shape->SetPolyPoints( src.m_pts );
3235 break;
3236 }
3237 case GR_SHAPE_OBLONG:
3238 {
3239 // Create as a polygon, but we could also make a group of two lines and two arcs
3240 const GRAPHIC_OBLONG& src = static_cast<const GRAPHIC_OBLONG&>( aGraphic );
3241
3242 const VECTOR2I c{ src.start_x, src.start_y };
3243 VECTOR2I s = c;
3244 int w = 0;
3245
3246 if( src.oblong_x )
3247 {
3248 w = src.size_y;
3249 s -= VECTOR2I{ ( src.size_x - w ) / 2, 0 };
3250 }
3251 else
3252 {
3253 w = src.size_x;
3254 s -= VECTOR2I{ 0, ( src.size_y - w ) / 2 };
3255 }
3256
3257 SHAPE_SEGMENT seg( s, c - ( s - c ), w );
3258
3259 SHAPE_POLY_SET poly;
3260 seg.TransformToPolygon( poly, boardSettings.m_MaxError, ERROR_LOC::ERROR_INSIDE );
3261
3262 new_shape->SetShape( SHAPE_T::POLY );
3263 new_shape->SetPolyShape( poly );
3264 break;
3265 }
3266 default:
3267 {
3268 wxLogError( _( "Unhandled shape type %d in polygon on layer %s, seq %d %d" ),
3269 aGraphic.shape, aGraphic.layer, aGraphic.seq, aGraphic.subseq );
3270 }
3271 }
3272
3273 new_items.emplace_back( std::move( new_shape ) );
3274 }
3275 }
3276
3277 for( std::unique_ptr<BOARD_ITEM>& new_item : new_items )
3278 {
3279 new_item->SetLayer( aLayer );
3280 }
3281
3282 // If there's more than one, group them
3283 if( new_items.size() > 1 )
3284 {
3285 auto new_group = std::make_unique<PCB_GROUP>( &aBoard );
3286 for( std::unique_ptr<BOARD_ITEM>& new_item : new_items )
3287 {
3288 new_group->AddItem( new_item.get() );
3289 }
3290 new_items.emplace_back( std::move( new_group ) );
3291 }
3292
3293 return new_items;
3294}
3295
3296
3297bool FABMASTER::loadPolygon( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
3298{
3299 if( aLine->segment.empty() )
3300 return false;
3301
3302 PCB_LAYER_ID layer = Cmts_User;
3303
3304 const PCB_LAYER_ID new_layer = getLayer( aLine->layer );
3305
3306 if( IsPcbLayer( new_layer ) )
3307 layer = new_layer;
3308
3309 const bool is_open = traceIsOpen( *aLine );
3310
3311 if( is_open )
3312 {
3313 for( const auto& seg : aLine->segment )
3314 {
3315 for( std::unique_ptr<BOARD_ITEM>& new_item : createBoardItems( *aBoard, layer, *seg ) )
3316 {
3317 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
3318 }
3319 }
3320 }
3321 else
3322 {
3323 STROKE_PARAMS defaultStroke( aBoard->GetDesignSettings().GetLineThickness( layer ) );
3324
3325 SHAPE_POLY_SET poly_outline = loadShapePolySet( aLine->segment );
3326
3328
3329 if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
3330 return false;
3331
3332 PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
3333
3334 new_poly->SetShape( SHAPE_T::POLY );
3335 new_poly->SetLayer( layer );
3336
3337 // Polygons on the silk layer are filled but other layers are not/fill doesn't make sense
3338 if( layer == F_SilkS || layer == B_SilkS )
3339 {
3340 new_poly->SetFilled( true );
3341 new_poly->SetStroke( STROKE_PARAMS( 0 ) );
3342 }
3343 else
3344 {
3345 new_poly->SetStroke(
3346 STROKE_PARAMS( ( *( aLine->segment.begin() ) )->width, LINE_STYLE::SOLID ) );
3347
3348 if( new_poly->GetWidth() == 0 )
3349 new_poly->SetStroke( defaultStroke );
3350 }
3351
3352 new_poly->SetPolyShape( poly_outline );
3353 aBoard->Add( new_poly, ADD_MODE::APPEND );
3354 }
3355
3356 return true;
3357}
3358
3359
3360bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
3361{
3362 if( aLine->segment.size() < 3 )
3363 return false;
3364
3365 SHAPE_POLY_SET* zone_outline = nullptr;
3366 ZONE* zone = nullptr;
3367
3368 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
3369 auto net_it = netinfo.find( aLine->netname );
3370 PCB_LAYER_ID layer = Cmts_User;
3371 auto new_layer = getLayer( aLine->layer );
3372
3373 if( IsPcbLayer( new_layer ) )
3374 layer = new_layer;
3375
3376 zone = new ZONE( aBoard );
3377 zone_outline = new SHAPE_POLY_SET;
3378
3379 if( net_it != netinfo.end() )
3380 zone->SetNet( net_it->second );
3381
3382 if( aLine->layer == "ALL" )
3383 zone->SetLayerSet( aBoard->GetLayerSet() & LSET::AllCuMask() );
3384 else
3385 zone->SetLayer( layer );
3386
3387 zone->SetIsRuleArea( false );
3388 zone->SetDoNotAllowTracks( false );
3389 zone->SetDoNotAllowVias( false );
3390 zone->SetDoNotAllowPads( false );
3391 zone->SetDoNotAllowFootprints( false );
3392 zone->SetDoNotAllowCopperPour( false );
3393
3394 if( aLine->lclass == "ROUTE KEEPOUT")
3395 {
3396 zone->SetIsRuleArea( true );
3397 zone->SetDoNotAllowTracks( true );
3398 }
3399 else if( aLine->lclass == "VIA KEEPOUT")
3400 {
3401 zone->SetIsRuleArea( true );
3402 zone->SetDoNotAllowVias( true );
3403 }
3404 else
3405 {
3406 zone->SetAssignedPriority( 50 );
3407 }
3408
3409 zone->SetLocalClearance( 0 );
3410 zone->SetPadConnection( ZONE_CONNECTION::FULL );
3411
3412 zone_outline->NewOutline();
3413
3414 std::unique_ptr<SHAPE_LINE_CHAIN> pending_hole = nullptr;
3415 SHAPE_LINE_CHAIN* active_chain = &zone_outline->Outline( 0 );
3416
3417 const auto add_hole_if_valid = [&]()
3418 {
3419 if( pending_hole )
3420 {
3421 pending_hole->SetClosed( true );
3422
3423 // If we get junk holes, assert, but don't add them to the zone, as that
3424 // will cause crashes later.
3425 if( !KIGEOM::AddHoleIfValid( *zone_outline, std::move( *pending_hole ) ) )
3426 {
3427 wxLogMessage( _( "Invalid hole with %d points in zone on layer %s with net %s" ),
3428 pending_hole->PointCount(), zone->GetLayerName(),
3429 zone->GetNetname() );
3430 }
3431
3432 pending_hole.reset();
3433 }
3434 };
3435
3436 int last_subseq = 0;
3437 for( const auto& seg : aLine->segment )
3438 {
3439 if( seg->subseq > 0 && seg->subseq != last_subseq )
3440 {
3441 // Don't knock holes in the BOUNDARY systems. These are the outer layers for
3442 // zone fills.
3443 if( aLine->lclass == "BOUNDARY" )
3444 break;
3445
3446 add_hole_if_valid();
3447 pending_hole = std::make_unique<SHAPE_LINE_CHAIN>();
3448 active_chain = pending_hole.get();
3449 last_subseq = seg->subseq;
3450 }
3451
3452 if( seg->shape == GR_SHAPE_LINE )
3453 {
3454 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
3455 const VECTOR2I start( src->start_x, src->start_y );
3456 const VECTOR2I end( src->end_x, src->end_y );
3457
3458 if( active_chain->PointCount() == 0 )
3459 {
3460 active_chain->Append( start );
3461 }
3462 else
3463 {
3464 const VECTOR2I& last = active_chain->CPoint( -1 );
3465
3466 // Not if this can ever happen, or what do if it does (add both points?).
3467 if( last != start )
3468 {
3469 wxLogError( _( "Outline seems discontinuous: last point was %s, "
3470 "start point of next segment is %s" ),
3471 last.Format(), start.Format() );
3472 }
3473 }
3474
3475 active_chain->Append( end );
3476 }
3477 else if( seg->shape == GR_SHAPE_ARC || seg->shape == GR_SHAPE_CIRCLE )
3478 {
3479 /* Even if it says "circle", it's actually an arc, it's just closed */
3480 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
3481 active_chain->Append( src->result );
3482 }
3483 else
3484 {
3485 wxLogError( _( "Invalid shape type %d in zone outline" ), seg->shape );
3486 }
3487 }
3488
3489 // Finalise the last hole, if any
3490 add_hole_if_valid();
3491
3492 if( zone_outline->Outline( 0 ).PointCount() >= 3 )
3493 {
3494 zone->SetOutline( zone_outline );
3495 aBoard->Add( zone, ADD_MODE::APPEND );
3496 }
3497 else
3498 {
3499 delete( zone_outline );
3500 delete( zone );
3501 }
3502
3503 return true;
3504}
3505
3506
3507bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
3508{
3509 PCB_LAYER_ID layer;
3510
3511 if( aLine->lclass == "BOARD GEOMETRY" && aLine->layer != "DIMENSION" )
3512 layer = Edge_Cuts;
3513 else if( aLine->lclass == "DRAWING FORMAT" )
3514 layer = Dwgs_User;
3515 else
3516 layer = Cmts_User;
3517
3518 for( auto& seg : aLine->segment )
3519 {
3520 for( std::unique_ptr<BOARD_ITEM>& new_item : createBoardItems( *aBoard, layer, *seg ) )
3521 {
3522 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
3523 }
3524 }
3525
3526 return true;
3527}
3528
3529
3531{
3532
3533 for( auto& geom : board_graphics )
3534 {
3535 checkpoint();
3536
3537 PCB_LAYER_ID layer;
3538
3539 // The pin numbers are not useful for us outside of the footprints
3540 if( geom.subclass == "PIN_NUMBER" )
3541 continue;
3542
3543 layer = getLayer( geom.subclass );
3544
3545 if( !IsPcbLayer( layer ) )
3546 layer = Cmts_User;
3547
3548 if( !geom.elements->empty() )
3549 {
3551 if( ( *( geom.elements->begin() ) )->width == 0 )
3552 {
3553 SHAPE_POLY_SET poly_outline = loadShapePolySet( *( geom.elements ) );
3554
3556
3557 if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
3558 continue;
3559
3560 PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard, SHAPE_T::POLY );
3561 new_poly->SetLayer( layer );
3562 new_poly->SetPolyShape( poly_outline );
3563 new_poly->SetStroke( STROKE_PARAMS( 0 ) );
3564
3565 if( layer == F_SilkS || layer == B_SilkS )
3566 new_poly->SetFilled( true );
3567
3568 aBoard->Add( new_poly, ADD_MODE::APPEND );
3569 }
3570 }
3571
3572 for( auto& seg : *geom.elements )
3573 {
3574 for( std::unique_ptr<BOARD_ITEM>& new_item : createBoardItems( *aBoard, layer, *seg ) )
3575 {
3576 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
3577 }
3578 }
3579 }
3580
3581 return true;
3582
3583}
3584
3585
3587{
3588 std::vector<ZONE*> sortedZones;
3589 std::copy( aBoard->Zones().begin(), aBoard->Zones().end(), std::back_inserter( sortedZones ) );
3590 std::sort( sortedZones.begin(), sortedZones.end(),
3591 [&]( const ZONE* a, const ZONE* b )
3592 {
3593 if( a->GetLayer() == b->GetLayer() )
3594 return a->GetBoundingBox().GetArea() > b->GetBoundingBox().GetArea();
3595
3596 return a->GetLayer() < b->GetLayer();
3597 } );
3598
3600 unsigned int priority = 0;
3601
3602 for( ZONE* zone : sortedZones )
3603 {
3605 if( zone->GetIsRuleArea() )
3606 continue;
3607
3608 if( zone->GetLayer() != layer )
3609 {
3610 layer = zone->GetLayer();
3611 priority = 0;
3612 }
3613
3614 zone->SetAssignedPriority( priority );
3615 priority += 10;
3616 }
3617
3618 return true;
3619}
3620
3621
3622bool FABMASTER::LoadBoard( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
3623{
3624 aBoard->SetFileName( m_filename.GetFullPath() );
3625 m_progressReporter = aProgressReporter;
3626
3627 m_totalCount = netnames.size()
3628 + layers.size()
3629 + vias.size()
3630 + components.size()
3631 + zones.size()
3632 + board_graphics.size()
3633 + traces.size();
3634 m_doneCount = 0;
3635
3636 loadNets( aBoard );
3637 loadLayers( aBoard );
3638 loadVias( aBoard );
3639 loadFootprints( aBoard );
3640 loadZones( aBoard );
3641 loadGraphics( aBoard );
3642
3643 for( auto& track : traces )
3644 {
3645 checkpoint();
3646
3647 if( track->lclass == "ETCH" )
3648 loadEtch( aBoard, track);
3649 else if( track->layer == "OUTLINE" || track->layer == "DIMENSION" )
3650 loadOutline( aBoard, track );
3651 else
3652 loadPolygon( aBoard, track );
3653 }
3654
3655 orderZones( aBoard );
3656
3657 return true;
3658}
const char * name
Definition: DXF_plotter.cpp:57
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
BASE_SET & set(size_t pos)
Definition: base_set.h:115
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
Container for design settings for a BOARD object.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:288
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:257
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
Definition: board_item.cpp:139
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:290
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:871
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:1003
void SetFileName(const wxString &aFileName)
Definition: board.h:325
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:798
const ZONES & Zones() const
Definition: board.h:335
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:592
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayer) const
Definition: board.cpp:734
const wxString & GetFileName() const
Definition: board.h:327
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:1137
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
EDA_ANGLE Normalize()
Definition: eda_angle.h:221
EDA_ANGLE Normalize180()
Definition: eda_angle.h:260
EDA_ANGLE Normalized() const
Definition: eda_angle.h:232
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:778
void SetFilled(bool aFlag)
Definition: eda_shape.h:108
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:294
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:131
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
void SetWidth(int aWidth)
Definition: eda_shape.h:121
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:551
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:384
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:408
void SetTextWidth(int aWidth)
Definition: eda_text.cpp:529
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:377
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:283
void SetTextHeight(int aHeight)
Definition: eda_text.cpp:540
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:416
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:269
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:291
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition: eda_text.cpp:299
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:400
size_t processFootprints(size_t aRow)
A!REFDES!COMP_CLASS!COMP_PART_NUMBER!COMP_HEIGHT!COMP_DEVICE_LABEL!COMP_INSERTION_CODE!...
unsigned m_doneCount
size_t processPins(size_t aRow)
A!SYM_NAME!SYM_MIRROR!PIN_NAME!PIN_NUMBER!PIN_X!PIN_Y!PAD_STACK_NAME!REFDES!PIN_ROTATION!...
int readInt(const std::string &aStr) const
wxFileName m_filename
GRAPHIC_OBLONG * processOblong(const GRAPHIC_DATA &aData, double aScale)
size_t processGeometry(size_t aRow)
A!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!GRAPHIC_DATA_3!...
static std::vector< std::unique_ptr< BOARD_ITEM > > createBoardItems(BOARD &aBoard, PCB_LAYER_ID aLayer, FABMASTER::GRAPHIC_ITEM &aGraphic)
Convert one Fabmaster graphic item to one or more PCB items.
bool Read(const std::string &aFile)
bool loadNets(BOARD *aBoard)
std::map< std::string, std::map< int, GEOM_GRAPHIC > > comp_graphics
GRAPHIC_CROSS * processCross(const GRAPHIC_DATA &aData, double aScale)
unsigned m_lastProgressCount
SYMTYPE parseSymType(const std::string &aSymType)
GRAPHIC_TEXT * processText(const GRAPHIC_DATA &aData, double aScale)
bool loadLayers(BOARD *aBoard)
static void setupText(const FABMASTER::GRAPHIC_TEXT &aGraphicText, PCB_LAYER_ID aLayer, PCB_TEXT &aText, const BOARD &aBoard, const OPT_VECTOR2I &aMirrorPoint)
Set parameters for graphic text.
PCB_LAYER_ID getLayer(const std::string &aLayerName)
GRAPHIC_RECTANGLE * processSquare(const GRAPHIC_DATA &aData, double aScale)
static bool traceIsOpen(const FABMASTER::TRACE &aLine)
bool loadZones(BOARD *aBoard)
Loads sections of the database into the board.
GRAPHIC_RECTANGLE * processRectangle(const GRAPHIC_DATA &aData, double aScale)
std::vector< std::string > single_row
size_t processSimpleLayers(size_t aRow)
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
bool loadFootprints(BOARD *aBoard)
GRAPHIC_ARC * processCircle(const GRAPHIC_DATA &aData, double aScale)
std::unordered_map< std::string, FM_PAD > pads
int getColFromName(size_t aRow, const std::string &aStr)
bool loadVias(BOARD *aBoard)
size_t processLayers(size_t aRow)
A!LAYER_SORT!LAYER_SUBCLASS!LAYER_ARTWORK!LAYER_USE!LAYER_CONDUCTOR!LAYER_DIELECTRIC_CONSTANT!...
std::map< std::string, FABMASTER_LAYER > layers
COMPCLASS parseCompClass(const std::string &aCompClass)
bool loadEtch(BOARD *aBoard, const std::unique_ptr< TRACE > &aLine)
GRAPHIC_RECTANGLE * processFigRectangle(const GRAPHIC_DATA &aData, double aScale)
std::map< std::string, std::set< std::unique_ptr< PIN >, PIN::BY_NUM > > pins
std::set< std::unique_ptr< GRAPHIC_ITEM >, GRAPHIC_ITEM::SEQ_CMP > graphic_element
std::map< std::pair< std::string, std::string >, NETNAME > pin_nets
GRAPHIC_ITEM * processGraphic(const GRAPHIC_DATA &aData, double aScale)
Specialty functions for processing graphical data rows into the internal database.
std::deque< single_row > rows
GRAPHIC_POLYGON * processPolygon(const GRAPHIC_DATA &aData, double aScale)
bool loadOutline(BOARD *aBoard, const std::unique_ptr< TRACE > &aLine)
size_t processPadStacks(size_t aRow)
A!PADNAME!RECNUMBER!LAYER!FIXFLAG!VIAFLAG!PADSHAPE1!PADWIDTH!PADHGHT! PADXOFF!PADYOFF!...
std::vector< GEOM_GRAPHIC > board_graphics
section_type detectType(size_t aOffset)
double readDouble(const std::string &aStr) const
Reads the double/integer value from a std string independent of the user locale.
bool loadZone(BOARD *aBoard, const std::unique_ptr< FABMASTER::TRACE > &aLine)
GRAPHIC_ARC * processArc(const GRAPHIC_DATA &aData, double aScale)
SHAPE_POLY_SET loadShapePolySet(const graphic_element &aLine)
bool loadGraphics(BOARD *aBoard)
std::unordered_map< std::string, FABMASTER_PAD_SHAPE > pad_shapes
bool LoadBoard(BOARD *aBoard, PROGRESS_REPORTER *aProgressReporter)
std::vector< std::unique_ptr< FM_VIA > > vias
std::set< std::unique_ptr< TRACE >, TRACE::BY_ID > traces
std::set< std::unique_ptr< TRACE >, TRACE::BY_ID > zones
std::map< std::string, std::vector< std::unique_ptr< COMPONENT > > > components
bool loadPolygon(BOARD *aBoard, const std::unique_ptr< FABMASTER::TRACE > &aLine)
std::set< std::unique_ptr< TRACE >, TRACE::BY_ID > refdes
@ GR_SHAPE_OBLONG
!< Actually 360° arcs (for both arcs where start==end and real circles)
@ GR_SHAPE_CROSS
!< X/Y oblongs
size_t processPadStackLayers(size_t aRow)
std::set< std::string > netnames
size_t processTraces(size_t aRow)
A!CLASS!SUBCLASS!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!...
size_t processNets(size_t aRow)
A!NET_NAME!REFDES!PIN_NUMBER!PIN_NAME!PIN_GROUND!PIN_POWER!
@ FABMASTER_EXTRACT_PINS
bool orderZones(BOARD *aBoard)
Sets zone priorities based on zone BB size.
double processScaleFactor(size_t aRow)
Processes data from text vectors into internal database for further ordering.
size_t processVias(size_t aRow)
A!VIA_X!VIA_Y!PAD_STACK_NAME!NET_NAME!TEST_POINT!
unsigned m_totalCount
for progress reporting
size_t processCustomPads(size_t aRow)
A!SUBCLASS!PAD_SHAPE_NAME!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!...
GRAPHIC_LINE * processLine(const GRAPHIC_DATA &aData, double aScale)
void SetPosition(const VECTOR2I &aPos) override
Definition: footprint.cpp:2388
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:249
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:658
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:239
void SetReference(const wxString &aReference)
Definition: footprint.h:628
void SetValue(const wxString &aValue)
Definition: footprint.h:649
PCB_FIELD & Reference()
Definition: footprint.h:659
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1025
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition: footprint.cpp:2330
VECTOR2I GetPosition() const override
Definition: footprint.h:224
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:51
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET UserMask()
Definition: lset.cpp:758
static LSET AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
Definition: lset.cpp:744
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:686
Handle the data for a net.
Definition: netinfo.h:56
unsigned GetNetCount() const
Definition: netinfo.h:369
const NETNAMES_MAP & NetsByName() const
Return the name map, at least for python.
Definition: netinfo.h:372
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:269
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:290
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:276
virtual void Mirror(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Mirror this object relative to a given horizontal axis the layer is not changed.
Definition: pcb_shape.cpp:494
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:79
int GetWidth() const override
Definition: pcb_shape.cpp:301
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition: pcb_shape.cpp:486
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:170
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:90
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:69
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:118
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:121
virtual void SetWidth(int aWidth)
Definition: pcb_track.h:115
void SetDrillDefault()
Set the drill value for vias to the default value UNDEFINED_DRILL_DIAMETER.
Definition: pcb_track.h:630
void SetDrill(int aDrill)
Set the drill value for vias.
Definition: pcb_track.h:608
void SetPosition(const VECTOR2I &aPoint) override
Definition: pcb_track.h:494
void SetWidth(int aWidth) override
Definition: pcb_track.cpp:351
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
virtual void SetCurrentProgress(double aProgress)=0
Set the progress value to aProgress (0..1).
Definition: seg.h:42
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:116
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Definition: shape_arc.cpp:645
const VECTOR2I & GetP1() const
Definition: shape_arc.h:115
const VECTOR2I & GetP0() const
Definition: shape_arc.h:114
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:523
bool PointOnEdge(const VECTOR2I &aP, int aAccuracy=0) const
Check if point aP lies on an edge or vertex of the line chain.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const std::vector< VECTOR2I > & CPoints() const
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 Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
int VertexCount(int aOutline=-1, int aHole=-1) const
Return the number of vertices in a given outline/hole.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
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 AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both)
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aError, ERROR_LOC aErrorLoc) const override
Fills a SHAPE_POLY_SET with a polygon representation of this shape.
Simple container to manage line stroke parameters.
Definition: stroke_params.h:79
const std::string Format() const
Return the vector formatted as a string.
Definition: vector2d.h:423
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:750
void SetLocalClearance(std::optional< int > aClearance)
Definition: zone.h:158
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:747
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:481
void SetIsRuleArea(bool aEnable)
Definition: zone.h:737
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:749
void SetLayerSet(const LSET &aLayerSet) override
Definition: zone.cpp:487
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:748
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:751
void SetAssignedPriority(unsigned aPriority)
Definition: zone.h:118
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:271
void SetOutline(SHAPE_POLY_SET *aOutline)
Definition: zone.h:343
The common library.
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE FULL_CIRCLE
Definition: eda_angle.h:399
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
static const wxChar traceFabmaster[]
Flag to enable FABMASTER plugin debugging output.
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
bool IsPcbLayer(int aLayer)
Test whether a layer is a valid layer for Pcbnew.
Definition: layer_ids.h:520
constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START
Definition: layer_ids.h:138
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:655
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:531
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ Edge_Cuts
Definition: layer_ids.h:112
@ Dwgs_User
Definition: layer_ids.h:107
@ F_Paste
Definition: layer_ids.h:104
@ Cmts_User
Definition: layer_ids.h:108
@ 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
@ User_9
Definition: layer_ids.h:132
@ UNSELECTED_LAYER
Definition: layer_ids.h:62
@ F_Fab
Definition: layer_ids.h:119
@ F_SilkS
Definition: layer_ids.h:100
@ B_CrtYd
Definition: layer_ids.h:115
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ User_1
Definition: layer_ids.h:124
@ B_SilkS
Definition: layer_ids.h:101
@ F_Cu
Definition: layer_ids.h:64
@ B_Fab
Definition: layer_ids.h:118
bool AddHoleIfValid(SHAPE_POLY_SET &aOutline, SHAPE_LINE_CHAIN &&aHole)
Adds a hole to a polygon if it is valid (i.e.
std::vector< VECTOR2I > MakeRegularPolygonPoints(const VECTOR2I &aCenter, size_t aN, const VECTOR2I &aPt0)
Get the corners of a regular polygon from the centre, one point and the number of sides.
std::vector< SEG > MakeCrossSegments(const VECTOR2I &aCenter, const VECTOR2I &aSize, EDA_ANGLE aAngle)
Create the two segments for a cross.
void delete_if(_Container &__c, _Function &&__f)
Deletes all values from __c for which __f returns true.
Definition: kicad_algo.h:174
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
void Flip(T &aValue)
Class to handle a set of BOARD_ITEMs.
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
Utility functions for working with shapes.
bool ReplaceIllegalFileNameChars(std::string *aName, int aReplaceChar)
Checks aName for illegal file name characters.
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
Definition: string_utils.h:317
const double IU_PER_MM
Definition: base_units.h:76
const double IU_PER_MILS
Definition: base_units.h:77
A!LAYER_SORT!LAYER_SUBCLASS!LAYER_ARTWORK!LAYER_USE!LAYER_CONDUCTOR!LAYER_DIELECTRIC_CONSTANT !...
bool disable
! if true, prevent the layer elements from being used
std::string name
! LAYER_SUBCLASS
int layerid
! pcbnew layer (assigned)
bool conductive
! LAYER_CONDUCTOR
bool positive
! LAYER_ARTWORK (either POSITIVE or NEGATIVE)
A!SUBCLASS!PAD_SHAPE_NAME!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!...
std::string name
! SYM_NAME
std::string refdes
! REFDES
std::string subclass
! SUBCLASS
int id
! RECORD_TAG[0]
std::unique_ptr< graphic_element > elements
int end_x
! GRAPHIC_DATA_3
SHAPE_ARC result
! KiCad-style arc representation
int radius
! GRAPHIC_DATA_7 ! width is GRAPHIC_DATA_8
int center_x
! GRAPHIC_DATA_5
bool clockwise
! GRAPHIC_DATA_9
int center_y
! GRAPHIC_DATA_6
int end_y
! GRAPHIC_DATA_4
int size_y
! GRAPHIC_DATA_4
int size_x
! GRAPHIC_DATA_3
std::string layer
! SUBCLASS
int subseq
! RECORD_TAG[1]
int width
! Various sections depending on type
GRAPHIC_SHAPE shape
! Shape of the graphic_item
int seq
! RECORD_TAG[0]
int start_y
! GRAPHIC_DATA_2
int start_x
! GRAPHIC_DATA_1
GRAPHIC_TYPE type
! Type of graphic item
int end_x
! GRAPHIC_DATA_3
int end_y
! GRAPHIC_DATA_4 ! width is GRAPHIC_DATA_5
bool oblong_x
! OBLONG_X (as opposed to OBLONG_Y)
int size_x
! GRAPHIC_DATA_3
int size_y
! GRAPHIC_DATA_4
std::vector< VECTOR2I > m_pts
double rotation
! GRAPHIC_DATA_3
std::string text
! GRAPHIC_DATA_7
int height
! GRAPHIC_DATA_6[2]
int thickness
! GRAPHIC_DATA_6[6]
GR_TEXT_H_ALIGN_T orient
! GRAPHIC_DATA_5
bool ital
! GRAPHIC_DATA_6[4] != 0.0
bool mirror
! GRAPHIC_DATA_4
std::string refdes
!< NET_NAME
bool pin_pwr
!< PIN_GND
std::string pin_num
!< REFDES
std::string pin_name
!< PIN_NUMBER
bool pin_gnd
!< PIN_NAME
graphic_element segment
! GRAPHIC_DATA (can be either LINE or ARC)
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
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:691