/*===========================================================================
  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 PLPER
.AUTHOR      R.M. van Hees IPG-ESO Garching
.KEYWORDS    plot software, graphics, bulk data fame, perspective  plotting
.LANGUAGE    C
.PURPOSE     Perspective plotting routine for two dimensional data
.COMMENTS    holds the functions : PLPER and PLPERI
             and static functions: TRANSPOSE, RC_COEF, L_STEEPER, L_CROSS,
                                   DRAW_LINES and HIDE_LINE
.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.3     24-Apr-1994   improved layout & removed bugs, RvH
             1.2     30-Nov-1993   2-D & 3-D data is passed in structs, RvH
             1.1     20-Sep-1993   Created by R.M. van Hees

 090424		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 <string.h>
#include <math.h>
#include <midas_def.h>

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

#define CASE_A		0                     /* Defined TRANSPOSE actions */
#define CASE_B		1
#define CASE_C		2
#define CASE_D		3

#define MAXCROSS	10                    /* Maximum number of crossings */
#define MEMSIZE		(4 * (ln_dim + ln_mx))

#define STEEPER		TRUE		      /* Constants used by L_STEEPER */
#define NOT_STEEPER	FALSE

#define OUTSIDE		-2
#define ABOVE		-1
#define BELOW		 0

/*++++++++++++++++++++++++++++++
.IDENTifer   TRANSPOSE
.PURPOSE     Dedicated routine; leave projection as is, but change the indices
             in such a way that the image is build up from the users viewpoint
             (buttom to top)
.INPUT/OUTPUT
      input    float  azim        :  viewing angle, only the azimuth
               double *step       :  distance between the pixels in w.c.
               int    nxval       :  number of pixels along the first axis
               int    nyval       :  number of pixels along the second axis
   in/ouput    struct Pdata *data :  data to be transposed 
--------------------------------*/
#ifdef __STDC__
      static void TRANSPOSE( float azim, double *step, int nrx, 
                             int nry, struct Pdata *data )
#else
      static void TRANSPOSE( azim, step, nrx, nry, data )
      int    nrx, nry;
      float  azim;
      double *step;
      struct Pdata *data;
#endif
{
int    trans;

/*
 * Determine the type of transposition we have to do
 */ 
if ( azim < 90.0 )
   { if ( step[0] > 0.0 && step[1] > 0.0 )
        trans = CASE_A;
     else if ( step[0] > 0.0 )
        trans = CASE_B;
     else if ( step[1] > 0.0 )
        trans = CASE_D;
     else
        trans = CASE_C;
   }
else if ( azim < 180.0 )
   { if ( step[0] > 0.0 && step[1] > 0.0 )
        trans = CASE_B;
     else if ( step[0] > 0.0 )
        trans = CASE_A;
     else if ( step[1] > 0.0 )
        trans = CASE_C;
     else
        trans = CASE_D;
   }
else if ( azim < 270.0 )
   { if ( step[0] > 0.0 && step[1] > 0.0 )
        trans = CASE_C;
     else if ( step[0] > 0.0 )
        trans = CASE_D;
     else if ( step[1] > 0.0 )
        trans = CASE_B;
     else
        trans = CASE_A;
   }
else
   { if ( step[0] > 0.0 && step[1] > 0.0 )
        trans = CASE_D;
     else if ( step[0] > 0.0 )
        trans = CASE_C;
     else if ( step[1] > 0.0 )
        trans = CASE_A;
     else
        trans = CASE_B;
   }

if ( trans != CASE_A )
   { register int nx, ny, nr;
     register int nval = nrx * nry;

     struct Pdata *bgn_data, *bgn_buff, *buff;
/*
 * make a copy
 */
     bgn_buff = buff = (struct Pdata *) osmmget( nval* sizeof(struct Pdata));
     bgn_data = data;
     while ( nval-- > 0 ) 
           { buff->px   = data->px;
             buff++->py = data++->py;
           }
     data = bgn_data;
     buff = bgn_buff;
/*
 * transpose!
 */
     switch ( trans )
        { case CASE_B:
              while ( nry > 0 )
                    { buff = bgn_buff + nrx * (--nry);
                      for ( nx = 0; nx < nrx; nx++ )
                          { data->px   = buff->px;
                            data++->py = buff++->py;
                          }
                    }
              break;
          case CASE_C:
              buff += (nval = nrx * nry) - 1;
              for ( nr = 0; nr < nval; nr++ )
                  { data->px   = buff->px;
                    data++->py = (buff--)->py;
                  }
              break;
          case CASE_D:
              buff += nrx - 1;
              for ( ny = 0; ny < nry; ny++ )
                  { for ( nx = 0; nx < nrx; nx++ )
                        { data->px   = buff->px;
                          data++->py = (buff--)->py;
                        }
                    buff += 2 * nrx;
                  }
              break;
          default:
              break;
        }

     (void) osmmfree( (char *) bgn_buff );
   }
}
   
/*++++++++++++++++++++++++++++++
.IDENTifer   RC_COEF
.PURPOSE     Dedicated routine, calculates TANGENT of a line between two points
.INPUT/OUTPUT
      input    struct Pdata  *point1
               struct Pdata  *point2
.RETURNS    (y2 - y1) / (x2 - x1)
--------------------------------*/
#ifdef __STDC__
      static double RC_COEF( struct Pdata *point1, struct Pdata *point2 )
#else
      static double RC_COEF( point1, point2 )
      struct Pdata *point1, *point2;
#endif

{

if ( point2->px == point1->px )
   { if ( point2->py >= point1->py ) 
        return (1.0e+20);
     else
        return (-1.0e+20);
   }
else
   return (point2->py - point1->py) / (point2->px - point1->px);
}

/*++++++++++++++++++++++++++++++
.IDENTifer   L_STEEPER
.PURPOSE     Dedicated routine; 
.INPUT/OUTPUT
     input    int    dir    direction of the plot (see HIDE_LINE)
              struct Pdata  *point3
  in/output   struct Pdata  segmnt[2]: line segment point1 --> point2

.RETURNS     STEEPER      if a line point1 --> point2 is steeper 
                             then a line from point1 --> point3
             NOT_STEEPER  if this is not the case then point2 = point3
--------------------------------*/
#ifdef __STDC__
      static int L_STEEPER( struct Pdata *point3, struct Pdata segmnt[2] )
#else
      static int L_STEEPER( point3, segmnt )
      struct Pdata *point3, segmnt[2];
#endif
{
if ( RC_COEF( segmnt, point3 ) > RC_COEF( segmnt, segmnt+1 ) )
   { (segmnt+1)->px = point3->px;
     (segmnt+1)->py = point3->py;
     return STEEPER;
   }
else
   return NOT_STEEPER;
}

/*++++++++++++++++++++++++++++++
.IDENTifer   L_CROSS
.PURPOSE     Dedicated routine; calculates an intersection between lines
.INPUT/OUTPUT
      input    int    mx_dim         : dimension of mx_plot
               struct Pdata *mx_plot : actual maxima in the plot
               struct Pdata newln[2] : new line segment to be plotted
     output    struct Pdata cross[2] : coordinates of the intersections

.RETURNS     number of intersections or BELOW, ABOVE, OUTSIDE
--------------------------------*/
#ifdef __STDC__
      static int L_CROSS( int mx_dim, struct Pdata *mx_plot, 
                          struct Pdata newln[2], struct Pdata *cross )
#else
      static int L_CROSS( mx_dim, mx_plot, newln, cross )
      int          mx_dim;
      struct Pdata *mx_plot, newln[2], *cross;
#endif
{
static   int  indx1 = 0;          /* indices of mx_plot->px resp. smaller &  */
register int  indx2;              /* larger than newln->px and (newln+1)->px */
register int  indx, nc = 0;

double  cc_new, cc_mx, rc_new, rc_mx, mx_valy;

/*
 * If the line segment has no overlap with the maxima of the current plot
 *    we return to the calling routine
 */
if ( newln[1].px < mx_plot->px || newln->px >= mx_plot[mx_dim-1].px )
   return OUTSIDE;
else if ( newln[1].px == mx_plot->px && newln[1].py == mx_plot->py )
   { cross[0].px = newln[1].px;
     cross[0].py = newln[1].py;
     return 1;
   }
/*
 * Find last point along mx_plot->px smaller than newln->px
 */
if ( indx1 > mx_dim-1 || newln->px < mx_plot[indx1].px ) indx1 = 0;
while ( indx1 < mx_dim-1 && newln->px > mx_plot[indx1+1].px ) indx1++;

/*
 * Find first point along mx_plot->px larger or equal than newln[1].px
 */
indx2 = indx1 + 1;
while ( indx2 < mx_dim-1 && newln[1].px > mx_plot[indx2].px ) indx2++;

/* 
 * check for each line segment of MX_PLOT between indx1 & indx2 
 * if it intersects with NEWLN
 */
rc_new = RC_COEF( newln, newln+1 );
cc_new = newln->py - rc_new * newln->px;
               
indx = indx1;
do { if ( newln[1].px == mx_plot[indx+1].px 
          && newln[1].py == mx_plot[indx+1].py )
        { cross[nc].px = newln[1].px;
          cross[nc].py = newln[1].py;
          nc++;
        }
     else
        { rc_mx = RC_COEF( mx_plot+indx, mx_plot+indx+1 );
          cc_mx = mx_plot[indx].py - rc_mx * mx_plot[indx].px;
          if ( rc_mx != rc_new )
             { cross[nc].px = (cc_new - cc_mx) / (rc_mx - rc_new);
               if ( cross[nc].px > MYMAX( newln->px, mx_plot[indx].px ) 
                    && cross[nc].px < MYMIN( newln[1].px, mx_plot[indx+1].px ))
                  { cross[nc].py = cc_new + rc_new * cross[nc].px;
                    nc++; 
                  }
             }
        }
   }
while( ++indx < indx2 );

/*
 * Lays NEWLN[1] below MX_PLOT? ( only if no intersection is found )
 */
if ( nc == 0 )
   { nc = ABOVE;
     indx = indx1;
     do { if ( newln[1].px > mx_plot[indx].px && 
               newln[1].px <= mx_plot[indx+1].px )
             { rc_mx = RC_COEF( mx_plot+indx, mx_plot+indx+1 );
               cc_mx = mx_plot[indx].py - rc_mx * mx_plot[indx].px;
               mx_valy = cc_mx + rc_mx * newln[1].px;
               if ( mx_valy > newln[1].py ) nc = BELOW;
             }
        }
     while( nc != BELOW && ++indx < indx2 );
   }
return nc;
}

/*+++++++++++++++++++++++++++++++
.IDENTifer   DRAW_LINE
.PURPOSE     A given set of points are connected with straight lines
             starting from the first point and drawn on the graphics device
.INPUT/OUTPUT
     input    int    dir             :  direction (see HIDE_LINE)
              int    nr              :  dimension of new_line
              struct Pdata *new_line :  data of the line given as (x,y)
              int    cls_line[2]     :  flags to close the surface:
                                        [0] != FALSE take first point of
                                                     old_line
                                        [1] != FALSE index to point in old_line
              struct Pdata *old_line :  data of the previous line
--------------------------------*/
#ifdef __STDC__
      static void DRAW_LINE( int dir, int nr, struct Pdata *new_line,
                             int cls_line[2], struct Pdata *old_line )
#else
      static void DRAW_LINE( dir, nr, new_line, cls_line, old_line )
      int          dir, nr, cls_line[2];
      struct Pdata *new_line, *old_line;
#endif
{ 
register int  ii, np = 0;

float  *xval, *yval;
static char   *err_mem = "*** FATAL: HIDE_LINE: can't allocate enough memory";

xval    = (float *)  osmmget( (nr+2) * sizeof( float ));
if ( xval == NULL ) SCETER( 1, err_mem );
yval    = (float *)  osmmget( (nr+2) * sizeof( float ));
if ( yval == NULL ) SCETER( 2, err_mem );

if ( cls_line[0] )
   { xval[np]   = (float) old_line->px;
     yval[np++] = (float) old_line->py;
   }

for ( ii = 0; ii < nr; ii++ )
    { xval[np]   = (float) (dir * new_line->px);
      yval[np++] = (float) new_line++->py;
    }

if ( cls_line[1] )
   { xval[np]   = (float) old_line[cls_line[1]].px;
     yval[np++] = (float) old_line[cls_line[1]].py;
   }
/*
 * draw the line
 */
AG_GPLL( xval, yval, np );

(void) osmmfree( (char *) xval );
(void) osmmfree( (char *) yval );
}

/*+++++++++++++++++++++++++++++++
.IDENTifer   HIDE_LINE
.PURPOSE     Check which part of a line is hidden and draw what can be seen
             on the graphics device
.INPUT/OUTPUT
     input    int    ln_nr       :   sequence number of the line
              int    ln_mx       :   maximum number of lines to be drawn
              int    ln_dim      :   dimension of line DATA
              struct Pdata *data :   data of the line given as (x,y)

.COMMENT     - the first line to be drawn has to have LN_NR = 1, to initialise
             backup arrays and no part of this line can be hiden by a line
             with a higher LN_NR.
             - Only after a call with LN_NR = LN_MX, LN_DIM can be changed,
             and we have to start again with LN_NR = 1
--------------------------------*/
#ifdef __STDC__
      static void HIDE_LINE( int ln_nr, int ln_mx, 
                              int ln_dim, struct Pdata *data )
#else
      static void HIDE_LINE( ln_nr, ln_mx, ln_dim, data )
      int ln_nr, ln_mx, ln_dim;
      struct Pdata *data;
#endif
{ 
register int  ii, np, nr;
int           ncross, cls_line[2], other_seg, plot_line;
struct Pdata  other_dir[1], segmnt[2], cross[MAXCROSS], *hd_line;

/* 
 * data which have to be kept in memory
 */
static char   *err_mem = "FATAL error PLPER: can't allocate enough memory";
static int    dir, mx_dim;
static struct Pdata *old_data, *mx_plot; 

/* 
 * Initialise number of point plotted and by default no connection 
 * with previous line
 */
ncross = np = 0;
for ( ii = 0; ii < 2; ii++ ) cls_line[ii] = FALSE; 

/*
 * HD_LINE contains the points which will be connected and drawn
 */
hd_line = (struct Pdata *) osmmget( MEMSIZE * sizeof(struct Pdata));
if ( hd_line == NULL ) SCETER( 3, err_mem );

/*
 * LN_NR == 1: allocate memory and collect points for first line
 */
plot_line = TRUE;
if ( ln_nr == 1 )
   { mx_dim = ln_dim;                        /* Dimension of the maxima line */
/*
 * DIRection in which lines are plotted: 1) left to right; -1) right to left
 */
     dir = ( data->px > data[ln_dim-1].px ) ? -1  : 1;
/*
 * old_data is the previous line 
 */
     old_data = (struct Pdata *) osmmget( ln_dim * sizeof(struct Pdata));
     if ( old_data == NULL ) SCETER( 4, err_mem );
/*
 * mx_plot is the actual maximum in the plot
 */
     mx_plot = (struct Pdata *) osmmget( MEMSIZE * sizeof(struct Pdata));
     if ( mx_plot == NULL ) SCETER( 5, err_mem );
/*
 * we can draw the first line: no hidden points!
 */
     while ( np < ln_dim )
           { hd_line[np].px   = dir * data->px;
             hd_line[np++].py = data++->py;
           }
   }
else
   { cls_line[0] = TRUE;                     /* first point of previous line */
     hd_line[np].px   = dir * data->px;           /* first point of new line */
     hd_line[np++].py = data++->py;
                
     for ( nr = 1; nr < ln_dim; nr++, data++ )
         { other_seg = FALSE;
/* 
 * We take the next line segment to be plotted
 */
           for ( ii = 0; ii < 2; ii++ ) 
               { segmnt[ii].px = dir * data[ii-1].px;
                 segmnt[ii].py = data[ii-1].py;
               }
/*
 * We take the connection to the previous line if this one is steeper
 */           
           if ( plot_line )
              { other_dir->px = dir * old_data[nr-1].px;
                other_dir->py = old_data[nr-1].py;
                other_seg = L_STEEPER( other_dir, segmnt );
              }
/*
 * First a special case: we were OUTSIDE and do not know if we are
 * below or above MX_PLOT
 */ 
           if ( ! other_seg && nr > 1 && ncross == OUTSIDE )
              { ncross = L_CROSS( mx_dim, mx_plot, segmnt, cross );
                if ( ncross > 0 ) 
                   plot_line = FALSE;              /* effectively a CROSS_UP */
                else if ( ncross == BELOW )
                   ncross = OUTSIDE;               /* we are below MX_PLOT!! */
              }
           else
              ncross = L_CROSS( mx_dim, mx_plot, segmnt, cross );
/*
 * if we (probably due to round off errors) can not close the surface with an
 * other segment use data...
 */
           if ( other_seg && ncross < 1 ) 
              { other_seg = FALSE;
                segmnt[1].px = dir * data->px;
                segmnt[1].py = data->py;
                ncross = L_CROSS( mx_dim, mx_plot, segmnt, cross );
              }

           if ( other_seg )
              { plot_line = FALSE;
                hd_line[np].px = cross[ncross-1].px;
                hd_line[np].py = cross[ncross-1].py;

                if ( dir * data->px > hd_line[np].px )
                   { np++;                             /* we have to plot np */
                     segmnt[1].px = dir * data->px;
                     segmnt[1].py = data->py;
                     ncross = L_CROSS( mx_dim, mx_plot, segmnt, cross );

                     for ( ii = 0; ii < ncross; ii++ )
                         { if ( cross[ii].px > hd_line[np-1].px )
                              { if ( plot_line )
                                   plot_line = FALSE;
                                else
                                   plot_line = TRUE; 
                                hd_line[np].px   = cross[ii].px;
                                hd_line[np++].py = cross[ii].py;
                              }
                          }
                     if ( plot_line )
                        { hd_line[np].px   = segmnt[1].px;
                          hd_line[np++].py = segmnt[1].py;
                        }
                   }
                else 
                   { register int kk = np, jj = 0;
                     float xval[2], yval[2];
/*
 * Draw this segment on the graphics display
 */
                     xval[0] = (float) dir * segmnt->px;
                     yval[0] = (float) segmnt->py;
                     xval[1] = (float) dir * hd_line[kk].px;
                     yval[1] = (float) hd_line[kk++].py;
                     AG_GPLL( xval, yval, 2 );
/*
 * Update MX_PLOT including the new line segment
 */
                     while ( jj < mx_dim && 
                             mx_plot[jj].px < hd_line[np].px ) jj++;
                     while ( jj < mx_dim )
                           { hd_line[kk].px   = mx_plot[jj].px;
                             hd_line[kk++].py = mx_plot[jj++].py;
                           }
                     mx_dim = kk;
                     while ( kk-- > 0 )
                           { mx_plot[kk].px = hd_line[kk].px;
                             mx_plot[kk].py = hd_line[kk].py;
                           }
                   }
              }
           else if ( ncross == ABOVE || ncross == OUTSIDE )
              { if ( ! plot_line )
                   { plot_line = TRUE;
                     hd_line[np].px   = segmnt[0].px;
                     hd_line[np++].py = segmnt[0].py;
                   }
                hd_line[np].px   = segmnt[1].px;
                hd_line[np++].py = segmnt[1].py;
              }
           else
              { for ( ii = 0; ii < ncross; ii++ )   /* For each intersection */
                    { if ( plot_line )                         /* CROSS_DOWN */
                         { plot_line = FALSE;
                           hd_line[np].px   = cross[ii].px;
                           hd_line[np++].py = cross[ii].py;
                         }
                      else                                       /* CROSS_UP */
                         { register int jj = 0;

                           plot_line = TRUE; 
                           while ( jj < mx_dim &&
                                   mx_plot[jj].px <= hd_line[np-1].px ) jj++;
                           while ( jj < mx_dim 
                                   && mx_plot[jj].px < cross[ii].px )
                                 { hd_line[np].px   = mx_plot[jj].px;
                                   hd_line[np++].py = mx_plot[jj++].py;
                                 }
                           hd_line[np].px   = cross[ii].px;
                           hd_line[np++].py = cross[ii].py;
                         }
                    }
                if ( plot_line )
                   { hd_line[np].px   = segmnt[1].px;
                     hd_line[np++].py = segmnt[1].py;
                   }
              }
         }
/*
 * close the surface with last point of current line and previous line
 */
     segmnt[0].px = dir * (data-1)->px;
     segmnt[0].py = (data-1)->py;
     segmnt[1].px = dir * old_data[ln_dim-1].px;
     segmnt[1].py = old_data[ln_dim-1].py;
     ncross = L_CROSS( mx_dim, mx_plot, segmnt, cross );
     if ( ncross == ABOVE )
        cls_line[1] = ln_dim - 1;
     else
        { for ( ii = 0; ii < ncross; ii++ )         /* For each intersection */
              { if ( plot_line )                               /* CROSS_DOWN */
                   { hd_line[np].px   = cross[ii].px;
                     hd_line[np++].py = cross[ii].py;
                     plot_line = FALSE;
                   }
                else                                             /* CROSS_UP */
                   { register int jj = 0;
                     while ( jj < mx_dim &&
                             mx_plot[jj].px <= hd_line[np-1].px ) jj++;
                     while ( jj < mx_dim 
                             && mx_plot[jj].px < cross[ii].px )
                           { hd_line[np].px   = mx_plot[jj].px;
                             hd_line[np++].py = mx_plot[jj++].py;
                           }
                     hd_line[np].px   = cross[ii].px;
                     hd_line[np++].py = cross[ii].py;
                     plot_line = TRUE; 
                   }
              }
          if ( plot_line ) cls_line[1] = ln_dim - 1;
        }
   }
/*
 * draw the line
 */
DRAW_LINE( dir, np, hd_line, cls_line, old_data );

if ( ln_nr == ln_mx )               /* release allocated memory at last call */
   { (void) osmmfree( (char *) old_data );
     (void) osmmfree( (char *) mx_plot );
   }
else                                      /* store data of last plotted line */
   { data -= ln_dim;
     for ( nr = 0; nr < ln_dim; nr++ ) 
         { old_data[nr].px = data->px;
           old_data[nr].py = data++->py;
         }

     if ( ln_nr != 1 && ! plot_line )
        { ii = 0;
          while ( ii < mx_dim && mx_plot[ii].px <= hd_line[np-1].px ) ii++;
          while ( ii < mx_dim )
                { hd_line[np].px   =  mx_plot[ii].px;
                  hd_line[np++].py =  mx_plot[ii++].py;
                }
        }

     for ( ii = 0; ii < np; ii++ )
         { mx_plot[ii].px = hd_line[ii].px;
           mx_plot[ii].py = hd_line[ii].py;
         }
     mx_dim = np;
   }
(void) osmmfree( (char *) hd_line );
}

/*++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENTifer   PLPER
.PURPOSE     plot a perspective plot 
.INPUT/OUTPUT
  call as    PLPER( wcfram, p_img, image, start, step, angle, scales, xyflag )

  input:
         float wcfram[12]	frame limits & ticks of (x,y,z)
                                  (amin, amax, abig & asmall)
         float  *p_img		pointer to the data of the frame
         float  image[4]	image size in pixel coordinates
         double start[2]	start values of the X and Y axes
         double step[2]		distance between pixels in world units
         float  angle[2]	viewing angles altitude and azimuth
         float  scales[2]	scale: scaling of the Z component,
                                  (= 1: screen filling)
                                offset: offset of plot in units of the Z-axis
         int    xyflag[2]	if xyflag[0] = 1 plot lines along first axis
                                if xyflag[1] = 1 plot lines along second axis
.COMMENTS    PLPER has a one serious restriction:
              - features unter the surface are not shown!
----------------------------------------------------*/
void PLPER( wcfram, p_img, image, start, step, angle, scales, xyflag )
int    *xyflag;
float  *image, *wcfram, *p_img, *angle, *scales;
double *start, *step;

{
register int nx, ny;
int     nrxval, nryval, nval;
double  xpos, ypos;
struct  Odata *data;
struct  Pdata *proj, *bgn_proj, *line;

nrxval = (int) fabs( image[1] - image[0] ) + 1;
nryval = (int) fabs( image[3] - image[2] ) + 1;
nval = nrxval * nryval;
data   = (struct Odata *) osmmget( nval * sizeof( struct Odata));

/*
 * store the three-dimensional data in a array
 */
ypos = start[1] + (image[2] - 1) * step[1];
for ( ny = 0; ny < nryval; ny++ )
    { xpos = start[0] + (image[0] - 1) * step[0];
      for ( nx = 0; nx < nrxval; nx++ )
          { data->xx   = xpos;
            data->yy   = ypos;
            data++->zz = *p_img++;
            xpos += *step;
          }
      ypos += *(step+1);
    }
data -= nval;
/*
 * Project the data on a two dimensional plane 
 */
proj = (struct Pdata *) osmmget( nval * sizeof( struct Pdata));
GETPROJ( wcfram, angle, scales, nval, data, proj );
(void) osmmfree((char *) data );

/*
 * store the data in the right order w.r.t. the users viewpoint 
 */
TRANSPOSE( angle[1], step, nrxval, nryval, proj );

/*
 * if lines in the plot have to be parallel to the first axis of the data...
 */
bgn_proj = proj;
if ( xyflag[0] == 1 )
   { for ( ny = 0; ny < nryval; ny++ )
         { HIDE_LINE( ny+1, nryval, nrxval, proj );
           proj += nrxval;
         }
   }
/*
 * if lines in the plot have to be parallel to the first axis of the data...
 */
if ( xyflag[1] == 1 )
   { line = (struct Pdata *) osmmget( nryval * sizeof( struct Pdata ));
     for ( nx = 0; nx < nrxval; nx++ )
         { proj = bgn_proj + nx;
           for ( ny = 0; ny < nryval; ny++ )
               { line[ny].px = proj->px;
                 line[ny].py = proj->py;
                 proj += nrxval;
               }
           HIDE_LINE( nx+1, nrxval, nryval, line );
         }
     (void) osmmfree( (char *) line );
   }

(void) osmmfree( (char *) bgn_proj );
} 


/*++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENTifer   PLPERI
.PURPOSE     produce plot information for a three-dimensional perspective plot

.INPUT/OUTPUT
  call as    PLPERI( plmode, name, ident, image, angle, wcfram )

  input:
         int   plmode		plot mode, see PMODE in PLISTAT
         char  *name		data frame name
         char  *ident		ascii identifier of image
         float *image		size in PIXEL coordinates
         float *angles		viewing angles altitude and azimuth
         float *wcfram		frame values of resp. X, Y and Z axis 
                                    (amin, amax, abig & asmall)
.COMMENTS     none
----------------------------------------------------*/
void PLPERI( plmode, name, ident, image, angle, wcfram )
char  *name, *ident; 
int   plmode;
float *angle, *image, *wcfram;

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

/*
 * check if PMODE == 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
 */
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 );

/*
 * minimum and maximum
 */
yt -= 2*yh;
(void) sprintf( buff, "Min: %-.3g ", wcfram[FOR_Z] );
AG_GTXT( xt, yt, buff, 1 );
yt -= yh;
(void) sprintf( buff, "Max: %-.3g ", wcfram[FOR_Z+1] );
AG_GTXT( xt, yt, buff, 1 );

/*
 * view angles
 */
yt -= 2*yh;
AG_GTXT( xt, yt, "View angles:", 1 );
yt -= yh;
(void) sprintf( buff, "Altitude: %-.3g ", angle[0] );
AG_GTXT( xt, yt, buff, 1 );
yt -= yh;
(void) sprintf( buff, "Azimuth : %-.3g ", angle[1] );
AG_GTXT( xt, 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();
   }
}
