'boxview' -- part 2 of 2

Kelvin Thompson kelvin at cs.utexas.edu
Wed Jul 27 13:26:19 AEST 1988


This is the second of two shar files that contain source for 'boxview',
a 3D object viewer.  The program displays a faceted object (read from a
data file) and lets the user rotate it and alter the viewing geometry
used to project it.  'Boxview' is different from other viewers because
it uses a unique, intuitive user interface that provides excellent
feedback to the user.  (I consider the standard set of Iris demos to
have *awful* feedback.)  'Boxview' will run only on an Iris, because
it makes extensive use of the Iris' architecture.  I've tested it on
a 3000-series Iris running a fairly old OS, and fairly new 4D.  The
program may not work well on Irises with 8 or fewer bitplanes.

I decided to post the program after I noticed that Siggraph next week
will have a paper entitled (roughly) "A Study in 3D Rotation with a
2D Pointing Device," which is exactly what I think 'boxview' is good at.
I thought I might grab myself some credit by letting people compare
my rotater with the one(s?) from the published paper.

Instructions for unpacking and running:

  (1) Save this article and the accompanying one into two Iris files.

  (2) Trim the text before and after the "--- cut here ---" lines
      in each of the two files.

  (3) Unpack the files by typing "sh file1" and "sh file2".

  (4) If you have a 4D system, edit 'Makefile' and remove the "-Zf"
      from the 'CFLAGS' definition.

  (5) Run "make" without parameters.

  (6) Read the documentation in README and 'boxview.1l' (the latter
      by typing "nroff -man boxview.1l".)

  (7) Run the program by typing either "boxview" or "boxview sticks.geom".

  (8) Exit 'boxview' by hitting the escape key.

Have fun.  I've got gobs more '.geom' files -- most stolen from DEC's
OFF release a year ago.  Send questions or comments to me at:

      kelvin at cs.utexas.edu      {backbone}!cs.utexas.edu!kelvin

If you want to see a live demo at Siggraph, you can contact me (Kelvin
Thompson) at the Nth Graphics booth, or at the Radisson Hotel.  I will
have a tar-tape (cartridge), but not an Iris (but maybe we can find one).

#------------------- cut this line and above ----------------------
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by cs.utexas.edu!kelvin on Tue Jul 26 21:19:44 CDT 1988
# Contents:  boxobj.c boxview.c manip.c rotate.c view.c xh.c
 
echo x - boxobj.c
sed 's/^@//' > "boxobj.c" <<'@//E*O*F boxobj.c//'
/*  FILENAME: boxobj.c

    AUTHOR:  Kelvin Thompson, 10/87

    DESCRIPTION:  Routines for handling the object(s) displayed in 
      the viewbox.
	You can modify this file to have 'boxview' display objects
      of your own creation.  Just write a new version of 'init_obj'
      (which 'boxview' calls shortly after startup) and 'draw_obj'
      (which 'boxview' calls every time it wants to draw the object).
      See the function code itself for more information.

    COPYRIGHT:  This software is Copyright 1986,1987,1988 by Kelvin 
      Thompson and carries GNU-like restrictions:  Permission is granted
      to use, copy, modify, and redistribute this software for non-
      commercial purposes, as long as copies and derivative works carry
      these same restrictions.
*/

#include <stdio.h>
#include <math.h>
#include <gl.h>
#include <device.h>
#include "ktypes.h"
#include "idraw.h"

/* programs in this file */
extern void init_obj();
extern void draw_obj();
extern void gen_sphere();
extern int gen_geomobj();

/* globals */
static Object theobj;




/*  ************************************
    *                                  *
    *       init_obj                   *
    *                                  *
    ************************************

    DESCRIPTION:  Initialize the object to be shown.
	This routine is called once, shortly after 'boxview'
      starts up.  Graphics operations have been initialized,
      but the user has not yet seen anything on the screen.
	This routine should do its initializations based only
      on input provided by the entry parameters 'argc' and 'argv'.
      This routine should not ask the user for information.
	Programmers should use calls of the form 

             cm_mapcolor( seg1, s1_OBJSTART+idx, R, G, B );

      to define color map entries.  'seg1' and 's1_OBJSTART' are
      globals;  idx varies for different index values;  R,G,B are
      the color entries in the table.  Corresponding references to
      the color map should take the form

             color( s1_OBJSTART+idx );

    ENTRY:
      argc,argv -- Standard Unix input.
*/

void init_obj ( argc, argv )
int argc;
char *argv[];
{
FILE *geomfile;

/* init */
theobj = genobj();

if ( argc == 2 )
  {
  makeobj( theobj );
    if ( ! gen_geomobj(argv[1]) )
      gen_sphere();
  closeobj();
  }
else
  {
  makeobj( theobj );
    gen_sphere();
  closeobj();
  }
}




/*  ************************************
    *                                  *
    *       draw_obj                   *
    *                                  *
    ************************************

    DESCRIPTION:  Draw the object.
	This routine is called every time 'boxview' wants to redraw
      the object it is showing.  It is called every refresh time
      (up to 60 times a second) even if the user performs no action,
      so moving objects can be displayed in this routine.
	The drawn object should fit inside the cube from (-1,-1,-1) to
      (1,1,1).  Only drawing and model-transforming calls should be made
      inside this routine (i.e. no viewing or windowing calls).
*/

void draw_obj ( )
{
pushmatrix();
  callobj( theobj );
popmatrix();
}




/*  ************************************
    *                                  *
    *       gen_sphere                 *
    *                                  *
    ************************************

    DESCRIPTION:  Generate a colorful sphere-like object.  This is
      intended to be a cheap default object, in case the real object
      can't be obtained.
*/

void gen_sphere ( )
{
/* three colors in sphere */
cm_mapcolor( seg1, s1_OBJSTART+0, 255, 100, 100 );
cm_mapcolor( seg1, s1_OBJSTART+1, 100, 255, 100 );
cm_mapcolor( seg1, s1_OBJSTART+2, 100, 100, 255 );

color( s1_OBJSTART+0 );
circ( 0.0, 0.0, 0.5 );
rotate( 90*10, 'x' );
color( s1_OBJSTART+1 );
circ( 0.0, 0.0, 0.5 );
rotate( 90*10, 'y' );
color( s1_OBJSTART+2 );
circ( 0.0, 0.0, 0.5 );
}




/*  ************************************
    *                                  *
    *       gen_geomobj                *
    *                                  *
    ************************************

    DESCRIPTION:  Load an object consisting of polygons from a file
      and store it in an Iris display list.  The object will render
      as a wireframe.

    ENTRY:  Filename of a geometry file.

    EXIT:  Returns FALSE if something went wrong, TRUE otherwise.
*/

#define MAX_POLY_EDGES 10

int gen_geomobj ( filename )
char *filename;
{
register i,j,temp;
int vcount,pcount,ecount,vindex;
register VEC3 *vertex,*vptr;
register float thisdist,maxdist;
VEC3 thispoly[MAX_POLY_EDGES];
FILE *gstream;

/* open the file */
gstream = fopen( filename, "r" );
if ( gstream == 0 )
  return FALSE;

/* read top line */
temp = fscanf( gstream, "%d %d %d", &vcount, &pcount, &ecount );
if ( temp != 3 )
  { fclose(gstream); return FALSE; }

/* allocate for vertex array */
vertex = (VEC3 *) malloc( vcount * sizeof(VEC3) );
if ( vertex == 0 )
  { fclose(gstream); return FALSE; }

/* read in vertex array */
maxdist = 0.0;
for ( 
    i = 0,        vptr = vertex; 
    i < vcount; 
    i++,          vptr++
    )
  {
  /* read in the point */
  temp = fscanf( gstream, "%f %f %f", &vptr->x, &vptr->y, &vptr->z );
  if ( temp != 3 )
    { free(vertex);  fclose(gstream);  return FALSE; }

  /* update maximum distance */
  thisdist = vptr->x * vptr->x + vptr->y * vptr->y + vptr->z * vptr->z;
  if ( thisdist > maxdist )
    maxdist = thisdist;
  }

/* scale vertex array */
maxdist = 1.0 / sqrt(maxdist);
for ( 
    i = 0,        vptr = vertex; 
    i < vcount; 
    i++,          vptr++
    )
  {
  vptr->x *= maxdist;
  vptr->y *= maxdist;
  vptr->z *= maxdist;
  }

/* define and set object color */
cm_mapcolor( seg1, s1_OBJSTART, 255, 100, 100 );
color( s1_OBJSTART );

/* read in polygons */
for ( i=0; i<pcount; i++ )
  {
  /* get edges in polygon */
  temp = fscanf( gstream, "%d", &ecount );
  if ( temp != 1 )
    { free(vertex);  fclose(gstream);  return FALSE; }

  /* loop through edges for this polygon */
  for ( j=0; j<ecount; j++ )
    {
    temp = fscanf( gstream, "%d", &vindex );
    if ( temp != 1 )
      { free(vertex);  fclose(gstream);  return FALSE; }
    vindex--;
    if ( j < MAX_POLY_EDGES )
      thispoly[j] = vertex[vindex];
    }

  /* send out polygon */
  if ( ecount <= MAX_POLY_EDGES )
    poly( ecount, thispoly );
  }

/* close and finish */
free( vertex );
fclose( gstream );
return TRUE;
}
@//E*O*F boxobj.c//
chmod u=rw,g=r,o=r boxobj.c
 
echo x - boxview.c
sed 's/^@//' > "boxview.c" <<'@//E*O*F boxview.c//'
/*  FILENAME: boxview.c

    AUTHOR:  Kelvin Thompson, 10/87

    DESCRIPTION:  Central routines for boxview program.

    COPYRIGHT:  This software is Copyright 1986,1987,1988 by Kelvin 
      Thompson and carries GNU-like restrictions:  Permission is granted
      to use, copy, modify, and redistribute this software for non-
      commercial purposes, as long as copies and derivative works carry
      these same restrictions.
*/

#define CENTRAL
#include <stdio.h>
#include <math.h>
#include <gl.h>
#include <device.h>
#include <get.h>
#include "ktypes.h"
#include "idraw.h"

/* programs in this file */
extern main();
extern void init_iris();
extern void init_globs();
extern void st_draw();
extern void readystate();

/* programs in 'rotate.c' */
extern void st_axis();

/* programs in 'wm.c' */
extern WINDOW *open_wdw();

/* programs in 'view.c' */
extern VIEW *open_view();

/* local globals */
UNS16 objplanes;




/*  *********************
    *                   *
    *     main          *
    *                   *
    *********************

    DESCRIPTION:
*/

main ( argc, argv )
int argc;
char **argv;
{
/* initialize */
init_iris();
init_globs();
init_manip();
init_view();
init_rot();
init_obj( argc, argv );

/* open main window */
w0ptr = open_wdw( wminx, wmaxx, wminy, wmaxy );
v0ptr = open_view( w0ptr );
open_xh( v0ptr );

/* initialize states */
init_states();
readystate();

while ( TRUE )
  {
  if ( !do_state() ) break;
  writemask( 0xffffffff );
  swapbuffers();
  }

/* shut down */
close_wdw( w0ptr );
close_states();
gflush();
finish();
ginit();
gexit();
}



/*  *********************
    *                   *
    *     init_iris     *
    *                   *
    *********************

    DESCRIPTION: Initialize iris and and a couple of globals.

    GLOBALS:
      mexon -- boolean indicating that we're running under 'mex'
*/

/* color table */
#define LAST_ENTRY  0xfff
typedef struct CLR_ENTRY
  {
  Colorindex  idx;
  short    r,g,b;
  } CLR_ENTRY;
static CLR_ENTRY s0_entry[] = 
  {
  { s0_2CURSOR,    255,   0,   0 },
  { s0_3WDW,       200, 200, 200 },
  { s0_2WDW,       255, 255, 255 },
  { s0_AXES,       100, 100, 255 },
  { s0_AXNAMES,    200,   0,   0 },
  { s0_XH_EDGE,    180, 180,   0 },
  { LAST_ENTRY,      0,   0,   0 }
  };
static CLR_ENTRY s1_entry[] = 
  {
  { s1_BLACK,        0,   0,   0 },
  { s1_BACKG,        0,   0,   0 },
  { s1_VW_BACKG,     0,   0,   0 },
  { s1_VW_LINES,     0,   0, 255 },
  { s1_VW_GRABS,   250,   0,   0 },
  { s1_VW_BUTT,    200,   0,   0 },
  { s1_VW_AREA,    100, 100, 100 },
  { s1_SPLAT,      200, 200,   0 },
  { LAST_ENTRY,      0,   0,   0 }
  };

void init_iris ( )
{
register i;
register struct CLR_ENTRY *clr;
int buffbits;
unsigned maxcolor;

/* mex preferences */
foreground();
prefposition( 0, XMAXSCREEN, 0, YMAXSCREEN );

/* start & init graphics */
getport( "idraw" ); 
greset();

/* see if we're running mex */
getsize( &wmaxx, &wmaxy );
mexon = ( wmaxx != 1024  ||  wmaxy != 1024 );

/* basic initialization */
color( BLACK );
clear();
onemap();
doublebuffer();
swapinterval(1);
gconfig();
setvaluator( MOUSEX, (XMAXSCREEN+1)/2, 0, XMAXSCREEN );
setvaluator( MOUSEY, (YMAXSCREEN+1)/2, 0, YMAXSCREEN );

/* enqueue all keyboard buttons */
for ( i=BUT0; i<MAXKBDBUT; i++ )
  qdevice( i );

/* load the grab font */
defrasterfont( GRAB_FONT_IDX, GRAB_MAX_HEIGHT, 
  NUM_GRAB_CHARS+1, grabchar, NUM_GRAB_RASTS, grabrast );

/* allocate the bitplane segments */
buffbits = cm_reset();
objplanes = buffbits - 3;
seg1 = cm_alloc( objplanes ) & 0xffff;
maxcolor = cm_alloc( 3 );
seg0 = maxcolor & 0xffff;
maxcolor = (1<<objplanes) - 1;

/* color segment 1 */
for ( clr = s1_entry;  clr->idx != LAST_ENTRY;  clr++ )
  cm_mapcolor( seg1, clr->idx, clr->r, clr->g, clr->b );

/* color segment 0 */
for ( clr = s0_entry;  clr->idx != LAST_ENTRY;  clr++ )
  cm_mapcolor( seg0, clr->idx, clr->r, clr->g, clr->b );

/* cursor stuff */
setcursor( 0, cm_truecolor(seg0,s0_2CURSOR), 0xffff );
curson();
}



/*  ************************************
    *                                  *
    *         init_globs               *
    *                                  *
    ************************************
  
    DESCRIPTION:  Initialize various globals.
  
    PREREQUSITES:  Need to run 'init_iris' first.
*/

void init_globs ( )
{
/* set up window bounds */
if ( FALSE && mexon ) /* mex presently uses full screen */
  {
  getorigin( &wminx, &wminy );
  wmaxx += wminx - 1;
  wmaxy += wminy - 1;
  }
else if ( getmonitor() == NTSC )
  {
  wminx = NTSC_XMIN;  wminy = NTSC_YMIN;
  wmaxx = NTSC_XMAX;  wmaxy = NTSC_YMAX;
  }
else
  {
  wminx = VP_XMIN;  wminy = VP_YMIN;
  wmaxx = VP_XMAX;  wmaxy = VP_YMAX;
  }

/* the screen-coord matrix */
map_rects( default_mtx, &screenrect, &ndcrect );
}



/*  ************************************
    *                                  *
    *       st_draw                    *
    *                                  *
    ************************************
  
    DESCRIPTION:  The main drawing state.
  
    ENTRY:  ignored
*/

void st_draw ( val1, val2 )
{
register pickcode;
register UNS16 thisleft;
static UNS16 prevleft=FALSE;

if ( !getbutton(ESCKEY) )
  {
  if ( (thisleft = getbutton(LEFTMOUSE))  &&  !prevleft )
    {
    if ( !check_view(v0ptr)  
        &&  !check_rot(v0ptr)
        &&  !check_xh(v0ptr) )
      readystate();
    }
  else
    readystate();

  prevleft = thisleft;
  }
}




/*  ************************************
    *                                  *
    *       readystate                 *
    *                                  *
    ************************************
  
    DESCRIPTION:
*/

void readystate ( )
{
use_view( v0ptr );
cm_color( seg1, s1_BACKG );
/* writemask( 0xffff ); */
/* color( 0 ); */
clear();
draw_obj();
draw_box();
draw_xh( v0ptr );
draw_view( v0ptr );
add_state( st_draw, 0, 0 );
}
@//E*O*F boxview.c//
chmod u=rw,g=r,o=r boxview.c
 
echo x - manip.c
sed 's/^@//' > "manip.c" <<'@//E*O*F manip.c//'
/*  FILENAME: manip.c

    DESCRIPTION:  Subroutines for user manipulations.

    COPYRIGHT:  This software is Copyright 1986,1987,1988 by Kelvin 
      Thompson and carries GNU-like restrictions:  Permission is granted
      to use, copy, modify, and redistribute this software for non-
      commercial purposes, as long as copies and derivative works carry
      these same restrictions.
*/

#include <gl.h>
#include <device.h>
#include "idraw.h"

/* programs in this file */
extern void init_manip();
extern void make_boxes();
extern void draw_box();
extern check_pick();

/* local variables */
static Object box_obj;



/*  ************************************
    *                                  *
    *       init_manip                 *
    *                                  *
    ************************************

    DESCRIPTION:  Manipulation initializations.
*/

void init_manip ( )
{
make_boxes();
}



/*  ************************************
    *                                  *
    *        make_boxes                *
    *                                  *
    ************************************

    DESCRIPTION:  Make object(s) for the main manipulator box.
*/

void make_boxes ( )
{
/* get the object ID */
box_obj = genobj();

/* the drawn box with axes */
makeobj( box_obj );
  cm_color( seg0, s0_3WDW );      /* the extent box */
  move( -1.0, -1.0, -1.0 );
  draw( -1.0,  1.0, -1.0 );
  draw(  1.0,  1.0, -1.0 );
  draw(  1.0, -1.0, -1.0 );
  draw( -1.0, -1.0, -1.0 );
  move( -1.0, -1.0,  1.0 );
  draw( -1.0,  1.0,  1.0 );
  draw(  1.0,  1.0,  1.0 );
  draw(  1.0, -1.0,  1.0 );
  draw( -1.0, -1.0,  1.0 );
  move( -1.0, -1.0, -1.0 );
  draw( -1.0, -1.0,  1.0 );
  move( -1.0,  1.0, -1.0 );
  draw( -1.0,  1.0,  1.0 );
  move(  1.0, -1.0, -1.0 );
  draw(  1.0, -1.0,  1.0 );
  move(  1.0,  1.0, -1.0 );
  draw(  1.0,  1.0,  1.0 );
closeobj();
}




/*  ************************************
    *                                  *
    *        draw_box                  *
    *                                  *
    ************************************

    DESCRIPTION:  Draw the main manipulator box.
*/

void draw_box ( )
{
callobj( box_obj );
}




/*  ************************************
    *                                  *
    *        check_pick                *
    *                                  *
    ************************************

    DESCRIPTION:  See if we pick anything that is drawn.

    ENTRY:
      generate -- Pointer to a function which will generate the
        objects to be matched.
      param -- parameter passed to this function

    EXIT: Returns last matched 'passthrough' code.

    PREREQUISITES:  Need to set viewport first.
*/

check_pick ( wdw, xform, generate, param )
WINDOW *wdw;
Matrix xform;
void (*generate)();
{
register long count;
register outcode;
register betwflag;
register short *codeptr;
RECT mousebox;
static Matrix pickmatx=I_INIT;

/* get the box for the mouse */
if ( ! getmousebox( wdw, &mousebox ) )
  return 0;

/* figure the transform */
qmap_rects( pickmatx, &mousebox, &ndcrect );

/* check for hits */
pushmatrix();
  loadmatrix( pickmatx );
  multmatrix( xform );
  feedback( fb_buff, FB_MAX );
  (*generate)( param );
  count = endfeedback( fb_buff );
popmatrix();

/* consolidate into a result */
outcode = 0;
betwflag = FALSE;
for ( codeptr = fb_buff;  (outcode==0) && (count>0);  --count )
  switch ( *codeptr++ )
    {
    case 8:    /* passthrough */
      if ( betwflag )
        {
        outcode = *codeptr;
        betwflag = FALSE;
        }
      codeptr++;
      --count;
      break;

    case 16:  /* move command */
    case 17:  /* draw command */
    case 18:  /* pnt command */
    case 48:  /* pmv command */
    case 49:  /* pdr command */
    case 51:  /* pclos command */
      betwflag = TRUE;
      codeptr += 2;
      count -= 2;
      break;

    case 56:  /* xfpt command */
      betwflag = TRUE;
      codeptr += 4;
      count -= 4;
      break;

    default:
      betwflag = TRUE;
      break;
    }

return outcode;
}
@//E*O*F manip.c//
chmod u=rw,g=r,o=r manip.c
 
echo x - rotate.c
sed 's/^@//' > "rotate.c" <<'@//E*O*F rotate.c//'
/*  FILENAME: rotate.c (version II)

    DESCRIPTION:  Various programs dealing with rotations.

    COPYRIGHT:  This software is Copyright 1986,1987,1988 by Kelvin 
      Thompson and carries GNU-like restrictions:  Permission is granted
      to use, copy, modify, and redistribute this software for non-
      commercial purposes, as long as copies and derivative works carry
      these same restrictions.
*/

#include <math.h>
#include <gl.h>
#include <device.h>
#include "idraw.h"

#define DRAWSPLAT  FALSE
#define DRAWCELL  FALSE

/* programs in this file */
extern void init_rot();
extern check_rot();
extern void pick_rot();
extern void st_axis();
extern void st_rotate();
extern void gen_splat();
extern void drawsplat();
extern void drawcell();
extern void draw_rot();
extern void make_wbox();
extern void gen_wbox();

/* programs in 'xform.c' */
extern void rot_x(),rot_y(),rot_z();

/* other programs */
extern void st_draw();

static Object wbox_obj;

typedef struct SPLATCELL
  {
  float  a,b;
  float  ang;
  } SPLATCELL;

/* tweakable parameters */
#define NUM_TESTS  45
#define SPLAT_ANG  (PI/5.3)
#define NEG_START  0.0
#define POS_START  0.0
#define RANGE      1.4

/* basic parameters */
#define NUM_AXES    3
#define NUM_CORNERS 4
#define NUM_DIRS    3

/* derived parameters */
#define NUM_SPLATS  (NUM_TESTS|1)  /* make it odd */
#define SPLAT_SEP   (SPLAT_ANG/(NUM_SPLATS-2.0))

/* corner constructs */
#define CORNER(a,b) (((a&1)?2:0) | ((b&1)?1:0))
static SPLATCELL cornsplat[NUM_CORNERS] =
  {
  { -1.0, -1.0, -0.75*PI },
  { -1.0,  1.0,  0.75*PI },
  {  1.0, -1.0, -0.25*PI },
  {  1.0,  1.0,  0.25*PI }
  };

/* direction symbols */
#define NO_DIR    0
#define POS_DIR   1
#define NEG_DIR   2

/* axis symbols */
#define X_AXIS    TOKCOD_X
#define Y_AXIS    TOKCOD_Y
#define Z_AXIS    TOKCOD_Z

/* splat arrays */
static SPLATCELL splat[NUM_CORNERS][NUM_DIRS][NUM_SPLATS];
static Object splatobj[NUM_AXES][NUM_CORNERS][NUM_DIRS];

/* state of rotate action */
typedef struct ROTSTATE
  {
  void    (*func)();
  float   ang;
  UNS8    axis;
  UNS8    corner;
  UNS8    dir;
  } ROTSTATE;
static ROTSTATE rstate;



/*  *********************
    *                   *
    *     init_rot      *
    *                   *
    *********************

    DESCRIPTION:  Initialize rotation stuff.
*/

void init_rot ( )
{
register corner,i,dir;
float relang,absang;
float aa,bb;

/* first splat for each direction and corner is zero offset angle */
for ( corner=0; corner<NUM_CORNERS; corner++ )
  for ( i=0; i<NUM_DIRS; i++ )
    {
    splat[corner][i][0] = cornsplat[corner];
    splat[corner][i][0].ang = 0.0;
    }

/* initialize rest of splat array */
for ( i=1; i<NUM_SPLATS; i++ )
  {
  /* neutral direction */
  relang = ((i&1)?-1:1) * ( (SPLAT_SEP*0.5) + ((i-1)>>1) * SPLAT_SEP );
  for ( corner=0; corner<NUM_CORNERS; corner++ )
    {
    absang = cornsplat[corner].ang + relang;
    splat[corner][NO_DIR][i].ang = relang;
    splat[corner][NO_DIR][i].a = SQRT_2 * cos(absang);
    splat[corner][NO_DIR][i].b = SQRT_2 * sin(absang);
    }

  /* negative direction */
  relang = NEG_START - (i-1) * SPLAT_SEP;
  for ( corner=0; corner<NUM_CORNERS; corner++ )
    {
    absang = cornsplat[corner].ang + relang;
    splat[corner][NEG_DIR][i].ang = relang;
    splat[corner][NEG_DIR][i].a = SQRT_2 * cos(absang);
    splat[corner][NEG_DIR][i].b = SQRT_2 * sin(absang);
    }

  /* positive direction */
  relang = POS_START + (i-1) * SPLAT_SEP;
  for ( corner=0; corner<NUM_CORNERS; corner++ )
    {
    absang = cornsplat[corner].ang + relang;
    splat[corner][POS_DIR][i].ang = relang;
    splat[corner][POS_DIR][i].a = SQRT_2 * cos(absang);
    splat[corner][POS_DIR][i].b = SQRT_2 * sin(absang);
    }
  }

/* initialize splat objects */
for ( corner=0; corner<NUM_CORNERS; corner++ )
  for ( dir=0; dir<NUM_DIRS; dir++ )
    {
    /* X axis */
    makeobj( splatobj[X_AXIS][corner][dir] = genobj() );
    for ( i=0; i<NUM_SPLATS; i++ )
      {
      aa = splat[corner][dir][i].a;
      bb = splat[corner][dir][i].b;
      move( -RANGE, aa, bb );
      draw(  RANGE, aa, bb );
      passthrough( i+1 );
      }
    closeobj();

    /* Y axis */
    makeobj( splatobj[Y_AXIS][corner][dir] = genobj() );
    for ( i=0; i<NUM_SPLATS; i++ )
      {
      aa = splat[corner][dir][i].a;
      bb = splat[corner][dir][i].b;
      move( bb, -RANGE, aa );
      draw( bb,  RANGE, aa );
      passthrough( i+1 );
      }
    closeobj();

    /* Z axis */
    makeobj( splatobj[Z_AXIS][corner][dir] = genobj() );
    for ( i=0; i<NUM_SPLATS; i++ )
      {
      aa = splat[corner][dir][i].a;
      bb = splat[corner][dir][i].b;
      move( aa, bb, -RANGE );
      draw( aa, bb,  RANGE );
      passthrough( i+1 );
      }
    closeobj();
    }

make_wbox();
}




/*  ************************************
    *                                  *
    *        check_rot                 *
    *                                  *
    ************************************

    DESCRIPTION:  Check rotation objects for a hit.

    EXIT:  Returns TRUE when we find something, FALSE otherwise.
*/

check_rot ( vptr )
register VIEW *vptr;
{
register UNS16 pickcode;
register WINDOW *wdw;
register out;

wdw = vptr->wdw;
viewport( wdw->edge.a.x, wdw->edge.b.x, wdw->edge.a.y, wdw->edge.b.y );
pickcode = check_pick( vptr->wdw, &vptr->total, gen_wbox );

switch ( pickcode & TOKTYP_MASK )
  {
  case TOKTYP_ROT:
    /* reactive rotation */
    pick_rot( pickcode & TOKCOD_MASK );
    draw_rot( vptr );
    out = TRUE;
    break;

  default:
    /* can't interpret -- punt */
    out = FALSE;
    break;
  }

return out;
}




/*  *********************
    *                   *
    *    pick_rot       *
    *                   *
    *********************

    DESCRIPTION:  Perform actions after picking a rotate bar.

    ENTRY:  Code for selected object.
*/

void pick_rot ( code )
UNS16 code;
{
static void (*rotfunc[NUM_AXES])() = { rot_x, rot_y, rot_z };

rstate.axis = code & TOKCOD_DIR;
rstate.dir = NO_DIR;
rstate.corner = (code & TOKPT(1,1)) >> 2;
rstate.func = rotfunc[rstate.axis];
rstate.ang = 0.0;

add_state( st_rotate, 0, 0 );
}



/*  ************************************
    *                                  *
    *        st_axis                   *
    *                                  *
    ************************************

    DESCRIPTION:  The axis rotation state.

    ENTRY:
      axis -- Axis of rotation.
      dum -- ignored dummy parameter
*/

void st_axis ( axis, dum )
UNS8 axis;
{
if ( !getbutton(LEFTMOUSE) )
  readystate();
else
  {
  switch ( axis )
    {
    case TOKCOD_X:
      rot_x( & v0ptr->box_to_rot, 2.0*PI/200.0 );
      break;

    case TOKCOD_Y:
      rot_y( & v0ptr->box_to_rot, 2.0*PI/200.0 );
      break;

    case TOKCOD_Z:
      rot_z( & v0ptr->box_to_rot, 2.0*PI/200.0 );
      break;
    }

  draw_rot( v0ptr );
  add_state( st_axis, axis, 0 );
  }
}




/*  *********************
    *                   *
    *    st_rotate      *
    *                   *
    *********************

    DESCRIPTION:  Reactive rotation

    ENTRY:
      dum1,dum2 -- ignored
*/

void st_rotate ( dum1, dum2 )
{
register i;
register SPLATCELL *splatptr;
register float ang;
UNS16 pickcode;

/* go back to waiting state if no mouse button */
if ( !getbutton(LEFTMOUSE) )
  readystate();

else
  {
#  if DRAWSPLAT
    drawsplat();
#  endif

  pickcode = check_pick( v0ptr->wdw, &v0ptr->total, gen_splat );

  if ( pickcode == 0 )
    {
    if ( rstate.dir != NO_DIR )
      (*rstate.func)( & v0ptr->box_to_rot, rstate.ang );
    rstate.dir = NO_DIR;
    }
  else
    {
#    if DRAWCELL
      drawcell( pickcode-1 );
#    endif

    rstate.ang = ang = splat[rstate.corner][rstate.dir][pickcode-1].ang;
    if ( ang == 0.0 )
      rstate.dir = NO_DIR;
    else
      {
      (*rstate.func)( & v0ptr->box_to_rot, ang );
      rstate.dir = (ang>0.0) ? POS_DIR : NEG_DIR;
      }
    }

  draw_rot( v0ptr );
  add_state( st_rotate, 0, 0 );
  }
}




/*  ************************************
    *                                  *
    *          gen_splat               *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void gen_splat ( )
{
callobj( splatobj[rstate.axis][rstate.corner][rstate.dir] );
}



#if DRAWSPLAT
/*  *********************
    *                   *
    *    drawsplat      *
    *                   *
    *********************

    DESCRIPTION:

    ENTRY:
*/

void drawsplat ( )
{
register i;
register SPLATCELL *splatptr;

cm_color( seg1, s1_SPLAT );
switch ( rstate.axis )
  {
  case X_AXIS:
    for (
      i = 0,      splatptr = splat[rstate.corner][rstate.dir];
      i < NUM_SPLATS;
      i++,      splatptr++
      )
      {
      move( -RANGE, splatptr->a, splatptr->b );
      draw(  RANGE, splatptr->a, splatptr->b );
      }
    break;

  case Y_AXIS:
    for (
      i = 0,      splatptr = splat[rstate.corner][rstate.dir];
      i < NUM_SPLATS;
      i++,      splatptr++
      )
      {
      move( splatptr->b, -RANGE, splatptr->a );
      draw( splatptr->b,  RANGE, splatptr->a );
      }
    break;

  case Z_AXIS:
    for (
      i = 0,      splatptr = splat[rstate.corner][rstate.dir];
      i < NUM_SPLATS;
      i++,      splatptr++
      )
      {
      move( splatptr->a, splatptr->b, -RANGE );
      draw( splatptr->a, splatptr->b,  RANGE );
      }
    break;
  }
}
#endif



#if DRAWCELL
/*  *********************
    *                   *
    *   drawcell        *
    *                   *
    *********************

    DESCRIPTION:

    ENTRY:
*/

void drawcell ( idx )
UNS16 idx;
{
register SPLATCELL *splatptr;

splatptr = & splat[rstate.corner][rstate.dir][idx];
finish();
@@@color( RED );
switch ( rstate.axis )
  {
  case X_AXIS:
    move( -RANGE, splatptr->a, splatptr->b );
    draw(  RANGE, splatptr->a, splatptr->b );
    break;

  case Y_AXIS:
    move( splatptr->b, -RANGE, splatptr->a );
    draw( splatptr->b,  RANGE, splatptr->a );
    break;

  case Z_AXIS:
    move( splatptr->a, splatptr->b, -RANGE );
    draw( splatptr->a, splatptr->b,  RANGE );
    break;
  }
}
#endif



/*  ************************************
    *                                  *
    *        make_wbox                 *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void make_wbox ( )
{
wbox_obj = genobj();

/* the marked box with axes (for feedback) */
makeobj( wbox_obj );
  /* do the edges of the box */
  move( -1.0, -1.0, -1.0 );
  draw( -1.0,  1.0, -1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Y | TOKPT(0,0) );
  draw(  1.0,  1.0, -1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_X | TOKPT(1,0) );
  draw(  1.0, -1.0, -1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Y | TOKPT(0,1) );
  draw( -1.0, -1.0, -1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_X | TOKPT(0,0) );
  move( -1.0, -1.0,  1.0 );
  draw( -1.0,  1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Y | TOKPT(1,0) );
  draw(  1.0,  1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_X | TOKPT(1,1) );
  draw(  1.0, -1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Y | TOKPT(1,1) );
  draw( -1.0, -1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_X | TOKPT(0,1) );
  move( -1.0, -1.0, -1.0 );
  draw( -1.0, -1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Z | TOKPT(0,0) );
  move( -1.0,  1.0, -1.0 );
  draw( -1.0,  1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Z | TOKPT(0,1) );
  move(  1.0, -1.0, -1.0 );
  draw(  1.0, -1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Z | TOKPT(1,0) );
  move(  1.0,  1.0, -1.0 );
  draw(  1.0,  1.0,  1.0 );
    passthrough( TOKTYP_ROT | TOKCOD_Z | TOKPT(1,1) );

#if 0
  /* do the axes */
  move( 0.0, 0.0, 0.0 );
  draw( 1.5, 0.0, 0.0 );
    passthrough( TOK_XAXIS );
  move( 0.0, 0.0, 0.0 );
  draw( 0.0, 1.5, 0.0 );
    passthrough( TOK_YAXIS );
  move( 0.0, 0.0, 0.0 );
  draw( 0.0, 0.0, 1.5 );
    passthrough( TOK_ZAXIS );
#endif
closeobj();
}



/*  ************************************
    *                                  *
    *        gen_wbox                  *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void gen_wbox ( )
{
callobj( wbox_obj );
}




/*  ************************************
    *                                  *
    *      draw_rot                    *
    *                                  *
    ************************************

    DESCRIPTION:
*/

void draw_rot ( vptr )
VIEW *vptr;
{
use_view( vptr );
cm_color( seg1, s1_BACKG );
clear();
draw_obj();
draw_box();
draw_xh( vptr );
}
@//E*O*F rotate.c//
chmod u=rw,g=r,o=r rotate.c
 
echo x - view.c
sed 's/^@//' > "view.c" <<'@//E*O*F view.c//'
/*  FILENAME: view.c

    DESCRIPTION:  Programs for manipulating viewing geometry.

    COPYRIGHT:  This software is Copyright 1986,1987,1988 by Kelvin 
      Thompson and carries GNU-like restrictions:  Permission is granted
      to use, copy, modify, and redistribute this software for non-
      commercial purposes, as long as copies and derivative works carry
      these same restrictions.
*/

#include <gl.h>
#include <device.h>
#include "idraw.h"

/* programs in this file */
extern void init_view();
extern VIEW *open_view();
extern void use_view();
extern void calc_vwtot();
extern void gen_view();
extern void draw_view();
extern void draw_vmanip();
extern void draw_vbutt();
extern check_view();
extern void runwind();
extern void runwend();
extern void runeye();
extern void runorg();
extern void donevmanip();
extern void draw_main();

/* programs in 'wm.c' */
extern WINDOW *open_wdw();

#define VW_SIZX    100
#define VW_SIZY    80
#define VW_RATIO   ((float)VW_SIZY/(float)VW_SIZX)
#define VW_BUTTRAD 8

static VIEW *now_vptr;
static VIEWGEOM prevgeom;
static float prev30,prev31,prev33;




/*  ************************************
    *                                  *
    *       init_view                  *
    *                                  *
    ************************************

    DESCRIPTION:
*/

void init_view ( )
{
}




/*  ************************************
    *                                  *
    *       open_view                  *
    *                                  *
    ************************************

    DESCRIPTION:

    EXIT:
*/

VIEW *open_view ( wdw )
register WINDOW *wdw;
{
register VIEW *vptr;

/* allocate & link */
vptr = (VIEW *)malloc( sizeof(VIEW) );
vptr->wdw = wdw;

/* viewing parameters */
vptr->g.R = VP_R;
vptr->g.D = VP_D;
vptr->g.wx = VP_WX;
vptr->g.wy = vptr->g.wx
  * (float)(wdw->edge.b.y - wdw->edge.a.y + 1)
  / (float)(wdw->edge.b.x - wdw->edge.a.x + 1);

/* matricies */
vptr->box_to_rot.a = I;
set_view( &vptr->rot_to_ndc, vptr->g.R, vptr->g.D, vptr->g.wx, vptr->g.wy );
vptr->total.a = vptr->rot_to_ndc.a;
calc_vwtot( vptr );

/* button's edges */
vptr->butt.a.x = wdw->edge.a.x;
vptr->butt.a.y = wdw->edge.a.y;
vptr->butt.b.x = vptr->butt.a.x + VW_BUTTRAD;
vptr->butt.b.y = vptr->butt.a.y + VW_BUTTRAD;

/* view-manipulation window */
vptr->vw = open_wdw( wdw->edge.a.x, wdw->edge.a.x+VW_SIZX,
  wdw->edge.a.y, wdw->edge.a.y+VW_SIZY );

vptr->viewon = FALSE;

return vptr;
}




/*  ************************************
    *                                  *
    *       use_view                   *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void use_view ( vptr )
register VIEW *vptr;
{
use_wdw( vptr->wdw );
loadmatrix( & vptr->rot_to_ndc );
multmatrix( & vptr->box_to_rot );
getmatrix( & vptr->total );
}




/*  ************************************
    *                                  *
    *       reuse_view                 *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void reuse_view ( vptr )
register VIEW *vptr;
{
use_wdw( vptr->wdw );
loadmatrix( & vptr->total );
}




/*  ************************************
    *                                  *
    *        check_view                *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:

    EXIT:
*/

check_view ( vptr )
register VIEW *vptr;
{
register long mx,my;
register pickcode;
VEC3 vec1,vec2;
SCRNRECT srect;

pickcode = 0;
mx = getvaluator( MOUSEX );
my = getvaluator( MOUSEY );

/* see if we're toggling the view window */
if ( vptr->butt.a.x <= mx  &&  mx <= vptr->butt.b.x
    &&  vptr->butt.a.y <= my  &&  my <= vptr->butt.b.y )
  {
  vptr->viewon = ! vptr->viewon;
  readystate();
  pickcode = TRUE;
  }

/* see if we're trying to change the view */
else if ( vptr->viewon )
  {
  /* check for a pick */
  vp_wdw( vptr->vw );
  loadmatrix( &vptr->vwtot );
  now_vptr = vptr;
  pickcode = check_pick( vptr->vw, &vptr->vwtot, gen_view );

  /* see if we hit any view manipulators */
  if ( (pickcode & TOKTYP_MASK) != TOKTYP_VW )
    {
    pickcode = 0;
    }
  else
    {
    /* remember the previous geometry */
    prevgeom = vptr->g;

    switch ( pickcode )
      {
      case TOK_VORG:
        /* try to map part of the X-axis to screen coords */
        vec1.x = 0.0;  vec1.y = 0.0;  vec1.z = 0.0;
        vec2.x = vptr->g.D - vptr->g.R;  vec2.y = 0.0;  vec2.z = 0.0;
        if ( !getslidepts( &vec1, &vec2, &srect ) )
          goto badpick;

        /* remember stuff */
        prev30 = vptr->vwtot.m[3][0];
        prev31 = vptr->vwtot.m[3][1];
        prev33 = vptr->vwtot.m[3][3];

        /* slide */
        doslide( &srect, runorg, donevmanip );
        break;

      case TOK_VEYE:
        /* try to map part of the X-axis to screen coords */
        vec1.x = -vptr->g.R;  vec1.y = 0.0;  vec1.z = 0.0;
        vec2.x = vptr->g.D - vptr->g.R;  vec2.y = 0.0;  vec2.z = 0.0;
        if ( !getslidepts( &vec1, &vec2, &srect ) )
          goto badpick;

        /* slide */
        doslide( &srect, runeye, donevmanip );
        break;

      case TOK_VWTOP:
        /* try to map part of the X-axis to screen coords */
        vec1.x = vptr->g.D - vptr->g.R;  vec1.y = 0.0;  vec1.z = 0.0;
        vec2.x = vec1.x;  vec2.y = vptr->g.wy;  vec2.z = 0.0;
        if ( !getslidepts( &vec1, &vec2, &srect ) )
          goto badpick;

        /* slide */
        doslide( &srect, runwend, donevmanip );
        break;

      case TOK_VWBOT:
        /* try to map part of the X-axis to screen coords */
        vec1.x = vptr->g.D - vptr->g.R;  vec1.y = 0.0;  vec1.z = 0.0;
        vec2.x = vec1.x;  vec2.y = -vptr->g.wy;  vec2.z = 0.0;
        if ( !getslidepts( &vec1, &vec2, &srect ) )
          goto badpick;

        /* slide */
        doslide( &srect, runwend, donevmanip );
        break;

      case TOK_VWIND:
        /* try to map part of the X-axis to screen coords */
        vec1.x = -vptr->g.R;  vec1.y = 0.0;  vec1.z = 0.0;
        vec2.x =        0.0;  vec2.y = 0.0;  vec2.z = 0.0;
        if ( !getslidepts( &vec1, &vec2, &srect ) )
          goto badpick;

        /* slide */
        doslide( &srect, runwind, donevmanip );
        break;

      default:
      badpick:
        ringbell();
        readystate();
        break;
      }
    }
  }

return pickcode;
}




/*  ************************************
    *                                  *
    *       draw_view                  *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void draw_view ( vptr )
register VIEW *vptr;
{
if ( vptr->viewon )
  draw_vmanip( vptr );
draw_vbutt( vptr );
}





/*  ************************************
    *                                  *
    *          draw_vmanip             *
    *                                  *
    ************************************

    DESCRIPTION:  Draw view manipulation structures.

    ENTRY:
*/

void draw_vmanip ( vptr )
register VIEW *vptr;
{
register float temp;

/* set up the view window */
use_wdw( vptr->vw );
loadmatrix( & vptr->vwtot );
cm_color( seg1, s1_VW_BACKG );
clear();

/* draw the clipping box */
cm_color( seg1, s1_VW_AREA );
circf( 0.0, 0.0, SQRT_2 );

/* draw the axis */
cm_color( seg1, s1_VW_LINES );
move2( -100.0, 0.0 );
draw2(  100.0, 0.0 );

/* draw the window plane */
temp = vptr->g.D - vptr->g.R;
move2( temp, -vptr->g.wy );
draw2( temp,  vptr->g.wy );

/* draw the fostrum edges */
move2( -vptr->g.R, 0.0 );
rdr2( vptr->g.D * 100.0,  -vptr->g.wy * 100.0 );
move2( -vptr->g.R, 0.0 );
rdr2( vptr->g.D * 100.0,   vptr->g.wy * 100.0 );

/* draw the selectable markers */
font( GRAB_FONT_IDX );
cm_color( seg1, s1_VW_GRABS );
cmov2( -vptr->g.R,          0.0 );  charstr( "\001" );
cmov2(        0.0,          0.0 );  charstr( "\001" );
cmov2(        temp, -vptr->g.wy );  charstr( "\001" );
cmov2(        temp,         0.0 );  charstr( "\001" );
cmov2(        temp,  vptr->g.wy );  charstr( "\001" );
}




/*  ************************************
    *                                  *
    *        draw_vbutt                *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void draw_vbutt ( vptr )
register VIEW *vptr;
{
viewport( 0, XMAXSCREEN, 0, YMAXSCREEN );
loadmatrix( default_mtx );
cm_color( seg1, s1_VW_BUTT );
rectfi( vptr->butt.a.x, vptr->butt.a.y, vptr->butt.b.x, vptr->butt.b.y );
}




/*  ************************************
    *                                  *
    *      calc_vwtot                  *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

#define VW_PAD  0.1

void calc_vwtot ( vptr )
register VIEW *vptr;
{
register float wantdx,wantdy,factor;
RECT ext;

/* calculate the rectangle we want to see */
ext.a.x = -(1.0+VW_PAD) * vptr->g.R;
ext.b.x =       VW_PAD  * vptr->g.R;
ext.b.y =  (1.0+VW_PAD) * vptr->g.wy;
ext.a.y = -ext.b.y;

/* calculate width, height, and ratio */
wantdx = ext.b.x - ext.a.x;
wantdy = ext.b.y - ext.a.y;

/* expand the rect to make its ratio fit */
if ( wantdy / wantdx > VW_RATIO )
  {
  /* window too tall -- need to make wider */
  factor = wantdy / (wantdx * VW_RATIO);
  ext.a.x += (1.0 - factor) * 0.5 * wantdx;
  ext.b.x = ext.a.x + wantdx * factor;
  }
else
  {
  /* window too narrow -- need to make taller */
  factor = (wantdx * VW_RATIO) / wantdy;
  ext.a.y += (1.0 - factor) * 0.5 * wantdy;
  ext.b.y = ext.a.y + wantdy * factor;
  }

map_rects( &vptr->vwtot, &ext, &ndcrect );
}




/*  ************************************
    *                                  *
    *        gen_view                  *
    *                                  *
    ************************************

    DESCRIPTION:
*/

void gen_view ( )
{
register float temp;
register VIEW *vptr;

vptr = now_vptr;
temp = vptr->g.D - vptr->g.R;

pnt2( -vptr->g.R,         0.0 );  passthrough( TOK_VEYE );
pnt2(        0.0,         0.0 );  passthrough( TOK_VORG );
pnt2(       temp, -vptr->g.wy );  passthrough( TOK_VWBOT );
pnt2(       temp,         0.0 );  passthrough( TOK_VWIND );
pnt2(       temp,  vptr->g.wy );  passthrough( TOK_VWTOP );
}




/*  ************************************
    *                                  *
    *       runwind                    *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void runwind ( tt )
register float tt;
{
register VIEW *vptr;

/* initialize */
vptr = now_vptr;

/* adjust 'tt' */
if ( tt < 0.1 )
  tt = 0.1;
else if ( 0.9 < tt )
  tt = 0.9;

/* adjust the parameters */
vptr->g.D = tt * prevgeom.R;
set_view( &vptr->rot_to_ndc, vptr->g.R, vptr->g.D, vptr->g.wx, vptr->g.wy );

/* draw the main 3-box */
draw_main( vptr );
}




/*  ************************************
    *                                  *
    *         donevmanip               *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void donevmanip ( tt )
register float tt;
{
calc_vwtot( now_vptr );
}




/*  ************************************
    *                                  *
    *         runwend                  *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void runwend ( tt )
register float tt;
{
register VIEW *vptr;

/* initialize */
vptr = now_vptr;

/* adjust 'tt' */
if ( tt < 0.1 )
  tt = 0.1;
else if ( 2.0 < tt )
  tt = 2.0;

/* adjust the parameters */
vptr->g.wy = tt * prevgeom.wy;
vptr->g.wx = tt * prevgeom.wx;
set_view( &vptr->rot_to_ndc, vptr->g.R, vptr->g.D, vptr->g.wx, vptr->g.wy );

/* draw the main 3-box */
draw_main( vptr );
}




/*  ************************************
    *                                  *
    *           runeye                 *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void runeye ( tt )
register float tt;
{
register VIEW *vptr;

/* initialize */
vptr = now_vptr;

/* adjust 'tt' */
if ( tt < -2.0 )
  tt = -2.0;
else if ( tt > 0.9 )
  tt = 0.9;

/* adjust the parameters */
vptr->g.R = prevgeom.R - tt * prevgeom.D;
vptr->g.D = prevgeom.D - (prevgeom.R - vptr->g.R);
set_view( &vptr->rot_to_ndc, vptr->g.R, vptr->g.D, vptr->g.wx, vptr->g.wy );

/* draw the main 3-box */
draw_main( vptr );
}




/*  ************************************
    *                                  *
    *           runorg                 *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void runorg ( tt )
register float tt;
{
register VIEW *vptr;
register float dx;

/* initialize */
vptr = now_vptr;

/* adjust 'tt' */
if ( tt < -2.0 )
  tt = -2.0;
else if ( tt > 0.9 )
  tt = 0.9;

/* adjust the viewing geometry */
vptr->g.R = prevgeom.R - tt * (prevgeom.R - prevgeom.D);
set_view( &vptr->rot_to_ndc, vptr->g.R, vptr->g.D, vptr->g.wx, vptr->g.wy );

/* adjust the transform for the view window */
dx = prevgeom.R - vptr->g.R;
vptr->vwtot.m[3][0] = prev30 - dx * vptr->vwtot.m[0][0];
vptr->vwtot.m[3][1] = prev31 - dx * vptr->vwtot.m[0][1];
vptr->vwtot.m[3][3] = prev33 - dx * vptr->vwtot.m[0][3];

/* draw the main 3-box */
draw_main( vptr );
}




/*  ************************************
    *                                  *
    *         draw_main                *
    *                                  *
    ************************************

    DESCRIPTION:

    ENTRY:
*/

void draw_main ( vptr )
VIEW *vptr;
{
use_view( vptr );
cm_color( seg1, s1_BACKG );
clear();
draw_obj();
draw_box();
draw_vmanip( vptr );
}
@//E*O*F view.c//
chmod u=rw,g=r,o=r view.c
 
echo x - xh.c
sed 's/^@//' > "xh.c" <<'@//E*O*F xh.c//'
/*  FILENAME: xh.c

    DESCRIPTION:  Deal with the crosshairs

    COPYRIGHT:  This software is Copyright 1986,1987,1988 by Kelvin 
      Thompson and carries GNU-like restrictions:  Permission is granted
      to use, copy, modify, and redistribute this software for non-
      commercial purposes, as long as copies and derivative works carry
      these same restrictions.
*/


#include <stdio.h>
#include <gl.h>
#include <device.h>
#include "idraw.h"

/* programs in this file */
extern void init_xh();
extern void open_xh();
extern void draw_xh();
extern check_xh();
extern void st_xhmov2();
extern void map2d3d();
/*extern unsigned mostest(); */

/* programs in other files */
extern void st_axis();

#define EDGE_FLAG  (TOKCOD_p << 1)
#define MOUSE_MAX  512
#define FABS(x)    fabs(x)

typedef struct MOUSEFLAT
  {
  VIEW  *vptr;
  int    xsign,ysign;
  } MOUSEFLAT;




/*  ************************************
    *                                  *
    *        init_xh                   *
    *                                  *
    ************************************

    DESCRIPTION:  Initialize crosshairs.
*/

void init_xh ( )
{
}




/*  ************************************
    *                                  *
    *        open_xh                   *
    *                                  *
    ************************************

    DESCRIPTION:  Open crosshairs.

    ENTRY:
      vptr -- Pointer to VIEW struct to hold crosshair info.
*/

void open_xh ( vptr )
register VIEW *vptr;
{
vptr->xhon = TRUE;
vptr->xhdir = FALSE;
vptr->xhpos.x = 0.0;
vptr->xhpos.y = 0.0;
vptr->xhpos.z = 0.0;
}




/*  ************************************
    *                                  *
    *         draw_xh                  *
    *                                  *
    ************************************

    DESCRIPTION:  Draw the 3-D crosshairs.

    ENTRY:

    EXIT:
*/

void draw_xh ( vptr )
register VIEW *vptr;
{
if ( vptr->xhon )
  {
  /* crosshair color */
  cm_color( seg0, s0_AXES );

  /* X hair */
  move(          -1.0, vptr->xhpos.y, vptr->xhpos.z );
  draw(           1.0, vptr->xhpos.y, vptr->xhpos.z );

  /* Y hair */
  move( vptr->xhpos.x,          -1.0, vptr->xhpos.z );
  draw( vptr->xhpos.x,           1.0, vptr->xhpos.z );

  /* Z hair */
  move( vptr->xhpos.x, vptr->xhpos.y,          -1.0 );
  draw( vptr->xhpos.x, vptr->xhpos.y,           1.0 );

  /* edges */
  if ( vptr->xhdir )
    {
    cm_color( seg0, s0_XH_EDGE );
    switch ( vptr->xhdir & TOKCOD_DIR )
      {
      case TOKCOD_X:
        move( vptr->xhpos.x, -1.0, -1.0 );
        draw( vptr->xhpos.x,  1.0, -1.0 );
        draw( vptr->xhpos.x,  1.0,  1.0 );
        draw( vptr->xhpos.x, -1.0,  1.0 );
        draw( vptr->xhpos.x, -1.0, -1.0 );
        break;

      case TOKCOD_Y:
        move( -1.0, vptr->xhpos.y, -1.0 );
        draw( -1.0, vptr->xhpos.y,  1.0 );
        draw(  1.0, vptr->xhpos.y,  1.0 );
        draw(  1.0, vptr->xhpos.y, -1.0 );
        draw( -1.0, vptr->xhpos.y, -1.0 );
        break;

      case TOKCOD_Z:
        move( -1.0, -1.0, vptr->xhpos.z );
        draw(  1.0, -1.0, vptr->xhpos.z );
        draw(  1.0,  1.0, vptr->xhpos.z );
        draw( -1.0,  1.0, vptr->xhpos.z );
        draw( -1.0, -1.0, vptr->xhpos.z );
        break;
      }
    }

  /* labels */
  font( 0 );
  cm_color( seg0, s0_AXNAMES );
  cmov(            1.0, vptr->xhpos.y, vptr->xhpos.z );  charstr( "X" );
  cmov(  vptr->xhpos.x,           1.0, vptr->xhpos.z );  charstr( "Y" );
  cmov(  vptr->xhpos.x, vptr->xhpos.y,           1.0 );  charstr( "Z" );
  }
}




/*  ************************************
    *                                  *
    *           gen_xh                 *
    *                                  *
    ************************************

    DESCRIPTION:  Generate feedback instructions for
      3D crosshairs.

    ENTRY:
*/

void gen_xh ( vptr )
register VIEW *vptr;
{
move(           0.0, vptr->xhpos.y, vptr->xhpos.z );
draw(          -1.0, vptr->xhpos.y, vptr->xhpos.z );
passthrough( TOK_XH_Xn );

move(           0.0, vptr->xhpos.y, vptr->xhpos.z );
draw(           1.0, vptr->xhpos.y, vptr->xhpos.z );
passthrough( TOK_XH_Xp );

move( vptr->xhpos.x,           0.0, vptr->xhpos.z );
draw( vptr->xhpos.x,          -1.0, vptr->xhpos.z );
passthrough( TOK_XH_Yn );

move( vptr->xhpos.x,           0.0, vptr->xhpos.z );
draw( vptr->xhpos.x,           1.0, vptr->xhpos.z );
passthrough( TOK_XH_Yp );

move( vptr->xhpos.x, vptr->xhpos.y,           0.0 );
draw( vptr->xhpos.x, vptr->xhpos.y,          -1.0 );
passthrough( TOK_XH_Zn );

move( vptr->xhpos.x, vptr->xhpos.y,           0.0 );
draw( vptr->xhpos.x, vptr->xhpos.y,           1.0 );
passthrough( TOK_XH_Zp );
}




/*  ************************************
    *                                  *
    *       check_xh                   *
    *                                  *
    ************************************

    DESCRIPTION:  See if mouse over crosshairs.

    ENTRY:

    EXIT:  Returns status code.
*/

check_xh ( vptr )
register VIEW *vptr;
{
register UNS16 pickcode;
UNS16 pos;
register WINDOW *wdw;
register out;
short xinit,yinit;
static MOUSEFLAT thisflat;

wdw = vptr->wdw;
viewport( wdw->edge.a.x, wdw->edge.b.x, wdw->edge.a.y, wdw->edge.b.y );
pickcode = check_pick( vptr->wdw, &vptr->total, gen_xh, vptr );

if ( (pickcode & TOKTYP_MASK) != TOKTYP_XH )
  {
  out = FALSE;
  }
else
  {
  /* init */
  pos = pickcode & TOKCOD_p;
  vptr->xhdir = EDGE_FLAG | (pickcode & TOKCOD_DIRp);

  /* redraw screen */
  cursoff();
  draw_rot( vptr );

  /* figure mapping of mouse to 3d crosshairs */
  thisflat.vptr = vptr;
  thisflat.xsign = 1;
  thisflat.ysign = 1;
  map2d3d( & thisflat );  /* modifies 'thisflat' */

  /* reset the mouse scaling */
  switch ( vptr->xhdir & TOKCOD_DIRp )
    {
    case TOKCOD_Xp:
      xinit = vptr->xhpos.y * MOUSE_MAX;
      yinit = vptr->xhpos.z * MOUSE_MAX;
      break;
    case TOKCOD_Xn:
      xinit = vptr->xhpos.z * MOUSE_MAX;
      yinit = vptr->xhpos.y * MOUSE_MAX;
      break;
    case TOKCOD_Yp:
      xinit = vptr->xhpos.z * MOUSE_MAX;
      yinit = vptr->xhpos.x * MOUSE_MAX;
      break;
    case TOKCOD_Yn:
      xinit = vptr->xhpos.x * MOUSE_MAX;
      yinit = vptr->xhpos.z * MOUSE_MAX;
      break;
    case TOKCOD_Zp:
      xinit = vptr->xhpos.x * MOUSE_MAX;
      yinit = vptr->xhpos.y * MOUSE_MAX;
      break;
    case TOKCOD_Zn:
      xinit = vptr->xhpos.y * MOUSE_MAX;
      yinit = vptr->xhpos.x * MOUSE_MAX;
      break;
    }
  xinit *= thisflat.xsign;
  yinit *= thisflat.ysign;
  setvaluator( MOUSEX, xinit, -MOUSE_MAX,  MOUSE_MAX );
  setvaluator( MOUSEY, yinit, -MOUSE_MAX,  MOUSE_MAX );

  /* set new state */
  add_state( st_xhmov2, &thisflat, 0 );

  out = TRUE;
  }

return out;
}




/*  ************************************
    *                                  *
    *          st_xhmov2               *
    *                                  *
    ************************************

    DESCRIPTION:  Move the crosshair position in 2D.

    ENTRY:
      axis -- code for frozen axis
*/

void st_xhmov2 ( thisflat, dum )
register MOUSEFLAT *thisflat;
{
register VIEW *vptr;
register float xpos,ypos;

/* init */
vptr = thisflat->vptr;

if ( ! getbutton(LEFTMOUSE) )
  {
  VEC4 xhwld,xhscn;
  XFORM toscreen;
  short xx,yy;

  /* reset variables */
  vptr->xhdir = FALSE;

  /* */
  map_rects( &toscreen, &ndcrect, &vptr->wdw->bounds );
  mmul( &toscreen, &vptr->total, &toscreen );
  xhwld.x = vptr->xhpos.x;
  xhwld.y = vptr->xhpos.y;
  xhwld.z = vptr->xhpos.z;
  xhwld.w = 1.0;
  vmul( &xhscn, &xhwld, &toscreen );
  xx = xhscn.x / xhscn.w;
  yy = xhscn.y / xhscn.w;

  /* reset mouse scaling */
  setvaluator( MOUSEX, xx, 0, XMAXSCREEN );
  setvaluator( MOUSEY, yy, 0, YMAXSCREEN );
  curson();

  /* go to base state */
  readystate();
  }
else
  {
  /* get 2D real mouse position */
  xpos = thisflat->xsign * getvaluator( MOUSEX ) * (1.0/(float)MOUSE_MAX);
  ypos = thisflat->ysign * getvaluator( MOUSEY ) * (1.0/(float)MOUSE_MAX);

  /* change the position */
  switch ( vptr->xhdir & TOKCOD_DIRp )
    {
    case TOKCOD_Xp:
      vptr->xhpos.z = ypos;
      vptr->xhpos.y = xpos;
      break;

    case TOKCOD_Xn:
      vptr->xhpos.z = xpos;
      vptr->xhpos.y = ypos;
      break;

    case TOKCOD_Yp:
      vptr->xhpos.z = xpos;
      vptr->xhpos.x = ypos;
      break;

    case TOKCOD_Yn:
      vptr->xhpos.z = ypos;
      vptr->xhpos.x = xpos;
      break;

    case TOKCOD_Zp:
      vptr->xhpos.x = xpos;
      vptr->xhpos.y = ypos;
      break;

    case TOKCOD_Zn:
      vptr->xhpos.x = ypos;
      vptr->xhpos.y = xpos;
      break;
    }

  draw_rot( v0ptr );
  add_state( st_xhmov2, thisflat, 0 );
  }
}



/*  ************************************
    *                                  *
    *        map2d3d                   *
    *                                  *
    ************************************

    DESCRIPTION:  Fill 'vptr->xhdir' and '?sign' in
      a MOUSEFLAT struct to determine how the mouse moves
      the 3D crosshairs.

    ENTRY:
*/

#define SLPD(ax,dir) FABS(axslp[ax][dir])
#define MOSTER(ax1,ax2,dir)                                         \
  (                                                                 \
    ( SLPD(ax1,dir) > SLPD(ax2,dir) )                               \
      ? ( (axslp[ax1][dir] > 0.0)  ?  (ax1) | TOKCOD_p  :  (ax1) )  \
      : ( (axslp[ax2][dir] > 0.0)  ?  (ax2) | TOKCOD_p  :  (ax2) )  \
  )

void map2d3d ( fptr )
register MOUSEFLAT *fptr;
{
register VIEW *vptr;
register XFORM *total;
UNS16 upaxis,rtaxis;
Matrix axslp;  /* axis slopes */

/* init */
vptr = fptr->vptr;
total = & vptr->total;
vptr->xhdir &= ~TOKCOD_p;

/* discover arrangement of axes on screen */
axslopes( total, axslp );

switch ( vptr->xhdir & TOKCOD_DIR )
  {
  case TOKCOD_Z:
    rtaxis = MOSTER( XX, YY, XX );
    upaxis = MOSTER( XX, YY, YY );
    if ( (rtaxis & TOKCOD_DIR) == XX )
      vptr->xhdir |= TOKCOD_p;
    break;

  case TOKCOD_X:
    rtaxis = MOSTER( YY, ZZ, XX );
    upaxis = MOSTER( YY, ZZ, YY );
    if ( (rtaxis & TOKCOD_DIR) == YY )
      vptr->xhdir |= TOKCOD_p;
    break;

  case TOKCOD_Y:
    rtaxis = MOSTER( XX, ZZ, XX );
    upaxis = MOSTER( XX, ZZ, YY );
    if ( (rtaxis & TOKCOD_DIR) == ZZ )
      vptr->xhdir |= TOKCOD_p;
    break;
  }

fptr->xsign = (rtaxis & TOKCOD_p) ?  1 : -1;
fptr->ysign = (upaxis & TOKCOD_p) ?  1 : -1;
}
@//E*O*F xh.c//
chmod u=rw,g=r,o=r xh.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     257     905    6488 boxobj.c
     289     786    5896 boxview.c
     186     523    4071 manip.c
     602    1530   12654 rotate.c
     662    1719   13891 view.c
     455    1231   10431 xh.c
    2451    6694   53431 total
!!!
wc  boxobj.c boxview.c manip.c rotate.c view.c xh.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0
#------------------- cut this line and below ----------------------
-- 
-- Kelvin Thompson, Lone Rider of the Apocalypse
   kelvin at cs.utexas.edu  {...,uunet}!cs.utexas.edu!kelvin 



More information about the Comp.sys.sgi mailing list