More concurrency and responsiveness fixes
[xiph/unicode.git] / sushivision / panel-1d.c
blob4adb5af587195f2c148106fb409372aa3cb21f8c
1 /*
3 * sushivision copyright (C) 2006 Monty <monty@xiph.org>
5 * sushivision is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
9 *
10 * sushivision is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with sushivision; see the file COPYING. If not, write to the
17 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
22 #define _GNU_SOURCE
23 #include <string.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <math.h>
28 #include <signal.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <gtk/gtk.h>
32 #include <cairo-ft.h>
33 #include <gdk/gdkkeysyms.h>
34 #include "internal.h"
36 #define LINETYPES 10
37 static char *line_name[LINETYPES+1] = {
38 "line",
39 "dot",
40 "circle",
41 "square",
42 "cross",
43 "triangle",
44 "line and circle",
45 "line and square",
46 "line and cross",
47 "line and triangle",
48 NULL
51 static void update_context_menus(sushiv_panel_t *p);
53 // called internally, assumes we hold lock
54 // redraws the data, does not compute the data
55 static void _sushiv_panel1d_remap(sushiv_panel_t *p){
56 sushiv_panel1d_t *p1 = p->subtype->p1;
57 Plot *plot = PLOT(p->private->graph);
58 cairo_surface_t *cs = plot->back;
59 cairo_t *c = cairo_create(cs);
61 cairo_set_line_width(c,1.);
63 if(plot){
64 int xi,i;
65 int dw = p1->data_size;
66 double h = p1->panel_h;
68 /* blank frame to black */
69 cairo_set_source_rgb (c, 0,0,0);
70 cairo_paint(c);
72 if(p1->data_vec){
74 /* do the panel and plot scales match? If not, redraw the plot
75 scales */
77 scalespace sx = (p1->flip?p1->y:p1->x);
78 scalespace sy = (p1->flip?p1->x:p1->y);
80 if(memcmp(&sx,&plot->x,sizeof(sx)) ||
81 memcmp(&sy,&plot->y,sizeof(sy))){
82 plot->x = sx;
83 plot->y = sy;
85 plot_draw_scales(plot);
88 /* by objective */
89 for(i=0;i<p->objectives;i++){
90 double *data_vec = p1->data_vec[i];
91 if(data_vec){
92 double yprev=NAN,xprev=NAN;
94 u_int32_t color = mapping_calc(p1->mappings+i,0,0);
96 cairo_set_source_rgb(c,
97 ((color>>16)&0xff)/255.,
98 ((color>>8)&0xff)/255.,
99 ((color)&0xff)/255.);
101 /* by x */
102 for(xi=0;xi<dw;xi++){
103 double val = data_vec[xi];
104 double xpixel = xi;
105 double ypixel = NAN;
107 /* in linked panels, the data vector doesn't match the graph width; map */
108 if(p1->link_x || p1->link_y)
109 xpixel = scalespace_pixel(&p1->x,scalespace_value(&p1->vs,xpixel))+.5;
111 /* map/render result */
112 if(!isnan(val))
113 ypixel = scalespace_pixel(&p1->y,val)+.5;
115 if(!isnan(ypixel) && !isnan(yprev)){
116 if(p1->flip){
117 cairo_move_to(c,yprev,h-xprev);
118 cairo_line_to(c,ypixel,h-xpixel);
119 }else{
120 cairo_move_to(c,xprev,h-yprev);
121 cairo_line_to(c,xpixel,h-ypixel);
123 cairo_stroke(c);
126 yprev=ypixel;
127 xprev=xpixel;
135 static void update_legend(sushiv_panel_t *p){
136 sushiv_panel1d_t *p1 = p->subtype->p1;
137 Plot *plot = PLOT(p->private->graph);
139 gdk_threads_enter ();
141 if(plot){
142 int i,depth=0;
143 char buffer[320];
144 plot_legend_clear(plot);
146 if(-p1->vs.decimal_exponent > depth) depth = 3-p1->vs.decimal_exponent;
148 // add each dimension to the legend
149 for(i=0;i<p->dimensions;i++){
150 // display decimal precision relative to bracket
151 //int depth = del_depth(p->dimension_list[i].d->bracket[0],
152 // p->dimension_list[i].d->bracket[1]) + offset;
153 snprintf(buffer,320,"%s = %+.*f",
154 p->dimension_list[i].d->name,
155 depth,
156 p->dimension_list[i].d->val);
157 plot_legend_add(plot,buffer);
160 // linked? add the linked dimension value to the legend
161 if(p1->link_x || p1->link_y){
162 sushiv_dimension_t *d;
163 int depth=0;
164 if(p1->link_x)
165 d = p1->link_x->subtype->p2->x_d;
166 else
167 d = p1->link_y->subtype->p2->y_d;
170 // add each dimension to the legend
171 // display decimal precision relative to display scales
172 if(3-p1->vs.decimal_exponent > depth) depth = 3-p1->vs.decimal_exponent;
173 snprintf(buffer,320,"%s = %+.*f",
174 d->name,
175 depth,
176 d->val);
177 plot_legend_add(plot,buffer);
180 // one space
181 plot_legend_add(plot,NULL);
183 // add each active objective to the legend
184 // choose the value under the crosshairs
186 double val = (p1->flip?plot->sely:plot->selx);
187 int bin = scalespace_pixel(&p1->vs, val);
188 u_int32_t color = mapping_calc(p1->mappings+i,0,0);
190 for(i=0;i<p->objectives;i++){
192 snprintf(buffer,320,"%s",
193 p->objective_list[i].o->name);
195 if(bin>=0 && bin<p1->data_size){
197 float val = p1->data_vec[i][bin];
199 if(!isnan(val)){
200 snprintf(buffer,320,"%s = %f",
201 p->objective_list[i].o->name,
202 val);
206 plot_legend_add_with_color(plot,buffer,color);
210 gdk_threads_leave ();
214 void _sushiv_panel1d_map_redraw(sushiv_panel_t *p){
215 Plot *plot = PLOT(p->private->graph);
217 gdk_threads_enter (); // misuse me as a global mutex
219 _sushiv_panel1d_remap(p);
220 if(plot)
221 plot_expose_request(plot);
223 gdk_threads_leave (); // misuse me as a global mutex
226 void _sushiv_panel1d_legend_redraw(sushiv_panel_t *p){
227 Plot *plot = PLOT(p->private->graph);
229 gdk_threads_enter (); // misuse me as a global mutex
230 update_legend(p);
231 if(plot)
232 plot_draw_scales(plot);
233 gdk_threads_leave (); // misuse me as a global mutex
236 static void mapchange_callback_1d(GtkWidget *w,gpointer in){
237 sushiv_objective_list_t *optr = (sushiv_objective_list_t *)in;
238 sushiv_panel_t *p = optr->p;
239 sushiv_panel1d_t *p1 = p->subtype->p1;
240 int onum = optr - p->objective_list;
242 _sushiv_panel_undo_push(p);
243 _sushiv_panel_undo_suspend(p);
245 // update colormap
246 // oh, the wasteful
247 solid_set_func(&p1->mappings[onum],
248 gtk_combo_box_get_active(GTK_COMBO_BOX(w)));
250 _sushiv_panel_dirty_map(p);
251 _sushiv_panel_dirty_legend(p);
252 _sushiv_panel_undo_resume(p);
255 static void linetype_callback_1d(GtkWidget *w,gpointer in){
256 sushiv_objective_list_t *optr = (sushiv_objective_list_t *)in;
257 sushiv_panel_t *p = optr->p;
258 sushiv_panel1d_t *p1 = p->subtype->p1;
259 int onum = optr - p->objective_list;
261 _sushiv_panel_undo_push(p);
262 _sushiv_panel_undo_suspend(p);
264 // update colormap
265 p1->linetype[onum]=gtk_combo_box_get_active(GTK_COMBO_BOX(w));
267 _sushiv_panel_dirty_map(p);
268 _sushiv_panel_undo_resume(p);
271 static void map_callback_1d(void *in,int buttonstate){
272 sushiv_panel_t *p = (sushiv_panel_t *)in;
273 sushiv_panel1d_t *p1 = p->subtype->p1;
274 Plot *plot = PLOT(p->private->graph);
276 if(buttonstate == 0){
277 _sushiv_panel_undo_push(p);
278 _sushiv_panel_undo_suspend(p);
281 // has new bracketing changed the plot range scale?
282 if(p1->range_bracket[0] != slider_get_value(p1->range_slider,0) ||
283 p1->range_bracket[1] != slider_get_value(p1->range_slider,1)){
285 int w = plot->w.allocation.width;
286 int h = plot->w.allocation.height;
288 p1->range_bracket[0] = slider_get_value(p1->range_slider,0);
289 p1->range_bracket[1] = slider_get_value(p1->range_slider,1);
291 p1->y = scalespace_linear(p1->range_bracket[0],
292 p1->range_bracket[1],
293 (p1->flip?w:h),
294 PLOT(p->private->graph)->scalespacing,
295 p1->range_scale->legend);
298 //redraw the plot
299 _sushiv_panel_dirty_map(p);
300 if(buttonstate == 2)
301 _sushiv_panel_undo_resume(p);
304 static void update_x_sel(sushiv_panel_t *p){
305 sushiv_panel1d_t *p1 = p->subtype->p1;
306 int i;
308 // enable/disable dimension slider thumbs
310 for(i=0;i<p->dimensions;i++){
312 if(p1->dim_xb[i] &&
313 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p1->dim_xb[i]))){
315 // set the x dim flag
316 p1->x_d = p->dimension_list[i].d;
317 p1->x_scale = p->private->dim_scales[i];
318 p1->x_dnum = i;
320 // set panel x scale to this dim
321 p1->x = p1->vs = scalespace_linear(p1->x_d->bracket[0],
322 p1->x_d->bracket[1],
323 p1->data_size,
324 PLOT(p->private->graph)->scalespacing,
325 p1->x_d->name);
328 if(p1->dim_xb[i] &&
329 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p1->dim_xb[i]))){
330 // make all thumbs visible
331 _sushiv_dim_widget_set_thumb_active(p->private->dim_scales[i],0,1);
332 _sushiv_dim_widget_set_thumb_active(p->private->dim_scales[i],2,1);
333 }else{
334 // make bracket thumbs invisible */
335 _sushiv_dim_widget_set_thumb_active(p->private->dim_scales[i],0,0);
336 _sushiv_dim_widget_set_thumb_active(p->private->dim_scales[i],2,0);
341 static void compute_1d(sushiv_panel_t *p,
342 int serialno,
343 int x_d,
344 double x_min,
345 double x_max,
346 int w,
347 double *dim_vals){
348 sushiv_panel1d_t *p1 = p->subtype->p1;
349 double work[w];
350 double inv_w = 1./w;
351 int i,j;
353 /* by objective */
354 for(i=0;i<p->objectives;i++){
355 sushiv_objective_t *o = p->objective_list[i].o;
357 /* by x */
358 for(j=0;j<w;j++){
360 /* compute value for this objective for this pixel */
361 dim_vals[x_d] = (x_max-x_min) * inv_w * j + x_min;
362 o->callback(dim_vals,work+j);
366 gdk_threads_enter (); // misuse me as a global mutex
367 if(p1->serialno == serialno){
368 /* store result in panel */
369 memcpy(p1->data_vec[i],work,w*sizeof(*work));
370 gdk_threads_leave (); // misuse me as a global mutex
371 }else{
372 gdk_threads_leave (); // misuse me as a global mutex
373 break;
378 // call only from main gtk thread
379 void _mark_recompute_1d(sushiv_panel_t *p){
380 if(!p->private->realized) return;
381 sushiv_panel1d_t *p1 = p->subtype->p1;
382 Plot *plot = PLOT(p->private->graph);
383 int w = plot->w.allocation.width;
384 int h = plot->w.allocation.height;
385 int dw = w;
386 sushiv_panel_t *link = (p1->link_x ? p1->link_x : p1->link_y);
387 sushiv_panel2d_t *p2 = (link?link->subtype->p2:NULL);
388 int i,j;
390 if(p1->link_x){
391 dw = p2->data_w;
392 p1->x_d = p2->x_d;
393 p1->x_scale = p2->x_scale;
395 if(p1->link_y){
396 dw = p2->data_h;
397 p1->x_d = p2->y_d;
398 p1->x_scale = p2->y_scale;
401 if(plot && GTK_WIDGET_REALIZED(GTK_WIDGET(plot))){
402 p1->x = scalespace_linear(p1->x_d->bracket[0],
403 p1->x_d->bracket[1],
404 (p1->flip?h:w),
405 PLOT(p->private->graph)->scalespacing,
406 p1->x_d->name);
408 p1->y = scalespace_linear(p1->range_bracket[0],
409 p1->range_bracket[1],
410 (p1->flip?w:h),
411 PLOT(p->private->graph)->scalespacing,
412 p1->range_scale->legend);
415 // handle the possibility that our data scale is from a link.
416 // 2d panels do not necessarily update their scales until
417 // recompute time, and 1d panels may be recomputed first,
418 // thus duplicate the scale computaiton here
419 p1->vs = scalespace_linear(p1->x_d->bracket[0],
420 p1->x_d->bracket[1],
422 PLOT(p->private->graph)->scalespacing,
423 p1->x_d->name);
425 // the data iterator may need to be mapped to the dimension type
426 p1->vs = _sushiv_dimension_datascale(p1->x_d, p1->vs);
428 if(p1->data_size != dw){
429 if(p1->data_vec){
431 // make new vec
432 int i;
433 for(i=0;i<p->objectives;i++){
434 double *new_vec = malloc(dw * sizeof(**p1->data_vec));
436 free(p1->data_vec[i]);
437 p1->data_vec[i] = new_vec;
442 p1->data_size = dw;
443 p1->panel_w = w;
444 p1->panel_h = h;
446 if(!p1->data_vec){
447 // allocate it
449 p1->data_vec = calloc(p->objectives,sizeof(*p1->data_vec));
450 for(i=0;i<p->objectives;i++)
451 p1->data_vec[i] = malloc(dw*sizeof(**p1->data_vec));
455 // blank it
456 for(i=0;i<p->objectives;i++)
457 for(j=0;j<dw;j++)
458 p1->data_vec[i][j]=NAN;
460 p1->serialno++;
461 p1->last_line = 0;
462 _sushiv_wake_workers();
466 static void recompute_callback_1d(void *ptr){
467 sushiv_panel_t *p = (sushiv_panel_t *)ptr;
468 _mark_recompute_1d(p);
471 void _sushiv_panel1d_mark_recompute_linked(sushiv_panel_t *p){
472 int i;
474 /* look to see if any 1d panels link to passed in panel */
475 sushiv_instance_t *s = p->sushi;
476 for(i=0;i<s->panels;i++){
477 sushiv_panel_t *q = s->panel_list[i];
478 if(q != p && q->type == SUSHIV_PANEL_1D){
479 sushiv_panel1d_t *q1 = q->subtype->p1;
480 if(q1->link_x == p)
481 _mark_recompute_1d(q);
482 else{
483 if(q1->link_y == p)
484 _mark_recompute_1d(q);
490 static void update_crosshair(sushiv_panel_t *p){
491 sushiv_panel1d_t *p1 = p->subtype->p1;
492 sushiv_panel_t *link=p1->link_x;
493 Plot *plot = PLOT(p->private->graph);
494 double x=0;
495 int i;
497 if(!p->private->realized)return;
499 if(p1->link_y)link=p1->link_y;
501 if(link){
502 for(i=0;i<link->dimensions;i++){
503 sushiv_dimension_t *d = link->dimension_list[i].d;
504 if(d == p1->x_d)
505 x = link->dimension_list[i].d->val;
507 }else{
508 for(i=0;i<p->dimensions;i++){
509 sushiv_dimension_t *d = p->dimension_list[i].d;
510 if(d == p1->x_d)
511 x = p->dimension_list[i].d->val;
515 if(p1->flip)
516 plot_set_crosshairs(plot,0,x);
517 else
518 plot_set_crosshairs(plot,x,0);
520 // in independent panels, crosshairs snap to a pixel position; the
521 // cached dimension value should be accurate with respect to the
522 // crosshairs. in linked panels, the crosshairs snap to a pixel
523 // position in the master panel; that is handled in the master, not
524 // here.
525 for(i=0;i<p->dimensions;i++){
526 sushiv_dimension_t *d = p->dimension_list[i].d;
527 sushiv_panel1d_t *p1 = p->subtype->p1;
528 if(d == p1->x_d){
529 if(p1->flip)
530 d->val = scalespace_value(&plot->x,plot_get_crosshair_ypixel(plot));
531 else
532 d->val = scalespace_value(&plot->x,plot_get_crosshair_xpixel(plot));
535 _sushiv_panel_dirty_legend(p);
538 void _sushiv_panel1d_update_linked_crosshairs(sushiv_panel_t *p, int xflag, int yflag){
539 int i;
541 /* look to see if any 1d panels link to passed in panel */
542 sushiv_instance_t *s = p->sushi;
543 for(i=0;i<s->panels;i++){
544 sushiv_panel_t *q = s->panel_list[i];
545 if(q != p && q->type == SUSHIV_PANEL_1D){
546 sushiv_panel1d_t *q1 = q->subtype->p1;
547 if(q1->link_x == p){
548 update_crosshair(q);
549 if(yflag)
550 q->private->request_compute(q);
551 }else{
552 if(q1->link_y == p){
553 update_crosshair(q);
554 if(xflag)
555 q->private->request_compute(q);
562 static void center_callback_1d(sushiv_dimension_list_t *dptr){
563 sushiv_dimension_t *d = dptr->d;
564 sushiv_panel_t *p = dptr->p;
565 sushiv_panel1d_t *p1 = p->subtype->p1;
566 int axisp = (d == p1->x_d);
568 if(!axisp){
569 // mid slider of a non-axis dimension changed, rerender
570 _mark_recompute_1d(p);
571 }else{
572 // mid slider of an axis dimension changed, move crosshairs
573 update_crosshair(p);
577 static void bracket_callback_1d(sushiv_dimension_list_t *dptr){
578 sushiv_dimension_t *d = dptr->d;
579 sushiv_panel_t *p = dptr->p;
580 sushiv_panel1d_t *p1 = p->subtype->p1;
581 int axisp = d == p1->x_d;
583 if(axisp)
584 _mark_recompute_1d(p);
588 static void dimchange_callback_1d(GtkWidget *button,gpointer in){
589 sushiv_panel_t *p = (sushiv_panel_t *)in;
591 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))){
593 _sushiv_panel_undo_push(p);
594 _sushiv_panel_undo_suspend(p);
596 update_x_sel(p);
597 update_crosshair(p);
598 plot_unset_box(PLOT(p->private->graph));
599 _mark_recompute_1d(p);
601 _sushiv_panel_undo_resume(p);
605 static void crosshair_callback(sushiv_panel_t *p){
606 sushiv_panel1d_t *p1 = p->subtype->p1;
607 sushiv_panel_t *link = p1->link_x;
608 double x=PLOT(p->private->graph)->selx;
609 int i;
611 if(p1->flip)
612 x=PLOT(p->private->graph)->sely;
613 if(p1->link_y)
614 link=p1->link_y;
616 if(p1->link_x){
617 // make it the master panel's problem.
618 plot_set_crosshairs_snap(PLOT(link->private->graph),
620 PLOT(link->private->graph)->sely);
621 link->private->crosshair_action(link);
622 }else if (p1->link_y){
623 // make it the master panel's problem.
624 plot_set_crosshairs_snap(PLOT(link->private->graph),
625 PLOT(link->private->graph)->selx,
627 link->private->crosshair_action(link);
628 }else{
630 _sushiv_panel_undo_push(p);
631 _sushiv_panel_undo_suspend(p);
633 for(i=0;i<p->dimensions;i++){
634 sushiv_dimension_t *d = p->dimension_list[i].d;
635 sushiv_panel1d_t *p1 = p->subtype->p1;
636 if(d == p1->x_d)
637 _sushiv_dimension_set_value(p->private->dim_scales[i],1,x);
639 p1->oldbox_active = 0;
641 _sushiv_panel_undo_resume(p);
645 static void box_callback(void *in, int state){
646 sushiv_panel_t *p = (sushiv_panel_t *)in;
647 sushiv_panel1d_t *p1 = p->subtype->p1;
648 Plot *plot = PLOT(p->private->graph);
650 switch(state){
651 case 0: // box set
652 _sushiv_panel_undo_push(p);
653 plot_box_vals(plot,p1->oldbox);
654 p1->oldbox_active = plot->box_active;
655 break;
656 case 1: // box activate
657 _sushiv_panel_undo_push(p);
658 _sushiv_panel_undo_suspend(p);
660 crosshair_callback(p);
662 _sushiv_dimension_set_value(p1->x_scale,0,p1->oldbox[0]);
663 _sushiv_dimension_set_value(p1->x_scale,2,p1->oldbox[1]);
664 p1->oldbox_active = 0;
665 _sushiv_panel_undo_resume(p);
666 break;
668 update_context_menus(p);
671 int _sushiv_panel_cooperative_compute_1d(sushiv_panel_t *p){
672 sushiv_panel1d_t *p1 = p->subtype->p1;
673 Plot *plot;
675 int dw,w,h,i,d;
676 int serialno;
677 double x_min, x_max;
678 int x_d=-1;
679 int render_scale_flag = 0;
680 scalespace sx;
681 scalespace sy;
682 scalespace sv;
684 // lock during setup
685 gdk_threads_enter ();
686 dw = p1->data_size;
687 w = p1->panel_w;
688 h = p1->panel_h;
690 sx = p1->x;
691 sy = p1->y;
692 sv = p1->vs;
694 if(p1->last_line){
695 gdk_threads_leave ();
696 return 0;
699 plot = PLOT(p->private->graph);
701 serialno = p1->serialno;
702 d = p->dimensions;
704 /* render using local dimension array; several threads will be
705 computing objectives */
706 double dim_vals[p->sushi->dimensions];
708 x_min = scalespace_value(&sv,0);
709 x_max = scalespace_value(&sv,dw);
710 x_d = p1->x_d->number;
712 if(p1->flip){
713 plot->x = sy;
714 plot->y = sx;
715 }else{
716 plot->x = sx;
717 plot->y = sy;
720 // Bulletproofing; shouldn't ever come up
721 if(x_d==-1){
722 gdk_threads_leave ();
723 fprintf(stderr,"Invalid/missing x dimension setting in 1d panel x_d\n");
724 return 0;
727 // Initialize local dimension value array
728 for(i=0;i<p->sushi->dimensions;i++){
729 sushiv_dimension_t *dim = p->sushi->dimension_list[i];
730 dim_vals[i]=dim->val;
733 // update scales if we're just starting
734 if(p1->last_line==0) render_scale_flag = 1;
736 if(plot->w.allocation.height == h &&
737 serialno == p1->serialno){
738 p1->last_line++;
740 /* unlock for computation */
741 gdk_threads_leave ();
743 if(render_scale_flag){
744 plot_draw_scales(plot);
745 render_scale_flag = 0;
748 /* compute */
749 compute_1d(p, serialno, x_d, x_min, x_max, dw, dim_vals);
750 gdk_threads_enter ();
751 _sushiv_panel_dirty_map(p);
752 _sushiv_panel_dirty_legend(p);
753 gdk_threads_leave ();
755 }else
756 gdk_threads_leave ();
758 return 1;
761 static void panel1d_undo_log(sushiv_panel_undo_t *u, sushiv_panel_t *p){
762 sushiv_panel1d_t *p1 = p->subtype->p1;
763 int i;
765 // alloc fields as necessary
766 if(!u->mappings)
767 u->mappings = calloc(p->objectives,sizeof(*u->mappings));
768 if(!u->submappings)
769 u->submappings = calloc(p->objectives,sizeof(*u->submappings));
770 if(!u->scale_vals[0])
771 u->scale_vals[0] = calloc(1,sizeof(**u->scale_vals));
772 if(!u->scale_vals[1])
773 u->scale_vals[1] = calloc(1,sizeof(**u->scale_vals));
774 if(!u->scale_vals[2])
775 u->scale_vals[2] = calloc(1,sizeof(**u->scale_vals));
776 if(!u->dim_vals[0])
777 u->dim_vals[0] = calloc(p->dimensions+1,sizeof(**u->dim_vals)); // +1 for possible linked dim
778 if(!u->dim_vals[1])
779 u->dim_vals[1] = calloc(p->dimensions+1,sizeof(**u->dim_vals)); // +1 for possible linked dim
780 if(!u->dim_vals[2])
781 u->dim_vals[2] = calloc(p->dimensions+1,sizeof(**u->dim_vals)); // +1 for possible linked dim
783 // populate undo
784 u->scale_vals[0][0] = slider_get_value(p1->range_slider,0);
785 u->scale_vals[1][0] = slider_get_value(p1->range_slider,1);
787 for(i=0;i<p->objectives;i++){
788 u->mappings[i] = p1->mappings[i].mapnum;
789 u->submappings[i] = p1->linetype[i];
792 for(i=0;i<p->dimensions;i++){
793 u->dim_vals[0][i] = p->dimension_list[i].d->bracket[0];
794 u->dim_vals[1][i] = p->dimension_list[i].d->val;
795 u->dim_vals[2][i] = p->dimension_list[i].d->bracket[1];
798 u->dim_vals[0][i] = p1->x_d->bracket[0];
799 u->dim_vals[1][i] = p1->x_d->val;
800 u->dim_vals[2][i] = p1->x_d->bracket[1];
802 u->x_d = p1->x_dnum;
803 u->box[0] = p1->oldbox[0];
804 u->box[1] = p1->oldbox[1];
805 u->box_active = p1->oldbox_active;
809 static void panel1d_undo_restore(sushiv_panel_undo_t *u, sushiv_panel_t *p){
810 sushiv_panel1d_t *p1 = p->subtype->p1;
811 Plot *plot = PLOT(p->private->graph);
812 sushiv_panel_t *link = (p1->link_x?p1->link_x:p1->link_y);
814 int i;
816 // go in through widgets
818 slider_set_value(p1->range_slider,0,u->scale_vals[0][0]);
819 slider_set_value(p1->range_slider,1,u->scale_vals[1][0]);
821 for(i=0;i<p->objectives;i++){
822 gtk_combo_box_set_active(GTK_COMBO_BOX(p1->map_pulldowns[i]),u->mappings[i]);
823 gtk_combo_box_set_active(GTK_COMBO_BOX(p1->line_pulldowns[i]),u->submappings[i]);
826 for(i=0;i<p->dimensions;i++){
827 _sushiv_dimension_set_value(p->private->dim_scales[i],0,u->dim_vals[0][i]);
828 _sushiv_dimension_set_value(p->private->dim_scales[i],1,u->dim_vals[1][i]);
829 _sushiv_dimension_set_value(p->private->dim_scales[i],2,u->dim_vals[2][i]);
832 if(p1->dim_xb && u->x_d<p->dimensions && p1->dim_xb[u->x_d])
833 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p1->dim_xb[u->x_d]),TRUE);
835 /* make sure x_d is set before updating the x_d sliders! */
836 update_x_sel(p);
838 if(link){
839 /* doesn't matter which widget belonging to the dimension is the one that gets set */
840 _sushiv_dimension_set_value(p1->x_d->private->widget_list[0],0,u->dim_vals[0][p->dimensions]);
841 _sushiv_dimension_set_value(p1->x_d->private->widget_list[0],1,u->dim_vals[1][p->dimensions]);
842 _sushiv_dimension_set_value(p1->x_d->private->widget_list[0],2,u->dim_vals[2][p->dimensions]);
845 if(u->box_active){
846 plot_box_set(plot,u->box);
847 p1->oldbox_active = 1;
848 }else{
849 plot_unset_box(plot);
850 p1->oldbox_active = 0;
854 // called with lock
855 static void panel1d_find_peak(sushiv_panel_t *p){
856 sushiv_panel1d_t *p1 = p->subtype->p1;
857 Plot *plot = PLOT(p->private->graph);
858 int i,j;
859 int dw = p1->data_size;
860 int count = 0;
862 // finds in order each peak (in the event there's more than one) of
863 // each active objective
864 while(1){
866 for(i=0;i<p->objectives;i++){
867 if(p1->data_vec && p1->data_vec[i] && !mapping_inactive_p(p1->mappings+i)){
868 double *data=p1->data_vec[i];
869 double best_val = data[0];
870 double best_j = 0;
871 int inner_count = count+1;
873 for(j=1;j<dw;j++){
874 if(!isnan(data[j])){
875 if(data[j]>best_val){
876 inner_count = count+1;
877 best_val = data[j];
878 best_j = j;
879 }else if (data[j]==best_val){
880 if(inner_count <= p1->peak_count){
881 inner_count++;
882 best_val = data[j];
883 best_j = j;
889 count = inner_count;
890 if(count>p1->peak_count){
891 double xv = scalespace_value(&p1->vs,best_j);
893 if(p1->flip)
894 plot_set_crosshairs(plot,0,xv);
895 else
896 plot_set_crosshairs(plot,xv,0);
897 crosshair_callback(p);
899 p1->peak_count++;
901 return;
906 if(p1->peak_count==0)
907 return; // must be all inactive
908 else
909 p1->peak_count=0;
913 static gboolean panel1d_keypress(GtkWidget *widget,
914 GdkEventKey *event,
915 gpointer in){
916 sushiv_panel_t *p = (sushiv_panel_t *)in;
918 if(event->state&GDK_MOD1_MASK) return FALSE;
919 if(event->state&GDK_CONTROL_MASK)return FALSE;
921 /* non-control keypresses */
922 switch(event->keyval){
924 case GDK_Q:
925 case GDK_q:
926 // quit
927 _sushiv_clean_exit(SIGINT);
928 return TRUE;
930 case GDK_BackSpace:
931 // undo
932 _sushiv_panel_undo_down(p);
933 return TRUE;
935 case GDK_r:
936 case GDK_space:
937 // redo/forward
938 _sushiv_panel_undo_up(p);
939 return TRUE;
941 case GDK_p:
942 // find [next] peak
943 panel1d_find_peak(p);
944 return TRUE;
947 return FALSE;
950 static void update_context_menus(sushiv_panel_t *p){
951 sushiv_panel1d_t *p1 = p->subtype->p1;
953 // is undo active?
954 if(!p->sushi->private->undo_stack ||
955 !p->sushi->private->undo_level){
956 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->popmenu),0),FALSE);
957 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->graphmenu),0),FALSE);
958 }else{
959 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->popmenu),0),TRUE);
960 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->graphmenu),0),TRUE);
963 // is redo active?
964 if(!p->sushi->private->undo_stack ||
965 !p->sushi->private->undo_stack[p->sushi->private->undo_level] ||
966 !p->sushi->private->undo_stack[p->sushi->private->undo_level+1]){
967 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->popmenu),1),FALSE);
968 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->graphmenu),1),FALSE);
969 }else{
970 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->popmenu),1),TRUE);
971 gtk_widget_set_sensitive(gtk_menu_get_item(GTK_MENU(p1->graphmenu),1),TRUE);
974 // are we starting or enacting a zoom box?
975 if(p1->oldbox_active){
976 gtk_menu_alter_item_label(GTK_MENU(p1->graphmenu),3,"Zoom to selection");
977 }else{
978 gtk_menu_alter_item_label(GTK_MENU(p1->graphmenu),3,"Start zoom selection");
983 void wrap_exit(sushiv_panel_t *dummy){
984 _sushiv_clean_exit(SIGINT);
987 static char *panel_menulist[]={
988 "Undo",
989 "Redo",
991 "Quit",
992 NULL
995 static char *panel_shortlist[]={
996 "Backspace",
997 "Space",
998 NULL,
999 "q",
1000 NULL
1003 static void (*panel_calllist[])(sushiv_panel_t *)={
1004 &_sushiv_panel_undo_down,
1005 &_sushiv_panel_undo_up,
1006 NULL,
1007 &wrap_exit,
1008 NULL,
1011 void wrap_enter(sushiv_panel_t *p){
1012 plot_do_enter(PLOT(p->private->graph));
1015 void wrap_escape(sushiv_panel_t *p){
1016 plot_do_escape(PLOT(p->private->graph));
1019 static char *graph_menulist[]={
1020 "Undo",
1021 "Redo",
1023 "Start zoom selection",
1024 "Clear selection",
1025 "Find peaks",
1027 "Quit",
1028 NULL
1031 static char *graph_shortlist[]={
1032 "Backspace",
1033 "Space",
1034 NULL,
1035 "Enter",
1036 "Escape",
1037 "p",
1038 NULL,
1039 "q",
1040 NULL
1043 static void (*graph_calllist[])(sushiv_panel_t *)={
1044 &_sushiv_panel_undo_down,
1045 &_sushiv_panel_undo_up,
1046 NULL,
1048 &wrap_enter,
1049 &wrap_escape,
1050 &panel1d_find_peak,
1051 NULL,
1052 &wrap_exit,
1053 NULL,
1056 void _sushiv_realize_panel1d(sushiv_panel_t *p){
1057 sushiv_panel1d_t *p1 = p->subtype->p1;
1058 int i;
1060 _sushiv_panel_undo_suspend(p);
1062 p->private->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1063 g_signal_connect_swapped (G_OBJECT (p->private->toplevel), "delete-event",
1064 G_CALLBACK (_sushiv_clean_exit), (void *)SIGINT);
1066 p1->top_table = gtk_table_new(3,4,0);
1068 gtk_container_add (GTK_CONTAINER (p->private->toplevel), p1->top_table);
1069 gtk_container_set_border_width (GTK_CONTAINER (p->private->toplevel), 5);
1071 p1->obj_table = gtk_table_new(p->objectives,3,0);
1072 gtk_table_attach(GTK_TABLE(p1->top_table),p1->obj_table,0,4,2,3,
1073 GTK_EXPAND|GTK_FILL,0,0,5);
1075 p1->dim_table = gtk_table_new(p->dimensions,3,0);
1076 gtk_table_attach(GTK_TABLE(p1->top_table),p1->dim_table,0,4,3,4,
1077 GTK_EXPAND|GTK_FILL,0,0,5);
1079 /* graph */
1081 unsigned flags = 0;
1082 if(p1->flip)
1083 flags |= PLOT_NO_X_CROSS;
1084 else
1085 flags |= PLOT_NO_Y_CROSS;
1086 p->private->graph = GTK_WIDGET(plot_new(recompute_callback_1d,p,
1087 (void *)(void *)crosshair_callback,p,
1088 box_callback,p,flags));
1089 gtk_table_attach(GTK_TABLE(p1->top_table),p->private->graph,0,4,0,1,
1090 GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,5);
1093 /* range slider */
1095 GtkWidget **sl = calloc(2,sizeof(*sl));
1097 int lo = p1->range_scale->val_list[0];
1098 int hi = p1->range_scale->val_list[p1->range_scale->vals-1];
1100 /* label */
1102 char *buf;
1103 asprintf(&buf,"%s range",p1->range_scale->legend);
1104 GtkWidget *label = gtk_label_new(buf);
1105 gtk_table_attach(GTK_TABLE(p1->top_table),label,0,1,1,2,
1106 0,0,10,0);
1107 free(buf);
1110 /* the range slices/slider */
1111 sl[0] = slice_new(map_callback_1d,p);
1112 sl[1] = slice_new(map_callback_1d,p);
1114 gtk_table_attach(GTK_TABLE(p1->top_table),sl[0],1,2,1,2,
1115 GTK_EXPAND|GTK_FILL,0,0,0);
1116 gtk_table_attach(GTK_TABLE(p1->top_table),sl[1],2,3,1,2,
1117 GTK_EXPAND|GTK_FILL,0,0,0);
1118 p1->range_slider = slider_new((Slice **)sl,2,
1119 p1->range_scale->label_list,
1120 p1->range_scale->val_list,
1121 p1->range_scale->vals,
1122 SLIDER_FLAG_INDEPENDENT_MIDDLE);
1124 slice_thumb_set((Slice *)sl[0],lo);
1125 slice_thumb_set((Slice *)sl[1],hi);
1128 /* objective pulldowns */
1129 p1->linetype = calloc(p->objectives,sizeof(*p1->linetype));
1130 p1->mappings = calloc(p->objectives,sizeof(*p1->mappings));
1131 p1->map_pulldowns = calloc(p->objectives,sizeof(*p1->map_pulldowns));
1132 p1->line_pulldowns = calloc(p->objectives,sizeof(*p1->line_pulldowns));
1134 for(i=0;i<p->objectives;i++){
1135 sushiv_objective_t *o = p->objective_list[i].o;
1137 /* label */
1138 GtkWidget *label = gtk_label_new(o->name);
1139 gtk_table_attach(GTK_TABLE(p1->obj_table),label,0,1,i,i+1,
1140 0,0,10,0);
1142 /* mapping pulldown */
1144 GtkWidget *menu=gtk_combo_box_new_markup();
1145 int j;
1146 for(j=0;j<num_solids();j++)
1147 gtk_combo_box_append_text (GTK_COMBO_BOX (menu), solid_name(j));
1148 gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
1149 g_signal_connect (G_OBJECT (menu), "changed",
1150 G_CALLBACK (mapchange_callback_1d), p->objective_list+i);
1151 gtk_table_attach(GTK_TABLE(p1->obj_table),menu,1,2,i,i+1,
1152 GTK_SHRINK,GTK_SHRINK,5,0);
1153 p1->map_pulldowns[i] = menu;
1154 solid_setup(&p1->mappings[i],0.,1.,0);
1157 /* line pulldown */
1159 GtkWidget *menu=gtk_combo_box_new_text();
1160 int j;
1161 for(j=0;j<LINETYPES;j++)
1162 gtk_combo_box_append_text (GTK_COMBO_BOX (menu), line_name[j]);
1163 gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
1164 g_signal_connect (G_OBJECT (menu), "changed",
1165 G_CALLBACK (linetype_callback_1d), p->objective_list+i);
1166 gtk_table_attach(GTK_TABLE(p1->obj_table),menu,2,3,i,i+1,
1167 GTK_SHRINK,GTK_SHRINK,5,0);
1168 p1->line_pulldowns[i] = menu;
1172 if(p->dimensions){
1173 p->private->dim_scales = calloc(p->dimensions,sizeof(*p->private->dim_scales));
1174 p1->dim_xb = calloc(p->dimensions,sizeof(*p1->dim_xb));
1175 GtkWidget *first_x = NULL;
1177 for(i=0;i<p->dimensions;i++){
1178 sushiv_dimension_t *d = p->dimension_list[i].d;
1180 /* label */
1181 GtkWidget *label = gtk_label_new(d->name);
1182 gtk_table_attach(GTK_TABLE(p1->dim_table),label,0,1,i,i+1,
1183 0,0,10,0);
1185 /* x radio buttons */
1186 if(!(d->flags & SUSHIV_DIM_NO_X) && !p1->link_x && !p1->link_y){
1187 if(first_x)
1188 p1->dim_xb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_x),"X");
1189 else{
1190 first_x = p1->dim_xb[i] = gtk_radio_button_new_with_label(NULL,"X");
1191 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p1->dim_xb[i]),TRUE);
1193 gtk_table_attach(GTK_TABLE(p1->dim_table),p1->dim_xb[i],1,2,i,i+1,
1194 0,0,10,0);
1197 p->private->dim_scales[i] =
1198 _sushiv_new_dimension_widget(p->dimension_list+i,center_callback_1d,bracket_callback_1d);
1200 gtk_table_attach(GTK_TABLE(p1->dim_table),
1201 GTK_WIDGET(p->private->dim_scales[i]->t),
1202 2,3,i,i+1,
1203 GTK_EXPAND|GTK_FILL,0,0,0);
1207 for(i=0;i<p->dimensions;i++)
1208 if(p1->dim_xb[i])
1209 g_signal_connect (G_OBJECT (p1->dim_xb[i]), "toggled",
1210 G_CALLBACK (dimchange_callback_1d), p);
1212 update_x_sel(p);
1215 p1->popmenu = gtk_menu_new_twocol(p->private->toplevel,
1216 panel_menulist,
1217 panel_shortlist,
1218 (void *)(void *)panel_calllist,
1220 p1->graphmenu = gtk_menu_new_twocol(p->private->graph,
1221 graph_menulist,
1222 graph_shortlist,
1223 (void *)(void *)graph_calllist,
1226 update_context_menus(p);
1228 g_signal_connect (G_OBJECT (p->private->toplevel), "key-press-event",
1229 G_CALLBACK (panel1d_keypress), p);
1230 gtk_window_set_title (GTK_WINDOW (p->private->toplevel), p->name);
1232 gtk_widget_realize(p->private->toplevel);
1233 gtk_widget_realize(p->private->graph);
1234 gtk_widget_show_all(p->private->toplevel);
1236 _sushiv_panel_undo_resume(p);
1239 int sushiv_new_panel_1d_linked(sushiv_instance_t *s,
1240 int number,
1241 const char *name,
1242 sushiv_scale_t *scale,
1243 int *objectives,
1244 int link,
1245 unsigned flags){
1247 if(link < 0 ||
1248 link >= s->panels ||
1249 s->panel_list[link]==NULL ||
1250 s->panel_list[link]->type != SUSHIV_PANEL_2D){
1251 fprintf(stderr,"1d linked panel must be linked to preexisting 2d panel.\n");
1252 return -EINVAL;
1255 int ret = _sushiv_new_panel(s,number,name,objectives,(int []){-1},flags);
1256 sushiv_panel_t *p;
1257 sushiv_panel1d_t *p1;
1258 sushiv_panel_t *p2 = s->panel_list[link];
1260 if(ret<0)return ret;
1261 p = s->panel_list[number];
1262 p1 = calloc(1, sizeof(*p1));
1263 p->subtype =
1264 calloc(1, sizeof(*p->subtype)); /* the union is alloced not
1265 embedded as its internal
1266 structure must be hidden */
1267 p->subtype->p1 = p1;
1268 p->type = SUSHIV_PANEL_1D;
1270 p1->range_scale = scale;
1272 if(flags && SUSHIV_PANEL_LINK_Y)
1273 p1->link_y = p2;
1274 else
1275 p1->link_x = p2;
1277 if(p->flags && SUSHIV_PANEL_FLIP)
1278 p1->flip=1;
1280 p->private->realize = _sushiv_realize_panel1d;
1281 p->private->map_redraw = _sushiv_panel1d_map_redraw;
1282 p->private->legend_redraw = _sushiv_panel1d_legend_redraw;
1283 p->private->compute_action = _sushiv_panel_cooperative_compute_1d;
1284 p->private->request_compute = _mark_recompute_1d;
1285 p->private->crosshair_action = crosshair_callback;
1287 p->private->undo_log = panel1d_undo_log;
1288 p->private->undo_restore = panel1d_undo_restore;
1289 p->private->update_menus = update_context_menus;
1291 return 0;