/*****************************************************************************/
/*
                                 PlotSpi.c

This little (and simple-minded) plotting package was developed to support the 
HyperSpi application.  It allows simple, two-colour (black on white) graphs to 
be created in memory (as pixmaps).  The bit-manipulation routines are long-
word aligned (in an attempt to make AXP systems efficient).

The function parameter 'PlotOperation' specifies whether the the pixel will be 
turned on, turned off or changed to the complement of the existing pixel 
(logical exclusive-OR).


BUILD_DETAILS
-------------
See BUILD_HYPERSPI.COM


VERSION HISTORY
---------------
01-AUG-97  MGD  general maintenance in line with hyperspi.c
20-JUN-95  MGD  initial development
*/
/*****************************************************************************/

/* standard C header files */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>

/***************/
/* definitions */
/***************/

#define boolean int
#define true 1
#define false 0

#define VMSok(x) ((x) & STS$M_SUCCESS)
#define VMSnok(x) !(((x) & STS$M_SUCCESS))

#ifdef __VMS
    /* vaxc$errno will be an extern int */
#   define ERROR_VALUE vaxc$errno
#else
#   define ERROR_VALUE errno
#endif

#define PLOT_ON 0
#define PLOT_OFF 1
#define PLOT_XOR 2

const int  LongBits = sizeof(unsigned long) * 8;

boolean  DoPlotDiagnostic;

int  PlotAreaX,
     PlotAreaY,
     PlotScaleX,
     PlotScaleY;

char *GraphPtr;

extern boolean  Debug;
extern int  RecordSampleRate,
            FromHour,
            SizeOfPlotX,
            SizeOfPlotY,
            SizeOfMarginX,
            SizeOfMarginY,
            XMag,
            YMag;
extern char  Utility[];

/*****************************************************************************/
/*
Allocate enough memory to accomodate the X by Y graph, pointed to by global 
storage 'GraphPtr'.  Set global storage 'PlotAreaX' and 'PlotAreaY' to the 
respective sizes of the graph (note that the actual maximum plot point of 
each of these is one less).
*/ 

PlotAllocate
(
int XSize,
int YSize
)
{
   int  BytesRequired;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "PlotAllocate() %d,%d\n", XSize, YSize);

   PlotAreaX = XSize;
   PlotAreaY = YSize;
   /* bits in plot area, divided by bits/per byte, + rounding error allowance */
   BytesRequired = ((PlotAreaX * PlotAreaY) / 8) + 4;
   if (Debug)
      fprintf (stdout, "BytesRequired: %d long-words: %d\n",
               BytesRequired, BytesRequired/4);
   if ((GraphPtr = (char*)malloc (BytesRequired)) == NULL)
      FatalError ("MALLOC", "error allocating graph memory", ERROR_VALUE);
   memset (GraphPtr, 0, BytesRequired);
}

/*****************************************************************************/
/*
Place a box surrounding the area specified by 'XSize' and 'YSize' offset from 
0,0 by the 'XOrigin' and 'YOrigin'.  Note that this box is actually outside 
the area specified by 'XSize' and 'YSize'.  Place graduation points alongside
each axis.
*/ 

PlotAxes (int PlotOperation)

{
   register int  hr, cnt, x, y;

   int  XSize,
        YSize,
        YGraduation;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "PlotAxes()\n");

   XSize = SizeOfPlotX + XMag - 1;
   YSize = SizeOfPlotY + YMag - 1;

   PlotLineX (PlotOperation, -1, XSize+1, -1, SizeOfMarginX, SizeOfMarginY);
   PlotLineX (PlotOperation, -1, XSize+1, YSize+1,
              SizeOfMarginX, SizeOfMarginY);
   PlotLineY (PlotOperation, -1, YSize+1, -1, SizeOfMarginX, SizeOfMarginY);
   PlotLineY (PlotOperation, -1, YSize+1, XSize+1,
              SizeOfMarginX, SizeOfMarginY);

   /* place graduation along X axis */
   hr = FromHour + 1;
   x = (cnt = 1) * 60 * XMag / RecordSampleRate;
   while (x < XSize+1)
   {
      for (y = -5; y < -1; y++)
         PlotPixel (PlotOperation, x, y, SizeOfMarginX, SizeOfMarginY);
      if (hr > 23)
      {
         y = -12;
         PlotPixel (PlotOperation, x-1, y, SizeOfMarginX, SizeOfMarginY);
         PlotPixel (PlotOperation, x+1, y, SizeOfMarginX, SizeOfMarginY);
         for (; y < -1; y++)
            PlotPixel (PlotOperation, x, y, SizeOfMarginX, SizeOfMarginY);
         hr = 0;
      }
      else
      if (hr == 12)
      {
         for (y = -10; y < -1; y++)
            PlotPixel (PlotOperation, x, y, SizeOfMarginX, SizeOfMarginY);
      }
      else
      if (!(hr % 6))
      {
         for (y = -8; y < -1; y++)
            PlotPixel (PlotOperation, x, y, SizeOfMarginX, SizeOfMarginY);
      }
      hr++;
      x = ++cnt * 60 * XMag / RecordSampleRate;
   }

   if (PlotScaleY % 2)
      YGraduation = 20 * YMag;
   else
   if (PlotScaleY == 50)
      YGraduation = 20 * YMag;
   else
   if (PlotScaleY == 250)
      YGraduation = 20 * YMag;
   else
   if (PlotScaleY == 500)
      YGraduation = 20 * YMag;
   else
   if (PlotScaleY == 2500)
      YGraduation = 20 * YMag;
   else
   if (PlotScaleY == 5000)
      YGraduation = 20 * YMag;
   else
      YGraduation = 10 * YMag;

   /* place graduation along Y axis */
   for (y = YGraduation; y < YSize+1; y += YGraduation)
      for (x = -6; x < -1; x++)
         PlotPixel (PlotOperation, x, y, SizeOfMarginX, SizeOfMarginY);

   if (PlotScaleY % 2 ||
       PlotScaleY == 250 ||
       PlotScaleY == 2500) return;

   /* place intermediate graduation along Y axis */
   for (y = (YGraduation/2); y < YSize+1; y += YGraduation)
      for (x = -4; x < -1; x++)
         PlotPixel (PlotOperation, x, y, SizeOfMarginX, SizeOfMarginY);
}

/*****************************************************************************/
/*
*/

PlotPixel
(
int PlotOperation,
int AtX,
int AtY,
int XOrigin,
int YOrigin
)
{
   register int  Bit,
                 LongWord;

   /*********/
   /* begin */
   /*********/

   if (Debug)
      fprintf (stdout, "PlotPixel() %d,%d %d,%d\n",
               AtX, AtY, XOrigin, YOrigin);

   AtX += XOrigin;
   AtY += YOrigin;
   if (AtX < 0)
   {
      PlotDiagnostic ("PlotPixel() X coordinate less than zero", AtX);
      AtX = 0;
   }
   if (AtX >= PlotAreaX)
   {
      PlotDiagnostic ("PlotPixel() X coordinate out of range", AtX);
      AtX = PlotAreaX-1;
   }
   if (AtY < 0)
   {
      PlotDiagnostic ("PlotPixel() Y coordinate less than zero", AtY);
      AtY = 0;
   }
   if (AtY >= PlotAreaY || AtY < 0)
   {
      PlotDiagnostic ("PlotPixel() Y coordinate out of range", AtY);
      AtY = PlotAreaY-1;
   }
   AtY = PlotAreaY - AtY - 1;
   LongWord = (Bit = AtX + (AtY * PlotAreaX)) / LongBits;
   Bit %= LongBits;
   if (!PlotOperation)
      ((unsigned long*)GraphPtr)[LongWord] |= 1 << Bit;
   else
   if (PlotOperation == PLOT_XOR)
      ((unsigned long*)GraphPtr)[LongWord] ^= 1 << Bit;
   else
      ((unsigned long*)GraphPtr)[LongWord] &= ~(1 << Bit);
}


/*****************************************************************************/
/*
*/

PlotLineX
(
int PlotOperation,
int FromX,
int ToX,
int AtY,
int XOrigin,
int YOrigin
)
{
   register int  AtX,
                 Bit,
                 CurrentLongWord,
                 LongWord;
   register unsigned long  Pixels;

   /*********/
   /* begin */
   /*********/

   if (Debug)
      fprintf (stdout, "PlotLineX() FromX: %d ToX: %d AtY: %d %d,%d\n",
               FromX, ToX, AtY, XOrigin, YOrigin);

   FromX += XOrigin;
   ToX += XOrigin;
   AtY += YOrigin;
   if (FromX < 0)
   {
      PlotDiagnostic ("PlotLineX() start X coordinate less than zero", FromX);
      FromX = 0;
   }
   if (FromX >= PlotAreaX)
   {
      PlotDiagnostic ("PlotLineX() start X coordinate out of range", FromX);
      FromX = PlotAreaX-1;
   }
   if (ToX < 0)
   {
      PlotDiagnostic ("PlotLineX() end X coordinate less than zero", ToX);
      ToX = 0;
   }
   if (ToX >= PlotAreaX)
   {
      PlotDiagnostic ("PlotLineX() end X coordinate out of range", ToX);
      ToX = PlotAreaX-1;
   }
   if (AtY < 0)
   {
      PlotDiagnostic ("PlotLineX() Y coordinate less than zero", AtY);
      AtY = 0;
   }
   if (AtY >= PlotAreaY)
   {
      PlotDiagnostic ("PlotLineX() Y coordinate out of range", AtY);
      AtY = PlotAreaY-1;
   }

   if (FromX > ToX)
   {
      int  Temp;

      Temp = FromX;
      FromX = ToX;
      ToX = Temp;
   }

   CurrentLongWord = -1;
   AtY = PlotAreaY - AtY - 1;
   for (AtX = FromX; AtX <= ToX; AtX++)
   {
      LongWord = (Bit = AtX + (AtY * PlotAreaX)) / LongBits;
      Bit %= LongBits;
      if (CurrentLongWord != LongWord)
      {
         if (CurrentLongWord != -1)
            ((unsigned long*)GraphPtr)[CurrentLongWord] = Pixels;
         Pixels = ((unsigned long*)GraphPtr)[CurrentLongWord = LongWord];
      }
      if (!PlotOperation)
         Pixels |= 1 << Bit;
      else
      if (PlotOperation == PLOT_XOR)
         Pixels ^= 1 << Bit;
      else
         Pixels &= ~(1 << Bit);
   }

   if (CurrentLongWord != -1)
   {
      if (Debug) fprintf (stdout, "storing: %d\n", CurrentLongWord);
      ((unsigned long*)GraphPtr)[CurrentLongWord] = Pixels;
   }
}


/*****************************************************************************/
/*
*/

PlotLineY
(
int PlotOperation,
int FromY,
int ToY,
int AtX,
int XOrigin,
int YOrigin
)
{
   register int  Bit,
                 AtY,
                 LongWord;

   /*********/
   /* begin */
   /*********/

   if (Debug)
      fprintf (stdout, "PlotLineY() FromY: %d ToY: %d AtX: %d %d,%d\n",
               FromY, ToY, AtX, XOrigin, YOrigin);

   FromY += YOrigin;
   ToY += YOrigin;
   AtX += XOrigin;
   if (FromY < 0)
   {
      PlotDiagnostic ("PlotLineY() start Y coordinate less than zero", FromY);
      FromY = 0;
   }
   if (FromY >= PlotAreaY)
   {
      PlotDiagnostic ("PlotLineY() start Y coordinate out of range", FromY);
      FromY = PlotAreaY-1;
   }
   if (ToY < 0)
   {
      PlotDiagnostic ("PlotLineY() end Y coordinate less than zero", ToY);
      ToY = 0;
   }
   if (ToY >= PlotAreaY)
   {
      PlotDiagnostic ("PlotLineY() end Y coordinate out of range", ToY);
      ToY = PlotAreaY-1;
   }
   if (AtX < 0)
   {
      PlotDiagnostic ("PlotLineY() X coordinate less than zero", AtX);
      AtX = 0;
   }
   if (AtX >= PlotAreaX)
   {
      PlotDiagnostic ("PlotLineY() X coordinate out of range", AtX);
      AtX = PlotAreaX-1;
   }

   if (FromY > ToY)
   {
      int  Temp;

      Temp = FromY;
      FromY = ToY;
      ToY = Temp;
   }

   FromY = PlotAreaY - FromY - 1;
   ToY = PlotAreaY - ToY - 1;
   for (AtY = FromY; AtY >= ToY; AtY--)
   {
      LongWord = (Bit = AtX + (AtY * PlotAreaX)) / LongBits;
      Bit %= LongBits;
      if (!PlotOperation)
         ((unsigned long*)GraphPtr)[LongWord] |= 1 << Bit;
      else
      if (PlotOperation == PLOT_XOR)
         ((unsigned long*)GraphPtr)[LongWord] ^= 1 << Bit;
      else
         ((unsigned long*)GraphPtr)[LongWord] &= ~(1 << Bit);
   }
}

/*****************************************************************************/
/*
*/

PlotDiagnostic
(
char *Description,
int Value
)
{
   if (!DoPlotDiagnostic) return;
   fprintf (stdout, "DIAGNOSTIC; %s: %d\n", Description, Value);
}

/*****************************************************************************/
/*
*/

FatalError
(
char *Identifier,
char *Description,
int ErrorValue
)
{
   /*********/
   /* begin */
   /*********/

#ifdef __VMS
   fprintf (stdout, "%%%s-E-%s, %s\n", Utility, Identifier, Description);
   if (ErrorValue)
      exit (ErrorValue);
   else
      exit (2 | 0x10000000);
#else
   if (ErrorValue)
      fprintf (stdout, "%s: %s: %s\n",
               Utility, Description, strerror(ErrorValue));
   else
      fprintf (stdout, "%s: %s\n", Utility, Description);
   exit (1);
#endif
}

/*****************************************************************************/
