draw.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. // Draw a graph
  2. extern "C"{
  3. #include "fxlib.h"
  4. }
  5. #include <string.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <math.h>
  9. #include "stdafx.h"
  10. #include "defs.h"
  11. #define DIMX 128
  12. #define DIMY 64
  13. #define F p3
  14. #define T p4
  15. #define X p5
  16. #define Y p6
  17. #define XT p7
  18. #define YT p8
  19. static double tmin, tmax;
  20. static double xmin, xmax;
  21. static double ymin, ymax;
  22. #define YMAX 1000
  23. typedef struct {
  24. int x, y;
  25. double t;
  26. } DRAWBUF;
  27. DRAWBUF *draw_buf;
  28. static int draw_count;
  29. void
  30. eval_draw(void)
  31. {
  32. draw_buf=(DRAWBUF*)calloc(YMAX,sizeof(DRAWBUF));
  33. F = cadr(p1);
  34. T = caddr(p1);
  35. if (T == symbol(NIL)) {
  36. push(F);
  37. rewrite();
  38. guess();
  39. T = pop();
  40. F = pop();
  41. }
  42. push(get_binding(T));
  43. push(get_arglist(T));
  44. draw_main();
  45. p2 = pop();
  46. p1 = pop();
  47. set_binding_and_arglist(T, p1, p2);
  48. // return value
  49. push(symbol(NIL));
  50. }
  51. void
  52. draw_main(void)
  53. {
  54. if (draw_flag) {
  55. draw_flag = 0; // so "stop" really stops
  56. stop("draw calls draw");
  57. }
  58. draw_flag++;
  59. setup_trange();
  60. setup_xrange();
  61. setup_yrange();
  62. check_for_parametric_draw();
  63. create_point_set();
  64. emit_graph();
  65. draw_flag--;
  66. }
  67. /* xrange sets the horizontal scale
  68. yrange sets the vertical scale
  69. Normally, the function F is evaluated from xrange[1] to xrange[2].
  70. However, if F returns a vector then parametric drawing is used. In this
  71. case F is evaluated from trange[1] to trange[2].
  72. */
  73. void
  74. check_for_parametric_draw(void)
  75. {
  76. eval_f(tmin);
  77. p1 = pop();
  78. if (!istensor(p1)) {
  79. tmin = xmin;
  80. tmax = xmax;
  81. }
  82. }
  83. #define N 100
  84. void
  85. create_point_set(void)
  86. {
  87. int i, n;
  88. double t;
  89. draw_count = 0;
  90. for (i = 0; i <= N; i++) {
  91. t = tmin + ((double) i / (double) N) * (tmax - tmin);
  92. new_point(t);
  93. }
  94. n = draw_count;
  95. for (i = 0; i < n - 1; i++)
  96. fill(i, i + 1, 0);
  97. }
  98. void
  99. new_point(double t)
  100. {
  101. double x, y;
  102. if (draw_count >= YMAX)
  103. return;
  104. draw_buf[draw_count].x = -10000;
  105. draw_buf[draw_count].y = -10000;
  106. draw_buf[draw_count].t = t;
  107. draw_count++;
  108. get_xy(t);
  109. if (!isnum(XT) || !isnum(YT))
  110. return;
  111. push(XT);
  112. x = pop_double();
  113. x = (x - xmin) / (xmax - xmin);
  114. x = (double) DIMX * x + 0.5; // map 0-1 to 0-DIM, +0.5 so draw(x^3) looks right
  115. push(YT);
  116. y = pop_double();
  117. y = (y - ymin) / (ymax - ymin);
  118. y = (double) DIMY * y + 0.5; // map 0-1 to 0-DIM, +0.5 so draw(x^3) looks right
  119. if (x < -10000.0)
  120. x = -10000.0;
  121. if (x > 10000.0)
  122. x = 10000.0;
  123. if (y < -10000.0)
  124. y = -10000.0;
  125. if (y > 10000.0)
  126. y = 10000.0;
  127. draw_buf[draw_count - 1].x = (int) x;
  128. draw_buf[draw_count - 1].y = (int) y;
  129. }
  130. // Evaluate F(t) and return in XT and YT.
  131. void
  132. get_xy(double t)
  133. {
  134. eval_f(t);
  135. p1 = pop();
  136. if (istensor(p1)) {
  137. if (p1->u.tensor->nelem >= 2) {
  138. XT = p1->u.tensor->elem[0];
  139. YT = p1->u.tensor->elem[1];
  140. } else {
  141. XT = symbol(NIL);
  142. YT = symbol(NIL);
  143. }
  144. return;
  145. }
  146. push_double(t);
  147. XT = pop();
  148. YT = p1;
  149. }
  150. // Evaluate F(t) without stopping due to an error such as divide by zero.
  151. void
  152. eval_f(double t)
  153. {
  154. // These must be volatile or it crashes. (Compiler error?)
  155. // Read it backwards, save_tos is a volatile int, etc.
  156. int volatile save_tos;
  157. U ** volatile save_frame;
  158. save();
  159. save_tos = tos;
  160. save_frame = frame;
  161. draw_flag++;
  162. if (setjmp(draw_stop_return)) {
  163. tos = save_tos;
  164. push(symbol(NIL));
  165. frame = save_frame;
  166. restore();
  167. draw_flag--;
  168. return;
  169. }
  170. push_double(t);
  171. p1 = pop();
  172. set_binding(T, p1);
  173. push(F);
  174. eval();
  175. yyfloat();
  176. eval();
  177. restore();
  178. draw_flag--;
  179. }
  180. #define MAX_DEPTH 6
  181. void
  182. fill(int i, int k, int level)
  183. {
  184. int dx, dy, j;
  185. double t;
  186. if (level >= MAX_DEPTH || draw_count >= YMAX)
  187. return;
  188. dx = abs(draw_buf[i].x - draw_buf[k].x);
  189. dy = abs(draw_buf[i].y - draw_buf[k].y);
  190. if (dx < 1 && dy < 1)
  191. return;
  192. t = (draw_buf[i].t + draw_buf[k].t) / 2.0;
  193. j = draw_count;
  194. new_point(t);
  195. fill(i, j, level + 1);
  196. fill(j, k, level + 1);
  197. }
  198. //-----------------------------------------------------------------------------
  199. //
  200. // Normalize x to [0,1]
  201. //
  202. // Example: xmin = -10, xmax = 10, xmax - xmin = 20
  203. //
  204. // x x - xmin (x - xmin) / (xmax - xmin)
  205. //
  206. // -10 0 0.00
  207. //
  208. // -5 5 0.25
  209. //
  210. // 0 10 0.50
  211. //
  212. // 5 15 0.75
  213. //
  214. // 10 20 1.00
  215. //
  216. //-----------------------------------------------------------------------------
  217. void
  218. setup_trange(void)
  219. {
  220. save();
  221. setup_trange_f();
  222. restore();
  223. }
  224. void
  225. setup_trange_f(void)
  226. {
  227. // default range is (-pi, pi)
  228. tmin = -M_PI;
  229. tmax = M_PI;
  230. p1 = usr_symbol("trange");
  231. if (!issymbol(p1))
  232. return;
  233. p1 = get_binding(p1);
  234. // must be two element vector
  235. if (!istensor(p1) || p1->u.tensor->ndim != 1 || p1->u.tensor->nelem != 2)
  236. return;
  237. push(p1->u.tensor->elem[0]);
  238. eval();
  239. yyfloat();
  240. eval();
  241. p2 = pop();
  242. push(p1->u.tensor->elem[1]);
  243. eval();
  244. yyfloat();
  245. eval();
  246. p3 = pop();
  247. if (!isnum(p2) || !isnum(p3))
  248. return;
  249. push(p2);
  250. tmin = pop_double();
  251. push(p3);
  252. tmax = pop_double();
  253. if (tmin == tmax)
  254. stop("draw: trange is zero");
  255. }
  256. void
  257. setup_xrange(void)
  258. {
  259. save();
  260. setup_xrange_f();
  261. restore();
  262. }
  263. void
  264. setup_xrange_f(void)
  265. {
  266. // default range is (-10,10)
  267. xmin = -10.0;
  268. xmax = 10.0;
  269. p1 = usr_symbol("xrange");
  270. if (!issymbol(p1))
  271. return;
  272. p1 = get_binding(p1);
  273. // must be two element vector
  274. if (!istensor(p1) || p1->u.tensor->ndim != 1 || p1->u.tensor->nelem != 2)
  275. return;
  276. push(p1->u.tensor->elem[0]);
  277. eval();
  278. yyfloat();
  279. eval();
  280. p2 = pop();
  281. push(p1->u.tensor->elem[1]);
  282. eval();
  283. yyfloat();
  284. eval();
  285. p3 = pop();
  286. if (!isnum(p2) || !isnum(p3))
  287. return;
  288. push(p2);
  289. xmin = pop_double();
  290. push(p3);
  291. xmax = pop_double();
  292. if (xmin == xmax)
  293. stop("draw: xrange is zero");
  294. }
  295. //-----------------------------------------------------------------------------
  296. //
  297. // Example: yrange=(-10,10)
  298. //
  299. // y d v (vertical pixel coordinate)
  300. //
  301. // 10 0.00 0
  302. //
  303. // 5 0.25 100
  304. //
  305. // 0 0.50 200
  306. //
  307. // -5 0.75 300
  308. //
  309. // -10 1.00 400
  310. //
  311. // We have
  312. //
  313. // d = (10 - y) / 20
  314. //
  315. // = (B - y) / (B - A)
  316. //
  317. // where yrange=(A,B)
  318. //
  319. // To convert d to v, multiply by N where N = 400.
  320. //
  321. //-----------------------------------------------------------------------------
  322. void
  323. setup_yrange(void)
  324. {
  325. save();
  326. setup_yrange_f();
  327. restore();
  328. }
  329. void
  330. setup_yrange_f(void)
  331. {
  332. // default range is (-10,10)
  333. ymin = -10.0;
  334. ymax = 10.0;
  335. p1 = usr_symbol("yrange");
  336. if (!issymbol(p1))
  337. return;
  338. p1 = get_binding(p1);
  339. // must be two element vector
  340. if (!istensor(p1) || p1->u.tensor->ndim != 1 || p1->u.tensor->nelem != 2)
  341. return;
  342. push(p1->u.tensor->elem[0]);
  343. eval();
  344. yyfloat();
  345. eval();
  346. p2 = pop();
  347. push(p1->u.tensor->elem[1]);
  348. eval();
  349. yyfloat();
  350. eval();
  351. p3 = pop();
  352. if (!isnum(p2) || !isnum(p3))
  353. return;
  354. push(p2);
  355. ymin = pop_double();
  356. push(p3);
  357. ymax = pop_double();
  358. if (ymin == ymax)
  359. stop("draw: yrange is zero");
  360. }
  361. void get_xyminmax(double* xminp, double* xmaxp, double* yminp, double* ymaxp) {
  362. *xminp = xmin;
  363. *xmaxp = xmax;
  364. *yminp = ymin;
  365. *ymaxp = ymax;
  366. }
  367. #define XOFF 0
  368. #define YOFF 24
  369. static void emit_xaxis(void);
  370. static void emit_yaxis(void);
  371. static void emit_xscale(void);
  372. static void emit_yscale(void);
  373. static void get_xzero(void);
  374. static void get_yzero(void);
  375. static int xzero, yzero;
  376. void
  377. emit_graph(void)
  378. {
  379. int i, x, y;
  380. Bdisp_AllClr_VRAM();
  381. get_xzero();
  382. get_yzero();
  383. emit_xaxis();
  384. emit_yaxis();
  385. emit_xscale();
  386. emit_yscale();
  387. for (i = 0; i < draw_count; i++) {
  388. x = draw_buf[i].x;
  389. y = DIMY - draw_buf[i].y; // flip the y coordinate
  390. if (x < 0 || x > DIMX)
  391. continue;
  392. if (y < 0 || y > DIMY)
  393. continue;
  394. Bdisp_SetPoint_VRAM(x+XOFF, y+YOFF, 1);
  395. }
  396. set_has_drawn(1);
  397. }
  398. static void
  399. emit_xaxis(void)
  400. {
  401. int x, y, x2, y2;
  402. if (yzero < 0 || yzero > DIMY)
  403. return;
  404. x = XOFF;
  405. y = YOFF + yzero;
  406. x2 = XOFF + DIMX;
  407. y2 = YOFF + yzero;
  408. Bdisp_DrawLineVRAM(x, y, x2, y2);
  409. }
  410. static void
  411. emit_yaxis(void)
  412. {
  413. int x, y, x2, y2;
  414. if (xzero < 0 || xzero > DIMX)
  415. return;
  416. x = XOFF + xzero;
  417. y = YOFF;
  418. x2 = XOFF + xzero;
  419. y2 = YOFF + DIMY;
  420. Bdisp_DrawLineVRAM(x, y, x2, y2);
  421. }
  422. static void
  423. get_xzero(void)
  424. {
  425. double x;
  426. x = -((double) DIMX) * xmin / (xmax - xmin) + 0.5;
  427. if (x < -10000.0)
  428. x = -10000.0;
  429. if (x > 10000.0)
  430. x = 10000.0;
  431. xzero = (int) x;
  432. }
  433. static void
  434. get_yzero(void)
  435. {
  436. double y;
  437. y = -((double) DIMY) * ymin / (ymax - ymin) + 0.5;
  438. if (y < -10000.0)
  439. y = -10000.0;
  440. if (y > 10000.0)
  441. y = 10000.0;
  442. yzero = DIMY - (int) y; // flip the y coordinate
  443. }
  444. static void emit_xscale_f(int, char *);
  445. static void
  446. emit_xscale(void)
  447. {
  448. static char s[100];
  449. sprintf(s, "%g", xmin);
  450. emit_xscale_f(0, s);
  451. sprintf(s, "%g", xmax);
  452. emit_xscale_f(DIMX, s);
  453. }
  454. static void
  455. emit_xscale_f(int xx, char *s)
  456. {
  457. int w, x, y;
  458. y = 0;
  459. w = 0;
  460. PrintMini( w, y, (unsigned char*)s, 0); // get width
  461. x = XOFF + xx;
  462. if(x >= DIMX) x = x-w;
  463. y = YOFF + yzero - 24+2;
  464. PrintMini( x, y, (unsigned char*)s, 0);
  465. }
  466. static void emit_yscale_f(int, char *);
  467. static void
  468. emit_yscale(void)
  469. {
  470. static char s[100];
  471. sprintf(s, "%g", ymax);
  472. emit_yscale_f(0, s);
  473. sprintf(s, "%g", ymin);
  474. emit_yscale_f(DIMY, s);
  475. }
  476. static void
  477. emit_yscale_f(int yy, char *s)
  478. {
  479. int w, x, y;
  480. y = 0;
  481. w = 0;
  482. PrintMini( w, y, (unsigned char*)s, 0); // get width
  483. x = xzero - w;
  484. y = YOFF + yy;
  485. if(y >= DIMY) y = y-9;
  486. y -= 24;
  487. PrintMini( x, y, (unsigned char*)s, 0);
  488. }
  489. /*
  490. #define XOFF 0
  491. #define YOFF 0
  492. #define SHIM 10
  493. static int k;
  494. static unsigned char *buf;
  495. static void emit_box(void);
  496. static void emit_xaxis(void);
  497. static void emit_yaxis(void);
  498. static void emit_xscale(void);
  499. static void emit_yscale(void);
  500. static void emit_xzero(void);
  501. static void emit_yzero(void);
  502. static void get_xzero(void);
  503. static void get_yzero(void);
  504. static int xzero, yzero;
  505. void
  506. emit_graph(void)
  507. {
  508. int h, i, len, x, y;
  509. get_xzero();
  510. get_yzero();
  511. len = 1000 + 5 * draw_count;
  512. buf = (unsigned char *) malloc(len);
  513. h = DIM + SHIM + text_metric[SMALL_FONT].ascent + text_metric[SMALL_FONT].descent;
  514. //buf[0] = (unsigned char) (h >> 8);
  515. //buf[1] = (unsigned char) h;
  516. //buf[2] = (unsigned char) (DIM >> 8);
  517. //buf[3] = (unsigned char) DIM;
  518. k = 0;
  519. emit_box();
  520. emit_xaxis();
  521. emit_yaxis();
  522. emit_xscale();
  523. emit_yscale();
  524. emit_xzero();
  525. emit_yzero();
  526. for (i = 0; i < draw_count; i++) {
  527. x = draw_buf[i].x;
  528. y = DIM - draw_buf[i].y; // flip the y coordinate
  529. if (x < 0 || x > DIM)
  530. continue;
  531. if (y < 0 || y > DIM)
  532. continue;
  533. x += XOFF;
  534. y += YOFF;
  535. buf[k++] = DRAW_POINT;
  536. buf[k++] = (unsigned char) (x >> 8);
  537. buf[k++] = (unsigned char) x;
  538. buf[k++] = (unsigned char) (y >> 8);
  539. buf[k++] = (unsigned char) y;
  540. }
  541. buf[k++] = 0;
  542. shipout(buf, DIM + 1, h);
  543. }
  544. static void
  545. get_xzero(void)
  546. {
  547. double x;
  548. x = -((double) DIM) * xmin / (xmax - xmin) + 0.5;
  549. if (x < -10000.0)
  550. x = -10000.0;
  551. if (x > 10000.0)
  552. x = 10000.0;
  553. xzero = (int) x;
  554. }
  555. static void
  556. get_yzero(void)
  557. {
  558. double y;
  559. y = -((double) DIM) * ymin / (ymax - ymin) + 0.5;
  560. if (y < -10000.0)
  561. y = -10000.0;
  562. if (y > 10000.0)
  563. y = 10000.0;
  564. yzero = DIM - (int) y; // flip the y coordinate
  565. }
  566. static void
  567. emit_box(void)
  568. {
  569. int x, y;
  570. buf[k++] = DRAW_BOX;
  571. x = XOFF;
  572. y = YOFF;
  573. buf[k++] = (unsigned char) (x >> 8);
  574. buf[k++] = (unsigned char) x;
  575. buf[k++] = (unsigned char) (y >> 8);
  576. buf[k++] = (unsigned char) y;
  577. x = XOFF + DIM;
  578. y = YOFF + DIM;
  579. buf[k++] = (unsigned char) (x >> 8);
  580. buf[k++] = (unsigned char) x;
  581. buf[k++] = (unsigned char) (y >> 8);
  582. buf[k++] = (unsigned char) y;
  583. }
  584. static void
  585. emit_xaxis(void)
  586. {
  587. int x, y;
  588. if (yzero < 0 || yzero > DIM)
  589. return;
  590. buf[k++] = DRAW_LINE;
  591. x = XOFF;
  592. y = YOFF + yzero;
  593. buf[k++] = (unsigned char) (x >> 8);
  594. buf[k++] = (unsigned char) x;
  595. buf[k++] = (unsigned char) (y >> 8);
  596. buf[k++] = (unsigned char) y;
  597. x = XOFF + DIM;
  598. y = YOFF + yzero;
  599. buf[k++] = (unsigned char) (x >> 8);
  600. buf[k++] = (unsigned char) x;
  601. buf[k++] = (unsigned char) (y >> 8);
  602. buf[k++] = (unsigned char) y;
  603. }
  604. static void
  605. emit_yaxis(void)
  606. {
  607. int x, y;
  608. if (xzero < 0 || xzero > DIM)
  609. return;
  610. buf[k++] = DRAW_LINE;
  611. x = XOFF + xzero;
  612. y = YOFF;
  613. buf[k++] = (unsigned char) (x >> 8);
  614. buf[k++] = (unsigned char) x;
  615. buf[k++] = (unsigned char) (y >> 8);
  616. buf[k++] = (unsigned char) y;
  617. x = XOFF + xzero;
  618. y = YOFF + DIM;
  619. buf[k++] = (unsigned char) (x >> 8);
  620. buf[k++] = (unsigned char) x;
  621. buf[k++] = (unsigned char) (y >> 8);
  622. buf[k++] = (unsigned char) y;
  623. }
  624. static void emit_xscale_f(int, char *);
  625. static void
  626. emit_xscale(void)
  627. {
  628. static char s[100];
  629. sprintf(s, "%g", xmin);
  630. emit_xscale_f(0, s);
  631. sprintf(s, "%g", xmax);
  632. emit_xscale_f(DIM, s);
  633. }
  634. static void
  635. emit_xscale_f(int xx, char *s)
  636. {
  637. int d, i, len, w, x, y;
  638. // want to center the number w/o sign
  639. w = text_width(SMALL_FONT, s);
  640. if (*s == '-')
  641. d = w - text_width(SMALL_FONT, s + 1);
  642. else
  643. d = 0;
  644. x = XOFF + xx - (w - d) / 2 - d;
  645. y = YOFF + DIM + SHIM;
  646. buf[k++] = SMALL_FONT;
  647. buf[k++] = (unsigned char) (x >> 8);
  648. buf[k++] = (unsigned char) x;
  649. buf[k++] = (unsigned char) (y >> 8);
  650. buf[k++] = (unsigned char) y;
  651. len = (int) strlen(s);
  652. buf[k++] = (unsigned char) len;
  653. for (i = 0; i < len; i++)
  654. buf[k++] = (unsigned char) s[i];
  655. }
  656. static void emit_yscale_f(int, char *);
  657. static void
  658. emit_yscale(void)
  659. {
  660. static char s[100];
  661. sprintf(s, "%g", ymax);
  662. emit_yscale_f(0, s);
  663. sprintf(s, "%g", ymin);
  664. emit_yscale_f(DIM, s);
  665. }
  666. static void
  667. emit_yscale_f(int yy, char *s)
  668. {
  669. int i, len, w, x, y;
  670. w = text_width(SMALL_FONT, s);
  671. x = XOFF - SHIM - w;
  672. y = YOFF + yy - text_metric[SMALL_FONT].ascent / 2;
  673. buf[k++] = SMALL_FONT;
  674. buf[k++] = (unsigned char) (x >> 8);
  675. buf[k++] = (unsigned char) x;
  676. buf[k++] = (unsigned char) (y >> 8);
  677. buf[k++] = (unsigned char) y;
  678. len = (int) strlen(s);
  679. buf[k++] = (unsigned char) len;
  680. for (i = 0; i < len; i++)
  681. buf[k++] = (unsigned char) s[i];
  682. }
  683. // emit the '0' axis label
  684. // make sure it doesn't hit the other labels
  685. static void
  686. emit_xzero(void)
  687. {
  688. int x, y;
  689. if (xzero < DIM / 4 || xzero > 3 * DIM / 4)
  690. return;
  691. x = XOFF + xzero - text_width(SMALL_FONT, "0") / 2;
  692. y = YOFF + DIM + SHIM;
  693. buf[k++] = SMALL_FONT;
  694. buf[k++] = (unsigned char) (x >> 8);
  695. buf[k++] = (unsigned char) x;
  696. buf[k++] = (unsigned char) (y >> 8);
  697. buf[k++] = (unsigned char) y;
  698. buf[k++] = 1;
  699. buf[k++] = '0';
  700. }
  701. // emit the '0' axis label
  702. // make sure it doesn't hit the other labels
  703. static void
  704. emit_yzero(void)
  705. {
  706. int x, y;
  707. if (yzero < DIM / 4 || yzero > 3 * DIM / 4)
  708. return;
  709. x = XOFF - SHIM - text_width(SMALL_FONT, "0");
  710. y = YOFF + yzero - text_metric[SMALL_FONT].ascent / 2;
  711. buf[k++] = SMALL_FONT;
  712. buf[k++] = (unsigned char) (x >> 8);
  713. buf[k++] = (unsigned char) x;
  714. buf[k++] = (unsigned char) (y >> 8);
  715. buf[k++] = (unsigned char) y;
  716. buf[k++] = 1;
  717. buf[k++] = '0';
  718. }
  719. */