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.

477 lines
13 KiB

  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 2018-2021 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 "kicadfootprint.h"
  25. #include "3d_resolver.h"
  26. #include "kicadcurve.h"
  27. #include "kicadmodel.h"
  28. #include "kicadpad.h"
  29. #include "oce_utils.h"
  30. #include <sexpr/sexpr.h>
  31. #include <wx/log.h>
  32. #include <wx/filename.h>
  33. #include <iostream>
  34. #include <limits>
  35. #include <memory>
  36. #include <sstream>
  37. #include <Standard_Failure.hxx>
  38. KICADFOOTPRINT::KICADFOOTPRINT( KICADPCB* aParent )
  39. {
  40. m_parent = aParent;
  41. m_side = LAYER_NONE;
  42. m_rotation = 0.0;
  43. m_smd = false;
  44. m_tht = false;
  45. m_virtual = false;
  46. return;
  47. }
  48. KICADFOOTPRINT::~KICADFOOTPRINT()
  49. {
  50. for( KICADPAD* i : m_pads )
  51. delete i;
  52. for( KICADCURVE* i : m_curves )
  53. delete i;
  54. for( KICADMODEL* i : m_models )
  55. delete i;
  56. return;
  57. }
  58. bool KICADFOOTPRINT::Read( SEXPR::SEXPR* aEntry )
  59. {
  60. if( !aEntry )
  61. return false;
  62. if( aEntry->IsList() )
  63. {
  64. size_t nc = aEntry->GetNumberOfChildren();
  65. SEXPR::SEXPR* child = aEntry->GetChild( 0 );
  66. std::string name = child->GetSymbol();
  67. if( name != "module" && name != "footprint" )
  68. {
  69. std::ostringstream ostr;
  70. ostr << "* BUG: module parser invoked for type '" << name << "'\n";
  71. wxLogMessage( "%s\n", ostr.str().c_str() );
  72. return false;
  73. }
  74. bool result = true;
  75. for( size_t i = 2; i < nc && result; ++i )
  76. {
  77. child = aEntry->GetChild( i );
  78. std::string symname;
  79. // skip the optional locked and/or placed attributes; due to the vagaries of the
  80. // KiCad version of sexpr, the attribute may be a Symbol or a String
  81. if( child->IsSymbol() || child->IsString() )
  82. {
  83. if( child->IsSymbol() )
  84. symname = child->GetSymbol();
  85. else if( child->IsString() )
  86. symname = child->GetString();
  87. if( symname == "locked" || symname == "placed" )
  88. continue;
  89. wxLogMessage( "* module descr in PCB file at line %d: unexpected keyword '%s'\n",
  90. child->GetLineNumber(), symname.c_str() );
  91. return false;
  92. }
  93. if( !child->IsList() )
  94. {
  95. wxLogMessage( "* corrupt module in PCB file at line %d\n",
  96. child->GetLineNumber() );
  97. return false;
  98. }
  99. symname = child->GetChild( 0 )->GetSymbol();
  100. if( symname == "layer" )
  101. result = parseLayer( child );
  102. else if( symname == "at" )
  103. result = parsePosition( child );
  104. else if( symname == "attr" )
  105. result = parseAttribute( child );
  106. else if( symname == "fp_text" )
  107. result = parseText( child );
  108. else if( symname == "fp_arc" )
  109. result = parseCurve( child, CURVE_ARC );
  110. else if( symname == "fp_line" )
  111. result = parseCurve( child, CURVE_LINE );
  112. else if( symname == "fp_circle" )
  113. result = parseCurve( child, CURVE_CIRCLE );
  114. else if( symname == "fp_rect" )
  115. result = parseRect( child );
  116. else if( symname == "fp_curve" )
  117. result = parseCurve( child, CURVE_BEZIER );
  118. else if( symname == "pad" )
  119. result = parsePad( child );
  120. else if( symname == "model" )
  121. result = parseModel( child );
  122. }
  123. if( !m_smd && !m_tht )
  124. m_virtual = true;
  125. return result;
  126. }
  127. std::ostringstream ostr;
  128. ostr << "* data is not a valid PCB module\n";
  129. wxLogMessage( "%s\n", ostr.str().c_str() );
  130. return false;
  131. }
  132. bool KICADFOOTPRINT::parseRect( SEXPR::SEXPR* data )
  133. {
  134. std::unique_ptr<KICADCURVE> rect = std::make_unique<KICADCURVE>();
  135. if( !rect->Read( data, CURVE_LINE ) )
  136. return false;
  137. // reject any curves not on the Edge.Cuts layer
  138. if( rect->GetLayer() != LAYER_EDGE )
  139. return true;
  140. KICADCURVE* top = new KICADCURVE( *rect );
  141. KICADCURVE* right = new KICADCURVE( *rect );
  142. KICADCURVE* bottom = new KICADCURVE( *rect );
  143. KICADCURVE* left = new KICADCURVE( *rect );
  144. top->m_end.y = right->m_start.y;
  145. m_curves.push_back( top );
  146. right->m_start.x = bottom->m_end.x;
  147. m_curves.push_back( right );
  148. bottom->m_start.y = left->m_end.y;
  149. m_curves.push_back( bottom );
  150. left->m_end.x = top->m_start.x;
  151. m_curves.push_back( left );
  152. return true;
  153. }
  154. bool KICADFOOTPRINT::parseModel( SEXPR::SEXPR* data )
  155. {
  156. KICADMODEL* mp = new KICADMODEL();
  157. if( !mp->Read( data ) )
  158. {
  159. delete mp;
  160. return false;
  161. }
  162. if( mp->Hide() )
  163. delete mp;
  164. else
  165. m_models.push_back( mp );
  166. return true;
  167. }
  168. bool KICADFOOTPRINT::parseCurve( SEXPR::SEXPR* data, CURVE_TYPE aCurveType )
  169. {
  170. KICADCURVE* mp = new KICADCURVE();
  171. if( !mp->Read( data, aCurveType ) )
  172. {
  173. delete mp;
  174. return false;
  175. }
  176. // NOTE: for now we are only interested in glyphs on the outline layer
  177. if( LAYER_EDGE != mp->GetLayer() )
  178. {
  179. delete mp;
  180. return true;
  181. }
  182. m_curves.push_back( mp );
  183. return true;
  184. }
  185. bool KICADFOOTPRINT::parseLayer( SEXPR::SEXPR* data )
  186. {
  187. SEXPR::SEXPR* val = data->GetChild( 1 );
  188. std::string layername;
  189. if( val->IsSymbol() )
  190. layername = val->GetSymbol();
  191. else if( val->IsString() )
  192. layername = val->GetString();
  193. else
  194. {
  195. std::ostringstream ostr;
  196. ostr << "* corrupt module in PCB file (line ";
  197. ostr << val->GetLineNumber() << "); layer cannot be parsed\n";
  198. wxLogMessage( "%s\n", ostr.str().c_str() );
  199. return false;
  200. }
  201. int layerId = m_parent->GetLayerId( layername );
  202. if( layerId == 31 )
  203. m_side = LAYER_BOTTOM;
  204. else
  205. m_side = LAYER_TOP;
  206. return true;
  207. }
  208. bool KICADFOOTPRINT::parsePosition( SEXPR::SEXPR* data )
  209. {
  210. return Get2DPositionAndRotation( data, m_position, m_rotation );
  211. }
  212. bool KICADFOOTPRINT::parseAttribute( SEXPR::SEXPR* data )
  213. {
  214. if( data->GetNumberOfChildren() < 2 )
  215. {
  216. std::ostringstream ostr;
  217. ostr << "* corrupt module in PCB file (line ";
  218. ostr << data->GetLineNumber() << "); attribute cannot be parsed\n";
  219. wxLogMessage( "%s\n", ostr.str().c_str() );
  220. return false;
  221. }
  222. SEXPR::SEXPR* child = data->GetChild( 1 );
  223. std::string text;
  224. if( child->IsSymbol() )
  225. text = child->GetSymbol();
  226. else if( child->IsString() )
  227. text = child->GetString();
  228. if( text == "smd" )
  229. m_smd = true;
  230. else if( text == "through_hole" )
  231. m_tht = true;
  232. else if( text == "virtual" )
  233. m_virtual = true;
  234. return true;
  235. }
  236. bool KICADFOOTPRINT::parseText( SEXPR::SEXPR* data )
  237. {
  238. // we're only interested in the Reference Designator
  239. if( data->GetNumberOfChildren() < 3 )
  240. return true;
  241. SEXPR::SEXPR* child = data->GetChild( 1 );
  242. std::string text;
  243. if( child->IsSymbol() )
  244. text = child->GetSymbol();
  245. else if( child->IsString() )
  246. text = child->GetString();
  247. if( text != "reference" )
  248. return true;
  249. child = data->GetChild( 2 );
  250. if( child->IsSymbol() )
  251. text = child->GetSymbol();
  252. else if( child->IsString() )
  253. text = child->GetString();
  254. m_refdes = text;
  255. return true;
  256. }
  257. bool KICADFOOTPRINT::parsePad( SEXPR::SEXPR* data )
  258. {
  259. KICADPAD* mp = new KICADPAD();
  260. if( !mp->Read( data ) )
  261. {
  262. delete mp;
  263. return false;
  264. }
  265. // NOTE: for now we only accept thru-hole pads
  266. // for the MCAD description
  267. if( !mp->IsThruHole() )
  268. {
  269. delete mp;
  270. return true;
  271. }
  272. m_pads.push_back( mp );
  273. return true;
  274. }
  275. bool KICADFOOTPRINT::ComposePCB( class PCBMODEL* aPCB, S3D_RESOLVER* resolver,
  276. DOUBLET aOrigin, bool aComposeVirtual, bool aSubstituteModels )
  277. {
  278. // translate pads and curves to final position and append to PCB.
  279. double dlim = (double)std::numeric_limits< float >::epsilon();
  280. double vsin = sin( m_rotation );
  281. double vcos = cos( m_rotation );
  282. bool hasdata = false;
  283. double posX = m_position.x - aOrigin.x;
  284. double posY = m_position.y - aOrigin.y;
  285. for( KICADCURVE* i : m_curves )
  286. {
  287. if( i->m_layer != LAYER_EDGE || CURVE_NONE == i->m_form )
  288. continue;
  289. KICADCURVE lcurve = *i;
  290. lcurve.m_start.y = -lcurve.m_start.y;
  291. lcurve.m_end.y = -lcurve.m_end.y;
  292. lcurve.m_angle = -lcurve.m_angle;
  293. if( m_rotation < -dlim || m_rotation > dlim )
  294. {
  295. double x = lcurve.m_start.x * vcos - lcurve.m_start.y * vsin;
  296. double y = lcurve.m_start.x * vsin + lcurve.m_start.y * vcos;
  297. lcurve.m_start.x = x;
  298. lcurve.m_start.y = y;
  299. x = lcurve.m_end.x * vcos - lcurve.m_end.y * vsin;
  300. y = lcurve.m_end.x * vsin + lcurve.m_end.y * vcos;
  301. lcurve.m_end.x = x;
  302. lcurve.m_end.y = y;
  303. }
  304. lcurve.m_start.x += posX;
  305. lcurve.m_start.y -= posY;
  306. lcurve.m_end.x += posX;
  307. lcurve.m_end.y -= posY;
  308. if( aPCB->AddOutlineSegment( &lcurve ) )
  309. hasdata = true;
  310. }
  311. for( KICADPAD* i : m_pads )
  312. {
  313. if( !i->IsThruHole() )
  314. continue;
  315. KICADPAD lpad = *i;
  316. lpad.m_position.y = -lpad.m_position.y;
  317. if( m_rotation < -dlim || m_rotation > dlim )
  318. {
  319. double x = lpad.m_position.x * vcos - lpad.m_position.y * vsin;
  320. double y = lpad.m_position.x * vsin + lpad.m_position.y * vcos;
  321. lpad.m_position.x = x;
  322. lpad.m_position.y = y;
  323. }
  324. lpad.m_position.x += posX;
  325. lpad.m_position.y -= posY;
  326. if( aPCB->AddPadHole( &lpad ) )
  327. hasdata = true;
  328. }
  329. if( m_virtual && !aComposeVirtual )
  330. return hasdata;
  331. DOUBLET newpos( posX, posY );
  332. for( KICADMODEL* i : m_models )
  333. {
  334. wxString mname = wxString::FromUTF8Unchecked( i->m_modelname.c_str() );
  335. if( mname.empty() )
  336. continue;
  337. std::vector<wxString> searchedPaths;
  338. mname = resolver->ResolvePath( mname, searchedPaths );
  339. if( !wxFileName::FileExists( mname ) )
  340. {
  341. wxString paths;
  342. for( const wxString& path : searchedPaths )
  343. paths += " " + path + "\n";
  344. ReportMessage( wxString::Format( "Could not add 3D model to %s.\n"
  345. "File not found: %s\n"
  346. "Searched paths:\n"
  347. "%s",
  348. m_refdes,
  349. mname,
  350. paths) );
  351. continue;
  352. }
  353. std::string fname( mname.ToUTF8() );
  354. try
  355. {
  356. if( aPCB->AddComponent( fname, m_refdes, LAYER_BOTTOM == m_side ? true : false,
  357. newpos, m_rotation, i->m_offset, i->m_rotation, i->m_scale,
  358. aSubstituteModels ) )
  359. {
  360. hasdata = true;
  361. }
  362. }
  363. catch( const Standard_Failure& e)
  364. {
  365. ReportMessage( wxString::Format( "Could not add 3D model to %s.\n"
  366. "OpenCASCADE error: %s\n",
  367. m_refdes,
  368. e.GetMessageString() ) );
  369. }
  370. }
  371. return hasdata;
  372. }