[xiph-commits] r12389 - trunk/sushivision

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Mon Jan 29 05:58:16 PST 2007


Author: xiphmont
Date: 2007-01-29 05:58:10 -0800 (Mon, 29 Jan 2007)
New Revision: 12389

Modified:
   trunk/sushivision/dimension.c
   trunk/sushivision/dimension.h
   trunk/sushivision/internal.h
   trunk/sushivision/main.c
   trunk/sushivision/mapping.c
   trunk/sushivision/mapping.h
   trunk/sushivision/panel-1d.c
   trunk/sushivision/panel-1d.h
   trunk/sushivision/panel-2d.c
   trunk/sushivision/panel-2d.h
   trunk/sushivision/panel.c
   trunk/sushivision/plot.c
   trunk/sushivision/plot.h
   trunk/sushivision/scale.c
   trunk/sushivision/scale.h
Log:
Huge round of changes to bring multiple plane types and continuous 
dimensions to 2d panels.  THEIS DOES NOT RUN YET.  This is going into SVN 
because it's a month of changes I don't feel like losing.



Modified: trunk/sushivision/dimension.c
===================================================================
--- trunk/sushivision/dimension.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/dimension.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -37,10 +37,8 @@
    underlying data vector (often the same as the display), and the
    third the same as the second, but over the absolute range [0 - n)
    such that discrete dimensions will count from 0 in iteration. */
-/* if a dimension is linked, that is not handled here; the passed in x
-   must be the linked x_v scale in which case it will be unaltered (it
-   will be filtered through the discrete transformation a second time,
-   which will not alter it) but x_i will bre generated fresh. */
+/* data_w ignored except in the continuous case, where it may be used
+   to generate linked or over/undersampled data scales. */
 int _sushiv_dimension_scales(sushiv_dimension_t *d,
 			     double lo,
 			     double hi,
@@ -112,6 +110,25 @@
   return data_w;
 }
 
+int _sushiv_dimension_scales_from_panel(sushiv_dimension_t *d,
+					scalespace panel,
+					int data_w,
+					scalespace *data, 
+					scalespace *iter){
+
+  return _sushiv_dimension_scales(d,
+				  panel.lo,
+				  panel.hi,
+				  panel.pixels,
+				  data_w,
+				  panel.spacing,
+				  panel.legend,
+				  &panel, // dummy
+				  data, 
+				  iter);
+}
+
+
 static double discrete_quantize_val(sushiv_dimension_t *d, double val){
   if(d->type == SUSHIV_DIM_DISCRETE){
     val *= d->private->discrete_denominator;

Modified: trunk/sushivision/dimension.h
===================================================================
--- trunk/sushivision/dimension.h	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/dimension.h	2007-01-29 13:58:10 UTC (rev 12389)
@@ -60,6 +60,12 @@
 				    scalespace *panel, 
 				    scalespace *data, 
 				    scalespace *iter);
+extern int _sushiv_dimension_scales_from_panel(sushiv_dimension_t *d,
+					       scalespace panel,
+					       int data_w,
+					       scalespace *data, 
+					       scalespace *iter);
+
 extern void _sushiv_dimension_set_value(sushiv_dim_widget_t *d, int thumb, double val);
 extern void _sushiv_dim_widget_set_thumb_active(sushiv_dim_widget_t *dw, int thumb, int active);
 extern sushiv_dim_widget_t *_sushiv_new_dimension_widget(sushiv_dimension_list_t *dl,   

Modified: trunk/sushivision/internal.h
===================================================================
--- trunk/sushivision/internal.h	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/internal.h	2007-01-29 13:58:10 UTC (rev 12389)
@@ -19,6 +19,7 @@
  * 
  */
 
+#include <time.h>
 #include <signal.h>
 #include <gtk/gtk.h>
 #include "sushivision.h"
@@ -54,11 +55,9 @@
   int box_active;
 } sushiv_panel_undo_t;
 
-typedef struct {
-  void (**call)(double *, double *);
-  double **fout; // [function number][outval_number*x]
-  int storage_width;
-
+typedef union {
+  _sushiv_compute_cache_1d p1;
+  _sushiv_compute_cache_2d p2;
 } _sushiv_compute_cache;
 
 struct sushiv_panel_internal {
@@ -72,6 +71,8 @@
   int maps_rendering;
   int legend_rendering;
 
+  time_t last_map_throttle;
+
   // function bundles 
   void (*realize)(sushiv_panel_t *p);
   void (*map_redraw)(sushiv_panel_t *p);
@@ -99,8 +100,11 @@
 			     int *objectives,
 			     int *dimensions,
 			     unsigned flags);
+extern void set_map_throttle_time(sushiv_panel_t *p);
 
+
 extern void _sushiv_panel_dirty_map(sushiv_panel_t *p);
+extern void _sushiv_panel_dirty_map_throttled(sushiv_panel_t *p);
 extern void _sushiv_panel_dirty_legend(sushiv_panel_t *p);
 extern void _sushiv_wake_workers(void);
 

Modified: trunk/sushivision/main.c
===================================================================
--- trunk/sushivision/main.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/main.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -85,38 +85,6 @@
   }
 }
 
-void _maintain_cache(sushiv_panel_t *p, _sushiv_compute_cache *c, int w){
-  
-  /* toplevel initialization */
-  if(c->fout == 0){
-    int i,j;
-    
-    /* determine which functions are actually needed */
-    c->call = calloc(p->sushi->functions,sizeof(*c->call));
-    c->fout = calloc(p->sushi->functions,sizeof(*c->fout));
-    for(i=0;i<p->objectives;i++){
-      sushiv_objective_t *o = p->objective_list[i].o;
-      for(j=0;j<o->outputs;j++)
-	c->call[o->function_map[j]]=
-	  p->sushi->function_list[o->function_map[j]]->callback;
-    }
-  }
-
-  /* once to begin, as well as anytime the data width changes */
-  if(c->storage_width < w){
-    int i;
-    c->storage_width = w;
-
-    for(i=0;i<p->sushi->functions;i++){
-      if(c->call[i]){
-	if(c->fout[i])free(c->fout[i]);
-	c->fout[i] = malloc(w * p->sushi->function_list[i]->outputs *
-			    sizeof(**c->fout));
-      }
-    }
-  }
-}
-
 static void *worker_thread(void *dummy){
   /* set up temporary working space for function rendering; this saves
      continuously recreating it in the loop below */
@@ -156,6 +124,7 @@
 	    gdk_threads_enter ();
 
 	    p->private->maps_rendering = 0;
+	    set_map_throttle_time(p);
 	  }
 
 	  // pending legend work?

Modified: trunk/sushivision/mapping.c
===================================================================
--- trunk/sushivision/mapping.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/mapping.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -319,6 +319,37 @@
   }
 }
 
+u_int32_t mapping_calc_a(mapping *m, float num, float den, u_int32_t mix){
+  u_int32_t o;
+  int r = (mix>>16)&0xff;
+  int g = (mix>>8)&0xff;
+  int b = mix&0xff;
+
+  if(den>0.f)
+    num /= den;
+
+  if(m->i_range==0){
+    if(num<=m->low)
+      o = m->mapfunc(0.,mix);
+    else
+      o = m->mapfunc(1.,mix);
+  }else{
+    double val = (num - m->low) * m->i_range;
+    o = m->mapfunc(val,mix);
+  }
+
+  r += (((o>>16)&0xff) - r)*den;
+  g += (((o>>8)&0xff) - g)*den;
+  b += ((o&0xff) - b)*den;
+  if(r<0)r=0;
+  if(g<0)g=0;
+  if(b<0)b=0;
+  if(r>255)r=255;
+  if(g>255)g=255;
+  if(b>255)b=255;
+  return (r<<16) + (g<<8) + b;
+}
+
 int mapping_inactive_p(mapping *m){
   if(m->mapfunc == inactive)return 1;
   return 0;

Modified: trunk/sushivision/mapping.h
===================================================================
--- trunk/sushivision/mapping.h	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/mapping.h	2007-01-29 13:58:10 UTC (rev 12389)
@@ -36,6 +36,7 @@
 extern void mapping_set_func(mapping *m, int funcnum);
 extern double mapping_val(mapping *m, double in);
 extern u_int32_t mapping_calc(mapping *m, double in, u_int32_t mix);
+extern u_int32_t mapping_calc_a(mapping *m, float num, float den, u_int32_t mix);
 extern int mapping_inactive_p(mapping *m);
 
 extern int num_solids();

Modified: trunk/sushivision/panel-1d.c
===================================================================
--- trunk/sushivision/panel-1d.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/panel-1d.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -346,7 +346,7 @@
     // choose the value under the crosshairs 
     {
       double val = (p1->flip?plot->sely:plot->selx);
-      int bin = scalespace_pixel(&p1->x_v, val);
+      int bin = rint(scalespace_pixel(&p1->x_v, val));
       u_int32_t color = mapping_calc(p1->mappings+i,1.,0);
 
       for(i=0;i<p->objectives;i++){
@@ -534,7 +534,7 @@
 		       double x_max, 
 		       int w, 
 		       double *dim_vals,
-		       _sushiv_compute_cache *c){
+		       _sushiv_compute_cache_1d *c){
   sushiv_panel1d_t *p1 = p->subtype->p1;
   double work[w];
   int i,j,fn=p->sushi->functions;
@@ -599,12 +599,12 @@
   int i,j;
 
   if(p1->link_x){
-    dw = p2->data_w;
+    dw = p2->x_v.pixels;
     p1->x_d = p2->x_d;
     p1->x_scale = p2->x_scale;
   }
   if(p1->link_y){
-    dw = p2->data_h;
+    dw = p2->y_v.pixels;
     p1->x_d = p2->y_d;
     p1->x_scale = p2->y_scale;
   }
@@ -871,6 +871,38 @@
   update_context_menus(p);
 }
 
+void _maintain_cache_1d(sushiv_panel_t *p, _sushiv_compute_cache_1d *c, int w){
+  
+  /* toplevel initialization */
+  if(c->fout == 0){
+    int i,j;
+    
+    /* determine which functions are actually needed */
+    c->call = calloc(p->sushi->functions,sizeof(*c->call));
+    c->fout = calloc(p->sushi->functions,sizeof(*c->fout));
+    for(i=0;i<p->objectives;i++){
+      sushiv_objective_t *o = p->objective_list[i].o;
+      for(j=0;j<o->outputs;j++)
+	c->call[o->function_map[j]]=
+	  p->sushi->function_list[o->function_map[j]]->callback;
+    }
+  }
+
+  /* once to begin, as well as anytime the data width changes */
+  if(c->storage_width < w){
+    int i;
+    c->storage_width = w;
+
+    for(i=0;i<p->sushi->functions;i++){
+      if(c->call[i]){
+	if(c->fout[i])free(c->fout[i]);
+	c->fout[i] = malloc(w * p->sushi->function_list[i]->outputs *
+			    sizeof(**c->fout));
+      }
+    }
+  }
+}
+
 int _sushiv_panel_cooperative_compute_1d(sushiv_panel_t *p,
 					 _sushiv_compute_cache *c){
   sushiv_panel1d_t *p1 = p->subtype->p1;
@@ -939,7 +971,7 @@
     dim_vals[i]=dim->val;
   }
 
-  _maintain_cache(p,c,dw);
+  _maintain_cache_1d(p,&c->p1,dw);
 
   // update scales if we're just starting
   if(p1->last_line==0){
@@ -959,7 +991,7 @@
     }
     
     /* compute */
-    compute_1d(p, serialno, x_d, x_min, x_max, dw, dim_vals, c);
+    compute_1d(p, serialno, x_d, x_min, x_max, dw, dim_vals, &c->p1);
     gdk_threads_enter ();
     _sushiv_panel_dirty_map(p);
     _sushiv_panel_dirty_legend(p);
@@ -1067,65 +1099,6 @@
   }
 }
 
-// called with lock
-static void panel1d_find_peak(sushiv_panel_t *p){
-  sushiv_panel1d_t *p1 = p->subtype->p1;
-  Plot *plot = PLOT(p->private->graph);
-  int i,j;
-  int dw = p1->data_size;
-  int count = 0;
-  
-  // finds in order each peak (in the event there's more than one) of
-  // each active objective
-  while(1){
-    
-    for(i=0;i<p->objectives;i++){
-      if(p1->data_vec && p1->data_vec[i] && !mapping_inactive_p(p1->mappings+i)){
-	double *data=p1->data_vec[i];
-	double best_val = data[0];
-	double best_j = 0;
-	int inner_count = count+1;
-	
-	for(j=1;j<dw;j++){
-	  if(!isnan(data[j])){
-	    if(data[j]>best_val){
-	      inner_count = count+1;
-	      best_val = data[j];
-	      best_j = j;
-	    }else if (data[j]==best_val){
-	      if(inner_count <= p1->peak_count){
-		inner_count++;
-		best_val = data[j];
-		best_j = j;
-	      }
-	    }
-	  }
-	}
-	
-	count = inner_count;
-	if(count>p1->peak_count){
-	  double xv = scalespace_value(&p1->x_v,best_j);
-
-	  if(p1->flip)
-	    plot_set_crosshairs(plot,0,xv);
-	  else
-	    plot_set_crosshairs(plot,xv,0);
-	  crosshair_callback(p);
-	  
-	  p1->peak_count++;
-	  
-	  return;
-	}
-      }
-    }
-    
-    if(p1->peak_count==0)
-      return; // must be all inactive
-    else
-      p1->peak_count=0;
-  }
-}
- 
 static gboolean panel1d_keypress(GtkWidget *widget,
 				 GdkEventKey *event,
 				 gpointer in){
@@ -1154,10 +1127,6 @@
     _sushiv_panel_undo_up(p);
     return TRUE;
 
-  case GDK_p:
-    // find [next] peak
-    panel1d_find_peak(p);
-    return TRUE;
   }
 
   return FALSE;
@@ -1238,7 +1207,6 @@
   "",
   "Start zoom selection",
   "Clear readouts",
-  "Find peaks",
   "",
   "Quit",
   NULL
@@ -1250,7 +1218,6 @@
   NULL,
   "Enter",
   "Escape",
-  "p",
   NULL,
   "q",
   NULL
@@ -1263,7 +1230,6 @@
 
   &wrap_enter,
   &wrap_escape,
-  &panel1d_find_peak,
   NULL,
   &wrap_exit,
   NULL,

Modified: trunk/sushivision/panel-1d.h
===================================================================
--- trunk/sushivision/panel-1d.h	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/panel-1d.h	2007-01-29 13:58:10 UTC (rev 12389)
@@ -70,3 +70,10 @@
   int peak_count;
 } sushiv_panel1d_t;
 
+typedef struct {
+  void (**call)(double *, double *);
+  double **fout; // [function number][outval_number*x]
+  int storage_width;
+
+} _sushiv_compute_cache_1d;
+

Modified: trunk/sushivision/panel-2d.c
===================================================================
--- trunk/sushivision/panel-2d.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/panel-2d.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -33,10 +33,498 @@
 #include <gdk/gdkkeysyms.h>
 #include "internal.h"
 
+/* helper functions for performing progressive computation */
+
+/* performs render helper cleanup after finishing a progressive
+   render; makes sure old map is completely replaced by new. */
+/* Don't check serialno or size; we must force any completion before a
+   resize/rerender, so it will always match anyway */
+/* call from lock */
+static void compute_complete_render(sushiv_panel_t *p, 
+				    int true_complete){
+
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  scalespace *panelx = &p2->x;
+  scalespace *panely = &p2->y;
+  scalespace *datay = &p2->y_v;
+
+  int i,x,y;
+  int w = panelx->pixels;
+  int h = panely->pixels;
+
+  /* these progressive rendering helpers are specific to resampled y;
+     if ph=dh, there's nothing to do */
+
+  if(h != datay->pixels){
+    if(p2->render_flag){
+      if(true_complete){
+	/* fully complete render; swap and zero */
+
+	for(i=0;i<p2->y_obj_num;i++){
+	  float *n = p2->y_num_rend[i];
+	  float *d = p2->y_den_rend[i];
+
+	  p2->y_num_rend[i] = p2->y_num[i];
+	  p2->y_num = n;
+	  p2->y_den_rend[i] = p2->y_den[i];
+	  p2->y_den = d;
+	  
+	  memset(p2->y_num_rend[i],0,sizeof(**p2->y_num_rend)*w*h);
+	  memset(p2->y_den_rend[i],0,sizeof(**p2->y_den_rend)*w*h);
+
+	}
+
+      }else{
+	/* partially complete render; force the completion as a mix of current/pending */
+
+	for(i=0;i<p2->y_obj_num;i++){
+	  for(y=0;y<h;y++){
+	    float del = p2->y_rend[y];
+	    if(del>0){
+	      float *a = p2->y_num[i] + y*w;
+	      float *b = p2->y_num_rend[i] + y*w;
+	      for(x=0;x<w;x++){
+		*a = (*a) + (*a - *b++)*del;
+		a++;
+	      }
+
+	      a = p2->y_den[i] + y*w;
+	      b = p2->y_den_rend[i] + y*w;
+	      for(x=0;x<w;x++){
+		*a = *a+(*a-*b++)*del;
+		a++;
+	      }
+	    }
+	  }
+
+	  memset(p2->y_num_rend[i],0,sizeof(**p2->y_num_rend)*w*h);
+	  memset(p2->y_den_rend[i],0,sizeof(**p2->y_den_rend)*w*h);
+
+	}      
+      
+      }
+
+      memset(p2->y_rend,0,sizeof(*p2->y_rend)*h);
+    }
+
+    p2->render_flag = 0;
+  }
+}
+
+/* prepares for a render (checks for / sets up resampling) */
+// call from lock
+static void compute_prepare_render(sushiv_panel_t *p){
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  scalespace *panelx = &p2->x;
+  scalespace *panely = &p2->y;
+  scalespace *datay = &p2->y_v;
+
+  int i;
+  int w = panelx->pixels;
+  int h = panely->pixels;
+
+  if(p2->render_flag)
+    compute_complete_render(p, 0);
+
+  /* progressive rendering helpers are specific to resampled y;
+     if ph=dh, there's nothing to do */
+  if(!p2->y_rend && h != datay->pixels){
+    p2->y_rend = calloc(h,sizeof(*p2->y_rend));
+
+    if(!p2->y_num_rend)
+      p2->y_num_rend = calloc(p2->y_obj_num,sizeof(*p2->y_num_rend));
+    if(!p2->y_den_rend)
+      p2->y_den_rend = calloc(p2->y_obj_num,sizeof(*p2->y_den_rend));
+
+    for(i=0;i<p2->y_obj_num;i++){
+      p2->y_num_rend[i] = calloc(w*h,sizeof(**p2->y_num_rend));
+      p2->y_den_rend[i] = calloc(w*h,sizeof(**p2->y_den_rend));
+    }
+  }
+}
+
+static void compute_free_render(sushiv_panel_t *p){
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  int i;
+
+  if(p2->y_rend){
+    free(p2->y_rend);
+    p2->y_rend = NULL;
+  }
+    
+  if(p2->y_num_rend){
+    for(i=0;i<p2->y_obj_num;i++){
+      free(p2->y_num_rend[i]);
+      p2->y_num_rend[i]=NULL;
+    }
+  }
+
+  if(p2->y_den_rend){
+    for(i=0;i<p2->y_obj_num;i++){
+      free(p2->y_den_rend[i]);
+      p2->y_den_rend[i]=NULL;
+    }
+  }
+}
+
+// enter unlocked
+static void compute_one_data_line_2d(sushiv_panel_t *p, 
+				     scalespace panelx,
+				     scalespace datax,
+				     int x_d, 
+				     double x_min, 
+				     double x_max, 
+				     double *dim_vals, 
+				     _sushiv_compute_cache_2d *c){
+  int pw = panelx.pixels;
+  int dw = datax.pixels;
+
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  int i,j;
+
+  /* cache access is unlocked because the cache is private to this
+     worker thread */
+
+  if(pw != dw){
+    /* resampled computation */
+    float scaledel = scalespace_scaledel(&panelx,&datax);
+    float outdel = scalespace_pixel(&panelx,scalespace_value(&datax,0));
+    int outbin = floor(outdel);
+    outdel -= outbin; 
+    
+    /* zero obj line cache */
+    for(i=0;i<p->objectives;i++){
+      if(c->y_num[i])
+	memset(c->y_num[i],0, c->storage_width * sizeof(**c->y_num));
+      if(c->y_den[i])
+	memset(c->y_den[i],0, c->storage_width * sizeof(**c->y_den));
+    }
+
+    /* by x */
+    for(j=0;j<dw;j++){
+      float outdel2 = outdel + scaledel;
+
+      double *fout = c->fout;
+      sushiv_function_t **f = p2->used_function_list;
+      int *obj_y_off = p2->y_fout_offset;
+
+      float obj_y[p2->y_obj_num];
+      int *onum = p2->y_obj_to_panel;
+      
+      /* by function */
+      dim_vals[x_d] = (x_max-x_min) * j / dw + x_min;
+      for(i=0;i<p2->used_functions;i++){
+	(*f)->callback(dim_vals,fout);
+	fout += (*f)->outputs;
+	f++;
+      }
+
+      /* process function output by plane type/objective */
+      /* 2d panels currently only care about the Y output value */
+
+      /* slider map */
+      for(i=0;i<p2->y_obj_num;i++){
+	obj_y[i] = (float)slider_val_to_del(p2->range_scales[*onum], c->fout[*obj_y_off]);
+	obj_y_off++;
+	onum++;
+      }
+      
+      /* resample */
+      while(outdel2>1.f){
+	float addel = (1.f - outdel);
+	
+	if(outbin >= 0 && outbin < pw){
+	  for(i=0;i<p2->y_obj_num;i++){
+	    if(!isnan(obj_y[i])){
+	       c->y_num[i][outbin] += obj_y[i] * addel;
+	       c->y_den[i][outbin] += addel;
+	    }
+	  }
+	}
+	
+	outdel2 -= addel;
+	outbin++;
+	outdel = 0.f;
+      }
+      
+      if(outdel2>0.f){
+	float addel = (outdel2 - outdel);
+	
+	if(outbin >= 0 && outbin < pw){
+	  for(i=0;i<p2->y_obj_num;i++){
+	    if(!isnan(obj_y[i])){
+	       c->y_num[i][outbin] += obj_y[i] * addel;
+	       c->y_den[i][outbin] += addel;
+	    }
+	  }
+	}
+      }
+    }
+
+  }else{
+    /* simpler non-resampling case */
+    /* by x */
+    for(j=0;j<dw;j++){
+      double *fout = c->fout;
+      sushiv_function_t **f = p2->used_function_list;
+      int *obj_y_off = p2->y_fout_offset;
+      int *onum = p2->y_obj_to_panel;
+      
+      /* by function */
+      dim_vals[x_d] = (x_max-x_min) * j / dw + x_min;
+      for(i=0;i<p2->used_functions;i++){
+	(*f)->callback(dim_vals,fout);
+	fout += (*f)->outputs;
+	f++;
+      }
+      
+      /* process function output by plane type/objective */
+      /* 2d panels currently only care about the Y output value */
+
+      /* slider map */
+      for(i=0;i<p2->y_obj_num;i++){
+	float yval = (float)slider_val_to_del(p2->range_scales[*onum], c->fout[*obj_y_off]);
+	
+	if(!isnan(yval)){
+	  c->y_num[i][j] = yval;
+	  c->y_den[i][j] = 1.f;
+	}else{
+	  c->y_num[i][j] = 0.f;
+	  c->y_den[i][j] = 0.f;
+	}
+
+	obj_y_off++;
+	onum++;
+      }
+    }
+  }
+}
+
+/* Although render/swizzle is done by data line, we still need to
+   display panel lines.  This is a wrapper around data line rendering
+   that updates the relevant panel line[s] with the computed
+   data. */
+static void compute_one_line_2d(sushiv_panel_t *p, 
+				int serialno,
+				scalespace panelx,
+				scalespace datax,
+				scalespace panely,
+				scalespace datay,
+				      
+				int y, // data line
+				int x_d, 
+				double x_min, 
+				double x_max, 
+				double *dim_vals, 
+				_sushiv_compute_cache_2d *c){
+
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  int i,j;
+  int w = panelx.pixels;
+  int ph = panely.pixels;
+  int dh = datay.pixels;
+  
+  /* before anything else-- compute the line. */
+  compute_one_data_line_2d(p, panelx, datax, x_d, x_min, x_max,
+			   dim_vals, c);    
+
+  if(ph != dh){
+    /* this is a resampling population */
+
+    float scaledel = scalespace_scaledel(&panely,&datay);
+    float outdel = scalespace_pixel(&panely,scalespace_value(&datay,y));
+    int outbin = floor(outdel);
+    float outdel2 = (outdel-outbin) + scaledel;
+    outdel -= outbin; 
+
+    while(outdel2>1.f){
+      float addel = (1.f - outdel);
+      
+      if(outbin >= 0 && outbin < ph){
+	gdk_threads_enter ();
+
+	if(p2->serialno == serialno){
+	  for(j=0;j<p2->y_obj_num;j++){
+	    float *n = p2->y_num_rend[j] + outbin*w;
+	    float *d = p2->y_den_rend[j] + outbin*w;
+	    float *tn = c->y_num[j];
+	    float *td = c->y_den[j];
+	    
+	    for(i=0;i<w;i++){
+	      n[i] += tn[i] * addel;
+	      d[i] += td[i] * addel;
+	    }
+	  }
+	  p2->y_rend[outbin]+=addel;
+	}else{
+	  gdk_threads_leave ();
+	  return;
+	}
+	gdk_threads_leave ();
+      }
+
+      outdel2 -= addel;
+      outbin++;
+      outdel = 0.f;
+    }
+
+    if(outdel2>0.f){
+      float addel = (outdel2 - outdel);
+      
+      if(outbin >= 0 && outbin < ph){
+	gdk_threads_enter ();
+	if(p2->serialno == serialno){
+	  for(j=0;j<p2->y_obj_num;j++){
+	    float *n = p2->y_num_rend[j] + outbin*w;
+	    float *d = p2->y_den_rend[j] + outbin*w;
+	    float *tn = c->y_num[j];
+	    float *td = c->y_den[j];
+	    
+	    for(i=0;i<w;i++){
+	      n[i] += tn[i] * addel;
+	      d[i] += td[i] * addel;
+	    }
+	  }
+	  p2->y_rend[outbin]+=addel;
+	}
+	gdk_threads_leave ();
+      }
+    }
+  }else{
+
+    gdk_threads_enter ();
+    
+    if(p2->serialno == serialno){
+      for(j=0;j<p2->y_obj_num;j++){
+	float *n = p2->y_num[j] + y*w;
+	float *d = p2->y_den[j] + y*w;
+	float *tn = c->y_num[j];
+	float *td = c->y_den[j];
+	
+	memcpy(n,tn,w*sizeof(*n));
+	memcpy(d,td,w*sizeof(*n));
+	
+      }
+    }
+    gdk_threads_leave ();
+  }
+}
+
+// call with lock
+static void clear_pane(sushiv_panel_t *p){
+
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  scalespace *panelx = &p2->x;
+  scalespace *panely = &p2->y;
+  int i;
+  int w = panelx->pixels;
+  int h = panely->pixels;
+
+  for(i=0;i<p2->y_obj_num;i++){
+    memset(p2->y_num[i],0,sizeof(**p2->y_num)*w*h);
+    memset(p2->y_den[i],0,sizeof(**p2->y_den)*w*h);
+    if(p2->y_num_rend && p2->y_num_rend[i])
+      memset(p2->y_num_rend[i],0,sizeof(**p2->y_num_rend)*w*h);
+    if(p2->y_den_rend && p2->y_den_rend[i])
+      memset(p2->y_den_rend[i],0,sizeof(**p2->y_den_rend)*w*h);
+  } 
+  if(p2->y_rend)
+    memset(p2->y_rend,0,sizeof(*p2->y_rend)*h);
+  p2->render_flag = 0;
+}
+
+typedef struct{
+  double x;
+  double y;
+  double z;
+  double e1;
+  double e2;
+  double p1;
+  double p2;
+  double m;
+} compute_result;
+
+// used by the legend code. this lets us get away with having only a mapped display pane
+// call with lock
+static void compute_single_point(sushiv_panel_t *p,sushiv_objective_t *o, double x, double y, compute_result *out){
+  double dim_vals[p->sushi->dimensions];
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  int i,j;
+  int pflag=0;
+  int eflag=0;
+
+  // fill in dimensions
+  int x_d = p2->x_d->number;
+  int y_d = p2->y_d->number;
+
+  for(i=0;i<p->sushi->dimensions;i++){
+    sushiv_dimension_t *dim = p->sushi->dimension_list[i];
+    dim_vals[i]=dim->val;
+  }
+
+  gdk_threads_leave();
+
+  dim_vals[x_d] = x;
+  dim_vals[y_d] = y;
+
+  *out = (compute_result){NAN,NAN,NAN,NAN,NAN,NAN,NAN,NAN};
+
+  // compute
+  for(i=0;i<p->sushi->functions;i++){
+    sushiv_function_t *f = p->sushi->function_list[i];
+    int compflag = 0;
+    double fout[f->outputs];
+    double val;
+
+    // compute and demultiplex output
+    for(j=0;j<o->outputs;j++){
+      if(o->function_map[j] == i){
+
+	if(!compflag) f->callback(dim_vals,fout);
+	compflag = 1;
+	
+	val = fout[o->output_map[j]];
+	switch(o->output_types[j]){
+	case 'X':
+	  out->x = val;
+	  break;
+	case 'Y':
+	  out->y = val;
+	  break;
+	case 'Z':
+	  out->z = val;
+	  break;
+	case 'E':
+	  if(eflag)
+	    out->e2 = val;
+	  else
+	    out->e1 = val;
+	  eflag = 1;
+	  break;
+	case 'P':
+	  if(pflag)
+	    out->p2 = val;
+	  else
+	    out->p1 = val;
+	  pflag = 1;
+	  break;
+	case 'M':
+	  out->m = val;
+	  break;
+	}
+      }
+    }
+  }
+  gdk_threads_enter();
+
+}
+
+/* functions that perform actual graphical rendering */
+
 static void render_checks(int w, int y, u_int32_t *render){
   int x,j;
   /* default checked background */
-  /* 16x16 'mid-checks' */
+  /* 16x16 'mid-checks' */ 
   int phase = (y>>4)&1;
   for(x=0;x<w;){
     u_int32_t phaseval = 0x505050;
@@ -47,40 +535,75 @@
   }
 }
 
+static void render_y_plane(sushiv_panel_t *p, int y, int objnum, u_int32_t *render){
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+
+  int w,h,x;
+  int cond_onum = p2->y_obj_from_panel[objnum];
+  double alpha = p2->alphadel[objnum];
+  w = p2->x.pixels;
+  h = p2->y.pixels;
+
+  // is this a resampled render in-progress?
+  if(p2->render_flag){
+    // resampled render in-progress; we must merge the panel and render buffers
+    float del = p2->y_rend[y];
+    float *numA = p2->y_num[cond_onum] + w*y;
+    float *denA = p2->y_den[cond_onum] + w*y;
+    float *numB = p2->y_num_rend[cond_onum] + w*y;
+    float *denB = p2->y_den_rend[cond_onum] + w*y;
+
+    for(x=0;x<w;x++){
+      float num = numA[x] + (numB[x] - numA[x])*del;
+      float den = denA[x] + (denB[x] - denA[x])*del;
+      if(den>0.f){
+	num /= den;
+	/* map/render result */
+	if(!isnan(num) && num>=alpha)
+	  render[x] = mapping_calc_a(p2->mappings+objnum,num,den,render[x]);
+      }
+    }
+
+  }else{
+    // normal render or fully complete resampled render 
+
+    float *num = p2->y_num[cond_onum] + w*y;
+    float *den = p2->y_den[cond_onum] + w*y;
+
+    for(x=0;x<w;x++){
+      /* map/render result */
+      if(!isnan(num[x]) && num[x]>=alpha)
+	render[x] = mapping_calc_a(p2->mappings+objnum,num[x],den[x],render[x]);
+    }
+  }
+}
+
 static void _sushiv_panel2d_remap(sushiv_panel_t *p){
   sushiv_panel2d_t *p2 = p->subtype->p2;
   Plot *plot = PLOT(p->private->graph);
 
-  int w,h,x,y,i;
-  w = p2->data_w;
-  h = p2->data_h;
+  int w,h,y,i;
+  w = p2->x.pixels;
+  h = p2->y.pixels;
 
   if(plot){
-    u_int32_t render[w];
-
-    /* iterate */
-    /* by line */
     for(y = 0; y<h; y++){
-   
+      u_int32_t *render = plot->datarect + y*w;
+      
+      /* background checks */
       render_checks(w,y,render);
-
+      
       /* by objective */
       for(i=0;i<p->objectives;i++){
-	double *data_rect = p2->data_rect[i] + y*w;
-	double alpha = p2->alphadel[i];
+	
+	/**** render Y plane */
+	render_y_plane(p, y, i, render);
+	
+	/**** render Z plane */
 
-	/* by x */
-	for(x=0;x<w;x++){
-	  double val = data_rect[x];
-	  
-	  /* map/render result */
-	  if(!isnan(val) && val>=alpha)
-	    render[x] = mapping_calc(p2->mappings+i,val,render[x]);
-	}
+	/**** render vector plane */
+
       }
-      
-      /* store result in panel */
-      memcpy(plot->datarect+y*w,render,w*sizeof(*render));
     }
   }
 }
@@ -90,10 +613,6 @@
   Plot *plot = PLOT(p->private->graph);
 
   gdk_threads_enter ();
-  int w = p2->data_w;
-  int h = p2->data_h;
-  int x = plot_get_crosshair_xpixel(plot);
-  int y = plot_get_crosshair_ypixel(plot);
 
   if(plot){
     int i;
@@ -106,36 +625,29 @@
     if(3-p2->x.decimal_exponent > depth) depth = 3-p2->x.decimal_exponent;
     if(3-p2->y.decimal_exponent > depth) depth = 3-p2->y.decimal_exponent;
     for(i=0;i<p->dimensions;i++){
-      //int depth = del_depth(p->dimension_list[i].d->bracket[0],
-      //		    p->dimension_list[i].d->bracket[1]) + offset;
       snprintf(buffer,320,"%s = %+.*f",
 	       p->dimension_list[i].d->name,
 	       depth,
 	       p->dimension_list[i].d->val);
       plot_legend_add(plot,buffer);
     }
-
+    
     // one space 
     plot_legend_add(plot,NULL);
 
-    // add each active objective to the legend
+    // add each active objective plane to the legend
     // choose the value under the crosshairs 
     for(i=0;i<p->objectives;i++){
-      float val=NAN;
 
-      if(p2->data_rect && p2->data_rect[i] &&
-	 x<w && x>0 &&
-	 y<h && y>0 )
-	val = p2->data_rect[i][y*w+x];
+      if(!mapping_inactive_p(p2->mappings+i)){
+	compute_result vals;
+	compute_single_point(p,p->objective_list[i].o, plot->selx, plot->sely, &vals);
 
-      if(!isnan(val) && val >= p2->alphadel[i]){
-	
-	val = slider_del_to_val(p2->range_scales[i],val);
-	
-	if(!isnan(val) && !mapping_inactive_p(p2->mappings+i)){
+	if(!isnan(vals.y)){
+	  
 	  snprintf(buffer,320,"%s = %f",
 		   p->objective_list[i].o->name,
-		   val);
+		   vals.y);
 	  plot_legend_add(plot,buffer);
 	}
       }
@@ -265,81 +777,6 @@
   } 
 }
 
-static void compute_one_line_2d(sushiv_panel_t *p, 
-				int serialno,
-				int y, 
-				int x_d, 
-				double x_min, 
-				double x_max, 
-				int w, 
-				double *dim_vals, 
-				u_int32_t *render,
-				_sushiv_compute_cache *c){
-
-  sushiv_panel2d_t *p2 = p->subtype->p2;
-  int i,j,fn=p->sushi->functions;
-  double work[w];
-
-  render_checks(w,y,render);
-
-  /* by function */
-  for(i=0;i<fn;i++){
-    if(c->call[i]){
-      sushiv_function_t *f = p->sushi->function_list[i];
-      int step = f->outputs;
-      double *fout = c->fout[i];
-      
-      /* by x */
-      for(j=0;j<w;j++){
-
-	dim_vals[x_d] = (x_max-x_min) * j / w + x_min;
-	c->call[i](dim_vals,fout);
-	fout+=step;
-      }
-    }
-  }
-
-  /* process function output by objective */
-  /* 2d panels only care about the Y output value */
-  for(i=0;i<p->objectives;i++){
-    sushiv_objective_t *o = p->objective_list[i].o;
-    int offset = o->private->y_fout;
-    sushiv_function_t *f = o->private->y_func;
-    if(f){
-      int step = f->outputs;
-      double *fout = c->fout[f->number]+offset;
-      mapping m;
-      double alpha;
-      
-      /* map result */
-      /* range scales are static so no need to lock; if that ever
-	 changes, locking should happen in the scales... */
-      for(j=0;j<w;j++){
-	work[j] = slider_val_to_del(p2->range_scales[i],*fout);
-	fout+=step;
-      }
-      
-      gdk_threads_enter (); // misuse me as a global mutex
-      if(p2->serialno == serialno){
-	memcpy(p2->data_rect[i]+y*w,work,w*sizeof(*work));
-	memcpy(&m,p2->mappings+i,sizeof(m));
-	alpha = p2->alphadel[i];
-	gdk_threads_leave (); // misuse me as a global mutex
-      }else{
-	gdk_threads_leave (); // misuse me as a global mutex
-	break;
-      }
-      
-      for(j=0;j<w;j++){	
-	double val = work[j];
-	if(!isnan(val) && val>=alpha)
-	  render[j] = mapping_calc(&m,val,render[j]);
-	
-      }
-    }
-  }
-}
-
 static int v_swizzle(int y, int height){
   int yy = height >> 5;
   if(y < yy)
@@ -370,15 +807,15 @@
 }
 
 // assumes data is locked
-static void fast_scale_x(double *data, 
+static void fast_scale_x(float *data, 
 			 int w,
 			 int h,
 			 scalespace new,
 			 scalespace old){
   int x,y;
-  double work[w];
+  float work[w];
   int mapbase[w];
-  double mapdel[w];
+  float mapdel[w];
 
   double old_w = old.pixels;
   double new_w = new.pixels;
@@ -396,19 +833,16 @@
   }
 
   for(y=0;y<h;y++){
-    double *data_line = data+y*w;
+    float *data_line = data+y*w;
     for(x=0;x<w;x++){
       if(mapbase[x]<0 || mapbase[x]>=(w-1)){
-	work[x]=NAN;
+	work[x]=0.f;
       }else{
 	int base = mapbase[x];
-	double del = mapdel[x];
-	double A = data_line[base];
-	double B = data_line[base+1];
-	if(isnan(A) || isnan(B)) // damn you SIGFPE
-	  work[x]=NAN;
-	else
-	  work[x]= A - A*del + B*del;
+	float del = mapdel[x];
+	float A = data_line[base];
+	float B = data_line[base+1];
+	  work[x]= A + (B - A)*del;
 	
       }
     }
@@ -416,15 +850,15 @@
   }   
 }
 
-static void fast_scale_y(double *data, 
+static void fast_scale_y(float *data, 
 			 int w,
 			 int h,
 			 scalespace new,
 			 scalespace old){
   int x,y;
-  double work[h];
+  float work[h];
   int mapbase[h];
-  double mapdel[h];
+  float mapdel[h];
 
   double old_h = old.pixels;
   double new_h = new.pixels;
@@ -443,21 +877,18 @@
   }
   
   for(x=0;x<w;x++){
-    double *data_column = data+x;
+    float *data_column = data+x;
     int stride = w;
     for(y=0;y<h;y++){
       if(mapbase[y]<0 || mapbase[y]>=(h-1)){
-	work[y]=NAN;
+	work[y]=0.f;
       }else{
 	int base = mapbase[y]*stride;
-	double del = mapdel[y];
-	double A = data_column[base];
-	double B = data_column[base+stride];
+	float del = mapdel[y];
+	float A = data_column[base];
+	float B = data_column[base+stride];
 	
-	if(isnan(A) || isnan(B)) // damn you SIGFPE
-	  work[y]=NAN;
-	else
-	  work[y]= A - A*del + B*del;
+	work[y]= A + (B-A)*del;
 	
       }
     }
@@ -468,10 +899,10 @@
   }   
 }
 
-static void fast_scale(double *newdata, 
+static void fast_scale(float *newdata, 
 		       scalespace xnew,
 		       scalespace ynew,
-		       double *olddata,
+		       float *olddata,
 		       scalespace xold,
 		       scalespace yold){
   int y;
@@ -485,8 +916,8 @@
     if(new_h > old_h){
       // copy image to new, scale there
       for(y=0;y<old_h;y++){
-	double *new_line = newdata+y*new_w;
-	double *old_line = olddata+y*old_w;
+	float *new_line = newdata+y*new_w;
+	float *old_line = olddata+y*old_w;
 	memcpy(new_line,old_line,old_w*(sizeof*new_line));
       }
       fast_scale_x(newdata,new_w,new_h,xnew,xold);
@@ -495,8 +926,8 @@
       // scale y in old pane, copy to new, scale x 
       fast_scale_y(olddata,old_w,old_h,ynew,yold);
       for(y=0;y<new_h;y++){
-	double *new_line = newdata+y*new_w;
-	double *old_line = olddata+y*old_w;
+	float *new_line = newdata+y*new_w;
+	float *old_line = olddata+y*old_w;
 	memcpy(new_line,old_line,old_w*(sizeof*new_line));
       }
       fast_scale_x(newdata,new_w,new_h,xnew,xold);
@@ -506,8 +937,8 @@
       // scale x in old pane, o=copy to new, scale y
       fast_scale_x(olddata,old_w,old_h,xnew,xold);
       for(y=0;y<old_h;y++){
-	double *new_line = newdata+y*new_w;
-	double *old_line = olddata+y*old_w;
+	float *new_line = newdata+y*new_w;
+	float *old_line = olddata+y*old_w;
 	memcpy(new_line,old_line,new_w*(sizeof*new_line));
       }
       fast_scale_y(newdata,new_w,new_h,ynew,yold);
@@ -518,8 +949,8 @@
       fast_scale_y(olddata,old_w,old_h,ynew,yold);
       if(olddata != newdata){
 	for(y=0;y<new_h;y++){
-	  double *new_line = newdata+y*new_w;
-	  double *old_line = olddata+y*old_w;
+	  float *new_line = newdata+y*new_w;
+	  float *old_line = olddata+y*old_w;
 	  memcpy(new_line,old_line,new_w*(sizeof*new_line));
 	}
       }
@@ -534,61 +965,96 @@
   Plot *plot = PLOT(p->private->graph);
   int w = plot->w.allocation.width;
   int h = plot->w.allocation.height;
+  int remapflag = 0;
 
   if(plot && GTK_WIDGET_REALIZED(GTK_WIDGET(plot))){
+    
+    if( p2->serialno && // we've been through once and alloced
+	(p2->x.pixels != w ||
+	 p2->y.pixels != h)){
+	
+	
+      // if a render was in progress, force completion 
+      compute_complete_render(p, 0);
+      compute_free_render(p);
 
-    if( (p2->data_w != w ||
-	 p2->data_h != h) &&
-	p2->data_rect){
-	
       // make new rects, do a fast/dirty scaling job from old to new
       int i;
-      for(i=0;i<p->objectives;i++){
-	double *new_rect = malloc(w * h* sizeof(**p2->data_rect));
+
+      /* Y planes */
+      for(i=0;i<p2->y_obj_num;i++){
+	float *new_n = calloc(w*h,sizeof(*new_n));
+	float *new_d = calloc(w*h,sizeof(*new_d));
 	
-	fast_scale(new_rect,plot->x,plot->y,p2->data_rect[i],p2->x,p2->y);
+	fast_scale(new_n,plot->x,plot->y,p2->y_num[i],p2->x,p2->y);
+	fast_scale(new_d,plot->x,plot->y,p2->y_den[i],p2->x,p2->y);
 	
-	free(p2->data_rect[i]);
-	p2->data_rect[i] = new_rect;
+	free(p2->y_num[i]);
+	free(p2->y_den[i]);
+	p2->y_num[i] = new_n;
+	p2->y_den[i] = new_d;
       }
+
+      /* match data scales to new panel size/scale */
       p2->x = plot->x;
+      _sushiv_dimension_scales_from_panel(p2->x_d,
+					  p2->x,
+					  p2->x.pixels, // over/undersample will go here
+					  &p2->x_v,
+					  &p2->x_i);
       p2->y = plot->y;
-      p2->data_w = w;
-      p2->data_h = h;
-      
+      _sushiv_dimension_scales_from_panel(p2->y_d,
+					  p2->y,
+					  p2->y.pixels, // over/undersample will go here
+					  &p2->y_v,
+					  &p2->y_i);
       _sushiv_panel2d_map_redraw(p);
     }else{
-      
-      p2->data_w = w;
-      p2->data_h = h;
-      
-      p2->x = scalespace_linear(p2->x_d->bracket[0],
-				p2->x_d->bracket[1],
-				w,
-				PLOT(p->private->graph)->scalespacing,
-				p2->x_d->name);
-      
-      p2->y = scalespace_linear(p2->y_d->bracket[0],
-				p2->y_d->bracket[1],
-				h,
-				PLOT(p->private->graph)->scalespacing,
-				p2->y_d->name);
+
+      _sushiv_dimension_scales(p2->x_d, 
+			       p2->x_d->bracket[0],
+			       p2->x_d->bracket[1],
+			       w,w,// over/undersample will go here
+			       plot->scalespacing,
+			       p2->x_d->name,
+			       &p2->x,
+			       &p2->x_v,
+			       &p2->x_i);
+      _sushiv_dimension_scales(p2->y_d, 
+			       p2->y_d->bracket[0],
+			       p2->y_d->bracket[1],
+			       h,h,// over/undersample will go here
+			       plot->scalespacing,
+			       p2->y_d->name,
+			       &p2->y,
+			       &p2->y_v,
+			       &p2->y_i);
     }
     
-    if(!p2->data_rect){
-      int i,j;
+    if(!p2->y_num){
+      int i;
       // allocate it
+      
+      p2->y_num = calloc(p2->y_obj_num,sizeof(*p2->y_num));
+      for(i=0;i<p2->y_obj_num;i++)
+	p2->y_num[i] = calloc(w*h, sizeof(**p2->y_num));
 
-      p2->data_rect = calloc(p->objectives,sizeof(*p2->data_rect));
-      for(i=0;i<p->objectives;i++)
-	p2->data_rect[i] = malloc(p2->data_w * p2->data_h* sizeof(**p2->data_rect));
+      remapflag = 1;
+    }
+
+    if(!p2->y_den){
+      int i;
+      // allocate it
       
-      // blank it 
-      for(i=0;i<p->objectives;i++)
-	for(j=0;j<p2->data_w*p2->data_h;j++)
-	  p2->data_rect[i][j]=NAN;
+      p2->y_den = calloc(p2->y_obj_num,sizeof(*p2->y_den));
+      for(i=0;i<p2->y_obj_num;i++)
+	p2->y_den[i] = calloc(w*h, sizeof(**p2->y_den));
+
+      remapflag = 1;
+    }
+
+    if(remapflag)
       _sushiv_panel2d_map_redraw(p);
-    }
     
     p2->serialno++;
     p2->last_line = 0;
@@ -655,8 +1121,6 @@
 
 static void dimchange_callback_2d(GtkWidget *button,gpointer in){
   sushiv_panel_t *p = (sushiv_panel_t *)in;
-  sushiv_panel2d_t *p2 = p->subtype->p2;
-  int i,j;
 
   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))){
 
@@ -666,10 +1130,7 @@
     plot_unset_box(PLOT(p->private->graph));
     update_xy_availability(p);
 
-    /* if the data_rect already exists, blank it */
-    for(i=0;i<p->objectives;i++)
-      for(j=0;j<p2->data_w*p2->data_h;j++)
-	p2->data_rect[i][j]=NAN;
+    clear_pane(p);
     _sushiv_panel2d_map_redraw(p);
     
     _mark_recompute_2d(p);
@@ -735,6 +1196,44 @@
   p->private->update_menus(p);
 }
 
+void _maintain_cache_2d(sushiv_panel_t *p, _sushiv_compute_cache_2d *c, int w){
+  sushiv_panel2d_t *p2 = p->subtype->p2;
+  
+  /* toplevel initialization */
+  if(c->fout == 0){
+    int i,count=0;
+    
+    /* allocate output temporary buffer */
+    for(i=0;i<p2->used_functions;i++){
+      int fnum = p2->used_function_list[i]->number;
+      sushiv_function_t *f = p->sushi->function_list[fnum];
+      count += f->outputs;
+    }
+    c->fout = calloc(count, sizeof(*c->fout));
+
+    /* objective line buffer index */
+    c->y_num = calloc(p2->y_obj_num,sizeof(*c->y_num));
+    c->y_den = calloc(p2->y_obj_num,sizeof(*c->y_den));
+    for(i=0;i<p2->y_obj_num;i++){
+      c->y_num[i] = calloc(w,sizeof(**c->y_num));
+      c->y_den[i] = calloc(w,sizeof(**c->y_den));
+    }
+    c->storage_width = w;
+  }
+
+  /* anytime the data width changes */
+  if(c->storage_width != w){
+    int i;
+    c->storage_width = w;
+
+    for(i=0;i<p2->y_obj_num;i++){
+      p2->y_num[i] = realloc(p2->y_num[i],w*sizeof(**p2->y_num));
+      p2->y_den[i] = realloc(p2->y_den[i],w*sizeof(**p2->y_den));
+    }
+  }
+}
+
+
 // called from one/all of the worker threads; the idea is that several
 // of the threads will all call this and they collectively interleave
 // ongoing computation of the pane
@@ -744,23 +1243,28 @@
   sushiv_panel2d_t *p2 = p->subtype->p2;
   Plot *plot;
   
-  int w,h,i,d;
+  int pw,ph,dw,dh,i,d;
   int serialno;
   double x_min, x_max;
   double y_min, y_max;
   int x_d=-1, y_d=-1;
   int render_scale_flag = 0;
-  scalespace sx;
-  scalespace sy;
+  scalespace sx,sx_v;
+  scalespace sy,sy_v;
 
   // lock during setup
   gdk_threads_enter ();
-  w = p2->data_w;
-  h = p2->data_h;
   sx = p2->x;
+  sx_v = p2->x_i;
   sy = p2->y;
+  sy_v = p2->y_i;
 
-  if(p2->last_line>=h){
+  pw = sx.pixels;
+  ph = sy.pixels;
+  dw = sx_v.pixels;
+  dh = sy_v.pixels;
+
+  if(p2->last_line>=dh){
     gdk_threads_leave ();
     return 0;
   }
@@ -774,52 +1278,45 @@
      computing objectives */
   double dim_vals[p->sushi->dimensions];
 
-  /* render into temporary line; computation may be interrupted by
-     events such as resizing, so we must be careful to simply assume
-     the widget is unaltered between locks. This allows us to minimize
-     checks. */
-  u_int32_t render[w];
-
-  x_min = scalespace_value(&sx,0);
-  x_max = scalespace_value(&sx,w);
+  x_min = scalespace_value(&p2->x_i,0);
+  x_max = scalespace_value(&p2->x_i,dw);
   x_d = p2->x_d->number;
 
-  y_min = scalespace_value(&sy,h);
-  y_max = scalespace_value(&sy,0);
+  y_min = scalespace_value(&p2->y_i,dh);
+  y_max = scalespace_value(&p2->y_i,0);
   y_d = p2->y_d->number;
 
   // if the scale bound has changed, fast scale our background data to fill
   // the pane while new, more precise data renders.
   if(memcmp(&sx,&plot->x,sizeof(sx))){
-    for(i=0;i<p->objectives;i++)
-      fast_scale_x(p2->data_rect[i],w,h,
+    for(i=0;i<p2->y_obj_num;i++){
+      fast_scale_x(p2->y_num[i],pw,ph,
 		   sx,plot->x);
+      fast_scale_x(p2->y_den[i],pw,ph,
+		   sx,plot->x);
+    }
     plot->x = sx;
     _sushiv_panel2d_remap(p);
   }
   if(memcmp(&sy,&plot->y,sizeof(sy))){
-    for(i=0;i<p->objectives;i++)
-      fast_scale_y(p2->data_rect[i],w,h,
+    for(i=0;i<p2->y_obj_num;i++){
+      fast_scale_y(p2->y_num[i],pw,ph,
 		   sy,plot->y);
+      fast_scale_y(p2->y_den[i],pw,ph,
+		   sy,plot->y);
+    }
     plot->y = sy;
     _sushiv_panel2d_remap(p);
   }
 
-  // Bulletproofing; shouldn't ever come up
-  if(x_d == y_d || x_d==-1 || y_d==-1){
-    gdk_threads_leave ();
-    fprintf(stderr,"Invalid/missing x/y dimension setting in panel x_d=%d, y_d=%d\n",
-	    x_d,y_d);
-    return 0;
-  }
-
   // Initialize local dimension value array
   for(i=0;i<p->sushi->dimensions;i++){
     sushiv_dimension_t *dim = p->sushi->dimension_list[i];
     dim_vals[i]=dim->val;
   }
 
-  _maintain_cache(p,c,w);
+  _maintain_cache_2d(p,&c->p2,pw);
+  compute_prepare_render(p);
 
   // update scales if we're just starting
   if(p2->last_line==0){
@@ -828,10 +1325,9 @@
 
   /* iterate */
   /* by line */
-  if(plot->w.allocation.height == h &&
-     p2->last_line<h &&
+  if(p2->last_line<dh &&
      serialno == p2->serialno){
-    int y = v_swizzle(p2->last_line,h);
+    int y = v_swizzle(p2->last_line,dh);
 
     p2->last_line++;
     
@@ -843,27 +1339,26 @@
       render_scale_flag = 0;
     }
     
-    dim_vals[y_d]= (y_max - y_min) / h * y + y_min;
+    dim_vals[y_d]= (y_max - y_min) / dh * y + y_min;
     
     /* compute line */
-    compute_one_line_2d(p, serialno, y, x_d, x_min, x_max, w, dim_vals, render, c);
-    
-    /* move rendered line back into widget */
+    compute_one_line_2d(p, serialno, sx, sx_v, sy, sy_v,  y, x_d, x_min, x_max, dim_vals, &c->p2);
+
     gdk_threads_enter ();
     p2->completed_lines++;
 
     if(p2->serialno == serialno){
-      u_int32_t *line = plot_get_background_line(plot, y);
-      memcpy(line,render,w*sizeof(*render));
-      if(p2->completed_lines==h){ 
+      if(p2->completed_lines==dh){ 
+	compute_complete_render(p, 1);
+	_sushiv_panel_dirty_map(p);
 	_sushiv_panel_dirty_legend(p);
 	plot_expose_request(plot);
       }else{
-	plot_expose_request_line(plot,y);
+	_sushiv_panel_dirty_map_throttled(p);
       }
     }
   }
-   
+  
   gdk_threads_leave ();
   return 1;
 }
@@ -945,67 +1440,6 @@
   }
 }
 
-// called with lock
-static void panel2d_find_peak(sushiv_panel_t *p){
-  sushiv_panel2d_t *p2 = p->subtype->p2;
-  Plot *plot = PLOT(p->private->graph);
-  int i,j;
-  int w = p2->data_w;
-  int h = p2->data_h;
-  int n = w*h;
-  int count = 0;
-  
-  // finds in order each peak (in the event there's more than one) of
-  // each active objective
-  while(1){
-
-    for(i=0;i<p->objectives;i++){
-      if(p2->data_rect && p2->data_rect[i] && !mapping_inactive_p(p2->mappings+i)){
-	double *data=p2->data_rect[i];
-	double best_val = data[0];
-	double best_j = 0;
-	int inner_count = count+1;
-	
-	for(j=1;j<n;j++){
-	  if(!isnan(data[j])){
-	    if(data[j]>best_val){
-	      inner_count = count+1;
-	      best_val = data[j];
-	      best_j = j;
-	    }else if (data[j]==best_val){
-	      if(inner_count <= p2->peak_count){
-		inner_count++;
-		best_val = data[j];
-		best_j = j;
-	      }
-	    }
-	  }
-	}
-	 
-	count = inner_count;
-	if(count>p2->peak_count){
-	  int y = best_j/w;
-	  int x = best_j - y*w;
-	  double xv = scalespace_value(&p2->x,x);
-	  double yv = scalespace_value(&p2->y,h-y);
-	  
-	  plot_set_crosshairs(plot,xv,yv);
-	  _sushiv_panel2d_crosshairs_callback(p);
-	  
-	  p2->peak_count++;
-	  
-	  return;
-	}
-      }
-    }
-    
-    if(p2->peak_count==0)
-      return; // must be all inactive
-    else
-      p2->peak_count=0;
-  }
-}
-
 static gboolean panel2d_keypress(GtkWidget *widget,
 				 GdkEventKey *event,
 				 gpointer in){
@@ -1035,10 +1469,6 @@
     _sushiv_panel_undo_up(p);
     return TRUE;
 
-  case GDK_p:
-    // find [next] peak
-    panel2d_find_peak(p);
-    return TRUE;
   }
 
   return FALSE;
@@ -1119,7 +1549,6 @@
   "",
   "Start zoom box",
   "Clear readouts",
-  "Find peaks",
   "",
   "Quit",
   NULL
@@ -1131,7 +1560,6 @@
   NULL,
   "Enter",
   "Escape",
-  "p",
   NULL,
   "q",
   NULL
@@ -1144,7 +1572,6 @@
 
   &wrap_enter,
   &wrap_escape,
-  &panel2d_find_peak,
   NULL,
   &wrap_exit,
   NULL,
@@ -1323,10 +1750,11 @@
 			int *dimensions,
 			unsigned flags){
   
-  int i;
+  int i,j;
   int ret = _sushiv_new_panel(s,number,name,objectives,dimensions,flags);
   sushiv_panel_t *p;
   sushiv_panel2d_t *p2;
+  int fout_offsets[s->functions];
 
   if(ret<0)return ret;
   p = s->panel_list[number];
@@ -1370,6 +1798,81 @@
   p->private->undo_log = panel2d_undo_log;
   p->private->undo_restore = panel2d_undo_restore;
   p->private->update_menus = update_context_menus;
+
+  /* set up helper data structures for rendering */
+
+  /* determine which functions are actually needed; if it's referenced
+     by an objective, it's used.  Precache them in dense form. */
+  {
+    int fn = p->sushi->functions;
+    int used[fn],count=0,offcount=0;
+    memset(used,0,sizeof(used));
+    memset(fout_offsets,-1,sizeof(fout_offsets));
+    
+    for(i=0;i<p->objectives;i++){
+      sushiv_objective_t *o = p->objective_list[i].o;
+      for(j=0;j<o->outputs;j++)
+	used[o->function_map[j]]=1;
+    }
+
+    for(i=0;i<fn;i++)
+      if(used[i]){
+	sushiv_function_t *f = p->sushi->function_list[i];
+	fout_offsets[i] = offcount;
+	offcount += f->outputs;
+	count++;
+      }
+
+    p2->used_functions = count;
+    p2->used_function_list = calloc(count, sizeof(*p2->used_function_list));
+
+    for(count=0,i=0;i<fn;i++)
+     if(used[i]){
+        p2->used_function_list[count]=p->sushi->function_list[i];
+	count++;
+      }
+  }
+
+  /* set up computation/render helpers for Y planes */
+
+  /* set up Y object mapping index */
+  {
+    int yobj_count = 0;
+
+    for(i=0;i<p->objectives;i++){
+      sushiv_objective_t *o = p->objective_list[i].o;
+      if(o->private->y_func) yobj_count++;
+    }
+
+    p2->y_obj_num = yobj_count;
+    p2->y_obj_list = calloc(yobj_count, sizeof(*p2->y_obj_list));
+    p2->y_obj_to_panel = calloc(yobj_count, sizeof(*p2->y_obj_to_panel));
+    p2->y_obj_from_panel = calloc(p->objectives, sizeof(*p2->y_obj_from_panel));
+    
+    yobj_count=0;
+    for(i=0;i<p->objectives;i++){
+      sushiv_objective_t *o = p->objective_list[i].o;
+      if(o->private->y_func){
+	p2->y_obj_list[yobj_count] = o;
+	p2->y_obj_to_panel[yobj_count] = i;
+	p2->y_obj_from_panel[i] = yobj_count;
+	yobj_count++;
+      }else
+	p2->y_obj_from_panel[i] = -1;
+      
+    }
+  }
+  
+  /* set up function Y output value demultiplex helper */
+  {
+    p2->y_fout_offset = calloc(p2->y_obj_num, sizeof(*p2->y_fout_offset));
+    for(i=0;i<p2->y_obj_num;i++){
+      sushiv_objective_t *o = p2->y_obj_list[i];
+      int funcnum = o->private->y_func->number;
+      p2->y_fout_offset[i] = fout_offsets[funcnum] + o->private->y_fout;
+    }
+  }
+  
   return 0;
 }
 

Modified: trunk/sushivision/panel-2d.h
===================================================================
--- trunk/sushivision/panel-2d.h	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/panel-2d.h	2007-01-29 13:58:10 UTC (rev 12389)
@@ -26,20 +26,48 @@
   GtkWidget *popmenu;
   GtkWidget *graphmenu;
 
-  int data_w;
-  int data_h;
   int serialno;
-  double **data_rect;
+  
+  /* only run those functions used by this panel */
+  int used_functions;
+  sushiv_function_t **used_function_list;
+
+  /**** Y PLANES ******/
+  float **y_num; // not indirected; unused planes are simply empty
+  float **y_den; // not indirected; unused planes are simply empty
+
+  int y_obj_num;
+  sushiv_objective_t **y_obj_list; // list of objectives with a y plane
+  int *y_obj_to_panel; /* maps from position in condensed list to position in full list */
+  int *y_obj_from_panel; /* maps from position in full list to position in condensed list */
+  int *y_fout_offset; 
+  
+  /* these are gratuitous temporary storage to make progressive
+     resampled render after a fast scale look nicer.  They're here
+     purely on the premise that 'lots of memory is cheap'.  I'm sure
+     Firefox is at least this stupidly wasteful of memory for cosmetic
+     benefit. */
+  int     render_flag;
+  float **y_num_rend;
+  float **y_den_rend;
+  float  *y_rend;
+
+  /* scales and data -> display scale mapping */
   scalespace x;
+  scalespace x_v;
+  scalespace x_i;
   scalespace y;
+  scalespace y_v;
+  scalespace y_i;
+
   int scales_init;
   double oldbox[4];
   int oldbox_active;
 
-  mapping *mappings;
+  mapping    *mappings;
   Slider    **range_scales;
   GtkWidget **range_pulldowns;
-  double *alphadel;
+  double     *alphadel;
 
   GtkWidget **dim_xb;
   GtkWidget **dim_yb;
@@ -48,8 +76,8 @@
   sushiv_dimension_t *y_d;
   sushiv_dim_widget_t *x_scale;
   sushiv_dim_widget_t *y_scale;
-  int x_dnum; // number of dimension within panel, not global instance
-  int y_dnum; // number of dimension within panel, not global instance
+  int x_dnum; // panel, not global list context
+  int y_dnum; // panel, not global list context
 
   int last_line;
   int completed_lines;
@@ -58,3 +86,13 @@
 
 } sushiv_panel2d_t;
 
+typedef struct {
+  double *fout; // [function number * outval_number]
+
+  float **y_num; // [y_obj_list[i]][px]
+  float **y_den; // [y_obj_list[i]][px]
+
+  int storage_width;
+
+} _sushiv_compute_cache_2d;
+

Modified: trunk/sushivision/panel.c
===================================================================
--- trunk/sushivision/panel.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/panel.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -47,6 +47,25 @@
   return 0;
 }
 
+void set_map_throttle_time(sushiv_panel_t *p){
+  struct timeval now;
+  gettimeofday(&now,NULL);
+
+  p->private->last_map_throttle = now.tv_sec*1000 + now.tv_usec/1000;
+}
+
+static int test_throttle_time(sushiv_panel_t *p){
+  struct timeval now;
+  long test;
+  gettimeofday(&now,NULL);
+  
+  test = now.tv_sec*1000 + now.tv_usec/1000;
+  if(p->private->last_map_throttle + 500 < test) 
+    return 1;
+
+  return 0;  
+}
+
 // the following is slightly odd; we want map and legend updates to
 // fire when the UI is otherwise idle (only good way to do event
 // compression in gtk), but we don't want it processed int he main UI
@@ -78,6 +97,13 @@
   gdk_threads_leave ();
 }
 
+void _sushiv_panel_dirty_map_throttled(sushiv_panel_t *p){
+  gdk_threads_enter ();
+  if(test_throttle_time(p))
+     g_idle_add(_idle_map_fire,p);
+  gdk_threads_leave ();
+}
+
 void _sushiv_panel_dirty_legend(sushiv_panel_t *p){
   gdk_threads_enter ();
   g_idle_add(_idle_legend_fire,p);

Modified: trunk/sushivision/plot.c
===================================================================
--- trunk/sushivision/plot.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/plot.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -27,7 +27,6 @@
 #include <stdlib.h>
 #include <math.h>
 #include <stdio.h>
-#include <time.h>
 #include <string.h>
 
 #include "scale.h"
@@ -856,11 +855,6 @@
   p->box_data = box_data;
   p->flags = flags;
 
-  struct timeval now;
-  gettimeofday(&now,NULL);
-
-  p->begin = now.tv_sec;
-  p->last_line_expose = 0;
   return p;
 }
 
@@ -896,36 +890,6 @@
   gdk_threads_leave();
 }
 
-// 'in progress' exposes that may or may not be honored
-void plot_expose_request_line(Plot *p, int num){
-  gdk_threads_enter();
-  GtkWidget *widget = GTK_WIDGET(p);
-  GdkRectangle r;
-  struct timeval now;
-  int64_t msec;
-
-  if(num<p->expose_y_lo)
-    p->expose_y_lo=num;
-  if(num>p->expose_y_hi)
-    p->expose_y_hi=num;
-  gettimeofday(&now,NULL);
-  msec = (now.tv_sec-p->begin)*100LL + now.tv_usec/10000;
-
-  if(msec > p->last_line_expose + 25){
-    r.x=0;
-    r.y=p->expose_y_lo;
-    r.width=widget->allocation.width;
-    r.height=p->expose_y_hi-p->expose_y_lo+1;
-  
-    gdk_window_invalidate_rect (widget->window, &r, FALSE);
-
-    p->expose_y_lo = widget->allocation.height;
-    p->expose_y_hi=0;
-    p->last_line_expose = msec;
-  }
-  gdk_threads_leave();
-}
-
 void plot_set_x_scale(Plot *p, scalespace x){
   scalespace temp = p->x;
   p->x = x;

Modified: trunk/sushivision/plot.h
===================================================================
--- trunk/sushivision/plot.h	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/plot.h	2007-01-29 13:58:10 UTC (rev 12389)
@@ -75,11 +75,6 @@
   u_int32_t *legend_colors;
   char **legend_list;
 
-  int expose_y_lo;
-  int expose_y_hi;
-  time_t last_line_expose;
-  time_t begin;
-
   unsigned flags;
 };
 
@@ -98,7 +93,6 @@
 
 // the widget subclass half
 void plot_expose_request(Plot *p);
-void plot_expose_request_line(Plot *p, int num);
 void plot_expose_request_partial(Plot *p,int x, int y, int w, int h);
 void plot_set_x_scale(Plot *p, scalespace x);
 void plot_set_y_scale(Plot *p, scalespace y);

Modified: trunk/sushivision/scale.c
===================================================================
--- trunk/sushivision/scale.c	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/scale.c	2007-01-29 13:58:10 UTC (rev 12389)
@@ -214,6 +214,10 @@
   return val;
 }
 
+double scalespace_scaledel(scalespace *from, scalespace *to){
+  return from->step_val / from->step_pixel * from->m / to->m * to->step_pixel / to->step_val;
+}
+
 int scalespace_mark(scalespace *s, int num){
   return s->first_pixel + s->step_pixel*num;
 }
@@ -247,6 +251,7 @@
   ret.init = 1;
   ret.pixels=pixels;
   ret.legend=name;
+  ret.spacing = max_spacing;
 
   if(orange < 1e-30*pixels){
     // insufficient to safeguard the int64 first var below all by

Modified: trunk/sushivision/scale.h
===================================================================
--- trunk/sushivision/scale.h	2007-01-29 13:18:50 UTC (rev 12388)
+++ trunk/sushivision/scale.h	2007-01-29 13:58:10 UTC (rev 12389)
@@ -36,11 +36,12 @@
 
   int init;
   int pixels;
+  int spacing;
 } scalespace;
 
 int del_depth(double A, double B);
 extern char **scale_generate_labels(unsigned scalevals, double *scaleval_list);
-
+extern double scalespace_scaledel(scalespace *from, scalespace *to);
 extern double scalespace_value(scalespace *s, double pixel);
 extern double scalespace_pixel(scalespace *s, double val);
 extern int scalespace_mark(scalespace *s, int num);



More information about the commits mailing list