/*  File drawmap.c  */

#include "intuition/intuition.h"
#include "graphics/gfxmacros.h"
#include "devices/printer.h"
#include "exec/io.h"
#include "exec/memory.h"
#include "exec/libraries.h"
#include "libraries/dos.h";
#include "libraries/dosextens.h"
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <reqbase.h>
#include <ILBM_lib.h>
#include <drawmap.h>
#include <drawmap-req.h>
#include <drawmap-menu.h>
#include <drawmap-help.h>

short *map, *map_trig;                 /* workspaces for map  */

struct Screen *s;                      /* pointer to screen   */
struct Window *w;                      /* pointer to Window   */
struct RastPort *rp;                   /* pointer to RastPort */
struct ViewPort *vp;                   /* pointer to ViewPort */
struct AreaInfo mapAreaInfo;
struct TmpRas mapTmpRas;
struct Library *GfxBase;
struct Library *IntuitionBase;

struct ReqLib *ReqBase;                /* address of requester library */
struct ExtendedColorRequester colorstruct;
struct ReqFileRequester filereq;
char filename[FCHARS];                 /* space for save file and */
char directoryname[DSIZE];             /*   dir name              */
char pathname[DSIZE+FCHARS];           /* full path and file name */

struct ILBMBase *ILBMBase;             /* address of ILBM library */
ILBMFrame IlbmFrame;

short areaArray[5*NUMPTS];             /* 5 words per point for drawing */

unsigned short *arrow, *cross;         /* storage for mouse pointers */
unsigned short *waiter, *transparent;

unsigned char *bp[DEPTH];              /* bitplane pointers */

struct Requester req;

struct BitMap map_bitmap;              /* holding area for initial flat map */
struct RastPort map_rp;                /* rastport for initial flat map */ 
char got_flat_map;

static long title_toggle = FALSE;

/* ============================================================= */

main ()

{
   extern void init_requesters(), PurgeFiles(), loadmappic();
   extern int handle_event(), init_helpitems();
   extern int readmap(), get_min_max();
   struct IntuiMessage *msg;
   PLANEPTR workspace;
   int ix, config;

   if ((GfxBase = (struct Library *) OpenLibrary ("graphics.library", 0L))
        == NULL)  {
      printf ("Can't open graphics library\n");
      exit (10);
   }
   if ((IntuitionBase=(struct Library *) OpenLibrary ("intuition.library",0L))
        == NULL)  {
      printf ("Can't open intuition library\n");
      goto end1;
   }
   if ((ReqBase = (struct ReqLib *) OpenLibrary ("req.library", 0L))
        == NULL)  {
      printf ("Can't open req.library\n");
      goto end2;
   }
   if ((ILBMBase = (struct ILBMBase *) OpenLibrary ("ilbm.library", 0L))
        == NULL)  {
      printf ("Can't open ilbm.library\n");
      goto end3;
   }
   if ((s = (struct Screen *) OpenScreen (&mapscreen)) == NULL)  {
      printf ("Can't open screen\n");
      goto end4;
   }
   mapWindow.Screen = s;
   newhelpw.Screen = s;
   if ((w = (struct Window *) OpenWindow (&mapWindow)) == NULL)  {
      printf ("Can't open window\n");
      goto end5;
   }
                                       /* load default color table */
   CopyMem ((char *) mapcolors, (char *) configcolors,
            sizeof(mapcolors));
   if ((config = open (configfile, O_RDONLY)) != -1)  {
      if ((ix = read (config, (char *) &configcolors[0],
                      sizeof(configcolors))) != sizeof(configcolors))  {
         CopyMem ((char *) mapcolors, (char *) configcolors,
                  sizeof(mapcolors));
      }
      close (config);
   }
   vp = &(s->ViewPort);                /* pointer to viewport */
   LoadRGB4 (vp, &configcolors[0], NUM_COLORS); /* init. color values  */
                                       /* init. mouse pointers */
   arrow = (UWORD *) AllocMem (ARROW_SIZE, MEMF_CHIP);
   CopyMem ((char *) arrow_data, (char *) arrow, ARROW_SIZE);

   cross = (UWORD *) AllocMem (CROSS_SIZE, MEMF_CHIP);
   CopyMem ((char *) cross_data, (char *) cross, CROSS_SIZE);
   
   waiter = (UWORD *) AllocMem (WAITER_SIZE, MEMF_CHIP);
   CopyMem ((char *) waiter_data, (char *) waiter, WAITER_SIZE);
   
   transparent = (UWORD *) AllocMem (TRANSPARENT_SIZE, MEMF_CHIP);
   CopyMem ((char *) transparent_data, (char *) transparent,
            TRANSPARENT_SIZE);

   SetPointer (w, waiter, WAITER_SIZE/4-2, 16, WAITER_X_OFFSET,
               WAITER_Y_OFFSET);

   rp = w->RPort;
   if ((workspace = (PLANEPTR) AllocRaster (WWIDTH,WHEIGHT)) == NULL)  {
      printf ("No space for Temporary Raster\n");
      goto end6;
   }

   InitBitMap (&map_bitmap, DEPTH, WWIDTH, WHEIGHT);
   InitRastPort (&map_rp);
   map_rp.BitMap = &map_bitmap;

   for (ix=0; ix<DEPTH; ++ix)          /* initialize flat map storage area */
      map_bitmap.Planes[ix] = NULL;
   for (ix=0; ix<DEPTH; ++ix)  {
      map_bitmap.Planes[ix] = (PLANEPTR) AllocRaster (WWIDTH, WHEIGHT);
      if (map_bitmap.Planes[ix] == NULL)  {
         printf ("No space for bitmap workspace\n");
         goto end7;
      }
   }

   InitTmpRas (&mapTmpRas, workspace, RASSIZE(WWIDTH,WHEIGHT));
   rp->TmpRas = &mapTmpRas;            /* link it to the RastPort */
   InitArea (&mapAreaInfo, &areaArray[0], NUMPTS);
   rp->AreaInfo = &mapAreaInfo;        /* link it to the RastPort */

   if ((map = (short *) calloc (1, (unsigned) (MAXVAL*sizeof(short))))
        == NULL)  {
      printf ("Can't get space for map\n");
      goto end7;
   }
   if ((ix = readmap (map, mapname, MAXVAL)) != OK)  { /* read map file */
      printf ("Error reading map file\n");
      goto end8;
   }
   if ((map_trig = (short *) calloc (1, (unsigned) (MAXVAL*sizeof(short))))
        == NULL)  {
      printf ("Can't get space for map_trig\n");
      goto end8;
   }                                   /* read map_trig file */
   if ((ix = readmap (map_trig, mapname_trig, MAXVAL)) != OK)  {
      printf ("Error reading map-trig file\n");
      goto end9;
   }

   if ((ix = get_min_max (map)) != OK)  {
      printf ("Map file is corrupted\n");
      goto end9;
   }

   if ((ix = init_helpitems ()) != OK)
      printf ("\nUnable to initialize help information, but continuing\n");

   loadmappic();                       /* load initial flat map */

   for (ix=0; ix<DEPTH; ++ix)          /* initialize bitplane pointers */
      bp[ix] = rp->BitMap->Planes[ix];

   init_requesters ();                 /* initialize the requesters */

   SetMenuStrip (w, &menu[0]);         /* bring up the menus */

   SetAPen (rp, ORANGE);               /* set initial drawing pens */
   SetBPen (rp, BLUE);
   SetDrMd (rp, JAM2);

   view_height = VIEW_HEIGHT;          /* constants for globe view */
   eta = view_height/RE;
   facp = 1. + eta;
   etap = 1./facp;

   SetPointer (w, arrow, ARROW_SIZE/4-2, 16, ARROW_X_OFFSET, ARROW_Y_OFFSET);

   while (1)  {                        /* wait for message from */
      WaitPort ( w->UserPort );        /*   Intuition           */
      if ((msg = (struct IntuiMessage *) GetMsg (w->UserPort)) == NULL)
         continue;
      else if ((ix = handle_event (msg)) != OK)
         break;
      else
         ReplyMsg (msg);
   }
   ReplyMsg (msg);
   if (helpbuffer != NULL)
      free (helpbuffer);
   PurgeFiles (&filereq);              /* clean up after file requester */
end9:
   if (seg != NULL)
      free ((char *) seg);
   free ((char *) map_trig);
end8:
   free ((char *) map);
end7:
   for (ix=0; ix<DEPTH; ++ix)
      if (map_bitmap.Planes[ix] != NULL)
         FreeRaster (map_bitmap.Planes[ix], WWIDTH, WHEIGHT);
   FreeRaster (workspace, WWIDTH, WHEIGHT);
end6:
   FreeMem (transparent, TRANSPARENT_SIZE);
   FreeMem (waiter, WAITER_SIZE);
   FreeMem (cross, CROSS_SIZE);
   FreeMem (arrow, ARROW_SIZE);
   ClearPointer (w);
   ClearMenuStrip (w);
   Forbid ();                          /* strip remaining messages */
   while (1)  {
      if ((msg = (struct IntuiMessage *) GetMsg (w->UserPort)) == NULL)
         break;
      else
         ReplyMsg (msg);
   }
   ModifyIDCMP (w, NULL);
   Permit ();
   CloseWindow (w);
end5:
   CloseScreen (s);
end4:
   CloseLibrary (ILBMBase);
end3:
   CloseLibrary (ReqBase);
end2:
   CloseLibrary (IntuitionBase);
end1:
   CloseLibrary (GfxBase);
}

/* ============================================================= */

int handle_event (msg)                 /* processes main Intuition events */

struct IntuiMessage *msg;

{
   extern int ExtendedColorRequester();
   extern int getbox(), floodfill(), do_text(), save_to_disk();
   extern int draw_line(), displayhelp(), printmap();
   extern void fullmap(), globe(), stars(), getcoord();
   extern void box(), grid(), shadow();
   extern void showmappic(), get_user_input();
   extern void getcoord_box(), disable_menus(), enable_menus();
   static char box_error[]               = "Box of zero size not allowed";
   static char drag_prompt[]             = "Press and drag left button to "
                                           "select box, right button to "
                                           "abort";
   static char expandbox_error[]         = "Invalid map displayed for Box "
                                           "Zoom Out option";
   static char flood_wait[]              = "Press left button to select area "
                                           "to fill, right button to abort";
   static char fmt_ORBITAL_TYPE[]        = "Orbital...view from %.2lf "
                                           "kilometers";
   static char fmt_ZOOM_IN_TYPE[]        = "Zoom In...view from %.2lf "
                                           "kilometers";
   static char fmt_ZOOM_OUT_TYPE[]       = "Zoom Out...view from %.2lf "
                                           "kilometers";
   static char grid_error[]              = "Invalid map displayed for Grid "
                                           "option";
   static char lines_wait[]              = "Press left button to draw lines, "
                                           "right button to abort";
   static char press_prompt[]            = "Press left button to select "
                                           "center point, right button to "
                                           "abort";
   static char print_abort[]             = "Printing aborted";
   static char print_error[]             = "Printer error";
   static char print_wait[]              = "Printing...press right button to "
                                           "abort";
   static char smallbox_error[]          = "Invalid map displayed for Box "
                                           "Zoom In option";
   static char title_BOX_TYPE[]          = "Box";
   static char title_BOX_ZOOM_IN_TYPE[]  = "Box Zoom In";
   static char title_BOX_ZOOM_OUT_TYPE[] = "Box Zoom Out";
   static char title_CLEARS_TYPE[]       = "Clear Screen";
   static char title_DRAW_LINE_TYPE[]    = "Draw Lines";
   static char title_FLAT_TYPE[]         = "Flat Map";
   static char title_FLOOD_COLOR_TYPE[]  = "Color for Flood Fill";
   static char title_FLOOD_TYPE[]        = "Flood Fill";
   static char title_GLOBE_TYPE[]        = "Globe...view from infinitely far "
                                           "away";
   static char title_HELP_TYPE[]         = "Display help file";
   static char title_HELP_TYPE_error[]   = "Error displaying help information";
   static char title_LINE_COLOR_TYPE[]   = "Color for lines";
   static char title_MERCATOR_TYPE[]     = "Mercator";
   static char title_PALETTE_TYPE[]      = "Modify Color Palette";
   static char title_PRINT_TYPE[]        = "Print Screen";
   static char title_RESET_COLOR_TYPE[]  = "Reset Colors";
   static char title_SHADOW_TYPE[]       = "Shadow";
   static char title_TEXT_COLOR_TYPE[]   = "Color for text";
   static char title_TEXT_TYPE[]         = "Text";
   static char title_abort[]             = "Selection aborted";
   static char title_buffer[]            = "                                "
                                           "                                ";
   static char title_config_not_saved[]  = "Configuration not saved";
   static char title_config_saved[]      = "Configuration saved to disk";
   static char title_not_saved[]         = "Screen not saved";
   static char title_saved[]             = "Screen saved to disk";
   static char first_time = TRUE;
   static long oldtype = -1L;
   static long flood_color = DK_GRN;
   static long line_color  = LT_YEL;
   static long text_color  = WHITE;
   static double lamg, latg;
   static double lat[2], lam[2];
   struct IntuiMessage *msgf;
   char abort;
   unsigned short select;
   int i, x, y, x0, y0, result, config;
   long menunum, itemnum, subnum, type;
   double latb[2], lamb[2], del, av;

   disable_menus (IDCMPFLAGS);         /* disable menus while do this event */
   abort = FALSE;
   if (first_time == TRUE)  {
      first_time = FALSE;
      if (got_flat_map == OK)
         oldtype = FLAT_TYPE;
   }
   switch (msg->Class) {
   case MENUPICK:
      select = msg->Code;
      if (select == MENUNULL)          /* no extended selections */
         break;
      menunum = MENUNUM (select);      /* get menu, menuitem and */
      itemnum = ITEMNUM (select);      /*   subitem id's         */
      subnum = SUBNUM (select);        /* build unique id for this */
      type = (long) (100*menunum+itemnum)*100+subnum; /*   menu selection */
      switch (menunum)  {
      case PROJECT:                    /* project menu */
         switch (itemnum)  {
         case HELP:                    /* display help file */
            SetWindowTitles (w, 0, title_HELP_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            ModifyIDCMP (w, CLOSEWINDOW);
            SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                        WAITER_X_OFFSET, WAITER_Y_OFFSET);
            if ((result = displayhelp ()) != OK)  {
               SetWindowTitles (w, 0, title_HELP_TYPE_error);
               DisplayBeep (0);
            }
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            ModifyIDCMP (w, IDCMPFLAGS);
            break;
         case SAVEIT:                  /* save map to disk */
            ModifyIDCMP (w, CLOSEWINDOW);
            if ((result = save_to_disk ()) != OK)  {
               SetWindowTitles (w, 0, title_not_saved);
               DisplayBeep (0);
            }
            else
               SetWindowTitles (w, 0, title_saved);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            ModifyIDCMP (w, IDCMPFLAGS);
            break;
         case SAVE_CONFIG:             /* save configuration to disk */
            ModifyIDCMP (w, CLOSEWINDOW);
            SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                        WAITER_X_OFFSET, WAITER_Y_OFFSET);
            if ((config = open (configfile, O_RDWR+O_CREAT)) != -1)  {
               if ((i = write (config, (char *) &configcolors[0],
                        sizeof(configcolors))) == sizeof(configcolors))
                  SetWindowTitles (w, 0, title_config_saved);
               else  {
                  SetWindowTitles (w, 0, title_config_not_saved);
                  DisplayBeep (0);
               }
               close (config);
            }
            else  {
               SetWindowTitles (w, 0, title_config_not_saved);
               DisplayBeep (0);
            }
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            ModifyIDCMP (w, IDCMPFLAGS);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            break;
         case PRINT:                   /* print map */
            SetWindowTitles (w, 0, print_wait);
            SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                        WAITER_X_OFFSET, WAITER_Y_OFFSET);
            if ((result = printmap (w)) == OK)
               SetWindowTitles (w, 0, title_PRINT_TYPE);
            else  {
               if (result == NOT_OK)
                  SetWindowTitles (w, 0, print_error);
               else
                  SetWindowTitles (w, 0, print_abort);
               DisplayBeep (0);
            }
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            break;
         case CLEARS:                  /* clear screen */
            SetWindowTitles (w, 0, title_CLEARS_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            SetRast (rp, BLUE);
            oldtype = type;
            break;
         case QUIT:                    /* close up shop */
            enable_menus ();
            return (NOT_OK);
            break;
         default:
            break;
         }
         break;
      case MAPS:                       /* maps menu */
         switch (itemnum)  {
         case PLANE:
            switch (subnum)  {
            case FLAT:                 /* flat map */
               SetWindowTitles (w, 0, title_FLAT_TYPE);
               ShowTitle (s, TRUE);
               title_toggle = TRUE;
               ModifyIDCMP (w, CLOSEWINDOW);
               SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                           WAITER_X_OFFSET, WAITER_Y_OFFSET);
               fullmap (map, FLAT_TYPE);
               SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                           ARROW_X_OFFSET, ARROW_Y_OFFSET);
               ModifyIDCMP (w, IDCMPFLAGS);
               oldtype = type;
               break;
            case MERCATOR:             /* Mercator map */
               SetWindowTitles (w, 0, title_MERCATOR_TYPE);
               ShowTitle (s, TRUE);
               title_toggle = TRUE;
               ModifyIDCMP (w, CLOSEWINDOW);
               SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                           WAITER_X_OFFSET, WAITER_Y_OFFSET);
               fullmap (map, MERCATOR_TYPE);
               SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                           ARROW_X_OFFSET, ARROW_Y_OFFSET);
               ModifyIDCMP (w, IDCMPFLAGS);
               oldtype = type;
               break;
            default:
               break;
            }
            break;
         case SPHERE:
            switch (subnum)  {
            case ZOOM_IN:              /* zoom views */
            case ZOOM_OUT:
               if (type==ZOOM_IN_TYPE)  {
                  view_height /= 2.;
                  if (view_height<MIN_HEIGHT)
                     view_height = MIN_HEIGHT;
                  sprintf (title_buffer, fmt_ZOOM_IN_TYPE, view_height);
               }
               else  {
                  view_height *= 2.;
                  sprintf (title_buffer, fmt_ZOOM_OUT_TYPE, view_height);
               }
               eta = view_height/RE;
               facp = 1. + eta;
               etap = 1./facp;
            case GLOBE:                /* globe views */
            case ORBITAL:
               if ( (type!=ZOOM_IN_TYPE && type!=ZOOM_OUT_TYPE) ||
                (oldtype!=ZOOM_IN_TYPE && oldtype!=ZOOM_OUT_TYPE &&
                 oldtype!=GLOBE_TYPE   && oldtype!=ORBITAL_TYPE) )  {
                  SetWindowTitles (w, 0, press_prompt);
                                       /* wait for mouse button */
                  if (oldtype!=FLAT_TYPE && oldtype!=MERCATOR_TYPE)  {
                     ModifyIDCMP (w, CLOSEWINDOW);
                     SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                                 WAITER_X_OFFSET, WAITER_Y_OFFSET);
                     showmappic ();
                     ModifyIDCMP (w, IDCMPFLAGS);
                     oldtype = FLAT_TYPE;
                  }
                  ShowTitle (s, TRUE);
                  title_toggle = TRUE;
                  SetPointer (w, cross, CROSS_SIZE/4-2, 16, 
                              CROSS_X_OFFSET, CROSS_Y_OFFSET);
                  while (1)  {
                     WaitPort (w->UserPort);
                     if ((msgf = (struct IntuiMessage *) GetMsg (w->UserPort))
                          ==NULL)
                        continue;
                     else if (msgf->Code==SELECTDOWN)
                        break;
                     else if (msgf->Code == MENUDOWN)  {
                        abort = TRUE;
                        ReplyMsg (msgf);
                        SetWindowTitles (w, 0, title_abort);
                        SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                                    ARROW_X_OFFSET, ARROW_Y_OFFSET);
                        DisplayBeep (0);
                        break;
                     }
                     else
                        ReplyMsg (msgf);
                  }
                  if (abort == TRUE)
                     break;
                  x = msgf->MouseX;    /* get mouse position */
                  y = msgf->MouseY;
                  ReplyMsg (msgf);
                  getcoord (x, y, oldtype, &latg, &lamg);
               }
               if (type==ORBITAL_TYPE)  {
                  get_user_input (ORBITAL_TYPE, x, y, &view_height,
                                  &x0, &y0);
                  eta = view_height/RE;   /* initialize values for */
                  facp = 1. + eta;        /*   orbital view        */
                  etap = 1./facp;
                  sprintf (title_buffer, fmt_ORBITAL_TYPE, view_height);
                  SetWindowTitles (w, 0, title_buffer);
               }
               else if (type==GLOBE_TYPE)
                  SetWindowTitles (w, 0, title_GLOBE_TYPE);
               else
                  SetWindowTitles (w, 0, title_buffer);
               ShowTitle (s, TRUE);
               title_toggle = TRUE;
               ModifyIDCMP (w, CLOSEWINDOW);
               SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                           WAITER_X_OFFSET, WAITER_Y_OFFSET);
               globe (map, map_trig, latg, lamg, type); /* draw map */
               stars ();
               SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                           ARROW_X_OFFSET, ARROW_Y_OFFSET);
               ModifyIDCMP (w, IDCMPFLAGS);
               oldtype = type;
               break;
            default:
               break;
            }
            break;
         case BOXES:
            switch (subnum)  {
            case BOX_ZOOM_IN:          /* box zoom in */
               if (oldtype != BOX_TYPE && oldtype != BOX_ZOOM_IN_TYPE &&
                   oldtype != BOX_ZOOM_OUT_TYPE)  {
                  SetWindowTitles (w, 0, smallbox_error);
                  ShowTitle (s, TRUE);
                  title_toggle = TRUE;
                  DisplayBeep (0);
                  break;
               }                       /* and fall through to  */
            case BOX:                  /* ordinary box request */
               SetWindowTitles (w, 0, drag_prompt);
               ShowTitle (s, TRUE);
               title_toggle = TRUE;
               if (type == BOX_TYPE && (oldtype!=FLAT_TYPE &&
                   oldtype!=MERCATOR_TYPE))  {
                  ModifyIDCMP (w, CLOSEWINDOW);
                  SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                              WAITER_X_OFFSET, WAITER_Y_OFFSET);
                  showmappic ();
                  ModifyIDCMP (w, IDCMPFLAGS);
                  oldtype = FLAT_TYPE;
               }
               SetPointer (w, cross, CROSS_SIZE/4-2, 16,
                           CROSS_X_OFFSET, CROSS_Y_OFFSET);
               if ((result = getbox (&x0, &y0, &x, &y)) != OK)  {
                  SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                              ARROW_X_OFFSET, ARROW_Y_OFFSET);
                  if (result == ABORT)
                     SetWindowTitles (w, 0, title_abort);
                  else
                     SetWindowTitles (w, 0, box_error);
                  DisplayBeep (0);
                  break;
               }
               if (type == BOX_TYPE)
                  SetWindowTitles (w, 0, title_BOX_TYPE);
               else
                  SetWindowTitles (w, 0, title_BOX_ZOOM_IN_TYPE);
               ModifyIDCMP (w, CLOSEWINDOW);
               SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                           WAITER_X_OFFSET, WAITER_Y_OFFSET);
               if (type == BOX_TYPE)  {
                  getcoord (x0, y0, oldtype, &lat[0], &lam[0]);
                  getcoord (x, y, oldtype, &lat[1], &lam[1]);
               }
               else  {
                  getcoord_box (x0, y0, lat, lam, &latb[0], &lamb[0]);
                  getcoord_box (x, y, lat, lam, &latb[1], &lamb[1]);
                  lat[0] = latb[0];
                  lat[1] = latb[1];
                  lam[0] = lamb[0];
                  lam[1] = lamb[1];
               }
               box (map, lat, lam);
               SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                           ARROW_X_OFFSET, ARROW_Y_OFFSET);
               ModifyIDCMP (w, IDCMPFLAGS);
               oldtype = type;
               break;
            case BOX_ZOOM_OUT:         /* box zoom out */
               if (oldtype != BOX_TYPE && oldtype != BOX_ZOOM_IN_TYPE &&
                   oldtype != BOX_ZOOM_OUT_TYPE)  {
                  SetWindowTitles (w, 0, expandbox_error);
                  ShowTitle (s, TRUE);
                  title_toggle = TRUE;
                  DisplayBeep (0);
                  break;
               }
               SetWindowTitles (w, 0, title_BOX_ZOOM_OUT_TYPE);
               ModifyIDCMP (w, CLOSEWINDOW);
               SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                           WAITER_X_OFFSET, WAITER_Y_OFFSET);
               del = EXPAND_SCALE_FACTOR * (lat[0] - lat[1])/2.;
               av = (lat[0] + lat[1])/2.;
               lat[0] = av + del;
               lat[1] = av - del;
               if (lat[0] > 90.)
                  lat[0] = 90.;
               if (lat[1] < -90.)
                  lat[1] = -90.;
               del = EXPAND_SCALE_FACTOR * (lam[1] - lam[0])/2.;
               av = (lam[0] + lam[1])/2.;
               lam[0] = av - del;
               lam[1] = av + del;
               if (lam[0] < -180.)
                  lam[0] = -180.;
               if (lam[1] > 180.)
                  lam[1] = 180.;
               box (map, lat, lam);
               SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                           ARROW_X_OFFSET, ARROW_Y_OFFSET);
               ModifyIDCMP (w, IDCMPFLAGS);
               oldtype = type;
               break;
            default:
               break;
            }
            break;
         default:
            break;
         }
         break;
      case COLORS:                     /* colors menu */
         switch (itemnum)  {
         case PALETTE:                 /* modify palette */
            SetWindowTitles (w, 0, title_PALETTE_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            ModifyIDCMP (w, CLOSEWINDOW);
            SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                        WAITER_X_OFFSET, WAITER_Y_OFFSET);
            result = ExtendedColorRequester (&colorstruct);
            for (i=0; i<NUM_COLORS; ++i)
               configcolors[i] = GetRGB4 (vp->ColorMap, i);
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            ModifyIDCMP (w, IDCMPFLAGS);
            break;
         case FLOOD_COLOR:             /* select color for flood fill */
            flood_color = subnum;
            SetWindowTitles (w, 0, title_FLOOD_COLOR_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            break;
         case LINE_COLOR:              /* select color for lines */
            line_color = subnum;
            SetWindowTitles (w, 0, title_LINE_COLOR_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            break;
         case TEXT_COLOR:              /* select color for text */
            text_color = subnum;
            SetWindowTitles (w, 0, title_TEXT_COLOR_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            break;
         case RESET_COLOR:             /* load default color table */
            SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                        WAITER_X_OFFSET, WAITER_Y_OFFSET);
            LoadRGB4 (vp, &mapcolors[0], NUM_COLORS);
            CopyMem ((char *) mapcolors, (char *) configcolors,
                     sizeof(mapcolors));
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            SetWindowTitles (w, 0, title_RESET_COLOR_TYPE);
            break;
         default:
            break;
         }
         break;
      case EDIT:                       /* edit menu */
         switch (itemnum)  {
         case GRID:
            if (oldtype!=FLAT_TYPE     && oldtype!=MERCATOR_TYPE &&
                oldtype!=GLOBE_TYPE    && oldtype!=ORBITAL_TYPE  &&
                oldtype!=ZOOM_IN_TYPE  && oldtype!=ZOOM_OUT_TYPE)  {
               SetWindowTitles (w, 0, grid_error);
               ShowTitle (s, TRUE);
               title_toggle = TRUE;
               DisplayBeep (0);
            }
            else  {
               ModifyIDCMP (w, CLOSEWINDOW);
               SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                           WAITER_X_OFFSET, WAITER_Y_OFFSET);
               grid (oldtype, latg, lamg);
               SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
               ModifyIDCMP (w, IDCMPFLAGS);
            }
            break;
         case FLOOD:                   /* flood fill */
            SetWindowTitles (w, 0, flood_wait);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            SetPointer (w, cross, CROSS_SIZE/4-2, 16,
                        CROSS_X_OFFSET, CROSS_Y_OFFSET);
            if ((result = floodfill (flood_color)) == OK)
               SetWindowTitles (w, 0, title_FLOOD_TYPE);
            else  {
               SetWindowTitles (w, 0, title_abort);
               DisplayBeep (0);
            }
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            break;
         case DRAW_LINE:               /* draw connected lines */
            SetWindowTitles (w, 0, lines_wait);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            SetPointer (w, cross, CROSS_SIZE/4-2, 16,
                        CROSS_X_OFFSET, CROSS_Y_OFFSET);
            if ((result = draw_line (line_color)) == OK)
               SetWindowTitles (w, 0, title_DRAW_LINE_TYPE);
            else  {
               SetWindowTitles (w, 0, title_abort);
               DisplayBeep (0);
            }
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            break;
         case SHADOW:                  /* make shadows */
            ModifyIDCMP (w, CLOSEWINDOW);
            ShowTitle (s, FALSE);      /* don't shadow the title */
            SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
                        WAITER_X_OFFSET, WAITER_Y_OFFSET);
            shadow ();
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            SetWindowTitles (w, 0, title_SHADOW_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            ModifyIDCMP (w, IDCMPFLAGS);
            break;
         case TEXT:                    /* get user text */
            x = msg->MouseX;
            y = msg->MouseY;
            if ((result = do_text (x, y, text_color)) == ABORT)  {
               SetWindowTitles (w, 0, title_abort);
               DisplayBeep (0);
            }
            else
               SetWindowTitles (w, 0, title_TEXT_TYPE);
            ShowTitle (s, TRUE);
            title_toggle = TRUE;
            break;
         case TTOGGLE:                 /* toggle screen title bar */
            title_toggle ^= 1L;
            ShowTitle (s, title_toggle);
            break;
         default:
            break;
         }
         break;
      default:
         break;
      }
      break;
   default:
      break;
   }
   enable_menus ();                    /* re-enable menu events */
   return (OK);
}

/* ================================================================ */

void box (ws, latp, lamp)              /* draws areas contained within */
                                       /*   a rectangular region       */
short *ws;
double *latp, *lamp;

{
   extern void drawbox();
   char first, prev_in_view, in_view;
   short h1, h1c, h1prev;              /* x-dist. (pix) from center */
   short h2, h2c, h2prev;              /* y-dist. (pix) from center */
   int i, na;
   int lat1i, lat2i, lam1i, lam2i;
   long x, y;
   double lam, lamc, lamprev;          /* longitude */
   double lat, latc, latprev;          /* latitude  */
   double bwidth, bheight, bcx, bcy, xscale, yscale;
   double lat1, lat2, lam1, lam2;

   lat1 = latp[0];                     /* store values for box corners */
   lat2 = latp[1];                     /*   locally                    */
   lam1 = lamp[0];
   lam2 = lamp[1];
   lat1i = 100.*lat1;
   lat2i = 100.*lat2;
   lam1i = 100.*lam1;
   lam2i = 100.*lam2;
   bwidth = lam2 - lam1;               /* box width (degrees)         */
   bheight = lat1 - lat2;              /* box height (degrees)        */
   bcx = (lam1 + lam2)/2.;             /* x-coord of box center (deg) */
   bcy = (lat1 + lat2)/2.;             /* y-coord of box center (deg) */
   xscale = WWIDTH / bwidth;           /* horizontal scale (pix/deg)  */
   yscale = WHEIGHT / bheight;         /* vertical scale (pix/deg)    */
   first = TRUE;
   SetRast (rp, BLUE);                 /* clear to blue background */
   SetAPen (rp, ORANGE);               /* set segment color */

   drawbox (0, 0, WWIDTH-1, WHEIGHT-1); /* outline the box */

   for (na=0; na<NSEGS; ++na)  {       /* do each segment */
      first = TRUE;                    /* skip if not in view */
      if (seg[na].lat_min > lat1i || seg[na].lat_max < lat2i ||
          seg[na].lam_min > lam2i || seg[na].lam_max < lam1i)
         continue;
      for (i=seg[na].first; i<=seg[na].last; i+=2)  {
         lat = ws[i];                  /* latitude */
         lat = lat/100.;
         lam = ws[i+1];                /* longitude */
         lam = lam/100.;
         in_view = FALSE;              /* get status of current point */
         if ( (lat<=lat1 && lat>=lat2)
           && (lam>=lam1 && lam<=lam2))  {
            in_view = TRUE;
            h1 = (lam-bcx) * xscale;
            h2 = - (lat-bcy) * yscale;
         }
         if (first==TRUE)  {           /* check first point */
            first = FALSE;
            if (in_view==TRUE)  {      /* if first point is in view, */
               x = h1 + CENTERX;       /*   move pen to first point  */
               y = h2 + CENTERY;       /*   and plot it              */
               Move (rp, x, y);
               WritePixel (rp, x, y);
               h1prev = h1;
               h2prev = h2;
            }
            prev_in_view = in_view;    /* save status of first point */
            latprev = lat;
            lamprev = lam;
            continue;
         }
         if (in_view==TRUE)  {
            if (prev_in_view==FALSE)  { /* if prev. point was not in view, */
               if (lamprev<=lam1)  {    /*   find rim point                */
                  lamc = lam1;
                  if (latprev<=lat2)   /* lower left */
                     latc = lat2;
                  else if (latprev>=lat1) /* upper left */
                     latc = lat1;
                  else                 /* left center */
                     latc = lat + (latprev-lat)*(lamc-lam)/(lamprev-lam);
               }
               else if (lamprev>=lam2)  {
                  lamc = lam2;
                  if (latprev>=lat1)   /* upper right */
                     latc = lat1;
                  else if (latprev<=lat2) /* lower right */
                     latc = lat2;
                  else                 /* right center */
                     latc = lat + (latprev-lat)*(lamc-lam)/(lamprev-lam);
               }
               else  {
                  if (latprev>=lat1)   /* top center */
                     latc = lat1;
                  else                 /* bottom center */
                     latc = lat2;
                  lamc = lam + (lamprev-lam)*(latc-lat)/(latprev-lat);
               }
               h1c = (lamc-bcx) * xscale;
               h2c = - (latc-bcy) * yscale;
               x = h1c + CENTERX;      /* move to rim point & plot it */
               y = h2c + CENTERY;
               Move (rp, x, y);
               WritePixel (rp, x, y);
               h1prev = h1c;
               h2prev = h2c;
            }
            if (h1!=h1prev || h2!=h2prev)  {
               x = h1 + CENTERX;       /* draw to current point */
               y = h2 + CENTERY;
               Draw (rp, x, y);
               Move (rp, x, y);
            }
            h1prev = h1;
            h2prev = h2;
         }
         else  {                       /* else out of view */
            if (prev_in_view==TRUE)  { /* if previous point was in view, */
               if (lam<=lam1)  {       /*   find rim point                */
                  lamc = lam1;
                  if (lat<=lat2)       /* lower left */
                     latc = lat2;
                  else if (lat>=lat1)  /* upper left */
                     latc = lat1;
                  else                 /* left center */
                     latc = lat + (latprev-lat)*(lamc-lam)/(lamprev-lam);
               }
               else if (lam>=lam2)  {
                  lamc = lam2;
                  if (lat>=lat1)       /* upper right */
                     latc = lat1;
                  else if (lat<=lat2)  /* lower right */
                     latc = lat2;
                  else                 /* right center */
                     latc = lat + (latprev-lat)*(lamc-lam)/(lamprev-lam);
               }
               else  {
                  if (lat>=lat1)       /* top center */
                     latc = lat1;
                  else                 /* bottom center */
                     latc = lat2;
                  lamc = lam + (lamprev-lam)*(latc-lat)/(latprev-lat);
               }
               h1c = (lamc-bcx) * xscale;
               h2c = - (latc-bcy) * yscale;
               x = h1c + CENTERX;      /* draw to rim point */
               y = h2c + CENTERY;
               Draw (rp, x, y);
               Move (rp, x, y);
            }
         }
         prev_in_view = in_view;       /* save status of current point */
         latprev = lat;
         lamprev = lam;
      }
   }
}

/* ============================================================= */

void disable_menus (flags)             /* disables menus */

long flags;

{
   ModifyIDCMP (w, flags);
   Forbid ();
   w->Flags |= RMBTRAP;
   Permit ();
}

/* ============================================================= */

int displayhelp ()                     /* displays help information */

{
   extern void dohelpitem();
   struct IntuiMessage *msg;
   short gadgetnum;

   if (helpbuffer == NULL)
      return (NOT_OK);
   if ((hw = (struct Window *) OpenWindow (&newhelpw)) == NULL)
      return (NOT_OK);
   SetPointer (hw, arrow, ARROW_SIZE/4-2, 16, ARROW_X_OFFSET,
               ARROW_Y_OFFSET);
   DrawBorder (hw->RPort, &border1, 0, 0);
   while (1)  {
      WaitPort (hw->UserPort);
      if ((msg = (struct IntuiMessage * ) GetMsg (hw->UserPort)) == NULL)
         continue;
      gadgetnum = ((struct Gadget *) (msg->IAddress))->GadgetID;
      ReplyMsg (msg);
      if (gadgetnum < NUMGADGETS-1)  {
         SetPointer (hw, waiter, WAITER_SIZE/4-2, 16, WAITER_X_OFFSET,
                     WAITER_Y_OFFSET);
         ModifyIDCMP (hw, NULL);
         dohelpitem (gadgetnum);
         ModifyIDCMP (hw, GADGETUP);
         SetPointer (hw, arrow, ARROW_SIZE/4-2, 16, ARROW_X_OFFSET,
                     ARROW_Y_OFFSET);
      }
      if (gadgetnum == NUMGADGETS-1)
         break;
   }
   ClearPointer (hw);
   CloseWindow (hw);
   return (OK);
}

/* ============================================================= */

void dohelpitem (num)

int num;

{
   static char defaulttext[] = "No help information to display";
   char *txt;
   short result;
   int disp;

   if ((disp = gadgetlist[num].disp) < 0)
      txt = defaulttext;
   else
      txt = &(helpbuffer[disp]);
   trs.Text = txt;
   trs.Window = hw;
   trs.Title = gadgetlist[num].text;
   result = TextRequest (&trs);
}

/* ============================================================= */

int do_text (xin, yin, color)          /* get user text input */

int xin, yin;
long color;

{
   extern void get_user_input(), drawbox();
   struct IntuiMessage *msg1;
   static char title_setdown[] = "Press left button to position text,"
                                 " right button to cancel";
   int x, y, xold, yold, pixlength;
   long oldpen;
   double t;
                                       /* get user text */
   get_user_input (TEXT_TYPE, xin, yin, &t, &xold, &yold);
   pixlength = TextLength (rp, user_text_input, strlen(user_text_input));
   SetWindowTitles (w, 0, title_setdown);
   ShowTitle (s, TRUE);
   title_toggle = TRUE;
   SetDrMd (rp, JAM2 | COMPLEMENT);
   SetPointer (w, transparent, TRANSPARENT_SIZE/4-2, 16,
               TRANSPARENT_X_OFFSET, TRANSPARENT_Y_OFFSET);
   drawbox (xold, yold, xold+pixlength-1, yold-9);
   ModifyIDCMP (w, IDCMPFLAGS | MOUSEMOVE);
   while (1)  {
      WaitPort (w->UserPort);          /* wait for mouse button  */
      if ((msg1 = (struct IntuiMessage *) GetMsg (w->UserPort)) == NULL)
         continue;
      else  {
         x = msg1->MouseX;             /* get current mouse position */
         y = msg1->MouseY;             /*   and erase old box        */
         if (msg1->Code == SELECTDOWN)  {
            ReplyMsg (msg1);           /* done if select button pressed */
            drawbox (xold, yold, xold+pixlength-1, yold-9);
            break;
         }
         else if (msg1->Class == MOUSEMOVE)  {
                                       /* else draw box at current position */
            drawbox (xold, yold, xold+pixlength-1, yold-9);
            drawbox (x, y, x+pixlength-1, y-9);
            xold = x;
            yold = y;
         }
         else if (msg1->Code == MENUDOWN)  {
            ReplyMsg (msg1);
            ModifyIDCMP (w, IDCMPFLAGS);
            drawbox (xold, yold, xold+pixlength-1, yold-9);
            SetDrMd (rp, JAM2);
            SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
                        ARROW_X_OFFSET, ARROW_Y_OFFSET);
            return (ABORT);
         }
         ReplyMsg (msg1);
      }
   }
   Move (rp, x, y-2);                  /* move to current mouse position  */
   SetDrMd (rp, JAM1);                 /* (-2 to allow for font baseline) */
   oldpen = rp->FgPen;
   SetAPen (rp, color);                /* draw text */
   Text (rp, user_text_input, strlen(user_text_input));
   SetAPen (rp, oldpen);
   SetDrMd (rp, JAM2);
   ModifyIDCMP (w, IDCMPFLAGS);        /* enable original event types */
   SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
               ARROW_X_OFFSET, ARROW_Y_OFFSET);
   return (OK);
}

/* ============================================================= */

void drawbox (x1, y1, x2, y2)          /* draws a box */

int x1, y1, x2, y2;

{
   Move (rp, x1, y1);
   Draw (rp, x2, y1);
   Move (rp, x2, y1);
   Draw (rp, x2, y2);
   Move (rp, x2, y2);
   Draw (rp, x1, y2);
   Move (rp, x1, y2);
   Draw (rp, x1, y1);
   Move (rp, x1, y1);
}

/* ============================================================= */

int draw_line (color)                  /* draws a line based on */
                                       /*   mouse positions     */
long color;

{
   struct IntuiMessage *msg;
   char selectbutton;
   int x, y;
   long oldpen;

   selectbutton = FALSE;
   oldpen = rp->FgPen;
   SetAPen (rp, color);
   ModifyIDCMP (w, IDCMPFLAGS | MOUSEMOVE); /* enable mouse move events */
   while (1)  {
      WaitPort (w->UserPort);
      if ((msg = (struct IntuiMessage *) GetMsg (w->UserPort)) == NULL)
         continue;
      else if (msg->Code == MENUDOWN)  { /* stop if press menu button */
         ReplyMsg (msg);
         ModifyIDCMP (w, IDCMPFLAGS);  /* disable mouse move events */
         SetAPen (rp, oldpen);         /* restore original pen */
         return (ABORT);
      }
      else if (msg->Code == SELECTDOWN)  {
         x = msg->MouseX;                 /* get current mouse position */
         y = msg->MouseY;
         ReplyMsg (msg);
         WritePixel (rp, x, y);
         Move (rp, x, y);
         selectbutton = TRUE;
      }
      else if (selectbutton == TRUE && msg->Class == MOUSEMOVE)  {
         x = msg->MouseX;           /* get current mouse position */
         y = msg->MouseY;
         ReplyMsg (msg);
         Draw (rp, x, y);           /* draw to current position */
         Move (rp, x, y);
      }
      else if (selectbutton == TRUE && msg->Code == SELECTUP)
         break;
      else
         ReplyMsg (msg);
   }
   ReplyMsg (msg);
   ModifyIDCMP (w, IDCMPFLAGS);        /* enable original event types */
   SetAPen (rp, oldpen);               /* restore original pen */
   return (OK);
}

/* ============================================================= */

void enable_menus ()                   /* enables menus */

{
   ModifyIDCMP (w, IDCMPFLAGS);
   Forbid ();
   w->Flags &= ~RMBTRAP;
   Permit ();
}

/* ============================================================= */

int floodfill (flood_color)            /* flood fills an area based */
                                       /*   on mouse position       */
long flood_color;

{
   struct IntuiMessage *msgf;
   int x, y;
   long oldpen;
   
   while (1)  {
      WaitPort (w->UserPort);          /* wait for message */
      if ((msgf = (struct IntuiMessage *) GetMsg (w->UserPort)) == NULL)
         continue;
      else if (msgf->Code==SELECTDOWN)
         break;
      else if (msgf->Code == MENUDOWN)  {
         ReplyMsg (msgf);
         return (ABORT);
      }
      else
         ReplyMsg (msgf);
   }
   x = msgf->MouseX;                   /*  get mouse coordinates  */
   y = msgf->MouseY;
   ReplyMsg (msgf);
   oldpen = rp->FgPen;
   SetAPen (rp, flood_color);
   Flood (rp, 1, x, y);                /* flood fill the region, then */
   SetAPen (rp, oldpen);               /*   restore original pen      */
   return (OK);
}

/* ============================================================= */

void fullmap (ws, type)                /* draws flat and mercator */
                                       /*   map projections       */
short *ws;
long type;

{
   extern void savemappic();
   short h1, h2, h1old, h2old;
   int i, na, np;
   long x, y;
   double t;

   SetRast (rp, BLUE);                 /* clear screen */
   SetAPen (rp, ORANGE);
   for (na=0; na<NSEGS; ++na)  {       /* do each segment */
      np = 0;
      for (i=seg[na].first; i<=seg[na].last; i+=2)  {
         t = ws[i];                    /* y = latitude */
         if (type==FLAT_TYPE)
            t = (t/100.) * VFACTOR;
         else if (type==MERCATOR_TYPE)  {
            t = (t/200. + 45.) * RAD;
            t = log (tan (t)) * M_VFACTOR;
         }
         if (t<0.)
            t -= 0.5;
         else
            t += 0.5;
         h2 = -t;
         t = ws[i+1];                  /* x = longitude */
         t = (t/100.) * HFACTOR;
         if (t<0.)
            t -= 0.5;
         else
            t += 0.5;
         h1 = t;
         x = h1 + CENTERX;
         y = h2 + CENTERY;
         if (np!=0)  {                 /* disallow identical adjacent pts */
            if (h1==h1old  && h2==h2old)
               continue;
            else
               Draw (rp, x, y);
         }
         else
            WritePixel (rp, x, y);
         Move (rp, x, y);
         h1old = h1;
         h2old = h2;
         ++np;
      }
   }
   if (got_flat_map != OK && type == FLAT_TYPE)  {
      savemappic ();                   /* save map in save area */
      got_flat_map = OK;
   }
}

/* ============================================================= */

int getbox (x0, y0, x, y)              /* selects a region to draw to */
                                       /*   larger scale              */
int *x0, *y0, *x, *y;

{
   extern void drawbox();
   struct IntuiMessage *msgf;
   char selectbutton;
   int xd, yd, x1, x2, y1, y2;

   selectbutton = FALSE;
   SetDrMd (rp, JAM2 | COMPLEMENT);    /* turn on complement mode and */
   ModifyIDCMP (w, IDCMPFLAGS | MOUSEMOVE); /* enable mouse move events */
   while (1)  {
      WaitPort (w->UserPort);
      if ((msgf = (struct IntuiMessage *) GetMsg (w->UserPort)) == NULL)
         continue;
      else if (msgf->Code == MENUDOWN)  { /* abort if user pressed */
         ReplyMsg (msgf);                 /*   menu button         */
         ModifyIDCMP (w, IDCMPFLAGS);
         SetDrMd (rp, JAM2);
         return (ABORT);
      }
      else if (msgf->Code==SELECTDOWN)  { /* if user pressed left button, */
         x1 = msgf->MouseX;               /*   get initial mouse position */
         y1 = msgf->MouseY;
         ReplyMsg (msgf);
         xd = x1;
         yd = y1;
         selectbutton = TRUE;
      }
      else if (selectbutton==TRUE && msgf->Class==MOUSEMOVE)  {
         x2 = msgf->MouseX;
         y2 = msgf->MouseY;
         ReplyMsg (msgf);
         drawbox (x1, y1, xd, yd);     /* erase old box */
         xd = x2;
         yd = y2;
         drawbox (x1, y1, xd, yd);     /* draw new box */
      }
      else if (selectbutton==TRUE && msgf->Code==SELECTUP)
         break;
   }
   ModifyIDCMP (w, IDCMPFLAGS);        /* disable mouse events and */
   x2 = msgf->MouseX;                  /*    erase current box     */
   y2 = msgf->MouseY;
   ReplyMsg (msgf);
   drawbox (x1, y1, xd, yd);
   SetDrMd (rp, JAM2);                 /* restore original drawing mode */
   *x0 = x1;
   *y0 = y1;
   *x = x2;
   *y = y2;
   if (x1==x2 || y1==y2)               /* error if box is of zero area */
      return (NOT_OK);
   if (x1>x2 && y1>y2)  {              /* ensure that the vertices of */
      *x0 = x2;                        /*   the box are in the proper */
      *y0 = y2;                        /*   order                     */
      *x = x1;
      *y = y1;
   }
   else if (x1<x2 && y1>y2)  {
      *x0 = x1;
      *y0 = y2;
      *x = x2;
      *y = y1;
   }
   else if (x1>x2 && y1<y2)  {
      *x0 = x2;
      *y0 = y1;
      *x = x1;
      *y = y2;
   }
   return (OK);
}

/* ============================================================= */

void getcoord (x, y, type, lat, lam)   /* converts screen coordinates   */
                                       /*   into latitude and longitude */
int x, y;                              /*   for flat and Mercator maps  */
long type;
double *lat, *lam;

{
   (*lam) = (x - CENTERX) / HFACTOR;
   if (type==FLAT_TYPE)
      (*lat) = (CENTERY - y) / VFACTOR;
   else
      (*lat) = -90. + 2.*atan(exp((CENTERY-y)/M_VFACTOR))/RAD;
}

/* ============================================================= */

                                       /* get latitude and longitude */
                                       /*   for Box Zoom In option   */
void getcoord_box (x, y, latin, lamin, latout, lamout)

int x, y;
double latin[2], lamin[2], *latout, *lamout;

{
   (*lamout) = (((double) (x)) / WWIDTH)  * (lamin[1]-lamin[0]) + lamin[0];
   (*latout) = (((double) (y)) / WHEIGHT) * (latin[1]-latin[0]) + latin[0];
}

/* ============================================================= */

int get_min_max (ws)                   /* find limits of each segment */

short *ws;

{
   int ifile, na, i;
   unsigned int ix;

   if ((seg = (struct Arc *) calloc (NSEGS, sizeof (struct Arc))) == NULL)  {
      printf ("Unable to get space for limits\n");
      return (NOT_OK);
   }                                   /* try to read limits from disk */
   if ((ifile = open (limitsfile, O_RDONLY)) > 0)  {
      ix = (unsigned) read (ifile, (char *) seg, SEG_LIST_SIZE);
      close (ifile);
      if (ix == SEG_LIST_SIZE)
         return (OK);
      else
         printf ("Limits file corrupted, building it afresh\n");
   }
   na = 0;                             /* build limits array from scratch */
   seg[0].first = seg[0].last = 0;
   seg[0].lat_min = 9000;
   seg[0].lat_max = -9000;
   seg[0].lam_min = 18000;
   seg[0].lam_max = -18000;
   for (i=0; i<MAXVAL; i+=2)  {
      if (ws[i] == 0 && ws[i+1] == 0)  {
         seg[na].last = i-2;
         if (na >= (NSEGS-1))          /* trap running past end of */
            break;                     /*   segment structure      */
         ++na;
         seg[na].first = i+2;
         seg[na].last = 0;
         seg[na].lat_min = 9000;
         seg[na].lat_max = -9000;
         seg[na].lam_min = 18000;
         seg[na].lam_max = -18000;
         continue;
      }
      if (ws[i] < seg[na].lat_min)
         seg[na].lat_min = ws[i];
      if (ws[i] > seg[na].lat_max)
         seg[na].lat_max = ws[i];
      if (ws[i+1] < seg[na].lam_min)
         seg[na].lam_min = ws[i+1];
      if (ws[i+1] > seg[na].lam_max)
         seg[na].lam_max = ws[i+1];
   }
   if (na != (NSEGS-1) || i != MAXVAL-2)  {
      free ((char *) seg);             /* error if map file */
      return (NOT_OK);                 /*   is corrupted    */
   }
   ix = 0;                             /* save limits to disk */
   if ((ifile = open (limitsfile, O_RDWR + O_CREAT)) != -1)  {
      ix = (unsigned) write (ifile, (char *) seg, SEG_LIST_SIZE);
      close (ifile);
   }
   if (ix != SEG_LIST_SIZE || ifile == -1) /* if error in saving to disk, */
      printf ("Unable to save limits to disk, continuing anyway\n");
   return (OK);                        /* continue anyway */
}

/* ============================================================= */

void get_user_input (type, xin, yin, d, xout, yout)
                                       /* prompts for user input */
                                       /*   (text or doubleword) */
long type;
int xin, yin, *xout, *yout;
double *d;

{
   extern double atof();
   struct IntuiMessage *msg;
   struct Gadget *g;
   int x, y;
                                       /* position the requester */
   x = xin - GAD_LEFT - 8*(NUM_CHAR-2);
   y = yin - GAD_TOP - 2;
   if ((x+TWIDTH) >= WWIDTH)
      x = WWIDTH - TWIDTH - 15;
   if (x < 10)
      x = 10;
   if ((y+THEIGHT) >= WHEIGHT)
      y = WHEIGHT - THEIGHT - 15;
   if (y < 5)
      y = 5;
   req.LeftEdge = x;
   req.TopEdge = y;
   if (type == ORBITAL_TYPE)  {        /* initialize for doubleword input */
      req.ReqText = &dtext;
      gadgetinput.Buffer = &user_double_input[0];
   }
   else  {                             /* initialize for text input */
      req.ReqText = &rtext;
      gadgetinput.Buffer = &user_text_input[0];
   }
   Request (&req, w);                  /* issue the requester */
   ModifyIDCMP (w, GADGETUP);          /* disable other events */
   ActivateGadget (&gad, w, &req);
   while (1)  {
      WaitPort (w->UserPort);
      if ((msg = (struct IntuiMessage *) GetMsg (w->UserPort)) == NULL)
         continue;
      else if (msg->Class == GADGETUP)  {
         g = (struct Gadget *) (msg->IAddress);
         if (g->GadgetID != GAD_FIRST)  {
            ReplyMsg (msg);
            continue;
         }
         *xout = msg->MouseX;          /* get mouse coordinates */
         *yout = msg->MouseY;
         ReplyMsg (msg);
         if (type == ORBITAL_TYPE)  {  /* get user value */
            *d = atof (&user_double_input[0]);
            if (errno == ERANGE)  {    /* trap erroneous input */
               *d = VIEW_HEIGHT;       /* store default value */
               sprintf (user_double_input, "%.2lf", *d);
               DisplayBeep (0);
            }
            else if ((*d) < MIN_HEIGHT)  {
               *d = MIN_HEIGHT;        /* store minimum value */
               sprintf (user_double_input, "%.2lf", *d);
               DisplayBeep (0);
            }
         }
         break;
      }
      else                             /* ignore inappropriate events */
         ReplyMsg (msg);
   }
   ModifyIDCMP (w, IDCMPFLAGS);
}

/* ============================================================= */

void globe (ws, ws_trig, lat0, lam0, type) /* draws globe projections */

short *ws, *ws_trig;
double lat0, lam0;
long type;

{
   extern int globe_in_view();
   extern void globe_rim_fix();
   static double fac = 1./32767.0;
   char first, prev_in_view, in_view, latzero;
   short h1, h1c, h1prev;              /* x-dist. (pix) from center */
   short h2, h2c, h2prev;              /* y-dist. (pix) from center */
   int i, na;
   long x, y;
   double lam, lamc, lamprev;          /* longitude */
   double lat, latc, latprev;          /* latitude  */
   double c0, s0, c1, s1, c2, zp, zpprev;
   double hp, fac2, fac3, facz, scale;
   double h1d, h2d, dlat, dlam, lat0p;
   double latmin, latmax, lammin, lammax;

   latzero = FALSE;
   if (type==GLOBE_TYPE)  {              /* ordinary globe view */
      hp = 0.;
      scale = 1.;
      fac3 = 1.;
   }
   else  {                             /* orbital globe view */
      hp = etap;
      scale = sqrt (1.-etap*etap);
      fac3 = (facp/(facp-hp)) * scale;
   }
   if (lat0==0.)  {
      c0 = 1.;
      s0 = 0.;
      if (type==GLOBE_TYPE)
         latzero = TRUE;               /* equatorial, ordinary globe view */
   }
   else  {
      lat0p = lat0 * RAD;
      c0 = cos (lat0p);
      s0 = sin (lat0p);
   }
   SetRast (rp, BLACK);                /* black background */
   SetAPen (rp, BLUE);                 /* blue globe */
   DrawEllipse (rp, CENTERX, CENTERY, HRADIUS, VRADIUS);
   Flood (rp, 1, CENTERX, CENTERY);
   SetAPen (rp, ORANGE);
   for (na=0; na<NSEGS; ++na)  {       /* do each segment */
      latmin = ((double) (seg[na].lat_min)) / 100.;
      latmax = ((double) (seg[na].lat_max)) / 100.;
      lammin = ((double) (seg[na].lam_min)) / 100.;
      lammax = ((double) (seg[na].lam_max)) / 100.;
      if ((i = globe_in_view (latmax, lammin, latmin, lammax, lat0, lam0,
                              hp)) != OK)
         continue;
      first = TRUE;
      for (i=seg[na].first; i<=seg[na].last; i+=2)  {
         lat = ws[i];                  /* latitude */
         lat = (lat/100.) * RAD;
         lam = ws[i+1];                /* longitude */
         lam = (lam/100. - lam0) * RAD;
         if (lam<-PI)
            lam += TWOPI;
         if (lam>PI)
            lam -= TWOPI;
         c1 = ws_trig[i];              /* cosine of latitude */
         c1 *= fac;
         s1 = ws_trig[i+1];            /* sine of latitude */
         s1 *= fac;
         in_view = FALSE;              /* get status of current point */
         if (latzero==TRUE)  {         /* equatorial globe view */
            zp = c1*cos (lam);
            if (lam>=-PI2 && lam<=+PI2)  {
               in_view = TRUE;
               h1 = HRADIUS * c1 * sin (lam);
               h2 = -VRADIUS * s1;
            }
         }
         else  {                       /* oblique earth view */
            c2 = cos (lam);
            zp = s1*s0 + c1*c0*c2;
            if (zp>=hp)  {             /* zp > hp => in view */
               in_view = TRUE;
               h1d = HRADIUS * c1 * sin (lam);
               h2d = -VRADIUS * (s1*c0 -c1*s0*c2);
               if (type!=GLOBE_TYPE)  {
                  fac2 = (facp/(facp-zp)) * scale;
                  h1d *= fac2;
                  h2d *= fac2;
               }
               h1 = h1d;
               h2 = h2d;
            }
         }
         if (first==TRUE)  {           /* get status of first point */
            first = FALSE;
            if (in_view==TRUE)  {      /* if first point is in view, */
               x = h1 + CENTERX;       /*   move pen to first point  */
               y = h2 + CENTERY;       /*   and plot it              */
               Move (rp, x, y);
               WritePixel (rp, x, y);
               h1prev = h1;
               h2prev = h2;
            }
            prev_in_view = in_view;    /* save status of first point */
            latprev = lat;
            lamprev = lam;
            zpprev = zp;
            continue;
         }
         if (in_view==TRUE)  {            /* if current point is in view, */
            if (prev_in_view==FALSE)  {   /*   but previous point was not */
               facz = zp / (zpprev-zp);   /*   in view, get rim point by  */
               latc = lat - (latprev-lat)*facz; /* linear interpolation   */
               lamc = lam - (lamprev-lam)*facz;
               dlat = fabs (lat-latprev);
               dlam = fabs (lam-lamprev);
               if ( fabs (latc-latprev)> dlat || /* if rim point not between */
                    fabs (latc-lat)    > dlat)   /*   current and previous   */
                  latc = (lat+latprev)/2.;       /*   point, use midpoint    */
               if ( fabs (lamc-lamprev)> dlam ||
                    fabs (lamc-lam)    > dlam )
                  lamc = (lam+lamprev)/2.;
               if (latzero==TRUE)  {
                  h1c = HRADIUS * cos (latc) * sin (lamc);
                  h2c = -VRADIUS * sin (latc);
               }
               else  {
                  c1 = cos (latc);
                  h1d = HRADIUS * c1 * sin (lamc);
                  h2d = -VRADIUS * (sin (latc)*c0 - c1*s0*cos (lamc));
                  if (type!=GLOBE_TYPE)  {
                     h1d *= fac3;
                     h2d *= fac3;
                  }
                  h1c = h1d;
                  h2c = h2d;
               }
               globe_rim_fix (&h1c, &h2c); /* correct the computed rim point */
               x = h1c + CENTERX;          /* move to rim point & plot it */
               y = h2c + CENTERY;
               Move (rp, x, y);
               WritePixel (rp, x, y);
               h1prev = h1c;
               h2prev = h2c;
            }
            if (h1!=h1prev || h2!=h2prev)  {
               x = h1 + CENTERX;       /* draw to current point */
               y = h2 + CENTERY;
               Draw (rp, x, y);
               Move (rp, x, y);
            }
            h1prev = h1;
            h2prev = h2;
         }
         else  {                       /* current point is out of view   */
            if (prev_in_view==TRUE)  { /* if previous point was in view, */
               facz = zp / (zpprev-zp);/*   get rim point by linear      */
               latc = lat - (latprev-lat)*facz; /* interpolation            */
               lamc = lam - (lamprev-lam)*facz;
               dlat = fabs (lat-latprev);
               dlam = fabs (lam-lamprev);
               if ( fabs (latc-latprev)> dlat || /* if rim point not between */
                    fabs (latc-lat)    > dlat)   /*   current and previous   */
                  latc = (lat+latprev)/2.;       /*   point, use midpoint    */
               if ( fabs (lamc-lamprev)> dlam ||
                    fabs (lamc-lam)    > dlam )
                  lamc = (lam+lamprev)/2.;
               if (latzero==TRUE)  {
                  h1c = HRADIUS * cos (latc) * sin (lamc);
                  h2c = -VRADIUS * sin (latc);
               }
               else  {
                  c1 = cos (latc);
                  h1d = HRADIUS * c1 * sin (lamc);
                  h2d = -VRADIUS * (c0*sin(latc) - c1*s0 * cos(lamc));
                  if (type!=GLOBE_TYPE)  {
                     h1d *= fac3;
                     h2d *= fac3;
                  }
                  h1c = h1d;
                  h2c = h2d;
               }
               globe_rim_fix (&h1c, &h2c); /* correct the computed rim point */ 
               x = h1c + CENTERX;          /* draw to rim point */
               y = h2c + CENTERY;
               Draw (rp, x, y);
               Move (rp, x, y);
            }
         }
         prev_in_view = in_view;       /* save status of current point */
         latprev = lat;
         lamprev = lam;
         zpprev = zp;
      }
   }
}

/* ============================================================= */

void globe_grid (type, lat0, lam0)     /* controls drawing globe grid */

double lat0, lam0;
long type;

{
   extern void globe_grid_plot();
   extern int limit_lam(), limit_lat();
   static double lat_interval = 20.;
   static double lam_interval = 30.;
   static double delta = 5.;
   char first;
   int k;
   long oldpen;
   double lat, lam, c0, s0, c1, s1, c2, s2, rlam, rlat;
   double hp, scale, fac3, dlat, dlam, ddelt;
   double lamp[2], latp[2];
   
   lat0 *= RAD;
   c0 = cos (lat0);
   s0 = sin (lat0);
   dlat = lat_interval;
   dlam = lam_interval;
   ddelt = delta;
   if (type==GLOBE_TYPE)  {              /* ordinary globe view */
      hp = 0.;
      scale = 1.;
      fac3 = 1.;
   }
   else  {                             /* orbital view */
      hp = etap;
      scale = sqrt (1.-etap*etap);
      fac3 = (facp/(facp-hp)) * scale;
      if (view_height<=1200.)  {
         dlat /= 4.;
         dlam /= 4.;
         ddelt /= 5.;
      }
   }
   oldpen = rp->FgPen;
   SetAPen (rp, BLACK);                /* grid lines in black */
   for (lat=80.; lat>=-80.; lat-=dlat)  {  /* lines of equal latitude */
      rlat = lat*RAD;
      if ((k=limit_lam (&lamp[0], rlat, lat0, hp))!=OK)
         continue;                     /* skip if entirely out of view */
      lamp[0] += lam0;
      lamp[1] += lam0;
      k = lamp[0]/ddelt - 1.;          /* express limits as multiple */
      lamp[0] = k*ddelt;               /*   of ddelt                 */
      k = lamp[1]/ddelt + 1.;
      lamp[1] = k*ddelt;
      c1 = cos (rlat);
      s1 = sin (rlat);
      first = TRUE;
      if (lat==0.)
         SetAPen (rp, WHITE);          /* draw equator in white */
      for (lam=lamp[0]; lam<=lamp[1]; lam+=ddelt)  {
         rlam = (lam-lam0)*RAD;
         if (rlam<-PI)
            rlam += TWOPI;
         if (rlam>+PI)
            rlam -= TWOPI;
         c2 = cos (rlam);
         s2 = sin (rlam);
         globe_grid_plot (rlat, rlam, c0, s0, c1, s1, c2, s2,
                          type, hp, scale, fac3, &first);
      }
      if (lat==0.)
         SetAPen (rp, BLACK);          /* reset pen to black */
   }
   for (lam=-180.; lam<+180.; lam+=dlam)  {  /* meridian circles */
      rlam = (lam-lam0)*RAD;
      if (rlam<-PI)
         rlam += TWOPI;
      if (rlam>+PI)
         rlam -= TWOPI;
      if ((k=limit_lat (&latp[0], lat0, rlam, hp))!=OK)
         continue;                     /* skip if entirely out of view */
      k = latp[0]/ddelt + 1.;          /* express limits as multiple */
      latp[0] = k*ddelt;               /*   of ddelt                 */
      k = latp[1]/ddelt - 1.;
      latp[1] = k*ddelt;
      if (latp[0]>=90.)                /* exclude North polar point */
         latp[0] = 90. - ddelt;
      if (latp[1]<=-90.)               /* exclude South polar point */
         latp[1] = -90. + ddelt;
      c2 = cos (rlam);
      s2 = sin (rlam);
      first = TRUE;
      if (lam==0. || lam==-180.)       /* prime meridian in white */
         SetAPen (rp, WHITE);
      for (lat=latp[0]; lat>=latp[1]; lat-=ddelt)  {
         rlat = lat*RAD;
         c1 = cos (rlat);
         s1 = sin (rlat);
         globe_grid_plot (rlat, rlam, c0, s0, c1, s1, c2, s2,
                          type, hp, scale, fac3, &first);
      }
      if (lam==0. || lam==-180.)
         SetAPen (rp, BLACK);          /* reset pen to black */
   }
   SetAPen (rp, oldpen);
}

/* ============================================================= */

void globe_grid_plot (lat, lam, c0, s0, c1, s1, c2, s2,
                      type, hp, scale, fac3, first)

double lat, lam, c0, s0, c1, s1, c2, s2;   /* draws globe grids */
long type;
double hp, scale, fac3;
char *first;

{
   extern void globe_rim_fix();
   static char prev_in_view;
   static short h1prev, h2prev;
   static double latprev, lamprev, zpprev;
   char in_view;
   short h1, h1c;                      /* x-dist. (pix) from center */
   short h2, h2c;                      /* y-dist. (pix) from center */
   long x, y;
   double lamc, dlam;                  /* longitude */
   double latc, dlat;                  /* latitude  */
   double zp, facz, h1d, h2d, fac2;
   
   in_view = FALSE;                    /* get status of current point */
   zp = s1*s0 + c1*c0*c2;
   if (zp>=hp)  {                      /* zp > hp => in view */
      in_view = TRUE;
      h1d = HRADIUS * c1 * s2;
      h2d = -VRADIUS * (s1*c0 - c1*s0*c2);
      if (type!=GLOBE_TYPE)  {
         fac2 = (facp/(facp-zp))*scale;
         h1d *= fac2;
         h2d *= fac2;
      }
      h1 = h1d;
      h2 = h2d;
   }
   if (*first==TRUE)  {                /* get status of first point */
      *first = FALSE;
      if (in_view==TRUE)  {            /* if first point is in view, */
         x = h1 + CENTERX;             /*   move pen to first point  */
         y = h2 + CENTERY;             /*   and plot it              */
         Move (rp, x, y);
         WritePixel (rp, x, y);
         h1prev = h1;
         h2prev = h2;
      }
      prev_in_view = in_view;          /* save status of first point */
      latprev = lat;
      lamprev = lam;
      zpprev = zp;
      return;
   }
   if (in_view==TRUE)  {               /* if current point is in view, */
      if (prev_in_view==FALSE)  {      /*   but previous point was not */
         facz = zp / (zpprev-zp);      /*   in view, get rim point by  */
         latc = lat - (latprev-lat)*facz; /* linear interpolation      */
         lamc = lam - (lamprev-lam)*facz;
         dlat = fabs (lat-latprev);
         dlam = fabs (lam-lamprev);
         if ( fabs (latc-latprev)> dlat || /* if rim point not between */
              fabs (latc-lat)    > dlat)   /*   current and previous   */
            latc = (lat+latprev)/2.;       /*   points, use midpoint   */
         if ( fabs (lamc-lamprev)> dlam ||
              fabs (lamc-lam)    > dlam )
            lamc = (lam+lamprev)/2.;
         c1 = cos (latc);
         h1d = HRADIUS * c1 * sin (lamc);
         h2d = -VRADIUS * (sin (latc)*c0 - c1*s0*cos (lamc));
         if (type!=GLOBE_TYPE)  {
            h1d *= fac3;
            h2d *= fac3;
         }
         h1c = h1d;
         h2c = h2d;
         globe_rim_fix (&h1c, &h2c);
         x = h1c + CENTERX;            /* move to rim point & plot it */
         y = h2c + CENTERY;
         Move (rp, x, y);
         WritePixel (rp, x, y);
         h1prev = h1c;
         h2prev = h2c;
      }
      if (h1!=h1prev || h2!=h2prev)  {
         x = h1 + CENTERX;             /* draw to current point */
         y = h2 + CENTERY;
         Draw (rp, x, y);
         Move (rp, x, y);
      }
      h1prev = h1;
      h2prev = h2;
   }
   else  {                             /* current point is out of view   */
      if (prev_in_view==TRUE)  {       /* if previous point was in view, */
         facz = zp / (zpprev-zp);      /*   get rim point by linear      */
         latc = lat - (latprev-lat)*facz; /* interpolation               */
         lamc = lam - (lamprev-lam)*facz;
         dlat = fabs (lat-latprev);
         dlam = fabs (lam-lamprev);
         if ( fabs (latc-latprev)> dlat || /* if rim point not between */
              fabs (latc-lat)    > dlat)   /*   current and previous   */
            latc = (lat+latprev)/2.;       /*   points, use midpoint   */
         if ( fabs (lamc-lamprev)> dlam ||
              fabs (lamc-lam)    > dlam )
            lamc = (lam+lamprev)/2.;
         c1 = cos (latc);         
         h1d = HRADIUS * c1 * sin (lamc);
         h2d = -VRADIUS * (c0*sin (latc) - c1*s0 * cos (lamc));
         if (type!=GLOBE_TYPE)  {
            h1d *= fac3;
            h2d *= fac3;
         }
         h1c = h1d;
         h2c = h2d;
         globe_rim_fix (&h1c, &h2c);
         x = h1c + CENTERX;            /* draw to rim point */
         y = h2c + CENTERY;
         Draw (rp, x, y);
         Move (rp, x, y);
      }
   }
   prev_in_view = in_view;             /* save status of current point */
   latprev = lat;
   lamprev = lam;
   zpprev = zp;
}

/* ============================================================= */

int globe_in_view (lat0, lam0, lat1, lam1, latv, lamv, hp)

double lat0, lam0, lat1, lam1;         /* checks if a segment is visible   */
double latv, lamv, hp;                 /*   in a globe view                */
                                       /* lat0, lam0 = coords of upper     */
                                       /*   left corner of segment (deg)   */
                                       /* lat1, lam1 = coords of lower     */
                                       /*   right corner of segment (deg)  */
                                       /* latv, lamv = coords of viewpoint */
                                       /*   (deg)                          */
                                       /* hp = modified view height        */
{
   extern int limit_lat(), limit_lam();
   int k;
   double lat[2], lam[2], latt, lamt, del1, del2, eps;
   double lat0p, lam0p, lat1p, lam1p, latvp, lamvp, lamtp;

   lat0p = lat0 * RAD;                 /* convert to radians */
   lat1p = lat1 * RAD;
   lam0p = lam0 * RAD;
   lam1p = lam1 * RAD;
   latvp = latv * RAD;
   lamvp = lamv * RAD;
   del1 = (lat0p-lat1p)/10.;           /* divide into horizontal strips */
   if (del1 > 0.)  {
      eps = del1/10.;
      for (latt=lat0p; latt>=lat1p-eps; latt-=del1)  {
         if ((k = limit_lam (lam, latt, latvp, hp)) == OK)  {
            lam[0] += lamv;
            lam[1] += lamv;
            if (lam[0] < -PI)
               lam[0] += TWOPI;
            if (lam[0] > +PI)
               lam[0] -= TWOPI;
            if (lam[1] < -PI)
               lam[1] += TWOPI;
            if (lam[1] > +PI)
               lam[1] -= TWOPI;
            if (lam[0] <= lam1 && lam[1] >= lam0)
               return (OK);
         }
      }
   }
   del2 = (lam1p-lam0p)/10.;           /* divide into vertical strips */
   if (del2 > 0.)  {
      eps = del2/10.;
      for (lamt=lam0p-lamvp; lamt<=lam1p-lamvp+eps; lamt+=del2)  {
         if ((lamtp = lamt) < -PI)
            lamtp += TWOPI;
         if (lamtp > +PI)
            lamtp -= TWOPI;
         if ((k = limit_lat (lat, latvp, lamtp, hp)) == OK)  {
            if (lat[0] >= lat1 && lat[1] <= lat0)
               return (OK);
         }
      }
   }
   if (del1 > 0. || del2 > 0.)
      return (NOT_OK);
   return (OK);                        /* let globe() do degenerate case */
}

/* ============================================================= */

void globe_rim_fix (h1, h2)            /* find nearest actual rim point */

short *h1, *h2;

{
   static int itmax = 20;
   short inc1, inc2;
   int i;
   long x, y, color;
   
   if ((*h1)>=0)                       /* right half */
      inc1 = +1;
   else                                /* left half */
      inc1 = -1;
   if ((*h2)>=0)                       /* bottom half */
      inc2 = +1;
   else                                /* top half */
      inc2 = -1;
   x = (*h1) + CENTERX + 5*inc1;       /* coords of test pixel */
   y = (*h2) + CENTERY + 5*inc2;
   for (i=0; i<itmax; ++i)  {          /* look for nearest black pixel */
      if ((color = ReadPixel (rp, x, y))==BLACK)
         break;
      x += inc1;
      y += inc2;
   }
   x -= inc1;
   y -= inc2;
   for (i=0; i<itmax; ++i)  {          /* look back for non-black one */
      if ((color = ReadPixel (rp, x, y)) != BLACK)
         break;
      x -= inc1;
      y -= inc2;
   }
   (*h1) = x - CENTERX;
   (*h2) = y - CENTERY;
}

/* ============================================================= */

void grid (type, lat0, lam0)           /* controls drawing of grids */

long type;
double lat0, lam0;

{
   extern void globe_grid();
   static double lat_interval = 20.;
   static double lam_interval = 30.;
   long oldpen, h1;
   double lam, lat, temp;

   oldpen = rp->FgPen;                 /* save original pen color */
   if (type!=FLAT_TYPE && type!=MERCATOR_TYPE)
      globe_grid (type, lat0, lam0);   /* draw grid for globe */
   else  {                             /* otherwise grid for flat */
                                       /*   or Mercator map       */
      SetAPen (rp, BLACK);             /* set grid color to black */
      for (lam=-180.; lam<=+180.; lam += lam_interval)  {
         h1 = lam*HFACTOR + CENTERX;
         Move (rp, h1, 0);
         Draw (rp, h1, WHEIGHT-1);
      }
      for (lat=80.; lat>=-80.; lat -= lat_interval)  {
         if (type==FLAT_TYPE)
            h1 = -lat*VFACTOR;
         else
            h1 = -log (tan((lat/2.+45.)*RAD)) * M_VFACTOR;
         h1 += CENTERY;
         Move (rp, 0, h1);
         Draw (rp, WWIDTH-1, h1);
      }
      SetAPen (rp, WHITE);             /* draw coordinate axes in white */
      Move (rp, CENTERX, 0);
      Draw (rp, CENTERX, WHEIGHT-1);
      Move (rp, 0, CENTERY);
      Draw (rp, WWIDTH-1, CENTERY);
   }
   SetAPen (rp, oldpen);               /* restore pen color */
}

/* ============================================================= */

int init_helpitems ()                  /* initializes help info */

{
   struct Gadget *gadgetpointer;
   short x, y, gadgetnum;
   int helpfile, ix, numrd, i;

   if ((helpbuffer = (char *) calloc (1, (unsigned) HELPBUFSIZE)) == NULL)
      return (NOT_OK);
   if ((helpfile = open (helpfilename, O_RDONLY)) < 0)  {
      free (helpbuffer);
      helpbuffer = NULL;
      return (NOT_OK);
   }
   if ((numrd = read (helpfile, helpbuffer, HELPBUFSIZE)) <= 0)  {
      close (helpfile);
      free (helpbuffer);
      helpbuffer = NULL;
      return (NOT_OK);
   }
   close (helpfile);
   ix = 1;
   gadgetlist[0].disp = 0;
   for (i=0; i<numrd-1; ++i)  {        /* get disp to each help entry */
      if (helpbuffer[i] == '\f')  {    /* replace formfeed with eol to */
         helpbuffer[i] = '\0';         /*   facilitate text display    */
         gadgetlist[ix].disp = i+1;
         ++ix;
         if (ix > NUMGADGETS)
            break;
      }
   }                                   /* now link the gadgets */
   for (gadgetnum=0; gadgetnum<NUMGADGETS; ++gadgetnum)  {
      x = HGADGETSTARTX + (gadgetnum&1) * (HWIDTH/2);
      y = HGADGETSTARTY + s->Font->ta_YSize + (gadgetnum/2) * 20;
      LinkGadget (&gadgetblocks[gadgetnum],
                  gadgetlist[gadgetnum].text,
                  &newhelpw, (long) x, (long) y);
      gadgetblocks[gadgetnum].Gadget.GadgetID = gadgetnum;
   }                                   /* put exit gadget at top */
   gadgetpointer = &gadgetblocks[NUMGADGETS-1].Gadget;
   gadgetpointer->TopEdge = s->Font->ta_YSize + 12;
   gadgetpointer->LeftEdge = (HWIDTH-gadgetpointer->Width) / 2;
   return (OK);
}

/* ============================================================= */

void init_requesters ()                /* initializes the requesters */

{
   static char title_file[] = "Select save file name:";
   unsigned char *s, *t;   

   InitRequester (&req);               /* initialize the requester */
   req.LeftEdge  = TLEFT;
   req.TopEdge   = TTOP;
   req.Width     = TWIDTH;
   req.Height    = THEIGHT;
   req.ReqGadget = &gad;
   req.ReqText   = &rtext;             /* assume text input */
   req.BackFill  = ORANGE;
   req.Flags     = 0;
   req.ReqBorder = &border_top;
   s = &user_text_input[0];            /* copy default text string */
   t = &default_text[0];
   while ((*s++ = *t++) != '\0')
            ;
   s = &user_double_input[0];          /* copy default orbital height */
   sprintf (default_double, "%.2lf", VIEW_HEIGHT);
   t = &default_double[0];
   while ((*s++ = *t++) != '\0')
            ;

   colorstruct.window = w;             /* window id for color requester */

   pathname[0]              = 0;       /* initialize file requester */
   filereq.Title            = title_file;  /* window title          */
   filereq.Dir              = directoryname;  /* space for dir name */
   filereq.File             = filename;    /* space for file name   */
   filereq.PathName         = pathname;    /* space for path name   */
   filereq.Window           = w;           /* display on map window */
   filereq.Flags            = FRQCACHINGM; /* cache the filenames   */
   filereq.Flags           |= FRQSAVINGM;  /* saving files          */
   filereq.dirnamescolor    = ORANGE;      /* dir names             */
   filereq.filenamescolor   = LT_YEL;      /* file names            */
   filereq.devicenamescolor = BLACK;       /* device names          */
   filereq.detailcolor      = BLUE;        /* window gadgets        */
   filereq.blockcolor       = DK_YEL;      /* title bar             */
   filereq.gadgettextcolor  = DK_YEL;      /* text in gadgets       */
   filereq.textmessagecolor = DK_YEL;      /* text in title         */
   filereq.stringnamecolor  = DK_YEL;      /* prompts               */
   filereq.stringgadgetcolor= LT_BL;       /* borders               */
   filereq.boxbordercolor   = LT_BL;
   filereq.gadgetboxcolor   = LT_BL;

   trs.MiddleText = "OK";              /* initialize items in */
   trs.PositiveText = 0;               /*   help requester    */
   trs.NegativeText = 0;
   trs.KeyMask = 0;
   trs.textcolor = ORANGE;
   trs.detailcolor = BLUE;
   trs.blockcolor = DK_YEL;
   trs.versionnumber = REQVERSION;
   trs.Timeout = 0;
   trs.AbortMask = 0;
   trs.rfu1 = 0;
}

/* ============================================================= */

int limit_lam (lam, lat, lat0, etap)   /* computes limits on longitude */
                                       /*   for constant latitude      */
double *lam, lat, lat0, etap;

{
   double alpha;
   
   alpha = (etap-sin(lat)*sin(lat0)) / (cos(lat)*cos(lat0));
   if (alpha<=-1.)  {                  /* negative => lamda covers */
      lam[0] = -180.;                  /*   entire hemisphere      */
      lam[1] = +180.;
      return (OK);
   }
   else if (alpha>=+1.)                /* positive => nothing in view */
      return (NOT_OK);
   else  {                             /* otherwise, compute limits */
      lam[0] = -acos (alpha)/RAD;
      lam[1] = -lam[0];
      return (OK);
   }
}

/* ============================================================= */

int limit_lat (lat, lat0, lam, etap)   /* computes limits on latitude */
                                       /*   for constant longitude    */
double *lat, lat0, lam, etap;

{
   double radical, a, b, sum, fac1;
   
   a = sin (lat0);
   b = cos (lat0) * cos (lam);
   sum = a*a + b*b;
   if ((radical = sum - etap*etap) <= 0.) /* no real solutions */
      return (NOT_OK);
   else  {                             /* two real solutions       */
      radical = sqrt (radical);        /* solve quadratic equation */
      fac1 = (a*etap + b*radical)/sum;
      lat[0] = asin (fac1)/RAD;
      fac1 = (a*etap - b*radical)/sum;
      lat[1] = asin (fac1)/RAD;
      if (lat[0]<lat[1])  {            /* put in correct order */
         b = lat[0];
         lat[0] = lat[1];
         lat[1] = b;
      }
      if (a>etap)                      /* check North pole */
         lat[0] = 90.;
      if (a<-etap)                     /* check South pole */
         lat[1] = -90.;
      return (OK);
   }
}

/* ================================================================ */

void loadmappic ()                     /* loads initial flat map */

{
   extern IFFP LoadIFFToWindow();
   extern void savemappic();
   IFFP Iresult;

   IlbmFrame.iWindow = w;
   IlbmFrame.iScreen = s;
   IlbmFrame.iUserFlags = 0;
   got_flat_map = NOT_OK;
   if ((Iresult = LoadIFFToWindow (flatmapfile, &IlbmFrame)) == IFF_OKAY)  {
      got_flat_map = OK;
      savemappic ();
      ShowTitle (s, TRUE);
      title_toggle = TRUE;
   }
}

/* ================================================================ */

int printmap (win)                     /* prints displayed map */

struct Window *win;

{
   struct IODRPReq *ioreq;
   struct MsgPort *printerport;
   struct IntuiMessage *msg;
   static char printerportname[] = "drmapdump";
   static char printerdevice[]   = "printer.device";
   static char print_canx[]      = "Printing canceled. Please wait ...";
   static char print_error[]     = "Printer error ... printing aborted";
   char abort;
   int returncode;
   unsigned long signal, winsig, printsig;

   abort = NULL;
   ModifyIDCMP (win, MOUSEBUTTONS);    /* disable all but mousebuttons */
   if ((printerport = CreatePort (printerportname, 0)) != NULL)  {
      if ((ioreq = (struct IODRPReq *) CreateExtIO (printerport,
                   sizeof (struct IODRPReq))) != NULL)  {
         if ( ! (OpenDevice (printerdevice, 0,
                             (struct IORequest *) ioreq, 0)))  {
            ioreq->io_Command = CMD_FLUSH;
            DoIO ((struct IORequest *) ioreq);
            if (ioreq->io_Error == PDERR_NOERR)  {
               winsig = 1 << win->UserPort->mp_SigBit;
               printsig = 1 << printerport->mp_SigBit;
               ioreq->io_Command = PRD_DUMPRPORT;
               ioreq->io_RastPort = rp;
               ioreq->io_ColorMap = vp->ColorMap;
               ioreq->io_Modes = VMODE;  /* vp->Modes;*/
               ioreq->io_SrcX = 0;
               ioreq->io_SrcY = 0;
               ioreq->io_SrcWidth = WWIDTH;
               ioreq->io_SrcHeight = WHEIGHT;
               ioreq->io_DestCols = 0;
               ioreq->io_DestRows = 0;
               ioreq->io_Special = SPECIAL_ASPECT|SPECIAL_FULLROWS;

               SendIO ((struct IORequest *) ioreq);
               while (abort == NULL)  {
                  signal = Wait (printsig | winsig);
                  if (signal & winsig)  {
                     while ((msg = (struct IntuiMessage *)
                                   GetMsg (win->UserPort)) != NULL)  {
                        if (msg->Code == MENUDOWN)  {
                           returncode = ABORT;
                           SetWindowTitles (w, 0, print_canx);
                           abort |= U_ABORT;
                        }
                        ReplyMsg ((struct IntuiMessage *) msg);
                     }
                  }
                  if (signal & printsig)  {
                     if (ioreq->io_Error != PDERR_NOERR)  {
                        returncode = NOT_OK;
                        SetWindowTitles (w, 0, print_error);
                        abort |= P_ABORT;
                     }
                     else
                        abort |= NOABORT;  /* ahem, not aborted, but ended OK */
                  }
               }
               if (abort == U_ABORT)  {  /* WAIT A MOMENT, crashes if */
                                         /*   printer.device is being */
                                         /*   loaded and printing is  */
                                         /*   canceled                */
                  DisplayBeep (0);
                  Delay (8 * TICKS_PER_SECOND);
                  AbortIO ((struct IORequest *)ioreq);
                  WaitIO ((struct IORequest *)ioreq);
               }
               else if (abort & NOABORT)
                  returncode = OK;
               while ((struct MsgPort *) GetMsg (printerport))
                  ;
            }
            CloseDevice ((struct IORequest *) ioreq);
         }
         DeleteExtIO ((struct IORequest *) ioreq);
      }
      DeletePort (printerport);
   }
   ModifyIDCMP (win, IDCMPFLAGS);        /* restore normal events */
   return (returncode);
}

/* ============================================================= */

int readmap (ws, fname, num)                /* reads map files into memory */

short *ws;
char *fname;
int num;

{
   int ibin, disp, num_read;
   
   if ((ibin = open (fname, O_RDONLY)) < 0)
      return (NOT_OK);
   disp = 0;
   while ((num_read = read (ibin, (char*) &ws[disp], 16000)) > 0)
      disp += num_read / sizeof(short);
   close (ibin);
   if (disp != num)
      return (NOT_OK);
   return (OK);
}

/* ============================================================= */

void savemappic ()                     /* moves map from screen to */
                                       /*   temporary bitmap       */
{
   ShowTitle (s, FALSE);               /* don't save window title bar */
   BltBitMapRastPort (rp->BitMap, 0, 0, &map_rp, 0, 0, WWIDTH, WHEIGHT,
                      0xc0);
   ShowTitle (s, title_toggle);
}

/* ================================================================ */

int save_to_disk ()                    /* saves map to disk */

{
   extern int FileRequester();
   extern IFFP SaveWindowToIFF();
   int result;
   IFFP iffresult;

   if ( (result = FileRequester (&filereq)) != TRUE) /* get file name */
      return (NOT_OK);
   ShowTitle (s, FALSE);               /* turn off title in saved file */
   SetPointer (w, waiter, WAITER_SIZE/4-2, 16,
               WAITER_X_OFFSET, WAITER_Y_OFFSET);
   iffresult = SaveWindowToIFF (pathname, w); /* save map to disk */
   SetPointer (w, arrow, ARROW_SIZE/4-2, 16,
               ARROW_X_OFFSET, ARROW_Y_OFFSET);
   ShowTitle (s, title_toggle);
   if (iffresult != IFF_OKAY)          /* set return code */
      return (NOT_OK);
   else
     return (OK);
}

/* ============================================================= */

void shadow ()                         /* makes shadowed screens */
                                       /* shadows all colors except */
                                       /*   blue and black          */
{
   static char first = NOT_OK;
   static int blackcolor[DEPTH], disp;
   static int bluecolor[DEPTH];
   static unsigned int bitval[] = {1, 2, 4, 8, 16, 32, 64, 128};
   static short ytable[WHEIGHT];       /* offsets from beginning of */
                                       /*   each screen row         */
   int modb0, modb1, color[DEPTH];
   int bcolor[DEPTH];
   int j, k, k1, k2, m, bitlast;
   int test, t;
   unsigned int byte[DEPTH];
   unsigned int byteb0[DEPTH], byteb1[DEPTH];
 
   if (first==NOT_OK)  {               /* initialize color values once */
      first = OK;
      test = BLACK;
      blackcolor[0] = test&bitval[0];
      for (k=1; k<DEPTH; ++k)
         blackcolor[k] = (test&bitval[k]) >> k;
      test = BLUE;
      bluecolor[0] = test&bitval[0];
      for (k=1; k<DEPTH; ++k)
         bluecolor[k] = (test&bitval[k]) >> k;
      ytable[0] = 0;                   /* initialize screen offsets */
      for (k=1; k<WHEIGHT; ++k)
         ytable[k] = ytable[k-1] + ROWOFFSET;
      disp = SHADOW_DISP * ROWOFFSET;  /* offset to shadowed row */
   }
   for (k=ytable[0]; k<ytable[WHEIGHT-SHADOW_DISP-1]; k+=ROWOFFSET)  {
                                       /* do each row                  */
      k2 = k + disp;                   /* displacement to shadowed row */
      for (j=0; j<ROWOFFSET; ++j)  {   /* do each byte in row          */
         for (k1=0; k1<DEPTH; ++k1)  {
            byte[k1] = bp[k1][k+j];    /* current row */
            byteb0[k1] = bp[k1][k2+j]; /* shadowed row */
            byteb1[k1] = bp[k1][k2+j+1]; /* shadowed row, adjacent byte */
         }                             /* if last byte in row, don't   */
                                       /*   need adjacent bytes        */
         ((j<ROWOFFSET-1) ? (bitlast=0) : (bitlast=SHADOW_DISP));
         modb0 = NOT_OK;
         modb1 = NOT_OK;
         for (m=7; m>=bitlast; --m)  { /* check each bit, left to right */
            for (k1=0; k1<DEPTH; ++k1)  {
               t = byte[k1];
               color[k1] = BITVAL(t, m);  /* current pixel color */
            }
            test = color[DEPTH-1];     /* build color value */
            for (k1=DEPTH-2; k1>=0; --k1)
               test = (test<<1) | color[k1];
            if (test != BLACK && test != BLUE)  {
               if (m>SHADOW_DISP-1)  {          /* get color of pixel */
                  for (k1=0; k1<DEPTH; ++k1)  { /*   in shadowed row  */
                     t = byteb0[k1];
                     bcolor[k1] = BITVAL(t, m-SHADOW_DISP);
                  }
               }
               else  {                 /* use adjacent byte */
                  for (k1=0; k1<DEPTH; ++k1)  {
                     t = byteb1[k1];
                     bcolor[k1] = BITVAL(t, m+8-SHADOW_DISP);
                  }
               }
               test = OK;
               for (k1=0; k1<DEPTH; ++k1)  {  /* check if it is blue */
                  if (bcolor[k1] != bluecolor[k1])  {
                     test = NOT_OK;
                     break;
                  }
               }
               if (test == OK)  {      /* if blue, set color to black */
                  if (m>SHADOW_DISP-1)  {
                     for (k1=0; k1<DEPTH; ++k1)  {
                        t = byteb0[k1];
                        BITSTORE(t, m-SHADOW_DISP, blackcolor[k1]);
                        byteb0[k1] = t;
                     }
                     modb0 = OK;
                  }
                  else  {              /* use adjacent byte */
                     for (k1=0; k1<DEPTH; ++k1)  {
                        t = byteb1[k1];
                        BITSTORE(t, m+8-SHADOW_DISP, blackcolor[k1]);
                        byteb1[k1] = t;
                     }
                     modb1 = OK;
                  }
               }
            }
         }                             /* end bit test */
         if (modb0==OK)  {             /* restore only modified bytes */
            for (k1=0; k1<DEPTH; ++k1)
               bp[k1][k2+j] = byteb0[k1];
         }
         if (modb1==OK)  {
            for (k1=0; k1<DEPTH; ++k1)
               bp[k1][k2+j+1] = byteb1[k1];
         }
      }                                /* end of row */
   }                                   /* last row   */
}

/* ============================================================= */

void showmappic ()                     /* moves map from temporary */
                                       /*   bitmap to screen       */
{
   extern void fullmap ();

   if (got_flat_map != OK)             /* check if have already */
      fullmap (map, FLAT_TYPE);        /*   saved a flat map    */
   else
      BltBitMapRastPort (&map_bitmap, 0, 0, rp, 0, 0, WWIDTH, WHEIGHT, 0xc0);
}

/* ============================================================= */

void stars ()                          /* draws stars into black background */

{
   static char init = FALSE;
   static unsigned int seed;           /* seed for rand() */
   static int nmax = 150;              /* max number of stars */
   unsigned int t;
   int nstars, x, y;
   long oldpen, color;

   if (init == FALSE)  {               /* initialize seed value */
      init = TRUE;
      seed = &seed;
      srand (seed);
   }
   oldpen = rp->FgPen;
   SetAPen (rp, WHITE);
   nstars = 0;
   while (nstars < nmax)  {
      t = rand();
      x = t % WWIDTH + 1;
      t = rand();
      y = t % WHEIGHT + 1;
      if ((color = ReadPixel (rp, x, y)) == BLACK)  {
         WritePixel (rp, x, y);
         ++nstars;
      }
   }
   SetAPen (rp, oldpen);
}
