#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "graphics.h"


/*************************/
/*  definition of struct */
/*************************/

struct graph_axis;
struct graph_label;
struct graph_window;

struct graph_axis {
   AXIS_TYPE axis;
   double *tic, *subtic, *grid;
   char *tic_lab;
   Boolean lab_flag;
   int ntic, nsubtic, ngrid;
   double tic_size, tic_off, tic_ang, tic_fjust, tic_ch, subtic_size;
   double lab_off, lab_ang, lab_fjust, lab_ch;
   int tic_lw, subtic_lw, grid_lw, grid_ls, lab_lw;
   char lab[CPG_LABEL_LEN];
   struct graph_window *window;
};

struct graph_label {
   double x, y, ang, fjust, ch;
   int lw;
   char label[CPG_LABEL_LEN];
   Boolean disp;
};

struct graph_window {
   Boolean vpdef, windef;
   double vpx0, vpx1, vpy0, vpy1, wx0, wx1, wy0, wy1;
   double title_ch, title_off;
   int title_lw, frame_lw;
   char title[CPG_LABEL_LEN];
   struct graph_axis axis[CPG_NAXIS];
   struct graph_label *labels[NLABEL_IN_WIN];
};


/*                         */
/* static global variables */
/*                         */
static struct graph_window pgW[NWINMAX];
static int current_window;
static Boolean cpg_started=FALSE;

static int graph_cimax, graph_colorfunc;

static Boolean select_field_set_flag=FALSE;
static int select_field_align;
static int num_select_field;
static double select_field_x0[NMAX_SELECT_FIELD];
static double select_field_x1[NMAX_SELECT_FIELD];
static double select_field_y0[NMAX_SELECT_FIELD];
static double select_field_y1[NMAX_SELECT_FIELD];
static char select_field_key[NMAX_SELECT_FIELD][NMAX_SELECT_KEY];

/*                 */
/* static fuctions */
/*                 */
static void init_window(int nwin);
static void init_axis(int nwin, int axis);
static FORMAT_TYPE tic_format(double step);
static void tic_count(double min, double max, double tic, int *tic0, int *tic1);
static void tic_lab_chr(char label[], FORMAT_TYPE type, double coo);
static int make_tic(double min, double max, double step, FORMAT_TYPE type, int *tic0);
static int make_grid(double min, double max, double step, int *tic0);
static void put_frame(int nwin);
static void put_title(int nwin);
static void put_tic(struct graph_axis *Ap);
static void put_tic_tic(struct graph_axis *Ap);
static void put_tic_lab(struct graph_axis *Ap);
static void put_subtic(struct graph_axis *Ap);
static void put_grid(struct graph_axis *Ap);
static void put_axis_lab(struct graph_axis *Ap);
static void put_label(struct graph_label *Lp);
static void make_select_field_boundary ( int align );
static int which_field_selected ( float xp, float yp );
static void plot_select_field( void );
static int data_xy_nearest ( double xobj, double yobj, int nref, double *xref, double *yref, double tolerance);
static double distance(double x1, double y1, double x2, double y2);



/*                         */
/* definitions of fuctions */
/*                         */

static void init_window(int nwin){
   int i;
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"init_window")==FALSE) exit(1);
   Wp=pgW+nwin;
   Wp->vpdef=FALSE; Wp->windef=FALSE;
   Wp->vpx0=0.0; Wp->vpx1=1.0; Wp->vpy0=0.0; Wp->vpy1=1.0;
   Wp->wx0=0.0; Wp->wx1=1.0; Wp->wy0=0.0; Wp->wy1=1.0;
   Wp->title_ch=2.0; Wp->title_lw=2; Wp->title_off=0.04; Wp->frame_lw=2;
   strcpy(Wp->title,"");
   for(i=0;i<CPG_NAXIS;i++) init_axis(nwin,i);
   for(i=0;i<NLABEL_IN_WIN;i++) Wp->labels[i]=NULL;
}

static void init_axis(int nwin, int axis){
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"init_axis")==FALSE) exit(1);
   if(check_axis(axis,"init_axis")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   Ap->axis=axis;
   Ap->lab_flag=TRUE;
   Ap->tic=NULL; Ap->subtic=NULL; Ap->grid=NULL;
   Ap->ntic=0; Ap->nsubtic=0; Ap->ngrid=0;
   Ap->tic_lab=NULL;
   switch(axis){
   case AXIS_X_LOW   :
      Ap->tic_off=-0.08; Ap->tic_fjust=0.5; Ap->tic_ang=0.0;
      Ap->lab_off=-0.16; Ap->lab_fjust=0.5; Ap->lab_ang=0.0; break;
   case AXIS_X_UP    :
      Ap->tic_off= 0.02; Ap->tic_fjust=0.5; Ap->tic_ang=0.0;
      Ap->lab_off= 0.10; Ap->lab_fjust=0.5; Ap->lab_ang=0.0; break;
   case AXIS_Y_LEFT  :
      Ap->tic_off=-0.02; Ap->tic_fjust=1.0; Ap->tic_ang=0.0;
      Ap->lab_off=-0.10; Ap->lab_fjust=0.5; Ap->lab_ang=90.0; break;
   case AXIS_Y_RIGHT :
      Ap->tic_off= 0.02; Ap->tic_fjust=0.0; Ap->tic_ang=0.0;
      Ap->lab_off= 0.10; Ap->lab_fjust=0.5; Ap->lab_ang=90.0; break;
   default: exit(1);
   }
   Ap->tic_size=0.05; Ap->tic_ch=1; Ap->tic_lw=1;
   Ap->subtic_size=0.025; Ap->subtic_lw=1; Ap->grid_lw=1;
   Ap->lab_lw=1; Ap->lab_ch=1;
   Ap->grid_ls=4;
   strcpy(Ap->lab,"");  
   Ap->window=pgW+nwin;
}

void cpgstart(char *dev, double width, double aspect){
   int i;
   if(cpg_started==TRUE){
      fprintf(stderr,"duplicate execution of cpgstart !\n"); exit(1);
   }
   if ( cpgbeg( 0, dev,1, 1 ) != 1 ){
      fprintf(stderr,"cpgstart : opening pgplot device failed.\n"); exit(1);
   }
   current_window=NWINMAX;
   for(i=0;i<NWINMAX;i++) init_window(i);
   cpgpap(width, aspect);
   cpg_started=TRUE;
}

void free_axis(int nwin, int axis){
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"free_axis")==FALSE) exit(1);
   if(check_axis(axis,"free_axis")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   if(Ap->tic!=NULL){ free(Ap->tic); Ap->tic=NULL; }
   if(Ap->subtic!=NULL){ free(Ap->subtic); Ap->subtic=NULL; }
   if(Ap->grid!=NULL){ free(Ap->grid); Ap->grid=NULL; }
   if(Ap->tic_lab!=NULL){ free(Ap->tic_lab); Ap->tic_lab=NULL; }
   init_axis(nwin,axis);
}

void free_label(int nwin, int id){
   struct graph_label *Lp=NULL;
   if(check_nwin(nwin,"free_label")==FALSE) exit(1);
   if(check_label_id(id,"free_label")==FALSE) exit(1);
   Lp=pgW[nwin].labels[id];
   if(Lp!=NULL){ free(Lp); Lp=NULL; }
}

void cpgfinish(void){
   int nwin, i;
   if(cpg_started==FALSE){
      fprintf(stderr,"cpgfinish : cpgstart not executed yet!\n"); exit(1);
   }
   for(nwin=0;nwin<NWINMAX;nwin++){
      for(i=0;i<CPG_NAXIS;i++) free_axis(nwin,i);
      for(i=0;i<NLABEL_IN_WIN;i++) free_label(nwin,i);
   }
   cpgclos();
   //cpgend();
   cpg_started=FALSE;
}

Boolean check_nwin(int nwin, char *func){
   if(nwin<0||nwin>=NWINMAX){
      fprintf(stderr,"graph_lib: nwin is out of range in %s\n",func);
      return FALSE;
   }
   return TRUE;
}

double window_param( int nwin, WIN_PARAM_TYPE type ){
   double tmp=0.0;
   struct graph_window *Wp=NULL;
   Wp=pgW+nwin;
   switch(type){
   case WIN_VPX0 : tmp=Wp->vpx0; break;
   case WIN_VPX1 : tmp=Wp->vpx1; break;
   case WIN_VPY0 : tmp=Wp->vpy0; break;
   case WIN_VPY1 : tmp=Wp->vpy1; break;
   case WIN_RANGE_VPX : tmp=(Wp->vpx1)-(Wp->vpx0); break;
   case WIN_RANGE_VPY : tmp=(Wp->vpy1)-(Wp->vpy0); break;
   case WIN_WX0 : tmp=Wp->wx0; break;
   case WIN_WX1 : tmp=Wp->wx1; break;
   case WIN_WY0 : tmp=Wp->wy0; break;
   case WIN_WY1 : tmp=Wp->wy1; break;
   case WIN_RANGE_WX : tmp=(Wp->wx1)-(Wp->wx0); break;
   case WIN_RANGE_WY : tmp=(Wp->wy1)-(Wp->wy0); break;
   default: fprintf(stderr,"graph_lib : incorrect window_param\n"); tmp=0.0;
   }
   return tmp;
}

void setvp(int nwin, double x0, double x1, double y0, double y1){
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"setvp")==FALSE) exit(1);
   Wp=pgW+nwin;
   Wp->vpx0=x0; Wp->vpx1=x1; Wp->vpy0=y0; Wp->vpy1=y1; Wp->vpdef=TRUE;
}

void setwin(int nwin, double x0, double x1, double y0, double y1){
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"setwin")==FALSE) exit(1);
   Wp=pgW+nwin;
   Wp->wx0=x0; Wp->wx1=x1; Wp->wy0=y0; Wp->wy1=y1; Wp->windef=TRUE;
}

void set_title(int nwin, const char *title){
   if(title!=NULL) strcpy(pgW[nwin].title,title);
}

void change_title_ch_lw(int nwin, double ch, int lw){
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"change_title_ch_lw")==FALSE) exit(1);
   if(ch<=0||lw<=0){
      fprintf(stderr,"change_title_ch_lw : ch and lw should be positive !\n");
      return;
   }
   Wp=pgW+nwin;
   Wp->title_ch=ch; Wp->title_lw=lw;
}

void change_title_off(int nwin, double off){
   if(check_nwin(nwin,"change_title_off")==FALSE) exit(1);
   pgW[nwin].title_off=off;
}

void change_frame_lw(int nwin, int lw){
   if(check_nwin(nwin,"change_frame_lw")==FALSE) exit(1);
   if(lw<=0){
      fprintf(stderr,"change_frame_lw: lw should be positive!\n"); return;
   }
   pgW[nwin].frame_lw=lw;
}

int get_current_window( void ){
   return current_window;
}

void window(int nwin){
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"window")==FALSE) exit(1);
   Wp=pgW+nwin;
   if(Wp->vpdef!=TRUE||Wp->windef!=TRUE){
      fprintf(stderr,"graph_lib: A Window No. %d is not defined !\n",nwin);
      exit(1);
   }
   cpgsvp(Wp->vpx0, Wp->vpx1, Wp->vpy0, Wp->vpy1);
   cpgswin(Wp->wx0, Wp->wx1, Wp->wy0, Wp->wy1);
   current_window=nwin;
}

void undef_graph_vp(int nwin){
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"undef_graph_vp")==FALSE) exit(1);
   Wp=pgW+nwin;
   Wp->vpx0=0.0; Wp->vpx1=1.0; Wp->vpy0=0.0; Wp->vpy1=1.0;
   Wp->vpdef=FALSE;
}

void undef_graph_win(int nwin){
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"undef_graph_win")==FALSE) exit(1);
   Wp=pgW+nwin;
   Wp->wx0=0.0; Wp->wx1=1.0; Wp->wy0=0.0; Wp->wy1=1.0;
   Wp->windef=FALSE;
}

Boolean check_axis(int axis, char *func){
   if(axis<0||axis>=CPG_NAXIS){
      fprintf(stderr,"graph_lib: axis is out of range in %s\n",func);
      return FALSE;
   }
   return TRUE;
}

void change_tic_lab_flag(int nwin, int axis, Boolean flag){
   if(check_nwin(nwin,"change_tic_lab_flag")==FALSE) exit(1);
   if(check_axis(axis,"change_tic_lab_flag")==FALSE) exit(1);
   pgW[nwin].axis[axis].lab_flag=flag;
}

void change_tic_size(int nwin, int axis, double size){
   if(check_nwin(nwin,"change_tic_size")==FALSE) exit(1);
   if(check_axis(axis,"change_tic_size")==FALSE) exit(1);
   pgW[nwin].axis[axis].tic_size=size;
}

void change_subtic_size(int nwin, int axis, double size){
   if(check_nwin(nwin,"change_subtic_size")==FALSE) exit(1);
   if(check_axis(axis,"change_subtic_size")==FALSE) exit(1);
   pgW[nwin].axis[axis].subtic_size=size;
}

void change_tic_ch_lw(int nwin, int axis, double ch, int lw){
   if(check_nwin(nwin,"change_tic_ch_lw")==FALSE) exit(1);
   if(check_axis(axis,"change_tic_ch_lw")==FALSE) exit(1);
   if(ch<=0||lw<=0){
      fprintf(stderr,"change_tic_ch_lw : ch and lw should be positive !\n");
      return;
   }
   pgW[nwin].axis[axis].tic_ch=ch;
   pgW[nwin].axis[axis].tic_lw=lw;
}

void change_subtic_lw(int nwin, int axis, int lw){
   if(check_nwin(nwin,"change_subtic_lw")==FALSE) exit(1);
   if(check_axis(axis,"change_subtic_lw")==FALSE) exit(1);
   if(lw<=0){
      fprintf(stderr,"change_subtic_lw: lw should be positive!\n"); return;
   }
   pgW[nwin].axis[axis].subtic_lw=lw;
}

void change_grid_lw(int nwin, int axis, int lw){
   if(check_nwin(nwin,"change_grid_lw")==FALSE) exit(1);
   if(check_axis(axis,"change_grid_lw")==FALSE) exit(1);
   if(lw<=0){
      fprintf(stderr,"change_grid_lw: lw should be positive!\n"); return;
   }
   pgW[nwin].axis[axis].grid_lw=lw;
}

void change_grid_ls(int nwin, int axis, int ls){
   if(check_nwin(nwin,"change_grid_ls")==FALSE) exit(1);
   if(check_axis(axis,"change_grid_ls")==FALSE) exit(1);
   pgW[nwin].axis[axis].grid_ls=ls;
}

void change_axis_lab_ch_lw(int nwin, int axis, double ch, int lw){
   if(check_nwin(nwin,"change_axis_lab_ch_lw")==FALSE) exit(1);
   if(check_axis(axis,"change_axis_lab_ch_lw")==FALSE) exit(1);
   if(ch<=0||lw<=0){
      fprintf(stderr,"change_axis_lab_ch_lw : ch and lw should be positive !\n"); return;
   }
   pgW[nwin].axis[axis].lab_ch=ch;
   pgW[nwin].axis[axis].lab_lw=lw;
}

void change_tic_off(int nwin, int axis, double off){
   if(check_nwin(nwin,"change_tic_off")==FALSE) exit(1);
   if(check_axis(axis,"change_tic_off")==FALSE) exit(1);
   pgW[nwin].axis[axis].tic_off=off;
}

void change_axis_lab_off(int nwin, int axis, double off){
   if(check_nwin(nwin,"change_axis_lab_off")==FALSE) exit(1);
   if(check_axis(axis,"change_axis_lab_off")==FALSE) exit(1);
   pgW[nwin].axis[axis].lab_off=off;
}

void change_tic_ang_fjust(int nwin, int axis, double ang, double fjust){
   if(check_nwin(nwin,"change_tic_ang_fjust")==FALSE) exit(1);
   if(check_axis(axis,"change_tic_ang_fjust")==FALSE) exit(1);
   pgW[nwin].axis[axis].tic_ang=ang;
   pgW[nwin].axis[axis].tic_fjust=fjust;
}

void change_axis_lab_ang_fjust(int nwin, int axis, double ang, double fjust){
   if(check_nwin(nwin,"change_axis_lab_ang_fjust")==FALSE) exit(1);
   if(check_axis(axis,"change_axis_lab_ang_fjust")==FALSE) exit(1);
   pgW[nwin].axis[axis].lab_ang=ang;
   pgW[nwin].axis[axis].lab_fjust=fjust;
}

static FORMAT_TYPE tic_format(double step){
   FORMAT_TYPE tmp;
   double order;
   order=log10(step);
   if(order>=5) tmp=FORMAT_1E;
   else if(order>=0) tmp=FORMAT_D;
   else if(order>=-1) tmp=FORMAT_1F;
   else if(order>=-2) tmp=FORMAT_2F;
   else if(order>=-3) tmp=FORMAT_3F;
   else if(order>=-4) tmp=FORMAT_4F;
   else tmp=FORMAT_1E;
   return tmp;
}

static void tic_count(double min, double max, double step, int *tic0, int *tic1){
   int i, pos;
   double coo, tic=0;
   if(step==0||fabs(min-max)/tic<=1.0){
      fprintf(stderr,"graph_lib: incorrect tic range\n");
      fprintf(stderr,"\tmin=%g max=%g tic=%g\n",min,max,tic);
      *tic0=0; *tic1=0;
      return ;
   }
   if(step<0) tic=-step;
   else tic=step;
   pos=(int)((min+max)/2.0/tic);
   for(i=0;;i++){
      coo=(pos+i)*tic;
      if((coo-min)*(coo-max)>0){
         *tic1=pos+i-1;
         break;
      }
   }
   for(i=0;;i--){
      coo=(pos+i)*tic;
      if((coo-min)*(coo-max)>0){
         *tic0=pos+i+1;
         break;
      }
   }
   return ;
}

/* ---- frametic ------------------------------------------ */
/* find the best values for min, max, and tic for the frame */
/* flag = 0 : un-fixed value for both min and max            */
/* flag = 1 : fixed value for min and un-fixed value for max */
/* flag = 2 : un-fixed value for min and fixed value for max */
/* flag = 3 : fixed value for both min and max               */
/* flag = 4 : un-fixed value and ask parameters              */
/* -------------------- Matsunaga N. (22/09/2002) ---------- */
void frametic(int flag, double *min, double *max, double *tic){
  int order, sign, tmp;
  double digit;

  if((*min)<(*max)) sign=1;
  else if((*min)>(*max)) sign=-1;
  else {
     fprintf(stderr,"frametic : initial minmax are equivalent %f.\n",(*min));
     exit(1);
  }

  order=floor(log10(fabs((*max)-(*min))));
  digit=fabs((*max)-(*min))/pow(10,order);
  if(digit<=1.0) (*tic)=0.2*pow(10,order);
  else if(digit<=1.5) (*tic)=0.3*pow(10,order);
  else if(digit<=2.5) (*tic)=0.5*pow(10,order);
  else if(digit<=5.0) (*tic)=pow(10,order);
  else (*tic)=2.0*pow(10,order);

  switch(flag){
  case 0:
    (*max)=(*tic)*floor((*max)/(*tic)+(1.2*sign+1.0)/2.0);
    (*min)=(*tic)*floor((*min)/(*tic)+(-1.2*sign+1.0)/2.0); break;
  case 1:
    tmp=ceil(fabs((*max)-(*min))/(*tic)+0.2);
    (*max)=(*min)+sign*tmp*(*tic);                          break;
  case 2:
    tmp=ceil(fabs((*max)-(*min))/(*tic)+0.2);
    (*min)=(*max)-sign*tmp*(*tic);                          break;
  case 3:
                                                            break;
  case 4:
    printf("\nmin=%.3f  max=%.3f  tic%.3f\n",*min,*max,*tic);
    printf("Please enter min, max, and tic : ");
    scanf("%lf %lf %lf",min,max,tic);                       break;
  default:
                                                            break;
  }
}

void set_tic(int nwin, int axis, double min, double max, double step, FORMAT_TYPE type){
   FORMAT_TYPE tic_type;
   int ntic, i, tic0;
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"set_tic")==FALSE) exit(1);
   if(check_axis(axis,"set_tic")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   if(type==FORMAT_AUTO) tic_type=tic_format(step);
   else tic_type=type;
   ntic=make_tic(min,max,step,tic_type,&tic0);
   Ap->ntic=ntic;
   Ap->tic=malloc(ntic*sizeof(double));
   Ap->tic_lab=malloc(ntic*CPG_TIC_LEN*sizeof(char));
   for(i=0;i<ntic;i++){
      Ap->tic[i]=(i+tic0)*step;
      tic_lab_chr(Ap->tic_lab+i*CPG_TIC_LEN,type,Ap->tic[i]);
   }
}

void set_axis_label(int nwin, int axis, const char *lab){
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"set_axis_label")==FALSE) exit(1);
   if(check_axis(axis,"set_axis_label")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   if(lab!=NULL) strcpy(Ap->lab,lab);
}

static void tic_lab_chr(char label[], FORMAT_TYPE type, double coo){
   switch(type){
      case FORMAT_D : sprintf(label,"%d",(int)floor(coo+1.0e-5)); break;
      case FORMAT_1F : sprintf(label,"%.1f",coo); break;
      case FORMAT_2F : sprintf(label,"%.2f",coo); break;
      case FORMAT_3F : sprintf(label,"%.3f",coo); break;
      case FORMAT_4F : sprintf(label,"%.4f",coo); break;
      case FORMAT_1E : sprintf(label,"%.1e",coo); break;
      case FORMAT_2E : sprintf(label,"%.2e",coo); break;
      case FORMAT_G : sprintf(label,"%g",coo); break;
      default : sprintf(label,"%g",coo); break;
   }
}


void copy_tic(int nwin, int axis, int ntic, const double *tic, FORMAT_TYPE type){
   int i;
   FORMAT_TYPE tic_type;
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"copy_tic")==FALSE) exit(1);
   if(check_axis(axis,"copy_tic")==FALSE) exit(1);
   if(type==FORMAT_AUTO) tic_type=FORMAT_G;
   else tic_type=type;
   Ap=&(pgW[nwin].axis[axis]);
   Ap->tic=malloc(ntic*sizeof(double));
   Ap->tic_lab=malloc(ntic*CPG_TIC_LEN*sizeof(char));
   for(i=0;i<ntic;i++){
      Ap->tic[i]=tic[i];
      tic_lab_chr(Ap->tic_lab+i*CPG_TIC_LEN,tic_type,Ap->tic[i]);
   }
   pgW[nwin].axis[axis].ntic=ntic;
}

void copy_tic_with_lab(int nwin, int axis, int ntic, const double *tic, const char **lab){
   int i;
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"copy_tic_with_lab")==FALSE) exit(1);
   if(check_axis(axis,"copy_tic_with_lab")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   Ap->tic=malloc(ntic*sizeof(double));
   Ap->tic_lab=malloc(ntic*CPG_TIC_LEN*sizeof(char));
   for(i=0;i<ntic;i++){
      Ap->tic[i]=tic[i]; strcpy(Ap->tic_lab+i*CPG_TIC_LEN,lab[i]);
   }
   pgW[nwin].axis[axis].ntic=ntic;
}

void set_subtic(int nwin, int axis, double min, double max, double step){
   struct graph_axis *Ap=NULL;
   int i, tic0, ngrid;
   if(check_nwin(nwin,"set_subtic")==FALSE) exit(1);
   if(check_axis(axis,"set_subtic")==FALSE) exit(1);
   if(fabs(max-min)/step>1000){
      fprintf(stderr,"Too many subtic grid (min=%g max=%g step=%g)!\n",min,max,step);
      return ;
   }
   Ap=&(pgW[nwin].axis[axis]);
   ngrid=make_grid(min,max,step,&tic0);
   Ap->nsubtic=ngrid;
   Ap->subtic=malloc(ngrid*sizeof(double));
   for(i=0;i<ngrid;i++) Ap->subtic[i]=(i+tic0)*step;
}

void copy_subtic(int nwin, int axis, int nsubtic, double *subtic){
   int i;
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"copy_subtic")==FALSE) exit(1);
   if(check_axis(axis,"copy_subtic")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   Ap->subtic=malloc(nsubtic*sizeof(double));
   Ap->nsubtic=nsubtic;
   for(i=0;i<nsubtic;i++) Ap->subtic[i]=subtic[i];
}

void set_grid(int nwin, int axis, double min, double max, double step){
   struct graph_axis *Ap=NULL;
   int i, ngrid, tic0;
   if(check_nwin(nwin,"set_grid")==FALSE) exit(1);
   if(check_axis(axis,"set_grid")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   ngrid=make_grid(min,max,step,&tic0);
   Ap->ngrid=ngrid;
   Ap->grid=malloc(ngrid*sizeof(double));
   for(i=0;i<ngrid;i++) Ap->grid[i]=(i+tic0)*step;
}

void copy_grid(int nwin, int axis, int ngrid, double *grid){
   int i;
   struct graph_axis *Ap=NULL;
   if(check_nwin(nwin,"copy_grid")==FALSE) exit(1);
   if(check_axis(axis,"copy_grid")==FALSE) exit(1);
   Ap=&(pgW[nwin].axis[axis]);
   Ap->grid=malloc(ngrid*sizeof(double));
   Ap->ngrid=ngrid;
   for(i=0;i<ngrid;i++) Ap->grid[i]=grid[i];
}

static int make_tic(double min, double max, double step, FORMAT_TYPE type, int *tic0){
   int ntic, tic1;
   tic_count(min,max,step,tic0,&tic1);
   ntic=fabs(tic1-(*tic0))+1;
   return ntic;
}

static int make_grid(double min, double max, double step, int *tic0){
   int ngrid, tic1;
   tic_count(min,max,step,tic0,&tic1);
   ngrid=fabs(tic1-(*tic0))+1;
   return ngrid;
}


Boolean check_label_id(int id, char *func){
   if(id<0||id>=CPG_TIC_LEN){
      fprintf(stderr,"graph_lib: label_id is out of range in %s\n",func);
      return FALSE;
   }
   return TRUE;
}

void set_label(int nwin, int id, double x, double y, const char *label){
   struct graph_label *Lp=NULL;
   if(check_nwin(nwin,"set_label")==FALSE) exit(1);
   if(check_label_id(id,"set_label")==FALSE) exit(1);
   if(pgW[nwin].labels[id]!=NULL) free_label(nwin,id);
   pgW[nwin].labels[id]=malloc(sizeof(struct graph_label));
   Lp=pgW[nwin].labels[id];
   strcpy(Lp->label,label);
   Lp->x=x; Lp->y=y; Lp->disp=TRUE;
   Lp->ang=0.0; Lp->fjust=0.5; Lp->ch=1.0; Lp->lw=1;
}

void change_label_xy(int nwin, int id, double x, double y){
   struct graph_label *Lp=NULL;
   if(check_nwin(nwin,"change_label_xy")==FALSE) exit(1);
   if(check_label_id(id,"change_label_xy")==FALSE) exit(1);
   Lp=pgW[nwin].labels[id];
   if(Lp==NULL){
      fprintf(stderr,"change_label_xy : label id=%d is not defined (nwin=%d)\n",id,nwin);
   } else { Lp->x=x; Lp->y=y; }
}

void change_label_ang_fjust(int nwin, int id, double ang, double fjust){
   struct graph_label *Lp=NULL;
   if(check_nwin(nwin,"change_label_ang_fjust")==FALSE) exit(1);
   if(check_label_id(id,"change_label_ang_fjust")==FALSE) exit(1);
   Lp=pgW[nwin].labels[id];
   if(Lp==NULL){
      fprintf(stderr,"change_label_ang_fjust : label id=%d is not define (nwin=%d)\n",id,nwin);
   } else { Lp->ang=ang; Lp->fjust=fjust; }
}

void change_label_ch_lw(int nwin, int id, double ch, int lw){
   struct graph_label *Lp=NULL;
   if(check_nwin(nwin,"change_label_ch_lw")==FALSE) exit(1);
   if(check_label_id(id,"change_label_ch_lw")==FALSE) exit(1);
   if(ch<=0||lw<=0){
      fprintf(stderr,"change_label_ch_lw : ch and lw should be positive!\n");
      return ;
   }
   Lp=pgW[nwin].labels[id];
   if(Lp==NULL){
      fprintf(stderr,"change_label_ch_lw : label id=%d is not defined (nwin=%d)\n",id,nwin);
   } else { Lp->ch=ch; Lp->lw=lw; }
}

void change_label_lab(int nwin, int id, const char *label){
   struct graph_label *Lp=NULL;
   if(check_nwin(nwin,"change_label_lab")==FALSE) exit(1);
   if(check_label_id(id,"change_label_lab")==FALSE) exit(1);
   Lp=pgW[nwin].labels[id];
   if(Lp==NULL){
      fprintf(stderr,"change_label_lab: label id=%d is not defined (nwin=%d)\n",id,nwin);
   } else strcpy(Lp->label,label);
}

void switch_label_disp(int nwin, int id, Boolean flag){
   struct graph_label *Lp=NULL;
   if(check_nwin(nwin,"switch_label_disp")==FALSE) exit(1);
   if(check_label_id(id,"switch_label_disp")==FALSE) exit(1);
   Lp=pgW[nwin].labels[id];
   if(Lp==NULL){
      fprintf(stderr,"Label id=%d is not define (nwin=%d)\n",id,nwin);
   } else Lp->disp=flag;
}


void mkframe(int nwin){
   int i, ls0, lw0;
   float ch0;
   if(check_nwin(nwin,"mkframe")==FALSE) exit(1);
   cpgqch(&ch0); cpgqlw(&lw0); cpgqls(&ls0);
   window(nwin);
   put_frame(nwin);
   put_title(nwin);
   for(i=0;i<CPG_NAXIS;i++) put_tic(pgW[nwin].axis+i);
   for(i=0;i<NLABEL_IN_WIN;i++) put_label(pgW[nwin].labels[i]);
   cpgsch(ch0); cpgslw(lw0); cpgsls(ls0);
}

static void put_frame(int nwin){
   double wx0, wx1, wy0, wy1, dx, dy;
   struct graph_window *Wp=NULL;
   if(check_nwin(nwin,"put_frame")==FALSE) exit(1);
   Wp=pgW+nwin;
   wx0=Wp->wx0; wx1=Wp->wx1; dx=wx1-wx0;
   wy0=Wp->wy0; wy1=Wp->wy1; dy=wy1-wy0;
   cpgslw(Wp->frame_lw);
   cpgmove(wx0+1.0e-3*dx,wy0+1.0e-3*dy);
   cpgdraw(wx0+1.0e-3*dx,wy1-1.0e-3*dy);
   cpgdraw(wx1-1.0e-3*dx,wy1-1.0e-3*dy);
   cpgdraw(wx1-1.0e-3*dx,wy0+1.0e-3*dy);
   cpgdraw(wx0+1.0e-3*dx,wy0+1.0e-3*dy);
}

static void put_title(int nwin){
   struct graph_window *Wp=NULL;
   double xp, yp;
   if(check_nwin(nwin,"put_title")==FALSE) exit(1);
   Wp=pgW+nwin;
   xp=(Wp->wx0+Wp->wx1)/2.0;
   yp=(1+Wp->title_off)*Wp->wy1-Wp->title_off*Wp->wy0;
   cpgsch(Wp->title_ch); cpgslw(Wp->title_lw);
   cpgptxt(xp,yp,0.0,0.5,Wp->title);
}

static void put_tic(struct graph_axis *Ap){
   put_tic_tic(Ap);
   if(Ap->lab_flag==TRUE) put_tic_lab(Ap);
   put_subtic(Ap);
   put_grid(Ap);
   put_axis_lab(Ap);
}

static void put_tic_tic(struct graph_axis *Ap){
   int i;
   AXIS_TYPE axis;
   double x0, x1, y0, y1, size; 
   double coo, on_axis, off_axis;
   axis=Ap->axis;
   x0=Ap->window->wx0; x1=Ap->window->wx1;
   y0=Ap->window->wy0; y1=Ap->window->wy1;
   size=Ap->tic_size;
   switch(axis){
   case AXIS_X_LOW   : on_axis=y0; off_axis=y0+size*(y1-y0); break;
   case AXIS_X_UP    : on_axis=y1; off_axis=y1-size*(y1-y0); break;
   case AXIS_Y_LEFT  : on_axis=x0; off_axis=x0+size*(x1-x0); break;
   case AXIS_Y_RIGHT : on_axis=x1; off_axis=x1-size*(x1-x0); break;
   default: exit(1);
   }
   cpgslw(Ap->tic_lw);
   for(i=0;i<Ap->ntic;i++){
      coo=Ap->tic[i];
      if(axis==AXIS_X_LOW||axis==AXIS_X_UP){
         cpgmove(coo,on_axis); cpgdraw(coo,off_axis);
      } else if(axis==AXIS_Y_LEFT||axis==AXIS_Y_RIGHT){
         cpgmove(on_axis,coo); cpgdraw(off_axis,coo);
      }
   }
}

static void put_tic_lab(struct graph_axis *Ap){
   int i;
   AXIS_TYPE axis;
   double x0, x1, y0, y1, off, ang, fjust;
   double coo, off_axis;
   axis=Ap->axis;
   x0=Ap->window->wx0; x1=Ap->window->wx1;
   y0=Ap->window->wy0; y1=Ap->window->wy1;
   off=Ap->tic_off; ang=Ap->tic_ang; fjust=Ap->tic_fjust;
   switch(axis){
   case AXIS_X_LOW   : off_axis=y0+off*(y1-y0); break;
   case AXIS_X_UP    : off_axis=y1+off*(y1-y0); break;
   case AXIS_Y_LEFT  : off_axis=x0+off*(x1-x0); break;
   case AXIS_Y_RIGHT : off_axis=x1+off*(x1-x0); break;
   default: exit(1);
   }
   cpgslw(Ap->tic_lw); cpgsch(Ap->tic_ch);
   for(i=0;i<Ap->ntic;i++){
      coo=Ap->tic[i];
      if(axis==AXIS_X_LOW||axis==AXIS_X_UP){
         cpgptxt(coo,off_axis,ang,fjust,Ap->tic_lab+i*CPG_TIC_LEN);
      } else if(axis==AXIS_Y_LEFT||axis==AXIS_Y_RIGHT){
         cpgptxt(off_axis,coo,ang,fjust,Ap->tic_lab+i*CPG_TIC_LEN);
      }
   }
}

static void put_subtic(struct graph_axis *Ap){
   AXIS_TYPE axis;
   int i;
   double x0, x1, y0, y1, size;
   double coo, on_axis, off_axis;
   if(Ap->subtic==NULL) return;
   axis=Ap->axis;
   x0=Ap->window->wx0; x1=Ap->window->wx1;
   y0=Ap->window->wy0; y1=Ap->window->wy1;
   size=Ap->subtic_size;
   switch(axis){
   case AXIS_X_LOW   : on_axis=y0; off_axis=y0+size*(y1-y0); break;
   case AXIS_X_UP    : on_axis=y1; off_axis=y1-size*(y1-y0); break;
   case AXIS_Y_LEFT  : on_axis=x0; off_axis=x0+size*(x1-x0); break;
   case AXIS_Y_RIGHT : on_axis=x1; off_axis=x1-size*(x1-x0); break;
   default: exit(1);
   }
   cpgslw(Ap->subtic_lw);
   for(i=0;i<Ap->nsubtic;i++){
      coo=Ap->subtic[i];
      if(axis==AXIS_X_LOW||axis==AXIS_X_UP){
         cpgmove(coo,on_axis); cpgdraw(coo,off_axis);
      } else if(axis==AXIS_Y_LEFT||axis==AXIS_Y_RIGHT){
         cpgmove(on_axis,coo); cpgdraw(off_axis,coo);
      }
   }
}

static void put_grid(struct graph_axis *Ap){
   AXIS_TYPE axis;
   int i, ls, ls0;
   double x0, x1, y0, y1, coo;
   if(Ap->grid==NULL) return;
   axis=Ap->axis;
   x0=Ap->window->wx0; x1=Ap->window->wx1;
   y0=Ap->window->wy0; y1=Ap->window->wy1;
   ls=Ap->grid_ls; cpgqls(&ls0);
   cpgslw(Ap->grid_lw); cpgsls(ls);
   for(i=0;i<Ap->ngrid;i++){
      coo=Ap->grid[i];
      if(axis==AXIS_X_LOW||axis==AXIS_X_UP){
         cpgmove(coo,y0); cpgdraw(coo,y1);
      } else if(axis==AXIS_Y_LEFT||axis==AXIS_Y_RIGHT){
         cpgmove(x0,coo); cpgdraw(x1,coo);
      }
   }
   cpgsls(ls0);
}

static void put_axis_lab(struct graph_axis *Ap){
   AXIS_TYPE axis;
   double xp, yp, x0, x1, y0, y1, off, ang, fjust;
   axis=Ap->axis;
   x0=Ap->window->wx0; x1=Ap->window->wx1;
   y0=Ap->window->wy0; y1=Ap->window->wy1;
   off=Ap->lab_off; ang=Ap->lab_ang; fjust=Ap->lab_fjust;
   switch(axis){
   case AXIS_X_LOW   : xp=(x0+x1)/2.0; yp=y0+off*(y1-y0); break;
   case AXIS_X_UP    : xp=(x0+x1)/2.0; yp=y1+off*(y1-y0); break;
   case AXIS_Y_LEFT  : yp=(y0+y1)/2.0; xp=x0+off*(x1-x0); break;
   case AXIS_Y_RIGHT : yp=(y0+y1)/2.0; xp=x1+off*(x1-x0); break;
   default: exit(1);
   }
   cpgslw(Ap->lab_lw); cpgsch(Ap->lab_ch);
   cpgptxt(xp,yp,ang,fjust,Ap->lab);
}

static void put_label(struct graph_label *Lp){
   double xp, yp, ang, fjust;
   if(Lp==NULL||Lp->disp!=TRUE) return;
   cpgsch(Lp->ch); cpgslw(Lp->lw);
   xp=Lp->x; yp=Lp->y; ang=Lp->ang; fjust=Lp->fjust;
   if(Lp->label!=NULL) cpgptxt(xp,yp,ang,fjust,Lp->label);
}


void xyplot (int num, double *x, double *y, int symbol){
   float xp, yp;
   int i;
   for(i=0;i<num;i++){
      xp=(float)x[i]; yp=(float)y[i];
      cpgpt(1, &xp, &yp, symbol);
   }
}

void xyplot_ex (int num, double *x, double *y, double *xe, int symbol){
   int i;
   xyplot(num,x,y,symbol);
   for(i=0;i<num;i++) draw_error_bar(x[i],y[i],xe[i],0);
}

void xyplot_ey (int num, double *x, double *y, double *ye, int symbol){
   int i;
   xyplot(num,x,y,symbol);
   for(i=0;i<num;i++) draw_error_bar(x[i],y[i],0,ye[i]);
}

void xyplot_exy (int num, double *x, double *y, double *xe, double *ye, int symbol){
   int i;
   xyplot(num,x,y,symbol);
   for(i=0;i<num;i++) draw_error_bar(x[i],y[i],xe[i],ye[i]);
}

void draw_error_bar(double xcen, double ycen, double xerror, double yerror){
   cpgmove(xcen-xerror,ycen);
   cpgdraw(xcen+xerror,ycen);
   cpgmove(xcen,ycen-yerror);
   cpgdraw(xcen,ycen+yerror);
}

void plcurve(double x0, double x1, double step, double (*pfunc)(double x)){
  double xp, yp, tmp;
  if(x0>x1){ tmp=x0; x0=x1; x1=tmp; }
  if(step<0) step = -step;
  xp=x0;
  yp=(*pfunc)(xp);
  cpgmove(xp,yp);
  while(xp<x1){
    xp+=step;
    yp=(*pfunc)(xp);
    cpgdraw(xp,yp);
  }
}

void drawline(double a, double b, double x0, double x1){
   float xp, yp;
   xp=x0; yp=a*xp+b; cpgmove(xp,yp);
   xp=x1; yp=a*xp+b; cpgdraw(xp,yp);
}

/* y=a+b*x+c*x^2 */
void drawpara(double a, double b, double c, double x0, double x1, double dx){
   float xp, yp;
   double tmp;
   if(x0>x1){ tmp=x0; x0=x1; x1=tmp; }
   if(dx<0) dx*=-1;
   xp=x0; yp=a+b*xp+c*xp*xp; cpgmove(xp,yp);
   while(xp<x1){
      xp+=dx;
      yp=a+b*xp+c*xp*xp;
      cpgdraw(xp,yp);
   }
}

void plot_polygon_line (int ndata, double *x, double *y){
   int i;
   cpgmove(x[0],y[0]);
   for(i=1;i<ndata;i++) cpgdraw(x[i],y[i]);
}

void plot_polygon (int ndata, double *x, double *y){
   int i;
   cpgmove(x[0],y[0]);
   for(i=1;i<ndata;i++) cpgdraw(x[i],y[i]);
   cpgdraw(x[0],y[0]);
}

void plot_histogram (int ndata, double *x, double *y){
   int i;
   float x0, x1, yp;
   x0=x[0]; yp=0.0; cpgmove(x0,yp);
   for(i=0; i<ndata-1; x0=x1,i++){
      x1=(x[i]+x[i+1])/2.0;
      yp=y[i];
      cpgdraw(x0,yp);
      cpgdraw(x1,yp);
   }
   cpgdraw((x[ndata-2]+x[ndata-1])/2.0,y[ndata-1]);
   cpgdraw(x[ndata-1],y[ndata-1]);
   cpgdraw(x[ndata-1],0.0);
}




/* ******************** */
/*  geometry condition  */
/* ******************** */

/* ## only for concave polygons ## */
/* return  1 if the point is within the object */
/* return  0 if the point is on the line of the object */
/* return -1 if the point is outside the object */
int inside( double xp, double yp, int num, double x[], double y[] ){
  int i, sign, tmp;
  double x0, y0, x1, y1;
  for(i=0;i<num;i++){
    x0=x[i]; y0=y[i];
    if(i==num-1){ x1=x[0]; y1=y[0]; }
    else { x1=x[i+1]; y1=y[i+1]; }
    if(online(x0,y0,xp,yp,x1,y1)>0) return 0;
  }
  sign=sideLR(xp,yp,x[0],y[0],x[num-1],y[num-1]);
  for(i=0;i<num-1;i++){
     tmp=sideLR(xp,yp,x[i+1],y[i+1],x[i],y[i]);
     if(sideLR(xp,yp,x[i+1],y[i+1],x[i],y[i])!=sign) return -1;
  }
  return 1;
}

/* return 2 or 3 in case of coincidence with one point */
/* return 1 in case of online */
/* return 0 otherwise */
int online(double x0, double y0, double xp, double yp, double x1, double y1){
  double dx0, dy0, dx1, dy1;
  dx0=xp-x0; dy0=yp-y0;
  dx1=xp-x1; dy1=yp-y1;
  if(dx0==0.0&&dy0==0.0) return 3;
  if(dx1==0.0&&dy1==0.0) return 2;
  if(dx0==0.0){
    if(dx1==0&&dy0*dy1<0) return 1;
    else return 0;
  } else {
    if(dx1==0.0) return 0;
    if(dy1/dx1-dy0/dx0==0.0&&dx0*dx1<0) return 1;
    else return 0;
  }
  return 0;
}

/* return the sign of the vector product (xp-x0,yp-y0) cross (x1-x0,y1-y0) */
int sideLR( double xp, double yp, double x0, double y0, double x1, double y1 ){
  double sp;
  sp=(xp-x0)*(y1-y0)-(yp-y0)*(x1-x0);
  if(sp==0) return 0;
  if(sp>0) return 1;
  else return -1;
}

/* return 1 if there is a concave point for the polygon */
int concave( int num, double x[], double y[]){
  int i, j, offset;
  double xtest[NPOLYMAX], ytest[NPOLYMAX] ;
  for(i=0;i<num;i++){
    offset=0;
    for(j=0;j<num;j++) if(j==i){
       offset=-1;
    }
    else {
       xtest[j+offset]=x[j];
       ytest[j+offset]=y[j];
    }
    if(inside(x[i],y[i],num-1,xtest,ytest)>0) return 1;
  }
  return 0;
}

Boolean in_window_or_not(int nwin, double xp, double yp){
   if(check_nwin(nwin,"in_window_or_not")==FALSE) exit(1);
   if((pgW[nwin].wx0-xp)*(pgW[nwin].wx1-xp)>0) return FALSE;
   if((pgW[nwin].wy0-yp)*(pgW[nwin].wy1-yp)>0) return FALSE;
   return TRUE;
}


void set_graph_cimax(int cimax){
   graph_cimax=cimax;
}

void set_colorfunc(int colorfunc){
   if(colorfunc>=1&&colorfunc<=7) graph_colorfunc=colorfunc;
   else askcolorscale();
}

void setcolors(int nstep){
  int i;
  double hue;
  if(nstep>CIMAX-15){
    printf("the number of steps in colorscale should be less than %d.\n",CIMAX-16);
    nstep=CIMAX-15;
  }
  graph_cimax=15+nstep;
  for(i=0;i<=nstep;i++){
     hue=360.0-240.0*i/(double)nstep;
     cpgshls(16+i,hue,0.5,1.0);
  }
}

void setgrays(int nstep){
  int i;
  double hue;
  if(nstep>CIMAX-15){
    printf("the number of steps in colorscale should be less than %d.\n",CIMAX-16);
    nstep=CIMAX-15;
  }
  graph_cimax=15+nstep;
  for(i=0;i<=nstep;i++){
     hue=0.66*(double)i/(double)nstep;
     cpgshls(16+i,0,hue,0.0);
  }
}

int colorscale(double scale){
  int ci;
  if(scale<0.0) scale=0.0;
  else if(scale>1.0) scale=1.0;
  ci=(int)(scale*(graph_cimax-16)+16.0);
  cpgsci(ci);
  return 0;
}

void colorlegend(int nwin, double vmin, double vmax){
  struct graph_window *Wp=NULL;
  int i;
  float x0, x1, y0, y1;
  double value, vtic;
  Wp=pgW+nwin;
  if(Wp->vpdef!=TRUE){
     fprintf(stderr,"graph_lib: no vp for colorlegend\n"); exit(1);
  }
  frametic(3,&vmin,&vmax,&vtic);
  if(Wp->vpx1-Wp->vpx0 > Wp->vpy1-Wp->vpy0){
     setwin(nwin,vmin,vmax,0.0,1.0);
     set_tic(nwin,AXIS_X_LOW,vmin,vmax,vtic,FORMAT_AUTO);
     window(nwin);
     for(i=0;i<=graph_cimax-15;i++){
        value=vmin+(double)i/(double)(graph_cimax-15);
        setcolorscale(value,vmin,vmax);
        x0=vmin+(double)i*(vmax-vmin)/(double)(graph_cimax-15);
        x1=vmin+(double)(i+1)*(vmax-vmin)/(double)(graph_cimax-15);
        y0=Wp->wy0; y1=Wp->wy1;
        cpgrect(x0,x1,y0,y1);
     }
  } else {
     setwin(nwin,0.0,1.0,vmin,vmax);
     set_tic(nwin,AXIS_Y_LEFT,vmin,vmax,vtic,FORMAT_AUTO);
     window(nwin);
     for(i=0;i<=graph_cimax-15;i++) {
        value=vmin+(double)i*(vmax-vmin)/(double)(graph_cimax-15);
        setcolorscale(value,vmin,vmax);
        x0=Wp->wx0; x1=Wp->wx1;
        y0=vmin+(double)i*(vmax-vmin)/(double)(graph_cimax-15);
        y1=vmin+(double)(i+1)*(vmax-vmin)/(double)(graph_cimax-15);
        cpgrect(x0,x1,y0,y1);
     }
  }
  cpgsci(1);
  mkframe(nwin);
}

void askcolorscale(void){
   int tmp;
   printf("What scale for color ? (1=linear/2=root/3=4sqrt/4=log/5=sq/6=sin/7=asin) : ");
   scanf("%d",&tmp);
   graph_colorfunc=tmp;
}
 
void setcolorscale(double value, double vmin, double vmax){
   double scale;
   switch(graph_colorfunc){
   case 2:
      scale=sqrt((value-vmin)/(vmax-vmin)); break;
   case 3:
      scale=sqrt(sqrt((value-vmin)/(vmax-vmin))); break;
   case 4:
      scale=log10(1+9.0*(value-vmin)/(vmax-vmin)); break;
   case 5:
      scale=pow((value-vmin)/(vmax-vmin),2); break;
   case 6:
      scale=(sin(M_PI*(value-(vmin+vmax)/2.0)/(vmax-vmin))/2.0)+0.5; break;
   case 7:
      scale=(asin(2.0*(value-(vmin+vmax)/2.0)/(vmax-vmin))/M_PI)+0.5; break;
   default:
      scale=(value-vmin)/(vmax-vmin); break;
   }
   colorscale(scale);
}


/* ********************* */
/*  interactive utility  */
/* ********************* */
   
char mouse_get_coo( int nwin, double *x, double *y){
   int ier;
   float xp,yp;
   char keyinput;
   window(nwin);
   do{
      ier = cpgcurs(&xp, &yp, &keyinput);
   } while ( in_window_or_not(nwin,xp,yp)!=0 );
   *x=(double)xp; *y=(double)yp;
   return keyinput;
}

int mouse_get_points( int nwin, int get[], int ndata, double x[], double y[], double tolerance, int ndatamax){
   int num, tmp, color;
   double xp, yp, sep;
   float xf, yf;
   char keyinput;
   cpgqci(&color);
   if(color==2) cpgsci(3);
   else cpgsci(2);
   num=0;
   fprintf(stderr," put 'q' to quit ");
   do{
      keyinput=mouse_get_coo(nwin,&xp,&yp);
      if(keyinput=='q'||keyinput=='Q') break;
      else {
         tmp=data_xy_nearest(xp,yp,ndata,x,y,tolerance);
         if(tmp<0||tmp>=ndata) continue;
         sep=sqrt(pow(xp-x[tmp],2)+pow(xp-x[tmp],2));
         if(sep<tolerance){
            xf=(float)x[tmp]; yf=(float)y[tmp];
            cpgpt(1,&xf,&yf,17);
            get[num]=tmp;
            ++num;
         }
      }
   } while(num<ndatamax);
   cpgsci(color);
   return num;
}


int mouse_put_points( int nwin, double x[], double y[], int ndatamax ){
   int num=0, color;
   double xp, yp;
   float xf, yf;
   char keyinput;
   cpgqci(&color);
   if(color==2) cpgsci(3);
   else cpgsci(2);
   do{
      keyinput=mouse_get_coo(nwin,&xp,&yp);
      if(keyinput=='q'||keyinput=='Q') break;
      else {
         x[num]=xp; y[num]=yp;
         xf=(float)xp; yf=(float)yp; cpgpt(1,&xf,&yf,17);
         ++num;
      }
   } while(num<=ndatamax);
   cpgsci(color);
   return num;
}

int mkpolygon( double x[], double y[], void (*plotpgbg)(void)){
  int i,ier, npoint, menu;
  char keyinput;
  float xp, yp;
  printf("\nSPACE=input  b=back  d=delete  q=quit\n");
  printf("   the number of points should be less than %d\n",NPOLYMAX);
  npoint=0;
  cpgeras();
  (*plotpgbg)();
  do{
    ier=cpgcurs(&xp, &yp, &keyinput);
    switch(keyinput){
    case ' ' :
      x[npoint]=(double)xp; y[npoint]=(double)yp;
      cpgsci(2);
      if(npoint==0) cpgmove(xp,yp);
      else if(npoint==NPOLYMAX){
        printf("## the number of points should be less than %d. ##\n",NPOLYMAX);
        printf("## PLEASE make a polygon without concave angles! ##\n");
        npoint=0;
        continue;
      } else cpgdraw(xp,yp);
      cpgpt(1,&xp,&yp,18);
      cpgsci(1);
      ++npoint;
      break;
    case 'b' :
      cpgeras();
      (*plotpgbg)();
      --npoint;
      cpgsci(2);
      xp=(float)x[0]; yp=(float)y[0];
      cpgpt(1,&xp,&yp,18);
      cpgmove(xp,yp);
      for(i=1;i<npoint;i++){
        xp=(float)x[i]; yp=(float)y[i];
        cpgdraw(xp,yp);
        cpgpt(1,&xp,&yp,18);
      }
      cpgsci(1);
      break;
    case 'd' :
      cpgeras();
      (*plotpgbg)();
      npoint=0;
      break;
    case 'q' :
      cpgsci(2);
      xp=(float)x[0]; yp=(float)y[0];
      cpgdraw(xp,yp);
      if(concave(npoint,x,y)==1){
        printf("## PLEASE make a polygon without concave angles! ##\n");
        menu=2;
      } else {
        printf("\n menu 1:extract  2:re-try  3:give-up : ");
        scanf("%d",&menu);
      }
      switch(menu){
      case 2:
         cpgeras();
         (*plotpgbg)();
         npoint=0;
         continue;
      case 3:
         goto err ;
      default:
         cpgsci(1);
         break;
      }
    default :
      break;
    }
  } while(keyinput!='q');

  return npoint;
err :
  return -1;
}

/* align = 0 for horizontal, 1 for vertical */
void set_select_field ( int nwin, int num_select, int align,
         double x0, double x1, double y0, double y1 ){
   int i;
   setvp( nwin, x0, x1, y0, y1 );
   setwin( nwin, -0.01, 1.01, -0.01, 1.01 );
   num_select_field = num_select;
   select_field_align = align;
   for(i=0;i<num_select_field;i++){
      sprintf(select_field_key[i],"%d",i);
   }
   make_select_field_boundary(align);
   select_field_set_flag=TRUE;
}

int set_select_field_key ( int field, char key[] ){
   if( select_field_set_flag!=TRUE){
     fprintf(stderr,"mouse_select_field : select_fields are unset !\n");
     exit(1);
   }
   strcpy(select_field_key[field],key);
   return 0;
}

int mouse_select_field( int nwin ){
   int tmp;
   double xp, yp;
   if( select_field_set_flag!=TRUE) {
      fprintf(stderr,"mouse_select_field : select_fields are unset !\n");
      exit(1);
   }
   window(nwin);
   plot_select_field( );
   do{
      mouse_get_coo(nwin,&xp,&yp);
      tmp = which_field_selected ( xp, yp );
   } while ( tmp<0 || tmp>=num_select_field );
   return tmp;
}

/* align = 0 for horizontal, 1 for vertical */
static void make_select_field_boundary ( int align ){
   int i;
   double step;
   step=1.0/(double)num_select_field;
   switch(align){
   case 0 : for(i=0;i<num_select_field;i++){
               select_field_x0[i]=(double)i * step;
               select_field_x1[i]=(double)(i+1) * step;
               select_field_y0[i]=0.0;   select_field_y1[i]=1.0;
            }
            break;
   case 1 : for(i=0;i<num_select_field;i++){
               select_field_x0[i]=0.0;  select_field_x1[i]=1.0;
               select_field_y0[i]=(double)(num_select_field-i-1) * step;
               select_field_y1[i]=(double)(num_select_field-i) * step;
            }
            break;
   default: fprintf(stderr,"make_select_field_boundary : align != 0 nor 1\n");
            exit(1);
   }
}

static int which_field_selected ( float xp, float yp ){
   int i, tmp=NMAX_SELECT_FIELD;
   if(select_field_set_flag!=TRUE){
     fprintf(stderr,"which_field_selected : select_fields are unset !\n");
     exit(1);
   }
   for(i=0;i<num_select_field;i++){
      if( (xp-select_field_x0[i])*(xp-select_field_x1[i])<0 &&
          (yp-select_field_y0[i])*(yp-select_field_y1[i])<0 ){
         tmp=i;
         break;
      }
      if(i==num_select_field-1) goto error;
   }
   return tmp;
error :
   fprintf(stderr,"which_field_selected : (%g %g) is out of range!\n",xp,yp);
   return num_select_field;
}

static void plot_select_field( void ){
   int i;
   float xp, yp;
   if( select_field_set_flag!=TRUE){
     fprintf(stderr,"mouse_select_field : select_fields are unset !\n");
     exit(1);
   }
   cpgsci(1); cpgsfs(2);
   for(i=0;i<num_select_field;i++){
      cpgrect(select_field_x0[i],select_field_x1[i],select_field_y0[i],select_field_y1[i]);
      xp=(select_field_x0[i]+select_field_x1[i])/2.0;
      yp=(select_field_y0[i]+select_field_y1[i])/2.0;
      cpgptxt( xp, yp, 0.0, 0.5, select_field_key[i] );
   }
}

static int data_xy_nearest ( double xobj, double yobj, int nref, double *xref, double *yref, double tolerance){
   int i,nmin;
   double min,r;
   if(nref<2){
      fprintf(stderr,"data_xy_nearest : incorrect array size\n"); return -1;
   }
   nmin=0;
   min=distance(xobj,yobj,xref[nmin],yref[nmin]);
   for(i=1;i<nref;i++){
      if(fabs(xobj-xref[i])<tolerance&&fabs(yobj-yref[i])<tolerance){
         r=distance(xref[i],yref[i],xobj,yobj);
         if(r<min){
            nmin=i;
            min=r;
         }
      }
   }
   return nmin;
}

static double distance(double x1, double y1, double x2, double y2){
   return sqrt( pow(x1-x2,2)+pow(y1-y2,2) );
}
