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.

661 lines
19 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com>
  5. * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include "kicadpcb.h"
  25. #include "kicadcurve.h"
  26. #include "kicadfootprint.h"
  27. #include "oce_utils.h"
  28. #include <sexpr/sexpr.h>
  29. #include <sexpr/sexpr_parser.h>
  30. #include <wx/filename.h>
  31. #include <wx/wxcrtvararg.h>
  32. #include <memory>
  33. #include <string>
  34. KICADPCB::KICADPCB( const wxString& aPcbName )
  35. {
  36. m_resolver.Set3DConfigDir( wxT( "" ) );
  37. m_topSolderMask = wxColour( 15, 102, 15 );
  38. m_bottomSolderMask = wxColour( 15, 102, 15 );
  39. m_topSilk = wxColour( 240, 240, 240 );
  40. m_bottomSilk = wxColour( 240, 240, 240 );
  41. m_copperFinish = wxColour( 191, 155, 58 );
  42. m_thickness = 1.6;
  43. m_pcb_model = nullptr;
  44. m_minDistance = MIN_DISTANCE;
  45. m_useGridOrigin = false;
  46. m_useDrillOrigin = false;
  47. m_hasGridOrigin = false;
  48. m_hasDrillOrigin = false;
  49. m_pcbName = aPcbName;
  50. }
  51. KICADPCB::~KICADPCB()
  52. {
  53. for( KICADFOOTPRINT* i : m_footprints )
  54. delete i;
  55. for( KICADCURVE* i : m_curves )
  56. delete i;
  57. delete m_pcb_model;
  58. return;
  59. }
  60. bool KICADPCB::ReadFile( const wxString& aFileName )
  61. {
  62. wxFileName fname( aFileName );
  63. if( fname.GetExt() != "kicad_pcb" )
  64. {
  65. ReportMessage( wxString::Format( wxT( "expecting extension kicad_pcb, got %s\n" ),
  66. fname.GetExt() ) );
  67. return false;
  68. }
  69. if( !fname.FileExists() )
  70. {
  71. ReportMessage( wxString::Format( wxT( "No such file: %s\n" ), aFileName ) );
  72. return false;
  73. }
  74. fname.Normalize();
  75. m_filename = fname.GetFullPath();
  76. try
  77. {
  78. SEXPR::PARSER parser;
  79. std::string infile( fname.GetFullPath().ToUTF8() );
  80. std::unique_ptr<SEXPR::SEXPR> data( parser.ParseFromFile( infile ) );
  81. if( !data )
  82. {
  83. ReportMessage( wxString::Format( wxT( "No data in file: %s\n" ), aFileName ) );
  84. return false;
  85. }
  86. if( !parsePCB( data.get() ) )
  87. return false;
  88. }
  89. catch( std::exception& e )
  90. {
  91. ReportMessage( wxString::Format( wxT( "error reading file: %s\n%s\n" ),
  92. aFileName,
  93. e.what() ) );
  94. return false;
  95. }
  96. catch( ... )
  97. {
  98. ReportMessage( wxString::Format( wxT( "unexpected exception while reading file: %s\n" ),
  99. aFileName ) );
  100. return false;
  101. }
  102. return true;
  103. }
  104. bool KICADPCB::WriteSTEP( const wxString& aFileName )
  105. {
  106. if( m_pcb_model )
  107. return m_pcb_model->WriteSTEP( aFileName );
  108. return false;
  109. }
  110. #ifdef SUPPORTS_IGES
  111. bool KICADPCB::WriteIGES( const wxString& aFileName )
  112. {
  113. if( m_pcb_model )
  114. return m_pcb_model->WriteIGES( aFileName );
  115. return false;
  116. }
  117. #endif
  118. bool KICADPCB::parsePCB( SEXPR::SEXPR* data )
  119. {
  120. if( NULL == data )
  121. return false;
  122. if( data->IsList() )
  123. {
  124. size_t nc = data->GetNumberOfChildren();
  125. SEXPR::SEXPR* child = data->GetChild( 0 );
  126. std::string name = child->GetSymbol();
  127. bool result = true;
  128. for( size_t i = 1; i < nc && result; ++i )
  129. {
  130. child = data->GetChild( i );
  131. if( !child->IsList() )
  132. {
  133. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ),
  134. child->GetLineNumber() ) );
  135. return false;
  136. }
  137. std::string symname( child->GetChild( 0 )->GetSymbol() );
  138. if( symname == "general" )
  139. result = result && parseGeneral( child );
  140. else if( symname == "setup" )
  141. result = result && parseSetup( child );
  142. else if( symname == "layers" )
  143. result = result && parseLayers( child );
  144. else if( symname == "module" )
  145. result = result && parseModule( child );
  146. else if( symname == "footprint" )
  147. result = result && parseModule( child );
  148. else if( symname == "gr_arc" )
  149. result = result && parseCurve( child, CURVE_ARC );
  150. else if( symname == "gr_line" )
  151. result = result && parseCurve( child, CURVE_LINE );
  152. else if( symname == "gr_rect" )
  153. result = result && parseRect( child );
  154. else if( symname == "gr_poly" )
  155. result = result && parsePolygon( child );
  156. else if( symname == "gr_circle" )
  157. result = result && parseCurve( child, CURVE_CIRCLE );
  158. else if( symname == "gr_curve" )
  159. result = result && parseCurve( child, CURVE_BEZIER );
  160. }
  161. return result;
  162. }
  163. ReportMessage( wxString::Format( wxT( "data is not a valid PCB file: %s\n" ), m_filename ) );
  164. return false;
  165. }
  166. bool KICADPCB::parseGeneral( SEXPR::SEXPR* data )
  167. {
  168. size_t nc = data->GetNumberOfChildren();
  169. SEXPR::SEXPR* child = NULL;
  170. for( size_t i = 1; i < nc; ++i )
  171. {
  172. child = data->GetChild( i );
  173. if( !child->IsList() )
  174. {
  175. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ),
  176. child->GetLineNumber() ) );
  177. return false;
  178. }
  179. // at the moment only the thickness is of interest in the general section
  180. if( child->GetChild( 0 )->GetSymbol() != "thickness" )
  181. continue;
  182. m_thickness = child->GetChild( 1 )->GetDouble();
  183. return true;
  184. }
  185. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n"
  186. "no PCB thickness specified in general section\n" ),
  187. child->GetLineNumber() ) );
  188. return false;
  189. }
  190. bool KICADPCB::parseLayers( SEXPR::SEXPR* data )
  191. {
  192. size_t nc = data->GetNumberOfChildren();
  193. SEXPR::SEXPR* child = NULL;
  194. // Read the layername and the corresponding layer id list:
  195. for( size_t i = 1; i < nc; ++i )
  196. {
  197. child = data->GetChild( i );
  198. if( !child->IsList() )
  199. {
  200. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ),
  201. child->GetLineNumber() ) );
  202. return false;
  203. }
  204. std::string ref;
  205. if( child->GetChild( 1 )->IsSymbol() )
  206. ref = child->GetChild( 1 )->GetSymbol();
  207. else
  208. ref = child->GetChild( 1 )->GetString();
  209. m_layersNames[ref] = child->GetChild( 0 )->GetInteger();
  210. }
  211. return true;
  212. }
  213. bool KICADPCB::parseStackupLayer( SEXPR::SEXPR* data )
  214. {
  215. if( data->IsList() && data->GetNumberOfChildren() >= 3 )
  216. {
  217. size_t nc = data->GetNumberOfChildren();
  218. SEXPR::SEXPR* child = NULL;
  219. std::string ref;
  220. std::string value;
  221. for( size_t i = 1; i < nc; ++i )
  222. {
  223. child = data->GetChild( i );
  224. if( child->IsList() && child->GetChild( 0 )->GetSymbol() == "type" )
  225. {
  226. if( child->GetChild( 1 )->IsSymbol() )
  227. ref = child->GetChild( 1 )->GetSymbol();
  228. else
  229. ref = child->GetChild( 1 )->GetString();
  230. }
  231. else if( child->IsList() && child->GetChild( 0 )->GetSymbol() == "color" )
  232. {
  233. if( child->GetChild( 1 )->IsSymbol() )
  234. value = child->GetChild( 1 )->GetSymbol();
  235. else
  236. value = child->GetChild( 1 )->GetString();
  237. }
  238. }
  239. if( !value.empty() )
  240. {
  241. wxString colorName( value );
  242. wxColour color;
  243. if( colorName.StartsWith( wxT( "#" ) ) )
  244. color = wxColour( colorName.Left( 7 ) /* drop alpha component */ );
  245. else if( colorName == wxT( "Green" ) )
  246. color = wxColour( 20, 51, 36 );
  247. else if( colorName == wxT( "Light Green" ) )
  248. color = wxColour( 91, 168, 12);
  249. else if( colorName == wxT( "Saturated Green" ) )
  250. color = wxColour( 13, 104, 11 );
  251. else if( colorName == wxT( "Red" ) )
  252. color = wxColour( 181, 19, 21 );
  253. else if( colorName == wxT( "Light Red" ) )
  254. color = wxColour( 210, 40, 14 );
  255. else if( colorName == wxT( "Red/Orange" ) )
  256. color = wxColour( 239, 53, 41 );
  257. else if( colorName == wxT( "Blue" ) )
  258. color = wxColour( 2, 59, 162 );
  259. else if( colorName == wxT( "Light Blue 1" ) )
  260. color = wxColour( 54, 79, 116 );
  261. else if( colorName == wxT( "Light Blue 2" ) )
  262. color = wxColour( 61, 85, 130 );
  263. else if( colorName == wxT( "Green/Blue" ) )
  264. color = wxColour( 21, 70, 80 );
  265. else if( colorName == wxT( "Black" ) )
  266. color = wxColour( 11, 11, 11 );
  267. else if( colorName == wxT( "White" ) )
  268. color = wxColour( 245, 245, 245 );
  269. else if( colorName == wxT( "Purple" ) )
  270. color = wxColour( 32, 2, 53 );
  271. else if( colorName == wxT( "Light Purple" ) )
  272. color = wxColour( 119, 31, 91 );
  273. else if( colorName == wxT( "Yellow" ) )
  274. color = wxColour( 194, 195, 0 );
  275. if( ref == "Top Silk Screen" )
  276. m_topSilk = color;
  277. else if( ref == "Top Solder Mask" )
  278. m_topSolderMask = color;
  279. else if( ref == "Bottom Silk Screen" )
  280. m_bottomSilk = color;
  281. else if( ref == "Bottom Solder Mask" )
  282. m_bottomSolderMask = color;
  283. }
  284. }
  285. return true;
  286. }
  287. bool KICADPCB::parseStackup( SEXPR::SEXPR* data )
  288. {
  289. size_t nc = data->GetNumberOfChildren();
  290. SEXPR::SEXPR* child = NULL;
  291. for( size_t i = 1; i < nc; ++i )
  292. {
  293. child = data->GetChild( i );
  294. if( !child->IsList() )
  295. {
  296. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ),
  297. child->GetLineNumber() ) );
  298. return false;
  299. }
  300. std::string ref;
  301. if( child->GetChild( 0 )->GetSymbol() == "layer" )
  302. {
  303. parseStackupLayer( child );
  304. }
  305. else if( child->GetChild( 0 )->GetSymbol() == "copper_finish" )
  306. {
  307. if( child->GetChild( 1 )->IsSymbol() )
  308. ref = child->GetChild( 1 )->GetSymbol();
  309. else
  310. ref = child->GetChild( 1 )->GetString();
  311. wxString finishName( ref );
  312. if( finishName.EndsWith( wxT( "OSP" ) ) )
  313. {
  314. m_copperFinish = wxColour( 184, 115, 50 );
  315. }
  316. else if( finishName.EndsWith( wxT( "IG" ) )
  317. || finishName.EndsWith( wxT( "gold" ) ) )
  318. {
  319. m_copperFinish = wxColour( 178, 156, 0 );
  320. }
  321. else if( finishName.StartsWith( wxT( "HAL" ) )
  322. || finishName.StartsWith( wxT( "HASL" ) )
  323. || finishName.EndsWith( wxT( "tin" ) )
  324. || finishName.EndsWith( wxT( "nickel" ) ) )
  325. {
  326. m_copperFinish = wxColour( 160, 160, 160 );
  327. }
  328. else if( finishName.EndsWith( wxT( "silver" ) ) )
  329. {
  330. m_copperFinish = wxColour( 213, 213, 213 );
  331. }
  332. }
  333. }
  334. return true;
  335. }
  336. int KICADPCB::GetLayerId( std::string& aLayerName )
  337. {
  338. int lid = -1;
  339. auto item = m_layersNames.find( aLayerName );
  340. if( item != m_layersNames.end() )
  341. lid = item->second;
  342. return lid;
  343. }
  344. bool KICADPCB::parseSetup( SEXPR::SEXPR* data )
  345. {
  346. size_t nc = data->GetNumberOfChildren();
  347. SEXPR::SEXPR* child = NULL;
  348. for( size_t i = 1; i < nc; ++i )
  349. {
  350. child = data->GetChild( i );
  351. if( !child->IsList() )
  352. {
  353. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d)\n" ),
  354. child->GetLineNumber() ) );
  355. return false;
  356. }
  357. // at the moment only the Grid and Drill origins are of interest in the setup section
  358. if( child->GetChild( 0 )->GetSymbol() == "grid_origin" )
  359. {
  360. if( child->GetNumberOfChildren() != 3 )
  361. {
  362. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d): grid_origin has "
  363. "%d children (expected: 3)\n" ),
  364. child->GetLineNumber(),
  365. child->GetNumberOfChildren() ) );
  366. return false;
  367. }
  368. m_gridOrigin.x = child->GetChild( 1 )->GetDouble();
  369. m_gridOrigin.y = child->GetChild( 2 )->GetDouble();
  370. m_hasGridOrigin = true;
  371. }
  372. else if( child->GetChild( 0 )->GetSymbol() == "aux_axis_origin" )
  373. {
  374. if( child->GetNumberOfChildren() != 3 )
  375. {
  376. ReportMessage( wxString::Format( wxT( "corrupt PCB file (line %d): aux_axis_origin "
  377. "has %d children (expected: 3)\n" ),
  378. child->GetLineNumber(),
  379. child->GetNumberOfChildren() ) );
  380. return false;
  381. }
  382. m_drillOrigin.x = child->GetChild( 1 )->GetDouble();
  383. m_drillOrigin.y = child->GetChild( 2 )->GetDouble();
  384. m_hasDrillOrigin = true;
  385. }
  386. else if( child->GetChild( 0 )->GetSymbol() == "stackup" )
  387. {
  388. parseStackup( child );
  389. }
  390. }
  391. return true;
  392. }
  393. bool KICADPCB::parseModule( SEXPR::SEXPR* data )
  394. {
  395. KICADFOOTPRINT* footprint = new KICADFOOTPRINT( this );
  396. if( !footprint->Read( data ) )
  397. {
  398. delete footprint;
  399. return false;
  400. }
  401. m_footprints.push_back( footprint );
  402. return true;
  403. }
  404. bool KICADPCB::parseRect( SEXPR::SEXPR* data )
  405. {
  406. KICADCURVE* rect = new KICADCURVE();
  407. if( !rect->Read( data, CURVE_LINE ) )
  408. {
  409. delete rect;
  410. return false;
  411. }
  412. // reject any curves not on the Edge.Cuts layer
  413. if( rect->GetLayer() != LAYER_EDGE )
  414. {
  415. delete rect;
  416. return true;
  417. }
  418. KICADCURVE* top = new KICADCURVE( *rect );
  419. KICADCURVE* right = new KICADCURVE( *rect );
  420. KICADCURVE* bottom = new KICADCURVE( *rect );
  421. KICADCURVE* left = new KICADCURVE( *rect );
  422. delete rect;
  423. top->m_end.y = right->m_start.y;
  424. m_curves.push_back( top );
  425. right->m_start.x = bottom->m_end.x;
  426. m_curves.push_back( right );
  427. bottom->m_start.y = left->m_end.y;
  428. m_curves.push_back( bottom );
  429. left->m_end.x = top->m_start.x;
  430. m_curves.push_back( left );
  431. return true;
  432. }
  433. bool KICADPCB::parsePolygon( SEXPR::SEXPR* data )
  434. {
  435. std::unique_ptr<KICADCURVE> poly = std::make_unique<KICADCURVE>();
  436. if( !poly->Read( data, CURVE_POLYGON ) )
  437. return false;
  438. // reject any curves not on the Edge.Cuts layer
  439. if( poly->GetLayer() != LAYER_EDGE )
  440. return true;
  441. auto pts = poly->m_poly;
  442. for( std::size_t ii = 1; ii < pts.size(); ++ii )
  443. {
  444. KICADCURVE* seg = new KICADCURVE();
  445. seg->m_form = CURVE_LINE;
  446. seg->m_layer = poly->GetLayer();
  447. seg->m_start = pts[ii - 1];
  448. seg->m_end = pts[ii];
  449. m_curves.push_back( seg );
  450. }
  451. KICADCURVE* seg = new KICADCURVE();
  452. seg->m_form = CURVE_LINE;
  453. seg->m_layer = poly->GetLayer();
  454. seg->m_start = pts.back();
  455. seg->m_end = pts.front();
  456. m_curves.push_back( seg );
  457. return true;
  458. }
  459. bool KICADPCB::parseCurve( SEXPR::SEXPR* data, CURVE_TYPE aCurveType )
  460. {
  461. KICADCURVE* curve = new KICADCURVE();
  462. if( !curve->Read( data, aCurveType ) )
  463. {
  464. delete curve;
  465. return false;
  466. }
  467. // reject any curves not on the Edge.Cuts layer
  468. if( curve->GetLayer() != LAYER_EDGE )
  469. {
  470. delete curve;
  471. return true;
  472. }
  473. m_curves.push_back( curve );
  474. return true;
  475. }
  476. bool KICADPCB::ComposePCB( bool aComposeVirtual, bool aSubstituteModels )
  477. {
  478. if( m_pcb_model )
  479. return true;
  480. if( m_footprints.empty() && m_curves.empty() )
  481. {
  482. ReportMessage( wxT( "Error: no PCB data (no footprint, no outline) to render\n" ) );
  483. return false;
  484. }
  485. DOUBLET origin;
  486. // Determine the coordinate system reference:
  487. // Precedence of reference point is Drill Origin > Grid Origin > User Offset
  488. if( m_useDrillOrigin && m_hasDrillOrigin )
  489. origin = m_drillOrigin;
  490. else if( m_useGridOrigin && m_hasDrillOrigin )
  491. origin = m_gridOrigin;
  492. else
  493. origin = m_origin;
  494. m_pcb_model = new PCBMODEL( m_pcbName );
  495. // TODO: Handle when top & bottom soldermask colours are different...
  496. m_pcb_model->SetBoardColor( m_topSolderMask.Red() / 255.0,
  497. m_topSolderMask.Green() / 255.0,
  498. m_topSolderMask.Blue() / 255.0 );
  499. m_pcb_model->SetPCBThickness( m_thickness );
  500. m_pcb_model->SetMinDistance( m_minDistance );
  501. for( KICADCURVE* i : m_curves )
  502. {
  503. if( CURVE_NONE == i->m_form || LAYER_EDGE != i->m_layer )
  504. continue;
  505. // adjust the coordinate system
  506. // Note: we negate the Y coordinates due to the fact in Pcbnew the Y axis
  507. // is from top to bottom.
  508. KICADCURVE lcurve = *i;
  509. lcurve.m_start.y = -( lcurve.m_start.y - origin.y );
  510. lcurve.m_end.y = -( lcurve.m_end.y - origin.y );
  511. lcurve.m_start.x -= origin.x;
  512. lcurve.m_end.x -= origin.x;
  513. // used in bezier curves:
  514. lcurve.m_bezierctrl1.y = -( lcurve.m_bezierctrl1.y - origin.y );
  515. lcurve.m_bezierctrl1.x -= origin.x;
  516. lcurve.m_bezierctrl2.y = -( lcurve.m_bezierctrl2.y - origin.y );
  517. lcurve.m_bezierctrl2.x -= origin.x;
  518. if( CURVE_ARC == lcurve.m_form )
  519. lcurve.m_angle = -lcurve.m_angle;
  520. m_pcb_model->AddOutlineSegment( &lcurve );
  521. }
  522. for( KICADFOOTPRINT* i : m_footprints )
  523. i->ComposePCB( m_pcb_model, &m_resolver, origin, aComposeVirtual, aSubstituteModels );
  524. ReportMessage( wxT( "Create PCB solid model\n" ) );
  525. if( !m_pcb_model->CreatePCB() )
  526. {
  527. ReportMessage( wxT( "could not create PCB solid model\n" ) );
  528. delete m_pcb_model;
  529. m_pcb_model = NULL;
  530. return false;
  531. }
  532. return true;
  533. }