It appears Solaris's cc is ignoring the signedness of bitfield types.
[xiph/unicode.git] / sushivision / panel-xy.c
blobfd13de67da1b9d2c74d8dfaecab567e71446c27f
1 /*
3 * sushivision copyright (C) 2006-2007 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 "internal.h"
35 #define LINETYPES 3
36 static _sv_propmap_t *line_name[LINETYPES+1] = {
37 &(_sv_propmap_t){"line", 0, NULL,NULL,NULL},
38 &(_sv_propmap_t){"fat line", 1, NULL,NULL,NULL},
39 &(_sv_propmap_t){"no line", 5, NULL,NULL,NULL},
40 NULL
43 #define POINTTYPES 9
44 static _sv_propmap_t *point_name[POINTTYPES+1] = {
45 &(_sv_propmap_t){"dot", 0, NULL,NULL,NULL},
46 &(_sv_propmap_t){"cross", 1, NULL,NULL,NULL},
47 &(_sv_propmap_t){"plus", 2, NULL,NULL,NULL},
48 &(_sv_propmap_t){"open circle", 3, NULL,NULL,NULL},
49 &(_sv_propmap_t){"open square", 4, NULL,NULL,NULL},
50 &(_sv_propmap_t){"open triangle", 5, NULL,NULL,NULL},
51 &(_sv_propmap_t){"solid circle", 6, NULL,NULL,NULL},
52 &(_sv_propmap_t){"solid square", 7, NULL,NULL,NULL},
53 &(_sv_propmap_t){"solid triangle", 8, NULL,NULL,NULL},
54 NULL
57 static void _sv_panelxy_clear_data(sv_panel_t *p){
58 _sv_panelxy_t *xy = p->subtype->xy;
59 int i;
61 if(xy->x_vec){
62 for(i=0;i<p->objectives;i++){
63 if(xy->x_vec[i]){
64 free(xy->x_vec[i]);
65 xy->x_vec[i]=0;
70 if(xy->y_vec){
71 for(i=0;i<p->objectives;i++){
72 if(xy->y_vec[i]){
73 free(xy->y_vec[i]);
74 xy->y_vec[i]=0;
80 static void render_checks(cairo_t *c, int w, int h){
81 /* default checked background */
82 /* 16x16 'mid-checks' */
83 int x,y;
85 cairo_set_source_rgb (c, .5,.5,.5);
86 cairo_paint(c);
87 cairo_set_source_rgb (c, .314,.314,.314);
89 for(y=0;y<h;y+=16){
90 int phase = (y>>4)&1;
91 for(x=0;x<w;x+=16){
92 if(phase){
93 cairo_rectangle(c,x,y,16.,16.);
94 cairo_fill(c);
96 phase=!phase;
101 // called internally, assumes we hold lock
102 // redraws the data, does not compute the data
103 static int _sv_panelxy_remap(sv_panel_t *p, cairo_t *c){
104 _sv_panelxy_t *xy = p->subtype->xy;
105 _sv_plot_t *plot = PLOT(p->private->graph);
107 int plot_serialno = p->private->plot_serialno;
108 int map_serialno = p->private->map_serialno;
109 int xi,i,j;
110 int pw = plot->x.pixels;
111 int ph = plot->y.pixels;
112 int ret = 1;
114 _sv_scalespace_t sx = xy->x;
115 _sv_scalespace_t sy = xy->y;
116 _sv_scalespace_t data_v = xy->data_v;
117 _sv_scalespace_t px = plot->x;
118 _sv_scalespace_t py = plot->y;
120 /* do the panel and plot scales match? If not, redraw the plot
121 scales */
123 if(memcmp(&sx,&px,sizeof(sx)) ||
124 memcmp(&sy,&py,sizeof(sy))){
126 plot->x = sx;
127 plot->y = sy;
129 gdk_threads_leave();
130 _sv_plot_draw_scales(plot);
131 }else
132 gdk_threads_leave();
134 /* blank frame to selected bg */
135 switch(p->private->bg_type){
136 case SV_BG_WHITE:
137 cairo_set_source_rgb (c, 1.,1.,1.);
138 cairo_paint(c);
139 break;
140 case SV_BG_BLACK:
141 cairo_set_source_rgb (c, 0,0,0);
142 cairo_paint(c);
143 break;
144 case SV_BG_CHECKS:
145 render_checks(c,pw,ph);
146 break;
149 gdk_threads_enter();
150 if(plot_serialno != p->private->plot_serialno ||
151 map_serialno != p->private->map_serialno) return -1;
153 if(xy->x_vec && xy->y_vec){
154 int dw = data_v.pixels;
155 double *xv = calloc(dw,sizeof(*xv));
156 double *yv = calloc(dw,sizeof(*yv));
158 /* by objective */
159 for(j=0;j<p->objectives;j++){
160 if(xy->x_vec[j] && xy->y_vec[j] && !_sv_mapping_inactive_p(xy->mappings+j)){
162 double alpha = _sv_slider_get_value(xy->alpha_scale[j],0);
163 int linetype = xy->linetype[j];
164 int pointtype = xy->pointtype[j];
165 u_int32_t color = _sv_mapping_calc(xy->mappings+j,1.,0);
167 // copy the list data over
168 memcpy(xv,xy->x_vec[j],dw*sizeof(*xv));
169 memcpy(yv,xy->y_vec[j],dw*sizeof(*yv));
170 gdk_threads_leave();
172 /* by x */
173 for(xi=0;xi<dw;xi++){
174 double xpixel = xv[xi];
175 double ypixel = yv[xi];
177 /* map data vector bin to x pixel location in the plot */
178 if(!isnan(xpixel))
179 xpixel = _sv_scalespace_pixel(&sx,xpixel)+.5;
181 if(!isnan(ypixel))
182 ypixel = _sv_scalespace_pixel(&sy,ypixel)+.5;
184 xv[xi] = xpixel;
185 yv[xi] = ypixel;
188 /* draw lines, if any */
189 if(linetype != 5){
190 cairo_set_source_rgba(c,
191 ((color>>16)&0xff)/255.,
192 ((color>>8)&0xff)/255.,
193 ((color)&0xff)/255.,
194 alpha);
195 if(linetype == 1)
196 cairo_set_line_width(c,2.);
197 else
198 cairo_set_line_width(c,1.);
200 for(i=1;i<dw;i++){
202 if(!isnan(yv[i-1]) && !isnan(yv[i]) &&
203 !isnan(xv[i-1]) && !isnan(xv[i]) &&
204 !(xv[i-1] < 0 && xv[i] < 0) &&
205 !(yv[i-1] < 0 && yv[i] < 0) &&
206 !(xv[i-1] > pw && xv[i] > pw) &&
207 !(yv[i-1] > ph && yv[i] > ph)){
209 cairo_move_to(c,xv[i-1],yv[i-1]);
210 cairo_line_to(c,xv[i],yv[i]);
211 cairo_stroke(c);
216 /* now draw the points */
217 if(pointtype > 0 || linetype == 5){
218 cairo_set_line_width(c,1.);
220 for(i=0;i<dw;i++){
221 if(!isnan(yv[i]) &&
222 !isnan(xv[i]) &&
223 !(xv[i]<-10) &&
224 !(yv[i]<-10) &&
225 !(xv[i]-10>pw) &&
226 !(yv[i]-10>ph)){
228 double xx,yy;
229 xx = xv[i];
230 yy = yv[i];
232 cairo_set_source_rgba(c,
233 ((color>>16)&0xff)/255.,
234 ((color>>8)&0xff)/255.,
235 ((color)&0xff)/255.,
236 alpha);
238 switch(pointtype){
239 case 0: /* pixeldots */
240 cairo_rectangle(c, xx-.5,yy-.5,1,1);
241 cairo_fill(c);
242 break;
243 case 1: /* X */
244 cairo_move_to(c,xx-4,yy-4);
245 cairo_line_to(c,xx+4,yy+4);
246 cairo_move_to(c,xx+4,yy-4);
247 cairo_line_to(c,xx-4,yy+4);
248 break;
249 case 2: /* + */
250 cairo_move_to(c,xx-4,yy);
251 cairo_line_to(c,xx+4,yy);
252 cairo_move_to(c,xx,yy-4);
253 cairo_line_to(c,xx,yy+4);
254 break;
255 case 3: case 6: /* circle */
256 cairo_arc(c,xx,yy,4,0,2.*M_PI);
257 break;
258 case 4: case 7: /* square */
259 cairo_rectangle(c,xx-4,yy-4,8,8);
260 break;
261 case 5: case 8: /* triangle */
262 cairo_move_to(c,xx,yy-5);
263 cairo_line_to(c,xx-4,yy+3);
264 cairo_line_to(c,xx+4,yy+3);
265 cairo_close_path(c);
266 break;
269 if(pointtype>5){
270 cairo_fill_preserve(c);
273 if(pointtype>0){
274 if(p->private->bg_type == SV_BG_WHITE)
275 cairo_set_source_rgba(c,0.,0.,0.,alpha);
276 else
277 cairo_set_source_rgba(c,1.,1.,1.,alpha);
278 cairo_stroke(c);
284 gdk_threads_enter();
285 if(plot_serialno != p->private->plot_serialno ||
286 map_serialno != p->private->map_serialno){
287 ret = -1;
288 break;
293 free(xv);
294 free(yv);
297 return ret;
300 static void _sv_panelxy_print(sv_panel_t *p, cairo_t *c, int w, int h){
301 _sv_plot_t *plot = PLOT(p->private->graph);
302 double pw = p->private->graph->allocation.width;
303 double ph = p->private->graph->allocation.height;
304 double scale;
306 if(w/pw < h/ph)
307 scale = w/pw;
308 else
309 scale = h/ph;
311 cairo_matrix_t m;
312 cairo_get_matrix(c,&m);
313 cairo_matrix_scale(&m,scale,scale);
314 cairo_set_matrix(c,&m);
316 _sv_plot_print(plot, c, ph*scale, (void(*)(void *, cairo_t *))_sv_panelxy_remap, p);
319 static void _sv_panelxy_update_legend(sv_panel_t *p){
320 _sv_panelxy_t *xy = p->subtype->xy;
321 _sv_plot_t *plot = PLOT(p->private->graph);
323 gdk_threads_enter ();
325 if(plot){
326 int i,depth=0;
327 char buffer[320];
328 _sv_plot_legend_clear(plot);
330 if(3-_sv_scalespace_decimal_exponent(&xy->x) > depth)
331 depth = 3-_sv_scalespace_decimal_exponent(&xy->x);
332 if(3-_sv_scalespace_decimal_exponent(&xy->y) > depth)
333 depth = 3-_sv_scalespace_decimal_exponent(&xy->y);
335 // if crosshairs are active, add them to the fun
336 if( plot->cross_active){
337 char *legend = xy->x_scale->legend;
338 if(!strcmp(legend,""))legend = "X";
339 snprintf(buffer,320,"%s = %+.*f",
340 legend,
341 depth,
342 plot->selx);
343 _sv_plot_legend_add(plot,buffer);
345 legend = xy->y_scale->legend;
346 if(!strcmp(legend,""))legend = "Y";
347 snprintf(buffer,320,"%s = %+.*f",
348 legend,
349 depth,
350 plot->sely);
351 _sv_plot_legend_add(plot,buffer);
353 if(p->dimensions)
354 _sv_plot_legend_add(plot,NULL);
357 // add each dimension to the legend
358 if(-_sv_scalespace_decimal_exponent(&xy->y) > depth)
359 depth = -_sv_scalespace_decimal_exponent(&xy->y);
361 for(i=0;i<p->dimensions;i++){
362 sv_dim_t *d = p->dimension_list[i].d;
364 if(d != p->private->x_d ||
365 plot->cross_active){
367 snprintf(buffer,320,"%s = %+.*f",
368 p->dimension_list[i].d->name,
369 depth,
370 p->dimension_list[i].d->val);
371 _sv_plot_legend_add(plot,buffer);
375 gdk_threads_leave ();
379 // call with lock
380 static double _sv_panelxy_zoom_metric(sv_panel_t *p, int off){
381 _sv_panelxy_t *xy = p->subtype->xy;
382 int on = p->objectives;
383 double pw = p->private->graph->allocation.width;
384 double ph = p->private->graph->allocation.height;
385 int dw = xy->data_v.pixels;
387 // if this is a discrete data set, size/view changes cannot affect
388 // the data spacing; that's set by the discrete scale
389 if(p->private->x_d->type != SV_DIM_CONTINUOUS) return -1;
391 double xscale = _sv_scalespace_pixel(&xy->x,1.) - _sv_scalespace_pixel(&xy->x,0.);
392 double yscale = _sv_scalespace_pixel(&xy->y,1.) - _sv_scalespace_pixel(&xy->y,0.);
393 double lox = _sv_scalespace_value(&xy->x,0.);
394 double loy = _sv_scalespace_value(&xy->y,ph);
395 double hix = _sv_scalespace_value(&xy->x,pw);
396 double hiy = _sv_scalespace_value(&xy->y,0.);
398 // by plane, look at the spacing between visible x/y points
399 double max = -1;
400 int i,j;
401 for(i=0;i<on;i++){
403 if(xy->x_vec[i] && xy->y_vec[i]){
404 double xacc = 0;
405 double yacc = 0;
406 double count = 0;
407 double *x = xy->x_vec[i];
408 double *y = xy->y_vec[i];
410 for(j = off; j<dw; j++){
411 if(!isnan(x[j-off]) &&
412 !isnan(y[j-off]) &&
413 !isnan(x[j]) &&
414 !isnan(y[j]) &&
415 !(x[j-off] < lox && x[j] < lox) &&
416 !(y[j-off] < loy && y[j] < loy) &&
417 !(x[j-off] > hix && x[j] > hix) &&
418 !(y[j-off] > hiy && y[j] > hiy)){
420 xacc += (x[j-off]-x[j]) * (x[j-off]-x[j]);
421 yacc += (y[j-off]-y[j]) * (y[j-off]-y[j]);
422 count++;
426 if(count>0){
427 double acc = sqrt((xacc*xscale*xscale + yacc*yscale*yscale)/count);
428 if(acc > max) max = acc;
433 return max;
437 // call while locked
438 static int _sv_panelxy_mark_recompute_by_metric(sv_panel_t *p, int recursing){
439 if(!p->private->realized) return 0;
441 _sv_panelxy_t *xy = p->subtype->xy;
443 // discrete val dimensions are immune to rerender by metric changes
444 if(p->private->x_d->type != SV_DIM_CONTINUOUS) return 0;
446 double target = (double) p->private->oversample_d / p->private->oversample_n;
447 double full = _sv_panelxy_zoom_metric(p, 1);
449 if(full > target){
450 // we want to halve the sample spacing. But first make sure we're
451 // not looping due to uncertainties in the metric.
452 if(recursing && xy->prev_zoom > xy->curr_zoom) return 0;
454 // also make sure our zoom level doesn't underflow
455 if(xy->data_v.massaged || xy->curr_zoom>48) return 0;
457 xy->req_zoom = xy->curr_zoom+1;
459 _sv_panel_dirty_plot(p); // trigger recompute
460 return 1;
461 } else {
463 double half = _sv_panelxy_zoom_metric(p, 2);
464 if(half < target){
465 // we want to double the sample spacing. But first make sure we're
466 // not looping due to uncertainties in the metric.
467 if(recursing && xy->prev_zoom < xy->curr_zoom) return 0;
469 // also make sure our zoom level doesn't overrflow
470 if(xy->curr_zoom == 0) return 0;
472 xy->req_zoom = xy->curr_zoom-1;
474 _sv_panel_dirty_plot(p); // trigger recompute
475 return 1;
480 xy->req_zoom = xy->prev_zoom = xy->curr_zoom;
481 return 0;
484 static void _sv_panelxy_mapchange_callback(GtkWidget *w,gpointer in){
485 sv_obj_list_t *optr = (sv_obj_list_t *)in;
486 sv_panel_t *p = optr->p;
487 _sv_panelxy_t *xy = p->subtype->xy;
488 int onum = optr - p->objective_list;
489 _sv_plot_t *plot = PLOT(p->private->graph);
491 _sv_undo_push();
492 _sv_undo_suspend();
494 // update colormap
495 // oh, the wasteful
496 int pos = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
497 _sv_solid_set_func(&xy->mappings[onum],pos);
498 _sv_slider_set_gradient(xy->alpha_scale[onum], &xy->mappings[onum]);
500 // if the mapping has become inactive and the crosshairs point to
501 // this objective, inactivate the crosshairs.
502 if(xy->cross_objnum == onum)
503 plot->cross_active = 0;
505 _sv_panel_dirty_map(p);
506 _sv_panel_dirty_legend(p);
508 _sv_undo_resume();
511 static void _sv_panelxy_alpha_callback(void * in, int buttonstate){
512 sv_obj_list_t *optr = (sv_obj_list_t *)in;
513 sv_panel_t *p = optr->p;
515 if(buttonstate == 0){
516 _sv_undo_push();
517 _sv_undo_suspend();
520 _sv_panel_dirty_map(p);
521 _sv_panel_dirty_legend(p);
523 if(buttonstate == 2)
524 _sv_undo_resume();
527 static void _sv_panelxy_linetype_callback(GtkWidget *w,gpointer in){
528 sv_obj_list_t *optr = (sv_obj_list_t *)in;
529 sv_panel_t *p = optr->p;
530 _sv_panelxy_t *xy = p->subtype->xy;
531 int onum = optr - p->objective_list;
533 _sv_undo_push();
534 _sv_undo_suspend();
536 // update colormap
537 int pos = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
538 xy->linetype[onum] = line_name[pos]->value;
540 _sv_panel_dirty_map(p);
541 _sv_undo_resume();
544 static void _sv_panelxy_pointtype_callback(GtkWidget *w,gpointer in){
545 sv_obj_list_t *optr = (sv_obj_list_t *)in;
546 sv_panel_t *p = optr->p;
547 _sv_panelxy_t *xy = p->subtype->xy;
548 int onum = optr - p->objective_list;
550 _sv_undo_push();
551 _sv_undo_suspend();
553 // update colormap
554 int pos = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
555 xy->pointtype[onum] = point_name[pos]->value;
557 _sv_panel_dirty_map(p);
558 _sv_undo_resume();
561 static void _sv_panelxy_map_callback(void *in,int buttonstate){
562 sv_panel_t *p = (sv_panel_t *)in;
563 _sv_panelxy_t *xy = p->subtype->xy;
564 _sv_plot_t *plot = PLOT(p->private->graph);
566 if(buttonstate == 0){
567 _sv_undo_push();
568 _sv_undo_suspend();
571 // has new bracketing changed the plot range scale?
572 if(xy->x_bracket[0] != _sv_slider_get_value(xy->x_slider,0) ||
573 xy->x_bracket[1] != _sv_slider_get_value(xy->x_slider,1) ||
574 xy->y_bracket[0] != _sv_slider_get_value(xy->y_slider,0) ||
575 xy->y_bracket[1] != _sv_slider_get_value(xy->y_slider,1)){
577 int w = plot->w.allocation.width;
578 int h = plot->w.allocation.height;
580 xy->x_bracket[0] = _sv_slider_get_value(xy->x_slider,0);
581 xy->x_bracket[1] = _sv_slider_get_value(xy->x_slider,1);
582 xy->y_bracket[0] = _sv_slider_get_value(xy->y_slider,0);
583 xy->y_bracket[1] = _sv_slider_get_value(xy->y_slider,1);
586 xy->x = _sv_scalespace_linear(xy->x_bracket[0],
587 xy->x_bracket[1],
589 plot->scalespacing,
590 xy->x_scale->legend);
591 xy->y = _sv_scalespace_linear(xy->y_bracket[1],
592 xy->y_bracket[0],
594 plot->scalespacing,
595 xy->y_scale->legend);
597 // a map view size change may trigger a progressive up/down render,
598 // but will at least cause a remap
599 _sv_panelxy_mark_recompute_by_metric(p,0);
600 _sv_panel_dirty_map(p);
604 if(buttonstate == 2)
605 _sv_undo_resume();
608 static void _sv_panelxy_update_xsel(sv_panel_t *p){
609 _sv_panelxy_t *xy = p->subtype->xy;
610 int i;
612 // enable/disable dimension slider thumbs
613 for(i=0;i<p->dimensions;i++){
615 if(xy->dim_xb[i] &&
616 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(xy->dim_xb[i]))){
618 // set the x dim flag
619 p->private->x_d = p->dimension_list[i].d;
620 xy->x_widget = p->private->dim_scales[i];
621 xy->x_dnum = i;
623 if(xy->dim_xb[i] &&
624 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(xy->dim_xb[i]))){
625 // make all thumbs visible
626 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],0,1);
627 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],2,1);
628 }else{
629 // make bracket thumbs invisible */
630 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],0,0);
631 _sv_dim_widget_set_thumb_active(p->private->dim_scales[i],2,0);
636 static void _sv_panelxy_compute_line(sv_panel_t *p,
637 int serialno,
638 int x_d,
639 _sv_scalespace_t sxi,
640 double *dim_vals,
641 char *prefilled,
642 double **x_vec,
643 double **y_vec,
644 _sv_bythread_cache_xy_t *c){
646 int i,j,fn=_sv_functions;
647 int w = sxi.pixels;
650 /* by x */
651 for(j=0;j<w;j++){
652 if(!prefilled[j]){
653 dim_vals[x_d] = _sv_scalespace_value(&sxi,j);
655 /* by function */
656 for(i=0;i<fn;i++){
657 if(c->call[i]){
658 double *fout = c->fout[i];
659 c->call[i](dim_vals,fout);
663 /* process function output by objective */
664 /* xy panels currently only care about the XY output values; in the
665 future, Z and others may also be relevant */
666 for(i=0;i<p->objectives;i++){
667 sv_obj_t *o = p->objective_list[i].o;
668 int xoff = o->private->x_fout;
669 int yoff = o->private->y_fout;
670 sv_func_t *xf = o->private->x_func;
671 sv_func_t *yf = o->private->y_func;
672 x_vec[i][j] = c->fout[xf->number][xoff];
673 y_vec[i][j] = c->fout[yf->number][yoff];
677 if(!(j&0x3f)){
678 gdk_threads_enter();
680 if(serialno != p->private->plot_serialno){
681 gdk_threads_leave();
682 return;
685 _sv_spinner_set_busy(p->private->spinner);
686 gdk_threads_leave();
691 // call with lock
692 void _sv_panelxy_mark_recompute(sv_panel_t *p){
693 if(!p->private->realized) return;
695 _sv_panelxy_t *xy = p->subtype->xy;
696 xy->req_zoom = xy->prev_zoom = xy->curr_zoom;
697 _sv_panel_dirty_plot(p);
700 // subtype entry point for plot remaps; lock held
701 int _sv_panelxy_map_redraw(sv_panel_t *p, _sv_bythread_cache_t *c){
702 if(p->private->map_progress_count)return 0;
703 p->private->map_progress_count++;
705 // render to a temp surface so that we can release the lock occasionally
706 _sv_plot_t *plot = PLOT(p->private->graph);
707 cairo_surface_t *back = plot->back;
708 cairo_surface_t *cs = cairo_surface_create_similar(back,CAIRO_CONTENT_COLOR,
709 cairo_image_surface_get_width(back),
710 cairo_image_surface_get_height(back));
711 cairo_t *ct = cairo_create(cs);
713 if(_sv_panelxy_remap(p,ct) == -1){ // returns -1 on abort
714 cairo_destroy(ct);
715 cairo_surface_destroy(cs);
716 }else{
717 // else complete
718 cairo_surface_destroy(plot->back);
719 plot->back = cs;
720 cairo_destroy(ct);
722 _sv_panel_clean_map(p);
723 _sv_plot_expose_request(plot);
726 return 1;
729 static void _sv_panelxy_recompute_callback(void *ptr){
730 sv_panel_t *p = (sv_panel_t *)ptr;
731 _sv_panelxy_t *xy = p->subtype->xy;
732 _sv_plot_t *plot = PLOT(p->private->graph);
733 int w = plot->w.allocation.width;
734 int h = plot->w.allocation.height;
736 plot->x = xy->x = _sv_scalespace_linear(xy->x_bracket[0],
737 xy->x_bracket[1],
739 plot->scalespacing,
740 xy->x_scale->legend);
741 plot->y = xy->y = _sv_scalespace_linear(xy->y_bracket[1],
742 xy->y_bracket[0],
744 plot->scalespacing,
745 xy->y_scale->legend);
747 if(xy->panel_w != w || xy->panel_h != h){
748 p->private->map_progress_count=0;
749 _sv_panelxy_map_redraw(p, NULL);
752 xy->panel_w = w;
753 xy->panel_h = h;
755 // always recompute, but also update zoom
756 if(!_sv_panelxy_mark_recompute_by_metric(p,0))
757 _sv_panelxy_mark_recompute(p);
760 static void _sv_panelxy_update_crosshair(sv_panel_t *p){
761 _sv_panelxy_t *xy = p->subtype->xy;
762 _sv_plot_t *plot = PLOT(p->private->graph);
765 if(!p->private->realized)return;
767 // crosshairs snap to the x/y location of a point; however, with
768 // multiple objectives, there are probably multiple possible points.
769 // So, if we're currently pointing to a point for a given objective,
770 // update to a point on the same objective. Otherwise if crosshairs
771 // are inactive, do nothing.
773 if(!plot->cross_active)return;
774 if(xy->cross_objnum<0 || xy->cross_objnum >= p->objectives)return;
775 if(!xy->x_vec || !xy->y_vec)return;
776 if(!xy->x_vec[xy->cross_objnum] || !xy->y_vec[xy->cross_objnum])return;
778 // get bin number of dim value
779 int x_bin = rint(_sv_scalespace_pixel(&xy->data_v, p->private->x_d->val));
780 double x = xy->x_vec[xy->cross_objnum][x_bin];
781 double y = xy->y_vec[xy->cross_objnum][x_bin];
783 _sv_plot_set_crosshairs(plot,x,y);
784 _sv_dim_set_thumb(p->private->x_d,1,_sv_scalespace_value(&xy->data_v, x_bin));
786 _sv_panel_dirty_legend(p);
789 static void _sv_panelxy_center_callback(sv_dim_list_t *dptr){
790 sv_dim_t *d = dptr->d;
791 sv_panel_t *p = dptr->p;
792 int axisp = (d == p->private->x_d);
794 if(!axisp){
795 // mid slider of a non-axis dimension changed, rerender
796 _sv_panelxy_clear_data(p);
797 _sv_panelxy_mark_recompute(p);
798 }else{
799 // mid slider of an axis dimension changed, move crosshairs
800 _sv_panelxy_update_crosshair(p);
804 static void _sv_panelxy_bracket_callback(sv_dim_list_t *dptr){
805 sv_dim_t *d = dptr->d;
806 sv_panel_t *p = dptr->p;
807 int axisp = (d == p->private->x_d);
809 // always need to recompute, may also need to update zoom
811 if(axisp)
812 if(!_sv_panelxy_mark_recompute_by_metric(p,0))
813 _sv_panelxy_mark_recompute(p);
817 static void _sv_panelxy_dimchange_callback(GtkWidget *button,gpointer in){
818 sv_panel_t *p = (sv_panel_t *)in;
819 _sv_panelxy_t *xy = p->subtype->xy;
821 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))){
823 _sv_undo_push();
824 _sv_undo_suspend();
826 _sv_panelxy_update_xsel(p);
828 // clear data vectors so that none of the data is reused.
829 _sv_panelxy_clear_data(p);
831 _sv_panelxy_update_crosshair(p); // which is to say, deactivate it
832 _sv_plot_unset_box(PLOT(p->private->graph));
834 xy->curr_zoom = xy->prev_zoom = xy->req_zoom = 0;
835 _sv_panelxy_mark_recompute(p);
837 _sv_undo_resume();
841 static void _sv_panelxy_crosshair_callback(sv_panel_t *p){
842 _sv_panelxy_t *xy = p->subtype->xy;
843 double x=PLOT(p->private->graph)->selx;
844 double y=PLOT(p->private->graph)->sely;
845 int i,j;
847 _sv_undo_push();
848 _sv_undo_suspend();
850 // snap crosshairs to the closest plotted x/y point
851 int besto=-1;
852 int bestbin=-1;
853 double bestdist;
855 if(xy->x_vec && xy->y_vec){
856 for(i=0;i<p->objectives;i++){
857 if(xy->x_vec[i] && xy->y_vec[i] && !_sv_mapping_inactive_p(xy->mappings+i)){
858 for(j=0;j<xy->data_v.pixels;j++){
859 double xd = x - xy->x_vec[i][j];
860 double yd = y - xy->y_vec[i][j];
861 double dist = xd*xd + yd*yd;
862 if(besto==-1 || dist<bestdist){
863 besto = i;
864 bestbin = j;
865 bestdist = dist;
872 if(besto>-1){
873 x = xy->x_vec[besto][bestbin];
874 y = xy->y_vec[besto][bestbin];
876 xy->cross_objnum = besto;
878 double dimval = _sv_scalespace_value(&xy->data_v, bestbin);
879 _sv_dim_set_thumb(p->private->x_d,1,dimval);
883 PLOT(p->private->graph)->selx = x;
884 PLOT(p->private->graph)->sely = y;
886 p->private->oldbox_active = 0;
887 _sv_undo_resume();
888 _sv_panel_dirty_legend(p);
892 static void _sv_panelxy_box_callback(void *in, int state){
893 sv_panel_t *p = (sv_panel_t *)in;
894 _sv_panelxy_t *xy = p->subtype->xy;
895 _sv_plot_t *plot = PLOT(p->private->graph);
897 switch(state){
898 case 0: // box set
899 _sv_undo_push();
900 _sv_plot_box_vals(plot,xy->oldbox);
901 p->private->oldbox_active = plot->box_active;
902 break;
903 case 1: // box activate
904 _sv_undo_push();
905 _sv_undo_suspend();
907 _sv_panelxy_crosshair_callback(p);
909 _sv_slider_set_value(xy->x_slider,0,xy->oldbox[0]);
910 _sv_slider_set_value(xy->x_slider,1,xy->oldbox[1]);
911 _sv_slider_set_value(xy->y_slider,0,xy->oldbox[2]);
912 _sv_slider_set_value(xy->y_slider,1,xy->oldbox[3]);
914 p->private->oldbox_active = 0;
915 _sv_undo_resume();
916 break;
918 _sv_panel_update_menus(p);
921 void _sv_panelxy_maintain_cache(sv_panel_t *p, _sv_bythread_cache_xy_t *c, int w){
923 /* toplevel initialization */
924 if(c->fout == 0){
925 int i,j;
927 /* determine which functions are actually needed */
928 c->call = calloc(_sv_functions,sizeof(*c->call));
929 c->fout = calloc(_sv_functions,sizeof(*c->fout));
930 for(i=0;i<p->objectives;i++){
931 sv_obj_t *o = p->objective_list[i].o;
932 for(j=0;j<o->outputs;j++)
933 c->call[o->function_map[j]]=
934 _sv_function_list[o->function_map[j]]->callback;
937 for(i=0;i<_sv_functions;i++){
938 if(c->call[i]){
939 c->fout[i] = malloc(_sv_function_list[i]->outputs *
940 sizeof(**c->fout));
946 // subtype entry point for legend redraws; lock held
947 int _sv_panelxy_legend_redraw(sv_panel_t *p){
948 _sv_plot_t *plot = PLOT(p->private->graph);
950 if(p->private->legend_progress_count)return 0;
951 p->private->legend_progress_count++;
952 _sv_panelxy_update_legend(p);
953 _sv_panel_clean_legend(p);
955 gdk_threads_leave();
956 _sv_plot_draw_scales(plot);
957 gdk_threads_enter();
959 _sv_plot_expose_request(plot);
960 return 1;
963 // dim scales are autozoomed; we want the initial values to quantize
964 // to the same grid regardless of zoom level or starting bracket as
965 // well as only encompass the desired range
966 static int _sv_panelxy_generate_dimscale(sv_dim_t *d, int zoom, _sv_scalespace_t *v, _sv_scalespace_t *i){
968 if(d->type != SV_DIM_CONTINUOUS){
969 // non-continuous is unaffected by zoom
970 _sv_dim_scales(d, d->bracket[0], d->bracket[1], 2, 2, 1, d->name, NULL, v, i);
971 return 0;
974 // continuous dimensions are, in some ways, handled like a discrete dim.
975 double lo = d->scale->val_list[0];
976 double hi = d->scale->val_list[d->scale->vals-1];
977 _sv_dim_scales(d, lo, hi, 2, 2, 1, d->name, NULL, v, i);
979 // this is iterative, not direct computation, so that at each level
980 // we have a small adjustment (as opposed to one huge adjustment at
981 // the end)
982 int sofar = 0;
983 int neg = (lo<hi?1:-1);
985 while (sofar<zoom){
986 // double scale resolution
987 _sv_scalespace_double(v);
988 _sv_scalespace_double(i);
990 if(v->massaged)return 1;
992 // clip scales down the the desired part of the range
993 // an assumption: v->step_pixel == 1 because spacing is 1. We can
994 // increment first_val instead of first_pixel.
995 while(_sv_scalespace_value(v,1)*neg < d->bracket[0]*neg){
996 v->first_val += v->neg;
997 i->first_val += v->neg;
998 v->pixels--;
999 i->pixels--;
1002 while(v->pixels>2 && _sv_scalespace_value(v,v->pixels-1)*neg > d->bracket[1]*neg){
1003 v->pixels--;
1004 i->pixels--;
1007 while(_sv_scalespace_value(v,v->pixels-1)*neg < d->bracket[1]*neg){
1008 v->pixels++;
1009 i->pixels++;
1012 sofar++;
1015 return 0;
1018 static void _sv_panelxy_rescale(_sv_scalespace_t *old, double **oldx, double **oldy,
1019 _sv_scalespace_t *new, double **newx, double **newy,
1020 char *prefilled, int objectives){
1022 if(!oldx || !oldy)return;
1024 int newi = 0;
1025 int oldi = 0;
1026 int j;
1028 for(j=0;j<objectives;j++)
1029 if(!oldx[j] || !oldy[j])
1030 return;
1032 long num = _sv_scalespace_scalenum(new,old);
1033 long den = _sv_scalespace_scaleden(new,old);
1034 long oldpos = -_sv_scalespace_scalebin(new,old);
1035 long newpos = 0;
1037 while(newi < new->pixels && oldi < old->pixels){
1039 if(oldpos == newpos &&
1040 oldi >= 0){
1041 prefilled[newi] = 1;
1042 for(j=0;j<objectives;j++){
1043 newx[j][newi] = oldx[j][oldi];
1044 newy[j][newi] = oldy[j][oldi];
1046 newpos += num;
1047 newi++;
1048 oldpos += den;
1049 oldi++;
1053 while(newi < new->pixels && newpos < oldpos ){
1054 newpos += num;
1055 newi++;
1057 while(oldi < old->pixels && oldpos < newpos ){
1058 oldpos += den;
1059 oldi++;
1064 // subtype entry point for recomputation; lock held
1065 int _sv_panelxy_compute(sv_panel_t *p,
1066 _sv_bythread_cache_t *c){
1067 _sv_panelxy_t *xy = p->subtype->xy;
1068 _sv_plot_t *plot;
1070 int dw,w,h,i,d;
1071 int serialno;
1072 int x_d=-1;
1073 int prev_zoom = xy->curr_zoom;
1074 int zoom = xy->req_zoom;
1076 _sv_scalespace_t sxv = xy->data_v;
1077 plot = PLOT(p->private->graph);
1079 dw = sxv.pixels;
1080 w = plot->w.allocation.width;
1081 h = plot->w.allocation.height;
1083 // this computation is single-threaded for now
1084 if(p->private->plot_progress_count)
1085 return 0;
1087 serialno = p->private->plot_serialno;
1088 p->private->plot_progress_count++;
1089 d = p->dimensions;
1091 /* render using local dimension array; several threads will be
1092 computing objectives */
1093 double dim_vals[_sv_dimensions];
1095 /* get iterator bounds, use iterator scale */
1096 x_d = p->private->x_d->number;
1098 /* generate a new data_v/data_i */
1099 _sv_scalespace_t newv;
1100 _sv_scalespace_t newi;
1101 _sv_panelxy_generate_dimscale(p->private->x_d, zoom, &newv, &newi);
1102 dw = newv.pixels;
1104 /* compare new/old data scales; pre-fill the data vec with values
1105 from old data vector if it can be reused */
1106 double *new_x_vec[p->objectives];
1107 double *new_y_vec[p->objectives];
1108 char *prefilled = calloc(dw,sizeof(*prefilled));
1109 for(i=0;i<p->objectives;i++){
1110 new_x_vec[i] = calloc(dw,sizeof(**new_x_vec));
1111 new_y_vec[i] = calloc(dw,sizeof(**new_y_vec));
1113 _sv_panelxy_rescale(&sxv,xy->x_vec,xy->y_vec,
1114 &newv,new_x_vec,new_y_vec,prefilled, p->objectives);
1116 // Initialize local dimension value array
1117 for(i=0;i<_sv_dimensions;i++){
1118 sv_dim_t *dim = _sv_dimension_list[i];
1119 dim_vals[i]=dim->val;
1122 _sv_panelxy_maintain_cache(p,&c->xy,dw);
1124 plot->x = xy->x;
1125 plot->y = xy->y;
1127 /* unlock for computation */
1128 gdk_threads_leave ();
1130 _sv_panelxy_compute_line(p, serialno, x_d, newi, dim_vals, prefilled, new_x_vec, new_y_vec, &c->xy);
1132 gdk_threads_enter ();
1134 if(serialno == p->private->plot_serialno){
1135 // replace data vectors
1136 p->private->plot_serialno++;
1137 _sv_panelxy_clear_data(p);
1138 if(!xy->x_vec)
1139 xy->x_vec = calloc(p->objectives, sizeof(*xy->x_vec));
1140 if(!xy->y_vec)
1141 xy->y_vec = calloc(p->objectives, sizeof(*xy->y_vec));
1142 for(i=0;i<p->objectives;i++){
1143 xy->x_vec[i] = new_x_vec[i];
1144 xy->y_vec[i] = new_y_vec[i];
1146 free(prefilled);
1147 // replace scales;
1148 xy->data_v = newv;
1149 xy->data_i = newi;
1151 xy->prev_zoom = prev_zoom;
1152 xy->curr_zoom = zoom;
1154 _sv_panel_clean_plot(p);
1155 if(!_sv_panelxy_mark_recompute_by_metric(p, 1)){
1156 _sv_panel_dirty_legend(p);
1157 _sv_panel_dirty_map(p);
1158 }else{
1159 _sv_panel_dirty_map_throttled(p);
1162 }else{
1163 for(i=0;i<p->objectives;i++){
1164 free(new_x_vec[i]);
1165 free(new_y_vec[i]);
1167 free(prefilled);
1170 return 1;
1173 static void _sv_panelxy_undo_log(_sv_panel_undo_t *u, sv_panel_t *p){
1174 _sv_panelxy_t *xy = p->subtype->xy;
1175 _sv_plot_t *plot = PLOT(p->private->graph);
1176 int i;
1178 // alloc fields as necessary
1179 if(!u->mappings)
1180 u->mappings = calloc(p->objectives,sizeof(*u->mappings));
1181 if(!u->scale_vals[0])
1182 u->scale_vals[0] = calloc(3,sizeof(**u->scale_vals));
1183 if(!u->scale_vals[1])
1184 u->scale_vals[1] = calloc(3,sizeof(**u->scale_vals));
1185 if(!u->scale_vals[2])
1186 u->scale_vals[2] = calloc(p->objectives,sizeof(**u->scale_vals));
1188 // populate undo
1189 u->scale_vals[0][0] = _sv_slider_get_value(xy->x_slider,0);
1190 u->scale_vals[1][0] = _sv_slider_get_value(xy->x_slider,1);
1191 u->scale_vals[0][1] = plot->selx;
1192 u->scale_vals[1][1] = plot->sely;
1193 u->scale_vals[0][2] = _sv_slider_get_value(xy->y_slider,0);
1194 u->scale_vals[1][2] = _sv_slider_get_value(xy->y_slider,1);
1196 for(i=0;i<p->objectives;i++){
1197 u->mappings[i] =
1198 (xy->mappings[i].mapnum<<24) |
1199 (xy->linetype[i]<<16) |
1200 (xy->pointtype[i]<<8);
1201 u->scale_vals[2][i] = _sv_slider_get_value(xy->alpha_scale[i],0);
1204 u->x_d = xy->x_dnum;
1205 u->box[0] = xy->oldbox[0];
1206 u->box[1] = xy->oldbox[1];
1207 u->box[2] = xy->oldbox[2];
1208 u->box[3] = xy->oldbox[3];
1210 u->box_active = p->private->oldbox_active;
1214 static void _sv_panelxy_undo_restore(_sv_panel_undo_t *u, sv_panel_t *p){
1215 _sv_panelxy_t *xy = p->subtype->xy;
1216 _sv_plot_t *plot = PLOT(p->private->graph);
1218 int i;
1220 // go in through widgets
1222 _sv_slider_set_value(xy->x_slider,0,u->scale_vals[0][0]);
1223 _sv_slider_set_value(xy->x_slider,1,u->scale_vals[1][0]);
1224 plot->selx = u->scale_vals[0][1];
1225 plot->sely = u->scale_vals[1][1];
1226 _sv_slider_set_value(xy->y_slider,0,u->scale_vals[0][2]);
1227 _sv_slider_set_value(xy->y_slider,1,u->scale_vals[1][2]);
1229 for(i=0;i<p->objectives;i++){
1230 gtk_combo_box_set_active(GTK_COMBO_BOX(xy->map_pulldowns[i]), (u->mappings[i]>>24)&0xff );
1231 gtk_combo_box_set_active(GTK_COMBO_BOX(xy->line_pulldowns[i]), (u->mappings[i]>>16)&0xff );
1232 gtk_combo_box_set_active(GTK_COMBO_BOX(xy->point_pulldowns[i]), (u->mappings[i]>>8)&0xff );
1233 _sv_slider_set_value(xy->alpha_scale[i],0,u->scale_vals[2][i]);
1236 if(xy->dim_xb && u->x_d<p->dimensions && xy->dim_xb[u->x_d])
1237 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(xy->dim_xb[u->x_d]),TRUE);
1239 _sv_panelxy_update_xsel(p);
1240 _sv_panelxy_crosshair_callback(p);
1242 if(u->box_active){
1243 xy->oldbox[0] = u->box[0];
1244 xy->oldbox[1] = u->box[1];
1245 xy->oldbox[2] = u->box[2];
1246 xy->oldbox[3] = u->box[3];
1247 _sv_plot_box_set(plot,u->box);
1248 p->private->oldbox_active = 1;
1249 }else{
1250 _sv_plot_unset_box(plot);
1251 p->private->oldbox_active = 0;
1255 static void _sv_panelxy_realize(sv_panel_t *p){
1256 _sv_panelxy_t *xy = p->subtype->xy;
1257 char buffer[160];
1258 int i;
1259 _sv_undo_suspend();
1261 p->private->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1262 g_signal_connect_swapped (G_OBJECT (p->private->toplevel), "delete-event",
1263 G_CALLBACK (_sv_clean_exit), (void *)SIGINT);
1265 // add border to sides with hbox/padding
1266 GtkWidget *borderbox = gtk_hbox_new(0,0);
1267 gtk_container_add (GTK_CONTAINER (p->private->toplevel), borderbox);
1269 // main layout vbox
1270 p->private->topbox = gtk_vbox_new(0,0);
1271 gtk_box_pack_start(GTK_BOX(borderbox), p->private->topbox, 1,1,4);
1272 gtk_container_set_border_width (GTK_CONTAINER (p->private->toplevel), 1);
1274 /* spinner, top bar */
1276 GtkWidget *hbox = gtk_hbox_new(0,0);
1277 gtk_box_pack_start(GTK_BOX(p->private->topbox), hbox, 0,0,0);
1278 gtk_box_pack_end(GTK_BOX(hbox),GTK_WIDGET(p->private->spinner),0,0,0);
1281 /* plotbox, graph */
1283 xy->graph_table = gtk_table_new(3,3,0);
1284 p->private->plotbox = xy->graph_table;
1285 gtk_box_pack_start(GTK_BOX(p->private->topbox), p->private->plotbox, 1,1,2);
1287 p->private->graph = GTK_WIDGET(_sv_plot_new(_sv_panelxy_recompute_callback,p,
1288 (void *)(void *)_sv_panelxy_crosshair_callback,p,
1289 _sv_panelxy_box_callback,p,0));
1291 gtk_table_attach(GTK_TABLE(xy->graph_table),p->private->graph,1,3,0,2,
1292 GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,1);
1295 /* X range slider */
1296 /* Y range slider */
1298 GtkWidget **slx = calloc(2,sizeof(*slx));
1299 GtkWidget **sly = calloc(2,sizeof(*sly));
1301 /* the range slices/slider */
1302 slx[0] = _sv_slice_new(_sv_panelxy_map_callback,p);
1303 slx[1] = _sv_slice_new(_sv_panelxy_map_callback,p);
1304 sly[0] = _sv_slice_new(_sv_panelxy_map_callback,p);
1305 sly[1] = _sv_slice_new(_sv_panelxy_map_callback,p);
1307 gtk_table_attach(GTK_TABLE(xy->graph_table),slx[0],1,2,2,3,
1308 GTK_EXPAND|GTK_FILL,0,0,0);
1309 gtk_table_attach(GTK_TABLE(xy->graph_table),slx[1],2,3,2,3,
1310 GTK_EXPAND|GTK_FILL,0,0,0);
1311 gtk_table_attach(GTK_TABLE(xy->graph_table),sly[0],0,1,1,2,
1312 GTK_SHRINK,GTK_EXPAND|GTK_FILL,0,0);
1313 gtk_table_attach(GTK_TABLE(xy->graph_table),sly[1],0,1,0,1,
1314 GTK_SHRINK,GTK_EXPAND|GTK_FILL,0,0);
1315 gtk_table_set_col_spacing(GTK_TABLE(xy->graph_table),0,4);
1317 xy->x_slider = _sv_slider_new((_sv_slice_t **)slx,2,
1318 xy->x_scale->label_list,
1319 xy->x_scale->val_list,
1320 xy->x_scale->vals,0);
1321 xy->y_slider = _sv_slider_new((_sv_slice_t **)sly,2,
1322 xy->y_scale->label_list,
1323 xy->y_scale->val_list,
1324 xy->y_scale->vals,
1325 _SV_SLIDER_FLAG_VERTICAL);
1327 int lo = xy->x_scale->val_list[0];
1328 int hi = xy->x_scale->val_list[xy->x_scale->vals-1];
1329 _sv_slice_thumb_set((_sv_slice_t *)slx[0],lo);
1330 _sv_slice_thumb_set((_sv_slice_t *)slx[1],hi);
1332 lo = xy->y_scale->val_list[0];
1333 hi = xy->y_scale->val_list[xy->y_scale->vals-1];
1334 _sv_slice_thumb_set((_sv_slice_t *)sly[0],lo);
1335 _sv_slice_thumb_set((_sv_slice_t *)sly[1],hi);
1338 /* obj box */
1340 xy->obj_table = gtk_table_new(p->objectives,5,0);
1341 gtk_box_pack_start(GTK_BOX(p->private->topbox), xy->obj_table, 0,0,1);
1343 /* pulldowns */
1344 xy->pointtype = calloc(p->objectives,sizeof(*xy->pointtype));
1345 xy->linetype = calloc(p->objectives,sizeof(*xy->linetype));
1346 xy->mappings = calloc(p->objectives,sizeof(*xy->mappings));
1347 xy->map_pulldowns = calloc(p->objectives,sizeof(*xy->map_pulldowns));
1348 xy->line_pulldowns = calloc(p->objectives,sizeof(*xy->line_pulldowns));
1349 xy->point_pulldowns = calloc(p->objectives,sizeof(*xy->point_pulldowns));
1350 xy->alpha_scale = calloc(p->objectives,sizeof(*xy->alpha_scale));
1352 for(i=0;i<p->objectives;i++){
1353 sv_obj_t *o = p->objective_list[i].o;
1355 /* label */
1356 GtkWidget *label = gtk_label_new(o->name);
1357 gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
1358 gtk_table_attach(GTK_TABLE(xy->obj_table),label,0,1,i,i+1,
1359 GTK_FILL,0,5,0);
1361 /* mapping pulldown */
1363 GtkWidget *menu=_gtk_combo_box_new_markup();
1364 int j;
1365 for(j=0;j<_sv_solid_names();j++){
1366 if(strcmp(_sv_solid_name(j),"inactive"))
1367 snprintf(buffer,sizeof(buffer),"<span foreground=\"%s\">%s</span>",_sv_solid_name(j),_sv_solid_name(j));
1368 else
1369 snprintf(buffer,sizeof(buffer),"%s",_sv_solid_name(j));
1371 gtk_combo_box_append_text (GTK_COMBO_BOX (menu), buffer);
1373 gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
1374 g_signal_connect (G_OBJECT (menu), "changed",
1375 G_CALLBACK (_sv_panelxy_mapchange_callback), p->objective_list+i);
1376 gtk_table_attach(GTK_TABLE(xy->obj_table),menu,1,2,i,i+1,
1377 GTK_SHRINK,GTK_SHRINK,5,0);
1378 xy->map_pulldowns[i] = menu;
1379 _sv_solid_setup(&xy->mappings[i],0.,1.,0);
1382 /* line pulldown */
1384 GtkWidget *menu=gtk_combo_box_new_text();
1385 int j;
1386 for(j=0;j<LINETYPES;j++)
1387 gtk_combo_box_append_text (GTK_COMBO_BOX (menu), line_name[j]->left);
1388 gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
1389 g_signal_connect (G_OBJECT (menu), "changed",
1390 G_CALLBACK (_sv_panelxy_linetype_callback), p->objective_list+i);
1391 gtk_table_attach(GTK_TABLE(xy->obj_table),menu,2,3,i,i+1,
1392 GTK_SHRINK,GTK_SHRINK,5,0);
1393 xy->line_pulldowns[i] = menu;
1396 /* point pulldown */
1398 GtkWidget *menu=gtk_combo_box_new_text();
1399 int j;
1400 for(j=0;j<POINTTYPES;j++)
1401 gtk_combo_box_append_text (GTK_COMBO_BOX (menu), point_name[j]->left);
1402 gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
1403 g_signal_connect (G_OBJECT (menu), "changed",
1404 G_CALLBACK (_sv_panelxy_pointtype_callback), p->objective_list+i);
1405 gtk_table_attach(GTK_TABLE(xy->obj_table),menu,3,4,i,i+1,
1406 GTK_SHRINK,GTK_SHRINK,5,0);
1407 xy->point_pulldowns[i] = menu;
1410 /* alpha slider */
1412 GtkWidget **sl = calloc(1, sizeof(*sl));
1413 sl[0] = _sv_slice_new(_sv_panelxy_alpha_callback,p->objective_list+i);
1415 gtk_table_attach(GTK_TABLE(xy->obj_table),sl[0],4,5,i,i+1,
1416 GTK_EXPAND|GTK_FILL,0,0,0);
1418 xy->alpha_scale[i] = _sv_slider_new((_sv_slice_t **)sl,1,
1419 (char *[]){"transparent","solid"},
1420 (double []){0.,1.},
1421 2,0);
1423 _sv_slider_set_gradient(xy->alpha_scale[i], &xy->mappings[i]);
1424 _sv_slice_thumb_set((_sv_slice_t *)sl[0],1.);
1430 /* dim box */
1431 if(p->dimensions){
1432 xy->dim_table = gtk_table_new(p->dimensions,3,0);
1433 gtk_box_pack_start(GTK_BOX(p->private->topbox), xy->dim_table, 0,0,4);
1435 p->private->dim_scales = calloc(p->dimensions,sizeof(*p->private->dim_scales));
1436 xy->dim_xb = calloc(p->dimensions,sizeof(*xy->dim_xb));
1437 GtkWidget *first_x = NULL;
1439 for(i=0;i<p->dimensions;i++){
1440 sv_dim_t *d = p->dimension_list[i].d;
1442 /* label */
1443 GtkWidget *label = gtk_label_new(d->name);
1444 gtk_misc_set_alignment(GTK_MISC(label),1.,.5);
1445 gtk_table_attach(GTK_TABLE(xy->dim_table),label,0,1,i,i+1,
1446 GTK_FILL,0,5,0);
1448 /* x radio buttons */
1449 if(!(d->flags & SV_DIM_NO_X)){
1450 if(first_x)
1451 xy->dim_xb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_x),"X");
1452 else{
1453 first_x = xy->dim_xb[i] = gtk_radio_button_new_with_label(NULL,"X");
1454 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(xy->dim_xb[i]),TRUE);
1456 gtk_table_attach(GTK_TABLE(xy->dim_table),xy->dim_xb[i],1,2,i,i+1,
1457 0,0,3,0);
1460 p->private->dim_scales[i] =
1461 _sv_dim_widget_new(p->dimension_list+i,_sv_panelxy_center_callback,_sv_panelxy_bracket_callback);
1463 gtk_table_attach(GTK_TABLE(xy->dim_table),
1464 GTK_WIDGET(p->private->dim_scales[i]->t),
1465 2,3,i,i+1,
1466 GTK_EXPAND|GTK_FILL,0,0,0);
1470 for(i=0;i<p->dimensions;i++)
1471 if(xy->dim_xb[i])
1472 g_signal_connect (G_OBJECT (xy->dim_xb[i]), "toggled",
1473 G_CALLBACK (_sv_panelxy_dimchange_callback), p);
1475 _sv_panelxy_update_xsel(p);
1478 gtk_widget_realize(p->private->toplevel);
1479 gtk_widget_realize(p->private->graph);
1480 gtk_widget_realize(GTK_WIDGET(p->private->spinner));
1481 gtk_widget_show_all(p->private->toplevel);
1483 _sv_undo_resume();
1487 static int _sv_panelxy_save(sv_panel_t *p, xmlNodePtr pn){
1488 _sv_panelxy_t *xy = p->subtype->xy;
1489 _sv_plot_t *plot = PLOT(p->private->graph);
1490 int ret=0,i;
1492 xmlNodePtr n;
1494 xmlNewProp(pn, (xmlChar *)"type", (xmlChar *)"xy");
1496 // box
1497 if(p->private->oldbox_active){
1498 xmlNodePtr boxn = xmlNewChild(pn, NULL, (xmlChar *) "box", NULL);
1499 _xmlNewPropF(boxn, "x1", xy->oldbox[0]);
1500 _xmlNewPropF(boxn, "x2", xy->oldbox[1]);
1501 _xmlNewPropF(boxn, "y1", xy->oldbox[2]);
1502 _xmlNewPropF(boxn, "y2", xy->oldbox[3]);
1505 // objective map settings
1506 for(i=0;i<p->objectives;i++){
1507 sv_obj_t *o = p->objective_list[i].o;
1509 xmlNodePtr on = xmlNewChild(pn, NULL, (xmlChar *) "objective", NULL);
1510 _xmlNewPropI(on, "position", i);
1511 _xmlNewPropI(on, "number", o->number);
1512 _xmlNewPropS(on, "name", o->name);
1513 _xmlNewPropS(on, "type", o->output_types);
1515 // right now Y is the only type; the below is Y-specific
1517 n = xmlNewChild(on, NULL, (xmlChar *) "y-map", NULL);
1518 _xmlNewMapProp(n, "color", _sv_solid_map(), xy->mappings[i].mapnum);
1519 _xmlNewMapProp(n, "line", line_name, xy->linetype[i]);
1520 _xmlNewMapProp(n, "point", point_name, xy->pointtype[i]);
1521 _xmlNewPropF(n, "alpha", _sv_slider_get_value(xy->alpha_scale[i],0));
1524 // x/y scale
1525 n = xmlNewChild(pn, NULL, (xmlChar *) "range", NULL);
1526 _xmlNewPropF(n, "x-low-bracket", _sv_slider_get_value(xy->x_slider,0));
1527 _xmlNewPropF(n, "x-high-bracket", _sv_slider_get_value(xy->x_slider,1));
1528 _xmlNewPropF(n, "y-low-bracket", _sv_slider_get_value(xy->y_slider,0));
1529 _xmlNewPropF(n, "y-high-bracket", _sv_slider_get_value(xy->y_slider,1));
1530 _xmlNewPropF(n, "x-cross", plot->selx);
1531 _xmlNewPropF(n, "y-cross", plot->sely);
1533 // x/y dim selection
1534 n = xmlNewChild(pn, NULL, (xmlChar *) "axes", NULL);
1535 _xmlNewPropI(n, "xpos", xy->x_dnum);
1537 return ret;
1540 int _sv_panelxy_load(sv_panel_t *p,
1541 _sv_panel_undo_t *u,
1542 xmlNodePtr pn,
1543 int warn){
1544 int i;
1546 // check type
1547 _xmlCheckPropS(pn,"type","xy", "Panel %d type mismatch in save file.",p->number,&warn);
1549 // box
1550 u->box_active = 0;
1551 _xmlGetChildPropFPreserve(pn, "box", "x1", &u->box[0]);
1552 _xmlGetChildPropFPreserve(pn, "box", "x2", &u->box[1]);
1553 _xmlGetChildPropFPreserve(pn, "box", "y1", &u->box[2]);
1554 _xmlGetChildPropFPreserve(pn, "box", "y2", &u->box[3]);
1556 xmlNodePtr n = _xmlGetChildS(pn, "box", NULL, NULL);
1557 if(n){
1558 u->box_active = 1;
1559 xmlFree(n);
1562 // objective map settings
1563 for(i=0;i<p->objectives;i++){
1564 sv_obj_t *o = p->objective_list[i].o;
1565 xmlNodePtr on = _xmlGetChildI(pn, "objective", "position", i);
1566 if(!on){
1567 _sv_first_load_warning(&warn);
1568 fprintf(stderr,"No save data found for panel %d objective \"%s\".\n",p->number, o->name);
1569 }else{
1570 // check name, type
1571 _xmlCheckPropS(on,"name",o->name, "Objectve position %d name mismatch in save file.",i,&warn);
1572 _xmlCheckPropS(on,"type",o->output_types, "Objectve position %d type mismatch in save file.",i,&warn);
1574 // right now Y is the only type; the below is Y-specific
1575 // load maptype, values
1576 int color = (u->mappings[i]>>24)&0xff;
1577 int line = (u->mappings[i]>>16)&0xff;
1578 int point = (u->mappings[i]>>8)&0xff;
1580 _xmlGetChildMapPreserve(on, "y-map", "color", _sv_solid_map(), &color,
1581 "Panel %d objective unknown mapping setting", p->number, &warn);
1582 _xmlGetChildMapPreserve(on, "y-map", "line", line_name, &line,
1583 "Panel %d objective unknown mapping setting", p->number, &warn);
1584 _xmlGetChildMapPreserve(on, "y-map", "point", point_name, &point,
1585 "Panel %d objective unknown mapping setting", p->number, &warn);
1586 _xmlGetChildPropF(on, "y-map", "alpha", &u->scale_vals[2][i]);
1588 u->mappings[i] = (color<<24) | (line<<16) | (point<<8);
1590 xmlFreeNode(on);
1594 _xmlGetChildPropFPreserve(pn, "range", "x-low-bracket", &u->scale_vals[0][0]);
1595 _xmlGetChildPropFPreserve(pn, "range", "x-high-bracket", &u->scale_vals[1][0]);
1596 _xmlGetChildPropFPreserve(pn, "range", "y-low-bracket", &u->scale_vals[0][2]);
1597 _xmlGetChildPropFPreserve(pn, "range", "y-high-bracket", &u->scale_vals[1][2]);
1598 _xmlGetChildPropFPreserve(pn, "range", "x-cross", &u->scale_vals[0][1]);
1599 _xmlGetChildPropF(pn, "range", "y-cross", &u->scale_vals[1][1]);
1601 // x/y dim selection
1602 _xmlGetChildPropI(pn, "axes", "xpos", &u->x_d);
1604 return warn;
1607 sv_panel_t *sv_panel_new_xy(int number,
1608 char *name,
1609 sv_scale_t *xscale,
1610 sv_scale_t *yscale,
1611 char *objectivelist,
1612 char *dimensionlist,
1613 unsigned flags){
1615 sv_panel_t *p = _sv_panel_new(number,name,objectivelist,dimensionlist,flags);
1616 _sv_panelxy_t *xy;
1618 if(!p)return NULL;
1619 xy = calloc(1, sizeof(*xy));
1620 p->subtype = calloc(1, sizeof(*p->subtype));
1622 p->subtype->xy = xy;
1623 p->type = SV_PANEL_XY;
1624 xy->x_scale = (sv_scale_t *)xscale;
1625 xy->y_scale = (sv_scale_t *)yscale;
1626 p->private->bg_type = SV_BG_WHITE;
1628 p->private->realize = _sv_panelxy_realize;
1629 p->private->map_action = _sv_panelxy_map_redraw;
1630 p->private->legend_action = _sv_panelxy_legend_redraw;
1631 p->private->compute_action = _sv_panelxy_compute;
1632 p->private->request_compute = _sv_panelxy_mark_recompute;
1633 p->private->crosshair_action = _sv_panelxy_crosshair_callback;
1634 p->private->print_action = _sv_panelxy_print;
1635 p->private->save_action = _sv_panelxy_save;
1636 p->private->load_action = _sv_panelxy_load;
1638 p->private->undo_log = _sv_panelxy_undo_log;
1639 p->private->undo_restore = _sv_panelxy_undo_restore;
1640 p->private->def_oversample_n = p->private->oversample_n = 1;
1641 p->private->def_oversample_d = p->private->oversample_d = 8;
1643 return p;