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