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.

982 lines
29 KiB

  1. /**
  2. * @file DXF_plotter.cpp
  3. * @brief Kicad: specialized plotter for DXF files format
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. #include <fctsys.h>
  28. #include <gr_basic.h>
  29. #include <trigo.h>
  30. #include <eda_base_frame.h>
  31. #include <base_struct.h>
  32. #include <plotter.h>
  33. #include <macros.h>
  34. #include <kicad_string.h>
  35. #include <convert_basic_shapes_to_polygon.h>
  36. /**
  37. * Oblique angle for DXF native text
  38. * (I don't remember if 15 degrees is the ISO value... it looks nice anyway)
  39. */
  40. static const double DXF_OBLIQUE_ANGLE = 15;
  41. /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
  42. - The primary colors (1 - 9)
  43. - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
  44. - Greys (251 - 255)
  45. There is *no* black... the white does it on paper, usually, and
  46. anyway it depends on the plotter configuration, since DXF colors
  47. are meant to be logical only (they represent *both* line color and
  48. width); later version with plot styles only complicate the matter!
  49. As usual, brown and magenta/purple are difficult to place since
  50. they are actually variations of other colors.
  51. */
  52. static const struct
  53. {
  54. const char *name;
  55. int color;
  56. } dxf_layer[NBCOLORS] =
  57. {
  58. { "BLACK", 7 }, // In DXF, color 7 is *both* white and black!
  59. { "GRAY1", 251 },
  60. { "GRAY2", 8 },
  61. { "GRAY3", 9 },
  62. { "WHITE", 7 },
  63. { "LYELLOW", 51 },
  64. { "BLUE1", 178 },
  65. { "GREEN1", 98 },
  66. { "CYAN1", 138 },
  67. { "RED1", 18 },
  68. { "MAGENTA1", 228 },
  69. { "BROWN1", 58 },
  70. { "BLUE2", 5 },
  71. { "GREEN2", 3 },
  72. { "CYAN2", 4 },
  73. { "RED2", 1 },
  74. { "MAGENTA2", 6 },
  75. { "BROWN2", 54 },
  76. { "BLUE3", 171 },
  77. { "GREEN3", 91 },
  78. { "CYAN3", 131 },
  79. { "RED3", 11 },
  80. { "MAGENTA3", 221 },
  81. { "YELLOW3", 2 },
  82. { "BLUE4", 5 },
  83. { "GREEN4", 3 },
  84. { "CYAN4", 4 },
  85. { "RED4", 1 },
  86. { "MAGENTA4", 6 },
  87. { "YELLOW4", 2 }
  88. };
  89. static const char* getDXFLineType( PlotDashType aType )
  90. {
  91. switch( aType )
  92. {
  93. case PLOTDASHTYPE_SOLID: return "CONTINUOUS";
  94. case PLOTDASHTYPE_DASH: return "DASHED";
  95. case PLOTDASHTYPE_DOT: return "DOTTED";
  96. case PLOTDASHTYPE_DASHDOT: return "DASHDOT";
  97. }
  98. wxFAIL_MSG( "Unhandled PlotDashType" );
  99. return "CONTINUOUS";
  100. }
  101. // A helper function to create a color name acceptable in DXF files
  102. // DXF files do not use a RGB definition
  103. static wxString getDXFColorName( COLOR4D aColor )
  104. {
  105. EDA_COLOR_T color = ColorFindNearest( int( aColor.r*255 ),
  106. int( aColor.g*255 ),
  107. int( aColor.b*255 ) );
  108. wxString cname( dxf_layer[color].name );
  109. return cname;
  110. }
  111. /**
  112. * Set the scale/position for the DXF plot
  113. * The DXF engine doesn't support line widths and mirroring. The output
  114. * coordinate system is in the first quadrant (in mm)
  115. */
  116. void DXF_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
  117. double aScale, bool aMirror )
  118. {
  119. plotOffset = aOffset;
  120. plotScale = aScale;
  121. /* DXF paper is 'virtual' so there is no need of a paper size.
  122. Also this way we can handle the aux origin which can be useful
  123. (for example when aligning to a mechanical drawing) */
  124. paperSize.x = 0;
  125. paperSize.y = 0;
  126. /* Like paper size DXF units are abstract too. Anyway there is a
  127. * system variable (MEASUREMENT) which will be set to 1 to indicate
  128. * metric units */
  129. m_IUsPerDecimil = aIusPerDecimil;
  130. iuPerDeviceUnit = 1.0 / aIusPerDecimil; // Gives a DXF in decimils
  131. iuPerDeviceUnit *= 0.00254; // ... now in mm
  132. SetDefaultLineWidth( 0 ); // No line width on DXF
  133. m_plotMirror = false; // No mirroring on DXF
  134. m_currentColor = COLOR4D::BLACK;
  135. }
  136. /**
  137. * Opens the DXF plot with a skeleton header
  138. */
  139. bool DXF_PLOTTER::StartPlot()
  140. {
  141. wxASSERT( outputFile );
  142. // DXF HEADER - Boilerplate
  143. // Defines the minimum for drawing i.e. the angle system and the
  144. // 4 linetypes (CONTINUOUS, DOTDASH, DASHED and DOTTED)
  145. fputs( " 0\n"
  146. "SECTION\n"
  147. " 2\n"
  148. "HEADER\n"
  149. " 9\n"
  150. "$ANGBASE\n"
  151. " 50\n"
  152. "0.0\n"
  153. " 9\n"
  154. "$ANGDIR\n"
  155. " 70\n"
  156. " 1\n"
  157. " 9\n"
  158. "$MEASUREMENT\n"
  159. " 70\n"
  160. "0\n"
  161. " 0\n" // This means 'metric units'
  162. "ENDSEC\n"
  163. " 0\n"
  164. "SECTION\n"
  165. " 2\n"
  166. "TABLES\n"
  167. " 0\n"
  168. "TABLE\n"
  169. " 2\n"
  170. "LTYPE\n"
  171. " 70\n"
  172. "4\n"
  173. " 0\n"
  174. "LTYPE\n"
  175. " 5\n"
  176. "40F\n"
  177. " 2\n"
  178. "CONTINUOUS\n"
  179. " 70\n"
  180. "0\n"
  181. " 3\n"
  182. "Solid line\n"
  183. " 72\n"
  184. "65\n"
  185. " 73\n"
  186. "0\n"
  187. " 40\n"
  188. "0.0\n"
  189. " 0\n"
  190. "LTYPE\n"
  191. " 5\n"
  192. "410\n"
  193. " 2\n"
  194. "DASHDOT\n"
  195. " 70\n"
  196. "0\n"
  197. " 3\n"
  198. "Dash Dot ____ _ ____ _\n"
  199. " 72\n"
  200. "65\n"
  201. " 73\n"
  202. "4\n"
  203. " 40\n"
  204. "2.0\n"
  205. " 49\n"
  206. "1.25\n"
  207. " 49\n"
  208. "-0.25\n"
  209. " 49\n"
  210. "0.25\n"
  211. " 49\n"
  212. "-0.25\n"
  213. " 0\n"
  214. "LTYPE\n"
  215. " 5\n"
  216. "411\n"
  217. " 2\n"
  218. "DASHED\n"
  219. " 70\n"
  220. "0\n"
  221. " 3\n"
  222. "Dashed __ __ __ __ __\n"
  223. " 72\n"
  224. "65\n"
  225. " 73\n"
  226. "2\n"
  227. " 40\n"
  228. "0.75\n"
  229. " 49\n"
  230. "0.5\n"
  231. " 49\n"
  232. "-0.25\n"
  233. " 0\n"
  234. "LTYPE\n"
  235. " 5\n"
  236. "43B\n"
  237. " 2\n"
  238. "DOTTED\n"
  239. " 70\n"
  240. "0\n"
  241. " 3\n"
  242. "Dotted . . . .\n"
  243. " 72\n"
  244. "65\n"
  245. " 73\n"
  246. "2\n"
  247. " 40\n"
  248. "0.2\n"
  249. " 49\n"
  250. "0.0\n"
  251. " 49\n"
  252. "-0.2\n"
  253. " 0\n"
  254. "ENDTAB\n",
  255. outputFile );
  256. // Text styles table
  257. // Defines 4 text styles, one for each bold/italic combination
  258. fputs( " 0\n"
  259. "TABLE\n"
  260. " 2\n"
  261. "STYLE\n"
  262. " 70\n"
  263. "4\n", outputFile );
  264. static const char *style_name[4] = {"KICAD", "KICADB", "KICADI", "KICADBI"};
  265. for(int i = 0; i < 4; i++ )
  266. {
  267. fprintf( outputFile,
  268. " 0\n"
  269. "STYLE\n"
  270. " 2\n"
  271. "%s\n" // Style name
  272. " 70\n"
  273. "0\n" // Standard flags
  274. " 40\n"
  275. "0\n" // Non-fixed height text
  276. " 41\n"
  277. "1\n" // Width factor (base)
  278. " 42\n"
  279. "1\n" // Last height (mandatory)
  280. " 50\n"
  281. "%g\n" // Oblique angle
  282. " 71\n"
  283. "0\n" // Generation flags (default)
  284. " 3\n"
  285. // The standard ISO font (when kicad is build with it
  286. // the dxf text in acad matches *perfectly*)
  287. "isocp.shx\n", // Font name (when not bigfont)
  288. // Apply a 15 degree angle to italic text
  289. style_name[i], i < 2 ? 0 : DXF_OBLIQUE_ANGLE );
  290. }
  291. // Layer table - one layer per color
  292. fprintf( outputFile,
  293. " 0\n"
  294. "ENDTAB\n"
  295. " 0\n"
  296. "TABLE\n"
  297. " 2\n"
  298. "LAYER\n"
  299. " 70\n"
  300. "%d\n", NBCOLORS );
  301. /* The layer/colors palette. The acad/DXF palette is divided in 3 zones:
  302. - The primary colors (1 - 9)
  303. - An HSV zone (10-250, 5 values x 2 saturations x 10 hues
  304. - Greys (251 - 255)
  305. */
  306. for( EDA_COLOR_T i = BLACK; i < NBCOLORS; i = NextColor(i) )
  307. {
  308. fprintf( outputFile,
  309. " 0\n"
  310. "LAYER\n"
  311. " 2\n"
  312. "%s\n" // Layer name
  313. " 70\n"
  314. "0\n" // Standard flags
  315. " 62\n"
  316. "%d\n" // Color number
  317. " 6\n"
  318. "CONTINUOUS\n",// Linetype name
  319. dxf_layer[i].name, dxf_layer[i].color );
  320. }
  321. // End of layer table, begin entities
  322. fputs( " 0\n"
  323. "ENDTAB\n"
  324. " 0\n"
  325. "ENDSEC\n"
  326. " 0\n"
  327. "SECTION\n"
  328. " 2\n"
  329. "ENTITIES\n", outputFile );
  330. return true;
  331. }
  332. bool DXF_PLOTTER::EndPlot()
  333. {
  334. wxASSERT( outputFile );
  335. // DXF FOOTER
  336. fputs( " 0\n"
  337. "ENDSEC\n"
  338. " 0\n"
  339. "EOF\n", outputFile );
  340. fclose( outputFile );
  341. outputFile = NULL;
  342. return true;
  343. }
  344. /**
  345. * The DXF exporter handles 'colors' as layers...
  346. */
  347. void DXF_PLOTTER::SetColor( COLOR4D color )
  348. {
  349. if( ( colorMode )
  350. || ( color == COLOR4D::BLACK )
  351. || ( color == COLOR4D::WHITE ) )
  352. {
  353. m_currentColor = color;
  354. }
  355. else
  356. m_currentColor = COLOR4D::BLACK;
  357. }
  358. /**
  359. * DXF rectangle: fill not supported
  360. */
  361. void DXF_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
  362. {
  363. wxASSERT( outputFile );
  364. MoveTo( p1 );
  365. LineTo( wxPoint( p1.x, p2.y ) );
  366. LineTo( wxPoint( p2.x, p2.y ) );
  367. LineTo( wxPoint( p2.x, p1.y ) );
  368. FinishTo( wxPoint( p1.x, p1.y ) );
  369. }
  370. /**
  371. * DXF circle: full functionality; it even does 'fills' drawing a
  372. * circle with a dual-arc polyline wide as the radius.
  373. *
  374. * I could use this trick to do other filled primitives
  375. */
  376. void DXF_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill, int width )
  377. {
  378. wxASSERT( outputFile );
  379. double radius = userToDeviceSize( diameter / 2 );
  380. DPOINT centre_dev = userToDeviceCoordinates( centre );
  381. if( radius > 0 )
  382. {
  383. wxString cname = getDXFColorName( m_currentColor );
  384. if( !fill )
  385. {
  386. fprintf( outputFile, "0\nCIRCLE\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n",
  387. TO_UTF8( cname ),
  388. centre_dev.x, centre_dev.y, radius );
  389. }
  390. if( fill == FILLED_SHAPE )
  391. {
  392. double r = radius*0.5;
  393. fprintf( outputFile, "0\nPOLYLINE\n");
  394. fprintf( outputFile, "8\n%s\n66\n1\n70\n1\n", TO_UTF8( cname ));
  395. fprintf( outputFile, "40\n%g\n41\n%g\n", radius, radius);
  396. fprintf( outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ));
  397. fprintf( outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
  398. centre_dev.x-r, centre_dev.y );
  399. fprintf( outputFile, "0\nVERTEX\n8\n%s\n", TO_UTF8( cname ));
  400. fprintf( outputFile, "10\n%g\n 20\n%g\n42\n1.0\n",
  401. centre_dev.x+r, centre_dev.y );
  402. fprintf( outputFile, "0\nSEQEND\n");
  403. }
  404. }
  405. }
  406. /**
  407. * DXF polygon: doesn't fill it but at least it close the filled ones
  408. * DXF does not know thick outline.
  409. * It does not know thhick segments, therefore filled polygons with thick outline
  410. * are converted to inflated polygon by aWidth/2
  411. */
  412. void DXF_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
  413. FILL_T aFill, int aWidth, void * aData )
  414. {
  415. if( aCornerList.size() <= 1 )
  416. return;
  417. unsigned last = aCornerList.size() - 1;
  418. // Plot outlines with lines (thickness = 0) to define the polygon
  419. if( aWidth <= 0 )
  420. {
  421. MoveTo( aCornerList[0] );
  422. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  423. LineTo( aCornerList[ii] );
  424. // Close polygon if 'fill' requested
  425. if( aFill )
  426. {
  427. if( aCornerList[last] != aCornerList[0] )
  428. LineTo( aCornerList[0] );
  429. }
  430. PenFinish();
  431. return;
  432. }
  433. // if the polygon outline has thickness, and is not filled
  434. // (i.e. is a polyline) plot outlines with thick segments
  435. if( aWidth > 0 && !aFill )
  436. {
  437. MoveTo( aCornerList[0] );
  438. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  439. ThickSegment( aCornerList[ii-1], aCornerList[ii],
  440. aWidth, FILLED, NULL );
  441. return;
  442. }
  443. // The polygon outline has thickness, and is filled
  444. // Build and plot the polygon which contains the initial
  445. // polygon and its thick outline
  446. SHAPE_POLY_SET bufferOutline;
  447. SHAPE_POLY_SET bufferPolybase;
  448. const int circleToSegmentsCount = 16;
  449. bufferPolybase.NewOutline();
  450. // enter outline as polygon:
  451. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  452. {
  453. TransformRoundedEndsSegmentToPolygon( bufferOutline,
  454. aCornerList[ii-1], aCornerList[ii], circleToSegmentsCount, aWidth );
  455. }
  456. // enter the initial polygon:
  457. for( unsigned ii = 0; ii < aCornerList.size(); ii++ )
  458. {
  459. bufferPolybase.Append( aCornerList[ii] );
  460. }
  461. // Merge polygons to build the polygon which contains the initial
  462. // polygon and its thick outline
  463. // create the outline which contains thick outline:
  464. bufferPolybase.BooleanAdd( bufferOutline, SHAPE_POLY_SET::PM_FAST );
  465. bufferPolybase.Fracture( SHAPE_POLY_SET::PM_FAST );
  466. if( bufferPolybase.OutlineCount() < 1 ) // should not happen
  467. return;
  468. const SHAPE_LINE_CHAIN& path = bufferPolybase.COutline( 0 );
  469. if( path.PointCount() < 2 ) // should not happen
  470. return;
  471. // Now, output the final polygon to DXF file:
  472. last = path.PointCount() - 1;
  473. VECTOR2I point = path.CPoint( 0 );
  474. wxPoint startPoint( point.x, point.y );
  475. MoveTo( startPoint );
  476. for( int ii = 1; ii < path.PointCount(); ii++ )
  477. {
  478. point = path.CPoint( ii );
  479. LineTo( wxPoint( point.x, point.y ) );
  480. }
  481. // Close polygon, if needed
  482. point = path.CPoint( last );
  483. wxPoint endPoint( point.x, point.y );
  484. if( endPoint != startPoint )
  485. LineTo( startPoint );
  486. PenFinish();
  487. }
  488. void DXF_PLOTTER::PenTo( const wxPoint& pos, char plume )
  489. {
  490. wxASSERT( outputFile );
  491. if( plume == 'Z' )
  492. {
  493. return;
  494. }
  495. DPOINT pos_dev = userToDeviceCoordinates( pos );
  496. DPOINT pen_lastpos_dev = userToDeviceCoordinates( penLastpos );
  497. if( penLastpos != pos && plume == 'D' )
  498. {
  499. wxASSERT( m_currentLineType >= 0 && m_currentLineType < 4 );
  500. // DXF LINE
  501. wxString cname = getDXFColorName( m_currentColor );
  502. const char *lname = getDXFLineType( (PlotDashType) m_currentLineType );
  503. fprintf( outputFile, "0\nLINE\n8\n%s\n6\n%s\n10\n%g\n20\n%g\n11\n%g\n21\n%g\n",
  504. TO_UTF8( cname ), lname,
  505. pen_lastpos_dev.x, pen_lastpos_dev.y, pos_dev.x, pos_dev.y );
  506. }
  507. penLastpos = pos;
  508. }
  509. void DXF_PLOTTER::SetDash( int dashed )
  510. {
  511. wxASSERT( dashed >= 0 && dashed < 4 );
  512. m_currentLineType = dashed;
  513. }
  514. void DXF_PLOTTER::ThickSegment( const wxPoint& aStart, const wxPoint& aEnd, int aWidth,
  515. EDA_DRAW_MODE_T aPlotMode, void* aData )
  516. {
  517. if( aPlotMode == SKETCH )
  518. {
  519. std::vector<wxPoint> cornerList;
  520. SHAPE_POLY_SET outlineBuffer;
  521. TransformOvalClearanceToPolygon( outlineBuffer,
  522. aStart, aEnd, aWidth, 32 , 1.0 );
  523. const SHAPE_LINE_CHAIN& path = outlineBuffer.COutline(0 );
  524. for( int jj = 0; jj < path.PointCount(); jj++ )
  525. cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) );
  526. // Ensure the polygon is closed
  527. if( cornerList[0] != cornerList[cornerList.size() - 1] )
  528. cornerList.push_back( cornerList[0] );
  529. PlotPoly( cornerList, NO_FILL );
  530. }
  531. else
  532. {
  533. MoveTo( aStart );
  534. FinishTo( aEnd );
  535. }
  536. }
  537. /* Plot an arc in DXF format
  538. * Filling is not supported
  539. */
  540. void DXF_PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
  541. FILL_T fill, int width )
  542. {
  543. wxASSERT( outputFile );
  544. if( radius <= 0 )
  545. return;
  546. // In DXF, arcs are drawn CCW.
  547. // In Kicad, arcs are CW or CCW
  548. // If StAngle > EndAngle, it is CW. So transform it to CCW
  549. if( StAngle > EndAngle )
  550. {
  551. std::swap( StAngle, EndAngle );
  552. }
  553. DPOINT centre_dev = userToDeviceCoordinates( centre );
  554. double radius_dev = userToDeviceSize( radius );
  555. // Emit a DXF ARC entity
  556. wxString cname = getDXFColorName( m_currentColor );
  557. fprintf( outputFile,
  558. "0\nARC\n8\n%s\n10\n%g\n20\n%g\n40\n%g\n50\n%g\n51\n%g\n",
  559. TO_UTF8( cname ),
  560. centre_dev.x, centre_dev.y, radius_dev,
  561. StAngle / 10.0, EndAngle / 10.0 );
  562. }
  563. /**
  564. * DXF oval pad: always done in sketch mode
  565. */
  566. void DXF_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, double orient,
  567. EDA_DRAW_MODE_T trace_mode, void* aData )
  568. {
  569. wxASSERT( outputFile );
  570. wxSize size( aSize );
  571. /* The chip is reduced to an oval tablet with size.y > size.x
  572. * (Oval vertical orientation 0) */
  573. if( size.x > size.y )
  574. {
  575. std::swap( size.x, size.y );
  576. orient = AddAngles( orient, 900 );
  577. }
  578. sketchOval( pos, size, orient, -1 );
  579. }
  580. /**
  581. * DXF round pad: always done in sketch mode; it could be filled but it isn't
  582. * pretty if other kinds of pad aren't...
  583. */
  584. void DXF_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre,
  585. EDA_DRAW_MODE_T trace_mode, void* aData )
  586. {
  587. wxASSERT( outputFile );
  588. Circle( pos, diametre, NO_FILL );
  589. }
  590. /**
  591. * DXF rectangular pad: alwayd done in sketch mode
  592. */
  593. void DXF_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize,
  594. double orient, EDA_DRAW_MODE_T trace_mode, void* aData )
  595. {
  596. wxASSERT( outputFile );
  597. wxSize size;
  598. int ox, oy, fx, fy;
  599. size.x = padsize.x / 2;
  600. size.y = padsize.y / 2;
  601. if( size.x < 0 )
  602. size.x = 0;
  603. if( size.y < 0 )
  604. size.y = 0;
  605. // If a dimension is zero, the trace is reduced to 1 line
  606. if( size.x == 0 )
  607. {
  608. ox = pos.x;
  609. oy = pos.y - size.y;
  610. RotatePoint( &ox, &oy, pos.x, pos.y, orient );
  611. fx = pos.x;
  612. fy = pos.y + size.y;
  613. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  614. MoveTo( wxPoint( ox, oy ) );
  615. FinishTo( wxPoint( fx, fy ) );
  616. return;
  617. }
  618. if( size.y == 0 )
  619. {
  620. ox = pos.x - size.x;
  621. oy = pos.y;
  622. RotatePoint( &ox, &oy, pos.x, pos.y, orient );
  623. fx = pos.x + size.x;
  624. fy = pos.y;
  625. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  626. MoveTo( wxPoint( ox, oy ) );
  627. FinishTo( wxPoint( fx, fy ) );
  628. return;
  629. }
  630. ox = pos.x - size.x;
  631. oy = pos.y - size.y;
  632. RotatePoint( &ox, &oy, pos.x, pos.y, orient );
  633. MoveTo( wxPoint( ox, oy ) );
  634. fx = pos.x - size.x;
  635. fy = pos.y + size.y;
  636. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  637. LineTo( wxPoint( fx, fy ) );
  638. fx = pos.x + size.x;
  639. fy = pos.y + size.y;
  640. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  641. LineTo( wxPoint( fx, fy ) );
  642. fx = pos.x + size.x;
  643. fy = pos.y - size.y;
  644. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  645. LineTo( wxPoint( fx, fy ) );
  646. FinishTo( wxPoint( ox, oy ) );
  647. }
  648. void DXF_PLOTTER::FlashPadRoundRect( const wxPoint& aPadPos, const wxSize& aSize,
  649. int aCornerRadius, double aOrient,
  650. EDA_DRAW_MODE_T aTraceMode, void* aData )
  651. {
  652. SHAPE_POLY_SET outline;
  653. const int segmentToCircleCount = 64;
  654. TransformRoundRectToPolygon( outline, aPadPos, aSize, aOrient,
  655. aCornerRadius, segmentToCircleCount );
  656. // TransformRoundRectToPolygon creates only one convex polygon
  657. SHAPE_LINE_CHAIN& poly = outline.Outline( 0 );
  658. MoveTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) );
  659. for( int ii = 1; ii < poly.PointCount(); ++ii )
  660. LineTo( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) );
  661. FinishTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) );
  662. }
  663. void DXF_PLOTTER::FlashPadCustom( const wxPoint& aPadPos, const wxSize& aSize,
  664. SHAPE_POLY_SET* aPolygons,
  665. EDA_DRAW_MODE_T aTraceMode, void* aData )
  666. {
  667. for( int cnt = 0; cnt < aPolygons->OutlineCount(); ++cnt )
  668. {
  669. SHAPE_LINE_CHAIN& poly = aPolygons->Outline( cnt );
  670. MoveTo( wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) );
  671. for( int ii = 1; ii < poly.PointCount(); ++ii )
  672. LineTo( wxPoint( poly.Point( ii ).x, poly.Point( ii ).y ) );
  673. FinishTo(wxPoint( poly.Point( 0 ).x, poly.Point( 0 ).y ) );
  674. }
  675. }
  676. /**
  677. * DXF trapezoidal pad: only sketch mode is supported
  678. */
  679. void DXF_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint *aCorners,
  680. double aPadOrient, EDA_DRAW_MODE_T aTrace_Mode, void* aData )
  681. {
  682. wxASSERT( outputFile );
  683. wxPoint coord[4]; /* coord actual corners of a trapezoidal trace */
  684. for( int ii = 0; ii < 4; ii++ )
  685. {
  686. coord[ii] = aCorners[ii];
  687. RotatePoint( &coord[ii], aPadOrient );
  688. coord[ii] += aPadPos;
  689. }
  690. // Plot edge:
  691. MoveTo( coord[0] );
  692. LineTo( coord[1] );
  693. LineTo( coord[2] );
  694. LineTo( coord[3] );
  695. FinishTo( coord[0] );
  696. }
  697. /**
  698. * Checks if a given string contains non-ASCII characters.
  699. * FIXME: the performance of this code is really poor, but in this case it can be
  700. * acceptable because the plot operation is not called very often.
  701. * @param string String to check
  702. * @return true if it contains some non-ASCII character, false if all characters are
  703. * inside ASCII range (<=255).
  704. */
  705. bool containsNonAsciiChars( const wxString& string )
  706. {
  707. for( unsigned i = 0; i < string.length(); i++ )
  708. {
  709. wchar_t ch = string[i];
  710. if( ch > 255 )
  711. return true;
  712. }
  713. return false;
  714. }
  715. void DXF_PLOTTER::Text( const wxPoint& aPos,
  716. COLOR4D aColor,
  717. const wxString& aText,
  718. double aOrient,
  719. const wxSize& aSize,
  720. enum EDA_TEXT_HJUSTIFY_T aH_justify,
  721. enum EDA_TEXT_VJUSTIFY_T aV_justify,
  722. int aWidth,
  723. bool aItalic,
  724. bool aBold,
  725. bool aMultilineAllowed,
  726. void* aData )
  727. {
  728. // Fix me: see how to use DXF text mode for multiline texts
  729. if( aMultilineAllowed && !aText.Contains( wxT( "\n" ) ) )
  730. aMultilineAllowed = false; // the text has only one line.
  731. if( textAsLines || containsNonAsciiChars( aText ) || aMultilineAllowed )
  732. {
  733. // output text as graphics.
  734. // Perhaps multiline texts could be handled as DXF text entity
  735. // but I do not want spend time about this (JPC)
  736. PLOTTER::Text( aPos, aColor, aText, aOrient, aSize, aH_justify, aV_justify,
  737. aWidth, aItalic, aBold, aMultilineAllowed );
  738. }
  739. else
  740. {
  741. /* Emit text as a text entity. This loses formatting and shape but it's
  742. more useful as a CAD object */
  743. DPOINT origin_dev = userToDeviceCoordinates( aPos );
  744. SetColor( aColor );
  745. wxString cname = getDXFColorName( m_currentColor );
  746. DPOINT size_dev = userToDeviceSize( aSize );
  747. int h_code = 0, v_code = 0;
  748. switch( aH_justify )
  749. {
  750. case GR_TEXT_HJUSTIFY_LEFT:
  751. h_code = 0;
  752. break;
  753. case GR_TEXT_HJUSTIFY_CENTER:
  754. h_code = 1;
  755. break;
  756. case GR_TEXT_HJUSTIFY_RIGHT:
  757. h_code = 2;
  758. break;
  759. }
  760. switch( aV_justify )
  761. {
  762. case GR_TEXT_VJUSTIFY_TOP:
  763. v_code = 3;
  764. break;
  765. case GR_TEXT_VJUSTIFY_CENTER:
  766. v_code = 2;
  767. break;
  768. case GR_TEXT_VJUSTIFY_BOTTOM:
  769. v_code = 1;
  770. break;
  771. }
  772. // Position, size, rotation and alignment
  773. // The two alignment point usages is somewhat idiot (see the DXF ref)
  774. // Anyway since we don't use the fit/aligned options, they're the same
  775. fprintf( outputFile,
  776. " 0\n"
  777. "TEXT\n"
  778. " 7\n"
  779. "%s\n" // Text style
  780. " 8\n"
  781. "%s\n" // Layer name
  782. " 10\n"
  783. "%g\n" // First point X
  784. " 11\n"
  785. "%g\n" // Second point X
  786. " 20\n"
  787. "%g\n" // First point Y
  788. " 21\n"
  789. "%g\n" // Second point Y
  790. " 40\n"
  791. "%g\n" // Text height
  792. " 41\n"
  793. "%g\n" // Width factor
  794. " 50\n"
  795. "%g\n" // Rotation
  796. " 51\n"
  797. "%g\n" // Oblique angle
  798. " 71\n"
  799. "%d\n" // Mirror flags
  800. " 72\n"
  801. "%d\n" // H alignment
  802. " 73\n"
  803. "%d\n", // V alignment
  804. aBold ? (aItalic ? "KICADBI" : "KICADB")
  805. : (aItalic ? "KICADI" : "KICAD"),
  806. TO_UTF8( cname ),
  807. origin_dev.x, origin_dev.x,
  808. origin_dev.y, origin_dev.y,
  809. size_dev.y, fabs( size_dev.x / size_dev.y ),
  810. aOrient / 10.0,
  811. aItalic ? DXF_OBLIQUE_ANGLE : 0,
  812. size_dev.x < 0 ? 2 : 0, // X mirror flag
  813. h_code, v_code );
  814. /* There are two issue in emitting the text:
  815. - Our overline character (~) must be converted to the appropriate
  816. control sequence %%O or %%o
  817. - Text encoding in DXF is more or less unspecified since depends on
  818. the DXF declared version, the acad version reading it *and* some
  819. system variables to be put in the header handled only by newer acads
  820. Also before R15 unicode simply is not supported (you need to use
  821. bigfonts which are a massive PITA). Common denominator solution:
  822. use Latin1 (and however someone could choke on it, anyway). Sorry
  823. for the extended latin people. If somewant want to try fixing this
  824. recent version seems to use UTF-8 (and not UCS2 like the rest of
  825. Windows)
  826. XXX Actually there is a *third* issue: older DXF formats are limited
  827. to 255 bytes records (it was later raised to 2048); since I'm lazy
  828. and text so long is not probable I just don't implement this rule.
  829. If someone is interested in fixing this, you have to emit the first
  830. partial lines with group code 3 (max 250 bytes each) and then finish
  831. with a group code 1 (less than 250 bytes). The DXF refs explains it
  832. in no more details...
  833. */
  834. bool overlining = false;
  835. fputs( " 1\n", outputFile );
  836. for( unsigned i = 0; i < aText.length(); i++ )
  837. {
  838. /* Here I do a bad thing: writing the output one byte at a time!
  839. but today I'm lazy and I have no idea on how to coerce a Unicode
  840. wxString to spit out latin1 encoded text ...
  841. Atleast stdio is *supposed* to do output buffering, so there is
  842. hope is not too slow */
  843. wchar_t ch = aText[i];
  844. if( ch > 255 )
  845. {
  846. // I can't encode this...
  847. putc( '?', outputFile );
  848. }
  849. else
  850. {
  851. if( ch == '~' )
  852. {
  853. // Handle the overline toggle
  854. fputs( overlining ? "%%o" : "%%O", outputFile );
  855. overlining = !overlining;
  856. }
  857. else
  858. {
  859. putc( ch, outputFile );
  860. }
  861. }
  862. }
  863. putc( '\n', outputFile );
  864. }
  865. }