/* xshistogram - 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 HIST_MAX_BINS 50
#define HIST_INITIAL_BINS 6
#define NOT_IN_HIST -2
#define NO_MORE_POINTS -1
#define BIN_POINTER -1
#define HIST_CLICK_WIDTH 1
#define HIST_CLICK_HEIGHT 1

typedef struct hist_point {
  int left, top, width, height, next;
} HistPoint;

typedef struct hist_bin {
  int low, high, count, height, smallest;
} Bin;

typedef struct hist {
  Bin bins[HIST_MAX_BINS];
  char *point_data;
  int num_bins, num_showing;
} *IViewHist;

extern LVAL s_histogram_internals, sk_draw, sk_redraw, sk_resize, s_true,
  s_scale_type, sk_show, sk_show_window;

extern LVAL xsapplysubr(), iview_isnew(), makearglist();

/* forward declarations */
LOCAL VOID bad_hist_data(V);
LOCAL VOID IViewHistSetNumBins P3H(LVAL, LVAL, int);
LOCAL VOID clear_bins P1H(LVAL);
LOCAL VOID draw_hist P2H(IVIEW_WINDOW, LVAL);
LOCAL VOID initialize_points P4H(IVIEW_WINDOW, LVAL, int, int);
LOCAL int insert_point P5H(IVIEW_WINDOW, LVAL, int, int, int);
LOCAL int remove_non_showing_points P2H(IVIEW_WINDOW, LVAL);
LOCAL VOID find_point_rects P2H(IVIEW_WINDOW, LVAL);
LOCAL VOID hilite_points P2H(IVIEW_WINDOW, LVAL);
LOCAL int sect_point_rect P6H(LVAL, int, int, int, int, int);
LOCAL int sort_bins P2H(IVIEW_WINDOW, LVAL);
LOCAL VOID size_bins P2H(IVIEW_WINDOW, LVAL);
LOCAL VOID draw_hist_point_state P4H(IVIEW_WINDOW, LVAL, int, int);
LOCAL VOID set_density_range P3H(IVIEW_WINDOW, int, double);

static LVAL gethistdata P1C(LVAL, object)
{
  LVAL val;
  
  val = slot_value(object, s_histogram_internals);
  if (! consp(val) || !adatap(car(val))) bad_hist_data();
  return(val);
}

static HistPoint *getpointdata P1C(LVAL, hdata)
{
  if (adatap(cdr(hdata)))
    return((HistPoint *) StRPtr(getadaddr(cdr(hdata))));
  else return(NULL);
}
  
static IViewHist getinternals P1C(LVAL, hdata)
{
  if (getadaddr(car(hdata)) == NULL) bad_hist_data();
  return((IViewHist) getadaddr(car(hdata)));
}

static VOID gethistargs P3C(IVIEW_WINDOW *, w, LVAL *, object, LVAL *, hdata)
{
  *object = xlgaobject();
  if (w != NULL) *w = (IVIEW_WINDOW) GETIVIEWADDRESS(*object);
  if (hdata != NULL) *hdata = gethistdata(*object);
}

VOID newhistinternals P1C(LVAL, object)
{
  LVAL val;
  
  xlsave1(val);
  val = newadata(sizeof(struct hist), 1, FALSE);
  val = consa(val);
  set_slot_value(object, s_histogram_internals, val);
  xlpop();
}

static VOID allocate_internal_points P2C(LVAL, object, int, n)
{
  LVAL val;
  val = gethistdata(object);
  if (adatap(cdr(val)))
    reallocaddata(cdr(val), sizeof(struct hist_point), n);
  else
    rplacd(val, newadata(sizeof(struct hist_point), n, TRUE));
}  

static VOID clear_internal_points P1C(LVAL, object)
{
  LVAL val;
  
  val = slot_value(object, s_histogram_internals);
  if (! consp(val) || ! adatap(cdr(val))) bad_hist_data();
  freeadata(cdr(val));
}

LOCAL VOID bad_hist_data(V) { xlfail("bad internal histogram data"); }

/**************************************************************************/
/**                                                                      **/
/**                     Histogram Creation Functions                     **/
/**                                                                      **/
/**************************************************************************/

LVAL iview_hist_isnew(V)
{
  LVAL object, args, hdata;
  int vars;
  
  object = xlgaobject();
  vars = getfixnum(xlgafixnum());
  if (vars < 1) xlfail("bad number of variables for histogram");
  
  newhistinternals(object);
  hdata = gethistdata(object);
  IViewHistSetNumBins(object, hdata, HIST_INITIAL_BINS);
  clear_bins(hdata);

  xlsave1(args);
  args = makearglist(xlargc, xlargv);
  args = cons(cvfixnum((FIXTYPE) (vars + 1)), args);
  args = cons(object, args);
  xsapplysubr(iview_isnew, args);
  xlpop();

  return(object);
}

LVAL iview_hist_allocate(V)
{
  LVAL object;
  IVIEW_WINDOW w;
  int vars, show;
  StGWWinInfo *gwinfo;

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

  gwinfo = StGWObWinInfo(object);
  w = IViewNew(object);
  initialize_iview(w, object);

  vars = IViewNumVariables(w);
  StGrSetClickRange(gwinfo, HIST_CLICK_WIDTH, HIST_CLICK_HEIGHT);
  StGrSetContentVariables(gwinfo, 0, vars - 1);

  /* use StShowWindow to show (map) window but NOT send :resize or :redraw */
  if (show) StShowWindow(w);
  
  return(object);
}

/**************************************************************************/
/**                                                                      **/
/**                     State Accessors and Mutators                     **/
/**                                                                      **/
/**************************************************************************/

LOCAL VOID IViewHistSetNumBins P3C(LVAL, object, LVAL, hdata, int, n)
{
  IViewHist h = getinternals(hdata);
  
  if (n < 1 || n > HIST_MAX_BINS) return;
  h->num_bins = n;
  send_message(object, sk_resize);
}

static int IViewHistNumBins P1C(LVAL, hdata)
{
  IViewHist h = getinternals(hdata);
  
  return(h->num_bins);
}
  
LVAL iview_hist_num_bins(V)
{
  LVAL object, hdata;
  IVIEW_WINDOW w;
  int bins;
  
  gethistargs(&w, &object, &hdata);
  if (moreargs()) {
    bins = getfixnum(xlgafixnum());
    IViewHistSetNumBins(object, hdata, bins);
    check_redraw(object, TRUE, TRUE);
  }
  return(cvfixnum((FIXTYPE) IViewHistNumBins(hdata)));
}

LVAL iview_hist_bin_counts(V)
{
  LVAL object, hdata, result, next;
  IVIEW_WINDOW w;
  int i, bins;
  IViewHist h;
  
  gethistargs(&w, &object, &hdata);
  xllastarg();
  
  if (hdata == NULL || (h = getinternals(hdata)) == NULL) result = NIL;
  else {
    bins = h->num_bins;
    xlsave1(result);
    result = mklist(bins, NIL);
    for (i = 0, next = result; i < bins; i++, next = cdr(next))
      rplaca(next, cvfixnum((FIXTYPE) h->bins[i].count));
    xlpop();
  }
  return(result);
}

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

LVAL iview_hist_add_points(V)
{
  IVIEW_WINDOW w;
  int old_n, n;
  LVAL object, data, hdata;
  
  gethistargs(&w, &object, &hdata);
  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  
  old_n = IViewNumPoints(w);
  
  xlsave1(data);
  data = xlgetarg();
  data = (fixp(data) || (consp(data) && seqp(car(data)))) 
       ? data : consa(data);
  internal_iview_add_points(w, object, data);
  xlpop();
  
  n = IViewNumPoints(w);
  allocate_internal_points(object, n);
  initialize_points(w, hdata, old_n, n);
  
  check_add_to_screen(object, 'P', old_n, n, TRUE);
  
  return(NIL);
}

LVAL iview_hist_clear_points(V)
{
  IVIEW_WINDOW w;
  LVAL object, hdata;
  
  gethistargs(&w, &object, &hdata);
 
  if (! IVIEW_WINDOW_NULL(w)) {
    IViewClearPoints(w);
    clear_bins(hdata);
    clear_internal_points(object);
    check_redraw(object, TRUE, TRUE);
  }
  return(NIL);
}

/**************************************************************************/
/**                                                                      **/
/**                    Drawing and Resizing Functions                    **/
/**                                                                      **/
/**************************************************************************/

static VOID IViewHistResize P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  IViewStdResize(w);
  clear_bins(hdata);
  initialize_points(w, hdata, 0, IViewNumPoints(w));
}

static VOID IViewHistRedrawContent P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  int left, top, width, height, x, y, vleft, vtop, vwidth, vheight;
  StGWWinInfo *gwinfo;
  
  gwinfo = IViewWindowWinInfo(w);

  if (IViewMouseMode(w) == brushing) IViewEraseBrush(w);
  IViewGetContentMarginRect(w, &left, &top, &width, &height);
  StGrGetContentVariables(gwinfo, &x, &y);
  
  StGWStartBuffering(gwinfo);
  StGWSetClipRect(gwinfo, TRUE, left, top, width + 1, height + 1);
  /*StGWEraseRect(gwinfo, left, top, width + 1, height + 1);*/
  IViewClearContent(w);

  sort_bins(w, hdata);
  size_bins(w, hdata);
  draw_hist(w, hdata);  
  IViewDrawDataLines(w, x, y, 0, IViewNumLines(w));
#ifdef USESTRINGS
  IViewDrawDataStrings(w, x, y, 0, IViewNumStrings(w));
#endif /* USESTRINGS */
  StGWBufferToScreen(gwinfo, left, top, width + 1, height + 1);
  StGWGetViewRect(gwinfo, &vleft, &vtop, &vwidth, &vheight);
  StGWSetClipRect(gwinfo, TRUE, vleft, vtop, vwidth, vheight);
  if (IViewMouseMode(w) == brushing) IViewDrawBrush(w);
  IViewResetScreenStates(w);
}

LVAL iview_hist_resize(V)
{
  LVAL object, hdata;
  IVIEW_WINDOW w;

  gethistargs(&w, &object, &hdata);
  if (! IVIEW_WINDOW_NULL(w)) IViewHistResize(w, hdata);
  return(NIL);
}

LVAL iview_hist_redraw_content(V)
{
  LVAL object, hdata;
  IVIEW_WINDOW w;

  gethistargs(&w, &object, &hdata);
  if (! IVIEW_WINDOW_NULL(w)) IViewHistRedrawContent(w, hdata);
  return(NIL);
}

LVAL iview_hist_adjust_to_data(V)
{
  LVAL object;
  IVIEW_WINDOW w;
  StGWWinInfo *gwinfo;
  double low, high;
  int ticks, labeled, x, y, scaled, bins;
  char *label;
  LVAL arg, hdata;
  
  gethistargs(&w, &object, &hdata);

  if (! IVIEW_WINDOW_NULL(w)) {
    gwinfo = StGWObWinInfo(object);
    StGrObAdjustToData(object, FALSE);
    scaled = (slot_value(object, s_scale_type) != NIL) ? TRUE : FALSE;
    
    StGrGetContentVariables(gwinfo, &x, &y);
    IViewGetRange(w, x, &low, &high);
    label = IViewVariableLabel(w, x);
    labeled = (label != NULL && strlen(label) != 0) ? TRUE : FALSE;
    ticks = 4;
    GetNiceRange(&low, &high, &ticks);
    IViewSetRange(w, x, low, high);
    IViewSetXaxis(w, ! scaled, labeled, ticks);
    bins = 5 + log(1.0 + IViewNumPoints(w));
    GetNiceRange(&low, &high, &bins);
    if (bins > 1) bins --;
    if (bins > 30) bins = 30;
    IViewHistSetNumBins(object, hdata, bins);

    if (! xlgetkeyarg(sk_draw, &arg)) arg = s_true;
    if (arg != NIL) send_message(object, sk_resize);
    if (arg != NIL) send_message(object, sk_redraw);  
  }
  return(NIL);
}

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

LVAL iview_hist_adjust_screen(V)
{
  IVIEW_WINDOW w;
  LVAL object, hdata;
  int x, y, lines_only = FALSE;
  StGWWinInfo *gwinfo;

  gethistargs(&w, &object, &hdata);

  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  gwinfo = StGWObWinInfo(object);
  if (StGrDirty(gwinfo)) {
	StGrSetDirty(gwinfo, FALSE);
    if (lines_only) {
      if (IViewMouseMode(w) == brushing) IViewEraseBrush(w);
      StGrGetContentVariables(gwinfo, &x, &y);
      IViewDrawDataLines(w, x, y, 0, IViewNumLines(w));
#ifdef USESTRINGS
      IViewDrawDataStrings(w, x, y, 0, IViewNumStrings(w));
#endif /* USESTRINGS */
      if (IViewMouseMode(w) == brushing) IViewDrawBrush(w);
	}
	else {
      IViewHistResize(w, hdata);
      IViewHistRedrawContent(w, hdata);
    }
  }
  return(NIL);
}

LVAL iview_hist_adjust_points_in_rect(V)
{
  int  i, n, in_rect;
  PointState point_state;
  IVIEW_WINDOW w;
  int left, top, width, height;
  PointState state;
  LVAL object, hdata;
  
  gethistargs(&w, &object, &hdata);

  left = getfixnum(xlgafixnum());
  top = getfixnum(xlgafixnum());
  width = getfixnum(xlgafixnum());
  height = getfixnum(xlgafixnum());
  state = decode_point_state(xlgetarg());

  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  
  IViewCheckLinks(w);
  n = IViewNumPoints(w);
  if (IViewMouseMode(w) == brushing) IViewEraseBrush(w);

  for (i = 0; i < n; i++) {
    point_state = IViewPointState(w, i);
    if (! IViewPointMasked(w, i) && point_state != pointInvisible) {
      in_rect = sect_point_rect(hdata, i, left, top, width, height);
      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_hist_mark_points_in_rect(V)
{
  int  i, n, in_rect;
  PointState point_state;
  IVIEW_WINDOW w;
  int left, top, width, height;
  LVAL object, hdata;
  
  gethistargs(&w, &object, &hdata);

  left = getfixnum(xlgafixnum());
  top = getfixnum(xlgafixnum());
  width = getfixnum(xlgafixnum());
  height = getfixnum(xlgafixnum());

  if (IVIEW_WINDOW_NULL(w)) return(NIL);
  
  n = IViewNumPoints(w);

  for (i = 0; i < n; i++) {
    point_state = IViewPointState(w, i);
    if (! IViewPointMasked(w, i) && point_state != pointInvisible) {
      in_rect = sect_point_rect(hdata, i, left, top, width, height);
      IViewSetPointMark(w, i, in_rect);
    }
  }
  return(NIL);
}

LVAL iview_hist_adjust_screen_point(V)
{
  IVIEW_WINDOW w;
  LVAL object, hdata;
  int point;
  PointState state, screen_state;
  
  gethistargs(&w, &object, &hdata);
  point = getfixnum(xlgafixnum());
  
  if (! IVIEW_WINDOW_NULL(w) && ! IViewPointMasked(w, point)) {
    state = IViewPointState(w, point);
    screen_state = IViewPointScreenState(w, point);
	if (state == pointInvisible || screen_state == pointInvisible) {
	  StGrSetDirty(StGWObWinInfo(object), TRUE);
	}
    else {
      draw_hist_point_state(w, hdata, point, state);
    }  
  }
  return(NIL);
}

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

LOCAL VOID clear_bins P1C(LVAL, hdata)
{
  IViewHist h = getinternals(hdata);
  int i;
   
  for (i = 0; i < h->num_bins; i++) {
    h->bins[i].low = 0;
    h->bins[i].high = 0;
    h->bins[i].count = 0;
    h->bins[i].height = 0;
    h->bins[i].smallest = NO_MORE_POINTS;
  }
}
  
LOCAL int sort_bins P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  IViewHist h = getinternals(hdata);
  int i, k, low, high, val, x, y, n = IViewNumPoints(w);
  int changed = FALSE;
  StGWWinInfo *gwinfo;
  
  gwinfo = IViewWindowWinInfo(w);

  StGrGetContentVariables(gwinfo, &x, &y);
  IViewGetScreenRange(w, x, &low, &high);
  if (high <= low) return(FALSE);
  if (remove_non_showing_points(w, hdata)) changed = TRUE;
  for (i = 0; i < h->num_bins; i++) h->bins[i].count = 0;
  h->num_showing = 0;
  for (i = 0; i < n; i++) {
    if (! IViewPointMasked(w, i) && IViewPointState(w, i) != pointInvisible) {
      val = IViewPointScreenValue(w, x, i);
      k = (h->num_bins * (val - low)) / (high - low);
      if (k == h->num_bins && val == high) k--; /* on boundary of last bin */
      if (k >= 0 && k < h->num_bins) {
        h->bins[k].count++;
        if (insert_point(w, hdata, x, i, k)) changed = TRUE;    
        h->num_showing++;
      }
    }
  }
  return(changed);
}

LOCAL VOID size_bins P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  IViewHist h = getinternals(hdata);
  int i, low, high, width, x, y, maxcount, m;
  double factor, density, x_low, x_high, range;
  StGWWinInfo *gwinfo = IViewWindowWinInfo(w);

  StGrGetContentVariables(gwinfo, &x, &y);
  StGrGetContentRect(gwinfo, &low, NULL, &width, NULL);
  high = low + width;
  
  maxcount = 1;
  for (i = 0; i < h->num_bins; i++) {
    h->bins[i].low = low + (i * (high - low)) / h->num_bins;
    h->bins[i].high = low + ((i + 1) * (high - low)) / h->num_bins;
    maxcount = (h->bins[i].count > maxcount) ? h->bins[i].count : maxcount;
  }
  
  IViewGetScreenRange(w, y, &low, &high);
  IViewGetScaledRange(w, x, &x_low, &x_high);
  range = x_high - x_low;
  m = h->num_showing;
  density = (range > 0.0 && m > 0) ? 1.2 * (h->num_bins * maxcount / range) / m : 1.0;
  set_density_range(w, y, density);
  factor = ((double) (high - low)) / (maxcount * 1.2);
  for (i = 0; i < h->num_bins; i++) {
    h->bins[i].height = h->bins[i].count * factor;
  }
  find_point_rects(w, hdata);
}

LOCAL VOID draw_hist P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  IViewHist h = getinternals(hdata);
  int i, left, top, width, height, content_top, content_height;
  StGWWinInfo *gwinfo = IViewWindowWinInfo(w);
   
  StGrGetContentRect(gwinfo, NULL, &content_top, NULL, &content_height);
  
  for (i = 0; i < h->num_bins; i++) {
    left = h->bins[i].low;
    top  = content_top  + content_height - h->bins[i].height;
    width = h->bins[i].high - left + 1;
    height = h->bins[i].height + 1;
    StGWFrameRect(gwinfo, left, top, width, height);
  }
  hilite_points(w, hdata);
}

LOCAL VOID initialize_points P4C(IVIEW_WINDOW, w, LVAL, hdata, int, m, int, n)
{
  HistPoint *points;
  int i;
   
  if (m < 0 || n > IViewNumPoints(w)) return;
  points = getpointdata(hdata);
  for (i = m; i < n; i++) {
    points[i].left = 0;
    points[i].top = 0;
    points[i].width = 0;
    points[i].height = 0;
    points[i].next = NOT_IN_HIST;
  }
}

LOCAL int insert_point P5C(IVIEW_WINDOW, w, LVAL, hdata, int, var, int, p, int, bin)
{
  IViewHist h = getinternals(hdata);
  int last, current, bin_value, point_value;
  HistPoint* points;
   
  points = getpointdata(hdata);
 
  if (points[p].next != NOT_IN_HIST) return(FALSE); /* already in hist */
  
  point_value = IViewPointScreenValue(w, var, p);
  last = BIN_POINTER;
  current = h->bins[bin].smallest;
  bin_value = (current >= 0) ? IViewPointScreenValue(w, var, current) : 0;
  while (bin_value < point_value && current != NO_MORE_POINTS) {
    last = current;
    current = points[current].next;
    bin_value = (current >= 0) ? IViewPointScreenValue(w, var, current) : 0;
  }
  
  points[p].next = current;
  if (last == BIN_POINTER) h->bins[bin].smallest = p;
  else points[last].next = p;
  
  return(TRUE);
}

LOCAL int remove_non_showing_points P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  IViewHist h = getinternals(hdata);
  int last, next, showing, p, bin;
  HistPoint* points;
  int changed = FALSE;
  
  points = getpointdata(hdata);
 
  for (bin = 0; bin < h->num_bins; bin++) 
    for (p = h->bins[bin].smallest, last = BIN_POINTER; p >= 0;) {  
      showing = (! IViewPointMasked(w, p) && IViewPointState(w, p) != pointInvisible);
      if (showing) {
        last = p;
        p = points[p].next;
      }
      else {
        changed = TRUE;
        if (last == BIN_POINTER) h->bins[bin].smallest = points[p].next;
        else points[last].next = points[p].next;
        next = points[p].next;
        points[p].next = NOT_IN_HIST;
        p = next;
      }
    }
  return(changed);
}

LOCAL VOID find_point_rects P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  IViewHist h = getinternals(hdata);
  int showing, p, k, bin, bin_width, bin_left, height, bottom, top, count, base;
  HistPoint* points;
  StGWWinInfo *gwinfo;
  
  gwinfo = IViewWindowWinInfo(w);
  points = getpointdata(hdata);

  StGrGetContentOrigin(gwinfo, NULL, &base);
  
  for (bin = 0; bin < h->num_bins; bin++) {
    bin_left = h->bins[bin].low + 1;
    bin_width = h->bins[bin].high - h->bins[bin].low - 1;
    count = h->bins[bin].count;
    height = h->bins[bin].height - 1;
    for (p = h->bins[bin].smallest, k = 0; p >= 0; p = points[p].next) {  
      showing = (! IViewPointMasked(w, p) && IViewPointState(w, p) != pointInvisible);
      if (showing) {
        points[p].left = bin_left;
        points[p].width = bin_width;
        bottom = base - ((count > 0) ? (height * k) / count : 0);
        top = base - ((count > 0) ? (height * (k + 1)) / count : 0);
        points[p].top = top;
        points[p].height = bottom - top;
        k++;
      }
    }
  }
}

LOCAL VOID hilite_points P2C(IVIEW_WINDOW, w, LVAL, hdata)
{
  int p, n = IViewNumPoints(w);
  HistPoint* points;
  int oldcolor = 0, color = 0, use_color;
  StGWWinInfo *gwinfo;

  gwinfo = IViewWindowWinInfo(w);
  
  points = getpointdata(hdata);
  if (points == NULL) return;
  use_color = StGWUseColor(gwinfo);
  
  for (p = 0; p < n; p++)
    if (points[p].next != NOT_IN_HIST && IViewPointState(w, p) != pointNormal) {
      if (use_color) {
        oldcolor = StGWDrawColor(gwinfo);
        color = IViewPointColor(w, p);
        if (color != NOCOLOR) StGWSetDrawColor(gwinfo, color);
      }
      StGWPaintRect(gwinfo, points[p].left, points[p].top,
                            points[p].width, points[p].height);
      if (use_color && color != NOCOLOR) StGWSetDrawColor(gwinfo, oldcolor);
    }
}

LOCAL int sect_point_rect P6C(LVAL, hdata,
                              int, p,
                              int, left, int, top, int, width, int, height)
{
  int right, bottom, p_right, p_bottom;
  HistPoint* points;
  
  points = getpointdata(hdata);

  right = left + width;
  bottom = top + height;
  p_right = points[p].left + points[p].width;
  p_bottom = points[p].top + points[p].height;
  
  left = (left > points[p].left) ? left : points[p].left;
  top = (top > points[p].top) ? top : points[p].top;
  right = (right < p_right) ? right : p_right;
  bottom = (bottom < p_bottom) ? bottom : p_bottom;
  
  return((left <= right) && (top <= bottom));
}

LOCAL VOID draw_hist_point_state P4C(IVIEW_WINDOW, w, LVAL, hdata, int, p, int, state)
{
  HistPoint* points;
  int oldcolor = 0 , color = 0, use_color;
  StGWWinInfo *gwinfo;

  gwinfo = IViewWindowWinInfo(w);
  
  points = getpointdata(hdata);

  if (state == pointNormal)
    StGWEraseRect(gwinfo, points[p].left, points[p].top,
                          points[p].width, points[p].height);
  else {
    use_color = StGWUseColor(gwinfo);
    if (use_color) {
      oldcolor = StGWDrawColor(gwinfo);
      color = IViewPointColor(w, p);
      if (color != NOCOLOR) StGWSetDrawColor(gwinfo, color);
    }
    StGWPaintRect(gwinfo, points[p].left, points[p].top,
                          points[p].width, points[p].height);
    if (use_color && color != NOCOLOR) StGWSetDrawColor(gwinfo, oldcolor);
  }
  IViewSetPointScreenState(w, p, (PointState) state);
}


LOCAL VOID set_density_range P3C(IVIEW_WINDOW, w, int, y, double, density)
{
  int showing, labeled, ticks;
  double low = 0.0;
  
  IViewGetYaxis(w, &showing, &labeled, &ticks);
  if (showing) {
    GetNiceRange(&low, &density, &ticks);
    IViewSetYaxis(w, showing, labeled, ticks);
  }
  IViewSetRange(w, y, 0.0, density);
}



syntax highlighted by Code2HTML, v. 0.9.1