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.

567 lines
17 KiB

  1. /*
  2. * This program source code file is part of KICAD, a free EDA CAD application.
  3. *
  4. * Copyright (C) 1992-2013 jean-pierre.charras
  5. * Copyright (C) 1992-2013 Kicad Developers, see change_log.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 <algorithm> // std::max
  25. #include <cmath>
  26. #include <errno.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <vector>
  31. #include <common.h>
  32. #include <geometry/shape_poly_set.h>
  33. #include <layers_id_colors_and_visibility.h>
  34. #include <potracelib.h>
  35. #include "bitmap2component.h"
  36. /* free a potrace bitmap */
  37. static void bm_free( potrace_bitmap_t* bm )
  38. {
  39. if( bm != NULL )
  40. {
  41. free( bm->map );
  42. }
  43. free( bm );
  44. }
  45. /* Helper class to handle useful info to convert a bitmap image to
  46. * a polygonal object description
  47. */
  48. class BITMAPCONV_INFO
  49. {
  50. public:
  51. enum OUTPUT_FMT_ID m_Format; // File format
  52. int m_PixmapWidth;
  53. int m_PixmapHeight; // the bitmap size in pixels
  54. double m_ScaleX;
  55. double m_ScaleY; // the conversion scale
  56. potrace_path_t* m_Paths; // the list of paths, from potrace (list of lines and bezier curves)
  57. FILE* m_Outfile; // File to create
  58. const char * m_CmpName; // The string used as cmp/footprint name
  59. public:
  60. BITMAPCONV_INFO();
  61. /**
  62. * Function CreateOutputFile
  63. * Creates the output file specified by m_Outfile,
  64. * depending on file format given by m_Format
  65. */
  66. void CreateOutputFile( BMP2CMP_MOD_LAYER aModLayer = (BMP2CMP_MOD_LAYER) 0 );
  67. private:
  68. /**
  69. * Function OuputFileHeader
  70. * write to file the header depending on file format
  71. */
  72. void OuputFileHeader( const char * aBrdLayerName );
  73. /**
  74. * Function OuputFileEnd
  75. * write to file the last strings depending on file format
  76. */
  77. void OuputFileEnd();
  78. /**
  79. * @return the board layer name depending on the board layer selected
  80. * @param aChoice = the choice (MOD_LYR_FSILKS to MOD_LYR_FINAL)
  81. */
  82. const char * getBrdLayerName( BMP2CMP_MOD_LAYER aChoice );
  83. /**
  84. * Function OuputOnePolygon
  85. * write one polygon to output file.
  86. * Polygon coordinates are expected scaled by the polugon extraction function
  87. */
  88. void OuputOnePolygon( SHAPE_LINE_CHAIN & aPolygon, const char* aBrdLayerName );
  89. };
  90. static void BezierToPolyline( std::vector <potrace_dpoint_t>& aCornersBuffer,
  91. potrace_dpoint_t p1,
  92. potrace_dpoint_t p2,
  93. potrace_dpoint_t p3,
  94. potrace_dpoint_t p4 );
  95. BITMAPCONV_INFO::BITMAPCONV_INFO()
  96. {
  97. m_Format = POSTSCRIPT_FMT;
  98. m_PixmapWidth = 0;
  99. m_PixmapHeight = 0;
  100. m_ScaleX = 1.0;
  101. m_ScaleY = 1.0;
  102. m_Paths = NULL;
  103. m_Outfile = NULL;
  104. m_CmpName = "LOGO";
  105. }
  106. int bitmap2component( potrace_bitmap_t* aPotrace_bitmap, FILE* aOutfile,
  107. OUTPUT_FMT_ID aFormat, int aDpi_X, int aDpi_Y,
  108. BMP2CMP_MOD_LAYER aModLayer )
  109. {
  110. potrace_param_t* param;
  111. potrace_state_t* st;
  112. // set tracing parameters, starting from defaults
  113. param = potrace_param_default();
  114. if( !param )
  115. {
  116. fprintf( stderr, "Error allocating parameters: %s\n", strerror( errno ) );
  117. return 1;
  118. }
  119. param->turdsize = 0;
  120. /* convert the bitmap to curves */
  121. st = potrace_trace( param, aPotrace_bitmap );
  122. if( !st || st->status != POTRACE_STATUS_OK )
  123. {
  124. if( st )
  125. {
  126. potrace_state_free( st );
  127. }
  128. potrace_param_free( param );
  129. fprintf( stderr, "Error tracing bitmap: %s\n", strerror( errno ) );
  130. return 1;
  131. }
  132. BITMAPCONV_INFO info;
  133. info.m_PixmapWidth = aPotrace_bitmap->w;
  134. info.m_PixmapHeight = aPotrace_bitmap->h; // the bitmap size in pixels
  135. info.m_Paths = st->plist;
  136. info.m_Outfile = aOutfile;
  137. switch( aFormat )
  138. {
  139. case KICAD_LOGO:
  140. info.m_Format = KICAD_LOGO;
  141. info.m_ScaleX = 1e3 * 25.4 / aDpi_X; // the conversion scale from PPI to micro
  142. info.m_ScaleY = 1e3 * 25.4 / aDpi_Y; // Y axis is top to bottom
  143. info.CreateOutputFile();
  144. break;
  145. case POSTSCRIPT_FMT:
  146. info.m_Format = POSTSCRIPT_FMT;
  147. info.m_ScaleX = 1.0; // the conversion scale
  148. info.m_ScaleY = info.m_ScaleX;
  149. // output vector data, e.g. as a rudimentary EPS file (mainly for tests)
  150. info.CreateOutputFile();
  151. break;
  152. case EESCHEMA_FMT:
  153. info.m_Format = EESCHEMA_FMT;
  154. info.m_ScaleX = 1000.0 / aDpi_X; // the conversion scale from PPI to UI
  155. info.m_ScaleY = -1000.0 / aDpi_Y; // Y axis is bottom to Top for components in libs
  156. info.CreateOutputFile();
  157. break;
  158. case PCBNEW_KICAD_MOD:
  159. info.m_Format = PCBNEW_KICAD_MOD;
  160. info.m_ScaleX = 1e6 * 25.4 / aDpi_X; // the conversion scale from PPI to UI
  161. info.m_ScaleY = 1e6 * 25.4 / aDpi_Y; // Y axis is top to bottom in modedit
  162. info.CreateOutputFile( aModLayer );
  163. break;
  164. default:
  165. break;
  166. }
  167. bm_free( aPotrace_bitmap );
  168. potrace_state_free( st );
  169. potrace_param_free( param );
  170. return 0;
  171. }
  172. const char* BITMAPCONV_INFO::getBrdLayerName( BMP2CMP_MOD_LAYER aChoice )
  173. {
  174. const char * layerName = "F.SilkS";
  175. switch( aChoice )
  176. {
  177. case MOD_LYR_FSOLDERMASK:
  178. layerName = "F.Mask";
  179. break;
  180. case MOD_LYR_ECO1:
  181. layerName = "Eco1.User";
  182. break;
  183. case MOD_LYR_ECO2:
  184. layerName = "Eco2.User";
  185. break;
  186. case MOD_LYR_FSILKS:
  187. default: // case MOD_LYR_FSILKS only unless there is a bug
  188. break;
  189. }
  190. return layerName;
  191. }
  192. void BITMAPCONV_INFO::OuputFileHeader( const char * aBrdLayerName )
  193. {
  194. int Ypos = (int) ( m_PixmapHeight / 2 * m_ScaleY );
  195. int fieldSize; // fields text size = 60 mils
  196. switch( m_Format )
  197. {
  198. case POSTSCRIPT_FMT:
  199. /* output vector data, e.g. as a rudimentary EPS file */
  200. fprintf( m_Outfile, "%%!PS-Adobe-3.0 EPSF-3.0\n" );
  201. fprintf( m_Outfile, "%%%%BoundingBox: 0 0 %d %d\n",
  202. m_PixmapWidth, m_PixmapHeight );
  203. fprintf( m_Outfile, "gsave\n" );
  204. break;
  205. case PCBNEW_KICAD_MOD:
  206. // fields text size = 1.5 mm
  207. // fields text thickness = 1.5 / 5 = 0.3mm
  208. fprintf( m_Outfile, "(module %s (layer F.Cu)\n (at 0 0)\n",
  209. m_CmpName );
  210. fprintf( m_Outfile, " (fp_text reference \"G***\" (at 0 0) (layer %s) hide\n"
  211. " (effects (font (thickness 0.3)))\n )\n", aBrdLayerName );
  212. fprintf( m_Outfile, " (fp_text value \"%s\" (at 0.75 0) (layer %s) hide\n"
  213. " (effects (font (thickness 0.3)))\n )\n", m_CmpName, aBrdLayerName );
  214. break;
  215. case KICAD_LOGO:
  216. fprintf( m_Outfile, "(polygon (pos 0 0 rbcorner) (rotate 0) (linewidth 0.01)\n" );
  217. break;
  218. case EESCHEMA_FMT:
  219. fprintf( m_Outfile, "EESchema-LIBRARY Version 2.3\n" );
  220. fprintf( m_Outfile, "#\n# %s\n", m_CmpName );
  221. fprintf( m_Outfile, "# pixmap size w = %d, h = %d\n#\n",
  222. m_PixmapWidth, m_PixmapHeight );
  223. // print reference and value
  224. fieldSize = 60; // fields text size = 60 mils
  225. Ypos += fieldSize / 2;
  226. fprintf( m_Outfile, "DEF %s G 0 40 Y Y 1 F N\n", m_CmpName );
  227. fprintf( m_Outfile, "F0 \"#G\" 0 %d %d H I C CNN\n", Ypos, fieldSize );
  228. fprintf( m_Outfile, "F1 \"%s\" 0 %d %d H I C CNN\n", m_CmpName, -Ypos, fieldSize );
  229. fprintf( m_Outfile, "DRAW\n" );
  230. break;
  231. }
  232. }
  233. void BITMAPCONV_INFO::OuputFileEnd()
  234. {
  235. switch( m_Format )
  236. {
  237. case POSTSCRIPT_FMT:
  238. fprintf( m_Outfile, "grestore\n" );
  239. fprintf( m_Outfile, "%%EOF\n" );
  240. break;
  241. case PCBNEW_KICAD_MOD:
  242. fprintf( m_Outfile, ")\n" );
  243. break;
  244. case KICAD_LOGO:
  245. fprintf( m_Outfile, ")\n" );
  246. break;
  247. case EESCHEMA_FMT:
  248. fprintf( m_Outfile, "ENDDRAW\n" );
  249. fprintf( m_Outfile, "ENDDEF\n" );
  250. break;
  251. }
  252. }
  253. /**
  254. * Function OuputOnePolygon
  255. * write one polygon to output file.
  256. * Polygon coordinates are expected scaled by the polygon extraction function
  257. */
  258. void BITMAPCONV_INFO::OuputOnePolygon( SHAPE_LINE_CHAIN & aPolygon, const char* aBrdLayerName )
  259. {
  260. int ii, jj;
  261. VECTOR2I currpoint;
  262. int offsetX = (int)( m_PixmapWidth / 2 * m_ScaleX );
  263. int offsetY = (int)( m_PixmapHeight / 2 * m_ScaleY );
  264. const VECTOR2I startpoint = aPolygon.CPoint( 0 );
  265. switch( m_Format )
  266. {
  267. case POSTSCRIPT_FMT:
  268. offsetY = (int)( m_PixmapHeight * m_ScaleY );
  269. fprintf( m_Outfile, "newpath\n%d %d moveto\n",
  270. startpoint.x, offsetY - startpoint.y );
  271. jj = 0;
  272. for( ii = 1; ii < aPolygon.PointCount(); ii++ )
  273. {
  274. currpoint = aPolygon.CPoint( ii );
  275. fprintf( m_Outfile, " %d %d lineto",
  276. currpoint.x, offsetY - currpoint.y );
  277. if( jj++ > 6 )
  278. {
  279. jj = 0;
  280. fprintf( m_Outfile, ("\n") );
  281. }
  282. }
  283. fprintf( m_Outfile, "\nclosepath fill\n" );
  284. break;
  285. case PCBNEW_KICAD_MOD:
  286. {
  287. double width = 0.01; // outline thickness in mm
  288. fprintf( m_Outfile, " (fp_poly (pts" );
  289. jj = 0;
  290. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  291. {
  292. currpoint = aPolygon.CPoint( ii );
  293. fprintf( m_Outfile, " (xy %f %f)",
  294. ( currpoint.x - offsetX ) / 1e6,
  295. ( currpoint.y - offsetY ) / 1e6 );
  296. if( jj++ > 6 )
  297. {
  298. jj = 0;
  299. fprintf( m_Outfile, ("\n ") );
  300. }
  301. }
  302. // Close polygon
  303. fprintf( m_Outfile, " (xy %f %f) )",
  304. ( startpoint.x - offsetX ) / 1e6, ( startpoint.y - offsetY ) / 1e6 );
  305. fprintf( m_Outfile, "(layer %s) (width %f)\n )\n", aBrdLayerName, width );
  306. }
  307. break;
  308. case KICAD_LOGO:
  309. fprintf( m_Outfile, " (pts" );
  310. // Internal units = micron, file unit = mm
  311. jj = 0;
  312. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  313. {
  314. currpoint = aPolygon.CPoint( ii );
  315. fprintf( m_Outfile, " (xy %.3f %.3f)",
  316. ( currpoint.x - offsetX ) / 1e3,
  317. ( currpoint.y - offsetY ) / 1e3 );
  318. if( jj++ > 4 )
  319. {
  320. jj = 0;
  321. fprintf( m_Outfile, ("\n ") );
  322. }
  323. }
  324. // Close polygon
  325. fprintf( m_Outfile, " (xy %.3f %.3f) )\n",
  326. ( startpoint.x - offsetX ) / 1e3, ( startpoint.y - offsetY ) / 1e3 );
  327. break;
  328. case EESCHEMA_FMT:
  329. fprintf( m_Outfile, "P %d 0 0 1", (int) aPolygon.PointCount() + 1 );
  330. for( ii = 0; ii < aPolygon.PointCount(); ii++ )
  331. {
  332. currpoint = aPolygon.CPoint( ii );
  333. fprintf( m_Outfile, " %d %d",
  334. currpoint.x - offsetX, currpoint.y - offsetY );
  335. }
  336. // Close polygon
  337. fprintf( m_Outfile, " %d %d",
  338. startpoint.x - offsetX, startpoint.y - offsetY );
  339. fprintf( m_Outfile, " F\n" );
  340. break;
  341. }
  342. }
  343. void BITMAPCONV_INFO::CreateOutputFile( BMP2CMP_MOD_LAYER aModLayer )
  344. {
  345. std::vector <potrace_dpoint_t> cornersBuffer;
  346. // polyset_areas is a set of polygon to draw
  347. SHAPE_POLY_SET polyset_areas;
  348. // polyset_holes is the set of holes inside polyset_areas outlines
  349. SHAPE_POLY_SET polyset_holes;
  350. potrace_dpoint_t( *c )[3];
  351. LOCALE_IO toggle; // Temporary switch the locale to standard C to r/w floats
  352. // The layer name has meaning only for .kicad_mod files.
  353. // For these files the header creates 2 invisible texts: value and ref
  354. // (needed but not usefull) on silk screen layer
  355. OuputFileHeader( getBrdLayerName( MOD_LYR_FSILKS ) );
  356. bool main_outline = true;
  357. /* draw each as a polygon with no hole.
  358. * Bezier curves are approximated by a polyline
  359. */
  360. potrace_path_t* paths = m_Paths; // the list of paths
  361. while( paths != NULL )
  362. {
  363. int cnt = paths->curve.n;
  364. int* tag = paths->curve.tag;
  365. c = paths->curve.c;
  366. potrace_dpoint_t startpoint = c[cnt - 1][2];
  367. for( int i = 0; i < cnt; i++ )
  368. {
  369. switch( tag[i] )
  370. {
  371. case POTRACE_CORNER:
  372. cornersBuffer.push_back( c[i][1] );
  373. cornersBuffer.push_back( c[i][2] );
  374. startpoint = c[i][2];
  375. break;
  376. case POTRACE_CURVETO:
  377. BezierToPolyline( cornersBuffer, startpoint, c[i][0], c[i][1], c[i][2] );
  378. startpoint = c[i][2];
  379. break;
  380. }
  381. }
  382. // Store current path
  383. if( main_outline )
  384. {
  385. main_outline = false;
  386. // build the current main polygon
  387. polyset_areas.NewOutline();
  388. for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
  389. {
  390. polyset_areas.Append( int( cornersBuffer[i].x * m_ScaleX ),
  391. int( cornersBuffer[i].y * m_ScaleY ) );
  392. }
  393. }
  394. else
  395. {
  396. // Add current hole in polyset_holes
  397. polyset_holes.NewOutline();
  398. for( unsigned int i = 0; i < cornersBuffer.size(); i++ )
  399. {
  400. polyset_holes.Append( int( cornersBuffer[i].x * m_ScaleX ),
  401. int( cornersBuffer[i].y * m_ScaleY ) );
  402. }
  403. }
  404. cornersBuffer.clear();
  405. /* at the end of a group of a positive path and its negative children, fill.
  406. */
  407. if( paths->next == NULL || paths->next->sign == '+' )
  408. {
  409. // Substract holes to main polygon:
  410. polyset_areas.Simplify( SHAPE_POLY_SET::PM_FAST );
  411. polyset_holes.Simplify( SHAPE_POLY_SET::PM_FAST );
  412. polyset_areas.BooleanSubtract( polyset_holes, SHAPE_POLY_SET::PM_FAST );
  413. polyset_areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
  414. // Output current resulting polygon(s)
  415. for( int ii = 0; ii < polyset_areas.OutlineCount(); ii++ )
  416. {
  417. SHAPE_LINE_CHAIN& poly = polyset_areas.Outline( ii );
  418. OuputOnePolygon(poly, getBrdLayerName( aModLayer ) );
  419. }
  420. polyset_areas.RemoveAllContours();
  421. polyset_holes.RemoveAllContours();
  422. main_outline = true;
  423. }
  424. paths = paths->next;
  425. }
  426. OuputFileEnd();
  427. }
  428. // a helper function to calculate a square value
  429. inline double square( double x )
  430. {
  431. return x*x;
  432. }
  433. // a helper function to calculate a cube value
  434. inline double cube( double x )
  435. {
  436. return x*x*x;
  437. }
  438. /* render a Bezier curve. */
  439. void BezierToPolyline( std::vector <potrace_dpoint_t>& aCornersBuffer,
  440. potrace_dpoint_t p1,
  441. potrace_dpoint_t p2,
  442. potrace_dpoint_t p3,
  443. potrace_dpoint_t p4 )
  444. {
  445. double dd0, dd1, dd, delta, e2, epsilon, t;
  446. // p1 = starting point
  447. /* we approximate the curve by small line segments. The interval
  448. * size, epsilon, is determined on the fly so that the distance
  449. * between the true curve and its approximation does not exceed the
  450. * desired accuracy delta. */
  451. delta = 0.25; /* desired accuracy, in pixels */
  452. /* let dd = maximal value of 2nd derivative over curve - this must
  453. * occur at an endpoint. */
  454. dd0 = square( p1.x - 2 * p2.x + p3.x ) + square( p1.y - 2 * p2.y + p3.y );
  455. dd1 = square( p2.x - 2 * p3.x + p4.x ) + square( p2.y - 2 * p3.y + p4.y );
  456. dd = 6 * sqrt( std::max( dd0, dd1 ) );
  457. e2 = 8 * delta <= dd ? 8 * delta / dd : 1;
  458. epsilon = sqrt( e2 ); /* necessary interval size */
  459. for( t = epsilon; t<1; t += epsilon )
  460. {
  461. potrace_dpoint_t intermediate_point;
  462. intermediate_point.x = p1.x * cube( 1 - t ) +
  463. 3* p2.x* square( 1 - t ) * t +
  464. 3 * p3.x * (1 - t) * square( t ) +
  465. p4.x* cube( t );
  466. intermediate_point.y = p1.y * cube( 1 - t ) +
  467. 3* p2.y* square( 1 - t ) * t +
  468. 3 * p3.y * (1 - t) * square( t ) + p4.y* cube( t );
  469. aCornersBuffer.push_back( intermediate_point );
  470. }
  471. aCornersBuffer.push_back( p4 );
  472. }