1 /*
2  * draw.c, part of the gwave waveform viewer tool
3  *
4  * Functions for drawing waveforms
5  *
6  * Copyright (C) 1998, 1999 Stephen G. Tell
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the Free
20  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  */
23 
24 #include <ctype.h>
25 #include <math.h>
26 #include <setjmp.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <sys/time.h>
34 
35 #include <gtk/gtk.h>
36 
37 #include <gwave.h>
38 #include <wavewin.h>
39 
40 /* convert double value to printable text.
41  * Two modes:
42  * 0: using suffixes
43  *    We always try to print 4 significant figures, with one nonzero digit
44  *    to the left of the decimal point.
45  *    maximum field width: 7 characters
46  *
47  * 1: use scientific notation, printf %g format.
48  *    maximum field width appears to be 10 characters
49  */
val2txt(double val,int mode)50 char *val2txt(double val, int mode)
51 {
52 	static char buf[64];
53 	double aval = fabs(val);
54 	double sval, asval;
55 	char suffix;
56 	int ddigits;
57 
58 	switch(mode) {
59 	case 1:
60 		sprintf(buf, "% .5g", val);
61 		break;
62 	case 0:
63 	default:
64 		if(1e12 <= aval) {
65 			suffix = 'T';
66 			sval = val / 1e12;
67 		} else if(1e9 <= aval && aval < 1e12) {
68 			suffix = 'G';
69 			sval = val / 1e9;
70 		} else if(1e6 <= aval && aval < 1e9) {
71 			suffix = 'M';
72 			sval = val / 1e6;
73 		} else if(1e3 <= aval && aval < 1e6) {
74 			suffix = 'K';
75 			sval = val / 1000;
76 		} else if(1e-3 <= aval && aval < 1) {
77 			suffix = 'm';
78 			sval = val * 1000;
79 		} else if(1e-6 <= aval && aval < 1e-3) {
80 			suffix = 'u';
81 			sval = val * 1e6;
82 		} else if(1e-9 <= aval && aval < 1e-6) {
83 			suffix = 'n';
84 			sval = val * 1e9;
85 		} else if(1e-12 <= aval && aval < 1e-9) {
86 			suffix = 'p';
87 			sval = val * 1e12;
88 		} else if(1e-15 <= aval && aval < 1e-12) {
89 			suffix = 'f';
90 			sval = val * 1e15;
91 		} else if(DBL_EPSILON < aval && aval < 1e-15) {
92 			suffix = 'a';
93 			sval = val * 1e18;
94 		} else {
95 			suffix = ' ';
96 			sval = val;
97 		}
98 		asval = fabs(sval);
99 		if(1.0 <=  asval && asval < 10.0)
100 			ddigits = 4;
101 		else if(10.0 <=  asval && asval < 100.0)
102 			ddigits = 3;
103 		else
104 			ddigits = 2;
105 		sprintf(buf, "% .*f%c", ddigits, sval, suffix);
106 		break;
107 	}
108 	return buf;
109 }
110 
111 /* convert pixmap Y coordinate to user dependent-variable value */
y2val(WavePanel * wp,int y)112 double y2val(WavePanel *wp, int y)
113 {
114 	int h = wp->drawing->allocation.height;
115 	double frac = - (double)(y - h + 3) / (double)(h - 6);
116 
117 	if(wp->logy) {
118 		double a;
119 		a = frac * (log10(wp->end_yval) - log10(wp->start_yval))
120 			+ log10(wp->start_yval);
121 		return pow(10, a);
122 	} else {
123 		return frac * (wp->end_yval - wp->start_yval)
124 			+ wp->start_yval;
125 	}
126 }
127 
128 /* convert user value to pixmap y coord */
val2y(WavePanel * wp,double val)129 int val2y(WavePanel *wp, double val)
130 {
131 	double top = wp->end_yval;
132 	double bot = wp->start_yval;
133 	int h = wp->drawing->allocation.height;
134 	double frac;
135 
136 	if(wp->logy) {
137 		if(bot < 0 || val < 0)
138 			return -1;
139 
140 		frac = (log10(val) - log10(bot)) /
141 			(log10(top) - log10(bot));
142 	} else {
143 		frac = (val - bot ) / (top - bot);
144 	}
145 
146 	return h - ((h-6) * frac) - 3;
147 }
148 
149 /* convert pixmap X coordinate to user independent-variable value */
x2val(WavePanel * wp,int x,int log)150 double x2val(WavePanel *wp, int x, int log)
151 {
152 	int w = wp->drawing->allocation.width;
153 	double frac = (double)x / (double)w;
154 
155 	if(log) {
156 		double a;
157 		a = frac * (log10(wp->end_xval) - log10(wp->start_xval))
158 			+ log10(wp->start_xval);
159 		return pow(10, a);
160 	} else {
161 		return frac * (wp->end_xval - wp->start_xval)
162 			+ wp->start_xval;
163 	}
164 }
165 
166 /* convert independent-variable value to pixmap X coordinate */
val2x(WavePanel * wp,double val,int log)167 int val2x(WavePanel *wp, double val, int log)
168 {
169 	int w = wp->drawing->allocation.width;
170 	double frac;
171 
172 	if(log) {
173 		if(val < 0 || val < wp->start_xval)
174 			return -1;
175 
176 		frac = (log10(val) - log10(wp->start_xval)) /
177 			(log10(wp->end_xval) - log10(wp->start_xval));
178 	} else {
179 		frac = (val - wp->start_xval) /
180 			(wp->end_xval - wp->start_xval);
181 	}
182 	return w * frac;
183 }
184 
185 typedef void (*WaveDrawFunc) (VisibleWave *vw, WavePanel *wp);
186 struct wavedraw_method {
187 	WaveDrawFunc func;
188 	char *desc;
189 };
190 
191 void vw_wp_draw_ppixel(VisibleWave *vw, WavePanel *wp);
192 void vw_wp_draw_lineclip(VisibleWave *vw, WavePanel *wp);
193 
194 struct wavedraw_method wavedraw_method_tab[] = {
195 	vw_wp_draw_ppixel, "per-pixel",
196 	vw_wp_draw_lineclip, "correct-line"
197 };
198 
199 const int n_wavedraw_methods = sizeof(wavedraw_method_tab)/sizeof(struct wavedraw_method);
200 
201 
202 /*
203  * We know how to do this right, but working on other things has taken
204  * precedence.  Remarkably, this isn't particularly slow or ugly looking.
205  *
206  * TODO: smarter partial redraws on scrolling, expose, etc.
207  */
208 void
vw_wp_visit_draw(VisibleWave * vw,WavePanel * wp)209 vw_wp_visit_draw(VisibleWave *vw, WavePanel *wp)
210 {
211 	if(!vw->gc) {
212 		if(!vw->label) {
213 			fprintf(stderr, "visit_draw(%s): label=NULL\n",
214 				vw->varname);
215 			return;
216 		}
217 		if(!gdk_color_alloc(win_colormap,
218 				    &vw->label->style->fg[GTK_STATE_NORMAL])) {
219 			fprintf(stderr,
220 				"visit_draw(%s): gdk_color_alloc failed\n",
221 				vw->varname);
222 			return;
223 		}
224 		vw->gc = gdk_gc_new(wp->drawing->window);
225 		gdk_gc_set_foreground(vw->gc,
226 				      &vw->label->style->fg[GTK_STATE_NORMAL]);
227 	}
228 	g_assert(vw->gc != NULL);
229 	if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(vw->button)))
230                  gdk_gc_set_line_attributes(vw->gc,
231                                             2, GDK_LINE_SOLID, GDK_CAP_BUTT,
232                                             GDK_JOIN_ROUND);
233          else
234                  gdk_gc_set_line_attributes(vw->gc,
235                                             1, GDK_LINE_SOLID, GDK_CAP_BUTT,
236                                             GDK_JOIN_ROUND);
237 
238 	(wavedraw_method_tab[1].func)(vw, wp);
239 }
240 
241 /* finish what we started in vw_wp_visit_draw(),
242  * using various different drawing algorithms
243  */
244 
245 /* half-assed pixel-steping wave-drawing routine.
246  * gets data value and draws a line for every pixel.
247  * will exhibit aliasing if data has samples at higher frequency than
248  * the screen has pixels.
249  * Fast but can alias badly.
250  */
251 void
vw_wp_draw_ppixel(VisibleWave * vw,WavePanel * wp)252 vw_wp_draw_ppixel(VisibleWave *vw, WavePanel *wp)
253 {
254 	int x0, x1;
255 	int y0, y1;
256 	int i;
257 	double xstep;
258 	double xval;
259 	double yval;
260 	int w = wp->drawing->allocation.width;
261 	int h = wp->drawing->allocation.height;
262 
263 	xstep = (wp->end_xval - wp->start_xval)/w;  /* linear only */
264 
265 	x1 = 0;
266 	yval = wv_interp_value(vw->var, wp->start_xval);
267 	y1 = val2y(wp, yval);
268 
269 	for(i = 0, xval = wp->start_xval; i < w; i++ ) {
270 		x0 = x1; y0 = y1;
271 		x1 = x0 + 1;
272 		if(vw->var->wv_iv->wds->min <= xval
273 		   && xval <= vw->var->wv_iv->wds->max) {
274 
275 			yval = wv_interp_value(vw->var, xval);
276 			y1 = val2y(wp, yval);
277 			gdk_draw_line(wp->pixmap, vw->gc, x0,y0, x1,y1);
278 		}
279 		if(wtable->logx)
280 			xval = x2val(wp, x0+1, wtable->logx);
281 		else
282 			xval += xstep;
283 	}
284 
285 }
286 
287 int point_code (double x, double y,
288                 double xmn, double ymn,
289                 double xmx, double ymx);
290 void swap_i (int *a, int *b);
291 void swap_d (double *a, double *b);
292 
293 /*-----------------------------------------------------------------------
294   ROUTINE:  line_clip
295   PURPOSE:  To clip a line segment against a rectangular window.
296 
297   INPUT:    x1, y1,    double endpoints of line segment to be clipped.
298             x2, y2
299             xmn, ymn   double corners of clipping window.
300             xmx, ymx
301             returns    int  TRUE if line segment inside window.
302                             FALSE if outside.
303   LOCAL:    i1, i2     int codes indicating location of points
304                        relative to viewport boundaries.  (see
305                        description in point_code()).
306 
307   KEYWORD:  TWODIM * CLIP LINE VIEWPORT
308 
309   REMARKS:  The direction of the line segment may be reversed.
310 
311   DATE:     July '80  J.A.T, May 2002 C.D.
312  -----------------------------------------------------------------------*/
line_clip(double * x1,double * y1,double * x2,double * y2,double xmn,double ymn,double xmx,double ymx)313  int line_clip (double *x1, double *y1, double *x2, double *y2,
314                 double xmn, double ymn, double xmx, double ymx) {
315 
316 	int    i1, i2;
317 
318 /* Code each point.   */
319 	i1 = point_code (*x1, *y1, xmn, ymn, xmx, ymx);
320 	i2 = point_code (*x2, *y2, xmn, ymn, xmx, ymx);
321 
322 /* ARE BOTH ENDPOINTS IN VIEWING PORT? */
323 	while(i1 != 0 || i2 != 0) {
324 		if((i1 & i2) != 0)	/* Is segment outside viewport?   */
325 			return FALSE;
326 		if(i1 == 0) {		/* Is point 1 outside viewport?   */
327 			swap_i(&i1, &i2);
328 			swap_d(x1, x2);
329 			swap_d(y1, y2);
330 		}
331 
332 		if((i1 & 1) != 0) {			/* Move toward left edge.   */
333 			*y1 = *y1 + (*y2 - *y1) * (xmn - *x1) / (*x2 - *x1);
334 			*x1 = xmn;
335 		} else
336 			if((i1 & 2) != 0) {		/* Move toward right edge.   */
337 				*y1 = *y1 + (*y2 - *y1) * (xmx - *x1) / (*x2 - *x1);
338 				*x1 = xmx;
339 			} else
340 				if((i1 & 4) != 0) {	/* Move toward bottom.   */
341 					*x1 = *x1 + (*x2 - *x1) * (ymn - *y1) / (*y2 - *y1);
342 					*y1 = ymn;
343 				} else {			/* Move toward top.   */
344 					*x1 = *x1 + (*x2 - *x1) * (ymx - *y1) / (*y2 - *y1);
345 					*y1 = ymx;
346 				}
347 		i1 = point_code(*x1, *y1, xmn, ymn, xmx, ymx);
348 	}
349 	return TRUE;
350 }
351 
352 /*-----------------------------------------------------------------------
353   FUNCTION: point_code
354   PURPOSE:  To encode a point.
355   REMARKS:  This routine returns an integer code for
356             the point based on this mapping:
357 
358                       *      *
359                  1001 * 1000 * 1010
360                       *      *
361                 ********************
362                       *      *
363                  0001 * 0000 * 0010
364                       *      *
365                 ********************
366                       *      *
367                  0101 * 0100 * 0110
368                       *      *
369 
370   INPUT:    x,y        double coordinates of point.
371             xmn, ymn   double corners of window
372             xmx, ymx
373             returns    int code from above (0-10)
374 
375   KEYWORD:  TWODIM * CODE POINT
376 
377   DATE:     July '80  J.A.T. May 2002 C.D.
378  -----------------------------------------------------------------------*/
point_code(double x,double y,double xmn,double ymn,double xmx,double ymx)379 int point_code(double x, double y,
380                double xmn, double ymn,
381                double xmx, double ymx) {
382 	int  ic;
383 
384 	ic = 0;
385 	if(x < xmn) ic = 1;
386 	if(x > xmx) ic = 2;
387 	if(y < ymn) ic+=4;
388 	if(y > ymx) ic+=8;
389 	return ic;
390 }
391 
swap_i(int * a,int * b)392 void swap_i(int *a, int *b) {
393 	int temp;
394 	temp = *a;
395 	*a = *b;
396 	*b = temp;
397 	return;
398 }
399 
swap_d(double * a,double * b)400 void swap_d(double *a, double *b) {
401 	double temp;
402 	temp = *a;
403 	*a = *b;
404 	*b = temp;
405 	return;
406 }
407 
408 /* visit all data points, applying line-clipping algorithm */
409 void
vw_wp_draw_lineclip(VisibleWave * vw,WavePanel * wp)410 vw_wp_draw_lineclip(VisibleWave *vw, WavePanel *wp)
411 {
412 	int x0, x1;
413 	int y0, y1;
414 	int i;
415         double xval1, yval1;
416         double xval0d, yval0d, xval1d, yval1d;
417 
418         xval1 = wds_get_point(&vw->var->wv_iv->wds[0], 0);
419         yval1 = wds_get_point(&vw->var->wds[0], 0);
420 
421         for(i = 1; i < vw->var->wtable->nvalues; i++) {
422                 xval0d = xval1;
423                 yval0d = yval1;
424                 xval1d = xval1 = wds_get_point(&vw->var->wv_iv->wds[0], i);
425                 yval1d = yval1 = wds_get_point(&vw->var->wds[0], i);
426 
427                 if(line_clip(&xval0d, &yval0d, &xval1d, &yval1d,
428                              wp->start_xval, wp->start_yval,
429                              wp->end_xval, wp->end_yval))  {
430                         x0 = val2x(wp, xval0d, wtable->logx);
431                         y0 = val2y(wp, yval0d);
432                         x1 = val2x(wp, xval1d, wtable->logx);
433                         y1 = val2y(wp, yval1d);
434                         if(x0 != x1 || y0 != y1)
435                                 gdk_draw_line(wp->pixmap, vw->gc, x0,y0, x1,y1);
436                 }
437         }
438 }
439 
440 /*
441  * Repaint all or part of a wavepanel.
442  */
443 void
draw_wavepanel(GtkWidget * widget,GdkEventExpose * event,WavePanel * wp)444 draw_wavepanel(GtkWidget *widget, GdkEventExpose *event, WavePanel *wp)
445 {
446 	int w = widget->allocation.width;
447 	int h = widget->allocation.height;
448 	int x, y;
449 	int i;
450 
451 	if(wp->pixmap == NULL)
452 		return;
453 
454 	gdk_draw_rectangle(wp->pixmap, bg_gdk_gc, TRUE, 0,0, w,h);
455 
456 	if(wp->selected) {
457 /*		gdk_draw_line(wp->pixmap, hl_gdk_gc, 0,   0,  w-1, 0);
458 		gdk_draw_line(wp->pixmap, hl_gdk_gc, w-1, 0,  w-1, h-1);
459 		gdk_draw_line(wp->pixmap, hl_gdk_gc, w-1, h-1,  0, h-1);
460 		gdk_draw_line(wp->pixmap, hl_gdk_gc, 0,   h-1,  0, 0);
461 */
462 		gdk_draw_line(wp->pixmap, hl_gdk_gc, 1,   1,  w-2, 1);
463 		gdk_draw_line(wp->pixmap, hl_gdk_gc, w-2, 1,  w-2, h-2);
464 		gdk_draw_line(wp->pixmap, hl_gdk_gc, w-2, h-2,  1, h-2);
465 		gdk_draw_line(wp->pixmap, hl_gdk_gc, 1,   h-2,  1, 1);
466 
467 	}
468 	/* draw horizontal line at y=zero.  future: do real graticule here */
469 	if(wp->start_yval < 0 && wp->end_yval > 0) {
470 		y = val2y(wp, 0);
471 		gdk_draw_line(wp->pixmap, pg_gdk_gc, 0, y, w, y);
472 	}
473 
474 	/* draw waves */
475 	g_list_foreach(wp->vwlist, (GFunc)vw_wp_visit_draw, wp);
476 
477 	/* draw cursors */
478 	for(i = 0; i < 2; i++) {
479 		VBCursor *csp = wtable->cursor[i];
480 		if(csp->shown) {
481 			if(wp->start_xval <= csp->xval
482 			   && csp->xval <= wp->end_xval) {
483 				x = val2x(wp, csp->xval, wtable->logx);
484 				gdk_draw_line(wp->pixmap, csp->gdk_gc,
485 					      x, 0, x, h);
486 			}
487 		}
488 	}
489 	/* draw select-range line, if in this WavePanel */
490 	if(wtable->srange->drawn && wtable->srange->wp == wp)
491 		draw_srange(wtable->srange);
492 
493 	if(event) {
494 	  /* Draw the exposed portions of the pixmap in its window. */
495 		gdk_draw_pixmap(widget->window,
496 				widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
497 		  wp->pixmap,
498 		  event->area.x, event->area.y,
499 		  event->area.x, event->area.y,
500 		  event->area.width, event->area.height);
501 	} else {
502 	  /* Draw the whole thing. */
503 		gdk_draw_pixmap(widget->window,
504 				widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
505 		  wp->pixmap,
506 		  0, 0, 0, 0, w, h);
507 	}
508 }
509 
510 /*
511  * update text labeling the waveform graphs' X-axis
512  */
draw_labels(WaveTable * wt)513 void draw_labels(WaveTable *wt)
514 {
515 	gtk_label_set(GTK_LABEL(wt->xlabel_left), val2txt(wt->start_xval, 0));
516 	gtk_label_set(GTK_LABEL(wt->xlabel_right), val2txt(wt->end_xval, 0));
517 }
518 
519 /*
520  * redraw contents of all wavepanels
521  */
522 SCM_DEFINE(wtable_redraw_x, "wtable-redraw!", 0, 0, 0, (),
523 	   "Redraw the waveforms in all wavepanels")
524 #define FUNC_NAME s_wtable_redraw_x
525 {
526 	int i;
527 	WavePanel *wp;
528 	wtable->suppress_redraw = 0;
529 	for(i = 0; i < wtable->npanels; i++) {
530 		wp = wtable->panels[i];
531 		draw_wavepanel(wp->drawing, NULL, wp);
532 	}
533 	return SCM_UNSPECIFIED;
534 }
535 #undef FUNC_NAME
536 
537 /* Color allocation and related stuff for waveform drawing area
538  * background and cursors, done on first expose event.
539  * Actually, we do it all on the first expose of the first drawing area,
540  * and hope that this is OK.  They are all in the same GtkWindow.
541  */
alloc_colors(GtkWidget * widget)542 void alloc_colors(GtkWidget *widget)
543 {
544 	int i;
545 	if(win_colormap == NULL)
546 		win_colormap = gdk_window_get_colormap(widget->window);
547 
548 	/* background */
549 	if(bg_color_name) {  /* explicitly set background color */
550 		gdk_color_alloc(win_colormap, &bg_gdk_color);
551 		bg_gdk_gc = gdk_gc_new(widget->window);
552 		gdk_gc_set_foreground(bg_gdk_gc, &bg_gdk_color);
553 	} else {  /* use the widget's default one - usually grey */
554 		bg_gdk_color = widget->style->bg[GTK_WIDGET_STATE(widget)];
555 		bg_gdk_gc = widget->style->bg_gc[GTK_WIDGET_STATE(widget)];
556 	}
557 	if(v_flag)
558 		fprintf(stderr, "panel background pixel=0x%x rgb=%d,%d,%d\n",
559 			bg_gdk_color.pixel,
560 			bg_gdk_color.red,
561 			bg_gdk_color.green,
562 			bg_gdk_color.blue);
563 
564 	/* vertical bar cursors */
565 	for(i = 0; i < 2; i++) {
566 		GdkColor tmp_color;
567 		VBCursor *csp = wtable->cursor[i];
568 		if(!gdk_colormap_alloc_color(win_colormap, &csp->gdk_color, FALSE, TRUE)) {
569 			fprintf(stderr, "gdk_color_alloc failed for cursor %d\n", i);
570 			exit(2);
571 		}
572 		csp->gdk_gc = gdk_gc_new(widget->window);
573 		if(!csp->gdk_gc) {
574 			fprintf(stderr, "couldn't allocate cursor %d gc\n", i);
575 			exit(2);
576 		}
577 		/* compute pixel to draw so XOR makes it come out right
578 		 * on the background.
579 		 */
580 		tmp_color.pixel = csp->gdk_color.pixel ^ bg_gdk_color.pixel;
581 		gdk_gc_set_foreground(csp->gdk_gc, &tmp_color);
582 		gdk_gc_set_function(csp->gdk_gc, GDK_XOR);
583 
584 		if(v_flag)
585 			fprintf(stderr, "cursor[%d] pixel=0x%x drawpix=0x%x rgb=%d,%d,%d\n",
586 				i, csp->gdk_color.pixel, tmp_color.pixel,
587 				csp->gdk_color.red,
588 				csp->gdk_color.green,
589 				csp->gdk_color.blue);
590 	}
591 
592 	/* graticule or zero-line */
593 	gdk_colormap_alloc_color(win_colormap, &pg_gdk_color, FALSE, TRUE);
594 	pg_gdk_gc = gdk_gc_new(widget->window);
595 	if(!pg_gdk_gc) {
596 			fprintf(stderr, "couldn't allocate graticule gc\n");
597 			exit(2);
598 	}
599 	gdk_gc_set_foreground(pg_gdk_gc, &pg_gdk_color);
600 
601 	/* panel highlight */
602 	gdk_colormap_alloc_color(win_colormap, &hl_gdk_color, FALSE, TRUE);
603 	hl_gdk_gc = gdk_gc_new(widget->window);
604 	if(!hl_gdk_gc) {
605 			fprintf(stderr, "couldn't allocate highlight gc\n");
606 			exit(2);
607 	}
608 	gdk_gc_set_foreground(hl_gdk_gc, &hl_gdk_color);
609 }
610 
611 
612 /* TODO: figure out how to get these colors from styles in wv.gtkrc
613  * without the hack that we use for waveform colors (picking them up from
614  * labels of the same color).
615  */
setup_colors(WaveTable * wt)616 void setup_colors(WaveTable *wt)
617 {
618 	int i;
619 
620 	/* cursors */
621 	wt->cursor[0]->color_name = "white";
622 	wt->cursor[1]->color_name = "yellow";
623 	for(i = 0; i < 2; i++) {
624 		if(!gdk_color_parse(wt->cursor[i]->color_name,
625 				    &wt->cursor[i]->gdk_color)) {
626 			fprintf(stderr, "failed to parse cursor %d color\n", i);
627 			exit(1);
628 		}
629 	}
630 
631 	/* range-select line */
632 	if(!gdk_color_parse("white", &wt->srange->gdk_color)) {
633 		fprintf(stderr, "failed to parse selrange color\n");
634 		exit(1);
635 	}
636 
637 	/* waveform background */
638 	if(bg_color_name) {
639 		if(!gdk_color_parse(bg_color_name, &bg_gdk_color)) {
640 			fprintf(stderr, "failed to parse bg color\n");
641 			exit(1);
642 		}
643 	}
644 
645 	/* waveform panel axis lines or graticule */
646 	if(pg_color_name) {
647 		if(!gdk_color_parse(pg_color_name, &pg_gdk_color)) {
648 			fprintf(stderr, "failed to parse panel graticule color\n");
649 			exit(1);
650 		}
651 	}
652 	/* waveform panel axis lines or graticule */
653 	if(hl_color_name) {
654 		if(!gdk_color_parse(hl_color_name, &hl_gdk_color)) {
655 			fprintf(stderr, "failed to parse highlight color\n");
656 			exit(1);
657 		}
658 	}
659 }
660 
661 /* given a string containing a number, possibly with a spice-syntax suffix,
662  * return the value  */
spice2val(char * s)663 double spice2val(char *s)
664 {
665 	double val, mul;
666 	char *ep;
667 	val = strtod(s, &ep);
668 
669 	if(ep && ep != s && *ep) {
670 		switch(*ep) {
671 		case 'T':
672 			mul = 1e12;
673 			break;
674 		case 'G':
675 			mul = 1e9;
676 			break;
677 		case 'M':
678 			mul = 1e6;
679 			break;
680 		case 'k':
681 		case 'K':
682 			mul = 1e3;
683 			break;
684 		case 'm':
685 			mul = 1e-3;
686 			break;
687 		case 'u':
688 			mul = 1e-6;
689 			break;
690 		case 'n':
691 			mul = 1e-9;
692 			break;
693 		case 'p':
694 			mul = 1e-12;
695 			break;
696 		case 'f':
697 			mul = 1e-15;
698 			break;
699 		case 'a':
700 			mul = 1e-18;
701 			break;
702 		default:
703 			mul = 1;
704 			break;
705 		}
706 
707 		return val * mul;
708 	} else {
709 		return val;
710 	}
711 }
712 
713 SCM_DEFINE(spice_number, "spice->number", 1, 0, 0, (SCM str),
714 "Given a string SSTR containing a representation of a number,"
715 "possibly containing spice-style multiplier suffixes, return a real number.")
716 #define FUNC_NAME s_spice_number
717 {
718 	double dval;
719 	char *s;
720 	VALIDATE_ARG_STR_NEWCOPY(1, str, s);
721 
722 	dval = spice2val(s);
723 	free(s);
724 	return scm_make_real(dval);
725 }
726 #undef FUNC_NAME
727 
728 SCM_DEFINE(number_spice, "number->spice", 1, 0, 0, (SCM val),
729  "Given a real number VAL, return a string representation "
730 "in spice suffix syntax.")
731 #define FUNC_NAME s_number_spice
732 {
733 	double dval;
734 	char *s;
735 	VALIDATE_ARG_DBL_COPY(1, val, dval);
736 	s = val2txt(dval, 0);
737 	return scm_makfrom0str(s);
738 }
739 #undef FUNC_NAME
740 
741 /* guile initialization */
init_draw()742 void init_draw()
743 {
744 
745 #ifndef SCM_MAGIC_SNARF_INITS
746 #include "draw.x"
747 #endif
748 }
749