#include <math.h>
#include <X11/X.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <Text.h>
#include <double.h>
#include <Scale.h>
#include "surCon.h"
#include "Axis3P.h"
#include "ThreedeeP.h"
#include "Plot3P.h"

#define DEBUG3D

#define NUMCHILDREN(w) (w->composite.num_children)
#define CHILD(w,i) (w->composite.children[i])
#define CONSTRAINT(w,i)((AtThreedeeConstraints)(CHILD(w,i)->core.constraints))
#define RESIZEPLOT3CHILD(w,i) (*((AtPlot3WidgetClass) CHILD(w, i)->core.widget_class)->plot3_class.resize)((AtThreedeeWidget) w, (AtPlot3Widget)CHILD(w,i))
#define MAX(a,b) ((a) > (b)?(a):(b))

#define AtWarning(w, msg) XtWarning(msg)

#define pBOTTOM 1.0
#define pTOP 0.0
#define pCENTER 0.5
#define pLEFT 0.0
#define pRIGHT 1.0

#define sq(n) n * n

void At3dComputeLayout(AtThreedeeWidget);
static void At3dError(AtThreedeeWidget, char *);
static void ProjDraw(AtThreedeeWidget, Projection);
void AutoScale(AtThreedeeWidget);
short GetEventRegion(AtThreedeeWidget, int, int);
Boolean NearAxis(int, int, AtAxis3Object, int);

/* action routines */
static void StartSelection();
static void EndSelection();
static void Drag();
static void CancelSelection();
static void HandleMotion();
			
/*  Default translation table and action list  */

static char defaultTranslations[] = 
    "<Btn1Down>:       start-selection() \n\
     <Btn1Motion>:     drag() \n\
     <Btn1Up>:         end-selection() \n\
     <Btn3Down>:       cancel-selection() \n\
     <Key>Escape:      cancel-selection() \n\
     <Motion>:         motion-notify()";

static XtActionsRec actions[] =
{
  { "motion-notify", (XtActionProc) HandleMotion },
  { "start-selection", (XtActionProc) StartSelection },
  { "drag", (XtActionProc) Drag },
  { "end-selection", (XtActionProc) EndSelection},
  { "cancel-selection", (XtActionProc) CancelSelection},
};

static Boolean frontFrameAxes[] = {True, False, False, False,
				     True, False, False, False,
				     True, False, False, False};

static Boolean wholeCubeAxes[] = {True, True, True, False, 
				True, True, True, False, 
				True, True, True, False};

static Boolean centeredAxes[]= {False, False, False, True,
				  False, False, False, True,
				  False, False, False, True};
static double defaultAngle = PI/6;

static double zero = 0.0;
static double one = 1.0;

static double top = TOP;
static double center = CENTER;
static double bottom = BOTTOM;
static double left = LEFT;
static double right = RIGHT;

#define offset(field) XtOffset(AtThreedeeWidget,field)
static XtResource threedee_resources[] = {

  {XtNfontFamily, XtCFontFamily, XtRString,
     sizeof(String), offset(threedee.fontFamily),
     XtRString, "new century schoolbook"},
  {XtNtitle, XtCTitle, XtRString,
     sizeof(String), offset(threedee.title), XtRString, NULL},
  {XtNtitleSize, XtCFontSize, XtRFontSize,
     sizeof(int), offset(threedee.titleSize),
     XtRImmediate, (caddr_t)AtFontBIG},
  {XtNtitleColor, XtCForeground, XtRPixel, sizeof(Pixel),
     offset(threedee.titleColor), XtRString, (caddr_t)XtDefaultForeground},
  {XtNtitlePosX, XtCTitlePosX, XtRDouble, sizeof(int), 
     offset(threedee.titlePosX), XtRDouble, (caddr_t)&center},
  {XtNtitlePosY, XtCTitlePosY, XtRDouble, sizeof(int), 
     offset(threedee.titlePosY), XtRDouble, (caddr_t)&top},
  {XtNautoScale, XtCAutoScale, XtRBoolean,
     sizeof(Boolean), offset(threedee.autoScale), XtRImmediate, (caddr_t)True},
  {XtNmarginWidth, XtCMargin, XtRShort,
     sizeof(short), offset(threedee.marginWidth), XtRImmediate, (caddr_t)5},
  {XtNmarginHeight, XtCMargin, XtRShort,
     sizeof(short), offset(threedee.marginHeight), XtRImmediate, (caddr_t)5},
  {XtNmotionCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(threedee.motionCallback), XtRCallback, NULL},
  {XtNclickCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(threedee.clickCallback), XtRCallback, NULL},
  {XtNdragCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(threedee.dragCallback), XtRCallback, NULL},
  {XtNselectCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(threedee.selectCallback), XtRCallback, NULL},
  {XtNerrorCallback, XtCCallback, XtRCallback,sizeof(XtCallbackList),
     offset(threedee.errorCallback), XtRCallback, NULL},
  {XtNx1axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.x1axis), XtRAxis3, NULL},
  {XtNx2axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.x2axis), XtRAxis3, NULL},
  {XtNx3axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.x3axis), XtRAxis3, NULL},
  {XtNx4axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.x4axis), XtRAxis3, NULL},
  {XtNy1axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.y1axis), XtRAxis3, NULL},
  {XtNy2axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.y2axis), XtRAxis3, NULL},
  {XtNy3axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.y3axis), XtRAxis3, NULL},
  {XtNy4axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.y4axis), XtRAxis3, NULL},
  {XtNz1axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.z1axis), XtRAxis3, NULL},
  {XtNz2axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.z2axis), XtRAxis3, NULL},
  {XtNx3axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.z3axis), XtRAxis3, NULL},
  {XtNx4axis, XtCAxis3, XtRAxis3, sizeof(AtAxis3Object),
     offset(threedee.z4axis), XtRAxis3, NULL},
  {XtNleftShear, XtCLeftShear, XtRDouble, sizeof(double),
     offset(threedee.leftShear), XtRDouble, (caddr_t)&defaultAngle},
  {XtNdownShear, XtCDownShear, XtRDouble, sizeof(double),
     offset(threedee.downShear), XtRDouble, (caddr_t)&defaultAngle},
  {XtNxmin, XtCXmin, XtRDouble,sizeof(double), 
     offset(threedee.xmin), XtRDouble, (caddr_t)&zero},
  {XtNxmax, XtCXmax, XtRDouble,sizeof(double), 
     offset(threedee.xmax), XtRDouble, (caddr_t)&one},
  {XtNymin, XtCYmin, XtRDouble,sizeof(double), 
     offset(threedee.ymin), XtRDouble, (caddr_t)&zero},
  {XtNymax, XtCYmax, XtRDouble,sizeof(double), 
     offset(threedee.ymax), XtRDouble, (caddr_t)&one},
  {XtNzmin, XtCZmin, XtRDouble,sizeof(double), 
     offset(threedee.zmin), XtRDouble, (caddr_t)&zero},
  {XtNzmax, XtCZmax, XtRDouble, sizeof(double), 
     offset(threedee.zmax), XtRDouble, (caddr_t)&one},
  {XtNaxisZone, XtCAxisZone, XtRShort, sizeof(short),
     offset(threedee.axisZone), XtRImmediate, (caddr_t)4},
  {XtNaxesShown, XtCAxesShown, XtRBoolArray, 
     (sizeof(Boolean) * POSSIBLEAXES), offset(threedee.axesShown), 
     XtRBoolArray, (caddr_t)&frontFrameAxes}
};
#undef offset

#define XX1 0
#define XX2 1
#define XX3 2
#define XX4 3
#define YY1 4
#define YY2 5
#define YY3 6
#define YY4 7
#define ZZ1 8
#define ZZ2 9
#define ZZ3 10
#define ZZ4 11

#ifdef CONSTR
#define offset(field) XtOffset(AtThreedeeConstraints,field)
static XtResource constraint_resources[] = {
  {XtNdisplayName, XtCDisplayName, XtRString, sizeof(String),
     offset(threedee.displayName), XtRString, NULL}, 
};
#undef offset
#endif CONSTR

static void ClassInitialize();
static void Initialize(AtThreedeeWidget, AtThreedeeWidget);
static void Destroy(AtThreedeeWidget);
static void Resize(AtThreedeeWidget);
static void Redisplay(AtThreedeeWidget, XEvent *, Region);
static Boolean SetValues(AtThreedeeWidget, AtThreedeeWidget, 
			 AtThreedeeWidget);
static void InsertChild(Widget);
static void DeleteChild(Widget);

#ifdef CONSTR
static void ConstraintInitialize(Widget, Widget);
static void ConstraintDestroy(Widget);
static Boolean ConstraintSetValues(Widget, Widget, Widget);
#endif CONSTR

#ifdef CONSTR
#define superclass (&compositeClassRec)
#else
#define superclass (&compositeClassRec)
#endif CONSTR

  externaldef(compositeclassrec) AtThreedeeClassRec atThreedeeClassRec = {
  { /******* CoreClassPart *******/
  /* superclass           */(WidgetClass) superclass,
  /* class_name           */ "AtThreedee",
  /* widget_size          */  sizeof(AtThreedeeRec),
  /* class_initialize     */  ClassInitialize,
  /* class_part_initialize*/  NULL,
  /* class_inited         */  FALSE,
  /* initialize           */  (XtInitProc)Initialize,
  /* initialize_hook      */  NULL,
    /* realize              */  XtInheritRealize,
    /* actions              */  actions,
    /* num_actions          */  XtNumber(actions),
    /* resources            */  threedee_resources,
    /* num_resources        */  XtNumber(threedee_resources),
    /* xrm_class            */  NULLQUARK,
    /* compress_motion      */  FALSE,
    /* compress_exposure    */  TRUE,
    /* compress_enterleave  */  TRUE,
    /* visible_interest     */  FALSE,
    /* destroy              */  (XtWidgetProc)Destroy,
    /* resize               */  (XtWidgetProc)Resize,
    /* expose               */  (XtExposeProc)Redisplay,
    /* set_values           */  (XtSetValuesFunc) SetValues,
    /* set_values_hook      */  NULL,
    /* set_values_almost    */  XtInheritSetValuesAlmost,
    /* get_values_hook      */  NULL,
    /* accept_focus         */  NULL,
    /* version              */  XtVersion,
    /* callback_offsets     */  NULL,
    /* tm_table             */  defaultTranslations,
    /* query_geometry       */  NULL,
    /* display_accelerator  */  NULL,
    /* extension            */  NULL 
  },
  { /**** CompositeClassPart ****/
    /* geometry_handler     */  NULL,
    /* change_managed       */  NULL,
    /* insert_child         */  (XtWidgetProc)InsertChild,
    /* delete_child         */  (XtWidgetProc)DeleteChild,
    /* extension            */  NULL
  },
#ifdef CONSTR
  { /**** ConstraintClassPart ****/
    /* resources            */  constraint_resources,
    /* num_resources        */  XtNumber(constraint_resources),
    /* constraint_size      */  sizeof(AtThreedeeConstraintsRec),
    /* initialize           */  (XtInitProc) ConstraintInitialize,
    /* destroy              */  (XtWidgetProc) ConstraintDestroy,
    /* set_values           */  (XtSetValuesFunc) ConstraintSetValues,
    /* extension            */  NULL,
  },
#endif CONSTR
  { /**** AtThreedeeClassPart ****/
    /* meaningless field */     0
  }
};

WidgetClass atThreedeeWidgetClass = (WidgetClass)&atThreedeeClassRec;
     


static void GetTitle(AtThreedeeWidget w)
{
    if (w->threedee.title != NULL) {
        w->threedee.title = XtNewString(w->threedee.title);
        w->threedee.titleText = AtTextCreate(w->threedee.title,
                                              w->threedee.ff, AtFontBIG);
      }
    else
        w->threedee.titleText = NULL;
}

static void FreeTitle(AtThreedeeWidget w)
{
    XtFree(w->threedee.title);
    w->threedee.title = NULL;
    if (w->threedee.titleText) AtTextDestroy(w->threedee.titleText);
    w->threedee.titleText = NULL;
}


static void ClassInitialize()
{
#ifdef R4
  static CompositeClassExtensionRec ext;
  
  ext.next_extension = NULL;
  ext.record_type = NULLQUARK;
  ext.version = XtCompositeExtensionVersion;
  ext.record_size = sizeof(CompositeClassExtensionRec);
  ext.accepts_objects = True;
  atPlotterClassRec.composite_class.extension = &ext;
#endif
  
  
  AtRegisterDoubleConverter();
  AtRegisterFontSizeConverter();
  AtRegisterSurfaceConverter();

  if (_AtStringToTransformRegistered == False) {
    XtAddConverter(XtRString, XtRTransform,AtCvtStringToTransform, NULL,0);
    _AtStringToTransformRegistered = True;
  }
  
}

static void Initialize(AtThreedeeWidget request,AtThreedeeWidget new)
{
  XGCValues gcv;
  XPoint cubePts[6];
  Arg axargs[4];
  /* get font fams */
  new->threedee.ff = AtFontFamilyGet(XtDisplay(new),
				     new->threedee.fontFamily);
  
  /* GCs */
  
  gcv.foreground = new->threedee.titleColor;
  new->threedee.titleGC = XtGetGC(new, GCForeground, &gcv);
  
  gcv.foreground = new->threedee.titleColor ^ new->core.background_pixel;
  new->threedee.dragGC = XtGetGC(new, GCForeground, &gcv);
  
  /* Calculate Costheta and Sintheta */
  
  new->threedee.yAngle = atan(tan(new->threedee.downShear)/
				tan(new->threedee.leftShear));
  
  new->threedee.costheta = cos(new->threedee.yAngle);
  new->threedee.sintheta = sin(new->threedee.yAngle);
  new->threedee.cumProjection = (Projection)ProjCreate();

  /* Create Axes */
  new->threedee.axes = (AtAxis3Object *)XtMalloc(sizeof(AtAxis3Object) * 
					     POSSIBLEAXES);

  XtSetArg(axargs[0], XtNaxisType, AxisTypeX);
  XtSetArg(axargs[1], XtNlabelSide, &bottom);
  new->threedee.axes[XX1] = 
    new->threedee.x1axis = (AtAxis3Object)XtCreateWidget("x1axis",
							 atAxis3ObjectClass,
							 new, axargs, 2);
  printf("Resources type and labelSide actually set to: %d %f\n",
	 new->threedee.x1axis->axis3.axisType,
	 new->threedee.x1axis->axis3.labelSide);
  XtSetArg(axargs[1], XtNlabelSide, &top);
  new->threedee.axes[XX2] =
    new->threedee.x2axis = (AtAxis3Object)XtCreateWidget("x2axis",
							 atAxis3ObjectClass,
							 new, axargs, 2);
  XtSetArg(axargs[1], XtNdrawLabel, False);
  XtSetArg(axargs[2], XtNdrawNumbers, False);
  new->threedee.axes[XX3] = 
    new->threedee.x3axis = (AtAxis3Object)XtCreateWidget("x3axis",
							 atAxis3ObjectClass,
							 new, axargs, 3);
  XtSetArg(axargs[1], XtNdrawLabel, False);
  XtSetArg(axargs[2], XtNdrawNumbers, False);
  new->threedee.axes[XX4] =
    new->threedee.x4axis = (AtAxis3Object)XtCreateWidget("x4axis",
							 atAxis3ObjectClass,
							 new, axargs, 3);
  XtSetArg(axargs[0], XtNaxisType, AxisTypeY);
  XtSetArg(axargs[1], XtNlabelSide, &bottom); 
  new->threedee.axes[YY1] =
    new->threedee.y1axis = (AtAxis3Object)XtCreateWidget("y1axis",
							 atAxis3ObjectClass,
							 new, axargs, 2);
  printf("Resources type and labelSide actually set to: %d %f\n",
	 new->threedee.y1axis->axis3.axisType,
	 new->threedee.y1axis->axis3.labelSide);

  XtSetArg(axargs[1], XtNlabelSide, &top);
  new->threedee.axes[YY2] =
    new->threedee.y2axis = (AtAxis3Object)XtCreateWidget("y2axis",
							 atAxis3ObjectClass,
							 new, axargs, 2);
  XtSetArg(axargs[1], XtNdrawLabel, False);
  XtSetArg(axargs[2], XtNdrawNumbers, False);
  new->threedee.axes[YY3] = 
    new->threedee.y3axis = (AtAxis3Object)XtCreateWidget("y3axis",
							 atAxis3ObjectClass,
  							 new, axargs, 3);
  XtSetArg(axargs[1], XtNdrawLabel, False);
  XtSetArg(axargs[2], XtNdrawNumbers, False);
  new->threedee.axes[YY4] = 
    new->threedee.y4axis  = (AtAxis3Object)XtCreateWidget("y4axis",	
							  atAxis3ObjectClass,
							  new, axargs, 3);
  XtSetArg(axargs[0], XtNaxisType, AxisTypeZ);
  XtSetArg(axargs[1], XtNlabelSide, &left); 
  printf("Set type = %d, labelside = %d\n",
	 axargs[0], axargs[1]);
  new->threedee.axes[ZZ1] =
    new->threedee.z1axis = (AtAxis3Object)XtCreateWidget("z1axis",
							 atAxis3ObjectClass,
							 new, axargs, 2);
  printf("Resources type and labelSide actually set to: %d %f\n",
	 new->threedee.z1axis->axis3.axisType,
	 new->threedee.z1axis->axis3.labelSide);

  XtSetArg(axargs[1], XtNlabelSide, RIGHT);	
  new->threedee.axes[ZZ2] =
    new->threedee.z2axis = (AtAxis3Object)XtCreateWidget("z2axis",
							 atAxis3ObjectClass,
							 new, axargs, 2);
  
  XtSetArg(axargs[1], XtNdrawLabel, False);
  XtSetArg(axargs[2], XtNdrawNumbers, False);  
  new->threedee.axes[ZZ3] =
    new->threedee.z3axis = (AtAxis3Object)XtCreateWidget("z3axis",
							 atAxis3ObjectClass,
							 new, axargs, 3);
  XtSetArg(axargs[1], XtNdrawLabel, False);
  XtSetArg(axargs[2], XtNdrawNumbers, False); 
  printf("Set type = %d, drawlabel = %d, drawnumbers = %d\n",
	 axargs[0], axargs[1], axargs[2]);
  new->threedee.axes[ZZ4] =
    new->threedee.z4axis = (AtAxis3Object)XtCreateWidget("z4axis",
							 atAxis3ObjectClass,
							 new, axargs, 3);
  /* Create Scales */
#ifdef DEBUG3D
  printf("3DInit: new->threedee.xmin %f and new->threedee.xmax %f\n", 
	 new->threedee.xmin, new->threedee.xmax);
#endif DEBUG3D
  new->threedee.xscale = AtScaleCreate(new->threedee.xmin, new->threedee.xmax,
				     0,1,
				     new->threedee.x1axis->
				       axis3.xscale->transform);
#ifdef DEBUG3D
  printf("3dInit: new->threedee.ymin %f and new->threedee.ymax %f\n", 
	 new->threedee.ymin, new->threedee.ymax);
#endif DEBUG3D
  new->threedee.yscalex = AtScaleCreate((new->threedee.ymin), 
				      (new->threedee.ymax), 
				      0,1,
					new->threedee.y1axis->
					axis3.xscale->transform);

#ifdef DEBUG3D
  printf("3DInit: new->threedee.ymin %f and new->threedee.ymax %f\n", 
	 new->threedee.ymin, new->threedee.ymax);
#endif DEBUG3D
  new->threedee.yscaley = AtScaleCreate((new->threedee.ymin),
				      (new->threedee.ymax),
				      0,1,
				      new->threedee.y1axis->
					axis3.yscale->transform);

#ifdef DEBUG3D
  printf("3DInit: new->threedee.zmin %f and new->threedee.zmax %f\n", 
	 new->threedee.zmin, new->threedee.zmax);
#endif DEBUG3D
  new->threedee.zscale = AtScaleCreate(new->threedee.zmin, 
				     new->threedee.zmax,
				     0,1,
				     new->threedee.z1axis->
				       axis3.yscale->transform);

#ifdef DEBUG3D
    printf("Threedee.c: done creating scales. Create cube + compute layout\n");
#endif DEBUG3D
  
  new->threedee.axisCube = (Cube)malloc(sizeof(CubeRec));
  At3dComputeLayout(new);
    
  new->threedee.clickx =  new->threedee.clicky = 0;
  new->threedee.dragging = False;
  new->threedee.dragwidth = new->threedee.dragheight = 0;

#ifdef DEBUG3D
    printf("Threedee.c: Initialize finished\n");
#endif DEBUG3D

}

static void Resize(AtThreedeeWidget tw)
{
  int i;
  Widget w;
  
#ifdef DEBUG3D
    printf("Threedee.c: Resize begins\n");
#endif DEBUG3D

  if(tw->threedee.autoScale) AutoScale(tw);
  At3dComputeLayout(tw); /* automatically resizes axes */

#ifdef DEBUG3D
    printf("Threedee.c: Resize: done with At3dComputeLayout, now do plots\n");
#endif DEBUG3D

  free(tw->threedee.cumProjection);
  tw->threedee.cumProjection = (Projection)ProjCreate();

  /* Must resize all plots */
  for(i = 0; i < NUMCHILDREN(tw); i++) {
    w = CHILD(tw, i);
    if(XtIsSubclass(w, atPlot3WidgetClass)) {
      RESIZEPLOT3CHILD(tw, i);
      if(((AtPlot3Widget)w)->plot3.plotProjection)
	ProjMerge(tw->threedee.cumProjection, ((AtPlot3Widget)w)->
		  plot3.plotProjection);
    }
  }
}

static void Redisplay(tw, event, region)
     AtThreedeeWidget tw;
     XEvent *event;
     Region region;
{
  static void Redraw();

#ifdef DEBUG3D
  printf("Threedee.c: Redisplay called\n");
#endif DEBUG3D

  Redraw(tw);
}

static void Redraw(AtThreedeeWidget tw)
{
  int i;

#ifdef DEBUG3D
  printf("Threedee.c: Redraw called\n");
#endif DEBUG3D

  XClearWindow(XtDisplay(tw), XtWindow(tw));

  /* Redraw axes */

  for( i = 0; i < POSSIBLEAXES; i++) 
    if(tw->threedee.axesShown[i])
      AtAxis3Draw(XtDisplay(tw), XtWindow(tw), tw->threedee.axes[i]);
  
  /* Draw Projection */
  
  if(!(tw->threedee.valid))
    At3dComputeLayout(tw);
  
  if(tw->threedee.cumProjection)
    {    
      /* draw the projection polygons */ 
      ProjDraw(tw, tw->threedee.cumProjection);
    }

  if(tw->threedee.titleText)
    AtTextDraw(XtDisplay(tw), XtWindow(tw), tw->threedee.titleGC,
	       tw->threedee.titleText,
	       tw->threedee.titleX, 
	       tw->threedee.titleY);

#ifdef DEBUG3D
    printf("Threedee.c: Redraw finishes\n");
#endif DEBUG3D

  
}
  

static void Destroy(AtThreedeeWidget tw)
{
#ifdef DEBUG3D
  printf("Threedee.c: Destroy begins\n");
#endif DEBUG3D

  FreeTitle(tw);
  AtFontFamilyRelease(tw->threedee.ff);
  XtReleaseGC(tw->threedee.titleGC);
  XtReleaseGC(tw->threedee.dragGC);
  if(tw->threedee.cumProjection)
    free(tw->threedee.cumProjection);
 
  AtScaleDestroy(tw->threedee.xscale);
  AtScaleDestroy(tw->threedee.yscalex);
  AtScaleDestroy(tw->threedee.yscaley);
  AtScaleDestroy(tw->threedee.zscale);
  
  if(tw->threedee.x1axis) XtDestroyWidget(tw->threedee.x1axis);
  if(tw->threedee.x2axis) XtDestroyWidget(tw->threedee.x2axis);
  if(tw->threedee.x3axis) XtDestroyWidget(tw->threedee.x3axis);
  if(tw->threedee.x4axis) XtDestroyWidget(tw->threedee.x4axis);

  if(tw->threedee.y1axis) XtDestroyWidget(tw->threedee.x1axis);
  if(tw->threedee.y2axis) XtDestroyWidget(tw->threedee.y2axis);
  if(tw->threedee.y3axis) XtDestroyWidget(tw->threedee.y3axis);
  if(tw->threedee.y4axis) XtDestroyWidget(tw->threedee.y4axis);

  if(tw->threedee.z1axis) XtDestroyWidget(tw->threedee.z1axis);
  if(tw->threedee.z2axis) XtDestroyWidget(tw->threedee.z2axis);
  if(tw->threedee.z3axis) XtDestroyWidget(tw->threedee.z3axis);
  if(tw->threedee.z4axis) XtDestroyWidget(tw->threedee.z4axis);

  free(tw->threedee.axes);
  if(tw->threedee.axisCube) free(tw->threedee.axisCube);
  if(tw->threedee.plotRegion) XDestroyRegion(tw->threedee.plotRegion);
#ifdef DEBUG3D
    printf("Threedee.c: Destroy finished\n");
#endif DEBUG3D

  return;

}


static Boolean SetValues(current, request, new)
#define Changed(field) (new->threedee.field != current->threedee.field)
     AtThreedeeWidget current, request, new;
{
  int i;
  Boolean redraw = False; 
  Boolean layout = False;
  
#ifdef DEBUG3D
    printf("Threedee.c: SetValues begins\n");
#endif DEBUG3D

  if(Changed(downShear) || Changed(leftShear) || 
     Changed(marginWidth) || 
     Changed(marginHeight)) {
    layout = redraw = True;
  }

for (i = 0; i < POSSIBLEAXES; i++)
  if(Changed(axesShown[i]))
    layout = redraw = True;

  if(Changed(autoScale)) {
    AutoScale(new);
    redraw = True;
  }

if(Changed(titleColor))
    redraw = True;

  if(Changed(title) || Changed(fontFamily) || Changed(titleSize)) {
    FreeTitle(current);
    GetTitle(new);
    layout = redraw = True;
  }

  if(Changed(titlePosX) || Changed(titlePosY)) {
    layout = redraw = True;
  }

  if(Changed(xmin) || Changed(xmax) || Changed(ymin) || Changed(ymax) ||
     Changed(zmin) || Changed(zmax)) {
    redraw = True;
    layout = True;
  }

  if (layout)
    At3dComputeLayout(new);

#ifdef DEBUG3D
    printf("Threedee.c: SetValues done\n");
#endif DEBUG3D

  return(redraw);
}
#undef Changed


static void InsertChild(Widget w)
{
  AtThreedeeWidget tw = (AtThreedeeWidget)XtParent(w);

#ifdef DEBUG3D
  printf("Threedee.c: InsertChild begins\n");
#endif DEBUG3D

  /* Warn if child of wrong class */
  if(!XtIsSubclass(w, atPlot3WidgetClass)) {
    if(!XtIsSubclass(w, atAxis3ObjectClass))
      AtWarning(tw, 
		"InsertChild:tried to insert child not subclass of AtPlot3"); 
  }
  else{
    /* Call superclass' InsertChild to actually add  the child */
    (*superclass->composite_class.insert_child)(w);
    
    tw->threedee.valid = False;
    if(XtIsSubclass(w, atAxis3ObjectClass))
      At3dComputeLayout(tw);
    else if(XtIsSubclass(w, atPlot3WidgetClass))
      {

	Resize(tw);

      } /* if XtIsSubclass */
    /* if (XtIsRealized(tw))  */
    Redraw(tw);
  } /* else{ */
#ifdef DEBUG3D
    printf("Threedee.c: InsertChild done\n");
#endif DEBUG3D
}


static void DeleteChild(Widget w)
{
  AtThreedeeWidget tw = (AtThreedeeWidget)XtParent(w);
  /* If a plot3 is to be deleted, delete its projection from the 
     cumProjection. */

#ifdef DEBUG3D
    printf("Threedee.: DeleteChild begins\n");
#endif DEBUG3D
  tw->threedee.valid = False;

  if(XtIsSubclass(w, atPlot3WidgetClass)) {
    
    ProjDeleteSurface(tw->threedee.cumProjection, w);
		  
    /* polygons derived from a plotSurface have pointers pointing back
       to the widget containing the surface. These pointers serve as 
       tags, allowing deletion of only those polygons which "belong" 
       to that surface */
    
    (*superclass->composite_class.delete_child)(w);
    
    if (tw->threedee.autoScale)
      {
	AutoScale(tw);
	if(!(tw->threedee.valid))
	  At3dComputeLayout(tw);
      }

  }  
  
  Redraw(tw);
#ifdef DEBUG3D
  printf("Threedee.c: DeleteChild done\n");
#endif DEBUG3D
}


void At3dComputeLayout(AtThreedeeWidget tw)
{
  int i;
  int x1, x2, y1, y2;
  int a,b;
  AtAxis3Object ax;
  XPoint cubePts[6];
  GetTitle(tw);
  
#ifdef DEBUG3D
  printf("At3dComputeLayout begins\n");
  printf("At3dComputeLayout: tw->threedee.margin height = %d\n", 
	 tw->threedee.marginHeight);
  printf("At3dComputeLayout: tw->threedee.margin width = %d\n", 
	 tw->threedee.marginWidth);
#endif DEBUG3D

  x1 = tw->threedee.marginWidth;
  y2 = tw->threedee.marginHeight;
  x2 = (int)tw->core.width - x1 - 1;
  y1 = (int)tw->core.height - y2 - 1;

#ifdef DEBUG3D
  printf("At3dComputeLayout: tw core dimensions width %d and height %d\n", 
	 tw->core.width, tw->core.height);
  printf("At3dComputeLayout: x1 = %d, y1 = %d\n  x2 = %d, y2 = %d\n",
	 x1, y1, x2, y2);
#endif DEBUG3D

  if(tw->threedee.titleText) { 
#define TT tw->threedee.titleText

    if(tw->threedee.titlePosX == pLEFT) {
      tw->threedee.titleX = x1;
      x1 += AtTextWidth(TT);
    }
    else if (tw->threedee.titlePosX == pRIGHT) {
      x2 -= AtTextWidth(TT);
      tw->threedee.titleX = x2;
    }

    if(tw->threedee.titlePosY == pTOP) {
      tw->threedee.titleY = y1 + AtTextAscent(TT);
      y1 += AtTextHeight(TT);
    }
    else if (tw->threedee.titlePosY == pBOTTOM) {
      y2 -= AtTextHeight(TT);
      tw->threedee.titleY = y2 + AtTextAscent(TT);
    }

    if(tw->threedee.titlePosX == pCENTER)
      tw->threedee.titleX = (x2 - x1 - AtTextWidth(TT))/2;
    if(tw->threedee.titlePosY == pCENTER)
      tw->threedee.titleY = (y1 - y2 - AtTextHeight(TT))/2;

#ifdef DEBUG3D
    printf("At3dComputeLayout: after title poitioned,\n");
    printf("     x1 = %d, y1 = %d, x2 = %d, y2 = %d\n", x1, y1, x2, y2);
#endif DEBUG3D
#undef TT
  }    
  /* compute yAngle and related fields */

  tw->threedee.yAngle = atan(tan(tw->threedee.downShear)/
			       tan(tw->threedee.leftShear));
  tw->threedee.sintheta = sin(tw->threedee.yAngle);
  tw->threedee.costheta = cos(tw->threedee.yAngle);
  
  /* a is the length of the x axis, arbitrarily set = to that of the y axis */

  a = (int)((x2 - x1)/(1 + cos(tw->threedee.leftShear)));
  /* b is the height in pixels the y axis achieves as it goes from some x to
     that x plus a*costheta */
  b = (int)(a * sin(tw->threedee.downShear));

#ifdef DEBUG3D
    printf("At3dComputeLayout: a = %d, b = %d\n", a, b);
#endif DEBUG3D

  /* If autoScale, let AutoScale() set the values of the axes' endpoints */
  
  if(tw->threedee.autoScale)
    AutoScale(tw);
  else 
    for(i = 0; i < POSSIBLEAXES; i++) {
      if(tw->threedee.axes[i]) {
	ax = (AtAxis3Object)(tw->threedee.axes[i]);
#ifdef DEBUG3D
	printf("At3dComputeLayout: switch on type, type being %d\n",
	       ax->axis3.axisType);
#endif DEBUG3D
        switch(ax->axis3.axisType) {
        case AxisTypeX:{  
	  AtAxis3SetBounds(tw->threedee.axes[i], tw->threedee.xmin,
			   tw->threedee.xmax); }
        case AxisTypeY:{
	  AtAxis3SetBounds(tw->threedee.axes[i], tw->threedee.ymin,
			   tw->threedee.ymax); }
        case AxisTypeZ:{
	  AtAxis3SetBounds(tw->threedee.axes[i], tw->threedee.zmin,
			   tw->threedee.zmax);}
        default:{
          AtWarning(tw, "Threedee.c AutoScale:Illegal axis type");
	  printf("xType = %d\n", ax->axis3.axisType);}
	}
      }
    }

  /* In order to determine the width of each axis, the axis' ticvalues 
     and ticspacing and ticlabels must be computed. */
  
  for(i = 0; i < POSSIBLEAXES; i++) {
    if(tw->threedee.axesShown[i]){
      if((tw->threedee.axes[i]->axis3.axisType == AxisTypeX) ||
	 (tw->threedee.axes[i]->axis3.axisType == AxisTypeY)) {
	/* x and y axes both have length = a pixels*/
#ifdef DEBUG3D
	printf("Threedee.c: Layout calls AtAxis3ComputeTicSpacing\n");
	printf("      Args are axes[%d] and %d\n", i, a);
#endif DEBUG3D
	AtAxis3ComputeTicSpacing(tw->threedee.axes[i], a);
      } /* X or Y type */
      else /* AxisTypeZ */ {
#ifdef DEBUG3D
	printf("Threedee.c: Layout calls AtAxis3ComputeTicSpacing\n");
	printf("      Args are axes[%d] and %d\n", i, y1 - y2 - b);
#endif DEBUG3D
	AtAxis3ComputeTicSpacing(tw->threedee.axes[i], y1 - y2 - b);
      }
      /* note that  y1 - y2 - b is the height of the available area minus
	 the vertical span of the y axis in the window */
    }/* if axis shown */
  }/* for i */
  
  /* alter approximations to make room for all axes that are visible */

  y1 -= MAX(((tw->threedee.axesShown[XX1])?
	     AtAxis3Width(tw->threedee.x1axis):
	     0),
	    ((tw->threedee.axesShown[YY1])?
	       AtTextHeight(tw->threedee.y1axis->axis3.labelText):
	       0));
  y2 += MAX(((tw->threedee.axesShown[XX2])?
	     AtAxis3Width(tw->threedee.x2axis):
	     0),
	    ((tw->threedee.axesShown[YY2])?
	       AtTextHeight(tw->threedee.y2axis->axis3.labelText):
	       0));
  x1 += MAX(((tw->threedee.axesShown[ZZ1])?
	     AtAxis3Width(tw->threedee.z1axis):
	     0),
	    ((tw->threedee.axesShown[YY2])?
	     (AtAxis3Width(tw->threedee.y1axis) - 
	      AtTextWidth(tw->threedee.y1axis->axis3.labelText)):
	     0));
  x2 -= MAX(((tw->threedee.axesShown[ZZ2])?
	      AtAxis3Width(tw->threedee.z2axis):
	      0),
	     (tw->threedee.axesShown[YY1]?
	      (AtAxis3Width(tw->threedee.y1axis) - 
	       AtTextWidth(tw->threedee.y1axis->axis3.labelText)):
	      0));
  
  /* Set the axisCube points according to the above limits */

  tw->threedee.axisCube->frontleft = x1;
  tw->threedee.axisCube->backright = x2;
  tw->threedee.axisCube->frontbottom = y1;
  tw->threedee.axisCube->backtop = y2;

  /* a is the length of the x axis, = to that of the y axis.
     We need to recompute a and b because the x1, x2 have changed */

  a = (int)((x2 - x1)/(1 + cos(tw->threedee.leftShear)));
  /* b is the height in pixels the y axis achieves as it goes from some x to
     that x plus a*costheta */
  b = (int)(a * sin(tw->threedee.downShear));

  tw->threedee.axisCube->frontright = (x1 + a);
  tw->threedee.axisCube->fronttop = (y2 + b);
  tw->threedee.axisCube->backleft = (x2 - a);
  tw->threedee.axisCube->backbottom = (y1 - b);

  /* Resize tw's internal scales */

  AtScaleResize(tw->threedee.xscale, x1, x1 + a);
  AtScaleResize(tw->threedee.yscalex, x1 + a, x2);
  AtScaleResize(tw->threedee.yscaley, y1, y1 - b);
  AtScaleResize(tw->threedee.zscale, y1, y2 + b);

  /* Set up the region plotRegion. */
#define AxWi1(axindex) (tw->threedee.axes[axindex]->axis3.ticLength * \
			tw->threedee.axes[axindex]->axis3.ticSide)
#define AxWi2(axindex) (tw->threedee.axes[axindex]->axis3.ticLength * \
			( pRIGHT - tw->threedee.axes[axindex]->axis3.ticSide))

  cubePts[0].x = tw->threedee.axisCube->frontleft + 
    ((tw->threedee.axesShown[ZZ1])?
     AxWi1(ZZ1):
     0);
  cubePts[0].y = tw->threedee.axisCube->frontbottom - 
    ((tw->threedee.axesShown[XX1])?
     AxWi2(XX1):
     0);
  cubePts[1].x = tw->threedee.axisCube->frontright -  
    ((tw->threedee.axesShown[YY1])?
     AxWi2(YY1):
     0);
  cubePts[1].y = tw->threedee.axisCube->frontbottom - 
 ((tw->threedee.axesShown[XX1])?
  AxWi2(XX1):
  0);
  cubePts[2].x = tw->threedee.axisCube->backright - 
 ((tw->threedee.axesShown[ZZ2])?
  AxWi2(ZZ2):
  0);
  cubePts[2].y = tw->threedee.axisCube->backbottom;

  cubePts[3].x = tw->threedee.axisCube->backright -  
    ((tw->threedee.axesShown[ZZ2])?
     AxWi2(ZZ2):
     0);
  cubePts[3].y = tw->threedee.axisCube->backtop + 
  ((tw->threedee.axesShown[XX2])?
   AxWi1(XX2):
   0);
  cubePts[4].x = tw->threedee.axisCube->backleft +  
  ((tw->threedee.axesShown[YY2])?
   AxWi2(YY2):
   0);
  cubePts[4].y = tw->threedee.axisCube->backtop + 
  ((tw->threedee.axesShown[XX2])?
   AxWi1(XX2):
   0);
  cubePts[5].x = tw->threedee.axisCube->frontleft +  
  ((tw->threedee.axesShown[ZZ1])?
   AxWi1(ZZ1):
   0);
  cubePts[5].y = tw->threedee.axisCube->fronttop;

#undef AxWi1
#undef AxWi2

  /* Actually create the Region */
  tw->threedee.plotRegion = XPolygonRegion(cubePts, 6, EvenOddRule);
  
  /* Set the locations of all visible axes. Among other things this 
     sets their scales' lowpix and highpix values by AtScaleResize 
     and computes the ticcoords and subticcoords for the axes */

  /* As a rule, when setting axis locations, obey the axisCube 
     locations for the axes and use the min and maxvalues for tw's scales 
     as min and max values for axis scales. Otherwise, the axes that result
     may not have the same scale values as the plots. */

  AtAxis3SetLocation(tw->threedee.x1axis, 
		     x1, y1, x2, y1);
  if(tw->threedee.axesShown[XX2])
    AtAxis3SetLocation(tw->threedee.x2axis, 
		       x2 - a, y2, x2, y2);
  if(tw->threedee.axesShown[XX3])       
    AtAxis3SetLocation(tw->threedee.x3axis,
		       x1, y2 + b,
		       x1 + a, y2 + b);
  if(tw->threedee.axesShown[XX4])       
    AtAxis3SetLocation(tw->threedee.x4axis,
		       x1 +
		       (int)(x1 + x2 - a/2),
		       y2 +
		       (int)((y2 + y1)/2),
		       x1 +
		       (int)(x1 +x2 + a/2),
		       y2 +
		       (int)((y2 + y1)/2));
  AtAxis3SetLocation(tw->threedee.y1axis, 
		     x1 + a, y1, x2, y1 - b);
  if(tw->threedee.axesShown[YY2])
    AtAxis3SetLocation(tw->threedee.y2axis,
		       x1, y2 + b, x1 + a, y2);
  if(tw->threedee.axesShown[YY3])
    AtAxis3SetLocation(tw->threedee.y3axis, 
		       x1 + a, y2 + b, x2, y1);
  if(tw->threedee.axesShown[YY4])
    AtAxis3SetLocation(tw->threedee.y4axis, 
		       x1+
		       (int)(a/2),
		       y2 + 
		       (int)((y1 + y2 + b)/2),
		       x2 -
		       (int)(a/2),
		       y2 + 
		       (int)((y1 + y2 - b)/2));
  AtAxis3SetLocation(tw->threedee.z1axis, 
		     x1, y1, x1, y2);
  if(tw->threedee.axesShown[ZZ2])
    AtAxis3SetLocation(tw->threedee.z2axis, 
		       x2, y1 + b, x2, y2);
  if(tw->threedee.axesShown[ZZ3])
    AtAxis3SetLocation(tw->threedee.z3axis, 
		       x1 + a, y1, x1 + a, y2 - b);
  if(tw->threedee.axesShown[ZZ4])
    AtAxis3SetLocation(tw->threedee.z4axis, 
		       x1 +
		       (int)((x1 + x2)/2),
		       y1 -
		       (int)(b/2),
		       x1 + 
		       (int)((x1 + x2)/2), 
		       y2 +
		       (int)(b/2));

  /* axes -- scales,tic values, subtics, labels -- are already
     resized. Now resize plot3s */
  
  for(i = 0; i < NUMCHILDREN(tw); i++) 
    if(XtIsSubclass(CHILD(tw,i), atPlot3WidgetClass))
      RESIZEPLOT3CHILD(tw, i);
  
  tw->threedee.valid = True;    
}

static void ProjDraw(AtThreedeeWidget tw, Projection pro)
{
  int i;
  /* Draw Projection */

#ifdef DEBUG3D	    
	    printf("tw is valid/invalid? 1 = valid: %d\n",tw->threedee.valid);
#endif DEBUG3D    

  if(!(tw->threedee.valid))
    At3dComputeLayout(tw);

  if(pro)
    {
      /* draw the projection polygons */

#define poly(z, i) z->polygons[i]

#ifdef DEBUG3D	    
	    printf("About to draw polygons of cumProjection\n");
#endif DEBUG3D    

      for(i = 0; i < pro->num_polys; i++) {				 
        if(!(((AtPlot3Widget)(poly(pro, i)->tag))->plot3.noFill))
          XFillPolygon(XtDisplay((Widget)tw), XtWindow((Widget)tw),
                       ((AtPlot3Widget)(poly(pro, i)->tag))->plot3.fillGC,
                       (poly(pro, i)->vertices),
                       (poly(pro, i)->num_verts),
		       Nonconvex,
                       CoordModeOrigin);
        XDrawLines(XtDisplay((Widget)tw), XtWindow((Widget)tw),
                   ((AtPlot3Widget)(poly(pro, i)->tag))->plot3.edgeGC,
                   poly(pro, i)->vertices,
                   poly(pro, i)->num_verts,
                   CoordModeOrigin); 
      }
#undef poly
    }
}


#ifdef CONSTR
void ConstraintInitialize(Widget request, Widget new)
{

  AtThreedeeConstraints threedee_const = 
    (AtThreedeeConstraints)new->core.constraints;
  AtThreedeeWidget tw = (AtThreedeeWidget)XtParent(new);
  threedee_const->threedee.plotRegion = tw->threedee.plotRegion;
  threedee_const->threedee.displayed = 
    threedee_const->threedee.needsRedraw = True;
}

void ConstraintDestroy(Widget w)
{

  AtThreedeeConstraints cns = (AtThreedeeConstraints)w->core.constraints;
  XDestroyRegion(cns->threedee.plotRegion);
}

static Boolean ConstraintSetValues(current, request, new)
     Widget current, request, new;
{
  AtThreedeeWidget parent = (AtThreedeeWidget)XtParent(new);
  AtThreedeeConstraints newc = (AtThreedeeConstraints)new->core.constraints;
  AtThreedeeConstraints curc = (AtThreedeeConstraints)current->core.constraints;
#define Changed(field) (newc->threedee.field != curc->threedee.field)

  if(Changed(displayed) || Changed(needsRedraw) || Changed(plotRegion))
    Redraw(parent);

  if(Changed(plotRegion))
     Redraw(parent);

  return(False);
#undef Changed
}
#endif CONSTR


void Redraw3d(AtThreedeeWidget tw)
{
#ifdef DEBUG3D	    
	    printf("Redraw3d called\n");
#endif DEBUG3D    
/*  if(XtIsRealized((Widget)tw)) */
    XClearArea(XtDisplay(tw), XtWindow(tw), 0, 0,
	       tw->core.width, tw->core.height, True);
/*  else At3dError(tw, "Redraw3d: widget not realized"); */
}

void At3dError(AtThreedeeWidget tw, String msg)
{
  if(tw->threedee.errorCallback)
    XtCallCallbacks(tw, XtNerrorCallback, (caddr_t)msg);
  else
    XtWarning(msg);
}

void AutoScale(AtThreedeeWidget tw)
{
  Boolean first = True;
  AtPlot3Widget c;
  int i;
  double maxx, maxy, maxz, minx, miny, minz;

#ifdef DEBUG3D	    
	    printf("Threedee:AutoScale begins\n", i);
#endif DEBUG3D    
  tw->threedee.valid = False;

  
  for (i = 0; i < NUMCHILDREN(tw); i++) {
#ifdef DEBUG3D	    
	    printf("child %d\n", i);
#endif DEBUG3D    
    if(XtIsSubclass(CHILD(tw, i), atPlot3WidgetClass)) {
      /* check each plot3's maxx, minx, ... minz & take max & min of all
	 of these */
      if(first) {
	c = (AtPlot3Widget)CHILD(tw, i);
	maxx = c->plot3.maxx;
	minx = c->plot3.minx;
	maxy = c->plot3.maxy;
	miny = c->plot3.miny;
	maxz = c->plot3.maxz;
	minz = c->plot3.minz;
	first = False;
      }
      else {
	c = (AtPlot3Widget)CHILD(tw, i);
	if(maxx > c->plot3.maxx) maxx = c->plot3.maxx;
	if(minx < c->plot3.minx) minx = c->plot3.minx;
	if(maxy > c->plot3.maxy) maxy = c->plot3.maxy;
	if(miny < c->plot3.miny) miny = c->plot3.miny;
	if(maxz > c->plot3.maxz) maxz = c->plot3.maxz;
	if(minz < c->plot3.minz) minz = c->plot3.minz;
      }
    } 
  }
  for (i = 0; i < POSSIBLEAXES; i++)
    if(tw->threedee.axes[i])
#ifdef DEBUG3D	    
	    printf("axis %d\n", i);
#endif DEBUG3D
      {
	switch(tw->threedee.axes[i]->axis3.axisType) 
	  {
	  case AxisTypeX:
#ifdef DEBUG3D	    
	    printf("case X-axis\n");
#endif DEBUG3D
	    tw->threedee.axes[i]->axis3.min = (minx * .95);
	    tw->threedee.axes[i]->axis3.max = (maxx * 1.05);
	  case AxisTypeY:
#ifdef DEBUG3D	    
	    printf("case Y-axis\n");
#endif DEBUG3D
	    tw->threedee.axes[i]->axis3.min = (miny * .95);
	    tw->threedee.axes[i]->axis3.max = (maxy * 1.05);
	  case AxisTypeZ:
#ifdef DEBUG3D	    
	    printf("case Z-axis\n");
#endif DEBUG3D
	    tw->threedee.axes[i]->axis3.min = (minz * .95);
	    tw->threedee.axes[i]->axis3.max = (maxz * 1.05);
	  default:
	    AtWarning(tw, "Threedee.c AutoScale:Illegal axis type");
	    printf("Type = %d\n", tw->threedee.axes[i]->axis3.axisType);
	  }
      }
  /* At3dComputeLayout(tw); */
}

/*************************************************************************/
/******************************** ACTIONS ********************************/
/*************************************************************************/
#define DrawDragRect(w) \
  XDrawRectangle(XtDisplay(w), XtWindow(w), w->threedee.dragGC,\
		 w->threedee.clickx, w->threedee.clicky, \
		 w->threedee.dragwidth, w->threedee.dragheight)
#define EraseDragRect(w) DrawDragRect(w)

#define DrawDragLine(w) \
  XDrawLine(XtDisplay(w), XtWindow(w), w->threedee.dragGC,\
	    w->threedee.clickx, w->threedee.clicky, \
	    w->threedee.clickx + w->threedee.dragwidth, \
	    w->threedee.clicky + w->threedee.dragheight)
#define EraseDragLine(w) DrawDragLine(w)

#define PTU(sc, p) AtScalePixelToUser(sc, p)
#define UTP(sc, p) AtScaleUserToPixel(sc, p)


static void StartSelection(AtThreedeeWidget w, XButtonPressedEvent *event) {
AtThreedeePart *t = &w->threedee;

  t->clickx = event->x;
  t->clicky = event->y;
  t->currentRegion = GetEventRegion(w, event->x, event->y);
}


static void Drag(AtThreedeeWidget w, XMotionEvent *event)
{
  AtThreedeePart *t = &w->threedee;
  short reg;

  reg = GetEventRegion(w, event->x, event->y);

  if(reg == t->currentRegion) { /* stayed in same region */
    
    switch(reg) {
    case RegNONE:
      /* do nothing */
    case RegPLOT:
      if(t->dragging) EraseDragRect(w);
      t->dragwidth = event->x - t->clickx;
      t->dragheight = event->y - t->clicky;
      DrawDragRect(w);
      t->dragging = True;
    default: /* in an axis region */
      if(t->dragging) EraseDragLine(w);
      t->dragwidth = event->x - t->clickx;
      t->dragheight = event->y - t->clicky;
      DrawDragLine(w);
      t->dragging = True;
    } /* switch */

  } /* same region */

  else { /* exited region */
    if(t->dragging) {
      if(t->currentRegion == RegPLOT)
	EraseDragRect(w);
      else if(t->currentRegion != RegNONE)
	EraseDragLine(w);
      t->dragging = False;
    }
  } /* exited region */

  t->currentRegion = reg;
}


#define AxisToReg(t)\
  switch(t) {\
	     case XX1:\
	       return(RegX1AXIS);\
	     case XX2:\
	       return(RegX2AXIS);\
	     case XX3:\
	       return(RegX3AXIS);\
	     case XX4:\
	       return(RegX4AXIS);\
	     case YY1:\
	       return(RegY1AXIS);\
	     case YY2:\
	       return(RegY2AXIS);\
	     case YY3:\
	       return(RegY3AXIS);\
	     case YY4:\
	       return(RegY4AXIS);\
	     case ZZ1:\
	       return(RegZ1AXIS);\
	     case ZZ2:\
	       return(RegZ2AXIS);\
	     case ZZ3:\
	       return(RegZ3AXIS);\
	     case ZZ4:\
	       return(RegZ4AXIS);\
	     default: \
	       return(RegNONE);\
	    }

static void ComputePointValue(AtThreedeeWidget w, PointStruct *p, int pixx, int pixy)
{

  /* fills in the pixx, pixy, x, y and z fields of a Pointstruct if
     given pixx and pixy */
  Cube axcube = w->threedee.axisCube;
  double tan = w->threedee.sintheta / w->threedee.costheta;
  p->pixx = pixx;
  p->pixy = pixy;

  /* case are right face, right top face, left top face, front face in that
     order */
  p->y = ((pixx > axcube->frontright)?
	  ((pixy >= axcube->fronttop + 
	   (pixx - axcube->frontright) * tan)?
	   PTU(w->threedee.yscalex, pixx):
	   PTU(w->threedee.yscaley, pixy + (axcube->frontbottom - 
					    axcube->fronttop))):
	  ((pixy < axcube->fronttop)?
	   PTU(w->threedee.yscaley, pixy + (axcube->frontbottom - 
					     axcube->fronttop)):
	   PTU(w->threedee.yscalex, axcube->frontright)));
  /* this last being a way of specifying the depth y of the 
     front face */
 
  /* cases are right face, right top face, front face, left top
     face in that order */
  p->x = ((p->pixx > axcube->frontright)?
	  ((p->pixy >= axcube->fronttop + 
	    (pixx - axcube->frontright) * tan)?
	   PTU(w->threedee.xscale, axcube->frontright):
	   PTU(w->threedee.xscale, pixx - UTP(w->threedee.yscalex, p->y))):
	  ((p->pixy >= axcube->fronttop)?
	   PTU(w->threedee.xscale, pixx):
	   PTU(w->threedee.xscale, pixx - UTP(w->threedee.yscalex, p->y))));
	  
  p->z = ((p->pixx > axcube->frontright)?
	  ((p->pixy >= axcube->fronttop + 
	    (pixx - axcube->frontright) * tan)?
	   PTU(w->threedee.zscale, pixy - UTP(w->threedee.yscaley, p->y)):
	   PTU(w->threedee.zscale, axcube->fronttop)):
	  ((p->pixy >= axcube->fronttop)?
	   PTU(w->threedee.zscale, pixy):
	   PTU(w->threedee.zscale, pixy + UTP(w->threedee.yscaley, p->y))));

}
	
	
	      
  
  
static void EndSelection(AtThreedeeWidget w, XButtonReleasedEvent *event)
{
  AtThreedeePart *t = &w->threedee;
  AtScale *xscale = t->xscale;
  AtScale *yscalex = t->yscalex;
  AtScale *yscaley = t->yscaley;
  AtScale *zscale = t->zscale;
  Cube axisCube = t->axisCube;
  int fl = axisCube->frontleft;
  int ft = axisCube->fronttop;
  int fr = axisCube->frontright;
  int fb = axisCube->frontbottom;
  int deltapix;
  int reg;
  
  reg = GetEventRegion(w, event->x, event->y);
  
  if(t->dragging) {
    if(t->currentRegion == RegPLOT) EraseDragRect(w);
    else EraseDragLine(w);
  }
  if(reg == RegPLOT) {
    if((t->dragging)&&(sq(t->dragwidth) + sq(t->dragheight) > 16)) {
      RectangleStruct r;

#define maxypix(z) ((z[1].pixy > z[0].pixy)?1:0)
#define minypix(z) ((z[1].pixy < z[0].pixy)?1:0)
#define maxxpix(z) ((z[1].pixx > z[0].pixx)?1:0)
#define minxpix(z) ((z[1].pixx < z[0].pixx)?1:0)
	
      if(!(XRectInRegion(t->plotRegion, t->clickx,
			t->clicky, t->dragwidth, t->dragheight))) {
	/* drag rect not wholely contained in plot region */
	/* cancel selection */
	if(t->dragging) {
	  EraseDragRect(w);
	  t->dragging = False;
	}
	t->currentRegion = reg;
      }

      else { /* rect in region */
	r[0].pixx = t->clickx;
	r[0].pixy = t->clicky;
	r[1].pixx = event->x;
	r[1].pixy = event->y;
	
	if((r[maxxpix(r)].pixx - fr < 0) &&
	   (ft - r[minypix(r)].pixy < 0)) { 
	  
	  /* Rectangle is wholely contained in front face.
	     Assume has same depth as front face (arbitrarily) */
	  
	  r[0].y = r[1].y  = PTU(yscalex, fr);
	  r[0].x = PTU(xscale, r[0].pixx);
	  r[0].z = PTU(zscale, r[0].pixy);
	  r[1].x = PTU(xscale, r[1].pixx);
	  r[1].z = PTU(zscale, r[1].pixy);	    
	} /* end front face */
	
	else if(r[maxxpix(r)].pixx - fr > 
		ft - r[minypix(r)].pixy) {

	  /* Arbitrarily assume that rectangle is inside of 
	     plot space, positioned so as to touch the
	     right face of the axisCube */

	  r[1].y = r[0].y = PTU(yscalex, r[maxxpix(r)].pixx);
	  r[maxxpix(r)].x = PTU(xscale, fr);	
	  r[minxpix(r)].x = PTU(xscale, fr - t->dragwidth);
	  deltapix = fb - UTP(yscaley, r[0].y);
	  r[0].z = PTU(zscale, r[0].pixy + deltapix);
	  r[1].z = PTU(zscale, r[1].pixy + deltapix);
	} /* end right face*/ 
	
	else {  

	  /* Drag rect assumed to be touching top face */

	  r[1].y = r[0].y = PTU(yscaley , r[minypix(r)].pixy + (fb - ft));
	  r[minypix(r)].z = PTU(zscale, ft);
	  r[maxypix(r)].z = PTU(zscale, ft + t->dragheight);
	  deltapix = UTP(yscalex, r[0].y) - fr;
	  r[0].x = PTU(xscale, r[0].pixx - deltapix);
	  r[1].x = PTU(xscale, r[1].pixx - deltapix);
	}/* end top face*/
	
	XtCallCallbacks(w, XtNdragCallback, (caddr_t)r);
	t->dragging = False;

      }/* RectInRegion */
    } /* dragging */
      
    else {	/* not dragging or rectangle too small */
      PointStruct point;
      ComputePointValue(w, &point, event->x, event->y);
      XtCallCallbacks(w, XtNclickCallback, (caddr_t)&point);
    }
    
  }/* end if (RegPLOT) */
  
  else if((t->currentRegion == RegX1AXIS) ||
	  (t->currentRegion == RegX2AXIS) ||
	  (t->currentRegion == RegX3AXIS) ||
	  (t->currentRegion == RegX4AXIS) ||
	  (t->currentRegion == RegY1AXIS) ||
	  (t->currentRegion == RegY2AXIS) ||
	  (t->currentRegion == RegY3AXIS) ||
	  (t->currentRegion == RegY4AXIS) ||
	  (t->currentRegion == RegZ1AXIS) ||
	  (t->currentRegion == RegZ2AXIS) ||
	  (t->currentRegion == RegZ3AXIS) ||
	  (t->currentRegion == RegZ4AXIS))   {

    if(reg == t->currentRegion) 
      {
	
	if((t->dragging) && (sq(t->dragwidth) + sq(t->dragheight) > 16))
	  {
	    RectangleStruct r;
	    ComputePointValue(w, &(r[0]), t->clickx, t->clicky);
	    ComputePointValue(w, &(r[1]), event->x, event->y);
	    XtCallCallbacks(w, XtNdragCallback, (caddr_t)r);
	    t->dragging = False;
	  }
	else /* not dragging or rectangle too small */
	  {
	    PointStruct p;
	    ComputePointValue(w, &p, event->x, event->y);
	    XtCallCallbacks(w, XtNclickCallback, (caddr_t)&p);
	  }

      }
    else /* exited the axis' region */
      {
	/* cancel selection */
	if(t->dragging) {
	  EraseDragLine(w);
	  t->dragging = False;
	}
	t->currentRegion = reg;
      }

  } /* end elseif (one of the RegAXES */

  else  /* any other region */
    t->currentRegion = reg;
}

static void CancelSelection(AtThreedeeWidget w, XEvent *event)
{
  AtThreedeePart *t = &w->threedee;

  if((t->currentRegion == RegPLOT) && 
     (t->dragging)) 
    {
      EraseDragRect(w);
      t->dragging = False;
    }  
  else if(((t->currentRegion == RegX1AXIS) ||
	   (t->currentRegion == RegX2AXIS) ||
	   (t->currentRegion == RegX3AXIS) ||
	   (t->currentRegion == RegX4AXIS) ||
	   (t->currentRegion == RegY1AXIS) ||
	   (t->currentRegion == RegY2AXIS) ||
	   (t->currentRegion == RegY3AXIS) ||
	   (t->currentRegion == RegY4AXIS) ||
	   (t->currentRegion == RegZ1AXIS) ||
	   (t->currentRegion == RegZ2AXIS) ||
	   (t->currentRegion == RegZ3AXIS) ||
	   (t->currentRegion == RegZ4AXIS)) &&
	  (t->dragging))
    {
      EraseDragLine(w);
      t->dragging = False;
    }
  /* no change in currentRegion */
}
  

static void HandleMotion(AtThreedeeWidget w, 
			 XMotionEvent *event)
{
  AtThreedeePart *t = &w->threedee;
  PointStruct point;
  short reg;
  reg = GetEventRegion(w, event->x, event->y);
  if(reg == RegPLOT) {
    ComputePointValue(w, &point, event->x, event->y);
    XtCallCallbacks(w, XtNmotionCallback, (caddr_t)&point);
  }
  else if((t->currentRegion == RegX1AXIS) ||
	  (t->currentRegion == RegX2AXIS) ||
	  (t->currentRegion == RegX3AXIS) ||
	  (t->currentRegion == RegX4AXIS) ||
	  (t->currentRegion == RegY1AXIS) ||
	  (t->currentRegion == RegY2AXIS) ||
	  (t->currentRegion == RegY3AXIS) ||
	  (t->currentRegion == RegY4AXIS) ||
	  (t->currentRegion == RegZ1AXIS) ||
	  (t->currentRegion == RegZ2AXIS) ||
	  (t->currentRegion == RegZ3AXIS) ||
	  (t->currentRegion == RegZ4AXIS))   {
    ComputePointValue(w, &point, event->x, event->y);
    XtCallCallbacks(w, XtNmotionCallback, (caddr_t)&point);
  }
}
      

/* ********************************************************* */

short GetEventRegion(tw, eventx, eventy)
     AtThreedeeWidget tw;
     int eventx, eventy;
{
  int i;
  
  if(XPointInRegion(tw->threedee.plotRegion, eventx, eventy))
    return(RegPLOT);

  for(i = 0; i < POSSIBLEAXES; i++) {
    if(tw->threedee.axesShown[i])
      if(NearAxis(eventx, eventy, tw->threedee.axes[i],
		  tw->threedee.axisZone))
	AxisToReg(i); 
    /* axis3.axisType is AxisTypeX/AxisTypeY/AxisTypeZ */
  }
  return(RegNONE);
}


Boolean NearAxis(int x, int y, AtAxis3Object axis, int axis_zone)
{
  int targety;

#ifdef DEBUG3
  printf("Call to NearAxis\n");
#endif DEBUG3


  switch(axis->axis3.axisType) {

  case AxisTypeX:
   
    return((x <= axis->axis3.xscale->highpix) &&
	   (x >= axis->axis3.xscale->lowpix) &&
	   (y >= axis->axis3.yscale->highpix - axis_zone) &&
	   (y <= axis->axis3.yscale->lowpix + axis_zone));  

  case AxisTypeY:
    
    if ((x <= axis->axis3.xscale->highpix) &&
	(x >= axis->axis3.xscale->lowpix)) 
      {
	targety = AtScaleUserToPixel(axis->axis3.yscale,
		     AtScalePixelToUser(axis->axis3.yscale, x));
	  return((y >= targety - axis_zone) &&
		 (y <= targety + axis_zone));
      }
    else return(False);
  
  case AxisTypeZ:

    return((x >= axis->axis3.xscale->lowpix - axis_zone) &&
	   (x <= axis->axis3.xscale->highpix + axis_zone) &&
	   (y <= axis->axis3.yscale->highpix) &&
	   (y >= axis->axis3.yscale->lowpix));
    
  default:
    
    return(False);

  } /* switch */

}


void At3dReplaceSurface(Widget w, AtPlot3Widget tag)
{	
#ifdef DEBUG3
  printf("Call to ReplaceSurface\n");
#endif DEBUG3

  AtThreedeeWidget tw = (AtThreedeeWidget)w;
  ProjDeleteSurface(tw->threedee.cumProjection, tag);
  XtFree(tag->plot3.plotProjection);
  tag->plot3.plotProjection = 
    (Projection)ProjectSurface(tag, tag->plot3.plotSurface);
  ProjMerge(tw->threedee.cumProjection, 
	    tag->plot3.plotProjection);
  if(tw->threedee.autoScale)
    At3dComputeLayout(tw); /* only a change in layout if autoScale true */
}
    

