KiCad PCB EDA Suite
Loading...
Searching...
No Matches
import_fabmaster.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 BeagleBoard Foundation
5 * Copyright (C) 2020-2023 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 <pad_shapes.h>
47#include <pcb_shape.h>
48#include <pcb_text.h>
49#include <pcb_track.h>
50#include <zone.h>
51#include <common.h>
52#include <geometry/shape_arc.h>
53#include <string_utils.h>
54#include <progress_reporter.h>
55#include <math/util.h>
56#include <wx/filename.h>
57
58
60{
61 const unsigned PROGRESS_DELTA = 250;
62
64 {
65 if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
66 {
68 / std::max( 1U, m_totalCount ) );
69
71 THROW_IO_ERROR( _( "Open cancelled by user." ) );
72
74 }
75 }
76}
77
78
79double FABMASTER::readDouble( const std::string& aStr ) const
80{
81 std::istringstream istr( aStr );
82 istr.imbue( std::locale::classic() );
83
84 double doubleValue;
85 istr >> doubleValue;
86 return doubleValue;
87}
88
89
90int FABMASTER::readInt( const std::string& aStr ) const
91{
92 std::istringstream istr( aStr );
93 istr.imbue( std::locale::classic() );
94
95 int intValue;
96 istr >> intValue;
97 return intValue;
98}
99
100
101bool FABMASTER::Read( const std::string& aFile )
102{
103
104 std::ifstream ifs( aFile, std::ios::in | std::ios::binary );
105
106 if( !ifs.is_open() )
107 return false;
108
109 m_filename = aFile;
110
111 // Read/ignore all bytes in the file to find the size and then go back to the beginning
112 ifs.ignore( std::numeric_limits<std::streamsize>::max() );
113 std::streamsize length = ifs.gcount();
114 ifs.clear();
115 ifs.seekg( 0, std::ios_base::beg );
116
117 std::string buffer( std::istreambuf_iterator<char>{ ifs }, {} );
118
119 std::vector < std::string > row;
120
121 // Reserve an estimate of the number of rows to prevent continual re-allocation
122 // crashing (Looking at you MSVC)
123 row.reserve( length / 100 );
124 std::string cell;
125 cell.reserve( 100 );
126
127 bool quoted = false;
128
129 for( auto& ch : buffer )
130 {
131 switch( ch )
132 {
133 case '"':
134
135 if( cell.empty() || cell[0] == '"' )
136 quoted = !quoted;
137
138 cell += ch;
139 break;
140
141 case '!':
142 if( !quoted )
143 {
144 row.push_back( cell );
145 cell.clear();
146 }
147 else
148 cell += ch;
149
150 break;
151
152 case '\n':
153
155 if( !cell.empty() )
156 row.push_back( cell );
157
158 cell.clear();
159 rows.push_back( row );
160 row.clear();
161 quoted = false;
162 break;
163
164 case '\r':
165 break;
166
167 default:
168 cell += std::toupper( ch );
169 }
170 }
171
172 // Handle last line without linebreak
173 if( !cell.empty() || !row.empty() )
174 {
175 row.push_back( cell );
176 cell.clear();
177 rows.push_back( row );
178 row.clear();
179 }
180
181 return true;
182}
183
185{
186 single_row row;
187 try
188 {
189 row = rows.at( aOffset );
190 }
191 catch( std::out_of_range& )
192 {
193 return UNKNOWN_EXTRACT;
194 }
195
196 if( row.size() < 3 )
197 return UNKNOWN_EXTRACT;
198
199 if( row[0].back() != 'A' )
200 return UNKNOWN_EXTRACT;
201
202 std::string row1 = row[1];
203 std::string row2 = row[2];
204 std::string row3{};
205
207 alg::delete_if( row1, []( char c ){ return c == '_'; } );
208 alg::delete_if( row2, []( char c ){ return c == '_'; } );
209
210 if( row.size() > 3 )
211 {
212 row3 = row[3];
213 alg::delete_if( row3, []( char c ){ return c == '_'; } );
214 }
215
216 if( row1 == "REFDES" && row2 == "COMPCLASS" )
217 return EXTRACT_REFDES;
218
219 if( row1 == "NETNAME" && row2 == "REFDES" )
220 return EXTRACT_NETS;
221
222 if( row1 == "CLASS" && row2 == "SUBCLASS" && row3.empty() )
224
225 if( row1 == "GRAPHICDATANAME" && row2 == "GRAPHICDATANUMBER" )
226 return EXTRACT_GRAPHICS;
227
228 if( row1 == "CLASS" && row2 == "SUBCLASS" && row3 == "GRAPHICDATANAME" )
229 return EXTRACT_TRACES;
230
231 if( row1 == "SYMNAME" && row2 == "PINNAME" )
233
234 if( row1 == "SYMNAME" && row2 == "SYMMIRROR" && row3 == "PINNAME" )
235 return EXTRACT_PINS;
236
237 if( row1 == "VIAX" && row2 == "VIAY" )
238 return EXTRACT_VIAS;
239
240 if( row1 == "SUBCLASS" && row2 == "PADSHAPENAME" )
241 return EXTRACT_PAD_SHAPES;
242
243 if( row1 == "PADNAME" )
244 return EXTRACT_PADSTACKS;
245
246 if( row1 == "LAYERSORT" )
247 return EXTRACT_FULL_LAYERS;
248
249 wxLogError( _( "Unknown FABMASTER section %s:%s at row %zu." ),
250 row1.c_str(),
251 row2.c_str(),
252 aOffset );
253 return UNKNOWN_EXTRACT;
254
255}
256
257double FABMASTER::processScaleFactor( size_t aRow )
258{
259 double retval = 0.0;
260
261 if( aRow >= rows.size() )
262 return -1.0;
263
264 if( rows[aRow].size() < 11 )
265 {
266 wxLogError( _( "Invalid row size in J row %zu. Expecting 11 elements but found %zu." ),
267 aRow,
268 rows[aRow].size() );
269 return -1.0;
270 }
271
272 for( int i = 7; i < 10 && retval < 1.0; ++i )
273 {
274 std::string units = rows[aRow][i];
275 std::transform(units.begin(), units.end(),units.begin(), ::toupper);
276
277 if( units == "MILS" )
278 retval = pcbIUScale.IU_PER_MILS;
279 else if( units == "MILLIMETERS" )
280 retval = pcbIUScale.IU_PER_MM;
281 else if( units == "MICRONS" )
282 retval = pcbIUScale.IU_PER_MM * 10.0;
283 else if( units == "INCHES" )
284 retval = pcbIUScale.IU_PER_MILS * 1000.0;
285 }
286
287 if( retval < 1.0 )
288 {
289 wxLogError( _( "Could not find units value, defaulting to mils." ) );
290 retval = pcbIUScale.IU_PER_MILS;
291 }
292
293 return retval;
294}
295
296int FABMASTER::getColFromName( size_t aRow, const std::string& aStr )
297{
298 if( aRow >= rows.size() )
299 return -1;
300
301 std::vector<std::string> header = rows[aRow];
302
303 for( size_t i = 0; i < header.size(); i++ )
304 {
307 alg::delete_if( header[i], []( const char c ) { return c == '_'; } );
308
309 if( header[i] == aStr )
310 return i;
311 }
312
313 THROW_IO_ERROR( wxString::Format( _( "Could not find column label %s." ), aStr.c_str() ) );
314 return -1;
315}
316
317
318PCB_LAYER_ID FABMASTER::getLayer( const std::string& aLayerName )
319{
320 const auto& kicad_layer = layers.find( aLayerName);
321
322 if( kicad_layer == layers.end() )
323 return UNDEFINED_LAYER;
324 else
325 return static_cast<PCB_LAYER_ID>( kicad_layer->second.layerid );
326}
327
328
330{
331 size_t rownum = aRow + 2;
332
333 if( rownum >= rows.size() )
334 return -1;
335
336 const single_row& header = rows[aRow];
337
338 int pad_name_col = getColFromName( aRow, "PADNAME" );
339 int pad_num_col = getColFromName( aRow, "RECNUMBER" );
340 int pad_lay_col = getColFromName( aRow, "LAYER" );
341 int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
342 int pad_via_col = getColFromName( aRow, "VIAFLAG" );
343 int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
344 int pad_width_col = getColFromName( aRow, "PADWIDTH" );
345 int pad_height_col = getColFromName( aRow, "PADHGHT" );
346 int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
347 int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
348 int pad_flash_col = getColFromName( aRow, "PADFLASH" );
349 int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
350
351 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
352 {
353 const single_row& row = rows[rownum];
354
355 if( row.size() != header.size() )
356 {
357 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
358 rownum,
359 header.size(),
360 row.size() );
361 continue;
362 }
363
364 auto& pad_name = row[pad_name_col];
365 auto& pad_num = row[pad_num_col];
366 auto& pad_layer = row[pad_lay_col];
367 auto& pad_is_fixed = row[pad_fix_col];
368 auto& pad_is_via = row[pad_via_col];
369 auto& pad_shape = row[pad_shape_col];
370 auto& pad_width = row[pad_width_col];
371 auto& pad_height = row[pad_height_col];
372 auto& pad_xoff = row[pad_xoff_col];
373 auto& pad_yoff = row[pad_yoff_col];
374 auto& pad_flash = row[pad_flash_col];
375 auto& pad_shapename = row[pad_shape_name_col];
376
377 // This layer setting seems to be unused
378 if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
379 continue;
380
381 // Skip the technical layers
382 if( pad_layer[0] == '~' )
383 break;
384
385 auto result = layers.emplace( pad_layer, FABMASTER_LAYER{} );
386 FABMASTER_LAYER& layer = result.first->second;
387
389 if( layer.id == 0 )
390 {
391 layer.name = pad_layer;
392 layer.id = readInt( pad_num );
393 layer.conductive = true;
394 }
395 }
396
397 return 0;
398}
399
400
407size_t FABMASTER::processPadStacks( size_t aRow )
408{
409 size_t rownum = aRow + 2;
410
411 if( rownum >= rows.size() )
412 return -1;
413
414 const single_row& header = rows[aRow];
415 double scale_factor = processScaleFactor( aRow + 1 );
416
417 if( scale_factor <= 0.0 )
418 return -1;
419
420 int pad_name_col = getColFromName( aRow, "PADNAME" );
421 int pad_num_col = getColFromName( aRow, "RECNUMBER" );
422 int pad_lay_col = getColFromName( aRow, "LAYER" );
423 int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
424 int pad_via_col = getColFromName( aRow, "VIAFLAG" );
425 int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
426 int pad_width_col = getColFromName( aRow, "PADWIDTH" );
427 int pad_height_col = getColFromName( aRow, "PADHGHT" );
428 int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
429 int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
430 int pad_flash_col = getColFromName( aRow, "PADFLASH" );
431 int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
432
433 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
434 {
435 const single_row& row = rows[rownum];
436 FM_PAD* pad;
437
438 if( row.size() != header.size() )
439 {
440 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
441 rownum,
442 header.size(),
443 row.size() );
444 continue;
445 }
446
447 auto& pad_name = row[pad_name_col];
448 auto& pad_num = row[pad_num_col];
449 auto& pad_layer = row[pad_lay_col];
450 auto& pad_is_fixed = row[pad_fix_col];
451 auto& pad_is_via = row[pad_via_col];
452 auto& pad_shape = row[pad_shape_col];
453 auto& pad_width = row[pad_width_col];
454 auto& pad_height = row[pad_height_col];
455 auto& pad_xoff = row[pad_xoff_col];
456 auto& pad_yoff = row[pad_yoff_col];
457 auto& pad_flash = row[pad_flash_col];
458 auto& pad_shapename = row[pad_shape_name_col];
459
460 // This layer setting seems to be unused
461 if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
462 continue;
463
464 int recnum = KiROUND( readDouble( pad_num ) );
465
466 auto new_pad = pads.find( pad_name );
467
468 if( new_pad != pads.end() )
469 pad = &new_pad->second;
470 else
471 {
472 pads[pad_name] = FM_PAD();
473 pad = &pads[pad_name];
474 pad->name = pad_name;
475 }
476
478 if( pad_layer == "~DRILL" )
479 {
480 int drill_hit;
481 int drill_x;
482 int drill_y;
483
484 try
485 {
486 drill_hit = KiROUND( std::fabs( readDouble( pad_shape ) * scale_factor ) );
487 drill_x = KiROUND( std::fabs( readDouble( pad_width ) * scale_factor ) );
488 drill_y = KiROUND( std::fabs( readDouble( pad_height ) * scale_factor ) );
489 }
490 catch( ... )
491 {
492 wxLogError( _( "Expecting drill size value but found %s!%s!%s in row %zu." ),
493 pad_shape.c_str(),
494 pad_width.c_str(),
495 pad_height.c_str(),
496 rownum );
497 continue;
498 }
499
500 if( drill_hit == 0 )
501 {
502 pad->drill = false;
503 continue;
504 }
505
506 pad->drill = true;
507
510 if( drill_x == drill_y )
511 {
512 pad->drill_size_x = drill_hit;
513 pad->drill_size_y = drill_hit;
514 }
515 else
516 {
517 pad->drill_size_x = drill_x;
518 pad->drill_size_y = drill_y;
519 }
520
521 if( !pad_shapename.empty() && pad_shapename[0] == 'P' )
522 pad->plated = true;
523
524 continue;
525 }
526
527 if( pad_shape.empty() )
528 continue;
529
530 double w;
531 double h;
532
533 try
534 {
535 w = readDouble( pad_width ) * scale_factor;
536 h = readDouble( pad_height ) * scale_factor;
537 }
538 catch( ... )
539 {
540 wxLogError( _( "Expecting pad size values but found %s : %s in row %zu." ),
541 pad_width.c_str(),
542 pad_height.c_str(),
543 rownum );
544 continue;
545 }
546
547 if( w <= 0.0 )
548 continue;
549
550 auto layer = layers.find( pad_layer );
551
552 if( layer != layers.end() )
553 {
554 if( layer->second.layerid == F_Cu )
555 pad->top = true;
556 else if( layer->second.layerid == B_Cu )
557 pad->bottom = true;
558 }
559
560 if( w > std::numeric_limits<int>::max() || h > std::numeric_limits<int>::max() )
561 {
562 wxLogError( _( "Invalid pad size in row %zu." ), rownum );
563 continue;
564 }
565
566 if( pad_layer == "~TSM" || pad_layer == "~BSM" )
567 {
568 if( w > 0.0 && h > 0.0 )
569 {
570 pad->mask_width = KiROUND( w );
571 pad->mask_height = KiROUND( h );
572 }
573 continue;
574 }
575
576 if( pad_layer == "~TSP" || pad_layer == "~BSP" )
577 {
578 if( w > 0.0 && h > 0.0 )
579 {
580 pad->paste_width = KiROUND( w );
581 pad->paste_height = KiROUND( h );
582 }
583 continue;
584 }
585
587 if( pad_layer[0] == '~' )
588 continue;
589
590 try
591 {
592 pad->x_offset = KiROUND( readDouble( pad_xoff ) * scale_factor );
593 pad->y_offset = -KiROUND( readDouble( pad_yoff ) * scale_factor );
594 }
595 catch( ... )
596 {
597 wxLogError( _( "Expecting pad offset values but found %s:%s in row %zu." ),
598 pad_xoff.c_str(),
599 pad_yoff.c_str(),
600 rownum );
601 continue;
602 }
603
604 if( w > 0.0 && h > 0.0 && recnum == 1 )
605 {
606 pad->width = KiROUND( w );
607 pad->height = KiROUND( h );
608 pad->via = ( std::toupper( pad_is_via[0] ) != 'V' );
609
610 if( pad_shape == "CIRCLE" )
611 {
612 pad->height = pad->width;
613 pad->shape = PAD_SHAPE::CIRCLE;
614 }
615 else if( pad_shape == "RECTANGLE" )
616 {
617 pad->shape = PAD_SHAPE::RECTANGLE;
618 }
619 else if( pad_shape == "ROUNDED_RECT" )
620 {
621 pad->shape = PAD_SHAPE::ROUNDRECT;
622 }
623 else if( pad_shape == "SQUARE" )
624 {
625 pad->shape = PAD_SHAPE::RECTANGLE;
626 pad->height = pad->width;
627 }
628 else if( pad_shape == "OBLONG" || pad_shape == "OBLONG_X" || pad_shape == "OBLONG_Y" )
629 pad->shape = PAD_SHAPE::OVAL;
630 else if( pad_shape == "OCTAGON" )
631 {
632 pad->shape = PAD_SHAPE::RECTANGLE;
633 pad->is_octogon = true;
634 }
635 else if( pad_shape == "SHAPE" )
636 {
637 pad->shape = PAD_SHAPE::CUSTOM;
638 pad->custom_name = pad_shapename;
639 }
640 else
641 {
642 wxLogError( _( "Unknown pad shape name '%s' on layer '%s' in row %zu." ),
643 pad_shape.c_str(),
644 pad_layer.c_str(),
645 rownum );
646 continue;
647 }
648 }
649 }
650
651 return rownum - aRow;
652}
653
654
656{
657 size_t rownum = aRow + 2;
658
659 if( rownum >= rows.size() )
660 return -1;
661
662 auto& header = rows[aRow];
663 double scale_factor = processScaleFactor( aRow + 1 );
664
665 if( scale_factor <= 0.0 )
666 return -1;
667
668 int layer_class_col = getColFromName( aRow, "CLASS" );
669 int layer_subclass_col = getColFromName( aRow, "SUBCLASS" );
670
671 if( layer_class_col < 0 || layer_subclass_col < 0 )
672 return -1;
673
674 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
675 {
676 const single_row& row = rows[rownum];
677
678 if( row.size() != header.size() )
679 {
680 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
681 rownum,
682 header.size(),
683 row.size() );
684 continue;
685 }
686
687 auto result = layers.emplace( row[layer_subclass_col], FABMASTER_LAYER{} );
688 FABMASTER_LAYER& layer = result.first->second;
689
690 layer.name = row[layer_subclass_col];
691 layer.positive = true;
692 layer.conductive = false;
693
694 if( row[layer_class_col] == "ANTI ETCH" )
695 {
696 layer.positive = false;
697 layer.conductive = true;
698 }
699 else if( row[layer_class_col] == "ETCH" )
700 {
701 layer.conductive = true;
702 }
703 }
704
705 return rownum - aRow;
706}
707
708
710{
711 bool has_l1 = false;
712 int max_layer = 0;
713 std::string max_layer_name;
714
715 std::vector<std::pair<std::string, int>> extra_layers
716 {
717 { "ASSEMBLY_TOP", F_Fab },
718 { "ASSEMBLY_BOTTOM", B_Fab },
719 { "PLACE_BOUND_TOP", F_CrtYd },
720 { "PLACE_BOUND_BOTTOM", B_CrtYd },
721 };
722
723 std::vector<FABMASTER_LAYER*> layer_order;
724
725 for( auto& el : layers )
726 {
727 FABMASTER_LAYER& layer = el.second;
729
730 if( layer.conductive )
731 {
732 layer_order.push_back( &layer );
733 }
734 else if( layer.name.find( "SILK" ) != std::string::npos &&
735 layer.name.find( "AUTOSILK" ) == std::string::npos ) // Skip the autosilk layer
736 {
737 if( layer.name.find( "B" ) != std::string::npos )
738 layer.layerid = B_SilkS;
739 else
740 layer.layerid = F_SilkS;
741 }
742 else if( layer.name.find( "MASK" ) != std::string::npos ||
743 layer.name.find( "MSK" ) != std::string::npos )
744 {
745 if( layer.name.find( "B" ) != std::string::npos )
746 layer.layerid = B_Mask;
747 else
748 layer.layerid = F_Mask;
749 }
750 else if( layer.name.find( "PAST" ) != std::string::npos )
751 {
752 if( layer.name.find( "B" ) != std::string::npos )
753 layer.layerid = B_Paste;
754 else
755 layer.layerid = F_Paste;
756 }
757 else if( layer.name.find( "NCLEGEND" ) != std::string::npos )
758 layer.layerid = Dwgs_User;
759 else
760 layer.disable = true;
761 }
762
763 std::sort( layer_order.begin(), layer_order.end(), FABMASTER_LAYER::BY_ID() );
764 int layernum = 0;
765
766 for( auto layer : layer_order )
767 layer->layerid = layernum++;
768
771 layer_order.back()->layerid = B_Cu;
772
773 for( auto& new_pair : extra_layers )
774 {
775 FABMASTER_LAYER new_layer;
776
777 new_layer.name = new_pair.first;
778 new_layer.layerid = new_pair.second;
779 new_layer.conductive = false;
780
781 auto result = layers.emplace( new_pair.first, new_layer );
782
783 if( !result.second )
784 {
785 result.first->second.layerid = new_pair.second;
786 result.first->second.disable = false;
787 }
788 }
789
790 return true;
791}
792
793
799size_t FABMASTER::processLayers( size_t aRow )
800{
801 size_t rownum = aRow + 2;
802
803 if( rownum >= rows.size() )
804 return -1;
805
806 auto& header = rows[aRow];
807 double scale_factor = processScaleFactor( aRow + 1 );
808
809 if( scale_factor <= 0.0 )
810 return -1;
811
812 int layer_sort_col = getColFromName( aRow, "LAYERSORT" );
813 int layer_subclass_col = getColFromName( aRow, "LAYERSUBCLASS" );
814 int layer_art_col = getColFromName( aRow, "LAYERARTWORK" );
815 int layer_use_col = getColFromName( aRow, "LAYERUSE" );
816 int layer_cond_col = getColFromName( aRow, "LAYERCONDUCTOR" );
817 int layer_er_col = getColFromName( aRow, "LAYERDIELECTRICCONSTANT" );
818 int layer_rho_col = getColFromName( aRow, "LAYERELECTRICALCONDUCTIVITY" );
819 int layer_mat_col = getColFromName( aRow, "LAYERMATERIAL" );
820
821 if( layer_sort_col < 0 || layer_subclass_col < 0 || layer_art_col < 0 || layer_use_col < 0
822 || layer_cond_col < 0 || layer_er_col < 0 || layer_rho_col < 0 || layer_mat_col < 0 )
823 return -1;
824
825 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
826 {
827 const single_row& row = rows[rownum];
828
829 if( row.size() != header.size() )
830 {
831 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
832 rownum,
833 header.size(),
834 row.size() );
835 continue;
836 }
837
838 auto& layer_sort = row[layer_sort_col];
839 auto& layer_subclass = row[layer_subclass_col];
840 auto& layer_art = row[layer_art_col];
841 auto& layer_use = row[layer_use_col];
842 auto& layer_cond = row[layer_cond_col];
843 auto& layer_er = row[layer_er_col];
844 auto& layer_rho = row[layer_rho_col];
845 auto& layer_mat = row[layer_mat_col];
846
847 if( layer_mat == "AIR" )
848 continue;
849
850 FABMASTER_LAYER layer;
851
852 if( layer_subclass.empty() )
853 {
854 if( layer_cond != "NO" )
855 layer.name = "In.Cu" + layer_sort;
856 else
857 layer.name = "Dielectric" + layer_sort;
858 }
859
860 layer.positive = ( layer_art != "NEGATIVE" );
861
862 layers.emplace( layer.name, layer );
863 }
864
865 return rownum - aRow;
866}
867
868
874size_t FABMASTER::processCustomPads( size_t aRow )
875{
876 size_t rownum = aRow + 2;
877
878 if( rownum >= rows.size() )
879 return -1;
880
881 auto& header = rows[aRow];
882 double scale_factor = processScaleFactor( aRow + 1 );
883
884 if( scale_factor <= 0.0 )
885 return -1;
886
887 int pad_subclass_col = getColFromName( aRow, "SUBCLASS" );
888 int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
889 int pad_grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
890 int pad_grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
891 int pad_record_tag_col = getColFromName( aRow, "RECORDTAG" );
892 int pad_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
893 int pad_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
894 int pad_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
895 int pad_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
896 int pad_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
897 int pad_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
898 int pad_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
899 int pad_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
900 int pad_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
901 int pad_stack_name_col = getColFromName( aRow, "PADSTACKNAME" );
902 int pad_refdes_col = getColFromName( aRow, "REFDES" );
903 int pad_pin_num_col = getColFromName( aRow, "PINNUMBER" );
904
905 if( pad_subclass_col < 0 || pad_shape_name_col < 0 || pad_grdata1_col < 0 || pad_grdata2_col < 0
906 || pad_grdata3_col < 0 || pad_grdata4_col < 0 || pad_grdata5_col < 0
907 || pad_grdata6_col < 0 || pad_grdata7_col < 0 || pad_grdata8_col < 0
908 || pad_grdata9_col < 0 || pad_stack_name_col < 0 || pad_refdes_col < 0
909 || pad_pin_num_col < 0 )
910 return -1;
911
912 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
913 {
914 const single_row& row = rows[rownum];
915
916 if( row.size() != header.size() )
917 {
918 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
919 rownum,
920 header.size(),
921 row.size() );
922
923 continue;
924 }
925
926 auto& pad_layer = row[pad_subclass_col];
927 auto pad_shape_name = row[pad_shape_name_col];
928 auto& pad_record_tag = row[pad_record_tag_col];
929
930 GRAPHIC_DATA gr_data;
931 gr_data.graphic_dataname = row[pad_grdata_name_col];
932 gr_data.graphic_datanum = row[pad_grdata_num_col];
933 gr_data.graphic_data1 = row[pad_grdata1_col];
934 gr_data.graphic_data2 = row[pad_grdata2_col];
935 gr_data.graphic_data3 = row[pad_grdata3_col];
936 gr_data.graphic_data4 = row[pad_grdata4_col];
937 gr_data.graphic_data5 = row[pad_grdata5_col];
938 gr_data.graphic_data6 = row[pad_grdata6_col];
939 gr_data.graphic_data7 = row[pad_grdata7_col];
940 gr_data.graphic_data8 = row[pad_grdata8_col];
941 gr_data.graphic_data9 = row[pad_grdata9_col];
942
943 auto& pad_stack_name = row[pad_stack_name_col];
944 auto& pad_refdes = row[pad_refdes_col];
945 auto& pad_pin_num = row[pad_pin_num_col];
946
947 // N.B. We get the FIGSHAPE records as "FIG_SHAPE name". We only want "name"
948 // and we don't process other pad shape records
949 std::string prefix( "FIG_SHAPE " );
950
951 if( pad_shape_name.length() <= prefix.length()
952 || !std::equal( prefix.begin(), prefix.end(), pad_shape_name.begin() ) )
953 {
954 continue;
955 }
956
957 // Custom pads are a series of records with the same record ID but incrementing
958 // Sequence numbers.
959 int id = -1;
960 int seq = -1;
961
962 if( std::sscanf( pad_record_tag.c_str(), "%d %d", &id, &seq ) != 2 )
963 {
964 wxLogError( _( "Invalid format for id string '%s' in custom pad row %zu." ),
965 pad_record_tag.c_str(),
966 rownum );
967 continue;
968 }
969
970 auto name = pad_shape_name.substr( prefix.length() );
971 name += "_" + pad_refdes + "_" + pad_pin_num;
972 auto ret = pad_shapes.emplace( name, FABMASTER_PAD_SHAPE{} );
973
974 auto& custom_pad = ret.first->second;
975
976 // If we were able to insert the pad name, then we need to initialize the
977 // record
978 if( ret.second )
979 {
980 custom_pad.name = name;
981 custom_pad.padstack = pad_stack_name;
982 custom_pad.pinnum = pad_pin_num;
983 custom_pad.refdes = pad_refdes;
984 }
985
986 // At this point we extract the individual graphical elements for processing the complex pad. The
987 // coordinates are in board origin format, so we'll need to fix the offset later when we assign them
988 // to the modules.
989
990 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
991
992 if( gr_item )
993 {
994 gr_item->layer = pad_layer;
995 gr_item->refdes = pad_refdes;
996 gr_item->seq = seq;
997 gr_item->subseq = 0;
998
1000 auto pad_it = custom_pad.elements.emplace( id, graphic_element{} );
1001 auto retval = pad_it.first->second.insert( std::move(gr_item ) );
1002
1003 if( !retval.second )
1004 {
1005 wxLogError( _( "Could not insert graphical item %d into padstack '%s'." ),
1006 seq,
1007 pad_stack_name.c_str() );
1008 }
1009 }
1010 else
1011 {
1012 wxLogError( _( "Unrecognized pad shape primitive '%s' in row %zu." ),
1013 gr_data.graphic_dataname,
1014 rownum );
1015 }
1016 }
1017
1018 return rownum - aRow;
1019}
1020
1021
1023{
1024 GRAPHIC_LINE* new_line = new GRAPHIC_LINE ;
1025
1026 new_line->shape = GR_SHAPE_LINE;
1027 new_line->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1028 new_line->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1029 new_line->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1030 new_line->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1031 new_line->width = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1032
1033 return new_line;
1034}
1035
1037{
1038 GRAPHIC_ARC* new_arc = new GRAPHIC_ARC ;
1039
1040 new_arc->shape = GR_SHAPE_ARC;
1041 new_arc->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1042 new_arc->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1043 new_arc->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1044 new_arc->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1045 new_arc->center_x = KiROUND( readDouble( aData.graphic_data5 ) * aScale );
1046 new_arc->center_y = -KiROUND( readDouble( aData.graphic_data6 ) * aScale );
1047 new_arc->radius = KiROUND( readDouble( aData.graphic_data7 ) * aScale );
1048 new_arc->width = KiROUND( readDouble( aData.graphic_data8 ) * aScale );
1049
1050 new_arc->clockwise = ( aData.graphic_data9 != "COUNTERCLOCKWISE" );
1051
1052 EDA_ANGLE startangle( VECTOR2I( new_arc->start_x, new_arc->start_y )
1053 - VECTOR2I( new_arc->center_x, new_arc->center_y ) );
1054 EDA_ANGLE endangle( VECTOR2I( new_arc->end_x, new_arc->end_y )
1055 - VECTOR2I( new_arc->center_x, new_arc->center_y ) );
1056 EDA_ANGLE angle;
1057
1058 startangle.Normalize();
1059 endangle.Normalize();
1060
1061 VECTOR2I center( new_arc->center_x, new_arc->center_y );
1062 VECTOR2I start( new_arc->start_x, new_arc->start_y );
1063 VECTOR2I mid( new_arc->start_x, new_arc->start_y );
1064 VECTOR2I end( new_arc->end_x, new_arc->end_y );
1065
1066 angle = endangle - startangle;
1067
1068 if( new_arc->clockwise && angle < ANGLE_0 )
1069 angle += ANGLE_360;
1070 if( !new_arc->clockwise && angle > ANGLE_0 )
1071 angle -= ANGLE_360;
1072
1073 if( start == end )
1074 angle = -ANGLE_360;
1075
1076 RotatePoint( mid, center, -angle / 2.0 );
1077
1078 if( start == end )
1079 new_arc->shape = GR_SHAPE_CIRCLE;
1080
1081 new_arc->result = SHAPE_ARC( start, mid, end, 0 );
1082
1083 return new_arc;
1084}
1085
1087{
1088 GRAPHIC_RECTANGLE* new_rect = new GRAPHIC_RECTANGLE;
1089
1090 new_rect->shape = GR_SHAPE_RECTANGLE;
1091 new_rect->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1092 new_rect->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1093 new_rect->end_x = KiROUND( readDouble( aData.graphic_data3 ) * aScale );
1094 new_rect->end_y = -KiROUND( readDouble( aData.graphic_data4 ) * aScale );
1095 new_rect->fill = aData.graphic_data5 == "1";
1096 new_rect->width = 0;
1097
1098 return new_rect;
1099}
1100
1102{
1103 GRAPHIC_TEXT* new_text = new GRAPHIC_TEXT;
1104
1105 new_text->shape = GR_SHAPE_TEXT;
1106 new_text->start_x = KiROUND( readDouble( aData.graphic_data1 ) * aScale );
1107 new_text->start_y = -KiROUND( readDouble( aData.graphic_data2 ) * aScale );
1108 new_text->rotation = KiROUND( readDouble( aData.graphic_data3 ) );
1109 new_text->mirror = ( aData.graphic_data4 == "YES" );
1110
1111 if( aData.graphic_data5 == "RIGHT" )
1112 new_text->orient = GR_TEXT_H_ALIGN_RIGHT;
1113 else if( aData.graphic_data5 == "CENTER" )
1114 new_text->orient = GR_TEXT_H_ALIGN_CENTER;
1115 else
1116 new_text->orient = GR_TEXT_H_ALIGN_LEFT;
1117
1118 std::vector<std::string> toks = split( aData.graphic_data6, " \t" );
1119
1120 if( toks.size() < 8 )
1121 {
1122 // We log the error here but continue in the case of too few tokens
1123 wxLogError( _( "Invalid token count. Expected 8 but found %zu." ), toks.size() );
1124 new_text->height = 0;
1125 new_text->width = 0;
1126 new_text->ital = false;
1127 new_text->thickness = 0;
1128 }
1129 else
1130 {
1131 // 0 = size
1132 // 1 = font
1133 new_text->height = KiROUND( readDouble( toks[2] ) * aScale );
1134 new_text->width = KiROUND( readDouble( toks[3] ) * aScale );
1135 new_text->ital = readDouble( toks[4] ) != 0.0;
1136 // 5 = character spacing
1137 // 6 = line spacing
1138 new_text->thickness = KiROUND( readDouble( toks[7] ) * aScale );
1139 }
1140
1141 new_text->text = aData.graphic_data7;
1142 return new_text;
1143}
1144
1145
1147{
1148 GRAPHIC_ITEM* retval = nullptr;
1149
1150 if( aData.graphic_dataname == "LINE" )
1151 retval = processLine( aData, aScale );
1152 else if( aData.graphic_dataname == "ARC" )
1153 retval = processArc( aData, aScale );
1154 else if( aData.graphic_dataname == "RECTANGLE" )
1155 retval = processRectangle( aData, aScale );
1156 else if( aData.graphic_dataname == "TEXT" )
1157 retval = processText( aData, aScale );
1158
1159 if( retval && !aData.graphic_data10.empty() )
1160 {
1161 if( aData.graphic_data10 == "CONNECT" )
1162 retval->type = GR_TYPE_CONNECT;
1163 else if( aData.graphic_data10 == "NOTCONNECT" )
1164 retval->type = GR_TYPE_NOTCONNECT;
1165 else if( aData.graphic_data10 == "SHAPE" )
1166 retval->type = GR_TYPE_NOTCONNECT;
1167 else if( aData.graphic_data10 == "VOID" )
1168 retval->type = GR_TYPE_NOTCONNECT;
1169 else if( aData.graphic_data10 == "POLYGON" )
1170 retval->type = GR_TYPE_NOTCONNECT;
1171 else
1172 retval->type = GR_TYPE_NONE;
1173 }
1174
1175 return retval;
1176}
1177
1178
1184size_t FABMASTER::processGeometry( size_t aRow )
1185{
1186 size_t rownum = aRow + 2;
1187
1188 if( rownum >= rows.size() )
1189 return -1;
1190
1191 const single_row& header = rows[aRow];
1192 double scale_factor = processScaleFactor( aRow + 1 );
1193
1194 if( scale_factor <= 0.0 )
1195 return -1;
1196
1197 int geo_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1198 int geo_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1199 int geo_tag_col = getColFromName( aRow, "RECORDTAG" );
1200 int geo_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1201 int geo_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1202 int geo_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1203 int geo_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1204 int geo_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1205 int geo_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1206 int geo_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1207 int geo_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1208 int geo_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1209 int geo_subclass_col = getColFromName( aRow, "SUBCLASS" );
1210 int geo_sym_name_col = getColFromName( aRow, "SYMNAME" );
1211 int geo_refdes_col = getColFromName( aRow, "REFDES" );
1212
1213 if( geo_name_col < 0 || geo_num_col < 0 || geo_grdata1_col < 0 || geo_grdata2_col < 0
1214 || geo_grdata3_col < 0 || geo_grdata4_col < 0 || geo_grdata5_col < 0
1215 || geo_grdata6_col < 0 || geo_grdata7_col < 0 || geo_grdata8_col < 0
1216 || geo_grdata9_col < 0 || geo_subclass_col < 0 || geo_sym_name_col < 0
1217 || geo_refdes_col < 0 )
1218 return -1;
1219
1220 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1221 {
1222 const single_row& row = rows[rownum];
1223
1224 if( row.size() != header.size() )
1225 {
1226 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1227 rownum,
1228 header.size(),
1229 row.size() );
1230 continue;
1231 }
1232
1233 auto& geo_tag = row[geo_tag_col];
1234
1235 GRAPHIC_DATA gr_data;
1236 gr_data.graphic_dataname = row[geo_name_col];
1237 gr_data.graphic_datanum = row[geo_num_col];
1238 gr_data.graphic_data1 = row[geo_grdata1_col];
1239 gr_data.graphic_data2 = row[geo_grdata2_col];
1240 gr_data.graphic_data3 = row[geo_grdata3_col];
1241 gr_data.graphic_data4 = row[geo_grdata4_col];
1242 gr_data.graphic_data5 = row[geo_grdata5_col];
1243 gr_data.graphic_data6 = row[geo_grdata6_col];
1244 gr_data.graphic_data7 = row[geo_grdata7_col];
1245 gr_data.graphic_data8 = row[geo_grdata8_col];
1246 gr_data.graphic_data9 = row[geo_grdata9_col];
1247
1248 auto& geo_refdes = row[geo_refdes_col];
1249
1250 // Grouped graphics are a series of records with the same record ID but incrementing
1251 // Sequence numbers.
1252 int id = -1;
1253 int seq = -1;
1254 int subseq = 0;
1255
1256 if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1257 {
1258 wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
1259 geo_tag.c_str(),
1260 rownum );
1261 continue;
1262 }
1263
1264 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1265
1266 if( !gr_item )
1267 {
1268 wxLogDebug( wxT( "Unhandled graphic item '%s' in row %zu." ),
1269 gr_data.graphic_dataname.c_str(),
1270 geo_tag.c_str(),
1271 rownum );
1272 continue;
1273 }
1274
1275 gr_item->layer = row[geo_subclass_col];
1276 gr_item->seq = seq;
1277 gr_item->subseq = subseq;
1278
1279 if( geo_refdes.empty() )
1280 {
1281 if( board_graphics.empty() || board_graphics.back().id != id )
1282 {
1283 GEOM_GRAPHIC new_gr;
1284 new_gr.subclass = row[geo_subclass_col];
1285 new_gr.refdes = row[geo_refdes_col];
1286 new_gr.name = row[geo_sym_name_col];
1287 new_gr.id = id;
1288 new_gr.elements = std::make_unique<graphic_element>();
1289 board_graphics.push_back( std::move( new_gr ) );
1290 }
1291
1292 GEOM_GRAPHIC& graphic = board_graphics.back();
1293 graphic.elements->emplace( std::move( gr_item ) );
1294 }
1295 else
1296 {
1297 auto sym_gr_it = comp_graphics.emplace( geo_refdes,
1298 std::map<int, GEOM_GRAPHIC>{} );
1299 auto map_it = sym_gr_it.first->second.emplace( id, GEOM_GRAPHIC{} );
1300 auto& gr = map_it.first;
1301
1302 if( map_it.second )
1303 {
1304 gr->second.subclass = row[geo_subclass_col];
1305 gr->second.refdes = row[geo_refdes_col];
1306 gr->second.name = row[geo_sym_name_col];
1307 gr->second.id = id;
1308 gr->second.elements = std::make_unique<graphic_element>();
1309 }
1310
1311 auto result = gr->second.elements->emplace( std::move( gr_item ) );
1312 }
1313 }
1314
1315 return rownum - aRow;
1316}
1317
1318
1322size_t FABMASTER::processVias( size_t aRow )
1323{
1324 size_t rownum = aRow + 2;
1325
1326 if( rownum >= rows.size() )
1327 return -1;
1328
1329 const single_row& header = rows[aRow];
1330 double scale_factor = processScaleFactor( aRow + 1 );
1331
1332 if( scale_factor <= 0.0 )
1333 return -1;
1334
1335 int viax_col = getColFromName( aRow, "VIAX" );
1336 int viay_col = getColFromName( aRow, "VIAY" );
1337 int padstack_name_col = getColFromName( aRow, "PADSTACKNAME" );
1338 int net_name_col = getColFromName( aRow, "NETNAME" );
1339 int test_point_col = getColFromName( aRow, "TESTPOINT" );
1340
1341 if( viax_col < 0 || viay_col < 0 || padstack_name_col < 0 || net_name_col < 0
1342 || test_point_col < 0 )
1343 return -1;
1344
1345 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1346 {
1347 const single_row& row = rows[rownum];
1348
1349 if( row.size() != header.size() )
1350 {
1351 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1352 rownum,
1353 header.size(),
1354 row.size() );
1355 continue;
1356 }
1357
1358 vias.emplace_back( std::make_unique<FM_VIA>() );
1359 auto& via = vias.back();
1360
1361 via->x = KiROUND( readDouble( row[viax_col] ) * scale_factor );
1362 via->y = -KiROUND( readDouble( row[viay_col] ) * scale_factor );
1363 via->padstack = row[padstack_name_col];
1364 via->net = row[net_name_col];
1365 via->test_point = ( row[test_point_col] == "YES" );
1366 }
1367
1368 return rownum - aRow;
1369}
1370
1371
1377size_t FABMASTER::processTraces( size_t aRow )
1378{
1379 size_t rownum = aRow + 2;
1380
1381 if( rownum >= rows.size() )
1382 return -1;
1383
1384 const single_row& header = rows[aRow];
1385 double scale_factor = processScaleFactor( aRow + 1 );
1386
1387 if( scale_factor <= 0.0 )
1388 return -1;
1389
1390 int class_col = getColFromName( aRow, "CLASS" );
1391 int layer_col = getColFromName( aRow, "SUBCLASS" );
1392 int grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
1393 int grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
1394 int tag_col = getColFromName( aRow, "RECORDTAG" );
1395 int grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
1396 int grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
1397 int grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
1398 int grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
1399 int grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
1400 int grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
1401 int grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
1402 int grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
1403 int grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
1404 int netname_col = getColFromName( aRow, "NETNAME" );
1405
1406 if( class_col < 0 || layer_col < 0 || grdata_name_col < 0 || grdata_num_col < 0
1407 || tag_col < 0 || grdata1_col < 0 || grdata2_col < 0 || grdata3_col < 0
1408 || grdata4_col < 0 || grdata5_col < 0 || grdata6_col < 0 || grdata7_col < 0
1409 || grdata8_col < 0 || grdata9_col < 0 || netname_col < 0 )
1410 return -1;
1411
1412 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1413 {
1414 const single_row& row = rows[rownum];
1415
1416 if( row.size() != header.size() )
1417 {
1418 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1419 rownum,
1420 header.size(),
1421 row.size() );
1422 continue;
1423 }
1424
1425 GRAPHIC_DATA gr_data;
1426 gr_data.graphic_dataname = row[grdata_name_col];
1427 gr_data.graphic_datanum = row[grdata_num_col];
1428 gr_data.graphic_data1 = row[grdata1_col];
1429 gr_data.graphic_data2 = row[grdata2_col];
1430 gr_data.graphic_data3 = row[grdata3_col];
1431 gr_data.graphic_data4 = row[grdata4_col];
1432 gr_data.graphic_data5 = row[grdata5_col];
1433 gr_data.graphic_data6 = row[grdata6_col];
1434 gr_data.graphic_data7 = row[grdata7_col];
1435 gr_data.graphic_data8 = row[grdata8_col];
1436 gr_data.graphic_data9 = row[grdata9_col];
1437
1438 const std::string& geo_tag = row[tag_col];
1439 // Grouped graphics are a series of records with the same record ID but incrementing
1440 // Sequence numbers.
1441 int id = -1;
1442 int seq = -1;
1443 int subseq = 0;
1444
1445 if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
1446 {
1447 wxLogError( _( "Invalid format for record_tag string '%s' in row %zu." ),
1448 geo_tag.c_str(),
1449 rownum );
1450 continue;
1451 }
1452
1453 auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
1454
1455 if( !gr_item )
1456 {
1457 wxLogDebug( _( "Unhandled graphic item '%s' in row %zu." ),
1458 gr_data.graphic_dataname.c_str(),
1459 rownum );
1460 continue;
1461 }
1462
1463 auto new_trace = std::make_unique<TRACE>();
1464 new_trace->id = id;
1465 new_trace->layer = row[layer_col];
1466 new_trace->netname = row[netname_col];
1467 new_trace->lclass = row[class_col];
1468
1469 gr_item->layer = row[layer_col];
1470 gr_item->seq = seq;
1471 gr_item->subseq = subseq;
1472
1473 // Collect the reference designator positions for the footprints later
1474 if( new_trace->lclass == "REF DES" )
1475 {
1476 auto result = refdes.emplace( std::move( new_trace ) );
1477 auto& ref = *result.first;
1478 ref->segment.emplace( std::move( gr_item ) );
1479 }
1480 else if( gr_item->width == 0 )
1481 {
1482 auto result = zones.emplace( std::move( new_trace ) );
1483 auto& zone = *result.first;
1484 auto gr_result = zone->segment.emplace( std::move( gr_item ) );
1485
1486 if( !gr_result.second )
1487 {
1488 wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
1489 id,
1490 seq,
1491 rownum );
1492 }
1493
1494 }
1495 else
1496 {
1497 auto result = traces.emplace( std::move( new_trace ) );
1498 auto& trace = *result.first;
1499 auto gr_result = trace->segment.emplace( std::move( gr_item ) );
1500
1501 if( !gr_result.second )
1502 {
1503 wxLogError( _( "Duplicate item for ID %d and sequence %d in row %zu." ),
1504 id,
1505 seq,
1506 rownum );
1507 }
1508 }
1509 }
1510
1511 return rownum - aRow;
1512}
1513
1514
1515FABMASTER::SYMTYPE FABMASTER::parseSymType( const std::string& aSymType )
1516{
1517 if( aSymType == "PACKAGE" )
1518 return SYMTYPE_PACKAGE;
1519 else if( aSymType == "DRAFTING")
1520 return SYMTYPE_DRAFTING;
1521 else if( aSymType == "MECHANICAL" )
1522 return SYMTYPE_MECH;
1523 else if( aSymType == "FORMAT" )
1524 return SYMTYPE_FORMAT;
1525
1526 return SYMTYPE_NONE;
1527}
1528
1529
1531{
1532 if( aCmpClass == "IO" )
1533 return COMPCLASS_IO;
1534 else if( aCmpClass == "IC" )
1535 return COMPCLASS_IC;
1536 else if( aCmpClass == "DISCRETE" )
1537 return COMPCLASS_DISCRETE;
1538
1539 return COMPCLASS_NONE;
1540}
1541
1546size_t FABMASTER::processFootprints( size_t aRow )
1547{
1548 size_t rownum = aRow + 2;
1549
1550 if( rownum >= rows.size() )
1551 return -1;
1552
1553 const single_row& header = rows[aRow];
1554 double scale_factor = processScaleFactor( aRow + 1 );
1555
1556 if( scale_factor <= 0.0 )
1557 return -1;
1558
1559 int refdes_col = getColFromName( aRow, "REFDES" );
1560 int compclass_col = getColFromName( aRow, "COMPCLASS" );
1561 int comppartnum_col = getColFromName( aRow, "COMPPARTNUMBER" );
1562 int compheight_col = getColFromName( aRow, "COMPHEIGHT" );
1563 int compdevlabelcol = getColFromName( aRow, "COMPDEVICELABEL" );
1564 int compinscode_col = getColFromName( aRow, "COMPINSERTIONCODE" );
1565 int symtype_col = getColFromName( aRow, "SYMTYPE" );
1566 int symname_col = getColFromName( aRow, "SYMNAME" );
1567 int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1568 int symrotate_col = getColFromName( aRow, "SYMROTATE" );
1569 int symx_col = getColFromName( aRow, "SYMX" );
1570 int symy_col = getColFromName( aRow, "SYMY" );
1571 int compvalue_col = getColFromName( aRow, "COMPVALUE" );
1572 int comptol_col = getColFromName( aRow, "COMPTOL" );
1573 int compvolt_col = getColFromName( aRow, "COMPVOLTAGE" );
1574
1575 if( refdes_col < 0 || compclass_col < 0 || comppartnum_col < 0 || compheight_col < 0
1576 || compdevlabelcol < 0 || compinscode_col < 0 || symtype_col < 0 || symname_col < 0
1577 || symmirror_col < 0 || symrotate_col < 0 || symx_col < 0 || symy_col < 0
1578 || compvalue_col < 0 || comptol_col < 0 || compvolt_col < 0 )
1579 return -1;
1580
1581 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1582 {
1583 const single_row& row = rows[rownum];
1584
1585 if( row.size() != header.size() )
1586 {
1587 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1588 rownum,
1589 header.size(),
1590 row.size() );
1591 continue;
1592 }
1593
1594 auto cmp = std::make_unique<COMPONENT>();
1595
1596 cmp->refdes = row[refdes_col];
1597 cmp->cclass = parseCompClass( row[compclass_col] );
1598 cmp->pn = row[comppartnum_col];
1599 cmp->height = row[compheight_col];
1600 cmp->dev_label = row[compdevlabelcol];
1601 cmp->insert_code = row[compinscode_col];
1602 cmp->type = parseSymType( row[symtype_col] );
1603 cmp->name = row[symname_col];
1604 cmp->mirror = ( row[symmirror_col] == "YES" );
1605 cmp->rotate = readDouble( row[symrotate_col] );
1606 cmp->x = KiROUND( readDouble( row[symx_col] ) * scale_factor );
1607 cmp->y = -KiROUND( readDouble( row[symy_col] ) * scale_factor );
1608 cmp->value = row[compvalue_col];
1609 cmp->tol = row[comptol_col];
1610 cmp->voltage = row[compvolt_col];
1611
1612 auto vec = components.find( cmp->refdes );
1613
1614 if( vec == components.end() )
1615 {
1616 auto retval = components.insert( std::make_pair( cmp->refdes, std::vector<std::unique_ptr<COMPONENT>>{} ) );
1617
1618 vec = retval.first;
1619 }
1620
1621 vec->second.push_back( std::move( cmp ) );
1622 }
1623
1624 return rownum - aRow;
1625}
1626
1627
1631size_t FABMASTER::processPins( size_t aRow )
1632{
1633 size_t rownum = aRow + 2;
1634
1635 if( rownum >= rows.size() )
1636 return -1;
1637
1638 const single_row& header = rows[aRow];
1639 double scale_factor = processScaleFactor( aRow + 1 );
1640
1641 if( scale_factor <= 0.0 )
1642 return -1;
1643
1644 int symname_col = getColFromName( aRow, "SYMNAME" );
1645 int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
1646 int pinname_col = getColFromName( aRow, "PINNAME" );
1647 int pinnum_col = getColFromName( aRow, "PINNUMBER" );
1648 int pinx_col = getColFromName( aRow, "PINX" );
1649 int piny_col = getColFromName( aRow, "PINY" );
1650 int padstack_col = getColFromName( aRow, "PADSTACKNAME" );
1651 int refdes_col = getColFromName( aRow, "REFDES" );
1652 int pinrot_col = getColFromName( aRow, "PINROTATION" );
1653 int testpoint_col = getColFromName( aRow, "TESTPOINT" );
1654
1655 if( symname_col < 0 ||symmirror_col < 0 || pinname_col < 0 || pinnum_col < 0 || pinx_col < 0
1656 || piny_col < 0 || padstack_col < 0 || refdes_col < 0 || pinrot_col < 0
1657 || testpoint_col < 0 )
1658 return -1;
1659
1660 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1661 {
1662 const single_row& row = rows[rownum];
1663
1664 if( row.size() != header.size() )
1665 {
1666 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1667 rownum,
1668 header.size(),
1669 row.size() );
1670 continue;
1671 }
1672
1673 auto pin = std::make_unique<PIN>();
1674
1675 pin->name = row[symname_col];
1676 pin->mirror = ( row[symmirror_col] == "YES" );
1677 pin->pin_name = row[pinname_col];
1678 pin->pin_number = row[pinnum_col];
1679 pin->pin_x = KiROUND( readDouble( row[pinx_col] ) * scale_factor );
1680 pin->pin_y = -KiROUND( readDouble( row[piny_col] ) * scale_factor );
1681 pin->padstack = row[padstack_col];
1682 pin->refdes = row[refdes_col];
1683 pin->rotation = readDouble( row[pinrot_col] );
1684
1685 auto map_it = pins.find( pin->refdes );
1686
1687 if( map_it == pins.end() )
1688 {
1689 auto retval = pins.insert( std::make_pair( pin->refdes, std::set<std::unique_ptr<PIN>, PIN::BY_NUM>{} ) );
1690 map_it = retval.first;
1691 }
1692
1693 map_it->second.insert( std::move( pin ) );
1694 }
1695
1696 return rownum - aRow;
1697}
1698
1699
1703size_t FABMASTER::processNets( size_t aRow )
1704{
1705 size_t rownum = aRow + 2;
1706
1707 if( rownum >= rows.size() )
1708 return -1;
1709
1710 const single_row& header = rows[aRow];
1711 double scale_factor = processScaleFactor( aRow + 1 );
1712
1713 if( scale_factor <= 0.0 )
1714 return -1;
1715
1716 int netname_col = getColFromName( aRow, "NETNAME" );
1717 int refdes_col = getColFromName( aRow, "REFDES" );
1718 int pinnum_col = getColFromName( aRow, "PINNUMBER" );
1719 int pinname_col = getColFromName( aRow, "PINNAME" );
1720 int pingnd_col = getColFromName( aRow, "PINGROUND" );
1721 int pinpwr_col = getColFromName( aRow, "PINPOWER" );
1722
1723 if( netname_col < 0 || refdes_col < 0 || pinnum_col < 0 || pinname_col < 0 || pingnd_col < 0
1724 || pinpwr_col < 0 )
1725 return -1;
1726
1727 for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
1728 {
1729 const single_row& row = rows[rownum];
1730
1731 if( row.size() != header.size() )
1732 {
1733 wxLogError( _( "Invalid row size in row %zu. Expecting %zu elements but found %zu." ),
1734 rownum,
1735 header.size(),
1736 row.size() );
1737 continue;
1738 }
1739
1740 NETNAME new_net;
1741 new_net.name = row[netname_col];
1742 new_net.refdes = row[refdes_col];
1743 new_net.pin_num = row[pinnum_col];
1744 new_net.pin_name = row[pinname_col];
1745 new_net.pin_gnd = ( row[pingnd_col] == "YES" );
1746 new_net.pin_pwr = ( row[pinpwr_col] == "YES" );
1747
1748 pin_nets.emplace( std::make_pair( new_net.refdes, new_net.pin_num ), new_net );
1749 netnames.insert( row[netname_col] );
1750 }
1751
1752 return rownum - aRow;
1753}
1754
1755
1757{
1758
1759 for( size_t i = 0; i < rows.size(); )
1760 {
1761 auto type = detectType( i );
1762
1763 switch( type )
1764 {
1765 case EXTRACT_PADSTACKS:
1766 {
1770 assignLayers();
1771 int retval = processPadStacks( i );
1772
1773 i += std::max( retval, 1 );
1774 break;
1775 }
1776
1778 {
1779 int retval = processLayers( i );
1780
1781 i += std::max( retval, 1 );
1782 break;
1783 }
1784
1786 {
1787 int retval = processSimpleLayers( i );
1788
1789 i += std::max( retval, 1 );
1790 break;
1791 }
1792
1793 case EXTRACT_VIAS:
1794 {
1795 int retval = processVias( i );
1796
1797 i += std::max( retval, 1 );
1798 break;
1799 }
1800
1801 case EXTRACT_TRACES:
1802 {
1803 int retval = processTraces( i );
1804
1805 i += std::max( retval, 1 );
1806 break;
1807 }
1808
1809 case EXTRACT_REFDES:
1810 {
1811 int retval = processFootprints( i );
1812
1813 i += std::max( retval, 1 );
1814 break;
1815 }
1816
1817 case EXTRACT_NETS:
1818 {
1819 int retval = processNets( i );
1820
1821 i += std::max( retval, 1 );
1822 break;
1823 }
1824
1825 case EXTRACT_GRAPHICS:
1826 {
1827 int retval = processGeometry( i );
1828
1829 i += std::max( retval, 1 );
1830 break;
1831 }
1832
1833 case EXTRACT_PINS:
1834 {
1835 int retval = processPins( i );
1836
1837 i += std::max( retval, 1 );
1838 break;
1839 }
1840
1841 case EXTRACT_PAD_SHAPES:
1842 {
1843 int retval = processCustomPads( i );
1844
1845 i += std::max( retval, 1 );
1846 break;
1847 }
1848
1849 default:
1850 ++i;
1851 break;
1852 }
1853
1854 }
1855
1856 return true;
1857}
1858
1859
1861{
1862 for( auto& zone : zones )
1863 {
1864 checkpoint();
1865
1866 if( IsCopperLayer( getLayer( zone->layer ) ) || zone->layer == "ALL" )
1867 {
1868 loadZone( aBoard, zone );
1869 }
1870 else
1871 {
1872 if( zone->layer == "OUTLINE" || zone->layer == "DESIGN_OUTLINE" )
1873 {
1874 loadOutline( aBoard, zone );
1875 }
1876 else
1877 {
1878 loadPolygon( aBoard, zone );
1879 }
1880 }
1881 }
1882
1893 std::set<ZONE*> zones_to_delete;
1894
1895 for( auto zone : aBoard->Zones() )
1896 {
1898 if( zone->GetNetCode() > 0 )
1899 {
1900 zones_to_delete.insert( zone );
1901 }
1902 }
1903
1904 for( auto zone1 : aBoard->Zones() )
1905 {
1907 if( zone1->GetNetCode() > 0 )
1908 continue;
1909
1910 SHAPE_LINE_CHAIN& outline1 = zone1->Outline()->Outline( 0 );
1911 std::vector<size_t> overlaps( aBoard->GetNetInfo().GetNetCount() + 1, 0 );
1912 std::vector<std::vector<ZONE*>> possible_deletions( overlaps.size() );
1913
1914 for( auto zone2 : aBoard->Zones() )
1915 {
1916 if( zone2->GetNetCode() <= 0 )
1917 continue;
1918
1919 SHAPE_LINE_CHAIN& outline2 = zone2->Outline()->Outline( 0 );
1920
1921 if( zone1->GetLayer() != zone2->GetLayer() )
1922 continue;
1923
1924 if( !outline1.BBox().Intersects( outline2.BBox() ) )
1925 continue;
1926
1927 for( auto& pt1 : outline1.CPoints() )
1928 {
1930 if( outline2.PointOnEdge( pt1, 1 ) )
1931 overlaps[ zone2->GetNetCode() ]++;
1932 }
1933
1934 for( auto& pt2 : outline2.CPoints() )
1935 {
1938 if( outline1.PointOnEdge( pt2, 1 ) )
1939 overlaps[ zone2->GetNetCode() ]++;
1940 }
1941 }
1942
1943 size_t max_net = 0;
1944 size_t max_net_id = 0;
1945
1946 for( size_t el = 1; el < overlaps.size(); ++el )
1947 {
1948 if( overlaps[el] > max_net )
1949 {
1950 max_net = overlaps[el];
1951 max_net_id = el;
1952 }
1953 }
1954
1955 if( max_net > 0 )
1956 zone1->SetNetCode( max_net_id );
1957 }
1958
1959 for( auto zone : zones_to_delete )
1960 {
1961 aBoard->Remove( zone );
1962 delete zone;
1963 }
1964
1965 return true;
1966}
1967
1968
1970{
1971 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
1972 const auto& ds = aBoard->GetDesignSettings();
1973
1974 for( auto& mod : components )
1975 {
1976 checkpoint();
1977
1978 bool has_multiple = mod.second.size() > 1;
1979
1980 for( int i = 0; i < mod.second.size(); ++i )
1981 {
1982 auto& src = mod.second[i];
1983
1984 FOOTPRINT* fp = new FOOTPRINT( aBoard );
1985
1986 wxString mod_ref = src->name;
1987 wxString lib_ref = m_filename.GetName();
1988
1989 if( has_multiple )
1990 mod_ref.Append( wxString::Format( wxT( "_%d" ), i ) );
1991
1992 ReplaceIllegalFileNameChars( lib_ref, '_' );
1993 ReplaceIllegalFileNameChars( mod_ref, '_' );
1994
1995 wxString key = !lib_ref.empty() ? lib_ref + wxT( ":" ) + mod_ref : mod_ref;
1996
1997 LIB_ID fpID;
1998 fpID.Parse( key, true );
1999 fp->SetFPID( fpID );
2000
2001 fp->SetPosition( VECTOR2I( src->x, src->y ) );
2002 fp->SetOrientationDegrees( -src->rotate );
2003
2004 // KiCad netlisting requires parts to have non-digit + digit annotation.
2005 // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
2006 wxString reference = src->refdes;
2007
2008 if( !std::isalpha( src->refdes[0] ) )
2009 reference.Prepend( "UNK" );
2010
2011 fp->SetReference( reference );
2012
2013 fp->SetValue( src->value );
2014 fp->Value().SetLayer( F_Fab );
2015 fp->Value().SetVisible( false );
2016
2017 for( auto& ref : refdes )
2018 {
2019 const GRAPHIC_TEXT *lsrc =
2020 static_cast<const GRAPHIC_TEXT*>( ( *( ref->segment.begin() ) ).get() );
2021
2022 if( lsrc->text == src->refdes )
2023 {
2024 PCB_TEXT* txt = nullptr;
2025 PCB_LAYER_ID layer = getLayer( ref->layer );
2026
2027 if( !IsPcbLayer( layer ) )
2028 {
2029 wxLogDebug("The layer %s is not mapped?\n", ref->layer.c_str() );
2030 continue;
2031 }
2032
2033 if( layer == F_SilkS || layer == B_SilkS )
2034 txt = &( fp->Reference() );
2035 else
2036 txt = new PCB_TEXT( fp );
2037
2038 if( src->mirror )
2039 {
2040 txt->SetLayer( FlipLayer( layer ) );
2041 txt->SetTextPos( VECTOR2I( lsrc->start_x, 2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
2042 }
2043 else
2044 {
2045 txt->SetLayer( layer );
2046 txt->SetTextPos( VECTOR2I( lsrc->start_x, lsrc->start_y - lsrc->height / 2 ) );
2047 }
2048
2049 txt->SetText( lsrc->text );
2050 txt->SetItalic( lsrc->ital );
2051 txt->SetTextThickness( lsrc->thickness );
2052 txt->SetTextHeight( lsrc->height );
2053 txt->SetTextWidth( lsrc->width );
2054 txt->SetHorizJustify( lsrc->orient );
2055
2056 if( txt != &fp->Reference() )
2057 fp->Add( txt, ADD_MODE::APPEND );
2058 }
2059 }
2060
2063 fp->SetLayer( F_Cu );
2064
2065 auto gr_it = comp_graphics.find( src->refdes );
2066
2067 if( gr_it == comp_graphics.end() )
2068 {
2069 continue;
2070 //TODO: Error
2071 }
2072
2073 for( auto& gr_ref : gr_it->second )
2074 {
2075 auto& graphic = gr_ref.second;
2076
2077 for( auto& seg : *graphic.elements )
2078 {
2079 PCB_LAYER_ID layer = Dwgs_User;
2080
2081 if( IsPcbLayer( getLayer( seg->layer ) ) )
2082 layer = getLayer( seg->layer );
2083
2084 STROKE_PARAMS defaultStroke( ds.GetLineThickness( layer ) );
2085
2086 switch( seg->shape )
2087 {
2088
2089 case GR_SHAPE_LINE:
2090 {
2091 const GRAPHIC_LINE* lsrc = static_cast<const GRAPHIC_LINE*>( seg.get() );
2092
2093 PCB_SHAPE* line = new PCB_SHAPE( fp, SHAPE_T::SEGMENT );
2094
2095 if( src->mirror )
2096 {
2097 line->SetLayer( FlipLayer( layer ) );
2098 line->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2099 line->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2100 }
2101 else
2102 {
2103 line->SetLayer( layer );
2104 line->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
2105 line->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2106 }
2107
2108 line->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
2109
2110 if( lsrc->width == 0 )
2111 line->SetStroke( defaultStroke );
2112
2113 fp->Add( line, ADD_MODE::APPEND );
2114 break;
2115 }
2116 case GR_SHAPE_CIRCLE:
2117 {
2118 const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
2119
2120 PCB_SHAPE* circle = new PCB_SHAPE( fp, SHAPE_T::CIRCLE );
2121
2122 circle->SetLayer( layer );
2123 circle->SetCenter( VECTOR2I( lsrc->center_x, lsrc->center_y ) );
2124 circle->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2125 circle->SetWidth( lsrc->width );
2126
2127 if( lsrc->width == 0 )
2128 circle->SetWidth( ds.GetLineThickness( circle->GetLayer() ) );
2129
2130 if( src->mirror )
2131 circle->Flip( circle->GetCenter(), false );
2132
2133 fp->Add( circle, ADD_MODE::APPEND );
2134 break;
2135 }
2136 case GR_SHAPE_ARC:
2137 {
2138 const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
2139
2140 PCB_SHAPE* arc = new PCB_SHAPE( fp, SHAPE_T::ARC );
2141
2142 arc->SetLayer( layer );
2143 arc->SetArcGeometry( lsrc->result.GetP0(),
2144 lsrc->result.GetArcMid(),
2145 lsrc->result.GetP1() );
2146 arc->SetStroke( STROKE_PARAMS( lsrc->width, LINE_STYLE::SOLID ) );
2147
2148 if( lsrc->width == 0 )
2149 arc->SetStroke( defaultStroke );
2150
2151 if( src->mirror )
2152 arc->Flip( arc->GetCenter(), false );
2153
2154 fp->Add( arc, ADD_MODE::APPEND );
2155 break;
2156 }
2157 case GR_SHAPE_RECTANGLE:
2158 {
2159 const GRAPHIC_RECTANGLE *lsrc =
2160 static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2161
2162 PCB_SHAPE* rect = new PCB_SHAPE( fp, SHAPE_T::RECTANGLE );
2163
2164 if( src->mirror )
2165 {
2166 rect->SetLayer( FlipLayer( layer ) );
2167 rect->SetStart( VECTOR2I( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
2168 rect->SetEnd( VECTOR2I( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
2169 }
2170 else
2171 {
2172 rect->SetLayer( layer );
2173 rect->SetStart( VECTOR2I( lsrc->start_x, lsrc->start_y ) );
2174 rect->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2175 }
2176
2177 rect->SetStroke( defaultStroke );
2178
2179 fp->Add( rect, ADD_MODE::APPEND );
2180 break;
2181 }
2182 case GR_SHAPE_TEXT:
2183 {
2184 const GRAPHIC_TEXT *lsrc =
2185 static_cast<const GRAPHIC_TEXT*>( seg.get() );
2186
2187 PCB_TEXT* txt = new PCB_TEXT( fp );
2188
2189 if( src->mirror )
2190 {
2191 txt->SetLayer( FlipLayer( layer ) );
2192 txt->SetTextPos( VECTOR2I( lsrc->start_x, 2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
2193 }
2194 else
2195 {
2196 txt->SetLayer( layer );
2197 txt->SetTextPos( VECTOR2I( lsrc->start_x, lsrc->start_y - lsrc->height / 2 ) );
2198 }
2199
2200 txt->SetText( lsrc->text );
2201 txt->SetItalic( lsrc->ital );
2202 txt->SetTextThickness( lsrc->thickness );
2203 txt->SetTextHeight( lsrc->height );
2204 txt->SetTextWidth( lsrc->width );
2205 txt->SetHorizJustify( lsrc->orient );
2206
2207 // FABMASTER doesn't have visibility flags but layers that are not silk should be hidden
2208 // by default to prevent clutter.
2209 if( txt->GetLayer() != F_SilkS && txt->GetLayer() != B_SilkS )
2210 txt->SetVisible( false );
2211
2212 fp->Add( txt, ADD_MODE::APPEND );
2213 break;
2214 }
2215 default:
2216 continue;
2217 }
2218 }
2219 }
2220
2221 auto pin_it = pins.find( src->refdes );
2222
2223 if( pin_it != pins.end() )
2224 {
2225 for( auto& pin : pin_it->second )
2226 {
2227 auto pin_net_it = pin_nets.find( std::make_pair( pin->refdes, pin->pin_number ) );
2228 auto padstack = pads.find( pin->padstack );
2229 std::string netname = "";
2230
2231 if( pin_net_it != pin_nets.end() )
2232 netname = pin_net_it->second.name;
2233
2234 auto net_it = netinfo.find( netname );
2235
2236 std::unique_ptr<PAD> newpad = std::make_unique<PAD>( fp );
2237
2238 if( net_it != netinfo.end() )
2239 newpad->SetNet( net_it->second );
2240 else
2241 newpad->SetNetCode( 0 );
2242
2243 newpad->SetX( pin->pin_x );
2244
2245 if( src->mirror )
2246 newpad->SetY( 2 * src->y - pin->pin_y );
2247 else
2248 newpad->SetY( pin->pin_y );
2249
2250 newpad->SetNumber( pin->pin_number );
2251
2252 if( padstack == pads.end() )
2253 {
2254 wxLogError( _( "Unable to locate padstack %s in file %s\n" ),
2255 pin->padstack.c_str(), aBoard->GetFileName().wc_str() );
2256 continue;
2257 }
2258 else
2259 {
2260 auto& pad = padstack->second;
2261
2262 newpad->SetShape( pad.shape );
2263
2264 if( pad.shape == PAD_SHAPE::CUSTOM )
2265 {
2266 // Choose the smaller dimension to ensure the base pad
2267 // is fully hidden by the custom pad
2268 int pad_size = std::min( pad.width, pad.height );
2269
2270 newpad->SetSize( VECTOR2I( pad_size / 2, pad_size / 2 ) );
2271
2272 std::string custom_name = pad.custom_name + "_" + pin->refdes + "_" + pin->pin_number;
2273 auto custom_it = pad_shapes.find( custom_name );
2274
2275 if( custom_it != pad_shapes.end() )
2276 {
2277
2278 SHAPE_POLY_SET poly_outline;
2279 int last_subseq = 0;
2280 int hole_idx = -1;
2281
2282 poly_outline.NewOutline();
2283
2284 // Custom pad shapes have a group of elements
2285 // that are a list of graphical polygons
2286 for( const auto& el : (*custom_it).second.elements )
2287 {
2288 // For now, we are only processing the custom pad for the top layer
2289 // TODO: Use full padstacks when implementing in KiCad
2290 PCB_LAYER_ID primary_layer = src->mirror ? B_Cu : F_Cu;
2291
2292 if( getLayer( ( *( el.second.begin() ) )->layer ) != primary_layer )
2293 continue;
2294
2295 for( const auto& seg : el.second )
2296 {
2297 if( seg->subseq > 0 || seg->subseq != last_subseq )
2298 {
2299 poly_outline.Polygon(0).back().SetClosed( true );
2300 hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
2301 }
2302
2303 if( seg->shape == GR_SHAPE_LINE )
2304 {
2305 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2306
2307 if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
2308 poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
2309
2310 poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
2311 }
2312 else if( seg->shape == GR_SHAPE_ARC )
2313 {
2314 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2315 SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
2316
2317 chain.Append( src->result );
2318 }
2319 }
2320 }
2321
2322 if( poly_outline.OutlineCount() < 1
2323 || poly_outline.Outline( 0 ).PointCount() < 3 )
2324 {
2325 wxLogError( _( "Invalid custom pad '%s'. Replacing with "
2326 "circular pad." ),
2327 custom_name.c_str() );
2328 newpad->SetShape( PAD_SHAPE::CIRCLE );
2329 }
2330 else
2331 {
2333
2334 poly_outline.Move( -newpad->GetPosition() );
2335
2336 if( src->mirror )
2337 {
2338 poly_outline.Mirror( false, true, VECTOR2I( 0, ( pin->pin_y - src->y ) ) );
2339 poly_outline.Rotate( EDA_ANGLE( src->rotate - pin->rotation, DEGREES_T ) );
2340 }
2341 else
2342 {
2343 poly_outline.Rotate( EDA_ANGLE( -src->rotate + pin->rotation, DEGREES_T ) );
2344 }
2345
2346 newpad->AddPrimitivePoly( poly_outline, 0, true );
2347 }
2348
2349 SHAPE_POLY_SET mergedPolygon;
2350 newpad->MergePrimitivesAsPolygon( &mergedPolygon );
2351
2352 if( mergedPolygon.OutlineCount() > 1 )
2353 {
2354 wxLogError( _( "Invalid custom pad '%s'. Replacing with "
2355 "circular pad." ),
2356 custom_name.c_str() );
2357 newpad->SetShape( PAD_SHAPE::CIRCLE );
2358 }
2359 }
2360 else
2361 {
2362 wxLogError( _( "Could not find custom pad '%s'." ),
2363 custom_name.c_str() );
2364 }
2365 }
2366 else
2367 newpad->SetSize( VECTOR2I( pad.width, pad.height ) );
2368
2369 if( pad.drill )
2370 {
2371 if( pad.plated )
2372 {
2373 newpad->SetAttribute( PAD_ATTRIB::PTH );
2374 newpad->SetLayerSet( PAD::PTHMask() );
2375 }
2376 else
2377 {
2378 newpad->SetAttribute( PAD_ATTRIB::NPTH );
2379 newpad->SetLayerSet( PAD::UnplatedHoleMask() );
2380 }
2381
2382 if( pad.drill_size_x == pad.drill_size_y )
2383 newpad->SetDrillShape( PAD_DRILL_SHAPE_CIRCLE );
2384 else
2385 newpad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG );
2386
2387 newpad->SetDrillSize( VECTOR2I( pad.drill_size_x, pad.drill_size_y ) );
2388 }
2389 else
2390 {
2391 newpad->SetAttribute( PAD_ATTRIB::SMD );
2392
2393 if( pad.top )
2394 newpad->SetLayerSet( PAD::SMDMask() );
2395 else if( pad.bottom )
2396 newpad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
2397 }
2398 }
2399
2400 if( src->mirror )
2401 newpad->SetOrientation( EDA_ANGLE( -src->rotate + pin->rotation, DEGREES_T ) );
2402 else
2403 newpad->SetOrientation( EDA_ANGLE( src->rotate - pin->rotation, DEGREES_T ) );
2404
2405 if( newpad->GetSizeX() > 0 || newpad->GetSizeY() > 0 )
2406 {
2407 fp->Add( newpad.release(), ADD_MODE::APPEND );
2408 }
2409 else
2410 {
2411 wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ),
2412 aBoard->GetFileName().wc_str() );
2413 }
2414 }
2415 }
2416
2417 if( src->mirror )
2418 {
2419 fp->SetOrientationDegrees( 180.0 - src->rotate );
2420 fp->Flip( fp->GetPosition(), true );
2421 }
2422
2423 aBoard->Add( fp, ADD_MODE::APPEND );
2424 }
2425 }
2426
2427 return true;
2428}
2429
2430
2432{
2433 LSET layer_set;
2434
2436 layer_set |= LSET::AllTechMask() | LSET::UserMask();
2437
2438 for( auto& layer : layers )
2439 {
2440 checkpoint();
2441
2442 if( layer.second.layerid >= PCBNEW_LAYER_ID_START )
2443 layer_set.set( layer.second.layerid );
2444 }
2445
2446 aBoard->SetEnabledLayers( layer_set );
2447
2448 for( auto& layer : layers )
2449 {
2450 if( layer.second.conductive )
2451 {
2452 aBoard->SetLayerName( static_cast<PCB_LAYER_ID>( layer.second.layerid ),
2453 layer.second.name );
2454 }
2455 }
2456
2457 return true;
2458}
2459
2460
2462{
2463 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2464 const auto& ds = aBoard->GetDesignSettings();
2465
2466 for( auto& via : vias )
2467 {
2468 checkpoint();
2469
2470 auto net_it = netinfo.find( via->net );
2471 auto padstack = pads.find( via->padstack );
2472
2473 PCB_VIA* new_via = new PCB_VIA( aBoard );
2474
2475 new_via->SetPosition( VECTOR2I( via->x, via->y ) );
2476
2477 if( net_it != netinfo.end() )
2478 new_via->SetNet( net_it->second );
2479
2480 if( padstack == pads.end() )
2481 {
2482 new_via->SetDrillDefault();
2483
2484 if( !ds.m_ViasDimensionsList.empty() )
2485 {
2486 new_via->SetWidth( ds.m_ViasDimensionsList[0].m_Diameter );
2487 new_via->SetDrill( ds.m_ViasDimensionsList[0].m_Drill );
2488 }
2489 else
2490 {
2491 new_via->SetDrillDefault();
2492 new_via->SetWidth( ds.m_ViasMinSize );
2493 }
2494 }
2495 else
2496 {
2497 new_via->SetDrill( padstack->second.drill_size_x );
2498 new_via->SetWidth( padstack->second.width );
2499 }
2500
2501 aBoard->Add( new_via, ADD_MODE::APPEND );
2502 }
2503
2504 return true;
2505}
2506
2507
2509{
2510 for( auto& net : netnames )
2511 {
2512 checkpoint();
2513
2514 NETINFO_ITEM *newnet = new NETINFO_ITEM( aBoard, net );
2515 aBoard->Add( newnet, ADD_MODE::APPEND );
2516 }
2517
2518 return true;
2519}
2520
2521
2522bool FABMASTER::loadEtch( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2523{
2524 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2525 auto net_it = netinfo.find( aLine->netname );
2526
2527 int last_subseq = 0;
2528 ZONE* new_zone = nullptr;
2529
2530 for( const auto& seg : aLine->segment )
2531 {
2532 PCB_LAYER_ID layer = getLayer( seg->layer );
2533
2534 if( IsCopperLayer( layer ) )
2535 {
2536 if( seg->shape == GR_SHAPE_LINE )
2537 {
2538 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2539
2540 PCB_TRACK* trk = new PCB_TRACK( aBoard );
2541
2542 trk->SetLayer( layer );
2543 trk->SetStart( VECTOR2I( src->start_x, src->start_y ) );
2544 trk->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2545 trk->SetWidth( src->width );
2546
2547 if( net_it != netinfo.end() )
2548 trk->SetNet( net_it->second );
2549
2550 aBoard->Add( trk, ADD_MODE::APPEND );
2551 }
2552 else if( seg->shape == GR_SHAPE_ARC )
2553 {
2554 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2555
2556 PCB_ARC* trk = new PCB_ARC( aBoard, &src->result );
2557 trk->SetLayer( layer );
2558 trk->SetWidth( src->width );
2559
2560 if( net_it != netinfo.end() )
2561 trk->SetNet( net_it->second );
2562
2563 aBoard->Add( trk, ADD_MODE::APPEND );
2564 }
2565 else if( seg->shape == GR_SHAPE_CIRCLE )
2566 {
2567 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2568
2569 PCB_SHAPE* circle = new PCB_SHAPE( aBoard, SHAPE_T::CIRCLE );
2570 circle->SetLayer( layer );
2571 circle->SetCenter( VECTOR2I( src->center_x, src->center_y ) );
2572 circle->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2573 circle->SetWidth( src->width );
2574
2575 aBoard->Add( circle, ADD_MODE::APPEND );
2576 }
2577 else if( seg->shape == GR_SHAPE_RECTANGLE )
2578 {
2579 const GRAPHIC_RECTANGLE *src =
2580 static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2581
2582 PCB_SHAPE* rect = new PCB_SHAPE( aBoard, SHAPE_T::RECTANGLE );
2583 rect->SetLayer( layer );
2584 rect->SetStart( VECTOR2I( src->start_x, src->start_y ) );
2585 rect->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2586 rect->SetStroke( STROKE_PARAMS( 0 ) );
2587 rect->SetFilled( true );
2588 aBoard->Add( rect, ADD_MODE::APPEND );
2589 }
2590 else if( seg->shape == GR_SHAPE_TEXT )
2591 {
2592 const GRAPHIC_TEXT *src =
2593 static_cast<const GRAPHIC_TEXT*>( seg.get() );
2594
2595 PCB_TEXT* txt = new PCB_TEXT( aBoard );
2596 txt->SetLayer( layer );
2597 txt->SetTextPos( VECTOR2I( src->start_x, src->start_y - src->height / 2 ) );
2598 txt->SetText( src->text );
2599 txt->SetItalic( src->ital );
2600 txt->SetTextThickness( src->thickness );
2601 txt->SetTextHeight( src->height );
2602 txt->SetTextWidth( src->width );
2603 txt->SetHorizJustify( src->orient );
2604 aBoard->Add( txt, ADD_MODE::APPEND );
2605 }
2606 }
2607 else
2608 {
2609 wxLogError( _( "Expecting etch data to be on copper layer. Row found on layer '%s'" ),
2610 seg->layer.c_str() );
2611 }
2612 }
2613
2614 return true;
2615}
2616
2617
2619{
2620 SHAPE_POLY_SET poly_outline;
2621 int last_subseq = 0;
2622 int hole_idx = -1;
2623
2624 poly_outline.NewOutline();
2625
2626 for( const auto& seg : aElement )
2627 {
2628 if( seg->subseq > 0 || seg->subseq != last_subseq )
2629 hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
2630
2631 if( seg->shape == GR_SHAPE_LINE )
2632 {
2633 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2634
2635 if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
2636 poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
2637
2638 poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
2639 }
2640 else if( seg->shape == GR_SHAPE_ARC )
2641 {
2642 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2643 SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
2644
2645 chain.Append( src->result );
2646 }
2647 }
2648
2649 return poly_outline;
2650}
2651
2652
2653bool FABMASTER::loadPolygon( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2654{
2655 if( aLine->segment.empty() )
2656 return false;
2657
2658 PCB_LAYER_ID layer = Cmts_User;
2659
2660 auto new_layer = getLayer( aLine->layer );
2661
2662 if( IsPcbLayer( new_layer ) )
2663 layer = new_layer;
2664
2665 STROKE_PARAMS defaultStroke( aBoard->GetDesignSettings().GetLineThickness( layer ) );
2666
2667 if( aLine->segment.size() < 3 )
2668 {
2669 for( const auto& seg : aLine->segment )
2670 {
2671 PCB_SHAPE* new_shape = new PCB_SHAPE( aBoard );
2672
2673 new_shape->SetLayer( layer );
2674
2675 new_shape->SetStroke(
2676 STROKE_PARAMS( ( *( aLine->segment.begin() ) )->width, LINE_STYLE::SOLID ) );
2677
2678 if( new_shape->GetWidth() == 0 )
2679 new_shape->SetStroke( defaultStroke );
2680
2681 if( seg->shape == GR_SHAPE_LINE )
2682 {
2683 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2684
2685 new_shape->SetShape( SHAPE_T::SEGMENT );
2686 new_shape->SetStart( VECTOR2I( src->start_x, src->start_y ) );
2687 new_shape->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2688 }
2689 else if( seg->shape == GR_SHAPE_ARC )
2690 {
2691 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2692
2693 new_shape->SetShape( SHAPE_T::ARC );
2694 new_shape->SetArcGeometry( src->result.GetP0(), src->result.GetArcMid(),
2695 src->result.GetP1() );
2696 }
2697
2698 aBoard->Add( new_shape, ADD_MODE::APPEND );
2699 }
2700 }
2701
2702 SHAPE_POLY_SET poly_outline = loadShapePolySet( aLine->segment );
2703
2705
2706 if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
2707 return false;
2708
2709 PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
2710
2711 new_poly->SetShape( SHAPE_T::POLY );
2712 new_poly->SetLayer( layer );
2713
2714 // Polygons on the silk layer are filled but other layers are not/fill doesn't make sense
2715 if( layer == F_SilkS || layer == B_SilkS )
2716 {
2717 new_poly->SetFilled( true );
2718 new_poly->SetStroke( STROKE_PARAMS( 0 ) );
2719 }
2720 else
2721 {
2722 new_poly->SetStroke( STROKE_PARAMS( ( *( aLine->segment.begin() ) )->width,
2723 LINE_STYLE::SOLID ) );
2724
2725 if( new_poly->GetWidth() == 0 )
2726 new_poly->SetStroke( defaultStroke );
2727 }
2728
2729 new_poly->SetPolyShape( poly_outline );
2730 aBoard->Add( new_poly, ADD_MODE::APPEND );
2731
2732 return true;
2733
2734}
2735
2736
2737bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2738{
2739 if( aLine->segment.size() < 3 )
2740 return false;
2741
2742 int last_subseq = 0;
2743 int hole_idx = -1;
2744 SHAPE_POLY_SET* zone_outline = nullptr;
2745 ZONE* zone = nullptr;
2746
2747 const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
2748 auto net_it = netinfo.find( aLine->netname );
2749 PCB_LAYER_ID layer = Cmts_User;
2750 auto new_layer = getLayer( aLine->layer );
2751
2752 if( IsPcbLayer( new_layer ) )
2753 layer = new_layer;
2754
2755 zone = new ZONE( aBoard );
2756 zone_outline = new SHAPE_POLY_SET;
2757
2758 if( net_it != netinfo.end() )
2759 zone->SetNet( net_it->second );
2760
2761 if( aLine->layer == "ALL" )
2762 zone->SetLayerSet( aBoard->GetLayerSet() & LSET::AllCuMask() );
2763 else
2764 zone->SetLayer( layer );
2765
2766 zone->SetIsRuleArea( false );
2767 zone->SetDoNotAllowTracks( false );
2768 zone->SetDoNotAllowVias( false );
2769 zone->SetDoNotAllowPads( false );
2770 zone->SetDoNotAllowFootprints( false );
2771 zone->SetDoNotAllowCopperPour( false );
2772
2773 if( aLine->lclass == "ROUTE KEEPOUT")
2774 {
2775 zone->SetIsRuleArea( true );
2776 zone->SetDoNotAllowTracks( true );
2777 }
2778 else if( aLine->lclass == "VIA KEEPOUT")
2779 {
2780 zone->SetIsRuleArea( true );
2781 zone->SetDoNotAllowVias( true );
2782 }
2783 else
2784 {
2785 zone->SetAssignedPriority( 50 );
2786 }
2787
2788 zone->SetLocalClearance( 0 );
2789 zone->SetPadConnection( ZONE_CONNECTION::FULL );
2790
2791 zone_outline->NewOutline();
2792
2793
2794 for( const auto& seg : aLine->segment )
2795 {
2796 if( seg->subseq > 0 && seg->subseq != last_subseq )
2797 {
2799 if( aLine->lclass == "BOUNDARY" )
2800 break;
2801
2802 hole_idx = zone_outline->AddHole( SHAPE_LINE_CHAIN{} );
2803 last_subseq = seg->subseq;
2804 last_subseq = seg->subseq;
2805 }
2806
2807 if( seg->shape == GR_SHAPE_LINE )
2808 {
2809 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2810
2811 if( zone_outline->VertexCount( 0, hole_idx ) == 0 )
2812 zone_outline->Append( src->start_x, src->start_y, 0, hole_idx );
2813
2814 zone_outline->Append( src->end_x, src->end_y, 0, hole_idx );
2815 }
2816 else if( seg->shape == GR_SHAPE_ARC )
2817 {
2818 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2819 zone_outline->Hole( 0, hole_idx ).Append( src->result );
2820 }
2821 }
2822
2823 if( zone_outline->Outline( 0 ).PointCount() >= 3 )
2824 {
2825 zone->SetOutline( zone_outline );
2826 aBoard->Add( zone, ADD_MODE::APPEND );
2827 }
2828 else
2829 {
2830 delete( zone_outline );
2831 delete( zone );
2832 }
2833
2834 return true;
2835}
2836
2837
2838bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
2839{
2840 PCB_LAYER_ID layer;
2841
2842 if( aLine->lclass == "BOARD GEOMETRY" && aLine->layer != "DIMENSION" )
2843 layer = Edge_Cuts;
2844 else if( aLine->lclass == "DRAWING FORMAT" )
2845 layer = Dwgs_User;
2846 else
2847 layer = Cmts_User;
2848
2849 STROKE_PARAMS defaultStroke( aBoard->GetDesignSettings().GetLineThickness( layer ) );
2850
2851 for( auto& seg : aLine->segment )
2852 {
2853 switch( seg->shape )
2854 {
2855
2856 case GR_SHAPE_LINE:
2857 {
2858 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2859
2860 PCB_SHAPE* line = new PCB_SHAPE( aBoard, SHAPE_T::SEGMENT );
2861 line->SetLayer( layer );
2862 line->SetStart( VECTOR2I( src->start_x, src->start_y ) );
2863 line->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2864 line->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
2865
2866 if( line->GetWidth() == 0 )
2867 line->SetStroke( defaultStroke );
2868
2869 aBoard->Add( line, ADD_MODE::APPEND );
2870 break;
2871 }
2872 case GR_SHAPE_CIRCLE:
2873 {
2874 const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
2875
2876 PCB_SHAPE* circle = new PCB_SHAPE( aBoard, SHAPE_T::CIRCLE );
2877
2878 circle->SetLayer( layer );
2879 circle->SetCenter( VECTOR2I( lsrc->center_x, lsrc->center_y ) );
2880 circle->SetEnd( VECTOR2I( lsrc->end_x, lsrc->end_y ) );
2881 circle->SetWidth( lsrc->width );
2882
2883 if( lsrc->width == 0 )
2884 circle->SetWidth( aBoard->GetDesignSettings().GetLineThickness( circle->GetLayer() ) );
2885
2886 aBoard->Add( circle, ADD_MODE::APPEND );
2887 break;
2888 }
2889 case GR_SHAPE_ARC:
2890 {
2891 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
2892
2893 PCB_SHAPE* arc = new PCB_SHAPE( aBoard, SHAPE_T::ARC );
2894 arc->SetLayer( layer );
2895 arc->SetArcGeometry( src->result.GetP0(),
2896 src->result.GetArcMid(),
2897 src->result.GetP1() );
2898 arc->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
2899
2900 if( arc->GetWidth() == 0 )
2901 arc->SetStroke( defaultStroke );
2902
2903 aBoard->Add( arc, ADD_MODE::APPEND );
2904 break;
2905 }
2906 case GR_SHAPE_RECTANGLE:
2907 {
2908 const GRAPHIC_RECTANGLE *src =
2909 static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
2910
2911 PCB_SHAPE* rect = new PCB_SHAPE( aBoard, SHAPE_T::RECTANGLE );
2912 rect->SetLayer( layer );
2913 rect->SetStart( VECTOR2I( src->start_x, src->start_y ) );
2914 rect->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
2915 rect->SetStroke( defaultStroke );
2916
2917 aBoard->Add( rect, ADD_MODE::APPEND );
2918 break;
2919 }
2920 case GR_SHAPE_TEXT:
2921 {
2922 const GRAPHIC_TEXT *src = static_cast<const GRAPHIC_TEXT*>( seg.get() );
2923
2924 PCB_TEXT* txt = new PCB_TEXT( aBoard );
2925 txt->SetLayer( layer );
2926 txt->SetTextPos( VECTOR2I( src->start_x, src->start_y - src->height / 2 ) );
2927 txt->SetText( src->text );
2928 txt->SetItalic( src->ital );
2929 txt->SetTextThickness( src->thickness );
2930 txt->SetTextHeight( src->height );
2931 txt->SetTextWidth( src->width );
2932 txt->SetHorizJustify( src->orient );
2933
2934 aBoard->Add( txt, ADD_MODE::APPEND );
2935 break;
2936 }
2937 default:
2938 return false;
2939 }
2940 }
2941
2942 return true;
2943}
2944
2945
2947{
2948
2949 for( auto& geom : board_graphics )
2950 {
2951 checkpoint();
2952
2953 PCB_LAYER_ID layer;
2954
2955 // The pin numbers are not useful for us outside of the footprints
2956 if( geom.subclass == "PIN_NUMBER" )
2957 continue;
2958
2959 layer = getLayer( geom.subclass );
2960
2961 if( !IsPcbLayer( layer ) )
2962 layer = Cmts_User;
2963
2964 if( !geom.elements->empty() )
2965 {
2967 if( ( *( geom.elements->begin() ) )->width == 0 )
2968 {
2969 SHAPE_POLY_SET poly_outline = loadShapePolySet( *( geom.elements ) );
2970
2972
2973 if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
2974 continue;
2975
2976 PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard, SHAPE_T::POLY );
2977 new_poly->SetLayer( layer );
2978 new_poly->SetPolyShape( poly_outline );
2979 new_poly->SetStroke( STROKE_PARAMS( 0 ) );
2980
2981 if( layer == F_SilkS || layer == B_SilkS )
2982 new_poly->SetFilled( true );
2983
2984 aBoard->Add( new_poly, ADD_MODE::APPEND );
2985 }
2986 }
2987
2988 for( auto& seg : *geom.elements )
2989 {
2990 switch( seg->shape )
2991 {
2992
2993 case GR_SHAPE_LINE:
2994 {
2995 const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
2996
2997 PCB_SHAPE* line = new PCB_SHAPE( aBoard, SHAPE_T::SEGMENT );
2998 line->SetLayer( layer );
2999 line->SetStart( VECTOR2I( src->start_x, src->start_y ) );
3000 line->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
3001 line->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
3002
3003 aBoard->Add( line, ADD_MODE::APPEND );
3004 break;
3005 }
3006 case GR_SHAPE_CIRCLE:
3007 {
3008 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
3009
3010 PCB_SHAPE* circle = new PCB_SHAPE( aBoard, SHAPE_T::CIRCLE );
3011
3012 circle->SetLayer( layer );
3013 circle->SetCenter( VECTOR2I( src->center_x, src->center_y ) );
3014 circle->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
3015 circle->SetWidth( src->width );
3016
3017 aBoard->Add( circle, ADD_MODE::APPEND );
3018 break;
3019 }
3020 case GR_SHAPE_ARC:
3021 {
3022 const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
3023
3024 PCB_SHAPE* arc = new PCB_SHAPE( aBoard, SHAPE_T::ARC );
3025 arc->SetLayer( layer );
3026 arc->SetArcGeometry( src->result.GetP0(),
3027 src->result.GetArcMid(),
3028 src->result.GetP1() );
3029 arc->SetStroke( STROKE_PARAMS( src->width, LINE_STYLE::SOLID ) );
3030
3031 aBoard->Add( arc, ADD_MODE::APPEND );
3032 break;
3033 }
3034 case GR_SHAPE_RECTANGLE:
3035 {
3036 const GRAPHIC_RECTANGLE *src =
3037 static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
3038
3039 PCB_SHAPE* rect = new PCB_SHAPE( aBoard, SHAPE_T::RECTANGLE );
3040 rect->SetLayer( layer );
3041 rect->SetStart( VECTOR2I( src->start_x, src->start_y ) );
3042 rect->SetEnd( VECTOR2I( src->end_x, src->end_y ) );
3043 rect->SetStroke( STROKE_PARAMS( 0 ) );
3044 rect->SetFilled( true );
3045 aBoard->Add( rect, ADD_MODE::APPEND );
3046 break;
3047 }
3048 case GR_SHAPE_TEXT:
3049 {
3050 const GRAPHIC_TEXT *src =
3051 static_cast<const GRAPHIC_TEXT*>( seg.get() );
3052
3053 PCB_TEXT* txt = new PCB_TEXT( aBoard );
3054 txt->SetLayer( layer );
3055 txt->SetTextPos( VECTOR2I( src->start_x, src->start_y - src->height / 2 ) );
3056 txt->SetText( src->text );
3057 txt->SetItalic( src->ital );
3058 txt->SetTextThickness( src->thickness );
3059 txt->SetTextHeight( src->height );
3060 txt->SetTextWidth( src->width );
3061 txt->SetHorizJustify( src->orient );
3062 aBoard->Add( txt, ADD_MODE::APPEND );
3063 break;
3064 }
3065 default:
3066 return false;
3067 }
3068 }
3069 }
3070
3071 return true;
3072
3073}
3074
3075
3077{
3078 std::vector<ZONE*> sortedZones;
3079 std::copy( aBoard->Zones().begin(), aBoard->Zones().end(), std::back_inserter( sortedZones ) );
3080 std::sort( sortedZones.begin(), sortedZones.end(),
3081 [&]( const ZONE* a, const ZONE* b )
3082 {
3083 if( a->GetLayer() == b->GetLayer() )
3084 return a->GetBoundingBox().GetArea() > b->GetBoundingBox().GetArea();
3085
3086 return a->GetLayer() < b->GetLayer();
3087 } );
3088
3090 unsigned int priority = 0;
3091
3092 for( ZONE* zone : sortedZones )
3093 {
3095 if( zone->GetIsRuleArea() )
3096 continue;
3097
3098 if( zone->GetLayer() != layer )
3099 {
3100 layer = zone->GetLayer();
3101 priority = 0;
3102 }
3103
3104 zone->SetAssignedPriority( priority );
3105 priority += 10;
3106 }
3107
3108 return true;
3109}
3110
3111
3112bool FABMASTER::LoadBoard( BOARD* aBoard, PROGRESS_REPORTER* aProgressReporter )
3113{
3114 aBoard->SetFileName( m_filename.GetFullPath() );
3115 m_progressReporter = aProgressReporter;
3116
3117 m_totalCount = netnames.size()
3118 + layers.size()
3119 + vias.size()
3120 + components.size()
3121 + zones.size()
3122 + board_graphics.size()
3123 + traces.size();
3124 m_doneCount = 0;
3125
3126 loadNets( aBoard );
3127 loadLayers( aBoard );
3128 loadVias( aBoard );
3129 loadFootprints( aBoard );
3130 loadZones( aBoard );
3131 loadGraphics( aBoard );
3132
3133 for( auto& track : traces )
3134 {
3135 checkpoint();
3136
3137 if( track->lclass == "ETCH" )
3138 loadEtch( aBoard, track);
3139 else if( track->layer == "OUTLINE" || track->layer == "DIMENSION" )
3140 loadOutline( aBoard, track );
3141 }
3142
3143 orderZones( aBoard );
3144
3145 return true;
3146}
const char * name
Definition: DXF_plotter.cpp:57
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:226
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:260
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:231
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:281
const NETINFO_LIST & GetNetInfo() const
Definition: board.h:850
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: board.cpp:882
void SetFileName(const wxString &aFileName)
Definition: board.h:316
void SetEnabledLayers(LSET aLayerMask)
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:700
const ZONES & Zones() const
Definition: board.h:326
bool SetLayerName(PCB_LAYER_ID aLayer, const wxString &aLayerName)
Changes the name of the layer given by aLayer.
Definition: board.cpp:583
const wxString & GetFileName() const
Definition: board.h:318
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:797
void Remove(BOARD_ITEM *aBoardItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: board.cpp:1020
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:270
EDA_ANGLE Normalize()
Definition: eda_angle.h:255
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:543
void SetFilled(bool aFlag)
Definition: eda_shape.h:96
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:270
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:129
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:119
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:154
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:623
void SetWidth(int aWidth)
Definition: eda_shape.h:109
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:419
void SetTextWidth(int aWidth)
Definition: eda_text.cpp:397
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:245
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:197
void SetTextHeight(int aHeight)
Definition: eda_text.cpp:408
virtual void SetText(const wxString &aText)
Definition: eda_text.cpp:183
void SetItalic(bool aItalic)
Definition: eda_text.cpp:213
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:268
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
size_t processGeometry(size_t aRow)
A!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!GRAPHIC_DATA_3!...
bool Read(const std::string &aFile)
bool loadNets(BOARD *aBoard)
std::map< std::string, std::map< int, GEOM_GRAPHIC > > comp_graphics
unsigned m_lastProgressCount
SYMTYPE parseSymType(const std::string &aSymType)
GRAPHIC_TEXT * processText(const GRAPHIC_DATA &aData, double aScale)
bool loadLayers(BOARD *aBoard)
PCB_LAYER_ID getLayer(const std::string &aLayerName)
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)
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)
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
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_CIRCLE
! Not actually in Fabmaster but we use for 360° arcs
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:2297
void SetFPID(const LIB_ID &aFPID)
Definition: footprint.h:234
PCB_FIELD & Value()
read/write accessors:
Definition: footprint.h:624
void SetOrientationDegrees(double aOrientation)
Definition: footprint.h:224
void SetReference(const wxString &aReference)
Definition: footprint.h:594
void SetValue(const wxString &aValue)
Definition: footprint.h:615
PCB_FIELD & Reference()
Definition: footprint.h:625
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
Definition: footprint.cpp:968
void Flip(const VECTOR2I &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: footprint.cpp:2238
VECTOR2I GetPosition() const override
Definition: footprint.h:209
A logical library item identifier and consists of various portions much like a URI.
Definition: lib_id.h:49
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
Definition: lib_id.cpp:51
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:574
static LSET UserMask()
Definition: lset.cpp:945
static LSET AllTechMask()
Return a mask holding all technical layers (no CU layer) on both side.
Definition: lset.cpp:931
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:863
Handle the data for a net.
Definition: netinfo.h:56
unsigned GetNetCount() const
Definition: netinfo.h:365
const NETNAMES_MAP & NetsByName() const
Return the name map, at least for python.
Definition: netinfo.h:368
static LSET PTHMask()
layer set for a through hole pad
Definition: pad.cpp:349
static LSET UnplatedHoleMask()
layer set for a mechanical unplated through hole pad
Definition: pad.cpp:370
static LSET SMDMask()
layer set for a SMD pad on Front layer
Definition: pad.cpp:356
void Flip(const VECTOR2I &aCentre, bool aFlipLeftRight) override
Flip this object, i.e.
Definition: pcb_shape.cpp:529
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:75
int GetWidth() const override
Definition: pcb_shape.cpp:366
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:314
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:86
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:70
void SetWidth(int aWidth)
Definition: pcb_track.h:106
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:109
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:112
void SetDrillDefault()
Set the drill value for vias to the default value UNDEFINED_DRILL_DIAMETER.
Definition: pcb_track.h:569
void SetDrill(int aDrill)
Set the drill value for vias.
Definition: pcb_track.h:550
void SetPosition(const VECTOR2I &aPoint) override
Definition: pcb_track.h:466
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).
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:114
const VECTOR2I & GetP1() const
Definition: shape_arc.h:113
const VECTOR2I & GetP0() const
Definition: shape_arc.h:112
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...
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 std::vector< VECTOR2I > & CPoints() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void Fracture(POLYGON_MODE aFastMode)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
int VertexCount(int aOutline=-1, int aHole=-1) const
Return the number of vertices in a given outline/hole.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
void Mirror(bool aX=true, bool aY=false, const VECTOR2I &aRef={ 0, 0 })
Mirror the line points about y or x (or both)
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.
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
Simple container to manage line stroke parameters.
Definition: stroke_params.h:81
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
void SetDoNotAllowPads(bool aEnable)
Definition: zone.h:721
void SetLocalClearance(std::optional< int > aClearance)
Definition: zone.h:154
void SetDoNotAllowCopperPour(bool aEnable)
Definition: zone.h:718
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: zone.cpp:261
void SetIsRuleArea(bool aEnable)
Definition: zone.h:717
void SetDoNotAllowTracks(bool aEnable)
Definition: zone.h:720
void SetDoNotAllowVias(bool aEnable)
Definition: zone.h:719
void SetLayerSet(LSET aLayerSet) override
Definition: zone.cpp:267
void SetDoNotAllowFootprints(bool aEnable)
Definition: zone.h:722
void SetAssignedPriority(unsigned aPriority)
Definition: zone.h:114
void SetPadConnection(ZONE_CONNECTION aPadConnection)
Definition: zone.h:267
void SetOutline(SHAPE_POLY_SET *aOutline)
Definition: zone.h:339
The common library.
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:435
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:441
#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:869
constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START
Definition: layer_ids.h:140
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:880
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:117
@ Edge_Cuts
Definition: layer_ids.h:113
@ Dwgs_User
Definition: layer_ids.h:109
@ F_Paste
Definition: layer_ids.h:101
@ Cmts_User
Definition: layer_ids.h:110
@ B_Mask
Definition: layer_ids.h:106
@ B_Cu
Definition: layer_ids.h:95
@ F_Mask
Definition: layer_ids.h:107
@ B_Paste
Definition: layer_ids.h:100
@ UNSELECTED_LAYER
Definition: layer_ids.h:62
@ F_Fab
Definition: layer_ids.h:120
@ F_SilkS
Definition: layer_ids.h:104
@ B_CrtYd
Definition: layer_ids.h:116
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ B_SilkS
Definition: layer_ids.h:103
@ F_Cu
Definition: layer_ids.h:64
@ B_Fab
Definition: layer_ids.h:119
LSET FlipLayerMask(LSET aMask, int aCopperLayersCount)
Calculate the mask layer when flipping a footprint.
Definition: lset.cpp:680
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition: lset.cpp:634
void delete_if(_Container &__c, _Function &&__f)
Deletes all values from __c for which __f returns true.
Definition: kicad_algo.h:174
@ PAD_DRILL_SHAPE_CIRCLE
Definition: pad_shapes.h:54
@ PAD_DRILL_SHAPE_OBLONG
Definition: pad_shapes.h:55
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:310
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 width
! Various sections depending on type
GRAPHIC_SHAPE shape
! Shape of the graphic_item
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
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
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
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:228
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588