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 The 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( _( "File import canceled 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 wxLogDebug( "FABMASTER::processPolygon: Expected x and y to be the same, got x = %s and y = %s ",
1315 aData.graphic_data3, aData.graphic_data4 );
1316 }
1317
1318 auto new_poly = std::make_unique<GRAPHIC_POLYGON>();
1319 new_poly->shape = GR_SHAPE_POLYGON;
1320 new_poly->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1321
1322 int radius = s.x / 2;
1323 bool across_corners = true;
1324 EDA_ANGLE pt0_angle = ANGLE_90; // /Pointing up
1325 int n_pts = 0;
1326
1327 if( aData.graphic_dataname == "TRIANGLE_1" )
1328 {
1329 // Upright equilateral triangle (pointing upwards, horizontal base)
1330 // The size appears to be (?) the size of the circumscribing circle,
1331 // rather than the width of the base.
1332 n_pts = 3;
1333 }
1334 else if( aData.graphic_dataname == "DIAMOND" )
1335 {
1336 // Square diamond (can it be non-square?)
1337 // Size is point-to-point width/height
1338 n_pts = 4;
1339 }
1340 else if( aData.graphic_dataname == "HEXAGON_X" )
1341 {
1342 // Hexagon with horizontal top/bottom
1343 // Size is the overall width (across corners)
1344 n_pts = 6;
1345 pt0_angle = ANGLE_0;
1346 }
1347 else if( aData.graphic_dataname == "HEXAGON_Y" )
1348 {
1349 // Hexagon with vertical left/right sides
1350 // Size is the height (i.e. across corners)
1351 n_pts = 6;
1352 }
1353 else if( aData.graphic_dataname == "OCTAGON" )
1354 {
1355 // Octagon with horizontal/vertical sides
1356 // Size is the overall width (across flats)
1357 across_corners = false;
1358 pt0_angle = FULL_CIRCLE / 16;
1359 n_pts = 8;
1360 }
1361 else
1362 {
1363 wxCHECK_MSG( false, nullptr,
1364 wxString::Format( "Unhandled polygon type: %s", aData.graphic_dataname ) );
1365 }
1366
1367 new_poly->m_pts =
1368 KIGEOM::MakeRegularPolygonPoints( c, n_pts, radius, across_corners, pt0_angle );
1369 return new_poly.release();
1370}
1371
1372
1374 double aScale )
1375{
1376 /*
1377 * Examples:
1378 * S!MANUFACTURING!NCLEGEND-1-6!CROSS!4!252571 1!-965.00!6906.00!6.00!6.00!0!!!!!!
1379 */
1380 auto new_cross = std::make_unique<GRAPHIC_CROSS>();
1381
1382 new_cross->shape = GR_SHAPE_CROSS;
1383 new_cross->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1384 new_cross->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1385 new_cross->size_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1386 new_cross->size_y = KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1387 new_cross->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1388
1389 return new_cross.release();
1390}
1391
1392
1394 double aScale )
1395{
1396 GRAPHIC_TEXT* new_text = new GRAPHIC_TEXT;
1397
1398 new_text->shape = GR_SHAPE_TEXT;
1399 new_text->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1400 new_text->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1401 new_text->rotation = KiROUND( readDouble( aData.graphic_data3 ) );
1402 new_text->mirror = ( aData.graphic_data4 == "YES" );
1403
1404 if( aData.graphic_data5 == "RIGHT" )
1405 new_text->orient = GR_TEXT_H_ALIGN_RIGHT;
1406 else if( aData.graphic_data5 == "CENTER" )
1407 new_text->orient = GR_TEXT_H_ALIGN_CENTER;
1408 else
1409 new_text->orient = GR_TEXT_H_ALIGN_LEFT;
1410
1411 std::vector<std::string> toks = split( aData.graphic_data6, " \t" );
1412
1413 if( toks.size() < 8 )
1414 {
1415 // We log the error here but continue in the case of too few tokens
1416 wxLogError( _( "Invalid token count. Expected 8 but found %zu." ), toks.size() );
1417 new_text->height = 0;
1418 new_text->width = 0;
1419 new_text->ital = false;
1420 new_text->thickness = 0;
1421 }
1422 else
1423 {
1424 // 0 = size
1425 // 1 = font
1426 new_text->height = KiROUND( readDouble( toks[2] ) * aScale );
1427 new_text->width = KiROUND( readDouble( toks[3] ) * aScale );
1428 new_text->ital = readDouble( toks[4] ) != 0.0;
1429 // 5 = character spacing
1430 // 6 = line spacing
1431 new_text->thickness = KiROUND( readDouble( toks[7] ) * aScale );
1432 }
1433
1434 new_text->text = aData.graphic_data7;
1435 return new_text;
1436}
1437
1438
1440{
1441 GRAPHIC_ITEM* retval = nullptr;
1442
1443 if( aData.graphic_dataname == "LINE" )
1444 retval = processLine( aData, aScale );
1445 else if( aData.graphic_dataname == "ARC" )
1446 retval = processArc( aData, aScale );
1447 else if( aData.graphic_dataname == "CIRCLE" )
1448 retval = processCircle( aData, aScale );
1449 else if( aData.graphic_dataname == "RECTANGLE" )
1450 retval = processRectangle( aData, aScale );
1451 else if( aData.graphic_dataname == "FIG_RECTANGLE" )
1452 retval = processFigRectangle( aData, aScale );
1453 else if( aData.graphic_dataname == "SQUARE" )
1454 retval = processSquare( aData, aScale );
1455 else if( aData.graphic_dataname == "OBLONG_X" || aData.graphic_dataname == "OBLONG_Y" )
1456 retval = processOblong( aData, aScale );
1457 else if( aData.graphic_dataname == "TRIANGLE_1" || aData.graphic_dataname == "DIAMOND"
1458 || aData.graphic_dataname == "HEXAGON_X" || aData.graphic_dataname == "HEXAGON_Y"
1459 || aData.graphic_dataname == "OCTAGON" )
1460 retval = processPolygon( aData, aScale );
1461 else if( aData.graphic_dataname == "CROSS" )
1462 retval = processCross( aData, aScale );
1463 else if( aData.graphic_dataname == "TEXT" )
1464 retval = processText( aData, aScale );
1465
1466 if( retval && !aData.graphic_data10.empty() )
1467 {
1468 if( aData.graphic_data10 == "CONNECT" )
1469 retval->type = GR_TYPE_CONNECT;
1470 else if( aData.graphic_data10 == "NOTCONNECT" )
1471 retval->type = GR_TYPE_NOTCONNECT;
1472 else if( aData.graphic_data10 == "SHAPE" )
1473 retval->type = GR_TYPE_NOTCONNECT;
1474 else if( aData.graphic_data10 == "VOID" )
1475 retval->type = GR_TYPE_NOTCONNECT;
1476 else if( aData.graphic_data10 == "POLYGON" )
1477 retval->type = GR_TYPE_NOTCONNECT;
1478 else
1479 retval->type = GR_TYPE_NONE;
1480 }
1481
1482 return retval;
1483}
1484
1485
1491size_t FABMASTER::processGeometry( size_t aRow )
1492{
1493 size_t rownum = aRow + 2;
1494
1495 if( rownum >= rows.size() )
1496 return -1;
1497
1498 const single_row& header = rows[aRow];
1499 double scale_factor = processScaleFactor( aRow + 1 );
1500
1501 if( scale_factor <= 0.0 )
1502 return -1;
1503
1504 int geo_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1505 int geo_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1506 int geo_tag_col = getColFromName( aRow, "RECORDTAG" );
1507 int geo_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1508 int geo_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1509 int geo_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1510 int geo_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1511 int geo_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1512 int geo_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1513 int geo_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1514 int geo_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1515 int geo_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1516 int geo_subclass_col = getColFromName( aRow, "SUBCLASS" );
1517 int geo_sym_name_col = getColFromName( aRow, "SYMNAME" );
1518 int geo_refdes_col = getColFromName( aRow, "REFDES" );
1519
1520 if( geo_name_col < 0 || geo_num_col < 0 || geo_grdata1_col < 0 || geo_grdata2_col < 0
1521 || geo_grdata3_col < 0 || geo_grdata4_col < 0 || geo_grdata5_col < 0
1522 || geo_grdata6_col < 0 || geo_grdata7_col < 0 || geo_grdata8_col < 0
1523 || geo_grdata9_col < 0 || geo_subclass_col < 0 || geo_sym_name_col < 0
1524 || geo_refdes_col < 0 )
1525 return -1;
1526
1527 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1528 {
1529 const single_row& row = rows[rownum];
1530
1531 if( row.size() != header.size() )
1532 {
1533 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1534 rownum,
1535 header.size(),
1536 row.size() );
1537 continue;
1538 }
1539
1540 auto& geo_tag = row[geo_tag_col];
1541
1542 GRAPHIC_DATA gr_data;
1543 gr_data.graphic_dataname = row[geo_name_col];
1544 gr_data.graphic_datanum = row[geo_num_col];
1545 gr_data.graphic_data1 = row[geo_grdata1_col];
1546 gr_data.graphic_data2 = row[geo_grdata2_col];
1547 gr_data.graphic_data3 = row[geo_grdata3_col];
1548 gr_data.graphic_data4 = row[geo_grdata4_col];
1549 gr_data.graphic_data5 = row[geo_grdata5_col];
1550 gr_data.graphic_data6 = row[geo_grdata6_col];
1551 gr_data.graphic_data7 = row[geo_grdata7_col];
1552 gr_data.graphic_data8 = row[geo_grdata8_col];
1553 gr_data.graphic_data9 = row[geo_grdata9_col];
1554
1555 auto& geo_refdes = row[geo_refdes_col];
1556
1557 // Grouped graphics are a series of records with the same record ID but incrementing
1558 // Sequence numbers.
1559 int id = -1;
1560 int seq = -1;
1561 int subseq = 0;
1562
1563 if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1564 {
1565 wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
1566 geo_tag.c_str(),
1567 rownum );
1568 continue;
1569 }
1570
1571 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1572
1573 if( !gr_item )
1574 {
1575 wxLogDebug( wxT( "Unhandled graphic item '%s' in row %zu." ),
1576 gr_data.graphic_dataname.c_str(),
1577 geo_tag.c_str(),
1578 rownum );
1579 continue;
1580 }
1581
1582 gr_item->layer = row[geo_subclass_col];
1583 gr_item->seq = seq;
1584 gr_item->subseq = subseq;
1585
1586 if( geo_refdes.empty() )
1587 {
1588 if( board_graphics.empty() || board_graphics.back().id != id )
1589 {
1590 GEOM_GRAPHIC new_gr;
1591 new_gr.subclass = row[geo_subclass_col];
1592 new_gr.refdes = row[geo_refdes_col];
1593 new_gr.name = row[geo_sym_name_col];
1594 new_gr.id = id;
1595 new_gr.elements = std::make_unique<graphic_element>();
1596 board_graphics.push_back( std::move( new_gr ) );
1597 }
1598
1599 GEOM_GRAPHIC& graphic = board_graphics.back();
1600 graphic.elements->emplace( std::move( gr_item ) );
1601 }
1602 else
1603 {
1604 auto sym_gr_it = comp_graphics.emplace( geo_refdes,
1605 std::map<int, GEOM_GRAPHIC>{} );
1606 auto map_it = sym_gr_it.first->second.emplace( id, GEOM_GRAPHIC{} );
1607 auto& gr = map_it.first;
1608
1609 if( map_it.second )
1610 {
1611 gr->second.subclass = row[geo_subclass_col];
1612 gr->second.refdes = row[geo_refdes_col];
1613 gr->second.name = row[geo_sym_name_col];
1614 gr->second.id = id;
1615 gr->second.elements = std::make_unique<graphic_element>();
1616 }
1617
1618 auto result = gr->second.elements->emplace( std::move( gr_item ) );
1619 }
1620 }
1621
1622 return rownum - aRow;
1623}
1624
1625
1629size_t FABMASTER::processVias( size_t aRow )
1630{
1631 size_t rownum = aRow + 2;
1632
1633 if( rownum >= rows.size() )
1634 return -1;
1635
1636 const single_row& header = rows[aRow];
1637 double scale_factor = processScaleFactor( aRow + 1 );
1638
1639 if( scale_factor <= 0.0 )
1640 return -1;
1641
1642 int viax_col = getColFromName( aRow, "VIAX" );
1643 int viay_col = getColFromName( aRow, "VIAY" );
1644 int padstack_name_col = getColFromName( aRow, "PADSTACKNAME" );
1645 int net_name_col = getColFromName( aRow, "NETNAME" );
1646 int test_point_col = getColFromName( aRow, "TESTPOINT" );
1647
1648 if( viax_col < 0 || viay_col < 0 || padstack_name_col < 0 || net_name_col < 0
1649 || test_point_col < 0 )
1650 return -1;
1651
1652 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1653 {
1654 const single_row& row = rows[rownum];
1655
1656 if( row.size() != header.size() )
1657 {
1658 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1659 rownum,
1660 header.size(),
1661 row.size() );
1662 continue;
1663 }
1664
1665 vias.emplace_back( std::make_unique<FM_VIA>() );
1666 auto& via = vias.back();
1667
1668 via->x = KiROUND( readDouble( row[viax_col] ) * scale_factor );
1669 via->y = -KiROUND( readDouble( row[viay_col] ) * scale_factor );
1670 via->padstack = row[padstack_name_col];
1671 via->net = row[net_name_col];
1672 via->test_point = ( row[test_point_col] == "YES" );
1673 }
1674
1675 return rownum - aRow;
1676}
1677
1678
1684size_t FABMASTER::processTraces( size_t aRow )
1685{
1686 size_t rownum = aRow + 2;
1687
1688 if( rownum >= rows.size() )
1689 return -1;
1690
1691 const single_row& header = rows[aRow];
1692 double scale_factor = processScaleFactor( aRow + 1 );
1693
1694 if( scale_factor <= 0.0 )
1695 return -1;
1696
1697 int class_col = getColFromName( aRow, "CLASS" );
1698 int layer_col = getColFromName( aRow, "SUBCLASS" );
1699 int grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1700 int grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1701 int tag_col = getColFromName( aRow, "RECORDTAG" );
1702 int grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1703 int grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1704 int grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1705 int grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1706 int grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1707 int grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1708 int grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1709 int grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1710 int grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1711 int netname_col = getColFromName( aRow, "NETNAME" );
1712
1713 if( class_col < 0 || layer_col < 0 || grdata_name_col < 0 || grdata_num_col < 0
1714 || tag_col < 0 || grdata1_col < 0 || grdata2_col < 0 || grdata3_col < 0
1715 || grdata4_col < 0 || grdata5_col < 0 || grdata6_col < 0 || grdata7_col < 0
1716 || grdata8_col < 0 || grdata9_col < 0 || netname_col < 0 )
1717 return -1;
1718
1719 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1720 {
1721 const single_row& row = rows[rownum];
1722
1723 if( row.size() != header.size() )
1724 {
1725 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1726 rownum,
1727 header.size(),
1728 row.size() );
1729 continue;
1730 }
1731
1732 GRAPHIC_DATA gr_data;
1733 gr_data.graphic_dataname = row[grdata_name_col];
1734 gr_data.graphic_datanum = row[grdata_num_col];
1735 gr_data.graphic_data1 = row[grdata1_col];
1736 gr_data.graphic_data2 = row[grdata2_col];
1737 gr_data.graphic_data3 = row[grdata3_col];
1738 gr_data.graphic_data4 = row[grdata4_col];
1739 gr_data.graphic_data5 = row[grdata5_col];
1740 gr_data.graphic_data6 = row[grdata6_col];
1741 gr_data.graphic_data7 = row[grdata7_col];
1742 gr_data.graphic_data8 = row[grdata8_col];
1743 gr_data.graphic_data9 = row[grdata9_col];
1744
1745 const std::string& geo_tag = row[tag_col];
1746 // Grouped graphics are a series of records with the same record ID but incrementing
1747 // Sequence numbers.
1748 int id = -1;
1749 int seq = -1;
1750 int subseq = 0;
1751
1752 if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1753 {
1754 wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
1755 geo_tag.c_str(),
1756 rownum );
1757 continue;
1758 }
1759
1760 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1761
1762 if( !gr_item )
1763 {
1764 wxLogTrace( traceFabmaster, _( "Unhandled graphic item '%s' in row %zu." ),
1765 gr_data.graphic_dataname.c_str(),
1766 rownum );
1767 continue;
1768 }
1769
1770 auto new_trace = std::make_unique<TRACE>();
1771 new_trace->id = id;
1772 new_trace->layer = row[layer_col];
1773 new_trace->netname = row[netname_col];
1774 new_trace->lclass = row[class_col];
1775
1776 gr_item->layer = row[layer_col];
1777 gr_item->seq = seq;
1778 gr_item->subseq = subseq;
1779
1780 // Collect the reference designator positions for the footprints later
1781 if( new_trace->lclass == "REF DES" )
1782 {
1783 auto result = refdes.emplace( std::move( new_trace ) );
1784 auto& ref = *result.first;
1785 ref->segment.emplace( std::move( gr_item ) );
1786 }
1787 else if( new_trace->lclass == "DEVICE TYPE" || new_trace->lclass == "COMPONENT VALUE"
1788 || new_trace->lclass == "TOLERANCE" )
1789 {
1790 // TODO: This seems like a value field, but it's not immediately clear how to map it
1791 // to the right footprint.
1792 // So these spam the board with huge amount of overlapping text.
1793
1794 // Examples:
1795 // 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!!!!
1796 // 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!!!!
1797 // 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!!!!
1798
1799 // For now, just don't do anything with them.
1800 }
1801 else if( gr_item->width == 0 )
1802 {
1803 auto result = zones.emplace( std::move( new_trace ) );
1804 auto& zone = *result.first;
1805 auto gr_result = zone->segment.emplace( std::move( gr_item ) );
1806
1807 if( !gr_result.second )
1808 {
1809 wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
1810 id,
1811 seq,
1812 rownum );
1813 }
1814 }
1815 else
1816 {
1817 auto result = traces.emplace( std::move( new_trace ) );
1818 auto& trace = *result.first;
1819 auto gr_result = trace->segment.emplace( std::move( gr_item ) );
1820
1821 if( !gr_result.second )
1822 {
1823 wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
1824 id,
1825 seq,
1826 rownum );
1827 }
1828 }
1829 }
1830
1831 return rownum - aRow;
1832}
1833
1834
1835FABMASTER::SYMTYPE FABMASTER::parseSymType( const std::string& aSymType )
1836{
1837 if( aSymType == "PACKAGE" )
1838 return SYMTYPE_PACKAGE;
1839 else if( aSymType == "DRAFTING")
1840 return SYMTYPE_DRAFTING;
1841 else if( aSymType == "MECHANICAL" )
1842 return SYMTYPE_MECH;
1843 else if( aSymType == "FORMAT" )
1844 return SYMTYPE_FORMAT;
1845
1846 return SYMTYPE_NONE;
1847}
1848
1849
1851{
1852 if( aCmpClass == "IO" )
1853 return COMPCLASS_IO;
1854 else if( aCmpClass == "IC" )
1855 return COMPCLASS_IC;
1856 else if( aCmpClass == "DISCRETE" )
1857 return COMPCLASS_DISCRETE;
1858
1859 return COMPCLASS_NONE;
1860}
1861
1862
1867size_t FABMASTER::processFootprints( size_t aRow )
1868{
1869 size_t rownum = aRow + 2;
1870
1871 if( rownum >= rows.size() )
1872 return -1;
1873
1874 const single_row& header = rows[aRow];
1875 double scale_factor = processScaleFactor( aRow + 1 );
1876
1877 if( scale_factor <= 0.0 )
1878 return -1;
1879
1880 int refdes_col = getColFromName( aRow, "REFDES" );
1881 int compclass_col = getColFromName( aRow, "COMPCLASS" );
1882 int comppartnum_col = getColFromName( aRow, "COMPPARTNUMBER" );
1883 int compheight_col = getColFromName( aRow, "COMPHEIGHT" );
1884 int compdevlabelcol = getColFromName( aRow, "COMPDEVICELABEL" );
1885 int compinscode_col = getColFromName( aRow, "COMPINSERTIONCODE" );
1886 int symtype_col = getColFromName( aRow, "SYMTYPE" );
1887 int symname_col = getColFromName( aRow, "SYMNAME" );
1888 int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1889 int symrotate_col = getColFromName( aRow, "SYMROTATE" );
1890 int symx_col = getColFromName( aRow, "SYMX" );
1891 int symy_col = getColFromName( aRow, "SYMY" );
1892 int compvalue_col = getColFromName( aRow, "COMPVALUE" );
1893 int comptol_col = getColFromName( aRow, "COMPTOL" );
1894 int compvolt_col = getColFromName( aRow, "COMPVOLTAGE" );
1895
1896 if( refdes_col < 0 || compclass_col < 0 || comppartnum_col < 0 || compheight_col < 0
1897 || compdevlabelcol < 0 || compinscode_col < 0 || symtype_col < 0 || symname_col < 0
1898 || symmirror_col < 0 || symrotate_col < 0 || symx_col < 0 || symy_col < 0
1899 || compvalue_col < 0 || comptol_col < 0 || compvolt_col < 0 )
1900 return -1;
1901
1902 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1903 {
1904 const single_row& row = rows[rownum];
1905
1906 if( row.size() != header.size() )
1907 {
1908 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1909 rownum,
1910 header.size(),
1911 row.size() );
1912 continue;
1913 }
1914
1915 const wxString& refdes = row[refdes_col];
1916
1917 if( row[symx_col].empty() || row[symy_col].empty() || row[symrotate_col].empty() )
1918 {
1919 wxLogError( _( "Missing X, Y, or rotation data in row %zu for refdes %s. "
1920 "This may be an unplaced component." ),
1921 rownum, refdes );
1922 continue;
1923 }
1924
1925 auto cmp = std::make_unique<COMPONENT>();
1926
1927 cmp->refdes = refdes;
1928 cmp->cclass = parseCompClass( row[compclass_col] );
1929 cmp->pn = row[comppartnum_col];
1930 cmp->height = row[compheight_col];
1931 cmp->dev_label = row[compdevlabelcol];
1932 cmp->insert_code = row[compinscode_col];
1933 cmp->type = parseSymType( row[symtype_col] );
1934 cmp->name = row[symname_col];
1935 cmp->mirror = ( row[symmirror_col] == "YES" );
1936 cmp->rotate = readDouble( row[symrotate_col] );
1937 cmp->x = KiROUND( readDouble( row[symx_col] ) * scale_factor );
1938 cmp->y = -KiROUND( readDouble( row[symy_col] ) * scale_factor );
1939 cmp->value = row[compvalue_col];
1940 cmp->tol = row[comptol_col];
1941 cmp->voltage = row[compvolt_col];
1942
1943 auto vec = components.find( cmp->refdes );
1944
1945 if( vec == components.end() )
1946 {
1947 auto retval = components.insert( std::make_pair( cmp->refdes, std::vector<std::unique_ptr<COMPONENT>>{} ) );
1948
1949 vec = retval.first;
1950 }
1951
1952 vec->second.push_back( std::move( cmp ) );
1953 }
1954
1955 return rownum - aRow;
1956}
1957
1958
1963size_t FABMASTER::processPins( size_t aRow )
1964{
1965 size_t rownum = aRow + 2;
1966
1967 if( rownum >= rows.size() )
1968 return -1;
1969
1970 const single_row& header = rows[aRow];
1971 double scale_factor = processScaleFactor( aRow + 1 );
1972
1973 if( scale_factor <= 0.0 )
1974 return -1;
1975
1976 int symname_col = getColFromName( aRow, "SYMNAME" );
1977 int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1978 int pinname_col = getColFromName( aRow, "PINNAME" );
1979 int pinnum_col = getColFromName( aRow, "PINNUMBER" );
1980 int pinx_col = getColFromName( aRow, "PINX" );
1981 int piny_col = getColFromName( aRow, "PINY" );
1982 int padstack_col = getColFromName( aRow, "PADSTACKNAME" );
1983 int refdes_col = getColFromName( aRow, "REFDES" );
1984 int pinrot_col = getColFromName( aRow, "PINROTATION" );
1985 int testpoint_col = getColFromName( aRow, "TESTPOINT" );
1986
1987 if( symname_col < 0 ||symmirror_col < 0 || pinname_col < 0 || pinnum_col < 0 || pinx_col < 0
1988 || piny_col < 0 || padstack_col < 0 || refdes_col < 0 || pinrot_col < 0
1989 || testpoint_col < 0 )
1990 return -1;
1991
1992 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1993 {
1994 const single_row& row = rows[rownum];
1995
1996 if( row.size() != header.size() )
1997 {
1998 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1999 rownum,
2000 header.size(),
2001 row.size() );
2002 continue;
2003 }
2004
2005 auto pin = std::make_unique<PIN>();
2006
2007 pin->name = row[symname_col];
2008 pin->mirror = ( row[symmirror_col] == "YES" );
2009 pin->pin_name = row[pinname_col];
2010 pin->pin_number = row[pinnum_col];
2011 pin->pin_x = KiROUND( readDouble( row[pinx_col] ) * scale_factor );
2012 pin->pin_y = -KiROUND( readDouble( row[piny_col] ) * scale_factor );
2013 pin->padstack = row[padstack_col];
2014 pin->refdes = row[refdes_col];
2015 pin->rotation = readDouble( row[pinrot_col] );
2016
2017 auto map_it = pins.find( pin->refdes );
2018
2019 if( map_it == pins.end() )
2020 {
2021 auto retval = pins.insert( std::make_pair( pin->refdes, std::set<std::unique_ptr<PIN>,
2022 PIN::BY_NUM>{} ) );
2023 map_it = retval.first;
2024 }
2025
2026 map_it->second.insert( std::move( pin ) );
2027 }
2028
2029 return rownum - aRow;
2030}
2031
2032
2036size_t FABMASTER::processNets( size_t aRow )
2037{
2038 size_t rownum = aRow + 2;
2039
2040 if( rownum >= rows.size() )
2041 return -1;
2042
2043 const single_row& header = rows[aRow];
2044 double scale_factor = processScaleFactor( aRow + 1 );
2045
2046 if( scale_factor <= 0.0 )
2047 return -1;
2048
2049 int netname_col = getColFromName( aRow, "NETNAME" );
2050 int refdes_col = getColFromName( aRow, "REFDES" );
2051 int pinnum_col = getColFromName( aRow, "PINNUMBER" );
2052 int pinname_col = getColFromName( aRow, "PINNAME" );
2053 int pingnd_col = getColFromName( aRow, "PINGROUND" );
2054 int pinpwr_col = getColFromName( aRow, "PINPOWER" );
2055
2056 if( netname_col < 0 || refdes_col < 0 || pinnum_col < 0 || pinname_col < 0 || pingnd_col < 0
2057 || pinpwr_col < 0 )
2058 return -1;
2059
2060 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
2061 {
2062 const single_row& row = rows[rownum];
2063
2064 if( row.size() != header.size() )
2065 {
2066 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
2067 rownum,
2068 header.size(),
2069 row.size() );
2070 continue;
2071 }
2072
2073 NETNAME new_net;
2074 new_net.name = row[netname_col];
2075 new_net.refdes = row[refdes_col];
2076 new_net.pin_num = row[pinnum_col];
2077 new_net.pin_name = row[pinname_col];
2078 new_net.pin_gnd = ( row[pingnd_col] == "YES" );
2079 new_net.pin_pwr = ( row[pinpwr_col] == "YES" );
2080
2081 pin_nets.emplace( std::make_pair( new_net.refdes, new_net.pin_num ), new_net );
2082 netnames.insert( row[netname_col] );
2083 }
2084
2085 return rownum - aRow;
2086}
2087
2088
2090{
2091
2092 for( size_t i = 0; i < rows.size(); )
2093 {
2094 auto type = detectType( i );
2095
2096 switch( type )
2097 {
2098 case EXTRACT_PADSTACKS:
2099 {
2103 assignLayers();
2104 int retval = processPadStacks( i );
2105
2106 i += std::max( retval, 1 );
2107 break;
2108 }
2109
2111 {
2112 int retval = processLayers( i );
2113
2114 i += std::max( retval, 1 );
2115 break;
2116 }
2117
2119 {
2120 int retval = processSimpleLayers( i );
2121
2122 i += std::max( retval, 1 );
2123 break;
2124 }
2125
2126 case EXTRACT_VIAS:
2127 {
2128 int retval = processVias( i );
2129
2130 i += std::max( retval, 1 );
2131 break;
2132 }
2133
2134 case EXTRACT_TRACES:
2135 {
2136 int retval = processTraces( i );
2137
2138 i += std::max( retval, 1 );
2139 break;
2140 }
2141
2142 case EXTRACT_REFDES:
2143 {
2144 int retval = processFootprints( i );
2145
2146 i += std::max( retval, 1 );
2147 break;
2148 }
2149
2150 case EXTRACT_NETS:
2151 {
2152 int retval = processNets( i );
2153
2154 i += std::max( retval, 1 );
2155 break;
2156 }
2157
2158 case EXTRACT_GRAPHICS:
2159 {
2160 int retval = processGeometry( i );
2161
2162 i += std::max( retval, 1 );
2163 break;
2164 }
2165
2166 case EXTRACT_PINS:
2167 {
2168 int retval = processPins( i );
2169
2170 i += std::max( retval, 1 );
2171 break;
2172 }
2173
2174 case EXTRACT_PAD_SHAPES:
2175 {
2176 int retval = processCustomPads( i );
2177
2178 i += std::max( retval, 1 );
2179 break;
2180 }
2181
2182 default:
2183 ++i;
2184 break;
2185 }
2186
2187 }
2188
2189 return true;
2190}
2191
2192
2194{
2195 for( auto& zone : zones )
2196 {
2197 checkpoint();
2198
2199 if( IsCopperLayer( getLayer( zone->layer ) ) || zone->layer == "ALL" )
2200 {
2201 loadZone( aBoard, zone );
2202 }
2203 else
2204 {
2205 if( zone->layer == "OUTLINE" || zone->layer == "DESIGN_OUTLINE" )
2206 {
2207 loadOutline( aBoard, zone );
2208 }
2209 else
2210 {
2211 loadPolygon( aBoard, zone );
2212 }
2213 }
2214 }
2215
2226 std::set<ZONE*> zones_to_delete;
2227
2228 for( auto zone : aBoard->Zones() )
2229 {
2231 if( zone->GetNetCode() > 0 )
2232 {
2233 zones_to_delete.insert( zone );
2234 }
2235 }
2236
2237 for( auto zone1 : aBoard->Zones() )
2238 {
2240 if( zone1->GetNetCode() > 0 )
2241 continue;
2242
2243 SHAPE_LINE_CHAIN& outline1 = zone1->Outline()->Outline( 0 );
2244 std::vector<size_t> overlaps( aBoard->GetNetInfo().GetNetCount() + 1, 0 );
2245 std::vector<std::vector<ZONE*>> possible_deletions( overlaps.size() );
2246
2247 for( auto zone2 : aBoard->Zones() )
2248 {
2249 if( zone2->GetNetCode() <= 0 )
2250 continue;
2251
2252 SHAPE_LINE_CHAIN& outline2 = zone2->Outline()->Outline( 0 );
2253
2254 if( zone1->GetLayer() != zone2->GetLayer() )
2255 continue;
2256
2257 if( !outline1.BBox().Intersects( outline2.BBox() ) )
2258 continue;
2259
2260 for( auto& pt1 : outline1.CPoints() )
2261 {
2263 if( outline2.PointOnEdge( pt1, 1 ) )
2264 overlaps[ zone2->GetNetCode() ]++;
2265 }
2266
2267 for( auto& pt2 : outline2.CPoints() )
2268 {
2271 if( outline1.PointOnEdge( pt2, 1 ) )
2272 overlaps[ zone2->GetNetCode() ]++;
2273 }
2274 }
2275
2276 size_t max_net = 0;
2277 size_t max_net_id = 0;
2278
2279 for( size_t el = 1; el < overlaps.size(); ++el )
2280 {
2281 if( overlaps[el] > max_net )
2282 {
2283 max_net = overlaps[el];
2284 max_net_id = el;
2285 }
2286 }
2287
2288 if( max_net > 0 )
2289 zone1->SetNetCode( max_net_id );
2290 }
2291
2292 for( auto zone : zones_to_delete )
2293 {
2294 aBoard->Remove( zone );
2295 delete zone;
2296 }
2297
2298 return true;
2299}
2300
2301
2303 PCB_TEXT& aText, const BOARD& aBoard, const OPT_VECTOR2I& aMirrorPoint )
2304{
2305 aText.SetHorizJustify( aGText.orient );
2306
2307 aText.SetKeepUpright( false );
2308
2309 EDA_ANGLE angle = EDA_ANGLE( aGText.rotation );
2310 angle.Normalize180();
2311
2312 if( aMirrorPoint.has_value() )
2313 {
2314 aText.SetLayer( aBoard.FlipLayer( aLayer ) );
2315 aText.SetTextPos( VECTOR2I(
2316 aGText.start_x, 2 * aMirrorPoint->y - ( aGText.start_y - aGText.height / 2 ) ) );
2317 aText.SetMirrored( !aGText.mirror );
2318
2319 aText.SetTextAngle( -angle + ANGLE_180 );
2320 }
2321 else
2322 {
2323 aText.SetLayer( aLayer );
2324 aText.SetTextPos( VECTOR2I( aGText.start_x, aGText.start_y - aGText.height / 2 ) );
2325 aText.SetMirrored( aGText.mirror );
2326
2327 aText.SetTextAngle( angle );
2328 }
2329
2330 if( std::abs( angle ) >= ANGLE_90 )
2331 {
2333 }
2334
2335 aText.SetText( aGText.text );
2336 aText.SetItalic( aGText.ital );
2337 aText.SetTextThickness( aGText.thickness );
2338 aText.SetTextHeight( aGText.height );
2339 aText.SetTextWidth( aGText.width );
2340}
2341
2342
2344{
2345 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2346 const auto& ds = aBoard->GetDesignSettings();
2347
2348 for( auto& mod : components )
2349 {
2350 checkpoint();
2351
2352 bool has_multiple = mod.second.size() > 1;
2353
2354 for( int i = 0; i < mod.second.size(); ++i )
2355 {
2356 auto& src = mod.second[i];
2357
2358 FOOTPRINT* fp = new FOOTPRINT( aBoard );
2359
2360 wxString mod_ref = src->name;
2361 wxString lib_ref = m_filename.GetName();
2362
2363 if( has_multiple )
2364 mod_ref.Append( wxString::Format( wxT( "_%d" ), i ) );
2365
2366 ReplaceIllegalFileNameChars( lib_ref, '_' );
2367 ReplaceIllegalFileNameChars( mod_ref, '_' );
2368
2369 wxString key = !lib_ref.empty() ? lib_ref + wxT( ":" ) + mod_ref : mod_ref;
2370
2371 LIB_ID fpID;
2372 fpID.Parse( key, true );
2373 fp->SetFPID( fpID );
2374
2375 fp->SetPosition( VECTOR2I( src->x, src->y ) );
2376 fp->SetOrientationDegrees( -src->rotate );
2377
2378 // KiCad netlisting requires parts to have non-digit + digit annotation.
2379 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source
2380 // designator
2381 wxString reference = src->refdes;
2382
2383 if( !std::isalpha( src->refdes[0] ) )
2384 reference.Prepend( "UNK" );
2385
2386 fp->SetReference( reference );
2387
2388 fp->SetValue( src->value );
2389 fp->Value().SetLayer( F_Fab );
2390 fp->Value().SetVisible( false );
2391
2392 // Set refdes invisible until we find the text for it
2393 // (otherwise we'll plonk a default-sized ref-des on the silkscreen layer
2394 // which wasn't there in the imported file)
2395 fp->Reference().SetVisible( false );
2396
2397 for( auto& ref : refdes )
2398 {
2399 const GRAPHIC_TEXT& lsrc =
2400 static_cast<const GRAPHIC_TEXT&>( **ref->segment.begin() );
2401
2402 if( lsrc.text == src->refdes )
2403 {
2404 PCB_TEXT* txt = nullptr;
2405 PCB_LAYER_ID layer = getLayer( ref->layer );
2406
2407 if( !IsPcbLayer( layer ) )
2408 {
2409 wxLogTrace( traceFabmaster, wxS( "The layer %s is not mapped?" ),
2410 ref->layer.c_str() );
2411 continue;
2412 }
2413
2414 if( layer == F_SilkS || layer == B_SilkS )
2415 txt = &( fp->Reference() );
2416 else
2417 txt = new PCB_TEXT( fp );
2418
2419 OPT_VECTOR2I flip_point = std::nullopt;
2420 if( src->mirror )
2421 flip_point = VECTOR2I( src->x, src->y );
2422
2423 const EDA_ANGLE fp_angle = EDA_ANGLE( lsrc.rotation ).Normalized();
2424 txt->SetTextAngle( fp_angle );
2425
2426 setupText( lsrc, layer, *txt, *aBoard, flip_point );
2427
2428 if( txt != &fp->Reference() )
2429 fp->Add( txt, ADD_MODE::APPEND );
2430 }
2431 }
2432
2435 fp->SetLayer( F_Cu );
2436
2437 auto gr_it = comp_graphics.find( src->refdes );
2438
2439 if( gr_it == comp_graphics.end() )
2440 {
2441 continue;
2442 //TODO: Error
2443 }
2444
2445 for( auto& gr_ref : gr_it->second )
2446 {
2447 auto& graphic = gr_ref.second;
2448
2449 for( auto& seg : *graphic.elements )
2450 {
2451 PCB_LAYER_ID layer = Dwgs_User;
2452
2453 if( IsPcbLayer( getLayer( seg->layer ) ) )
2454 layer = getLayer( seg->layer );
2455
2456 STROKE_PARAMS defaultStroke( ds.GetLineThickness( layer ) );
2457
2458 switch( seg->shape )
2459 {
2460
2461 case GR_SHAPE_LINE:
2462 {
2463 const GRAPHIC_LINE* lsrc = static_cast<const GRAPHIC_LINE*>( seg.get() );
2464
2465 PCB_SHAPE* line = new PCB_SHAPE( fp, SHAPE_T::SEGMENT );
2466
2467 if( src->mirror )
2468 {
2469 line->SetLayer( aBoard->FlipLayer( layer ) );
2470 line->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2471 line->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2472 }
2473 else
2474 {
2475 line->SetLayer( layer );
2476 line->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
2477 line->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2478 }
2479
2480 line->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
2481
2482 if( lsrc->width == 0 )
2483 line->SetStroke( defaultStroke );
2484
2485 fp->Add( line, ADD_MODE::APPEND );
2486 break;
2487 }
2488 case GR_SHAPE_CIRCLE:
2489 {
2490 const GRAPHIC_ARC& lsrc = static_cast<const GRAPHIC_ARC&>( *seg );
2491
2492 PCB_SHAPE* circle = new PCB_SHAPE( fp, SHAPE_T::CIRCLE );
2493
2494 circle->SetLayer( layer );
2495 circle->SetCenter( VECTOR2I( lsrc.center_x, lsrc.center_y ) );
2496 circle->SetEnd( VECTOR2I( lsrc.end_x, lsrc.end_y ) );
2497 circle->SetWidth( lsrc.width );
2498
2499 if( IsBackLayer( layer ) )
2500 {
2501 // Circles seem to have a flip around the FP origin that lines don't have
2502 const VECTOR2I fp_orig = fp->GetPosition();
2503 circle->Mirror( fp_orig, FLIP_DIRECTION::TOP_BOTTOM );
2504 }
2505
2506 if( lsrc.width == 0 )
2507 {
2508 // It seems that 0-width circles on DISPLAY_T/B layers are filled
2509 // (but not, say, SILKSCREEN_T/B).
2510 // There is an oblique reference to something like this here:
2511 // https://github.com/plusea/EAGLE/blob/master/ulp/fabmaster.ulp
2512 if( lsrc.layer == "DISPLAY_TOP" || lsrc.layer == "DISPLAY_BOTTOM" )
2513 circle->SetFilled( true );
2514 else
2515 circle->SetWidth( ds.GetLineThickness( circle->GetLayer() ) );
2516 }
2517
2518 if( src->mirror )
2519 circle->Flip( circle->GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
2520
2521 fp->Add( circle, ADD_MODE::APPEND );
2522 break;
2523 }
2524 case GR_SHAPE_ARC:
2525 {
2526 const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
2527
2528 std::unique_ptr<PCB_SHAPE> arc =
2529 std::make_unique<PCB_SHAPE>( fp, SHAPE_T::ARC );
2530
2531 SHAPE_ARC sarc = lsrc->result;
2532
2533 if( IsBackLayer( layer ) )
2534 {
2535 // Arcs seem to have a vertical flip around the FP origin that lines don't have
2536 // and are also flipped around their center (this is a best guess at the transformation)
2537 const VECTOR2I fp_orig = fp->GetPosition();
2538 sarc.Mirror( fp_orig, FLIP_DIRECTION::TOP_BOTTOM );
2539 sarc.Mirror( sarc.GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
2540 }
2541
2542 arc->SetLayer( layer );
2543 arc->SetArcGeometry( sarc.GetP0(), sarc.GetArcMid(), sarc.GetP1() );
2544 arc->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
2545
2546 if( lsrc->width == 0 )
2547 arc->SetStroke( defaultStroke );
2548
2549 if( src->mirror )
2550 arc->Flip( arc->GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
2551
2552 fp->Add( arc.release(), ADD_MODE::APPEND );
2553 break;
2554 }
2555 case GR_SHAPE_RECTANGLE:
2556 {
2557 const GRAPHIC_RECTANGLE *lsrc =
2558 static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2559
2560 PCB_SHAPE* rect = new PCB_SHAPE( fp, SHAPE_T::RECTANGLE );
2561
2562 if( src->mirror )
2563 {
2564 rect->SetLayer( aBoard->FlipLayer( layer ) );
2565 rect->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2566 rect->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2567 }
2568 else
2569 {
2570 rect->SetLayer( layer );
2571 rect->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
2572 rect->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2573 }
2574
2575 rect->SetStroke( defaultStroke );
2576
2577 fp->Add( rect, ADD_MODE::APPEND );
2578 break;
2579 }
2580 case GR_SHAPE_TEXT:
2581 {
2582 const GRAPHIC_TEXT& lsrc = static_cast<const GRAPHIC_TEXT&>( *seg );
2583
2584 std::unique_ptr<PCB_TEXT> txt = std::make_unique<PCB_TEXT>( fp );
2585
2586 OPT_VECTOR2I flip_point;
2587
2588 if( src->mirror )
2589 flip_point = VECTOR2I( src->x, src->y );
2590
2591 setupText( lsrc, layer, *txt, *aBoard, flip_point );
2592
2593 // FABMASTER doesn't have visibility flags but layers that are not silk
2594 // should be hidden by default to prevent clutter.
2595 if( txt->GetLayer() != F_SilkS && txt->GetLayer() != B_SilkS )
2596 {
2597 PCB_FIELD* field = new PCB_FIELD( *txt, FIELD_T::USER );
2598 field->SetVisible( false );
2599 fp->Add( field, ADD_MODE::APPEND );
2600 }
2601 else
2602 {
2603 fp->Add( txt.release(), ADD_MODE::APPEND );
2604 }
2605
2606 break;
2607 }
2608 default:
2609 continue;
2610 }
2611 }
2612 }
2613
2614 auto pin_it = pins.find( src->refdes );
2615
2616 if( pin_it != pins.end() )
2617 {
2618 for( auto& pin : pin_it->second )
2619 {
2620 auto pin_net_it = pin_nets.find( std::make_pair( pin->refdes,
2621 pin->pin_number ) );
2622 auto padstack = pads.find( pin->padstack );
2623 std::string netname = "";
2624
2625 if( pin_net_it != pin_nets.end() )
2626 netname = pin_net_it->second.name;
2627
2628 auto net_it = netinfo.find( netname );
2629
2630 std::unique_ptr<PAD> newpad = std::make_unique<PAD>( fp );
2631
2632 if( net_it != netinfo.end() )
2633 newpad->SetNet( net_it->second );
2634 else
2635 newpad->SetNetCode( 0 );
2636
2637 newpad->SetX( pin->pin_x );
2638
2639 if( src->mirror )
2640 newpad->SetY( 2 * src->y - pin->pin_y );
2641 else
2642 newpad->SetY( pin->pin_y );
2643
2644 newpad->SetNumber( pin->pin_number );
2645
2646 if( padstack == pads.end() )
2647 {
2648 wxLogError( _( "Unable to locate padstack %s in file %s\n" ),
2649 pin->padstack.c_str(), aBoard->GetFileName().wc_str() );
2650 continue;
2651 }
2652 else
2653 {
2654 auto& pad = padstack->second;
2655
2656 newpad->SetShape( PADSTACK::ALL_LAYERS, pad.shape );
2657
2658 if( pad.shape == PAD_SHAPE::CUSTOM )
2659 {
2660 // Choose the smaller dimension to ensure the base pad
2661 // is fully hidden by the custom pad
2662 int pad_size = std::min( pad.width, pad.height );
2663
2664 newpad->SetSize( PADSTACK::ALL_LAYERS,
2665 VECTOR2I( pad_size / 2, pad_size / 2 ) );
2666
2667 std::string custom_name = pad.custom_name + "_" + pin->refdes + "_" +
2668 pin->pin_number;
2669 auto custom_it = pad_shapes.find( custom_name );
2670
2671 if( custom_it != pad_shapes.end() )
2672 {
2673
2674 SHAPE_POLY_SET poly_outline;
2675 int last_subseq = 0;
2676 int hole_idx = -1;
2677
2678 poly_outline.NewOutline();
2679
2680 // Custom pad shapes have a group of elements
2681 // that are a list of graphical polygons
2682 for( const auto& el : (*custom_it).second.elements )
2683 {
2684 // For now, we are only processing the custom pad for the
2685 // top layer
2686 // TODO: Use full padstacks when implementing in KiCad
2687 PCB_LAYER_ID primary_layer = src->mirror ? B_Cu : F_Cu;
2688
2689 if( getLayer( ( *( el.second.begin() ) )->layer ) != primary_layer )
2690 continue;
2691
2692 for( const auto& seg : el.second )
2693 {
2694 if( seg->subseq > 0 || seg->subseq != last_subseq )
2695 {
2696 poly_outline.Polygon(0).back().SetClosed( true );
2697 hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
2698 }
2699
2700 if( seg->shape == GR_SHAPE_LINE )
2701 {
2702 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2703
2704 if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
2705 poly_outline.Append( src->start_x, src->start_y,
2706 0, hole_idx );
2707
2708 poly_outline.Append( src->end_x, src->end_y, 0,
2709 hole_idx );
2710 }
2711 else if( seg->shape == GR_SHAPE_ARC )
2712 {
2713 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2714 SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
2715
2716 chain.Append( src->result );
2717 }
2718 }
2719 }
2720
2721 if( poly_outline.OutlineCount() < 1
2722 || poly_outline.Outline( 0 ).PointCount() < 3 )
2723 {
2724 wxLogError( _( "Invalid custom pad '%s'. Replacing with "
2725 "circular pad." ),
2726 custom_name.c_str() );
2727 newpad->SetShape( F_Cu, PAD_SHAPE::CIRCLE );
2728 }
2729 else
2730 {
2731 poly_outline.Fracture();
2732
2733 poly_outline.Move( -newpad->GetPosition() );
2734
2735 if( src->mirror )
2736 {
2737 poly_outline.Mirror( VECTOR2I( 0, ( pin->pin_y - src->y ) ),
2738 FLIP_DIRECTION::TOP_BOTTOM );
2739 poly_outline.Rotate( EDA_ANGLE( src->rotate - pin->rotation,
2740 DEGREES_T ) );
2741 }
2742 else
2743 {
2744 poly_outline.Rotate( EDA_ANGLE( -src->rotate + pin->rotation,
2745 DEGREES_T ) );
2746 }
2747
2748 newpad->AddPrimitivePoly( PADSTACK::ALL_LAYERS, poly_outline, 0, true );
2749 }
2750
2751 SHAPE_POLY_SET mergedPolygon;
2752 newpad->MergePrimitivesAsPolygon( PADSTACK::ALL_LAYERS, &mergedPolygon );
2753
2754 if( mergedPolygon.OutlineCount() > 1 )
2755 {
2756 wxLogError( _( "Invalid custom pad '%s'. Replacing with "
2757 "circular pad." ),
2758 custom_name.c_str() );
2759 newpad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
2760 }
2761 }
2762 else
2763 {
2764 wxLogError( _( "Could not find custom pad '%s'." ),
2765 custom_name.c_str() );
2766 }
2767 }
2768 else
2769 {
2770 newpad->SetSize( PADSTACK::ALL_LAYERS,
2771 VECTOR2I( pad.width, pad.height ) );
2772 }
2773
2774 if( pad.drill )
2775 {
2776 if( pad.plated )
2777 {
2778 newpad->SetAttribute( PAD_ATTRIB::PTH );
2779 newpad->SetLayerSet( PAD::PTHMask() );
2780 }
2781 else
2782 {
2783 newpad->SetAttribute( PAD_ATTRIB::NPTH );
2784 newpad->SetLayerSet( PAD::UnplatedHoleMask() );
2785 }
2786
2787 if( pad.drill_size_x == pad.drill_size_y )
2788 newpad->SetDrillShape( PAD_DRILL_SHAPE::CIRCLE );
2789 else
2790 newpad->SetDrillShape( PAD_DRILL_SHAPE::OBLONG );
2791
2792 newpad->SetDrillSize( VECTOR2I( pad.drill_size_x, pad.drill_size_y ) );
2793 }
2794 else
2795 {
2796 newpad->SetAttribute( PAD_ATTRIB::SMD );
2797
2798 if( pad.top )
2799 newpad->SetLayerSet( PAD::SMDMask() );
2800 else if( pad.bottom )
2801 newpad->SetLayerSet( PAD::SMDMask().FlipStandardLayers() );
2802 }
2803 }
2804
2805 if( src->mirror )
2806 newpad->SetOrientation( EDA_ANGLE( -src->rotate + pin->rotation,
2807 DEGREES_T ) );
2808 else
2809 newpad->SetOrientation( EDA_ANGLE( src->rotate - pin->rotation,
2810 DEGREES_T ) );
2811
2812 if( newpad->GetSizeX() > 0 || newpad->GetSizeY() > 0 )
2813 {
2814 fp->Add( newpad.release(), ADD_MODE::APPEND );
2815 }
2816 else
2817 {
2818 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
2819 aBoard->GetFileName().wc_str() );
2820 }
2821 }
2822 }
2823
2824 if( src->mirror )
2825 {
2826 fp->SetOrientationDegrees( 180.0 - src->rotate );
2827 fp->Flip( fp->GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
2828 }
2829
2830 aBoard->Add( fp, ADD_MODE::APPEND );
2831 }
2832 }
2833
2834 return true;
2835}
2836
2837
2839{
2840 LSET layer_set;
2841
2843 layer_set |= LSET::AllTechMask() | LSET::UserMask();
2844
2845 for( auto& layer : layers )
2846 {
2847 checkpoint();
2848
2849 if( layer.second.layerid >= PCBNEW_LAYER_ID_START )
2850 layer_set.set( layer.second.layerid );
2851 }
2852
2853 aBoard->SetEnabledLayers( layer_set );
2854
2855 for( auto& layer : layers )
2856 {
2857 if( layer.second.conductive )
2858 {
2859 aBoard->SetLayerName( static_cast<PCB_LAYER_ID>( layer.second.layerid ),
2860 layer.second.name );
2861 }
2862 }
2863
2864 return true;
2865}
2866
2867
2869{
2870 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2871 const auto& ds = aBoard->GetDesignSettings();
2872
2873 for( auto& via : vias )
2874 {
2875 checkpoint();
2876
2877 auto net_it = netinfo.find( via->net );
2878 auto padstack = pads.find( via->padstack );
2879
2880 PCB_VIA* new_via = new PCB_VIA( aBoard );
2881
2882 new_via->SetPosition( VECTOR2I( via->x, via->y ) );
2883
2884 if( net_it != netinfo.end() )
2885 new_via->SetNet( net_it->second );
2886
2887 if( padstack == pads.end() )
2888 {
2889 new_via->SetDrillDefault();
2890
2891 if( !ds.m_ViasDimensionsList.empty() )
2892 {
2893 new_via->SetWidth( PADSTACK::ALL_LAYERS, ds.m_ViasDimensionsList[0].m_Diameter );
2894 new_via->SetDrill( ds.m_ViasDimensionsList[0].m_Drill );
2895 }
2896 else
2897 {
2898 new_via->SetDrillDefault();
2899 new_via->SetWidth( PADSTACK::ALL_LAYERS, ds.m_ViasMinSize );
2900 }
2901 }
2902 else
2903 {
2904 new_via->SetDrill( padstack->second.drill_size_x );
2905 new_via->SetWidth( PADSTACK::ALL_LAYERS, padstack->second.width );
2906 }
2907
2908 aBoard->Add( new_via, ADD_MODE::APPEND );
2909 }
2910
2911 return true;
2912}
2913
2914
2916{
2917 for( auto& net : netnames )
2918 {
2919 checkpoint();
2920
2921 NETINFO_ITEM *newnet = new NETINFO_ITEM( aBoard, net );
2922 aBoard->Add( newnet, ADD_MODE::APPEND );
2923 }
2924
2925 return true;
2926}
2927
2928
2929bool FABMASTER::loadEtch( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
2930{
2931 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2932 auto net_it = netinfo.find( aLine->netname );
2933
2934 int last_subseq = 0;
2935 ZONE* new_zone = nullptr;
2936
2937 for( const auto& seg : aLine->segment )
2938 {
2939 PCB_LAYER_ID layer = getLayer( seg->layer );
2940
2941 if( IsCopperLayer( layer ) )
2942 {
2943 switch( seg->shape )
2944 {
2945 case GR_SHAPE_LINE:
2946 {
2947 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2948
2949 PCB_TRACK* trk = new PCB_TRACK( aBoard );
2950
2951 trk->SetLayer( layer );
2952 trk->SetStart( VECTOR2I( src->start_x, src->start_y ) );
2953 trk->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2954 trk->SetWidth( src->width );
2955
2956 if( net_it != netinfo.end() )
2957 trk->SetNet( net_it->second );
2958
2959 aBoard->Add( trk, ADD_MODE::APPEND );
2960 break;
2961 }
2962 case GR_SHAPE_ARC:
2963 {
2964 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2965
2966 PCB_ARC* trk = new PCB_ARC( aBoard, &src->result );
2967 trk->SetLayer( layer );
2968 trk->SetWidth( src->width );
2969
2970 if( net_it != netinfo.end() )
2971 trk->SetNet( net_it->second );
2972
2973 aBoard->Add( trk, ADD_MODE::APPEND );
2974 break;
2975 }
2976 default:
2977 {
2978 // Defer to the generic graphics factory
2979 for( std::unique_ptr<BOARD_ITEM>& new_item :
2980 createBoardItems( *aBoard, layer, *seg ) )
2981 {
2982 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
2983 }
2984 break;
2985 }
2986 }
2987 }
2988 else
2989 {
2990 wxLogError( _( "Expecting etch data to be on copper layer. Row found on layer '%s'" ),
2991 seg->layer.c_str() );
2992 }
2993 }
2994
2995 return true;
2996}
2997
2998
3000{
3001 SHAPE_POLY_SET poly_outline;
3002 int last_subseq = 0;
3003 int hole_idx = -1;
3004
3005 poly_outline.NewOutline();
3006
3007 for( const auto& seg : aElement )
3008 {
3009 if( seg->subseq > 0 || seg->subseq != last_subseq )
3010 hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
3011
3012 if( seg->shape == GR_SHAPE_LINE )
3013 {
3014 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
3015
3016 if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
3017 poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
3018
3019 poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
3020 }
3021 else if( seg->shape == GR_SHAPE_ARC || seg->shape == GR_SHAPE_CIRCLE )
3022 {
3023 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
3024 SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
3025
3026 chain.Append( src->result );
3027 }
3028 }
3029
3030 return poly_outline;
3031}
3032
3033
3034/*
3035 * The format doesn't seem to distinguish between open and closed polygons.
3036 * So the best we can really do is to try to detect an open polyline by looking
3037 * for a closed subsequence 0.
3038 *
3039 * For example three lines like this will be open:
3040 *
3041 * +----
3042 * |
3043 * +----
3044 *
3045 * But four lines will be closed:
3046 *
3047 * +----+
3048 * | |
3049 * +----+
3050 *
3051 * This means that "closed" zones (which can have fill patterns in Allegro)
3052 * and "a bunch of lines, which happen to be closed) are not distinguishable,
3053 * but that just seems to be information thrown away on export to FABMASTER.
3054 */
3056{
3057 if( aLine.segment.size() == 0 )
3058 return true;
3059
3060 // First and last item in the first subsequence
3061 const GRAPHIC_ITEM* first = nullptr;
3062 const GRAPHIC_ITEM* last = nullptr;
3063 int first_subseq = -1;
3064 bool have_multiple_subseqs = false;
3065
3066 for( const std::unique_ptr<GRAPHIC_ITEM>& gr_item : aLine.segment )
3067 {
3068 if( first == nullptr )
3069 {
3070 first = gr_item.get();
3071 first_subseq = gr_item->subseq;
3072 }
3073 else if( gr_item->subseq == first_subseq )
3074 {
3075 last = gr_item.get();
3076 }
3077 else
3078 {
3079 have_multiple_subseqs = true;
3080 break;
3081 }
3082 }
3083
3084 // Should have at least one item
3085 wxCHECK( first, true );
3086
3087 // First subsequence was only one item
3088 if( !last )
3089 {
3090 // It can still be a closed polygon if the outer border is a circle
3091 // and there are inner shapes.
3092 if( first->shape == GR_SHAPE_CIRCLE && have_multiple_subseqs )
3093 return false;
3094
3095 return true;
3096 }
3097
3098 const VECTOR2I start{ first->start_x, first->start_y };
3099
3100 // It's not always possible to find an end
3102
3103 switch( last->shape )
3104 {
3105 case GR_SHAPE_LINE:
3106 {
3107 const GRAPHIC_LINE& line = static_cast<const GRAPHIC_LINE&>( *last );
3108 end = VECTOR2I{ line.end_x, line.end_y };
3109 break;
3110 }
3111 case GR_SHAPE_ARC:
3112 {
3113 const GRAPHIC_ARC& arc = static_cast<const GRAPHIC_ARC&>( *last );
3114 end = VECTOR2I{ arc.end_x, arc.end_y };
3115 break;
3116 }
3117 default:
3118 // These shapes don't have "ends" that make sense for a polyline
3119 break;
3120 }
3121
3122 // This looks like a closed polygon
3123 if( end.has_value() && start == end )
3124 return false;
3125
3126 // Open polyline
3127 return true;
3128}
3129
3130
3131std::vector<std::unique_ptr<BOARD_ITEM>>
3133{
3134 std::vector<std::unique_ptr<BOARD_ITEM>> new_items;
3135
3136 const BOARD_DESIGN_SETTINGS& boardSettings = aBoard.GetDesignSettings();
3137 const STROKE_PARAMS defaultStroke( boardSettings.GetLineThickness( aLayer ) );
3138
3139 const auto setShapeParameters = [&]( PCB_SHAPE& aShape )
3140 {
3141 aShape.SetStroke( STROKE_PARAMS( aGraphic.width, LINE_STYLE::SOLID ) );
3142
3143 if( aShape.GetWidth() == 0 )
3144 aShape.SetStroke( defaultStroke );
3145 };
3146
3147 switch( aGraphic.shape )
3148 {
3149 case GR_SHAPE_TEXT:
3150 {
3151 const GRAPHIC_TEXT& src = static_cast<const GRAPHIC_TEXT&>( aGraphic );
3152
3153 auto new_text = std::make_unique<PCB_TEXT>( &aBoard );
3154
3155 if( IsBackLayer( aLayer ) )
3156 {
3157 new_text->SetMirrored( true );
3158 }
3159
3160 setupText( src, aLayer, *new_text, aBoard, std::nullopt );
3161
3162 new_items.emplace_back( std::move( new_text ) );
3163 break;
3164 }
3165 case GR_SHAPE_CROSS:
3166 {
3167 const GRAPHIC_CROSS& src = static_cast<const GRAPHIC_CROSS&>( aGraphic );
3168
3169 const VECTOR2I c{ src.start_x, src.start_y };
3170 const VECTOR2I s{ src.size_x, src.size_y };
3171
3172 const std::vector<SEG> segs = KIGEOM::MakeCrossSegments( c, s, ANGLE_0 );
3173
3174 for( const SEG& seg : segs )
3175 {
3176 auto line = std::make_unique<PCB_SHAPE>( &aBoard );
3177 line->SetShape( SHAPE_T::SEGMENT );
3178 line->SetStart( seg.A );
3179 line->SetEnd( seg.B );
3180
3181 setShapeParameters( *line );
3182 new_items.emplace_back( std::move( line ) );
3183 }
3184 break;
3185 }
3186 default:
3187 {
3188 // Simple single shape
3189 auto new_shape = std::make_unique<PCB_SHAPE>( &aBoard );
3190
3191 setShapeParameters( *new_shape );
3192
3193 switch( aGraphic.shape )
3194 {
3195 case GR_SHAPE_LINE:
3196 {
3197 const GRAPHIC_LINE& src = static_cast<const GRAPHIC_LINE&>( aGraphic );
3198
3199 new_shape->SetShape( SHAPE_T::SEGMENT );
3200 new_shape->SetStart( VECTOR2I( src.start_x, src.start_y ) );
3201 new_shape->SetEnd( VECTOR2I( src.end_x, src.end_y ) );
3202
3203 break;
3204 }
3205 case GR_SHAPE_ARC:
3206 {
3207 const GRAPHIC_ARC& src = static_cast<const GRAPHIC_ARC&>( aGraphic );
3208
3209 new_shape->SetShape( SHAPE_T::ARC );
3210 new_shape->SetArcGeometry( src.result.GetP0(), src.result.GetArcMid(),
3211 src.result.GetP1() );
3212 break;
3213 }
3214 case GR_SHAPE_CIRCLE:
3215 {
3216 const GRAPHIC_ARC& src = static_cast<const GRAPHIC_ARC&>( aGraphic );
3217
3218 new_shape->SetShape( SHAPE_T::CIRCLE );
3219 new_shape->SetCenter( VECTOR2I( src.center_x, src.center_y ) );
3220 new_shape->SetRadius( src.radius );
3221 break;
3222 }
3223 case GR_SHAPE_RECTANGLE:
3224 {
3225 const GRAPHIC_RECTANGLE& src = static_cast<const GRAPHIC_RECTANGLE&>( aGraphic );
3226
3227 new_shape->SetShape( SHAPE_T::RECTANGLE );
3228 new_shape->SetStart( VECTOR2I( src.start_x, src.start_y ) );
3229 new_shape->SetEnd( VECTOR2I( src.end_x, src.end_y ) );
3230
3231 new_shape->SetFilled( src.fill );
3232 break;
3233 }
3234 case GR_SHAPE_POLYGON:
3235 {
3236 const GRAPHIC_POLYGON& src = static_cast<const GRAPHIC_POLYGON&>( aGraphic );
3237 new_shape->SetShape( SHAPE_T::POLY );
3238 new_shape->SetPolyPoints( src.m_pts );
3239 break;
3240 }
3241 case GR_SHAPE_OBLONG:
3242 {
3243 // Create as a polygon, but we could also make a group of two lines and two arcs
3244 const GRAPHIC_OBLONG& src = static_cast<const GRAPHIC_OBLONG&>( aGraphic );
3245
3246 const VECTOR2I c{ src.start_x, src.start_y };
3247 VECTOR2I s = c;
3248 int w = 0;
3249
3250 if( src.oblong_x )
3251 {
3252 w = src.size_y;
3253 s -= VECTOR2I{ ( src.size_x - w ) / 2, 0 };
3254 }
3255 else
3256 {
3257 w = src.size_x;
3258 s -= VECTOR2I{ 0, ( src.size_y - w ) / 2 };
3259 }
3260
3261 SHAPE_SEGMENT seg( s, c - ( s - c ), w );
3262
3263 SHAPE_POLY_SET poly;
3264 seg.TransformToPolygon( poly, boardSettings.m_MaxError, ERROR_LOC::ERROR_INSIDE );
3265
3266 new_shape->SetShape( SHAPE_T::POLY );
3267 new_shape->SetPolyShape( poly );
3268 break;
3269 }
3270 default:
3271 {
3272 wxLogError( _( "Unhandled shape type %d in polygon on layer %s, seq %d %d" ),
3273 aGraphic.shape, aGraphic.layer, aGraphic.seq, aGraphic.subseq );
3274 }
3275 }
3276
3277 new_items.emplace_back( std::move( new_shape ) );
3278 }
3279 }
3280
3281 for( std::unique_ptr<BOARD_ITEM>& new_item : new_items )
3282 {
3283 new_item->SetLayer( aLayer );
3284 }
3285
3286 // If there's more than one, group them
3287 if( new_items.size() > 1 )
3288 {
3289 auto new_group = std::make_unique<PCB_GROUP>( &aBoard );
3290 for( std::unique_ptr<BOARD_ITEM>& new_item : new_items )
3291 {
3292 new_group->AddItem( new_item.get() );
3293 }
3294 new_items.emplace_back( std::move( new_group ) );
3295 }
3296
3297 return new_items;
3298}
3299
3300
3301bool FABMASTER::loadPolygon( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
3302{
3303 if( aLine->segment.empty() )
3304 return false;
3305
3306 PCB_LAYER_ID layer = Cmts_User;
3307
3308 const PCB_LAYER_ID new_layer = getLayer( aLine->layer );
3309
3310 if( IsPcbLayer( new_layer ) )
3311 layer = new_layer;
3312
3313 const bool is_open = traceIsOpen( *aLine );
3314
3315 if( is_open )
3316 {
3317 for( const auto& seg : aLine->segment )
3318 {
3319 for( std::unique_ptr<BOARD_ITEM>& new_item : createBoardItems( *aBoard, layer, *seg ) )
3320 {
3321 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
3322 }
3323 }
3324 }
3325 else
3326 {
3327 STROKE_PARAMS defaultStroke( aBoard->GetDesignSettings().GetLineThickness( layer ) );
3328
3329 SHAPE_POLY_SET poly_outline = loadShapePolySet( aLine->segment );
3330
3331 poly_outline.Fracture();
3332
3333 if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
3334 return false;
3335
3336 PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
3337
3338 new_poly->SetShape( SHAPE_T::POLY );
3339 new_poly->SetLayer( layer );
3340
3341 // Polygons on the silk layer are filled but other layers are not/fill doesn't make sense
3342 if( layer == F_SilkS || layer == B_SilkS )
3343 {
3344 new_poly->SetFilled( true );
3345 new_poly->SetStroke( STROKE_PARAMS( 0 ) );
3346 }
3347 else
3348 {
3349 new_poly->SetStroke(
3350 STROKE_PARAMS( ( *( aLine->segment.begin() ) )->width, LINE_STYLE::SOLID ) );
3351
3352 if( new_poly->GetWidth() == 0 )
3353 new_poly->SetStroke( defaultStroke );
3354 }
3355
3356 new_poly->SetPolyShape( poly_outline );
3357 aBoard->Add( new_poly, ADD_MODE::APPEND );
3358 }
3359
3360 return true;
3361}
3362
3363
3364bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
3365{
3366 if( aLine->segment.size() < 3 )
3367 return false;
3368
3369 SHAPE_POLY_SET* zone_outline = nullptr;
3370 ZONE* zone = nullptr;
3371
3372 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
3373 auto net_it = netinfo.find( aLine->netname );
3374 PCB_LAYER_ID layer = Cmts_User;
3375 auto new_layer = getLayer( aLine->layer );
3376
3377 if( IsPcbLayer( new_layer ) )
3378 layer = new_layer;
3379
3380 zone = new ZONE( aBoard );
3381 zone_outline = new SHAPE_POLY_SET;
3382
3383 if( net_it != netinfo.end() )
3384 zone->SetNet( net_it->second );
3385
3386 if( aLine->layer == "ALL" )
3387 zone->SetLayerSet( aBoard->GetLayerSet() & LSET::AllCuMask() );
3388 else
3389 zone->SetLayer( layer );
3390
3391 zone->SetIsRuleArea( false );
3392 zone->SetDoNotAllowTracks( false );
3393 zone->SetDoNotAllowVias( false );
3394 zone->SetDoNotAllowPads( false );
3395 zone->SetDoNotAllowFootprints( false );
3396 zone->SetDoNotAllowZoneFills( false );
3397
3398 if( aLine->lclass == "ROUTE KEEPOUT")
3399 {
3400 zone->SetIsRuleArea( true );
3401 zone->SetDoNotAllowTracks( true );
3402 }
3403 else if( aLine->lclass == "VIA KEEPOUT")
3404 {
3405 zone->SetIsRuleArea( true );
3406 zone->SetDoNotAllowVias( true );
3407 }
3408 else
3409 {
3410 zone->SetAssignedPriority( 50 );
3411 }
3412
3413 zone->SetLocalClearance( 0 );
3414 zone->SetPadConnection( ZONE_CONNECTION::FULL );
3415
3416 zone_outline->NewOutline();
3417
3418 std::unique_ptr<SHAPE_LINE_CHAIN> pending_hole = nullptr;
3419 SHAPE_LINE_CHAIN* active_chain = &zone_outline->Outline( 0 );
3420
3421 const auto add_hole_if_valid = [&]()
3422 {
3423 if( pending_hole )
3424 {
3425 pending_hole->SetClosed( true );
3426
3427 // If we get junk holes, assert, but don't add them to the zone, as that
3428 // will cause crashes later.
3429 if( !KIGEOM::AddHoleIfValid( *zone_outline, std::move( *pending_hole ) ) )
3430 {
3431 wxLogMessage( _( "Invalid hole with %d points in zone on layer %s with net %s" ),
3432 pending_hole->PointCount(), zone->GetLayerName(),
3433 zone->GetNetname() );
3434 }
3435
3436 pending_hole.reset();
3437 }
3438 };
3439
3440 int last_subseq = 0;
3441 for( const auto& seg : aLine->segment )
3442 {
3443 if( seg->subseq > 0 && seg->subseq != last_subseq )
3444 {
3445 // Don't knock holes in the BOUNDARY systems. These are the outer layers for
3446 // zone fills.
3447 if( aLine->lclass == "BOUNDARY" )
3448 break;
3449
3450 add_hole_if_valid();
3451 pending_hole = std::make_unique<SHAPE_LINE_CHAIN>();
3452 active_chain = pending_hole.get();
3453 last_subseq = seg->subseq;
3454 }
3455
3456 if( seg->shape == GR_SHAPE_LINE )
3457 {
3458 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
3459 const VECTOR2I start( src->start_x, src->start_y );
3460 const VECTOR2I end( src->end_x, src->end_y );
3461
3462 if( active_chain->PointCount() == 0 )
3463 {
3464 active_chain->Append( start );
3465 }
3466 else
3467 {
3468 const VECTOR2I& last = active_chain->CPoint( -1 );
3469
3470 // Not if this can ever happen, or what do if it does (add both points?).
3471 if( last != start )
3472 {
3473 wxLogError( _( "Outline seems discontinuous: last point was %s, "
3474 "start point of next segment is %s" ),
3475 last.Format(), start.Format() );
3476 }
3477 }
3478
3479 active_chain->Append( end );
3480 }
3481 else if( seg->shape == GR_SHAPE_ARC || seg->shape == GR_SHAPE_CIRCLE )
3482 {
3483 /* Even if it says "circle", it's actually an arc, it's just closed */
3484 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
3485 active_chain->Append( src->result );
3486 }
3487 else
3488 {
3489 wxLogError( _( "Invalid shape type %d in zone outline" ), seg->shape );
3490 }
3491 }
3492
3493 // Finalise the last hole, if any
3494 add_hole_if_valid();
3495
3496 if( zone_outline->Outline( 0 ).PointCount() >= 3 )
3497 {
3498 zone->SetOutline( zone_outline );
3499 aBoard->Add( zone, ADD_MODE::APPEND );
3500 }
3501 else
3502 {
3503 delete( zone_outline );
3504 delete( zone );
3505 }
3506
3507 return true;
3508}
3509
3510
3511bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine )
3512{
3513 PCB_LAYER_ID layer;
3514
3515 if( aLine->lclass == "BOARD GEOMETRY" && aLine->layer != "DIMENSION" )
3516 layer = Edge_Cuts;
3517 else if( aLine->lclass == "DRAWING FORMAT" )
3518 layer = Dwgs_User;
3519 else
3520 layer = Cmts_User;
3521
3522 for( auto& seg : aLine->segment )
3523 {
3524 for( std::unique_ptr<BOARD_ITEM>& new_item : createBoardItems( *aBoard, layer, *seg ) )
3525 {
3526 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
3527 }
3528 }
3529
3530 return true;
3531}
3532
3533
3535{
3536
3537 for( auto& geom : board_graphics )
3538 {
3539 checkpoint();
3540
3541 PCB_LAYER_ID layer;
3542
3543 // The pin numbers are not useful for us outside of the footprints
3544 if( geom.subclass == "PIN_NUMBER" )
3545 continue;
3546
3547 layer = getLayer( geom.subclass );
3548
3549 if( !IsPcbLayer( layer ) )
3550 layer = Cmts_User;
3551
3552 if( !geom.elements->empty() )
3553 {
3555 if( ( *( geom.elements->begin() ) )->width == 0 )
3556 {
3557 SHAPE_POLY_SET poly_outline = loadShapePolySet( *( geom.elements ) );
3558
3559 poly_outline.Fracture();
3560
3561 if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
3562 continue;
3563
3564 PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard, SHAPE_T::POLY );
3565 new_poly->SetLayer( layer );
3566 new_poly->SetPolyShape( poly_outline );
3567 new_poly->SetStroke( STROKE_PARAMS( 0 ) );
3568
3569 if( layer == F_SilkS || layer == B_SilkS )
3570 new_poly->SetFilled( true );
3571
3572 aBoard->Add( new_poly, ADD_MODE::APPEND );
3573 }
3574 }
3575
3576 for( auto& seg : *geom.elements )
3577 {
3578 for( std::unique_ptr<BOARD_ITEM>& new_item : createBoardItems( *aBoard, layer, *seg ) )
3579 {
3580 aBoard->Add( new_item.release(), ADD_MODE::APPEND );
3581 }
3582 }
3583 }
3584
3585 return true;
3586
3587}
3588
3589
3591{
3592 std::vector<ZONE*> sortedZones;
3593 std::copy( aBoard->Zones().begin(), aBoard->Zones().end(), std::back_inserter( sortedZones ) );
3594 std::sort( sortedZones.begin(), sortedZones.end(),
3595 [&]( const ZONE* a, const ZONE* b )
3596 {
3597 if( a->GetLayer() == b->GetLayer() )
3598 return a->GetBoundingBox().GetArea() > b->GetBoundingBox().GetArea();
3599
3600 return a->GetLayer() < b->GetLayer();
3601 } );
3602
3604 unsigned int priority = 0;
3605
3606 for( ZONE* zone : sortedZones )
3607 {
3609 if( zone->GetIsRuleArea() )
3610 continue;
3611
3612 if( zone->GetLayer() != layer )
3613 {
3614 layer = zone->GetLayer();
3615 priority = 0;
3616 }
3617
3618 zone->SetAssignedPriority( priority );
3619 priority += 10;
3620 }
3621
3622 return true;
3623}
3624
3625
3626bool FABMASTER::LoadBoard( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
3627{
3628 aBoard->SetFileName( m_filename.GetFullPath() );
3629 m_progressReporter = aProgressReporter;
3630
3631 m_totalCount = netnames.size()
3632 + layers.size()
3633 + vias.size()
3634 + components.size()
3635 + zones.size()
3636 + board_graphics.size()
3637 + traces.size();
3638 m_doneCount = 0;
3639
3640 loadNets( aBoard );
3641 loadLayers( aBoard );
3642 loadVias( aBoard );
3643 loadFootprints( aBoard );
3644 loadZones( aBoard );
3645 loadGraphics( aBoard );
3646
3647 for( auto& track : traces )
3648 {
3649 checkpoint();
3650
3651 if( track->lclass == "ETCH" )
3652 loadEtch( aBoard, track);
3653 else if( track->layer == "OUTLINE" || track->layer == "DIMENSION" )
3654 loadOutline( aBoard, track );
3655 else
3656 loadPolygon( aBoard, track );
3657 }
3658
3659 orderZones( aBoard );
3660
3661 return true;
3662}
const char * name
Definition: DXF_plotter.cpp:62
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:112
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
BASE_SET & set(size_t pos)
Definition: base_set.h:116
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:283
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
Definition: board_item.cpp:180
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:932
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:1138
void SetFileName(const wxString &aFileName)
Definition: board.h:352
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: board.h:637
const ZONES & Zones() const
Definition: board.h:362
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:697
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayer) const
Definition: board.cpp:851
const wxString & GetFileName() const
Definition: board.h:354
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1023
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:1301
void SetEnabledLayers(const LSET &aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:926
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
EDA_ANGLE Normalize()
Definition: eda_angle.h:229
EDA_ANGLE Normalize180()
Definition: eda_angle.h:268
EDA_ANGLE Normalized() const
Definition: eda_angle.h:240
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:345
virtual void SetFilled(bool aFlag)
Definition: eda_shape.h:136
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:177
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:167
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:219
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:578
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:393
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition: eda_text.cpp:417
void SetTextWidth(int aWidth)
Definition: eda_text.cpp:556
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:386
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:284
void SetTextHeight(int aHeight)
Definition: eda_text.cpp:567
void SetKeepUpright(bool aKeepUpright)
Definition: eda_text.cpp:425
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:270
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition: eda_text.cpp:299
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition: eda_text.cpp:307
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:409
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:2465
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:252
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:661
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:242
void SetReference(const wxString &aReference)
Definition: footprint.h:631
void SetValue(const wxString &aValue)
Definition: footprint.h:652
PCB_FIELD & Reference()
Definition: footprint.h:662
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:1076
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection) override
Flip this object, i.e.
Definition: footprint.cpp:2406
VECTOR2I GetPosition() const override
Definition: footprint.h:227
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:52
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static const LSET & UserMask()
Definition: lset.cpp:673
static const LSET & AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
Definition: lset.cpp:659
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition: lset.cpp:591
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:285
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:306
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:292
int GetWidth() const override
Definition: pcb_shape.cpp:385
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:176
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:92
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:148
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:151
virtual void SetWidth(int aWidth)
Definition: pcb_track.h:145
void SetDrillDefault()
Set the drill value for vias to the default value UNDEFINED_DRILL_DIAMETER.
Definition: pcb_track.h:693
void SetDrill(int aDrill)
Set the drill value for vias.
Definition: pcb_track.h:671
void SetPosition(const VECTOR2I &aPoint) override
Definition: pcb_track.h:557
void SetWidth(int aWidth) override
Definition: pcb_track.cpp:373
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:118
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Definition: shape_arc.cpp:984
const VECTOR2I & GetP1() const
Definition: shape_arc.h:117
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:849
const VECTOR2I GetCenter() const
Definition: shape_circle.h:123
void SetCenter(const VECTOR2I &aCenter)
Definition: shape_circle.h:113
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.
int VertexCount(int aOutline=-1, int aHole=-1) const
Return the number of vertices in a given outline/hole.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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:94
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:74
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:723
void SetLocalClearance(std::optional< int > aClearance)
Definition: zone.h:186
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:505
void SetIsRuleArea(bool aEnable)
Definition: zone.h:700
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:722
void SetLayerSet(const LSET &aLayerSet) override
Definition: zone.cpp:511
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:721
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:724
void SetDoNotAllowZoneFills(bool aEnable)
Definition: zone.h:720
void SetAssignedPriority(unsigned aPriority)
Definition: zone.h:121
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:299
void SetOutline(SHAPE_POLY_SET *aOutline)
Definition: zone.h:338
The common library.
static bool empty(const wxTextEntryBase *aCtrl)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:413
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE FULL_CIRCLE
Definition: eda_angle.h:409
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:417
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:415
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:653
constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START
Definition: layer_ids.h:174
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:789
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:664
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:400
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:327
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)
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
@ 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:695