/* 1610, Mon 10 Jun 96

   N_PLOT.C:  Nevil's simple plotting package

   Copyright (C) 1992-2002 by Nevil Brownlee,
   CAIDA | University of Auckland */

/*
 * $Log: n_plot.c,v $
 * Revision 1.1.1.2.2.7  2002/02/23 01:57:18  nevil
 * Moving srl examples to examples/ directory.  Modified examples/Makefile.in
 *
 * Revision 1.1.1.2.2.3  2000/06/06 03:38:09  nevil
 * Combine NEW_ATR with TCP_ATR, various bug fixes
 *
 * Revision 1.1.1.2  1999/10/03 21:06:15  nevil
 * *** empty log message ***
 *
 * Revision 1.1.1.1.2.1  1999/01/08 01:38:28  nevil
 * Distribution file for 4.3b7
 *
 * Revision 1.1.1.1  1998/11/16 03:57:27  nevil
 * Import of NeTraMet 4.3b3
 *
 * Revision 1.1.1.1  1998/11/16 03:22:00  nevil
 * Import of release 4.3b3
 *
 * Revision 1.1.1.1  1998/10/28 20:31:23  nevil
 * Import of NeTraMet 4.3b1
 *
 * Revision 1.1.3.2  1998/10/18 23:44:05  nevil
 * Added Nicolai's patches, some 'tidying up' of the source
 *
 * Revision 1.1.3.1  1998/10/13 02:48:17  nevil
 * Import of Nicolai's 4.2.2
 *
 * Revision 1.1.1.1  1998/08/24 12:09:28  nguba
 * NetraMet 4.2 Original Distribution
 *
 * Revision 1.3  1998/05/07 04:28:53  rtfm
 * Implement NetFlowMet, the Cisco NetFlow RTFM meter
 */

#if HAVE_CONFIG_H
#include <ntm_conf.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <Xm/Xm.h>

#include "n_plot.h"

#define noPLT_DEBUG
#define noPLT_DEBUG_DRAW

static Widget plot_w;
static Drawable plot_d;
static GC plot_gc;

static int sxsize,sysize;
static int currsx,currsy, sxmin,sxmax, symin,symax;
static float currvx,currvy, vxmin,vxmax, vymin,vymax;
static int vsmode = P_LIN_LIN;  /* Current transform */
static float xscale,yscale, xoffset,yoffset, xlogf,ylogf;
#define P_TIC_LEN  4
static XFontStruct *def_font_info;
static int dfchasc,dfchht,dfchwd;

static int imin(a,b)
{ return a < b ? a : b; }
static int imax(a,b)
{ return a > b ? a : b; }
#define onscreen(c,cl)  imin(imax(0,c),cl-1)

static void compute_transform(void)
{
#ifdef PLT_DEBUG
   printf("cmp_tfrm(): vsmode=%d, vxmin=%g,vxmax=%g, vymin=%g,vymax=%g\n",
      vsmode, vxmin,vxmax, vymin,vymax);
   printf("   sxmin=%d,sxmax=%d, symin=%d,symax=%d\n",
      sxmin,sxmax, symin,symax);
#endif
   if (vsmode == P_LOG_LIN || vsmode == P_LOG_LOG) {
      if (vxmin < 0 || vxmax < 0) {
         printf("negative extrema for LOG x axis!\n");
         exit(1);
         }
      }
   if (vsmode == P_LIN_LOG || vsmode == P_LOG_LOG) {
      if (vymin < 0 || vymax < 0) {
         printf("negative extrema for LOG y axis!\n");
         exit(1);
         }
      }
   switch (vsmode) {
   case P_LIN_LIN:
      xscale = vxmin == vxmax ? 0 : (sxmax-sxmin)/(vxmax-vxmin);
      xoffset = sxmin - xscale*vxmin;
      yscale = vymin == vymax ? 0 : (symax-symin)/(vymax-vymin);
      yoffset = symin - yscale*vymin;
      break;
   case P_LOG_LIN:
      if (vxmin == 0) { vxmax = 0;  vxmin = 1; }
      xlogf = (sxmax-sxmin)/log10(vxmax/vxmin);
      yscale = vymin == vymax ? 0 : (symax-symin)/(vymax-vymin);
      yoffset = symin - yscale*vymin;
      break;
   case P_LIN_LOG:
      xscale = vxmin == vxmax ? 0 : (sxmax-sxmin)/(vxmax-vxmin);
      xoffset = sxmin - xscale*vxmin;
      if (vymin == 0) { vymax = 0;  vymin = 1; }
      ylogf = (symax-symin)/log10(vymax/vymin);
      break;
   case P_LOG_LOG:
      if (vxmin == 0) { vxmax = 0;  vxmin = 1; }
      xlogf = (sxmax-sxmin)/log10(vxmax/vxmin);
      if (vymin == 0) { vymax = 0;  vymin = 1; }
      ylogf = (symax-symin)/log10(vymax/vymin);
      break;
      }   
#ifdef PLT_DEBUG
   printf("   xscale=%g,xoff=%g, yscale=%g,yoff=%g, xlogf=%g,ylogf=%g\n",
      xscale,xoffset, yscale,yoffset, xlogf,ylogf);
#endif
   }

void settrn(int t)
{
   vsmode = t;
   compute_transform();
   }

void swindo(int minx,int lenx, int miny,int leny)
{
   sxmin = onscreen(minx,sxsize);
   sxmax = onscreen(minx+lenx-1,sxsize);
   symin = onscreen(miny,sysize);
   symax = onscreen(miny+leny-1,sysize);
   compute_transform();
   }

void dwindo(float minx,float maxx, float miny,float maxy)
{
   vxmin = minx;  vxmax = maxx;
   vymin = miny;  vymax = maxy;
   compute_transform();
   }

void plot_window(Widget pw, GC pgc)
{
   Dimension w_width,w_height;
   plot_w = pw;  plot_gc = pgc;
   def_font_info = XQueryFont(XtDisplay(plot_w),
      XGContextFromGC(plot_gc));
   dfchasc = def_font_info->ascent;
   dfchht = def_font_info->ascent+def_font_info->descent;
   dfchwd = astwdt("0",1);
   XtVaGetValues(pw,  /* Get window size */
      XmNwidth,&w_width, XmNheight,&w_height, NULL);
   sxsize = w_width; sysize = w_height;
#ifdef PLT_DEBUG
   printf("plt_window(): sxsize=%d, sysize=%d\n", sxsize,sysize);
#endif
   }

void plot_device(Drawable d)
{
   plot_d = d;
   }

void vtostransform(int *ix,int *iy, float x,float y)
{
   switch (vsmode) {
   case P_LIN_LIN:
      *ix = x*xscale + xoffset;
      *iy = y*yscale + yoffset;
      break;
   case P_LOG_LIN:
      *ix = sxmin + log10(x/vxmin)*xlogf;
      *iy = y*yscale + yoffset;
      break;
   case P_LIN_LOG:
      *ix = x*xscale + xoffset;
      *iy = symin + log10(y/vymin)*ylogf;
      break;
   case P_LOG_LOG:
      *ix = sxmin + log10(x/vxmin)*xlogf;
      *iy = symin + log10(y/vymin)*ylogf;
      break;
      }   
   }

static void vecplot(int code, int x,int y)
{
#ifdef PLT_DEBUG_DRAW
    printf("vecplot(%s): %d,%d\n", code == P_DRAW ? "draw" : "move", x,y);
#endif
   if (code == P_DRAW) {
      XDrawLine(XtDisplay(plot_w), plot_d, plot_gc,
         currsx,sysize-currsy, x,sysize-y);
      }
   currsx = x;  currsy = y;
   }

void movabs(int x,int y)
{ vecplot(P_MOVE,x,y); }

void drwabs(int x,int y)
{ vecplot(P_DRAW,x,y); }

void movrel(int x,int y)
{ vecplot(P_MOVE,currsx+x,currsy+y); }

void drwrel(int x,int y)
{ vecplot(P_DRAW,currsx+x,currsy+y); }

void pltsym(int sym)
{
   int sx = currsx, sy = currsy;
   char c;
   switch (sym) {
   case P_DOT:
      movrel(-1,-1);
      drwrel(2,0);  drwrel(0,2);  drwrel(-2,0);  drwrel(0,-2);
      break;
   case P_DIAMOND:
      movrel(-2,0);
      drwrel(2,2);  drwrel(2,-2);  drwrel(-2,-2);  drwrel(-2,2);
      break;
   case P_PLUS:
      movrel(-2,-0);  drwrel(4,0);
      movrel(-2,2);  drwrel(0,-4);
      break;
   case P_SQUARE:
      movrel(-2,-2);
      drwrel(4,0);  drwrel(0,4);  drwrel(-4,0);  drwrel(0,-4);
      break;
   default:
      movrel(-dfchwd/2,-dfchasc/2);
      c = sym;  aoutst(&c,1);
      break;
      }
   movabs(sx,sy);
   }

#define updatevcursor(x,y)  { currvx = x;  currvy = y; }

static void virtualplot(int code, float x,float y)
{
   int ix,iy;
   vtostransform(&ix,&iy, x,y);
   vecplot(code, ix,iy);
   updatevcursor(x,y);
#ifdef PLT_DEBUG_DRAW
   printf("vx,vy = %g,%g,  ix,iy = %d,%d\n", x,y, ix,iy);
#endif
   }

void movea(float x,float y)
{ virtualplot(P_MOVE, x,y); }

void drawa(float x,float y)
{ virtualplot(P_DRAW, x,y); }

int astwdt(char *a, int n)
{
   return XTextWidth(def_font_info, a,n);
   }

void aoutst(char *a, int n)
{
   int lw;
   if (n > 0) {  /* Draw to right */
      lw = astwdt(a,n);
      XDrawString(XtDisplay(plot_w), plot_d, plot_gc,
         currsx,sysize-currsy, a,n);
      }
   else {  /* Draw to left */
      lw = astwdt(a,-n);
      movabs(currsx-lw,currsy);
      XDrawString(XtDisplay(plot_w), plot_d, plot_gc,
         currsx,sysize-currsy, a,-n);
      }
   currsx += lw;
   }

static void drawxtic(float x,float ypos,
   int ytics,int yticf,int ynbr)
{
   char label[20];  int len, lw;
   movea(x,ypos);
   movabs(currsx,currsy+ytics);
   drwabs(currsx,currsy+yticf);
   sprintf(label,"%g", x);  len = strlen(label);
   lw = astwdt(label,len);
   movabs(currsx-lw/2, currsy+ynbr);
   aoutst(label,len);
   }

void xaxis(float xpos,float ypos, float xint)
{
   int hticl, ytics,yticf,ynbr,ytitl;
   float x;
   hticl = P_TIC_LEN/2;
   movea(xpos,ypos);
   if ((currsy-symin) < (symax-currsy)) {  /* Low */
      ytics = hticl;  yticf = -P_TIC_LEN;
      ynbr = -(hticl+dfchasc);
      ytitl = -(hticl+dfchasc);
      }
   else {  /* High */
      ytics = -hticl;  yticf = P_TIC_LEN;
      ynbr = hticl;
      ytitl = hticl+dfchasc;
      }
/* printf("xaxis(): ypos=%g, ytics=%d, yticf=%d, ynbr=%d, ytitl=%d\n",
   ypos, ytics,yticf,ynbr,ytitl); */
   movea(vxmax,ypos);  drawa(vxmin,ypos);
   for (x = xpos; ; ) {
      if (vsmode == P_LIN_LIN || vsmode == P_LIN_LOG) x = x-xint;
      else x = x/xint;
      if (x <= vxmin) break;
      drawxtic(x,ypos, ytics,yticf,ynbr);
      }
   for (x = xpos; ; ) {
      if (vsmode == P_LIN_LIN || vsmode == P_LIN_LOG) x = x+xint;
      else x = x*xint;
      if (x >= vxmax) break;
      drawxtic(x,ypos, ytics,yticf,ynbr);
      }
   movabs(currsx,currsy+ytitl);
   }

static int drawytic(float xpos,float y,
   int xtics,int xticf,int xnbr,int mside)
{
   char label[20];  int len, lw, xtitl;
   movea(xpos,y);
   movabs(currsx+xtics,currsy);
   drwabs(currsx+xticf,currsy);
   sprintf(label,"%g", y);  len = strlen(label);
   lw = astwdt(label,len);
   movabs(xtitl = currsx+xnbr, currsy-dfchasc/2);
   aoutst(label,len*mside);
   return xtitl;
   }

void yaxis(float xpos,float ypos, float yint)
{
   int hticl, xtics,xticf,xnbr,mside, xtitl,ytitl;
   float y;
   hticl = P_TIC_LEN/2;
   movea(xpos,ypos);
   if ((currsx-sxmin) < (sxmax-currsx)) {  /* Left */
      xtics = hticl;  xticf = -P_TIC_LEN;
      xnbr = -hticl;  mside = -1;
      }
   else {  /* Right */
      xtics = -hticl;  xticf = P_TIC_LEN;
      xnbr = hticl;  mside = 1;
      }
/* printf("yaxis(): xpos=%g, xtics=%d, xticf=%d, xnbr=%d, mside=%d\n",
   xpos, xtics,xticf,xnbr,mside); */
   movea(xpos,vymax);  drawa(xpos,vymin);
   for (y = ypos; ; ) {
      if (vsmode == P_LIN_LIN || vsmode == P_LOG_LIN) y = y-yint;
      else y = y/yint;
      if (y <= vymin) break;
      xtitl = drawytic(xpos,y, xtics,xticf,xnbr,mside);
      }
   for (y = ypos; ; ) {
      if (vsmode == P_LIN_LIN || vsmode == P_LOG_LIN) y = y+yint;
      else y = y*yint;
      if (y >= vymax) break;
      xtitl = drawytic(xpos,y, xtics,xticf,xnbr,mside);
      }
   ytitl = dfchht+hticl;  if (symin > symax) y = -y;
   movabs(xtitl,currsy+ytitl);
   }
