/* xsscatmat - XLISP interface to IVIEW dynamic graphics package.      */
/* XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney                  */
/* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz    */
/* You may give out copies of this software; for conditions see the    */
/* file COPYING included with this distribution.                       */

#include "xlisp.h"
#include "xlstat.h"

#define SCAT_PLOT_GAP 1
#define SCAT_INSET 4
#define LABEL_OFFSET 5
#define MAX_NUM_VARS 10

#define VAR_FORMAT "var %d"
#define POINT_FORMAT "%d"

extern LVAL s_scale_type, sk_show, sk_show_window;

static int pdata[MAX_NUM_VARS];

static struct {
  int x, y, left, top, size, bottom;
} current;

/* forward declarations */
LOCAL VOID get_plot_layout P4H(IVIEW_WINDOW, int *, int *, int *);
LOCAL VOID find_current_plot P3H(IVIEW_WINDOW, int, int);
LOCAL VOID scat_draw_point P3H(IVIEW_WINDOW, int, PointState);

/**************************************************************************/
/**                                                                      **/
/**                        Plot Creation Functions                       **/
/**                                                                      **/
/**************************************************************************/

LVAL iview_scatmat_allocate(V)
{  
  LVAL object;
  int i, vars, show;
  IVIEW_WINDOW w;
  char s[100];

  object = xlgaobject();
  show = xsboolkey(sk_show, TRUE);

  get_iview_ivars(object, &vars);
  if (vars < 2) xlfail("Too few variables for scatmat");
  if (vars > MAX_NUM_VARS) xlfail("Too many variables for scatmat");
  
  w = IViewNew(object);
  
  /* should replace this by something lisp-based +++++++ */
  for (i = 0; i < vars; i++) {
    sprintf(s, VAR_FORMAT, i);
    IViewSetVariableLabel(w, i, s);
    IViewSetRange(w, i, 0.0, 1.0);
  }
  
  initialize_iview(w, object);
  /* use StShowWindow to show (map) window but NOT send :resize or :redraw */
  if (show) StShowWindow(w);
  return(object);
}

/**************************************************************************/
/**                                                                      **/
/**                            Data Functions                            **/
/**                                                                      **/
/**************************************************************************/

static LVAL scatmat_add_data P1C(int, which)
{
  IVIEW_WINDOW w;
  LVAL data, object;
  int old_n = 0, n = 0;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  data = xlgetarg();

  switch(which) {
  case 'P':
    old_n = IViewNumPoints(w);
    internal_iview_add_points(w, object, data);
    n = IViewNumPoints(w);
    break;
  case 'L':
    old_n = IViewNumLines(w);
    internal_iview_add_lines(w, object, data);
    n = IViewNumLines(w);
    break;
#ifdef USESTRINGS
  case 'S':
    old_n = IViewNumStrings(w);
    internal_iview_add_strings(w, object, data);
    n = IViewNumStrings(w);
    break;
#endif /* USESTRINGS */
  }
  
  check_add_to_screen(object, which, old_n, n, TRUE);
  
  return(NIL);
}

LVAL iview_scatmat_add_points(V)  { return(scatmat_add_data('P')); }
LVAL iview_scatmat_add_lines(V)   { return(scatmat_add_data('L')); }
#ifdef USESTRINGS
LVAL iview_scatmat_add_strings(V) { return(scatmat_add_data('S')); }
#endif /* USESTRINGS */
 
/**************************************************************************/
/**                                                                      **/
/**                    Drawing and Resizing Functions                    **/
/**                                                                      **/
/**************************************************************************/

LVAL iview_scatmat_resize(V)
{
  int vars;
  int i, top, left, subsize, low, high;
  IVIEW_WINDOW w;
  LVAL object;
  
  object = xlgaobject();
  xllastarg();
  
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  
  vars = IViewNumVariables(w);
  IViewSetFixedAspect(w, TRUE);
  IViewStdResize(w);

  get_plot_layout(w, &left, &top, &subsize);

  for (i = 0; i < vars; i++) {
    low = i * (subsize + SCAT_PLOT_GAP) + 2 * SCAT_INSET + SCAT_PLOT_GAP;
    high = low + subsize - 3 * SCAT_INSET;
    IViewSetScreenRange(w, i, low, high);
  }
  return(NIL);
}

LVAL iview_scatmat_redraw_content(V)
{
  int vars;
  int left, top, subleft, subtop, subsize;
  int cmleft, cmtop, cmwidth, cmheight;
  int i, j;
  double low, high;
  char s[100];
  IVIEW_WINDOW w;
  LVAL object, scale_type;
  StGWWinInfo *gwinfo;
  
  object = xlgaobject();
  xllastarg();
  
  gwinfo = StGWObWinInfo(object);
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  if (IVIEW_WINDOW_NULL(w)) return(NIL);
    
  vars = IViewNumVariables(w);
  if (IViewMouseMode(w) == brushing) IViewEraseBrush(w);
  IViewGetContentMarginRect(w, &cmleft, &cmtop, &cmwidth, &cmheight);
  get_plot_layout(w, &left, &top, &subsize);
  scale_type = slot_value(object, s_scale_type);

  /**** should set/reset clip rectangle */
  StGWStartBuffering(gwinfo);
  /*StGWEraseRect(gwinfo, cmleft, cmtop, cmwidth + 1, cmheight + 1);*/
  IViewClearContent(w);
  for (i = 0; i < vars; i++) 
    for (j = 0; j < vars; j++) {
      subleft = left + j * (SCAT_PLOT_GAP + subsize);
      subtop = top + (vars - i - 1) * (SCAT_PLOT_GAP + subsize);
      StGWFrameRect(gwinfo, subleft, subtop, subsize, subsize);
      if (i == j && scale_type == NIL) {
        if (IViewVariableLabel(w, i) != 0)
          StGWDrawText(gwinfo, IViewVariableLabel(w, i), 
                       subleft + subsize / 2, subtop + subsize / 2, 1, 0);
        IViewGetRange(w, i, &low, &high);
        sprintf(s, "%.3g", high);
        StGWDrawText(gwinfo, s, 
		     subleft + subsize - SCAT_INSET,
		     subtop + SCAT_INSET, 2, 1);
        sprintf(s, "%.3g", low);
        StGWDrawText(gwinfo, s, 
		     subleft + SCAT_INSET,
		     subtop + subsize - SCAT_INSET, 0, 0);
      }
      else if (i != j) {
        IViewDrawDataPoints(w, i, j, 0, IViewNumPoints(w));
        IViewDrawDataLines(w, i, j, 0, IViewNumLines(w));
#ifdef USESTRINGS
        IViewDrawDataStrings(w, i, j, 0, IViewNumStrings(w));
#endif /* USESTRINGS */
      }
    }
  StGWBufferToScreen(gwinfo, cmleft, cmtop, cmwidth + 1, cmheight + 1);
  if (IViewMouseMode(w) == brushing) IViewDrawBrush(w);
  IViewResetScreenStates(w);
  return(NIL);
}

/**************************************************************************/
/**                                                                      **/
/**                           Mouse Functions                            **/
/**                                                                      **/
/**************************************************************************/

static LVAL iview_scatmat_mouse P1C(int, click)
{
  IVIEW_WINDOW w;
  LVAL object;
  int x, y;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);

  if (! IVIEW_WINDOW_NULL(w)) {
    x = fixp(peekarg(0)) ? getfixnum(peekarg(0)) : 0;
    y = fixp(peekarg(1)) ? getfixnum(peekarg(1)) : 0;
    find_current_plot(w, x, y);
    if (click) IViewDoClick(object);
    else IViewDoMotion(object);
  }
  return(NIL);
}

LVAL iview_scatmat_click(V) { return(iview_scatmat_mouse(TRUE)); }
LVAL iview_scatmat_motion(V) { return(iview_scatmat_mouse(FALSE)); }

LVAL iview_scatmat_adjust_screen_point(V)
{
  LVAL object;
  int point;
  IVIEW_WINDOW w;
  PointState state, screen_state;
  
  object = xlgaobject();
  point = getfixnum(xlgafixnum());
  xllastarg();

  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  
  if (! IViewPointMasked(w, point)) {
    state = IViewPointState(w, point);
    screen_state = IViewPointScreenState(w, point);
	if (state == pointInvisible || screen_state == pointInvisible) {
	  StGrSetDirty(StGWObWinInfo(object), TRUE);
	}
    else {
      scat_draw_point(w, point, state);
    }
  }
  
  return(NIL);
}

LVAL iview_scatmat_adjust_points_in_rect(V)
{
  int var1, var2, x, y, px, py;
  int  i, n, in_rect, right, bottom, left, top, width, height;
  PointState point_state, state;
  IVIEW_WINDOW w;
  LVAL object;
  StGWWinInfo *gwinfo;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  left = getfixnum(xlgafixnum());
  top = getfixnum(xlgafixnum());
  width = getfixnum(xlgafixnum());
  height = getfixnum(xlgafixnum());
  state = decode_point_state(xlgetarg());
  xllastarg();
  
  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  gwinfo = StGWObWinInfo(object);
  
  IViewCheckLinks(w);
  n = IViewNumPoints(w);
  bottom = top + height;
  right = left + width;
  StGrGetContentVariables(gwinfo, &var1, &var2);
  StGrGetContentOrigin(gwinfo, &x, &y);
  
  if (IViewMouseMode(w) == brushing) IViewEraseBrush(w);

  for (i = 0; i < n; i++) {
    point_state = IViewPointState(w, i);
    if (! IViewPointMasked(w, i) && point_state != pointInvisible) {
      px = x + IViewPointScreenValue(w, var1, i);
      py = y - IViewPointScreenValue(w, var2, i);
      in_rect = (var1 != var2 
		 && px >= left && px <= right && py >= top && py <= bottom);
      if (in_rect && (int) point_state < (int) state) {
        IViewSetPointState(w, i, state);
      }
      else if (! in_rect 
	       && state == pointHilited && point_state == pointHilited) {
        IViewSetPointState(w, i, pointNormal);
      }
    }
  }
  IViewAdjustScreens(w);
  if (IViewMouseMode(w) == brushing) IViewDrawBrush(w);
  
  return(NIL);
}

LVAL iview_scatmat_mark_points_in_rect(V)
{
  int var1, var2;
  int left, top, width, height;
  IVIEW_WINDOW w;
  LVAL object;
  StGWWinInfo *gwinfo;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  left = getfixnum(xlgafixnum());
  top = getfixnum(xlgafixnum());
  width = getfixnum(xlgafixnum());
  height = getfixnum(xlgafixnum());
  xllastarg();
  
  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  gwinfo = StGWObWinInfo(object);
  
  StGrGetContentVariables(gwinfo, &var1, &var2);
  if (var1 == var2)   IViewClearPointMarks(w);
  else IViewStdMarkPointsInRect(w, left, top, width, height);
  return(NIL);
}

/**************************************************************************/
/**                                                                      **/
/**                          Internal Functions                          **/
/**                                                                      **/
/**************************************************************************/

LOCAL VOID get_plot_layout P4C(IVIEW_WINDOW, w, int *, subleft, int *, subtop, int *, subsize)
{
  int vars, left, top, width, height, delta;
  StGWWinInfo *gwinfo = IViewWindowWinInfo(w);

  vars = IViewNumVariables(w);
  StGrGetContentRect(gwinfo, &left, &top, &width, &height);

  if (subleft != NULL && subtop != NULL && subsize != NULL) {
    *subsize = (width - SCAT_PLOT_GAP * (vars + 1)) / vars;
    if (*subsize < 0) *subsize = 0;

    delta = (width - (vars * *subsize + (vars - 1) * SCAT_PLOT_GAP)) / 2;
    if (delta < 0) delta = 0;

    *subleft = left + delta;
    *subtop = top + delta;
  }
}

static VOID ScatDrawPoint P4C(IVIEW_WINDOW, w, int, point,
                              PointState, state, PointState, screen_state)
{
  int vars = IViewNumVariables(w);
  int left, bottom;
  int oldwidth, oldheight, newwidth, newheight;
  int i, j;
  int x, y, oldsym, newsym, sym, hsym, replace;
  StGWWinInfo *gwinfo = IViewWindowWinInfo(w);
  int color = 0, oldcolor = 0, use_color = StGWUseColor(gwinfo);

  IViewGetPointSymbol(w, point, &sym, &hsym);
  oldsym = (screen_state == pointNormal) ? sym : hsym;
  newsym = (state == pointNormal) ? sym : hsym;
  if (state == pointInvisible) return;
  
  StGWGetSymbolSize(oldsym, &oldwidth, &oldheight);
  StGWGetSymbolSize(newsym, &newwidth, &newheight);
  replace = (oldwidth > newwidth || oldheight > newheight);

  IViewGetScreenPointValues(w, point, pdata);

  StGrGetContentOrigin(gwinfo, &left, &bottom);
  if (use_color) {
    oldcolor = StGWDrawColor(gwinfo);
    color = IViewPointColor(w, point);
    if (color != NOCOLOR) StGWSetDrawColor(gwinfo, color);
  }
  for (i = 0; i < vars; i++) {
    y = bottom - pdata[i];
    for (j = 0; j < vars; j++)
      if (i != j) {
        x = left + pdata[j];
        if (replace) StGWReplaceSymbol(gwinfo, oldsym, newsym, x, y);
        else StGWDrawSymbol(gwinfo, newsym, x, y);
      }
  }
  if (use_color && color != NOCOLOR) StGWSetDrawColor(gwinfo, oldcolor);
}

static VOID DrawLabel P2C(IVIEW_WINDOW, w, int, point)
{
  int vars = IViewNumVariables(w);
  int left, bottom;
  int i, j;
  int x, y;
  char *label;
  StGWWinInfo *gwinfo = IViewWindowWinInfo(w);
  int mode = StGWDrawMode(gwinfo);

  label = IViewPointLabel(w, point);
  if (label == NULL) return;
    
  for (i = 0; i < vars; i++) pdata[i] = IViewPointScreenValue(w, i, point);
  StGrGetContentOrigin(gwinfo, &left, &bottom);
  for (i = 0; i < vars; i++) {
    y = bottom - pdata[i] - LABEL_OFFSET;
    for (j = 0; j < vars; j++)
      if (i != j) {
        x = left + pdata[j] + LABEL_OFFSET;
        StGWSetDrawMode(gwinfo, 1);
        StGWDrawString(gwinfo, label, x, y);
        StGWSetDrawMode(gwinfo, mode);
      }
  }
}

LOCAL VOID find_current_plot P3C(IVIEW_WINDOW, w, int, x, int, y)
{
  int width, height;
  int left, top, subsize, vars;
  StGWWinInfo *gwinfo = IViewWindowWinInfo(w);
  
  vars = IViewNumVariables(w);
  
  if (IViewMouseMode(w) == brushing) {
    IViewGetBrush(w, NULL, NULL, &width, &height);
    x -= width / 2;
    y -= height / 2;
  }
  
  get_plot_layout(w, &left, &top, &subsize);
  subsize += SCAT_PLOT_GAP;
  current.x = (x - left) / subsize;
  current.y = (top + vars * subsize - y - SCAT_PLOT_GAP) / subsize;
  if (current.x < 0 || current.x >= vars 
      || current.y < 0 || current.y >= vars) {
    current.x = 0;
    current.y = 0;
  }
  current.size = vars * subsize - SCAT_PLOT_GAP;
  current.left = left;
  current.top = top;
  current.bottom = current.left + current.top + current.size;
  StGrSetContentVariables(gwinfo, current.x, current.y);
}

LOCAL VOID scat_draw_point P3C(IVIEW_WINDOW, w, int, i, PointState, state)
{
  int showingLabels = IViewShowingLabels(w);
  
  if (state == pointNormal && showingLabels) DrawLabel(w, i); /* to erase */
  ScatDrawPoint(w, i, state, state);
  if (state != pointNormal && showingLabels) DrawLabel(w, i); /* to draw */
  IViewSetPointScreenState(w, i, state);
}



syntax highlighted by Code2HTML, v. 0.9.1