#ifdef SCCS
static char sccsid[]="@(#)fb.c	1.14 Oki Electric Industry Co., Ltd. 93/05/24";
#endif
/*
	This file is under sccs control at Stardent in:
	/nfs/sole/root/sccs1.p/X11R5/mit/demos/pexdraw/s.fb.c
*/
/*
 *			Copyright (c) 1992 by
 *			Oki Electric Industry Co., Ltd.
 *			All Rights Reserved
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of Oki not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission. Oki
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 * This supercedes the Stardent copyright granting the same rights that
 * appeared in the pdraw predecessor to pexdraw.
 *
 *************************************************************************
 *
 *                         P E X D R A W
 *
 *  A PEX drawing program based on PDRAW,
 *
 *   
 *
 *************************************************************************/

#include <stdio.h>

#include <math.h>

#include <X11/PEX5/PEXlib.h>
#include <X11/Xutil.h> /* because of XVisualInfo in pexdraw.h !?! */

#include "pexdraw.h"

int theAnchorX, theAnchorY, theDynX, theDynY, theAltX, theAltY;
int theTriStripCount = 0;

#define TRIMAX 12
XPoint theTriDCs[TRIMAX];
PEXCoord       theTriPoints[TRIMAX];

float theTheta, thePhi, theDeltaTheta, theDeltaPhi;


/*************************************************************************
 *
 * D Y N A M I C    F E E D B A C K   R O U T I N E S
 *
 */
static void fbInitLine(
#if NeedFunctionPrototypes
XButtonPressedEvent * /* event */
#endif
);

static void fbDynLine(
#if NeedFunctionPrototypes
XMotionEvent * /* event */
#endif
);

static void fbEndLine(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);

static void fbEndMClip(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);

static void fbDummy();
static void fbDynBox(
#if NeedFunctionPrototypes
XMotionEvent * /* event */
#endif
);

static void fbEndPick(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbInitTriStrip(
#if NeedFunctionPrototypes
XButtonPressedEvent * /* event */
#endif
);

static void fbDynTriStrip(
#if NeedFunctionPrototypes
XMotionEvent * /* event */
#endif
);

static void fbEndTriStrip(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);

static void fbFinishTriStrip(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbInitNURBCurve(
#if NeedFunctionPrototypes
XButtonPressedEvent * /* event */
#endif
);

static void fbDynNURBCurve(
#if NeedFunctionPrototypes
XMotionEvent * /* event */
#endif
);

static void fbEndNURBCurve(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);

static void fbFinishNURBCurve(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbFinishPolygon (
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbEndText(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbInitView(
#if NeedFunctionPrototypes
XButtonPressedEvent * /* event */
#endif
);

static void fbDynView(
#if NeedFunctionPrototypes
XMotionEvent * /* event */
#endif
);

static void fbEndView(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbInitTranslate(
#if NeedFunctionPrototypes
XButtonPressedEvent * /* event */
#endif
);

/* use dynline */
static void fbEndTranslate(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbInitStretch(
#if NeedFunctionPrototypes
XButtonPressedEvent * /* event */
#endif
);

/* use dynline, or dyntristrip */
static void fbEndStretch(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbEndZoom(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);

static void fbZoomReset(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);


static void fbInitCircle(
#if NeedFunctionPrototypes
XButtonPressedEvent * /* event */
#endif
);

static void fbEndCircle(
#if NeedFunctionPrototypes
XButtonReleasedEvent * /* event */
#endif
);

static void fbDynCircle(
#if NeedFunctionPrototypes
XMotionEvent * /* event */
#endif
);




/*************************************************************************
 *
 *  T O O L   S E L E C T I O N   R O U T I N E S
 *
 *************************************************************************
 */

void 
set_tool_pointer()
{
    theCurrentTool = TOOL_POINTER;

    /*
     * don't deal with fancy pre-pick hilite, yet.
     */
    if (the51Flag) {
      thePressHandlerTable[0] = fbInitLine;
      theMotionHandlerTable[0] = fbDynBox;
    } else {
      thePressHandlerTable[0] = fbDummy;
      theMotionHandlerTable[0] = fbDummy;
    }
    theReleaseHandlerTable[0] = fbEndPick;

    thePressHandlerTable[1] = fbInitTranslate;
    theMotionHandlerTable[1] = fbDynLine;
    theReleaseHandlerTable[1] = fbEndTranslate;

    thePressHandlerTable[2] = fbInitStretch;
    theMotionHandlerTable[2] = fbDummy;
    theReleaseHandlerTable[2] = fbEndStretch;
}

void 
set_tool_line()
{
    theCurrentTool = TOOL_LINE;

    thePressHandlerTable[0] = fbInitLine;
    theMotionHandlerTable[0] = fbDynLine;
    theReleaseHandlerTable[0] = fbEndLine;

    thePressHandlerTable[1] = fbInitLine;
    theMotionHandlerTable[1] = fbDynLine;
    theReleaseHandlerTable[1] = fbEndLine;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
}

void 
set_tool_view()
{

    theCurrentTool = TOOL_VIEW;

    thePressHandlerTable[0] = fbInitView;
    theMotionHandlerTable[0] = fbDynView;
    theReleaseHandlerTable[0] = fbEndView;

    thePressHandlerTable[1] = fbDummy;
    theMotionHandlerTable[1] = fbDummy;
    theReleaseHandlerTable[1] = fbDummy;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
}

void
set_tool_nurbc()
{
    theCurrentTool = TOOL_NURBC;

    thePressHandlerTable[0] = fbInitNURBCurve;
    theMotionHandlerTable[0] = fbDynLine;
    theReleaseHandlerTable[0] = fbEndNURBCurve;

    thePressHandlerTable[1] = fbFinishNURBCurve;
    theMotionHandlerTable[1] = fbDummy;
    theReleaseHandlerTable[1] = fbDummy;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
}

void
set_tool_pgon()
{
    theCurrentTool = TOOL_PGON;

    thePressHandlerTable[0] = fbInitNURBCurve;
    theMotionHandlerTable[0] = fbDynLine;
    theReleaseHandlerTable[0] = fbEndNURBCurve;

    thePressHandlerTable[1] = fbFinishPolygon;
    theMotionHandlerTable[1] = fbDummy;
    theReleaseHandlerTable[1] = fbDummy;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
}

void
set_tool_circle()
{
    theCurrentTool = TOOL_CIRCLE;

    thePressHandlerTable[0] = fbInitCircle;
    theMotionHandlerTable[0] = fbDynCircle;
    theReleaseHandlerTable[0] = fbEndCircle;

    thePressHandlerTable[1] = fbDummy;
    theMotionHandlerTable[1] = fbDummy;
    theReleaseHandlerTable[1] = fbDummy;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
}


void
set_tool_text()
{
  theCurrentTool = TOOL_TEXT;

  thePressHandlerTable[0] = fbDummy;
  theMotionHandlerTable[0] = fbDummy;
  theReleaseHandlerTable[0] = fbEndText;

  thePressHandlerTable[1] = fbDummy;
  theMotionHandlerTable[1] = fbDummy;
  theReleaseHandlerTable[1] = fbDummy;

  thePressHandlerTable[2] = fbInitView;
  theMotionHandlerTable[2] = fbDynView;
  theReleaseHandlerTable[2] = fbEndView;
}

void
set_tool_tristrip()
{
    theCurrentTool = TOOL_TRISTRIP;

    thePressHandlerTable[0] = fbInitTriStrip;
    theMotionHandlerTable[0] = fbDynTriStrip;
    theReleaseHandlerTable[0] = fbEndTriStrip;

    thePressHandlerTable[1] = fbFinishTriStrip;
    theMotionHandlerTable[1] = fbDummy;
    theReleaseHandlerTable[1] = fbDummy;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
}

void
set_tool_zoom()
{
  if (theCurrentTool != TOOL_ZOOM) {
    theCurrentTool = TOOL_ZOOM;

    thePressHandlerTable[0] = fbInitLine;
    theMotionHandlerTable[0] = fbDynBox;
    theReleaseHandlerTable[0] = fbEndZoom;

    thePressHandlerTable[1] = fbDummy;
    theMotionHandlerTable[1] = fbDummy;
    theReleaseHandlerTable[1] = fbZoomReset;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
  }
}

void
set_tool_mclip()
{
  if (theCurrentTool != TOOL_MCLIP) {
    theCurrentTool = TOOL_MCLIP;

    thePressHandlerTable[0] = fbInitLine;
    theMotionHandlerTable[0] = fbDynLine;
    theReleaseHandlerTable[0] = fbEndMClip;

    thePressHandlerTable[1] = fbDummy;
    theMotionHandlerTable[1] = fbDummy;
    theReleaseHandlerTable[1] = fbDummy;;

    thePressHandlerTable[2] = fbInitView;
    theMotionHandlerTable[2] = fbDynView;
    theReleaseHandlerTable[2] = fbEndView;
  }
}

/*************************************************************************
 *
 * D Y N A M I C     F E E D B A C K     R O U T I N E S
 *
 */
static void fbInitLine(event )
 XButtonPressedEvent *event;
{
  XGCValues values;

  theAnchorX = event->x;
  theAnchorY = event->y;
  theDynX = event->x;
  theDynY = event->y;
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );
}

static void fbDynLine(event )
 XMotionEvent *event;
{
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );
  theDynX = event->x;
  theDynY = event->y;
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );
}

static void fbDynBox(event )
 XMotionEvent *event;
{
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theAnchorX, theDynY );
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theDynY, theDynX, theDynY );
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theDynX, theDynY, theDynX, theAnchorY );
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theDynX, theAnchorY, theAnchorX, theAnchorY );
  theDynX = event->x;
  theDynY = event->y;
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theAnchorX, theDynY );
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theDynY, theDynX, theDynY );
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theDynX, theDynY, theDynX, theAnchorY );
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theDynX, theAnchorY, theAnchorX, theAnchorY );
}

static void fbEndLine(event )
 XButtonReleasedEvent *event;
{
  XPoint xp[2];
  PEXCoord *pl;
  int count;

  xp[0].x = theAnchorX;
  xp[0].y = theAnchorY;
  xp[1].x = theDynX;
  xp[1].y = theDynY;

  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, 2, xp, &pl)) ==  2) {
    InsertLineCmd( 2, pl, 1 );
    free((char *)pl);
    theRenderProcs.ReDraw();
  }
}

static void fbEndMClip(event )
 XButtonReleasedEvent *event;
{
  XPoint xp[2];
  PEXCoord *pl;
  int count;

  xp[0].x = theAnchorX;
  xp[0].y = theAnchorY;
  xp[1].x = theDynX;
  xp[1].y = theDynY;

  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, 2, xp, &pl)) ==  2) {
    InsertMClipCmd( pl, 1 );
    free((char *)pl);
    theRenderProcs.ReDraw();
  }
}


/*
 * no dynamic hiliting
 */
static void fbDummy() {}

#define DIFFERENT(a,b) (((((a)-(b))>3)||(((a)-(b))<-3))?1:0)

static void fbEndPick(event )
 XButtonReleasedEvent *event;
{
  long s;
  int e;
  int count;
  long *struxArray;
  int *elemArray;

  XDrawPoint(  theDisplay, theWindow, theDynGC, event->x, event->y );
  ClearSelection();

  if (the51Flag && 
      (DIFFERENT(event->x,theAnchorX) || DIFFERENT(event->y,theAnchorY))) {
    /* do pick all */
    theRenderProcs.PickAll( event->x, event->y, theAnchorX,theAnchorY,
			   &count, &struxArray, &elemArray );
    if (count > 0) {
      ColorGroup( count, struxArray, elemArray );
      SelectSomething( struxArray[0], elemArray[0] );
    }
  } else {
    theRenderProcs.PickOne( event->x, event->y, &s, &e);
    if ( e != -1 )
      SelectSomething( s, e );
  }
  theRenderProcs.ReDraw();
}

/*************************************************************************
 *  fbInitTriStrip - initialze and reinit tristrip inster dynamics.
 *
 * if we are working on a tristrip, then start the next point. Otherwise,
 *  we are just drawing a line!
 */
static void fbInitTriStrip(event )
 XButtonPressedEvent *event;
{
  if (theTriStripCount) {
    if (theTriStripCount == TRIMAX) {
      fbFinishTriStrip( event );
      return;
    }
    theAnchorX = theAltX;
    theAnchorY = theAltY;
    theAltX = theDynX;
    theAltY = theDynY;
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );
    XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAltX, theAltY, theDynX, theDynY );
  } else {
    theAnchorX = event->x;
    theAnchorY = event->y;
    theDynX = event->x;
    theDynY = event->y;
    theAltX = event->x;
    theAltY = event->y;
    XDrawLine( theDisplay, theWindow, theDynGC, 
	      theAnchorX, theAnchorY, theDynX, theDynY );
  }
}

static void fbDynTriStrip(event )
 XMotionEvent *event;
{
  if (theTriStripCount) {
    XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );
    XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAltX, theAltY, theDynX, theDynY );
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );
    XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAltX, theAltY, theDynX, theDynY );
  } else {
    XDrawLine( theDisplay, theWindow, theDynGC, 
	      theAnchorX, theAnchorY, theDynX, theDynY );
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, theDynGC, 
	      theAnchorX, theAnchorY, theDynX, theDynY );
  }
}
static void fbEndTriStrip(event )
 XButtonReleasedEvent *event;
{
  if (theTriStripCount) {
    XDrawLine( theDisplay, theWindow, 
	      theDynGC, theAnchorX, theAnchorY, theDynX, theDynY );
    XDrawLine( theDisplay, theWindow,
	      theDynGC, theAltX, theAltY, theDynX, theDynY );
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, 
	      theDynGC, theAnchorX, theAnchorY, theDynX, theDynY );
    XDrawLine( theDisplay, theWindow,
	      theDynGC, theAltX, theAltY, theDynX, theDynY );
    theTriDCs[theTriStripCount].x = theDynX;
    theTriDCs[theTriStripCount].y = theDynY;
    theTriStripCount++;
  } else {
    XDrawLine( theDisplay, theWindow, theDynGC, 
	      theAnchorX, theAnchorY, theDynX, theDynY );
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, theDynGC, 
	      theAnchorX, theAnchorY, theDynX, theDynY );
    theTriDCs[0].x = theAnchorX;
    theTriDCs[0].y = theAnchorY;
    theTriDCs[1].x = theDynX;
    theTriDCs[1].y = theDynY;
    theTriStripCount = 2;
  }
}

static void fbFinishTriStrip (event )
 XButtonReleasedEvent *event;
{
  int i, count;
  PEXCoord *p;

  if (theTriStripCount < 3) return;

  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, theTriStripCount,
				       theTriDCs, &p))  ==  theTriStripCount) {
    if ( count > 2 ) {
      InsertTriStripCmd( count, p, 1 );
      free((char *)p);
      theRenderProcs.ReDraw();
    }
  }
  theTriStripCount = 0;
}

/*************************************************************************
 *  fbInitNURBCurve - initialze and reinit NURBCurve inster dynamics.
 *
 * if we are working on a NURBCurve, then start the next point. Otherwise,
 *  we are just drawing a line!
 *
 * made applicable to polygons by calling the press handler of [1] when full.
 */
static void fbInitNURBCurve(event )
 XButtonPressedEvent *event;
{
  if (theTriStripCount) {
    if (theTriStripCount == TRIMAX) {
      thePressHandlerTable[1]( event );
      return;
    }
    theAnchorX = theDynX;
    theAnchorY = theDynY;
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );
  } else {
    theAnchorX = event->x;
    theAnchorY = event->y;
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, theDynGC, 
	      theAnchorX, theAnchorY, theDynX, theDynY );
    theTriDCs[theTriStripCount].x = theDynX;
    theTriDCs[theTriStripCount].y = theDynY;
    theTriStripCount++;  /* save this point as the first point */
  }
}

static void fbEndNURBCurve(event )
 XButtonReleasedEvent *event;
{
  if (theTriStripCount) {
    XDrawLine( theDisplay, theWindow, 
	      theDynGC, theAnchorX, theAnchorY, theDynX, theDynY );
    theDynX = event->x;
    theDynY = event->y;
    XDrawLine( theDisplay, theWindow, 
	      theDynGC, theAnchorX, theAnchorY, theDynX, theDynY );
    theTriDCs[theTriStripCount].x = theDynX;
    theTriDCs[theTriStripCount].y = theDynY;
    theTriStripCount++;
  }
}

static void fbFinishNURBCurve (event )
 XButtonReleasedEvent *event;
{
  int i, count;
  PEXCoord *p;

  if (theTriStripCount < 3) return;

  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, theTriStripCount,
				       theTriDCs, &p))  ==  theTriStripCount) {
    if ( count > 2 ) {
      InsertNURBCurveCmd( count, p, 1 );
      free((char *)p);
      theRenderProcs.ReDraw();
    }
  }
  theTriStripCount = 0;
}

static void fbFinishPolygon (event )
 XButtonReleasedEvent *event;
{
  int i, count;
  PEXCoord *p;

  if (theTriStripCount < 3) return;

  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, theTriStripCount,
				       theTriDCs, &p))  ==  theTriStripCount) {
    if ( count > 2 ) {
      InsertPolygonCmd( count, p, 1 );
      free((char *)p);
      theRenderProcs.ReDraw();
    }
  }
  theTriStripCount = 0;
}

static void fbEndText(event )
 XButtonReleasedEvent *event;
{
  XPoint xp[2];
  PEXCoord *pl;
  int count;
  char *charStr;
  int flags;
  

  XDrawPoint(  theDisplay, theWindow, theDynGC, event->x, event->y );

  xp[0].x = event->x;
  xp[0].y = event->y;

  if ((theRenderProcs.MapXToMC( &theMCMatrix, 1, xp, &pl)) == 1) {
    GetSomeTextFromUI( event->x, event->y, &count, &charStr, &flags );
    InsertTextCmd( pl, count, charStr, flags, 1 );
    free(charStr);
    free((char *)pl);
    theRenderProcs.ReDraw();
  }
}


/*************************************************************************
 *
 */
static void fbInitTranslate(event )
 XButtonPressedEvent *event;
{

  theAnchorX = event->x;
  theAnchorY = event->y;
  theDynX = event->x;
  theDynY = event->y;
  XDrawPoint( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY );
}

static void fbEndTranslate(event ) 
 XButtonReleasedEvent *event;
{
  XPoint xp[2];
  PEXCoord *pl;
  int count;

  if ( theSelectedElement == -1 ) {
    printf("nothing selected\n");
    return;
  }

  theDynX = event->x;
  theDynY = event->y;
  XDrawPoint( theDisplay, theWindow, theDynGC, 
	    theDynX, theDynY );

  xp[0].x = theAnchorX;
  xp[0].y = theAnchorY;
  xp[1].x = theDynX;
  xp[1].y = theDynY;
  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, 2, xp, &pl)) ==  2) {
    TranslateSelected( pl );
    free((char *)pl);
    theRenderProcs.ReDraw();
  }
}

/*************************************************************************
 *
 */
static void fbInitStretch(event )
 XButtonPressedEvent *event;
{
  int i, count;
  XPoint *points, *p;
  float dist, currentDist;
  int error;

  theSelectedIndex = -1;

  error = GetXPointsFromSelected(&count, &points);
  if (error) {
    printf("imagine a popup dialog... bad mapping in InitStretch %d\n", error );
    return;
  }

  currentDist = MAXFLOAT;

  for (i = 0, p = points; i < count; i++, p++ ) {
    /* don't bother with divide, only relative dist counts */
    dist = (event->x - p->x)*(event->x - p->x) + 
           (event->y - p->y)*(event->y - p->y);
    if (dist < currentDist) {
      currentDist = dist;
      theSelectedIndex = i;
    }
  }

  if (theSelectedIndex == -1) return;

  if (theSelectedIndex > 0) {
    theAnchorX = points[theSelectedIndex-1].x;
    theAnchorY = points[theSelectedIndex-1].y;
  } else {
    theAnchorX = points[theSelectedIndex+1].x;
    theAnchorY = points[theSelectedIndex+1].y;
  }
  free(points); /* all done */
  
  theDynX = event->x;
  theDynY = event->y;
  XDrawLine( theDisplay, theWindow, theDynGC, 
	    theAnchorX, theAnchorY, theDynX, theDynY );

  /*
   * O.K.  we're ready.
   */
  theMotionHandlerTable[2] = fbDynLine;
}

/*************************************************************************
 * fbEndStretch
 */
static void fbEndStretch(event )
 XButtonReleasedEvent *event;
{
  XPoint xp;
  PEXCoord *pl;
  int count;

  xp.x = event->x;
  xp.y = event->y;
  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, 1, &xp, &pl)) ==  1) {
    if ( count == 1 ) {
      StretchSelected(pl);
      free((char *)pl);
      theRenderProcs.ReDraw();
    }
    theMotionHandlerTable[2] = fbDummy;
  }
}       

/*************************************************************************
 *
 */
static void fbInitView(event ) 
 XButtonPressedEvent *event;
{
  PEXVector  vpn;  /* view plane normal    */
  float tsinTheta;
    
  theDynX = event->x;
  theDynY = event->y;

  /* view plane normal */
  GetVPN(1, &vpn);

  PEXNormalizeVectors(1,&vpn,&vpn); /* just in case the user was playing */

  /*
   * theta = atan2(cos(phi)*sin(theta), cos(phi)*cos(theta))
   */
  theTheta = atan2(vpn.x, vpn.z);
  /*
   * atan2(sin(theta), cos(theta)) gives theta.
   * since we have sin(phi) = dy and dx = cos(phi)*sin(theta)
   * we want atan2(dy, dx/sin(theta)) BUT sin(theta) will be zero
   * so we use dy*sin(theta) BUT when sin(theta) and dx are negative
   * it changes the quadrant. so...
   */
  tsinTheta = sin(theTheta);
  if (tsinTheta >= 0 ) {
   thePhi = atan2( vpn.y * tsinTheta, vpn.x);
  } else {
   thePhi = atan2( -(vpn.y * tsinTheta), -vpn.x);
  }
/*  
  thedebug = 1;
  printf("start t %g, p %g, x,y,z %g, %g, %g\n", theTheta, thePhi,  
   vpn.x, vpn.y, vpn.z);
*/
}

static void fbDynView(event )
 XMotionEvent *event;
{
  PEXVector  vpn;  /* view plane normal    */
  PEXVector  vup;  /* view up vector       */
  float cx, cy, cz;
  int t, c;
    	
  theTheta += 0.005 * (theDynX - event->x);
  thePhi   += 0.005 * (event->y - theDynY);

  theDynX = event->x;
  theDynY = event->y;


  vpn.x = cos( thePhi ) * sin( theTheta );
  vpn.y = sin( thePhi );
  vpn.z = cos( thePhi ) * cos( theTheta );

  /* view up vector */
  GetVUP(1, &vup);

  t = 6; c = 0;
  while (t--) {
    cx = vpn.y*vup.z - vpn.z*vup.y;
    cy = vpn.z*vup.x - vpn.x*vup.z;
    cz = vpn.x*vup.y - vpn.y*vup.x;
  
    if ((cx*cx + cy*cy + cz*cz) < 0.1) {
    	if ((vup.y < 0.8) && (vup.y > -0.8)) {
    	  vup.y += 0.03;
        } else {
    	  vup.x += 0.05;
    	}
	PEXNormalizeVectors(1,&vup,&vup);
    	c = 1;
    } else {
    	t = 0;
    }
  }

  if (c) {
    SetVUP(1,&vup);
  }  	
  SetVPN(1,&vpn);

  applyViewSet(1);
  XSync(theDisplay,1); /* sync and discard events */
}

static void fbEndView(event )
 XButtonReleasedEvent *event;
{
  Window root, child;
  int root_x, root_y;
  int win_x, win_y;
  unsigned int keys_buttons;
  	
  XQueryPointer(theDisplay,theWindow,
                &root, &child, &root_x, &root_y, &win_x, &win_y,
                &keys_buttons);
  XQueryPointer(theDisplay,theWindow,
                &root, &child, &root_x, &root_y, &win_x, &win_y,
                &keys_buttons);
/*                
	if ((win_x != theDynX) && (win_y != theDynY)) {
		printf("spinning... %d, %d, %d, %d\n", 
		theDynX,win_x,theDynY,win_y);
	}
*/
}

/*************************************************************************
 * fbEndZoom
 *
 */
static void fbEndZoom(event )
 XButtonReleasedEvent *event;
{
  PEXMatrix XCtoNPC;
  PEXCoord p[2];
  int err;
  float t;
  XWindowAttributes winAttrs;
  
  p[0].x = event->x;
  p[0].y = event->y;
  p[0].z = 0.0;
  p[1].x = theAnchorX;
  p[1].y = theAnchorY;
  p[1].z = 0.0;

  theRenderProcs.MapXToNPC( 2, p );

  /* this really belongs next to Resize */

#define SWAP(a,b) {t=a;a=b;b=t;}

  if (p[0].x > p[1].x) SWAP(p[0].x, p[1].x);
  if (p[0].y > p[1].y) SWAP(p[0].y, p[1].y);

  if ((p[1].y - p[0].y) > (p[1].x - p[0].x)) {
    theWindowSize = (p[1].y - p[0].y)/2.0;
  } else {
    theWindowSize = (p[1].x - p[0].x)/2.0;
  }
  theWindowCenterX = (p[0].x+p[1].x)/2.0;
  theWindowCenterY = (p[0].y+p[1].y)/2.0;

  XGetWindowAttributes(theDisplay,theWindow,&winAttrs);

  ResizeWindow(theWindow, winAttrs.width, winAttrs.height );

  theRenderProcs.ReDraw();
}

static void fbZoomReset(event )
 XButtonReleasedEvent *event;
{
  XWindowAttributes winAttrs;

  theWindowSize = 0.5;
  theWindowCenterX = 0.5;
  theWindowCenterY = 0.5;

  XGetWindowAttributes(theDisplay,theWindow,&winAttrs);

  ResizeWindow(theWindow, winAttrs.width, winAttrs.height );

  theRenderProcs.ReDraw();
}

/*************************************************************************
 * fbInitCircle - 
 */
static void fbInitCircle(event )
 XButtonPressedEvent *event;
{
  double angle;
  int i;
  double distance;

  theAnchorX = event->x;
  theAnchorY = event->y;
  theDynX = event->x+1;
  theDynY = event->y;

  angle = (2*M_PI) / (double)(TRIMAX-1);
  distance = sqrt((double)((theDynX-theAnchorX)*(theDynX-theAnchorX) + 
			   (theDynY-theAnchorY)*(theDynY-theAnchorY)));

  for (i = 0; i < TRIMAX; i++) {
    theTriDCs[i].x = theDynX + (int)(distance*cos((double)i*angle));
    theTriDCs[i].y = theDynY + (int)(distance*sin((double)i*angle));;
  }
  XDrawLines( theDisplay, theWindow, theDynGC, 
	     theTriDCs, TRIMAX, CoordModeOrigin );
}

static void fbEndCircle(event )
 XButtonReleasedEvent *event;
{
  XPoint xp[3];
  PEXCoord *pl;
  int count;

  xp[0].x = theAnchorX;
  xp[0].y = theAnchorY;
  xp[1].x = theDynX;
  xp[1].y = theDynY;

  xp[2].x = (theDynX == theAnchorY)?theDynX+5:theDynX;
  xp[2].y = (theDynY == theAnchorY)?theAnchorX+5:theAnchorX;

  if ((count = theRenderProcs.MapXToMC( &theMCMatrix, 3, xp, &pl)) ==  3) {
    InsertCircleCmd( 3, pl, 1 );
    free((char *)pl);
    theRenderProcs.ReDraw();
  }
}

static void fbDynCircle(event )
 XMotionEvent *event;
{
  double angle;
  int i;
  double distance;

  XDrawLines( theDisplay, theWindow, theDynGC, 
	     theTriDCs, TRIMAX, CoordModeOrigin );
  theDynX = event->x;
  theDynY = event->y;

  angle = (2*M_PI) / (double)(TRIMAX-1);
  distance = sqrt((double)((theDynX-theAnchorX)*(theDynX-theAnchorX) + 
			   (theDynY-theAnchorY)*(theDynY-theAnchorY)));

  for (i = 0; i < TRIMAX; i++) {
    theTriDCs[i].x = theDynX + (int)(distance*cos((double)i*angle));
    theTriDCs[i].y = theDynY + (int)(distance*sin((double)i*angle));;
  }
  XDrawLines( theDisplay, theWindow, theDynGC, 
	     theTriDCs, TRIMAX, CoordModeOrigin );
}
