#ifdef SCCS
static char sccsid[]="@(#)pexdraw.c	1.29 Oki Electric Industry Co., Ltd. 93/05/24";
#endif
/*
 *			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.
 *
 *************************************************************************
 *
 *                         P E X D R A W
 *
 *  A PEX drawing program based on PDRAW,
 *
 *   
 *
 *************************************************************************/

#include <stdio.h>

#include <math.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>

#include <X11/PEX5/PEXlib.h>

#define DECL_GLOBALS 1

#include "pexdraw.h"

#define LIGHTMAX 8
PEXLookupTable	theLightLUT;
PEXTableIndex	theLightsOn[LIGHTMAX];
int		theLightsOnCount = 0;
PEXTableIndex	theLightsOff[1];
int		theLightsOffCount = 0;

static float	AmbientColor[3] = { 1,1,1 };
static float	LightColor[3] = { 0.9, 0.9, 0.9 };
static float	LightVector[3] = { 0.0, 0.0, -1.0 };

#define SELECT_COLOR_IND 3
static int theRandomColorIndex = 1;
#define RANDOM_COLOR_MAX 8

#define INT_STYLE_ELEM 3
#define REFL_EQN_ELEM 4
#define REFL_ATTRS_ELEM 5
#define INT_SHAD_ELEM 6
#define LIGHT_SRC_ELEM 7
#define MATRIX_ELEM 8
#define FACE_CULL_ELEM 9

extern renderProcs wksProcs, rdrProcs;

static int theMCSeqNo = 0;
int theHPFlag = 0;

/*************************************************************************
 *
 * A P P L I C A T I O N    P R O C E D U R E S
 *
 *************************************************************************
 *
 * Still a little overlap with the selected object interactions.
 */


/*************************************************************************
 * TranslateSelected - translate whatever is selected by the given vector
 */
void
TranslateSelected(points)
 PEXCoord *points;
{
  int error;
  char *eData;
  float deltaX, deltaY, deltaZ;
  PEXCoord *p;
  int i, nPoints;
  unsigned long count, length;
  PEXOCData *ocd, *OCData;

  if ( theSelectedElement == -1 ) return;

  deltaX = points[1].x - points[0].x;
  deltaY = points[1].y - points[0].y;
  deltaZ = points[1].z - points[0].z;

  if (!(PEXFetchElements( theDisplay, theSelectedStrux,
			   PEXBeginning, theSelectedElement+1,
			   PEXBeginning, theSelectedElement+1, theFF,
			   &count, &length, &eData ))) { return; }

  OCData = PEXDecodeOCs( theFF, count, length, eData );

  if ( OCData->oc_type  == PEXOCPolyline) {

    nPoints = OCData->data.Polyline.count;
    p = OCData->data.Polyline.points;

    for (i = 0; i < nPoints; i++, p++ ) {
      p->x += deltaX;
      p->y += deltaY;
      p->z +=deltaZ;
    }

    PEXSetElementPtr( theDisplay, theSelectedStrux, 
		     PEXBeginning, theSelectedElement+2 );
    PEXPolyline(theDisplay, theSelectedStrux, PEXOCStore, nPoints,  
		OCData->data.Polyline.points);
    PEXMarkers(theDisplay, theSelectedStrux, PEXOCStore, nPoints,  
	       OCData->data.Polyline.points);
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2);
  } else if ( OCData->oc_type == PEXOCTriangleStrip ) { 

    if (OCData->data.TriangleStrip.vertex_attributes) {
      /* should check to make sure there are no normals!!! */
      /* it would be fairly easy to work with these, but not now */
      return;
    }

    p = OCData->data.TriangleStrip.vertices.no_data;

    for (i = 0; i < OCData->data.TriangleStrip.count; i++, p++ ) {
      p->x += deltaX;
      p->y += deltaY;
      p->z += deltaZ;
    }

    PEXSetElementPtr( theDisplay, theSelectedStrux, 
		     PEXBeginning, theSelectedElement+2 );
    PEXTriangleStrip( theDisplay, theSelectedStrux, PEXOCStore, 
		     OCData->data.TriangleStrip.facet_attributes,
		     OCData->data.TriangleStrip.vertex_attributes,
		     OCData->data.TriangleStrip.color_type,
		     OCData->data.TriangleStrip.facet_data,
		     OCData->data.TriangleStrip.count,
		     OCData->data.TriangleStrip.vertices );
    PEXMarkers( theDisplay, theSelectedStrux, PEXOCStore, 
	       OCData->data.TriangleStrip.count,
	       OCData->data.TriangleStrip.vertices.no_data );
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2 );
  } else if ( OCData->oc_type == PEXOCNURBCurve ) { 

    if (OCData->data.NURBCurve.rationality == PEXRational) {
      int i;
      double w;
      PEXCoord4D *p4;

      p4 = OCData->data.NURBCurve.points.point_4d;
      for (i = 0; i < OCData->data.NURBCurve.count; i++, p4++ ) {
	w = 1.0 / p4->w;
	p4->x = ((p4->x * w) + deltaX) * p4->w;
	p4->y = ((p4->y * w) + deltaY) * p4->w;
	p4->z = ((p4->z * w) + deltaZ) * p4->w;
      }
    } else {
      p = OCData->data.NURBCurve.points.point;

      for (i = 0; i < OCData->data.NURBCurve.count; i++, p++ ) {
	p->x += deltaX;
	p->y += deltaY;
	p->z += deltaZ;
      }
    }

    PEXSetElementPtr( theDisplay, theSelectedStrux, 
		     PEXBeginning, theSelectedElement+2 );
    PEXNURBCurve(theDisplay, theSelectedStrux, PEXOCStore, 
		 OCData->data.NURBCurve.rationality,
		 OCData->data.NURBCurve.order, 
		 OCData->data.NURBCurve.knots,
		 OCData->data.NURBCurve.count, OCData->data.NURBCurve.points,
		 OCData->data.NURBCurve.tmin, OCData->data.NURBCurve.tmax );
    if (OCData->data.NURBCurve.rationality == PEXRational) {
      int i;
      double w;
      for (i = 0; i < OCData->data.NURBCurve.count; i++ ) {
	w = 1.0 / OCData->data.NURBCurve.points.point_4d[i].w;
	OCData->data.NURBCurve.points.point[i].x = 
	  OCData->data.NURBCurve.points.point_4d[i].x * w;
	OCData->data.NURBCurve.points.point[i].y = 
	  OCData->data.NURBCurve.points.point_4d[i].y * w;
	OCData->data.NURBCurve.points.point[i].z = 
	  OCData->data.NURBCurve.points.point_4d[i].z * w;
      }
    }
    PEXMarkers( theDisplay, theSelectedStrux, PEXOCStore, 
	       OCData->data.NURBCurve.count,
	       OCData->data.NURBCurve.points.point );
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2 );
  } else if ( OCData->oc_type == PEXOCText ) { 

    OCData->data.EncodedText.origin.x += deltaX;
    OCData->data.EncodedText.origin.y += deltaY;
    OCData->data.EncodedText.origin.z += deltaZ;

    PEXSetElementPtr( theDisplay, theSelectedStrux, 
		     PEXBeginning, theSelectedElement+2 );
    PEXEncodedText( theDisplay, theSelectedStrux, PEXOCStore, 
		   &OCData->data.EncodedText.origin,
		   &OCData->data.EncodedText.vector1,
		   &OCData->data.EncodedText.vector2,
		   OCData->data.EncodedText.count,
		   OCData->data.EncodedText.encoded_text );
    PEXMarkers( theDisplay, theSelectedStrux, PEXOCStore, 1,
	       &OCData->data.EncodedText.origin );
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2 );
  } else {
    printf("imagine a Motif alarm! selecting unknown element type %d\n",
	   OCData->oc_type );
    return;
  }
}

/*************************************************************************
 * StretchSelected
 */
StretchSelected(points)
     PEXCoord *points;
{
  int error;
  char *eData;
  float deltaX, deltaY, deltaZ;
  PEXCoord *p;
  int i, nPoints;
  unsigned long count, length;
  PEXOCData *ocd, *OCData;

  if ( theSelectedElement == -1 ) return;

  if (!(PEXFetchElements( theDisplay, theSelectedStrux,
			   PEXBeginning, theSelectedElement+1,
			   PEXBeginning, theSelectedElement+1, theFF,
			   &count, &length, &eData ))) { return; }

  OCData = PEXDecodeOCs( theFF, count, length, eData );

  if (OCData->oc_type  == PEXOCPolyline) {

    nPoints = OCData->data.Polyline.count;
    p = OCData->data.Polyline.points;

    p[theSelectedIndex] = points[0];
    PEXSetElementPtr( theDisplay, theSelectedStrux,
		     PEXBeginning,  theSelectedElement+2 );
    PEXPolyline(theDisplay, theSelectedStrux, PEXOCStore, nPoints,
		OCData->data.Polyline.points);
    PEXMarkers(theDisplay, theSelectedStrux, PEXOCStore, nPoints,
		OCData->data.Polyline.points);
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2);
  } else if (OCData->oc_type == PEXOCTriangleStrip ) { 

    if (OCData->data.TriangleStrip.vertex_attributes) {
      /* should check to make sure there are no normals!!! */
      return;
    }

    p = OCData->data.TriangleStrip.vertices.no_data;
    p[theSelectedIndex] = points[0];

    PEXSetElementPtr( theDisplay, theSelectedStrux,
		     PEXBeginning,  theSelectedElement+2 );
    PEXTriangleStrip( theDisplay, theSelectedStrux, PEXOCStore, 
		     OCData->data.TriangleStrip.facet_attributes,
		     OCData->data.TriangleStrip.vertex_attributes,
		     OCData->data.TriangleStrip.color_type,
		     OCData->data.TriangleStrip.facet_data,
		     OCData->data.TriangleStrip.count,
		     OCData->data.TriangleStrip.vertices );
    PEXMarkers( theDisplay, theSelectedStrux, PEXOCStore, 
	       OCData->data.TriangleStrip.count,
	       OCData->data.TriangleStrip.vertices.no_data );
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2);
  } else if ( OCData->oc_type == PEXOCNURBCurve ) { 

    if (OCData->data.NURBCurve.rationality == PEXRational) {
      PEXCoord4D *p4;

      p4 = OCData->data.NURBCurve.points.point_4d;
      p4[theSelectedIndex].x = points[0].x * p4[theSelectedIndex].w;
      p4[theSelectedIndex].y = points[0].y * p4[theSelectedIndex].w;
      p4[theSelectedIndex].z = points[0].z * p4[theSelectedIndex].w;
    } else {
      p = OCData->data.NURBCurve.points.point;
      p[theSelectedIndex] = points[0];
    }

    PEXSetElementPtr( theDisplay, theSelectedStrux, 
		     PEXBeginning, theSelectedElement+2 );
    PEXNURBCurve(theDisplay, theSelectedStrux, PEXOCStore, 
		 OCData->data.NURBCurve.rationality,
		 OCData->data.NURBCurve.order, 
		 OCData->data.NURBCurve.knots,
		 OCData->data.NURBCurve.count, OCData->data.NURBCurve.points,
		 OCData->data.NURBCurve.tmin, OCData->data.NURBCurve.tmax );
    if (OCData->data.NURBCurve.rationality == PEXRational) {
      int i;
      double w;
      for (i = 0; i < OCData->data.NURBCurve.count; i++ ) {
	w = 1.0 / OCData->data.NURBCurve.points.point_4d[i].w;
	OCData->data.NURBCurve.points.point[i].x = 
	  OCData->data.NURBCurve.points.point_4d[i].x * w;
	OCData->data.NURBCurve.points.point[i].y = 
	  OCData->data.NURBCurve.points.point_4d[i].y * w;
	OCData->data.NURBCurve.points.point[i].z = 
	  OCData->data.NURBCurve.points.point_4d[i].z * w;
      }
    }
    PEXMarkers( theDisplay, theSelectedStrux, PEXOCStore, 
	       OCData->data.NURBCurve.count,
	       OCData->data.NURBCurve.points.point );
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2 );
  } else if ( OCData->oc_type == PEXOCText ) { 

    OCData->data.EncodedText.origin = points[0];

    PEXSetElementPtr( theDisplay, theSelectedStrux, 
		     PEXBeginning, theSelectedElement+2 );
    PEXEncodedText( theDisplay, theSelectedStrux, PEXOCStore, 
		   &OCData->data.EncodedText.origin,
		   &OCData->data.EncodedText.vector1,
		   &OCData->data.EncodedText.vector2,
		   OCData->data.EncodedText.count,
		   OCData->data.EncodedText.encoded_text );
    PEXMarkers( theDisplay, theSelectedStrux, PEXOCStore, 1,
	       &OCData->data.EncodedText.origin );
    PEXDeleteElements( theDisplay, theSelectedStrux, 
		      PEXCurrent, -3, PEXCurrent, -2 );
  } else {
    printf("imagine a Motif alarm! selecting unknown element type %d, %d\n",
	   OCData->oc_type );
  }
}

/*************************************************************************
 * SelectSomething - make something look selected.
 *
 */
void SelectSomething(strux,element)
 long strux;
 long element;
{
  int error;
  char *eData;
  PEXCoord *p;  
  int nPoints;
  unsigned long count, length;
  PEXOCData *OCData;

  if (!(PEXFetchElements( theDisplay, strux,
			    PEXBeginning, element, PEXBeginning, element, 
			    theFF, &count, &length, &eData ))) { return; }

  PEXSetElementPtr( theDisplay, strux, PEXBeginning,  element );

  OCData = PEXDecodeOCs( theFF, count, length, eData );

  if (OCData->oc_type  == PEXOCPolyline) {

    nPoints = OCData->data.Polyline.count;
    p = OCData->data.Polyline.points;
    PEXMarkers(theDisplay, strux, PEXOCStore, nPoints, p);
    PEXSetElementPtr( theDisplay, strux, PEXCurrent,  -2 );
    PEXSetLineColorIndex( theDisplay, strux, PEXOCStore,
			 SELECT_COLOR_IND);
  } else if (OCData->oc_type == PEXOCTriangleStrip ) { 

   if (OCData->data.TriangleStrip.vertex_attributes) {
      /* should check to make sure there are no normals!!! */
      /* it would be fairly easy to work with these, but not now */
      return;
    }

    PEXMarkers(theDisplay, strux, PEXOCStore, 
	       OCData->data.TriangleStrip.count, 
	       OCData->data.TriangleStrip.vertices.no_data );
    PEXSetElementPtr( theDisplay, strux, PEXCurrent,  -2 );
    PEXSetSurfaceColorIndex( theDisplay, strux, PEXOCStore, 
			    SELECT_COLOR_IND);
  } else if (OCData->oc_type == PEXOCNURBCurve ) { 

    if (OCData->data.NURBCurve.rationality == PEXRational) {
      int i;
      double w;
      for (i = 0; i < OCData->data.NURBCurve.count; i++ ) {
	w = 1.0 / OCData->data.NURBCurve.points.point_4d[i].w;
	OCData->data.NURBCurve.points.point[i].x = 
	  OCData->data.NURBCurve.points.point_4d[i].x * w;
	OCData->data.NURBCurve.points.point[i].y = 
	  OCData->data.NURBCurve.points.point_4d[i].y * w;
	OCData->data.NURBCurve.points.point[i].z = 
	  OCData->data.NURBCurve.points.point_4d[i].z * w;
      }
    }

    PEXMarkers(theDisplay, strux, PEXOCStore, 
	       OCData->data.NURBCurve.count, 
	       OCData->data.NURBCurve.points.point );
    PEXSetElementPtr( theDisplay, strux, PEXCurrent,  -2 );
    PEXSetLineColorIndex( theDisplay, strux, PEXOCStore, 
			    SELECT_COLOR_IND);
  } else if (OCData->oc_type == PEXOCText ) { 

    if (GetTextRect(OCData, theSelectedStrux, theSelectedElement-1, &p)) {
      PEXMarkers(theDisplay, strux, PEXOCStore, 4, p);
    } else {
      PEXMarkers(theDisplay, strux, PEXOCStore, 1,
		 &OCData->data.EncodedText.origin );
    }
    PEXSetElementPtr( theDisplay, strux, PEXCurrent,  -2 );
    PEXSetTextColorIndex( theDisplay, strux, PEXOCStore, 
			    SELECT_COLOR_IND);
  } else {
    printf("imagine a Motif alarm! selecting unknown element type %d\n",
	   OCData->oc_type );
    PEXFreeOCData(count, OCData);
    free(eData);
    return;
  }

  theSelectedStrux = strux;
  theSelectedElement = element;
  PEXFreeOCData(count, OCData);
  free(eData);
}

/*************************************************************************
 * ClearSelection
 *
 * Implicitly knows that each element has a color followed by a primitive
 *  - color
 *  - primitive
 *  
 * The selected Element has an extra color between the color & prim and 
 * the point primitive directly afterwords.  Two extra elements.  This
 * will be a headache if multiple elements are allowed to be selected.
 *
 *  - color
 *  - select color
 *  - primitive
 *  - points.
 *
 * The selected color is really at theSelectedElement. Set the pointer and 
 * delete it. The element pointer moves forward.  Skip two elements and 
 * delete the points.
 *
 */
void ClearSelection()
{
  if ( theSelectedElement == -1 ) return;

  PEXDeleteElements( theDisplay, theSelectedStrux,
		    PEXBeginning, theSelectedElement,
		    PEXBeginning, theSelectedElement);

  PEXDeleteElements( theDisplay, theSelectedStrux,
		    PEXBeginning, theSelectedElement+1,
		    PEXBeginning, theSelectedElement+1);

  theSelectedStrux = -1;
  theSelectedElement= -1;
}

/*************************************************************************
 * DeleteSelected
 */
void DeleteSelected()
{
  if ( theSelectedElement == -1 ) return;

  PEXDeleteElements( theDisplay, theSelectedStrux,
		    PEXBeginning, theSelectedElement-1,
		    PEXBeginning, theSelectedElement+2);

  theSelectedStrux = -1;
  theSelectedElement= -1;

}
/*************************************************************************
 * ColorGroup -- this is in lieu of SelectGroup and the management of
 * a group of selected objects.
 *
 */
void ColorGroup( count, struxArray, elemArray )
     int count;
     long *struxArray;
     int *elemArray;
{
  int i;
  unsigned long length;

  PEXElementInfo *eInfo;
  unsigned long eCount;

  if ( ++theRandomColorIndex > RANDOM_COLOR_MAX) theRandomColorIndex = 2;

  for (i = 0; i < count; i++ ) {
       if(PEXGetElementInfo(theDisplay, struxArray[i],
			    PEXBeginning, elemArray[i],
			    PEXBeginning, elemArray[i],
			    theFF, &eCount, &eInfo )){
    if ((eCount == 1) &&
	((eInfo->type >= OCPRIMTAB_BASE) && (eInfo->type <= OCPRIMTAB_MAX))) {

      PEXDeleteElements( theDisplay, struxArray[i],
			PEXBeginning, elemArray[i]-1,
			PEXBeginning, elemArray[i]-1 );

      switch (myOCPrimType[eInfo->type-OCPRIMTAB_BASE]) {
      case OCPRIM_MARKER:
	PEXSetMarkerColorIndex( theDisplay, struxArray[i], PEXOCStore,
			     theRandomColorIndex );
	break;
      case OCPRIM_LINE:
	PEXSetLineColorIndex( theDisplay, struxArray[i], PEXOCStore,
			     theRandomColorIndex );
	break;
      case OCPRIM_PGON:
	PEXSetSurfaceColorIndex( theDisplay, struxArray[i], PEXOCStore, 
			     theRandomColorIndex );
	break;
      case OCPRIM_TEXT:
	PEXSetTextColorIndex( theDisplay, struxArray[i], PEXOCStore,
			     theRandomColorIndex );
	break;
      default:
	break;
      }
    }
  } else { printf("PEXGetStructureInfo failed\n");}
}
}

/*************************************************************************
 * InitStrux
 */
PEXStructure 
InitStrux()
{
  PEXName names[2];
  int error;
  PEXMatrix matrix;
  PEXReflectionAttributes reflAttrs;
  PEXStructure struxid;

  struxid = PEXCreateStructure(theDisplay);

  PEXSetEditingMode(theDisplay, struxid, PEXStructureInsert);

  theRenderProcs.Post( struxid );

  /*
   * need this for picking
   */
  names[0] = 2;
  names[1] = 1;
  PEXAddToNameSet(theDisplay, struxid, PEXOCStore, 2, names );

  PEXSetViewIndex(theDisplay, struxid, PEXOCStore, 1);

  /* INT_STYLE_ELEM == element 3 */
  PEXSetInteriorStyle(theDisplay, struxid, PEXOCStore, PEXInteriorStyleHollow );

  /* REFL_EQN_ELEM  == element 4 */
  PEXSetReflectionModel(theDisplay, struxid, PEXOCStore, PEXReflectionSpecular);

  /* set the area properties */
  reflAttrs.ambient = 0.3;
  reflAttrs.diffuse = 0.3;
  reflAttrs.specular = 0.7;
  reflAttrs.specular_conc = 50.0;
  reflAttrs.transmission = 0.0;
  reflAttrs.specular_color.type = PEXColorTypeRGB;
  reflAttrs.specular_color.value.rgb.red   = 1.0;
  reflAttrs.specular_color.value.rgb.green = 1.0;
  reflAttrs.specular_color.value.rgb.blue  = 1.0;

  PEXSetReflectionAttributes(theDisplay, struxid, PEXOCStore, &reflAttrs);
  /* INT_SHAD_ELEM  == element */
  PEXSetSurfaceInterpMethod(theDisplay, struxid, PEXOCStore, 
			    PEXSurfaceInterpNone);
  PEXSetLightSourceState(theDisplay, struxid, PEXOCStore, 
			 theLightsOnCount, theLightsOn, 
			 theLightsOffCount, theLightsOff );
  PEXRotate(PEXXAxis, 0.0, theMCMatrix.matrix );
  PEXSetLocalTransform(theDisplay, struxid, PEXOCStore, PEXReplace, 
		       theMCMatrix.matrix);
  theMCMatrix.strux = struxid;
  theMCMatrix.seqNo = ++theMCSeqNo;

  return (struxid);
}

/*************************************************************************
 * InsertLine
 */
void InsertLineCmd( nPoints, points, select )
     int nPoints;
     PEXCoord *points;
     int select;
{

  int error, e;

  if (theNewStrux == 0) {
    theNewStrux = InitStrux();
  } else if ( theSelectedElement != -1 ) {
    ClearSelection();
  }

  PEXSetElementPtr( theDisplay, theNewStrux, PEXEnd,  0 );

  PEXSetLineColorIndex( theDisplay, theNewStrux, PEXOCStore, 1);

  PEXPolyline(theDisplay, theNewStrux, PEXOCStore, nPoints, points);

  if (select) {
    PEXStructureInfo info;

    if (PEXGetStructureInfo(theDisplay, theNewStrux, theFF, PEXElementPtr,
			    &info )) {
      SelectSomething( theNewStrux, info.element_pointer );
    } else { printf("PEXGetStructureInfo failed\n");}
  }
}

/*************************************************************************
 * InsertTriStrip
 */
void
InsertTriStripCmd( nPoints, points, select )
     int nPoints;
     PEXCoord *points;
     int select;
{
  int error, e;
  PEXArrayOfFacetData tooLateToCast;
  PEXArrayOfVertex too;  /* should be able to cast these somehow, too late */

  if (theNewStrux == 0) {
    theNewStrux = InitStrux();
  } else if ( theSelectedElement != -1 ) {
    ClearSelection();
  }

  PEXSetElementPtr( theDisplay, theNewStrux, PEXEnd,  0 );

  PEXSetSurfaceColorIndex( theDisplay, theNewStrux, PEXOCStore, 1);
  too.no_data = points;
  tooLateToCast.index = NULL;
  PEXTriangleStrip( theDisplay, theNewStrux, PEXOCStore, 0, 0, PEXColorTypeRGB,
		   tooLateToCast, nPoints, too);

  if (select) {
    PEXStructureInfo  info;

    if (PEXGetStructureInfo(theDisplay, theNewStrux, theFF, PEXElementPtr,
			    &info )) {
      SelectSomething( theNewStrux, info.element_pointer );
    } else { printf("PEXGetStructureInfo failed\n");}
  }
}

/*************************************************************************
 * InsertNURBCurveCmd
 */
void InsertNURBCurveCmd( nPoints, points, select )
     int nPoints;
     PEXCoord *points;
     int select;
{
  int order = 3;
  float *knots, knotValue;
  int error, e, i;
  PEXArrayOfCoord p;

  if (nPoints < order) {printf("not enough points\n"); return;}

  if (theNewStrux == 0) {
    theNewStrux = InitStrux();
  } else if ( theSelectedElement != -1 ) {
    ClearSelection();
  }

  knots = (float *)malloc((nPoints+order)*sizeof(float)); if (!knots)
    {printf("Knots\n"); exit(1);}
  p.point = points;

  /*
   * generate a knot vector for a uniform B-splines
   */
  knotValue = 0.0;
  for (i = 0; i < order; i++ ) knots[i] = knotValue;
  knotValue++;
  for (i = order; i < nPoints; i++, knotValue++) knots[i] = knotValue;
  for (i = nPoints; i < nPoints+order; i++ ) knots[i] = knotValue;

  PEXSetElementPtr( theDisplay, theNewStrux, PEXEnd,  0 );

  PEXSetLineColorIndex( theDisplay, theNewStrux, PEXOCStore, 1);

  PEXNURBCurve(theDisplay, theNewStrux, PEXOCStore, PEXNonRational, order, 
	       knots, (unsigned int)nPoints, p, 
	       (double)0.0, (double)knotValue);

  free((char *)knots);

  if (select) {
    PEXStructureInfo info;

    if (PEXGetStructureInfo(theDisplay, theNewStrux, theFF, PEXElementPtr,
			    &info )) {
      SelectSomething( theNewStrux, info.element_pointer );
    } else { printf("PEXGetStructureInfo failed\n");}
  }
}

void Cross3D( A, B, C )
     PEXVector *A;
     PEXVector *B;
     PEXVector *C;
{
  C->x = (A->y * B->z) - (A->z * B->y);
  C->y = (A->z * B->x) - (A->x * B->z);
  C->z = (A->x * B->y) - (A->y * B->x);
}

/*************************************************************************
 * InsertCircleCmd - do a rational NURB. There are at least three ways to do
 *  this:
 * 2 are found on page 374, Mathematical Elements for Computer Graphics, 
 *   2nd ed. David F. Rogers and J Alan Adams. McGraw Hill, 1990.
 * One of which is the triangle method, using 7 control points at the corners
 *   and middles of an equilateral triangle, with knot vector
 *   [ 0, 0, 0, 1, 1, 2, 2, 3, 3, 3 ] and weights ( or H or W values )
 *   [ 1.0, 0.5, 1.0, 0.5, 1.0, 0.5, 1.0]
 *
 * The second is a square, with control points at the corners and midpoints.
 *  X (knots) = [0,0,0,1,1,2,2,3,3,4,4,4]
 *  W         = [1.0, 0.707, 1.0, 0.707, 1.0, 0.707, 1.0] 
 *                                           0.707 = sqrt(2)/sqrt(2)
 *
 * A third is a square, but only two of the sides have midpoint controls.
 *  X [ 0,0,0, 0.25, 0.5, 0.5, 0.75, 1,1,1]
 *  W [1, 0.5, 0.5, 1, 0.5, 0.5, 1]
 * from Piegl, Les. On NURBS: A Survey. IEEE CG&A, January 1991.
 *
 * We are going to use the triangle.
 *
 * we have two points, center of circle & triangle & p1 == p7.
 *  distance to p2, & p6  is RADICAL3 * distance from p1 to center.
 *  distance from p1 to p4 ( top of triangle ) is 3 * distance to center.
 *  just make p3 & p5 midpoints.
 *
 * We must have a thrid point to construct the plane.
 *
 */
void InsertCircleCmd( nPoints, points, select )
     int nPoints;
     PEXCoord *points;
     int select;
{
  int order = 3;
  float knots[10];
  PEXCoord4D p4[7];
  PEXArrayOfCoord p;
  double distance;
  PEXVector A, B, C;

  if (nPoints != 3) {printf("not enough points\n"); return;}

  if (theNewStrux == 0) {
    theNewStrux = InitStrux();
  } else if ( theSelectedElement != -1 ) {
    ClearSelection();
  }

  A.x = points[0].x - points[1].x;
  A.y = points[0].y - points[1].y;
  A.z = points[0].z - points[1].z;

  B.x = points[2].x - points[1].x;
  B.y = points[2].y - points[1].y;
  B.z = points[2].z - points[1].z;

  Cross3D( &A, &B, &C );
  Cross3D( &A, &C, &B );
  PEXNormalizeVectors( 1, &B, &B );

  /* 
   * B now contains a unit vector perpedicular to A in the plane
   * defined by the three input points.  After we get the distance
   * we can define all of the points of the equilateral triangle.
   */
  distance = sqrt(A.x*A.x + A.y*A.y + A.z*A.z);

#define RADICAL3 1.732

  p4[0].x =points[0].x; p4[0].y =points[0].y; p4[0].z =points[0].z; 
  p4[0].w = 1.0;

  p4[6] = p4[0];

  /*
   * rational NURB control points are given as x*w, y*w, z*w, w 
   * p1 & p7 are the midpoint of the base.
   * p2 & p6 are the corners of base.
   */
  p4[1].w = 0.5;
  p4[1].x = (points[0].x + RADICAL3 * distance * B.x) * p4[1].w;
  p4[1].y = (points[0].y + RADICAL3 * distance * B.y) * p4[1].w;
  p4[1].z = (points[0].z + RADICAL3 * distance * B.z) * p4[1].w;

  p4[5].w = 0.5;
  p4[5].x = (points[0].x - RADICAL3 * distance * B.x) * p4[1].w;
  p4[5].y = (points[0].y - RADICAL3 * distance * B.y) * p4[1].w;
  p4[5].z = (points[0].z - RADICAL3 * distance * B.z) * p4[1].w;

  /* the top of the triangle is 3X past the center */

  p4[3].w = 0.5;
  p4[3].x = (points[0].x - 3 * A.x) * p4[1].w;
  p4[3].y = (points[0].y - 3 * A.y) * p4[1].w;
  p4[3].z = (points[0].z - 3 * A.z) * p4[1].w;

  /* now get the midpoints, special case of weights = 0.5 !!! */

  p4[2].w = p4[1].w + p4[3].w;
  p4[2].x = p4[1].x + p4[3].x;
  p4[2].y = p4[1].y + p4[3].y;
  p4[2].z = p4[1].z + p4[3].z;

  p4[4].w = p4[5].w + p4[3].w;
  p4[4].x = p4[5].x + p4[3].x;
  p4[4].y = p4[5].y + p4[3].y;
  p4[4].z = p4[5].z + p4[3].z;

  p.point_4d = p4;

  /*
   * generate a knot vector for a uniform B-splines
   *   [ 0, 0, 0, 1, 1, 2, 2, 3, 3, 3 ]
   */
  knots[0] = 0; knots[1] = 0; knots[2] = 0;
  knots[3] = 1; knots[4] = 1;  
  knots[5] = 2; knots[6] = 2;
  knots[7] = 3; knots[8] = 3; knots[9] = 3;

  PEXSetElementPtr( theDisplay, theNewStrux, PEXEnd,  0 );

  PEXSetLineColorIndex( theDisplay, theNewStrux, PEXOCStore, 1);

  PEXNURBCurve(theDisplay, theNewStrux, PEXOCStore, PEXRational,
	       order, knots, (unsigned int)7, p, 
	       (double)0.0, (double)3.0);

  if (select) {
    PEXStructureInfo info;

    if (PEXGetStructureInfo(theDisplay, theNewStrux, theFF, PEXElementPtr,
			    &info )) {
      SelectSomething( theNewStrux, info.element_pointer );
    } else { printf("PEXGetStructureInfo failed\n");}
  }
}

/*************************************************************************
 * InsertPolygonCmd
 */
void InsertPolygonCmd( nPoints, points, select )
     int nPoints;
     PEXCoord *points;
     int select;
{

  if (nPoints < 3) {printf("not enough points\n"); return;}

  if (theNewStrux == 0) {
    theNewStrux = InitStrux();
  } else if ( theSelectedElement != -1 ) {
    ClearSelection();
  }

  PEXSetElementPtr( theDisplay, theNewStrux, PEXEnd,  0 );

  PEXSetSurfaceColorIndex( theDisplay, theNewStrux, PEXOCStore, 1);

  PEXFillArea(theDisplay, theNewStrux, PEXOCStore, PEXShapeUnknown, 
	      True, /* ignore edges, for the heck of it */
	       (unsigned int)nPoints, points );

  if (select) {
    PEXStructureInfo info;

    if (PEXGetStructureInfo(theDisplay, theNewStrux, theFF, PEXElementPtr,
			    &info )) {
      SelectSomething( theNewStrux, info.element_pointer );
    } else { printf("PEXGetStructureInfo failed\n");}
  }
}

/*************************************************************************
 * InsertTextCmd - 
 */
void InsertTextCmd( point,nChars,charStr, flags, select )
     PEXCoord *point;
     int nChars;
     char *charStr;
     int flags;
     int select;
{
  PEXVector vector1, vector2;
  int error, e;

  if (theNewStrux == 0) {
    theNewStrux = InitStrux();
  } else if ( theSelectedElement != -1 ) {
    ClearSelection();
  }

    vector1.x = 1.0;
    vector1.y = 0.0;
    vector1.z = 0.0;
    vector2.x = 0.0;
    vector2.y = 1.0;
    vector2.z = 0.0;

  PEXSetElementPtr( theDisplay, theNewStrux, PEXEnd,  0 );

  PEXSetTextColorIndex( theDisplay, theNewStrux, PEXOCStore, 1);

  PEXText(theDisplay, theNewStrux, PEXOCStore,
	  point, &vector1, &vector2, nChars, charStr );

  if (select) {
    PEXStructureInfo info;

    if (PEXGetStructureInfo(theDisplay, theNewStrux, theFF, PEXElementPtr,
			    &info )) {
      SelectSomething( theNewStrux, info.element_pointer );
    } else { printf("PEXGetStructureInfo failed\n");}
  }
}

/*************************************************************************
 *  GetXPointsFromSelected
 *
 *  generate X coordinates for each vertex of the selected object.
 *   - inquire element contents
 *   - map from world [should be model] to screen using MapWCPointsToX
 *
 * ALLOCATES points array.
 */
int
GetXPointsFromSelected(nPoints_return, points )
     int *nPoints_return;
     XPoint **points;
{
  int i;
  int error;
  char *eData;
  PEXCoord *p;  
  int nPoints, nGot;
  unsigned long count, length;
  PEXOCData *OCData, *matrixOC;
  MCMatrix mc;

  if (!(PEXFetchElements( theDisplay, theSelectedStrux,
			   PEXBeginning, theSelectedElement+1,
			   PEXBeginning, theSelectedElement+1, theFF,
			   &count, &length, &eData ))) { return; }


  OCData = PEXDecodeOCs( theFF, count, length, eData );
  XFree((char *)eData);

  if (OCData->oc_type  == PEXOCPolyline) {
    nPoints = OCData->data.Polyline.count;
    p = OCData->data.Polyline.points;
  } else if (OCData->oc_type  == PEXOCTriangleStrip ) {
    nPoints = OCData->data.TriangleStrip.count;
    p = OCData->data.TriangleStrip.vertices.no_data;
  } else if (OCData->oc_type  == PEXOCNURBCurve ) {
    if (OCData->data.NURBCurve.rationality == PEXRational) {
      int i;
      double w;
      for (i = 0; i < OCData->data.NURBCurve.count; i++ ) {
	w = 1.0 / OCData->data.NURBCurve.points.point_4d[i].w;
	OCData->data.NURBCurve.points.point[i].x = 
	  OCData->data.NURBCurve.points.point_4d[i].x * w;
	OCData->data.NURBCurve.points.point[i].y = 
	  OCData->data.NURBCurve.points.point_4d[i].y * w;
	OCData->data.NURBCurve.points.point[i].z = 
	  OCData->data.NURBCurve.points.point_4d[i].z * w;
      }
    }

    nPoints = OCData->data.NURBCurve.count;
    p = OCData->data.NURBCurve.points.point;
  } else if (OCData->oc_type  == PEXOCText ) {

    PEXSetElementPtr( theDisplay, theSelectedStrux, PEXBeginning,  5 );

    if (GetTextRect(OCData, theSelectedStrux, theSelectedElement-1, &p)) {
      nPoints = 4;
    } else {
      nPoints = 1;
      p = &OCData->data.EncodedText.origin;
    }
  } else {
    printf("imagine a Motif alarm! selecting unknown element type %d\n",
	   OCData->oc_type );
    PEXFreeOCData(count, OCData);
    free(eData);
    return (1);
  }

/* do not need this now.
   if (!(PEXFetchElements( theDisplay, theSelectedStrux,
			   PEXBeginning, MATRIX_ELEM,
			   PEXBeginning, MATRIX_ELEM, theFF,
			   &count, &length, &edata ))) { return; }

  matrixOC = PEXDecodeOCs( theFF, count, length, eData );
  XFree((char *)eData);

  theMCMatrix.strux = theSelectedStrux;
  theMCMatrix.seqNo = ++theMCSeqNo;
  bcopy( theMCMatrix.matrix, 
	matrixOC->data.SetLocalTransform.transform, sizeof(PEXMatrix));
*/
  nGot = theRenderProcs.MapMCToX(&theMCMatrix, nPoints, p, points );

  PEXFreeOCData(count, OCData);
  PEXFreeOCData(count, matrixOC);
  free(eData);

  if (nGot != nPoints) return (error);

  *nPoints_return = nPoints;
  return (0);
}

/*************************************************************************
 * InsertMClipCmd - take two points and create a positiion & vector, that
 * defines a model clipping plane.
 * Wow is me, the UI for this should really be thought out more. For now,
 * We will put:
 *  - marker color
 *  - line - When selected.
 *  - marker
 *  - model clip indicator
 *  - model clip volume
 */
void InsertMClipCmd( points, select )
     PEXCoord *points;
     int select;
{
  PEXStructure strux;
  int error, e;
  PEXHalfSpace halfSpace;

  if ( theSelectedElement != -1 ) {
    strux = theSelectedStrux;
  } else if (theNewStrux != 0) {
    strux = theNewStrux;
  }

  if ( theSelectedElement != -1 ) {
    ClearSelection();
  }

  halfSpace.point = points[0];
  halfSpace.vector.x = points[1].x - points[0].x;
  halfSpace.vector.y = points[1].y - points[0].y;
  halfSpace.vector.z = points[1].z - points[0].z;

  PEXNormalizeVectors(1, &halfSpace.vector, &halfSpace.vector);

  points[1].x = points[0].x + halfSpace.vector.x;
  points[1].y = points[0].y + halfSpace.vector.y;
  points[1].z = points[0].z + halfSpace.vector.z;

  PEXSetElementPtr( theDisplay, strux, PEXBeginning, FACE_CULL_ELEM );
  PEXSetMarkerColorIndex( theDisplay, strux, PEXOCStore, 7);
  if (select) PEXPolyline(theDisplay, strux, PEXOCStore, 2, points);
  PEXMarkers(theDisplay, strux, PEXOCStore, 2, points);

  PEXSetModelClipFlag( theDisplay, strux, PEXOCStore, PEXClip );
  PEXSetModelClipVolume( theDisplay, strux, PEXOCStore,
			   PEXModelClipIntersection, 1, &halfSpace );

  if (select) {
    theSelectedStrux = strux;
    theSelectedElement = FACE_CULL_ELEM + 2;
  }
}

/*************************************************************************
 * DumpStrux
 *
 * In: strux   - structure to dump
 *     element - is -1 do whole structure, else just that one
 *     pretty  - use pretty formatting, dump into Motif window???
 */
void
DumpStrux( strux, element, pretty )
     long strux;
     long element;
     int pretty;
{
  char *eData, *eStuff;
  PEXOCData *ocd, *OCData;
  int i, start;
  unsigned long count, length;

  if (element != -1) {
    if (!(PEXFetchElements( theDisplay, strux,
			     PEXBeginning, element, PEXBeginning, element, 
			     theFF, &count, &length, &eData  ))) { return; }
    start = element;
  } else {
    if (!(PEXFetchElements( theDisplay, strux,
			     PEXBeginning, 1, PEXEnd, 0, theFF,
			     &count, &length, &eData ))) { return; }
    start = 1;
  }

  if (eData) printf("fetch!\n");

  OCData = PEXDecodeOCs( theFF, count, length, eData );
  for (i = 0, ocd = OCData; i < count; i++, start++) {
    printf("%d element is type %d - first 12 bytes %x, %x, %x\n", start,
	   ocd->oc_type, ocd->data.FillAreaSet.shape_hint, 
	   ocd->data.FillAreaSet.ignore_edges,
	   ocd->data.FillAreaSet.contour_hint );

    ocd++;
  }
  PEXFreeOCData(count, OCData);

  XFree(eData);
}

/*************************************************************************
 * PrintElement - dump out the element contents in hex.
 */
/*
void
PrintElement( long struxID, long element, long ocType, long eSize,
		  char *elem_data )
{
  long i, *p;
}
*/
/*************************************************************************
 * ResizeWindow
 *
 * uses rubber sheet. The alternative would be porthole.
 *
 * It changes the Workstation Window (NPC) to the aspect ratio of the window.
 * It changes the Workstation Viewport (DC) to the size of the window.
 *
 * To do porthole, the Workstation Window would move around NPC. The
 * Workstation Viewport would still be the whole window.
 *
 * Watch out for X Window, View Window and Workstation Window confusion.
 */
void ResizeWindow( w, width, height)
     Window w;
     int width;
     int height;
{
   float d, minx, maxx, miny, maxy;
   PEXNPCSubVolume  volume;
   PEXViewport  viewport; 
   
   theRenderProcs.ReconfigureWindow( w, width, height );

   if (width > height) {
      minx = theWindowCenterX - theWindowSize;
      maxx = theWindowCenterX + theWindowSize;
      d = (float)(height)/(float)(width);
      miny = theWindowCenterY - theWindowSize*(d);
      maxy = theWindowCenterY + theWindowSize*(d);
   }
   else {
      miny = theWindowCenterY - theWindowSize;
      maxy = theWindowCenterY + theWindowSize;
      d = (float)(width)/(float)(height);
      minx = theWindowCenterX - theWindowSize*(d);
      maxx = theWindowCenterX + theWindowSize*(d);
   }

   viewport.min.x = 0.0;
   viewport.max.x = width;
   viewport.min.y = 0.0;
   viewport.max.y = height;
   viewport.min.z = 0.0;
   viewport.max.z = 1.0;
   viewport.use_drawable = 1; 
    
   volume.min.x = (float)minx;
   volume.max.x = (float)maxx;
   volume.min.y = (float)miny;
   volume.max.y = (float)maxy;
   volume.min.z = 0.0;
   volume.max.z = 1.0;

   theRenderProcs.SetNPCToDC( &volume, &viewport );
}

/*************************************************************************
 * InitLights - random init of two lights.
 */
InitLights()
{
  PEXLightEntry			light;

  light.type = PEXLightAmbient;
  light.color.type = PEXColorTypeRGB;
  light.color.value.rgb.red   =  AmbientColor[0];
  light.color.value.rgb.green =  AmbientColor[1];
  light.color.value.rgb.blue  =  AmbientColor[2];
  PEXSetTableEntries(theDisplay,theLightLUT,1,1,PEXLUTLight,(char *)&light);

  light.type = PEXLightWCVector;
  light.color.type = PEXColorTypeRGB;
  light.color.value.rgb.red   =  LightColor[0];
  light.color.value.rgb.green =  LightColor[1];
  light.color.value.rgb.blue  =  LightColor[2];
  light.direction.x = LightVector[0];
  light.direction.y = LightVector[1];
  light.direction.z = LightVector[2];
  PEXSetTableEntries(theDisplay,theLightLUT,2,1,PEXLUTLight,(char *)&light);

  /* set up light state info for structure elements */
  theLightsOn[0] = 1;
  theLightsOn[1] = 2;
  theLightsOnCount = 2;
  theLightsOffCount = 0;
}

/*************************************************************************
 * StartSpinning - install oneSpin as a Motif "work proc"
 *
 * set up the Global theSpinInfo to allow oneSpin to incrementally spin
 * theNewStrux. This is installed as a Motif work proc and gets called
 * whenever the XtMainLoop has nothing better to do.
 *
 * SEE ALSO, StopSpinning().  This sets theSpinInfo.stop which gets returned
 * by oneSpin each time it is called.  A non-zero value mean that the work
 * proc is done.
 */
int
StartSpinning(sid)
     PEXStructure sid;
{
  SpinInfo *si;
  int i;

  if (GetSpinInfo(sid)) return (0); /* it's going, don't bother */

  si = (SpinInfo *)malloc(sizeof(SpinInfo));

  si->strux = sid;
  /*
   * If I weren't such a wimp I'd get the matrix and decompose it so we start
   * from the right place, tick, tick...
   */
  si->point.x = 0.0;
  si->point.y = 0.0;
  si->point.z = 0.0;
  si->offset.x = 0;
  si->offset.y = 0;
  si->offset.z = 0;
  si->scale.x = 1.0;
  si->scale.y = 1.0;
  si->scale.z = 1.0;
  si->x_ang = 0.0;
  si->y_ang = 0.0;
  si->z_ang = 0.0;
  si->delta_x = ((float)(theSpinList.count+1))/10.0;
  si->delta_y = (float)0.1;
  si->delta_z = 0.0;

  si->wk_id = theRenderObj;
  si->dpy = theDisplay;
  si->stop = 0;
  si->next = theSpinList.listHead;
  theSpinList.count++;
  theSpinList.listHead = si;

  if (theSpinList.count == 1)
  AddWorkProc(manageSpin,(char *)si);

  return (1); /* we started something spinning */
}

SpinInfo *
GetSpinInfo(sid)
     PEXStructure sid;
{
  SpinInfo *si;
  int i;

  if (theSpinList.count == 0) theSpinList.listHead = (SpinInfo *)0; /* mem leak */
  /*
   * first check to make sure we are not spinning this strux already
   */
  si = theSpinList.listHead;
  for (i=0; i < theSpinList.count; i++) { /* what me trust linked lists? */
    if (si == (SpinInfo *)0) {
      printf("imagine there's no heaven, -John Lennon\n");
      /* oh, just let it crash */
    }
    if (si->strux == sid) return (si);
    si = si->next;
  }
  return ((SpinInfo *)0);
}

/*************************************************************************
 * oneSpin - Motif "work proc" for spinning a structure
 *
 * there is a bug in Xt that prevents multiple work procs from getting
 * time. So this one WorkProc manages all of the spins.
 * If Xt could round robin the workProcs, then oneSpin would be the
 * work proc.
 */
int manageSpin(client_data)
     char *client_data;
{
  SpinInfo *si;
  int error;
  int stop = 1;
  int i;
  
  /*
   * walk through list, call oneSpin for each.
   */
  si = theSpinList.listHead;
  for (i=0; i < theSpinList.count; i++) { /* what me trust linked lists? */
    if (si == (SpinInfo *)0) {
      printf("imagine there's no heaven, -John Lennon\n");
      /* oh, just let it crash */
    }
    if (!oneSpin((char *)si)) stop = 0;
    si = si->next;
  }
  theRenderProcs.ReDraw();

  XSync(theDisplay, 0); /* perhaps save the first display? */

  return (stop);
}

/*************************************************************************
 * oneSpin - Motif "work proc" for spinning a structure
 */
int oneSpin(client_data)
     char *client_data;
{
  SpinInfo *si;
  int error;

  si = (SpinInfo *)client_data;

  if (si->stop) return (si->stop);
/*
 * go to replace mode because we just want to bang that matrix a bunch of
 * times. err... well in this case, just once.
 */
  PEXSetEditingMode(theDisplay, si->strux, PEXStructureReplace );
  PEXSetElementPtr( theDisplay, si->strux, PEXBeginning,  MATRIX_ELEM );

  si->x_ang += si->delta_x;
  si->y_ang += si->delta_y;
  si->z_ang += si->delta_z;

  theMCMatrix.seqNo = ++theMCSeqNo;
  PEXBuildTransform(&si->point, &si->offset,
	si->x_ang, si->y_ang, si->z_ang, &si->scale, 
	theMCMatrix.matrix);

  PEXSetLocalTransform(theDisplay, si->strux, PEXOCStoreSingle, PEXReplace, 
		       theMCMatrix.matrix);
  PEXSetEditingMode(theDisplay, si->strux, PEXStructureInsert );

  return (si->stop);
}
/*************************************************************************
 * StopSpinning(sidToStop)
 *
 * When Is Making Possible: to free the spin memory.
 */
int
StopSpinning(sid)
     PEXStructure sid;
{
  SpinInfo *si, *presi;
  int i;

  si = theSpinList.listHead;
  if (sid == (PEXStructure)-1) {
    for (i=0; i < theSpinList.count; i++) { /* what me trust linked lists? */
      si->stop = 1;
      si = si->next;
    }
    theSpinList.count = 0;
    return (2);
  }
  /*
   * first check to make sure we are not spinning this strux already
   */
  presi = (SpinInfo *)0;
  si = theSpinList.listHead;
  for (i=0; i < theSpinList.count; i++) { /* what me trust linked lists? */
    if (si == (SpinInfo *)0) {
      printf("imagine there's no heaven, -John Lennon\n");
      /* oh, just let it crash */
    }
    if (si->strux == sid) {
      si->stop = 1;
      if (presi == (SpinInfo *)0) {
	theSpinList.listHead = si->next;
      } else {
	presi->next = si->next;
	return (1);
      }
      theSpinList.count--;
    }
    si = si->next;
  }
  return (0); /* nothing started */
}
/*************************************************************************
 * ReadArchiveFile
 *
 * Find all of the root structures - first find all structres and weed out
 *  those that are instanced by others.
 * Read the whole archive, should do this be reading networks of posters, but..
 * Post all of the poster candidates.
 */
int ReadArchiveFile(filename)
  char *filename;
{
  char *command;
  int len1;
  XID newStrux;
  int enough = 200;
  
  len1 = strlen(filename);
  command = (char *)malloc(len1+enough);
  if (!command)   {printf("can't malloc %s\n",filename); return (-1);}
  strcat(command,filename);
  
  newStrux = InitStrux();
  XSync(theDisplay, 0);

  strcat(&command[len1]," -display ");
  len1 += strlen(" -display ");
  strcat(&command[len1],DisplayString(theDisplay));
  len1 += strlen(DisplayString(theDisplay));
  strcat(&command[len1]," -strux ");
  len1 += strlen(" -strux ");
  sprintf(&command[len1],"%d\n",newStrux);
  printf(" system >%s< \n",command);
  system(command);
  free(command);
}

/*************************************************************************
 *  WriteArchiveFile
 */
int WriteArchiveFile(filename)
     char *filename;
{
  int arid;

  printf("closed archive file %s\n", filename);
}


/*************************************************************************
 * SolidToggleCmd - ideally this should inquire the state, if solid
 * make hollow, if hollow make solid and return the state so the menu
 * can give the user feedback about the state.
 */
int 
InteriorStyleCmd(style)
     int style;
{
  long strux;

  if ( theSelectedElement != -1 ) {
    strux =  theSelectedStrux;
  } else {
    if (theNewStrux == 0) theNewStrux = InitStrux();
    strux =  theNewStrux;
  }

  PEXSetEditingMode(theDisplay, strux, PEXStructureReplace );
  PEXSetElementPtr( theDisplay, strux, PEXBeginning,  INT_STYLE_ELEM );
  PEXSetInteriorStyle(theDisplay, strux, PEXOCStoreSingle, style);
  PEXSetEditingMode(theDisplay, strux, PEXStructureInsert );

  return (0);
}

int SurfaceInterpCmd( method)
     int method;
{
  long strux;

  if ( theSelectedElement != -1 ) {
    strux =  theSelectedStrux;
  } else {
    if (theNewStrux == 0) theNewStrux = InitStrux();
    strux =  theNewStrux;
  }

  PEXSetEditingMode(theDisplay, strux, PEXStructureReplace );
  PEXSetElementPtr( theDisplay, strux, PEXBeginning, INT_SHAD_ELEM );
  PEXSetSurfaceInterpMethod(theDisplay, strux, PEXOCStoreSingle, method );
  PEXSetEditingMode(theDisplay, strux, PEXStructureInsert );

  return (0);
}

/*************************************************************************
 *
 */
int FacetCullingCmd( mode)
     int mode;
{
  long strux;
  PEXElementInfo *eInfo;
  unsigned long count;

  if ( theSelectedElement != -1 ) {
    strux =  theSelectedStrux;
  } else {
    if (theNewStrux == 0) theNewStrux = InitStrux();
    strux =  theNewStrux;
  }

  PEXSetEditingMode(theDisplay, strux, PEXStructureReplace );
  PEXSetElementPtr( theDisplay, strux, PEXBeginning, FACE_CULL_ELEM);
/*
 * check to make sure there is a facet culling OC here, if not insert.
 */
  if(PEXGetElementInfo(theDisplay, strux, PEXBeginning, FACE_CULL_ELEM,
		    PEXBeginning, FACE_CULL_ELEM, theFF, &count, &eInfo )){
    if ((count != 1) || (eInfo->type != PEXOCFacetCullingMode)) {
      PEXSetEditingMode(theDisplay, strux, PEXStructureInsert );
      PEXSetElementPtr( theDisplay, strux, PEXCurrent, -1);
      if (theSelectedElement != -1 ) theSelectedElement++;
    }
  } else { printf("PEXGetStructureInfo failed\n");}

  PEXSetFacetCullingMode(theDisplay, strux, PEXOCStoreSingle, mode );
  PEXSetEditingMode(theDisplay, strux, PEXStructureInsert );

  return (0);
}

int ReflectionAttributesCmd(reflAttrs)
     PEXReflectionAttributes *reflAttrs;
{
  long strux;

  if ( theSelectedElement != -1 ) {
    strux =  theSelectedStrux;
  } else {
    if (theNewStrux == 0) theNewStrux = InitStrux();
    strux =  theNewStrux;
  }

  PEXSetEditingMode(theDisplay, strux, PEXStructureReplace );
  PEXSetElementPtr( theDisplay, strux, PEXBeginning, REFL_ATTRS_ELEM );
  PEXSetReflectionAttributes(theDisplay, strux, PEXOCStoreSingle, reflAttrs );
  PEXSetEditingMode(theDisplay, strux, PEXStructureInsert );

  return (0);
}

void
DumpStruxCmd(pretty)
     int pretty;
{
    if ( theSelectedElement != -1 ) {
      DumpStrux(theSelectedStrux, -1, pretty ); 
    } else if (theNewStrux != 0){
      DumpStrux(theNewStrux, -1, pretty ); 
    }
  }

/*************************************************************************
 * From posting by Alan Paeth
 *
Platonic Solids (regular and quasi-regular, Kepler-Poinset star solids omitted)

The orientations minimize the number of distinct coordinates, thereby revealing
both symmetry groups and embedding (eg, tetrahedron in cube in dodecahedron).
Consequently, the latter is depicted resting on an edge (Z taken as up/down).

SOLID		 VERTEX COORDINATES
-----------      -------------------
Tetrahedron      (  1,  1,  1), (  1, -1, -1), ( -1,  1, -1), ( -1, -1,  1)
Cube             (+-1,+-1,+-1)
Octahedron       (+-1,  0,  0), (  0,+-1,  0), (  0,  0,+-1)
Cubeoctahedron   (  0,+-1,+-1), (+-1,  0,+-1), (+-1,+-1,  0)
Icosahedron      (  0,+-p,+-1), (+-1,  0,+-p), (+-p,+-1,  0)
Dodecahedron     (  0,+-i,+-p), (+-p,  0,+-i), (+-i,+-p,  0), (+-1,+-1,+-1)
Icosidodecahedron(+-2,  0,  0), (  0,+-2,  0), (  0,  0,+-2), ...
                 (+-p,+-i,+-1), (+-1,+-p,+-i), (+-i,+-1,+-p)

with golden mean: p = (sqrt(5)+1)/2; i = (sqrt(5)-1)/2 = 1/p = p-1
------------------------------------------------------------------------------

   /Alan Paeth
   Computer Graphics Laboratory
   University of Waterloo

Why am I doing this split tristrip, when I should be writing!!!

while (i<n) {
 t1[j] = v[i], n1[j] = vn[i]
 t1[j+1] = (v[i]+v[i+1])/2, n[j+1] = vn[i]+vn[i+1]/2 Normalize
 t1[j+2] = (v[i]+v[i+2])/2, n[j+2] = vn[i]+vn[i+2]/2 Normalize
 t1[j+3] = (v[i+1]+v[i+2])/2, n[j+3] = vn[i+1]+vn[i+2]/2 Normalize

 if length is odd and i = n-2 then skip this.

 t2[j] = t1[j+1], n2[j] = n[j+1]
 t2[j+1] = v[i+1], n2[j+1] = vn[i+1]
 t2[j+2] = t1[j+3]
 t2[j+3] = (v[i+1]+v[i+3])/2, n2[j+3] = vn[i+1]+vn[i+3]/2 Normalize

 j += 4;
 i += 2;
 }

*/

static int cohCount = 0;
static PEXCoord cohVertex[] = { {0,1,1},{1,1,0},{1,0,1},{1,0,-1},{1,-1,0},
 {0,-1,-1},{0,-1,1},{-1,-1,0},{-1,0,1},{-1,0,-1},{-1,1,0},{0,1,-1},
 {0,1,1},{1,1,0}, /* duplicate */ {1,1,0},{0,1,-1}, {1,0,-1}, 
 {-1,0,-1}, {0,-1,-1}, {-1,-1,0},{-1,-1,0}, /*duplicate, web, duplicate */
 {-1,1,0},{-1,1,0},{0,1,1},{-1,0,1},{1,0,1},{0,-1,1},{1,-1,0}};

#define R3I .577

/* this is sort of interesting, but let's use the utility */
#ifdef NOT_DEF
static PEXVector cohNormals[] = { {R3I,R3I,R3I},{1,0,0},{1,0,0},{R3I,-R3I,-R3I},{0,-1,0},
 {0,-1,0},{-R3I,-R3I,R3I},{-1,0,0},{-1,0,0},{-R3I,R3I,-R3I},{0,1,0},{0,1,0},
 {R3I,R3I,-R3I},{R3I,R3I,-R3I}, /* duplicate now 14,15 */ {0,0,-1},{0,0,-1}, {-R3I,-R3I,-R3I}, 
 {-R3I,-R3I,-R3I}, {-R3I,-R3I,-R3I}, {-R3I,-R3I,-R3I},{-R3I,R3I,R3I}, /*duplicate, web, duplicate */
 {-R3I,R3I,R3I},{0,0,1},{0,0,1},{0,0,1},{0,0,1}};
#endif

#define COH_VERTS 28

long
Cubeoctahedron(offset, radius)
     PEXCoord *offset;
     double radius;
{
  PEXVertexNormal		vertex_data[COH_VERTS];
  PEXVertexNormal		*nv1, *nv2;
  PEXCoord p[2];
  float sq2;
  int i, j;
  long strux;
  PEXArrayOfFacetData facetNormals;
  PEXVector *fn1, *fn2;
  PEXArrayOfVertex too;  /* should be able to cast these somehow, too late */
  PEXVector cohNormals[COH_VERTS];

  strux = InitStrux();

  PEXSetLineColorIndex( theDisplay, strux, PEXOCStore, 2);
  
  p[0].x = 0; p[0].y = 0; p[0].z = 0;
  p[1].x = offset->x*radius;
  p[1].y = offset->y*radius;
  p[1].z = offset->z*radius;
  PEXPolyline(theDisplay, strux, PEXOCStore, 2, p );

/*
 * if we are passing though an odd pass insert a NURB sphere.
 */

  if (cohCount++ & 0x1) {
    too.normal = vertex_data;    

/* this is now done in the pipeline context.
 *   PEXSetSurfaceApprox(theDisplay, strux, PEXOCStore, 
 *			PEXApproxConstantBetweenKnots, 2.0, 2.0 );
 */
    PEXSetSurfaceColorIndex( theDisplay, strux, PEXOCStore, 5 );
    InsertNURBSphere( theDisplay, strux );

  } else {

    /*
     * generate the cubo
     */
    sq2 = sqrt((double)2.0) / 2.0;
    
    for (i = 0; i < COH_VERTS; i++ ) {
      vertex_data[i].point.x = offset->x + radius*cohVertex[i].x;
      vertex_data[i].point.y = offset->y + radius*cohVertex[i].y;
      vertex_data[i].point.z = offset->z + radius*cohVertex[i].z;
      vertex_data[i].normal.x = sq2*cohVertex[i].x;
      vertex_data[i].normal.y = sq2*cohVertex[i].y;
      vertex_data[i].normal.z = sq2*cohVertex[i].z;
    }
#define NO_DOUBLE 1

#ifdef NO_DOUBLE

    PEXSetSurfaceColorIndex( theDisplay, strux, PEXOCStore, 4 );
    too.normal = vertex_data;
    facetNormals.normal = cohNormals;
    PEXGeoNormTriangleStrip( PEXGANormal, PEXGANormal,
		     PEXColorTypeRGB, facetNormals, 28, too);

    for (i = 0; i < COH_VERTS-2; i++ ) {
      cohNormals[i].x = -cohNormals[i].x;
      cohNormals[i].y = -cohNormals[i].y;
      cohNormals[i].z = -cohNormals[i].z;
    }

    PEXTriangleStrip( theDisplay, strux, PEXOCStore, PEXGANormal, PEXGANormal,
		     PEXColorTypeRGB, facetNormals, 28, too);

#else

/*
 * double the tristrip
 */

    nv1 = (PEXVertexNormal *)malloc(COH_VERTS*4*sizeof(PEXVertexNormal));
      if (!nv1) {printf("nv1\n"); exit(1);}
    nv2 = nv1+2*COH_VERTS;

    fn1 = (PEXVector *)malloc(COH_VERTS*4*sizeof(PEXVector));
      if (!fn1) {printf("nv1\n"); exit(1);}
    fn2 = fn1+2*COH_VERTS;


    i = 0; j = 0;

#define AVERAGE(a,b,c) {a.x=(b.x+c.x)/2;a.y=(b.y+c.y)/2;a.z=(b.z+c.z)/2;}

    while (i < COH_VERTS) {

      nv1[j].point = vertex_data[i].point;
      nv1[j].normal = vertex_data[i].normal;

      fn1[j] = cohNormals[i];
      fn1[j+1] = cohNormals[i];
      fn1[j+2] = cohNormals[i];
      fn1[j+3] = cohNormals[i+1];

      AVERAGE(nv1[j+1].point, vertex_data[i].point, vertex_data[i+1].point);
      AVERAGE(nv1[j+1].normal, vertex_data[i].normal, vertex_data[i+1].normal);
      PEXNormalizeVectors(1, &nv1[j+1].normal, &nv1[j+1].normal);

      AVERAGE(nv1[j+2].point, vertex_data[i].point, vertex_data[i+2].point);
      AVERAGE(nv1[j+2].normal, vertex_data[i].normal, vertex_data[i+2].normal);
      PEXNormalizeVectors(1, &nv1[j+2].normal, &nv1[j+2].normal);

      AVERAGE(nv1[j+3].point, vertex_data[i+1].point, vertex_data[i+2].point);
      AVERAGE(nv1[j+3].normal,vertex_data[i+1].normal,vertex_data[i+2].normal);
      PEXNormalizeVectors(1, &nv1[j+3].normal, &nv1[j+3].normal);

      /*  if length is odd and i = n-2 then skip this. */

      nv2[j] = nv1[j+1];

      nv2[j+1].point = vertex_data[i+1].point;
      nv2[j+1].normal = vertex_data[i+1].normal;

      nv2[j+2] = nv1[j+3];

      AVERAGE(nv2[j+3].point, vertex_data[i+1].point, vertex_data[i+3].point);
      AVERAGE(nv2[j+3].normal,vertex_data[i+1].normal,vertex_data[i+3].normal);
      PEXNormalizeVectors(1, &nv2[j+3].normal, &nv2[j+3].normal);

      fn2[j] = cohNormals[i];
      fn2[j+1] = cohNormals[i+1];
      fn2[j+2] = cohNormals[i+1];
      fn2[j+3] = cohNormals[i+1];

      j += 4;
      i += 2;
    }
    PEXSetSurfaceColorIndex( theDisplay, strux, PEXOCStore, 4 );
    too.normal = nv1;
    facetNormals.normal = fn1;
    PEXTriangleStrip( theDisplay, strux, PEXOCStore, PEXGANormal, PEXGANormal,
		     PEXColorTypeRGB, facetNormals, 54, too);

    PEXSetSurfaceColorIndex( theDisplay, strux, PEXOCStore, 4 );
    too.normal = nv2;
    facetNormals.normal = fn2;
    PEXTriangleStrip( theDisplay, strux, PEXOCStore, PEXGANormal, PEXGANormal,
		     PEXColorTypeRGB, facetNormals, 54, too);

    free(nv1);
    free(fn1);
#endif
  }

  theRenderProcs.Post( strux );

  return (strux);
}
/*************************************************************************
 *
 */
int GetTextRect(OCData, strux, offset, rect )
     PEXOCData *OCData;
     XID strux;
     int offset;
     PEXCoord **rect;
{
  PEXTextExtent *extent;
  TextAttributes *tAttrs;
  PEXListOfEncodedText listOfEncodedText;

/* GetTextAttributes - really needs to be a renderer function?
 * No, the structure inquiry should happen in application code, getting
 * the default should be from the renderer.
				      tAttrs->index, tAttrs->path,
				      tAttrs->expansion, tAttrs->spacing,
				      tAttrs->height, 
				      tAttrs->halign, tAttrs->valign, 
 */

 listOfEncodedText.count = OCData->data.EncodedText.count;
 listOfEncodedText.encoded_text = OCData->data.EncodedText.encoded_text;

  XSync(theDisplay, 0);

  extent = PEXQueryEncodedTextExtents( theDisplay, theFontLUT,
				      (unsigned int)1, PEXPathRight,
				      1.0, 0.0, 0.08, 
				      PEXVAlignNormal, PEXHAlignNormal,
				      1, &listOfEncodedText );

  textRect[0].x = OCData->data.EncodedText.origin.x + extent->lower_left.x;
  textRect[0].y = OCData->data.EncodedText.origin.y + extent->lower_left.y;
  textRect[2].x = textRect[0].x + extent->upper_right.x;
  textRect[2].y = textRect[0].y + extent->upper_right.y;

  textRect[1].x = textRect[2].x;
  textRect[1].y = textRect[0].y;
  textRect[3].x = textRect[0].x;
  textRect[3].y = textRect[2].y;

  textRect[3].z = textRect[2].z = textRect[1].z = textRect[0].z = 
    OCData->data.EncodedText.origin.z;

  *rect = textRect;
  return (1);
}

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

InsertNURBSphere(dpy,struxid)
     Display *dpy;
     XID struxid;
{
  float uKnots[8]; /* 3 + 5 = 8 <> order + points = number of knots */
  float vKnots[12]; /* 3 + 9 = 12 */
  PEXCoord uPoints[5];
  PEXCoord vPoints[9];
  PEXCoord4D points[45];
  float uWeights[5];
  float vWeights[9];
  PEXArrayOfCoord parr;
  int i,j;

#define SET_POINT(p,a,b,c) {(p)->x=(a);(p)->y=(b);(p)->z=(c);}

  SET_POINT(&uPoints[0], 0, 0, -0.5); uWeights[0] = 1;
  SET_POINT(&uPoints[1], 0.5, 0, -0.5); uWeights[1] = 0.707107;
  SET_POINT(&uPoints[2], 0.5, 0, 0);  uWeights[2] = 1;
  SET_POINT(&uPoints[3], 0.5, 0, 0.5);  uWeights[3] = 0.707107;
  SET_POINT(&uPoints[4], 0, 0, 0.5);  uWeights[4] = 1;

  uKnots[0]=0; uKnots[1]=0; uKnots[2]=0;
  uKnots[3]=1; uKnots[4]=1;
  uKnots[5]=2; uKnots[6]=2; uKnots[7]=2;

  SET_POINT(&vPoints[0], 0, 1, 0);   vWeights[0] = 1;        
  SET_POINT(&vPoints[1], 1, 1, 0);   vWeights[1] = 0.707107; 
  SET_POINT(&vPoints[2], 1, 0, 0);   vWeights[2] = 1;        
  SET_POINT(&vPoints[3], 1, -1, 0);  vWeights[3] = 0.707107; 
  SET_POINT(&vPoints[4], 0, -1, 0);  vWeights[4] = 1;        
  SET_POINT(&vPoints[5], -1, -1, 0); vWeights[5] = 0.707107; 
  SET_POINT(&vPoints[6], -1, 0, 0);  vWeights[6] = 1;        
  SET_POINT(&vPoints[7], -1, 1, 0);  vWeights[7] = 0.707107; 
  SET_POINT(&vPoints[8], 0, 1, 0);   vWeights[8] = 1;        

  vKnots[0]=0; vKnots[1]=0; vKnots[2]=0;
  vKnots[3]=1; vKnots[4]=1;
  vKnots[5]=2; vKnots[6]=2;
  vKnots[7]=3; vKnots[8]=3;
  vKnots[9]=4; vKnots[10]=4; vKnots[11]=4;

  for (i = 0; i < 9; i++ ) { /* for each v point */
    for (j = 0; j < 5; j++ ) { /* for each u point */
      points[i*5+j].w = uWeights[j] * vWeights[i];
      SET_POINT(&points[i*5+j], uPoints[j].x*vPoints[i].x * points[i*5+j].w,
		uPoints[j].x*vPoints[i].y  * points[i*5+j].w,
		uPoints[j].z * points[i*5+j].w);
    }
  }
  parr.point_4d = points;
  PEXNURBSurface(dpy,struxid,PEXOCStore, PEXRational, 3, 3,
		 uKnots, vKnots, 
		 5, 9, parr, 0, 0 );
}

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

InsertNURBSurface(dpy,struxid, rational)
     Display *dpy;
     XID struxid;
     int rational;
{
  float uKnots[6]; /* 3 + 5 = 8 <> order + points = number of knots */
  float vKnots[8]; /* 3 + 9 = 12 */
  PEXCoord p3[12];
  PEXCoord4D p4[12];
  PEXArrayOfCoord parr;
  int i;

  uKnots[0]=0; uKnots[1]=0; uKnots[2]=0;
  uKnots[3]=1; uKnots[4]=1; uKnots[5]=1;

  vKnots[0]=0; vKnots[1]=0; vKnots[2]=0; vKnots[3]=0;
  vKnots[4]=1; vKnots[5]=1; vKnots[6]=1; vKnots[7]=1;

  SET_POINT(&p3[0], 0.0, 0.5, 0.0);
  SET_POINT(&p3[1], 0.0, 0.6, 0.5);
  SET_POINT(&p3[2], 0.0, 0.5, 1.0);
  SET_POINT(&p3[3], 0.3, 0.5, 0.0);
  SET_POINT(&p3[4], 0.3, 1.0, 0.5);
  SET_POINT(&p3[5], 0.3, 0.5, 1.0);
  SET_POINT(&p3[6], 0.6, 0.4, 0.0);
  SET_POINT(&p3[7], 0.6, 0.5, 0.5);
  SET_POINT(&p3[8], 0.6, 0.4, 1.0);
  SET_POINT(&p3[9], 0.9, 0.5, 0.0);
  SET_POINT(&p3[10], 0.9, 0.3, 0.5);
  SET_POINT(&p3[11], 0.9, 0.2, 1.0);

  if (rational == PEXRational) {
    PEXCoord   *pp3;
    PEXCoord4D *pp4;
    float w;

    w = 0.5;

    for (i=0, pp4 = p4, pp3 = p3; i < 12;  i++, pp4++, pp3++ ) {
	pp4->x = pp3->x * w;
	pp4->y = pp3->y * w;
	pp4->z = pp3->z * w;
	pp4->w = w;
      }
    p4[11].w = 0.1;
    parr.point_4d = p4;
  } else {
    parr.point = p3;
  }

  PEXSetSurfaceApprox(theDisplay, struxid, PEXOCStore, 
		      PEXApproxConstantBetweenKnots, 2.0, 2.0 );
  PEXSetSurfaceColorIndex( theDisplay, struxid, PEXOCStore, 5 );
  PEXNURBSurface(dpy,struxid,PEXOCStore, rational, 3, 4,
		 uKnots, vKnots, 
		 3, 4, parr, 0, 0 );
}
/*************************************************************************
 *
 */

int CheckImpDepInteger(impDepConst)
  int impDepConst;
{
  PEXImpDepConstant *impDeps;
  unsigned short id[1];         /* it get's promoted if a paramter, sigh */
  int value;
/*
 */
  id[0] = (unsigned short)impDepConst;

  if(PEXGetImpDepConstants( theDisplay, theWindow,
			   1, id, &impDeps )) {
    value = impDeps[0].integer;
    XFree((char *)impDeps);
  } else { printf("CheckImpDep failed on %d\n",impDepConst); value = 0; }
  return (value);
}	

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

int CheckEnumType( eType, eValue )
     int eType;
     int eValue;
{
  PEXEnumTypeDesc *enums;
  unsigned long *enumCounts;
  int found = 0;
  int j;

  if (PEXGetEnumTypeInfo( theDisplay, theWindow, 1, &eType,
				    PEXETIndex, &enumCounts, &enums )) {
    for (j = 0; j < enumCounts[0]; j++ ) {
      /* we have a list of the supported enums for the type eType */
      /* See if the one we wnat is in here */
      if (enums[j].index == (PEXEnumTypeIndex)eValue) found = 1;
      }
    PEXFreeEnumInfo(1, enumCounts, enums );
  }
  return (found);
}

/*************************************************************************
 *  GetColorAttribute get the color of the given element.
 *
 *  Function return 1 if O.K.
 *
 *  Get the element 2 before it, if color, assume it is right type go to FC
 *   if not color, fetch element, decide type, increment elem[hmmm...]
 *     [hmm... violation of global selected vs. what we got, but it's exception
 *   Insert Noop to be color.
 *  FC - Fetch Color
 *   if indexed, get color table from renderer, get color from color table
 *  fill in
 */
int GetColorAttribute(strux, elem, rgb)
     PEXStructure strux;
     long  elem;
     PEXColorRGB *rgb;
{
  int i;
  int error;
  char *eData;
  unsigned long count, length;
  PEXOCData *OCData;
  long  e;

  if (elem == -1) {
    rgb->red = 1.0;
    rgb->green = 1.0;
    rgb->blue = 1.0;
    return (0);
  }

  e = elem-1; /* get to the color element */

  if (!(PEXFetchElements( theDisplay, strux,
			   PEXBeginning, e, PEXBeginning, e, theFF,
			   &count, &length, &eData ))) { return; }

  OCData = PEXDecodeOCs( theFF, count, length, eData );
  XFree((char *)eData);

  /* no edge or backface color test */
  if ((OCData->oc_type  == PEXOCLineColor) ||
      (OCData->oc_type  == PEXOCTextColor) ||
      (OCData->oc_type  == PEXOCMarkerColor) ||
      (OCData->oc_type  == PEXOCSurfaceColor)) { 
    if (OCData->data.SetLineColor.color_type != PEXColorTypeRGB)printf("RGB\n");
    *rgb = OCData->data.SetLineColor.color.rgb;
  } else if ((OCData->oc_type  == PEXOCLineColorIndex) ||
      (OCData->oc_type  == PEXOCTextColorIndex) ||
      (OCData->oc_type  == PEXOCMarkerColorIndex) ||
      (OCData->oc_type  == PEXOCSurfaceColorIndex)) { 
    theRenderProcs.GetColorFromIndex(OCData->data.SetLineColorIndex.index, rgb);
  } else {
    /*
     * not a color element, insert a Noop after elem and call color group
     */
    long struxArray[1];
    int elemArray[1];

    PEXSetElementPtr( theDisplay, strux, PEXBeginning, e );
    PEXNoop(theDisplay,strux,PEXOCStore); /* ColorGroup will replace */
    
    struxArray[0] = strux;
    if ((strux == theSelectedStrux) && (elem >= theSelectedElement)) {
      theSelectedElement++;
      elemArray[0] = elem+1;
    } else {
      elemArray[0] = elem;
    }
    ColorGroup(1,struxArray,elemArray);
     
    theRenderProcs.GetColorFromIndex(theRandomColorIndex, rgb);
  }
  return (1);
}

int SetColorAttribute(strux, elem, rgb)
     PEXStructure strux;
     long  elem;
     PEXColorRGB *rgb;
{
  unsigned long length;
  PEXElementInfo *eInfo;
  unsigned long eCount;
  long pe, del;

  if ((strux == theSelectedStrux) && (elem == theSelectedElement)) {
    pe = elem+1;  /* primitive is behind select color strux */
    del = elem;   /* neuter the select color */
  } else {
    pe = elem;
    del = elem-1;
  }
  

  if(PEXGetElementInfo(theDisplay, strux, 
			    PEXBeginning, pe,
			    PEXBeginning, pe,
			    theFF, &eCount, &eInfo )){
    if ((eCount == 1) &&
	((eInfo->type >= OCPRIMTAB_BASE) && (eInfo->type <= OCPRIMTAB_MAX))) {

      PEXDeleteElements( theDisplay, strux,
			PEXBeginning, elem-1,
			PEXBeginning, del );

      switch (myOCPrimType[eInfo->type-OCPRIMTAB_BASE]) {
      case OCPRIM_MARKER:
	PEXSetMarkerColor( theDisplay, strux, PEXOCStore,
			  PEXColorTypeRGB, (PEXColor *)rgb);
	break;
      case OCPRIM_LINE:
	PEXSetLineColor( theDisplay, strux, PEXOCStore,
			PEXColorTypeRGB, (PEXColor *)rgb);
	break;
      case OCPRIM_PGON:
	PEXSetSurfaceColor( theDisplay, strux, PEXOCStore, 
			   PEXColorTypeRGB, (PEXColor *)rgb);
	break;
      case OCPRIM_TEXT:
	PEXSetTextColor( theDisplay, strux, PEXOCStore,
			PEXColorTypeRGB, (PEXColor *)rgb);
	break;
      default:
	break;
      }
      if ((strux == theSelectedStrux) && (elem == theSelectedElement)) {
	PEXNoop(theDisplay, strux, PEXOCStore);
      }
    } else { printf("SetColorAttribute, %d not prim\n",eInfo->type);}
  } else { printf("PEXGetStructureInfo failed\n");}
}

DeleteStrux(strux)
     PEXStructure strux;
{
  PEXDestroyStructures(theDisplay, 1, (PEXStructure *)&strux);
  if ((strux == theSelectedStrux) && (theSelectedElement != -1)) {
    theSelectedElement = -1;
  }
}

ExecStrux(strux)
     PEXStructure strux;
{
  if ((strux == theSelectedStrux) && (theSelectedElement != -1)) {
    printf("a structure cannot execute itself\n");
    return;
  }

  PEXSetElementPtr( theDisplay, theSelectedStrux, PEXEnd,  0 );
  PEXExecuteStructure(theDisplay, theSelectedStrux, PEXOCStore, strux );
}

