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.

722 lines
19 KiB

  1. /**
  2. * @file common_plotHPGL_functions.cpp
  3. * @brief KiCad: Common plot HPGL Routines
  4. * Filled primitive are not supported, but some could be using HPGL/2
  5. * Since this plot engine is mostly intended for import in external programs,
  6. * sadly HPGL/2 isn't supported a lot... some of the primitives use overlapped
  7. * strokes to fill the shape
  8. */
  9. /* Some HPGL commands:
  10. * Note: the HPGL unit is 25 micrometers
  11. * All commands MUST be terminated by a semi-colon or a linefeed.
  12. * Spaces can NOT be substituted for required commas in the syntax of a command.
  13. *
  14. *
  15. * AA (Arc Absolute): Angle is a floating point # (requires non integer value)
  16. * Draws an arc with the center at (X,Y).
  17. * A positive angle creates a counter-clockwise arc.
  18. * If the chord angle is specified,
  19. * this will be the number of degrees used for stepping around the arc.
  20. * If no value is given then a default value of five degrees is used.
  21. * AA x, y, a {,b};
  22. *
  23. * AR (Arc Relative):
  24. * AR Dx, Dy, a {, b};
  25. *
  26. * CA (Alternate Character Set):
  27. * CA {n};
  28. *
  29. * CI (Circle):
  30. * CI r {,b};
  31. *
  32. * CP (Character Plot):
  33. * CP {h, v};
  34. * h [-127.9999 .. 127.9999] Anzahl der Zeichen horizontal
  35. * v [-127.9999 .. 127.9999] Anzahl der Zeichen vertikal
  36. *
  37. * CS (Standard Character Set):
  38. * CS {n};
  39. *
  40. * DR (Relative Direction for Label Text):
  41. * DR s, a;
  42. *
  43. * DI (Absolute Direction for Label Text):
  44. * DI {s, a};
  45. *
  46. * DT (Define Terminator - this character becomes unavailable except to terminate a label string.
  47. * Default is ^C control-C):
  48. * DT t;
  49. *
  50. * EA (rEctangle Absolute - Unfilled, from current position to diagonal x,y):
  51. * EA x, y;
  52. *
  53. * ER (rEctangle Relative - Unfilled, from current position to diagonal x,y):
  54. * ER x,y;
  55. *
  56. * FT (Fill Type):
  57. * FT {s {,l {a}}};
  58. *
  59. * IM (Input Mask):
  60. * IM {f};
  61. *
  62. * IN (Initialize): This command instructs the controller to begin processing the HPGL plot file.
  63. * Without this, the commands in the file are received but never executed.
  64. * If multiple IN s are found during execution of the file,
  65. * the controller performs a Pause/Cancel operation.
  66. * All motion from the previous job, yet to be executed, is lost,
  67. * and the new information is executed.
  68. * IN;
  69. *
  70. * IP Input P1 and P2:
  71. * IP {P1x, P1y {, P2x, P2y}};
  72. *
  73. * IW (Input Window):
  74. * IW {XUL, YUL, XOR, YOR};
  75. *
  76. * LB (Label):
  77. * LB c1 .. cn t;
  78. *
  79. * PA (Plot Absolute): Moves to an absolute HPGL position and sets absolute mode for
  80. * future PU and PD commands. If no arguments follow the command,
  81. * only absolute mode is set.
  82. * PA {x1, y1 {{PU|PD|,} ..., ..., xn, yn}};
  83. * P1x, P1y, P2x, P2y [Integer in ASCII]
  84. *
  85. * PD (Pen Down): Executes <current pen> pen then moves to the requested position
  86. * if one is specified. This position is dependent on whether absolute
  87. * or relative mode is set. This command performs no motion in 3-D mode,
  88. * but the outputs and feedrates are affected.
  89. * PD {x, y};
  90. *
  91. * PR (Plot Relative): Moves to the relative position specified and sets relative mode
  92. * for future PU and PD commands.
  93. * If no arguments follow the command, only relative mode is set.
  94. * PR {Dx1, Dy1 {{PU|PD|,} ..., ..., Dxn, Dyn}};
  95. *
  96. * PS (Paper Size):
  97. * PS {n};
  98. *
  99. * PT (Pen Thickness):
  100. * PT {l};
  101. *
  102. * PU (Pen Up): Executes <current pen> pen then moves to the requested position
  103. * if one is specified. This position is dependent on whether absolute
  104. * or relative mode is set.
  105. * This command performs no motion in 3-D mode, but the outputs
  106. * and feedrates are affected.
  107. * PU {x, y};
  108. *
  109. * RA (Rectangle Absolute - Filled, from current position to diagonal x,y):
  110. * RA x, y;
  111. *
  112. * RO (Rotate Coordinate System):
  113. * RO;
  114. *
  115. * RR (Rectangle Relative - Filled, from current position to diagonal x,y):
  116. * RR x, y;
  117. *
  118. * SA (Select Alternate Set):
  119. * SA;
  120. *
  121. * SC (Scale):
  122. * SC {Xmin, Xmax, Ymin, Ymax};
  123. *
  124. * SI (Absolute Character Size):
  125. * SI b, h;
  126. * b [-127.9999 .. 127.9999, keine 0]
  127. * h [-127.9999 .. 127.9999, keine 0]
  128. *
  129. * SL (Character Slant):
  130. * SL {a};
  131. * a [-3.5 .. -0.5, 0.5 .. 3.5]
  132. *
  133. * SP (Select Pen): Selects a new pen or tool for use.
  134. * If no pen number or a value of zero is given,
  135. * the controller performs an EOF (end of file command).
  136. * Once an EOF is performed, no motion is executed,
  137. * until a new IN command is received.
  138. * SP n;
  139. *
  140. * SR (Relative Character Size):
  141. * SR {b, h};
  142. * b [-127.9999 .. 127.9999, keine 0]
  143. * h [-127.9999 .. 127.9999, keine 0]
  144. *
  145. * SS (Select Standard Set):
  146. * SS;
  147. *
  148. * TL (Tick Length):
  149. * TL {tp {, tm}};
  150. *
  151. * UC (User Defined Character):
  152. * UC {i,} x1, y1, {i,} x2, y2, ... {i,} xn, yn;
  153. *
  154. * VS (Velocity Select):
  155. * VS {v {, n}};
  156. * v [1 .. 40]
  157. * n [1 .. 8, je nach Ausstattung]
  158. *
  159. * XT (X Tick):
  160. * XT;
  161. *
  162. * YT (Y Tick):
  163. * YT;
  164. */
  165. #include <fctsys.h>
  166. #include <gr_basic.h>
  167. #include <trigo.h>
  168. #include <wxstruct.h>
  169. #include <base_struct.h>
  170. #include <plot_common.h>
  171. #include <macros.h>
  172. #include <kicad_string.h>
  173. // HPGL scale factor (1 PLU = 1/40mm = 25 micrometers)
  174. static const double PLUsPERDECIMIL = 0.102041;
  175. HPGL_PLOTTER::HPGL_PLOTTER()
  176. {
  177. SetPenSpeed( 40 ); // Default pen speed = 40 cm/s
  178. SetPenNumber( 1 ); // Default pen num = 1
  179. }
  180. void HPGL_PLOTTER::SetViewport( const wxPoint& aOffset, double aIusPerDecimil,
  181. double aScale, bool aMirror )
  182. {
  183. wxASSERT( !outputFile );
  184. plotOffset = aOffset;
  185. plotScale = aScale;
  186. m_IUsPerDecimil = aIusPerDecimil;
  187. iuPerDeviceUnit = PLUsPERDECIMIL / aIusPerDecimil;
  188. /* Compute the paper size in IUs */
  189. paperSize = pageInfo.GetSizeMils();
  190. paperSize.x *= 10.0 * aIusPerDecimil;
  191. paperSize.y *= 10.0 * aIusPerDecimil;
  192. SetDefaultLineWidth( 0 ); // HPGL has pen sizes instead
  193. plotMirror = aMirror;
  194. }
  195. /**
  196. * At the start of the HPGL plot pen speed and number are requested
  197. */
  198. bool HPGL_PLOTTER::StartPlot()
  199. {
  200. wxASSERT( outputFile );
  201. fprintf( outputFile, "IN;VS%d;PU;PA;SP%d;\n", penSpeed, penNumber );
  202. return true;
  203. }
  204. /**
  205. * HPGL end of plot: pen return and release
  206. */
  207. bool HPGL_PLOTTER::EndPlot()
  208. {
  209. wxASSERT( outputFile );
  210. fputs( "PU;PA;SP0;\n", outputFile );
  211. fclose( outputFile );
  212. outputFile = NULL;
  213. return true;
  214. }
  215. /**
  216. * HPGL rectangle: fill not supported
  217. */
  218. void HPGL_PLOTTER::Rect( const wxPoint& p1, const wxPoint& p2, FILL_T fill, int width )
  219. {
  220. wxASSERT( outputFile );
  221. DPOINT p2dev = userToDeviceCoordinates( p2 );
  222. MoveTo( p1 );
  223. fprintf( outputFile, "EA %.0f,%.0f;\n", p2dev.x, p2dev.y );
  224. PenFinish();
  225. }
  226. /**
  227. * HPGL circle: fill not supported
  228. */
  229. void HPGL_PLOTTER::Circle( const wxPoint& centre, int diameter, FILL_T fill,
  230. int width )
  231. {
  232. wxASSERT( outputFile );
  233. double radius = userToDeviceSize( diameter / 2 );
  234. if( radius > 0 )
  235. {
  236. MoveTo( centre );
  237. fprintf( outputFile, "CI %g;\n", radius );
  238. PenFinish();
  239. }
  240. }
  241. /**
  242. * HPGL polygon: fill not supported (but closed, at least)
  243. */
  244. void HPGL_PLOTTER::PlotPoly( const std::vector<wxPoint>& aCornerList,
  245. FILL_T aFill, int aWidth )
  246. {
  247. if( aCornerList.size() <= 1 )
  248. return;
  249. MoveTo( aCornerList[0] );
  250. for( unsigned ii = 1; ii < aCornerList.size(); ii++ )
  251. LineTo( aCornerList[ii] );
  252. // Close polygon if filled.
  253. if( aFill )
  254. {
  255. int ii = aCornerList.size() - 1;
  256. if( aCornerList[ii] != aCornerList[0] )
  257. LineTo( aCornerList[0] );
  258. }
  259. PenFinish();
  260. }
  261. /**
  262. * Pen control logic (remove redundant pen activations)
  263. */
  264. void HPGL_PLOTTER::penControl( char plume )
  265. {
  266. wxASSERT( outputFile );
  267. switch( plume )
  268. {
  269. case 'U':
  270. if( penState != 'U' )
  271. {
  272. fputs( "PU;", outputFile );
  273. penState = 'U';
  274. }
  275. break;
  276. case 'D':
  277. if( penState != 'D' )
  278. {
  279. fputs( "PD;", outputFile );
  280. penState = 'D';
  281. }
  282. break;
  283. case 'Z':
  284. fputs( "PU;", outputFile );
  285. penState = 'U';
  286. penLastpos.x = -1;
  287. penLastpos.y = -1;
  288. break;
  289. }
  290. }
  291. void HPGL_PLOTTER::PenTo( const wxPoint& pos, char plume )
  292. {
  293. wxASSERT( outputFile );
  294. if( plume == 'Z' )
  295. {
  296. penControl( 'Z' );
  297. return;
  298. }
  299. penControl( plume );
  300. DPOINT pos_dev = userToDeviceCoordinates( pos );
  301. if( penLastpos != pos )
  302. fprintf( outputFile, "PA %.0f,%.0f;\n", pos_dev.x, pos_dev.y );
  303. penLastpos = pos;
  304. }
  305. /**
  306. * HPGL supports dashed lines
  307. */
  308. void HPGL_PLOTTER::SetDash( bool dashed )
  309. {
  310. wxASSERT( outputFile );
  311. if( dashed )
  312. fputs( "LI 2;\n", outputFile );
  313. else
  314. fputs( "LI;\n", outputFile );
  315. }
  316. void HPGL_PLOTTER::ThickSegment( const wxPoint& start, const wxPoint& end,
  317. int width, EDA_DRAW_MODE_T tracemode )
  318. {
  319. wxASSERT( outputFile );
  320. wxPoint center;
  321. wxSize size;
  322. // Suppress overlap if pen is too big or in line mode
  323. if( (penDiameter >= width) || (tracemode == LINE) )
  324. {
  325. MoveTo( start );
  326. FinishTo( end );
  327. }
  328. else
  329. segmentAsOval( start, end, width, tracemode );
  330. }
  331. /* Plot an arc:
  332. * Center = center coord
  333. * Stangl, endAngle = angle of beginning and end
  334. * Radius = radius of the arc
  335. * Command
  336. * PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, NbSegm; PU;
  337. * Or PU PY x, y; PD start_arc_X AA, start_arc_Y, angle, PU;
  338. */
  339. void HPGL_PLOTTER::Arc( const wxPoint& centre, int StAngle, int EndAngle, int radius,
  340. FILL_T fill, int width )
  341. {
  342. wxASSERT( outputFile );
  343. double angle;
  344. if( radius <= 0 )
  345. return;
  346. DPOINT centre_dev = userToDeviceCoordinates( centre );
  347. if( plotMirror )
  348. angle = (StAngle - EndAngle) / 10.0;
  349. else
  350. angle = (EndAngle - StAngle) / 10.0;
  351. // Calculate start point,
  352. wxPoint cmap;
  353. cmap.x = (int) ( centre.x + ( radius * cos( RAD2DEG( StAngle / 10.0 ) ) ) );
  354. cmap.y = (int) ( centre.y - ( radius * sin( RAD2DEG( StAngle / 10.0 ) ) ) );
  355. DPOINT cmap_dev = userToDeviceCoordinates( cmap );
  356. fprintf( outputFile,
  357. "PU;PA %.0f,%.0f;PD;AA %.0f,%.0f,",
  358. cmap_dev.x,
  359. cmap_dev.y,
  360. centre_dev.x,
  361. centre_dev.y );
  362. fprintf( outputFile, "%.0f", angle );
  363. fprintf( outputFile, ";PU;\n" );
  364. PenFinish();
  365. }
  366. /* Plot oval pad.
  367. */
  368. void HPGL_PLOTTER::FlashPadOval( const wxPoint& pos, const wxSize& aSize, int orient,
  369. EDA_DRAW_MODE_T trace_mode )
  370. {
  371. wxASSERT( outputFile );
  372. int deltaxy, cx, cy;
  373. wxSize size( aSize );
  374. /* The pad is reduced to an oval with size.y > size.x
  375. * (Oval vertical orientation 0)
  376. */
  377. if( size.x > size.y )
  378. {
  379. EXCHG( size.x, size.y ); orient += 900;
  380. if( orient >= 3600 )
  381. orient -= 3600;
  382. }
  383. deltaxy = size.y - size.x; // distance between centers of the oval
  384. if( trace_mode == FILLED )
  385. {
  386. FlashPadRect( pos, wxSize( size.x, deltaxy + KiROUND( penDiameter ) ),
  387. orient, trace_mode );
  388. cx = 0; cy = deltaxy / 2;
  389. RotatePoint( &cx, &cy, orient );
  390. FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode );
  391. cx = 0; cy = -deltaxy / 2;
  392. RotatePoint( &cx, &cy, orient );
  393. FlashPadCircle( wxPoint( cx + pos.x, cy + pos.y ), size.x, trace_mode );
  394. }
  395. else // Plot in SKETCH mode.
  396. {
  397. sketchOval( pos, size, orient, KiROUND( penDiameter ) );
  398. }
  399. }
  400. /* Plot round pad or via.
  401. */
  402. void HPGL_PLOTTER::FlashPadCircle( const wxPoint& pos, int diametre,
  403. EDA_DRAW_MODE_T trace_mode )
  404. {
  405. wxASSERT( outputFile );
  406. DPOINT pos_dev = userToDeviceCoordinates( pos );
  407. int delta = KiROUND( penDiameter - penOverlap );
  408. int radius = diametre / 2;
  409. if( trace_mode != LINE )
  410. {
  411. radius = ( diametre - KiROUND( penDiameter ) ) / 2;
  412. }
  413. if( radius < 0 )
  414. {
  415. radius = 0;
  416. }
  417. double rsize = userToDeviceSize( radius );
  418. fprintf( outputFile, "PA %.0f,%.0fd;CI %.0f;\n",
  419. pos_dev.x, pos_dev.y, rsize );
  420. if( trace_mode == FILLED ) // Plot in filled mode.
  421. {
  422. if( delta > 0 )
  423. {
  424. while( (radius -= delta ) >= 0 )
  425. {
  426. rsize = userToDeviceSize( radius );
  427. fprintf( outputFile, "PA %.0f,%.0f;CI %.0f;\n",
  428. pos_dev.x, pos_dev.y, rsize );
  429. }
  430. }
  431. }
  432. PenFinish();
  433. }
  434. void HPGL_PLOTTER::FlashPadRect( const wxPoint& pos, const wxSize& padsize,
  435. int orient, EDA_DRAW_MODE_T trace_mode )
  436. {
  437. wxASSERT( outputFile );
  438. wxSize size;
  439. int delta;
  440. int ox, oy, fx, fy;
  441. size.x = padsize.x / 2;
  442. size.y = padsize.y / 2;
  443. if( trace_mode != LINE )
  444. {
  445. size.x = (padsize.x - (int) penDiameter) / 2;
  446. size.y = (padsize.y - (int) penDiameter) / 2;
  447. }
  448. if( size.x < 0 )
  449. size.x = 0;
  450. if( size.y < 0 )
  451. size.y = 0;
  452. // If a dimension is zero, the trace is reduced to 1 line.
  453. if( size.x == 0 )
  454. {
  455. ox = pos.x;
  456. oy = pos.y - size.y;
  457. RotatePoint( &ox, &oy, pos.x, pos.y, orient );
  458. fx = pos.x;
  459. fy = pos.y + size.y;
  460. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  461. MoveTo( wxPoint( ox, oy ) );
  462. FinishTo( wxPoint( fx, fy ) );
  463. return;
  464. }
  465. if( size.y == 0 )
  466. {
  467. ox = pos.x - size.x;
  468. oy = pos.y;
  469. RotatePoint( &ox, &oy, pos.x, pos.y, orient );
  470. fx = pos.x + size.x;
  471. fy = pos.y;
  472. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  473. MoveTo( wxPoint( ox, oy ) );
  474. FinishTo( wxPoint( fx, fy ) );
  475. return;
  476. }
  477. ox = pos.x - size.x;
  478. oy = pos.y - size.y;
  479. RotatePoint( &ox, &oy, pos.x, pos.y, orient );
  480. MoveTo( wxPoint( ox, oy ) );
  481. fx = pos.x - size.x;
  482. fy = pos.y + size.y;
  483. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  484. LineTo( wxPoint( fx, fy ) );
  485. fx = pos.x + size.x;
  486. fy = pos.y + size.y;
  487. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  488. LineTo( wxPoint( fx, fy ) );
  489. fx = pos.x + size.x;
  490. fy = pos.y - size.y;
  491. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  492. LineTo( wxPoint( fx, fy ) );
  493. FinishTo( wxPoint( ox, oy ) );
  494. if( trace_mode == FILLED )
  495. {
  496. // Plot in filled mode.
  497. delta = (int) (penDiameter - penOverlap);
  498. if( delta > 0 )
  499. while( (size.x > 0) && (size.y > 0) )
  500. {
  501. size.x -= delta;
  502. size.y -= delta;
  503. if( size.x < 0 )
  504. size.x = 0;
  505. if( size.y < 0 )
  506. size.y = 0;
  507. ox = pos.x - size.x;
  508. oy = pos.y - size.y;
  509. RotatePoint( &ox, &oy, pos.x, pos.y, orient );
  510. MoveTo( wxPoint( ox, oy ) );
  511. fx = pos.x - size.x;
  512. fy = pos.y + size.y;
  513. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  514. LineTo( wxPoint( fx, fy ) );
  515. fx = pos.x + size.x;
  516. fy = pos.y + size.y;
  517. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  518. LineTo( wxPoint( fx, fy ) );
  519. fx = pos.x + size.x;
  520. fy = pos.y - size.y;
  521. RotatePoint( &fx, &fy, pos.x, pos.y, orient );
  522. LineTo( wxPoint( fx, fy ) );
  523. FinishTo( wxPoint( ox, oy ) );
  524. }
  525. }
  526. }
  527. void HPGL_PLOTTER::FlashPadTrapez( const wxPoint& aPadPos, const wxPoint* aCorners,
  528. int aPadOrient, EDA_DRAW_MODE_T aTrace_Mode )
  529. {
  530. wxASSERT( outputFile );
  531. wxPoint polygone[4]; // coordinates of corners relatives to the pad
  532. wxPoint coord[4]; // absolute coordinates of corners (coordinates in plotter space)
  533. int move;
  534. move = KiROUND( penDiameter );
  535. for( int ii = 0; ii < 4; ii++ )
  536. polygone[ii] = aCorners[ii];
  537. // polygone[0] is assumed the lower left
  538. // polygone[1] is assumed the upper left
  539. // polygone[2] is assumed the upper right
  540. // polygone[3] is assumed the lower right
  541. // Plot the outline:
  542. for( int ii = 0; ii < 4; ii++ )
  543. {
  544. coord[ii] = polygone[ii];
  545. RotatePoint( &coord[ii], aPadOrient );
  546. coord[ii] += aPadPos;
  547. }
  548. MoveTo( coord[0] );
  549. LineTo( coord[1] );
  550. LineTo( coord[2] );
  551. LineTo( coord[3] );
  552. FinishTo( coord[0] );
  553. // Fill shape:
  554. if( aTrace_Mode == FILLED )
  555. {
  556. // TODO: replace this par the HPGL plot polygon.
  557. int jj;
  558. // Fill the shape
  559. move = KiROUND( penDiameter - penOverlap );
  560. // Calculate fill height.
  561. if( polygone[0].y == polygone[3].y ) // Horizontal
  562. {
  563. jj = polygone[3].y - (int) ( penDiameter + ( 2 * penOverlap ) );
  564. }
  565. else // vertical
  566. {
  567. jj = polygone[3].x - (int) ( penDiameter + ( 2 * penOverlap ) );
  568. }
  569. // Calculation of dd = number of segments was traced to fill.
  570. jj = jj / (int) ( penDiameter - penOverlap );
  571. // Trace the outline.
  572. for( ; jj > 0; jj-- )
  573. {
  574. polygone[0].x += move;
  575. polygone[0].y -= move;
  576. polygone[1].x += move;
  577. polygone[1].y += move;
  578. polygone[2].x -= move;
  579. polygone[2].y += move;
  580. polygone[3].x -= move;
  581. polygone[3].y -= move;
  582. // Test for crossed vertexes.
  583. if( polygone[0].x > polygone[3].x ) /* X axis intersection on
  584. * vertexes 0 and 3 */
  585. {
  586. polygone[0].x = polygone[3].x = 0;
  587. }
  588. if( polygone[1].x > polygone[2].x ) /* X axis intersection on
  589. * vertexes 1 and 2 */
  590. {
  591. polygone[1].x = polygone[2].x = 0;
  592. }
  593. if( polygone[1].y > polygone[0].y ) /* Y axis intersection on
  594. * vertexes 0 and 1 */
  595. {
  596. polygone[0].y = polygone[1].y = 0;
  597. }
  598. if( polygone[2].y > polygone[3].y ) /* Y axis intersection on
  599. * vertexes 2 and 3 */
  600. {
  601. polygone[2].y = polygone[3].y = 0;
  602. }
  603. for( int ii = 0; ii < 4; ii++ )
  604. {
  605. coord[ii] = polygone[ii];
  606. RotatePoint( &coord[ii], aPadOrient );
  607. coord[ii] += aPadPos;
  608. }
  609. MoveTo( coord[0] );
  610. LineTo( coord[1] );
  611. LineTo( coord[2] );
  612. LineTo( coord[3] );
  613. FinishTo( coord[0] );
  614. }
  615. }
  616. }