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.

973 lines
26 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2014-2017 Cirilo Bernardo
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. /*
  24. * This program takes an IDF base name, loads the board outline
  25. * and component outine files, and creates a single VRML file.
  26. * The VRML file can be used to visually verify the IDF files
  27. * before sending them to a mechanical designer. The output scale
  28. * is 10:1; this scale was chosen because VRML was originally
  29. * intended to describe large virtual worlds and rounding errors
  30. * would be more likely if we used a 1:1 scale.
  31. */
  32. #include <wx/app.h>
  33. #include <wx/cmdline.h>
  34. #include <wx/log.h>
  35. #include <wx/string.h>
  36. #include <wx/filename.h>
  37. #include <iostream>
  38. #include <iomanip>
  39. #include <fstream>
  40. #include <string>
  41. #include <sstream>
  42. #include <cmath>
  43. #include <cstdio>
  44. #include <cerrno>
  45. #include <list>
  46. #include <utility>
  47. #include <vector>
  48. #include <cstdlib>
  49. #include <cstring>
  50. #include <algorithm>
  51. #include <boost/ptr_container/ptr_map.hpp>
  52. #include "idf_helpers.h"
  53. #include "idf_common.h"
  54. #include "idf_parser.h"
  55. #include "streamwrapper.h"
  56. #include "vrml_layer.h"
  57. #ifndef MIN_ANG
  58. #define MIN_ANG 0.01
  59. #endif
  60. class IDF2VRML : public wxAppConsole
  61. {
  62. public:
  63. virtual bool OnInit() override;
  64. virtual int OnRun() override;
  65. virtual void OnInitCmdLine(wxCmdLineParser& parser) override;
  66. virtual bool OnCmdLineParsed(wxCmdLineParser& parser) override;
  67. private:
  68. double m_ScaleFactor;
  69. bool m_Compact;
  70. bool m_NoOutlineSubs;
  71. wxString m_filename;
  72. };
  73. static const wxCmdLineEntryDesc cmdLineDesc[] =
  74. {
  75. { wxCMD_LINE_OPTION, "f", NULL, "input file name",
  76. wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
  77. { wxCMD_LINE_OPTION, "s", NULL, "scale factor",
  78. wxCMD_LINE_VAL_DOUBLE, wxCMD_LINE_PARAM_OPTIONAL },
  79. { wxCMD_LINE_SWITCH, "k", NULL, "produce KiCad-friendly VRML output; default is compact VRML",
  80. wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
  81. { wxCMD_LINE_SWITCH, "d", NULL, "suppress substitution of default outlines",
  82. wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
  83. { wxCMD_LINE_SWITCH, "z", NULL, "suppress rendering of zero-height outlines",
  84. wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
  85. { wxCMD_LINE_SWITCH, "m", NULL, "print object mapping to stdout for debugging",
  86. wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
  87. { wxCMD_LINE_SWITCH, "h", NULL, "display this message",
  88. wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
  89. { wxCMD_LINE_NONE, nullptr, nullptr, nullptr, wxCMD_LINE_VAL_NONE, 0 }
  90. };
  91. wxIMPLEMENT_APP_CONSOLE( IDF2VRML );
  92. bool nozeroheights;
  93. bool showObjectMapping;
  94. bool IDF2VRML::OnInit()
  95. {
  96. m_ScaleFactor = 1.0;
  97. m_Compact = true;
  98. m_NoOutlineSubs = false;
  99. nozeroheights = false;
  100. showObjectMapping = false;
  101. if( !wxAppConsole::OnInit() )
  102. return false;
  103. return true;
  104. }
  105. void IDF2VRML::OnInitCmdLine( wxCmdLineParser& parser )
  106. {
  107. parser.SetDesc( cmdLineDesc );
  108. parser.SetSwitchChars( "-" );
  109. return;
  110. }
  111. bool IDF2VRML::OnCmdLineParsed( wxCmdLineParser& parser )
  112. {
  113. if( parser.Found( "k" ) )
  114. m_Compact = false;
  115. double scale;
  116. if( parser.Found( "s", &scale ) )
  117. m_ScaleFactor = scale;
  118. wxString fname;
  119. if( parser.Found( "f", &fname ) )
  120. m_filename = fname;
  121. if( parser.Found( "d" ) )
  122. m_NoOutlineSubs = true;
  123. if( parser.Found( "z" ) )
  124. nozeroheights = true;
  125. if( parser.Found( "m" ) )
  126. showObjectMapping = true;
  127. return true;
  128. }
  129. using namespace boost;
  130. // define colors
  131. struct VRML_COLOR
  132. {
  133. double diff[3];
  134. double emis[3];
  135. double spec[3];
  136. double ambi;
  137. double tran;
  138. double shin;
  139. };
  140. struct VRML_IDS
  141. {
  142. int colorIndex;
  143. std::string objectName;
  144. bool used;
  145. bool bottom;
  146. double dX, dY, dZ, dA;
  147. VRML_IDS()
  148. {
  149. colorIndex = 0;
  150. used = false;
  151. bottom = false;
  152. dX = 0.0;
  153. dY = 0.0;
  154. dZ = 0.0;
  155. dA = 0.0;
  156. }
  157. };
  158. #define NCOLORS 7
  159. VRML_COLOR colors[NCOLORS] =
  160. {
  161. { { 0, 0.82, 0.247 }, { 0, 0, 0 }, { 0, 0.82, 0.247 }, 0.9, 0, 0.1 },
  162. { { 1, 0, 0 }, { 1, 0, 0 }, { 1, 0, 0 }, 0.9, 0, 0.1 },
  163. { { 0.659, 0, 0.463 }, { 0, 0, 0 }, { 0.659, 0, 0.463 }, 0.9, 0, 0.1 },
  164. { { 0.659, 0.294, 0 }, { 0, 0, 0 }, { 0.659, 0.294, 0 }, 0.9, 0, 0.1 },
  165. { { 0, 0.918, 0.659 }, { 0, 0, 0 }, { 0, 0.918, 0.659 }, 0.9, 0, 0.1 },
  166. { { 0.808, 0.733, 0.071 }, { 0, 0, 0 }, { 0.808, 0.733 , 0.071 }, 0.9, 0, 0.1 },
  167. { { 0.102, 1, 0.984 }, { 0, 0, 0 }, { 0.102, 1, 0.984 }, 0.9, 0, 0.1 }
  168. };
  169. bool WriteHeader( IDF3_BOARD& board, std::ostream& file );
  170. bool MakeBoard( IDF3_BOARD& board, std::ostream& file );
  171. bool MakeComponents( IDF3_BOARD& board, std::ostream& file, bool compact );
  172. bool MakeOtherOutlines( IDF3_BOARD& board, std::ostream& file );
  173. bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom,
  174. double scale, double dX = 0.0, double dY = 0.0, double angle = 0.0 );
  175. bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg );
  176. bool WriteTriangles( std::ostream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
  177. bool top, double top_z, double bottom_z, int precision, bool compact );
  178. inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
  179. double dX, double dY, double angle );
  180. VRML_IDS* GetColor( boost::ptr_map<const std::string, VRML_IDS>& cmap,
  181. int& index, const std::string& uid );
  182. int IDF2VRML::OnRun()
  183. {
  184. // Essential inputs:
  185. // 1. IDF file
  186. // 2. Output scale: internal IDF units are mm, so 1 = 1mm per VRML unit,
  187. // 0.1 = 1cm per VRML unit, 0.01 = 1m per VRML unit,
  188. // 1/25.4 = 1in per VRML unit, 1/2.54 = 0.1in per VRML unit (KiCad model)
  189. // 3. KiCad-friendly output (do not reuse features via DEF+USE)
  190. // Render each component to VRML; if the user wants
  191. // a KiCad friendly output then we must avoid DEF+USE;
  192. // otherwise we employ DEF+USE to minimize file size
  193. if( m_ScaleFactor < 0.001 || m_ScaleFactor > 10.0 )
  194. {
  195. wxLogMessage("scale factor out of range (%d); range is 0.001 to 10.0", m_ScaleFactor);
  196. return -1;
  197. }
  198. IDF3_BOARD pcb( IDF3::CAD_ELEC );
  199. wxLogMessage( "Reading file: '%s'", m_filename );
  200. if( !pcb.ReadFile( m_filename, m_NoOutlineSubs ) )
  201. {
  202. wxLogMessage( "Failed to read IDF data: %s", pcb.GetError() );
  203. return -1;
  204. }
  205. // set the scale and output precision ( scale 1 == precision 5)
  206. pcb.SetUserScale( m_ScaleFactor );
  207. if( m_ScaleFactor < 0.01 )
  208. pcb.SetUserPrecision( 8 );
  209. else if( m_ScaleFactor < 0.1 )
  210. pcb.SetUserPrecision( 7 );
  211. else if( m_ScaleFactor < 1.0 )
  212. pcb.SetUserPrecision( 6 );
  213. else if( m_ScaleFactor < 10.0 )
  214. pcb.SetUserPrecision( 5 );
  215. else
  216. pcb.SetUserPrecision( 4 );
  217. // Create the VRML file and write the header
  218. wxFileName fname( m_filename );
  219. fname.SetExt( "wrl" );
  220. fname.Normalize();
  221. wxLogMessage( "Writing file: '%s'", fname.GetFullName() );
  222. OPEN_OSTREAM( ofile, fname.GetFullPath().ToUTF8() );
  223. if( ofile.fail() )
  224. {
  225. wxLogMessage( "Could not open file: '%s'", fname.GetFullName() );
  226. }
  227. ofile.imbue( std::locale( "C" ) );
  228. ofile << std::fixed; // do not use exponents in VRML output
  229. WriteHeader( pcb, ofile );
  230. // STEP 1: Render the PCB alone
  231. MakeBoard( pcb, ofile );
  232. // STEP 2: Render the components
  233. MakeComponents( pcb, ofile, m_Compact );
  234. // STEP 3: Render the OTHER outlines
  235. MakeOtherOutlines( pcb, ofile );
  236. ofile << "]\n}\n";
  237. CLOSE_STREAM( ofile );
  238. return 0;
  239. }
  240. bool WriteHeader( IDF3_BOARD& board, std::ostream& file )
  241. {
  242. std::string bname = board.GetBoardName();
  243. if( bname.empty() )
  244. {
  245. bname = "BoardWithNoName";
  246. }
  247. else
  248. {
  249. std::string::iterator ss = bname.begin();
  250. std::string::iterator se = bname.end();
  251. while( ss != se )
  252. {
  253. if( *ss == '/' || *ss == ' ' || *ss == ':' )
  254. *ss = '_';
  255. ++ss;
  256. }
  257. }
  258. file << "#VRML V2.0 utf8\n\n";
  259. file << "WorldInfo {\n";
  260. file << " title \"" << bname << "\"\n}\n\n";
  261. file << "Transform {\n";
  262. file << "children [\n";
  263. return !file.fail();
  264. }
  265. bool MakeBoard( IDF3_BOARD& board, std::ostream& file )
  266. {
  267. VRML_LAYER vpcb;
  268. if( board.GetBoardOutlinesSize() < 1 )
  269. {
  270. wxLogMessage( "Cannot proceed; no board outline in IDF object" );
  271. return false;
  272. }
  273. double scale = board.GetUserScale();
  274. // set the arc parameters according to output scale
  275. int tI;
  276. double tMin, tMax;
  277. vpcb.GetArcParams( tI, tMin, tMax );
  278. vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
  279. if( !PopulateVRML( vpcb, board.GetBoardOutline()->GetOutlines(), false, board.GetUserScale() ) )
  280. {
  281. return false;
  282. }
  283. vpcb.EnsureWinding( 0, false );
  284. int nvcont = vpcb.GetNContours() - 1;
  285. while( nvcont > 0 )
  286. vpcb.EnsureWinding( nvcont--, true );
  287. // Add the drill holes
  288. const std::list<IDF_DRILL_DATA*>* drills = &board.GetBoardDrills();
  289. std::list<IDF_DRILL_DATA*>::const_iterator sd = drills->begin();
  290. std::list<IDF_DRILL_DATA*>::const_iterator ed = drills->end();
  291. while( sd != ed )
  292. {
  293. vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
  294. (*sd)->GetDrillDia() * scale / 2.0, true );
  295. ++sd;
  296. }
  297. std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
  298. std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
  299. std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
  300. while( sc != ec )
  301. {
  302. drills = sc->second->GetDrills();
  303. sd = drills->begin();
  304. ed = drills->end();
  305. while( sd != ed )
  306. {
  307. vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
  308. (*sd)->GetDrillDia() * scale / 2.0, true );
  309. ++sd;
  310. }
  311. ++sc;
  312. }
  313. // tesselate and write out
  314. vpcb.Tesselate( NULL );
  315. double thick = board.GetBoardThickness() / 2.0 * scale;
  316. VRML_IDS tvid;
  317. tvid.colorIndex = 0;
  318. WriteTriangles( file, &tvid, &vpcb, false, false,
  319. thick, -thick, board.GetUserPrecision(), false );
  320. return true;
  321. }
  322. bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom, double scale,
  323. double dX, double dY, double angle )
  324. {
  325. // empty outlines are not unusual so we fail quietly
  326. if( items->size() < 1 )
  327. return false;
  328. int nvcont = 0;
  329. int iseg = 0;
  330. std::list< IDF_OUTLINE* >::const_iterator scont = items->begin();
  331. std::list< IDF_OUTLINE* >::const_iterator econt = items->end();
  332. std::list<IDF_SEGMENT*>::iterator sseg;
  333. std::list<IDF_SEGMENT*>::iterator eseg;
  334. IDF_SEGMENT lseg;
  335. while( scont != econt )
  336. {
  337. nvcont = model.NewContour();
  338. if( nvcont < 0 )
  339. {
  340. wxLogMessage( "Cannot create an outline" );
  341. return false;
  342. }
  343. if( (*scont)->size() < 1 )
  344. {
  345. wxLogMessage( "Invalid contour: no vertices" );
  346. return false;
  347. }
  348. sseg = (*scont)->begin();
  349. eseg = (*scont)->end();
  350. iseg = 0;
  351. while( sseg != eseg )
  352. {
  353. lseg = **sseg;
  354. TransformPoint( lseg, scale, bottom, dX, dY, angle );
  355. if( !AddSegment( model, &lseg, nvcont, iseg ) )
  356. return false;
  357. ++iseg;
  358. ++sseg;
  359. }
  360. ++scont;
  361. }
  362. return true;
  363. }
  364. bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg )
  365. {
  366. // note: in all cases we must add all but the last point in the segment
  367. // to avoid redundant points
  368. if( seg->angle != 0.0 )
  369. {
  370. if( seg->IsCircle() )
  371. {
  372. if( iseg != 0 )
  373. {
  374. wxLogMessage( "Adding a circle to an existing vertex list" );
  375. return false;
  376. }
  377. return model.AppendCircle( seg->center.x, seg->center.y, seg->radius, icont );
  378. }
  379. else
  380. {
  381. return model.AppendArc( seg->center.x, seg->center.y, seg->radius,
  382. seg->offsetAngle, seg->angle, icont );
  383. }
  384. }
  385. if( !model.AddVertex( icont, seg->startPoint.x, seg->startPoint.y ) )
  386. return false;
  387. return true;
  388. }
  389. bool WriteTriangles( std::ostream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
  390. bool top, double top_z, double bottom_z, int precision, bool compact )
  391. {
  392. if( vID == NULL || layer == NULL )
  393. return false;
  394. file << "Transform {\n";
  395. if( compact && !vID->objectName.empty() )
  396. {
  397. file << "translation " << std::setprecision( precision ) << vID->dX;
  398. file << " " << vID->dY << " ";
  399. if( vID->bottom )
  400. {
  401. file << -vID->dZ << "\n";
  402. double tx, ty;
  403. // calculate the rotation axis and angle
  404. tx = cos( M_PI2 - vID->dA / 2.0 );
  405. ty = sin( M_PI2 - vID->dA / 2.0 );
  406. file << "rotation " << std::setprecision( precision );
  407. file << tx << " " << ty << " 0 ";
  408. file << std::setprecision(5) << M_PI << "\n";
  409. }
  410. else
  411. {
  412. file << vID->dZ << "\n";
  413. file << "rotation 0 0 1 " << std::setprecision(5) << vID->dA << "\n";
  414. }
  415. file << "children [\n";
  416. if( vID->used )
  417. {
  418. file << "USE " << vID->objectName << "\n";
  419. file << "]\n";
  420. file << "}\n";
  421. return true;
  422. }
  423. file << "DEF " << vID->objectName << " Transform {\n";
  424. if( !plane && top_z <= bottom_z )
  425. {
  426. // the height specification is faulty; make the component
  427. // a bright red to highlight it
  428. vID->colorIndex = 1;
  429. // we don't know the scale, but 5 units is huge in most situations
  430. top_z = bottom_z + 5.0;
  431. }
  432. }
  433. VRML_COLOR* color = &colors[vID->colorIndex];
  434. vID->used = true;
  435. file << "children [\n";
  436. file << "Group {\n";
  437. file << "children [\n";
  438. file << "Shape {\n";
  439. file << "appearance Appearance {\n";
  440. file << "material Material {\n";
  441. // material definition
  442. file << "diffuseColor " << std::setprecision(3) << color->diff[0] << " ";
  443. file << color->diff[1] << " " << color->diff[2] << "\n";
  444. file << "specularColor " << color->spec[0] << " " << color->spec[1];
  445. file << " " << color->spec[2] << "\n";
  446. file << "emissiveColor " << color->emis[0] << " " << color->emis[1];
  447. file << " " << color->emis[2] << "\n";
  448. file << "ambientIntensity " << color->ambi << "\n";
  449. file << "transparency " << color->tran << "\n";
  450. file << "shininess " << color->shin << "\n";
  451. file << "}\n";
  452. file << "}\n";
  453. file << "geometry IndexedFaceSet {\n";
  454. file << "solid TRUE\n";
  455. file << "coord Coordinate {\n";
  456. file << "point [\n";
  457. // Coordinates (vertices)
  458. if( plane )
  459. {
  460. if( !layer->WriteVertices( top_z, file, precision ) )
  461. {
  462. wxLogMessage( "Errors writing planar vertices to %s\n%s",
  463. vID->objectName, layer->GetError() );
  464. }
  465. }
  466. else
  467. {
  468. if( !layer->Write3DVertices( top_z, bottom_z, file, precision ) )
  469. {
  470. wxLogMessage( "Errors writing 3D vertices to %s\n%s",
  471. vID->objectName, layer->GetError() );
  472. }
  473. }
  474. file << "\n";
  475. file << "]\n";
  476. file << "}\n";
  477. file << "coordIndex [\n";
  478. // Indices
  479. if( plane )
  480. layer->WriteIndices( top, file );
  481. else
  482. layer->Write3DIndices( file );
  483. file << "\n";
  484. file << "]\n";
  485. file << "}\n";
  486. file << "}\n";
  487. file << "]\n";
  488. file << "}\n";
  489. file << "]\n";
  490. file << "}\n";
  491. if( compact && !vID->objectName.empty() )
  492. {
  493. file << "]\n";
  494. file << "}\n";
  495. }
  496. return !file.fail();
  497. }
  498. inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
  499. double dX, double dY, double angle )
  500. {
  501. dX *= frac;
  502. dY *= frac;
  503. if( bottom )
  504. {
  505. // mirror points on the Y axis
  506. seg.startPoint.x = -seg.startPoint.x;
  507. seg.endPoint.x = -seg.endPoint.x;
  508. seg.center.x = -seg.center.x;
  509. angle = -angle;
  510. }
  511. seg.startPoint.x *= frac;
  512. seg.startPoint.y *= frac;
  513. seg.endPoint.x *= frac;
  514. seg.endPoint.y *= frac;
  515. seg.center.x *= frac;
  516. seg.center.y *= frac;
  517. double tsin = 0.0;
  518. double tcos = 0.0;
  519. if( angle > MIN_ANG || angle < -MIN_ANG )
  520. {
  521. double ta = angle * M_PI / 180.0;
  522. double tx, ty;
  523. tsin = sin( ta );
  524. tcos = cos( ta );
  525. tx = seg.startPoint.x * tcos - seg.startPoint.y * tsin;
  526. ty = seg.startPoint.x * tsin + seg.startPoint.y * tcos;
  527. seg.startPoint.x = tx;
  528. seg.startPoint.y = ty;
  529. tx = seg.endPoint.x * tcos - seg.endPoint.y * tsin;
  530. ty = seg.endPoint.x * tsin + seg.endPoint.y * tcos;
  531. seg.endPoint.x = tx;
  532. seg.endPoint.y = ty;
  533. if( seg.angle != 0 )
  534. {
  535. tx = seg.center.x * tcos - seg.center.y * tsin;
  536. ty = seg.center.x * tsin + seg.center.y * tcos;
  537. seg.center.x = tx;
  538. seg.center.y = ty;
  539. }
  540. }
  541. seg.startPoint.x += dX;
  542. seg.startPoint.y += dY;
  543. seg.endPoint.x += dX;
  544. seg.endPoint.y += dY;
  545. seg.center.x += dX;
  546. seg.center.y += dY;
  547. if( seg.angle != 0 )
  548. {
  549. seg.radius *= frac;
  550. if( bottom )
  551. {
  552. if( !seg.IsCircle() )
  553. {
  554. seg.angle = -seg.angle;
  555. if( seg.offsetAngle > 0.0 )
  556. seg.offsetAngle = 180 - seg.offsetAngle;
  557. else
  558. seg.offsetAngle = -seg.offsetAngle - 180;
  559. }
  560. }
  561. if( angle > MIN_ANG || angle < -MIN_ANG )
  562. seg.offsetAngle += angle;
  563. }
  564. return;
  565. }
  566. bool MakeComponents( IDF3_BOARD& board, std::ostream& file, bool compact )
  567. {
  568. int cidx = 2; // color index; start at 2 since 0,1 are special (board, NOGEOM_NOPART)
  569. VRML_LAYER vpcb;
  570. double scale = board.GetUserScale();
  571. double thick = board.GetBoardThickness() / 2.0;
  572. // set the arc parameters according to output scale
  573. int tI;
  574. double tMin, tMax;
  575. vpcb.GetArcParams( tI, tMin, tMax );
  576. vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
  577. // Add the component outlines
  578. const std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
  579. std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
  580. std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
  581. std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator so;
  582. std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator eo;
  583. double vX, vY, vA;
  584. double tX, tY, tZ, tA;
  585. double top, bot;
  586. bool bottom;
  587. IDF3::IDF_LAYER lyr;
  588. boost::ptr_map< const std::string, VRML_IDS> cmap; // map colors by outline UID
  589. VRML_IDS* vcp;
  590. IDF3_COMP_OUTLINE* pout;
  591. while( sc != ec )
  592. {
  593. sc->second->GetPosition( vX, vY, vA, lyr );
  594. if( lyr == IDF3::LYR_BOTTOM )
  595. bottom = true;
  596. else
  597. bottom = false;
  598. so = sc->second->GetOutlinesData()->begin();
  599. eo = sc->second->GetOutlinesData()->end();
  600. while( so != eo )
  601. {
  602. if( (*so)->GetOutline()->GetThickness() < 0.00000001 && nozeroheights )
  603. {
  604. vpcb.Clear();
  605. ++so;
  606. continue;
  607. }
  608. (*so)->GetOffsets( tX, tY, tZ, tA );
  609. tX += vX;
  610. tY += vY;
  611. tA += vA;
  612. if( ( pout = (IDF3_COMP_OUTLINE*)((*so)->GetOutline()) ) )
  613. {
  614. vcp = GetColor( cmap, cidx, pout->GetUID() );
  615. }
  616. else
  617. {
  618. vpcb.Clear();
  619. ++so;
  620. continue;
  621. }
  622. if( !compact )
  623. {
  624. if( !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), bottom,
  625. board.GetUserScale(), tX, tY, tA ) )
  626. {
  627. return false;
  628. }
  629. }
  630. else
  631. {
  632. if( !vcp->used && !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), false,
  633. board.GetUserScale() ) )
  634. {
  635. return false;
  636. }
  637. vcp->dX = tX * scale;
  638. vcp->dY = tY * scale;
  639. vcp->dZ = tZ * scale;
  640. vcp->dA = tA * M_PI / 180.0;
  641. }
  642. if( !compact || !vcp->used )
  643. {
  644. vpcb.EnsureWinding( 0, false );
  645. int nvcont = vpcb.GetNContours() - 1;
  646. while( nvcont > 0 )
  647. vpcb.EnsureWinding( nvcont--, true );
  648. vpcb.Tesselate( NULL );
  649. }
  650. if( !compact )
  651. {
  652. if( bottom )
  653. {
  654. top = -thick - tZ;
  655. bot = (top - (*so)->GetOutline()->GetThickness() ) * scale;
  656. top *= scale;
  657. }
  658. else
  659. {
  660. bot = thick + tZ;
  661. top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
  662. bot *= scale;
  663. }
  664. }
  665. else
  666. {
  667. bot = thick;
  668. top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
  669. bot *= scale;
  670. }
  671. vcp = GetColor( cmap, cidx, ((IDF3_COMP_OUTLINE*)((*so)->GetOutline()))->GetUID() );
  672. vcp->bottom = bottom;
  673. // note: this can happen because IDF allows some negative heights/thicknesses
  674. if( bot > top )
  675. std::swap( bot, top );
  676. WriteTriangles( file, vcp, &vpcb, false,
  677. false, top, bot, board.GetUserPrecision(), compact );
  678. vpcb.Clear();
  679. ++so;
  680. }
  681. ++sc;
  682. }
  683. return true;
  684. }
  685. VRML_IDS* GetColor( boost::ptr_map<const std::string, VRML_IDS>& cmap, int& index, const std::string& uid )
  686. {
  687. static int refnum = 0;
  688. if( index < 2 )
  689. index = 2; // 0 and 1 are special (BOARD, UID=NOGEOM_NOPART)
  690. boost::ptr_map<const std::string, VRML_IDS>::iterator cit = cmap.find( uid );
  691. if( cit == cmap.end() )
  692. {
  693. VRML_IDS* id = new VRML_IDS;
  694. if( !uid.compare( "NOGEOM_NOPART" ) )
  695. id->colorIndex = 1;
  696. else
  697. id->colorIndex = index++;
  698. std::ostringstream ostr;
  699. ostr << "OBJECTn" << refnum++;
  700. id->objectName = ostr.str();
  701. if( showObjectMapping )
  702. wxLogMessage( "* %s = '%s'", ostr.str(), uid );
  703. cmap.insert( uid, id );
  704. if( index >= NCOLORS )
  705. index = 2;
  706. return id;
  707. }
  708. return cit->second;
  709. }
  710. bool MakeOtherOutlines( IDF3_BOARD& board, std::ostream& file )
  711. {
  712. int cidx = 2; // color index; start at 2 since 0,1 are special (board, NOGEOM_NOPART)
  713. VRML_LAYER vpcb;
  714. double scale = board.GetUserScale();
  715. double thick = board.GetBoardThickness() / 2.0;
  716. // set the arc parameters according to output scale
  717. int tI;
  718. double tMin, tMax;
  719. vpcb.GetArcParams( tI, tMin, tMax );
  720. vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
  721. // Add the component outlines
  722. const std::map< std::string, OTHER_OUTLINE* >*const comp = board.GetOtherOutlines();
  723. std::map< std::string, OTHER_OUTLINE* >::const_iterator sc = comp->begin();
  724. std::map< std::string, OTHER_OUTLINE* >::const_iterator ec = comp->end();
  725. double top, bot;
  726. bool bottom;
  727. int nvcont;
  728. boost::ptr_map< const std::string, VRML_IDS> cmap; // map colors by outline UID
  729. VRML_IDS* vcp;
  730. OTHER_OUTLINE* pout;
  731. while( sc != ec )
  732. {
  733. pout = sc->second;
  734. if( pout->GetThickness() < 0.00000001 && nozeroheights )
  735. {
  736. vpcb.Clear();
  737. ++sc;
  738. continue;
  739. }
  740. vcp = GetColor( cmap, cidx, pout->GetOutlineIdentifier() );
  741. if( !PopulateVRML( vpcb, pout->GetOutlines(), false,
  742. board.GetUserScale(), 0, 0, 0 ) )
  743. {
  744. return false;
  745. }
  746. vpcb.EnsureWinding( 0, false );
  747. nvcont = vpcb.GetNContours() - 1;
  748. while( nvcont > 0 )
  749. vpcb.EnsureWinding( nvcont--, true );
  750. vpcb.Tesselate( NULL );
  751. if( pout->GetSide() == IDF3::LYR_BOTTOM )
  752. bottom = true;
  753. else
  754. bottom = false;
  755. if( bottom )
  756. {
  757. top = -thick;
  758. bot = ( top - pout->GetThickness() ) * scale;
  759. top *= scale;
  760. }
  761. else
  762. {
  763. bot = thick;
  764. top = (bot + pout->GetThickness() ) * scale;
  765. bot *= scale;
  766. }
  767. // note: this can happen because IDF allows some negative heights/thicknesses
  768. if( bot > top )
  769. std::swap( bot, top );
  770. vcp->bottom = bottom;
  771. WriteTriangles( file, vcp, &vpcb, false,
  772. false, top, bot, board.GetUserPrecision(), false );
  773. vpcb.Clear();
  774. ++sc;
  775. }
  776. return true;
  777. }