PageRenderTime 155ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/ecg/third/wfdb-10.5.4/wave/sig.c

https://github.com/kastur/ECGCS
C | 538 lines | 394 code | 52 blank | 92 comment | 177 complexity | 00fb0e1cfc470f7a0e58f6fd4090f14a MD5 | raw file
  1. /* file: sig.c G. Moody 27 April 1990
  2. Last revised: 2 March 2010
  3. Signal display functions for WAVE
  4. -------------------------------------------------------------------------------
  5. WAVE: Waveform analyzer, viewer, and editor
  6. Copyright (C) 1990-2010 George B. Moody
  7. This program is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free Software
  9. Foundation; either version 2 of the License, or (at your option) any later
  10. version.
  11. This program is distributed in the hope that it will be useful, but WITHOUT ANY
  12. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  13. PARTICULAR PURPOSE. See the GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License along with
  15. this program; if not, write to the Free Software Foundation, Inc., 59 Temple
  16. Place - Suite 330, Boston, MA 02111-1307, USA.
  17. You may contact the author by e-mail (george@mit.edu) or postal mail
  18. (MIT Room E25-505A, Cambridge, MA 02139 USA). For updates to this software,
  19. please visit PhysioNet (http://www.physionet.org/).
  20. _______________________________________________________________________________
  21. */
  22. #include "wave.h"
  23. #include "xvwave.h"
  24. static void show_signal_names(), show_signal_baselines();
  25. /* Show_display_list() plots the display list pointed to by its argument. */
  26. static struct display_list *lp_current;
  27. static int highlighted = -1;
  28. int in_siglist(i)
  29. int i;
  30. {
  31. int nsl;
  32. for (nsl = 0; nsl < siglistlen; nsl++)
  33. if (siglist[nsl] == i) return (1);
  34. return (0);
  35. }
  36. static void drawtrace(b, n, ybase, gc, mode)
  37. XPoint *b;
  38. int mode, n, ybase;
  39. GC gc;
  40. {
  41. int j, xn, xp;
  42. XPoint *p, *q;
  43. if (ybase == -9999) return;
  44. for (j = 0, p = q = b; j <= n; j++) {
  45. if (j == n || q->y == WFDB_INVALID_SAMPLE) {
  46. if (p < q) {
  47. while (p < q && p->y == WFDB_INVALID_SAMPLE)
  48. p++;
  49. xp = p->x;
  50. xn = p - b;
  51. if (nsamp <= canvas_width) xn *= tscale;
  52. p->x = xn;
  53. p->y += ybase;
  54. XDrawLines(display, osb, gc, p, q-p, CoordModePrevious);
  55. if (mode)
  56. XDrawLines(display, xid, gc, p, q-p, CoordModePrevious);
  57. p->x = xp;
  58. p->y -= ybase;
  59. }
  60. p = ++q;
  61. }
  62. else
  63. ++q;
  64. }
  65. }
  66. static void show_display_list(lp)
  67. struct display_list *lp;
  68. {
  69. int i, j, k, xn, xp, in_siglist();
  70. XPoint *p, *q;
  71. lp_current = lp;
  72. if (!lp) return;
  73. if (sig_mode == 0)
  74. for (i = 0; i < nsig; i++) {
  75. if (lp->vlist[i])
  76. drawtrace(lp->vlist[i], lp->ndpts, base[i], draw_sig, 0);
  77. }
  78. else if (sig_mode == 1)
  79. for (i = 0; i < siglistlen; i++) {
  80. if (0 <= siglist[i] && siglist[i] < nsig && lp->vlist[siglist[i]])
  81. drawtrace(lp->vlist[siglist[i]], lp->ndpts,base[i],draw_sig,0);
  82. }
  83. else { /* sig_mode == 2 (show valid signals only) */
  84. int j, nvsig;
  85. for (i = nvsig = 0; i < nsig; i++)
  86. if (lp->vlist[i] && vvalid[i]) nvsig++;
  87. for (i = j = 0; i < nsig; i++) {
  88. if (lp->vlist[i] && vvalid[i]) {
  89. base[i] = canvas_height*(2*(j++)+1.)/(2.*nvsig);
  90. drawtrace(lp->vlist[i], lp->ndpts, base[i], draw_sig, 0);
  91. }
  92. else
  93. base[i] = -9999;
  94. }
  95. }
  96. highlighted = -1;
  97. }
  98. void sig_highlight(i)
  99. int i;
  100. {
  101. extern void repaint();
  102. if (!lp_current) return;
  103. if (0 <= highlighted && highlighted < lp_current->nsig) {
  104. if (sig_mode != 1) {
  105. drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
  106. base[highlighted], unhighlight_sig, 1);
  107. drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
  108. base[highlighted], draw_sig, 1);
  109. }
  110. else {
  111. int j;
  112. for (j = 0; j < siglistlen; j++) {
  113. if (siglist[j] == highlighted) {
  114. drawtrace(lp_current->vlist[highlighted],
  115. lp_current->ndpts, base[j], unhighlight_sig, 1);
  116. drawtrace(lp_current->vlist[highlighted],
  117. lp_current->ndpts, base[j], draw_sig, 1);
  118. }
  119. }
  120. }
  121. }
  122. highlighted = i;
  123. if (0 <= highlighted && highlighted < lp_current->nsig) {
  124. if (sig_mode != 1) {
  125. drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
  126. base[highlighted], clear_sig, 1);
  127. drawtrace(lp_current->vlist[highlighted], lp_current->ndpts,
  128. base[highlighted], highlight_sig, 1);
  129. }
  130. else {
  131. int j;
  132. for (j = 0; j < siglistlen; j++) {
  133. if (siglist[j] == highlighted) {
  134. drawtrace(lp_current->vlist[highlighted],
  135. lp_current->ndpts, base[j], clear_sig, 1);
  136. drawtrace(lp_current->vlist[highlighted],
  137. lp_current->ndpts, base[j], highlight_sig, 1);
  138. }
  139. }
  140. }
  141. }
  142. }
  143. /* Do_disp() executes a display request. The display will show nsamp samples
  144. of nsig signals, starting at display_start_time. */
  145. void do_disp()
  146. {
  147. char *tp;
  148. int c, i, x0, x1, y0;
  149. struct display_list *lp;
  150. /* This might take a while ... */
  151. xv_set(frame, FRAME_BUSY, TRUE, NULL);
  152. /* Show the grid if requested. */
  153. show_grid();
  154. /* Make sure that the requested time is reasonable. */
  155. if (display_start_time < 0) display_start_time = 0;
  156. /* Update the panel items that indicate the start and end times. */
  157. tp = wtimstr(display_start_time);
  158. set_start_time(tp);
  159. while (*tp == ' ') tp++;
  160. y0 = canvas_height - mmy(2);
  161. x0 = mmx(2);
  162. XDrawString(display, osb, time_mode == 1 ? draw_ann : draw_sig,
  163. x0, y0, tp, strlen(tp));
  164. tp = wtimstr(display_start_time + nsamp);
  165. set_end_time(tp);
  166. while (*tp == ' ') tp++;
  167. x1 = canvas_width - XTextWidth(font, tp, strlen(tp)) - mmx(2);
  168. XDrawString(display, osb, time_mode == 1 ? draw_ann : draw_sig,
  169. x1, y0, tp, strlen(tp));
  170. /* Show the annotations, if available. */
  171. show_annotations(display_start_time, nsamp);
  172. /* Get a display list for the requested screen, and show it. */
  173. lp = find_display_list(display_start_time);
  174. show_display_list(lp);
  175. /* If requested, show the signal names. */
  176. if (show_signame) show_signal_names();
  177. /* If requested, show the signal baselines. */
  178. if (show_baseline) show_signal_baselines(lp);
  179. xv_set(frame, FRAME_BUSY, FALSE, NULL);
  180. XClearWindow(display, xid);
  181. }
  182. static int nlists;
  183. static int vlist_size;
  184. /* Get_display_list() obtains storage for display lists from the heap (via
  185. malloc) or by recycling a display list in the cache. Since the screen duration
  186. (nsamp) does not change frequently, get_display_list() calculates the pixel
  187. abscissas when a new display list is obtained from the heap, and recalculates
  188. them only when nsamp has been changed. */
  189. static struct display_list *get_display_list()
  190. {
  191. int i, maxx, x;
  192. static int max_nlists = MAX_DISPLAY_LISTS;
  193. struct display_list *lp = NULL, *lpl;
  194. if (nlists < max_nlists &&
  195. (lp = (struct display_list *)calloc(1, sizeof(struct display_list))))
  196. nlists++;
  197. if (lp == NULL)
  198. switch (nlists) {
  199. case 0: return (NULL);
  200. case 1: lp = first_list; break;
  201. default: lpl = first_list; lp = lpl->next;
  202. while (lp->next) {
  203. lpl = lp;
  204. lp = lp->next;
  205. }
  206. lpl->next = NULL;
  207. break;
  208. }
  209. if (lp->nsig < nsig) {
  210. lp->sb = realloc(lp->sb, nsig * sizeof(int));
  211. lp->vlist = realloc(lp->vlist, nsig * sizeof(XPoint *));
  212. for (i = lp->nsig; i < nsig; i++) {
  213. lp->sb[i] = 0;
  214. lp->vlist[i] = NULL;
  215. }
  216. lp->nsig = nsig;
  217. }
  218. if (canvas_width > vlist_size) vlist_size = canvas_width;
  219. for (i = 0; i < nsig; i++) {
  220. if (lp->vlist[i] == NULL) {
  221. if ((lp->vlist[i] =
  222. (XPoint *)calloc(vlist_size, sizeof(XPoint)))==NULL) {
  223. while (--i >= 0)
  224. free(lp->vlist[i]);
  225. free(lp);
  226. max_nlists = --nlists;
  227. return (get_display_list());
  228. }
  229. }
  230. /* If there are more samples to be shown than addressable x-pixels
  231. in the window, the abscissas are simply the integers from 0 to
  232. the canvas width (and some compression will be needed). If the
  233. largest abscissa is correct, all of the others must also be correct,
  234. and they need not be reset. */
  235. if (nsamp > canvas_width) {
  236. x = maxx = canvas_width - 1;
  237. if (lp->vlist[i][x].x != x) {
  238. int xx;
  239. lp->vlist[i][0].x = 0; /* absolute first abscissa */
  240. for (xx = 1; xx <= x; xx++)
  241. lp->vlist[i][xx].x = 1; /* relative to previous */
  242. }
  243. }
  244. /* Otherwise, no compression is needed, and the abscissas must be
  245. distributed across the window (at intervals > 1 pixel). Again,
  246. if the largest abscissa is correct, all of the others must also
  247. be correct, and no computation is needed. */
  248. else {
  249. x = maxx = nsamp - 1;
  250. if (lp->vlist[i][vlist_size-1].x != (int)(x*tscale)) {
  251. int xp, xpp;
  252. lp->vlist[i][0].x = xp = 0; /* absolute first abscissa */
  253. for (x = 1; x < vlist_size; x++) {
  254. xpp = xp;
  255. xp = x*tscale;
  256. lp->vlist[i][x].x = xp - xpp; /* relative to prev */
  257. }
  258. }
  259. }
  260. }
  261. lp->next = first_list;
  262. lp->npoints = nsamp;
  263. lp->xmax = maxx;
  264. return (first_list = lp);
  265. }
  266. /* Find_display_list() obtains a display list beginning at the sample number
  267. specified by its argument. If such a list (with the correct duration) is
  268. found in the cache, it can be returned immediately. Otherwise, the function
  269. reads the requested segment and determines the pixel ordinates of the
  270. vertices of the polylines for each signal. */
  271. struct display_list *find_display_list(fdl_time)
  272. long fdl_time;
  273. {
  274. int c, i, j, x, x0, y, ymax, ymin;
  275. struct display_list *lp;
  276. XPoint *tp;
  277. if (fdl_time < 0L) fdl_time = -fdl_time;
  278. /* If the requested display list is in the cache, return it at once. */
  279. for (lp = first_list; lp; lp = lp->next)
  280. if (lp->start == fdl_time && lp->npoints == nsamp) return (lp);
  281. /* Give up if a display list can't be allocated, or if we can't skip
  282. to the requested segment, or if we can't read at least one sample. */
  283. if ((lp = get_display_list()) == NULL ||
  284. (fdl_time != strtim("i") && isigsettime(fdl_time) < 0) ||
  285. getvec(v0) < 0)
  286. return (NULL);
  287. /* Record the starting time. */
  288. lp->start = fdl_time;
  289. /* Set the starting point for each signal. */
  290. for (c = 0; c < nsig; c++) {
  291. vmin[c] = vmax[c] = v0[c];
  292. vvalid[c] = 0;
  293. if (v0[c] == WFDB_INVALID_SAMPLE)
  294. lp->vlist[c][0].y = -1 << 15;
  295. else
  296. lp->vlist[c][0].y = v0[c]*vscale[c];
  297. }
  298. /* If there are more than canvas_width samples to be shown, compress the
  299. data. */
  300. if (nsamp > canvas_width) {
  301. for (i = 1, x0 = 0; i < nsamp && getvec(v) > 0; i++) {
  302. for (c = 0, vvalid[c] = 0; c < nsig; c++) {
  303. if (v[c] != WFDB_INVALID_SAMPLE) {
  304. if (v[c] > vmax[c]) vmax[c] = v[c];
  305. if (v[c] < vmin[c]) vmin[c] = v[c];
  306. vvalid[c] = 1;
  307. }
  308. }
  309. if ((x = i*tscale) > x0) {
  310. x0 = x;
  311. for (c = 0; c < nsig; c++) {
  312. if (vvalid[c]) {
  313. if (vmax[c] - v0[c] > v0[c] - vmin[c])
  314. v0[c] = vmin[c] = vmax[c];
  315. else
  316. v0[c] = vmax[c] = vmin[c];
  317. lp->vlist[c][x0].y = v0[c]*vscale[c];
  318. }
  319. else
  320. lp->vlist[c][x0].y = -1 << 15;
  321. }
  322. }
  323. }
  324. i = x0+1;
  325. }
  326. /* If there are canvas_width or fewer samples to be shown, no compression
  327. is necessary. */
  328. else
  329. for (i = 1; i < nsamp && getvec(v) > 0; i++)
  330. for (c = 0; c < nsig; c++) {
  331. if (v[c] == WFDB_INVALID_SAMPLE)
  332. lp->vlist[c][i].y = -1 << 15;
  333. else
  334. lp->vlist[c][i].y = v[c]*vscale[c];
  335. }
  336. /* Record the number of displayed points. This may be less than
  337. expected at the end of the record. */
  338. lp->ndpts = i;
  339. /* Set the y-offset so that the signal will be vertically centered about
  340. the nominal baseline if the midrange is near the mean. The y-offset
  341. is actually a weighted sum of the midrange and the mean, which favors
  342. the mean if the two values differ significantly. */
  343. for (c = 0; c < nsig; c++) {
  344. int dy; /* the y-offset */
  345. int n; /* the number of valid ordinates */
  346. int ymid; /* the midpoint of the ordinate range */
  347. long ymean; /* the mean of the ordinates */
  348. double w; /* weight assigned to ymean in y-offset calculation */
  349. tp = lp->vlist[c];
  350. /* Find the first valid sample in the trace, if any. */
  351. for (j = 0; j < i && tp[j].y == WFDB_INVALID_SAMPLE; j++)
  352. ;
  353. ymean = ymax = ymin = (j < i) ? tp[j].y : 0;
  354. for (n = 1; j < i; j++) {
  355. if ((y = tp[j].y) != WFDB_INVALID_SAMPLE) {
  356. if (y > ymax) ymax = y;
  357. else if (y < ymin) ymin = y;
  358. ymean += y;
  359. n++;
  360. }
  361. }
  362. ymean /= n;
  363. ymid = (ymax + ymin)/2;
  364. /* Since ymin <= ymid <= ymax, the next lines imply 0 <= w <= 1 */
  365. if (ymid > ymean) /* in this case, ymax must be > ymean */
  366. w = (ymid - ymean)/(ymax - ymean);
  367. else if (ymid < ymean) /* in this case, ymin must be < ymean */
  368. w = (ymean - ymid)/(ymean - ymin);
  369. else w = 1.0;
  370. dy = -(ymid + ((double)ymean-ymid)*w);
  371. for (j = i-1; j >= 0; j--) {
  372. if (tp[j].y == WFDB_INVALID_SAMPLE) continue;
  373. /* The bounds-checking below shouldn't be necessary (the X server
  374. should clip at the canvas boundaries), but Sun's X11/NeWS
  375. server will crash (and may bring the system down with it) if
  376. run on a system with the GX accelerator and if passed
  377. sufficiently out-of-bounds ordinates (maybe abscissas, too --
  378. this hasn't been tested). This bug is present in both the
  379. original X11/NeWS server and the GFX revision. It appears that
  380. the bug can be avoided if the maximum distance from the window
  381. edge to the out-of-bounds ordinate is less than about 2000
  382. pixels (although this may be dependent on the window height).
  383. This bug has not been observed with other X servers. */
  384. if ((tp[j].y += dy) < -canvas_height) tp[j].y = -canvas_height;
  385. else if (tp[j].y > canvas_height) tp[j].y = canvas_height;
  386. /* Convert all except the first ordinate in each set of contiguous
  387. valid samples to relative ordinates. */
  388. if (j < i-1 && tp[j+1].y != WFDB_INVALID_SAMPLE)
  389. tp[j+1].y -= tp[j].y;
  390. }
  391. if (dc_coupled[c]) lp->sb[c] = sigbase[c]*vscale[c] + dy;
  392. }
  393. return (lp);
  394. }
  395. /* Clear_cache() marks all of the display lists in the cache as invalid. This
  396. function should be executed whenever the gain (vscale) or record is changed,
  397. or whenever the canvas width has been increased. */
  398. void clear_cache()
  399. {
  400. int i;
  401. struct display_list *lp;
  402. if (canvas_width > vlist_size) {
  403. for (lp = first_list; lp; lp = lp->next) {
  404. for (i = 0; i < lp->nsig && lp->vlist[i] != NULL; i++) {
  405. free(lp->vlist[i]);
  406. lp->vlist[i] = NULL;
  407. }
  408. }
  409. vlist_size = 0;
  410. }
  411. else {
  412. for (lp = first_list; lp; lp = lp->next) {
  413. lp->start = -1;
  414. lp->npoints = 0;
  415. }
  416. }
  417. }
  418. static void show_signal_names()
  419. {
  420. int i, xoff, yoff;
  421. xoff = mmx(5);
  422. yoff = (nsig > 1) ? (base[1] - base[0])/3 : canvas_height/3;
  423. if (sig_mode == 0)
  424. for (i = 0; i < nsig; i++)
  425. XDrawString(display, osb, draw_sig, xoff, base[i] - yoff,
  426. signame[i], strlen(signame[i]));
  427. else if (sig_mode == 1) {
  428. for (i = 0; i < siglistlen; i++)
  429. if (0 <= siglist[i] && siglist[i] < nsig)
  430. XDrawString(display, osb, draw_sig, xoff, base[i] - yoff,
  431. signame[siglist[i]], strlen(signame[siglist[i]]));
  432. }
  433. else { /* sig_mode == 2 (show valid signals only) */
  434. int j, nvsig;
  435. for (i = nvsig = 0; i < nsig; i++)
  436. if (vvalid[i]) nvsig++;
  437. for (i = j = 0; i < nsig; i++) {
  438. if (vvalid[i]) {
  439. base[i] = canvas_height*(2*(j++)+1.)/(2.*nvsig);
  440. XDrawString(display, osb, draw_sig, xoff, base[i] - yoff,
  441. signame[i], strlen(signame[i]));
  442. }
  443. }
  444. }
  445. }
  446. static void show_signal_baselines(lp)
  447. struct display_list *lp;
  448. {
  449. int i, l, xoff, yoff;
  450. yoff = mmy(2);
  451. for (i = 0; i < nsig; i++) {
  452. if (base[i] == -9999) continue;
  453. if (dc_coupled[i] && 0 <= lp->sb[i] && lp->sb[i] < canvas_height) {
  454. XDrawLine(display, osb, draw_ann,
  455. 0, lp->sb[i]+base[i], canvas_width, lp->sb[i]+base[i]);
  456. if (blabel[i]) {
  457. l = strlen(blabel[i]);
  458. xoff = canvas_width - XTextWidth(font, blabel[i], l) - mmx(2);
  459. XDrawString(display, osb, draw_sig,
  460. xoff, lp->sb[i]+base[i] - yoff, blabel[i], l);
  461. }
  462. }
  463. }
  464. }
  465. /* Return window y-coordinate corresponding to the level of displayed trace
  466. i at abscissa x. */
  467. int sigy(i, x)
  468. int i, x;
  469. {
  470. int ix, j = -1, xx, yy;
  471. if (sig_mode != 1) j = i;
  472. else if (0 <= i && i < siglistlen) j = siglist[i];
  473. if (j < 0 || j >= nsig || lp_current->vlist[j] == NULL) return (-1);
  474. if (nsamp > canvas_width) ix = x;
  475. else ix = (int)x/tscale;
  476. if (ix >= lp_current->ndpts) ix = lp_current->ndpts - 1;
  477. for (xx = yy = 0; xx < ix; xx++)
  478. yy += lp_current->vlist[j][xx].y;
  479. return (yy + base[i]);
  480. }