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)
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.
30 #include <sys/types.h>
35 // called from worker thread
36 static void recompute_setup(sv_plane2d_t
*pl
, sv_panel_t
*p
,
37 sv_dim_data_t
*payload
, int dims
){
40 pl
->image_x
= _sv_dim_panelscale(payload
+ x_dim
, p
->w
, 0);
41 pl
->image_y
= _sv_dim_panelscale(payload
+ y_dim
, p
->h
, 1);
46 // called from worker thread
47 static int image_work(sv_plane2d_t
*pl
, sv_panel_t
*p
){
52 // called from worker thread
53 static int data_work(sv_plane2d_t
*pl
, sv_panel_t
*p
){
58 // called from GTK/API
59 static void plane_remap(sv_plane2d_t
*pl
, sv_panel_t
*p
){
64 sv_plane_t
*sv_plane2d_new(){
74 static void _sv_planez_compute_line(sv_panel_t
*p
,
84 _sv_bythread_cache_2d_t
*c
){
86 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
89 /* cache access is unlocked because the cache is private to this
93 double *fout
= c
->fout
;
94 sv_func_t
**f
= p2
->used_function_list
;
95 int *obj_y_off
= p2
->y_fout_offset
;
96 int *onum
= p2
->y_obj_to_panel
;
99 dim_vals
[x_d
] = _sv_scalespace_value(&sxi
,j
);
100 for(i
=0;i
<p2
->used_functions
;i
++){
101 (*f
)->callback(dim_vals
,fout
);
102 fout
+= (*f
)->outputs
;
106 /* process function output by plane type/objective */
107 /* 2d panels currently only care about the Y output value */
110 for(i
=0;i
<p2
->y_obj_num
;i
++){
111 float val
= (float)_sv_slider_val_to_del(p2
->range_scales
[*onum
++], c
->fout
[*obj_y_off
++]);
117 c
->y_map
[i
][j
] = rint(val
* (256.f
*256.f
*256.f
));
123 if(p
->private->plot_serialno
== serialno
){
124 for(j
=0;j
<p2
->y_obj_num
;j
++){
125 int *d
= p2
->y_map
[j
] + y
*dw
;
126 int *td
= c
->y_map
[j
];
128 memcpy(d
,td
,dw
*sizeof(*d
));
136 static void _sv_panel2d_clear_pane(sv_panel_t
*p
){
138 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
139 int pw
= p2
->x
.pixels
;
140 int ph
= p2
->y
.pixels
;
142 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
144 for(i
=0;i
<p2
->y_obj_num
;i
++){
145 // map is freed and nulled to avoid triggering a fast-scale on an empty data pane
150 // free y_planes so that initial remap doesn't waste time on mix
151 // op; they are recreated during remap at the point the y_todo
152 // vector indicates something to do
154 free(p2
->y_planes
[i
]);
155 p2
->y_planes
[i
] = NULL
;
157 // work vector is merely cleared
158 memset(p2
->y_planetodo
[i
], 0, ph
*sizeof(**p2
->y_planetodo
));
161 // clear the background surface
163 memset(plot
->datarect
, 0, ph
*pw
*sizeof(*plot
->datarect
));
165 // the bg is not marked to be refreshed; computation setup will do
166 // that as part of the fast_scale, even if the fast_scale is
167 // short-circuited to a noop.
176 // used by the legend code. this lets us get away with having only a mapped display pane
178 static void _sv_panel2d_compute_point(sv_panel_t
*p
,sv_obj_t
*o
, double x
, double y
, compute_result
*out
){
179 double dim_vals
[_sv_dimensions
];
184 // fill in dimensions
185 int x_d
= p
->private->x_d
->number
;
186 int y_d
= p
->private->y_d
->number
;
188 for(i
=0;i
<_sv_dimensions
;i
++){
189 sv_dim_t
*dim
= _sv_dimension_list
[i
];
190 dim_vals
[i
]=dim
->val
;
198 *out
= (compute_result
){NAN
,NAN
,NAN
,NAN
,NAN
,NAN
,NAN
,NAN
};
201 for(i
=0;i
<_sv_functions
;i
++){
202 sv_func_t
*f
= _sv_function_list
[i
];
204 double fout
[f
->outputs
];
207 // compute and demultiplex output
208 for(j
=0;j
<o
->outputs
;j
++){
209 if(o
->function_map
[j
] == i
){
211 if(!compflag
) f
->callback(dim_vals
,fout
);
214 val
= fout
[o
->output_map
[j
]];
215 switch(o
->output_types
[j
]){
250 /* functions that perform actual graphical rendering */
252 static float _sv_panel2d_resample_helpers_init(_sv_scalespace_t
*to
, _sv_scalespace_t
*from
,
253 unsigned char *delA
, unsigned char *delB
,
254 int *posA
, int *posB
,
257 int dw
= from
->pixels
;
260 long scalenum
= _sv_scalespace_scalenum(to
,from
);
261 long scaleden
= _sv_scalespace_scaleden(to
,from
);
262 long del
= _sv_scalespace_scaleoff(to
,from
);
263 int bin
= del
/ scaleden
;
264 del
-= bin
* scaleden
;
265 int discscale
= (scaleden
>scalenum
?scalenum
:scaleden
);
266 int total
= xymul
*scalenum
/discscale
;
269 long del2
= del
+ scalenum
;
270 int sizeceil
= (del2
+ scaleden
- 1)/ scaleden
; // ceiling
271 int sizefloor
= del2
/ scaleden
;
273 while(bin
<0 && del2
>scaleden
){
280 if(del2
> scaleden
&& bin
>=0 && bin
<dw
){
283 delA
[i
] = ((xymul
* (scaleden
- del
)) + (discscale
>>1)) / discscale
;
286 rem
-= xymul
*(sizeceil
-2);
288 while(bin
+sizeceil
>dw
){
298 delB
[i
] = rem
; // don't leak
300 posB
[i
] = bin
+sizeceil
;
303 if(bin
<0 || bin
>=dw
){
313 if(del2
== scaleden
)del2
=0;
320 return (float)xymul
/total
;
323 /* x resample helpers are put in the per-thread cache because locking it would
324 be relatively expensive. */
326 static void _sv_panel2d_resample_helpers_manage_x(sv_panel_t
*p
, _sv_bythread_cache_2d_t
*c
){
327 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
328 if(p
->private->plot_serialno
!= c
->serialno
){
329 int pw
= p2
->x
.pixels
;
330 c
->serialno
= p
->private->plot_serialno
;
341 c
->xdelA
= calloc(pw
,sizeof(*c
->xdelA
));
342 c
->xdelB
= calloc(pw
,sizeof(*c
->xdelB
));
343 c
->xnumA
= calloc(pw
,sizeof(*c
->xnumA
));
344 c
->xnumB
= calloc(pw
,sizeof(*c
->xnumB
));
345 c
->xscalemul
= _sv_panel2d_resample_helpers_init(&p2
->x
, &p2
->x_v
, c
->xdelA
, c
->xdelB
, c
->xnumA
, c
->xnumB
, 17);
349 /* y resample is in the panel struct as per-row access is already locked */
351 static void _sv_panel2d_resample_helpers_manage_y(sv_panel_t
*p
){
352 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
353 if(p
->private->plot_serialno
!= p2
->resample_serialno
){
354 int ph
= p2
->y
.pixels
;
355 p2
->resample_serialno
= p
->private->plot_serialno
;
366 p2
->ydelA
= calloc(ph
,sizeof(*p2
->ydelA
));
367 p2
->ydelB
= calloc(ph
,sizeof(*p2
->ydelB
));
368 p2
->ynumA
= calloc(ph
,sizeof(*p2
->ynumA
));
369 p2
->ynumB
= calloc(ph
,sizeof(*p2
->ynumB
));
370 p2
->yscalemul
= _sv_panel2d_resample_helpers_init(&p2
->y
, &p2
->y_v
, p2
->ydelA
, p2
->ydelB
, p2
->ynumA
, p2
->ynumB
, 15);
374 static inline void _sv_panel2d_mapping_calc( void (*m
)(int,int, _sv_lcolor_t
*),
381 if(mul
&& in
>=alpha
){
382 int val
= rint((in
- low
) * range
);
384 if(val
>65536)val
=65536;
389 /* the data rectangle is data width/height mapped deltas. we render
390 and subsample at the same time. */
391 /* return: -1 == abort
392 0 == more work to be done in this plane
393 1 == plane fully dispatched (possibly not complete) */
395 /* enter with lock */
396 static int _sv_panel2d_resample_render_y_plane_line(sv_panel_t
*p
, _sv_bythread_cache_2d_t
*c
,
397 int plot_serialno
, int map_serialno
, int y_no
){
399 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
400 int objnum
= p2
->y_obj_to_panel
[y_no
];
401 int *in_data
= p2
->y_map
[y_no
];
402 int ph
= p2
->y
.pixels
;
405 p
->private->map_complete_count
-= ph
;
409 unsigned char *todo
= p2
->y_planetodo
[y_no
];
410 int i
= p2
->y_next_line
;
413 /* find a row that needs to be updated */
414 while(i
<ph
&& !todo
[i
]){
415 p
->private->map_complete_count
--;
420 if(i
== ph
) return 1;
424 /* row [i] needs to be updated; marshal */
425 _sv_mapping_t
*map
= p2
->mappings
+objnum
;
426 void (*mapfunc
)(int,int, _sv_lcolor_t
*) = map
->mapfunc
;
427 int ol_alpha
= rint(p2
->alphadel
[y_no
] * 16777216.f
);
428 _sv_ucolor_t
*panel
= p2
->y_planes
[y_no
];
429 int ol_low
= rint(map
->low
* 16777216.f
);
430 float ol_range
= map
->i_range
* (1.f
/256.f
);
432 int pw
= p2
->x
.pixels
;
433 int dw
= p2
->x_v
.pixels
;
434 int dh
= p2
->y_v
.pixels
;
435 _sv_ccolor_t work
[pw
];
438 panel
= p2
->y_planes
[y_no
] = calloc(pw
*ph
, sizeof(**p2
->y_planes
));
440 if(ph
!=dh
|| pw
!=dw
){
441 /* resampled row computation; may involve multiple data rows */
443 _sv_panel2d_resample_helpers_manage_y(p
);
444 _sv_panel2d_resample_helpers_manage_x(p
,c
);
446 float idel
= p2
->yscalemul
* c
->xscalemul
;
449 int ydelA
=p2
->ydelA
[i
];
450 int ydelB
=p2
->ydelB
[i
];
451 int ystart
=p2
->ynumA
[i
];
452 int yend
=p2
->ynumB
[i
];
453 int lh
= yend
- ystart
;
456 memcpy(data
,in_data
+ystart
*dw
,sizeof(data
));
460 unsigned char *xdelA
= c
->xdelA
;
461 unsigned char *xdelB
= c
->xdelB
;
462 int *xnumA
= c
->xnumA
;
463 int *xnumB
= c
->xnumB
;
468 _sv_lcolor_t out
= (_sv_lcolor_t
){0,0,0,0};
469 int xstart
= xnumA
[j
];
479 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
++], ol_alpha
, ydelA
*xA
, &out
);
481 for(; dx
< xend
-1; dx
++)
482 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
], ol_alpha
, ydelA
*17, &out
);
485 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
], ol_alpha
, ydelA
*xB
, &out
);
494 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
++], ol_alpha
, 15*xA
, &out
);
496 for(; dx
< xend
-1; dx
++)
497 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
], ol_alpha
, 255, &out
);
500 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
], ol_alpha
, 15*xB
, &out
);
508 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
++], ol_alpha
, ydelB
*xA
, &out
);
510 for(; dx
< xend
-1; dx
++)
511 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
], ol_alpha
, ydelB
*17, &out
);
514 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[dx
], ol_alpha
, ydelB
*xB
, &out
);
517 work
[j
].a
= (u_int32_t
)(out
.a
*idel
);
518 work
[j
].r
= (u_int32_t
)(out
.r
*idel
);
519 work
[j
].g
= (u_int32_t
)(out
.g
*idel
);
520 work
[j
].b
= (u_int32_t
)(out
.b
*idel
);
525 /* non-resampling render */
528 memcpy(data
,in_data
+i
*dw
,sizeof(data
));
533 _sv_lcolor_t out
= (_sv_lcolor_t
){0,0,0,0};
534 _sv_panel2d_mapping_calc(mapfunc
, ol_low
, ol_range
, data
[j
], ol_alpha
, 255, &out
);
536 work
[j
].a
= (u_int32_t
)(out
.a
);
537 work
[j
].r
= (u_int32_t
)(out
.r
);
538 work
[j
].g
= (u_int32_t
)(out
.g
);
539 work
[j
].b
= (u_int32_t
)(out
.b
);
544 if(plot_serialno
!= p
->private->plot_serialno
||
545 map_serialno
!= p
->private->map_serialno
)
547 memcpy(panel
+i
*pw
,work
,sizeof(work
));
549 p2
->y_planetodo
[y_no
][i
] = 0;
551 // must be last; it indicates completion
552 p
->private->map_complete_count
--;
556 static void render_checks(_sv_ucolor_t
*c
, int w
, int y
){
557 /* default checked background */
558 /* 16x16 'mid-checks' */
561 int phase
= (y
>>4)&1;
563 u_int32_t phaseval
= 0xff505050UL
;
564 if(phase
) phaseval
= 0xff808080UL
;
565 for(j
=0;j
<16 && x
<w
;j
++,x
++)
572 static int _sv_panel2d_render_bg_line(sv_panel_t
*p
, int plot_serialno
, int map_serialno
){
573 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
574 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
575 if(plot_serialno
!= p
->private->plot_serialno
||
576 map_serialno
!= p
->private->map_serialno
) return -1;
578 int ph
= p2
->y
.pixels
;
579 int pw
= p2
->x
.pixels
;
580 unsigned char *todo
= p2
->bg_todo
;
581 int i
= p2
->bg_next_line
,j
;
582 _sv_ucolor_t work_bg
[pw
];
583 _sv_ucolor_t work_pl
[pw
];
584 int bgmode
= p
->private->bg_type
;
586 /* find a row that needs to be updated */
587 while(i
<ph
&& !todo
[i
]){
588 p
->private->map_complete_count
--;
596 if(i
< p2
->bg_first_line
) p2
->bg_first_line
= i
;
597 if(i
+1 > p2
->bg_last_line
) p2
->bg_last_line
= i
+1;
600 /* gray background checks */
606 work_bg
[j
].u
= 0xffffffffU
;
610 work_bg
[j
].u
= 0xff000000U
;
613 render_checks(work_bg
,pw
,i
);
618 for(j
=0;j
<p
->objectives
;j
++){
619 int o_ynum
= p2
->y_obj_from_panel
[j
];
622 if(plot_serialno
!= p
->private->plot_serialno
||
623 map_serialno
!= p
->private->map_serialno
) return -1;
627 if(p2
->y_planes
[o_ynum
]){
629 _sv_ucolor_t (*mixfunc
)(_sv_ucolor_t
,_sv_ucolor_t
) = p2
->mappings
[j
].mixfunc
;
630 _sv_ucolor_t
*rect
= p2
->y_planes
[o_ynum
] + i
*pw
;
631 memcpy(work_pl
,rect
,sizeof(work_pl
));
635 work_bg
[x
] = mixfunc(work_pl
[x
],work_bg
[x
]);
641 /**** mix vector plane */
646 if(plot_serialno
!= p
->private->plot_serialno
||
647 map_serialno
!= p
->private->map_serialno
) return -1;
649 // rendered a line, get it on the screen */
651 memcpy(plot
->datarect
+pw
*i
, work_bg
, sizeof(work_bg
));
653 p
->private->map_complete_count
--;
656 if(p
->private->map_complete_count
)
657 return 1; // not done yet
662 static void _sv_panel2d_mark_map_full(sv_panel_t
*p
);
664 // enter with lock; returns zero if thread should sleep / get distracted
665 static int _sv_panel2d_remap(sv_panel_t
*p
, _sv_bythread_cache_2d_t
*thread_cache
){
666 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
667 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
669 if(!plot
) goto abort
;
670 int ph
= plot
->y
.pixels
;
671 int pw
= plot
->x
.pixels
;
673 int plot_serialno
= p
->private->plot_serialno
;
674 int map_serialno
= p
->private->map_serialno
;
676 /* brand new remap indicated by the generic progress indicator being set to 0 */
677 if(p
->private->map_progress_count
== 0){
679 p
->private->map_progress_count
= 1; // 'in progress'
680 p
->private->map_complete_count
= p2
->y_obj_num
* ph
; // count down to 0; 0 indicates completion
682 // set up Y plane rendering
683 p2
->y_next_plane
= 0;
687 p2
->bg_next_line
= 0;
688 p2
->bg_first_line
= ph
;
689 p2
->bg_last_line
= 0;
691 if(!p2
->partial_remap
)
692 _sv_panel2d_mark_map_full(p
);
695 /* by plane, by line; each plane renders independently */
698 if(p2
->y_next_plane
< p2
->y_obj_num
){
699 int status
= _sv_panel2d_resample_render_y_plane_line(p
, thread_cache
,
700 plot_serialno
, map_serialno
,
702 if(status
== -1) goto abort
;
710 p
->private->map_complete_count
= 0;
713 /* renders have been completely dispatched, but are they complete? */
714 /* the below is effectively a a thread join */
715 if(p2
->bg_next_line
== 0){
717 // join still needs to complete....
718 if(p
->private->map_complete_count
){
719 // nonzero complete count, not finished. returning zero will cause
720 // this worker thread to sleep or go on to do other things.
723 // zero complete count, the planes are done; we can begin
724 // background render. At least one thread is guaranteed to get
725 // here, which is enough; we can now wake the others [if they were
726 // asleep] and have them look for work here. */
727 p
->private->map_complete_count
= ph
; // [ph] lines to render in bg plane
728 p2
->bg_next_line
= 0;
734 /* mix new background, again line by line */
735 if(p2
->bg_next_line
< ph
){
736 int status
= _sv_panel2d_render_bg_line(p
, plot_serialno
, map_serialno
);
737 if(status
== -1) goto abort
;
738 if(p
->private->map_complete_count
)return status
;
740 return 0; // nothing left to dispatch
742 // entirely finished.
744 // remap completed; flush background to screen
745 _sv_plot_expose_request_partial (plot
,0,p2
->bg_first_line
,
746 pw
,p2
->bg_last_line
- p2
->bg_first_line
);
749 // clean bg todo list
750 memset(p2
->bg_todo
,0,ph
*sizeof(*p2
->bg_todo
));
752 // clear 'panel in progress' flag
753 p2
->partial_remap
= 0;
754 _sv_panel_clean_map(p
);
758 // reset progress to 'start over'
762 // looks like a cop-out but is actually the correct thing to do; the
763 // data *must* be WYSIWYG from panel display.
764 static void _sv_panel2d_print_bg(sv_panel_t
*p
, cairo_t
*c
){
765 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
769 cairo_pattern_t
*pattern
= cairo_pattern_create_for_surface(plot
->back
);
770 cairo_pattern_set_filter(pattern
, CAIRO_FILTER_NEAREST
);
771 cairo_set_source(c
,pattern
);
774 cairo_pattern_destroy(pattern
);
777 static void _sv_panel2d_print(sv_panel_t
*p
, cairo_t
*c
, int w
, int h
){
778 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
779 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
780 double pw
= p
->private->graph
->allocation
.width
;
781 double ph
= p
->private->graph
->allocation
.height
;
794 cairo_get_matrix(c
,&m
);
795 cairo_matrix_scale(&m
,scale
,scale
);
796 cairo_set_matrix(c
,&m
);
798 _sv_plot_print(plot
, c
, ph
*scale
, (void(*)(void *, cairo_t
*))_sv_panel2d_print_bg
, p
);
801 // find extents widths for objective scale labels
802 cairo_set_font_size(c
,10);
803 for(i
=0;i
<p
->objectives
;i
++){
804 cairo_text_extents_t ex
;
805 sv_obj_t
*o
= p
->objective_list
[i
].o
;
806 cairo_text_extents(c
, o
->name
, &ex
);
807 if(ex
.width
> maxlabelw
) maxlabelw
=ex
.width
;
813 for(i
=0;i
<p
->objectives
;i
++){
814 sv_obj_t
*o
= p
->objective_list
[i
].o
;
815 _sv_slider_t
*s
= p2
->range_scales
[i
];
818 double labelh
= _sv_slider_print_height(s
);
819 cairo_text_extents_t ex
;
820 cairo_text_extents (c
, o
->name
, &ex
);
822 int lx
= maxlabelw
- ex
.width
;
823 int ly
= labelh
/2 + ex
.height
/2;
825 // print objective labels
826 cairo_set_source_rgb(c
,0.,0.,0.);
827 cairo_move_to (c
, lx
,ly
+y
);
828 cairo_show_text (c
, o
->name
);
833 cairo_translate (c
, maxlabelw
+ 10, y
);
834 _sv_slider_print(s
, c
, pw
*scale
- maxlabelw
- 10, labelh
);
843 static void _sv_panel2d_mark_map_plane(sv_panel_t
*p
, int onum
, int y
, int z
, int v
){
844 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
845 int ph
= p2
->y
.pixels
;
847 if(y
&& p2
->y_planetodo
){
848 int y_no
= p2
->y_obj_from_panel
[onum
];
849 if(y_no
>=0 && p2
->y_planetodo
[y_no
])
850 memset(p2
->y_planetodo
[y_no
],1,ph
* sizeof(**p2
->y_planetodo
));
852 p2
->partial_remap
= 1;
856 static void _sv_panel2d_mark_map_line_y(sv_panel_t
*p
, int line
){
857 // determine all panel lines this y data line affects
858 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
859 int ph
= p2
->y
.pixels
;
860 int pw
= p2
->x
.pixels
;
861 int dw
= p2
->x_v
.pixels
;
862 int dh
= p2
->y_v
.pixels
;
865 p2
->partial_remap
= 1;
867 if(ph
!=dh
|| pw
!=dw
){
868 /* resampled row computation; may involve multiple data rows */
870 _sv_panel2d_resample_helpers_manage_y(p
);
873 if(p2
->ynumA
[i
]<=line
&&
876 for(j
=0;j
<p2
->y_obj_num
;j
++)
877 if(p2
->y_planetodo
[j
])
878 p2
->y_planetodo
[j
][i
]=1;
883 if(line
>=0 && line
<ph
)
884 for(j
=0;j
<p2
->y_obj_num
;j
++)
885 if(p2
->y_planetodo
[j
])
886 p2
->y_planetodo
[j
][line
]=1;
891 static void _sv_panel2d_mark_map_full(sv_panel_t
*p
){
892 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
893 int ph
= p2
->y
.pixels
;
896 p2
->partial_remap
= 1;
899 for(j
=0;j
<p2
->y_obj_num
;j
++){
900 if(p2
->y_planetodo
[j
]){
902 p2
->y_planetodo
[j
][i
]=1;
910 static void _sv_panel2d_update_legend(sv_panel_t
*p
){
911 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
912 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
918 _sv_plot_legend_clear(plot
);
920 // potentially add each dimension to the legend; add axis
921 // dimensions only if crosshairs are active
923 // display decimal precision relative to display scales
924 if(3-_sv_scalespace_decimal_exponent(&p2
->x
) > depth
)
925 depth
= 3-_sv_scalespace_decimal_exponent(&p2
->x
);
926 if(3-_sv_scalespace_decimal_exponent(&p2
->y
) > depth
)
927 depth
= 3-_sv_scalespace_decimal_exponent(&p2
->y
);
928 for(i
=0;i
<p
->dimensions
;i
++){
929 sv_dim_t
*d
= p
->dimension_list
[i
].d
;
930 if( (d
!=p
->private->x_d
&& d
!=p
->private->y_d
) ||
932 snprintf(buffer
,320,"%s = %+.*f",
933 p
->dimension_list
[i
].d
->legend
,
935 p
->dimension_list
[i
].d
->val
);
936 _sv_plot_legend_add(plot
,buffer
);
940 // add each active objective plane to the legend
941 // choose the value under the crosshairs
942 if(plot
->cross_active
){
944 _sv_plot_legend_add(plot
,NULL
);
946 for(i
=0;i
<p
->objectives
;i
++){
948 if(!_sv_mapping_inactive_p(p2
->mappings
+i
)){
950 _sv_panel2d_compute_point(p
,p
->objective_list
[i
].o
, plot
->selx
, plot
->sely
, &vals
);
954 snprintf(buffer
,320,"%s = %f",
955 p
->objective_list
[i
].o
->name
,
957 _sv_plot_legend_add(plot
,buffer
);
965 static void _sv_panel2d_mapchange_callback(GtkWidget
*w
,gpointer in
){
966 sv_obj_list_t
*optr
= (sv_obj_list_t
*)in
;
967 //sv_obj_t *o = optr->o;
968 sv_panel_t
*p
= optr
->p
;
969 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
970 int onum
= optr
- p
->objective_list
;
975 _sv_mapping_set_func(&p2
->mappings
[onum
],gtk_combo_box_get_active(GTK_COMBO_BOX(w
)));
977 //redraw the map slider
978 _sv_slider_set_gradient(p2
->range_scales
[onum
], &p2
->mappings
[onum
]);
980 // in the event the mapping active state changed
981 _sv_panel_dirty_legend(p
);
984 _sv_panel2d_mark_map_plane(p
,onum
,1,0,0);
985 _sv_panel_dirty_map(p
);
989 static void _sv_panel2d_map_callback(void *in
,int buttonstate
){
990 sv_obj_list_t
*optr
= (sv_obj_list_t
*)in
;
991 //sv_obj_t *o = optr->o;
992 sv_panel_t
*p
= optr
->p
;
993 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
994 int onum
= optr
- p
->objective_list
;
996 if(buttonstate
== 0){
1001 // recache alpha del */
1002 p2
->alphadel
[onum
] =
1003 _sv_slider_val_to_del(p2
->range_scales
[onum
],
1004 _sv_slider_get_value(p2
->range_scales
[onum
],1));
1006 // redraw the plot on motion
1007 if(buttonstate
== 1){
1008 _sv_panel2d_mark_map_plane(p
,onum
,1,0,0);
1009 _sv_panel_dirty_map(p
);
1011 if(buttonstate
== 2)
1015 static void _sv_panel2d_update_xysel(sv_panel_t
*p
){
1016 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1018 // update which x/y buttons are pressable */
1019 // enable/disable dimension slider thumbs
1021 for(i
=0;i
<p
->dimensions
;i
++){
1023 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2
->dim_xb
[i
]))){
1024 // make the y insensitive
1026 _gtk_widget_set_sensitive_fixup(p2
->dim_yb
[i
],FALSE
);
1028 // set the x dim flag
1029 p
->private->x_d
= p
->dimension_list
[i
].d
;
1030 p2
->x_scale
= p
->private->dim_scales
[i
];
1033 // if there is a y, make it sensitive
1035 _gtk_widget_set_sensitive_fixup(p2
->dim_yb
[i
],TRUE
);
1038 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2
->dim_yb
[i
]))){
1039 // make the x insensitive
1041 _gtk_widget_set_sensitive_fixup(p2
->dim_xb
[i
],FALSE
);
1044 p
->private->y_d
= p
->dimension_list
[i
].d
;
1045 p2
->y_scale
= p
->private->dim_scales
[i
];
1048 // if there is a x, make it sensitive
1050 _gtk_widget_set_sensitive_fixup(p2
->dim_xb
[i
],TRUE
);
1052 if((p2
->dim_xb
[i
] &&
1053 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2
->dim_xb
[i
]))) ||
1055 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2
->dim_yb
[i
])))){
1056 // make all thumbs visible
1057 _sv_dim_widget_set_thumb_active(p
->private->dim_scales
[i
],0,1);
1058 _sv_dim_widget_set_thumb_active(p
->private->dim_scales
[i
],2,1);
1060 // make bracket thumbs invisible */
1061 _sv_dim_widget_set_thumb_active(p
->private->dim_scales
[i
],0,0);
1062 _sv_dim_widget_set_thumb_active(p
->private->dim_scales
[i
],2,0);
1067 static int _v_swizzle(int y
, int height
){
1068 int yy
= height
>> 5;
1073 yy
= (height
+16) >> 5;
1078 yy
= (height
+8) >> 4;
1083 yy
= (height
+4) >> 3;
1088 yy
= (height
+2) >> 2;
1096 // assumes data is locked
1097 static void _sv_panel2d_fast_scale_x(_sv_spinner_t
*sp
,
1101 _sv_scalespace_t
new,
1102 _sv_scalespace_t old
){
1107 double old_w
= old
.pixels
;
1108 double new_w
= new.pixels
;
1110 double old_lo
= _sv_scalespace_value(&old
,0);
1111 double old_hi
= _sv_scalespace_value(&old
,old_w
);
1112 double new_lo
= _sv_scalespace_value(&new,0);
1113 double new_hi
= _sv_scalespace_value(&new,new_w
);
1114 double newscale
= (new_hi
-new_lo
)/new_w
;
1115 double oldscale
= old_w
/(old_hi
-old_lo
);
1117 double xval
= (x
)*newscale
+new_lo
;
1118 double map
= ((xval
-old_lo
)*oldscale
);
1119 int base
= (int)floor(map
);
1120 int del
= rint((map
- floor(map
))*64.f
);
1121 /* hack to overwhelm roundoff error; this is inside a purely
1122 temporary cosmetic approximation anyway*/
1123 if(base
>0 && del
==0){
1133 int *data_line
= data
+y
*w
;
1134 _sv_spinner_set_busy(sp
);
1136 if(mapbase
[x
]<0 || mapbase
[x
]>=(w
-1)){
1139 int base
= mapbase
[x
];
1140 int A
= data_line
[base
];
1141 int B
= data_line
[base
+1];
1145 work
[x
]= A
+ (((B
- A
)*mapdel
[x
])>>6);
1149 memcpy(data_line
,work
,w
*(sizeof(*work
)));
1153 static void _sv_panel2d_fast_scale_y(_sv_spinner_t
*sp
,
1158 _sv_scalespace_t
new,
1159 _sv_scalespace_t old
){
1161 int w
= (oldw
<neww
?oldw
:neww
);
1163 int old_h
= old
.pixels
;
1164 int new_h
= new.pixels
;
1169 double old_lo
= _sv_scalespace_value(&old
,0);
1170 double old_hi
= _sv_scalespace_value(&old
,(double)old_h
);
1171 double new_lo
= _sv_scalespace_value(&new,0);
1172 double new_hi
= _sv_scalespace_value(&new,(double)new_h
);
1173 double newscale
= (new_hi
-new_lo
)/new_h
;
1174 double oldscale
= old_h
/(old_hi
-old_lo
);
1176 for(y
=0;y
<new_h
;y
++){
1177 double yval
= (y
)*newscale
+new_lo
;
1178 double map
= ((yval
-old_lo
)*oldscale
);
1179 int base
= (int)floor(map
);
1180 int del
= rint((map
- floor(map
))*64.);
1181 /* hack to overwhelm roundoff error; this is inside a purely
1182 temporary cosmetic approximation anyway */
1183 if(base
>0 && del
==0){
1193 for(y
=0;y
<new_h
;y
++){
1194 int base
= mapbase
[y
];
1195 int *new_column
= &newdata
[y
*neww
];
1196 _sv_spinner_set_busy(sp
);
1198 if(base
<0 || base
>=(old_h
-1)){
1202 int del
= mapdel
[y
];
1203 int *old_column
= &olddata
[base
*oldw
];
1206 int A
= old_column
[x
];
1207 int B
= old_column
[x
+oldw
];
1211 new_column
[x
]= A
+ (((B
-A
)*del
)>>6);
1217 static void _sv_panel2d_fast_scale(_sv_spinner_t
*sp
,
1219 _sv_scalespace_t xnew
,
1220 _sv_scalespace_t ynew
,
1222 _sv_scalespace_t xold
,
1223 _sv_scalespace_t yold
){
1225 int new_w
= xnew
.pixels
;
1226 int new_h
= ynew
.pixels
;
1227 int old_w
= xold
.pixels
;
1228 int old_h
= yold
.pixels
;
1231 _sv_panel2d_fast_scale_y(sp
,olddata
,newdata
,old_w
,new_w
,ynew
,yold
);
1232 _sv_panel2d_fast_scale_x(sp
,newdata
,new_w
,new_h
,xnew
,xold
);
1234 _sv_panel2d_fast_scale_x(sp
,olddata
,old_w
,old_h
,xnew
,xold
);
1235 _sv_panel2d_fast_scale_y(sp
,olddata
,newdata
,old_w
,new_w
,ynew
,yold
);
1239 // call only from main gtk thread
1240 static void _sv_panel2d_mark_recompute(sv_panel_t
*p
){
1241 if(!p
->private->realized
) return;
1242 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
1244 if(plot
&& GTK_WIDGET_REALIZED(GTK_WIDGET(plot
))){
1245 _sv_panel_dirty_plot(p
);
1249 static void _sv_panel2d_update_crosshairs(sv_panel_t
*p
){
1250 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
1254 for(i
=0;i
<p
->dimensions
;i
++){
1255 sv_dim_t
*d
= p
->dimension_list
[i
].d
;
1256 if(d
== p
->private->x_d
)
1258 if(d
== p
->private->y_d
)
1263 _sv_plot_set_crosshairs(plot
,x
,y
);
1264 _sv_panel_dirty_legend(p
);
1267 static void _sv_panel2d_center_callback(sv_dim_list_t
*dptr
){
1268 sv_dim_t
*d
= dptr
->d
;
1269 sv_panel_t
*p
= dptr
->p
;
1270 int axisp
= (d
== p
->private->x_d
|| d
== p
->private->y_d
);
1273 // mid slider of a non-axis dimension changed, rerender
1274 _sv_panel2d_mark_recompute(p
);
1276 // mid slider of an axis dimension changed, move crosshairs
1277 _sv_panel2d_update_crosshairs(p
);
1281 static void _sv_panel2d_bracket_callback(sv_dim_list_t
*dptr
){
1282 sv_dim_t
*d
= dptr
->d
;
1283 sv_panel_t
*p
= dptr
->p
;
1284 int axisp
= (d
== p
->private->x_d
|| d
== p
->private->y_d
);
1287 _sv_panel2d_mark_recompute(p
);
1291 static void _sv_panel2d_dimchange_callback(GtkWidget
*button
,gpointer in
){
1292 sv_panel_t
*p
= (sv_panel_t
*)in
;
1294 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button
))){
1299 _sv_plot_unset_box(PLOT(p
->private->graph
));
1300 _sv_panel2d_update_xysel(p
);
1302 _sv_panel2d_clear_pane(p
);
1303 _sv_panel2d_mark_recompute(p
);
1304 _sv_panel2d_update_crosshairs(p
);
1310 static void _sv_panel2d_crosshairs_callback(sv_panel_t
*p
){
1311 double x
=PLOT(p
->private->graph
)->selx
;
1312 double y
=PLOT(p
->private->graph
)->sely
;
1318 //plot_snap_crosshairs(PLOT(p->private->graph));
1320 for(i
=0;i
<p
->dimensions
;i
++){
1321 sv_dim_t
*d
= p
->dimension_list
[i
].d
;
1322 if(d
== p
->private->x_d
){
1323 _sv_dim_widget_set_thumb(p
->private->dim_scales
[i
],1,x
);
1326 if(d
== p
->private->y_d
){
1327 _sv_dim_widget_set_thumb(p
->private->dim_scales
[i
],1,y
);
1330 p
->private->oldbox_active
= 0;
1333 // dimension setting might have enforced granularity restrictions;
1334 // have the display reflect that
1335 x
= p
->private->x_d
->val
;
1336 y
= p
->private->y_d
->val
;
1338 _sv_plot_set_crosshairs(PLOT(p
->private->graph
),x
,y
);
1340 _sv_panel_dirty_legend(p
);
1344 static void _sv_panel2d_box_callback(void *in
, int state
){
1345 sv_panel_t
*p
= (sv_panel_t
*)in
;
1346 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1347 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
1352 _sv_plot_box_vals(plot
,p2
->oldbox
);
1353 p
->private->oldbox_active
= plot
->box_active
;
1355 case 1: // box activate
1359 _sv_panel2d_crosshairs_callback(p
);
1361 _sv_dim_widget_set_thumb(p2
->x_scale
,0,p2
->oldbox
[0]);
1362 _sv_dim_widget_set_thumb(p2
->x_scale
,2,p2
->oldbox
[1]);
1363 _sv_dim_widget_set_thumb(p2
->y_scale
,0,p2
->oldbox
[2]);
1364 _sv_dim_widget_set_thumb(p2
->y_scale
,2,p2
->oldbox
[3]);
1365 p
->private->oldbox_active
= 0;
1369 _sv_panel_update_menus(p
);
1372 void _sv_panel2d_maintain_cache(sv_panel_t
*p
, _sv_bythread_cache_2d_t
*c
, int w
){
1373 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1375 /* toplevel initialization */
1379 /* allocate output temporary buffer */
1380 for(i
=0;i
<p2
->used_functions
;i
++){
1381 int fnum
= p2
->used_function_list
[i
]->number
;
1382 sv_func_t
*f
= _sv_function_list
[fnum
];
1383 count
+= f
->outputs
;
1385 c
->fout
= calloc(count
, sizeof(*c
->fout
));
1387 /* objective line buffer index */
1388 c
->y_map
= calloc(p2
->y_obj_num
,sizeof(*c
->y_map
));
1389 for(i
=0;i
<p2
->y_obj_num
;i
++)
1390 c
->y_map
[i
] = calloc(w
,sizeof(**c
->y_map
));
1391 c
->storage_width
= w
;
1394 /* anytime the data width changes */
1395 if(c
->storage_width
!= w
){
1397 c
->storage_width
= w
;
1399 for(i
=0;i
<p2
->y_obj_num
;i
++)
1400 c
->y_map
[i
] = realloc(c
->y_map
[i
],w
*sizeof(**c
->y_map
));
1406 // subtype entry point for plot remaps; lock held
1407 static int _sv_panel2d_map_redraw(sv_panel_t
*p
, _sv_bythread_cache_t
*c
){
1408 return _sv_panel2d_remap(p
,&c
->p2
);
1411 // subtype entry point for legend redraws; lock held
1412 static int _sv_panel2d_legend_redraw(sv_panel_t
*p
){
1413 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
1415 if(p
->private->legend_progress_count
)return 0;
1416 p
->private->legend_progress_count
++;
1417 _sv_panel2d_update_legend(p
);
1418 _sv_panel_clean_legend(p
);
1421 _sv_plot_draw_scales(plot
);
1424 _sv_plot_expose_request(plot
);
1428 // subtype entry point for recomputation; lock held
1429 static int _sv_panel2d_compute(sv_panel_t
*p
,
1430 _sv_bythread_cache_t
*c
){
1432 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1435 int pw
,ph
,dw
,dh
,i
,d
;
1437 double x_min
, x_max
;
1438 double y_min
, y_max
;
1440 _sv_scalespace_t sx
,sx_v
,sx_i
;
1441 _sv_scalespace_t sy
,sy_v
,sy_i
;
1443 plot
= PLOT(p
->private->graph
);
1444 pw
= plot
->x
.pixels
;
1445 ph
= plot
->y
.pixels
;
1447 x_d
= p
->private->x_d
->number
;
1448 y_d
= p
->private->y_d
->number
;
1450 // beginning of computation init
1451 if(p
->private->plot_progress_count
==0){
1454 _sv_scalespace_t old_x
= p2
->x
;
1455 _sv_scalespace_t old_y
= p2
->y
;
1456 _sv_scalespace_t old_xv
= p2
->x_v
;
1457 _sv_scalespace_t old_yv
= p2
->y_v
;
1459 // generate new scales
1460 _sv_dim_scales(p
->private->x_d
,
1461 p
->private->x_d
->bracket
[0],
1462 p
->private->x_d
->bracket
[1],
1463 pw
,pw
* p
->private->oversample_n
/ p
->private->oversample_d
,
1465 p
->private->x_d
->legend
,
1469 _sv_dim_scales(p
->private->y_d
,
1470 p
->private->y_d
->bracket
[1],
1471 p
->private->y_d
->bracket
[0],
1472 ph
,ph
* p
->private->oversample_n
/ p
->private->oversample_d
,
1474 p
->private->y_d
->legend
,
1491 p
->private->plot_progress_count
++;
1492 p
->private->plot_serialno
++; // we're about to free the old data rectangles
1494 // realloc/fast scale the current data contents if appropriate
1495 if(memcmp(&sx_v
,&old_xv
,sizeof(sx_v
)) || memcmp(&sy_v
,&old_yv
,sizeof(sy_v
))){
1497 // maintain data planes
1498 for(i
=0;i
<p2
->y_obj_num
;i
++){
1499 // allocate new storage
1500 int *newmap
= calloc(sx_v
.pixels
*sy_v
.pixels
,sizeof(*newmap
));
1501 int *oldmap
= p2
->y_map
[i
];
1504 for(j
=0;j
<sx_v
.pixels
*sy_v
.pixels
;j
++)
1507 // zoom scale data in map planes as placeholder for render
1509 _sv_panel2d_fast_scale(p
->private->spinner
,newmap
, sx_v
, sy_v
,
1510 oldmap
,old_xv
, old_yv
);
1513 p2
->y_map
[i
] = newmap
;
1518 // realloc render planes if appropriate
1519 if(memcmp(&sx
,&old_x
,sizeof(sx
)) || memcmp(&sy
,&old_y
,sizeof(sy
))){
1520 for(i
=0;i
<p2
->y_obj_num
;i
++){
1524 free(p2
->y_planes
[i
]);
1525 p2
->y_planes
[i
] = calloc(sx
.pixels
*sy
.pixels
,sizeof(**p2
->y_planes
));
1528 if(p2
->y_planetodo
[i
])
1529 free(p2
->y_planetodo
[i
]);
1530 p2
->y_planetodo
[i
] = calloc(sy
.pixels
,sizeof(**p2
->y_planetodo
));
1536 p2
->bg_todo
=calloc(ph
,sizeof(*p2
->bg_todo
));
1542 _sv_panel2d_mark_map_full(p
);
1543 _sv_panel_dirty_map(p
);
1546 _sv_plot_draw_scales(plot
); // this should happen outside lock
1550 _sv_map_set_throttle_time(p
); // swallow the first 'throttled' remap which would only be a single line;
1560 serialno
= p
->private->plot_serialno
;
1566 if(p
->private->plot_progress_count
>dh
) return 0;
1568 _sv_panel2d_maintain_cache(p
,&c
->p2
,dw
);
1572 /* render using local dimension array; several threads will be
1573 computing objectives */
1574 double dim_vals
[_sv_dimensions
];
1575 int y
= _v_swizzle(p
->private->plot_progress_count
-1,dh
);
1576 p
->private->plot_progress_count
++;
1578 x_min
= _sv_scalespace_value(&p2
->x_i
,0);
1579 x_max
= _sv_scalespace_value(&p2
->x_i
,dw
);
1581 y_min
= _sv_scalespace_value(&p2
->y_i
,0);
1582 y_max
= _sv_scalespace_value(&p2
->y_i
,dh
);
1584 // Initialize local dimension value array
1585 for(i
=0;i
<_sv_dimensions
;i
++){
1586 sv_dim_t
*dim
= _sv_dimension_list
[i
];
1587 dim_vals
[i
]=dim
->val
;
1590 /* unlock for computation */
1593 dim_vals
[y_d
]=_sv_scalespace_value(&sy_i
, y
);
1594 _sv_panel2d_compute_line(p
, serialno
, dw
, y
, x_d
, sx_i
, dim_vals
, &c
->p2
);
1598 if(p
->private->plot_serialno
== serialno
){
1599 p
->private->plot_complete_count
++;
1600 _sv_panel2d_mark_map_line_y(p
,y
);
1601 if(p
->private->plot_complete_count
>=dh
){
1602 _sv_panel_dirty_map(p
);
1603 _sv_panel_dirty_legend(p
);
1604 _sv_panel_clean_plot(p
);
1606 _sv_panel_dirty_map_throttled(p
);
1612 // only called for resize events
1613 static void _sv_panel2d_recompute_callback(void *ptr
){
1614 sv_panel_t
*p
= (sv_panel_t
*)ptr
;
1618 _sv_panel2d_mark_recompute(p
);
1619 _sv_panel2d_compute(p
,NULL
); // initial scale setup
1621 // temporary: blank background to checks
1622 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
1623 int pw
= plot
->x
.pixels
;
1624 int ph
= plot
->y
.pixels
;
1626 render_checks((_sv_ucolor_t
*)plot
->datarect
+pw
*i
, pw
, i
);
1631 static void _sv_panel2d_undo_log(_sv_panel_undo_t
*u
, sv_panel_t
*p
){
1632 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1635 // alloc fields as necessary
1638 u
->mappings
= calloc(p
->objectives
,sizeof(*u
->mappings
));
1639 if(!u
->scale_vals
[0])
1640 u
->scale_vals
[0] = calloc(p
->objectives
,sizeof(**u
->scale_vals
));
1641 if(!u
->scale_vals
[1])
1642 u
->scale_vals
[1] = calloc(p
->objectives
,sizeof(**u
->scale_vals
));
1643 if(!u
->scale_vals
[2])
1644 u
->scale_vals
[2] = calloc(p
->objectives
,sizeof(**u
->scale_vals
));
1647 for(i
=0;i
<p
->objectives
;i
++){
1648 u
->mappings
[i
] = p2
->mappings
[i
].mapnum
;
1649 u
->scale_vals
[0][i
] = _sv_slider_get_value(p2
->range_scales
[i
],0);
1650 u
->scale_vals
[1][i
] = _sv_slider_get_value(p2
->range_scales
[i
],1);
1651 u
->scale_vals
[2][i
] = _sv_slider_get_value(p2
->range_scales
[i
],2);
1654 u
->x_d
= p2
->x_dnum
;
1655 u
->y_d
= p2
->y_dnum
;
1656 u
->box
[0] = p2
->oldbox
[0];
1657 u
->box
[1] = p2
->oldbox
[1];
1658 u
->box
[2] = p2
->oldbox
[2];
1659 u
->box
[3] = p2
->oldbox
[3];
1660 u
->box_active
= p
->private->oldbox_active
;
1663 static void _sv_panel2d_undo_restore(_sv_panel_undo_t
*u
, sv_panel_t
*p
){
1664 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1665 _sv_plot_t
*plot
= PLOT(p
->private->graph
);
1668 // go in through widgets
1669 for(i
=0;i
<p
->objectives
;i
++){
1670 gtk_combo_box_set_active(GTK_COMBO_BOX(p2
->range_pulldowns
[i
]),u
->mappings
[i
]);
1671 _sv_slider_set_value(p2
->range_scales
[i
],0,u
->scale_vals
[0][i
]);
1672 _sv_slider_set_value(p2
->range_scales
[i
],1,u
->scale_vals
[1][i
]);
1673 _sv_slider_set_value(p2
->range_scales
[i
],2,u
->scale_vals
[2][i
]);
1676 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2
->dim_xb
[u
->x_d
]),TRUE
);
1677 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2
->dim_yb
[u
->y_d
]),TRUE
);
1679 _sv_panel2d_update_xysel(p
);
1682 p2
->oldbox
[0] = u
->box
[0];
1683 p2
->oldbox
[1] = u
->box
[1];
1684 p2
->oldbox
[2] = u
->box
[2];
1685 p2
->oldbox
[3] = u
->box
[3];
1686 _sv_plot_box_set(plot
,u
->box
);
1687 p
->private->oldbox_active
= 1;
1689 _sv_plot_unset_box(plot
);
1690 p
->private->oldbox_active
= 0;
1694 static void _sv_panel2d_realize(sv_panel_t
*p
){
1695 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1700 p
->private->toplevel
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
1701 g_signal_connect_swapped (G_OBJECT (p
->private->toplevel
), "delete-event",
1702 G_CALLBACK (_sv_clean_exit
), (void *)SIGINT
);
1704 // add border to sides with hbox/padding
1705 GtkWidget
*borderbox
= gtk_hbox_new(0,0);
1706 gtk_container_add (GTK_CONTAINER (p
->private->toplevel
), borderbox
);
1709 p
->private->topbox
= gtk_vbox_new(0,0);
1710 gtk_box_pack_start(GTK_BOX(borderbox
), p
->private->topbox
, 1,1,4);
1711 gtk_container_set_border_width (GTK_CONTAINER (p
->private->toplevel
), 1);
1713 /* spinner, top bar */
1715 GtkWidget
*hbox
= gtk_hbox_new(0,0);
1716 gtk_box_pack_start(GTK_BOX(p
->private->topbox
), hbox
, 0,0,0);
1717 gtk_box_pack_end(GTK_BOX(hbox
),GTK_WIDGET(p
->private->spinner
),0,0,0);
1720 /* plotbox, graph */
1722 p
->private->graph
= GTK_WIDGET(_sv_plot_new(_sv_panel2d_recompute_callback
,p
,
1723 (void *)(void *)_sv_panel2d_crosshairs_callback
,p
,
1724 _sv_panel2d_box_callback
,p
,0));
1725 p
->private->plotbox
= p
->private->graph
;
1726 gtk_box_pack_start(GTK_BOX(p
->private->topbox
), p
->private->plotbox
, 1,1,2);
1731 p2
->obj_table
= gtk_table_new(p
->objectives
, 5, 0);
1732 gtk_box_pack_start(GTK_BOX(p
->private->topbox
), p2
->obj_table
, 0,0,1);
1734 /* objective sliders */
1735 p2
->range_scales
= calloc(p
->objectives
,sizeof(*p2
->range_scales
));
1736 p2
->range_pulldowns
= calloc(p
->objectives
,sizeof(*p2
->range_pulldowns
));
1737 p2
->alphadel
= calloc(p
->objectives
,sizeof(*p2
->alphadel
));
1738 p2
->mappings
= calloc(p
->objectives
,sizeof(*p2
->mappings
));
1739 for(i
=0;i
<p
->objectives
;i
++){
1740 GtkWidget
**sl
= calloc(3,sizeof(*sl
));
1741 sv_obj_t
*o
= p
->objective_list
[i
].o
;
1742 int lo
= o
->scale
->val_list
[0];
1743 int hi
= o
->scale
->val_list
[o
->scale
->vals
-1];
1746 GtkWidget
*label
= gtk_label_new(o
->name
);
1747 gtk_misc_set_alignment(GTK_MISC(label
),1.,.5);
1748 gtk_table_attach(GTK_TABLE(p2
->obj_table
),label
,0,1,i
,i
+1,
1751 /* mapping pulldown */
1753 GtkWidget
*menu
=_gtk_combo_box_new_markup();
1755 for(j
=0;j
<_sv_mapping_names();j
++)
1756 gtk_combo_box_append_text (GTK_COMBO_BOX (menu
), _sv_mapping_name(j
));
1757 gtk_combo_box_set_active(GTK_COMBO_BOX(menu
),0);
1758 g_signal_connect (G_OBJECT (menu
), "changed",
1759 G_CALLBACK (_sv_panel2d_mapchange_callback
), p
->objective_list
+i
);
1760 gtk_table_attach(GTK_TABLE(p2
->obj_table
),menu
,4,5,i
,i
+1,
1761 GTK_SHRINK
,GTK_SHRINK
,0,0);
1762 p2
->range_pulldowns
[i
] = menu
;
1765 /* the range mapping slices/slider */
1766 sl
[0] = _sv_slice_new(_sv_panel2d_map_callback
,p
->objective_list
+i
);
1767 sl
[1] = _sv_slice_new(_sv_panel2d_map_callback
,p
->objective_list
+i
);
1768 sl
[2] = _sv_slice_new(_sv_panel2d_map_callback
,p
->objective_list
+i
);
1770 gtk_table_attach(GTK_TABLE(p2
->obj_table
),sl
[0],1,2,i
,i
+1,
1771 GTK_EXPAND
|GTK_FILL
,0,0,0);
1772 gtk_table_attach(GTK_TABLE(p2
->obj_table
),sl
[1],2,3,i
,i
+1,
1773 GTK_EXPAND
|GTK_FILL
,0,0,0);
1774 gtk_table_attach(GTK_TABLE(p2
->obj_table
),sl
[2],3,4,i
,i
+1,
1775 GTK_EXPAND
|GTK_FILL
,0,0,0);
1776 p2
->range_scales
[i
] = _sv_slider_new((_sv_slice_t
**)sl
,3,o
->scale
->label_list
,o
->scale
->val_list
,
1777 o
->scale
->vals
,_SV_SLIDER_FLAG_INDEPENDENT_MIDDLE
);
1778 gtk_table_set_col_spacing(GTK_TABLE(p2
->obj_table
),3,5);
1780 _sv_slice_thumb_set((_sv_slice_t
*)sl
[0],lo
);
1781 _sv_slice_thumb_set((_sv_slice_t
*)sl
[1],lo
);
1782 _sv_slice_thumb_set((_sv_slice_t
*)sl
[2],hi
);
1783 _sv_mapping_setup(&p2
->mappings
[i
],0.,1.,0);
1784 _sv_slider_set_gradient(p2
->range_scales
[i
], &p2
->mappings
[i
]);
1790 p2
->dim_table
= gtk_table_new(p
->dimensions
,4,0);
1791 gtk_box_pack_start(GTK_BOX(p
->private->topbox
), p2
->dim_table
, 0,0,4);
1793 GtkWidget
*first_x
= NULL
;
1794 GtkWidget
*first_y
= NULL
;
1795 GtkWidget
*pressed_y
= NULL
;
1796 p
->private->dim_scales
= calloc(p
->dimensions
,sizeof(*p
->private->dim_scales
));
1797 p2
->dim_xb
= calloc(p
->dimensions
,sizeof(*p2
->dim_xb
));
1798 p2
->dim_yb
= calloc(p
->dimensions
,sizeof(*p2
->dim_yb
));
1800 for(i
=0;i
<p
->dimensions
;i
++){
1801 sv_dim_t
*d
= p
->dimension_list
[i
].d
;
1804 GtkWidget
*label
= gtk_label_new(d
->legend
);
1805 gtk_misc_set_alignment(GTK_MISC(label
),1.,.5);
1806 gtk_table_attach(GTK_TABLE(p2
->dim_table
),label
,0,1,i
,i
+1,
1809 /* x/y radio buttons */
1810 if(!(d
->flags
& SV_DIM_NO_X
)){
1812 p2
->dim_xb
[i
] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_x
),"X");
1814 first_x
= p2
->dim_xb
[i
] = gtk_radio_button_new_with_label(NULL
,"X");
1815 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2
->dim_xb
[i
]),TRUE
);
1817 gtk_table_attach(GTK_TABLE(p2
->dim_table
),p2
->dim_xb
[i
],1,2,i
,i
+1,
1821 if(!(d
->flags
& SV_DIM_NO_Y
)){
1823 p2
->dim_yb
[i
] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_y
),"Y");
1825 first_y
= p2
->dim_yb
[i
] = gtk_radio_button_new_with_label(NULL
,"Y");
1826 if(!pressed_y
&& p2
->dim_xb
[i
]!=first_x
){
1827 pressed_y
= p2
->dim_yb
[i
];
1828 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2
->dim_yb
[i
]),TRUE
);
1830 gtk_table_attach(GTK_TABLE(p2
->dim_table
),p2
->dim_yb
[i
],2,3,i
,i
+1,
1834 p
->private->dim_scales
[i
] =
1835 _sv_dim_widget_new(p
->dimension_list
+i
,_sv_panel2d_center_callback
,_sv_panel2d_bracket_callback
);
1837 gtk_table_attach(GTK_TABLE(p2
->dim_table
),
1838 p
->private->dim_scales
[i
]->t
,
1840 GTK_EXPAND
|GTK_FILL
,0,0,0);
1843 for(i
=0;i
<p
->dimensions
;i
++){
1845 g_signal_connect (G_OBJECT (p2
->dim_xb
[i
]), "toggled",
1846 G_CALLBACK (_sv_panel2d_dimchange_callback
), p
);
1848 g_signal_connect (G_OBJECT (p2
->dim_yb
[i
]), "toggled",
1849 G_CALLBACK (_sv_panel2d_dimchange_callback
), p
);
1853 _sv_panel2d_update_xysel(p
);
1855 gtk_widget_realize(p
->private->toplevel
);
1856 gtk_widget_realize(p
->private->graph
);
1857 gtk_widget_realize(GTK_WIDGET(p
->private->spinner
));
1858 gtk_widget_show_all(p
->private->toplevel
);
1859 _sv_panel2d_update_xysel(p
); // yes, this was already done; however,
1860 // gtk clobbered the event setup on the
1861 // insensitive buttons when it realized
1862 // them. This call will restore them.
1867 static int _sv_panel2d_save(sv_panel_t
*p
, xmlNodePtr pn
){
1868 _sv_panel2d_t
*p2
= p
->subtype
->p2
;
1873 xmlNewProp(pn
, (xmlChar
*)"type", (xmlChar
*)"2d");
1876 if(p
->private->oldbox_active
){
1877 xmlNodePtr boxn
= xmlNewChild(pn
, NULL
, (xmlChar
*) "box", NULL
);
1878 _xmlNewPropF(boxn
, "x1", p2
->oldbox
[0]);
1879 _xmlNewPropF(boxn
, "x2", p2
->oldbox
[1]);
1880 _xmlNewPropF(boxn
, "y1", p2
->oldbox
[2]);
1881 _xmlNewPropF(boxn
, "y2", p2
->oldbox
[3]);
1884 // objective map settings
1885 for(i
=0;i
<p
->objectives
;i
++){
1886 sv_obj_t
*o
= p
->objective_list
[i
].o
;
1887 xmlNodePtr on
= xmlNewChild(pn
, NULL
, (xmlChar
*) "objective", NULL
);
1888 _xmlNewPropI(on
, "position", i
);
1889 _xmlNewPropI(on
, "number", o
->number
);
1890 _xmlNewPropS(on
, "name", o
->name
);
1891 _xmlNewPropS(on
, "type", o
->output_types
);
1893 // right now Y is the only type; the below is Y-specific
1894 n
= xmlNewChild(on
, NULL
, (xmlChar
*) "y-map", NULL
);
1895 _xmlNewPropS(n
, "color", _sv_mapping_name(p2
->mappings
[i
].mapnum
));
1896 _xmlNewPropF(n
, "low-bracket", _sv_slider_get_value(p2
->range_scales
[i
],0));
1897 _xmlNewPropF(n
, "alpha", _sv_slider_get_value(p2
->range_scales
[i
],1));
1898 _xmlNewPropF(n
, "high-bracket", _sv_slider_get_value(p2
->range_scales
[i
],2));
1901 // x/y dim selection
1902 n
= xmlNewChild(pn
, NULL
, (xmlChar
*) "axes", NULL
);
1903 _xmlNewPropI(n
, "xpos", p2
->x_dnum
);
1904 _xmlNewPropI(n
, "ypos", p2
->y_dnum
);
1909 int _sv_panel2d_load(sv_panel_t
*p
,
1910 _sv_panel_undo_t
*u
,
1916 _xmlCheckPropS(pn
,"type","2d", "Panel %d type mismatch in save file.",p
->number
,&warn
);
1920 _xmlGetChildPropFPreserve(pn
, "box", "x1", &u
->box
[0]);
1921 _xmlGetChildPropFPreserve(pn
, "box", "x2", &u
->box
[1]);
1922 _xmlGetChildPropFPreserve(pn
, "box", "y1", &u
->box
[2]);
1923 _xmlGetChildPropFPreserve(pn
, "box", "y2", &u
->box
[3]);
1925 xmlNodePtr n
= _xmlGetChildS(pn
, "box", NULL
, NULL
);
1931 // objective map settings
1932 for(i
=0;i
<p
->objectives
;i
++){
1933 sv_obj_t
*o
= p
->objective_list
[i
].o
;
1934 xmlNodePtr on
= _xmlGetChildI(pn
, "objective", "position", i
);
1936 _sv_first_load_warning(&warn
);
1937 fprintf(stderr
,"No save data found for panel %d objective \"%s\".\n",p
->number
, o
->name
);
1940 _xmlCheckPropS(on
,"name",o
->name
, "Objectve position %d name mismatch in save file.",i
,&warn
);
1941 _xmlCheckPropS(on
,"type",o
->output_types
, "Objectve position %d type mismatch in save file.",i
,&warn
);
1943 // right now Y is the only type; the below is Y-specific
1944 // load maptype, values
1945 _xmlGetChildPropFPreserve(on
, "y-map", "low-bracket", &u
->scale_vals
[0][i
]);
1946 _xmlGetChildPropFPreserve(on
, "y-map", "alpha", &u
->scale_vals
[1][i
]);
1947 _xmlGetChildPropFPreserve(on
, "y-map", "high-bracket", &u
->scale_vals
[2][i
]);
1948 _xmlGetChildMap(on
, "y-map", "color", _sv_mapping_map(), &u
->mappings
[i
],
1949 "Panel %d objective unknown mapping setting", p
->number
, &warn
);
1955 // x/y dim selection
1956 _xmlGetChildPropIPreserve(pn
, "axes", "xpos", &u
->x_d
);
1957 _xmlGetChildPropI(pn
, "axes", "ypos", &u
->y_d
);
1962 sv_panel_t
*sv_panel_new_2d(int number
,
1964 char *objectivelist
,
1965 char *dimensionlist
,
1969 sv_panel_t
*p
= _sv_panel_new(number
,name
,objectivelist
,dimensionlist
,flags
);
1972 _sv_panel2d_t
*p2
= calloc(1, sizeof(*p2
));
1973 int fout_offsets
[_sv_functions
];
1976 calloc(1, sizeof(*p
->subtype
)); /* the union is alloced not
1977 embedded as its internal
1978 structure must be hidden */
1979 p
->subtype
->p2
= p2
;
1980 p
->type
= SV_PANEL_2D
;
1981 p
->private->bg_type
= SV_BG_CHECKS
;
1983 // verify all the objectives have scales
1984 for(i
=0;i
<p
->objectives
;i
++){
1985 if(!p
->objective_list
[i
].o
->scale
){
1986 fprintf(stderr
,"All objectives in a 2d panel must have a scale\n");
1992 p
->private->realize
= _sv_panel2d_realize
;
1993 p
->private->map_action
= _sv_panel2d_map_redraw
;
1994 p
->private->legend_action
= _sv_panel2d_legend_redraw
;
1995 p
->private->compute_action
= _sv_panel2d_compute
;
1996 p
->private->request_compute
= _sv_panel2d_mark_recompute
;
1997 p
->private->crosshair_action
= _sv_panel2d_crosshairs_callback
;
1998 p
->private->print_action
= _sv_panel2d_print
;
1999 p
->private->undo_log
= _sv_panel2d_undo_log
;
2000 p
->private->undo_restore
= _sv_panel2d_undo_restore
;
2001 p
->private->save_action
= _sv_panel2d_save
;
2002 p
->private->load_action
= _sv_panel2d_load
;
2004 /* set up helper data structures for rendering */
2006 /* determine which functions are actually needed; if it's referenced
2007 by an objective, it's used. Precache them in dense form. */
2009 int fn
= _sv_functions
;
2010 int used
[fn
],count
=0,offcount
=0;
2011 memset(used
,0,sizeof(used
));
2012 memset(fout_offsets
,-1,sizeof(fout_offsets
));
2014 for(i
=0;i
<p
->objectives
;i
++){
2015 sv_obj_t
*o
= p
->objective_list
[i
].o
;
2016 for(j
=0;j
<o
->outputs
;j
++)
2017 used
[o
->function_map
[j
]]=1;
2022 sv_func_t
*f
= _sv_function_list
[i
];
2023 fout_offsets
[i
] = offcount
;
2024 offcount
+= f
->outputs
;
2028 p2
->used_functions
= count
;
2029 p2
->used_function_list
= calloc(count
, sizeof(*p2
->used_function_list
));
2031 for(count
=0,i
=0;i
<fn
;i
++)
2033 p2
->used_function_list
[count
]=_sv_function_list
[i
];
2038 /* set up computation/render helpers for Y planes */
2040 /* set up Y object mapping index */
2044 for(i
=0;i
<p
->objectives
;i
++){
2045 sv_obj_t
*o
= p
->objective_list
[i
].o
;
2046 if(o
->private->y_func
) yobj_count
++;
2049 p2
->y_obj_num
= yobj_count
;
2050 p2
->y_obj_list
= calloc(yobj_count
, sizeof(*p2
->y_obj_list
));
2051 p2
->y_obj_to_panel
= calloc(yobj_count
, sizeof(*p2
->y_obj_to_panel
));
2052 p2
->y_obj_from_panel
= calloc(p
->objectives
, sizeof(*p2
->y_obj_from_panel
));
2055 for(i
=0;i
<p
->objectives
;i
++){
2056 sv_obj_t
*o
= p
->objective_list
[i
].o
;
2057 if(o
->private->y_func
){
2058 p2
->y_obj_list
[yobj_count
] = o
;
2059 p2
->y_obj_to_panel
[yobj_count
] = i
;
2060 p2
->y_obj_from_panel
[i
] = yobj_count
;
2063 p2
->y_obj_from_panel
[i
] = -1;
2068 /* set up function Y output value demultiplex helper */
2070 p2
->y_fout_offset
= calloc(p2
->y_obj_num
, sizeof(*p2
->y_fout_offset
));
2071 for(i
=0;i
<p2
->y_obj_num
;i
++){
2072 sv_obj_t
*o
= p2
->y_obj_list
[i
];
2073 int funcnum
= o
->private->y_func
->number
;
2074 p2
->y_fout_offset
[i
] = fout_offsets
[funcnum
] + o
->private->y_fout
;
2078 p2
->y_map
= calloc(p2
->y_obj_num
,sizeof(*p2
->y_map
));
2079 p2
->y_planetodo
= calloc(p2
->y_obj_num
,sizeof(*p2
->y_planetodo
));
2080 p2
->y_planes
= calloc(p2
->y_obj_num
,sizeof(*p2
->y_planes
));