/* xsspin - 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 LABEL_OFFSET 3
#define AXIS_FRACTION 0.5

#define SPIN_CONTROL_HEIGHT 20
#define SPIN_CONTROL_SIZE 10
#define SPIN_CONTROL_LEFT 5
#define SPIN_CONTROL_TOP 5
#define SPIN_CONTROL_GAP 5
#define SPIN_PITCH_STRING "Pitch"
#define SPIN_ROLL_STRING  "Roll"
#define SPIN_YAW_STRING   "Yaw"
#define ALPHA 3.14159 / 72.0

#define Pitching    0
#define Rolling     1
#define Yawing      2
#define ApplyMatrix 3

/* external variables */
extern LVAL s_depth_cuing, s_showing_labels, s_showing_axes, 
  s_variable_labels, s_content_variables, s_true, s_rotation_type,
  s_rotation_angle, s_rotation_controls, sk_scale, sk_draw, sk_show,
  sk_resize, sk_redraw, sk_adjust_depth_cuing, sk_draw_axes, sk_show_window,
  sk_apply_transformation, s_pitching, s_rolling, s_yawing;
  
/* forward declarations */
LOCAL VOID set_content_variables P4H(LVAL, unsigned, unsigned, unsigned);
LOCAL VOID IViewSpinRotate P1H(IVIEW_WINDOW);
LOCAL int is_showing_axes P1H(LVAL);
LOCAL VOID set_showing_axes P2H(LVAL, int);
LOCAL int rotation_type P1H(LVAL);
LOCAL VOID set_rotation_type P2H(LVAL, int);
LOCAL VOID set_angle P2H(LVAL, double);
LOCAL double spin_angle _((LVAL object));
LOCAL int is_cuing P1H(LVAL);
LOCAL VOID set_cuing P2H(LVAL, int);
LOCAL VOID adjust_cuing P1H(LVAL);
LOCAL VOID cuing_off P1H(LVAL);

/**************************************************************************/
/**                                                                      **/
/**                      Spinner Creation Functions                      **/
/**                                                                      **/
/**************************************************************************/

LVAL iview_spin_allocate(V)
{
  LVAL object;
  int vars, i, show, ascent, height;
  IVIEW_WINDOW w;
  StGWWinInfo *gwinfo;

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

  gwinfo = StGWObWinInfo(object);
  get_iview_ivars(object, &vars);
  
  if (vars < 3) xlfail("too few variables");
  w = IViewNew(object);
  initialize_iview(w, object);
  
  for (i = 0; i < vars; i++)
    IViewSetScaledRange(w, i, -sqrt((double) vars), sqrt((double) vars));
  set_content_variables(object, 0, 1, 2);
  
  IViewSetIdentityTransformation(w);
  set_rotation_type(object, Rolling);
  set_angle(object, ALPHA);
  ascent = StGWTextAscent(gwinfo);
  height = (ascent > SPIN_CONTROL_SIZE) ? 2 * ascent : SPIN_CONTROL_HEIGHT;
  StGrSetMargin(gwinfo, 0, 0, 0, height);
  
  /* use StShowWindow to show (map) window but NOT send :resize or :redraw */
  if (show) StShowWindow(w);

  return(object);
}

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

LVAL iview_spin_content_variables(V)
{
  int x = 0, y = 0, z = 0, set = FALSE;
  LVAL object;
  
  object = xlgaobject();
  if (moreargs()) {
    set = TRUE;
    x = getfixnum(xlgafixnum());
    y = getfixnum(xlgafixnum());
    z = getfixnum(xlgafixnum());
  } 
  xllastarg();
  
  if (set) set_content_variables(object, x, y, z);

  return(slot_value(object, s_content_variables));
}

LOCAL VOID set_content_variables P4C(LVAL, object, unsigned, x, unsigned, y, unsigned, z)
{    
  StGWWinInfo *gwinfo = StGWObWinInfo(object);
  StGrSetContentVariables(gwinfo, x, y);
  set_slot_value(object, s_content_variables, integer_list_3(x, y, z));
}

static VOID get_content_variables P4C(LVAL, object, int *, x, int *, y, int *, z)
{
  LVAL val = slot_value(object, s_content_variables);
  if (consp(val) && fixp(car(val))) { 
    if (x != NULL) *x = getfixnum(car(val)); 
    val = cdr(val);
  }    
  if (consp(val) && fixp(car(val))) {
    if (y != NULL) *y = getfixnum(car(val));
    val = cdr(val);
  }    
  if (consp(val) && fixp(car(val))) {
    if (z != NULL) *z = getfixnum(car(val));
    val = cdr(val);
  }    
}

LVAL iview_spin_depth_cuing(V)
{
  LVAL object;
  
  object = xlgaobject();
  if (moreargs()) 
    set_cuing(object, (xlgetarg() != NIL) ? TRUE : FALSE);
  xllastarg();
  
  return((is_cuing(object)) ? s_true : NIL);
}

LOCAL int is_cuing P1C(LVAL, object)
{
  return((slot_value(object, s_depth_cuing) != NIL) ? TRUE : FALSE);
}

LOCAL VOID set_cuing P2C(LVAL, object, int, cuing)
{
  set_slot_value(object, s_depth_cuing, (cuing) ? s_true : NIL);    
  if (cuing) adjust_cuing(object);
  else cuing_off(object);
}

LVAL iview_spin_showing_axes(V)
{
  LVAL object;
  
  object = xlgaobject();
  if (moreargs())
    set_showing_axes(object, (xlgetarg() != NIL) ? TRUE : FALSE);
  xllastarg();
  
  return((is_showing_axes(object)) ? s_true : NIL);
}

LOCAL int is_showing_axes P1C(LVAL, object)
{
  return((slot_value(object, s_showing_axes) != NIL) ? TRUE : FALSE);
}

LOCAL VOID set_showing_axes P2C(LVAL, object, int, showing)
{
  set_slot_value(object, s_showing_axes, (showing) ? s_true : NIL);    
}

LOCAL int rotation_type P1C(LVAL, object)
{
  LVAL value = slot_value(object, s_rotation_type);

  if (symbolp(value)) {
    if (value == s_pitching) return(Pitching);
    else if (value == s_yawing) return(Yawing);
    else return(Rolling);
  }
  else if (matrixp(value)) return(ApplyMatrix);
  else return(Pitching);
}

LOCAL VOID set_rotation_type P2C(LVAL, object, int, type)
{
  LVAL value;

  switch (type) {
  case Pitching: value = s_pitching; break;
  case Rolling:  value = s_rolling;  break;
  case Yawing:   value = s_yawing;   break;
  default:       value = s_pitching; break;
  }
  set_slot_value(object, s_rotation_type, value);
}

LOCAL VOID set_angle P2C(LVAL, object, double, alpha)
{
  set_slot_value(object, s_rotation_angle, cvflonum((FLOTYPE) alpha));
}

LOCAL double spin_angle P1C(LVAL, object)
{
  LVAL value = slot_value(object, s_rotation_angle);
  
  if (floatp(value)) return(getflonum(value));
  else return(0.0);
}

LVAL iview_spin_angle(V)
{
  LVAL object;
  
  object = xlgaobject();
  if (moreargs()) set_angle(object, makefloat(xlgetarg()));
  xllastarg();
  
  return(slot_value(object, s_rotation_angle));
}

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

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

LVAL iview_spin_resize(V)
{
  IVIEW_WINDOW w;
  LVAL object;
  int vars, i, top, left, width, height, size;
  StGWWinInfo *gwinfo;
  object = xlgaobject();
  xllastarg();
  
  gwinfo = StGWObWinInfo(object);
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  if (IVIEW_WINDOW_NULL(w) || gwinfo == NULL) return(NIL);
  
  vars = IViewNumVariables(w);
  IViewSetFixedAspect(w, TRUE);
  IViewStdResize(w);
  StGrGetContentRect(gwinfo, &left, &top, &width, &height);
  size = width;
  StGrSetContentOrigin(gwinfo, left + size / 2, top + size / 2);
    
  for (i = 0; i < vars; i++)
    IViewSetScreenRange(w, i, - size / 2, size / 2);
  StGWGetViewRect(gwinfo, &left, &top, &width, &height);
  StGWSetClipRect(gwinfo, TRUE, left, top, width, height);

  return(NIL);
}

static VOID redraw_content P2C(IVIEW_WINDOW, w, LVAL, object)
{
  int left, top, width, height;
  StGWWinInfo *gwinfo = StGWObWinInfo(object);
  
  if (is_cuing(object)) adjust_cuing(object);
  StGWStartBuffering(gwinfo);
  IViewStdRedrawContent(w);
  if (is_showing_axes(object)) send_message(object, sk_draw_axes);
  StGrGetContentRect(gwinfo, &left, &top, &width, &height);
  StGWBufferToScreen(gwinfo, left, top, width, height);
}

LVAL iview_spin_draw_axes(V)
{
  IVIEW_WINDOW w;
  int x, y, xorig, yorig, size, xend, yend, vars, i, unit;
  char *s;
  double **a;
  StGWWinInfo *gwinfo;
  LVAL object;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  gwinfo = StGWObWinInfo(object);
  if (! IVIEW_WINDOW_NULL(w)) {
    StGrGetContentVariables(gwinfo, &x, &y);
    StGrGetContentOrigin(gwinfo, &xorig, &yorig);
    StGrGetContentRect(gwinfo, NULL, NULL, &size, NULL);
    vars = IViewNumVariables(w);
    a = IViewTransformation(w);

    unit = size / 2;
    for (i = 0; i < vars; i++) {
      xend = xorig + a[x][i] * AXIS_FRACTION * unit;
      yend = yorig - a[y][i] * AXIS_FRACTION * unit;
      StGWDrawLine(gwinfo, xorig, yorig, xend, yend);
      s = IViewVariableLabel(w, i);
      xend += LABEL_OFFSET;
      yend -= LABEL_OFFSET;
      StGWDrawString(gwinfo, s, xend, yend);
    }
  }
  return(NIL);
}

LVAL iview_spin_redraw_content(V)
{
  IVIEW_WINDOW w;
  LVAL object;
  
  object = xlgaobject();
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  xllastarg();
  
  if (! IVIEW_WINDOW_NULL(w)) redraw_content(w, object);
  return(NIL);
}

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

LOCAL VOID adjust_cuing P1C(LVAL, object)
{
  int vx, vy, vz;
  
  get_content_variables(object, &vx, &vy, &vz);
  send_message1(object, sk_adjust_depth_cuing, vz);
}

LOCAL VOID cuing_off P1C(LVAL, object)
{
  IVIEW_WINDOW w;
  int n, i;

  w = (IVIEW_WINDOW) GETIVIEWADDRESS(object);
  if (IVIEW_WINDOW_NULL(w)) return;

  n  = IViewNumPoints(w);
  for (i = 0; i < n; i++) IViewSetPointSymbol(w, i, 0, 5);
  n = IViewNumLines(w);
  for (i = 0; i < n; i++) IViewSetLineWidth(w, i, 1);
} 

/**************************************************************************/
/**                                                                      **/
/**                         Rotation Functions                           **/
/**                                                                      **/
/**************************************************************************/

LOCAL VOID IViewSpinRotate P1C(IVIEW_WINDOW, w)
{
  int x, y, z;
  LVAL object = IViewWindowGetObject(w);
  double alpha = spin_angle(object);
  
  get_content_variables(object, &x, &y, &z);
  
  switch (rotation_type(object)) {
  case Pitching:    IViewRotate2(w, y, z, alpha); break;
  case Rolling:     IViewRotate2(w, x, y, alpha); break;
  case Yawing:      IViewRotate2(w, x, z, alpha); break;
  case ApplyMatrix: send_message_1L(object, 
                                    sk_apply_transformation,
                                    slot_value(object, s_rotation_type));
  }
  IViewRedrawContent(w);
}

LVAL iview_spin_rotate(V)
{
  IVIEW_WINDOW w;
  
  w = (IVIEW_WINDOW) GETIVIEWADDRESS(xlgaobject());
  xllastarg();
  
  if (! IVIEW_WINDOW_NULL(w)) IViewSpinRotate(w);
  return(NIL);
}


syntax highlighted by Code2HTML, v. 0.9.1