scope_disp.c
1 /** This file, 'halsc_disp.c', contains the portion of halscope 2 that actually displays waveforms. 3 */ 4 5 /** Copyright (C) 2003 John Kasunich 6 <jmkasunich AT users DOT sourceforge DOT net> 7 */ 8 9 /** This program is free software; you can redistribute it and/or 10 modify it under the terms of version 2 of the GNU General 11 Public License as published by the Free Software Foundation. 12 This library is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public 18 License along with this library; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 21 THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR 22 ANY HARM OR LOSS RESULTING FROM ITS USE. IT IS _EXTREMELY_ UNWISE 23 TO RELY ON SOFTWARE ALONE FOR SAFETY. Any machinery capable of 24 harming persons must have provisions for completely removing power 25 from all motors, etc, before persons enter any danger area. All 26 machinery must be designed to comply with local and national safety 27 codes, and the authors of this software can not, and do not, take 28 any responsibility for such compliance. 29 30 This code was written as part of the EMC HAL project. For more 31 information, go to www.linuxcnc.org. 32 */ 33 34 #include <math.h> 35 #include <sys/types.h> 36 #include <unistd.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <ctype.h> 40 #include <string.h> 41 42 #include "rtapi.h" /* RTAPI realtime OS API */ 43 #include "hal.h" /* HAL public API decls */ 44 #include "../hal_priv.h" /* private HAL decls */ 45 46 #include <gtk/gtk.h> 47 #include "miscgtk.h" /* generic GTK stuff */ 48 #include "scope_usr.h" /* scope related declarations */ 49 50 #define BUFLEN 80 /* length for sprintf buffers */ 51 52 /*********************************************************************** 53 * GLOBAL VARIABLES DECLARATIONS * 54 ************************************************************************/ 55 56 /*********************************************************************** 57 * LOCAL FUNCTION PROTOTYPES * 58 ************************************************************************/ 59 60 static void init_display_window(void); 61 static void clear_display_window(void); 62 static void update_readout(void); 63 static void draw_grid(void); 64 static void draw_baseline(int chan_num, int highlight); 65 static void draw_waveform(int chan_num, int highlight); 66 static void draw_triggerline(int chan_num, int highlight); 67 static void handle_window_expose(GtkWidget * widget, gpointer data); 68 static int handle_click(GtkWidget *widget, GdkEventButton *event, gpointer data); 69 static int handle_release(GtkWidget *widget, GdkEventButton *event, gpointer data); 70 static int handle_motion(GtkWidget *widget, GdkEventButton *event, gpointer data); 71 static int handle_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer data); 72 static void conflict_avoid(int *y, int h); 73 static int conflict_avoid_dy(int y, int h, int dy); 74 static void conflict_reset(int height); 75 76 /*********************************************************************** 77 * PUBLIC FUNCTIONS * 78 ************************************************************************/ 79 80 static int DRAWING = 0; 81 static int cursor_valid = 0; 82 static double cursor_time = 0; 83 static double cursor_prev_value = 0.; 84 static double cursor_value = 0; 85 86 void init_display(void) 87 { 88 89 /* allocate a user space buffer */ 90 ctrl_usr->disp_buf = g_malloc(sizeof(scope_data_t) * ctrl_shm->buf_len); 91 if (ctrl_usr->disp_buf == 0) { 92 /* malloc failed */ 93 /* should never get here - gmalloc checks its return value */ 94 exit(-1); 95 } 96 /* initialize waveform window */ 97 init_display_window(); 98 99 invalidate_all_channels(); 100 } 101 102 void invalidate_channel(int chan) 103 { 104 ctrl_usr->vert.data_offset[chan - 1] = -1; 105 } 106 107 void invalidate_all_channels(void) 108 { 109 int n; 110 111 for (n = 0; n < 16; n++) { 112 ctrl_usr->vert.data_offset[n] = -1; 113 } 114 } 115 116 void request_display_refresh(int delay) 117 { 118 if (delay > 5) { 119 delay = 5; 120 } 121 if (delay < 1) { 122 delay = 1; 123 } 124 /* request a refresh after approximately delay/10 seconds of idleness */ 125 ctrl_usr->display_refresh_timer = delay; 126 } 127 128 static int motion_x = -1, motion_y = -1; 129 130 static void calculate_offset(int chan_num) { 131 int n; 132 scope_chan_t *chan = &(ctrl_usr->chan[chan_num]); 133 scope_data_t *dptr = 134 ctrl_usr->disp_buf + ctrl_usr->vert.data_offset[chan_num]; 135 int type = chan->data_type; 136 137 double sum=0, value; 138 139 if(!chan->ac_offset) return; 140 141 for(n=0; n < ctrl_usr->samples; n++) { 142 switch (type) { 143 case HAL_BIT: 144 if (dptr->d_u8) { 145 value = 1.0; 146 } else { 147 value = 0.0; 148 }; 149 break; 150 case HAL_FLOAT: 151 value = dptr->d_real; 152 break; 153 case HAL_S32: 154 value = dptr->d_s32; 155 break; 156 case HAL_U32: 157 value = dptr->d_u32; 158 break; 159 default: 160 value = 0.0; 161 break; 162 } 163 sum = sum + value; 164 } 165 if(n == 0) { 166 chan->vert_offset = 0; 167 } else { 168 chan->vert_offset = sum / n; 169 } 170 } 171 172 void refresh_display(void) 173 { 174 int n; 175 scope_disp_t *disp; 176 scope_vert_t *vert; 177 scope_horiz_t *horiz; 178 int depth; 179 double pixels_per_div, pixels_per_sec, overall_record_length; 180 double screen_center_time, screen_start_time, screen_end_time; 181 182 /* cancel any pending refresh request */ 183 ctrl_usr->display_refresh_timer = 0; 184 /* set pointers */ 185 disp = &(ctrl_usr->disp); 186 vert = &(ctrl_usr->vert); 187 horiz = &(ctrl_usr->horiz); 188 /* get window pointer */ 189 disp->win = disp->drawing->window; 190 if (disp->win == NULL) { 191 /* window isn't visible yet, do nothing */ 192 printf("refresh_display(): win = NULL, bailing!\n"); 193 return; 194 } 195 /* create drawing context if needed */ 196 if (disp->context == NULL) { 197 disp->context = gdk_gc_new(disp->win); 198 } 199 200 /* get window dimensions */ 201 gdk_window_get_geometry(disp->win, NULL, NULL, &(disp->width), 202 &(disp->height), &depth); 203 /* calculate horizontal params that depend on width */ 204 pixels_per_div = disp->width * 0.1; 205 pixels_per_sec = pixels_per_div / horiz->disp_scale; 206 disp->pixels_per_sample = pixels_per_sec * horiz->sample_period; 207 overall_record_length = horiz->sample_period * ctrl_shm->rec_len; 208 screen_center_time = overall_record_length * horiz->pos_setting; 209 screen_start_time = screen_center_time - (5.0 * horiz->disp_scale); 210 disp->horiz_offset = screen_start_time * pixels_per_sec; 211 disp->start_sample = screen_start_time / horiz->sample_period; 212 if (disp->start_sample < 0) { 213 disp->start_sample = 0; 214 } 215 screen_end_time = screen_center_time + (5.0 * horiz->disp_scale); 216 disp->end_sample = (screen_end_time / horiz->sample_period) + 1; 217 if (disp->end_sample > ctrl_shm->rec_len - 1) { 218 disp->end_sample = ctrl_shm->rec_len - 1; 219 } 220 221 { 222 GdkRectangle rect = {0, 0, disp->width, disp->height}; 223 GdkRegion *region = gdk_region_rectangle(&rect); 224 gdk_window_begin_paint_region(disp->drawing->window, region); 225 gdk_region_destroy(region); 226 } 227 228 DRAWING = 1; 229 clear_display_window(); 230 draw_grid(); 231 232 /* calculate offsets for AC-offset channels */ 233 for (n = 0; n < 16; n++) { 234 if (vert->chan_enabled[n]) calculate_offset(n); 235 } 236 237 /* draw baselines first */ 238 for (n = 0; n < 16; n++) { 239 if ((vert->chan_enabled[n]) && (n + 1 != vert->selected)) { 240 draw_baseline(n + 1, FALSE); 241 } 242 } 243 if (vert->chan_enabled[vert->selected - 1]) { 244 draw_baseline(vert->selected, TRUE); 245 } 246 247 /* Draw trigger line */ 248 if (vert->chan_enabled[ctrl_shm->trig_chan - 1]) { 249 draw_triggerline(ctrl_shm->trig_chan, 250 ctrl_shm->trig_chan == vert->selected); 251 } 252 253 conflict_reset(disp->height); 254 255 /* draw non-highlighted waveforms next */ 256 for (n = 0; n < 16; n++) { 257 if ((vert->chan_enabled[n]) && (vert->data_offset[n] >= 0) 258 && (n + 1 != vert->selected)) { 259 draw_waveform(n + 1, FALSE); 260 } 261 } 262 /* draw highlighted waveform last */ 263 if ((vert->chan_enabled[vert->selected - 1]) 264 && (vert->data_offset[vert->selected - 1] >= 0)) { 265 draw_waveform(vert->selected, TRUE); 266 } 267 268 update_readout(); 269 270 gdk_window_end_paint(disp->drawing->window); 271 } 272 273 /*********************************************************************** 274 * LOCAL FUNCTIONS * 275 ************************************************************************/ 276 277 gboolean alloc_color(GdkColor * color, GdkColormap * map, 278 unsigned char red, unsigned char green, unsigned char blue) 279 { 280 int retval; 281 282 color->red = ((unsigned long) red) << 8; 283 color->green = ((unsigned long) green) << 8; 284 color->blue = ((unsigned long) blue) << 8; 285 color->pixel = 286 ((unsigned long) red) << 16 | ((unsigned long) green) << 8 | 287 ((unsigned long) blue); 288 retval = gdk_colormap_alloc_color(map, color, FALSE, TRUE); 289 if (retval == 0) { 290 printf("alloc_color( %d, %d, %d ) failed\n", red, green, blue); 291 } 292 return retval; 293 } 294 295 #if 0 /* this will be needed if/when I allow user defined colors */ 296 static void free_color(GdkColor * color, GdkColormap * map) 297 { 298 gdk_colormap_free_colors(map, color, 1); 299 } 300 #endif 301 302 int normal_colors[16][3] = { 303 {204, 0, 0}, 304 { 0, 204, 204}, 305 {102, 204, 0}, 306 {102, 0, 204}, 307 {204, 153, 0}, 308 { 0, 204, 51}, 309 { 0, 51, 204}, 310 {204, 0, 153}, 311 {153, 95, 61}, 312 {141, 153, 61}, 313 { 72, 153, 61}, 314 { 61, 153, 118}, 315 { 61, 118, 153}, 316 { 72, 61, 153}, 317 {141, 61, 153}, 318 {153, 61, 95}, 319 }; 320 321 int selected_colors[16][3] = { 322 {255, 204, 204}, 323 {204, 255, 255}, 324 {229, 255, 204}, 325 {229, 204, 255}, 326 {255, 242, 204}, 327 {204, 255, 216}, 328 {204, 216, 255}, 329 {255, 204, 242}, 330 {229, 186, 160}, 331 {220, 229, 160}, 332 {169, 229, 160}, 333 {160, 229, 203}, 334 {160, 203, 229}, 335 {169, 160, 229}, 336 {220, 160, 229}, 337 {229, 160, 186}, 338 }; 339 340 341 342 static void init_display_window(void) 343 { 344 scope_disp_t *disp; 345 int i; 346 347 disp = &(ctrl_usr->disp); 348 349 /* allocate a drawing area */ 350 disp->drawing = gtk_drawing_area_new(); 351 /* put it into the display window */ 352 gtk_box_pack_start(GTK_BOX(ctrl_usr->waveform_win), disp->drawing, TRUE, 353 TRUE, 0); 354 /* hook up a function to handle expose events */ 355 gtk_signal_connect(GTK_OBJECT(disp->drawing), "expose_event", 356 GTK_SIGNAL_FUNC(handle_window_expose), NULL); 357 gtk_signal_connect(GTK_OBJECT(disp->drawing), "button_release_event", 358 GTK_SIGNAL_FUNC(handle_release), NULL); 359 gtk_signal_connect(GTK_OBJECT(disp->drawing), "button_press_event", 360 GTK_SIGNAL_FUNC(handle_click), NULL); 361 gtk_signal_connect(GTK_OBJECT(disp->drawing), "motion_notify_event", 362 GTK_SIGNAL_FUNC(handle_motion), NULL); 363 gtk_signal_connect(GTK_OBJECT(disp->drawing), "scroll_event", 364 GTK_SIGNAL_FUNC(handle_scroll), NULL); 365 gtk_widget_add_events(GTK_WIDGET(disp->drawing), 366 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 367 | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK 368 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK 369 | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON1_MOTION_MASK 370 | GDK_SCROLL_MASK ); 371 gtk_widget_show(disp->drawing); 372 /* get color map */ 373 disp->map = gtk_widget_get_colormap(disp->drawing); 374 /* allocate colors */ 375 alloc_color(&(disp->color_bg), disp->map, 0, 0, 0); 376 alloc_color(&(disp->color_grid), disp->map, 255, 255, 255); 377 alloc_color(&disp->color_baseline, disp->map, 128, 128, 128); 378 for(i = 0; i<16; i++) { 379 alloc_color(&(disp->color_normal[i]), disp->map, normal_colors[i][0], normal_colors[i][1], normal_colors[i][2]); 380 alloc_color(&(disp->color_selected[i]), disp->map, selected_colors[i][0], selected_colors[i][1], selected_colors[i][2]); 381 } 382 383 } 384 385 static void handle_window_expose(GtkWidget * widget, gpointer data) 386 { 387 /* we don't want to react immediately - sometime we get a burst of expose 388 events - instead we request a refresh for later */ 389 request_display_refresh(2); 390 } 391 392 void clear_display_window(void) 393 { 394 scope_disp_t *disp; 395 396 disp = &(ctrl_usr->disp); 397 /* set color to draw */ 398 gdk_gc_set_foreground(disp->context, &(disp->color_bg)); 399 /* draw a big rectangle to clear the screen */ 400 gdk_draw_rectangle(disp->win, disp->context, TRUE, 0, 0, disp->width, 401 disp->height); 402 } 403 404 void draw_grid(void) 405 { 406 scope_disp_t *disp; 407 double xscale, yscale; 408 int xmajor, xminor, ymajor, yminor; 409 int nx, ny, m; 410 double fx, fy; 411 int x, y; 412 413 disp = &(ctrl_usr->disp); 414 /* set color to draw */ 415 gdk_gc_set_foreground(disp->context, &(disp->color_grid)); 416 /* calculate scale factors */ 417 xscale = disp->width - 1.0; 418 yscale = disp->height - 1.0; 419 /* calculate grid spacings */ 420 xmajor = xscale * 0.1; 421 if (xmajor >= 40) { 422 xminor = 10; 423 } else if (xmajor >= 20) { 424 xminor = 5; 425 } else { 426 xminor = xmajor / 4; 427 } 428 ymajor = yscale * 0.1; 429 if (ymajor >= 40) { 430 yminor = 10; 431 } else if (ymajor >= 20) { 432 yminor = 5; 433 } else { 434 yminor = ymajor / 4; 435 } 436 /* draw the vertical lines */ 437 for (nx = 0; nx <= 10; nx++) { 438 /* calc the major division x coordinate */ 439 fx = nx * 0.1; 440 for (ny = 0; ny <= 10; ny++) { 441 /* calc the major division y coordinate */ 442 fy = ny * 0.1; 443 /* draw the major division point */ 444 x = fx * xscale; 445 y = fy * yscale; 446 gdk_draw_point(disp->win, disp->context, x, y); 447 /* draw minor divisions (vertical) */ 448 if (ny < 10) { 449 for (m = 1; m < yminor; m++) { 450 y = (((0.1 * m) / yminor) + fy) * yscale; 451 gdk_draw_point(disp->win, disp->context, x, y); 452 } 453 } 454 /* draw minor divisions (horizontal) */ 455 if (nx < 10) { 456 y = fy * yscale; 457 for (m = 1; m < xminor; m++) { 458 x = (((0.1 * m) / xminor) + fx) * xscale; 459 gdk_draw_point(disp->win, disp->context, x, y); 460 } 461 } 462 } 463 } 464 } 465 466 static int select_x, select_y, target; 467 static double min_dist; 468 static int select_trace(int x, int y) { 469 int n; 470 scope_disp_t *disp = &(ctrl_usr->disp); 471 472 min_dist = hypot(disp->width, disp->height) / 100.; 473 if(min_dist < 5) min_dist = 5; 474 target = -1; 475 476 DRAWING = 0; 477 select_x = x; 478 select_y = y; 479 for(n=0; n<16; n++) { 480 scope_vert_t *vert = &(ctrl_usr->vert); 481 draw_baseline(n+1, FALSE); 482 if((vert->chan_enabled[n]) && (vert->data_offset[n] >= 0)) { 483 draw_waveform(n+1, FALSE); 484 } 485 } 486 draw_triggerline(ctrl_shm->trig_chan, FALSE); 487 return target; 488 } 489 490 static int handle_release(GtkWidget *widget, GdkEventButton *event, gpointer data) { 491 return 1; 492 } 493 494 static void change_zoom(int dir, int x) { 495 scope_horiz_t *horiz = &(ctrl_usr->horiz); 496 scope_disp_t *disp = &(ctrl_usr->disp); 497 498 double old_pixels_per_sample, pixels_per_div, 499 pixels_per_sec, new_pixels_per_sample, old_fraction, new_fraction; 500 501 old_pixels_per_sample = disp->pixels_per_sample; 502 503 set_horiz_zoom(horiz->zoom_setting + dir); 504 505 /* calculate horizontal params that depend on width */ 506 pixels_per_div = disp->width * 0.1; 507 pixels_per_sec = pixels_per_div / horiz->disp_scale; 508 disp->pixels_per_sample = new_pixels_per_sample = 509 pixels_per_sec * horiz->sample_period; 510 511 // how many samples away from the center of the window is this 512 // pixel? 513 old_fraction = (x - disp->width / 2) / old_pixels_per_sample / ctrl_shm->rec_len; 514 // and new? 515 new_fraction = (x - disp->width / 2) / new_pixels_per_sample / ctrl_shm->rec_len; 516 // displace by the difference 517 set_horiz_pos( horiz->pos_setting - new_fraction + old_fraction ); 518 } 519 520 static int handle_click(GtkWidget *widget, GdkEventButton *event, gpointer data) { 521 scope_vert_t *vert = &(ctrl_usr->vert); 522 scope_disp_t *disp = &(ctrl_usr->disp); 523 motion_y = event->y; 524 motion_x = event->x; 525 if(event->button == 4) { // zoom in 526 change_zoom(1, event->x); 527 } else if(event->button == 5) { // zoom out 528 change_zoom(-1, event->x); 529 } else { 530 int z = select_trace(event->x, event->y); 531 int new_channel = z & 0xff; 532 int channel_part = z >> 8; 533 534 disp->selected_part = channel_part; 535 536 if(new_channel != vert->selected) { 537 if(z == -1) vert->selected = -1; 538 else vert->selected = new_channel; 539 channel_changed(); 540 } 541 if(channel_part == 3) { 542 set_trigger_polarity(!ctrl_shm->trig_edge); 543 } 544 } 545 return 1; 546 } 547 548 static int get_cursor_info(double *t, double *p, double *v) { 549 if(!cursor_valid) return 0; 550 if(t) *t = cursor_time; 551 if(p) *p = cursor_prev_value; 552 if(v) *v = cursor_value; 553 return 1; 554 } 555 556 static int handle_scroll(GtkWidget *widget, GdkEventScroll *event, gpointer data) { 557 change_zoom(event->direction ? -1 : 1, event->x); 558 return TRUE; 559 } 560 561 static void middle_drag(int dx) { 562 scope_disp_t *disp = &(ctrl_usr->disp); 563 scope_horiz_t *horiz = &(ctrl_usr->horiz); 564 double dt = (dx / disp->pixels_per_sample) / ctrl_shm->rec_len; 565 set_horiz_pos(horiz->pos_setting + 5 * dt); 566 refresh_display(); 567 } 568 569 static double snap(int y) { 570 scope_disp_t *disp = &(ctrl_usr->disp); 571 double new_position = y * 1.0 / disp->height; 572 double mod = fmod(new_position, 0.05); 573 if(mod > .045) new_position = new_position + (.05-mod); 574 if(mod < .005) new_position = new_position - mod; 575 return new_position; 576 } 577 578 static void left_drag(int dy, int y, GdkModifierType state) { 579 scope_disp_t *disp = &(ctrl_usr->disp); 580 scope_vert_t *vert = &(ctrl_usr->vert); 581 scope_chan_t *chan = &(ctrl_usr->chan[vert->selected - 1]); 582 583 if(vert->selected == -1) return; 584 585 if(disp->selected_part == 2 || (state & GDK_CONTROL_MASK)) { 586 double new_position = snap(y); 587 set_trigger_level(new_position); 588 } else if(disp->selected_part == 1 || (state & GDK_SHIFT_MASK)) { 589 double new_position = snap(y); 590 set_vert_pos(new_position); 591 // chan->position = new_position; 592 refresh_display(); 593 motion_y = y; 594 } else { 595 if(abs(dy) > 5) { 596 int direction = dy > 0 ? 1 : -1; 597 int baseline_y = chan->position * disp-> height; 598 int side = select_y > baseline_y ? -1 : 1; 599 set_vert_scale(chan->scale_index + direction * side); 600 motion_y = y; 601 } 602 } 603 } 604 605 static int handle_motion(GtkWidget *widget, GdkEventButton *event, gpointer data) { 606 scope_disp_t *disp = &(ctrl_usr->disp); 607 GdkModifierType mod; 608 int x, y; 609 610 gdk_window_get_pointer(disp->drawing->window, &x, &y, &mod); 611 if(mod & GDK_BUTTON1_MASK) { 612 left_drag(y-motion_y, y, event->state); 613 return TRUE; 614 } 615 if(mod & GDK_BUTTON2_MASK) { 616 middle_drag(motion_x - x); 617 } 618 motion_x = x; 619 refresh_display(); 620 return TRUE; 621 } 622 623 #define TIPFORMAT "<tt>f(% 8.5f) = % 8.5f (ddt % 8.5f)</tt>" 624 void update_readout(void) { 625 scope_vert_t *vert = &(ctrl_usr->vert); 626 scope_horiz_t *horiz = &(ctrl_usr->horiz); 627 char tip[512]; 628 GdkRectangle r = {vert->readout_label->allocation.x, 629 vert->readout_label->allocation.y, 630 vert->readout_label->allocation.width, 631 vert->readout_label->allocation.height}; 632 if(vert->selected != -1) { 633 double t=0, p=0, v=0; 634 int result = get_cursor_info(&t, &p, &v); 635 if(result > 0) { 636 snprintf(tip, sizeof(tip), TIPFORMAT, t, v, (v - p)/horiz->sample_period); 637 } else { 638 strcpy(tip, ""); 639 } 640 } else { 641 strcpy(tip, ""); 642 } 643 644 gtk_label_set_markup(GTK_LABEL(vert->readout_label), tip); 645 646 gtk_widget_draw(vert->readout_label, &r); 647 648 } 649 650 struct pt { double x, y; }; 651 static double dot(struct pt *a, struct pt *b) { 652 return a->x * b->x + a->y * b->y; 653 } 654 655 static double mag(struct pt *p) { 656 return hypot(p->x, p->y); 657 } 658 659 static double distance_point_line(int x, int y, int x1, int y1, int x2, int y2) { 660 struct pt M = {x2-x1, y2-y1}, 661 Q = {x-x1, y-y1}, 662 R; 663 664 double t0 = dot(&M, &Q) / dot(&M, &M); 665 if(t0 < 0) t0 = 0; 666 if(t0 > 1) t0 = 1; 667 R.x = x - (x1 + t0 * M.x); 668 R.y = y - (y1 + t0 * M.y); 669 return mag(&R); 670 } 671 672 #define COORDINATE_CLIP(coord) ((coord < -32768) ? -32768 : (coord > 32767) ? 32767 : coord) 673 674 void line(int chan_num, int x1, int y1, int x2, int y2) { 675 scope_disp_t *disp = &(ctrl_usr->disp); 676 if(DRAWING) { 677 gdk_draw_line(disp->win, disp->context, COORDINATE_CLIP(x1), COORDINATE_CLIP(y1), COORDINATE_CLIP(x2), COORDINATE_CLIP(y2)); 678 } else { 679 double dist = distance_point_line(select_x, select_y, x1, y1, x2, y2); 680 if(dist < min_dist) { 681 min_dist = dist; 682 target = chan_num; 683 } 684 } 685 } 686 687 void lines(int chan_num, GdkPoint points[], gint npoints) { 688 double dist; 689 690 scope_disp_t *disp = &(ctrl_usr->disp); 691 if(DRAWING) { 692 gdk_draw_lines(disp->win, disp->context, points, npoints); 693 } else { 694 int x1 = points[0].x, y1 = points[0].y, x2, y2, i; 695 for(i=1; i<npoints; i++) { 696 x2 = points[i].x; y2 = points[i].y; 697 dist = distance_point_line(select_x, select_y, x1, y1, x2, y2); 698 if(dist < min_dist) { 699 min_dist = dist; 700 target = chan_num; 701 } 702 x1 = x2; y1 = y2; 703 } 704 } 705 } 706 707 708 709 static 710 void draw_triggerline(int chan_num, int highlight) { 711 static gint8 dashes[2] = {2,4}; 712 scope_disp_t *disp = &(ctrl_usr->disp); 713 scope_chan_t *chan = &(ctrl_usr->chan[chan_num - 1]); 714 scope_trig_t *trig = &(ctrl_usr->trig); 715 double yfoffset = chan->vert_offset; 716 double ypoffset = chan->position * disp->height; 717 double yscale = disp->height / (-10.0 * chan->scale); 718 double fp_level = 719 chan->scale * ((chan->position - trig->level) * 10) + 720 chan->vert_offset; 721 722 int y1 = (fp_level-yfoffset) * yscale + ypoffset; 723 double dx = hypot(disp->width, disp->height) * .01; 724 double dy = dx * 1.3; 725 if(dx < 5) dx = 5; 726 if(dy < dx + 1) dy = dx + 1; 727 728 if(chan->data_type == HAL_BIT) 729 y1 = ypoffset; 730 731 if(ctrl_shm->trig_edge) dy = -dy; 732 733 if(highlight) { 734 gdk_gc_set_foreground(disp->context, &(disp->color_selected[chan_num-1])); 735 } else { 736 gdk_gc_set_foreground(disp->context, &(disp->color_normal[chan_num-1])); 737 } 738 gdk_gc_set_dashes(disp->context, 0, dashes, 2); 739 gdk_gc_set_line_attributes(disp->context, 0, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER); 740 line(chan_num | 0x200, 0, y1, disp->width, y1); 741 gdk_gc_set_line_attributes(disp->context, 0, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); 742 if(highlight) { 743 gdk_gc_set_foreground(disp->context, &(disp->color_grid)); 744 } else { 745 gdk_gc_set_foreground(disp->context, &(disp->color_baseline)); 746 } 747 line(chan_num | 0x300, 2*dx, y1, 2*dx, y1 + 2*dy); 748 line(chan_num | 0x300, dx, y1+dy, 2*dx, y1 + 2*dy); 749 line(chan_num | 0x300, 3*dx, y1+dy, 2*dx, y1 + 2*dy); 750 } 751 752 753 void draw_baseline(int chan_num, int highlight) { 754 scope_disp_t *disp = &(ctrl_usr->disp); 755 scope_chan_t *chan = &(ctrl_usr->chan[chan_num - 1]); 756 double yfoffset = chan->vert_offset; 757 double ypoffset = chan->position * disp->height; 758 double yscale = disp->height / (-10.0 * chan->scale); 759 int y1 = -yfoffset * yscale + ypoffset;; 760 if(highlight) { 761 gdk_gc_set_foreground(disp->context, &(disp->color_grid)); 762 } else { 763 gdk_gc_set_foreground(disp->context, &(disp->color_baseline)); 764 } 765 line(chan_num | 0x100, 0, y1, disp->width, y1); 766 } 767 768 /* waveform styles: if neither is defined, an intermediate style is used */ 769 // #define DRAW_STEPPED 770 // #define DRAW_SMOOTH 771 772 void draw_waveform(int chan_num, int highlight) 773 { 774 scope_data_t *dptr; 775 int start, end, n, sample_len; 776 scope_disp_t *disp; 777 scope_chan_t *chan; 778 double xscale, xoffset; 779 double yscale, yfoffset, ypoffset, fy, prev_fy = 0.; 780 hal_type_t type; 781 int x1, y1, x2, y2, miny, maxy, midx, ct, pn; 782 int first=1; 783 scope_horiz_t *horiz = &(ctrl_usr->horiz); 784 785 cursor_valid = 0; 786 disp = &(ctrl_usr->disp); 787 chan = &(ctrl_usr->chan[chan_num - 1]); 788 /* calculate a bunch of local vars */ 789 sample_len = ctrl_shm->sample_len; 790 xscale = disp->pixels_per_sample; 791 xoffset = disp->horiz_offset; 792 miny = -disp->height; 793 maxy = 2 * disp->height; 794 type = chan->data_type; 795 yscale = disp->height / (-10.0 * chan->scale); 796 yfoffset = chan->vert_offset; 797 ypoffset = chan->position * disp->height; 798 /* point to first sample in the record for this channel */ 799 dptr = ctrl_usr->disp_buf + ctrl_usr->vert.data_offset[chan_num - 1]; 800 /* point to first one that gets displayed */ 801 start = disp->start_sample; 802 end = disp->end_sample; 803 ct = end - start + 1; 804 GdkPoint points[2*ct]; 805 pn = 0; 806 n = start; 807 dptr += n * sample_len; 808 809 810 /* set color to draw */ 811 if (highlight) { 812 gdk_gc_set_foreground(disp->context, &(disp->color_selected[chan_num-1])); 813 } else { 814 gdk_gc_set_foreground(disp->context, &(disp->color_normal[chan_num-1])); 815 } 816 817 x1 = y1 = 0; 818 while (n <= end) { 819 /* calc x coordinate of this point */ 820 x2 = (n * xscale) - xoffset; 821 /* calc y coordinate of this point */ 822 switch (type) { 823 case HAL_BIT: 824 if (dptr->d_u8) { 825 fy = 1.0; 826 } else { 827 fy = 0.0; 828 }; 829 break; 830 case HAL_FLOAT: 831 fy = dptr->d_real; 832 break; 833 case HAL_S32: 834 fy = dptr->d_s32; 835 break; 836 case HAL_U32: 837 fy = dptr->d_u32; 838 break; 839 default: 840 fy = 0.0; 841 break; 842 } 843 y2 = ((fy - yfoffset) * yscale) + ypoffset; 844 if (y2 < miny) { 845 y2 = miny; 846 } else if (y2 > maxy) { 847 y2 = maxy; 848 } 849 x1 = COORDINATE_CLIP(x1); 850 x2 = COORDINATE_CLIP(x2); 851 y1 = COORDINATE_CLIP(y1); 852 y2 = COORDINATE_CLIP(y2); 853 /* don't draw segment ending at first point */ 854 if (n > start) { 855 if(pn == 0) { 856 points[pn].x = x1; points[pn].y = y1; pn++; 857 } 858 if(xscale < 1) { 859 if(points[pn-1].x != x2 || points[pn-1].y != y2) { 860 points[pn].x = x2; points[pn].y = y2; pn++; 861 } 862 } else { 863 #if defined(DRAW_SMOOTH) 864 /* this is a smoothed line display */ 865 points[pn].x = x2; points[pn].y = y2; pn++; 866 #elif defined(DRAW_STEPPED) 867 /* this is a stepped one */ 868 points[pn].x = x1; points[pn].y = y2; pn++; 869 points[pn].x = x2; points[pn].y = y2; pn++; 870 #else 871 /* this is halfway between the two extremes */ 872 midx = (x1 + x2) / 2; 873 if(midx != x2) { 874 points[pn].x = midx; points[pn].y = y2; pn++; 875 } 876 points[pn].x = x2; points[pn].y = y2; pn++; 877 #endif 878 } 879 if(first && highlight && DRAWING && x2 >= motion_x) { 880 first = 0; 881 gdk_draw_arc(disp->win, disp->context, TRUE, 882 x2-3, y2-3, 7, 7, 0, 360*64); 883 cursor_prev_value = prev_fy; 884 cursor_value = fy; 885 cursor_time = (n - ctrl_shm->pre_trig)*horiz->sample_period; 886 cursor_valid = 1; 887 } 888 } 889 /* end of this segment is start of next one */ 890 x1 = x2; 891 y1 = y2; 892 893 /* point to next sample */ 894 dptr += sample_len; 895 n++; 896 prev_fy = fy; 897 } 898 if(pn) { 899 lines(chan_num, points, pn); 900 if(DRAWING) { 901 PangoLayout *p; 902 int y = points[0].y; 903 char scale[HAL_NAME_LEN]; 904 char buffer[2 * HAL_NAME_LEN]; 905 int h; 906 PangoRectangle r; 907 908 format_scale_value(scale, sizeof(scale), chan->scale); 909 snprintf(buffer, sizeof(buffer), "%s\n%s", chan->name, scale); 910 p=gtk_widget_create_pango_layout(disp->drawing, buffer); 911 pango_layout_get_extents(p, NULL, &r); 912 h = PANGO_PIXELS(r.height); 913 914 if(y < 0 || y+h > disp->height) 915 // if the first sample isn't visible, try the zero value 916 y = (0-yfoffset) * yscale + ypoffset; 917 if(y < 0 || y+h > disp->height) 918 // if that's not visible either, try the offset value 919 y = ypoffset; 920 921 conflict_avoid(&y, h); 922 gdk_draw_layout(disp->win, disp->context, 5, y, p); 923 g_object_unref(p); 924 } 925 } 926 } 927 928 static int ch=0; 929 // X limits all windows to 16-bit heights, so this static array will be OK 930 static char conflict_map[32768]; 931 932 void conflict_reset(int h) { 933 ch = h; 934 memset(conflict_map, 0, sizeof(conflict_map)); 935 } 936 937 int conflict_avoid_dy(int y0, int h, int dy) { 938 int oc = 0; 939 for(; y0 > 0 && y0 < ch-h; y0 += dy) { 940 if(conflict_map[y0]) { oc = 0; } 941 oc++; 942 if(oc == h) break; 943 } 944 return y0; 945 } 946 947 void conflict_avoid(int *y, int h) { 948 int yd = conflict_avoid_dy(*y, h, 1)-h; 949 int yu = conflict_avoid_dy(*y, h, -1); 950 if(abs(yd-*y) < abs(yu-*y)) *y = yd; 951 else *y = yu; 952 memset(conflict_map+*y, 1, h); 953 } 954 955 // vim:sts=4:sw=4:et