[xiph-commits] r11955 - in trunk: . sushivision

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Thu Oct 26 19:14:26 PDT 2006


Author: xiphmont
Date: 2006-10-26 19:14:20 -0700 (Thu, 26 Oct 2006)
New Revision: 11955

Added:
   trunk/sushivision/
   trunk/sushivision/Makefile
   trunk/sushivision/dimension.c
   trunk/sushivision/example_submain.c
   trunk/sushivision/internal.h
   trunk/sushivision/main.c
   trunk/sushivision/mapping.c
   trunk/sushivision/mapping.h
   trunk/sushivision/objective.c
   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
   trunk/sushivision/slice.c
   trunk/sushivision/slice.h
   trunk/sushivision/slider.c
   trunk/sushivision/slider.h
   trunk/sushivision/sushi-gtkrc
   trunk/sushivision/sushi.h
   trunk/sushivision/sushivision.h
Log:
Initial commit of sushivision code.  BUild system incomplete, example 
submain doesn't do much of anything, lots of funcitonality still 
missing.



Added: trunk/sushivision/Makefile
===================================================================
--- trunk/sushivision/Makefile	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/Makefile	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,59 @@
+# Fuck Automake
+# Fuck the horse it rode in on
+# and Fuck its little dog Libtool too
+
+TARGET  = sushivision
+CC      = gcc 
+LD      = gcc
+INSTALL = install
+STRIP   = strip
+PREFIX  = /usr/local
+BINDIR  = $(PREFIX)/bin
+ETCDIR  = /etc/$(TARGET)
+MANDIR  = $(PREFIX)/man
+
+SRC  = main.c scale.c plot.c slider.c slice.c panel.c panel-2d.c mapping.c dimension.c objective.c example_submain.c
+OBJ  = main.o scale.o plot.o slider.o slice.o panel.o panel-2d.o mapping.o dimension.o objective.o example_submain.o
+INC  = sushivision.h
+LIBS = -lpthread
+CAIROVER =  >= 1.0.0
+GTKVER   =  >= 2.8.0
+GCF  = `pkg-config --static --cflags "gtk+-2.0 $(GTKVER) cairo $(CAIROVER) freetype2 gthread-2.0"`
+LDF  = -pthread -L/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lpangocairo-1.0 -lpango-1.0 -lgobject-2.0 -lgmodule-2.0 -ldl -lm -lfontconfig -lpng12 -lXrender -lX11 -lpthread -lfreetype -lz -lgthread-2.0 -lglib-2.0 -lcairo
+
+all:    
+	pkg-config --cflags "gtk+-2.0 $(GTKVER) cairo $(CAIROVER) freetype2" 1>/dev/null
+	$(MAKE) target CFLAGS='-O2 -ffast-math $(GCF) $(ADD_DEF)'
+	$(STRIP) $(TARGET)
+
+debug:
+	pkg-config --cflags "gtk+-2.0 $(GTKVER) cairo $(CAIROVER) freetype2" 1>/dev/null
+	$(MAKE) target CFLAGS='-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)'
+
+profile:
+	pkg-config --cflags "gtk+-2.0 $(GTKVER) cairo $(CAIROVER) freetype2" 1>/dev/null
+	$(MAKE) target CFLAGS='-pg -g -O2 -ffast-math $(GCF) $(ADD_DEF)' LIBS='-lgprof-helper'
+
+clean:
+	rm -f $(OBJ) *.d *.d.* gmon.out $(TARGET)
+
+distclean: clean
+	rm -f *~
+
+%.d: %.c
+	$(CC) -M $(CFLAGS) $< > $@.$$$$; sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$
+
+ifeq ($(MAKECMDGOALS),target)
+include $(SRC:.c=.d)
+endif
+
+ifeq ($(MAKECMDGOALS),static-target)
+include $(SRC:.c=.d)
+endif
+
+target:  $(OBJ) 
+	$(LD) $(OBJ) $(CFLAGS) -o $(TARGET) $(LIBS) $(LDF)
+
+install: target
+	$(INSTALL) -d -m 0755 $(BINDIR)
+	$(INSTALL) -m 0755 $(TARGET) $(BINDIR)

Added: trunk/sushivision/dimension.c
===================================================================
--- trunk/sushivision/dimension.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/dimension.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,103 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sushivision.h"
+#include "internal.h"
+#include "scale.h"
+
+int sushiv_dim_set_scale(sushiv_dimension_t *d, unsigned scalevals, double *scaleval_list){
+  int i;
+
+  if(scalevals<2){
+    fprintf(stderr,"Scale requires at least two scale values.");
+    return -EINVAL;
+  }
+
+  if(d->scale_val_list)free(d->scale_val_list);
+  if(d->scale_label_list){
+    for(i=0;i<d->scale_vals;i++)
+      free(d->scale_label_list[i]);
+    free(d->scale_label_list);
+  }
+
+  // copy values
+  d->scale_vals = scalevals;
+  d->scale_val_list = calloc(scalevals,sizeof(*d->scale_val_list));
+  for(i=0;i<(int)scalevals;i++)
+    d->scale_val_list[i] = scaleval_list[i];
+
+  // generate labels
+  d->scale_label_list = scale_generate_labels(scalevals,scaleval_list);
+
+  return 0;
+}
+
+int sushiv_dim_set_scalelabels(sushiv_dimension_t *d, char **scalelabel_list){
+  int i;
+  for(i=0;i<d->scale_vals;i++){
+    if(d->scale_label_list[i])
+      free(d->scale_label_list[i]);
+    d->scale_label_list[i] = strdup(scalelabel_list[i]);
+  }
+  return 0;
+}
+
+int sushiv_new_dimension(sushiv_instance_t *s,
+			 int number,
+			 const char *name,
+			 unsigned scalevals, double *scaleval_list,
+			 int (*callback)(sushiv_dimension_t *),
+			 unsigned flags){
+  sushiv_dimension_t *d;
+  
+  if(number<0){
+    fprintf(stderr,"Dimension number must be >= 0\n");
+    return -EINVAL;
+  }
+
+  if(number<s->dimensions){
+    if(s->dimension_list[number]!=NULL){
+      fprintf(stderr,"Dimension number %d already exists\n",number);
+      return -EINVAL;
+    }
+  }else{
+    if(s->dimensions == 0){
+      s->dimension_list = calloc (number+1,sizeof(*s->dimension_list));
+    }else{
+      s->dimension_list = realloc (s->dimension_list,(number+1) * sizeof(*s->dimension_list));
+      memset(s->dimension_list + s->dimensions, 0, sizeof(*s->dimension_list)*(number + 1 - s->dimensions));
+    }
+    s->dimensions=number+1;
+  }
+
+  d = s->dimension_list[number] = calloc(1, sizeof(**s->dimension_list));
+  d->number = number;
+  d->name = strdup(name);
+  d->flags = flags;
+  d->sushi = s;
+  d->callback = callback;
+  d->panel = NULL;
+  return sushiv_dim_set_scale(d, scalevals, scaleval_list);
+}

Added: trunk/sushivision/example_submain.c
===================================================================
--- trunk/sushivision/example_submain.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/example_submain.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,135 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "sushivision.h"
+
+sushiv_instance_t *s;
+#define todB(x)   ((x)==0?-400.f:log((x)*(x))*4.34294480f)
+
+/* time, blocksz, amp_0, amp_del, freq_0, phase_0, phase_del */
+int funcsize=64;
+double function[64];
+static double fourier_objective(double *d){
+  int i;
+  double re_obj=0;
+  double im_obj=0;
+  double obj;
+
+  for(i=0;i<funcsize;i++){
+    im_obj += function[i] * sin(i*d[4]*2*M_PI);
+    re_obj += function[i] * cos(i*d[4]*2*M_PI);
+  
+  }
+  im_obj/=funcsize;
+  re_obj/=funcsize;
+
+  obj = sqrt(im_obj*im_obj + re_obj*re_obj);
+  return todB(obj);
+
+}
+
+/*
+static double objective(double *d){
+  
+  return .5;
+
+}
+*/
+
+static int time_callback(sushiv_dimension_t *d){
+
+
+  return 1; // indicate that default processing chain should continue
+}
+
+static int blocksize_callback(sushiv_dimension_t *d){
+
+
+  return 1; // indicate that default processing chain should continue
+}
+
+int sushiv_submain(int argc, char *argv[]){
+  int i;
+
+  for(i=0;i<funcsize;i++)
+    function[i]=sin(i*.1)*.1;
+
+
+  s=sushiv_new_instance();
+
+  sushiv_new_dimension(s,0,"time",
+		       4,(double []){0,1024,2048,4096},
+		       time_callback,
+		       0);
+  sushiv_new_dimension(s,1,"blocksize",
+		       8,(double []){64,128,256,512,1024,2048,4096,8192},
+		       blocksize_callback,
+		       0);
+
+  sushiv_new_dimension(s,2,"amplitude",
+		       9,(double []){-96,-84,-72,-60,-48,-36,-24,-12,0},
+		       NULL,
+		       SUSHIV_X_RANGE|SUSHIV_Y_RANGE);
+  sushiv_new_dimension(s,3,"amplitude delta",
+		       9,(double []){-96,-48,-24,-12,0,12,24,48,96},
+		       NULL,
+		       SUSHIV_X_RANGE|SUSHIV_Y_RANGE);
+  sushiv_new_dimension(s,4,"frequency",
+		       6,(double []){0,.1,.2,.3,.4,.5},
+		       NULL,
+		       SUSHIV_X_RANGE|SUSHIV_Y_RANGE);
+  sushiv_new_dimension(s,5,"phase",
+		       3,(double []){-.5,0,.5},
+		       NULL,
+		       SUSHIV_X_RANGE|SUSHIV_Y_RANGE);
+  sushiv_new_dimension(s,6,"phase delta",
+		       3,(double []){-10,0,10},
+		       NULL,
+		       SUSHIV_X_RANGE|SUSHIV_Y_RANGE);
+  
+  sushiv_new_objective(s,0,"fourier",fourier_objective,0);
+  //sushiv_new_objective(s,1,"fit",fit_objective,0);
+  //sushiv_new_objective(s,2,"waveform",fourier_objective,0);
+
+  sushiv_new_panel_2d(s,0,"fourier objective",8,
+		      (double []){-96,-48,-36,-24,-12,-6,0,6},
+		      (int []){0,-1},
+		      (int []){2,3,4,5,6,-1},
+		      0);
+  
+  /*sushiv_linked_panel_1d(s,1,"fourier x slice",8,
+		      (double []){-96,-48,-36,-24,-12,-6,0,6},
+		      0,0,
+		      0);
+  sushiv_linked_panel_1d(s,2,"fourier y slice",8,
+		      (double []){-96,-48,-36,-24,-12,-6,0,6},
+		      0,1,
+		      0);
+  sushiv_new_panel_1d(s,3,"input block",8,
+		      (double []){-96,-48,-36,-24,-12,-6,0,6},
+		      (int *){2,-1},
+		      NULL,
+		      0);*/
+
+  return 0;
+}

Added: trunk/sushivision/internal.h
===================================================================
--- trunk/sushivision/internal.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/internal.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,43 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <signal.h>
+extern void _sushiv_realize_panel(sushiv_panel_t *p);
+extern void _sushiv_clean_exit(int sig);
+extern int _sushiv_new_panel(sushiv_instance_t *s,
+			     int number,
+			     const char *name, 
+			     unsigned scalevals,
+			     double *scaleval_list,
+			     int *objectives,
+			     int *dimensions,
+			     unsigned flags);
+
+extern void _sushiv_panel_dirty_map(sushiv_panel_t *p);
+extern void _sushiv_wake_workers(void);
+
+extern int _sushiv_panel_cooperative_compute(sushiv_panel_t *p);
+
+extern sig_atomic_t _sushiv_exiting;
+
+#define SUSHIV_DIM_MASK 0x003
+#define SUSHIV_X_DIM 0x001
+#define SUSHIV_Y_DIM 0x002

Added: trunk/sushivision/main.c
===================================================================
--- trunk/sushivision/main.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/main.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,179 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <gtk/gtk.h>
+#include <cairo-ft.h>
+#include <pthread.h>
+#include "sushivision.h"
+#include "internal.h"
+
+static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t mc = PTHREAD_COND_INITIALIZER;
+sig_atomic_t _sushiv_exiting=0;
+
+static int instances;
+static sushiv_instance_t **instance_list;
+
+void _sushiv_clean_exit(int sig){
+  _sushiv_exiting = 1;
+
+  pthread_mutex_lock(&m);
+  pthread_cond_broadcast(&mc);
+  pthread_mutex_unlock(&m);
+
+  signal(sig,SIG_IGN);
+  if(sig!=SIGINT)
+    fprintf(stderr,
+            "\nTrapped signal %d; exiting!\n",sig);
+
+  gtk_main_quit();
+  exit(0);
+}
+
+static int num_proccies(){
+  FILE *f = fopen("/proc/cpuinfo","r");
+  char * line = NULL;
+  size_t len = 0;
+  ssize_t read;
+  int num=1,arg;
+
+  if (f == NULL) return 1;
+  while ((read = getline(&line, &len, f)) != -1) {
+    if(sscanf(line,"processor : %d",&arg)==1)
+      if(arg+1>num)num=arg+1;
+  }
+  if (line)
+    free(line);
+
+  fprintf(stderr,"Number of processors: %d\n",num);
+  fclose(f);
+  return num;
+}
+
+void _sushiv_wake_workers(){
+  if(instances){
+    pthread_mutex_lock(&m);
+    pthread_cond_broadcast(&mc);
+    pthread_mutex_unlock(&m);
+  }
+}
+
+static void *worker_thread(void *dummy){
+  pthread_mutex_lock(&m);
+  while(1){
+    if(_sushiv_exiting)break;
+
+    // look for work
+    {
+      int i,j,flag=0;
+      // by instance
+      for(j=0;j<instances;j++){
+	sushiv_instance_t *s = instance_list[j];
+				 
+	for(i=0;i<s->panels;i++){
+	  if(_sushiv_exiting)break;
+	  pthread_mutex_unlock(&m);
+	  flag |= _sushiv_panel_cooperative_compute(s->panel_list[i]);
+	  pthread_mutex_lock(&m);
+	}
+      }
+      if(flag==1)continue;
+    }
+    
+    // nothing to do, wait
+    pthread_cond_wait(&mc,&m);
+  }
+  
+  pthread_mutex_unlock(&m);
+  return 0;
+}
+
+static char * gtkrc_string(){
+
+  return "";
+}
+
+static void sushiv_realize_instance(sushiv_instance_t *s){
+  int i;
+  for(i=0;i<s->panels;i++)
+    _sushiv_realize_panel(s->panel_list[i]);
+}
+
+static void sushiv_realize_all(void){
+  int i;
+  for(i=0;i<instances;i++)
+    sushiv_realize_instance(instance_list[i]);
+}
+
+int main (int argc, char *argv[]){
+  int ret;
+
+  gtk_init (&argc, &argv);
+  g_thread_init (NULL);
+  gdk_threads_init ();
+  gtk_rc_parse_string(gtkrc_string());
+  gtk_rc_add_default_file("sushi-gtkrc");
+
+  ret = sushiv_submain(argc,argv);
+  if(ret)return ret;
+
+  sushiv_realize_all();
+  
+  {
+    pthread_t dummy;
+    int threads = num_proccies();
+    while(threads--)
+      pthread_create(&dummy, NULL, &worker_thread,NULL);
+  }
+
+  signal(SIGINT,_sushiv_clean_exit);
+  //signal(SIGSEGV,_sushiv_clean_exit);
+  gtk_main ();
+
+
+  return 0;
+}
+
+/* externally visible interface */
+
+sushiv_instance_t *sushiv_new_instance(void) {
+  sushiv_instance_t *ret=calloc(1,sizeof(*ret));
+
+  if(instances){
+    instance_list = realloc(instance_list,(instances+1)*sizeof(*instance_list));
+  }else{
+    instance_list = malloc((instances+1)*sizeof(*instance_list));
+  }
+  instance_list[instances] = ret;
+  instances++;
+  
+  return ret;
+}
+

Added: trunk/sushivision/mapping.c
===================================================================
--- trunk/sushivision/mapping.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/mapping.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,213 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include "mapping.h"
+
+static u_int32_t scalloped_colorwheel(double val){
+  if(val<0.)val=0.;
+  if(val>1.)val=1.;
+  {
+    int r,g,b;
+    
+    int category = val*12;
+    int v = rint((val*12 - category)*255.);
+    
+    switch(category){
+    case 0:
+      r=0;g=v;b=0;break;
+    case 1:
+      r=v;g=v;b=0;break;
+    case 2:
+      r=v;g=(v>>1);b=0;break;
+    case 3:
+      r=v;g=0;b=0;break;
+    case 4:
+      r=v;g=0;b=v*3/4;break;
+    case 5:
+      r=v*3/4;g=0;b=v;break;
+    case 6:
+      r=(v>>1);g=0;b=v;break;
+    case 7:
+      r=0;g=0;b=v;break;
+    case 8:
+      r=0;g=v*2/3;b=v;break;
+    case 9:
+      r=0;g=v;b=v;break;
+    case 10:
+      r=v*2/3;g=v;b=v;break;
+    case 11:
+      r=v;g=v;b=v;break;
+    case 12:
+      r=255;g=255;b=255;break;
+    }
+    
+    return (r<<16) + (g<<8) + b;
+  }
+}
+
+#include <stdio.h>
+static u_int32_t smooth_colorwheel(double val){
+  if(val<0)val=0;
+  if(val>1)val=1;
+  {
+    int r,g,b;
+
+    if(val<= (4./7.)){
+      if(val<= (2./7.)){
+	if(val<= (1./7.)){
+	  // 0->1, 0->g
+	  r=0;
+	  g= rint(7.*255.*val);
+	  b=0;
+	  
+	}else{
+	  // 1->2, g->rg
+	  r= rint(7.*255.*val-255.);
+	  g=255;
+	  b=0;
+	  
+	}
+      }else{
+	if(val<=(3./7.)){
+	  // 2->3, rg->r
+	  r= 255;
+	  g= rint(3.*255. - 7.*255.*val);
+	  b=0;
+
+	}else{
+	  // 3->4, r->rb
+	  r= 255;
+	  g= 0.;
+	  b= rint(7.*255.*val-3.*255.);
+	}
+      }
+    }else{
+      if(val<= (5./7.)){
+	// 4->5, rb->b
+	r= rint(5.*255. - 7.*255.*val);
+	g= 0;
+	b= 255;
+
+      }else{
+	if(val<= (6./7.)){
+	  // 5->6, b->bg
+	  r= 0.;
+	  g= rint(7.*255.*val-5.*255.);
+	  b= 255;
+	  
+	}else{
+	  // 6->7, bg->rgb
+	  r= rint(7.*255.*val-6.*255.);
+	  g= 255;
+	  b= 255;
+	  
+	}
+      }
+    }
+    return (r<<16) + (g<<8) + b;
+  }
+}
+
+static u_int32_t grayscale(double val){
+  if(val<0)val=0;
+  if(val>1)val=1;
+  {
+    int g=rint(val*255.);
+    return (g<<16)+(g<<8)+g;
+  }
+}
+
+static u_int32_t grayscale_cont(double val){
+  if(val<0)val=0;
+  if(val>1)val=1;
+  {
+    int g=rint(val*255.);
+    if((g & 0xf) < 0x1) g=0;
+    return (g<<16)+(g<<8)+g;
+  }
+}
+
+static u_int32_t (*mapfunc[])(double)={
+  scalloped_colorwheel,
+  smooth_colorwheel,
+  grayscale,
+  grayscale_cont,
+};
+
+static char *mapnames[]={
+  "scalloped colorwheel",
+  "smooth colorwheel",
+  "grayscale",
+  "grayscale with contours",
+  0
+};
+
+int num_mappings(){
+  int i=0;
+  while(mapnames[i])i++;
+  return i;
+}
+
+char *mapping_name(int i){
+  return mapnames[i];
+}
+
+void mapping_setup(mapping *m, double lo, double hi, int funcnum){
+  m->low = lo;
+  m->high = hi;
+  m->i_range = 1./(hi-lo);
+  m->mapfunc = mapfunc[funcnum];
+}
+
+void mapping_set_lo(mapping *m, double lo){
+  m->low = lo;
+  if(m->high-m->low>0.)
+    m->i_range = 1./(m->high-m->low);
+  else
+    m->i_range=0;
+}
+
+void mapping_set_hi(mapping *m, double hi){
+  m->high=hi;
+  if(m->high-m->low>0.)
+    m->i_range = 1./(m->high-m->low);
+  else
+    m->i_range=0;
+}
+
+void mapping_set_func(mapping *m, int funcnum){
+  m->mapfunc = mapfunc[funcnum];
+}
+
+u_int32_t mapping_calc(mapping *m, double in){
+  if(m->i_range==0){
+    if(in<=m->low)
+      return m->mapfunc(0.);
+    else
+      return m->mapfunc(1.);
+  }else{
+    double val = (in - m->low) * m->i_range;
+    return m->mapfunc(val);
+  }
+}

Added: trunk/sushivision/mapping.h
===================================================================
--- trunk/sushivision/mapping.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/mapping.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,38 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <sys/types.h>
+typedef struct {
+
+  double low;
+  double high;
+  double i_range;
+  u_int32_t (*mapfunc)(double val);
+
+} mapping;
+
+extern int num_mappings();
+extern char *mapping_name(int i);
+extern void mapping_setup(mapping *m, double lo, double hi, int funcnum);
+extern void mapping_set_lo(mapping *m, double lo);
+extern void mapping_set_hi(mapping *m, double hi);
+extern void mapping_set_func(mapping *m, int funcnum);
+extern u_int32_t mapping_calc(mapping *m, double in);

Added: trunk/sushivision/objective.c
===================================================================
--- trunk/sushivision/objective.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/objective.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,64 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "sushivision.h"
+#include "internal.h"
+
+int sushiv_new_objective(sushiv_instance_t *s,
+			 int number,
+			 const char *name,
+			 double(*callback)(double *),
+			 unsigned flags){
+  sushiv_objective_t *o;
+  
+  if(number<0){
+    fprintf(stderr,"Objective number must be >= 0\n");
+    return -EINVAL;
+  }
+
+  if(number<s->objectives){
+    if(s->objective_list[number]!=NULL){
+      fprintf(stderr,"Objective number %d already exists\n",number);
+      return -EINVAL;
+    }
+  }else{
+    if(s->objectives == 0){
+      s->objective_list = calloc (number+1,sizeof(*s->objective_list));
+    }else{
+      s->objective_list = realloc (s->objective_list,(number+1) * sizeof(*s->objective_list));
+      memset(s->objective_list + s->objectives, 0, sizeof(*s->objective_list)*(number +1 - s->objectives));
+    }
+    s->objectives=number+1;
+  }
+
+  o = s->objective_list[number] = calloc(1, sizeof(**s->objective_list));
+  o->number = number;
+  o->name = strdup(name);
+  o->flags = flags;
+  o->sushi = s;
+  o->panel = NULL;
+  o->callback = callback;
+  return 0;
+}

Added: trunk/sushivision/panel-2d.c
===================================================================
--- trunk/sushivision/panel-2d.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/panel-2d.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,671 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <gtk/gtk.h>
+#include <cairo-ft.h>
+#include "sushivision.h"
+#include "mapping.h"
+#include "plot.h"
+#include "slice.h"
+#include "slider.h"
+#include "panel-2d.h"
+#include "internal.h"
+
+static void render_checks(int w, int y, u_int32_t *render){
+  int x,j;
+  /* default checked background */
+  /* 16x16 'mid-checks' */
+  int phase = (y>>4)&1;
+  for(x=0;x<w;){
+    u_int32_t phaseval = 0x505050;
+    if(phase) phaseval = 0x808080;
+    for(j=0;j<16 && x<w;j++,x++)
+      render[x] = phaseval;
+    phase=!phase;
+  }
+}
+
+/* called from idle handler only, and as such, we can be sure we're
+   locked and uninterruptable */
+void _sushiv_panel2d_map_redraw(sushiv_panel_t *p){
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  Plot *plot = PLOT(p2->graph);
+
+  int w,h,x,y,i;
+  w = p2->data_w;
+  h = p2->data_h;
+
+  if(plot){
+    u_int32_t render[w];
+
+    /* iterate */
+    /* by line */
+    for(y = 0; y<h; y++){
+   
+      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];
+
+	/* 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);
+
+	}
+      }
+      
+      /* store result in panel */
+      memcpy(plot->datarect+y*w,render,w*sizeof(*render));
+    }
+    plot_expose_request(plot);
+  }
+}
+
+static void mapchange_callback_2d(GtkWidget *w,gpointer in){
+  sushiv_objective_t **optr = (sushiv_objective_t **)in;
+  sushiv_objective_t *o = *optr;
+  sushiv_panel_t *p = o->panel;
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  int onum = optr - p->objective_list;
+
+  mapping_set_func(&p2->mappings[onum],gtk_combo_box_get_active(GTK_COMBO_BOX(w)));
+  
+  //redraw the map slider
+  slider_draw_background(p2->range_scales[onum]);
+  slider_draw(p2->range_scales[onum]);
+  slider_expose(p2->range_scales[onum]);
+    
+  //redraw the plot
+  _sushiv_panel_dirty_map(p);
+}
+
+static void map_callback_2d(void *in){
+  sushiv_objective_t **optr = (sushiv_objective_t **)in;
+  sushiv_objective_t *o = *optr;
+  sushiv_panel_t *p = o->panel;
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  int onum = optr - p->objective_list;
+
+  // recache alpha del */
+  p2->alphadel[onum] = 
+    slider_val_to_del(p2->range_scales[onum],
+		      slider_get_value(p2->range_scales[onum],1));
+
+  //redraw the plot
+  _sushiv_panel_dirty_map(p);
+}
+
+static void update_xy_availability(sushiv_panel_t *p){
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  int i;
+  // update which x/y buttons are pressable */
+  // enable/disable dimension slider thumbs
+  for(i=0;i<p->dimensions;i++){
+    if(p2->dim_xb[i] &&
+       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]))){
+      // make the y insensitive
+      if(p2->dim_yb[i])
+	gtk_widget_set_sensitive(p2->dim_yb[i],FALSE);
+      // set the dim x flag
+      p->dimension_list[i]->flags |= SUSHIV_X_DIM;
+    }else{
+      // if there is a y, make it sensitive 
+      if(p2->dim_yb[i])
+	gtk_widget_set_sensitive(p2->dim_yb[i],TRUE);
+      // unset dim x flag 
+      p->dimension_list[i]->flags &= ~SUSHIV_X_DIM;
+    }
+    if(p2->dim_yb[i] &&
+       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i]))){
+      // make the x insensitive
+      if(p2->dim_xb[i])
+	gtk_widget_set_sensitive(p2->dim_xb[i],FALSE);
+      // set the dim y flag
+      p->dimension_list[i]->flags |= SUSHIV_Y_DIM;
+    }else{
+      // if there is a x, make it sensitive 
+      if(p2->dim_xb[i])
+	gtk_widget_set_sensitive(p2->dim_xb[i],TRUE);
+      // unset dim y flag 
+      p->dimension_list[i]->flags &= ~SUSHIV_Y_DIM;
+    }
+    if((p2->dim_xb[i] &&
+	gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]))) ||
+       (p2->dim_yb[i] &&
+	gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i])))){
+      // make all thumbs visible 
+      slider_set_thumb_active(p2->dim_scales[i],0,1);
+      slider_set_thumb_active(p2->dim_scales[i],2,1);
+    }else{
+      // make bracket thumbs invisible */
+      slider_set_thumb_active(p2->dim_scales[i],0,0);
+      slider_set_thumb_active(p2->dim_scales[i],2,0);
+    }
+  } 
+}
+
+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_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  double work[w];
+  double inv_w = 1./w;
+  int i,j;
+
+  render_checks(w,y,render);
+  gdk_threads_enter (); // misuse me as a global mutex
+  
+  /* by objective */
+  for(i=0;i<p->objectives;i++){
+    sushiv_objective_t *o = p->objective_list[i];
+    double alpha = p2->alphadel[i];
+    
+    gdk_threads_leave (); // misuse me as a global mutex
+    
+    /* by x */
+    for(j=0;j<w;j++){
+
+      /* compute value for this objective for this pixel */
+      dim_vals[x_d] = (x_max-x_min) * inv_w * j;
+      work[j] = o->callback(dim_vals);
+      
+    }
+    
+    gdk_threads_enter (); // misuse me as a global mutex
+    if(p2->serialno == serialno){
+      
+      /* map/render result */
+      for(j=0;j<w;j++){
+	double val = slider_val_to_del(p2->range_scales[i],work[j]);
+	work[j] = val;
+	
+	if(!isnan(val) && val>=alpha)
+	  render[j] = mapping_calc(p2->mappings+i,val);
+	
+      }
+      
+      /* store result in panel */
+      memcpy(p2->data_rect[i]+y*w,work,w*sizeof(*work));
+    }else
+      break;
+  }
+  gdk_threads_leave (); // misuse me as a global mutex 
+}
+
+static int v_swizzle(int y, int height){
+  int yy = height >> 5;
+  if(y < yy)
+    return (y<<5)+31;
+
+  y -= yy;
+  yy = (height+16) >> 5;
+  if(y < yy)
+    return (y<<5)+15;
+
+  y -= yy;
+  yy = (height+8) >> 4;
+  if(y < yy)
+    return (y<<4)+7;
+
+  y -= yy;
+  yy = (height+4) >> 3;
+  if(y < yy)
+    return (y<<3)+3;
+
+  y -= yy;
+  yy = (height+2) >> 2;
+  if(y < yy)
+    return (y<<2)+1;
+
+  y -= yy;
+  return y<<1;
+}
+
+// call only from main gtk thread!
+void _mark_recompute_2d(sushiv_panel_t *p){
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  Plot *plot = PLOT(p2->graph);
+
+  if(plot && GTK_WIDGET_REALIZED(GTK_WIDGET(plot))){
+    if(p2->data_w != plot->w.allocation.width ||
+       p2->data_h != plot->w.allocation.height){
+      if(p2->data_rect){
+	int i;
+	for(i=0;i<p->objectives;i++)
+	  free(p2->data_rect[i]);
+	free(p2->data_rect);
+	p2->data_rect = NULL;
+      }
+      
+    }
+    
+    p2->data_w = plot->w.allocation.width;
+    p2->data_h = plot->w.allocation.height;
+    p2->serialno++;
+    p2->last_line = 0;
+    
+    if(!p2->data_rect){
+      int i;
+      // allocate it
+      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));
+    }
+    {
+      int i,j;
+      // 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;
+      _sushiv_panel2d_map_redraw(p);
+    }
+    
+    _sushiv_wake_workers();
+  }
+}
+
+static void recompute_callback_2d(void *ptr){
+  sushiv_panel_t *p = (sushiv_panel_t *)ptr;
+  _mark_recompute_2d(p);
+}
+
+static void dim_callback_2d(void *in){
+  sushiv_dimension_t **dptr = (sushiv_dimension_t **)in;
+  sushiv_dimension_t *d = *dptr;
+  sushiv_panel_t *p = d->panel;
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  int dnum = dptr - p->dimension_list;
+
+  int axisp = (d->flags & SUSHIV_DIM_MASK);
+
+  d->val = slider_get_value(p2->dim_scales[dnum],1);
+
+  // if the mid slider of a non-axis dimension changed, rerender
+  if(!axisp){
+    _mark_recompute_2d(p);
+    return;
+  }else{
+    
+    // if the mid slider of an axis dimension changed, move crosshairs
+    double x=0,y=0;
+    int i;
+    
+    for(i=0;i<p->dimensions;i++){
+      sushiv_dimension_t *d = p->dimension_list[i];
+      sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+      if(d->flags & SUSHIV_X_DIM)
+	x = slider_get_value(p2->dim_scales[i],1);
+      if(d->flags & SUSHIV_Y_DIM)
+	y = slider_get_value(p2->dim_scales[i],1);
+      
+    }
+    
+    plot_set_crosshairs(PLOT(p2->graph),x,y);
+
+  }
+
+}
+
+static void bracket_callback_2d(void *in){
+  sushiv_dimension_t **dptr = (sushiv_dimension_t **)in;
+  sushiv_dimension_t *d = *dptr;
+  sushiv_panel_t *p = d->panel;
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  int dnum = dptr - p->dimension_list;
+
+  int axisp = (d->flags & SUSHIV_DIM_MASK);
+
+  d->bracket[0] = slider_get_value(p2->dim_scales[dnum],0);
+  d->bracket[1] = slider_get_value(p2->dim_scales[dnum],2);
+
+  // if the bracketing of an axis dimension changed, rerender
+  if(axisp)
+    _mark_recompute_2d(p);
+ 
+}
+
+static void dimchange_callback_2d(GtkWidget *button,gpointer in){
+  sushiv_panel_t *p = (sushiv_panel_t *)in;
+
+  update_xy_availability(p);
+  _mark_recompute_2d(p);
+}
+
+static void crosshairs_callback(void *in){
+  sushiv_panel_t *p = (sushiv_panel_t *)in;
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  double x=PLOT(p2->graph)->selx_val;
+  double y=PLOT(p2->graph)->sely_val;
+  int i;
+  
+  for(i=0;i<p->dimensions;i++){
+    sushiv_dimension_t *d = p->dimension_list[i];
+    sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+    if(d->flags & SUSHIV_X_DIM)
+      slider_set_value(p2->dim_scales[i],1,x);
+    if(d->flags & SUSHIV_Y_DIM)
+      slider_set_value(p2->dim_scales[i],1,y);
+  }
+}
+
+// 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
+int _sushiv_panel_cooperative_compute_2d(sushiv_panel_t *p){
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  Plot *plot;
+
+  int w,h,i,d;
+  int serialno;
+  double x_min, x_max;
+  double y_min, y_max;
+  double invh;
+  int x_d=-1, y_d=-1;
+
+  // lock during setup
+  gdk_threads_enter ();
+  w = p2->data_w;
+  h = p2->data_h;
+
+  if(p2->last_line>=h){
+      gdk_threads_leave ();
+      return 0;
+  }
+
+  plot = PLOT(p2->graph);
+  serialno = p2->serialno;
+  invh = 1./h;
+  d = p->dimensions;
+
+  /* render using local dimension array; several threads will be
+     computing objectives */
+  double dim_vals[d];
+
+  /* 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];
+
+  /* which dim is our x?  Our y? */
+  for(i=0;i<d;i++){
+    sushiv_dimension_t *dim = p->dimension_list[i];
+    if((dim->flags & SUSHIV_DIM_MASK) == SUSHIV_X_DIM ){
+      x_d = dim->number;
+      x_min = dim->bracket[0];
+      x_max = dim->bracket[1];
+      break;
+    }
+  }
+
+  for(i=0;i<d;i++){
+    sushiv_dimension_t *dim = p->dimension_list[i];
+    if((dim->flags & SUSHIV_DIM_MASK) == SUSHIV_Y_DIM){
+      y_d = dim->number;
+      y_min = dim->bracket[0];
+      y_max = dim->bracket[1];
+      break;
+    }
+  }
+
+  // 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<d;i++){
+    sushiv_dimension_t *dim = p->dimension_list[i];
+    dim_vals[i]=dim->val;
+  }
+
+  // update scales if we're just starting
+  if(p2->last_line==0){
+    plot_set_x_scale(PLOT(p2->graph),x_min,x_max);
+    plot_set_y_scale(PLOT(p2->graph),y_min,y_max); 
+  }
+
+  /* iterate */
+  /* by line */
+  while(!_sushiv_exiting){
+    int last = p2->last_line;
+    int y;
+    if(plot->w.allocation.height != h)break;
+    if(last>=h)break;
+    p2->last_line++;
+
+    /* unlock for computation */
+    gdk_threads_leave ();
+    y = v_swizzle(last,h);
+    dim_vals[y_d]= (y_max - y_min) * h * y;
+
+    /* compute line */
+    compute_one_line_2d(p, serialno, y, x_d, x_min, x_max, w, dim_vals, render);
+
+    /* move rendered line back into widget */
+    gdk_threads_enter ();
+    if(p2->serialno == serialno){
+      u_int32_t *line = plot_get_background_line(plot, y);
+      memcpy(line,render,w*sizeof(*render));
+      plot_expose_request_line(plot,y);
+    }else
+      break;
+  }
+   
+  gdk_threads_leave ();
+  return 1;
+}
+
+void _sushiv_realize_panel2d(sushiv_panel_t *p){
+  sushiv_panel2d_t *p2 = (sushiv_panel2d_t *)p->internal;
+  int i;
+  int lo = p->scale_val_list[0];
+  int hi = p->scale_val_list[p->scale_vals-1];
+
+  p2->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  g_signal_connect_swapped (G_OBJECT (p2->toplevel), "delete-event",
+			    G_CALLBACK (_sushiv_clean_exit), (void *)SIGINT);
+ 
+  p2->top_table = gtk_table_new(2 + p->objectives,5,0);
+  gtk_container_add (GTK_CONTAINER (p2->toplevel), p2->top_table);
+  gtk_container_set_border_width (GTK_CONTAINER (p2->toplevel), 5);
+  
+  p2->dim_table = gtk_table_new(p->dimensions,6,0);
+  gtk_table_attach(GTK_TABLE(p2->top_table),p2->dim_table,0,5,1+p->objectives,2+p->objectives,
+		   GTK_EXPAND|GTK_FILL,0,0,5);
+  
+  /* graph */
+  p2->graph = GTK_WIDGET(plot_new(recompute_callback_2d,p,
+				  crosshairs_callback,p)); 
+  gtk_table_attach(GTK_TABLE(p2->top_table),p2->graph,0,5,0,1,
+		   GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,5);
+
+  /* objective sliders */
+  p2->range_scales = calloc(p->objectives,sizeof(*p2->range_scales));
+  p2->alphadel = calloc(p->objectives,sizeof(*p2->alphadel));
+  p2->mappings = calloc(p->objectives,sizeof(*p2->mappings));
+  for(i=0;i<p->objectives;i++){
+    GtkWidget **sl = calloc(3,sizeof(*sl));
+    sushiv_objective_t *o = p->objective_list[i];
+
+    /* label */
+    GtkWidget *label = gtk_label_new(o->name);
+    gtk_table_attach(GTK_TABLE(p2->top_table),label,0,1,i+1,i+2,
+		     0,0,10,0);
+    
+    /* mapping pulldown */
+    {
+      GtkWidget *menu=gtk_combo_box_new_text();
+      int j;
+      for(j=0;j<num_mappings();j++)
+	gtk_combo_box_append_text (GTK_COMBO_BOX (menu), mapping_name(j));
+      gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+      g_signal_connect (G_OBJECT (menu), "changed",
+			G_CALLBACK (mapchange_callback_2d), p->objective_list+i);
+      gtk_table_attach(GTK_TABLE(p2->top_table),menu,4,5,i+1,i+2,
+		       GTK_SHRINK,GTK_SHRINK,5,0);
+    }
+
+    /* the range mapping slices/slider */ 
+    sl[0] = slice_new(map_callback_2d,p->objective_list+i);
+    sl[1] = slice_new(map_callback_2d,p->objective_list+i);
+    sl[2] = slice_new(map_callback_2d,p->objective_list+i);
+
+    gtk_table_attach(GTK_TABLE(p2->top_table),sl[0],1,2,i+1,i+2,
+		     GTK_EXPAND|GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(p2->top_table),sl[1],2,3,i+1,i+2,
+		     GTK_EXPAND|GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(p2->top_table),sl[2],3,4,i+1,i+2,
+		     GTK_EXPAND|GTK_FILL,0,0,0);
+    p2->range_scales[i] = slider_new((Slice **)sl,3,p->scale_label_list,p->scale_val_list,
+				    p->scale_vals,SLIDER_FLAG_INDEPENDENT_MIDDLE);
+
+    slice_thumb_set((Slice *)sl[0],lo);
+    slice_thumb_set((Slice *)sl[1],lo);
+    slice_thumb_set((Slice *)sl[2],hi);
+    mapping_setup(&p2->mappings[i],0.,1.,0);
+    slider_set_gradient(p2->range_scales[i], &p2->mappings[i]);
+  }
+
+  GtkWidget *first_x = NULL;
+  GtkWidget *first_y = NULL;
+  GtkWidget *pressed_y = NULL;
+  p2->dim_scales = calloc(p->dimensions,sizeof(*p2->dim_scales));
+  p2->dim_xb = calloc(p->dimensions,sizeof(*p2->dim_xb));
+  p2->dim_yb = calloc(p->dimensions,sizeof(*p2->dim_yb));
+
+  for(i=0;i<p->dimensions;i++){
+    GtkWidget **sl = calloc(3,sizeof(*sl));
+    sushiv_dimension_t *d = p->dimension_list[i];
+
+    /* label */
+    GtkWidget *label = gtk_label_new(d->name);
+    gtk_table_attach(GTK_TABLE(p2->dim_table),label,0,1,i,i+1,
+		     0,0,10,0);
+    
+    /* x/y radio buttons */
+    if(d->flags & SUSHIV_X_RANGE){
+      if(first_x)
+	p2->dim_xb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_x),"X");
+      else{
+	first_x = p2->dim_xb[i] = gtk_radio_button_new_with_label(NULL,"X");
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_xb[i]),TRUE);
+      }
+      gtk_table_attach(GTK_TABLE(p2->dim_table),p2->dim_xb[i],1,2,i,i+1,
+		       0,0,10,0);
+    }
+    
+    if(d->flags & SUSHIV_Y_RANGE){
+      if(first_y)
+	p2->dim_yb[i] = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(first_y),"Y");
+      else
+	first_y = p2->dim_yb[i] = gtk_radio_button_new_with_label(NULL,"Y");
+      if(!pressed_y && p2->dim_xb[i]!=first_x){
+	pressed_y = p2->dim_yb[i];
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(p2->dim_yb[i]),TRUE);
+      }
+      gtk_table_attach(GTK_TABLE(p2->dim_table),p2->dim_yb[i],2,3,i,i+1,
+		       0,0,10,0);
+    }
+
+    /* the dimension slices/slider */ 
+    sl[0] = slice_new(bracket_callback_2d,p->dimension_list+i);
+    sl[1] = slice_new(dim_callback_2d,p->dimension_list+i);
+    sl[2] = slice_new(bracket_callback_2d,p->dimension_list+i);
+
+    gtk_table_attach(GTK_TABLE(p2->dim_table),sl[0],3,4,i,i+1,
+		     GTK_EXPAND|GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(p2->dim_table),sl[1],4,5,i,i+1,
+		     GTK_EXPAND|GTK_FILL,0,0,0);
+    gtk_table_attach(GTK_TABLE(p2->dim_table),sl[2],5,6,i,i+1,
+		     GTK_EXPAND|GTK_FILL,0,0,0);
+
+    p2->dim_scales[i] = slider_new((Slice **)sl,3,d->scale_label_list,d->scale_val_list,
+				   d->scale_vals,0);
+
+    slice_thumb_set((Slice *)sl[0],d->scale_val_list[0]);
+    slice_thumb_set((Slice *)sl[1],0);
+    slice_thumb_set((Slice *)sl[2],d->scale_val_list[d->scale_vals-1]);
+
+  }
+  for(i=0;i<p->dimensions;i++){
+    if(p2->dim_xb[i])
+      g_signal_connect (G_OBJECT (p2->dim_xb[i]), "toggled",
+			  G_CALLBACK (dimchange_callback_2d), p);
+    if(p2->dim_yb[i])
+      g_signal_connect (G_OBJECT (p2->dim_yb[i]), "toggled",
+			G_CALLBACK (dimchange_callback_2d), p);
+  }
+  update_xy_availability(p);
+
+
+  gtk_widget_realize(p2->toplevel);
+  gtk_widget_realize(p2->graph);
+  gtk_widget_show_all(p2->toplevel);
+}
+
+int sushiv_new_panel_2d(sushiv_instance_t *s,
+			int number,
+			const char *name, 
+			unsigned scalevals,
+			double *scaleval_list,
+			       int *objectives,
+			int *dimensions,
+			unsigned flags){
+  
+  int ret = _sushiv_new_panel(s,number,name,scalevals,scaleval_list,
+			      objectives,dimensions,flags);
+  sushiv_panel_t *p;
+  sushiv_panel2d_t *p2;
+
+  if(ret<0)return ret;
+  p = s->panel_list[number];
+  p2 = calloc(1, sizeof(*p2));
+  p->internal = p2;
+  p->type = SUSHIV_PANEL_2D;
+
+  return 0;
+}
+

Added: trunk/sushivision/panel-2d.h
===================================================================
--- trunk/sushivision/panel-2d.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/panel-2d.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,49 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+typedef struct sushiv_panel2d {
+
+  GtkWidget *toplevel;
+  GtkWidget *graph;
+  GtkWidget *top_table;
+  GtkWidget *dim_table;
+
+  int data_w;
+  int data_h;
+  int serialno;
+  double **data_rect;
+
+  mapping *mappings;
+  Slider    **range_scales;
+  GtkWidget **range_pulldowns;
+  double *alphadel;
+
+  Slider **dim_scales;
+  GtkWidget **dim_xb;
+  GtkWidget **dim_yb;
+
+  int last_line;
+  int dirty_flag;
+} sushiv_panel2d_t;
+
+extern void _sushiv_realize_panel2d(sushiv_panel_t *p);
+extern int _sushiv_panel_cooperative_compute_2d(sushiv_panel_t *p);
+extern void _sushiv_panel2d_map_redraw(sushiv_panel_t *p);

Added: trunk/sushivision/panel.c
===================================================================
--- trunk/sushivision/panel.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/panel.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,205 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <gtk/gtk.h>
+#include <cairo-ft.h>
+#include "sushivision.h"
+#include "mapping.h"
+#include "plot.h"
+#include "slice.h"
+#include "slider.h"
+#include "panel-2d.h"
+#include "internal.h"
+
+void _sushiv_realize_panel(sushiv_panel_t *p){
+  if(!p->realized){
+    switch(p->type){
+    case SUSHIV_PANEL_1D:
+      //_sushiv_realize_panel1d(p);
+      break;
+    case SUSHIV_PANEL_2D:
+      _sushiv_realize_panel2d(p);
+      break;
+    }
+    p->realized=1;
+  }
+}
+
+int sushiv_panel_set_scale(sushiv_panel_t *d, unsigned scalevals, 
+			   double *scaleval_list){
+  int i;
+
+  if(scalevals<2){
+    fprintf(stderr,"Scale requires at least two scale values.");
+    return -EINVAL;
+  }
+
+  if(d->scale_val_list)free(d->scale_val_list);
+  if(d->scale_label_list){
+    for(i=0;i<d->scale_vals;i++)
+      free(d->scale_label_list[i]);
+    free(d->scale_label_list);
+  }
+
+  // copy values
+  d->scale_vals = scalevals;
+  d->scale_val_list = calloc(scalevals,sizeof(*d->scale_val_list));
+  for(i=0;i<(int)scalevals;i++)
+    d->scale_val_list[i] = scaleval_list[i];
+
+  // generate labels
+  d->scale_label_list = scale_generate_labels(scalevals,scaleval_list);
+
+  return 0;
+}
+
+int sushiv_panel_set_scalelabels(sushiv_panel_t *d, char **scalelabel_list){
+  int i;
+  for(i=0;i<d->scale_vals;i++){
+    if(d->scale_label_list[i])
+      free(d->scale_label_list[i]);
+    d->scale_label_list[i] = strdup(scalelabel_list[i]);
+  }
+  return 0;
+}
+
+static void _sushiv_panel_map_redraw(sushiv_panel_t *p){
+  if(p->maps_dirty){
+    p->maps_dirty = 0;
+    switch(p->type){
+    case SUSHIV_PANEL_1D:
+      //_sushiv_panel1d_map_redraw(p);
+      break;
+    case SUSHIV_PANEL_2D:
+      _sushiv_panel2d_map_redraw(p);
+      break;
+    }
+  }
+}
+
+int _sushiv_panel_cooperative_compute(sushiv_panel_t *p){
+  if(p->realized){
+    if(p->type == SUSHIV_PANEL_2D)
+      return _sushiv_panel_cooperative_compute_2d(p);
+  }
+  return 0;
+}
+
+/* doesn't take an unbounded period, but shouldn't be
+   synchronous in the interests of responsiveness. */
+static gboolean _map_idle_work(gpointer ptr){
+  sushiv_instance_t *s = (sushiv_instance_t *)ptr;
+  int i;
+  
+  for(i=0;i<s->panels;i++)
+    _sushiv_panel_map_redraw(s->panel_list[i]);
+  
+  return FALSE;
+}
+
+
+void _sushiv_panel_dirty_map(sushiv_panel_t *p){
+  p->maps_dirty = 1;
+  g_idle_add(_map_idle_work,p->sushi);
+}
+
+int _sushiv_new_panel(sushiv_instance_t *s,
+		      int number,
+		      const char *name, 
+		      unsigned scalevals,
+		      double *scaleval_list,
+		      int *objectives,
+		      int *dimensions,
+		      unsigned flags){
+  
+  sushiv_panel_t *p;
+  int i;
+
+  if(number<0){
+    fprintf(stderr,"Panel number must be >= 0\n");
+    return -EINVAL;
+  }
+
+  if(number<s->panels){
+    if(s->panel_list[number]!=NULL){
+      fprintf(stderr,"Panel number %d already exists\n",number);
+      return -EINVAL;
+    }
+  }else{
+    if(s->panels == 0){
+      s->panel_list = calloc (number+1,sizeof(*s->panel_list));
+    }else{
+      s->panel_list = realloc (s->panel_list,(number+1) * sizeof(*s->panel_list));
+      memset(s->panel_list + s->panels, 0, sizeof(*s->panel_list)*(number+1 - s->panels));
+    }
+    s->panels = number+1; 
+  }
+
+  p = s->panel_list[number] = calloc(1, sizeof(**s->panel_list));
+
+  p->number = number;
+  p->name = strdup(name);
+  p->flags = flags;
+  p->sushi = s;
+  
+  i=0;
+  while(objectives && objectives[i]>=0)i++;
+  p->objectives = i;
+  p->objective_list = malloc(i*sizeof(*p->objective_list));
+  for(i=0;i<p->objectives;i++){
+    sushiv_objective_t *o = s->objective_list[objectives[i]];
+    if(o->panel){
+      fprintf(stderr,"Objective %d already in use with another panel\n",o->number);
+      return -EINVAL;
+    }
+    o->panel = p;
+    p->objective_list[i] = o;
+    
+  }
+
+  i=0;
+  while(dimensions && dimensions[i]>=0)i++;
+  p->dimensions = i;
+  p->dimension_list = malloc(i*sizeof(*p->dimension_list));
+  for(i=0;i<p->dimensions;i++){
+    sushiv_dimension_t *d = s->dimension_list[dimensions[i]];
+    if(d->panel){
+      fprintf(stderr,"Dimension %d already in use with another panel\n",d->number);
+      return -EINVAL;
+    }
+    d->panel = p;
+    p->dimension_list[i] = d;
+  }
+
+  sushiv_panel_set_scale(p, scalevals, scaleval_list);
+
+  return number;
+}
+

Added: trunk/sushivision/plot.c
===================================================================
--- trunk/sushivision/plot.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/plot.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,492 @@
+/*
+ *
+ *  sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#define _GNU_SOURCE
+#include <gtk/gtk.h>
+#include <gtk/gtkmain.h>
+#include <gdk/gdk.h>
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include "plot.h"
+
+static GtkWidgetClass *parent_class = NULL;
+
+static void draw_scales(Plot *p){
+  GtkWidget *widget = GTK_WIDGET(p);
+  cairo_t *c = cairo_create(p->fore);
+  int i=0,x,y;
+  char buffer[80];
+
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+  cairo_set_source_rgba (c, 1,1,1,1);
+  cairo_paint(c);
+  cairo_restore(c);
+
+  // draw all axis lines, then stroke
+  cairo_save(c);
+  cairo_set_operator(c,CAIRO_OPERATOR_XOR); 
+
+  cairo_set_line_width(c,1.);
+  cairo_set_source_rgba(c,.7,.7,1.,.3);
+
+  i=0;
+  x=scalespace_mark(&p->x,i++);
+  while(x < widget->allocation.width){
+    cairo_move_to(c,x+.5,0);
+    cairo_line_to(c,x+.5,widget->allocation.height);
+    x=scalespace_mark(&p->x,i++);
+  }
+
+  i=0;
+  y=scalespace_mark(&p->y,i++);
+  while(y < widget->allocation.height){
+    cairo_move_to(c,0,widget->allocation.height-y+.5);
+    cairo_line_to(c,widget->allocation.width,widget->allocation.height-y+.5);
+    y=scalespace_mark(&p->y,i++);
+  }
+  cairo_stroke(c);
+  cairo_restore(c);
+
+  // text labels
+  cairo_select_font_face (c, "Sans",
+			  CAIRO_FONT_SLANT_NORMAL,
+			  CAIRO_FONT_WEIGHT_NORMAL);
+  cairo_set_font_size (c, 10);
+  cairo_set_line_width(c,2);
+
+  i=0;
+  y=scalespace_mark(&p->y,i);
+  scalespace_label(&p->y,i++,buffer);
+
+  while(y < widget->allocation.height){
+    cairo_text_extents_t extents;
+    cairo_text_extents (c, buffer, &extents);
+
+    if(y - extents.height > 0){
+      
+      double yy = widget->allocation.height-y+.5-(extents.height/2 + extents.y_bearing);
+
+      cairo_move_to(c,2, yy);
+      cairo_set_source_rgba(c,0,0,0,.5);
+      cairo_text_path (c, buffer);  
+      cairo_stroke(c);
+      
+      cairo_set_source_rgba(c,1.,1.,1.,1.);
+      cairo_move_to(c,2, yy);
+      cairo_show_text (c, buffer);
+    }
+
+    y=scalespace_mark(&p->y,i);
+    scalespace_label(&p->y,i++,buffer);
+  }
+
+  i=0;
+  x=scalespace_mark(&p->x,i);
+  scalespace_label(&p->x,i++,buffer);
+  
+  {
+    cairo_matrix_t m = {0.,-1., 1.,0.,  0.,widget->allocation.height};
+    cairo_set_matrix(c,&m);
+  }
+
+  while(x < widget->allocation.width){
+    cairo_text_extents_t extents;
+    cairo_text_extents (c, buffer, &extents);
+
+    if(x - extents.height > 0){
+
+      cairo_move_to(c,2, x+.5-(extents.height/2 + extents.y_bearing));
+      cairo_set_source_rgba(c,0,0,0,.5);
+      cairo_text_path (c, buffer);  
+      cairo_stroke(c);
+      
+      cairo_move_to(c,2, x+.5-(extents.height/2 + extents.y_bearing));
+      cairo_set_source_rgba(c,1.,1.,1.,1.);
+      cairo_show_text (c, buffer);
+    }
+
+    x=scalespace_mark(&p->x,i);
+    scalespace_label(&p->x,i++,buffer);
+  }
+
+  cairo_destroy(c);
+}
+
+
+static void plot_init (Plot *p){
+  // instance initialization
+  p->scalespacing = 50;
+  p->x = scalespace_linear(0.0,1.0,400,p->scalespacing);
+  p->y = scalespace_linear(0.0,1.0,200,p->scalespacing);
+}
+
+static void plot_destroy (GtkObject *object){
+  if (GTK_OBJECT_CLASS (parent_class)->destroy)
+    (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+
+  GtkWidget *widget = GTK_WIDGET(object);
+  Plot *p = PLOT (widget);
+  // free local resources
+  if(p->wc){
+    cairo_destroy(p->wc);
+    p->wc=0;
+  }
+  if(p->back){
+    cairo_surface_destroy(p->back);
+    p->back=0;
+  }
+  if(p->fore){
+    cairo_surface_destroy(p->fore);
+    p->fore=0;
+  }
+  if(p->stage){
+    cairo_surface_destroy(p->stage);
+    p->stage=0;
+  }
+
+}
+
+void plot_draw (Plot *p,
+		int x, int y, int w, int h){
+
+  GtkWidget *widget = GTK_WIDGET(p);
+  
+  if (GTK_WIDGET_REALIZED (widget)){
+
+    cairo_t *c = cairo_create(p->stage);
+    cairo_set_source_surface(c,p->back,0,0);
+    cairo_rectangle(c,x,y,w,h);
+    cairo_fill(c);
+    
+    cairo_set_source_surface(c,p->fore,0,0);
+    cairo_rectangle(c,x,y,w,h);
+    cairo_fill(c);
+    
+    // transient foreground
+    cairo_set_source_rgba(c,1.,1.,1.,.6);
+    cairo_set_line_width(c,1.);
+    cairo_move_to(c,0,p->sely+.5);
+    cairo_line_to(c,widget->allocation.width,p->sely+.5);
+    cairo_move_to(c,p->selx+.5,0);
+    cairo_line_to(c,p->selx+.5,widget->allocation.height);
+    cairo_stroke(c);
+
+    cairo_destroy(c);
+
+    // blit to window
+    cairo_set_source_surface(p->wc,p->stage,0,0);
+    cairo_rectangle(p->wc,x,y,w,h);
+    cairo_fill(p->wc);
+
+  }
+}
+
+static gint plot_expose (GtkWidget      *widget,
+			 GdkEventExpose *event){
+  if (GTK_WIDGET_REALIZED (widget)){
+    Plot *p = PLOT (widget);
+
+    int x = event->area.x;
+    int y = event->area.y;
+    int w = event->area.width;
+    int h = event->area.height;
+
+    plot_draw(p, x, y, w, h);
+
+  }
+  return FALSE;
+}
+
+static void plot_size_request (GtkWidget *widget,
+			       GtkRequisition *requisition){
+  requisition->width = 400; // XXX
+  requisition->height = 200; // XXX
+}
+
+static void plot_realize (GtkWidget *widget){
+  GdkWindowAttr attributes;
+  gint      attributes_mask;
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.event_mask = 
+    gtk_widget_get_events (widget) | 
+    GDK_EXPOSURE_MASK|
+    GDK_POINTER_MOTION_MASK|
+    GDK_BUTTON_PRESS_MASK  |
+    GDK_BUTTON_RELEASE_MASK|
+    GDK_KEY_PRESS_MASK |
+    GDK_KEY_RELEASE_MASK |
+    GDK_STRUCTURE_MASK |
+    GDK_ENTER_NOTIFY_MASK |
+    GDK_LEAVE_NOTIFY_MASK;
+
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  widget->window = gdk_window_new (widget->parent->window,
+				   &attributes, attributes_mask);
+  gtk_style_attach (widget->style, widget->window);
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+  gdk_window_set_user_data (widget->window, widget);
+  gtk_widget_set_double_buffered (widget, FALSE);
+}
+
+static void plot_size_allocate (GtkWidget     *widget,
+				GtkAllocation *allocation){
+  Plot *p = PLOT (widget);
+  
+  if (GTK_WIDGET_REALIZED (widget)){
+
+    if(p->wc)
+      cairo_destroy(p->wc);
+    if (p->fore)
+      cairo_surface_destroy(p->fore);
+    if (p->back)
+      cairo_surface_destroy(p->back);
+    if (p->stage)
+      cairo_surface_destroy(p->stage);
+    
+    gdk_window_move_resize (widget->window, allocation->x, allocation->y, 
+			    allocation->width, allocation->height);
+    
+    p->wc = gdk_cairo_create(widget->window);
+
+    // the background is created from data
+    p->datarect = calloc(allocation->width * allocation->height,4);
+    
+    p->back = cairo_image_surface_create_for_data ((unsigned char *)p->datarect,
+						   CAIRO_FORMAT_RGB24,
+						   allocation->width,
+						   allocation->height,
+						   allocation->width*4);
+
+    p->stage = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+					  allocation->width,
+					  allocation->height);
+    p->fore = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+					  allocation->width,
+					  allocation->height);
+
+  }
+
+  widget->allocation = *allocation;
+  p->x = scalespace_linear(p->x.lo,p->x.hi,widget->allocation.width,p->scalespacing);
+  p->y = scalespace_linear(p->y.lo,p->y.hi,widget->allocation.height,p->scalespacing);
+  draw_scales(p);
+  if(p->recompute_callback)p->recompute_callback(p->app_data);
+
+}
+
+static gint mouse_motion(GtkWidget        *widget,
+			 GdkEventMotion   *event){
+  //Plot *p = PLOT (widget);
+
+  return TRUE;
+}
+
+static gboolean mouse_press (GtkWidget        *widget,
+			     GdkEventButton   *event){
+  //Plot *p = PLOT (widget);
+ 
+
+  return TRUE;
+}
+ 
+static gboolean mouse_release (GtkWidget        *widget,
+			       GdkEventButton   *event){
+  Plot *p = PLOT (widget);
+  plot_expose_request(p);
+  p->selx = event->x;
+  p->sely = event->y;
+  p->selx_val = scalespace_value(&p->x,p->selx);
+  p->sely_val = scalespace_value(&p->y,widget->allocation.height-p->sely);
+
+  if(p->crosshairs_callback)
+    p->crosshairs_callback(p->cross_data);
+  plot_expose_request(p);
+
+  return TRUE;
+}
+
+static gboolean plot_enter (GtkWidget        *widget,
+			    GdkEventCrossing   *event){
+  //Plot *p = PLOT (widget);
+
+  return TRUE;
+}
+
+static gboolean plot_leave (GtkWidget        *widget,
+			    GdkEventCrossing   *event){	
+  //Plot *p = PLOT (widget);
+
+  return TRUE;
+}
+
+static void plot_class_init (PlotClass * class) {
+
+  GtkObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+
+  object_class = (GtkObjectClass *) class;
+  widget_class = (GtkWidgetClass *) class;
+
+  parent_class = gtk_type_class (GTK_TYPE_WIDGET);
+
+  object_class->destroy = plot_destroy;
+
+  widget_class->realize = plot_realize;
+  widget_class->expose_event = plot_expose;
+  widget_class->size_request = plot_size_request;
+  widget_class->size_allocate = plot_size_allocate;
+  widget_class->button_press_event = mouse_press;
+  widget_class->button_release_event = mouse_release;
+  widget_class->motion_notify_event = mouse_motion;
+  widget_class->enter_notify_event = plot_enter;
+  widget_class->leave_notify_event = plot_leave;
+
+}
+
+GType plot_get_type (void){
+
+  static GType plot_type = 0;
+
+  if (!plot_type)
+    {
+      static const GTypeInfo plot_info = {
+        sizeof (PlotClass),
+        NULL,
+        NULL,
+        (GClassInitFunc) plot_class_init,
+        NULL,
+        NULL,
+        sizeof (Plot),
+        0,
+        (GInstanceInitFunc) plot_init,
+	0
+      };
+
+      plot_type = g_type_register_static (GTK_TYPE_WIDGET, "Plot",
+                                               &plot_info, 0);
+    }
+  
+  return plot_type;
+}
+
+Plot *plot_new (void (*callback)(void *),void *app_data,
+		void (*cross_callback)(void *),void *cross_data) {
+  GtkWidget *g = GTK_WIDGET (g_object_new (PLOT_TYPE, NULL));
+  Plot *p = PLOT (g);
+  p->recompute_callback = callback;
+  p->app_data = app_data;
+  p->crosshairs_callback = cross_callback;
+  p->cross_data = cross_data;
+  return p;
+}
+
+void plot_expose_request(Plot *p){
+  GtkWidget *widget = GTK_WIDGET(p);
+  GdkRectangle r;
+  
+  r.x=0;
+  r.y=0;
+  r.width=widget->allocation.width;
+  r.height=widget->allocation.height;
+  
+  gdk_window_invalidate_rect (widget->window, &r, FALSE);
+}
+
+void plot_expose_request_line(Plot *p, int num){
+  GtkWidget *widget = GTK_WIDGET(p);
+  GdkRectangle r;
+  
+  r.x=0;
+  r.y=num;
+  r.width=widget->allocation.width;
+  r.height=1;
+  
+  gdk_window_invalidate_rect (widget->window, &r, FALSE);
+}
+
+void plot_draw_line(Plot *p, int num){
+  GtkWidget *widget = GTK_WIDGET(p);
+  //GdkRectangle r;
+  
+  plot_draw(p,0,num,widget->allocation.width,1);
+}
+
+void plot_set_x_scale(Plot *p, double low, double high){
+  GtkWidget *widget = GTK_WIDGET(p);
+  scalespace temp = p->x;
+  p->x = scalespace_linear(low,high,widget->allocation.width,p->scalespacing);
+  p->selx = scalespace_pixel(&p->x,p->selx_val);
+  if(memcmp(&temp,&p->x,sizeof(temp)))
+    draw_scales(p);
+}
+
+void plot_set_y_scale(Plot *p, double low, double high){
+  GtkWidget *widget = GTK_WIDGET(p);
+  scalespace temp = p->y;
+  p->y = scalespace_linear(low,high,widget->allocation.height,p->scalespacing);
+  p->sely = widget->allocation.height - scalespace_pixel(&p->y,p->sely_val);
+
+  if(memcmp(&temp,&p->y,sizeof(temp)))
+    draw_scales(p);
+}
+
+void plot_set_x_name(Plot *p, char *name){
+  p->namex = name;
+  draw_scales(p);
+}
+
+void plot_set_y_name(Plot *p, char *name){
+  p->namey = name;
+  draw_scales(p);
+}
+
+u_int32_t *plot_get_background_line(Plot *p, int num){
+  GtkWidget *widget = GTK_WIDGET(p);
+  return p->datarect + num*widget->allocation.width;
+}
+
+cairo_t *plot_get_background_cairo(Plot *p){
+  return cairo_create(p->back);
+}
+
+void plot_set_crosshairs(Plot *p, double x, double y){
+  p->selx_val = x;
+  p->sely_val = y;
+  p->selx = scalespace_pixel(&p->x,x);
+  p->sely = GTK_WIDGET(p)->allocation.height - scalespace_pixel(&p->y,y);
+
+  plot_expose_request(p);
+}

Added: trunk/sushivision/plot.h
===================================================================
--- trunk/sushivision/plot.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/plot.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,87 @@
+/*
+ *
+ *  sushivision copyright (C) 2005 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <sys/types.h>
+#include "scale.h"
+
+G_BEGIN_DECLS
+
+#define PLOT_TYPE            (plot_get_type ())
+#define PLOT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), PLOT_TYPE, Plot))
+#define PLOT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), PLOT_TYPE, PlotClass))
+#define IS_PLOT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLOT_TYPE))
+#define IS_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLOT_TYPE))
+
+typedef struct _Plot       Plot;
+typedef struct _PlotClass  PlotClass;
+
+struct _Plot{
+  GtkWidget w;
+  cairo_t         *wc;
+  cairo_t         *bc;
+  cairo_surface_t *back;
+  cairo_surface_t *fore;
+  cairo_surface_t *stage;
+
+  int scalespacing;
+  scalespace x;
+  scalespace y;
+  char *namex;
+  char *namey;
+
+  double selx_val;
+  double sely_val;
+  double selx;
+  double sely;
+
+  u_int32_t *datarect;
+  void *app_data;
+  void (*recompute_callback)(void *);
+  void *cross_data;
+  void (*crosshairs_callback)(void *);
+  
+};
+
+struct _PlotClass{
+  GtkWidgetClass parent_class;
+  void (*plot) (Plot *m);
+};
+
+GType     plot_get_type        (void);
+Plot*     plot_new             (void (*callback)(void *),void *app_data,
+				void (*cross_callback)(void *),void *cross_data);
+
+G_END_DECLS
+
+// the widget subclass half
+void plot_expose_request(Plot *p);
+void plot_expose_request_line(Plot *p, int num);
+void plot_set_x_scale(Plot *p, double low, double high);
+void plot_set_y_scale(Plot *p, double low, double high);
+void plot_set_x_name(Plot *p, char *name);
+void plot_set_y_name(Plot *p, char *name);
+u_int32_t * plot_get_background_line(Plot *p, int num);
+cairo_t *plot_get_background_cairo(Plot *p);
+void plot_set_crosshairs(Plot *p, double x, double y);

Added: trunk/sushivision/scale.c
===================================================================
--- trunk/sushivision/scale.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/scale.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,227 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#define _GNU_SOURCE
+#include <math.h>
+#include <gtk/gtk.h>
+#include <cairo-ft.h>
+#include <fontconfig/fontconfig.h>
+#include <stdio.h>
+#include "scale.h"
+
+/* slider scales */
+static int trailing_zeroes(double A){
+  int count=0;
+  if(A<0)A=-A;
+  if(A==0)return 0;
+  A = floor(A);
+  A/=10;
+  while(A == floor(A)){
+    count++;
+    A/=10;
+  }
+  return count;
+}
+
+/* depth, at a minimum, must capture the difference between consective scale values */
+static int del_depth(double A, double B){
+  int depth = 0;
+
+  double del = B-A;
+  if(del<0)del=-del;
+  if(del != 0){
+    if(del<1){
+      float testdel=del;
+      while(testdel<1){
+	depth++;
+	testdel = del * pow(10,depth);
+      }
+    }else{
+      float testdel=del;
+      while(testdel>10){
+	depth--;
+	testdel = del / pow(10,-depth);
+      }
+    }
+  }
+  return depth;
+}
+
+static int abs_depth(double A){
+  int depth = 0;
+
+  if(A<0)A=-A;
+  if(A != 0){
+    if(A<1){
+      /* first ratchet down to find the beginning */
+      while(A * pow(10.,depth) < 1)
+	depth++;
+
+      /* look for trailing zeroes; assume a reasonable max depth */
+      depth+=5;
+
+      A*=pow(10.,depth);
+      int zero0 = trailing_zeroes(A);
+      int zeroN = trailing_zeroes(A-1);
+      int zeroP = trailing_zeroes(A+1);
+
+      if(zero0 >= zeroN && zero0 >= zeroP)
+	depth -= zero0;
+      else if(zeroN >= zero0 && zeroN >= zeroP)
+	depth -= zeroN;
+      else 
+	depth -=zeroP;
+
+    }else
+      // Max at five sig figs, less if trailing zeroes... */
+      depth= 5-trailing_zeroes(A*100000.);
+  }
+
+  return depth;
+}
+
+static char *generate_label(double val,int depth){
+  char *ret;
+  
+  if(depth>0){
+    asprintf(&ret,"%.*f",depth,val);
+  }else{
+    asprintf(&ret,"%ld",(long)val);
+  }
+  return ret;
+}
+
+/* default scale label generation is hard, but fill it in in an ad-hoc
+   fashion.  Add flags to help out later */
+char **scale_generate_labels(unsigned scalevals, double *scaleval_list){
+  unsigned i;
+  int depth;
+  char **ret;
+
+  // default behavior; display each scale label at a uniform decimal
+  // depth.  Begin by finding the smallest significant figure in any
+  // label.  Since they're being passed in explicitly, they'll be
+  // pre-hinted to the app's desires. Deal with rounding. */
+
+  if(scalevals<2){
+    fprintf(stderr,"Scale requires at least two scale values.");
+    return NULL;
+  }
+
+  depth = del_depth(scaleval_list[0],scaleval_list[1]);
+
+  for(i=2;i<scalevals;i++){
+    int val = del_depth(scaleval_list[i-1],scaleval_list[i]);
+    if(val>depth)depth=val;
+  }
+  
+  for(i=0;i<scalevals;i++){
+    int val = abs_depth(scaleval_list[i]);
+    if(val>depth)depth=val;
+  }
+  
+  ret = calloc(scalevals,sizeof(*ret));
+  for(i=0;i<scalevals;i++)
+    ret[i] = generate_label(scaleval_list[i],depth);
+  
+  return ret;
+}
+
+
+/* plot and graph scales */
+
+double scalespace_value(scalespace *s, double pixel){
+  double val = (double)((pixel-s->first_pixel)*s->step_val)/s->step_pixel + s->first_val;
+  return val * s->m;
+}
+
+double scalespace_pixel(scalespace *s, double val){
+  val /= s->m;
+  val -= s->first_val;
+  val *= s->step_pixel;
+  val /= s->step_val;
+  val += s->first_pixel;
+
+  return val;
+}
+
+int scalespace_mark(scalespace *s, int num){
+  return s->first_pixel + s->step_pixel*num;
+}
+
+double scalespace_label(scalespace *s, int num, char *buffer){
+  int pixel = scalespace_mark(s,num);
+  double val = scalespace_value(s,pixel);
+
+  if(s->decimal_exponent<0){
+    sprintf(buffer,"%.*f",-s->decimal_exponent,val);
+  }else{
+    sprintf(buffer,"%.0f",val);
+  }
+  return val;
+}
+
+scalespace scalespace_linear (double lowpoint, double highpoint, int pixels, int max_spacing){
+  double range = fabs(highpoint - lowpoint);
+  scalespace ret;
+
+  int place = 0;
+  int step = 1;
+  int first;
+
+  ret.lo = lowpoint;
+  ret.hi = highpoint;
+
+  while(pixels / range < max_spacing){
+    place++;
+    range *= .1;
+  }
+  while(pixels / range > max_spacing){
+    place--;
+    range *= 10;
+  }
+
+  ret.decimal_exponent = place;
+
+  if (pixels / (range*.2) <= max_spacing){
+    step *= 5;
+    range *= .2;
+  }
+  if (pixels / (range*.5) <= max_spacing){
+    step *= 2;
+    range *= .5;
+  }
+
+  ret.step_val = step;
+  ret.step_pixel = rint(pixels / range);
+  ret.m = pow(10,place);
+  first = (int)(lowpoint/ret.m)/step*step;
+
+  while(first * ret.m < lowpoint)
+    first += step;
+  while(first * ret.m > highpoint)
+    first -= step;
+
+  ret.first_val = first;
+  ret.first_pixel = rint((first - (lowpoint / ret.m)) * ret.step_pixel / ret.step_val);
+
+  return ret;
+}

Added: trunk/sushivision/scale.h
===================================================================
--- trunk/sushivision/scale.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/scale.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,42 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+typedef struct {
+  double lo;
+  double hi;
+
+  long first_val;
+  long first_pixel;
+  
+  long step_val;
+  long step_pixel;
+
+  int decimal_exponent;
+  double m;
+} scalespace;
+
+extern char **scale_generate_labels(unsigned scalevals, double *scaleval_list);
+
+extern double scalespace_value(scalespace *s, double pixel);
+extern double scalespace_pixel(scalespace *s, double val);
+extern int scalespace_mark(scalespace *s, int num);
+extern double scalespace_label(scalespace *s, int num, char *buffer);
+extern scalespace scalespace_linear (double lowpoint, double highpoint, int pixels, int max_spacing);

Added: trunk/sushivision/slice.c
===================================================================
--- trunk/sushivision/slice.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/slice.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,257 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include "mapping.h"
+#include "slice.h"
+#include "slider.h"
+
+static void draw_and_expose(GtkWidget *widget){
+  Slice *s=SLICE(widget);
+
+  slider_draw(s->slider);
+  slider_expose(s->slider);
+}
+
+static gboolean slice_expose(GtkWidget *widget, GdkEventExpose *event ){
+  Slice *s=SLICE(widget);
+  slider_expose_slice(s->slider,s->slicenum);
+  return FALSE;
+}
+
+static void slice_size_request (GtkWidget *widget,GtkRequisition *requisition){
+  Slice *s=SLICE(widget);
+  
+  slider_size_request_slice(s->slider,requisition);
+}
+
+static gboolean slice_focus (GtkWidget         *widget,
+			     GtkDirectionType   direction){
+  Slice *s=SLICE(widget);
+
+  if(!s->thumb_active)return FALSE;
+  if(s->thumb_focus)return TRUE;
+
+  s->thumb_focus=1;
+  gtk_widget_grab_focus(widget);
+  draw_and_expose(widget);
+
+  return TRUE;
+}
+
+static gint slice_motion(GtkWidget        *widget,
+			 GdkEventMotion   *event){
+  Slice *s=SLICE(widget);
+  slider_motion(s->slider,s->slicenum,event->x,event->y);
+  
+  return TRUE;
+}
+
+static gint slice_enter(GtkWidget        *widget,
+			GdkEventCrossing *event){
+  Slice *s=SLICE(widget);
+  slider_lightme(s->slider,s->slicenum,event->x,event->y);
+  draw_and_expose(widget);
+  return TRUE;
+}
+
+static gint slice_leave(GtkWidget        *widget,
+			GdkEventCrossing *event){
+  Slice *s=SLICE(widget);
+
+  slider_unlight(s->slider);
+  slider_draw(s->slider);
+  slider_expose(s->slider);
+
+  return TRUE;
+}
+
+static gboolean slice_button_press(GtkWidget        *widget,
+				   GdkEventButton   *event){
+  Slice *s=SLICE(widget);
+
+  slider_button_press(s->slider,s->slicenum,event->x,event->y);
+  return TRUE;
+}
+
+static gboolean slice_button_release(GtkWidget        *widget,
+				     GdkEventButton   *event){
+  Slice *s=SLICE(widget);
+  slider_button_release(s->slider,s->slicenum,event->x,event->y);
+  return TRUE;
+}
+
+static gboolean slice_unfocus(GtkWidget        *widget,
+			      GdkEventFocus       *event){
+  Slice *s=SLICE(widget);
+  if(s->thumb_focus){
+    s->thumb_focus=0;
+    draw_and_expose(widget);
+  }
+  return TRUE;
+}
+
+static gboolean slice_refocus(GtkWidget        *widget,
+			      GdkEventFocus       *event){
+  Slice *s=SLICE(widget);
+  if(!s->thumb_focus){
+    s->thumb_focus=1;
+    draw_and_expose(widget);
+  }
+  return TRUE;
+}
+
+static gboolean slice_key_press(GtkWidget *widget,GdkEventKey *event){
+  Slice *s=SLICE(widget);
+
+  return slider_key_press(s->slider,event);
+}
+
+static void slice_state_changed(GtkWidget *w,GtkStateType ps){
+  draw_and_expose(w);
+}
+
+static void slice_realize (GtkWidget *widget){
+  GdkWindowAttr attributes;
+  gint      attributes_mask;
+
+  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+  GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+
+  attributes.x = widget->allocation.x;
+  attributes.y = widget->allocation.y;
+  attributes.width = widget->allocation.width;
+  attributes.height = widget->allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.event_mask = 
+    gtk_widget_get_events (widget) | 
+    GDK_EXPOSURE_MASK|
+    GDK_POINTER_MOTION_MASK|
+    GDK_BUTTON_PRESS_MASK  |
+    GDK_BUTTON_RELEASE_MASK|
+    GDK_KEY_PRESS_MASK |
+    GDK_KEY_RELEASE_MASK |
+    GDK_STRUCTURE_MASK |
+    GDK_ENTER_NOTIFY_MASK |
+    GDK_FOCUS_CHANGE_MASK |
+    GDK_LEAVE_NOTIFY_MASK;
+
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.colormap = gtk_widget_get_colormap (widget);
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+  widget->window = gdk_window_new (widget->parent->window,
+                                   &attributes, attributes_mask);
+  gtk_style_attach (widget->style, widget->window);
+  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+  gdk_window_set_user_data (widget->window, widget);
+  gtk_widget_set_double_buffered (widget, FALSE);
+}
+
+static void slice_size_allocate (GtkWidget     *widget,
+				 GtkAllocation *allocation){
+  //Slice *s = SLICE (widget);  
+  if (GTK_WIDGET_REALIZED (widget)){
+    
+    gdk_window_move_resize (widget->window, allocation->x, allocation->y, 
+                            allocation->width, allocation->height);
+    
+  }
+  
+  widget->allocation = *allocation;
+
+}
+
+static GtkWidgetClass *parent_class = NULL;
+
+static void slice_class_init (SliceClass *class){
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+  //GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
+
+  parent_class = g_type_class_peek_parent (class);
+
+  //object_class->destroy = slice_destroy;
+  widget_class->realize = slice_realize;
+  widget_class->expose_event = slice_expose;
+  widget_class->size_request = slice_size_request;
+  widget_class->size_allocate = slice_size_allocate;
+  widget_class->key_press_event = slice_key_press;
+  widget_class->button_press_event = slice_button_press;
+  widget_class->button_release_event = slice_button_release;
+  widget_class->enter_notify_event = slice_enter;
+  widget_class->leave_notify_event = slice_leave;
+  widget_class->motion_notify_event = slice_motion;
+  widget_class->focus_out_event = slice_unfocus;
+  widget_class->focus_in_event = slice_refocus;
+  widget_class->state_changed = slice_state_changed;
+}
+
+static void slice_init (Slice *s){
+}
+
+GType slice_get_type (void){
+  static GType m_type = 0;
+  if (!m_type){
+    static const GTypeInfo m_info={
+      sizeof (SliceClass),
+      NULL, /* base_init */
+      NULL, /* base_finalize */
+      (GClassInitFunc) slice_class_init,
+      NULL, /* class_finalize */
+      NULL, /* class_data */
+      sizeof (Slice),
+      0,
+      (GInstanceInitFunc) slice_init,
+      0
+    };
+    
+    m_type = g_type_register_static (GTK_TYPE_WIDGET, "Slider", &m_info, 0);
+  }
+
+  return m_type;
+}
+
+GtkWidget* slice_new (void (*callback)(void *), void *data){
+  GtkWidget *ret= GTK_WIDGET (g_object_new (slice_get_type (), NULL));
+  Slice *s=SLICE(ret);
+  s->callback = callback;
+  s->callback_data = data;
+  s->thumb_active = 1;
+  return ret;
+}
+
+void slice_set_active(Slice *s, int activep){
+  s->thumb_active = activep;
+  draw_and_expose(GTK_WIDGET(s));
+}
+
+void slice_thumb_set(Slice *s,float v){
+  GtkWidget *w=GTK_WIDGET(s);
+
+  s->thumb_val=v;
+  slider_vals_bound(s->slider,s->slicenum);
+  
+  if(s->callback)s->callback(s->callback_data);
+  draw_and_expose(w);
+}

Added: trunk/sushivision/slice.h
===================================================================
--- trunk/sushivision/slice.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/slice.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,73 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#ifndef __SLICE_H__
+#define __SLICE_H__
+
+#include <sys/time.h>
+#include <time.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtksignal.h>
+
+G_BEGIN_DECLS
+
+#define SLICE_TYPE            (slice_get_type ())
+#define SLICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SLICE_TYPE, Slice))
+#define SLICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SLICE_TYPE, SliceClass))
+#define IS_SLICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SLICE_TYPE))
+#define IS_SLICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SLICE_TYPE))
+
+typedef struct _Slice       Slice;
+typedef struct _SliceClass  SliceClass;
+
+typedef struct _Slider Slider;
+
+struct _Slice{
+  GtkWidget widget;
+
+  Slider *slider;
+  int slicenum;
+
+  int    thumb_active;
+  int    thumb_focus;
+  int    thumb_state;
+  int    thumb_grab;
+  double thumb_val;
+
+  void (*callback)(void *);
+  void *callback_data;
+
+};
+
+struct _SliceClass{
+  GtkWidgetClass parent_class;
+  void (*slice) (Slice *s);
+};
+
+extern GType slice_get_type        (void);
+extern GtkWidget* slice_new (void (*callback)(void *), void *data);
+extern void slice_set_active(Slice *s, int activep);
+extern void slice_thumb_set(Slice *s,float v);
+
+G_END_DECLS
+#endif

Added: trunk/sushivision/slider.c
===================================================================
--- trunk/sushivision/slider.c	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/slider.c	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,745 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "mapping.h"
+#include "slice.h"
+#include "slider.h"
+
+static int total_slice_width(Slider *s){
+  int i;
+  int count=0;
+  for(i=0;i<s->num_slices;i++)
+    count += s->slices[i]->allocation.width;
+  return count;
+}
+
+static int slice_width(Slider *s,int slices){
+  int i;
+  int count=0;
+  for(i=0;i<slices;i++)
+    count += s->slices[i]->allocation.width;
+  return count;
+}
+
+static int total_slice_height(Slider *s){
+  int i;
+  int max=0;
+  for(i=0;i<s->num_slices;i++)
+    if(max<s->slices[i]->allocation.height)
+      max = s->slices[i]->allocation.height;
+  return max;
+}
+
+/* guess where I came from. */
+static void rounded_rectangle (cairo_t *c,
+			       double x, double y, double w, double h,
+			       double radius)
+{
+  cairo_move_to (c, x+radius, y);
+  cairo_arc (c, x+w-radius, y+radius, radius, M_PI * 1.5, M_PI * 2);
+  cairo_arc (c, x+w-radius, y+h-radius, radius, 0, M_PI * 0.5);
+  cairo_arc (c, x+radius,   y+h-radius, radius, M_PI * 0.5, M_PI);
+  cairo_arc (c, x+radius,   y+radius,   radius, M_PI, M_PI * 1.5);
+}
+
+double shades[] = {1.15, 0.95, 0.896, 0.82, 0.7, 0.665, 0.5, 0.45, 0.4};
+
+static void set_shade(GtkWidget *w, cairo_t *c, int shade){
+  Slice *sl = SLICE(w);
+  GdkColor *bg = &w->style->bg[sl->thumb_state?GTK_STATE_ACTIVE:GTK_STATE_NORMAL];
+  double shade_r=bg->red*shades[shade]/65535;
+  double shade_g=bg->green*shades[shade]/65535;
+  double shade_b=bg->blue*shades[shade]/65535;
+
+  cairo_set_source_rgb (c, shade_r,shade_g,shade_b);
+}
+
+static void parent_shade(Slider *s, cairo_t *c, int shade){
+  GtkWidget *parent=gtk_widget_get_parent(s->slices[0]);
+  GdkColor *bg = &parent->style->bg[GTK_STATE_NORMAL];
+  double shade_r=bg->red*shades[shade]/65535;
+  double shade_g=bg->green*shades[shade]/65535;
+  double shade_b=bg->blue*shades[shade]/65535;
+
+  cairo_set_source_rgb (c, shade_r,shade_g,shade_b);
+}
+
+void slider_draw_background(Slider *s){
+  int i;
+  GtkWidget *parent=gtk_widget_get_parent(s->slices[0]);
+  GdkColor *bg = &parent->style->bg[0];
+  GdkColor *fg = &s->slices[0]->style->fg[0];
+  int textborder=1;
+  double textr=1.;
+  double textg=1.;
+  double textb=1.;
+
+  int x=0;
+  int y=0;
+  int w=s->w;
+  int h=s->h;
+  
+  int tx=x;
+  int ty=y+s->ypad;
+  int tw=w;
+  int th=h - s->ypad*2;
+  cairo_pattern_t *pattern;
+
+  // prepare background 
+  cairo_t *c = cairo_create(s->background);
+  
+  // Fill with bg color
+  gdk_cairo_set_source_color(c,bg); 
+  cairo_rectangle(c,0,0,w,h);
+  cairo_fill(c);
+
+  // Create trough innards
+ if(s->gradient){
+    // background map gradient 
+    u_int32_t *pixel=s->backdata+ty*s->w;
+    
+    for(i=tx;i<tx+tw;i++)
+      pixel[i]=mapping_calc(s->gradient,slider_pixel_to_del(s,0,i));
+    
+    for(i=ty+1;i<ty+th;i++){
+      memcpy(pixel+w,pixel,w*4);
+      pixel+=s->w;
+    }
+ 
+  }else{
+    // normal background
+    textr=fg->red;
+    textg=fg->green;
+    textb=fg->blue;
+    textborder=0;
+ 
+    cairo_rectangle (c, x+1, ty, w-2, th);
+    parent_shade(s,c,3);
+    cairo_fill (c);
+  }
+
+  // Top shadow 
+  cairo_rectangle (c, tx+1, ty, tw-2, 4);
+  pattern = cairo_pattern_create_linear (0, ty-1, 0, ty+3);
+  cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0., 0., 0., 0.2);	
+  cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0., 0., 0., 0.);	
+  cairo_set_source (c, pattern);
+  cairo_fill (c);
+  cairo_pattern_destroy (pattern);
+
+  // Left shadow 
+  cairo_rectangle (c, tx+1, ty, tx+4, th);
+  pattern = cairo_pattern_create_linear (tx, ty-1, tx+3, ty-1);
+  cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0., 0., 0., 0.1);	
+  cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0., 0., 0., 0.);	
+  cairo_set_source (c, pattern);
+  cairo_fill (c);
+  cairo_pattern_destroy (pattern);
+ 
+  // lines & labels
+  for(i=0;i<s->labels;i++){
+    int x=rint(((float)i)/(s->labels-1)*(s->w - s->xpad*2 -1))+s->xpad;
+    int y=s->h-s->ypad-1.5;
+    
+    cairo_move_to(c,x+.5,s->ypad+2);
+    cairo_line_to(c,x+.5,s->h-s->ypad-2);
+    cairo_set_source_rgba(c,textr,textg,textb,.8);
+    cairo_set_line_width(c,1);
+    cairo_stroke(c);
+
+    if(i>0){
+      cairo_text_extents_t ex;
+      cairo_text_extents (c, s->label[i], &ex);
+      
+      x-=2;
+      x-=ex.width;
+ 
+    }else{
+      x+=2;
+    }
+
+    if(textborder){
+      cairo_set_source_rgba(c,0,0,0,.8);
+      cairo_set_line_width(c,2);
+      cairo_move_to (c, x,y);
+      cairo_text_path (c, s->label[i]); 
+      cairo_stroke(c);
+    }
+
+    cairo_set_source_rgba(c,textr,textg,textb,.8);
+    cairo_move_to (c, x,y);
+    cairo_show_text (c, s->label[i]); 
+  }
+
+  // Draw border 
+  rounded_rectangle (c, tx+0.5, ty-0.5, tw-1, th+1, 1.5);
+  parent_shade(s,c,7);
+  cairo_set_line_width (c, 1.0);
+  cairo_stroke (c);
+	
+  cairo_destroy(c);
+}
+
+void slider_realize(Slider *s){
+  int w = total_slice_width(s);
+  int h = total_slice_height(s);
+  if(s->background == 0 || w != s->w || h != s->h){
+
+    if(s->background)
+      cairo_surface_destroy(s->background);
+
+    if(s->foreground)
+      cairo_surface_destroy(s->foreground);
+
+    if(s->backdata)
+      free(s->backdata);
+
+    s->backdata = calloc(w*h,4);
+    
+    s->background = cairo_image_surface_create_for_data ((unsigned char *)s->backdata,
+							 CAIRO_FORMAT_RGB24,
+							 w,h,w*4);
+    s->foreground = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+						w,h);
+
+    s->w=w;
+    s->h=h;
+
+    s->xpad=h*.45;
+    if(s->xpad<4)s->xpad=4;
+    slider_draw_background(s);    
+    slider_draw(s);
+
+  }
+}
+
+static double val_to_pixel(Slider *s,double v){
+  int j;
+  double ret=0;
+
+  int tx=s->xpad;
+  int tw=s->w - tx*2;
+  
+  if(v<s->label_vals[0]){
+    ret=0;
+  }else if(v>s->label_vals[s->labels-1]){
+    ret=tw;
+  }else{
+    for(j=0;j<s->labels;j++){
+      if(v>=s->label_vals[j] && v<=s->label_vals[j+1]){
+        double del=(v-s->label_vals[j])/(s->label_vals[j+1]-s->label_vals[j]);
+        double pixlo=rint((double)j/(s->labels-1)*tw);
+        double pixhi=rint((double)(j+1)/(s->labels-1)*tw);
+        ret=pixlo*(1.-del)+pixhi*del+tx;
+        break;
+      }
+    }
+  }
+  return ret;
+}
+
+double slider_val_to_del(Slider *s,double v){
+  if(isnan(v))return NAN;
+
+  int j;
+  double ret=0;
+  
+  for(j=0;j<s->labels;j++){
+    if(v<=s->label_vals[j+1] || (j+1)==s->labels){
+      double del=(v-s->label_vals[j])/(s->label_vals[j+1]-s->label_vals[j]);
+      return (j+del)/(s->labels-1);
+    }
+  }
+
+  return NAN;
+}
+
+void slider_draw(Slider *s){
+  int i;
+  cairo_t *c;
+  //int w=s->w;
+  int h=s->h;
+
+  c = cairo_create(s->foreground);
+  cairo_set_source_surface(c,s->background,0,0);
+  cairo_rectangle(c,0,0,s->w,s->h);
+  cairo_fill(c);
+
+  // thumbs
+  for(i=0;i<s->num_slices;i++){
+    GtkWidget *sl = s->slices[i];
+    double x = val_to_pixel(s,((Slice *)(s->slices[i]))->thumb_val)+.5;
+    double rad = 2.;
+    
+    float y = rint(h/2)+.5;
+    float xd = y*.575;
+    float rx = 1.73*rad;
+    cairo_pattern_t *pattern;
+
+    if(SLICE(sl)->thumb_active){
+      if ((s->num_slices == 1) || (s->num_slices == 3 && i==1)){
+	// outline
+	cairo_move_to(c,x,s->h/2);
+	cairo_arc_negative(c, x+xd-rx, rad+.5, rad, 30.*(M_PI/180.), 270.*(M_PI/180.));
+	cairo_arc_negative(c, x-xd+rx, rad+.5, rad, 270.*(M_PI/180.), 150.*(M_PI/180.));
+	cairo_close_path(c);
+	
+	set_shade(sl,c,2);
+	cairo_fill_preserve(c);
+
+	cairo_set_line_width(c,1);
+	set_shade(sl,c,7);
+      
+	if(((Slice *)s->slices[i])->thumb_focus)
+	  cairo_set_source_rgba(c,0,0,0,1);
+
+	cairo_stroke_preserve(c);
+	cairo_set_dash (c, NULL, 0, 0.);
+
+	// top highlight
+	pattern = cairo_pattern_create_linear (0, 0, 0, 4);
+	cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.2);	
+	cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);	
+	cairo_set_source (c, pattern);
+	cairo_fill_preserve (c);
+	cairo_pattern_destroy (pattern);
+
+	// Left highlight
+	pattern = cairo_pattern_create_linear (x-xd+3, 0, x-xd+6, 0);
+	cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.1);	
+	cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);	
+	cairo_set_source (c, pattern);
+	cairo_fill (c);
+	cairo_pattern_destroy (pattern);
+
+	// needle shadow
+	cairo_set_line_width(c,2);
+	cairo_move_to(c,x,s->h/2+3);
+	cairo_line_to(c,x,s->ypad/2);
+	cairo_set_source_rgba(c,0.,0.,0.,.5);
+	cairo_stroke(c);
+
+	// needle
+	cairo_set_line_width(c,1);
+	cairo_move_to(c,x,s->h/2+2);
+	cairo_line_to(c,x,s->ypad/2);
+	cairo_set_source_rgb(c,1.,1.,0);
+	cairo_stroke(c);
+
+      }else{
+	if(i==0){
+	  // bracket left
+	
+	  // outline
+	  cairo_move_to(c,x,s->h/2);
+	  cairo_line_to(c,x-xd/2,s->h/2);
+	  cairo_arc_negative(c, x-xd*3/2+rx, h-rad-.5, rad, 210.*(M_PI/180.), 90.*(M_PI/180.));
+	  cairo_line_to(c, x, h-.5);
+	  cairo_close_path(c);
+	
+	  set_shade(sl,c,2);
+	  cairo_set_line_width(c,1);
+	  cairo_fill_preserve(c);
+	  set_shade(sl,c,7);
+	  if(((Slice *)s->slices[i])->thumb_focus)
+	    cairo_set_source_rgba(c,0,0,0,1);
+	  cairo_stroke_preserve(c);
+	
+	  // top highlight
+	  pattern = cairo_pattern_create_linear (0, y, 0, y+4);
+	  cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.2);	
+	  cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);	
+	  cairo_set_source (c, pattern);
+	  cairo_fill_preserve (c);
+	  cairo_pattern_destroy (pattern);
+	
+	  // Left highlight
+	  pattern = cairo_pattern_create_linear (x-xd*3/2+3, 0, x-xd*3/2+6, 0);
+	  cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.1);	
+	  cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);	
+	  cairo_set_source (c, pattern);
+	  cairo_fill (c);
+	  cairo_pattern_destroy (pattern);
+	}else{
+
+	  // bracket right
+	
+	  // outline
+	  cairo_move_to(c,x,s->h/2);
+	  cairo_line_to(c,x+xd/2,s->h/2);
+	  cairo_arc(c, x+xd*3/2-rx, h-rad-.5, rad, 330.*(M_PI/180.), 90.*(M_PI/180.));
+	  cairo_line_to(c, x, h-.5);
+	  cairo_close_path(c);
+	
+	  set_shade(sl,c,2);
+	  cairo_set_line_width(c,1);
+	  cairo_fill_preserve(c);
+	  set_shade(sl,c,7);
+	  if(((Slice *)s->slices[i])->thumb_focus)
+	    cairo_set_source_rgba(c,0,0,0,1);
+	  cairo_stroke_preserve(c);
+	
+	  // top highlight
+	  pattern = cairo_pattern_create_linear (0, y, 0, y+4);
+	  cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.2);	
+	  cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);	
+	  cairo_set_source (c, pattern);
+	  cairo_fill_preserve (c);
+	  cairo_pattern_destroy (pattern);
+	
+	  // Left highlight
+	  pattern = cairo_pattern_create_linear (x+1, 0, x+4, 0);
+	  cairo_pattern_add_color_stop_rgba (pattern, 0.0, 1., 1., 1., 0.1);	
+	  cairo_pattern_add_color_stop_rgba (pattern, 1.0, 1., 1., 1., 0.);	
+	  cairo_set_source (c, pattern);
+	  cairo_fill (c);
+	  cairo_pattern_destroy (pattern);
+
+	}
+	// needle shadow
+	cairo_set_line_width(c,2);
+	cairo_move_to(c,x,s->h/2-3);
+	cairo_line_to(c,x,h-s->ypad/2);
+	cairo_set_source_rgba(c,0.,0.,0.,.5);
+	cairo_stroke(c);
+
+	// needle
+	cairo_set_line_width(c,1);
+	cairo_move_to(c,x,s->h/2-2);
+	cairo_line_to(c,x,h-s->ypad/2);
+	cairo_set_source_rgb(c,1.,1.,0);
+	cairo_stroke(c);
+      }
+    }
+  }
+
+  cairo_destroy(c);
+}
+
+void slider_expose_slice(Slider *s, int slicenum){
+  Slice *slice = (Slice *)(s->slices[slicenum]);
+  GtkWidget *w = GTK_WIDGET(slice);
+  
+  if(GTK_WIDGET_REALIZED(w)){
+    cairo_t *c = gdk_cairo_create(w->window);
+
+    slider_realize(s);
+    cairo_set_source_surface(c,s->foreground,-slice_width(s,slicenum),0);
+    cairo_rectangle(c,0,0,w->allocation.width,w->allocation.height);
+    cairo_fill(c);
+  
+    cairo_destroy(c);
+  }
+}
+
+void slider_expose(Slider *s){
+  int i;
+  for(i=0;i<s->num_slices;i++)
+    slider_expose_slice(s,i);
+}
+
+void slider_size_request_slice(Slider *s,GtkRequisition *requisition){
+  int maxx=0,x0=0,x1=0,maxy=0,i,w;
+  
+  // need a dummy surface to find text sizes 
+  cairo_surface_t *dummy=cairo_image_surface_create(CAIRO_FORMAT_RGB24,1,1);
+  cairo_t *c = cairo_create(dummy);
+  
+  // find the widest label
+  for(i=0;i<s->labels;i++){
+    cairo_text_extents_t ex;
+    cairo_text_extents(c, s->label[i], &ex);
+    if(i==0) x0 = ex.width;
+    if(i==1) x1 = ex.width;
+    if(ex.width > maxx)maxx=ex.width;
+    if(ex.height > maxy)maxy=ex.height;
+  }
+
+  // also check first + second label width
+  if(x0+x1 > maxx)maxx=x0+x1;
+
+  w = (maxx*1.5+2)*s->labels+4;
+  requisition->width = (w+s->num_slices-1)/s->num_slices;
+  requisition->height = maxy+4+s->ypad*2;
+
+  cairo_destroy(c);
+  cairo_surface_destroy(dummy);
+}
+
+static double slice_adjust_pixel(Slider *s,int slicenum, double x){
+  double width = slice_width(s,slicenum);
+  return x+width;
+}
+
+double slider_pixel_to_val(Slider *s,int slicenum,double x){
+  int j;
+  int tx=s->xpad;
+  int tw=s->w - tx*2;
+
+  x=slice_adjust_pixel(s,slicenum,x);
+
+  for(j=0;j<s->labels-1;j++){
+
+    double pixlo=rint((float)j/(s->labels-1)*(tw-1))+tx;
+    double pixhi=rint((float)(j+1)/(s->labels-1)*(tw-1))+tx;
+
+    if(x>=pixlo && x<=pixhi){
+      if(pixlo==pixhi)return s->label_vals[j];
+      double del=(float)(x-pixlo)/(pixhi-pixlo);
+      return (1.-del)*s->label_vals[j] + del*s->label_vals[j+1];
+    }
+  }
+  if(x<tx)
+    return s->label_vals[0];
+  else
+    return (s->label_vals[s->labels-1]);
+}
+
+double slider_pixel_to_del(Slider *s,int slicenum,double x){
+  int tx=s->xpad;
+  int tw=s->w - tx*2;
+
+  x=slice_adjust_pixel(s,slicenum,x-tx);
+
+  if(x<=0){
+    return 0.;
+  }else if (x>tw){
+    return 1.;
+  }else
+    return x/tw;
+}
+
+void slider_vals_bound(Slider *s,int slicenum){
+  int i,flag=0;
+  Slice *center = SLICE(s->slices[slicenum]);
+  double min = s->label_vals[0];
+  double max = s->label_vals[s->labels-1];
+  if(center->thumb_val < min)
+    center->thumb_val = min;
+
+  if(center->thumb_val > max)
+    center->thumb_val = max;
+
+  // now make sure other sliders have valid spacing
+  if( (s->flags & SLIDER_FLAG_INDEPENDENT_MIDDLE) &&
+      s->num_slices == 3)
+    flag=1;
+  
+  for(i=slicenum-1; i>=0;i--){
+    if(flag && (i==0 || i==1))continue;
+
+    Slice *sl = SLICE(s->slices[i]);
+    Slice *sl2 = SLICE(s->slices[i+1]);
+    if(sl->thumb_val>sl2->thumb_val)
+      sl->thumb_val=sl2->thumb_val;
+  }
+  
+  for(i=slicenum+1; i<s->num_slices;i++){
+    if(flag && (i==2 || i==1))continue;
+
+    Slice *sl = SLICE(s->slices[i]);
+    Slice *sl2 = SLICE(s->slices[i-1]);
+    if(sl->thumb_val<sl2->thumb_val)
+      sl->thumb_val=sl2->thumb_val;
+  }
+}
+
+static int determine_thumb(Slider *s,int slicenum,int x,int y){
+  int i;
+  int best=-1;
+  float bestdist=s->w+1;
+
+  x=slice_adjust_pixel(s,slicenum,x);
+  for(i=0;i<s->num_slices;i++){
+    Slice *sl = SLICE(s->slices[i]);
+    if(sl->thumb_active){
+      float tpix = val_to_pixel(s,sl->thumb_val) + i - s->num_slices/2;
+      float d = fabs(x - tpix);
+      if(d<bestdist){
+	best=i;
+	bestdist=d;
+      }
+    }
+  }
+  return best;
+}
+
+static int lit_thumb(Slider *s){
+  int i;
+  for(i=0;i<s->num_slices;i++){
+    Slice *sl = SLICE(s->slices[i]);
+    if(sl->thumb_state)return i;
+  }
+  return -1;
+}
+
+void slider_unlight(Slider *s){
+  int i;
+  for(i=0;i<s->num_slices;i++){
+    Slice *sl = SLICE(s->slices[i]);
+    if(!sl->thumb_grab)
+      sl->thumb_state = 0;
+  }
+}
+
+int slider_lightme(Slider *s,int slicenum,int x,int y){
+  int last = lit_thumb(s);
+  int best = determine_thumb(s,slicenum,x,y);
+  if(last != best){
+    slider_unlight(s);
+    if(best>-1){
+      Slice *sl = SLICE(s->slices[best]);
+      sl->thumb_state=1;
+    }
+    return 1;
+  }
+  return 0;
+}
+
+void slider_button_press(Slider *s,int slicenum,int x,int y){
+  int i;
+  for(i=0;i<s->num_slices;i++){
+    Slice *sl = SLICE(s->slices[i]);
+    if(sl->thumb_state){
+      sl->thumb_grab=1;
+      sl->thumb_focus=1;
+      gtk_widget_grab_focus(GTK_WIDGET(sl));
+
+      slider_motion(s,slicenum,x,y);
+    }
+  }
+  slider_draw(s);
+  slider_expose(s);
+}
+
+void slider_button_release(Slider *s,int slicenum,int x,int y){
+  int i;
+  for(i=0;i<s->num_slices;i++){
+    Slice *sl = SLICE(s->slices[i]);
+    sl->thumb_grab=0;
+  }
+}
+
+void slider_motion(Slider *s,int slicenum,int x,int y){
+  int altered=0;
+  int i;
+  
+  /* is a thumb already grabbed? */
+  for(i=0;i<s->num_slices;i++){
+    Slice *sl = SLICE(s->slices[i]);
+    if(sl->thumb_grab){      
+      sl->thumb_val=slider_pixel_to_val(s,slicenum,x);
+      slider_vals_bound(s,i);
+      altered=i+1;
+    }
+  }
+
+  // did a gradient get altered?
+  if(s->gradient && s->num_slices>=2){
+    Slice *sl = SLICE(s->slices[0]);
+    Slice *sh = SLICE(s->slices[s->num_slices-1]);
+    if(s->gradient->low != sl->thumb_val ||
+       s->gradient->high != sh->thumb_val){
+
+      mapping_set_lo(s->gradient,slider_val_to_del(s,sl->thumb_val));
+      mapping_set_hi(s->gradient,slider_val_to_del(s,sh->thumb_val));
+      slider_draw_background(s);
+    }
+  }
+  if(altered){
+    Slice *sl = SLICE(s->slices[altered-1]);
+    
+    if(sl->callback)sl->callback(sl->callback_data);
+    slider_draw(s);
+    slider_expose(s);
+  }else{
+    /* nothing grabbed right now; determine if we're in a a thumb's area */
+    if(slider_lightme(s,slicenum,x,y)){
+      slider_draw(s);
+      slider_expose(s);
+    }
+  }
+}
+
+gboolean slider_key_press(Slider *s,GdkEventKey *event){
+
+  return FALSE; // keep processing
+}
+
+Slider *slider_new(Slice **slices, int num_slices, char **labels, double *label_vals, int num_labels,
+		   unsigned flags){
+  int i;
+  Slider *ret = calloc(1,sizeof(*ret)); 
+
+  ret->slices = (GtkWidget **)slices;
+  ret->num_slices = num_slices;
+
+  ret->label = calloc(num_labels,sizeof(*ret->label));
+  for(i=0;i<num_labels;i++)
+    ret->label[i]=strdup(labels[i]);
+
+  ret->label_vals = calloc(num_labels,sizeof(*ret->label_vals));
+  memcpy(ret->label_vals,label_vals,sizeof(*ret->label_vals)*num_labels);
+  ret->labels = num_labels;
+
+  // set up each slice
+  for(i=0;i<num_slices;i++){
+    slices[i]->slider = ret;
+    slices[i]->slicenum = i;
+  }
+  ret->ypad=8;
+  ret->xpad=5;
+  //ret->minstep=minstep;
+  //ret->step=step;
+
+  ret->flags=flags;
+  return ret;
+}
+
+void slider_set_gradient(Slider *s, mapping *m){
+  s->gradient = m;
+}
+
+void slider_set_thumb_active(Slider *s, int thumbnum, int activep){
+  slice_set_active(SLICE(s->slices[thumbnum]),activep);
+}
+
+double slider_get_value(Slider *s, int thumbnum){
+  GtkWidget *w;
+  if(thumbnum >= s->num_slices)return 0;
+  if(thumbnum < 0)return 0;
+  w = s->slices[thumbnum];
+  return SLICE(w)->thumb_val;
+}
+
+void slider_set_value(Slider *s, int thumbnum, double v){
+  GtkWidget *w;
+  if(thumbnum >= s->num_slices)return;
+  if(thumbnum < 0)return;
+  w = s->slices[thumbnum];
+  slice_thumb_set(SLICE(w),v);
+}

Added: trunk/sushivision/slider.h
===================================================================
--- trunk/sushivision/slider.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/slider.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,70 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#include <sys/types.h>
+struct _Slider {
+  GtkWidget **slices;
+  int num_slices;
+  
+  u_int32_t *backdata;
+  cairo_surface_t *background;
+  cairo_surface_t *foreground;
+  int w;
+  int h;
+  mapping *gradient;
+  int xpad;
+  int ypad;
+
+  char **label;
+  double *label_vals;
+  int labels;
+
+  int flags;
+
+  //double minstep;
+  //double step;
+};
+
+#define SLIDER_FLAG_INDEPENDENT_MIDDLE 0x1
+
+extern void slider_draw_background(Slider *s);
+extern void slider_realize(Slider *s);
+extern void slider_draw(Slider *);
+extern void slider_expose_slice(Slider *s, int slicenum);
+extern void slider_expose(Slider *s);
+extern void slider_size_request_slice(Slider *s,GtkRequisition *requisition);
+extern double slider_pixel_to_val(Slider *slider,int slicenum,double x);
+extern double slider_pixel_to_del(Slider *slider,int slicenum,double x);
+extern double slider_val_to_del(Slider *slider,double v);
+extern void slider_vals_bound(Slider *slider,int slicenum);
+extern int slider_lightme(Slider *slider,int slicenum,int x,int y);
+extern void slider_unlight(Slider *slider);
+extern void slider_button_press(Slider *slider,int slicenum,int x,int y);
+extern void slider_button_release(Slider *s,int slicenum,int x,int y);
+extern void slider_motion(Slider *s,int slicenum,int x,int y);
+extern gboolean slider_key_press(Slider *slider,GdkEventKey *event);
+extern Slider *slider_new(Slice **slices, int num_slices, 
+			  char **labels, double *label_vals, int num_labels,
+			  unsigned flags);
+extern void slider_set_thumb_active(Slider *s, int thumbnum, int activep);
+extern void slider_set_gradient(Slider *s, mapping *m);
+extern double slider_get_value(Slider *s, int thumbnum);
+extern void slider_set_value(Slider *s, int thumbnum, double v);

Added: trunk/sushivision/sushi-gtkrc
===================================================================
--- trunk/sushivision/sushi-gtkrc	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/sushi-gtkrc	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,202 @@
+style "button-poppy" {
+	bg[NORMAL]="#80a0ff" 
+	bg[ACTIVE]="#c0f0ff" 
+	bg[PRELIGHT]="#c0f0ff" 
+
+	text[NORMAL]="#000000"
+	text[ACTIVE]="#000000"
+	text[PRELIGHT]="#000000" 
+
+	fg[NORMAL]="#000000"
+	fg[ACTIVE]="#000000"
+	fg[PRELIGHT]="#000000" 
+
+	font_name = "sans 8"
+
+	GtkWidget::focus_line_width       = 1
+	GtkWidget::focus_padding                     = 0 
+	GtkWidget::interior_focus                    = 0 
+	GtkWidget::internal_padding                  = 0 
+
+}
+
+style "panel-label" {
+	font_name = "sans bold 11"
+}
+
+style "panel-text" {
+	font_name = "sans 9"
+}
+
+style "small-marker" {
+        fg[NORMAL]="#905050" 
+	font_name = "sans 6"
+}
+
+style "scale-marker" {
+	font_name = "sans 7"
+}
+
+style "frame-label" {
+	font_name = "sans bold 10"
+}
+
+style "frame-text" {
+	font_name = "sans 9"
+}
+
+style "check-poppy" {
+	bg[NORMAL]="#80a0ff" 
+
+	font_name = "sans 8"	
+	GtkButton::focus-padding = 0
+	GtkButton::focus-line-width = 1
+	GtkButton::interior-focus = 0
+}
+
+style "slider" {
+	bg[NORMAL]="#80a0ff" 
+	bg[PRELIGHT]="#c0f0ff" 
+	GtkWidget::focus-padding = 0
+	GtkWidget::focus-line-width = 1
+	GtkWidget::interior-focus = 0
+}
+
+style "multibar" {
+        bg[NORMAL]="#80a0ff" 
+        bg[ACTIVE]="#b0b0b0" 
+        bg[PRELIGHT]="#c0f0ff" 
+
+        fg[NORMAL]="#000000" 
+        fg[ACTIVE]="#ff8080" 
+        fg[PRELIGHT]="#f0f080" 
+
+        text[NORMAL]="#c0c0d0" 
+        text[ACTIVE]="#ffb0b0" 
+        font_name = "sans 7"    
+}
+
+style "multislide" {
+        bg[NORMAL]="#80a0ff" 
+        bg[ACTIVE]="#b0b0b0" 
+        bg[PRELIGHT]="#c0f0ff" 
+
+        fg[NORMAL]="#000000" 
+        fg[ACTIVE]="#ff8080" 
+        fg[PRELIGHT]="#f0f080" 
+
+        text[NORMAL]="#707070" 
+        text[ACTIVE]="#905050" 
+        font_name = "sans 7"    
+}
+
+style "clipbar" {
+	fg[NORMAL]="#404040" 
+	fg[ACTIVE]="#ff8080" 
+	text[NORMAL]="#c0c0d0" 
+	text[ACTIVE]="#c0c0d0" 
+	font_name = "sans 8"	
+}
+
+style "readout" {
+	base[NORMAL]="#ffffff" 
+	base[ACTIVE]="#ffffff" 
+	bg[NORMAL]="#ffffff" 
+	bg[ACTIVE]="#ffffff" 
+
+	font_name = "Fixed, Nimbus Mono L, Courier, Monospace 10"	
+}
+
+style "small-readout" {
+	base[NORMAL]="#ffffff" 
+	base[ACTIVE]="#ffffff" 
+	bg[NORMAL]="#ffffff" 
+	bg[ACTIVE]="#ffffff" 
+	text[NORMAL]="#606060"
+	font_name = "Fixed, Nimbus Mono L, Courier, Monospace 8"	
+}
+
+style "darkpanel" {
+	bg[NORMAL]="#b0b0b0" 
+	bg[INSENSITIVE]="#b0b0b0" 
+}
+
+style "quitbutton" {
+	bg[NORMAL]="#d0d0d0"
+	bg[PRELIGHT]="#ffc0c0"
+	bg[ACTIVE]="#ffc0c0"
+	font_name = "sans 8"	
+	GtkButton::focus-padding = 0
+	GtkButton::focus-line-width = 1
+	GtkButton::interior-focus = 0
+}
+
+style "left" {
+	text[NORMAL] = "#606060"
+	text[ACTIVE] = "#606060"
+	text[SELECTED] = "#606060"
+	text[PRELIGHT] = "#606060"
+	fg[ACTIVE] = "#606060"
+	bg[NORMAL]="#80a0ff" 
+}
+style "right" {
+	text[NORMAL] = "#cc0000"
+	text[ACTIVE] = "#cc0000"
+	text[SELECTED] = "#cc0000"
+	text[PRELIGHT] = "#cc0000"
+	bg[NORMAL]="#80a0ff" 
+}
+style "mid" {
+	text[NORMAL] = "#0000fc"
+	text[ACTIVE] = "#0000fc"
+	text[SELECTED] = "#0000fc"
+	text[PRELIGHT] = "#0000fc"
+	bg[NORMAL]="#80a0ff" 
+}
+style "side" {
+	text[NORMAL] = "#00B200"
+	text[ACTIVE] = "#00B200"
+	text[SELECTED] = "#00B200"
+	text[PRELIGHT] = "#00B200"
+	bg[NORMAL]="#80a0ff" 
+}
+
+widget "*.GtkLabel" style "panel-text"
+widget "*.scalemarker" style "scale-marker"
+widget "*.smallmarker" style "small-marker"
+
+widget "*.color0" style "left"
+widget "*.color1" style "right"
+widget "*.color2" style "mid"
+widget "*.color3" style "side"
+
+widget "*.panelbox*" style "darkpanel"
+widget "*.winpanel*" style "darkpanel"
+
+widget "*.choiceframe.*" style "frame-text"
+widget "*.GtkFrame.GtkLabel" style "frame-label"
+widget "*.GtkFrame.GtkHBox.GtkLabel" style "frame-label"
+widget "*.framelabel" style "frame-label"
+
+widget "*.Readout*" style "readout"
+widget "*.smallreadout" style "small-readout"
+widget "*.GtkEntry" style "readout"
+widget "*.GtkHScale" style "slider"
+widget "*.GtkMenu*" style "button-poppy"
+widget "*.GtkComboBox*" style "button-poppy"
+widget "*.GtkToggleButton*" style "button-poppy"
+widget "*.GtkButton*" style "button-poppy"
+widget "*.GtkCheckButton" style "check-poppy"
+widget "*.Windowbutton*" style "button-poppy"
+widget "*.windowbuttonlike" style "button-poppy"
+widget "*.quitbutton" style "quitbutton"
+widget "*.quitbutton.GtkLabel" style "quitbutton"
+
+widget "*.panelbutton*" style "button-poppy"
+widget "*.panelbutton*.GtkLabel" style "panel-label"
+widget "*.Multibar*" style "multibar"
+widget "*.Multislide*" style "multislide"
+
+widget "*.clipbar*" style "clipbar"
+
+widget "*.Slider" style "button-poppy"

Added: trunk/sushivision/sushi.h
===================================================================
--- trunk/sushivision/sushi.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/sushi.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,29 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+
+extern void sushi_submain();
+extern void sushi_new_2d(int axes, double (*objective)(double *),
+			 char **labels, double *vals,
+			 double small_step, double large_step);
+extern void sushi_axis_2d(double range_low, double range_hi,
+			  double small_step, double large_step);
+

Added: trunk/sushivision/sushivision.h
===================================================================
--- trunk/sushivision/sushivision.h	2006-10-26 15:53:19 UTC (rev 11954)
+++ trunk/sushivision/sushivision.h	2006-10-27 02:14:20 UTC (rev 11955)
@@ -0,0 +1,137 @@
+/*
+ *
+ *     sushivision copyright (C) 2006 Monty <monty at xiph.org>
+ *
+ *  sushivision is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  sushivision is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with sushivision; see the file COPYING.  If not, write to the
+ *  Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 
+ */
+
+#ifndef _SUSHIVISION_
+#define _SUSHIVISION_
+  
+typedef struct sushiv_panel sushiv_panel_t;
+typedef struct sushiv_dimension sushiv_dimension_t;
+typedef struct sushiv_objective sushiv_objective_t;
+
+typedef struct sushiv_instance {
+  int dimensions;
+  sushiv_dimension_t **dimension_list;
+
+  int objectives;
+  sushiv_objective_t **objective_list;
+
+  int panels;
+  struct sushiv_panel **panel_list;
+
+  void *internal;
+} sushiv_instance_t;
+
+#define SUSHIV_X_RANGE 0x100
+#define SUSHIV_Y_RANGE 0x200
+
+struct sushiv_dimension{ 
+  int number;
+  char *name;
+  double bracket[2];
+  double val;
+
+  int scale_vals;
+  double *scale_val_list;
+  char **scale_label_list; 
+  unsigned flags;
+  
+  int (*callback)(sushiv_dimension_t *);
+  sushiv_panel_t *panel;
+  sushiv_instance_t *sushi;
+  void *internal;
+};
+
+struct sushiv_objective { 
+  int number;
+  char *name;
+
+  double (*callback)(double[]);
+  sushiv_panel_t *panel;
+  sushiv_instance_t *sushi;
+  void *internal;
+  unsigned flags;
+};
+
+enum sushiv_panel_type { SUSHIV_PANEL_1D, SUSHIV_PANEL_2D };
+ 
+struct sushiv_panel {
+  int number;
+  char *name;
+  enum sushiv_panel_type type;
+  int realized;
+  int maps_dirty;
+
+  int dimensions;
+  sushiv_dimension_t **dimension_list;
+  int objectives;
+  sushiv_objective_t **objective_list;
+
+  int scale_vals;
+  double *scale_val_list;
+  char **scale_label_list; 
+
+  sushiv_instance_t *sushi;
+  void *internal;
+  unsigned flags;
+};
+
+extern sushiv_instance_t *sushiv_new_instance(void);
+
+extern int sushiv_new_dimension(sushiv_instance_t *s,
+				int number,
+				const char *name,
+				unsigned scalevals, 
+				double *scaleval_list,
+				int (*callback)(sushiv_dimension_t *),
+				unsigned flags);
+extern int sushiv_dim_set_scale(sushiv_dimension_t *d, 
+				unsigned scalevals, 
+				double *scaleval_list);
+extern int sushiv_dim_set_scalelabels(sushiv_dimension_t *d, 
+				      char **scalelabel_list);
+
+extern int sushiv_new_objective(sushiv_instance_t *s,
+				int number,
+				const char *name,
+				double (*callback)(double *),
+				unsigned flags);
+
+extern int sushiv_new_panel_2d(sushiv_instance_t *s,
+			       int number,
+			       const char *name, 
+			       unsigned scalevals,
+			       double *scaleval_list,
+			       int *objectives,
+			       int *dimensions,
+			       unsigned flags);
+
+extern int sushiv_new_panel_1d(sushiv_instance_t *s,
+			       int number,
+			       const char *name,
+			       unsigned scalevals,
+			       double *scaleval_list,
+			       int *objectives,
+			       int *dimensions,
+			       unsigned flags);
+
+extern int sushiv_submain(int argc, char *argv[]);
+
+#endif



More information about the commits mailing list