/*************************************************************
 *
 *  draw.c
 *
 *  Grafische Routinen zu FunktZeich.
 *  funcdraw() malt die Funktion selbst, axisdraw() die
 *  Achsen mit Beschriftungen.
 *
 *  Peter Gober, 15-May-88
 *               22-May-88  ftoapg-Routine
 *               12-Oct-88  ftopg neu geschrieben,
 *                          "Ticks" ausgebaut
 *
 *************************************************************/


#include <exec/types.h>
#include <graphics/rastport.h>
#include <math.h>
#include <functions.h>

/*
 *  Puffer für den IPN-Code, den fcc() ausgibt
 */

int rules[128];
float constants[64];

funcdraw(formula, rport, width, height, minx, maxx, miny, maxy)
char *formula;
register struct RastPort *rport;
int width, height;
double minx, maxx, miny, maxy;
{
   int error;
   int penup = 1;                 /* Stift angehoben */
   int xw,yw;                     /* Bildschirmkoordinaten */
   float ywf;
   float x, y;                    /* tatsächliche Koordinaten */
   float deltax = (maxx - minx) / (width - 1);
   float maxminy = maxy - miny;

   /*
    *  Formel in IPN-Code übersetzen
    */

   if (fcc(formula, rules, constants))
      return(-1);

   if (maxx <= minx || maxy <= miny)
      return(-1);

   /*
    *  Bildschirmkoordinaten durchlaufen, von jedem Punkt y berechnen
    *  und zeichnen
    */

   x = minx;
   for (xw = 0; xw <= width - 1; xw++)
   {
      error = fci(rules, constants, x, &y);

      /*
       *  Umrechnung in Fenster-Koordinaten
       */

      ywf = (float) (height - 1) * (1.0 - (y - miny) / maxminy);

      /*
       *  Die clipping-Routinen des Betriebssystems versagen irgendwo,
       *  Vorbeugung gegen int-Überlauf (die Punkte werden etwas näher
       *  heran geschoben, was die Zeichnung nicht beeinflußt)
       */

      if (ywf < -5000.0) yw = -5000;
      else if (ywf > 5000.0) yw = 5000;
      else yw = (int) ywf;

      if (!error)
      {
         /*
          *  Zeichnen!
          */

         if (penup)
            Move(rport, (long) xw, (long) yw);
         else
            Draw(rport, (long) xw, (long) yw);
         penup = 0;
      }
      else
      {
         /*
          *  Wenn Fehler: vertikalen Strich zeichnen, Stift anheben
          *  (man könnte den Strich evt. auch in einer anderen Farbe zeichnen)
          */

         Move(rport, (long) xw, 0L);
         Draw(rport, (long) xw, (long) (height - 1));
         penup = 1;
      }

      x += deltax;
   }

   return(0);
}

axisdraw(rport, width, height, minx, maxx, miny, maxy, dx, dy)
register struct RastPort *rport;
int width, height;
double minx, maxx, miny, maxy;
double dx, dy;                          /* Abstand der Zahlen */
{
   int xposy, yposx;                    /* Position der Achsen (xposy =
                                           "x-Position der y-Achse")  */
   float x,y;
   int xw,yw;
   char s[20];                          /* Puffer für Zahlen in ASCII */

   /*
    *  Position der Achsen
    */

   y = (float) (height - 1) * (1.0 + miny / (maxy - miny));

   if (y < 0.0) yposx = 0;             /* Korrektur, falls zu weit ab vom
                                          Schuß */
   else if (y > (float) (height - 21)) yposx = height - 21;
   else yposx = (int) y;

   x = (float) (width - 1) * (- minx / (maxx - minx));

   if (x < 79.0) xposy = 79;
   else if (x > (float) (width - 1)) xposy = width - 1;
   else xposy = (int) x;

   /*
    *  Achsen selbst zeichnen
    */

   Move(rport, 0L, (long) yposx);
   Draw(rport, (long) (width - 1), (long) yposx);

   Move(rport, (long) xposy, (long) (height - 1));
   Draw(rport, (long) xposy, 0L);

   /*
    *  Zahlenmarkierungen
    */

   if (dx != 0.0)
   {
      for (x = minx; x <= maxx; x+= dx)
      {
         xw = (width - 1) * (x - minx) / (maxx - minx);
         Move(rport, (long) xw, (long) (yposx - 2));
         Draw(rport, (long) xw, (long) (yposx + 2));
         ftoapg(x, s);
         Move(rport, (long) (xw - TextLength(rport, s, (long) strlen(s)) / 2),
                     (long) (yposx + rport->TxBaseline + 4));
         Text(rport, s, (long) strlen(s));
      }
   }

   if (dy != 0.0)
   {
      for (y = miny; y <= maxy; y+= dy)
      {
         yw = (height - 1) * (1 - (y - miny) / (maxy - miny));
         Move(rport, (long) (xposy - 4), (long) yw);
         Draw(rport, (long) (xposy + 4), (long) yw);
         ftoapg(y, s);
         Move(rport, (long) (xposy - TextLength(rport, s,
                     (long) strlen(s)) - 8),
                     (long) (yw + rport->TxBaseline / 2));
         Text(rport, s, (long) strlen(s));
      }
   }

   return(0);
}

/*
 *  ftoapg() konvertiert eine double-float-Zahl nach ASCII und benutzt dabei
 *  das Format, das volle Genauigkeit bei geringstem Platzbedarf bietet. Im
 *  Gegensatz zum %g-Format werden überflüssige 0en geöscht. Die entspricht
 *  dem Format, das von Taschenrechnern bekannt ist.
 */

ftoapg(val, buf)
register double val;
register char *buf;
{
   char *strchr();
   register char *exppos, *pos;
   int only0flag;

   ftoa(val, buf, 4, 2);                /* %g-Format, 4 Stellen */

   if (exppos = strchr(buf, 'e'))       /* e für Exponent suchen */
   {
      /*
       *  Eine Zahl mit Exponent ist herausgekommen,
       *  unwichtige 0en nach dem . löschen
       */

      only0flag = 1;                    /* signalisiert: wir haben nur 0en */

      pos = exppos - 1;                 /* momentane Suchposition */
      while ((*pos == '0' || *pos == '.') && only0flag)
      {
         if (*pos == '.')
            only0flag = 0;              /* jetzt Stoppen, da ist der . */
         pos--;
      }

      movmem(exppos, pos + 1, strlen(buf) - (exppos - buf) + 1);
                                        /* Exponent verschieben */
   }
   else
   {
      /*
       *  Kein Exponent, Nachkommanullen löschen
       */

      only0flag = 1;                    /* signalisiert: wir haben nur 0en */

      pos = buf + strlen(buf) - 1;      /* momentane Suchposition */
      while ((*pos == '0' || *pos == '.') && only0flag)
      {
         if (*pos == '.')
            only0flag = 0;              /* jetzt Stoppen, da ist der . */
         *pos = '\0';                   /* diese Position löschen */
         pos--;
      }
   }

   return(0);
}
