/*
 * From: Diego Montanez <diegom@pts.mot.com>
 * To: zhao@bragg.phys.uwm.edu
 * Subject: bar graph free object
 *
 *
 * I want to make available to you my try at a bar graph free object.
 * Here is the source:
 */

/* $Header: bar_graph.h,v 1.8 95/10/16 20:18:38 diegom Exp $ */

#ifndef BAR_GRAPH_H
#define BAR_GRAPH_H

#define FL_BAR_GRAPH		1001
#define FL_NORMAL_BAR_GRAPH	   0

#define X_OFFSET		  60
#define Y_OFFSET                  20

extern FL_OBJECT *fl_create_bar_graph (int, FL_Coord, FL_Coord, FL_Coord,
				       FL_Coord, const char *);

extern FL_OBJECT *fl_add_bar_graph (int, FL_Coord, FL_Coord, FL_Coord,
				    FL_Coord, const char *);

extern void fl_set_bar_graph_value (FL_OBJECT *, double *, int);

extern void fl_show_bar_graph_dashes (FL_OBJECT *, int);

extern void fl_set_bar_graph_precision (FL_OBJECT *, int);

#endif


/* What follows is the .c program: */

/* $Header: bar_graph.c,v 1.8 95/10/16 20:18:47 diegom Exp $ */

#include "forms.h"
#include "bar_graph.h"

typedef struct
{
  double *chart_value;
  int count;
  int dashes;
  int prec;
} SPEC;

#define PROP(x)		((SPEC *)x->spec)

static double 
max_graph_value (FL_OBJECT * obj)
{
  int i;
  double max = PROP (obj)->chart_value[0];

  for (i = 0; i <= PROP (obj)->count; i++)
    if (PROP (obj)->chart_value[i] > max)
      max = PROP (obj)->chart_value[i];

  if (max < 10)
    return (max);
  if (max < 100)
    return (max + 3);
  if (max < 1000)
    return (max + 25);
  if (max < 10000)
    return (max + 150);
  if (max < 100000)
    return (max + 1500);
}

static int 
bar_graph_handler (FL_OBJECT * obj, int event, FL_Coord mx,
		   FL_Coord my, int key, void *xev)
{
  int i, tic, bw;
  double j, k = 0;
  char buf[9];

  switch (event)
    {
    case FL_DRAW:
      /* Box */
      fl_drw_box (obj->boxtype, obj->x, obj->y, obj->w, obj->h, obj->col1,
		  FL_BOUND_WIDTH);

      /* Label */
      fl_drw_text_beside (obj->align, obj->x, obj->y, obj->w, obj->h, obj->lcol,
			  obj->lsize, obj->lstyle, obj->label);

      /* x axis */
      fl_line (obj->x + X_OFFSET, obj->h - Y_OFFSET, obj->w - X_OFFSET - FL_BOUND_WIDTH,
	       1, FL_BLACK);

      /* y axis */
      fl_line (obj->x + X_OFFSET, obj->y + FL_BOUND_WIDTH, 1, obj->h - Y_OFFSET - 12,
	       FL_BLACK);

      /* xtics */
      for (i = 1; i <= PROP (obj)->count; i++)
	{
	  tic = (obj->x + X_OFFSET) + ((obj->w / (PROP (obj)->count + 2)) * i);
	  fl_line (tic, obj->h - Y_OFFSET, 1, 5, FL_BLACK);
	  sprintf (buf, "%d", i);
	  fl_drw_text (FL_ALIGN_CENTER, tic - 4, obj->h - (Y_OFFSET / 2), 10, 10, FL_BLACK,
		       FL_MEDIUM_SIZE, FL_BOLD_STYLE, buf);
	}

      /* ytics */
      j = max_graph_value (obj) / PROP (obj)->count;
      fl_drw_text (FL_ALIGN_CENTER, obj->x + X_OFFSET - 20, obj->h - Y_OFFSET - 5,
		   20, 10, FL_BLACK, FL_MEDIUM_SIZE, FL_BOLD_STYLE, "0");
      k += j;
      for (i = 1; i <= PROP (obj)->count; i++)
	{
	  tic = (obj->h - Y_OFFSET) - ((obj->h / (PROP (obj)->count + 2)) * i);
	  fl_linestyle (LineSolid);
	  fl_line (obj->x + X_OFFSET - 5, tic, 5, 1, FL_BLACK);
	  if (PROP (obj)->dashes)
	    {
	      fl_linestyle (LineOnOffDash);
	      fl_line (obj->x + X_OFFSET, tic, obj->w - X_OFFSET - FL_BOUND_WIDTH, 1,
		       FL_BLACK);
	    }
	  sprintf (buf, "%5.*f", PROP (obj)->prec, k);
	  fl_drw_text (FL_ALIGN_CENTER, obj->x + X_OFFSET - 40, tic - 5, 20, 10, FL_BLACK,
		       FL_MEDIUM_SIZE, FL_BOLD_STYLE, buf);
	  k += j;
	}

      fl_linestyle (LineSolid);

      /* bars */
      bw = ((obj->x + X_OFFSET) + ((obj->w / (PROP (obj)->count + 2)) * 2)) -
	((obj->x + X_OFFSET) + ((obj->w / (PROP (obj)->count + 2))));

      for (i = 1; i <= PROP (obj)->count; i++)
	{
	  tic = ((obj->x + X_OFFSET) + ((obj->w / (PROP (obj)->count + 2)) * i)) - (bw / 2);
	  j = (PROP (obj)->chart_value[i - 1] * (obj->h - Y_OFFSET * 3) /
	       max_graph_value (obj)) + Y_OFFSET;
	  fl_rectf (tic, obj->h - j, bw, j - Y_OFFSET, obj->col2);
	  fl_rect (tic, obj->h - j, bw, j - Y_OFFSET, FL_BLACK);

	  sprintf (buf, "%5.*f", PROP (obj)->prec, PROP (obj)->chart_value[i - 1]);
	  fl_drw_text (FL_ALIGN_CENTER, tic, (j < 50) ? obj->h - j - 20 : obj->h - j + 5,
		       bw, 20, (j < 50) ? FL_BLACK : FL_WHITE, FL_MEDIUM_SIZE, FL_BOLD_STYLE, buf);
	}
      return 0;
    }
  return 0;
}

FL_OBJECT *
fl_create_bar_graph (int type, FL_Coord x, FL_Coord y, FL_Coord w,
		     FL_Coord h, const char *label)
{
  FL_OBJECT *obj;

  obj = fl_make_object (FL_BAR_GRAPH, type, x, y, w, h, label, bar_graph_handler);
  obj->boxtype = FL_DOWN_BOX;
  obj->align = FL_ALIGN_BOTTOM;
  obj->spec = calloc (1, sizeof (SPEC));
  PROP (obj)->dashes = FALSE;
  PROP (obj)->prec = 0;
  return obj;
}

FL_OBJECT *
fl_add_bar_graph (int type, FL_Coord x, FL_Coord y, FL_Coord w,
		  FL_Coord h, const char *label)
{
  FL_OBJECT *obj = fl_create_bar_graph (type, x, y, w, h, label);

  fl_add_object (fl_current_form, obj);
  return obj;
}

void 
fl_set_bar_graph_value (FL_OBJECT * obj, double *values, int c)
{
  PROP (obj)->chart_value = values;
  PROP (obj)->count = c;
}

void 
fl_show_bar_graph_dashes (FL_OBJECT * obj, int tf)
{
  PROP (obj)->dashes = tf;
}

void 
fl_set_bar_graph_precision (FL_OBJECT * obj, int p)
{
  PROP (obj)->prec = p;
}

/*
Finally, the makefile. Change it to match your system.

#
# $Header: bar_graph.mk,v 1.1 95/10/07 20:40:47 diegom Exp $
#

CC = cc

CCOPTS1 = -Aa -c

CCOPTS2 = -Aa -O -s

LIB_SRC = bar_graph.c

TEST_SRC = free_main.c

X_INCLUDES_DIR = -I/usr/include/X11R5

X_LIBS_DIR = -L/usr/lib/X11R5

FORMS_INCLUDES_DIR = -I/tools/Xforms/FORMS

FORMS_LIBS_DIR = -L/tools/Xforms/FORMS

FORMS_LIBS = bar_graph.o -lforms

X_LIBS = -lX11 -lm

lib: bar_graph.h
	$(CC) $(CCOPTS1) $(LIB_SRC) $(X_INCLUDES_DIR) \
	$(FORMS_INCLUDES_DIR) $(X_LIBS_DIR) $(FORMS_LIBS_DIR)

*/
