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.

559 lines
14 KiB

  1. /**
  2. * @file class_plotter.cpp
  3. * @brief KiCad: Base of all the plot routines
  4. * the class PLOTTER handle basic functions to plot schematic and boards
  5. * with different plot formats.
  6. *
  7. * There are currently engines for:
  8. * HPGL
  9. * POSTSCRIPT
  10. * GERBER
  11. * DXF
  12. * an SVG 'plot' is also provided along with the 'print' function by wx, but
  13. * is not handled here.
  14. */
  15. #include <fctsys.h>
  16. #include <trigo.h>
  17. #include <wxstruct.h>
  18. #include <base_struct.h>
  19. #include <common.h>
  20. #include <plot_common.h>
  21. #include <macros.h>
  22. #include <class_base_screen.h>
  23. #include <drawtxt.h>
  24. PLOTTER::PLOTTER( )
  25. {
  26. plotScale = 1;
  27. defaultPenWidth = 0;
  28. currentPenWidth = -1; // To-be-set marker
  29. penState = 'Z'; // End-of-path idle
  30. m_plotMirror = false; // Mirror flag
  31. m_mirrorIsHorizontal = true;
  32. m_yaxisReversed = false;
  33. outputFile = 0;
  34. colorMode = false; // Starts as a BW plot
  35. negativeMode = false;
  36. }
  37. PLOTTER::~PLOTTER()
  38. {
  39. // Emergency cleanup, but closing the file is
  40. // usually made in EndPlot().
  41. if( outputFile )
  42. {
  43. fclose( outputFile );
  44. }
  45. }
  46. /*
  47. * Open or create the plot file aFullFilename
  48. * return true if success, false if the file connot be created/opened
  49. *
  50. * Virtual because some plotters use ascii files, some others binary files (PDF)
  51. * The base class open the file in text mode
  52. */
  53. bool PLOTTER::OpenFile( const wxString& aFullFilename )
  54. {
  55. filename = aFullFilename;
  56. wxASSERT( !outputFile );
  57. // Open the file in text mode (not suitable for all plotters
  58. // but only for most of them
  59. outputFile = wxFopen( filename, wxT( "wt" ) );
  60. if( outputFile == NULL )
  61. return false ;
  62. return true;
  63. }
  64. /**
  65. * Modifies coordinates according to the orientation,
  66. * scale factor, and offsets trace. Also convert from a wxPoint to DPOINT,
  67. * since some output engines needs floating point coordinates.
  68. */
  69. DPOINT PLOTTER::userToDeviceCoordinates( const wxPoint& aCoordinate )
  70. {
  71. wxPoint pos = aCoordinate - plotOffset;
  72. double x = pos.x * plotScale;
  73. double y = ( paperSize.y - pos.y * plotScale );
  74. if( m_plotMirror )
  75. {
  76. if( m_mirrorIsHorizontal )
  77. x = ( paperSize.x - pos.x * plotScale );
  78. else
  79. y = pos.y * plotScale;
  80. }
  81. if( m_yaxisReversed )
  82. y = paperSize.y - y;
  83. x *= iuPerDeviceUnit;
  84. y *= iuPerDeviceUnit;
  85. return DPOINT( x, y );
  86. }
  87. /**
  88. * Modifies size according to the plotter scale factors
  89. * (wxSize version, returns a DPOINT)
  90. */
  91. DPOINT PLOTTER::userToDeviceSize( const wxSize& size )
  92. {
  93. return DPOINT( size.x * plotScale * iuPerDeviceUnit,
  94. size.y * plotScale * iuPerDeviceUnit );
  95. }
  96. /**
  97. * Modifies size according to the plotter scale factors
  98. * (simple double version)
  99. */
  100. double PLOTTER::userToDeviceSize( double size )
  101. {
  102. return size * plotScale * iuPerDeviceUnit;
  103. }
  104. /**
  105. * Generic fallback: arc rendered as a polyline
  106. */
  107. void PLOTTER::Arc( const wxPoint& centre, double StAngle, double EndAngle, int radius,
  108. FILL_T fill, int width )
  109. {
  110. wxPoint start, end;
  111. const int delta = 50; // increment (in 0.1 degrees) to draw circles
  112. if( StAngle > EndAngle )
  113. EXCHG( StAngle, EndAngle );
  114. SetCurrentLineWidth( width );
  115. /* Please NOTE the different sign due to Y-axis flip */
  116. start.x = centre.x + KiROUND( cosdecideg( radius, -StAngle ) );
  117. start.y = centre.y + KiROUND( sindecideg( radius, -StAngle ) );
  118. MoveTo( start );
  119. for( int ii = StAngle + delta; ii < EndAngle; ii += delta )
  120. {
  121. end.x = centre.x + KiROUND( cosdecideg( radius, -ii ) );
  122. end.y = centre.y + KiROUND( sindecideg( radius, -ii ) );
  123. LineTo( end );
  124. }
  125. end.x = centre.x + KiROUND( cosdecideg( radius, -EndAngle ) );
  126. end.y = centre.y + KiROUND( sindecideg( radius, -EndAngle ) );
  127. FinishTo( end );
  128. }
  129. /**
  130. * Fallback: if it doesn't handle bitmaps, we plot a rectangle
  131. */
  132. void PLOTTER::PlotImage(const wxImage & aImage, const wxPoint& aPos,
  133. double aScaleFactor )
  134. {
  135. wxSize size( aImage.GetWidth() * aScaleFactor,
  136. aImage.GetHeight() * aScaleFactor );
  137. wxPoint start = aPos;
  138. start.x -= size.x / 2;
  139. start.y -= size.y / 2;
  140. wxPoint end = start;
  141. end.x += size.x;
  142. end.y += size.y;
  143. Rect( start, end, NO_FILL );
  144. }
  145. /**
  146. * Plot a square centered on the position. Building block for markers
  147. */
  148. void PLOTTER::markerSquare( const wxPoint& position, int radius )
  149. {
  150. double r = KiROUND( radius / 1.4142 );
  151. std::vector< wxPoint > corner_list;
  152. wxPoint corner;
  153. corner.x = position.x + r;
  154. corner.y = position.y + r;
  155. corner_list.push_back( corner );
  156. corner.x = position.x + r;
  157. corner.y = position.y - r;
  158. corner_list.push_back( corner );
  159. corner.x = position.x - r;
  160. corner.y = position.y - r;
  161. corner_list.push_back( corner );
  162. corner.x = position.x - r;
  163. corner.y = position.y + r;
  164. corner_list.push_back( corner );
  165. corner.x = position.x + r;
  166. corner.y = position.y + r;
  167. corner_list.push_back( corner );
  168. PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() );
  169. }
  170. /**
  171. * Plot a circle centered on the position. Building block for markers
  172. */
  173. void PLOTTER::markerCircle( const wxPoint& position, int radius )
  174. {
  175. Circle( position, radius * 2, NO_FILL, GetCurrentLineWidth() );
  176. }
  177. /**
  178. * Plot a lozenge centered on the position. Building block for markers
  179. */
  180. void PLOTTER::markerLozenge( const wxPoint& position, int radius )
  181. {
  182. std::vector< wxPoint > corner_list;
  183. wxPoint corner;
  184. corner.x = position.x;
  185. corner.y = position.y + radius;
  186. corner_list.push_back( corner );
  187. corner.x = position.x + radius;
  188. corner.y = position.y,
  189. corner_list.push_back( corner );
  190. corner.x = position.x;
  191. corner.y = position.y - radius;
  192. corner_list.push_back( corner );
  193. corner.x = position.x - radius;
  194. corner.y = position.y;
  195. corner_list.push_back( corner );
  196. corner.x = position.x;
  197. corner.y = position.y + radius;
  198. corner_list.push_back( corner );
  199. PlotPoly( corner_list, NO_FILL, GetCurrentLineWidth() );
  200. }
  201. /**
  202. * Plot a - bar centered on the position. Building block for markers
  203. */
  204. void PLOTTER::markerHBar( const wxPoint& pos, int radius )
  205. {
  206. MoveTo( wxPoint( pos.x - radius, pos.y ) );
  207. FinishTo( wxPoint( pos.x + radius, pos.y ) );
  208. }
  209. /**
  210. * Plot a / bar centered on the position. Building block for markers
  211. */
  212. void PLOTTER::markerSlash( const wxPoint& pos, int radius )
  213. {
  214. MoveTo( wxPoint( pos.x - radius, pos.y - radius ) );
  215. FinishTo( wxPoint( pos.x + radius, pos.y + radius ) );
  216. }
  217. /**
  218. * Plot a \ bar centered on the position. Building block for markers
  219. */
  220. void PLOTTER::markerBackSlash( const wxPoint& pos, int radius )
  221. {
  222. MoveTo( wxPoint( pos.x + radius, pos.y - radius ) );
  223. FinishTo( wxPoint( pos.x - radius, pos.y + radius ) );
  224. }
  225. /**
  226. * Plot a | bar centered on the position. Building block for markers
  227. */
  228. void PLOTTER::markerVBar( const wxPoint& pos, int radius )
  229. {
  230. MoveTo( wxPoint( pos.x, pos.y - radius ) );
  231. FinishTo( wxPoint( pos.x, pos.y + radius ) );
  232. }
  233. /**
  234. * Draw a pattern shape number aShapeId, to coord x0, y0.
  235. * x0, y0 = coordinates tables
  236. * Diameter diameter = (coord table) hole
  237. * AShapeId = index (used to generate forms characters)
  238. */
  239. void PLOTTER::Marker( const wxPoint& position, int diametre, unsigned aShapeId )
  240. {
  241. int radius = diametre / 2;
  242. /* Marker are composed by a series of 'parts' superimposed; not every
  243. combination make sense, obviously. Since they are used in order I
  244. tried to keep the uglier/more complex constructions at the end.
  245. Also I avoided the |/ |\ -/ -\ construction because they're *very*
  246. ugly... if needed they could be added anyway... I'd like to see
  247. a board with more than 58 drilling/slotting tools!
  248. If Visual C++ supported the 0b literals they would be optimally
  249. and easily encoded as an integer array. We have to do with octal */
  250. static const unsigned char marker_patterns[MARKER_COUNT] = {
  251. // Bit order: O Square Lozenge - | \ /
  252. // First choice: simple shapes
  253. 0003, // X
  254. 0100, // O
  255. 0014, // +
  256. 0040, // Sq
  257. 0020, // Lz
  258. // Two simple shapes
  259. 0103, // X O
  260. 0017, // X +
  261. 0043, // X Sq
  262. 0023, // X Lz
  263. 0114, // O +
  264. 0140, // O Sq
  265. 0120, // O Lz
  266. 0054, // + Sq
  267. 0034, // + Lz
  268. 0060, // Sq Lz
  269. // Three simple shapes
  270. 0117, // X O +
  271. 0143, // X O Sq
  272. 0123, // X O Lz
  273. 0057, // X + Sq
  274. 0037, // X + Lz
  275. 0063, // X Sq Lz
  276. 0154, // O + Sq
  277. 0134, // O + Lz
  278. 0074, // + Sq Lz
  279. // Four simple shapes
  280. 0174, // O Sq Lz +
  281. 0163, // X O Sq Lz
  282. 0157, // X O Sq +
  283. 0137, // X O Lz +
  284. 0077, // X Sq Lz +
  285. // This draws *everything *
  286. 0177, // X O Sq Lz +
  287. // Here we use the single bars... so the cross is forbidden
  288. 0110, // O -
  289. 0104, // O |
  290. 0101, // O /
  291. 0050, // Sq -
  292. 0044, // Sq |
  293. 0041, // Sq /
  294. 0030, // Lz -
  295. 0024, // Lz |
  296. 0021, // Lz /
  297. 0150, // O Sq -
  298. 0144, // O Sq |
  299. 0141, // O Sq /
  300. 0130, // O Lz -
  301. 0124, // O Lz |
  302. 0121, // O Lz /
  303. 0070, // Sq Lz -
  304. 0064, // Sq Lz |
  305. 0061, // Sq Lz /
  306. 0170, // O Sq Lz -
  307. 0164, // O Sq Lz |
  308. 0161, // O Sq Lz /
  309. // Last resort: the backlash component (easy to confound)
  310. 0102, // \ O
  311. 0042, // \ Sq
  312. 0022, // \ Lz
  313. 0142, // \ O Sq
  314. 0122, // \ O Lz
  315. 0062, // \ Sq Lz
  316. 0162 // \ O Sq Lz
  317. };
  318. if( aShapeId >= MARKER_COUNT )
  319. {
  320. // Fallback shape
  321. markerCircle( position, radius );
  322. }
  323. else
  324. {
  325. // Decode the pattern and draw the corresponding parts
  326. unsigned char pat = marker_patterns[aShapeId];
  327. if( pat & 0001 )
  328. markerSlash( position, radius );
  329. if( pat & 0002 )
  330. markerBackSlash( position, radius );
  331. if( pat & 0004 )
  332. markerVBar( position, radius );
  333. if( pat & 0010 )
  334. markerHBar( position, radius );
  335. if( pat & 0020 )
  336. markerLozenge( position, radius );
  337. if( pat & 0040 )
  338. markerSquare( position, radius );
  339. if( pat & 0100 )
  340. markerCircle( position, radius );
  341. }
  342. }
  343. /**
  344. * Convert a thick segment and plot it as an oval
  345. */
  346. void PLOTTER::segmentAsOval( const wxPoint& start, const wxPoint& end, int width,
  347. EDA_DRAW_MODE_T tracemode )
  348. {
  349. wxPoint center( (start.x + end.x) / 2, (start.y + end.y) / 2 );
  350. wxSize size( end.x - start.x, end.y - start.y );
  351. double orient;
  352. if( size.y == 0 )
  353. orient = 0;
  354. else if( size.x == 0 )
  355. orient = 900;
  356. else
  357. orient = -ArcTangente( size.y, size.x );
  358. size.x = KiROUND( EuclideanNorm( size ) ) + width;
  359. size.y = width;
  360. FlashPadOval( center, size, orient, tracemode );
  361. }
  362. void PLOTTER::sketchOval( const wxPoint& pos, const wxSize& aSize, double orient,
  363. int width )
  364. {
  365. SetCurrentLineWidth( width );
  366. width = currentPenWidth;
  367. int radius, deltaxy, cx, cy;
  368. wxSize size( aSize );
  369. if( size.x > size.y )
  370. {
  371. EXCHG( size.x, size.y );
  372. orient = AddAngles( orient, 900 );
  373. }
  374. deltaxy = size.y - size.x; /* distance between centers of the oval */
  375. radius = ( size.x - width ) / 2;
  376. cx = -radius;
  377. cy = -deltaxy / 2;
  378. RotatePoint( &cx, &cy, orient );
  379. MoveTo( wxPoint( cx + pos.x, cy + pos.y ) );
  380. cx = -radius;
  381. cy = deltaxy / 2;
  382. RotatePoint( &cx, &cy, orient );
  383. FinishTo( wxPoint( cx + pos.x, cy + pos.y ) );
  384. cx = radius;
  385. cy = -deltaxy / 2;
  386. RotatePoint( &cx, &cy, orient );
  387. MoveTo( wxPoint( cx + pos.x, cy + pos.y ) );
  388. cx = radius;
  389. cy = deltaxy / 2;
  390. RotatePoint( &cx, &cy, orient );
  391. FinishTo( wxPoint( cx + pos.x, cy + pos.y ) );
  392. cx = 0;
  393. cy = deltaxy / 2;
  394. RotatePoint( &cx, &cy, orient );
  395. Arc( wxPoint( cx + pos.x, cy + pos.y ),
  396. orient + 1800, orient + 3600,
  397. radius, NO_FILL );
  398. cx = 0;
  399. cy = -deltaxy / 2;
  400. RotatePoint( &cx, &cy, orient );
  401. Arc( wxPoint( cx + pos.x, cy + pos.y ),
  402. orient, orient + 1800,
  403. radius, NO_FILL );
  404. }
  405. /* Plot 1 segment like a track segment
  406. */
  407. void PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end, int width,
  408. EDA_DRAW_MODE_T tracemode )
  409. {
  410. switch( tracemode )
  411. {
  412. case FILLED:
  413. case LINE:
  414. SetCurrentLineWidth( tracemode==FILLED ? width : -1 );
  415. MoveTo( start );
  416. FinishTo( end );
  417. break;
  418. case SKETCH:
  419. SetCurrentLineWidth( -1 );
  420. segmentAsOval( start, end, width, tracemode );
  421. break;
  422. }
  423. }
  424. void PLOTTER::ThickArc( const wxPoint& centre, double StAngle, double EndAngle,
  425. int radius, int width, EDA_DRAW_MODE_T tracemode )
  426. {
  427. switch( tracemode )
  428. {
  429. case LINE:
  430. SetCurrentLineWidth( -1 );
  431. Arc( centre, StAngle, EndAngle, radius, NO_FILL, -1 );
  432. break;
  433. case FILLED:
  434. Arc( centre, StAngle, EndAngle, radius, NO_FILL, width );
  435. break;
  436. case SKETCH:
  437. SetCurrentLineWidth( -1 );
  438. Arc( centre, StAngle, EndAngle,
  439. radius - ( width - currentPenWidth ) / 2, NO_FILL, -1 );
  440. Arc( centre, StAngle, EndAngle,
  441. radius + ( width - currentPenWidth ) / 2, NO_FILL, -1 );
  442. break;
  443. }
  444. }
  445. void PLOTTER::ThickRect( const wxPoint& p1, const wxPoint& p2, int width,
  446. EDA_DRAW_MODE_T tracemode )
  447. {
  448. switch( tracemode )
  449. {
  450. case LINE:
  451. Rect( p1, p2, NO_FILL, -1 );
  452. break;
  453. case FILLED:
  454. Rect( p1, p2, NO_FILL, width );
  455. break;
  456. case SKETCH:
  457. SetCurrentLineWidth( -1 );
  458. wxPoint offsetp1( p1.x - (width - currentPenWidth) / 2,
  459. p1.y - (width - currentPenWidth) / 2 );
  460. wxPoint offsetp2( p2.x + (width - currentPenWidth) / 2,
  461. p2.y + (width - currentPenWidth) / 2 );
  462. Rect( offsetp1, offsetp2, NO_FILL, -1 );
  463. offsetp1.x += (width - currentPenWidth);
  464. offsetp1.y += (width - currentPenWidth);
  465. offsetp2.x -= (width - currentPenWidth);
  466. offsetp2.y -= (width - currentPenWidth);
  467. Rect( offsetp1, offsetp2, NO_FILL, -1 );
  468. break;
  469. }
  470. }
  471. void PLOTTER::ThickCircle( const wxPoint& pos, int diametre, int width,
  472. EDA_DRAW_MODE_T tracemode )
  473. {
  474. switch( tracemode )
  475. {
  476. case LINE:
  477. Circle( pos, diametre, NO_FILL, -1 );
  478. break;
  479. case FILLED:
  480. Circle( pos, diametre, NO_FILL, width );
  481. break;
  482. case SKETCH:
  483. SetCurrentLineWidth( -1 );
  484. Circle( pos, diametre - width + currentPenWidth, NO_FILL, -1 );
  485. Circle( pos, diametre + width - currentPenWidth, NO_FILL, -1 );
  486. break;
  487. }
  488. }
  489. void PLOTTER::SetPageSettings( const PAGE_INFO& aPageSettings )
  490. {
  491. wxASSERT( !outputFile );
  492. pageInfo = aPageSettings;
  493. }