/*===========================================================================
  Copyright (C) 1993-2009 European Southern Observatory (ESO)
 
  This program 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 of 
  the License, or (at your option) any later version.
 
  This program 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 this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENTifer   module PLGRA
.AUTHOR      R.M. van Hees IPG-ESO Garching
.KEYWORDS    plot software, graphics, bulk data fame, gray scale plotting
.LANGUAGE    C
.PURPOSE     Grayscale plotting routine for two dimensional data
.COMMENTS    holds PLGRA, WEDGE and PLGRAI
.ENVIRONment MIDAS and AGL
             #include <agl.h>           Prototypes for AGL application programs
             #include <midas_def.h>     Prototypes for MIDAS interfaces
             #include <plot_def.h>      Symbols used by the PLT interfaces

.VERSION     1.1     23-Sep-1993   FORTRAN --> ANSI-C    RvH

090422		last modif
------------------------------------------------------------*/
/*
 * Define _POSIX_SOURCE to indicate
 * that this is a POSIX program
 */
#define  _POSIX_SOURCE 1

/*
 * definition of the used functions in this module
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <midas_def.h>

/*
 * define some macros and constants
 */
#include <plot_def.h>

#ifndef RAND_MAX
#define RAND_MAX	32768.0
#endif

/*
 * The array "options" holds TRUE and FALSE values for the following options
 */
#define LINS	( (*options == FALSE) ? TRUE : FALSE )      /*lineair scaling*/
#define LOGS	( (*options == TRUE)  ? TRUE : FALSE )         /*log. scaling*/
#define CONT	( (*(options+1) == FALSE) ? TRUE : FALSE ) /*cont. gray scale*/
#define STEP	( (*(options+1) == TRUE)  ? TRUE : FALSE )    /*stepwise gray*/
#define FLIP	( (*(options+2) == TRUE) ? TRUE : FALSE )       /*change sign*/
#define ABSV	( (*(options+3) == TRUE) ? TRUE : FALSE )   /*take abs values*/
#define CUTP	( (*(options+4) == TRUE) ? TRUE : FALSE ) /*only gray between
 								   cut values*/
/*++++++++++++++++++++++++++++++
.IDENTifer   DWLOG
.PURPOSE     return the "traditional" Dwingeloo/Westerbork log scaling
     input    double value
.RETURNS     Dwi/Wbrk log
.COMMENTS    static function
--------------------------------*/
#ifdef __STDC__
      static double DWLOG( double value )
#else
      static double DWLOG( value )
      double value;
#endif
{
value *= 0.549999 + 0.45 * value;
return log10( 30 * value + 1 )/ log10( 30.42 ) - 0.007;
}

/*++++++++++++++++++++++++++++++
.IDENTifer   HALFT
.PURPOSE     Produce halftone plot of a 2-D image
     input:    double vnom    normalized intensity: 0 <= vnom <= 1.0
               int    ipx     number of pixels in the X direction
               int    ipy     number of pixels in the Y direction
               double pixx    pixel size in world coordinates in X
               double pixy    pixel size in world coordinates in Y
               double xstr    position of stip in plotter coordinates
               double ystr    position of stip in plotter coordinates
               float  greynes grayness parameter: 0 <= greyness <= 1.0

.COMMENTS    static function
--------------------------------*/
#ifdef __STDC__
      static void HALFT( double vnom, int ipx, int ipy, double pixx, 
                         double pixy, double xstr, double ystr, float greynes )
#else
      static void HALFT( vnom, ipx, ipy, pixx, pixy, xstr, ystr, greynes )
      int    ipx, ipy; 
      float  greynes;
      double vnom, pixx, pixy, xstr, ystr;
#endif
{
float  xpos, ypos;
double rrand;

register int    ix, iy;

if ( (ipx+1) * pixx < 1.0 ) ipx +=1;

vnom *= greynes * RAND_MAX;
for ( ix = 0; ix < ipx; ix++ )
    { for ( iy = 0; iy < ipy; iy++ )
          { rrand = fmod( (double) rand(), RAND_MAX );
            
            if ( rrand < vnom )
               { xpos = (float) (xstr + ix * pixx);
                 ypos = (float) (ystr + iy * pixy);
                 AG_GPLM( &xpos, &ypos, 1, 0 );
               }
          }
    }
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENTifer   PLGRA
.PURPOSE     Gray scale plotting routine for two dimensional data
     input:   float  *p_img   pointer to the data of the frame
              float  *image   image size in pixel coordinates
              float  *area    image size in world coordinates
              double *step    distance between pixels in world units
              float  *glevl   level of the countours
              int    nlevl    number of countours
              int    *options holds the following options: 
                                 LIN - LOG  gray scaling
                                 STEP - CONTineously increasing gray scale
                                 POS - NEG  increasing or decreasing gray scale
                                 ABS        take the absolute values
                                 CUTS       only gray scaling between the cuts
              float  greynes  grayness parameter between 0.0 and 1.0,
                                 1.0 gives the maximum blackness
                                 0.0 means white

.COMMENTS    if image[0] > image[1] or image[2] > image[3] then
             the values are put in the right order
--------------------------------*/
#ifdef __STDC__
void PLGRA(float *p_img, float *image, float *area, double *step, float *glevl, int nlevl, int *options, float greynes)
#else
void PLGRA( p_img, image, area, step, glevl, nlevl, options, greynes )
int    nlevl, *options;
float  *p_img, *image, *area, *glevl, greynes;
double *step;
#endif
{
int    ipx, ipy, ipy1, ix, iy, nl, nx, ny, pixnx, pixny;

float  rres[2], clpl[4], wndl[4];

double delwx, delwy, delcx, delcy, diff, eps, pixx, pixy, val, vnom, 
       xnom, xstr, ystr, ynom;

/*
 * this functions only works if the number of levels is greater than zero
 */
if ( nlevl == 0 ) return;

/*
 * get viewport characteristics
 */
(void) AG_RGET( "clpl", clpl );
(void) AG_RGET( "wndl", wndl );
(void) AG_RGET( "reso", rres );
AG_SSET( "sydi=0.10" );

/*
 * get the start world coordinates and window characteristics
 */
nx = (int) fabs( image[1] - image[0] ) + 1;
ny = (int) fabs( image[3] - image[2] ) + 1;

if ( *step < 0 )
   xnom = MYMAX( area[0], area[1] );
else
   xnom = MYMIN( area[0], area[1] );

delwx = wndl[1] - wndl[0];                               /* window size in X */
delwy = wndl[3] - wndl[2];                               /* window size in Y */
delcx = fabs( clpl[1] - clpl[0] );                     /* clipping size in X */
delcy = fabs( clpl[3] - clpl[2] );                     /* clipping size in Y */
pixnx = NINT( delcx / rres[0] );                       /* number of X pixels */
pixny = NINT( delcy / rres[1] );                       /* number of Y pixels */
pixx  = delwx / pixnx;                          /* size of a X pixel in w.c. */
pixy  = delwy / pixny;                          /* size of a Y pixel in w.c. */
eps   = fabs( *glevl * 0.0001 );

/*
 * start the code
 */
ipx = (int) ceil( *step/pixx );
ipy = (int) ceil( *(step+1)/pixy );

/*
 * reverse order of contours and reverse sign
 */
if ( FLIP )
   { for ( nl = 0; nl < NINT( nlevl/2.0 ); nl++ )
         { register int   nn = nlevl - nl - 1;
           register float dummy = *(glevl+nl);
           *(glevl+nl) = - *(glevl+nn);
           *(glevl+nn) = - dummy;
         }
   }
/*
 * Begin value in Y of gray - tone
 * ynom = nominal plot position; ystr = actual starting plot position
 */
if ( *(step+1) < 0 )
   ynom = MYMAX( area[2], area[3] ) - 1.5 * *(step+1);
else
   ynom = MYMIN( area[2], area[3] ) - 1.5 * *(step+1);
ystr = ynom;

for ( iy = 0; iy < ny - 1; iy++ )
    { ystr += ipy * pixy;
      ynom += *(step+1);
      diff = ystr - ynom;
      if ( diff < 0 ) ystr += pixy;               /* Does it start too low?  */
         
      if ( diff > pixy ) ystr -= pixy;            /* Does it start too high? */
/*
 * how many lines within grid-interval 
 */
      ipy1 = (int) ceil( (ynom - ystr + *(step+1))/pixy );

      for ( ix = 0; ix < nx; ix++ )
          { xstr = xnom + (ix - 0.5) * *step;         /* start position in X */

            val = *(p_img+ix);
            if ( fabs( val ) < eps ) val = 0.0;

            if ( ABSV ) val = fabs( val );
            if ( FLIP ) val = -val;

            if ( val > (double) *glevl )
               { if ( val >= (double) *(glevl+nlevl - 1) )
                    { val = *(glevl+nlevl-1);
                      if ( CUTP ) val += eps;
                    }
               }

            vnom = 0.0;
            if ( STEP )
               { if ( nlevl > 1 )
                    { for ( nl = 0; nl < nlevl-1; nl++ )
                          { if ( val >= (double) *(glevl+nl) 
                                 && val < (double) *(glevl+nl+1))
                               { vnom = (double) (nl+1) / nlevl;
                                 if ( LOGS )         /* "log" scale Dwi/Wbrk.*/
                                    vnom = DWLOG( vnom );
                               }
                          }
                    }
                 if ( val == (double) *(glevl+nlevl-1) )
                    { vnom = 1.0;
                       if ( LOGS )                   /* "log" scale Dwi/Wbrk.*/
                          vnom = DWLOG( vnom );
                    }
               }
            else    
               { if ( val >= (double) *glevl && val <= (double) *(glevl+1) )
                    { vnom = (val - *glevl)/fabs( *(glevl+1) - *glevl );
                      if ( LOGS )                    /* "log" scale Dwi/Wbrk.*/
                         vnom = DWLOG( vnom );
                    }
               }
            if ( val >= (double) *glevl && val <= (double) *(glevl+nlevl-1) )
               HALFT( vnom, ipx, ipy1, pixx, pixy, xstr, ystr, greynes);
          }
/*
 * set the pointer to the begin of the new line to be drawn
 */
      p_img += nx;

    }
/*
 * reverse order of contours and reverse sign to get the original values
 */
if ( FLIP )
   { for ( nl = 0; nl < NINT( nlevl/2.0 ); nl++ )
         { register int   nn = nlevl - nl - 1;
           register float dummy = *(glevl+nl);
           *(glevl+nl) = - *(glevl+nn);
           *(glevl+nn) = - dummy;
         }
   }

return;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENTifer   WEDGE
.PURPOSE     plots a gray scale wedge
     input:
              float  *glevl   level of the countours
              int    nlevl    number of countours
              int    *option  holds the following options: 
                                 LIN  - LOG gray scaling
                                 STEP - CONTineously increasing gray scale
                                 POS  - NEG increasing or decreasing gray scale
                                 ABS        take the absolute values
                                 CUTS       only gray scaling between the cuts
              float  greynes  grayness parameter between 0.0 and 1.0,
                                 1.0 gives the maximum blackness
                                 0.0 means white
.COMMENTS    
----------------------------------------------------*/
#ifdef __STDC__
void WEDGE(double *step, float *glevl, int nlevl, int *options, float greynes)
#else
void WEDGE( step, glevl, nlevl, options, greynes )
int    nlevl, *options;
float  *glevl, greynes;
double *step;
#endif
{
int    il, ix, iy, ngray, nx, ny;
float  eps, clpl[4], frame[4], image[4], xl[2], yl[2], *wdata;

(void) AG_RGET( "clpl", clpl );

/*
 * define the area for the wedge
 */
clpl[2] = clpl[3] - (clpl[3] - clpl[2]) / 20;
AG_VN2U( clpl[0], clpl[2], frame  , frame+2 );
AG_VN2U( clpl[1], clpl[3], frame+1, frame+3 );
eps = 0.00001;

/*
 * draw a line between the wedge and the gray scale plot
 */
xl[0] = frame[0];
xl[1] = frame[1];
yl[0] = frame[2];
yl[1] = frame[2];
AG_GPLL( xl, yl, 2 );

nx = (int) fabs( (frame[1] - frame[0]) / *step ) + 2;
ny = (int) fabs( (frame[3] - frame[2]) / *(step+1) ) + 2;
wdata = (float *) osmmget( nx * ny * sizeof( float ));

if ( STEP )
   { ngray = NINT( (float) nx / nlevl );  /* # gr. pnts. with same gray tone */
     for ( iy = 0; iy < ny; iy++ )
         { for ( il = 0; il < nlevl-1; il++ )
               { eps = 0.00001 * (double) glevl[il];
                 for ( ix = 0; ix < ngray; ix++ )
                     *(wdata + (il-1) * ngray + ix) = *(glevl+il) + eps;
               }
           for ( ix = (nlevl-1) * ngray; ix < nx; ix++ )
               *(wdata + ix) = *(glevl+il) + eps;
           wdata += nx;
         }
     wdata -= nx * ny;
   }
else                                                /* continuous gray scale */
   { for ( iy = 0; iy < ny; iy++ )
         { for ( ix = 0; ix < nx; ix++ )
               { *(wdata + ix) = *glevl + ix * ( *(glevl+1) - *glevl) / nx;
               } 
           wdata += nx;
         }
     wdata -= nx * ny;
   }
/*
 * size of the wedge in pixel coordinates
 */
image[0] = image[2] = 0;
image[1] = nx;
image[2] = ny;

/*
 * size of the wedge in world coordinates
 */
frame[0] += 1.5 * *step;
frame[1] += 1.5 * *step;
frame[2] += 0.5 * *(step+1);
frame[3] += 0.5 * *(step+1);
PLGRA( wdata, image, frame, step, glevl, nlevl, options, greynes );

return;
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENTifer   PLGRAI
.PURPOSE     produce plot information for a two-dimensional grayscaler plot
     input:   int   plmode    plot mode, see PMODE in PLISTAT
              char  *name     data frame name
              char  *ident    ascii identifier of image
              int   nlevl     number of contour levels
              float *glevl    values of the contours
.COMMENTS    none
----------------------------------------------------*/
void PLGRAI( plmode, name, ident, nlevl, glevl, options )
int   plmode, nlevl, *options;
char  *name, *ident;
float *glevl; 

{
int    actvals, ii;
float  one, ssize, tsize, x1, x2, y1, y2, xt, yt, yh, xstr;
float  mnmx[2], scale[2], xl[3], yl[3], clpl[4], image[4], wndl[4];
char   buff[81];

/*
 * only plot mode is 2
 */
if ( plmode != 2 ) return;

/*
 * get the symbol and character dimensions, from the MIDAS keywords
 */
PCKRDR( "SSIZE", 1, &actvals, &ssize );
PCKRDR( "TSIZE", 1, &actvals, &tsize );

/*
 * if the symbol size or the text size is not equal to 1, set it to 1
 * and call PCTSET to get the proper sizes for the MIDAS layout
 */
if ( ssize != 1.0 || tsize != 1.0 )
   { one = 1.0;
     PCKWRR( "SSIZE", 1, &one );
     PCKWRR( "TSIZE", 1, &one );
   }
PCTSET();

AG_SSET( "norm");
AG_SSET( "linx");
AG_SSET( "liny");
(void) AG_RGET( "clpl", clpl );
(void) AG_RGET( "wndl", wndl );
x1 = clpl[1] + 0.01;
x2 = 1.0;
y1 = 0.0;
y2 = clpl[3];
AG_CDEF( x1, x2, y1, y2);
AG_WDEF( 0.0, 1.0, 0.0, 1.0 );

/*
 * plot MIDAS logo
 */
PLLOGI( &xt, &yt );

/* 
 * set character height
 */
AG_SSET( "sydi=0.75;chdi=0.75,0.75" );
AG_TGET( "M", xl, yl );
yh = 2.0 * yl[1];

/*
 * plot user name
 */
PLUSER( buff );
AG_GTXT( xt, yt, buff, 1 );

/*
 * name of frame
 */
yt -= 2*yh;
if ( strlen( name ) > (size_t) 12 )
   { AG_GTXT( xt, yt, "Frame:", 1 );
     yt -= yh;
     AG_GTXT( xt, yt, name, 1 ); 
   }
else
   { (void) sprintf( buff, "Frame: %s", name );
     AG_GTXT( xt, yt, buff, 1 );
   }
/*
 * identification
 */
if ( strlen( ident ) > (size_t) 0 )
   { yt -= 2*yh;
     AG_GTXT( xt, yt, "Identification:", 1 );
     yt -= yh;
     AG_GTXT( xt, yt, ident, 1 );
   }
/*
 * area
 */
PCKRDR( "PIXEL", 4, &actvals, image );
yt -= 2*yh;
AG_GTXT( xt, yt, "Area:", 1 );
yt -= yh;
(void) sprintf( buff, "X: %.0f to %.0f", image[0], image[1] );
AG_GTXT( xt, yt, buff, 1 );
yt -= yh;
(void) sprintf( buff, "Y: %.0f to %.0f", image[2], image[3] );
AG_GTXT( xt, yt, buff, 1 );
/*
 * scales
 */
PCKRDR( "SCALES", 2, &actvals, scale );
yt -= 2*yh;
AG_GTXT( xt, yt, "Scales:", 1 );
yt -= yh;
(void) sprintf( buff, "X: %-.6g", scale[0] );
AG_GTXT( xt, yt, buff, 1 );
yt -= yh;
(void) sprintf( buff, "Y: %-.6g", scale[1] );
AG_GTXT( xt, yt, buff, 1 );
/*
 * minimum and maximum
 */
PCKRDR( "ZWNDL", 2, &actvals, mnmx );
yt -= 2*yh;
(void) sprintf( buff, "Min: %-.3g ", mnmx[0] );
AG_GTXT( xt, yt, buff, 1 );
yt -= yh;
(void) sprintf( buff, "Max: %-.3g ", mnmx[1] );
AG_GTXT( xt, yt, buff, 1 );

/*
 * contour levels
 */
yt -= 2*yh;
if ( STEP )
   AG_GTXT( xt, yt, "Gray scale levels:", 1 );
else
   { nlevl = 2;
     AG_GTXT( xt, yt, "Gray scale range:", 1 );
   }

for ( ii = 0; ii < nlevl; ii++ )
    { xstr = xt + 11 * xl[1];
      if ( ii % 2 == 0 )
         { xstr = xt;
           yt  -= yh;
         }
      (void) sprintf( buff, "%8.5g", glevl[ii] );
      AG_GTXT( xstr, yt, buff, 1 );
    }

yt -= 2.*yh;
PLDATI( &xt, &yt );

/*
 * back to standard sizes
 */
AG_CDEF( clpl[0], clpl[1], clpl[2], clpl[3] );
AG_WDEF( wndl[0], wndl[1], wndl[2], wndl[3] );

/*
 * reset if necessary the symbol and text size
 */
if ( ssize != 1.0 || tsize != 1.0 )
   { PCKWRR( "SSIZE", 1, &ssize );
     PCKWRR( "TSIZE", 1, &tsize );
     PCTSET();
   }
return;
}
