You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2936 lines
97 KiB

5 years ago
5 years ago
  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 KiCad Developers, see AUTHORS.txt for contributors.
  6. * Author: Seth Hillbrand <hillbrand@kipro-pcb.com>
  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. #include "import_fabmaster.h"
  26. #include <algorithm>
  27. #include <array>
  28. #include <iostream>
  29. #include <fstream>
  30. #include <map>
  31. #include <memory>
  32. #include <string>
  33. #include <sstream>
  34. #include <vector>
  35. #include <utility>
  36. #include <wx/log.h>
  37. #include <board.h>
  38. #include <board_item.h>
  39. #include <convert_to_biu.h>
  40. #include <fp_shape.h>
  41. #include <pad_shapes.h>
  42. #include <pcb_shape.h>
  43. #include <pcb_text.h>
  44. #include <track.h>
  45. #include <common.h>
  46. #include <geometry/shape_arc.h>
  47. #include <kicad_string.h>
  48. #include <convert_to_biu.h>
  49. #include <math/util.h>
  50. #include <wx/filename.h>
  51. double FABMASTER::readDouble( const std::string aStr ) const
  52. {
  53. std::istringstream istr( aStr );
  54. istr.imbue( std::locale::classic() );
  55. double doubleValue;
  56. istr >> doubleValue;
  57. return doubleValue;
  58. }
  59. int FABMASTER::readInt( const std::string aStr ) const
  60. {
  61. std::istringstream istr( aStr );
  62. istr.imbue( std::locale::classic() );
  63. int intValue;
  64. istr >> intValue;
  65. return intValue;
  66. }
  67. bool FABMASTER::Read( const std::string& aFile )
  68. {
  69. std::ifstream ifs( aFile, std::ios::in | std::ios::binary );
  70. if( !ifs.is_open() )
  71. return false;
  72. m_filename = aFile;
  73. // Read/ignore all bytes in the file to find the size and then go back to the beginning
  74. ifs.ignore( std::numeric_limits<std::streamsize>::max() );
  75. std::streamsize length = ifs.gcount();
  76. ifs.clear();
  77. ifs.seekg( 0, std::ios_base::beg );
  78. std::string buffer( std::istreambuf_iterator<char>{ ifs }, {} );
  79. std::vector < std::string > row;
  80. // Reserve an estimate of the number of rows to prevent continual re-allocation
  81. // crashing (Looking at you MSVC)
  82. row.reserve( length / 100 );
  83. std::string cell;
  84. cell.reserve( 100 );
  85. bool quoted = false;
  86. for( auto ch : buffer )
  87. {
  88. switch( ch )
  89. {
  90. case '"':
  91. if( cell.empty() || cell[0] == '"' )
  92. quoted = !quoted;
  93. cell += ch;
  94. break;
  95. case '!':
  96. if( !quoted )
  97. {
  98. row.push_back( cell );
  99. cell.clear();
  100. }
  101. else
  102. cell += ch;
  103. break;
  104. case '\n':
  105. /// Rows end with "!" and we don't want to keep the empty cell
  106. if( !cell.empty() )
  107. row.push_back( cell );
  108. cell.clear();
  109. rows.push_back( row );
  110. row.clear();
  111. quoted = false;
  112. break;
  113. case '\r':
  114. break;
  115. default:
  116. cell += std::toupper( ch );
  117. }
  118. }
  119. // Handle last line without linebreak
  120. if( !cell.empty() || !row.empty() )
  121. {
  122. row.push_back( cell );
  123. cell.clear();
  124. rows.push_back( row );
  125. row.clear();
  126. }
  127. return true;
  128. }
  129. FABMASTER::section_type FABMASTER::detectType( size_t aOffset )
  130. {
  131. single_row row;
  132. try
  133. {
  134. row = rows.at( aOffset );
  135. }
  136. catch( std::out_of_range& )
  137. {
  138. return UNKNOWN_EXTRACT;
  139. }
  140. if( row.size() < 3 )
  141. return UNKNOWN_EXTRACT;
  142. if( row[0].back() != 'A' )
  143. return UNKNOWN_EXTRACT;
  144. std::string row1 = row[1];
  145. std::string row2 = row[2];
  146. std::string row3{};
  147. /// We strip the underscores from all column names as some export variants use them and some do not
  148. row1.erase( std::remove_if( row1.begin(), row1.end(), []( char c ){ return c == '_'; } ),
  149. row1.end() );
  150. row2.erase( std::remove_if( row2.begin(), row2.end(), []( char c ){ return c == '_'; } ),
  151. row2.end() );
  152. if( row.size() > 3 )
  153. {
  154. row3 = row[3];
  155. row3.erase( std::remove_if( row3.begin(), row3.end(), []( char c ){ return c == '_'; } ),
  156. row3.end() );
  157. }
  158. if( row1 == "REFDES" && row2 == "COMPCLASS" )
  159. return EXTRACT_REFDES;
  160. if( row1 == "NETNAME" && row2 == "REFDES" )
  161. return EXTRACT_NETS;
  162. if( row1 == "CLASS" && row2 == "SUBCLASS" && row3.empty() )
  163. return EXTRACT_BASIC_LAYERS;
  164. if( row1 == "GRAPHICDATANAME" && row2 == "GRAPHICDATANUMBER" )
  165. return EXTRACT_GRAPHICS;
  166. if( row1 == "CLASS" && row2 == "SUBCLASS" && row3 == "GRAPHICDATANAME" )
  167. return EXTRACT_TRACES;
  168. if( row1 == "SYMNAME" && row2 == "PINNAME" )
  169. return FABMASTER_EXTRACT_PINS;
  170. if( row1 == "SYMNAME" && row2 == "SYMMIRROR" && row3 == "PINNAME" )
  171. return EXTRACT_PINS;
  172. if( row1 == "VIAX" && row2 == "VIAY" )
  173. return EXTRACT_VIAS;
  174. if( row1 == "SUBCLASS" && row2 == "PADSHAPENAME" )
  175. return EXTRACT_PAD_SHAPES;
  176. if( row1 == "PADNAME" )
  177. return EXTRACT_PADSTACKS;
  178. if( row1 == "LAYERSORT" )
  179. return EXTRACT_FULL_LAYERS;
  180. wxLogError
  181. (
  182. wxString::Format( _( "Unknown FABMASTER section %s:%s at row %zu." ), row1.c_str(),
  183. row2.c_str(), aOffset ) );
  184. return UNKNOWN_EXTRACT;
  185. }
  186. double FABMASTER::processScaleFactor( size_t aRow )
  187. {
  188. double retval = 0.0;
  189. if( aRow >= rows.size() )
  190. return -1.0;
  191. if( rows[aRow].size() < 11 )
  192. {
  193. wxLogError( wxString::Format( _( "Invalid row size in J row %zu. "
  194. "Expecting 11 elements but found %zu" ), aRow, rows[aRow].size() ) );
  195. return -1.0;
  196. }
  197. for( int i = 7; i < 10 && retval < 1.0; ++i )
  198. {
  199. auto units = rows[aRow][i];
  200. std::transform(units.begin(), units.end(),units.begin(), ::toupper);
  201. if( units == "MILS" )
  202. retval = IU_PER_MILS;
  203. else if( units == "MILLIMETERS" )
  204. retval = IU_PER_MM;
  205. else if( units == "MICRONS" )
  206. retval = IU_PER_MM * 10.0;
  207. else if( units == "INCHES" )
  208. retval = IU_PER_MILS * 1000.0;
  209. }
  210. if( retval < 1.0 )
  211. {
  212. wxLogError( _( "Could not find units value, defaulting to Mils" ) );
  213. retval = IU_PER_MILS;
  214. }
  215. return retval;
  216. }
  217. int FABMASTER::getColFromName( size_t aRow, const std::string& aStr )
  218. {
  219. if( aRow >= rows.size() )
  220. return -1;
  221. auto header = rows[aRow];
  222. for( size_t i = 0; i < header.size(); i++ )
  223. {
  224. /// Some Fabmaster headers include the underscores while others do not
  225. /// so we strip them uniformly before comparing
  226. header[i].erase( std::remove_if( header[i].begin(), header[i].end(),
  227. []( const char c ){ return c == '_'; } ), header[i].end() );
  228. if( header[i] == aStr )
  229. return i;
  230. }
  231. THROW_IO_ERROR( wxString::Format( _( "Could not find column label %s" ), aStr.c_str() ) );
  232. return -1;
  233. }
  234. PCB_LAYER_ID FABMASTER::getLayer( const std::string& aLayerName )
  235. {
  236. const auto& kicad_layer = layers.find( aLayerName);
  237. if( kicad_layer == layers.end() )
  238. return UNDEFINED_LAYER;
  239. else
  240. return static_cast<PCB_LAYER_ID>( kicad_layer->second.layerid );
  241. }
  242. size_t FABMASTER::processPadStackLayers( size_t aRow )
  243. {
  244. size_t rownum = aRow + 2;
  245. if( rownum >= rows.size() )
  246. return -1;
  247. const auto header = rows[aRow];
  248. int pad_name_col = getColFromName( aRow, "PADNAME" );
  249. int pad_num_col = getColFromName( aRow, "RECNUMBER" );
  250. int pad_lay_col = getColFromName( aRow, "LAYER" );
  251. int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
  252. int pad_via_col = getColFromName( aRow, "VIAFLAG" );
  253. int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
  254. int pad_width_col = getColFromName( aRow, "PADWIDTH" );
  255. int pad_height_col = getColFromName( aRow, "PADHGHT" );
  256. int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
  257. int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
  258. int pad_flash_col = getColFromName( aRow, "PADFLASH" );
  259. int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
  260. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  261. {
  262. auto row = rows[rownum];
  263. if( row.size() != header.size() )
  264. {
  265. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  266. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  267. continue;
  268. }
  269. auto pad_name = row[pad_name_col];
  270. auto pad_num = row[pad_num_col];
  271. auto pad_layer = row[pad_lay_col];
  272. auto pad_is_fixed = row[pad_fix_col];
  273. auto pad_is_via = row[pad_via_col];
  274. auto pad_shape = row[pad_shape_col];
  275. auto pad_width = row[pad_width_col];
  276. auto pad_height = row[pad_height_col];
  277. auto pad_xoff = row[pad_xoff_col];
  278. auto pad_yoff = row[pad_yoff_col];
  279. auto pad_flash = row[pad_flash_col];
  280. auto pad_shapename = row[pad_shape_name_col];
  281. // This layer setting seems to be unused
  282. if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
  283. continue;
  284. // Skip the technical layers
  285. if( pad_layer[0] == '~' )
  286. break;
  287. auto result = layers.emplace( pad_layer, FABMASTER_LAYER{} );
  288. FABMASTER_LAYER& layer = result.first->second;
  289. /// If the layer ids have not yet been assigned
  290. if( layer.id == 0 )
  291. {
  292. layer.name = pad_layer;
  293. layer.id = readInt( pad_num );
  294. layer.conductive = true;
  295. }
  296. }
  297. return 0;
  298. }
  299. /**
  300. * A!PADNAME!RECNUMBER!LAYER!FIXFLAG!VIAFLAG!PADSHAPE1!PADWIDTH!PADHGHT!
  301. * PADXOFF!PADYOFF!PADFLASH!PADSHAPENAME!TRELSHAPE1!TRELWIDTH!TRELHGHT!
  302. * TRELXOFF!TRELYOFF!TRELFLASH!TRELSHAPENAME!APADSHAPE1!APADWIDTH!APADHGHT!
  303. * APADXOFF!APADYOFF!APADFLASH!APADSHAPENAME!
  304. */
  305. size_t FABMASTER::processPadStacks( size_t aRow )
  306. {
  307. size_t rownum = aRow + 2;
  308. if( rownum >= rows.size() )
  309. return -1;
  310. const auto header = rows[aRow];
  311. double scale_factor = processScaleFactor( aRow + 1 );
  312. if( scale_factor <= 0.0 )
  313. return -1;
  314. int pad_name_col = getColFromName( aRow, "PADNAME" );
  315. int pad_num_col = getColFromName( aRow, "RECNUMBER" );
  316. int pad_lay_col = getColFromName( aRow, "LAYER" );
  317. int pad_fix_col = getColFromName( aRow, "FIXFLAG" );
  318. int pad_via_col = getColFromName( aRow, "VIAFLAG" );
  319. int pad_shape_col = getColFromName( aRow, "PADSHAPE1" );
  320. int pad_width_col = getColFromName( aRow, "PADWIDTH" );
  321. int pad_height_col = getColFromName( aRow, "PADHGHT" );
  322. int pad_xoff_col = getColFromName( aRow, "PADXOFF" );
  323. int pad_yoff_col = getColFromName( aRow, "PADYOFF" );
  324. int pad_flash_col = getColFromName( aRow, "PADFLASH" );
  325. int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
  326. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  327. {
  328. auto row = rows[rownum];
  329. FM_PAD* pad;
  330. if( row.size() != header.size() )
  331. {
  332. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  333. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  334. continue;
  335. }
  336. auto pad_name = row[pad_name_col];
  337. auto pad_num = row[pad_num_col];
  338. auto pad_layer = row[pad_lay_col];
  339. auto pad_is_fixed = row[pad_fix_col];
  340. auto pad_is_via = row[pad_via_col];
  341. auto pad_shape = row[pad_shape_col];
  342. auto pad_width = row[pad_width_col];
  343. auto pad_height = row[pad_height_col];
  344. auto pad_xoff = row[pad_xoff_col];
  345. auto pad_yoff = row[pad_yoff_col];
  346. auto pad_flash = row[pad_flash_col];
  347. auto pad_shapename = row[pad_shape_name_col];
  348. // This layer setting seems to be unused
  349. if( pad_layer == "INTERNAL_PAD_DEF" || pad_layer == "internal_pad_def" )
  350. continue;
  351. int recnum = KiROUND( readDouble( pad_num ) );
  352. auto new_pad = pads.find( pad_name );
  353. if( new_pad != pads.end() )
  354. pad = &new_pad->second;
  355. else
  356. {
  357. pads[pad_name] = FM_PAD();
  358. pad = &pads[pad_name];
  359. pad->name = pad_name;
  360. }
  361. /// Handle the drill layer
  362. if( pad_layer == "~DRILL" )
  363. {
  364. int drill_hit;
  365. int drill_x;
  366. int drill_y;
  367. try
  368. {
  369. drill_hit = KiROUND( std::fabs( readDouble( pad_shape ) * scale_factor ) );
  370. drill_x = KiROUND( std::fabs( readDouble( pad_width ) * scale_factor ) );
  371. drill_y = KiROUND( std::fabs( readDouble( pad_height ) * scale_factor ) );
  372. }
  373. catch( ... )
  374. {
  375. wxLogError( wxString::Format( _( "Expecting drill size value "
  376. "but found %s!%s!%s at line %zu" ),
  377. pad_shape.c_str(), pad_width.c_str(), pad_height.c_str(), rownum ) );
  378. continue;
  379. }
  380. if( drill_hit == 0 )
  381. {
  382. pad->drill = false;
  383. continue;
  384. }
  385. pad->drill = true;
  386. /// This is to account for broken fabmaster outputs where circle drill hits don't actually get the
  387. /// drill hit value.
  388. if( drill_x == drill_y )
  389. {
  390. pad->drill_size_x = drill_hit;
  391. pad->drill_size_y = drill_hit;
  392. }
  393. else
  394. {
  395. pad->drill_size_x = drill_x;
  396. pad->drill_size_y = drill_y;
  397. }
  398. if( !pad_shapename.empty() && pad_shapename[0] == 'P' )
  399. pad->plated = true;
  400. continue;
  401. }
  402. if( pad_shape.empty() )
  403. continue;
  404. double w;
  405. double h;
  406. try
  407. {
  408. w = readDouble( pad_width ) * scale_factor;
  409. h = readDouble( pad_height ) * scale_factor;
  410. }
  411. catch( ... )
  412. {
  413. wxLogError( wxString::Format( _( "Expecting pad size values "
  414. "but found %s : %s at line %zu" ), pad_width.c_str(), pad_height.c_str(), rownum ) );
  415. continue;
  416. }
  417. if( w <= 0.0 )
  418. continue;
  419. auto layer = layers.find( pad_layer );
  420. if( layer != layers.end() )
  421. {
  422. if( layer->second.layerid == F_Cu )
  423. pad->top = true;
  424. else if( layer->second.layerid == B_Cu )
  425. pad->bottom = true;
  426. }
  427. if( w > std::numeric_limits<int>::max() || h > std::numeric_limits<int>::max() )
  428. {
  429. wxLogError( wxString::Format( _( "Invalid pad size on line %zu" ), rownum ) );
  430. continue;
  431. }
  432. if( pad_layer == "~TSM" || pad_layer == "~BSM" )
  433. {
  434. if( w > 0.0 && h > 0.0 )
  435. {
  436. pad->mask_width = KiROUND( w );
  437. pad->mask_height = KiROUND( h );
  438. }
  439. continue;
  440. }
  441. if( pad_layer == "~TSP" || pad_layer == "~BSP" )
  442. {
  443. if( w > 0.0 && h > 0.0 )
  444. {
  445. pad->paste_width = KiROUND( w );
  446. pad->paste_height = KiROUND( h );
  447. }
  448. continue;
  449. }
  450. /// All remaining technical layers are not handled
  451. if( pad_layer[0] == '~' )
  452. continue;
  453. try
  454. {
  455. pad->x_offset = KiROUND( readDouble( pad_xoff ) * scale_factor );
  456. pad->y_offset = -KiROUND( readDouble( pad_yoff ) * scale_factor );
  457. }
  458. catch( ... )
  459. {
  460. wxLogError( wxString::Format( _( "Expecting pad offset values "
  461. "but found %s : %s at line %zu" ), pad_xoff.c_str(), pad_yoff.c_str(), rownum ) );
  462. continue;
  463. }
  464. if( w > 0.0 && h > 0.0 && recnum == 1 )
  465. {
  466. pad->width = KiROUND( w );
  467. pad->height = KiROUND( h );
  468. pad->via = ( std::toupper( pad_is_via[0] ) != 'V' );
  469. if( pad_shape == "CIRCLE" )
  470. {
  471. pad->height = pad->width;
  472. pad->shape = PAD_SHAPE_CIRCLE;
  473. }
  474. else if( pad_shape == "RECTANGLE" )
  475. {
  476. pad->shape = PAD_SHAPE_RECT;
  477. }
  478. else if( pad_shape == "ROUNDED_RECT" )
  479. {
  480. pad->shape = PAD_SHAPE_ROUNDRECT;
  481. }
  482. else if( pad_shape == "SQUARE" )
  483. {
  484. pad->shape = PAD_SHAPE_RECT;
  485. pad->height = pad->width;
  486. }
  487. else if( pad_shape == "OBLONG" || pad_shape == "OBLONG_X" || pad_shape == "OBLONG_Y" )
  488. pad->shape = PAD_SHAPE_OVAL;
  489. else if( pad_shape == "OCTAGON" )
  490. {
  491. pad->shape = PAD_SHAPE_RECT;
  492. pad->is_octogon = true;
  493. }
  494. else if( pad_shape == "SHAPE" )
  495. {
  496. pad->shape = PAD_SHAPE_CUSTOM;
  497. pad->custom_name = pad_shapename;
  498. }
  499. else
  500. {
  501. wxLogError( wxString::Format( _( "Unknown pad shape name '%s' on layer '%s' at line %zu" ),
  502. pad_shape.c_str(), pad_layer.c_str(), rownum ) );
  503. continue;
  504. }
  505. }
  506. }
  507. return rownum - aRow;
  508. }
  509. size_t FABMASTER::processSimpleLayers( size_t aRow )
  510. {
  511. size_t rownum = aRow + 2;
  512. if( rownum >= rows.size() )
  513. return -1;
  514. auto header = rows[aRow];
  515. double scale_factor = processScaleFactor( aRow + 1 );
  516. if( scale_factor <= 0.0 )
  517. return -1;
  518. int layer_class_col = getColFromName( aRow, "CLASS" );
  519. int layer_subclass_col = getColFromName( aRow, "SUBCLASS" );
  520. if( layer_class_col < 0 || layer_subclass_col < 0 )
  521. return -1;
  522. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  523. {
  524. auto row = rows[rownum];
  525. if( row.size() != header.size() )
  526. {
  527. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  528. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  529. continue;
  530. }
  531. auto result = layers.emplace( row[layer_subclass_col], FABMASTER_LAYER{} );
  532. FABMASTER_LAYER& layer = result.first->second;
  533. layer.name = row[layer_subclass_col];
  534. layer.positive = true;
  535. layer.conductive = false;
  536. if( row[layer_class_col] == "ANTI ETCH" )
  537. {
  538. layer.positive = false;
  539. layer.conductive = true;
  540. }
  541. else if( row[layer_class_col] == "ETCH" )
  542. {
  543. layer.conductive = true;
  544. }
  545. }
  546. return rownum - aRow;
  547. }
  548. bool FABMASTER::assignLayers()
  549. {
  550. bool has_l1 = false;
  551. int max_layer = 0;
  552. std::string max_layer_name;
  553. std::vector<std::pair<std::string, int>> extra_layers
  554. {
  555. { "ASSEMBLY_TOP", F_Fab },
  556. { "ASSEMBLY_BOTTOM", B_Fab },
  557. { "PLACE_BOUND_TOP", F_CrtYd },
  558. { "PLACE_BOUND_BOTTOM", B_CrtYd },
  559. };
  560. std::vector<FABMASTER_LAYER*> layer_order;
  561. for( auto& el : layers )
  562. {
  563. FABMASTER_LAYER& layer = el.second;
  564. layer.layerid = UNSELECTED_LAYER;
  565. if( layer.conductive )
  566. {
  567. layer_order.push_back( &layer );
  568. }
  569. else if( layer.name.find( "SILK" ) != std::string::npos &&
  570. layer.name.find( "AUTOSILK" ) == std::string::npos ) // Skip the autosilk layer
  571. {
  572. if( layer.name.find( "B" ) != std::string::npos )
  573. layer.layerid = B_SilkS;
  574. else
  575. layer.layerid = F_SilkS;
  576. }
  577. else if( layer.name.find( "MASK" ) != std::string::npos ||
  578. layer.name.find( "MSK" ) != std::string::npos )
  579. {
  580. if( layer.name.find( "B" ) != std::string::npos )
  581. layer.layerid = B_Mask;
  582. else
  583. layer.layerid = F_Mask;
  584. }
  585. else if( layer.name.find( "PAST" ) != std::string::npos )
  586. {
  587. if( layer.name.find( "B" ) != std::string::npos )
  588. layer.layerid = B_Paste;
  589. else
  590. layer.layerid = F_Paste;
  591. }
  592. else if( layer.name.find( "NCLEGEND" ) != std::string::npos )
  593. layer.layerid = Dwgs_User;
  594. else
  595. layer.disable = true;
  596. }
  597. std::sort( layer_order.begin(), layer_order.end(), FABMASTER_LAYER::BY_ID() );
  598. int layernum = 0;
  599. for( auto layer : layer_order )
  600. layer->layerid = layernum++;
  601. /// Back copper has a special id number, so assign that to the last copper layer
  602. /// in the stackup
  603. layer_order.back()->layerid = B_Cu;
  604. for( auto& new_pair : extra_layers )
  605. {
  606. FABMASTER_LAYER new_layer;
  607. new_layer.name = new_pair.first;
  608. new_layer.layerid = new_pair.second;
  609. new_layer.conductive = false;
  610. auto result = layers.emplace( new_pair.first, new_layer );
  611. if( !result.second )
  612. {
  613. result.first->second.layerid = new_pair.second;
  614. result.first->second.disable = false;
  615. }
  616. }
  617. return true;
  618. }
  619. /**
  620. * A!LAYER_SORT!LAYER_SUBCLASS!LAYER_ARTWORK!LAYER_USE!LAYER_CONDUCTOR!LAYER_DIELECTRIC_CONSTANT!
  621. * LAYER_ELECTRICAL_CONDUCTIVITY!LAYER_MATERIAL!LAYER_SHIELD_LAYER!LAYER_THERMAL_CONDUCTIVITY!
  622. * LAYER_THICKNESS!
  623. */
  624. size_t FABMASTER::processLayers( size_t aRow )
  625. {
  626. size_t rownum = aRow + 2;
  627. if( rownum >= rows.size() )
  628. return -1;
  629. auto header = rows[aRow];
  630. double scale_factor = processScaleFactor( aRow + 1 );
  631. if( scale_factor <= 0.0 )
  632. return -1;
  633. int layer_sort_col = getColFromName( aRow, "LAYERSORT" );
  634. int layer_subclass_col = getColFromName( aRow, "LAYERSUBCLASS" );
  635. int layer_art_col = getColFromName( aRow, "LAYERARTWORK" );
  636. int layer_use_col = getColFromName( aRow, "LAYERUSE" );
  637. int layer_cond_col = getColFromName( aRow, "LAYERCONDUCTOR" );
  638. int layer_er_col = getColFromName( aRow, "LAYERDIELECTRICCONSTANT" );
  639. int layer_rho_col = getColFromName( aRow, "LAYERELECTRICALCONDUCTIVITY" );
  640. int layer_mat_col = getColFromName( aRow, "LAYERMATERIAL" );
  641. if( layer_sort_col < 0 || layer_subclass_col < 0 || layer_art_col < 0 || layer_use_col < 0
  642. || layer_cond_col < 0 || layer_er_col < 0 || layer_rho_col < 0 || layer_mat_col < 0 )
  643. return -1;
  644. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  645. {
  646. auto row = rows[rownum];
  647. if( row.size() != header.size() )
  648. {
  649. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  650. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  651. continue;
  652. }
  653. auto layer_sort = row[layer_sort_col];
  654. auto layer_subclass = row[layer_subclass_col];
  655. auto layer_art = row[layer_art_col];
  656. auto layer_use = row[layer_use_col];
  657. auto layer_cond = row[layer_cond_col];
  658. auto layer_er = row[layer_er_col];
  659. auto layer_rho = row[layer_rho_col];
  660. auto layer_mat = row[layer_mat_col];
  661. if( layer_mat == "AIR" )
  662. continue;
  663. FABMASTER_LAYER layer;
  664. if( layer_subclass.empty() )
  665. {
  666. if( layer_cond != "NO" )
  667. layer.name = "In.Cu" + layer_sort;
  668. else
  669. layer.name = "Dielectric" + layer_sort;
  670. }
  671. layer.positive = ( layer_art != "NEGATIVE" );
  672. layers.emplace( layer.name, layer );
  673. }
  674. return rownum - aRow;
  675. }
  676. /**
  677. * A!SUBCLASS!PAD_SHAPE_NAME!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!
  678. * GRAPHIC_DATA_2!GRAPHIC_DATA_3!GRAPHIC_DATA_4!GRAPHIC_DATA_5!GRAPHIC_DATA_6!GRAPHIC_DATA_7!
  679. * GRAPHIC_DATA_8!GRAPHIC_DATA_9!PAD_STACK_NAME!REFDES!PIN_NUMBER!
  680. */
  681. size_t FABMASTER::processCustomPads( size_t aRow )
  682. {
  683. size_t rownum = aRow + 2;
  684. if( rownum >= rows.size() )
  685. return -1;
  686. auto header = rows[aRow];
  687. double scale_factor = processScaleFactor( aRow + 1 );
  688. if( scale_factor <= 0.0 )
  689. return -1;
  690. int pad_subclass_col = getColFromName( aRow, "SUBCLASS" );
  691. int pad_shape_name_col = getColFromName( aRow, "PADSHAPENAME" );
  692. int pad_grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
  693. int pad_grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
  694. int pad_record_tag_col = getColFromName( aRow, "RECORDTAG" );
  695. int pad_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
  696. int pad_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
  697. int pad_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
  698. int pad_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
  699. int pad_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
  700. int pad_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
  701. int pad_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
  702. int pad_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
  703. int pad_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
  704. int pad_stack_name_col = getColFromName( aRow, "PADSTACKNAME" );
  705. int pad_refdes_col = getColFromName( aRow, "REFDES" );
  706. int pad_pin_num_col = getColFromName( aRow, "PINNUMBER" );
  707. if( pad_subclass_col < 0 || pad_shape_name_col < 0 || pad_grdata1_col < 0 || pad_grdata2_col < 0
  708. || pad_grdata3_col < 0 || pad_grdata4_col < 0 || pad_grdata5_col < 0
  709. || pad_grdata6_col < 0 || pad_grdata7_col < 0 || pad_grdata8_col < 0
  710. || pad_grdata9_col < 0 || pad_stack_name_col < 0 || pad_refdes_col < 0
  711. || pad_pin_num_col < 0 )
  712. return -1;
  713. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  714. {
  715. auto row = rows[rownum];
  716. if( row.size() != header.size() )
  717. {
  718. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  719. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  720. continue;
  721. }
  722. auto pad_layer = row[pad_subclass_col];
  723. auto pad_shape_name = row[pad_shape_name_col];
  724. auto pad_record_tag = row[pad_record_tag_col];
  725. GRAPHIC_DATA gr_data;
  726. gr_data.graphic_dataname = row[pad_grdata_name_col];
  727. gr_data.graphic_datanum = row[pad_grdata_num_col];
  728. gr_data.graphic_data1 = row[pad_grdata1_col];
  729. gr_data.graphic_data2 = row[pad_grdata2_col];
  730. gr_data.graphic_data3 = row[pad_grdata3_col];
  731. gr_data.graphic_data4 = row[pad_grdata4_col];
  732. gr_data.graphic_data5 = row[pad_grdata5_col];
  733. gr_data.graphic_data6 = row[pad_grdata6_col];
  734. gr_data.graphic_data7 = row[pad_grdata7_col];
  735. gr_data.graphic_data8 = row[pad_grdata8_col];
  736. gr_data.graphic_data9 = row[pad_grdata9_col];
  737. auto pad_stack_name = row[pad_stack_name_col];
  738. auto pad_refdes = row[pad_refdes_col];
  739. auto pad_pin_num = row[pad_pin_num_col];
  740. // N.B. We get the FIGSHAPE records as "FIG_SHAPE name". We only want "name"
  741. // and we don't process other pad shape records
  742. std::string prefix( "FIG_SHAPE " );
  743. if( pad_shape_name.length() <= prefix.length()
  744. || !std::equal( prefix.begin(), prefix.end(), pad_shape_name.begin() ) )
  745. {
  746. continue;
  747. }
  748. // Custom pads are a series of records with the same record ID but incrementing
  749. // Sequence numbers.
  750. int id = -1;
  751. int seq = -1;
  752. if( std::sscanf( pad_record_tag.c_str(), "%d %d", &id, &seq ) != 2 )
  753. {
  754. wxLogError( wxString::Format( _( "Invalid format for id string \"%s\" "
  755. "in custom pad row %zu" ),
  756. pad_record_tag.c_str(), rownum ) );
  757. continue;
  758. }
  759. auto name = pad_shape_name.substr( prefix.length() );
  760. name += "_" + pad_refdes + "_" + pad_pin_num;
  761. auto ret = pad_shapes.emplace( name, PAD_SHAPE{} );
  762. auto& custom_pad = ret.first->second;
  763. // If we were able to insert the pad name, then we need to initialize the
  764. // record
  765. if( ret.second )
  766. {
  767. custom_pad.name = name;
  768. custom_pad.padstack = pad_stack_name;
  769. custom_pad.pinnum = pad_pin_num;
  770. custom_pad.refdes = pad_refdes;
  771. }
  772. // At this point we extract the individual graphical elements for processing the complex pad. The
  773. // coordinates are in board origin format, so we'll need to fix the offset later when we assign them
  774. // to the modules.
  775. auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
  776. if( gr_item )
  777. {
  778. gr_item->layer = pad_layer;
  779. gr_item->refdes = pad_refdes;
  780. gr_item->seq = seq;
  781. gr_item->subseq = 0;
  782. /// emplace may fail here, in which case, it returns the correct position to use for the existing map
  783. auto pad_it = custom_pad.elements.emplace( id, graphic_element{} );
  784. auto retval = pad_it.first->second.insert( std::move(gr_item ) );
  785. if( !retval.second )
  786. {
  787. wxLogError( wxString::Format( _( "Could not insert graphical item %d into padstack \"%s\"" ),
  788. seq, pad_stack_name.c_str() ) );
  789. }
  790. }
  791. else
  792. {
  793. wxLogError( wxString::Format( _( "Unrecognized pad shape primitive \"%s\" in line %zu." ),
  794. gr_data.graphic_dataname, rownum ) );
  795. }
  796. }
  797. return rownum - aRow;
  798. }
  799. FABMASTER::GRAPHIC_LINE* FABMASTER::processLine( const FABMASTER::GRAPHIC_DATA& aData, double aScale )
  800. {
  801. GRAPHIC_LINE* new_line = new GRAPHIC_LINE ;
  802. new_line->shape = GR_SHAPE_LINE;
  803. new_line->start_x = KiROUND( readDouble( aData.graphic_data1.c_str() ) * aScale );
  804. new_line->start_y = -KiROUND( readDouble( aData.graphic_data2.c_str() ) * aScale );
  805. new_line->end_x = KiROUND( readDouble( aData.graphic_data3.c_str() ) * aScale );
  806. new_line->end_y = -KiROUND( readDouble( aData.graphic_data4.c_str() ) * aScale );
  807. new_line->width = KiROUND( readDouble( aData.graphic_data5.c_str() ) * aScale );
  808. return new_line;
  809. }
  810. FABMASTER::GRAPHIC_ARC* FABMASTER::processArc( const FABMASTER::GRAPHIC_DATA& aData, double aScale )
  811. {
  812. GRAPHIC_ARC* new_arc = new GRAPHIC_ARC ;
  813. new_arc->shape = GR_SHAPE_ARC;
  814. new_arc->start_x = KiROUND( readDouble( aData.graphic_data1.c_str() ) * aScale );
  815. new_arc->start_y = -KiROUND( readDouble( aData.graphic_data2.c_str() ) * aScale );
  816. new_arc->end_x = KiROUND( readDouble( aData.graphic_data3.c_str() ) * aScale );
  817. new_arc->end_y = -KiROUND( readDouble( aData.graphic_data4.c_str() ) * aScale );
  818. new_arc->center_x = KiROUND( readDouble( aData.graphic_data5.c_str() ) * aScale );
  819. new_arc->center_y = -KiROUND( readDouble( aData.graphic_data6.c_str() ) * aScale );
  820. new_arc->radius = KiROUND( readDouble( aData.graphic_data7.c_str() ) * aScale );
  821. new_arc->width = KiROUND( readDouble( aData.graphic_data8.c_str() ) * aScale );
  822. new_arc->clockwise = ( aData.graphic_data9 != "COUNTERCLOCKWISE" );
  823. double startangle = NormalizeAnglePos( RAD2DECIDEG(
  824. atan2( new_arc->start_y - new_arc->center_y,
  825. new_arc->start_x - new_arc->center_x ) ) );
  826. double endangle = NormalizeAnglePos( RAD2DECIDEG(
  827. atan2( new_arc->end_y - new_arc->center_y,
  828. new_arc->end_x - new_arc->center_x ) ) );
  829. double angle;
  830. VECTOR2I center( new_arc->center_x, new_arc->center_y );
  831. VECTOR2I start( new_arc->start_x, new_arc->start_y );
  832. VECTOR2I mid( new_arc->start_x, new_arc->start_y );
  833. VECTOR2I end( new_arc->end_x, new_arc->end_y );
  834. angle = endangle - startangle;
  835. if( new_arc->clockwise && angle < 0.0 )
  836. angle += 3600.0;
  837. if( !new_arc->clockwise && angle > 0.0 )
  838. angle -= 3600.0;
  839. if( start == end )
  840. angle = -3600.0;
  841. RotatePoint( mid, center, -angle / 2.0 );
  842. new_arc->result = SHAPE_ARC( start, mid, end, 0 );
  843. return new_arc;
  844. }
  845. FABMASTER::GRAPHIC_RECTANGLE* FABMASTER::processRectangle( const FABMASTER::GRAPHIC_DATA& aData, double aScale )
  846. {
  847. GRAPHIC_RECTANGLE* new_rect = new GRAPHIC_RECTANGLE;
  848. new_rect->shape = GR_SHAPE_RECTANGLE;
  849. new_rect->start_x = KiROUND( readDouble( aData.graphic_data1.c_str() ) * aScale );
  850. new_rect->start_y = -KiROUND( readDouble( aData.graphic_data2.c_str() ) * aScale );
  851. new_rect->end_x = KiROUND( readDouble( aData.graphic_data3.c_str() ) * aScale );
  852. new_rect->end_y = -KiROUND( readDouble( aData.graphic_data4.c_str() ) * aScale );
  853. new_rect->fill = aData.graphic_data5 == "1";
  854. new_rect->width = 0;
  855. return new_rect;
  856. }
  857. FABMASTER::GRAPHIC_TEXT* FABMASTER::processText( const FABMASTER::GRAPHIC_DATA& aData, double aScale )
  858. {
  859. GRAPHIC_TEXT* new_text = new GRAPHIC_TEXT;
  860. new_text->shape = GR_SHAPE_TEXT;
  861. new_text->start_x = KiROUND( readDouble( aData.graphic_data1.c_str() ) * aScale );
  862. new_text->start_y = -KiROUND( readDouble( aData.graphic_data2.c_str() ) * aScale );
  863. new_text->rotation = KiROUND( readDouble( aData.graphic_data3.c_str() ) );
  864. new_text->mirror = ( aData.graphic_data4 == "YES" );
  865. if( aData.graphic_data5 == "RIGHT" )
  866. new_text->orient = GR_TEXT_HJUSTIFY_RIGHT;
  867. else if( aData.graphic_data5 == "CENTER" )
  868. new_text->orient = GR_TEXT_HJUSTIFY_CENTER;
  869. else
  870. new_text->orient = GR_TEXT_HJUSTIFY_LEFT;
  871. std::vector<std::string> toks = split( aData.graphic_data6, " \t" );
  872. if( toks.size() < 8 )
  873. {
  874. // We log the error here but continue in the case of too few tokens
  875. wxLogError( wxString::Format( _( "Invalid token count."
  876. " Expected 8 but found %zu" ), toks.size() ) );
  877. new_text->height = 0;
  878. new_text->width = 0;
  879. new_text->ital = false;
  880. new_text->thickness = 0;
  881. }
  882. else
  883. {
  884. // 0 = size
  885. // 1 = font
  886. new_text->height = KiROUND( readDouble( toks[2].c_str() ) * aScale );
  887. new_text->width = KiROUND( readDouble( toks[3].c_str() ) * aScale );
  888. new_text->ital = readDouble( toks[4].c_str() ) != 0.0;
  889. // 5 = character spacing
  890. // 6 = line spacing
  891. new_text->thickness = KiROUND( readDouble( toks[7].c_str() ) * aScale );
  892. }
  893. new_text->text = aData.graphic_data7;
  894. return new_text;
  895. }
  896. FABMASTER::GRAPHIC_ITEM* FABMASTER::processGraphic( const GRAPHIC_DATA& aData, double aScale )
  897. {
  898. GRAPHIC_ITEM* retval = nullptr;
  899. if( aData.graphic_dataname == "LINE" )
  900. retval = processLine( aData, aScale );
  901. else if( aData.graphic_dataname == "ARC" )
  902. retval = processArc( aData, aScale );
  903. else if( aData.graphic_dataname == "RECTANGLE" )
  904. retval = processRectangle( aData, aScale );
  905. else if( aData.graphic_dataname == "TEXT" )
  906. retval = processText( aData, aScale );
  907. if( retval && !aData.graphic_data10.empty() )
  908. {
  909. if( aData.graphic_data10 == "CONNECT" )
  910. retval->type = GR_TYPE_CONNECT;
  911. else if( aData.graphic_data10 == "NOTCONNECT" )
  912. retval->type = GR_TYPE_NOTCONNECT;
  913. else if( aData.graphic_data10 == "SHAPE" )
  914. retval->type = GR_TYPE_NOTCONNECT;
  915. else if( aData.graphic_data10 == "VOID" )
  916. retval->type = GR_TYPE_NOTCONNECT;
  917. else if( aData.graphic_data10 == "POLYGON" )
  918. retval->type = GR_TYPE_NOTCONNECT;
  919. else
  920. retval->type = GR_TYPE_NONE;
  921. }
  922. return retval;
  923. }
  924. /**
  925. * A!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!GRAPHIC_DATA_3!
  926. * GRAPHIC_DATA_4!GRAPHIC_DATA_5!GRAPHIC_DATA_6!GRAPHIC_DATA_7!GRAPHIC_DATA_8!GRAPHIC_DATA_9!
  927. * SUBCLASS!SYM_NAME!REFDES!
  928. */
  929. size_t FABMASTER::processGeometry( size_t aRow )
  930. {
  931. size_t rownum = aRow + 2;
  932. if( rownum >= rows.size() )
  933. return -1;
  934. const auto header = rows[aRow];
  935. double scale_factor = processScaleFactor( aRow + 1 );
  936. if( scale_factor <= 0.0 )
  937. return -1;
  938. int geo_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
  939. int geo_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
  940. int geo_tag_col = getColFromName( aRow, "RECORDTAG" );
  941. int geo_grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
  942. int geo_grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
  943. int geo_grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
  944. int geo_grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
  945. int geo_grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
  946. int geo_grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
  947. int geo_grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
  948. int geo_grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
  949. int geo_grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
  950. int geo_subclass_col = getColFromName( aRow, "SUBCLASS" );
  951. int geo_sym_name_col = getColFromName( aRow, "SYMNAME" );
  952. int geo_refdes_col = getColFromName( aRow, "REFDES" );
  953. if( geo_name_col < 0 || geo_num_col < 0 || geo_grdata1_col < 0 || geo_grdata2_col < 0
  954. || geo_grdata3_col < 0 || geo_grdata4_col < 0 || geo_grdata5_col < 0
  955. || geo_grdata6_col < 0 || geo_grdata7_col < 0 || geo_grdata8_col < 0
  956. || geo_grdata9_col < 0 || geo_subclass_col < 0 || geo_sym_name_col < 0
  957. || geo_refdes_col < 0 )
  958. return -1;
  959. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  960. {
  961. auto row = rows[rownum];
  962. if( row.size() != header.size() )
  963. {
  964. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  965. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  966. continue;
  967. }
  968. auto geo_tag = row[geo_tag_col];
  969. GRAPHIC_DATA gr_data;
  970. gr_data.graphic_dataname = row[geo_name_col];
  971. gr_data.graphic_datanum = row[geo_num_col];
  972. gr_data.graphic_data1 = row[geo_grdata1_col];
  973. gr_data.graphic_data2 = row[geo_grdata2_col];
  974. gr_data.graphic_data3 = row[geo_grdata3_col];
  975. gr_data.graphic_data4 = row[geo_grdata4_col];
  976. gr_data.graphic_data5 = row[geo_grdata5_col];
  977. gr_data.graphic_data6 = row[geo_grdata6_col];
  978. gr_data.graphic_data7 = row[geo_grdata7_col];
  979. gr_data.graphic_data8 = row[geo_grdata8_col];
  980. gr_data.graphic_data9 = row[geo_grdata9_col];
  981. auto geo_refdes = row[geo_refdes_col];
  982. // Grouped graphics are a series of records with the same record ID but incrementing
  983. // Sequence numbers.
  984. int id = -1;
  985. int seq = -1;
  986. int subseq = 0;
  987. if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
  988. {
  989. wxLogError( wxString::Format( _( "Invalid format for record_tag string \"%s\" "
  990. "in Geometric definition row %zu" ),
  991. geo_tag.c_str(), rownum ) );
  992. continue;
  993. }
  994. auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
  995. if( !gr_item )
  996. {
  997. wxLogDebug( wxString::Format( _( "Unhandled graphic item '%s' "
  998. "in Geometric definition row %zu" ),
  999. gr_data.graphic_dataname.c_str(), geo_tag.c_str(), rownum ) );
  1000. continue;
  1001. }
  1002. gr_item->layer = row[geo_subclass_col];
  1003. gr_item->seq = seq;
  1004. gr_item->subseq = subseq;
  1005. if( geo_refdes.empty() )
  1006. {
  1007. if( board_graphics.empty() || board_graphics.back().id != id )
  1008. {
  1009. GEOM_GRAPHIC new_gr;
  1010. new_gr.subclass = row[geo_subclass_col];
  1011. new_gr.refdes = row[geo_refdes_col];
  1012. new_gr.name = row[geo_sym_name_col];
  1013. new_gr.id = id;
  1014. new_gr.elements = std::make_unique<graphic_element>();
  1015. board_graphics.push_back( std::move( new_gr ) );
  1016. }
  1017. GEOM_GRAPHIC& graphic = board_graphics.back();
  1018. graphic.elements->emplace( std::move( gr_item ) );
  1019. }
  1020. else
  1021. {
  1022. auto sym_gr_it = comp_graphics.emplace( geo_refdes,
  1023. std::map<int, GEOM_GRAPHIC>{} );
  1024. auto map_it = sym_gr_it.first->second.emplace( id, GEOM_GRAPHIC{} );
  1025. auto& gr = map_it.first;
  1026. if( map_it.second )
  1027. {
  1028. gr->second.subclass = row[geo_subclass_col];
  1029. gr->second.refdes = row[geo_refdes_col];
  1030. gr->second.name = row[geo_sym_name_col];
  1031. gr->second.id = id;
  1032. gr->second.elements = std::make_unique<graphic_element>();
  1033. }
  1034. auto result = gr->second.elements->emplace( std::move( gr_item ) );
  1035. }
  1036. }
  1037. return rownum - aRow;
  1038. }
  1039. /**
  1040. * A!VIA_X!VIA_Y!PAD_STACK_NAME!NET_NAME!TEST_POINT!
  1041. */
  1042. size_t FABMASTER::processVias( size_t aRow )
  1043. {
  1044. size_t rownum = aRow + 2;
  1045. if( rownum >= rows.size() )
  1046. return -1;
  1047. const auto header = rows[aRow];
  1048. double scale_factor = processScaleFactor( aRow + 1 );
  1049. if( scale_factor <= 0.0 )
  1050. return -1;
  1051. int viax_col = getColFromName( aRow, "VIAX" );
  1052. int viay_col = getColFromName( aRow, "VIAY" );
  1053. int padstack_name_col = getColFromName( aRow, "PADSTACKNAME" );
  1054. int net_name_col = getColFromName( aRow, "NETNAME" );
  1055. int test_point_col = getColFromName( aRow, "TESTPOINT" );
  1056. if( viax_col < 0 || viay_col < 0 || padstack_name_col < 0 || net_name_col < 0
  1057. || test_point_col < 0 )
  1058. return -1;
  1059. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  1060. {
  1061. auto row = rows[rownum];
  1062. if( row.size() != header.size() )
  1063. {
  1064. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  1065. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  1066. continue;
  1067. }
  1068. vias.emplace_back( std::make_unique<FM_VIA>() );
  1069. auto& via = vias.back();
  1070. via->x = KiROUND( readDouble( row[viax_col] ) * scale_factor );
  1071. via->y = -KiROUND( readDouble( row[viay_col] ) * scale_factor );
  1072. via->padstack = row[padstack_name_col];
  1073. via->net = row[net_name_col];
  1074. via->test_point = ( row[test_point_col] == "YES" );
  1075. }
  1076. return rownum - aRow;
  1077. }
  1078. /**
  1079. * A!CLASS!SUBCLASS!GRAPHIC_DATA_NAME!GRAPHIC_DATA_NUMBER!RECORD_TAG!GRAPHIC_DATA_1!GRAPHIC_DATA_2!
  1080. * GRAPHIC_DATA_3!GRAPHIC_DATA_4!GRAPHIC_DATA_5!GRAPHIC_DATA_6!GRAPHIC_DATA_7!GRAPHIC_DATA_8!
  1081. * GRAPHIC_DATA_9!NET_NAME!
  1082. */
  1083. size_t FABMASTER::processTraces( size_t aRow )
  1084. {
  1085. size_t rownum = aRow + 2;
  1086. if( rownum >= rows.size() )
  1087. return -1;
  1088. const auto header = rows[aRow];
  1089. double scale_factor = processScaleFactor( aRow + 1 );
  1090. if( scale_factor <= 0.0 )
  1091. return -1;
  1092. int class_col = getColFromName( aRow, "CLASS" );
  1093. int layer_col = getColFromName( aRow, "SUBCLASS" );
  1094. int grdata_name_col = getColFromName( aRow, "GRAPHICDATANAME" );
  1095. int grdata_num_col = getColFromName( aRow, "GRAPHICDATANUMBER" );
  1096. int tag_col = getColFromName( aRow, "RECORDTAG" );
  1097. int grdata1_col = getColFromName( aRow, "GRAPHICDATA1" );
  1098. int grdata2_col = getColFromName( aRow, "GRAPHICDATA2" );
  1099. int grdata3_col = getColFromName( aRow, "GRAPHICDATA3" );
  1100. int grdata4_col = getColFromName( aRow, "GRAPHICDATA4" );
  1101. int grdata5_col = getColFromName( aRow, "GRAPHICDATA5" );
  1102. int grdata6_col = getColFromName( aRow, "GRAPHICDATA6" );
  1103. int grdata7_col = getColFromName( aRow, "GRAPHICDATA7" );
  1104. int grdata8_col = getColFromName( aRow, "GRAPHICDATA8" );
  1105. int grdata9_col = getColFromName( aRow, "GRAPHICDATA9" );
  1106. int netname_col = getColFromName( aRow, "NETNAME" );
  1107. if( class_col < 0 || layer_col < 0 || grdata_name_col < 0 || grdata_num_col < 0
  1108. || tag_col < 0 || grdata1_col < 0 || grdata2_col < 0 || grdata3_col < 0
  1109. || grdata4_col < 0 || grdata5_col < 0 || grdata6_col < 0 || grdata7_col < 0
  1110. || grdata8_col < 0 || grdata9_col < 0 || netname_col < 0 )
  1111. return -1;
  1112. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  1113. {
  1114. auto row = rows[rownum];
  1115. if( row.size() != header.size() )
  1116. {
  1117. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  1118. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  1119. continue;
  1120. }
  1121. GRAPHIC_DATA gr_data;
  1122. gr_data.graphic_dataname = row[grdata_name_col];
  1123. gr_data.graphic_datanum = row[grdata_num_col];
  1124. gr_data.graphic_data1 = row[grdata1_col];
  1125. gr_data.graphic_data2 = row[grdata2_col];
  1126. gr_data.graphic_data3 = row[grdata3_col];
  1127. gr_data.graphic_data4 = row[grdata4_col];
  1128. gr_data.graphic_data5 = row[grdata5_col];
  1129. gr_data.graphic_data6 = row[grdata6_col];
  1130. gr_data.graphic_data7 = row[grdata7_col];
  1131. gr_data.graphic_data8 = row[grdata8_col];
  1132. gr_data.graphic_data9 = row[grdata9_col];
  1133. auto geo_tag = row[tag_col];
  1134. // Grouped graphics are a series of records with the same record ID but incrementing
  1135. // Sequence numbers.
  1136. int id = -1;
  1137. int seq = -1;
  1138. int subseq = 0;
  1139. if( std::sscanf( geo_tag.c_str(), "%d %d %d", &id, &seq, &subseq ) < 2 )
  1140. {
  1141. wxLogError( wxString::Format( _( "Invalid format for record_tag string \"%s\" "
  1142. "in Traces definition row %zu" ),
  1143. geo_tag.c_str(), rownum ) );
  1144. continue;
  1145. }
  1146. auto gr_item = std::unique_ptr<GRAPHIC_ITEM>( processGraphic( gr_data, scale_factor ) );
  1147. if( !gr_item )
  1148. {
  1149. wxLogDebug( wxString::Format( _( "Unhandled graphic item '%s' "
  1150. "in Traces definition row %zu" ),
  1151. gr_data.graphic_dataname.c_str(), rownum ) );
  1152. continue;
  1153. }
  1154. auto new_trace = std::make_unique<TRACE>();
  1155. new_trace->id = id;
  1156. new_trace->layer = row[layer_col];
  1157. new_trace->netname = row[netname_col];
  1158. new_trace->lclass = row[class_col];
  1159. gr_item->layer = row[layer_col];
  1160. gr_item->seq = seq;
  1161. gr_item->subseq = subseq;
  1162. // Collect the reference designator positions for the footprints later
  1163. if( new_trace->lclass == "REF DES" )
  1164. {
  1165. auto result = refdes.emplace( std::move( new_trace ) );
  1166. auto& ref = *result.first;
  1167. ref->segment.emplace( std::move( gr_item ) );
  1168. }
  1169. else if( gr_item->width == 0 )
  1170. {
  1171. auto result = zones.emplace( std::move( new_trace ) );
  1172. auto& zone = *result.first;
  1173. auto gr_result = zone->segment.emplace( std::move( gr_item ) );
  1174. if( !gr_result.second )
  1175. {
  1176. wxLogError( wxString::Format( _( "Duplicate item for ID %d and sequence %d "
  1177. "in Traces definition row %zu \n" ), id, seq, rownum ) );
  1178. }
  1179. }
  1180. else
  1181. {
  1182. auto result = traces.emplace( std::move( new_trace ) );
  1183. auto& trace = *result.first;
  1184. auto gr_result = trace->segment.emplace( std::move( gr_item ) );
  1185. if( !gr_result.second )
  1186. {
  1187. wxLogError( wxString::Format( _( "Duplicate item for ID %d and sequence %d "
  1188. "in Traces definition row %zu \n" ), id, seq, rownum ) );
  1189. }
  1190. }
  1191. }
  1192. return rownum - aRow;
  1193. }
  1194. FABMASTER::SYMTYPE FABMASTER::parseSymType( const std::string& aSymType )
  1195. {
  1196. if( aSymType == "PACKAGE" )
  1197. return SYMTYPE_PACKAGE;
  1198. else if( aSymType == "DRAFTING")
  1199. return SYMTYPE_DRAFTING;
  1200. else if( aSymType == "MECHANICAL" )
  1201. return SYMTYPE_MECH;
  1202. else if( aSymType == "FORMAT" )
  1203. return SYMTYPE_FORMAT;
  1204. return SYMTYPE_NONE;
  1205. }
  1206. FABMASTER::COMPCLASS FABMASTER::parseCompClass( const std::string& aCmpClass )
  1207. {
  1208. if( aCmpClass == "IO" )
  1209. return COMPCLASS_IO;
  1210. else if( aCmpClass == "IC" )
  1211. return COMPCLASS_IC;
  1212. else if( aCmpClass == "DISCRETE" )
  1213. return COMPCLASS_DISCRETE;
  1214. return COMPCLASS_NONE;
  1215. }
  1216. /**
  1217. * A!REFDES!COMP_CLASS!COMP_PART_NUMBER!COMP_HEIGHT!COMP_DEVICE_LABEL!COMP_INSERTION_CODE!SYM_TYPE!
  1218. * SYM_NAME!SYM_MIRROR!SYM_ROTATE!SYM_X!SYM_Y!COMP_VALUE!COMP_TOL!COMP_VOLTAGE!
  1219. */
  1220. size_t FABMASTER::processFootprints( size_t aRow )
  1221. {
  1222. size_t rownum = aRow + 2;
  1223. if( rownum >= rows.size() )
  1224. return -1;
  1225. const auto header = rows[aRow];
  1226. double scale_factor = processScaleFactor( aRow + 1 );
  1227. if( scale_factor <= 0.0 )
  1228. return -1;
  1229. int refdes_col = getColFromName( aRow, "REFDES" );
  1230. int compclass_col = getColFromName( aRow, "COMPCLASS" );
  1231. int comppartnum_col = getColFromName( aRow, "COMPPARTNUMBER" );
  1232. int compheight_col = getColFromName( aRow, "COMPHEIGHT" );
  1233. int compdevlabelcol = getColFromName( aRow, "COMPDEVICELABEL" );
  1234. int compinscode_col = getColFromName( aRow, "COMPINSERTIONCODE" );
  1235. int symtype_col = getColFromName( aRow, "SYMTYPE" );
  1236. int symname_col = getColFromName( aRow, "SYMNAME" );
  1237. int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
  1238. int symrotate_col = getColFromName( aRow, "SYMROTATE" );
  1239. int symx_col = getColFromName( aRow, "SYMX" );
  1240. int symy_col = getColFromName( aRow, "SYMY" );
  1241. int compvalue_col = getColFromName( aRow, "COMPVALUE" );
  1242. int comptol_col = getColFromName( aRow, "COMPTOL" );
  1243. int compvolt_col = getColFromName( aRow, "COMPVOLTAGE" );
  1244. if( refdes_col < 0 || compclass_col < 0 || comppartnum_col < 0 || compheight_col < 0
  1245. || compdevlabelcol < 0 || compinscode_col < 0 || symtype_col < 0 || symname_col < 0
  1246. || symmirror_col < 0 || symrotate_col < 0 || symx_col < 0 || symy_col < 0
  1247. || compvalue_col < 0 || comptol_col < 0 || compvolt_col < 0 )
  1248. return -1;
  1249. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  1250. {
  1251. auto row = rows[rownum];
  1252. if( row.size() != header.size() )
  1253. {
  1254. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  1255. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  1256. continue;
  1257. }
  1258. auto cmp = std::make_unique<COMPONENT>();
  1259. cmp->refdes = row[refdes_col];
  1260. cmp->cclass = parseCompClass( row[compclass_col] );
  1261. cmp->pn = row[comppartnum_col];
  1262. cmp->height = row[compheight_col];
  1263. cmp->dev_label = row[compdevlabelcol];
  1264. cmp->insert_code = row[compinscode_col];
  1265. cmp->type = parseSymType( row[symtype_col] );
  1266. cmp->name = row[symname_col];
  1267. cmp->mirror = ( row[symmirror_col] == "YES" );
  1268. cmp->rotate = readDouble( row[symrotate_col] );
  1269. cmp->x = KiROUND( readDouble( row[symx_col] ) * scale_factor );
  1270. cmp->y = -KiROUND( readDouble( row[symy_col] ) * scale_factor );
  1271. cmp->value = row[compvalue_col];
  1272. cmp->tol = row[comptol_col];
  1273. cmp->voltage = row[compvolt_col];
  1274. auto vec = components.find( cmp->refdes );
  1275. if( vec == components.end() )
  1276. {
  1277. auto retval = components.insert( std::make_pair( cmp->refdes, std::vector<std::unique_ptr<COMPONENT>>{} ) );
  1278. vec = retval.first;
  1279. }
  1280. vec->second.push_back( std::move( cmp ) );
  1281. }
  1282. return rownum - aRow;
  1283. }
  1284. /**
  1285. * A!SYM_NAME!SYM_MIRROR!PIN_NAME!PIN_NUMBER!PIN_X!PIN_Y!PAD_STACK_NAME!REFDES!PIN_ROTATION!TEST_POINT!
  1286. */
  1287. size_t FABMASTER::processPins( size_t aRow )
  1288. {
  1289. size_t rownum = aRow + 2;
  1290. if( rownum >= rows.size() )
  1291. return -1;
  1292. const auto header = rows[aRow];
  1293. double scale_factor = processScaleFactor( aRow + 1 );
  1294. if( scale_factor <= 0.0 )
  1295. return -1;
  1296. int symname_col = getColFromName( aRow, "SYMNAME" );
  1297. int symmirror_col = getColFromName( aRow, "SYMMIRROR" );
  1298. int pinname_col = getColFromName( aRow, "PINNAME" );
  1299. int pinnum_col = getColFromName( aRow, "PINNUMBER" );
  1300. int pinx_col = getColFromName( aRow, "PINX" );
  1301. int piny_col = getColFromName( aRow, "PINY" );
  1302. int padstack_col = getColFromName( aRow, "PADSTACKNAME" );
  1303. int refdes_col = getColFromName( aRow, "REFDES" );
  1304. int pinrot_col = getColFromName( aRow, "PINROTATION" );
  1305. int testpoint_col = getColFromName( aRow, "TESTPOINT" );
  1306. if( symname_col < 0 ||symmirror_col < 0 || pinname_col < 0 || pinnum_col < 0 || pinx_col < 0
  1307. || piny_col < 0 || padstack_col < 0 || refdes_col < 0 || pinrot_col < 0
  1308. || testpoint_col < 0 )
  1309. return -1;
  1310. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  1311. {
  1312. auto row = rows[rownum];
  1313. if( row.size() != header.size() )
  1314. {
  1315. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  1316. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  1317. continue;
  1318. }
  1319. auto pin = std::make_unique<PIN>();
  1320. pin->name = row[symname_col];
  1321. pin->mirror = ( row[symmirror_col] == "YES" );
  1322. pin->pin_name = row[pinname_col];
  1323. pin->pin_number = row[pinnum_col];
  1324. pin->pin_x = KiROUND( readDouble( row[pinx_col] ) * scale_factor );
  1325. pin->pin_y = -KiROUND( readDouble( row[piny_col] ) * scale_factor );
  1326. pin->padstack = row[padstack_col];
  1327. pin->refdes = row[refdes_col];
  1328. pin->rotation = readDouble( row[pinrot_col] );
  1329. auto map_it = pins.find( pin->refdes );
  1330. if( map_it == pins.end() )
  1331. {
  1332. auto retval = pins.insert( std::make_pair( pin->refdes, std::set<std::unique_ptr<PIN>, PIN::BY_NUM>{} ) );
  1333. map_it = retval.first;
  1334. }
  1335. map_it->second.insert( std::move( pin ) );
  1336. }
  1337. return rownum - aRow;
  1338. }
  1339. /**
  1340. * A!NET_NAME!REFDES!PIN_NUMBER!PIN_NAME!PIN_GROUND!PIN_POWER!
  1341. */
  1342. size_t FABMASTER::processNets( size_t aRow )
  1343. {
  1344. size_t rownum = aRow + 2;
  1345. if( rownum >= rows.size() )
  1346. return -1;
  1347. const auto header = rows[aRow];
  1348. double scale_factor = processScaleFactor( aRow + 1 );
  1349. if( scale_factor <= 0.0 )
  1350. return -1;
  1351. int netname_col = getColFromName( aRow, "NETNAME" );
  1352. int refdes_col = getColFromName( aRow, "REFDES" );
  1353. int pinnum_col = getColFromName( aRow, "PINNUMBER" );
  1354. int pinname_col = getColFromName( aRow, "PINNAME" );
  1355. int pingnd_col = getColFromName( aRow, "PINGROUND" );
  1356. int pinpwr_col = getColFromName( aRow, "PINPOWER" );
  1357. if( netname_col < 0 || refdes_col < 0 || pinnum_col < 0 || pinname_col < 0 || pingnd_col < 0
  1358. || pinpwr_col < 0 )
  1359. return -1;
  1360. for( ; rownum < rows.size() && rows[rownum].size() > 0 && rows[rownum][0] == "S"; ++rownum )
  1361. {
  1362. auto row = rows[rownum];
  1363. if( row.size() != header.size() )
  1364. {
  1365. wxLogError( wxString::Format( _( "Invalid row size in row %zu. "
  1366. "Expecting %zu elements but found %zu" ), rownum, header.size(), row.size() ) );
  1367. continue;
  1368. }
  1369. NETNAME new_net;
  1370. new_net.name = row[netname_col];
  1371. new_net.refdes = row[refdes_col];
  1372. new_net.pin_num = row[pinnum_col];
  1373. new_net.pin_name = row[pinname_col];
  1374. new_net.pin_gnd = ( row[pingnd_col] == "YES" );
  1375. new_net.pin_pwr = ( row[pinpwr_col] == "YES" );
  1376. pin_nets.emplace( std::make_pair( new_net.refdes, new_net.pin_num ), new_net );
  1377. netnames.insert( row[netname_col] );
  1378. }
  1379. return rownum - aRow;
  1380. }
  1381. bool FABMASTER::Process()
  1382. {
  1383. for( size_t i = 0; i < rows.size(); )
  1384. {
  1385. auto type = detectType( i );
  1386. switch( type )
  1387. {
  1388. case EXTRACT_PADSTACKS:
  1389. {
  1390. /// We extract the basic layers from the padstacks first as this is the only place
  1391. /// the stackup is kept in the basic fabmaster export
  1392. processPadStackLayers( i );
  1393. assignLayers();
  1394. int retval = processPadStacks( i );
  1395. i += std::max( retval, 1 );
  1396. break;
  1397. }
  1398. case EXTRACT_FULL_LAYERS:
  1399. {
  1400. int retval = processLayers( i );
  1401. i += std::max( retval, 1 );
  1402. break;
  1403. }
  1404. case EXTRACT_BASIC_LAYERS:
  1405. {
  1406. int retval = processSimpleLayers( i );
  1407. i += std::max( retval, 1 );
  1408. break;
  1409. }
  1410. case EXTRACT_VIAS:
  1411. {
  1412. int retval = processVias( i );
  1413. i += std::max( retval, 1 );
  1414. break;
  1415. }
  1416. case EXTRACT_TRACES:
  1417. {
  1418. int retval = processTraces( i );
  1419. i += std::max( retval, 1 );
  1420. break;
  1421. }
  1422. case EXTRACT_REFDES:
  1423. {
  1424. int retval = processFootprints( i );
  1425. i += std::max( retval, 1 );
  1426. break;
  1427. }
  1428. case EXTRACT_NETS:
  1429. {
  1430. int retval = processNets( i );
  1431. i += std::max( retval, 1 );
  1432. break;
  1433. }
  1434. case EXTRACT_GRAPHICS:
  1435. {
  1436. int retval = processGeometry( i );
  1437. i += std::max( retval, 1 );
  1438. break;
  1439. }
  1440. case EXTRACT_PINS:
  1441. {
  1442. int retval = processPins( i );
  1443. i += std::max( retval, 1 );
  1444. break;
  1445. }
  1446. case EXTRACT_PAD_SHAPES:
  1447. {
  1448. int retval = processCustomPads( i );
  1449. i += std::max( retval, 1 );
  1450. break;
  1451. }
  1452. default:
  1453. ++i;
  1454. break;
  1455. }
  1456. }
  1457. return true;
  1458. }
  1459. bool FABMASTER::loadZones( BOARD* aBoard )
  1460. {
  1461. for( auto& zone : zones )
  1462. {
  1463. if( IsCopperLayer( getLayer( zone->layer ) ) || zone->layer == "ALL" )
  1464. loadZone( aBoard, zone );
  1465. else
  1466. {
  1467. if( zone->layer == "OUTLINE" || zone->layer == "DESIGN_OUTLINE" )
  1468. {
  1469. loadOutline( aBoard, zone );
  1470. }
  1471. else
  1472. {
  1473. loadPolygon( aBoard, zone );
  1474. }
  1475. }
  1476. }
  1477. /**
  1478. * Zones in FABMASTER come in two varieties:
  1479. * - Outlines with no net code attached
  1480. * - Filled areas with net code attached
  1481. *
  1482. * In pcbnew, we want the outline with net code attached. To determine which
  1483. * outline should have which netcode, we look for overlapping areas. Each unnetted zone
  1484. * outline will be assigned the netcode that with the most hits on the edge of their
  1485. * outline.
  1486. */
  1487. std::set<ZONE*> zones_to_delete;
  1488. for( auto zone : aBoard->Zones() )
  1489. {
  1490. /// Remove the filled areas in favor of the outlines
  1491. if( zone->GetNetCode() > 0 )
  1492. {
  1493. zones_to_delete.insert( zone );
  1494. }
  1495. }
  1496. for( auto zone1 : aBoard->Zones() )
  1497. {
  1498. /// Zone1 will be the destination zone for the new net
  1499. if( zone1->GetNetCode() > 0 )
  1500. continue;
  1501. SHAPE_LINE_CHAIN& outline1 = zone1->Outline()->Outline( 0 );
  1502. std::vector<size_t> overlaps( aBoard->GetNetInfo().GetNetCount() + 1, 0 );
  1503. std::vector<std::vector<ZONE*>> possible_deletions( overlaps.size() );
  1504. for( auto zone2 : aBoard->Zones() )
  1505. {
  1506. if( zone2->GetNetCode() <= 0 )
  1507. continue;
  1508. SHAPE_LINE_CHAIN& outline2 = zone2->Outline()->Outline( 0 );
  1509. if( zone1->GetLayer() != zone2->GetLayer() )
  1510. continue;
  1511. if( !outline1.BBox().Intersects( outline2.BBox() ) )
  1512. continue;
  1513. for( auto& pt1 : outline1.CPoints() )
  1514. {
  1515. /// We're looking for the netcode with the most overlaps to the un-netted zone
  1516. if( outline2.PointOnEdge( pt1, 1 ) )
  1517. overlaps[ zone2->GetNetCode() ]++;
  1518. }
  1519. for( auto& pt2 : outline2.CPoints() )
  1520. {
  1521. /// The overlap between outline1 and outline2 isn't perfect, so look for overlaps
  1522. /// in both directions
  1523. if( outline1.PointOnEdge( pt2, 1 ) )
  1524. overlaps[ zone2->GetNetCode() ]++;
  1525. }
  1526. }
  1527. size_t max_net = 0;
  1528. size_t max_net_id = 0;
  1529. for( size_t el = 1; el < overlaps.size(); ++el )
  1530. {
  1531. if( overlaps[el] > max_net )
  1532. {
  1533. max_net = overlaps[el];
  1534. max_net_id = el;
  1535. }
  1536. }
  1537. if( max_net > 0 )
  1538. zone1->SetNetCode( max_net_id );
  1539. }
  1540. for( auto zone : zones_to_delete )
  1541. {
  1542. aBoard->Remove( zone );
  1543. delete zone;
  1544. }
  1545. return true;
  1546. }
  1547. bool FABMASTER::loadFootprints( BOARD* aBoard )
  1548. {
  1549. const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
  1550. const auto& ds = aBoard->GetDesignSettings();
  1551. for( auto& mod : components )
  1552. {
  1553. bool has_multiple = mod.second.size() > 1;
  1554. for( int i = 0; i < mod.second.size(); ++i )
  1555. {
  1556. auto& src = mod.second[i];
  1557. FOOTPRINT* fp = new FOOTPRINT( aBoard );
  1558. wxString mod_ref = src->name;
  1559. wxString lib_ref = m_filename.GetName();
  1560. if( has_multiple )
  1561. mod_ref.Append( wxString::Format( "_%d", i ) );
  1562. ReplaceIllegalFileNameChars( lib_ref, '_' );
  1563. ReplaceIllegalFileNameChars( mod_ref, '_' );
  1564. wxString key = !lib_ref.empty() ? lib_ref + ":" + mod_ref : mod_ref;
  1565. LIB_ID fpID;
  1566. fpID.Parse( key, true );
  1567. fp->SetFPID( fpID );
  1568. fp->SetPosition( wxPoint( src->x, src->y ) );
  1569. fp->SetOrientationDegrees( -src->rotate );
  1570. // KiCad netlisting requires parts to have non-digit + digit annotation.
  1571. // If the reference begins with a number, we prepend 'UNK' (unknown) for the source designator
  1572. wxString reference = src->refdes;
  1573. if( !std::isalpha( src->refdes[0] ) )
  1574. reference.Prepend( "UNK" );
  1575. fp->SetReference( reference );
  1576. fp->SetValue( src->value );
  1577. fp->Value().SetLayer( F_Fab );
  1578. fp->Value().SetVisible( false );
  1579. for( auto& ref : refdes )
  1580. {
  1581. const GRAPHIC_TEXT *lsrc =
  1582. static_cast<const GRAPHIC_TEXT*>( ( *( ref->segment.begin() ) ).get() );
  1583. if( lsrc->text == src->refdes )
  1584. {
  1585. FP_TEXT* txt = nullptr;
  1586. PCB_LAYER_ID layer = getLayer( ref->layer );
  1587. if( !IsPcbLayer( layer ) )
  1588. {
  1589. printf("The layer %s is not mapped?\n", ref->layer.c_str() );
  1590. continue;
  1591. }
  1592. if( layer == F_SilkS || layer == B_SilkS )
  1593. txt = &( fp->Reference() );
  1594. else
  1595. txt = new FP_TEXT( fp );
  1596. if( src->mirror )
  1597. {
  1598. txt->SetLayer( FlipLayer( layer ) );
  1599. txt->SetTextPos( wxPoint( lsrc->start_x, 2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
  1600. }
  1601. else
  1602. {
  1603. txt->SetLayer( layer );
  1604. txt->SetTextPos( wxPoint( lsrc->start_x, lsrc->start_y - lsrc->height / 2 ) );
  1605. }
  1606. txt->SetText( lsrc->text );
  1607. txt->SetItalic( lsrc->ital );
  1608. txt->SetTextThickness( lsrc->thickness );
  1609. txt->SetTextHeight( lsrc->height );
  1610. txt->SetTextWidth( lsrc->width );
  1611. txt->SetHorizJustify( lsrc->orient );
  1612. txt->SetLocalCoord();
  1613. if( txt != &fp->Reference() )
  1614. fp->Add( txt, ADD_MODE::APPEND );
  1615. }
  1616. }
  1617. /// Always set the module to the top and flip later if needed
  1618. /// When flipping later, we get the full coordinate transform for free
  1619. fp->SetLayer( F_Cu );
  1620. auto gr_it = comp_graphics.find( src->refdes );
  1621. if( gr_it == comp_graphics.end() )
  1622. {
  1623. continue;
  1624. //TODO: Error
  1625. }
  1626. for( auto& gr_ref : gr_it->second )
  1627. {
  1628. auto& graphic = gr_ref.second;
  1629. for( auto& seg : *graphic.elements )
  1630. {
  1631. PCB_LAYER_ID layer = Dwgs_User;
  1632. if( IsPcbLayer( getLayer( seg->layer ) ) )
  1633. layer = getLayer( seg->layer );
  1634. switch( seg->shape )
  1635. {
  1636. case GR_SHAPE_LINE:
  1637. {
  1638. const GRAPHIC_LINE* lsrc = static_cast<const GRAPHIC_LINE*>( seg.get() );
  1639. FP_SHAPE* line = new FP_SHAPE( fp, PCB_SHAPE_TYPE::SEGMENT );
  1640. if( src->mirror )
  1641. {
  1642. line->SetLayer( FlipLayer( layer ) );
  1643. line->SetStart( wxPoint( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
  1644. line->SetEnd( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
  1645. }
  1646. else
  1647. {
  1648. line->SetLayer( layer );
  1649. line->SetStart( wxPoint( lsrc->start_x, lsrc->start_y ) );
  1650. line->SetEnd( wxPoint( lsrc->end_x, lsrc->end_y ) );
  1651. }
  1652. line->SetWidth( lsrc->width );
  1653. line->SetLocalCoord();
  1654. if( lsrc->width == 0 )
  1655. line->SetWidth( ds.GetLineThickness( line->GetLayer() ) );
  1656. fp->Add( line, ADD_MODE::APPEND );
  1657. break;
  1658. }
  1659. case GR_SHAPE_ARC:
  1660. {
  1661. const GRAPHIC_ARC* lsrc = static_cast<const GRAPHIC_ARC*>( seg.get() );
  1662. FP_SHAPE* arc = new FP_SHAPE( fp, PCB_SHAPE_TYPE::ARC );
  1663. if( src->mirror )
  1664. {
  1665. arc->SetLayer( FlipLayer( layer ) );
  1666. arc->SetCenter( wxPoint( lsrc->center_x, 2 * src->y - lsrc->center_y ) );
  1667. arc->SetArcStart( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
  1668. arc->SetAngle( lsrc->result.GetCentralAngle() * 10.0 );
  1669. }
  1670. else
  1671. {
  1672. arc->SetLayer( layer );
  1673. arc->SetCenter( wxPoint( lsrc->center_x, lsrc->center_y ) );
  1674. arc->SetArcStart( wxPoint( lsrc->end_x, lsrc->end_y ) );
  1675. arc->SetAngle( -lsrc->result.GetCentralAngle() * 10.0 );
  1676. }
  1677. arc->SetWidth( lsrc->width );
  1678. arc->SetLocalCoord();
  1679. if( lsrc->width == 0 )
  1680. arc->SetWidth( ds.GetLineThickness( arc->GetLayer() ) );
  1681. fp->Add( arc, ADD_MODE::APPEND );
  1682. break;
  1683. }
  1684. case GR_SHAPE_RECTANGLE:
  1685. {
  1686. const GRAPHIC_RECTANGLE *lsrc =
  1687. static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
  1688. FP_SHAPE* rect = new FP_SHAPE( fp, PCB_SHAPE_TYPE::RECT );
  1689. if( src->mirror )
  1690. {
  1691. rect->SetLayer( FlipLayer( layer ) );
  1692. rect->SetStart( wxPoint( lsrc->start_x, 2 * src->y - lsrc->start_y ) );
  1693. rect->SetEnd( wxPoint( lsrc->end_x, 2 * src->y - lsrc->end_y ) );
  1694. }
  1695. else
  1696. {
  1697. rect->SetLayer( layer );
  1698. rect->SetStart( wxPoint( lsrc->start_x, lsrc->start_y ) );
  1699. rect->SetEnd( wxPoint( lsrc->end_x, lsrc->end_y ) );
  1700. }
  1701. rect->SetWidth( ds.GetLineThickness( rect->GetLayer() ) );
  1702. rect->SetLocalCoord();
  1703. fp->Add( rect, ADD_MODE::APPEND );
  1704. break;
  1705. }
  1706. case GR_SHAPE_TEXT:
  1707. {
  1708. const GRAPHIC_TEXT *lsrc =
  1709. static_cast<const GRAPHIC_TEXT*>( seg.get() );
  1710. FP_TEXT* txt = new FP_TEXT( fp );
  1711. if( src->mirror )
  1712. {
  1713. txt->SetLayer( FlipLayer( layer ) );
  1714. txt->SetTextPos( wxPoint( lsrc->start_x, 2 * src->y - ( lsrc->start_y - lsrc->height / 2 ) ) );
  1715. }
  1716. else
  1717. {
  1718. txt->SetLayer( layer );
  1719. txt->SetTextPos( wxPoint( lsrc->start_x, lsrc->start_y - lsrc->height / 2 ) );
  1720. }
  1721. txt->SetText( lsrc->text );
  1722. txt->SetItalic( lsrc->ital );
  1723. txt->SetTextThickness( lsrc->thickness );
  1724. txt->SetTextHeight( lsrc->height );
  1725. txt->SetTextWidth( lsrc->width );
  1726. txt->SetHorizJustify( lsrc->orient );
  1727. txt->SetLocalCoord();
  1728. // FABMASTER doesn't have visibility flags but layers that are not silk should be hidden
  1729. // by default to prevent clutter.
  1730. if( txt->GetLayer() != F_SilkS && txt->GetLayer() != B_SilkS )
  1731. txt->SetVisible( false );
  1732. fp->Add( txt, ADD_MODE::APPEND );
  1733. break;
  1734. }
  1735. default:
  1736. continue;
  1737. }
  1738. }
  1739. }
  1740. auto pin_it = pins.find( src->refdes );
  1741. if( pin_it != pins.end() )
  1742. {
  1743. for( auto& pin : pin_it->second )
  1744. {
  1745. auto pin_net_it = pin_nets.find( std::make_pair( pin->refdes, pin->pin_number ) );
  1746. auto padstack = pads.find( pin->padstack );
  1747. std::string netname = "";
  1748. if( pin_net_it != pin_nets.end() )
  1749. netname = pin_net_it->second.name;
  1750. auto net_it = netinfo.find( netname );
  1751. PAD* newpad = new PAD( fp );
  1752. if( net_it != netinfo.end() )
  1753. newpad->SetNet( net_it->second );
  1754. else
  1755. newpad->SetNetCode( 0 );
  1756. newpad->SetX( pin->pin_x );
  1757. if( src->mirror )
  1758. newpad->SetY( 2 * src->y - pin->pin_y );
  1759. else
  1760. newpad->SetY( pin->pin_y );
  1761. newpad->SetName( pin->pin_number );
  1762. if( padstack == pads.end() )
  1763. {
  1764. ///TODO:Warning
  1765. delete newpad;
  1766. continue;
  1767. }
  1768. else
  1769. {
  1770. auto& pad = padstack->second;
  1771. newpad->SetShape( pad.shape );
  1772. if( pad.shape == PAD_SHAPE_CUSTOM )
  1773. {
  1774. // Choose the smaller dimension to ensure the base pad
  1775. // is fully hidden by the custom pad
  1776. int pad_size = std::min( pad.width, pad.height );
  1777. newpad->SetSize( wxSize( pad_size / 2, pad_size / 2 ) );
  1778. std::string custom_name = pad.custom_name + "_" + pin->refdes + "_" + pin->pin_number;
  1779. auto custom_it = pad_shapes.find( custom_name );
  1780. if( custom_it != pad_shapes.end() )
  1781. {
  1782. SHAPE_POLY_SET poly_outline;
  1783. int last_subseq = 0;
  1784. int hole_idx = -1;
  1785. poly_outline.NewOutline();
  1786. // Custom pad shapes have a group of elements
  1787. // that are a list of graphical polygons
  1788. for( const auto& el : (*custom_it).second.elements )
  1789. {
  1790. // For now, we are only processing the custom pad for the top layer
  1791. // TODO: Use full padstacks when implementing in KiCad
  1792. PCB_LAYER_ID primary_layer = src->mirror ? B_Cu : F_Cu;
  1793. if( getLayer( ( *( el.second.begin() ) )->layer ) != primary_layer )
  1794. continue;
  1795. for( const auto& seg : el.second )
  1796. {
  1797. if( seg->subseq > 0 || seg->subseq != last_subseq )
  1798. {
  1799. poly_outline.Polygon(0).back().SetClosed( true );
  1800. hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
  1801. }
  1802. if( seg->shape == GR_SHAPE_LINE )
  1803. {
  1804. const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
  1805. if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
  1806. poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
  1807. poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
  1808. }
  1809. else if( seg->shape == GR_SHAPE_ARC )
  1810. {
  1811. const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
  1812. SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
  1813. chain.Append( src->result );
  1814. }
  1815. }
  1816. }
  1817. if( poly_outline.OutlineCount() < 1
  1818. || poly_outline.Outline( 0 ).PointCount() < 3 )
  1819. {
  1820. wxLogError( wxString::Format(
  1821. _( "Invalid custom pad named '%s'. Replacing with circular pad." ),
  1822. custom_name.c_str() ) );
  1823. newpad->SetShape( PAD_SHAPE_CIRCLE );
  1824. }
  1825. else
  1826. {
  1827. poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
  1828. poly_outline.Move( -newpad->GetPosition() );
  1829. if( src->mirror )
  1830. {
  1831. poly_outline.Mirror( false, true, VECTOR2I( 0, ( pin->pin_y - src->y ) ) );
  1832. poly_outline.Rotate( ( -src->rotate + pin->rotation ) * M_PI / 180.0 );
  1833. }
  1834. else
  1835. {
  1836. poly_outline.Rotate( ( src->rotate - pin->rotation ) * M_PI / 180.0 );
  1837. }
  1838. newpad->AddPrimitivePoly( poly_outline, 0, true );
  1839. }
  1840. SHAPE_POLY_SET mergedPolygon;
  1841. newpad->MergePrimitivesAsPolygon( &mergedPolygon, UNDEFINED_LAYER );
  1842. if( mergedPolygon.OutlineCount() > 1 )
  1843. {
  1844. wxLogError( wxString::Format(
  1845. _( "Invalid custom pad named '%s'. Replacing with circular pad." ),
  1846. custom_name.c_str() ) );
  1847. newpad->SetShape( PAD_SHAPE_CIRCLE );
  1848. }
  1849. }
  1850. else
  1851. {
  1852. wxLogError( wxString::Format( _( "Could not find custom pad named %s" ),
  1853. custom_name.c_str() ) );
  1854. }
  1855. }
  1856. else
  1857. newpad->SetSize( wxSize( pad.width, pad.height ) );
  1858. if( pad.drill )
  1859. {
  1860. if( pad.plated )
  1861. {
  1862. newpad->SetAttribute( PAD_ATTR_T::PAD_ATTRIB_PTH );
  1863. newpad->SetLayerSet( PAD::PTHMask() );
  1864. }
  1865. else
  1866. {
  1867. newpad->SetAttribute( PAD_ATTR_T::PAD_ATTRIB_NPTH );
  1868. newpad->SetLayerSet( PAD::UnplatedHoleMask() );
  1869. }
  1870. if( pad.drill_size_x == pad.drill_size_y )
  1871. newpad->SetDrillShape( PAD_DRILL_SHAPE_CIRCLE );
  1872. else
  1873. newpad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG );
  1874. newpad->SetDrillSize( wxSize( pad.drill_size_x, pad.drill_size_y ) );
  1875. }
  1876. else
  1877. {
  1878. newpad->SetAttribute( PAD_ATTR_T::PAD_ATTRIB_SMD );
  1879. if( pad.top )
  1880. newpad->SetLayerSet( PAD::SMDMask() );
  1881. else if( pad.bottom )
  1882. newpad->SetLayerSet( FlipLayerMask( PAD::SMDMask() ) );
  1883. }
  1884. }
  1885. newpad->SetLocalCoord();
  1886. if( src->mirror )
  1887. newpad->SetOrientation( ( -src->rotate + pin->rotation ) * 10.0 );
  1888. else
  1889. newpad->SetOrientation( ( src->rotate - pin->rotation ) * 10.0 );
  1890. fp->Add( newpad, ADD_MODE::APPEND );
  1891. }
  1892. }
  1893. if( src->mirror )
  1894. {
  1895. fp->SetOrientationDegrees( 180.0 - src->rotate );
  1896. fp->Flip( fp->GetPosition(), true );
  1897. }
  1898. aBoard->Add( fp, ADD_MODE::APPEND );
  1899. }
  1900. }
  1901. return true;
  1902. }
  1903. bool FABMASTER::loadLayers( BOARD* aBoard )
  1904. {
  1905. LSET layer_set;
  1906. /// The basic layers that get enabled for normal boards
  1907. layer_set |= LSET::AllTechMask() | LSET::UserMask();
  1908. for( auto& layer : layers )
  1909. {
  1910. if( layer.second.layerid >= PCBNEW_LAYER_ID_START )
  1911. layer_set.set( layer.second.layerid );
  1912. }
  1913. aBoard->SetEnabledLayers( layer_set );
  1914. for( auto& layer : layers )
  1915. {
  1916. if( layer.second.conductive )
  1917. {
  1918. aBoard->SetLayerName( static_cast<PCB_LAYER_ID>( layer.second.layerid ),
  1919. layer.second.name );
  1920. }
  1921. }
  1922. return true;
  1923. }
  1924. bool FABMASTER::loadVias( BOARD* aBoard )
  1925. {
  1926. const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
  1927. const auto& ds = aBoard->GetDesignSettings();
  1928. for( auto& via : vias )
  1929. {
  1930. auto net_it = netinfo.find( via->net );
  1931. auto padstack = pads.find( via->padstack );
  1932. VIA* new_via = new VIA( aBoard );
  1933. new_via->SetPosition( wxPoint( via->x, via->y ) );
  1934. if( net_it != netinfo.end() )
  1935. new_via->SetNet( net_it->second );
  1936. if( padstack == pads.end() )
  1937. {
  1938. new_via->SetDrillDefault();
  1939. if( !ds.m_ViasDimensionsList.empty() )
  1940. {
  1941. new_via->SetWidth( ds.m_ViasDimensionsList[0].m_Diameter );
  1942. new_via->SetDrill( ds.m_ViasDimensionsList[0].m_Drill );
  1943. }
  1944. else
  1945. {
  1946. new_via->SetDrillDefault();
  1947. new_via->SetWidth( ds.m_ViasMinSize );
  1948. }
  1949. }
  1950. else
  1951. {
  1952. new_via->SetDrill( padstack->second.drill_size_x );
  1953. new_via->SetWidth( padstack->second.width );
  1954. }
  1955. aBoard->Add( new_via, ADD_MODE::APPEND );
  1956. }
  1957. return true;
  1958. }
  1959. bool FABMASTER::loadNets( BOARD* aBoard )
  1960. {
  1961. for( auto& net : netnames )
  1962. {
  1963. NETINFO_ITEM *newnet = new NETINFO_ITEM( aBoard, net );
  1964. aBoard->Add( newnet, ADD_MODE::APPEND );
  1965. }
  1966. return true;
  1967. }
  1968. bool FABMASTER::loadEtch( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
  1969. {
  1970. const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
  1971. auto net_it = netinfo.find( aLine->netname );
  1972. int last_subseq = 0;
  1973. ZONE* new_zone = nullptr;
  1974. for( const auto& seg : aLine->segment )
  1975. {
  1976. PCB_LAYER_ID layer = getLayer( seg->layer );
  1977. if( IsCopperLayer( layer ) )
  1978. {
  1979. if( seg->shape == GR_SHAPE_LINE )
  1980. {
  1981. const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
  1982. TRACK* trk = new TRACK( aBoard );
  1983. trk->SetLayer( layer );
  1984. trk->SetStart( wxPoint( src->start_x, src->start_y ) );
  1985. trk->SetEnd( wxPoint( src->end_x, src->end_y ) );
  1986. trk->SetWidth( src->width );
  1987. if( net_it != netinfo.end() )
  1988. trk->SetNet( net_it->second );
  1989. aBoard->Add( trk, ADD_MODE::APPEND );
  1990. }
  1991. else if( seg->shape == GR_SHAPE_ARC )
  1992. {
  1993. const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
  1994. ARC* trk = new ARC( aBoard, &src->result );
  1995. trk->SetLayer( layer );
  1996. trk->SetWidth( src->width );
  1997. if( net_it != netinfo.end() )
  1998. trk->SetNet( net_it->second );
  1999. aBoard->Add( trk, ADD_MODE::APPEND );
  2000. }
  2001. }
  2002. else
  2003. {
  2004. wxLogError( wxString::Format( _( "Expecting etch data to be on copper layer. "
  2005. "Row found on layer '%s'" ), seg->layer.c_str() ) );
  2006. }
  2007. }
  2008. return true;
  2009. }
  2010. SHAPE_POLY_SET FABMASTER::loadShapePolySet( const graphic_element& aElement )
  2011. {
  2012. SHAPE_POLY_SET poly_outline;
  2013. int last_subseq = 0;
  2014. int hole_idx = -1;
  2015. poly_outline.NewOutline();
  2016. for( const auto& seg : aElement )
  2017. {
  2018. if( seg->subseq > 0 || seg->subseq != last_subseq )
  2019. hole_idx = poly_outline.AddHole( SHAPE_LINE_CHAIN{} );
  2020. if( seg->shape == GR_SHAPE_LINE )
  2021. {
  2022. const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
  2023. if( poly_outline.VertexCount( 0, hole_idx ) == 0 )
  2024. poly_outline.Append( src->start_x, src->start_y, 0, hole_idx );
  2025. poly_outline.Append( src->end_x, src->end_y, 0, hole_idx );
  2026. }
  2027. else if( seg->shape == GR_SHAPE_ARC )
  2028. {
  2029. const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
  2030. SHAPE_LINE_CHAIN& chain = poly_outline.Hole( 0, hole_idx );
  2031. chain.Append( src->result );
  2032. }
  2033. }
  2034. poly_outline.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
  2035. return poly_outline;
  2036. }
  2037. bool FABMASTER::loadPolygon( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
  2038. {
  2039. if( aLine->segment.size() < 3 )
  2040. return false;
  2041. PCB_LAYER_ID layer = Cmts_User;
  2042. auto new_layer = getLayer( aLine->layer );
  2043. if( IsPcbLayer( new_layer ) )
  2044. layer = new_layer;
  2045. SHAPE_POLY_SET poly_outline = loadShapePolySet( aLine->segment );
  2046. if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
  2047. return false;
  2048. PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
  2049. new_poly->SetShape( PCB_SHAPE_TYPE::POLYGON );
  2050. new_poly->SetLayer( layer );
  2051. // Polygons on the silk layer are filled but other layers are not/fill doesn't make sense
  2052. if( layer == F_SilkS || layer == B_SilkS )
  2053. {
  2054. new_poly->SetFilled( true );
  2055. new_poly->SetWidth( 0 );
  2056. }
  2057. else
  2058. {
  2059. new_poly->SetWidth( ( *( aLine->segment.begin() ) )->width );
  2060. if( new_poly->GetWidth() == 0 )
  2061. new_poly->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
  2062. }
  2063. new_poly->SetPolyShape( poly_outline );
  2064. aBoard->Add( new_poly, ADD_MODE::APPEND );
  2065. return true;
  2066. }
  2067. bool FABMASTER::loadZone( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
  2068. {
  2069. if( aLine->segment.size() < 3 )
  2070. return false;
  2071. int last_subseq = 0;
  2072. int hole_idx = -1;
  2073. SHAPE_POLY_SET* zone_outline = nullptr;
  2074. ZONE* zone = nullptr;
  2075. const NETNAMES_MAP& netinfo = aBoard->GetNetInfo().NetsByName();
  2076. auto net_it = netinfo.find( aLine->netname );
  2077. PCB_LAYER_ID layer = Cmts_User;
  2078. auto new_layer = getLayer( aLine->layer );
  2079. if( IsPcbLayer( new_layer ) )
  2080. layer = new_layer;
  2081. zone = new ZONE( aBoard );
  2082. zone_outline = new SHAPE_POLY_SET;
  2083. if( net_it != netinfo.end() )
  2084. zone->SetNet( net_it->second );
  2085. if( aLine->layer == "ALL" )
  2086. zone->SetLayerSet( aBoard->GetLayerSet() & LSET::AllCuMask() );
  2087. else
  2088. zone->SetLayer( layer );
  2089. zone->SetIsRuleArea( false );
  2090. zone->SetDoNotAllowTracks( false );
  2091. zone->SetDoNotAllowVias( false );
  2092. zone->SetDoNotAllowPads( false );
  2093. zone->SetDoNotAllowFootprints( false );
  2094. zone->SetDoNotAllowCopperPour( false );
  2095. if( aLine->lclass == "ROUTE KEEPOUT")
  2096. {
  2097. zone->SetIsRuleArea( true );
  2098. zone->SetDoNotAllowTracks( true );
  2099. }
  2100. else if( aLine->lclass == "VIA KEEPOUT")
  2101. {
  2102. zone->SetIsRuleArea( true );
  2103. zone->SetDoNotAllowVias( true );
  2104. }
  2105. zone->SetPriority( 50 );
  2106. zone->SetLocalClearance( 0 );
  2107. zone->SetPadConnection( ZONE_CONNECTION::FULL );
  2108. zone_outline->NewOutline();
  2109. for( const auto& seg : aLine->segment )
  2110. {
  2111. if( seg->subseq > 0 && seg->subseq != last_subseq )
  2112. {
  2113. /// Don't knock holes in the BOUNDARY systems. These are the outer layers for zone fills.
  2114. if( aLine->lclass == "BOUNDARY" )
  2115. break;
  2116. hole_idx = zone_outline->AddHole( SHAPE_LINE_CHAIN{} );
  2117. last_subseq = seg->subseq;
  2118. last_subseq = seg->subseq;
  2119. }
  2120. if( seg->shape == GR_SHAPE_LINE )
  2121. {
  2122. const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
  2123. if( zone_outline->VertexCount( 0, hole_idx ) == 0 )
  2124. zone_outline->Append( src->start_x, src->start_y, 0, hole_idx );
  2125. zone_outline->Append( src->end_x, src->end_y, 0, hole_idx );
  2126. }
  2127. else if( seg->shape == GR_SHAPE_ARC )
  2128. {
  2129. const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
  2130. zone_outline->Hole( 0, hole_idx ).Append( src->result );
  2131. }
  2132. }
  2133. if( zone_outline->Outline( 0 ).PointCount() >= 3 )
  2134. {
  2135. zone->SetOutline( zone_outline );
  2136. aBoard->Add( zone, ADD_MODE::APPEND );
  2137. }
  2138. else
  2139. {
  2140. delete( zone_outline );
  2141. delete( zone );
  2142. }
  2143. return true;
  2144. }
  2145. bool FABMASTER::loadOutline( BOARD* aBoard, const std::unique_ptr<FABMASTER::TRACE>& aLine)
  2146. {
  2147. PCB_LAYER_ID layer;
  2148. if( aLine->lclass == "BOARD GEOMETRY" )
  2149. layer = Edge_Cuts;
  2150. else if( aLine->lclass == "DRAWING FORMAT" )
  2151. layer = Dwgs_User;
  2152. else
  2153. layer = Cmts_User;
  2154. for( auto& seg : aLine->segment )
  2155. {
  2156. switch( seg->shape )
  2157. {
  2158. case GR_SHAPE_LINE:
  2159. {
  2160. const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
  2161. PCB_SHAPE* line = new PCB_SHAPE( aBoard );
  2162. line->SetShape( PCB_SHAPE_TYPE::SEGMENT );
  2163. line->SetLayer( layer );
  2164. line->SetStart( wxPoint( src->start_x, src->start_y ) );
  2165. line->SetEnd( wxPoint( src->end_x, src->end_y ) );
  2166. line->SetWidth( src->width );
  2167. if( line->GetWidth() == 0 )
  2168. line->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
  2169. aBoard->Add( line, ADD_MODE::APPEND );
  2170. break;
  2171. }
  2172. case GR_SHAPE_ARC:
  2173. {
  2174. const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
  2175. PCB_SHAPE* arc = new PCB_SHAPE( aBoard );
  2176. arc->SetShape( PCB_SHAPE_TYPE::ARC );
  2177. arc->SetLayer( layer );
  2178. arc->SetCenter( wxPoint( src->center_x, src->center_y ) );
  2179. arc->SetArcStart( wxPoint( src->start_x, src->start_y ) );
  2180. arc->SetAngle( src->result.GetCentralAngle() * 10.0 );
  2181. arc->SetWidth( src->width );
  2182. if( arc->GetWidth() == 0 )
  2183. arc->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
  2184. aBoard->Add( arc, ADD_MODE::APPEND );
  2185. break;
  2186. }
  2187. case GR_SHAPE_RECTANGLE:
  2188. {
  2189. const GRAPHIC_RECTANGLE *src =
  2190. static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
  2191. PCB_SHAPE* rect = new PCB_SHAPE( aBoard );
  2192. rect->SetShape( PCB_SHAPE_TYPE::RECT );
  2193. rect->SetLayer( layer );
  2194. rect->SetStart( wxPoint( src->start_x, src->start_y ) );
  2195. rect->SetEnd( wxPoint( src->end_x, src->end_y ) );
  2196. rect->SetWidth( aBoard->GetDesignSettings().GetLineThickness( layer ) );
  2197. aBoard->Add( rect, ADD_MODE::APPEND );
  2198. break;
  2199. }
  2200. case GR_SHAPE_TEXT:
  2201. {
  2202. const GRAPHIC_TEXT *src =
  2203. static_cast<const GRAPHIC_TEXT*>( seg.get() );
  2204. PCB_TEXT* txt = new PCB_TEXT( aBoard );
  2205. txt->SetLayer( layer );
  2206. txt->SetTextPos( wxPoint( src->start_x, src->start_y - src->height / 2 ) );
  2207. txt->SetText( src->text );
  2208. txt->SetItalic( src->ital );
  2209. txt->SetTextThickness( src->thickness );
  2210. txt->SetTextHeight( src->height );
  2211. txt->SetTextWidth( src->width );
  2212. txt->SetHorizJustify( src->orient );
  2213. aBoard->Add( txt, ADD_MODE::APPEND );
  2214. break;
  2215. }
  2216. default:
  2217. return false;
  2218. }
  2219. }
  2220. return true;
  2221. }
  2222. bool FABMASTER::loadGraphics( BOARD* aBoard )
  2223. {
  2224. for( auto& geom : board_graphics )
  2225. {
  2226. PCB_LAYER_ID layer;
  2227. // The pin numbers are not useful for us outside of the footprints
  2228. if( geom.subclass == "PIN_NUMBER" )
  2229. continue;
  2230. layer = getLayer( geom.subclass );
  2231. if( !IsPcbLayer( layer ) )
  2232. layer = Cmts_User;
  2233. if( !geom.elements->empty() )
  2234. {
  2235. /// Zero-width segments/arcs are polygon outlines
  2236. if( ( *( geom.elements->begin() ) )->width == 0 )
  2237. {
  2238. SHAPE_POLY_SET poly_outline = loadShapePolySet( *( geom.elements ) );
  2239. if( poly_outline.OutlineCount() < 1 || poly_outline.COutline( 0 ).PointCount() < 3 )
  2240. continue;
  2241. PCB_SHAPE* new_poly = new PCB_SHAPE( aBoard );
  2242. new_poly->SetShape( PCB_SHAPE_TYPE::POLYGON );
  2243. new_poly->SetLayer( layer );
  2244. new_poly->SetPolyShape( poly_outline );
  2245. new_poly->SetWidth( 0 );
  2246. if( layer == F_SilkS || layer == B_SilkS )
  2247. new_poly->SetFilled( true );
  2248. aBoard->Add( new_poly, ADD_MODE::APPEND );
  2249. }
  2250. }
  2251. for( auto& seg : *geom.elements )
  2252. {
  2253. switch( seg->shape )
  2254. {
  2255. case GR_SHAPE_LINE:
  2256. {
  2257. const GRAPHIC_LINE* src = static_cast<const GRAPHIC_LINE*>( seg.get() );
  2258. PCB_SHAPE* line = new PCB_SHAPE( aBoard );
  2259. line->SetShape( PCB_SHAPE_TYPE::SEGMENT );
  2260. line->SetLayer( layer );
  2261. line->SetStart( wxPoint( src->start_x, src->start_y ) );
  2262. line->SetEnd( wxPoint( src->end_x, src->end_y ) );
  2263. line->SetWidth( src->width );
  2264. aBoard->Add( line, ADD_MODE::APPEND );
  2265. break;
  2266. }
  2267. case GR_SHAPE_ARC:
  2268. {
  2269. const GRAPHIC_ARC* src = static_cast<const GRAPHIC_ARC*>( seg.get() );
  2270. PCB_SHAPE* arc = new PCB_SHAPE( aBoard );
  2271. arc->SetShape( PCB_SHAPE_TYPE::ARC );
  2272. arc->SetLayer( layer );
  2273. arc->SetCenter( wxPoint( src->center_x, src->center_y ) );
  2274. arc->SetArcStart( wxPoint( src->start_x, src->start_y ) );
  2275. arc->SetAngle( src->result.GetCentralAngle() * 10.0 );
  2276. arc->SetWidth( src->width );
  2277. aBoard->Add( arc, ADD_MODE::APPEND );
  2278. break;
  2279. }
  2280. case GR_SHAPE_RECTANGLE:
  2281. {
  2282. const GRAPHIC_RECTANGLE *src =
  2283. static_cast<const GRAPHIC_RECTANGLE*>( seg.get() );
  2284. PCB_SHAPE* rect = new PCB_SHAPE( aBoard );
  2285. rect->SetShape( PCB_SHAPE_TYPE::RECT );
  2286. rect->SetLayer( layer );
  2287. rect->SetStart( wxPoint( src->start_x, src->start_y ) );
  2288. rect->SetEnd( wxPoint( src->end_x, src->end_y ) );
  2289. rect->SetWidth( 0 );
  2290. aBoard->Add( rect, ADD_MODE::APPEND );
  2291. break;
  2292. }
  2293. case GR_SHAPE_TEXT:
  2294. {
  2295. const GRAPHIC_TEXT *src =
  2296. static_cast<const GRAPHIC_TEXT*>( seg.get() );
  2297. PCB_TEXT* txt = new PCB_TEXT( aBoard );
  2298. txt->SetLayer( layer );
  2299. txt->SetTextPos( wxPoint( src->start_x, src->start_y - src->height / 2 ) );
  2300. txt->SetText( src->text );
  2301. txt->SetItalic( src->ital );
  2302. txt->SetTextThickness( src->thickness );
  2303. txt->SetTextHeight( src->height );
  2304. txt->SetTextWidth( src->width );
  2305. txt->SetHorizJustify( src->orient );
  2306. aBoard->Add( txt, ADD_MODE::APPEND );
  2307. break;
  2308. }
  2309. default:
  2310. return false;
  2311. }
  2312. }
  2313. }
  2314. return true;
  2315. }
  2316. bool FABMASTER::orderZones( BOARD* aBoard )
  2317. {
  2318. std::vector<ZONE*> zones = aBoard->Zones();
  2319. std::sort( zones.begin(), zones.end(),
  2320. [&]( const ZONE* a, const ZONE* b ) {
  2321. if( a->GetLayer() == b->GetLayer() )
  2322. return a->GetBoundingBox().GetArea() > b->GetBoundingBox().GetArea();
  2323. return a->GetLayer() < b->GetLayer();
  2324. } );
  2325. PCB_LAYER_ID layer = UNDEFINED_LAYER;
  2326. unsigned int priority = 0;
  2327. for( ZONE* zone : zones )
  2328. {
  2329. if( zone->GetLayer() != layer )
  2330. {
  2331. layer = zone->GetLayer();
  2332. priority = 0;
  2333. }
  2334. zone->SetPriority( priority );
  2335. priority += 10;
  2336. }
  2337. return true;
  2338. }
  2339. bool FABMASTER::LoadBoard( BOARD* aBoard )
  2340. {
  2341. aBoard->SetFileName( m_filename.GetFullPath() );
  2342. loadNets( aBoard );
  2343. loadLayers( aBoard );
  2344. loadVias( aBoard );
  2345. loadFootprints( aBoard );
  2346. loadZones( aBoard );
  2347. loadGraphics( aBoard );
  2348. for( auto& track : traces )
  2349. {
  2350. if( track->lclass == "ETCH" )
  2351. {
  2352. loadEtch( aBoard, track);
  2353. }
  2354. else if( track->layer == "OUTLINE" )
  2355. {
  2356. loadOutline( aBoard, track );
  2357. }
  2358. }
  2359. orderZones( aBoard );
  2360. return true;
  2361. }