/* program PICK to pick line out of an image

to run, type:

pick [-x<file size> -l<scale factor> -d( no dither ) -s<line size>
                                                    <input file> <output file>] 

or just pick and use the menu */

#include <graph.h>
#include <stdio.h>
#include "az:plane.h"
#include "az:general.h"

#define X_SIDE  110  /* left side of disp. square */
#define Y_BASE  100  /* base for display square */
#define MIN_SIDE 5   /* minimun side for square */
#define X0  430      /* x for slice origin */
#define Y0  100      /* y for slice origin */
#define M   255      /* vectors run from 0 to M */
#define NO_DATA -1000.0    /* to indicate no data */

char dith_flag;      /* true if dither required */
char menu_flag;      /* true if main menu active */
char move_flag;      /* true if text line cleared when cursor moved */
char larg_flag;      /* true if expanded display required */
char out_flag;       /* true if output file given */
char new_flag;       /* true if new line required */
char side_flag;      /* true if fixed line length required */
 
unsigned short fontbuf[FONTSIZE];	/* buffer for largest expected font */
char *fontname = "fmacs:font.visual";
int l, h, w;

int xl_menu;              /* left x for menu region */
int xr_menu;              /* right x for menu region */
int y_menu;               /* top y for menu region */
int nop;                  /* no. of options for menu */

FILE *fp = NULL;
FILE *fq = NULL;

int size_file = 0;       /* size of file */
int size = 0;            /* size of image */
int scale_size = 1;      /* enlargement factor for displaying image, reduction
                            factor when writing out image */
int grey;                /* current grey level for text & information regions */
int col;                 /* current colour - red or cyam */
char store_filename[ 40 ];   /* stores output filename if any */

char int_array[ 256 * 256 ];  /* intensity array */
float vec[ M + 1 ], out_vec[ M + 1 ];

extern char read_file(), write_file();

void region(), pick(), limit_image(), draw_line(), clear_line();
void write_size(), clear_size();
int max();
char near(), in_image();
char sel_side();
void shift(), copy_line();
void menu(), draw_menu(), cl_menu();
void form_vec(), plot_vector();

main(argc,argv)
char **argv;
int argc;
{
   char header_flag = TRUE; /* true if file contains header */
   char arg_flag = FALSE;    /* true if command line arguments given */
   char i_flag = FALSE;      /* true if intensity file given */
   char c, *p, *p1;
   int line_size;


   dith_flag = TRUE;         /* FLAG DEFAULTS */
   out_flag = FALSE;
   larg_flag = FALSE;
   new_flag = TRUE;
   side_flag = FALSE;

   if ( argc > 1 ) { /* command line arguments given */
      argv++;
      arg_flag = TRUE;

      while( ( argc > 0 ) && ( (*argv)[0] == '-' ) )
      {
         switch( (*argv)[1] ) {

            case 'x':
               sscanf( *argv + 2, "%d", &size_file);
               header_flag = FALSE;
               break;

            case 'd':      /* no dither */
               dith_flag = FALSE;
               break;

            case 'l': 
               larg_flag = TRUE;
               sscanf( *argv+2, "%d", &scale_size );
               break;

            case 's': 
               side_flag = TRUE;
               sscanf( *argv+2, "%d", &line_size );
               break;
            default:
               error( "bad option");
               break;
         } 
  
         argc--;
         argv++;
      }               
   }       

   if ( --argc> 0 ) /* open input file if any */
   {
      if ( ( fp = fopen( *(argv++), "r" ) ) == NULL )
      {
         putchar( BEEP );
         error( "can't open picture file" );
      }
      else
         i_flag = TRUE;
   }

   if ( --argc > 0 ) /* open output file if any */
   {
      if ( ( fq = fopen( *(argv++), "w" ) ) == NULL )
      {
         putchar( BEEP );
         error("can't open output file");
      }
      else
      {
         out_flag = TRUE;
         p = *(argv);
         p1 = store_filename;
         while ( *p1++ = *p++ ); /* copy string */
      }
   }

   l = readfont(fontname, fontbuf, FONTSIZE, &h, &w);

   grey = P_WHITE;
   col = P_RED;

   offset(0,0);
   sterm( 5 );

   setup_map();
   clear_screen();
   setup_text();
   setup_menu();
   draw_axes( X0, Y0 );

   /* default -  menu on */

                          /*   DEFAULT VALUES  */
   menu_flag = TRUE;  
   draw_menu();

   if ( i_flag ) {        /* display image */
      if ( header_flag )
         size_file = getc( fp ) & 0377;
         
      if ( size_check() )
         display( X_SIDE, Y_BASE, int_array, TRUE );
      else
         exit( 0 );
   }

   if ( arg_flag ) {   /* to avoid error with command line arg  */
      write_text( "press return to continue " ); /* if command file not used */
      if ( ( c = getchar() ) == EOF )
         clearerr( stdin );  /* clear buffer */
      clear_text();
   }

   if ( side_flag ) /* write size */
   {
      enable( col );
      colour( P_COL );
      write_size( line_size );
   }

   region( line_size );
}



void region( line_size ) /*  middle Mouse Button ( MB ) selects option */
int line_size;
{
   int option;
   int x = 50;  /* initial position for cursor */
   int y = 360;
   char exit_region_flag = FALSE;

   int x_ll, y_ll, side;  /* lower left coordinates & length of line */

   if ( side_flag ) /* side given in command line */
      side = line_size - 1;

   while ( !exit_region_flag )  { 

      enable( P_GREEN );
      cursor( 0, &x, &y);
      if ( (mouse->b & 0377) == 2 ) {

         if ( in_image( x, y ) )      /* select line */
         {
            pick( 2, &x, &y, &x_ll, &y_ll, &side );

         }
         else if ( ( menu_flag ) && ( option = in_box( x, y) ) ) {
            
            menu( option, &x, &y, &x_ll, &y_ll, &side );       

            /* draw cursor - while button held */

            enable( P_GREEN );
            draw_cross( x, y); 

         }     
      } 
      while ( mouse->b == 2 ) /* pause until button released */
         ;
   } /* end of while */  
}  /* end of function */

void pick( c, px, py, px_ll, py_ll, pside ) /* while c = mouse>b
                                moves cursor & returns current position (px,py) 
                                & returns line */
int c, *px, *py, *px_ll, *py_ll, *pside;
{
   static int delta = 3;

   int old_x, old_y, x_offset, y_offset;
   int x_max, y_max, new_side;

   x_max = X_SIDE + size; /* upper limit for display image */
   y_max = Y_BASE + size;

   old_x = *px;   /* stores current position */
   old_y = *py;

   /* calculate offset between mouse position and current position */
   x_offset = SCALE( mouse->x ) - old_x;
   y_offset = SCALE( mouse->y ) - old_y;

   while ( (mouse->b & 0377) == c ) {

      *px = SCALE( mouse->x ) - x_offset;        /* new position */
      *py = SCALE( mouse->y ) - y_offset; 
      limit_image( px, py);               /* restricts cursor to image */

      enable( P_GREEN );
      draw_cross( *px, *py);              /* draw cursor at new position */

      if ( new_flag )
      {
         if ( !side_flag ) /* size not fixed */
            *pside = MIN_SIDE;

         if ( ( *px <= (x_max - 1 - *pside) ) )
         {
            /* start new line */
 
            *px_ll = *px;    /* lower left hand corner for line */
            *py_ll = *py;
            draw_line( *px_ll, *py_ll, *pside );
            write_size( *pside + 1 );
            new_flag = FALSE;
            delta = max( *pside >> 2, 3 );
         }
         else
         {
            putchar( BEEP );
            write_text("cursor too close to image boundary" );
            move_flag = TRUE;
         }
      }
      else 
      {
         if ( near( *px, *py, *px_ll, *py_ll, delta ) &&
              ( (*px + *pside) < x_max ) )
         {
            /* move line - not over boundary */

            clear_line( *px_ll, *py_ll, *pside );
            *px_ll = *px;
            *py_ll = *py;
            draw_line( *px_ll, *py_ll, *pside );
         }
         else if ( !side_flag && 
                   near( *px, *py, *px_ll + *pside, *py_ll, delta ) )
         {
            /* change side - not growing over boundary */
 
            new_side = fabs( *px - *px_ll );

            /* to prevent line shrinking to nothing */

            new_side = max( new_side, MIN_SIDE );

            if ( ( (*px_ll + new_side) < x_max ) && 
                 ( *pside != new_side ) )
            {
               clear_line( *px_ll, *py_ll, *pside );
               *pside = new_side;
               draw_line( *px_ll, *py_ll, *pside );
               write_size( *pside + 1 );
               delta = max( *pside >> 2, 3 );
            }
         }
         else 
            draw_line( *px_ll, *py_ll, *pside );
      } 
          

      if ( (*px != old_x) || (*py != old_y) ) { /* erase old cursor position */


         if ( move_flag ) { /* clear text */
            clear_text();
            move_flag = FALSE;
         }

         enable( P_GREEN );
         clear_cross( old_x, old_y);
         old_x = *px;
         old_y = *py;
      }
   }
}

void limit_image( px, py) /* restricts px & py to image */
int *px, *py;
{
   if ( *px < X_SIDE ) *px = X_SIDE;
   if ( *px > X_SIDE + size - 1 ) *px = X_SIDE + size - 1;
   if ( *py < Y_BASE ) *py = Y_BASE;
   if ( *py > Y_BASE + size - 1 ) *py = Y_BASE + size - 1;
}

char near( x, y, x1, y1, delta ) /* returns TRUE if (x,y) nearer than delta 
                                    but not coincident with (x1,y1)
                                    else returns FALSE */
int x, y, x1, y1, delta;
{
   int dx, dy;

   dx = abs( x - x1 );
   dy = abs( y - y1 );

   if ( ( dx <= delta ) && ( dy <= delta ) && 
        ( ( dx > 0 ) || ( dy > 0 ) ) )
      return( TRUE );
   else
      return( FALSE );
}

int max( x, y ) /* returns maximun of x & y */
int x, y;
{
   if ( x >= y )
      return( x );
   else
      return( y );
}

void draw_line( x1, y1, side ) /* draws line with lower left hand corner
                                    (x1, y1) & side size */
int x1, y1, side;
{
   enable( col );
   colour( P_COL );
   hline( x1, x1 + side, y1);
}

void clear_line( x1, y1, side ) /* clears line with lower left hand corner
                                    (x1, y1) & side size */
int x1, y1, side;
{
   enable( col );
   colour( BLACK );
   hline( x1, x1 + side, y1);
}


char in_image( x, y) /* returns TRUE if (x,y) inside image
                                                        else returns FALSE */ 
int x, y;
{

   /* check if mouse inside image */

   if ( ( x >= X_SIDE ) && ( x < ( X_SIDE + size ) ) && 
        ( y >= Y_BASE ) && ( y < Y_BASE + size ) ) {
      return( TRUE );
   }
   else
      return( FALSE );
}

void write_size( size ) /* writes size - should only be invoked when 
                                         size changed */
int size;
{
   static char size_text[ 20 ] = ""; /* space to store text */
   static char *text;
   static int x_text;
   static int y_text;

   x_text = xl_menu + 26;  /* 15 + w */
   y_text = y_menu - 420;  /* y_menu - nop * 50 - 70 */

   /* erase old text */

   colour( BLACK );

   text = size_text;

   setpos( x_text, y_text );
   while ( *text ) gputc( *text++ );

   /* write new text */

   if ( size < 10 ) 
      sprintf( size_text, "  %d", size );
   else if ( size < 100 )
      sprintf( size_text, " %d", size );
   else
      sprintf( size_text, "%d", size );

   colour( P_COL );

   text = size_text;

   setpos( x_text, y_text );
   while ( *text ) gputc( *text++ );
}

void clear_size() /* clears size */
{
   static int x_text;
   static int y_text;

   x_text = xl_menu + 26;  /* 15 + w */
   y_text = y_menu - 420;  /* y_menu - nop * 50 - 70 */

   /* erase size */

   enable( P_COL );
   colour( BLACK );

   fill( x_text, y_text, x_text + 3 * w, y_text + h );
}

/* ************************** MENU FUNCTIONS ******************************* */

void menu( option, px, py, px_ll, py_ll, pside ) /*  selects option */
int option, *px, *py, *px_ll, *py_ll, *pside;
{
static char answer[ 20 ];
char statement[ 40 ];

   switch ( option ) {

      case 1 :                      /* read intensity file */
         if ( read_file() ) {
            display( X_SIDE, Y_BASE, int_array, TRUE );
         }
         move_flag = TRUE;
         break;

      case 2 :                      /* write out to file */
         if ( out_flag )
         {
            sprintf( statement, "writing to file %s", store_filename );
            write_text( statement );
            copy_line( *px_ll, *py_ll, *pside, vec );
            form_vec( vec, *pside, out_vec );
            write_vec( out_vec );
            out_flag = FALSE;
         }
         if ( open_write() )
         {
            copy_line( *px_ll, *py_ll, *pside, vec );
            form_vec( vec, *pside, out_vec );
            write_vec( out_vec );
         }
         move_flag = TRUE;
         break;

      case 3 :                      /* new box */
         if ( sel_side( pside ) )
            new_flag = TRUE;
         move_flag = TRUE;
         break;
      
      case 4 :                      /* clear */  
         cl_menu( px, py, px_ll, py_ll, pside );
         clear_menu();
         clear_text();
         nop = 7;
         lab_menu( " read", "write", " new", "clear", " draw" ,"dither", " quit" );
         break;

      case 5 :                      /* draw */
         copy_line( *px_ll, *py_ll, *pside, vec );
         plot_vector( col, vec, X0, Y0, *pside );
         break;

      case 6 :                      /* dither on/off */
         dith_flag = !dith_flag;
         if ( dith_flag )
            write_text( "dither on" );
         else
            write_text( "dither off" );
         move_flag = TRUE;
         break;

      case 7 :                      /* quit */
         clear_screen();
         exit( 0 );                     
         break;

      default:
         break;
   }

} 


void draw_menu() /* draws menu */
{
   char *text;
   int x_text;

   nop = 7;
   x_text = xl_menu + 15;

   /* draw and label menu region */

   enable( P_GREY );
   colour( grey );

   line( xr_menu + 10, 5, xr_menu + 10, y_menu );

   /* label menu */

   lab_menu( " read", "write", " new", "clear", " draw" ,"dither", " quit" );

   /* write size label */

   text = " size";
   setpos( x_text, y_menu - nop * 50 - 50 );
   while ( *text ) gputc( *text++ );

   /* draw line for text region */

   hline( 0, XPIXELS, YPIXELS - h - 3 );
}      

void cl_menu( px, py, px_ll, py_ll, pside ) /* CLEAR */
int *px, *py, *px_ll, *py_ll, *pside;
{
   int option;
   static char title[ 20 ] = "CLEAR";
   char exit_menu;

   clear_menu();
   nop = 4;
   lab_menu( "line", "red", "cyam", "return" );

   exit_menu = FALSE;

   while ( mouse->b == 2 ) /* pause until button released */
      ;

   while ( !exit_menu )
   { 

      write_text( title );

      enable( P_GREEN );
      cursor( 0, px, py);

      if ( ( (mouse->b & 0377) == 2 ) && ( option = in_box( *px, *py) ) )
      {

         switch ( option )
         {
            case 1 :                            /* line */
               clear_line( *px_ll, *py_ll, *pside );
               clear_size();
               new_flag = TRUE;
               break;

            case 2 :                            /* red */
               colour( BLACK );
               enable( P_RED );
               fill( X0, Y0, X0 + M, Y0 + M );
               break;

            case 3 :                            /* cyam */
               colour( BLACK );
               enable( P_CYAM );
               fill( X0, Y0, X0 + M, Y0 + M );
               break;

            case 4 :                            /* return */
               exit_menu = TRUE;
               break;

            default:
               break;
         } 
      } /* end of if */

      while ( mouse->b == 2 ) /* pause until button released */
         ;

   } /* end of while */  
}    /* end of cl_menu */

/***************************** GENERAL FUNCTIONS ***************************/

void copy_line( x_ll, y_ll, side, pvec ) /* copies contents of line into vector */
int x_ll, y_ll, side;
float *pvec;
{
   int x_image, y_image, new_size, intens;
   register int x, y1;    
   register char *p;

   shift( x_ll, y_ll, &x_image, &y_image ); /* convert to image origin */

   /* copy line */

   y1 = size - 1 - y_image;          /* since picture starts from the top */

   p = int_array + size * y1;  /* start of display line */

   for ( x = x_image; x < x_image + side + 1; x++ )
   {
      intens = *( p + x ) & 0377;
      *pvec++ = (float)intens;
   }
}


void shift( x, y, px_image, py_image) /* changes from screen origin to display
                                    image origin */
int x, y;
int *px_image, *py_image;
{
   *px_image = x - X_SIDE;
   *py_image = y - Y_BASE;
}

char sel_side( pside ) /* selects side length for line */
int *pside;
{
   int line_size;
   char answer[ 20 ];

   write_text( "fixed line size ? (y/n) : " );
   read_response( answer );
   if ( ( answer[ 0 ] == 'y' ) || ( answer[ 0 ] == 'Y' ) )
   {
      write_text("size : ");
      read_response( answer );
      sscanf( answer, "%d", &line_size );
      if ( line_size > size )
      {
         putchar( BEEP );
         write_text( "size too large" );
         return( FALSE );
      }
      else if ( line_size < (MIN_SIDE + 1) )
      {
         putchar( BEEP );
         write_text( "size too small" );
         return( FALSE );
      }
      else
      {
         *pside = line_size - 1;
         side_flag = TRUE;
         clear_size();

         if ( col == P_RED ) /* change colour */
            col = P_CYAM;
         else
            col = P_RED;

         enable( col );
         colour( P_COL );
         write_size( line_size );
         write_text( "select line" );
         return( TRUE );
      }
   }  
   else if ( ( answer[ 0 ] == 'n' ) || ( answer[ 0 ] == 'N' ) ) 
   {
      side_flag = FALSE;
      clear_size();
      write_text( "select line" );

      if ( col == P_RED ) /* change colour */
         col = P_CYAM;
      else
         col = P_RED;

      return( TRUE );
   }
   else
   {
      putchar( BEEP );
      return( FALSE );
   }
}                  

void form_vec( vec, m, out_vec ) /* formats vector for output - expands vector
                                    to as near 256 as possible, fills 
                                    remaining points with NO_DATA */
float *vec, *out_vec;
int m;
{
   register int x, i;
   register float g;
   int scale_size, remainder;

   /* determine expansion factor */

   scale_size = ( M + 1 ) / ( m + 1 );
   remainder = M + 1 - ( m + 1 ) * scale_size;

   /* expand by scale size & write to output vector */

   for ( x = 0; x < m + 1; x++ )
   {
      g = *vec++;
      for ( i = 0; i < scale_size; i++ )
         *out_vec++ = g;
   }

   /* fill in remainder */

   for ( i = 0; i < remainder; i++ )
      *out_vec++ = NO_DATA;
}


void plot_vector( col, p, x0, y0, m ) /* draws vector ( length = m + 1 )
                                         colour col as y values on screen */
int col;
float *p;
int x0, y0, m;
{
   int x, y;

   enable( col );
   colour( P_COL );

   for ( x = 0; x < m + 1; x++ ) 
   {
      if ( *p != NO_DATA )
      {
         y = *p++ + 0.5;
         plot( x + x0 , y + y0 );
      }
      else
      {
         p++;
      }
   }
}
   
